diff --git a/.circleci/config.yml b/.circleci/config.yml index f13e9bf66f1..b0a705966a2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,6 +5,16 @@ orbs: win: circleci/windows@5.0 # Add Windows orb commands: + skip_if_unrelated_changes: + parameters: + category: + type: enum + enum: ["backend", "client"] + default: "backend" + steps: + - run: + name: "Skip job when no << parameters.category >>-relevant files changed" + command: bash .circleci/scripts/path_filter.sh << parameters.category >> setup_google_dns: steps: - run: @@ -282,6 +292,7 @@ jobs: parallelism: 4 steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - restore_cache: keys: @@ -354,6 +365,7 @@ jobs: parallelism: 4 steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - restore_cache: keys: @@ -427,6 +439,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - restore_cache: keys: @@ -480,6 +493,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -545,6 +559,7 @@ jobs: DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/litellm_test" steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -584,6 +599,7 @@ jobs: DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/litellm_test" steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -624,6 +640,7 @@ jobs: DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/litellm_test" steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -656,6 +673,7 @@ jobs: FAKE_OPENAI_API_BASE: http://127.0.0.1:8190 steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - restore_cache: @@ -705,6 +723,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - restore_cache: @@ -755,6 +774,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -787,6 +807,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - restore_cache: @@ -832,6 +853,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -877,6 +899,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -918,6 +941,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -963,6 +987,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1004,9 +1029,12 @@ jobs: - *python312_image working_directory: ~/project resource_class: large + environment: + REQUEST_TIMEOUT: "180" steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - restore_cache: @@ -1032,7 +1060,8 @@ jobs: -v -x \ --junitxml=test-results/junit.xml \ --durations=5 \ - -n 8" + -n 8 \ + --reruns 1 --only-rerun Timeout" no_output_timeout: 15m # Store test results @@ -1045,6 +1074,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1089,6 +1119,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1132,6 +1163,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1163,6 +1195,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1205,6 +1238,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1248,6 +1282,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1291,6 +1326,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1321,6 +1357,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1366,6 +1403,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1407,6 +1445,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - restore_cache: keys: @@ -1459,6 +1498,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1482,6 +1522,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1507,6 +1548,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1531,6 +1573,7 @@ jobs: steps: - checkout + - skip_if_unrelated_changes - attach_workspace: at: ~/project - setup_google_dns @@ -1570,14 +1613,14 @@ jobs: - run: name: Run helm lint command: | - helm lint ./deploy/charts/litellm-helm + helm lint ./helm/litellm-helm # Run helm tests - run: name: Run helm tests command: | IMAGE_TAG=${CIRCLE_SHA1:-ci} - helm install litellm ./deploy/charts/litellm-helm -f ./deploy/charts/litellm-helm/ci/test-values.yaml \ + helm install litellm ./helm/litellm-helm -f ./helm/litellm-helm/ci/test-values.yaml \ --set image.repository=litellm-ci \ --set image.tag=${IMAGE_TAG} \ --set image.pullPolicy=Never @@ -1606,6 +1649,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1698,6 +1742,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - attach_workspace: at: ~/project - setup_google_dns @@ -1746,13 +1791,13 @@ jobs: -e LANGFUSE_PROJECT1_SECRET=$LANGFUSE_PROJECT1_SECRET \ -e LANGFUSE_PROJECT2_SECRET=$LANGFUSE_PROJECT2_SECRET \ -e RECORDER_OPENAI_BASE_URL=http://host.docker.internal:8090/v1 \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/proxy_server_config.yaml:/app/config.yaml \ my-app:latest \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ + --port 4000 - run: name: Start outputting logs command: docker logs -f my-app @@ -1787,6 +1832,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1832,13 +1878,13 @@ jobs: -e LANGFUSE_PROJECT2_PUBLIC=$LANGFUSE_PROJECT2_PUBLIC \ -e LANGFUSE_PROJECT1_SECRET=$LANGFUSE_PROJECT1_SECRET \ -e LANGFUSE_PROJECT2_SECRET=$LANGFUSE_PROJECT2_SECRET \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/litellm/proxy/example_config_yaml/oai_misc_config.yaml:/app/config.yaml \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ + --port 4000 - run: name: Start outputting logs command: docker logs -f my-app @@ -1869,6 +1915,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -1911,14 +1958,14 @@ jobs: -e COHERE_API_KEY=$COHERE_API_KEY \ -e RECORDER_COHERE_BASE_URL=http://host.docker.internal:8090/__recorder_upstream/api.cohere.com \ -e GCS_FLUSH_INTERVAL="1" \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/litellm/proxy/example_config_yaml/otel_test_config.yaml:/app/config.yaml \ -v $(pwd)/litellm/proxy/example_config_yaml/custom_guardrail.py:/app/custom_guardrail.py \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ + --port 4000 - run: name: Start outputting logs command: docker logs -f my-app @@ -1960,13 +2007,13 @@ jobs: -e OPENAI_API_KEY=$OPENAI_API_KEY \ -e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \ -e LITELLM_LICENSE="bad-license" \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app-3 \ -v $(pwd)/litellm/proxy/example_config_yaml/enterprise_config.yaml:/app/config.yaml \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug + --port 4000 - run: name: Start outputting logs for second container @@ -2000,6 +2047,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -2041,13 +2089,13 @@ jobs: -e DD_SITE=$DD_SITE \ -e AWS_REGION_NAME=$AWS_REGION_NAME \ -e PROXY_BATCH_WRITE_AT=2 \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/litellm/proxy/example_config_yaml/spend_tracking_config.yaml:/app/config.yaml \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ + --port 4000 - run: name: Start outputting logs command: docker logs -f my-app @@ -2085,6 +2133,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -2117,13 +2166,13 @@ jobs: -e USE_DDTRACE=True \ -e DD_API_KEY=$DD_API_KEY \ -e DD_SITE=$DD_SITE \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/litellm/proxy/example_config_yaml/multi_instance_simple_config.yaml:/app/config.yaml \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ + --port 4000 - run: name: Run Docker container 2 command: | @@ -2139,13 +2188,13 @@ jobs: -e USE_DDTRACE=True \ -e DD_API_KEY=$DD_API_KEY \ -e DD_SITE=$DD_SITE \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app-2 \ -v $(pwd)/litellm/proxy/example_config_yaml/multi_instance_simple_config.yaml:/app/config.yaml \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4001 \ - --detailed_debug + --port 4001 - run: name: Start outputting logs command: docker logs -f my-app @@ -2180,6 +2229,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -2201,19 +2251,20 @@ jobs: # the OTEL test - should get this as a trace command: | docker run -d \ + --restart on-failure \ -p 4000:4000 \ -e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \ -e STORE_MODEL_IN_DB="True" \ -e LITELLM_MASTER_KEY="sk-1234" \ -e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \ -e LITELLM_LICENSE=$LITELLM_LICENSE \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/litellm/proxy/example_config_yaml/store_model_db_config.yaml:/app/config.yaml \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ + --port 4000 - run: name: Start outputting logs command: docker logs -f my-app @@ -2252,6 +2303,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns # Remove Docker CLI installation since it's already available in machine executor - install_uv @@ -2289,13 +2341,13 @@ jobs: -e DD_API_KEY=$DD_API_KEY \ -e DD_SITE=$DD_SITE \ -e GCS_FLUSH_INTERVAL="1" \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/docker/build_from_pip/litellm_config.yaml:/app/config.yaml \ my-app:latest \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ + --port 4000 - run: name: Start outputting logs command: docker logs -f my-app @@ -2333,6 +2385,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -2365,14 +2418,14 @@ jobs: -e DD_SITE=$DD_SITE \ -e LITELLM_LICENSE=$LITELLM_LICENSE \ -e LITELLM_USE_CHAT_COMPLETIONS_URL_FOR_ANTHROPIC_MESSAGES=true \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/litellm/proxy/example_config_yaml/pass_through_config.yaml:/app/config.yaml \ -v $(pwd)/litellm/proxy/example_config_yaml/custom_auth_basic.py:/app/custom_auth_basic.py \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ + --port 4000 - run: name: Start outputting logs command: docker logs -f my-app @@ -2471,6 +2524,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - setup_google_dns - install_uv - run: @@ -2499,13 +2553,13 @@ jobs: -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ -e AWS_REGION_NAME="us-east-1" \ -e LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS="True" \ + -e LITELLM_LOG=ERROR \ --add-host host.docker.internal:host-gateway \ --name my-app \ -v $(pwd)/tests/proxy_e2e_anthropic_messages_tests/test_config.yaml:/app/config.yaml \ litellm-docker-database:ci \ --config /app/config.yaml \ - --port 4000 \ - --detailed_debug + --port 4000 - run: name: Start outputting logs command: docker logs -f my-app @@ -2537,6 +2591,7 @@ jobs: - *python312_image steps: - checkout + - skip_if_unrelated_changes - attach_workspace: at: . # Check file locations @@ -2567,6 +2622,8 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes: + category: client - setup_google_dns - restore_cache: keys: @@ -2609,6 +2666,8 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes: + category: client - setup_google_dns - restore_cache: keys: @@ -2629,7 +2688,7 @@ jobs: cd ui/litellm-dashboard CI=true npm run test -- --run \ - --pool forks --poolOptions.forks.maxForks=8 + --pool forks --poolOptions.forks.maxForks=6 e2e_ui_testing: docker: @@ -2654,6 +2713,8 @@ jobs: PROXY_LOGOUT_URL: "https://www.example.com" steps: - checkout + - skip_if_unrelated_changes: + category: client - setup_google_dns - install_uv - restore_cache: @@ -2791,6 +2852,8 @@ jobs: SERVER_ROOT_PATH: "/litellm" steps: - checkout + - skip_if_unrelated_changes: + category: client - setup_google_dns - install_uv - restore_cache: @@ -2892,6 +2955,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - run: name: Build Docker image @@ -2917,6 +2981,7 @@ jobs: working_directory: ~/project steps: - checkout + - skip_if_unrelated_changes - attach_workspace: at: ~/project - setup_google_dns diff --git a/.circleci/scripts/classify_changes.sh b/.circleci/scripts/classify_changes.sh new file mode 100755 index 00000000000..2c15428be6a --- /dev/null +++ b/.circleci/scripts/classify_changes.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -uo pipefail + +category="${1:?usage: classify_changes.sh }" + +has_client=false +has_backend=false +while IFS= read -r file || [ -n "$file" ]; do + [ -n "$file" ] || continue + case "$file" in + ui/*) has_client=true ;; + docs/* | *.md | *.mdx) : ;; + *) has_backend=true ;; + esac +done + +case "$category" in + backend) + [ "$has_backend" = true ] && echo run || echo skip + ;; + client) + { [ "$has_client" = true ] || [ "$has_backend" = true ]; } && echo run || echo skip + ;; + *) + echo run + ;; +esac diff --git a/.circleci/scripts/path_filter.sh b/.circleci/scripts/path_filter.sh new file mode 100755 index 00000000000..dcf64a24399 --- /dev/null +++ b/.circleci/scripts/path_filter.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -uo pipefail + +category="${1:?usage: path_filter.sh }" +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +run_full() { + echo "path-filter[$category]: running job ($1)" + exit 0 +} + +[ -n "${CIRCLE_PULL_REQUEST:-}" ] || run_full "not a pull request" + +candidate_bases="main litellm_internal_staging litellm_oss_staging" +merge_base="" +for base in $candidate_bases; do + git fetch --quiet origin "$base" 2>/dev/null || continue + candidate="$(git merge-base HEAD FETCH_HEAD 2>/dev/null)" || continue + [ -n "$candidate" ] || continue + if [ -z "$merge_base" ] || git merge-base --is-ancestor "$merge_base" "$candidate" 2>/dev/null; then + merge_base="$candidate" + fi +done + +[ -n "$merge_base" ] || run_full "could not resolve a merge base against $candidate_bases" + +changed="$(git diff --name-only "$merge_base" HEAD 2>/dev/null)" || run_full "git diff failed" +[ -n "$changed" ] || run_full "no files changed vs $merge_base" + +echo "path-filter[$category]: changed files vs ${merge_base}:" +printf '%s\n' "$changed" | sed 's/^/ /' || true + +decision="$(printf '%s\n' "$changed" | bash "$here/classify_changes.sh" "$category")" || run_full "classify_changes.sh failed" + +if [ "$decision" = run ]; then + run_full "$category-relevant changes detected" +fi + +echo "path-filter[$category]: only unrelated (docs/client) changes detected; halting job as successful" +circleci-agent step halt diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 7e705ec4f8f..2527239b904 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -13,7 +13,7 @@ 7edf3a9cb55548b143df1692f4ed7c4681d7fcf7 # style: reformat litellm/ with ruff format (#31317) -430b5b8f1b12dc261a49fda99ac5d1b22381a428 +17bfd415aeb5a57fb646b5cc67da1c730aa7c50b # style: unify ruff format width on 120 (#31518) -3dfbeabe626d203ac9de86024519d9a96c484ce4 +48b5a5a0cc5a694a11219416ee0b6eb6e620e74e diff --git a/.github/actions/detect-backend-changes/action.yml b/.github/actions/detect-backend-changes/action.yml new file mode 100644 index 00000000000..af01038f294 --- /dev/null +++ b/.github/actions/detect-backend-changes/action.yml @@ -0,0 +1,48 @@ +name: "Detect backend-relevant changes" +description: >- + Classify the pull request's changed files with .circleci/scripts/classify_changes.sh + and expose decision=run|skip. decision=skip means only ui/**, **.md or **.mdx files + changed, so callers can short-circuit expensive steps while the job still completes + successfully and satisfies its required status check. The decision defaults to run for + any non pull_request event or whenever the changed set cannot be resolved, so tests are + never skipped when the classification is uncertain. + +outputs: + decision: + description: "run when backend-relevant files changed, otherwise skip" + value: ${{ steps.classify.outputs.decision }} + +runs: + using: composite + steps: + - id: classify + shell: bash + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: | + set -uo pipefail + if [ -z "${BASE_SHA:-}" ]; then + echo "detect-backend-changes: not a pull_request event; running job" + echo "decision=run" >> "${GITHUB_OUTPUT}" + exit 0 + fi + if ! git fetch --no-tags --depth=1 origin "${BASE_SHA}" >/dev/null 2>&1; then + echo "detect-backend-changes: could not fetch base ${BASE_SHA}; running job" + echo "decision=run" >> "${GITHUB_OUTPUT}" + exit 0 + fi + changed="$(git diff --name-only "${BASE_SHA}" HEAD 2>/dev/null)" || { + echo "detect-backend-changes: git diff failed; running job" + echo "decision=run" >> "${GITHUB_OUTPUT}" + exit 0 + } + if [ -z "${changed}" ]; then + echo "detect-backend-changes: no changed files vs ${BASE_SHA}; skipping job" + echo "decision=skip" >> "${GITHUB_OUTPUT}" + exit 0 + fi + echo "detect-backend-changes: changed files vs ${BASE_SHA}:" + printf '%s\n' "${changed}" | sed 's/^/ /' + decision="$(printf '%s\n' "${changed}" | bash .circleci/scripts/classify_changes.sh backend)" || decision="run" + echo "detect-backend-changes: decision=${decision}" + echo "decision=${decision}" >> "${GITHUB_OUTPUT}" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 12ad124fa20..d7e80b32749 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,7 @@ ## Linear ticket - + ## Pre-Submission checklist @@ -13,7 +13,7 @@ - [ ] I have added meaningful tests - [ ] My PR passes all CI/CD checks (e.g., lint, format, unit tests) - [ ] 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 +- [ ] I have received a Greptile **Confidence Score of at least 4/5** before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment `@greptileai` to re-request a review after pushing changes) ## Delays in PR merge? @@ -24,6 +24,7 @@ If you're seeing a delay in your PR being merged, ping the LiteLLM Team on [Slac @@ -40,3 +41,27 @@ If you're seeing a delay in your PR being merged, ping the LiteLLM Team on [Slac ✅ Test ## Changes + +## QA runbook + + + +### Final Attestation + +- [ ] The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR diff --git a/.github/workflows/_test-unit-base.yml b/.github/workflows/_test-unit-base.yml index 25c6d4a7019..9fd81b27f3b 100644 --- a/.github/workflows/_test-unit-base.yml +++ b/.github/workflows/_test-unit-base.yml @@ -45,12 +45,18 @@ jobs: name: Run tests runs-on: ubuntu-latest timeout-minutes: ${{ inputs.timeout-minutes }} + outputs: + decision: ${{ steps.changes.outputs.decision }} steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false + - name: Detect backend-relevant changes + id: changes + uses: ./.github/actions/detect-backend-changes + - name: Set up Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: @@ -72,16 +78,19 @@ jobs: ${{ runner.os }}-uv- - name: Install dependencies + if: steps.changes.outputs.decision != 'skip' run: | .github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router - name: Generate Prisma client + if: steps.changes.outputs.decision != 'skip' env: PRISMA_BINARY_CACHE_DIR: ${{ runner.temp }}/prisma-cache run: | uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma - name: Run tests + if: steps.changes.outputs.decision != 'skip' env: TEST_PATH: ${{ inputs.test-path }} MAX_FAILURES: ${{ inputs.max-failures }} @@ -114,7 +123,7 @@ jobs: fi - name: Save coverage report - if: always() + if: always() && steps.changes.outputs.decision != 'skip' uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: coverage-${{ inputs.artifact-name }}-${{ github.run_id }}-${{ github.run_attempt }} @@ -124,7 +133,7 @@ jobs: upload-coverage: name: Upload coverage to Codecov needs: run - if: always() + if: always() && needs.run.outputs.decision != 'skip' runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index 17efbf90339..a1772102b89 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -4,9 +4,11 @@ on: push: branches: - main + - litellm_internal_staging pull_request: branches: - main + - litellm_internal_staging # Allow CodSpeed to trigger backtest performance analysis # in order to generate initial data workflow_dispatch: @@ -21,8 +23,8 @@ concurrency: jobs: benchmarks: - runs-on: ubuntu-latest - timeout-minutes: 15 + runs-on: ubuntu-24.04 + timeout-minutes: 60 steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 @@ -48,6 +50,8 @@ jobs: uv run --frozen --no-default-groups --with pytest==8.3.5 --with pytest-codspeed==4.3.0 + --with "mcp>=1.26.0,<2.0" + --with "a2a-sdk>=1.1.0,<2.0" pytest -p pytest_codspeed.plugin tests/benchmarks/ diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 4834775e329..0ad84cd3ceb 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -122,10 +122,28 @@ jobs: makeLatest = (!latestVersion || isAtLeast(newVersion, latestVersion)) ? "true" : "false"; } + try { + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/tags/${tag}`, + sha: commitHash, + }); + } catch (error) { + if (error.status !== 422) throw error; + const existing = await github.rest.git.getRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `tags/${tag}`, + }); + if (existing.data.object.sha !== commitHash) { + throw new Error(`Tag ${tag} already exists at ${existing.data.object.sha}, expected ${commitHash}`); + } + } + const response = await github.rest.repos.createRelease({ draft: true, generate_release_notes: true, - target_commitish: commitHash, name: tag, owner: context.repo.owner, prerelease: isPrerelease, @@ -138,11 +156,21 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, release_id: response.data.id, + tag_name: tag, body: updatedBody, draft: false, - make_latest: makeLatest, }); + if (!isPrerelease) { + await github.rest.repos.updateRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: response.data.id, + tag_name: tag, + make_latest: makeLatest, + }); + } + } catch (error) { core.setFailed(error.message); } diff --git a/.github/workflows/create_daily_oss_branch.yml b/.github/workflows/create_daily_oss_branch.yml new file mode 100644 index 00000000000..43de4a0e75f --- /dev/null +++ b/.github/workflows/create_daily_oss_branch.yml @@ -0,0 +1,61 @@ +name: Create Daily OSS Branch + +on: + schedule: + - cron: "0 16 * * 1-5" # 9am PT during daylight saving time, weekdays. + workflow_dispatch: + inputs: + date: + description: "Branch date in YYYY_MM_DD format. Defaults to today's UTC date." + required: false + type: string + +permissions: + contents: write + +jobs: + create-oss-branch: + if: github.repository == 'BerriAI/litellm' + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Create dated OSS branch + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REQUESTED_DATE: ${{ inputs.date }} + run: | + set -euo pipefail + + if [ -n "${REQUESTED_DATE}" ]; then + if ! echo "${REQUESTED_DATE}" | grep -Eq '^[0-9]{4}_[0-9]{2}_[0-9]{2}$'; then + echo "::error::date must use YYYY_MM_DD format, got '${REQUESTED_DATE}'" + exit 1 + fi + BRANCH_DATE="${REQUESTED_DATE}" + else + BRANCH_DATE="$(date -u +'%Y_%m_%d')" + fi + + BRANCH_NAME="litellm_oss_daily_${BRANCH_DATE}" + echo "Creating branch: ${BRANCH_NAME}" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git fetch origin main "${BRANCH_NAME}" || true + + if git show-ref --verify --quiet "refs/remotes/origin/${BRANCH_NAME}"; then + echo "Branch ${BRANCH_NAME} already exists. Skipping creation." + exit 0 + fi + + git checkout -b "${BRANCH_NAME}" origin/main + git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "${BRANCH_NAME}" + echo "Successfully created and pushed branch: ${BRANCH_NAME}" diff --git a/.github/workflows/guard-main-branch.yml b/.github/workflows/guard-main-branch.yml index 21aad18d298..aa4968f0c1e 100644 --- a/.github/workflows/guard-main-branch.yml +++ b/.github/workflows/guard-main-branch.yml @@ -31,12 +31,12 @@ jobs: echo "PR head repo: $HEAD_REPO" echo "PR head branch: $HEAD_REF" if [ "$HEAD_REPO" != "$BASE_REPO" ]; then - echo "::error::PRs to main must originate from the canonical repository ($BASE_REPO), not a fork ($HEAD_REPO). External contributors should open PRs against the 'litellm_oss_staging' branch instead." + echo "::error::PRs to main must originate from the canonical repository ($BASE_REPO), not a fork ($HEAD_REPO). External contributors should open PRs against the current daily OSS branch (named litellm_oss_daily_YYYY_MM_DD; a fresh one is cut each weekday, so target the most recent) instead." exit 1 fi if [ "$HEAD_REF" = "litellm_internal_staging" ] || [[ "$HEAD_REF" == litellm_hotfix_?* ]]; then echo "Allowed source branch." exit 0 fi - echo "::error::PRs to main must originate from 'litellm_internal_staging' or a 'litellm_hotfix_*' branch. Got: '$HEAD_REF'. If this is a contribution, retarget the PR against 'litellm_oss_staging' instead." + echo "::error::PRs to main must originate from 'litellm_internal_staging' or a 'litellm_hotfix_*' branch. Got: '$HEAD_REF'. If this is a contribution, retarget the PR against the current daily OSS branch (named litellm_oss_daily_YYYY_MM_DD; a fresh one is cut each weekday, so target the most recent) instead." exit 1 diff --git a/.github/workflows/helm_unit_test.yml b/.github/workflows/helm_unit_test.yml index 06836b1d1cd..5b9d20d97f3 100644 --- a/.github/workflows/helm_unit_test.yml +++ b/.github/workflows/helm_unit_test.yml @@ -38,4 +38,6 @@ jobs: echo "Helm unittest plugin integrity verified: $ACTUAL_SHA" - name: Run unit tests - run: helm unittest -f 'tests/*.yaml' deploy/charts/litellm-helm + run: | + helm unittest -f 'tests/*.yaml' helm/litellm-helm + helm unittest -f 'tests/*.yaml' helm/litellm diff --git a/.github/workflows/oss_daily_guardrails.yml b/.github/workflows/oss_daily_guardrails.yml new file mode 100644 index 00000000000..950c51c9b60 --- /dev/null +++ b/.github/workflows/oss_daily_guardrails.yml @@ -0,0 +1,50 @@ +name: OSS Daily Guardrails + +on: + push: + branches: + - "litellm_oss_daily_20*" + pull_request: + branches: + - "litellm_oss_daily_20*" + - litellm_internal_staging + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + oss-safe-checks: + name: Run OSS daily safe checks + if: startsWith(github.ref_name, 'litellm_oss_daily_20') || startsWith(github.head_ref, 'litellm_oss_daily_20') || startsWith(github.base_ref, 'litellm_oss_daily_20') + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" + + - name: Run secret scan test + run: | + uv run --frozen --with 'pytest==9.0.2' pytest tests/litellm/test_no_hardcoded_secrets.py -v + + - name: Run Ruff + run: | + uv sync --frozen + cd litellm + uv run --no-sync ruff check . diff --git a/.github/workflows/test-linting.yml b/.github/workflows/test-linting.yml index 6deb28c95c7..0b5b0e9b976 100644 --- a/.github/workflows/test-linting.yml +++ b/.github/workflows/test-linting.yml @@ -48,7 +48,7 @@ jobs: - name: Install dependencies run: | - uv sync --frozen --group proxy-dev + uv sync --frozen --group proxy-dev --group e2e-dev # basedpyright resolves Prisma's generated client (litellm/proxy/schema.prisma) # only after `prisma generate` writes prisma/client.py et al. Without this the @@ -63,7 +63,7 @@ jobs: env: BASE_SHA: ${{ github.event.pull_request.base.sha }} run: | - git diff --name-only "$BASE_SHA"...HEAD -- 'litellm/**/*.py' | grep -v '^litellm/enterprise/' > "$RUNNER_TEMP/ruff_format_files.txt" || true + git diff --name-only --diff-filter=ACMR "$BASE_SHA"...HEAD -- 'litellm/**/*.py' | grep -v '^litellm/enterprise/' > "$RUNNER_TEMP/ruff_format_files.txt" || true if [ ! -s "$RUNNER_TEMP/ruff_format_files.txt" ]; then echo "No changed litellm Python files to check with ruff format." exit 0 @@ -107,6 +107,16 @@ jobs: run: | (uv run --no-sync basedpyright --outputjson || true) | uv run --no-sync python scripts/type_check_gate.py --base "$BASE_SHA" + - name: Check tests/e2e basedpyright (zero errors) + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: | + if git diff --name-only --diff-filter=ACMRD "$BASE_SHA"...HEAD -- 'tests/e2e/**/*.py' | grep -q .; then + uv run --no-sync basedpyright tests/e2e + else + echo "No changed tests/e2e Python files; skipping." + fi + - name: Check for circular imports run: | cd litellm diff --git a/.github/workflows/test-litellm-ui-build.yml b/.github/workflows/test-litellm-ui-build.yml index ce8d8cb9c95..525e2c5b949 100644 --- a/.github/workflows/test-litellm-ui-build.yml +++ b/.github/workflows/test-litellm-ui-build.yml @@ -36,79 +36,3 @@ jobs: - name: Build run: npm run build - - frontend-lint: - runs-on: ubuntu-latest - timeout-minutes: 8 - defaults: - run: - working-directory: ui/litellm-dashboard - - steps: - - name: Checkout repository - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Collect changed files - id: changed - env: - BASE_SHA: ${{ github.event.pull_request.base.sha }} - run: | - : > "$RUNNER_TEMP/prettier_files.txt" - : > "$RUNNER_TEMP/eslint_files.txt" - while IFS= read -r f; do - [ -f "$f" ] || continue - case "$f" in - *.js | *.jsx | *.ts | *.tsx | *.mjs | *.cjs) - printf '%s\n' "$f" >> "$RUNNER_TEMP/prettier_files.txt" - printf '%s\n' "$f" >> "$RUNNER_TEMP/eslint_files.txt" ;; - *.json | *.css | *.scss | *.md | *.mdx | *.yml | *.yaml | *.html) - printf '%s\n' "$f" >> "$RUNNER_TEMP/prettier_files.txt" ;; - esac - done < <(git diff --name-only --diff-filter=ACMR --relative "$BASE_SHA"...HEAD -- .) - if [ -s "$RUNNER_TEMP/prettier_files.txt" ] || [ -s "$RUNNER_TEMP/eslint_files.txt" ]; then - echo "has_files=true" >> "$GITHUB_OUTPUT" - else - echo "has_files=false" >> "$GITHUB_OUTPUT" - echo "No lintable UI files changed in this PR; nothing to check." - fi - - - name: Setup Node.js - if: steps.changed.outputs.has_files == 'true' - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 - with: - node-version: "20" - cache: "npm" - cache-dependency-path: ui/litellm-dashboard/package-lock.json - - - name: Install dependencies - if: steps.changed.outputs.has_files == 'true' - run: npm ci - - - name: Lint changed files (prettier + eslint) - if: steps.changed.outputs.has_files == 'true' - run: | - prettier_files=() - eslint_files=() - while IFS= read -r f; do prettier_files+=("$f"); done < "$RUNNER_TEMP/prettier_files.txt" - while IFS= read -r f; do eslint_files+=("$f"); done < "$RUNNER_TEMP/eslint_files.txt" - status=0 - if [ ${#prettier_files[@]} -gt 0 ]; then - echo "::group::Prettier (${#prettier_files[@]} files)" - npx prettier --check "${prettier_files[@]}" || { status=1; echo "::error::Unformatted files. Fix with: npm run format"; } - echo "::endgroup::" - fi - if [ ${#eslint_files[@]} -gt 0 ]; then - echo "::group::ESLint (${#eslint_files[@]} files)" - npx eslint --no-warn-ignored --pass-on-unpruned-suppressions "${eslint_files[@]}" || status=1 - echo "::endgroup::" - fi - exit $status - - - name: Check lint budgets - if: ${{ !cancelled() && steps.changed.outputs.has_files == 'true' }} - run: | - npx eslint . -f json -o "$RUNNER_TEMP/lint-report.json" || true - node scripts/check-lint-budgets.mjs "$RUNNER_TEMP/lint-report.json" eslint-budgets.json --check eslint-metrics.json diff --git a/.github/workflows/test-litellm-ui-lint.yml b/.github/workflows/test-litellm-ui-lint.yml new file mode 100644 index 00000000000..5a5c4709ca2 --- /dev/null +++ b/.github/workflows/test-litellm-ui-lint.yml @@ -0,0 +1,92 @@ +name: UI Lint +permissions: + contents: read + +on: + pull_request: + branches: + - main + - litellm_internal_staging + - litellm_oss_staging + - "litellm_**" + +jobs: + frontend-lint: + runs-on: ubuntu-latest + timeout-minutes: 8 + defaults: + run: + working-directory: ui/litellm-dashboard + + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Collect changed files + id: changed + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: | + : > "$RUNNER_TEMP/prettier_files.txt" + : > "$RUNNER_TEMP/eslint_files.txt" + while IFS= read -r f; do + [ -f "$f" ] || continue + case "$f" in + *.js | *.jsx | *.ts | *.tsx | *.mjs | *.cjs) + printf '%s\n' "$f" >> "$RUNNER_TEMP/prettier_files.txt" + printf '%s\n' "$f" >> "$RUNNER_TEMP/eslint_files.txt" ;; + *.json | *.css | *.scss | *.md | *.mdx | *.yml | *.yaml | *.html) + printf '%s\n' "$f" >> "$RUNNER_TEMP/prettier_files.txt" ;; + esac + done < <(git diff --name-only --diff-filter=ACMR --relative "$BASE_SHA"...HEAD -- .) + if [ -s "$RUNNER_TEMP/prettier_files.txt" ] || [ -s "$RUNNER_TEMP/eslint_files.txt" ]; then + echo "has_files=true" >> "$GITHUB_OUTPUT" + else + echo "has_files=false" >> "$GITHUB_OUTPUT" + echo "No lintable UI files changed in this PR; nothing to check." + fi + + - name: Setup Node.js + if: steps.changed.outputs.has_files == 'true' + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "20" + cache: "npm" + cache-dependency-path: ui/litellm-dashboard/package-lock.json + + - name: Install dependencies + if: steps.changed.outputs.has_files == 'true' + run: npm ci + + - name: Lint changed files (prettier + eslint) + if: steps.changed.outputs.has_files == 'true' + run: | + prettier_files=() + eslint_files=() + while IFS= read -r f; do prettier_files+=("$f"); done < "$RUNNER_TEMP/prettier_files.txt" + while IFS= read -r f; do eslint_files+=("$f"); done < "$RUNNER_TEMP/eslint_files.txt" + status=0 + if [ ${#prettier_files[@]} -gt 0 ]; then + echo "::group::Prettier (${#prettier_files[@]} files)" + npx prettier --check "${prettier_files[@]}" || { status=1; echo "::error::Unformatted files. Fix with: npm run format"; } + echo "::endgroup::" + fi + if [ ${#eslint_files[@]} -gt 0 ]; then + echo "::group::ESLint (${#eslint_files[@]} files)" + npx eslint --no-warn-ignored --pass-on-unpruned-suppressions "${eslint_files[@]}" || status=1 + echo "::endgroup::" + fi + exit $status + + - name: Check lint budgets + if: ${{ !cancelled() && steps.changed.outputs.has_files == 'true' }} + run: | + npx eslint . -f json -o "$RUNNER_TEMP/lint-report.json" || true + node scripts/check-lint-budgets.mjs "$RUNNER_TEMP/lint-report.json" eslint-budgets.json + + - name: Check for dead code (knip) + if: ${{ !cancelled() && steps.changed.outputs.has_files == 'true' }} + run: npm run knip diff --git a/.github/workflows/test-terraform-provider.yml b/.github/workflows/test-terraform-provider.yml new file mode 100644 index 00000000000..03d8ff3461c --- /dev/null +++ b/.github/workflows/test-terraform-provider.yml @@ -0,0 +1,113 @@ +name: Terraform Provider + +on: + push: + paths: + - "terraform/provider/**" + - ".github/workflows/test-terraform-provider.yml" + pull_request: + branches: + - main + - litellm_internal_staging + - litellm_oss_staging + - "litellm_**" + paths: + - "terraform/provider/**" + - "litellm/proxy/**" + - ".github/workflows/test-terraform-provider.yml" + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + provider-checks: + name: gofmt, vet, build, test + runs-on: ubuntu-latest + timeout-minutes: 10 + defaults: + run: + working-directory: terraform/provider + steps: + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false + + - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + with: + go-version-file: terraform/provider/go.mod + cache: true + cache-dependency-path: terraform/provider/go.sum + + - name: gofmt + run: | + UNFORMATTED=$(gofmt -l .) + if [ -n "${UNFORMATTED}" ]; then + echo "::error::gofmt required for: ${UNFORMATTED}" + exit 1 + fi + + - name: go vet + run: go vet ./... + + - name: Build + run: go build ./... + + - name: Test + run: go test -timeout 120s ./... + + endpoint-drift: + name: Provider endpoints vs proxy OpenAPI schema + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + + - name: Set up uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 + with: + version: "0.10.9" + + - name: Cache uv dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: | + ~/.cache/uv + .venv + key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }} + restore-keys: | + ${{ runner.os }}-uv- + + - name: Install dependencies + run: | + .github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router + + - name: Generate Prisma client + env: + PRISMA_BINARY_CACHE_DIR: ${{ runner.temp }}/prisma-cache + run: | + uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma + + - name: Generate proxy OpenAPI schema + run: | + uv run --no-sync python terraform/provider/tools/dump_openapi.py "${RUNNER_TEMP}/openapi.json" + + - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + with: + go-version-file: terraform/provider/go.mod + cache: true + cache-dependency-path: terraform/provider/go.sum + + - name: Audit provider endpoints against the schema + working-directory: terraform/provider + run: go run ./tools/endpointaudit -provider-dir ./litellm -spec "${RUNNER_TEMP}/openapi.json" diff --git a/.github/workflows/test-unit-documentation.yml b/.github/workflows/test-unit-documentation.yml index 4cef791a9b3..03f9f0a510b 100644 --- a/.github/workflows/test-unit-documentation.yml +++ b/.github/workflows/test-unit-documentation.yml @@ -32,6 +32,10 @@ jobs: path: docs/my-website persist-credentials: false + - name: Detect backend-relevant changes + id: changes + uses: ./.github/actions/detect-backend-changes + - name: Set up Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: @@ -53,10 +57,12 @@ jobs: ${{ runner.os }}-uv- - name: Install dependencies + if: steps.changes.outputs.decision != 'skip' run: | .github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router - name: Generate Prisma client + if: steps.changes.outputs.decision != 'skip' env: PRISMA_BINARY_CACHE_DIR: ${{ runner.temp }}/prisma-cache run: | @@ -64,6 +70,7 @@ jobs: # Run the same documentation tests that CircleCI ran (as direct Python scripts) - name: Run documentation validation tests + if: steps.changes.outputs.decision != 'skip' run: | uv run --no-sync python ./tests/documentation_tests/test_env_keys.py uv run --no-sync python ./tests/documentation_tests/test_router_settings.py diff --git a/.github/workflows/test-unit-proxy-legacy.yml b/.github/workflows/test-unit-proxy-legacy.yml index 8db218cd1fc..0068e80e584 100644 --- a/.github/workflows/test-unit-proxy-legacy.yml +++ b/.github/workflows/test-unit-proxy-legacy.yml @@ -49,6 +49,10 @@ jobs: with: persist-credentials: false + - name: Detect backend-relevant changes + id: changes + uses: ./.github/actions/detect-backend-changes + - name: Set up Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: @@ -70,16 +74,19 @@ jobs: ${{ runner.os }}-uv- - name: Install dependencies + if: steps.changes.outputs.decision != 'skip' run: | .github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router - name: Generate Prisma client + if: steps.changes.outputs.decision != 'skip' env: PRISMA_BINARY_CACHE_DIR: ${{ runner.temp }}/prisma-cache run: | uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma - name: Run tests - ${{ matrix.test-group.name }} + if: steps.changes.outputs.decision != 'skip' env: TEST_PATH: ${{ matrix.test-group.path }} run: | diff --git a/.github/workflows/test_server_root_path.yml b/.github/workflows/test_server_root_path.yml index ac363071d55..f59cee29893 100644 --- a/.github/workflows/test_server_root_path.yml +++ b/.github/workflows/test_server_root_path.yml @@ -16,6 +16,7 @@ jobs: timeout-minutes: 30 strategy: + fail-fast: false matrix: root_path: ["/api/v1", "/llmproxy"] @@ -108,8 +109,26 @@ jobs: - name: Install UI deps and Chromium working-directory: ui/litellm-dashboard run: | - npm ci - npx playwright install --with-deps chromium + retry() { + local attempt=1 + local max_attempts=4 + until "$@"; do + if [ "$attempt" -ge "$max_attempts" ]; then + echo "Command failed after $attempt attempts: $*" + return 1 + fi + echo "Attempt $attempt failed: $*. Retrying in $((attempt * 15))s..." + sleep $((attempt * 15)) + attempt=$((attempt + 1)) + done + } + + npm config set fetch-retries 5 + npm config set fetch-retry-mintimeout 20000 + npm config set fetch-retry-maxtimeout 120000 + + retry npm ci + retry npx playwright install --with-deps chromium - name: Run SERVER_ROOT_PATH redirect e2e working-directory: ui/litellm-dashboard diff --git a/.gitignore b/.gitignore index 5b7c6e5585b..e3ccf50508f 100644 --- a/.gitignore +++ b/.gitignore @@ -50,9 +50,10 @@ litellm/proxy/tests/package-lock.json ui/litellm-dashboard/.next ui/litellm-dashboard/node_modules ui/litellm-dashboard/next-env.d.ts -deploy/charts/litellm/*.tgz -deploy/charts/litellm/charts/* -deploy/charts/*.tgz +ui/litellm-dashboard/package.json +ui/litellm-dashboard/package-lock.json +helm/litellm-helm/*.tgz +helm/*.tgz litellm/proxy/vertex_key.json **/.vim/ **/node_modules @@ -85,12 +86,17 @@ litellm/proxy/db/migrations/* litellm/proxy/migrations/*config.yaml litellm/proxy/migrations/* litellm/proxy/to_delete_loadtest_work/* +config.yaml tests/litellm/litellm_core_utils/llm_cost_calc/log.txt tests/test_custom_dir/* +test.py +litellm_config.yaml +!.github/observatory/litellm_config.yaml .cursor litellm/proxy/to_delete_loadtest_work/* update_model_cost_map.py +tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py scripts/test_vertex_ai_search.py LAZY_LOADING_IMPROVEMENTS.md STABILIZATION_TODO.md @@ -124,6 +130,4 @@ crash.*.log # pytest coverage data .coverage -# _experimental/out UI build output -# (both componentized and non-componentized build the UI on project release) -litellm/proxy/_experimental/out/ \ No newline at end of file +ui/litellm-dashboard/out/ diff --git a/CLAUDE.md b/CLAUDE.md index eb32c2cd6da..9f708716c6d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8 +1,7 @@ -Do not write comments unless they are absolutely necessary to explain some very complex business logic. Please clean up if there are comments that are not absolutely necessary. Do not remove comments that are unrelated to the addition of the code of this PR - -Explanation: code comments are, in a way, a violation of DRY code. You must update logic in two locations to change the code and "hard to change" is literally the definition of tech debt. We should instead aim to write code that is intuitive to the reader, while being both easy to maintain and high performance +Do not write any comments (existing comments can stay) unless explicitly asked to in a user (not system) prompt Don't assume that the existing code is correct or the right way of doing things / good coding patterns. In fact, there are a lot of bad coding practices, overly complex code, code smells, etc. If something doesn't look right, speak up. Feel free to break existing patterns or question weird existing code to make new code high quality, as in: + - correct - secure - performant @@ -18,11 +17,15 @@ Same thing for bug fixes. The tests should make it so that this specific bug can `tests/test_litellm/` mirrors `litellm/` in a parallel path (see `tests/test_litellm/readme.md`). Name tests `test_.py`, but always match the existing test file in the directory you touch — many provider dirs use longer descriptive names (e.g. `test_anthropic_chat_transformation.py`) to avoid ambiguity across sibling folders. For bug fixes, extend the existing mapped test file rather than creating a new one. Only create a new test file for a new feature (provider, endpoint, or transformation module) that has no mapped test yet, following that directory's naming convention (or `test_.py` if you're the first test there). One focused regression test beats many shallow ones -When creating PRs, don't set base to `main`. `litellm_internal_staging` serves that purpose +End-to-end tests belong in `tests/e2e/` and must follow the harness conventions documented in that directory's `CLAUDE.md` -Always use @.github/pull_request_template.md as a guide for your PR body +When creating PRs, don't set base to `main`. `litellm_internal_staging` serves that purpose for internal contributors; external / OSS contributions target the current daily OSS branch instead, named `litellm_oss_daily_YYYY_MM_DD` (a fresh one is cut each weekday, so use the most recent) -Never use `pytest` commands or the like as "Screenshots / Proof of Fix". We prefer curl'ing a live proxy instance running on localhost:4000 (I like to run it with `python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload --use_v2_migration_resolver 2>&1 | tee litellm.log`) and showing both the command run and the output. Also, it should hit real LLM provider APIs, not mocks, and cost real $$$ because that is the most realistic test. The proof of fix should be exactly what the end user / customer would see / do. The run logs in PR #27703 is a prime example of how to do it (not a huge fan of using a python test script that future me and the team will have no visibility into; I prefer just curl commands or a short list of bash commands (e.g., using `for`)). If it's a UI thing, just tell me which URLs to go to (e.g., http://localhost:4000/ui/?page=logs), where to click, what fields to fill out, etc. along with the other commands to run in an ordered list, and I'll do it myself and post the screenshots after you make the PR +When writing a PR body, treat the comments and imperative instructions inside @.github/pull_request_template.md as rules to follow, not just layout. Agent harnesses may strip HTML comments from copies of that file injected into context, so read .github/pull_request_template.md from disk before writing a PR body to make sure you see every comment rule + +If you're resolving a linear ticket, in the "## Linear ticket" section of the PR, say "Resolves LIT-1234", replacing "LIT-1234" with the actual ticket id that you're resolving. If you don't have the ticket id, don't make one up or search for it. Just leave the section blank + +Never use `pytest` commands or the like as "Screenshots / Proof of Fix". We prefer curl'ing a live proxy instance running on localhost:4000 (I like to run it with `python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload --use_v2_migration_resolver 2>&1 | tee litellm.log`; the Admin UI dev server is `npm run dev` in `ui/litellm-dashboard`, served on port 3000) and showing both the command run and the output. Also, it should hit real LLM provider APIs, not mocks, and cost real $$$ because that is the most realistic test. The proof of fix should be exactly what the end user / customer would see / do. The run logs in PR #27703 is a prime example of how to do it (not a huge fan of using a python test script that future me and the team will have no visibility into; I prefer just curl commands or a short list of bash commands (e.g., using `for`)). If it's a UI thing, just tell me which URLs to go to (e.g., http://localhost:4000/ui/?page=logs), where to click, what fields to fill out, etc. along with the other commands to run in an ordered list, and I'll do it myself and post the screenshots after you make the PR If you ever make public-facing PR descriptions, comments, issues, commit messages, etc., always follow these guidelines to sound less AI-y: - don't use emojis @@ -34,17 +37,23 @@ If you ever make public-facing PR descriptions, comments, issues, commit message Don't hesitate to use values in .env to get needed API keys and other secrets, as long as you never add them to conversation history, commit them, or include them in GitHub issues / PRs -Run tests, format your code, and lint your code before each commit +Python max line length is 120, not 88 -When you fix violations gated by `ruff-strict-budget.json` or `basedpyright-code-budget.json`, run `make lint-budget-update` and commit the lowered baselines so the ceilings ratchet down instead of leaving stale headroom +On a fresh worktree or clone, run `make bootstrap` before anything else. It provisions everything tests, `make pre-commit`, and a local proxy need + +Run tests before you commit. Also, run `make pre-commit` right before each commit, which generates types (as needed) and formats/lints your code. Any errors found must be fixed. It only runs when there are staged frontend and/or backend changes and calculates violations, generates types, etc. based on the worktree, so stage what you need or stash/delete unwanted files in litellm/ or ui/ (where backend and frontend lint run, respectively) before running it. If it fails because dashboard api types are stale, it already regenerated them for you. You just need to stage the schema.d.ts, re-run `make pre-commit` to confirm it passes, and commit + +When you fix violations gated by `ruff-strict-budget.json`, `type-discipline-budget.json`, or `basedpyright-code-budget.json`, run `make lint-budget-update` and commit the lowered limits so the ceilings ratchet down instead of leaving stale headroom. It measures the working tree, so it must contain exactly the fixes you're committing If you're trying to create a new function that relies on untyped stuff, instead of adding more Any's and pushing `reportAny` / `reportExplicitAny` closer to their basedpyright ceilings, just validate it in the caller with Pydantic (a model or `TypeAdapter` that returns the typed thing or raises will do) and then pass the now typed variable in If you get an LIT001 or LIT002 fail, refactor the code to follow functional programming best practices rather than introducing mutable data structures. For example, build values in one shot with comprehensions or generators wrapped in `tuple()` / `frozenset()` instead of seeding an empty `list`/`dict`/`set` and mutating it over time. Ideally `# mutable-ok` is never used; reach for it only as a genuine last resort when an immutable rewrite is truly impossible, and always pair it with a real reason -Ask to commit and push your work when you're done (or if you're confident that your code is good and works, just do it) +Every lint or type suppression must name the exact rule inside brackets and carry a reason comment, e.g. `# pyright: ignore[reportArgumentType] # stubs lack async overload` or `# noqa: TID251 # `. `# type: ignore` is banned (LIT009): pyrightconfig.json sets `enableTypeIgnoreComments` to false, so it silently does nothing -When you must use real LLM models to, for example, write e2e tests, write a QA runbook, etc., make sure to use the latest models (doesn't have to be smartest, can also be a modern small, fast one. No strong preference for smart vs fast here, just use something modern) as of the year and month of the current date. Do a web search as necessary to figure that out +Commit and push your work when you're done without asking + +When referencing or running models (coding, QA'ing, writing docs, writing tests, etc.), use the latest model in that model family unless otherwise specified; treat your training knowledge, memories, configs, and tests as stale, and determine the family's latest with model_prices_and_context_window.json or the web If you're an internal contributor, when creating a new PR, the typical flow is to branch off litellm_internal_staging and create a branch prefixed with litellm_. Do not create a branch prefixed with claude/ and generally do not have / in your branch names @@ -70,6 +79,7 @@ Follow these coding conventions for new/updated code (a three-line fix in a lega - No monster files or god objects - No file sprawl: deliberate file and folder structure - Standard over hand-rolled: use the official SDK or a library where one exists; where none does, follow industry standards instead of inventing local conventions +- API-fragmentation-aware: when logic must branch on which API surface produced or consumes data (e.g. chat completions vs Anthropic Messages vs Responses API shapes), proactively look for an existing shared helper (e.g. `litellm_core_utils/prompt_templates/factory.py`) before writing per-surface parsing in the new module; if none exists, add one there instead of duplicating the same format-detection logic in every new guardrail/integration Follow conventional commits for commit names and PR titles diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1080579d0fa..0202965ec4b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -322,7 +322,7 @@ npm run build ## Submitting Your PR 1. **Push your branch**: `git push origin your-feature-branch` -2. **Create a PR**: Go to GitHub and create a pull request +2. **Create a PR**: Go to GitHub and open a pull request against the current daily OSS branch, named `litellm_oss_daily_YYYY_MM_DD`. A fresh one is cut each weekday, so pick the most recent from the [branch list](https://github.com/BerriAI/litellm/branches/all?query=litellm_oss_daily). Do not target `main`. 3. **Fill out the PR template**: Provide clear description of changes 4. **Wait for review**: Maintainers will review and provide feedback 5. **Address feedback**: Make requested changes and push updates diff --git a/Dockerfile b/Dockerfile index b6fef1a21fc..bc0e6a5ca6f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ # syntax=docker/dockerfile:1.7 # Base image for building -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f # Runtime image -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. ARG UI_BUILD_IMAGE=node:20.18-alpine3.20@sha256:3488b10bf958af7125a176419d2d8a9937d895bf124012aae811651988d2ffe6 diff --git a/Makefile b/Makefile index 7701f54e15c..8b657dcb465 100644 --- a/Makefile +++ b/Makefile @@ -4,15 +4,17 @@ .PHONY: help test test-unit test-unit-llms test-unit-proxy-guardrails test-unit-proxy-core test-unit-proxy-misc \ test-unit-integrations test-unit-core-utils test-unit-other test-unit-root \ test-proxy-unit-a test-proxy-unit-b test-integration test-unit-helm \ - info lint lint-dev format \ - lint-basedpyright lint-basedpyright-budget-update \ + info lint lint-dev lint-checks format \ + lint-basedpyright lint-e2e-basedpyright lint-basedpyright-budget-update lint-type-discipline lint-type-discipline-budget-update \ lint-ruff-budget lint-ruff-budget-update lint-budget-update lint-gate \ install-dev install-proxy-dev install-test-deps install-hooks \ - install-helm-unittest check-circular-imports check-import-safety + install-helm-unittest check-circular-imports check-import-safety pre-commit \ + lint-install lint-fetch-base bootstrap # Default target help: @echo "Available commands:" + @echo " make bootstrap - Provision a fresh clone/worktree" @echo " make install-dev - Install development dependencies" @echo " make install-proxy-dev - Install proxy development dependencies" @echo " make install-dev-ci - Install dev dependencies (CI-compatible, pins OpenAI)" @@ -20,17 +22,19 @@ help: @echo " make install-test-deps - Install the full local test environment" @echo " make install-helm-unittest - Install helm unittest plugin" @echo " make install-hooks - Install git hooks (Conventional Commits + Branches)" + @echo " make pre-commit - Run CI-equivalent lint on staged files (run before committing)" @echo " make format - Apply ruff format code formatting" @echo " make format-check - Check ruff format code formatting (matches CI)" @echo " make lint - Run all linting (Ruff, basedpyright, format check, circular imports, import safety)" @echo " make lint-ruff - Run Ruff linting only" @echo " make lint-basedpyright - Run basedpyright strict, gated by per-rule error counts" - @echo " make lint-basedpyright-budget-update - Re-capture the basedpyright per-rule budget (ratchet)" + @echo " make lint-e2e-basedpyright - Run basedpyright over tests/e2e (zero errors allowed)" + @echo " make lint-basedpyright-budget-update - Ratchet basedpyright limits down by what this branch fixed" @echo " make lint-format - Check ruff format formatting (matches CI)" - @echo " make lint-ruff-budget - Gate the codebase total of each strict ruff rule against its ceiling" + @echo " make lint-ruff-budget - Gate the codebase total of each strict ruff rule against its limit" @echo " make lint-gate - Strict ruff gate in CI-parity mode (fetches staging, simulates the merge)" - @echo " make lint-ruff-budget-update - Re-capture per-rule baselines in ruff-strict-budget.json (ratchet)" - @echo " make lint-budget-update - Re-capture all ratchet budgets (ruff + basedpyright)" + @echo " make lint-ruff-budget-update - Ratchet ruff-strict-budget.json limits down by what this branch fixed" + @echo " make lint-budget-update - Ratchet all budgets down (ruff + type-discipline + basedpyright)" @echo " make check-circular-imports - Check for circular imports" @echo " make check-import-safety - Check import safety" @echo " make test - Run all tests" @@ -51,13 +55,34 @@ help: UV := uv UV_RUN := $(UV) run --no-sync +LINT_DEP_INSTALL ?= install-dev +LINT_E2E_DEP_INSTALL ?= lint-install +LINT_DEP_BASE ?= lint-fetch-base +LINT_JOBS := $(shell sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4) +LINT_OUTPUT_SYNC := $(if $(filter output-sync,$(.FEATURES)),--output-sync=target,) + # Show info info: @echo "UV: $(UV)" # Installation targets +# --inexact: sync the locked deps without pruning anything already installed, so running +# a lint/format target doesn't tear the proxy extras (prisma, websockets, ...) out from +# under a dev's venv (CI installs its own env per job, so it is unaffected by this). install-dev: - $(UV) sync --frozen + $(UV) sync --inexact --frozen + +bootstrap: + $(UV) sync --inexact --frozen --extra proxy --group proxy-dev --group e2e-dev + $(UV_RUN) python scripts/prisma_generate_if_needed.py + cd ui/litellm-dashboard && npm ci --no-audit --no-fund + @main_root=$$(git worktree list --porcelain | head -1 | sed 's/^worktree //'); \ + if [ "$$main_root" != "$$(git rev-parse --show-toplevel)" ] && [ -f "$$main_root/.env" ] && [ ! -f .env ]; then \ + cp "$$main_root/.env" .env && echo "bootstrap: copied .env from $$main_root"; \ + else \ + echo "bootstrap: .env left untouched"; \ + fi + @echo "bootstrap: done" install-proxy-dev: $(UV) sync --frozen --group proxy-dev --extra proxy @@ -83,15 +108,40 @@ install-hooks: # Formatting # Wrap width is ruff.toml's single source of truth (line-length = 120), shared by the -# formatter, E501, and the import sorter so there's no 88-vs-120 split to reconcile. +# formatter and the import sorter so there's no 88-vs-120 split to reconcile. format: install-dev cd litellm && $(UV_RUN) ruff format --exclude '/enterprise/' . && cd .. format-check: install-dev cd litellm && $(UV_RUN) ruff format --check --exclude '/enterprise/' . && cd .. +# Single fetch of the PR base so the delta-based gates below share one network round +# trip instead of each re-fetching when chained from `lint`. +lint-fetch-base: + git fetch origin litellm_internal_staging + +# Mirror test-linting.yml's lint job environment: the proxy-dev group plus a generated +# Prisma client, so basedpyright resolves the same modules CI does (without the generated +# client the DB wrappers typed against it degrade to Unknown, drifting the budget from +# CI's). --inexact tops up the venv instead of pruning the proxy extras gen:api and the +# running proxy need. +lint-install: + $(UV) sync --inexact --frozen --group proxy-dev --group e2e-dev + $(UV_RUN) python scripts/prisma_generate_if_needed.py + +# Diff-scoped format check, identical to test-linting.yml's "Check ruff format" step: +# only the litellm Python files changed vs the base are checked, so a pre-existing +# format issue elsewhere doesn't block an unrelated commit. +lint-format-check-changed: $(LINT_DEP_INSTALL) $(LINT_DEP_BASE) + @files=$$(git diff --name-only origin/litellm_internal_staging...HEAD -- 'litellm/**/*.py' | grep -v '^litellm/enterprise/' || true); \ + if [ -z "$$files" ]; then \ + echo "No changed litellm Python files to format-check."; \ + else \ + echo "$$files" | xargs $(UV_RUN) ruff format --check --exclude '/enterprise/'; \ + fi + # Linting targets -lint-ruff: install-dev +lint-ruff: $(LINT_DEP_INSTALL) cd litellm && $(UV_RUN) ruff check . && cd .. # faster linter for developing ... @@ -126,11 +176,20 @@ lint-ruff-FULL-dev: install-dev if [ -n "$$files" ]; then echo "$$files" | xargs $(UV_RUN) ruff check; \ else echo "No changed .py files to check."; fi -lint-basedpyright: install-dev - git fetch origin litellm_internal_staging +lint-basedpyright: $(LINT_DEP_INSTALL) $(LINT_DEP_BASE) ($(UV_RUN) basedpyright --outputjson || true) | $(UV_RUN) python scripts/type_check_gate.py --base origin/litellm_internal_staging -lint-basedpyright-budget-update: install-dev +lint-e2e-basedpyright: $(LINT_E2E_DEP_INSTALL) + $(UV_RUN) basedpyright tests/e2e + +# Type-discipline budget (mutable collections / casts / type guards / kwargs / +# unexplained suppressions), the test-linting.yml step `make lint` used to omit. +lint-type-discipline: $(LINT_DEP_INSTALL) $(LINT_DEP_BASE) + $(UV_RUN) python scripts/type_discipline_gate.py --base origin/litellm_internal_staging + +# --update lowers each limit by what this branch fixed since its branch point, so +# it needs the base ref fetched to resolve the merge-base. +lint-basedpyright-budget-update: install-dev lint-fetch-base ($(UV_RUN) basedpyright --outputjson || true) | $(UV_RUN) python scripts/type_check_gate.py --update lint-format: format-check @@ -140,28 +199,47 @@ lint-ruff-budget: install-dev # Strict gate, invoked the same way CI does in test-linting.yml so a local pass # means the CI check will pass too. -lint-gate: install-dev - git fetch origin litellm_internal_staging +lint-gate: $(LINT_DEP_INSTALL) $(LINT_DEP_BASE) $(UV_RUN) python scripts/ruff_strict_gate.py --base origin/litellm_internal_staging -lint-ruff-budget-update: install-dev +lint-ruff-budget-update: install-dev lint-fetch-base $(UV_RUN) python scripts/ruff_strict_gate.py --update -# Ratchet all budgets in one shot (ruff strict + basedpyright) -lint-budget-update: lint-ruff-budget-update lint-basedpyright-budget-update +lint-type-discipline-budget-update: install-dev lint-fetch-base + $(UV_RUN) python scripts/type_discipline_gate.py --update -check-circular-imports: install-dev +# Ratchet all budgets in one shot (ruff strict + type-discipline + basedpyright) +lint-budget-update: lint-ruff-budget-update lint-type-discipline-budget-update lint-basedpyright-budget-update + +check-circular-imports: $(LINT_DEP_INSTALL) cd litellm && $(UV_RUN) python ../tests/documentation_tests/test_circular_imports.py && cd .. -check-import-safety: install-dev +check-import-safety: $(LINT_DEP_INSTALL) @$(UV_RUN) python -c "from litellm import *; print('[from litellm import *] OK! no issues!');" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) -# Combined linting (matches test-linting.yml workflow) -lint: format-check lint-ruff lint-basedpyright check-circular-imports check-import-safety lint-ruff-budget +# Combined linting, isomorphic to test-linting.yml's lint job so a local pass means a +# green CI lint: it installs the same env (proxy-dev + generated Prisma client) and then +# runs the diff-scoped ruff format check, whole-tree ruff check, the strict-rule / +# type-discipline / basedpyright budgets as a delta vs the base, then the circular-import +# and import-safety checks. Steps that compare against the base resolve it the same way CI +# does (merge-base with origin/litellm_internal_staging). Setup (env sync, Prisma client, +# base fetch) runs once up front; the checks themselves are independent, so a sub-make +# fans them out with -j and the fast ones finish under basedpyright's shadow. +lint: lint-install lint-fetch-base + $(MAKE) -j $(LINT_JOBS) $(LINT_OUTPUT_SYNC) LINT_DEP_INSTALL= LINT_E2E_DEP_INSTALL= LINT_DEP_BASE= lint-checks + +lint-checks: lint-format-check-changed lint-ruff lint-gate lint-type-discipline lint-basedpyright lint-e2e-basedpyright check-circular-imports check-import-safety # Faster linting for local development (only checks changed code) lint-dev: lint-format-changed check-circular-imports check-import-safety +# Run the gating CI checks against your staged files right before committing. Mirrors +# test-linting.yml (Python), test-litellm-ui-build.yml's frontend-lint (dashboard), and +# check-ui-api-types.yml (API-type drift), skipping any whose files you didn't stage. +# Not auto-installed as a git hook so it never slows an unrelated human commit. +pre-commit: + ./scripts/pre_commit_lint.sh + # Testing targets test: install-test-deps $(UV_RUN) pytest tests/ @@ -205,7 +283,7 @@ test-integration: install-test-deps $(UV_RUN) pytest tests/ -k "not test_litellm" test-unit-helm: install-helm-unittest - helm unittest -f 'tests/*.yaml' deploy/charts/litellm-helm + helm unittest -f 'tests/*.yaml' helm/litellm-helm # LLM Translation testing targets test-llm-translation: install-test-deps diff --git a/README.md b/README.md index 90d3e944fcc..32b0160dbaa 100644 --- a/README.md +++ b/README.md @@ -552,17 +552,12 @@ The Terraform modules live at [`terraform/litellm/aws/`](./terraform/litellm/aws 2. Run dependent services `docker-compose up db prometheus` #### Backend -1. (In root) create virtual environment `python -m venv .venv` -2. Activate virtual environment `source .venv/bin/activate` -3. Install dependencies `uv sync --all-extras --group proxy-dev` -4. `uv run prisma generate` -5. `prisma generate` -6. Start proxy backend `python litellm/proxy/proxy_cli.py` +1. Run `make bootstrap` +2. Start proxy backend: `uv run python litellm/proxy/proxy_cli.py` #### Frontend -1. Navigate to `ui/litellm-dashboard` -2. Install dependencies `npm install` -3. Run `npm run dev` to start the dashboard +1. Navigate to `ui/litellm-dashboard` (dependencies were already installed w/ `make bootstrap`) +2. Start dashboard: `npm run dev` ### Verify Docker Image Signatures diff --git a/backend/Dockerfile b/backend/Dockerfile index 667bdb073eb..62bd8b56483 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,5 +1,5 @@ -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a FROM $UV_IMAGE AS uvbin diff --git a/backend/routes/allowlist.py b/backend/routes/allowlist.py index b67f7d42127..02574ca505d 100644 --- a/backend/routes/allowlist.py +++ b/backend/routes/allowlist.py @@ -46,6 +46,7 @@ BACKEND_PATH_PREFIXES: tuple[str, ...] = ( "/fallback", "/fallbacks", "/cache_settings", + "/coordination_redis/", "/cost_tracking", "/cost/", "/credentials", diff --git a/basedpyright-code-budget.json b/basedpyright-code-budget.json index f2b54e1f889..edfb3536ad3 100644 --- a/basedpyright-code-budget.json +++ b/basedpyright-code-budget.json @@ -1,194 +1,146 @@ { "reportAny": { - "baseline": 24989, - "slack": 2500 + "limit": 37484 }, "reportArgumentType": { - "baseline": 1814, - "slack": 180 + "limit": 2704 }, "reportAssignmentType": { - "baseline": 220, - "slack": 22 + "limit": 330 }, "reportAttributeAccessIssue": { - "baseline": 346, - "slack": 35 + "limit": 516 }, "reportCallIssue": { - "baseline": 87, - "slack": 10 + "limit": 124 }, "reportConstantRedefinition": { - "baseline": 39, - "slack": 4 + "limit": 59 }, "reportDeprecated": { - "baseline": 217, - "slack": 22 + "limit": 326 }, "reportDuplicateImport": { - "baseline": 28, - "slack": 3 + "limit": 42 }, "reportExplicitAny": { - "baseline": 6931, - "slack": 700 + "limit": 10397 }, "reportFunctionMemberAccess": { - "baseline": 7, - "slack": 3 + "limit": 11 }, "reportGeneralTypeIssues": { - "baseline": 151, - "slack": 15 + "limit": 227 }, "reportIncompatibleMethodOverride": { - "baseline": 52, - "slack": 5 + "limit": 78 }, "reportIncompatibleVariableOverride": { - "baseline": 8, - "slack": 3 + "limit": 12 }, "reportInconsistentOverload": { - "baseline": 12, - "slack": 3 + "limit": 18 }, "reportIndexIssue": { - "baseline": 26, - "slack": 3 + "limit": 37 }, "reportInvalidTypeForm": { - "baseline": 23, - "slack": 3 + "limit": 35 }, "reportInvalidTypeVarUse": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "reportMatchNotExhaustive": { - "baseline": 1, - "slack": 0 + "limit": 0 }, "reportMissingParameterType": { - "baseline": 3933, - "slack": 390 + "limit": 5900 }, "reportMissingTypeArgument": { - "baseline": 10612, - "slack": 1000 + "limit": 15903 }, "reportMissingTypeStubs": { - "baseline": 27, - "slack": 10 + "limit": 41 }, "reportOperatorIssue": { - "baseline": 6, - "slack": 3 + "limit": 0 }, "reportOptionalCall": { - "baseline": 4, - "slack": 3 + "limit": 0 }, "reportOptionalIterable": { - "baseline": 3, - "slack": 3 + "limit": 0 }, "reportOptionalMemberAccess": { - "baseline": 724, - "slack": 72 + "limit": 1085 }, "reportOptionalOperand": { - "baseline": 3, - "slack": 3 + "limit": 0 }, "reportOptionalSubscript": { - "baseline": 11, - "slack": 3 + "limit": 0 }, "reportPossiblyUnboundVariable": { - "baseline": 52, - "slack": 10 + "limit": 77 }, "reportPrivateUsage": { - "baseline": 1625, - "slack": 160 + "limit": 2438 }, "reportRedeclaration": { - "baseline": 8, - "slack": 3 + "limit": 12 }, "reportReturnType": { - "baseline": 126, - "slack": 100 + "limit": 225 }, "reportTypedDictNotRequiredAccess": { - "baseline": 20, - "slack": 3 + "limit": 27 }, "reportUndefinedVariable": { - "baseline": 2, - "slack": 3 + "limit": 0 }, "reportUnknownArgumentType": { - "baseline": 30603, - "slack": 3000 + "limit": 45894 }, "reportUnknownLambdaType": { - "baseline": 75, - "slack": 10 + "limit": 113 }, "reportUnknownMemberType": { - "baseline": 27037, - "slack": 2500 + "limit": 40539 }, "reportUnknownParameterType": { - "baseline": 13612, - "slack": 1000 + "limit": 20403 }, "reportUnknownVariableType": { - "baseline": 21445, - "slack": 2000 + "limit": 32141 }, "reportUnnecessaryCast": { - "baseline": 118, - "slack": 10 + "limit": 177 }, "reportUnnecessaryComparison": { - "baseline": 683, - "slack": 100 + "limit": 1025 }, "reportUnnecessaryContains": { - "baseline": 4, - "slack": 3 + "limit": 7 }, "reportUnnecessaryIsInstance": { - "baseline": 808, - "slack": 80 + "limit": 1209 }, "reportUntypedBaseClass": { - "baseline": 110, - "slack": 11 + "limit": 165 }, "reportUntypedFunctionDecorator": { - "baseline": 22, - "slack": 3 + "limit": 33 }, "reportUnusedClass": { - "baseline": 22, - "slack": 3 + "limit": 33 }, "reportUnusedFunction": { - "baseline": 137, - "slack": 10 + "limit": 206 }, "reportUnusedImport": { - "baseline": 670, - "slack": 50 + "limit": 1005 }, "reportUnusedVariable": { - "baseline": 865, - "slack": 50 + "limit": 1297 } } diff --git a/codecov.yaml b/codecov.yaml index f5acdd39136..bc0b3604329 100644 --- a/codecov.yaml +++ b/codecov.yaml @@ -15,6 +15,16 @@ ignore: flag_management: default_rules: carryforward: true + # Dead flags no CI job uploads anymore: their carried-forward sessions were + # measured against old revisions, and the stale line maps mark comment lines + # of since-edited files as missed, sinking patch coverage on unrelated PRs. + individual_flags: + - name: proxy-mgmt-behavior + carryforward: false + - name: security + carryforward: false + - name: proxy-db-schema-migration + carryforward: false component_management: individual_components: diff --git a/deploy/azure_resource_manager/azure_marketplace.zip b/deploy/azure_resource_manager/azure_marketplace.zip deleted file mode 100644 index 34751258637..00000000000 Binary files a/deploy/azure_resource_manager/azure_marketplace.zip and /dev/null differ diff --git a/deploy/azure_resource_manager/azure_marketplace/createUiDefinition.json b/deploy/azure_resource_manager/azure_marketplace/createUiDefinition.json deleted file mode 100644 index 4eba73bdba4..00000000000 --- a/deploy/azure_resource_manager/azure_marketplace/createUiDefinition.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", - "handler": "Microsoft.Azure.CreateUIDef", - "version": "0.1.2-preview", - "parameters": { - "config": { - "isWizard": false, - "basics": { } - }, - "basics": [ ], - "steps": [ ], - "outputs": { }, - "resourceTypes": [ ] - } -} \ No newline at end of file diff --git a/deploy/azure_resource_manager/azure_marketplace/mainTemplate.json b/deploy/azure_resource_manager/azure_marketplace/mainTemplate.json deleted file mode 100644 index 114e855bf54..00000000000 --- a/deploy/azure_resource_manager/azure_marketplace/mainTemplate.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "imageName": { - "type": "string", - "defaultValue": "ghcr.io/berriai/litellm:main-latest" - }, - "containerName": { - "type": "string", - "defaultValue": "litellm-container" - }, - "dnsLabelName": { - "type": "string", - "defaultValue": "litellm" - }, - "portNumber": { - "type": "int", - "defaultValue": 4000 - } - }, - "resources": [ - { - "type": "Microsoft.ContainerInstance/containerGroups", - "apiVersion": "2021-03-01", - "name": "[parameters('containerName')]", - "location": "[resourceGroup().location]", - "properties": { - "containers": [ - { - "name": "[parameters('containerName')]", - "properties": { - "image": "[parameters('imageName')]", - "resources": { - "requests": { - "cpu": 1, - "memoryInGB": 2 - } - }, - "ports": [ - { - "port": "[parameters('portNumber')]" - } - ] - } - } - ], - "osType": "Linux", - "restartPolicy": "Always", - "ipAddress": { - "type": "Public", - "ports": [ - { - "protocol": "tcp", - "port": "[parameters('portNumber')]" - } - ], - "dnsNameLabel": "[parameters('dnsLabelName')]" - } - } - } - ] - } \ No newline at end of file diff --git a/deploy/azure_resource_manager/main.bicep b/deploy/azure_resource_manager/main.bicep deleted file mode 100644 index b104cefe1e1..00000000000 --- a/deploy/azure_resource_manager/main.bicep +++ /dev/null @@ -1,42 +0,0 @@ -param imageName string = 'ghcr.io/berriai/litellm:main-latest' -param containerName string = 'litellm-container' -param dnsLabelName string = 'litellm' -param portNumber int = 4000 - -resource containerGroupName 'Microsoft.ContainerInstance/containerGroups@2021-03-01' = { - name: containerName - location: resourceGroup().location - properties: { - containers: [ - { - name: containerName - properties: { - image: imageName - resources: { - requests: { - cpu: 1 - memoryInGB: 2 - } - } - ports: [ - { - port: portNumber - } - ] - } - } - ] - osType: 'Linux' - restartPolicy: 'Always' - ipAddress: { - type: 'Public' - ports: [ - { - protocol: 'tcp' - port: portNumber - } - ] - dnsNameLabel: dnsLabelName - } - } -} diff --git a/deploy/charts/litellm-helm/templates/configmap-litellm.yaml b/deploy/charts/litellm-helm/templates/configmap-litellm.yaml deleted file mode 100644 index acbe4e3a4b5..00000000000 --- a/deploy/charts/litellm-helm/templates/configmap-litellm.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- if .Values.proxyConfigMap.create }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "litellm.fullname" . }}-config -data: - config.yaml: | -{{ .Values.proxy_config | toYaml | indent 6 }} -{{- end }} diff --git a/docker/Dockerfile.database b/docker/Dockerfile.database index b3af953511d..4564ee403fe 100644 --- a/docker/Dockerfile.database +++ b/docker/Dockerfile.database @@ -1,10 +1,10 @@ # syntax=docker/dockerfile:1.7 # Base image for building -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f # Runtime image -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. ARG UI_BUILD_IMAGE=node:20.18-alpine3.20@sha256:3488b10bf958af7125a176419d2d8a9937d895bf124012aae811651988d2ffe6 diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index c24cb9008f0..1883e87be60 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -1,8 +1,8 @@ # syntax=docker/dockerfile:1.7 # Base images -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f ARG PROXY_EXTRAS_SOURCE=published ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. diff --git a/docker/build_admin_ui.sh b/docker/build_admin_ui.sh index efb2bac3535..68acdd78e3e 100755 --- a/docker/build_admin_ui.sh +++ b/docker/build_admin_ui.sh @@ -57,8 +57,6 @@ source ~/.nvm/nvm.sh nvm install v18.17.0 nvm use v18.17.0 -# copy _enterprise.json from this directory to /ui/litellm-dashboard, and rename it to ui_colors.json -cp enterprise/enterprise_ui/enterprise_colors.json ui/litellm-dashboard/ui_colors.json # cd in to /ui/litellm-dashboard cd ui/litellm-dashboard diff --git a/enterprise/README.md b/enterprise/README.md index f5eb5078e81..c708dad5a06 100644 --- a/enterprise/README.md +++ b/enterprise/README.md @@ -6,4 +6,4 @@ Code in this folder is licensed under a commercial license. Please review the [L 👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://enterprise.litellm.ai/demo?month=2024-02) -See all Enterprise Features here 👉 [Docs](https://docs.litellm.ai/docs/proxy/enterprise) +See all Enterprise Features here 👉 [Docs](https://docs.litellm.ai/docs/enterprise) diff --git a/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py index 89c3b854686..e7898cac565 100644 --- a/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py +++ b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py @@ -239,6 +239,7 @@ class BaseEmailLogger(CustomLogger): max_budget_info=max_budget_info, base_url=email_params.base_url, email_support_contact=email_params.support_contact, + email_footer=email_params.signature, ) await self.send_email( from_email=self.DEFAULT_LITELLM_EMAIL, @@ -311,6 +312,7 @@ class BaseEmailLogger(CustomLogger): max_budget_info=max_budget_info, base_url=email_params.base_url, email_support_contact=email_params.support_contact, + email_footer=email_params.signature, ) # Send email to all recipients @@ -379,6 +381,7 @@ class BaseEmailLogger(CustomLogger): alert_threshold=alert_threshold_str, base_url=email_params.base_url, email_support_contact=email_params.support_contact, + email_footer=email_params.signature, ) await self.send_email( from_email=self.DEFAULT_LITELLM_EMAIL, @@ -403,6 +406,7 @@ class BaseEmailLogger(CustomLogger): alert_threshold=alert_threshold_str, base_url=email_params.base_url, email_support_contact=email_params.support_contact, + email_footer=email_params.signature, ) await self.send_email( from_email=self.DEFAULT_LITELLM_EMAIL, @@ -473,9 +477,12 @@ class BaseEmailLogger(CustomLogger): _id = user_info.token or user_info.user_id or "default_id" _cache_key = f"email_budget_alerts:soft_budget_crossed:{_id}" - # Check if we've already sent this alert - result = await _cache.async_get_cache(key=_cache_key) - if result is None: + send_count = await _cache.async_increment_cache( + key=_cache_key, + value=1, + ttl=EMAIL_BUDGET_ALERT_TTL, + ) + if send_count is None or send_count <= 1: # Create WebhookEvent for soft budget alert event_message = f"Soft Budget Crossed - Total Soft Budget: ${user_info.soft_budget}" webhook_event = WebhookEvent( @@ -504,18 +511,12 @@ class BaseEmailLogger(CustomLogger): await self.send_team_soft_budget_alert_email(webhook_event) else: await self.send_soft_budget_alert_email(webhook_event) - - # Cache the alert to prevent duplicate sends - await _cache.async_set_cache( - key=_cache_key, - value="SENT", - ttl=EMAIL_BUDGET_ALERT_TTL, - ) except Exception as e: verbose_proxy_logger.error( f"Error sending soft budget alert email: {e}", exc_info=True, ) + await self._release_budget_alert_claim(_cache, _cache_key) return # For max_budget_alert, check if we've already sent an alert @@ -541,9 +542,12 @@ class BaseEmailLogger(CustomLogger): _id = user_info.token or user_info.user_id or "default_id" _cache_key = f"email_budget_alerts:max_budget_alert:{_id}" - # Check if we've already sent this alert - result = await _cache.async_get_cache(key=_cache_key) - if result is None: + send_count = await _cache.async_increment_cache( + key=_cache_key, + value=1, + ttl=EMAIL_BUDGET_ALERT_TTL, + ) + if send_count is None or send_count <= 1: # Calculate percentage percentage = int( EMAIL_BUDGET_ALERT_MAX_SPEND_ALERT_PERCENTAGE * 100 @@ -572,18 +576,12 @@ class BaseEmailLogger(CustomLogger): try: await self.send_max_budget_alert_email(webhook_event) - - # Cache the alert to prevent duplicate sends - await _cache.async_set_cache( - key=_cache_key, - value="SENT", - ttl=EMAIL_BUDGET_ALERT_TTL, - ) except Exception as e: verbose_proxy_logger.error( f"Error sending max budget alert email: {e}", exc_info=True, ) + await self._release_budget_alert_claim(_cache, _cache_key) return async def _handle_multi_threshold_max_budget_alert( @@ -613,10 +611,6 @@ class BaseEmailLogger(CustomLogger): f"email_budget_alerts:max_budget_alert:{threshold_pct}:{_id}" ) - result = await _cache.async_get_cache(key=_cache_key) - if result is not None: - continue - # Parse emails + auto-include owner emails = _parse_email_list(raw_emails) if user_info.user_email: @@ -630,6 +624,14 @@ class BaseEmailLogger(CustomLogger): continue recipient_emails = list(set(emails)) + send_count = await _cache.async_increment_cache( + key=_cache_key, + value=1, + ttl=EMAIL_BUDGET_ALERT_TTL, + ) + if send_count is not None and send_count > 1: + continue + event_message = f"Max Budget Alert - {threshold_pct}% of Maximum Budget Reached" webhook_event = WebhookEvent( event="max_budget_alert", @@ -656,16 +658,21 @@ class BaseEmailLogger(CustomLogger): threshold_pct=threshold_pct, recipient_emails=recipient_emails, ) - await _cache.async_set_cache( - key=_cache_key, - value="SENT", - ttl=EMAIL_BUDGET_ALERT_TTL, - ) except Exception as e: verbose_proxy_logger.error( f"Error sending multi-threshold max budget alert email for {threshold_pct}%: {e}", exc_info=True, ) + await self._release_budget_alert_claim(_cache, _cache_key) + + async def _release_budget_alert_claim(self, cache: DualCache, cache_key: str) -> None: + try: + await cache.async_delete_cache(key=cache_key) + except Exception: + verbose_proxy_logger.debug( + "Failed to release budget alert claim for %s; it expires with the TTL", + cache_key, + ) async def _get_email_params( self, @@ -912,9 +919,9 @@ class BaseEmailLogger(CustomLogger): """ Construct invitation link for the user - # http://localhost:4000/ui?invitation_id=7a096b3a-37c6-440f-9dd1-ba22e8043f6b + # http://localhost:4000/ui/onboarding?invitation_id=7a096b3a-37c6-440f-9dd1-ba22e8043f6b """ - return f"{base_url}/ui?invitation_id={invitation_id}" + return f"{base_url}/ui/onboarding?invitation_id={invitation_id}" async def send_email( self, diff --git a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py index ee7745d0add..b9ac98f515c 100644 --- a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py +++ b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py @@ -13,8 +13,11 @@ from litellm.constants import ( ) if TYPE_CHECKING: + from litellm.integrations.prometheus import PrometheusLogger + from litellm.proxy._types import LiteLLM_ManagedObjectTable from litellm.proxy.utils import PrismaClient, ProxyLogging from litellm.router import Router + from litellm.types.utils import LiteLLMBatch CHECK_BATCH_COST_USER_AGENT = "LiteLLM Proxy/CheckBatchCost" @@ -26,6 +29,7 @@ class CheckBatchCost: proxy_logging_obj: "ProxyLogging", prisma_client: "PrismaClient", llm_router: "Router", + track_unmanaged_vertex_batch_cost: bool = False, ): from litellm.proxy.utils import PrismaClient, ProxyLogging from litellm.router import Router @@ -33,6 +37,7 @@ class CheckBatchCost: self.proxy_logging_obj: ProxyLogging = proxy_logging_obj self.prisma_client: PrismaClient = prisma_client self.llm_router: Router = llm_router + self._track_unmanaged_vertex_batch_cost = track_unmanaged_vertex_batch_cost # Cached after the first poll cycle. Once we know the column is absent we skip # the guaranteed-failing primary query on every subsequent cycle. self._has_batch_processed_column: bool = True @@ -97,13 +102,196 @@ class CheckBatchCost: order={"created_at": "asc"}, ) - async def check_batch_cost(self): + @staticmethod + def _record_error( + prom_logger: Optional["PrometheusLogger"], error_type: str + ) -> None: + if prom_logger is not None: + prom_logger.record_check_batch_cost_error(error_type) + + def _resolve_job_routing( + self, + job: "LiteLLM_ManagedObjectTable", + prom_logger: Optional["PrometheusLogger"], + ) -> Optional[Tuple[str, str]]: """ - Check if the batch JOB has been tracked. - - get all status="validating" and file_purpose="batch" jobs - - check if batch is now complete - - if not, return False - - if so, return True + Resolve (model_id, batch_id) for a managed-object row, where model_id is a router + deployment id and batch_id is the raw provider batch id. + + Managed batches encode both in a base64 unified id. Unmanaged Vertex batches, created with + a raw gs:// input_file_id, store the raw provider job id as unified_object_id; when + track_unmanaged_vertex_batch_cost is enabled the model is derived from the gs:// path and + mapped to a configured vertex_ai deployment. Returns None (recording a metric) when the row + can't be routed. + """ + from litellm.proxy.openai_files_endpoints.common_utils import ( + _is_base64_encoded_unified_file_id, + get_batch_id_from_unified_batch_id, + get_model_id_from_unified_batch_id, + ) + + unified_object_id = job.unified_object_id + decoded = _is_base64_encoded_unified_file_id(unified_object_id) + if decoded: + model_id = get_model_id_from_unified_batch_id(decoded) + if model_id is None: + verbose_proxy_logger.info( + f"Skipping job {unified_object_id} because it is not a valid model id" + ) + self._record_error(prom_logger, "invalid_model_id") + return None + return model_id, get_batch_id_from_unified_batch_id(decoded) + + if self._track_unmanaged_vertex_batch_cost: + return self._resolve_unmanaged_vertex_routing(job, prom_logger) + + verbose_proxy_logger.info( + f"Skipping job {unified_object_id} because it is not a valid unified object id" + ) + self._record_error(prom_logger, "invalid_unified_id") + return None + + def _resolve_unmanaged_vertex_routing( + self, + job: "LiteLLM_ManagedObjectTable", + prom_logger: Optional["PrometheusLogger"], + ) -> Optional[Tuple[str, str]]: + from litellm.llms.vertex_ai.batches.transformation import ( + VertexAIBatchTransformation, + ) + + input_file_id = self._get_input_file_id(job) + if not VertexAIBatchTransformation.is_unmanaged_gcs_batch_input_file_id( + input_file_id + ): + verbose_proxy_logger.info( + f"Skipping job {job.unified_object_id}: not an unmanaged vertex batch " + "(no gs:// input_file_id with a publishers/ model path)" + ) + self._record_error(prom_logger, "invalid_unified_id") + return None + assert input_file_id is not None # narrowed by is_unmanaged_gcs_batch_input_file_id + + bare_model_name = VertexAIBatchTransformation.get_bare_model_name_from_gcs_file( + input_file_id + ) + deployment_id = self._get_vertex_ai_deployment_id_for_bare_model( + bare_model_name + ) + if deployment_id is None: + verbose_proxy_logger.info( + f"Skipping unmanaged vertex batch {job.unified_object_id}: no vertex_ai " + f"deployment configured for model {bare_model_name}" + ) + self._record_error(prom_logger, "unmanaged_no_matching_deployment") + return None + + return deployment_id, job.unified_object_id + + def _get_vertex_ai_deployment_id_for_bare_model( + self, bare_model_name: str + ) -> Optional[str]: + model_group = self.llm_router.resolve_model_name_from_model_id(bare_model_name) + deployment_id = ( + self._get_vertex_ai_deployment_id(model_group) if model_group else None + ) + if deployment_id is not None: + return deployment_id + + return self._get_vertex_ai_deployment_id_from_matching_deployments( + bare_model_name + ) + + def _get_vertex_ai_deployment_id_from_matching_deployments( + self, bare_model_name: str + ) -> Optional[str]: + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + for deployment in self.llm_router.get_model_list(model_name=None) or []: + litellm_params = deployment.get("litellm_params") or {} + actual_model = litellm_params.get("model") + if not isinstance(actual_model, str): + continue + if not self._is_bare_model_match(actual_model, bare_model_name): + continue + try: + _, llm_provider, _, _ = get_llm_provider( + model=actual_model, + custom_llm_provider=litellm_params.get("custom_llm_provider"), + ) + except Exception: + continue + if llm_provider != "vertex_ai": + continue + model_info = deployment.get("model_info") or {} + deployment_id = model_info.get("id") + if isinstance(deployment_id, str): + return deployment_id + return None + + @staticmethod + def _is_bare_model_match(actual_model: str, bare_model_name: str) -> bool: + return ( + actual_model == bare_model_name + or actual_model.endswith(f"/{bare_model_name}") + or actual_model.endswith(f":{bare_model_name}") + ) + + def _get_vertex_ai_deployment_id(self, model_group: str) -> Optional[str]: + """ + Returns the first deployment id for `model_group` whose provider is vertex_ai, + skipping deployments from other providers that happen to share the model group name. + """ + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + for deployment_id in self.llm_router.get_model_ids(model_name=model_group): + deployment_info = self.llm_router.get_deployment(model_id=deployment_id) + if deployment_info is None: + continue + try: + _, llm_provider, _, _ = get_llm_provider( + model=deployment_info.litellm_params.model, + custom_llm_provider=deployment_info.litellm_params.custom_llm_provider, + ) + except Exception: + continue + if llm_provider == "vertex_ai": + return deployment_id + return None + + @staticmethod + def _get_input_file_id(job: "LiteLLM_ManagedObjectTable") -> Optional[str]: + import json + + from litellm.types.utils import LiteLLMBatch + + file_object = job.file_object + if isinstance(file_object, str): + try: + file_object = json.loads(file_object) + except (json.JSONDecodeError, ValueError): + return None + if not isinstance(file_object, dict): + return None + try: + return LiteLLMBatch.model_validate(file_object).input_file_id + except Exception: + return None + + async def _track_completed_batch_cost( + self, + job: "LiteLLM_ManagedObjectTable", + response: "LiteLLMBatch", + model_id: str, + batch_id: str, + prom_logger: Optional["PrometheusLogger"], + ) -> Optional[Tuple[Optional[str], Optional[str]]]: + """ + Fetch a completed batch's results, compute cost/usage, and emit the + aretrieve_batch spend log. Returns (model_name, llm_provider) on + success, None when the job can't be routed to a deployment. Raises on + results-fetch or cost-computation failures so the caller can leave the + job unprocessed and retry it on a later poll. """ from litellm.batches.batch_utils import ( _get_file_content_as_dictionary, @@ -114,10 +302,186 @@ class CheckBatchCost: from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging from litellm.proxy.openai_files_endpoints.common_utils import ( _is_base64_encoded_unified_file_id, - get_batch_id_from_unified_batch_id, - get_model_id_from_unified_batch_id, ) + verbose_proxy_logger.info( + f"Batch ID: {batch_id} is complete, tracking cost and usage" + ) + + # aretrieve_batch is called with the raw provider batch ID, so response.id + # is the raw provider value (e.g. "batch_20260223-0518.234"). We need the + # unified base64 ID in the S3 log so downstream consumers can correlate it + # back to the batch they submitted via the proxy. + # + # CheckBatchCost builds its own LiteLLMLogging object (logging_obj below) and + # calls async_success_handler(result=response) directly. That handler calls + # _build_standard_logging_payload(response, ...) which reads response.id at + # that point — so setting response.id here is sufficient. + # + # The HTTP endpoint does this substitution via the managed files hook + # (async_post_call_success_hook). CheckBatchCost bypasses that hook entirely, + # so we do it explicitly here. + response.id = job.unified_object_id + + # This background job runs as default_user_id, so going through the HTTP endpoint + # would trigger check_managed_file_id_access and get 403. Instead, extract the raw + # provider file ID and call afile_content directly with deployment credentials. + raw_output_file_id = response.output_file_id + decoded = _is_base64_encoded_unified_file_id(raw_output_file_id) + if decoded: + try: + raw_output_file_id = decoded.split("llm_output_file_id,")[1].split(";")[0] + except (IndexError, AttributeError): + pass + + credentials = self.llm_router.get_deployment_credentials_with_provider(model_id) or {} + _file_content = await afile_content( + file_id=raw_output_file_id, + **credentials, + ) + + # Access content - handle both direct attribute and method call + if hasattr(_file_content, 'content'): + content_bytes = _file_content.content # type: ignore[union-attr] + elif hasattr(_file_content, 'read'): + content_bytes = await _file_content.read() # type: ignore[misc] + else: + content_bytes = _file_content # type: ignore[assignment] + + file_content_as_dict = _get_file_content_as_dictionary( + content_bytes # type: ignore[arg-type] + ) + + # Record output file size + if prom_logger and content_bytes: + try: + prom_logger.record_managed_file_size( + size_bytes=len(content_bytes), # type: ignore + purpose="batch", + file_type="output", + model=model_id, + ) + except Exception: + pass + + deployment_info = self.llm_router.get_deployment(model_id=model_id) + if deployment_info is None: + verbose_proxy_logger.info( + f"Skipping job {job.unified_object_id} because it is not a valid deployment info" + ) + self._record_error(prom_logger, "deployment_not_found") + return None + custom_llm_provider = deployment_info.litellm_params.custom_llm_provider + litellm_model_name = deployment_info.litellm_params.model + + model_name, llm_provider, _, _ = get_llm_provider( + model=litellm_model_name, + custom_llm_provider=custom_llm_provider, + ) + + # CheckBatchCost bypasses async_post_call_success_hook, so convert raw + # output/error file IDs to managed base64 IDs before the DB write here. + managed_files_hook = self.proxy_logging_obj.get_proxy_hook("managed_files") + if managed_files_hook is not None: + from litellm.proxy._types import UserAPIKeyAuth + _minimal_auth = UserAPIKeyAuth( + user_id=job.created_by or "default-user-id", + team_id=getattr(job, "team_id", None), + ) + for _file_attr in ["output_file_id", "error_file_id"]: + _raw_file_id = getattr(response, _file_attr, None) + if _raw_file_id and not _is_base64_encoded_unified_file_id(_raw_file_id): + try: + _unified_file_id = managed_files_hook.get_unified_output_file_id( + output_file_id=_raw_file_id, + model_id=model_id, + model_name=str(model_name) if model_name else deployment_info.model_name or None, + ) + await managed_files_hook.store_unified_file_id( + file_id=_unified_file_id, + file_object=None, + litellm_parent_otel_span=None, + model_mappings={model_id: _raw_file_id}, + user_api_key_dict=_minimal_auth, + ) + setattr(response, _file_attr, _unified_file_id) + verbose_proxy_logger.info( + f"CheckBatchCost: converted {_file_attr} " + f"{_raw_file_id!r} -> managed ID for batch {batch_id}" + ) + except Exception as _e: + verbose_proxy_logger.warning( + f"CheckBatchCost: failed to create managed file ID for " + f"{_file_attr}={_raw_file_id!r}: {_e}" + ) + + # Pass deployment model_info so custom batch pricing + # (input_cost_per_token_batches etc.) is used for cost calc + deployment_model_info = deployment_info.model_info.model_dump() if deployment_info.model_info else {} + batch_cost, batch_usage, batch_models = ( + await calculate_batch_cost_and_usage( + file_content_dictionary=file_content_as_dict, + custom_llm_provider=llm_provider, # type: ignore + model_name=model_name, + model_info=deployment_model_info, # type: ignore[arg-type] + ) + ) + logging_obj = LiteLLMLogging( + model=batch_models[0], + messages=[{"role": "user", "content": ""}], + stream=False, + call_type="aretrieve_batch", + start_time=datetime.now(), + litellm_call_id=str(uuid.uuid4()), + function_id=str(uuid.uuid4()), + ) + + creator_user_id = job.created_by + user_info = await self._get_user_info(batch_id, job.created_by) + + logging_obj.update_environment_variables( + litellm_params={ + # set the user-agent header so that S3 callback consumers can easily identify CheckBatchCost callbacks + "proxy_server_request": { + "headers": { + "user-agent": CHECK_BATCH_COST_USER_AGENT, + } + }, + "metadata": { + "user_api_key_user_id": creator_user_id, + **user_info, + }, + }, + optional_params={}, + ) + + await logging_obj.async_success_handler( + result=response, + batch_cost=batch_cost, + batch_usage=batch_usage, + batch_models=batch_models, + ) + + # Record batch duration (completed_at - created_at) + if prom_logger and response.completed_at and response.created_at: + duration_seconds = float(response.completed_at - response.created_at) + if duration_seconds >= 0: + prom_logger.record_managed_batch_duration( + duration_seconds=duration_seconds, + model=model_name, + api_provider=str(llm_provider) if llm_provider else None, + ) + + return model_name, str(llm_provider) if llm_provider else None + + async def check_batch_cost(self): + """ + Check if the batch JOB has been tracked. + - get all status="validating" and file_purpose="batch" jobs + - check if batch is now complete + - if not, return False + - if so, return True + """ try: from litellm.integrations.prometheus import PrometheusLogger prom_logger = PrometheusLogger.get_instance() @@ -172,31 +536,10 @@ class CheckBatchCost: else: jobs = await self._fallback_find_jobs() for job in jobs: - # get the model from the job - unified_object_id = job.unified_object_id - decoded_unified_object_id = _is_base64_encoded_unified_file_id( - unified_object_id - ) - if not decoded_unified_object_id: - verbose_proxy_logger.info( - f"Skipping job {unified_object_id} because it is not a valid unified object id" - ) - if prom_logger: - prom_logger.record_check_batch_cost_error("invalid_unified_id") - continue - else: - unified_object_id = decoded_unified_object_id - - model_id = get_model_id_from_unified_batch_id(unified_object_id) - batch_id = get_batch_id_from_unified_batch_id(unified_object_id) - - if model_id is None: - verbose_proxy_logger.info( - f"Skipping job {unified_object_id} because it is not a valid model id" - ) - if prom_logger: - prom_logger.record_check_batch_cost_error("invalid_model_id") + routing = self._resolve_job_routing(job, prom_logger) + if routing is None: continue + model_id, batch_id = routing verbose_proxy_logger.info( f"Querying model ID: {model_id} for cost and usage of batch ID: {batch_id}" @@ -213,7 +556,7 @@ class CheckBatchCost: ) except Exception as e: verbose_proxy_logger.info( - f"Skipping job {unified_object_id} because of error querying model ID: {model_id} for cost and usage of batch ID: {batch_id}: {e}" + f"Skipping job {job.unified_object_id} because of error querying model ID: {model_id} for cost and usage of batch ID: {batch_id}: {e}" ) if prom_logger: prom_logger.record_check_batch_cost_error("provider_retrieval_error") @@ -224,177 +567,26 @@ class CheckBatchCost: response.status == "completed" and response.output_file_id is not None ): - verbose_proxy_logger.info( - f"Batch ID: {batch_id} is complete, tracking cost and usage" - ) - - # aretrieve_batch is called with the raw provider batch ID, so response.id - # is the raw provider value (e.g. "batch_20260223-0518.234"). We need the - # unified base64 ID in the S3 log so downstream consumers can correlate it - # back to the batch they submitted via the proxy. - # - # CheckBatchCost builds its own LiteLLMLogging object (logging_obj below) and - # calls async_success_handler(result=response) directly. That handler calls - # _build_standard_logging_payload(response, ...) which reads response.id at - # that point — so setting response.id here is sufficient. - # - # The HTTP endpoint does this substitution via the managed files hook - # (async_post_call_success_hook). CheckBatchCost bypasses that hook entirely, - # so we do it explicitly here. - response.id = job.unified_object_id - - # This background job runs as default_user_id, so going through the HTTP endpoint - # would trigger check_managed_file_id_access and get 403. Instead, extract the raw - # provider file ID and call afile_content directly with deployment credentials. - raw_output_file_id = response.output_file_id - decoded = _is_base64_encoded_unified_file_id(raw_output_file_id) - if decoded: - try: - raw_output_file_id = decoded.split("llm_output_file_id,")[1].split(";")[0] - except (IndexError, AttributeError): - pass - - credentials = self.llm_router.get_deployment_credentials_with_provider(model_id) or {} - _file_content = await afile_content( - file_id=raw_output_file_id, - **credentials, - ) - - # Access content - handle both direct attribute and method call - if hasattr(_file_content, 'content'): - content_bytes = _file_content.content # type: ignore[union-attr] - elif hasattr(_file_content, 'read'): - content_bytes = await _file_content.read() # type: ignore[misc] - else: - content_bytes = _file_content # type: ignore[assignment] - - file_content_as_dict = _get_file_content_as_dictionary( - content_bytes # type: ignore[arg-type] - ) - - # Record output file size - if prom_logger and content_bytes: - try: - prom_logger.record_managed_file_size( - size_bytes=len(content_bytes), # type: ignore - purpose="batch", - file_type="output", - model=model_id, - ) - except Exception: - pass - - deployment_info = self.llm_router.get_deployment(model_id=model_id) - if deployment_info is None: - verbose_proxy_logger.info( - f"Skipping job {unified_object_id} because it is not a valid deployment info" + try: + tracked = await self._track_completed_batch_cost( + job=job, + response=response, + model_id=model_id, + batch_id=batch_id, + prom_logger=prom_logger, ) - if prom_logger: - prom_logger.record_check_batch_cost_error("deployment_not_found") + except Exception as tracking_err: + verbose_proxy_logger.error( + f"CheckBatchCost: failed to track cost for batch {batch_id} " + f"(job {job.id}); leaving it unprocessed so the next poll retries: {tracking_err}" + ) + self._record_error(prom_logger, "cost_tracking_error") + continue + if tracked is None: continue - custom_llm_provider = deployment_info.litellm_params.custom_llm_provider - litellm_model_name = deployment_info.litellm_params.model - - model_name, llm_provider, _, _ = get_llm_provider( - model=litellm_model_name, - custom_llm_provider=custom_llm_provider, - ) - - # CheckBatchCost bypasses async_post_call_success_hook, so convert raw - # output/error file IDs to managed base64 IDs before the DB write here. - managed_files_hook = self.proxy_logging_obj.get_proxy_hook("managed_files") - if managed_files_hook is not None: - from litellm.proxy._types import UserAPIKeyAuth - _minimal_auth = UserAPIKeyAuth( - user_id=job.created_by or "default-user-id", - team_id=getattr(job, "team_id", None), - ) - for _file_attr in ["output_file_id", "error_file_id"]: - _raw_file_id = getattr(response, _file_attr, None) - if _raw_file_id and not _is_base64_encoded_unified_file_id(_raw_file_id): - try: - _unified_file_id = managed_files_hook.get_unified_output_file_id( - output_file_id=_raw_file_id, - model_id=model_id, - model_name=str(model_name) if model_name else deployment_info.model_name or None, - ) - await managed_files_hook.store_unified_file_id( - file_id=_unified_file_id, - file_object=None, - litellm_parent_otel_span=None, - model_mappings={model_id: _raw_file_id}, - user_api_key_dict=_minimal_auth, - ) - setattr(response, _file_attr, _unified_file_id) - verbose_proxy_logger.info( - f"CheckBatchCost: converted {_file_attr} " - f"{_raw_file_id!r} -> managed ID for batch {batch_id}" - ) - except Exception as _e: - verbose_proxy_logger.warning( - f"CheckBatchCost: failed to create managed file ID for " - f"{_file_attr}={_raw_file_id!r}: {_e}" - ) - - # Pass deployment model_info so custom batch pricing - # (input_cost_per_token_batches etc.) is used for cost calc - deployment_model_info = deployment_info.model_info.model_dump() if deployment_info.model_info else {} - batch_cost, batch_usage, batch_models = ( - await calculate_batch_cost_and_usage( - file_content_dictionary=file_content_as_dict, - custom_llm_provider=llm_provider, # type: ignore - model_name=model_name, - model_info=deployment_model_info, # type: ignore[arg-type] - ) - ) - logging_obj = LiteLLMLogging( - model=batch_models[0], - messages=[{"role": "user", "content": ""}], - stream=False, - call_type="aretrieve_batch", - start_time=datetime.now(), - litellm_call_id=str(uuid.uuid4()), - function_id=str(uuid.uuid4()), - ) - - creator_user_id = job.created_by - user_info = await self._get_user_info(batch_id, job.created_by) - - logging_obj.update_environment_variables( - litellm_params={ - # set the user-agent header so that S3 callback consumers can easily identify CheckBatchCost callbacks - "proxy_server_request": { - "headers": { - "user-agent": CHECK_BATCH_COST_USER_AGENT, - } - }, - "metadata": { - "user_api_key_user_id": creator_user_id, - **user_info, - }, - }, - optional_params={}, - ) - - await logging_obj.async_success_handler( - result=response, - batch_cost=batch_cost, - batch_usage=batch_usage, - batch_models=batch_models, - ) - - # Record batch duration (completed_at - created_at) - if prom_logger and response.completed_at and response.created_at: - duration_seconds = float(response.completed_at - response.created_at) - if duration_seconds >= 0: - prom_logger.record_managed_batch_duration( - duration_seconds=duration_seconds, - model=model_name, - api_provider=str(llm_provider) if llm_provider else None, - ) # Track this job for the final metrics summary - processed_models.append((model_name, str(llm_provider) if llm_provider else None)) + processed_models.append(tracked) # mark the job as complete try: @@ -413,6 +605,26 @@ class CheckBatchCost: f"CheckBatchCost: failed to mark job {job.id} complete in DB: {db_err}" ) + elif response.status in ("failed", "expired", "cancelled"): + try: + update_data = { + "status": response.status, + "file_object": response.model_dump_json(), + } + if self._has_batch_processed_column: + update_data["batch_processed"] = True + await self.prisma_client.db.litellm_managedobjecttable.update( + where={"id": job.id}, + data=update_data, + ) + verbose_proxy_logger.info( + f"CheckBatchCost: marked job {job.id} as {response.status} in DB" + ) + except Exception as db_err: + verbose_proxy_logger.error( + f"CheckBatchCost: failed to mark job {job.id} as {response.status} in DB: {db_err}" + ) + # Record polling run metrics (always, even if nothing was processed) if prom_logger: prom_logger.record_check_batch_cost_run( diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index af4870bb1a5..3f42867d90e 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -125,23 +125,33 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): "team_id": user_api_key_dict.team_id, "updated_by": user_api_key_dict.user_id, } + update_data = { + "model_mappings": json.dumps(model_mappings), + "flat_model_file_ids": list(model_mappings.values()), + "updated_by": user_api_key_dict.user_id, + } if file_object is not None: - db_data["file_object"] = file_object.model_dump_json() + file_object_json = file_object.model_dump_json() + db_data["file_object"] = file_object_json + update_data["file_object"] = file_object_json # Extract storage metadata from hidden params if present hidden_params = getattr(file_object, "_hidden_params", {}) or {} if "storage_backend" in hidden_params: db_data["storage_backend"] = hidden_params["storage_backend"] + update_data["storage_backend"] = hidden_params["storage_backend"] if "storage_url" in hidden_params: db_data["storage_url"] = hidden_params["storage_url"] + update_data["storage_url"] = hidden_params["storage_url"] verbose_logger.debug( f"Storage metadata: storage_backend={db_data.get('storage_backend')}, " f"storage_url={db_data.get('storage_url')}" ) - result = await self.prisma_client.db.litellm_managedfiletable.create( - data=db_data + result = await self.prisma_client.db.litellm_managedfiletable.upsert( + where={"unified_file_id": file_id}, + data={"create": db_data, "update": update_data}, ) verbose_logger.debug( f"LiteLLM Managed File object with id={file_id} stored in db: {result}" diff --git a/enterprise/pyproject.toml b/enterprise/pyproject.toml index 66f6aeb7abc..85ccbef752f 100644 --- a/enterprise/pyproject.toml +++ b/enterprise/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "litellm-enterprise" -version = "0.1.44" +version = "0.1.49" description = "Package for LiteLLM Enterprise features" readme = "README.md" requires-python = ">=3.9" @@ -26,7 +26,7 @@ required-version = ">=0.10.9" module-root = "" [tool.commitizen] -version = "0.1.44" +version = "0.1.49" version_files = [ "pyproject.toml:^version", "../pyproject.toml:litellm-enterprise==", diff --git a/gateway/Dockerfile b/gateway/Dockerfile index 716b2fa09d1..da2f2c9c1e0 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -1,5 +1,5 @@ -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a FROM $UV_IMAGE AS uvbin diff --git a/gateway/main.py b/gateway/main.py index 09d30f5da3f..61b885b27e4 100644 --- a/gateway/main.py +++ b/gateway/main.py @@ -25,17 +25,25 @@ DatabaseURLSettings.from_env().apply_to_env() from litellm.proxy.proxy_server import app -from gateway.routes.allowlist import GATEWAY_EXACT_PATHS, GATEWAY_PATH_PREFIXES +from gateway.routes.allowlist import ( + GATEWAY_EXACT_PATHS, + GATEWAY_MOUNT_PATHS, + GATEWAY_PATH_PREFIXES, +) def _is_gateway_route(route) -> bool: - """Keep the route on the gateway if its path is in the LLM data-plane surface.""" + """Keep the route on the gateway if its path is in the LLM data-plane surface. + + Prometheus registers /metrics as a Mount (``app.mount("/metrics", make_asgi_app())``), + so Mounts are matched against GATEWAY_MOUNT_PATHS instead of being dropped with + the UI static mounts. + """ path = getattr(route, "path", None) if path is None: return False if isinstance(route, Mount): - # Gateway never serves the static UI or its asset bundles. - return False + return path in GATEWAY_MOUNT_PATHS if path in GATEWAY_EXACT_PATHS: return True return any(path.startswith(prefix) for prefix in GATEWAY_PATH_PREFIXES) diff --git a/gateway/routes/allowlist.py b/gateway/routes/allowlist.py index 144bb4c473f..792a56a2cd8 100644 --- a/gateway/routes/allowlist.py +++ b/gateway/routes/allowlist.py @@ -106,7 +106,7 @@ GATEWAY_PATH_PREFIXES: tuple[str, ...] = ( # Health & ops "/health", "/metrics", - "/watsonx" + "/watsonx", ) GATEWAY_EXACT_PATHS: frozenset[str] = frozenset( @@ -120,3 +120,9 @@ GATEWAY_EXACT_PATHS: frozenset[str] = frozenset( "/test", } ) + +GATEWAY_MOUNT_PATHS: frozenset[str] = frozenset( + { + "/metrics", + } +) diff --git a/deploy/charts/litellm-helm/.helmignore b/helm/litellm-helm/.helmignore similarity index 100% rename from deploy/charts/litellm-helm/.helmignore rename to helm/litellm-helm/.helmignore diff --git a/deploy/charts/litellm-helm/Chart.lock b/helm/litellm-helm/Chart.lock similarity index 100% rename from deploy/charts/litellm-helm/Chart.lock rename to helm/litellm-helm/Chart.lock diff --git a/deploy/charts/litellm-helm/Chart.yaml b/helm/litellm-helm/Chart.yaml similarity index 100% rename from deploy/charts/litellm-helm/Chart.yaml rename to helm/litellm-helm/Chart.yaml diff --git a/deploy/charts/litellm-helm/README.md b/helm/litellm-helm/README.md similarity index 100% rename from deploy/charts/litellm-helm/README.md rename to helm/litellm-helm/README.md diff --git a/deploy/charts/litellm-helm/charts/postgresql-14.3.1.tgz b/helm/litellm-helm/charts/postgresql-14.3.1.tgz similarity index 100% rename from deploy/charts/litellm-helm/charts/postgresql-14.3.1.tgz rename to helm/litellm-helm/charts/postgresql-14.3.1.tgz diff --git a/deploy/charts/litellm-helm/charts/redis-18.19.1.tgz b/helm/litellm-helm/charts/redis-18.19.1.tgz similarity index 100% rename from deploy/charts/litellm-helm/charts/redis-18.19.1.tgz rename to helm/litellm-helm/charts/redis-18.19.1.tgz diff --git a/deploy/charts/litellm-helm/ci/test-values.yaml b/helm/litellm-helm/ci/test-values.yaml similarity index 100% rename from deploy/charts/litellm-helm/ci/test-values.yaml rename to helm/litellm-helm/ci/test-values.yaml diff --git a/deploy/charts/litellm-helm/templates/NOTES.txt b/helm/litellm-helm/templates/NOTES.txt similarity index 100% rename from deploy/charts/litellm-helm/templates/NOTES.txt rename to helm/litellm-helm/templates/NOTES.txt diff --git a/deploy/charts/litellm-helm/templates/_helpers.tpl b/helm/litellm-helm/templates/_helpers.tpl similarity index 91% rename from deploy/charts/litellm-helm/templates/_helpers.tpl rename to helm/litellm-helm/templates/_helpers.tpl index 25b02dd5f37..469d52c03a7 100644 --- a/deploy/charts/litellm-helm/templates/_helpers.tpl +++ b/helm/litellm-helm/templates/_helpers.tpl @@ -76,10 +76,13 @@ so fall back to "default" (or an explicit override) to avoid a cyclic dependency {{- end }} {{/* -Get redis service name +Get redis service name. +The bundled Redis subchart only serves sentinel in "replication" architecture +(it rejects standalone + sentinel outright), and in that mode the sentinel +Service is named "-redis", not "-redis-master". */}} {{- define "litellm.redis.serviceName" -}} -{{- if and (eq .Values.redis.architecture "standalone") .Values.redis.sentinel.enabled -}} +{{- if .Values.redis.sentinel.enabled -}} {{- printf "%s-%s" .Release.Name (default "redis" .Values.redis.nameOverride | trunc 63 | trimSuffix "-") -}} {{- else -}} {{- printf "%s-%s-master" .Release.Name (default "redis" .Values.redis.nameOverride | trunc 63 | trimSuffix "-") -}} diff --git a/helm/litellm-helm/templates/configmap-litellm.yaml b/helm/litellm-helm/templates/configmap-litellm.yaml new file mode 100644 index 00000000000..03e4f620206 --- /dev/null +++ b/helm/litellm-helm/templates/configmap-litellm.yaml @@ -0,0 +1,22 @@ +{{- if .Values.proxyConfigMap.create }} +{{- $config := deepCopy .Values.proxy_config }} +{{- if and .Values.redis.enabled (dig "coordination" "enabled" true .Values.redis) }} +{{- $generalSettings := (get $config "general_settings") | default dict }} +{{- if not (hasKey $generalSettings "coordination_redis") }} +{{- $coordinationRedis := dict "host" "os.environ/REDIS_HOST" "port" "os.environ/REDIS_PORT" "password" "os.environ/REDIS_PASSWORD" }} +{{- if .Values.redis.sentinel.enabled }} +{{- $sentinelNode := list (include "litellm.redis.serviceName" .) (include "litellm.redis.port" . | int) }} +{{- $coordinationRedis = dict "sentinel_nodes" (list $sentinelNode) "service_name" (default "mymaster" .Values.redis.sentinel.masterSet) "password" "os.environ/REDIS_PASSWORD" }} +{{- end }} +{{- $_ := set $generalSettings "coordination_redis" $coordinationRedis }} +{{- $_ := set $config "general_settings" $generalSettings }} +{{- end }} +{{- end }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "litellm.fullname" . }}-config +data: + config.yaml: | +{{ $config | toYaml | indent 6 }} +{{- end }} diff --git a/deploy/charts/litellm-helm/templates/deployment.yaml b/helm/litellm-helm/templates/deployment.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/deployment.yaml rename to helm/litellm-helm/templates/deployment.yaml diff --git a/deploy/charts/litellm-helm/templates/extra-resources.yaml b/helm/litellm-helm/templates/extra-resources.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/extra-resources.yaml rename to helm/litellm-helm/templates/extra-resources.yaml diff --git a/deploy/charts/litellm-helm/templates/hpa.yaml b/helm/litellm-helm/templates/hpa.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/hpa.yaml rename to helm/litellm-helm/templates/hpa.yaml diff --git a/deploy/charts/litellm-helm/templates/ingress.yaml b/helm/litellm-helm/templates/ingress.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/ingress.yaml rename to helm/litellm-helm/templates/ingress.yaml diff --git a/deploy/charts/litellm-helm/templates/keda.yaml b/helm/litellm-helm/templates/keda.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/keda.yaml rename to helm/litellm-helm/templates/keda.yaml diff --git a/deploy/charts/litellm-helm/templates/migrations-job.yaml b/helm/litellm-helm/templates/migrations-job.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/migrations-job.yaml rename to helm/litellm-helm/templates/migrations-job.yaml diff --git a/deploy/charts/litellm-helm/templates/poddisruptionbudget.yaml b/helm/litellm-helm/templates/poddisruptionbudget.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/poddisruptionbudget.yaml rename to helm/litellm-helm/templates/poddisruptionbudget.yaml diff --git a/deploy/charts/litellm-helm/templates/secret-dbcredentials.yaml b/helm/litellm-helm/templates/secret-dbcredentials.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/secret-dbcredentials.yaml rename to helm/litellm-helm/templates/secret-dbcredentials.yaml diff --git a/deploy/charts/litellm-helm/templates/secret-masterkey.yaml b/helm/litellm-helm/templates/secret-masterkey.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/secret-masterkey.yaml rename to helm/litellm-helm/templates/secret-masterkey.yaml diff --git a/deploy/charts/litellm-helm/templates/service.yaml b/helm/litellm-helm/templates/service.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/service.yaml rename to helm/litellm-helm/templates/service.yaml diff --git a/deploy/charts/litellm-helm/templates/serviceaccount.yaml b/helm/litellm-helm/templates/serviceaccount.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/serviceaccount.yaml rename to helm/litellm-helm/templates/serviceaccount.yaml diff --git a/deploy/charts/litellm-helm/templates/servicemonitor.yaml b/helm/litellm-helm/templates/servicemonitor.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/servicemonitor.yaml rename to helm/litellm-helm/templates/servicemonitor.yaml diff --git a/deploy/charts/litellm-helm/templates/tests/test-connection.yaml b/helm/litellm-helm/templates/tests/test-connection.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/tests/test-connection.yaml rename to helm/litellm-helm/templates/tests/test-connection.yaml diff --git a/deploy/charts/litellm-helm/templates/tests/test-env-vars.yaml b/helm/litellm-helm/templates/tests/test-env-vars.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/tests/test-env-vars.yaml rename to helm/litellm-helm/templates/tests/test-env-vars.yaml diff --git a/deploy/charts/litellm-helm/templates/tests/test-servicemonitor.yaml b/helm/litellm-helm/templates/tests/test-servicemonitor.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/tests/test-servicemonitor.yaml rename to helm/litellm-helm/templates/tests/test-servicemonitor.yaml diff --git a/helm/litellm-helm/tests/coordination_redis_tests.yaml b/helm/litellm-helm/tests/coordination_redis_tests.yaml new file mode 100644 index 00000000000..0b58b1e6bc8 --- /dev/null +++ b/helm/litellm-helm/tests/coordination_redis_tests.yaml @@ -0,0 +1,143 @@ +suite: test coordination redis +templates: + - configmap-litellm.yaml + - deployment.yaml +tests: + - it: should not render coordination_redis when redis is disabled + template: configmap-litellm.yaml + set: + redis.enabled: false + asserts: + - notMatchRegex: + path: data["config.yaml"] + pattern: coordination_redis + + - it: should not emit redis env vars when redis is disabled + template: deployment.yaml + set: + redis.enabled: false + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_HOST + value: RELEASE-NAME-redis-master + any: true + + - it: should render coordination_redis pointing at the bundled redis when enabled + template: configmap-litellm.yaml + set: + redis.enabled: true + asserts: + - matchRegex: + path: data["config.yaml"] + pattern: "coordination_redis:\n host: os.environ/REDIS_HOST\n password: os.environ/REDIS_PASSWORD\n port: os.environ/REDIS_PORT\n" + - matchRegex: + path: data["config.yaml"] + pattern: "master_key: os.environ/PROXY_MASTER_KEY" + + - it: should emit redis env vars backing the coordination_redis os.environ refs + template: deployment.yaml + set: + redis.enabled: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_HOST + value: RELEASE-NAME-redis-master + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PORT + value: "6379" + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: RELEASE-NAME-redis + key: redis-password + + - it: should not render coordination_redis when coordination is opted out + template: configmap-litellm.yaml + set: + redis.enabled: true + redis.coordination.enabled: false + asserts: + - notMatchRegex: + path: data["config.yaml"] + pattern: coordination_redis + + - it: should keep emitting redis env vars when coordination is opted out + template: deployment.yaml + set: + redis.enabled: true + redis.coordination.enabled: false + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_HOST + value: RELEASE-NAME-redis-master + + - it: should not clobber a user supplied coordination_redis block + template: configmap-litellm.yaml + set: + redis.enabled: true + proxy_config.general_settings.coordination_redis: + url: os.environ/COORDINATION_REDIS_URL + asserts: + - matchRegex: + path: data["config.yaml"] + pattern: "coordination_redis:\n url: os.environ/COORDINATION_REDIS_URL\n" + - notMatchRegex: + path: data["config.yaml"] + pattern: "host: os.environ/REDIS_HOST" + + - it: should render sentinel_nodes and service_name in sentinel mode + template: configmap-litellm.yaml + set: + redis.enabled: true + redis.architecture: replication + redis.sentinel.enabled: true + asserts: + # The sentinel Service the redis subchart renders is "-redis", and a + # plain client cannot speak the sentinel protocol, so host/port must not appear + - matchRegex: + path: data["config.yaml"] + pattern: "coordination_redis:\n password: os.environ/REDIS_PASSWORD\n sentinel_nodes:\n - - RELEASE-NAME-redis\n - 26379\n service_name: mymaster\n" + - notMatchRegex: + path: data["config.yaml"] + pattern: "host: os.environ/REDIS_HOST" + + - it: should carry a custom sentinel masterSet into service_name + template: configmap-litellm.yaml + set: + redis.enabled: true + redis.architecture: replication + redis.sentinel.enabled: true + redis.sentinel.masterSet: litellm-master + asserts: + - matchRegex: + path: data["config.yaml"] + pattern: "service_name: litellm-master" + + - it: should point REDIS_HOST at the sentinel service in sentinel mode + template: deployment.yaml + set: + redis.enabled: true + redis.architecture: replication + redis.sentinel.enabled: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_HOST + value: RELEASE-NAME-redis + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PORT + value: "26379" diff --git a/deploy/charts/litellm-helm/tests/deployment_command_args_labels_tests.yaml b/helm/litellm-helm/tests/deployment_command_args_labels_tests.yaml similarity index 100% rename from deploy/charts/litellm-helm/tests/deployment_command_args_labels_tests.yaml rename to helm/litellm-helm/tests/deployment_command_args_labels_tests.yaml diff --git a/deploy/charts/litellm-helm/tests/deployment_tests.yaml b/helm/litellm-helm/tests/deployment_tests.yaml similarity index 100% rename from deploy/charts/litellm-helm/tests/deployment_tests.yaml rename to helm/litellm-helm/tests/deployment_tests.yaml diff --git a/deploy/charts/litellm-helm/tests/hpa_tests.yaml b/helm/litellm-helm/tests/hpa_tests.yaml similarity index 100% rename from deploy/charts/litellm-helm/tests/hpa_tests.yaml rename to helm/litellm-helm/tests/hpa_tests.yaml diff --git a/deploy/charts/litellm-helm/tests/ingress_tests.yaml b/helm/litellm-helm/tests/ingress_tests.yaml similarity index 100% rename from deploy/charts/litellm-helm/tests/ingress_tests.yaml rename to helm/litellm-helm/tests/ingress_tests.yaml diff --git a/deploy/charts/litellm-helm/tests/masterkey-secret_tests.yaml b/helm/litellm-helm/tests/masterkey-secret_tests.yaml similarity index 100% rename from deploy/charts/litellm-helm/tests/masterkey-secret_tests.yaml rename to helm/litellm-helm/tests/masterkey-secret_tests.yaml diff --git a/deploy/charts/litellm-helm/tests/migrations-job_tests.yaml b/helm/litellm-helm/tests/migrations-job_tests.yaml similarity index 100% rename from deploy/charts/litellm-helm/tests/migrations-job_tests.yaml rename to helm/litellm-helm/tests/migrations-job_tests.yaml diff --git a/deploy/charts/litellm-helm/tests/pdb_tests.yaml b/helm/litellm-helm/tests/pdb_tests.yaml similarity index 100% rename from deploy/charts/litellm-helm/tests/pdb_tests.yaml rename to helm/litellm-helm/tests/pdb_tests.yaml diff --git a/deploy/charts/litellm-helm/tests/service_tests.yaml b/helm/litellm-helm/tests/service_tests.yaml similarity index 100% rename from deploy/charts/litellm-helm/tests/service_tests.yaml rename to helm/litellm-helm/tests/service_tests.yaml diff --git a/deploy/charts/litellm-helm/values.yaml b/helm/litellm-helm/values.yaml similarity index 91% rename from deploy/charts/litellm-helm/values.yaml rename to helm/litellm-helm/values.yaml index 6e30a6af444..d3821a547e5 100644 --- a/deploy/charts/litellm-helm/values.yaml +++ b/helm/litellm-helm/values.yaml @@ -331,12 +331,28 @@ postgresql: # secretKeys: # userPasswordKey: password -# requires cache: true in config file -# either enable this or pass a secret for REDIS_HOST, REDIS_PORT, REDIS_PASSWORD or REDIS_URL -# with cache: true to use existing redis instance +# Redis is the proxy's coordination store: cross-pod tpm/rpm rate limits, spend +# tracking, and the pod lock manager. Enabling this deploys the bundled Redis +# subchart, wires REDIS_HOST / REDIS_PORT / REDIS_PASSWORD into the proxy, and +# renders a `general_settings.coordination_redis` block into the proxy config. +# +# To point at an existing Redis instead, leave `enabled: false` and pass a +# secret for REDIS_HOST, REDIS_PORT, REDIS_PASSWORD or REDIS_URL; the proxy +# falls back to those env vars for coordination. Set `cache: true` in the proxy +# config only if you also want LLM response caching, which is independent of +# coordination +# +# When `redis.sentinel.enabled` is set, the coordination block is rendered with +# `sentinel_nodes` and `service_name` (from `redis.sentinel.masterSet`) instead +# of host/port, because a plain Redis client cannot talk to the sentinel port redis: enabled: false architecture: standalone + coordination: + # Set to false to keep the bundled Redis for response caching only and leave + # `general_settings.coordination_redis` out of the rendered config. A + # `coordination_redis` block you define yourself in `proxy_config` always wins + enabled: true # Prisma migration job settings migrationJob: diff --git a/helm/litellm/templates/_helpers.tpl b/helm/litellm/templates/_helpers.tpl index 4319907883e..7c281aa158b 100644 --- a/helm/litellm/templates/_helpers.tpl +++ b/helm/litellm/templates/_helpers.tpl @@ -213,6 +213,10 @@ harmless no-op for the Job and authoritative for the app pods. */}} - name: DISABLE_SCHEMA_UPDATE value: "true" +{{/* These feed the proxy's coordination Redis (cross-pod rate limits, spend + tracking, pod lock manager) via its REDIS_* env fallback. An explicit + `general_settings.coordination_redis` block in proxy_config takes + precedence over anything emitted here. */}} {{- if $root.Values.redis.host }} - name: REDIS_HOST value: {{ $root.Values.redis.host | quote }} @@ -226,10 +230,11 @@ harmless no-op for the Job and authoritative for the app pods. key: {{ $root.Values.redis.passwordSecret.passwordKey | default "password" }} {{- end }} {{- if $root.Values.redis.cluster }} -{{/* The proxy's Cache() reads REDIS_CLUSTER_NODES as JSON and constructs a - RedisClusterCache when it's set (litellm/caching/caching.py:169-192). - We seed with the single configured endpoint — the cluster client - discovers the remaining nodes from CLUSTER SLOTS at startup. */}} +{{/* The proxy falls back to REDIS_CLUSTER_NODES (JSON) to build a cluster-mode + coordination client when `general_settings.coordination_redis` is absent + and no plain-Redis response cache is configured. We seed with the single + configured endpoint; the cluster client discovers the remaining nodes from + CLUSTER SLOTS at startup. */}} - name: REDIS_CLUSTER_NODES value: {{ printf "[{\"host\":%q,\"port\":%v}]" $root.Values.redis.host (int $root.Values.redis.port) | quote }} {{- end }} diff --git a/helm/litellm/templates/backend/deployment.yaml b/helm/litellm/templates/backend/deployment.yaml index b355db43540..8b4552bf302 100644 --- a/helm/litellm/templates/backend/deployment.yaml +++ b/helm/litellm/templates/backend/deployment.yaml @@ -45,11 +45,16 @@ spec: value: /app/config/config.yaml {{- end }} {{- include "litellm.envFrom" .Values.backend | nindent 10 }} - {{- if .Values.gateway.config.create }} + {{- if or .Values.gateway.config.create .Values.backend.volumeMounts }} volumeMounts: + {{- if .Values.gateway.config.create }} - name: gateway-config mountPath: /app/config/config.yaml subPath: config.yaml + {{- end }} + {{- with .Values.backend.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} {{- end }} {{- with .Values.backend.livenessProbe }} livenessProbe: @@ -61,11 +66,16 @@ spec: {{- end }} resources: {{- toYaml .Values.backend.resources | nindent 12 }} - {{- if .Values.gateway.config.create }} + {{- if or .Values.gateway.config.create .Values.backend.volumes }} volumes: + {{- if .Values.gateway.config.create }} - name: gateway-config configMap: name: {{ include "litellm.gateway.fullname" . }}-config + {{- end }} + {{- with .Values.backend.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} {{- with .Values.backend.nodeSelector }} nodeSelector: diff --git a/helm/litellm/templates/gateway/deployment.yaml b/helm/litellm/templates/gateway/deployment.yaml index 05ea4052159..bd491b69e0f 100644 --- a/helm/litellm/templates/gateway/deployment.yaml +++ b/helm/litellm/templates/gateway/deployment.yaml @@ -47,11 +47,16 @@ spec: value: {{ .Values.gateway.numWorkers | quote }} {{- end }} {{- include "litellm.envFrom" .Values.gateway | nindent 10 }} - {{- if .Values.gateway.config.create }} + {{- if or .Values.gateway.config.create .Values.gateway.volumeMounts }} volumeMounts: + {{- if .Values.gateway.config.create }} - name: gateway-config mountPath: /app/config/config.yaml subPath: config.yaml + {{- end }} + {{- with .Values.gateway.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} {{- end }} {{- with .Values.gateway.livenessProbe }} livenessProbe: @@ -63,11 +68,16 @@ spec: {{- end }} resources: {{- toYaml .Values.gateway.resources | nindent 12 }} - {{- if .Values.gateway.config.create }} + {{- if or .Values.gateway.config.create .Values.gateway.volumes }} volumes: + {{- if .Values.gateway.config.create }} - name: gateway-config configMap: name: {{ include "litellm.gateway.fullname" . }}-config + {{- end }} + {{- with .Values.gateway.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} {{- with .Values.gateway.nodeSelector }} nodeSelector: diff --git a/helm/litellm/templates/ui/deployment.yaml b/helm/litellm/templates/ui/deployment.yaml index b40b44cca53..79e9a3e43bb 100644 --- a/helm/litellm/templates/ui/deployment.yaml +++ b/helm/litellm/templates/ui/deployment.yaml @@ -46,6 +46,10 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} {{- include "litellm.envFrom" .Values.ui | nindent 10 }} + {{- with .Values.ui.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} {{- with .Values.ui.livenessProbe }} livenessProbe: {{- toYaml . | nindent 12 }} @@ -56,6 +60,10 @@ spec: {{- end }} resources: {{- toYaml .Values.ui.resources | nindent 12 }} + {{- with .Values.ui.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} {{- with .Values.ui.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/helm/litellm/tests/deployment_volumes_tests.yaml b/helm/litellm/tests/deployment_volumes_tests.yaml new file mode 100644 index 00000000000..3a64300b86c --- /dev/null +++ b/helm/litellm/tests/deployment_volumes_tests.yaml @@ -0,0 +1,172 @@ +suite: test deployment volumes and volumeMounts +templates: + - gateway/deployment.yaml + - gateway/configmap.yaml + - backend/deployment.yaml + - ui/deployment.yaml +values: + - ./values/required.yaml +tests: + - it: gateway renders only the config volume by default + template: gateway/deployment.yaml + asserts: + - equal: + path: spec.template.spec.volumes + value: + - name: gateway-config + configMap: + name: RELEASE-NAME-litellm-gateway-config + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: + - name: gateway-config + mountPath: /app/config/config.yaml + subPath: config.yaml + + - it: gateway merges user volumes and volumeMounts with the config volume + template: gateway/deployment.yaml + set: + gateway.volumes: + - name: custom-callbacks + configMap: + name: custom-callbacks + gateway.volumeMounts: + - name: custom-callbacks + mountPath: /app/custom_callbacks.py + subPath: custom_callbacks.py + asserts: + - equal: + path: spec.template.spec.volumes[0].name + value: gateway-config + - equal: + path: spec.template.spec.volumes[1] + value: + name: custom-callbacks + configMap: + name: custom-callbacks + - equal: + path: spec.template.spec.containers[0].volumeMounts[0].name + value: gateway-config + - equal: + path: spec.template.spec.containers[0].volumeMounts[1] + value: + name: custom-callbacks + mountPath: /app/custom_callbacks.py + subPath: custom_callbacks.py + + - it: gateway renders user volumes even when config creation is disabled + template: gateway/deployment.yaml + set: + gateway.config.create: false + gateway.volumes: + - name: certs + secret: + secretName: tls-certs + gateway.volumeMounts: + - name: certs + mountPath: /etc/certs + readOnly: true + asserts: + - equal: + path: spec.template.spec.volumes + value: + - name: certs + secret: + secretName: tls-certs + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: + - name: certs + mountPath: /etc/certs + readOnly: true + + - it: gateway omits volumes when config creation is disabled and no user volumes are set + template: gateway/deployment.yaml + set: + gateway.config.create: false + asserts: + - isNull: + path: spec.template.spec.volumes + - isNull: + path: spec.template.spec.containers[0].volumeMounts + + - it: backend merges user volumes and volumeMounts with the shared config volume + template: backend/deployment.yaml + set: + backend.volumes: + - name: sso-handler + configMap: + name: sso-handler + backend.volumeMounts: + - name: sso-handler + mountPath: /app/custom_sso.py + subPath: custom_sso.py + asserts: + - equal: + path: spec.template.spec.volumes[0].name + value: gateway-config + - equal: + path: spec.template.spec.volumes[1] + value: + name: sso-handler + configMap: + name: sso-handler + - equal: + path: spec.template.spec.containers[0].volumeMounts[1] + value: + name: sso-handler + mountPath: /app/custom_sso.py + subPath: custom_sso.py + + - it: backend renders user volumes even when config creation is disabled + template: backend/deployment.yaml + set: + gateway.config.create: false + backend.volumes: + - name: data + emptyDir: {} + backend.volumeMounts: + - name: data + mountPath: /data + asserts: + - equal: + path: spec.template.spec.volumes + value: + - name: data + emptyDir: {} + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: + - name: data + mountPath: /data + + - it: ui renders no volumes by default + template: ui/deployment.yaml + asserts: + - isNull: + path: spec.template.spec.volumes + - isNull: + path: spec.template.spec.containers[0].volumeMounts + + - it: ui renders user volumes and volumeMounts + template: ui/deployment.yaml + set: + ui.volumes: + - name: nginx-config + configMap: + name: custom-nginx + ui.volumeMounts: + - name: nginx-config + mountPath: /etc/nginx/conf.d + asserts: + - equal: + path: spec.template.spec.volumes + value: + - name: nginx-config + configMap: + name: custom-nginx + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: + - name: nginx-config + mountPath: /etc/nginx/conf.d diff --git a/helm/litellm/tests/redis_env_tests.yaml b/helm/litellm/tests/redis_env_tests.yaml new file mode 100644 index 00000000000..684d7071b35 --- /dev/null +++ b/helm/litellm/tests/redis_env_tests.yaml @@ -0,0 +1,109 @@ +suite: test redis coordination env vars +templates: + - gateway/deployment.yaml + - gateway/configmap.yaml + - backend/deployment.yaml +values: + - ./values/required.yaml +tests: + - it: gateway omits redis env vars when no host is configured + template: gateway/deployment.yaml + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_HOST + value: redis.example.com + any: true + - notContains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_CLUSTER_NODES + any: true + + - it: gateway emits host, port and password when redis is configured + template: gateway/deployment.yaml + set: + redis.host: redis.example.com + redis.port: 6380 + redis.passwordSecret.name: redis-secret + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_HOST + value: redis.example.com + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PORT + value: "6380" + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis-secret + key: password + + - it: backend emits the same redis env vars so both pods coordinate on one redis + template: backend/deployment.yaml + set: + redis.host: redis.example.com + redis.passwordSecret.name: redis-secret + redis.passwordSecret.passwordKey: redis-password + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_HOST + value: redis.example.com + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis-secret + key: redis-password + + - it: gateway omits REDIS_PASSWORD for an auth-less redis + template: gateway/deployment.yaml + set: + redis.host: redis.example.com + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_PASSWORD + any: true + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_HOST + value: redis.example.com + + - it: gateway seeds REDIS_CLUSTER_NODES from host and port in cluster mode + template: gateway/deployment.yaml + set: + redis.host: redis.example.com + redis.port: 6380 + redis.cluster: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_CLUSTER_NODES + value: '[{"host":"redis.example.com","port":6380}]' + + - it: gateway omits REDIS_CLUSTER_NODES when cluster mode is off + template: gateway/deployment.yaml + set: + redis.host: redis.example.com + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: REDIS_CLUSTER_NODES + any: true diff --git a/helm/litellm/tests/values/required.yaml b/helm/litellm/tests/values/required.yaml new file mode 100644 index 00000000000..21d3f7a5a6a --- /dev/null +++ b/helm/litellm/tests/values/required.yaml @@ -0,0 +1,4 @@ +database: + writer: + host: postgres.example.com + dbname: litellm diff --git a/helm/litellm/values.yaml b/helm/litellm/values.yaml index 934661643bd..a8f2d39663e 100644 --- a/helm/litellm/values.yaml +++ b/helm/litellm/values.yaml @@ -100,7 +100,18 @@ database: usernameKey: username passwordKey: password -# Optional Redis (caching, rate limiting). Leave host empty to disable. +# Optional Redis. Leave host empty to disable. +# +# This is the proxy's coordination store: cross-pod tpm/rpm rate limits, spend +# tracking, and the pod lock manager. The chart emits REDIS_HOST / REDIS_PORT / +# REDIS_PASSWORD, which the proxy picks up through its coordination Redis env +# fallback. Response caching is separate and off unless you enable it in +# `proxy_config.litellm_settings.cache`. +# +# For full control, define `general_settings.coordination_redis` in +# `proxy_config` (host/port/password/username/url/ssl/startup_nodes/ +# sentinel_nodes/sentinel_password/service_name, each accepting os.environ/VAR +# refs). An explicit block overrides these env vars. # # Set `cluster: true` for Redis Cluster mode (e.g. AWS ElastiCache Cluster, # self-hosted Redis Cluster). The chart emits REDIS_CLUSTER_NODES from @@ -124,6 +135,11 @@ gateway: extraEnv: [] # Add extra environment variables to the gateway envConfigMaps: [] # Add extra environment variables to the gateway from config maps envSecrets: [] # Add extra environment variables to the gateway from secrets + # Additional volumes on the gateway Deployment (e.g. a ConfigMap holding + # custom callback / SSO handler code, mounted next to the proxy config). + volumes: [] + # Additional volumeMounts on the gateway container. + volumeMounts: [] config: create: true proxy_config: {} @@ -167,6 +183,10 @@ backend: extraEnv: [] envConfigMaps: [] envSecrets: [] + # Additional volumes on the backend Deployment. + volumes: [] + # Additional volumeMounts on the backend container. + volumeMounts: [] image: repository: ghcr.io/berriai/litellm-backend tag: "" @@ -206,6 +226,10 @@ ui: extraEnv: [] envConfigMaps: [] envSecrets: [] + # Additional volumes on the ui Deployment. + volumes: [] + # Additional volumeMounts on the ui container. + volumeMounts: [] image: repository: ghcr.io/berriai/litellm-ui tag: "" diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260626120000_add_mcp_tool_search_enabled/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260626120000_add_mcp_tool_search_enabled/migration.sql new file mode 100644 index 00000000000..542677426ba --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260626120000_add_mcp_tool_search_enabled/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "LiteLLM_ObjectPermissionTable" ADD COLUMN "mcp_tool_search_enabled" BOOLEAN; diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260629000000_add_max_concurrent_requests_to_mcp_server_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260629000000_add_max_concurrent_requests_to_mcp_server_table/migration.sql new file mode 100644 index 00000000000..eeeecce741d --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260629000000_add_max_concurrent_requests_to_mcp_server_table/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "LiteLLM_MCPServerTable" ADD COLUMN "max_concurrent_requests" INTEGER; diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260630120000_add_token_exchange_to_mcp_servers/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260630120000_add_token_exchange_to_mcp_servers/migration.sql new file mode 100644 index 00000000000..dec5fccc319 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260630120000_add_token_exchange_to_mcp_servers/migration.sql @@ -0,0 +1,8 @@ +-- Timestamp sorts before some already-applied migrations; this is safe: the +-- runner is `prisma migrate deploy`, which applies every pending migration +-- regardless of name order (utils.py has an informational check for exactly +-- this), and IF NOT EXISTS keeps a re-apply idempotent. +-- AlterTable +ALTER TABLE "LiteLLM_MCPServerTable" ADD COLUMN IF NOT EXISTS "token_exchange_endpoint" TEXT; +ALTER TABLE "LiteLLM_MCPServerTable" ADD COLUMN IF NOT EXISTS "audience" TEXT; +ALTER TABLE "LiteLLM_MCPServerTable" ADD COLUMN IF NOT EXISTS "subject_token_type" TEXT; diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260630190000_add_budget_fallbacks_to_litellm_verification_token/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260630190000_add_budget_fallbacks_to_litellm_verification_token/migration.sql new file mode 100644 index 00000000000..1a5c16288de --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260630190000_add_budget_fallbacks_to_litellm_verification_token/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "LiteLLM_VerificationToken" ADD COLUMN IF NOT EXISTS "budget_fallbacks" JSONB NOT NULL DEFAULT '{}'; + +-- AlterTable +ALTER TABLE "LiteLLM_DeletedVerificationToken" ADD COLUMN IF NOT EXISTS "budget_fallbacks" JSONB NOT NULL DEFAULT '{}'; diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260703120000_add_token_exchange_profile_to_mcp_servers/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260703120000_add_token_exchange_profile_to_mcp_servers/migration.sql new file mode 100644 index 00000000000..6dda56c4fb3 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260703120000_add_token_exchange_profile_to_mcp_servers/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "LiteLLM_MCPServerTable" ADD COLUMN IF NOT EXISTS "token_exchange_profile" TEXT; diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260710000000_add_dcr_bridge_to_mcp_server_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260710000000_add_dcr_bridge_to_mcp_server_table/migration.sql new file mode 100644 index 00000000000..2cfabb9c02e --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260710000000_add_dcr_bridge_to_mcp_server_table/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "LiteLLM_MCPServerTable" ADD COLUMN "dcr_bridge" BOOLEAN; diff --git a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma index e21c0016491..fb4d8d0b5a3 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma +++ b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma @@ -279,6 +279,7 @@ model LiteLLM_ObjectPermissionTable { blocked_tools String[] @default([]) // Tool names blocked for any key/team/user with this permission mcp_toolsets String[] @default([]) // Toolset IDs granted to this key/team/user search_tools String[] @default([]) // search_tool_name values this key/team/user may call + mcp_tool_search_enabled Boolean? teams LiteLLM_TeamTable[] projects LiteLLM_ProjectTable[] verification_tokens LiteLLM_VerificationToken[] @@ -328,15 +329,23 @@ model LiteLLM_MCPServerTable { token_url String? registration_url String? oauth2_flow String? + token_exchange_endpoint String? + // Named for the RFC 8693 "audience" token-exchange request parameter (that flow only). + // RFC 8707 resource indicators are a separate concept, named "resource" in the v2 egress types. + audience String? + subject_token_type String? + token_exchange_profile String? allow_all_keys Boolean @default(false) available_on_public_internet Boolean @default(true) delegate_auth_to_upstream Boolean @default(false) oauth_passthrough Boolean @default(false) + dcr_bridge Boolean? is_byok Boolean @default(false) byok_description String[] @default([]) byok_api_key_help_url String? source_url String? timeout Float? + max_concurrent_requests Int? // BYOM submission lifecycle approval_status String? @default("active") submitted_by String? @@ -417,6 +426,7 @@ model LiteLLM_VerificationToken { access_group_ids String[] @default([]) model_spend Json @default("{}") model_max_budget Json @default("{}") + budget_fallbacks Json @default("{}") budget_id String? organization_id String? object_permission_id String? @@ -510,6 +520,7 @@ model LiteLLM_DeletedVerificationToken { access_group_ids String[] @default([]) model_spend Json @default("{}") model_max_budget Json @default("{}") + budget_fallbacks Json @default("{}") router_settings Json? @default("{}") budget_id String? organization_id String? diff --git a/litellm-proxy-extras/pyproject.toml b/litellm-proxy-extras/pyproject.toml index e2a86205fc5..a54db2ace65 100644 --- a/litellm-proxy-extras/pyproject.toml +++ b/litellm-proxy-extras/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "litellm-proxy-extras" -version = "0.4.74" +version = "0.4.76" description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package." readme = "README.md" requires-python = ">=3.9" @@ -26,7 +26,7 @@ required-version = ">=0.10.9" module-root = "" [tool.commitizen] -version = "0.4.74" +version = "0.4.76" version_files = [ "pyproject.toml:^version", "../pyproject.toml:litellm-proxy-extras==", diff --git a/litellm/__init__.py b/litellm/__init__.py index 15e95ded906..6e2a03b7c7c 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -263,6 +263,8 @@ azure_key: Optional[str] = None anthropic_key: Optional[str] = None replicate_key: Optional[str] = None bytez_key: Optional[str] = None +gdc_key: Optional[str] = None +gdc_api_base: Optional[str] = None cohere_key: Optional[str] = None infinity_key: Optional[str] = None clarifai_key: Optional[str] = None @@ -377,6 +379,7 @@ budget_duration: Optional[str] = ( None # proxy only - resets budget after fixed duration. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). ) default_soft_budget: float = DEFAULT_SOFT_BUDGET # by default all litellm proxy keys have a soft budget of 50.0 +budget_exceeded_throttle_percentage: Optional[float] = None forward_traceparent_to_llm_provider: bool = False @@ -586,6 +589,7 @@ gemini_models: Set = set() xai_models: Set = set() zai_models: Set = set() deepseek_models: Set = set() +tencent_models: Set = set() runwayml_models: Set = set() azure_ai_models: Set = set() jina_ai_models: Set = set() @@ -799,6 +803,8 @@ def add_known_models(model_cost_map: Optional[Dict] = None): fal_ai_models.add(key) elif value.get("litellm_provider") == "deepseek": deepseek_models.add(key) + elif value.get("litellm_provider") == "tencent": + tencent_models.add(key) elif value.get("litellm_provider") == "runwayml": runwayml_models.add(key) elif value.get("litellm_provider") == "meta_llama": @@ -1091,6 +1097,7 @@ models_by_provider: dict = { "zai": zai_models, "fal_ai": fal_ai_models, "deepseek": deepseek_models, + "tencent": tencent_models, "runwayml": runwayml_models, "mistral": mistral_chat_models, "azure_ai": azure_ai_models, @@ -1787,6 +1794,7 @@ if TYPE_CHECKING: from .llms.nvidia_nim.embed import ( NvidiaNimEmbeddingConfig as NvidiaNimEmbeddingConfig, ) + from .llms.gdc.chat.transformation import GDCGeminiConfig as GDCGeminiConfig # Type stubs for lazy-loaded config instances openaiOSeriesConfig: OpenAIOSeriesConfig @@ -1801,6 +1809,9 @@ if TYPE_CHECKING: from .llms.deepseek.chat.transformation import ( DeepSeekChatConfig as _DeepSeekChatConfig, ) + from .llms.tencent.chat.transformation import ( + TencentChatConfig as _TencentChatConfig, + ) from .llms.sap.chat.transformation import ( GenAIHubOrchestrationConfig as _GenAIHubOrchestrationConfig, ) @@ -1843,6 +1854,7 @@ if TYPE_CHECKING: # Type stubs for lazy-loaded config classes (to help mypy understand types) VLLMConfig: Type[_VLLMConfig] DeepSeekChatConfig: Type[_DeepSeekChatConfig] + TencentChatConfig: Type[_TencentChatConfig] GenAIHubOrchestrationConfig: Type[_GenAIHubOrchestrationConfig] GenAIHubEmbeddingConfig: Type[_GenAIHubEmbeddingConfig] AzureOpenAIO1Config: Type[_AzureOpenAIO1Config] diff --git a/litellm/_lazy_imports_registry.py b/litellm/_lazy_imports_registry.py index 4f131354d2e..488331e3895 100644 --- a/litellm/_lazy_imports_registry.py +++ b/litellm/_lazy_imports_registry.py @@ -284,6 +284,7 @@ LLM_CONFIG_NAMES = ( "LiteLLMProxyChatConfig", "VLLMConfig", "DeepSeekChatConfig", + "TencentChatConfig", "LMStudioChatConfig", "LmStudioEmbeddingConfig", "NscaleConfig", @@ -323,6 +324,7 @@ LLM_CONFIG_NAMES = ( "SnowflakeEmbeddingConfig", "AmazonNovaChatConfig", "SonioxAudioTranscriptionConfig", + "GDCGeminiConfig", ) # Types that support lazy loading via _lazy_import_types @@ -1095,6 +1097,7 @@ _LLM_CONFIGS_IMPORT_MAP = { ), "VLLMConfig": (".llms.vllm.completion.transformation", "VLLMConfig"), "DeepSeekChatConfig": (".llms.deepseek.chat.transformation", "DeepSeekChatConfig"), + "TencentChatConfig": (".llms.tencent.chat.transformation", "TencentChatConfig"), "LMStudioChatConfig": (".llms.lm_studio.chat.transformation", "LMStudioChatConfig"), "LmStudioEmbeddingConfig": ( ".llms.lm_studio.embed.transformation", @@ -1157,6 +1160,10 @@ _LLM_CONFIGS_IMPORT_MAP = { ".llms.dashscope.chat.transformation", "DashScopeChatConfig", ), + "GDCGeminiConfig": ( + ".llms.gdc.chat.transformation", + "GDCGeminiConfig", + ), "ModelScopeChatConfig": ( ".llms.modelscope.chat.transformation", "ModelScopeChatConfig", diff --git a/litellm/_redis.py b/litellm/_redis.py index 2bcce0e1083..0b91cdabffc 100644 --- a/litellm/_redis.py +++ b/litellm/_redis.py @@ -23,7 +23,11 @@ from litellm._redis_credential_provider import ( GCPIAMCredentialProvider, _generate_gcp_iam_access_token, ) -from litellm.constants import REDIS_CONNECTION_POOL_TIMEOUT, REDIS_SOCKET_TIMEOUT +from litellm.constants import ( + REDIS_CLUSTER_HEALTH_CHECK_INTERVAL, + REDIS_CONNECTION_POOL_TIMEOUT, + REDIS_SOCKET_TIMEOUT, +) from litellm.litellm_core_utils.sensitive_data_masker import SensitiveDataMasker from ._logging import verbose_logger @@ -102,6 +106,8 @@ def _get_redis_cluster_kwargs(client=None): "max_connections", "socket_timeout", "socket_connect_timeout", + "health_check_interval", + "socket_keepalive", } return available_args @@ -319,8 +325,19 @@ def _get_redis_client_logic(**env_overrides): value = get_secret(v) # type: ignore env_overrides[k] = value + environment_kwargs = _redis_kwargs_from_environment() + + # An explicitly configured connection target outranks REDIS_URL from the + # environment. Without this, the url branch below strips the caller's + # host/port/password and silently connects to whatever REDIS_URL names. + caller_named_a_target = any( + env_overrides.get(key) is not None for key in ("host", "startup_nodes", "sentinel_nodes") + ) + if caller_named_a_target and env_overrides.get("url") is None: + environment_kwargs.pop("url", None) + redis_kwargs = { - **_redis_kwargs_from_environment(), + **environment_kwargs, **env_overrides, } @@ -579,6 +596,13 @@ def get_redis_async_client( new_startup_nodes.append(ClusterNode(**item)) cluster_kwargs.pop("startup_nodes", None) + # Default to a periodic health check + TCP keepalive so a connection silently dropped + # by a cluster restart (e.g. ElastiCache Serverless maintenance) is revalidated and + # reconnected before reuse instead of stalling in re-initialization; an explicit value + # from config still wins. + cluster_kwargs.setdefault("health_check_interval", REDIS_CLUSTER_HEALTH_CHECK_INTERVAL) + cluster_kwargs.setdefault("socket_keepalive", True) + # Create async RedisCluster with IAM token as password if available cluster_client = async_redis.RedisCluster( startup_nodes=new_startup_nodes, @@ -665,9 +689,8 @@ def get_redis_connection_pool( redis_kwargs["credential_provider"] = GCPIAMCredentialProvider(redis_connect_func._gcp_service_account) connection_class = async_redis.Connection - if "ssl" in redis_kwargs: + if redis_kwargs.pop("ssl", False): connection_class = async_redis.SSLConnection - redis_kwargs.pop("ssl", None) redis_kwargs["connection_class"] = connection_class return async_redis.BlockingConnectionPool(timeout=REDIS_CONNECTION_POOL_TIMEOUT, **redis_kwargs) diff --git a/litellm/a2a_protocol/main.py b/litellm/a2a_protocol/main.py index 37bf7c34f02..4c23ecfed54 100644 --- a/litellm/a2a_protocol/main.py +++ b/litellm/a2a_protocol/main.py @@ -129,6 +129,33 @@ def _set_agent_id_on_logging_obj( litellm_logging_obj.model_call_details["agent_id"] = agent_id +_A2A_COST_PARAM_KEYS = ("cost_per_query", "input_cost_per_token", "output_cost_per_token") + + +def _set_litellm_params_on_logging_obj( + kwargs: dict[str, Any], + litellm_params: dict[str, Any], +) -> None: + """ + Merge the agent's pricing params into model_call_details["litellm_params"] + so A2ACostCalculator can read them. + + The non-streaming path reuses the proxy-built logging object, whose + litellm_params already carries metadata / proxy_server_request / user-key + context, so merge the pricing keys in rather than replacing the dict. + """ + logging_obj = kwargs.get("litellm_logging_obj") + if logging_obj is None: + return + + cost_params = {key: litellm_params[key] for key in _A2A_COST_PARAM_KEYS if litellm_params.get(key) is not None} + if not cost_params: + return + + existing = logging_obj.model_call_details.get("litellm_params") or {} + logging_obj.model_call_details["litellm_params"] = {**existing, **cost_params} + + def _get_a2a_model_info(a2a_client: Any, kwargs: Dict[str, Any]) -> str: """ Extract agent info and set model/custom_llm_provider for cost tracking. @@ -477,6 +504,9 @@ async def asend_message( completion_tokens=completion_tokens, ) + # Merge agent pricing params into the logging obj so cost is calculated + _set_litellm_params_on_logging_obj(kwargs=kwargs, litellm_params=litellm_params) + # Set agent_id on logging obj for SpendLogs tracking _set_agent_id_on_logging_obj(kwargs=kwargs, agent_id=agent_id) diff --git a/litellm/a2a_protocol/streaming_iterator.py b/litellm/a2a_protocol/streaming_iterator.py index 529154919f3..1ef174a5eee 100644 --- a/litellm/a2a_protocol/streaming_iterator.py +++ b/litellm/a2a_protocol/streaming_iterator.py @@ -11,7 +11,6 @@ from litellm._logging import verbose_logger from litellm.a2a_protocol.cost_calculator import A2ACostCalculator from litellm.a2a_protocol.utils import A2ARequestUtils from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj -from litellm.litellm_core_utils.thread_pool_executor import executor if TYPE_CHECKING: from a2a.types import SendStreamingMessageRequest, SendStreamingMessageResponse @@ -128,22 +127,15 @@ class A2AStreamingIterator: # Call success handlers - they will build standard_logging_object asyncio.create_task( - self.logging_obj.async_success_handler( - result=result, + self.logging_obj.dispatch_success_handlers( + result, start_time=self.start_time, end_time=end_time, cache_hit=None, + prefer_async_handlers=True, ) ) - executor.submit( - self.logging_obj.success_handler, - result=result, - cache_hit=None, - start_time=self.start_time, - end_time=end_time, - ) - verbose_logger.info( f"A2A streaming completed: prompt_tokens={prompt_tokens}, " f"completion_tokens={completion_tokens}, total_tokens={total_tokens}, " diff --git a/litellm/a2a_protocol/utils.py b/litellm/a2a_protocol/utils.py index 0dbd1eefc63..ce5a168c3ac 100644 --- a/litellm/a2a_protocol/utils.py +++ b/litellm/a2a_protocol/utils.py @@ -121,8 +121,13 @@ class A2ARequestUtils: Returns: Tuple of (prompt_tokens, completion_tokens, total_tokens) """ - # Count input tokens + # Count input tokens. Dump the message to a dict first so extraction hits + # the dict branch — request-side parts are a2a-sdk Part RootModels whose + # kind/text live on part.root, which the object branch cannot read. This + # mirrors how the response side already works (it operates on model_dump). input_message = A2ARequestUtils.get_input_message_from_request(request) + if input_message is not None and hasattr(input_message, "model_dump"): + input_message = input_message.model_dump(mode="json") input_text = A2ARequestUtils.extract_text_from_message(input_message) prompt_tokens = A2ARequestUtils.count_tokens(input_text) diff --git a/litellm/anthropic_beta_headers_config.json b/litellm/anthropic_beta_headers_config.json index 11fdb26e42d..3f6817f6e35 100644 --- a/litellm/anthropic_beta_headers_config.json +++ b/litellm/anthropic_beta_headers_config.json @@ -102,7 +102,7 @@ "computer-use-2025-01-24": "computer-use-2025-01-24", "computer-use-2025-11-24": "computer-use-2025-11-24", "context-1m-2025-08-07": "context-1m-2025-08-07", - "context-management-2025-06-27": null, + "context-management-2025-06-27": "context-management-2025-06-27", "effort-2025-11-24": "effort-2025-11-24", "fast-mode-2026-02-01": null, "files-api-2025-04-14": null, diff --git a/litellm/batches/batch_utils.py b/litellm/batches/batch_utils.py index 985198ce7ce..11b07d39981 100644 --- a/litellm/batches/batch_utils.py +++ b/litellm/batches/batch_utils.py @@ -3,6 +3,7 @@ from typing import Any, Iterator, List, Literal, Optional, Tuple import litellm from litellm._logging import verbose_logger +from litellm.litellm_core_utils.llm_cost_calc.utils import _parse_prompt_tokens_details from litellm.types.llms.openai import Batch from litellm.types.utils import CallTypes, ModelInfo, Usage from litellm.utils import token_counter @@ -34,7 +35,7 @@ async def calculate_batch_cost_and_usage( custom_llm_provider=custom_llm_provider, model_name=model_name, ) - batch_models = _get_batch_models_from_file_content(file_content_dictionary, model_name) + batch_models = _get_batch_models_from_file_content(file_content_dictionary, model_name, custom_llm_provider) return batch_cost, batch_usage, batch_models @@ -70,7 +71,7 @@ async def _handle_completed_batch( model_name=model_name, ) - batch_models = _get_batch_models_from_file_content(file_content_dictionary, model_name) + batch_models = _get_batch_models_from_file_content(file_content_dictionary, model_name, custom_llm_provider) return batch_cost, batch_usage, batch_models @@ -78,6 +79,7 @@ async def _handle_completed_batch( def _get_batch_models_from_file_content( file_content_dictionary: List[dict], model_name: Optional[str] = None, + custom_llm_provider: str = "openai", ) -> List[str]: """ Get the models from the file content @@ -86,8 +88,8 @@ def _get_batch_models_from_file_content( return [model_name] batch_models = [] for _item in file_content_dictionary: - if _batch_response_was_successful(_item): - _response_body = _get_response_from_batch_job_output_file(_item) + if _batch_response_was_successful(_item, custom_llm_provider): + _response_body = _get_response_from_batch_job_output_file(_item, custom_llm_provider) _model = _response_body.get("model") if _model: batch_models.append(_model) @@ -373,10 +375,10 @@ def _get_batch_job_cost_from_file_content( # parse the file content as json verbose_logger.debug("file_content_dictionary=%s", json.dumps(file_content_dictionary, indent=4)) for _item in file_content_dictionary: - if _batch_response_was_successful(_item): - _response_body = _get_response_from_batch_job_output_file(_item) - if model_info is not None: - usage = _get_batch_job_usage_from_response_body(_response_body) + if _batch_response_was_successful(_item, custom_llm_provider): + _response_body = _get_response_from_batch_job_output_file(_item, custom_llm_provider) + if model_info is not None or custom_llm_provider == "anthropic": + usage = _get_batch_job_usage_from_response_body(_response_body, custom_llm_provider) model = _response_body.get("model", "") prompt_cost, completion_cost = batch_cost_calculator( usage=usage, @@ -418,17 +420,31 @@ def _get_batch_job_total_usage_from_file_content( total_tokens: int = 0 prompt_tokens: int = 0 completion_tokens: int = 0 + cache_read_tokens: int = 0 + cache_creation_tokens: int = 0 for _item in file_content_dictionary: - if _batch_response_was_successful(_item): - _response_body = _get_response_from_batch_job_output_file(_item) - usage: Usage = _get_batch_job_usage_from_response_body(_response_body) + if _batch_response_was_successful(_item, custom_llm_provider): + _response_body = _get_response_from_batch_job_output_file(_item, custom_llm_provider) + usage: Usage = _get_batch_job_usage_from_response_body(_response_body, custom_llm_provider) total_tokens += usage.total_tokens prompt_tokens += usage.prompt_tokens completion_tokens += usage.completion_tokens + prompt_details = _parse_prompt_tokens_details(usage) + cache_read_tokens += prompt_details["cache_hit_tokens"] + cache_creation_tokens += prompt_details["cache_creation_tokens"] + cache_token_params = { + key: tokens + for key, tokens in ( + ("cache_read_input_tokens", cache_read_tokens), + ("cache_creation_input_tokens", cache_creation_tokens), + ) + if tokens > 0 + } return Usage( total_tokens=total_tokens, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, + **cache_token_params, ) @@ -465,27 +481,51 @@ def _count_prompt_or_input_tokens(model: str, value: Any) -> int: return 0 -def _get_batch_job_usage_from_response_body(response_body: dict) -> Usage: +def _get_batch_job_usage_from_response_body(response_body: dict, custom_llm_provider: str = "openai") -> Usage: """ Get the tokens of a batch job from the response body """ + if custom_llm_provider == "anthropic": + from litellm.llms.anthropic.chat.transformation import AnthropicConfig + + return AnthropicConfig().calculate_usage( + usage_object=response_body.get("usage", None) or {}, + reasoning_content=None, + ) _usage_dict = response_body.get("usage", None) or {} usage: Usage = Usage(**_usage_dict) return usage -def _get_response_from_batch_job_output_file(batch_job_output_file: dict) -> Any: +def _get_anthropic_result_from_batch_results_line(batch_results_line: dict) -> dict: + """ + Get the ``result`` object from a line of an Anthropic message batch results JSONL file. + + Anthropic batch results lines look like: + ``{"custom_id": ..., "result": {"type": "succeeded", "message": {..., "usage": {...}}}}`` + """ + return batch_results_line.get("result", None) or {} + + +def _get_response_from_batch_job_output_file(batch_job_output_file: dict, custom_llm_provider: str = "openai") -> Any: """ Get the response from the batch job output file """ + if custom_llm_provider == "anthropic": + return _get_anthropic_result_from_batch_results_line(batch_job_output_file).get("message", None) or {} _response: dict = batch_job_output_file.get("response", None) or {} _response_body = _response.get("body", None) or {} return _response_body -def _batch_response_was_successful(batch_job_output_file: dict) -> bool: +def _batch_response_was_successful(batch_job_output_file: dict, custom_llm_provider: str = "openai") -> bool: """ - Check if the batch job response status == 200 + Check if the batch job response was successful + + OpenAI-shaped output rows report ``response.status_code == 200``; Anthropic + message batch results lines report ``result.type == "succeeded"``. """ + if custom_llm_provider == "anthropic": + return _get_anthropic_result_from_batch_results_line(batch_job_output_file).get("type") == "succeeded" _response: dict = batch_job_output_file.get("response", None) or {} return _response.get("status_code", None) == 200 diff --git a/litellm/caching/disk_cache.py b/litellm/caching/disk_cache.py index b51acbe9cfd..d9f65ce949e 100644 --- a/litellm/caching/disk_cache.py +++ b/litellm/caching/disk_cache.py @@ -59,8 +59,9 @@ class DiskCache(BaseCache): def increment_cache(self, key, value: int, **kwargs) -> int: # get the value - init_value = self.get_cache(key=key) or 0 - value = init_value + value # type: ignore + cached_value = self.get_cache(key=key) + init_value = cached_value if isinstance(cached_value, int) else 0 + value = init_value + value self.set_cache(key, value, **kwargs) return value @@ -76,8 +77,9 @@ class DiskCache(BaseCache): async def async_increment(self, key, value: int, **kwargs) -> int: # get the value - init_value = await self.async_get_cache(key=key) or 0 - value = init_value + value # type: ignore + cached_value = await self.async_get_cache(key=key) + init_value = cached_value if isinstance(cached_value, int) else 0 + value = init_value + value await self.async_set_cache(key, value, **kwargs) return value diff --git a/litellm/caching/valkey_semantic_cache.py b/litellm/caching/valkey_semantic_cache.py index 746e91207d8..76b7f7d5b87 100644 --- a/litellm/caching/valkey_semantic_cache.py +++ b/litellm/caching/valkey_semantic_cache.py @@ -279,7 +279,7 @@ class ValkeySemanticCache(RedisSemanticCache): print_verbose("No prompt provided for semantic caching") return - embedding = await self._get_async_embedding(prompt, **kwargs) + embedding = await self._get_async_embedding(prompt, metadata=kwargs.get("metadata")) await self._ensure_index_async(len(embedding)) doc_key = self._doc_key(key) @@ -298,7 +298,7 @@ class ValkeySemanticCache(RedisSemanticCache): kwargs.setdefault("metadata", {})["semantic-similarity"] = 0.0 return None - embedding = await self._get_async_embedding(prompt, **kwargs) + embedding = await self._get_async_embedding(prompt, metadata=kwargs.get("metadata")) await self._ensure_index_async(len(embedding)) search_result = await self.async_client.ft(self.index_name).search( diff --git a/litellm/constants.py b/litellm/constants.py index aeb74a65839..715d57e594d 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -332,6 +332,10 @@ REDIS_CONNECTION_POOL_TIMEOUT = int(os.getenv("REDIS_CONNECTION_POOL_TIMEOUT", 5 REDIS_CIRCUIT_BREAKER_FAILURE_THRESHOLD = int(os.getenv("REDIS_CIRCUIT_BREAKER_FAILURE_THRESHOLD", 5)) REDIS_CIRCUIT_BREAKER_RECOVERY_TIMEOUT = int(os.getenv("REDIS_CIRCUIT_BREAKER_RECOVERY_TIMEOUT", 60)) REDIS_CIRCUIT_BREAKER_ENABLED = os.getenv("REDIS_CIRCUIT_BREAKER_ENABLED", "true").lower() == "true" +# Seconds of idle before a Redis cluster connection is validated with a PING and +# reconnected if dead, so a connection silently dropped by a cluster restart +# (e.g. ElastiCache Serverless maintenance) is not reused while broken +REDIS_CLUSTER_HEALTH_CHECK_INTERVAL = 25 # Default Redis major version to assume when version cannot be determined # Using 7 as it's the modern version that supports LPOP with count parameter DEFAULT_REDIS_MAJOR_VERSION = int(os.getenv("DEFAULT_REDIS_MAJOR_VERSION", 7)) @@ -456,6 +460,7 @@ LITELLM_CHAT_PROVIDERS = [ "openai", "openai_like", "bytez", + "gdc", "xai", "custom_openai", "text-completion-openai", @@ -503,6 +508,7 @@ LITELLM_CHAT_PROVIDERS = [ "text-completion-codestral", "text-completion-inception", "deepseek", + "tencent", "sambanova", "maritalk", "cloudflare", @@ -709,6 +715,7 @@ openai_compatible_endpoints: List = [ "https://api.clarifai.com/v2/ext/openai/v1", "https://api.libertai.io/v1", "https://pinstripes.io/v1", + "https://api.meta.ai/v1", ] @@ -724,6 +731,7 @@ openai_compatible_providers: List = [ "volcengine", "codestral", "deepseek", + "tencent", "deepinfra", "perplexity", "xinference", @@ -774,6 +782,7 @@ openai_compatible_providers: List = [ "ragflow", "pinstripes", # Pinstripes - JSON-configured provider "darkbloom", + "meta", # Meta Model API (Muse Spark) - JSON-configured provider ] openai_text_completion_compatible_providers: List = [ # providers that support `/v1/completions` "together_ai", @@ -1123,6 +1132,7 @@ BEDROCK_CONVERSE_MODELS = [ "anthropic.claude-haiku-4-5-20251001-v1:0", "anthropic.claude-sonnet-4-5-20250929-v1:0", "anthropic.claude-fable-5", + "anthropic.claude-sonnet-5", "anthropic.claude-opus-4-8", "anthropic.claude-opus-4-7", "anthropic.claude-opus-4-6-v1:0", @@ -1496,6 +1506,7 @@ LITELLM_SETTINGS_SAFE_DB_OVERRIDES = [ "public_model_groups_links", "cost_discount_config", "cost_margin_config", + "budget_exceeded_throttle_percentage", ] SPECIAL_LITELLM_AUTH_TOKEN = ["ui-token"] DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL = int(os.getenv("DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL", 60)) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index e8535a570c8..a40a8e1389c 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -52,6 +52,9 @@ from litellm.llms.databricks.cost_calculator import ( from litellm.llms.deepseek.cost_calculator import ( cost_per_token as deepseek_cost_per_token, ) +from litellm.llms.tencent.cost_calculator import ( + cost_per_token as tencent_cost_per_token, +) from litellm.llms.fireworks_ai.cost_calculator import ( cost_per_token as fireworks_ai_cost_per_token, ) @@ -219,7 +222,7 @@ def _cost_per_token_custom_pricing_helper( output_cost = completion_tokens * output_cost_per_token return input_cost, output_cost elif custom_cost_per_second is not None: - output_cost = custom_cost_per_second * response_time_ms / 1000 # type: ignore + output_cost = custom_cost_per_second * (response_time_ms or 0.0) / 1000 return 0, output_cost return None @@ -625,6 +628,8 @@ def cost_per_token( return gemini_cost_per_token(model=model, usage=usage_block, service_tier=service_tier) elif custom_llm_provider == "deepseek": return deepseek_cost_per_token(model=model, usage=usage_block) + elif custom_llm_provider == "tencent": + return tencent_cost_per_token(model=model, usage=usage_block) elif custom_llm_provider == "perplexity": return perplexity_cost_per_token(model=model, usage=usage_block) elif custom_llm_provider == "xai": @@ -657,29 +662,27 @@ def cost_per_token( data_residency=data_residency, ) - if model_info.get("input_cost_per_second", None) is not None and response_time_ms is not None: + input_cost_per_second = model_info.get("input_cost_per_second") + if input_cost_per_second is not None and response_time_ms is not None: verbose_logger.debug( "For model=%s - input_cost_per_second: %s; response time: %s", model, - model_info.get("input_cost_per_second", None), + input_cost_per_second, response_time_ms, ) ## COST PER SECOND ## - prompt_tokens_cost_usd_dollar = ( - model_info["input_cost_per_second"] * response_time_ms / 1000 # type: ignore - ) + prompt_tokens_cost_usd_dollar = input_cost_per_second * response_time_ms / 1000 - if model_info.get("output_cost_per_second", None) is not None and response_time_ms is not None: + output_cost_per_second = model_info.get("output_cost_per_second") + if output_cost_per_second is not None and response_time_ms is not None: verbose_logger.debug( "For model=%s - output_cost_per_second: %s; response time: %s", model, - model_info.get("output_cost_per_second", None), + output_cost_per_second, response_time_ms, ) ## COST PER SECOND ## - completion_tokens_cost_usd_dollar = ( - model_info["output_cost_per_second"] * response_time_ms / 1000 # type: ignore - ) + completion_tokens_cost_usd_dollar = output_cost_per_second * response_time_ms / 1000 verbose_logger.debug( "Returned custom cost for model=%s - prompt_tokens_cost_usd_dollar: %s, completion_tokens_cost_usd_dollar: %s", @@ -757,7 +760,11 @@ def _select_model_name_for_cost_calc( if custom_pricing is True: if router_model_id is not None and router_model_id in litellm.model_cost: entry = litellm.model_cost[router_model_id] - if entry.get("input_cost_per_token") is not None or entry.get("input_cost_per_second") is not None: + if ( + entry.get("input_cost_per_token") is not None + or entry.get("input_cost_per_second") is not None + or entry.get("tiered_pricing") is not None + ): return_model = router_model_id else: return_model = model @@ -1495,6 +1502,7 @@ def completion_cost( custom_llm_provider=custom_llm_provider, litellm_model_name=model, data_residency=data_residency, + litellm_logging_obj=litellm_logging_obj, ) elif call_type == _MCP_CALL_TYPE: from litellm.proxy._experimental.mcp_server.cost_calculator import ( @@ -2151,17 +2159,23 @@ def batch_cost_calculator( if input_cost_per_token_batches: total_prompt_cost = usage.prompt_tokens * input_cost_per_token_batches elif input_cost_per_token: + details = _parse_prompt_tokens_details(usage) + cache_read_tokens = details["cache_hit_tokens"] + cache_creation_tokens = details["cache_creation_tokens"] + # Subtract cached tokens from prompt_tokens before calculating cost # Fixes issue where cached tokens are being charged again + base_input_tokens = get_billable_input_tokens(usage) - cache_creation_tokens total_prompt_cost = ( - get_billable_input_tokens(usage) * (input_cost_per_token) / 2 + base_input_tokens * (input_cost_per_token) / 2 ) # batch cost is usually half of the regular token cost # Add cache read cost if applicable - details = _parse_prompt_tokens_details(usage) - cache_read_tokens = details["cache_hit_tokens"] cache_read_cost_key = _get_service_tier_cost_key("cache_read_input_token_cost", None) total_prompt_cost += calculate_cost_component(model_info, cache_read_cost_key, cache_read_tokens) / 2 + + cache_creation_cost = model_info.get("cache_creation_input_token_cost") or input_cost_per_token + total_prompt_cost += cache_creation_tokens * cache_creation_cost / 2 if output_cost_per_token_batches: total_completion_cost = usage.completion_tokens * output_cost_per_token_batches elif output_cost_per_token: @@ -2297,6 +2311,7 @@ def handle_realtime_stream_cost_calculation( custom_llm_provider: str, litellm_model_name: str, data_residency: Optional[str] = None, + litellm_logging_obj: Optional[LitellmLoggingObject] = None, ) -> float: """ Handles the cost calculation for realtime stream responses. @@ -2332,14 +2347,25 @@ def handle_realtime_stream_cost_calculation( input_cost_per_token += _input_cost_per_token output_cost_per_token += _output_cost_per_token break # exit if we find a valid model - total_cost = input_cost_per_token + output_cost_per_token - - if any(r.get("type") == _TRANSCRIPTION_COMPLETED_EVENT_TYPE for r in results): - total_cost += handle_realtime_transcription_cost_calculation( + transcription_cost = ( + handle_realtime_transcription_cost_calculation( results=results, custom_llm_provider=custom_llm_provider, litellm_model_name=litellm_model_name, ) + if any(r.get("type") == _TRANSCRIPTION_COMPLETED_EVENT_TYPE for r in results) + else 0.0 + ) + total_cost = input_cost_per_token + output_cost_per_token + transcription_cost + + _store_cost_breakdown_in_logging_obj( + litellm_logging_obj=litellm_logging_obj, + prompt_tokens_cost_usd_dollar=input_cost_per_token, + completion_tokens_cost_usd_dollar=output_cost_per_token, + cost_for_built_in_tools_cost_usd_dollar=0.0, + total_cost_usd_dollar=total_cost, + additional_costs={"transcription_cost": transcription_cost} if transcription_cost > 0 else None, + ) return total_cost diff --git a/litellm/exceptions.py b/litellm/exceptions.py index d97ba347b07..aca3fb551cc 100644 --- a/litellm/exceptions.py +++ b/litellm/exceptions.py @@ -1165,29 +1165,21 @@ class ModifyResponseException(Exception): request_data: Dict[str, Any], guardrail_name: Optional[str] = None, detection_info: Optional[Dict[str, Any]] = None, + original_response: Optional[Any] = None, ): self.message = message self.model = model self.request_data = request_data self.guardrail_name = guardrail_name self.detection_info = detection_info or {} + # The LLM response that was blocked (post-call). Carries the real token + # usage the upstream call consumed, so the synthetic block response can + # report it instead of discarding it. None for pre-call blocks (the LLM + # was never invoked). + self.original_response = original_response super().__init__(message) -class GuardrailInterventionNormalStringError( - Exception -): # custom exception to raise when a guardrail intervenes, but we want to return a normal string to the user - def __init__(self, message: str): - self.message = message - super().__init__(self.message) - - def __str__(self): - return self.message - - def __repr__(self): - return self.__str__() - - class SensitiveDataRouteException(Exception): """ Exception raised when a guardrail detects sensitive data and wants to reroute the request. diff --git a/litellm/experimental_mcp_client/client.py b/litellm/experimental_mcp_client/client.py index 831e588e5ba..da711463a44 100644 --- a/litellm/experimental_mcp_client/client.py +++ b/litellm/experimental_mcp_client/client.py @@ -382,15 +382,25 @@ class MCPClient: if root_cause is not None and isinstance(in_flight_error, asyncio.CancelledError): raise root_cause from in_flight_error - async def run_with_session(self, operation: Callable[[ClientSession], Awaitable[TSessionResult]]) -> TSessionResult: - """Open a session, run the provided coroutine, and clean up.""" + async def run_with_session( + self, + operation: Callable[[ClientSession], Awaitable[TSessionResult]], + *, + quiet_on_error: bool = False, + ) -> TSessionResult: + """Open a session, run the provided coroutine, and clean up. + + quiet_on_error demotes the failure line to debug for callers that own the exception + (call_tool / list_tools under raise_on_error), so an expected pass-through re-auth does + not emit a warning per call; every other caller keeps the operator-visible warning.""" http_client: Optional[httpx.AsyncClient] = None try: self._last_initialize_instructions = None transport_ctx, http_client = self._create_transport_context() return await self._execute_session_operation(transport_ctx, operation) except Exception: - verbose_logger.warning("MCP client run_with_session failed for %s", self.server_url or "stdio") + _log = verbose_logger.debug if quiet_on_error else verbose_logger.warning + _log("MCP client run_with_session failed for %s", self.server_url or "stdio") raise finally: if http_client is not None: @@ -491,7 +501,7 @@ class MCPClient: return await session.list_tools() try: - result = await self.run_with_session(_list_tools_operation) + result = await self.run_with_session(_list_tools_operation, quiet_on_error=raise_on_error) tool_count = len(result.tools) tool_names = [tool.name for tool in result.tools] verbose_logger.info(f"MCP client listed {tool_count} tools from {self.server_url or 'stdio'}: {tool_names}") @@ -501,7 +511,13 @@ class MCPClient: raise except Exception as e: error_type = type(e).__name__ - verbose_logger.exception( + # Mirror call_tool: when the caller opted into raise_on_error it owns the exception and + # logs it at the fitting level (an expected pass-through re-auth 401 is info, not an + # error), so log at debug here to avoid an error-level line + traceback that would trip + # error-rate alerts on that expected signal. The swallow path still logs the full + # exception because nothing downstream will surface the failure. + _log = verbose_logger.debug if raise_on_error else verbose_logger.exception + _log( f"MCP client list_tools failed - " f"Error Type: {error_type}, " f"Error: {str(e)}, " @@ -510,7 +526,8 @@ class MCPClient: ) # Check if it's a stream/connection error if "BrokenResourceError" in error_type or "Broken" in error_type: - verbose_logger.error( + _log_broken = verbose_logger.debug if raise_on_error else verbose_logger.error + _log_broken( "MCP client detected broken connection/stream during list_tools - " "the MCP server may have crashed, disconnected, or timed out" ) @@ -520,13 +537,28 @@ class MCPClient: # Return empty list instead of raising to allow graceful degradation return [] + @staticmethod + def error_tool_result(exc: Exception) -> MCPCallToolResult: + """The error result ``call_tool`` returns when it swallows a failure (no re-execution).""" + return MCPCallToolResult( + content=[TextContent(type="text", text=f"{type(exc).__name__}: {str(exc)}")], + isError=True, + ) + async def call_tool( self, call_tool_request_params: MCPCallToolRequestParams, host_progress_callback: Optional[Callable] = None, + raise_on_error: bool = False, ) -> MCPCallToolResult: """ Call an MCP Tool. + + Args: + raise_on_error: When True, re-raise the underlying exception instead of returning an + ``isError=True`` result. The token-exchange (OBO) tool-call path uses this to detect + an upstream 401 so it can re-mint the exchanged token and retry once; every other + caller keeps the default and gets graceful ``isError`` degradation. """ verbose_logger.info(f"MCP client calling tool '{call_tool_request_params.name}'") @@ -552,7 +584,7 @@ class MCPClient: ) try: - tool_result = await self.run_with_session(_call_tool_operation) + tool_result = await self.run_with_session(_call_tool_operation, quiet_on_error=raise_on_error) verbose_logger.info(f"MCP client tool call '{call_tool_request_params.name}' completed successfully") return tool_result except asyncio.CancelledError: @@ -565,7 +597,13 @@ class MCPClient: verbose_logger.debug(f"MCP client tool call traceback:\n{error_trace}") # Log detailed error information error_type = type(e).__name__ - verbose_logger.error( + # When the caller opted into raise_on_error it owns the exception and logs it at the + # level that fits (an expected pass-through re-auth 401 is info, not an operator-actionable + # error), so log at debug here to avoid an error-level line that would trip error-rate + # alerts on that expected signal. The swallow path (raise_on_error=False) still logs at + # error because nothing downstream will surface the failure. + _log = verbose_logger.debug if raise_on_error else verbose_logger.error + _log( f"MCP client call_tool failed - " f"Error Type: {error_type}, " f"Error: {str(e)}, " @@ -575,15 +613,14 @@ class MCPClient: ) # Check if it's a stream/connection error if "BrokenResourceError" in error_type or "Broken" in error_type: - verbose_logger.error( + _log( "MCP client detected broken connection/stream - " "the MCP server may have crashed, disconnected, or timed out." ) + if raise_on_error: + raise # Return a default error result instead of raising - return MCPCallToolResult( - content=[TextContent(type="text", text=f"{error_type}: {str(e)}")], # Empty content for error case - isError=True, - ) + return self.error_tool_result(e) async def list_prompts(self) -> List[Prompt]: """List available prompts from the server.""" diff --git a/litellm/fine_tuning/main.py b/litellm/fine_tuning/main.py index 8a8a916fa9c..ce5074cdaf5 100644 --- a/litellm/fine_tuning/main.py +++ b/litellm/fine_tuning/main.py @@ -256,8 +256,6 @@ def create_fine_tuning_job( extra_body = optional_params.get("extra_body", {}) if extra_body is not None: extra_body.pop("azure_ad_token", None) - else: - get_secret_str("AZURE_AD_TOKEN") # type: ignore # Prepare Azure-specific parameters for extra_body extra_body = _prepare_azure_extra_body(extra_body, kwargs, azure_specific_hyperparams) @@ -442,7 +440,7 @@ def cancel_fine_tuning_job( ) # Azure OpenAI elif custom_llm_provider == "azure": - api_base = optional_params.api_base or litellm.api_base or get_secret("AZURE_API_BASE") # type: ignore + api_base = optional_params.api_base or litellm.api_base or get_secret_str("AZURE_API_BASE") api_version = optional_params.api_version or litellm.api_version or get_secret_str("AZURE_API_VERSION") # type: ignore @@ -457,8 +455,6 @@ def cancel_fine_tuning_job( extra_body = optional_params.get("extra_body", {}) if extra_body is not None: extra_body.pop("azure_ad_token", None) - else: - get_secret_str("AZURE_AD_TOKEN") # type: ignore response = azure_fine_tuning_apis_instance.cancel_fine_tuning_job( api_base=api_base, @@ -616,8 +612,6 @@ def list_fine_tuning_jobs( extra_body = optional_params.get("extra_body", {}) if extra_body is not None: extra_body.pop("azure_ad_token", None) - else: - get_secret("AZURE_AD_TOKEN") # type: ignore response = azure_fine_tuning_apis_instance.list_fine_tuning_jobs( api_base=api_base, @@ -759,8 +753,6 @@ def retrieve_fine_tuning_job( extra_body = optional_params.get("extra_body", {}) if extra_body is not None: extra_body.pop("azure_ad_token", None) - else: - get_secret_str("AZURE_AD_TOKEN") # type: ignore response = azure_fine_tuning_apis_instance.retrieve_fine_tuning_job( api_base=api_base, diff --git a/litellm/integrations/anthropic_cache_control_hook.py b/litellm/integrations/anthropic_cache_control_hook.py index 1314fd82255..608fdebc1d9 100644 --- a/litellm/integrations/anthropic_cache_control_hook.py +++ b/litellm/integrations/anthropic_cache_control_hook.py @@ -1,9 +1,12 @@ """ -This hook is used to inject cache control directives into the messages of a chat completion. +This hook is used to inject cache control directives into messages. Users can define - `cache_control_injection_points` in the completion params and litellm will inject the cache control directives into the messages at the specified injection points. +Supported for both `v1/chat/completions` (via the prompt-management hook) and +`v1/messages` (via `apply_to_anthropic_messages_request`). + """ import copy @@ -225,6 +228,98 @@ class AnthropicCacheControlHook(CustomPromptManagement): message_content[-1]["cache_control"] = control # type: ignore return message + @staticmethod + def apply_to_anthropic_messages_request( + messages: List[Dict], + system: str | list | None, + injection_points: List[CacheControlInjectionPoint], + ) -> Tuple[List[Dict], str | list | None, List[CacheControlInjectionPoint]]: + """Apply cache control injection for the Anthropic-native v1/messages endpoint. + + Returns (messages, system, remaining_non_message_points). + """ + if not injection_points: + return messages, system, [] + + processed_messages: List[Dict] = copy.deepcopy(messages) + processed_system = copy.deepcopy(system) if system is not None else None + + message_points: List[CacheControlMessageInjectionPoint] = [] + system_points: List[CacheControlMessageInjectionPoint] = [] + remaining_points: List[CacheControlInjectionPoint] = [] + + for point in injection_points: + if point.get("location") == "message": + msg_point = cast(CacheControlMessageInjectionPoint, point) + if msg_point.get("role") == "system": + system_points.append(msg_point) + else: + message_points.append(msg_point) + else: + remaining_points.append(point) + + reserved_blocks = 1 if any(p.get("location") == "tool_config" for p in remaining_points) else 0 + max_blocks = MAX_CACHE_CONTROL_BLOCKS - reserved_blocks + + used_blocks = sum( + AnthropicCacheControlHook._count_cache_control_blocks(cast(AllMessageValues, msg)) + for msg in processed_messages + ) + if isinstance(processed_system, list): + used_blocks += sum( + 1 for b in processed_system if isinstance(b, dict) and b.get("cache_control") is not None + ) + + if system_points and processed_system is not None and used_blocks < max_blocks: + system_already_has_cc = isinstance(processed_system, list) and any( + isinstance(b, dict) and b.get("cache_control") is not None for b in processed_system + ) + if not system_already_has_cc: + control = system_points[0].get("control") or ChatCompletionCachedContent(type="ephemeral") + if isinstance(processed_system, str): + processed_system = [{"type": "text", "text": processed_system, "cache_control": control}] + used_blocks += 1 + elif len(processed_system) > 0 and isinstance(processed_system[-1], dict): + processed_system[-1] = {**processed_system[-1], "cache_control": control} + used_blocks += 1 + + for i, msg in enumerate(processed_messages): + content = msg.get("content") + if isinstance(content, str): + processed_messages[i] = {**msg, "content": [{"type": "text", "text": content}]} + + processed_messages = AnthropicCacheControlHook._apply_message_injections( + points=message_points, + messages=cast(List[AllMessageValues], processed_messages), + max_blocks=max_blocks - used_blocks, + ) + + return processed_messages, processed_system, remaining_points + + @staticmethod + def maybe_inject_cache_control( + messages: List[Dict], + system: str | list | None, + kwargs: Dict[str, Any], + ) -> Tuple[List[Dict], str | list | None]: + """Extract cache_control_injection_points from kwargs and apply if present. + + Pops the key from kwargs; if remaining (non-message) points exist they + are written back so downstream transforms can handle them. + """ + injection_points = kwargs.pop("cache_control_injection_points", None) + if not injection_points: + return messages, system + + messages, system, remaining = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=system, + injection_points=injection_points, + ) + if remaining: + kwargs["cache_control_injection_points"] = remaining + return messages, system + @property def integration_name(self) -> str: """Return the integration name for this hook.""" diff --git a/litellm/integrations/azure_sentinel/azure_sentinel.py b/litellm/integrations/azure_sentinel/azure_sentinel.py index 182a2e185ef..5f8afe58cb0 100644 --- a/litellm/integrations/azure_sentinel/azure_sentinel.py +++ b/litellm/integrations/azure_sentinel/azure_sentinel.py @@ -61,13 +61,15 @@ class AzureSentinelLogger(CustomBatchLogger): client_secret (str, optional): Azure Client Secret for OAuth2 authentication. If not provided, will use AZURE_SENTINEL_CLIENT_SECRET or AZURE_CLIENT_SECRET env var. audit_stream_name (str, optional): Stream name from DCR for audit logs. - If not provided, audit logs use the standard stream name. + If not provided, will use AZURE_SENTINEL_AUDIT_STREAM_NAME env var or the standard stream name. """ self.async_httpx_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.LoggingCallback) resolved_dcr_immutable_id = dcr_immutable_id or os.getenv("AZURE_SENTINEL_DCR_IMMUTABLE_ID") resolved_stream_name = stream_name or os.getenv("AZURE_SENTINEL_STREAM_NAME") or "Custom-LiteLLM" - resolved_audit_stream_name = audit_stream_name or resolved_stream_name + resolved_audit_stream_name = ( + audit_stream_name or os.getenv("AZURE_SENTINEL_AUDIT_STREAM_NAME") or resolved_stream_name + ) resolved_endpoint = endpoint or os.getenv("AZURE_SENTINEL_ENDPOINT") resolved_tenant_id = tenant_id or os.getenv("AZURE_SENTINEL_TENANT_ID") or os.getenv("AZURE_TENANT_ID") resolved_client_id = client_id or os.getenv("AZURE_SENTINEL_CLIENT_ID") or os.getenv("AZURE_CLIENT_ID") diff --git a/litellm/integrations/code_interpreter_interception/handler.py b/litellm/integrations/code_interpreter_interception/handler.py index cd7b211f1a5..759b2be3a84 100644 --- a/litellm/integrations/code_interpreter_interception/handler.py +++ b/litellm/integrations/code_interpreter_interception/handler.py @@ -40,9 +40,11 @@ from litellm.types.utils import ( LITELLM_CODE_EXECUTION_TOOL_NAME = "litellm_code_execution" _INTERCEPTION_ACTIVE_KEY = "_code_interpreter_interception_active" _SANDBOX_KEY = "_code_interpreter_interception_sandbox_key" +_SESSION_SCOPED_KEY = "_code_interpreter_interception_session_scoped" _CONVERTED_STREAM_KEY = "_code_interpreter_interception_converted_stream" _LITELLM_METADATA_KEY = "litellm_metadata" _CACHE_TTL_SECONDS = 15 * 60 +_SESSION_SCOPED_PER_IDENTITY_CAP = 10 class CodeExecutionToolCall(TypedDict, total=False): @@ -107,6 +109,20 @@ class ChatCompletionFunctionToolChoice(TypedDict): CodeExecutionFunctionToolChoice = ResponsesFunctionToolChoice | ChatCompletionFunctionToolChoice +def _extract_session_id(kwargs: dict[str, Any]) -> str | None: + for meta_key in ("metadata", "litellm_metadata"): + meta = kwargs.get(meta_key) + if isinstance(meta, dict): + sid = meta.get("session_id") + if sid and isinstance(sid, str): + return sid + return None + + +def _extract_identity(kwargs: dict[str, Any]) -> str: + return kwargs.get("user_api_key_hash") or "" + + def _resolve_sandbox_tool(sandbox_tool_name: str | None) -> dict[str, Any] | None: try: from litellm.sandbox.sandbox_tools import resolve_sandbox_tool @@ -140,7 +156,7 @@ class CodeInterpreterInterceptionLogger(CustomLogger): self.enabled_providers = enabled_providers self.sandbox_tool_name = sandbox_tool_name self.sandbox_config = sandbox_config - self._container_cache: dict[str, tuple[Any, dict[str, Any] | None, float]] = {} + self._container_cache: dict[str, tuple[Any, dict[str, Any] | None, float, str | None]] = {} @classmethod def from_config_yaml(cls, config: CodeInterpreterInterceptionConfig) -> "CodeInterpreterInterceptionLogger": @@ -191,7 +207,13 @@ class CodeInterpreterInterceptionLogger(CustomLogger): return None kwargs[_INTERCEPTION_ACTIVE_KEY] = True - kwargs[_SANDBOX_KEY] = uuid.uuid4().hex + session_id = _extract_session_id(kwargs) + if session_id: + identity = _extract_identity(kwargs) + kwargs[_SANDBOX_KEY] = f"{identity}:{session_id}" if identity else session_id + kwargs[_SESSION_SCOPED_KEY] = True + else: + kwargs[_SANDBOX_KEY] = uuid.uuid4().hex if kwargs.get("stream"): kwargs["stream"] = False kwargs[_CONVERTED_STREAM_KEY] = True @@ -217,6 +239,7 @@ class CodeInterpreterInterceptionLogger(CustomLogger): if not is_interception_internal_key(key) and not key.startswith("_agentic_loop") and key != "max_agentic_loops" + and key != _SESSION_SCOPED_KEY } if filtered_metadata: kwargs[_LITELLM_METADATA_KEY] = filtered_metadata @@ -227,7 +250,7 @@ class CodeInterpreterInterceptionLogger(CustomLogger): def _write_interception_metadata(kwargs: dict[str, Any]) -> None: metadata = kwargs.get(_LITELLM_METADATA_KEY) metadata = dict(metadata) if isinstance(metadata, dict) else {} - for key in (_INTERCEPTION_ACTIVE_KEY, _SANDBOX_KEY, _CONVERTED_STREAM_KEY): + for key in (_INTERCEPTION_ACTIVE_KEY, _SANDBOX_KEY, _SESSION_SCOPED_KEY, _CONVERTED_STREAM_KEY): if key in kwargs: metadata[key] = kwargs[key] kwargs[_LITELLM_METADATA_KEY] = metadata @@ -347,7 +370,9 @@ class CodeInterpreterInterceptionLogger(CustomLogger): await self._prune_expired_cache() tool_calls = cast(list[CodeExecutionToolCall], tools.get("tool_calls", [])) sandbox_key = kwargs.get(_SANDBOX_KEY) - container, params = await self._get_or_create_container(cache_key=sandbox_key) + is_session = bool(kwargs.get(_SESSION_SCOPED_KEY)) + identity = _extract_identity(kwargs) if is_session else None + container, params = await self._get_or_create_container(cache_key=sandbox_key, identity=identity) try: container_id = cast(str | None, getattr(container, "id", None)) @@ -404,6 +429,7 @@ class CodeInterpreterInterceptionLogger(CustomLogger): metadata={ "tool_type": "code_interpreter", "sandbox_key": sandbox_key or "", + "is_session_scoped": bool(kwargs.get(_SESSION_SCOPED_KEY)), "code_interpreter_calls": code_interpreter_calls, }, ) @@ -419,7 +445,9 @@ class CodeInterpreterInterceptionLogger(CustomLogger): await self._prune_expired_cache() tool_calls = cast(list[CodeExecutionToolCall], tools.get("tool_calls", [])) sandbox_key = cast(str | None, kwargs.get(_SANDBOX_KEY)) - container, params = await self._get_or_create_container(cache_key=sandbox_key) + is_session = bool(kwargs.get(_SESSION_SCOPED_KEY)) + identity = _extract_identity(cast(dict[str, Any], kwargs)) if is_session else None + container, params = await self._get_or_create_container(cache_key=sandbox_key, identity=identity) try: container_id = cast(str | None, getattr(container, "id", None)) @@ -455,6 +483,7 @@ class CodeInterpreterInterceptionLogger(CustomLogger): metadata={ "tool_type": "code_interpreter", "sandbox_key": sandbox_key or "", + "is_session_scoped": bool(kwargs.get(_SESSION_SCOPED_KEY)), "code_interpreter_calls": code_interpreter_calls, "response_format": "openai", }, @@ -489,6 +518,8 @@ class CodeInterpreterInterceptionLogger(CustomLogger): async def async_agentic_loop_cleanup_hook(self, plan: AgenticLoopPlan, kwargs: dict) -> None: metadata = plan.metadata or {} if plan else {} + if metadata.get("is_session_scoped"): + return await self._delete_container_for_cache_key(metadata.get("sandbox_key")) @staticmethod @@ -520,7 +551,8 @@ class CodeInterpreterInterceptionLogger(CustomLogger): async def async_post_agentic_loop_response_hook(self, response: Any, plan: AgenticLoopPlan, kwargs: dict) -> Any: metadata = plan.metadata or {} if plan else {} - await self._delete_container_for_cache_key(metadata.get("sandbox_key")) + if not metadata.get("is_session_scoped"): + await self._delete_container_for_cache_key(metadata.get("sandbox_key")) calls = metadata.get("code_interpreter_calls") if not calls: @@ -565,17 +597,32 @@ class CodeInterpreterInterceptionLogger(CustomLogger): return f"[execution error] {message}" return getattr(result, "stdout", "") or "" - async def _get_or_create_container(self, cache_key: str | None) -> tuple[Any, dict[str, Any] | None]: + async def _get_or_create_container( + self, + cache_key: str | None, + identity: str | None = None, + ) -> tuple[Any, dict[str, Any] | None]: if cache_key: cached = self._container_cache.get(cache_key) if cached is not None: + self._container_cache[cache_key] = (cached[0], cached[1], time.time(), cached[3]) return cached[0], cached[1] container, params = await self._create_container() if cache_key: - self._container_cache[cache_key] = (container, params, time.time()) + if identity is not None: + await self._evict_lru_session_if_over_cap(identity) + self._container_cache[cache_key] = (container, params, time.time(), identity) return container, params + async def _evict_lru_session_if_over_cap(self, identity: str) -> None: + identity_entries = [(k, v) for k, v in self._container_cache.items() if v[3] == identity] + if len(identity_entries) < _SESSION_SCOPED_PER_IDENTITY_CAP: + return + lru_key, lru_entry = min(identity_entries, key=lambda item: item[1][2]) + self._container_cache.pop(lru_key, None) + await self._delete_container(container=lru_entry[0], params=lru_entry[1]) + async def _create_container(self) -> tuple[Any, dict[str, Any] | None]: if self.sandbox_config is not None: return await self.sandbox_config.acreate_sandbox(), None @@ -739,12 +786,8 @@ class CodeInterpreterInterceptionLogger(CustomLogger): now = time.time() expired = [ (cache_key, container, params) - for cache_key, ( - container, - params, - created_at, - ) in self._container_cache.items() - if now - created_at > _CACHE_TTL_SECONDS + for cache_key, (container, params, last_accessed, *_) in self._container_cache.items() + if now - last_accessed > _CACHE_TTL_SECONDS ] for cache_key, container, params in expired: self._container_cache.pop(cache_key, None) diff --git a/litellm/integrations/custom_guardrail.py b/litellm/integrations/custom_guardrail.py index 59d37639098..c8bfcabc64e 100644 --- a/litellm/integrations/custom_guardrail.py +++ b/litellm/integrations/custom_guardrail.py @@ -1,3 +1,4 @@ +import os import secrets from datetime import datetime from typing import ( @@ -17,6 +18,7 @@ from litellm._logging import verbose_logger from litellm.litellm_core_utils.core_helpers import redact_nested_match_and_regex_keys from litellm.caching import DualCache from litellm.integrations.custom_logger import CustomLogger +from litellm.secret_managers.main import str_to_bool from litellm.types.guardrails import ( DynamicGuardrailParams, GuardrailEventHooks, @@ -59,6 +61,20 @@ from litellm.exceptions import ( _PRE_CALL_EXECUTED_TOKEN = secrets.token_hex(16) +def _strict_guardrail_modes_enabled() -> bool: + """Whether guardrail-mode validation raises (default) or logs a warning. + + Set `LITELLM_STRICT_GUARDRAIL_MODES=false` to keep the pre-LIT-4226 behavior + for guardrails whose supported_event_hooks list newly includes their + configured mode: log the mismatch and continue instead of raising at boot. + """ + raw = os.environ.get("LITELLM_STRICT_GUARDRAIL_MODES") + if raw is None: + return True + parsed = str_to_bool(raw) + return True if parsed is None else parsed + + def get_session_id_from_request_data(request_data: Dict[str, Any]) -> Optional[str]: """Extract session_id from request data (litellm_session_id or metadata).""" session_id = request_data.get("litellm_session_id") @@ -132,7 +148,17 @@ class CustomGuardrail(CustomLogger): if supported_event_hooks: ## validate event_hook is in supported_event_hooks - self._validate_event_hook(event_hook, supported_event_hooks) + try: + self._validate_event_hook(event_hook, supported_event_hooks) + except ValueError as validation_error: + if _strict_guardrail_modes_enabled(): + raise + verbose_logger.warning( + "%s. LITELLM_STRICT_GUARDRAIL_MODES=false; continuing " + "with unsupported event_hook. Set the env var to true " + "(default) to enforce validation and fail at startup.", + validation_error, + ) super().__init__(**kwargs) def render_violation_message(self, default: str, context: Optional[Dict[str, Any]] = None) -> str: @@ -303,6 +329,18 @@ class CustomGuardrail(CustomLogger): """ return None + @classmethod + def get_supported_event_hooks(cls) -> Optional[List[GuardrailEventHooks]]: + """ + Returns the event hooks this guardrail supports, for the UI to render. + + Subclasses should override to return their supported hooks list. When a + subclass returns None, the endpoint omits it from the per-provider map + and the UI is expected to fall back to the global `supported_modes` + list client-side. + """ + return None + def _validate_event_hook( self, event_hook: Optional[Union[GuardrailEventHooks, List[GuardrailEventHooks], Mode]], @@ -757,6 +795,12 @@ class CustomGuardrail(CustomLogger): # raw provider JSON so redaction is not duplicated upstream). clean_guardrail_response = redact_nested_match_and_regex_keys(clean_guardrail_response) + from litellm.litellm_core_utils.sensitive_data_masker import ( + mask_credentials_in_payload, + ) + + clean_guardrail_response = mask_credentials_in_payload(clean_guardrail_response) + slg = StandardLoggingGuardrailInformation( guardrail_name=self.guardrail_name, guardrail_provider=guardrail_provider, diff --git a/litellm/integrations/datadog/datadog.py b/litellm/integrations/datadog/datadog.py index 6775858c124..20239d831cc 100644 --- a/litellm/integrations/datadog/datadog.py +++ b/litellm/integrations/datadog/datadog.py @@ -19,7 +19,7 @@ import os import time import traceback from datetime import datetime as datetimeObj -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Sequence, Union import httpx from httpx import Response @@ -50,6 +50,7 @@ from litellm.types.integrations.base_health_check import IntegrationHealthCheckS from litellm.types.integrations.datadog import ( DD_ERRORS, DD_MAX_BATCH_SIZE, + DD_MAX_PAYLOAD_SIZE_BYTES, DataDogStatus, DatadogInitParams, DatadogPayload, @@ -354,14 +355,14 @@ class DataDogLogger( Raises: Raises a NON Blocking verbose_logger.exception if an error occurs """ + if not self.log_queue: + verbose_logger.exception("Datadog: log_queue does not exist") + return + + batch_to_send = self.log_queue[:] + self.log_queue = [] + try: - if not self.log_queue: - verbose_logger.exception("Datadog: log_queue does not exist") - return - - batch_to_send = self.log_queue[:] - self.log_queue = [] - verbose_logger.debug( "Datadog - about to flush %s events on %s", len(batch_to_send), @@ -384,8 +385,10 @@ class DataDogLogger( async def _send_with_413_split(self, batch: List) -> List: """ - Send a batch, halving any sub-batch that 413s (payload too large) and retrying the - halves, since Datadog enforces a 5MB uncompressed limit per request. + Send a batch, halving any sub-batch that exceeds Datadog's intake limits before + sending, and halving again on a 413 (payload too large) response, since Datadog + enforces a 5MB uncompressed limit per request. The proactive split avoids paying + a serialize + gzip + round trip for a payload the intake is guaranteed to reject. A 413 surfaces as a raised MaskedHTTPStatusError (httpx raise_for_status), not a returned response, so both paths are handled. A lone event that still 413s is @@ -398,6 +401,11 @@ class DataDogLogger( chunk = pending.pop() if not chunk: continue + if len(chunk) > 1 and self._exceeds_intake_limits(chunk): + mid = len(chunk) // 2 + pending.append(chunk[mid:]) + pending.append(chunk[:mid]) + continue try: response = await self.async_send_compressed_data(chunk) except Exception as e: @@ -436,6 +444,21 @@ class DataDogLogger( def _undelivered(chunk: List, pending: List[List]) -> List: return chunk + [event for remaining in reversed(pending) for event in remaining] + @staticmethod + def _exceeds_intake_limits(chunk: Sequence[DatadogPayload]) -> bool: + """ + True when a chunk would breach Datadog's log intake limits: more than + DD_MAX_BATCH_SIZE events per payload, or a serialized size above + DD_MAX_PAYLOAD_SIZE_BYTES (held under Datadog's 5MB uncompressed cap so + the batch is split before the intake rejects it with a 413). + """ + from litellm.litellm_core_utils.safe_json_dumps import safe_dumps + + if len(chunk) > DD_MAX_BATCH_SIZE: + return True + payload_size_bytes = len(safe_dumps(chunk).encode("utf-8")) + return payload_size_bytes > DD_MAX_PAYLOAD_SIZE_BYTES + async def flush_queue(self): if self.flush_lock is None: return diff --git a/litellm/integrations/email_templates/templates.py b/litellm/integrations/email_templates/templates.py index 8df816dfecd..b4f39074a94 100644 --- a/litellm/integrations/email_templates/templates.py +++ b/litellm/integrations/email_templates/templates.py @@ -81,8 +81,7 @@ SOFT_BUDGET_ALERT_EMAIL_TEMPLATE = """ If you have any questions, please send an email to {email_support_contact}

- Best,
- The LiteLLM team
+ {email_footer} """ TEAM_SOFT_BUDGET_ALERT_EMAIL_TEMPLATE = """ @@ -105,8 +104,7 @@ TEAM_SOFT_BUDGET_ALERT_EMAIL_TEMPLATE = """ If you have any questions, please send an email to {email_support_contact}

- Best,
- The LiteLLM team
+ {email_footer} """ MAX_BUDGET_ALERT_EMAIL_TEMPLATE = """ @@ -129,6 +127,5 @@ MAX_BUDGET_ALERT_EMAIL_TEMPLATE = """ If you have any questions, please send an email to {email_support_contact}

- Best,
- The LiteLLM team
+ {email_footer} """ diff --git a/litellm/integrations/otel/README.md b/litellm/integrations/otel/README.md index 17011bb8db7..3038bdb90b2 100644 --- a/litellm/integrations/otel/README.md +++ b/litellm/integrations/otel/README.md @@ -223,6 +223,15 @@ lives in [`plumbing/`](./plumbing): readers/exporters receive them alongside the server metrics, and one is built and registered as the global only when none is set (mirroring how V2 owns trace export). +- [`events.py`](./plumbing/events.py) — GenAI client events. Gated on + `enable_events` (`LITELLM_OTEL_INTEGRATION_ENABLE_EVENTS`), a failed LLM call + records the semconv `gen_ai.client.operation.exception` log event at severity + WARN, carrying `exception.type` / `exception.message` / `exception.stacktrace` + and correlated to the failed span through the trace and span ids. The + `LoggerProvider` is resolved like the meter provider, except that an explicit + `NoOpLoggerProvider` global is an operator opt-out that builds no recorder at + all. The deprecated `error.*` span attributes and the `exception` span event + are still stamped by the emitter for backwards compatibility. ### Adapter diff --git a/litellm/integrations/otel/__init__.py b/litellm/integrations/otel/__init__.py index da3ce4af3e7..5e167e006ff 100644 --- a/litellm/integrations/otel/__init__.py +++ b/litellm/integrations/otel/__init__.py @@ -32,11 +32,13 @@ from litellm.integrations.otel.model.payloads import ( LLMCallSpanData, LLMRequestParams, LLMUsage, + MCPListToolsSpanData, MCPToolCallSpanData, ProxyRequestSpanData, ServerInfo, ServiceSpanData, SpanError, + is_mcp_list_tools, is_mcp_tool_call, ) from litellm.integrations.otel.model.semconv import ( @@ -50,6 +52,7 @@ from litellm.integrations.otel.model.semconv import ( GenAIProvider, JsonRpc, LiteLLM, + LiteLLMError, MCPMethod, Metric, Network, @@ -85,6 +88,7 @@ __all__ = [ "HTTP", "JsonRpc", "LiteLLM", + "LiteLLMError", "MCP", "MCPMethod", "Metric", @@ -106,6 +110,7 @@ __all__ = [ "LLMCallSpanData", "LLMRequestParams", "LLMUsage", + "MCPListToolsSpanData", "MCPToolCallSpanData", "ProxyRequestSpanData", "RequestContext", @@ -113,6 +118,7 @@ __all__ = [ "ServerInfo", "ServiceSpanData", "SpanError", + "is_mcp_list_tools", "is_mcp_tool_call", "promoted_baggage", ] diff --git a/litellm/integrations/otel/emitter.py b/litellm/integrations/otel/emitter.py index 69fc53c5b9d..f97f8b8394c 100644 --- a/litellm/integrations/otel/emitter.py +++ b/litellm/integrations/otel/emitter.py @@ -4,7 +4,7 @@ from collections import OrderedDict from typing import Callable, Sequence from opentelemetry.context import Context -from opentelemetry.trace import Span, Tracer +from opentelemetry.trace import Link, Span, Tracer from opentelemetry.trace.status import Status, StatusCode from litellm.integrations.otel.model.config import OpenTelemetryV2Config @@ -13,16 +13,20 @@ from litellm.integrations.otel.mappers.base import AttributeMapper, SpanData from litellm.integrations.otel.model.payloads import ( GuardrailSpanData, LLMCallSpanData, + MCPListToolsSpanData, MCPToolCallSpanData, ServiceSpanData, + SpanError, ) +from litellm.integrations.otel.plumbing.events import GenAIEventRecorder from litellm.integrations.otel.plumbing.providers import to_otel_span_kind -from litellm.integrations.otel.model.semconv import Error, ExceptionEvent +from litellm.integrations.otel.model.semconv import Error, ExceptionEvent, LiteLLMError from litellm.integrations.otel.model.spans import ( SPAN_REGISTRY, SpanRole, guardrail_span_name, llm_call_span_name, + mcp_list_tools_span_name, mcp_tool_call_span_name, service_span_name, ) @@ -33,6 +37,7 @@ from litellm.integrations.otel.model.spans import ( _NAME_BUILDERS: dict[SpanRole, Callable[..., str]] = { SpanRole.LLM_CALL: llm_call_span_name, SpanRole.MCP_TOOL_CALL: mcp_tool_call_span_name, + SpanRole.MCP_LIST_TOOLS: mcp_list_tools_span_name, SpanRole.GUARDRAIL: guardrail_span_name, # DB_CALL and SERVICE are both built from ServiceSpanData; they differ only in # span kind (CLIENT vs INTERNAL) and attribute vocabulary, not in naming. @@ -46,15 +51,38 @@ _NAME_BUILDERS: dict[SpanRole, Callable[..., str]] = { _DEDUP_CACHE_MAX = 10_000 +def _stamp_otel_error_attributes(span: Span, error_type: str, resolved_message: str) -> None: + """Stamp the OTel-semconv error attributes (``error.type`` + ``error.message``). + ``error_type`` and ``resolved_message`` are ``finish_span``'s already-computed + fallback chains, so the pair on the status, event, and attributes stays in + lockstep.""" + span.set_attribute(Error.TYPE, error_type) + span.set_attribute(Error.MESSAGE, resolved_message) + + +def _stamp_litellm_error_attributes(span: Span, error: SpanError) -> None: + """Stamp litellm-specific error detail attributes. Emitted only when the + corresponding field is populated so guardrail-shape errors carrying only a + message aren't polluted with empty detail keys.""" + if error.code: + span.set_attribute(LiteLLMError.CODE, error.code) + if error.stack_trace: + span.set_attribute(LiteLLMError.STACK_TRACE, error.stack_trace) + if error.llm_provider: + span.set_attribute(LiteLLMError.LLM_PROVIDER, error.llm_provider) + + class SpanEmitter: def __init__( self, tracer: Tracer, config: OpenTelemetryV2Config, mappers: Sequence[AttributeMapper] | None = None, + event_recorder: GenAIEventRecorder | None = None, ) -> None: self._tracer = tracer self._config = config + self._event_recorder = event_recorder # The mapper chain is the sole source of span attributes. When not # passed in, resolve it from the config so there's one source of truth. self._mappers: list[AttributeMapper] = ( @@ -74,18 +102,21 @@ class SpanEmitter: start_time_ns: int | None = None, *, tracer: Tracer | None = None, + links: Sequence[Link] | None = None, ) -> Span: """Start a span for ``role`` without dedup or attribute mapping. For callers that own and manage their own span lifecycle. ``tracer`` overrides the bound tracer for this span only, used for per-request - multi-tenant credential routing. + multi-tenant credential routing. ``links`` records related-but-not-parent + spans (e.g. the transport span of an MCP message, per MCP semconv). """ return (tracer or self._tracer).start_span( name, context=parent_context, kind=to_otel_span_kind(SPAN_REGISTRY[role].kind), start_time=start_time_ns, + links=list(links) if links else None, ) def _seen(self, dedup_key: str | None, role: SpanRole) -> bool: @@ -116,16 +147,23 @@ class SpanEmitter: start_time_ns: int | None = None, end_time_ns: int | None = None, tracer: Tracer | None = None, + links: Sequence[Link] | None = None, ) -> Span | None: """Emit one complete span: dedup, start, map attributes, status, end. Return the span, or ``None`` if it was deduplicated away. ``tracer`` overrides the bound tracer for this span, used for per-request routing. + ``links`` records related-but-not-parent spans (the transport span of an + MCP message). """ # LLM-call and MCP tool-call spans carry a dedup key (their request's # call id), so a sync+async double-firing coalesces. ``isinstance`` narrows # the type for mypy and keeps the engine free of duck-typed attribute reads. - dedup_key = data.identity.call_id if isinstance(data, (LLMCallSpanData, MCPToolCallSpanData)) else None + dedup_key = ( + data.identity.call_id + if isinstance(data, (LLMCallSpanData, MCPToolCallSpanData, MCPListToolsSpanData)) + else None + ) if self._seen(dedup_key, role): return None span = self.start_span( @@ -134,6 +172,7 @@ class SpanEmitter: parent_context=parent_context, start_time_ns=start_time_ns, tracer=tracer, + links=links, ) self.finish_span(role, span, data, end_time_ns=end_time_ns) return span @@ -166,6 +205,7 @@ class SpanEmitter: ( LLMCallSpanData, MCPToolCallSpanData, + MCPListToolsSpanData, ServiceSpanData, GuardrailSpanData, ), @@ -175,16 +215,25 @@ class SpanEmitter: if error and (error.error_type or error.message): error_type = error.error_type or "error" message = error.message or error.error_type or "error" - span.set_attribute(Error.TYPE, error_type) + _stamp_otel_error_attributes(span, error_type, message) + _stamp_litellm_error_attributes(span, error) span.set_status(Status(StatusCode.ERROR, message)) - # Carry the full message on the standard ``exception`` event so backends - # map it as full text under ``exception.message``. Setting it as a bare - # string attribute instead lets backends like Elasticsearch dynamic-map - # it to a ``keyword`` capped at 1024 chars, truncating the message. + # Also emit the semconv ``exception`` event so backends that + # dynamic-map unknown string span attrs to ``keyword`` (e.g. + # Elasticsearch with a 1024-char ``ignore_above``) still see the + # full untruncated message on the recognized event field. span.add_event( ExceptionEvent.NAME, {ExceptionEvent.TYPE: error_type, ExceptionEvent.MESSAGE: message}, ) + if self._event_recorder is not None and role is SpanRole.LLM_CALL: + self._event_recorder.record_operation_exception( + span_context=span.get_span_context(), + error_type=error_type, + message=message, + stack_trace=error.stack_trace, + timestamp_ns=end_time_ns, + ) # On success leave the status UNSET (the semconv default) rather than # forcing OK — that matches the FastAPI server span and avoids implying a # span-level health signal litellm doesn't actually evaluate. Only a diff --git a/litellm/integrations/otel/logger.py b/litellm/integrations/otel/logger.py index 44484559948..be72fabd387 100644 --- a/litellm/integrations/otel/logger.py +++ b/litellm/integrations/otel/logger.py @@ -5,7 +5,8 @@ from contextlib import contextmanager from datetime import datetime from typing import TYPE_CHECKING, Any, Callable, Iterator, Mapping, Sequence, cast -from opentelemetry.context import attach, get_current +from opentelemetry.context import Context, attach, get_current +from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk.trace import TracerProvider from opentelemetry.trace import Span, Tracer, get_current_span, use_span @@ -17,6 +18,7 @@ from litellm.integrations.otel.model.config import OpenTelemetryV2Config from litellm.integrations.otel.plumbing.context import ( is_recordable_span, request_root_span, + resolve_mcp_span_context, resolve_parent_context, resolve_request_span_context, set_request_baggage, @@ -32,19 +34,24 @@ from litellm.integrations.otel.model.metadata import ( from litellm.integrations.otel.model.payloads import ( GuardrailSpanData, LLMCallSpanData, + MCPListToolsSpanData, MCPToolCallSpanData, ServiceSpanData, SpanError, + is_mcp_list_tools, is_mcp_tool_call, ) +from litellm.integrations.otel.plumbing.events import GenAIEventRecorder from litellm.integrations.otel.plumbing.metrics import ( GenAIMetricRecorder, create_genai_metrics, ) from litellm.integrations.otel.plumbing.providers import ( build_tracer_provider, + get_event_logger, get_meter, get_tracer, + resolve_logger_provider, resolve_meter_provider, ) from litellm.integrations.otel.plumbing.routing import TenantTracerCache @@ -101,7 +108,7 @@ class OpenTelemetryV2(CustomLogger): config: OpenTelemetryV2Config | None = None, callback_name: str | None = None, tracer_provider: TracerProvider | None = None, - logger_provider: Any | None = None, # reserved for OTel logs + logger_provider: LoggerProvider | None = None, meter_provider: Any | None = None, **kwargs: Any, ) -> None: @@ -114,7 +121,12 @@ class OpenTelemetryV2(CustomLogger): self.tracer: Tracer = get_tracer(self._tracer_provider, LITELLM_TRACER_NAME) self._metrics_recorder = self._init_metrics(meter_provider) self._metric_filter_error_logged = False - self._emitter = SpanEmitter(self.tracer, self.config, mappers=resolve_mappers(self.config.mapper_names)) + self._emitter = SpanEmitter( + self.tracer, + self.config, + mappers=resolve_mappers(self.config.mapper_names), + event_recorder=self._init_events(logger_provider), + ) self._tenant_tracers = TenantTracerCache(self.config, callback_name, LITELLM_TRACER_NAME) self._open_llm_calls: "OrderedDict[str, _LLMCallSpan]" = OrderedDict() self._init_otel_logger_on_litellm_proxy() @@ -133,6 +145,22 @@ class OpenTelemetryV2(CustomLogger): meter = get_meter(provider, LITELLM_TRACER_NAME) return GenAIMetricRecorder(create_genai_metrics(meter), self.callback_name) + def _init_events(self, logger_provider: LoggerProvider | None) -> "GenAIEventRecorder | None": + """Create the GenAI event recorder when events are enabled, else ``None``. + + ``logger_provider`` is an explicit override (tests inject one); otherwise the + provider is resolved from the OTel global so an operator-configured logs + pipeline receives the events, building and registering one only when no + global provider is set. A ``None`` resolution means the operator opted out + of the logs signal, so no recorder is built. + """ + if not self.config.enable_events: + return None + provider = resolve_logger_provider(self.config, logger_provider) + if provider is None: + return None + return GenAIEventRecorder(get_event_logger(provider, LITELLM_TRACER_NAME)) + # ====================================================================== # # Proxy global registration # ====================================================================== # @@ -218,6 +246,8 @@ class OpenTelemetryV2(CustomLogger): async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): if self._emit_mcp_tool_call(kwargs, start_time, end_time): return + if self._emit_mcp_list_tools(kwargs, start_time, end_time): + return self._close_llm_call(kwargs, start_time, end_time) self._record_metrics(kwargs, response_obj, start_time, end_time) @@ -242,8 +272,24 @@ class OpenTelemetryV2(CustomLogger): async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time): if self._emit_mcp_tool_call(kwargs, start_time, end_time): return + if self._emit_mcp_list_tools(kwargs, start_time, end_time): + return self._close_llm_call(kwargs, start_time, end_time) + def _seed_identity_baggage(self, identity: RequestIdentity, model: str | None, context: Context) -> Context: + """Seed authenticated request-identity Baggage onto ``context`` so the Baggage + processor stamps team/key/metadata onto the span. Identity is read from the + parsed payload, never the client's ``params._meta`` carrier, so it can't be + spoofed.""" + bag = promoted_baggage( + identity, + model, + promoted_keys=tuple(self.config.baggage_promoted_keys), + metadata_keys=tuple(self.config.baggage_metadata_keys), + team_metadata_keys=tuple(self.config.baggage_team_metadata_keys), + ) + return set_request_baggage(bag, context=context) if bag else context + def _emit_mcp_tool_call( self, kwargs: Mapping[str, Any], @@ -254,10 +300,12 @@ class OpenTelemetryV2(CustomLogger): MCP tool calls reach the success/failure callbacks like any other request (with ``call_type`` ``call_mcp_tool``), but they are not LLM calls and have - no ``pre_call`` carrier — so they get their own CLIENT span here, parented - to the request's server span. Returns whether it handled the event, so the - caller skips the LLM-call path. The whole span is emitted at once (there is - no boundary to open it at), deduped on the call id by the emitter. + no ``pre_call`` carrier — so they get their own CLIENT span here. Per the MCP + semconv it parents to the trace context the client propagated in + ``params._meta`` (or starts a new root) and links the transport span, rather + than nesting under the HTTP/session span. Returns whether it handled the + event, so the caller skips the LLM-call path. The whole span is emitted at + once (there is no boundary to open it at), deduped on the call id. """ raw_payload = kwargs.get("standard_logging_object") if not raw_payload or not is_mcp_tool_call(cast(Mapping[str, object], raw_payload)): @@ -271,12 +319,51 @@ class OpenTelemetryV2(CustomLogger): # as a phantom LLM span. if data.identity.call_id: self._open_llm_calls.pop(data.identity.call_id, None) + parent_context, links = resolve_mcp_span_context() + parent_context = self._seed_identity_baggage(data.identity, None, parent_context) self._emitter.emit( SpanRole.MCP_TOOL_CALL, data, - parent_context=resolve_request_span_context(), + parent_context=parent_context, start_time_ns=to_ns(start_time), end_time_ns=to_ns(end_time), + links=links, + ) + return True + + def _emit_mcp_list_tools( + self, + kwargs: Mapping[str, object], + start_time: datetime | float | None, + end_time: datetime | float | None, + ) -> bool: + """Emit an MCP ``tools/list`` span when the closed request was a discovery call. + + Like a tool call, listing reaches the success/failure callbacks (here with + ``call_type`` ``list_mcp_tools``) with no ``pre_call`` carrier, so it gets its + own CLIENT span. Per the MCP semconv it parents to the ``params._meta`` trace + context (or starts a new root) and links the transport span, rather than + nesting under the HTTP/session span. Returns whether it handled the event so + the caller skips the LLM-call path. + """ + raw_payload = kwargs.get("standard_logging_object") + if not raw_payload or not is_mcp_list_tools(cast(Mapping[str, object], raw_payload)): + return False + payload = cast("StandardLoggingPayload", raw_payload) + data = MCPListToolsSpanData.from_standard_logging_payload( + payload, capture_content=self.config.capture_span_content + ) + if data.identity.call_id: + self._open_llm_calls.pop(data.identity.call_id, None) + parent_context, links = resolve_mcp_span_context() + parent_context = self._seed_identity_baggage(data.identity, None, parent_context) + self._emitter.emit( + SpanRole.MCP_LIST_TOOLS, + data, + parent_context=parent_context, + start_time_ns=to_ns(start_time), + end_time_ns=to_ns(end_time), + links=links, ) return True @@ -306,7 +393,11 @@ class OpenTelemetryV2(CustomLogger): # it (named provisionally) so it isn't leaked as an open span. carrier.span.end(end_time=to_ns(end_time)) return None - data = LLMCallSpanData.from_standard_logging_payload(payload, capture_content=self.config.capture_span_content) + data = LLMCallSpanData.from_standard_logging_payload( + payload, + capture_content=self.config.capture_span_content, + time_to_first_chunk_seconds=call.time_to_first_chunk_seconds, + ) end_time_ns = to_ns(end_time) if carrier.span is not None: # Born at the boundary: stamp attributes from the typed payload, set @@ -319,16 +410,7 @@ class OpenTelemetryV2(CustomLogger): # root span — parent to it (ambient fallback on the SDK path). Seed identity # Baggage so the span — and the SDK path, which has none — is labeled # consistently. - parent_ctx = resolve_request_span_context() - bag = promoted_baggage( - data.identity, - data.request_model, - promoted_keys=tuple(self.config.baggage_promoted_keys), - metadata_keys=tuple(self.config.baggage_metadata_keys), - team_metadata_keys=tuple(self.config.baggage_team_metadata_keys), - ) - if bag: - parent_ctx = set_request_baggage(bag, context=parent_ctx) + parent_ctx = self._seed_identity_baggage(data.identity, data.request_model, resolve_request_span_context()) return self._emitter.emit( SpanRole.LLM_CALL, data, diff --git a/litellm/integrations/otel/mappers/base.py b/litellm/integrations/otel/mappers/base.py index 6685e34578b..809d956a9c7 100644 --- a/litellm/integrations/otel/mappers/base.py +++ b/litellm/integrations/otel/mappers/base.py @@ -7,6 +7,7 @@ from typing_extensions import Protocol, runtime_checkable from litellm.integrations.otel.model.payloads import ( GuardrailSpanData, LLMCallSpanData, + MCPListToolsSpanData, MCPToolCallSpanData, ServiceSpanData, ) @@ -20,7 +21,7 @@ AttributeMap = dict[str, AttrValue] # The closed set of span-data types the engine routes through the mapper chain. # Server spans (PROXY_REQUEST + management routes) belong to the mounted FastAPI # instrumentor, not the mapper chain. -SpanData = LLMCallSpanData | MCPToolCallSpanData | GuardrailSpanData | ServiceSpanData +SpanData = LLMCallSpanData | MCPToolCallSpanData | MCPListToolsSpanData | GuardrailSpanData | ServiceSpanData @runtime_checkable diff --git a/litellm/integrations/otel/mappers/genai.py b/litellm/integrations/otel/mappers/genai.py index ad6d3e7ff21..f568afa9e3e 100644 --- a/litellm/integrations/otel/mappers/genai.py +++ b/litellm/integrations/otel/mappers/genai.py @@ -19,6 +19,7 @@ from litellm.integrations.otel.mappers.utils import ( from litellm.integrations.otel.model.payloads import ( GuardrailSpanData, LLMCallSpanData, + MCPListToolsSpanData, MCPToolCallSpanData, ServiceSpanData, ToolDefinition, @@ -54,6 +55,7 @@ class GenAIMapper: GenAI.RESPONSE_MODEL: lambda d: d.response_model, GenAI.RESPONSE_ID: lambda d: d.response_id, GenAI.RESPONSE_FINISH_REASONS: lambda d: list(d.finish_reasons) if d.finish_reasons else None, + GenAI.RESPONSE_TIME_TO_FIRST_CHUNK: lambda d: d.time_to_first_chunk_seconds, GenAI.USAGE_INPUT_TOKENS: lambda d: d.usage.input_tokens, GenAI.USAGE_OUTPUT_TOKENS: lambda d: d.usage.output_tokens, Error.TYPE: lambda d: d.error.error_type if d.error else None, @@ -100,6 +102,15 @@ class GenAIMapper: f"{LiteLLM.COST_PREFIX}total": lambda d: d.response_cost, } + # A tools/list discovery span: the method and session only. Per semconv it must + # NOT carry gen_ai.operation.name (execute_tool) or gen_ai.tool.name — those are + # for tool calls, and listing executes no tool. + _MCP_LIST_ATTRS: dict[str, Callable[[MCPListToolsSpanData], AttrValue | None]] = { + MCP.METHOD_NAME: lambda d: d.method, + MCP.SESSION_ID: lambda d: d.session_id, + LiteLLM.CALL_ID: lambda d: d.identity.call_id or None, + } + _GUARDRAIL_ATTRS: dict[str, Callable[[GuardrailSpanData], AttrValue | None]] = { LiteLLM.GUARDRAIL_NAME: lambda d: d.guardrail_name, LiteLLM.GUARDRAIL_MODE: lambda d: d.mode, @@ -130,6 +141,8 @@ class GenAIMapper: return self._llm_call(data) case MCPToolCallSpanData(): return collect(self._MCP_ATTRS, data) + case MCPListToolsSpanData(): + return collect(self._MCP_LIST_ATTRS, data) case GuardrailSpanData(): return self._guardrail(data) case ServiceSpanData(): diff --git a/litellm/integrations/otel/model/metadata.py b/litellm/integrations/otel/model/metadata.py index 37bb5464315..7ff4f540908 100644 --- a/litellm/integrations/otel/model/metadata.py +++ b/litellm/integrations/otel/model/metadata.py @@ -41,7 +41,7 @@ from typing import TYPE_CHECKING, Any, Mapping, cast from litellm.constants import LITELLM_LOGGING_NO_UPSTREAM_LLM_CALL from litellm.integrations.otel.model.semconv import resolve_operation -from litellm.integrations.otel.model.utils import as_str +from litellm.integrations.otel.model.utils import as_str, to_seconds if TYPE_CHECKING: from litellm.types.utils import StandardLoggingPayload @@ -201,6 +201,7 @@ class LLMCallEvent: # span is renamed from the typed payload at close (``finish_span``); this only # needs to be reasonable for a span that never gets closed (a leak). provisional_span_name: str + time_to_first_chunk_seconds: float | None @classmethod def from_dict(cls, kwargs: Mapping[str, Any]) -> "LLMCallEvent": @@ -214,9 +215,25 @@ class LLMCallEvent: dynamic_params=kwargs.get("standard_callback_dynamic_params"), is_no_upstream_call=bool(kwargs.get(LITELLM_LOGGING_NO_UPSTREAM_LLM_CALL)), provisional_span_name=f"{operation.value} {model}".strip(), + time_to_first_chunk_seconds=time_to_first_chunk_seconds(kwargs), ) +def time_to_first_chunk_seconds(kwargs: Mapping[str, Any]) -> float | None: + """Seconds from the upstream request being issued (``api_call_start_time``) + to the first streamed chunk (``completion_start_time``); ``None`` for + non-streaming calls, where ``completion_start_time`` is backfilled with the + end time and would not measure first-chunk latency.""" + optional_params = cast(Mapping[str, Any], kwargs.get("optional_params") or {}) + if not optional_params.get("stream"): + return None + api_call_start = to_seconds(kwargs.get("api_call_start_time")) + completion_start = to_seconds(kwargs.get("completion_start_time")) + if api_call_start is None or completion_start is None: + return None + return completion_start - api_call_start + + def _call_id(payload: "StandardLoggingPayload | None", kwargs: Mapping[str, Any]) -> str | None: """The call id from the payload (when closed) or the bare kwargs (at pre_call).""" if payload is not None: diff --git a/litellm/integrations/otel/model/payloads.py b/litellm/integrations/otel/model/payloads.py index a368a862024..4a8f01858b5 100644 --- a/litellm/integrations/otel/model/payloads.py +++ b/litellm/integrations/otel/model/payloads.py @@ -37,12 +37,14 @@ __all__ = [ "LLMCost", "LLMRequestParams", "LLMUsage", + "MCPListToolsSpanData", "MCPToolCallSpanData", "ProxyRequestSpanData", "ServerInfo", "ServiceSpanData", "SpanError", "ToolDefinition", + "is_mcp_list_tools", "is_mcp_tool_call", ] @@ -139,6 +141,9 @@ class LLMCost: class SpanError: error_type: str | None = None message: str | None = None + code: str | None = None + stack_trace: str | None = None + llm_provider: str | None = None @dataclass(frozen=True) @@ -303,10 +308,14 @@ class LLMCallSpanData: messages_in: tuple[Mapping[str, object], ...] = () choices_out: tuple[Mapping[str, object], ...] = () system_fingerprint: str | None = None + time_to_first_chunk_seconds: float | None = None @classmethod def from_standard_logging_payload( - cls, payload: "StandardLoggingPayload", capture_content: bool = False + cls, + payload: "StandardLoggingPayload", + capture_content: bool = False, + time_to_first_chunk_seconds: float | None = None, ) -> "LLMCallSpanData": params = cast(Mapping[str, object], payload.get("model_parameters") or {}) # The single parse of the request's metadata — the request-vs-provider @@ -347,6 +356,7 @@ class LLMCallSpanData: messages_in=_dicts(payload.get("messages")) if capture_content else (), choices_out=choices_out if capture_content else (), system_fingerprint=as_str(response.get("system_fingerprint")), + time_to_first_chunk_seconds=time_to_first_chunk_seconds, ) @@ -415,6 +425,42 @@ def is_mcp_tool_call(payload: Mapping[str, object]) -> bool: return bool(_mcp_tool_call_metadata(payload)) or (payload.get("call_type") == "call_mcp_tool") +@dataclass(frozen=True) +class MCPListToolsSpanData: + """One MCP ``tools/list`` discovery call, parsed from a closed request's payload. + + The proxy is an MCP *client* enumerating an upstream server's tools, so this is + a CLIENT span. It carries neither ``gen_ai.operation.name`` nor ``gen_ai.tool.name``: + the GenAI semconv sets ``execute_tool`` (and the tool name) only for tool *calls*, + and listing executes no tool. + """ + + method: str + session_id: str | None + error: SpanError | None + identity: RequestIdentity + + @classmethod + def from_standard_logging_payload( + cls, payload: StandardLoggingPayload, capture_content: bool = False + ) -> MCPListToolsSpanData: + # The list-tools logging path does not thread an MCP session id into the + # payload (only the tool-call path stamps ``mcp_tool_call_metadata``), so + # there is none to read here; ``mcp.session.id`` is simply omitted. + return cls( + method=MCPMethod.TOOLS_LIST.value, + session_id=None, + error=_parse_error(payload), + identity=RequestContext.from_standard_logging_payload(payload).identity, + ) + + +def is_mcp_list_tools(payload: Mapping[str, object]) -> bool: + """Whether a closed request's payload is an MCP ``tools/list`` discovery call + rather than a tool call or an LLM call — true when the call type says so.""" + return payload.get("call_type") == "list_mcp_tools" + + # --- service event_metadata sanitization ------------------------------------ # # Substrings (case-insensitive) of keys that must never reach a span: secrets, @@ -528,6 +574,9 @@ def _parse_error(payload: "StandardLoggingPayload") -> SpanError | None: return SpanError( error_type=as_str(info.get("error_class")) or as_str(info.get("error_code")), message=as_str(info.get("error_message")) or as_str(payload.get("error_str")), + code=as_str(info.get("error_code")), + stack_trace=as_str(info.get("traceback")), + llm_provider=as_str(info.get("llm_provider")), ) diff --git a/litellm/integrations/otel/model/semconv.py b/litellm/integrations/otel/model/semconv.py index 6315a5a4a89..44b2f7e0488 100644 --- a/litellm/integrations/otel/model/semconv.py +++ b/litellm/integrations/otel/model/semconv.py @@ -69,6 +69,7 @@ class GenAI: RESPONSE_ID: Final = "gen_ai.response.id" RESPONSE_MODEL: Final = "gen_ai.response.model" RESPONSE_FINISH_REASONS: Final = "gen_ai.response.finish_reasons" + RESPONSE_TIME_TO_FIRST_CHUNK: Final = "gen_ai.response.time_to_first_chunk" # usage USAGE_INPUT_TOKENS: Final = "gen_ai.usage.input_tokens" USAGE_OUTPUT_TOKENS: Final = "gen_ai.usage.output_tokens" @@ -143,7 +144,24 @@ class Client: class Error: + """OTel-defined error attribute keys, from the semconv ``error.*`` registry. + ``MESSAGE`` is marked *Deprecated* upstream in favor of domain-specific + error message keys plus ``exception.message`` on the exception event, but + litellm still stamps it.""" + TYPE: Final = "error.type" + MESSAGE: Final = "error.message" + + +class LiteLLMError: + """Detail keys for the mapped provider exception of a failed LLM call. + OTel semconv does not define these, so they live under the ``litellm.*`` + vendor namespace rather than squatting on the semconv-owned ``error.*`` + namespace.""" + + CODE: Final = "litellm.provider.error.code" + STACK_TRACE: Final = "litellm.provider.error.stack_trace" + LLM_PROVIDER: Final = "litellm.provider.error.llm_provider" class ExceptionEvent: @@ -159,6 +177,19 @@ class ExceptionEvent: NAME: Final = "exception" TYPE: Final = "exception.type" MESSAGE: Final = "exception.message" + STACKTRACE: Final = "exception.stacktrace" + + +class GenAIEvent: + """GenAI semconv event names, from the GenAI registry's *events* section. + + ``gen_ai.client.operation.exception`` is defined as a log-based event + (severity WARN) carrying the ``exception.*`` trio, correlated to the failed + span via the trace/span ids — the semconv-compliant home for GenAI failure + details, unlike the deprecated ``error.message`` span attribute. + """ + + OPERATION_EXCEPTION: Final = "gen_ai.client.operation.exception" class Server: diff --git a/litellm/integrations/otel/model/spans.py b/litellm/integrations/otel/model/spans.py index bc624cf6a57..c93f95ec97d 100644 --- a/litellm/integrations/otel/model/spans.py +++ b/litellm/integrations/otel/model/spans.py @@ -18,6 +18,13 @@ before the LLM call even starts), so a guardrail is a sibling of the LLM call, not a child of it. The emitter parents every span to the ambient OTel context (the active server span), which matches this. +MCP spans (``MCP_TOOL_CALL``, ``MCP_LIST_TOOLS``) are intentionally NOT in this +tree. Per the OTel GenAI MCP semconv, MCP and the HTTP transport are independent +contexts, so an MCP span parents to the trace context the client propagated in +``params._meta`` (or starts its own root when none is propagated) and records the +``PROXY_REQUEST`` transport span as a span *link*, never a parent. The registry +encodes this as ``parent=None, links=PROXY_REQUEST``. + Not every service call becomes a span — :func:`span_role_for_service` decides: - ``DB_CALL`` (CLIENT) — outbound datastores (redis, postgres, @@ -46,6 +53,7 @@ if TYPE_CHECKING: from litellm.integrations.otel.model.payloads import ( GuardrailSpanData, LLMCallSpanData, + MCPListToolsSpanData, MCPToolCallSpanData, ProxyRequestSpanData, ServiceSpanData, @@ -56,6 +64,7 @@ class SpanRole(str, Enum): PROXY_REQUEST = "proxy_request" LLM_CALL = "llm_call" MCP_TOOL_CALL = "mcp_tool_call" + MCP_LIST_TOOLS = "mcp_list_tools" GUARDRAIL = "guardrail" DB_CALL = "db_call" SERVICE = "service" @@ -74,14 +83,24 @@ class SpanSpec: role: SpanRole kind: LiteLLMSpanKind parent: SpanRole | None + links: SpanRole | None = None SPAN_REGISTRY: dict[SpanRole, SpanSpec] = { SpanRole.PROXY_REQUEST: SpanSpec(SpanRole.PROXY_REQUEST, LiteLLMSpanKind.SERVER, parent=None), SpanRole.LLM_CALL: SpanSpec(SpanRole.LLM_CALL, LiteLLMSpanKind.CLIENT, parent=SpanRole.PROXY_REQUEST), - # The proxy is an MCP client to the upstream server it dispatches the tool - # call to, so this is a CLIENT span, sibling of the LLM call under the request. - SpanRole.MCP_TOOL_CALL: SpanSpec(SpanRole.MCP_TOOL_CALL, LiteLLMSpanKind.CLIENT, parent=SpanRole.PROXY_REQUEST), + # MCP and the HTTP transport are independent contexts (OTel GenAI MCP semconv), + # so an MCP span does not nest under the transport span. The proxy is an MCP + # client to the upstream server, so it's a CLIENT span; it parents to the trace + # context the client propagated in ``params._meta`` (or starts its own root when + # none is propagated) and records the PROXY_REQUEST transport span as a span + # *link*, never a parent — hence ``parent=None, links=PROXY_REQUEST``. + SpanRole.MCP_TOOL_CALL: SpanSpec( + SpanRole.MCP_TOOL_CALL, LiteLLMSpanKind.CLIENT, parent=None, links=SpanRole.PROXY_REQUEST + ), + SpanRole.MCP_LIST_TOOLS: SpanSpec( + SpanRole.MCP_LIST_TOOLS, LiteLLMSpanKind.CLIENT, parent=None, links=SpanRole.PROXY_REQUEST + ), SpanRole.GUARDRAIL: SpanSpec(SpanRole.GUARDRAIL, LiteLLMSpanKind.INTERNAL, parent=SpanRole.PROXY_REQUEST), SpanRole.DB_CALL: SpanSpec(SpanRole.DB_CALL, LiteLLMSpanKind.CLIENT, parent=SpanRole.PROXY_REQUEST), SpanRole.SERVICE: SpanSpec(SpanRole.SERVICE, LiteLLMSpanKind.INTERNAL, parent=SpanRole.PROXY_REQUEST), @@ -163,6 +182,12 @@ def mcp_tool_call_span_name(data: "MCPToolCallSpanData") -> str: return f"{data.method} {data.tool_name}".strip() +def mcp_list_tools_span_name(data: "MCPListToolsSpanData") -> str: + """``"{mcp.method.name}"`` i.e. ``"tools/list"`` — no low-cardinality target, so + the method name alone names the span (MCP semconv).""" + return data.method + + def proxy_request_span_name(data: "ProxyRequestSpanData") -> str: """``"{method} {route}"`` (HTTP semconv).""" return f"{data.http_method} {data.route}".strip() @@ -179,7 +204,8 @@ def service_span_name(data: "ServiceSpanData") -> str: def root_roles() -> list[SpanRole]: - """Roles that start a new trace (no in-process parent).""" + """Roles with no in-process parent. They start a new trace unless they adopt a + remote parent (e.g. an MCP span joining the client's propagated context).""" return [role for role, spec in SPAN_REGISTRY.items() if spec.parent is None] @@ -196,6 +222,8 @@ def validate_registry( raise ValueError(f"SPAN_REGISTRY[{role}] has mismatched role {spec.role}") if spec.parent is not None and spec.parent not in reg: raise ValueError(f"span role {role} declares unknown parent {spec.parent}") + if spec.links is not None and spec.links not in reg: + raise ValueError(f"span role {role} declares unknown link target {spec.links}") missing = [role for role in SpanRole if role not in reg] if missing: raise ValueError(f"SPAN_REGISTRY is missing roles: {missing}") diff --git a/litellm/integrations/otel/plumbing/context.py b/litellm/integrations/otel/plumbing/context.py index ff513c84d95..8acac112c3d 100644 --- a/litellm/integrations/otel/plumbing/context.py +++ b/litellm/integrations/otel/plumbing/context.py @@ -1,11 +1,11 @@ """Trace-context + Baggage helpers.""" -from contextvars import ContextVar +from contextvars import ContextVar, Token from typing import Mapping from opentelemetry import baggage from opentelemetry.context import Context, get_current -from opentelemetry.trace import Span, get_current_span, set_span_in_context +from opentelemetry.trace import Link, Span, get_current_span, set_span_in_context from opentelemetry.trace.propagation.tracecontext import ( TraceContextTextMapPropagator, ) @@ -47,6 +47,31 @@ def request_root_span() -> "Span | None": return span if is_recordable_span(span) else None +# The W3C trace-context carrier (``traceparent``/``tracestate``/``baggage``) the +# MCP client propagated in the current request's ``params._meta``. The MCP gateway +# sets it per message so the MCP span can parent to the client's span rather than +# to the transport. A ``ContextVar`` because, like the root-span anchor, it must +# ride the request task and be readable by the inline success-logging callback. +_mcp_message_trace_carrier: "ContextVar[Mapping[str, str] | None]" = ContextVar( + "litellm_otel_mcp_message_trace_carrier", default=None +) + + +def set_mcp_message_trace_carrier( + carrier: "Mapping[str, str] | None", +) -> "Token[Mapping[str, str] | None]": + """Stash the current MCP message's propagated trace-context carrier. + + Returns the reset token; the caller must reset it once the message is handled + so the carrier never leaks to the next message on the same session task. + """ + return _mcp_message_trace_carrier.set(carrier) + + +def reset_mcp_message_trace_carrier(token: "Token[Mapping[str, str] | None]") -> None: + _mcp_message_trace_carrier.reset(token) + + def set_request_baggage(values: Mapping[str, str], context: Context | None = None) -> Context: """Return a context with ``values`` written into Baggage.""" ctx = context @@ -104,6 +129,38 @@ def resolve_request_span_context() -> Context: return get_current() +def resolve_mcp_span_context( + carrier: "Mapping[str, str] | None" = None, +) -> "tuple[Context, tuple[Link, ...]]": + """Parent context + links for an MCP message span, per the OTel GenAI MCP semconv. + + MCP and the underlying transport (HTTP) are independent lifecycles — one + streamable-HTTP session multiplexes many messages, so nesting the message span + under the HTTP/session span is wrong (it renders the message at the session's + start, skewed by however long the session has been open). Instead: + + * parent to the trace context the client propagated in the request's + ``params._meta`` (a *remote* parent), and + * record the transport/session span as a *link*, never the parent. + + Only trace context (``traceparent``/``tracestate``) is extracted, never the + client's W3C Baggage: ``params._meta`` is caller-controlled, and the otel + baggage processor stamps allowlisted baggage keys (``litellm.team.id``, + ``litellm.metadata.*``, ...) onto the span as attributes, so honoring remote + baggage would let a client spoof a span's identity attribution. + + With no propagated context the returned context carries no span, so the span + starts its own root trace (still linked to the transport). The base context is + explicitly empty so an absent ``traceparent`` can never fall through to the + ambient (stale session) span. + """ + source = carrier if carrier is not None else _mcp_message_trace_carrier.get() + parent = _PROPAGATOR.extract(dict(source or {}), context=Context()) + transport = request_root_span() + links = (Link(transport.get_span_context()),) if transport is not None else () + return parent, links + + def is_recordable_span(obj: object) -> bool: """True if ``obj`` is a live span with a valid context (safe to parent under).""" if not isinstance(obj, Span): diff --git a/litellm/integrations/otel/plumbing/events.py b/litellm/integrations/otel/plumbing/events.py new file mode 100644 index 00000000000..f674526d04f --- /dev/null +++ b/litellm/integrations/otel/plumbing/events.py @@ -0,0 +1,52 @@ +"""GenAI client events: the ``gen_ai.client.operation.exception`` log event. + +The GenAI semantic conventions define exception recording for client +operations as a log-based event (severity WARN) carrying the ``exception.*`` +attribute trio, correlated to the failed span through the trace/span ids — +not as a span attribute or span event. This module owns building and +emitting that event; the exporter pipeline it rides is built in +:mod:`litellm.integrations.otel.plumbing.providers`. +""" + +from dataclasses import dataclass + +from opentelemetry._events import Event, EventLogger +from opentelemetry._logs.severity import SeverityNumber +from opentelemetry.trace import SpanContext + +from litellm.integrations.otel.model.semconv import ExceptionEvent, GenAIEvent + + +@dataclass(frozen=True, slots=True) +class GenAIEventRecorder: + event_logger: EventLogger + + def record_operation_exception( + self, + span_context: SpanContext, + error_type: str, + message: str, + stack_trace: str | None, + timestamp_ns: int | None, + ) -> None: + # ``exception.type`` and ``exception.message`` are the semconv-required + # pair and always ride the event; only the recommended stacktrace is + # conditional on the payload carrying one. + stacktrace = ((ExceptionEvent.STACKTRACE, stack_trace),) if stack_trace else () + self.event_logger.emit( + Event( + name=GenAIEvent.OPERATION_EXCEPTION, + timestamp=timestamp_ns, + trace_id=span_context.trace_id, + span_id=span_context.span_id, + trace_flags=span_context.trace_flags, + severity_number=SeverityNumber.WARN, + attributes=dict( + ( + (ExceptionEvent.TYPE, error_type), + (ExceptionEvent.MESSAGE, message), + *stacktrace, + ) + ), + ) + ) diff --git a/litellm/integrations/otel/plumbing/metrics.py b/litellm/integrations/otel/plumbing/metrics.py index cb1f9214876..50d0fb75962 100644 --- a/litellm/integrations/otel/plumbing/metrics.py +++ b/litellm/integrations/otel/plumbing/metrics.py @@ -21,6 +21,7 @@ from litellm.integrations.opentelemetry import ( _build_metric_attribute_filter, _resolve_metric_attribute_filter, ) +from litellm.integrations.otel.model.metadata import time_to_first_chunk_seconds from litellm.integrations.otel.model.semconv import Metric, resolve_operation from litellm.integrations.otel.model.utils import to_seconds from litellm.litellm_core_utils.safe_json_dumps import safe_dumps @@ -181,13 +182,10 @@ class GenAIMetricRecorder: self._metrics.token_usage.record(usage.get("completion_tokens", 0), attributes=out_attrs) def _record_time_to_first_token(self, kwargs: Mapping[str, Any], common_attrs: dict) -> None: - if not kwargs.get("optional_params", {}).get("stream", False): + time_to_first_chunk = time_to_first_chunk_seconds(kwargs) + if time_to_first_chunk is None: return - api_call_start = to_seconds(kwargs.get("api_call_start_time")) - completion_start = to_seconds(kwargs.get("completion_start_time")) - if api_call_start is None or completion_start is None: - return - self._metrics.time_to_first_token.record(completion_start - api_call_start, attributes=common_attrs) + self._metrics.time_to_first_token.record(time_to_first_chunk, attributes=common_attrs) def _record_time_per_output_token( self, diff --git a/litellm/integrations/otel/plumbing/providers.py b/litellm/integrations/otel/plumbing/providers.py index ac971c6daa8..ced65aa1ec3 100644 --- a/litellm/integrations/otel/plumbing/providers.py +++ b/litellm/integrations/otel/plumbing/providers.py @@ -2,9 +2,20 @@ from typing import TYPE_CHECKING, Any, Callable, Iterable -from opentelemetry import baggage, metrics +from opentelemetry import _logs, baggage, metrics +from opentelemetry._events import EventLogger +from opentelemetry._logs import LoggerProvider, NoOpLoggerProvider from opentelemetry.context import Context from opentelemetry.metrics import MeterProvider, NoOpMeterProvider +from opentelemetry.sdk._events import EventLoggerProvider +from opentelemetry.sdk._logs import LoggerProvider as SDKLoggerProvider +from opentelemetry.sdk._logs.export import ( + BatchLogRecordProcessor, + ConsoleLogExporter, + InMemoryLogExporter, + LogExporter, + SimpleLogRecordProcessor, +) from opentelemetry.sdk.metrics import MeterProvider as SDKMeterProvider from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor, TracerProvider @@ -224,6 +235,112 @@ def build_metric_reader(config: OpenTelemetryV2Config) -> "MetricReader": return PeriodicExportingMetricReader(exporter, export_interval_millis=5000) +def _otlp_logs_endpoint(endpoint: str | None) -> str | None: + """Point an OTLP/HTTP base endpoint at the ``/v1/logs`` signal path. + + The OTLP/HTTP exporter only appends ``/v1/logs`` when it reads + ``OTEL_EXPORTER_OTLP_ENDPOINT`` itself; an explicitly passed endpoint is used + verbatim, so a base URL would POST to the root. Mirror ``_otlp_traces_endpoint`` + for the logs signal (rewriting a sibling signal path when present). + """ + if not endpoint: + return endpoint + endpoint = endpoint.rstrip("/") + if endpoint.endswith("/v1/logs"): + return endpoint + for other_signal in ("/v1/traces", "/v1/metrics"): + if endpoint.endswith(other_signal): + return endpoint[: -len(other_signal)] + "/v1/logs" + return endpoint + "/v1/logs" + + +def build_log_exporter(config: OpenTelemetryV2Config) -> LogExporter: + """Build a log exporter mirroring the exporter selection of the other signals. + + ``console`` (and any unrecognized kind) exports to the console; ``otlp_http`` + and ``otlp_grpc`` export over OTLP with the configured endpoint/headers; + ``in_memory`` buffers for tests. Like GenAI metrics, events ride the + single-destination shorthand fields, not the multi-exporter ``exporters`` list. + """ + kind = (config.exporter or "console").lower() + if kind in ("in_memory", "inmemory", "memory"): + return InMemoryLogExporter() + if kind in ("otlp_http", "http", "http/protobuf", "http/json"): + from opentelemetry.exporter.otlp.proto.http._log_exporter import ( + OTLPLogExporter as HTTPLogExporter, + ) + + return HTTPLogExporter( + endpoint=_otlp_logs_endpoint(config.endpoint), + headers=parse_headers(config.headers), + ) + if kind in ("otlp_grpc", "grpc"): + try: + from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( + OTLPLogExporter as GRPCLogExporter, + ) + except ImportError as exc: + raise ImportError( + "OpenTelemetry OTLP gRPC log exporter is not available. Install " + "`opentelemetry-exporter-otlp` and `grpcio` (or `litellm[grpc]`)." + ) from exc + + return GRPCLogExporter(endpoint=config.endpoint, headers=parse_headers(config.headers)) + return ConsoleLogExporter() + + +def build_logger_provider( + config: OpenTelemetryV2Config, + log_exporter: LogExporter | None = None, +) -> SDKLoggerProvider: + """Build the :class:`LoggerProvider` GenAI events export through. + + ``log_exporter`` is an explicit override (tests inject an + ``InMemoryLogExporter``); otherwise the exporter is selected from the config's + exporter kind via :func:`build_log_exporter`. Console and in-memory exporters + get a Simple processor (synchronous export, which tests rely on), everything + else a Batch processor — the same split as span processing. + """ + exporter = log_exporter if log_exporter is not None else build_log_exporter(config) + provider = SDKLoggerProvider(resource=build_resource(config)) + use_simple = isinstance(exporter, (ConsoleLogExporter, InMemoryLogExporter)) + provider.add_log_record_processor( + SimpleLogRecordProcessor(exporter) if use_simple else BatchLogRecordProcessor(exporter) + ) + return provider + + +def resolve_logger_provider( + config: OpenTelemetryV2Config, + logger_provider: SDKLoggerProvider | None = None, +) -> SDKLoggerProvider | None: + """Resolve the :class:`LoggerProvider` GenAI events record through, or ``None`` + when the operator has opted out of the logs signal. + + Same resolution order as :func:`resolve_meter_provider`: an injected provider + wins (DI/tests); an operator-configured SDK global is reused so events ride + their pipeline; an explicit ``NoOpLoggerProvider`` global is an opt-out and + yields ``None``, so no event is ever built. Only the default placeholder + global makes V2 build a provider from the config and publish it as the global. + """ + if logger_provider is not None: + return logger_provider + + existing: LoggerProvider = _logs.get_logger_provider() + if isinstance(existing, SDKLoggerProvider): + return existing + if isinstance(existing, NoOpLoggerProvider): + return None + + provider = build_logger_provider(config) + _logs.set_logger_provider(provider) + return provider + + +def get_event_logger(provider: SDKLoggerProvider, name: str = "litellm") -> EventLogger: + return EventLoggerProvider(logger_provider=provider).get_event_logger(name, litellm_version) + + def build_meter_provider( config: OpenTelemetryV2Config, metric_reader: "MetricReader | None" = None, diff --git a/litellm/integrations/otel/runtime.py b/litellm/integrations/otel/runtime.py index ac3b991c971..eb512375023 100644 --- a/litellm/integrations/otel/runtime.py +++ b/litellm/integrations/otel/runtime.py @@ -8,7 +8,23 @@ identity unconditionally. """ from contextlib import contextmanager -from typing import Any, Iterator +from functools import cache +from typing import Any, Callable, Iterator, Optional + + +@cache +def _otel_runtime() -> "Optional[tuple[Callable[[str], Any], Callable[..., None]]]": + """Resolve the SDK-backed hooks once and cache the outcome, absence included. + + CPython never caches a failed import, so without this memoization every call + site re-attempts the import on each request; when the OTel SDK is not installed + that re-scans ``sys.path`` and contends on the import lock on the hot path. + """ + try: + from litellm.integrations.otel import logger + except Exception: + return None + return (logger.phase_span, logger.seed_request_identity) @contextmanager @@ -18,21 +34,17 @@ def phase_span(name: str) -> "Iterator[Any]": Yields ``None`` (a plain no-op) when the OTel SDK is unavailable or V2 is not the active logger. """ - try: - from litellm.integrations.otel.logger import phase_span as _phase_span - except Exception: + runtime = _otel_runtime() + if runtime is None: yield None return - with _phase_span(name) as span: + with runtime[0](name) as span: yield span def seed_request_identity(user_api_key_dict: Any, model: Any = None) -> None: """Seed request-identity Baggage at the auth boundary (no-op without V2).""" - try: - from litellm.integrations.otel.logger import ( - seed_request_identity as _seed_request_identity, - ) - except Exception: + runtime = _otel_runtime() + if runtime is None: return - _seed_request_identity(user_api_key_dict, model=model) + runtime[1](user_api_key_dict, model=model) diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py index 1f516e9dc93..f575372fc3d 100644 --- a/litellm/integrations/prometheus.py +++ b/litellm/integrations/prometheus.py @@ -4,6 +4,7 @@ from __future__ import annotations import asyncio +import math import os import sys from datetime import datetime, timedelta @@ -65,6 +66,26 @@ if TYPE_CHECKING: else: AsyncIOScheduler = Any +_DEFAULT_BUDGET_METRICS_PER_REQUEST_TIMEOUT = 5.0 + + +def _get_budget_metrics_per_request_timeout() -> float: + raw = os.getenv("PROMETHEUS_BUDGET_METRICS_PER_REQUEST_TIMEOUT") + if raw is None: + return _DEFAULT_BUDGET_METRICS_PER_REQUEST_TIMEOUT + try: + parsed = float(raw) + except ValueError: + parsed = None + if parsed is None or not math.isfinite(parsed) or parsed <= 0: + verbose_logger.debug( + "[Non-Blocking] Prometheus: invalid PROMETHEUS_BUDGET_METRICS_PER_REQUEST_TIMEOUT=%r; using default %ss.", + raw, + _DEFAULT_BUDGET_METRICS_PER_REQUEST_TIMEOUT, + ) + return _DEFAULT_BUDGET_METRICS_PER_REQUEST_TIMEOUT + return parsed + class PrometheusLogger(CustomLogger): # Class variables or attributes @@ -593,6 +614,21 @@ class PrometheusLogger(CustomLogger): labelnames=[], ) + ######################################## + # MCP Tool Call Metrics + ######################################## + self.litellm_mcp_tool_calls_total = self._counter_factory( + name="litellm_mcp_tool_calls_total", + documentation="Total MCP tool calls, segmented by tool and server name", + labelnames=self.get_labels_for_metric("litellm_mcp_tool_calls_total"), + ) + + self.litellm_mcp_tool_call_spend_metric = self._counter_factory( + name="litellm_mcp_tool_call_spend_metric", + documentation="Total spend on MCP tool calls, segmented by tool and server name", + labelnames=self.get_labels_for_metric("litellm_mcp_tool_call_spend_metric"), + ) + except Exception as e: print_verbose(f"Got exception on init prometheus client {str(e)}") raise e @@ -1300,6 +1336,13 @@ class PrometheusLogger(CustomLogger): label_context=label_context, ) + # MCP tool call metrics + self._increment_mcp_tool_call_metrics( + standard_logging_payload=standard_logging_payload, + enum_values=enum_values, + response_cost=response_cost, + ) + # increment litellm_proxy_total_requests_metric for all successful requests # (both streaming and non-streaming) in this single location to prevent # double-counting that occurs when async_post_call_success_hook also increments @@ -1521,6 +1564,49 @@ class PrometheusLogger(CustomLogger): amount=float(provider_cache_creation_tokens), ) + def _increment_mcp_tool_call_metrics( + self, + standard_logging_payload: StandardLoggingPayload, + enum_values: UserAPIKeyLabelValues, + response_cost: float, + ) -> None: + metadata = standard_logging_payload.get("metadata") + if not isinstance(metadata, dict): + return + mcp_meta = metadata.get("mcp_tool_call_metadata") + if not isinstance(mcp_meta, dict): + return + + mcp_enum_values = UserAPIKeyLabelValues( + mcp_tool_name=mcp_meta.get("name"), + mcp_server_name=mcp_meta.get("mcp_server_name"), + hashed_api_key=enum_values.hashed_api_key, + api_key_alias=enum_values.api_key_alias, + team=enum_values.team, + team_alias=enum_values.team_alias, + user=enum_values.user, + end_user=enum_values.end_user, + ) + mcp_label_context = PrometheusLabelFactoryContext(mcp_enum_values) + + PrometheusLogger._inc_labeled_counter( + self, + self.litellm_mcp_tool_calls_total, + "litellm_mcp_tool_calls_total", + mcp_enum_values, + label_context=mcp_label_context, + ) + + if response_cost > 0: + PrometheusLogger._inc_labeled_counter( + self, + self.litellm_mcp_tool_call_spend_metric, + "litellm_mcp_tool_call_spend_metric", + mcp_enum_values, + label_context=mcp_label_context, + amount=response_cost, + ) + async def _increment_remaining_budget_metrics( self, user_api_team: Optional[str], @@ -1532,6 +1618,14 @@ class PrometheusLogger(CustomLogger): user_id: Optional[str] = None, user_api_key_org_id: Optional[str] = None, ): + if ( + isinstance(self.litellm_remaining_team_budget_metric, NoOpMetric) + and isinstance(self.litellm_remaining_api_key_budget_metric, NoOpMetric) + and isinstance(self.litellm_remaining_user_budget_metric, NoOpMetric) + and isinstance(self.litellm_remaining_org_budget_metric, NoOpMetric) + ): + return + _metadata = litellm_params.get("metadata") or {} _team_spend = _metadata.get("user_api_key_team_spend", None) _team_max_budget = _metadata.get("user_api_key_team_max_budget", None) @@ -1542,7 +1636,15 @@ class PrometheusLogger(CustomLogger): _user_spend = _metadata.get("user_api_key_user_spend", None) _user_max_budget = _metadata.get("user_api_key_user_max_budget", None) - results = await asyncio.gather( + # Bound the per-request budget-metric emission so that slow Redis/DB + # lookups under load cannot consume the whole LoggingWorker watchdog + # (LOGGING_WORKER_MAX_TIME_PER_COROUTINE, default 20s) and get the entire + # success-logging event cancelled. Budget gauges are also refreshed by the + # periodic cron every PROMETHEUS_BUDGET_METRICS_REFRESH_INTERVAL_MINUTES, + # so dropping one slow per-request emission only loses sub-cron real-time + # detail, not correctness. + budget_metrics_timeout = _get_budget_metrics_per_request_timeout() + gather_coro = asyncio.gather( self._set_api_key_budget_metrics_after_api_request( user_api_key=user_api_key, user_api_key_alias=user_api_key_alias, @@ -1569,6 +1671,16 @@ class PrometheusLogger(CustomLogger): ), return_exceptions=True, ) + try: + results = await asyncio.wait_for(gather_coro, timeout=budget_metrics_timeout) + except asyncio.TimeoutError: + verbose_logger.debug( + "[Non-Blocking] Prometheus: per-request budget metric emission " + "exceeded %ss under load; skipping (values are refreshed by the " + "periodic budget-metrics cron job).", + budget_metrics_timeout, + ) + return for i, r in enumerate(results): if isinstance(r, Exception): verbose_logger.debug( @@ -1939,6 +2051,43 @@ class PrometheusLogger(CustomLogger): return False + @staticmethod + def _extract_api_provider_from_request_data(request_data: dict) -> Optional[str]: + """ + Best-effort provider for the client-side failure path. + + A request can fail before a deployment is resolved, so the provider is + not always known. Prefer the resolved ``custom_llm_provider`` on + ``litellm_params``, then any provider recovered onto a partial + ``standard_logging_object`` (e.g. a stream that broke mid-flight), and + finally infer it from the requested model name (e.g. ``gpt-4o-mini`` -> + ``openai``) since the proxy's failure ``request_data`` usually carries + only the client-supplied model. Return ``None`` when it cannot be + determined so the label emits empty rather than a guess. + """ + litellm_params = request_data.get("litellm_params") or {} + provider = litellm_params.get("custom_llm_provider") + if provider: + return provider + standard_logging_object = request_data.get("standard_logging_object") or {} + provider = standard_logging_object.get("custom_llm_provider") + if provider: + return provider + model = litellm_params.get("model") or request_data.get("model") + if not model: + return None + try: + return litellm.get_llm_provider(model=model)[1] or None + except litellm.exceptions.BadRequestError: + return None + except Exception as e: # noqa: BLE001 - metrics labeling must never break request/failure handling + verbose_logger.debug( + "prometheus: unexpected error inferring api_provider from model=%s: %s", + model, + e, + ) + return None + async def async_post_call_failure_hook( self, request_data: dict, @@ -1974,6 +2123,7 @@ class PrometheusLogger(CustomLogger): _metadata = request_data.get("metadata", {}) or {} model_id = _metadata.get("model_info", {}).get("id") or request_data.get("model_info", {}).get("id") rate_limit_category, rate_limit_type = self._extract_rate_limit_labels(original_exception) + api_provider = self._extract_api_provider_from_request_data(request_data) enum_values = UserAPIKeyLabelValues( end_user=user_api_key_dict.end_user_id, user=user_api_key_dict.user_id, @@ -1995,6 +2145,7 @@ class PrometheusLogger(CustomLogger): client_ip=_metadata.get("requester_ip_address"), user_agent=_metadata.get("user_agent"), model_id=model_id, + api_provider=api_provider, stream=(str(request_data.get("stream")) if litellm.prometheus_emit_stream_label else None), ) _label_ctx = PrometheusLabelFactoryContext(enum_values) @@ -3189,6 +3340,9 @@ class PrometheusLogger(CustomLogger): - looks up team info from db if not available in metadata - Set team budget metrics """ + if isinstance(self.litellm_remaining_team_budget_metric, NoOpMetric): + return + if user_api_team: team_object = await self._assemble_team_object( team_id=user_api_team, @@ -3310,6 +3464,9 @@ class PrometheusLogger(CustomLogger): - Fetches org info via cache (get_org_object) - Sets org budget metrics """ + if isinstance(self.litellm_remaining_org_budget_metric, NoOpMetric): + return + if not org_id: return @@ -3439,6 +3596,9 @@ class PrometheusLogger(CustomLogger): key_max_budget: Optional[float], key_spend: Optional[float], ): + if isinstance(self.litellm_remaining_api_key_budget_metric, NoOpMetric): + return + if user_api_key: user_api_key_dict = await self._assemble_key_object( user_api_key=user_api_key, @@ -3476,6 +3636,7 @@ class PrometheusLogger(CustomLogger): hashed_token=user_api_key_dict.token, prisma_client=prisma_client, user_api_key_cache=user_api_key_cache, + check_cache_only=True, ) if key_object: user_api_key_dict.budget_reset_at = key_object.budget_reset_at @@ -3498,6 +3659,9 @@ class PrometheusLogger(CustomLogger): - looks up user info from db if not available in metadata - Set user budget metrics """ + if isinstance(self.litellm_remaining_user_budget_metric, NoOpMetric): + return + if user_id: user_object = await self._assemble_user_object( user_id=user_id, @@ -3804,6 +3968,10 @@ def _get_combined_custom_metadata_from_standard_logging_payload( ) -> Dict[str, Any]: """ Combine the metadata sources that can supply custom Prometheus labels. + + Includes top-level scalar fields from the standard logging metadata (e.g. + user_api_key_project_alias, user_api_key_team_alias) so they are accessible + via custom_prometheus_metadata_labels configuration. """ if not isinstance(standard_logging_payload, dict): return {} @@ -3817,6 +3985,7 @@ def _get_combined_custom_metadata_from_standard_logging_payload( spend_logs_metadata = standard_logging_metadata.get("spend_logs_metadata") return { + **{k: v for k, v in standard_logging_metadata.items() if not isinstance(v, dict)}, **(requester_metadata if isinstance(requester_metadata, dict) else {}), **(user_api_key_auth_metadata if isinstance(user_api_key_auth_metadata, dict) else {}), **(spend_logs_metadata if isinstance(spend_logs_metadata, dict) else {}), diff --git a/litellm/integrations/rubrik.py b/litellm/integrations/rubrik.py index 2b54a411ec7..11809ee6361 100644 --- a/litellm/integrations/rubrik.py +++ b/litellm/integrations/rubrik.py @@ -7,7 +7,7 @@ import time import urllib.parse import uuid from collections import Counter -from typing import TYPE_CHECKING, Any, Literal, Optional +from typing import TYPE_CHECKING, Any, List, Literal, Optional import httpx from litellm._logging import verbose_logger @@ -52,6 +52,10 @@ class _MalformedToolBlockingResponseError(Exception): class RubrikLogger(CustomGuardrail, CustomBatchLogger): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [GuardrailEventHooks.pre_call, GuardrailEventHooks.post_call] + def __init__( self, api_key: str | None = None, @@ -69,6 +73,7 @@ class RubrikLogger(CustomGuardrail, CustomBatchLogger): kwargs["event_hook"] = kwargs.get("event_hook") or GuardrailEventHooks.post_call if kwargs.get("default_on") is None: kwargs["default_on"] = True + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__( flush_lock=self.flush_lock, **kwargs, diff --git a/litellm/integrations/s3_v2.py b/litellm/integrations/s3_v2.py index 939289f96ea..5b953035cfd 100644 --- a/litellm/integrations/s3_v2.py +++ b/litellm/integrations/s3_v2.py @@ -54,6 +54,7 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM): s3_strip_base64_files: bool = False, s3_use_key_prefix: bool = False, s3_use_virtual_hosted_style: bool = False, + s3_server_side_encryption: Optional[str] = None, s3_callback_params_override: Optional[dict] = None, **kwargs, ): @@ -92,6 +93,7 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM): s3_strip_base64_files=s3_strip_base64_files, s3_use_key_prefix=s3_use_key_prefix, s3_use_virtual_hosted_style=s3_use_virtual_hosted_style, + s3_server_side_encryption=s3_server_side_encryption, ) verbose_logger.debug(f"s3 logger using endpoint url {s3_endpoint_url}") @@ -145,6 +147,7 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM): s3_strip_base64_files: bool = False, s3_use_key_prefix: bool = False, s3_use_virtual_hosted_style: bool = False, + s3_server_side_encryption: Optional[str] = None, params_source: Optional[dict] = None, ): """ @@ -194,6 +197,8 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM): bool(params.get("s3_use_virtual_hosted_style", False)) or s3_use_virtual_hosted_style ) + self.s3_server_side_encryption = params.get("s3_server_side_encryption") or s3_server_side_encryption + return async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): @@ -273,6 +278,7 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM): async def async_upload_data_to_s3(self, batch_logging_element: s3BatchLoggingElement): try: + import base64 import hashlib import requests @@ -317,14 +323,23 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM): # Calculate SHA256 hash of the content content_hash = hashlib.sha256(json_string.encode("utf-8")).hexdigest() + content_md5 = base64.b64encode( + hashlib.md5(json_string.encode("utf-8"), usedforsecurity=False).digest() + ).decode() # Prepare the request headers = { "Content-Type": "application/json", + "Content-MD5": content_md5, "x-amz-content-sha256": content_hash, "Content-Language": "en", "Content-Disposition": f'inline; filename="{batch_logging_element.s3_object_download_filename}"', "Cache-Control": "private, immutable, max-age=31536000, s-maxage=0", + **( + {"x-amz-server-side-encryption": self.s3_server_side_encryption} + if self.s3_server_side_encryption + else {} + ), } req = requests.Request("PUT", url, data=json_string, headers=headers) prepped = req.prepare() @@ -447,6 +462,7 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM): def upload_data_to_s3(self, batch_logging_element: s3BatchLoggingElement): try: + import base64 import hashlib import requests @@ -482,14 +498,23 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM): # Calculate SHA256 hash of the content content_hash = hashlib.sha256(json_string.encode("utf-8")).hexdigest() + content_md5 = base64.b64encode( + hashlib.md5(json_string.encode("utf-8"), usedforsecurity=False).digest() + ).decode() # Prepare the request headers = { "Content-Type": "application/json", + "Content-MD5": content_md5, "x-amz-content-sha256": content_hash, "Content-Language": "en", "Content-Disposition": f'inline; filename="{batch_logging_element.s3_object_download_filename}"', "Cache-Control": "private, immutable, max-age=31536000, s-maxage=0", + **( + {"x-amz-server-side-encryption": self.s3_server_side_encryption} + if self.s3_server_side_encryption + else {} + ), } req = requests.Request("PUT", url, data=json_string, headers=headers) prepped = req.prepare() diff --git a/litellm/integrations/websearch_interception/handler.py b/litellm/integrations/websearch_interception/handler.py index 2e11405af3f..00c67e9f0fb 100644 --- a/litellm/integrations/websearch_interception/handler.py +++ b/litellm/integrations/websearch_interception/handler.py @@ -31,6 +31,7 @@ from litellm.types.integrations.websearch_interception import ( WebSearchInterceptionConfig, ) from litellm.types.integrations.custom_logger import ( + CHAT_COMPLETION_AGENTIC_SURFACE, AgenticLoopPlan, AgenticLoopRequestPatch, ) @@ -90,6 +91,7 @@ class WebSearchInterceptionLogger(CustomLogger): messages: List[Dict], tools: Optional[List[Dict]], custom_llm_provider: Optional[str], + kwargs: Optional[dict[str, Any]] = None, ) -> Optional[Dict[str, Any]]: """ Short-circuit web-search-only requests by executing the search directly. @@ -119,21 +121,26 @@ class WebSearchInterceptionLogger(CustomLogger): if self.enabled_providers is not None and provider_str not in self.enabled_providers: return None - # Only short-circuit for providers without native Anthropic Messages - # support. Providers that have a BaseAnthropicMessagesConfig (bedrock, - # vertex_ai, azure_ai, anthropic) already use the agentic loop, which - # includes a follow-up LLM call to synthesize the answer from search - # results. Short-circuiting those would skip that synthesis step and - # return raw search text — a regression for existing users. + # Only short-circuit for providers whose Anthropic Messages agentic loop + # does not run web_search itself. Providers that have a + # BaseAnthropicMessagesConfig which handles web search natively (bedrock, + # vertex_ai, azure_ai, anthropic) already perform the search plus a + # follow-up LLM synthesis step; short-circuiting those would skip that + # synthesis and return raw search text — a regression for existing users. + # + # github_copilot has a BaseAnthropicMessagesConfig (added for thinking + # passthrough) but does not handle web_search natively, so its config + # returns handles_web_search_natively() == False and we still short-circuit + # web-search-only requests against it. try: provider_enum = LlmProviders(provider_str) anthropic_config = ProviderConfigManager.get_provider_anthropic_messages_config( model=model, provider=provider_enum ) - if anthropic_config is not None: + if anthropic_config is not None and anthropic_config.handles_web_search_natively(): verbose_logger.debug( f"WebSearchInterception: Skipping short-circuit for {provider_str} " - "(provider has native Anthropic Messages support, using agentic loop)" + "(provider handles web search natively via the agentic loop)" ) return None except (ValueError, Exception): @@ -170,7 +177,10 @@ class WebSearchInterceptionLogger(CustomLogger): # Execute search — keep the structured SearchResponse so the native # block can carry per-result url/title/page_age. try: - search_result_text, structured = await self._execute_search(query) + if kwargs is None: + search_result_text, structured = await self._execute_search(query) + else: + search_result_text, structured = await self._execute_search(query, kwargs=kwargs) except Exception as e: verbose_logger.error(f"WebSearchInterception: Short-circuit search failed: {e}") search_result_text, structured = f"Search failed: {e}", None @@ -440,12 +450,16 @@ class WebSearchInterceptionLogger(CustomLogger): custom_llm_provider: str, kwargs: Dict, ) -> Tuple[bool, Dict]: - """ - Check if WebSearch tool interception is needed for Anthropic Messages API. - - This is the legacy method for Anthropic-style responses. - For chat completions, use async_should_run_chat_completion_agentic_loop instead. - """ + if kwargs.get("_agentic_loop_api_surface") == CHAT_COMPLETION_AGENTIC_SURFACE: + return await self.async_should_run_chat_completion_agentic_loop( + response=response, + model=model, + messages=messages, + tools=tools, + stream=stream, + custom_llm_provider=custom_llm_provider, + kwargs=kwargs, + ) verbose_logger.debug(f"WebSearchInterception: Hook called! provider={custom_llm_provider}, stream={stream}") verbose_logger.debug(f"WebSearchInterception: Response type: {type(response)}") @@ -629,6 +643,18 @@ class WebSearchInterceptionLogger(CustomLogger): stream: bool, kwargs: Dict, ) -> AgenticLoopPlan: + if kwargs.get("_agentic_loop_api_surface") == CHAT_COMPLETION_AGENTIC_SURFACE: + return await self.async_build_chat_completion_agentic_loop_plan( + tools=tools, + model=model, + messages=messages, + response=response, + optional_params=anthropic_messages_optional_request_params, + logging_obj=logging_obj, + stream=stream, + kwargs=kwargs, + ) + tool_calls = tools["tool_calls"] thinking_blocks = tools.get("thinking_blocks", []) request_patch, structured_results = await self._build_anthropic_request_patch( @@ -914,7 +940,7 @@ class WebSearchInterceptionLogger(CustomLogger): query = tool_call["input"].get("query") if query: verbose_logger.debug(f"WebSearchInterception: Queuing search for query='{query}'") - search_tasks.append(self._execute_search(query)) + search_tasks.append(self._execute_search(query, kwargs=kwargs)) else: verbose_logger.debug(f"WebSearchInterception: Tool call {tool_call['id']} has no query") # Add empty result for tools without query @@ -987,7 +1013,9 @@ class WebSearchInterceptionLogger(CustomLogger): ) return patch, structured_results - async def _execute_search(self, query: str) -> Tuple[str, Optional[SearchResponse]]: + async def _execute_search( + self, query: str, kwargs: Optional[dict[str, Any]] = None + ) -> Tuple[str, Optional[SearchResponse]]: """ Execute a single web search using router's search tools. @@ -1009,36 +1037,13 @@ class WebSearchInterceptionLogger(CustomLogger): ) llm_router = None - # Determine search provider from router's search_tools + search_tool = self._select_search_tool_from_router(llm_router=llm_router) search_provider: Optional[str] = None - if llm_router is not None and hasattr(llm_router, "search_tools"): - if self.search_tool_name: - # Find specific search tool by name - matching_tools = [ - tool - for tool in llm_router.search_tools - if tool.get("search_tool_name") == self.search_tool_name - ] - if matching_tools: - search_tool = matching_tools[0] - search_provider = search_tool.get("litellm_params", {}).get("search_provider") - verbose_logger.debug( - f"WebSearchInterception: Found search tool '{self.search_tool_name}' " - f"with provider '{search_provider}'" - ) - else: - verbose_logger.debug( - f"WebSearchInterception: Search tool '{self.search_tool_name}' not found in router, " - "falling back to first available or perplexity" - ) - - # If no specific tool or not found, use first available - if not search_provider and llm_router.search_tools: - first_tool = llm_router.search_tools[0] - search_provider = first_tool.get("litellm_params", {}).get("search_provider") - verbose_logger.debug( - f"WebSearchInterception: Using first available search tool with provider '{search_provider}'" - ) + search_litellm_params: dict[str, Any] = {} + if search_tool is not None: + await self._authorize_search_tool(search_tool=search_tool, kwargs=kwargs) + search_litellm_params = dict(search_tool.get("litellm_params", {}) or {}) + search_provider = search_litellm_params.get("search_provider") # Fallback to perplexity if no router or no search tools configured if not search_provider: @@ -1051,7 +1056,12 @@ class WebSearchInterceptionLogger(CustomLogger): verbose_logger.debug( f"WebSearchInterception: Executing search for '{query}' using provider '{search_provider}'" ) - result = await litellm.asearch(query=query, search_provider=search_provider) + search_kwargs = { + key: value + for key, value in search_litellm_params.items() + if key != "search_provider" and value is not None + } + result = await litellm.asearch(query=query, search_provider=search_provider, **search_kwargs) # Format using transformation function search_result_text = WebSearchTransformation.format_search_response(result) @@ -1064,6 +1074,107 @@ class WebSearchInterceptionLogger(CustomLogger): verbose_logger.error(f"WebSearchInterception: Search failed for '{query}': {str(e)}") raise + async def _authorize_search_tool( + self, + search_tool: dict[str, Any], + kwargs: Optional[dict[str, Any]], + ) -> None: + search_tool_name = search_tool.get("search_tool_name") + if not isinstance(search_tool_name, str) or not search_tool_name: + return + + user_api_key_auth = self._get_user_api_key_auth_from_kwargs(kwargs) + if user_api_key_auth is None: + return + + from litellm.proxy.auth.auth_checks import ( + can_key_call_search_tool, + can_team_call_search_tool, + get_team_object, + ) + + await can_key_call_search_tool( + search_tool_name=search_tool_name, + valid_token=user_api_key_auth, + ) + + team_id = getattr(user_api_key_auth, "team_id", None) + if team_id: + from litellm.proxy.proxy_server import ( + prisma_client, + proxy_logging_obj, + user_api_key_cache, + ) + + team_object = await get_team_object( + team_id=team_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + parent_otel_span=getattr(user_api_key_auth, "parent_otel_span", None), + proxy_logging_obj=proxy_logging_obj, + ) + await can_team_call_search_tool( + search_tool_name=search_tool_name, + team_object=team_object, + ) + + @staticmethod + def _get_user_api_key_auth_from_kwargs(kwargs: Optional[dict[str, Any]]) -> Any: + if not kwargs: + return None + + for metadata_key in ("metadata", "litellm_metadata"): + metadata = kwargs.get(metadata_key) + if isinstance(metadata, dict) and metadata.get("user_api_key_auth") is not None: + return metadata["user_api_key_auth"] + + litellm_params = kwargs.get("litellm_params") + if not isinstance(litellm_params, dict): + return None + + for metadata_key in ("metadata", "litellm_metadata"): + metadata = litellm_params.get(metadata_key) + if isinstance(metadata, dict) and metadata.get("user_api_key_auth") is not None: + return metadata["user_api_key_auth"] + + return None + + def _select_search_tool_from_router(self, llm_router: Any) -> Optional[dict[str, Any]]: + if llm_router is None or not hasattr(llm_router, "search_tools"): + return None + search_tools = list(getattr(llm_router, "search_tools") or []) + return self._select_search_tool_from_list(search_tools=search_tools, source="router") + + def _select_search_tool_from_list( + self, + search_tools: list[dict[str, Any]], + source: str, + ) -> Optional[dict[str, Any]]: + if self.search_tool_name: + matching_tools = [tool for tool in search_tools if tool.get("search_tool_name") == self.search_tool_name] + if matching_tools: + search_provider = (matching_tools[0].get("litellm_params", {}) or {}).get("search_provider") + verbose_logger.debug( + f"WebSearchInterception: Found search tool '{self.search_tool_name}' " + f"from {source} with provider '{search_provider}'" + ) + return matching_tools[0] + verbose_logger.debug( + f"WebSearchInterception: Search tool '{self.search_tool_name}' not found in {source}, " + "falling back to first available or perplexity" + ) + + if search_tools: + first_tool = search_tools[0] + search_provider = (first_tool.get("litellm_params", {}) or {}).get("search_provider") + verbose_logger.debug( + f"WebSearchInterception: Using first available search tool from {source} " + f"with provider '{search_provider}'" + ) + return first_tool + + return None + async def _execute_chat_completion_agentic_loop( self, model: str, @@ -1088,6 +1199,7 @@ class WebSearchInterceptionLogger(CustomLogger): raise ValueError("WebSearchInterception: missing follow-up messages") params = dict(optional_params) params.update(request_patch.optional_params) + params.pop("tool_choice", None) return await litellm.acompletion( model=request_patch.model or model, messages=request_patch.messages, @@ -1122,7 +1234,7 @@ class WebSearchInterceptionLogger(CustomLogger): if query: verbose_logger.debug(f"WebSearchInterception: Queuing search for query='{query}'") - search_tasks.append(self._execute_search(query)) + search_tasks.append(self._execute_search(query, kwargs=kwargs)) else: verbose_logger.debug(f"WebSearchInterception: Tool call {tool_call.get('id')} has no query") # Add empty result for tools without query @@ -1203,6 +1315,7 @@ class WebSearchInterceptionLogger(CustomLogger): if k not in { "tools", + "tool_choice", "extra_body", "model_alias_map", "stream_response", diff --git a/litellm/interactions/streaming_iterator.py b/litellm/interactions/streaming_iterator.py index 45c5443cfd2..0d9d1b4579c 100644 --- a/litellm/interactions/streaming_iterator.py +++ b/litellm/interactions/streaming_iterator.py @@ -174,22 +174,15 @@ class InteractionsAPIStreamingIterator(BaseInteractionsAPIStreamingIterator): logging_response = copy.deepcopy(self.completed_response) asyncio.create_task( - self.logging_obj.async_success_handler( - result=logging_response, + self.logging_obj.dispatch_success_handlers( + logging_response, start_time=self.start_time, end_time=datetime.now(), cache_hit=None, + prefer_async_handlers=True, ) ) - executor.submit( - self.logging_obj.success_handler, - result=logging_response, - cache_hit=None, - start_time=self.start_time, - end_time=datetime.now(), - ) - class SyncInteractionsAPIStreamingIterator(BaseInteractionsAPIStreamingIterator): """ diff --git a/litellm/litellm_core_utils/audio_utils/utils.py b/litellm/litellm_core_utils/audio_utils/utils.py index f86243c73b7..e5007ceec34 100644 --- a/litellm/litellm_core_utils/audio_utils/utils.py +++ b/litellm/litellm_core_utils/audio_utils/utils.py @@ -123,6 +123,34 @@ def process_audio_file(audio_file: FileTypes) -> ProcessedAudioFile: return ProcessedAudioFile(file_content=file_content, filename=filename, content_type=content_type) +BARE_ISO_639_1_TO_BCP47 = { + "en": "en-US", + "es": "es-ES", + "de": "de-DE", + "fr": "fr-FR", + "it": "it-IT", + "pt": "pt-BR", + "ja": "ja-JP", + "ko": "ko-KR", + "zh": "zh-CN", + "ru": "ru-RU", + "hi": "hi-IN", + "ar": "ar-SA", +} + + +def normalize_transcription_language_to_bcp47(language: str) -> str: + """ + OpenAI's transcription `language` param accepts bare ISO-639-1 codes like + ``en``; speech APIs such as Google Speech-to-Text and NVIDIA Riva require + BCP-47 like ``en-US``. Map the most common bare codes and pass through + anything already region-qualified (or unknown, for a clear provider error). + """ + if "-" in language: + return language + return BARE_ISO_639_1_TO_BCP47.get(language.lower(), language) + + def get_audio_file_name(file_obj: FileTypes) -> str: """ Safely get the name of a file-like object or return its string representation. diff --git a/litellm/litellm_core_utils/chat_completion_agentic_loop.py b/litellm/litellm_core_utils/chat_completion_agentic_loop.py index 828605d5ef8..b7262a42324 100644 --- a/litellm/litellm_core_utils/chat_completion_agentic_loop.py +++ b/litellm/litellm_core_utils/chat_completion_agentic_loop.py @@ -137,8 +137,8 @@ async def _execute_chat_completion_agentic_plan( optional_params_for_followup = {**optional_params, **patch.optional_params} if patch.tools is not None: optional_params_for_followup["tools"] = patch.tools - if "tool_choice" not in patch.optional_params: - optional_params_for_followup.pop("tool_choice", None) + if "tool_choice" not in patch.optional_params: + optional_params_for_followup.pop("tool_choice", None) kwargs_for_followup = _filter_followup_kwargs(kwargs) kwargs_for_followup.update( @@ -206,10 +206,11 @@ async def maybe_run_chat_completion_agentic_loop( for callback in callbacks: if not isinstance(callback, CustomLogger): continue + if not _gate_overridden(callback): continue - gate_kwargs = { + hook_kwargs = { **kwargs, "_agentic_loop_api_surface": CHAT_COMPLETION_AGENTIC_SURFACE, "custom_llm_provider": custom_llm_provider, @@ -222,7 +223,7 @@ async def maybe_run_chat_completion_agentic_loop( tools=tools, stream=stream, custom_llm_provider=custom_llm_provider, - kwargs=gate_kwargs, + kwargs=hook_kwargs, ) except Exception as e: verbose_logger.exception( @@ -243,11 +244,6 @@ async def maybe_run_chat_completion_agentic_loop( ) try: - plan_kwargs = { - **kwargs, - "_agentic_loop_api_surface": CHAT_COMPLETION_AGENTIC_SURFACE, - "custom_llm_provider": custom_llm_provider, - } if not _build_plan_overridden(callback): return await callback.async_run_agentic_loop( tools=tool_calls, @@ -258,7 +254,7 @@ async def maybe_run_chat_completion_agentic_loop( anthropic_messages_optional_request_params=optional_params, logging_obj=logging_obj, stream=stream, - kwargs=plan_kwargs, + kwargs=hook_kwargs, ) plan = await callback.async_build_agentic_loop_plan( @@ -270,7 +266,7 @@ async def maybe_run_chat_completion_agentic_loop( anthropic_messages_optional_request_params=optional_params, logging_obj=logging_obj, stream=stream, - kwargs=plan_kwargs, + kwargs=hook_kwargs, ) if plan.response_override is not None: diff --git a/litellm/litellm_core_utils/cli_token_utils.py b/litellm/litellm_core_utils/cli_token_utils.py index eb01359cdc0..e730f60bc3b 100644 --- a/litellm/litellm_core_utils/cli_token_utils.py +++ b/litellm/litellm_core_utils/cli_token_utils.py @@ -7,6 +7,7 @@ This module has no dependencies on proxy code and can be safely imported at the import json import os +import time from pathlib import Path from typing import Optional @@ -68,3 +69,17 @@ def get_litellm_gateway_api_key( if stored_url != expected_base_url.rstrip("/"): return None return token_data["key"] + + +def is_cli_token_fresh(token_data: dict, buffer_hours: float = 0.1) -> bool: + """Check whether a cached CLI token (as stored in token.json) is still + within its expiration window. Used by `lite auth print-token` to fail + fast, without a network round trip, once the cached token is past + `LITELLM_CLI_JWT_EXPIRATION_HOURS`.""" + from litellm.constants import CLI_JWT_EXPIRATION_HOURS + + timestamp = token_data.get("timestamp") + if not isinstance(timestamp, (int, float)): + return False + age_hours = (time.time() - timestamp) / 3600 + return age_hours < (CLI_JWT_EXPIRATION_HOURS - buffer_hours) diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index 2441cbb3903..fdab3d5b9d4 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -95,6 +95,9 @@ class ExceptionCheckers: if "current length is" in _error_str_lowercase and "while limit is" in _error_str_lowercase: return True + if "maximum input length is" in _error_str_lowercase and "tokens" in _error_str_lowercase: + return True + return False @staticmethod @@ -1944,7 +1947,7 @@ def _map_azure_exception( response=getattr(original_exception, "response", None), body=getattr(original_exception, "body", None), ) - elif "invalid_request_error" in error_str: + elif "invalid_request_error" in error_str and getattr(original_exception, "status_code", None) in (None, 400): raise BadRequestError( message=f"AzureException BadRequestError - {message}", llm_provider="azure", @@ -1986,6 +1989,14 @@ def _map_azure_exception( litellm_debug_info=extra_information, response=getattr(original_exception, "response", None), ) + elif original_exception.status_code == 404: + raise NotFoundError( + message=f"AzureException NotFoundError - {message}", + llm_provider="azure", + model=model, + litellm_debug_info=extra_information, + response=getattr(original_exception, "response", None), + ) elif original_exception.status_code == 408: raise Timeout( message=f"AzureException Timeout - {message}", @@ -2173,7 +2184,7 @@ def exception_type( # type: ignore litellm_response_headers = _get_response_headers(original_exception=original_exception) try: error_str = redact_string(str(original_exception)) if _ENABLE_SECRET_REDACTION else str(original_exception) - if model: + if model or custom_llm_provider: if hasattr(original_exception, "message"): error_str = ( redact_string(str(original_exception.message)) diff --git a/litellm/litellm_core_utils/fallback_generalizations.py b/litellm/litellm_core_utils/fallback_generalizations.py index abc171f900a..410bb9623fe 100644 --- a/litellm/litellm_core_utils/fallback_generalizations.py +++ b/litellm/litellm_core_utils/fallback_generalizations.py @@ -3,52 +3,69 @@ Declarative fallback generalizations for unknown / newly-released models. The ``fallback_generalizations`` block in ``model_prices_and_context_window.json`` holds an ordered list of rules. Each rule pairs a single case-insensitive regex -with the metadata to apply when a model name has no exact entry in the cost map. -The metadata is a partial cost-map entry: ``litellm_provider`` drives provider -routing, and the remaining fields (``mode``, ``supports_*``, context window, -pricing, ...) drive ``get_model_info`` / ``supports_*``. +with a ``model_info`` dict, and the structure of ``model_info`` decides which of +two kinds the rule is. -Precedence: rules are evaluated in file order and the first match wins. They are -consulted only after exact and case-insensitive lookups miss, so an exact entry -always takes precedence over a rule. +A ROUTING rule carries exactly one ``model_info`` key, ``litellm_provider``. It is +consumed only by ``get_llm_provider`` bare-id inference: the first routing rule +whose regex matches decides the provider. Routing rules never contribute to model +info. + +A CAPABILITY rule carries any ``model_info`` keys except ``litellm_provider`` +(``mode``, ``supports_*``, context window, pricing, ...). It is consumed by +``get_model_info`` fallback resolution: the ``model_info`` of ALL capability rules +whose regex matches is unioned in file order, with later rules overriding earlier +ones on key conflicts, and the caller backfills ``litellm_provider`` with the +provider it requested. If no capability rule matches, model-info resolution misses +as if no rules existed. + +LEGACY-SCHEMA SHIM (temporary, until the new-schema JSON reaches main): released +proxies fetch this JSON remotely from main, whose block still ships the old schema +where a rule mixes ``litellm_provider`` with capability keys and may inherit a +parent's ``model_info`` via ``extends``. Such a legacy rule is tolerated rather +than skipped: ``extends`` is resolved once at install time (single level, against +raw parents), and the resolved rule acts as BOTH kinds, a routing rule (its +``litellm_provider`` participates in first-hit inference) and a capability rule +(its full ``model_info``, provider included, participates in the union). New-schema +rules never mix the two and never use ``extends``. A rule whose +``litellm_provider`` is not a string is invalid and is warned about and skipped +(a warning rather than a crash, for the same remote-fetch reason). + +Rules are only consulted after exact and case-insensitive lookups miss, so an +exact cost-map entry always takes precedence over any rule. Patterns are matched case-insensitively with ``re.search`` and are not implicitly -anchored: a rule must include ``^`` and ``$`` (as the shipped rules do) to bind to -the whole model name, otherwise it matches as a substring. Keeping anchoring in the -regex makes the rule the single, self-contained source of truth for what it matches. - -A rule may set ``extends`` to the ``name`` of another rule to inherit that rule's -``model_info``; the rule's own ``model_info`` overrides the inherited keys, so a -narrow rule (for example a version-gated capability flag) carries only its delta -instead of duplicating the parent's pricing block. Inheritance is resolved once, -at install time, against each rule's raw (unresolved) ``model_info``; it is a -single level (a parent that itself extends is not chained). +anchored: a rule must include ``^`` and ``$`` to bind to the whole model name, +otherwise it matches as a substring. Keeping anchoring in the regex makes the rule +the single, self-contained source of truth for what it matches. Any other keys on a rule (for example a free-text ``description`` documenting what the regex matches) are ignored by the engine and exist only for the reader. -The compiled-regex list is built once and cached. ``match_fallback_generalization`` -is O(number of rules); callers must only invoke it on a cache miss. +Rules are compiled and classified once, at install time. The match functions are +O(number of rules); callers must only invoke them on a cache miss. """ import re -from typing import Optional +from dataclasses import dataclass +from typing import Optional, Union from litellm._logging import verbose_logger NAME_FIELD = "name" PATTERN_FIELD = "pattern" MODEL_INFO_FIELD = "model_info" -EXTENDS_FIELD = "extends" +PROVIDER_KEY = "litellm_provider" +LEGACY_EXTENDS_FIELD = "extends" -def _resolve_extends(rules: list) -> list: - """Expand ``extends`` inheritance so each rule's ``model_info`` is self-contained. +def _resolve_legacy_extends(rules: list) -> list: + """Expand legacy ``extends`` inheritance so each rule's ``model_info`` is self-contained. - A rule with ``extends: `` is rewritten with ``model_info`` set to the parent's - ``model_info`` overlaid by its own. Resolution is single-level and uses each rule's - raw ``model_info`` as the parent source. Non-dict rules and dangling parents are - passed through unchanged. + Compatibility shim for the old remote schema: single level, resolved against each + parent's raw ``model_info``, with the child's own keys winning on conflict. Non-dict + rules and dangling parents pass through unchanged; new-schema rules carry no + ``extends`` and are untouched. """ base_by_name = { rule[NAME_FIELD]: rule[MODEL_INFO_FIELD] @@ -58,84 +75,138 @@ def _resolve_extends(rules: list) -> list: and isinstance(rule.get(MODEL_INFO_FIELD), dict) } - def resolved(rule: dict) -> dict: - parent_name = rule.get(EXTENDS_FIELD) + def resolved(rule: object) -> object: + if not isinstance(rule, dict): + return rule + parent_name = rule.get(LEGACY_EXTENDS_FIELD) own_info = rule.get(MODEL_INFO_FIELD) parent_info = base_by_name.get(parent_name) if isinstance(parent_name, str) else None if parent_info is None or not isinstance(own_info, dict): return rule return {**rule, MODEL_INFO_FIELD: {**parent_info, **own_info}} - return [resolved(rule) if isinstance(rule, dict) else rule for rule in rules] + return [resolved(rule) for rule in rules] + + +@dataclass(frozen=True, slots=True) +class _RoutingRule: + pattern: re.Pattern + provider: str + + +@dataclass(frozen=True, slots=True) +class _CapabilityRule: + pattern: re.Pattern + model_info: dict + + +_CompiledRule = Union[_RoutingRule, _CapabilityRule] + + +def _compile_rule(rule: object) -> tuple[_CompiledRule, ...]: + if not isinstance(rule, dict): + return () + pattern = rule.get(PATTERN_FIELD) + model_info = rule.get(MODEL_INFO_FIELD) + if not isinstance(pattern, str) or not isinstance(model_info, dict): + verbose_logger.warning( + "LiteLLM: skipping malformed fallback generalization rule %s (needs string '%s' and dict '%s').", + rule.get(NAME_FIELD, pattern), + PATTERN_FIELD, + MODEL_INFO_FIELD, + ) + return () + try: + compiled = re.compile(pattern, re.IGNORECASE) + except re.error as e: + verbose_logger.warning( + "LiteLLM: skipping fallback generalization rule with invalid regex %r: %s", + pattern, + e, + ) + return () + if PROVIDER_KEY not in model_info: + return (_CapabilityRule(pattern=compiled, model_info=model_info),) + provider = model_info[PROVIDER_KEY] + if not isinstance(provider, str): + verbose_logger.warning( + "LiteLLM: skipping invalid fallback generalization rule %s: '%s' in '%s' must be a string.", + rule.get(NAME_FIELD, pattern), + PROVIDER_KEY, + MODEL_INFO_FIELD, + ) + return () + if len(model_info) == 1: + return (_RoutingRule(pattern=compiled, provider=provider),) + return ( + _RoutingRule(pattern=compiled, provider=provider), + _CapabilityRule(pattern=compiled, model_info=model_info), + ) class _FallbackGeneralizations: - """Holds the active rule list and its lazily-compiled regex cache.""" + """Holds the raw rule list and its install-time-compiled routing and capability rules.""" def __init__(self) -> None: - self.rules: list[dict] = [] - self._compiled: Optional[list[tuple[re.Pattern, dict]]] = None + self.rules: list = [] + self.routing_rules: tuple = () + self.capability_rules: tuple = () - def set_rules(self, rules: Optional[list[dict]]) -> None: - self.rules = rules if isinstance(rules, list) else [] - self._compiled = None + def set_rules(self, rules: Optional[list]) -> None: + installed = rules if isinstance(rules, list) else [] + compiled = tuple(kind for rule in _resolve_legacy_extends(installed) for kind in _compile_rule(rule)) + self.rules = installed + self.routing_rules = tuple(rule for rule in compiled if isinstance(rule, _RoutingRule)) + self.capability_rules = tuple(rule for rule in compiled if isinstance(rule, _CapabilityRule)) - def _compile(self) -> list[tuple[re.Pattern, dict]]: - compiled: list[tuple[re.Pattern, dict]] = [] - for rule in self.rules: - if not isinstance(rule, dict): - continue - pattern = rule.get(PATTERN_FIELD) - model_info = rule.get(MODEL_INFO_FIELD) - if not isinstance(pattern, str) or not isinstance(model_info, dict): - verbose_logger.warning( - "LiteLLM: skipping malformed fallback generalization rule %s (needs string '%s' and dict '%s').", - rule.get("name", pattern), - PATTERN_FIELD, - MODEL_INFO_FIELD, - ) - continue - try: - compiled.append((re.compile(pattern, re.IGNORECASE), model_info)) - except re.error as e: - verbose_logger.warning( - "LiteLLM: skipping fallback generalization rule with invalid regex %r: %s", - pattern, - e, - ) - return compiled - - def match(self, model: str) -> Optional[dict]: + def match_routing(self, model: str) -> Optional[str]: if not model: return None - if self._compiled is None: - self._compiled = self._compile() - for pattern, model_info in self._compiled: - if pattern.search(model) is not None: - return dict(model_info) - return None + return next( + (rule.provider for rule in self.routing_rules if rule.pattern.search(model) is not None), + None, + ) + + def match_capabilities(self, model: str) -> Optional[dict]: + if not model: + return None + matched = tuple(rule.model_info for rule in self.capability_rules if rule.pattern.search(model) is not None) + if not matched: + return None + return {key: value for model_info in matched for key, value in model_info.items()} _registry = _FallbackGeneralizations() -def set_fallback_generalizations(rules: Optional[list[dict]]) -> None: - """Install the active rule list and invalidate the compiled-regex cache. +def set_fallback_generalizations(rules: Optional[list]) -> None: + """Install the active rule list, compiling and classifying each rule. - ``extends`` inheritance is resolved here, once, before the rules are stored. - Called once when the model cost map is loaded (and again on any reload). + Legacy ``extends`` inheritance is resolved here, once, before classification; + a legacy rule mixing ``litellm_provider`` with capability keys installs as both + kinds. Malformed and invalid-regex rules are warned about and skipped. Called + once when the model cost map is loaded (and again on any reload). """ - _registry.set_rules(_resolve_extends(rules) if isinstance(rules, list) else rules) + _registry.set_rules(rules) -def get_fallback_generalization_rules() -> list[dict]: +def get_fallback_generalization_rules() -> list: """Return the raw rule list (read-only view for callers/tests).""" return _registry.rules -def match_fallback_generalization(model: str) -> Optional[dict]: - """Return the ``model_info`` of the first rule whose regex matches ``model``. +def match_routing_generalization(model: str) -> Optional[str]: + """Return the provider of the first routing rule whose regex matches ``model``. O(number of rules). Only call this once exact lookups have missed. """ - return _registry.match(model) + return _registry.match_routing(model) + + +def match_capability_generalizations(model: str) -> Optional[dict]: + """Return the union of the ``model_info`` of every capability rule matching ``model``. + + Later rules override earlier ones on key conflicts. Returns ``None`` when no + capability rule matches. O(number of rules); only call once exact lookups have missed. + """ + return _registry.match_capabilities(model) diff --git a/litellm/litellm_core_utils/get_litellm_params.py b/litellm/litellm_core_utils/get_litellm_params.py index fbed9594a0b..352e55e9c23 100644 --- a/litellm/litellm_core_utils/get_litellm_params.py +++ b/litellm/litellm_core_utils/get_litellm_params.py @@ -36,6 +36,8 @@ OPTIONAL_KWARGS_KEYS = frozenset( "aws_bedrock_project_id", "tpm", "rpm", + "itpm", + "otpm", "use_xai_oauth", } ) @@ -74,6 +76,7 @@ def get_litellm_params( proxy_server_request=None, acompletion=None, aembedding=None, + allm_passthrough_route=None, preset_cache_key=None, no_log=None, input_cost_per_second=None, @@ -116,6 +119,7 @@ def get_litellm_params( # Build base dict with explicit parameters (always included) litellm_params = { "acompletion": acompletion, + "allm_passthrough_route": allm_passthrough_route, "api_key": api_key, "force_timeout": force_timeout, "logger_fn": logger_fn, diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py index 122d09c855b..487a7b7e25f 100644 --- a/litellm/litellm_core_utils/get_llm_provider_logic.py +++ b/litellm/litellm_core_utils/get_llm_provider_logic.py @@ -4,7 +4,7 @@ from urllib.parse import urlparse import litellm from litellm.constants import REPLICATE_MODEL_NAME_WITH_ID_LENGTH from litellm.litellm_core_utils.fallback_generalizations import ( - match_fallback_generalization, + match_routing_generalization, ) from litellm.llms.openai_like.json_loader import JSONProviderRegistry from litellm.secret_managers.main import get_secret, get_secret_str @@ -346,6 +346,9 @@ def get_llm_provider( elif endpoint == "https://pinstripes.io/v1": custom_llm_provider = "pinstripes" dynamic_api_key = get_secret_str("PINSTRIPES_API_KEY") + elif endpoint == "https://api.meta.ai/v1": + custom_llm_provider = "meta" + dynamic_api_key = get_secret_str("META_API_KEY") if api_base is not None and not isinstance(api_base, str): raise Exception("api base needs to be a string. api_base={}".format(api_base)) @@ -446,6 +449,8 @@ def get_llm_provider( # bytez models elif model.startswith("bytez/"): custom_llm_provider = "bytez" + elif model.startswith("gdc/"): + custom_llm_provider = "gdc" elif model.startswith("lemonade/"): custom_llm_provider = "lemonade" elif model.startswith("heroku/"): @@ -469,12 +474,10 @@ def get_llm_provider( custom_llm_provider = "sap" # Last resort for an otherwise-unknown model: a declarative - # fallback-generalization rule (e.g. routes future claude-* to anthropic). + # fallback-generalization routing rule (e.g. routes future claude-* to anthropic). # Exact provider matches above always win; this only runs on a miss. if not custom_llm_provider: - generalization = match_fallback_generalization(model) - if generalization is not None: - custom_llm_provider = generalization.get("litellm_provider") or None + custom_llm_provider = match_routing_generalization(model) if not custom_llm_provider: if litellm.suppress_debug_info is False: @@ -650,6 +653,10 @@ def _get_openai_compatible_provider_info( api_base = api_base or get_secret("DEEPSEEK_API_BASE") or "https://api.deepseek.com/beta" # type: ignore dynamic_api_key = api_key or get_secret_str("DEEPSEEK_API_KEY") + elif custom_llm_provider == "tencent": + api_base = api_base or get_secret("TENCENT_API_BASE") or "https://tokenhub-intl.tencentcloudmaas.com/v1" + + dynamic_api_key = api_key or get_secret_str("TENCENT_API_KEY") elif custom_llm_provider == "fireworks_ai": # fireworks is openai compatible, we just need to set this to custom_openai and have the api_base be https://api.fireworks.ai/inference/v1 ( diff --git a/litellm/litellm_core_utils/get_supported_openai_params.py b/litellm/litellm_core_utils/get_supported_openai_params.py index c4ddb4b7ee0..19149da0316 100644 --- a/litellm/litellm_core_utils/get_supported_openai_params.py +++ b/litellm/litellm_core_utils/get_supported_openai_params.py @@ -106,6 +106,8 @@ def get_supported_openai_params( return litellm.VLLMConfig().get_supported_openai_params(model=model) elif custom_llm_provider == "deepseek": return litellm.DeepSeekChatConfig().get_supported_openai_params(model=model) + elif custom_llm_provider == "tencent": + return litellm.TencentChatConfig().get_supported_openai_params(model=model) elif custom_llm_provider == "cohere_chat" or custom_llm_provider == "cohere": return litellm.CohereChatConfig().get_supported_openai_params(model=model) elif custom_llm_provider == "maritalk": diff --git a/litellm/litellm_core_utils/health_check_helpers.py b/litellm/litellm_core_utils/health_check_helpers.py index 405366382a1..9fc036e2a99 100644 --- a/litellm/litellm_core_utils/health_check_helpers.py +++ b/litellm/litellm_core_utils/health_check_helpers.py @@ -95,6 +95,17 @@ class HealthCheckHelpers: """ import litellm + logging_obj = filtered_model_params.get("litellm_logging_obj") + if logging_obj is not None: + api_base = filtered_model_params.get("api_base") + logging_obj.update_from_kwargs( + kwargs=filtered_model_params, + model=filtered_model_params.get("model"), + user=None, + optional_params={}, + litellm_params={"api_base": api_base} if api_base else None, + ) + if custom_llm_provider in LIST_BATCHES_SUPPORTED_PROVIDERS: return await litellm.alist_batches(**filtered_model_params) else: @@ -188,6 +199,7 @@ class HealthCheckHelpers: api_base=model_params.get("api_base", None), api_key=model_params.get("api_key", None), api_version=model_params.get("api_version", None), + model_params=model_params, ), "batch": lambda: HealthCheckHelpers._batch_health_check( custom_llm_provider=custom_llm_provider, diff --git a/litellm/litellm_core_utils/initialize_dynamic_callback_params.py b/litellm/litellm_core_utils/initialize_dynamic_callback_params.py index d0a6ec30c9e..06a9e98c5ac 100644 --- a/litellm/litellm_core_utils/initialize_dynamic_callback_params.py +++ b/litellm/litellm_core_utils/initialize_dynamic_callback_params.py @@ -1,7 +1,23 @@ -from typing import Dict, Optional +from typing import Any, Dict, Iterator, Optional from litellm.types.utils import StandardCallbackDynamicParams +_CLIENT_CALLBACK_METADATA_SLOTS: tuple[str, ...] = ("litellm_metadata", "metadata") + + +def iter_client_callback_metadata_dicts( + kwargs: dict[str, Any], +) -> Iterator[tuple[str, dict[str, Any]]]: + litellm_params = kwargs.get("litellm_params") + if isinstance(litellm_params, dict): + nested = litellm_params.get("metadata") + if isinstance(nested, dict): + yield "litellm_params.metadata", nested + for key in _CLIENT_CALLBACK_METADATA_SLOTS: + candidate = kwargs.get(key) + if isinstance(candidate, dict): + yield key, candidate + def _is_env_reference(value: object) -> bool: return isinstance(value, str) and "os.environ/" in value @@ -55,6 +71,7 @@ _supported_callback_params = [ "dd_site", "dd_agent_host", "dd_agent_port", + "turn_off_message_logging", ] _request_blocked_callback_params = { @@ -87,19 +104,13 @@ def initialize_standard_callback_dynamic_params( validate_no_callback_env_reference(param, _param_value, source="request body") standard_callback_dynamic_params[param] = _param_value # type: ignore - # 2. Fallback: check "metadata" or "litellm_params" -> "metadata" - metadata = (kwargs.get("metadata") or {}).copy() - litellm_params = kwargs.get("litellm_params") or {} - if isinstance(litellm_params, dict): - metadata.update(litellm_params.get("metadata") or {}) - - if isinstance(metadata, dict): + for slot_label, metadata in iter_client_callback_metadata_dicts(kwargs): for param in _supported_callback_params: if param in _request_blocked_callback_params: continue if param not in standard_callback_dynamic_params and param in metadata: _param_value = metadata.get(param) - validate_no_callback_env_reference(param, _param_value, source="metadata") + validate_no_callback_env_reference(param, _param_value, source=slot_label) standard_callback_dynamic_params[param] = _param_value # type: ignore return standard_callback_dynamic_params diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index da204855465..936d79b22d6 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -1530,6 +1530,7 @@ class Logging(LiteLLMLoggingBaseClass): and litellm_params.get(CallTypes.aembedding.value, False) is not True and litellm_params.get(CallTypes.aimage_generation.value, False) is not True and litellm_params.get(CallTypes.atranscription.value, False) is not True + and litellm_params.get(CallTypes.allm_passthrough_route.value, False) is not True ) def _is_assembled_stream_success(self, result=None) -> bool: @@ -4722,7 +4723,7 @@ class StandardLoggingPayloadSetup: api_base: Optional[str] = None, ) -> StandardLoggingModelInformation: model_cost_name = _select_model_name_for_cost_calc( - model=None, + model=base_model if custom_pricing else None, completion_response=init_response_obj, # type: ignore base_model=base_model, custom_pricing=custom_pricing, @@ -5268,6 +5269,11 @@ def get_standard_logging_object_payload( ## Get model cost information ## base_model = _get_base_model_from_metadata(model_call_details=kwargs) + # The router overrides completion_response.model to the model-group alias before + # this payload is built, so cost-map lookup via that alias always misses. + # Fall back to the actual deployment model set by the router in metadata. + if base_model is None: + base_model = metadata.get("deployment") custom_pricing = use_custom_pricing_for_model(litellm_params=litellm_params) raw_response_cost = kwargs.get("response_cost") response_cost: float = raw_response_cost or 0.0 @@ -5389,7 +5395,7 @@ def get_standard_logging_object_payload( def emit_standard_logging_payload(payload: StandardLoggingPayload): if os.getenv("LITELLM_PRINT_STANDARD_LOGGING_PAYLOAD"): - print(json.dumps(payload, indent=4)) # noqa: T201 + print(json.dumps(payload, indent=4), flush=True) # noqa: T201 def get_standard_logging_metadata( diff --git a/litellm/litellm_core_utils/llm_cost_calc/tiered_pricing.py b/litellm/litellm_core_utils/llm_cost_calc/tiered_pricing.py new file mode 100644 index 00000000000..836b02f2049 --- /dev/null +++ b/litellm/litellm_core_utils/llm_cost_calc/tiered_pricing.py @@ -0,0 +1,139 @@ +""" +Provider-neutral graduated tiered pricing calculation. + +Shared by provider cost calculators (e.g. Dashscope) and the proxy budget +reservation logic so neither has to depend on the other. +""" + +from typing import List, Optional, Union + + +def _coerce_cost_per_token(value: Union[float, int, str, None]) -> float: + """ + Coerce a per-token cost into a float. + + Model cost values loaded from YAML config may arrive as strings (e.g. + scientific notation like "4e-07"), which would break arithmetic. + """ + if value is None: + return 0.0 + if isinstance(value, str): + try: + return float(value) + except ValueError: + return 0.0 + return float(value) + + +def calculate_tiered_cost( + tokens: int, + tiered_pricing: List[dict], + cost_key: str, + fallback_cost_key: Optional[str] = None, +) -> float: + """ + Calculate cost for a given number of tokens based on a true tiered pricing structure. + + This function iterates through sorted pricing tiers, calculates the cost for the + number of tokens that fall into each tier's range, and sums them up to get the total cost. + + Args: + tokens (int): The total number of tokens to calculate the cost for. + tiered_pricing (List[dict]): A list of dictionaries, where each dictionary + represents a pricing tier. + cost_key (str): The key in the tier dictionary that holds the per-token cost + (e.g., 'input_cost_per_token'). + fallback_cost_key (Optional[str], optional): A fallback key to use if the + primary `cost_key` is not found in a tier. Defaults to None. + + Returns: + float: The total calculated cost for the given tokens. + + Example: + >>> tiered_pricing = [ + ... {"range": [0, 100000], "input_cost_per_token": 0.0001}, + ... {"range": [100000, 500000], "input_cost_per_token": 0.00005}, + ... ] + + Calculating cost for 150,000 tokens: + (100,000 * 0.0001) + (50,000 * 0.00005) = $12.5 + """ + if not tiered_pricing or tokens <= 0: + return 0.0 + + total_cost = 0.0 + tokens_processed = 0 + + sorted_tiers = sorted(tiered_pricing, key=lambda x: x.get("range", [0, 0])[0]) + + for tier in sorted_tiers: + if tokens_processed >= tokens: + break + + tier_range = tier.get("range", []) + if len(tier_range) != 2: + continue + + range_start, range_end = tier_range + + if tokens <= range_start: + continue + + tier_start = max(range_start, tokens_processed) + tier_end = min(range_end, tokens) + + if tier_end > tier_start: + tokens_in_tier = tier_end - tier_start + cost_per_token = tier.get(cost_key) or tier.get(fallback_cost_key, 0) + total_cost += tokens_in_tier * _coerce_cost_per_token(cost_per_token) + tokens_processed = tier_end + + # After loop, check if any tokens remain (i.e., tokens > highest tier's end range) + # and charge them at the last tier's rate. + if tokens_processed < tokens and sorted_tiers: + last_tier = sorted_tiers[-1] + remaining_tokens = tokens - tokens_processed + cost_per_token = last_tier.get(cost_key) or last_tier.get(fallback_cost_key, 0) + total_cost += remaining_tokens * _coerce_cost_per_token(cost_per_token) + + return total_cost + + +def select_tier_for_input( + tiered_pricing: List[dict], + input_tokens: int, +) -> Optional[dict]: + """ + Select the pricing tier for a request based on its total input token count. + + Alibaba Model Studio (Dashscope) tiered pricing is all-or-nothing: the tier is + chosen by the total input tokens of a single request and every token in the + request (input and output) is billed at that one tier's rate, rather than + graduated income-tax-style slicing. A tier matches when + ``range_start < input_tokens <= range_end`` (so a request of exactly + ``range_end`` tokens stays in the lower tier, matching the official + ``0 < Token <= 32K`` phrasing). Requests above the highest declared range fall + back to the last (most expensive) tier. + """ + if not tiered_pricing or input_tokens <= 0: + return None + + sorted_tiers = sorted(tiered_pricing, key=lambda t: t.get("range", [0, 0])[0]) + valid_tiers = [tier for tier in sorted_tiers if len(tier.get("range", [])) == 2] + if not valid_tiers: + return None + + matching = [tier for tier in valid_tiers if tier["range"][0] < input_tokens <= tier["range"][1]] + if matching: + return matching[0] + return valid_tiers[-1] + + +def tier_rate( + tier: dict, + cost_key: str, + fallback_cost_key: Optional[str] = None, +) -> float: + """Read a per-token rate from a tier, coercing YAML string costs to float.""" + raw = tier.get(cost_key) or tier.get(fallback_cost_key, 0) + return _coerce_cost_per_token(raw) diff --git a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py index 58107d9804b..47daf33824e 100644 --- a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py +++ b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py @@ -656,7 +656,7 @@ def convert_to_model_response_object( message: Optional[Message] = None finish_reason: Optional[str] = None - if _should_convert_tool_call_to_json_mode( + if tool_calls is not None and _should_convert_tool_call_to_json_mode( tool_calls=tool_calls, convert_tool_call_to_json_mode=convert_tool_call_to_json_mode, ): diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 1f0df51d7de..f7ff4d6b16f 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -6,7 +6,7 @@ import mimetypes import re import xml.etree.ElementTree as ET from enum import Enum -from typing import Any, Dict, List, Optional, Set, Tuple, Union, cast, overload +from typing import Any, Dict, List, Optional, Set, Tuple, TypedDict, Union, cast, overload from jinja2.sandbox import ImmutableSandboxedEnvironment @@ -3626,6 +3626,7 @@ class BedrockImageProcessor: def _convert_to_bedrock_tool_call_invoke( tool_calls: list, + model: Optional[str] = None, ) -> List[BedrockContentBlock]: """ OpenAI tool invokes: @@ -3701,7 +3702,13 @@ def _convert_to_bedrock_tool_call_invoke( # cache_control applies to the whole original # tool call; attach after the last split block. if tool.get("cache_control", None) is not None: - _parts_list.append(BedrockContentBlock(cachePoint=CachePointBlock(type="default"))) + _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( + {"cache_control": tool["cache_control"]}, + block_type="content_block", + model=model, + ) + if _cache_point_block is not None: + _parts_list.append(_cache_point_block) continue # Fallback: no objects extracted — use empty dict. arguments_dict = {} @@ -3712,8 +3719,13 @@ def _convert_to_bedrock_tool_call_invoke( # Check for cache_control and add a separate cachePoint block if tool.get("cache_control", None) is not None: - cache_point_block = BedrockContentBlock(cachePoint=CachePointBlock(type="default")) - _parts_list.append(cache_point_block) + cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( + {"cache_control": tool["cache_control"]}, + block_type="content_block", + model=model, + ) + if cache_point_block is not None: + _parts_list.append(cache_point_block) return _parts_list except Exception as e: raise Exception( @@ -4377,6 +4389,7 @@ class BedrockConverseMessagesProcessor: _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( message_block=cast(OpenAIMessageContentListBlock, element), block_type="content_block", + model=model, ) if _cache_point_block is not None: _parts.append(_cache_point_block) @@ -4384,7 +4397,7 @@ class BedrockConverseMessagesProcessor: elif message_block["content"] and isinstance(message_block["content"], str): _part = BedrockContentBlock(text=messages[msg_i]["content"]) _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( - message_block, block_type="content_block" + message_block, block_type="content_block", model=model ) user_content.append(_part) if _cache_point_block is not None: @@ -4416,22 +4429,27 @@ class BedrockConverseMessagesProcessor: tool_content.append(tool_call_result) # Check if we need to add a separate cachePoint block - has_cache_control = False + tool_msg_cache_control = None # Check for message-level cache_control if current_message.get("cache_control", None) is not None: - has_cache_control = True + tool_msg_cache_control = current_message["cache_control"] # Check for content-level cache_control in list content elif isinstance(current_message.get("content"), list): for content_element in current_message["content"]: if isinstance(content_element, dict) and content_element.get("cache_control", None) is not None: - has_cache_control = True + tool_msg_cache_control = content_element["cache_control"] break # Add a separate cachePoint block if cache_control is present - if has_cache_control: - cache_point_block = BedrockContentBlock(cachePoint=CachePointBlock(type="default")) - tool_content.append(cache_point_block) + if tool_msg_cache_control is not None: + cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( + {"cache_control": tool_msg_cache_control}, + block_type="content_block", + model=model, + ) + if cache_point_block is not None: + tool_content.append(cache_point_block) msg_i += 1 # Deduplicate toolResult blocks with the same toolUseId @@ -4509,6 +4527,7 @@ class BedrockConverseMessagesProcessor: _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( message_block=cast(OpenAIMessageContentListBlock, element), block_type="content_block", + model=model, ) if _cache_point_block is not None: assistants_parts.append(_cache_point_block) @@ -4520,14 +4539,14 @@ class BedrockConverseMessagesProcessor: # If content is empty/whitespace, skip it (don't add a placeholder) # Add cache point block for assistant string content _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( - assistant_message_block, block_type="content_block" + assistant_message_block, block_type="content_block", model=model ) if _cache_point_block is not None: assistant_content.append(_cache_point_block) _tool_calls = assistant_message_block.get("tool_calls", []) if _tool_calls: - assistant_content.extend(_convert_to_bedrock_tool_call_invoke(_tool_calls)) + assistant_content.extend(_convert_to_bedrock_tool_call_invoke(_tool_calls, model=model)) msg_i += 1 @@ -4745,6 +4764,7 @@ def _bedrock_converse_messages_pt( _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( message_block=cast(OpenAIMessageContentListBlock, element), block_type="content_block", + model=model, ) if _cache_point_block is not None: _parts.append(_cache_point_block) @@ -4752,7 +4772,7 @@ def _bedrock_converse_messages_pt( elif message_block["content"] and isinstance(message_block["content"], str): _part = BedrockContentBlock(text=messages[msg_i]["content"]) _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( - message_block, block_type="content_block" + message_block, block_type="content_block", model=model ) user_content.append(_part) if _cache_point_block is not None: @@ -4786,22 +4806,27 @@ def _bedrock_converse_messages_pt( tool_content.append(tool_call_result) # Check if we need to add a separate cachePoint block - has_cache_control = False + tool_msg_cache_control = None # Check for message-level cache_control if current_message.get("cache_control", None) is not None: - has_cache_control = True + tool_msg_cache_control = current_message["cache_control"] # Check for content-level cache_control in list content elif isinstance(current_message.get("content"), list): for content_element in current_message["content"]: if isinstance(content_element, dict) and content_element.get("cache_control", None) is not None: - has_cache_control = True + tool_msg_cache_control = content_element["cache_control"] break # Add a separate cachePoint block if cache_control is present - if has_cache_control: - cache_point_block = BedrockContentBlock(cachePoint=CachePointBlock(type="default")) - tool_content.append(cache_point_block) + if tool_msg_cache_control is not None: + cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( + {"cache_control": tool_msg_cache_control}, + block_type="content_block", + model=model, + ) + if cache_point_block is not None: + tool_content.append(cache_point_block) msg_i += 1 # Deduplicate toolResult blocks with the same toolUseId @@ -4882,6 +4907,7 @@ def _bedrock_converse_messages_pt( _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( message_block=cast(OpenAIMessageContentListBlock, element), block_type="content_block", + model=model, ) if _cache_point_block is not None: assistants_parts.append(_cache_point_block) @@ -4892,13 +4918,13 @@ def _bedrock_converse_messages_pt( assistant_content.append(BedrockContentBlock(text=_assistant_content)) # Add cache point block for assistant string content _cache_point_block = litellm.AmazonConverseConfig()._get_cache_point_block( - assistant_message_block, block_type="content_block" + assistant_message_block, block_type="content_block", model=model ) if _cache_point_block is not None: assistant_content.append(_cache_point_block) _tool_calls = assistant_message_block.get("tool_calls", []) if _tool_calls: - assistant_content.extend(_convert_to_bedrock_tool_call_invoke(_tool_calls)) + assistant_content.extend(_convert_to_bedrock_tool_call_invoke(_tool_calls, model=model)) msg_i += 1 @@ -5035,15 +5061,18 @@ def _bedrock_tools_pt(tools: List, model: Optional[str] = None) -> List[BedrockT ] """ from litellm.llms.bedrock.common_utils import ( - get_bedrock_base_model, + bedrock_converse_supports_strict_tools, normalize_json_schema_custom_types_to_object, ) from litellm.litellm_core_utils.prompt_templates.common_utils import unpack_defs _valid_json_schema_root_types = frozenset(("array", "boolean", "integer", "null", "number", "object", "string")) # Only Claude on Bedrock honours strict tool schemas; other families - # (Nova, Llama, GPT-OSS) reject the strict field outright. - supports_strict_tools = bool(model and get_bedrock_base_model(model).startswith("anthropic")) + # (Nova, Llama, GPT-OSS) reject the strict field outright. Opus 4.7/4.8 + # also reject `strict` on Bedrock Converse (see #31582) — their validator + # maps toolSpec to the native Anthropic tool shape, which has no strict + # field, even though Anthropic's native API accepts it as a top-level key. + supports_strict_tools = bool(model and bedrock_converse_supports_strict_tools(model)) tool_block_list: List[BedrockToolBlock] = [] for tool_idx, tool in enumerate(tools): # Check if tool is already a BedrockToolBlock (e.g., systemTool for Nova grounding) @@ -5322,3 +5351,199 @@ def get_attribute_or_key(tool_or_function, attribute, default=None): if hasattr(tool_or_function, attribute): return getattr(tool_or_function, attribute) return tool_or_function.get(attribute, default) + + +class NormalizedToolCall(TypedDict): + id: Optional[str] + name: Optional[str] + arguments: dict[str, Any] + + +def _parse_tool_call_arguments(raw: Any, tool_name: Optional[str], context: str) -> dict[str, Any]: + # Anthropic's tool_use blocks already carry a parsed dict in "input"; + # chat completions and the Responses API carry a JSON string that may be + # truncated by the model, so route those through the repair-aware parser. + if isinstance(raw, dict): + return raw + if not isinstance(raw, str): + return {} + from litellm.litellm_core_utils.prompt_templates.common_utils import ( + parse_tool_call_arguments, + ) + + try: + parsed = parse_tool_call_arguments(raw, tool_name=tool_name, context=context) + except ValueError as e: + verbose_logger.warning("Failed to parse tool call arguments: %s", e) + return {} + return parsed if isinstance(parsed, dict) else {} + + +def _tool_calls_from_chat_completion_response(response: Any) -> list[NormalizedToolCall]: + choices = get_attribute_or_key(response, "choices", None) + if not (isinstance(choices, list) and choices): + return [] + message = get_attribute_or_key(choices[0], "message", None) + tool_calls = get_attribute_or_key(message, "tool_calls", None) if message else None + if not isinstance(tool_calls, list): + return [] + result: list[NormalizedToolCall] = [] + for tc in tool_calls: + fn = get_attribute_or_key(tc, "function", None) + if fn is None: + continue + name = get_attribute_or_key(fn, "name") + result.append( + NormalizedToolCall( + id=get_attribute_or_key(tc, "id"), + name=name, + arguments=_parse_tool_call_arguments( + get_attribute_or_key(fn, "arguments", "{}"), + tool_name=name, + context="chat completions", + ), + ) + ) + return result + + +def _tool_calls_from_responses_api_response(response: Any) -> list[NormalizedToolCall]: + output = get_attribute_or_key(response, "output", None) + if not isinstance(output, list): + return [] + result: list[NormalizedToolCall] = [] + for item in output: + if get_attribute_or_key(item, "type") != "function_call": + continue + name = get_attribute_or_key(item, "name") + result.append( + NormalizedToolCall( + id=get_attribute_or_key(item, "call_id") or get_attribute_or_key(item, "id"), + name=name, + arguments=_parse_tool_call_arguments( + get_attribute_or_key(item, "arguments", "{}"), + tool_name=name, + context="responses API", + ), + ) + ) + return result + + +def _tool_calls_from_anthropic_messages_response(response: Any) -> list[NormalizedToolCall]: + content = get_attribute_or_key(response, "content", None) + if not isinstance(content, list): + return [] + result: list[NormalizedToolCall] = [] + for block in content: + if get_attribute_or_key(block, "type") != "tool_use": + continue + raw_input = get_attribute_or_key(block, "input", {}) + result.append( + NormalizedToolCall( + id=get_attribute_or_key(block, "id"), + name=get_attribute_or_key(block, "name"), + arguments=raw_input if isinstance(raw_input, dict) else {}, + ) + ) + return result + + +def get_tool_calls_from_response(response: Any) -> list[NormalizedToolCall]: + """ + Extract tool/function calls from a response object into a normalized + ``{"id", "name", "arguments"}`` shape, regardless of which API surface + produced it: chat completions (``choices[].message.tool_calls``), + the Responses API (``output`` items of type ``function_call``), or the + Anthropic Messages API (``content`` blocks of type ``tool_use``). + + Callers that only care about a specific tool should filter the result by + ``name`` themselves -- this returns every tool call found. + """ + for extractor in ( + _tool_calls_from_chat_completion_response, + _tool_calls_from_responses_api_response, + _tool_calls_from_anthropic_messages_response, + ): + tool_calls = extractor(response) + if tool_calls: + return tool_calls + return [] + + +def has_tool_with_name(tools: Any, tool_name: str) -> bool: + """ + Check whether a tools list (as sent to an LLM) includes a tool with the + given name, regardless of shape: OpenAI-style function tools + (``{"type": "function", "function": {"name": ...}}``) or Anthropic's + native tool shape (a top-level ``"name"``, e.g. + ``{"name": ..., "input_schema": ...}``). Anthropic's documented client + tool format doesn't require a ``"type"`` key at all -- ``"custom"`` is + only one of several possible values -- so any non-OpenAI-shaped tool is + matched on its top-level ``"name"``. + """ + if not isinstance(tools, list): + return False + for tool in tools: + if not isinstance(tool, dict): + continue + function = tool.get("function") + if tool.get("type") == "function" and isinstance(function, dict): + if function.get("name") == tool_name: + return True + elif tool.get("name") == tool_name: + return True + return False + + +def resolve_structured_messages( + messages: list[dict[str, Any]] | None, + request_kwargs: dict[str, Any], +) -> list[dict[str, Any]] | None: + """ + Normalize a request's messages to OpenAI-spec chat-completions shape, + regardless of which API surface produced them (chat completions, + Anthropic /v1/messages, Responses API ``input``, etc). + + Returns ``messages`` unchanged if already present. Otherwise dispatches + through the guardrail translation handlers (the same per-surface + conversion logic guardrails use) to convert e.g. Responses API ``input`` + into a message list. Returns ``None`` if no messages could be resolved. + """ + if messages: + return messages + + from litellm.litellm_core_utils.api_route_to_call_types import ( + get_call_types_for_route, + ) + from litellm.llms import load_guardrail_translation_mappings + from litellm.types.utils import CallTypes + + mappings = load_guardrail_translation_mappings() + call_type: CallTypes | None = None + + # 1. Try route-based inference from proxy metadata + route = request_kwargs.get("litellm_metadata", {}).get("user_api_key_request_route") + if route: + call_types_list = get_call_types_for_route(route) + if call_types_list: + for ct in call_types_list: + if ct in mappings: + call_type = ct + break + + # 2. Fallback: try each mapped handler until one produces messages + handlers_to_try: list[Any] = [] + if call_type is not None and call_type in mappings: + handlers_to_try.append(mappings[call_type]()) + else: + handlers_to_try.extend(handler_cls() for handler_cls in mappings.values()) + + for handler in handlers_to_try: + structured = handler.get_structured_messages(request_kwargs) + if structured: + return [ + msg if isinstance(msg, dict) else msg.model_dump() # type: ignore + for msg in structured + ] + return None diff --git a/litellm/litellm_core_utils/prompt_templates/image_handling.py b/litellm/litellm_core_utils/prompt_templates/image_handling.py index 7129d6bba81..92a4296c432 100644 --- a/litellm/litellm_core_utils/prompt_templates/image_handling.py +++ b/litellm/litellm_core_utils/prompt_templates/image_handling.py @@ -72,6 +72,9 @@ def _process_image_response(response: Response, url: str) -> str: async def async_convert_url_to_base64(url: str) -> str: + if url.startswith("data:") and ";base64," in url: + return url + # If MAX_IMAGE_URL_DOWNLOAD_SIZE_MB is 0, block all image downloads if MAX_IMAGE_URL_DOWNLOAD_SIZE_MB == 0: raise litellm.ImageFetchError( @@ -95,6 +98,9 @@ async def async_convert_url_to_base64(url: str) -> str: def convert_url_to_base64(url: str) -> str: + if url.startswith("data:") and ";base64," in url: + return url + # If MAX_IMAGE_URL_DOWNLOAD_SIZE_MB is 0, block all image downloads if MAX_IMAGE_URL_DOWNLOAD_SIZE_MB == 0: raise litellm.ImageFetchError( diff --git a/litellm/litellm_core_utils/realtime_streaming.py b/litellm/litellm_core_utils/realtime_streaming.py index bd6406c6241..220d1caa3d2 100644 --- a/litellm/litellm_core_utils/realtime_streaming.py +++ b/litellm/litellm_core_utils/realtime_streaming.py @@ -1,10 +1,10 @@ import asyncio -import concurrent.futures import json from typing import TYPE_CHECKING, Any, Dict, List, Optional, Protocol, Union, cast import litellm from litellm._logging import verbose_logger +from litellm.litellm_core_utils.logging_worker import GLOBAL_LOGGING_WORKER from litellm.llms.base_llm.realtime.transformation import BaseRealtimeConfig from litellm.types.llms.openai import ( OpenAIRealtimeEvents, @@ -24,9 +24,6 @@ if TYPE_CHECKING: else: CLIENT_CONNECTION_CLASS = Any -# Create a thread pool with a maximum of 10 threads -executor = concurrent.futures.ThreadPoolExecutor(max_workers=10) - class RealtimeEventNormalizer(Protocol): def should_drop(self, event: object) -> bool: ... @@ -314,11 +311,12 @@ class RealTimeStreaming: if self.session_tools or self.tool_calls: self.logging_obj.model_call_details["realtime_tools"] = self.session_tools self.logging_obj.model_call_details["realtime_tool_calls"] = self.tool_calls - ## ASYNC LOGGING - # Create an event loop for the new thread - asyncio.create_task(self.logging_obj.async_success_handler(self.messages)) - ## SYNC LOGGING - executor.submit(self.logging_obj.success_handler(self.messages)) + # Route through the bounded logging worker (per-coroutine timeout + + # concurrency cap) instead of a bare create_task, so a slow callback + # can't leave suspended tasks pinning each call's response in memory. + GLOBAL_LOGGING_WORKER.ensure_initialized_and_enqueue( + self.logging_obj.dispatch_success_handlers(self.messages, prefer_async_handlers=True) + ) async def _send_to_backend(self, message: str) -> bool: """Send a message to the backend WebSocket. diff --git a/litellm/litellm_core_utils/sensitive_data_masker.py b/litellm/litellm_core_utils/sensitive_data_masker.py index daca48120cd..7861e13bae5 100644 --- a/litellm/litellm_core_utils/sensitive_data_masker.py +++ b/litellm/litellm_core_utils/sensitive_data_masker.py @@ -1,6 +1,8 @@ from collections.abc import Mapping from typing import Any, Dict, List, Optional, Set +from pydantic import BaseModel + from litellm.constants import DEFAULT_MAX_RECURSE_DEPTH_SENSITIVE_DATA_MASKER @@ -131,8 +133,59 @@ class SensitiveDataMasker: return masked_data + def mask(self, data: object) -> object: + if isinstance(data, Mapping): + return self.mask_dict(dict(data)) + if isinstance(data, list): + return self._mask_sequence( + data, + 0, + DEFAULT_MAX_RECURSE_DEPTH_SENSITIVE_DATA_MASKER, + None, + False, + ) + return data + _default_masker = SensitiveDataMasker() +_error_masker = SensitiveDataMasker(visible_prefix=4, visible_suffix=0) + + +def mask_sensitive_structure(data: object) -> object: + return _error_masker.mask(data) + + +def mask_credentials_in_payload(data: object) -> object: + """Return a copy of ``data`` where string values under sensitive-named keys + are masked but every other value (``None``, ``int``, ``float``, ``bool``, + ``bytes``, ``datetime``, tuples, sets, typed objects) is preserved by + identity, and dicts/lists are rebuilt structurally. + + Use this for logging payloads that carry response data through to + SpendLogs / OTel / Langfuse, where :meth:`SensitiveDataMasker.mask`'s + config-dump semantics (``None`` -> ``"None"``, tuples stringified, + objects flattened via ``__dict__``) would silently distort the record. + + Sensitive-key detection is delegated to the shared + :class:`SensitiveDataMasker` so pattern updates stay in one place. + """ + return _walk_payload(data, key_is_sensitive=False, depth=0) + + +def _walk_payload(node: object, key_is_sensitive: bool, depth: int) -> object: + if depth >= DEFAULT_MAX_RECURSE_DEPTH_SENSITIVE_DATA_MASKER: + return node + if isinstance(node, Mapping): + return {k: _walk_payload(v, _default_masker.is_sensitive_key(k), depth + 1) for k, v in node.items()} + if isinstance(node, list): + return [_walk_payload(item, key_is_sensitive, depth + 1) for item in node] + if isinstance(node, tuple): + return tuple(_walk_payload(item, key_is_sensitive, depth + 1) for item in node) + if isinstance(node, BaseModel): + return _walk_payload(node.model_dump(), key_is_sensitive, depth) + if key_is_sensitive and isinstance(node, str) and node: + return _default_masker._mask_value(node) + return node def mask_sensitive_keys(data: Dict[str, Any], sensitive_fields: Set[str]) -> Dict[str, Any]: diff --git a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py index deeee3b7daf..38bc68f2f78 100644 --- a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py +++ b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py @@ -7,6 +7,7 @@ from litellm.types.llms.openai import ( ChatCompletionAudioDelta, ) from litellm.types.utils import ( + CacheCreationTokenDetails, ChatCompletionAudioResponse, ChatCompletionMessageToolCall, Choices, @@ -541,6 +542,12 @@ class ChunkProcessor: web_search_requests: Optional[int] = None completion_tokens_details: Optional[CompletionTokensDetails] = None prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = None + # Anthropic emits the cache-creation TTL breakdown (5m/1h split) only on + # the `message_start` event; the later `message_delta` carries the flat + # cache-creation count but drops the nested breakdown. prompt_tokens_details + # is last-wins, so without preserving this separately the 1h breakdown is + # lost and 1h cache writes get billed at the 5m rate. + cache_creation_token_details: Optional[CacheCreationTokenDetails] = None for chunk in chunks: usage_chunk: Optional[Usage] = None if "usage" in chunk: @@ -594,7 +601,18 @@ class ChunkProcessor: "web_search_requests", ) - prompt_tokens_details = usage_chunk_dict["prompt_tokens_details"] + prompt_tokens_details = cast( + Optional[PromptTokensDetailsWrapper], + usage_chunk_dict["prompt_tokens_details"], + ) + + cache_creation_token_details = self._capture_cache_creation_token_details( + prompt_tokens_details, cache_creation_token_details + ) + + prompt_tokens_details = self._attach_cache_creation_token_details( + prompt_tokens_details, cache_creation_token_details + ) completion_tokens = self._reset_anthropic_cursor_completion_tokens( chunks=chunks, @@ -613,6 +631,34 @@ class ChunkProcessor: prompt_tokens_details=prompt_tokens_details, ) + @staticmethod + def _capture_cache_creation_token_details( + prompt_tokens_details: Optional[PromptTokensDetailsWrapper], + current: Optional[CacheCreationTokenDetails], + ) -> Optional[CacheCreationTokenDetails]: + incoming = cast( + Optional[CacheCreationTokenDetails], + getattr(prompt_tokens_details, "cache_creation_token_details", None), + ) + if incoming is not None: + return incoming + return current + + @staticmethod + def _attach_cache_creation_token_details( + prompt_tokens_details: Optional[PromptTokensDetailsWrapper], + cache_creation_token_details: Optional[CacheCreationTokenDetails], + ) -> Optional[PromptTokensDetailsWrapper]: + if prompt_tokens_details is None or cache_creation_token_details is None: + return prompt_tokens_details + existing = cast( + Optional[CacheCreationTokenDetails], + getattr(prompt_tokens_details, "cache_creation_token_details", None), + ) + if existing is not None: + return prompt_tokens_details + return prompt_tokens_details.model_copy(update={"cache_creation_token_details": cache_creation_token_details}) + @staticmethod def _reset_anthropic_cursor_completion_tokens( chunks: list[dict[str, Any] | ModelResponse], diff --git a/litellm/litellm_core_utils/streaming_handler.py b/litellm/litellm_core_utils/streaming_handler.py index 587a3a58a94..128ba0bf3ab 100644 --- a/litellm/litellm_core_utils/streaming_handler.py +++ b/litellm/litellm_core_utils/streaming_handler.py @@ -1884,7 +1884,7 @@ class CustomStreamWrapper: await self.fetch_stream() if is_async_iterable(self.completion_stream): - async for chunk in self.completion_stream: # type: ignore[union-attr] + async for chunk in self.completion_stream: # pyright: ignore[reportOptionalIterable] # is_async_iterable guard proves __aiter__ if chunk == "None" or chunk is None: continue # skip None chunks diff --git a/litellm/litellm_core_utils/token_counter.py b/litellm/litellm_core_utils/token_counter.py index 56b9d42092c..071b16c8378 100644 --- a/litellm/litellm_core_utils/token_counter.py +++ b/litellm/litellm_core_utils/token_counter.py @@ -407,6 +407,37 @@ def token_counter( return num_tokens +def _count_function_call_tokens( + key: str, + value: Any, + message: Mapping[str, Any], + count_function: TokenCounterFunction, +) -> int: + """ + Count tokens contributed by an assistant message's tool/function call payload. + + Handles both the modern `tool_calls` list and the legacy OpenAI + `function_call` dict. Only the `arguments` string is counted (matching the + existing tool_calls behavior); names are accounted for elsewhere via the + tool/function definitions and `tool_choice`. + """ + if key == "tool_calls": + if not isinstance(value, List): + raise ValueError(f"Unsupported type {type(value)} for key tool_calls in message {message}") + total = 0 + for tool_call in value: + if "function" not in tool_call: + raise ValueError(f"Unsupported tool call {tool_call} must contain a function key") + function_arguments = tool_call["function"].get("arguments", "") + total += count_function(str(function_arguments)) + return total + if key == "function_call": + if not isinstance(value, Mapping): + raise ValueError(f"Unsupported type {type(value)} for key function_call in message {message}") + return count_function(str(value.get("arguments", ""))) + raise ValueError(f"Unexpected key {key!r}; expected 'tool_calls' or 'function_call'") + + def _count_messages( params: _MessageCountParams, messages: List[AllMessageValues], @@ -430,16 +461,8 @@ def _count_messages( for key, value in message.items(): if value is None: pass - elif key == "tool_calls": - if isinstance(value, List): - for tool_call in value: - if "function" in tool_call: - function_arguments = tool_call["function"].get("arguments", []) - num_tokens += params.count_function(str(function_arguments)) - else: - raise ValueError(f"Unsupported tool call {tool_call} must contain a function key") - else: - raise ValueError(f"Unsupported type {type(value)} for key tool_calls in message {message}") + elif key in ("tool_calls", "function_call"): + num_tokens += _count_function_call_tokens(key, value, message, params.count_function) elif isinstance(value, str): num_tokens += params.count_function(value) if key == "name": diff --git a/litellm/llms/a2a/chat/transformation.py b/litellm/llms/a2a/chat/transformation.py index c9623a817bf..113c000f352 100644 --- a/litellm/llms/a2a/chat/transformation.py +++ b/litellm/llms/a2a/chat/transformation.py @@ -10,7 +10,7 @@ import httpx from litellm.llms.base_llm.base_model_iterator import BaseModelResponseIterator from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException from litellm.types.llms.openai import AllMessageValues -from litellm.types.utils import Choices, Message, ModelResponse +from litellm.types.utils import Choices, Message, ModelResponse, Usage from ..common_utils import ( A2AError, @@ -312,6 +312,25 @@ class A2AConfig(BaseConfig): # Set ID from response model_response.id = response_json.get("id", str(uuid.uuid4())) + # A2A agents don't return token usage; estimate it so per-token pricing + # produces real cost and callers don't receive usage of 0/0/0. + try: + from litellm.utils import token_counter + + prompt_tokens = token_counter(model="gpt-3.5-turbo", messages=messages) + completion_tokens = token_counter(model="gpt-3.5-turbo", text=text, count_response_tokens=True) + setattr( + model_response, + "usage", + Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), + ) + except Exception: # noqa: BLE001 - best-effort estimate; a tokenizer hiccup must not break the response + pass + return model_response def get_model_response_iterator( diff --git a/litellm/llms/anthropic/chat/guardrail_translation/handler.py b/litellm/llms/anthropic/chat/guardrail_translation/handler.py index 4506c114208..7000c20d9c4 100644 --- a/litellm/llms/anthropic/chat/guardrail_translation/handler.py +++ b/litellm/llms/anthropic/chat/guardrail_translation/handler.py @@ -48,7 +48,10 @@ from litellm.types.utils import ( ) if TYPE_CHECKING: - from litellm.integrations.custom_guardrail import CustomGuardrail + from litellm.integrations.custom_guardrail import ( + CustomGuardrail, + ModifyResponseException, + ) from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.types.llms.anthropic_messages.anthropic_response import ( AnthropicMessagesResponse, @@ -70,6 +73,170 @@ class AnthropicMessagesHandler(BaseTranslation): super().__init__() self.adapter = LiteLLMAnthropicMessagesAdapter() + @staticmethod + def _build_streaming_usage_response( + responses_so_far: list[Any], + request_data: Optional[dict], + ) -> Optional[ModelResponse]: + chunks = tuple(response for response in responses_so_far if isinstance(response, (str, bytes))) + if not chunks: + return None + try: + return AnthropicPassthroughLoggingHandler._build_usage_only_response_from_chunks( + all_chunks=chunks, + model=str((request_data or {}).get("model") or ""), + ) + except (AttributeError, TypeError, ValueError): + return None + + def build_block_sse_chunks( + self, + exc: "ModifyResponseException", + stream_started: bool = False, + responses_so_far: Optional[list[Any]] = None, + ) -> list[bytes]: + """ + Build an Anthropic SSE sequence delivering the guardrail block message + and terminating the stream cleanly. + + - ``stream_started`` False (buffered / pre-stream): nothing has been + sent, so emit a complete standalone message (message_start -> + content_block_* -> message_delta -> message_stop) via + FakeAnthropicMessagesStreamIterator, the same converter the + /v1/messages pre-stream block handler uses. + - ``stream_started`` True (sampling / detect-only end-of-stream): real + chunks were already sent, so *continue* the in-progress message -- + close the open content block, append the block message as a new text + block, then end the message. Emitting a second ``message_start`` here + would make Anthropic clients reject the stream. + """ + if stream_started: + return self._block_continuation_chunks(exc, responses_so_far or []) + return self._standalone_block_chunks(exc) + + def _standalone_block_chunks(self, exc: "ModifyResponseException") -> list[bytes]: + import uuid + + from litellm.llms.anthropic.experimental_pass_through.messages.fake_stream_iterator import ( + FakeAnthropicMessagesStreamIterator, + ) + from litellm.llms.base_llm.guardrail_translation.utils import ( + blocked_response_usage, + ) + from litellm.types.utils import AnthropicMessagesResponse + + block_response = AnthropicMessagesResponse( + id=f"msg_{uuid.uuid4()}", + type="message", + role="assistant", + content=[{"type": "text", "text": exc.message}], + model=exc.model, + stop_reason="end_turn", + usage=blocked_response_usage(getattr(exc, "original_response", None)), + ) + return list(FakeAnthropicMessagesStreamIterator(response=block_response)) + + def _block_continuation_chunks(self, exc: "ModifyResponseException", responses_so_far: list[Any]) -> list[bytes]: + """Continue an already-started message: close the open content block, + append the block message as a new text block, then end the message -- + without a second message_start.""" + + from litellm.llms.base_llm.guardrail_translation.utils import ( + blocked_response_usage, + ) + + def _sse(event_type: str, payload: dict) -> bytes: + return f"event: {event_type}\ndata: {json.dumps(payload)}\n\n".encode() + + output_tokens = blocked_response_usage(getattr(exc, "original_response", None))["output_tokens"] + open_index, max_index = self._content_block_state(responses_so_far) + new_index = (max_index + 1) if max_index is not None else 0 + chunks: list[bytes] = [] + if open_index is not None: + chunks.append(_sse("content_block_stop", {"type": "content_block_stop", "index": open_index})) + chunks += [ + _sse( + "content_block_start", + { + "type": "content_block_start", + "index": new_index, + "content_block": {"type": "text", "text": ""}, + }, + ), + _sse( + "content_block_delta", + { + "type": "content_block_delta", + "index": new_index, + "delta": {"type": "text_delta", "text": exc.message}, + }, + ), + _sse("content_block_stop", {"type": "content_block_stop", "index": new_index}), + _sse( + "message_delta", + { + "type": "message_delta", + "delta": {"stop_reason": "end_turn", "stop_sequence": None}, + "usage": {"output_tokens": output_tokens}, + }, + ), + _sse("message_stop", {"type": "message_stop"}), + ] + return chunks + + @staticmethod + def _content_block_state( + responses_so_far: list[Any], + ) -> tuple[Optional[int], Optional[int]]: + """From the SSE chunks already sent to the client, return (open + content-block index or None, highest content-block index seen or None). + + A single streamed item may bundle multiple SSE events (raw bytes) or be + an already-parsed event dict, so every event across every item is + considered -- matching how ``get_streaming_string_so_far`` reads the + same stream.""" + open_indices: set[int] = set() + max_index: Optional[int] = None + for item in responses_so_far: + for data in AnthropicMessagesHandler._iter_sse_events(item): + event_type = data.get("type") + index = data.get("index") + if not isinstance(index, int): + continue + if event_type == "content_block_start": + open_indices.add(index) + max_index = index if max_index is None else max(max_index, index) + elif event_type == "content_block_stop": + open_indices.discard(index) + open_index = max(open_indices) if open_indices else None + return open_index, max_index + + @staticmethod + def _iter_sse_events(item: Any) -> list[dict]: + """Yield the event-data dicts in one stream chunk. + + Handles both formats this stream can carry (see + ``get_streaming_string_so_far``): raw SSE ``bytes`` -- which may bundle + several events separated by a blank line -- and an already-parsed event + ``dict``.""" + if isinstance(item, dict): + return [item] + if not isinstance(item, (bytes, bytearray)): + return [] + events: list[dict] = [] + for block in item.decode("utf-8", errors="replace").split("\n\n"): + for line in block.split("\n"): + line = line.strip() + if not line.startswith("data:"): + continue + try: + parsed = json.loads(line[len("data:") :].strip()) + except json.JSONDecodeError: + continue + if isinstance(parsed, dict): + events.append(parsed) + return events + def _translate_to_openai(self, data: dict) -> ChatCompletionRequest: """Translate Anthropic request to OpenAI chat completion format.""" ( @@ -406,6 +573,8 @@ class AnthropicMessagesHandler(BaseTranslation): Get the string so far, check the apply guardrail to the string so far, and return the list of responses so far. """ + from litellm.integrations.custom_guardrail import ModifyResponseException + has_ended = self._check_streaming_has_ended(responses_so_far) if has_ended: # build the model response from the responses_so_far @@ -430,25 +599,35 @@ class AnthropicMessagesHandler(BaseTranslation): if tool_calls_list: guardrail_inputs["tool_calls"] = tool_calls_list - _guardrailed_inputs = ( - await guardrail_to_apply.apply_guardrail( # allow rejecting the response, if invalid + try: + _guardrailed_inputs = await guardrail_to_apply.apply_guardrail( inputs=guardrail_inputs, request_data=request_data if request_data is not None else {}, input_type="response", logging_obj=litellm_logging_obj, ) - ) + except ModifyResponseException as e: + if e.original_response is None: + e.original_response = built_response or self._build_streaming_usage_response( + responses_so_far, request_data + ) + raise else: verbose_proxy_logger.debug("Skipping output guardrail - model response has no choices") return responses_so_far string_so_far = self.get_streaming_string_so_far(responses_so_far) - _guardrailed_inputs = await guardrail_to_apply.apply_guardrail( # allow rejecting the response, if invalid - inputs={"texts": [string_so_far]}, - request_data=request_data if request_data is not None else {}, - input_type="response", - logging_obj=litellm_logging_obj, - ) + try: + _guardrailed_inputs = await guardrail_to_apply.apply_guardrail( + inputs={"texts": [string_so_far]}, + request_data=request_data if request_data is not None else {}, + input_type="response", + logging_obj=litellm_logging_obj, + ) + except ModifyResponseException as e: + if e.original_response is None: + e.original_response = self._build_streaming_usage_response(responses_so_far, request_data) + raise return responses_so_far def _prepare_request_data( diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 9721b797584..1f5b76f3d0a 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -227,6 +227,10 @@ DROP_UNSUPPORTED_OUTPUT_CONFIG_WARNING = ( "Sonnet 4.6+, and Mythos Preview." ) +DROP_UNSUPPORTED_ADAPTIVE_THINKING_WARNING = ( + "Dropping adaptive `thinking` for model=%s: max_tokens is too small to fit the minimum thinking budget." +) + DROP_UNSUPPORTED_SPEED_WARNING = ( "Dropping unsupported `speed` for model=%s (drop_params=True). Fast mode is only supported on select Opus models." ) @@ -266,6 +270,10 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): def custom_llm_provider(self) -> Optional[str]: return "anthropic" + @property + def _resolved_provider(self) -> str: + return self.custom_llm_provider or "anthropic" + @classmethod def get_config(cls, *, model: Optional[str] = None): config = super().get_config() @@ -335,23 +343,26 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): return any(v in model_lower for v in ("opus-4-7", "opus_4_7", "opus-4.7", "opus_4.7")) @staticmethod - def _supports_effort_level(model: str, level: str) -> bool: + def _supports_effort_level(model: str, level: str, custom_llm_provider: str) -> bool: """Check ``supports_{level}_reasoning_effort`` in the model map.""" - return AnthropicConfig._supports_model_capability(model, f"supports_{level}_reasoning_effort") + return AnthropicConfig._supports_model_capability( + model, f"supports_{level}_reasoning_effort", custom_llm_provider + ) @staticmethod - def _validate_effort_for_model(model: str, effort: Optional[str]) -> Optional[str]: + def _validate_effort_for_model(model: str, effort: Optional[str], custom_llm_provider: str) -> Optional[str]: """Return ``None`` if ``effort`` is allowed on ``model``, else an error message.""" if effort == "max" and not ( - AnthropicConfig._is_adaptive_thinking_model(model) or AnthropicConfig._supports_effort_level(model, "max") + AnthropicConfig._is_adaptive_thinking_model(model, custom_llm_provider) + or AnthropicConfig._supports_effort_level(model, "max", custom_llm_provider) ): return f"effort='max' is not supported by this model. Got model: {model}" - if effort == "xhigh" and not AnthropicConfig._supports_effort_level(model, "xhigh"): + if effort == "xhigh" and not AnthropicConfig._supports_effort_level(model, "xhigh", custom_llm_provider): return f"effort='xhigh' is not supported by this model. Got model: {model}" return None @staticmethod - def _model_supports_effort_param(model: str) -> bool: + def _model_supports_effort_param(model: str, custom_llm_provider: str) -> bool: """Whether the model accepts ``output_config.effort`` at all. A model qualifies if its map entry advertises ``supports_output_config`` @@ -359,10 +370,10 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): signals: e.g. Claude Opus 4.5 supports ``output_config`` without advertising a non-default (max/xhigh) effort level. """ - if AnthropicConfig._supports_model_capability(model, "supports_output_config"): + if AnthropicConfig._supports_model_capability(model, "supports_output_config", custom_llm_provider): return True return any( - AnthropicConfig._supports_effort_level(model, level) + AnthropicConfig._supports_effort_level(model, level, custom_llm_provider) for level in ("low", "minimal", "medium", "high", "xhigh", "max") ) @@ -451,7 +462,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): if ( "claude-3-7-sonnet" in model - or AnthropicConfig._is_adaptive_thinking_model(model) + or AnthropicConfig._is_adaptive_thinking_model(model, self._resolved_provider) or supports_reasoning( model=model, custom_llm_provider=self.custom_llm_provider, @@ -1159,11 +1170,13 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): def _map_reasoning_effort( reasoning_effort: Optional[Union[REASONING_EFFORT, str]], model: str, + custom_llm_provider: str, llm_provider: str = "anthropic", ) -> Optional[AnthropicThinkingParam]: + """Capability probes read the cost map under ``custom_llm_provider``; ``llm_provider`` only tags raised exceptions.""" if reasoning_effort is None or reasoning_effort == "none": return None - if AnthropicConfig._is_adaptive_thinking_model(model): + if AnthropicConfig._is_adaptive_thinking_model(model, custom_llm_provider): return AnthropicThinkingParam( type="adaptive", ) @@ -1211,6 +1224,23 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): llm_provider=llm_provider, ) + @staticmethod + def _cap_thinking_budget_to_max_tokens( + thinking: AnthropicThinkingParam, max_tokens: Optional[int] + ) -> Optional[AnthropicThinkingParam]: + """Cap a legacy ``thinking.budget_tokens`` below ``max_tokens`` (Anthropic + requires ``max_tokens > budget_tokens``). Returns the (possibly capped) + thinking dict, or ``None`` when ``max_tokens`` is too small to fit even the + minimum thinking budget and thinking should be dropped.""" + budget = thinking.get("budget_tokens") + if max_tokens is None or not isinstance(budget, int): + return thinking + if max_tokens <= ANTHROPIC_MIN_THINKING_BUDGET_TOKENS: + return None + if budget < max_tokens: + return thinking + return AnthropicThinkingParam(type=thinking.get("type", "enabled"), budget_tokens=max_tokens - 1) + def _extract_json_schema_from_response_format(self, value: Optional[dict]) -> Optional[dict]: if value is None: return None @@ -1454,7 +1484,38 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): ): optional_params["metadata"] = {"user_id": value} elif param == "thinking": - optional_params["thinking"] = value + if ( + isinstance(value, dict) + and value.get("type") == "adaptive" + and not AnthropicConfig._is_adaptive_thinking_model(model, self._resolved_provider) + ): + # Callers (e.g. Claude Code) send adaptive thinking + # unconditionally; translate it down to the legacy + # `thinking={type: enabled, budget_tokens}` interface a + # pre-4.6 model actually supports instead of forwarding a + # shape the model will reject. + max_tokens = non_default_params.get("max_completion_tokens") or non_default_params.get("max_tokens") + legacy_thinking = AnthropicConfig._map_reasoning_effort( + reasoning_effort="medium", + model=model, + custom_llm_provider=self._resolved_provider, + llm_provider=self._resolved_provider, + ) + capped_thinking = ( + AnthropicConfig._cap_thinking_budget_to_max_tokens(legacy_thinking, max_tokens) + if legacy_thinking is not None + else None + ) + if capped_thinking is not None: + optional_params["thinking"] = capped_thinking + else: + litellm.verbose_logger.warning( + DROP_UNSUPPORTED_ADAPTIVE_THINKING_WARNING, + model, + ) + optional_params.pop("thinking", None) + else: + optional_params["thinking"] = value elif param == "reasoning_effort": # Accept both string ("low") and dict ({"effort": "low", # "summary": "concise"}). The Responses->Chat parser keeps the @@ -1471,20 +1532,21 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): mapped_thinking = AnthropicConfig._map_reasoning_effort( reasoning_effort=effort_value, model=model, - llm_provider=self.custom_llm_provider or "anthropic", + custom_llm_provider=self._resolved_provider, + llm_provider=self._resolved_provider, ) if mapped_thinking is None: optional_params.pop("thinking", None) optional_params.pop("output_config", None) else: optional_params["thinking"] = mapped_thinking - if AnthropicConfig._is_adaptive_thinking_model(model): + if AnthropicConfig._is_adaptive_thinking_model(model, self._resolved_provider): mapped_effort = REASONING_EFFORT_TO_OUTPUT_CONFIG_EFFORT.get(effort_value) if mapped_effort is None: AnthropicConfig._raise_invalid_reasoning_effort( model=model, value=effort_value, - llm_provider=self.custom_llm_provider or "anthropic", + llm_provider=self._resolved_provider, ) optional_params["output_config"] = {"effort": mapped_effort} elif param == "web_search_options" and isinstance(value, dict): @@ -1813,7 +1875,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): anthropic_messages = anthropic_messages_pt( model=model, messages=messages, - llm_provider=self.custom_llm_provider or "anthropic", + llm_provider=self._resolved_provider, ) except Exception as e: raise AnthropicError( @@ -1902,7 +1964,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): output_config = optional_params.get("output_config") if not output_config or not isinstance(output_config, dict): return - if litellm.drop_params is True and not self._model_supports_effort_param(model): + if litellm.drop_params is True and not self._model_supports_effort_param(model, self._resolved_provider): litellm.verbose_logger.warning( DROP_UNSUPPORTED_OUTPUT_CONFIG_WARNING, model, @@ -1916,14 +1978,14 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): raise litellm.exceptions.BadRequestError( message=(f"Invalid effort value: {effort!r}. Must be one of: 'high', 'medium', 'low', 'xhigh', 'max'"), model=model, - llm_provider=self.custom_llm_provider or "anthropic", + llm_provider=self._resolved_provider, ) - gate_error = self._validate_effort_for_model(model, effort) + gate_error = self._validate_effort_for_model(model, effort, self._resolved_provider) if gate_error is not None: raise litellm.exceptions.BadRequestError( message=gate_error, model=model, - llm_provider=self.custom_llm_provider or "anthropic", + llm_provider=self._resolved_provider, ) data["output_config"] = output_config diff --git a/litellm/llms/anthropic/common_utils.py b/litellm/llms/anthropic/common_utils.py index db540e5441d..0bcf34a45d6 100644 --- a/litellm/llms/anthropic/common_utils.py +++ b/litellm/llms/anthropic/common_utils.py @@ -289,6 +289,13 @@ class AnthropicModelInfo(BaseLLMModelInfo): status_code=400, ) + @staticmethod + def _strip_version_suffix(model: str) -> str: + at = model.rfind("@") + if at > 0: + return model[:at] + return model + @staticmethod def _model_map_lookup_candidates(model: str) -> List[str]: """Model-map keys to try for ``model``: the id itself, the same id with a @@ -324,6 +331,7 @@ class AnthropicModelInfo(BaseLLMModelInfo): _DATED_RELEASE_SUFFIX_RE.sub("", cand), _DOTTED_VERSION_RE.sub(r"\1-\2", cand), _strip_bedrock_id_suffixes(cand), + AnthropicModelInfo._strip_version_suffix(cand), ) ) return list(dict.fromkeys((*primary, *normalized))) @@ -352,18 +360,43 @@ class AnthropicModelInfo(BaseLLMModelInfo): return value if isinstance(value, bool) else None @staticmethod - def _supports_model_capability(model: str, key: str) -> bool: - """Check a boolean capability ``key`` in the model map. + def _get_provider_resolved_capability(model: str, key: str, custom_llm_provider: str) -> Optional[bool]: + """Resolve boolean capability ``key`` for ``model`` under the caller's provider. - Strips bedrock/vertex prefixes so a provider-routed Claude still - resolves to the Anthropic model-map entry. + Returns the flag when the provider-aware lookup resolves ``model`` to an + entry (or fallback rule) that sets it explicitly, and ``None`` when the + model does not resolve under that provider or the resolved entry has no + opinion on ``key``. + """ + from litellm.utils import _get_model_info_helper + + try: + resolved_model, resolved_provider, _, _ = litellm.get_llm_provider( + model=model, custom_llm_provider=custom_llm_provider + ) + value = _get_model_info_helper(model=resolved_model, custom_llm_provider=resolved_provider).get(key) + except Exception: # noqa: BLE001 # _get_model_info_helper raises bare Exception for unmapped models + return None + return value if isinstance(value, bool) else None + + @staticmethod + def _supports_model_capability(model: str, key: str, custom_llm_provider: str) -> bool: + """Check a boolean capability ``key`` in the model map under the caller's provider. + + The provider-aware lookup is authoritative when it resolves an explicit flag, + so ``key: false`` on the provider-namespaced entry wins over every fallback. + Otherwise ``_supports_factory``'s provider-level fallbacks and the raw + model-map walk remain as backstops for alias forms the lookup misses. """ from litellm.utils import _supports_factory + resolved = AnthropicModelInfo._get_provider_resolved_capability(model, key, custom_llm_provider) + if resolved is not None: + return resolved try: if _supports_factory( model=model, - custom_llm_provider="anthropic", + custom_llm_provider=custom_llm_provider, key=key, ): return True @@ -372,17 +405,24 @@ class AnthropicModelInfo(BaseLLMModelInfo): return AnthropicModelInfo._get_model_capability(model, key) is True @staticmethod - def _is_adaptive_thinking_model(model: str) -> bool: + def _is_adaptive_thinking_model(model: str, custom_llm_provider: str) -> bool: """Whether ``model`` uses adaptive thinking (``output_config.effort``). The model cost map is authoritative: an explicit ``supports_adaptive_thinking`` - entry, or a ``fallback_generalizations`` rule for unknown Claude models. The - version gate (>= 4.6, including provider-prefixed Bedrock/Vertex ids that map to - no exact entry) lives entirely in that declarative rule, not here. + entry resolved under ``custom_llm_provider``, or a ``fallback_generalizations`` + rule for unknown Claude models. The version gate (>= 4.6, including + provider-prefixed Bedrock/Vertex ids that map to no exact entry) lives entirely + in that declarative rule, not here. """ - return AnthropicModelInfo._supports_model_capability(model, "supports_adaptive_thinking") + return AnthropicModelInfo._supports_model_capability(model, "supports_adaptive_thinking", custom_llm_provider) - def is_effort_used(self, optional_params: Optional[dict], model: Optional[str] = None) -> bool: + def is_effort_used( + self, + optional_params: Optional[dict], + model: Optional[str] = None, + *, + custom_llm_provider: str, + ) -> bool: """ Check if effort parameter is being used and requires a beta header. @@ -394,7 +434,7 @@ class AnthropicModelInfo(BaseLLMModelInfo): return False # Claude 4.6+ models use output_config as a stable API feature — no beta header needed - if model and self._is_adaptive_thinking_model(model): + if model and self._is_adaptive_thinking_model(model, custom_llm_provider): return False # Check if reasoning_effort is provided for Claude Opus 4.5 @@ -475,6 +515,8 @@ class AnthropicModelInfo(BaseLLMModelInfo): prompt_caching_set: bool = False, file_id_used: bool = False, mcp_server_used: bool = False, + *, + custom_llm_provider: str, ) -> List[str]: """ Get list of common beta headers based on the features that are active. @@ -487,7 +529,7 @@ class AnthropicModelInfo(BaseLLMModelInfo): betas = [] # Detect features - effort_used = self.is_effort_used(optional_params, model) + effort_used = self.is_effort_used(optional_params, model, custom_llm_provider=custom_llm_provider) if effort_used: betas.append(ANTHROPIC_EFFORT_BETA_HEADER) # effort-2025-11-24 @@ -643,7 +685,7 @@ class AnthropicModelInfo(BaseLLMModelInfo): tool_search_used = self.is_tool_search_used(tools=tools) programmatic_tool_calling_used = self.is_programmatic_tool_calling_used(tools=tools) input_examples_used = self.is_input_examples_used(tools=tools) - effort_used = self.is_effort_used(optional_params=optional_params, model=model) + effort_used = self.is_effort_used(optional_params=optional_params, model=model, custom_llm_provider="anthropic") code_execution_tool_used = self.is_code_execution_tool_used(tools=tools) container_with_skills_used = self.is_container_with_skills_used(optional_params=optional_params) user_anthropic_beta_headers = self._get_user_anthropic_beta_headers( diff --git a/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py b/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py index 812e0f62c96..7299fc16897 100644 --- a/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py @@ -78,7 +78,7 @@ async def _prepare_context_managed_request( system: Optional[Any], context_management_spec: Any, litellm_metadata: Optional[Dict], - drop_params: Optional[bool], + additional_drop_params: Optional[list[str]], llm_router: Any, user_api_key_auth: Any = None, ) -> Optional[PolyfillResult]: @@ -95,7 +95,7 @@ async def _prepare_context_managed_request( # silently drop intermediate turns. polyfill_will_run = _polyfill_will_run( context_management_spec=context_management_spec, - drop_params=drop_params, + additional_drop_params=additional_drop_params, ) if polyfill_will_run: @@ -117,7 +117,7 @@ async def _prepare_context_managed_request( system=working_system, context_management_spec=context_management_spec, litellm_metadata=litellm_metadata, - drop_params=drop_params, + additional_drop_params=additional_drop_params, llm_router=llm_router, user_api_key_auth=user_api_key_auth, ) @@ -143,18 +143,19 @@ async def _prepare_context_managed_request( def _polyfill_will_run( *, context_management_spec: Any, - drop_params: Optional[bool], + additional_drop_params: Optional[list[str]], ) -> bool: """Return True when ``compact_20260112`` will run via the polyfill dispatcher. - Mirrors the gating in ``_run_polyfill_if_enabled``: an empty spec or - effective ``drop_params`` short-circuits the polyfill. The pre-processing - skip only applies when the dispatcher will actually invoke - ``apply_compact_20260112`` (which has its own compaction-block slicing). + Mirrors the gating in ``_run_polyfill_if_enabled``: an empty spec or an + explicit ``context_management`` entry in ``additional_drop_params`` + short-circuits the polyfill. The pre-processing skip only applies when the + dispatcher will actually invoke ``apply_compact_20260112`` (which has its + own compaction-block slicing). """ edits = _normalize_spec_edits( context_management_spec=context_management_spec, - drop_params=drop_params, + additional_drop_params=additional_drop_params, ) if edits is None: return False @@ -169,7 +170,7 @@ def _polyfill_will_run( def _spec_has_non_compact_edits( *, context_management_spec: Any, - drop_params: Optional[bool], + additional_drop_params: Optional[list[str]], ) -> bool: """Return True when the spec includes edits other than ``compact_20260112``. @@ -180,7 +181,7 @@ def _spec_has_non_compact_edits( """ edits = _normalize_spec_edits( context_management_spec=context_management_spec, - drop_params=drop_params, + additional_drop_params=additional_drop_params, ) if edits is None: return False @@ -195,10 +196,22 @@ def _spec_has_non_compact_edits( ) +def _context_management_explicitly_dropped(additional_drop_params: Optional[list[str]]) -> bool: + """True when the caller opted out of context_management via ``additional_drop_params``. + + ``drop_params`` deliberately does NOT gate the polyfill: ``context_management`` + is a LiteLLM-supported param (native on Anthropic, polyfilled elsewhere), and + ``drop_params`` only exists to drop genuinely unsupported params. + """ + if not isinstance(additional_drop_params, list): + return False + return "context_management" in additional_drop_params + + def _normalize_spec_edits( *, context_management_spec: Any, - drop_params: Optional[bool], + additional_drop_params: Optional[list[str]], ) -> Optional[List[Dict[str, Any]]]: """Return the normalized ``edits`` list, or ``None`` if the polyfill won't run. @@ -208,8 +221,7 @@ def _normalize_spec_edits( if not context_management_spec: return None - effective_drop_params = drop_params if drop_params is not None else litellm.drop_params - if effective_drop_params: + if _context_management_explicitly_dropped(additional_drop_params): return None from litellm.llms.anthropic.experimental_pass_through.context_management.dispatcher import ( @@ -230,22 +242,23 @@ async def _run_polyfill_if_enabled( system: Optional[Any], context_management_spec: Any, litellm_metadata: Optional[Dict], - drop_params: Optional[bool], + additional_drop_params: Optional[list[str]], llm_router: Any, user_api_key_auth: Any = None, ) -> Optional[PolyfillResult]: """Run the async context_management polyfill if a spec is present. - Returns ``None`` when the spec is empty or drop_params is on. Raises - ``AnthropicContextManagementError`` so the /v1/messages endpoint can - emit an Anthropic-format 400. All other exceptions are best-effort - swallowed (matches v0 behavior). + Returns ``None`` when the spec is empty or ``context_management`` is + listed in ``additional_drop_params`` (the explicit opt-out; ``drop_params`` + does not disable the polyfill because context_management is a supported + param). Raises ``AnthropicContextManagementError`` so the /v1/messages + endpoint can emit an Anthropic-format 400. All other exceptions are + best-effort swallowed (matches v0 behavior). """ if not context_management_spec: return None - effective_drop_params = drop_params if drop_params is not None else litellm.drop_params - if effective_drop_params: + if _context_management_explicitly_dropped(additional_drop_params): return None try: @@ -274,7 +287,7 @@ async def _run_polyfill_if_enabled( # emits an Anthropic-format error. if _spec_has_non_compact_edits( context_management_spec=context_management_spec, - drop_params=drop_params, + additional_drop_params=additional_drop_params, ): raise AnthropicContextManagementError( status_code=500, @@ -533,7 +546,7 @@ class LiteLLMMessagesToCompletionTransformationHandler: ) -> Union[AnthropicMessagesResponse, AsyncIterator[Any], Iterator[bytes]]: """Handle non-Anthropic models asynchronously using the adapter""" context_management = kwargs.pop("context_management", None) - drop_params: Optional[bool] = kwargs.get("drop_params", None) + additional_drop_params: Optional[list[str]] = kwargs.get("additional_drop_params", None) litellm_router = kwargs.pop("litellm_router", None) if litellm_router is None: try: @@ -555,7 +568,7 @@ class LiteLLMMessagesToCompletionTransformationHandler: system=system, context_management_spec=context_management, litellm_metadata=proxy_litellm_metadata, - drop_params=drop_params, + additional_drop_params=additional_drop_params, llm_router=litellm_router, user_api_key_auth=user_api_key_auth, ) @@ -661,7 +674,7 @@ class LiteLLMMessagesToCompletionTransformationHandler: # ``compact_20260112`` editor can ``await`` the summarization model); # bridge to it via ``run_async_function``. context_management = kwargs.pop("context_management", None) - drop_params: Optional[bool] = kwargs.get("drop_params", None) + additional_drop_params: Optional[list[str]] = kwargs.get("additional_drop_params", None) # Deliberately do NOT auto-attach the proxy ``llm_router`` here: # ``run_async_function`` spawns a new event loop in a worker thread # to bridge to the async dispatcher, but the proxy router's httpx @@ -696,7 +709,7 @@ class LiteLLMMessagesToCompletionTransformationHandler: system=system, context_management_spec=context_management, litellm_metadata=proxy_litellm_metadata, - drop_params=drop_params, + additional_drop_params=additional_drop_params, llm_router=litellm_router, user_api_key_auth=user_api_key_auth, ) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/agentic_streaming_iterator.py b/litellm/llms/anthropic/experimental_pass_through/messages/agentic_streaming_iterator.py index cb37725d79c..4bf36a0d5c6 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/agentic_streaming_iterator.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/agentic_streaming_iterator.py @@ -193,6 +193,14 @@ class AgenticAnthropicStreamingIterator: raise StopAsyncIteration + async def aclose(self) -> None: + from litellm.llms.anthropic.experimental_pass_through.messages.streaming_iterator import ( + aclose_if_supported, + ) + + await aclose_if_supported(self._inner) + await aclose_if_supported(self._follow_up_iterator) + async def _process_agentic_hooks(self) -> None: """Rebuild the Anthropic response from collected SSE bytes and call hooks.""" if self._hook_processing_done: diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index 9c9427c7302..dd983f0c344 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -61,6 +61,19 @@ def _should_route_to_responses_api(custom_llm_provider: Optional[str]) -> bool: return custom_llm_provider in _RESPONSES_API_PROVIDERS +def _deployment_passes_through_anthropic_messages(model_info: object) -> bool: + """Whether the deployment opted into forwarding /v1/messages untranslated. + + The opt-in is ``model_info.supported_endpoints`` containing ``"/v1/messages"``, + declared per deployment in config.yaml and plumbed here as ``kwargs["model_info"]`` + by the router. + """ + if not isinstance(model_info, dict): + return False + supported_endpoints = model_info.get("supported_endpoints") + return isinstance(supported_endpoints, (list, tuple)) and "/v1/messages" in supported_endpoints + + ####### ENVIRONMENT VARIABLES ################### # Initialize any necessary instances or variables here base_llm_http_handler = BaseLLMHTTPHandler() @@ -135,6 +148,7 @@ async def _try_websearch_short_circuit( tools: Optional[List[Dict]], custom_llm_provider: Optional[str], stream: Optional[bool], + kwargs: Optional[dict] = None, ) -> Optional[Union[AnthropicMessagesResponse, AsyncIterator]]: """ Attempt to short-circuit a web-search-only request. @@ -164,6 +178,7 @@ async def _try_websearch_short_circuit( messages=messages, tools=tools, custom_llm_provider=custom_llm_provider, + kwargs=kwargs, ) if response is not None: anthropic_response = cast(AnthropicMessagesResponse, response) @@ -186,7 +201,7 @@ async def anthropic_messages( metadata: Optional[Dict] = None, stop_sequences: Optional[List[str]] = None, stream: Optional[bool] = False, - system: Optional[str] = None, + system: Optional[Union[str, list]] = None, temperature: Optional[float] = None, thinking: Optional[Dict] = None, tool_choice: Optional[Dict] = None, @@ -217,6 +232,12 @@ async def anthropic_messages( # ids like ``functions.Bash:0`` that violate Anthropic's id pattern. messages = sanitize_tool_use_ids_in_anthropic_messages(messages) + from litellm.integrations.anthropic_cache_control_hook import ( + AnthropicCacheControlHook, + ) + + messages, system = AnthropicCacheControlHook.maybe_inject_cache_control(messages, system, kwargs) + original_stream = stream or kwargs.get("_websearch_interception_converted_stream", False) # Execute pre-request hooks to allow CustomLoggers to modify request. @@ -273,6 +294,7 @@ async def anthropic_messages( tools=tools, custom_llm_provider=custom_llm_provider, stream=original_stream, + kwargs={**kwargs, "metadata": metadata}, ) if short_circuit_response is not None: return short_circuit_response @@ -362,7 +384,7 @@ def anthropic_messages_handler( metadata: Optional[Dict] = None, stop_sequences: Optional[List[str]] = None, stream: Optional[bool] = False, - system: Optional[str] = None, + system: Optional[Union[str, list]] = None, temperature: Optional[float] = None, thinking: Optional[Dict] = None, tool_choice: Optional[Dict] = None, @@ -399,6 +421,12 @@ def anthropic_messages_handler( messages = strip_empty_text_blocks_from_anthropic_messages(messages) messages = sanitize_tool_use_ids_in_anthropic_messages(messages) + from litellm.integrations.anthropic_cache_control_hook import ( + AnthropicCacheControlHook, + ) + + messages, system = AnthropicCacheControlHook.maybe_inject_cache_control(messages, system, kwargs) + metadata = validate_anthropic_api_metadata(metadata) local_vars = locals() @@ -456,6 +484,14 @@ def anthropic_messages_handler( model=model, provider=litellm.LlmProviders(custom_llm_provider), ) + if anthropic_messages_provider_config is None and _deployment_passes_through_anthropic_messages( + kwargs.get("model_info") + ): + from litellm.llms.openai_like.messages.transformation import ( + OpenAILikeAnthropicMessagesConfig, + ) + + anthropic_messages_provider_config = OpenAILikeAnthropicMessagesConfig() if anthropic_messages_provider_config is None: # Route to Responses API for OpenAI / Azure, chat/completions for everything else. _shared_kwargs = dict( diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/interceptors/advisor.py b/litellm/llms/anthropic/experimental_pass_through/messages/interceptors/advisor.py index 6c72b7a3e00..79faa39c7a2 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/interceptors/advisor.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/interceptors/advisor.py @@ -17,7 +17,9 @@ How it works: import uuid from typing import Any, AsyncIterator, Dict, List, Optional, Union +import litellm import litellm.constants as _c +from litellm.litellm_core_utils.url_utils import validate_url from litellm.llms.anthropic.common_utils import strip_advisor_blocks_from_messages from litellm.types.llms.anthropic_messages.anthropic_response import ( AnthropicMessagesResponse, @@ -76,16 +78,7 @@ class AdvisorOrchestrationHandler(MessagesInterceptor): raise ValueError("advisor tool definition must include a 'model' field specifying the advisor model") _raw_max_uses = advisor_tool.get("max_uses") max_uses: int = ADVISOR_MAX_USES if _raw_max_uses is None else int(_raw_max_uses) - # Optional routing overrides for the advisor sub-call (e.g. proxy routing). - # If not set in the tool definition, litellm resolves from env vars. - # The advisor tool is caller-controlled; only honor a client-supplied - # api_base/api_key when the proxy has enabled clientside credentials, - # otherwise let litellm resolve from server config. - advisor_api_key: Optional[str] = None - advisor_api_base: Optional[str] = None - if _allow_client_side_advisor_credentials(): - advisor_api_key = advisor_tool.get("api_key") - advisor_api_base = advisor_tool.get("api_base") + advisor_api_key, advisor_api_base = _resolve_advisor_credentials(advisor_tool) # Build the synthetic tool definition the provider will receive. synthetic_advisor_tool = _make_synthetic_advisor_tool() @@ -186,6 +179,49 @@ def _allow_client_side_advisor_credentials() -> bool: return general_settings.get("allow_client_side_credentials") is True +def _resolve_advisor_credentials(advisor_tool: dict) -> tuple[Optional[str], Optional[str]]: + """Resolve the (api_key, api_base) override for the advisor sub-call. + + A caller-supplied ``api_base`` is only honored alongside a caller-supplied + ``api_key``: without one, ``AnthropicModelInfo.get_auth_header()`` falls + back to the proxy's own Anthropic credentials, which would then be sent to + the caller-chosen ``api_base``. A caller-supplied ``api_base`` is also + required to be https with TLS verification on, and SSRF-validated so it + can't target a private/internal/cloud-metadata address, mirroring + ``proxy.auth.auth_utils.check_complete_credentials``. https with TLS + verification is required because ``validate_url`` only rewrites the + connection to a DNS-pinned IP for http, or for https with + ``litellm.ssl_verify`` disabled; otherwise it returns the URL unchanged + and relies on certificate validation to block DNS rebinding, so this + closes the same gap without threading the pinned URL through the whole + ``anthropic_messages()`` call chain. + """ + if not _allow_client_side_advisor_credentials(): + return None, None + api_key: Optional[str] = advisor_tool.get("api_key") + api_base: Optional[str] = advisor_tool.get("api_base") + if api_base is None: + return api_key, None + if not api_key: + raise ValueError( + "advisor tool definition sets 'api_base' without 'api_key'. A " + "caller-supplied api_base is only honored alongside a " + "caller-supplied api_key, so the proxy's own credentials are " + "never sent to a caller-chosen destination." + ) + if not api_base.startswith("https://"): + raise ValueError(f"advisor tool definition sets 'api_base'={api_base!r}, which must use the https scheme.") + if getattr(litellm, "ssl_verify", True) is False: + raise ValueError( + "advisor tool definition sets 'api_base' but the proxy has TLS verification " + "disabled (litellm.ssl_verify=False), so a caller-supplied api_base can't be " + "safely validated against DNS rebinding." + ) + if getattr(litellm, "user_url_validation", True): + validate_url(api_base) + return api_key, api_base + + def _make_synthetic_advisor_tool() -> Dict: """Build a regular tool definition the executor provider can understand.""" return { diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/streaming_iterator.py b/litellm/llms/anthropic/experimental_pass_through/messages/streaming_iterator.py index 2357960f716..5f2b23d7eca 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/streaming_iterator.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/streaming_iterator.py @@ -1,8 +1,13 @@ import asyncio import json from datetime import datetime -from typing import Any, AsyncIterator, List, Union +from typing import Any, AsyncIterator, List, Protocol, Union, runtime_checkable +import httpx +from pydantic import TypeAdapter +from typing_extensions import TypedDict + +from litellm.litellm_core_utils.core_helpers import process_response_headers from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.proxy.pass_through_endpoints.success_handler import ( PassThroughEndpointLogging, @@ -12,6 +17,93 @@ from litellm.types.utils import GenericStreamingChunk, ModelResponseStream GLOBAL_PASS_THROUGH_SUCCESS_HANDLER_OBJ = PassThroughEndpointLogging() +INCOMPLETE_STREAM_ERROR_MESSAGE = ( + "Provider stream ended before emitting a message_stop event; " + "the response is incomplete and any partial content (e.g. tool_use input JSON) may be truncated." +) + + +def _is_message_stop_chunk(chunk: object) -> bool: + if isinstance(chunk, dict): + return chunk.get("type") == "message_stop" + if isinstance(chunk, (bytes, bytearray)): + return any(line == b"event: message_stop" for line in chunk.splitlines()) + return False + + +def _is_provider_error_chunk(chunk: object) -> bool: + if isinstance(chunk, dict): + return chunk.get("type") == "error" + if isinstance(chunk, (bytes, bytearray)): + return any(line == b"event: error" for line in chunk.splitlines()) + return False + + +def _is_terminal_stream_chunk(chunk: object) -> bool: + return _is_message_stop_chunk(chunk) or _is_provider_error_chunk(chunk) + + +def _incomplete_stream_error_sse_event() -> bytes: + payload = json.dumps( + { + "type": "error", + "error": {"type": "api_error", "message": INCOMPLETE_STREAM_ERROR_MESSAGE}, + } + ) + return f"event: error\ndata: {payload}\n\n".encode() + + +class AnthropicMessagesStreamHiddenParams(TypedDict): + additional_headers: dict[str, str] + + +@runtime_checkable +class SupportsAclose(Protocol): + async def aclose(self) -> None: ... + + +async def aclose_if_supported(stream: object) -> None: + if isinstance(stream, SupportsAclose): + await stream.aclose() + + +_RESPONSE_HEADERS_ADAPTER: TypeAdapter[dict[str, str]] = TypeAdapter(dict[str, str]) + + +def anthropic_messages_stream_hidden_params( + response_headers: httpx.Headers, +) -> AnthropicMessagesStreamHiddenParams: + return AnthropicMessagesStreamHiddenParams( + additional_headers=_RESPONSE_HEADERS_ADAPTER.validate_python(process_response_headers(response_headers)) + ) + + +class AnthropicMessagesStreamingResponse: + """ + Wraps the /v1/messages SSE byte stream so upstream provider response + headers (e.g. Bedrock's x-amzn-requestid / x-amzn-trace-id) survive as + ``_hidden_params["additional_headers"]``, which the proxy forwards to + clients as ``llm_provider-*`` response headers. Bare async generators + cannot carry attributes, so header context was previously dropped. + """ + + def __init__( + self, + completion_stream: AsyncIterator[bytes], + hidden_params: AnthropicMessagesStreamHiddenParams, + ) -> None: + self.completion_stream = completion_stream + self._hidden_params = hidden_params + + def __aiter__(self) -> "AnthropicMessagesStreamingResponse": + return self + + async def __anext__(self) -> bytes: + return await self.completion_stream.__anext__() + + async def aclose(self) -> None: + await aclose_if_supported(self.completion_stream) + class BaseAnthropicMessagesStreamingIterator: """ @@ -102,13 +194,18 @@ class BaseAnthropicMessagesStreamingIterator: This method provides the common logic for both Anthropic and Bedrock implementations. """ collected_chunks = [] + saw_terminal_event = False async for chunk in completion_stream: if self.completion_start_time is None: self.completion_start_time = datetime.now() + saw_terminal_event = saw_terminal_event or _is_terminal_stream_chunk(chunk) encoded_chunk = self._convert_chunk_to_sse_format(chunk) collected_chunks.append(encoded_chunk) yield encoded_chunk + if not saw_terminal_event: + yield _incomplete_stream_error_sse_event() + # Handle logging after all chunks are processed await self._handle_streaming_logging(collected_chunks) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py b/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py index e78802a1587..00941587753 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py @@ -32,8 +32,22 @@ from ...common_utils import ( DEFAULT_ANTHROPIC_API_VERSION = "2023-06-01" +DROP_UNSUPPORTED_ADAPTIVE_EFFORT_WARNING = ( + "Dropping adaptive `thinking`/`output_config.effort` for model=%s: the model " + "does not support extended thinking, or max_tokens is too small to fit the " + "minimum thinking budget." +) + class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): + @property + def custom_llm_provider(self) -> Optional[str]: + return "anthropic" + + @property + def _resolved_provider(self) -> str: + return self.custom_llm_provider or "anthropic" + def get_supported_anthropic_messages_params(self, model: str) -> list: return [ "messages", @@ -174,7 +188,7 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): return headers, api_base @staticmethod - def _translate_reasoning_effort_to_anthropic(model: str, optional_params: Dict) -> None: + def _translate_reasoning_effort_to_anthropic(model: str, optional_params: Dict, custom_llm_provider: str) -> None: """Map OpenAI-style ``reasoning_effort`` to native Anthropic params. Caller-supplied ``thinking`` / ``output_config`` win over the alias. @@ -191,7 +205,11 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): return try: - mapped_thinking = AnthropicConfig._map_reasoning_effort(reasoning_effort=reasoning_effort, model=model) + mapped_thinking = AnthropicConfig._map_reasoning_effort( + reasoning_effort=reasoning_effort, + model=model, + custom_llm_provider=custom_llm_provider, + ) except _BadRequestError as e: raise AnthropicError(message=str(e.message), status_code=400) @@ -201,7 +219,7 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): return optional_params.setdefault("thinking", mapped_thinking) - if AnthropicModelInfo._is_adaptive_thinking_model(model): + if AnthropicModelInfo._is_adaptive_thinking_model(model, custom_llm_provider): mapped_effort = REASONING_EFFORT_TO_OUTPUT_CONFIG_EFFORT.get(reasoning_effort) if mapped_effort is None: raise AnthropicError( @@ -212,7 +230,7 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): ), status_code=400, ) - gate_error = AnthropicConfig._validate_effort_for_model(model, mapped_effort) + gate_error = AnthropicConfig._validate_effort_for_model(model, mapped_effort, custom_llm_provider) if gate_error is not None: raise AnthropicError(message=gate_error, status_code=400) existing_output_config = optional_params.get("output_config") @@ -222,13 +240,15 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): optional_params["output_config"] = existing_output_config @staticmethod - def _translate_legacy_thinking_for_adaptive_model(model: str, optional_params: Dict) -> None: + def _translate_legacy_thinking_for_adaptive_model( + model: str, optional_params: Dict, custom_llm_provider: str + ) -> None: """Translate legacy ``thinking.type=enabled`` to adaptive for 4.6/4.7. Caller-provided ``output_config.effort`` is never overridden. """ from litellm.llms.anthropic.chat.transformation import AnthropicConfig - if not AnthropicModelInfo._is_adaptive_thinking_model(model): + if not AnthropicModelInfo._is_adaptive_thinking_model(model, custom_llm_provider): return thinking = optional_params.get("thinking") if not isinstance(thinking, dict) or thinking.get("type") != "enabled": @@ -236,7 +256,7 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): budget = int(thinking.get("budget_tokens") or 0) if budget >= DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET and ( - AnthropicConfig._supports_effort_level(model, "xhigh") + AnthropicConfig._supports_effort_level(model, "xhigh", custom_llm_provider) ): effort = "xhigh" elif budget >= DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET: @@ -253,6 +273,108 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): existing_output_config.setdefault("effort", effort) optional_params["output_config"] = existing_output_config + @staticmethod + def _translate_adaptive_effort_for_non_adaptive_model( + model: str, optional_params: Dict, max_tokens: Optional[int], custom_llm_provider: str + ) -> None: + """Translate the 4.6+ adaptive-thinking interface (``thinking.type=adaptive`` + and/or ``output_config.effort``) down to what an older Anthropic model + supports. Clients like Claude Code send this interface unconditionally, so + without translation it reaches a pre-4.6 model and Anthropic rejects it with + "This model does not support the effort parameter". + + The reshape is silent, matching how the messages path already strips + unsupported ``output_config`` for older models (bedrock invoke, issue + #22797): the goal is to keep the request working, not to fail it. + + ``thinking.type=adaptive`` and ``output_config.effort`` are independent + capabilities. Adaptive thinking needs ``supports_adaptive_thinking`` (4.6+); + ``output_config.effort`` needs ``supports_output_config``, which some + non-adaptive models (e.g. Claude Opus 4.5) advertise on its own. So the two + are handled separately: + + - Adaptive-thinking models (4.6+): both are native, left untouched. + - ``supports_output_config`` but non-adaptive (Opus 4.5): keep + ``output_config.effort`` (native), only drop the unsupported adaptive + ``thinking`` block. When adaptive thinking is being dropped and the + effort level itself isn't supported by the model (e.g. ``xhigh``/``max`` + on Opus 4.5, which only accepts low/medium/high, while ``xhigh`` is + Claude Code's default), fall through to the legacy translation below + instead of forwarding a level Anthropic would reject. Effort-only + requests are always left untouched: provider subclasses own their level + normalization (bedrock clamps ``xhigh`` to the model's ceiling after + this base transform runs). + - Thinking-capable but neither (``supports_reasoning``, e.g. Haiku/Sonnet + 4.5): map effort to legacy ``thinking={type: enabled, budget_tokens}`` via + ``AnthropicConfig._map_reasoning_effort``, capped below ``max_tokens`` + (Anthropic requires ``max_tokens > budget_tokens``) and dropped when + ``max_tokens`` can't fit even the minimum budget. + - No reasoning support: ``thinking`` is dropped. + + For the last two, only the consumed ``effort`` key is removed from + ``output_config``; any residual (e.g. ``format``) is left for provider + subclasses to handle. + """ + from litellm.exceptions import BadRequestError as _BadRequestError + from litellm.llms.anthropic.chat.transformation import AnthropicConfig + + if AnthropicConfig._is_adaptive_thinking_model(model, custom_llm_provider): + return + + output_config = optional_params.get("output_config") + thinking = optional_params.get("thinking") + effort = output_config.get("effort") if isinstance(output_config, dict) else None + adaptive_thinking = isinstance(thinking, dict) and thinking.get("type") == "adaptive" + if effort is None and not adaptive_thinking: + return + + # Models that natively accept `output_config.effort` but are not adaptive (Claude Opus 4.5). + # Keep the native effort and only drop the adaptive `thinking` block, which these models + # reject. Effort-only requests pass through so provider subclasses (bedrock/vertex) keep + # owning level clamping; an adaptive request only stays here when its effort level is one + # the model supports, otherwise it falls through to the legacy budget translation below. + if AnthropicConfig._model_supports_effort_param(model, custom_llm_provider) and ( + not adaptive_thinking + or AnthropicConfig._validate_effort_for_model(model, effort, custom_llm_provider) is None + ): + if adaptive_thinking: + optional_params.pop("thinking", None) + return + + supports_thinking = AnthropicModelInfo._supports_model_capability( + model, "supports_reasoning", custom_llm_provider + ) + try: + legacy_thinking = ( + AnthropicConfig._map_reasoning_effort( + reasoning_effort=effort or "medium", + model=model, + custom_llm_provider=custom_llm_provider, + ) + if supports_thinking + else None + ) + except _BadRequestError as e: + raise AnthropicError(message=str(e.message), status_code=400) + capped_thinking = ( + AnthropicConfig._cap_thinking_budget_to_max_tokens(legacy_thinking, max_tokens) + if legacy_thinking is not None + else None + ) + + if capped_thinking is not None: + optional_params["thinking"] = capped_thinking + else: + verbose_logger.warning(DROP_UNSUPPORTED_ADAPTIVE_EFFORT_WARNING, model) + optional_params.pop("thinking", None) + + if isinstance(output_config, dict) and "effort" in output_config: + residual = {k: v for k, v in output_config.items() if k != "effort"} + if residual: + optional_params["output_config"] = residual + else: + optional_params.pop("output_config", None) + def transform_anthropic_messages_request( self, model: str, @@ -277,11 +399,20 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): self._translate_reasoning_effort_to_anthropic( model=model, optional_params=anthropic_messages_optional_request_params, + custom_llm_provider=self._resolved_provider, ) self._translate_legacy_thinking_for_adaptive_model( model=model, optional_params=anthropic_messages_optional_request_params, + custom_llm_provider=self._resolved_provider, + ) + + self._translate_adaptive_effort_for_non_adaptive_model( + model=model, + optional_params=anthropic_messages_optional_request_params, + max_tokens=max_tokens, + custom_llm_provider=self._resolved_provider, ) system_param = anthropic_messages_optional_request_params.get("system") diff --git a/litellm/llms/anthropic/files/transformation.py b/litellm/llms/anthropic/files/transformation.py index cf12ad9ab32..0fa01e09492 100644 --- a/litellm/llms/anthropic/files/transformation.py +++ b/litellm/llms/anthropic/files/transformation.py @@ -39,6 +39,7 @@ from ..common_utils import AnthropicError, AnthropicModelInfo ANTHROPIC_FILES_API_BASE = "https://api.anthropic.com" ANTHROPIC_FILES_BETA_HEADER = "files-api-2025-04-14" +ANTHROPIC_MESSAGE_BATCH_ID_PREFIX = "msgbatch_" class AnthropicFilesConfig(BaseFilesConfig): @@ -258,6 +259,8 @@ class AnthropicFilesConfig(BaseFilesConfig): file_id = file_content_request.get("file_id") api_base = AnthropicModelInfo.get_api_base(litellm_params.get("api_base")) or ANTHROPIC_FILES_API_BASE encoded_file_id = encode_url_path_segment(file_id, field_name="file_id") + if file_id.startswith(ANTHROPIC_MESSAGE_BATCH_ID_PREFIX): + return f"{api_base.rstrip('/')}/v1/messages/batches/{encoded_file_id}/results", {} return f"{api_base.rstrip('/')}/v1/files/{encoded_file_id}/content", {} def transform_file_content_response( diff --git a/litellm/llms/azure/responses/transformation.py b/litellm/llms/azure/responses/transformation.py index fef9b7d0154..d0b0dbb070d 100644 --- a/litellm/llms/azure/responses/transformation.py +++ b/litellm/llms/azure/responses/transformation.py @@ -205,7 +205,7 @@ class AzureOpenAIResponsesAPIConfig(OpenAIResponsesAPIConfig): ######################################################### ########## DELETE RESPONSE API TRANSFORMATION ############## ######################################################### - def _construct_url_for_response_id_in_path(self, api_base: str, response_id: str) -> str: + def _construct_url_for_response_id_in_path(self, api_base: str, response_id: str, path_suffix: str = "") -> str: """ Constructs a URL for the API request with the response_id in the path. """ @@ -218,14 +218,14 @@ class AzureOpenAIResponsesAPIConfig(OpenAIResponsesAPIConfig): # Remove trailing slash if present to avoid double slashes path = parsed_url.path.rstrip("/") encoded_response_id = encode_url_path_segment(response_id, field_name="response_id") - new_path = f"{path}/{encoded_response_id}" + new_path = f"{path}/{encoded_response_id}{path_suffix}" # Reconstruct the URL with all original components but with the modified path constructed_url = urlunparse( ( parsed_url.scheme, # http, https parsed_url.netloc, # domain name, port - new_path, # path with response_id added + new_path, parsed_url.params, # parameters parsed_url.query, # query string parsed_url.fragment, # fragment @@ -288,7 +288,9 @@ class AzureOpenAIResponsesAPIConfig(OpenAIResponsesAPIConfig): limit: int = 20, order: Literal["asc", "desc"] = "desc", ) -> Tuple[str, Dict]: - url = self._construct_url_for_response_id_in_path(api_base=api_base, response_id=response_id) + "/input_items" + url = self._construct_url_for_response_id_in_path( + api_base=api_base, response_id=response_id, path_suffix="/input_items" + ) params: Dict[str, Any] = {} if after is not None: params["after"] = after @@ -322,27 +324,8 @@ class AzureOpenAIResponsesAPIConfig(OpenAIResponsesAPIConfig): This function handles URLs with query parameters by inserting the response_id at the correct location (before any query parameters). """ - from urllib.parse import urlparse, urlunparse - - # Parse the URL to separate its components - parsed_url = urlparse(api_base) - - # Insert the response_id and /cancel at the end of the path component - # Remove trailing slash if present to avoid double slashes - path = parsed_url.path.rstrip("/") - encoded_response_id = encode_url_path_segment(response_id, field_name="response_id") - new_path = f"{path}/{encoded_response_id}/cancel" - - # Reconstruct the URL with all original components but with the modified path - cancel_url = urlunparse( - ( - parsed_url.scheme, # http, https - parsed_url.netloc, # domain name, port - new_path, # path with response_id and /cancel added - parsed_url.params, # parameters - parsed_url.query, # query string - parsed_url.fragment, # fragment - ) + cancel_url = self._construct_url_for_response_id_in_path( + api_base=api_base, response_id=response_id, path_suffix="/cancel" ) data: Dict = {} diff --git a/litellm/llms/azure_ai/anthropic/messages_transformation.py b/litellm/llms/azure_ai/anthropic/messages_transformation.py index 1de18701a2f..8cee35989af 100644 --- a/litellm/llms/azure_ai/anthropic/messages_transformation.py +++ b/litellm/llms/azure_ai/anthropic/messages_transformation.py @@ -21,6 +21,10 @@ class AzureAnthropicMessagesConfig(AnthropicMessagesConfig): and Azure endpoint format. """ + @property + def custom_llm_provider(self) -> Optional[str]: + return "azure_ai" + def should_strip_billing_metadata(self) -> bool: return True diff --git a/litellm/llms/azure_ai/ocr/common_utils.py b/litellm/llms/azure_ai/ocr/common_utils.py index d1d5b80b78d..14b77338fd7 100644 --- a/litellm/llms/azure_ai/ocr/common_utils.py +++ b/litellm/llms/azure_ai/ocr/common_utils.py @@ -13,6 +13,17 @@ if TYPE_CHECKING: from litellm.llms.base_llm.ocr.transformation import BaseOCRConfig +def is_azure_document_intelligence_model(model: str) -> bool: + """Whether an azure_ai OCR model routes to Azure Document Intelligence. + + Azure AI exposes two OCR services on the same provider; the sub-route in the + model name (`azure_ai/doc-intelligence/`) selects Document Intelligence + over Mistral OCR. This is the single source of truth for that routing decision. + """ + lowered = model.lower() + return "doc-intelligence" in lowered or "documentintelligence" in lowered + + def get_azure_ai_ocr_config(model: str) -> Optional["BaseOCRConfig"]: """ Determine which Azure AI OCR configuration to use based on the model name. @@ -41,7 +52,7 @@ def get_azure_ai_ocr_config(model: str) -> Optional["BaseOCRConfig"]: from litellm.llms.azure_ai.ocr.transformation import AzureAIOCRConfig # Check for Azure Document Intelligence models - if "doc-intelligence" in model or "documentintelligence" in model: + if is_azure_document_intelligence_model(model): verbose_logger.debug(f"Routing {model} to Azure Document Intelligence OCR config") return AzureDocumentIntelligenceOCRConfig() diff --git a/litellm/llms/azure_ai/ocr/document_intelligence/transformation.py b/litellm/llms/azure_ai/ocr/document_intelligence/transformation.py index c67703f64d7..7d915892a28 100644 --- a/litellm/llms/azure_ai/ocr/document_intelligence/transformation.py +++ b/litellm/llms/azure_ai/ocr/document_intelligence/transformation.py @@ -15,6 +15,7 @@ from typing import Any, Dict from urllib.parse import quote import httpx +from pydantic import BaseModel from litellm._logging import verbose_logger from litellm.litellm_core_utils.url_utils import SSRFError, assert_same_origin @@ -38,6 +39,30 @@ from litellm.secret_managers.main import get_secret_str AZURE_DOCUMENT_INTELLIGENCE_API_KEY_ENV_VAR = "AZURE_DOCUMENT_INTELLIGENCE_API_KEY" +class AzureDocumentIntelligenceLine(BaseModel): + content: str | None = None + + +class AzureDocumentIntelligencePage(BaseModel): + pageNumber: int | None = None + width: float | None = None + height: float | None = None + unit: str | None = None + lines: tuple[AzureDocumentIntelligenceLine, ...] = () + + +class AzureDocumentIntelligenceAnalyzeResult(BaseModel): + content: str | None = None + pages: tuple[AzureDocumentIntelligencePage, ...] = () + tables: list[dict[str, object]] | None = None + keyValuePairs: list[dict[str, object]] | None = None + + +class AzureDocumentIntelligenceOperation(BaseModel): + status: str | None = None + analyzeResult: AzureDocumentIntelligenceAnalyzeResult | None = None + + class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): """ Azure Document Intelligence OCR transformation configuration. @@ -67,11 +92,14 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): (1-based, e.g. "1-3,5,7-9"). To keep the public request shape aligned with Mistral OCR, callers pass `pages` using Mistral semantics — a list of 0-based integers — or a pre-formatted - Azure-style string. Other Mistral-specific params (e.g. + Azure-style string. Azure DI also exposes a `features` query + parameter enabling add-on capabilities (e.g. "keyValuePairs", + "languages"), passed as a list of feature names or a + comma-separated string. Other Mistral-specific params (e.g. `include_image_base64`) are not supported by Azure DI and are ignored during transformation. """ - return ["pages"] + return ["pages", "features"] def map_ocr_params( self, @@ -85,16 +113,18 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): Translates Mistral-style `pages` (list[int], 0-based) into Azure's `pages` query string (1-based, e.g. "1,2,3" or "1-3,5"). A raw string that already matches Azure's format is passed through - unchanged. + unchanged. `features` (list[str] or comma-separated string) is + normalized into Azure's comma-joined `features` query string. """ pages = non_default_params.get("pages") - if pages is None: - return optional_params - - normalized = self._normalize_pages_param(pages) - if normalized: - optional_params["pages"] = normalized - return optional_params + features = non_default_params.get("features") + normalized_pages = self._normalize_pages_param(pages) if pages is not None else "" + normalized_features = self._normalize_features_param(features) if features is not None else "" + return { + **optional_params, + **({"pages": normalized_pages} if normalized_pages else {}), + **({"features": normalized_features} if normalized_features else {}), + } @staticmethod def _normalize_pages_param(pages: Any) -> str: @@ -140,6 +170,39 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): raise ValueError("`pages` must be a list[int] (0-based, Mistral-style) or a string like '1-3,5,7-9'.") + @staticmethod + def _normalize_features_param(features: object) -> str: + """ + Convert a caller-provided `features` value to Azure DI's query-string + form (comma-joined feature names, e.g. "keyValuePairs,languages"). + + Accepted inputs: + - list[str]: feature names like ["keyValuePairs", "languages"]. + - str: a single feature name or comma-separated names. + """ + invalid_features_error = ValueError( + f"Invalid `features` for Azure Document Intelligence: {features!r}. " + f"Expected a list of feature names or a comma-separated string like " + f"'keyValuePairs' or 'keyValuePairs,languages'." + ) + + if isinstance(features, str): + raw_tokens = features.split(",") + elif isinstance(features, list): + if len(features) == 0: + return "" + raw_tokens = [feature for feature in features if isinstance(feature, str)] + if len(raw_tokens) != len(features): + raise invalid_features_error + else: + raise invalid_features_error + + tokens = tuple(token.strip() for token in raw_tokens) + feature_pattern = re.compile(r"^[A-Za-z][A-Za-z0-9]*$") + if not all(feature_pattern.match(token) for token in tokens): + raise invalid_features_error + return ",".join(tokens) + def validate_environment( self, headers: Dict, @@ -228,13 +291,15 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): f"?api-version={AZURE_DOCUMENT_INTELLIGENCE_API_VERSION}" ) - # Azure DI accepts `pages` as a query param (1-based, e.g. "1-3,5"). + # Azure DI accepts `pages` (1-based, e.g. "1-3,5") and `features` + # (comma-joined names, e.g. "keyValuePairs") as query params. # `optional_params` has already been normalized in `map_ocr_params`. pages = optional_params.get("pages") if optional_params else None - if pages: - url += f"&pages={quote(str(pages), safe=',-')}" + features = optional_params.get("features") if optional_params else None + pages_query = f"&pages={quote(str(pages), safe=',-')}" if pages else "" + features_query = f"&features={quote(str(features), safe=',')}" if features else "" - return url + return f"{url}{pages_query}{features_query}" def _extract_base64_from_data_uri(self, data_uri: str) -> str: """ @@ -328,27 +393,15 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): return OCRRequestData(data=data, files=None) - def _extract_page_markdown(self, page_data: Dict[str, Any]) -> str: - """ - Extract text from Azure DI page and format as markdown. - - Azure DI provides text in 'lines' array. We concatenate them with newlines. - - Args: - page_data: Azure DI page object - - Returns: - Markdown-formatted text - """ - lines = page_data.get("lines", []) - if not lines: - return "" - - # Extract text content from each line - text_lines = [line.get("content", "") for line in lines] - - # Join with newlines to preserve structure - return "\n".join(text_lines) + def _transform_azure_page(self, azure_page: AzureDocumentIntelligencePage) -> OCRPage: + page_number = azure_page.pageNumber if azure_page.pageNumber is not None else 1 + markdown = "\n".join(line.content or "" for line in azure_page.lines) + dimensions = self._convert_dimensions( + width=azure_page.width if azure_page.width is not None else 8.5, + height=azure_page.height if azure_page.height is not None else 11, + unit=azure_page.unit if azure_page.unit is not None else "inch", + ) + return OCRPage(index=page_number - 1, markdown=markdown, dimensions=dimensions) def _convert_dimensions(self, width: float, height: float, unit: str) -> OCRPageDimensions: """ @@ -526,6 +579,52 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): retry_after = self._get_retry_after(response=response) await asyncio.sleep(retry_after) + def _get_polling_target(self, raw_response: httpx.Response) -> tuple[str, Dict[str, str]]: + operation_url = raw_response.headers.get("Operation-Location") + if not operation_url: + raise ValueError("Azure Document Intelligence returned 202 but no Operation-Location header found") + + # Reject cross-origin polling URLs — the auth headers + # below would otherwise leak to whatever URL the upstream + # (or an attacker-controlled upstream) returns. VERIA-51. + try: + assert_same_origin(operation_url, str(raw_response.request.url)) + except SSRFError as ssrf_err: + raise ValueError(f"Azure Document Intelligence: rejected polling URL ({ssrf_err})") + + poll_headers = {"Ocp-Apim-Subscription-Key": raw_response.request.headers.get("Ocp-Apim-Subscription-Key", "")} + return operation_url, poll_headers + + def _transform_completed_response(self, model: str, raw_response: httpx.Response) -> OCRResponse: + """ + Transform a completed Azure Document Intelligence analyze operation + into the Mistral OCR response shape, preserving Azure-native + `analyzeResult` fields (`content`, `tables`, `keyValuePairs`) as + top-level response fields. + """ + operation = AzureDocumentIntelligenceOperation.model_validate(raw_response.json()) + + verbose_logger.debug(f"Azure Document Intelligence response status: {operation.status}") + + if operation.status != "succeeded": + raise ValueError(f"Azure Document Intelligence analysis failed with status: {operation.status}") + + analyze_result = ( + operation.analyzeResult if operation.analyzeResult is not None else AzureDocumentIntelligenceAnalyzeResult() + ) + mistral_pages = [self._transform_azure_page(azure_page) for azure_page in analyze_result.pages] + usage_info = OCRUsageInfo(pages_processed=len(mistral_pages), doc_size_bytes=None) + + return OCRResponse( + pages=mistral_pages, + model=model, + usage_info=usage_info, + object="ocr", + content=analyze_result.content, + tables=analyze_result.tables, + keyValuePairs=analyze_result.keyValuePairs, + ) + def transform_ocr_response( self, model: str, @@ -552,11 +651,13 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): "unit": "inch", "lines": [{"content": "text", "boundingBox": [...]}] } - ] + ], + "tables": [...], + "keyValuePairs": [...] } } - Mistral OCR format: + Mistral OCR format (with Azure-native fields preserved): { "pages": [ { @@ -567,7 +668,10 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): ], "model": "azure_ai/doc-intelligence/prebuilt-layout", "usage_info": {"pages_processed": 1}, - "object": "ocr" + "object": "ocr", + "content": "Full document text...", + "tables": [...], + "keyValuePairs": [...] } Args: @@ -578,86 +682,17 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): Returns: OCRResponse in Mistral format """ - try: - # Check if we got 202 Accepted (async operation started) - if raw_response.status_code == 202: - verbose_logger.debug("Azure DI returned 202 Accepted, polling operation...") + if raw_response.status_code != 202: + return self._transform_completed_response(model=model, raw_response=raw_response) - # Get Operation-Location header - operation_url = raw_response.headers.get("Operation-Location") - if not operation_url: - raise ValueError("Azure Document Intelligence returned 202 but no Operation-Location header found") - - # Reject cross-origin polling URLs — the auth headers - # below would otherwise leak to whatever URL the upstream - # (or an attacker-controlled upstream) returns. VERIA-51. - try: - assert_same_origin(operation_url, str(raw_response.request.url)) - except SSRFError as ssrf_err: - raise ValueError(f"Azure Document Intelligence: rejected polling URL ({ssrf_err})") - - # Get headers for polling (need auth) - poll_headers = { - "Ocp-Apim-Subscription-Key": raw_response.request.headers.get("Ocp-Apim-Subscription-Key", "") - } - - # Get timeout from kwargs or use default - timeout_secs = AZURE_OPERATION_POLLING_TIMEOUT - - # Poll until operation completes - raw_response = self._poll_operation_sync( - operation_url=operation_url, - headers=poll_headers, - timeout_secs=timeout_secs, - ) - - # Now parse the completed response - response_json = raw_response.json() - - verbose_logger.debug(f"Azure Document Intelligence response status: {response_json.get('status')}") - - # Check if request succeeded - status = response_json.get("status") - if status != "succeeded": - raise ValueError(f"Azure Document Intelligence analysis failed with status: {status}") - - # Extract analyze result - analyze_result = response_json.get("analyzeResult", {}) - azure_pages = analyze_result.get("pages", []) - - # Transform pages to Mistral format - mistral_pages = [] - for azure_page in azure_pages: - page_number = azure_page.get("pageNumber", 1) - index = page_number - 1 # Convert to 0-based index - - # Extract markdown text - markdown = self._extract_page_markdown(azure_page) - - # Convert dimensions - width = azure_page.get("width", 8.5) - height = azure_page.get("height", 11) - unit = azure_page.get("unit", "inch") - dimensions = self._convert_dimensions(width=width, height=height, unit=unit) - - # Build OCR page - ocr_page = OCRPage(index=index, markdown=markdown, dimensions=dimensions) - mistral_pages.append(ocr_page) - - # Build usage info - usage_info = OCRUsageInfo(pages_processed=len(mistral_pages), doc_size_bytes=None) - - # Return Mistral OCR response - return OCRResponse( - pages=mistral_pages, - model=model, - usage_info=usage_info, - object="ocr", - ) - - except Exception as e: - verbose_logger.error(f"Error parsing Azure Document Intelligence response: {e}") - raise e + verbose_logger.debug("Azure DI returned 202 Accepted, polling operation...") + operation_url, poll_headers = self._get_polling_target(raw_response) + completed_response = self._poll_operation_sync( + operation_url=operation_url, + headers=poll_headers, + timeout_secs=AZURE_OPERATION_POLLING_TIMEOUT, + ) + return self._transform_completed_response(model=model, raw_response=completed_response) async def async_transform_ocr_response( self, @@ -680,81 +715,14 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig): Returns: OCRResponse in Mistral format """ - try: - # Check if we got 202 Accepted (async operation started) - if raw_response.status_code == 202: - verbose_logger.debug("Azure DI returned 202 Accepted, polling operation (async)...") + if raw_response.status_code != 202: + return self._transform_completed_response(model=model, raw_response=raw_response) - # Get Operation-Location header - operation_url = raw_response.headers.get("Operation-Location") - if not operation_url: - raise ValueError("Azure Document Intelligence returned 202 but no Operation-Location header found") - - # Reject cross-origin polling URLs (see sync path). VERIA-51. - try: - assert_same_origin(operation_url, str(raw_response.request.url)) - except SSRFError as ssrf_err: - raise ValueError(f"Azure Document Intelligence: rejected polling URL ({ssrf_err})") - - # Get headers for polling (need auth) - poll_headers = { - "Ocp-Apim-Subscription-Key": raw_response.request.headers.get("Ocp-Apim-Subscription-Key", "") - } - - # Get timeout from kwargs or use default - timeout_secs = AZURE_OPERATION_POLLING_TIMEOUT - - # Poll until operation completes (async) - raw_response = await self._poll_operation_async( - operation_url=operation_url, - headers=poll_headers, - timeout_secs=timeout_secs, - ) - - # Now parse the completed response - response_json = raw_response.json() - - verbose_logger.debug(f"Azure Document Intelligence response status: {response_json.get('status')}") - - # Check if request succeeded - status = response_json.get("status") - if status != "succeeded": - raise ValueError(f"Azure Document Intelligence analysis failed with status: {status}") - - # Extract analyze result - analyze_result = response_json.get("analyzeResult", {}) - azure_pages = analyze_result.get("pages", []) - - # Transform pages to Mistral format - mistral_pages = [] - for azure_page in azure_pages: - page_number = azure_page.get("pageNumber", 1) - index = page_number - 1 # Convert to 0-based index - - # Extract markdown text - markdown = self._extract_page_markdown(azure_page) - - # Convert dimensions - width = azure_page.get("width", 8.5) - height = azure_page.get("height", 11) - unit = azure_page.get("unit", "inch") - dimensions = self._convert_dimensions(width=width, height=height, unit=unit) - - # Build OCR page - ocr_page = OCRPage(index=index, markdown=markdown, dimensions=dimensions) - mistral_pages.append(ocr_page) - - # Build usage info - usage_info = OCRUsageInfo(pages_processed=len(mistral_pages), doc_size_bytes=None) - - # Return Mistral OCR response - return OCRResponse( - pages=mistral_pages, - model=model, - usage_info=usage_info, - object="ocr", - ) - - except Exception as e: - verbose_logger.error(f"Error parsing Azure Document Intelligence response (async): {e}") - raise e + verbose_logger.debug("Azure DI returned 202 Accepted, polling operation (async)...") + operation_url, poll_headers = self._get_polling_target(raw_response) + completed_response = await self._poll_operation_async( + operation_url=operation_url, + headers=poll_headers, + timeout_secs=AZURE_OPERATION_POLLING_TIMEOUT, + ) + return self._transform_completed_response(model=model, raw_response=completed_response) diff --git a/litellm/llms/base_llm/anthropic_messages/transformation.py b/litellm/llms/base_llm/anthropic_messages/transformation.py index 7f8403c0223..448c1d07009 100644 --- a/litellm/llms/base_llm/anthropic_messages/transformation.py +++ b/litellm/llms/base_llm/anthropic_messages/transformation.py @@ -103,6 +103,30 @@ class BaseAnthropicMessagesConfig(ABC): """ return headers, None + def should_filter_anthropic_beta_headers(self) -> bool: + """ + Whether ``anthropic-beta`` header values should be filtered down to the + ones the routed provider supports before the upstream request. + + Cross-provider translation paths (bedrock, vertex_ai, ...) need this so + unsupported betas are dropped. Configs that forward natively to an + Anthropic-compatible endpoint return False to pass betas through verbatim. + """ + return True + + def handles_web_search_natively(self) -> bool: + """ + Whether the upstream this config routes to executes ``web_search`` tools + itself as part of its Anthropic Messages agentic loop. + + The web-search interception handler short-circuits web-search-only + requests (running the search itself and returning synthetic results) only + for providers that do NOT. Providers whose agentic loop already performs + the search plus a follow-up synthesis step (bedrock, vertex_ai, ...) + return True so those requests flow through untouched. + """ + return True + def get_async_streaming_response_iterator( self, model: str, diff --git a/litellm/llms/base_llm/guardrail_translation/base_translation.py b/litellm/llms/base_llm/guardrail_translation/base_translation.py index 68db36b529e..6c41b46cfa0 100644 --- a/litellm/llms/base_llm/guardrail_translation/base_translation.py +++ b/litellm/llms/base_llm/guardrail_translation/base_translation.py @@ -2,7 +2,10 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, List, Optional if TYPE_CHECKING: - from litellm.integrations.custom_guardrail import CustomGuardrail + from litellm.integrations.custom_guardrail import ( + CustomGuardrail, + ModifyResponseException, + ) from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.proxy._types import UserAPIKeyAuth from litellm.types.llms.openai import AllMessageValues @@ -98,6 +101,30 @@ class BaseTranslation(ABC): """ return responses_so_far + def build_block_sse_chunks( + self, + exc: "ModifyResponseException", + stream_started: bool = False, + responses_so_far: Optional[list[Any]] = None, + ) -> Optional[list[bytes]]: + """ + Build the streaming chunks that deliver a guardrail block message and + cleanly terminate the stream in this provider's wire format. + + ``stream_started`` is True when real chunks were already sent to the + client: the result must *continue* the in-progress message (e.g. close + the open content block and append the block message) rather than start + a new one, which clients reject. ``responses_so_far`` provides the prior + chunks needed to do so. When False, nothing has been sent and a + standalone block message is emitted. + + Returns None when the format has no safe terminator; the caller then + re-raises ``exc`` so the proxy can surface a clean error instead. + Override in provider subclasses that support synthesizing a block + stream. + """ + return None + def get_structured_messages(self, data: dict) -> Optional[List["AllMessageValues"]]: """ Convert request data to OpenAI-spec structured messages. diff --git a/litellm/llms/base_llm/guardrail_translation/utils.py b/litellm/llms/base_llm/guardrail_translation/utils.py index 97ece6b5eab..8a06dd4ea52 100644 --- a/litellm/llms/base_llm/guardrail_translation/utils.py +++ b/litellm/llms/base_llm/guardrail_translation/utils.py @@ -1,10 +1,100 @@ from __future__ import annotations -from typing import Any, List +import json +from typing import Any, List, Optional +from litellm.types.llms.anthropic_messages.anthropic_response import AnthropicUsage from litellm.types.llms.openai import AllMessageValues +def _anthropic_stream_chunk_events(item: Any) -> list[dict]: + if isinstance(item, dict): + return [item] + if isinstance(item, bytes): + chunk = item.decode("utf-8", errors="replace") + elif isinstance(item, str): + chunk = item + else: + return [] + + events: list[dict] = [] + for block in chunk.split("\n\n"): + for line in block.splitlines(): + stripped = line.strip() + if not stripped.startswith("data:"): + continue + payload = stripped[len("data:") :].strip() + if not payload or payload == "[DONE]": + continue + try: + parsed = json.loads(payload) + except json.JSONDecodeError: + continue + if isinstance(parsed, dict): + events.append(parsed) + return events + + +def _usage_from_anthropic_stream_chunks(original_response: list[Any]) -> Optional[AnthropicUsage]: + input_tokens = 0 + output_tokens = 0 + found_usage = False + + for item in original_response: + for event in _anthropic_stream_chunk_events(item): + event_type = event.get("type") + if event_type == "message_start": + message = event.get("message") or {} + usage_obj = message.get("usage") or {} + elif event_type == "message_delta": + usage_obj = event.get("usage") or {} + else: + usage_obj = {} + if not isinstance(usage_obj, dict): + continue + if usage_obj.get("input_tokens") is not None: + input_tokens = int(usage_obj.get("input_tokens") or 0) + found_usage = True + if usage_obj.get("output_tokens") is not None: + output_tokens = int(usage_obj.get("output_tokens") or 0) + found_usage = True + + if not found_usage: + return None + return AnthropicUsage(input_tokens=input_tokens, output_tokens=output_tokens) + + +def blocked_response_usage(original_response: Optional[Any]) -> AnthropicUsage: + """ + Token usage for a synthetic guardrail-blocked response. + + A post-call block replaces the LLM's response with the violation message, + but the upstream call already consumed tokens -- report that real usage + (carried on ``ModifyResponseException.original_response``) rather than + discarding it. Pre-call blocks never invoked the LLM (no original_response), + so usage is zero. + """ + usage_obj: Any = None + if isinstance(original_response, list): + stream_usage = _usage_from_anthropic_stream_chunks(original_response) + if stream_usage is not None: + return stream_usage + elif isinstance(original_response, dict): + usage_obj = original_response.get("usage") + elif original_response is not None: + usage_obj = getattr(original_response, "usage", None) + + def _tokens(key: str, fallback_key: str) -> int: + if isinstance(usage_obj, dict): + return int(usage_obj.get(key, usage_obj.get(fallback_key, 0)) or 0) + return int(getattr(usage_obj, key, getattr(usage_obj, fallback_key, 0)) or 0) + + return AnthropicUsage( + input_tokens=_tokens("input_tokens", "prompt_tokens"), + output_tokens=_tokens("output_tokens", "completion_tokens"), + ) + + def effective_skip_system_message_for_guardrail(guardrail_to_apply: Any) -> bool: per = getattr(guardrail_to_apply, "skip_system_message_in_guardrail", None) if per is not None: diff --git a/litellm/llms/base_llm/ocr/transformation.py b/litellm/llms/base_llm/ocr/transformation.py index a38e5bfdcd6..0d878bd308c 100644 --- a/litellm/llms/base_llm/ocr/transformation.py +++ b/litellm/llms/base_llm/ocr/transformation.py @@ -70,6 +70,9 @@ class OCRResponse(LiteLLMPydanticObjectBase): model: str document_annotation: Any | None = None usage_info: OCRUsageInfo | None = None + content: str | None = None + tables: list[dict[str, object]] | None = None + keyValuePairs: list[dict[str, object]] | None = None object: str = "ocr" model_config = {"extra": "allow"} diff --git a/litellm/llms/bedrock/base_aws_llm.py b/litellm/llms/bedrock/base_aws_llm.py index 380cc91ed98..f449851b76f 100644 --- a/litellm/llms/bedrock/base_aws_llm.py +++ b/litellm/llms/bedrock/base_aws_llm.py @@ -50,6 +50,8 @@ _STS_REGION_FROM_ENDPOINT_PATTERN = re.compile( r"(?:^|\.)sts(?:-fips)?\.([a-z0-9-]+)\.(?:amazonaws\.com(?:\.cn)?|vpce\.amazonaws\.com)" ) +SIGV4_COMPUTED_HEADERS = frozenset({"authorization", "x-amz-date", "x-amz-security-token", "date"}) + class Boto3CredentialsInfo(BaseModel): credentials: Credentials @@ -1400,11 +1402,13 @@ class BaseAWSLLM: # Add back all original headers (including forwarded ones) after signature calculation for header_name, header_value in headers.items(): - if header_value is not None: + if header_value is not None and header_name.lower() not in SIGV4_COMPUTED_HEADERS: request.headers[header_name] = header_value if ( - extra_headers is not None and "Authorization" in extra_headers + extra_headers is not None + and "Authorization" in extra_headers + and not extra_headers["Authorization"].startswith("AWS4-HMAC-SHA256") ): # prevent sigv4 from overwriting the auth header request.headers["Authorization"] = extra_headers["Authorization"] prepped = request.prepare() @@ -1527,9 +1531,15 @@ class BaseAWSLLM: # Add back original headers after signing. Only headers in SignedHeaders # are integrity-protected; forwarded headers (x-forwarded-*) must remain unsigned. for header_name, header_value in headers.items(): - if header_value is not None: + if header_value is not None and header_name.lower() not in SIGV4_COMPUTED_HEADERS: request_headers_dict[header_name] = header_value - if headers is not None and "Authorization" in headers: # prevent sigv4 from overwriting the auth header - request_headers_dict["Authorization"] = headers["Authorization"] + incoming_authorization = next( + (value for name, value in headers.items() if name.lower() == "authorization" and value is not None), + None, + ) + if incoming_authorization is not None and not incoming_authorization.startswith( + "AWS4-HMAC-SHA256" + ): # prevent sigv4 from overwriting the auth header + request_headers_dict["Authorization"] = incoming_authorization return request_headers_dict, request.body diff --git a/litellm/llms/bedrock/chat/converse_transformation.py b/litellm/llms/bedrock/chat/converse_transformation.py index b135a116753..c38b3593465 100644 --- a/litellm/llms/bedrock/chat/converse_transformation.py +++ b/litellm/llms/bedrock/chat/converse_transformation.py @@ -33,6 +33,7 @@ from litellm.litellm_core_utils.prompt_templates.factory import ( make_valid_bedrock_tool_name, ) from litellm.llms.anthropic.chat.transformation import ( + DROP_UNSUPPORTED_ADAPTIVE_THINKING_WARNING, DROP_UNSUPPORTED_OUTPUT_CONFIG_WARNING, REASONING_EFFORT_TO_OUTPUT_CONFIG_EFFORT, AnthropicConfig, @@ -76,6 +77,7 @@ from litellm.utils import ( from ..common_utils import ( BedrockError, BedrockModelInfo, + bedrock_converse_supports_parallel_tool_use_config, get_anthropic_beta_from_headers, get_bedrock_tool_name, is_claude_4_5_on_bedrock, @@ -422,6 +424,7 @@ class AmazonConverseConfig(BaseConfig): mapped_thinking = AnthropicConfig._map_reasoning_effort( reasoning_effort=reasoning_effort, model=model, + custom_llm_provider="bedrock", llm_provider="bedrock_converse", ) if mapped_thinking is None: @@ -429,7 +432,7 @@ class AmazonConverseConfig(BaseConfig): optional_params.pop("output_config", None) else: optional_params["thinking"] = mapped_thinking - if AnthropicConfig._is_adaptive_thinking_model(model): + if AnthropicConfig._is_adaptive_thinking_model(model, "bedrock"): mapped_effort = REASONING_EFFORT_TO_OUTPUT_CONFIG_EFFORT.get(reasoning_effort) if mapped_effort is None: AnthropicConfig._raise_invalid_reasoning_effort( @@ -464,7 +467,7 @@ class AmazonConverseConfig(BaseConfig): model=model, llm_provider="bedrock_converse", ) - error = AnthropicConfig._validate_effort_for_model(model=model, effort=effort) + error = AnthropicConfig._validate_effort_for_model(model=model, effort=effort, custom_llm_provider="bedrock") if error is not None: raise litellm.exceptions.BadRequestError( message=error, @@ -897,7 +900,28 @@ class AmazonConverseConfig(BaseConfig): "tool_choice": {"disable_parallel_tool_use": disable_parallel} } if param == "thinking": - optional_params["thinking"] = value + if ( + isinstance(value, dict) + and value.get("type") == "adaptive" + and not AnthropicConfig._is_adaptive_thinking_model(model, "bedrock") + ): + max_tokens = non_default_params.get("max_completion_tokens") or non_default_params.get("max_tokens") + legacy_thinking = AnthropicConfig._map_reasoning_effort( + reasoning_effort="medium", + model=model, + custom_llm_provider="bedrock", + ) + capped = ( + AnthropicConfig._cap_thinking_budget_to_max_tokens(legacy_thinking, max_tokens) + if legacy_thinking is not None + else None + ) + if capped is not None: + optional_params["thinking"] = capped + else: + litellm.verbose_logger.warning(DROP_UNSUPPORTED_ADAPTIVE_THINKING_WARNING, model) + else: + optional_params["thinking"] = value elif param == "reasoning_effort" and isinstance(value, str): self._handle_reasoning_effort_parameter( model=model, reasoning_effort=value, optional_params=optional_params @@ -1106,18 +1130,28 @@ class AmazonConverseConfig(BaseConfig): if cache_control is None: return None - cache_point = CachePointBlock(type="default") - if isinstance(cache_control, dict) and "ttl" in cache_control: - ttl = cache_control["ttl"] - if ttl in ["5m", "1h"] and model is not None: - if is_claude_4_5_on_bedrock(model): - cache_point["ttl"] = ttl + cache_point = self._build_cache_point_block(cache_control, model) if block_type == "system": return SystemContentBlock(cachePoint=cache_point) else: return ContentBlock(cachePoint=cache_point) + @staticmethod + def _build_cache_point_block(control: Optional[dict], model: Optional[str] = None) -> CachePointBlock: + """Build a Bedrock ``cachePoint`` block from an OpenAI-style ``cache_control``/``control`` dict. + + ``type`` is always ``"default"`` (the only value Bedrock's Converse API + accepts). ``ttl`` is only honored for models that support extended TTL + caching (Claude 4.5 family on Bedrock). + """ + cache_point = CachePointBlock(type="default") + if isinstance(control, dict) and "ttl" in control: + ttl = control["ttl"] + if ttl in ["5m", "1h"] and model is not None and is_claude_4_5_on_bedrock(model): + cache_point["ttl"] = ttl + return cache_point + def _transform_system_message( self, messages: List[AllMessageValues], model: Optional[str] = None ) -> Tuple[List[AllMessageValues], List[SystemContentBlock]]: @@ -1241,7 +1275,7 @@ class AmazonConverseConfig(BaseConfig): # Handle parallel_tool_calls configuration parallel_tool_use_config = additional_request_params.pop("_parallel_tool_use_config", None) - if parallel_tool_use_config is not None and is_claude_4_5_on_bedrock(model): + if parallel_tool_use_config is not None and bedrock_converse_supports_parallel_tool_use_config(model): for key, value in parallel_tool_use_config.items(): if ( key in additional_request_params @@ -1268,7 +1302,7 @@ class AmazonConverseConfig(BaseConfig): if anthropic_output_config is not None and isinstance(anthropic_output_config, dict): if base_model.startswith("anthropic"): - if litellm.drop_params is True and not AnthropicConfig._model_supports_effort_param(model): + if litellm.drop_params is True and not AnthropicConfig._model_supports_effort_param(model, "bedrock"): litellm.verbose_logger.warning( DROP_UNSUPPORTED_OUTPUT_CONFIG_WARNING, model, @@ -1411,7 +1445,7 @@ class AmazonConverseConfig(BaseConfig): if ( isinstance(output_config, dict) and output_config.get("effort") is not None - and not AnthropicConfig._is_adaptive_thinking_model(model) + and not AnthropicConfig._is_adaptive_thinking_model(model, "bedrock") ): from litellm.types.llms.anthropic import ( ANTHROPIC_EFFORT_BETA_HEADER, @@ -1526,7 +1560,8 @@ class AmazonConverseConfig(BaseConfig): if cache_injection_points and len(bedrock_tools) > 0: for point in cache_injection_points: if point.get("location") == "tool_config": - bedrock_tools.append({"cachePoint": {"type": "default"}}) + cache_point = self._build_cache_point_block(point.get("control"), model) + bedrock_tools.append(ToolBlock(cachePoint=cache_point)) break bedrock_tool_config: Optional[ToolConfigBlock] = None diff --git a/litellm/llms/bedrock/chat/invoke_handler.py b/litellm/llms/bedrock/chat/invoke_handler.py index b381b5a85fe..4c256be1ab8 100644 --- a/litellm/llms/bedrock/chat/invoke_handler.py +++ b/litellm/llms/bedrock/chat/invoke_handler.py @@ -1558,7 +1558,7 @@ class AWSEventStreamDecoder: text = chunk_data["outputText"] # ai21 mapping elif "ai21" in self.model: # fake ai21 streaming - text = chunk_data.get("completions")[0].get("data").get("text") # type: ignore + text = chunk_data["completions"][0]["data"]["text"] is_finished = True finish_reason = "stop" ######## /bedrock/converse mappings ############### diff --git a/litellm/llms/bedrock/chat/invoke_transformations/amazon_qwen2_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/amazon_qwen2_transformation.py index d63642c806f..a2aa98d6676 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/amazon_qwen2_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/amazon_qwen2_transformation.py @@ -51,10 +51,7 @@ class AmazonQwen2Config(AmazonQwen3Config): Qwen2 uses "text" field, but we also support "generation" field for compatibility. """ try: - if hasattr(raw_response, "json"): - response_data = raw_response.json() - else: - response_data = raw_response + response_data = raw_response.json() # Extract the generated text - Qwen2 uses "text" field, but also support "generation" for compatibility generated_text = response_data.get("generation", "") or response_data.get("text", "") diff --git a/litellm/llms/bedrock/chat/invoke_transformations/amazon_qwen3_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/amazon_qwen3_transformation.py index 762631cac5e..4f496df084e 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/amazon_qwen3_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/amazon_qwen3_transformation.py @@ -175,10 +175,7 @@ class AmazonQwen3Config(AmazonInvokeConfig, BaseConfig): Transform Qwen3 Bedrock response to OpenAI format """ try: - if hasattr(raw_response, "json"): - response_data = raw_response.json() - else: - response_data = raw_response + response_data = raw_response.json() # Extract the generated text - Qwen3 uses "generation" field generated_text = response_data.get("generation", "") diff --git a/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py index 60d532eb8c5..6b5cb304bec 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py @@ -115,7 +115,7 @@ class AmazonAnthropicClaudeConfig(AmazonInvokeConfig, AnthropicConfig): keeps working. Non-adaptive models and models without a ceiling are left untouched. """ - if not AnthropicConfig._is_adaptive_thinking_model(model): + if not AnthropicConfig._is_adaptive_thinking_model(model, "bedrock"): return effort = params.get("reasoning_effort") if not isinstance(effort, str): @@ -228,7 +228,7 @@ class AmazonAnthropicClaudeConfig(AmazonInvokeConfig, AnthropicConfig): custom_llm_provider="bedrock", key="supports_output_config", ) - or AnthropicConfig._model_supports_effort_param(model) + or AnthropicConfig._model_supports_effort_param(model, "bedrock") ): if anthropic_request.pop("output_config", None) is not None: verbose_logger.warning( @@ -269,6 +269,7 @@ class AmazonAnthropicClaudeConfig(AmazonInvokeConfig, AnthropicConfig): prompt_caching_set=False, file_id_used=self.is_file_id_used(messages), mcp_server_used=self.is_mcp_server_used(optional_params.get("mcp_servers")), + custom_llm_provider="bedrock", ) beta_set.update(auto_betas) diff --git a/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py index bbe16e26713..dd7cf12604d 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py @@ -5,6 +5,7 @@ from functools import partial from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union, cast, get_args import httpx +from pydantic import TypeAdapter, ValidationError import litellm from litellm._logging import verbose_logger @@ -24,6 +25,7 @@ from litellm.llms.custom_httpx.http_handler import ( HTTPHandler, _get_httpx_client, ) +from litellm.types.llms.bedrock import GuardrailConfigBlock from litellm.types.llms.openai import AllMessageValues from litellm.types.utils import ModelResponse, Usage from litellm.utils import CustomStreamWrapper @@ -37,6 +39,38 @@ else: from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM +_GUARDRAIL_CONFIG_VALIDATOR: "TypeAdapter[GuardrailConfigBlock]" = TypeAdapter(GuardrailConfigBlock) + +_GUARDRAIL_CONFIG_EXPECTED_FORMAT = ( + "{'guardrailIdentifier': str, 'guardrailVersion': str, 'trace': 'enabled'|'disabled'|'enabled_full'}" +) + + +def _bedrock_invoke_guardrail_headers(raw_guardrail_config: object) -> "dict[str, str]": + try: + guardrail_config = _GUARDRAIL_CONFIG_VALIDATOR.validate_python(raw_guardrail_config) + except ValidationError as e: + raise BedrockError( + status_code=400, + message="Invalid guardrailConfig={}. Expected format: {}. Error: {}".format( + raw_guardrail_config, _GUARDRAIL_CONFIG_EXPECTED_FORMAT, e + ), + ) + if "guardrailIdentifier" not in guardrail_config: + raise BedrockError( + status_code=400, + message="guardrailConfig={} is missing 'guardrailIdentifier'. Expected format: {}".format( + raw_guardrail_config, _GUARDRAIL_CONFIG_EXPECTED_FORMAT + ), + ) + trace = guardrail_config.get("trace") + candidate_headers = { + "X-Amzn-Bedrock-GuardrailIdentifier": guardrail_config.get("guardrailIdentifier"), + "X-Amzn-Bedrock-GuardrailVersion": guardrail_config.get("guardrailVersion"), + "X-Amzn-Bedrock-Trace": trace.upper() if trace is not None else None, + } + return {name: value for name, value in candidate_headers.items() if value is not None} + class AmazonInvokeConfig(BaseConfig, BaseAWSLLM): def __init__(self, **kwargs): @@ -390,7 +424,16 @@ class AmazonInvokeConfig(BaseConfig, BaseAWSLLM): api_key: Optional[str] = None, api_base: Optional[str] = None, ) -> dict: - return headers + raw_guardrail_config = optional_params.pop("guardrailConfig", None) + if raw_guardrail_config is None: + return headers + existing_header_names = frozenset(name.lower() for name in headers) + guardrail_headers = { + name: value + for name, value in _bedrock_invoke_guardrail_headers(raw_guardrail_config).items() + if name.lower() not in existing_header_names + } + return {**headers, **guardrail_headers} def get_error_class( self, error_message: str, status_code: int, headers: Union[dict, httpx.Headers] diff --git a/litellm/llms/bedrock/chat/mantle/transformation.py b/litellm/llms/bedrock/chat/mantle/transformation.py index d84e077c37b..d7ffff65ff0 100644 --- a/litellm/llms/bedrock/chat/mantle/transformation.py +++ b/litellm/llms/bedrock/chat/mantle/transformation.py @@ -7,13 +7,14 @@ The bedrock-mantle endpoint uses the Anthropic Messages API format but is served at a different endpoint (bedrock-mantle.{region}.api.aws) with AWS SigV4 auth. """ -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Any, AsyncIterator, Iterator, List, Optional from litellm.llms.bedrock.chat.invoke_transformations.anthropic_claude3_transformation import ( AmazonAnthropicClaudeConfig, ) from litellm.llms.bedrock.common_utils import build_mantle_messages_url from litellm.types.llms.openai import AllMessageValues +from litellm.types.utils import ModelResponse if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj @@ -91,10 +92,14 @@ class AmazonMantleConfig(AmazonAnthropicClaudeConfig): litellm_params=litellm_params, headers=headers, ) - # The parent strips "model" from the body (Invoke API puts it in URL). - # The mantle endpoint (Messages API) requires "model" in the body. - request["model"] = model_id - return request + # The parent strips "model" and "stream" from the body (Invoke API puts + # the model in the URL and streams via a dedicated endpoint). The mantle + # endpoint (Messages API) requires both in the body. + return self._restore_mantle_body_fields( + request=request, + model_id=model_id, + optional_params=optional_params, + ) async def async_transform_request( self, @@ -114,5 +119,31 @@ class AmazonMantleConfig(AmazonAnthropicClaudeConfig): headers=headers, ) await self._async_convert_document_url_sources_to_base64(request) - request["model"] = model_id - return request + return self._restore_mantle_body_fields( + request=request, + model_id=model_id, + optional_params=optional_params, + ) + + @staticmethod + def _restore_mantle_body_fields(request: dict, model_id: str, optional_params: dict) -> dict: + stream_fields: dict = {"stream": True} if optional_params.get("stream") is True else {} + return {**request, "model": model_id, **stream_fields} + + @property + def has_custom_stream_wrapper(self) -> bool: + return False + + def get_model_response_iterator( + self, + streaming_response: Iterator[str] | AsyncIterator[str] | ModelResponse, + sync_stream: bool, + json_mode: Optional[bool] = False, + ) -> Any: + from litellm.llms.anthropic.chat.handler import ModelResponseIterator + + return ModelResponseIterator( + streaming_response=streaming_response, + sync_stream=sync_stream, + json_mode=json_mode, + ) diff --git a/litellm/llms/bedrock/claude_platform/transformation.py b/litellm/llms/bedrock/claude_platform/transformation.py index 0868d9bddfe..6f5ccececc7 100644 --- a/litellm/llms/bedrock/claude_platform/transformation.py +++ b/litellm/llms/bedrock/claude_platform/transformation.py @@ -54,7 +54,9 @@ class BedrockClaudePlatformConfig(BedrockClaudePlatformMixin, AnthropicConfig): tool_search_used=self.is_tool_search_used(tools=optional_params.get("tools")), programmatic_tool_calling_used=self.is_programmatic_tool_calling_used(tools=optional_params.get("tools")), input_examples_used=self.is_input_examples_used(tools=optional_params.get("tools")), - effort_used=self.is_effort_used(optional_params=optional_params, model=model), + effort_used=self.is_effort_used( + optional_params=optional_params, model=model, custom_llm_provider="anthropic" + ), user_anthropic_beta_headers=self._get_user_anthropic_beta_headers( anthropic_beta_header=headers.get("anthropic-beta") ), diff --git a/litellm/llms/bedrock/common_utils.py b/litellm/llms/bedrock/common_utils.py index 467e1050c99..5114677ffc0 100644 --- a/litellm/llms/bedrock/common_utils.py +++ b/litellm/llms/bedrock/common_utils.py @@ -4,9 +4,11 @@ from __future__ import annotations Common utilities used across bedrock chat/embedding/image generation """ +import contextlib import functools import json import os +import re from typing import ( TYPE_CHECKING, Any, @@ -683,39 +685,72 @@ def get_bedrock_base_model(model: str) -> str: return model +def bedrock_converse_supports_parallel_tool_use_config(model: str) -> bool: + return any( + (litellm.model_cost.get(candidate) or {}).get("supports_parallel_tool_use_config") is True + for candidate in (model, get_bedrock_base_model(model)) + ) + + def is_claude_4_5_on_bedrock(model: str) -> bool: """ - Check if the model is a Claude 4.5 model on Bedrock. - Claude 4.5 models support prompt caching with '5m' and '1h' TTL on Bedrock. + Check if the model supports Bedrock prompt caching with an extended '1h' TTL + (in addition to the default 5m TTL). + + Backed by the ``cache_creation_input_token_cost_above_1hr`` field in + ``model_prices_and_context_window.json`` instead of a hardcoded list of + model-name patterns, so newly released models pick up support as soon as + their pricing entry ships, with no code change required here. """ - model_lower = model.lower() - claude_4_5_patterns = [ - "sonnet-4.5", - "sonnet_4.5", - "sonnet-4-5", - "sonnet_4_5", - "haiku-4.5", - "haiku_4.5", - "haiku-4-5", - "haiku_4_5", - "opus-4.5", - "opus_4.5", - "opus-4-5", - "opus_4_5", - "sonnet-4.6", - "sonnet_4.6", - "sonnet-4-6", - "sonnet_4_6", - "opus-4.6", - "opus_4.6", - "opus-4-6", - "opus_4_6", - "opus-4.7", - "opus_4.7", - "opus-4-7", - "opus_4_7", - ] - return any(pattern in model_lower for pattern in claude_4_5_patterns) + return any( + (litellm.model_cost.get(candidate) or {}).get("cache_creation_input_token_cost_above_1hr") is not None + for candidate in (model, get_bedrock_base_model(model)) + ) + + +_BEDROCK_MODEL_VERSION_SUFFIX_RE = re.compile(r"-v\d+(?::\d+)?$") + + +def bedrock_converse_supports_strict_tools(model: str) -> bool: + """ + Whether ``toolSpec.strict`` can be forwarded to Bedrock Converse for ``model``. + + Non-Anthropic Bedrock families (Nova, Llama, GPT-OSS) reject the field + outright. Anthropic models forward it unless their entry in + ``model_prices_and_context_window.json`` sets + ``bedrock_converse_supports_strict_tools: false`` — Bedrock routes those + (Opus 4.7/4.8, see #31582) through a stricter validator that rejects the + ``strict`` key on ``toolSpec`` even though Anthropic's native API accepts + it as a top-level tool field. + """ + base = get_bedrock_base_model(model) + if not base.startswith("anthropic"): + return False + flag = _get_bedrock_converse_strict_tools_flag(base) + return flag if flag is not None else True + + +def _get_bedrock_converse_strict_tools_flag(base_model: str) -> Optional[bool]: + candidates = dict.fromkeys((base_model, _BEDROCK_MODEL_VERSION_SUFFIX_RE.sub("", base_model))) + for candidate in candidates: + with contextlib.suppress(Exception): + model_info = get_cached_model_info()( + model=candidate, + custom_llm_provider="bedrock", + ) + + flag = model_info.get("bedrock_converse_supports_strict_tools") + if isinstance(flag, bool): + return flag + + model_cost_key = model_info.get("key") + if isinstance(model_cost_key, str): + local_flag = ( + _get_local_model_cost_map().get(model_cost_key, {}).get("bedrock_converse_supports_strict_tools") + ) + if isinstance(local_flag, bool): + return local_flag + return None def normalize_bedrock_opus_output_config_effort(model: str, output_config: Any) -> None: diff --git a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py index f5309d521a9..a00d3ba1363 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -77,6 +77,10 @@ class AmazonAnthropicClaudeMessagesConfig( DEFAULT_BEDROCK_ANTHROPIC_API_VERSION = "bedrock-2023-05-31" + @property + def custom_llm_provider(self) -> Optional[str]: + return "bedrock" + BEDROCK_INVOKE_ALLOWED_TOP_LEVEL_FIELDS = frozenset(BedrockInvokeAnthropicMessagesRequest.__annotations__.keys()) def __init__(self, **kwargs): @@ -93,26 +97,48 @@ class AmazonAnthropicClaudeMessagesConfig( return [{"type": "text", "text": value}] return [value] - def _normalize_system_role_messages_for_bedrock(self, anthropic_messages_request: dict) -> None: - """Bedrock Invoke rejects ``role: "system"`` entries inside ``messages`` on - some Claude aliases; Anthropic Messages carries that content in the - top-level ``system`` field. Move any such entries into ``system`` before - the Invoke request is built.""" + @staticmethod + def _is_system_role_message(message: Any) -> bool: + return isinstance(message, dict) and message.get("role") == "system" + + def _normalize_system_role_messages_for_bedrock(self, anthropic_messages_request: dict, model: str) -> None: + """Bedrock Invoke validates ``role: "system"`` entries inside ``messages`` + per model. Models carrying ``supports_mid_conversation_system`` in the + cost map (the Opus 4.8 family) only reject a leading run ("messages.0: + use the top-level 'system' parameter for the initial system prompt") and + accept mid-conversation entries (e.g. Claude Code's + ``mid-conversation-system-2026-04-07`` reminders) in place, where they + MUST stay: hoisting one mutates the ``system`` prefix and invalidates the + prompt cache for the entire message history. Older Claude models (Opus + 4.7, Sonnet 4.6, Haiku 4.5, ...) reject the role in every position + ("role 'system' is not supported on this model"), so without the flag + every system entry is hoisted into the top-level ``system`` field. + Billing-header system blocks are stripped from the top-level ``system`` + field regardless of whether anything was hoisted.""" messages = anthropic_messages_request.get("messages") if not isinstance(messages, list): return - system_role_messages = [m for m in messages if isinstance(m, dict) and m.get("role") == "system"] - if not system_role_messages: - return - - anthropic_messages_request["messages"] = [ - m for m in messages if not (isinstance(m, dict) and m.get("role") == "system") - ] + if _supports_factory( + model=model, + custom_llm_provider="bedrock", + key="supports_mid_conversation_system", + ): + leading_count = next( + (i for i, m in enumerate(messages) if not self._is_system_role_message(m)), + len(messages), + ) + hoisted = messages[:leading_count] + remaining = messages[leading_count:] + else: + hoisted = [m for m in messages if self._is_system_role_message(m)] + remaining = [m for m in messages if not self._is_system_role_message(m)] + if hoisted: + anthropic_messages_request["messages"] = remaining system_content = [ block for source in ( anthropic_messages_request.get("system"), - *(m.get("content") for m in system_role_messages), + *(m.get("content") for m in hoisted), ) for block in self._as_system_content_blocks(source) ] @@ -247,7 +273,7 @@ class AmazonAnthropicClaudeMessagesConfig( Returns: True if the model supports extended thinking on Bedrock """ - if AnthropicModelInfo._is_adaptive_thinking_model(model): + if AnthropicModelInfo._is_adaptive_thinking_model(model, "bedrock"): return True model_lower = model.lower() @@ -297,7 +323,7 @@ class AmazonAnthropicClaudeMessagesConfig( if not self._supports_extended_thinking_on_bedrock(model): return False - is_adaptive_thinking_model = AnthropicModelInfo._is_adaptive_thinking_model(model) + is_adaptive_thinking_model = AnthropicModelInfo._is_adaptive_thinking_model(model, "bedrock") thinking = anthropic_messages_request.get("thinking") if isinstance(thinking, dict): @@ -489,24 +515,43 @@ class AmazonAnthropicClaudeMessagesConfig( if self._supports_tool_search_on_bedrock(model): beta_set.add("tool-search-tool-2025-10-19") + # Bedrock-InvokeModel-supported ``context_management.edits`` types and the + # ``anthropic-beta`` header that each one requires. ``clear_thinking_20251015`` + # is intentionally absent — it is LiteLLM-internal, consumed via + # ``_ensure_thinking_for_clear_thinking_context_management``, and forwarding + # the raw edit trips Bedrock's + # ``"context_management: Extra inputs are not permitted"`` 400. + # + # Bedrock InvokeModel DOES support ``clear_tool_uses_20250919`` under the + # ``context-management-2025-06-27`` beta. AWS docs: + # https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md + _BEDROCK_INVOKE_SUPPORTED_CONTEXT_MANAGEMENT_EDITS: Dict[str, str] = { + "compact_20260112": ANTHROPIC_BETA_HEADER_VALUES.COMPACT_2026_01_12.value, + "clear_tool_uses_20250919": ANTHROPIC_BETA_HEADER_VALUES.CONTEXT_MANAGEMENT_2025_06_27.value, + } + @staticmethod def _filter_context_management_for_bedrock_invoke( anthropic_messages_request: Dict, beta_set: set, ) -> None: """ - Bedrock InvokeModel accepts ``context_management`` only when it carries - ``compact_20260112`` edits paired with the ``compact-2026-01-12`` - anthropic-beta header. Other edit types (notably ``clear_thinking_20251015``, - which Claude Code sends on every request) are LiteLLM-internal and would - cause Bedrock to 400 with ``"context_management: Extra inputs are not - permitted"``. + Filter ``context_management.edits`` to the subset that Bedrock InvokeModel + accepts and add the matching ``anthropic-beta`` header for each surviving + edit type. - Filter the edits list to the supported subset, add the beta header when - compact edits remain, and drop ``context_management`` entirely when no - supported edits are left so the safety-net allowlist can pass it through. + - ``compact_20260112`` -> ``compact-2026-01-12`` + - ``clear_tool_uses_20250919`` -> ``context-management-2025-06-27`` - Ref: https://github.com/BerriAI/litellm/issues/27532 + Other edit types (notably ``clear_thinking_20251015``, which Claude Code + sends on every request) are LiteLLM-internal: thinking is injected + separately via ``_ensure_thinking_for_clear_thinking_context_management``, + and forwarding the raw edit would trip Bedrock's + ``"context_management: Extra inputs are not permitted"`` 400. + + Refs: + * https://github.com/BerriAI/litellm/issues/27532 + * https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md """ cm = anthropic_messages_request.get("context_management") if not isinstance(cm, dict): @@ -516,15 +561,17 @@ class AmazonAnthropicClaudeMessagesConfig( anthropic_messages_request.pop("context_management", None) return - compact_edits = [e for e in edits if isinstance(e, dict) and e.get("type") == "compact_20260112"] - if compact_edits: - beta_set.add(ANTHROPIC_BETA_HEADER_VALUES.COMPACT_2026_01_12.value) - anthropic_messages_request["context_management"] = { - **cm, - "edits": compact_edits, - } - else: + supported = AmazonAnthropicClaudeMessagesConfig._BEDROCK_INVOKE_SUPPORTED_CONTEXT_MANAGEMENT_EDITS + retained_edits = [e for e in edits if isinstance(e, dict) and e.get("type") in supported] + if not retained_edits: anthropic_messages_request.pop("context_management", None) + return + + beta_set.update(supported[e["type"]] for e in retained_edits) + anthropic_messages_request["context_management"] = { + **cm, + "edits": retained_edits, + } def _get_bedrock_invoke_anthropic_beta_headers( self, @@ -553,6 +600,7 @@ class AmazonAnthropicClaudeMessagesConfig( mcp_server_used=anthropic_model_info.is_mcp_server_used( anthropic_messages_optional_request_params.get("mcp_servers") ), + custom_llm_provider="bedrock", ) beta_set.update(auto_betas) @@ -619,7 +667,7 @@ class AmazonAnthropicClaudeMessagesConfig( path degrades ``xhigh`` -> ``max`` rather than 400-ing. Non-adaptive models and models without a ceiling are left untouched. """ - if not AnthropicModelInfo._is_adaptive_thinking_model(model): + if not AnthropicModelInfo._is_adaptive_thinking_model(model, "bedrock"): return effort = optional_params.get("reasoning_effort") if not isinstance(effort, str): @@ -648,7 +696,7 @@ class AmazonAnthropicClaudeMessagesConfig( litellm_params=litellm_params, headers=headers, ) - self._normalize_system_role_messages_for_bedrock(anthropic_messages_request) + self._normalize_system_role_messages_for_bedrock(anthropic_messages_request, model=model) ######################################################### ############## BEDROCK Invoke SPECIFIC TRANSFORMATION ### ######################################################### @@ -707,7 +755,7 @@ class AmazonAnthropicClaudeMessagesConfig( custom_llm_provider="bedrock", key="supports_output_config", ) - or AnthropicConfig._model_supports_effort_param(model) + or AnthropicConfig._model_supports_effort_param(model, "bedrock") ): if anthropic_messages_request.pop("output_config", None) is not None: verbose_logger.warning( @@ -744,7 +792,7 @@ class AmazonAnthropicClaudeMessagesConfig( if ( litellm.drop_params is True and "output_config" in anthropic_messages_request - and not AnthropicConfig._model_supports_effort_param(model) + and not AnthropicConfig._model_supports_effort_param(model, "bedrock") ): verbose_logger.warning( DROP_UNSUPPORTED_OUTPUT_CONFIG_WARNING, diff --git a/litellm/llms/bedrock/messages/mantle_transformation.py b/litellm/llms/bedrock/messages/mantle_transformation.py index a8a7b7ed1d5..da7b8697a6b 100644 --- a/litellm/llms/bedrock/messages/mantle_transformation.py +++ b/litellm/llms/bedrock/messages/mantle_transformation.py @@ -6,8 +6,13 @@ AmazonAnthropicClaudeMessagesConfig. Overrides only the URL and model-prefix stripping that are specific to the bedrock-mantle endpoint. """ -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple +from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, List, Optional, Tuple +import httpx + +from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, +) from litellm.llms.bedrock.common_utils import build_mantle_messages_url from litellm.llms.bedrock.messages.invoke_transformations.anthropic_claude3_transformation import ( AmazonAnthropicClaudeMessagesConfig, @@ -89,8 +94,26 @@ class AmazonMantleMessagesConfig(AmazonAnthropicClaudeMessagesConfig): headers=headers, ) - # Parent (AmazonAnthropicClaudeMessagesConfig) removes "model" from the - # body (Bedrock Invoke puts model in the URL). The mantle endpoint - # (Messages API) requires "model" in the request body. - request["model"] = model_id - return request + # Parent (AmazonAnthropicClaudeMessagesConfig) removes "model" and + # "stream" from the body (Bedrock Invoke puts the model in the URL and + # streams via a dedicated endpoint). The mantle endpoint (Messages API) + # requires both in the request body. + stream_fields: dict[str, bool] = ( + {"stream": True} if anthropic_messages_optional_request_params.get("stream") is True else {} + ) + return {**request, "model": model_id, **stream_fields} + + def get_async_streaming_response_iterator( + self, + model: str, + httpx_response: httpx.Response, + request_body: dict, + litellm_logging_obj: LiteLLMLoggingObj, + ) -> AsyncIterator: + return AnthropicMessagesConfig.get_async_streaming_response_iterator( + self, + model=model, + httpx_response=httpx_response, + request_body=request_body, + litellm_logging_obj=litellm_logging_obj, + ) diff --git a/litellm/llms/bedrock/realtime/handler.py b/litellm/llms/bedrock/realtime/handler.py index 6db2571090a..b48c37791c4 100644 --- a/litellm/llms/bedrock/realtime/handler.py +++ b/litellm/llms/bedrock/realtime/handler.py @@ -5,6 +5,7 @@ This uses aws_sdk_bedrock_runtime for bidirectional streaming with Nova Sonic. """ import asyncio +import contextlib import json from typing import Any, Optional @@ -12,6 +13,7 @@ from litellm._logging import _redact_string, verbose_proxy_logger from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging from ..base_aws_llm import BaseAWSLLM +from ..common_utils import BedrockError from .transformation import BedrockRealtimeConfig @@ -58,9 +60,7 @@ class BedrockRealtime(BaseAWSLLM): InvokeModelWithBidirectionalStreamOperationInput, ) from aws_sdk_bedrock_runtime.config import Config - from smithy_aws_core.identity.environment import ( - EnvironmentCredentialsResolver, - ) + from smithy_aws_core.identity import StaticCredentialsResolver except ImportError: raise ImportError("Missing aws_sdk_bedrock_runtime. Install with: pip install aws-sdk-bedrock-runtime") @@ -81,11 +81,36 @@ class BedrockRealtime(BaseAWSLLM): verbose_proxy_logger.debug(f"Bedrock Realtime: Connecting to {endpoint_uri} with model {model}") + credentials = self.get_credentials( + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_region_name=aws_region_name, + aws_session_name=aws_session_name, + aws_profile_name=aws_profile_name, + aws_role_name=aws_role_name, + aws_web_identity_token=aws_web_identity_token, + aws_sts_endpoint=aws_sts_endpoint, + aws_external_id=aws_external_id, + ) + if credentials is None: + raise BedrockError( + status_code=401, + message=( + "No AWS credentials found for Bedrock realtime. Set aws_* params in litellm_params " + "or configure credentials in the environment" + ), + ) + frozen_credentials = credentials.get_frozen_credentials() + # Initialize Bedrock client with aws_sdk_bedrock_runtime config = Config( endpoint_uri=endpoint_uri, region=aws_region_name, - aws_credentials_identity_resolver=EnvironmentCredentialsResolver(), + aws_access_key_id=frozen_credentials.access_key, + aws_secret_access_key=frozen_credentials.secret_key, + aws_session_token=frozen_credentials.token, + aws_credentials_identity_resolver=StaticCredentialsResolver(), ) bedrock_client = BedrockRuntimeClient(config=config) @@ -156,12 +181,19 @@ class BedrockRealtime(BaseAWSLLM): session_state: dict, ): """Forward messages from client WebSocket to Bedrock stream.""" - try: - from aws_sdk_bedrock_runtime.models import ( - BidirectionalInputPayloadPart, - InvokeModelWithBidirectionalStreamInputChunk, - ) + from aws_sdk_bedrock_runtime.models import ( + BidirectionalInputPayloadPart, + InvokeModelWithBidirectionalStreamInputChunk, + ) + async def send_to_bedrock(bedrock_message: str) -> None: + event = InvokeModelWithBidirectionalStreamInputChunk( + value=BidirectionalInputPayloadPart(bytes_=bedrock_message.encode("utf-8")) + ) + await bedrock_stream.input_stream.send(event) + verbose_proxy_logger.debug(f"Bedrock Realtime: Sent to Bedrock: {bedrock_message[:200]}") + + try: while True: # Receive message from client message = await client_ws.receive_text() @@ -176,19 +208,15 @@ class BedrockRealtime(BaseAWSLLM): # Send transformed messages to Bedrock for bedrock_message in transformed_messages: - event = InvokeModelWithBidirectionalStreamInputChunk( - value=BidirectionalInputPayloadPart(bytes_=bedrock_message.encode("utf-8")) - ) - await bedrock_stream.input_stream.send(event) - verbose_proxy_logger.debug(f"Bedrock Realtime: Sent to Bedrock: {bedrock_message[:200]}") + await send_to_bedrock(bedrock_message) except Exception as e: verbose_proxy_logger.debug(f"Client to Bedrock forwarding ended: {e}", exc_info=True) - # Close the Bedrock stream input - try: + for close_message in transformation_config.session_close_messages(): + with contextlib.suppress(Exception): + await send_to_bedrock(close_message) + with contextlib.suppress(Exception): await bedrock_stream.input_stream.close() - except Exception: - pass async def _forward_bedrock_to_client( self, @@ -206,6 +234,10 @@ class BedrockRealtime(BaseAWSLLM): output = await bedrock_stream.await_output() result = await output[1].receive() + if result is None: + verbose_proxy_logger.debug("Bedrock Realtime: Bedrock stream ended") + break + if result.value and result.value.bytes_: bedrock_response = result.value.bytes_.decode("utf-8") verbose_proxy_logger.debug(f"Bedrock Realtime: Received from Bedrock: {bedrock_response[:200]}") @@ -252,6 +284,7 @@ class BedrockRealtime(BaseAWSLLM): except Exception as e: verbose_proxy_logger.debug(f"Bedrock to client forwarding ended: {e}", exc_info=True) + finally: # Close the client WebSocket try: await client_ws.close() diff --git a/litellm/llms/bedrock/realtime/transformation.py b/litellm/llms/bedrock/realtime/transformation.py index 498567a4ecf..fe5f0584e03 100644 --- a/litellm/llms/bedrock/realtime/transformation.py +++ b/litellm/llms/bedrock/realtime/transformation.py @@ -4,14 +4,18 @@ This file contains the transformation logic for Bedrock Nova Sonic realtime API. Transforms between OpenAI Realtime API format and Bedrock Nova Sonic format. """ +import base64 import json import uuid as uuid_lib from typing import Any, List, Optional, Union +from pydantic import BaseModel + from litellm._logging import verbose_logger from litellm._uuid import uuid from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.llms.base_llm.realtime.transformation import BaseRealtimeConfig +from litellm.llms.bedrock.realtime.trigger_audio import ready_trigger_pcm from litellm.types.llms.openai import ( OpenAIRealtimeContentPartDone, OpenAIRealtimeDoneEvent, @@ -35,6 +39,17 @@ from litellm.types.realtime import ( from litellm.utils import get_empty_usage +class BedrockContentEnd(BaseModel): + stopReason: Optional[str] = None + + +TRIGGER_AUDIO_SAMPLE_RATE_HERTZ = 16000 +TRIGGER_AUDIO_BYTES_PER_SECOND = TRIGGER_AUDIO_SAMPLE_RATE_HERTZ * 2 +TRIGGER_LEADING_SILENCE = bytes(TRIGGER_AUDIO_BYTES_PER_SECOND // 2) +TRIGGER_TRAILING_SILENCE = bytes(TRIGGER_AUDIO_BYTES_PER_SECOND * 3) +TRIGGER_AUDIO_CHUNK_SIZE = 1024 + + class BedrockRealtimeConfig(BaseRealtimeConfig): """Configuration for Bedrock Nova Sonic realtime transformations.""" @@ -43,6 +58,8 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): self.prompt_name = str(uuid_lib.uuid4()) self.content_name = str(uuid_lib.uuid4()) self.audio_content_name = str(uuid_lib.uuid4()) + self.prompt_started = False + self.client_audio_streamed = False # Default configuration values # Inference configuration @@ -247,6 +264,7 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): prompt_start = {"event": {"promptStart": prompt_start_config}} messages.append(json.dumps(prompt_start)) + self.prompt_started = True # Send system prompt if provided instructions = session_config.get("instructions") @@ -304,8 +322,22 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): List of Bedrock format messages (JSON strings) """ verbose_logger.debug("Handling input_audio_buffer.append") + self.client_audio_streamed = True messages: List[str] = [] + if hasattr(self, "_audio_content_started") and self._audio_content_sample_rate != self.input_sample_rate_hertz: + mismatched_content_end = { + "event": { + "contentEnd": { + "promptName": self.prompt_name, + "contentName": self.audio_content_name, + } + } + } + messages.append(json.dumps(mismatched_content_end)) + delattr(self, "_audio_content_started") + self.audio_content_name = str(uuid_lib.uuid4()) + # Check if we need to start audio content if not hasattr(self, "_audio_content_started"): audio_content_start = { @@ -329,6 +361,7 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): } messages.append(json.dumps(audio_content_start)) self._audio_content_started = True + self._audio_content_sample_rate = self.input_sample_rate_hertz # Send audio chunk audio_data = json_message.get("audio", "") @@ -383,7 +416,6 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): List of Bedrock format messages (JSON strings) """ verbose_logger.debug("Handling conversation.item.create") - messages: List[str] = [] item = json_message.get("item", {}) item_type = item.get("type") @@ -392,6 +424,8 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): if item_type == "function_call_output": return self.transform_conversation_item_create_tool_result_event(json_message) + messages: list[str] = [] + # Handle regular message if item_type == "message": content = item.get("content", []) @@ -443,6 +477,12 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): """ Transform response.create event to Bedrock format. + Nova Sonic only starts generating after it detects user speech, so text-only + sessions never get a response on their own. Injecting a short spoken "ready" + utterance (followed by silence) makes the model respond to the pending + interactive text input. Sessions where the client streams its own audio rely + on Nova Sonic's built-in turn detection instead. + Args: json_message: OpenAI response.create message @@ -450,8 +490,53 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): List of Bedrock format messages (JSON strings) """ verbose_logger.debug("Handling response.create") - # Bedrock starts generating automatically, no explicit trigger needed - return [] + if not self.prompt_started or self.client_audio_streamed: + return [] + + messages: list[str] = [] + if not hasattr(self, "_audio_content_started"): + trigger_content_start = { + "event": { + "contentStart": { + "promptName": self.prompt_name, + "contentName": self.audio_content_name, + "type": "AUDIO", + "interactive": True, + "role": "USER", + "audioInputConfiguration": { + "mediaType": self.input_media_type, + "sampleRateHertz": TRIGGER_AUDIO_SAMPLE_RATE_HERTZ, + "sampleSizeBits": self.input_sample_size_bits, + "channelCount": self.input_channel_count, + "audioType": self.input_audio_type, + "encoding": self.input_encoding, + }, + } + } + } + messages.append(json.dumps(trigger_content_start)) + self._audio_content_started = True + self._audio_content_sample_rate = TRIGGER_AUDIO_SAMPLE_RATE_HERTZ + + messages.extend(self._response_trigger_audio_messages()) + return messages + + def _response_trigger_audio_messages(self) -> list[str]: + pcm = TRIGGER_LEADING_SILENCE + ready_trigger_pcm() + TRIGGER_TRAILING_SILENCE + return [ + json.dumps( + { + "event": { + "audioInput": { + "promptName": self.prompt_name, + "contentName": self.audio_content_name, + "content": base64.b64encode(pcm[offset : offset + TRIGGER_AUDIO_CHUNK_SIZE]).decode(), + } + } + } + ) + for offset in range(0, len(pcm), TRIGGER_AUDIO_CHUNK_SIZE) + ] def transform_response_cancel_event(self, json_message: dict) -> List[str]: """ @@ -467,6 +552,35 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): # Send interrupt signal if needed return [] + def session_close_messages(self) -> list[str]: + """ + Build the Bedrock events that gracefully close the session + (contentEnd for any open audio content, promptEnd, sessionEnd). + + Returns: + List of Bedrock format messages (JSON strings) + """ + if not self.prompt_started: + return [] + + messages: list[str] = [] + if hasattr(self, "_audio_content_started"): + audio_content_end = { + "event": { + "contentEnd": { + "promptName": self.prompt_name, + "contentName": self.audio_content_name, + } + } + } + messages.append(json.dumps(audio_content_end)) + delattr(self, "_audio_content_started") + + messages.append(json.dumps({"event": {"promptEnd": {"promptName": self.prompt_name}}})) + messages.append(json.dumps({"event": {"sessionEnd": {}}})) + self.prompt_started = False + return messages + def transform_realtime_request( self, message: str, @@ -837,10 +951,11 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): Optional[ALL_DELTA_TYPES], ]: """ - Transform Bedrock promptEnd event to OpenAI response.done. + Transform a Bedrock end-of-response event (promptEnd, completionEnd, or an + END_TURN contentEnd) to OpenAI response.done. Args: - event: Bedrock promptEnd event + event: Bedrock event that ends the response current_response_id: Current response ID current_conversation_id: Current conversation ID @@ -848,7 +963,18 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): Tuple of (events, reset_output_item_id, reset_response_id, reset_delta_type) """ verbose_logger.debug("Handling promptEnd") + return self._response_done_events(current_response_id, current_conversation_id) + def _response_done_events( + self, + current_response_id: Optional[str], + current_conversation_id: Optional[str], + ) -> tuple[ + List[OpenAIRealtimeEvents], + Optional[str], + Optional[str], + Optional[ALL_DELTA_TYPES], + ]: if not current_response_id or not current_conversation_id: return [], None, None, None @@ -1084,6 +1210,14 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): current_delta_chunks, ) returned_messages.extend(events) + if BedrockContentEnd.model_validate(event["contentEnd"]).stopReason == "END_TURN": + ( + done_events, + current_output_item_id, + current_response_id, + current_delta_type, + ) = self._response_done_events(current_response_id, current_conversation_id) + returned_messages.extend(done_events) elif "toolUse" in event: events, tool_call_id, tool_name = self.transform_tool_use_event( @@ -1093,7 +1227,7 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): # Store tool call info for potential use verbose_logger.debug(f"Tool use event: {tool_name} (ID: {tool_call_id})") - elif "promptEnd" in event: + elif "promptEnd" in event or "completionEnd" in event: ( events, current_output_item_id, diff --git a/litellm/llms/bedrock/realtime/trigger_audio.py b/litellm/llms/bedrock/realtime/trigger_audio.py new file mode 100644 index 00000000000..5783dae54bb --- /dev/null +++ b/litellm/llms/bedrock/realtime/trigger_audio.py @@ -0,0 +1,208 @@ +""" +Pre-rendered spoken "ready" trigger audio (16kHz, 16-bit, mono PCM), generated with Amazon Polly. + +Amazon Nova Sonic v1 only starts generating after it hears the user speak, so text-only realtime +sessions inject this short utterance to trigger a response (same approach as Pipecat's +AWSNovaSonicLLMService assistant-response trigger). +""" + +import base64 +import gzip +from functools import lru_cache + +READY_TRIGGER_PCM_16KHZ_MONO_GZIP_B64 = ( + "H4sIANGpRWoC/517dXQcR9Bnw+Duis3MzMwkc8xsxxTZMTMzM0PMmJhBjpmZmWKQZSaxFrQ80H0lJXf3vXf/nev17ExPQ3Xh" + "r2wPQv8/f/D/uMP/zxv8f64YkSyiWU1AIpCcRSqyICtQCApF4SgSRaHsKCfKjfKhgqgIKo5KobKoPKqEqqFaqC5qgBqjFqg1" + "ao86o+6oF+qDYtAgNBSNQKPQODQeTUZT0Uw0B81D89ECtAgtRcvRMrQCrQRahVajNWgtWo/+QBvQJrQRbQbagraibWg70Da4" + "2/rf7zbozxyxCcaug1krYZXFaCGai2ajGWgKmgi7jYFdh6ABqB/qjXqgLqgd8NUcNQUe66CaqDJwXQaVhDMUgNNkh7NZ4bSc" + "69zLXTyVJ/Kv/BN/z9/w5/w+0B1+g1/kZ/gpfoL/zQ/xA/wvvovv5Jv5Fmjr+Tq+lq/iS/kioPl8Fp+eRRP4KKBhfBDvw3vw" + "Lrw978jb8Fa8JW8B1JQ34PV5Q2iZ1wa8Ea/Da/Ja0Gpm9TTmzXgT3hzGt+a/wNz2vBPM78TbwtyW0NuIV+fVeCWgCrwyUHmg" + "yll9NWClOrwurNA0a42GMLYerFoN3pf7j8rwYkBFeWFehOcHKshz8dw8Ow/nUTyCh/IwaDYuc4mrPATIBs8WeJMNxmTnOWF0" + "Tp4DnsK4lYscc8405mNulsFc0FKYg9lZKjQX3KXDcxJck+E5haWxRPYd2k/2hX2G9gkonr1nX6H3C7RkGOmB1QjsGckLAK+1" + "QAqdeQwfD7Jdz3fzk/we/4f/5D4uoVyoGOizOdjbMLCw+WBBO1Esuo7uoZfoE/qJnCgARq7iUByGs+G8OGcWReAQbMUCDqBk" + "FI9eoGvoGFjVSrCdAWAr9VBhsHsHf8EP87m8F8gV88dsBxvFajKJvTS3mjFmOdNnXDMWG12NUoZPf6jv0afpnfRKejadaena" + "B+219kqL0xK0oBamV9E761P1I3qcLhhVjL7GEuOckWAUMHube02nWZ/NY7dYBFjJZR4JNnsRqbgr3o7f4WykNZlJDpEH5AfR" + "SRgtQMvTarQJbUZb0JZA7Wjr/1o7+gs8N6K1aRmal9qoSRLJC3KF7CHLyUTSl7QhtUkxkptkIyEklOQghUgF0oh0IINg/WVk" + "O9lLjsI+u8kfZC4ZQ/qQZqQ6jM9DchIbkQglfpyKP+Cb+DDejBfiEbgDrogx/gftQWNRbaTx23wBjwYbuMwmsfLsq7nZ7Ghi" + "84IxHs7r1A/rA/Wiery2UeukRWiPg4uD0UEeuBSYFWgQEAPP/dv9k/xd/K387fy9/DP9x/2p/mqB1QEjMCvIg/M1m75Tr2m8" + "NuaZ5dhrNoaHQnzIDpzUIzdJLXqS5hEmCVcFJpQXu4gjxelAk8XBYgexgiiLP4XLwmphkNBAyCkk0yt0Be1HK1JCX5JdZDSp" + "D2d7htfhLmAXX9FR0H5XVAPlQDp4/xN+BezsCHj6Xrge4cfA9y/wS/w6nPUxWN97/h2ihIObXISoWASiXwxEtDPIjRriTTiA" + "B5N3pD19RH8RbgilxWXiZ7GI1EUaLU2QxkojpcHSUGmGtEO6KX2UXkvbpYaSQ7wq/iXuEleKQ8XS4gdhsVBXEISjoNd3ZBzJ" + "S27hCbgMfgeRTUGHeXfuY6tZFfbQ7G+mGmOMJH2UTvRNWnZtfjA8eBjk+srfx+/x7fQ18/m8+739veW8yPvec8nzl2ejZzXQ" + "Ns9pz2dPiLeld4+3iO+pb6t/RWBr8IL2UY8wm7Pp/AC6huPIF/pOuC1ul4bKuZVDSnF1kfpUDbPUs/xq6WmpaZEtD9TJaqi6" + "QymlHJAj5KnSM7GYOF64SYvTtSSC7MB1cALE6WbIC/GyG8QaESJKAfDm2TyWx3EZInAryA9LQfZvwOor4c54PF4B8juGn+A0" + "HEWqgAf0JkPIeDKFTCOryA5yiaQSG42mI+hf9DvNLvQXrgg2sZ+4V/wilpH6SFulT1JReYJ8WU6QsynFlbxASPkiX5S3yNPk" + "0fJkebm8RO4ox0lNpbNia/Gj0BEsYxxYhZ1cJOdJE3IWM8h0vfgpFsVGmaeNaOOL3kK/ohXV2gfnBL74R/q/+rr57N6V3ibe" + "gGevp4fnm3uuu7T7bcaJjJkZTTMsGfddm11jXF1dfVyzXBdcpTOeZFx2f/HU8j33rww207Obz9l0VJqcoBXEQ1KyXEgtYgla" + "TlgL2ZbZ/rGl2GwheUKUkHhbN9t1a6S1iWWM2kepJxeTUoQLdD5pgWuiWnwWe282Ac3vNhZBNBllTDBWGDuNNwYzmpixZgTb" + "ysqBhJtDDOyL43E0nCw/HUPP0jTwlipCKaGQQIR4eotupcNoPcrJY3KP3CEmaUKnUZ22F24KMeItcbKUXf5DrqUsUxxKe3Wc" + "+lBNVQVLW8twy2kLs2S3NrY2tFqsryytLN/VieoLpaEyV/ZLG6VGUlDcKo4VO4uR4mOhs3CI/iQyyY3t/CmLNfcaF/TT2sXg" + "+0BUoLd/r4/4fvde80ieTu6/M1Jc0a5LzvzOBY4P9qH2bPaE9Pj0T+mivaZ9sv20XbfncNR3vHY8dHbPWOQp6v9VSzXX4wpi" + "HWWK5Zw1xtbd9t46yeq0PLHkt26y9rXlDukSEh1yz5ZgPWwppw6VN4qKsIL0wQPQJH6WtQH9Kmwge8em8ZFoNP6NtKDXAf5E" + "Co/AunLQzPiZi26gFYTzgiSGiYeEdHqKNMKneW0WahY2Ruo+7Yi2SzsBkT6P3lZfol/Tdb2+McLYblw2TNDEA/MhU5FANgnD" + "5BWWniEnw6ZHDIqcGZk7ck7EzfAe4ZXDK4WvCufhvSPORFyJGBnxPJyHpYW+DEm1mdYm1o2WoDpCjVOqKmvlEvJtaaqUXWom" + "NhLiSS5cmzczF+rZtBzBsYEE/xV/G/8nXz/feu87j+zp5l6TkTfjlquFq4DrunOUs6bzo2OsQ3Vst3ezlwdJNrBXg2uUXU8v" + "YRccp5293WX85fSd/C4tIt9UW1jnW7mljWWk2k5NV1Msw21KaL6wa2F/hrUPXWOba/lFmShlE2/SPeQNfoZsaDS/xtYyCRAS" + "Q71IN+qhbYRswl+0Ab1N/OQcfSa0l47Km5X3ynilh/xSjBIakivoL/6aDWcCm21+MmoYO/WC+m1tnrZUO6p91urq8/THeg8j" + "YHw28/Ih6DO+TW+KcfJE4KVw6O2wHeHFImIi9PDocCNsadiksPSwjLA3YTfCqoZ1Cz0fcsrmsi6ydrXOtn6z5rAxqwJetNBa" + "w/qPpYflndoRYtVpeYpUW/TRu2QktqKebJtxR7se8Phue85nNHGZjuoOh32MvYy9kP1D+iOgielqesv0kukF0wdCs6ZPT7+V" + "Xsf+1Z5gz+ko6Ryc8dHr19aiuaKpbrC1BaA02rpcfaNUV7tbGtkehFQJ9YSk2aKsK9SH8hWxibCFzEM9WG/Dqbm02UYCy0W+" + "CbOlmfJ4eZf0U7whrpAaKVUsH6wOW/mQt7bBtlfWn5azqqjYxao0G+pu1tK7a39px/U4Y7hZzaxvJGoDg7kCp/yJ/seBGE00" + "jplTeE9cmhYSp0mfpXjJK54RHgmzxDxyjCU8pFrYwvBLYUbIV2tXdYjslC5IyZJPPqUsVwOWYrZFIQmhxcECK4d+tcVa21kr" + "2mJDDobVi4iNiIooGUZCcljnqcWUSPmBGCGEkv68jtle7x787v/V53UnuD44CtuT0r+m77B3dMQ4tzvDnDmcC+3v01ukR6Tt" + "TU1NiUlpnKKkzk4bm97GUdn5wFHa2cQx3jnE0zJoQy7Rp26x9rP+Zsmh7lJKKHOVEeoPJUbpLn8SL4rVwffO0R0oYAwPCv6u" + "vqf+/noV/ogchlKFC7eFH+JB+YCabmliZdZctiW2DiH3Q1qFjg2Js2ZT44VpaKJRVOOBtYGMwG/BaUF74GwgIrg9uF7bodcF" + "9HZTb2M8Z+GkjlhfGaJ6lRVyZbEEnYC9fBZCdKT4WZYtsyDnzVRtymepqFQTOLog51Z3WIbZzoa8CG0YViwsLLRqyGXbUlux" + "0Blh9cIPRoRHvo14H/4+9KXtpkVTHssnpcLSafEvYSe1ksm8tnldEwN7vf3d91yHXYMztrtPex54FnoKu486v9u/p81Lq2Zf" + "51jhDHfddaxOb5w2K+Vz0vDk2sk1UtypQ1MrplZKr5ihBYeRnmopm2RrrtaX5otLxFNSYbWvdZ/NbZmjZJPzilNoE4xMR6CJ" + "d7a7jO+e9pYdJctETTwtxNB89K6QJEdaZ4WsCWsUPjl8emT5qKURR0K3qIwW5tX1ZsHHwdMQ15jWMVjMH+mb7X3oUwLvA9O1" + "3kYF3pRUFB/LLdVcahl5s5CPHAL0nIRvkXByGM1k7dkhXhDF4TkkkgSIRNcLprRV2WM5bb1pfWxZot5TvZarNlfIr6GvQ3rb" + "zlrXWivbqobst32wVrW+VAurMapb3Q8Svw6RppVYXahEz5JpaIy5WGvk/8tb1fsNUEwzb0fvBPdJl+o8YGfpetqxlE7JD5OV" + "ZHeyJd2bylJbptLEbgkzEub8nJrUPqUa2F2T9J0Zn/RE8aytctigkHVyfmrisUKI8kW9Z+mulpSGkXGsvDnUSAi6vH9nrHYd" + "zNjjL8C60fFSXmm+MFxoJc6QhlpWhA4Mt0SWiwyNWBs+MnS+db9yQtjO8xjN9ViwpSFGV+2Cv5D3c0ZOT/NAV8PkB/FG8oHO" + "EHaJQ+W2ynO5gtiQLIQKWoUKeTeqz18YX4I5AgW110YaTyd++kpoK42RXyjvLZutO61rbKlwHWfbZntqq2UbaTmtDJDPSivE" + "Z+Im+Z1yXm2reuWncmu5lrxIvqx8VzR5tXwEUO99uh3FsbV6seBNX3VvVY+asTVjbUaM+45nrPuOa7Kjpb1C6viU96lbU3+H" + "bLAqvaj9YdqQVG9S++SMpEpJ+ZOvJfZPupU8zLFBmyuWCskRut26Rub4LkvlbmGuckC9r1SUDpJu5qLACt8Zz09XlPOKc73n" + "czBonsPRtBO5C7V/FD0rb7A5w/dGXc/2W9THsDO2CEsZOU24Txriv9EP/oYdMO/q6wKPvfc8m7xlghfMmmSdWFdC0kBxn9hf" + "6iwtkBLFPmJNcZjgpnXJYtSLbdE2Bry+rv7DOkVf6BbpqhQQZ0tHpRRptdoSLOpL6PzQyyFNbYssJRVBuihECcWFC5Ku/KME" + "lFFKafmlRKSOUi55plLUkmLxWH9YTXWaPFdcCRmpBA81rgU/Brr48/sEr83TMyObe2LGtYyKGdQlgVfOs/9m/9W+1RnpLOTa" + "6yzuLOOw2KemD087n5or7XJqVNrxlI2OzcEmwjXLGltTWz7rXsmHR6D2Qg85XGmu9FduSHVJvLbYW9Q9OKOFu6N/oLGTj0WD" + "+RjeEkXjz2SfOF5Ntk0NC4scEtUj8n3IA6Wl+E4whfeCIrYQPuKnrKYR0P4MouDvwWjdyk+SseIieaKsgLzaiGWl3+UcgL/t" + "4kmxoxCPu3HNGKiN97cEVHAv8IFtIa3EpVJuqb+0Uv6hlLXeCu0R3i78aHjZ8KuhD6wB5Z3UX2wijhFVmaiXFEWeJM2VqskN" + "lGfqaUtXywBLJ8tjS0OrWx0sj4DYeAQ/Rk9YUb2Xv41HdI/wxPk3BqoHznkPuG46cju+2rO7Jrlfuld5Orh7uMY6UtNcKcVT" + "XqRMs/d2fAFLy5WamPwlMT4h2rmcpakTbLct9eQUWhg9QOWkebarIdstneUV4lK0UkvwXM8o5U7y7AycMP38lrkkuMbPgkvM" + "R/ik/MV6JvRC2OKw26HlQ2pZtsrvpMUKs3yw3Fbmih5eTTvvzevt6m+vjTFGQs1o5fXYXwyjFrgHnSp2lgeotS0n1HxKfekG" + "nYldPC8aiUrxDuYzbV1wj57MMvA3GifIYkF5gtJC/R3QbnnrGdvvIYNCRoSsty2zrJEjxDHCNGGcmENi4haxkVhQbCkmin+K" + "1aQSck8lQemozlOGSSWFFqQCNnkzNkDPHsjh++p54tnofe4p7NnnphkhzucO2RHpPORIcrRycecl++r0hmkd0i6nXUvLbk+2" + "b007n9wv0f+z18/eSdczTqCnwEV1eSjNiYuwYby1MMYSb8WW5/IF8Q46qKV6DrivePr79wSjjFxGpDbKN80bHqhp3ECquFZ5" + "py5XO6uJ6lJLH6jdHljKWT+ABMrL+ehD9lgrGiyiLTT2mR+Nq3p+QJUHg80NiU/A2+lyyCo7lA9ST7G4oJFtpAytJ8wX2pN1" + "bKNeWauqt2Nf8HlaR9SEUmJJKb/cTj6gRFjyWX22waHZQh/aBloqKWmARLLJY+Rlsk1OETeLzaTfpUZyqNxdKg4I5Kh0XEwX" + "2gr5hFPUiVvxFMMZXO2zeX96Xnp+95z0rPBU8sx1j3P5HGccVR0THCcc+ZzvHbvBzkTHqfS/0lhq7rRHqS9SHiUfT56WJCVO" + "TinnsbM4saFcU/ydKLgl7k5LyonKCbWepaKlitKcxurEl99d2X3Jt16/ZlbVW/o2ua+5a/h6Bp+bY8k46aO62/YjZKtttGW0" + "ck8pad0Y2jS8avhj2yC5Iz5ohhvT9C/aJL2u3kZfpGXTbgS1wOBAC20qy0PGCNehIh1J+9PG9D3dJBQUDNwY70EneF+eyErx" + "bmgk/5v7UVN8nRQUZovD5EbKW6WQpT6g7t8tFdWDUGVvV+up1dRR6gGlkdJPOSvHS3OkKOmq6Befi7PEOKjLiJhIV5BsuBeP" + "NQO6qB/S2gUf+Gv5Zd8nTzvPM3fpjDEZbd0lM+yueq5RjvaOk/Yj6eUcOR2b06um9Ujbl/otJZAyJmVHct/k8Ylm6iNfPL8m" + "vBaX0p1ordnabEZqWVjotfAjEQNDk6RkZg9sDEzQn7JCZCfpx5r4H7lyu2q7b/mi9EW8HDWB1gq9hWV0jlBL2WcLhPYM/xg+" + "OmyRdbtUmJbDyxGF+nMiyYcKGPmCBX2LQKtj/cP10eCPV9l9VpdNNZuzs+gQHSzekcbJ/SQH7Yr38VH8Mxol7FBmWpLU29IY" + "KuMfqBn9JtUCpDrBgi1vlbbyWOkPaay8Us1u7R1SPjTNWly9IX2Vsit31G7WrdZfLS3Ur1I5KVaYT0fSKjSO7MLvmUNvHGwW" + "OOO/4nvtb+Xv7D/rO+dxZSxxDnDUdPZ3+jOKeVhGWsZOxxzHLOcNRzVHFfu+tI72nvYSzvN2nLYh+U5i/bRX3oJsjbBOGMP3" + "aFv9/Q2H8FvInPDVYTVsWP1NmihZAOk9JUfJCuGm+EkYgmP1OP8dL/Jv1xawW7gj+Qc/RJfZIfYnukvbyolqYVunkCe2RdZK" + "lpxqbzla2gtoNsz6QE0QFdzD1PQocx27z/2skDlK9wQHBI8Ex2pD9InGZvMMX49aoVb8EPOa7dkPvpjWlG+rpS3V1WxyEbCT" + "dKmv+tLa1bbF2tkSq4yS8ggzSXVynJwRFkh15GxSSZqEuqC9uK2wTFovj5SvSjOlN2JRsZ+whBakaaQSbUkvkTr4BDtrtDRK" + "GgWMMMOr/2oc02toAwMj/O39NPA60Dp4NoACjfzcN8g/33/eX9Pfz7/LPzJQIdDN/8i71PvIq/na+6f6fb7FvrK+VpDB6wXL" + "6T2NXUayXkq36KqxyVzBf6ApuC9ug/fg2aQY7Uo70qm0mNBHHCqFyPelUCm/+EYYLo6Ucsqq3FzaJVYRK4klxThhidBYaCa0" + "FiYLc+HuJq1I15NhZBcJp9VobjqHfMF/4K34LN6HS+JDaACajY4CXv0FHectuMRdzMds/CNY4ho2iDVi9Vg1VpR9M/+B6h2z" + "7mw2W88WsXXsFrPwxfwHb4I+o214FqlOT9FywkqhipgkXpNmyW65PlS4awFreJXcaj91rjpWbawSNZtaDjDtVSW7ckPeJJ+Q" + "H8lz5ZbyFSlacon7xV/FIuJrYa1QWfhEe9PDgJhHYILvAzYcjt7zXHwy1MjXzbNmZ/O2Ud9I0LkeYvyp59evaCF6V/2HVl+7" + "FFS1odo+bZWWR3MGfcHcWh+oD19oH7Uw3dSi9G76Dv2+Lhn9jM9GTXOYudzMxY6z3jyBt0NnUGmcgJeTNyQDNL2KZhOmCjeF" + "/cJzIVVIFz5BZXUCetqAVOsKOj0K8hxKB9ESdCqpSsaTg2Qy0fGvuDy+jQlZgp+hTegaKoPD8E70htfgE3gldB81Rvl5GkM8" + "hl/ij/g4OFNP/op3Qh3Rc76fS+hPFIeeo1j0BMWji8BREfwPfoR34qW4J/6EB0HkOIcL4J7ER6uLTQERHRMKiUfF/NI06ZM0" + "UE6Rdfmn7JQny4XlgDRIVpQIpZscKp0SPdJguZvwinQVeymX5FhiomJ0txAmuPAEEi3cEeNJB3bfIKiqsFFYgzqbETw77sH7" + "m3HGbUZxU15AfxJYbj4jt9DcQE1Pu8AEQA+GVs/XxP3I89pX3dfWc8izxVfQ9ynjJkTWFs4Qd3bPNvfbjJ0ZroyJbuQu7C7p" + "669PRXdISTyC1TNv80pCbmWEJcaaCnk3TGksL5AXKw3UfOohiGOfxIOCHw9jHY1nxjeWD6Ww7sbrYOFAw0CJYIhe1jzDCnCF" + "tTE/mJv5e9yUWoQVgiFsFpk4TmwmCDQnrSnMEE+Jd6gPcLLf+M2MZTX4HLbTKA45aoB2XCuiT9ZT9KrGZOOm0d7MzZqxsUzh" + "VxChX+gGmkSO0q/SHMs72y3bQ8vfag+L23Y47GB4IIyF/hXSPWSibYxtrO2sLbstzXJWraO+VLyyJNcBlBgqD5BbSNuEUPqV" + "nKIvwVZa0f34CLrOh/BjLM50QcyYpY/TfIFFvl7e7p47npKemu5Ql+z43V4rfaq9rn2m470jxWHY+9gD6Rn2ac69rnoZ21yf" + "nE8dCY7Rjp7OEMc654yMyoFpbANgg799Yd5xwSn4jFLWFmodofwqrKbJQnO1ZsjQsCahf1vbK1ukW2J1MVY4KcwFO9kudMOD" + "zc6Qp8ELeAymGHGXPkILaJfN3XiesEDcKRSnPch0spdeEqNkopyXndJj0SL0IrPwc2yjOv1OP+EdbIveReun7zQT2F2WYTzT" + "OgVZ4FZwmE7NLhA5Nppe44KBWU/+FY0kPehPuoLaqIcspZWFdWKsfMyywXbEdkBdIbeVN1h+C8uIbBDVOqJ9SJp1sG1m2KjI" + "6KgGkRfCZodE2wxrrDVo/cP6wTrXGmrppNjhLKOEN0J7abTUWcT0JW/DnpqLWCLfxAexNvqXwFH/BH/dAA52CTz2j/HW8XQG" + "lLnD293bx1PD43V/gadmnuvuhxnRbu555Z3pu+BZ7lrveuh65on3fvTMz3DZ36UfcO7z3te7Glv09to37atxlX8lqXK0Jdq6" + "3XLOEms9YIsIuRA6I+x0+NDwAaHRNq5slffJR2Qmq1I+YQr6aAzQl+kB46d5wLip19RcgRrB6dpdoxl7xfbwKMRRbfwb/pPs" + "p5XFNGmjlCB2E24SCy1KI0FTC2lu3IntN44bb4x044QxXN+rXQxUDZQIJPsbBotqHfW7ekv9oD7COGpW4xfRX+QSXSh4BK9Q" + "XzwjSvI4Ja/6UPkgf5RqSM2lF1DlfJcaSYOkgdI0eZTUWxorFZEHKK3VDLWL5ara1nLU8tmaIyQyJNVW0NbaehckftzaDk7K" + "1dNKXXmrVFBKEqPEAO1FC5ICOMh/5cO4G07iN8eZd40YyIHVjdzGMWO1EW6YelP9qH5Hf6/XNsrqY7SZmk+rb9zTYoJ3/N99" + "Df2T/C8Dq7SPgak+X0bLjK/uEr6R/k3+Hd5ZGcmO2/aSzrYZZXzegKwtDxjeBb6+2p/sJCkF3nxTXCMa4i35rGVZSPbQ1qGX" + "bLr1hWWv2lTNrf6i3lZ+in+SQzzNzM5izR1mE7OF/jTo9yf6EwOdtK96DVMzC7IE8xY7juKJKnaUvksDZIu8Vm6jvFA2qyfU" + "HGpRpadUV4wWooU8wgTaj1RBSWyYed/IYVY0b5g5TMmw6o81ppc3j7E/+GP+FOJ1EFUnOWgd4bQ4X5okHYT4c0s8DDX1KDmn" + "PA+qyGNCUaGTUFvYQJNJKGSFEXgjqoVa8OmQOynqie6iUtiNp5Ph9BJUtyOl7vJ+ZaQ6Sa1guWIRrUnWN9bxttfW65aClli1" + "r+pRHsjT5ZJSM/EFrUQfkglkAN6B7vETPJ2/52/5CI54bV6GV0P90Xv0GvVG1RFFQV4V18VLcF4UZAvZUraJSXwcY6YXrOpc" + "cF+wmjZYuwvect230fvYG+W96W3stXuXeZO9g72jfdhnerODx2ieGH/LwIGA7o13L3ZJbp/ns39y8F6wTGCd76gvEDxi1kHh" + "uBtksHD8lZhCWfmJMkph8g95qDJFWaeUV+Yrk2SvtFE8S+/i+ugyyG8f97L3RiftRKBioFPwnvZOf6pP174G04O59QqmzNei" + "vWgMesGroI54EJ0LFv6LcJdmh5PXhXiiCDPpFvIJsmsRXodnwNkqsjNmS3Oi4dSHAzZYaNSGDH/Q/N2MMFeb81lfNA5bSDw+" + "iC/j4WQlHSLUESPF08JZ+gQQcZTAaFPhDBVpA9IJf4As3BNvwX6UyntwkS/lW/gD/pKf5PlQTkTQRJDtZfwDHyOlSVFSjvwk" + "u2gh+ZY63BqrtBRvCLklXW1giwm5Z30ia/QsLS19scyw7lW3i4/xKvQNmbSJ1EJsSbsjJ3sC2cxEGvbgDngef8Pqs3GsJMqJ" + "LbQVOYiK8aGsNHei4kSmS/BBHg8WWYZ15dPRH3yXWc7YqjXWFL2+cc7w6D+D4wO5/B/8CwNbg9eDawNvfMu8y71nfZ/8yYGQ" + "YJVAuH+dL+hL9r8OSNru4NZAG8Cgyb7K/iHglaavju+NZ61nmfeQ/0bgQ4AGogNnA5u0O0ZDbgHc4uQd0QXcX2gnXZRySylC" + "vBAQfpME+bX0u5hBF5I5+Djglt7oGy/MT5qTjIL6ccizTfUJxndjmPGrflOL0IdB/OsAqKY72sdX8UkgwdV0hDBAuEcdZB/J" + "DnKaKPygfahIOuI/0G20D61Cy3gO/sP8Zo40k81TZln203SZO808LIZl4624xsuiQ+gn6g54NgeZSl4Dzi1L29OF9A96gQ6m" + "PWkZWp6eIevJIjKQNCTpeAh+DzY1DC1Dh9F2NBhFoIN8B//ER6FkZMWv0Snwyc+oLt6N47EXNFyG5APMlRcyZRM8F88B9DUN" + "VUWf+Tn0lGymi2htfBAsoh3ZQI+LE0VVmItr4MWkgrhPOif+IswhGjbwG1oBcvAq6sFVcB2chseSAnQJ4fgFmouao79RNL6B" + "r+LO+BmvxPvBiS5Bj4pdfAnbY3KzIH/Bz4NdTDaHGSWMQ8ZaswfrYYYZT7VnWjc9ylhotDEW6wO1O5AZLmiadlcrpcnatuCc" + "YKvg8uCtYB1tp7Zfq6sVB0RcWOurubRhejP9kvY4GAjm1dZoKcHdwa+B9oE431jfLX/rYDDYJZgW6BS4F6ivXTR6A25dzXez" + "KrwsjhS+iYa0X7oIOL28RJQNUJ8fl03psLhfKCu8A83NJs/xO/SUt2A1TQm8DvKfqZjTjRCjqNHT/MEOg9ev5PNA3m9REO8E" + "NFBHWAzzFtDFQmnxunhPdAoXaCnamJ6morALPH0XyLcxmoI+ITvUAU4+lecDC1vMG6NnaD9YksZrQ3XQAktkA7lOupAK5DeC" + "6T+0uTBCoLBCY1qf7qC1AIlFCvkA2dwFNC2CHTzG1fFKpPNlwM02fpd7uJcfBPlrrBTE0rloCrlLCN2FV+MreAf9IVSVTHGU" + "aNI0ekEoJFWXR0itxLpCPL0D9b5T2C10oJOIieuQaWQumUU+YahV0W9oLWIoDb1EfVB+tIB35Im8NVqCBiEVjeBNAft/56Mh" + "lqSDN3xjn1kDfhsqkY38LcvDyrOS7G+WDFSUXTTzmQ3NoeYnM948aYabF4zZxjJjr+ExrhlrjMZGFcOlNzfyGtkhC/cGSRcy" + "DF3XX+g39WqGYUjmPWORscAoYqwyJhjbjQ+Aek9BpXkCaiMXRH9Zp8ZnvYeeDlXSDn2LKfEcqCiKBT4eo5NkidBa7CEmCYOE" + "CUJZMZdUVfoilhJLCoOpi1yCk4aSebgfeKmfLWBlWS3WkxVgDyDmTjefm9HMyabwhXw7j+V+QNeHcQhpCxIqC1X3JFIeasZH" + "oOupJCdxQUSeDh6ajNvh2agMYmDzSZDVXvO2/ALgjHdmPkZ5V96Mn4PqcxeL5PGQFRag0Wge1K+FoaJ9g29BDLgD9ZJK+pGl" + "pDv47Rt8AE/HM3B9XBG/RVsgOiXye4ATN4D91AWPSmSPGeEvWSrLz/vw8lCZKVCpreU/uZ1f5M/5bIjLR8FXn5G+OD9+hr/R" + "cDFB/CKMoflJFdpPWC01kz4Jg+hkUonE0F1CmnCbRpGf6ADEj1nATUWcG5Xgw1lr9hL2ymCbWV7GjarGF+Oc2Zc1YGvNw0Zl" + "o7MRajrNtuyC6TdqGaVAQwUg0iWZVcwWRub/kSpnjDBaGYn6bEBWrfWaemn9ofZKOwTe30JbrH3VtukrdFnvpaUHPwYfapHG" + "buO23l+bERS1QfpOI8FoYVzRNgUnB4fq1c3b5gTDDk8tgh20WP2AEWkc0ioHjwRqB84ECgdzabX0aVBHm8FhwU3BNvpHcw/4" + "wmIWYdYyv7OLWBDaCXPJSbCNENyZVhIThLx0HqrH/+RV8ESSg0xGvVl1UzZfQ4Um8pHsF7OYEdSnGtPMOnD2juwzVO5fzSfs" + "B98OmX0haohKodVoEG5F/oQa+ig2UDbcEGrwAmQcXgQV8Vq+HuJFgJ/mRfmvLNlMNXuy2+wHm8/c5iPTY66ASroxj+ClOGdN" + "wFIqo2gUwKWon06k+0k0vSdYAH80VprLJQBhF5WvKbvUi8ouaZyYU2wAttxYGiEuojvxLaizk7CJo/FRHsZGm6/MBawo38QO" + "md+N341NRjtTZArbbJrGTyPJKGHOMLeaG8w+ZoaxzmhvdDfyg34GgCY362+1G1qaVhF0lK7N1eppWCukjdUua7u07oCE/9Yu" + "aY+04lB7RulYb6iv1f36Ez1WZxDF8xtljUHGTqODEWF81csY0fB8yzhvtATNn9bP68eMp8ZRw69P1wfoY2BORV3TdmvttSda" + "DtD/St2mH9WG6E0MK1TFPVgFVpltZFd5PDqJd+D7+DTW8ABikmq0CN1EOpM+pAy5jffjXTgP3gY5qAh6D9k0yA6yADvPtrAB" + "rB3ryiaxdNYKUGVR3gAiZHPIyHlxBdwCb8Yy+YMECKI7yQVyk1Sko2lniml7cgnk2BM8OTeJxhPAj1uDd37OnIcW8t58HQ/y" + "pqgGikQPOEJ7kIBDwC/forKQM7dDbGgC8fsFROcA3g94oL+gizHSXvEBDRO4sEQ+qvRRo5RYQFfHxXrKR7WKmiAFhexCTaG/" + "9Ctg+TNCfTIMFyHT6Sc6nwZxKJoBEaQmPoXjkIx+5QIgiWdZ/z9tOrcD6nrKPjAXa80L8sesGgsCpkgzK7LZLJrNM+1GqpHd" + "3GdeNqeapc32Rro+3GgMz83NP4yuoPPFRnmwv1um1exnLDGY8c58b640J5oPjUuGag4yR5uFoAYZZa6A3wwjxGxh7jYlFsZe" + "mH+aE0wCWPU31oodBQ4UvoddAzk35GtAxicgwiWznFAfVASMUZUfYe1ZDXYKeJ3F3piNzApmN/O8mWJeMTuayGxjPgZLvccO" + "sBOsMh8Lc74AbmwNES832Uzy0xi6mt6nNkDN+4UzQpywWTgEGNovHBGWC4uEacIGYYXQWsgvvIEs5qTRQlXhDh0A6GkT/Uwj" + "hDDhHO1LR9A9lIKMswkfICcvAmT1lHrobcjZeagVcFs8OUQWkpLkOF6J/8Br8C84ATDUTPQraomaoCoomZ+BU+3kY/gQPpoP" + "4Pn5a+ZmYZBn4yD6XmYzWQfWh/0JVrcSbG4Q6wTeHcdk7mGxEOcNFs3nQBwewpvwipBHdJ4TneLt+Ax+E3Lpab6cl+aJIL/T" + "7B82mjnN2SCjbmZuwBxTjOZGTfCmTB/abQwxphnfjJVQHeaFODLf3GN2gsxaAmTdgSUCqlzDMmP/FPan2d8caNrYNjaDOSBa" + "5THXmB3YWeBvBLsPntWTPWCD4BTPYN5A0GIhfoyf4934PqaZ7VhuyC4ePoEfg3eH2G98EFqBHMD9L5BNjqJeeBXuiL+gE6g5" + "5KjF5CHIrT5g0t+JQAfR6bQLIJVcgFcH0aX0OX0GuptBp9FdoEFF8NFXUKXsAVzzmJo0lxAuIEDK72gcdVMXzSOUFpLpXnqc" + "vqcBKgspNJ4mgo7O0GP0Mj0H1610Dm1NI6lB/iEXyRrItDlJCkSFvoCCnOgaZNHeKDs6C3KtDNXObfYXG8PysX/MhWZb8zez" + "u1nHDBiPjT+NbXANMweDV3SGqjjaXG5WAinMYb8wysLheoF9Z59YP3bHRCChC2wR42Cru83rUEHbANEkmTpg+UKAbxLN/FDn" + "bGI3IDdeYW1YX9DgHL4L9D6DTQdrqMj/gHqrFlSqMvjqKf6RL+CxLDfkkO6sC8g0lV0xx5onzPqgncfA7XKzgfnSfAkYys2G" + "slxgSRfZYO7ml3l2/gkqqFjI6REoFOLcfrivgOYDTUTd0Ep0ErBlFTwI98R9cCwOJ+1ISzIOsA0nrcAb9tBVdBlIuq1wVYgR" + "GgrFhYrCDuG94BO2CNWFdFpDOCDsgTc24Ss9CXrjtKTwnY6lw0GjW0CTo0GfPWgL2px2h5o9B5UgTn4mxyBG9iZNSSo+D1G4" + "J7ZAvmuOCiGM4gCbNOGP2E6wycxqPYaVY5/MJ6YXrLQAE1kEK8JGwpt1bDf7BtF5KoxvxkuAbxREf6HJyAOy6QxW+BdKRfkQ" + "Bvm9ZHY+BCLgHrbRPGyeZD7+hhfgCeZ4sybrwXsgO7/BcrLGbDHvDRK5xW+x9Swc8Noy9JaH8f3sKqvAu/OZPIT3Y7VZL5bE" + "7oDH5mdW0Fpb8N+hrAlzmcUga5wDPxwPmf4j+MV4sKUVIPvi/A5vC7IuDRX/Rn4B4v8OyP3loHpdwHOh8+g71HgR6AO3gh4a" + "44E4HY0EnRTEx7AbqqKWuDTuhr/g/qQu8eG1gPFU0gFQZyNSk+g4P0S5O+Qc5LPGgPoXkPPQJpI2gP0WgH/NIc2BOoB/7YAZ" + "MaQaaUbGQN5aQlqRPKQErNiF1CGYnMDH8Vf8Cf+N22IRJ0EV2BgXww4UA/KrApzfBn7qgd084gSeqwO37yDuzIOYdgEs9A6c" + "sy54+C98OOS3pTyGV+HR0NOfb+Vn+QNAv1P5Zs4ArRZBtVBhVAxVg6ozFs6dAtcZaCj6A71AOXADQD/zwQZi0D9QzXaAKDoD" + "0FFtFI/64SlYwNXQd94eEdwf94J3ZaFyqYycaDzOh7cgBSE0AOJKdvwTdUZfuJN3hrwbD/VtEcTBBzqDhb8EVDoD6qVBaCfS" + "kIyj8DW0FWT+DZXANjivHR0DPrzoF1wV18YI5JAT+gfgybg3rgnv2wFPeyBLL4GePhDJNuFrED+24EOABg7B3TnQzVl4vxIw" + "xd+AfXeCL+2E380wFmo4sPIL+B6+iY/i61B7fcXpMP4ESP0baPcFfgn19k5A6/vwZbwY/4qbAw4cj3/HkwBDExyGa+HfQEIN" + "sQR6aYy742kQSfNhDjh7CnjuQOBPxSWhuh6AO8HJ7qPHyAZzauMfYGFX4akYrgZzn0DN74c5Q2F9N5xVwiNhz93AcyWwtNX4" + "FQ7CGWbiIVBP3McZ2AOn7I1H47v4LT6Jx+GyIIVecP4FuD2OAC1UwIPh7S+YAo/FYeQ6OPkUsNhieAR+CLXINuCtLm4C1xP4" + "COyVDeuoAIxsB/uXwCnoOHoEVpADZP8CUNAZdAc9QXHIA56bBHyGg9YzUC7cFFaoB3G6GlBFeOoBZy8Ke/qRgq1YQ270APR4" + "Al1Cr9EhqHJmgkedh7sD4Ge7AGUdQgkoACgsDr1D6VmaL4Jz47xYhhq1DkgtJ1h9KPSaIJGnMOYruoXuocuQxcehsWAzM9Bc" + "NAZNh+sk8M3xsGLm10br0Z/oAljwKRj7Cj2E2BMLfFyD5+3ob5h9Aew4HnoWoo5gZ3HAwQPwoxhA6KPA0r6g56g9VHkR8HQQ" + "bHUVaoUk8KwY4Pwi6oWyoVyoDRoIz0MAL3ohducCPFEc+fg/PJVbsr7LskFzAJ7rhPqiquAPAioP/tMcVYZxAYhxGbw8zHXy" + "q4BAbvJX/BvEtDi+B/LzTOi5Dv47iQ/iXSDa9uUT+TTeizeCyDkK3o4E/LUavPYoP86vwHUt5KVrsO9tvgXmDOBzwdO381kw" + "PhQwc33o6Qy45ht7zt6yCMApeXgG5P9tkJdUHgkY4x92GCiOBSGuMvYOMtMz5oPa2M88gPzesIcQo++xF5AHr8P1NvzugAz4" + "AOgji4fK8SWsYGcCz8utXIV6pwjE+/K8NuxUCCJsLdi9HUScXLBvDog+9Xh96E/KmpON5wSk9Ra4OQVR28841Lqn2XZATKks" + "FNZKhVG3YQcJcFQYd7LPEN3DYJ98gJZdgNMMVpiXhCedpQDHCE4j8gCMT8j6gkmHmvYDcPg16ykF8lECy/wXZD9gqcx/S9Zh" + "fjjwLMFqHDCdwA0WAs0LY3U4T6YMMr+I4jDHBOlk7v+NMajJ3bDmOzj9VxhpByl9YjdBMq9h/ZfsFrsE0jnFjgBCPMk2sFWA" + "HLextWw5mwd36yB7zQe0PIdNAyy/AbLRQshQw9kU6N/H/mDLoKoZAW0zzN4I2Ws0GwLzFrAlMHoYIJe+bCqMmQH9fWHWDMjA" + "2wCH7oCxu2DXJ+wV6O4+aOwVcPMJatFPwNFd6HsE7Snc3QVOrwCufQTvnwLPz2HOY/ae/QSJp8GJ3kCzg4R0kIcGJwsyK+gw" + "G2iVQZ+Y9YWZyhFPgLVTs74ZSwRJPIP93rMvgFlT4PoeUFYCzMWcgEw1kLKPabCeAjoUuBfeuOGdBTK2BTSX+RUagvUj4UmC" + "0UF4p4AeEcjfAfIPQnOBTtJhxwSgL6DTz7DHBzhB5lnuw1nuAd1kf4PMTwFie5aF2U6BPG7AGd8D3YOnk5D/n8Cch9B/HGqb" + "83DuNzD7HNh+LEjkJTxfh/7tgFxOgA7Psr2AGdYB0jkNY87CmL9g5EUY9Qxs9Ro7A5b5AlZ4AatfgBkXofch+ETmzifg/Y0s" + "zu5D73XofZxlH09h/AewoR8gtUzJpcPZvf/5WVrWd3SMUZCaAXWuASdnIBkdbA+BNEx4MqE/CM0DErHDDCfc+0G6Bsz8l4ws" + "+8xcReaZI3WwXpYlvUSQpi9rTS9I8SM0b9a85CzNpWRpiAK+/Q7c2WFU5peCOoxxQKOgEQY7uWCcEzTmyfKLn2BdicBFpgc4" + "4ZoEazmybMYH751gHQ6Yk8m9BnNSss6dBL0JWfvHAb2DnTNl8SVrpWSYw4CLf79htIK1RPIoiFyleTGoggtlURmIKqXA54tA" + "1V8BYkwT3hjiSQ2oResDzmzD2/JWvDn8doZ4GcMH8t+zrkP5CIiM0wHrTAGaDL9zAN8tgjYPouVivgqq240QRxfyJXC/EbDR" + "Zoihq/kGiMKxgJNO8YOAy49CXL4FNdRtoJtQDb/g7wD3v4Xfp0AvIYa/4E/+o8fQnkM9/5rH8w8Q239Cre+CPGEHvOOG2O/M" + "ujqA0uDXBe8/8s8w7itP4Mk8Ba6JWTM8PIn/gJYIfZlfun6AFd/Avp8z/44aKB5mpMCoVOj5B3Z9Du/igJeHwN8d4OE1PL3L" + "4vMb7O3nBmA9g5tQqQazyOAUWSFXyUgEnGaDKisfygF1iIwsKArlRjmhT0SZowXokSEneoBnDZ4yv0PWss4QhDU1HgB+0+HZ" + "CyfL/NtqB/xqWd/oemBfjWd+s4wgQ2a+C3IF8imGJy/wYoV9MneKhL0iUQHA4uWBiqKCgDCrZH2pXAdQaUVUKeu75frQakNG" + "rQitMqoAWLMkUGkYWzyLMr91LoTyojyQo3PBunlh5QhYNxKFoXDYQYLz/PvVNAb+vf/x6wPOU7Iknwb3buA+Fe7c8NYP5M3i" + "WoNz/vsHAW4lsIqUJbd/v8KWQH4yXKX/VpeyrlAI//elNs36Jf/d/89vuDMl+b+/7/73G+/MHTL34P9jv/97/ffd/3369/d/" + "AYxHlHJ2PgAA" +) + + +@lru_cache(maxsize=1) +def ready_trigger_pcm() -> bytes: + return gzip.decompress(base64.b64decode(READY_TRIGGER_PCM_16KHZ_MONO_GZIP_B64)) diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 9f18b669124..c426714a1bd 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -1300,16 +1300,15 @@ class BaseLLMHTTPHandler: if client is None or not isinstance(client, HTTPHandler): client = _get_httpx_client() + json_data = data if files is None and isinstance(data, dict) else None + try: - # Make the POST request - clean and simple, always use data and files response = client.post( url=complete_url, headers=headers, - data=data, + data=data if json_data is None else None, files=files, - json=( - data if files is None and isinstance(data, dict) else None - ), # Use json param only when no files and data is dict + json=json_data, timeout=timeout, ) except Exception as e: @@ -1373,16 +1372,15 @@ class BaseLLMHTTPHandler: else: async_httpx_client = client + json_data = data if files is None and isinstance(data, dict) else None + try: - # Make the async POST request - clean and simple, always use data and files response = await async_httpx_client.post( url=complete_url, headers=headers, - data=data, + data=data if json_data is None else None, files=files, - json=( - data if files is None and isinstance(data, dict) else None - ), # Use json param only when no files and data is dict + json=json_data, timeout=timeout, ) except Exception as e: @@ -1986,7 +1984,8 @@ class BaseLLMHTTPHandler: api_base=api_base, ) - headers = update_headers_with_filtered_beta(headers=headers, provider=custom_llm_provider) + if anthropic_messages_provider_config.should_filter_anthropic_beta_headers(): + headers = update_headers_with_filtered_beta(headers=headers, provider=custom_llm_provider) logging_obj.update_from_kwargs( kwargs=kwargs, @@ -2083,12 +2082,18 @@ class BaseLLMHTTPHandler: initial_response: Union[AsyncIterator, AnthropicMessagesResponse] if stream: + from litellm.llms.anthropic.experimental_pass_through.messages.streaming_iterator import ( + AnthropicMessagesStreamingResponse, + anthropic_messages_stream_hidden_params, + ) + completion_stream = anthropic_messages_provider_config.get_async_streaming_response_iterator( model=model, httpx_response=response, request_body=request_body, litellm_logging_obj=logging_obj, ) + stream_hidden_params = anthropic_messages_stream_hidden_params(response.headers) if not self._has_agentic_completion_hook(logging_obj): # No callback overrides async_should_run_agentic_loop, so the @@ -2096,7 +2101,10 @@ class BaseLLMHTTPHandler: # and rebuilding the response from SSE at end-of-stream to call # hooks that all return (False, {}). Stream through directly and # skip that per-chunk + end-of-stream overhead. - return completion_stream + return AnthropicMessagesStreamingResponse( + completion_stream=completion_stream, + hidden_params=stream_hidden_params, + ) from litellm.llms.anthropic.experimental_pass_through.messages.agentic_streaming_iterator import ( AgenticAnthropicStreamingIterator, @@ -2111,9 +2119,12 @@ class BaseLLMHTTPHandler: anthropic_messages_optional_request_params=anthropic_messages_optional_request_params, logging_obj=logging_obj, custom_llm_provider=custom_llm_provider, - kwargs=kwargs, + kwargs={**kwargs, "api_key": api_key} if api_key else kwargs, + ) + return AnthropicMessagesStreamingResponse( + completion_stream=initial_response, + hidden_params=stream_hidden_params, ) - return initial_response else: initial_response = anthropic_messages_provider_config.transform_anthropic_messages_response( model=model, @@ -2121,6 +2132,10 @@ class BaseLLMHTTPHandler: logging_obj=logging_obj, ) + # Inject api_key into kwargs so follow-up calls in agentic hooks can + # authenticate. api_key is a named param here (not in kwargs), so + # _prepare_followup_kwargs would miss it otherwise. + kwargs_for_agentic = {**kwargs, "api_key": api_key} if api_key else kwargs # Call agentic completion hooks (non-streaming path only) final_response = await self._call_agentic_completion_hooks( response=initial_response, @@ -2131,7 +2146,7 @@ class BaseLLMHTTPHandler: logging_obj=logging_obj, stream=False, custom_llm_provider=custom_llm_provider, - kwargs=kwargs, + kwargs=kwargs_for_agentic, ) return self._maybe_wrap_in_fake_stream( @@ -2897,6 +2912,7 @@ class BaseLLMHTTPHandler: try: response = sync_httpx_client.get(url=url, headers=headers, params=data) + response.raise_for_status() except Exception as e: raise self._handle_error( e=e, @@ -2968,9 +2984,9 @@ class BaseLLMHTTPHandler: try: response = await async_httpx_client.get(url=url, headers=headers, params=data) - + response.raise_for_status() except Exception as e: - verbose_logger.exception(f"Error retrieving response: {e}") + verbose_logger.debug(f"Error retrieving response: {e}") raise self._handle_error( e=e, provider_config=responses_api_provider_config, @@ -3061,6 +3077,7 @@ class BaseLLMHTTPHandler: try: response = sync_httpx_client.get(url=url, headers=headers, params=params) + response.raise_for_status() except Exception as e: raise self._handle_error(e=e, provider_config=responses_api_provider_config) @@ -3134,6 +3151,7 @@ class BaseLLMHTTPHandler: try: response = await async_httpx_client.get(url=url, headers=headers, params=params) + response.raise_for_status() except Exception as e: raise self._handle_error(e=e, provider_config=responses_api_provider_config) @@ -4720,6 +4738,13 @@ class BaseLLMHTTPHandler: except Exception as e: raise self._handle_error(e=e, provider_config=provider_config) + if response.status_code >= 400: + raise provider_config.get_error_class( + error_message=response.text, + status_code=response.status_code, + headers=response.headers, + ) + return provider_config.transform_file_content_response( raw_response=response, logging_obj=logging_obj, @@ -4776,6 +4801,13 @@ class BaseLLMHTTPHandler: except Exception as e: raise self._handle_error(e=e, provider_config=provider_config) + if response.status_code >= 400: + raise provider_config.get_error_class( + error_message=response.text, + status_code=response.status_code, + headers=response.headers, + ) + return provider_config.transform_file_content_response( raw_response=response, logging_obj=logging_obj, diff --git a/litellm/llms/dashscope/cost_calculator.py b/litellm/llms/dashscope/cost_calculator.py index 2f710d78126..2732b97cd35 100644 --- a/litellm/llms/dashscope/cost_calculator.py +++ b/litellm/llms/dashscope/cost_calculator.py @@ -7,6 +7,7 @@ Handles tiered pricing and prompt caching scenarios. from dataclasses import dataclass from typing import List, Optional, Tuple +from litellm.litellm_core_utils.llm_cost_calc.tiered_pricing import calculate_tiered_cost from litellm.types.utils import ModelInfo, Usage from litellm.utils import get_model_info @@ -42,80 +43,6 @@ def _extract_token_breakdown(usage: Usage) -> TokenBreakdown: return TokenBreakdown(text_tokens, cached_tokens, completion_tokens, reasoning_tokens) -def _calculate_tiered_cost( - tokens: int, - tiered_pricing: List[dict], - cost_key: str, - fallback_cost_key: Optional[str] = None, -) -> float: - """ - Calculate cost for a given number of tokens based on a true tiered pricing structure. - - This function iterates through sorted pricing tiers, calculates the cost for the - number of tokens that fall into each tier's range, and sums them up to get the total cost. - - Args: - tokens (int): The total number of tokens to calculate the cost for. - tiered_pricing (List[dict]): A list of dictionaries, where each dictionary - represents a pricing tier. - cost_key (str): The key in the tier dictionary that holds the per-token cost - (e.g., 'input_cost_per_token'). - fallback_cost_key (Optional[str], optional): A fallback key to use if the - primary `cost_key` is not found in a tier. Defaults to None. - - Returns: - float: The total calculated cost for the given tokens. - - Example: - >>> tiered_pricing = [ - ... {"range": [0, 100000], "input_cost_per_token": 0.0001}, - ... {"range": [100000, 500000], "input_cost_per_token": 0.00005}, - ... ] - - Calculating cost for 150,000 tokens: - (100,000 * 0.0001) + (50,000 * 0.00005) = $12.5 - """ - if not tiered_pricing or tokens <= 0: - return 0.0 - - total_cost = 0.0 - tokens_processed = 0 - - sorted_tiers = sorted(tiered_pricing, key=lambda x: x.get("range", [0, 0])[0]) - - for tier in sorted_tiers: - if tokens_processed >= tokens: - break - - tier_range = tier.get("range", []) - if len(tier_range) != 2: - continue - - range_start, range_end = tier_range - - if tokens <= range_start: - continue - - tier_start = max(range_start, tokens_processed) - tier_end = min(range_end, tokens) - - if tier_end > tier_start: - tokens_in_tier = tier_end - tier_start - cost_per_token = tier.get(cost_key) or tier.get(fallback_cost_key, 0) - total_cost += tokens_in_tier * cost_per_token - tokens_processed = tier_end - - # After loop, check if any tokens remain (i.e., tokens > highest tier's end range) - # and charge them at the last tier's rate. - if tokens_processed < tokens and sorted_tiers: - last_tier = sorted_tiers[-1] - remaining_tokens = tokens - tokens_processed - cost_per_token = last_tier.get(cost_key) or last_tier.get(fallback_cost_key, 0) - total_cost += remaining_tokens * cost_per_token - - return total_cost - - def _calculate_prompt_cost( breakdown: TokenBreakdown, model_info: ModelInfo, @@ -123,12 +50,12 @@ def _calculate_prompt_cost( ) -> float: """Calculate total prompt cost including cached tokens.""" if tiered_pricing: - text_cost = _calculate_tiered_cost( + text_cost = calculate_tiered_cost( tokens=breakdown.text_tokens, tiered_pricing=tiered_pricing, cost_key="input_cost_per_token", ) - cache_cost = _calculate_tiered_cost( + cache_cost = calculate_tiered_cost( tokens=breakdown.cached_tokens, tiered_pricing=tiered_pricing, cost_key="cache_read_input_token_cost", @@ -155,12 +82,12 @@ def _calculate_completion_cost( ) -> float: """Calculate total completion cost including reasoning tokens.""" if tiered_pricing: - completion_cost = _calculate_tiered_cost( + completion_cost = calculate_tiered_cost( tokens=breakdown.completion_tokens, tiered_pricing=tiered_pricing, cost_key="output_cost_per_token", ) - reasoning_cost = _calculate_tiered_cost( + reasoning_cost = calculate_tiered_cost( tokens=breakdown.reasoning_tokens, tiered_pricing=tiered_pricing, cost_key="output_cost_per_reasoning_token", diff --git a/litellm/llms/databricks/chat/transformation.py b/litellm/llms/databricks/chat/transformation.py index ba8c312ea51..9c05899c719 100644 --- a/litellm/llms/databricks/chat/transformation.py +++ b/litellm/llms/databricks/chat/transformation.py @@ -181,6 +181,10 @@ class DatabricksConfig(DatabricksBase, OpenAILikeChatConfig, AnthropicConfig): if key != "self" and value is not None: setattr(self.__class__, key, value) + @property + def custom_llm_provider(self) -> Optional[str]: + return "databricks" + @classmethod def get_config(cls): return super().get_config() @@ -372,6 +376,7 @@ class DatabricksConfig(DatabricksBase, OpenAILikeChatConfig, AnthropicConfig): mapped_thinking = AnthropicConfig._map_reasoning_effort( reasoning_effort=reasoning_effort_value, model=model, + custom_llm_provider="databricks", llm_provider="databricks", ) if mapped_thinking is None: @@ -379,7 +384,7 @@ class DatabricksConfig(DatabricksBase, OpenAILikeChatConfig, AnthropicConfig): optional_params.pop("output_config", None) else: optional_params["thinking"] = mapped_thinking - if AnthropicConfig._is_adaptive_thinking_model(model): + if AnthropicConfig._is_adaptive_thinking_model(model, "databricks"): mapped_effort: Optional[str] = None if isinstance(reasoning_effort_value, str): mapped_effort = REASONING_EFFORT_TO_OUTPUT_CONFIG_EFFORT.get(reasoning_effort_value) diff --git a/litellm/llms/deepseek/chat/transformation.py b/litellm/llms/deepseek/chat/transformation.py index 7a548136f2a..525de1476e2 100644 --- a/litellm/llms/deepseek/chat/transformation.py +++ b/litellm/llms/deepseek/chat/transformation.py @@ -35,7 +35,9 @@ class DeepSeekChatConfig(OpenAIGPTConfig): Map OpenAI params to DeepSeek params. Handles `thinking` and `reasoning_effort` parameters for DeepSeek reasoner models. - DeepSeek only supports `{"type": "enabled"}` - no budget_tokens like Anthropic. + DeepSeek supports `{"type": "enabled"}` and `{"type": "disabled"}` - no budget_tokens + like Anthropic. `reasoning_effort="none"` is the OpenAI-style way to ask for thinking + off, so it maps to `{"type": "disabled"}`; any other effort keeps thinking on. Reference: https://api-docs.deepseek.com/guides/thinking_mode """ @@ -47,15 +49,13 @@ class DeepSeekChatConfig(OpenAIGPTConfig): thinking_value = optional_params.pop("thinking", None) reasoning_effort = optional_params.pop("reasoning_effort", None) - # Handle thinking parameter - only accept {"type": "enabled"} - if thinking_value is not None: - if isinstance(thinking_value, dict) and thinking_value.get("type") == "enabled": - # DeepSeek only accepts {"type": "enabled"}, ignore budget_tokens - optional_params["thinking"] = {"type": "enabled"} + # Handle thinking parameter - accept both enabled and disabled, ignore budget_tokens + if isinstance(thinking_value, dict) and thinking_value.get("type") in ("enabled", "disabled"): + optional_params["thinking"] = {"type": thinking_value["type"]} - # Handle reasoning_effort - map to thinking enabled - elif reasoning_effort is not None and reasoning_effort != "none": - optional_params["thinking"] = {"type": "enabled"} + # Otherwise fall back to reasoning_effort: "none" disables, anything else enables + elif reasoning_effort is not None: + optional_params["thinking"] = {"type": "disabled" if reasoning_effort == "none" else "enabled"} return optional_params diff --git a/litellm/llms/gdc/__init__.py b/litellm/llms/gdc/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/litellm/llms/gdc/chat/__init__.py b/litellm/llms/gdc/chat/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/litellm/llms/gdc/chat/transformation.py b/litellm/llms/gdc/chat/transformation.py new file mode 100644 index 00000000000..61631920a64 --- /dev/null +++ b/litellm/llms/gdc/chat/transformation.py @@ -0,0 +1,285 @@ +""" +GDC Gemini chat completion transformation +""" + +import json +import os +import re +import threading +from typing import Any, Final +from urllib.parse import urlsplit + +import litellm +from litellm.llms.openai_like.chat.transformation import OpenAILikeChatConfig + + +class GDCGeminiConfig(OpenAILikeChatConfig): + supports_vertex_params: bool = True # Tell LiteLLM utilities not to strip vertex_ params + _GDCH_CREDENTIAL_TYPE: Final[str] = "gdch_service_account" + _PATH_ID_PATTERN: Final[re.Pattern[str]] = re.compile(r"^[a-zA-Z0-9_-]+$") + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self._creds_lock = threading.Lock() + self._gdch_creds_cache: dict = {} + + def get_supported_openai_params(self, model: str) -> list: + return [ + "vertex_project", + "vertex_location", + ] + super().get_supported_openai_params(model) + + def _resolve_project(self, optional_params: dict, litellm_params: dict) -> str | None: + return ( + litellm_params.get("vertex_project") + or litellm_params.get("vertex_ai_project") + or getattr(litellm, "vertex_project", None) + or optional_params.get("vertex_project") + or optional_params.get("vertex_ai_project") + ) + + def _resolve_location(self, optional_params: dict, litellm_params: dict) -> str | None: + return ( + litellm_params.get("vertex_location") + or litellm_params.get("vertex_ai_location") + or getattr(litellm, "vertex_location", None) + or optional_params.get("vertex_location") + or optional_params.get("vertex_ai_location") + ) + + def _effective_project(self, api_base: str, optional_params: dict, litellm_params: dict) -> str | None: + match = re.search(r"/v1/projects/([^/]+)", api_base) + if match: + return match.group(1) + return self._resolve_project(optional_params, litellm_params) + + def _validate_path_id(self, value: str, field: str, model: str) -> str: + if not self._PATH_ID_PATTERN.match(value): + raise litellm.utils.AuthenticationError( + message=f"{field} must be a plain identifier of letters, digits, hyphens or underscores.", + llm_provider="gdc", + model=model, + ) + return value + + def get_complete_url( + self, + api_base: str | None, + api_key: str | None, + model: str, + optional_params: dict, + litellm_params: dict, + stream: bool | None = None, + ) -> str: + api_base = api_base or litellm.gdc_api_base or litellm.api_base + if not api_base: + raise litellm.utils.AuthenticationError( + message="api_base/host is required for GDC Gemini. Please set it or pass it.", + llm_provider="gdc", + model=model, + ) + + if not api_base.startswith("http"): + api_base = f"https://{api_base}" + + api_base = api_base.rstrip("/") + + if "/v1/projects/" in api_base: + return api_base + + project = self._resolve_project(optional_params, litellm_params) + + if not project: + raise litellm.utils.AuthenticationError( + message="project is required for GDC Gemini. Please pass vertex_project.", + llm_provider="gdc", + model=model, + ) + + location = self._resolve_location(optional_params, litellm_params) + + if not location: + raise litellm.utils.AuthenticationError( + message="location is required for GDC Gemini. Please pass vertex_location.", + llm_provider="gdc", + model=model, + ) + + project = self._validate_path_id(project, "vertex_project", model) + location = self._validate_path_id(location, "vertex_location", model) + + return f"{api_base}/v1/projects/{project}/locations/{location}/chat/completions" + + def _read_env_bool(self, val: Any, env_var: str, default: bool = True) -> bool | str: + def _parse(s: str) -> bool | str: + cleaned = s.strip().lower() + if cleaned in ("false", "0", "no", "off"): + return False + if cleaned in ("true", "1", "yes", "on"): + return True + return s + + if val is not None: + if isinstance(val, str): + return _parse(val) + return val + + _env_val = os.getenv(env_var) + if _env_val is None: + return default + return _parse(_env_val) + + def _fetch_auth(self, gdch_creds: Any, ssl_verify: bool | str) -> None: + import requests + from google.auth.transport import requests as auth_requests + + auth_session = requests.Session() + auth_session.verify = ssl_verify + auth_request = auth_requests.Request(session=auth_session) + gdch_creds.refresh(auth_request) + + def _cached_fetch_token(self, creds: Any, audience: str, ssl_verify: bool | str, api_key: str | None = None) -> str: + # Key cache by both audience and credential identity to prevent cross-caller contamination + cache_key = (audience.rstrip("/"), api_key or str(id(creds))) + + with self._creds_lock: + if cache_key not in self._gdch_creds_cache: + self._gdch_creds_cache[cache_key] = creds.with_gdch_audience(audience.rstrip("/")) + + gdch_creds = self._gdch_creds_cache[cache_key] + + if not getattr(gdch_creds, "valid", False) or not getattr(gdch_creds, "token", None): + self._fetch_auth(gdch_creds, ssl_verify) + + token = gdch_creds.token + + return token + + def _load_creds_from_key(self, api_key: str) -> tuple[Any, bool]: + import google.auth + + try: + json_obj = json.loads(api_key) + except json.JSONDecodeError: + return None, False + if not isinstance(json_obj, dict) or json_obj.get("type") != self._GDCH_CREDENTIAL_TYPE: + raise ValueError( + "GDC only accepts a GDCH service account credential as a JSON api_key " + '(expected "type": "gdch_service_account"). Other Google credential types are ' + "rejected so their token or external-account endpoints cannot drive server-side requests." + ) + creds, _ = google.auth.load_credentials_from_dict(json_obj) + return creds, True + + def validate_environment( + self, + headers: dict, + model: str, + messages: list[Any], + optional_params: dict, + litellm_params: dict, + api_key: str | None = None, + api_base: str | None = None, + ) -> dict: + import google.auth.exceptions + + api_base = api_base or litellm.gdc_api_base or litellm.api_base + if not api_base: + raise litellm.utils.AuthenticationError( + message="api_base/host is required for GDC Gemini. Please set it or pass it.", + llm_provider="gdc", + model=model, + ) + + if not api_key: + raise litellm.utils.AuthenticationError( + message="api_key is required for GDC Gemini. Please pass your service account string or token as the api_key.", + llm_provider="gdc", + model=model, + ) + + project = self._effective_project(api_base, optional_params, litellm_params) + if not project: + raise litellm.utils.AuthenticationError( + message="project is required for GDC Gemini. Please pass vertex_project.", + llm_provider="gdc", + model=model, + ) + project = self._validate_path_id(project, "vertex_project", model) + + _audience_parts = urlsplit(api_base if api_base.startswith("http") else f"https://{api_base}") + audience = f"{_audience_parts.scheme}://{_audience_parts.netloc}" + + try: + creds, is_service_account = self._load_creds_from_key(api_key) + except ( + google.auth.exceptions.GoogleAuthError, + ValueError, + TypeError, + KeyError, + AttributeError, + ) as e: + raise litellm.utils.AuthenticationError( + message=f"Failed to load service account credentials from api_key: {str(e)}", + llm_provider="gdc", + model=model, + ) from e + + if creds is not None: + ssl_verify = self._read_env_bool(litellm_params.get("ssl_verify"), "SSL_VERIFY", default=True) + if self._read_env_bool(litellm_params.get("gdc_token_caching"), "GDC_TOKEN_CACHING", default=False): + token = self._cached_fetch_token(creds, audience, ssl_verify, api_key) + else: + gdch_creds = creds.with_gdch_audience(audience) + self._fetch_auth(gdch_creds, ssl_verify) + token = gdch_creds.token + headers["Authorization"] = f"Bearer {token}" + + if "Authorization" not in headers and not is_service_account: + headers["Authorization"] = f"Bearer {api_key}" + + # Standardize necessary metadata headers + if "content-type" not in headers and "Content-Type" not in headers: + headers["Content-Type"] = "application/json" + + stale_quota_headers = tuple(h for h in headers if h.lower() == "x-goog-user-project") + for stale in stale_quota_headers: + headers.pop(stale, None) + headers["x-goog-user-project"] = f"projects/{project}" + + return headers + + def transform_request( + self, + model: str, + messages: list[Any], + optional_params: dict, + litellm_params: dict, + headers: dict, + ) -> dict: + """ + Transforms the request to the GDC provider + """ + if model.startswith("gdc/"): + model = model.split("/", 1)[1] + + data = super().transform_request( + model=model, + messages=messages, + optional_params=optional_params, + litellm_params=litellm_params, + headers=headers, + ) + + # Remove extra params used for routing/auth + for param in [ + "vertex_project", + "vertex_ai_project", + "vertex_location", + "vertex_ai_location", + "ssl_verify", + "gdc_token_caching", + ]: + data.pop(param, None) + + return data diff --git a/litellm/llms/github_copilot/messages/__init__.py b/litellm/llms/github_copilot/messages/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/litellm/llms/github_copilot/messages/transformation.py b/litellm/llms/github_copilot/messages/transformation.py new file mode 100644 index 00000000000..4d7b003c48f --- /dev/null +++ b/litellm/llms/github_copilot/messages/transformation.py @@ -0,0 +1,122 @@ +from typing import Any, Optional + +from litellm.exceptions import AuthenticationError +from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, +) + +from ..authenticator import Authenticator +from ..common_utils import ( + DEFAULT_GITHUB_COPILOT_API_BASE, + GetAPIKeyError, + get_copilot_default_headers, +) + +_MESSAGES_PROXY_API_VERSION = "2026-06-01" + + +class GithubCopilotAnthropicMessagesConfig(AnthropicMessagesConfig): + """ + GitHub Copilot implementation of Anthropic messages API. + Routes requests to Copilot's /v1/messages endpoint with appropriate authentication and headers. + """ + + def __init__(self) -> None: + super().__init__() + self.authenticator = Authenticator() + + @property + def custom_llm_provider(self) -> Optional[str]: + return "github_copilot" + + def handles_web_search_natively(self) -> bool: + """ + Copilot's /v1/messages endpoint does not execute ``web_search`` tools, so + the interception handler must short-circuit web-search-only requests + instead of routing them here. + """ + return False + + def should_filter_anthropic_beta_headers(self) -> bool: + """ + Copilot's /v1/messages is a native Anthropic Messages passthrough, so + ``anthropic-beta`` values injected by ``_update_headers_with_anthropic_beta`` + (context_management, structured outputs, ...) must reach the upstream + verbatim. The default provider-scoped filter would drop them because + github_copilot has no entry in ``anthropic_beta_headers_config.json``. + """ + return False + + def validate_anthropic_messages_environment( + self, + headers: dict, + model: str, + messages: list[Any], + optional_params: dict, + litellm_params: dict, + api_key: Optional[str] = None, + api_base: Optional[str] = None, + ) -> tuple[dict, Optional[str]]: + """ + Validate environment for GitHub Copilot and add Copilot-specific headers. + + The caller-supplied ``api_base`` is intentionally ignored. Routing this + request anywhere other than the authenticated Copilot endpoint would + leak the Copilot bearer token to a caller-controlled URL. + """ + # Always use the Copilot endpoint resolved from the authenticated + # session, never the caller-supplied api_base. rstrip so a + # tenant-specific base with a trailing slash does not yield a + # double-slash URL once "/v1/messages" is appended downstream. + dynamic_api_base = (self.authenticator.get_api_base() or DEFAULT_GITHUB_COPILOT_API_BASE).rstrip("/") + try: + dynamic_api_key = self.authenticator.get_api_key() + except GetAPIKeyError as e: + raise AuthenticationError( + model=model, + llm_provider="github_copilot", + message=str(e), + ) + + # Merge Copilot headers with provided headers + copilot_headers = get_copilot_default_headers(dynamic_api_key) + for key, value in copilot_headers.items(): + if key not in headers: + headers[key] = value + + headers["openai-intent"] = "messages-proxy" + headers["x-interaction-type"] = "messages-proxy" + headers["x-github-api-version"] = _MESSAGES_PROXY_API_VERSION + + if "anthropic-version" not in headers: + headers["anthropic-version"] = "2023-06-01" + + headers = self._update_headers_with_anthropic_beta( + headers, optional_params, custom_llm_provider="github_copilot" + ) + + return headers, dynamic_api_base + + def get_complete_url( + self, + api_base: Optional[str], + api_key: Optional[str], + model: str, + optional_params: dict, + litellm_params: dict, + stream: Optional[bool] = None, + ) -> str: + """ + Return the complete URL for GitHub Copilot /v1/messages endpoint. + + ``api_base`` here is the value already resolved by + ``validate_anthropic_messages_environment`` (the authenticated Copilot + host), not the raw caller-supplied base — that one is discarded there to + avoid leaking the Copilot bearer token to a caller-controlled URL. We + reuse it to avoid a second authenticator read, falling back to a fresh + resolution only if it was not provided. + """ + resolved = (api_base or self.authenticator.get_api_base() or DEFAULT_GITHUB_COPILOT_API_BASE).rstrip("/") + if not resolved.endswith("/v1/messages"): + resolved = f"{resolved}/v1/messages" + return resolved diff --git a/litellm/llms/oci/chat/cohere.py b/litellm/llms/oci/chat/cohere.py index 3661ac908d2..ca85a6309d7 100644 --- a/litellm/llms/oci/chat/cohere.py +++ b/litellm/llms/oci/chat/cohere.py @@ -110,7 +110,7 @@ def adapt_messages_to_cohere_standard( tool_calls: Optional[List[CohereToolCall]] = None if role == "assistant" and msg.get("tool_calls"): # type: ignore[union-attr,typeddict-item] tool_calls = [] - for tc in msg["tool_calls"]: # type: ignore[union-attr,typeddict-item] + for tc in msg["tool_calls"]: # pyright: ignore[reportOptionalIterable] # truthiness check above rules out None raw_arguments: Any = tc.get("function", {}).get("arguments", {}) if isinstance(raw_arguments, str): try: diff --git a/litellm/llms/ollama/chat/transformation.py b/litellm/llms/ollama/chat/transformation.py index 3152ded2367..694f8cdd6c2 100644 --- a/litellm/llms/ollama/chat/transformation.py +++ b/litellm/llms/ollama/chat/transformation.py @@ -363,7 +363,11 @@ class OllamaChatConfig(BaseConfig): response_json_message["reasoning_content"] = reasoning_content response_json_message["content"] = content - if request_data.get("format", "") == "json" and litellm_params.get("function_name") is not None: + if ( + request_data.get("format", "") == "json" + and litellm_params.get("function_name") is not None + and response_json_message is not None + ): function_call = json.loads(response_json_message["content"]) message = litellm.Message( content=None, diff --git a/litellm/llms/openai/chat/gpt_transformation.py b/litellm/llms/openai/chat/gpt_transformation.py index 396ad5b105e..f2498c0a7e2 100644 --- a/litellm/llms/openai/chat/gpt_transformation.py +++ b/litellm/llms/openai/chat/gpt_transformation.py @@ -2,6 +2,7 @@ Support for gpt model family """ +import json from typing import ( TYPE_CHECKING, Any, @@ -782,8 +783,30 @@ class OpenAIChatCompletionStreamingHandler(BaseModelResponseIterator): delta["reasoning_content"] = delta.pop("reasoning") return choices + @staticmethod + def _extract_error_from_chunk(chunk: dict) -> Optional[tuple[str, int]]: + """OpenAI-compatible backends (vLLM, sglang) can return an HTTP 200 + stream whose body carries an error payload, e.g. + ``data: {"error": {"message": "...", "code": 400}}``.""" + error = chunk.get("error") + if not error: + return None + if not isinstance(error, dict): + return str(error), 500 + message = error.get("message") + code = error.get("code") + status_code = code if isinstance(code, int) and 400 <= code < 600 else 500 + return (message if isinstance(message, str) else json.dumps(error)), status_code + def chunk_parser(self, chunk: dict) -> ModelResponseStream: try: + error_details = self._extract_error_from_chunk(chunk) + if error_details is not None: + error_message, error_status_code = error_details + raise OpenAIError( + status_code=error_status_code, + message=error_message, + ) choices = chunk.get("choices", []) choices = self._map_reasoning_to_reasoning_content(choices) diff --git a/litellm/llms/openai/chat/guardrail_translation/handler.py b/litellm/llms/openai/chat/guardrail_translation/handler.py index c7a49a2e47f..a6b6a6267c3 100644 --- a/litellm/llms/openai/chat/guardrail_translation/handler.py +++ b/litellm/llms/openai/chat/guardrail_translation/handler.py @@ -716,7 +716,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): elif isinstance(content, list) and content_idx_optional is not None: # Replace specific text item in list content - choice.message.content[content_idx_optional]["text"] = guardrail_response # type: ignore + content[content_idx_optional]["text"] = guardrail_response async def _apply_guardrail_responses_to_output_tool_calls( self, diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index 6ac33ffa44a..093dffccac0 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -45,6 +45,7 @@ from litellm.types.llms.openai import ( AllMessageValues, ChatCompletionToolCallChunk, ChatCompletionToolParam, + ResponsesAPIStreamEvents, ) from litellm.types.responses.main import ( GenericResponseOutputItem, @@ -196,12 +197,13 @@ class OpenAIResponsesHandler(BaseTranslation): return data def extract_request_tool_names(self, data: dict) -> List[str]: - """Extract tool names from Responses API request (tools[].name for function, tools[].server_label for mcp).""" + """Extract tool names from Responses API request (tools[].name for function + and custom, tools[].server_label for mcp).""" names: List[str] = [] for tool in data.get("tools") or []: if not isinstance(tool, dict): continue - if tool.get("type") == "function" and tool.get("name"): + if tool.get("type") in ("function", "custom") and tool.get("name"): names.append(str(tool["name"])) elif tool.get("type") == "mcp" and tool.get("server_label"): names.append(str(tool["server_label"])) @@ -586,7 +588,14 @@ class OpenAIResponsesHandler(BaseTranslation): """ Check if the streaming has ended. """ - return all(response.choices[0].finish_reason is not None for response in responses_so_far) + if not responses_so_far: + return False + terminal_types = { + ResponsesAPIStreamEvents.RESPONSE_COMPLETED.value, + ResponsesAPIStreamEvents.RESPONSE_FAILED.value, + ResponsesAPIStreamEvents.RESPONSE_INCOMPLETE.value, + } + return responses_so_far[-1].get("type") in terminal_types def get_streaming_string_so_far(self, responses_so_far: List[Any]) -> str: """ diff --git a/litellm/llms/openai_like/dynamic_config.py b/litellm/llms/openai_like/dynamic_config.py index 3c763ed9b9b..31c913d5d4e 100644 --- a/litellm/llms/openai_like/dynamic_config.py +++ b/litellm/llms/openai_like/dynamic_config.py @@ -91,7 +91,7 @@ def create_config_class(provider: SimpleProviderConfig): def get_supported_openai_params(self, model: str) -> list: """Get supported OpenAI params, excluding tool-related params for models that don't support function calling.""" - from litellm.utils import supports_function_calling + from litellm.utils import supports_function_calling, supports_reasoning supported_params = super().get_supported_openai_params(model=model) @@ -113,6 +113,10 @@ def create_config_class(provider: SimpleProviderConfig): f"function calling — removed tool-related params from supported params." ) + _supports_reasoning = supports_reasoning(model=model, custom_llm_provider=provider.slug) + if _supports_reasoning and "reasoning_effort" not in supported_params: + supported_params.append("reasoning_effort") + return supported_params def map_openai_params( diff --git a/litellm/llms/openai_like/messages/__init__.py b/litellm/llms/openai_like/messages/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py new file mode 100644 index 00000000000..0d593d8d0f4 --- /dev/null +++ b/litellm/llms/openai_like/messages/transformation.py @@ -0,0 +1,138 @@ +from typing import Any, Optional + +import litellm +from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, +) +from litellm.llms.openai_like.json_loader import SimpleProviderConfig +from litellm.secret_managers.main import get_secret_str + +DEFAULT_ANTHROPIC_API_VERSION = "2023-06-01" + + +class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): + """ + Forwards Anthropic /v1/messages requests to an OpenAI-compatible server that + also natively exposes the Anthropic Messages API, with no translation. + + Opted into per deployment via ``model_info.supported_endpoints`` containing + ``"/v1/messages"``. The inbound Anthropic payload (system, cache_control, + thinking, tools, ...) is forwarded essentially unchanged to + ``{api_base}/v1/messages``, so Anthropic-only features that the + Anthropic->OpenAI translation would otherwise drop are preserved. Response + parsing and streaming are inherited from the native Anthropic config. + """ + + def validate_anthropic_messages_environment( + self, + headers: dict[str, str], + model: str, + messages: list[Any], + optional_params: dict, + litellm_params: dict, + api_key: Optional[str] = None, + api_base: Optional[str] = None, + ) -> tuple[dict[str, str], Optional[str]]: + present = {key.lower() for key in headers} + needs_auth = bool(api_key) and "authorization" not in present and "x-api-key" not in present + defaults: dict[str, str] = { + **({"authorization": f"Bearer {api_key}"} if needs_auth else {}), + **({"anthropic-version": DEFAULT_ANTHROPIC_API_VERSION} if "anthropic-version" not in present else {}), + **({"content-type": "application/json"} if "content-type" not in present else {}), + } + combined = {**headers, **defaults} + normalized = { + ("anthropic-beta" if key.lower() == "anthropic-beta" else key): value for key, value in combined.items() + } + merged = self._update_headers_with_anthropic_beta( + headers=normalized, + optional_params=optional_params, + ) + return merged, api_base + + def should_filter_anthropic_beta_headers(self) -> bool: + return False + + def get_complete_url( + self, + api_base: Optional[str], + api_key: Optional[str], + model: str, + optional_params: dict, + litellm_params: dict, + stream: Optional[bool] = None, + ) -> str: + if not api_base: + raise ValueError("api_base is required to forward Anthropic /v1/messages to a native endpoint") + base = api_base.rstrip("/") + if base.endswith("/v1/messages"): + return base + if base.endswith("/v1"): + base = base[: -len("/v1")] + return f"{base}/v1/messages" + + +class JSONProviderAnthropicMessagesConfig(OpenAILikeAnthropicMessagesConfig): + """ + Provider-level native Anthropic Messages passthrough for JSON-configured + OpenAI-compatible providers whose ``supported_endpoints`` in providers.json + includes ``"/v1/messages"``. Resolves the api key and api base from the + provider's configured env vars, then forwards the Anthropic payload + untranslated like ``OpenAILikeAnthropicMessagesConfig``. + """ + + def __init__(self, provider: SimpleProviderConfig): + super().__init__() + self._provider = provider + + @property + def custom_llm_provider(self) -> Optional[str]: + return self._provider.slug + + def should_strip_billing_metadata(self) -> bool: + return True + + def _resolve_api_key(self, api_key: Optional[str]) -> Optional[str]: + return api_key or get_secret_str(self._provider.api_key_env) or litellm.api_key + + def _resolve_api_base(self, api_base: Optional[str]) -> str: + env_api_base = get_secret_str(self._provider.api_base_env) if self._provider.api_base_env else None + return api_base or env_api_base or self._provider.base_url + + def validate_anthropic_messages_environment( + self, + headers: dict[str, str], + model: str, + messages: list[Any], + optional_params: dict, + litellm_params: dict, + api_key: Optional[str] = None, + api_base: Optional[str] = None, + ) -> tuple[dict[str, str], Optional[str]]: + return super().validate_anthropic_messages_environment( + headers=headers, + model=model, + messages=messages, + optional_params=optional_params, + litellm_params=litellm_params, + api_key=self._resolve_api_key(api_key), + api_base=api_base, + ) + + def get_complete_url( + self, + api_base: Optional[str], + api_key: Optional[str], + model: str, + optional_params: dict, + litellm_params: dict, + stream: Optional[bool] = None, + ) -> str: + return super().get_complete_url( + api_base=self._resolve_api_base(api_base), + api_key=api_key, + model=model, + optional_params=optional_params, + litellm_params=litellm_params, + stream=stream, + ) diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index d87346fea70..164100d4194 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -168,6 +168,13 @@ }, "supported_endpoints": ["/v1/chat/completions", "/v1/responses"] }, + "meta": { + "base_url": "https://api.meta.ai/v1", + "api_key_env": "META_API_KEY", + "api_base_env": "META_API_BASE", + "base_class": "openai_gpt", + "supported_endpoints": ["/v1/chat/completions", "/v1/responses", "/v1/messages"] + }, "pinstripes": { "base_url": "https://pinstripes.io/v1", "api_key_env": "PINSTRIPES_API_KEY", diff --git a/litellm/llms/sap/chat/transformation.py b/litellm/llms/sap/chat/transformation.py index d9d0f9bc236..4bf8272a334 100755 --- a/litellm/llms/sap/chat/transformation.py +++ b/litellm/llms/sap/chat/transformation.py @@ -155,7 +155,7 @@ class GenAIHubOrchestrationConfig(OpenAIGPTConfig): def headers(self) -> Dict[str, str]: if self.token_creator is None: self.run_env_setup() - access_token = self.token_creator() # type: ignore + access_token = self.token_creator() # pyright: ignore[reportOptionalCall] # run_env_setup set it or raised return { "Authorization": access_token, "AI-Resource-Group": self.resource_group, diff --git a/litellm/llms/tencent/__init__.py b/litellm/llms/tencent/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/litellm/llms/tencent/chat/__init__.py b/litellm/llms/tencent/chat/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/litellm/llms/tencent/chat/transformation.py b/litellm/llms/tencent/chat/transformation.py new file mode 100644 index 00000000000..4dea0c4b8c7 --- /dev/null +++ b/litellm/llms/tencent/chat/transformation.py @@ -0,0 +1,68 @@ +""" +Translates from OpenAI's `/v1/chat/completions` to Tencent TokenHub's +OpenAI-compatible endpoint. +""" + +from typing import Optional + +from litellm.secret_managers.main import get_secret_str +from litellm.utils import supports_reasoning + +from ...openai.chat.gpt_transformation import OpenAIGPTConfig + + +class TencentChatConfig(OpenAIGPTConfig): + def get_supported_openai_params(self, model: str) -> list: + params = super().get_supported_openai_params(model) + if supports_reasoning(model, custom_llm_provider="tencent"): + params.extend(["thinking", "reasoning_effort"]) + return params + + def map_openai_params( + self, + non_default_params: dict, + optional_params: dict, + model: str, + drop_params: bool, + ) -> dict: + optional_params = super().map_openai_params(non_default_params, optional_params, model, drop_params) + + thinking_value = optional_params.pop("thinking", None) + reasoning_effort = optional_params.pop("reasoning_effort", None) + + if thinking_value is not None: + if isinstance(thinking_value, dict): + optional_params["thinking"] = thinking_value + elif reasoning_effort is not None and reasoning_effort != "none": + optional_params["thinking"] = {"type": "enabled"} + + return optional_params + + def _get_openai_compatible_provider_info( + self, api_base: Optional[str], api_key: Optional[str] + ) -> tuple[Optional[str], Optional[str]]: + api_base = api_base or get_secret_str("TENCENT_API_BASE") or "https://tokenhub-intl.tencentcloudmaas.com/v1" + dynamic_api_key = api_key or get_secret_str("TENCENT_API_KEY") + return api_base, dynamic_api_key + + def get_complete_url( + self, + api_base: Optional[str], + api_key: Optional[str], + model: str, + optional_params: dict, + litellm_params: dict, + stream: Optional[bool] = None, + ) -> str: + if not api_base: + api_base = "https://tokenhub-intl.tencentcloudmaas.com/v1" + + api_base = api_base.rstrip("/") + + if api_base.endswith("/chat/completions"): + return api_base + + if not api_base.endswith("/v1"): + api_base = f"{api_base}/v1" + + return f"{api_base}/chat/completions" diff --git a/litellm/llms/tencent/cost_calculator.py b/litellm/llms/tencent/cost_calculator.py new file mode 100644 index 00000000000..d9aebdc3284 --- /dev/null +++ b/litellm/llms/tencent/cost_calculator.py @@ -0,0 +1,6 @@ +from litellm.litellm_core_utils.llm_cost_calc.utils import generic_cost_per_token +from litellm.types.utils import Usage + + +def cost_per_token(model: str, usage: Usage) -> tuple[float, float]: + return generic_cost_per_token(model=model, usage=usage, custom_llm_provider="tencent") diff --git a/litellm/llms/tencent/messages/__init__.py b/litellm/llms/tencent/messages/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/litellm/llms/tencent/messages/transformation.py b/litellm/llms/tencent/messages/transformation.py new file mode 100644 index 00000000000..e0f13aa9ca4 --- /dev/null +++ b/litellm/llms/tencent/messages/transformation.py @@ -0,0 +1,85 @@ +""" +Tencent Anthropic-compatible messages transformation config. + +Tencent TokenHub exposes an Anthropic-compatible Messages API endpoint +alongside its standard OpenAI-compatible chat completions endpoint. +""" + +from typing import Any, Optional + +import litellm +from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, +) +from litellm.secret_managers.main import get_secret_str + + +class TencentAnthropicMessagesConfig(AnthropicMessagesConfig): + """ + Tencent TokenHub exposes an Anthropic-compatible Messages API. + + Unlike the chat completions endpoint (which uses /v1), the Anthropic + endpoint may use a different base URL. Configure via + TENCENT_ANTHROPIC_API_BASE or TENCENT_API_BASE. + """ + + @property + def custom_llm_provider(self) -> Optional[str]: + return "tencent" + + def should_strip_billing_metadata(self) -> bool: + return True + + @staticmethod + def get_api_key(api_key: Optional[str] = None) -> Optional[str]: + return api_key or get_secret_str("TENCENT_API_KEY") or litellm.api_key + + @staticmethod + def get_api_base(api_base: Optional[str] = None) -> str: + return ( + api_base + or get_secret_str("TENCENT_ANTHROPIC_API_BASE") + or get_secret_str("TENCENT_API_BASE") + or "https://tokenhub-intl.tencentcloudmaas.com" + ) + + def validate_anthropic_messages_environment( + self, + headers: dict, + model: str, + messages: list[Any], + optional_params: dict, + litellm_params: dict, + api_key: Optional[str] = None, + api_base: Optional[str] = None, + ) -> tuple[dict, Optional[str]]: + return super().validate_anthropic_messages_environment( + headers=headers, + model=model, + messages=messages, + optional_params=optional_params, + litellm_params=litellm_params, + api_key=self.get_api_key(api_key=api_key), + api_base=api_base, + ) + + def get_complete_url( + self, + api_base: Optional[str], + api_key: Optional[str], + model: str, + optional_params: dict, + litellm_params: dict, + stream: Optional[bool] = None, + ) -> str: + base_url = self.get_api_base(api_base=api_base).rstrip("/") + + if base_url.endswith("/v1/messages"): + return base_url + + if base_url.endswith("/v1/chat/completions"): + base_url = base_url[: -len("/v1/chat/completions")] + elif base_url.endswith("/v1"): + base_url = base_url[: -len("/v1")] + + return f"{base_url}/v1/messages" diff --git a/litellm/llms/tinyfish/search/transformation.py b/litellm/llms/tinyfish/search/transformation.py index 4b7d38e3661..cef5f9cd02e 100644 --- a/litellm/llms/tinyfish/search/transformation.py +++ b/litellm/llms/tinyfish/search/transformation.py @@ -6,53 +6,42 @@ Docs: https://docs.tinyfish.ai/search-api from __future__ import annotations -from typing import Literal, TypedDict +import json +from typing import Literal from urllib.parse import urlencode import httpx -from pydantic import BaseModel, TypeAdapter, ValidationError +from pydantic import TypeAdapter, ValidationError +from litellm._logging import verbose_logger from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.llms.base_llm.chat.transformation import BaseLLMException from litellm.llms.base_llm.search.transformation import ( BaseSearchConfig, SearchResponse, - SearchResult, ) from litellm.secret_managers.main import get_secret_str - -class _TinyfishSearchRequestRequired(TypedDict): - query: str - - -class TinyfishSearchRequest(_TinyfishSearchRequestRequired, total=False): - location: str - language: str - page: int - include_thumbnail: bool - max_results: int - - -class _TinyfishResultItem(BaseModel, frozen=True): - title: str = "" - url: str = "" - snippet: str = "" - - -class _TinyfishApiResponse(BaseModel, frozen=True): - results: tuple[_TinyfishResultItem, ...] = () - - _UrlEncodableParams = TypeAdapter(dict[str, str | int | bool]) _StrList = TypeAdapter(list[str]) _StrFrozenSet = TypeAdapter(frozenset[str]) _TINYFISH_PARAMS_KEY = "_tinyfish_params" +_TINYFISH_DOCS_URL = "https://docs.tinyfish.ai/search-api" +_TINYFISH_RESULT_CAP = 10 # TinyFish's natural per-page SERP ceiling class TinyfishSearchConfig(BaseSearchConfig): TINYFISH_API_BASE = "https://api.search.tinyfish.ai" + def __init__(self) -> None: + super().__init__() + # Threaded from transform_search_request → transform_search_response so the + # response slice honors the caller's max_results without re-sending it on + # the wire (TinyFish doesn't honor it server-side). Safe because the + # config is instantiated per-call via ProviderConfigManager. + self._caller_max_results: int | None = None + @staticmethod def ui_friendly_name() -> str: return "TinyFish" @@ -97,36 +86,77 @@ class TinyfishSearchConfig(BaseSearchConfig): optional_params: dict[str, object], **kwargs: object, ) -> dict[str, object]: + """ + Transform a LiteLLM search request to TinyFish's querystring format. + + Maps LiteLLM's unified-spec params (see + ``BaseSearchConfig.get_supported_perplexity_optional_params``) to + TinyFish equivalents: + - ``query`` (str or list[str]) → ``query`` (list joined by spaces) + - ``country`` → ``location`` + - ``search_domain_filter`` (list[str]) → folded into the query as + ``() (site:a OR site:b ...)`` (TinyFish has no first-class + field today; see ML-2084 for the planned ``include_domains``) + - ``max_results`` → not sent on the wire; stashed on + ``self._caller_max_results`` for client-side response truncation + (TinyFish doesn't honor it server-side) + - ``max_tokens_per_page`` → silently dropped (no TinyFish equivalent) + + Any other ``optional_params`` keys are forwarded to TinyFish as-is. + dict/list values are JSON-encoded so they survive ``urlencode``. + + Returns: + ``{_TINYFISH_PARAMS_KEY: }``. + ``get_complete_url`` reads this back to build the final URL. + """ resolved_query = " ".join(query) if isinstance(query, list) else query - request_data: TinyfishSearchRequest = {"query": resolved_query} - - country = optional_params.get("country") - if isinstance(country, str): - request_data["location"] = country - - raw_max = optional_params.get("max_results") - if isinstance(raw_max, (int, float, str)): - request_data["max_results"] = max(1, min(int(raw_max), 20)) - try: domains = _StrList.validate_python(optional_params.get("search_domain_filter")) except (ValidationError, TypeError): domains = [] if domains: - request_data["query"] = _append_domain_filters(request_data["query"], domains) + resolved_query = _append_domain_filters(resolved_query, domains) - result_data: dict[str, object] = dict(request_data) + request_data: dict[str, object] = {"query": resolved_query} + + country = optional_params.get("country") + if isinstance(country, str): + request_data["location"] = country + + # max_results is enforced client-side on the response (TinyFish ignores + # the param and always returns ~10). Clamp to [1, 10] and stash on self + # so transform_search_response can slice without re-reading the URL. + raw_max = optional_params.get("max_results") + if isinstance(raw_max, (int, float, str)): + try: + self._caller_max_results = max(1, min(int(raw_max), _TINYFISH_RESULT_CAP)) + except (ValueError, TypeError, OverflowError): + # OverflowError covers int(float('inf')) and similar non-finite floats. + verbose_logger.warning( + "TinyFish Search: max_results=%r is not a valid integer; ignoring.", + raw_max, + ) raw_supported: object = ( self.get_supported_perplexity_optional_params() # any-ok: base class returns bare set ) supported_perplexity = _StrFrozenSet.validate_python(raw_supported) for param, value in optional_params.items(): - if param not in supported_perplexity and param not in result_data: - result_data[param] = value + if param not in supported_perplexity and param not in request_data: + # `fetch` expects a JSON-encoded object on the wire; accept the + # natural Python dict form and serialize here so callers don't + # have to pre-stringify. + if isinstance(value, dict): + value = json.dumps(value, separators=(",", ":")) + # `urlencode` would render Python bool as "True"/"False" + # (capitalized). ux-labs validators require lowercase + # "true"/"false" (e.g. `include_thumbnail`); normalize here. + elif isinstance(value, bool): + value = "true" if value else "false" + request_data[param] = value - return {_TINYFISH_PARAMS_KEY: result_data} + return {_TINYFISH_PARAMS_KEY: request_data} def transform_search_response( self, @@ -134,24 +164,158 @@ class TinyfishSearchConfig(BaseSearchConfig): logging_obj: LiteLLMLoggingObj | None, **kwargs: object, ) -> SearchResponse: - raw_json: object = raw_response.json() # any-ok: httpx Response.json() -> Any - parsed = _TinyfishApiResponse.model_validate(raw_json) + """ + Transform a TinyFish response to LiteLLM's unified ``SearchResponse``. - max_results_str: str = "20" - if raw_response.request: - raw_param: object = raw_response.request.url.params.get( # any-ok: httpx QueryParams.get() -> Any - "max_results", "20" + Mappings (per-result): + - ``title`` → ``SearchResult.title`` (defaults to ``""`` if missing/null) + - ``url`` → ``SearchResult.url`` (defaults to ``""``) + - ``snippet`` → ``SearchResult.snippet`` (defaults to ``""``) + - all other per-result fields (``position``, ``site_name``, + ``thumbnail_url``, ``fetch``, ``fetch_error``, ...) ride through as + extras on ``SearchResult`` via its ``extra="allow"`` config. + + Top-level ``parameter_warnings`` (see ML-2085) is read when present and + each entry is re-fired via ``verbose_logger.warning``. Absent or + malformed entries are silently skipped — never throws. + + Error paths routed through ``self._wrap_error`` for uniform + ``"TinyFish Search: . See for details."`` wrapping: + - non-2xx HTTP status (caught here because ``AsyncHTTPHandler.get`` + does not call ``raise_for_status``) + - 200 with non-JSON body + - 200 with valid JSON whose shape doesn't satisfy ``SearchResponse`` + + Returns: + ``SearchResponse`` truncated to ``self._caller_max_results`` (or + ``_TINYFISH_RESULT_CAP`` when the caller didn't set ``max_results``). + """ + # AsyncHTTPHandler.get does not call raise_for_status, so non-2xx + # responses arrive here looking successful. Dispatch through + # get_error_class so callers see a uniform attributed error. + if not (200 <= raw_response.status_code < 300): + raise self._wrap_error( + error_message=raw_response.text, + status_code=raw_response.status_code, + headers=dict(raw_response.headers), ) - max_results_str = str(raw_param) - max_results: int = min(int(max_results_str), 20) - results = [ - SearchResult(title=item.title, url=item.url, snippet=item.snippet) for item in parsed.results[:max_results] - ] + try: + raw_json: object = raw_response.json() # any-ok: httpx Response.json() -> Any + except json.JSONDecodeError: + raise self._wrap_error( + error_message=f"Expected JSON response, got: {raw_response.text[:200]}", + status_code=raw_response.status_code, + headers=dict(raw_response.headers), + ) - return SearchResponse(results=results, object="search") + _default_missing_result_fields(raw_json) + + try: + parsed = SearchResponse.model_validate(raw_json) + except ValidationError as e: + raise self._wrap_error( + error_message=(f"Response shape does not match LiteLLM's SearchResponse schema: {e}"), + status_code=raw_response.status_code, + headers=dict(raw_response.headers), + ) + + _emit_parameter_warnings(parsed) + + max_results = self._caller_max_results or _TINYFISH_RESULT_CAP + return SearchResponse(results=list(parsed.results[:max_results])) + + def _wrap_error( + self, + error_message: str, + status_code: int, + headers: dict[str, str], + ) -> Exception: + """ + Build an attributed ``BaseLLMException`` from a TinyFish error body. + + Used only at the call sites we control inside + ``transform_search_response`` (non-2xx, JSONDecodeError, ValidationError). + Not an override of ``BaseSearchConfig.get_error_class``: that path is + left to inherit from the base so it auto-picks-up any future LiteLLM + improvements. Trade-off: network failures (routed through LiteLLM + core's ``_handle_error`` → ``BaseSearchConfig.get_error_class``) won't + carry the ``TinyFish Search:`` prefix — the bare error already names + the host in the URL, so attribution is implicit there. + """ + # ux-labs frontend wraps every error body as {"error": {"code", "message", "details"?}}. + # Best-effort unwrap to surface the inner message; fall back to the raw body + # for non-ux-labs responses (CDN HTML pages, other JSON envelopes, plain text). + inner_message = error_message + try: + body: object = json.loads(error_message) # any-ok: json.loads -> Any + if isinstance(body, dict): + error_obj: object = body.get("error") # any-ok: untyped dict + if isinstance(error_obj, dict): + candidate: object = error_obj.get("message") # any-ok: untyped dict + if isinstance(candidate, str) and candidate: + inner_message = candidate + except (json.JSONDecodeError, TypeError): + pass + + return BaseLLMException( + status_code=status_code, + message=f"TinyFish Search: {inner_message}. See {_TINYFISH_DOCS_URL} for details.", + headers=headers, + ) def _append_domain_filters(query: str, domains: list[str]) -> str: domain_clauses = " OR ".join(f"site:{d}" for d in domains) return f"({query}) ({domain_clauses})" + + +def _default_missing_result_fields(raw_json: object) -> None: + """Default missing/null title/url/snippet to "" on each result item in place. + + SearchResult requires these three fields; a degraded TinyFish result flows + through with empty strings instead of failing the whole call. + """ + if not isinstance(raw_json, dict): + return + results_in = raw_json.get("results") + if not isinstance(results_in, list): + return + for item in results_in: + if not isinstance(item, dict): + continue + for field in ("title", "url", "snippet"): + if not isinstance(item.get(field), str): + item[field] = "" + + +def _emit_parameter_warnings(parsed: SearchResponse) -> None: + """Re-fire TinyFish-side ``parameter_warnings`` (see ML-2085) as warnings. + + Defensive: skip silently on any shape we don't recognize so a malformed + entry (or an early/partial rollout of the field) never throws. + Schema per entry: ``{type, parameter, message, docs_url?}``. + """ + warnings_field: object = ( + getattr(parsed, "parameter_warnings", None) # any-ok: extras=allow field + ) + if not isinstance(warnings_field, list): + return + for entry in warnings_field: + if not isinstance(entry, dict): + continue + warning_type: object = entry.get("type") # any-ok: untyped dict + parameter: object = entry.get("parameter") # any-ok: untyped dict + message: object = entry.get("message") # any-ok: untyped dict + if not isinstance(warning_type, str) or not warning_type: + continue + if not isinstance(parameter, str) or not parameter: + continue + if not isinstance(message, str) or not message: + continue + verbose_logger.warning( + "TinyFish Search parameter_warning (%s) `%s`: %s", + warning_type, + parameter, + message, + ) diff --git a/litellm/llms/vertex_ai/audio_transcription/transformation.py b/litellm/llms/vertex_ai/audio_transcription/transformation.py new file mode 100644 index 00000000000..03769bf2601 --- /dev/null +++ b/litellm/llms/vertex_ai/audio_transcription/transformation.py @@ -0,0 +1,194 @@ +import base64 + +from httpx import Headers, Response + +import litellm +from litellm.exceptions import UnsupportedParamsError +from litellm.litellm_core_utils.audio_utils.utils import ( + normalize_transcription_language_to_bcp47, + process_audio_file, +) +from litellm.llms.base_llm.audio_transcription.transformation import ( + AudioTranscriptionRequestData, + BaseAudioTranscriptionConfig, +) +from litellm.llms.base_llm.chat.transformation import BaseLLMException +from litellm.llms.vertex_ai.common_utils import VertexAIError, validate_vertex_location +from litellm.llms.vertex_ai.vertex_llm_base import VertexBase +from litellm.types.llms.openai import ( + AllMessageValues, + OpenAIAudioTranscriptionOptionalParams, +) +from litellm.types.llms.vertex_ai_speech_to_text import ( + VertexSpeechToTextAutoDecodingConfig, + VertexSpeechToTextRecognitionConfig, + VertexSpeechToTextRecognitionFeatures, + VertexSpeechToTextRecognizeRequest, + VertexSpeechToTextRecognizeResponse, +) +from litellm.types.utils import FileTypes, TranscriptionResponse + +DEFAULT_SPEECH_TO_TEXT_LOCATION = "us" +AUTO_LANGUAGE_CODE = "auto" +SUPPORTED_RESPONSE_FORMATS = ("json", "text") +_URL_UNSAFE_PROJECT_CHARS = ("/", "?", "#", "\\", ":", " ", "\t", "\n", "\r") + + +class VertexAIAudioTranscriptionConfig(BaseAudioTranscriptionConfig, VertexBase): + def __init__(self) -> None: + BaseAudioTranscriptionConfig.__init__(self) + VertexBase.__init__(self) + + def get_supported_openai_params(self, model: str) -> list[OpenAIAudioTranscriptionOptionalParams]: + return ["language", "response_format"] + + def map_openai_params( + self, + non_default_params: dict, + optional_params: dict, + model: str, + drop_params: bool, + ) -> dict: + supported_params = self.get_supported_openai_params(model) + mapped = { + **optional_params, + **{k: v for k, v in non_default_params.items() if k in supported_params}, + } + response_format = mapped.get("response_format") + if response_format is None or response_format in SUPPORTED_RESPONSE_FORMATS: + return mapped + if drop_params or litellm.drop_params: + return {k: v for k, v in mapped.items() if k != "response_format"} + raise UnsupportedParamsError( + status_code=400, + message=( + f"Google Speech-to-Text does not support response_format={response_format!r}. " + f"Supported values: {', '.join(SUPPORTED_RESPONSE_FORMATS)}. " + "To drop unsupported openai params from the call, set `litellm.drop_params = True`" + ), + ) + + def get_error_class(self, error_message: str, status_code: int, headers: dict | Headers) -> BaseLLMException: + return VertexAIError(status_code=status_code, message=error_message, headers=headers) + + def validate_environment( + self, + headers: dict, + model: str, + messages: list[AllMessageValues], + optional_params: dict, + litellm_params: dict, + api_key: str | None = None, + api_base: str | None = None, + ) -> dict: + access_token, project_id = self._ensure_access_token( + credentials=self.safe_get_vertex_ai_credentials(litellm_params), + project_id=self.safe_get_vertex_ai_project(litellm_params), + custom_llm_provider="vertex_ai", + ) + return { + **headers, + "Authorization": f"Bearer {access_token}", + "x-goog-user-project": project_id, + "Content-Type": "application/json", + } + + def get_complete_url( + self, + api_base: str | None, + api_key: str | None, + model: str, + optional_params: dict, + litellm_params: dict, + stream: bool | None = None, + ) -> str: + location = self._validate_location(self.safe_get_vertex_ai_location(litellm_params)) + project_id = self._validate_project_id( + self.safe_get_vertex_ai_project(litellm_params) or self._resolve_project_id_from_credentials(litellm_params) + ) + host = "speech.googleapis.com" if location == "global" else f"{location}-speech.googleapis.com" + base_url = (api_base or f"https://{host}").rstrip("/") + return f"{base_url}/v2/projects/{project_id}/locations/{location}/recognizers/_:recognize" + + @staticmethod + def _validate_location(location: str | None) -> str: + try: + return validate_vertex_location(location or DEFAULT_SPEECH_TO_TEXT_LOCATION) + except ValueError as e: + raise VertexAIError(status_code=400, message=str(e)) from e + + @staticmethod + def _validate_project_id(project_id: str) -> str: + if not project_id or ".." in project_id or any(c in project_id for c in _URL_UNSAFE_PROJECT_CHARS): + raise VertexAIError(status_code=400, message=f"Invalid vertex_project format: {project_id!r}") + return project_id + + def _resolve_project_id_from_credentials(self, litellm_params: dict) -> str: + _, project_id = self._ensure_access_token( + credentials=self.safe_get_vertex_ai_credentials(litellm_params), + project_id=None, + custom_llm_provider="vertex_ai", + ) + return project_id + + def transform_audio_transcription_request( + self, + model: str, + audio_file: FileTypes, + optional_params: dict, + litellm_params: dict, + ) -> AudioTranscriptionRequestData: + processed_audio = process_audio_file(audio_file) + language = optional_params.get("language") + language_codes = ( + [normalize_transcription_language_to_bcp47(language)] + if isinstance(language, str) and language + else [AUTO_LANGUAGE_CODE] + ) + request_body = VertexSpeechToTextRecognizeRequest( + config=VertexSpeechToTextRecognitionConfig( + model=model.removeprefix("vertex_ai/"), + languageCodes=language_codes, + features=VertexSpeechToTextRecognitionFeatures(enableAutomaticPunctuation=True), + autoDecodingConfig=VertexSpeechToTextAutoDecodingConfig(), + ), + content=base64.b64encode(processed_audio.file_content).decode("utf-8"), + ) + return AudioTranscriptionRequestData(data=dict(request_body)) + + def transform_audio_transcription_response( + self, + raw_response: Response, + ) -> TranscriptionResponse: + try: + response_json = raw_response.json() + except ValueError: + raise VertexAIError( + status_code=raw_response.status_code, + message=f"Received non-JSON response from Google Speech-to-Text: {raw_response.text}", + ) + parsed = VertexSpeechToTextRecognizeResponse.model_validate(response_json) + transcripts = tuple( + result.alternatives[0].transcript + for result in parsed.results + if result.alternatives and result.alternatives[0].transcript + ) + response = TranscriptionResponse(text=" ".join(transcripts)) + response["task"] = "transcribe" + detected_language = next((result.languageCode for result in parsed.results if result.languageCode), None) + if detected_language is not None: + response["language"] = detected_language + billed_duration = _parse_duration_seconds(parsed.metadata.totalBilledDuration if parsed.metadata else None) + if billed_duration is not None: + response["duration"] = billed_duration + response._hidden_params = response_json + return response + + +def _parse_duration_seconds(duration: str | None) -> float | None: + if duration is None or not duration.endswith("s"): + return None + try: + return float(duration[:-1]) + except ValueError: + return None diff --git a/litellm/llms/vertex_ai/batches/transformation.py b/litellm/llms/vertex_ai/batches/transformation.py index c75efdb43e8..6bbe8f75701 100644 --- a/litellm/llms/vertex_ai/batches/transformation.py +++ b/litellm/llms/vertex_ai/batches/transformation.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any, Dict, Optional from litellm._uuid import uuid from litellm.llms.vertex_ai.common_utils import ( @@ -47,7 +47,7 @@ class VertexAIBatchTransformation: ) -> LiteLLMBatch: return LiteLLMBatch( id=cls._get_batch_id_from_vertex_ai_batch_response(response), - completion_window="24hrs", + completion_window="24h", created_at=_convert_vertex_datetime_to_openai_datetime(vertex_datetime=response.get("createTime", "")), endpoint="", input_file_id=cls._get_input_file_id_from_vertex_ai_batch_response(response), @@ -207,3 +207,19 @@ class VertexAIBatchTransformation: parts = model_path.split("/") model = f"publishers/{'/'.join(parts[:3])}" return model + + @classmethod + def is_unmanaged_gcs_batch_input_file_id(cls, input_file_id: Optional[str]) -> bool: + """ + Returns True if `input_file_id` is a raw gs:// Vertex batch input file (i.e. not a + LiteLLM-managed unified file id) with a `publishers/` model path that + `_get_model_from_gcs_file` can parse. + """ + return input_file_id is not None and input_file_id.startswith("gs://") and "publishers/" in input_file_id + + @classmethod + def get_bare_model_name_from_gcs_file(cls, gcs_file_uri: str) -> str: + """ + Extracts the bare model name (e.g. "gemini-1.5-flash-001") from a gcs file uri. + """ + return cls._get_model_from_gcs_file(gcs_file_uri).rsplit("/", 1)[-1] diff --git a/litellm/llms/vertex_ai/common_utils.py b/litellm/llms/vertex_ai/common_utils.py index 36522dfe396..7dcb4dcf2e8 100644 --- a/litellm/llms/vertex_ai/common_utils.py +++ b/litellm/llms/vertex_ai/common_utils.py @@ -311,6 +311,28 @@ def get_vertex_base_model_name(model: str) -> str: return model +def validate_vertex_location(vertex_location: Optional[str]) -> str: + """ + Validate a Vertex AI location before interpolating it into a request host or + URL path. + + ``vertex_location`` is client-controllable on the proxy (it flows in from the + request body), so it must never be trusted verbatim in a URL or an attacker + could point the host at their own server and exfiltrate the admin's Google + access token. Allow the special ``global`` control plane and otherwise require + a lowercase alphanumeric-plus-hyphen token (e.g. ``us``, ``us-central1``, + ``eu``), which rejects host injection like ``attacker.example/`` or + ``evil.com#``. + """ + if vertex_location == "global": + return vertex_location + if vertex_location is None: + raise ValueError("vertex_location is required") + if not re.match(r"^[a-z][a-z0-9-]*$", vertex_location): + raise ValueError("Invalid vertex_location format") + return vertex_location + + def get_vertex_base_url( vertex_location: Optional[str], ) -> str: @@ -321,15 +343,12 @@ def get_vertex_base_url( - Multi-region geographies (e.g. ``us``, ``eu``) use ``aiplatform.{geo}.rep.googleapis.com``. - Regional locations (e.g. ``us-central1``) use ``{region}-aiplatform.googleapis.com``. """ - if vertex_location == "global": + validated_location = validate_vertex_location(vertex_location) + if validated_location == "global": return "https://aiplatform.googleapis.com" - if vertex_location is None: - raise ValueError("vertex_location is required") - if not re.match(r"^[a-z][a-z0-9-]*$", vertex_location): - raise ValueError("Invalid vertex_location format") - if "-" not in vertex_location: - return f"https://aiplatform.{vertex_location}.rep.googleapis.com" - return f"https://{vertex_location}-aiplatform.googleapis.com" + if "-" not in validated_location: + return f"https://aiplatform.{validated_location}.rep.googleapis.com" + return f"https://{validated_location}-aiplatform.googleapis.com" def _get_embedding_url( diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index 678877c0721..f9ed8cea9b5 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -2315,17 +2315,15 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): # Store thoughtSignatures in provider_specific_fields if thought_signatures is not None: - if "provider_specific_fields" not in chat_completion_message: - chat_completion_message["provider_specific_fields"] = {} - chat_completion_message["provider_specific_fields"]["thought_signatures"] = thought_signatures # type: ignore + thought_signature_fields = chat_completion_message.get("provider_specific_fields") or {} + thought_signature_fields["thought_signatures"] = thought_signatures + chat_completion_message["provider_specific_fields"] = thought_signature_fields # Store server-side tool invocations in provider_specific_fields if server_side_tool_invocations is not None: - if "provider_specific_fields" not in chat_completion_message: - chat_completion_message["provider_specific_fields"] = {} - chat_completion_message["provider_specific_fields"]["server_side_tool_invocations"] = ( - server_side_tool_invocations # type: ignore - ) + tool_invocation_fields = chat_completion_message.get("provider_specific_fields") or {} + tool_invocation_fields["server_side_tool_invocations"] = server_side_tool_invocations + chat_completion_message["provider_specific_fields"] = tool_invocation_fields if isinstance(model_response, ModelResponseStream): choice = VertexGeminiConfig._create_streaming_choice( diff --git a/litellm/llms/vertex_ai/image_generation/vertex_gemini_transformation.py b/litellm/llms/vertex_ai/image_generation/vertex_gemini_transformation.py index 39503bd78dd..572725ac789 100644 --- a/litellm/llms/vertex_ai/image_generation/vertex_gemini_transformation.py +++ b/litellm/llms/vertex_ai/image_generation/vertex_gemini_transformation.py @@ -1,5 +1,7 @@ import os -from typing import TYPE_CHECKING, Any, Dict, List, Optional +from typing import TYPE_CHECKING, Any, Optional + +from litellm._logging import verbose_logger import httpx @@ -52,6 +54,7 @@ class VertexAIGeminiImageGenerationConfig(BaseImageGenerationConfig, VertexLLM): return [ "n", "size", + "imageConfig", "aspectRatio", "aspect_ratio", "imageSize", @@ -83,7 +86,12 @@ class VertexAIGeminiImageGenerationConfig(BaseImageGenerationConfig, VertexLLM): mapped_params["aspectRatio"] = v elif k in ("imageSize", "image_size"): mapped_params["imageSize"] = v - elif k not in ("tools", "web_search_options"): + elif k == "imageConfig": + if isinstance(v, dict): + mapped_params["imageConfig"] = v + else: + verbose_logger.warning("imageConfig must be a dict, got %s — ignoring.", type(v).__name__) + elif k not in ("tools", "web_search_options", "imageConfig"): mapped_params[k] = v mapped_params = map_gemini_image_tools_params(non_default_params, mapped_params) @@ -167,7 +175,7 @@ class VertexAIGeminiImageGenerationConfig(BaseImageGenerationConfig, VertexLLM): self, headers: dict, model: str, - messages: List[AllMessageValues], + messages: list[AllMessageValues], optional_params: dict, litellm_params: dict, api_key: Optional[str] = None, @@ -209,18 +217,16 @@ class VertexAIGeminiImageGenerationConfig(BaseImageGenerationConfig, VertexLLM): contents = [{"role": "user", "parts": [{"text": prompt}]}] # Prepare generation config - generation_config: Dict[str, Any] = {"responseModalities": ["IMAGE"]} + generation_config: dict[str, Any] = {"responseModalities": ["IMAGE"]} - # Handle image-specific config parameters - image_config: Dict[str, Any] = {} + # Seed from user-supplied imageConfig dict; flat params are overlaid for backward compat. + image_config: dict[str, Any] = dict(optional_params.get("imageConfig") or {}) - # Map aspectRatio if "aspectRatio" in optional_params: image_config["aspectRatio"] = optional_params["aspectRatio"] elif "aspect_ratio" in optional_params: image_config["aspectRatio"] = optional_params["aspect_ratio"] - # Map imageSize (for Gemini 3 Pro) if "imageSize" in optional_params: image_config["imageSize"] = optional_params["imageSize"] elif "image_size" in optional_params: @@ -235,7 +241,7 @@ class VertexAIGeminiImageGenerationConfig(BaseImageGenerationConfig, VertexLLM): elif "n" in optional_params: generation_config["candidateCount"] = optional_params["n"] - request_body: Dict[str, Any] = { + request_body: dict[str, Any] = { "contents": contents, "generationConfig": generation_config, } diff --git a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/experimental_pass_through/transformation.py b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/experimental_pass_through/transformation.py index 8566496bf9c..de72795cabc 100644 --- a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/experimental_pass_through/transformation.py +++ b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/experimental_pass_through/transformation.py @@ -17,6 +17,10 @@ from ..output_params_utils import sanitize_vertex_anthropic_output_params class VertexAIPartnerModelsAnthropicMessagesConfig(AnthropicMessagesConfig, VertexBase): + @property + def custom_llm_provider(self) -> Optional[str]: + return "vertex_ai" + def should_strip_billing_metadata(self) -> bool: return True diff --git a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/output_params_utils.py b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/output_params_utils.py index 280cc1c888a..b87d05ab1fd 100644 --- a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/output_params_utils.py +++ b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/output_params_utils.py @@ -26,7 +26,7 @@ def _model_accepts_output_config_effort(model: str) -> bool: """ from litellm.llms.anthropic.chat.transformation import AnthropicConfig - return AnthropicConfig._model_supports_effort_param(model) + return AnthropicConfig._model_supports_effort_param(model, "vertex_ai") def sanitize_vertex_anthropic_output_params(data: dict, model: str) -> None: diff --git a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py index c8d91be359b..8fcefb04b34 100644 --- a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py +++ b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py @@ -112,6 +112,7 @@ class VertexAIAnthropicConfig(AnthropicConfig): prompt_caching_set=self.is_cache_control_set(messages), file_id_used=self.is_file_id_used(messages), mcp_server_used=self.is_mcp_server_used(optional_params.get("mcp_servers")), + custom_llm_provider="vertex_ai", ) beta_set = set(auto_betas) diff --git a/litellm/llms/vertex_ai/vertex_llm_base.py b/litellm/llms/vertex_ai/vertex_llm_base.py index d57d7bf17df..788261ac1fe 100644 --- a/litellm/llms/vertex_ai/vertex_llm_base.py +++ b/litellm/llms/vertex_ai/vertex_llm_base.py @@ -9,6 +9,7 @@ import json import os import threading from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Tuple +from urllib.parse import urlparse import litellm from litellm._logging import verbose_logger @@ -315,6 +316,9 @@ class VertexBase: api_base=api_base, ) + if partner == VertexPartnerProvider.llama: + return default_api_base + if len(default_api_base.split(":")) > 1: endpoint = default_api_base.split(":")[-1] else: @@ -615,7 +619,8 @@ class VertexBase: Handles custom api_base for: 1. Gemini (Google AI Studio) - constructs /models/{model}:{endpoint} - 2. Vertex AI with standard proxies - constructs {api_base}:{endpoint} + 2. Vertex AI with standard proxies - constructs {api_base}:{endpoint}; + if api_base has no path (bare host), grafts the default vertex URL path onto it 3. Vertex AI with PSC endpoints - constructs full path structure {api_base}/v1/projects/{project}/locations/{location}/endpoints/{model}:{endpoint} (only when use_psc_endpoint_format=True) @@ -660,8 +665,9 @@ class VertexBase: model_for_url, endpoint, ) + elif urlparse(api_base).path in ("", "/"): + url = api_base.rstrip("/") + urlparse(url).path else: - # Fallback to simple format if we don't have all parameters url = "{}:{}".format(api_base, endpoint) if stream is True: url = url + "?alt=sse" diff --git a/litellm/main.py b/litellm/main.py index 18d2c367f8d..7d457d9cdd1 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -210,6 +210,7 @@ from .llms.bedrock.embed.embedding import BedrockEmbedding from .llms.bedrock.image_edit.handler import BedrockImageEdit from .llms.bedrock.image_generation.image_handler import BedrockImageGeneration from .llms.bytez.chat.transformation import BytezChatConfig +from .llms.gdc.chat.transformation import GDCGeminiConfig from .llms.clarifai.chat.transformation import ClarifaiConfig from .llms.codestral.completion.handler import CodestralTextCompletion from .llms.cohere.embed import handler as cohere_embed @@ -318,6 +319,7 @@ google_batch_embeddings = GoogleBatchEmbeddings() vertex_partner_models_chat_completion = VertexAIPartnerModels() vertex_gemma_chat_completion = VertexAIGemmaModels() vertex_model_garden_chat_completion = VertexAIModelGardenModels() +gdc_transformation = GDCGeminiConfig() # vertex_text_to_speech is now replaced by VertexAITextToSpeechConfig sagemaker_llm = SagemakerLLM() watsonx_chat_completion = WatsonXChatHandler() @@ -580,6 +582,7 @@ async def acompletion( "api_key": api_key, "model_list": model_list, "reasoning_effort": reasoning_effort, + "verbosity": verbosity, "safety_identifier": safety_identifier, "service_tier": service_tier, "extra_headers": extra_headers, @@ -1079,6 +1082,54 @@ def _build_custom_pricing_entry( return entry +def _get_router_deployment_id(kwargs: dict) -> Optional[str]: + for metadata_key in ("litellm_metadata", "metadata"): + metadata = kwargs.get(metadata_key) or {} + if not isinstance(metadata, dict): + continue + deployment_model_info = metadata.get("model_info") or {} + if not isinstance(deployment_model_info, dict): + continue + deployment_id = deployment_model_info.get("id") + if deployment_id is not None: + return str(deployment_id) + return None + + +def _register_custom_pricing_for_request( + model: str, + custom_llm_provider: str, + kwargs: dict, + model_info: Optional[dict], +) -> None: + """Register per-request custom pricing in litellm.model_cost. + + Router-originated requests (identified by the deployment id the router puts + in metadata) get their full pricing registered under that unique id only; + the shared ``{provider}/{model}`` key receives the entry with pricing fields + stripped, mirroring Router._create_deployment. This keeps one deployment's + pricing overrides (e.g. a zero-cost wildcard) from clobbering built-in + pricing used by sibling deployments of the same backend model. Direct SDK + calls keep the legacy behavior of registering the shared key with pricing. + """ + entry = _build_custom_pricing_entry( + custom_llm_provider=custom_llm_provider, + kwargs=kwargs, + model_info=model_info, + ) + shared_key = f"{custom_llm_provider}/{model}" + deployment_id = _get_router_deployment_id(kwargs) + if deployment_id is None: + litellm.register_model({shared_key: entry}) + return + litellm.register_model( + { + deployment_id: entry, + shared_key: CustomPricingLiteLLMParams.strip_custom_pricing_fields(entry), + } + ) + + def _complete_azure(ctx: _CompletionDispatchContext) -> _CompletionDispatchResult: _azure_detection_model = ctx._azure_detection_model acompletion = ctx.acompletion @@ -4336,6 +4387,45 @@ def _complete_gradient_ai(ctx: _CompletionDispatchContext) -> _CompletionDispatc ) +def _complete_gdc(ctx: _CompletionDispatchContext) -> _CompletionDispatchResult: + acompletion = ctx.acompletion + api_base = ctx.api_base + api_key = ctx.api_key + client = ctx.client + custom_llm_provider = ctx.custom_llm_provider + headers = ctx.headers + litellm_params = ctx.litellm_params + logging = ctx.logging + messages = ctx.messages + model = ctx.model + model_response = ctx.model_response + optional_params = ctx.optional_params + stream = ctx.stream + timeout = ctx.timeout + + api_key = api_key or litellm.gdc_key or get_secret_str("GDC_API_KEY") or litellm.api_key + api_base = api_base or litellm.gdc_api_base or get_secret_str("GDC_API_BASE") or litellm.api_base + + return base_llm_http_handler.completion( + model=model, + messages=messages, + headers=headers, + model_response=model_response, + api_key=api_key, + api_base=api_base, + acompletion=acompletion, + logging_obj=logging, + optional_params=optional_params, + litellm_params=litellm_params, + timeout=timeout, # type: ignore + client=client, + custom_llm_provider=custom_llm_provider, + encoding=_get_encoding(), + stream=stream, + provider_config=gdc_transformation, + ) + + def _complete_bytez(ctx: _CompletionDispatchContext) -> _CompletionDispatchResult: acompletion = ctx.acompletion api_base = ctx.api_base @@ -5066,14 +5156,11 @@ def completion( # type: ignore if ( input_cost_per_token is not None and output_cost_per_token is not None ) or input_cost_per_second is not None: - litellm.register_model( - { - f"{custom_llm_provider}/{model}": _build_custom_pricing_entry( - custom_llm_provider=custom_llm_provider, - kwargs=kwargs, - model_info=model_info, - ) - } + _register_custom_pricing_for_request( + model=model, + custom_llm_provider=custom_llm_provider, + kwargs=kwargs, + model_info=model_info, ) ### BUILD CUSTOM PROMPT TEMPLATE -- IF GIVEN ### custom_prompt_dict = {} # type: ignore @@ -5152,6 +5239,7 @@ def completion( # type: ignore "parallel_tool_calls": parallel_tool_calls, "messages": messages, "reasoning_effort": reasoning_effort, + "verbosity": verbosity, "thinking": thinking, "web_search_options": web_search_options, "include_server_side_tool_invocations": ( @@ -5533,6 +5621,8 @@ def completion( # type: ignore elif custom_llm_provider == "gradient_ai": response = _complete_gradient_ai(_dispatch_ctx) + elif custom_llm_provider == "gdc": + response = _complete_gdc(_dispatch_ctx) elif custom_llm_provider == "bytez": response = _complete_bytez(_dispatch_ctx) elif custom_llm_provider == "lemonade": @@ -5914,14 +6004,11 @@ def embedding( ### REGISTER CUSTOM MODEL PRICING -- IF GIVEN ### if (input_cost_per_token is not None and output_cost_per_token is not None) or input_cost_per_second is not None: - litellm.register_model( - { - f"{custom_llm_provider}/{model}": _build_custom_pricing_entry( - custom_llm_provider=custom_llm_provider, - kwargs=kwargs, - model_info=kwargs.get("model_info"), - ) - } + _register_custom_pricing_for_request( + model=model, + custom_llm_provider=custom_llm_provider, + kwargs=kwargs, + model_info=kwargs.get("model_info"), ) litellm_params_dict = get_litellm_params(**kwargs) @@ -8261,26 +8348,6 @@ def stream_chunk_builder_text_completion(chunks: list, messages: Optional[List] finish_reason = chunks[-1]["choices"][0]["finish_reason"] logprobs = chunks[-1]["choices"][0]["logprobs"] - response = { - "id": id, - "object": object, - "created": created, - "model": model, - "system_fingerprint": system_fingerprint, - "choices": [ - { - "text": None, - "index": 0, - "logprobs": logprobs, - "finish_reason": finish_reason, - } - ], - "usage": { - "prompt_tokens": None, - "completion_tokens": None, - "total_tokens": None, - }, - } content_list = [] for chunk in chunks: choices = chunk["choices"] @@ -8292,25 +8359,37 @@ def stream_chunk_builder_text_completion(chunks: list, messages: Optional[List] # Combine the "content" strings into a single string || combine the 'function' strings into a single string combined_content = "".join(content_list) - # Update the "content" field within the response dictionary - response["choices"][0]["text"] = combined_content - - if len(combined_content) > 0: - pass - else: - pass - # # Update usage information if needed try: - response["usage"]["prompt_tokens"] = token_counter(model=model, messages=messages) + prompt_tokens = token_counter(model=model, messages=messages) except Exception: # don't allow this failing to block a complete streaming response from being returned print_verbose("token_counter failed, assuming prompt tokens is 0") - response["usage"]["prompt_tokens"] = 0 - response["usage"]["completion_tokens"] = token_counter( + prompt_tokens = 0 + completion_tokens = token_counter( model=model, text=combined_content, count_response_tokens=True, # count_response_tokens is a Flag to tell token counter this is a response, No need to add extra tokens we do for input messages ) - response["usage"]["total_tokens"] = response["usage"]["prompt_tokens"] + response["usage"]["completion_tokens"] + + response = { + "id": id, + "object": object, + "created": created, + "model": model, + "system_fingerprint": system_fingerprint, + "choices": [ + { + "text": combined_content, + "index": 0, + "logprobs": logprobs, + "finish_reason": finish_reason, + } + ], + "usage": { + "prompt_tokens": prompt_tokens, + "completion_tokens": completion_tokens, + "total_tokens": prompt_tokens + completion_tokens, + }, + } return TextCompletionResponse(**response) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 21132db93cb..a6f59abc140 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -744,7 +744,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "anthropic.claude-haiku-4-5@20251001": { "cache_creation_input_token_cost": 1.25e-06, @@ -768,7 +769,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_streaming": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "anthropic.claude-3-5-sonnet-20240620-v1:0": { "input_cost_per_token": 3e-06, @@ -787,8 +789,6 @@ "output_cost_per_token_above_200k_tokens": 3e-05, "cache_creation_input_token_cost_above_200k_tokens": 7.5e-06, "cache_read_input_token_cost_above_200k_tokens": 6e-07, - "cache_creation_input_token_cost_above_1hr": 7.5e-06, - "cache_creation_input_token_cost_above_1hr_above_200k_tokens": 1.5e-05, "cache_creation_input_token_cost": 3.75e-06, "cache_read_input_token_cost": 3e-07 }, @@ -813,9 +813,7 @@ "input_cost_per_token_above_200k_tokens": 6e-06, "output_cost_per_token_above_200k_tokens": 3e-05, "cache_creation_input_token_cost_above_200k_tokens": 7.5e-06, - "cache_read_input_token_cost_above_200k_tokens": 6e-07, - "cache_creation_input_token_cost_above_1hr": 7.5e-06, - "cache_creation_input_token_cost_above_1hr_above_200k_tokens": 1.5e-05 + "cache_read_input_token_cost_above_200k_tokens": 6e-07 }, "anthropic.claude-3-7-sonnet-20240620-v1:0": { "cache_creation_input_token_cost": 4.5e-06, @@ -991,7 +989,8 @@ "supports_vision": true, "supports_native_structured_output": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "high" + "bedrock_output_config_effort_ceiling": "high", + "supports_parallel_tool_use_config": true }, "anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1010,7 +1009,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1023,7 +1021,8 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1042,7 +1041,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1055,7 +1053,8 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1074,7 +1073,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1087,7 +1085,8 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1106,7 +1105,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1119,7 +1117,8 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1138,7 +1137,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1151,9 +1149,11 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, @@ -1170,7 +1170,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1185,7 +1184,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "anthropic.claude-mythos-preview": { "input_cost_per_token": 0, @@ -1203,6 +1203,7 @@ "supports_output_config": true }, "global.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, @@ -1219,7 +1220,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1234,9 +1234,11 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, @@ -1253,7 +1255,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1268,9 +1269,11 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, @@ -1287,7 +1290,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1302,9 +1304,11 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, @@ -1321,7 +1325,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1336,7 +1339,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "anthropic.claude-fable-5": { "cache_creation_input_token_cost": 1.25e-05, @@ -1355,6 +1359,7 @@ "search_context_size_medium": 0.01 }, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1369,7 +1374,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-fable-5": { "cache_creation_input_token_cost": 1.25e-05, @@ -1388,6 +1394,7 @@ "search_context_size_medium": 0.01 }, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1402,7 +1409,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-fable-5": { "cache_creation_input_token_cost": 1.375e-05, @@ -1421,6 +1429,7 @@ "search_context_size_medium": 0.01 }, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1435,7 +1444,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-fable-5": { "cache_creation_input_token_cost": 1.375e-05, @@ -1454,6 +1464,7 @@ "search_context_size_medium": 0.01 }, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1468,10 +1479,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, @@ -1487,7 +1501,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1502,10 +1515,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, @@ -1521,7 +1537,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1536,10 +1551,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, "cache_read_input_token_cost": 5.5e-07, @@ -1555,7 +1573,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1570,10 +1587,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, "cache_read_input_token_cost": 5.5e-07, @@ -1589,7 +1609,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1604,10 +1623,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, "cache_read_input_token_cost": 5.5e-07, @@ -1623,7 +1645,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1638,9 +1659,47 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "jp.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "cache_creation_input_token_cost": 6.875e-06, + "cache_creation_input_token_cost_above_1hr": 1.1e-05, + "cache_read_input_token_cost": 5.5e-07, + "input_cost_per_token": 5.5e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 2.75e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "jp.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "cache_creation_input_token_cost": 6.875e-06, "cache_read_input_token_cost": 5.5e-07, "input_cost_per_token": 5.5e-06, @@ -1669,7 +1728,218 @@ "supports_xhigh_reasoning_effort": true, "supports_native_structured_output": true, "supports_max_reasoning_effort": true, - "supports_minimal_reasoning_effort": true + "supports_minimal_reasoning_effort": true, + "supports_parallel_tool_use_config": true + }, + "anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "global.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "us.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + "input_cost_per_token": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "eu.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + "input_cost_per_token": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "au.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + "input_cost_per_token": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "jp.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + "input_cost_per_token": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1688,7 +1958,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1700,7 +1969,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1719,7 +1989,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1731,7 +2000,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1750,7 +2020,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1762,7 +2031,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1781,7 +2051,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1793,7 +2062,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1812,7 +2082,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1824,7 +2093,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "jp.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1843,7 +2113,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1855,7 +2124,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 3.75e-06, @@ -1884,7 +2154,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "anthropic.claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 3.75e-06, @@ -1916,7 +2187,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "anthropic.claude-v1": { "input_cost_per_token": 8e-06, @@ -2166,7 +2438,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "apac.anthropic.claude-3-sonnet-20240229-v1:0": { "input_cost_per_token": 3e-06, @@ -2211,7 +2484,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "assemblyai/best": { "input_cost_per_second": 3.333e-05, @@ -2255,7 +2529,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "azure/ada": { "input_cost_per_token": 1e-07, @@ -2364,7 +2639,6 @@ "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -2394,7 +2668,6 @@ "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -2455,7 +2728,6 @@ "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -2511,6 +2783,36 @@ "supports_tool_choice": true, "supports_vision": true }, + "azure_ai/claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_max_reasoning_effort": true + }, "azure_ai/claude-sonnet-4-6": { "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 3.75e-06, @@ -2523,7 +2825,6 @@ "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -5511,6 +5812,76 @@ "supports_tool_choice": true, "supports_vision": true }, + "azure/us/gpt-5.4": { + "cache_read_input_token_cost": 2.8e-07, + "cache_read_input_token_cost_priority": 5.5e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_priority": 5.5e-06, + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_priority": 3.3e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "azure/eu/gpt-5.4": { + "cache_read_input_token_cost": 2.8e-07, + "cache_read_input_token_cost_priority": 5.5e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_priority": 5.5e-06, + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_priority": 3.3e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, "azure/gpt-5.4-2026-03-05": { "cache_read_input_token_cost": 2.5e-07, "cache_read_input_token_cost_above_272k_tokens": 5e-07, @@ -5552,6 +5923,76 @@ "supports_tool_choice": true, "supports_vision": true }, + "azure/us/gpt-5.4-2026-03-05": { + "cache_read_input_token_cost": 2.8e-07, + "cache_read_input_token_cost_priority": 5.5e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_priority": 5.5e-06, + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_priority": 3.3e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "azure/eu/gpt-5.4-2026-03-05": { + "cache_read_input_token_cost": 2.8e-07, + "cache_read_input_token_cost_priority": 5.5e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_priority": 5.5e-06, + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_priority": 3.3e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, "azure/gpt-5.4-pro": { "cache_read_input_token_cost": 3e-06, "cache_read_input_token_cost_above_272k_tokens": 6e-06, @@ -5622,6 +6063,522 @@ "supports_vision": true, "supports_web_search": true }, + "azure/gpt-5.6": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "cache_read_input_token_cost_priority": 1e-06, + "cache_read_input_token_cost_above_272k_tokens_priority": 2e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "input_cost_per_token_priority": 1e-05, + "input_cost_per_token_above_272k_tokens_priority": 2e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "output_cost_per_token_above_272k_tokens": 4.5e-05, + "output_cost_per_token_priority": 6e-05, + "output_cost_per_token_above_272k_tokens_priority": 9e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/gpt-5.6-sol": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "cache_read_input_token_cost_priority": 1e-06, + "cache_read_input_token_cost_above_272k_tokens_priority": 2e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "input_cost_per_token_priority": 1e-05, + "input_cost_per_token_above_272k_tokens_priority": 2e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "output_cost_per_token_above_272k_tokens": 4.5e-05, + "output_cost_per_token_priority": 6e-05, + "output_cost_per_token_above_272k_tokens_priority": 9e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/gpt-5.6-terra": { + "cache_read_input_token_cost": 2.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 5e-07, + "cache_read_input_token_cost_priority": 5e-07, + "cache_read_input_token_cost_above_272k_tokens_priority": 1e-06, + "input_cost_per_token": 2.5e-06, + "input_cost_per_token_above_272k_tokens": 5e-06, + "input_cost_per_token_priority": 5e-06, + "input_cost_per_token_above_272k_tokens_priority": 1e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "output_cost_per_token_above_272k_tokens": 2.25e-05, + "output_cost_per_token_priority": 3e-05, + "output_cost_per_token_above_272k_tokens_priority": 4.5e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/gpt-5.6-luna": { + "cache_read_input_token_cost": 1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2e-07, + "cache_read_input_token_cost_priority": 2e-07, + "cache_read_input_token_cost_above_272k_tokens_priority": 4e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "input_cost_per_token_priority": 2e-06, + "input_cost_per_token_above_272k_tokens_priority": 4e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_272k_tokens": 9e-06, + "output_cost_per_token_priority": 1.2e-05, + "output_cost_per_token_above_272k_tokens_priority": 1.8e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/us/gpt-5.6": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.375e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/us/gpt-5.6-sol": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.375e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/us/gpt-5.6-terra": { + "cache_read_input_token_cost": 2.75e-07, + "cache_read_input_token_cost_above_272k_tokens": 5.5e-07, + "cache_read_input_token_cost_priority": 6.875e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_above_272k_tokens": 5.5e-06, + "input_cost_per_token_priority": 6.875e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_above_272k_tokens": 2.475e-05, + "output_cost_per_token_priority": 4.125e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/us/gpt-5.6-luna": { + "cache_read_input_token_cost": 1.1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2.2e-07, + "cache_read_input_token_cost_priority": 2.75e-07, + "input_cost_per_token": 1.1e-06, + "input_cost_per_token_above_272k_tokens": 2.2e-06, + "input_cost_per_token_priority": 2.75e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 6.6e-06, + "output_cost_per_token_above_272k_tokens": 9.9e-06, + "output_cost_per_token_priority": 1.65e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.6": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.375e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.6-sol": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.375e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.6-terra": { + "cache_read_input_token_cost": 2.75e-07, + "cache_read_input_token_cost_above_272k_tokens": 5.5e-07, + "cache_read_input_token_cost_priority": 6.875e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_above_272k_tokens": 5.5e-06, + "input_cost_per_token_priority": 6.875e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_above_272k_tokens": 2.475e-05, + "output_cost_per_token_priority": 4.125e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.6-luna": { + "cache_read_input_token_cost": 1.1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2.2e-07, + "cache_read_input_token_cost_priority": 2.75e-07, + "input_cost_per_token": 1.1e-06, + "input_cost_per_token_above_272k_tokens": 2.2e-06, + "input_cost_per_token_priority": 2.75e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 6.6e-06, + "output_cost_per_token_above_272k_tokens": 9.9e-06, + "output_cost_per_token_priority": 1.65e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, "azure/gpt-5.5": { "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, @@ -5667,6 +6624,90 @@ "supports_xhigh_reasoning_effort": true, "supports_minimal_reasoning_effort": false }, + "azure/us/gpt-5.5": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.38e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.5": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.38e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, "azure/gpt-5.5-2026-04-23": { "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, @@ -5709,6 +6750,84 @@ "supports_vision": true, "supports_web_search": true }, + "azure/us/gpt-5.5-2026-04-23": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.38e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, + "azure/eu/gpt-5.5-2026-04-23": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.38e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "azure/gpt-5.5-pro": { "cache_read_input_token_cost": 3e-06, "cache_read_input_token_cost_above_272k_tokens": 6e-06, @@ -9132,17 +10251,16 @@ }, "bedrock/us-east-1/minimax.minimax-m2.5": { "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 8192, "max_tokens": 8192, "mode": "chat", - "source": "https://aws.amazon.com/bedrock/pricing/", "supports_function_calling": true, - "supports_reasoning": true, "supports_system_messages": true, "supports_tool_choice": true, - "output_cost_per_token": 1.2e-06 + "source": "https://aws.amazon.com/bedrock/pricing/" }, "bedrock/us-east-1/moonshotai.kimi-k2-thinking": { "input_cost_per_token": 6e-07, @@ -9371,7 +10489,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-east-1/claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 4.5e-06, @@ -9393,7 +10512,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-east-1/meta.llama3-70b-instruct-v1:0": { "input_cost_per_token": 2.65e-06, @@ -9546,7 +10666,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-west-1/claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 4.5e-06, @@ -9568,7 +10689,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-west-1/meta.llama3-70b-instruct-v1:0": { "input_cost_per_token": 2.65e-06, @@ -9754,17 +10876,16 @@ }, "bedrock/us-west-2/minimax.minimax-m2.5": { "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 8192, "max_tokens": 8192, "mode": "chat", - "source": "https://aws.amazon.com/bedrock/pricing/", "supports_function_calling": true, - "supports_reasoning": true, "supports_system_messages": true, "supports_tool_choice": true, - "output_cost_per_token": 1.2e-06 + "source": "https://aws.amazon.com/bedrock/pricing/" }, "bedrock/us-west-2/moonshotai.kimi-k2-thinking": { "input_cost_per_token": 6e-07, @@ -10245,6 +11366,40 @@ "supports_vision": true, "supports_web_search": true }, + "claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "anthropic", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_max_reasoning_effort": true, + "provider_specific_entry": { + "us": 1.1 + }, + "supports_output_config": true + }, "claude-sonnet-4-6": { "cache_creation_input_token_cost": 3.75e-06, "cache_creation_input_token_cost_above_1hr": 6e-06, @@ -10298,7 +11453,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "supports_parallel_tool_use_config": true }, "claude-opus-4-1": { "cache_creation_input_token_cost": 1.875e-05, @@ -14370,7 +15526,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-3-5-sonnet-20240620-v1:0": { "input_cost_per_token": 3e-06, @@ -14551,7 +15708,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "eu.anthropic.claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 4.125e-06, @@ -14583,7 +15741,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "eu.meta.llama3-2-1b-instruct-v1:0": { "input_cost_per_token": 1.3e-07, @@ -17588,6 +18747,49 @@ }, "supports_image_size": false }, + "gemini/gemini-3-pro-image": { + "input_cost_per_image": 0.0011, + "input_cost_per_token": 2e-06, + "input_cost_per_token_batches": 1e-06, + "litellm_provider": "gemini", + "max_input_tokens": 65536, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "image_generation", + "output_cost_per_image": 0.134, + "output_cost_per_image_token": 0.00012, + "output_cost_per_token": 1.2e-05, + "rpm": 1000, + "tpm": 4000000, + "output_cost_per_token_batches": 6e-06, + "source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-3-pro-image", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/completions", + "/v1/batch" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text", + "image" + ], + "supports_function_calling": false, + "supports_prompt_caching": true, + "supports_reasoning": false, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_vision": true, + "supports_web_search": true, + "search_context_cost_per_query": { + "search_context_size_low": 0.014, + "search_context_size_medium": 0.014, + "search_context_size_high": 0.014 + }, + "web_search_billing_unit": "per_query" + }, "gemini/gemini-3-pro-image-preview": { "input_cost_per_image": 0.0011, "input_cost_per_token": 2e-06, @@ -17619,6 +18821,49 @@ ], "supports_function_calling": false, "supports_prompt_caching": true, + "supports_reasoning": false, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_vision": true, + "supports_web_search": true, + "search_context_cost_per_query": { + "search_context_size_low": 0.014, + "search_context_size_medium": 0.014, + "search_context_size_high": 0.014 + }, + "web_search_billing_unit": "per_query" + }, + "gemini/gemini-3.1-flash-image": { + "input_cost_per_token": 2.5e-07, + "input_cost_per_token_batches": 1.25e-07, + "litellm_provider": "gemini", + "max_input_tokens": 65536, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "image_generation", + "output_cost_per_image": 0.045, + "output_cost_per_image_token": 6e-05, + "output_cost_per_token": 1.5e-06, + "output_cost_per_token_batches": 7.5e-07, + "rpm": 1000, + "tpm": 4000000, + "source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-3.1-flash-image", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/completions", + "/v1/batch" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text", + "image" + ], + "supports_function_calling": false, + "supports_prompt_caching": true, + "supports_reasoning": false, "supports_response_schema": true, "supports_system_messages": true, "supports_vision": true, @@ -17660,6 +18905,7 @@ ], "supports_function_calling": false, "supports_prompt_caching": true, + "supports_reasoning": false, "supports_response_schema": true, "supports_system_messages": true, "supports_vision": true, @@ -18914,7 +20160,8 @@ "max_tokens": 16000, "mode": "chat", "supported_endpoints": [ - "/v1/chat/completions" + "/v1/chat/completions", + "/v1/messages" ], "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -18927,7 +20174,8 @@ "max_tokens": 16000, "mode": "chat", "supported_endpoints": [ - "/v1/chat/completions" + "/v1/chat/completions", + "/v1/messages" ], "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -18944,7 +20192,6 @@ "supported_endpoints": [ "/v1/chat/completions" ], - "supports_adaptive_thinking": true, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_vision": true @@ -18980,7 +20227,8 @@ "max_tokens": 16000, "mode": "chat", "supported_endpoints": [ - "/v1/chat/completions" + "/v1/chat/completions", + "/v1/messages" ], "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -19780,7 +21028,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 3.75e-06, @@ -19809,7 +21058,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "global.anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.25e-06, @@ -19832,7 +21082,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "global.amazon.nova-2-lite-v1:0": { "cache_read_input_token_cost": 7.5e-08, @@ -21676,6 +22927,218 @@ "supports_xhigh_reasoning_effort": true, "supports_minimal_reasoning_effort": true }, + "gpt-5.6": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05, + "cache_creation_input_token_cost_flex": 3.125e-06, + "cache_creation_input_token_cost_priority": 1.25e-05, + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "cache_read_input_token_cost_flex": 2.5e-07, + "cache_read_input_token_cost_priority": 1e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "input_cost_per_token_batches": 2.5e-06, + "input_cost_per_token_flex": 2.5e-06, + "input_cost_per_token_priority": 1e-05, + "litellm_provider": "openai", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "output_cost_per_token_above_272k_tokens": 4.5e-05, + "output_cost_per_token_batches": 1.5e-05, + "output_cost_per_token_flex": 1.5e-05, + "output_cost_per_token_priority": 6e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, + "gpt-5.6-sol": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05, + "cache_creation_input_token_cost_flex": 3.125e-06, + "cache_creation_input_token_cost_priority": 1.25e-05, + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "cache_read_input_token_cost_flex": 2.5e-07, + "cache_read_input_token_cost_priority": 1e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "input_cost_per_token_batches": 2.5e-06, + "input_cost_per_token_flex": 2.5e-06, + "input_cost_per_token_priority": 1e-05, + "litellm_provider": "openai", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "output_cost_per_token_above_272k_tokens": 4.5e-05, + "output_cost_per_token_batches": 1.5e-05, + "output_cost_per_token_flex": 1.5e-05, + "output_cost_per_token_priority": 6e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, + "gpt-5.6-terra": { + "cache_creation_input_token_cost": 3.125e-06, + "cache_creation_input_token_cost_above_272k_tokens": 6.25e-06, + "cache_creation_input_token_cost_flex": 1.5625e-06, + "cache_creation_input_token_cost_priority": 6.25e-06, + "cache_read_input_token_cost": 2.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 5e-07, + "cache_read_input_token_cost_flex": 1.25e-07, + "cache_read_input_token_cost_priority": 5e-07, + "input_cost_per_token": 2.5e-06, + "input_cost_per_token_above_272k_tokens": 5e-06, + "input_cost_per_token_batches": 1.25e-06, + "input_cost_per_token_flex": 1.25e-06, + "input_cost_per_token_priority": 5e-06, + "litellm_provider": "openai", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "output_cost_per_token_above_272k_tokens": 2.25e-05, + "output_cost_per_token_batches": 7.5e-06, + "output_cost_per_token_flex": 7.5e-06, + "output_cost_per_token_priority": 3e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, + "gpt-5.6-luna": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_creation_input_token_cost_flex": 6.25e-07, + "cache_creation_input_token_cost_priority": 2.5e-06, + "cache_read_input_token_cost": 1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2e-07, + "cache_read_input_token_cost_flex": 5e-08, + "cache_read_input_token_cost_priority": 2e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "input_cost_per_token_batches": 5e-07, + "input_cost_per_token_flex": 5e-07, + "input_cost_per_token_priority": 2e-06, + "litellm_provider": "openai", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_272k_tokens": 9e-06, + "output_cost_per_token_batches": 3e-06, + "output_cost_per_token_flex": 3e-06, + "output_cost_per_token_priority": 1.2e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, "gpt-5.5": { "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, @@ -21696,8 +23159,8 @@ "output_cost_per_token_flex": 1.5e-05, "output_cost_per_token_batches": 1.5e-05, "output_cost_per_token_priority": 6e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -21745,8 +23208,8 @@ "output_cost_per_token_flex": 1.5e-05, "output_cost_per_token_batches": 1.5e-05, "output_cost_per_token_priority": 6e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -21790,8 +23253,8 @@ "output_cost_per_token_above_272k_tokens": 0.00027, "output_cost_per_token_flex": 9e-05, "output_cost_per_token_batches": 9e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/responses", "/v1/batch" @@ -21835,8 +23298,8 @@ "output_cost_per_token_above_272k_tokens": 0.00027, "output_cost_per_token_flex": 9e-05, "output_cost_per_token_batches": 9e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/responses", "/v1/batch" @@ -21884,8 +23347,8 @@ "output_cost_per_token_flex": 7.5e-06, "output_cost_per_token_batches": 7.5e-06, "output_cost_per_token_priority": 3e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -21932,8 +23395,8 @@ "output_cost_per_token_flex": 7.5e-06, "output_cost_per_token_batches": 7.5e-06, "output_cost_per_token_priority": 3e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -21973,8 +23436,8 @@ "output_cost_per_token_above_272k_tokens": 0.00027, "output_cost_per_token_flex": 9e-05, "output_cost_per_token_batches": 9e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/responses", "/v1/batch" @@ -22017,8 +23480,8 @@ "output_cost_per_token_above_272k_tokens": 0.00027, "output_cost_per_token_flex": 9e-05, "output_cost_per_token_batches": 9e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/responses", "/v1/batch" @@ -22062,8 +23525,8 @@ "output_cost_per_token_flex": 2.25e-06, "output_cost_per_token_batches": 2.25e-06, "output_cost_per_token_priority": 9e-06, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22108,8 +23571,8 @@ "output_cost_per_token_flex": 2.25e-06, "output_cost_per_token_batches": 2.25e-06, "output_cost_per_token_priority": 9e-06, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22151,8 +23614,8 @@ "output_cost_per_token": 1.25e-06, "output_cost_per_token_flex": 6.25e-07, "output_cost_per_token_batches": 6.25e-07, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22194,8 +23657,8 @@ "output_cost_per_token": 1.25e-06, "output_cost_per_token_flex": 6.25e-07, "output_cost_per_token_batches": 6.25e-07, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22906,6 +24369,76 @@ "supports_system_messages": true, "supports_tool_choice": true }, + "gpt-realtime-2.1": { + "cache_creation_input_audio_token_cost": 4e-07, + "cache_read_input_audio_token_cost": 4e-07, + "cache_read_input_token_cost": 4e-07, + "input_cost_per_audio_token": 3.2e-05, + "input_cost_per_image": 5e-06, + "input_cost_per_token": 4e-06, + "litellm_provider": "openai", + "max_input_tokens": 128000, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_audio_token": 6.4e-05, + "output_cost_per_token": 2.4e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/realtime" + ], + "supported_modalities": [ + "text", + "image", + "audio" + ], + "supported_output_modalities": [ + "text", + "audio" + ], + "supports_audio_input": true, + "supports_audio_output": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_system_messages": true, + "supports_tool_choice": true + }, + "gpt-realtime-2.1-mini": { + "cache_creation_input_audio_token_cost": 3e-07, + "cache_read_input_audio_token_cost": 3e-07, + "cache_read_input_token_cost": 6e-08, + "input_cost_per_audio_token": 1e-05, + "input_cost_per_image": 8e-07, + "input_cost_per_token": 6e-07, + "litellm_provider": "openai", + "max_input_tokens": 128000, + "max_output_tokens": 4096, + "max_tokens": 4096, + "mode": "chat", + "output_cost_per_audio_token": 2e-05, + "output_cost_per_token": 2.4e-06, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/realtime" + ], + "supported_modalities": [ + "text", + "image", + "audio" + ], + "supported_output_modalities": [ + "text", + "audio" + ], + "supports_audio_input": true, + "supports_audio_output": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_system_messages": true, + "supports_tool_choice": true + }, "gpt-realtime-mini": { "cache_creation_input_audio_token_cost": 3e-07, "cache_read_input_audio_token_cost": 3e-07, @@ -23901,7 +25434,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "jp.anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.375e-06, @@ -23924,7 +25458,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "crusoe/deepseek-ai/DeepSeek-R1-0528": { "input_cost_per_token": 3e-06, @@ -24620,6 +26155,42 @@ "supports_function_calling": true, "supports_tool_choice": false }, + "meta/muse-spark-1.1": { + "cache_read_input_token_cost": 1.5e-07, + "input_cost_per_token": 1.25e-06, + "litellm_provider": "meta", + "max_input_tokens": 1048576, + "max_output_tokens": 131072, + "max_tokens": 131072, + "mode": "chat", + "output_cost_per_token": 4.25e-06, + "source": "https://dev.meta.ai/docs/getting-started/pricing-rate-limits", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses", + "/v1/messages" + ], + "supported_modalities": [ + "text", + "image", + "video" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, "meta_llama/Llama-3.3-70B-Instruct": { "litellm_provider": "meta_llama", "max_input_tokens": 128000, @@ -24712,14 +26283,13 @@ }, "minimax.minimax-m2.5": { "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 8192, "max_tokens": 8192, "mode": "chat", - "output_cost_per_token": 1.2e-06, "supports_function_calling": true, - "supports_reasoning": true, "supports_system_messages": true, "supports_tool_choice": true, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -28175,7 +29745,6 @@ "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_200k_tokens": 2.25e-05, "source": "https://openrouter.ai/anthropic/claude-sonnet-4.6", - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -28215,7 +29784,6 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -28277,7 +29845,6 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -30200,7 +31767,6 @@ "supports_adaptive_thinking": true, "litellm_provider": "perplexity", "mode": "responses", - "supports_adaptive_thinking": true, "supports_web_search": true, "supports_reasoning": false, "supports_function_calling": true, @@ -30210,7 +31776,6 @@ "supports_adaptive_thinking": true, "litellm_provider": "perplexity", "mode": "responses", - "supports_adaptive_thinking": true, "supports_web_search": true, "supports_reasoning": false, "supports_function_calling": true, @@ -31126,7 +32691,7 @@ "supports_tool_choice": true }, "sambanova/Meta-Llama-3.2-1B-Instruct": { - "deprecation_date": "2025-06-25", + "deprecation_date": "2025-06-25", "input_cost_per_token": 4e-08, "litellm_provider": "sambanova", "max_input_tokens": 16384, @@ -31257,15 +32822,15 @@ "supports_vision": true, "source": "https://cloud.sambanova.ai/plans/pricing" }, - "snowflake/claude-3-5-sonnet": { + "snowflake/claude-3-5-sonnet": { "litellm_provider": "snowflake", "max_input_tokens": 200000, "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, "supports_computer_use": true, "supports_function_calling": true, "supports_vision": true, @@ -31273,14 +32838,14 @@ "supports_system_messages": true, "supports_response_schema": true }, - "snowflake/deepseek-r1": { + "snowflake/deepseek-r1": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.00000135, - "output_cost_per_token": 0.0000054, + "input_cost_per_token": 1.35e-06, + "output_cost_per_token": 5.4e-06, "supports_reasoning": true, "supports_system_messages": true }, @@ -31339,8 +32904,8 @@ "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.0000012, - "output_cost_per_token": 0.0000012, + "input_cost_per_token": 1.2e-06, + "output_cost_per_token": 1.2e-06, "supports_function_calling": true, "supports_system_messages": true }, @@ -31350,8 +32915,8 @@ "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.00000072, - "output_cost_per_token": 0.00000072, + "input_cost_per_token": 7.2e-07, + "output_cost_per_token": 7.2e-07, "supports_function_calling": true, "supports_system_messages": true }, @@ -31361,8 +32926,8 @@ "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.00000024, - "output_cost_per_token": 0.00000024, + "input_cost_per_token": 2.4e-07, + "output_cost_per_token": 2.4e-07, "supports_system_messages": true }, "snowflake/llama3.2-1b": { @@ -31379,17 +32944,17 @@ "max_tokens": 8192, "mode": "chat" }, - "snowflake/llama3.3-70b": { + "snowflake/llama3.3-70b": { "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, - "input_cost_per_token": 0.00000072, - "output_cost_per_token": 0.00000072, + "input_cost_per_token": 7.2e-07, + "output_cost_per_token": 7.2e-07, "litellm_provider": "snowflake", "mode": "chat", "supports_function_calling": true, "supports_system_messages": true - }, + }, "snowflake/mistral-7b": { "litellm_provider": "snowflake", "max_input_tokens": 32000, @@ -31404,14 +32969,14 @@ "max_tokens": 8192, "mode": "chat" }, - "snowflake/mistral-large2": { + "snowflake/mistral-large2": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.000002, - "output_cost_per_token": 0.000006, + "input_cost_per_token": 2e-06, + "output_cost_per_token": 6e-06, "supports_function_calling": true, "supports_system_messages": true, "supports_response_schema": true @@ -31451,17 +33016,17 @@ "max_tokens": 8192, "mode": "chat" }, - "snowflake/snowflake-llama-3.3-70b": { + "snowflake/snowflake-llama-3.3-70b": { "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, - "input_cost_per_token": 0.00000072, - "output_cost_per_token": 0.00000072, + "input_cost_per_token": 7.2e-07, + "output_cost_per_token": 7.2e-07, "litellm_provider": "snowflake", "mode": "chat", "supports_function_calling": true, "supports_system_messages": true - }, + }, "stability/sd3": { "litellm_provider": "stability", "mode": "image_generation", @@ -32545,7 +34110,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-3-5-sonnet-20240620-v1:0": { "input_cost_per_token": 3e-06, @@ -32704,7 +34270,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 4.5e-06, @@ -32713,7 +34280,7 @@ "input_cost_per_token": 3.6e-06, "input_cost_per_token_above_200k_tokens": 7.2e-06, "output_cost_per_token_above_200k_tokens": 2.7e-05, - "cache_creation_input_token_cost_above_200k_tokens": 9.0e-06, + "cache_creation_input_token_cost_above_200k_tokens": 9e-06, "cache_creation_input_token_cost_above_1hr_above_200k_tokens": 1.44e-05, "cache_read_input_token_cost_above_200k_tokens": 7.2e-07, "litellm_provider": "bedrock_converse", @@ -32731,7 +34298,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.375e-06, @@ -32753,7 +34321,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-opus-4-20250514-v1:0": { "cache_creation_input_token_cost": 1.875e-05, @@ -32807,7 +34376,8 @@ "supports_vision": true, "supports_native_structured_output": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "high" + "bedrock_output_config_effort_ceiling": "high", + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-opus-4-5-20251101-v1:0": { "cache_creation_input_token_cost": 6.25e-06, @@ -32836,7 +34406,8 @@ "supports_vision": true, "supports_native_structured_output": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "high" + "bedrock_output_config_effort_ceiling": "high", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-opus-4-5-20251101-v1:0": { "cache_creation_input_token_cost": 6.25e-06, @@ -32864,7 +34435,8 @@ "supports_vision": true, "supports_native_structured_output": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "high" + "bedrock_output_config_effort_ceiling": "high", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 3.75e-06, @@ -32893,7 +34465,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "us.deepseek.r1-v1:0": { "input_cost_per_token": 1.35e-06, @@ -33455,7 +35028,6 @@ "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -34357,6 +35929,19 @@ "/v1/audio/speech" ] }, + "vertex_ai/chirp_3": { + "input_cost_per_second": 0.00026667, + "litellm_provider": "vertex_ai", + "metadata": { + "calculation": "$0.016/60 seconds = $0.00026667 per second", + "original_pricing_per_minute": 0.016 + }, + "mode": "audio_transcription", + "source": "https://cloud.google.com/speech-to-text/pricing", + "supported_endpoints": [ + "/v1/audio/transcriptions" + ] + }, "vertex_ai/claude-3-5-haiku": { "input_cost_per_token": 1e-06, "litellm_provider": "vertex_ai-anthropic_models", @@ -34690,7 +36275,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34720,7 +36304,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34750,7 +36333,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34781,7 +36363,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34872,7 +36453,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34903,7 +36483,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34944,6 +36523,36 @@ "supports_tool_choice": true, "supports_vision": true }, + "vertex_ai/claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "vertex_ai-anthropic_models", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_max_reasoning_effort": true + }, "vertex_ai/claude-sonnet-4-6": { "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 3.75e-06, @@ -34956,7 +36565,6 @@ "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -35256,6 +36864,7 @@ "supports_parallel_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, + "supports_reasoning": false, "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, @@ -35265,6 +36874,22 @@ "tpm": 8000000, "supports_image_size": false }, + "vertex_ai/gemini-3-pro-image": { + "input_cost_per_image": 0.0011, + "input_cost_per_token": 2e-06, + "input_cost_per_token_batches": 1e-06, + "litellm_provider": "vertex_ai-language-models", + "max_input_tokens": 65536, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "image_generation", + "output_cost_per_image": 0.134, + "output_cost_per_image_token": 0.00012, + "output_cost_per_token": 1.2e-05, + "output_cost_per_token_batches": 6e-06, + "supports_reasoning": false, + "source": "https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro-image" + }, "vertex_ai/gemini-3-pro-image-preview": { "input_cost_per_image": 0.0011, "input_cost_per_token": 2e-06, @@ -35278,8 +36903,23 @@ "output_cost_per_image_token": 0.00012, "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, + "supports_reasoning": false, "source": "https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro-image" }, + "vertex_ai/gemini-3.1-flash-image": { + "input_cost_per_image": 0.00056, + "input_cost_per_token": 5e-07, + "litellm_provider": "vertex_ai-language-models", + "max_input_tokens": 65536, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "image_generation", + "output_cost_per_image": 0.0672, + "output_cost_per_image_token": 6e-05, + "output_cost_per_token": 3e-06, + "supports_reasoning": false, + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#gemini-models" + }, "vertex_ai/gemini-3.1-flash-image-preview": { "input_cost_per_image": 0.00056, "input_cost_per_token": 5e-07, @@ -35291,6 +36931,7 @@ "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, "output_cost_per_token": 3e-06, + "supports_reasoning": false, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#gemini-models" }, "vertex_ai/gemini-3.1-flash-lite-preview": { @@ -37395,6 +39036,48 @@ "supports_vision": true, "supports_web_search": true }, + "xai/grok-4.5": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_200k_tokens": 1e-06, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_200k_tokens": 4e-06, + "litellm_provider": "xai", + "max_input_tokens": 500000, + "max_output_tokens": 500000, + "max_tokens": 500000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_200k_tokens": 1.2e-05, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, + "xai/grok-4.5-latest": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_200k_tokens": 1e-06, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_200k_tokens": 4e-06, + "litellm_provider": "xai", + "max_input_tokens": 500000, + "max_output_tokens": 500000, + "max_tokens": 500000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_200k_tokens": 1.2e-05, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "xai/grok-beta": { "input_cost_per_token": 5e-06, "litellm_provider": "xai", @@ -37485,12 +39168,12 @@ }, "zai.glm-5": { "input_cost_per_token": 1e-06, + "output_cost_per_token": 3.2e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", - "output_cost_per_token": 3.2e-06, "supports_function_calling": true, "supports_reasoning": true, "supports_system_messages": true, @@ -37511,20 +39194,6 @@ "supports_tool_choice": true, "source": "https://aws.amazon.com/bedrock/pricing/" }, - "zai.glm-5": { - "input_cost_per_token": 1e-06, - "litellm_provider": "bedrock_converse", - "max_input_tokens": 200000, - "max_output_tokens": 128000, - "max_tokens": 128000, - "mode": "chat", - "output_cost_per_token": 3.2e-06, - "source": "https://aws.amazon.com/bedrock/pricing/", - "supports_function_calling": true, - "supports_reasoning": true, - "supports_system_messages": true, - "supports_tool_choice": true - }, "zai/glm-5": { "cache_creation_input_token_cost": 0, "cache_read_input_token_cost": 2e-07, @@ -42381,6 +44050,36 @@ "search_context_size_high": 0.035 } }, + "vertex_ai/claude-sonnet-5@default": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "vertex_ai-anthropic_models", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_max_reasoning_effort": true + }, "vertex_ai/claude-sonnet-4-6@default": { "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 3.75e-06, @@ -42393,7 +44092,6 @@ "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -42427,7 +44125,10 @@ "max_output_tokens": 32768, "max_tokens": 32768, "mode": "chat", - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_reasoning": true, @@ -42442,7 +44143,10 @@ "max_output_tokens": 32768, "max_tokens": 32768, "mode": "chat", - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_reasoning": true, @@ -42457,7 +44161,9 @@ "max_output_tokens": 65536, "max_tokens": 65536, "mode": "chat", - "supported_endpoints": ["/v1/chat/completions"], + "supported_endpoints": [ + "/v1/chat/completions" + ], "supports_function_calling": true, "supports_reasoning": true, "supports_response_schema": true, @@ -42471,7 +44177,9 @@ "max_output_tokens": 65536, "max_tokens": 65536, "mode": "chat", - "supported_endpoints": ["/v1/chat/completions"], + "supported_endpoints": [ + "/v1/chat/completions" + ], "supports_function_calling": true, "supports_reasoning": true, "supports_response_schema": true, @@ -42487,9 +44195,16 @@ "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/responses"], - "supported_modalities": ["text", "image"], - "supported_output_modalities": ["text"], + "supported_endpoints": [ + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, @@ -42507,9 +44222,16 @@ "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/responses"], - "supported_modalities": ["text", "image"], - "supported_output_modalities": ["text"], + "supported_endpoints": [ + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, @@ -42526,7 +44248,10 @@ "max_tokens": 256000, "mode": "chat", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": false, "supports_reasoning": true, @@ -42542,7 +44267,10 @@ "max_tokens": 256000, "mode": "chat", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": false, "supports_reasoning": true, @@ -42558,13 +44286,36 @@ "max_tokens": 128000, "mode": "chat", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": false, "supports_reasoning": true, "supports_tool_choice": true, "supports_vision": true }, + "bedrock_mantle/xai.grok-4.3": { + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 2.5e-06, + "cache_read_input_token_cost": 2e-07, + "litellm_provider": "bedrock_mantle", + "max_input_tokens": 131072, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supports_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "source": "https://aws.amazon.com/bedrock/pricing/" + }, "volcengine/doubao-seed-2-0-pro-260215": { "litellm_provider": "volcengine", "max_input_tokens": 256000, @@ -42717,20 +44468,6 @@ } ] }, - "zai.glm-5": { - "input_cost_per_token": 1e-06, - "output_cost_per_token": 3.2e-06, - "litellm_provider": "bedrock_converse", - "max_input_tokens": 200000, - "max_output_tokens": 128000, - "max_tokens": 128000, - "mode": "chat", - "supports_function_calling": true, - "supports_reasoning": true, - "supports_system_messages": true, - "supports_tool_choice": true, - "source": "https://aws.amazon.com/bedrock/pricing/" - }, "bedrock/us-east-1/zai.glm-5": { "input_cost_per_token": 1e-06, "output_cost_per_token": 3.2e-06, @@ -42759,45 +44496,6 @@ "supports_tool_choice": true, "source": "https://aws.amazon.com/bedrock/pricing/" }, - "minimax.minimax-m2.5": { - "input_cost_per_token": 3e-07, - "output_cost_per_token": 1.2e-06, - "litellm_provider": "bedrock_converse", - "max_input_tokens": 1000000, - "max_output_tokens": 8192, - "max_tokens": 8192, - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_tool_choice": true, - "source": "https://aws.amazon.com/bedrock/pricing/" - }, - "bedrock/us-east-1/minimax.minimax-m2.5": { - "input_cost_per_token": 3e-07, - "output_cost_per_token": 1.2e-06, - "litellm_provider": "bedrock", - "max_input_tokens": 1000000, - "max_output_tokens": 8192, - "max_tokens": 8192, - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_tool_choice": true, - "source": "https://aws.amazon.com/bedrock/pricing/" - }, - "bedrock/us-west-2/minimax.minimax-m2.5": { - "input_cost_per_token": 3e-07, - "output_cost_per_token": 1.2e-06, - "litellm_provider": "bedrock", - "max_input_tokens": 1000000, - "max_output_tokens": 8192, - "max_tokens": 8192, - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_tool_choice": true, - "source": "https://aws.amazon.com/bedrock/pricing/" - }, "bedrock/us-gov-east-1/anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.5e-06, "cache_creation_input_token_cost_above_1hr": 2.4e-06, @@ -42819,7 +44517,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_pdf_input": true + "supports_pdf_input": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-west-1/anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.5e-06, @@ -42842,364 +44541,368 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_pdf_input": true + "supports_pdf_input": true, + "supports_parallel_tool_use_config": true + }, + "snowflake/claude-sonnet-4-5": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/claude-sonnet-4-6": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/claude-4-sonnet": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/claude-4-opus": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2.5e-05, + "cache_read_input_token_cost": 5e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "supports_response_schema": true + }, + "snowflake/claude-haiku-4-5": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 1e-06, + "output_cost_per_token": 5e-06, + "cache_read_input_token_cost": 1e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/claude-3-7-sonnet": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "supports_response_schema": true + }, + "snowflake/openai-gpt-4.1": { + "max_tokens": 16384, + "max_input_tokens": 300000, + "max_output_tokens": 16384, + "input_cost_per_token": 2e-06, + "output_cost_per_token": 8e-06, + "cache_read_input_token_cost": 5e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/openai-gpt-5": { + "max_tokens": 16384, + "max_input_tokens": 300000, + "max_output_tokens": 16384, + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "cache_read_input_token_cost": 1.25e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "supports_response_schema": true + }, + "snowflake/openai-gpt-5-mini": { + "max_tokens": 16384, + "max_input_tokens": 1000000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/openai-gpt-5-nano": { + "max_tokens": 16384, + "max_input_tokens": 5000000, + "max_output_tokens": 16384, + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/llama4-maverick": { + "max_tokens": 16384, + "max_input_tokens": 128000, + "max_output_tokens": 16384, + "input_cost_per_token": 2.4e-07, + "output_cost_per_token": 9.7e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_system_messages": true + }, + "snowflake/snowflake-arctic-embed-l-v2.0": { + "max_tokens": 8192, + "max_input_tokens": 8192, + "input_cost_per_token": 7e-08, + "output_cost_per_token": 0.0, + "litellm_provider": "snowflake", + "mode": "embedding" + }, + "snowflake/snowflake-arctic-embed-m-v2.0": { + "max_tokens": 8192, + "max_input_tokens": 8192, + "input_cost_per_token": 7e-08, + "output_cost_per_token": 0.0, + "litellm_provider": "snowflake", + "mode": "embedding" + }, + "soniox/stt-async-v4": { + "litellm_provider": "soniox", + "max_output_tokens": 8000, + "max_tokens": 8000, + "input_cost_per_second": 0.0, + "output_cost_per_second": 2.77778e-05, + "mode": "audio_transcription", + "source": "https://soniox.com/pricing", + "supported_endpoints": [ + "/v1/audio/transcriptions" + ], + "supports_audio_input": true + }, + "soniox/stt-async-v5": { + "litellm_provider": "soniox", + "max_output_tokens": 8000, + "max_tokens": 8000, + "input_cost_per_second": 0.0, + "output_cost_per_second": 2.77778e-05, + "mode": "audio_transcription", + "source": "https://soniox.com/pricing", + "supported_endpoints": [ + "/v1/audio/transcriptions" + ], + "supports_audio_input": true + }, + "tensormesh/Qwen/Qwen3.5-397B-A17B-FP8": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 3.6e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 4.5e-07, + "output_cost_per_token": 1.8e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/Qwen/Qwen3.6-27B-FP8": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 3.2e-07, + "output_cost_per_token": 3.2e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/lukealonso/GLM-5.1-NVFP4-MTP": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 1.4e-06, + "output_cost_per_token": 4.4e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 202752, + "max_output_tokens": 202752, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/deepseek-ai/DeepSeek-V4-Flash": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 2.8e-07, + "cache_read_input_token_cost": 0, + "max_input_tokens": 32768, + "max_output_tokens": 32768, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/moonshotai/Kimi-K2.6": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 9.6e-07, + "output_cost_per_token": 4e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 32768, + "max_output_tokens": 32768, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/MiniMaxAI/MiniMax-M2.5": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 196608, + "max_output_tokens": 196608, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/google/gemma-4-31B-it": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 5.6e-07, + "cache_read_input_token_cost": 0, + "max_input_tokens": 32768, + "max_output_tokens": 32768, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/openai/gpt-oss-120b": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "cache_read_input_token_cost": 0, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/openai/gpt-oss-20b": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 7e-08, + "output_cost_per_token": 2.8e-07, + "cache_read_input_token_cost": 0, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" }, - "snowflake/claude-sonnet-4-5": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/claude-sonnet-4-6": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/claude-4-sonnet": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/claude-4-opus": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000005, - "output_cost_per_token": 0.000025, - "cache_read_input_token_cost": 0.0000005, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "supports_response_schema": true - }, - "snowflake/claude-haiku-4-5": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000001, - "output_cost_per_token": 0.000005, - "cache_read_input_token_cost": 0.0000001, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/claude-3-7-sonnet": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "supports_response_schema": true - }, - "snowflake/openai-gpt-4.1": { - "max_tokens": 16384, - "max_input_tokens": 300000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000002, - "output_cost_per_token": 0.000008, - "cache_read_input_token_cost": 0.0000005, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/openai-gpt-5": { - "max_tokens": 16384, - "max_input_tokens": 300000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.00000125, - "output_cost_per_token": 0.00001, - "cache_read_input_token_cost": 0.000000125, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "supports_response_schema": true - }, - "snowflake/openai-gpt-5-mini": { - "max_tokens": 16384, - "max_input_tokens": 1000000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.0000003, - "output_cost_per_token": 0.0000012, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/openai-gpt-5-nano": { - "max_tokens": 16384, - "max_input_tokens": 5000000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.00000015, - "output_cost_per_token": 0.0000006, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/llama4-maverick": { - "max_tokens": 16384, - "max_input_tokens": 128000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.00000024, - "output_cost_per_token": 0.00000097, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true - }, - "snowflake/snowflake-arctic-embed-l-v2.0": { - "max_tokens": 8192, - "max_input_tokens": 8192, - "input_cost_per_token": 0.00000007, - "output_cost_per_token": 0.0, - "litellm_provider": "snowflake", - "mode": "embedding" - }, - "snowflake/snowflake-arctic-embed-m-v2.0": { - "max_tokens": 8192, - "max_input_tokens": 8192, - "input_cost_per_token": 0.00000007, - "output_cost_per_token": 0.0, - "litellm_provider": "snowflake", - "mode": "embedding" - }, - "soniox/stt-async-v4": { - "litellm_provider": "soniox", - "max_output_tokens": 8000, - "max_tokens": 8000, - "input_cost_per_second": 0.0, - "output_cost_per_second": 0.0000277778, - "mode": "audio_transcription", - "source": "https://soniox.com/pricing", - "supported_endpoints": ["/v1/audio/transcriptions"], - "supports_audio_input": true - }, - "soniox/stt-async-v5": { - "litellm_provider": "soniox", - "max_output_tokens": 8000, - "max_tokens": 8000, - "input_cost_per_second": 0.0, - "output_cost_per_second": 0.0000277778, - "mode": "audio_transcription", - "source": "https://soniox.com/pricing", - "supported_endpoints": ["/v1/audio/transcriptions"], - "supports_audio_input": true - }, - "tensormesh/Qwen/Qwen3.5-397B-A17B-FP8": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 6e-07, - "output_cost_per_token": 3.6e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 262144, - "max_output_tokens": 262144, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 4.5e-07, - "output_cost_per_token": 1.8e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 262144, - "max_output_tokens": 262144, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/Qwen/Qwen3.6-27B-FP8": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 3.2e-07, - "output_cost_per_token": 3.2e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 262144, - "max_output_tokens": 262144, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/lukealonso/GLM-5.1-NVFP4-MTP": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 1.4e-06, - "output_cost_per_token": 4.4e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 202752, - "max_output_tokens": 202752, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/deepseek-ai/DeepSeek-V4-Flash": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 1.4e-07, - "output_cost_per_token": 2.8e-07, - "cache_read_input_token_cost": 0, - "max_input_tokens": 32768, - "max_output_tokens": 32768, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/moonshotai/Kimi-K2.6": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 9.6e-07, - "output_cost_per_token": 4e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 32768, - "max_output_tokens": 32768, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/MiniMaxAI/MiniMax-M2.5": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 3e-07, - "output_cost_per_token": 1.2e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 196608, - "max_output_tokens": 196608, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/google/gemma-4-31B-it": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 1.4e-07, - "output_cost_per_token": 5.6e-07, - "cache_read_input_token_cost": 0, - "max_input_tokens": 32768, - "max_output_tokens": 32768, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/openai/gpt-oss-120b": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 1.5e-07, - "output_cost_per_token": 6e-07, - "cache_read_input_token_cost": 0, - "max_input_tokens": 131072, - "max_output_tokens": 131072, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/openai/gpt-oss-20b": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 7e-08, - "output_cost_per_token": 2.8e-07, - "cache_read_input_token_cost": 0, - "max_input_tokens": 131072, - "max_output_tokens": 131072, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - } - , "deepseek-v4-flash": { "cache_creation_input_token_cost": 0.0, "cache_read_input_token_cost": 2.8e-09, @@ -43334,12 +45037,64 @@ "supports_tool_choice": true, "supports_vision": false }, + "tencent/deepseek-v4-pro": { + "cache_creation_input_token_cost": 0.0, + "cache_read_input_token_cost": 3.625e-09, + "input_cost_per_token": 4.35e-07, + "input_cost_per_token_cache_hit": 3.625e-09, + "litellm_provider": "tencent", + "max_input_tokens": 1000000, + "max_output_tokens": 384000, + "max_tokens": 384000, + "mode": "chat", + "output_cost_per_token": 8.7e-07, + "source": "https://www.tencentcloud.com/products/tokenhub", + "supported_endpoints": [ + "/v1/chat/completions" + ], + "supports_assistant_prefill": true, + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_reasoning": true, + "supports_vision": false + }, + "tencent/deepseek-v4-flash": { + "cache_creation_input_token_cost": 0.0, + "cache_read_input_token_cost": 2.8e-09, + "input_cost_per_token": 1.4e-07, + "input_cost_per_token_cache_hit": 2.8e-09, + "litellm_provider": "tencent", + "max_input_tokens": 1000000, + "max_output_tokens": 384000, + "max_tokens": 384000, + "mode": "chat", + "output_cost_per_token": 2.8e-07, + "source": "https://www.tencentcloud.com/products/tokenhub", + "supported_endpoints": [ + "/v1/chat/completions" + ], + "supports_assistant_prefill": true, + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_reasoning": true, + "supports_vision": false + }, "pinstripes/ps/glm-4.5-air": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, - "input_cost_per_token": 0.000000125, - "output_cost_per_token": 0.00000045, + "input_cost_per_token": 1.25e-07, + "output_cost_per_token": 4.5e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43351,8 +45106,8 @@ "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, - "input_cost_per_token": 0.00000014, - "output_cost_per_token": 0.00000045, + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 4.5e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43364,8 +45119,8 @@ "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, - "input_cost_per_token": 0.00000009, - "output_cost_per_token": 0.0000002, + "input_cost_per_token": 9e-08, + "output_cost_per_token": 2e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43377,8 +45132,8 @@ "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, - "input_cost_per_token": 0.0000003, - "output_cost_per_token": 0.0000006, + "input_cost_per_token": 3e-07, + "output_cost_per_token": 6e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43390,8 +45145,8 @@ "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, - "input_cost_per_token": 0.0000001, - "output_cost_per_token": 0.0000002, + "input_cost_per_token": 1e-07, + "output_cost_per_token": 2e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43403,8 +45158,8 @@ "max_tokens": 1000192, "max_input_tokens": 1000192, "max_output_tokens": 1000192, - "input_cost_per_token": 0.000000255, - "output_cost_per_token": 0.00000055, + "input_cost_per_token": 2.55e-07, + "output_cost_per_token": 5.5e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43412,39 +45167,61 @@ "supports_reasoning": false, "source": "https://pinstripes.io/pricing" }, - "fallback_generalizations": { - "rules": [ - { - "name": "anthropic-claude-adaptive-thinking", - "pattern": "(?:opus|sonnet|haiku)[-._](?:4[-._](?:[6-9]|[1-9]\\d)(?!\\d)|(?:[5-9]|[1-9]\\d{1,})[-._]\\d{1,2}(?!\\d))", - "description": "Claude opus/sonnet/haiku at version 4.6 or higher: 4.6 through 4.99, then any 5.x, 6.x or later major. The minor is capped at two digits so an 8-digit date suffix such as claude-opus-4-20250514 is never read as a >= 4.6 minor. Turns on adaptive thinking for new families with no code change.", - "extends": "anthropic-claude", - "model_info": { - "supports_adaptive_thinking": true - } - }, - { - "name": "anthropic-claude", - "pattern": "^claude-[a-z]+-\\d+[-.]\\d+(?:-\\d{8})?$", - "description": "Any Claude family-major-minor id, optionally with an 8-digit date suffix, anchored to the whole name. Version-neutral fallback that gives an unmapped Claude provider routing and baseline capabilities; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number.", - "model_info": { - "litellm_provider": "anthropic", - "mode": "chat", - "max_input_tokens": 200000, - "max_output_tokens": 64000, - "max_tokens": 64000, - "supports_function_calling": true, - "supports_parallel_function_calling": true, - "supports_vision": true, - "supports_tool_choice": true, - "supports_assistant_prefill": true, - "supports_prompt_caching": true, - "supports_response_schema": true, - "supports_reasoning": true, - "supports_pdf_input": true, - "supports_system_messages": true - } - } - ] - } + "fallback_generalizations": { + "rules": [ + { + "name": "bedrock-claude-ids", + "pattern": "^(?:[a-z-]+\\.)?anthropic\\.claude-", + "description": "A Bedrock-syntax Claude id, for every version: anthropic.claude- at the start of the name, optionally behind a single dotted geo segment (us./eu./au./jp./apac./global./us-gov.). Anchored to the start because routing rules see the raw request string and provider inference feeds the proxy's provider/* wildcard access checks: an id under an unrecognized namespace such as bedrockz/anthropic.claude-... must stay unroutable rather than resolve to bedrock and slip through a bedrock/* key. Routes to bedrock before the bare-id Anthropic rule is consulted.", + "model_info": { + "litellm_provider": "bedrock" + } + }, + { + "name": "anthropic-claude-ids", + "pattern": "^claude-[a-z]+-\\d+(?:[-.]\\d+)?(?:-\\d{8})?$", + "description": "A bare Claude family-major id with an optional minor and an optional 8-digit date suffix, anchored to the whole name, so claude-newfamily-5 routes like claude-newfamily-5-1 does. Routes an unmapped Claude id that carries no provider namespace to the Anthropic API.", + "model_info": { + "litellm_provider": "anthropic" + } + }, + { + "name": "claude-family-baseline", + "pattern": "claude-[a-z]+-\\d+(?:[-.]\\d+)?(?:-\\d{8})?", + "description": "Any Claude family-major id with an optional minor and an optional 8-digit date suffix, under any provider namespace (bare, bedrock-dotted, vertex, databricks, ...), so bare majors like claude-newfamily-5 get the same baseline as claude-newfamily-5-1. Carries the model-family facts every Claude shares; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number.", + "model_info": { + "mode": "chat", + "max_input_tokens": 200000, + "max_output_tokens": 64000, + "max_tokens": 64000, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_vision": true, + "supports_tool_choice": true, + "supports_assistant_prefill": true, + "supports_prompt_caching": true, + "supports_response_schema": true, + "supports_reasoning": true, + "supports_pdf_input": true, + "supports_system_messages": true + } + }, + { + "name": "claude-adaptive-thinking", + "pattern": "claude-[a-z]+-(?:4[-._](?:[6-9]|[1-9]\\d)(?!\\d)|(?:[5-9]|[1-9]\\d)(?!\\d)(?:[-._]\\d{1,2}(?!\\d))?)", + "description": "Claude at version 4.6 or higher, in any id shape that contains claude--: minors 4.6 through 4.99, any later major-minor, and bare 5+ majors so a new family shaped like claude-fable-5 matches. Requiring the claude- prefix keeps non-Claude names such as team-sonnet-5-1 out. The minor is capped at two digits so an 8-digit date suffix such as claude-opus-4-20250514 is never read as a >= 4.6 minor. Turns on adaptive thinking for new versions and new families with no code change.", + "model_info": { + "supports_adaptive_thinking": true + } + }, + { + "name": "claude-mid-conversation-system", + "pattern": "claude-[a-z]+-(?:4[-._](?:[89]|[1-9]\\d)(?!\\d)|(?:[5-9]|[1-9]\\d)(?!\\d)(?:[-._]\\d{1,2}(?!\\d))?)", + "description": "Claude at version 4.8 or higher, in any id shape that contains claude--: minors 4.8 through 4.99, any later major-minor, and bare 5+ majors so a new family like claude-fable-5 matches. Anthropic introduced mid-conversation system messages with Opus 4.8 and every newer Claude keeps them; 4.7 and below reject the system role inside messages.", + "model_info": { + "supports_mid_conversation_system": true + } + } + ] + } } diff --git a/litellm/models/end_user.py b/litellm/models/end_user.py index 15fd03ec2ca..9bf895b9447 100644 --- a/litellm/models/end_user.py +++ b/litellm/models/end_user.py @@ -21,6 +21,7 @@ class LiteLLM_EndUserTable(LiteLLMPydanticObjectBase): spend: float = 0.0 allowed_model_region: Optional[Literal["eu", "us"]] = None default_model: Optional[str] = None + budget_id: Optional[str] = None litellm_budget_table: Optional[LiteLLM_BudgetTable] = None object_permission_id: Optional[str] = None object_permission: Optional[LiteLLM_ObjectPermissionTable] = None diff --git a/litellm/models/managed_files.py b/litellm/models/managed_files.py index 24154768860..99ba764dd98 100644 --- a/litellm/models/managed_files.py +++ b/litellm/models/managed_files.py @@ -51,12 +51,12 @@ class LiteLLM_ManagedVectorStoreTable(LiteLLMPydanticObjectBase): class LiteLLM_ManagedVectorStoresTable(LiteLLMPydanticObjectBase): vector_store_id: str custom_llm_provider: str - vector_store_name: Optional[str] - vector_store_description: Optional[str] - vector_store_metadata: Optional[Dict[str, Any]] - created_at: Optional[datetime] - updated_at: Optional[datetime] - litellm_credential_name: Optional[str] - litellm_params: Optional[Dict[str, Any]] - team_id: Optional[str] - user_id: Optional[str] + vector_store_name: Optional[str] = None + vector_store_description: Optional[str] = None + vector_store_metadata: Optional[Dict[str, Any]] = None + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None + litellm_credential_name: Optional[str] = None + litellm_params: Optional[Dict[str, Any]] = None + team_id: Optional[str] = None + user_id: Optional[str] = None diff --git a/litellm/models/mcp_server.py b/litellm/models/mcp_server.py index 3d03eff6df8..af2efa822b0 100644 --- a/litellm/models/mcp_server.py +++ b/litellm/models/mcp_server.py @@ -83,16 +83,27 @@ class LiteLLM_MCPServerTable(LiteLLMPydanticObjectBase): token_url: Optional[str] = None registration_url: Optional[str] = None oauth2_flow: Optional[Literal["client_credentials", "authorization_code"]] = None + # Token Exchange (OBO) fields — RFC 8693. ``audience`` is named for the RFC's + # request parameter (token-exchange only); RFC 8707 resource indicators are a + # separate concept named ``resource`` in the v2 egress types. A null + # ``subject_token_type`` means DEFAULT_SUBJECT_TOKEN_TYPE (litellm.types.mcp), + # applied at the egress build sites. + token_exchange_endpoint: Optional[str] = None + audience: Optional[str] = None + subject_token_type: Optional[str] = None + token_exchange_profile: Optional[str] = None allow_all_keys: bool = False available_on_public_internet: bool = True delegate_auth_to_upstream: bool = False oauth_passthrough: bool = False + dcr_bridge: Optional[bool] = None is_byok: bool = False byok_description: List[str] = Field(default_factory=list) byok_api_key_help_url: Optional[str] = None has_user_credential: Optional[bool] = None source_url: Optional[str] = None timeout: Optional[float] = None + max_concurrent_requests: Optional[int] = None approval_status: Optional[str] = Field( default="active", description="Approval status: 'pending_review', 'active', 'rejected'", diff --git a/litellm/models/object_permission.py b/litellm/models/object_permission.py index 6c0d100046c..3052a2af459 100644 --- a/litellm/models/object_permission.py +++ b/litellm/models/object_permission.py @@ -24,3 +24,4 @@ class LiteLLM_ObjectPermissionTable(LiteLLMPydanticObjectBase): mcp_toolsets: Optional[List[str]] = None blocked_tools: Optional[List[str]] = [] search_tools: Optional[List[str]] = [] + mcp_tool_search_enabled: Optional[bool] = None diff --git a/litellm/models/verification_token.py b/litellm/models/verification_token.py index 8bddd1c1619..d67726be584 100644 --- a/litellm/models/verification_token.py +++ b/litellm/models/verification_token.py @@ -39,6 +39,7 @@ class LiteLLM_VerificationToken(LiteLLMPydanticObjectBase): permissions: Dict = {} model_spend: Dict = {} model_max_budget: Dict = {} + budget_fallbacks: dict[str, list[str]] = {} soft_budget_cooldown: bool = False blocked: Optional[bool] = None litellm_budget_table: Optional[dict] = None diff --git a/litellm/ocr/main.py b/litellm/ocr/main.py index 5716155361d..38f3f804e10 100644 --- a/litellm/ocr/main.py +++ b/litellm/ocr/main.py @@ -17,6 +17,9 @@ import litellm from litellm._logging import verbose_logger from litellm.constants import request_timeout from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.llms.azure_ai.ocr.common_utils import ( + is_azure_document_intelligence_model, +) from litellm.llms.base_llm.ocr.transformation import BaseOCRConfig, OCRResponse from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler from litellm.rust_bridge import ocr as rust_ocr_bridge @@ -83,6 +86,8 @@ def _prepare_ocr_request( if doc_type not in ["document_url", "image_url"]: raise ValueError(f"Invalid document type: {doc_type}. Must be 'document_url', 'image_url', or 'file'") + caller_supplied_api_base = api_base is not None + ( model, custom_llm_provider, @@ -95,9 +100,14 @@ def _prepare_ocr_request( api_key=api_key, ) + suppress_dynamic_api_base = ( + not caller_supplied_api_base + and custom_llm_provider == "azure_ai" + and is_azure_document_intelligence_model(model) + ) if dynamic_api_key: api_key = dynamic_api_key - if dynamic_api_base: + if dynamic_api_base and not suppress_dynamic_api_base: api_base = dynamic_api_base ocr_provider_config = ProviderConfigManager.get_provider_ocr_config( @@ -191,8 +201,7 @@ def _rust_bridge_api_base( if prepared_request.api_base is not None: return prepared_request.api_base if prepared_request.custom_llm_provider == "azure_ai": - model = prepared_request.model.lower() - if "doc-intelligence" in model or "documentintelligence" in model: + if is_azure_document_intelligence_model(prepared_request.model): return resolve_secret("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT") return resolve_secret("AZURE_AI_API_BASE") return None diff --git a/litellm/passthrough/main.py b/litellm/passthrough/main.py index 66367513062..cdeedd7b522 100644 --- a/litellm/passthrough/main.py +++ b/litellm/passthrough/main.py @@ -171,7 +171,6 @@ def llm_passthrough_route( api_key: Optional[str] = None, request_query_params: Optional[dict] = None, request_headers: Optional[dict] = None, - allm_passthrough_route: bool = False, content: Optional[Any] = None, data: Optional[dict] = None, files: Optional[RequestFiles] = None, @@ -198,7 +197,7 @@ def llm_passthrough_route( from litellm.types.utils import LlmProviders from litellm.utils import ProviderConfigManager - _is_async = allm_passthrough_route + _is_async = bool(kwargs.get("allm_passthrough_route", False)) litellm_logging_obj = cast("LiteLLMLoggingObj", kwargs.get("litellm_logging_obj")) diff --git a/litellm/proxy/_experimental/mcp_server/AGENTS.md b/litellm/proxy/_experimental/mcp_server/AGENTS.md index 8eebc3ea3b3..6e1d121c3be 100644 --- a/litellm/proxy/_experimental/mcp_server/AGENTS.md +++ b/litellm/proxy/_experimental/mcp_server/AGENTS.md @@ -41,6 +41,7 @@ litellm/proxy/_experimental/mcp_server/ sampling_handler.py # MCP sampling to LiteLLM completion flow elicitation_handler.py # MCP elicitation relay flow semantic_tool_filter.py # semantic filtering of available MCP tools + tool_search.py # opt-in virtual tools (mcp_tool_search + mcp_tool_call) for large catalogs guardrail_translation/ handler.py # MCP guardrail result translation sse_transport.py # SSE transport implementation @@ -79,6 +80,11 @@ module materially harder to understand. encryption need focused tests for both allowed and rejected paths. - Avoid adding comments to new code unless they explain non-obvious security or protocol behavior. Prefer clear names and small functions. +- The virtual tool path (`tool_search.py`, gated by `mcp_tool_search_enabled`) + must mirror the normal tool flow: IP filtering, server allowlist, per-key tool + permissions, no-accessible-server rejection, per-request auth headers, server + scope, error to `isError` conversion, and spend logging. Reuse `_list_mcp_tools` + and `execute_mcp_tool` rather than reimplementing any of these checks. ## Tests diff --git a/litellm/proxy/_experimental/mcp_server/auth/token_exchange.py b/litellm/proxy/_experimental/mcp_server/auth/token_exchange.py index 80e72fa2bf2..cd41dd648ee 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/token_exchange.py +++ b/litellm/proxy/_experimental/mcp_server/auth/token_exchange.py @@ -28,6 +28,7 @@ from litellm.proxy._experimental.mcp_server.auth.token_endpoint_auth import ( build_token_endpoint_client_auth, ) from litellm.types.llms.custom_http import httpxSpecialProvider +from litellm.types.mcp import DEFAULT_SUBJECT_TOKEN_TYPE if TYPE_CHECKING: from litellm.types.mcp_server.mcp_server_manager import MCPServer @@ -35,8 +36,6 @@ if TYPE_CHECKING: # RFC 8693 grant type constant TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange" -DEFAULT_SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token" - class TokenExchangeHandler: """Handles OAuth 2.0 Token Exchange (RFC 8693) for MCP servers. diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index 2520c7e82a1..e300a22e5db 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -1,26 +1,44 @@ import re +from datetime import datetime, timezone from typing import Dict, List, Optional, Set, Tuple, cast from fastapi import HTTPException from starlette.datastructures import Headers from starlette.requests import Request from starlette.types import Scope +from typing_extensions import assert_never +import litellm from litellm._logging import verbose_logger +from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( + BridgeEnvelopeAdmitted, + BridgeEnvelopeInvalid, + NotBridgeEnvelope, + envelope_keys_from_master_key, + is_bridge_envelope_shaped, + resolve_bridge_envelope, +) from litellm.proxy._types import ( + UI_TEAM_ID, LiteLLM_TeamTable, ProxyException, SpecialHeaders, + SpecialMCPServerName, SpecialMCPServerNames, UserAPIKeyAuth, ) from litellm.proxy.auth.ip_address_utils import IPAddressUtils -from litellm.proxy.auth.user_api_key_auth import user_api_key_auth +from litellm.proxy.auth.user_api_key_auth import ( + _run_centralized_common_checks, + user_api_key_auth, +) +from litellm.proxy.common_utils.http_parsing_utils import _read_request_body from litellm.proxy.common_utils.user_api_key_cache import get_management_object_ttl from litellm.repositories.table_repositories import ( AgentsRepository, MCPServerRepository, ) +from litellm.types.mcp_server.mcp_server_manager import MCPServer def _parse_mcp_server_names_from_path(path: str, mcp_servers_header: Optional[List[str]] = None) -> Optional[List[str]]: @@ -218,6 +236,35 @@ class MCPRequestHandler: # when EVERY target is auth_type=oauth2 with delegate_auth_to_upstream # set; fails closed otherwise. validated_user_api_key_auth = UserAPIKeyAuth() + elif MCPRequestHandler._target_servers_are_true_passthrough( + path=request_route, + mcp_servers=mcp_servers, + client_ip=IPAddressUtils.get_mcp_client_ip(request), + ): + validated_user_api_key_auth = UserAPIKeyAuth() + elif ( + ( + bridge_delegate_target := MCPRequestHandler._single_dcr_bridge_delegate_target( + path=request_route, + mcp_servers=mcp_servers, + client_ip=IPAddressUtils.get_mcp_client_ip(request), + ) + ) + is not None + and oauth2_headers + and is_bridge_envelope_shaped(oauth2_headers["Authorization"]) + ): + # A single DCR-bridge oauth_delegate target carrying an envelope-shaped + # Authorization: open the envelope, admit under its recovered identity, and + # inject the inner upstream token for egress. A non-envelope bearer on the same + # server is NOT admitted here — it falls through to the oauth2 arm, which 401s. + validated_user_api_key_auth, mcp_server_auth_headers = await MCPRequestHandler._admit_dcr_bridge_delegate( + server=bridge_delegate_target, + authorization_value=oauth2_headers["Authorization"], + mcp_server_auth_headers=mcp_server_auth_headers, + request=request, + route=request_route, + ) elif oauth2_headers: # Authorization on a non-delegated server: the bearer must be a real # LiteLLM credential, so a failed validation is a genuine 401/403 and @@ -356,6 +403,7 @@ class MCPRequestHandler: # Inline imports avoid a circular dependency: mcp_server_manager imports # from this module. from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + MCPServerManager, global_mcp_server_manager, ) from litellm.types.mcp import MCPAuth @@ -381,10 +429,289 @@ class MCPRequestHandler: # fetches the upstream token automatically using stored credentials, # so allowing anonymous bypass would let any external caller invoke # tools authenticated as LiteLLM's service account. - if server.has_client_credentials: + # + # Resolve the flow rather than reading has_client_credentials directly: + # this is a security gate, and a legacy row whose oauth2_flow was never + # stamped still carries the M2M credential shape (client_id/secret + + # token_url, no authorization_url). Treating an unstamped-but-M2M-shaped + # row as non-M2M here would reopen the anonymous bypass the explicit + # column no longer closes on its own. Shares the one resolution helper + # with the egress backstop and the anonymous-delegate allowlist; all fail + # closed on the ambiguous shape and are removed together once no null rows + # remain. A pure-PKCE delegate server (no stored credentials) resolves to a + # non-M2M flow and keeps its bypass. + if MCPServerManager.effective_oauth2_flow(server) == "client_credentials": return False return True + @staticmethod + def _target_servers_are_true_passthrough( + path: str, mcp_servers: Optional[list[str]], client_ip: Optional[str] + ) -> bool: + """ + True only when EVERY MCP server the request targets is ``auth_type == true_passthrough``. + Fails closed when any target does not opt in or cannot be resolved. + + Used by :meth:`process_mcp_request` to skip LiteLLM admission auth entirely: the gateway is a + transparent proxy and the caller's ``Authorization`` is an upstream token, never a LiteLLM key. + Mirrors :meth:`_target_servers_delegate_auth_to_upstream`; a mixed-target request keeps normal auth. + """ + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.types.mcp import MCPAuth + + target_names = MCPRequestHandler._resolve_target_server_names(path=path, mcp_servers_header=mcp_servers) + if not target_names: + return False + + for name in target_names: + server = global_mcp_server_manager.get_mcp_server_by_name(name, client_ip=client_ip) + if server is None or server.auth_type != MCPAuth.true_passthrough: + return False + return True + + @staticmethod + def _single_dcr_bridge_delegate_target( + path: str, mcp_servers: Optional[List[str]], client_ip: Optional[str] + ) -> Optional[MCPServer]: + """The one DCR-bridge ``oauth_delegate`` server this request targets, or ``None``. + + Returns the server only when EXACTLY ONE target resolves and it is both + ``is_oauth_delegate`` and ``is_dcr_bridge``. Fails closed (``None``) on a + multi-target request, an unresolved target, or a non-matching server, so the + envelope admission arm never fires for an aggregate scope or a server that did not + opt into the bridge. Mirrors :meth:`_target_servers_are_true_passthrough`. + """ + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + target_names = MCPRequestHandler._resolve_target_server_names(path=path, mcp_servers_header=mcp_servers) + if len(target_names) != 1: + return None + server = global_mcp_server_manager.get_mcp_server_by_name(target_names[0], client_ip=client_ip) + if server is None or not server.is_oauth_delegate or not server.is_dcr_bridge: + return None + # Egress resolves the injected per-server token only by alias / server_name; a server with + # neither cannot receive the forwarded token, so fail closed rather than admit-and-drop. + if not (server.server_name or server.alias): + return None + return server + + @staticmethod + async def _admit_dcr_bridge_delegate( + server: MCPServer, + authorization_value: str, + mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]], + request: Request, + route: str, + ) -> Tuple[UserAPIKeyAuth, Optional[Dict[str, Dict[str, str]]]]: + """Open the bridge envelope and admit the caller under the live key it references. + + The envelope's signature proves the user authenticated when it was minted, but + authorization is resolved fresh here rather than trusted from the envelope: the + sealed ``key_hash`` reloads the current ``UserAPIKeyAuth`` record, and the admitted + identity then runs through the standard pipeline's centralized policy gate, so the + key's present restrictions and revocation state gate the request instead of a + snapshot frozen at mint time. The inner upstream token is injected under the + server's per-server auth-header key so egress forwards it via the + ``PassthroughConfig`` override; the envelope ``Authorization`` the leak-defense + strips never reaches the upstream. A new headers dict is returned rather than + mutating the input. Fails closed with a 401 on an invalid or expired envelope, or + when the referenced key is missing, blocked, or expired, its owner is + SCIM-deactivated, or the centralized policy gate rejects it (blocked team or + project, org or budget limits). + + The sealed token is keyed alias-first, matching the order egress resolves + (``lookup_mcp_server_auth_in_headers`` tries ``alias`` before ``server_name``). Keying + under ``server_name`` would leave a caller-supplied ``x-mcp-{alias}-authorization`` at the + higher-priority alias slot, pairing the admitted identity with an attacker's upstream + credential; the alias-keyed injection overwrites any such caller value. + """ + from litellm.proxy.proxy_server import master_key + + if not master_key: + raise HTTPException(status_code=500, detail="Server misconfigured: master_key is not set") + + await MCPRequestHandler._run_pre_db_read_auth_checks(request=request, route=route) + + keys = envelope_keys_from_master_key(master_key) + result = resolve_bridge_envelope(authorization_value, keys, datetime.now(timezone.utc), server.server_id) + match result: + case BridgeEnvelopeAdmitted(): + header_key = server.alias or server.server_name + if header_key is None: + raise HTTPException(status_code=500, detail="Server misconfigured: MCP server has no routable name") + admitted = await MCPRequestHandler._reload_admitted_key(result.identity.key_hash) + await MCPRequestHandler._enforce_admitted_live_policy(admitted=admitted, request=request, route=route) + injected = {header_key: {"Authorization": result.upstream_authorization.get_secret_value()}} + new_headers = {**(mcp_server_auth_headers or {}), **injected} + return admitted, new_headers + case BridgeEnvelopeInvalid() | NotBridgeEnvelope(): + raise HTTPException(status_code=401, detail="Invalid or expired credential") + case _: + assert_never(result) + + @staticmethod + async def _run_pre_db_read_auth_checks(request: Request, route: str) -> None: + """Run the proxy-wide gates ``user_api_key_auth`` applies before any key lookup: the + request-size and body-safety limits, the IP allowlist, and the ``general_settings`` + route allowlist. The envelope arm bypasses ``user_api_key_auth`` (it opens the envelope + and reloads the identity itself), so without this a caller blocked by IP or hitting a + proxy route the allowlist forbids would be admitted through an envelope where the same + principal presented on the normal MCP admission path would be rejected. Runs before the + envelope crypto so a disallowed caller is turned away before any work, mirroring the + standard pipeline's pre-DB ordering. Violations raise the gate's own status (an IP or + route block is a 403, an oversized body its own limit error).""" + from litellm.proxy.auth.auth_utils import pre_db_read_auth_checks + + await pre_db_read_auth_checks( + request=request, + request_data=await _read_request_body(request=request), + route=route, + ) + + @staticmethod + async def _reload_admitted_key(key_hash: str) -> UserAPIKeyAuth: + """Reload the live key record an admitted envelope references and re-check live policy. + + Resolving the current ``UserAPIKeyAuth`` (cache first, then DB) is what stops the + envelope from carrying frozen authority: the key's present team/org/object-permission + restrictions ride on the returned object, and a key that has since been deleted, + blocked, or expired fails closed with a 401 here rather than being admitted as an + unrestricted identity. ``get_key_object`` raises for a hash with no key row; a + blocked or expired row is rejected explicitly because ``get_key_object`` resolves a + row without applying those checks (the main ``user_api_key_auth`` pipeline enforces + them downstream, which this admission path bypasses). The owner's SCIM state is the + other builder-inline check mirrored here, so IdP offboarding revokes every envelope + minted under the user's keys rather than leaving them live until expiry. Team, + project, org, and budget state are NOT re-checked here; the caller runs the admitted + identity through ``_enforce_admitted_live_policy`` for those. + """ + from litellm.proxy.auth.auth_checks import get_key_object + from litellm.proxy.proxy_server import prisma_client, user_api_key_cache + + if prisma_client is None: + raise HTTPException(status_code=500, detail="Server misconfigured: no database connection") + try: + key_object = await get_key_object( + hashed_token=key_hash, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + ) + except (ProxyException, HTTPException): + raise HTTPException(status_code=401, detail="Invalid or expired credential") from None + except Exception as e: # noqa: BLE001 # a DB outage during reload is a retryable 503, not an opaque 500 + MCPRequestHandler._raise_503_if_db_unavailable(e) + raise + if not MCPRequestHandler._admitted_key_is_active(key_object): + raise HTTPException(status_code=401, detail="Invalid or expired credential") + await MCPRequestHandler._reject_if_admitted_owner_scim_deactivated(key_object) + return key_object + + @staticmethod + def _raise_503_if_db_unavailable(e: Exception) -> None: + """Raise a retryable 503 when ``e`` means the auth database is unreachable, else return so the + caller applies its own fail-closed mapping. A DB outage must not masquerade as an auth failure + (401) or surface as an opaque 500; the caller retries. Mirrors ``UserAPIKeyAuthExceptionHandler``, + which renders a service-unavailable database error as 503 on the standard pipeline.""" + from litellm.proxy.db.exception_handler import PrismaDBExceptionHandler + + if PrismaDBExceptionHandler.is_database_service_unavailable_error(e): + raise HTTPException( + status_code=503, + detail="Service Unavailable, the authentication database is temporarily unreachable. Please retry shortly.", + ) from None + + @staticmethod + async def _reject_if_admitted_owner_scim_deactivated(key_object: UserAPIKeyAuth) -> None: + """Fail closed with a 401 when the key's owning user was deactivated via SCIM. + + The standard pipeline enforces this inline in ``_user_api_key_auth_builder`` rather + than in ``common_checks``, so the centralized policy gate does not cover it; without + this mirror, IdP offboarding would leave the user's already-minted envelopes live + until expiry. A failed user lookup skips the gate (fail-open), matching the builder: + this is the one deliberately fail-open check in an otherwise fail-closed arm, so a + transient DB outage during this lookup admits the request rather than rejecting it, + keeping parity with how the standard pipeline treats the same lookup failure.""" + if key_object.user_id is None: + return + from litellm.proxy.auth.auth_checks import get_user_object + from litellm.proxy.proxy_server import prisma_client, user_api_key_cache + + try: + user_object = await get_user_object( + user_id=key_object.user_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + user_id_upsert=False, + ) + except Exception as e: # noqa: BLE001 # mirror the builder's fail-open user lookup; DB errors are of any type + verbose_logger.debug(f"bridge admission: user lookup failed, skipping SCIM gate: {e}") + user_object = None + if user_object is None or not isinstance(user_object.metadata, dict): + return + if user_object.metadata.get("scim_active") is False: + raise HTTPException(status_code=401, detail="Invalid or expired credential") + + @staticmethod + async def _enforce_admitted_live_policy(admitted: UserAPIKeyAuth, request: Request, route: str) -> None: + """Run the standard pipeline's authorization checks over the admitted identity. + + Mirrors the ``user_api_key_auth`` wrapper between the builder and its return: clear the + request-scoped ``budget_reservation`` on the reloaded identity, run the route gate + (``RouteChecks.should_call_route``) to enforce the identity's ``allowed_routes`` and any + disabled/admin-only route, then run ``_run_centralized_common_checks`` (the same gate every + builder path funnels through) for team-block, project-block, org, and budget. The route gate + closes a bypass: a key barred from MCP routes could otherwise mint an envelope at the token + endpoint (not itself an MCP route) and replay it against MCP, because the centralized checks + treat MCP as an inference route and never re-check ``allowed_routes``. + + Failures surface with the status the standard pipeline would give them, mirroring + ``UserAPIKeyAuthExceptionHandler``: a disallowed route is the route gate's own 403, an + over-budget identity is a 429, a sub-check that raised its own ``HTTPException``/ + ``ProxyException`` keeps that status, a transient database outage is a retryable 503, and + only a genuinely unresolvable failure (a blocked team/project raises a bare ``Exception``, + same as the standard pipeline's fallback) becomes the fail-closed 401. Collapsing every + failure to 401 was misleading: it told an over-budget but validly-authenticated caller their + credential was invalid, which on a DCR client reads as broken auth and can trigger a + pointless re-authorize loop that cannot fix a budget problem, and it masked a DB outage as an + auth error.""" + from litellm.proxy.auth.route_checks import RouteChecks + + admitted.budget_reservation = None + try: + RouteChecks.should_call_route(route=route, valid_token=admitted, request=request) + await _run_centralized_common_checks( + user_api_key_auth_obj=admitted, + request=request, + request_data=await _read_request_body(request=request), + route=route, + ) + except (HTTPException, ProxyException): + raise + except litellm.BudgetExceededError as e: + raise HTTPException(status_code=getattr(e, "status_code", 429), detail=str(e)) from None + except Exception as e: # noqa: BLE001 # untyped gate failure: retryable 503 for a DB outage, else fail closed 401 + MCPRequestHandler._raise_503_if_db_unavailable(e) + raise HTTPException(status_code=401, detail="Invalid or expired credential") from None + + @staticmethod + def _admitted_key_is_active(key_object: UserAPIKeyAuth) -> bool: + """False when the referenced key is blocked or past its expiry, so a revoked key + cannot be admitted through its still-unexpired envelope. Mirrors the active-key gate + the bridge token endpoint applies at mint time.""" + if key_object.blocked is True: + return False + expires = key_object.expires + if expires is None: + return True + expiry = expires if isinstance(expires, datetime) else datetime.fromisoformat(expires) + if expiry.tzinfo is None or expiry.tzinfo.utcoffset(expiry) is None: + expiry = expiry.replace(tzinfo=timezone.utc) + return expiry >= datetime.now(timezone.utc) + @staticmethod def _resolve_target_server_names(path: str, mcp_servers_header: Optional[List[str]]) -> List[str]: """ @@ -544,10 +871,19 @@ class MCPRequestHandler: ASGI headers are in format: List[List[bytes, bytes]] We need to convert them to the format Headers expects. + + Collapsing the ASGI list into a dict keeps the last value for a duplicated + header name, so a request carrying more than one ``Authorization`` is + rejected first: for the client-forwarded token modes the gateway relays the + caller's ``Authorization`` upstream, so a duplicate would make which token is + forwarded ambiguous (and diverge from what admission inspected). Multiple + ``Authorization`` headers is malformed for bearer auth anyway (RFC 9110: not + a comma-combinable field), so fail closed with a 400. """ + raw_headers = scope.get("headers", []) + MCPRequestHandler._reject_duplicate_authorization(raw_headers) try: # ASGI headers are list of [name: bytes, value: bytes] pairs - raw_headers = scope.get("headers", []) # Convert bytes to strings and create dict for Headers constructor headers_dict = {name.decode("latin-1"): value.decode("latin-1") for name, value in raw_headers} return Headers(headers_dict) @@ -556,6 +892,26 @@ class MCPRequestHandler: # Return empty Headers object with empty dict return Headers({}) + @staticmethod + def _reject_duplicate_authorization(raw_headers: object) -> None: + """Raise 400 when the raw ASGI headers carry more than one ``Authorization`` header.""" + if not isinstance(raw_headers, (list, tuple)): + return + count = 0 + for entry in raw_headers: + if not isinstance(entry, (list, tuple)) or len(entry) < 1: + continue + name = entry[0] + if isinstance(name, (bytes, bytearray)) and bytes(name).lower() == b"authorization": + count += 1 + elif isinstance(name, str) and name.lower() == "authorization": + count += 1 + if count > 1: + raise HTTPException( + status_code=400, + detail="Multiple Authorization headers are not allowed", + ) + @staticmethod async def get_allowed_mcp_servers( user_api_key_auth: Optional[UserAPIKeyAuth] = None, @@ -725,6 +1081,9 @@ class MCPRequestHandler: if not user_api_key_auth or not user_api_key_auth.team_id or not prisma_client: return None + if user_api_key_auth.team_id == UI_TEAM_ID: + return None + # Get the team object (which has object_permission already loaded) team_obj: Optional[LiteLLM_TeamTable] = await get_team_object( team_id=user_api_key_auth.team_id, @@ -1020,6 +1379,9 @@ class MCPRequestHandler: if user_api_key_auth is None or not user_api_key_auth.team_id or prisma_client is None: return [] + if user_api_key_auth.team_id == UI_TEAM_ID: + return [] + team_obj: Optional[LiteLLM_TeamTable] = await get_team_object( team_id=user_api_key_auth.team_id, prisma_client=prisma_client, @@ -1041,6 +1403,9 @@ class MCPRequestHandler: if object_permissions is None: return list(set(team_access_group_servers)) + if SpecialMCPServerName.all_proxy_servers.value in (object_permissions.mcp_servers or []): + return list(global_mcp_server_manager.get_registry().keys()) + direct_mcp_servers = global_mcp_server_manager.expand_permission_list(object_permissions.mcp_servers or []) legacy_access_group_servers = await MCPRequestHandler._get_mcp_servers_from_access_groups( @@ -1499,6 +1864,9 @@ class MCPRequestHandler: verbose_logger.debug("prisma_client is None") return [] + if user_api_key_auth.team_id == UI_TEAM_ID: + return [] + try: team_obj: Optional[LiteLLM_TeamTable] = await get_team_object( team_id=user_api_key_auth.team_id, diff --git a/litellm/proxy/_experimental/mcp_server/db.py b/litellm/proxy/_experimental/mcp_server/db.py index a2ce3307061..97cefb3f2cb 100644 --- a/litellm/proxy/_experimental/mcp_server/db.py +++ b/litellm/proxy/_experimental/mcp_server/db.py @@ -3,7 +3,7 @@ import binascii import hashlib import json from datetime import datetime, timedelta, timezone -from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Set, Union, cast +from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Iterable, List, Optional, Set, Union, cast from litellm._logging import verbose_proxy_logger from litellm._uuid import uuid @@ -46,6 +46,64 @@ from litellm.types.mcp import MCPCredentials if TYPE_CHECKING: from litellm.types.mcp_server.mcp_server_manager import MCPServer +_AUTH_FLOW_SCOPED_FIELDS: frozenset = frozenset( + { + "authorization_url", + "token_url", + "registration_url", + "oauth2_flow", + "dcr_bridge", + "token_exchange_endpoint", + "audience", + "subject_token_type", + "token_exchange_profile", + } +) + +# Token-exchange settings with dedicated columns that also exist on +# ``MCPCredentials`` as a legacy shape (rows and REST callers that predate the +# columns). Every write lifts blob values into the columns and strips them from +# the stored blob, so the read-time ``column or blob`` fallback only serves rows +# the current code has never written — a cleared column can then never be +# silently resurrected by a stale blob copy. These keys are stored plaintext +# (endpoints/identifiers, not secrets), so values lift as-is. +_TOKEN_EXCHANGE_COLUMN_FIELDS: frozenset = frozenset( + { + "token_exchange_endpoint", + "audience", + "subject_token_type", + "token_exchange_profile", + } +) + +# The client-forwarded token modes share one stored-credential shape: the admin-declared upstream +# OAuth app (client_id/client_secret) plus the same authorize relay, and neither mints anything the +# gateway keeps. So a switch WITHIN this class must preserve the stored app, unlike a cross-class +# switch (e.g. an oauth2 row whose client may be DCR-minted and is not reusable elsewhere). +_CLIENT_FORWARDED_AUTH_TYPES: frozenset = frozenset({"true_passthrough", "oauth_delegate"}) + +# Minted token material that must never survive a client rotation on a persisted row. +_MINTED_TOKEN_CREDENTIAL_FIELDS: frozenset = frozenset({"access_token", "refresh_token", "expires_in"}) + + +def _credential_auth_class(auth_type: Optional[str]) -> Optional[str]: + """Collapse the client-forwarded modes to one credential class; every other auth_type is its own + class. Used so credential handling keys off whether the stored-credential shape actually changed, + not off a raw auth_type inequality that treats true_passthrough<->oauth_delegate as a full reset.""" + if auth_type in _CLIENT_FORWARDED_AUTH_TYPES: + return "client_forwarded" + return auth_type + + +def _drop_stale_minted_on_client_rotation(merged: Dict[str, Any], new_creds: Dict[str, Any]) -> Dict[str, Any]: + """When the update rotates the client, drop stale minted token keys it did not itself set, so an old + app's access/refresh token never rides forward under the new client. A no-op when no client key changed.""" + if "client_id" not in new_creds and "client_secret" not in new_creds: + return merged + return { + key: value for key, value in merged.items() if key not in _MINTED_TOKEN_CREDENTIAL_FIELDS or key in new_creds + } + def _is_global_env_var_scope(scope: Any) -> bool: """``scope="user"`` entries are placeholders the user fills in; everything @@ -241,6 +299,14 @@ def _prepare_mcp_server_data( # Handle credentials serialization credentials = data_dict.get("credentials") if credentials is not None: + # Lift legacy blob-shaped token-exchange settings into their dedicated + # columns (an explicit top-level value wins, including an explicit + # null) and strip them from the blob so it never seeds the read-time + # fallback for rows written by current code. + for te_field in _TOKEN_EXCHANGE_COLUMN_FIELDS: + blob_value = credentials.pop(te_field, None) + if blob_value is not None and te_field not in data_dict: + data_dict[te_field] = blob_value data_dict["credentials"] = encrypt_credentials(credentials=credentials, encryption_key=_get_salt_key()) data_dict["credentials"] = safe_dumps(data_dict["credentials"]) @@ -521,7 +587,11 @@ async def delete_mcp_server_from_virtualkey(): pass -async def delete_mcp_server(prisma_client: PrismaClient, server_id: str) -> Optional[LiteLLM_MCPServerTable]: +async def delete_mcp_server( + prisma_client: PrismaClient, + server_id: str, + invalidate_token_cache: Optional[Callable[[str, str], Awaitable[None]]] = None, +) -> Optional[LiteLLM_MCPServerTable]: """ Delete the mcp server from the db by server_id @@ -532,6 +602,12 @@ async def delete_mcp_server(prisma_client: PrismaClient, server_id: str) -> Opti caller-visible error. Each table is cleaned independently so a failure on one still attempts the other. + Each enumerated credential row's user also gets their cached per-user token + invalidated (legacy cache + v2 store, via invalidate_token_cache, defaulting + to the manager's shared invalidation): the caches are keyed by + (user_id, server_id), so without this a re-created server reusing the same + server_id would serve tokens minted for the deleted server until TTL. + Returns the deleted mcp server record if it exists, otherwise None """ deleted_server = await MCPServerRepository(prisma_client).table.delete( @@ -540,6 +616,18 @@ async def delete_mcp_server(prisma_client: PrismaClient, server_id: str) -> Opti }, ) if deleted_server is not None: + credential_user_ids: List[str] = [] + try: + credential_rows = await prisma_client.db.litellm_mcpusercredentials.find_many( + where={"server_id": server_id} + ) + credential_user_ids = [row.user_id for row in credential_rows] + except Exception as e: # noqa: BLE001 - enumeration is best-effort; cached tokens expire by TTL + verbose_proxy_logger.warning( + "MCP server %s deleted but per-user credential enumeration failed; cached tokens expire by TTL: %s", + server_id, + e, + ) for model, label in ( (prisma_client.db.litellm_mcpusercredentials, "credential"), (prisma_client.db.litellm_mcpuserenvvars, "env var"), @@ -554,6 +642,15 @@ async def delete_mcp_server(prisma_client: PrismaClient, server_id: str) -> Opti label, e, ) + if credential_user_ids: + if invalidate_token_cache is None: + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + invalidate_token_cache = global_mcp_server_manager.invalidate_user_oauth_token_cache + for user_id in credential_user_ids: + await invalidate_token_cache(user_id, server_id) return deleted_server @@ -603,29 +700,54 @@ async def update_mcp_server( # Pre-fetch existing record once if we need it for auth_type or credential logic existing = None has_credentials = "credentials" in data_dict and data_dict["credentials"] is not None - if data.auth_type or has_credentials: + # An explicit token-exchange column write (set or clear) also migrates the + # legacy blob copies below, so the existing row is needed for those updates. + explicit_te_write = bool(_TOKEN_EXCHANGE_COLUMN_FIELDS & data_dict.keys()) + if data.auth_type or has_credentials or explicit_te_write: existing = await MCPServerRepository(prisma_client).table.find_unique(where={"server_id": data.server_id}) - # Clear stale credentials when auth_type changes but no new credentials provided - if ( + auth_type_changed = bool( data.auth_type - and "credentials" not in data_dict and existing - and existing.auth_type is not None - and existing.auth_type != data.auth_type - ): + and _credential_auth_class(existing.auth_type) != _credential_auth_class(data.auth_type) + ) + + # Clear stale credentials when auth_type changes but no new credentials provided + if auth_type_changed and "credentials" not in data_dict: data_dict["credentials"] = None + if auth_type_changed: + data_dict.update({field: None for field in _AUTH_FLOW_SCOPED_FIELDS if field not in data_dict}) + + # An explicit column write that does not touch credentials must still migrate + # the row's legacy blob copies: lift values for columns the caller left + # untouched, strip every copy from the blob. Without this, clearing a column + # (e.g. to re-enable RFC 9728/8414 discovery) would leave the blob copy in + # place, and the next credentials update's migrate-on-write would silently + # repopulate the column the admin just cleared. (When credentials ARE in the + # update, the merge below performs the same migration.) + if explicit_te_write and "credentials" not in data_dict and existing is not None and existing.credentials: + existing_creds = ( + json.loads(existing.credentials) if isinstance(existing.credentials, str) else dict(existing.credentials) + ) + if _TOKEN_EXCHANGE_COLUMN_FIELDS & existing_creds.keys(): + for te_field in _TOKEN_EXCHANGE_COLUMN_FIELDS: + legacy_value = existing_creds.pop(te_field, None) + if legacy_value is not None and te_field not in data_dict and getattr(existing, te_field, None) is None: + data_dict[te_field] = legacy_value + data_dict["credentials"] = safe_dumps(existing_creds) + # Merge credentials: preserve existing fields not present in the update. # Without this, a partial credential update (e.g. changing only region) # would wipe encrypted secrets that the UI cannot display back. if "credentials" in data_dict and data_dict["credentials"] is not None: if existing and existing.credentials: - # Only merge when auth_type is unchanged. Switching auth types - # (e.g. oauth2 → api_key) should replace credentials entirely - # to avoid stale secrets from the previous auth type lingering. - auth_type_unchanged = data.auth_type is None or data.auth_type == existing.auth_type - if auth_type_unchanged: + # Only merge when the credential CLASS is unchanged. A cross-class switch + # (e.g. oauth2 → api_key, or oauth2 → true_passthrough) replaces credentials + # entirely to avoid stale secrets from the previous class lingering; a switch + # within the client-forwarded class (true_passthrough ↔ oauth_delegate) keeps + # the same declared app and so must merge, not replace. + if not auth_type_changed: existing_creds = ( json.loads(existing.credentials) if isinstance(existing.credentials, str) @@ -636,13 +758,35 @@ async def update_mcp_server( if isinstance(data_dict["credentials"], str) else dict(data_dict["credentials"]) ) - # New values override existing; existing keys not in update are preserved - merged = {**existing_creds, **new_creds} + # New values override existing; existing keys not in update are preserved. A client + # rotation additionally drops the previous app's stale minted token keys. + merged = _drop_stale_minted_on_client_rotation({**existing_creds, **new_creds}, new_creds) + # Migrate-on-write for legacy rows: token-exchange settings the + # old blob shape carried move to their dedicated columns (unless + # the caller set the column this update, or the row already has + # one) and are never re-persisted in the blob. Stored plaintext, + # so the merged value lifts as-is. + for te_field in _TOKEN_EXCHANGE_COLUMN_FIELDS: + legacy_value = merged.pop(te_field, None) + if ( + legacy_value is not None + and te_field not in data_dict + and getattr(existing, te_field, None) is None + ): + data_dict[te_field] = legacy_value data_dict["credentials"] = safe_dumps(merged) # Add audit fields data_dict["updated_by"] = touched_by + # prisma-python rejects a raw ``None`` for a ``Json?`` field ("value is required but not set"); the + # clear paths above use ``None`` as the merge-skip sentinel, so translate it here to ``Json(None)``, + # which writes SQL null and reads back as ``None``. Done at the edge so the merge guards stay simple. + if "credentials" in data_dict and data_dict["credentials"] is None: + from prisma import Json # noqa: PLC0415 # local import: prisma may be ungenerated at module load in some tools + + data_dict["credentials"] = Json(None) + updated_mcp_server = await MCPServerRepository(prisma_client).table.update( where={"server_id": data.server_id}, data=data_dict, # type: ignore @@ -998,6 +1142,103 @@ async def list_user_oauth_credentials( return results +def _decrypted_credential_field(creds: Dict[str, object], field: str) -> object: + """Return one credential field decrypted with the global salt key; non-string and legacy + plaintext values come back unchanged (decrypt_value_helper returns the original on failure).""" + value = creds.get(field) + if not isinstance(value, str): + return value + return decrypt_value_helper( + value=value, + key=field, + exception_type="debug", + return_original_value=True, + ) + + +def mcp_oauth_token_identity(server: object) -> tuple[object, ...]: + """The upstream-OAuth-token-determining fields of an MCP server: the resource/audience (url, or + spec_path for OpenAPI servers), the OAuth mode/grant (auth_type, oauth2_flow), the + authorization-server endpoints, and the OAuth client + scopes. Mirrors the dashboard's + getOAuthAuthorizationIdentity. When any of these change on a server update, previously stored + per-user tokens were minted for the old identity and are stale. Excludes transport and + delegate_auth_to_upstream, which do not affect what token is minted (RFC 8707/8693). + + client_id/client_secret are compared decrypted: stored values are NaCl-encrypted with a fresh + nonce on every write, so comparing ciphertext would flag every routine save as an identity + change and purge tokens that are still valid.""" + creds = getattr(server, "credentials", None) + if isinstance(creds, str): + try: + parsed: object = json.loads(creds) + except ValueError: + parsed = None + else: + parsed = creds + creds_dict: Dict[str, object] = parsed if isinstance(parsed, dict) else {} + return ( + getattr(server, "url", None), + getattr(server, "spec_path", None), + getattr(server, "auth_type", None), + getattr(server, "oauth2_flow", None), + getattr(server, "authorization_url", None), + getattr(server, "token_url", None), + getattr(server, "registration_url", None), + _decrypted_credential_field(creds_dict, "client_id"), + _decrypted_credential_field(creds_dict, "client_secret"), + creds_dict.get("scopes"), + ) + + +async def purge_user_oauth_credentials_for_server( + prisma_client: PrismaClient, + server_id: str, + invalidate_token_cache: Optional[Callable[[str, str], Awaitable[None]]] = None, +) -> int: + """Delete every stored per-user OAuth token for a server and invalidate each user's cached + token everywhere it can be served from (the legacy per-user token cache and the v2 per-user OAuth + token store), so no user keeps a token minted for a superseded configuration. Called when a server + update changes a mint-relevant field (see mcp_oauth_token_identity). Returns the number of rows + removed. + + LiteLLM_MCPUserCredentials also stores BYOK API keys in the same column; only rows whose payload + decodes as an OAuth2 credential (see _decode_oauth_payload) are deleted, because a config change + only invalidates minted tokens, never a user's own stored key. Rows are therefore deleted per + (user_id, server_id) pair rather than by a blanket server_id filter. An OAuth row inserted while + the purge runs for a user not yet enumerated survives; a re-auth completing in the window for an + already-enumerated user is deleted along with the stale row (the pair delete cannot tell them + apart), which costs that user one extra re-auth and nothing else. + + invalidate_token_cache is injectable for tests; it defaults to the manager's shared + invalidate_user_oauth_token_cache, the single invalidation point for per-user tokens.""" + repo = MCPUserCredentialsRepository(prisma_client) + rows = await repo.table.find_many(where={"server_id": server_id}) + oauth_rows = [row for row in rows if _decode_oauth_payload(row.credential_b64) is not None] + if not oauth_rows: + return 0 + deleted_count = await repo.table.delete_many( + where={"server_id": server_id, "user_id": {"in": [row.user_id for row in oauth_rows]}} + ) + if invalidate_token_cache is None: + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + invalidate_token_cache = global_mcp_server_manager.invalidate_user_oauth_token_cache + + for row in oauth_rows: + await invalidate_token_cache(row.user_id, server_id) + if deleted_count != len(oauth_rows): + verbose_proxy_logger.warning( + "MCP server %s: purge removed %d OAuth credential row(s) but %d were enumerated; " + "row(s) were deleted concurrently during the purge", + server_id, + deleted_count, + len(oauth_rows), + ) + return deleted_count + + async def refresh_user_oauth_token( prisma_client: PrismaClient, user_id: str, @@ -1228,6 +1469,23 @@ def _remaining_token_seconds(expires_at: str | None) -> int | None: return remaining if remaining > 0 else None +async def get_active_submitted_mcp_server_ids_for_user( + prisma_client: PrismaClient, + user_id: str, +) -> list[str]: + """Return active BYOM servers submitted by this user (creator visibility).""" + if not user_id: + return [] + + rows = await MCPServerRepository(prisma_client).table.find_many( + where={ + "submitted_by": user_id, + "approval_status": MCPApprovalStatus.active, + }, + ) + return [row.server_id for row in rows] + + async def approve_mcp_server( prisma_client: PrismaClient, server_id: str, diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 6933aa06b2d..ebceb320906 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -1,14 +1,16 @@ import asyncio import html as _html import json +import secrets import time from datetime import datetime, timezone -from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple +from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Tuple from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse import httpx from fastapi import APIRouter, Form, HTTPException, Request -from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse +from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, Response +from pydantic import BaseModel, ValidationError from litellm._logging import verbose_logger from litellm.llms.custom_httpx.http_handler import ( @@ -31,11 +33,11 @@ from litellm.proxy.common_utils.encrypt_decrypt_utils import ( ) from litellm.proxy.common_utils.http_parsing_utils import _read_request_body from litellm.proxy.utils import get_server_root_path -from litellm.types.mcp import MCPAuth +from litellm.types.mcp import MCPAuth, MCPCredentials from litellm.types.mcp_server.mcp_server_manager import MCPServer if TYPE_CHECKING: - from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy._types import LiteLLM_MCPServerTable, UserAPIKeyAuth # TTL cache for upstream OAuth metadata fetched from pass-through MCP servers. # Keeps us from hammering the upstream IdP on each discovery request. @@ -136,6 +138,72 @@ def decode_state_hash(encrypted_state: str) -> dict: return state_data +# LIT-4197: some upstream authorization servers reject an over-long ``state`` +# (the encrypted OAuth session blob routinely exceeds their limit). The upstream +# only needs an opaque value it echoes back on ``/callback``, so we forward a +# short random handle and keep the encrypted session in a per-flow HttpOnly +# cookie bound to that handle. The browser carries the cookie across the +# upstream round trip, so the flow stays correct with no server-side session +# store (works across proxy replicas, unlike an in-process map). +_OAUTH_STATE_COOKIE_PREFIX = "mcp_oauth_state_" +_OAUTH_STATE_COOKIE_TTL_SECONDS = 600 +_OAUTH_STATE_HANDLE_BYTES = 32 + + +def _oauth_state_cookie_name(relay_state: str) -> str: + return f"{_OAUTH_STATE_COOKIE_PREFIX}{relay_state}" + + +def _oauth_state_cookie_path_and_secure(request: Request) -> tuple[str, bool]: + parsed = urlparse(get_request_base_url(request)) + return parsed.path or "/", parsed.scheme == "https" + + +def _set_oauth_state_cookie( + response: Response, + request: Request, + relay_state: str, + encoded_state: str, +) -> None: + path, secure = _oauth_state_cookie_path_and_secure(request) + response.set_cookie( + key=_oauth_state_cookie_name(relay_state), + value=encoded_state, + max_age=_OAUTH_STATE_COOKIE_TTL_SECONDS, + path=path, + secure=secure, + httponly=True, + samesite="lax", + ) + + +def _resolve_encoded_oauth_state(request: Request, state: str) -> str: + """Return the encrypted OAuth session for a ``/callback`` request. + + New flows carry it in a per-flow cookie keyed by the short handle we + forwarded upstream (the IdP echoes that handle back as ``state``). Flows + started before this change - or in flight across a deploy - carry the + encrypted blob directly in ``state``, so fall back to it when the cookie + is absent. + """ + cookie_value = request.cookies.get(_oauth_state_cookie_name(state)) + return cookie_value if cookie_value else state + + +def _clear_oauth_state_cookie(response: Response, request: Request, state: str) -> None: + cookie_name = _oauth_state_cookie_name(state) + if cookie_name not in request.cookies: + return + path, secure = _oauth_state_cookie_path_and_secure(request) + response.delete_cookie( + key=cookie_name, + path=path, + secure=secure, + httponly=True, + samesite="lax", + ) + + def _get_validated_client_redirect_uri(request: Request, state_data: Dict[str, Any]) -> str: """Return a trusted (same-origin, loopback, or ops-allowlisted) client redirect URI from OAuth state. @@ -380,6 +448,12 @@ async def _store_per_user_token_server_side( ) return # Don't warm Redis if DB write failed + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( # noqa: PLC0415 + global_mcp_server_manager, + ) + + await global_mcp_server_manager.invalidate_user_oauth_token_cache(user_id, server.server_id) + # Warm the Redis cache so the first subsequent MCP call is a cache hit ttl = _compute_per_user_token_ttl(server, expires_in) await mcp_per_user_token_cache.set( @@ -390,6 +464,121 @@ async def _store_per_user_token_server_side( ) +def _raise_if_not_oauth2(mcp_server: MCPServer) -> None: + """Reject a server without upstream OAuth from the gateway's authorize/token/register flow. + + The client-forwarded token modes (``true_passthrough`` / ``oauth_delegate``) are allowed + through: the caller owns the upstream token, and this relayed flow is how a browser obtains + one against the upstream IdP (the admin UI's browser-only Authorize uses it). The minted + token is upstream-audienced and held by the caller; the gateway persists nothing for these + modes (``_persist_dcr_client_registration`` skips them unconditionally, so even the admin + Authorize path with ``persist_credentials`` enabled writes nothing to the server row). + """ + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( # noqa: PLC0415 # circular import with mcp_server_manager at module load + _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES, + ) + + if mcp_server.auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES: + return + raise HTTPException( + status_code=400, + detail={ + "error": "server_not_oauth2", + "message": ( + f"MCP server '{mcp_server.server_name or mcp_server.name}' does not use OAuth " + f"(auth_type={mcp_server.auth_type}). This server does not support the authorization-code " + "flow; it has no client_id, authorize, token, or registration endpoint. " + "Access is controlled by the server's configured auth_type and access groups" + ), + }, + ) + + +def _raise_unless_oauth2_discovery_server( + mcp_server: Optional[MCPServer], + mcp_server_name: Optional[str], + description: str, +) -> None: + """404 a NAMED discovery request unless it resolves to an oauth2 or DCR-bridge server. + + A named server that is unknown (or hidden from the caller) and one that exists + but is non-oauth2 both return the same 404, so the well-known discovery paths + cannot be used to enumerate non-OAuth server names. Root discovery (no name) is + unaffected, and pass-through servers are resolved by the caller before this runs. + DCR-bridge servers are admitted because they serve the gateway's own authorization + server metadata (the register, authorize, and token relays). + """ + if mcp_server_name is None: + return + if mcp_server is not None and mcp_server.auth_type == MCPAuth.oauth2: + return + if mcp_server is not None and mcp_server.is_dcr_bridge: + return + raise HTTPException( + status_code=404, + detail=f"MCP server '{mcp_server_name}' is {description}", + ) + + +def _dcr_bridge_relays_client_registration(mcp_server: MCPServer) -> bool: + """True when a DCR-bridge server relays client registration to the upstream authorization + server instead of short-circuiting to an admin-configured OAuth client. In the relay arm the + upstream holds each client's own registration, so the authorize and token relays pass the + client's ``client_id`` and ``redirect_uri`` through verbatim and the authorization code + returns directly to the client's redirect URI without transiting the gateway. Gateway-side + redirect trust and the ``/callback`` state relay therefore only apply to the short-circuit + arm, where the upstream only knows the gateway's own callback.""" + return mcp_server.is_dcr_bridge and bool(mcp_server.registration_url) and not mcp_server.client_id + + +def _require_s256_pkce( + code_challenge: Optional[str], + code_challenge_method: Optional[str], +) -> Tuple[str, str]: + """DCR-bridge servers serve unauthenticated public OAuth clients, so the PKCE downgrade + paths (no challenge, or a non-S256 method; RFC 7636 defaults a missing method to ``plain``) + are rejected at the gateway instead of relying on upstream enforcement. Returns the + validated pair so callers get non-optional values.""" + if code_challenge and code_challenge_method == "S256": + return code_challenge, code_challenge_method + raise HTTPException( + status_code=400, + detail=( + "This server requires PKCE: send code_challenge with " + "code_challenge_method=S256 on the authorization request" + ), + ) + + +def _redirect_to_upstream_authorize( + *, + mcp_server: MCPServer, + client_id: str, + redirect_uri: str, + state: str, + code_challenge: str, + code_challenge_method: str, + response_type: Optional[str], + scope: Optional[str], +) -> RedirectResponse: + """The bridge relay arm's authorize redirect: every client-supplied parameter passes through + to the upstream authorize endpoint verbatim, no relay state cookie is set, and the upstream + enforces its own registered redirect binding for the client.""" + scope_value = scope or (" ".join(mcp_server.scopes) if mcp_server.scopes else None) + passthrough_params = { + "client_id": client_id, + "redirect_uri": redirect_uri, + "state": state, + "response_type": response_type or "code", + "code_challenge": code_challenge, + "code_challenge_method": code_challenge_method, + **({"scope": scope_value} if scope_value else {}), + } + parsed_auth_url = urlparse(mcp_server.authorization_url or "") + merged_params = {**dict(parse_qsl(parsed_auth_url.query)), **passthrough_params} + return RedirectResponse(urlunparse(parsed_auth_url._replace(query=urlencode(merged_params)))) + + async def authorize_with_server( request: Request, mcp_server: MCPServer, @@ -401,11 +590,28 @@ async def authorize_with_server( response_type: Optional[str] = None, scope: Optional[str] = None, ): - if mcp_server.auth_type != "oauth2": - raise HTTPException(status_code=400, detail="MCP server is not OAuth2") + _raise_if_not_oauth2(mcp_server) if mcp_server.authorization_url is None: raise HTTPException(status_code=400, detail="MCP server authorization url is not set") + if mcp_server.is_dcr_bridge: + # Enforce S256 PKCE on both bridge arms. The relay arm forwards the validated, + # now-non-optional pair to the upstream authorize; the short-circuit arm keeps + # calling this for its enforcement side effect, then falls through to the gateway + # /callback flow below, which reads the original code_challenge names. + bridge_challenge, bridge_method = _require_s256_pkce(code_challenge, code_challenge_method) + if _dcr_bridge_relays_client_registration(mcp_server): + return _redirect_to_upstream_authorize( + mcp_server=mcp_server, + client_id=client_id, + redirect_uri=redirect_uri, + state=state, + code_challenge=bridge_challenge, + code_challenge_method=bridge_method, + response_type=response_type, + scope=scope, + ) + # Trusted redirect_uri: same-origin, loopback, or ops-allowlisted. # The URI is encrypted into the OAuth state and decoded on # /callback to redirect the user back; a non-trusted URI would be @@ -421,11 +627,12 @@ async def authorize_with_server( code_challenge_method=code_challenge_method, client_redirect_uri=redirect_uri, ) + relay_state = secrets.token_urlsafe(_OAUTH_STATE_HANDLE_BYTES) params = { "client_id": mcp_server.client_id if mcp_server.client_id else client_id, "redirect_uri": f"{request_base_url}/callback", - "state": encoded_state, + "state": relay_state, "response_type": response_type or "code", } if scope: @@ -442,7 +649,9 @@ async def authorize_with_server( existing_params = dict(parse_qsl(parsed_auth_url.query)) existing_params.update(params) final_url = urlunparse(parsed_auth_url._replace(query=urlencode(existing_params))) - return RedirectResponse(final_url) + response = RedirectResponse(final_url) + _set_oauth_state_cookie(response, request, relay_state, encoded_state) + return response async def exchange_token_with_server( @@ -457,14 +666,19 @@ async def exchange_token_with_server( refresh_token: Optional[str] = None, scope: Optional[str] = None, ): + _raise_if_not_oauth2(mcp_server) if grant_type not in ("authorization_code", "refresh_token"): raise HTTPException(status_code=400, detail="Unsupported grant_type") if mcp_server.token_url is None: raise HTTPException(status_code=400, detail="MCP server token url is not set") + # The id and secret must come from the same source. When the server-side client_id wins, + # falling back to the caller's secret pairs the persisted client with a foreign secret; the + # register short-circuit hands clients a placeholder secret ("dummy"), so a re-auth against a + # persisted public PKCE client (no stored secret) would send that placeholder and the IdP 401s. resolved_client_id = mcp_server.client_id if mcp_server.client_id else client_id - resolved_client_secret = mcp_server.client_secret if mcp_server.client_secret else client_secret + resolved_client_secret = mcp_server.client_secret if mcp_server.client_id else client_secret try: client_auth = build_token_endpoint_client_auth( auth_method=mcp_server.token_endpoint_auth_method, @@ -493,11 +707,21 @@ async def exchange_token_with_server( status_code=400, detail="code is required for authorization_code grant", ) + bridge_token_relay = _dcr_bridge_relays_client_registration(mcp_server) + if bridge_token_relay and not redirect_uri: + raise HTTPException( + status_code=400, + detail=( + "redirect_uri is required for the authorization_code grant on this server; " + "send the same redirect_uri used on the authorization request" + ), + ) proxy_base_url = get_request_base_url(request) + resolved_redirect_uri = redirect_uri if bridge_token_relay else f"{proxy_base_url}/callback" token_data = { "grant_type": "authorization_code", "code": code, - "redirect_uri": f"{proxy_base_url}/callback", + "redirect_uri": resolved_redirect_uri, **client_auth.body, } if code_verifier: @@ -515,7 +739,17 @@ async def exchange_token_with_server( detail="MCP upstream token endpoint returned no response", ) - response.raise_for_status() + try: + response.raise_for_status() + except httpx.HTTPStatusError as exc: + if "invalid_target" in exc.response.text: + verbose_logger.warning( + "MCP server %s: the upstream authorization server rejected the token request with " + "invalid_target; it may require RFC 8707 resource indicators, which the gateway " + "does not send yet (tracked as LIT-4339)", + mcp_server.server_id, + ) + raise token_response = response.json() access_token = token_response["access_token"] @@ -573,6 +807,262 @@ async def exchange_token_with_server( return JSONResponse(result, headers=TOKEN_NO_CACHE_HEADERS) +class _DcrClientRegistration(BaseModel): + """RFC 7591 dynamic client registration response, narrowed to the fields the gateway + must persist to authenticate later token-endpoint calls. Extra members are ignored.""" + + client_id: str + client_secret: Optional[str] = None + token_endpoint_auth_method: Optional[str] = None + + +class _PersistedDcrCredentials(BaseModel): + client_id: Optional[str] = None + client_secret: Optional[str] = None + token_endpoint_auth_method: Optional[str] = None + redirect_uris: Optional[list[str]] = None + + +def _redirect_uri_not_registered(credentials: _PersistedDcrCredentials, current_redirect_uri: str) -> bool: + """Whether a persisted DCR client is positively known NOT to cover the current callback. + + A DCR client is bound to the redirect_uris it was registered with; if the proxy's + resolved public origin has since changed, every authorize built for it will be + rejected by the IdP. Clients persisted before ``redirect_uris`` was recorded (and + admin-configured clients, which never get a recording) return False so they are + grandfathered rather than re-registered, because re-minting a client_id orphans + every user's refresh tokens for that server.""" + recorded = credentials.redirect_uris + if not recorded: + return False + return current_redirect_uri not in recorded + + +def _get_persisted_dcr_credentials(credentials: object) -> Optional[_PersistedDcrCredentials]: + if not credentials: + return None + try: + return ( + _PersistedDcrCredentials.model_validate_json(credentials) + if isinstance(credentials, str) + else _PersistedDcrCredentials.model_validate(credentials) + ) + except ValidationError: + return None + + +def _decrypt_persisted_dcr_credential(value: Optional[str], key: str) -> Optional[str]: + if value is None: + return None + return decrypt_value_helper( + value=value, + key=key, + exception_type="debug", + return_original_value=True, + ) + + +def _apply_persisted_dcr_credentials(mcp_server: MCPServer, credentials: _PersistedDcrCredentials) -> bool: + client_id = _decrypt_persisted_dcr_credential(credentials.client_id, "client_id") + if not client_id: + return False + mcp_server.client_id = client_id + mcp_server.client_secret = _decrypt_persisted_dcr_credential(credentials.client_secret, "client_secret") + mcp_server.token_endpoint_auth_method = credentials.token_endpoint_auth_method + return True + + +async def _get_persisted_mcp_server_with_dcr_client_id( + mcp_server: MCPServer, +) -> Optional[tuple["LiteLLM_MCPServerTable", _PersistedDcrCredentials]]: + from litellm.proxy._experimental.mcp_server.db import get_mcp_server # noqa: PLC0415 + from litellm.proxy.utils import get_prisma_client_or_throw # noqa: PLC0415 + + try: + prisma_client = get_prisma_client_or_throw("Database not connected. Cannot read MCP OAuth client registration.") + persisted_mcp_server = await get_mcp_server( + prisma_client=prisma_client, + server_id=mcp_server.server_id, + ) + except Exception as exc: # noqa: BLE001 + verbose_logger.debug( + "register_client_with_server: failed to read persisted DCR client registration for server_id=%s: %s", + mcp_server.server_id, + exc, + ) + return None + + if persisted_mcp_server is None: + return None + + credentials = _get_persisted_dcr_credentials(persisted_mcp_server.credentials) + if credentials is None or not credentials.client_id: + return None + + return persisted_mcp_server, credentials + + +async def _reuse_persisted_dcr_client_if_available( + mcp_server: MCPServer, current_redirect_uri: Optional[str] = None +) -> bool: + persisted = await _get_persisted_mcp_server_with_dcr_client_id(mcp_server) + if persisted is None: + return False + persisted_mcp_server, credentials = persisted + if current_redirect_uri is not None and _redirect_uri_not_registered(credentials, current_redirect_uri): + verbose_logger.debug( + "register_client_with_server: not reusing persisted DCR client for server_id=%s; its registered " + "redirect_uris=%s do not include the current callback %s. The operator-facing warning for this " + "re-registration event is emitted once by _persisted_dcr_redirect_uri_is_stale.", + mcp_server.server_id, + credentials.redirect_uris, + current_redirect_uri, + ) + return False + if not _apply_persisted_dcr_credentials(mcp_server, credentials): + return False + + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( # noqa: PLC0415 + global_mcp_server_manager, + ) + + try: + await global_mcp_server_manager.update_server(persisted_mcp_server) + except Exception as exc: # noqa: BLE001 + verbose_logger.warning( + "register_client_with_server: failed to refresh persisted DCR client registration for server_id=%s: %s", + mcp_server.server_id, + exc, + ) + return bool(mcp_server.client_id) + + +async def _persisted_dcr_redirect_uri_is_stale(mcp_server: MCPServer, current_redirect_uri: str) -> bool: + """Whether the server's persisted DCR client is bound to redirect_uris that no longer + cover the current proxy callback, meaning authorize is guaranteed to fail IdP-side. + + Consulted when the in-memory server already carries a hydrated client_id, which + otherwise short-circuits registration before any redirect check can run. Servers + without a persisted DCR recording (admin-configured client_id, or registered before + redirect_uris were recorded) are never reported stale.""" + persisted = await _get_persisted_mcp_server_with_dcr_client_id(mcp_server) + if persisted is None: + return False + _, credentials = persisted + if not _redirect_uri_not_registered(credentials, current_redirect_uri): + return False + verbose_logger.warning( + "register_client_with_server: persisted DCR client for server_id=%s is registered with redirect_uris=%s " + "which do not include the current callback %s (proxy origin changed); registering a replacement client. " + "Users previously signed in to this server will need to re-authenticate.", + mcp_server.server_id, + credentials.redirect_uris, + current_redirect_uri, + ) + return True + + +DcrRegistrationPersistenceResult = Literal["persisted", "reused", "skipped", "failed"] + + +async def _persist_dcr_client_registration( + mcp_server: MCPServer, registration_response: object, current_redirect_uri: str +) -> DcrRegistrationPersistenceResult: + """Persist the dynamically registered OAuth client (RFC 7591) onto the MCP server row. + + The interactive authorization_code flow mints a ``client_id`` via Dynamic Client + Registration that discovery cannot re-derive; without persisting it the autonomous + ``refresh_token`` grant has no client identity, so an expired access token forces a + full re-authorization instead of a silent refresh. Mirrors the ``encrypt_credentials`` + write that ``client_credentials`` and token exchange already use. Failures are logged, + never raised: registration still returns to the caller even when persistence fails. + + The client-forwarded token modes (``true_passthrough`` / ``oauth_delegate``) are skipped + unconditionally: the caller holds the upstream token and the gateway must hold no OAuth + client identity for these servers. Persisting here would stamp ``oauth2_flow`` and a + ``client_id`` onto a server whose mode promises the gateway stores nothing, making a + fresh pass-through server read as gateway-authorized. + + ``redirect_uris`` records what the client is bound to so a later origin change can be + detected as a positive mismatch and trigger re-registration instead of stranding the + server on IdP-side redirect_uri rejections. ``client_secret`` and + ``token_endpoint_auth_method`` are written explicitly (None when absent) because + ``update_mcp_server`` merges credential blobs: a re-registered public client must not + inherit the previous client's secret or auth method. + """ + if mcp_server.is_true_passthrough or mcp_server.is_oauth_delegate: + return "skipped" + + try: + registration = _DcrClientRegistration.model_validate(registration_response) + except ValidationError as exc: + verbose_logger.warning( + "register_client_with_server: DCR response has no usable client_id for server_id=%s; " + "client registration not persisted (%s)", + mcp_server.server_id, + exc, + ) + return "failed" + + if await _reuse_persisted_dcr_client_if_available(mcp_server, current_redirect_uri=current_redirect_uri): + return "reused" + + credentials: MCPCredentials = { + "client_id": registration.client_id, + "client_secret": registration.client_secret, + "token_endpoint_auth_method": ( + "client_secret_basic" if registration.token_endpoint_auth_method == "client_secret_basic" else None + ), + "redirect_uris": [current_redirect_uri], + } + + from litellm.proxy._experimental.mcp_server.db import update_mcp_server # noqa: PLC0415 + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( # noqa: PLC0415 + global_mcp_server_manager, + ) + from litellm.proxy._types import UpdateMCPServerRequest # noqa: PLC0415 + from litellm.proxy.utils import get_prisma_client_or_throw # noqa: PLC0415 + + try: + prisma_client = get_prisma_client_or_throw( + "Database not connected. Cannot persist MCP OAuth client registration." + ) + updated_row = await update_mcp_server( + prisma_client=prisma_client, + data=UpdateMCPServerRequest( + server_id=mcp_server.server_id, + credentials=credentials, + oauth2_flow="authorization_code", + **({"token_url": mcp_server.token_url} if mcp_server.token_url else {}), + ), + touched_by="mcp_oauth_dcr", + ) + await global_mcp_server_manager.update_server(updated_row) + return "persisted" + except Exception as exc: # noqa: BLE001 + verbose_logger.warning( + "register_client_with_server: failed to persist DCR client registration for server_id=%s: %s", + mcp_server.server_id, + exc, + ) + return "failed" + + +_MAX_UPSTREAM_ERROR_CHARS = 500 + + +def _safe_upstream_error_detail(response: httpx.Response) -> str: + """Bounded plaintext summary of an upstream registration failure for the client. + + RFC 7591 error bodies are small JSON objects (``error`` / ``error_description``); relaying the + text lets the client read the real reason instead of a bare 500, and the length bound keeps a + hostile or oversized upstream body from bloating the gateway response.""" + body = response.text + if not body: + return response.reason_phrase or "upstream registration failed" + return body[:_MAX_UPSTREAM_ERROR_CHARS] + + async def register_client_with_server( request: Request, mcp_server: MCPServer, @@ -581,15 +1071,29 @@ async def register_client_with_server( response_types: Optional[list], token_endpoint_auth_method: Optional[str], fallback_client_id: Optional[str] = None, + persist_credentials: bool = False, + client_redirect_uris: Optional[list] = None, ): + _raise_if_not_oauth2(mcp_server) request_base_url = get_request_base_url(request) + current_redirect_uri = f"{request_base_url}/callback" dummy_return = { "client_id": fallback_client_id or mcp_server.server_name, "client_secret": "dummy", - "redirect_uris": [f"{request_base_url}/callback"], + "redirect_uris": [current_redirect_uri], } - if mcp_server.client_id and mcp_server.client_secret: + if mcp_server.client_id and not ( + persist_credentials + and mcp_server.registration_url + and await _persisted_dcr_redirect_uri_is_stale(mcp_server, current_redirect_uri) + ): + return dummy_return + + if await _reuse_persisted_dcr_client_if_available( + mcp_server, + current_redirect_uri=current_redirect_uri if persist_credentials else None, + ): return dummy_return if mcp_server.authorization_url is None: @@ -598,12 +1102,19 @@ async def register_client_with_server( if mcp_server.registration_url is None: return dummy_return + bridge_relay = _dcr_bridge_relays_client_registration(mcp_server) + if bridge_relay and not client_redirect_uris: + raise HTTPException( + status_code=400, + detail="redirect_uris is required to register a client with this server", + ) + register_data = { "client_name": client_name, - "redirect_uris": [f"{request_base_url}/callback"], - "grant_types": grant_types or [], - "response_types": response_types or [], - "token_endpoint_auth_method": token_endpoint_auth_method or "", + "redirect_uris": client_redirect_uris if bridge_relay else [current_redirect_uri], + "grant_types": grant_types or (["authorization_code", "refresh_token"] if bridge_relay else []), + "response_types": response_types or (["code"] if bridge_relay else []), + "token_endpoint_auth_method": token_endpoint_auth_method or ("none" if bridge_relay else ""), } headers = { "Content-Type": "application/json", @@ -621,10 +1132,17 @@ async def register_client_with_server( status_code=502, detail="MCP upstream registration endpoint returned no response", ) + if bridge_relay and response.status_code >= 400: + raise HTTPException(status_code=response.status_code, detail=_safe_upstream_error_detail(response)) response.raise_for_status() token_response = response.json() + if persist_credentials and not bridge_relay: + persistence_result = await _persist_dcr_client_registration(mcp_server, token_response, current_redirect_uri) + if persistence_result == "reused": + return dummy_return + return JSONResponse(token_response) @@ -655,6 +1173,7 @@ async def authorize( mcp_server = _resolve_oauth2_server_for_root_endpoints(client_ip=client_ip) if mcp_server is None: raise HTTPException(status_code=404, detail="MCP server not found") + _raise_if_not_oauth2(mcp_server) # Use server's stored client_id when caller doesn't supply one. # Raise a clear error instead of passing an empty string — an empty # client_id would silently produce a broken authorization URL. @@ -791,17 +1310,19 @@ async def callback( error_description, ) if state: + encoded_state = _resolve_encoded_oauth_state(request, state) try: - state_data = decode_state_hash(state) + state_data = decode_state_hash(encoded_state) original_state = state_data.get("original_state") redirect_uri = _get_validated_client_redirect_uri(request, state_data) - except HTTPException: - # Untrusted/invalid client redirect_uri — surface inline rather - # than blindly forwarding the error to an attacker-controlled URL. - return _render_oauth_error_html(error, error_description) except Exception: - # State could not be decrypted (expired key, tampered, etc.). - return _render_oauth_error_html(error, error_description) + # Untrusted/invalid client redirect_uri (HTTPException), or an + # undecryptable state (expired key, tampered): surface the IdP + # error inline rather than forwarding it to an attacker-controlled + # URL, and drop the one-time cookie we can no longer consume. + response = _render_oauth_error_html(error, error_description) + _clear_oauth_state_cookie(response, request, state) + return response params: Dict[str, str] = {"error": error} if error_description: @@ -811,7 +1332,9 @@ async def callback( if original_state is not None: params["state"] = original_state complete_returned_url = _append_query_params(redirect_uri, params) - return RedirectResponse(url=complete_returned_url, status_code=302) + response = RedirectResponse(url=complete_returned_url, status_code=302) + _clear_oauth_state_cookie(response, request, state) + return response # No state — nothing to round-trip to. Show the user the error. return _render_oauth_error_html(error, error_description) @@ -827,7 +1350,8 @@ async def callback( # 3. Successful authorization response. try: - state_data = decode_state_hash(state) + encoded_state = _resolve_encoded_oauth_state(request, state) + state_data = decode_state_hash(encoded_state) original_state = state_data["original_state"] # Re-validate the client redirect URI at the sink. /authorize @@ -840,14 +1364,18 @@ async def callback( params = {"code": code, "state": original_state} complete_returned_url = _append_query_params(redirect_uri, params) - return RedirectResponse(url=complete_returned_url, status_code=302) + response = RedirectResponse(url=complete_returned_url, status_code=302) + _clear_oauth_state_cookie(response, request, state) + return response except HTTPException: # Re-raise so a non-loopback base_url surfaces as 400 instead of # a generic "authentication incomplete" redirect. raise except Exception: - return HTMLResponse("Authentication incomplete. You can close this window.") + response = HTMLResponse("Authentication incomplete. You can close this window.") + _clear_oauth_state_cookie(response, request, state) + return response # ------------------------------ @@ -987,11 +1515,15 @@ async def _build_oauth_protected_resource_response( """ Build OAuth protected resource response with the appropriate URL pattern. - For pass-through MCP servers (``MCPServer.is_oauth_passthrough``), the - gateway proxies the upstream's own ``oauth-protected-resource`` metadata - so that standards-compliant MCP clients discover the **upstream** IdP - instead of the gateway. The ``resource`` field is rewritten to the - gateway's own URL so clients present the bearer token back to the gateway. + For pass-through MCP servers, the gateway proxies the upstream's own + ``oauth-protected-resource`` metadata so standards-compliant MCP clients + discover the **upstream** IdP instead of the gateway. For ``true_passthrough`` + and ``oauth_delegate`` the metadata is returned verbatim (``resource`` stays + the upstream): the caller's token is forwarded to and validated by the + upstream, so its audience must be the upstream — rewriting it to the gateway + would make a strict IdP (e.g. Entra) refuse to mint it or the upstream reject + it. Only the legacy ``is_oauth_passthrough`` opt-in rewrites ``resource`` to + the gateway's own URL so clients present the bearer token back to the gateway. Args: request: FastAPI Request object @@ -1030,9 +1562,18 @@ async def _build_oauth_protected_resource_response( else: resource_url = f"{request_base_url}/mcp" + if mcp_server is not None and mcp_server_name and mcp_server.is_dcr_bridge: + return { + "authorization_servers": [f"{request_base_url}/{mcp_server_name}"], + "resource": resource_url, + "scopes_supported": (mcp_server.scopes if mcp_server.scopes else []), + } + # Pass-through branch: proxy the upstream's own metadata so discovery # directs the client at the real IdP (Okta, Keycloak, …) instead of us. - if mcp_server is not None and mcp_server.is_oauth_passthrough: + if mcp_server is not None and ( + mcp_server.is_oauth_passthrough or mcp_server.is_oauth_delegate or mcp_server.is_true_passthrough + ): try: upstream_metadata = await fetch_upstream_oauth_protected_resource(mcp_server) except Exception as exc: @@ -1048,8 +1589,9 @@ async def _build_oauth_protected_resource_response( ) if upstream_metadata is not None: - response = {**upstream_metadata, "resource": resource_url} - return response + if mcp_server.is_true_passthrough or mcp_server.is_oauth_delegate: + return upstream_metadata + return {**upstream_metadata, "resource": resource_url} # Upstream responded but with non-200 or non-dict payload. For # pass-through servers the gateway is NOT the authorization server, @@ -1063,6 +1605,15 @@ async def _build_oauth_protected_resource_response( detail=(f"Upstream oauth-protected-resource metadata unavailable for MCP server {mcp_server.name!r}"), ) + obo_response = _obo_protected_resource_response(mcp_server, resource_url) + if obo_response is not None: + return obo_response + + # An OBO server with no configured issuer falls through to the gateway default so discovery still + # returns metadata; every other non-oauth2 named server 404s to avoid enumeration. + if mcp_server is None or mcp_server.auth_type != MCPAuth.oauth2_token_exchange: + _raise_unless_oauth2_discovery_server(mcp_server, mcp_server_name, "not an OAuth-protected resource") + return { "authorization_servers": [ (f"{request_base_url}/{mcp_server_name}" if mcp_server_name else f"{request_base_url}") @@ -1072,6 +1623,51 @@ async def _build_oauth_protected_resource_response( } +def _obo_protected_resource_response(mcp_server: Optional[MCPServer], resource_url: str) -> Optional[dict]: + """The OBO (token_exchange) PRM, or None when this server is not OBO / no issuer is configured. + + The client SSOs with the IdP to obtain a subject token, which LiteLLM then exchanges, so discovery + points at the JWT-auth issuer(s) LiteLLM trusts (the same IdP that issues and validates the + subject), not the gateway. None falls the caller back to the gateway default so discovery still + returns metadata; it just can't name the IdP. + """ + if mcp_server is None or mcp_server.auth_type != MCPAuth.oauth2_token_exchange: + return None + issuers = _jwt_auth_issuers() + if not issuers: + return None + return { + "authorization_servers": issuers, + "resource": resource_url, + "scopes_supported": (mcp_server.scopes if mcp_server.scopes else []), + } + + +def _jwt_auth_issuers() -> list: + """The OAuth issuer identifier(s) LiteLLM's JWT auth trusts, for the OBO PRM authorization_servers. + + In token_exchange the IdP that issues the subject JWT is the same one LiteLLM validates it + against, so OBO discovery points clients at the JWT-auth issuer to obtain a subject token. + Sourced from ``JWT_ISSUER`` and any configured ``litellm_jwtauth.issuers``. + """ + import os # noqa: PLC0415 + + from litellm.proxy.proxy_server import general_settings # noqa: PLC0415 + + issuers: list = [] + env_issuer = os.getenv("JWT_ISSUER") + if env_issuer: + issuers.append(env_issuer) + + jwtauth = general_settings.get("litellm_jwtauth") if isinstance(general_settings, dict) else None + raw_issuers = jwtauth.get("issuers") if isinstance(jwtauth, dict) else getattr(jwtauth, "issuers", None) + for cfg in raw_issuers or []: + issuer = cfg.get("issuer") if isinstance(cfg, dict) else getattr(cfg, "issuer", None) + if issuer and issuer not in issuers: + issuers.append(issuer) + return issuers + + # Standard MCP pattern: /.well-known/oauth-protected-resource/mcp/{server_name} # This is the pattern expected by standard MCP clients (mcp-inspector, VSCode Copilot) @router.get( @@ -1149,6 +1745,8 @@ def _build_oauth_authorization_server_response( if mcp_server_name: mcp_server = global_mcp_server_manager.get_mcp_server_by_name(mcp_server_name, client_ip=client_ip) + _raise_unless_oauth2_discovery_server(mcp_server, mcp_server_name, "not an OAuth authorization server") + return { "issuer": request_base_url, # point to your proxy "authorization_endpoint": authorization_endpoint, @@ -1300,6 +1898,7 @@ async def register_client(request: Request, mcp_server_name: Optional[str] = Non response_types=data.get("response_types", []), token_endpoint_auth_method=data.get("token_endpoint_auth_method", ""), fallback_client_id=resolved.server_name or resolved.name, + client_redirect_uris=data.get("redirect_uris"), ) return dummy_return @@ -1314,4 +1913,5 @@ async def register_client(request: Request, mcp_server_name: Optional[str] = Non response_types=data.get("response_types", []), token_endpoint_auth_method=data.get("token_endpoint_auth_method", ""), fallback_client_id=mcp_server_name, + client_redirect_uris=data.get("redirect_uris"), ) diff --git a/litellm/proxy/_experimental/mcp_server/exceptions.py b/litellm/proxy/_experimental/mcp_server/exceptions.py index b3f7ca9bbe2..3e3e549008d 100644 --- a/litellm/proxy/_experimental/mcp_server/exceptions.py +++ b/litellm/proxy/_experimental/mcp_server/exceptions.py @@ -73,3 +73,18 @@ class MCPUpstreamAuthError(Exception): detail=detail, headers={"www-authenticate": challenge} if challenge else None, ) + + +class MCPToolResultError(Exception): + """An MCP tool call completed with ``isError=True`` in its result. + + Never raised on the wire path: streamable HTTP MCP correctly returns tool + failures as HTTP 200 with ``result.isError: true`` per the MCP spec. This + exception only drives the standard failure logging (``status="failure"`` + payload, OTel ERROR span) for such results. + + Lives here rather than ``utils.py`` deliberately: tests reload ``utils`` + to re-read its env-derived constants, and a reload would fork this class + into two identities, breaking ``isinstance`` checks against instances + created before the reload. + """ diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index b6760e58852..1d681b43b9e 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -13,10 +13,12 @@ import json import os import re import time -from typing import Any, Callable, Dict, List, Literal, Optional, Set, Tuple, Union, cast +from contextlib import asynccontextmanager +from typing import Any, AsyncIterator, Callable, Literal, Optional, Union, cast from urllib.parse import urlparse import anyio +import httpx from fastapi import HTTPException from httpx import HTTPStatusError from mcp import ReadResourceResult, Resource @@ -55,7 +57,11 @@ from litellm.proxy._experimental.mcp_server.elicitation_handler import ( from litellm.proxy._experimental.mcp_server.sampling_handler import ( MCP_SAMPLING_AVAILABLE, ) -from litellm.proxy._experimental.mcp_server.oauth2_token_cache import resolve_mcp_auth +from litellm.proxy._experimental.mcp_server.oauth2_token_cache import ( + MCPPerUserTokenCache, + mcp_per_user_token_cache, + resolve_mcp_auth, +) from litellm.proxy._experimental.mcp_server.outbound_credentials import ( Error, Ok, @@ -63,15 +69,25 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials import ( ) from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( raise_public, + raise_token_exchange_challenge, raise_user_oauth_challenge, to_server_spec, to_subject, ) +from litellm.proxy._experimental.mcp_server.outbound_credentials.oauth_token_store import ( + InvalidatableOAuthTokenStore, +) from litellm.proxy._experimental.mcp_server.outbound_credentials.per_user_oauth_store import ( LazyPerUserOAuthTokenStore, ) +from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchange_provider import ( + build_token_exchanger, +) from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( AuthorizationCodeConfig, + PassthroughConfig, + ServerSpec, + TokenExchangeConfig, ) from litellm.proxy._experimental.mcp_server.utils import ( MCP_TOOL_PREFIX_SEPARATOR, @@ -104,10 +120,10 @@ from litellm.proxy._types import ( from litellm.proxy.auth.ip_address_utils import IPAddressUtils from litellm.proxy.common_utils.encrypt_decrypt_utils import decrypt_value_helper from litellm.proxy.common_utils.user_api_key_cache import get_management_object_ttl -from litellm.proxy.utils import ProxyLogging +from litellm.proxy.utils import ProxyLogging, get_server_root_path from litellm.repositories.table_repositories import MCPServerRepository from litellm.types.llms.custom_http import httpxSpecialProvider -from litellm.types.mcp import MCPAuth, MCPStdioConfig +from litellm.types.mcp import DEFAULT_SUBJECT_TOKEN_TYPE, MCPAuth, MCPStdioConfig from litellm.types.mcp_server.mcp_server_manager import ( MCPInfo, MCPOAuthMetadata, @@ -155,10 +171,20 @@ _AZURE_ENTRA_HOSTS = { # BYOK credential cache. Keyed by (user_id, server_id); value is # (values_dict, monotonic_timestamp). Keeps the tool-call and tool-listing # paths off the DB on every request within the TTL window. -_user_env_vars_cache: Dict[Tuple[str, str], Tuple[Dict[str, str], float]] = {} +_user_env_vars_cache: dict[tuple[str, str], tuple[dict[str, str], float]] = {} _USER_ENV_VARS_CACHE_TTL = 60 # seconds _USER_ENV_VARS_CACHE_MAX_SIZE = 4096 # cap to prevent unbounded growth +# Auth types whose upstream OAuth endpoints (protected-resource + authorization-server metadata) the +# gateway discovers from the upstream itself: interactive oauth2 and the two client-forwarded modes. +# OBO/M2M endpoint discovery is decided separately via _obo_needs_endpoint_discovery. Shared by the +# config-YAML and DB server loaders so the two paths cannot drift on which modes trigger discovery. +_UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES: tuple[MCPAuth, ...] = ( + MCPAuth.oauth2, + MCPAuth.true_passthrough, + MCPAuth.oauth_delegate, +) + def invalidate_user_env_vars_cache(user_id: str, server_id: str) -> None: """Drop a cached entry after the user stores or clears their env var values @@ -166,7 +192,7 @@ def invalidate_user_env_vars_cache(user_id: str, server_id: str) -> None: _user_env_vars_cache.pop((user_id, server_id), None) -def _write_user_env_vars_cache(user_id: str, server_id: str, values: Dict[str, str]) -> None: +def _write_user_env_vars_cache(user_id: str, server_id: str, values: dict[str, str]) -> None: cache_key = (user_id, server_id) # Re-insert at the tail so eviction drops the oldest-written entry, not a # freshly refreshed one, and only sheds a single entry instead of wiping the @@ -179,7 +205,7 @@ def _write_user_env_vars_cache(user_id: str, server_id: str, values: Dict[str, s def _should_strip_caller_authorization( mcp_server: MCPServer, - raw_headers: Optional[Dict[str, str]], + raw_headers: Optional[dict[str, str]], user_api_key_auth: Optional[UserAPIKeyAuth], ) -> bool: """Decide whether the caller's ``Authorization`` header must NOT be @@ -205,7 +231,18 @@ def _should_strip_caller_authorization( pass-through cold-start case (RFC 9728) the bearer in ``Authorization`` is the upstream OAuth token and must be forwarded, so we keep it. + - **oauth_delegate servers**: admission always runs and there is no + anonymous path, so the caller's separate ``Authorization`` is + forwarded only when a distinct ``x-litellm-api-key`` carried + admission. Without that header the ``Authorization`` *was* the + admission credential — a virtual key, an IdP JWT, or an SSO / OIDC / + session token whose ``api_key`` is ``None`` — and must never reach + the upstream, so it is stripped regardless of the ``api_key`` value. """ + if mcp_server.auth_type == MCPAuth.oauth2_token_exchange: + # OBO: the inbound Authorization is the subject token. It is exchanged at the IdP and only the + # exchanged token is sent upstream, so the raw caller bearer must never be forwarded. + return True if mcp_server.has_client_credentials: return True if mcp_server.auth_type == MCPAuth.oauth2 and to_server_spec(mcp_server) is not None: @@ -214,11 +251,13 @@ def _should_strip_caller_authorization( # upstream — it would override another user's stored credential. Delegate and # pass-through return None from to_server_spec and keep forwarding the bearer. return True - if not mcp_server.is_oauth_passthrough: + if not (mcp_server.is_oauth_passthrough or mcp_server.is_oauth_delegate): return False normalized_raw_headers = {str(k).lower(): v for k, v in (raw_headers or {}).items() if isinstance(k, str)} has_explicit_litellm_admission_header = normalized_raw_headers.get("x-litellm-api-key") is not None + if mcp_server.is_oauth_delegate: + return not has_explicit_litellm_admission_header admission_consumed_authorization_as_litellm_key = ( user_api_key_auth is not None and bool(getattr(user_api_key_auth, "api_key", None)) @@ -241,9 +280,162 @@ def _without_authorization( return filtered or None +def _format_byok_openapi_auth_header(mcp_server: MCPServer, mcp_auth_header: str) -> str: + """Format a raw BYOK credential for OpenAPI tool ``Authorization`` injection.""" + if mcp_server.auth_type == MCPAuth.api_key: + return f"ApiKey {mcp_auth_header}" + if mcp_server.auth_type == MCPAuth.basic: + return f"Basic {mcp_auth_header}" + return f"Bearer {mcp_auth_header}" + + +def _openapi_forwarded_extra_headers( + mcp_server: MCPServer, + raw_headers: Optional[dict[str, str]], + user_api_key_auth: Optional[UserAPIKeyAuth], +) -> Optional[dict[str, str]]: + if not mcp_server.extra_headers or not raw_headers: + return None + normalized_raw = {str(k).lower(): v for k, v in raw_headers.items() if isinstance(k, str)} + skip_caller_authorization = _should_strip_caller_authorization( + mcp_server=mcp_server, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ) + forwarded: dict[str, str] = {} + for header_name in mcp_server.extra_headers: + if not isinstance(header_name, str): + continue + if skip_caller_authorization and header_name.lower() == "authorization": + continue + value = normalized_raw.get(header_name.lower()) + if value is not None: + forwarded[header_name] = value + return forwarded or None + + +async def _resolve_byok_mcp_auth_header( + mcp_server: MCPServer, + user_api_key_auth: Optional[UserAPIKeyAuth], + mcp_auth_header: Optional[str], +) -> Optional[str]: + """Resolve BYOK credential for tool calls that bypass ``execute_mcp_tool``.""" + if not mcp_server.is_byok: + return mcp_auth_header + + from litellm.proxy._experimental.mcp_server.server import ( + _check_byok_credential, + _get_byok_credential, + ) + + if not mcp_auth_header: + byok_cred = await _get_byok_credential(mcp_server, user_api_key_auth) + if byok_cred is None: + raise HTTPException( + status_code=401, + detail={ + "error": "byok_auth_required", + "server_id": mcp_server.server_id, + "server_name": mcp_server.server_name or mcp_server.name, + "message": ( + "No stored credential found for this BYOK server. " + "Complete the OAuth authorization flow to provide your API key." + ), + }, + headers={"WWW-Authenticate": 'Bearer resource_metadata="/.well-known/oauth-protected-resource"'}, + ) + return byok_cred + + await _check_byok_credential(mcp_server, user_api_key_auth) + return mcp_auth_header + + +def _client_forwarded_authorization_headers( + mcp_server: MCPServer, + oauth2_headers: Optional[dict[str, str]], + raw_headers: Optional[dict[str, str]], + user_api_key_auth: Optional[UserAPIKeyAuth], +) -> Optional[dict[str, str]]: + """Egress headers for the client-forwarded-token modes (``true_passthrough`` / ``oauth_delegate``). + + Forwards the caller's ``Authorization`` to the upstream, stripped when + ``_should_strip_caller_authorization`` says it was consumed as the LiteLLM admission key. Shared by + ``_call_regular_mcp_tool`` and ``server.py``'s ``_prepare_mcp_server_headers`` so the two egress + paths cannot drift, mirroring the ``_should_strip_caller_authorization`` split. + """ + extra_headers = oauth2_headers.copy() if oauth2_headers else None + if extra_headers and _should_strip_caller_authorization( + mcp_server=mcp_server, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ): + return _without_authorization(extra_headers) + return extra_headers + + +def _take_forwarded_authorization( + headers: Optional[dict[str, str]], +) -> tuple[Optional[str], Optional[dict[str, str]]]: + """Pop the ``Authorization`` value out of ``headers`` (case-insensitive), returning it with the + remaining headers, so the passthrough resolver arm is the single Authorization source rather than + the header also riding in ``extra_headers`` (which the resolved auth would then defer to).""" + if not headers: + return None, headers + value = next((v for k, v in headers.items() if k.lower() == "authorization"), None) + return value, _without_authorization(headers) + + +def _passthrough_token_from_mcp_auth_header( + mcp_auth_header: Optional[Union[str, dict[str, str]]], +) -> Optional[str]: + """The caller's per-server upstream credential for a passthrough-mode server, or None. + + Sourced from ``x-mcp-{alias}-authorization`` (string or per-header dict form) or the deprecated + global ``x-mcp-auth`` fallback. Per-server headers are the multi-server shape: they bind one + token to one server, so an aggregate scope with several passthrough-mode servers never replays + a single credential across upstreams. The value is forwarded verbatim, so it must be the full + header value (e.g. ``Bearer ``).""" + if isinstance(mcp_auth_header, str): + return mcp_auth_header or None + if isinstance(mcp_auth_header, dict): + return next((v for k, v in mcp_auth_header.items() if k.lower() == "authorization"), None) + return None + + +def _consumes_caller_authorization(server: MCPServer) -> bool: + """True when this server's egress forwards the caller's request-wide ``Authorization`` upstream: + the client-forwarded token modes, legacy OAuth pass-through, and legacy upstream-delegated + interactive oauth2. An unstamped oauth2 row (flow column not yet backfilled) reads as a consumer, + which errs toward suppression — the fail-safe direction.""" + if server.is_true_passthrough or server.is_oauth_delegate or server.is_oauth_passthrough: + return True + return ( + server.auth_type == MCPAuth.oauth2 + and getattr(server, "delegate_auth_to_upstream", False) is True + and not server.has_client_credentials + ) + + +def _caller_authorization_fans_out( + server: MCPServer, + scope_servers: Optional[list[MCPServer]], +) -> bool: + """True when forwarding the caller's request-wide ``Authorization`` to ``server`` inside a + listing fan-out would replay one credential against multiple upstreams: another server in the + scope also consumes it (RFC 9700 cross-resource replay). ``scope_servers`` is None for + explicitly-addressed operations (tool call, get_prompt, read_resource, single-server routes), + where the client named the one target and the gateway is not choosing recipients.""" + if scope_servers is None: + return False + return any( + other is not None and other.server_id != server.server_id and _consumes_caller_authorization(other) + for other in scope_servers + ) + + def _extract_upstream_auth_failure( exc: BaseException, -) -> Optional[Tuple[int, Optional[str]]]: +) -> Optional[tuple[int, Optional[str]]]: """Walk the exception tree looking for an HTTP 401/403 response from the upstream MCP server. @@ -255,8 +447,8 @@ def _extract_upstream_auth_failure( Returns ``(status_code, www_authenticate)`` on match, else ``None``. """ - seen: Set[int] = set() - stack: List[BaseException] = [exc] + seen: set[int] = set() + stack: list[BaseException] = [exc] while stack: current = stack.pop() if id(current) in seen: @@ -337,7 +529,7 @@ def _warn_internal_delegate_pkce_if_applicable(server: MCPServer, *, source: str ) -def _deserialize_json_dict(data: Any) -> Optional[Dict[str, str]]: +def _deserialize_json_dict(data: Any) -> Optional[dict[str, str]]: """ Deserialize optional JSON mappings stored in the database. @@ -358,7 +550,7 @@ def _deserialize_json_dict(data: Any) -> Optional[Dict[str, str]]: return data -def _deserialize_json_list(data: Any) -> Optional[List[Dict[str, Any]]]: +def _deserialize_json_list(data: Any) -> Optional[list[dict[str, Any]]]: """Deserialize a JSON array stored in the DB (``env_vars`` and friends). Returns ``None`` for empty / null / unparseable input. Accepts strings @@ -499,6 +691,21 @@ def _create_elicitation_callback(): class MCPServerManager: _STDIO_ENV_TEMPLATE_PATTERN = re.compile(r"^\$\{(X-[^}]+)\}$") + @staticmethod + def _explicit_oauth2_flow( + oauth2_flow: Optional[str], + ) -> Optional[Literal["client_credentials", "authorization_code"]]: + """DB rows persist their flow (write-time stamps plus the startup backfill) and + config servers must declare it (validated at load), so both builds read the + value verbatim: unknown or null resolves to None, which + ``needs_user_oauth_token`` already treats as interactive. Field-shape inference + survives only in the request-time security helpers (``effective_oauth2_flow`` / + ``resolve_oauth2_flow_for_request``). + """ + if oauth2_flow in ("client_credentials", "authorization_code"): + return cast(Literal["client_credentials", "authorization_code"], oauth2_flow) + return None + @staticmethod def _resolve_oauth2_flow( *, @@ -509,11 +716,18 @@ class MCPServerManager: client_id: Optional[str], client_secret: Optional[str], ) -> Optional[Literal["client_credentials", "authorization_code"]]: - """Infer oauth2_flow for legacy records that omit the field. + """Infer oauth2_flow from field shape when the value is omitted. - DB rows created before oauth2_flow support may have OAuth2 client - credentials + token_url but a null oauth2_flow. Treat these as M2M, - unless authorization_url is present (interactive OAuth). + SECURITY-SENSITIVE: this is the shape-inference engine both request-time security + helpers delegate to, so it is what decides M2M-vs-interactive for an unstamped row. + Always access it through ``effective_oauth2_flow`` (boolean/enum decisions) or + ``resolve_oauth2_flow_for_request`` (the egress object backstop), which are the single + choke points for request-time resolution; do not call it directly from security sites + and do not weaken its M2M-shape branch without accounting for those callers. DB rows + are stamped at write time and by the startup backfill, config servers must declare + oauth2_flow (validated at load), and both builds read the value verbatim via + ``_explicit_oauth2_flow``. Delete this whole request-time layer only once the backstop + warning stays silent in production. """ if oauth2_flow in ("client_credentials", "authorization_code"): return cast(Literal["client_credentials", "authorization_code"], oauth2_flow) @@ -528,12 +742,79 @@ class MCPServerManager: return "client_credentials" return None - def __init__(self, cred_provider: Optional[UpstreamCredentialProvider] = None): - self._cred_provider = cred_provider or UpstreamCredentialProvider( - oauth_token_store=LazyPerUserOAuthTokenStore(self.get_mcp_server_by_id) + @staticmethod + def effective_oauth2_flow(server: "MCPServer") -> Optional[Literal["client_credentials", "authorization_code"]]: + """The oauth2_flow a security decision must use for ``server`` this request. + + Column-first, shape-fallback: a stamped row returns its explicit value; an + unstamped (null) row whose fields carry the M2M shape resolves to + ``client_credentials`` so it is treated as M2M and fails closed. Every + security-sensitive reader (anonymous-delegate allowlist and gate, egress flow + resolution) goes through this one helper rather than reading the bare + ``has_client_credentials`` column, which is unreliable for null rows. + """ + return MCPServerManager._resolve_oauth2_flow( + auth_type=server.auth_type, + oauth2_flow=server.oauth2_flow, + token_url=server.token_url, + authorization_url=server.authorization_url, + client_id=server.client_id, + client_secret=server.client_secret, ) - self.registry: Dict[str, MCPServer] = {} - self.config_mcp_servers: Dict[str, MCPServer] = {} + + @staticmethod + def resolve_oauth2_flow_for_request(server: "MCPServer") -> "MCPServer": + """Return ``server`` with its effective oauth2_flow applied, for egress paths. + + A stamped row is returned unchanged (its effective flow equals the stored value). + An unstamped M2M-shape row is returned as a per-request copy carrying + ``oauth2_flow=client_credentials`` so downstream ``has_client_credentials`` / + ``needs_user_oauth_token`` compute correctly and the stored client credentials are + used instead of forwarding the caller's Authorization. Use this at every point that + resolves an allowed server id into an ``MCPServer`` for a tool call or listing. + """ + effective = MCPServerManager.effective_oauth2_flow(server) + if effective is None or effective == server.oauth2_flow: + return server + verbose_logger.warning( + "MCP server %s has no persisted oauth2_flow but matches the %s shape; using the " + "inferred flow for this request. The startup backfill leaves this ambiguous M2M " + "shape unstamped on purpose, so it will NOT self-heal: set oauth2_flow explicitly " + "in the dashboard or via PUT /v1/mcp/server (client_credentials for M2M, or " + "authorization_code after an interactive sign-in).", + server.server_id, + effective, + ) + return server.model_copy(update={"oauth2_flow": effective}) + + @staticmethod + def _obo_needs_endpoint_discovery( + auth_type: Optional[MCPAuthType], + token_exchange_endpoint: Optional[str], + token_url: Optional[str], + ) -> bool: + """An ``oauth2_token_exchange`` server with no configured token endpoint can have it + discovered (RFC 9728 -> RFC 8414) the same way the ``oauth2`` flow already does; an explicitly + configured ``token_exchange_endpoint``/``token_url`` wins and skips the discovery round-trip. + """ + return auth_type == MCPAuth.oauth2_token_exchange and not (token_exchange_endpoint or token_url) + + def __init__( + self, + cred_provider: Optional[UpstreamCredentialProvider] = None, + per_user_oauth_token_store: Optional[InvalidatableOAuthTokenStore] = None, + per_user_token_cache: Optional[MCPPerUserTokenCache] = None, + ): + self._per_user_oauth_token_store = per_user_oauth_token_store or LazyPerUserOAuthTokenStore( + self.get_mcp_server_by_id + ) + self._per_user_token_cache = per_user_token_cache or mcp_per_user_token_cache + self._cred_provider = cred_provider or UpstreamCredentialProvider( + oauth_token_store=self._per_user_oauth_token_store, + token_exchanger=build_token_exchanger(), + ) + self.registry: dict[str, MCPServer] = {} + self.config_mcp_servers: dict[str, MCPServer] = {} """ eg. [ @@ -550,17 +831,24 @@ class MCPServerManager: ] """ - self.tool_name_to_mcp_server_name_mapping: Dict[str, str] = {} + # Per-server outbound tool-call concurrency limiters, lazily created from + # each server's max_concurrent_requests. Keyed by server_id so the cap + # survives the registry atomic-swap on config reload; a missing key means + # the server has no configured limit. The limit is cached alongside the + # semaphore so an edited limit rebuilds it instead of keeping the old cap + # until restart. + self._server_call_semaphores: dict[str, tuple[int, asyncio.Semaphore]] = {} + self.tool_name_to_mcp_server_name_mapping: dict[str, str] = {} """ { "gmail_send_email": "zapier_mcp_server", } """ - self._upstream_initialize_instructions_by_server_id: Dict[str, str] = {} + self._upstream_initialize_instructions_by_server_id: dict[str, str] = {} # Per-server monotonic timestamp of last upstream prefetch attempt (success, # empty result, or failure). Used to throttle re-probes for servers that do # not return instructions, and to apply a short cooldown after failures. - self._upstream_initialize_instructions_probed_at: Dict[str, float] = {} + self._upstream_initialize_instructions_probed_at: dict[str, float] = {} def _remember_upstream_initialize_instructions(self, server: MCPServer, client: MCPClient) -> None: raw = getattr(client, "_last_initialize_instructions", None) @@ -616,7 +904,7 @@ class MCPServerManager: user_api_key_auth=None, raise_on_missing=False, ) - extra_headers: Optional[Dict[str, str]] = dict(resolved_static_headers) if resolved_static_headers else None + extra_headers: Optional[dict[str, str]] = dict(resolved_static_headers) if resolved_static_headers else None client = await self._create_mcp_client( server=server, mcp_auth_header=None, @@ -636,7 +924,7 @@ class MCPServerManager: e, ) - def get_registry(self) -> Dict[str, MCPServer]: + def get_registry(self) -> dict[str, MCPServer]: """ Get the registered MCP Servers from the registry and union with the config MCP Servers """ @@ -644,8 +932,8 @@ class MCPServerManager: async def load_servers_from_config( self, - mcp_servers_config: Dict[str, Any], - mcp_aliases: Optional[Dict[str, str]] = None, + mcp_servers_config: dict[str, Any], + mcp_aliases: Optional[dict[str, str]] = None, ): """ Load the MCP Servers from the config @@ -663,7 +951,7 @@ class MCPServerManager: for server_name, server_config in mcp_servers_config.items(): validate_mcp_server_name(server_name) - _mcp_info: Dict[str, Any] = server_config.get("mcp_info", None) or {} + _mcp_info: dict[str, Any] = server_config.get("mcp_info", None) or {} # Preserve all custom fields from config while setting defaults for core fields mcp_info: MCPInfo = _mcp_info.copy() # Set default values for core fields if not present @@ -711,14 +999,27 @@ class MCPServerManager: ) auth_type = server_config.get("auth_type", None) - if server_url and auth_type is not None and auth_type == MCPAuth.oauth2: + if server_url and ( + auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES + or self._obo_needs_endpoint_discovery( + auth_type, + server_config.get("token_exchange_endpoint"), + server_config.get("token_url"), + ) + ): mcp_oauth_metadata = await self._descovery_metadata( server_url=server_url, + allow_origin_fallback=auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES, ) else: mcp_oauth_metadata = None - resolved_scopes = server_config.get("scopes") or (mcp_oauth_metadata.scopes if mcp_oauth_metadata else None) + # Filter blank scopes (e.g. YAML ``scopes: [""]``) the same way the DB-build path does, so + # an all-blank list normalizes to None rather than a ``("",)`` tuple that skips the + # entra_obo fail-closed scope precondition and POSTs an empty scope to the IdP. + resolved_scopes = self._extract_scopes(server_config.get("scopes")) or ( + mcp_oauth_metadata.scopes if mcp_oauth_metadata else None + ) resolved_authorization_url = server_config.get("authorization_url") or ( mcp_oauth_metadata.authorization_url if mcp_oauth_metadata else None ) @@ -729,6 +1030,38 @@ class MCPServerManager: mcp_oauth_metadata.registration_url if mcp_oauth_metadata else None ) + config_oauth2_flow = server_config.get("oauth2_flow", None) + if auth_type == MCPAuth.oauth2 and config_oauth2_flow not in ( + "client_credentials", + "authorization_code", + ): + raise ValueError( + f"Invalid config for MCP server '{server_name or server_id}': auth_type oauth2 " + f"requires an explicit oauth2_flow (got {config_oauth2_flow!r}). Set " + "oauth2_flow: client_credentials for machine-to-machine servers (the proxy mints " + "a shared token at token_url using client_id/client_secret, no user interaction) " + "or oauth2_flow: authorization_code for interactive servers (per-user tokens via " + "browser sign-in, including delegate_auth_to_upstream)." + ) + + config_dcr_bridge = server_config.get("dcr_bridge", None) + if config_dcr_bridge is not None and not isinstance(config_dcr_bridge, bool): + raise ValueError( + f"Invalid config for MCP server '{server_name or server_id}': dcr_bridge " + f"must be a boolean (got {config_dcr_bridge!r})." + ) + if config_dcr_bridge and auth_type not in ( + MCPAuth.true_passthrough, + MCPAuth.oauth_delegate, + ): + raise ValueError( + f"Invalid config for MCP server '{server_name or server_id}': dcr_bridge is only " + f"supported for auth_type true_passthrough or oauth_delegate (got {auth_type!r}). " + "The DCR bridge serves gateway-hosted OAuth discovery for the client-forwarded " + "token modes; interactive oauth2 servers already run the gateway " + "authorization-code flow." + ) + new_server = MCPServer( server_id=server_id, name=name_for_prefix, @@ -742,14 +1075,7 @@ class MCPServerManager: # oauth specific fields client_id=server_config.get("client_id", None), client_secret=server_config.get("client_secret", None), - oauth2_flow=self._resolve_oauth2_flow( - auth_type=auth_type, - oauth2_flow=server_config.get("oauth2_flow", None), - token_url=resolved_token_url, - authorization_url=resolved_authorization_url, - client_id=server_config.get("client_id", None), - client_secret=server_config.get("client_secret", None), - ), + oauth2_flow=self._explicit_oauth2_flow(config_oauth2_flow), scopes=resolved_scopes, authorization_url=resolved_authorization_url, token_url=resolved_token_url, @@ -771,6 +1097,7 @@ class MCPServerManager: available_on_public_internet=bool(server_config.get("available_on_public_internet", True)), delegate_auth_to_upstream=bool(server_config.get("delegate_auth_to_upstream", False)), oauth_passthrough=bool(server_config.get("oauth_passthrough", False)), + dcr_bridge=config_dcr_bridge, # AWS SigV4 fields aws_access_key_id=server_config.get("aws_access_key_id", None), aws_secret_access_key=server_config.get("aws_secret_access_key", None), @@ -785,11 +1112,13 @@ class MCPServerManager: audience=server_config.get("audience", None), subject_token_type=server_config.get( "subject_token_type", - "urn:ietf:params:oauth:token-type:access_token", + DEFAULT_SUBJECT_TOKEN_TYPE, ), + token_exchange_profile=server_config.get("token_exchange_profile", "rfc8693"), allow_sampling=bool(server_config.get("allow_sampling", False)), allow_elicitation=bool(server_config.get("allow_elicitation", False)), timeout=server_config.get("timeout", None), + max_concurrent_requests=server_config.get("max_concurrent_requests", None), ) self._assign_unique_short_prefix(new_server) _warn_internal_delegate_pkce_if_applicable(new_server, source="config") @@ -851,7 +1180,7 @@ class MCPServerManager: server_prefix = get_server_prefix(server) # Build headers from server configuration - headers: Dict[str, str] = {} + headers: dict[str, str] = {} # Add authentication headers if configured if server.authentication_token: @@ -961,7 +1290,7 @@ class MCPServerManager: openapi_key_prefix = prefix_root + MCP_TOOL_PREFIX_SEPARATOR global_mcp_tool_registry.unregister_tools_with_prefix(openapi_key_prefix) - owned_raw: Set[str] = set() + owned_raw: set[str] = set() for p in iter_known_server_prefixes(server): if p: owned_raw.add(p) @@ -970,7 +1299,7 @@ class MCPServerManager: owned_normalized = {normalize_server_name(x) for x in owned_raw} - stale_mapping_keys: List[str] = [] + stale_mapping_keys: list[str] = [] for tool_name, mapped_server in list(self.tool_name_to_mcp_server_name_mapping.items()): if mapped_server in owned_raw: stale_mapping_keys.append(tool_name) @@ -998,7 +1327,7 @@ class MCPServerManager: mcp_server: LiteLLM_MCPServerTable, *, env_vars_are_encrypted: bool, - ) -> Optional[List[Dict[str, Any]]]: + ) -> Optional[list[dict[str, Any]]]: env_vars_list = _deserialize_json_list(getattr(mcp_server, "env_vars", None)) if env_vars_are_encrypted: from litellm.proxy._experimental.mcp_server.db import ( # noqa: PLC0415 @@ -1073,7 +1402,7 @@ class MCPServerManager: # AWS SigV4 credential fields aws_creds = self._extract_aws_credentials(credentials_dict, credentials_are_encrypted) - scopes: Optional[List[str]] = None + scopes: Optional[list[str]] = None if credentials_dict: scopes_value = credentials_dict.get("scopes") if scopes_value is not None: @@ -1090,9 +1419,20 @@ class MCPServerManager: auth_type = cast(MCPAuthType, mcp_server.auth_type) server_url = mcp_server.url - needs_discovery = bool(server_url) and auth_type == MCPAuth.oauth2 and not mcp_server.authorization_url + needs_discovery = bool(server_url) and ( + (auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES and not mcp_server.authorization_url) + or self._obo_needs_endpoint_discovery( + auth_type, + mcp_server.token_exchange_endpoint + or (credentials_dict.get("token_exchange_endpoint") if credentials_dict else None), + mcp_server.token_url, + ) + ) mcp_oauth_metadata = ( - await self._descovery_metadata(server_url=server_url) # type: ignore[arg-type] + await self._descovery_metadata( + server_url=server_url, # type: ignore[arg-type] + allow_origin_fallback=auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES, + ) if needs_discovery else None ) @@ -1115,15 +1455,7 @@ class MCPServerManager: env_vars=env_vars_list, client_id=client_id_value or getattr(mcp_server, "client_id", None), client_secret=client_secret_value or getattr(mcp_server, "client_secret", None), - oauth2_flow=self._resolve_oauth2_flow( - auth_type=auth_type, - oauth2_flow=getattr(mcp_server, "oauth2_flow", None), - token_url=mcp_server.token_url or getattr(mcp_oauth_metadata, "token_url", None), - authorization_url=mcp_server.authorization_url - or getattr(mcp_oauth_metadata, "authorization_url", None), - client_id=client_id_value or getattr(mcp_server, "client_id", None), - client_secret=client_secret_value or getattr(mcp_server, "client_secret", None), - ), + oauth2_flow=self._explicit_oauth2_flow(getattr(mcp_server, "oauth2_flow", None)), scopes=resolved_scopes, authorization_url=mcp_server.authorization_url or getattr(mcp_oauth_metadata, "authorization_url", None), token_url=mcp_server.token_url or getattr(mcp_oauth_metadata, "token_url", None), @@ -1141,6 +1473,7 @@ class MCPServerManager: available_on_public_internet=bool(getattr(mcp_server, "available_on_public_internet", True)), delegate_auth_to_upstream=bool(getattr(mcp_server, "delegate_auth_to_upstream", False)), oauth_passthrough=bool(getattr(mcp_server, "oauth_passthrough", False)), + dcr_bridge=getattr(mcp_server, "dcr_bridge", None), created_at=getattr(mcp_server, "created_at", None), updated_at=getattr(mcp_server, "updated_at", None), tool_name_to_display_name=_deserialize_json_dict(getattr(mcp_server, "tool_name_to_display_name", None)), @@ -1158,16 +1491,64 @@ class MCPServerManager: aws_role_name=aws_creds.get("aws_role_name"), aws_session_name=aws_creds.get("aws_session_name"), instructions=mcp_server.instructions, - # Token Exchange (OBO) fields — read from credentials JSON blob - token_exchange_endpoint=(credentials_dict.get("token_exchange_endpoint") if credentials_dict else None), - audience=(credentials_dict.get("audience") if credentials_dict else None), - subject_token_type=(credentials_dict.get("subject_token_type") if credentials_dict else None) - or "urn:ietf:params:oauth:token-type:access_token", + # Token exchange (OBO) fields: dedicated columns, with the credentials blob as a + # back-compat fallback for servers persisted before the columns existed. + token_exchange_endpoint=mcp_server.token_exchange_endpoint + or (credentials_dict.get("token_exchange_endpoint") if credentials_dict else None), + audience=mcp_server.audience or (credentials_dict.get("audience") if credentials_dict else None), + subject_token_type=mcp_server.subject_token_type + or (credentials_dict.get("subject_token_type") if credentials_dict else None) + or DEFAULT_SUBJECT_TOKEN_TYPE, + token_exchange_profile=mcp_server.token_exchange_profile + or (credentials_dict.get("token_exchange_profile") if credentials_dict else None) + or "rfc8693", timeout=getattr(mcp_server, "timeout", None), + max_concurrent_requests=getattr(mcp_server, "max_concurrent_requests", None), ) _warn_internal_delegate_pkce_if_applicable(new_server, source="database") + await self._persist_discovered_obo_token_url( + server_id=mcp_server.server_id, + auth_type=auth_type, + existing_token_url=mcp_server.token_url, + discovered_token_url=new_server.token_url, + ) return new_server + async def _persist_discovered_obo_token_url( + self, + *, + server_id: str, + auth_type: Optional[MCPAuthType], + existing_token_url: Optional[str], + discovered_token_url: Optional[str], + ) -> None: + """Write a freshly discovered OBO token endpoint back onto the DB row. + + ``build_mcp_server_from_table`` resolves ``token_url`` via RFC 9728 -> RFC 8414 for an + ``oauth2_token_exchange`` server that has none configured, but that resolved value otherwise + lives only on the returned in-memory object; the row keeps ``token_url=None`` so every rebuild + re-runs discovery, and a transient upstream outage during a rebuild leaves the server with no + endpoint until discovery next succeeds. Persisting it makes ``_obo_needs_endpoint_discovery`` + return False on the next build. Fires at most once per server (skipped once the row has a + value), and is best-effort: a write failure just means discovery runs again next time. + """ + if auth_type != MCPAuth.oauth2_token_exchange: + return + if existing_token_url or not discovered_token_url: + return + from litellm.proxy.proxy_server import prisma_client # noqa: PLC0415 + + if prisma_client is None: + return + try: + await MCPServerRepository(prisma_client).table.update( + where={"server_id": server_id}, + data={"token_url": discovered_token_url}, + ) + verbose_logger.debug("Persisted discovered OBO token_url for MCP server %s", server_id) + except Exception as exc: # noqa: BLE001 - best-effort; a failed write re-discovers next build + verbose_logger.warning("Failed to persist discovered OBO token_url for MCP server %s: %s", server_id, exc) + async def _maybe_register_openapi_tools(self, server: MCPServer, *, initialize_mapping: bool = True): """Register OpenAPI tools if the server has a spec_path configured.""" if server.spec_path: @@ -1235,18 +1616,79 @@ class MCPServerManager: verbose_logger.debug(f"Failed to udpate MCP server: {str(e)}") raise e - def get_all_mcp_server_ids(self) -> Set[str]: + def get_all_mcp_server_ids(self) -> set[str]: """ Get all MCP server IDs """ all_servers = list(self.get_registry().values()) return {server.server_id for server in all_servers} - def get_allow_all_keys_server_ids(self) -> List[str]: + def get_allow_all_keys_server_ids(self) -> list[str]: """Return server IDs that bypass per-key restrictions.""" return [server.server_id for server in self.get_registry().values() if server.allow_all_keys is True] - async def get_allowed_mcp_servers(self, user_api_key_auth: Optional[UserAPIKeyAuth] = None) -> List[str]: + @staticmethod + def get_byom_submitted_servers_cache_key(user_id: str) -> str: + return f"byom_submitted_servers:{user_id}" + + async def invalidate_byom_submitted_servers_cache(self, user_id: str | None) -> None: + if not user_id: + return + try: + from litellm.proxy.proxy_server import user_api_key_cache + + await user_api_key_cache.async_delete_cache(key=self.get_byom_submitted_servers_cache_key(user_id)) + except Exception as e: # noqa: BLE001 + verbose_logger.warning(f"Failed to invalidate BYOM submitted MCP server cache: {str(e)}") + + async def _get_active_submitted_mcp_server_ids_for_user( + self, user_api_key_auth: UserAPIKeyAuth | None + ) -> list[str]: + submitter_user_id = getattr(user_api_key_auth, "user_id", None) if user_api_key_auth else None + if not submitter_user_id: + return [] + + try: + from litellm.proxy._experimental.mcp_server.db import ( # noqa: PLC0415 + get_active_submitted_mcp_server_ids_for_user, + ) + from litellm.proxy.proxy_server import prisma_client, user_api_key_cache + except Exception as e: # noqa: BLE001 + verbose_logger.warning(f"Failed to load BYOM submitted MCP server cache dependencies: {str(e)}") + return [] + + byom_cache_key = self.get_byom_submitted_servers_cache_key(submitter_user_id) + submitted_server_ids: list[str] | None = None + try: + cached_submitted_server_ids = await user_api_key_cache.async_get_cache(key=byom_cache_key) + if cached_submitted_server_ids is not None: + submitted_server_ids = cast(list[str], cached_submitted_server_ids) + except Exception as e: # noqa: BLE001 + verbose_logger.warning(f"Failed to read BYOM submitted MCP server cache: {str(e)}") + + if submitted_server_ids is None: + if prisma_client is None: + submitted_server_ids = [] + else: + try: + submitted_server_ids = await get_active_submitted_mcp_server_ids_for_user( + prisma_client, submitter_user_id + ) + except Exception as e: # noqa: BLE001 + verbose_logger.warning(f"Failed to read BYOM submitted MCP servers from database: {str(e)}") + submitted_server_ids = [] + try: + await user_api_key_cache.async_set_cache( + key=byom_cache_key, + value=submitted_server_ids, + ttl=60, + ) + except Exception as e: # noqa: BLE001 + verbose_logger.warning(f"Failed to write BYOM submitted MCP server cache: {str(e)}") + + return [server_id for server_id in submitted_server_ids if self.get_mcp_server_by_id(server_id) is not None] + + async def get_allowed_mcp_servers(self, user_api_key_auth: Optional[UserAPIKeyAuth] = None) -> list[str]: """ Get the allowed MCP Servers for the user. @@ -1259,25 +1701,30 @@ class MCPServerManager: allow_all_server_ids = self.get_allow_all_keys_server_ids() + # The key explicitly opted out of every MCP server. Return zero before + # layering on allow_all_keys or submitted servers so the opt-out is absolute. + key_object_permission = user_api_key_auth.object_permission if user_api_key_auth else None + if key_object_permission is not None and ( + SpecialMCPServerNames.no_mcp_servers.value in (key_object_permission.mcp_servers or []) + ): + return [] + + # Check if object_permission.mcp_servers is explicitly set (not None, empty list is valid) + has_explicit_object_permission = key_object_permission is not None and ( + key_object_permission.mcp_servers is not None + ) + if has_explicit_object_permission: + verbose_logger.debug(f"Object permission mcp_servers explicitly set: {key_object_permission.mcp_servers}") + + # BYOM creator visibility never widens a key that was explicitly scoped: + # only keys without their own mcp_servers list get submitted servers unioned in. + submitted_server_ids = ( + [] + if has_explicit_object_permission + else await self._get_active_submitted_mcp_server_ids_for_user(user_api_key_auth) + ) + try: - # The key explicitly opted out of every MCP server. Return zero before - # layering on allow_all_keys servers so the opt-out is absolute. - key_object_permission = user_api_key_auth.object_permission if user_api_key_auth else None - if key_object_permission is not None and ( - SpecialMCPServerNames.no_mcp_servers.value in (key_object_permission.mcp_servers or []) - ): - return [] - - # Check if object_permission.mcp_servers is explicitly set - has_explicit_object_permission = False - if user_api_key_auth and user_api_key_auth.object_permission: - # Check if mcp_servers is explicitly set (not None, empty list is valid) - if user_api_key_auth.object_permission.mcp_servers is not None: - has_explicit_object_permission = True - verbose_logger.debug( - f"Object permission mcp_servers explicitly set: {user_api_key_auth.object_permission.mcp_servers}" - ) - # If admin but NO explicit object permission, get all servers if user_api_key_auth and _user_has_admin_view(user_api_key_auth) and not has_explicit_object_permission: verbose_logger.debug("Admin user without explicit object_permission - returning all servers") @@ -1299,6 +1746,7 @@ class MCPServerManager: in_toolset_scope = _mcp_active_toolset_id.get() is not None if not in_toolset_scope: combined_servers.update(allow_all_server_ids) + combined_servers.update(submitted_server_ids) # For anonymous callers (no user_id, no role), also surface any # servers the operator has opted into upstream-delegated auth. @@ -1316,12 +1764,18 @@ class MCPServerManager: delegate_server_ids = [ server.server_id for server in self.get_registry().values() - if getattr(server, "auth_type", None) == MCPAuth.oauth2 - and getattr(server, "delegate_auth_to_upstream", False) is True - # M2M servers must not be exposed anonymously: an - # unauthenticated caller would get LiteLLM to proxy tool - # calls using its stored client_credentials. - and not server.has_client_credentials + if ( + getattr(server, "auth_type", None) == MCPAuth.oauth2 + and getattr(server, "delegate_auth_to_upstream", False) is True + # M2M servers must not be exposed anonymously: an + # unauthenticated caller would get LiteLLM to proxy tool + # calls using its stored client_credentials. Resolve the flow + # rather than reading has_client_credentials so an unstamped + # M2M-shape row (null column, verbatim-read as non-M2M) still + # fails closed here, matching the anonymous-delegate auth gate. + and MCPServerManager.effective_oauth2_flow(server) != "client_credentials" + ) + or getattr(server, "auth_type", None) == MCPAuth.true_passthrough ] combined_servers.update(delegate_server_ids) @@ -1331,14 +1785,14 @@ class MCPServerManager: except Exception: # noqa: BLE001 verbose_logger.exception( "Failed to get allowed MCP servers; team-level object_permission " - "grants may be dropped. Falling back to global servers only." + "grants may be dropped. Falling back to global and submitted servers." ) - return allow_all_server_ids + return list(dict.fromkeys(allow_all_server_ids + submitted_server_ids)) async def resolve_toolset_tool_permissions( self, - toolset_ids: List[str], - ) -> Dict[str, List[str]]: + toolset_ids: list[str], + ) -> dict[str, list[str]]: """ Resolve a list of toolset IDs into a mcp_tool_permissions dict. @@ -1360,7 +1814,7 @@ class MCPServerManager: try: toolsets = await list_mcp_toolsets(prisma_client, toolset_ids=toolset_ids) - tool_permissions: Dict[str, List[str]] = {} + tool_permissions: dict[str, list[str]] = {} for toolset in toolsets: for tool in toolset.tools: raw_name = tool["tool_name"] @@ -1453,7 +1907,7 @@ class MCPServerManager: ) return toolset - def filter_server_ids_by_ip(self, server_ids: List[str], client_ip: Optional[str]) -> List[str]: + def filter_server_ids_by_ip(self, server_ids: list[str], client_ip: Optional[str]) -> list[str]: """ Filter server IDs by client IP — external callers only see public servers. @@ -1463,8 +1917,8 @@ class MCPServerManager: return filtered def filter_server_ids_by_ip_with_info( - self, server_ids: List[str], client_ip: Optional[str] - ) -> Tuple[List[str], int]: + self, server_ids: list[str], client_ip: Optional[str] + ) -> tuple[list[str], int]: """ Filter server IDs by client IP — external callers only see public servers. @@ -1484,7 +1938,7 @@ class MCPServerManager: blocked += 1 return allowed, blocked - async def get_tools_for_server(self, server_id: str) -> List[MCPTool]: + async def get_tools_for_server(self, server_id: str) -> list[MCPTool]: """ Get the tools for a given server """ @@ -1502,8 +1956,8 @@ class MCPServerManager: self, user_api_key_auth: Optional[UserAPIKeyAuth] = None, mcp_auth_header: Optional[str] = None, - mcp_server_auth_headers: Optional[Dict[str, Union[str, Dict[str, str]]]] = None, - ) -> List[MCPTool]: + mcp_server_auth_headers: Optional[dict[str, Union[str, dict[str, str]]]] = None, + ) -> list[MCPTool]: """ List all tools available across all MCP Servers. @@ -1520,7 +1974,7 @@ class MCPServerManager: verbose_logger.debug("SERVER MANAGER LISTING TOOLS") - async def _fetch_server_tools(server_id: str) -> List[MCPTool]: + async def _fetch_server_tools(server_id: str) -> list[MCPTool]: """Fetch tools from a single server with error handling.""" server = self.get_mcp_server_by_id(server_id) if server is None: @@ -1528,7 +1982,7 @@ class MCPServerManager: return [] # Get server-specific auth header if available - server_auth_header: Optional[Union[str, Dict[str, str]]] = None + server_auth_header: Optional[Union[str, dict[str, str]]] = None if mcp_server_auth_headers: from litellm.proxy._experimental.mcp_server.utils import ( lookup_mcp_server_auth_in_headers, @@ -1562,7 +2016,7 @@ class MCPServerManager: results = await asyncio.gather(*tasks) # Flatten results into single list - list_tools_result: List[MCPTool] = [tool for tools in results for tool in tools] + list_tools_result: list[MCPTool] = [tool for tools in results for tool in tools] verbose_logger.info(f"Successfully fetched {len(list_tools_result)} tools total from all servers") return list_tools_result @@ -1572,8 +2026,8 @@ class MCPServerManager: ######################################################### @staticmethod def _extract_bearer_token( - oauth2_headers: Optional[Dict[str, str]], - raw_headers: Optional[Dict[str, str]], + oauth2_headers: Optional[dict[str, str]], + raw_headers: Optional[dict[str, str]], ) -> Optional[str]: """Extract the bare Bearer token from oauth2_headers or raw_headers. @@ -1593,17 +2047,32 @@ class MCPServerManager: return auth_value return None + def _obo_subject_token( + self, + server: MCPServer, + raw_headers: Optional[dict[str, str]], + ) -> Optional[str]: + """The caller's bearer as the token_exchange (OBO) subject token, for that mode only. + + Prompts/resources discovery and reads on a token_exchange server must exchange the caller's + token like the tools paths do, not connect with no credential. Other modes never read the + inbound bearer, so return None to avoid forwarding it. + """ + if server.auth_type != MCPAuth.oauth2_token_exchange: + return None + return self._extract_bearer_token(None, raw_headers) + def _build_stdio_env( self, server: MCPServer, - raw_headers: Optional[Dict[str, str]] = None, - ) -> Optional[Dict[str, str]]: + raw_headers: Optional[dict[str, str]] = None, + ) -> Optional[dict[str, str]]: """Resolve stdio env values, supporting header-driven placeholders.""" if server.transport != MCPTransport.stdio or not server.env: return None - resolved_env: Dict[str, str] = {} + resolved_env: dict[str, str] = {} normalized_headers = {k.lower(): v for k, v in (raw_headers or {}).items()} for env_key, env_value in server.env.items(): @@ -1645,7 +2114,7 @@ class MCPServerManager: user_api_key_auth: Optional[UserAPIKeyAuth], *, raise_on_missing: bool = True, - ) -> Optional[Dict[str, str]]: + ) -> Optional[dict[str, str]]: """Return server.static_headers with ``${NAME}`` interpolated. Globals come from ``server.env_vars`` entries with ``scope=="global"``. @@ -1688,7 +2157,7 @@ class MCPServerManager: referenced_user_vars = referenced & user_var_names required_user_vars = {name for name in referenced_user_vars if name not in global_values} - user_values: Dict[str, str] = {} + user_values: dict[str, str] = {} if required_user_vars: try: user_values = await self._load_user_env_vars(server, user_api_key_auth) @@ -1727,7 +2196,7 @@ class MCPServerManager: # admin globals win, so a stale row from when a var was user-scoped can # never override the global value the admin set after switching it. scoped_user_values = {name: value for name, value in user_values.items() if name in user_var_names} - merged_vars: Dict[str, str] = {**scoped_user_values, **global_values} + merged_vars: dict[str, str] = {**scoped_user_values, **global_values} if not static_headers: return static_headers return interpolate_headers(static_headers, merged_vars) @@ -1738,7 +2207,7 @@ class MCPServerManager: user_api_key_auth: Optional[UserAPIKeyAuth], *, force_refresh: bool = False, - ) -> Dict[str, str]: + ) -> dict[str, str]: """Look up the calling user's env var values for ``server``. Returns an empty dict when no user is available. Results are cached in a @@ -1783,12 +2252,99 @@ class MCPServerManager: _write_user_env_vars_cache(user_id, server.server_id, values) return values + async def _resolve_v2_auth( + self, + *, + server: MCPServer, + spec: ServerSpec, + provider: UpstreamCredentialProvider, + subject_token: Optional[str], + user_api_key_auth: Optional[UserAPIKeyAuth], + extra_headers: Optional[dict[str, str]], + ) -> tuple[Optional[httpx.Auth], Optional[dict[str, str]]]: + """Resolve a v2-owned server's upstream credential into ``(resolved_auth, extra_headers)``. + + On a missing/rejected per-user credential this raises the mode's discovery challenge + (authorization_code's browser-OAuth 401, token_exchange's RFC 9728 challenge) or maps any + other ``CredError`` onto its public HTTP status; it never returns an error as a value. + """ + match await provider.resolve_credentials(to_subject(user_api_key_auth, subject_token), spec): + case Ok(auth): + # NoOpAuth has no header_name and so never conflicts. + header_name = getattr(auth, "header_name", None) + conflicts = bool( + header_name and extra_headers and any(key.lower() == header_name.lower() for key in extra_headers) + ) + if not conflicts: + return auth, extra_headers + if isinstance(spec.config, (TokenExchangeConfig, AuthorizationCodeConfig)): + # The resolver owns the per-user credential here (token_exchange's exchanged + # token, authorization_code's stored token). It is authoritative: a guardrail such + # as MCPJWTSigner, static_headers, or any other injected Authorization must NOT + # shadow it (otherwise the upstream gets e.g. the signer's JWT instead of the + # exchanged token and rejects it). Drop the conflicting header so the resolved + # token reaches upstream. + return auth, _without_authorization(extra_headers) + # Other modes: an Authorization already supplied via extra_headers (a forwarded caller + # header or static_headers) is intentional and wins; v1 applies those last. + return None, extra_headers + case Error(err): + if err.tag == "unauthorized" and isinstance(spec.config, AuthorizationCodeConfig): + # authorization_code's missing per-user token -> the per-server browser-OAuth + # challenge, built here where the full MCPServer is in hand. + raise_user_oauth_challenge(server, root_path=get_server_root_path()) + if err.tag == "unauthorized" and isinstance(spec.config, TokenExchangeConfig): + # token_exchange (OBO): a missing/rejected subject token -> the RFC 9728 challenge + # pointing at the IdP the client must SSO with to obtain one, rather than an opaque + # 401. No gateway-side browser flow. An IdP step-up rejection (Entra Conditional + # Access) threads its claims blob into the challenge for the client to satisfy. + raise_token_exchange_challenge( + server, + root_path=get_server_root_path(), + claims=err.unauthorized.claims, + ) + raise_public(err) + + async def preflight_token_exchange( + self, + server: MCPServer, + oauth2_headers: Optional[dict[str, str]], + user_api_key_auth: Optional[UserAPIKeyAuth], + ) -> None: + """Run the OBO exchange for a caller-supplied subject at the transport edge. + + Single-server routes call this before the MCP session opens, where an HTTP status and + ``WWW-Authenticate`` still reach the client. A rejected subject raises the RFC 9728 + challenge and any other ``CredError`` maps onto its public HTTP status, so an exchange + failure surfaces as a failure instead of the session continuing into an empty tool list. + A successful exchange is cached by the exchanger, so the session's list/call reuses it. + """ + if server.auth_type != MCPAuth.oauth2_token_exchange: + return + subject_token = self._extract_bearer_token(oauth2_headers, None) + if not subject_token: + return + spec = to_server_spec(server) + if spec is None or not isinstance(spec.config, TokenExchangeConfig): + return + match await self._cred_provider.resolve_credentials(to_subject(user_api_key_auth, subject_token), spec): + case Ok(_): + return + case Error(err): + if err.tag == "unauthorized": + raise_token_exchange_challenge( + server, + root_path=get_server_root_path(), + claims=err.unauthorized.claims, + ) + raise_public(err) + async def _create_mcp_client( self, server: MCPServer, - mcp_auth_header: Optional[Union[str, Dict[str, str]]] = None, - extra_headers: Optional[Dict[str, str]] = None, - stdio_env: Optional[Dict[str, str]] = None, + mcp_auth_header: Optional[Union[str, dict[str, str]]] = None, + extra_headers: Optional[dict[str, str]] = None, + stdio_env: Optional[dict[str, str]] = None, subject_token: Optional[str] = None, user_api_key_auth: Optional[UserAPIKeyAuth] = None, cred_provider: Optional[UpstreamCredentialProvider] = None, @@ -1817,11 +2373,18 @@ class MCPServerManager: spec = None if transport == MCPTransport.stdio else to_server_spec(server) provider = cred_provider or self._cred_provider # A caller-supplied per-request override (mcp_auth_header / x-mcp-*) defers to the v1 path - # so it wins - except for authorization_code, whose per-user token the v2 resolver owns. A - # caller must not be able to substitute another user's stored credential, so we keep the v2 - # spec and ignore the override there; the REST tools preview supplies its not-yet-persisted - # token through the resolver (cred_provider), never this path. - if spec is not None and mcp_auth_header and not isinstance(spec.config, AuthorizationCodeConfig): + # so it wins - except for the modes the v2 resolver owns per-caller (authorization_code's + # stored token, token_exchange's RFC 8693 minted token, and the passthrough modes' + # forwarded caller token). A caller must not be able to substitute another user's stored + # credential, nor silently disable the OBO exchange and forward an arbitrary bearer + # upstream, so we keep the v2 spec and ignore the override for these; the REST tools + # preview supplies its not-yet-persisted token through the resolver (cred_provider), + # never this path. + if ( + spec is not None + and mcp_auth_header + and not isinstance(spec.config, (AuthorizationCodeConfig, PassthroughConfig, TokenExchangeConfig)) + ): spec = None auth_value = ( await resolve_mcp_auth(server, mcp_auth_header, subject_token=subject_token) if spec is None else None @@ -1887,26 +2450,20 @@ class MCPServerManager: server_url = server.url or "" if spec is not None: - match await provider.resolve_credentials(to_subject(user_api_key_auth, subject_token), spec): - case Ok(auth): - resolved_auth = auth - # Do not override an Authorization already supplied via extra_headers - # (a guardrail hook such as the JWT signer, static_headers, or a - # forwarded caller header): v1 applies those last, so they win. NoOpAuth - # has no header_name and so never skips. - header_name = getattr(resolved_auth, "header_name", None) - if ( - header_name - and extra_headers - and any(key.lower() == header_name.lower() for key in extra_headers) - ): - resolved_auth = None - case Error(err): - if err.tag == "unauthorized": - # The arm signals a missing per-user token semantically; raise the - # per-server OAuth challenge here, where the full MCPServer is in hand. - raise_user_oauth_challenge(server) - raise_public(err) + inbound_token = subject_token + if isinstance(spec.config, PassthroughConfig): + inbound_token, extra_headers = _take_forwarded_authorization(extra_headers) + per_server_token = _passthrough_token_from_mcp_auth_header(mcp_auth_header) + if per_server_token is not None: + inbound_token = per_server_token + resolved_auth, extra_headers = await self._resolve_v2_auth( + server=server, + spec=spec, + provider=provider, + subject_token=inbound_token, + user_api_key_auth=user_api_key_auth, + extra_headers=extra_headers, + ) return MCPClient( server_url=server_url, transport_type=transport, @@ -1946,12 +2503,13 @@ class MCPServerManager: async def _get_tools_from_server( self, server: MCPServer, - mcp_auth_header: Optional[Union[str, Dict[str, str]]] = None, - extra_headers: Optional[Dict[str, str]] = None, + mcp_auth_header: Optional[Union[str, dict[str, str]]] = None, + extra_headers: Optional[dict[str, str]] = None, add_prefix: bool = True, - raw_headers: Optional[Dict[str, str]] = None, + raw_headers: Optional[dict[str, str]] = None, user_api_key_auth: Optional[UserAPIKeyAuth] = None, - ) -> List[MCPTool]: + oauth2_headers: Optional[dict[str, str]] = None, + ) -> list[MCPTool]: """ Helper method to get tools from a single MCP server with prefixed names. @@ -2024,11 +2582,21 @@ class MCPServerManager: stdio_env = self._build_stdio_env(server, raw_headers) + # token_exchange (OBO) discovery needs the caller's token too: list it with the user's own + # token (mirrors the call path), not v1's deleted client_credentials fallback. Other modes + # never read the inbound bearer, so leave subject_token None to avoid forwarding it. + subject_token = ( + self._extract_bearer_token(oauth2_headers, raw_headers) + if server.auth_type == MCPAuth.oauth2_token_exchange + else None + ) + client = await self._create_mcp_client( server=server, mcp_auth_header=mcp_auth_header, extra_headers=extra_headers, stdio_env=stdio_env, + subject_token=subject_token, user_api_key_auth=user_api_key_auth, ) @@ -2055,18 +2623,40 @@ class MCPServerManager: ] return tools else: - tools = await self._fetch_tools_with_timeout(client, server.name, server=server) + tools = await self._fetch_tools_with_timeout(client, server.name) self._remember_upstream_initialize_instructions(server, client) prefixed_or_original_tools = self._create_prefixed_tools(tools, server, add_prefix=add_prefix) return prefixed_or_original_tools - except MCPUpstreamAuthError: + except MCPUpstreamAuthError as upstream_auth_error: # Pass-through 401 must surface to single-server routes so the # client triggers the upstream OAuth flow. The multi-server # aggregator catches this explicitly to keep absorbing. + if server.is_dcr_bridge and upstream_auth_error.www_authenticate is not None: + raise MCPUpstreamAuthError( + status_code=upstream_auth_error.status_code, + www_authenticate=None, + server_name=upstream_auth_error.server_name, + ) from upstream_auth_error raise + except HTTPException as e: + # A v2 resolver auth challenge (token_exchange's RFC 9728 401, authorization_code's + # browser-OAuth 401, or a 403) is raised at client-build time, inside this try. Route it + # through the same MCPUpstreamAuthError channel as pass-through so single-server routes + # surface the challenge (the client re-authenticates) while the aggregator keeps absorbing. + # Non-auth HTTP errors stay absorbed so one misconfigured server can't blank the listing. + if e.status_code in (401, 403): + headers = e.headers or {} + challenge_header = headers.get("WWW-Authenticate") or headers.get("www-authenticate") + raise MCPUpstreamAuthError( + status_code=e.status_code, + www_authenticate=None if server.is_dcr_bridge else challenge_header, + server_name=server.name, + ) from e + verbose_logger.warning(f"Failed to get tools from server {server.name}: {str(e)}") + return [] except Exception as e: verbose_logger.warning(f"Failed to get tools from server {server.name}: {str(e)}") return [] @@ -2074,11 +2664,11 @@ class MCPServerManager: async def get_prompts_from_server( self, server: MCPServer, - mcp_auth_header: Optional[Union[str, Dict[str, str]]] = None, - extra_headers: Optional[Dict[str, str]] = None, + mcp_auth_header: Optional[Union[str, dict[str, str]]] = None, + extra_headers: Optional[dict[str, str]] = None, add_prefix: bool = True, - raw_headers: Optional[Dict[str, str]] = None, - ) -> List[Prompt]: + raw_headers: Optional[dict[str, str]] = None, + ) -> list[Prompt]: """ Helper method to get prompts from a single MCP server with prefixed names. @@ -2102,12 +2692,14 @@ class MCPServerManager: extra_headers.update(server.static_headers) stdio_env = self._build_stdio_env(server, raw_headers) + subject_token = self._obo_subject_token(server, raw_headers) client = await self._create_mcp_client( server=server, mcp_auth_header=mcp_auth_header, extra_headers=extra_headers, stdio_env=stdio_env, + subject_token=subject_token, ) prompts = await client.list_prompts() @@ -2123,11 +2715,11 @@ class MCPServerManager: async def get_resources_from_server( self, server: MCPServer, - mcp_auth_header: Optional[Union[str, Dict[str, str]]] = None, - extra_headers: Optional[Dict[str, str]] = None, + mcp_auth_header: Optional[Union[str, dict[str, str]]] = None, + extra_headers: Optional[dict[str, str]] = None, add_prefix: bool = True, - raw_headers: Optional[Dict[str, str]] = None, - ) -> List[Resource]: + raw_headers: Optional[dict[str, str]] = None, + ) -> list[Resource]: """Fetch available resources from a single MCP server.""" verbose_logger.debug(f"Connecting to url: {server.url}") @@ -2142,12 +2734,14 @@ class MCPServerManager: extra_headers.update(server.static_headers) stdio_env = self._build_stdio_env(server, raw_headers) + subject_token = self._obo_subject_token(server, raw_headers) client = await self._create_mcp_client( server=server, mcp_auth_header=mcp_auth_header, extra_headers=extra_headers, stdio_env=stdio_env, + subject_token=subject_token, ) resources = await client.list_resources() @@ -2163,11 +2757,11 @@ class MCPServerManager: async def get_resource_templates_from_server( self, server: MCPServer, - mcp_auth_header: Optional[Union[str, Dict[str, str]]] = None, - extra_headers: Optional[Dict[str, str]] = None, + mcp_auth_header: Optional[Union[str, dict[str, str]]] = None, + extra_headers: Optional[dict[str, str]] = None, add_prefix: bool = True, - raw_headers: Optional[Dict[str, str]] = None, - ) -> List[ResourceTemplate]: + raw_headers: Optional[dict[str, str]] = None, + ) -> list[ResourceTemplate]: """Fetch available resource templates from a single MCP server.""" verbose_logger.debug(f"Connecting to url: {server.url}") @@ -2182,12 +2776,14 @@ class MCPServerManager: extra_headers.update(server.static_headers) stdio_env = self._build_stdio_env(server, raw_headers) + subject_token = self._obo_subject_token(server, raw_headers) client = await self._create_mcp_client( server=server, mcp_auth_header=mcp_auth_header, extra_headers=extra_headers, stdio_env=stdio_env, + subject_token=subject_token, ) resource_templates = await client.list_resource_templates() @@ -2206,9 +2802,9 @@ class MCPServerManager: self, server: MCPServer, url: AnyUrl, - mcp_auth_header: Optional[Union[str, Dict[str, str]]] = None, - extra_headers: Optional[Dict[str, str]] = None, - raw_headers: Optional[Dict[str, str]] = None, + mcp_auth_header: Optional[Union[str, dict[str, str]]] = None, + extra_headers: Optional[dict[str, str]] = None, + raw_headers: Optional[dict[str, str]] = None, ) -> ReadResourceResult: """Read resource contents from a specific MCP server.""" @@ -2221,12 +2817,14 @@ class MCPServerManager: extra_headers.update(server.static_headers) stdio_env = self._build_stdio_env(server, raw_headers) + subject_token = self._obo_subject_token(server, raw_headers) client = await self._create_mcp_client( server=server, mcp_auth_header=mcp_auth_header, extra_headers=extra_headers, stdio_env=stdio_env, + subject_token=subject_token, ) return await client.read_resource(url) @@ -2235,10 +2833,10 @@ class MCPServerManager: self, server: MCPServer, prompt_name: str, - arguments: Optional[Dict[str, Any]] = None, - mcp_auth_header: Optional[Union[str, Dict[str, str]]] = None, - extra_headers: Optional[Dict[str, str]] = None, - raw_headers: Optional[Dict[str, str]] = None, + arguments: Optional[dict[str, Any]] = None, + mcp_auth_header: Optional[Union[str, dict[str, str]]] = None, + extra_headers: Optional[dict[str, str]] = None, + raw_headers: Optional[dict[str, str]] = None, ) -> GetPromptResult: """Fetch a specific prompt definition from a single MCP server.""" @@ -2251,12 +2849,14 @@ class MCPServerManager: extra_headers.update(server.static_headers) stdio_env = self._build_stdio_env(server, raw_headers) + subject_token = self._obo_subject_token(server, raw_headers) client = await self._create_mcp_client( server=server, mcp_auth_header=mcp_auth_header, extra_headers=extra_headers, stdio_env=stdio_env, + subject_token=subject_token, ) get_prompt_request_params = GetPromptRequestParams( @@ -2309,8 +2909,17 @@ class MCPServerManager: async def _descovery_metadata( self, server_url: str, + *, + allow_origin_fallback: bool = True, ) -> Optional[MCPOAuthMetadata]: - """Discover OAuth metadata by following RFC 9728 (protected resource metadata discovery).""" + """Discover OAuth metadata by following RFC 9728 (protected resource metadata discovery). + + ``allow_origin_fallback`` controls the last-resort guess that treats the resource server's own + origin as its authorization server when nothing is advertised. The browser ``oauth2`` flow keeps + it (a human sees the redirect), but token_exchange (OBO) sets it False so the gateway never + exchanges a subject token against an endpoint it inferred rather than one explicitly configured + or authoritatively advertised via RFC 9728 / RFC 8414. + """ try: client = get_async_httpx_client(llm_provider=httpxSpecialProvider.MCP) @@ -2360,7 +2969,7 @@ class MCPServerManager: ) = await self._attempt_well_known_discovery(server_url) metadata = None - if not authorization_servers: + if allow_origin_fallback and not authorization_servers: try: parsed_url = urlparse(server_url) if parsed_url.scheme and parsed_url.netloc: @@ -2382,7 +2991,7 @@ class MCPServerManager: verbose_logger.debug("MCP OAuth discovery failed for %s: %s", server_url, exc) return None - def _parse_www_authenticate_header(self, header_value: Optional[str]) -> Tuple[Optional[str], Optional[List[str]]]: + def _parse_www_authenticate_header(self, header_value: Optional[str]) -> tuple[Optional[str], Optional[list[str]]]: if not header_value: return None, None @@ -2390,7 +2999,7 @@ class MCPServerManager: params_section = params_section or header_value param_pattern = re.compile(r"([a-zA-Z0-9_]+)\s*=\s*\"?([^\",]+)\"?") - params: Dict[str, str] = { + params: dict[str, str] = { match.group(1).lower(): match.group(2).strip() for match in param_pattern.finditer(params_section) } @@ -2404,7 +3013,7 @@ class MCPServerManager: async def _fetch_oauth_metadata_from_resource( self, resource_metadata_url: str, server_url: str - ) -> Tuple[List[str], Optional[List[str]]]: + ) -> tuple[list[str], Optional[list[str]]]: if not resource_metadata_url: return [], None @@ -2439,7 +3048,7 @@ class MCPServerManager: return authorization_servers, scopes - async def _attempt_well_known_discovery(self, server_url: str) -> Tuple[List[str], Optional[List[str]]]: + async def _attempt_well_known_discovery(self, server_url: str) -> tuple[list[str], Optional[list[str]]]: try: parsed = urlparse(server_url) except Exception: @@ -2452,7 +3061,7 @@ class MCPServerManager: path = parsed.path or "" path = path.strip("/") - candidate_urls: List[str] = [] + candidate_urls: list[str] = [] if path: candidate_urls.append(f"{base}/.well-known/oauth-protected-resource/{path}") candidate_urls.append(f"{base}/.well-known/oauth-protected-resource") @@ -2468,7 +3077,7 @@ class MCPServerManager: return [], None async def _fetch_authorization_server_metadata( - self, authorization_servers: List[str], server_url: str + self, authorization_servers: list[str], server_url: str ) -> Optional[MCPOAuthMetadata]: for issuer in authorization_servers: metadata = await self._fetch_single_authorization_server_metadata(issuer, server_url) @@ -2490,7 +3099,7 @@ class MCPServerManager: base = f"{parsed.scheme}://{parsed.netloc}" path = (parsed.path or "").strip("/") - candidate_urls: List[str] = [] + candidate_urls: list[str] = [] if path: candidate_urls.append(f"{base}/.well-known/oauth-authorization-server/{path}") candidate_urls.append(f"{base}/.well-known/openid-configuration/{path}") @@ -2522,6 +3131,14 @@ class MCPServerManager: continue scopes = self._extract_scopes(data.get("scopes_supported")) + verbose_logger.debug( + "Authorization server metadata from %s: issuer=%s grant_types_supported=%s " + "token_endpoint_auth_methods_supported=%s", + url, + data.get("issuer"), + data.get("grant_types_supported"), + data.get("token_endpoint_auth_methods_supported"), + ) metadata = MCPOAuthMetadata( scopes=scopes, authorization_url=data.get("authorization_endpoint"), @@ -2576,9 +3193,9 @@ class MCPServerManager: def _extract_aws_credentials( self, - credentials_dict: Optional[Dict[str, str]], + credentials_dict: Optional[dict[str, str]], credentials_are_encrypted: bool, - ) -> Dict[str, Optional[str]]: + ) -> dict[str, Optional[str]]: """Extract and decrypt AWS SigV4 credential fields from credentials dict.""" if not credentials_dict: return {} @@ -2604,7 +3221,7 @@ class MCPServerManager: "aws_session_name": credentials_dict.get("aws_session_name"), } - def _extract_scopes(self, scopes_value: Any) -> Optional[List[str]]: + def _extract_scopes(self, scopes_value: Any) -> Optional[list[str]]: if isinstance(scopes_value, str): scopes = [s.strip() for s in scopes_value.split() if s.strip()] return scopes or None @@ -2617,46 +3234,34 @@ class MCPServerManager: self, client: MCPClient, server_name: str, - server: Optional[MCPServer] = None, - ) -> List[MCPTool]: + ) -> list[MCPTool]: """ Fetch tools from MCP client with timeout and error handling. Uses anyio.fail_after() instead of asyncio.wait_for() to avoid conflicts with the MCP SDK's anyio TaskGroup. See GitHub issue #20715 for details. - For OAuth pass-through and upstream-delegated OAuth2 MCP servers, an - upstream HTTP 401 is converted into :class:`MCPUpstreamAuthError` - instead of being swallowed to an empty tool list. That lets the - single-server HTTP routes surface a proper 401 + ``WWW-Authenticate`` - challenge so standards-compliant MCP clients trigger the upstream - OAuth flow. Other servers keep today's swallow-and-log behaviour so - the multi-server ``/mcp`` aggregator doesn't get tainted by a single - bad server. + An upstream HTTP 401 is converted into :class:`MCPUpstreamAuthError` + instead of being swallowed to an empty tool list, regardless of the + server's auth_type. Callers route it by surface: the single-server HTTP + routes turn it into a 401 + ``WWW-Authenticate`` challenge so standards- + compliant MCP clients trigger the upstream OAuth flow, while the + multi-server ``/mcp`` aggregator absorbs it to an empty list so one + unauthenticated server doesn't fail the whole listing. Only a 401 + (missing/invalid credential) drives the re-auth challenge; a 403 + (authenticated but forbidden, e.g. insufficient scope) is not a re-auth + signal and, like other non-auth errors, returns an empty list. Args: client: MCP client instance server_name: Name of the server for logging - server: Optional MCPServer; when upstream auth is delegated, auth - errors are re-raised as :class:`MCPUpstreamAuthError`. Returns: List of tools from the server """ - should_surface_upstream_auth = bool( - server is not None - and ( - server.is_oauth_passthrough - or ( - server.auth_type == MCPAuth.oauth2 - and getattr(server, "delegate_auth_to_upstream", False) is True - and not server.has_client_credentials - ) - ) - ) try: with anyio.fail_after(MCP_TOOL_LISTING_TIMEOUT): - tools = await client.list_tools(raise_on_error=should_surface_upstream_auth) + tools = await client.list_tools(raise_on_error=True) verbose_logger.debug(f"Tools from {server_name}: {tools}") return tools except TimeoutError: @@ -2669,16 +3274,15 @@ class MCPServerManager: verbose_logger.warning(f"Connection error while listing tools from {server_name}: {str(e)}") return [] except Exception as e: - if should_surface_upstream_auth: - auth_info = _extract_upstream_auth_failure(e) - if auth_info is not None: - status_code, www_authenticate = auth_info - verbose_logger.info(f"Upstream auth failure from MCP server {server_name}: HTTP {status_code}") - raise MCPUpstreamAuthError( - status_code=status_code, - www_authenticate=www_authenticate, - server_name=server_name, - ) from e + auth_info = _extract_upstream_auth_failure(e) + if auth_info is not None and auth_info[0] == 401: + _, www_authenticate = auth_info + verbose_logger.info(f"Upstream auth failure from MCP server {server_name}: HTTP 401") + raise MCPUpstreamAuthError( + status_code=401, + www_authenticate=www_authenticate, + server_name=server_name, + ) from e verbose_logger.warning(f"Error listing tools from {server_name}: {str(e)}") return [] @@ -2687,7 +3291,7 @@ class MCPServerManager: def _assign_unique_short_prefix( self, server: MCPServer, - registry: Optional[Dict[str, MCPServer]] = None, + registry: Optional[dict[str, MCPServer]] = None, ) -> None: """Resolve and cache a collision-free short tool prefix on ``server``. @@ -2711,7 +3315,7 @@ class MCPServerManager: if not server.server_id: return - used: Dict[str, str] = {} + used: dict[str, str] = {} registry_for_collision_check = registry or self.get_registry() for other in registry_for_collision_check.values(): if other.server_id == server.server_id: @@ -2744,7 +3348,7 @@ class MCPServerManager: "attempts; the 3-character prefix space is too crowded." ) - def _create_prefixed_tools(self, tools: List[MCPTool], server: MCPServer, add_prefix: bool = True) -> List[MCPTool]: + def _create_prefixed_tools(self, tools: list[MCPTool], server: MCPServer, add_prefix: bool = True) -> list[MCPTool]: """ Create prefixed tools and update tool mapping. @@ -2782,8 +3386,8 @@ class MCPServerManager: return prefixed_tools def _create_prefixed_prompts( - self, prompts: List[Prompt], server: MCPServer, add_prefix: bool = True - ) -> List[Prompt]: + self, prompts: list[Prompt], server: MCPServer, add_prefix: bool = True + ) -> list[Prompt]: """ Create prefixed prompts and update prompt mapping. @@ -2809,11 +3413,11 @@ class MCPServerManager: return prefixed_prompts def _create_prefixed_resources( - self, resources: List[Resource], server: MCPServer, add_prefix: bool = True - ) -> List[Resource]: + self, resources: list[Resource], server: MCPServer, add_prefix: bool = True + ) -> list[Resource]: """Prefix resource names and track origin server for read requests.""" - prefixed_resources: List[Resource] = [] + prefixed_resources: list[Resource] = [] prefix = get_server_prefix(server) for resource in resources: @@ -2826,13 +3430,13 @@ class MCPServerManager: def _create_prefixed_resource_templates( self, - resource_templates: List[ResourceTemplate], + resource_templates: list[ResourceTemplate], server: MCPServer, add_prefix: bool = True, - ) -> List[ResourceTemplate]: + ) -> list[ResourceTemplate]: """Prefix resource template names for multi-server scenarios.""" - prefixed_templates: List[ResourceTemplate] = [] + prefixed_templates: list[ResourceTemplate] = [] prefix = get_server_prefix(server) for resource_template in resource_templates: @@ -2865,7 +3469,7 @@ class MCPServerManager: ) return True - def validate_allowed_params(self, tool_name: str, arguments: Dict[str, Any], server: MCPServer) -> None: + def validate_allowed_params(self, tool_name: str, arguments: dict[str, Any], server: MCPServer) -> None: """ Filter arguments to only include allowed parameters for the given tool. @@ -2956,7 +3560,7 @@ class MCPServerManager: self, server: MCPServer, tool_name: str, - arguments: Dict[str, Any], + arguments: dict[str, Any], ) -> CallToolResult: """ Call an OpenAPI tool handler directly. @@ -3013,13 +3617,13 @@ class MCPServerManager: async def pre_call_tool_check( self, name: str, - arguments: Dict[str, Any], + arguments: dict[str, Any], server_name: str, user_api_key_auth: Optional[UserAPIKeyAuth], proxy_logging_obj: ProxyLogging, server: MCPServer, - raw_headers: Optional[Dict[str, str]] = None, - ) -> Dict[str, Any]: + raw_headers: Optional[dict[str, str]] = None, + ) -> dict[str, Any]: """ Run pre-call checks and guardrail hooks for an MCP tool call. @@ -3079,7 +3683,7 @@ class MCPServerManager: # Convert to LLM format for existing guardrail compatibility synthetic_llm_data = proxy_logging_obj._convert_mcp_to_llm_format(mcp_request_obj, pre_hook_kwargs) - hook_result: Dict[str, Any] = {} + hook_result: dict[str, Any] = {} try: # Use standard pre_call_hook modified_data = await proxy_logging_obj.pre_call_hook( @@ -3109,7 +3713,7 @@ class MCPServerManager: def _create_during_hook_task( self, name: str, - arguments: Dict[str, Any], + arguments: dict[str, Any], server_name_from_prefix: Optional[str], user_api_key_auth: Optional[UserAPIKeyAuth], proxy_logging_obj: ProxyLogging, @@ -3144,19 +3748,79 @@ class MCPServerManager: ) ) + def _get_call_semaphore(self, mcp_server: MCPServer) -> Optional[asyncio.Semaphore]: + limit = mcp_server.max_concurrent_requests + if limit is None or limit <= 0: + return None + cached = self._server_call_semaphores.get(mcp_server.server_id) + if cached is not None and cached[0] == limit: + return cached[1] + semaphore = asyncio.Semaphore(limit) + self._server_call_semaphores[mcp_server.server_id] = (limit, semaphore) + return semaphore + + @asynccontextmanager + async def _limit_outbound_concurrency(self, mcp_server: MCPServer) -> AsyncIterator[None]: + semaphore = self._get_call_semaphore(mcp_server) + if semaphore is None: + yield + return + async with semaphore: + yield + + async def _obo_call_tool_with_retry( + self, + *, + client: MCPClient, + call_tool_params: MCPCallToolRequestParams, + host_progress_callback: Optional[Callable], + mcp_server: MCPServer, + server_auth_header: str | dict[str, str] | None, + extra_headers: Optional[dict[str, str]], + stdio_env: Optional[dict[str, str]], + subject_token: Optional[str], + user_api_key_auth: Optional[UserAPIKeyAuth], + ) -> CallToolResult: + """Call a token_exchange (OBO) tool; on an upstream 401/403 re-mint the token once and retry. + + The exchanged token is baked into the client at build time, so the retry invalidates the + cached exchange and rebuilds the client (which re-exchanges). One retry only: a non-auth + failure or a second auth failure degrades to the normal ``isError`` result, and a re-exchange + that now fails surfaces its own 401 challenge from ``_create_mcp_client``. + """ + try: + return await client.call_tool( + call_tool_params, host_progress_callback=host_progress_callback, raise_on_error=True + ) + except Exception as exc: + if _extract_upstream_auth_failure(exc) is None: + return MCPClient.error_tool_result(exc) + spec = to_server_spec(mcp_server) + if spec is not None: + await self._cred_provider.invalidate_credentials(to_subject(user_api_key_auth, subject_token), spec) + retry_client = await self._create_mcp_client( + server=mcp_server, + mcp_auth_header=server_auth_header, + extra_headers=extra_headers, + stdio_env=stdio_env, + subject_token=subject_token, + user_api_key_auth=user_api_key_auth, + ) + return await retry_client.call_tool(call_tool_params, host_progress_callback=host_progress_callback) + async def _call_regular_mcp_tool( self, mcp_server: MCPServer, original_tool_name: str, - arguments: Dict[str, Any], - tasks: List, + arguments: dict[str, Any], + tasks: list, mcp_auth_header: Optional[str], - mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]], - oauth2_headers: Optional[Dict[str, str]], - raw_headers: Optional[Dict[str, str]], + mcp_server_auth_headers: Optional[dict[str, dict[str, str]]], + oauth2_headers: Optional[dict[str, str]], + raw_headers: Optional[dict[str, str]], proxy_logging_obj: Optional[ProxyLogging], host_progress_callback: Optional[Callable] = None, - hook_extra_headers: Optional[Dict[str, str]] = None, + hook_extra_headers: Optional[dict[str, str]] = None, user_api_key_auth: Optional[UserAPIKeyAuth] = None, ) -> CallToolResult: """ @@ -3187,7 +3851,7 @@ class MCPServerManager: # Get server-specific auth header if available (case-insensitive) # FIX: Added case-insensitive matching to handle auth header keys that may not match # the exact case of server alias/name (e.g., '1litellmagcgateway' vs '1LiteLLMAGCGateway') - server_auth_header: Optional[Union[Dict[str, str], str]] = None + server_auth_header: Optional[Union[dict[str, str], str]] = None if mcp_server_auth_headers: # Normalize keys for case-insensitive lookup from litellm.proxy._experimental.mcp_server.utils import ( @@ -3206,7 +3870,7 @@ class MCPServerManager: # Extract subject token for OAuth2 Token Exchange (OBO) flow subject_token: Optional[str] = None - extra_headers: Optional[Dict[str, str]] = None + extra_headers: Optional[dict[str, str]] = None if mcp_server.auth_type == MCPAuth.oauth2_token_exchange: subject_token = self._extract_bearer_token(oauth2_headers, raw_headers) elif mcp_server.auth_type == MCPAuth.oauth2: @@ -3225,6 +3889,13 @@ class MCPServerManager: user_api_key_auth=user_api_key_auth, ): extra_headers = _without_authorization(extra_headers) + elif mcp_server.is_true_passthrough or mcp_server.is_oauth_delegate: + extra_headers = _client_forwarded_authorization_headers( + mcp_server=mcp_server, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ) if mcp_server.extra_headers and raw_headers: if extra_headers is None: @@ -3302,10 +3973,74 @@ class MCPServerManager: arguments=arguments, ) - async def _call_tool_via_client(client, params): - return await client.call_tool(params, host_progress_callback=host_progress_callback) + if mcp_server.auth_type == MCPAuth.oauth2_token_exchange and subject_token: + # OBO: the exchanged token may have been revoked/rotated upstream since it was cached, so + # an upstream 401 gets one re-mint + retry. Gated to this mode; all others keep the plain + # single call below. + async def _obo_call_tool_limited(): + async with self._limit_outbound_concurrency(mcp_server): + return await self._obo_call_tool_with_retry( + client=client, + call_tool_params=call_tool_params, + host_progress_callback=host_progress_callback, + mcp_server=mcp_server, + server_auth_header=server_auth_header, + extra_headers=extra_headers, + stdio_env=stdio_env, + subject_token=subject_token, + user_api_key_auth=user_api_key_auth, + ) - tasks.append(asyncio.create_task(_call_tool_via_client(client, call_tool_params))) + tool_call_coro = _obo_call_tool_limited() + else: + # Scoped to the two client-forwarded token modes this stack introduced; legacy + # oauth2 + delegate_auth_to_upstream (is_oauth_passthrough) is being removed, so it is not + # added here even though the list path still relays for it. + relays_upstream_auth = mcp_server.is_true_passthrough or mcp_server.is_oauth_delegate + server_label = mcp_server.name or mcp_server.server_name or mcp_server.alias or "" + + async def _call_tool_via_client(client, params): + async with self._limit_outbound_concurrency(mcp_server): + if not relays_upstream_auth: + return await client.call_tool(params, host_progress_callback=host_progress_callback) + # The client-forwarded modes carry the caller's own upstream token, so an upstream + # 401 (expired/invalid token) is the caller's to resolve: relay it as + # MCPUpstreamAuthError so single-server REST callers turn it into a 401 + + # WWW-Authenticate and re-run the upstream OAuth flow. Only 401 is a re-auth signal + # (mirrors the list path and MCPUpstreamAuthError's contract); a 403 is a genuine + # authorization failure that re-auth won't fix, so it takes the non-auth branch and + # stays a visible warning. raise_on_error only re-raises transport failures + # (tool-level isError results are still returned normally); a non-auth failure keeps + # the same isError degradation the default path produces. + try: + return await client.call_tool( + params, host_progress_callback=host_progress_callback, raise_on_error=True + ) + except Exception as e: + auth_info = _extract_upstream_auth_failure(e) + if auth_info is None or auth_info[0] != 401: + # A genuine (non-auth or 403-forbidden) upstream/transport failure. + # raise_on_error demoted the client-layer log to debug, so surface it here at + # warning level to keep the outage visible; the caller still gets the graceful + # isError result the default masking path would have produced. Log the + # exception type only, never str(e), which for an httpx error embeds the + # upstream URL (a credential can hide in it). + verbose_logger.warning( + "Pass-through MCP tool call failed against %s (non-auth, %s)", + server_label, + type(e).__name__, + ) + return client.error_tool_result(e) + _, www_authenticate = auth_info + raise MCPUpstreamAuthError( + status_code=401, + www_authenticate=www_authenticate, + server_name=server_label, + ) from e + + tool_call_coro = _call_tool_via_client(client, call_tool_params) + + tasks.append(asyncio.create_task(tool_call_coro)) _timeout = mcp_server.timeout if mcp_server.timeout is not None else MCP_CLIENT_TIMEOUT try: @@ -3390,12 +4125,34 @@ class MCPServerManager: return False return await self._cred_provider.has_user_token(to_subject(user_api_key_auth, None), spec) + async def invalidate_user_oauth_token_cache(self, user_id: str, server_id: str) -> None: + """Drop every cached token for ``(user_id, server_id)`` after the credential row changes + (re-auth, revoke, config-change purge): the v2 chain's cache and the legacy per-user token + cache, so the next resolve reads the new row instead of serving the replaced token until its + cache TTL, whichever path resolves it. This is the single invalidation point for per-user + OAuth tokens; callers must not evict individual caches directly. Best-effort: a cache-drop + failure is logged, never raised, because the DB write already succeeded and the TTL remains + the backstop. + """ + try: + await self._per_user_oauth_token_store.invalidate(user_id, server_id) + except Exception as exc: # noqa: BLE001 - cache drop is best-effort; TTL is the backstop + verbose_logger.warning( + "Failed to invalidate cached MCP OAuth token for user=%s server=%s: %s", user_id, server_id, exc + ) + try: + await self._per_user_token_cache.delete(user_id, server_id) + except Exception as exc: # noqa: BLE001 - cache drop is best-effort; TTL is the backstop + verbose_logger.warning( + "Failed to drop legacy cached MCP OAuth token for user=%s server=%s: %s", user_id, server_id, exc + ) + async def _resolve_oauth2_headers_for_tool_call( self, mcp_server: MCPServer, - oauth2_headers: Optional[Dict[str, str]], + oauth2_headers: Optional[dict[str, str]], user_api_key_auth: Optional[UserAPIKeyAuth], - ) -> Optional[Dict[str, str]]: + ) -> Optional[dict[str, str]]: """Look up per-user OAuth headers when the client did not supply a token.""" if not mcp_server.needs_user_oauth_token or oauth2_headers or user_api_key_auth is None: return oauth2_headers @@ -3432,7 +4189,7 @@ class MCPServerManager: async def _gather_openapi_tool_tasks( self, - tasks: List[Any], + tasks: list[Any], proxy_logging_obj: Optional[ProxyLogging], ) -> CallToolResult: """Await OpenAPI tool tasks and return the tool call result.""" @@ -3452,13 +4209,13 @@ class MCPServerManager: self, server_name: str, name: str, - arguments: Dict[str, Any], + arguments: dict[str, Any], user_api_key_auth: Optional[UserAPIKeyAuth] = None, mcp_auth_header: Optional[str] = None, - mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]] = None, + mcp_server_auth_headers: Optional[dict[str, dict[str, str]]] = None, proxy_logging_obj: Optional[ProxyLogging] = None, - oauth2_headers: Optional[Dict[str, str]] = None, - raw_headers: Optional[Dict[str, str]] = None, + oauth2_headers: Optional[dict[str, str]] = None, + raw_headers: Optional[dict[str, str]] = None, host_progress_callback: Optional[Callable] = None, ) -> CallToolResult: """ @@ -3480,12 +4237,21 @@ class MCPServerManager: start_time = datetime.datetime.now() mcp_server = self._resolve_mcp_server_for_tool_call(server_name, name) + # Resolved before any hook runs so a missing BYOK credential (401) never + # leaves during-hook side effects (audit logging, rate-limit bookkeeping) + # recorded against a call that ultimately fails. + mcp_auth_header = await _resolve_byok_mcp_auth_header( + mcp_server, + user_api_key_auth, + mcp_auth_header, + ) + ######################################################### # Pre MCP Tool Call Hook # Allow validation and modification of tool calls before execution # Using standard pre_call_hook ######################################################### - hook_result: Dict[str, Any] = {} + hook_result: dict[str, Any] = {} if proxy_logging_obj: hook_result = await self.pre_call_tool_check( name=name, @@ -3525,7 +4291,28 @@ class MCPServerManager: "transport to enable hook header injection.", server_name, ) - tasks.append(asyncio.create_task(self._call_openapi_tool_handler(mcp_server, name, arguments))) + + auth_header_value = ( + _format_byok_openapi_auth_header(mcp_server, mcp_auth_header) if mcp_auth_header else None + ) + forwarded_headers = _openapi_forwarded_extra_headers(mcp_server, raw_headers, user_api_key_auth) + + async def _call_openapi_via_handler(): + from litellm.proxy._experimental.mcp_server.openapi_to_mcp_generator import ( + _request_auth_header, + _request_extra_headers, + ) + + auth_token = _request_auth_header.set(auth_header_value) + extra_token = _request_extra_headers.set(forwarded_headers) + try: + async with self._limit_outbound_concurrency(mcp_server): + return await self._call_openapi_tool_handler(mcp_server, name, arguments) + finally: + _request_auth_header.reset(auth_token) + _request_extra_headers.reset(extra_token) + + tasks.append(asyncio.create_task(_call_openapi_via_handler())) else: return await self._call_regular_mcp_tool( mcp_server=mcp_server, @@ -3606,7 +4393,7 @@ class MCPServerManager: # Build prefix → server lookup covering every known form a tool name # may take (alias / server_name / server_id / short ID). This is what # makes the short-prefix mode work without breaking historical names. - prefix_to_server: Dict[str, MCPServer] = {} + prefix_to_server: dict[str, MCPServer] = {} for server in registry_servers: for known_prefix in iter_known_server_prefixes(server): normalised = normalize_server_name(known_prefix) @@ -3667,7 +4454,7 @@ class MCPServerManager: verbose_logger.info(f"Found {len(db_mcp_servers)} MCP servers in database") previous_registry = self.registry - new_registry: Dict[str, MCPServer] = {} + new_registry: dict[str, MCPServer] = {} # Stage one: build every server. Stage two assigns short prefixes # against the *full* set so dedup is deterministic regardless of @@ -3713,7 +4500,7 @@ class MCPServerManager: # Assign short prefixes against the full candidate set without # publishing the staged registry to concurrent callers. - registered_registry: Dict[str, MCPServer] = {} + registered_registry: dict[str, MCPServer] = {} registered_openapi_tools = False for server_id, new_server in new_registry.items(): try: @@ -3739,7 +4526,7 @@ class MCPServerManager: verbose_logger.debug("MCP registry refreshed (%s servers in registry)", len(registered_registry)) - def get_mcp_servers_from_ids(self, server_ids: List[str]) -> List[MCPServer]: + def get_mcp_servers_from_ids(self, server_ids: list[str]) -> list[MCPServer]: servers = [] registry = self.get_registry() for server in registry.values(): @@ -3747,7 +4534,7 @@ class MCPServerManager: servers.append(server) return servers - def _get_general_settings(self) -> Dict[str, Any]: + def _get_general_settings(self) -> dict[str, Any]: """Get general_settings, importing lazily to avoid circular imports.""" try: from litellm.proxy.proxy_server import ( @@ -3790,7 +4577,7 @@ class MCPServerManager: return server return None - def get_public_mcp_servers(self) -> List[MCPServer]: + def get_public_mcp_servers(self) -> list[MCPServer]: """ Return the MCP servers published to the AI Hub via /v1/mcp/make_public. @@ -3820,7 +4607,7 @@ class MCPServerManager: if server.available_on_public_internet or server.server_id in public_ids ] - def expand_permission_list(self, identifiers: List[str]) -> List[str]: + def expand_permission_list(self, identifiers: list[str]) -> list[str]: """ Expand a permission list of server_ids/names/aliases into concrete server_ids against the current region's config + DB registry union. @@ -3836,12 +4623,12 @@ class MCPServerManager: if not identifiers: return [] registry = self.get_registry() - expanded: Set[str] = set() + expanded: set[str] = set() for identifier in identifiers: if identifier in registry: expanded.add(identifier) continue - matches: List[str] = [ + matches: list[str] = [ server_id for server_id, server in registry.items() if server.alias == identifier or server.server_name == identifier or server.name == identifier @@ -3862,8 +4649,8 @@ class MCPServerManager: def expand_tool_permissions( self, - tool_permissions: Optional[Dict[str, List[str]]], - ) -> Dict[str, List[str]]: + tool_permissions: Optional[dict[str, list[str]]], + ) -> dict[str, list[str]]: """ Rewrite an ``mcp_tool_permissions`` dict keyed by id/name/alias so every key is a concrete server_id where possible. Tool lists from @@ -3878,7 +4665,7 @@ class MCPServerManager: """ if not tool_permissions: return {} - result: Dict[str, List[str]] = {} + result: dict[str, list[str]] = {} for key, tools in tool_permissions.items(): for server_id in self.expand_permission_list([key]): result.setdefault(server_id, []).extend(tools or []) @@ -3919,7 +4706,7 @@ class MCPServerManager: return server return None - def get_filtered_registry(self, client_ip: Optional[str] = None) -> Dict[str, MCPServer]: + def get_filtered_registry(self, client_ip: Optional[str] = None) -> dict[str, MCPServer]: """ Get registry filtered by client IP access control. @@ -4079,16 +4866,23 @@ class MCPServerManager: authorization_url=server.authorization_url, token_url=server.token_url, registration_url=server.registration_url, + oauth2_flow=server.oauth2_flow, + dcr_bridge=server.dcr_bridge, + token_exchange_endpoint=server.token_exchange_endpoint, + audience=server.audience, + subject_token_type=server.subject_token_type, + token_exchange_profile=server.token_exchange_profile, allow_all_keys=server.allow_all_keys, instructions=server.instructions, timeout=server.timeout, + max_concurrent_requests=server.max_concurrent_requests, ) async def get_all_mcp_servers_with_health_and_teams( self, user_api_key_auth: Optional[UserAPIKeyAuth] = None, - server_ids: Optional[List[str]] = None, - ) -> List[LiteLLM_MCPServerTable]: + server_ids: Optional[list[str]] = None, + ) -> list[LiteLLM_MCPServerTable]: """ Get all MCP servers that the user has access to, with health status and team information. @@ -4117,7 +4911,7 @@ class MCPServerManager: async def get_all_allowed_mcp_servers( self, user_api_key_auth: Optional[UserAPIKeyAuth] = None, - ) -> List[LiteLLM_MCPServerTable]: + ) -> list[LiteLLM_MCPServerTable]: """ Get all MCP servers that the user has access to. @@ -4130,7 +4924,7 @@ class MCPServerManager: # Get allowed server IDs allowed_server_ids = await self.get_allowed_mcp_servers(user_api_key_auth) - list_mcp_servers: List[LiteLLM_MCPServerTable] = [] + list_mcp_servers: list[LiteLLM_MCPServerTable] = [] for server_id in allowed_server_ids: server = self.get_mcp_server_by_id(server_id) @@ -4145,8 +4939,8 @@ class MCPServerManager: @staticmethod def _env_vars_to_models( - env_vars: Optional[List[Dict[str, Any]]], - ) -> Optional[List[MCPEnvVar]]: + env_vars: Optional[list[dict[str, Any]]], + ) -> Optional[list[MCPEnvVar]]: if env_vars is None: return None return [MCPEnvVar.model_validate(env_var) for env_var in env_vars] @@ -4166,6 +4960,8 @@ class MCPServerManager: teams=[], mcp_access_groups=server.access_groups or [], allowed_tools=server.allowed_tools or [], + tool_name_to_display_name=server.tool_name_to_display_name, + tool_name_to_description=server.tool_name_to_description, extra_headers=server.extra_headers or [], mcp_info=server.mcp_info, static_headers=server.static_headers, @@ -4179,33 +4975,40 @@ class MCPServerManager: authorization_url=server.authorization_url, token_url=server.token_url, registration_url=server.registration_url, + oauth2_flow=server.oauth2_flow, + token_exchange_endpoint=server.token_exchange_endpoint, + audience=server.audience, + subject_token_type=server.subject_token_type, + token_exchange_profile=server.token_exchange_profile, allow_all_keys=server.allow_all_keys, available_on_public_internet=server.available_on_public_internet, delegate_auth_to_upstream=server.delegate_auth_to_upstream, oauth_passthrough=getattr(server, "oauth_passthrough", False), + dcr_bridge=server.dcr_bridge, is_byok=server.is_byok, byok_description=server.byok_description, byok_api_key_help_url=server.byok_api_key_help_url, source_url=server.source_url, instructions=server.instructions, timeout=server.timeout, + max_concurrent_requests=server.max_concurrent_requests, ) - async def get_all_mcp_servers_unfiltered(self) -> List[LiteLLM_MCPServerTable]: + async def get_all_mcp_servers_unfiltered(self) -> list[LiteLLM_MCPServerTable]: """Return all MCP servers from registry without applying access controls.""" registry = self.get_registry() if not registry: return [] - servers: List[LiteLLM_MCPServerTable] = [] + servers: list[LiteLLM_MCPServerTable] = [] for server in registry.values(): servers.append(self._build_mcp_server_table(server)) return servers async def get_all_mcp_servers_with_health_unfiltered( - self, server_ids: Optional[List[str]] = None - ) -> List[LiteLLM_MCPServerTable]: + self, server_ids: Optional[list[str]] = None + ) -> list[LiteLLM_MCPServerTable]: """Return health info for all servers in registry regardless of user access.""" registry = self.get_registry() @@ -4222,7 +5025,7 @@ class MCPServerManager: return await self._run_health_checks(target_server_ids) - async def _run_health_checks(self, target_server_ids: List[str]) -> List[LiteLLM_MCPServerTable]: + async def _run_health_checks(self, target_server_ids: list[str]) -> list[LiteLLM_MCPServerTable]: if not target_server_ids: return [] diff --git a/litellm/proxy/_experimental/mcp_server/oauth2_flow_backfill.py b/litellm/proxy/_experimental/mcp_server/oauth2_flow_backfill.py new file mode 100644 index 00000000000..02cec2475e2 --- /dev/null +++ b/litellm/proxy/_experimental/mcp_server/oauth2_flow_backfill.py @@ -0,0 +1,155 @@ +"""Startup backfill for oauth2 MCP server rows persisted before oauth2_flow was written. + +Rows created before the write-side stamps (DCR persist, UI create, REST create) carry a +null ``oauth2_flow`` and rely on read-time field-shape inference, which cannot tell a +DCR-registered interactive server from an M2M server unless endpoint discovery succeeds +first. This backfill classifies each null row once, at rest, using signals inference +never had, and persists the result so the read path never has to infer again. + +Signal order, strongest first: + +1. Per-user OAuth token rows exist for the server: only the interactive flow mints + per-user tokens, so this is definitive and immune to the discovery trap. BYOK API + keys share the same table (``LiteLLM_MCPUserCredentials``), so only rows whose + payload decodes as a ``type: oauth2`` token count as proof; bare keys and + undecodable rows prove nothing about the flow. +2. ``authorization_url`` persisted: interactive needs a user-facing authorization + endpoint; M2M (RFC 6749 section 4.4) never has one. +3. ``registration_url`` persisted: dynamic client registration (RFC 7591) exists to mint + clients for the interactive flow; M2M servers are configured with static credentials. +4. ``token_url`` plus decryptable ``client_id`` and ``client_secret``: ambiguous, left + unstamped. The shape is shared by M2M servers and DCR-registered interactive servers + whose authorization endpoint lives only in discovery (registered but never signed + in), so stamping client_credentials here could permanently route per-user traffic + through the proxy's stored client credential. The row keeps working through the + request-time backstop and a warning names it with the one-line fix (set oauth2_flow + via the dashboard or ``PUT /v1/mcp/server``); a completed interactive sign-in also + heals it via rule 1 at the next boot. +5. Anything else is interactive: matching how ``needs_user_oauth_token`` treats a null + flow, so the stamp never changes runtime routing for rows no rule recognizes. + +The backfill never stamps client_credentials: M2M is asserted by a human (config +requires it, the API accepts it, the dashboard sets it), mirroring the config-level +validation error. Runs before the first registry load on every boot and is idempotent: +a healed fleet has no null rows and the backfill exits after one query. +""" + +import json +from collections import Counter +from typing import Any, Literal, Optional + +from litellm._logging import verbose_proxy_logger +from litellm.proxy._experimental.mcp_server.db import _decode_oauth_payload, decrypt_credentials +from litellm.proxy.utils import PrismaClient +from litellm.types.mcp import MCPCredentials + +OAuth2Flow = Literal["client_credentials", "authorization_code"] +BackfillRule = Literal[ + "per_user_tokens", + "authorization_url", + "registration_url", + "ambiguous_m2m_shape", + "interactive_default", +] + +_BACKFILL_AUDIT_ACTOR = "oauth2_flow_backfill" + + +def _decrypted_credentials(raw_credentials: Any) -> Optional[MCPCredentials]: + if raw_credentials is None: + return None + if isinstance(raw_credentials, str): + try: + parsed = json.loads(raw_credentials) + except (ValueError, TypeError): + return None + else: + parsed = raw_credentials + if not isinstance(parsed, dict): + return None + return decrypt_credentials(credentials=dict(parsed)) + + +def classify_null_flow_row( + *, + has_per_user_tokens: bool, + authorization_url: Optional[str], + registration_url: Optional[str], + token_url: Optional[str], + credentials: Optional[MCPCredentials], +) -> tuple[Optional[OAuth2Flow], BackfillRule]: + if has_per_user_tokens: + return "authorization_code", "per_user_tokens" + if authorization_url: + return "authorization_code", "authorization_url" + if registration_url: + return "authorization_code", "registration_url" + if token_url and credentials and credentials.get("client_id") and credentials.get("client_secret"): + return None, "ambiguous_m2m_shape" + return "authorization_code", "interactive_default" + + +async def backfill_null_oauth2_flows(prisma_client: PrismaClient) -> dict[BackfillRule, int]: + """Classify every ``auth_type=oauth2`` row whose ``oauth2_flow`` is null; stamp the provable + ones, warn on the ambiguous ones, and return counts per rule.""" + null_rows: list[Any] = await prisma_client.db.litellm_mcpservertable.find_many( + where={"auth_type": "oauth2", "oauth2_flow": None}, + ) + if not null_rows: + return {} + + server_ids = [row.server_id for row in null_rows] + token_rows: list[Any] = await prisma_client.db.litellm_mcpusercredentials.find_many( + where={"server_id": {"in": server_ids}}, + ) + server_ids_with_oauth_tokens: set[str] = { + token_row.server_id for token_row in token_rows if _decode_oauth_payload(token_row.credential_b64) is not None + } + + classified = tuple( + ( + row, + classify_null_flow_row( + has_per_user_tokens=row.server_id in server_ids_with_oauth_tokens, + authorization_url=row.authorization_url, + registration_url=row.registration_url, + token_url=row.token_url, + credentials=_decrypted_credentials(row.credentials), + ), + ) + for row in null_rows + ) + + for row, (flow, rule) in classified: + if flow is None: + verbose_proxy_logger.warning( + "oauth2_flow backfill: server_id=%s is ambiguous (client credentials + token_url, " + "no interactive signal); left unstamped. Set oauth2_flow explicitly via the " + "dashboard or PUT /v1/mcp/server: client_credentials if this server is M2M, or " + "complete an interactive sign-in and it will be stamped authorization_code at the " + "next boot.", + row.server_id, + ) + else: + verbose_proxy_logger.info( + "oauth2_flow backfill: server_id=%s stamped %s (rule=%s)", + row.server_id, + flow, + rule, + ) + + stamped_flows = {flow for _, (flow, _) in classified if flow is not None} + for stamped_flow in stamped_flows: + server_ids_for_flow = [row.server_id for row, (row_flow, _) in classified if row_flow == stamped_flow] + await prisma_client.db.litellm_mcpservertable.update_many( + where={"server_id": {"in": server_ids_for_flow}, "oauth2_flow": None}, + data={"oauth2_flow": stamped_flow, "updated_by": _BACKFILL_AUDIT_ACTOR}, + ) + + counts: dict[BackfillRule, int] = dict(Counter(rule for _, (_, rule) in classified)) + verbose_proxy_logger.info( + "oauth2_flow backfill: processed %d oauth2 server row(s): %s", + len(null_rows), + counts, + ) + return counts diff --git a/litellm/proxy/_experimental/mcp_server/oauth_utils.py b/litellm/proxy/_experimental/mcp_server/oauth_utils.py index 4d5813dbc5b..6edb22dd858 100644 --- a/litellm/proxy/_experimental/mcp_server/oauth_utils.py +++ b/litellm/proxy/_experimental/mcp_server/oauth_utils.py @@ -129,7 +129,7 @@ def get_request_base_url(request: Request) -> str: if x_forwarded_port and ":" not in netloc: netloc = f"{netloc}:{x_forwarded_port}" - return urlunparse((scheme, netloc, parsed.path, "", "", "")) + return urlunparse((scheme, _strip_default_port(scheme, netloc), parsed.path, "", "", "")) def validate_loopback_redirect_uri(redirect_uri: str) -> None: diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py index 815fc2ba29d..e87e8081ced 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py @@ -12,7 +12,7 @@ every other mode so the caller defers to v1 (parity-safe); it grows one branch p from __future__ import annotations import base64 -from typing import TYPE_CHECKING, NoReturn, Optional +from typing import TYPE_CHECKING, Literal, NoReturn, Optional from fastapi import HTTPException from pydantic import SecretStr @@ -23,11 +23,13 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( AuthorizationCodeConfig, CredError, NoneConfig, + PassthroughConfig, ServerSpec, SharedKey, Subject, + TokenExchangeConfig, ) -from litellm.types.mcp import MCPAuth +from litellm.types.mcp import DEFAULT_SUBJECT_TOKEN_TYPE, MCPAuth if TYPE_CHECKING: from litellm.proxy._types import UserAPIKeyAuth @@ -61,8 +63,10 @@ def to_server_spec(server: MCPServer) -> Optional[ServerSpec]: an ``assert_never`` tail, so a newly added auth mode fails the type gate here until it is explicitly mapped or explicitly deferred, rather than silently falling through to v1. Live modes: ``none``, the static-header family (``api_key`` plus the Authorization schemes, - all shared-key), and ``oauth2`` per-user tokens (``authorization_code``); client_credentials - (M2M), delegated/passthrough oauth2, token exchange, and SigV4 return None and stay on v1. + all shared-key), ``oauth2`` per-user tokens (``authorization_code``), ``oauth2_token_exchange`` + (OBO), and the client-forwarded token modes ``true_passthrough`` / ``oauth_delegate`` + (``PassthroughConfig``); client_credentials (M2M), delegated/passthrough oauth2, and SigV4 + return None and stay on v1. """ if server.is_byok: return None # per-user BYOK source not migrated yet -> defer to v1 (any auth_type) @@ -92,11 +96,49 @@ def to_server_spec(server: MCPServer) -> Optional[ServerSpec]: ) # client_credentials (M2M) and delegate/passthrough oauth2 stay on v1 return None - case MCPAuth.oauth2_token_exchange | MCPAuth.aws_sigv4: - return None # token exchange and SigV4 are not migrated yet -> defer to v1 + case MCPAuth.true_passthrough | MCPAuth.oauth_delegate: + return ServerSpec(server_id=server.server_id, resource=resource, config=PassthroughConfig()) + case MCPAuth.oauth2_token_exchange: + return _token_exchange_spec(server, resource) + case MCPAuth.aws_sigv4: + return None # SigV4 is not migrated yet -> defer to v1 assert_never(auth_type) +def _token_exchange_spec(server: MCPServer, resource: str) -> Optional[ServerSpec]: + """Build a token_exchange (OBO) spec, or defer (None) when it is not OBO-configured. + + An OBO server with ``client_id``/``client_secret`` is owned by the v2 arm even if the + ``token_exchange_endpoint``/``token_url`` is absent: a missing endpoint then fails closed (412) at + the exchanger rather than silently deferring to v1 and connecting unauthenticated, since the + gateway must not guess the IdP or fall back to a weaker source. Without client credentials there is + nothing to own, so the server stays on v1 (parity-safe). ``profile`` selects the wire dialect + (``rfc8693`` default, ``entra_obo`` for Microsoft Entra On-Behalf-Of); an unrecognized value + normalizes to ``rfc8693`` so a bad config value cannot crash spec-building. ``audience`` is + forwarded only when the operator set it; a missing one is omitted, not derived. + """ + endpoint = server.token_exchange_endpoint or server.token_url + if not server.client_id or not server.client_secret: + return None + profile: Literal["rfc8693", "entra_obo"] = ( + "entra_obo" if server.token_exchange_profile == "entra_obo" else "rfc8693" + ) + return ServerSpec( + server_id=server.server_id, + resource=resource, + config=TokenExchangeConfig( + profile=profile, + subject_token_type=server.subject_token_type or DEFAULT_SUBJECT_TOKEN_TYPE, + token_exchange_endpoint=endpoint, + audience=server.audience, + client_id=server.client_id, + client_secret=SecretStr(server.client_secret), + token_endpoint_auth_method=server.token_endpoint_auth_method, + scopes=tuple(server.scopes or ()), + ), + ) + + def _shared_key_spec( server: MCPServer, resource: str, @@ -148,23 +190,75 @@ def raise_public(error: CredError) -> NoReturn: assert_never(error.tag) -def raise_user_oauth_challenge(server: MCPServer) -> NoReturn: +def oauth_protected_resource_path(root_path: str, server: MCPServer) -> str: + """The server's RFC 9728 Protected Resource Metadata path, the shared anchor of both challenges. + + ``root_path`` is the proxy's ``SERVER_ROOT_PATH``, resolved by the caller (the imperative shell) + so this stays a pure function of its inputs; ``"/"`` and ``""`` both mean no prefix. The path is + relative, so it resolves against the caller's own host (correct even behind a reverse proxy). + """ + prefix = "" if root_path == "/" else root_path + name = server.alias or server.server_name or server.name or server.server_id + return f"/.well-known/oauth-protected-resource{prefix}/mcp/{name}" + + +def raise_user_oauth_challenge(server: MCPServer, *, root_path: str) -> NoReturn: """Raise the 401 an ``authorization_code`` server returns at egress when the user has no token. - Points at the server's RFC 9728 Protected Resource Metadata (``resource_metadata``), which names - the upstream authorization server the client must complete OAuth with. The URL is per-server and - relative, so it resolves against the caller's own host (correct even behind a reverse proxy) - without needing request context. The listing-phase 401 still emits the RFC 8414 ``authorization_uri`` - form pending the format unification; both target the same server, so the difference is cosmetic. + Points at the server's RFC 9728 Protected Resource Metadata, which names the upstream + authorization server the client must complete OAuth with. The listing-phase 401 still emits the + RFC 8414 ``authorization_uri`` form pending the format unification; both target the same server, + so the difference is cosmetic. """ - from litellm.proxy.utils import get_server_root_path # noqa: PLC0415 - - root = get_server_root_path() - prefix = "" if root == "/" else root - name = server.alias or server.server_name or server.name or server.server_id - resource_metadata = f"/.well-known/oauth-protected-resource{prefix}/mcp/{name}" + resource_metadata = oauth_protected_resource_path(root_path, server) raise HTTPException( status_code=401, detail="Unauthorized", headers={"WWW-Authenticate": f'Bearer resource_metadata="{resource_metadata}"'}, ) + + +def raise_token_exchange_challenge( + server: MCPServer, + *, + root_path: str, + claims: str | None = None, +) -> NoReturn: + """Raise the RFC 9728 / RFC 6750 challenge an OBO (``token_exchange``) server returns when the + caller's subject token is missing or the IdP rejected it. + + Points at the server's Protected Resource Metadata, whose ``authorization_servers`` names the IdP + the client must SSO with to obtain a subject token; ``error="invalid_token"`` tells a + spec-compliant MCP client to discover that AS and retry with a fresh bearer. Mirrors + ``raise_user_oauth_challenge`` but for the exchange flow: there is no gateway-side browser OAuth — + the client re-authenticates directly with the IdP, and LiteLLM then exchanges the resulting token. + + An IdP step-up rejection (Entra Conditional Access / CAE) passes its ``claims`` blob. Per the + Microsoft claims-challenge format the challenge then uses ``error="insufficient_claims"`` (the + value MSAL-family clients key on) and carries the claims base64-encoded in a ``claims`` parameter + the client replays to the IdP to satisfy the step-up. Without a claims blob the challenge keeps + ``error="invalid_token"`` and is byte-identical to the static one. Both the error value (one of + two literals) and the base64 claims draw from a fixed alphabet, so nothing from the IdP body + reaches the header unescaped. + """ + resource_metadata = oauth_protected_resource_path(root_path, server) + encoded_claims = base64.b64encode(claims.encode()).decode() if claims else None + error = "insufficient_claims" if encoded_claims else "invalid_token" + error_description = ( + "Step-up authentication required; satisfy the returned claims challenge with the IdP and retry" + if encoded_claims + else "Missing or invalid subject token; authenticate with the IdP and retry" + ) + www_authenticate = ", ".join( + ( + f'Bearer resource_metadata="{resource_metadata}"', + f'error="{error}"', + f'error_description="{error_description}"', + *((f'claims="{encoded_claims}"',) if encoded_claims else ()), + ) + ) + raise HTTPException( + status_code=401, + detail="Unauthorized", + headers={"WWW-Authenticate": www_authenticate}, + ) diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/bridge_credentials.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/bridge_credentials.py new file mode 100644 index 00000000000..5530fbc46fd --- /dev/null +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/bridge_credentials.py @@ -0,0 +1,169 @@ +"""Producer and consumer helpers for the DCR-bridge ``oauth_delegate`` envelope. + +A DCR-bridge ``oauth_delegate`` client presents ONE bearer that is a litellm-signed +envelope (see :mod:`.envelope`) carrying both a litellm identity and the upstream OAuth +token. The gateway token endpoint mints it (producer) at OAuth issuance, and at the MCP +admission edge the gateway derives the envelope keys from the proxy ``master_key``, opens +it, admits the request under the recovered identity, and forwards the inner upstream token +to the upstream MCP server (consumer). This module is the pure surface for both sides; the +token-endpoint and admission wiring live in their respective call sites. +""" + +import hashlib +from datetime import datetime +from functools import lru_cache +from typing import Literal, TypeAlias + +from pydantic import BaseModel, ConfigDict, SecretStr + +from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + EnvelopeIdentity, + EnvelopeKeys, + EnvelopeMintError, + OpenedEnvelope, + SealedEnvelope, + UpstreamTokenGrant, + is_envelope, + mint_envelope, + open_envelope, +) + +_SIGNING_KEY_DOMAIN = b"litellm-mcp-bridge:envelope-signing:" +_ENCRYPTION_KEY_DOMAIN = b"litellm-mcp-bridge:envelope-encryption:" + +# scrypt work factors (RFC 7914). n=2**15 with r=8/p=1 costs ~50ms and ~32MB per derivation, which +# makes offline guessing of a candidate master key memory-hard rather than a bare hash comparison. +_SCRYPT_N = 2**15 +_SCRYPT_R = 8 +_SCRYPT_P = 1 +# scrypt's working-set is ~128 * N * r * p bytes; cap at twice that so the maxmem ceiling scales +# with every work factor and a future p or r bump does not trip "memory limit exceeded". +_SCRYPT_MAXMEM = 128 * _SCRYPT_N * _SCRYPT_R * _SCRYPT_P * 2 +_DERIVED_KEY_BYTES = 32 + + +@lru_cache(maxsize=8) +def envelope_keys_from_master_key(master_key: str) -> EnvelopeKeys: + """Derive the envelope signing and encryption keys from the proxy master key. + + A memory-hard scrypt KDF (RFC 7914) over two distinct domain-label salts yields two + independent 256-bit subkeys from the one secret, so the producer (mint) and consumer + (open) agree on keys without persisting any. scrypt is used rather than a bare hash or + HMAC so that a captured envelope is not a cheap offline oracle for the master key: each + candidate guess costs a full memory-hard derivation, which is what protects a deployment + whose master key is weaker than it should be. The result is cached (the master key is + fixed for a process), so the KDF runs once per key and adds nothing to the per-request + admission path. The derivation is deterministic; rotating ``master_key`` invalidates + every outstanding envelope, which is the intended behavior for a signing-key change. + """ + signing = hashlib.scrypt( + master_key.encode(), + salt=_SIGNING_KEY_DOMAIN, + n=_SCRYPT_N, + r=_SCRYPT_R, + p=_SCRYPT_P, + maxmem=_SCRYPT_MAXMEM, + dklen=_DERIVED_KEY_BYTES, + ).hex() + encryption = hashlib.scrypt( + master_key.encode(), + salt=_ENCRYPTION_KEY_DOMAIN, + n=_SCRYPT_N, + r=_SCRYPT_R, + p=_SCRYPT_P, + maxmem=_SCRYPT_MAXMEM, + dklen=_DERIVED_KEY_BYTES, + ).hex() + return EnvelopeKeys(signing_key=SecretStr(signing), encryption_key=SecretStr(encryption)) + + +def build_bridge_token_response( + identity: EnvelopeIdentity, + grant: UpstreamTokenGrant, + keys: EnvelopeKeys, + now: datetime, +) -> SealedEnvelope | EnvelopeMintError: + """Seal ``grant`` for ``identity`` into the client-held bearer the token endpoint returns. + + The producer mirror of :func:`resolve_bridge_envelope`: a thin, pure wrapper over + :func:`mint_envelope` that returns the sealed envelope, or the mint error as a value + (an oversized grant) for the caller to map onto an OAuth error response. + """ + return mint_envelope(identity, grant, keys, now) + + +class NotBridgeEnvelope(BaseModel): + """The bearer is not an envelope; admission continues on its normal path.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["not_bridge_envelope"] = "not_bridge_envelope" + + +class BridgeEnvelopeAdmitted(BaseModel): + """A valid envelope: the identity to admit under and the full upstream ``Authorization`` + value (``token_type access_token``) to forward to the upstream MCP server.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["admitted"] = "admitted" + identity: EnvelopeIdentity + upstream_authorization: SecretStr + + +class BridgeEnvelopeInvalid(BaseModel): + """The bearer is envelope-shaped but did not open (expired, tampered, wrong key); + admission must fail closed rather than fall through to normal validation.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["invalid"] = "invalid" + + +BridgeEnvelopeResult: TypeAlias = NotBridgeEnvelope | BridgeEnvelopeAdmitted | BridgeEnvelopeInvalid + + +def _strip_bearer(value: str) -> str: + parts = value.split(None, 1) + if len(parts) == 2 and parts[0].lower() == "bearer": + return parts[1] + return value + + +def is_bridge_envelope_shaped(authorization_value: str) -> bool: + """Cheap, keyless test that an ``Authorization`` value carries an envelope (optional + ``Bearer`` scheme stripped). The admission edge engages the bridge arm only for an + envelope, so a plain upstream bearer falls through to normal oauth2 admission.""" + return is_envelope(_strip_bearer(authorization_value)) + + +def resolve_bridge_envelope( + authorization_value: str, + keys: EnvelopeKeys, + now: datetime, + expected_server_id: str, +) -> BridgeEnvelopeResult: + """Classify an ``Authorization`` value presented to a bridge ``oauth_delegate`` server. + + Strips an optional ``Bearer`` scheme, then returns ``NotBridgeEnvelope`` for a + non-envelope bearer (normal admission continues), ``BridgeEnvelopeAdmitted`` with the + recovered identity and the upstream ``Authorization`` value to forward for a valid + envelope, and ``BridgeEnvelopeInvalid`` for an envelope-shaped bearer that will not + open. Never raises: it is total over hostile input via :func:`open_envelope`. + + ``expected_server_id`` is the ``server_id`` of the MCP server the request targets; an + opened envelope whose sealed ``server_id`` does not match is rejected as + ``BridgeEnvelopeInvalid``. Binding here (rather than leaving it to the caller) prevents + replaying an envelope minted for one server against another, which would forward the + first server's upstream credential across a server boundary. ``server_id`` is not a + secret (the caller targets that server), so a plain equality check is sufficient and, + unlike ``hmac.compare_digest`` on ``str``, does not raise on a non-ASCII server_id. + """ + candidate = _strip_bearer(authorization_value) + if not is_envelope(candidate): + return NotBridgeEnvelope() + opened = open_envelope(candidate, keys, now) + if not isinstance(opened, OpenedEnvelope): + return BridgeEnvelopeInvalid() + if opened.identity.server_id != expected_server_id: + return BridgeEnvelopeInvalid() + grant = opened.grant + upstream_authorization = f"{grant.token_type} {grant.access_token.get_secret_value()}" + return BridgeEnvelopeAdmitted(identity=opened.identity, upstream_authorization=SecretStr(upstream_authorization)) diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/envelope.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/envelope.py new file mode 100644 index 00000000000..517c2ef5c8f --- /dev/null +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/envelope.py @@ -0,0 +1,366 @@ +"""Client-held sealed envelope for the oauth_delegate DCR bridge. + +A DCR-bridge client holds ONE bearer that must carry BOTH a litellm identity and the +upstream OAuth grant, with zero server-side storage. The gateway token endpoint mints a +litellm-signed envelope (:func:`mint_envelope`); the MCP edge validates it, recovers the +identity claims and the inner upstream grant (:func:`open_envelope`), and forwards the +inner access token upstream. This module is pure and unwired: it imports nothing from +endpoint or edge code, reads no proxy globals, and takes all key material and the clock +as explicit parameters. + +Wire shape: ``llm_env_`` + an HS256 JWT (same signing approach as the BYOK session +bearer in ``byok_oauth_endpoints.py``). Registered claims are ``iss``/``iat``/``exp``; +custom claims are ``server_id``, ``key_hash``, and ``grant``, where ``grant`` is the +upstream token grant serialized to JSON, encrypted with the repo's symmetric +encryption helpers (``encrypt_value``/``decrypt_value`` from +``encrypt_decrypt_utils`` — the same family ``encrypt_value_helper`` applies to +persisted DCR credentials), and base64url-encoded, so the inner token never appears +in plaintext anywhere in the envelope. + +Failures are values: :func:`open_envelope` returns one of the frozen +``EnvelopeOpenError`` variants (discriminated on ``tag``) for invalid, expired, +tampered, or undecryptable input, and :func:`mint_envelope` returns +``EnvelopeTooLarge`` for oversized grants. Error values carry tags and sizes only, +never token material. + +The pydantic input models reject programmer errors at construction (e.g. a +non-positive ``expires_in`` or an empty required field). :func:`open_envelope` is +additionally total over hostile, attacker-controlled input: it never raises, only +returns an ``EnvelopeOpenError``. :func:`mint_envelope` operates on a +gateway-supplied grant (an upstream IdP's UTF-8 JSON token response), so it does not +defend against non-UTF-8 field content that cannot survive JSON parsing; its only +value-typed failure is ``EnvelopeTooLarge``. +""" + +from __future__ import annotations + +import base64 +from datetime import datetime, timedelta +from typing import Literal, TypeAlias + +import jwt +from pydantic import BaseModel, ConfigDict, Field, SecretStr, ValidationError + +from litellm.proxy.common_utils.encrypt_decrypt_utils import decrypt_value, encrypt_value + +ENVELOPE_PREFIX = "llm_env_" +"""Marker prefix on every serialized envelope so the edge can cheaply tell an envelope +from a raw upstream token before doing any cryptography.""" + +ENVELOPE_ISSUER = "litellm-mcp-bridge" +"""``iss`` claim stamped into every envelope and required back on open.""" + +MAX_ENVELOPE_TTL_SECONDS = 3600 +"""Hard ceiling on envelope lifetime. ``exp`` is ``min(upstream expires_in, this cap)`` +(the cap alone when the upstream omits ``expires_in``), matching the 1h lifetime of the +BYOK session bearer this module's signing approach is borrowed from: a client-held +credential should never outlive a bounded window even when the upstream token does.""" + +MAX_ENVELOPE_BYTES = 12288 +"""Size cap on the final serialized envelope (prefix + JWT, in bytes). Upstream JWTs +commonly run 2-4KB; base64 plus encryption overhead roughly doubles that inside the +envelope, and common proxy/server header limits sit around 16KB total. 12288 leaves +comfortable headroom for a large upstream token while keeping the envelope safely +transmittable as a single Authorization header. Oversized grants are rejected with a +typed error, never truncated.""" + +_ENVELOPE_JWT_ALGORITHM = "HS256" + + +class EnvelopeIdentity(BaseModel): + """The litellm identity the envelope binds the inner grant to. + + ``key_hash`` is the hashed litellm key that authorized the mint, never a raw + credential (and the edge rejects a bare hash presented as a bearer). Admission + reloads the live key record by it, so the key's current team/org/object-permission + restrictions and its revocation state are enforced at use time rather than frozen at + mint time. ``server_id`` binds the envelope to one MCP server so it cannot be replayed + across a server boundary. + """ + + model_config = ConfigDict(frozen=True) + server_id: str = Field(min_length=1) + key_hash: str = Field(min_length=1) + + +class UpstreamTokenGrant(BaseModel): + """The upstream OAuth token response fields sealed inside the envelope. + + ``expires_in`` must be positive when present; a non-positive value is a programmer + error rejected at construction. Token fields are ``SecretStr`` so reprs never leak + them. + """ + + model_config = ConfigDict(frozen=True) + access_token: SecretStr = Field(min_length=1) + token_type: str = Field(min_length=1) + refresh_token: SecretStr | None = None + scope: str | None = None + expires_in: int | None = Field(default=None, gt=0) + + +class EnvelopeKeys(BaseModel): + """Injected key material: the HS256 signing key and the symmetric encryption key. + + ``signing_key`` must be at least 32 bytes: HS256's HMAC-SHA256 has a 256-bit + security level, RFC 7518 requires a key of at least that size, and a shorter key + makes PyJWT emit ``InsecureKeyLengthWarning``. + """ + + model_config = ConfigDict(frozen=True) + signing_key: SecretStr = Field(min_length=32) + encryption_key: SecretStr = Field(min_length=1) + + +class SealedEnvelope(BaseModel): + """A minted envelope: the client-held bearer value and when it expires.""" + + model_config = ConfigDict(frozen=True) + token: SecretStr + expires_at: datetime + + +class OpenedEnvelope(BaseModel): + """A validated envelope: the identity it was minted for and the recovered grant.""" + + model_config = ConfigDict(frozen=True) + identity: EnvelopeIdentity + grant: UpstreamTokenGrant + + +class EnvelopeTooLarge(BaseModel): + """The serialized envelope exceeded ``MAX_ENVELOPE_BYTES``; carries sizes only.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["envelope_too_large"] = "envelope_too_large" + size_bytes: int + max_bytes: int + + +EnvelopeMintError: TypeAlias = EnvelopeTooLarge + + +class NotAnEnvelope(BaseModel): + """The candidate does not carry the envelope prefix.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["not_an_envelope"] = "not_an_envelope" + + +class BadSignature(BaseModel): + """The JWT signature does not verify under the provided signing key.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["bad_signature"] = "bad_signature" + + +class Expired(BaseModel): + """The envelope's ``exp`` is not in the future relative to the provided ``now``.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["expired"] = "expired" + + +class MalformedPayload(BaseModel): + """The token is not a well-formed envelope: undecodable JWT, wrong issuer, missing + or mistyped claims, or a decrypted grant that fails validation.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["malformed_payload"] = "malformed_payload" + + +class DecryptFailed(BaseModel): + """The signed ``grant`` blob could not be decrypted under the provided key.""" + + model_config = ConfigDict(frozen=True) + tag: Literal["decrypt_failed"] = "decrypt_failed" + + +EnvelopeOpenError: TypeAlias = NotAnEnvelope | BadSignature | Expired | MalformedPayload | DecryptFailed + + +class _EnvelopeClaims(BaseModel): + """Decoded-claims boundary that pins the exact shape :func:`mint_envelope` emits. + + ``server_id``/``key_hash`` mirror the ``min_length`` constraints of + :class:`EnvelopeIdentity` so any claim set that validates here also constructs an + identity, keeping :func:`open_envelope` raise-free: a correctly signed JWT with an + empty identity claim fails here and maps to ``MalformedPayload``. + + ``strict`` rejects coerced types (``exp: "123"``, ``exp: 123.0``) rather than opening + on them, and ``extra="forbid"`` rejects any claim the gateway never mints (a hostile + ``nbf``/``aud``/... rides along on a re-signed token). Since PyJWT's own ``iat``/ + ``nbf``/``exp`` validators are disabled at decode (they raise on hostile claim types + and, for ``iat``/``nbf``, compare against the wall clock rather than the injected + ``now``), this model is the sole, total type gate for every registered claim. + """ + + model_config = ConfigDict(frozen=True, strict=True, extra="forbid") + iss: str + iat: int + exp: int + server_id: str = Field(min_length=1) + key_hash: str = Field(min_length=1) + grant: str = Field(min_length=1) + + +class _GrantWire(BaseModel): + model_config = ConfigDict(frozen=True) + access_token: str + token_type: str + refresh_token: str | None = None + scope: str | None = None + expires_in: int | None = None + + +def is_envelope(candidate: str) -> bool: + """Cheap prefix check so the edge can route envelopes vs raw tokens without crypto.""" + return candidate.startswith(ENVELOPE_PREFIX) + + +def mint_envelope( + identity: EnvelopeIdentity, + grant: UpstreamTokenGrant, + keys: EnvelopeKeys, + now: datetime, +) -> SealedEnvelope | EnvelopeMintError: + """Seal ``grant`` for ``identity`` into a client-held envelope. + + ``exp`` is ``min(grant.expires_in, MAX_ENVELOPE_TTL_SECONDS)`` seconds from ``now`` + (the cap alone when ``expires_in`` is absent). Returns ``EnvelopeTooLarge`` when the + serialized envelope exceeds ``MAX_ENVELOPE_BYTES``. + """ + expires_at = now + timedelta(seconds=_envelope_ttl_seconds(grant.expires_in)) + claims = _EnvelopeClaims( + iss=ENVELOPE_ISSUER, + iat=int(now.timestamp()), + exp=int(expires_at.timestamp()), + server_id=identity.server_id, + key_hash=identity.key_hash, + grant=_encrypt_grant_blob(_grant_plaintext(grant), keys.encryption_key), + ) + token = ENVELOPE_PREFIX + jwt.encode( + claims.model_dump(), + keys.signing_key.get_secret_value(), + algorithm=_ENVELOPE_JWT_ALGORITHM, + ) + size_bytes = len(token.encode("utf-8")) + if size_bytes > MAX_ENVELOPE_BYTES: + return EnvelopeTooLarge(size_bytes=size_bytes, max_bytes=MAX_ENVELOPE_BYTES) + return SealedEnvelope(token=SecretStr(token), expires_at=expires_at) + + +def open_envelope( + candidate: str, + keys: EnvelopeKeys, + now: datetime, +) -> OpenedEnvelope | EnvelopeOpenError: + """Validate ``candidate`` and recover the identity and inner grant. + + Never raises for bad input: every invalid, expired, tampered, or undecryptable + candidate maps to a distinct ``EnvelopeOpenError`` variant. The recovered + ``grant.expires_in`` is the value the upstream reported at mint time and is not + re-derived, so it is stale by up to the envelope's lifetime; callers that need a + live remaining lifetime should use ``now`` against the upstream, not this field. + """ + if not is_envelope(candidate): + return NotAnEnvelope() + # UTF-8 byte length is never below character length, so a character count already over the + # cap rejects an oversize candidate in O(1) without encoding it; the exact byte check then + # runs only on candidates already bounded to <= MAX_ENVELOPE_BYTES characters. + if len(candidate) > MAX_ENVELOPE_BYTES: + return MalformedPayload() + if len(candidate.encode("utf-8", "surrogatepass")) > MAX_ENVELOPE_BYTES: + return MalformedPayload() + claims = _decode_claims(candidate.removeprefix(ENVELOPE_PREFIX), keys.signing_key) + if not isinstance(claims, _EnvelopeClaims): + return claims + if now.timestamp() >= claims.exp: + return Expired() + grant = _decrypt_grant(claims.grant, keys.encryption_key) + if not isinstance(grant, UpstreamTokenGrant): + return grant + return OpenedEnvelope( + identity=EnvelopeIdentity(server_id=claims.server_id, key_hash=claims.key_hash), + grant=grant, + ) + + +def _envelope_ttl_seconds(upstream_expires_in: int | None) -> int: + if upstream_expires_in is None: + return MAX_ENVELOPE_TTL_SECONDS + return min(upstream_expires_in, MAX_ENVELOPE_TTL_SECONDS) + + +def _grant_plaintext(grant: UpstreamTokenGrant) -> str: + wire = _GrantWire( + access_token=grant.access_token.get_secret_value(), + token_type=grant.token_type, + refresh_token=None if grant.refresh_token is None else grant.refresh_token.get_secret_value(), + scope=grant.scope, + expires_in=grant.expires_in, + ) + return wire.model_dump_json(exclude_none=True) + + +def _decode_claims( + compact: str, + signing_key: SecretStr, +) -> _EnvelopeClaims | BadSignature | MalformedPayload: + """Verify the HS256 signature and shape of an attacker-controlled compact JWT. + + ``compact`` is fully hostile and bounded to ``MAX_ENVELOPE_BYTES`` by the caller. + PyJWT's ``iat``/``nbf``/``exp`` validators are disabled: they raise on hostile claim + types and, for ``iat``/``nbf``, compare against the wall clock rather than the + injected ``now`` (``exp`` is checked by the caller against ``now``). Apart from a + signature mismatch (``BadSignature``), every decode failure is ``MalformedPayload``: + a non-UTF-8 candidate surfaces as ``UnicodeEncodeError`` (a ``ValueError``), a + non-string registered claim such as ``iss`` as a ``TypeError`` from PyJWT's claim + validators, and a wrong issuer or structurally invalid token as an + ``InvalidTokenError``. ``_EnvelopeClaims`` is the total type gate for the payload. + """ + try: + payload = jwt.decode( + compact, + signing_key.get_secret_value(), + algorithms=[_ENVELOPE_JWT_ALGORITHM], + issuer=ENVELOPE_ISSUER, + options={ + "verify_exp": False, + "verify_iat": False, + "verify_nbf": False, + "require": ["iss", "iat", "exp"], + }, + ) + except jwt.InvalidSignatureError: + return BadSignature() + except (jwt.InvalidTokenError, ValueError, TypeError): + return MalformedPayload() + try: + return _EnvelopeClaims.model_validate(payload) + except ValidationError: + return MalformedPayload() + + +def _encrypt_grant_blob(plaintext: str, encryption_key: SecretStr) -> str: + ciphertext = bytes(encrypt_value(value=plaintext, signing_key=encryption_key.get_secret_value())) + return base64.urlsafe_b64encode(ciphertext).decode("ascii") + + +def _decrypt_grant( + blob: str, + encryption_key: SecretStr, +) -> UpstreamTokenGrant | DecryptFailed | MalformedPayload: + from nacl.exceptions import CryptoError + + try: + plaintext = decrypt_value( + value=base64.urlsafe_b64decode(blob), + signing_key=encryption_key.get_secret_value(), + ) + except (CryptoError, ValueError): + return DecryptFailed() + try: + return UpstreamTokenGrant.model_validate_json(plaintext) + except ValidationError: + return MalformedPayload() diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/oauth_token_store.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/oauth_token_store.py index fd2cb2f3e06..c1c70cf9050 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/oauth_token_store.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/oauth_token_store.py @@ -69,6 +69,17 @@ class OAuthTokenStore(Protocol): async def fetch(self, user_id: str, server_id: str) -> OAuthToken | None: ... +class InvalidatableOAuthTokenStore(OAuthTokenStore, Protocol): + """An ``OAuthTokenStore`` whose cached entry for a ``(user, server)`` pair can be dropped. + + The write side calls ``invalidate`` after a (re)authorization or revocation changes the + credential row, so reads stop serving the replaced token immediately instead of until its + cache TTL. ``CachedOAuthTokenStore`` (the top of the per-user chain) satisfies this. + """ + + async def invalidate(self, user_id: str, server_id: str) -> None: ... + + class TokenRefresher(Protocol): """Mints a fresh token from an expired one and persists it, returning the new token. diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/per_user_oauth_store.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/per_user_oauth_store.py index 3bc10f1a0eb..21001c09f25 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/per_user_oauth_store.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/per_user_oauth_store.py @@ -24,8 +24,8 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.dual_cache_toke ) from litellm.proxy._experimental.mcp_server.outbound_credentials.oauth_token_store import ( CachedOAuthTokenStore, + InvalidatableOAuthTokenStore, OAuthToken, - OAuthTokenStore, RefreshCoordinator, RefreshingTokenStore, TokenCacheBackend, @@ -51,7 +51,7 @@ if TYPE_CHECKING: _DEFAULT_TTL_SECONDS = 300.0 ServerLookup = Callable[[str], "MCPServer | None"] -StoreBuilder = Callable[[ServerLookup], tuple[OAuthTokenStore, bool]] +StoreBuilder = Callable[[ServerLookup], tuple[InvalidatableOAuthTokenStore, bool]] async def _read_credential(user_id: str, server_id: str) -> dict[str, object] | None: @@ -185,7 +185,7 @@ class LazyPerUserOAuthTokenStore: self._server_lookup = server_lookup self._store_builder = store_builder self._redis_available = redis_available - self._store: OAuthTokenStore | None = None + self._store: InvalidatableOAuthTokenStore | None = None self._uses_redis = False self._fetch_lock = asyncio.Condition() self._local_fetches = 0 @@ -203,7 +203,26 @@ class LazyPerUserOAuthTokenStore: if not uses_redis: await self._finish_local_fetch() - async def _store_for_fetch(self) -> tuple[OAuthTokenStore, bool]: + async def invalidate(self, user_id: str, server_id: str) -> None: + """Drop the chain's cached entry for ``(user_id, server_id)`` after the credential row + changes (re-auth, revoke). Builds the chain if no fetch has run yet, so a shared (Redis) + cache entry written by another worker is dropped too; the in-process case is then a no-op + on an empty cache. + """ + if self._uses_redis: + store = self._store + if store is not None: + await store.invalidate(user_id, server_id) + return + + store, uses_redis = await self._store_for_fetch() + try: + await store.invalidate(user_id, server_id) + finally: + if not uses_redis: + await self._finish_local_fetch() + + async def _store_for_fetch(self) -> tuple[InvalidatableOAuthTokenStore, bool]: async with self._fetch_lock: while ( self._store is not None and not self._uses_redis and self._redis_available() and self._local_fetches > 0 diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/resolver.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/resolver.py index f9a9fa00b23..ecfd471190c 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/resolver.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/resolver.py @@ -7,9 +7,11 @@ no precedence cascade. It is wildcard-free with an `assert_never` tail, so addin an arm fails the type gate (basedpyright `reportMatchNotExhaustive`); a bypassed gate fails loudly at runtime instead of returning `None`. -`none` and `api_key` (shared-key source) are live, as is `authorization_code`, which reads the -user's token from the injected `OAuthTokenStore`. The remaining arms are `not_implemented` stubs -that each land in a follow-up PR with their seam. Pure v2: no imports from v1. +`none`, `api_key` (shared-key source), and `passthrough` (forwards the caller's own inbound token) +are live, as is `authorization_code`, which reads the user's token from the injected +`OAuthTokenStore`, and `token_exchange`, which swaps the caller's inbound token through the +injected `TokenExchanger`. The remaining arms are `not_implemented` stubs that each land in a +follow-up PR with their seam. Pure v2: no imports from v1. """ from __future__ import annotations @@ -31,6 +33,9 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.result import ( Ok, Result, ) +from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger import ( + TokenExchanger, +) from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( ApiKeyConfig, AuthorizationCodeConfig, @@ -55,16 +60,36 @@ class _NullOAuthTokenStore: return None +class _NullTokenExchanger: + """Fail-closed default: with no exchanger wired, token_exchange cannot produce a credential.""" + + async def exchange( + self, subject_token: str, server: ServerSpec, config: TokenExchangeConfig, *, tenant_id: str = "" + ) -> Result[OAuthToken, CredError]: + return Error(CredError.of_misconfigured("token exchange collaborator not wired")) + + async def invalidate( + self, subject_token: str, server: ServerSpec, config: TokenExchangeConfig, *, tenant_id: str = "" + ) -> None: + return None + + class UpstreamCredentialProvider: """Produces the one `httpx.Auth` for a `(subject, upstream)` pair, per declared mode. Collaborators (the per-mode credential stores and token fetchers) are injected as each arm is built; the live `none` and `api_key`-shared arms read from the config and need none, while - `authorization_code` reads the user's token from the injected `OAuthTokenStore`. + `authorization_code` reads the user's token from the injected `OAuthTokenStore` and + `token_exchange` swaps the caller's token through the injected `TokenExchanger`. """ - def __init__(self, oauth_token_store: OAuthTokenStore | None = None) -> None: + def __init__( + self, + oauth_token_store: OAuthTokenStore | None = None, + token_exchanger: TokenExchanger | None = None, + ) -> None: self._oauth_token_store: OAuthTokenStore = oauth_token_store or _NullOAuthTokenStore() + self._token_exchanger: TokenExchanger = token_exchanger or _NullTokenExchanger() async def resolve_credentials(self, subject: Subject, server: ServerSpec) -> Result[httpx.Auth, CredError]: match server.config: @@ -73,11 +98,11 @@ class UpstreamCredentialProvider: case ApiKeyConfig() as config: return self._api_key(config) case PassthroughConfig(): - return _not_implemented(AuthSpecKind.passthrough) + return self._passthrough(subject) case ClientCredentialsConfig(): return _not_implemented(AuthSpecKind.client_credentials) - case TokenExchangeConfig(): - return _not_implemented(AuthSpecKind.token_exchange) + case TokenExchangeConfig() as config: + return await self._token_exchange(subject, server, config) case AuthorizationCodeConfig(): return await self._authorization_code(subject, server) case AwsSigV4Config(): @@ -94,6 +119,18 @@ class UpstreamCredentialProvider: """ return await self._authz_token(subject, server) is not None + def _passthrough(self, subject: Subject) -> Result[httpx.Auth, CredError]: + """Forward the caller's own upstream credential verbatim; the gateway mints nothing. + + The inbound token is the caller's already-disambiguated ``Authorization`` (never the LiteLLM + admission credential; the edge adapter drops that before building the ``Subject``). When it is + absent the request is sent unauthenticated so the upstream's own 401 surfaces, rather than the + gateway challenging on the upstream's behalf. + """ + if subject.inbound_token is None: + return Ok(NoOpAuth()) + return Ok(StaticHeaderAuth(subject.inbound_token.get_secret_value(), header_name="Authorization")) + def _api_key(self, config: ApiKeyConfig) -> Result[httpx.Auth, CredError]: match config.key_source: case SharedKey() as source: @@ -110,6 +147,43 @@ class UpstreamCredentialProvider: return Error(CredError.of_unauthorized("Authorization required: complete the OAuth flow for this server.")) return Ok(StaticHeaderAuth(f"Bearer {token.access_token}", header_name="Authorization")) + async def _token_exchange( + self, subject: Subject, server: ServerSpec, config: TokenExchangeConfig + ) -> Result[StaticHeaderAuth, CredError]: + """RFC 8693 OBO: exchange the caller's inbound token for an upstream-bound bearer. + + No inbound token means there is nothing to exchange, so the arm fails closed with a 401 rather + than falling through to a weaker source (§1.5); the exchanger handles the IdP round-trip and + caching and returns the upstream token or a typed error. + """ + inbound = subject.inbound_token + if inbound is None: + return Error( + CredError.of_unauthorized( + "Token exchange requires a caller token to exchange (OBO).", + www_authenticate='Bearer error="invalid_request"', + ) + ) + match await self._token_exchanger.exchange( + inbound.get_secret_value(), server, config, tenant_id=subject.tenant_id + ): + case Ok(token): + return Ok(StaticHeaderAuth(f"Bearer {token.access_token}", header_name="Authorization")) + case Error(err): + return Error(err) + + async def invalidate_credentials(self, subject: Subject, server: ServerSpec) -> None: + """Drop any cached credential the resolver owns for this `(subject, server)`. + + Used after an upstream rejects the injected credential, so the next resolve re-mints rather + than serving the same rejected token until TTL. Only `token_exchange` holds a re-mintable + cached credential here; other modes are a no-op. + """ + if isinstance(server.config, TokenExchangeConfig) and subject.inbound_token is not None: + await self._token_exchanger.invalidate( + subject.inbound_token.get_secret_value(), server, server.config, tenant_id=subject.tenant_id + ) + async def _authz_token(self, subject: Subject, server: ServerSpec) -> OAuthToken | None: """The user's authorization_code token, or None when absent or the store is unreachable. diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/token_exchange_provider.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/token_exchange_provider.py new file mode 100644 index 00000000000..e49de4559c6 --- /dev/null +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/token_exchange_provider.py @@ -0,0 +1,114 @@ +"""Composition root for the v2-native token_exchange (OBO) exchanger. + +Wires the pure ``OboTokenExchanger`` to its runtime edges: the real httpx POST against the IdP and +the configured cache sizing/TTL constants. ``build_token_exchanger`` is built once at egress +construction and reused, so the in-process exchanged-token cache survives across requests. Unlike the +per-user store, nothing here reads a runtime global at build time (the httpx client is acquired per +call), so it needs no lazy wrapper. +""" + +from __future__ import annotations + +import httpx + +from litellm._logging import verbose_logger +from litellm.constants import ( + MCP_OAUTH2_TOKEN_CACHE_DEFAULT_TTL, + MCP_OAUTH2_TOKEN_CACHE_MIN_TTL, + MCP_OAUTH2_TOKEN_EXPIRY_BUFFER_SECONDS, + MCP_TOKEN_EXCHANGE_CACHE_MAX_SIZE, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.oauth_token_store import ( + InMemoryTokenCacheBackend, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger import ( + OboTokenExchanger, + SubjectTokenRejected, + TokenExchangeClientError, +) + +# RFC 6749 5.2 error codes that mean the gateway's own request/credentials are wrong (not the +# caller's subject token), so they surface as a 500 the caller can't fix by re-authenticating. +_GATEWAY_FAULT_OAUTH_ERRORS = frozenset( + {"invalid_client", "unauthorized_client", "unsupported_grant_type", "invalid_target", "invalid_scope"} +) + + +def _oauth_error_fields(response: httpx.Response) -> tuple[str | None, str | None]: + """Read the RFC 6749 5.2 ``error`` code and the IdP's step-up ``claims`` blob from a + token-endpoint error body, as ``(error, claims)`` with None for whatever is absent. + + ``claims`` is the Entra Conditional Access / CAE challenge (a JSON string the client must + replay to the IdP to satisfy the step-up); it is the caller's own requirement, not an IdP + internal, so it may travel to the caller. The ``error_description`` is deliberately not read: + it can carry IdP internals and must never reach the caller. + """ + try: + body: object = response.json() + except Exception: # noqa: BLE001 + return None, None + if not isinstance(body, dict): + return None, None + code = body.get("error") + claims = body.get("claims") + return ( + code if isinstance(code, str) else None, + claims if isinstance(claims, str) and claims else None, + ) + + +async def _post_exchange_endpoint( + url: str, form: dict[str, str], client_auth_headers: dict[str, str] +) -> dict[str, object] | None: + from litellm.llms.custom_httpx.http_handler import ( # noqa: PLC0415 + get_async_httpx_client, # pyright: ignore + ) + from litellm.types.llms.custom_http import httpxSpecialProvider # noqa: PLC0415 + + # litellm's httpx handler and httpx.Response are only partially typed; the IdP returns a JSON + # object and the exchanger validates each field, so the untyped boundary is contained here. + # A 4xx is the IdP rejecting the subject (non-retryable -> 401 via SubjectTokenRejected); any + # other failure is a miss (-> None -> upstream_unavailable -> 503), matching v1's fail-closed. + headers = {"Accept": "application/json", **client_auth_headers} + try: + client = get_async_httpx_client(llm_provider=httpxSpecialProvider.MCP) # pyright: ignore + response = await client.post(url, headers=headers, data=form) # pyright: ignore + response.raise_for_status() # pyright: ignore + parsed: object = response.json() # pyright: ignore + except httpx.HTTPStatusError as status_err: + status_code = status_err.response.status_code + if 400 <= status_code < 500: + oauth_error, claims = _oauth_error_fields(status_err.response) + if oauth_error in _GATEWAY_FAULT_OAUTH_ERRORS: + verbose_logger.warning( + "MCP token exchange rejected as %s (HTTP %d); check the gateway client credentials, " + "audience, and scope for this server", + oauth_error, + status_code, + ) + raise TokenExchangeClientError(oauth_error) from status_err + raise SubjectTokenRejected( + f"IdP rejected the subject token (HTTP {status_code})", + claims=claims, + ) from status_err + verbose_logger.warning("MCP token exchange request failed: %s", status_err) + return None + except Exception as exc: # noqa: BLE001 + verbose_logger.warning("MCP token exchange request failed: %s", exc) + return None + if not isinstance(parsed, dict): + # A valid-but-non-object JSON body (list/string/number) would crash the field parsing; map it + # to a miss so it surfaces as a typed upstream_unavailable, not a 500. + verbose_logger.warning("MCP token exchange returned non-object JSON (%s)", type(parsed).__name__) + return None + return parsed # pyright: ignore + + +def build_token_exchanger() -> OboTokenExchanger: + return OboTokenExchanger( + _post_exchange_endpoint, + cache=InMemoryTokenCacheBackend(max_size=MCP_TOKEN_EXCHANGE_CACHE_MAX_SIZE), + default_ttl_seconds=MCP_OAUTH2_TOKEN_CACHE_DEFAULT_TTL, + min_ttl_seconds=MCP_OAUTH2_TOKEN_CACHE_MIN_TTL, + expiry_buffer_seconds=MCP_OAUTH2_TOKEN_EXPIRY_BUFFER_SECONDS, + ) diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/token_exchanger.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/token_exchanger.py new file mode 100644 index 00000000000..02b6d4eafb1 --- /dev/null +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/token_exchanger.py @@ -0,0 +1,376 @@ +"""v2-native OBO token exchange: swap the caller's token for an upstream-bound one. + +The pure core of the ``token_exchange`` mode. Given the caller's inbound token and the server's +``TokenExchangeConfig``, ``OboTokenExchanger.exchange`` POSTs the grant selected by ``config.profile`` +to the configured endpoint and returns the upstream-bound ``access_token`` as a typed ``OAuthToken``, +or a typed ``CredError`` - never a raise (the HTTP edge is the injected ``ExchangeHttpPost``, whose +adapter contains the I/O). Two profiles share this one engine: ``rfc8693`` (the RFC 8693 token-exchange +grant) and ``entra_obo`` (Microsoft Entra On-Behalf-Of, which is the RFC 7523 ``jwt-bearer`` grant); +only the request form differs, so the cache, single-flight, and TTL machinery are dialect-agnostic. The +exchanged token is cached and single-flighted per ``(subject_token, tenant, config, server)`` so a +repeated caller token skips the IdP round-trip and concurrent calls collapse to one exchange, reusing +the shared in-process cache + coordinator foundation. A rotated caller token hashes to a new key and +re-exchanges. Pure v2 apart from the shared RFC 6749 client-auth helper, which carries no v1 state. + +A missing/expired exchange is an error, never a fall-through to a weaker source (§1.5): the caller +presenting no token is the resolver arm's 401, and an IdP that does not return a usable token is an +``upstream_unavailable`` here. +""" + +from __future__ import annotations + +import hashlib +import time +from collections.abc import Awaitable, Callable +from typing import Literal, Protocol + +from typing_extensions import assert_never + +from litellm._logging import verbose_logger +from litellm.proxy._experimental.mcp_server.outbound_credentials.oauth_token_store import ( + InMemoryTokenCacheBackend, + InProcessRefreshCoordinator, + OAuthToken, + RefreshCoordinator, + TokenCacheBackend, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.result import ( + Error, + Ok, + Result, +) +from litellm.proxy._experimental.mcp_server.auth.token_endpoint_auth import ( + build_token_endpoint_client_auth, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( + CredError, + ServerSpec, + TokenExchangeConfig, +) + +# A token with no declared expiry is cached for this long; one with an expiry is cached until then +# minus the skew buffer, floored at the minimum. Values mirror v1's MCP_OAUTH2_* constants; the +# composition root injects the configured ones. +_DEFAULT_TTL_SECONDS = 3600.0 +_MIN_TTL_SECONDS = 10.0 +_EXPIRY_BUFFER_SECONDS = 60.0 + +_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange" +# Microsoft Entra On-Behalf-Of speaks the RFC 7523 jwt-bearer grant, not RFC 8693, and gates delegation +# behind ``requested_token_use=on_behalf_of`` (a Microsoft extension present in neither RFC). +_JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer" +_REQUESTED_TOKEN_USE_OBO = "on_behalf_of" + +# RFC 8693 3 token-type URNs that are not usable as an upstream Bearer access token. token_type +# already rejects the common non-access case (N_A); this catches a malformed STS that mints one of +# these but still labels it Bearer. An access_token / jwt / absent / unknown type is accepted (lenient). +_NON_ACCESS_ISSUED_TOKEN_TYPES = frozenset( + { + "urn:ietf:params:oauth:token-type:refresh_token", + "urn:ietf:params:oauth:token-type:id_token", + "urn:ietf:params:oauth:token-type:saml1", + "urn:ietf:params:oauth:token-type:saml2", + } +) + +# The IdP returns an opaque JSON object; the post adapter hands it over untyped and the exchanger +# validates each field, so no Any leaks past this seam (None == any transport/HTTP failure). The +# second dict is the form body; the third is the client-auth headers (HTTP Basic for +# client_secret_basic, empty for client_secret_post). +ExchangeHttpPost = Callable[[str, "dict[str, str]", "dict[str, str]"], Awaitable["dict[str, object] | None"]] + + +class SubjectTokenRejected(Exception): + """The IdP refused to exchange the subject token (an RFC 8693 4xx, e.g. ``invalid_grant``). + + Distinct from a transport / IdP-availability failure, which the post adapter maps to ``None`` -> + ``upstream_unavailable`` -> 503 (retryable). A rejected subject is the caller's problem, not the + gateway's, so the arm surfaces it as a non-retryable 401 (the OBO challenge) instead. + ``claims`` is the IdP's step-up challenge blob (Entra Conditional Access / CAE) from the + rejection body; it threads into the 401 challenge so the client can satisfy the step-up and + retry. The ``error_description`` is never carried (it can leak IdP internals). + """ + + def __init__(self, detail: str, *, claims: str | None = None) -> None: + super().__init__(detail) + self.claims = claims + + +class TokenExchangeClientError(Exception): + """The IdP rejected the exchange for a reason that is the gateway's fault, not the caller's. + + RFC 6749 5.2 codes such as ``invalid_client`` (the gateway's own STS credentials are wrong), + ``unauthorized_client`` / ``unsupported_grant_type`` (the gateway is not permitted to exchange), + ``invalid_target`` / ``invalid_scope`` (the gateway's audience/scope config for this server is + wrong). The caller cannot fix these by re-authenticating, so the arm surfaces them as a 500 + (``misconfigured``), not the 401 OBO challenge. The IdP ``error_description`` is never carried. + """ + + +class TokenExchanger(Protocol): + """Exchanges a caller token for an upstream-bound one, per the server's token_exchange config.""" + + async def exchange( + self, subject_token: str, server: ServerSpec, config: TokenExchangeConfig, *, tenant_id: str = "" + ) -> Result[OAuthToken, CredError]: ... + + async def invalidate( + self, subject_token: str, server: ServerSpec, config: TokenExchangeConfig, *, tenant_id: str = "" + ) -> None: ... + + +def _cache_key(subject_token: str, tenant_id: str, config: TokenExchangeConfig) -> str: + """Bind the cache entry to the caller token, the tenant, AND the exchange config that minted it. + + A rotated caller token, a different tenant, profile, endpoint, audience, scope, client_id, secret, + auth method, or subject_token_type all change the key, so two tenants behind the same opaque token + never share an entry and a config change (including a profile flip that alters the wire form) + forces a fresh exchange instead of serving a token minted for the old config until TTL. Everything + is hashed, so no secret is held in the key. + """ + secret = config.client_secret.get_secret_value() if config.client_secret else "" + material = "\x00".join( + ( + subject_token, + tenant_id, + config.profile, + config.token_exchange_endpoint or "", + config.audience or "", + config.subject_token_type, + config.client_id or "", + secret, + config.token_endpoint_auth_method or "", + " ".join(config.scopes), + ) + ) + return hashlib.sha256(material.encode()).hexdigest() + + +def _parse_expires_in(raw: object) -> int | None: + if isinstance(raw, bool): + return None + if isinstance(raw, (int, float)): + return int(raw) + if isinstance(raw, str): + try: + return int(float(raw)) + except ValueError: + return None + return None + + +def _rfc8693_form( + *, + subject_token: str, + subject_token_type: str, + audience: str | None, + scopes: tuple[str, ...], +) -> dict[str, str]: + return { + "grant_type": _GRANT_TYPE, + "subject_token": subject_token, + "subject_token_type": subject_token_type, + **({"audience": audience} if audience else {}), + **({"scope": " ".join(scopes)} if scopes else {}), + } + + +def _entra_obo_form( + *, + subject_token: str, + scopes: tuple[str, ...], +) -> dict[str, str]: + # Microsoft Entra On-Behalf-Of (RFC 7523 jwt-bearer, not RFC 8693): the caller's inbound access + # token rides as ``assertion`` (its ``aud`` must be this gateway's ``client_id``); the target + # resource is carried in ``scope`` (e.g. api:///.default), since Entra has no audience + # parameter and ignores subject_token_type; ``requested_token_use=on_behalf_of`` is the Microsoft + # extension that turns the jwt-bearer grant into a delegation. ``scope`` is required, and the + # exchange precondition rejects an empty one, so it is always present here. Client authentication + # (client_id/client_secret via post, or Basic) is layered on by the caller through + # build_token_endpoint_client_auth, so it is not built into the form here. + return { + "grant_type": _JWT_BEARER_GRANT_TYPE, + "assertion": subject_token, + "scope": " ".join(scopes), + "requested_token_use": _REQUESTED_TOKEN_USE_OBO, + } + + +def _build_exchange_form( + *, + profile: Literal["rfc8693", "entra_obo"], + subject_token: str, + subject_token_type: str, + audience: str | None, + scopes: tuple[str, ...], +) -> dict[str, str]: + match profile: + case "rfc8693": + return _rfc8693_form( + subject_token=subject_token, + subject_token_type=subject_token_type, + audience=audience, + scopes=scopes, + ) + case "entra_obo": + return _entra_obo_form( + subject_token=subject_token, + scopes=scopes, + ) + assert_never(profile) + + +class OboTokenExchanger: + """``TokenExchanger`` that runs the profile's OBO grant once per caller token, then caches the result. + + The HTTP post is injected (``None`` on any IdP failure, mirroring v1: a failed exchange is a miss, + not a 500). The cache and single-flight coordinator default to the in-process foundation; a + deployment with no shared state needs nothing more (v1's exchanged-token cache is per-process too). + The clock is injected so TTL/expiry is deterministic in tests. + """ + + def __init__( + self, + http_post: ExchangeHttpPost, + *, + cache: TokenCacheBackend | None = None, + coordinator: RefreshCoordinator | None = None, + clock: Callable[[], float] = time.time, + default_ttl_seconds: float = _DEFAULT_TTL_SECONDS, + min_ttl_seconds: float = _MIN_TTL_SECONDS, + expiry_buffer_seconds: float = _EXPIRY_BUFFER_SECONDS, + ) -> None: + self._http_post = http_post + self._cache: TokenCacheBackend = cache or InMemoryTokenCacheBackend(clock=clock) + self._coordinator: RefreshCoordinator = coordinator or InProcessRefreshCoordinator() + self._clock = clock + self._default_ttl_seconds = default_ttl_seconds + self._min_ttl_seconds = min_ttl_seconds + self._expiry_buffer_seconds = expiry_buffer_seconds + + async def exchange( + self, subject_token: str, server: ServerSpec, config: TokenExchangeConfig, *, tenant_id: str = "" + ) -> Result[OAuthToken, CredError]: + endpoint = config.token_exchange_endpoint + client_id = config.client_id + client_secret = config.client_secret + if not endpoint: + # No endpoint configured and none discoverable: fail closed (412) rather than guess an IdP + # or fall back to a weaker source. The caller's token is never sent anywhere. + return Error( + CredError.of_precondition_required("token exchange endpoint is not configured for this server") + ) + if not client_id or client_secret is None: + return Error(CredError.of_misconfigured("token_exchange requires client_id and client_secret")) + if config.profile == "entra_obo" and not config.scopes: + # Entra carries the target resource in ``scope`` (api:///.default); with no scope the + # IdP cannot resolve an audience, so fail closed as misconfigured rather than POST a form the + # IdP will reject. + return Error( + CredError.of_misconfigured("entra_obo token exchange requires a scope (e.g. api:///.default)") + ) + + cache_key = _cache_key(subject_token, tenant_id, config) + server_id = server.server_id + cached = await self._cache.get(cache_key, server_id) + if cached is not None: + verbose_logger.debug("MCP token exchange cache hit for server %s", server_id) + return Ok(cached) + + client_auth = build_token_endpoint_client_auth( + auth_method=config.token_endpoint_auth_method, + client_id=client_id, + client_secret=client_secret.get_secret_value(), + ) + form = { + **_build_exchange_form( + profile=config.profile, + subject_token=subject_token, + subject_token_type=config.subject_token_type, + audience=config.audience, + scopes=config.scopes, + ), + **client_auth.body, + } + + async def run_exchange() -> OAuthToken | None: + fresh = await self._cache.get(cache_key, server_id) + if fresh is not None: + return fresh + verbose_logger.debug( + "Exchanging token for MCP server %s at %s (audience=%s)", server_id, endpoint, config.audience + ) + body = await self._http_post(endpoint, form, client_auth.headers) + if body is None: + return None + token = self._token_from_body(body) + if token is None: + return None + await self._cache.set(cache_key, server_id, token, self._ttl_seconds(token)) + verbose_logger.info("Token exchange succeeded for MCP server %s", server_id) + return token + + async def reread() -> OAuthToken | None: + return await self._cache.get(cache_key, server_id) + + try: + token = await self._coordinator.run(cache_key, server_id, refresh=run_exchange, reread=reread) + except SubjectTokenRejected as rejected: + # The IdP rejected the subject token (4xx). This is non-retryable: the caller must + # re-authenticate with the IdP, so it surfaces as a 401 (the OBO challenge), not a 503. + # A step-up rejection (Entra Conditional Access) carries the claims blob through so the + # edge's challenge tells the client how to satisfy it. + return Error( + CredError.of_unauthorized( + str(rejected) or "subject token rejected by the IdP", + claims=rejected.claims, + ) + ) + except TokenExchangeClientError: + # RFC 6749 5.2 gateway-fault code (invalid_client / invalid_target / ...): the caller can't + # fix it by re-authenticating, so surface a 500 rather than the OBO 401 challenge. The + # specific code is logged at the edge; the user-facing summary stays generic. + return Error( + CredError.of_misconfigured( + "token exchange configuration error: the gateway's credentials, audience, or scope " + "for this server were not accepted by the IdP" + ) + ) + if token is None: + return Error(CredError.of_upstream_unavailable("token exchange did not return a usable access token")) + return Ok(token) + + async def invalidate( + self, subject_token: str, server: ServerSpec, config: TokenExchangeConfig, *, tenant_id: str = "" + ) -> None: + """Drop the cached exchanged token so the next call re-exchanges (e.g. after an upstream 401).""" + await self._cache.delete(_cache_key(subject_token, tenant_id, config), server.server_id) + + def _token_from_body(self, body: dict[str, object]) -> OAuthToken | None: + access_token = body.get("access_token") + if not isinstance(access_token, str) or not access_token: + return None + # token_type is forwarded downstream as Bearer, so a present-but-non-Bearer type (e.g. N_A) + # must fail closed rather than be minted as a bogus Bearer; an absent type defaults to Bearer. + token_type = body.get("token_type") + if isinstance(token_type, str) and token_type.strip().lower() != "bearer": + verbose_logger.warning( + "MCP token exchange returned unusable token_type %r; refusing to forward it as Bearer", token_type + ) + return None + # issued_token_type says what representation was minted; reject a clearly-non-access type + # (refresh/id/saml) even if token_type claimed Bearer. access_token / jwt / absent / unknown pass. + issued_token_type = body.get("issued_token_type") + if isinstance(issued_token_type, str) and issued_token_type in _NON_ACCESS_ISSUED_TOKEN_TYPES: + return None + expires_in = _parse_expires_in(body.get("expires_in")) + expires_at = self._clock() + expires_in if expires_in is not None else None + return OAuthToken(access_token=access_token, expires_at=expires_at) + + def _ttl_seconds(self, token: OAuthToken) -> float: + if token.expires_at is None: + return self._default_ttl_seconds + lifetime = max(0.0, token.expires_at - self._clock()) + # Floor at min_ttl, but never cache past the token's own expiry: a token whose remaining + # lifetime is below the buffer (or even below min_ttl) must not be served stale upstream. + return min(max(lifetime - self._expiry_buffer_seconds, self._min_ttl_seconds), lifetime) diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/types.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/types.py index 671de63eabe..7e04be4f045 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/types.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/types.py @@ -39,6 +39,7 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.result import ( Ok, Result, ) +from litellm.types.mcp import DEFAULT_SUBJECT_TOKEN_TYPE class AuthSpecKind(str, Enum): @@ -67,11 +68,15 @@ class Unauthorized: ``detail`` is the human message; ``www_authenticate`` and ``body`` carry a scheme-specific challenge (e.g. BYOK's provisioning prompt) so the edge can reproduce it verbatim. + ``claims`` carries an IdP step-up challenge (e.g. Entra Conditional Access) so the edge can + fold it into the ``WWW-Authenticate`` it builds; the client replays the claims to the IdP to + satisfy the step-up, then retries with the fresh token. """ detail: str www_authenticate: str | None = None body: Mapping[str, str] | None = None + claims: str | None = None @tagged_union(frozen=True) @@ -104,8 +109,16 @@ class CredError: *, www_authenticate: str | None = None, body: Mapping[str, str] | None = None, + claims: str | None = None, ) -> CredError: - return CredError(unauthorized=Unauthorized(detail=detail, www_authenticate=www_authenticate, body=body)) + return CredError( + unauthorized=Unauthorized( + detail=detail, + www_authenticate=www_authenticate, + body=body, + claims=claims, + ) + ) @staticmethod def of_misconfigured(detail: str) -> CredError: @@ -182,18 +195,33 @@ class ClientCredentialsConfig(BaseModel): class TokenExchangeConfig(BaseModel): - """RFC 8693 OBO; swap the caller's live subject_token for a token bound to the upstream's - audience (`server.resource`, RFC 8707). The gateway authenticates to the exchange endpoint - as an OAuth client (`client_id`/`client_secret`); the inbound token is sent only to that - endpoint, never to the upstream. + """OBO: swap the caller's live inbound token for a token bound to the upstream's audience. The + gateway authenticates to the exchange endpoint as an OAuth client (`client_id`/`client_secret`); + the inbound token is sent only to that endpoint, never to the upstream. + + `profile` selects the wire dialect, since not every IdP speaks RFC 8693: + - `rfc8693` (default) is the standard token-exchange grant: the inbound token is the + `subject_token` (typed by `subject_token_type`), the target is the optional `audience`. + - `entra_obo` is Microsoft Entra On-Behalf-Of, which is the RFC 7523 `jwt-bearer` grant rather + than 8693: the inbound token rides as `assertion`, the target resource is carried in `scopes` + (`api:///.default`, since Entra has no audience parameter), and the Microsoft-only + `requested_token_use=on_behalf_of` extension makes the jwt-bearer grant a delegation. + `subject_token_type` and `audience` are unused in this profile. + + `audience` (rfc8693 only) is optional and sent only when the operator configured one, since both + `audience` and `resource` are optional in RFC 8693 and the authorization server applies its own + default when neither is sent (fabricating one risks `invalid_target`). """ model_config = ConfigDict(frozen=True) kind: Literal[AuthSpecKind.token_exchange] = AuthSpecKind.token_exchange - subject_token_type: str = "urn:ietf:params:oauth:token-type:access_token" + profile: Literal["rfc8693", "entra_obo"] = "rfc8693" + subject_token_type: str = DEFAULT_SUBJECT_TOKEN_TYPE token_exchange_endpoint: str | None = None + audience: str | None = None client_id: str | None = None client_secret: SecretStr | None = None + token_endpoint_auth_method: Literal["client_secret_basic", "client_secret_post"] | None = None scopes: tuple[str, ...] = () diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index d30d8af2af2..111fde86ea0 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -8,6 +8,7 @@ from typing import ( Dict, List, Literal, + Mapping, Optional, Set, Tuple, @@ -68,6 +69,7 @@ if MCP_AVAILABLE: from mcp.types import Tool as MCPTool from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES, global_mcp_server_manager, ) from litellm.proxy._experimental.mcp_server.oauth_utils import ( @@ -77,6 +79,8 @@ if MCP_AVAILABLE: ListMCPToolsRestAPIResponseObject, MCPInfo, MCPServer, + _apply_toolset_scope, + _fire_mcp_tool_call_logging, _tool_name_matches, execute_mcp_tool, filter_tools_by_allowed_tools, @@ -84,6 +88,120 @@ if MCP_AVAILABLE: ######################################################## ############ MCP Server REST API Routes ################# + async def _safe_fire_mcp_tool_call_logging( + logging_obj: Optional[Any], + result: Any, + start_time: datetime, + end_time: datetime, + user_api_key_auth: Optional[UserAPIKeyAuth] = None, + request_data: Optional[Mapping[str, object]] = None, + ) -> None: + if logging_obj is None: + return + logging_results = await asyncio.gather( + _fire_mcp_tool_call_logging( + logging_obj, + result, + start_time, + end_time, + user_api_key_auth=user_api_key_auth, + request_data=request_data, + ), + return_exceptions=True, + ) + logging_error = logging_results[0] + if isinstance(logging_error, asyncio.CancelledError): + raise logging_error + if isinstance(logging_error, BaseException): + verbose_logger.warning("MCP tool call logging failed (continuing): %s", logging_error) + + def _relay_upstream_auth_http_exception(e: MCPUpstreamAuthError, request: Request) -> HTTPException: + """Convert a client-forwarded pass-through upstream 401 into an HTTPException that preserves the + upstream WWW-Authenticate, so a standards-compliant MCP client can run the upstream OAuth flow + instead of the generic 500 the endpoint catch-all would return.""" + return e.to_http_exception( + base_url=get_request_base_url(request), + request_path=request.scope.get("_original_path") or request.url.path, + ) + + async def _handle_virtual_mcp_tool( + request: Request, + data: Dict[str, Any], + tool_name: str, + user_api_key_dict: UserAPIKeyAuth, + ) -> Any: + """Handle the virtual ``mcp_tool_search`` / ``mcp_tool_call`` REST tools (gated on + ``mcp_tool_search_enabled``). Kept out of ``call_tool_rest_api`` so that endpoint stays a single + dispatch. An upstream 401 raised by the virtual ``mcp_tool_call`` propagates unhandled to the + caller's ``except MCPUpstreamAuthError`` relay, the same as the direct call path.""" + from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( + MCPRequestHandler, + ) + from litellm.proxy._experimental.mcp_server.tool_search import ( + MCP_TOOL_SEARCH_TOOL_NAME, + coerce_top_k, + handle_mcp_tool_call, + handle_mcp_tool_search, + ) + from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing + from litellm.proxy.proxy_server import general_settings, proxy_config, proxy_logging_obj + + if not getattr(getattr(user_api_key_dict, "object_permission", None), "mcp_tool_search_enabled", False): + raise HTTPException( + status_code=403, + detail={"error": "forbidden", "message": f"{tool_name} requires mcp_tool_search_enabled on the key"}, + ) + tool_arguments = data.get("arguments") or {} + rest_client_ip = IPAddressUtils.get_mcp_client_ip(request) + ( + virtual_mcp_auth_header, + virtual_mcp_server_auth_headers, + virtual_raw_headers, + ) = _extract_mcp_headers_from_request(request, MCPRequestHandler) + virtual_oauth2_headers = MCPRequestHandler._get_oauth2_headers_from_headers(request.headers) + if tool_name == MCP_TOOL_SEARCH_TOOL_NAME: + return await handle_mcp_tool_search( + query=tool_arguments.get("query", ""), + top_k=coerce_top_k(tool_arguments.get("top_k", 5)), + user_api_key_dict=user_api_key_dict, + client_ip=rest_client_ip, + mcp_auth_header=virtual_mcp_auth_header, + mcp_server_auth_headers=virtual_mcp_server_auth_headers, + oauth2_headers=virtual_oauth2_headers, + raw_headers=virtual_raw_headers, + ) + # MCP_TOOL_CALL_TOOL_NAME: run the same pre-call pipeline as the normal path so the tool + # execution is spend-logged and guardrail-checked. + (_, virtual_logging_obj) = await ProxyBaseLLMRequestProcessing(data=data).common_processing_pre_call_logic( + request=request, + user_api_key_dict=user_api_key_dict, + proxy_config=proxy_config, + route_type=CallTypes.call_mcp_tool.value, + proxy_logging_obj=proxy_logging_obj, + general_settings=general_settings, + ) + _tool_start_time = datetime.now() + result = await handle_mcp_tool_call( + tool_name=tool_arguments.get("tool_name", ""), + arguments=tool_arguments.get("arguments") or {}, + user_api_key_dict=user_api_key_dict, + client_ip=rest_client_ip, + mcp_auth_header=virtual_mcp_auth_header, + mcp_server_auth_headers=virtual_mcp_server_auth_headers, + oauth2_headers=virtual_oauth2_headers, + raw_headers=virtual_raw_headers, + litellm_logging_obj=virtual_logging_obj, + ) + await _safe_fire_mcp_tool_call_logging( + virtual_logging_obj, + result, + _tool_start_time, + datetime.now(), + user_api_key_auth=user_api_key_dict, + request_data=data, + ) + return result + def _get_server_auth_header( server, mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]], @@ -522,10 +640,37 @@ if MCP_AVAILABLE: "message": "Successfully retrieved tools", } + def _as_query_str(value: Any) -> Optional[str]: + """Coerce an Optional[str] Query param to str|None, dropping unresolved FastAPI defaults.""" + return value if isinstance(value, str) else None + + async def _resolve_toolset_scope( + toolset_name: Optional[str], + user_api_key_dict: UserAPIKeyAuth, + ) -> UserAPIKeyAuth: + """Resolve ``toolset_name`` to its scoped ``UserAPIKeyAuth``, or return unchanged.""" + if not toolset_name: + return user_api_key_dict + + from litellm.proxy.utils import get_prisma_client_or_throw + + prisma_client = get_prisma_client_or_throw("Database not available. Connect a database to your proxy") + toolset = await global_mcp_server_manager.get_toolset_by_name_cached(prisma_client, toolset_name) + if toolset is None: + raise HTTPException( + status_code=404, + detail=f"Toolset '{toolset_name}' not found", + ) + return await _apply_toolset_scope(user_api_key_dict, toolset.toolset_id) + @router.get("/tools/list", dependencies=[Depends(user_api_key_auth)]) async def list_tool_rest_api( request: Request, server_id: Optional[str] = Query(None, description="The server id to list tools for"), + mcp_server_name: Optional[str] = Query( + None, description="Filter tools to a single MCP server by name or alias" + ), + toolset_name: Optional[str] = Query(None, description="Filter tools to a single toolset by name"), include_disabled_tools: bool = Query( False, description=( @@ -563,12 +708,40 @@ if MCP_AVAILABLE: ) try: + mcp_server_name = _as_query_str(mcp_server_name) + toolset_name = _as_query_str(toolset_name) + # The full catalog (allowlist filter skipped) is admin-only so the # REST endpoint can't be used to enumerate deliberately-disabled tools. apply_tool_filters = not ( include_disabled_tools and user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN ) + user_api_key_dict = await _resolve_toolset_scope(toolset_name, user_api_key_dict) + + if server_id is None: + server_id = mcp_server_name + + if ( + apply_tool_filters + and server_id is None + and toolset_name is None + and getattr( + getattr(user_api_key_dict, "object_permission", None), + "mcp_tool_search_enabled", + False, + ) + ): + from litellm.proxy._experimental.mcp_server.tool_search import ( + get_virtual_tool_definitions, + ) + + return { + "tools": get_virtual_tool_definitions(), + "error": None, + "message": "Successfully retrieved tools", + } + # Extract auth headers from request headers = request.headers raw_headers_from_request = dict(headers) @@ -685,6 +858,8 @@ if MCP_AVAILABLE: request_path=request.scope.get("_original_path") or request.url.path, ) except HTTPException as http_exc: + if http_exc.status_code == status.HTTP_404_NOT_FOUND: + raise # Internal access/IP 403s keep the legacy error-dict response shape # so the existing contract stays intact. verbose_logger.exception("HTTPException in list_tool_rest_api: %s", str(http_exc)) @@ -727,6 +902,17 @@ if MCP_AVAILABLE: try: data = await request.json() + tool_name = data.get("name") + tool_arguments = data.get("arguments") or {} + + from litellm.proxy._experimental.mcp_server.tool_search import ( + MCP_TOOL_CALL_TOOL_NAME, + MCP_TOOL_SEARCH_TOOL_NAME, + ) + + if tool_name in (MCP_TOOL_SEARCH_TOOL_NAME, MCP_TOOL_CALL_TOOL_NAME): + return await _handle_virtual_mcp_tool(request, data, tool_name, user_api_key_dict) + # Validate required parameters early server_id = data.get("server_id") if not server_id: @@ -738,7 +924,6 @@ if MCP_AVAILABLE: }, ) - tool_name = data.get("name") if not tool_name: raise HTTPException( status_code=400, @@ -748,8 +933,6 @@ if MCP_AVAILABLE: }, ) - tool_arguments = data.get("arguments") or {} - proxy_base_llm_response_processor = ProxyBaseLLMRequestProcessing(data=data) ( data, @@ -796,11 +979,12 @@ if MCP_AVAILABLE: user_oauth_extra_headers = await _get_user_oauth_extra_headers(target_server, user_api_key_dict) # Call execute_mcp_tool directly (permission checks already done) + _tool_start_time = datetime.now() result = await execute_mcp_tool( name=tool_name, arguments=tool_arguments, allowed_mcp_servers=allowed_mcp_servers, - start_time=datetime.now(), + start_time=_tool_start_time, user_api_key_auth=data.get("user_api_key_auth"), mcp_auth_header=data.get("mcp_auth_header"), mcp_server_auth_headers=data.get("mcp_server_auth_headers"), @@ -809,6 +993,14 @@ if MCP_AVAILABLE: litellm_logging_obj=data.get("litellm_logging_obj"), requested_server_id=canonical_server_id, ) + await _safe_fire_mcp_tool_call_logging( + logging_obj, + result, + _tool_start_time, + datetime.now(), + user_api_key_auth=user_api_key_dict, + request_data=data, + ) return result except MCPMissingUserEnvVarsError as e: verbose_logger.info( @@ -848,8 +1040,16 @@ if MCP_AVAILABLE: "guardrail_name": getattr(e, "guardrail_name", None), }, ) + except MCPUpstreamAuthError as e: + # A client-forwarded pass-through upstream 401 from either the direct or the virtual call + # branch. Relay it as a 401 + WWW-Authenticate so the MCP client can re-run upstream OAuth, + # and log at info: an expected caller-must-reauth signal, not an operator-actionable error. + verbose_logger.info(f"MCP tool call relaying upstream HTTP {e.status_code}") + raise _relay_upstream_auth_http_exception(e, request) except HTTPException as e: - # Re-raise HTTPException as-is to preserve status code and detail + # Locally generated denials (tool/server permission, IP filtering, BYOK) stay at error level + # so restriction probing keeps full monitoring visibility; the relayed upstream 401 above is + # the only status demoted to info. verbose_logger.error(f"HTTPException in MCP tool call: {str(e)}") raise e except Exception as e: @@ -975,11 +1175,6 @@ if MCP_AVAILABLE: forwarded_authorization = ( effective_oauth2_headers.get("Authorization") if effective_oauth2_headers else None ) - is_interactive_authz_code = ( - server_model.auth_type == MCPAuth.oauth2 - and forwarded_authorization is not None - and to_server_spec(server_model) is not None - ) preview_cred_provider = ( UpstreamCredentialProvider( oauth_token_store=PresentedOAuthTokenStore( @@ -990,7 +1185,11 @@ if MCP_AVAILABLE: ) ) ) - if is_interactive_authz_code + if ( + server_model.auth_type == MCPAuth.oauth2 + and forwarded_authorization is not None + and to_server_spec(server_model) is not None + ) else None ) @@ -1151,8 +1350,13 @@ if MCP_AVAILABLE: if isinstance(credentials, dict): mcp_auth_header = credentials.get("auth_value") + # Authorization doubles as the admission fallback (LITELLM_API_KEY_HEADER_NAME_SECONDARY): + # when the primary x-litellm-api-key header is absent, the Authorization value is the + # caller's LiteLLM key, not an upstream token, and must never be forwarded upstream. oauth2_headers: Optional[Dict[str, str]] = None - if new_mcp_server_request.auth_type == MCPAuth.oauth2: + if new_mcp_server_request.auth_type in _UPSTREAM_OAUTH_DISCOVERY_AUTH_TYPES and headers.get( + MCPRequestHandler.LITELLM_API_KEY_HEADER_NAME_PRIMARY + ): oauth2_headers = MCPRequestHandler._get_oauth2_headers_from_headers(headers) async def _list_tools_operation(client): diff --git a/litellm/proxy/_experimental/mcp_server/semantic_tool_filter.py b/litellm/proxy/_experimental/mcp_server/semantic_tool_filter.py index f24d5715e83..e12c6cdbd56 100644 --- a/litellm/proxy/_experimental/mcp_server/semantic_tool_filter.py +++ b/litellm/proxy/_experimental/mcp_server/semantic_tool_filter.py @@ -7,6 +7,8 @@ Filters MCP tools semantically for /chat/completions and /responses endpoints. from typing import TYPE_CHECKING, Any, Dict, List, Optional from litellm._logging import verbose_logger +from litellm.exceptions import ContextWindowExceededError +from litellm.litellm_core_utils.exception_mapping_utils import ExceptionCheckers from litellm.proxy._experimental.mcp_server.utils import MCP_TOOL_PREFIX_SEPARATOR if TYPE_CHECKING: @@ -15,6 +17,36 @@ if TYPE_CHECKING: from litellm.router import Router +class SemanticToolFilterContextWindowError(Exception): + """Raised when the embedding model exceeds its context window, so semantic filtering cannot run.""" + + def __init__(self, embedding_model: str, stage: str, original_error: str): + self.embedding_model = embedding_model + self.stage = stage + self.original_error = original_error + super().__init__( + f"MCP semantic tool filtering could not run: embedding model '{embedding_model}' " + f"exceeded its context window while embedding {stage}. " + f"The request was blocked instead of silently passing all tools through. " + f"Switch to an embedding model with a larger context window, or disable " + f"semantic tool filtering." + ) + + +def _is_context_window_error(error: Optional[BaseException], max_depth: int = 5) -> bool: + """Detect a context-window overflow anywhere in an exception's cause chain.""" + current = error + for _ in range(max_depth): + if current is None: + return False + if isinstance(current, ContextWindowExceededError): + return True + if ExceptionCheckers.is_error_str_context_window_exceeded(str(current)): + return True + current = current.__cause__ or current.__context__ + return False + + class SemanticMCPToolFilter: """Filters MCP tools using semantic similarity to reduce context window size.""" @@ -42,6 +74,7 @@ class SemanticMCPToolFilter: self.embedding_model = embedding_model self.router_instance = litellm_router_instance self.tool_router: Optional["SemanticRouter"] = None + self.context_window_error: Optional[str] = None self._tool_map: Dict[str, Any] = {} # MCPTool objects or OpenAI function dicts async def build_router_from_mcp_registry(self) -> None: @@ -111,6 +144,7 @@ class SemanticMCPToolFilter: return try: + self.context_window_error = None # Convert tools to routes routes = [] self._tool_map = {} @@ -143,6 +177,9 @@ class SemanticMCPToolFilter: except Exception as e: verbose_logger.error(f"Failed to build semantic router: {e}") self.tool_router = None + if _is_context_window_error(e): + self.context_window_error = str(e) + return raise async def filter_tools( @@ -169,6 +206,13 @@ class SemanticMCPToolFilter: if not available_tools: return available_tools + if self.context_window_error is not None: + raise SemanticToolFilterContextWindowError( + embedding_model=self.embedding_model, + stage="the MCP tool descriptions during semantic router build", + original_error=self.context_window_error, + ) + if not query or not query.strip(): return available_tools @@ -189,6 +233,16 @@ class SemanticMCPToolFilter: return self._get_tools_by_names(matched_tool_names, available_tools) except Exception as e: + if _is_context_window_error(e): + verbose_logger.error( + f"Semantic tool filter embedding exceeded its context window: {e}", + exc_info=True, + ) + raise SemanticToolFilterContextWindowError( + embedding_model=self.embedding_model, + stage="the user query", + original_error=str(e), + ) from e verbose_logger.error(f"Semantic tool filter failed: {e}", exc_info=True) return available_tools diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 4b55510a629..5090aa7d7d5 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -10,8 +10,8 @@ import contextvars import hashlib import json import time -import types import traceback +import types import uuid from datetime import datetime from typing import ( @@ -20,12 +20,14 @@ from typing import ( Callable, Dict, List, + Mapping, Optional, Set, Tuple, Union, cast, ) +from urllib.parse import urlsplit, urlunsplit import httpx from fastapi import FastAPI, HTTPException @@ -37,13 +39,20 @@ from starlette.types import Message, Receive, Scope, Send from litellm._logging import verbose_logger from litellm.constants import MAXIMUM_TRACEBACK_LINES_TO_LOG from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.llms.custom_httpx.http_handler import ( + get_async_httpx_client, + httpxSpecialProvider, +) from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( MCPRequestHandler, ) -from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( get_request_base_url, ) +from litellm.proxy._experimental.mcp_server.exceptions import ( + MCPToolResultError, + MCPUpstreamAuthError, +) from litellm.proxy._experimental.mcp_server.mcp_context import ( _mcp_active_toolset_id, _mcp_gateway_initialize_instructions, @@ -56,13 +65,10 @@ from litellm.proxy._experimental.mcp_server.utils import ( LITELLM_MCP_SERVER_VERSION, MCPMissingUserEnvVarsError, add_server_prefix_to_name, + extract_mcp_tool_result_error_message, get_server_prefix, iter_known_server_prefixes, ) -from litellm.llms.custom_httpx.http_handler import ( - get_async_httpx_client, - httpxSpecialProvider, -) from litellm.proxy._types import ( ProxyException, SpecialMCPServerNames, @@ -100,6 +106,27 @@ _MAX_STATEFUL_SESSIONS_PER_OWNER = 100 _MCP_ROUTING_PEEK_MAX_BYTES = 4096 +def _redact_mcp_resource_url(url: Optional[str]) -> Optional[str]: + """Reduce an MCP server URL to its origin (scheme + host + port) for logging. + + Everything else is dropped: userinfo (``user:pass@``), the query string, the + fragment, and the path, because hosted MCP servers routinely embed the + credential in the path (e.g. ``/mcp/s/``) and this value is persisted + in spend-log metadata that a caller who can invoke the tool can read back. + Returns None when the URL has no host to identify (nothing safe to log). + """ + if not isinstance(url, str) or not url: + return None + try: + parts = urlsplit(url) + except ValueError: + return None + if not parts.hostname: + return None + netloc = f"{parts.hostname}:{parts.port}" if parts.port else parts.hostname + return urlunsplit((parts.scheme, netloc, "", "", "")) or None + + def _invalidate_byok_cred_cache(user_id: str, server_id: str) -> None: """Remove a (user_id, server_id) entry from the BYOK credential cache. @@ -122,9 +149,12 @@ def _write_byok_cred_cache(user_id: str, server_id: str, credential: Optional[st # TODO: Make this a util function for litellm client usage MCP_AVAILABLE: bool = True try: + import weakref + from mcp import ReadResourceResult, Resource from mcp.server import Server from mcp.server.lowlevel.helper_types import ReadResourceContents + from mcp.server.session import ServerSession as _McpServerSession from mcp.types import ( BlobResourceContents, GetPromptResult, @@ -132,8 +162,6 @@ try: TextResourceContents, Tool, ) - from mcp.server.session import ServerSession as _McpServerSession - import weakref # Robust auth lookup keyed by session_object. _session_obj_auth_storage: "weakref.WeakKeyDictionary[Any, MCPAuthenticatedUser]" = weakref.WeakKeyDictionary() @@ -229,6 +257,56 @@ def _jsonrpc_text_has_top_level_method(text: str) -> bool: return False +def _mcp_meta_trace_carrier(req_ctx: object) -> Optional[dict[str, str]]: + """The W3C trace context (``traceparent``/``tracestate``) the MCP client + propagated in the request's ``params._meta`` (SEP-414), or ``None``. + + Per the OTel MCP semconv the MCP span parents to this propagated context rather + than to the HTTP/session transport (which is recorded as a link instead), so a + streamable-HTTP session that multiplexes many messages does not glue every + message under the session's first request. The client's W3C Baggage is + deliberately excluded: it is caller-controlled, and the otel baggage processor + stamps allowlisted baggage keys (``litellm.team.id``, ``litellm.metadata.*``, + ...) onto the span, so honoring remote baggage would let a client spoof a + span's identity attribution. + """ + meta = getattr(req_ctx, "meta", None) + extra = getattr(meta, "model_extra", None) + if not isinstance(extra, dict): + return None + carrier = {key: extra[key] for key in ("traceparent", "tracestate") if isinstance(extra.get(key), str)} + return carrier or None + + +def _otel_set_mcp_trace_carrier(carrier: Optional[dict[str, str]]) -> object: + """Stash ``carrier`` for the otel_v2 MCP span and return a reset token, or + ``None`` when otel_v2 is unavailable. Lazily imported so opentelemetry stays an + optional dependency.""" + try: + from litellm.integrations.otel.plumbing.context import ( + set_mcp_message_trace_carrier, + ) + + return set_mcp_message_trace_carrier(carrier) + except ImportError: + return None + + +def _otel_reset_mcp_trace_carrier(token: object) -> None: + """Clear the per-message trace carrier so it never leaks to the next message on + the same session task. Paired with ``_otel_set_mcp_trace_carrier``.""" + if token is None: + return + try: + from litellm.integrations.otel.plumbing.context import ( + reset_mcp_message_trace_carrier, + ) + + reset_mcp_message_trace_carrier(token) + except ImportError: + return + + def _proxy_exception_to_http_exception(exc: ProxyException) -> HTTPException: """Map a ``ProxyException`` to an ``HTTPException`` that preserves its real status code and headers. @@ -253,14 +331,14 @@ def _proxy_exception_to_http_exception(exc: ProxyException) -> HTTPException: if MCP_AVAILABLE: from mcp.server import Server - from mcp.server.lowlevel.server import NotificationOptions - from mcp.server.models import InitializationOptions # Import auth context variables and middleware from mcp.server.auth.middleware.auth_context import ( AuthContextMiddleware, auth_context_var, ) + from mcp.server.lowlevel.server import NotificationOptions + from mcp.server.models import InitializationOptions try: from mcp.server.streamable_http_manager import StreamableHTTPSessionManager @@ -280,6 +358,8 @@ if MCP_AVAILABLE: ) from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( MCPServerManager, + _caller_authorization_fans_out, + _client_forwarded_authorization_headers, _should_strip_caller_authorization, _without_authorization, global_mcp_server_manager, @@ -595,8 +675,10 @@ if MCP_AVAILABLE: _session_reset_token = None if req_ctx: _session_reset_token = active_mcp_session_var.set(req_ctx.session) + _trace_token = None try: + _trace_token = _otel_set_mcp_trace_carrier(_mcp_meta_trace_carrier(req_ctx)) # Get user authentication from context variable ( user_api_key_auth, @@ -612,6 +694,19 @@ if MCP_AVAILABLE: verbose_logger.debug( f"MCP list_tools - MCP server auth headers: {list(mcp_server_auth_headers.keys()) if mcp_server_auth_headers else None}" ) + if getattr( + getattr(user_api_key_auth, "object_permission", None), + "mcp_tool_search_enabled", + False, + ): + from mcp.types import Tool + + from litellm.proxy._experimental.mcp_server.tool_search import ( + get_virtual_tool_definitions, + ) + + return [Tool(**d) for d in get_virtual_tool_definitions()] + # Get mcp_servers from context variable verbose_logger.debug("MCP list_tools - Calling _list_mcp_tools") tools = await _list_mcp_tools( @@ -632,9 +727,154 @@ if MCP_AVAILABLE: # This prevents the HTTP stream from failing and allows the client to get a response return [] finally: + _otel_reset_mcp_trace_carrier(_trace_token) if _session_reset_token is not None: active_mcp_session_var.reset(_session_reset_token) + def _capture_host_progress_callback(host_server) -> Optional[Callable]: + """Return a progress-forwarding callback bound to the host MCP session. + + Returns ``None`` when the host did not supply a progress token. + """ + try: + host_ctx = host_server.request_context + except Exception as e: + verbose_logger.warning(f"Could not capture host progress context: {e}") + return None + + if not (host_ctx and hasattr(host_ctx, "meta") and host_ctx.meta): + return None + host_token = getattr(host_ctx.meta, "progressToken", None) + if host_token is None or not (hasattr(host_ctx, "session") and host_ctx.session): + return None + host_session = host_ctx.session + + async def forward_progress(progress: float, total: Optional[float]): + """Forward progress notifications from external MCP to Host""" + try: + await host_session.send_progress_notification( + progress_token=host_token, + progress=progress, + total=total, + ) + verbose_logger.debug(f"Forwarded progress {progress}/{total} to Host") + except Exception as e: + verbose_logger.error(f"Failed to forward progress to Host: {e}") + + verbose_logger.debug(f"Host progressToken captured: {str(host_token)[:8]}...") + return forward_progress + + async def _build_virtual_call_logging_obj( + name: str, + arguments: dict[str, Any], + user_api_key_auth: UserAPIKeyAuth, + ) -> Optional[LiteLLMLoggingObj]: + """Run the pre-call pipeline (guardrails + logging setup) for a virtual + mcp_tool_call so the SSE path spend-logs like the REST path.""" + from fastapi import Request + + from litellm.proxy.common_request_processing import ( + ProxyBaseLLMRequestProcessing, + ) + from litellm.proxy.proxy_server import ( + general_settings, + proxy_config, + proxy_logging_obj, + ) + + request = Request( + scope={ + "type": "http", + "method": "POST", + "path": "/mcp/tools/call", + "headers": [(b"content-type", b"application/json")], + } + ) + _, virtual_logging_obj = await ProxyBaseLLMRequestProcessing( + data={"name": name, "arguments": arguments} + ).common_processing_pre_call_logic( + request=request, + user_api_key_dict=user_api_key_auth, + proxy_config=proxy_config, + route_type=CallTypes.call_mcp_tool.value, + proxy_logging_obj=proxy_logging_obj, + general_settings=general_settings, + ) + return virtual_logging_obj + + async def _dispatch_virtual_mcp_tool( + name: str, + arguments: Optional[dict[str, Any]], + user_api_key_auth: Optional[UserAPIKeyAuth], + client_ip: Optional[str], + mcp_servers: Optional[list[str]] = None, + mcp_auth_header: Optional[str] = None, + mcp_server_auth_headers: Optional[dict[str, dict[str, str]]] = None, + oauth2_headers: Optional[dict[str, str]] = None, + raw_headers: Optional[dict[str, str]] = None, + ) -> Optional[CallToolResult]: + """Handle the mcp_tool_search / mcp_tool_call virtual tools. + + Returns a CallToolResult when ``name`` is a virtual tool, else ``None`` so + the caller falls through to normal tool routing. + """ + from litellm.proxy._experimental.mcp_server.tool_search import ( + MCP_TOOL_CALL_TOOL_NAME, + MCP_TOOL_SEARCH_TOOL_NAME, + coerce_top_k, + handle_mcp_tool_call, + handle_mcp_tool_search, + ) + + if name not in (MCP_TOOL_SEARCH_TOOL_NAME, MCP_TOOL_CALL_TOOL_NAME): + return None + + if not getattr( + getattr(user_api_key_auth, "object_permission", None), + "mcp_tool_search_enabled", + False, + ): + return CallToolResult( + content=[ + TextContent( + type="text", + text=f"Tool {name} requires mcp_tool_search_enabled on the key", + ) + ], + isError=True, + ) + + args = arguments or {} + if name == MCP_TOOL_SEARCH_TOOL_NAME: + return await handle_mcp_tool_search( + query=args.get("query", ""), + top_k=coerce_top_k(args.get("top_k", 5)), + user_api_key_dict=user_api_key_auth, + client_ip=client_ip, + mcp_servers=mcp_servers, + mcp_auth_header=mcp_auth_header, + mcp_server_auth_headers=mcp_server_auth_headers, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + ) + + assert user_api_key_auth is not None # guaranteed by the flag check above + virtual_logging_obj = await _build_virtual_call_logging_obj( + name=name, arguments=args, user_api_key_auth=user_api_key_auth + ) + return await handle_mcp_tool_call( + tool_name=args.get("tool_name", ""), + arguments=args.get("arguments") or {}, + user_api_key_dict=user_api_key_auth, + client_ip=client_ip, + mcp_servers=mcp_servers, + mcp_auth_header=mcp_auth_header, + mcp_server_auth_headers=mcp_server_auth_headers, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + litellm_logging_obj=virtual_logging_obj, + ) + @server.call_tool() async def mcp_server_tool_call(name: str, arguments: Dict[str, Any] | None) -> CallToolResult: """ @@ -648,18 +888,21 @@ if MCP_AVAILABLE: HTTPException: If tool not found or arguments missing """ from fastapi import Request + from mcp.server.lowlevel.server import request_ctx + from mcp.types import CallToolResult + from litellm.exceptions import BlockedPiiEntityError, GuardrailRaisedException from litellm.proxy.litellm_pre_call_utils import add_litellm_data_to_request from litellm.proxy.proxy_server import proxy_config - from mcp.types import CallToolResult - from mcp.server.lowlevel.server import request_ctx req_ctx = request_ctx.get(None) _session_reset_token = None if req_ctx: _session_reset_token = active_mcp_session_var.set(req_ctx.session) + _trace_token = None try: + _trace_token = _otel_set_mcp_trace_carrier(_mcp_meta_trace_carrier(req_ctx)) # Validate arguments ( user_api_key_auth, @@ -675,31 +918,25 @@ if MCP_AVAILABLE: ) verbose_logger.debug(f"MCP mcp_server_tool_call - User API Key Auth from context: {user_api_key_auth}") - host_progress_callback = None - try: - host_ctx = server.request_context - if host_ctx and hasattr(host_ctx, "meta") and host_ctx.meta: - host_token = getattr(host_ctx.meta, "progressToken", None) - if host_token and hasattr(host_ctx, "session") and host_ctx.session: - host_session = host_ctx.session - async def forward_progress(progress: float, total: Optional[float]): - """Forward progress notifications from external MCP to Host""" - try: - await host_session.send_progress_notification( - progress_token=host_token, - progress=progress, - total=total, - ) - verbose_logger.debug(f"Forwarded progress {progress}/{total} to Host") - except Exception as e: - verbose_logger.error(f"Failed to forward progress to Host: {e}") - - host_progress_callback = forward_progress - verbose_logger.debug(f"Host progressToken captured: {host_token[:8]}...") - except Exception as e: - verbose_logger.warning(f"Could not capture host progress context: {e}") try: + # Inside this try so virtual-tool errors convert to isError + # CallToolResult instead of raising out of the protocol handler. + virtual_tool_result = await _dispatch_virtual_mcp_tool( + name=name, + arguments=arguments, + user_api_key_auth=user_api_key_auth, + client_ip=_client_ip, + mcp_servers=mcp_servers, + mcp_auth_header=mcp_auth_header, + mcp_server_auth_headers=mcp_server_auth_headers, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + ) + if virtual_tool_result is not None: + return virtual_tool_result + + host_progress_callback = _capture_host_progress_callback(server) # Create a body date for logging body_data = {"name": name, "arguments": arguments} # Set trace/session id from raw_headers so spend logs and logging_obj stay consistent (same as A2A) @@ -769,6 +1006,22 @@ if MCP_AVAILABLE: content=[TextContent(text=f"Error: {str(e.detail)}", type="text")], isError=True, ) + except MCPUpstreamAuthError as e: + # The MCP session manager serializes handler exceptions as JSON-RPC errors, so a + # mid-session tool call cannot emit a raw 401 + WWW-Authenticate the way the REST + # call path and the connect-time preemptive check do. Return an explicit isError + # naming the upstream status (at info level, not a traceback) so the client still + # learns it must re-authenticate upstream and expected pass-through 401s don't spam. + verbose_logger.info(f"Upstream auth failure calling MCP tool: HTTP {e.status_code}") + return CallToolResult( + content=[ + TextContent( + text=f"Error: upstream authentication required (HTTP {e.status_code})", + type="text", + ) + ], + isError=True, + ) except Exception as e: verbose_logger.exception(f"MCP mcp_server_tool_call - error: {e}") return CallToolResult( @@ -778,6 +1031,7 @@ if MCP_AVAILABLE: return response finally: + _otel_reset_mcp_trace_carrier(_trace_token) if _session_reset_token is not None: active_mcp_session_var.reset(_session_reset_token) @@ -1218,18 +1472,8 @@ if MCP_AVAILABLE: for allowed_mcp_server_id in allowed_mcp_server_ids: mcp_server = global_mcp_server_manager.get_mcp_server_by_id(allowed_mcp_server_id) if mcp_server is not None: - # Apply oauth2_flow resolution for legacy DB rows where it may be NULL - resolved_flow = MCPServerManager._resolve_oauth2_flow( - auth_type=mcp_server.auth_type, - oauth2_flow=mcp_server.oauth2_flow, - token_url=mcp_server.token_url, - authorization_url=mcp_server.authorization_url, - client_id=mcp_server.client_id, - client_secret=mcp_server.client_secret, - ) - if resolved_flow and resolved_flow != mcp_server.oauth2_flow: - # Create a new instance with the resolved flow for this request - mcp_server = mcp_server.model_copy(update={"oauth2_flow": resolved_flow}) + # Apply the request-time oauth2_flow backstop for legacy null rows. + mcp_server = MCPServerManager.resolve_oauth2_flow_for_request(mcp_server) allowed_mcp_servers.append(mcp_server) if mcp_servers is not None: @@ -1240,6 +1484,35 @@ if MCP_AVAILABLE: return allowed_mcp_servers + def _client_has_per_server_auth_header( + server: MCPServer, + mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]], + ) -> bool: + """True if the request carries a per-server ``x-mcp-{alias}-authorization`` + header for this server. This is the multi-server binding: it names one + upstream, so it is unambiguously the caller's upstream token regardless of + auth mode (never the LiteLLM admission credential). + + Resolves through the same ``lookup_mcp_server_auth_in_headers`` egress uses, so + the connect gate and egress agree on which per-server header names match: a + dashboard client sends ``x-mcp-{sanitize_mcp_alias_for_header(alias)}-authorization``, + and matching only the raw alias here would 401 a token egress would forward. + """ + if not mcp_server_auth_headers: + return False + from litellm.proxy._experimental.mcp_server.utils import ( + lookup_mcp_server_auth_in_headers, + ) + + server_headers = lookup_mcp_server_auth_in_headers( + mcp_server_auth_headers, alias=server.alias, server_name=server.server_name + ) + if isinstance(server_headers, str): + return bool(server_headers.strip()) + if isinstance(server_headers, dict): + return any(isinstance(hk, str) and hk.lower() == "authorization" for hk in server_headers) + return False + def _client_has_passthrough_authorization( server: MCPServer, oauth2_headers: Optional[Dict[str, str]], @@ -1257,24 +1530,7 @@ if MCP_AVAILABLE: for k in oauth2_headers.keys(): if k.lower() == "authorization": return True - if mcp_server_auth_headers: - for key in (server.alias, server.server_name, server.name): - if not key: - continue - server_headers = None - for k, v in mcp_server_auth_headers.items(): - if k.lower() == key.lower(): - server_headers = v - break - if server_headers is None: - continue - if isinstance(server_headers, str) and server_headers.strip(): - return True - if isinstance(server_headers, dict): - for hk in server_headers.keys(): - if hk.lower() == "authorization": - return True - return False + return _client_has_per_server_auth_header(server, mcp_server_auth_headers) async def _get_user_oauth_extra_headers_from_db( server: MCPServer, @@ -1329,8 +1585,16 @@ if MCP_AVAILABLE: oauth2_headers: Optional[Dict[str, str]], raw_headers: Optional[Dict[str, str]], user_api_key_auth: Optional[UserAPIKeyAuth] = None, + scope_servers: Optional[list[MCPServer]] = None, ) -> Tuple[Optional[Union[Dict[str, str], str]], Optional[Dict[str, str]]]: - """Build auth and extra headers for a server.""" + """Build auth and extra headers for a server. + + ``scope_servers`` is the full server list a fan-out handler iterates. Passing it lets the + client-forwarded token modes withhold the caller's request-wide ``Authorization`` when + another server in the scope would also receive it (``_caller_authorization_fans_out``); + explicitly-addressed operations leave it None. Per-server ``x-mcp-{alias}-authorization`` + headers are unaffected — they bind one token to one server and are the multi-server shape. + """ server_auth_header: Optional[Union[Dict[str, str], str]] = None if mcp_server_auth_headers: from litellm.proxy._experimental.mcp_server.utils import ( @@ -1344,6 +1608,16 @@ if MCP_AVAILABLE: ) extra_headers: Optional[Dict[str, str]] = None + is_client_forwarded_mode = server.is_true_passthrough or server.is_oauth_delegate + # In a multi-server listing scope the request-wide Authorization can only carry one token, + # so it is withheld from a client-forwarded server when another server in scope also consumes + # it (RFC 9700 cross-resource replay); such scopes must bind per-server via + # x-mcp-{alias}-authorization. The decision is computed once so BOTH the forwarding branch and + # the extra_headers copy loop below honor it — otherwise a server that lists Authorization in + # extra_headers would re-copy the withheld bearer from raw_headers and replay it anyway. + withhold_forwarded_authorization = is_client_forwarded_mode and _caller_authorization_fans_out( + server, scope_servers + ) if server.auth_type == MCPAuth.oauth2: # For OAuth2 M2M servers, upstream Authorization must come from # client_credentials token fetch, never from caller headers. @@ -1362,6 +1636,14 @@ if MCP_AVAILABLE: user_api_key_auth=user_api_key_auth, ): extra_headers = _without_authorization(extra_headers) + elif is_client_forwarded_mode: + if not withhold_forwarded_authorization: + extra_headers = _client_forwarded_authorization_headers( + mcp_server=server, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ) if server.extra_headers and raw_headers: if extra_headers is None: @@ -1382,7 +1664,9 @@ if MCP_AVAILABLE: for header in server.extra_headers: if not isinstance(header, str): continue - if header.lower() == "authorization" and strip_caller_authorization: + if header.lower() == "authorization" and ( + strip_caller_authorization or withhold_forwarded_authorization + ): continue header_value = normalized_raw_headers.get(header.lower()) if header_value is None: @@ -1472,6 +1756,8 @@ if MCP_AVAILABLE: log_list_tools_to_spendlogs: bool = False, list_tools_log_source: Optional[str] = None, litellm_trace_id: Optional[str] = None, + request_tags: Optional[list[str]] = None, + client_ip: Optional[str] = None, ) -> List[MCPTool]: """ Helper method to fetch tools from MCP servers based on server filtering criteria. @@ -1514,6 +1800,7 @@ if MCP_AVAILABLE: "litellm_trace_id": effective_litellm_trace_id, "metadata": { "spend_logs_metadata": spend_logs_metadata, + **({"tags": request_tags} if request_tags else {}), }, # Provide a small input payload for standard logging "input": [ @@ -1559,6 +1846,7 @@ if MCP_AVAILABLE: allowed_mcp_servers = await _get_allowed_mcp_servers( user_api_key_auth=user_api_key_auth, mcp_servers=mcp_servers, + client_ip=client_ip, ) # Pre-fetch OAuth credentials only when at least one server uses OAuth2, @@ -1582,6 +1870,7 @@ if MCP_AVAILABLE: oauth2_headers=oauth2_headers, raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + scope_servers=allowed_mcp_servers, ) # Prefer server-stored per-user OAuth when configured, so a stale @@ -1625,6 +1914,7 @@ if MCP_AVAILABLE: add_prefix=True, # Always add server prefix raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + oauth2_headers=oauth2_headers, ) filtered_tools = filter_tools_by_allowed_tools(tools, server) @@ -1643,12 +1933,14 @@ if MCP_AVAILABLE: ) return filtered_tools except MCPUpstreamAuthError: - # Surface upstream 401/403 to the outer handler so the - # client receives a proper WWW-Authenticate challenge - # instead of a silently empty tool list. Without this - # re-raise the broad ``except Exception`` below would - # swallow the auth error. - raise + # Absorb so one unauthenticated server does not empty every other server's + # tools. Surfacing the upstream 401 to the client as a re-auth challenge is + # intentionally not done here: raising from this list handler cannot produce a + # 401 + WWW-Authenticate (the MCP session manager serializes it as a JSON-RPC + # error). Single-server routes surface it via the request-scope preemptive + # check in _raise_preemptive_401_for_unauthenticated_servers instead. + verbose_logger.debug(f"MCP list_tools: omitting {server.name}; it needs upstream auth") + return [] except Exception as e: verbose_logger.exception(f"Error getting tools from server {server.name}: {str(e)}") return [] @@ -1687,7 +1979,9 @@ if MCP_AVAILABLE: end_time = datetime.now() try: await litellm_logging_obj.async_success_handler( - result=all_tools, + result=[ + tool.model_dump(mode="json") if isinstance(tool, MCPTool) else tool for tool in all_tools + ], start_time=list_tools_start_time, end_time=end_time, ) @@ -1763,6 +2057,7 @@ if MCP_AVAILABLE: oauth2_headers=oauth2_headers, raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + scope_servers=allowed_mcp_servers, ) try: @@ -1815,6 +2110,7 @@ if MCP_AVAILABLE: oauth2_headers=oauth2_headers, raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + scope_servers=allowed_mcp_servers, ) try: @@ -1865,6 +2161,7 @@ if MCP_AVAILABLE: oauth2_headers=oauth2_headers, raw_headers=raw_headers, user_api_key_auth=user_api_key_auth, + scope_servers=allowed_mcp_servers, ) try: @@ -1967,6 +2264,7 @@ if MCP_AVAILABLE: raw_headers: Optional[Dict[str, str]] = None, log_list_tools_to_spendlogs: bool = False, list_tools_log_source: Optional[str] = None, + client_ip: Optional[str] = None, ) -> List[MCPTool]: """ List all available MCP tools. @@ -1976,6 +2274,7 @@ if MCP_AVAILABLE: mcp_auth_header: Optional auth header for MCP server (deprecated) mcp_servers: Optional list of server names/aliases to filter by mcp_server_auth_headers: Optional dict of server-specific auth headers {server_alias: auth_value} + client_ip: Client IP for IP-based server access control Returns: List[MCPTool]: Combined list of tools from all accessible servers @@ -1999,6 +2298,7 @@ if MCP_AVAILABLE: raw_headers=raw_headers, log_list_tools_to_spendlogs=log_list_tools_to_spendlogs, list_tools_log_source=list_tools_log_source, + client_ip=client_ip, ) verbose_logger.debug(f"Successfully fetched {len(managed_tools)} tools from managed MCP servers") except Exception as e: @@ -2473,12 +2773,18 @@ if MCP_AVAILABLE: # Forward named client headers to OpenAPI tool upstream requests. # MCPServer.extra_headers lists header names to copy from raw_headers. - # OAuth2 M2M: never take Authorization from the caller (matches - # _prepare_mcp_server_headers for managed MCP). + # The strip decision is centralized in _should_strip_caller_authorization so this + # OpenAPI/local path agrees with the managed paths: M2M and the resolver-owned modes + # (token_exchange's raw subject token, authorization_code's stored token) must never + # have the caller's Authorization forwarded verbatim upstream. forwarded_headers: Optional[Dict[str, str]] = None if mcp_server and mcp_server.extra_headers and raw_headers: normalized_raw = {str(k).lower(): v for k, v in raw_headers.items() if isinstance(k, str)} - skip_caller_authorization = bool(mcp_server.has_client_credentials) + skip_caller_authorization = _should_strip_caller_authorization( + mcp_server=mcp_server, + raw_headers=raw_headers, + user_api_key_auth=user_api_key_auth, + ) for header_name in mcp_server.extra_headers: if not isinstance(header_name, str): continue @@ -2526,6 +2832,74 @@ if MCP_AVAILABLE: return response + _MCP_CREDENTIAL_REQUEST_FIELDS = frozenset( + { + "raw_headers", + "mcp_auth_header", + "mcp_server_auth_headers", + "oauth2_headers", + "user_api_key_auth", + } + ) + + async def _fire_mcp_tool_call_logging( + logging_obj: LiteLLMLoggingObj, + result: Any, + start_time: datetime, + end_time: datetime, + user_api_key_auth: Optional[UserAPIKeyAuth] = None, + request_data: Optional[Mapping[str, object]] = None, + ) -> None: + """Fire post-call logging for an executed MCP tool call. + + A result with ``isError=True`` is logged as a failure (``status="failure"`` + payload, so OTel marks the span ERROR) while the HTTP wire behavior stays + 200 + ``isError: true`` per the MCP spec. The error check runs after + ``async_post_mcp_tool_call_hook`` because guardrails may flip the result + to ``isError=True`` in that hook. Raised exceptions never reach here (the + ``@client`` wrapper and ``call_mcp_tool``'s except path log those), so + this cannot double-log a failure. + + ``request_data`` may carry credential-bearing fields (the REST path puts + ``raw_headers``, ``mcp_auth_header``, ``mcp_server_auth_headers``, and + ``oauth2_headers`` at the top level of its data dict), so those are + stripped before the dict is handed to ``post_call_failure_hook`` + callbacks. + """ + logging_obj.post_call(original_response=result) + await logging_obj.async_post_mcp_tool_call_hook( + kwargs=logging_obj.model_call_details, + response_obj=result, + start_time=start_time, + end_time=end_time, + ) + logging_obj.call_type = CallTypes.call_mcp_tool.value + error_message = extract_mcp_tool_result_error_message(result) + if error_message is None: + await logging_obj.async_success_handler(result=result, start_time=start_time, end_time=end_time) + return + + logging_obj.has_run_logging(event_type="sync_success") + logging_obj.has_run_logging(event_type="async_success") + tool_error = MCPToolResultError(error_message) + logging_obj.failure_handler(tool_error, "", start_time, end_time) + await logging_obj.async_failure_handler(tool_error, "", start_time, end_time) + + if user_api_key_auth is None: + return + from litellm.proxy.proxy_server import proxy_logging_obj + + if proxy_logging_obj: + sanitized_request_data = { + key: value for key, value in (request_data or {}).items() if key not in _MCP_CREDENTIAL_REQUEST_FIELDS + } + await proxy_logging_obj.post_call_failure_hook( + request_data=sanitized_request_data, + original_exception=tool_error, + user_api_key_dict=user_api_key_auth, + route="/mcp/call_tool", + ) + @client async def call_mcp_tool( name: str, @@ -2557,6 +2931,9 @@ if MCP_AVAILABLE: for allowed_mcp_server_id in allowed_mcp_server_ids: allowed_server = global_mcp_server_manager.get_mcp_server_by_id(allowed_mcp_server_id) if allowed_server is not None: + # Same request-time oauth2_flow backstop the listing path applies, + # so a null-flow M2M-shape row is treated as M2M on tool calls too. + allowed_server = MCPServerManager.resolve_oauth2_flow_for_request(allowed_server) allowed_mcp_servers.append(allowed_server) allowed_mcp_servers = await _get_allowed_mcp_servers_from_mcp_server_names( @@ -2582,6 +2959,14 @@ if MCP_AVAILABLE: raw_headers=raw_headers, **kwargs, ) + except MCPUpstreamAuthError: + # A client-forwarded pass-through upstream 401 is an expected caller-must-reauth signal, so + # re-raise it without post_call_failure_hook, which fires the proxy's llm_exceptions alert. + # mcp_server_tool_call then downgrades it to an informational isError result for the + # streamable client. Note: this function is @client-decorated, so the decorator's standard + # failure logging still records the event (spend log / OTel); only the extra alert sink is + # skipped here. + raise except Exception as e: traceback_str = traceback.format_exc(limit=MAXIMUM_TRACEBACK_LINES_TO_LOG) from litellm.proxy.proxy_server import proxy_logging_obj @@ -2597,16 +2982,14 @@ if MCP_AVAILABLE: raise if litellm_logging_obj: - litellm_logging_obj.post_call(original_response=response) - end_time = datetime.now() - await litellm_logging_obj.async_post_mcp_tool_call_hook( - kwargs=litellm_logging_obj.model_call_details, - response_obj=response, + await _fire_mcp_tool_call_logging( + logging_obj=litellm_logging_obj, + result=response, start_time=start_time, - end_time=end_time, + end_time=datetime.now(), + user_api_key_auth=user_api_key_auth, + request_data=kwargs, ) - litellm_logging_obj.call_type = CallTypes.call_mcp_tool.value - await litellm_logging_obj.async_success_handler(result=response, start_time=start_time, end_time=end_time) return response async def mcp_get_prompt( @@ -2727,6 +3110,8 @@ if MCP_AVAILABLE: mcp_server_logo_url=mcp_info.get("logo_url"), namespaced_tool_name=namespaced_tool_name, mcp_session_id=session_id, + mcp_auth_mode=mcp_server.auth_type, + mcp_server_resource=_redact_mcp_resource_url(mcp_server.url), ) else: return StandardLoggingMCPToolCall( @@ -3240,6 +3625,36 @@ if MCP_AVAILABLE: headers={"www-authenticate": authorization_uri}, ) + # token_exchange (OBO): the caller supplied no subject token. Challenge at connect + # (transport level, where WWW-Authenticate survives) with the RFC 9728 resource_metadata + # so the client discovers the IdP, SSOs, and retries with a subject token, which LiteLLM + # then exchanges. A tool-call-time 401 would be wrapped into a JSON-RPC error and the + # header lost, so the discovery flow needs this pre-emptive challenge. + if server and server.auth_type == MCPAuth.oauth2_token_exchange and not oauth2_headers: + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( # noqa: PLC0415 + raise_token_exchange_challenge, + ) + from litellm.proxy.utils import get_server_root_path # noqa: PLC0415 + + raise_token_exchange_challenge(server, root_path=get_server_root_path()) + + # token_exchange (OBO) with a subject present: run the exchange here at the transport + # edge, so a rejected subject raises the RFC 9728 challenge (and a gateway fault its + # public status) instead of the session opening and list_tools masking the failure as + # an empty tool list. Gated to single-server routes; the multi-server aggregate keeps + # absorbing per-server auth failures so one bad server cannot 401 the whole connect. + if ( + server + and server.auth_type == MCPAuth.oauth2_token_exchange + and oauth2_headers + and len(mcp_servers or []) == 1 + ): + await global_mcp_server_manager.preflight_token_exchange( + server=server, + oauth2_headers=oauth2_headers, + user_api_key_auth=user_api_key_auth, + ) + # Pass-through OAuth: when the admin has opted a server into # forwarding the client's bearer token (is_oauth_passthrough) and # the client hasn't supplied one, fail fast with 401 and point @@ -3261,6 +3676,52 @@ if MCP_AVAILABLE: headers={"www-authenticate": www_authenticate}, ) + if ( + server + and server.is_oauth_delegate + and len(mcp_servers or []) == 1 + and _get_forwarded_auth_from_scope(scope) is None + and not _client_has_per_server_auth_header(server, mcp_server_auth_headers) + ): + www_authenticate = _get_passthrough_www_authenticate( + scope=scope, + server_name=server_name, + ) + raise HTTPException( + status_code=401, + detail="Unauthorized", + headers={"www-authenticate": www_authenticate}, + ) + + if ( + server + and server.is_true_passthrough + and len(mcp_servers or []) == 1 + and not _scope_has_authorization_header(scope) + and not _client_has_per_server_auth_header(server, mcp_server_auth_headers) + ): + if server.is_dcr_bridge: + raise HTTPException( + status_code=401, + detail="Unauthorized", + headers={ + "www-authenticate": _get_passthrough_www_authenticate( + scope=scope, + server_name=server_name, + ) + }, + ) + upstream_status, upstream_www_authenticate = await _probe_upstream_auth(server.url or "", "") + if upstream_status == 401 and upstream_www_authenticate: + raise HTTPException( + status_code=401, + detail="Unauthorized", + headers={"www-authenticate": upstream_www_authenticate}, + ) + + def _scope_has_authorization_header(scope: Scope) -> bool: + return any(key.lower() == b"authorization" for key, _ in scope.get("headers", [])) + def _get_forwarded_auth_from_scope(scope: Scope) -> Optional[str]: """Return the upstream-bound ``Authorization`` header value, or None. @@ -3288,7 +3749,7 @@ if MCP_AVAILABLE: url: str, auth_header: str, timeout: float = 5.0, - ) -> tuple: + ) -> tuple[int, Optional[str]]: """JSON-RPC initialize-probe the upstream URL to check whether the token is accepted. Uses POST so StreamableHTTP MCP servers run the same auth path as a @@ -3318,8 +3779,8 @@ if MCP_AVAILABLE: }, } probe_headers = { - "Authorization": auth_header, "Accept": "application/json, text/event-stream", + **({"Authorization": auth_header} if auth_header else {}), } try: resp = await client.post( diff --git a/litellm/proxy/_experimental/mcp_server/tool_search.py b/litellm/proxy/_experimental/mcp_server/tool_search.py new file mode 100644 index 00000000000..fa57a2b3eb2 --- /dev/null +++ b/litellm/proxy/_experimental/mcp_server/tool_search.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +import json +from datetime import datetime +from typing import TYPE_CHECKING, Any, Optional + +if TYPE_CHECKING: + from mcp.types import CallToolResult + + from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj + from litellm.proxy._types import UserAPIKeyAuth + +MCP_TOOL_SEARCH_TOOL_NAME: str = "mcp_tool_search" +MCP_TOOL_CALL_TOOL_NAME: str = "mcp_tool_call" + + +def coerce_top_k(value: Any, default: int = 5) -> int: + try: + return int(value) + except (TypeError, ValueError): + return default + + +def search_tools(query: str, tools: list[dict[str, Any]], top_k: int = 5) -> list[dict[str, Any]]: + if not query: + return [] + tokens = query.lower().split() + + def _score(tool: dict[str, Any]) -> int: + haystack = (tool.get("name", "") + " " + tool.get("description", "")).lower() + return sum(1 for t in tokens if t in haystack) + + scored = ((s, tool) for tool in tools if (s := _score(tool)) > 0) + return [tool for _, tool in sorted(scored, key=lambda x: x[0], reverse=True)[:top_k]] + + +def get_virtual_tool_definitions() -> list[dict[str, Any]]: + return [ + { + "name": MCP_TOOL_SEARCH_TOOL_NAME, + "description": "Search for MCP tools by keyword. Returns top matching tools with names, descriptions, and input schemas.", + "inputSchema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "Keywords to search for in tool names and descriptions.", + }, + "top_k": { + "type": "integer", + "description": "Maximum number of results to return.", + "default": 5, + }, + }, + "required": ["query"], + }, + }, + { + "name": MCP_TOOL_CALL_TOOL_NAME, + "description": "Call an MCP tool by name with the given arguments.", + "inputSchema": { + "type": "object", + "properties": { + "tool_name": { + "type": "string", + "description": "The exact name of the MCP tool to call.", + }, + "arguments": { + "type": "object", + "description": "Arguments to pass to the tool.", + }, + }, + "required": ["tool_name"], + }, + }, + ] + + +async def handle_mcp_tool_search( + query: str, + top_k: int, + user_api_key_dict: UserAPIKeyAuth, + client_ip: Optional[str] = None, + mcp_servers: Optional[list[str]] = None, + mcp_auth_header: Optional[str] = None, + mcp_server_auth_headers: Optional[dict[str, dict[str, str]]] = None, + oauth2_headers: Optional[dict[str, str]] = None, + raw_headers: Optional[dict[str, str]] = None, +) -> CallToolResult: + from mcp.types import CallToolResult, TextContent + + from litellm.proxy._experimental.mcp_server.server import _list_mcp_tools + + mcp_tools = await _list_mcp_tools( + user_api_key_auth=user_api_key_dict, + mcp_servers=mcp_servers, + client_ip=client_ip, + mcp_auth_header=mcp_auth_header, + mcp_server_auth_headers=mcp_server_auth_headers, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + ) + tools = [ + { + "name": t.name, + "description": t.description or "", + "inputSchema": t.inputSchema, + } + for t in mcp_tools + ] + results = search_tools(query, tools, top_k) + return CallToolResult(content=[TextContent(type="text", text=json.dumps(results))], isError=False) + + +async def handle_mcp_tool_call( + tool_name: str, + arguments: dict[str, Any], + user_api_key_dict: UserAPIKeyAuth, + client_ip: Optional[str] = None, + mcp_servers: Optional[list[str]] = None, + mcp_auth_header: Optional[str] = None, + mcp_server_auth_headers: Optional[dict[str, dict[str, str]]] = None, + oauth2_headers: Optional[dict[str, str]] = None, + raw_headers: Optional[dict[str, str]] = None, + litellm_logging_obj: Optional[LiteLLMLoggingObj] = None, +) -> CallToolResult: + from litellm.proxy._experimental.mcp_server.server import ( + _get_allowed_mcp_servers, + execute_mcp_tool, + ) + + allowed_mcp_servers = await _get_allowed_mcp_servers( + user_api_key_auth=user_api_key_dict, + mcp_servers=mcp_servers, + client_ip=client_ip, + ) + + # Reject before dispatch when the key has no accessible servers; otherwise an + # unprefixed local tool name would fall through to the local registry in + # execute_mcp_tool, which has no server permission check. + if not allowed_mcp_servers: + from fastapi import HTTPException + + raise HTTPException(status_code=403, detail="User not allowed to call this tool.") + + return await execute_mcp_tool( + name=tool_name, + arguments=arguments, + allowed_mcp_servers=allowed_mcp_servers, + start_time=datetime.now(), + user_api_key_auth=user_api_key_dict, + mcp_auth_header=mcp_auth_header, + mcp_server_auth_headers=mcp_server_auth_headers, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + litellm_logging_obj=litellm_logging_obj, + ) diff --git a/litellm/proxy/_experimental/mcp_server/utils.py b/litellm/proxy/_experimental/mcp_server/utils.py index 9cb6d404b01..80a469b8c1a 100644 --- a/litellm/proxy/_experimental/mcp_server/utils.py +++ b/litellm/proxy/_experimental/mcp_server/utils.py @@ -214,12 +214,14 @@ def server_applies_tool_allowlist(mcp_server: Any) -> bool: def validate_and_normalize_mcp_server_payload(payload: Any) -> None: """ - Validate and normalize MCP server payload fields (server_name and alias). + Validate and normalize MCP server payload fields (server_name, alias, and + tool_name_to_display_name). This function: 1. Validates that server_name and alias don't contain the MCP_TOOL_PREFIX_SEPARATOR - 2. Normalizes alias by replacing spaces with underscores - 3. Sets default alias if not provided (using server_name as base) + 2. Validates that tool_name_to_display_name values satisfy Bedrock's tool-name pattern + 3. Normalizes alias by replacing spaces with underscores + 4. Sets default alias if not provided (using server_name as base) Args: payload: The payload object containing server_name and alias fields @@ -235,6 +237,10 @@ def validate_and_normalize_mcp_server_payload(payload: Any) -> None: if hasattr(payload, "alias") and payload.alias: validate_mcp_server_name(payload.alias, raise_http_exception=True) + # Tool display name validation: must satisfy Bedrock's tool-name pattern + if hasattr(payload, "tool_name_to_display_name") and payload.tool_name_to_display_name: + validate_tool_display_names(payload.tool_name_to_display_name) + # Alias normalization and defaulting alias = getattr(payload, "alias", None) server_name = getattr(payload, "server_name", None) @@ -409,6 +415,61 @@ def validate_mcp_server_name(server_name: str, raise_http_exception: bool = Fals raise Exception(error_message) +def extract_mcp_tool_result_error_message(result: object) -> Optional[str]: + """The first text content of an ``isError=True`` tool result, or ``None`` + when the result is not an error. + + Accepts both ``mcp.types.CallToolResult`` objects and their dict + equivalents, duck-typed so the ``mcp`` package is not required. + """ + is_error: object = result.get("isError") if isinstance(result, Mapping) else getattr(result, "isError", None) + if is_error is not True: + return None + content: object = result.get("content") if isinstance(result, Mapping) else getattr(result, "content", None) + if isinstance(content, (list, tuple)): + for item in content: + text: object = item.get("text") if isinstance(item, Mapping) else getattr(item, "text", None) + if isinstance(text, str) and text: + return text + return "MCP tool call returned isError=true" + + +TOOL_DISPLAY_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9_-]+$") + + +def validate_tool_display_names(tool_name_to_display_name: Optional[Mapping[str, str]]) -> None: + """ + Validate tool display name overrides against Bedrock's tool-name constraint. + + A display name replaces the tool name sent to the LLM provider, so it must + satisfy the strictest provider requirement in use (Bedrock's + ``[a-zA-Z0-9_-]+``); a name with spaces or other characters saves + successfully but fails every subsequent Bedrock tool call. + + Raises: + HTTPException: If any display name fails the pattern. + """ + if not tool_name_to_display_name: + return + + for original_name, display_name in tool_name_to_display_name.items(): + if display_name and not TOOL_DISPLAY_NAME_PATTERN.match(display_name): + from fastapi import HTTPException + from starlette import status + + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail={ + "error": ( + f"Invalid display name '{display_name}' for tool '{original_name}'. " + "Display names may only contain letters, digits, underscores, and " + "hyphens (no spaces or other special characters), since they replace " + "the tool name sent to the LLM provider." + ) + }, + ) + + class MCPMissingUserEnvVarsError(Exception): """Raised when an MCP request can't be built because the calling user has not supplied one or more required per-user environment variables. diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html new file mode 100644 index 00000000000..7d4cc0b67af --- /dev/null +++ b/litellm/proxy/_experimental/out/404.html @@ -0,0 +1 @@ +404: This page could not be found.LiteLLM Dashboard

404

This page could not be found.

\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/404/index.html b/litellm/proxy/_experimental/out/404/index.html new file mode 100644 index 00000000000..7d4cc0b67af --- /dev/null +++ b/litellm/proxy/_experimental/out/404/index.html @@ -0,0 +1 @@ +404: This page could not be found.LiteLLM Dashboard

404

This page could not be found.

\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.__PAGE__.txt b/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.__PAGE__.txt new file mode 100644 index 00000000000..b87b291253e --- /dev/null +++ b/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[871135,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/__next._full.txt b/litellm/proxy/_experimental/out/__next._full.txt new file mode 100644 index 00000000000..8aebbcdc258 --- /dev/null +++ b/litellm/proxy/_experimental/out/__next._full.txt @@ -0,0 +1,31 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +d:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{},null,false,null]},null,false,null]},null,false,null],"$Lc",false]],"m":"$undefined","G":["$d",["$Le","$Lf"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +10:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +11:I[871135,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js"],"default"] +14:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +15:"$Sreact.suspense" +17:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[["$","$L10",null,{"Component":"$11","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@12","$@13"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true,"nonce":"$undefined"}]],["$","$L14",null,{"children":["$","$15",null,{"name":"Next.MetadataOutlet","children":"$@16"}]}]]}] +c:["$","$1","h",{"children":[null,["$","$L17",null,{"children":"$L18"}],["$","div",null,{"hidden":true,"children":["$","$L19",null,{"children":["$","$15",null,{"name":"Next.Metadata","children":"$L1a"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +e:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +f:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +12:{} +13:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +18:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1b:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +16:null +1a:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1b","4",{}]] diff --git a/litellm/proxy/_experimental/out/__next._head.txt b/litellm/proxy/_experimental/out/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/__next._index.txt b/litellm/proxy/_experimental/out/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/__next._tree.txt b/litellm/proxy/_experimental/out/__next._tree.txt new file mode 100644 index 00000000000..70be0036004 --- /dev/null +++ b/litellm/proxy/_experimental/out/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_buildManifest.js new file mode 100644 index 00000000000..d74e1661bbe --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_buildManifest.js @@ -0,0 +1,16 @@ +self.__BUILD_MANIFEST = { + "__rewrites": { + "afterFiles": [], + "beforeFiles": [ + { + "source": "/litellm-asset-prefix/_next/:path+", + "destination": "/_next/:path+" + } + ], + "fallback": [] + }, + "sortedPages": [ + "/_app", + "/_error" + ] +};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB() \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_clientMiddlewareManifest.js b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_clientMiddlewareManifest.js new file mode 100644 index 00000000000..a8acaffa33a --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_clientMiddlewareManifest.js @@ -0,0 +1 @@ +self.__MIDDLEWARE_MATCHERS = [];self.__MIDDLEWARE_MATCHERS_CB && self.__MIDDLEWARE_MATCHERS_CB() \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_ssgManifest.js new file mode 100644 index 00000000000..5b3ff592fd4 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/N7WCdfNd30Hp6HEF5tFIL/_ssgManifest.js @@ -0,0 +1 @@ +self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB() \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0-px9-g~2oyp5.js b/litellm/proxy/_experimental/out/_next/static/chunks/0-px9-g~2oyp5.js new file mode 100644 index 00000000000..0c51d099fb1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0-px9-g~2oyp5.js @@ -0,0 +1,48 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),n=e.i(540143),l=e.i(915823),a=e.i(619273),i=class extends l.Subscribable{#e;#t=void 0;#r;#n;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#l()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,a.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,a.hashKey)(t.mutationKey)!==(0,a.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#l(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#l(),this.#a()}mutate(e,t){return this.#n=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#l(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#a(e){n.notifyManager.batch(()=>{if(this.#n&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#n.onSuccess?.(e.data,t,r,n)}catch(e){Promise.reject(e)}try{this.#n.onSettled?.(e.data,null,t,r,n)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#n.onError?.(e.error,t,r,n)}catch(e){Promise.reject(e)}try{this.#n.onSettled?.(void 0,e.error,t,r,n)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},o=e.i(912598);e.s(["useMutation",0,function(e,r){let l=(0,o.useQueryClient)(r),[s]=t.useState(()=>new i(l,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let d=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(n.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),c=t.useCallback((e,t)=>{s.mutate(e,t).catch(a.noop)},[s]);if(d.error&&(0,a.shouldThrowError)(s.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:c,mutateAsync:d.mutate}}],954616)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var l=e.i(9583),a=r.forwardRef(function(e,a){return r.createElement(l.default,(0,t.default)({},e,{ref:a,icon:n}))});e.s(["ExclamationCircleOutlined",0,a],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(529681),l=e.i(242064),a=e.i(517455),i=e.i(185793),o=e.i(721369),s=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};let d=e=>{var{prefixCls:n,className:a,hoverable:i=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(l.ConfigContext),c=d("card",n),u=(0,r.default)(`${c}-grid`,a,{[`${c}-grid-hoverable`]:i});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var c=e.i(915654),u=e.i(183293),m=e.i(246422),g=e.i(838378);let p=(0,m.genStyleHooks)("Card",e=>{let t=(0,g.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:r,cardHeadPadding:n,colorBorderSecondary:l,boxShadowTertiary:a,bodyPadding:i,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:a},[`${t}-head`]:(e=>{let{antCls:t,componentCls:r,headerHeight:n,headerPadding:l,tabsMarginBottom:a}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:n,marginBottom:-1,padding:`0 ${(0,c.unit)(l)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${r}-typography, + > ${r}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:a,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:i,borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:r,cardShadow:n,lineWidth:l}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,c.unit)(l)} 0 0 0 ${r}, + 0 ${(0,c.unit)(l)} 0 0 ${r}, + ${(0,c.unit)(l)} ${(0,c.unit)(l)} 0 0 ${r}, + ${(0,c.unit)(l)} 0 0 0 ${r} inset, + 0 ${(0,c.unit)(l)} 0 0 ${r} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:n}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:r,actionsLiMargin:n,cardActionsIconSize:l,colorBorderSecondary:a,actionsBg:i}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:i,borderTop:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`,display:"flex",borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:n,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${r}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,c.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${r}`]:{fontSize:l,lineHeight:(0,c.unit)(e.calc(l).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,c.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${l}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:r}},[`${t}-contain-grid`]:{borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:n}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:r,headerPadding:n,bodyPadding:l}=e;return{[`${t}-head`]:{padding:`0 ${(0,c.unit)(n)}`,background:r,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,c.unit)(e.padding)} ${(0,c.unit)(l)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:r,headerPaddingSM:n,headerHeightSM:l,headerFontSizeSM:a}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:l,padding:`0 ${(0,c.unit)(n)}`,fontSize:a,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:r}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,r;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(r=e.headerPadding)?r:e.paddingLG}});var b=e.i(792812),h=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};let f=e=>{let{actionClasses:r,actions:n=[],actionStyle:l}=e;return t.createElement("ul",{className:r,style:l},n.map((e,r)=>{let l=`action-${r}`;return t.createElement("li",{style:{width:`${100/n.length}%`},key:l},t.createElement("span",null,e))}))},y=t.forwardRef((e,s)=>{let c,{prefixCls:u,className:m,rootClassName:g,style:y,extra:x,headStyle:v={},bodyStyle:j={},title:C,loading:O,bordered:S,variant:$,size:w,type:E,cover:k,actions:T,tabList:P,children:N,activeTabKey:I,defaultActiveTabKey:M,tabBarExtraContent:B,hoverable:R,tabProps:A={},classNames:F,styles:D}=e,L=h(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:z,direction:H,card:_}=t.useContext(l.ConfigContext),[G]=(0,b.default)("card",$,S),W=e=>{var t;return(0,r.default)(null==(t=null==_?void 0:_.classNames)?void 0:t[e],null==F?void 0:F[e])},K=e=>{var t;return Object.assign(Object.assign({},null==(t=null==_?void 0:_.styles)?void 0:t[e]),null==D?void 0:D[e])},X=t.useMemo(()=>{let e=!1;return t.Children.forEach(N,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[N]),q=z("card",u),[U,Q,V]=p(q),Y=t.createElement(i.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},N),Z=void 0!==I,J=Object.assign(Object.assign({},A),{[Z?"activeKey":"defaultActiveKey"]:Z?I:M,tabBarExtraContent:B}),ee=(0,a.default)(w),et=ee&&"default"!==ee?ee:"large",er=P?t.createElement(o.default,Object.assign({size:et},J,{className:`${q}-head-tabs`,onChange:t=>{var r;null==(r=e.onTabChange)||r.call(e,t)},items:P.map(e=>{var{tab:t}=e;return Object.assign({label:t},h(e,["tab"]))})})):null;if(C||x||er){let e=(0,r.default)(`${q}-head`,W("header")),n=(0,r.default)(`${q}-head-title`,W("title")),l=(0,r.default)(`${q}-extra`,W("extra")),a=Object.assign(Object.assign({},v),K("header"));c=t.createElement("div",{className:e,style:a},t.createElement("div",{className:`${q}-head-wrapper`},C&&t.createElement("div",{className:n,style:K("title")},C),x&&t.createElement("div",{className:l,style:K("extra")},x)),er)}let en=(0,r.default)(`${q}-cover`,W("cover")),el=k?t.createElement("div",{className:en,style:K("cover")},k):null,ea=(0,r.default)(`${q}-body`,W("body")),ei=Object.assign(Object.assign({},j),K("body")),eo=t.createElement("div",{className:ea,style:ei},O?Y:N),es=(0,r.default)(`${q}-actions`,W("actions")),ed=(null==T?void 0:T.length)?t.createElement(f,{actionClasses:es,actionStyle:K("actions"),actions:T}):null,ec=(0,n.default)(L,["onTabChange"]),eu=(0,r.default)(q,null==_?void 0:_.className,{[`${q}-loading`]:O,[`${q}-bordered`]:"borderless"!==G,[`${q}-hoverable`]:R,[`${q}-contain-grid`]:X,[`${q}-contain-tabs`]:null==P?void 0:P.length,[`${q}-${ee}`]:ee,[`${q}-type-${E}`]:!!E,[`${q}-rtl`]:"rtl"===H},m,g,Q,V),em=Object.assign(Object.assign({},null==_?void 0:_.style),y);return U(t.createElement("div",Object.assign({ref:s},ec,{className:eu,style:em}),c,el,eo,ed))});var x=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};y.Grid=d,y.Meta=e=>{let{prefixCls:n,className:a,avatar:i,title:o,description:s}=e,d=x(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:c}=t.useContext(l.ConfigContext),u=c("card",n),m=(0,r.default)(`${u}-meta`,a),g=i?t.createElement("div",{className:`${u}-meta-avatar`},i):null,p=o?t.createElement("div",{className:`${u}-meta-title`},o):null,b=s?t.createElement("div",{className:`${u}-meta-description`},s):null,h=p||b?t.createElement("div",{className:`${u}-meta-detail`},p,b):null;return t.createElement("div",Object.assign({},d,{className:m}),g,h)},e.s(["Card",0,y],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(908206),l=e.i(242064),a=e.i(517455),i=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var d=e.i(876556),c=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r},u=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};let m=e=>{let{itemPrefixCls:n,component:l,span:a,className:i,style:o,labelStyle:d,contentStyle:c,bordered:u,label:m,content:g,colon:p,type:b,styles:h}=e,{classNames:f}=t.useContext(s),y=Object.assign(Object.assign({},d),null==h?void 0:h.label),x=Object.assign(Object.assign({},c),null==h?void 0:h.content);if(u)return t.createElement(l,{colSpan:a,style:o,className:(0,r.default)(i,{[`${n}-item-${b}`]:"label"===b||"content"===b,[null==f?void 0:f.label]:(null==f?void 0:f.label)&&"label"===b,[null==f?void 0:f.content]:(null==f?void 0:f.content)&&"content"===b})},null!=m&&t.createElement("span",{style:y},m),null!=g&&t.createElement("span",{style:x},g));return t.createElement(l,{colSpan:a,style:o,className:(0,r.default)(`${n}-item`,i)},t.createElement("div",{className:`${n}-item-container`},null!=m&&t.createElement("span",{style:y,className:(0,r.default)(`${n}-item-label`,null==f?void 0:f.label,{[`${n}-item-no-colon`]:!p})},m),null!=g&&t.createElement("span",{style:x,className:(0,r.default)(`${n}-item-content`,null==f?void 0:f.content)},g)))};function g(e,{colon:r,prefixCls:n,bordered:l},{component:a,type:i,showLabel:o,showContent:s,labelStyle:d,contentStyle:c,styles:u}){return e.map(({label:e,children:g,prefixCls:p=n,className:b,style:h,labelStyle:f,contentStyle:y,span:x=1,key:v,styles:j},C)=>"string"==typeof a?t.createElement(m,{key:`${i}-${v||C}`,className:b,style:h,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),f),null==j?void 0:j.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),y),null==j?void 0:j.content)},span:x,colon:r,component:a,itemPrefixCls:p,bordered:l,label:o?e:null,content:s?g:null,type:i}):[t.createElement(m,{key:`label-${v||C}`,className:b,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),h),f),null==j?void 0:j.label),span:1,colon:r,component:a[0],itemPrefixCls:p,bordered:l,label:e,type:"label"}),t.createElement(m,{key:`content-${v||C}`,className:b,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),h),y),null==j?void 0:j.content),span:2*x-1,component:a[1],itemPrefixCls:p,bordered:l,content:g,type:"content"})])}let p=e=>{let r=t.useContext(s),{prefixCls:n,vertical:l,row:a,index:i,bordered:o}=e;return l?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${i}`,className:`${n}-row`},g(a,e,Object.assign({component:"th",type:"label",showLabel:!0},r))),t.createElement("tr",{key:`content-${i}`,className:`${n}-row`},g(a,e,Object.assign({component:"td",type:"content",showContent:!0},r)))):t.createElement("tr",{key:i,className:`${n}-row`},g(a,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},r)))};e.i(296059);var b=e.i(915654),h=e.i(183293),f=e.i(246422),y=e.i(838378);let x=(0,f.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:r,itemPaddingBottom:n,itemPaddingEnd:l,colonMarginRight:a,colonMarginLeft:i,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,h.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:r}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.padding)} ${(0,b.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:r,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.paddingSM)} ${(0,b.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.paddingXS)} ${(0,b.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},h.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:r,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:n,paddingInlineEnd:l},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,b.unit)(i)} ${(0,b.unit)(a)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,y.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var v=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,n=Object.getOwnPropertySymbols(e);lt.indexOf(n[l])&&Object.prototype.propertyIsEnumerable.call(e,n[l])&&(r[n[l]]=e[n[l]]);return r};let j=e=>{let m,{prefixCls:g,title:b,extra:h,column:f,colon:y=!0,bordered:j,layout:C,children:O,className:S,rootClassName:$,style:w,size:E,labelStyle:k,contentStyle:T,styles:P,items:N,classNames:I}=e,M=v(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:B,direction:R,className:A,style:F,classNames:D,styles:L}=(0,l.useComponentConfig)("descriptions"),z=B("descriptions",g),H=(0,i.default)(),_=t.useMemo(()=>{var e;return"number"==typeof f?f:null!=(e=(0,n.matchScreen)(H,Object.assign(Object.assign({},o),f)))?e:3},[H,f]),G=(m=t.useMemo(()=>N||(0,d.default)(O).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[N,O]),t.useMemo(()=>m.map(e=>{var{span:t}=e,r=c(e,["span"]);return"filled"===t?Object.assign(Object.assign({},r),{filled:!0}):Object.assign(Object.assign({},r),{span:"number"==typeof t?t:(0,n.matchScreen)(H,t)})}),[m,H])),W=(0,a.default)(E),K=((e,r)=>{let[n,l]=(0,t.useMemo)(()=>{let t,n,l,a;return t=[],n=[],l=!1,a=0,r.filter(e=>e).forEach(r=>{let{filled:i}=r,o=u(r,["filled"]);if(i){n.push(o),t.push(n),n=[],a=0;return}let s=e-a;(a+=r.span||1)>=e?(a>e?(l=!0,n.push(Object.assign(Object.assign({},o),{span:s}))):n.push(o),t.push(n),n=[],a=0):n.push(o)}),n.length>0&&t.push(n),[t=t.map(t=>{let r=t.reduce((e,t)=>e+(t.span||1),0);if(r({labelStyle:k,contentStyle:T,styles:{content:Object.assign(Object.assign({},L.content),null==P?void 0:P.content),label:Object.assign(Object.assign({},L.label),null==P?void 0:P.label)},classNames:{label:(0,r.default)(D.label,null==I?void 0:I.label),content:(0,r.default)(D.content,null==I?void 0:I.content)}}),[k,T,P,I,D,L]);return X(t.createElement(s.Provider,{value:Q},t.createElement("div",Object.assign({className:(0,r.default)(z,A,D.root,null==I?void 0:I.root,{[`${z}-${W}`]:W&&"default"!==W,[`${z}-bordered`]:!!j,[`${z}-rtl`]:"rtl"===R},S,$,q,U),style:Object.assign(Object.assign(Object.assign(Object.assign({},F),L.root),null==P?void 0:P.root),w)},M),(b||h)&&t.createElement("div",{className:(0,r.default)(`${z}-header`,D.header,null==I?void 0:I.header),style:Object.assign(Object.assign({},L.header),null==P?void 0:P.header)},b&&t.createElement("div",{className:(0,r.default)(`${z}-title`,D.title,null==I?void 0:I.title),style:Object.assign(Object.assign({},L.title),null==P?void 0:P.title)},b),h&&t.createElement("div",{className:(0,r.default)(`${z}-extra`,D.extra,null==I?void 0:I.extra),style:Object.assign(Object.assign({},L.extra),null==P?void 0:P.extra)},h)),t.createElement("div",{className:`${z}-view`},t.createElement("table",null,t.createElement("tbody",null,K.map((e,r)=>t.createElement(p,{key:r,index:r,colon:y,prefixCls:z,vertical:"vertical"===C,bordered:j,row:e}))))))))};j.Item=({children:e})=>e,e.s(["Descriptions",0,j],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),r=e.i(732961),n=e.i(289882),l=e.i(170517),a=e.i(628882),i=e.i(320890),o=e.i(104458),s=e.i(722319),d=e.i(8398),c=e.i(279728);e.i(765846);var u=e.i(602716),m=e.i(328052),g=e.i(135551);let p=(e,t)=>new g.FastColor(e).setA(t).toRgbString(),b=(e,t)=>new g.FastColor(e).lighten(t).toHexString(),h=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},f=(e,t)=>{let r=e||"#000",n=t||"#fff";return{colorBgBase:r,colorTextBase:n,colorText:p(n,.85),colorTextSecondary:p(n,.65),colorTextTertiary:p(n,.45),colorTextQuaternary:p(n,.25),colorFill:p(n,.18),colorFillSecondary:p(n,.12),colorFillTertiary:p(n,.08),colorFillQuaternary:p(n,.04),colorBgSolid:p(n,.95),colorBgSolidHover:p(n,1),colorBgSolidActive:p(n,.9),colorBgElevated:b(r,12),colorBgContainer:b(r,8),colorBgLayout:b(r,0),colorBgSpotlight:b(r,26),colorBgBlur:p(n,.04),colorBorder:b(r,26),colorBorderSecondary:b(r,19)}},y={defaultSeed:i.defaultConfig.token,useToken:function(){let[e,t,r]=(0,o.useToken)();return{theme:e,token:t,hashId:r}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let r=Object.keys(l.defaultPresetColors).map(t=>{let r=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,n,l)=>(e[`${t}-${l+1}`]=r[l],e[`${t}${l+1}`]=r[l],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),n=null!=t?t:(0,s.default)(e),a=(0,m.default)(e,{generateColorPalettes:h,generateNeutralColorPalettes:f});return Object.assign(Object.assign(Object.assign(Object.assign({},n),r),a),{colorPrimaryBg:a.colorPrimaryBorder,colorPrimaryBgHover:a.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let r=null!=t?t:(0,s.default)(e),n=r.fontSizeSM,l=r.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},r),function(e){let{sizeUnit:t,sizeStep:r}=e,n=r-2;return{sizeXXL:t*(n+10),sizeXL:t*(n+6),sizeLG:t*(n+2),sizeMD:t*(n+2),sizeMS:t*(n+1),size:t*n,sizeSM:t*n,sizeXS:t*(n-1),sizeXXS:t*(n-1)}}(null!=t?t:e)),(0,c.default)(n)),{controlHeight:l}),(0,d.default)(Object.assign(Object.assign({},r),{controlHeight:l})))},getDesignToken:e=>{let i=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):n.default,o=Object.assign(Object.assign({},l.default),null==e?void 0:e.token);return(0,r.getComputedToken)(o,{override:null==e?void 0:e.token},i,a.default)},defaultConfig:i.defaultConfig,_internalContext:i.DesignTokenContext};e.s(["theme",0,y],368869)},127952,e=>{"use strict";var t=e.i(843476),r=e.i(560445),n=e.i(175712),l=e.i(869216),a=e.i(311451),i=e.i(212931),o=e.i(898586),s=e.i(368869),d=e.i(270377),c=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:m,message:g,resourceInformationTitle:p,resourceInformation:b,onCancel:h,onOk:f,confirmLoading:y,requiredConfirmation:x}){let{Title:v,Text:j}=o.Typography,{token:C}=s.theme.useToken(),[O,S]=(0,c.useState)("");return(0,c.useEffect)(()=>{e&&S("")},[e]),(0,t.jsx)(i.Modal,{title:u,open:e,onOk:f,onCancel:h,confirmLoading:y,okText:y?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!x&&O!==x||y},cancelButtonProps:{disabled:y},children:(0,t.jsxs)("div",{className:"space-y-4",children:[m&&(0,t.jsx)(r.Alert,{message:m,type:"warning"}),(0,t.jsx)(n.Card,{title:p,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:C.colorErrorBg,borderColor:C.colorErrorBorder}},style:{backgroundColor:C.colorErrorBg,borderColor:C.colorErrorBorder},children:(0,t.jsx)(l.Descriptions,{column:1,size:"small",children:b&&b.map(({label:e,value:r,...n})=>(0,t.jsx)(l.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(j,{...n,children:r??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(j,{children:g})}),x&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(j,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(j,{children:"Type "}),(0,t.jsx)(j,{strong:!0,type:"danger",children:x}),(0,t.jsx)(j,{children:" to confirm deletion:"})]}),(0,t.jsx)(a.Input,{value:O,onChange:e=>S(e.target.value),placeholder:x,className:"rounded-md",prefix:(0,t.jsx)(d.ExclamationCircleOutlined,{style:{color:C.colorError}}),autoFocus:!0})]})]})})}])},233538,e=>{"use strict";e.s(["isDisabledReactIssue7711",0,function(e){let t=e.parentElement,r=null;for(;t&&!(t instanceof HTMLFieldSetElement);)t instanceof HTMLLegendElement&&(r=t),t=t.parentElement;let n=(null==t?void 0:t.getAttribute("disabled"))==="";return!(n&&function(e){if(!e)return!1;let t=e.previousElementSibling;for(;null!==t;){if(t instanceof HTMLLegendElement)return!1;t=t.previousElementSibling}return!0}(r))&&n}])},83733,233137,e=>{"use strict";let t,r;var n,l,a=e.i(247167),i=e.i(271645),o=e.i(544508),s=e.i(746725),d=e.i(835696);void 0!==a.default&&"u">typeof globalThis&&"u">typeof Element&&(null==(n=null==a.default?void 0:a.default.env)?void 0:n.NODE_ENV)==="test"&&void 0===(null==(l=null==Element?void 0:Element.prototype)?void 0:l.getAnimations)&&(Element.prototype.getAnimations=function(){return console.warn(["Headless UI has polyfilled `Element.prototype.getAnimations` for your tests.","Please install a proper polyfill e.g. `jsdom-testing-mocks`, to silence these warnings.","","Example usage:","```js","import { mockAnimationsApi } from 'jsdom-testing-mocks'","mockAnimationsApi()","```"].join(` +`)),[]});var c=((t=c||{})[t.None=0]="None",t[t.Closed=1]="Closed",t[t.Enter=2]="Enter",t[t.Leave=4]="Leave",t);e.s(["transitionDataAttributes",0,function(e){let t={};for(let r in e)!0===e[r]&&(t[`data-${r}`]="");return t},"useTransition",0,function(e,t,r,n){let[l,a]=(0,i.useState)(r),{hasFlag:c,addFlag:u,removeFlag:m}=function(e=0){let[t,r]=(0,i.useState)(e),n=(0,i.useCallback)(e=>r(e),[t]),l=(0,i.useCallback)(e=>r(t=>t|e),[t]),a=(0,i.useCallback)(e=>(t&e)===e,[t]);return{flags:t,setFlag:n,addFlag:l,hasFlag:a,removeFlag:(0,i.useCallback)(e=>r(t=>t&~e),[r]),toggleFlag:(0,i.useCallback)(e=>r(t=>t^e),[r])}}(e&&l?3:0),g=(0,i.useRef)(!1),p=(0,i.useRef)(!1),b=(0,s.useDisposables)();return(0,d.useIsoMorphicEffect)(()=>{var l;if(e){if(r&&a(!0),!t){r&&u(3);return}return null==(l=null==n?void 0:n.start)||l.call(n,r),function(e,{prepare:t,run:r,done:n,inFlight:l}){let a=(0,o.disposables)();return function(e,{inFlight:t,prepare:r}){if(null!=t&&t.current)return r();let n=e.style.transition;e.style.transition="none",r(),e.offsetHeight,e.style.transition=n}(e,{prepare:t,inFlight:l}),a.nextFrame(()=>{r(),a.requestAnimationFrame(()=>{a.add(function(e,t){var r,n;let l=(0,o.disposables)();if(!e)return l.dispose;let a=!1;l.add(()=>{a=!0});let i=null!=(n=null==(r=e.getAnimations)?void 0:r.call(e).filter(e=>e instanceof CSSTransition))?n:[];return 0===i.length?t():Promise.allSettled(i.map(e=>e.finished)).then(()=>{a||t()}),l.dispose}(e,n))})}),a.dispose}(t,{inFlight:g,prepare(){p.current?p.current=!1:p.current=g.current,g.current=!0,p.current||(r?(u(3),m(4)):(u(4),m(2)))},run(){p.current?r?(m(3),u(4)):(m(4),u(3)):r?m(1):u(1)},done(){var e;p.current&&"function"==typeof t.getAnimations&&t.getAnimations().length>0||(g.current=!1,m(7),r||a(!1),null==(e=null==n?void 0:n.end)||e.call(n,r))}})}},[e,r,t,b]),e?[l,{closed:c(1),enter:c(2),leave:c(4),transition:c(2)||c(4)}]:[r,{closed:void 0,enter:void 0,leave:void 0,transition:void 0}]}],83733);let u=(0,i.createContext)(null);u.displayName="OpenClosedContext";var m=((r=m||{})[r.Open=1]="Open",r[r.Closed=2]="Closed",r[r.Closing=4]="Closing",r[r.Opening=8]="Opening",r);e.s(["OpenClosedProvider",0,function({value:e,children:t}){return i.default.createElement(u.Provider,{value:e},t)},"ResetOpenClosedProvider",0,function({children:e}){return i.default.createElement(u.Provider,{value:null},e)},"State",0,m,"useOpenClosed",0,function(){return(0,i.useContext)(u)}],233137)},677667,674175,886148,543086,e=>{"use strict";let t,r;var n,l=e.i(290571),a=e.i(783222),i=e.i(433336),o=e.i(271645),s=e.i(394487),d=e.i(914189),c=e.i(144279),u=e.i(294316),m=e.i(83733);let g=(0,o.createContext)(()=>{});function p({value:e,children:t}){return o.default.createElement(g.Provider,{value:e},t)}e.s(["CloseProvider",0,p],674175);var b=e.i(233137),h=e.i(233538),f=e.i(397701),y=e.i(402155),x=e.i(700020);let v=null!=(n=o.default.startTransition)?n:function(e){e()};var j=e.i(998348),C=((t=C||{})[t.Open=0]="Open",t[t.Closed=1]="Closed",t),O=((r=O||{})[r.ToggleDisclosure=0]="ToggleDisclosure",r[r.CloseDisclosure=1]="CloseDisclosure",r[r.SetButtonId=2]="SetButtonId",r[r.SetPanelId=3]="SetPanelId",r[r.SetButtonElement=4]="SetButtonElement",r[r.SetPanelElement=5]="SetPanelElement",r);let S={0:e=>({...e,disclosureState:(0,f.match)(e.disclosureState,{0:1,1:0})}),1:e=>1===e.disclosureState?e:{...e,disclosureState:1},2:(e,t)=>e.buttonId===t.buttonId?e:{...e,buttonId:t.buttonId},3:(e,t)=>e.panelId===t.panelId?e:{...e,panelId:t.panelId},4:(e,t)=>e.buttonElement===t.element?e:{...e,buttonElement:t.element},5:(e,t)=>e.panelElement===t.element?e:{...e,panelElement:t.element}},$=(0,o.createContext)(null);function w(e){let t=(0,o.useContext)($);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,w),t}return t}$.displayName="DisclosureContext";let E=(0,o.createContext)(null);E.displayName="DisclosureAPIContext";let k=(0,o.createContext)(null);function T(e,t){return(0,f.match)(t.type,S,e,t)}k.displayName="DisclosurePanelContext";let P=o.Fragment,N=x.RenderFeatures.RenderStrategy|x.RenderFeatures.Static,I=Object.assign((0,x.forwardRefWithAs)(function(e,t){let{defaultOpen:r=!1,...n}=e,l=(0,o.useRef)(null),a=(0,u.useSyncRefs)(t,(0,u.optionalRef)(e=>{l.current=e},void 0===e.as||e.as===o.Fragment)),i=(0,o.useReducer)(T,{disclosureState:+!r,buttonElement:null,panelElement:null,buttonId:null,panelId:null}),[{disclosureState:s,buttonId:c},m]=i,g=(0,d.useEvent)(e=>{m({type:1});let t=(0,y.getOwnerDocument)(l);if(!t||!c)return;let r=e?e instanceof HTMLElement?e:e.current instanceof HTMLElement?e.current:t.getElementById(c):t.getElementById(c);null==r||r.focus()}),h=(0,o.useMemo)(()=>({close:g}),[g]),v=(0,o.useMemo)(()=>({open:0===s,close:g}),[s,g]),j=(0,x.useRender)();return o.default.createElement($.Provider,{value:i},o.default.createElement(E.Provider,{value:h},o.default.createElement(p,{value:g},o.default.createElement(b.OpenClosedProvider,{value:(0,f.match)(s,{0:b.State.Open,1:b.State.Closed})},j({ourProps:{ref:a},theirProps:n,slot:v,defaultTag:P,name:"Disclosure"})))))}),{Button:(0,x.forwardRefWithAs)(function(e,t){let r=(0,o.useId)(),{id:n=`headlessui-disclosure-button-${r}`,disabled:l=!1,autoFocus:m=!1,...g}=e,[p,b]=w("Disclosure.Button"),f=(0,o.useContext)(k),y=null!==f&&f===p.panelId,v=(0,o.useRef)(null),C=(0,u.useSyncRefs)(v,t,(0,d.useEvent)(e=>{if(!y)return b({type:4,element:e})}));(0,o.useEffect)(()=>{if(!y)return b({type:2,buttonId:n}),()=>{b({type:2,buttonId:null})}},[n,b,y]);let O=(0,d.useEvent)(e=>{var t;if(y){if(1===p.disclosureState)return;switch(e.key){case j.Keys.Space:case j.Keys.Enter:e.preventDefault(),e.stopPropagation(),b({type:0}),null==(t=p.buttonElement)||t.focus()}}else switch(e.key){case j.Keys.Space:case j.Keys.Enter:e.preventDefault(),e.stopPropagation(),b({type:0})}}),S=(0,d.useEvent)(e=>{e.key===j.Keys.Space&&e.preventDefault()}),$=(0,d.useEvent)(e=>{var t;(0,h.isDisabledReactIssue7711)(e.currentTarget)||l||(y?(b({type:0}),null==(t=p.buttonElement)||t.focus()):b({type:0}))}),{isFocusVisible:E,focusProps:T}=(0,a.useFocusRing)({autoFocus:m}),{isHovered:P,hoverProps:N}=(0,i.useHover)({isDisabled:l}),{pressed:I,pressProps:M}=(0,s.useActivePress)({disabled:l}),B=(0,o.useMemo)(()=>({open:0===p.disclosureState,hover:P,active:I,disabled:l,focus:E,autofocus:m}),[p,P,I,E,l,m]),R=(0,c.useResolveButtonType)(e,p.buttonElement),A=y?(0,x.mergeProps)({ref:C,type:R,disabled:l||void 0,autoFocus:m,onKeyDown:O,onClick:$},T,N,M):(0,x.mergeProps)({ref:C,id:n,type:R,"aria-expanded":0===p.disclosureState,"aria-controls":p.panelElement?p.panelId:void 0,disabled:l||void 0,autoFocus:m,onKeyDown:O,onKeyUp:S,onClick:$},T,N,M);return(0,x.useRender)()({ourProps:A,theirProps:g,slot:B,defaultTag:"button",name:"Disclosure.Button"})}),Panel:(0,x.forwardRefWithAs)(function(e,t){let r=(0,o.useId)(),{id:n=`headlessui-disclosure-panel-${r}`,transition:l=!1,...a}=e,[i,s]=w("Disclosure.Panel"),{close:c}=function e(t){let r=(0,o.useContext)(E);if(null===r){let r=Error(`<${t} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(r,e),r}return r}("Disclosure.Panel"),[g,p]=(0,o.useState)(null),h=(0,u.useSyncRefs)(t,(0,d.useEvent)(e=>{v(()=>s({type:5,element:e}))}),p);(0,o.useEffect)(()=>(s({type:3,panelId:n}),()=>{s({type:3,panelId:null})}),[n,s]);let f=(0,b.useOpenClosed)(),[y,j]=(0,m.useTransition)(l,g,null!==f?(f&b.State.Open)===b.State.Open:0===i.disclosureState),C=(0,o.useMemo)(()=>({open:0===i.disclosureState,close:c}),[i.disclosureState,c]),O={ref:h,id:n,...(0,m.transitionDataAttributes)(j)},S=(0,x.useRender)();return o.default.createElement(b.ResetOpenClosedProvider,null,o.default.createElement(k.Provider,{value:i.panelId},S({ourProps:O,theirProps:a,slot:C,defaultTag:"div",features:N,visible:y,name:"Disclosure.Panel"})))})});e.s(["Disclosure",0,I],886148);let M=(0,o.createContext)(void 0);var B=e.i(444755);let R=(0,e.i(673706).makeClassName)("Accordion"),A=(0,o.createContext)({isOpen:!1}),F=o.default.forwardRef((e,t)=>{var r;let{defaultOpen:n=!1,children:a,className:i}=e,s=(0,l.__rest)(e,["defaultOpen","children","className"]),d=null!=(r=(0,o.useContext)(M))?r:(0,B.tremorTwMerge)("rounded-tremor-default border");return o.default.createElement(I,Object.assign({as:"div",ref:t,className:(0,B.tremorTwMerge)(R("root"),"overflow-hidden","bg-tremor-background border-tremor-border","dark:bg-dark-tremor-background dark:border-dark-tremor-border",d,i),defaultOpen:n},s),({open:e})=>o.default.createElement(A.Provider,{value:{isOpen:e}},a))});F.displayName="Accordion",e.s(["OpenContext",0,A,"default",0,F],543086),e.s(["Accordion",0,F],677667)},898667,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(886148);let l=e=>{var n=(0,t.__rest)(e,[]);return r.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},n),r.default.createElement("path",{d:"M11.9999 10.8284L7.0502 15.7782L5.63599 14.364L11.9999 8L18.3639 14.364L16.9497 15.7782L11.9999 10.8284Z"}))};var a=e.i(543086),i=e.i(444755);let o=(0,e.i(673706).makeClassName)("AccordionHeader"),s=r.default.forwardRef((e,s)=>{let{children:d,className:c}=e,u=(0,t.__rest)(e,["children","className"]),{isOpen:m}=(0,r.useContext)(a.OpenContext);return r.default.createElement(n.Disclosure.Button,Object.assign({ref:s,className:(0,i.tremorTwMerge)(o("root"),"w-full flex items-center justify-between px-4 py-3","text-tremor-content-emphasis","dark:text-dark-tremor-content-emphasis",c)},u),r.default.createElement("div",{className:(0,i.tremorTwMerge)(o("children"),"flex flex-1 text-inherit mr-4")},d),r.default.createElement("div",null,r.default.createElement(l,{className:(0,i.tremorTwMerge)(o("arrowIcon"),"h-5 w-5 -mr-1","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle",m?"transition-all":"transition-all -rotate-180")})))});s.displayName="AccordionHeader",e.s(["AccordionHeader",0,s],898667)},130643,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(886148),l=e.i(444755);let a=(0,e.i(673706).makeClassName)("AccordionBody"),i=r.default.forwardRef((e,i)=>{let{children:o,className:s}=e,d=(0,t.__rest)(e,["children","className"]);return r.default.createElement(n.Disclosure.Panel,Object.assign({ref:i,className:(0,l.tremorTwMerge)(a("root"),"w-full text-tremor-default px-4 pb-3","text-tremor-content","dark:text-dark-tremor-content",s)},d),o)});i.displayName="AccordionBody",e.s(["AccordionBody",0,i],130643)},728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(829087),l=e.i(480731),a=e.i(444755),i=e.i(673706),o=e.i(95779);let s={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,i.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:g,variant:p="simple",tooltip:b,size:h=l.Sizes.SM,color:f,className:y}=e,x=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),v=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,i.getColorClassNames)(t,o.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,i.getColorClassNames)(t,o.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,a.tremorTwMerge)((0,i.getColorClassNames)(t,o.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(p,f),{tooltipProps:j,getReferenceProps:C}=(0,n.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,j.refs.setReference]),className:(0,a.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",v.bgColor,v.textColor,v.borderColor,v.ringColor,c[p].rounded,c[p].border,c[p].shadow,c[p].ring,s[h].paddingX,s[h].paddingY,y)},C,x),r.default.createElement(n.default,Object.assign({text:b},j)),r.default.createElement(g,{className:(0,a.tremorTwMerge)(u("icon"),"shrink-0",d[h].height,d[h].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},122577,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,r],122577)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},434626,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,r],434626)},902555,e=>{"use strict";var t=e.i(843476),r=e.i(591935),n=e.i(122577),l=e.i(278587),a=e.i(68155),i=e.i(360820),o=e.i(871943),s=e.i(434626),d=e.i(551332),c=e.i(592968),u=e.i(115504),m=e.i(752978);function g({icon:e,onClick:r,className:n,disabled:l,dataTestId:a}){return l?(0,t.jsx)(m.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":a}):(0,t.jsx)(m.Icon,{icon:e,size:"sm",onClick:r,className:(0,u.cx)("cursor-pointer",n),"data-testid":a})}let p={Edit:{icon:r.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:a.TrashIcon,className:"hover:text-red-600"},Test:{icon:n.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:l.RefreshIcon,className:"hover:text-green-600"},Up:{icon:i.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:o.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:s.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:r,disabled:n=!1,disabledTooltipText:l,dataTestId:a,variant:i}){let{icon:o,className:s}=p[i];return(0,t.jsx)(c.Tooltip,{title:n?l:r,children:(0,t.jsx)("span",{children:(0,t.jsx)(g,{icon:o,onClick:e,className:s,disabled:n,dataTestId:a})})})}],902555)},359200,e=>{"use strict";var t=e.i(843476),r=e.i(994388),n=e.i(304967),l=e.i(197647),a=e.i(653824),i=e.i(269200),o=e.i(942232),s=e.i(977572),d=e.i(427612),c=e.i(64848),u=e.i(496020),m=e.i(881073),g=e.i(404206),p=e.i(723731),b=e.i(599724),h=e.i(271645),f=e.i(650056),y=e.i(127952),x=e.i(902555),v=e.i(727749),j=e.i(266027),C=e.i(954616),O=e.i(912598),S=e.i(243652),$=e.i(602869),w=e.i(135214);let E=(0,S.createQueryKeys)("budgets");e.i(622826);var k=e.i(964471),T=e.i(779241),P=e.i(677667),N=e.i(898667),I=e.i(130643),M=e.i(464571),B=e.i(212931),R=e.i(808613),A=e.i(28651),F=e.i(199133);let D=({isModalVisible:e,setIsModalVisible:r})=>{let[n]=R.Form.useForm(),l=(()=>{let{accessToken:e}=(0,w.default)(),t=(0,O.useQueryClient)();return(0,C.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return(0,$.budgetCreateCall)(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:E.all})}})})(),a=async e=>{try{v.default.info("Making API Call"),await l.mutateAsync(e),v.default.success("Budget Created"),n.resetFields(),r(!1)}catch(e){console.error("Error creating the budget:",e),v.default.fromBackend(`Error creating the budget: ${e}`)}};return(0,t.jsx)(B.Modal,{title:"Create Budget",open:e,width:800,footer:null,onOk:()=>{r(!1),n.resetFields()},onCancel:()=>{r(!1),n.resetFields()},children:(0,t.jsxs)(R.Form,{form:n,onFinish:a,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(R.Form.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,t.jsx)(T.TextInput,{placeholder:""})}),(0,t.jsx)(R.Form.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,t.jsx)(A.InputNumber,{step:1,precision:2,width:200})}),(0,t.jsx)(R.Form.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,t.jsx)(A.InputNumber,{step:1,precision:2,width:200})}),(0,t.jsxs)(P.Accordion,{className:"mt-20 mb-8",children:[(0,t.jsx)(N.AccordionHeader,{children:(0,t.jsx)("b",{children:"Optional Settings"})}),(0,t.jsxs)(I.AccordionBody,{children:[(0,t.jsx)(R.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(A.InputNumber,{step:.01,precision:2,width:200})}),(0,t.jsx)(R.Form.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(F.Select,{defaultValue:null,placeholder:"n/a",children:[(0,t.jsx)(F.Select.Option,{value:"24h",children:"daily"}),(0,t.jsx)(F.Select.Option,{value:"7d",children:"weekly"}),(0,t.jsx)(F.Select.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(M.Button,{htmlType:"submit",children:"Create Budget"})})]})})},L=({isModalVisible:e,setIsModalVisible:r,existingBudget:n})=>{let[l]=R.Form.useForm(),a=(()=>{let{accessToken:e}=(0,w.default)(),t=(0,O.useQueryClient)();return(0,C.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return(0,$.budgetUpdateCall)(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:E.all})}})})();(0,h.useEffect)(()=>{l.setFieldsValue(n)},[n,l]);let i=async e=>{try{v.default.info("Making API Call"),await a.mutateAsync(e),v.default.success("Budget Updated"),l.resetFields(),r(!1)}catch(e){console.error("Error updating the budget:",e),v.default.fromBackend(`Error updating the budget: ${e}`)}};return(0,t.jsx)(B.Modal,{title:"Edit Budget",open:e,width:800,footer:null,onOk:()=>{r(!1),l.resetFields()},onCancel:()=>{r(!1),l.resetFields()},children:(0,t.jsxs)(R.Form,{form:l,onFinish:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:n,children:[(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(R.Form.Item,{label:"Budget ID",name:"budget_id",help:"Budget ID cannot be changed after creation",children:(0,t.jsx)(T.TextInput,{placeholder:"",disabled:!0})}),(0,t.jsx)(R.Form.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,t.jsx)(A.InputNumber,{step:1,precision:2,width:200})}),(0,t.jsx)(R.Form.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,t.jsx)(A.InputNumber,{step:1,precision:2,width:200})}),(0,t.jsxs)(P.Accordion,{className:"mt-20 mb-8",children:[(0,t.jsx)(N.AccordionHeader,{children:(0,t.jsx)("b",{children:"Optional Settings"})}),(0,t.jsxs)(I.AccordionBody,{children:[(0,t.jsx)(R.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(A.InputNumber,{step:.01,precision:2,width:200})}),(0,t.jsx)(R.Form.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(F.Select,{defaultValue:null,placeholder:"n/a",children:[(0,t.jsx)(F.Select.Option,{value:"24h",children:"daily"}),(0,t.jsx)(F.Select.Option,{value:"7d",children:"weekly"}),(0,t.jsx)(F.Select.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(M.Button,{htmlType:"submit",children:"Save"})})]})})},z=` +curl -X POST --location '/end_user/new' \\ + +-H 'Authorization: Bearer ' \\ + +-H 'Content-Type: application/json' \\ + +-d '{"user_id": "my-customer-id', "budget_id": ""}' # 👈 KEY CHANGE + +`,H=` +curl -X POST --location '/chat/completions' \\ + +-H 'Authorization: Bearer ' \\ + +-H 'Content-Type: application/json' \\ + +-d '{ + "model": "gpt-3.5-turbo', + "messages":[{"role": "user", "content": "Hey, how's it going?"}], + "user": "my-customer-id" +}' # 👈 KEY CHANGE + +`,_=`from openai import OpenAI +client = OpenAI( + base_url="", + api_key="" +) + +completion = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello!"} + ], + user="my-customer-id" +) + +print(completion.choices[0].message)`;var G=e.i(708347);let W=({accessToken:e})=>{let[S,T]=(0,h.useState)(!1),[P,N]=(0,h.useState)(!1),[I,M]=(0,h.useState)(null),[B,R]=(0,h.useState)(!1),{userRole:A}=(0,w.default)(),F=(0,G.isProxyAdminRole)(A??""),{data:W=[]}=(()=>{let{accessToken:e}=(0,w.default)();return(0,j.useQuery)({queryKey:E.list({}),queryFn:async()=>(await (0,$.getBudgetList)(e)??[]).filter(e=>null!=e),enabled:!!e})})(),K=(()=>{let{accessToken:e}=(0,w.default)(),t=(0,O.useQueryClient)();return(0,C.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return(0,$.budgetDeleteCall)(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:E.all})}})})(),X=async t=>{null!=e&&(M(t),N(!0))},q=async()=>{if(I&&null!=e)try{await K.mutateAsync(I.budget_id),v.default.success("Budget deleted.")}catch(e){console.error("Error deleting budget:",e),"function"==typeof v.default.fromBackend?v.default.fromBackend("Failed to delete budget"):v.default.info("Failed to delete budget")}finally{R(!1),M(null)}};return(0,t.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[F&&(0,t.jsx)(r.Button,{size:"sm",variant:"primary",className:"mb-2",onClick:()=>T(!0),children:"+ Create Budget"}),(0,t.jsxs)(a.TabGroup,{children:[(0,t.jsxs)(m.TabList,{children:[(0,t.jsx)(l.Tab,{children:"Budgets"}),(0,t.jsx)(l.Tab,{children:"Examples"})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(g.TabPanel,{children:(0,t.jsxs)("div",{className:"mt-6",children:[(0,t.jsx)(D,{isModalVisible:S,setIsModalVisible:T}),I&&(0,t.jsx)(L,{isModalVisible:P,setIsModalVisible:N,existingBudget:I}),(0,t.jsxs)(n.Card,{children:[(0,t.jsx)(b.Text,{children:"Create a budget to assign to customers."}),(0,t.jsxs)(i.Table,{children:[(0,t.jsx)(d.TableHead,{children:(0,t.jsxs)(u.TableRow,{children:[(0,t.jsx)(c.TableHeaderCell,{children:"Budget ID"}),(0,t.jsx)(c.TableHeaderCell,{children:"Max Budget"}),(0,t.jsx)(c.TableHeaderCell,{children:"TPM"}),(0,t.jsx)(c.TableHeaderCell,{children:"RPM"})]})}),(0,t.jsx)(o.TableBody,{children:W.slice().sort((e,t)=>new Date(t.updated_at).getTime()-new Date(e.updated_at).getTime()).map(e=>(0,t.jsxs)(u.TableRow,{children:[(0,t.jsx)(s.TableCell,{children:e.budget_id}),(0,t.jsx)(s.TableCell,{children:(0,t.jsx)(k.MoneyCell,{value:e.max_budget,decimals:2,showZero:!0,emptyText:"Unlimited"})}),(0,t.jsx)(s.TableCell,{children:e.tpm_limit?e.tpm_limit:"n/a"}),(0,t.jsx)(s.TableCell,{children:e.rpm_limit?e.rpm_limit:"n/a"}),F&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(x.default,{variant:"Edit",tooltipText:"Edit budget",onClick:()=>X(e),dataTestId:"edit-budget-button"}),(0,t.jsx)(x.default,{variant:"Delete",tooltipText:"Delete budget",onClick:()=>{M(e),R(!0)},dataTestId:"delete-budget-button"})]})]},e.budget_id))})]})]}),(0,t.jsx)(y.default,{isOpen:B,title:"Delete Budget?",message:"Are you sure you want to delete this budget? This action cannot be undone.",resourceInformationTitle:"Budget Information",resourceInformation:[{label:"Budget ID",value:I?.budget_id,code:!0},{label:"Max Budget",value:I?.max_budget},{label:"TPM",value:I?.tpm_limit},{label:"RPM",value:I?.rpm_limit}],onCancel:()=>{R(!1)},onOk:q,confirmLoading:K.isPending})]})}),(0,t.jsx)(g.TabPanel,{children:(0,t.jsxs)("div",{className:"mt-6",children:[(0,t.jsx)(b.Text,{className:"text-base",children:"How to use budget id"}),(0,t.jsxs)(a.TabGroup,{children:[(0,t.jsxs)(m.TabList,{children:[(0,t.jsx)(l.Tab,{children:"Assign Budget to Customer"}),(0,t.jsx)(l.Tab,{children:"Test it (Curl)"}),(0,t.jsx)(l.Tab,{children:"Test it (OpenAI SDK)"})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(g.TabPanel,{children:(0,t.jsx)(f.Prism,{language:"bash",children:z})}),(0,t.jsx)(g.TabPanel,{children:(0,t.jsx)(f.Prism,{language:"bash",children:H})}),(0,t.jsx)(g.TabPanel,{children:(0,t.jsx)(f.Prism,{language:"python",children:_})})]})]})]})})]})]})]})};e.s(["default",0,function(){let{accessToken:e}=(0,w.default)();return(0,t.jsx)(W,{accessToken:e})}],359200)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0-~nw1zmks9_4.js b/litellm/proxy/_experimental/out/_next/static/chunks/0-~nw1zmks9_4.js new file mode 100644 index 00000000000..6504ddd6e5e --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0-~nw1zmks9_4.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,737434,e=>{"use strict";var t=e.i(184163);e.s(["DownloadOutlined",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),i=e.i(201072),n=e.i(121229),s=e.i(726289),o=e.i(864517),a=e.i(343794),l=e.i(529681),c=e.i(242064),u=e.i(931067),d=e.i(209428),h=e.i(703923),f={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},p=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),i=!1;e.current.forEach(function(e){if(e){i=!0;var n=e.style;n.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(n.transitionDuration="0s, 0s")}}),i&&(r.current=Date.now())}),e.current},g=e.i(410160),m=e.i(392221),y=e.i(654310),_=0,b=(0,y.default)();let k=function(e){var r=t.useState(),i=(0,m.default)(r,2),n=i[0],s=i[1];return t.useEffect(function(){var e;s("rc_progress_".concat((b?(e=_,_+=1):e="TEST_OR_SSR",e)))},[]),e||n};var v=function(e){var r=e.bg,i=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},i)};function C(e,t){return Object.keys(e).map(function(r){var i=parseFloat(r),n="".concat(Math.floor(i*t),"%");return"".concat(e[r]," ").concat(n)})}var x=t.forwardRef(function(e,r){var i=e.prefixCls,n=e.color,s=e.gradientId,o=e.radius,a=e.style,l=e.ptg,c=e.strokeLinecap,u=e.strokeWidth,d=e.size,h=e.gapDegree,f=n&&"object"===(0,g.default)(n),p=d/2,m=t.createElement("circle",{className:"".concat(i,"-circle-path"),r:o,cx:p,cy:p,stroke:f?"#FFF":void 0,strokeLinecap:c,strokeWidth:u,opacity:+(0!==l),style:a,ref:r});if(!f)return m;var y="".concat(s,"-conic"),_=C(n,(360-h)/360),b=C(n,1),k="conic-gradient(from ".concat(h?"".concat(180+h/2,"deg"):"0deg",", ").concat(_.join(", "),")"),x="linear-gradient(to ".concat(h?"bottom":"top",", ").concat(b.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:y},m),t.createElement("foreignObject",{x:0,y:0,width:d,height:d,mask:"url(#".concat(y,")")},t.createElement(v,{bg:x},t.createElement(v,{bg:k}))))}),E=function(e,t,r,i,n,s,o,a,l,c){var u=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,d=(100-i)/100*t;return"round"===l&&100!==i&&(d+=c/2)>=t&&(d=t-.01),{stroke:"string"==typeof a?a:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:d+u,transform:"rotate(".concat(n+r/100*360*((360-s)/360)+(0===s?0:({bottom:0,top:180,left:90,right:-90})[o]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},w=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function S(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let $=function(e){var r,i,n,s,o=(0,d.default)((0,d.default)({},f),e),l=o.id,c=o.prefixCls,m=o.steps,y=o.strokeWidth,_=o.trailWidth,b=o.gapDegree,v=void 0===b?0:b,C=o.gapPosition,$=o.trailColor,O=o.strokeLinecap,R=o.style,I=o.className,A=o.strokeColor,j=o.percent,D=(0,h.default)(o,w),T=k(l),L="".concat(T,"-gradient"),F=50-y/2,z=2*Math.PI*F,M=v>0?90+v/2:-90,P=(360-v)/360*z,N="object"===(0,g.default)(m)?m:{count:m,gap:2},W=N.count,B=N.gap,U=S(j),H=S(A),q=H.find(function(e){return e&&"object"===(0,g.default)(e)}),K=q&&"object"===(0,g.default)(q)?"butt":O,X=E(z,P,0,100,M,v,C,$,K,y),Q=p();return t.createElement("svg",(0,u.default)({className:(0,a.default)("".concat(c,"-circle"),I),viewBox:"0 0 ".concat(100," ").concat(100),style:R,id:l,role:"presentation"},D),!W&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:F,cx:50,cy:50,stroke:$,strokeLinecap:K,strokeWidth:_||y,style:X}),W?(r=Math.round(W*(U[0]/100)),i=100/W,n=0,Array(W).fill(null).map(function(e,s){var o=s<=r-1?H[0]:$,a=o&&"object"===(0,g.default)(o)?"url(#".concat(L,")"):void 0,l=E(z,P,n,i,M,v,C,o,"butt",y,B);return n+=(P-l.strokeDashoffset+B)*100/P,t.createElement("circle",{key:s,className:"".concat(c,"-circle-path"),r:F,cx:50,cy:50,stroke:a,strokeWidth:y,opacity:1,style:l,ref:function(e){Q[s]=e}})})):(s=0,U.map(function(e,r){var i=H[r]||H[H.length-1],n=E(z,P,s,e,M,v,C,i,K,y);return s+=e,t.createElement(x,{key:r,color:i,ptg:e,radius:F,prefixCls:c,gradientId:L,style:n,strokeLinecap:K,strokeWidth:y,gapDegree:v,ref:function(e){Q[r]=e},size:100})}).reverse()))};var O=e.i(491816);e.i(765846);var R=e.i(896091);function I(e){return!e||e<0?0:e>100?100:e}function A({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let j=(e,t,r)=>{var i,n,s,o;let a=-1,l=-1;if("step"===t){let t=r.steps,i=r.strokeWidth;"string"==typeof e||void 0===e?(a="small"===e?2:14,l=null!=i?i:8):"number"==typeof e?[a,l]=[e,e]:[a=14,l=8]=Array.isArray(e)?e:[e.width,e.height],a*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?l=t||("small"===e?6:8):"number"==typeof e?[a,l]=[e,e]:[a=-1,l=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[a,l]="small"===e?[60,60]:[120,120]:"number"==typeof e?[a,l]=[e,e]:Array.isArray(e)&&(a=null!=(n=null!=(i=e[0])?i:e[1])?n:120,l=null!=(o=null!=(s=e[0])?s:e[1])?o:120));return[a,l]},D=e=>{let{prefixCls:r,trailColor:i=null,strokeLinecap:n="round",gapPosition:s,gapDegree:o,width:l=120,type:c,children:u,success:d,size:h=l,steps:f}=e,[p,g]=j(h,"circle"),{strokeWidth:m}=e;void 0===m&&(m=Math.max(3/p*100,6));let y=t.useMemo(()=>o||0===o?o:"dashboard"===c?75:void 0,[o,c]),_=(({percent:e,success:t,successPercent:r})=>{let i=I(A({success:t,successPercent:r}));return[i,I(I(e)-i)]})(e),b="[object Object]"===Object.prototype.toString.call(e.strokeColor),k=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||R.presetPrimaryColors.green,t||null]})({success:d,strokeColor:e.strokeColor}),v=(0,a.default)(`${r}-inner`,{[`${r}-circle-gradient`]:b}),C=t.createElement($,{steps:f,percent:f?_[1]:_,strokeWidth:m,trailWidth:m,strokeColor:f?k[1]:k,strokeLinecap:n,trailColor:i,prefixCls:r,gapDegree:y,gapPosition:s||"dashboard"===c&&"bottom"||void 0}),x=p<=20,E=t.createElement("div",{className:v,style:{width:p,height:g,fontSize:.15*p+6}},C,!x&&u);return x?t.createElement(O.default,{title:u},E):E};e.i(296059);var T=e.i(694758),L=e.i(915654),F=e.i(183293),z=e.i(246422),M=e.i(838378);let P="--progress-line-stroke-color",N="--progress-percent",W=e=>{let t=e?"100%":"-100%";return new T.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},B=(0,z.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,M.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,F.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${P})`]},height:"100%",width:`calc(1 / var(${N}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,L.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:W(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:W(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var U=function(e,t){var r={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(r[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,i=Object.getOwnPropertySymbols(e);nt.indexOf(i[n])&&Object.prototype.propertyIsEnumerable.call(e,i[n])&&(r[i[n]]=e[i[n]]);return r};let H=e=>{let{prefixCls:r,direction:i,percent:n,size:s,strokeWidth:o,strokeColor:l,strokeLinecap:c="round",children:u,trailColor:d=null,percentPosition:h,success:f}=e,{align:p,type:g}=h,m=l&&"string"!=typeof l?((e,t)=>{let{from:r=R.presetPrimaryColors.blue,to:i=R.presetPrimaryColors.blue,direction:n="rtl"===t?"to left":"to right"}=e,s=U(e,["from","to","direction"]);if(0!==Object.keys(s).length){let e,t=(e=[],Object.keys(s).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:s[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${n}, ${t})`;return{background:r,[P]:r}}let o=`linear-gradient(${n}, ${r}, ${i})`;return{background:o,[P]:o}})(l,i):{[P]:l,background:l},y="square"===c||"butt"===c?0:void 0,[_,b]=j(null!=s?s:[-1,o||("small"===s?6:8)],"line",{strokeWidth:o}),k=Object.assign(Object.assign({width:`${I(n)}%`,height:b,borderRadius:y},m),{[N]:I(n)/100}),v=A(e),C={width:`${I(v)}%`,height:b,borderRadius:y,backgroundColor:null==f?void 0:f.strokeColor},x=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:d||void 0,borderRadius:y}},t.createElement("div",{className:(0,a.default)(`${r}-bg`,`${r}-bg-${g}`),style:k},"inner"===g&&u),void 0!==v&&t.createElement("div",{className:`${r}-success-bg`,style:C})),E="outer"===g&&"start"===p,w="outer"===g&&"end"===p;return"outer"===g&&"center"===p?t.createElement("div",{className:`${r}-layout-bottom`},x,u):t.createElement("div",{className:`${r}-outer`,style:{width:_<0?"100%":_}},E&&u,x,w&&u)},q=e=>{let{size:r,steps:i,rounding:n=Math.round,percent:s=0,strokeWidth:o=8,strokeColor:l,trailColor:c=null,prefixCls:u,children:d}=e,h=n(s/100*i),[f,p]=j(null!=r?r:["small"===r?2:14,o],"step",{steps:i,strokeWidth:o}),g=f/i,m=Array.from({length:i});for(let e=0;et.indexOf(i)&&(r[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,i=Object.getOwnPropertySymbols(e);nt.indexOf(i[n])&&Object.prototype.propertyIsEnumerable.call(e,i[n])&&(r[i[n]]=e[i[n]]);return r};let X=["normal","exception","active","success"],Q=t.forwardRef((e,u)=>{let d,{prefixCls:h,className:f,rootClassName:p,steps:g,strokeColor:m,percent:y=0,size:_="default",showInfo:b=!0,type:k="line",status:v,format:C,style:x,percentPosition:E={}}=e,w=K(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:S="end",type:$="outer"}=E,O=Array.isArray(m)?m[0]:m,R="string"==typeof m||Array.isArray(m)?m:void 0,T=t.useMemo(()=>{if(O){let e="string"==typeof O?O:Object.values(O)[0];return new r.FastColor(e).isLight()}return!1},[m]),L=t.useMemo(()=>{var t,r;let i=A(e);return Number.parseInt(void 0!==i?null==(t=null!=i?i:0)?void 0:t.toString():null==(r=null!=y?y:0)?void 0:r.toString(),10)},[y,e.success,e.successPercent]),F=t.useMemo(()=>!X.includes(v)&&L>=100?"success":v||"normal",[v,L]),{getPrefixCls:z,direction:M,progress:P}=t.useContext(c.ConfigContext),N=z("progress",h),[W,U,Q]=B(N),J="line"===k,V=J&&!g,Y=t.useMemo(()=>{let r;if(!b)return null;let l=A(e),c=C||(e=>`${e}%`),u=J&&T&&"inner"===$;return"inner"===$||C||"exception"!==F&&"success"!==F?r=c(I(y),I(l)):"exception"===F?r=J?t.createElement(s.default,null):t.createElement(o.default,null):"success"===F&&(r=J?t.createElement(i.default,null):t.createElement(n.default,null)),t.createElement("span",{className:(0,a.default)(`${N}-text`,{[`${N}-text-bright`]:u,[`${N}-text-${S}`]:V,[`${N}-text-${$}`]:V}),title:"string"==typeof r?r:void 0},r)},[b,y,L,F,k,N,C]);"line"===k?d=g?t.createElement(q,Object.assign({},e,{strokeColor:R,prefixCls:N,steps:"object"==typeof g?g.count:g}),Y):t.createElement(H,Object.assign({},e,{strokeColor:O,prefixCls:N,direction:M,percentPosition:{align:S,type:$}}),Y):("circle"===k||"dashboard"===k)&&(d=t.createElement(D,Object.assign({},e,{strokeColor:O,prefixCls:N,progressStatus:F}),Y));let Z=(0,a.default)(N,`${N}-status-${F}`,{[`${N}-${"dashboard"===k&&"circle"||k}`]:"line"!==k,[`${N}-inline-circle`]:"circle"===k&&j(_,"circle")[0]<=20,[`${N}-line`]:V,[`${N}-line-align-${S}`]:V,[`${N}-line-position-${$}`]:V,[`${N}-steps`]:g,[`${N}-show-info`]:b,[`${N}-${_}`]:"string"==typeof _,[`${N}-rtl`]:"rtl"===M},null==P?void 0:P.className,f,p,U,Q);return W(t.createElement("div",Object.assign({ref:u,style:Object.assign(Object.assign({},null==P?void 0:P.style),x),className:Z,role:"progressbar","aria-valuenow":L,"aria-valuemin":0,"aria-valuemax":100},(0,l.default)(w,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),d))});e.s(["default",0,Q],309821)},993914,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"}}]},name:"file-text",theme:"outlined"};var n=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(n.default,(0,t.default)({},e,{ref:s,icon:i}))});e.s(["FileTextOutlined",0,s],993914)},59935,(e,t,r)=>{var i;let n;e.e,i=function e(){var t,r="u">typeof self?self:"u">typeof window?window:void 0!==r?r:{},i=!r.document&&!!r.postMessage,n=r.IS_PAPA_WORKER||!1,s={},o=0,a={};function l(e){this._handle=null,this._finished=!1,this._completed=!1,this._halted=!1,this._input=null,this._baseIndex=0,this._partialLine="",this._rowCount=0,this._start=0,this._nextChunk=null,this.isFirstChunk=!0,this._completeResults={data:[],errors:[],meta:{}},(function(e){var t=b(e);t.chunkSize=parseInt(t.chunkSize),e.step||e.chunk||(t.chunkSize=null),this._handle=new f(t),(this._handle.streamer=this)._config=t}).call(this,e),this.parseChunk=function(e,t){var i=parseInt(this._config.skipFirstNLines)||0;if(this.isFirstChunk&&0=this._config.preview,n)r.postMessage({results:s,workerId:a.WORKER_ID,finished:i});else if(v(this._config.chunk)&&!t){if(this._config.chunk(s,this._handle),this._handle.paused()||this._handle.aborted())return void(this._halted=!0);this._completeResults=s=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(s.data),this._completeResults.errors=this._completeResults.errors.concat(s.errors),this._completeResults.meta=s.meta),this._completed||!i||!v(this._config.complete)||s&&s.meta.aborted||(this._config.complete(this._completeResults,this._input),this._completed=!0),i||s&&s.meta.paused||this._nextChunk(),s}this._halted=!0},this._sendError=function(e){v(this._config.error)?this._config.error(e):n&&this._config.error&&r.postMessage({workerId:a.WORKER_ID,error:e,finished:!1})}}function c(e){var t;(e=e||{}).chunkSize||(e.chunkSize=a.RemoteChunkSize),l.call(this,e),this._nextChunk=i?function(){this._readChunk(),this._chunkLoaded()}:function(){this._readChunk()},this.stream=function(e){this._input=e,this._nextChunk()},this._readChunk=function(){if(this._finished)this._chunkLoaded();else{if(t=new XMLHttpRequest,this._config.withCredentials&&(t.withCredentials=this._config.withCredentials),i||(t.onload=k(this._chunkLoaded,this),t.onerror=k(this._chunkError,this)),t.open(this._config.downloadRequestBody?"POST":"GET",this._input,!i),this._config.downloadRequestHeaders){var e,r,n=this._config.downloadRequestHeaders;for(r in n)t.setRequestHeader(r,n[r])}this._config.chunkSize&&(e=this._start+this._config.chunkSize-1,t.setRequestHeader("Range","bytes="+this._start+"-"+e));try{t.send(this._config.downloadRequestBody)}catch(e){this._chunkError(e.message)}i&&0===t.status&&this._chunkError()}},this._chunkLoaded=function(){let e;4===t.readyState&&(t.status<200||400<=t.status?this._chunkError():(this._start+=this._config.chunkSize||t.responseText.length,this._finished=!this._config.chunkSize||this._start>=(null!==(e=(e=t).getResponseHeader("Content-Range"))?parseInt(e.substring(e.lastIndexOf("/")+1)):-1),this.parseChunk(t.responseText)))},this._chunkError=function(e){e=t.statusText||e,this._sendError(Error(e))}}function u(e){(e=e||{}).chunkSize||(e.chunkSize=a.LocalChunkSize),l.call(this,e);var t,r,i="u">typeof FileReader;this.stream=function(e){this._input=e,r=e.slice||e.webkitSlice||e.mozSlice,i?((t=new FileReader).onload=k(this._chunkLoaded,this),t.onerror=k(this._chunkError,this)):t=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount=this._input.size,this.parseChunk(e.target.result)},this._chunkError=function(){this._sendError(t.error)}}function d(e){var t;l.call(this,e=e||{}),this.stream=function(e){return t=e,this._nextChunk()},this._nextChunk=function(){var e,r;if(!this._finished)return t=(e=this._config.chunkSize)?(r=t.substring(0,e),t.substring(e)):(r=t,""),this._finished=!t,this.parseChunk(r)}}function h(e){l.call(this,e=e||{});var t=[],r=!0,i=!1;this.pause=function(){l.prototype.pause.apply(this,arguments),this._input.pause()},this.resume=function(){l.prototype.resume.apply(this,arguments),this._input.resume()},this.stream=function(e){this._input=e,this._input.on("data",this._streamData),this._input.on("end",this._streamEnd),this._input.on("error",this._streamError)},this._checkIsFinished=function(){i&&1===t.length&&(this._finished=!0)},this._nextChunk=function(){this._checkIsFinished(),t.length?this.parseChunk(t.shift()):r=!0},this._streamData=k(function(e){try{t.push("string"==typeof e?e:e.toString(this._config.encoding)),r&&(r=!1,this._checkIsFinished(),this.parseChunk(t.shift()))}catch(e){this._streamError(e)}},this),this._streamError=k(function(e){this._streamCleanUp(),this._sendError(e)},this),this._streamEnd=k(function(){this._streamCleanUp(),i=!0,this._streamData("")},this),this._streamCleanUp=k(function(){this._input.removeListener("data",this._streamData),this._input.removeListener("end",this._streamEnd),this._input.removeListener("error",this._streamError)},this)}function f(e){var t,r,i,n,s=/^\s*-?(\d+\.?|\.\d+|\d+\.\d+)([eE][-+]?\d+)?\s*$/,o=/^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/,l=this,c=0,u=0,d=!1,h=!1,f=[],m={data:[],errors:[],meta:{}};function y(t){return"greedy"===e.skipEmptyLines?""===t.join("").trim():1===t.length&&0===t[0].length}function _(){if(m&&i&&(C("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+a.DefaultDelimiter+"'"),i=!1),e.skipEmptyLines&&(m.data=m.data.filter(function(e){return!y(e)})),k()){if(m)if(Array.isArray(m.data[0])){for(var t,r=0;k()&&r(e.dynamicTypingFunction&&void 0===e.dynamicTyping[t]&&(e.dynamicTyping[t]=e.dynamicTypingFunction(t)),!0===(e.dynamicTyping[t]||e.dynamicTyping))?"true"===r||"TRUE"===r||"false"!==r&&"FALSE"!==r&&((e=>{if(s.test(e)&&-0x20000000000000<(e=parseFloat(e))&&e<0x20000000000000)return 1})(r)?parseFloat(r):o.test(r)?new Date(r):""===r?null:r):r)(a=e.header?n>=f.length?"__parsed_extra":f[n]:a,l=e.transform?e.transform(l,a):l);"__parsed_extra"===a?(i[a]=i[a]||[],i[a].push(l)):i[a]=l}return e.header&&(n>f.length?C("FieldMismatch","TooManyFields","Too many fields: expected "+f.length+" fields but parsed "+n,u+r):ne.preview?r.abort():(m.data=m.data[0],n(m,l))))}),this.parse=function(n,s,o){var l=e.quoteChar||'"',l=(e.newline||(e.newline=this.guessLineEndings(n,l)),i=!1,e.delimiter?v(e.delimiter)&&(e.delimiter=e.delimiter(n),m.meta.delimiter=e.delimiter):((l=((t,r,i,n,s)=>{var o,l,c,u;s=s||[","," ","|",";",a.RECORD_SEP,a.UNIT_SEP];for(var d=0;d=r.length/2?"\r\n":"\r"}}function p(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function g(e){var t=(e=e||{}).delimiter,r=e.newline,i=e.comments,n=e.step,s=e.preview,o=e.fastMode,l=null,c=!1,u=null==e.quoteChar?'"':e.quoteChar,d=u;if(void 0!==e.escapeChar&&(d=e.escapeChar),("string"!=typeof t||-1=s)return M(!0);break}E.push({type:"Quotes",code:"InvalidQuotes",message:"Trailing quote on quoted field is malformed",row:x.length,index:h}),j++}}else if(i&&0===w.length&&a.substring(h,h+k)===i){if(-1===I)return M();h=I+b,I=a.indexOf(r,h),R=a.indexOf(t,h)}else if(-1!==R&&(R=s)return M(!0)}return F();function T(e){x.push(e),S=h}function L(e){return -1!==e&&(e=a.substring(j+1,e))&&""===e.trim()?e.length:0}function F(e){return m||(void 0===e&&(e=a.substring(h)),w.push(e),h=y,T(w),C&&P()),M()}function z(e){h=e,T(w),w=[],I=a.indexOf(r,h)}function M(i){if(e.header&&!g&&x.length&&!c){var n=x[0],s=Object.create(null),o=new Set(n);let t=!1;for(let r=0;r{if("object"==typeof t){if("string"!=typeof t.delimiter||a.BAD_DELIMITERS.filter(function(e){return -1!==t.delimiter.indexOf(e)}).length||(n=t.delimiter),("boolean"==typeof t.quotes||"function"==typeof t.quotes||Array.isArray(t.quotes))&&(r=t.quotes),"boolean"!=typeof t.skipEmptyLines&&"string"!=typeof t.skipEmptyLines||(c=t.skipEmptyLines),"string"==typeof t.newline&&(s=t.newline),"string"==typeof t.quoteChar&&(o=t.quoteChar),"boolean"==typeof t.header&&(i=t.header),Array.isArray(t.columns)){if(0===t.columns.length)throw Error("Option columns is empty");u=t.columns}void 0!==t.escapeChar&&(l=t.escapeChar+o),t.escapeFormulae instanceof RegExp?d=t.escapeFormulae:"boolean"==typeof t.escapeFormulae&&t.escapeFormulae&&(d=/^[=+\-@\t\r].*$/)}})(),RegExp(p(o),"g"));if("string"==typeof e&&(e=JSON.parse(e)),Array.isArray(e)){if(!e.length||Array.isArray(e[0]))return f(null,e,c);if("object"==typeof e[0])return f(u||Object.keys(e[0]),e,c)}else if("object"==typeof e)return"string"==typeof e.data&&(e.data=JSON.parse(e.data)),Array.isArray(e.data)&&(e.fields||(e.fields=e.meta&&e.meta.fields||u),e.fields||(e.fields=Array.isArray(e.data[0])?e.fields:"object"==typeof e.data[0]?Object.keys(e.data[0]):[]),Array.isArray(e.data[0])||"object"==typeof e.data[0]||(e.data=[e.data])),f(e.fields||[],e.data||[],c);throw Error("Unable to serialize unrecognized input");function f(e,t,r){var o="",a=("string"==typeof e&&(e=JSON.parse(e)),"string"==typeof t&&(t=JSON.parse(t)),Array.isArray(e)&&0{for(var r=0;r{"use strict";let t=(0,e.i(475254).default)("chevron-down",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);e.s(["default",0,t])},246349,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["default",0,t])},174886,991124,e=>{"use strict";let t=(0,e.i(475254).default)("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);e.s(["default",0,t],991124),e.s(["Copy",0,t],174886)},560445,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(201072),r=e.i(726289),i=e.i(864517),s=e.i(562901),l=e.i(779573),n=e.i(343794),o=e.i(361275),c=e.i(244009),u=e.i(611935),d=e.i(763731),f=e.i(242064);e.i(296059);var m=e.i(915654),h=e.i(183293),g=e.i(246422);let p=(e,t,a,r,i)=>({background:e,border:`${(0,m.unit)(r.lineWidth)} ${r.lineType} ${t}`,[`${i}-icon`]:{color:a}}),v=(0,g.genStyleHooks)("Alert",e=>[(e=>{let{componentCls:t,motionDurationSlow:a,marginXS:r,marginSM:i,fontSize:s,fontSizeLG:l,lineHeight:n,borderRadiusLG:o,motionEaseInOutCirc:c,withDescriptionIconSize:u,colorText:d,colorTextHeading:f,withDescriptionPadding:m,defaultPadding:g}=e;return{[t]:Object.assign(Object.assign({},(0,h.resetComponent)(e)),{position:"relative",display:"flex",alignItems:"center",padding:g,wordWrap:"break-word",borderRadius:o,[`&${t}-rtl`]:{direction:"rtl"},[`${t}-content`]:{flex:1,minWidth:0},[`${t}-icon`]:{marginInlineEnd:r,lineHeight:0},"&-description":{display:"none",fontSize:s,lineHeight:n},"&-message":{color:f},[`&${t}-motion-leave`]:{overflow:"hidden",opacity:1,transition:`max-height ${a} ${c}, opacity ${a} ${c}, + padding-top ${a} ${c}, padding-bottom ${a} ${c}, + margin-bottom ${a} ${c}`},[`&${t}-motion-leave-active`]:{maxHeight:0,marginBottom:"0 !important",paddingTop:0,paddingBottom:0,opacity:0}}),[`${t}-with-description`]:{alignItems:"flex-start",padding:m,[`${t}-icon`]:{marginInlineEnd:i,fontSize:u,lineHeight:0},[`${t}-message`]:{display:"block",marginBottom:r,color:f,fontSize:l},[`${t}-description`]:{display:"block",color:d}},[`${t}-banner`]:{marginBottom:0,border:"0 !important",borderRadius:0}}})(e),(e=>{let{componentCls:t,colorSuccess:a,colorSuccessBorder:r,colorSuccessBg:i,colorWarning:s,colorWarningBorder:l,colorWarningBg:n,colorError:o,colorErrorBorder:c,colorErrorBg:u,colorInfo:d,colorInfoBorder:f,colorInfoBg:m}=e;return{[t]:{"&-success":p(i,r,a,e,t),"&-info":p(m,f,d,e,t),"&-warning":p(n,l,s,e,t),"&-error":Object.assign(Object.assign({},p(u,c,o,e,t)),{[`${t}-description > pre`]:{margin:0,padding:0}})}}})(e),(e=>{let{componentCls:t,iconCls:a,motionDurationMid:r,marginXS:i,fontSizeIcon:s,colorIcon:l,colorIconHover:n}=e;return{[t]:{"&-action":{marginInlineStart:i},[`${t}-close-icon`]:{marginInlineStart:i,padding:0,overflow:"hidden",fontSize:s,lineHeight:(0,m.unit)(s),backgroundColor:"transparent",border:"none",outline:"none",cursor:"pointer",[`${a}-close`]:{color:l,transition:`color ${r}`,"&:hover":{color:n}}},"&-close-text":{color:l,transition:`color ${r}`,"&:hover":{color:n}}}}})(e)],e=>({withDescriptionIconSize:e.fontSizeHeading3,defaultPadding:`${e.paddingContentVerticalSM}px 12px`,withDescriptionPadding:`${e.paddingMD}px ${e.paddingContentHorizontalLG}px`}));var y=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(a[r[i]]=e[r[i]]);return a};let x={success:a.default,info:l.default,error:r.default,warning:s.default},b=e=>{let{icon:a,prefixCls:r,type:i}=e,s=x[i]||null;return a?(0,d.replaceElement)(a,t.createElement("span",{className:`${r}-icon`},a),()=>({className:(0,n.default)(`${r}-icon`,a.props.className)})):t.createElement(s,{className:`${r}-icon`})},w=e=>{let{isClosable:a,prefixCls:r,closeIcon:s,handleClose:l,ariaProps:n}=e,o=!0===s||void 0===s?t.createElement(i.default,null):s;return a?t.createElement("button",Object.assign({type:"button",onClick:l,className:`${r}-close-icon`,tabIndex:0},n),o):null},k=t.forwardRef((e,a)=>{let{description:r,prefixCls:i,message:s,banner:l,className:d,rootClassName:m,style:h,onMouseEnter:g,onMouseLeave:p,onClick:x,afterClose:k,showIcon:_,closable:j,closeText:E,closeIcon:S,action:N,id:C}=e,M=y(e,["description","prefixCls","message","banner","className","rootClassName","style","onMouseEnter","onMouseLeave","onClick","afterClose","showIcon","closable","closeText","closeIcon","action","id"]),[I,P]=t.useState(!1),O=t.useRef(null);t.useImperativeHandle(a,()=>({nativeElement:O.current}));let{getPrefixCls:T,direction:L,closable:R,closeIcon:z,className:$,style:A}=(0,f.useComponentConfig)("alert"),D=T("alert",i),[B,F,V]=v(D),H=t=>{var a;P(!0),null==(a=e.onClose)||a.call(e,t)},U=t.useMemo(()=>void 0!==e.type?e.type:l?"warning":"info",[e.type,l]),q=t.useMemo(()=>"object"==typeof j&&!!j.closeIcon||!!E||("boolean"==typeof j?j:!1!==S&&null!=S||!!R),[E,S,j,R]),K=!!l&&void 0===_||_,G=(0,n.default)(D,`${D}-${U}`,{[`${D}-with-description`]:!!r,[`${D}-no-icon`]:!K,[`${D}-banner`]:!!l,[`${D}-rtl`]:"rtl"===L},$,d,m,V,F),Q=(0,c.default)(M,{aria:!0,data:!0}),W=t.useMemo(()=>"object"==typeof j&&j.closeIcon?j.closeIcon:E||(void 0!==S?S:"object"==typeof R&&R.closeIcon?R.closeIcon:z),[S,j,R,E,z]),Y=t.useMemo(()=>{let e=null!=j?j:R;if("object"==typeof e){let{closeIcon:t}=e;return y(e,["closeIcon"])}return{}},[j,R]);return B(t.createElement(o.default,{visible:!I,motionName:`${D}-motion`,motionAppear:!1,motionEnter:!1,onLeaveStart:e=>({maxHeight:e.offsetHeight}),onLeaveEnd:k},({className:a,style:i},l)=>t.createElement("div",Object.assign({id:C,ref:(0,u.composeRef)(O,l),"data-show":!I,className:(0,n.default)(G,a),style:Object.assign(Object.assign(Object.assign({},A),h),i),onMouseEnter:g,onMouseLeave:p,onClick:x,role:"alert"},Q),K?t.createElement(b,{description:r,icon:e.icon,prefixCls:D,type:U}):null,t.createElement("div",{className:`${D}-content`},s?t.createElement("div",{className:`${D}-message`},s):null,r?t.createElement("div",{className:`${D}-description`},r):null),N?t.createElement("div",{className:`${D}-action`},N):null,t.createElement(w,{isClosable:q,prefixCls:D,closeIcon:W,handleClose:H,ariaProps:Y}))))});var _=e.i(278409),j=e.i(233848),E=e.i(487806),S=e.i(479671),N=e.i(480002),C=e.i(868917);let M=function(e){function a(){var e,t,r;return(0,_.default)(this,a),t=a,r=arguments,t=(0,E.default)(t),(e=(0,N.default)(this,(0,S.default)()?Reflect.construct(t,r||[],(0,E.default)(this).constructor):t.apply(this,r))).state={error:void 0,info:{componentStack:""}},e}return(0,C.default)(a,e),(0,j.default)(a,[{key:"componentDidCatch",value:function(e,t){this.setState({error:e,info:t})}},{key:"render",value:function(){let{message:e,description:a,id:r,children:i}=this.props,{error:s,info:l}=this.state,n=(null==l?void 0:l.componentStack)||null,o=void 0===e?(s||"").toString():e;return s?t.createElement(k,{id:r,type:"error",message:o,description:t.createElement("pre",{style:{fontSize:"0.9em",overflowX:"auto"}},void 0===a?n:a)}):i}}])}(t.Component);k.ErrorBoundary=M,e.s(["Alert",0,k],560445)},621482,e=>{"use strict";var t=e.i(869230),a=e.i(992571),r=class extends t.QueryObserver{constructor(e,t){super(e,t)}bindMethods(){super.bindMethods(),this.fetchNextPage=this.fetchNextPage.bind(this),this.fetchPreviousPage=this.fetchPreviousPage.bind(this)}setOptions(e){e._type="infinite",super.setOptions(e)}getOptimisticResult(e){return e._type="infinite",super.getOptimisticResult(e)}fetchNextPage(e){return this.fetch({...e,meta:{fetchMore:{direction:"forward"}}})}fetchPreviousPage(e){return this.fetch({...e,meta:{fetchMore:{direction:"backward"}}})}createResult(e,t){let{state:r}=e,i=super.createResult(e,t),{isFetching:s,isRefetching:l,isError:n,isRefetchError:o}=i,c=r.fetchMeta?.fetchMore?.direction,u=n&&"forward"===c,d=s&&"forward"===c,f=n&&"backward"===c,m=s&&"backward"===c;return{...i,fetchNextPage:this.fetchNextPage,fetchPreviousPage:this.fetchPreviousPage,hasNextPage:(0,a.hasNextPage)(t,r.data),hasPreviousPage:(0,a.hasPreviousPage)(t,r.data),isFetchNextPageError:u,isFetchingNextPage:d,isFetchPreviousPageError:f,isFetchingPreviousPage:m,isRefetchError:o&&!u&&!f,isRefetching:l&&!d&&!m}}},i=e.i(469637);e.s(["useInfiniteQuery",0,function(e,t){return(0,i.useBaseQuery)(e,r,t)}],621482)},785242,270345,e=>{"use strict";var t=e.i(619273),a=e.i(621482),r=e.i(266027),i=e.i(912598),s=e.i(135214),l=e.i(602869);let n=async(e,t,a,r)=>"Admin"!=a&&"Admin Viewer"!=a?await (0,l.teamListCall)(e,r?.organization_id||null,t):await (0,l.teamListCall)(e,r?.organization_id||null);e.s(["fetchTeams",0,n],270345);var o=e.i(243652),c=e.i(431703);let u=async(e,t,a,r={})=>{try{let i=(0,l.getProxyBaseUrl)(),s=new URLSearchParams(Object.entries({team_id:r.teamID,organization_id:r.organizationID,team_alias:r.team_alias,search:r.search,user_id:r.userID,page:t,page_size:a,sort_by:r.sortBy,sort_order:r.sortOrder,status:r.status}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),n=`${i?`${i}/v2/team/list`:"/v2/team/list"}?${s}`,o=await fetch(n,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!o.ok){let e=await o.json(),t=(0,c.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return await o.json()}catch(e){throw console.error("Failed to list teams:",e),e}},d=(0,o.createQueryKeys)("teams"),f=async e=>{let t=await u(e,1,100),a=t.total_pages??1;return a<=1?t.teams:[t,...await Promise.all(Array.from({length:a-1},(t,a)=>u(e,a+2,100)))].flatMap(e=>e.teams)},m=(0,o.createQueryKeys)("infiniteTeams"),h=async(e,t,a,r={})=>{try{let i=(0,l.getProxyBaseUrl)(),s=new URLSearchParams(Object.entries({team_id:r.teamID,organization_id:r.organizationID,team_alias:r.team_alias,search:r.search,user_id:r.userID,page:t,page_size:a,sort_by:r.sortBy,sort_order:r.sortOrder,status:"deleted"}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),n=`${i?`${i}/v2/team/list`:"/v2/team/list"}?${s}`,o=await fetch(n,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!o.ok){let e=await o.json(),t=(0,c.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}let u=await o.json();if(u&&"object"==typeof u&&"teams"in u)return u.teams;return u}catch(e){throw console.error("Failed to list deleted teams:",e),e}},g=(0,o.createQueryKeys)("deletedTeams");e.s(["teamListCall",0,u,"useAllTeams",0,()=>{let{accessToken:e}=(0,s.default)();return(0,r.useQuery)({queryKey:d.list({filters:{scope:"all",pageSize:100,accessToken:e??""}}),queryFn:async()=>await f(e),enabled:!!e,staleTime:3e4})},"useDeletedTeams",0,(e,a,i={})=>{let{accessToken:l}=(0,s.default)();return(0,r.useQuery)({queryKey:g.list({page:e,limit:a,...i}),queryFn:async()=>await h(l,e,a,i),enabled:!!l,staleTime:3e4,placeholderData:t.keepPreviousData})},"useInfiniteTeams",0,(e=50,t,r)=>{let{accessToken:i,userId:l,userRole:n}=(0,s.default)(),o="Admin"===n||"Admin Viewer"===n;return(0,a.useInfiniteQuery)({queryKey:m.list({filters:{pageSize:e,...t&&{search:t},...r&&{organizationId:r},...l&&{userId:l}}}),queryFn:async({pageParam:a})=>await u(i,a,e,{team_alias:t||void 0,organizationID:r,userID:o?void 0:l}),initialPageParam:1,getNextPageParam:e=>{if(e.page{let{accessToken:t}=(0,s.default)(),a=(0,i.useQueryClient)();return(0,r.useQuery)({queryKey:d.detail(e),enabled:!!(t&&e),queryFn:async()=>{if(!t||!e)throw Error("Missing auth or teamId");return(0,l.teamInfoCall)(t,e)},initialData:()=>{if(!e)return;let t=a.getQueryData(d.list({}));return t?.find(t=>t.team_id===e)}})},"useTeams",0,()=>{let{accessToken:e,userId:t,userRole:a}=(0,s.default)();return(0,r.useQuery)({queryKey:d.list({}),queryFn:async()=>await n(e,t,a,null),enabled:!!e})}],785242)},109799,e=>{"use strict";var t=e.i(135214),a=e.i(602869),r=e.i(266027),i=e.i(912598);let s=(0,e.i(243652).createQueryKeys)("organizations");e.s(["organizationKeys",0,s,"useOrganization",0,e=>{let l=(0,i.useQueryClient)(),{accessToken:n}=(0,t.default)();return(0,r.useQuery)({queryKey:s.detail(e),enabled:!!(n&&e),queryFn:async()=>{if(!n||!e)throw Error("Missing auth or teamId");return(0,a.organizationInfoCall)(n,e)},initialData:()=>{if(e)return l.getQueriesData({queryKey:s.lists()}).flatMap(([,e])=>e??[]).find(t=>t.organization_id===e)}})},"useOrganizations",0,e=>{let{accessToken:i,userId:l,userRole:n}=(0,t.default)(),o=e?.org_id||null,c=e?.org_alias||null;return(0,r.useQuery)({queryKey:s.list(o||c?{filters:{...o&&{org_id:o},...c&&{org_alias:c}}}:{}),queryFn:async()=>await (0,a.organizationListCall)(i,o,c),enabled:!!(i&&l&&n)})}])},531278,e=>{"use strict";let t=(0,e.i(475254).default)("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);e.s(["Loader2",0,t],531278)},98740,e=>{"use strict";let t=(0,e.i(475254).default)("users",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["path",{d:"M16 3.128a4 4 0 0 1 0 7.744",key:"16gr8j"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}]]);e.s(["default",0,t])},195116,e=>{"use strict";let t=(0,e.i(475254).default)("wrench",[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z",key:"cbrjhi"}]]);e.s(["Wrench",0,t],195116)},657150,e=>{"use strict";let t=(0,e.i(475254).default)("bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);e.s(["default",0,t])},531245,e=>{"use strict";var t=e.i(657150);e.s(["Bot",()=>t.default])},755151,e=>{"use strict";var t=e.i(247153);e.s(["DownOutlined",()=>t.default])},326373,e=>{"use strict";var t=e.i(21539);e.s(["Dropdown",()=>t.default])},344523,e=>{"use strict";let t=(0,e.i(475254).default)("chevrons-up-down",[["path",{d:"m7 15 5 5 5-5",key:"1hf1tw"}],["path",{d:"m7 9 5-5 5 5",key:"sgt6xg"}]]);e.s(["ChevronsUpDown",0,t],344523)},100486,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M899.6 276.5L705 396.4 518.4 147.5a8.06 8.06 0 00-12.9 0L319 396.4 124.3 276.5c-5.7-3.5-13.1 1.2-12.2 7.9L188.5 865c1.1 7.9 7.9 14 16 14h615.1c8 0 14.9-6 15.9-14l76.4-580.6c.8-6.7-6.5-11.4-12.3-7.9zm-126 534.1H250.3l-53.8-409.4 139.8 86.1L512 252.9l175.7 234.4 139.8-86.1-53.9 409.4zM512 509c-62.1 0-112.6 50.5-112.6 112.6S449.9 734.2 512 734.2s112.6-50.5 112.6-112.6S574.1 509 512 509zm0 160.9c-26.6 0-48.2-21.6-48.2-48.3 0-26.6 21.6-48.3 48.2-48.3s48.2 21.6 48.2 48.3c0 26.6-21.6 48.3-48.2 48.3z"}}]},name:"crown",theme:"outlined"};var i=e.i(9583),s=a.forwardRef(function(e,s){return a.createElement(i.default,(0,t.default)({},e,{ref:s,icon:r}))});e.s(["CrownOutlined",0,s],100486)},602073,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64L128 192v384c0 212.1 171.9 384 384 384s384-171.9 384-384V192L512 64zm312 512c0 172.3-139.7 312-312 312S200 748.3 200 576V246l312-110 312 110v330z"}},{tag:"path",attrs:{d:"M378.4 475.1a35.91 35.91 0 00-50.9 0 35.91 35.91 0 000 50.9l129.4 129.4 2.1 2.1a33.98 33.98 0 0048.1 0L730.6 434a33.98 33.98 0 000-48.1l-2.8-2.8a33.98 33.98 0 00-48.1 0L483 579.7 378.4 475.1z"}}]},name:"safety",theme:"outlined"};var i=e.i(9583),s=a.forwardRef(function(e,s){return a.createElement(i.default,(0,t.default)({},e,{ref:s,icon:r}))});e.s(["SafetyOutlined",0,s],602073)},477189,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"}}]},name:"appstore",theme:"outlined"};var i=e.i(9583),s=a.forwardRef(function(e,s){return a.createElement(i.default,(0,t.default)({},e,{ref:s,icon:r}))});e.s(["AppstoreOutlined",0,s],477189)},295320,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M704 446H320c-4.4 0-8 3.6-8 8v402c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8V454c0-4.4-3.6-8-8-8zm-328 64h272v117H376V510zm272 290H376V683h272v117z"}},{tag:"path",attrs:{d:"M424 748a32 32 0 1064 0 32 32 0 10-64 0zm0-178a32 32 0 1064 0 32 32 0 10-64 0z"}},{tag:"path",attrs:{d:"M811.4 368.9C765.6 248 648.9 162 512.2 162S258.8 247.9 213 368.8C126.9 391.5 63.5 470.2 64 563.6 64.6 668 145.6 752.9 247.6 762c4.7.4 8.7-3.3 8.7-8v-60.4c0-4-3-7.4-7-7.9-27-3.4-52.5-15.2-72.1-34.5-24-23.5-37.2-55.1-37.2-88.6 0-28 9.1-54.4 26.2-76.4 16.7-21.4 40.2-36.9 66.1-43.7l37.9-10 13.9-36.7c8.6-22.8 20.6-44.2 35.7-63.5 14.9-19.2 32.6-36 52.4-50 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.3c19.9 14 37.5 30.8 52.4 50 15.1 19.3 27.1 40.7 35.7 63.5l13.8 36.6 37.8 10c54.2 14.4 92.1 63.7 92.1 120 0 33.6-13.2 65.1-37.2 88.6-19.5 19.2-44.9 31.1-71.9 34.5-4 .5-6.9 3.9-6.9 7.9V754c0 4.7 4.1 8.4 8.8 8 101.7-9.2 182.5-94 183.2-198.2.6-93.4-62.7-172.1-148.6-194.9z"}}]},name:"cloud-server",theme:"outlined"};var i=e.i(9583),s=a.forwardRef(function(e,s){return a.createElement(i.default,(0,t.default)({},e,{ref:s,icon:r}))});e.s(["CloudServerOutlined",0,s],295320)},283713,e=>{"use strict";var t=e.i(271645),a=e.i(602869),r=e.i(612256);let i="litellm_selected_worker_id";e.s(["useWorker",0,()=>{let{data:e}=(0,r.useUIConfig)(),s=e?.is_control_plane??!1,l=e?.workers??[],[n,o]=(0,t.useState)(()=>localStorage.getItem(i));(0,t.useEffect)(()=>{if(!n||0===l.length)return;let e=l.find(e=>e.worker_id===n);e&&(0,a.switchToWorkerUrl)(e.url)},[n,l]);let c=l.find(e=>e.worker_id===n)??null,u=(0,t.useCallback)(e=>{let t=l.find(t=>t.worker_id===e);t&&(o(e),localStorage.setItem(i,e),(0,a.switchToWorkerUrl)(t.url))},[l]);return{isControlPlane:s,workers:l,selectedWorkerId:n,selectedWorker:c,selectWorker:u,disconnectFromWorker:(0,t.useCallback)(()=>{o(null),localStorage.removeItem(i),(0,a.switchToWorkerUrl)(null)},[])}}])},463059,e=>{"use strict";var t=e.i(246349);e.s(["ChevronRight",()=>t.default])},951437,e=>{"use strict";var t=e.i(271645);e.s(["useControlled",0,function({controlled:e,default:a,name:r,state:i="value"}){let{current:s}=t.useRef(void 0!==e),[l,n]=t.useState(a),o=t.useCallback(e=>{s||n(e)},[]);return[s?e:l,o]}])},678745,e=>{"use strict";let t=(0,e.i(475254).default)("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);e.s(["default",0,t])},54943,e=>{"use strict";let t=(0,e.i(475254).default)("search",[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]);e.s(["default",0,t])},555436,e=>{"use strict";var t=e.i(54943);e.s(["Search",()=>t.default])},664659,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDown",()=>t.default])},643531,e=>{"use strict";var t=e.i(678745);e.s(["Check",()=>t.default])},652225,e=>{"use strict";var t=e.i(271645),a=e.i(552245);let r=t.forwardRef(function(e,t){let{className:r,render:i,orientation:s="horizontal",style:l,...n}=e;return(0,a.useRenderElement)("div",e,{state:{orientation:s},ref:t,props:[{role:"separator","aria-orientation":s},n]})});e.s(["Separator",0,r])},201675,e=>{"use strict";e.s(["clamp",0,function(e,t=Number.MIN_SAFE_INTEGER,a=Number.MAX_SAFE_INTEGER){return Math.max(t,Math.min(e,a))}])},346570,e=>{"use strict";var t=e.i(271645),a=e.i(174080),r=e.i(647554),i=e.i(383976),s=e.i(675606),l=e.i(56434);e.s(["useTriggerFocusGuards",0,function(e,n){let o=t.useRef(null);return{preFocusGuardRef:o,handlePreFocusGuardFocus:function(t){a.flushSync(()=>{e.setOpen(!1,(0,s.createChangeEventDetails)(l.REASONS.focusOut,t.nativeEvent,t.currentTarget))});let r=(0,i.getTabbableBeforeElement)(o.current);r?.focus()},handleFocusTargetFocus:function(t){let o=e.select("positionerElement");if(o&&(0,i.isOutsideEvent)(t,o))e.context.beforeContentFocusGuardRef.current?.focus();else{a.flushSync(()=>{e.setOpen(!1,(0,s.createChangeEventDetails)(l.REASONS.focusOut,t.nativeEvent,t.currentTarget))});let c=(0,i.getTabbableAfterElement)(e.context.triggerFocusTargetRef.current||n.current);for(;null!==c&&(0,r.contains)(o,c);){let e=c;if((c=(0,i.getNextTabbable)(c))===e)break}c?.focus()}}}}])},33383,96533,e=>{"use strict";var t=e.i(271645),a=e.i(108868),r=e.i(145484),i=e.i(146376);e.s(["useAnchoredPopupScrollLock",0,function(e,s,l,n){let[o,c]=t.useState(!1);(0,i.useIsoLayoutEffect)(()=>{if(!e||!s||null==l)return void c(!1);let t=(0,a.ownerDocument)(l).documentElement.clientWidth,r=l.offsetWidth;c(t>0&&r>0&&r>=t-20)},[e,s,l]),(0,r.useScrollLock)(e&&(!s||o),n)}],33383),e.i(247167);var s=e.i(733332);let l=t.createContext(void 0);e.s(["useToolbarRootContext",0,function(e){let a=t.useContext(l);if(void 0===a&&!e)throw Error((0,s.default)(69));return a}],96533)},469690,875812,381104,e=>{"use strict";e.i(247167);var t,a=e.i(733332),r=e.i(271645),i=e.i(956789);let s=((t={}).disabled="data-disabled",t.valid="data-valid",t.invalid="data-invalid",t.touched="data-touched",t.dirty="data-dirty",t.filled="data-filled",t.focused="data-focused",t),l={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},n={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},o={disabled:!1,...n};e.s(["DEFAULT_FIELD_ROOT_STATE",0,o,"DEFAULT_FIELD_STATE_ATTRIBUTES",0,n,"DEFAULT_VALIDITY_STATE",0,l,"fieldValidityMapping",0,{valid:e=>null===e?null:e?{[s.valid]:""}:{[s.invalid]:""}}],875812);let c={invalid:void 0,name:void 0,validityData:{state:l,errors:[],error:"",value:"",initialValue:null},setValidityData:i.NOOP,disabled:void 0,touched:n.touched,setTouched:i.NOOP,dirty:n.dirty,setDirty:i.NOOP,filled:n.filled,setFilled:i.NOOP,focused:n.focused,setFocused:i.NOOP,validate:()=>null,validationMode:"onSubmit",validationDebounceTime:0,shouldValidateOnChange:()=>!1,state:o,markedDirtyRef:{current:!1},registerFieldControl:i.NOOP,validation:{getValidationProps:(e,t=i.EMPTY_OBJECT)=>t,inputRef:{current:null},registerInput:i.NOOP,commit:async()=>{},change:i.NOOP}},u=r.createContext(c);function d(e=!0){let t=r.useContext(u);if(t.setValidityData===i.NOOP&&!e)throw Error((0,a.default)(28));return t}e.s(["useFieldRootContext",0,d],469690);var f=e.i(146376);e.s(["useRegisterFieldControl",0,function(e,t,a,i,s=!0,l){let{registerFieldControl:n}=d(),o=r.useRef(null);o.current||(o.current=Symbol()),(0,f.useIsoLayoutEffect)(()=>{let r=o.current;if(r&&s)return n(r,{controlRef:e,getValue:i,id:t,name:l,value:a}),()=>{n(r,void 0)}},[e,s,i,t,l,n,a])}],381104)},884708,e=>{"use strict";var t=e.i(271645),a=e.i(956789);let r=t.createContext({formRef:{current:{fields:new Map}},errors:{},clearErrors:a.NOOP,validationMode:"onSubmit",submitAttemptedRef:{current:!1}});e.s(["useFormContext",0,function(){return t.useContext(r)}])},538489,247778,e=>{"use strict";var t=e.i(271645),a=e.i(146376),r=e.i(667865),i=e.i(921374),s=e.i(229315),l=e.i(956789),n=e.i(788015);e.i(247167);let o=t.createContext({controlId:void 0,registerControlId:l.NOOP,labelId:void 0,setLabelId:l.NOOP,messageIds:[],setMessageIds:l.NOOP,getDescriptionProps:e=>e});function c(){return t.useContext(o)}e.s(["useLabelableContext",0,c],247778),e.s(["useLabelableId",0,function(e={}){let{id:o,implicit:u=!1,controlRef:d}=e,{controlId:f,registerControlId:m}=c(),h=(0,n.useBaseUiId)(o),g=u?f:void 0,p=(0,i.useRefWithInit)(()=>Symbol("labelable-control")),v=t.useRef(!1),y=t.useRef(null!=o),x=(0,r.useStableCallback)(()=>{v.current&&m!==l.NOOP&&(v.current=!1,m(p.current,void 0))});return(0,a.useIsoLayoutEffect)(()=>{let e;if(m!==l.NOOP){if(u){let t=d?.current;e=(0,s.isElement)(t)&&null!=t.closest("label")?o??null:g??h}else if(null!=o)y.current=!0,e=o;else{if(!y.current)return void x();e=h}if(void 0===e)return void x();v.current=!0,m(p.current,e)}},[o,d,g,m,u,h,p,x]),t.useEffect(()=>x,[x]),f??h}],538489)},757337,e=>{"use strict";var t=e.i(146376),a=e.i(788015);e.s(["useRegisteredLabelId",0,function(e,r){let i=(0,a.useBaseUiId)(e);return(0,t.useIsoLayoutEffect)(()=>(r(i),()=>{r(void 0)}),[i,r]),i}])},284614,e=>{"use strict";let t=(0,e.i(475254).default)("user",[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]]);e.s(["User",0,t],284614)},546467,e=>{"use strict";let t=(0,e.i(475254).default)("external-link",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);e.s(["default",0,t])},778917,e=>{"use strict";var t=e.i(546467);e.s(["ExternalLink",()=>t.default])},217923,e=>{"use strict";let t=(0,e.i(475254).default)("chart-column",[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16",key:"c24i48"}],["path",{d:"M18 17V9",key:"2bz60n"}],["path",{d:"M13 17V5",key:"1frdt8"}],["path",{d:"M8 17v-3",key:"17ska0"}]]);e.s(["BarChart3",0,t],217923)},686311,e=>{"use strict";let t=(0,e.i(475254).default)("message-square",[["path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z",key:"1lielz"}]]);e.s(["MessageSquare",0,t],686311)},465261,e=>{"use strict";let t=(0,e.i(475254).default)("key-round",[["path",{d:"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",key:"1s6t7t"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor",key:"w0ekpg"}]]);e.s(["KeyRound",0,t],465261)},772436,e=>{"use strict";var t=e.i(843476),a=e.i(652225),r=e.i(271645),i=e.i(115504);let s=r.forwardRef(({className:e,orientation:r="horizontal",...s},l)=>(0,t.jsx)(a.Separator,{ref:l,"data-slot":"separator",orientation:r,className:(0,i.cn)("shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",e),...s}));s.displayName="Separator",e.s(["Separator",0,s])},373264,e=>{"use strict";let t=(0,e.i(475254).default)("layout-grid",[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1",key:"1g98yp"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1",key:"nxv5o0"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1",key:"1bb6yr"}]]);e.s(["LayoutGrid",0,t],373264)},571303,e=>{"use strict";var t=e.i(843476),a=e.i(271645),r=e.i(115504);let i=a.default.forwardRef(({className:e="",...i},s)=>{var l,n;let o=(0,a.useId)();return l=()=>{let e=document.getAnimations().filter(e=>e instanceof CSSAnimation&&"spin"===e.animationName),t=e.find(e=>e.effect.target?.getAttribute("data-spinner-id")===o),a=e.find(e=>e.effect instanceof KeyframeEffect&&e.effect.target?.getAttribute("data-spinner-id")!==o);t&&a&&(t.currentTime=a.currentTime)},n=[o],(0,a.useLayoutEffect)(l,n),(0,t.jsxs)("svg",{ref:s,"data-spinner-id":o,className:(0,r.cx)("pointer-events-none size-12 animate-spin text-current",e),fill:"none",viewBox:"0 0 24 24",...i,children:[(0,t.jsx)("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),(0,t.jsx)("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]})});i.displayName="UiLoadingSpinner",e.s(["UiLoadingSpinner",0,i],571303)},936578,e=>{"use strict";var t=e.i(843476),a=e.i(115504),r=e.i(571303);e.s(["default",0,function(){return(0,t.jsxs)("div",{className:(0,a.cx)("h-screen","flex items-center justify-center gap-4"),children:[(0,t.jsx)("div",{className:"text-lg font-medium py-2 pr-4 border-r border-r-gray-200",children:"🚅 LiteLLM"}),(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2",children:[(0,t.jsx)(r.UiLoadingSpinner,{className:"size-4"}),(0,t.jsx)("span",{className:"text-gray-600 text-sm",children:"Loading..."})]})]})}])},953651,e=>{"use strict";let t=(0,e.i(475254).default)("server",[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]]);e.s(["default",0,t])},868054,e=>{"use strict";let t=(0,e.i(475254).default)("terminal",[["path",{d:"M12 19h8",key:"baeox8"}],["path",{d:"m4 17 6-6-6-6",key:"1yngyt"}]]);e.s(["Terminal",0,t],868054)},844444,814431,e=>{"use strict";var t=e.i(843476),a=e.i(906579),r=e.i(271645),i=e.i(115571);function s(e){let t=t=>{"disableShowNewBadge"===t.key&&e()},a=t=>{let{key:a}=t.detail;"disableShowNewBadge"===a&&e()};return window.addEventListener("storage",t),window.addEventListener(i.LOCAL_STORAGE_EVENT,a),()=>{window.removeEventListener("storage",t),window.removeEventListener(i.LOCAL_STORAGE_EVENT,a)}}function l(){return"true"===(0,i.getLocalStorageItem)("disableShowNewBadge")}function n(){return(0,r.useSyncExternalStore)(s,l)}e.s(["useDisableShowNewBadge",0,n],814431),e.s(["default",0,function({children:e,dot:r=!1}){return n()?e?(0,t.jsx)(t.Fragment,{children:e}):null:e?(0,t.jsx)(a.Badge,{color:"blue",count:r?void 0:"New",dot:r,children:e}):(0,t.jsx)(a.Badge,{color:"blue",count:r?void 0:"New",dot:r})}],844444)},903446,e=>{"use strict";let t=(0,e.i(475254).default)("settings",[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",key:"1qme2f"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["default",0,t])},178583,38982,e=>{"use strict";var t=e.i(475254);let a=(0,t.default)("file-text",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]]);e.s(["FileText",0,a],178583);let r=(0,t.default)("flask-conical",[["path",{d:"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2",key:"18mbvz"}],["path",{d:"M6.453 15h11.094",key:"3shlmq"}],["path",{d:"M8.5 2h7",key:"csnxdl"}]]);e.s(["FlaskConical",0,r],38982)},239616,e=>{"use strict";var t=e.i(903446);e.s(["Settings",()=>t.default])},98919,e=>{"use strict";let t=(0,e.i(475254).default)("shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);e.s(["Shield",0,t],98919)},216370,e=>{"use strict";var t=e.i(843476),a=e.i(271645),r=e.i(519455),i=e.i(463059),s=e.i(115504);let l=a.forwardRef(({...e},a)=>(0,t.jsx)("nav",{ref:a,"aria-label":"breadcrumb","data-slot":"breadcrumb",...e}));l.displayName="Breadcrumb";let n=a.forwardRef(({className:e,...a},r)=>(0,t.jsx)("ol",{ref:r,"data-slot":"breadcrumb-list",className:(0,s.cn)("flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground",e),...a}));n.displayName="BreadcrumbList";let o=a.forwardRef(({className:e,...a},r)=>(0,t.jsx)("li",{ref:r,"data-slot":"breadcrumb-item",className:(0,s.cn)("inline-flex items-center gap-1.5",e),...a}));o.displayName="BreadcrumbItem",a.forwardRef(({className:e,...a},r)=>(0,t.jsx)("a",{ref:r,"data-slot":"breadcrumb-link",className:(0,s.cn)("transition-colors hover:text-foreground",e),...a})).displayName="BreadcrumbLink";let c=a.forwardRef(({className:e,...a},r)=>(0,t.jsx)("span",{ref:r,"data-slot":"breadcrumb-page",role:"link","aria-disabled":"true","aria-current":"page",className:(0,s.cn)("font-medium text-foreground",e),...a}));c.displayName="BreadcrumbPage";let u=a.forwardRef(({children:e,className:a,...r},l)=>(0,t.jsx)("li",{ref:l,"data-slot":"breadcrumb-separator",role:"presentation","aria-hidden":"true",className:(0,s.cn)("[&>svg]:size-3.5",a),...r,children:e??(0,t.jsx)(i.ChevronRight,{})}));u.displayName="BreadcrumbSeparator";var d=e.i(772436),f=e.i(111672),m=e.i(251773),h=e.i(771243),g=e.i(895335),p=e.i(853295),v=e.i(383862),y=e.i(283713),x=e.i(636772),b=e.i(268004),w=e.i(321836);function k({page:e}){let{title:a}=(0,f.getBreadcrumb)(e),{isControlPlane:i,selectedWorker:s}=(0,y.useWorker)(),_=(0,x.useDisableShowPrompts)();return(0,t.jsxs)("header",{className:"flex h-14 flex-none items-center justify-between gap-4 border-b border-border bg-background px-4",children:[(0,t.jsx)(l,{className:"min-w-0",children:(0,t.jsxs)(n,{className:"flex-nowrap",children:[(0,t.jsx)(o,{className:"flex-none",children:(0,t.jsx)(p.default,{})}),(0,t.jsx)(u,{}),(0,t.jsx)(o,{className:"min-w-0",children:(0,t.jsx)(c,{className:"truncate",children:a})})]})}),(0,t.jsxs)("div",{className:"flex flex-none items-center gap-1",children:[i&&null!==s&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(v.default,{onWorkerSwitch:e=>{(0,b.clearTokenCookies)(),(0,w.clearStoredReturnUrl)(),localStorage.removeItem("litellm_selected_worker_id"),localStorage.removeItem("litellm_worker_url"),window.location.href=`/ui/login?worker=${encodeURIComponent(e)}`}}),(0,t.jsx)(d.Separator,{orientation:"vertical",className:"mx-1.5 h-5"})]}),(0,t.jsx)(r.Button,{variant:"ghost",size:"sm",nativeButton:!1,render:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/",target:"_blank",rel:"noopener noreferrer"}),className:"text-muted-foreground",children:"Docs"}),(0,t.jsx)(m.BlogDropdown,{}),!_&&(0,t.jsx)(h.CommunityEngagementButtons,{}),(0,t.jsx)(d.Separator,{orientation:"vertical",className:"mx-1.5 h-5"}),(0,t.jsx)(g.NotificationsBell,{})]})]})}var _=e.i(402874),j=e.i(936578),E=e.i(275144),S=e.i(557951),N=e.i(602869),C=e.i(135214);let M=({setPage:e,defaultSelectedKey:r,sidebarCollapsed:i,onToggleCollapsed:s})=>{let{accessToken:l}=(0,C.default)(),[n,o]=(0,a.useState)(null),[c,u]=(0,a.useState)(!1),[d,m]=(0,a.useState)(!1),[h,g]=(0,a.useState)(!1),[p,v]=(0,a.useState)(!1),[y,x]=(0,a.useState)(!1),[b,w]=(0,a.useState)(!1);return(0,a.useEffect)(()=>{(async()=>{if(l)try{let e=await (0,N.getUISettings)(l);e?.values?.enabled_ui_pages_internal_users!==void 0&&o(e.values.enabled_ui_pages_internal_users),e?.values?.enable_projects_ui!==void 0&&u(!!e.values.enable_projects_ui),e?.values?.enable_chat_ui!==void 0&&m(!!e.values.enable_chat_ui),e?.values?.disable_agents_for_internal_users!==void 0&&g(!!e.values.disable_agents_for_internal_users),e?.values?.allow_agents_for_team_admins!==void 0&&v(!!e.values.allow_agents_for_team_admins),e?.values?.disable_vector_stores_for_internal_users!==void 0&&x(!!e.values.disable_vector_stores_for_internal_users),e?.values?.allow_vector_stores_for_team_admins!==void 0&&w(!!e.values.allow_vector_stores_for_team_admins)}catch(e){console.error("[SidebarProvider] Failed to fetch UI settings:",e)}})()},[l]),(0,t.jsx)(f.default,{setPage:e,defaultSelectedKey:r,collapsed:i,onToggleCollapsed:s,enabledPagesInternalUsers:n,enableProjectsUI:c,enableChatUI:d,disableAgentsForInternalUsers:h,allowAgentsForTeamAdmins:p,disableVectorStoresForInternalUsers:y,allowVectorStoresForTeamAdmins:b})};var I=e.i(618566),P=e.i(560445),O=e.i(143488);let T=({accessToken:e})=>{let{data:a}=(0,O.useHealthReadinessDetails)(e);return a?.is_detailed_debug?(0,t.jsx)(P.Alert,{message:"Performance Warning: Detailed Debug Mode Active",description:(0,t.jsxs)(t.Fragment,{children:["Detailed debug logging (",(0,t.jsx)("code",{children:"LITELLM_LOG=DEBUG"}),") is currently enabled. This mode logs extensive diagnostic information and will significantly degrade performance. It should only be used for troubleshooting and disabled in production environments."]}),type:"warning",showIcon:!0,banner:!0,style:{marginBottom:0,borderRadius:0}}):null};var L=e.i(858488),R=e.i(625005);let z="sales@berri.ai",$=(0,t.jsx)("a",{href:`mailto:${z}`,children:z}),A=({licenseInfo:e})=>{let[r,i]=(0,a.useState)(!1),s=e?.expiration_date??null,l=(0,R.getLicenseExpiryTier)(s),n=(0,R.getDaysUntilExpiration)(s);if(null===s||"none"===l||null===n)return null;let o="warning"===l,c=`litellm:licenseExpiryBannerDismissed:${s}`,u=!!o&&"true"===sessionStorage.getItem(c);if(o&&(r||u))return null;let d=(0,R.formatExpiryDate)(s),f="expired"===l?`Your LiteLLM Enterprise license expired on ${d}`:`Your LiteLLM Enterprise license ${n<=0?"expires today":1===n?"expires in 1 day":`expires in ${n} days`} (${d})`,m="expired"===l?(0,t.jsxs)(t.Fragment,{children:["Enterprise features are now disabled. Reach out to ",$," to restore access"]}):"critical"===l?(0,t.jsxs)(t.Fragment,{children:["Renew now to avoid losing enterprise features. Reach out to ",$]}):(0,t.jsxs)(t.Fragment,{children:["Renew before it lapses to keep enterprise features. Reach out to ",$]});return(0,t.jsx)(P.Alert,{message:f,description:m,type:"warning"===l?"warning":"error",showIcon:!0,banner:!0,closable:o,onClose:()=>{sessionStorage.setItem(c,"true"),i(!0)},style:{marginBottom:0,borderRadius:0}})},D=({accessToken:e})=>{let{data:a}=(0,L.useLicenseInfo)(e);return(0,t.jsx)(A,{licenseInfo:a??null})};var B=e.i(571353),F=e.i(658140);let V=(0,e.i(431703).createApiClient)({getBaseUrl:()=>(0,N.getProxyBaseUrl)()??""});function H({children:e}){let{accessToken:a}=(0,S.useAuth)();return(0,t.jsx)(F.PluginModeProvider,{accessToken:a,children:e})}function U(){let{activePlugin:e}=(0,F.usePluginMode)(),r=e?.name,i=e?.url??"",{accessToken:s}=(0,S.useAuth)(),l=(0,a.useRef)(null),[n,o]=(0,a.useState)(null);return((0,a.useEffect)(()=>{if(!s||!r)return;let e=!1;return V.get("/api/plugins/auth-token",{accessToken:s,query:{plugin_name:r}}).then(t=>{!e&&t?.session_claim&&o({plugin:r,claim:t.session_claim})}).catch(()=>{}),()=>{e=!0}},[s,r]),(0,a.useEffect)(()=>{let e=l.current;if(!e||!n||n.plugin!==r||!i)return;let t=()=>{e.contentWindow?.postMessage({type:"litellm-auth",session_claim:n.claim},i)};return t(),e.addEventListener("load",t),()=>e.removeEventListener("load",t)},[n,r,i]),i)?(0,t.jsx)("iframe",{ref:l,src:`${i.replace(/\/$/,"")}/`,style:{width:"100%",height:"100%",border:"none",flex:1,minHeight:"calc(100vh - 56px)"},title:e?.display_name??"Plugin",allow:"clipboard-write"}):(0,t.jsx)("div",{className:"flex flex-1 items-center justify-center text-gray-500",children:(0,t.jsxs)("div",{className:"text-center",children:[(0,t.jsx)("p",{className:"text-lg font-medium mb-2",children:"Plugin"}),(0,t.jsx)("p",{className:"text-sm",children:"Configure the plugin URL in settings"})]})})}function q({children:e}){let r=(0,I.useRouter)(),i=(0,I.useSearchParams)(),s=(0,I.usePathname)(),{accessToken:l}=(0,S.useAuth)(),[n,o]=(0,a.useState)(!1),{mode:c}=(0,F.usePluginMode)(),u=(0,B.legacyKeyForPathname)(s)||i.get("page")||"api-keys";return"ai-gateway"!==c?(0,t.jsxs)("div",{className:"flex h-screen flex-col overflow-hidden bg-background",children:[(0,t.jsx)(_.default,{accessToken:l,isPublicPage:!1}),(0,t.jsx)(T,{accessToken:l}),(0,t.jsx)(D,{accessToken:l}),(0,t.jsx)("main",{className:"flex min-h-0 flex-1 overflow-hidden",children:(0,t.jsx)(U,{})})]}):(0,t.jsxs)("div",{className:"flex h-screen overflow-hidden bg-background",children:[(0,t.jsx)(M,{setPage:e=>{let t=B.MIGRATED_PAGES[e];r.push(t?(0,B.migratedHref)(t):(0,B.legacyPageHref)(e))},defaultSelectedKey:u,sidebarCollapsed:n,onToggleCollapsed:()=>o(e=>!e)}),(0,t.jsxs)("div",{className:"flex min-w-0 flex-1 flex-col overflow-hidden",children:[(0,t.jsx)(k,{page:u}),(0,t.jsx)(T,{accessToken:l}),(0,t.jsx)(D,{accessToken:l}),(0,t.jsx)("main",{className:"min-w-0 flex-1 overflow-y-auto",children:e})]})]})}function K({children:e}){let r=(0,I.useRouter)(),i=(0,I.useSearchParams)(),{accessToken:s,authLoading:l}=(0,S.useAuth)(),n=!!i.get("invitation_id");return((0,a.useEffect)(()=>{!l&&n&&r.replace(`${(0,B.migratedHref)("onboarding")}?${i.toString()}`)},[l,n,r,i]),l||n)?(0,t.jsx)(j.default,{}):(0,t.jsx)(E.ThemeProvider,{accessToken:s,children:(0,t.jsx)(q,{children:e})})}e.s(["AgentControlPlaneView",0,U,"default",0,function({children:e}){return(0,t.jsx)(a.Suspense,{fallback:(0,t.jsx)(j.default,{}),children:(0,t.jsx)(H,{children:(0,t.jsx)(K,{children:e})})})}],216370)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0.bx44y-6~tug.js b/litellm/proxy/_experimental/out/_next/static/chunks/0.bx44y-6~tug.js new file mode 100644 index 00000000000..343688035a1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0.bx44y-6~tug.js @@ -0,0 +1,10 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,954616,e=>{"use strict";var t=e.i(271645),n=e.i(114272),i=e.i(540143),l=e.i(915823),r=e.i(619273),a=class extends l.Subscribable{#e;#t=void 0;#n;#i;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#l()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,r.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,r.hashKey)(t.mutationKey)!==(0,r.hashKey)(this.options.mutationKey)?this.reset():this.#n?.state.status==="pending"&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#l(),this.#r(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#l(),this.#r()}mutate(e,t){return this.#i=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#l(){let e=this.#n?.state??(0,n.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#r(e){i.notifyManager.batch(()=>{if(this.#i&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,i={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#i.onSuccess?.(e.data,t,n,i)}catch(e){Promise.reject(e)}try{this.#i.onSettled?.(e.data,null,t,n,i)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#i.onError?.(e.error,t,n,i)}catch(e){Promise.reject(e)}try{this.#i.onSettled?.(void 0,e.error,t,n,i)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},o=e.i(912598);e.s(["useMutation",0,function(e,n){let l=(0,o.useQueryClient)(n),[s]=t.useState(()=>new a(l,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let c=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(i.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),d=t.useCallback((e,t)=>{s.mutate(e,t).catch(r.noop)},[s]);if(c.error&&(0,r.shouldThrowError)(s.options.throwOnError,[c.error]))throw c.error;return{...c,mutate:d,mutateAsync:c.mutate}}],954616)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var l=e.i(9583),r=n.forwardRef(function(e,r){return n.createElement(l.default,(0,t.default)({},e,{ref:r,icon:i}))});e.s(["ExclamationCircleOutlined",0,r],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(529681),l=e.i(242064),r=e.i(517455),a=e.i(185793),o=e.i(721369),s=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let c=e=>{var{prefixCls:i,className:r,hoverable:a=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:c}=t.useContext(l.ConfigContext),d=c("card",i),u=(0,n.default)(`${d}-grid`,r,{[`${d}-grid-hoverable`]:a});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var d=e.i(915654),u=e.i(183293),g=e.i(246422),b=e.i(838378);let p=(0,g.genStyleHooks)("Card",e=>{let t=(0,b.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:n,cardHeadPadding:i,colorBorderSecondary:l,boxShadowTertiary:r,bodyPadding:a,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:r},[`${t}-head`]:(e=>{let{antCls:t,componentCls:n,headerHeight:i,headerPadding:l,tabsMarginBottom:r}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:i,marginBottom:-1,padding:`0 ${(0,d.unit)(l)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${n}-typography, + > ${n}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:r,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:a,borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:n,cardShadow:i,lineWidth:l}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,d.unit)(l)} 0 0 0 ${n}, + 0 ${(0,d.unit)(l)} 0 0 ${n}, + ${(0,d.unit)(l)} ${(0,d.unit)(l)} 0 0 ${n}, + ${(0,d.unit)(l)} 0 0 0 ${n} inset, + 0 ${(0,d.unit)(l)} 0 0 ${n} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:i}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:n,actionsLiMargin:i,cardActionsIconSize:l,colorBorderSecondary:r,actionsBg:a}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:a,borderTop:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${r}`,display:"flex",borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:i,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${n}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,d.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${n}`]:{fontSize:l,lineHeight:(0,d.unit)(e.calc(l).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${r}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,d.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${l}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:n}},[`${t}-contain-grid`]:{borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:i}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:n,headerPadding:i,bodyPadding:l}=e;return{[`${t}-head`]:{padding:`0 ${(0,d.unit)(i)}`,background:n,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,d.unit)(e.padding)} ${(0,d.unit)(l)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:n,headerPaddingSM:i,headerHeightSM:l,headerFontSizeSM:r}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:l,padding:`0 ${(0,d.unit)(i)}`,fontSize:r,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:n}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,n;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(n=e.headerPadding)?n:e.paddingLG}});var m=e.i(792812),h=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let f=e=>{let{actionClasses:n,actions:i=[],actionStyle:l}=e;return t.createElement("ul",{className:n,style:l},i.map((e,n)=>{let l=`action-${n}`;return t.createElement("li",{style:{width:`${100/i.length}%`},key:l},t.createElement("span",null,e))}))},y=t.forwardRef((e,s)=>{let d,{prefixCls:u,className:g,rootClassName:b,style:y,extra:$,headStyle:v={},bodyStyle:O={},title:x,loading:j,bordered:S,variant:C,size:E,type:w,cover:N,actions:z,tabList:M,children:P,activeTabKey:B,defaultActiveTabKey:T,tabBarExtraContent:k,hoverable:R,tabProps:L={},classNames:G,styles:I}=e,H=h(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:W,direction:D,card:A}=t.useContext(l.ConfigContext),[F]=(0,m.default)("card",C,S),X=e=>{var t;return(0,n.default)(null==(t=null==A?void 0:A.classNames)?void 0:t[e],null==G?void 0:G[e])},K=e=>{var t;return Object.assign(Object.assign({},null==(t=null==A?void 0:A.styles)?void 0:t[e]),null==I?void 0:I[e])},q=t.useMemo(()=>{let e=!1;return t.Children.forEach(P,t=>{(null==t?void 0:t.type)===c&&(e=!0)}),e},[P]),U=W("card",u),[Q,V,_]=p(U),J=t.createElement(a.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},P),Y=void 0!==B,Z=Object.assign(Object.assign({},L),{[Y?"activeKey":"defaultActiveKey"]:Y?B:T,tabBarExtraContent:k}),ee=(0,r.default)(E),et=ee&&"default"!==ee?ee:"large",en=M?t.createElement(o.default,Object.assign({size:et},Z,{className:`${U}-head-tabs`,onChange:t=>{var n;null==(n=e.onTabChange)||n.call(e,t)},items:M.map(e=>{var{tab:t}=e;return Object.assign({label:t},h(e,["tab"]))})})):null;if(x||$||en){let e=(0,n.default)(`${U}-head`,X("header")),i=(0,n.default)(`${U}-head-title`,X("title")),l=(0,n.default)(`${U}-extra`,X("extra")),r=Object.assign(Object.assign({},v),K("header"));d=t.createElement("div",{className:e,style:r},t.createElement("div",{className:`${U}-head-wrapper`},x&&t.createElement("div",{className:i,style:K("title")},x),$&&t.createElement("div",{className:l,style:K("extra")},$)),en)}let ei=(0,n.default)(`${U}-cover`,X("cover")),el=N?t.createElement("div",{className:ei,style:K("cover")},N):null,er=(0,n.default)(`${U}-body`,X("body")),ea=Object.assign(Object.assign({},O),K("body")),eo=t.createElement("div",{className:er,style:ea},j?J:P),es=(0,n.default)(`${U}-actions`,X("actions")),ec=(null==z?void 0:z.length)?t.createElement(f,{actionClasses:es,actionStyle:K("actions"),actions:z}):null,ed=(0,i.default)(H,["onTabChange"]),eu=(0,n.default)(U,null==A?void 0:A.className,{[`${U}-loading`]:j,[`${U}-bordered`]:"borderless"!==F,[`${U}-hoverable`]:R,[`${U}-contain-grid`]:q,[`${U}-contain-tabs`]:null==M?void 0:M.length,[`${U}-${ee}`]:ee,[`${U}-type-${w}`]:!!w,[`${U}-rtl`]:"rtl"===D},g,b,V,_),eg=Object.assign(Object.assign({},null==A?void 0:A.style),y);return Q(t.createElement("div",Object.assign({ref:s},ed,{className:eu,style:eg}),d,el,eo,ec))});var $=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};y.Grid=c,y.Meta=e=>{let{prefixCls:i,className:r,avatar:a,title:o,description:s}=e,c=$(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:d}=t.useContext(l.ConfigContext),u=d("card",i),g=(0,n.default)(`${u}-meta`,r),b=a?t.createElement("div",{className:`${u}-meta-avatar`},a):null,p=o?t.createElement("div",{className:`${u}-meta-title`},o):null,m=s?t.createElement("div",{className:`${u}-meta-description`},s):null,h=p||m?t.createElement("div",{className:`${u}-meta-detail`},p,m):null;return t.createElement("div",Object.assign({},c,{className:g}),b,h)},e.s(["Card",0,y],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(908206),l=e.i(242064),r=e.i(517455),a=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var c=e.i(876556),d=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n},u=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let g=e=>{let{itemPrefixCls:i,component:l,span:r,className:a,style:o,labelStyle:c,contentStyle:d,bordered:u,label:g,content:b,colon:p,type:m,styles:h}=e,{classNames:f}=t.useContext(s),y=Object.assign(Object.assign({},c),null==h?void 0:h.label),$=Object.assign(Object.assign({},d),null==h?void 0:h.content);if(u)return t.createElement(l,{colSpan:r,style:o,className:(0,n.default)(a,{[`${i}-item-${m}`]:"label"===m||"content"===m,[null==f?void 0:f.label]:(null==f?void 0:f.label)&&"label"===m,[null==f?void 0:f.content]:(null==f?void 0:f.content)&&"content"===m})},null!=g&&t.createElement("span",{style:y},g),null!=b&&t.createElement("span",{style:$},b));return t.createElement(l,{colSpan:r,style:o,className:(0,n.default)(`${i}-item`,a)},t.createElement("div",{className:`${i}-item-container`},null!=g&&t.createElement("span",{style:y,className:(0,n.default)(`${i}-item-label`,null==f?void 0:f.label,{[`${i}-item-no-colon`]:!p})},g),null!=b&&t.createElement("span",{style:$,className:(0,n.default)(`${i}-item-content`,null==f?void 0:f.content)},b)))};function b(e,{colon:n,prefixCls:i,bordered:l},{component:r,type:a,showLabel:o,showContent:s,labelStyle:c,contentStyle:d,styles:u}){return e.map(({label:e,children:b,prefixCls:p=i,className:m,style:h,labelStyle:f,contentStyle:y,span:$=1,key:v,styles:O},x)=>"string"==typeof r?t.createElement(g,{key:`${a}-${v||x}`,className:m,style:h,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),f),null==O?void 0:O.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),y),null==O?void 0:O.content)},span:$,colon:n,component:r,itemPrefixCls:p,bordered:l,label:o?e:null,content:s?b:null,type:a}):[t.createElement(g,{key:`label-${v||x}`,className:m,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),h),f),null==O?void 0:O.label),span:1,colon:n,component:r[0],itemPrefixCls:p,bordered:l,label:e,type:"label"}),t.createElement(g,{key:`content-${v||x}`,className:m,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),h),y),null==O?void 0:O.content),span:2*$-1,component:r[1],itemPrefixCls:p,bordered:l,content:b,type:"content"})])}let p=e=>{let n=t.useContext(s),{prefixCls:i,vertical:l,row:r,index:a,bordered:o}=e;return l?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${a}`,className:`${i}-row`},b(r,e,Object.assign({component:"th",type:"label",showLabel:!0},n))),t.createElement("tr",{key:`content-${a}`,className:`${i}-row`},b(r,e,Object.assign({component:"td",type:"content",showContent:!0},n)))):t.createElement("tr",{key:a,className:`${i}-row`},b(r,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},n)))};e.i(296059);var m=e.i(915654),h=e.i(183293),f=e.i(246422),y=e.i(838378);let $=(0,f.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:n,itemPaddingBottom:i,itemPaddingEnd:l,colonMarginRight:r,colonMarginLeft:a,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,h.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:n}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.padding)} ${(0,m.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:n,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.paddingSM)} ${(0,m.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.paddingXS)} ${(0,m.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},h.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:n,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:i,paddingInlineEnd:l},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,m.unit)(a)} ${(0,m.unit)(r)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,y.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var v=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let O=e=>{let g,{prefixCls:b,title:m,extra:h,column:f,colon:y=!0,bordered:O,layout:x,children:j,className:S,rootClassName:C,style:E,size:w,labelStyle:N,contentStyle:z,styles:M,items:P,classNames:B}=e,T=v(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:k,direction:R,className:L,style:G,classNames:I,styles:H}=(0,l.useComponentConfig)("descriptions"),W=k("descriptions",b),D=(0,a.default)(),A=t.useMemo(()=>{var e;return"number"==typeof f?f:null!=(e=(0,i.matchScreen)(D,Object.assign(Object.assign({},o),f)))?e:3},[D,f]),F=(g=t.useMemo(()=>P||(0,c.default)(j).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[P,j]),t.useMemo(()=>g.map(e=>{var{span:t}=e,n=d(e,["span"]);return"filled"===t?Object.assign(Object.assign({},n),{filled:!0}):Object.assign(Object.assign({},n),{span:"number"==typeof t?t:(0,i.matchScreen)(D,t)})}),[g,D])),X=(0,r.default)(w),K=((e,n)=>{let[i,l]=(0,t.useMemo)(()=>{let t,i,l,r;return t=[],i=[],l=!1,r=0,n.filter(e=>e).forEach(n=>{let{filled:a}=n,o=u(n,["filled"]);if(a){i.push(o),t.push(i),i=[],r=0;return}let s=e-r;(r+=n.span||1)>=e?(r>e?(l=!0,i.push(Object.assign(Object.assign({},o),{span:s}))):i.push(o),t.push(i),i=[],r=0):i.push(o)}),i.length>0&&t.push(i),[t=t.map(t=>{let n=t.reduce((e,t)=>e+(t.span||1),0);if(n({labelStyle:N,contentStyle:z,styles:{content:Object.assign(Object.assign({},H.content),null==M?void 0:M.content),label:Object.assign(Object.assign({},H.label),null==M?void 0:M.label)},classNames:{label:(0,n.default)(I.label,null==B?void 0:B.label),content:(0,n.default)(I.content,null==B?void 0:B.content)}}),[N,z,M,B,I,H]);return q(t.createElement(s.Provider,{value:V},t.createElement("div",Object.assign({className:(0,n.default)(W,L,I.root,null==B?void 0:B.root,{[`${W}-${X}`]:X&&"default"!==X,[`${W}-bordered`]:!!O,[`${W}-rtl`]:"rtl"===R},S,C,U,Q),style:Object.assign(Object.assign(Object.assign(Object.assign({},G),H.root),null==M?void 0:M.root),E)},T),(m||h)&&t.createElement("div",{className:(0,n.default)(`${W}-header`,I.header,null==B?void 0:B.header),style:Object.assign(Object.assign({},H.header),null==M?void 0:M.header)},m&&t.createElement("div",{className:(0,n.default)(`${W}-title`,I.title,null==B?void 0:B.title),style:Object.assign(Object.assign({},H.title),null==M?void 0:M.title)},m),h&&t.createElement("div",{className:(0,n.default)(`${W}-extra`,I.extra,null==B?void 0:B.extra),style:Object.assign(Object.assign({},H.extra),null==M?void 0:M.extra)},h)),t.createElement("div",{className:`${W}-view`},t.createElement("table",null,t.createElement("tbody",null,K.map((e,n)=>t.createElement(p,{key:n,index:n,colon:y,prefixCls:W,vertical:"vertical"===x,bordered:O,row:e}))))))))};O.Item=({children:e})=>e,e.s(["Descriptions",0,O],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),n=e.i(732961),i=e.i(289882),l=e.i(170517),r=e.i(628882),a=e.i(320890),o=e.i(104458),s=e.i(722319),c=e.i(8398),d=e.i(279728);e.i(765846);var u=e.i(602716),g=e.i(328052),b=e.i(135551);let p=(e,t)=>new b.FastColor(e).setA(t).toRgbString(),m=(e,t)=>new b.FastColor(e).lighten(t).toHexString(),h=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},f=(e,t)=>{let n=e||"#000",i=t||"#fff";return{colorBgBase:n,colorTextBase:i,colorText:p(i,.85),colorTextSecondary:p(i,.65),colorTextTertiary:p(i,.45),colorTextQuaternary:p(i,.25),colorFill:p(i,.18),colorFillSecondary:p(i,.12),colorFillTertiary:p(i,.08),colorFillQuaternary:p(i,.04),colorBgSolid:p(i,.95),colorBgSolidHover:p(i,1),colorBgSolidActive:p(i,.9),colorBgElevated:m(n,12),colorBgContainer:m(n,8),colorBgLayout:m(n,0),colorBgSpotlight:m(n,26),colorBgBlur:p(i,.04),colorBorder:m(n,26),colorBorderSecondary:m(n,19)}},y={defaultSeed:a.defaultConfig.token,useToken:function(){let[e,t,n]=(0,o.useToken)();return{theme:e,token:t,hashId:n}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let n=Object.keys(l.defaultPresetColors).map(t=>{let n=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,i,l)=>(e[`${t}-${l+1}`]=n[l],e[`${t}${l+1}`]=n[l],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),i=null!=t?t:(0,s.default)(e),r=(0,g.default)(e,{generateColorPalettes:h,generateNeutralColorPalettes:f});return Object.assign(Object.assign(Object.assign(Object.assign({},i),n),r),{colorPrimaryBg:r.colorPrimaryBorder,colorPrimaryBgHover:r.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let n=null!=t?t:(0,s.default)(e),i=n.fontSizeSM,l=n.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},n),function(e){let{sizeUnit:t,sizeStep:n}=e,i=n-2;return{sizeXXL:t*(i+10),sizeXL:t*(i+6),sizeLG:t*(i+2),sizeMD:t*(i+2),sizeMS:t*(i+1),size:t*i,sizeSM:t*i,sizeXS:t*(i-1),sizeXXS:t*(i-1)}}(null!=t?t:e)),(0,d.default)(i)),{controlHeight:l}),(0,c.default)(Object.assign(Object.assign({},n),{controlHeight:l})))},getDesignToken:e=>{let a=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):i.default,o=Object.assign(Object.assign({},l.default),null==e?void 0:e.token);return(0,n.getComputedToken)(o,{override:null==e?void 0:e.token},a,r.default)},defaultConfig:a.defaultConfig,_internalContext:a.DesignTokenContext};e.s(["theme",0,y],368869)},127952,e=>{"use strict";var t=e.i(843476),n=e.i(560445),i=e.i(175712),l=e.i(869216),r=e.i(311451),a=e.i(212931),o=e.i(898586),s=e.i(368869),c=e.i(270377),d=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:g,message:b,resourceInformationTitle:p,resourceInformation:m,onCancel:h,onOk:f,confirmLoading:y,requiredConfirmation:$}){let{Title:v,Text:O}=o.Typography,{token:x}=s.theme.useToken(),[j,S]=(0,d.useState)("");return(0,d.useEffect)(()=>{e&&S("")},[e]),(0,t.jsx)(a.Modal,{title:u,open:e,onOk:f,onCancel:h,confirmLoading:y,okText:y?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!$&&j!==$||y},cancelButtonProps:{disabled:y},children:(0,t.jsxs)("div",{className:"space-y-4",children:[g&&(0,t.jsx)(n.Alert,{message:g,type:"warning"}),(0,t.jsx)(i.Card,{title:p,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:x.colorErrorBg,borderColor:x.colorErrorBorder}},style:{backgroundColor:x.colorErrorBg,borderColor:x.colorErrorBorder},children:(0,t.jsx)(l.Descriptions,{column:1,size:"small",children:m&&m.map(({label:e,value:n,...i})=>(0,t.jsx)(l.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(O,{...i,children:n??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(O,{children:b})}),$&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(O,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(O,{children:"Type "}),(0,t.jsx)(O,{strong:!0,type:"danger",children:$}),(0,t.jsx)(O,{children:" to confirm deletion:"})]}),(0,t.jsx)(r.Input,{value:j,onChange:e=>S(e.target.value),placeholder:$,className:"rounded-md",prefix:(0,t.jsx)(c.ExclamationCircleOutlined,{style:{color:x.colorError}}),autoFocus:!0})]})]})})}])},525720,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(529681),l=e.i(908286),r=e.i(242064),a=e.i(246422),o=e.i(838378);let s=["wrap","nowrap","wrap-reverse"],c=["flex-start","flex-end","start","end","center","space-between","space-around","space-evenly","stretch","normal","left","right"],d=["center","start","end","flex-start","flex-end","self-start","self-end","baseline","normal","stretch"],u=function(e,t){let i,l,r;return(0,n.default)(Object.assign(Object.assign(Object.assign({},(i=!0===t.wrap?"wrap":t.wrap,{[`${e}-wrap-${i}`]:i&&s.includes(i)})),(l={},d.forEach(n=>{l[`${e}-align-${n}`]=t.align===n}),l[`${e}-align-stretch`]=!t.align&&!!t.vertical,l)),(r={},c.forEach(n=>{r[`${e}-justify-${n}`]=t.justify===n}),r)))},g=(0,a.genStyleHooks)("Flex",e=>{let{paddingXS:t,padding:n,paddingLG:i}=e,l=(0,o.mergeToken)(e,{flexGapSM:t,flexGap:n,flexGapLG:i});return[(e=>{let{componentCls:t}=e;return{[t]:{display:"flex",margin:0,padding:0,"&-vertical":{flexDirection:"column"},"&-rtl":{direction:"rtl"},"&:empty":{display:"none"}}}})(l),(e=>{let{componentCls:t}=e;return{[t]:{"&-gap-small":{gap:e.flexGapSM},"&-gap-middle":{gap:e.flexGap},"&-gap-large":{gap:e.flexGapLG}}}})(l),(e=>{let{componentCls:t}=e,n={};return s.forEach(e=>{n[`${t}-wrap-${e}`]={flexWrap:e}}),n})(l),(e=>{let{componentCls:t}=e,n={};return d.forEach(e=>{n[`${t}-align-${e}`]={alignItems:e}}),n})(l),(e=>{let{componentCls:t}=e,n={};return c.forEach(e=>{n[`${t}-justify-${e}`]={justifyContent:e}}),n})(l)]},()=>({}),{resetStyle:!1});var b=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let p=t.default.forwardRef((e,a)=>{let{prefixCls:o,rootClassName:s,className:c,style:d,flex:p,gap:m,vertical:h=!1,component:f="div",children:y}=e,$=b(e,["prefixCls","rootClassName","className","style","flex","gap","vertical","component","children"]),{flex:v,direction:O,getPrefixCls:x}=t.default.useContext(r.ConfigContext),j=x("flex",o),[S,C,E]=g(j),w=null!=h?h:null==v?void 0:v.vertical,N=(0,n.default)(c,s,null==v?void 0:v.className,j,C,E,u(j,e),{[`${j}-rtl`]:"rtl"===O,[`${j}-gap-${m}`]:(0,l.isPresetSize)(m),[`${j}-vertical`]:w}),z=Object.assign(Object.assign({},null==v?void 0:v.style),d);return p&&(z.flex=p),m&&!(0,l.isPresetSize)(m)&&(z.gap=m),S(t.default.createElement(f,Object.assign({ref:a,className:N,style:z},(0,i.default)($,["justify","wrap","align"])),y))});e.s(["Flex",0,p],525720)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0.cm9osit06~i.js b/litellm/proxy/_experimental/out/_next/static/chunks/0.cm9osit06~i.js new file mode 100644 index 00000000000..565f5ec8246 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0.cm9osit06~i.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),s=e.i(529681);let l=e=>{let{prefixCls:a,className:s,style:l,size:n,shape:i}=e,o=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),c=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),d=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,o,c,s),style:Object.assign(Object.assign({},d),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),o=e.i(246422),c=e.i(838378);let d=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),m=e=>({height:e,lineHeight:(0,i.unit)(e)}),u=e=>Object.assign({width:e},m(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},m(e)),p=e=>Object.assign({width:e},m(e)),x=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},m(e)),f=(0,o.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:s,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:o,controlHeightLG:c,controlHeightSM:m,gradientFromColor:f,padding:b,marginSM:v,borderRadius:j,titleHeight:N,blockRadius:w,paragraphLiHeight:y,controlHeightXS:k,paragraphMarginTop:$}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:b,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:f},u(o)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},u(c)),[`${r}-sm`]:Object.assign({},u(m))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:N,background:f,borderRadius:w,[`+ ${s}`]:{marginBlockStart:m}},[s]:{padding:0,"> li":{width:"100%",height:y,listStyle:"none",background:f,borderRadius:w,"+ li":{marginBlockStart:k}}},[`${s}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${s} > li`]:{borderRadius:j}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:v,[`+ ${s}`]:{marginBlockStart:$}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},x(e,a,r)),{[`${r}-lg`]:Object.assign({},h(s,i))}),x(e,s,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),x(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},u(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},u(s)),[`${t}${t}-sm`]:Object.assign({},u(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},g(t,i)),[`${a}-lg`]:Object.assign({},g(s,i)),[`${a}-sm`]:Object.assign({},g(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:s,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:s},p(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},p(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${s} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:d,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,c.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),b=e=>{let{prefixCls:a,className:s,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,s),style:l},i)},v=({prefixCls:e,className:a,width:s,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:s},l)});function j(e){return e&&"object"==typeof e?e:{}}let N=e=>{let{prefixCls:s,loading:n,className:i,rootClassName:o,style:c,children:d,avatar:m=!1,title:u=!0,paragraph:g=!0,active:p,round:x}=e,{getPrefixCls:h,direction:N,className:w,style:y}=(0,a.useComponentConfig)("skeleton"),k=h("skeleton",s),[$,C,T]=f(k);if(n||!("loading"in e)){let e,a,s=!!m,n=!!u,d=!!g;if(s){let r=Object.assign(Object.assign({prefixCls:`${k}-avatar`},n&&!d?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),j(m));e=t.createElement("div",{className:`${k}-header`},t.createElement(l,Object.assign({},r)))}if(n||d){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${k}-title`},!s&&d?{width:"38%"}:s&&d?{width:"50%"}:{}),j(u));e=t.createElement(v,Object.assign({},r))}if(d){let e,a=Object.assign(Object.assign({prefixCls:`${k}-paragraph`},(e={},s&&n||(e.width="61%"),!s&&n?e.rows=3:e.rows=2,e)),j(g));r=t.createElement(b,Object.assign({},a))}a=t.createElement("div",{className:`${k}-content`},e,r)}let h=(0,r.default)(k,{[`${k}-with-avatar`]:s,[`${k}-active`]:p,[`${k}-rtl`]:"rtl"===N,[`${k}-round`]:x},w,i,o,C,T);return $(t.createElement("div",{className:h,style:Object.assign(Object.assign({},y),c)},e,a))}return null!=d?d:null};N.Button=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d=!1,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-button`,size:m},b))))},N.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,shape:d="circle",size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls","className"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-avatar`,shape:d,size:m},b))))},N.Input=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-input`,size:m},b))))},N.Image=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),d=c("skeleton",s),[m,u,g]=f(d),p=(0,r.default)(d,`${d}-element`,{[`${d}-active`]:o},l,n,u,g);return m(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${d}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${d}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${d}-image-path`})))))},N.Node=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o,children:c}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),m=d("skeleton",s),[u,g,p]=f(m),x=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:o},g,l,n,p);return u(t.createElement("div",{className:x},t.createElement("div",{className:(0,r.default)(`${m}-image`,l),style:i},c)))},e.s(["default",0,N],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let s=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(s),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let s={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",s);let l=e<0?"-":"",n=Math.abs(e),i=n,o="";return n>=1e6?(i=n/1e6,o="M"):n>=1e3&&(i=n/1e3,o="K"),`${l}${i.toLocaleString("en-US",s)}${o}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return s(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),s(e,r)}},s=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let s=document.execCommand("copy");if(document.body.removeChild(a),s)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),s=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(s.TooltipProvider,{delay:300,children:(0,t.jsxs)(s.Tooltip,{children:[(0,t.jsx)(s.TooltipTrigger,{render:r}),(0,t.jsx)(s.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:s,tooltip:i,dataTestId:o}){let c=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":o,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:s});return i?(0,t.jsx)(l,{content:i,trigger:c}):c}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],s=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,o,c,d=e?new Date(e):null;return!d||Number.isNaN(d.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,o=`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`,c=`${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`,`${o}, ${c} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`:`${a[d.getMonth()]} ${d.getDate()}, ${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let o={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:s,copyable:c=!1,truncate:d=!0,fallback:m="-",tooltip:u,disabled:g=!1,dataTestId:p,className:x}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:m});let h=!!s&&!g,f=(0,n.cn)(o[a].base,h&&o[a].clickable,d&&"block max-w-[15ch] truncate",g&&"opacity-50",x),b=h?(0,r.jsx)("button",{type:"button",className:f,"data-testid":p,onClick:()=>s(e),children:e}):(0,r.jsx)("span",{className:f,"data-testid":p,children:e}),v=(0,r.jsx)(t.CellTooltip,{content:u??e,trigger:b});return c?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[v,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):v}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:s=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?s?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(s("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},o),n))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},o),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-middle whitespace-nowrap text-left p-4",i)},o),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},o),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},o),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("row"),i)},o),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),s=e.i(480731),l=e.i(95779),n=e.i(444755),i=e.i(673706);let o={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},c={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},d=(0,i.makeClassName)("Badge"),m=r.default.forwardRef((e,m)=>{let{color:u,icon:g,size:p=s.Sizes.SM,tooltip:x,className:h,children:f}=e,b=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),v=g||null,{tooltipProps:j,getReferenceProps:N}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,j.refs.setReference]),className:(0,n.tremorTwMerge)(d("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",u?(0,n.tremorTwMerge)((0,i.getColorClassNames)(u,l.colorPalette.background).bgColor,(0,i.getColorClassNames)(u,l.colorPalette.iconText).textColor,(0,i.getColorClassNames)(u,l.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),o[p].paddingX,o[p].paddingY,o[p].fontSize,h)},N,b),r.default.createElement(a.default,Object.assign({text:x},j)),v?r.default.createElement(v,{className:(0,n.tremorTwMerge)(d("icon"),"shrink-0 -ml-1 mr-1.5",c[p].height,c[p].width)}):null,r.default.createElement("span",{className:(0,n.tremorTwMerge)(d("text"),"whitespace-nowrap")},f))});m.displayName="Badge",e.s(["Badge",0,m],389083)},530212,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,r],530212)},384767,e=>{"use strict";var t=e.i(843476),r=e.i(599724),a=e.i(271645),s=e.i(389083);let l=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"}))});var n=e.i(602869);let i=function({vectorStores:e,accessToken:i}){let[o,c]=(0,a.useState)([]);return(0,a.useEffect)(()=>{(async()=>{if(i&&0!==e.length)try{let e=await (0,n.vectorStoreListCall)(i);e.data&&c(e.data.map(e=>({vector_store_id:e.vector_store_id,vector_store_name:e.vector_store_name})))}catch(e){console.error("Error fetching vector stores:",e)}})()},[i,e.length]),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Vector Stores"}),(0,t.jsx)(s.Badge,{color:"blue",size:"xs",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map((e,r)=>{let a;return(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-sm font-medium",children:(a=o.find(t=>t.vector_store_id===e))?`${a.vector_store_name||a.vector_store_id} (${a.vector_store_id})`:e},r)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No vector stores configured"})]})]})},o=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"}))});var c=e.i(871943),d=e.i(502547),m=e.i(592968),u=e.i(234713);let g=function({mcpServers:e,mcpAccessGroups:l=[],mcpToolPermissions:i={},mcpToolsets:g=[],accessToken:p}){let[x,h]=(0,a.useState)([]),[f,b]=(0,a.useState)([]),[v,j]=(0,a.useState)(new Set),[N,w]=(0,a.useState)(new Set);(0,a.useEffect)(()=>{(async()=>{if(p&&e.length>0)try{let e=await (0,n.fetchMCPServers)(p);e&&Array.isArray(e)?h(e):e.data&&Array.isArray(e.data)&&h(e.data)}catch(e){console.error("Error fetching MCP servers:",e)}})()},[p,e.length]),(0,a.useEffect)(()=>{(async()=>{if(p&&g.length>0)try{let e=await (0,n.fetchMCPToolsets)(p),t=Array.isArray(e)?e.filter(e=>g.includes(e.toolset_id)):[];b(t)}catch(e){console.error("Error fetching toolsets:",e)}})()},[p,g.length]);let y=e.includes(u.NO_MCP_SERVERS_SENTINEL),k=e.includes(u.ALL_PROXY_MCP_SERVERS_SENTINEL),$=[...e.filter(e=>e!==u.NO_MCP_SERVERS_SENTINEL&&e!==u.ALL_PROXY_MCP_SERVERS_SENTINEL).map(e=>({type:"server",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],C=$.length+g.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"MCP Servers"}),(0,t.jsx)(s.Badge,{color:y?"red":"blue",size:"xs",children:y?"Blocked":k?"All":C})]}),y?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-red-400"}),(0,t.jsx)(r.Text,{className:"text-red-700 text-sm",children:"No MCP servers — this key is blocked from all MCP servers, including its team's servers"})]}):k?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-400"}),(0,t.jsx)(r.Text,{className:"text-blue-700 text-sm",children:"All Proxy MCP Servers"})]}):C>0?(0,t.jsxs)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:[$.map((e,r)=>{let a="server"===e.type?i[e.value]:void 0,s=a&&a.length>0,l=v.has(e.value);return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>{var t;return s&&(t=e.value,void j(e=>{let r=new Set(e);return r.has(t)?r.delete(t):r.add(t),r}))},className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 transition-all ${s?"cursor-pointer hover:bg-gray-50 hover:border-gray-300":"bg-white"}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"server"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-blue-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=x.find(t=>t.server_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.alias} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})}),s&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:a.length}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===a.length?"tool":"tools"}),l?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),s&&l&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-blue-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.map((e,r)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-xs font-medium",children:e},r))})})]},r)}),g.length>0&&g.map((e,r)=>{let a=f.find(t=>t.toolset_id===e),s=N.has(e),l=a?.tools.length??0;return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>l>0&&void w(t=>{let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}),className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-purple-200 transition-all ${l>0?"cursor-pointer hover:bg-purple-50 hover:border-purple-300":"bg-white"}`,children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:a?.toolset_name??e}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-purple-600 bg-purple-50 border border-purple-200 rounded-sm uppercase tracking-wide shrink-0",children:"Toolset"})]}),l>0&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:l}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===l?"tool":"tools"}),s?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),l>0&&s&&a&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-purple-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.tools.map((e,r)=>(0,t.jsxs)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-purple-50 border border-purple-200 text-purple-800 text-xs font-medium",children:[(0,t.jsxs)("span",{className:"text-purple-400 mr-1 text-[10px]",children:[e.server_id.slice(0,6),"…"]}),e.tool_name]},r))})})]},`toolset-${r}`)})]}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No MCP servers, access groups, or toolsets configured"})]})]})},p=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"}))}),x=function({agents:e,agentAccessGroups:l=[],accessToken:i}){let[o,c]=(0,a.useState)([]);(0,a.useEffect)(()=>{(async()=>{if(i&&e.length>0)try{let e=await (0,n.getAgentsList)(i);e&&e.agents&&Array.isArray(e.agents)&&c(e.agents)}catch(e){console.error("Error fetching agents:",e)}})()},[i,e.length]);let d=[...e.map(e=>({type:"agent",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],u=d.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-purple-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Agents"}),(0,t.jsx)(s.Badge,{color:"purple",size:"xs",children:u})]}),u>0?(0,t.jsx)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:d.map((e,r)=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsx)("div",{className:"flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 bg-white",children:(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"agent"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=o.find(t=>t.agent_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.agent_name} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})})})},r))}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No agents or access groups configured"})]})]})};e.s(["default",0,function({objectPermission:e,variant:a="card",className:s="",accessToken:l}){let n=e?.vector_stores||[],o=e?.mcp_servers||[],c=e?.mcp_access_groups||[],d=e?.mcp_tool_permissions||{},m=e?.mcp_toolsets||[],u=e?.agents||[],p=e?.agent_access_groups||[],h=e?.search_tools||[],f=(0,t.jsxs)("div",{className:"card"===a?"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6":"space-y-4",children:[(0,t.jsx)(i,{vectorStores:n,accessToken:l}),(0,t.jsx)(g,{mcpServers:o,mcpAccessGroups:c,mcpToolPermissions:d,mcpToolsets:m,accessToken:l}),(0,t.jsx)(x,{agents:u,agentAccessGroups:p,accessToken:l}),(0,t.jsxs)("div",{className:"rounded-md border border-gray-100 p-4",children:[(0,t.jsx)(r.Text,{className:"text-sm font-medium text-gray-800",children:"Search tools"}),0===h.length?(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-500",children:"No restriction — all configured search tools are allowed for this team."}):(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-700",children:h.join(", ")})]})]});return"card"===a?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${s}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Object Permissions"}),(0,t.jsx)(r.Text,{className:"text-xs text-gray-500",children:"Access control for Vector Stores and MCP Servers"})]})}),f]}):(0,t.jsxs)("div",{className:`${s}`,children:[(0,t.jsx)(r.Text,{className:"font-medium text-gray-900 mb-3",children:"Object Permissions"}),f]})}],384767)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0.xp85h9ki~9t.js b/litellm/proxy/_experimental/out/_next/static/chunks/0.xp85h9ki~9t.js new file mode 100644 index 00000000000..24c8f4e7454 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0.xp85h9ki~9t.js @@ -0,0 +1,41 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,735049,e=>{"use strict";var t=e.i(654310),n=function(e){if((0,t.default)()&&window.document.documentElement){var n=Array.isArray(e)?e:[e],l=window.document.documentElement;return n.some(function(e){return e in l.style})}return!1},l=function(e,t){if(!n(e))return!1;var l=document.createElement("div"),r=l.style[e];return l.style[e]=t,l.style[e]!==r};e.s(["isStyleSupport",0,function(e,t){return Array.isArray(e)||void 0===t?n(e):l(e,t)}])},190144,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"}}]},name:"copy",theme:"outlined"};var r=e.i(9583),o=n.forwardRef(function(e,o){return n.createElement(r.default,(0,t.default)({},e,{ref:o,icon:l}))});e.s(["default",0,o],190144)},486794,(e,t,n)=>{t.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],l=0;l{"use strict";var l=e.r(486794),r={"text/plain":"Text","text/html":"Url",default:"Text"};t.exports=function(e,t){var n,o,a,i,c,s,u,d,p=!1;t||(t={}),a=t.debug||!1;try{if(c=l(),s=document.createRange(),u=document.getSelection(),(d=document.createElement("span")).textContent=e,d.ariaHidden="true",d.style.all="unset",d.style.position="fixed",d.style.top=0,d.style.clip="rect(0, 0, 0, 0)",d.style.whiteSpace="pre",d.style.webkitUserSelect="text",d.style.MozUserSelect="text",d.style.msUserSelect="text",d.style.userSelect="text",d.addEventListener("copy",function(n){if(n.stopPropagation(),t.format)if(n.preventDefault(),void 0===n.clipboardData){a&&console.warn("unable to use e.clipboardData"),a&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var l=r[t.format]||r.default;window.clipboardData.setData(l,e)}else n.clipboardData.clearData(),n.clipboardData.setData(t.format,e);t.onCopy&&(n.preventDefault(),t.onCopy(n.clipboardData))}),document.body.appendChild(d),s.selectNodeContents(d),u.addRange(s),!document.execCommand("copy"))throw Error("copy command was unsuccessful");p=!0}catch(l){a&&console.error("unable to copy using execCommand: ",l),a&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(t.format||"text",e),t.onCopy&&t.onCopy(window.clipboardData),p=!0}catch(l){a&&console.error("unable to copy using clipboardData: ",l),a&&console.error("falling back to prompt"),n="message"in t?t.message:"Copy to clipboard: #{key}, Enter",o=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C",i=n.replace(/#{\s*key\s*}/g,o),window.prompt(i,e)}}finally{u&&("function"==typeof u.removeRange?u.removeRange(s):u.removeAllRanges()),d&&document.body.removeChild(d),c()}return p}},898586,401361,335771,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(8211),l=e.i(931067);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"}}]},name:"edit",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,n){return t.createElement(o.default,(0,l.default)({},e,{ref:n,icon:r}))});e.s(["default",0,a],401361);var i=e.i(343794),c=e.i(430073),s=e.i(876556),u=e.i(174428),d=e.i(914949),p=e.i(529681),f=e.i(611935),m=e.i(735049),g=e.i(242064),b=e.i(929447),y=e.i(491816);let v={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M864 170h-60c-4.4 0-8 3.6-8 8v518H310v-73c0-6.7-7.8-10.5-13-6.3l-141.9 112a8 8 0 000 12.6l141.9 112c5.3 4.2 13 .4 13-6.3v-75h498c35.3 0 64-28.7 64-64V178c0-4.4-3.6-8-8-8z"}}]},name:"enter",theme:"outlined"};var h=t.forwardRef(function(e,n){return t.createElement(o.default,(0,l.default)({},e,{ref:n,icon:v}))}),x=e.i(404948),O=e.i(763731),E=e.i(635432),w=e.i(183293),S=e.i(246422);e.i(765846);var j=e.i(896091);let C=(0,S.genStyleHooks)("Typography",e=>{let t,{componentCls:n,titleMarginTop:l}=e;return{[n]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({color:e.colorText,wordBreak:"break-word",lineHeight:e.lineHeight,[`&${n}-secondary`]:{color:e.colorTextDescription},[`&${n}-success`]:{color:e.colorSuccessText},[`&${n}-warning`]:{color:e.colorWarningText},[`&${n}-danger`]:{color:e.colorErrorText,"a&:active, a&:focus":{color:e.colorErrorTextActive},"a&:hover":{color:e.colorErrorTextHover}},[`&${n}-disabled`]:{color:e.colorTextDisabled,cursor:"not-allowed",userSelect:"none"},[` + div&, + p + `]:{marginBottom:"1em"}},(t={},[1,2,3,4,5].forEach(n=>{t[` + h${n}&, + div&-h${n}, + div&-h${n} > textarea, + h${n} + `]=((e,t,n,l)=>{let{titleMarginBottom:r,fontWeightStrong:o}=l;return{marginBottom:r,color:n,fontWeight:o,fontSize:e,lineHeight:t}})(e[`fontSizeHeading${n}`],e[`lineHeightHeading${n}`],e.colorTextHeading,e)}),t)),{[` + & + h1${n}, + & + h2${n}, + & + h3${n}, + & + h4${n}, + & + h5${n} + `]:{marginTop:l},[` + div, + ul, + li, + p, + h1, + h2, + h3, + h4, + h5`]:{[` + + h1, + + h2, + + h3, + + h4, + + h5 + `]:{marginTop:l}}}),{code:{margin:"0 0.2em",paddingInline:"0.4em",paddingBlock:"0.2em 0.1em",fontSize:"85%",fontFamily:e.fontFamilyCode,background:"rgba(150, 150, 150, 0.1)",border:"1px solid rgba(100, 100, 100, 0.2)",borderRadius:3},kbd:{margin:"0 0.2em",paddingInline:"0.4em",paddingBlock:"0.15em 0.1em",fontSize:"90%",fontFamily:e.fontFamilyCode,background:"rgba(150, 150, 150, 0.06)",border:"1px solid rgba(100, 100, 100, 0.2)",borderBottomWidth:2,borderRadius:3},mark:{padding:0,backgroundColor:j.gold[2]},"u, ins":{textDecoration:"underline",textDecorationSkipInk:"auto"},"s, del":{textDecoration:"line-through"},strong:{fontWeight:e.fontWeightStrong},"ul, ol":{marginInline:0,marginBlock:"0 1em",padding:0,li:{marginInline:"20px 0",marginBlock:0,paddingInline:"4px 0",paddingBlock:0}},ul:{listStyleType:"circle",ul:{listStyleType:"disc"}},ol:{listStyleType:"decimal"},"pre, blockquote":{margin:"1em 0"},pre:{padding:"0.4em 0.6em",whiteSpace:"pre-wrap",wordWrap:"break-word",background:"rgba(150, 150, 150, 0.1)",border:"1px solid rgba(100, 100, 100, 0.2)",borderRadius:3,fontFamily:e.fontFamilyCode,code:{display:"inline",margin:0,padding:0,fontSize:"inherit",fontFamily:"inherit",background:"transparent",border:0}},blockquote:{paddingInline:"0.6em 0",paddingBlock:0,borderInlineStart:"4px solid rgba(100, 100, 100, 0.2)",opacity:.85}}),(e=>{let{componentCls:t}=e;return{"a&, a":Object.assign(Object.assign({},(0,w.operationUnit)(e)),{userSelect:"text",[`&[disabled], &${t}-disabled`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:active, &:hover":{color:e.colorTextDisabled},"&:active":{pointerEvents:"none"}}})}})(e)),{[` + ${n}-expand, + ${n}-collapse, + ${n}-edit, + ${n}-copy + `]:Object.assign(Object.assign({},(0,w.operationUnit)(e)),{marginInlineStart:e.marginXXS})}),(e=>{let{componentCls:t,paddingSM:n}=e;return{"&-edit-content":{position:"relative","div&":{insetInlineStart:e.calc(e.paddingSM).mul(-1).equal(),insetBlockStart:e.calc(n).div(-2).add(1).equal(),marginBottom:e.calc(n).div(2).sub(2).equal()},[`${t}-edit-content-confirm`]:{position:"absolute",insetInlineEnd:e.calc(e.marginXS).add(2).equal(),insetBlockEnd:e.marginXS,color:e.colorIcon,fontWeight:"normal",fontSize:e.fontSize,fontStyle:"normal",pointerEvents:"none"},textarea:{margin:"0!important",MozTransition:"none",height:"1em"}}}})(e)),{[`${e.componentCls}-copy-success`]:{[` + &, + &:hover, + &:focus`]:{color:e.colorSuccess}},[`${e.componentCls}-copy-icon-only`]:{marginInlineStart:0}}),{[` + a&-ellipsis, + span&-ellipsis + `]:{display:"inline-block",maxWidth:"100%"},"&-ellipsis-single-line":{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis","a&, span&":{verticalAlign:"bottom"},"> code":{paddingBlock:0,maxWidth:"calc(100% - 1.2em)",display:"inline-block",overflow:"hidden",textOverflow:"ellipsis",verticalAlign:"bottom",boxSizing:"content-box"}},"&-ellipsis-multiple-line":{display:"-webkit-box",overflow:"hidden",WebkitLineClamp:3,WebkitBoxOrient:"vertical"}}),{"&-rtl":{direction:"rtl"}})}},()=>({titleMarginTop:"1.2em",titleMarginBottom:"0.5em"})),k=e=>{let{prefixCls:n,"aria-label":l,className:r,style:o,direction:a,maxLength:c,autoSize:s=!0,value:u,onSave:d,onCancel:p,onEnd:f,component:m,enterIcon:g=t.createElement(h,null)}=e,b=t.useRef(null),y=t.useRef(!1),v=t.useRef(null),[w,S]=t.useState(u);t.useEffect(()=>{S(u)},[u]),t.useEffect(()=>{var e;if(null==(e=b.current)?void 0:e.resizableTextArea){let{textArea:e}=b.current.resizableTextArea;e.focus();let{length:t}=e.value;e.setSelectionRange(t,t)}},[]);let j=()=>{d(w.trim())},[k,R,$]=C(n),T=(0,i.default)(n,`${n}-edit-content`,{[`${n}-rtl`]:"rtl"===a,[`${n}-${m}`]:!!m},r,R,$);return k(t.createElement("div",{className:T,style:o},t.createElement(E.default,{ref:b,maxLength:c,value:w,onChange:({target:e})=>{S(e.value.replace(/[\n\r]/g,""))},onKeyDown:({keyCode:e})=>{y.current||(v.current=e)},onKeyUp:({keyCode:e,ctrlKey:t,altKey:n,metaKey:l,shiftKey:r})=>{v.current!==e||y.current||t||n||l||r||(e===x.default.ENTER?(j(),null==f||f()):e===x.default.ESC&&p())},onCompositionStart:()=>{y.current=!0},onCompositionEnd:()=>{y.current=!1},onBlur:()=>{j()},"aria-label":l,rows:1,autoSize:s}),null!==g?(0,O.cloneElement)(g,{className:`${n}-edit-content-confirm`}):null))};var R=e.i(844343),$=e.i(175066);function T(e,n){return t.useMemo(()=>{let t=!!e;return[t,Object.assign(Object.assign({},n),t&&"object"==typeof e?e:null)]},[e])}var I=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let D=t.forwardRef((e,n)=>{let{prefixCls:l,component:r="article",className:o,rootClassName:a,setContentRef:c,children:s,direction:u,style:d}=e,p=I(e,["prefixCls","component","className","rootClassName","setContentRef","children","direction","style"]),{getPrefixCls:m,direction:b,className:y,style:v}=(0,g.useComponentConfig)("typography"),h=c?(0,f.composeRef)(n,c):n,x=m("typography",l),[O,E,w]=C(x),S=(0,i.default)(x,y,{[`${x}-rtl`]:"rtl"===(null!=u?u:b)},o,a,E,w),j=Object.assign(Object.assign({},v),d);return O(t.createElement(r,Object.assign({className:S,style:j,ref:h},p),s))});var P=e.i(121229),H=e.i(190144),B=e.i(739295);function M(e){return!1===e?[!1,!1]:Array.isArray(e)?e:[e]}function z(e,t,n){return!0===e||void 0===e?t:e||n&&t}let A=e=>["string","number"].includes(typeof e),L=({prefixCls:e,copied:n,locale:l,iconOnly:r,tooltips:o,icon:a,tabIndex:c,onCopy:s,loading:u})=>{let d=M(o),p=M(a),{copied:f,copy:m}=null!=l?l:{},g=n?f:m,b=z(d[+!!n],g),v="string"==typeof b?b:g;return t.createElement(y.default,{title:b},t.createElement("button",{type:"button",className:(0,i.default)(`${e}-copy`,{[`${e}-copy-success`]:n,[`${e}-copy-icon-only`]:r}),onClick:s,"aria-label":v,tabIndex:c},n?z(p[1],t.createElement(P.default,null),!0):z(p[0],u?t.createElement(B.default,null):t.createElement(H.default,null),!0)))},W=t.forwardRef(({style:e,children:n},l)=>{let r=t.useRef(null);return t.useImperativeHandle(l,()=>({isExceed:()=>{let e=r.current;return e.scrollHeight>e.clientHeight},getHeight:()=>r.current.clientHeight})),t.createElement("span",{"aria-hidden":!0,ref:r,style:Object.assign({position:"fixed",display:"block",left:0,top:0,pointerEvents:"none",backgroundColor:"rgba(255, 0, 0, 0.65)"},e)},n)});function N(e,t){let n=0,l=[];for(let r=0;rt){let e=t-n;return l.push(String(o).slice(0,e)),l}l.push(o),n=a}return e}let U={display:"-webkit-box",overflow:"hidden",WebkitBoxOrient:"vertical"};function F(e){let{enableMeasure:l,width:r,text:o,children:a,rows:i,expanded:c,miscDeps:d,onEllipsis:p}=e,f=t.useMemo(()=>(0,s.default)(o),[o]),m=t.useMemo(()=>f.reduce((e,t)=>e+(A(t)?String(t).length:1),0),[o]),g=t.useMemo(()=>a(f,!1),[o]),[b,y]=t.useState(null),v=t.useRef(null),h=t.useRef(null),x=t.useRef(null),O=t.useRef(null),E=t.useRef(null),[w,S]=t.useState(!1),[j,C]=t.useState(0),[k,R]=t.useState(0),[$,T]=t.useState(null);(0,u.default)(()=>{l&&r&&m?C(1):C(0)},[r,o,i,l,f]),(0,u.default)(()=>{var e,t,n,l;if(1===j)C(2),T(h.current&&getComputedStyle(h.current).whiteSpace);else if(2===j){let r=!!(null==(e=x.current)?void 0:e.isExceed());C(r?3:4),y(r?[0,m]:null),S(r),R(Math.max((null==(t=x.current)?void 0:t.getHeight())||0,(1===i?0:(null==(n=O.current)?void 0:n.getHeight())||0)+((null==(l=E.current)?void 0:l.getHeight())||0))+1),p(r)}},[j]);let I=b?Math.ceil((b[0]+b[1])/2):0;(0,u.default)(()=>{var e;let[t,n]=b||[0,0];if(t!==n){let l=((null==(e=v.current)?void 0:e.getHeight())||0)>k,r=I;n-t==1&&(r=l?t:n),y(l?[t,r]:[r,n])}},[b,I]);let D=t.useMemo(()=>{if(!l)return a(f,!1);if(3!==j||!b||b[0]!==b[1]){let e=a(f,!1);return[4,0].includes(j)?e:t.createElement("span",{style:Object.assign(Object.assign({},U),{WebkitLineClamp:i})},e)}return a(c?f:N(f,b[0]),w)},[c,j,b,f].concat((0,n.default)(d))),P={width:r,margin:0,padding:0,whiteSpace:"nowrap"===$?"normal":"inherit"};return t.createElement(t.Fragment,null,D,2===j&&t.createElement(t.Fragment,null,t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:i}),ref:x},g),t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:i-1}),ref:O},g),t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:1}),ref:E},a([],!0))),3===j&&b&&b[0]!==b[1]&&t.createElement(W,{style:Object.assign(Object.assign({},P),{top:400}),ref:v},a(N(f,I),!0)),1===j&&t.createElement("span",{style:{whiteSpace:"inherit"},ref:h}))}let q=({enableEllipsis:e,isEllipsis:n,children:l,tooltipProps:r})=>(null==r?void 0:r.title)&&e?t.createElement(y.default,Object.assign({open:!!n&&void 0},r),l):l;var V=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let X=["delete","mark","code","underline","strong","keyboard","italic"],K=t.forwardRef((e,l)=>{var r;let o,v,h,{prefixCls:x,className:O,style:E,type:w,disabled:S,children:j,ellipsis:C,editable:I,copyable:P,component:H,title:B}=e,M=V(e,["prefixCls","className","style","type","disabled","children","ellipsis","editable","copyable","component","title"]),{getPrefixCls:z,direction:W}=t.useContext(g.ConfigContext),[N]=(0,b.default)("Text"),U=t.useRef(null),K=t.useRef(null),_=z("typography",x),G=(0,p.default)(M,X),[J,Q]=T(I),[Y,Z]=(0,d.default)(!1,{value:Q.editing}),{triggerType:ee=["icon"]}=Q,et=e=>{var t;e&&(null==(t=Q.onStart)||t.call(Q)),Z(e)},en=(o=(0,t.useRef)(void 0),(0,t.useEffect)(()=>{o.current=Y}),o.current);(0,u.default)(()=>{var e;!Y&&en&&(null==(e=K.current)||e.focus())},[Y]);let el=e=>{null==e||e.preventDefault(),et(!0)},[er,eo]=T(P),{copied:ea,copyLoading:ei,onClick:ec}=(({copyConfig:e,children:n})=>{let[l,r]=t.useState(!1),[o,a]=t.useState(!1),i=t.useRef(null),c=()=>{i.current&&clearTimeout(i.current)},s={};e.format&&(s.format=e.format),t.useEffect(()=>c,[]);let u=(0,$.default)(t=>{var l,o,u,d;return l=void 0,o=void 0,u=void 0,d=function*(){var l;null==t||t.preventDefault(),null==t||t.stopPropagation(),a(!0);try{let o="function"==typeof e.text?yield e.text():e.text;(0,R.default)(o||((e,t=!1)=>t&&null==e?[]:Array.isArray(e)?e:[e])(n,!0).join("")||"",s),a(!1),r(!0),c(),i.current=setTimeout(()=>{r(!1)},3e3),null==(l=e.onCopy)||l.call(e,t)}catch(e){throw a(!1),e}},new(u||(u=Promise))(function(e,t){function n(e){try{a(d.next(e))}catch(e){t(e)}}function r(e){try{a(d.throw(e))}catch(e){t(e)}}function a(t){var l;t.done?e(t.value):((l=t.value)instanceof u?l:new u(function(e){e(l)})).then(n,r)}a((d=d.apply(l,o||[])).next())})});return{copied:l,copyLoading:o,onClick:u}})({copyConfig:eo,children:j}),[es,eu]=t.useState(!1),[ed,ep]=t.useState(!1),[ef,em]=t.useState(!1),[eg,eb]=t.useState(!1),[ey,ev]=t.useState(!0),[eh,ex]=T(C,{expandable:!1,symbol:e=>e?null==N?void 0:N.collapse:null==N?void 0:N.expand}),[eO,eE]=(0,d.default)(ex.defaultExpanded||!1,{value:ex.expanded}),ew=eh&&(!eO||"collapsible"===ex.expandable),{rows:eS=1}=ex,ej=t.useMemo(()=>ew&&(void 0!==ex.suffix||ex.onEllipsis||ex.expandable||J||er),[ew,ex,J,er]);(0,u.default)(()=>{eh&&!ej&&(eu((0,m.isStyleSupport)("webkitLineClamp")),ep((0,m.isStyleSupport)("textOverflow")))},[ej,eh]);let[eC,ek]=t.useState(ew),eR=t.useMemo(()=>!ej&&(1===eS?ed:es),[ej,ed,es]);(0,u.default)(()=>{ek(eR&&ew)},[eR,ew]);let e$=ew&&(eC?eg:ef),eT=ew&&1===eS&&eC,eI=ew&&eS>1&&eC,[eD,eP]=t.useState(0),eH=e=>{var t;em(e),ef!==e&&(null==(t=ex.onEllipsis)||t.call(ex,e))};t.useEffect(()=>{let e=U.current;if(eh&&eC&&e){let t,n,l,r=(t=document.createElement("em"),e.appendChild(t),n=e.getBoundingClientRect(),l=t.getBoundingClientRect(),e.removeChild(t),n.left>l.left||l.right>n.right||n.top>l.top||l.bottom>n.bottom);eg!==r&&eb(r)}},[eh,eC,j,eI,ey,eD]),t.useEffect(()=>{let e=U.current;if("u"{ev(!!e.offsetParent)});return t.observe(e),()=>{t.disconnect()}},[eC,ew]);let eB=(v=ex.tooltip,h=Q.text,(0,t.useMemo)(()=>!0===v?{title:null!=h?h:j}:(0,t.isValidElement)(v)?{title:v}:"object"==typeof v?Object.assign({title:null!=h?h:j},v):{title:v},[v,h,j])),eM=t.useMemo(()=>{if(eh&&!eC)return[Q.text,j,B,eB.title].find(A)},[eh,eC,B,eB.title,e$]);return Y?t.createElement(k,{value:null!=(r=Q.text)?r:"string"==typeof j?j:"",onSave:e=>{var t;null==(t=Q.onChange)||t.call(Q,e),et(!1)},onCancel:()=>{var e;null==(e=Q.onCancel)||e.call(Q),et(!1)},onEnd:Q.onEnd,prefixCls:_,className:O,style:E,direction:W,component:H,maxLength:Q.maxLength,autoSize:Q.autoSize,enterIcon:Q.enterIcon}):t.createElement(c.default,{onResize:({offsetWidth:e})=>{eP(e)},disabled:!ew},r=>t.createElement(q,{tooltipProps:eB,enableEllipsis:ew,isEllipsis:e$},t.createElement(D,Object.assign({className:(0,i.default)({[`${_}-${w}`]:w,[`${_}-disabled`]:S,[`${_}-ellipsis`]:eh,[`${_}-ellipsis-single-line`]:eT,[`${_}-ellipsis-multiple-line`]:eI},O),prefixCls:x,style:Object.assign(Object.assign({},E),{WebkitLineClamp:eI?eS:void 0}),component:H,ref:(0,f.composeRef)(r,U,l),direction:W,onClick:ee.includes("text")?el:void 0,"aria-label":null==eM?void 0:eM.toString(),title:B},G),t.createElement(F,{enableMeasure:ew&&!eC,text:j,rows:eS,width:eD,onEllipsis:eH,expanded:eO,miscDeps:[ea,eO,ei,J,er,N].concat((0,n.default)(X.map(t=>e[t])))},(n,l)=>{let r;return function({mark:e,code:n,underline:l,delete:r,strong:o,keyboard:a,italic:i},c){let s=c;function u(e,n){n&&(s=t.createElement(e,{},s))}return u("strong",o),u("u",l),u("del",r),u("code",n),u("mark",e),u("kbd",a),u("i",i),s}(e,t.createElement(t.Fragment,null,n.length>0&&l&&!eO&&eM?t.createElement("span",{key:"show-content","aria-hidden":!0},n):n,[(r=l)&&!eO&&t.createElement("span",{"aria-hidden":!0,key:"ellipsis"},"..."),ex.suffix,[r&&(()=>{let{expandable:e,symbol:n}=ex;return e?t.createElement("button",{type:"button",key:"expand",className:`${_}-${eO?"collapse":"expand"}`,onClick:e=>{var t,n;eE((t={expanded:!eO}).expanded),null==(n=ex.onExpand)||n.call(ex,e,t)},"aria-label":eO?N.collapse:null==N?void 0:N.expand},"function"==typeof n?n(eO):n):null})(),(()=>{if(!J)return;let{icon:e,tooltip:n,tabIndex:l}=Q,r=(0,s.default)(n)[0]||(null==N?void 0:N.edit),o="string"==typeof r?r:"";return ee.includes("icon")?t.createElement(y.default,{key:"edit",title:!1===n?"":r},t.createElement("button",{type:"button",ref:K,className:`${_}-edit`,onClick:el,"aria-label":o,tabIndex:l},e||t.createElement(a,{role:"button"}))):null})(),er?t.createElement(L,Object.assign({key:"copy"},eo,{prefixCls:_,copied:ea,locale:N,onCopy:ec,loading:ei,iconOnly:null==j})):null]]))}))))});var _=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let G=t.forwardRef((e,n)=>{let{ellipsis:l,rel:r,children:o,navigate:a}=e,i=_(e,["ellipsis","rel","children","navigate"]),c=Object.assign(Object.assign({},i),{rel:void 0===r&&"_blank"===i.target?"noopener noreferrer":r});return t.createElement(K,Object.assign({},c,{ref:n,ellipsis:!!l,component:"a"}),o)});var J=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let Q=t.forwardRef((e,n)=>{let{children:l}=e,r=J(e,["children"]);return t.createElement(K,Object.assign({ref:n},r,{component:"div"}),l)});var Y=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let Z=t.forwardRef((e,n)=>{let{ellipsis:l,children:r}=e,o=Y(e,["ellipsis","children"]),a=t.useMemo(()=>l&&"object"==typeof l?(0,p.default)(l,["expandable","rows"]):l,[l]);return t.createElement(K,Object.assign({ref:n},o,{ellipsis:a,component:"span"}),r)});var ee=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let et=[1,2,3,4,5],en=t.forwardRef((e,n)=>{let{level:l=1,children:r}=e,o=ee(e,["level","children"]),a=et.includes(l)?`h${l}`:"h1";return t.createElement(K,Object.assign({ref:n},o,{component:a}),r)});e.s(["default",0,en],335771),D.Text=Z,D.Link=G,D.Title=en,D.Paragraph=Q,e.s(["Typography",0,D],898586)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/00-xblkiz3o8~.js b/litellm/proxy/_experimental/out/_next/static/chunks/00-xblkiz3o8~.js new file mode 100644 index 00000000000..53ea96e5a69 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/00-xblkiz3o8~.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,372024,e=>{"use strict";var t=e.i(843476),a=e.i(994388),l=e.i(304967),s=e.i(350967),r=e.i(35983),i=e.i(793130),n=e.i(197647),o=e.i(653824),c=e.i(269200),d=e.i(942232),u=e.i(977572),m=e.i(427612),h=e.i(64848),x=e.i(496020),g=e.i(881073),f=e.i(404206),p=e.i(723731),y=e.i(599724),j=e.i(779241),b=e.i(271645),C=e.i(464571),k=e.i(808613),v=e.i(311451),T=e.i(212931),w=e.i(199133),_=e.i(898586),N=e.i(727749),S=e.i(602869),E=e.i(312361),F=e.i(482725),I=e.i(536916);let{Title:P}=_.Typography,A=({accessToken:e})=>{let[s,r]=(0,b.useState)(!0),[i,n]=(0,b.useState)([]);(0,b.useEffect)(()=>{o()},[e]);let o=async()=>{if(e){r(!0);try{let t=await (0,S.getEmailEventSettings)(e);n(t.settings)}catch(e){console.error("Failed to fetch email event settings:",e),N.default.fromBackend(e)}finally{r(!1)}}},c=async()=>{if(e)try{await (0,S.updateEmailEventSettings)(e,{settings:i}),N.default.success("Email event settings updated successfully")}catch(e){console.error("Failed to update email event settings:",e),N.default.fromBackend(e)}},d=async()=>{if(e)try{await (0,S.resetEmailEventSettings)(e),N.default.success("Email event settings reset to defaults"),o()}catch(e){console.error("Failed to reset email event settings:",e),N.default.fromBackend(e)}};return(0,t.jsxs)(l.Card,{children:[(0,t.jsx)(P,{level:4,children:"Email Notifications"}),(0,t.jsx)(y.Text,{children:"Select which events should trigger email notifications."}),(0,t.jsx)(E.Divider,{}),s?(0,t.jsx)("div",{style:{textAlign:"center",padding:"20px"},children:(0,t.jsx)(F.Spin,{size:"large"})}):(0,t.jsx)("div",{className:"space-y-4",children:i.map(e=>(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)(I.Checkbox,{checked:e.enabled,onChange:t=>{var a,l;return a=e.event,l=t.target.checked,void n(i.map(e=>e.event===a?{...e,enabled:l}:e))}}),(0,t.jsxs)("div",{className:"ml-3",children:[(0,t.jsx)(y.Text,{children:e.event}),(0,t.jsx)("div",{className:"text-sm text-gray-500 block",children:(e=>{if(e.includes("Virtual Key Created"))return"An email will be sent to the user when a new virtual key is created with their user ID";{if(e.includes("New User Invitation"))return"An email will be sent to the email address of the user when a new user is created";let t=e.split(/(?=[A-Z])/).join(" ").toLowerCase();return`Receive an email notification when ${t}`}})(e.event)})]})]},e.event))}),(0,t.jsxs)("div",{className:"mt-6 flex space-x-4",children:[(0,t.jsx)(a.Button,{onClick:c,disabled:s,children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:d,variant:"secondary",disabled:s,children:"Reset to Defaults"})]})]})},{Title:B}=_.Typography,L=({accessToken:e,premiumUser:r,alerts:i})=>{let n=async()=>{if(!e)return;let t={};i.filter(e=>"email"===e.name).forEach(e=>{Object.entries(e.variables??{}).forEach(([e,a])=>{let l=document.querySelector(`input[name="${e}"]`);l&&l.value&&(t[e]=l?.value)})});try{await (0,S.setCallbacksCall)(e,{general_settings:{alerting:["email"]},environment_variables:t}),N.default.success("Email settings updated successfully")}catch(e){N.default.fromBackend(e)}};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"mt-6 mb-6",children:(0,t.jsx)(A,{accessToken:e})}),(0,t.jsxs)(l.Card,{children:[(0,t.jsx)(B,{level:4,children:"Email Server Settings"}),(0,t.jsxs)(y.Text,{children:[(0,t.jsxs)("a",{href:"https://docs.litellm.ai/docs/proxy/email",target:"_blank",style:{color:"blue"},children:[" ","LiteLLM Docs: email alerts"]})," ",(0,t.jsx)("br",{})]}),(0,t.jsx)("div",{className:"flex w-full",children:i.filter(e=>"email"===e.name).map((e,a)=>(0,t.jsx)(u.TableCell,{children:(0,t.jsx)("ul",{children:(0,t.jsx)(s.Grid,{numItems:2,children:Object.entries(e.variables??{}).map(([e,a])=>(0,t.jsxs)("li",{className:"mx-2 my-2",children:[!0!=r&&("EMAIL_LOGO_URL"===e||"EMAIL_SUPPORT_CONTACT"===e)?(0,t.jsxs)("div",{children:[(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:(0,t.jsxs)(y.Text,{className:"mt-2",children:[" ✨ ",e]})}),(0,t.jsx)(j.TextInput,{name:e,defaultValue:a,type:"password",disabled:!0,style:{width:"400px"}})]}):(0,t.jsxs)("div",{children:[(0,t.jsx)(y.Text,{className:"mt-2",children:e}),(0,t.jsx)(j.TextInput,{name:e,defaultValue:a,type:"password",style:{width:"400px"}})]}),(0,t.jsxs)("p",{style:{fontSize:"small",fontStyle:"italic"},children:["SMTP_HOST"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP host address, e.g. `smtp.resend.com`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PORT"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP port number, e.g. `587`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_USERNAME"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP username, e.g. `username`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PASSWORD"===e&&(0,t.jsx)("span",{style:{color:"red"},children:" Required * "}),"SMTP_SENDER_EMAIL"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Enter the sender email address, e.g. `sender@berri.ai`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"TEST_EMAIL_ADDRESS"===e&&(0,t.jsxs)("div",{style:{color:"gray"},children:["Email Address to send `Test Email Alert` to. example: `info@berri.ai`",(0,t.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"EMAIL_LOGO_URL"===e&&(0,t.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the Logo that appears in the email, pass a url to your logo"}),"EMAIL_SUPPORT_CONTACT"===e&&(0,t.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the support email address that appears in the email. Default is support@berri.ai"})]})]},e))})})},a))}),(0,t.jsx)(a.Button,{className:"mt-2",onClick:()=>n(),children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:async()=>{if(e)try{await (0,S.serviceHealthCheck)(e,"email"),N.default.success("Email test triggered. Check your configured email inbox/logs.")}catch(e){N.default.fromBackend(e)}},className:"mx-2",children:"Test Email Alerts"})]})]})};var z=e.i(555987),O=e.i(905536),D=e.i(28651),U=e.i(68155),Z=e.i(220508),R=e.i(389083),M=e.i(752978);let q=({alertingSettings:e,handleInputChange:l,handleResetField:s,handleSubmit:r,premiumUser:n})=>{let[o]=k.Form.useForm();return(0,t.jsxs)(k.Form,{form:o,onFinish:()=>{let e=o.getFieldsValue();Object.entries(e).every(([e,t])=>"boolean"!=typeof t&&(""===t||null==t))||r(e)},labelAlign:"left",children:[e.map((e,r)=>(0,t.jsxs)(x.TableRow,{children:[(0,t.jsxs)(u.TableCell,{align:"center",children:[(0,t.jsx)(y.Text,{children:e.field_name}),(0,t.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),e.premium_field?n?(0,t.jsx)(k.Form.Item,{name:e.field_name,children:(0,t.jsx)(u.TableCell,{children:"Integer"===e.field_type?(0,t.jsx)(D.InputNumber,{step:1,value:e.field_value,onChange:t=>l(e.field_name,t)}):"Boolean"===e.field_type?(0,t.jsx)(i.Switch,{checked:e.field_value,onChange:t=>l(e.field_name,t)}):(0,t.jsx)(v.Input,{value:e.field_value,onChange:t=>l(e.field_name,t)})})}):(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(a.Button,{className:"flex items-center justify-center",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})})}):(0,t.jsx)(k.Form.Item,{name:e.field_name,className:"mb-0",valuePropName:"Boolean"===e.field_type?"checked":"value",children:(0,t.jsx)(u.TableCell,{children:"Integer"===e.field_type?(0,t.jsx)(D.InputNumber,{step:1,value:e.field_value,onChange:t=>l(e.field_name,t),className:"p-0"}):"Boolean"===e.field_type?(0,t.jsx)(i.Switch,{checked:e.field_value,onChange:t=>{l(e.field_name,t),o.setFieldsValue({[e.field_name]:t})}}):(0,t.jsx)(v.Input,{value:e.field_value,onChange:t=>l(e.field_name,t)})})}),(0,t.jsx)(u.TableCell,{children:!0==e.stored_in_db?(0,t.jsx)(R.Badge,{icon:Z.CheckCircleIcon,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,t.jsx)(R.Badge,{className:"text-gray bg-white outline-solid",children:"In Config"}):(0,t.jsx)(R.Badge,{className:"text-gray bg-white outline-solid",children:"Not Set"})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(M.Icon,{icon:U.TrashIcon,color:"red",onClick:()=>s(e.field_name,r),children:"Reset"})})]},r)),(0,t.jsx)("div",{children:(0,t.jsx)(C.Button,{htmlType:"submit",children:"Update Settings"})})]})},$=({accessToken:e,premiumUser:a})=>{let[l,s]=(0,b.useState)([]);return(0,b.useEffect)(()=>{e&&(0,S.alertingSettingsCall)(e).then(e=>{s(e)})},[e]),(0,t.jsx)(q,{alertingSettings:l,handleInputChange:(e,t)=>{s(l.map(a=>a.field_name===e?{...a,field_value:t}:a))},handleResetField:(t,a)=>{if(e)try{let e=l.map(e=>e.field_name===t?{...e,stored_in_db:null,field_value:e.field_default_value}:e);s(e)}catch(e){}},handleSubmit:t=>{if(!e||null==t||void 0==t)return;let a={};l.forEach(e=>{a[e.field_name]=e.field_value});let{slack_alerting:s,...r}={...t,...a};try{(0,S.updateConfigFieldSetting)(e,"alerting_args",r),"boolean"==typeof s&&(!0==s?(0,S.updateConfigFieldSetting)(e,"alerting",["slack"]):(0,S.updateConfigFieldSetting)(e,"alerting",[])),N.default.success("Wait 10s for proxy to update.")}catch(e){}},premiumUser:a})};var H=e.i(954616),G=e.i(266027),K=e.i(912598),W=e.i(243652);let Q=(0,W.createQueryKeys)("cloudZeroSettings"),V=async e=>{let t=(0,S.getProxyBaseUrl)(),a=t?`${t}/cloudzero/settings`:"/cloudzero/settings",l=await fetch(a,{method:"GET",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!l.ok){let e="Failed to fetch CloudZero settings";try{let t=await l.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=l.statusText||e}throw Error(e)}let s=await l.json();return s&&(s.api_key_masked||s.connection_id)?s:null},J=async(e,t)=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/settings`:"/cloudzero/settings",s=await fetch(l,{method:"PUT",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({...t.connection_id&&{connection_id:t.connection_id},...t.timezone&&{timezone:t.timezone},...t.api_key&&{api_key:t.api_key}})});if(!s.ok){let e="Failed to update CloudZero settings";try{let t=await s.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=s.statusText||e}throw Error(e)}return await s.json()},X=async e=>{let t=(0,S.getProxyBaseUrl)(),a=t?`${t}/cloudzero/delete`:"/cloudzero/delete",l=await fetch(a,{method:"DELETE",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!l.ok){let e="Failed to delete CloudZero settings";try{let t=await l.json();"object"==typeof t&&null!==t?e=t?.error?.message||t?.error||t?.message||t?.detail||("string"==typeof t?.error?t.error:e):"string"==typeof t&&(e=t)}catch{e=l.statusText||e}throw Error(e)}return await l.json()};var Y=e.i(135214),ee=e.i(175712),et=e.i(21548);let{Title:ea,Paragraph:el}=_.Typography;function es({startCreation:e}){return(0,t.jsx)("div",{className:"bg-white p-12 rounded-lg border border-dashed border-gray-300 text-center max-w-2xl mx-auto mt-8",children:(0,t.jsx)(et.Empty,{image:et.Empty.PRESENTED_IMAGE_SIMPLE,description:(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)(ea,{level:4,children:"No CloudZero Integration Found"}),(0,t.jsx)(el,{type:"secondary",className:"max-w-md mx-auto",children:"Connect your CloudZero account to start tracking and analyzing your cloud costs directly from LiteLLM."})]}),children:(0,t.jsx)(C.Button,{type:"primary",size:"large",onClick:e,className:"flex items-center gap-2 mx-auto mt-4",children:"Add CloudZero Integration"})})})}var er=e.i(888259);let ei=async(e,t)=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/init`:"/cloudzero/init",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({connection_id:t.connection_id,timezone:t.timezone??"UTC",...t.api_key&&{api_key:t.api_key}})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to create CloudZero integration")}return await s.json()};function en({open:e,onOk:a,onCancel:l}){let s,{accessToken:r}=(0,Y.default)(),[i]=k.Form.useForm(),n=(s=r||"",(0,H.useMutation)({mutationFn:async e=>{if(!s)throw Error("Access token is required");return await ei(s,e)}}));(0,b.useEffect)(()=>{e&&i.resetFields()},[e,i]);let o=async()=>{try{let e=await i.validateFields();n.mutate({connection_id:e.connection_id,timezone:e.timezone||"UTC",...e.api_key&&{api_key:e.api_key}},{onSuccess:()=>{er.default.success("CloudZero integration created successfully"),i.resetFields(),a()},onError:e=>{e?.errorFields||er.default.error(e?.message||"Failed to create CloudZero integration")}})}catch(e){if(e?.errorFields)return;er.default.error(e?.message||"Failed to create CloudZero integration")}};return(0,t.jsx)(T.Modal,{title:"Create CloudZero Integration",open:e,onOk:o,onCancel:()=>{i.resetFields(),l()},confirmLoading:n.isPending,okText:n.isPending?"Creating...":"Create",cancelText:"Cancel",okButtonProps:{disabled:n.isPending},cancelButtonProps:{disabled:n.isPending},children:(0,t.jsxs)(k.Form,{form:i,layout:"vertical",onFinish:o,children:[(0,t.jsx)(k.Form.Item,{label:"CloudZero API Key",name:"api_key",rules:[{required:!0,message:"Please enter your CloudZero API key"}],children:(0,t.jsx)(v.Input.Password,{placeholder:"Enter your CloudZero API key"})}),(0,t.jsx)(k.Form.Item,{label:"Connection ID",name:"connection_id",rules:[{required:!0,message:"Please enter your CloudZero connection ID"}],children:(0,t.jsx)(v.Input,{placeholder:"Enter your CloudZero connection ID"})}),(0,t.jsx)(k.Form.Item,{label:"Timezone",name:"timezone",tooltip:"Timezone for date handling (defaults to UTC if not provided)",children:(0,t.jsx)(v.Input,{placeholder:"UTC"})})]})})}let eo=async(e,t={})=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/dry-run`:"/cloudzero/dry-run",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({limit:t.limit??10})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to perform dry run")}return await s.json()},ec=async(e,t={})=>{let a=(0,S.getProxyBaseUrl)(),l=a?`${a}/cloudzero/export`:"/cloudzero/export",s=await fetch(l,{method:"POST",headers:{[(0,S.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({operation:t.operation??"replace_hourly"})});if(!s.ok){let e=await s.json().catch(()=>({}));throw Error(e?.error?.message||e?.message||e?.detail||"Failed to export data")}return await s.json()};var ed=e.i(127952),eu=e.i(560445),em=e.i(869216),eh=e.i(883552),ex=e.i(262218),eg=e.i(269638),ef=e.i(688511),ep=e.i(431343),ey=e.i(727612),ej=e.i(569074);function eb({open:e,onOk:a,onCancel:l,settings:s}){var r;let i,{accessToken:n}=(0,Y.default)(),[o]=k.Form.useForm(),c=(r=n||"",i=(0,K.useQueryClient)(),(0,H.useMutation)({mutationFn:async e=>{if(!r)throw Error("Access token is required");return await J(r,e)},onSuccess:()=>{i.invalidateQueries({queryKey:Q.list({})})}}));(0,b.useEffect)(()=>{e&&s?o.setFieldsValue({connection_id:s.connection_id,timezone:s.timezone||"UTC",api_key:""}):e&&o.resetFields()},[e,s,o]);let d=async()=>{try{let e=await o.validateFields();c.mutate({connection_id:e.connection_id,timezone:e.timezone||"UTC",...e.api_key&&{api_key:e.api_key}},{onSuccess:()=>{er.default.success("CloudZero integration updated successfully"),o.resetFields(),a()},onError:e=>{e?.errorFields||er.default.error(e?.message||"Failed to update CloudZero integration")}})}catch(e){if(e?.errorFields)return;er.default.error(e?.message||"Failed to update CloudZero integration")}};return(0,t.jsx)(T.Modal,{title:"Edit CloudZero Integration",open:e,onOk:d,onCancel:()=>{o.resetFields(),l()},confirmLoading:c.isPending,okText:c.isPending?"Updating...":"Update",cancelText:"Cancel",okButtonProps:{disabled:c.isPending},cancelButtonProps:{disabled:c.isPending},children:(0,t.jsxs)(k.Form,{form:o,layout:"vertical",onFinish:d,children:[(0,t.jsx)(k.Form.Item,{label:"CloudZero API Key",name:"api_key",rules:[{required:!1,message:"Please enter your CloudZero API key"}],tooltip:"Leave empty to keep the existing API key",children:(0,t.jsx)(v.Input.Password,{placeholder:"Leave empty to keep existing"})}),(0,t.jsx)(k.Form.Item,{label:"Connection ID",name:"connection_id",rules:[{required:!0,message:"Please enter your CloudZero connection ID"}],children:(0,t.jsx)(v.Input,{placeholder:"Enter your CloudZero connection ID"})}),(0,t.jsx)(k.Form.Item,{label:"Timezone",name:"timezone",tooltip:"Timezone for date handling (defaults to UTC if not provided)",children:(0,t.jsx)(v.Input,{placeholder:"UTC"})})]})})}function eC({settings:e,onSettingsUpdated:a}){var l;let s,r,i,{accessToken:n}=(0,Y.default)(),[o,c]=(0,b.useState)(!1),[d,u]=(0,b.useState)(!1),m=(s=n||"",(0,H.useMutation)({mutationFn:async(e={})=>{if(!s)throw Error("Access token is required");return await eo(s,e)}})),h=(r=n||"",(0,H.useMutation)({mutationFn:async(e={})=>{if(!r)throw Error("Access token is required");return await ec(r,e)}})),x=(l=n||"",i=(0,K.useQueryClient)(),(0,H.useMutation)({mutationFn:async()=>{if(!l)throw Error("Access token is required");return await X(l)},onSuccess:()=>{i.invalidateQueries({queryKey:Q.list({})})}})),g=m.data?JSON.stringify(m.data,null,2):null,f=async()=>{c(!1),a()};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"space-y-6 w-full max-w-4xl mx-auto",children:(0,t.jsxs)(ee.Card,{title:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"text-lg font-semibold",children:"CloudZero Configuration"}),(0,t.jsx)(ex.Tag,{color:"success",className:"ml-2 capitalize",children:e.status||"Active"})]}),extra:(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(C.Button,{icon:(0,t.jsx)(ef.Edit,{size:16}),onClick:()=>{c(!0)},className:"flex items-center gap-2",children:"Edit"}),(0,t.jsx)(C.Button,{danger:!0,icon:(0,t.jsx)(ey.Trash2,{size:16}),onClick:()=>{u(!0)},className:"flex items-center gap-2",children:"Delete"})]}),className:"shadow-xs",children:[(0,t.jsxs)(em.Descriptions,{bordered:!0,column:{xxl:1,xl:1,lg:1,md:1,sm:1,xs:1},children:[(0,t.jsx)(em.Descriptions.Item,{label:"API Key (Redacted)",children:(0,t.jsx)("span",{className:"font-mono text-gray-600",children:e.api_key_masked||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Not configured"})})}),(0,t.jsx)(em.Descriptions.Item,{label:"Connection ID",children:(0,t.jsx)("span",{className:"font-mono text-gray-600",children:e.connection_id||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Not configured"})})}),(0,t.jsx)(em.Descriptions.Item,{label:"Timezone",children:e.timezone||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Default (UTC)"})})]}),(0,t.jsx)(E.Divider,{orientation:"left",className:"text-gray-500",children:"Actions"}),(0,t.jsxs)("div",{className:"flex flex-wrap gap-4 mb-6",children:[(0,t.jsx)(C.Button,{onClick:()=>{n&&m.mutate({limit:10},{onSuccess:e=>{er.default.success("Dry run completed successfully")},onError:e=>{er.default.error(e?.message||"Failed to perform dry run")}})},loading:m.isPending,icon:(0,t.jsx)(ep.Play,{size:16}),className:"flex items-center gap-2",children:"Run Dry Run Simulation"}),(0,t.jsx)(eh.Popconfirm,{title:"Export Data to CloudZero",description:"This will push the current accumulated cost data to CloudZero. Continue?",onConfirm:()=>{n&&h.mutate({operation:"replace_hourly"},{onSuccess:()=>{er.default.success("Data successfully exported to CloudZero")},onError:e=>{er.default.error(e?.message||"Failed to export data")}})},okText:"Export",cancelText:"Cancel",children:(0,t.jsx)(C.Button,{type:"primary",loading:h.isPending,icon:(0,t.jsx)(ej.Upload,{size:16}),className:"flex items-center gap-2",children:"Export Data Now"})})]}),g&&(0,t.jsx)("div",{className:"mt-6 animate-in fade-in slide-in-from-top-4 duration-300",children:(0,t.jsx)(eu.Alert,{message:"Dry Run Results",description:(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)("p",{className:"mb-2 text-gray-600",children:["Simulation output for connection: ",e.connection_id]}),(0,t.jsx)("pre",{className:"bg-gray-50 p-4 rounded-md border border-gray-200 overflow-x-auto text-xs font-mono text-gray-800",children:g})]}),type:"info",showIcon:!0,icon:(0,t.jsx)(eg.CheckCircle,{className:"text-blue-500"})})})]})}),(0,t.jsx)(eb,{open:o,onOk:f,onCancel:()=>{c(!1)},settings:e}),(0,t.jsx)(ed.default,{isOpen:d,title:"Delete CloudZero Integration?",message:"Are you sure you want to delete this CloudZero integration? All associated settings and configurations will be permanently removed.",resourceInformationTitle:"Integration Details",resourceInformation:[{label:"Connection ID",value:e.connection_id,code:!0},{label:"Timezone",value:e.timezone||"Default (UTC)"}],onCancel:()=>{u(!1)},onOk:()=>{n&&x.mutate(void 0,{onSuccess:()=>{er.default.success("CloudZero integration deleted successfully"),u(!1),a()},onError:e=>{er.default.error(e?.message||"Failed to delete CloudZero integration")}})},confirmLoading:x.isPending})]})}function ek(){let{accessToken:e}=(0,Y.default)(),{data:a,isLoading:l,error:s}=(0,G.useQuery)({queryKey:Q.list({}),queryFn:async()=>await V(e),enabled:!!e,staleTime:36e5,gcTime:36e5}),r=(0,K.useQueryClient)(),i=(0,W.createQueryKeys)("cloudZeroSettings"),[n,o]=(0,b.useState)(!1),c=async()=>{o(!1),await r.invalidateQueries({queryKey:i.list({})})};return l?(0,t.jsx)(ee.Card,{children:(0,t.jsx)(_.Typography.Text,{children:"Loading CloudZero settings..."})}):s?(0,t.jsx)(ee.Card,{children:(0,t.jsxs)(_.Typography.Text,{className:"text-red-600",children:["Error loading CloudZero settings: ",s instanceof Error?s.message:String(s)]})}):a?(0,t.jsx)(t.Fragment,{children:(0,t.jsx)(eC,{settings:a,onSettingsUpdated:c})}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(es,{startCreation:()=>o(!0)}),(0,t.jsx)(en,{open:n,onOk:c,onCancel:()=>{o(!1)}})]})}var ev=e.i(291542),eT=e.i(335771);e.i(622826);var ew=e.i(112179),e_=e.i(902555);let eN=[{value:"success",label:"Success"},{value:"failure",label:"Failure"},{value:"success_and_failure",label:"Success & Failure"}],eS=({callbacks:e,availableCallbacks:l={},onTest:s=()=>{},onEdit:r=()=>{},onDelete:i=()=>{},onAdd:n=()=>{}})=>{let o=[{title:(0,t.jsx)("span",{className:"font-medium text-gray-700",children:"Callback Name"}),dataIndex:"name",key:"name",render:(e,a)=>{let s=a.name,r=l[s]?.ui_callback_name||s;return(0,t.jsx)("div",{className:"font-medium text-gray-800",children:r})}},{title:(0,t.jsx)("span",{className:"font-medium text-gray-700",children:"Mode"}),key:"mode",render:(e,a)=>{let l=a.type||a.mode||"success",s=eN.find(e=>e.value===l)?.label||l;return(0,t.jsx)(ew.StatusBadge,{tone:"success"===l?"success":"failure"===l?"error":"info",label:s})},width:240},{title:(0,t.jsx)("span",{className:"font-medium text-gray-700 text-right w-full block",children:"Actions"}),key:"actions",align:"right",render:(e,a)=>(0,t.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,t.jsx)(e_.default,{variant:"Test",tooltipText:"Test Callback",onClick:()=>s(a)}),(0,t.jsx)(e_.default,{variant:"Edit",tooltipText:"Edit Callback",onClick:()=>r(a)}),(0,t.jsx)(e_.default,{variant:"Delete",tooltipText:"Delete Callback",onClick:()=>i(a)})]}),width:240}];return(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)("div",{className:"w-full mt-4",children:[(0,t.jsx)(a.Button,{onClick:n,className:"mx-auto",children:"+ Add Callback"}),(0,t.jsx)("div",{className:"flex justify-between items-center my-2",children:(0,t.jsx)(eT.default,{level:4,children:"Active Logging Callbacks"})}),0===e.length?(0,t.jsx)("div",{className:"flex flex-col items-center justify-center p-8 bg-gray-50 border border-gray-200 rounded-lg",children:(0,t.jsxs)("div",{className:"text-center",children:[(0,t.jsx)("h3",{className:"text-lg font-medium text-gray-700 mb-2",children:"No callbacks configured"}),(0,t.jsx)("p",{className:"text-gray-500",children:"Add your first callback to start logging data to external services."})]})}):(0,t.jsx)("div",{className:"bg-white border border-gray-200 rounded-lg overflow-hidden",children:(0,t.jsx)(ev.Table,{columns:o,dataSource:e,rowKey:e=>`${e.name}-${e.type||e.mode||"success"}`,pagination:!1,rowClassName:()=>"hover:bg-gray-50"})})]})})};var eE=e.i(190702);let{Title:eF,Paragraph:eI}=_.Typography,eP=({params:e,callbackConfigs:a,selectedCallback:l})=>e&&0!==e.length?(0,t.jsx)("div",{className:"space-y-4 mt-6 p-4 bg-gray-50 rounded-lg border",children:e.map(e=>{let s=a.find(e=>e.id===l),r=s?.dynamic_params?.[e]||{},i=r.type||"text",n=r.ui_name||e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase()),o=r.required||!1;return(0,t.jsx)(O.default,{label:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:[n," "]}),name:e,className:"mb-4",rules:o?[{required:!0,message:`Please enter the ${n.toLowerCase()}`}]:void 0,children:"password"===i?(0,t.jsx)(v.Input.Password,{size:"large",placeholder:`Enter your ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500"}):"number"===i?(0,t.jsx)(v.Input,{type:"number",size:"large",placeholder:`Enter ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500",min:0,max:1,step:.1}):(0,t.jsx)(v.Input,{size:"large",placeholder:`Enter your ${n.toLowerCase()}`,className:"w-full rounded-md border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500"})},e)})}):null,eA=({callbackConfigs:e,selectedCallback:a,onCallbackChange:l,disabled:s=!1})=>(0,t.jsx)(O.default,{label:"Callback",name:"callback",rules:s?void 0:[{required:!0,message:"Please select a callback"}],children:(0,t.jsx)(w.Select,{placeholder:"Choose a logging callback...",size:"large",className:"w-full",showSearch:!0,disabled:s,value:a,filterOption:(e,t)=>(t?.value?.toString()??"").toLowerCase().includes(e.toLowerCase()),onChange:l,children:e.map(e=>{let a=e.logo,l=(0,z.resolveLogoSrc)(a&&(a.includes("/")||a.startsWith("data:")||a.startsWith("http"))?a:`/ui/assets/logos/${a}`);return(0,t.jsx)(r.SelectItem,{value:e.id,children:(0,t.jsxs)("div",{className:"flex items-center space-x-3 py-1",children:[(0,t.jsx)("div",{className:"w-6 h-6 flex items-center justify-center",children:(0,t.jsx)("img",{src:l,alt:`${e.displayName} logo`,className:"w-6 h-6 rounded-sm object-contain",onError:e=>{e.currentTarget.style.display="none"}})}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:e.displayName})]})},e.id)})})}),eB=(e,t,a)=>{if(!e)return a?Object.keys(a):[];let l=t.find(t=>t.id===e);return l?.dynamic_params?Object.keys(l.dynamic_params):a?Object.keys(a):[]},eL=({accessToken:e,userRole:r,userID:v,premiumUser:w})=>{let[_,E]=(0,b.useState)([]),[F,I]=(0,b.useState)([]),[P,A]=(0,b.useState)(!1),[B]=k.Form.useForm(),[z]=k.Form.useForm(),[O,D]=(0,b.useState)(null),[U,Z]=(0,b.useState)(""),[R,M]=(0,b.useState)({}),[q,H]=(0,b.useState)([]),[G,K]=(0,b.useState)(!1),[W,Q]=(0,b.useState)([]),[V,J]=(0,b.useState)({}),[X,Y]=(0,b.useState)([]),[ee,et]=(0,b.useState)(!1),[ea,el]=(0,b.useState)(null),[es,er]=(0,b.useState)(!1),[ei,en]=(0,b.useState)(null),[eo,ec]=(0,b.useState)(!1),[eu,em]=(0,b.useState)(!1),[eh,ex]=(0,b.useState)(!1);(0,b.useEffect)(()=>{e&&(0,S.getCallbackConfigsCall)(e).then(e=>{Q(e||[])}).catch(e=>{N.default.fromBackend("Failed to load callback configs: "+(0,eE.parseErrorMessage)(e))})},[e]),(0,b.useEffect)(()=>{if(ee&&ea){let e=Object.fromEntries(Object.entries(ea.variables||{}).map(([e,t])=>[e,t??""]));z.setFieldsValue({...e,callback:ea.name})}},[ee,ea,z]);let eg=e=>{q.includes(e)?H(q.filter(t=>t!==e)):H([...q,e])},ef={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)",db_exceptions:"Database Exceptions (Read/Write)",daily_reports:"Weekly/Monthly Spend Reports",outage_alerts:"Outage Alerts",region_outage_alerts:"Region Outage Alerts"};(0,b.useEffect)(()=>{e&&r&&v&&(0,S.getCallbacksCall)(e,v,r).then(e=>{E(e.callbacks),J(e.available_callbacks);let t=e.alerts;if(t&&t.length>0){let e=t[0],a=e.variables.SLACK_WEBHOOK_URL;H(e.active_alerts),Z(a),M(e.alerts_to_webhook)}I(t)})},[e,r,v]);let ep=e=>q&&q.includes(e),ey=async(t,a,l)=>{if(e){l?ec(!0):em(!0);try{if(await (0,S.setCallbacksCall)(e,{environment_variables:t,litellm_settings:{success_callback:[a]}}),N.default.success(l?"Callback updated successfully":`Callback ${a} added successfully`),l?(et(!1),z.resetFields(),el(null)):(K(!1),B.resetFields(),D(null),Y([])),v&&r){let t=await (0,S.getCallbacksCall)(e,v,r);E(t.callbacks)}}catch(e){N.default.fromBackend(e)}finally{l?ec(!1):em(!1)}}},ej=async e=>{ea&&await ey(e,ea.name,!0)},eb=async e=>{let t=e?.callback;t&&await ey(e,t,!1)},eC=async()=>{if(!e)return;let t={};Object.entries(ef).forEach(([e,a])=>{let l=document.querySelector(`input[name="${e}"]`),s=l?.value||"";t[e]=s});try{await (0,S.setCallbacksCall)(e,{general_settings:{alert_to_webhook_url:t,alert_types:q}})}catch(e){N.default.fromBackend(e)}N.default.success("Alerts updated successfully")},ev=async()=>{if(ei&&e)try{if(ex(!0),await (0,S.deleteCallback)(e,ei.name),N.default.success(`Callback ${ei.name} deleted successfully`),v&&r){let t=await (0,S.getCallbacksCall)(e,v,r);E(t.callbacks)}er(!1),en(null)}catch(e){console.error("Failed to delete callback:",e),N.default.fromBackend(e)}finally{ex(!1)}};return e?(0,t.jsxs)("div",{className:"w-full mx-4",children:[(0,t.jsx)(s.Grid,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,t.jsxs)(o.TabGroup,{children:[(0,t.jsxs)(g.TabList,{variant:"line",defaultValue:"1",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Logging Callbacks"}),(0,t.jsx)(n.Tab,{value:"2",children:"CloudZero Cost Tracking"}),(0,t.jsx)(n.Tab,{value:"2",children:"Alerting Types"}),(0,t.jsx)(n.Tab,{value:"3",children:"Alerting Settings"}),(0,t.jsx)(n.Tab,{value:"4",children:"Email Alerts"})]}),(0,t.jsxs)(p.TabPanels,{children:[(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)(eS,{callbacks:_,availableCallbacks:V,onAdd:()=>K(!0),onEdit:e=>{el(e),et(!0)},onDelete:e=>{en(e),er(!0)},onTest:async t=>{try{await (0,S.serviceHealthCheck)(e,t.name),N.default.success("Health check triggered")}catch(e){N.default.fromBackend((0,eE.parseErrorMessage)(e))}}})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)("div",{className:"p-8",children:(0,t.jsx)(ek,{})})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsxs)(l.Card,{children:[(0,t.jsxs)(y.Text,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from"," ",(0,t.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,t.jsxs)(c.Table,{children:[(0,t.jsx)(m.TableHead,{children:(0,t.jsxs)(x.TableRow,{children:[(0,t.jsx)(h.TableHeaderCell,{}),(0,t.jsx)(h.TableHeaderCell,{}),(0,t.jsx)(h.TableHeaderCell,{children:"Slack Webhook URL"})]})}),(0,t.jsx)(d.TableBody,{children:Object.entries(ef).map(([e,l],s)=>(0,t.jsxs)(x.TableRow,{children:[(0,t.jsx)(u.TableCell,{children:"region_outage_alerts"==e?w?(0,t.jsx)(i.Switch,{id:"switch",name:"switch",checked:ep(e),onChange:()=>eg(e)}):(0,t.jsx)(a.Button,{className:"flex items-center justify-center",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})}):(0,t.jsx)(i.Switch,{id:"switch",name:"switch",checked:ep(e),onChange:()=>eg(e)})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(y.Text,{children:l})}),(0,t.jsx)(u.TableCell,{children:(0,t.jsx)(j.TextInput,{name:e,type:"password",defaultValue:R&&R[e]?R[e]:U})})]},s))})]}),(0,t.jsx)(a.Button,{size:"xs",className:"mt-2",onClick:eC,children:"Save Changes"}),(0,t.jsx)(a.Button,{onClick:async()=>{try{await (0,S.serviceHealthCheck)(e,"slack"),N.default.success("Alert test triggered. Test request to slack made - check logs/alerts on slack to verify")}catch(e){N.default.fromBackend((0,eE.parseErrorMessage)(e))}},className:"mx-2",children:"Test Alerts"})]})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)($,{accessToken:e,premiumUser:w})}),(0,t.jsx)(f.TabPanel,{children:(0,t.jsx)(L,{accessToken:e,premiumUser:w,alerts:F})})]})]})}),(0,t.jsxs)(T.Modal,{title:"Add Logging Callback",open:G,width:800,onCancel:()=>{K(!1),D(null),Y([])},footer:null,children:[(0,t.jsxs)("a",{href:"https://docs.litellm.ai/docs/proxy/logging",className:"mb-8 mt-4",target:"_blank",style:{color:"blue"},children:[" ","LiteLLM Docs: Logging"]}),(0,t.jsxs)(k.Form,{form:B,onFinish:eb,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsx)(eA,{callbackConfigs:W,selectedCallback:O,onCallbackChange:e=>{D(e),Y(eB(e,W))}}),(0,t.jsx)(eP,{params:X,callbackConfigs:W,selectedCallback:O}),(0,t.jsxs)("div",{className:"flex justify-end space-x-3 pt-6 mt-6 border-t border-gray-200",children:[(0,t.jsx)(C.Button,{onClick:()=>{K(!1),D(null),Y([]),B.resetFields()},disabled:eu,children:"Cancel"}),(0,t.jsx)(C.Button,{htmlType:"submit",loading:eu,disabled:eu,children:eu?"Adding...":"Add Callback"})]})]})]}),(0,t.jsx)(T.Modal,{open:ee,width:800,title:"Edit Callback Settings",onCancel:()=>{et(!1),el(null),z.resetFields()},footer:null,children:(0,t.jsxs)(k.Form,{form:z,onFinish:ej,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[ea&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(eA,{callbackConfigs:W,selectedCallback:ea.name,onCallbackChange:()=>{},disabled:!0}),(0,t.jsx)(eP,{params:eB(ea.name,W,ea.variables),callbackConfigs:W,selectedCallback:ea.name})]}),(0,t.jsxs)("div",{className:"flex justify-end space-x-3 pt-6 mt-6 border-t border-gray-200",children:[(0,t.jsx)(C.Button,{onClick:()=>{et(!1),el(null),z.resetFields()},disabled:eo,children:"Cancel"}),(0,t.jsx)(C.Button,{onClick:()=>{z.submit()},loading:eo,disabled:eo,children:eo?"Saving...":"Save Changes"})]})]})}),(0,t.jsx)(ed.default,{isOpen:es,title:"Delete Callback",message:"Are you sure you want to delete this callback? This action cannot be undone.",resourceInformationTitle:"Callback Information",resourceInformation:[{label:"Callback Name",value:ei?.name},{label:"Mode",value:ei?.mode||"success"}],onCancel:()=>{er(!1),en(null)},onOk:ev,confirmLoading:eh})]}):null};e.s(["default",0,function(){let{accessToken:e,userRole:a,userId:l,premiumUser:s}=(0,Y.default)();return(0,t.jsx)(eL,{userID:l,userRole:a,accessToken:e,premiumUser:s})}],372024)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/003_1s9xbht43.js b/litellm/proxy/_experimental/out/_next/static/chunks/003_1s9xbht43.js new file mode 100644 index 00000000000..74f24e425e0 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/003_1s9xbht43.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,115504,207670,e=>{"use strict";function r(){for(var e,r,o=0,t="",l=arguments.length;o"boolean"==typeof e?`${e}`:0===e?"0":e,t=e=>{let t=function(){for(var o,t,l=arguments.length,a=Array(l),n=0;n{let o=Object.fromEntries(Object.entries(e||{}).filter(e=>{let[r]=e;return!["class","className"].includes(r)}));return t(r.map(e=>e(o)),null==e?void 0:e.class,null==e?void 0:e.className)}},cva:e=>r=>{var l;if((null==e?void 0:e.variants)==null)return t(null==e?void 0:e.base,null==r?void 0:r.class,null==r?void 0:r.className);let{variants:a,defaultVariants:n}=e,s=Object.keys(a).map(e=>{let t=null==r?void 0:r[e],l=null==n?void 0:n[e],s=o(t)||o(l);return a[e][s]}),i={...n,...r&&Object.entries(r).reduce((e,r)=>{let[o,t]=r;return void 0===t?e:{...e,[o]:t}},{})},d=null==e||null==(l=e.compoundVariants)?void 0:l.reduce((e,r)=>{let{class:o,className:t,...l}=r;return Object.entries(l).every(e=>{let[r,o]=e,t=i[r];return Array.isArray(o)?o.includes(t):t===o})?[...e,o,t]:e},[]);return t(null==e?void 0:e.base,s,d,null==r?void 0:r.class,null==r?void 0:r.className)},cx:t}},{compose:l,cva:a,cx:n}=t(),s=(e=new Map,r=null,o)=>({nextPart:e,validators:r,classGroupId:o}),i=[],d=(e,r,o)=>{if(0==e.length-r)return o.classGroupId;let t=e[r],l=o.nextPart.get(t);if(l){let o=d(e,r+1,l);if(o)return o}let a=o.validators;if(null===a)return;let n=0===r?e.join("-"):e.slice(r).join("-"),s=a.length;for(let e=0;e{let o=s();for(let t in e)m(e[t],o,t,r);return o},m=(e,r,o,t)=>{let l=e.length;for(let a=0;a{"string"==typeof e?u(e,r,o):"function"==typeof e?b(e,r,o,t):f(e,r,o,t)},u=(e,r,o)=>{(""===e?r:g(r,e)).classGroupId=o},b=(e,r,o,t)=>{h(e)?m(e(t),r,o,t):(null===r.validators&&(r.validators=[]),r.validators.push({classGroupId:o,validator:e}))},f=(e,r,o,t)=>{let l=Object.entries(e),a=l.length;for(let e=0;e{let o=e,t=r.split("-"),l=t.length;for(let e=0;e"isThemeGetter"in e&&!0===e.isThemeGetter,k=[],x=(e,r,o,t,l)=>({modifiers:e,hasImportantModifier:r,baseClassName:o,maybePostfixModifierPosition:t,isExternal:l}),v=/\s+/,w=e=>{let r;if("string"==typeof e)return e;let o="";for(let t=0;t{let r=r=>r[e]||y;return r.isThemeGetter=!0,r},j=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,O=/^\((?:(\w[\w-]*):)?(.+)\)$/i,N=/^\d+\/\d+$/,C=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,G=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,A=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,$=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,I=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,T=e=>N.test(e),M=e=>!!e&&!Number.isNaN(Number(e)),W=e=>!!e&&Number.isInteger(Number(e)),P=e=>e.endsWith("%")&&M(e.slice(0,-1)),S=e=>C.test(e),q=()=>!0,B=e=>G.test(e)&&!A.test(e),E=()=>!1,K=e=>$.test(e),R=e=>I.test(e),U=e=>!V(e)&&!Q(e),_=e=>et(e,es,E),V=e=>j.test(e),D=e=>et(e,ei,B),F=e=>et(e,ed,M),H=e=>et(e,ea,E),J=e=>et(e,en,R),L=e=>et(e,em,K),Q=e=>O.test(e),X=e=>el(e,ei),Y=e=>el(e,ec),Z=e=>el(e,ea),ee=e=>el(e,es),er=e=>el(e,en),eo=e=>el(e,em,!0),et=(e,r,o)=>{let t=j.exec(e);return!!t&&(t[1]?r(t[1]):o(t[2]))},el=(e,r,o=!1)=>{let t=O.exec(e);return!!t&&(t[1]?r(t[1]):o)},ea=e=>"position"===e||"percentage"===e,en=e=>"image"===e||"url"===e,es=e=>"length"===e||"size"===e||"bg-size"===e,ei=e=>"length"===e,ed=e=>"number"===e,ec=e=>"family-name"===e,em=e=>"shadow"===e,ep=((e,...r)=>{let o,t,l,a,n=e=>{let r=t(e);if(r)return r;let a=((e,r)=>{let{parseClassName:o,getClassGroupId:t,getConflictingClassGroupIds:l,sortModifiers:a}=r,n=[],s=e.trim().split(v),i="";for(let e=s.length-1;e>=0;e-=1){let r=s[e],{isExternal:d,modifiers:c,hasImportantModifier:m,baseClassName:p,maybePostfixModifierPosition:u}=o(r);if(d){i=r+(i.length>0?" "+i:i);continue}let b=!!u,f=t(b?p.substring(0,u):p);if(!f){if(!b||!(f=t(p))){i=r+(i.length>0?" "+i:i);continue}b=!1}let g=0===c.length?"":1===c.length?c[0]:a(c).join(":"),h=m?g+"!":g,k=h+f;if(n.indexOf(k)>-1)continue;n.push(k);let x=l(f,b);for(let e=0;e0?" "+i:i)}return i})(e,o);return l(e,a),a};return a=s=>{var m;let p;return t=(o={cache:(e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let r=0,o=Object.create(null),t=Object.create(null),l=(l,a)=>{o[l]=a,++r>e&&(r=0,t=o,o=Object.create(null))};return{get(e){let r=o[e];return void 0!==r?r:void 0!==(r=t[e])?(l(e,r),r):void 0},set(e,r){e in o?o[e]=r:l(e,r)}}})((m=r.reduce((e,r)=>r(e),e())).cacheSize),parseClassName:(e=>{let{prefix:r,experimentalParseClassName:o}=e,t=e=>{let r,o=[],t=0,l=0,a=0,n=e.length;for(let s=0;sa?r-a:void 0)};if(r){let e=r+":",o=t;t=r=>r.startsWith(e)?o(r.slice(e.length)):x(k,!1,r,void 0,!0)}if(o){let e=t;t=r=>o({className:r,parseClassName:e})}return t})(m),sortModifiers:(p=new Map,m.orderSensitiveModifiers.forEach((e,r)=>{p.set(e,1e6+r)}),e=>{let r=[],o=[];for(let t=0;t0&&(o.sort(),r.push(...o),o=[]),r.push(l)):o.push(l)}return o.length>0&&(o.sort(),r.push(...o)),r}),...(e=>{let r=(e=>{let{theme:r,classGroups:o}=e;return c(o,r)})(e),{conflictingClassGroups:o,conflictingClassGroupModifiers:t}=e;return{getClassGroupId:e=>{if(e.startsWith("[")&&e.endsWith("]")){var o;let r,t,l;return -1===(o=e).slice(1,-1).indexOf(":")?void 0:(t=(r=o.slice(1,-1)).indexOf(":"),(l=r.slice(0,t))?"arbitrary.."+l:void 0)}let t=e.split("-"),l=+(""===t[0]&&t.length>1);return d(t,l,r)},getConflictingClassGroupIds:(e,r)=>{if(r){let r=t[e],l=o[e];if(r){if(l){let e=Array(l.length+r.length);for(let r=0;ra(((...e)=>{let r,o,t=0,l="";for(;t{let e=z("color"),r=z("font"),o=z("text"),t=z("font-weight"),l=z("tracking"),a=z("leading"),n=z("breakpoint"),s=z("container"),i=z("spacing"),d=z("radius"),c=z("shadow"),m=z("inset-shadow"),p=z("text-shadow"),u=z("drop-shadow"),b=z("blur"),f=z("perspective"),g=z("aspect"),h=z("ease"),k=z("animate"),x=()=>["auto","avoid","all","avoid-page","page","left","right","column"],v=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],w=()=>[...v(),Q,V],y=()=>["auto","hidden","clip","visible","scroll"],j=()=>["auto","contain","none"],O=()=>[Q,V,i],N=()=>[T,"full","auto",...O()],C=()=>[W,"none","subgrid",Q,V],G=()=>["auto",{span:["full",W,Q,V]},W,Q,V],A=()=>[W,"auto",Q,V],$=()=>["auto","min","max","fr",Q,V],I=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],B=()=>["start","end","center","stretch","center-safe","end-safe"],E=()=>["auto",...O()],K=()=>[T,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...O()],R=()=>[e,Q,V],et=()=>[...v(),Z,H,{position:[Q,V]}],el=()=>["no-repeat",{repeat:["","x","y","space","round"]}],ea=()=>["auto","cover","contain",ee,_,{size:[Q,V]}],en=()=>[P,X,D],es=()=>["","none","full",d,Q,V],ei=()=>["",M,X,D],ed=()=>["solid","dashed","dotted","double"],ec=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],em=()=>[M,P,Z,H],ep=()=>["","none",b,Q,V],eu=()=>["none",M,Q,V],eb=()=>["none",M,Q,V],ef=()=>[M,Q,V],eg=()=>[T,"full",...O()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[S],breakpoint:[S],color:[q],container:[S],"drop-shadow":[S],ease:["in","out","in-out"],font:[U],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[S],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[S],shadow:[S],spacing:["px",M],text:[S],"text-shadow":[S],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",T,V,Q,g]}],container:["container"],columns:[{columns:[M,V,Q,s]}],"break-after":[{"break-after":x()}],"break-before":[{"break-before":x()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:w()}],overflow:[{overflow:y()}],"overflow-x":[{"overflow-x":y()}],"overflow-y":[{"overflow-y":y()}],overscroll:[{overscroll:j()}],"overscroll-x":[{"overscroll-x":j()}],"overscroll-y":[{"overscroll-y":j()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:N()}],"inset-x":[{"inset-x":N()}],"inset-y":[{"inset-y":N()}],start:[{start:N()}],end:[{end:N()}],top:[{top:N()}],right:[{right:N()}],bottom:[{bottom:N()}],left:[{left:N()}],visibility:["visible","invisible","collapse"],z:[{z:[W,"auto",Q,V]}],basis:[{basis:[T,"full","auto",s,...O()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[M,T,"auto","initial","none",V]}],grow:[{grow:["",M,Q,V]}],shrink:[{shrink:["",M,Q,V]}],order:[{order:[W,"first","last","none",Q,V]}],"grid-cols":[{"grid-cols":C()}],"col-start-end":[{col:G()}],"col-start":[{"col-start":A()}],"col-end":[{"col-end":A()}],"grid-rows":[{"grid-rows":C()}],"row-start-end":[{row:G()}],"row-start":[{"row-start":A()}],"row-end":[{"row-end":A()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":$()}],"auto-rows":[{"auto-rows":$()}],gap:[{gap:O()}],"gap-x":[{"gap-x":O()}],"gap-y":[{"gap-y":O()}],"justify-content":[{justify:[...I(),"normal"]}],"justify-items":[{"justify-items":[...B(),"normal"]}],"justify-self":[{"justify-self":["auto",...B()]}],"align-content":[{content:["normal",...I()]}],"align-items":[{items:[...B(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...B(),{baseline:["","last"]}]}],"place-content":[{"place-content":I()}],"place-items":[{"place-items":[...B(),"baseline"]}],"place-self":[{"place-self":["auto",...B()]}],p:[{p:O()}],px:[{px:O()}],py:[{py:O()}],ps:[{ps:O()}],pe:[{pe:O()}],pt:[{pt:O()}],pr:[{pr:O()}],pb:[{pb:O()}],pl:[{pl:O()}],m:[{m:E()}],mx:[{mx:E()}],my:[{my:E()}],ms:[{ms:E()}],me:[{me:E()}],mt:[{mt:E()}],mr:[{mr:E()}],mb:[{mb:E()}],ml:[{ml:E()}],"space-x":[{"space-x":O()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":O()}],"space-y-reverse":["space-y-reverse"],size:[{size:K()}],w:[{w:[s,"screen",...K()]}],"min-w":[{"min-w":[s,"screen","none",...K()]}],"max-w":[{"max-w":[s,"screen","none","prose",{screen:[n]},...K()]}],h:[{h:["screen","lh",...K()]}],"min-h":[{"min-h":["screen","lh","none",...K()]}],"max-h":[{"max-h":["screen","lh",...K()]}],"font-size":[{text:["base",o,X,D]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[t,Q,F]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",P,V]}],"font-family":[{font:[Y,V,r]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[l,Q,V]}],"line-clamp":[{"line-clamp":[M,"none",Q,F]}],leading:[{leading:[a,...O()]}],"list-image":[{"list-image":["none",Q,V]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",Q,V]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:R()}],"text-color":[{text:R()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...ed(),"wavy"]}],"text-decoration-thickness":[{decoration:[M,"from-font","auto",Q,D]}],"text-decoration-color":[{decoration:R()}],"underline-offset":[{"underline-offset":[M,"auto",Q,V]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:O()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",Q,V]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",Q,V]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:et()}],"bg-repeat":[{bg:el()}],"bg-size":[{bg:ea()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},W,Q,V],radial:["",Q,V],conic:[W,Q,V]},er,J]}],"bg-color":[{bg:R()}],"gradient-from-pos":[{from:en()}],"gradient-via-pos":[{via:en()}],"gradient-to-pos":[{to:en()}],"gradient-from":[{from:R()}],"gradient-via":[{via:R()}],"gradient-to":[{to:R()}],rounded:[{rounded:es()}],"rounded-s":[{"rounded-s":es()}],"rounded-e":[{"rounded-e":es()}],"rounded-t":[{"rounded-t":es()}],"rounded-r":[{"rounded-r":es()}],"rounded-b":[{"rounded-b":es()}],"rounded-l":[{"rounded-l":es()}],"rounded-ss":[{"rounded-ss":es()}],"rounded-se":[{"rounded-se":es()}],"rounded-ee":[{"rounded-ee":es()}],"rounded-es":[{"rounded-es":es()}],"rounded-tl":[{"rounded-tl":es()}],"rounded-tr":[{"rounded-tr":es()}],"rounded-br":[{"rounded-br":es()}],"rounded-bl":[{"rounded-bl":es()}],"border-w":[{border:ei()}],"border-w-x":[{"border-x":ei()}],"border-w-y":[{"border-y":ei()}],"border-w-s":[{"border-s":ei()}],"border-w-e":[{"border-e":ei()}],"border-w-t":[{"border-t":ei()}],"border-w-r":[{"border-r":ei()}],"border-w-b":[{"border-b":ei()}],"border-w-l":[{"border-l":ei()}],"divide-x":[{"divide-x":ei()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":ei()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...ed(),"hidden","none"]}],"divide-style":[{divide:[...ed(),"hidden","none"]}],"border-color":[{border:R()}],"border-color-x":[{"border-x":R()}],"border-color-y":[{"border-y":R()}],"border-color-s":[{"border-s":R()}],"border-color-e":[{"border-e":R()}],"border-color-t":[{"border-t":R()}],"border-color-r":[{"border-r":R()}],"border-color-b":[{"border-b":R()}],"border-color-l":[{"border-l":R()}],"divide-color":[{divide:R()}],"outline-style":[{outline:[...ed(),"none","hidden"]}],"outline-offset":[{"outline-offset":[M,Q,V]}],"outline-w":[{outline:["",M,X,D]}],"outline-color":[{outline:R()}],shadow:[{shadow:["","none",c,eo,L]}],"shadow-color":[{shadow:R()}],"inset-shadow":[{"inset-shadow":["none",m,eo,L]}],"inset-shadow-color":[{"inset-shadow":R()}],"ring-w":[{ring:ei()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:R()}],"ring-offset-w":[{"ring-offset":[M,D]}],"ring-offset-color":[{"ring-offset":R()}],"inset-ring-w":[{"inset-ring":ei()}],"inset-ring-color":[{"inset-ring":R()}],"text-shadow":[{"text-shadow":["none",p,eo,L]}],"text-shadow-color":[{"text-shadow":R()}],opacity:[{opacity:[M,Q,V]}],"mix-blend":[{"mix-blend":[...ec(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":ec()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[M]}],"mask-image-linear-from-pos":[{"mask-linear-from":em()}],"mask-image-linear-to-pos":[{"mask-linear-to":em()}],"mask-image-linear-from-color":[{"mask-linear-from":R()}],"mask-image-linear-to-color":[{"mask-linear-to":R()}],"mask-image-t-from-pos":[{"mask-t-from":em()}],"mask-image-t-to-pos":[{"mask-t-to":em()}],"mask-image-t-from-color":[{"mask-t-from":R()}],"mask-image-t-to-color":[{"mask-t-to":R()}],"mask-image-r-from-pos":[{"mask-r-from":em()}],"mask-image-r-to-pos":[{"mask-r-to":em()}],"mask-image-r-from-color":[{"mask-r-from":R()}],"mask-image-r-to-color":[{"mask-r-to":R()}],"mask-image-b-from-pos":[{"mask-b-from":em()}],"mask-image-b-to-pos":[{"mask-b-to":em()}],"mask-image-b-from-color":[{"mask-b-from":R()}],"mask-image-b-to-color":[{"mask-b-to":R()}],"mask-image-l-from-pos":[{"mask-l-from":em()}],"mask-image-l-to-pos":[{"mask-l-to":em()}],"mask-image-l-from-color":[{"mask-l-from":R()}],"mask-image-l-to-color":[{"mask-l-to":R()}],"mask-image-x-from-pos":[{"mask-x-from":em()}],"mask-image-x-to-pos":[{"mask-x-to":em()}],"mask-image-x-from-color":[{"mask-x-from":R()}],"mask-image-x-to-color":[{"mask-x-to":R()}],"mask-image-y-from-pos":[{"mask-y-from":em()}],"mask-image-y-to-pos":[{"mask-y-to":em()}],"mask-image-y-from-color":[{"mask-y-from":R()}],"mask-image-y-to-color":[{"mask-y-to":R()}],"mask-image-radial":[{"mask-radial":[Q,V]}],"mask-image-radial-from-pos":[{"mask-radial-from":em()}],"mask-image-radial-to-pos":[{"mask-radial-to":em()}],"mask-image-radial-from-color":[{"mask-radial-from":R()}],"mask-image-radial-to-color":[{"mask-radial-to":R()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":v()}],"mask-image-conic-pos":[{"mask-conic":[M]}],"mask-image-conic-from-pos":[{"mask-conic-from":em()}],"mask-image-conic-to-pos":[{"mask-conic-to":em()}],"mask-image-conic-from-color":[{"mask-conic-from":R()}],"mask-image-conic-to-color":[{"mask-conic-to":R()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:et()}],"mask-repeat":[{mask:el()}],"mask-size":[{mask:ea()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",Q,V]}],filter:[{filter:["","none",Q,V]}],blur:[{blur:ep()}],brightness:[{brightness:[M,Q,V]}],contrast:[{contrast:[M,Q,V]}],"drop-shadow":[{"drop-shadow":["","none",u,eo,L]}],"drop-shadow-color":[{"drop-shadow":R()}],grayscale:[{grayscale:["",M,Q,V]}],"hue-rotate":[{"hue-rotate":[M,Q,V]}],invert:[{invert:["",M,Q,V]}],saturate:[{saturate:[M,Q,V]}],sepia:[{sepia:["",M,Q,V]}],"backdrop-filter":[{"backdrop-filter":["","none",Q,V]}],"backdrop-blur":[{"backdrop-blur":ep()}],"backdrop-brightness":[{"backdrop-brightness":[M,Q,V]}],"backdrop-contrast":[{"backdrop-contrast":[M,Q,V]}],"backdrop-grayscale":[{"backdrop-grayscale":["",M,Q,V]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[M,Q,V]}],"backdrop-invert":[{"backdrop-invert":["",M,Q,V]}],"backdrop-opacity":[{"backdrop-opacity":[M,Q,V]}],"backdrop-saturate":[{"backdrop-saturate":[M,Q,V]}],"backdrop-sepia":[{"backdrop-sepia":["",M,Q,V]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":O()}],"border-spacing-x":[{"border-spacing-x":O()}],"border-spacing-y":[{"border-spacing-y":O()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",Q,V]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[M,"initial",Q,V]}],ease:[{ease:["linear","initial",h,Q,V]}],delay:[{delay:[M,Q,V]}],animate:[{animate:["none",k,Q,V]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[f,Q,V]}],"perspective-origin":[{"perspective-origin":w()}],rotate:[{rotate:eu()}],"rotate-x":[{"rotate-x":eu()}],"rotate-y":[{"rotate-y":eu()}],"rotate-z":[{"rotate-z":eu()}],scale:[{scale:eb()}],"scale-x":[{"scale-x":eb()}],"scale-y":[{"scale-y":eb()}],"scale-z":[{"scale-z":eb()}],"scale-3d":["scale-3d"],skew:[{skew:ef()}],"skew-x":[{"skew-x":ef()}],"skew-y":[{"skew-y":ef()}],transform:[{transform:[Q,V,"","none","gpu","cpu"]}],"transform-origin":[{origin:w()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:eg()}],"translate-x":[{"translate-x":eg()}],"translate-y":[{"translate-y":eg()}],"translate-z":[{"translate-z":eg()}],"translate-none":["translate-none"],accent:[{accent:R()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:R()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",Q,V]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":O()}],"scroll-mx":[{"scroll-mx":O()}],"scroll-my":[{"scroll-my":O()}],"scroll-ms":[{"scroll-ms":O()}],"scroll-me":[{"scroll-me":O()}],"scroll-mt":[{"scroll-mt":O()}],"scroll-mr":[{"scroll-mr":O()}],"scroll-mb":[{"scroll-mb":O()}],"scroll-ml":[{"scroll-ml":O()}],"scroll-p":[{"scroll-p":O()}],"scroll-px":[{"scroll-px":O()}],"scroll-py":[{"scroll-py":O()}],"scroll-ps":[{"scroll-ps":O()}],"scroll-pe":[{"scroll-pe":O()}],"scroll-pt":[{"scroll-pt":O()}],"scroll-pr":[{"scroll-pr":O()}],"scroll-pb":[{"scroll-pb":O()}],"scroll-pl":[{"scroll-pl":O()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",Q,V]}],fill:[{fill:["none",...R()]}],"stroke-w":[{stroke:[M,X,D,F]}],stroke:[{stroke:["none",...R()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}}),{cva:eu,cx:eb,compose:ef}=t({hooks:{onComplete:e=>ep(e)}});e.s(["cn",0,eb,"cva",0,eu,"cx",0,eb],115504)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/00tczcrtv5upo.js b/litellm/proxy/_experimental/out/_next/static/chunks/00tczcrtv5upo.js new file mode 100644 index 00000000000..caf394915fd --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/00tczcrtv5upo.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,337822,e=>{"use strict";var t,n=e.i(843476);e.s([],158421),e.i(158421);var i=e.i(271645),r=e.i(956789),a=e.i(17989),o=e.i(46420);e.i(247167);var s=e.i(733332);let l=i.createContext(void 0);function u(e){let t=i.useContext(l);if(void 0===t&&!e)throw Error((0,s.default)(47));return t}var d=e.i(174080),c=e.i(301252),p=e.i(616269),f=e.i(439957),g=e.i(56434),h=e.i(264111),v=e.i(116786),m=e.i(990627),S=e.i(638396);let b={...v.popupStoreSelectors,disabled:(0,p.createSelector)(e=>e.disabled),instantType:(0,p.createSelector)(e=>e.instantType),openMethod:(0,p.createSelector)(e=>e.openMethod),openChangeReason:(0,p.createSelector)(e=>e.openChangeReason),modal:(0,p.createSelector)(e=>e.modal),focusManagerModal:(0,p.createSelector)(e=>e.focusManagerModal),stickIfOpen:(0,p.createSelector)(e=>e.stickIfOpen),titleElementId:(0,p.createSelector)(e=>e.titleElementId),descriptionElementId:(0,p.createSelector)(e=>e.descriptionElementId),openOnHover:(0,p.createSelector)(e=>e.openOnHover),closeDelay:(0,p.createSelector)(e=>e.closeDelay),hasViewport:(0,p.createSelector)(e=>e.hasViewport)};class R extends c.ReactStore{constructor(e,t,n=!1){const r={...(0,v.createInitialPopupStoreState)(),disabled:!1,modal:!1,focusManagerModal:!1,instantType:void 0,openMethod:null,openChangeReason:null,titleElementId:void 0,descriptionElementId:void 0,stickIfOpen:!0,nested:!1,openOnHover:!1,closeDelay:0,hasViewport:!1,...e},a=new m.PopupTriggerMap;r.open&&e?.mounted===void 0&&(r.mounted=!0),r.floatingRootContext=(0,v.createPopupFloatingRootContext)(a,t,n),super(r,{popupRef:i.createRef(),backdropRef:i.createRef(),internalBackdropRef:i.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerFocusTargetRef:i.createRef(),beforeContentFocusGuardRef:i.createRef(),stickIfOpenTimeout:new f.Timeout,triggerElements:a},b)}setOpen=(e,t)=>{let n=t.reason===g.REASONS.triggerHover,i=t.reason===g.REASONS.triggerPress&&0===t.event.detail,r=!e&&(t.reason===g.REASONS.escapeKey||null==t.reason),a=(0,h.attachPreventUnmountOnClose)(t),o=this.select("activeTriggerId");if(e||t.reason!==g.REASONS.closePress||null!=t.trigger||null==o||(t.trigger=this.context.triggerElements.getById(o)??this.select("activeTriggerElement")??void 0),this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let s=()=>{let n={open:e,openChangeReason:t.reason};(0,h.setPopupOpenState)(n,e,t.trigger,a()),this.update(n)};n?(this.set("stickIfOpen",!0),this.context.stickIfOpenTimeout.start(S.PATIENT_CLICK_THRESHOLD,()=>{this.set("stickIfOpen",!1)}),d.flushSync(s)):s(),i||r?this.set("instantType",i?"click":"dismiss"):t.reason===g.REASONS.focusOut?this.set("instantType","focus"):this.set("instantType",void 0)};static useStore(e,t){let{store:n,internalStore:r}=(0,h.usePopupStore)(e,(e,n)=>new R(t,e,n));return i.useEffect(()=>r?.disposeEffect(),[r]),n}disposeEffect=()=>this.context.stickIfOpenTimeout.disposeEffect()}var C=e.i(675606),y=e.i(176782);function E({props:e}){let{children:t,open:r,defaultOpen:a=!1,onOpenChange:s,onOpenChangeComplete:u,modal:d=!1,handle:c,triggerId:p,defaultTriggerId:f=null}=e,v=R.useStore(c?.store,{modal:d,open:a,openProp:r,activeTriggerId:f,triggerIdProp:p});(0,h.useInitialOpenSync)(v,r,a,f),v.useControlledProp("openProp",r),v.useControlledProp("triggerIdProp",p);let m=v.useState("open"),S=v.useState("mounted"),b=v.useState("payload"),y=null!=(0,o.useFloatingParentNodeId)();v.useContextCallback("onOpenChange",s),v.useContextCallback("onOpenChangeComplete",u),(0,h.usePopupRootSync)(v,m),(0,h.useImplicitActiveTrigger)(v);let{forceUnmount:P}=(0,h.useOpenStateTransitions)(m,v,()=>{v.update({stickIfOpen:!0,openChangeReason:null})});v.useSyncedValues({modal:d,nested:y}),i.useEffect(()=>{m||v.context.stickIfOpenTimeout.clear()},[v,m]);let O=i.useCallback(()=>{v.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction))},[v]);i.useImperativeHandle(e.actionsRef,()=>({unmount:P,close:O}),[P,O]);let k=m||S,I=i.useMemo(()=>({store:v}),[v]);return(0,n.jsxs)(l.Provider,{value:I,children:[k&&(0,n.jsx)(x,{store:v,modal:d}),"function"==typeof t?t({payload:b}):t]})}function x({store:e,modal:t}){let n=e.useState("floatingRootContext"),o=(0,a.useDismiss)(n,{outsidePressEvent:{mouse:"trap-focus"===t?"sloppy":"intentional",touch:"sloppy"}}),s=o.reference??r.EMPTY_OBJECT,l=o.trigger??r.EMPTY_OBJECT,u=i.useMemo(()=>(0,y.mergeProps)(h.FOCUSABLE_POPUP_PROPS,o.floating),[o.floating]);return(0,h.usePopupInteractionProps)(e,{activeTriggerProps:s,inactiveTriggerProps:l,popupProps:u}),null}var P=e.i(540886),O=e.i(405005),k=e.i(552245),I=e.i(650316),w=e.i(385689),T=e.i(872135),M=e.i(788015),A=e.i(152535),F=e.i(346570),j=e.i(32199);let N=i.forwardRef(function(e,t){let{render:r,className:a,style:o,disabled:l=!1,nativeButton:d=!0,handle:c,payload:p,openOnHover:f=!1,delay:v=300,closeDelay:m=0,id:b,...R}=e,C=u(!0),y=c?.store??C?.store;if(!y)throw Error((0,s.default)(74));let E=(0,M.useBaseUiId)(b),x=y.useState("isTriggerActive",E),N=y.useState("floatingRootContext"),D=y.useState("isOpenedByTrigger",E),B=y.useState("triggerPopupId",E),H=i.useRef(null),{registerTrigger:L,isMountedByThisTrigger:V}=(0,h.useTriggerDataForwarding)(E,H,y,{payload:p,disabled:l,openOnHover:f,closeDelay:m}),z=y.useState("openChangeReason"),U=y.useState("stickIfOpen"),K=y.useState("openMethod"),_=y.useState("focusManagerModal"),W=(0,T.useHoverReferenceInteraction)(N,{enabled:!l&&null!=N&&f&&("touch"!==K||z!==g.REASONS.triggerPress),mouseOnly:!0,move:!1,handleClose:(0,I.safePolygon)(),restMs:v,delay:{close:m},triggerElementRef:H,isActiveTrigger:x,isClosing:()=>"ending"===y.select("transitionStatus")}),G=(0,w.useClick)(N,{enabled:null!=N,stickIfOpen:U}),q=(0,j.useOpenMethodTriggerProps)(()=>y.select("open"),e=>{y.set("openMethod",e)}),Y=y.useState("triggerProps",V),{getButtonProps:J,buttonRef:$}=(0,P.useButton)({disabled:l,native:d}),{preFocusGuardRef:Q,handlePreFocusGuardFocus:X,handleFocusTargetFocus:Z}=(0,F.useTriggerFocusGuards)(y,H),ee=(0,k.useRenderElement)("button",e,{state:{disabled:l,open:D},ref:[$,t,L,H],props:[G.reference,W,Y,q,{[S.CLICK_TRIGGER_IDENTIFIER]:"",id:E,"aria-haspopup":"dialog","aria-expanded":D,"aria-controls":B},R,J],stateAttributesMapping:{open:e=>e&&z===g.REASONS.triggerPress?O.pressableTriggerOpenStateMapping.open(e):O.triggerOpenStateMapping.open(e)}});return V&&!_?(0,n.jsxs)(i.Fragment,{children:[(0,n.jsx)(A.FocusGuard,{ref:Q,onFocus:X}),(0,n.jsx)(i.Fragment,{children:ee},E),(0,n.jsx)(A.FocusGuard,{ref:y.context.triggerFocusTargetRef,onFocus:Z})]}):(0,n.jsx)(i.Fragment,{children:ee},E)});var D=e.i(726674);let B=i.createContext(void 0),H=i.forwardRef(function(e,t){let{keepMounted:i=!1,...r}=e,{store:a}=u();return a.useState("mounted")||i?(0,n.jsx)(B.Provider,{value:i,children:(0,n.jsx)(D.FloatingPortal,{ref:t,...r})}):null});var L=e.i(144394),V=e.i(146376);let z=i.createContext(void 0);function U(){let e=i.useContext(z);if(!e)throw Error((0,s.default)(46));return e}var K=e.i(329365),_=e.i(426),W=e.i(222640),G=e.i(360495),q=e.i(789579),Y=e.i(33383);let J=i.forwardRef(function(e,t){let{render:r,className:a,style:l,anchor:d,positionMethod:c="absolute",side:p="bottom",align:f="center",sideOffset:h=0,alignOffset:v=0,collisionBoundary:m="clipping-ancestors",collisionPadding:b=5,arrowPadding:R=5,sticky:C=!1,disableAnchorTracking:y=!1,collisionAvoidance:E=S.POPUP_COLLISION_AVOIDANCE,...x}=e,{store:P}=u(),O=function(){let e=i.useContext(B);if(void 0===e)throw Error((0,s.default)(45));return e}(),k=(0,o.useFloatingNodeId)(),I=P.useState("floatingRootContext"),w=P.useState("mounted"),T=P.useState("open"),M=P.useState("openChangeReason"),A=P.useState("activeTriggerElement"),F=P.useState("modal"),j=P.useState("openMethod"),N=P.useState("positionerElement"),D=P.useState("instantType"),H=P.useState("transitionStatus"),U=P.useState("hasViewport"),J=i.useRef(null),$=(0,W.useAnimationsFinished)(N,!1,!1),Q=(0,K.useAnchorPositioning)({anchor:d,floatingRootContext:I,positionMethod:c,mounted:w,side:p,sideOffset:h,align:f,alignOffset:v,arrowPadding:R,collisionBoundary:m,collisionPadding:b,sticky:C,disableAnchorTracking:y,keepMounted:O,nodeId:k,collisionAvoidance:E,adaptiveOrigin:U?G.adaptiveOrigin:void 0}),X=I.useState("domReferenceElement");(0,V.useIsoLayoutEffect)(()=>{let e=J.current;if(X&&(J.current=X),e&&X&&X!==e){P.set("instantType",void 0);let e=new AbortController;return $(()=>{P.set("instantType","trigger-change")},e.signal),()=>{e.abort()}}},[X,$,P]),(0,Y.useAnchoredPopupScrollLock)(T&&!0===F&&M!==g.REASONS.triggerHover,"touch"===j,N,A);let Z=i.useCallback(e=>{P.set("positionerElement",e)},[P]),ee={open:T,side:Q.side,align:Q.align,anchorHidden:Q.anchorHidden,instant:D},et=(0,q.usePositioner)(e,ee,{styles:Q.positionerStyles,transitionStatus:H,props:x,refs:[t,Z],hidden:!w,inert:!T});return(0,n.jsxs)(z.Provider,{value:Q,children:[w&&!0===F&&M!==g.REASONS.triggerHover&&(0,n.jsx)(_.InternalBackdrop,{ref:P.context.internalBackdropRef,inert:(0,L.inertValue)(!T),cutout:A}),(0,n.jsx)(o.FloatingNode,{id:k,children:et})]})});var $=e.i(229315),Q=e.i(61487),X=e.i(431157),Z=e.i(209407),ee=e.i(137584),et=e.i(673327),en=e.i(96533),ei=e.i(815982),er=e.i(667865);let ea=i.createContext(void 0);function eo(e){let{value:t,children:i}=e;return(0,n.jsx)(ea.Provider,{value:t,children:i})}let es={...O.popupStateMapping,...Z.transitionStatusMapping},el=i.forwardRef(function(e,t){let{render:r,className:a,style:o,initialFocus:s,finalFocus:l,...d}=e,{store:c}=u(),p=U(),f=null!=(0,en.useToolbarRootContext)(!0),{context:v,hasClosePart:m}=function(){let[e,t]=i.useState(0),n=(0,er.useStableCallback)(()=>(t(e=>e+1),()=>{t(e=>Math.max(0,e-1))}));return{context:i.useMemo(()=>({register:n}),[n]),hasClosePart:e>0}}(),S=c.useState("open"),b=c.useState("openMethod"),R=c.useState("instantType"),C=c.useState("transitionStatus"),y=c.useState("popupProps"),E=c.useState("titleElementId"),x=c.useState("descriptionElementId"),P=c.useState("modal"),O=c.useState("mounted"),I=c.useState("openChangeReason"),w=c.useState("activeTriggerElement"),T=c.useState("floatingRootContext"),M=T.useState("floatingId"),A=c.useState("disabled"),F=c.useState("openOnHover"),j=c.useState("closeDelay"),N=d.id??M;(0,ee.useOpenChangeComplete)({open:S,ref:c.context.popupRef,onComplete(){S&&c.context.onOpenChangeComplete?.(!0)}}),(0,X.useHoverFloatingInteraction)(T,{enabled:F&&!A,closeDelay:j});let D=void 0===s?(0,h.createDefaultInitialFocus)(c.context.popupRef):s,B=!1!==P&&m;c.useSyncedValue("focusManagerModal",B);let H=i.useCallback(e=>{c.set("popupElement",e)},[c]),L={open:S,side:p.side,align:p.align,instant:R,transitionStatus:C},V=(0,k.useRenderElement)("div",e,{state:L,ref:[t,c.context.popupRef,H],props:[y,{id:N,role:"dialog",...h.FOCUSABLE_POPUP_PROPS,"aria-labelledby":E,"aria-describedby":x,onKeyDown(e){f&&et.COMPOSITE_KEYS.has(e.key)&&e.stopPropagation()}},(0,ei.getDisabledMountTransitionStyles)(C),d],stateAttributesMapping:es});return(0,n.jsx)(Q.FloatingFocusManager,{context:T,openInteractionType:b,modal:B,disabled:!O||I===g.REASONS.triggerHover,initialFocus:D,returnFocus:l,restoreFocus:"popup",previousFocusableElement:(0,$.isHTMLElement)(w)?w:void 0,nextFocusableElement:c.context.triggerFocusTargetRef,beforeContentFocusGuardRef:c.context.beforeContentFocusGuardRef,children:(0,n.jsx)(eo,{value:v,children:V})})}),eu=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=o.useState("open"),{arrowRef:l,side:d,align:c,arrowUncentered:p,arrowStyles:f}=U();return(0,k.useRenderElement)("div",e,{state:{open:s,side:d,align:c,uncentered:p},ref:[t,l],props:[{style:f,"aria-hidden":!0},a],stateAttributesMapping:O.popupStateMapping})}),ed={...O.popupStateMapping,...Z.transitionStatusMapping},ec=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=o.useState("open"),l=o.useState("mounted"),d=o.useState("transitionStatus"),c=o.useState("openChangeReason");return(0,k.useRenderElement)("div",e,{state:{open:s,transitionStatus:d},ref:[o.context.backdropRef,t],props:[{role:"presentation",hidden:!l,style:{pointerEvents:c===g.REASONS.triggerHover?"none":void 0,userSelect:"none",WebkitUserSelect:"none"}},a],stateAttributesMapping:ed})}),ep=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=(0,M.useBaseUiId)(a.id);return o.useSyncedValueWithCleanup("titleElementId",s),(0,k.useRenderElement)("h2",e,{ref:t,props:[{id:s},a]})}),ef=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=(0,M.useBaseUiId)(a.id);return o.useSyncedValueWithCleanup("descriptionElementId",s),(0,k.useRenderElement)("p",e,{ref:t,props:[{id:s},a]})}),eg=i.forwardRef(function(e,t){let n,{render:r,className:a,style:o,disabled:s=!1,nativeButton:l=!0,...d}=e,{buttonRef:c,getButtonProps:p}=(0,P.useButton)({disabled:s,focusableWhenDisabled:!1,native:l}),{store:f}=u();return n=i.useContext(ea),(0,V.useIsoLayoutEffect)(()=>n?.register(),[n]),(0,k.useRenderElement)("button",e,{ref:[t,c],props:[{onClick(e){f.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.closePress,e.nativeEvent))}},d,p]})}),eh=((t={}).popupWidth="--popup-width",t.popupHeight="--popup-height",t);var ev=e.i(818390);let em={activationDirection:e=>e?{"data-activation-direction":e}:null},eS=i.forwardRef(function(e,t){let{render:n,className:i,style:r,children:a,...o}=e,{store:s}=u(),{side:l}=U(),d=s.useState("instantType"),{children:c,state:p}=(0,ev.usePopupViewport)({store:s,side:l,cssVars:eh,children:a}),f={activationDirection:p.activationDirection,transitioning:p.transitioning,instant:d};return(0,k.useRenderElement)("div",e,{state:f,ref:t,props:[o,{children:c}],stateAttributesMapping:em})});class eb{constructor(){this.store=new R}open(e){let t=e?this.store.context.triggerElements.getById(e)??void 0:void 0;if(e&&!t)throw Error((0,s.default)(80,e));this.store.setOpen(!0,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction,void 0,t))}close(){this.store.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction,void 0,void 0))}get isOpen(){return this.store.select("open")}}e.s(["Arrow",0,eu,"Backdrop",0,ec,"Close",0,eg,"Description",0,ef,"Handle",0,eb,"Popup",0,el,"Portal",0,H,"Positioner",0,J,"Root",0,function(e){return u(!0)?(0,n.jsx)(E,{props:e}):(0,n.jsx)(o.FloatingTree,{children:(0,n.jsx)(E,{props:e})})},"Title",0,ep,"Trigger",0,N,"Viewport",0,eS,"createHandle",0,function(){return new eb}],466914);var eR=e.i(466914),eR=eR,eC=e.i(115504);e.s(["Popover",0,function({...e}){return(0,n.jsx)(eR.Root,{"data-slot":"popover",...e})},"PopoverContent",0,function({className:e,align:t="center",alignOffset:i=0,side:r="bottom",sideOffset:a=4,...o}){return(0,n.jsx)(eR.Portal,{children:(0,n.jsx)(eR.Positioner,{align:t,alignOffset:i,side:r,sideOffset:a,className:"isolate z-50",children:(0,n.jsx)(eR.Popup,{"data-slot":"popover-content",className:(0,eC.cn)("z-50 flex w-72 origin-(--transform-origin) flex-col gap-4 rounded-md bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",e),...o})})})},"PopoverTrigger",0,function({...e}){return(0,n.jsx)(eR.Trigger,{"data-slot":"popover-trigger",...e})}],337822)},204258,e=>{"use strict";var t,n,i,r=e.i(843476);e.s([],958842),e.i(958842),e.i(247167);var a=e.i(271645),o=e.i(667865),s=e.i(552245),l=e.i(951437),u=e.i(788015),d=e.i(675606),c=e.i(56434),p=e.i(223910),f=e.i(733332);let g=a.createContext(void 0);function h(){let e=a.useContext(g);if(void 0===e)throw Error((0,f.default)(15));return e}var v=e.i(209407);let m=((t={}).open="data-open",t.closed="data-closed",t[t.startingStyle=v.TransitionStatusDataAttributes.startingStyle]="startingStyle",t[t.endingStyle=v.TransitionStatusDataAttributes.endingStyle]="endingStyle",t),S=((n={}).panelOpen="data-panel-open",n),b={[m.open]:""},R={[m.closed]:""},C={open:e=>e?b:R,...v.transitionStatusMapping},y=a.forwardRef(function(e,t){let{render:n,className:i,defaultOpen:f=!1,disabled:h=!1,onOpenChange:v,open:m,style:S,...b}=e,R=(0,o.useStableCallback)(v),y=function(e){let{open:t,defaultOpen:n,onOpenChange:i,disabled:r}=e,[s,f]=(0,l.useControlled)({controlled:t,default:n,name:"Collapsible",state:"open"}),{mounted:g,setMounted:h,transitionStatus:v}=(0,p.useTransitionStatus)(s,!0,!0),m=(0,u.useBaseUiId)(),[S,b]=a.useState(),R=S??m,C=(0,o.useStableCallback)(e=>{let t=!s,n=(0,d.createChangeEventDetails)(c.REASONS.triggerPress,e.nativeEvent);i(t,n),n.isCanceled||f(t)});return a.useMemo(()=>({disabled:r,handleTrigger:C,mounted:g,open:s,panelId:R,setMounted:h,setOpen:f,setPanelIdState:b,transitionStatus:v}),[r,C,g,s,R,h,f,b,v])}({open:m,defaultOpen:f,onOpenChange:R,disabled:h}),E=a.useMemo(()=>({open:y.open,disabled:y.disabled,transitionStatus:y.transitionStatus}),[y.open,y.disabled,y.transitionStatus]),x=a.useMemo(()=>({...y,onOpenChange:R,state:E}),[y,R,E]),P=(0,s.useRenderElement)("div",e,{state:E,ref:t,props:b,stateAttributesMapping:C});return(0,r.jsx)(g.Provider,{value:x,children:P})});var E=e.i(540886);let x={open:e=>e?{[S.panelOpen]:""}:null,...v.transitionStatusMapping},P=a.forwardRef(function(e,t){let{panelId:n,open:i,handleTrigger:r,state:a,disabled:o}=h(),{className:l,disabled:u=o,render:d,nativeButton:c=!0,style:p,...f}=e,{getButtonProps:g,buttonRef:v}=(0,E.useButton)({disabled:u,focusableWhenDisabled:!0,native:c});return(0,s.useRenderElement)("button",e,{state:a,ref:[t,v],props:[{"aria-controls":i?n:void 0,"aria-expanded":i,onClick:r},f,g],stateAttributesMapping:x})});var O=e.i(146376),k=e.i(377570),I=e.i(574735),w=e.i(828918),T=e.i(708445),M=e.i(446265),A=e.i(333848),F=e.i(137584),j=e.i(222640);let N={height:void 0,width:void 0};function D(e){return{height:e.scrollHeight,width:e.scrollWidth}}function B(e){return e.split(",").map(e=>e.trim()).some(e=>""!==e&&Number.parseFloat(e)>0)}function H(e,t,n){let i=e.style.getPropertyValue(t),r=e.style.getPropertyPriority(t);return e.style.setProperty(t,n),()=>{""===i?e.style.removeProperty(t):e.style.setProperty(t,i,r)}}let L=((i={}).collapsiblePanelHeight="--collapsible-panel-height",i.collapsiblePanelWidth="--collapsible-panel-width",i),V=a.forwardRef(function(e,t){let{className:n,hiddenUntilFound:i,keepMounted:r,render:l,id:u,style:p,...f}=e,{mounted:g,onOpenChange:v,open:S,panelId:b,setMounted:R,setPanelIdState:y,setOpen:E,state:x,transitionStatus:P}=h();(0,O.useIsoLayoutEffect)(()=>{if(u)return y(u),()=>{y(void 0)}},[u,y]);let{height:V,props:z,ref:U,shouldPreventOpenAnimation:K,shouldRender:_,transitionStatus:W,width:G}=function(e){let{externalRef:t,hiddenUntilFound:n,id:i,keepMounted:r,mounted:s,onOpenChange:l,open:u,setMounted:p,setOpen:f,transitionStatus:g}=e,h=a.useRef(null),v=a.useRef(null),[S,b]=a.useState(N),R=a.useRef(N),C=a.useRef(!1),y=a.useRef(u),E=a.useRef(!1),[x,P]=a.useState(!1),k=a.useRef(null),L=(0,w.useMergedRefs)(t,h),V=(0,M.useValueAsRef)({mounted:s,open:u}),z=(0,j.useAnimationsFinished)(h,!1,!1),U=!u&&!s,K=x?"idle":g,_=u&&(y.current||E.current),W=!u&&s&&"css-animation"===v.current&&void 0===S.height&&void 0===S.width?R.current:S,G=n&&U&&"css-animation"!==v.current,q=(0,o.useStableCallback)((e,t=!0)=>{t&&(R.current=e),b(e)}),Y=(0,o.useStableCallback)(()=>{k.current?.(),k.current=null}),J=(0,o.useStableCallback)(e=>{Y(),k.current=()=>{k.current=null,e()}}),$=(0,o.useStableCallback)(()=>{u&&s&&"css-animation"===v.current&&(E.current=!0)});(0,O.useIsoLayoutEffect)(()=>{x&&"starting"!==g&&P(!1)},[x,g]),a.useEffect(()=>()=>{$(),Y()},[$,Y]),(0,O.useIsoLayoutEffect)(()=>{let e=h.current;if(!e)return;!u&&k.current&&Y();let t=function(e,t=!1){let n=(0,A.ownerWindow)(e).getComputedStyle(e),i=(n.animationName.split(",").map(e=>e.trim()).some(e=>""!==e&&"none"!==e)||t)&&B(n.animationDuration),r=B(n.transitionDuration);return i&&r||r?"css-transition":i?"css-animation":"none"}(e,_);if(v.current=t,u&&"idle"===g&&y.current&&"css-animation"===t){R.current=D(e);return}if(u&&"starting"===g){let n=C.current;if(C.current=!1,"none"===t){q(D(e)),P(!0);return}if("css-transition"===t){let t=function(e){let t={"justify-content":e.style.justifyContent,"align-items":e.style.alignItems,"align-content":e.style.alignContent,"justify-items":e.style.justifyItems};function n(){Object.entries(t).forEach(([t,n])=>{""===n?e.style.removeProperty(t):e.style.setProperty(t,n)})}Object.keys(t).forEach(t=>{e.style.setProperty(t,"initial","important")});let i=T.AnimationFrame.request(n);return()=>{T.AnimationFrame.cancel(i),n()}}(e);return q(D(e)),n&&(J(H(e,"transition-duration","0s")),P(!0)),t}if("css-animation"===t){if(q(D(e)),!n)return void H(e,"animation-name","none")();let t=H(e,"animation-name","none"),i=H(e,"animation-duration","0s");return t(),J(i),P(!0),void 0}}if(!u&&s&&("idle"===g||"starting"===g)){if(y.current=!1,E.current=!1,"none"===t){q(N,!1),p(!1);return}q(D(e));return}if("ending"!==g)return;if("none"===t)return void p(!1);let n=D(e);(n.height??0)>0||(n.width??0)>0?(q(n),"css-animation"===t&&H(e,"animation-name","none")()):p(!1)},[s,u,Y,q,p,J,_,g]),(0,F.useOpenChangeComplete)({enabled:u&&s&&"idle"===K,open:!0,ref:h,onComplete(){u&&q(N,!1)}}),a.useEffect(()=>{if(u||!s||"ending"!==K||!h.current)return;let e=new AbortController,t=-1;function n(){V.current.open||(p(!1),q(N,!1))}return t=T.AnimationFrame.request(()=>{e.signal.aborted||z(n,e.signal)}),()=>{T.AnimationFrame.cancel(t),e.abort()}},[V,s,u,K,z,q,p]),(0,O.useIsoLayoutEffect)(()=>{let e=h.current;e&&n&&U&&e.setAttribute("hidden","until-found")},[U,n]),a.useEffect(function(){let e=h.current;if(e)return(0,I.addEventListener)(e,"beforematch",function(e){let t=(0,d.createChangeEventDetails)(c.REASONS.none,e);l(!0,t),t.isCanceled||(C.current=!0,f(!0))})},[l,f]);let Q=r||n||s||u;return{height:W.height,props:{...G?{[m.startingStyle]:""}:void 0,hidden:U,id:i},ref:L,shouldPreventOpenAnimation:_,shouldRender:Q,transitionStatus:K,width:W.width}}({externalRef:t,hiddenUntilFound:i??!1,id:b,keepMounted:r??!1,mounted:g,onOpenChange:v,open:S,setMounted:R,setOpen:E,transitionStatus:P}),q={...x,transitionStatus:W},Y=(0,k.resolveStyle)(p,q),J=(0,s.useRenderElement)("div",{...e,style:void 0},{state:q,ref:U,props:[z,{style:{[L.collapsiblePanelHeight]:void 0===V?"auto":`${V}px`,[L.collapsiblePanelWidth]:void 0===G?"auto":`${G}px`}},f,Y?{style:Y}:void 0,K?{style:{animationName:"none"}}:void 0],stateAttributesMapping:C});return _?J:null});e.s(["Panel",0,V,"Root",0,y,"Trigger",0,P],596315);var z=e.i(596315),z=z;e.s(["Collapsible",0,function({...e}){return(0,r.jsx)(z.Root,{"data-slot":"collapsible",...e})},"CollapsibleContent",0,function({...e}){return(0,r.jsx)(z.Panel,{"data-slot":"collapsible-content",...e})},"CollapsibleTrigger",0,function({...e}){return(0,r.jsx)(z.Trigger,{"data-slot":"collapsible-trigger",...e})}],204258)},699375,e=>{"use strict";var t,n=e.i(843476);e.s([],924305),e.i(924305),e.i(247167);var i=e.i(271645),r=e.i(951437),a=e.i(828918),o=e.i(146376),s=e.i(502077),l=e.i(956789),u=e.i(333848),d=e.i(552245),c=e.i(176782),p=e.i(788015),f=e.i(540886),g=e.i(733332);let h=i.createContext(void 0);var v=e.i(875812);let m=((t={}).checked="data-checked",t.unchecked="data-unchecked",t.disabled="data-disabled",t.readonly="data-readonly",t.required="data-required",t.valid="data-valid",t.invalid="data-invalid",t.touched="data-touched",t.dirty="data-dirty",t.filled="data-filled",t.focused="data-focused",t),S={...v.fieldValidityMapping,checked:e=>e?{[m.checked]:""}:{[m.unchecked]:""}};var b=e.i(469690),R=e.i(381104),C=e.i(884708),y=e.i(247778),E=e.i(538489),x=e.i(675606),P=e.i(56434),O=e.i(606039);let k=i.forwardRef(function(e,t){let{checked:g,className:v,defaultChecked:m,"aria-labelledby":k,form:I,id:w,inputRef:T,name:M,nativeButton:A=!1,onCheckedChange:F,readOnly:j=!1,required:N=!1,disabled:D=!1,render:B,uncheckedValue:H,value:L,style:V,...z}=e,{clearErrors:U}=(0,C.useFormContext)(),{state:K,setTouched:_,setDirty:W,validityData:G,setFilled:q,setFocused:Y,validationMode:J,disabled:$,name:Q,validation:X}=(0,b.useFieldRootContext)(),{labelId:Z}=(0,y.useLabelableContext)(),ee=$||D,et=Q??M,en=i.useRef(null),ei=(0,a.useMergedRefs)(en,T,X.inputRef),er=i.useRef(null),ea=(0,p.useBaseUiId)(),eo=(0,E.useLabelableId)({id:w,implicit:!1,controlRef:er}),es=A?void 0:eo,[el,eu]=(0,r.useControlled)({controlled:g,default:!!m,name:"Switch",state:"checked"});(0,R.useRegisterFieldControl)(er,ea,el,void 0,!ee,M),(0,o.useIsoLayoutEffect)(()=>{en.current&&q(en.current.checked)},[en,q]),(0,O.useValueChanged)(el,()=>{U(et),W(el!==G.initialValue),q(el),X.change(el)});let{getButtonProps:ed,buttonRef:ec}=(0,f.useButton)({disabled:ee,native:A}),ep=function(e,t,n,r=!0,a){let[s,l]=i.useState(),u=(0,p.useBaseUiId)(a?`${a}-label`:void 0),d=e??t??s;return(0,o.useIsoLayoutEffect)(()=>{let i=e||t||!r?void 0:function(e,t){let n=function(e){if(!e)return;let t=e.parentElement;if(t&&"LABEL"===t.tagName)return t;let n=e.id;if(n){let t=e.nextElementSibling;if(t&&t.htmlFor===n)return t}let i=e.labels;return i&&i[0]}(e);if(n)return!n.id&&t&&(n.id=t),n.id||void 0}(n.current,u);s!==i&&l(i)}),d}(k,Z,en,!A,es),ef=(0,c.mergeProps)({checked:el,disabled:ee,form:I,id:es,name:et,required:N,style:et?s.visuallyHiddenInput:s.visuallyHidden,tabIndex:-1,type:"checkbox","aria-hidden":!0,ref:ei,onChange(e){if(e.nativeEvent.defaultPrevented)return;if(j)return void e.preventDefault();let t=e.currentTarget.checked,n=(0,x.createChangeEventDetails)(P.REASONS.none,e.nativeEvent);F?.(t,n),n.isCanceled||eu(t)},onFocus(){er.current?.focus()}},e=>X.getValidationProps(ee,e),void 0!==L?{value:L}:l.EMPTY_OBJECT),eg=i.useMemo(()=>({...K,checked:el,disabled:ee,readOnly:j,required:N}),[K,el,ee,j,N]),eh=(0,d.useRenderElement)("span",e,{state:eg,ref:[t,er,ec],props:[{id:A?eo:ea,role:"switch","aria-checked":el,"aria-readonly":j||void 0,"aria-required":N||void 0,"aria-labelledby":ep,onFocus(){ee||Y(!0)},onBlur(){let e=en.current;e&&!ee&&(_(!0),Y(!1),"onBlur"===J&&X.commit(e.checked))},onClick(e){if(j||ee)return;e.preventDefault();let t=en.current;t&&t.dispatchEvent(new((0,u.ownerWindow)(t)).PointerEvent("click",{bubbles:!0,shiftKey:e.shiftKey,ctrlKey:e.ctrlKey,altKey:e.altKey,metaKey:e.metaKey}))}},z,ed,e=>X.getValidationProps(ee,e)],stateAttributesMapping:S});return(0,n.jsxs)(h.Provider,{value:eg,children:[eh,!el&&et&&void 0!==H&&(0,n.jsx)("input",{type:"hidden",form:I,name:et,value:H,disabled:ee}),(0,n.jsx)("input",{...ef,suppressHydrationWarning:!0})]})}),I=i.forwardRef(function(e,t){let{render:n,className:r,style:a,...o}=e,s=function(){let e=i.useContext(h);if(void 0===e)throw Error((0,g.default)(63));return e}();return(0,d.useRenderElement)("span",e,{state:s,ref:t,stateAttributesMapping:S,props:o})});e.s(["Root",0,k,"Thumb",0,I],450994);var w=e.i(450994),w=w,T=e.i(115504);e.s(["Switch",0,function({className:e,size:t="default",...i}){return(0,n.jsx)(w.Root,{"data-slot":"switch","data-size":t,className:(0,T.cn)("peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",e),...i,children:(0,n.jsx)(w.Thumb,{"data-slot":"switch-thumb",className:"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"})})}],699375)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/011mgw.-67gs_.js b/litellm/proxy/_experimental/out/_next/static/chunks/011mgw.-67gs_.js new file mode 100644 index 00000000000..6fff53bedce --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/011mgw.-67gs_.js @@ -0,0 +1,10 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,597440,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var l=e.i(9583),r=n.forwardRef(function(e,r){return n.createElement(l.default,(0,t.default)({},e,{ref:r,icon:i}))});e.s(["default",0,r],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},954616,e=>{"use strict";var t=e.i(271645),n=e.i(114272),i=e.i(540143),l=e.i(915823),r=e.i(619273),a=class extends l.Subscribable{#e;#t=void 0;#n;#i;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#l()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,r.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,r.hashKey)(t.mutationKey)!==(0,r.hashKey)(this.options.mutationKey)?this.reset():this.#n?.state.status==="pending"&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#l(),this.#r(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#l(),this.#r()}mutate(e,t){return this.#i=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#l(){let e=this.#n?.state??(0,n.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#r(e){i.notifyManager.batch(()=>{if(this.#i&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,i={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#i.onSuccess?.(e.data,t,n,i)}catch(e){Promise.reject(e)}try{this.#i.onSettled?.(e.data,null,t,n,i)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#i.onError?.(e.error,t,n,i)}catch(e){Promise.reject(e)}try{this.#i.onSettled?.(void 0,e.error,t,n,i)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},o=e.i(912598);e.s(["useMutation",0,function(e,n){let l=(0,o.useQueryClient)(n),[s]=t.useState(()=>new a(l,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let c=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(i.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),d=t.useCallback((e,t)=>{s.mutate(e,t).catch(r.noop)},[s]);if(c.error&&(0,r.shouldThrowError)(s.options.throwOnError,[c.error]))throw c.error;return{...c,mutate:d,mutateAsync:c.mutate}}],954616)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var l=e.i(9583),r=n.forwardRef(function(e,r){return n.createElement(l.default,(0,t.default)({},e,{ref:r,icon:i}))});e.s(["ExclamationCircleOutlined",0,r],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(529681),l=e.i(242064),r=e.i(517455),a=e.i(185793),o=e.i(721369),s=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let c=e=>{var{prefixCls:i,className:r,hoverable:a=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:c}=t.useContext(l.ConfigContext),d=c("card",i),u=(0,n.default)(`${d}-grid`,r,{[`${d}-grid-hoverable`]:a});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var d=e.i(915654),u=e.i(183293),g=e.i(246422),b=e.i(838378);let m=(0,g.genStyleHooks)("Card",e=>{let t=(0,b.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:n,cardHeadPadding:i,colorBorderSecondary:l,boxShadowTertiary:r,bodyPadding:a,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:r},[`${t}-head`]:(e=>{let{antCls:t,componentCls:n,headerHeight:i,headerPadding:l,tabsMarginBottom:r}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:i,marginBottom:-1,padding:`0 ${(0,d.unit)(l)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${n}-typography, + > ${n}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:r,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:a,borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:n,cardShadow:i,lineWidth:l}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,d.unit)(l)} 0 0 0 ${n}, + 0 ${(0,d.unit)(l)} 0 0 ${n}, + ${(0,d.unit)(l)} ${(0,d.unit)(l)} 0 0 ${n}, + ${(0,d.unit)(l)} 0 0 0 ${n} inset, + 0 ${(0,d.unit)(l)} 0 0 ${n} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:i}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:n,actionsLiMargin:i,cardActionsIconSize:l,colorBorderSecondary:r,actionsBg:a}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:a,borderTop:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${r}`,display:"flex",borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:i,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${n}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,d.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${n}`]:{fontSize:l,lineHeight:(0,d.unit)(e.calc(l).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${r}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,d.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${l}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:n}},[`${t}-contain-grid`]:{borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:i}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:n,headerPadding:i,bodyPadding:l}=e;return{[`${t}-head`]:{padding:`0 ${(0,d.unit)(i)}`,background:n,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,d.unit)(e.padding)} ${(0,d.unit)(l)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:n,headerPaddingSM:i,headerHeightSM:l,headerFontSizeSM:r}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:l,padding:`0 ${(0,d.unit)(i)}`,fontSize:r,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:n}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,n;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(n=e.headerPadding)?n:e.paddingLG}});var p=e.i(792812),h=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let f=e=>{let{actionClasses:n,actions:i=[],actionStyle:l}=e;return t.createElement("ul",{className:n,style:l},i.map((e,n)=>{let l=`action-${n}`;return t.createElement("li",{style:{width:`${100/i.length}%`},key:l},t.createElement("span",null,e))}))},y=t.forwardRef((e,s)=>{let d,{prefixCls:u,className:g,rootClassName:b,style:y,extra:$,headStyle:v={},bodyStyle:O={},title:j,loading:x,bordered:S,variant:C,size:E,type:w,cover:z,actions:M,tabList:B,children:N,activeTabKey:T,defaultActiveTabKey:P,tabBarExtraContent:k,hoverable:R,tabProps:L={},classNames:H,styles:I}=e,G=h(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:W,direction:D,card:A}=t.useContext(l.ConfigContext),[F]=(0,p.default)("card",C,S),X=e=>{var t;return(0,n.default)(null==(t=null==A?void 0:A.classNames)?void 0:t[e],null==H?void 0:H[e])},K=e=>{var t;return Object.assign(Object.assign({},null==(t=null==A?void 0:A.styles)?void 0:t[e]),null==I?void 0:I[e])},q=t.useMemo(()=>{let e=!1;return t.Children.forEach(N,t=>{(null==t?void 0:t.type)===c&&(e=!0)}),e},[N]),U=W("card",u),[Q,V,_]=m(U),J=t.createElement(a.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},N),Y=void 0!==T,Z=Object.assign(Object.assign({},L),{[Y?"activeKey":"defaultActiveKey"]:Y?T:P,tabBarExtraContent:k}),ee=(0,r.default)(E),et=ee&&"default"!==ee?ee:"large",en=B?t.createElement(o.default,Object.assign({size:et},Z,{className:`${U}-head-tabs`,onChange:t=>{var n;null==(n=e.onTabChange)||n.call(e,t)},items:B.map(e=>{var{tab:t}=e;return Object.assign({label:t},h(e,["tab"]))})})):null;if(j||$||en){let e=(0,n.default)(`${U}-head`,X("header")),i=(0,n.default)(`${U}-head-title`,X("title")),l=(0,n.default)(`${U}-extra`,X("extra")),r=Object.assign(Object.assign({},v),K("header"));d=t.createElement("div",{className:e,style:r},t.createElement("div",{className:`${U}-head-wrapper`},j&&t.createElement("div",{className:i,style:K("title")},j),$&&t.createElement("div",{className:l,style:K("extra")},$)),en)}let ei=(0,n.default)(`${U}-cover`,X("cover")),el=z?t.createElement("div",{className:ei,style:K("cover")},z):null,er=(0,n.default)(`${U}-body`,X("body")),ea=Object.assign(Object.assign({},O),K("body")),eo=t.createElement("div",{className:er,style:ea},x?J:N),es=(0,n.default)(`${U}-actions`,X("actions")),ec=(null==M?void 0:M.length)?t.createElement(f,{actionClasses:es,actionStyle:K("actions"),actions:M}):null,ed=(0,i.default)(G,["onTabChange"]),eu=(0,n.default)(U,null==A?void 0:A.className,{[`${U}-loading`]:x,[`${U}-bordered`]:"borderless"!==F,[`${U}-hoverable`]:R,[`${U}-contain-grid`]:q,[`${U}-contain-tabs`]:null==B?void 0:B.length,[`${U}-${ee}`]:ee,[`${U}-type-${w}`]:!!w,[`${U}-rtl`]:"rtl"===D},g,b,V,_),eg=Object.assign(Object.assign({},null==A?void 0:A.style),y);return Q(t.createElement("div",Object.assign({ref:s},ed,{className:eu,style:eg}),d,el,eo,ec))});var $=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};y.Grid=c,y.Meta=e=>{let{prefixCls:i,className:r,avatar:a,title:o,description:s}=e,c=$(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:d}=t.useContext(l.ConfigContext),u=d("card",i),g=(0,n.default)(`${u}-meta`,r),b=a?t.createElement("div",{className:`${u}-meta-avatar`},a):null,m=o?t.createElement("div",{className:`${u}-meta-title`},o):null,p=s?t.createElement("div",{className:`${u}-meta-description`},s):null,h=m||p?t.createElement("div",{className:`${u}-meta-detail`},m,p):null;return t.createElement("div",Object.assign({},c,{className:g}),b,h)},e.s(["Card",0,y],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(908206),l=e.i(242064),r=e.i(517455),a=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var c=e.i(876556),d=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n},u=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let g=e=>{let{itemPrefixCls:i,component:l,span:r,className:a,style:o,labelStyle:c,contentStyle:d,bordered:u,label:g,content:b,colon:m,type:p,styles:h}=e,{classNames:f}=t.useContext(s),y=Object.assign(Object.assign({},c),null==h?void 0:h.label),$=Object.assign(Object.assign({},d),null==h?void 0:h.content);if(u)return t.createElement(l,{colSpan:r,style:o,className:(0,n.default)(a,{[`${i}-item-${p}`]:"label"===p||"content"===p,[null==f?void 0:f.label]:(null==f?void 0:f.label)&&"label"===p,[null==f?void 0:f.content]:(null==f?void 0:f.content)&&"content"===p})},null!=g&&t.createElement("span",{style:y},g),null!=b&&t.createElement("span",{style:$},b));return t.createElement(l,{colSpan:r,style:o,className:(0,n.default)(`${i}-item`,a)},t.createElement("div",{className:`${i}-item-container`},null!=g&&t.createElement("span",{style:y,className:(0,n.default)(`${i}-item-label`,null==f?void 0:f.label,{[`${i}-item-no-colon`]:!m})},g),null!=b&&t.createElement("span",{style:$,className:(0,n.default)(`${i}-item-content`,null==f?void 0:f.content)},b)))};function b(e,{colon:n,prefixCls:i,bordered:l},{component:r,type:a,showLabel:o,showContent:s,labelStyle:c,contentStyle:d,styles:u}){return e.map(({label:e,children:b,prefixCls:m=i,className:p,style:h,labelStyle:f,contentStyle:y,span:$=1,key:v,styles:O},j)=>"string"==typeof r?t.createElement(g,{key:`${a}-${v||j}`,className:p,style:h,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),f),null==O?void 0:O.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),y),null==O?void 0:O.content)},span:$,colon:n,component:r,itemPrefixCls:m,bordered:l,label:o?e:null,content:s?b:null,type:a}):[t.createElement(g,{key:`label-${v||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),h),f),null==O?void 0:O.label),span:1,colon:n,component:r[0],itemPrefixCls:m,bordered:l,label:e,type:"label"}),t.createElement(g,{key:`content-${v||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),h),y),null==O?void 0:O.content),span:2*$-1,component:r[1],itemPrefixCls:m,bordered:l,content:b,type:"content"})])}let m=e=>{let n=t.useContext(s),{prefixCls:i,vertical:l,row:r,index:a,bordered:o}=e;return l?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${a}`,className:`${i}-row`},b(r,e,Object.assign({component:"th",type:"label",showLabel:!0},n))),t.createElement("tr",{key:`content-${a}`,className:`${i}-row`},b(r,e,Object.assign({component:"td",type:"content",showContent:!0},n)))):t.createElement("tr",{key:a,className:`${i}-row`},b(r,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},n)))};e.i(296059);var p=e.i(915654),h=e.i(183293),f=e.i(246422),y=e.i(838378);let $=(0,f.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:n,itemPaddingBottom:i,itemPaddingEnd:l,colonMarginRight:r,colonMarginLeft:a,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,h.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:n}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.padding)} ${(0,p.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:n,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingSM)} ${(0,p.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingXS)} ${(0,p.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},h.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:n,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:i,paddingInlineEnd:l},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,p.unit)(a)} ${(0,p.unit)(r)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,y.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var v=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let O=e=>{let g,{prefixCls:b,title:p,extra:h,column:f,colon:y=!0,bordered:O,layout:j,children:x,className:S,rootClassName:C,style:E,size:w,labelStyle:z,contentStyle:M,styles:B,items:N,classNames:T}=e,P=v(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:k,direction:R,className:L,style:H,classNames:I,styles:G}=(0,l.useComponentConfig)("descriptions"),W=k("descriptions",b),D=(0,a.default)(),A=t.useMemo(()=>{var e;return"number"==typeof f?f:null!=(e=(0,i.matchScreen)(D,Object.assign(Object.assign({},o),f)))?e:3},[D,f]),F=(g=t.useMemo(()=>N||(0,c.default)(x).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[N,x]),t.useMemo(()=>g.map(e=>{var{span:t}=e,n=d(e,["span"]);return"filled"===t?Object.assign(Object.assign({},n),{filled:!0}):Object.assign(Object.assign({},n),{span:"number"==typeof t?t:(0,i.matchScreen)(D,t)})}),[g,D])),X=(0,r.default)(w),K=((e,n)=>{let[i,l]=(0,t.useMemo)(()=>{let t,i,l,r;return t=[],i=[],l=!1,r=0,n.filter(e=>e).forEach(n=>{let{filled:a}=n,o=u(n,["filled"]);if(a){i.push(o),t.push(i),i=[],r=0;return}let s=e-r;(r+=n.span||1)>=e?(r>e?(l=!0,i.push(Object.assign(Object.assign({},o),{span:s}))):i.push(o),t.push(i),i=[],r=0):i.push(o)}),i.length>0&&t.push(i),[t=t.map(t=>{let n=t.reduce((e,t)=>e+(t.span||1),0);if(n({labelStyle:z,contentStyle:M,styles:{content:Object.assign(Object.assign({},G.content),null==B?void 0:B.content),label:Object.assign(Object.assign({},G.label),null==B?void 0:B.label)},classNames:{label:(0,n.default)(I.label,null==T?void 0:T.label),content:(0,n.default)(I.content,null==T?void 0:T.content)}}),[z,M,B,T,I,G]);return q(t.createElement(s.Provider,{value:V},t.createElement("div",Object.assign({className:(0,n.default)(W,L,I.root,null==T?void 0:T.root,{[`${W}-${X}`]:X&&"default"!==X,[`${W}-bordered`]:!!O,[`${W}-rtl`]:"rtl"===R},S,C,U,Q),style:Object.assign(Object.assign(Object.assign(Object.assign({},H),G.root),null==B?void 0:B.root),E)},P),(p||h)&&t.createElement("div",{className:(0,n.default)(`${W}-header`,I.header,null==T?void 0:T.header),style:Object.assign(Object.assign({},G.header),null==B?void 0:B.header)},p&&t.createElement("div",{className:(0,n.default)(`${W}-title`,I.title,null==T?void 0:T.title),style:Object.assign(Object.assign({},G.title),null==B?void 0:B.title)},p),h&&t.createElement("div",{className:(0,n.default)(`${W}-extra`,I.extra,null==T?void 0:T.extra),style:Object.assign(Object.assign({},G.extra),null==B?void 0:B.extra)},h)),t.createElement("div",{className:`${W}-view`},t.createElement("table",null,t.createElement("tbody",null,K.map((e,n)=>t.createElement(m,{key:n,index:n,colon:y,prefixCls:W,vertical:"vertical"===j,bordered:O,row:e}))))))))};O.Item=({children:e})=>e,e.s(["Descriptions",0,O],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),n=e.i(732961),i=e.i(289882),l=e.i(170517),r=e.i(628882),a=e.i(320890),o=e.i(104458),s=e.i(722319),c=e.i(8398),d=e.i(279728);e.i(765846);var u=e.i(602716),g=e.i(328052),b=e.i(135551);let m=(e,t)=>new b.FastColor(e).setA(t).toRgbString(),p=(e,t)=>new b.FastColor(e).lighten(t).toHexString(),h=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},f=(e,t)=>{let n=e||"#000",i=t||"#fff";return{colorBgBase:n,colorTextBase:i,colorText:m(i,.85),colorTextSecondary:m(i,.65),colorTextTertiary:m(i,.45),colorTextQuaternary:m(i,.25),colorFill:m(i,.18),colorFillSecondary:m(i,.12),colorFillTertiary:m(i,.08),colorFillQuaternary:m(i,.04),colorBgSolid:m(i,.95),colorBgSolidHover:m(i,1),colorBgSolidActive:m(i,.9),colorBgElevated:p(n,12),colorBgContainer:p(n,8),colorBgLayout:p(n,0),colorBgSpotlight:p(n,26),colorBgBlur:m(i,.04),colorBorder:p(n,26),colorBorderSecondary:p(n,19)}},y={defaultSeed:a.defaultConfig.token,useToken:function(){let[e,t,n]=(0,o.useToken)();return{theme:e,token:t,hashId:n}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let n=Object.keys(l.defaultPresetColors).map(t=>{let n=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,i,l)=>(e[`${t}-${l+1}`]=n[l],e[`${t}${l+1}`]=n[l],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),i=null!=t?t:(0,s.default)(e),r=(0,g.default)(e,{generateColorPalettes:h,generateNeutralColorPalettes:f});return Object.assign(Object.assign(Object.assign(Object.assign({},i),n),r),{colorPrimaryBg:r.colorPrimaryBorder,colorPrimaryBgHover:r.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let n=null!=t?t:(0,s.default)(e),i=n.fontSizeSM,l=n.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},n),function(e){let{sizeUnit:t,sizeStep:n}=e,i=n-2;return{sizeXXL:t*(i+10),sizeXL:t*(i+6),sizeLG:t*(i+2),sizeMD:t*(i+2),sizeMS:t*(i+1),size:t*i,sizeSM:t*i,sizeXS:t*(i-1),sizeXXS:t*(i-1)}}(null!=t?t:e)),(0,d.default)(i)),{controlHeight:l}),(0,c.default)(Object.assign(Object.assign({},n),{controlHeight:l})))},getDesignToken:e=>{let a=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):i.default,o=Object.assign(Object.assign({},l.default),null==e?void 0:e.token);return(0,n.getComputedToken)(o,{override:null==e?void 0:e.token},a,r.default)},defaultConfig:a.defaultConfig,_internalContext:a.DesignTokenContext};e.s(["theme",0,y],368869)},127952,e=>{"use strict";var t=e.i(843476),n=e.i(560445),i=e.i(175712),l=e.i(869216),r=e.i(311451),a=e.i(212931),o=e.i(898586),s=e.i(368869),c=e.i(270377),d=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:g,message:b,resourceInformationTitle:m,resourceInformation:p,onCancel:h,onOk:f,confirmLoading:y,requiredConfirmation:$}){let{Title:v,Text:O}=o.Typography,{token:j}=s.theme.useToken(),[x,S]=(0,d.useState)("");return(0,d.useEffect)(()=>{e&&S("")},[e]),(0,t.jsx)(a.Modal,{title:u,open:e,onOk:f,onCancel:h,confirmLoading:y,okText:y?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!$&&x!==$||y},cancelButtonProps:{disabled:y},children:(0,t.jsxs)("div",{className:"space-y-4",children:[g&&(0,t.jsx)(n.Alert,{message:g,type:"warning"}),(0,t.jsx)(i.Card,{title:m,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder}},style:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder},children:(0,t.jsx)(l.Descriptions,{column:1,size:"small",children:p&&p.map(({label:e,value:n,...i})=>(0,t.jsx)(l.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(O,{...i,children:n??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(O,{children:b})}),$&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(O,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(O,{children:"Type "}),(0,t.jsx)(O,{strong:!0,type:"danger",children:$}),(0,t.jsx)(O,{children:" to confirm deletion:"})]}),(0,t.jsx)(r.Input,{value:x,onChange:e=>S(e.target.value),placeholder:$,className:"rounded-md",prefix:(0,t.jsx)(c.ExclamationCircleOutlined,{style:{color:j.colorError}}),autoFocus:!0})]})]})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/016u~n51r0h1k.js b/litellm/proxy/_experimental/out/_next/static/chunks/016u~n51r0h1k.js new file mode 100644 index 00000000000..bd41af1a6d9 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/016u~n51r0h1k.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,184163,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"download",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["default",0,i],184163)},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),n=e.i(201072),o=e.i(121229),i=e.i(726289),a=e.i(864517),l=e.i(343794),s=e.i(529681),c=e.i(242064),u=e.i(931067),d=e.i(209428),p=e.i(703923),f={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},g=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),n=!1;e.current.forEach(function(e){if(e){n=!0;var o=e.style;o.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(o.transitionDuration="0s, 0s")}}),n&&(r.current=Date.now())}),e.current},m=e.i(410160),h=e.i(392221),b=e.i(654310),v=0,y=(0,b.default)();let x=function(e){var r=t.useState(),n=(0,h.default)(r,2),o=n[0],i=n[1];return t.useEffect(function(){var e;i("rc_progress_".concat((y?(e=v,v+=1):e="TEST_OR_SSR",e)))},[]),e||o};var k=function(e){var r=e.bg,n=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},n)};function $(e,t){return Object.keys(e).map(function(r){var n=parseFloat(r),o="".concat(Math.floor(n*t),"%");return"".concat(e[r]," ").concat(o)})}var C=t.forwardRef(function(e,r){var n=e.prefixCls,o=e.color,i=e.gradientId,a=e.radius,l=e.style,s=e.ptg,c=e.strokeLinecap,u=e.strokeWidth,d=e.size,p=e.gapDegree,f=o&&"object"===(0,m.default)(o),g=d/2,h=t.createElement("circle",{className:"".concat(n,"-circle-path"),r:a,cx:g,cy:g,stroke:f?"#FFF":void 0,strokeLinecap:c,strokeWidth:u,opacity:+(0!==s),style:l,ref:r});if(!f)return h;var b="".concat(i,"-conic"),v=$(o,(360-p)/360),y=$(o,1),x="conic-gradient(from ".concat(p?"".concat(180+p/2,"deg"):"0deg",", ").concat(v.join(", "),")"),C="linear-gradient(to ".concat(p?"bottom":"top",", ").concat(y.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:b},h),t.createElement("foreignObject",{x:0,y:0,width:d,height:d,mask:"url(#".concat(b,")")},t.createElement(k,{bg:C},t.createElement(k,{bg:x}))))}),w=function(e,t,r,n,o,i,a,l,s,c){var u=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,d=(100-n)/100*t;return"round"===s&&100!==n&&(d+=c/2)>=t&&(d=t-.01),{stroke:"string"==typeof l?l:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:d+u,transform:"rotate(".concat(o+r/100*360*((360-i)/360)+(0===i?0:({bottom:0,top:180,left:90,right:-90})[a]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},S=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function E(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let O=function(e){var r,n,o,i,a=(0,d.default)((0,d.default)({},f),e),s=a.id,c=a.prefixCls,h=a.steps,b=a.strokeWidth,v=a.trailWidth,y=a.gapDegree,k=void 0===y?0:y,$=a.gapPosition,O=a.trailColor,j=a.strokeLinecap,_=a.style,N=a.className,I=a.strokeColor,D=a.percent,M=(0,p.default)(a,S),P=x(s),A="".concat(P,"-gradient"),T=50-b/2,z=2*Math.PI*T,R=k>0?90+k/2:-90,W=(360-k)/360*z,L="object"===(0,m.default)(h)?h:{count:h,gap:2},F=L.count,H=L.gap,B=E(D),X=E(I),V=X.find(function(e){return e&&"object"===(0,m.default)(e)}),U=V&&"object"===(0,m.default)(V)?"butt":j,K=w(z,W,0,100,R,k,$,O,U,b),q=g();return t.createElement("svg",(0,u.default)({className:(0,l.default)("".concat(c,"-circle"),N),viewBox:"0 0 ".concat(100," ").concat(100),style:_,id:s,role:"presentation"},M),!F&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:T,cx:50,cy:50,stroke:O,strokeLinecap:U,strokeWidth:v||b,style:K}),F?(r=Math.round(F*(B[0]/100)),n=100/F,o=0,Array(F).fill(null).map(function(e,i){var a=i<=r-1?X[0]:O,l=a&&"object"===(0,m.default)(a)?"url(#".concat(A,")"):void 0,s=w(z,W,o,n,R,k,$,a,"butt",b,H);return o+=(W-s.strokeDashoffset+H)*100/W,t.createElement("circle",{key:i,className:"".concat(c,"-circle-path"),r:T,cx:50,cy:50,stroke:l,strokeWidth:b,opacity:1,style:s,ref:function(e){q[i]=e}})})):(i=0,B.map(function(e,r){var n=X[r]||X[X.length-1],o=w(z,W,i,e,R,k,$,n,U,b);return i+=e,t.createElement(C,{key:r,color:n,ptg:e,radius:T,prefixCls:c,gradientId:A,style:o,strokeLinecap:U,strokeWidth:b,gapDegree:k,ref:function(e){q[r]=e},size:100})}).reverse()))};var j=e.i(491816);e.i(765846);var _=e.i(896091);function N(e){return!e||e<0?0:e>100?100:e}function I({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let D=(e,t,r)=>{var n,o,i,a;let l=-1,s=-1;if("step"===t){let t=r.steps,n=r.strokeWidth;"string"==typeof e||void 0===e?(l="small"===e?2:14,s=null!=n?n:8):"number"==typeof e?[l,s]=[e,e]:[l=14,s=8]=Array.isArray(e)?e:[e.width,e.height],l*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?s=t||("small"===e?6:8):"number"==typeof e?[l,s]=[e,e]:[l=-1,s=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[l,s]="small"===e?[60,60]:[120,120]:"number"==typeof e?[l,s]=[e,e]:Array.isArray(e)&&(l=null!=(o=null!=(n=e[0])?n:e[1])?o:120,s=null!=(a=null!=(i=e[0])?i:e[1])?a:120));return[l,s]},M=e=>{let{prefixCls:r,trailColor:n=null,strokeLinecap:o="round",gapPosition:i,gapDegree:a,width:s=120,type:c,children:u,success:d,size:p=s,steps:f}=e,[g,m]=D(p,"circle"),{strokeWidth:h}=e;void 0===h&&(h=Math.max(3/g*100,6));let b=t.useMemo(()=>a||0===a?a:"dashboard"===c?75:void 0,[a,c]),v=(({percent:e,success:t,successPercent:r})=>{let n=N(I({success:t,successPercent:r}));return[n,N(N(e)-n)]})(e),y="[object Object]"===Object.prototype.toString.call(e.strokeColor),x=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||_.presetPrimaryColors.green,t||null]})({success:d,strokeColor:e.strokeColor}),k=(0,l.default)(`${r}-inner`,{[`${r}-circle-gradient`]:y}),$=t.createElement(O,{steps:f,percent:f?v[1]:v,strokeWidth:h,trailWidth:h,strokeColor:f?x[1]:x,strokeLinecap:o,trailColor:n,prefixCls:r,gapDegree:b,gapPosition:i||"dashboard"===c&&"bottom"||void 0}),C=g<=20,w=t.createElement("div",{className:k,style:{width:g,height:m,fontSize:.15*g+6}},$,!C&&u);return C?t.createElement(j.default,{title:u},w):w};e.i(296059);var P=e.i(694758),A=e.i(915654),T=e.i(183293),z=e.i(246422),R=e.i(838378);let W="--progress-line-stroke-color",L="--progress-percent",F=e=>{let t=e?"100%":"-100%";return new P.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},H=(0,z.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,R.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,T.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${W})`]},height:"100%",width:`calc(1 / var(${L}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,A.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:F(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:F(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var B=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let X=e=>{let{prefixCls:r,direction:n,percent:o,size:i,strokeWidth:a,strokeColor:s,strokeLinecap:c="round",children:u,trailColor:d=null,percentPosition:p,success:f}=e,{align:g,type:m}=p,h=s&&"string"!=typeof s?((e,t)=>{let{from:r=_.presetPrimaryColors.blue,to:n=_.presetPrimaryColors.blue,direction:o="rtl"===t?"to left":"to right"}=e,i=B(e,["from","to","direction"]);if(0!==Object.keys(i).length){let e,t=(e=[],Object.keys(i).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:i[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${o}, ${t})`;return{background:r,[W]:r}}let a=`linear-gradient(${o}, ${r}, ${n})`;return{background:a,[W]:a}})(s,n):{[W]:s,background:s},b="square"===c||"butt"===c?0:void 0,[v,y]=D(null!=i?i:[-1,a||("small"===i?6:8)],"line",{strokeWidth:a}),x=Object.assign(Object.assign({width:`${N(o)}%`,height:y,borderRadius:b},h),{[L]:N(o)/100}),k=I(e),$={width:`${N(k)}%`,height:y,borderRadius:b,backgroundColor:null==f?void 0:f.strokeColor},C=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:d||void 0,borderRadius:b}},t.createElement("div",{className:(0,l.default)(`${r}-bg`,`${r}-bg-${m}`),style:x},"inner"===m&&u),void 0!==k&&t.createElement("div",{className:`${r}-success-bg`,style:$})),w="outer"===m&&"start"===g,S="outer"===m&&"end"===g;return"outer"===m&&"center"===g?t.createElement("div",{className:`${r}-layout-bottom`},C,u):t.createElement("div",{className:`${r}-outer`,style:{width:v<0?"100%":v}},w&&u,C,S&&u)},V=e=>{let{size:r,steps:n,rounding:o=Math.round,percent:i=0,strokeWidth:a=8,strokeColor:s,trailColor:c=null,prefixCls:u,children:d}=e,p=o(i/100*n),[f,g]=D(null!=r?r:["small"===r?2:14,a],"step",{steps:n,strokeWidth:a}),m=f/n,h=Array.from({length:n});for(let e=0;et.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let K=["normal","exception","active","success"],q=t.forwardRef((e,u)=>{let d,{prefixCls:p,className:f,rootClassName:g,steps:m,strokeColor:h,percent:b=0,size:v="default",showInfo:y=!0,type:x="line",status:k,format:$,style:C,percentPosition:w={}}=e,S=U(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:E="end",type:O="outer"}=w,j=Array.isArray(h)?h[0]:h,_="string"==typeof h||Array.isArray(h)?h:void 0,P=t.useMemo(()=>{if(j){let e="string"==typeof j?j:Object.values(j)[0];return new r.FastColor(e).isLight()}return!1},[h]),A=t.useMemo(()=>{var t,r;let n=I(e);return Number.parseInt(void 0!==n?null==(t=null!=n?n:0)?void 0:t.toString():null==(r=null!=b?b:0)?void 0:r.toString(),10)},[b,e.success,e.successPercent]),T=t.useMemo(()=>!K.includes(k)&&A>=100?"success":k||"normal",[k,A]),{getPrefixCls:z,direction:R,progress:W}=t.useContext(c.ConfigContext),L=z("progress",p),[F,B,q]=H(L),Q="line"===x,Y=Q&&!m,G=t.useMemo(()=>{let r;if(!y)return null;let s=I(e),c=$||(e=>`${e}%`),u=Q&&P&&"inner"===O;return"inner"===O||$||"exception"!==T&&"success"!==T?r=c(N(b),N(s)):"exception"===T?r=Q?t.createElement(i.default,null):t.createElement(a.default,null):"success"===T&&(r=Q?t.createElement(n.default,null):t.createElement(o.default,null)),t.createElement("span",{className:(0,l.default)(`${L}-text`,{[`${L}-text-bright`]:u,[`${L}-text-${E}`]:Y,[`${L}-text-${O}`]:Y}),title:"string"==typeof r?r:void 0},r)},[y,b,A,T,x,L,$]);"line"===x?d=m?t.createElement(V,Object.assign({},e,{strokeColor:_,prefixCls:L,steps:"object"==typeof m?m.count:m}),G):t.createElement(X,Object.assign({},e,{strokeColor:j,prefixCls:L,direction:R,percentPosition:{align:E,type:O}}),G):("circle"===x||"dashboard"===x)&&(d=t.createElement(M,Object.assign({},e,{strokeColor:j,prefixCls:L,progressStatus:T}),G));let J=(0,l.default)(L,`${L}-status-${T}`,{[`${L}-${"dashboard"===x&&"circle"||x}`]:"line"!==x,[`${L}-inline-circle`]:"circle"===x&&D(v,"circle")[0]<=20,[`${L}-line`]:Y,[`${L}-line-align-${E}`]:Y,[`${L}-line-position-${O}`]:Y,[`${L}-steps`]:m,[`${L}-show-info`]:y,[`${L}-${v}`]:"string"==typeof v,[`${L}-rtl`]:"rtl"===R},null==W?void 0:W.className,f,g,B,q);return F(t.createElement("div",Object.assign({ref:u,style:Object.assign(Object.assign({},null==W?void 0:W.style),C),className:J,role:"progressbar","aria-valuenow":A,"aria-valuemin":0,"aria-valuemax":100},(0,s.default)(S,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),d))});e.s(["default",0,q],309821)},993914,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"}}]},name:"file-text",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["FileTextOutlined",0,i],993914)},663435,152473,e=>{"use strict";var t=e.i(843476),r=e.i(271645),n=e.i(199133),o=e.i(898586),i=e.i(56456);let a={enabled:!0,leading:!1,trailing:!0,wait:0,onExecute:()=>{}};class l{constructor(e,t){this.fn=e,this._canLeadingExecute=!0,this._isPending=!1,this._executionCount=0,this._options={...a,...t}}setOptions(e){return this._options={...this._options,...e},this._options.enabled||(this._isPending=!1),this._options}getOptions(){return this._options}maybeExecute(...e){this._options.leading&&this._canLeadingExecute&&(this.executeFunction(...e),this._canLeadingExecute=!1),(this._options.leading||this._options.trailing)&&(this._isPending=!0),this._timeoutId&&clearTimeout(this._timeoutId),this._timeoutId=setTimeout(()=>{this._canLeadingExecute=!0,this._isPending=!1,this._options.trailing&&this.executeFunction(...e)},this._options.wait)}executeFunction(...e){this._options.enabled&&(this.fn(...e),this._executionCount++,this._options.onExecute(this))}cancel(){this._timeoutId&&(clearTimeout(this._timeoutId),this._canLeadingExecute=!0,this._isPending=!1)}getExecutionCount(){return this._executionCount}getIsPending(){return this._options.enabled&&this._isPending}}function s(e,t){let[n,o]=(0,r.useState)(e),i=function(e,t){let[n]=(0,r.useState)(()=>{var r;return Object.getOwnPropertyNames(Object.getPrototypeOf(r=new l(e,t))).filter(e=>"function"==typeof r[e]).reduce((e,t)=>{let n=r[t];return"function"==typeof n&&(e[t]=n.bind(r)),e},{})});return n.setOptions(t),n}(o,t);return[n,i.maybeExecute,i]}e.s(["useDebouncedState",0,s],152473);var c=e.i(785242);let{Text:u}=o.Typography;e.s(["default",0,({value:e,onChange:o,onTeamSelect:a,disabled:l,organizationId:d,pageSize:p=20})=>{let[f,g]=(0,r.useState)(""),[m,h]=s("",{wait:300}),{data:b,fetchNextPage:v,hasNextPage:y,isFetchingNextPage:x,isLoading:k}=(0,c.useInfiniteTeams)(p,m||void 0,d),$=(0,r.useMemo)(()=>{if(!b?.pages)return[];let e=new Set,t=[];for(let r of b.pages)for(let n of r.teams)e.has(n.team_id)||(e.add(n.team_id),t.push(n));return t},[b]);return(0,t.jsx)(n.Select,{showSearch:!0,placeholder:"Search or select a team",value:e||void 0,onChange:e=>{o?.(e??""),a&&a(e?$.find(t=>t.team_id===e)??null:null)},disabled:l,allowClear:!0,filterOption:!1,onSearch:e=>{g(e),h(e)},searchValue:f,onPopupScroll:e=>{let t=e.currentTarget;(t.scrollTop+t.clientHeight)/t.scrollHeight>=.8&&y&&!x&&v()},loading:k,notFoundContent:k?(0,t.jsx)(i.LoadingOutlined,{spin:!0}):"No teams found","data-testid":"team-dropdown",popupRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,x&&(0,t.jsx)("div",{style:{textAlign:"center",padding:8},children:(0,t.jsx)(i.LoadingOutlined,{spin:!0})})]}),children:$.map(e=>(0,t.jsxs)(n.Select.Option,{value:e.team_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.team_alias})," ",(0,t.jsxs)(u,{type:"secondary",children:["(",e.team_id,")"]})]},e.team_id))})}],663435)},519756,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"upload",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["UploadOutlined",0,i],519756)},435451,e=>{"use strict";var t=e.i(843476),r=e.i(290571),n=e.i(271645);let o=e=>{var t=(0,r.__rest)(e,[]);return n.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),n.default.createElement("path",{d:"M12 4v16m8-8H4"}))},i=e=>{var t=(0,r.__rest)(e,[]);return n.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),n.default.createElement("path",{d:"M20 12H4"}))};var a=e.i(444755),l=e.i(673706),s=e.i(677955);let c="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",u="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",d=n.default.forwardRef((e,t)=>{let{onSubmit:d,enableStepper:p=!0,disabled:f,onValueChange:g,onChange:m}=e,h=(0,r.__rest)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),b=(0,n.useRef)(null),[v,y]=n.default.useState(!1),x=n.default.useCallback(()=>{y(!0)},[]),k=n.default.useCallback(()=>{y(!1)},[]),[$,C]=n.default.useState(!1),w=n.default.useCallback(()=>{C(!0)},[]),S=n.default.useCallback(()=>{C(!1)},[]);return n.default.createElement(s.default,Object.assign({type:"number",ref:(0,l.mergeRefs)([b,t]),disabled:f,makeInputClassName:(0,l.makeClassName)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null==(t=b.current)?void 0:t.value;null==d||d(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&x(),"ArrowUp"===e.key&&w()},onKeyUp:e=>{"ArrowDown"===e.key&&k(),"ArrowUp"===e.key&&S()},onChange:e=>{f||(null==g||g(parseFloat(e.target.value)),null==m||m(e))},stepper:p?n.default.createElement("div",{className:(0,a.tremorTwMerge)("flex justify-center align-middle")},n.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;f||(null==(e=b.current)||e.stepDown(),null==(t=b.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,a.tremorTwMerge)(!f&&u,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},n.default.createElement(i,{"data-testid":"step-down",className:(v?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),n.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;f||(null==(e=b.current)||e.stepUp(),null==(t=b.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,a.tremorTwMerge)(!f&&u,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},n.default.createElement(o,{"data-testid":"step-up",className:($?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},h))});d.displayName="NumberInput",e.s(["default",0,({step:e=.01,style:r={width:"100%"},placeholder:n="Enter a numerical value",min:o,max:i,onChange:a,...l})=>(0,t.jsx)(d,{onWheel:e=>e.currentTarget.blur(),step:e,style:r,placeholder:n,min:o,max:i,onChange:a,...l})],435451)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01dk-b-_masm~.js b/litellm/proxy/_experimental/out/_next/static/chunks/01dk-b-_masm~.js new file mode 100644 index 00000000000..03fe5143c6c --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01dk-b-_masm~.js @@ -0,0 +1,86 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,509345,e=>{"use strict";var t,a,l=e.i(843476),r=e.i(271645),i=e.i(464571),s=e.i(326373),n=e.i(653496),o=e.i(755151),d=e.i(646563),c=e.i(245094),m=e.i(602869),u=e.i(808613),p=e.i(311451),g=e.i(212931),x=e.i(199133),h=e.i(262218),f=e.i(898586),y=e.i(727749),j=e.i(770914),_=e.i(515831),b=e.i(175712),v=e.i(519756);let{Text:w}=f.Typography,{Option:C}=x.Select,N=({visible:e,prebuiltPatterns:t,categories:a,selectedPatternName:r,patternAction:s,onPatternNameChange:n,onActionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add prebuilt pattern",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(w,{strong:!0,children:"Pattern type"}),(0,l.jsx)(x.Select,{placeholder:"Choose pattern type",value:r,onChange:n,style:{width:"100%",marginTop:8},showSearch:!0,filterOption:(e,a)=>{let l=t.find(e=>e.name===a?.value);return!!l&&(l.display_name.toLowerCase().includes(e.toLowerCase())||l.name.toLowerCase().includes(e.toLowerCase()))},children:a.map(e=>{let a=t.filter(t=>t.category===e);return 0===a.length?null:(0,l.jsx)(x.Select.OptGroup,{label:e,children:a.map(e=>(0,l.jsx)(C,{value:e.name,children:e.display_name},e.name))},e)})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(w,{strong:!0,children:"Action"}),(0,l.jsx)(w,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this pattern is detected"}),(0,l.jsxs)(x.Select,{value:s,onChange:o,style:{width:"100%"},children:[(0,l.jsx)(C,{value:"BLOCK",children:"Block"}),(0,l.jsx)(C,{value:"MASK",children:"Mask"})]})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]}),{Text:k}=f.Typography,{Option:S}=x.Select,I=({visible:e,patternName:t,patternRegex:a,patternAction:r,onNameChange:s,onRegexChange:n,onActionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add custom regex pattern",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(k,{strong:!0,children:"Pattern name"}),(0,l.jsx)(p.Input,{placeholder:"e.g., internal_id, employee_code",value:t,onChange:e=>s(e.target.value),style:{marginTop:8}})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(k,{strong:!0,children:"Regex pattern"}),(0,l.jsx)(p.Input,{placeholder:"e.g., ID-[0-9]{6}",value:a,onChange:e=>n(e.target.value),style:{marginTop:8}}),(0,l.jsx)(k,{type:"secondary",style:{fontSize:12},children:"Enter a valid regular expression to match sensitive data"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(k,{strong:!0,children:"Action"}),(0,l.jsx)(k,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this pattern is detected"}),(0,l.jsxs)(x.Select,{value:r,onChange:o,style:{width:"100%"},children:[(0,l.jsx)(S,{value:"BLOCK",children:"Block"}),(0,l.jsx)(S,{value:"MASK",children:"Mask"})]})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]}),{Text:A}=f.Typography,{Option:O}=x.Select,T=({visible:e,keyword:t,action:a,description:r,onKeywordChange:s,onActionChange:n,onDescriptionChange:o,onAdd:d,onCancel:c})=>(0,l.jsxs)(g.Modal,{title:"Add blocked keyword",open:e,onCancel:c,footer:null,width:800,children:[(0,l.jsxs)(j.Space,{direction:"vertical",style:{width:"100%"},size:"large",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Keyword"}),(0,l.jsx)(p.Input,{placeholder:"Enter sensitive keyword or phrase",value:t,onChange:e=>s(e.target.value),style:{marginTop:8}})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Action"}),(0,l.jsx)(A,{type:"secondary",style:{display:"block",marginTop:4,marginBottom:8},children:"Choose what action the guardrail should take when this keyword is detected"}),(0,l.jsxs)(x.Select,{value:a,onChange:n,style:{width:"100%"},children:[(0,l.jsx)(O,{value:"BLOCK",children:"Block"}),(0,l.jsx)(O,{value:"MASK",children:"Mask"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(A,{strong:!0,children:"Description (optional)"}),(0,l.jsx)(p.Input.TextArea,{placeholder:"Explain why this keyword is sensitive",value:r,onChange:e=>o(e.target.value),rows:3,style:{marginTop:8}})]})]}),(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"8px",marginTop:"24px"},children:[(0,l.jsx)(i.Button,{onClick:c,children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",onClick:d,children:"Add"})]})]});var P=e.i(291542),L=e.i(955135);let{Text:B}=f.Typography,{Option:F}=x.Select,$=({patterns:e,onActionChange:t,onRemove:a})=>{let r=[{title:"Type",dataIndex:"type",key:"type",width:100,render:e=>(0,l.jsx)(h.Tag,{color:"prebuilt"===e?"blue":"green",children:"prebuilt"===e?"Prebuilt":"Custom"})},{title:"Pattern name",dataIndex:"name",key:"name",render:(e,t)=>t.display_name||t.name},{title:"Regex pattern",dataIndex:"pattern",key:"pattern",render:e=>e?(0,l.jsxs)(B,{code:!0,style:{fontSize:12},children:[e.substring(0,40),"..."]}):"-"},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>t(a.id,e),style:{width:120},size:"small",children:[(0,l.jsx)(F,{value:"BLOCK",children:"Block"}),(0,l.jsx)(F,{value:"MASK",children:"Mask"})]})},{title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>a(t.id),children:"Delete"})}];return 0===e.length?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No patterns added."}):(0,l.jsx)(P.Table,{dataSource:e,columns:r,rowKey:"id",pagination:!1,size:"small"})},{Text:E}=f.Typography,{Option:M}=x.Select,R=({keywords:e,onActionChange:t,onRemove:a})=>{let r=[{title:"Keyword",dataIndex:"keyword",key:"keyword"},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>t(a.id,"action",e),style:{width:120},size:"small",children:[(0,l.jsx)(M,{value:"BLOCK",children:"Block"}),(0,l.jsx)(M,{value:"MASK",children:"Mask"})]})},{title:"Description",dataIndex:"description",key:"description",render:e=>e||"-"},{title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>a(t.id),children:"Delete"})}];return 0===e.length?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No keywords added."}):(0,l.jsx)(P.Table,{dataSource:e,columns:r,rowKey:"id",pagination:!1,size:"small"})};var G=e.i(362024),z=e.i(993914);let{Title:D,Text:K}=f.Typography,{Option:q}=x.Select,H=({availableCategories:e,selectedCategories:t,onCategoryAdd:a,onCategoryRemove:s,onCategoryUpdate:n,accessToken:o,pendingSelection:c,onPendingSelectionChange:u})=>{let[p,g]=r.default.useState(""),f=void 0!==c?c:p,y=u||g,[j,_]=r.default.useState({}),[v,w]=r.default.useState({}),[C,N]=r.default.useState({}),[k,S]=r.default.useState([]),[I,A]=r.default.useState(""),[O,T]=r.default.useState(!1),B=async e=>{if(o&&!j[e]){N(t=>({...t,[e]:!0}));try{let t=await (0,m.getCategoryYaml)(o,e),a=t.yaml_content;if("json"===t.file_type)try{let e=JSON.parse(a);a=JSON.stringify(e,null,2)}catch(t){console.warn(`Failed to format JSON for ${e}:`,t)}_(t=>({...t,[e]:a})),w(a=>({...a,[e]:t.file_type||"yaml"}))}catch(t){console.error(`Failed to fetch content for category ${e}:`,t)}finally{N(t=>({...t,[e]:!1}))}}};r.default.useEffect(()=>{if(f&&o){let e=j[f];if(e)return void A(e);T(!0),(0,m.getCategoryYaml)(o,f).then(e=>{let t=e.yaml_content;if("json"===e.file_type)try{let e=JSON.parse(t);t=JSON.stringify(e,null,2)}catch(e){console.warn(`Failed to format JSON for ${f}:`,e)}A(t),_(e=>({...e,[f]:t})),w(t=>({...t,[f]:e.file_type||"yaml"}))}).catch(e=>{console.error(`Failed to fetch preview content for category ${f}:`,e),A("")}).finally(()=>{T(!1)})}else A(""),T(!1)},[f,o]);let F=[{title:"Category",dataIndex:"display_name",key:"display_name",render:(t,a)=>{let r=e.find(e=>e.name===a.category);return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{fontWeight:500},children:t}),r?.description&&(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888",marginTop:"4px"},children:r.description})]})}},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,t)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>n(t.id,"action",e),style:{width:"100%"},children:[(0,l.jsx)(q,{value:"BLOCK",children:(0,l.jsx)(h.Tag,{color:"red",children:"BLOCK"})}),(0,l.jsx)(q,{value:"MASK",children:(0,l.jsx)(h.Tag,{color:"orange",children:"MASK"})})]})},{title:"Severity Threshold",dataIndex:"severity_threshold",key:"severity_threshold",width:180,render:(e,t)=>(0,l.jsxs)(x.Select,{value:e,onChange:e=>n(t.id,"severity_threshold",e),style:{width:"100%"},children:[(0,l.jsx)(q,{value:"low",children:"Low"}),(0,l.jsx)(q,{value:"medium",children:"Medium"}),(0,l.jsx)(q,{value:"high",children:"High"})]})},{title:"",key:"actions",width:80,render:(e,t)=>(0,l.jsx)(i.Button,{icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>s(t.id),size:"small",children:"Remove"})}],$=e.filter(e=>!t.some(t=>t.category===e.name));return(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:8},children:[(0,l.jsx)(D,{level:5,style:{margin:0},children:"Blocked topics"}),(0,l.jsx)(K,{type:"secondary",style:{fontSize:12,fontWeight:400},children:"Select topics to block using keyword and semantic analysis"})]}),size:"small",children:[(0,l.jsxs)("div",{style:{marginBottom:16,display:"flex",gap:8},children:[(0,l.jsx)(x.Select,{placeholder:"Select a content category",value:f||void 0,onChange:y,style:{flex:1},showSearch:!0,optionLabelProp:"label",filterOption:(e,t)=>(t?.label?.toString().toLowerCase()??"").includes(e.toLowerCase()),children:$.map(e=>(0,l.jsx)(q,{value:e.name,label:e.display_name,children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{fontWeight:500},children:e.display_name}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#666",marginTop:"2px"},children:e.description})]})},e.name))}),(0,l.jsx)(i.Button,{type:"primary",onClick:()=>{if(!f)return;let l=e.find(e=>e.name===f);!l||t.some(e=>e.category===f)||(a({id:`category-${Date.now()}`,category:l.name,display_name:l.display_name,action:l.default_action,severity_threshold:"medium"}),y(""),A(""))},disabled:!f,icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add"})]}),f&&(0,l.jsxs)("div",{style:{marginBottom:16,padding:"12px",background:"#f9f9f9",border:"1px solid #e0e0e0",borderRadius:"4px"},children:[(0,l.jsxs)("div",{style:{marginBottom:8,fontWeight:500,fontSize:"14px"},children:["Preview: ",e.find(e=>e.name===f)?.display_name,v[f]&&(0,l.jsxs)("span",{style:{marginLeft:8,fontSize:"12px",color:"#888",fontWeight:400},children:["(",v[f]?.toUpperCase(),")"]})]}),O?(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Loading content..."}):I?(0,l.jsx)("pre",{style:{background:"#fff",padding:"12px",borderRadius:"4px",overflow:"auto",maxHeight:"300px",maxWidth:"100%",fontSize:"12px",lineHeight:"1.5",margin:0,border:"1px solid #e0e0e0",whiteSpace:"pre-wrap",wordBreak:"break-word"},children:(0,l.jsx)("code",{children:I})}):(0,l.jsx)("div",{style:{padding:"8px",textAlign:"center",color:"#888",fontSize:"12px"},children:"Unable to load category content"})]}),t.length>0?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(P.Table,{dataSource:t,columns:F,pagination:!1,size:"small",rowKey:"id"}),(0,l.jsx)("div",{style:{marginTop:16},children:(0,l.jsx)(G.Collapse,{activeKey:k,onChange:e=>{let t=Array.isArray(e)?e:e?[e]:[],a=new Set(k);t.forEach(e=>{a.has(e)||j[e]||B(e)}),S(t)},ghost:!0,items:t.map(e=>{let t=(v[e.category]||"yaml").toUpperCase();return{key:e.category,label:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:[(0,l.jsx)(z.FileTextOutlined,{}),(0,l.jsxs)("span",{children:["View ",t," for ",e.display_name]})]}),children:C[e.category]?(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Loading content..."}):j[e.category]?(0,l.jsx)("pre",{style:{background:"#f5f5f5",padding:"16px",borderRadius:"4px",overflow:"auto",maxHeight:"400px",fontSize:"12px",lineHeight:"1.5",margin:0},children:(0,l.jsx)("code",{children:j[e.category]})}):(0,l.jsx)("div",{style:{padding:"16px",textAlign:"center",color:"#888"},children:"Content will load when expanded"})}})})})]}):(0,l.jsx)("div",{style:{textAlign:"center",padding:"24px",color:"#888",border:"1px dashed #d9d9d9",borderRadius:"4px"},children:"No blocked topics selected. Add topics to detect and block harmful content."})]})};var U=e.i(790848),J=e.i(28651);let{Title:W,Text:V}=f.Typography,{Option:Y}=x.Select,Q={competitor_intent_type:"airline",brand_self:[],locations:[],policy:{competitor_comparison:"refuse",possible_competitor_comparison:"reframe"},threshold_high:.7,threshold_medium:.45,threshold_low:.3},X=({enabled:e,config:t,onChange:a,accessToken:i})=>{let s=t??Q,[n,o]=(0,r.useState)([]),[d,c]=(0,r.useState)(!1);(0,r.useEffect)(()=>{"airline"===s.competitor_intent_type&&i&&0===n.length&&(c(!0),(0,m.getMajorAirlines)(i).then(e=>o(e.airlines??[])).catch(()=>o([])).finally(()=>c(!1)))},[s.competitor_intent_type,i,n.length]);let p=e=>{a(e,e?{...Q}:null)},g=(t,l)=>{a(e,{...s,[t]:l})},h=(t,l)=>{a(e,{...s,policy:{...s.policy,[t]:l}})},f=(t,l)=>{a(e,{...s,[t]:l.filter(Boolean)})};return e?(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(W,{level:5,style:{margin:0},children:"Competitor Intent Filter"}),(0,l.jsx)(U.Switch,{checked:e,onChange:p})]}),size:"small",children:[(0,l.jsx)(V,{type:"secondary",style:{display:"block",marginBottom:16},children:"Block or reframe competitor comparison questions. Airline type uses major airlines (excluding your brand); generic requires manual competitor list."}),(0,l.jsxs)(u.Form,{layout:"vertical",size:"small",children:[(0,l.jsx)(u.Form.Item,{label:"Type",children:(0,l.jsxs)(x.Select,{value:s.competitor_intent_type,onChange:e=>g("competitor_intent_type",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"airline",children:"Airline (auto-load competitors from IATA)"}),(0,l.jsx)(Y,{value:"generic",children:"Generic (specify competitors manually)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Your Brand (brand_self)",required:!0,help:"airline"===s.competitor_intent_type?"Select your airline from the list (excluded from competitors) or type to add a custom term":"Names/codes users use for your brand",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:d?"Loading airlines...":"airline"===s.competitor_intent_type?"Search or select airline, or type to add custom":"Type and press Enter to add",value:s.brand_self,onChange:t=>"airline"===s.competitor_intent_type&&n.length>0?(t=>{let l=t.filter(Boolean),r=[],i=new Set;for(let e of l){let t=n.find(t=>t.match.split("|")[0]?.trim().toLowerCase()===e.toLowerCase());if(t)for(let e of t.match.split("|").map(e=>e.trim().toLowerCase()).filter(Boolean))i.has(e)||(i.add(e),r.push(e));else i.has(e.toLowerCase())||(i.add(e.toLowerCase()),r.push(e))}a(e,{...s,brand_self:r})})(t??[]):f("brand_self",t??[]),tokenSeparators:[","],loading:d,showSearch:!0,filterOption:(e,t)=>(t?.label?.toString().toLowerCase()??"").includes(e.toLowerCase()),optionFilterProp:"label",options:"airline"===s.competitor_intent_type&&n.length>0?n.map(e=>{let t=e.match.split("|")[0]?.trim()??e.id,a=e.match.split("|").map(e=>e.trim().toLowerCase()).filter(Boolean);return{value:t.toLowerCase(),label:`${t}${a.length>1?` (${a.slice(1).join(", ")})`:""}`}}):void 0})}),"airline"===s.competitor_intent_type&&(0,l.jsx)(u.Form.Item,{label:"Locations (optional)",help:"Countries, cities, airports for disambiguation (e.g. qatar, doha)",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:"Type and press Enter to add",value:s.locations??[],onChange:e=>f("locations",e??[]),tokenSeparators:[","]})}),"generic"===s.competitor_intent_type&&(0,l.jsx)(u.Form.Item,{label:"Competitors",required:!0,help:"Competitor names to detect (required for generic type)",children:(0,l.jsx)(x.Select,{mode:"tags",style:{width:"100%"},placeholder:"Type and press Enter to add",value:s.competitors??[],onChange:e=>f("competitors",e??[]),tokenSeparators:[","]})}),(0,l.jsx)(u.Form.Item,{label:"Policy: Competitor comparison",children:(0,l.jsxs)(x.Select,{value:s.policy?.competitor_comparison??"refuse",onChange:e=>h("competitor_comparison",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"refuse",children:"Refuse (block request)"}),(0,l.jsx)(Y,{value:"reframe",children:"Reframe (suggest alternative)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Policy: Possible competitor comparison",children:(0,l.jsxs)(x.Select,{value:s.policy?.possible_competitor_comparison??"reframe",onChange:e=>h("possible_competitor_comparison",e),style:{width:"100%"},children:[(0,l.jsx)(Y,{value:"refuse",children:"Refuse (block request)"}),(0,l.jsx)(Y,{value:"reframe",children:"Reframe (suggest alternative to backend LLM)"})]})}),(0,l.jsx)(u.Form.Item,{label:"Confidence thresholds",help:(0,l.jsxs)(l.Fragment,{children:["Classify competitor intent by confidence (0–1). Higher confidence → stronger intent.",(0,l.jsxs)("ul",{style:{marginBottom:0,marginTop:4,paddingLeft:20},children:[(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"High (≥)"}),': Treat as full competitor comparison → uses "Competitor comparison" policy']}),(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"Medium (≥)"}),': Treat as possible comparison → uses "Possible competitor comparison" policy']}),(0,l.jsxs)("li",{children:[(0,l.jsx)("strong",{children:"Low (≥)"}),": Log only; allow request. Below Low → allow with no action"]})]}),"Raise thresholds to be more permissive; lower them to be stricter."]}),children:(0,l.jsxs)(j.Space,{wrap:!0,children:[(0,l.jsx)(u.Form.Item,{label:"High",style:{marginBottom:0},help:"e.g. 0.7",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_high??.7,onChange:e=>g("threshold_high",e??.7),style:{width:80}})}),(0,l.jsx)(u.Form.Item,{label:"Medium",style:{marginBottom:0},help:"e.g. 0.45",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_medium??.45,onChange:e=>g("threshold_medium",e??.45),style:{width:80}})}),(0,l.jsx)(u.Form.Item,{label:"Low",style:{marginBottom:0},help:"e.g. 0.3",children:(0,l.jsx)(J.InputNumber,{min:0,max:1,step:.05,value:s.threshold_low??.3,onChange:e=>g("threshold_low",e??.3),style:{width:80}})})]})})]})]}):(0,l.jsx)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(W,{level:5,style:{margin:0},children:"Competitor Intent Filter"}),(0,l.jsx)(U.Switch,{checked:!1,onChange:p})]}),size:"small",children:(0,l.jsx)(V,{type:"secondary",children:"Block or reframe competitor comparison questions. When enabled, airline type auto-loads competitors from IATA; generic type requires manual competitor list."})})},{Title:Z,Text:ee}=f.Typography,et=({prebuiltPatterns:e,categories:t,selectedPatterns:a,blockedWords:s,onPatternAdd:n,onPatternRemove:o,onPatternActionChange:c,onBlockedWordAdd:u,onBlockedWordRemove:p,onBlockedWordUpdate:g,onFileUpload:x,accessToken:h,showStep:f,contentCategories:w=[],selectedContentCategories:C=[],onContentCategoryAdd:k,onContentCategoryRemove:S,onContentCategoryUpdate:A,pendingCategorySelection:O,onPendingCategorySelectionChange:P,competitorIntentEnabled:L=!1,competitorIntentConfig:B=null,onCompetitorIntentChange:F})=>{let[E,M]=(0,r.useState)(!1),[G,z]=(0,r.useState)(!1),[D,K]=(0,r.useState)(!1),[q,U]=(0,r.useState)(""),[J,W]=(0,r.useState)("BLOCK"),[V,Y]=(0,r.useState)(""),[Q,et]=(0,r.useState)(""),[ea,el]=(0,r.useState)("BLOCK"),[er,ei]=(0,r.useState)(""),[es,en]=(0,r.useState)("BLOCK"),[eo,ed]=(0,r.useState)(""),[ec,em]=(0,r.useState)(!1),eu=async e=>{em(!0);try{let t=await e.text();if(h){let e=await (0,m.validateBlockedWordsFile)(h,t);if(e.valid)x&&x(t),y.default.success(e.message||"File uploaded successfully");else{let t=e.error||e.errors&&e.errors.join(", ")||"Invalid file";y.default.error(`Validation failed: ${t}`)}}}catch(e){y.default.error(`Failed to upload file: ${e}`)}finally{em(!1)}return!1};return(0,l.jsxs)("div",{className:"space-y-6",children:[!f&&(0,l.jsx)("div",{children:(0,l.jsx)(ee,{type:"secondary",children:"Configure patterns, keywords, and content categories to detect and filter sensitive information in requests and responses."})}),(!f||"patterns"===f)&&(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(Z,{level:5,style:{margin:0},children:"Pattern Detection"}),(0,l.jsx)(ee,{type:"secondary",style:{fontSize:14,fontWeight:400},children:"Detect sensitive information using regex patterns (SSN, credit cards, API keys, etc.)"})]}),size:"small",children:[(0,l.jsx)("div",{style:{marginBottom:16},children:(0,l.jsxs)(j.Space,{children:[(0,l.jsx)(i.Button,{type:"primary",onClick:()=>M(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add prebuilt pattern"}),(0,l.jsx)(i.Button,{onClick:()=>K(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add custom regex"})]})}),(0,l.jsx)($,{patterns:a,onActionChange:c,onRemove:o})]}),(!f||"keywords"===f)&&(0,l.jsxs)(b.Card,{title:(0,l.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,l.jsx)(Z,{level:5,style:{margin:0},children:"Blocked Keywords"}),(0,l.jsx)(ee,{type:"secondary",style:{fontSize:14,fontWeight:400},children:"Block or mask specific sensitive terms and phrases"})]}),size:"small",children:[(0,l.jsx)("div",{style:{marginBottom:16},children:(0,l.jsxs)(j.Space,{children:[(0,l.jsx)(i.Button,{type:"primary",onClick:()=>z(!0),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add keyword"}),(0,l.jsx)(_.Upload,{beforeUpload:eu,accept:".yaml,.yml",showUploadList:!1,children:(0,l.jsx)(i.Button,{icon:(0,l.jsx)(v.UploadOutlined,{}),loading:ec,children:"Upload YAML file"})})]})}),(0,l.jsx)(R,{keywords:s,onActionChange:g,onRemove:p})]}),(!f||"competitor_intent"===f||"categories"===f)&&F&&(0,l.jsx)(X,{enabled:L,config:B,onChange:F,accessToken:h}),(!f||"categories"===f)&&w.length>0&&k&&S&&A&&(0,l.jsx)(H,{availableCategories:w,selectedCategories:C,onCategoryAdd:k,onCategoryRemove:S,onCategoryUpdate:A,accessToken:h,pendingSelection:O,onPendingSelectionChange:P}),(0,l.jsx)(N,{visible:E,prebuiltPatterns:e,categories:t,selectedPatternName:q,patternAction:J,onPatternNameChange:U,onActionChange:e=>W(e),onAdd:()=>{if(!q)return void y.default.error("Please select a pattern");let t=e.find(e=>e.name===q);n({id:`pattern-${Date.now()}`,type:"prebuilt",name:q,display_name:t?.display_name,action:J}),M(!1),U(""),W("BLOCK")},onCancel:()=>{M(!1),U(""),W("BLOCK")}}),(0,l.jsx)(I,{visible:D,patternName:V,patternRegex:Q,patternAction:ea,onNameChange:Y,onRegexChange:et,onActionChange:e=>el(e),onAdd:()=>{V&&Q?(n({id:`custom-${Date.now()}`,type:"custom",name:V,pattern:Q,action:ea}),K(!1),Y(""),et(""),el("BLOCK")):y.default.error("Please provide pattern name and regex")},onCancel:()=>{K(!1),Y(""),et(""),el("BLOCK")}}),(0,l.jsx)(T,{visible:G,keyword:er,action:es,description:eo,onKeywordChange:ei,onActionChange:e=>en(e),onDescriptionChange:ed,onAdd:()=>{er?(u({id:`word-${Date.now()}`,keyword:er,action:es,description:eo||void 0}),z(!1),ei(""),ed(""),en("BLOCK")):y.default.error("Please enter a keyword")},onCancel:()=>{z(!1),ei(""),ed(""),en("BLOCK")}})]})};var ea=e.i(555987),el=((t={}).PresidioPII="Presidio PII",t.Bedrock="Bedrock Guardrail",t.Lakera="Lakera",t);let er={},ei=e=>{let t={};return t.PresidioPII="Presidio PII",t.Bedrock="Bedrock Guardrail",t.Lakera="Lakera",t.LlmAsAJudge="LiteLLM LLM as a Judge",Object.entries(e).forEach(([e,a])=>{a&&"object"==typeof a&&"ui_friendly_name"in a&&(t[e.split("_").map((e,t)=>e.charAt(0).toUpperCase()+e.slice(1)).join("")]=a.ui_friendly_name)}),er=t,t},es=()=>Object.keys(er).length>0?er:el,en={PresidioPII:"presidio",Bedrock:"bedrock",Lakera:"lakera_v2",LitellmContentFilter:"litellm_content_filter",ToolPermission:"tool_permission",BlockCodeExecution:"block_code_execution",Promptguard:"promptguard",LlmAsAJudge:"llm_as_a_judge",Xecguard:"xecguard",QostodianNexus:"qostodian_nexus",Repelloai:"repelloai"},eo=e=>{Object.entries(e).forEach(([e,t])=>{t&&"object"==typeof t&&"ui_friendly_name"in t&&(en[e.split("_").map((e,t)=>e.charAt(0).toUpperCase()+e.slice(1)).join("")]=e)})},ed=e=>Array.isArray(e)?e.filter(e=>"string"==typeof e):"string"==typeof e?[e]:[],ec=(e,t)=>{let a=t?en[t]?.toLowerCase():null;return(a&&e?.supported_modes_by_provider?e.supported_modes_by_provider[a]:void 0)??e?.supported_modes},em=e=>!!e&&"Presidio PII"===es()[e],eu=e=>!!e&&"LiteLLM Content Filter"===es()[e],ep=e=>!!e&&"llm_as_a_judge"===en[e],eg="/ui/assets/logos/",ex={"Zscaler AI Guard":`${eg}zscaler.svg`,"Presidio PII":`${eg}microsoft_azure.svg`,"Bedrock Guardrail":`${eg}bedrock.svg`,Lakera:`${eg}lakeraai.jpeg`,"Azure Content Safety Prompt Shield":`${eg}microsoft_azure.svg`,"Azure Content Safety Text Moderation":`${eg}microsoft_azure.svg`,"Aporia AI":`${eg}aporia.png`,"PANW Prisma AIRS":`${eg}palo_alto_networks.jpeg`,"Cisco AI Defense":`${eg}cisco.png`,"Noma Security":`${eg}noma_security.png`,"Javelin Guardrails":`${eg}javelin.png`,"Pillar Guardrail":`${eg}pillar.jpeg`,"Google Cloud Model Armor":`${eg}google.svg`,"Guardrails AI":`${eg}guardrails_ai.jpeg`,"Lasso Guardrail":`${eg}lasso.png`,"Pangea Guardrail":`${eg}pangea.png`,"AIM Guardrail":`${eg}aim_security.jpeg`,"Cato Networks Guardrail":`${eg}cato_networks.svg`,"OpenAI Moderation":`${eg}openai_small.svg`,EnkryptAI:`${eg}enkrypt_ai.avif`,"Prompt Security":`${eg}prompt_security.png`,PromptGuard:`${eg}promptguard.svg`,XecGuard:`${eg}xecguard.svg`,"LiteLLM Content Filter":`${eg}litellm_logo.jpg`,"LiteLLM LLM as a Judge":`${eg}litellm_logo.jpg`,Akto:`${eg}akto.svg`,"Qostodian Nexus":`${eg}qohash.jpg`,"RepelloAI Argus":`${eg}repelloai.png`},eh=e=>{if(!e)return{logo:"",displayName:"-"};let t=Object.keys(en).find(t=>en[t].toLowerCase()===e.toLowerCase());if(!t)return{logo:"",displayName:e};let a=es()[t];return{logo:(0,ea.resolveLogoSrc)(ex[a])??"",displayName:a||e}};function ef(e){return!0===e?"yes":!1===e?"no":"inherit"}function ey(e){return!0===e?"yes":!1===e?"no":"inherit"}var ej=e.i(435451);let{Title:e_}=f.Typography,eb=({field:e,fieldKey:t,fullFieldKey:a,value:s})=>{let[n,o]=r.default.useState([]),[d,c]=r.default.useState(e.dict_key_options||[]);return r.default.useEffect(()=>{if(s&&"object"==typeof s){let t=Object.keys(s);o(t.map(e=>({key:e,id:`${e}_${Date.now()}_${Math.random()}`}))),c((e.dict_key_options||[]).filter(e=>!t.includes(e)))}},[s,e.dict_key_options]),(0,l.jsxs)("div",{className:"space-y-3",children:[n.map(t=>(0,l.jsxs)("div",{className:"flex items-center space-x-3 p-3 border rounded-lg",children:[(0,l.jsx)("div",{className:"w-24 font-medium text-sm",children:t.key}),(0,l.jsx)("div",{className:"flex-1",children:(0,l.jsx)(u.Form.Item,{name:Array.isArray(a)?[...a,t.key]:[a,t.key],style:{marginBottom:0},initialValue:s&&"object"==typeof s?s[t.key]:void 0,normalize:"number"===e.dict_value_type?e=>{if(null==e||""===e)return;let t=Number(e);return isNaN(t)?e:t}:void 0,children:"number"===e.dict_value_type?(0,l.jsx)(ej.default,{step:1,width:200,placeholder:`Enter ${t.key} value`}):"boolean"===e.dict_value_type?(0,l.jsxs)(x.Select,{placeholder:`Select ${t.key} value`,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):(0,l.jsx)(p.Input,{placeholder:`Enter ${t.key} value`})})}),(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",onClick:()=>{var e,a;return e=t.id,a=t.key,void(o(n.filter(t=>t.id!==e)),c([...d,a].sort()))},children:"Remove"})]},t.id)),d.length>0&&(0,l.jsxs)("div",{className:"flex items-center space-x-3 mt-2",children:[(0,l.jsx)(x.Select,{placeholder:"Select category to configure",style:{width:200},onSelect:e=>e&&void(!e||(o([...n,{key:e,id:`${e}_${Date.now()}`}]),c(d.filter(t=>t!==e)))),value:void 0,children:d.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}),(0,l.jsx)("span",{className:"text-sm text-gray-500",children:"Select a category to add threshold configuration"})]})]})},ev=({optionalParams:e,parentFieldKey:t,values:a})=>e.fields&&0!==Object.keys(e.fields).length?(0,l.jsxs)("div",{className:"guardrail-optional-params",children:[(0,l.jsxs)("div",{className:"mb-8 pb-4 border-b border-gray-100",children:[(0,l.jsx)(e_,{level:3,className:"mb-2 font-semibold text-gray-900",children:"Optional Parameters"}),(0,l.jsx)("p",{className:"text-gray-600 text-sm",children:e.description||"Configure additional settings for this guardrail provider"})]}),(0,l.jsx)("div",{className:"space-y-8",children:Object.entries(e.fields).map(([e,r])=>{let i,s;return i=`${t}.${e}`,s=a?.[e],"dict"===r.type&&r.dict_key_options?(0,l.jsxs)("div",{className:"mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200",children:[(0,l.jsx)("div",{className:"mb-4 font-medium text-gray-900 text-base",children:e}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mb-4",children:r.description}),(0,l.jsx)(eb,{field:r,fieldKey:e,fullFieldKey:[t,e],value:s})]},i):(0,l.jsx)("div",{className:"mb-8 p-6 bg-white rounded-lg border border-gray-200 shadow-xs",children:(0,l.jsx)(u.Form.Item,{name:[t,e],label:(0,l.jsxs)("div",{className:"mb-2",children:[(0,l.jsx)("div",{className:"font-medium text-gray-900 text-base",children:e}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mt-1",children:r.description})]}),rules:r.required?[{required:!0,message:`${e} is required`}]:void 0,className:"mb-0",initialValue:void 0!==s?s:r.default_value,normalize:"number"===r.type?e=>{if(null==e||""===e)return;let t=Number(e);return isNaN(t)?e:t}:void 0,children:"select"===r.type&&r.options?(0,l.jsx)(x.Select,{placeholder:r.description,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"multiselect"===r.type&&r.options?(0,l.jsx)(x.Select,{mode:"multiple",placeholder:r.description,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"bool"===r.type||"boolean"===r.type?(0,l.jsxs)(x.Select,{placeholder:r.description,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):"number"===r.type?(0,l.jsx)(ej.default,{step:1,width:400,placeholder:r.description}):e.includes("password")||e.includes("secret")||e.includes("key")?(0,l.jsx)(p.Input.Password,{placeholder:r.description}):(0,l.jsx)(p.Input,{placeholder:r.description})})},i)})})]}):null;var ew=e.i(482725),eC=e.i(850627);let eN=({selectedProvider:e,accessToken:t,providerParams:a=null,value:i=null})=>{let[s,n]=(0,r.useState)(!1),[o,d]=(0,r.useState)(a),[c,g]=(0,r.useState)(null);if((0,r.useEffect)(()=>{if(a)return void d(a);let e=async()=>{if(t){n(!0),g(null);try{let e=await (0,m.getGuardrailProviderSpecificParams)(t);d(e),ei(e),eo(e)}catch(e){console.error("Error fetching provider params:",e),g("Failed to load provider parameters")}finally{n(!1)}}};a||e()},[t,a]),!e)return null;if(s)return(0,l.jsx)(ew.Spin,{tip:"Loading provider parameters..."});if(c)return(0,l.jsx)("div",{className:"text-red-500",children:c});let h=en[e]?.toLowerCase(),f=o&&o[h];if(!f||0===Object.keys(f).length)return(0,l.jsx)("div",{children:"No configuration fields available for this provider."});let y=new Set(["patterns","blocked_words","blocked_words_file","categories","severity_threshold","pattern_redaction_format","keyword_redaction_tag"]),j=eu(e),_=(e,t="",a)=>Object.entries(e).map(([e,r])=>{let s=t?`${t}.${e}`:e,n=a?a[e]:i?.[e];if("ui_friendly_name"===e||"optional_params"===e&&"nested"===r.type&&r.fields||j&&y.has(e))return null;if("nested"===r.type&&r.fields)return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"mb-2 font-medium",children:e}),(0,l.jsx)("div",{className:"ml-4 border-l-2 border-gray-200 pl-4",children:_(r.fields,s,n)})]},s);let o=void 0!==n?n:r.default_value??("percentage"===r.type?.5:void 0);return(0,l.jsx)(u.Form.Item,{name:s,label:e,tooltip:r.description,rules:r.required?[{required:!0,message:`${e} is required`}]:void 0,initialValue:o,children:"select"===r.type&&r.options?(0,l.jsx)(x.Select,{placeholder:r.description,defaultValue:n||r.default_value,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"multiselect"===r.type&&r.options?(0,l.jsx)(x.Select,{mode:"multiple",placeholder:r.description,defaultValue:n||r.default_value,children:r.options.map(e=>(0,l.jsx)(x.Select.Option,{value:e,children:e},e))}):"bool"===r.type||"boolean"===r.type?(0,l.jsxs)(x.Select,{placeholder:r.description,children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"True"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"False"})]}):"percentage"===r.type&&null!=r.min&&null!=r.max?(0,l.jsx)(eC.Slider,{min:r.min,max:r.max,step:r.step??.1,marks:{[r.min]:"0%",[(r.min+r.max)/2]:"50%",[r.max]:"100%"}}):"number"===r.type?(0,l.jsx)(ej.default,{step:1,width:400,placeholder:r.description,defaultValue:void 0!==n?Number(n):void 0}):e.includes("password")||e.includes("secret")||e.includes("key")?(0,l.jsx)(p.Input.Password,{placeholder:r.description,defaultValue:n||""}):(0,l.jsx)(p.Input,{placeholder:r.description,defaultValue:n||""})},s)});return(0,l.jsx)(l.Fragment,{children:_(f)})};var ek=e.i(592968),eS=e.i(750113);let eI=({availableModels:e,form:t})=>(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{style:{background:"#f6ffed",border:"1px solid #b7eb8f",borderRadius:6,padding:"10px 14px",marginBottom:16,fontSize:13,color:"#389e0d"},children:["After each LLM response, the ",(0,l.jsx)("strong",{children:"Judge Model"})," scores it 0–100 against your criteria. If the weighted average falls below the threshold, the response is blocked (or logged)."]}),(0,l.jsx)(u.Form.Item,{name:"judge_model",label:(0,l.jsxs)("span",{children:["Judge Model ",(0,l.jsx)(ek.Tooltip,{title:"The LLM that reads each response and grades it. Pick a capable model — it never sees end-user data beyond what the LLM returned.",children:(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),rules:[{required:!0,message:"Select a judge model"}],children:(0,l.jsx)(x.Select,{showSearch:!0,placeholder:"Select a model",options:e.map(e=>({label:e,value:e}))})}),(0,l.jsx)(u.Form.Item,{name:"overall_threshold",label:(0,l.jsxs)("span",{children:["Minimum Score to Pass ",(0,l.jsx)(ek.Tooltip,{title:"0–100. If the weighted average of criterion scores falls below this, the guardrail triggers. 80 is a good default.",children:(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),initialValue:80,children:(0,l.jsx)(J.InputNumber,{min:0,max:100,addonAfter:"/ 100",style:{width:"100%"}})}),(0,l.jsx)(u.Form.Item,{name:"on_failure",label:(0,l.jsxs)("span",{children:["On Failure ",(0,l.jsx)(ek.Tooltip,{title:"Block: return HTTP 422 when the score is too low. Log: record the result but let the response through.",children:(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),initialValue:"block",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"block",children:"Block (return 422)"}),(0,l.jsx)(x.Select.Option,{value:"log",children:"Log only"})]})}),(0,l.jsx)(u.Form.Item,{label:(0,l.jsxs)("span",{children:["Evaluation Criteria ",(0,l.jsx)(ek.Tooltip,{title:"Each criterion is something the judge checks. Weights must add up to 100%.",children:(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#8c8c8c"}})})]}),children:(0,l.jsx)(u.Form.List,{name:"criteria",initialValue:[{name:"",weight:100,description:""}],children:(e,{add:a,remove:r})=>(0,l.jsxs)(l.Fragment,{children:[e.map(({key:e,name:t,...a})=>(0,l.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,padding:"12px 12px 0",marginBottom:8},children:[(0,l.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"flex-end"},children:[(0,l.jsx)(u.Form.Item,{...a,name:[t,"name"],rules:[{required:!0,message:"Enter criterion name"}],style:{flex:2,marginBottom:8},children:(0,l.jsx)(p.Input,{placeholder:"Criterion name (e.g. Policy accuracy)"})}),(0,l.jsx)(u.Form.Item,{...a,name:[t,"weight"],label:(0,l.jsx)(ek.Tooltip,{title:"How much this criterion counts toward the final score. All weights must add up to 100%.",children:(0,l.jsxs)("span",{style:{fontSize:12,color:"#595959"},children:["Weight ",(0,l.jsx)(eS.QuestionCircleOutlined,{style:{color:"#bfbfbf"}})]})}),rules:[{required:!0,message:"Enter weight"}],style:{flex:1,marginBottom:8},children:(0,l.jsx)(J.InputNumber,{min:0,max:100,addonAfter:"%",style:{width:"100%"},placeholder:"e.g. 50"})}),(0,l.jsx)("div",{style:{marginBottom:8},children:(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",onClick:()=>r(t),children:"×"})})]}),(0,l.jsx)(u.Form.Item,{...a,name:[t,"description"],rules:[{required:!0,message:"Describe what to check"}],style:{marginBottom:8},children:(0,l.jsx)(p.Input,{placeholder:"What should the judge check for this criterion?"})})]},e)),(0,l.jsx)(i.Button,{type:"dashed",block:!0,style:{marginTop:4},onClick:()=>a({name:"",weight:0,description:""}),icon:(0,l.jsx)(d.PlusOutlined,{}),children:"Add Criterion"}),e.length>0&&(0,l.jsx)(u.Form.Item,{shouldUpdate:!0,noStyle:!0,children:()=>{let e=(t.getFieldValue("criteria")||[]).reduce((e,t)=>e+(Number(t?.weight)||0),0),a=100===e;return(0,l.jsxs)("div",{style:{marginTop:6,fontSize:12,color:a?"#52c41a":"#faad14"},children:["Weights total: ",e,"%",a?" ✓":" — must add up to 100%"]})}})]})})})]});var eA=e.i(536916),eO=e.i(149192),eT=e.i(741585),eT=eT,eP=e.i(724154);e.i(247167);var eL=e.i(931067);let eB={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M880.1 154H143.9c-24.5 0-39.8 26.7-27.5 48L349 597.4V838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V597.4L907.7 202c12.2-21.3-3.1-48-27.6-48zM603.4 798H420.6V642h182.9v156zm9.6-236.6l-9.5 16.6h-183l-9.5-16.6L212.7 226h598.6L613 561.4z"}}]},name:"filter",theme:"outlined"};var eF=e.i(9583),e$=r.forwardRef(function(e,t){return r.createElement(eF.default,(0,eL.default)({},e,{ref:t,icon:eB}))});let{Text:eE}=f.Typography,{Option:eM}=x.Select,eR=({categories:e,selectedCategories:t,onChange:a})=>(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center mb-2",children:[(0,l.jsx)(e$,{className:"text-gray-500 mr-1"}),(0,l.jsx)(eE,{className:"text-gray-500 font-medium",children:"Filter by category"})]}),(0,l.jsx)(x.Select,{mode:"multiple",placeholder:"Select categories to filter by",style:{width:"100%"},onChange:a,value:t,allowClear:!0,showSearch:!0,optionFilterProp:"children",className:"mb-4",tagRender:e=>(0,l.jsx)(h.Tag,{color:"blue",closable:e.closable,onClose:e.onClose,className:"mr-2 mb-2",children:e.label}),children:e.map(e=>(0,l.jsx)(eM,{value:e.category,children:e.category},e.category))})]}),eG=({onSelectAll:e,onUnselectAll:t,hasSelectedEntities:a})=>(0,l.jsxs)("div",{className:"bg-gray-50 p-5 rounded-lg mb-6 border border-gray-200 shadow-xs",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-4",children:[(0,l.jsxs)("div",{className:"flex items-center",children:[(0,l.jsx)(eE,{strong:!0,className:"text-gray-700 text-base",children:"Quick Actions"}),(0,l.jsx)(ek.Tooltip,{title:"Apply action to all PII types at once",children:(0,l.jsx)("div",{className:"ml-2 text-gray-400 cursor-help text-xs",children:"ⓘ"})})]}),(0,l.jsx)(i.Button,{color:"danger",variant:"outlined",onClick:t,disabled:!a,icon:(0,l.jsx)(eO.CloseOutlined,{}),children:"Unselect All"})]}),(0,l.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,l.jsx)(i.Button,{color:"primary",variant:"outlined",onClick:()=>e("MASK"),className:"h-10",block:!0,icon:(0,l.jsx)(eT.default,{}),children:"Select All & Mask"}),(0,l.jsx)(i.Button,{color:"danger",variant:"outlined",onClick:()=>e("BLOCK"),className:"h-10 hover:bg-red-100",block:!0,icon:(0,l.jsx)(eP.StopOutlined,{}),children:"Select All & Block"})]})]}),ez=({entities:e,selectedEntities:t,selectedActions:a,actions:r,onEntitySelect:i,onActionSelect:s,entityToCategoryMap:n})=>(0,l.jsxs)("div",{className:"border rounded-lg overflow-hidden shadow-xs",children:[(0,l.jsxs)("div",{className:"bg-gray-50 px-5 py-3 border-b flex",children:[(0,l.jsx)(eE,{strong:!0,className:"flex-1 text-gray-700",children:"PII Type"}),(0,l.jsx)(eE,{strong:!0,className:"w-32 text-right text-gray-700",children:"Action"})]}),(0,l.jsx)("div",{className:"max-h-[400px] overflow-y-auto",children:0===e.length?(0,l.jsx)("div",{className:"py-10 text-center text-gray-500",children:"No PII types match your filter criteria"}):e.map(e=>(0,l.jsxs)("div",{className:`px-5 py-3 flex items-center justify-between hover:bg-gray-50 border-b ${t.includes(e)?"bg-blue-50":""}`,children:[(0,l.jsxs)("div",{className:"flex items-center flex-1",children:[(0,l.jsx)(eA.Checkbox,{checked:t.includes(e),onChange:()=>i(e),className:"mr-3"}),(0,l.jsx)(eE,{className:t.includes(e)?"font-medium text-gray-900":"text-gray-700",children:e.replace(/_/g," ")}),n.get(e)&&(0,l.jsx)(h.Tag,{className:"ml-2 text-xs",color:"blue",children:n.get(e)})]}),(0,l.jsx)("div",{className:"w-32",children:(0,l.jsx)(x.Select,{value:t.includes(e)&&a[e]||"MASK",onChange:t=>s(e,t),style:{width:120},disabled:!t.includes(e),className:`${!t.includes(e)?"opacity-50":""}`,dropdownMatchSelectWidth:!1,children:r.map(e=>(0,l.jsx)(eM,{value:e,children:(0,l.jsxs)("div",{className:"flex items-center",children:[(e=>{switch(e){case"MASK":return(0,l.jsx)(eT.default,{style:{marginRight:4}});case"BLOCK":return(0,l.jsx)(eP.StopOutlined,{style:{marginRight:4}});default:return null}})(e),e]})},e))})})]},e))})]}),{Title:eD,Text:eK}=f.Typography,eq=({entities:e,actions:t,selectedEntities:a,selectedActions:i,onEntitySelect:s,onActionSelect:n,entityCategories:o=[]})=>{let[d,c]=(0,r.useState)([]),m=new Map;o.forEach(e=>{e.entities.forEach(t=>{m.set(t,e.category)})});let u=e.filter(e=>0===d.length||d.includes(m.get(e)||""));return(0,l.jsxs)("div",{className:"pii-configuration",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-5",children:[(0,l.jsx)("div",{className:"flex items-center",children:(0,l.jsx)(eD,{level:4,className:"m-0! font-semibold text-gray-800",children:"Configure PII Protection"})}),(0,l.jsxs)(eK,{className:"text-gray-500",children:[a.length," items selected"]})]}),(0,l.jsxs)("div",{className:"mb-6",children:[(0,l.jsx)(eR,{categories:o,selectedCategories:d,onChange:c}),(0,l.jsx)(eG,{onSelectAll:t=>{e.forEach(e=>{a.includes(e)||s(e),n(e,t)})},onUnselectAll:()=>{a.forEach(e=>{s(e)})},hasSelectedEntities:a.length>0})]}),(0,l.jsx)(ez,{entities:u,selectedEntities:a,selectedActions:i,actions:t,onEntitySelect:s,onActionSelect:n,entityToCategoryMap:m})]})};var eH=e.i(304967),eU=e.i(599724),eJ=e.i(312361),eW=e.i(21548),eV=e.i(827252);let eY={rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""},eQ=({value:e,onChange:t,disabled:a=!1})=>{let r={...eY,...e||{},rules:e?.rules?[...e.rules]:[]},s=e=>{let a={...r,...e};t?.(a)},n=(e,t)=>{s({rules:r.rules.map((a,l)=>l===e?{...a,...t}:a)})},o=(e,t)=>{let a=r.rules[e];if(!a)return;let l=Object.entries(a.allowed_param_patterns||{});t(l);let i={};l.forEach(([e,t])=>{i[e]=t}),n(e,{allowed_param_patterns:Object.keys(i).length>0?i:void 0})};return(0,l.jsxs)(eH.Card,{children:[(0,l.jsxs)("div",{className:"flex items-center justify-between",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-lg font-semibold",children:"LiteLLM Tool Permission Guardrail"}),(0,l.jsx)(eU.Text,{className:"text-sm text-gray-500",children:"Provide regex patterns (e.g., ^mcp__github_.*$) for tool names or types and optionally constrain payload fields."})]}),!a&&(0,l.jsx)(i.Button,{icon:(0,l.jsx)(d.PlusOutlined,{}),type:"primary",onClick:()=>{s({rules:[...r.rules,{id:`rule_${Math.random().toString(36).slice(2,8)}`,decision:"allow",allowed_param_patterns:void 0}]})},className:"bg-blue-600! text-white! hover:bg-blue-500!",children:"Add Rule"})]}),(0,l.jsx)(eJ.Divider,{}),0===r.rules.length?(0,l.jsx)(eW.Empty,{description:"No tool rules added yet"}):(0,l.jsx)("div",{className:"space-y-4",children:r.rules.map((e,t)=>{let d;return(0,l.jsxs)(eH.Card,{className:"bg-gray-50",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,l.jsxs)(eU.Text,{className:"font-semibold",children:["Rule ",t+1]}),(0,l.jsx)(i.Button,{icon:(0,l.jsx)(L.DeleteOutlined,{}),danger:!0,type:"text",disabled:a,onClick:()=>{s({rules:r.rules.filter((e,a)=>a!==t)})},children:"Remove"})]}),(0,l.jsxs)("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Rule ID"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"unique_rule_id",value:e.id,onChange:e=>n(t,{id:e.target.value})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Tool Name (optional)"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^mcp__github_.*$",value:e.tool_name??"",onChange:e=>n(t,{tool_name:""===e.target.value.trim()?void 0:e.target.value})})]})]}),(0,l.jsx)("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 mt-4",children:(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Tool Type (optional)"}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^function$",value:e.tool_type??"",onChange:e=>n(t,{tool_type:""===e.target.value.trim()?void 0:e.target.value})})]})}),(0,l.jsxs)("div",{className:"mt-4 flex flex-col gap-2",children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Decision"}),(0,l.jsxs)(x.Select,{disabled:a,value:e.decision,style:{width:200},onChange:e=>n(t,{decision:e}),children:[(0,l.jsx)(x.Select.Option,{value:"allow",children:"Allow"}),(0,l.jsx)(x.Select.Option,{value:"deny",children:"Deny"})]})]}),(0,l.jsx)("div",{className:"mt-4",children:0===(d=Object.entries(e.allowed_param_patterns||{})).length?(0,l.jsx)(i.Button,{disabled:a,size:"small",onClick:()=>n(t,{allowed_param_patterns:{"":""}}),children:"+ Restrict tool arguments (optional)"}):(0,l.jsxs)("div",{className:"space-y-2",children:[(0,l.jsx)(eU.Text,{className:"text-sm text-gray-600",children:"Argument constraints (dot or array paths)"}),d.map(([r,s],n)=>(0,l.jsxs)(j.Space,{align:"start",children:[(0,l.jsx)(p.Input,{disabled:a,placeholder:"messages[0].content",value:r,onChange:e=>{var a;return a=e.target.value,void o(t,e=>{if(!e[n])return;let[,t]=e[n];e[n]=[a,t]})}}),(0,l.jsx)(p.Input,{disabled:a,placeholder:"^email@.*$",value:s,onChange:e=>{var a;return a=e.target.value,void o(t,e=>{if(!e[n])return;let[t]=e[n];e[n]=[t,a]})}}),(0,l.jsx)(i.Button,{disabled:a,icon:(0,l.jsx)(L.DeleteOutlined,{}),danger:!0,onClick:()=>o(t,e=>{e.splice(n,1)})})]},`${e.id||t}-${n}`)),(0,l.jsx)(i.Button,{disabled:a,size:"small",onClick:()=>n(t,{allowed_param_patterns:{...e.allowed_param_patterns||{},"":""}}),children:"+ Add another constraint"})]})})]},e.id||t)})}),(0,l.jsx)(eJ.Divider,{}),(0,l.jsxs)("div",{className:"grid gap-4 md:grid-cols-2",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Default action"}),(0,l.jsxs)(x.Select,{disabled:a,value:r.default_action,onChange:e=>s({default_action:e}),children:[(0,l.jsx)(x.Select.Option,{value:"allow",children:"Allow"}),(0,l.jsx)(x.Select.Option,{value:"deny",children:"Deny"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)(eU.Text,{className:"text-sm font-medium flex items-center gap-1",children:["On disallowed action",(0,l.jsx)(ek.Tooltip,{title:"Block returns an error when a forbidden tool is invoked. Rewrite strips the tool call but lets the rest of the response continue.",children:(0,l.jsx)(eV.InfoCircleOutlined,{})})]}),(0,l.jsxs)(x.Select,{disabled:a,value:r.on_disallowed_action,onChange:e=>s({on_disallowed_action:e}),children:[(0,l.jsx)(x.Select.Option,{value:"block",children:"Block"}),(0,l.jsx)(x.Select.Option,{value:"rewrite",children:"Rewrite"})]})]})]}),(0,l.jsxs)("div",{className:"mt-4",children:[(0,l.jsx)(eU.Text,{className:"text-sm font-medium",children:"Violation message (optional)"}),(0,l.jsx)(p.Input.TextArea,{disabled:a,rows:3,placeholder:"This violates our org policy...",value:r.violation_message_template,onChange:e=>s({violation_message_template:e.target.value})})]})]})},{Title:eX,Text:eZ,Link:e0}=f.Typography,{Option:e1}=x.Select,e2={pre_call:"Before LLM Call - Runs before the LLM call and checks the input (Recommended)",during_call:"During LLM Call - Runs in parallel with the LLM call, with response held until check completes",post_call:"After LLM Call - Runs after the LLM call and checks only the output",logging_only:"Logging Only - Only runs on logging callbacks without affecting the LLM call",pre_mcp_call:"Before MCP Tool Call - Runs before MCP tool execution and validates tool calls",during_mcp_call:"During MCP Tool Call - Runs in parallel with MCP tool execution for monitoring"},e4=()=>({rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""}),e5=({visible:e,onClose:t,accessToken:a,onSuccess:s,preset:n})=>{let[o]=u.Form.useForm(),[d,c]=(0,r.useState)(!1),[f,j]=(0,r.useState)(null),[_,b]=(0,r.useState)(null),[v,w]=(0,r.useState)([]),[C,N]=(0,r.useState)({}),[k,S]=(0,r.useState)(0),[I,A]=(0,r.useState)(null),[O,T]=(0,r.useState)([]),[P,L]=(0,r.useState)(2),[B,F]=(0,r.useState)({}),[$,E]=(0,r.useState)([]),[M,R]=(0,r.useState)([]),[G,z]=(0,r.useState)([]),[D,K]=(0,r.useState)(""),[q,H]=(0,r.useState)(!1),[U,J]=(0,r.useState)(null),[W,V]=(0,r.useState)(""),[Y,Q]=(0,r.useState)(void 0),[X,Z]=(0,r.useState)("warn"),[ee,el]=(0,r.useState)(""),[er,eg]=(0,r.useState)(!1),[eh,ef]=(0,r.useState)([]),[ey,ej]=(0,r.useState)(e4),e_=(0,r.useMemo)(()=>!!f&&"tool_permission"===(en[f]||"").toLowerCase(),[f]);(0,r.useEffect)(()=>{a&&(async()=>{try{let[e,t,l]=await Promise.all([(0,m.getGuardrailUISettings)(a),(0,m.getGuardrailProviderSpecificParams)(a),(0,m.modelAvailableCall)(a,"","").catch(()=>null)]);b(e),A(t),l?.data&&ef(l.data.map(e=>e.id)),ei(t),eo(t)}catch(e){console.error("Error fetching guardrail data:",e),y.default.fromBackend("Failed to load guardrail configuration")}})()},[a]),(0,r.useEffect)(()=>{if(!n||!e||!_)return;j(n.provider);let t={provider:n.provider,guardrail_name:n.guardrailNameSuggestion,mode:n.mode,default_on:n.defaultOn,skip_system_message_choice:"inherit",skip_tool_message_choice:"inherit"};if("BlockCodeExecution"===n.provider&&(t.confidence_threshold=.5),o.setFieldsValue(t),n.categoryName&&_.content_filter_settings?.content_categories){let e=_.content_filter_settings.content_categories.find(e=>e.name===n.categoryName);e&&z([{id:`category-${Date.now()}`,category:e.name,display_name:e.display_name,action:e.default_action,severity_threshold:"medium"}])}},[n,e,_,o]);let eb=e=>{j(e);let t={config:void 0,presidio_analyzer_api_base:void 0,presidio_anonymizer_api_base:void 0};"BlockCodeExecution"===e&&(t.confidence_threshold=.5);let a=en[e]?.toLowerCase(),l=a&&_?.supported_modes_by_provider?_.supported_modes_by_provider[a]:void 0;if(l){let e=ed(o.getFieldValue("mode")),a=e.filter(e=>l.includes(e));a.length!==e.length&&(t.mode=a.length>0?a:void 0)}o.setFieldsValue(t),w([]),N({}),T([]),L(2),F({}),E([]),R([]),z([]),K(""),H(!1),J(null),ej(e4()),"LlmAsAJudge"===e&&o.setFieldsValue({mode:"post_call"})},ew=e=>{w(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},eC=(e,t)=>{N(a=>({...a,[e]:t}))},ek=async()=>{try{if(0===k&&(await o.validateFields(["guardrail_name","provider","mode","default_on"]),f)){let e=["guardrail_name","provider","mode","default_on"];"PresidioPII"===f&&e.push("presidio_analyzer_api_base","presidio_anonymizer_api_base"),await o.validateFields(e)}if(1===k&&em(f)&&0===v.length)return void y.default.fromBackend("Please select at least one PII entity to continue");S(k+1)}catch(e){console.error("Form validation failed:",e)}},eS=()=>{o.resetFields(),j(null),w([]),N({}),T([]),L(2),F({}),E([]),R([]),z([]),K(""),ej(e4()),V(""),Q(void 0),Z("warn"),el(""),eg(!1),S(0)},eA=()=>{eS(),t()},eO=async()=>{try{var e,l;c(!0),await o.validateFields();let r=o.getFieldsValue(!0),i=en[r.provider],n={guardrail_name:r.guardrail_name,litellm_params:{guardrail:i,mode:r.mode,default_on:r.default_on},guardrail_info:{}},d=(e=r.skip_system_message_choice,"yes"===e||"no"!==e&&void 0);void 0!==d&&(n.litellm_params.skip_system_message_in_guardrail=d);let u=(l=r.skip_tool_message_choice,"yes"===l||"no"!==l&&void 0);if(void 0!==u&&(n.litellm_params.skip_tool_message_in_guardrail=u),"PresidioPII"===r.provider&&v.length>0){let e={};v.forEach(t=>{e[t]=C[t]||"MASK"}),n.litellm_params.pii_entities_config=e,r.presidio_analyzer_api_base&&(n.litellm_params.presidio_analyzer_api_base=r.presidio_analyzer_api_base),r.presidio_anonymizer_api_base&&(n.litellm_params.presidio_anonymizer_api_base=r.presidio_anonymizer_api_base)}if(eu(r.provider)){let e=q&&(U?.brand_self?.length??0)>0;if(!($.length>0||M.length>0||G.length>0)&&!e){y.default.fromBackend("Please configure at least one content filter setting (category, pattern, keyword, or competitor intent)"),c(!1);return}$.length>0&&(n.litellm_params.patterns=$.map(e=>({pattern_type:"prebuilt"===e.type?"prebuilt":"regex",pattern_name:"prebuilt"===e.type?e.name:void 0,pattern:"custom"===e.type?e.pattern:void 0,name:e.name,action:e.action}))),M.length>0&&(n.litellm_params.blocked_words=M.map(e=>({keyword:e.keyword,action:e.action,description:e.description}))),G.length>0&&(n.litellm_params.categories=G.map(e=>({category:e.category,enabled:!0,action:e.action,severity_threshold:e.severity_threshold||"medium"}))),e&&U&&(n.litellm_params.competitor_intent_config={competitor_intent_type:U.competitor_intent_type??"airline",brand_self:U.brand_self,locations:(U.locations?.length??0)>0?U.locations:void 0,competitors:"generic"===U.competitor_intent_type&&(U.competitors?.length??0)>0?U.competitors:void 0,policy:U.policy,threshold_high:U.threshold_high,threshold_medium:U.threshold_medium,threshold_low:U.threshold_low})}else if(r.config)try{n.guardrail_info=JSON.parse(r.config)}catch(e){y.default.fromBackend("Invalid JSON in configuration"),c(!1);return}if("llm_as_a_judge"===i){let e=r.criteria||[];if(0===e.length){y.default.fromBackend("Add at least one evaluation criterion"),c(!1);return}let t=e.reduce((e,t)=>e+(Number(t?.weight)||0),0);if(100!==t){y.default.fromBackend(`Criterion weights must sum to 100% (currently ${t}%)`),c(!1);return}n.litellm_params.judge_model=r.judge_model,n.litellm_params.overall_threshold=r.overall_threshold??80,n.litellm_params.on_failure=r.on_failure??"block",n.litellm_params.criteria=e.map(e=>({name:e.name,weight:Number(e.weight),description:e.description||""}))}if("tool_permission"===i){if(0===ey.rules.length){y.default.fromBackend("Add at least one tool permission rule"),c(!1);return}n.litellm_params.rules=ey.rules,n.litellm_params.default_action=ey.default_action,n.litellm_params.on_disallowed_action=ey.on_disallowed_action,ey.violation_message_template&&(n.litellm_params.violation_message_template=ey.violation_message_template)}if(eu(r.provider)&&(void 0!==Y&&Y>0&&(n.litellm_params.end_session_after_n_fails=Y),X&&"realtime"===W&&(n.litellm_params.on_violation=X),ee.trim()&&(n.litellm_params.realtime_violation_message=ee.trim())),I&&f&&"llm_as_a_judge"!==i){let e=I[en[f]?.toLowerCase()]||{},t=new Set;Object.keys(e).forEach(e=>{"optional_params"!==e&&t.add(e)}),e.optional_params&&e.optional_params.fields&&Object.keys(e.optional_params.fields).forEach(e=>{t.add(e)}),t.forEach(e=>{let t=r[e];(null==t||""===t)&&(t=r.optional_params?.[e]),null!=t&&""!==t&&(n.litellm_params[e]=t)})}if(!a)throw Error("No access token available");await (0,m.createGuardrailCall)(a,n),y.default.success("Guardrail created successfully"),eS(),s(),t()}catch(e){console.error("Failed to create guardrail:",e),y.default.fromBackend("Failed to create guardrail: "+(e instanceof Error?e.message:String(e)))}finally{c(!1)}},eT=e=>{if(!_||!eu(f))return null;let t=_.content_filter_settings;return t?(0,l.jsx)(et,{prebuiltPatterns:t.prebuilt_patterns||[],categories:t.pattern_categories||[],selectedPatterns:$,blockedWords:M,onPatternAdd:e=>E([...$,e]),onPatternRemove:e=>E($.filter(t=>t.id!==e)),onPatternActionChange:(e,t)=>{E($.map(a=>a.id===e?{...a,action:t}:a))},onBlockedWordAdd:e=>R([...M,e]),onBlockedWordRemove:e=>R(M.filter(t=>t.id!==e)),onBlockedWordUpdate:(e,t,a)=>{R(M.map(l=>l.id===e?{...l,[t]:a}:l))},contentCategories:t.content_categories||[],selectedContentCategories:G,onContentCategoryAdd:e=>z([...G,e]),onContentCategoryRemove:e=>z(G.filter(t=>t.id!==e)),onContentCategoryUpdate:(e,t,a)=>{z(G.map(l=>l.id===e?{...l,[t]:a}:l))},pendingCategorySelection:D,onPendingCategorySelectionChange:K,accessToken:a,showStep:e,competitorIntentEnabled:q,competitorIntentConfig:U,onCompetitorIntentChange:(e,t)=>{H(e),J(t)}}):null},eP=eu(f)?[{title:"Basic Info",optional:!1},{title:"Topics",optional:!1},{title:"Patterns",optional:!1},{title:"Keywords",optional:!1},{title:"Endpoint Settings (Optional)",optional:!0}]:em(f)?[{title:"Basic Info",optional:!1},{title:"PII Configuration",optional:!1}]:[{title:"Basic Info",optional:!1},{title:"Provider Configuration",optional:!1}];return(0,l.jsx)(g.Modal,{title:null,open:e,onCancel:eA,maskClosable:!1,footer:null,width:1e3,closable:!1,className:"top-8",styles:{body:{padding:0}},children:(0,l.jsxs)("div",{className:"flex flex-col",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between px-6 py-4 border-b border-gray-200",children:[(0,l.jsx)("h3",{className:"text-base font-semibold text-gray-900 m-0",children:"Create guardrail"}),(0,l.jsx)("button",{onClick:eA,className:"text-gray-400 hover:text-gray-600 bg-transparent border-none cursor-pointer text-base leading-none p-1",children:"✕"})]}),(0,l.jsx)("div",{className:"overflow-auto px-6 py-4",style:{maxHeight:"calc(80vh - 120px)"},children:(0,l.jsx)(u.Form,{form:o,layout:"vertical",initialValues:{mode:"pre_call",default_on:!1,skip_system_message_choice:"inherit",skip_tool_message_choice:"inherit"},children:eP.map((e,t)=>{let r=t{r&&S(t)},style:{minHeight:24},children:[(0,l.jsx)("span",{className:"text-sm",style:{fontWeight:i?600:500,color:i?"#1e293b":r?"#4f46e5":"#94a3b8"},children:e.title}),e.optional&&!i&&(0,l.jsx)("span",{className:"text-[11px] text-slate-400",children:"optional"}),r&&(0,l.jsx)("span",{className:"text-[11px] text-indigo-500 hover:underline",children:"Edit"})]}),i&&(0,l.jsx)("div",{className:"mt-3",children:(()=>{switch(k){case 0:let e;return e=!e_&&!eu(f)&&!ep(f),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(u.Form.Item,{name:"guardrail_name",label:"Guardrail Name",rules:[{required:!0,message:"Please enter a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"Enter a name for this guardrail"})}),(0,l.jsx)(u.Form.Item,{name:"provider",label:"Guardrail Provider",rules:[{required:!0,message:"Please select a provider"}],children:(0,l.jsx)(x.Select,{placeholder:"Select a guardrail provider",onChange:eb,labelInValue:!1,optionLabelProp:"label",dropdownRender:e=>e,showSearch:!0,children:Object.entries(es()).map(([e,t])=>(0,l.jsx)(e1,{value:e,label:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ex[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ex[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]}),children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ex[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ex[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]})},e))})}),(0,l.jsx)(u.Form.Item,{name:"mode",label:"Mode",tooltip:"How the guardrail should be applied",rules:[{required:!0,message:"Please select a mode"}],children:(0,l.jsx)(x.Select,{optionLabelProp:"label",mode:"multiple",children:ec(_,f)?.map(e=>(0,l.jsx)(e1,{value:e,label:e,children:(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:e}),"pre_call"===e&&(0,l.jsx)(h.Tag,{color:"green",style:{marginLeft:"8px"},children:"Recommended"})]}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2[e]})]})},e))||(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(e1,{value:"pre_call",label:"pre_call",children:(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"pre_call"})," ",(0,l.jsx)(h.Tag,{color:"green",children:"Recommended"})]}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2.pre_call})]})}),(0,l.jsx)(e1,{value:"during_call",label:"during_call",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"during_call"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2.during_call})]})}),(0,l.jsx)(e1,{value:"post_call",label:"post_call",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"post_call"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2.post_call})]})}),(0,l.jsx)(e1,{value:"logging_only",label:"logging_only",children:(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{children:(0,l.jsx)("strong",{children:"logging_only"})}),(0,l.jsx)("div",{style:{fontSize:"12px",color:"#888"},children:e2.logging_only})]})})]})})}),(0,l.jsx)(u.Form.Item,{name:"default_on",label:"Always On",tooltip:"If enabled, this guardrail will be applied to all requests by default.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"Yes"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"No"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_system_message_choice",label:"Skip system messages in guardrail",tooltip:"Unified guardrails only: omit role: system from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_tool_message_choice",label:"Skip tool messages in guardrail",tooltip:"Unified guardrails only: omit role: tool from guardrail evaluation input (OpenAI chat + Anthropic messages). The model still receives full messages. Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),e&&(0,l.jsx)(eN,{selectedProvider:f,accessToken:a,providerParams:I})]});case 1:if(em(f))return _&&"PresidioPII"===f?(0,l.jsx)(eq,{entities:_.supported_entities,actions:_.supported_actions,selectedEntities:v,selectedActions:C,onEntitySelect:ew,onActionSelect:eC,entityCategories:_.pii_entity_categories}):null;if(eu(f))return eT("categories");if(ep(f))return(0,l.jsx)(eI,{availableModels:eh,form:o});if(!f)return null;if(e_)return(0,l.jsx)(eQ,{value:ey,onChange:ej});if(!I)return null;let t=en[f]?.toLowerCase(),r=I&&I[t];return r&&r.optional_params?(0,l.jsx)(ev,{optionalParams:r.optional_params,parentFieldKey:"optional_params"}):null;case 2:if(eu(f))return eT("patterns");return null;case 3:if(eu(f))return eT("keywords");return null;case 4:return(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsx)("div",{children:(0,l.jsxs)("p",{className:"text-sm text-gray-500",children:["Configure settings for a specific call type. Most guardrails don't need this — skip it unless you're using a specific endpoint like ",(0,l.jsx)("code",{children:"/v1/realtime"}),"."]})}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"Call type"}),(0,l.jsx)(x.Select,{placeholder:"Select a call type",value:W||void 0,onChange:e=>{V(e),eg(!1)},style:{width:260},allowClear:!0,options:[{value:"realtime",label:"/v1/realtime"}]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"More call types coming soon."})]}),"realtime"===W&&(0,l.jsxs)("div",{className:"border border-gray-200 rounded-lg overflow-hidden",children:[(0,l.jsxs)("button",{type:"button",onClick:()=>eg(e=>!e),className:"w-full flex items-center justify-between px-4 py-3 bg-gray-50 hover:bg-gray-100 text-sm font-medium text-gray-700",children:[(0,l.jsx)("span",{children:"/v1/realtime settings"}),(0,l.jsx)("svg",{className:`w-4 h-4 text-gray-500 transition-transform ${er?"rotate-180":""}`,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,l.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"})})]}),er&&(0,l.jsxs)("div",{className:"space-y-5 px-4 py-4 border-t border-gray-200",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"End session after X violations"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Automatically close the session after this many guardrail violations. Leave empty to never auto-close."}),(0,l.jsx)("input",{type:"number",min:1,placeholder:"e.g. 3",value:Y??"",onChange:e=>Q(e.target.value?parseInt(e.target.value,10):void 0),className:"border border-gray-300 rounded-sm px-3 py-1.5 text-sm w-32"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:"On violation"}),(0,l.jsx)("div",{className:"space-y-2",children:["warn","end_session"].map(e=>(0,l.jsxs)("label",{className:"flex items-start gap-2 cursor-pointer",children:[(0,l.jsx)("input",{type:"radio",name:"on_violation",value:e,checked:X===e,onChange:()=>Z(e),className:"mt-0.5"}),(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"text-sm font-medium text-gray-800",children:"warn"===e?"Warn":"End session"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 m-0",children:"warn"===e?"Bot speaks the message, session continues":"Bot speaks the message, connection closes immediately"})]})]},e))})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:"Message the user hears"}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"What the bot says aloud when this guardrail fires. Falls back to the default violation message if empty."}),(0,l.jsx)("textarea",{rows:3,placeholder:"e.g. I'm not able to continue this conversation. Please contact us at 1-800-774-2678.",value:ee,onChange:e=>el(e.target.value),className:"border border-gray-300 rounded-sm px-3 py-2 text-sm w-full resize-none"})]})]})]})]});default:return null}})()})]})]},t)})})}),(0,l.jsxs)("div",{className:"flex items-center justify-end space-x-3 px-6 py-3 border-t border-gray-200",children:[(0,l.jsx)(i.Button,{onClick:eA,children:"Cancel"}),k>0&&(0,l.jsx)(i.Button,{onClick:()=>{S(k-1)},children:"Previous"}),k{let d,c,[h]=u.Form.useForm(),[f,j]=(0,r.useState)(!1),[_,b]=(0,r.useState)(o?.provider||null),[v,w]=(0,r.useState)(null),[C,N]=(0,r.useState)([]),[k,S]=(0,r.useState)({});(0,r.useEffect)(()=>{(async()=>{try{if(!a)return;let e=await (0,m.getGuardrailUISettings)(a);w(e)}catch(e){console.error("Error fetching guardrail settings:",e),y.default.fromBackend("Failed to load guardrail settings")}})()},[a]),(0,r.useEffect)(()=>{o?.pii_entities_config&&Object.keys(o.pii_entities_config).length>0&&(N(Object.keys(o.pii_entities_config)),S(o.pii_entities_config))},[o]);let I=e=>{N(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},A=(e,t)=>{S(a=>({...a,[e]:t}))},O=async()=>{try{j(!0);let e=await h.validateFields(),l=en[e.provider],r=n&&"object"==typeof n?{...n}:{};r.guardrail=l,r.mode=e.mode,r.default_on=e.default_on;let o=e.skip_system_message_choice;"yes"===o?r.skip_system_message_in_guardrail=!0:"no"===o?r.skip_system_message_in_guardrail=!1:delete r.skip_system_message_in_guardrail;let d=e.skip_tool_message_choice;"yes"===d?r.skip_tool_message_in_guardrail=!0:"no"===d?r.skip_tool_message_in_guardrail=!1:delete r.skip_tool_message_in_guardrail;let c={};if("PresidioPII"===e.provider&&C.length>0){let e={};C.forEach(t=>{e[t]=k[t]||"MASK"}),r.pii_entities_config=e}else if(e.config)try{let t=JSON.parse(e.config);"Bedrock"===e.provider&&t?(t.guardrail_id&&(r.guardrailIdentifier=t.guardrail_id),t.guardrail_version&&(r.guardrailVersion=t.guardrail_version)):c=t}catch(e){y.default.fromBackend("Invalid JSON in configuration"),j(!1);return}let u={guardrail_id:s,guardrail:{guardrail_name:e.guardrail_name,litellm_params:r,guardrail_info:c}};if(!a)throw Error("No access token available");let p=`/guardrails/${s}`,g=await fetch(p,{method:"PUT",headers:{[(0,m.getGlobalLitellmHeaderName)()]:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify(u)});if(!g.ok){let e=await g.text();throw Error(e||"Failed to update guardrail")}y.default.success("Guardrail updated successfully"),i(),t()}catch(e){console.error("Failed to update guardrail:",e),y.default.fromBackend("Failed to update guardrail: "+(e instanceof Error?e.message:String(e)))}finally{j(!1)}};return(0,l.jsx)(g.Modal,{title:"Edit Guardrail",open:e,onCancel:t,footer:null,width:700,children:(0,l.jsxs)(u.Form,{form:h,layout:"vertical",initialValues:o,children:[(0,l.jsx)(u.Form.Item,{name:"guardrail_name",label:"Guardrail Name",rules:[{required:!0,message:"Please enter a guardrail name"}],children:(0,l.jsx)(tu.TextInput,{placeholder:"Enter a name for this guardrail"})}),(0,l.jsx)(u.Form.Item,{name:"provider",label:"Guardrail Provider",rules:[{required:!0,message:"Please select a provider"}],children:(0,l.jsx)(x.Select,{placeholder:"Select a guardrail provider",onChange:e=>{b(e),h.setFieldsValue({config:void 0}),N([]),S({})},disabled:!0,optionLabelProp:"label",children:Object.entries(es()).map(([e,t])=>(0,l.jsx)(tx,{value:e,label:t,children:(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[ex[t]&&(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(ex[t]),alt:"",style:{height:"20px",width:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,l.jsx)("span",{children:t})]})},e))})}),(0,l.jsx)(u.Form.Item,{name:"mode",label:"Mode",tooltip:"How the guardrail should be applied",rules:[{required:!0,message:"Please select a mode"}],children:(0,l.jsx)(x.Select,{children:(d=ec(v,_)??["pre_call","post_call"],[...c=ed(o?.mode).filter(e=>!d.includes(e)),...d].map(e=>(0,l.jsx)(tx,{value:e,children:c.includes(e)?`${e} (not supported by ${_}, pick another)`:e},e)))})}),(0,l.jsx)(u.Form.Item,{name:"default_on",label:"Always On",tooltip:"If enabled, this guardrail will be applied to all requests by default",valuePropName:"checked",children:(0,l.jsx)(U.Switch,{})}),(0,l.jsx)(u.Form.Item,{name:"skip_system_message_choice",label:"Skip system messages in guardrail",tooltip:"Unified guardrails only: whether role: system content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(tx,{value:"inherit",children:"Use global default"}),(0,l.jsx)(tx,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(tx,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{name:"skip_tool_message_choice",label:"Skip tool messages in guardrail",tooltip:"Unified guardrails only: whether role: tool content is omitted from guardrail input (LLM still receives full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(tx,{value:"inherit",children:"Use global default"}),(0,l.jsx)(tx,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(tx,{value:"no",children:"No — always include in scan"})]})}),(()=>{if(!_)return null;if("PresidioPII"===_)return v&&_&&"PresidioPII"===_?(0,l.jsx)(eq,{entities:v.supported_entities,actions:v.supported_actions,selectedEntities:C,selectedActions:k,onEntitySelect:I,onActionSelect:A,entityCategories:v.pii_entity_categories}):null;switch(_){case"Aporia":return(0,l.jsx)(u.Form.Item,{label:"Aporia Configuration",name:"config",tooltip:"JSON configuration for Aporia",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ + "api_key": "your_aporia_api_key", + "project_name": "your_project_name" +}`})});case"AimSecurity":return(0,l.jsx)(u.Form.Item,{label:"Aim Security Configuration",name:"config",tooltip:"JSON configuration for Aim Security",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ + "api_key": "your_aim_api_key" +}`})});case"Bedrock":return(0,l.jsx)(u.Form.Item,{label:"Amazon Bedrock Configuration",name:"config",tooltip:"JSON configuration for Amazon Bedrock guardrails",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ + "guardrail_id": "your_guardrail_id", + "guardrail_version": "your_guardrail_version" +}`})});case"CatoNetworks":return(0,l.jsx)(u.Form.Item,{label:"Cato Networks Configuration",name:"config",tooltip:"JSON configuration for Cato Networks",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ + "api_key": "your_cato_api_key" +}`})});case"GuardrailsAI":return(0,l.jsx)(u.Form.Item,{label:"Guardrails.ai Configuration",name:"config",tooltip:"JSON configuration for Guardrails.ai",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ + "api_key": "your_guardrails_api_key", + "guardrail_id": "your_guardrail_id" +}`})});case"LakeraAI":return(0,l.jsx)(u.Form.Item,{label:"Lakera AI Configuration",name:"config",tooltip:"JSON configuration for Lakera AI",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ + "api_key": "your_lakera_api_key" +}`})});case"PromptInjection":return(0,l.jsx)(u.Form.Item,{label:"Prompt Injection Configuration",name:"config",tooltip:"JSON configuration for prompt injection detection",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ + "threshold": 0.8 +}`})});default:return(0,l.jsx)(u.Form.Item,{label:"Custom Configuration",name:"config",tooltip:"JSON configuration for your custom guardrail",children:(0,l.jsx)(p.Input.TextArea,{rows:4,placeholder:`{ + "key1": "value1", + "key2": "value2" +}`})})}})(),(0,l.jsxs)("div",{className:"flex justify-end space-x-2 mt-4",children:[(0,l.jsx)(tm.Button,{variant:"secondary",onClick:t,children:"Cancel"}),(0,l.jsx)(tm.Button,{onClick:O,loading:f,children:"Update Guardrail"})]})]})})};var tf=((a={}).DB="db",a.CONFIG="config",a);let ty=({guardrailsList:e,isLoading:t,onDeleteClick:a,accessToken:i,onGuardrailUpdated:s,isAdmin:n=!1,onGuardrailClick:o})=>{let[d,c]=(0,r.useState)([{id:"created_at",desc:!0}]),[m,u]=(0,r.useState)(!1),[p,g]=(0,r.useState)(null),x=[{header:"Guardrail ID",accessorKey:"guardrail_id",cell:e=>(0,l.jsx)(tn.IdCell,{value:e.getValue(),onClick:o})},{header:"Name",accessorKey:"guardrail_name",cell:({row:e})=>{let t=e.original;return(0,l.jsx)(ek.Tooltip,{title:t.guardrail_name,children:(0,l.jsx)("span",{className:"text-xs font-medium",children:t.guardrail_name||"-"})})}},{header:"Provider",accessorKey:"litellm_params.guardrail",cell:({row:e})=>{let{logo:t,displayName:a}=eh(e.original.litellm_params.guardrail);return(0,l.jsxs)("div",{className:"flex items-center space-x-2",children:[t&&(0,l.jsx)("img",{src:t,alt:`${a} logo`,className:"w-4 h-4",onError:e=>{e.target.style.display="none"}}),(0,l.jsx)("span",{className:"text-xs",children:a})]})}},{header:"Mode",accessorKey:"litellm_params.mode",cell:({row:e})=>{let t=e.original;return(0,l.jsx)("span",{className:"text-xs",children:t.litellm_params.mode})}},{header:"Default On",accessorKey:"litellm_params.default_on",cell:({row:e})=>{let t=!!e.original.litellm_params?.default_on;return(0,l.jsx)(to.StatusBadge,{tone:t?"success":"neutral",label:t?"Default On":"Default Off"})}},{header:"Created At",accessorKey:"created_at",cell:({row:e})=>(0,l.jsx)(ts.DateCell,{value:e.original.created_at})},{header:"Updated At",accessorKey:"updated_at",cell:({row:e})=>(0,l.jsx)(ts.DateCell,{value:e.original.updated_at})},{id:"actions",header:"Actions",cell:({row:e})=>{let t=e.original,r=t.guardrail_definition_location===tf.CONFIG;return(0,l.jsx)("div",{className:"flex space-x-2",children:r?(0,l.jsx)(ek.Tooltip,{title:"Config guardrail cannot be deleted on the dashboard. Please delete it from the config file.",children:(0,l.jsx)(tt.Icon,{"data-testid":"config-delete-icon",icon:ta.TrashIcon,size:"sm",className:"cursor-not-allowed text-gray-400",title:"Config guardrail cannot be deleted on the dashboard. Please delete it from the config file.","aria-label":"Delete guardrail (config)"})}):(0,l.jsx)(ek.Tooltip,{title:"Delete guardrail",children:(0,l.jsx)(tt.Icon,{icon:ta.TrashIcon,size:"sm",onClick:()=>t.guardrail_id&&a(t.guardrail_id,t.guardrail_name||"Unnamed Guardrail"),className:"cursor-pointer hover:text-red-500"})})})}}],h=(0,td.useReactTable)({data:e,columns:x,state:{sorting:d},onSortingChange:c,getCoreRowModel:(0,tc.getCoreRowModel)(),getSortedRowModel:(0,tc.getSortedRowModel)(),enableSorting:!0});return(0,l.jsxs)("div",{className:"rounded-lg custom-border relative",children:[(0,l.jsx)("div",{className:"overflow-x-auto",children:(0,l.jsxs)(e8.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,l.jsx)(e7.TableHead,{children:h.getHeaderGroups().map(e=>(0,l.jsx)(te.TableRow,{children:e.headers.map(e=>(0,l.jsx)(e9.TableHeaderCell,{className:`py-1 h-8 ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,onClick:e.column.getToggleSortingHandler(),children:(0,l.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,l.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,td.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&(0,l.jsx)("div",{className:"w-4",children:e.column.getIsSorted()?({asc:(0,l.jsx)(tr.ChevronUpIcon,{className:"h-4 w-4 text-blue-500"}),desc:(0,l.jsx)(ti.ChevronDownIcon,{className:"h-4 w-4 text-blue-500"})})[e.column.getIsSorted()]:(0,l.jsx)(tl.SwitchVerticalIcon,{className:"h-4 w-4 text-gray-400"})})]})},e.id))},e.id))}),(0,l.jsx)(e6.TableBody,{children:t?(0,l.jsx)(te.TableRow,{children:(0,l.jsx)(e3.TableCell,{colSpan:x.length,className:"h-8 text-center",children:(0,l.jsx)("div",{className:"text-center text-gray-500",children:(0,l.jsx)("p",{children:"Loading..."})})})}):e.length>0?h.getRowModel().rows.map(e=>(0,l.jsx)(te.TableRow,{className:"h-8",children:e.getVisibleCells().map(e=>(0,l.jsx)(e3.TableCell,{className:`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap ${"actions"===e.column.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,children:(0,td.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,l.jsx)(te.TableRow,{children:(0,l.jsx)(e3.TableCell,{colSpan:x.length,className:"h-8 text-center",children:(0,l.jsx)("div",{className:"text-center text-gray-500",children:(0,l.jsx)("p",{children:"No guardrails found"})})})})})]})}),p&&(0,l.jsx)(th,{visible:m,onClose:()=>u(!1),accessToken:i,onSuccess:()=>{u(!1),g(null),s()},guardrailId:p.guardrail_id||"",fullLitellmParams:p.litellm_params,initialValues:{guardrail_name:p.guardrail_name||"",provider:Object.keys(en).find(e=>en[e]===p?.litellm_params.guardrail)||"",mode:p.litellm_params.mode,default_on:p.litellm_params.default_on,pii_entities_config:p.litellm_params.pii_entities_config,skip_system_message_choice:ef(p.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:ey(p.litellm_params?.skip_tool_message_in_guardrail),...p.guardrail_info}})]})};var tj=e.i(708347),t_=e.i(500330),eT=eT,tb=e.i(530212),tv=e.i(389083),tw=e.i(350967),tC=e.i(197647),tN=e.i(653824),tk=e.i(881073),tS=e.i(404206),tI=e.i(723731),tA=e.i(629569),tO=e.i(678784),tT=e.i(118366),tP=e.i(560445);let{Text:tL}=f.Typography,{Option:tB}=x.Select,tF=({categories:e,onActionChange:t,onSeverityChange:a,onRemove:r,readOnly:s=!1})=>{let n=[{title:"Category",dataIndex:"display_name",key:"display_name",render:(e,t)=>(0,l.jsxs)("div",{children:[(0,l.jsx)(tL,{strong:!0,children:e}),e!==t.category&&(0,l.jsx)("div",{children:(0,l.jsx)(tL,{type:"secondary",style:{fontSize:12},children:t.category})})]})},{title:"Severity Threshold",dataIndex:"severity_threshold",key:"severity_threshold",width:180,render:(e,t)=>s?(0,l.jsx)(h.Tag,{color:{high:"red",medium:"orange",low:"yellow"}[e],children:e.toUpperCase()}):(0,l.jsxs)(x.Select,{value:e,onChange:e=>a?.(t.id,e),style:{width:150},size:"small",children:[(0,l.jsx)(tB,{value:"high",children:"High"}),(0,l.jsx)(tB,{value:"medium",children:"Medium"}),(0,l.jsx)(tB,{value:"low",children:"Low"})]})},{title:"Action",dataIndex:"action",key:"action",width:150,render:(e,a)=>s?(0,l.jsx)(h.Tag,{color:"BLOCK"===e?"red":"blue",children:e}):(0,l.jsxs)(x.Select,{value:e,onChange:e=>t?.(a.id,e),style:{width:120},size:"small",children:[(0,l.jsx)(tB,{value:"BLOCK",children:"Block"}),(0,l.jsx)(tB,{value:"MASK",children:"Mask"})]})}];return(s||n.push({title:"",key:"actions",width:100,render:(e,t)=>(0,l.jsx)(i.Button,{type:"text",danger:!0,size:"small",icon:(0,l.jsx)(L.DeleteOutlined,{}),onClick:()=>r?.(t.id),children:"Delete"})}),0===e.length)?(0,l.jsx)("div",{style:{textAlign:"center",padding:"40px 0",color:"#999"},children:"No categories configured."}):(0,l.jsx)(P.Table,{dataSource:e,columns:n,rowKey:"id",pagination:!1,size:"small"})},t$=({patterns:e,blockedWords:t,categories:a=[],readOnly:r=!0,onPatternActionChange:i,onPatternRemove:s,onBlockedWordUpdate:n,onBlockedWordRemove:o,onCategoryActionChange:d,onCategorySeverityChange:c,onCategoryRemove:m})=>{if(0===e.length&&0===t.length&&0===a.length)return null;let u=()=>{};return(0,l.jsxs)(l.Fragment,{children:[a.length>0&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eU.Text,{className:"text-lg font-semibold",children:"Content Categories"}),(0,l.jsxs)(tv.Badge,{color:"blue",children:[a.length," categories configured"]})]}),(0,l.jsx)(tF,{categories:a,onActionChange:r?void 0:d,onSeverityChange:r?void 0:c,onRemove:r?void 0:m,readOnly:r})]}),e.length>0&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eU.Text,{className:"text-lg font-semibold",children:"Pattern Detection"}),(0,l.jsxs)(tv.Badge,{color:"blue",children:[e.length," patterns configured"]})]}),(0,l.jsx)($,{patterns:e,onActionChange:r?u:i||u,onRemove:r?u:s||u})]}),t.length>0&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(eU.Text,{className:"text-lg font-semibold",children:"Blocked Keywords"}),(0,l.jsxs)(tv.Badge,{color:"blue",children:[t.length," keywords configured"]})]}),(0,l.jsx)(R,{keywords:t,onActionChange:r?u:n||u,onRemove:r?u:o||u})]})]})},{Text:tE}=f.Typography,tM=({guardrailData:e,guardrailSettings:t,isEditing:a,accessToken:i,onDataChange:s,onUnsavedChanges:n})=>{let[o,d]=(0,r.useState)([]),[c,m]=(0,r.useState)([]),[u,p]=(0,r.useState)([]),[g,x]=(0,r.useState)([]),[h,f]=(0,r.useState)([]),[y,j]=(0,r.useState)([]),[_,b]=(0,r.useState)(!1),[v,w]=(0,r.useState)(null),[C,N]=(0,r.useState)(!1),[k,S]=(0,r.useState)(null);(0,r.useEffect)(()=>{if(e?.litellm_params?.patterns){let t=e.litellm_params.patterns.map((e,t)=>({id:`pattern-${t}`,type:"prebuilt"===e.pattern_type?"prebuilt":"custom",name:e.pattern_name||e.name,display_name:e.display_name,pattern:e.pattern,action:e.action||"BLOCK"}));d(t),x(t)}else d([]),x([]);if(e?.litellm_params?.blocked_words){let t=e.litellm_params.blocked_words.map((e,t)=>({id:`word-${t}`,keyword:e.keyword,action:e.action||"BLOCK",description:e.description}));m(t),f(t)}else m([]),f([]);if(e?.litellm_params?.categories?.length>0){let a=t?.content_filter_settings?.content_categories?Object.fromEntries(t.content_filter_settings.content_categories.map(e=>[e.name,e])):{},l=e.litellm_params.categories.map((e,t)=>{let l=a[e.category];return{id:`category-${t}`,category:e.category,display_name:l?.display_name??e.category,action:e.action||"BLOCK",severity_threshold:e.severity_threshold||"medium"}});p(l),j(l)}else p([]),j([]);let a=e?.litellm_params?.competitor_intent_config;if(a&&"object"==typeof a){let e=!!(a.brand_self&&Array.isArray(a.brand_self)&&a.brand_self.length>0),t={competitor_intent_type:a.competitor_intent_type??"airline",brand_self:Array.isArray(a.brand_self)?a.brand_self:[],locations:Array.isArray(a.locations)?a.locations:[],competitors:Array.isArray(a.competitors)?a.competitors:[],policy:a.policy??{competitor_comparison:"refuse",possible_competitor_comparison:"reframe"},threshold_high:"number"==typeof a.threshold_high?a.threshold_high:.7,threshold_medium:"number"==typeof a.threshold_medium?a.threshold_medium:.45,threshold_low:"number"==typeof a.threshold_low?a.threshold_low:.3};b(e),w(t),N(e),S(t)}else b(!1),w(null),N(!1),S(null)},[e,t?.content_filter_settings?.content_categories]),(0,r.useEffect)(()=>{s&&s(o,c,u,_,v)},[o,c,u,_,v,s]);let I=r.default.useMemo(()=>{let e=JSON.stringify(o)!==JSON.stringify(g),t=JSON.stringify(c)!==JSON.stringify(h),a=JSON.stringify(u)!==JSON.stringify(y),l=_!==C||JSON.stringify(v)!==JSON.stringify(k);return e||t||a||l},[o,c,u,_,v,g,h,y,C,k]);return((0,r.useEffect)(()=>{a&&n&&n(I)},[I,a,n]),e?.litellm_params?.guardrail!=="litellm_content_filter")?null:a?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eJ.Divider,{orientation:"left",children:"Content Filter Configuration"}),I&&(0,l.jsx)(tP.Alert,{type:"warning",showIcon:!0,className:"mb-4",message:(0,l.jsx)(tE,{children:'You have unsaved changes to patterns or keywords. Remember to click "Save Changes" at the bottom.'})}),(0,l.jsx)("div",{className:"mb-6",children:t&&t.content_filter_settings&&(0,l.jsx)(et,{prebuiltPatterns:t.content_filter_settings.prebuilt_patterns||[],categories:t.content_filter_settings.pattern_categories||[],selectedPatterns:o,blockedWords:c,onPatternAdd:e=>d([...o,e]),onPatternRemove:e=>d(o.filter(t=>t.id!==e)),onPatternActionChange:(e,t)=>d(o.map(a=>a.id===e?{...a,action:t}:a)),onBlockedWordAdd:e=>m([...c,e]),onBlockedWordRemove:e=>m(c.filter(t=>t.id!==e)),onBlockedWordUpdate:(e,t,a)=>m(c.map(l=>l.id===e?{...l,[t]:a}:l)),onFileUpload:e=>{},accessToken:i,contentCategories:t.content_filter_settings.content_categories||[],selectedContentCategories:u,onContentCategoryAdd:e=>p([...u,e]),onContentCategoryRemove:e=>p(u.filter(t=>t.id!==e)),onContentCategoryUpdate:(e,t,a)=>p(u.map(l=>l.id===e?{...l,[t]:a}:l)),competitorIntentEnabled:_,competitorIntentConfig:v,onCompetitorIntentChange:(e,t)=>{b(e),w(t)}})})]}):(0,l.jsx)(t$,{patterns:o,blockedWords:c,categories:u,readOnly:!0})};var tR=e.i(788191),tG=e.i(245704),tz=e.i(518617);let tD={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M715.8 493.5L335 165.1c-14.2-12.2-35-1.2-35 18.5v656.8c0 19.7 20.8 30.7 35 18.5l380.8-328.4c10.9-9.4 10.9-27.6 0-37z"}}]},name:"caret-right",theme:"outlined"};var tK=r.forwardRef(function(e,t){return r.createElement(eF.default,(0,eL.default)({},e,{ref:t,icon:tD}))}),tq=e.i(987432);let tH={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M892 772h-80v-80c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v80h-80c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h80v80c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-80h80c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM373.5 498.4c-.9-8.7-1.4-17.5-1.4-26.4 0-15.9 1.5-31.4 4.3-46.5.7-3.6-1.2-7.3-4.5-8.8-13.6-6.1-26.1-14.5-36.9-25.1a127.54 127.54 0 01-38.7-95.4c.9-32.1 13.8-62.6 36.3-85.6 24.7-25.3 57.9-39.1 93.2-38.7 31.9.3 62.7 12.6 86 34.4 7.9 7.4 14.7 15.6 20.4 24.4 2 3.1 5.9 4.4 9.3 3.2 17.6-6.1 36.2-10.4 55.3-12.4 5.6-.6 8.8-6.6 6.3-11.6-32.5-64.3-98.9-108.7-175.7-109.9-110.8-1.7-203.2 89.2-203.2 200 0 62.8 28.9 118.8 74.2 155.5-31.8 14.7-61.1 35-86.5 60.4-54.8 54.7-85.8 126.9-87.8 204a8 8 0 008 8.2h56.1c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5 29.4-29.4 65.4-49.8 104.7-59.7 3.8-1.1 6.4-4.8 5.9-8.8zM824 472c0-109.4-87.9-198.3-196.9-200C516.3 270.3 424 361.2 424 472c0 62.8 29 118.8 74.2 155.5a300.95 300.95 0 00-86.4 60.4C357 742.6 326 814.8 324 891.8a8 8 0 008 8.2h56c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5C505.8 695.7 563 672 624 672c110.4 0 200-89.5 200-200zm-109.5 90.5C690.3 586.7 658.2 600 624 600s-66.3-13.3-90.5-37.5a127.26 127.26 0 01-37.5-91.8c.3-32.8 13.4-64.5 36.3-88 24-24.6 56.1-38.3 90.4-38.7 33.9-.3 66.8 12.9 91 36.6 24.8 24.3 38.4 56.8 38.4 91.4-.1 34.2-13.4 66.3-37.6 90.5z"}}]},name:"usergroup-add",theme:"outlined"};var tU=r.forwardRef(function(e,t){return r.createElement(eF.default,(0,eL.default)({},e,{ref:t,icon:tH}))}),tJ=e.i(872934);let{Panel:tW}=G.Collapse,{TextArea:tV}=p.Input,tY={empty:{name:"Empty Template",code:`async def apply_guardrail(inputs, request_data, input_type): + # inputs: {texts, images, tools, tool_calls, structured_messages, model} + # request_data: {model, user_id, team_id, end_user_id, metadata} + # input_type: "request" or "response" + return allow()`},blockSSN:{name:"Block SSN",code:`def apply_guardrail(inputs, request_data, input_type): + for text in inputs["texts"]: + if regex_match(text, r"\\d{3}-\\d{2}-\\d{4}"): + return block("SSN detected") + return allow()`},redactEmail:{name:"Redact Emails",code:`def apply_guardrail(inputs, request_data, input_type): + pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}" + modified = [] + for text in inputs["texts"]: + modified.append(regex_replace(text, pattern, "[EMAIL REDACTED]")) + return modify(texts=modified)`},blockSQL:{name:"Block SQL Injection",code:`def apply_guardrail(inputs, request_data, input_type): + if input_type != "request": + return allow() + for text in inputs["texts"]: + if contains_code_language(text, ["sql"]): + return block("SQL code not allowed") + return allow()`},validateJSON:{name:"Validate JSON",code:`def apply_guardrail(inputs, request_data, input_type): + if input_type != "response": + return allow() + + schema = {"type": "object", "required": ["name", "value"]} + + for text in inputs["texts"]: + obj = json_parse(text) + if obj is None: + return block("Invalid JSON response") + if not json_schema_valid(obj, schema): + return block("Response missing required fields") + return allow()`},externalAPI:{name:"External API Check (async)",code:`async def apply_guardrail(inputs, request_data, input_type): + # Call an external moderation API (async for non-blocking) + for text in inputs["texts"]: + response = await http_post( + "https://api.example.com/moderate", + body={"text": text, "user_id": request_data["user_id"]}, + headers={"Authorization": "Bearer YOUR_API_KEY"}, + timeout=10 + ) + + if not response["success"]: + # API call failed, allow by default or block + return allow() + + if response["body"].get("flagged"): + return block(response["body"].get("reason", "Content flagged")) + + return allow()`}},tQ={"Return Values":[{name:"allow()",desc:"Let request/response through"},{name:"block(reason)",desc:"Reject with message"},{name:"modify(texts=[], images=[], tool_calls=[])",desc:"Transform content"}],"HTTP Requests (async)":[{name:"await http_request(url, method, headers, body)",desc:"Make async HTTP request"},{name:"await http_get(url, headers)",desc:"Async GET request"},{name:"await http_post(url, body, headers)",desc:"Async POST request"}],"Regex Functions":[{name:"regex_match(text, pattern)",desc:"Returns True if pattern found"},{name:"regex_replace(text, pattern, replacement)",desc:"Replace all matches"},{name:"regex_find_all(text, pattern)",desc:"Return list of matches"}],"JSON Functions":[{name:"json_parse(text)",desc:"Parse JSON string, returns None on error"},{name:"json_stringify(obj)",desc:"Convert to JSON string"},{name:"json_schema_valid(obj, schema)",desc:"Validate against JSON schema"}],"URL Functions":[{name:"extract_urls(text)",desc:"Extract all URLs from text"},{name:"is_valid_url(url)",desc:"Check if URL is valid"},{name:"all_urls_valid(text)",desc:"Check all URLs in text are valid"}],"Code Detection":[{name:"detect_code(text)",desc:"Returns True if code detected"},{name:"detect_code_languages(text)",desc:"Returns list of detected languages"},{name:'contains_code_language(text, ["sql"])',desc:"Check for specific languages"}],"Text Utilities":[{name:"contains(text, substring)",desc:"Check if substring exists"},{name:"contains_any(text, [substr1, substr2])",desc:"Check if any substring exists"},{name:"word_count(text)",desc:"Count words"},{name:"char_count(text)",desc:"Count characters"},{name:"lower(text) / upper(text) / trim(text)",desc:"String transforms"}]},tX=[{value:"pre_call",label:"pre_call (Request)"},{value:"post_call",label:"post_call (Response)"},{value:"during_call",label:"during_call (Parallel)"},{value:"logging_only",label:"logging_only"},{value:"pre_mcp_call",label:"pre_mcp_call (Before MCP Tool Call)"},{value:"post_mcp_call",label:"post_mcp_call (After MCP Tool Call)"},{value:"during_mcp_call",label:"during_mcp_call (During MCP Tool Call)"}],tZ=({visible:e,onClose:t,onSuccess:a,accessToken:i,editData:s})=>{let n=!!s,[o,d]=(0,r.useState)(""),[u,p]=(0,r.useState)(["pre_call"]),[h,f]=(0,r.useState)(!1),[j,_]=(0,r.useState)("empty"),[b,v]=(0,r.useState)(tY.empty.code),[w,C]=(0,r.useState)(!1),[N,k]=(0,r.useState)(!1),[S,I]=(0,r.useState)(!1),A={texts:["Hello, my SSN is 123-45-6789"],images:[],tools:[{type:"function",function:{name:"get_weather",description:"Get the current weather in a location",parameters:{type:"object",properties:{location:{type:"string",description:"City name"}},required:["location"]}}}],tool_calls:[],structured_messages:[{role:"system",content:"You are a helpful assistant."},{role:"user",content:"Hello, my SSN is 123-45-6789"}],model:"gpt-4"},O={texts:["The weather in San Francisco is 72°F and sunny."],images:[],tools:[],tool_calls:[{id:"call_abc123",type:"function",function:{name:"get_weather",arguments:'{"location": "San Francisco"}'}}],structured_messages:[],model:"gpt-4"},T={texts:['Tool: read_wiki_structure\nArguments: {"repoName": "BerriAI/litellm"}'],images:[],tools:[{type:"function",function:{name:"read_wiki_structure",description:"Read the structure of a GitHub repository (MCP tool passed as OpenAI tool)",parameters:{type:"object",properties:{repoName:{type:"string",description:"Repository name, e.g. BerriAI/litellm"}},required:["repoName"]}}}],tool_calls:[{id:"call_mcp_001",type:"function",function:{name:"read_wiki_structure",arguments:'{"repoName": "BerriAI/litellm"}'}}],structured_messages:[{role:"user",content:'Tool: read_wiki_structure\nArguments: {"repoName": "BerriAI/litellm"}'}],model:"mcp-tool-call"},[P,L]=(0,r.useState)(JSON.stringify(A,null,2)),[B,F]=(0,r.useState)(null),[$,E]=(0,r.useState)(null),M=(0,r.useRef)(null),R=e=>null==e?["pre_call"]:Array.isArray(e)?e.length?e:["pre_call"]:[e];(0,r.useEffect)(()=>{e&&(s?(d(s.guardrail_name||""),p(R(s.litellm_params?.mode)),f(s.litellm_params?.default_on||!1),v(s.litellm_params?.custom_code||tY.empty.code),_("")):(d(""),p(["pre_call"]),f(!1),_("empty"),v(tY.empty.code)),F(null),I(!1))},[e,s]);let z=async e=>{try{await navigator.clipboard.writeText(e),E(e),setTimeout(()=>E(null),2e3)}catch(e){console.error("Failed to copy:",e)}},D=async()=>{if(!o.trim())return void y.default.fromBackend("Please enter a guardrail name");if(!b.trim())return void y.default.fromBackend("Please enter custom code");if(!i)return void y.default.fromBackend("No access token available");C(!0);try{if(n&&s){let e={litellm_params:{custom_code:b}};o!==s.guardrail_name&&(e.guardrail_name=o);let t=R(s.litellm_params?.mode);(u.length!==t.length||u.some((e,a)=>e!==t[a]))&&(e.litellm_params.mode=u),h!==s.litellm_params?.default_on&&(e.litellm_params.default_on=h),await (0,m.updateGuardrailCall)(i,s.guardrail_id,e),y.default.success("Custom code guardrail updated successfully")}else await (0,m.createGuardrailCall)(i,{guardrail_name:o,litellm_params:{guardrail:"custom_code",mode:u,default_on:h,custom_code:b},guardrail_info:{}}),y.default.success("Custom code guardrail created successfully");a(),t()}catch(e){console.error("Failed to save guardrail:",e),y.default.fromBackend(`Failed to ${n?"update":"create"} guardrail: `+(e instanceof Error?e.message:String(e)))}finally{C(!1)}},K=async()=>{if(!i)return void F({error:"No access token available"});k(!0),F(null);try{let e;try{e=JSON.parse(P)}catch(e){F({error:"Invalid test input JSON"}),k(!1);return}e.texts||(e.texts=[]);let t=["pre_call","pre_mcp_call"],a=["post_call","post_mcp_call"],l=u.some(e=>t.includes(e))?"request":u.some(e=>a.includes(e))?"response":"request",r=await (0,m.testCustomCodeGuardrail)(i,{custom_code:b,test_input:e,input_type:l,request_data:{model:"test-model",metadata:{}}});r.success&&r.result?F(r.result):r.error?F({error:r.error,error_type:r.error_type}):F({error:"Unknown error occurred"})}catch(e){console.error("Failed to test custom code:",e),F({error:e instanceof Error?e.message:"Failed to test custom code"})}finally{k(!1)}},q=b.split("\n").length;return(0,l.jsxs)(g.Modal,{open:e,onCancel:t,footer:null,width:1400,className:"custom-code-modal",closable:!0,destroyOnClose:!0,children:[(0,l.jsxs)("div",{className:"flex flex-col h-[80vh]",children:[(0,l.jsxs)("div",{className:"pb-4 border-b border-gray-200",children:[(0,l.jsx)("h2",{className:"text-xl font-semibold text-gray-900",children:n?"Edit Custom Guardrail":"Create Custom Guardrail"}),(0,l.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"Define custom logic using Python-like syntax"})]}),(0,l.jsxs)("div",{className:"flex items-center gap-4 py-4 border-b border-gray-100",children:[(0,l.jsxs)("div",{className:"flex-1 max-w-[200px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Guardrail Name"}),(0,l.jsx)(tu.TextInput,{value:o,onValueChange:d,placeholder:"e.g., block-pii-custom"})]}),(0,l.jsxs)("div",{className:"w-[280px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Mode (can select multiple)"}),(0,l.jsx)(x.Select,{mode:"multiple",value:u,onChange:p,options:tX,className:"w-full",size:"middle",placeholder:"Select modes"})]}),(0,l.jsxs)("div",{className:"w-[180px]",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Template"}),(0,l.jsx)(x.Select,{value:j,onChange:e=>{_(e),v(tY[e].code)},className:"w-full",size:"middle",dropdownRender:e=>(0,l.jsxs)(l.Fragment,{children:[e,(0,l.jsx)(eJ.Divider,{style:{margin:"8px 0"}}),(0,l.jsxs)("div",{style:{padding:"8px 12px",cursor:"pointer",color:"#1890ff",fontSize:"12px",display:"flex",alignItems:"center",gap:"4px"},onClick:e=>{e.preventDefault(),window.open("https://models.litellm.ai/guardrails","_blank")},onMouseEnter:e=>{e.currentTarget.style.backgroundColor="#f0f0f0"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent"},children:[(0,l.jsx)(tU,{}),(0,l.jsx)("span",{children:"Browse Community templates"}),(0,l.jsx)(tJ.ExportOutlined,{style:{fontSize:"10px"}})]})]}),children:(0,l.jsx)(x.Select.OptGroup,{label:"STANDARD",children:Object.entries(tY).map(([e,t])=>(0,l.jsx)(x.Select.Option,{value:e,children:t.name},e))})})]}),(0,l.jsxs)("div",{className:"flex items-center gap-2 pt-5",children:[(0,l.jsx)("span",{className:"text-sm text-gray-600",children:"Default On"}),(0,l.jsx)(U.Switch,{checked:h,onChange:f})]})]}),(0,l.jsxs)("div",{className:"flex flex-1 overflow-hidden mt-4 gap-6",children:[(0,l.jsxs)("div",{className:"flex-2 flex flex-col min-w-0 overflow-y-auto",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2 shrink-0",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wide",children:"Python Logic"}),(0,l.jsx)("span",{className:"text-xs text-gray-400",children:"Restricted environment (no imports)"})]}),(0,l.jsxs)("div",{className:"relative rounded-lg overflow-hidden border border-gray-700 bg-[#1e1e1e] shrink-0",style:{minHeight:"300px",maxHeight:"400px"},children:[(0,l.jsx)("div",{className:"absolute left-0 top-0 bottom-0 w-12 bg-[#1e1e1e] border-r border-gray-700 text-right pr-3 pt-3 select-none overflow-hidden",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace",fontSize:"14px",lineHeight:"1.6"},children:Array.from({length:Math.max(q,20)},(e,t)=>(0,l.jsx)("div",{className:"text-gray-500 h-[22.4px]",children:t+1},t+1))}),(0,l.jsx)("textarea",{ref:M,value:b,onChange:e=>v(e.target.value),onKeyDown:e=>{if("Tab"===e.key){e.preventDefault();let t=e.currentTarget,a=t.selectionStart,l=t.selectionEnd;v(b.substring(0,a)+" "+b.substring(l)),setTimeout(()=>{t.selectionStart=t.selectionEnd=a+4},0)}},spellCheck:!1,className:"w-full h-full pl-14 pr-4 pt-3 pb-3 resize-none focus:outline-hidden bg-transparent text-gray-200",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace",fontSize:"14px",lineHeight:"1.6",tabSize:4}})]}),(0,l.jsx)(G.Collapse,{activeKey:S?["test"]:[],onChange:e=>I(e.includes("test")),className:"mt-3 bg-white border border-gray-200 rounded-lg shrink-0",expandIcon:({isActive:e})=>(0,l.jsx)(tK,{rotate:90*!!e}),children:(0,l.jsx)(tW,{header:(0,l.jsxs)("span",{className:"flex items-center gap-2 text-sm font-medium",children:[(0,l.jsx)(tR.PlayCircleOutlined,{className:"text-blue-500"}),"Test Your Guardrail"]}),children:(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2",children:[(0,l.jsx)("label",{className:"block text-xs font-medium text-gray-600",children:"Test Input (JSON)"}),(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("span",{className:"text-xs text-gray-500",children:"Load example:"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(A,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-orange-200 bg-orange-50 text-orange-700 hover:bg-orange-100 transition-colors",children:"Pre-call"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(T,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-purple-200 bg-purple-50 text-purple-700 hover:bg-purple-100 transition-colors",children:"Pre MCP"}),(0,l.jsx)("button",{type:"button",onClick:()=>L(JSON.stringify(O,null,2)),className:"px-2 py-1 text-xs rounded-sm border border-green-200 bg-green-50 text-green-700 hover:bg-green-100 transition-colors",children:"Post-call"})]})]}),(0,l.jsx)("div",{className:"mb-2 p-2 bg-gray-50 rounded-sm text-xs text-gray-600 border border-gray-200",children:(0,l.jsxs)("div",{className:"grid grid-cols-2 gap-x-4 gap-y-1",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"texts"}),": Message content (always)"]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"images"}),": Base64 images (vision)"]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"tools"}),": Tool definitions ",(0,l.jsx)("span",{className:"text-orange-600",children:"(pre_call)"}),", MCP as OpenAI tool ",(0,l.jsx)("span",{className:"text-purple-600",children:"(pre_mcp_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"tool_calls"}),": LLM tool calls"," ",(0,l.jsx)("span",{className:"text-green-600",children:"(post_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"structured_messages"}),": Full messages"," ",(0,l.jsx)("span",{className:"text-orange-600",children:"(pre_call)"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("strong",{children:"model"}),": Model name (always)"]})]})}),(0,l.jsx)(tV,{value:P,onChange:e=>L(e.target.value),rows:8,className:"font-mono text-xs",placeholder:'{"texts": ["test message"], ...}'})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)(tm.Button,{size:"xs",onClick:K,disabled:N,icon:tR.PlayCircleOutlined,children:N?"Running...":"Run Test"}),B&&(0,l.jsx)("div",{className:`flex items-center gap-2 text-sm ${B.error?"text-red-600":"allow"===B.action?"text-green-600":"block"===B.action?"text-orange-600":"text-blue-600"}`,children:B.error?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tz.CloseCircleOutlined,{}),(0,l.jsxs)("span",{children:[B.error_type&&(0,l.jsxs)("span",{className:"font-medium",children:["[",B.error_type,"] "]}),B.error]})]}):"allow"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tG.CheckCircleOutlined,{})," Allowed"]}):"block"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tz.CloseCircleOutlined,{})," Blocked: ",B.reason]}):"modify"===B.action?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tG.CheckCircleOutlined,{})," Modified",B.texts&&B.texts.length>0&&(0,l.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:["→ ",B.texts[0].substring(0,50),B.texts[0].length>50?"...":""]})]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(tG.CheckCircleOutlined,{})," ",B.action||"Unknown"]})})]})]})},"test")}),(0,l.jsxs)("div",{className:"mt-3 p-4 bg-linear-to-r from-blue-50 to-indigo-50 border border-blue-200 rounded-lg flex items-center justify-between shrink-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)("div",{className:"bg-blue-100 rounded-full p-2",children:(0,l.jsx)(tU,{className:"text-blue-600 text-lg"})}),(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"text-sm font-medium text-gray-900",children:"Built a useful guardrail?"}),(0,l.jsx)("div",{className:"text-xs text-gray-600",children:"Share it with the community and help others build faster"})]})]}),(0,l.jsx)(tm.Button,{size:"xs",onClick:()=>window.open("https://github.com/BerriAI/litellm-guardrails","_blank"),icon:tJ.ExportOutlined,className:"bg-blue-600 hover:bg-blue-700 text-white border-0",children:"Contribute Template"})]})]}),(0,l.jsxs)("div",{className:"w-[300px] shrink-0 overflow-auto border-l border-gray-200 pl-6",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-3",children:[(0,l.jsx)(c.CodeOutlined,{className:"text-blue-500"}),(0,l.jsx)("span",{className:"font-semibold text-gray-700",children:"Available Primitives"})]}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mb-3",children:"Click to copy functions to clipboard"}),(0,l.jsx)(G.Collapse,{defaultActiveKey:["Return Values"],className:"primitives-collapse bg-transparent border-0",expandIconPosition:"end",children:Object.entries(tQ).map(([e,t])=>(0,l.jsx)(tW,{header:(0,l.jsx)("span",{className:"text-sm font-medium text-gray-700",children:e}),className:"bg-white mb-2 rounded-lg border border-gray-200",children:(0,l.jsx)("div",{className:"space-y-2",children:t.map(e=>(0,l.jsx)("button",{onClick:()=>z(e.name),className:`w-full text-left px-2 py-2 rounded transition-colors ${$===e.name?"bg-green-100":"bg-gray-50 hover:bg-blue-50"}`,children:$===e.name?(0,l.jsxs)("span",{className:"flex items-center gap-1 text-xs font-mono text-green-700",children:[(0,l.jsx)(tG.CheckCircleOutlined,{})," Copied!"]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("div",{className:"text-xs font-mono text-gray-800",children:e.name}),(0,l.jsx)("div",{className:"text-[10px] text-gray-500 mt-0.5",children:e.desc})]})},e.name))})},e))})]})]}),(0,l.jsxs)("div",{className:"flex items-center justify-between pt-4 mt-4 border-t border-gray-200",children:[(0,l.jsx)("span",{className:"text-xs text-gray-400",children:"Changes are auto-saved to local draft"}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsx)(tm.Button,{variant:"secondary",onClick:t,children:"Cancel"}),(0,l.jsx)(tm.Button,{onClick:D,loading:w,disabled:w||!o.trim(),icon:tq.SaveOutlined,children:n?"Update Guardrail":"Save Guardrail"})]})]})]}),(0,l.jsx)("style",{children:` + .custom-code-modal .ant-modal-content { + padding: 24px; + } + .custom-code-modal .ant-modal-close { + top: 20px; + right: 20px; + } + .primitives-collapse .ant-collapse-item { + border: none !important; + } + .primitives-collapse .ant-collapse-header { + padding: 8px 12px !important; + } + .primitives-collapse .ant-collapse-content-box { + padding: 8px 12px !important; + } + `})]})},t0=({guardrailId:e,onClose:t,accessToken:a,isAdmin:s})=>{let n,[o,d]=(0,r.useState)(null),[g,h]=(0,r.useState)(null),[f,j]=(0,r.useState)(!0),[_,b]=(0,r.useState)(!1),[v]=u.Form.useForm(),[w,C]=(0,r.useState)([]),[N,k]=(0,r.useState)({}),[S,I]=(0,r.useState)(null),[A,O]=(0,r.useState)({}),[T,P]=(0,r.useState)(!1),L={rules:[],default_action:"deny",on_disallowed_action:"block",violation_message_template:""},[B,F]=(0,r.useState)(L),[$,E]=(0,r.useState)(!1),[M,R]=(0,r.useState)(!1),G=r.default.useRef({patterns:[],blockedWords:[],categories:[]}),z=(0,r.useCallback)((e,t,a,l,r)=>{G.current={patterns:e,blockedWords:t,categories:a||[],competitorIntentEnabled:l,competitorIntentConfig:r}},[]),D=async()=>{try{if(j(!0),!a)return;let t=await (0,m.getGuardrailInfo)(a,e);if(d(t),t.litellm_params?.pii_entities_config){let e=t.litellm_params.pii_entities_config;if(C([]),k({}),Object.keys(e).length>0){let t=[],a={};Object.entries(e).forEach(([e,l])=>{t.push(e),a[e]="string"==typeof l?l:"MASK"}),C(t),k(a)}}else C([]),k({})}catch(e){y.default.fromBackend("Failed to load guardrail information"),console.error("Error fetching guardrail info:",e)}finally{j(!1)}},K=async()=>{try{if(!a)return;let e=await (0,m.getGuardrailProviderSpecificParams)(a);h(e)}catch(e){console.error("Error fetching guardrail provider specific params:",e)}},q=async()=>{try{if(!a)return;let e=await (0,m.getGuardrailUISettings)(a);I(e)}catch(e){console.error("Error fetching guardrail UI settings:",e)}};(0,r.useEffect)(()=>{K()},[a]),(0,r.useEffect)(()=>{D(),q()},[e,a]),(0,r.useEffect)(()=>{if(o&&v){let e={...o.litellm_params||{}};delete e.skip_system_message_in_guardrail,delete e.skip_tool_message_in_guardrail,v.setFieldsValue({guardrail_name:o.guardrail_name,...e,skip_system_message_choice:ef(o.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:ey(o.litellm_params?.skip_tool_message_in_guardrail),guardrail_info:o.guardrail_info?JSON.stringify(o.guardrail_info,null,2):"",...o.litellm_params?.optional_params&&{optional_params:o.litellm_params.optional_params}})}},[o,g,v]);let H=(0,r.useCallback)(()=>{o?.litellm_params?.guardrail==="tool_permission"?F({rules:o.litellm_params?.rules||[],default_action:(o.litellm_params?.default_action||"deny").toLowerCase(),on_disallowed_action:(o.litellm_params?.on_disallowed_action||"block").toLowerCase(),violation_message_template:o.litellm_params?.violation_message_template||""}):F(L),E(!1)},[o]);(0,r.useEffect)(()=>{H()},[H]);let U=async t=>{try{if(!a)return;let d={litellm_params:{}};t.guardrail_name!==o.guardrail_name&&(d.guardrail_name=t.guardrail_name),t.default_on!==o.litellm_params?.default_on&&(d.litellm_params.default_on=t.default_on);let c=ef(o.litellm_params?.skip_system_message_in_guardrail),u=t.skip_system_message_choice;void 0!==u&&u!==c&&("inherit"===u?d.litellm_params.skip_system_message_in_guardrail=null:"yes"===u?d.litellm_params.skip_system_message_in_guardrail=!0:d.litellm_params.skip_system_message_in_guardrail=!1);let p=ey(o.litellm_params?.skip_tool_message_in_guardrail),x=t.skip_tool_message_choice;void 0!==x&&x!==p&&("inherit"===x?d.litellm_params.skip_tool_message_in_guardrail=null:"yes"===x?d.litellm_params.skip_tool_message_in_guardrail=!0:d.litellm_params.skip_tool_message_in_guardrail=!1);let h=o.guardrail_info,f=t.guardrail_info?JSON.parse(t.guardrail_info):void 0;JSON.stringify(h)!==JSON.stringify(f)&&(d.guardrail_info=f);let j=o.litellm_params?.pii_entities_config||{},_={};if(w.forEach(e=>{_[e]=N[e]||"MASK"}),JSON.stringify(j)!==JSON.stringify(_)&&(d.litellm_params.pii_entities_config=_),o.litellm_params?.guardrail==="litellm_content_filter"&&T){var l,r,i,s,n;let e,t=(l=G.current.patterns||[],r=G.current.blockedWords||[],i=G.current.categories||[],s=G.current.competitorIntentEnabled,n=G.current.competitorIntentConfig,e={patterns:l.map(e=>({pattern_type:"prebuilt"===e.type?"prebuilt":"regex",pattern_name:"prebuilt"===e.type?e.name:void 0,pattern:"custom"===e.type?e.pattern:void 0,name:e.name,action:e.action})),blocked_words:r.map(e=>({keyword:e.keyword,action:e.action,description:e.description}))},void 0!==i&&(e.categories=i.map(e=>({category:e.category,enabled:!0,action:e.action,severity_threshold:e.severity_threshold||"medium"}))),s&&n&&n.brand_self.length>0&&(e.competitor_intent_config={competitor_intent_type:n.competitor_intent_type,brand_self:n.brand_self,locations:n.locations?.length?n.locations:void 0,competitors:"generic"===n.competitor_intent_type&&n.competitors?.length?n.competitors:void 0,policy:n.policy,threshold_high:n.threshold_high,threshold_medium:n.threshold_medium,threshold_low:n.threshold_low}),e);d.litellm_params.patterns=t.patterns,d.litellm_params.blocked_words=t.blocked_words,d.litellm_params.categories=t.categories,d.litellm_params.competitor_intent_config=t.competitor_intent_config??null}if(o.litellm_params?.guardrail==="tool_permission"){let e=o.litellm_params?.rules||[],t=B.rules||[],a=JSON.stringify(e)!==JSON.stringify(t),l=(o.litellm_params?.default_action||"deny").toLowerCase(),r=(B.default_action||"deny").toLowerCase(),i=l!==r,s=(o.litellm_params?.on_disallowed_action||"block").toLowerCase(),n=(B.on_disallowed_action||"block").toLowerCase(),c=s!==n,m=o.litellm_params?.violation_message_template||"",u=B.violation_message_template||"",p=m!==u;($||a||i||c||p)&&(d.litellm_params.rules=t,d.litellm_params.default_action=r,d.litellm_params.on_disallowed_action=n,d.litellm_params.violation_message_template=u||null)}let v=Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail),C=o.litellm_params?.guardrail==="tool_permission";if(g&&v&&!C){let e=g[en[v]?.toLowerCase()]||{},a=new Set;Object.keys(e).forEach(e=>{"optional_params"!==e&&a.add(e)}),e.optional_params&&e.optional_params.fields&&Object.keys(e.optional_params.fields).forEach(e=>{a.add(e)}),a.forEach(e=>{if("patterns"===e||"blocked_words"===e||"categories"===e)return;let a=t[e];(null==a||""===a)&&(a=t.optional_params?.[e]);let l=o.litellm_params?.[e];JSON.stringify(a)!==JSON.stringify(l)&&(null!=a&&""!==a?d.litellm_params[e]=a:null!=l&&""!==l&&(d.litellm_params[e]=null))})}if(0===Object.keys(d.litellm_params).length&&delete d.litellm_params,0===Object.keys(d).length){y.default.info("No changes detected"),b(!1);return}await (0,m.updateGuardrailCall)(a,e,d),y.default.success("Guardrail updated successfully"),P(!1),D(),b(!1)}catch(e){console.error("Error updating guardrail:",e),y.default.fromBackend("Failed to update guardrail")}};if(f)return(0,l.jsx)("div",{className:"p-4",children:"Loading..."});if(!o)return(0,l.jsx)("div",{className:"p-4",children:"Guardrail not found"});let J=e=>e?new Date(e).toLocaleString():"-",{logo:W,displayName:V}=eh(o.litellm_params?.guardrail||""),Y=async(e,t)=>{await (0,t_.copyToClipboard)(e)&&(O(e=>({...e,[t]:!0})),setTimeout(()=>{O(e=>({...e,[t]:!1}))},2e3))},Q="config"===o.guardrail_definition_location;return(0,l.jsxs)("div",{className:"p-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(i.Button,{type:"text",icon:(0,l.jsx)(tb.ArrowLeftIcon,{className:"w-4 h-4"}),onClick:t,className:"mb-4",children:"Back to Guardrails"}),(0,l.jsx)(tA.Title,{children:o.guardrail_name||"Unnamed Guardrail"}),(0,l.jsxs)("div",{className:"flex items-center cursor-pointer",children:[(0,l.jsx)(eU.Text,{className:"text-gray-500 font-mono",children:o.guardrail_id}),(0,l.jsx)(i.Button,{type:"text",size:"small",icon:A["guardrail-id"]?(0,l.jsx)(tO.CheckIcon,{size:12}):(0,l.jsx)(tT.CopyIcon,{size:12}),onClick:()=>Y(o.guardrail_id,"guardrail-id"),className:`left-2 z-10 transition-all duration-200 ${A["guardrail-id"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]})]}),(0,l.jsxs)(tN.TabGroup,{children:[(0,l.jsxs)(tk.TabList,{className:"mb-4",children:[(0,l.jsx)(tC.Tab,{children:"Overview"},"overview"),s?(0,l.jsx)(tC.Tab,{children:"Settings"},"settings"):(0,l.jsx)(l.Fragment,{})]}),(0,l.jsxs)(tI.TabPanels,{children:[(0,l.jsxs)(tS.TabPanel,{children:[(0,l.jsxs)(tw.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,l.jsxs)(eH.Card,{children:[(0,l.jsx)(eU.Text,{children:"Provider"}),(0,l.jsxs)("div",{className:"mt-2 flex items-center space-x-2",children:[W&&(0,l.jsx)("img",{src:W,alt:`${V} logo`,className:"w-6 h-6",onError:e=>{e.target.style.display="none"}}),(0,l.jsx)(tA.Title,{children:V})]})]}),(0,l.jsxs)(eH.Card,{children:[(0,l.jsx)(eU.Text,{children:"Mode"}),(0,l.jsxs)("div",{className:"mt-2",children:[(0,l.jsx)(tA.Title,{children:o.litellm_params?.mode||"-"}),(0,l.jsx)(tv.Badge,{color:o.litellm_params?.default_on?"green":"gray",children:o.litellm_params?.default_on?"Default On":"Default Off"})]})]}),(0,l.jsxs)(eH.Card,{children:[(0,l.jsx)(eU.Text,{children:"Created At"}),(0,l.jsxs)("div",{className:"mt-2",children:[(0,l.jsx)(tA.Title,{children:J(o.created_at)}),(0,l.jsxs)(eU.Text,{children:["Last Updated: ",J(o.updated_at)]})]})]})]}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsx)(eH.Card,{className:"mt-6",children:(0,l.jsxs)("div",{className:"flex justify-between items-center",children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"PII Protection"}),(0,l.jsxs)(tv.Badge,{color:"blue",children:[Object.keys(o.litellm_params.pii_entities_config).length," PII entities configured"]})]})}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsx)(eU.Text,{className:"mb-4 text-lg font-semibold",children:"PII Entity Configuration"}),(0,l.jsxs)("div",{className:"border rounded-lg overflow-hidden shadow-xs",children:[(0,l.jsxs)("div",{className:"bg-gray-50 px-5 py-3 border-b flex",children:[(0,l.jsx)(eU.Text,{className:"flex-1 font-semibold text-gray-700",children:"Entity Type"}),(0,l.jsx)(eU.Text,{className:"flex-1 font-semibold text-gray-700",children:"Configuration"})]}),(0,l.jsx)("div",{className:"max-h-[400px] overflow-y-auto",children:Object.entries(o.litellm_params?.pii_entities_config).map(([e,t])=>(0,l.jsxs)("div",{className:"px-5 py-3 flex border-b hover:bg-gray-50 transition-colors",children:[(0,l.jsx)(eU.Text,{className:"flex-1 font-medium text-gray-900",children:e}),(0,l.jsx)(eU.Text,{className:"flex-1",children:(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 ${"MASK"===t?"text-blue-600":"text-red-600"}`,children:["MASK"===t?(0,l.jsx)(eT.default,{}):(0,l.jsx)(eP.StopOutlined,{}),String(t)]})})]},e))})]})]}),o.litellm_params?.guardrail==="tool_permission"&&(0,l.jsx)(eH.Card,{className:"mt-6",children:(0,l.jsx)(eQ,{value:B,disabled:!0})}),o.litellm_params?.guardrail==="custom_code"&&o.litellm_params?.custom_code&&(0,l.jsxs)(eH.Card,{className:"mt-6",children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)(c.CodeOutlined,{className:"text-blue-500"}),(0,l.jsx)(eU.Text,{className:"font-medium text-lg",children:"Custom Code"})]}),s&&!Q&&(0,l.jsx)(i.Button,{size:"small",icon:(0,l.jsx)(c.CodeOutlined,{}),onClick:()=>R(!0),children:"Edit Code"})]}),(0,l.jsx)("div",{className:"relative rounded-lg overflow-hidden border border-gray-700 bg-[#1e1e1e]",children:(0,l.jsx)("pre",{className:"p-4 text-sm text-gray-200 overflow-x-auto",style:{fontFamily:"'Fira Code', 'Monaco', 'Consolas', monospace"},children:(0,l.jsx)("code",{children:o.litellm_params.custom_code})})})]}),(0,l.jsx)(tM,{guardrailData:o,guardrailSettings:S,isEditing:!1,accessToken:a})]}),s&&(0,l.jsx)(tS.TabPanel,{children:(0,l.jsxs)(eH.Card,{children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,l.jsx)(tA.Title,{children:"Guardrail Settings"}),Q&&(0,l.jsx)(ek.Tooltip,{title:"Guardrail is defined in the config file and cannot be edited.",children:(0,l.jsx)(eV.InfoCircleOutlined,{})}),!_&&!Q&&(o.litellm_params?.guardrail==="custom_code"?(0,l.jsx)(i.Button,{icon:(0,l.jsx)(c.CodeOutlined,{}),onClick:()=>R(!0),children:"Edit Code"}):(0,l.jsx)(i.Button,{onClick:()=>b(!0),children:"Edit Settings"}))]}),_?(0,l.jsxs)(u.Form,{form:v,onFinish:U,initialValues:{guardrail_name:o.guardrail_name,...(n={...o.litellm_params||{}},delete n.skip_system_message_in_guardrail,delete n.skip_tool_message_in_guardrail,n),skip_system_message_choice:ef(o.litellm_params?.skip_system_message_in_guardrail),skip_tool_message_choice:ey(o.litellm_params?.skip_tool_message_in_guardrail),guardrail_info:o.guardrail_info?JSON.stringify(o.guardrail_info,null,2):"",...o.litellm_params?.optional_params&&{optional_params:o.litellm_params.optional_params}},layout:"vertical",children:[(0,l.jsx)(u.Form.Item,{label:"Guardrail Name",name:"guardrail_name",rules:[{required:!0,message:"Please input a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"Enter guardrail name"})}),(0,l.jsx)(u.Form.Item,{label:"Default On",name:"default_on",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:!0,children:"Yes"}),(0,l.jsx)(x.Select.Option,{value:!1,children:"No"})]})}),(0,l.jsx)(u.Form.Item,{label:"Skip system messages in guardrail",name:"skip_system_message_choice",tooltip:"Unified guardrails: omit role: system from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_system_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),(0,l.jsx)(u.Form.Item,{label:"Skip tool messages in guardrail",name:"skip_tool_message_choice",tooltip:"Unified guardrails: omit role: tool from guardrail input (LLM still gets full messages). Use global default follows litellm_settings.skip_tool_message_in_guardrail.",children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"inherit",children:"Use global default"}),(0,l.jsx)(x.Select.Option,{value:"yes",children:"Yes — exclude from guardrail scan"}),(0,l.jsx)(x.Select.Option,{value:"no",children:"No — always include in scan"})]})}),o.litellm_params?.guardrail==="presidio"&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eJ.Divider,{orientation:"left",children:"PII Protection"}),(0,l.jsx)("div",{className:"mb-6",children:S&&(0,l.jsx)(eq,{entities:S.supported_entities,actions:S.supported_actions,selectedEntities:w,selectedActions:N,onEntitySelect:e=>{C(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},onActionSelect:(e,t)=>{k(a=>({...a,[e]:t}))},entityCategories:S.pii_entity_categories})})]}),(0,l.jsx)(tM,{guardrailData:o,guardrailSettings:S,isEditing:!0,accessToken:a,onDataChange:z,onUnsavedChanges:P}),(o.litellm_params?.guardrail==="tool_permission"||g)&&(0,l.jsx)(eJ.Divider,{orientation:"left",children:"Provider Settings"}),o.litellm_params?.guardrail==="tool_permission"?(0,l.jsx)(eQ,{value:B,onChange:F}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(eN,{selectedProvider:Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail)||null,accessToken:a,providerParams:g,value:o.litellm_params}),g&&(()=>{let e=Object.keys(en).find(e=>en[e]===o.litellm_params?.guardrail);if(!e)return null;let t=g[en[e]?.toLowerCase()];return t&&t.optional_params?(0,l.jsx)(ev,{optionalParams:t.optional_params,parentFieldKey:"optional_params",values:o.litellm_params}):null})()]}),(0,l.jsx)(eJ.Divider,{orientation:"left",children:"Advanced Settings"}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Information",name:"guardrail_info",children:(0,l.jsx)(p.Input.TextArea,{rows:5})}),(0,l.jsxs)("div",{className:"flex justify-end gap-2 mt-6",children:[(0,l.jsx)(i.Button,{onClick:()=>{b(!1),P(!1),H()},children:"Cancel"}),(0,l.jsx)(i.Button,{type:"primary",htmlType:"submit",children:"Save Changes"})]})]}):(0,l.jsxs)("div",{className:"space-y-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Guardrail ID"}),(0,l.jsx)("div",{className:"font-mono",children:o.guardrail_id})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Guardrail Name"}),(0,l.jsx)("div",{children:o.guardrail_name||"Unnamed Guardrail"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Provider"}),(0,l.jsx)("div",{children:V})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Mode"}),(0,l.jsx)("div",{children:o.litellm_params?.mode||"-"})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Default On"}),(0,l.jsx)(tv.Badge,{color:o.litellm_params?.default_on?"green":"gray",children:o.litellm_params?.default_on?"Yes":"No"})]}),o.litellm_params?.pii_entities_config&&Object.keys(o.litellm_params.pii_entities_config).length>0&&(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"PII Protection"}),(0,l.jsx)("div",{className:"mt-2",children:(0,l.jsxs)(tv.Badge,{color:"blue",children:[Object.keys(o.litellm_params.pii_entities_config).length," PII entities configured"]})})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Created At"}),(0,l.jsx)("div",{children:J(o.created_at)})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)(eU.Text,{className:"font-medium",children:"Last Updated"}),(0,l.jsx)("div",{children:J(o.updated_at)})]}),o.litellm_params?.guardrail==="tool_permission"&&(0,l.jsx)(eQ,{value:B,disabled:!0})]})]})})]})]}),(0,l.jsx)(tZ,{visible:M,onClose:()=>R(!1),onSuccess:()=>{R(!1),D()},accessToken:a,editData:o?{guardrail_id:o.guardrail_id,guardrail_name:o.guardrail_name,litellm_params:o.litellm_params}:null})]})};var t1=e.i(573421),t2=e.i(19732),t4=e.i(928685),t5=e.i(166406),t8=e.i(637235),t6=e.i(240647);let{Text:t3}=f.Typography,t7=function({results:e,errors:t}){let[a,i]=(0,r.useState)(new Set),s=e=>{let t=new Set(a);t.has(e)?t.delete(e):t.add(e),i(t)},n=async e=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0;{let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.focus(),t.select();let a=document.execCommand("copy");if(document.body.removeChild(t),!a)throw Error("execCommand failed");return!0}}catch(e){return console.error("Copy failed:",e),!1}};return e||t?(0,l.jsxs)("div",{className:"space-y-3 pt-4 border-t border-gray-200",children:[(0,l.jsx)("h3",{className:"text-sm font-semibold text-gray-900",children:"Results"}),e&&e.map(e=>{let t=a.has(e.guardrailName);return(0,l.jsx)(eH.Card,{className:"bg-green-50 border-green-200",children:(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-2 cursor-pointer flex-1",onClick:()=>s(e.guardrailName),children:[t?(0,l.jsx)(t6.RightOutlined,{className:"text-gray-500 text-xs"}):(0,l.jsx)(o.DownOutlined,{className:"text-gray-500 text-xs"}),(0,l.jsx)(tG.CheckCircleOutlined,{className:"text-green-600 text-lg"}),(0,l.jsx)("span",{className:"text-sm font-medium text-green-800",children:e.guardrailName})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-1 text-xs text-gray-600",children:[(0,l.jsx)(t8.ClockCircleOutlined,{}),(0,l.jsxs)("span",{className:"font-medium",children:[e.latency,"ms"]})]}),!t&&(0,l.jsx)(tm.Button,{size:"xs",variant:"secondary",icon:t5.CopyOutlined,onClick:async()=>{await n(e.response_text)?y.default.success("Result copied to clipboard"):y.default.fromBackend("Failed to copy result")},children:"Copy"})]})]}),!t&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{className:"bg-white border border-green-200 rounded-sm p-3",children:[(0,l.jsx)("label",{className:"text-xs font-medium text-gray-600 mb-2 block",children:"Output Text"}),(0,l.jsx)("div",{className:"font-mono text-sm text-gray-900 whitespace-pre-wrap wrap-break-word",children:e.response_text})]}),(0,l.jsxs)("div",{className:"text-xs text-gray-600",children:[(0,l.jsx)("span",{className:"font-medium",children:"Characters:"})," ",e.response_text.length]})]})]})},e.guardrailName)}),t&&t.map(e=>{let t=a.has(e.guardrailName);return(0,l.jsx)(eH.Card,{className:"bg-red-50 border-red-200",children:(0,l.jsxs)("div",{className:"flex items-start space-x-2",children:[(0,l.jsx)("div",{className:"cursor-pointer mt-0.5",onClick:()=>s(e.guardrailName),children:t?(0,l.jsx)(t6.RightOutlined,{className:"text-gray-500 text-xs"}):(0,l.jsx)(o.DownOutlined,{className:"text-gray-500 text-xs"})}),(0,l.jsx)("div",{className:"text-red-600 mt-0.5",children:(0,l.jsx)("svg",{className:"w-5 h-5",fill:"currentColor",viewBox:"0 0 20 20",children:(0,l.jsx)("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z",clipRule:"evenodd"})})}),(0,l.jsxs)("div",{className:"flex-1",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-1",children:[(0,l.jsxs)("p",{className:"text-sm font-medium text-red-800 cursor-pointer",onClick:()=>s(e.guardrailName),children:[e.guardrailName," - Error"]}),(0,l.jsxs)("div",{className:"flex items-center space-x-1 text-xs text-gray-600",children:[(0,l.jsx)(t8.ClockCircleOutlined,{}),(0,l.jsxs)("span",{className:"font-medium",children:[e.latency,"ms"]})]})]}),!t&&(0,l.jsx)("p",{className:"text-sm text-red-700 mt-1",children:e.error.message})]})]})},e.guardrailName)})]}):null},{TextArea:t9}=p.Input,{Text:ae}=f.Typography,at=function({guardrailNames:e,onSubmit:t,isLoading:a,results:i,errors:s,onClose:n}){let[o,d]=(0,r.useState)(""),c=()=>{o.trim()?t(o):y.default.fromBackend("Please enter text to test")},m=async e=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0;{let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.focus(),t.select();let a=document.execCommand("copy");if(document.body.removeChild(t),!a)throw Error("execCommand failed");return!0}}catch(e){return console.error("Copy failed:",e),!1}},u=async()=>{await m(o)?y.default.success("Input copied to clipboard"):y.default.fromBackend("Failed to copy input")};return(0,l.jsxs)("div",{className:"space-y-4 h-full flex flex-col",children:[(0,l.jsx)("div",{className:"flex items-center justify-between pb-3 border-b border-gray-200",children:(0,l.jsx)("div",{className:"flex items-center space-x-3",children:(0,l.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,l.jsxs)("div",{className:"flex items-center space-x-2 mb-1",children:[(0,l.jsx)("h2",{className:"text-lg font-semibold text-gray-900",children:"Test Guardrails:"}),(0,l.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map(e=>(0,l.jsx)("div",{className:"inline-flex items-center space-x-1 bg-blue-50 px-3 py-1 rounded-md border border-blue-200",children:(0,l.jsx)("span",{className:"font-mono text-blue-700 font-medium text-sm",children:e})},e))})]}),(0,l.jsxs)("p",{className:"text-sm text-gray-500",children:["Test ",e.length>1?"guardrails":"guardrail"," and compare results"]})]})})}),(0,l.jsxs)("div",{className:"flex-1 overflow-auto space-y-4",children:[(0,l.jsxs)("div",{className:"space-y-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex justify-between items-center mb-2",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Input Text"}),(0,l.jsx)(ek.Tooltip,{title:"Press Enter to submit. Use Shift+Enter for new line.",children:(0,l.jsx)(eV.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),o&&(0,l.jsx)(tm.Button,{size:"xs",variant:"secondary",icon:t5.CopyOutlined,onClick:u,children:"Copy Input"})]}),(0,l.jsx)(t9,{value:o,onChange:e=>d(e.target.value),onKeyDown:e=>{"Enter"!==e.key||e.shiftKey||e.ctrlKey||e.metaKey||(e.preventDefault(),c())},placeholder:"Enter text to test with guardrails...",rows:8,className:"font-mono text-sm"}),(0,l.jsxs)("div",{className:"flex justify-between items-center mt-1",children:[(0,l.jsxs)(ae,{className:"text-xs text-gray-500",children:["Press ",(0,l.jsx)("kbd",{className:"px-1 py-0.5 bg-gray-100 border border-gray-300 rounded-sm text-xs",children:"Enter"})," to submit •"," ",(0,l.jsx)("kbd",{className:"px-1 py-0.5 bg-gray-100 border border-gray-300 rounded-sm text-xs",children:"Shift+Enter"})," for new line"]}),(0,l.jsxs)(ae,{className:"text-xs text-gray-500",children:["Characters: ",o.length]})]})]}),(0,l.jsx)("div",{className:"pt-2",children:(0,l.jsx)(tm.Button,{onClick:c,loading:a,disabled:!o.trim(),className:"w-full",children:a?`Testing ${e.length} guardrail${e.length>1?"s":""}...`:`Test ${e.length} guardrail${e.length>1?"s":""}`})})]}),(0,l.jsx)(t7,{results:i,errors:s})]})]})},aa=({guardrailsList:e,isLoading:t,accessToken:a,onClose:i})=>{let[s,n]=(0,r.useState)(new Set),[o,d]=(0,r.useState)(""),[c,u]=(0,r.useState)([]),[g,x]=(0,r.useState)([]),[h,j]=(0,r.useState)(!1),_=e.filter(e=>e.guardrail_name?.toLowerCase().includes(o.toLowerCase())),v=async e=>{if(0===s.size||!a)return;j(!0),u([]),x([]);let t=[],l=[];await Promise.all(Array.from(s).map(async r=>{let i=Date.now();try{let l=await (0,m.applyGuardrail)(a,r,e,null,null),s=Date.now()-i;t.push({guardrailName:r,response_text:l.response_text,latency:s})}catch(t){let e=Date.now()-i;console.error(`Error testing guardrail ${r}:`,t),l.push({guardrailName:r,error:t,latency:e})}})),u(t),x(l),j(!1),t.length>0&&y.default.success(`${t.length} guardrail${t.length>1?"s":""} applied successfully`),l.length>0&&y.default.fromBackend(`${l.length} guardrail${l.length>1?"s":""} failed`)};return(0,l.jsx)("div",{className:"w-full h-[calc(100vh-200px)]",children:(0,l.jsx)(b.Card,{className:"h-full",styles:{body:{padding:0,height:"100%"}},children:(0,l.jsxs)("div",{className:"flex h-full",children:[(0,l.jsxs)("div",{className:"w-1/4 border-r border-gray-200 flex flex-col overflow-hidden",children:[(0,l.jsx)("div",{className:"p-4 border-b border-gray-200",children:(0,l.jsxs)("div",{className:"mb-3",children:[(0,l.jsx)("h3",{className:"text-lg font-semibold mb-3",children:"Guardrails"}),(0,l.jsx)(p.Input,{prefix:(0,l.jsx)(t4.SearchOutlined,{}),placeholder:"Search guardrails...",value:o,onChange:e=>d(e.target.value)})]})}),(0,l.jsx)("div",{className:"flex-1 overflow-auto",children:t?(0,l.jsx)("div",{className:"flex items-center justify-center h-32",children:(0,l.jsx)(ew.Spin,{})}):0===_.length?(0,l.jsx)("div",{className:"p-4",children:(0,l.jsx)(eW.Empty,{description:o?"No guardrails match your search":"No guardrails available"})}):(0,l.jsx)(t1.List,{dataSource:_,renderItem:e=>(0,l.jsx)(t1.List.Item,{onClick:()=>{var t;let a;e.guardrail_name&&(t=e.guardrail_name,(a=new Set(s)).has(t)?a.delete(t):a.add(t),n(a))},style:{paddingLeft:24,paddingRight:16},className:`cursor-pointer hover:bg-gray-50 transition-colors ${s.has(e.guardrail_name||"")?"bg-blue-50 border-l-4 border-l-blue-500":"border-l-4 border-l-transparent"}`,children:(0,l.jsx)(t1.List.Item.Meta,{title:(0,l.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,l.jsx)(t2.ExperimentOutlined,{className:"text-gray-400"}),(0,l.jsx)("span",{className:"font-medium text-gray-900",children:e.guardrail_name})]}),description:(0,l.jsxs)("div",{className:"text-xs space-y-1 mt-1",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"font-medium",children:"Type: "}),(0,l.jsx)("span",{className:"text-gray-600",children:e.litellm_params.guardrail})]}),(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"font-medium",children:"Mode: "}),(0,l.jsx)("span",{className:"text-gray-600",children:e.litellm_params.mode})]})]})})})})}),(0,l.jsx)("div",{className:"p-3 border-t border-gray-200 bg-gray-50",children:(0,l.jsxs)(f.Typography.Text,{className:"text-xs text-gray-600",children:[s.size," of ",_.length," selected"]})})]}),(0,l.jsxs)("div",{className:"w-3/4 flex flex-col bg-white",children:[(0,l.jsx)("div",{className:"p-4 border-b border-gray-200 flex justify-between items-center",children:(0,l.jsx)(f.Typography.Title,{level:2,className:"text-xl font-semibold mb-0",children:"Guardrail Testing Playground"})}),(0,l.jsx)("div",{className:"flex-1 overflow-auto p-4",children:0===s.size?(0,l.jsxs)("div",{className:"h-full flex flex-col items-center justify-center text-gray-400",children:[(0,l.jsx)(t2.ExperimentOutlined,{style:{fontSize:"48px",marginBottom:"16px"}}),(0,l.jsx)(f.Typography.Paragraph,{className:"text-lg font-medium text-gray-600 mb-2",children:"Select Guardrails to Test"}),(0,l.jsx)(f.Typography.Paragraph,{className:"text-center text-gray-500 max-w-md",children:"Choose one or more guardrails from the left sidebar to start testing and comparing results."})]}):(0,l.jsx)("div",{className:"h-full",children:(0,l.jsx)(at,{guardrailNames:Array.from(s),onSubmit:v,results:c.length>0?c:null,errors:g.length>0?g:null,isLoading:h,onClose:()=>n(new Set)})})})]})]})})})};var al=e.i(127952),ar=e.i(266537);let ai="/ui/assets/logos/",as=[{id:"cf_denied_financial",name:"Denied Financial Advice",description:"Detects requests for personalized financial advice, investment recommendations, or financial planning.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"],eval:{f1:100,precision:100,recall:100,testCases:207,latency:"<0.1ms"}},{id:"cf_denied_insults",name:"Insults & Personal Attacks",description:"Detects insults, name-calling, and personal attacks directed at the chatbot, staff, or other people.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"],eval:{f1:100,precision:100,recall:100,testCases:299,latency:"<0.1ms"}},{id:"cf_denied_legal",name:"Denied Legal Advice",description:"Detects requests for unauthorized legal advice, case analysis, or legal recommendations.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"]},{id:"cf_denied_medical",name:"Denied Medical Advice",description:"Detects requests for medical diagnosis, treatment recommendations, or health advice.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Topic Blocker"]},{id:"cf_harmful_violence",name:"Harmful Violence",description:"Detects content related to violence, criminal planning, attacks, and violent threats.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_self_harm",name:"Harmful Self-Harm",description:"Detects content related to self-harm, suicide, and dangerous self-destructive behavior.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_child_safety",name:"Harmful Child Safety",description:"Detects content that could endanger child safety or exploit minors.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_harmful_illegal_weapons",name:"Harmful Illegal Weapons",description:"Detects content related to illegal weapons manufacturing, distribution, or acquisition.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Safety"]},{id:"cf_bias_gender",name:"Bias: Gender",description:"Detects gender-based discrimination, stereotypes, and biased language.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_racial",name:"Bias: Racial",description:"Detects racial discrimination, stereotypes, and racially biased content.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_religious",name:"Bias: Religious",description:"Detects religious discrimination, intolerance, and religiously biased content.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_bias_sexual_orientation",name:"Bias: Sexual Orientation",description:"Detects discrimination based on sexual orientation and related biased content.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Bias"]},{id:"cf_prompt_injection_jailbreak",name:"Prompt Injection: Jailbreak",description:"Detects jailbreak attempts designed to bypass AI safety guidelines and restrictions.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_data_exfil",name:"Prompt Injection: Data Exfiltration",description:"Detects attempts to extract sensitive data through prompt manipulation.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_sql",name:"Prompt Injection: SQL",description:"Detects SQL injection attempts embedded in prompts.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_malicious_code",name:"Prompt Injection: Malicious Code",description:"Detects attempts to inject malicious code through prompts.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_prompt_injection_system_prompt",name:"Prompt Injection: System Prompt",description:"Detects attempts to extract or override system prompts.",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Prompt Injection"]},{id:"cf_toxic_abuse",name:"Toxic & Abusive Language",description:"Detects toxic, abusive, and hateful language across multiple languages (EN, AU, DE, ES, FR).",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Toxicity"]},{id:"cf_patterns",name:"Pattern Matching",description:"Detect and block sensitive data patterns like SSNs, credit card numbers, API keys, and custom regex patterns.",category:"litellm",subcategory:"Patterns",logo:`${ai}litellm_logo.jpg`,tags:["PII","Regex","Data Protection"]},{id:"cf_keywords",name:"Keyword Blocking",description:"Block or mask content containing specific keywords or phrases. Upload custom word lists or add individual terms.",category:"litellm",subcategory:"Keywords",logo:`${ai}litellm_logo.jpg`,tags:["Keywords","Blocklist"]},{id:"block_code_execution",name:"Block Code Execution",description:"Detects markdown fenced code blocks in requests and responses. Block or mask executable code (e.g. Python, JavaScript, Bash) by language with configurable confidence.",category:"litellm",subcategory:"Code Safety",logo:`${ai}litellm_logo.jpg`,tags:["Code","Safety","Prompt Injection"]},{id:"cf_competitor_intent",name:"Competitor Name Blocking",description:"Block or reframe competitor comparison and ranking intent. Detect when users ask to compare or recommend competitors (airline or generic competitor lists).",category:"litellm",subcategory:"Content Category",logo:`${ai}litellm_logo.jpg`,tags:["Content Category","Competitor","Topic Blocker"]},{id:"presidio",name:"Presidio PII",description:"Microsoft Presidio for PII detection and anonymization. Supports 30+ entity types with configurable actions.",category:"partner",logo:`${ai}microsoft_azure.svg`,tags:["PII","Microsoft"],providerKey:"PresidioPII"},{id:"bedrock",name:"Bedrock Guardrail",description:"AWS Bedrock Guardrails for content filtering, topic avoidance, and sensitive information detection.",category:"partner",logo:`${ai}bedrock.svg`,tags:["AWS","Content Safety"],providerKey:"Bedrock"},{id:"lakera",name:"Lakera",description:"AI security platform protecting against prompt injections, data leakage, and harmful content.",category:"partner",logo:`${ai}lakeraai.jpeg`,tags:["Security","Prompt Injection"],providerKey:"Lakera"},{id:"openai_moderation",name:"OpenAI Moderation",description:"OpenAI's content moderation API for detecting harmful content across multiple categories.",category:"partner",logo:`${ai}openai_small.svg`,tags:["Content Moderation","OpenAI"]},{id:"google_model_armor",name:"Google Cloud Model Armor",description:"Google Cloud's model protection service for safe and responsible AI deployments.",category:"partner",logo:`${ai}google.svg`,tags:["Google Cloud","Safety"]},{id:"guardrails_ai",name:"Guardrails AI",description:"Open-source framework for adding structural, type, and quality guarantees to LLM outputs.",category:"partner",logo:`${ai}guardrails_ai.jpeg`,tags:["Open Source","Validation"]},{id:"zscaler",name:"Zscaler AI Guard",description:"Enterprise AI security from Zscaler for monitoring and protecting AI/ML workloads.",category:"partner",logo:`${ai}zscaler.svg`,tags:["Enterprise","Security"]},{id:"panw",name:"PANW Prisma AIRS",description:"Palo Alto Networks Prisma AI Runtime Security for securing AI applications in production.",category:"partner",logo:`${ai}palo_alto_networks.jpeg`,tags:["Enterprise","Security"]},{id:"cisco_ai_defense",name:"Cisco AI Defense",description:"Cisco AI Defense Inspection API for runtime protection: prompt injection, PII/PCI/PHI, harassment, hate speech, profanity, violence, and code detection.",category:"partner",logo:`${ai}cisco.png`,tags:["Enterprise","Security","Prompt Injection","PII"],providerKey:"CiscoAiDefense"},{id:"noma",name:"Noma Security",description:"AI security platform for detecting and preventing AI-specific threats and vulnerabilities.",category:"partner",logo:`${ai}noma_security.png`,tags:["Security","Threat Detection"]},{id:"aporia",name:"Aporia AI",description:"Real-time AI guardrails for hallucination detection, topic control, and policy enforcement.",category:"partner",logo:`${ai}aporia.png`,tags:["Hallucination","Policy"]},{id:"aim",name:"AIM Guardrail",description:"AIM Security guardrails for comprehensive AI threat detection and mitigation.",category:"partner",logo:`${ai}aim_security.jpeg`,tags:["Security","Threat Detection"]},{id:"cato_networks",name:"Cato Networks Guardrail",description:"Cato Networks guardrails for comprehensive AI threat detection and mitigation.",category:"partner",logo:`${ai}cato_networks.svg`,tags:["Security","Threat Detection"]},{id:"prompt_security",name:"Prompt Security",description:"Protect against prompt injection attacks, data leakage, and other LLM security threats.",category:"partner",logo:`${ai}prompt_security.png`,tags:["Prompt Injection","Security"]},{id:"lasso",name:"Lasso Guardrail",description:"Content moderation and safety guardrails for responsible AI deployments.",category:"partner",logo:`${ai}lasso.png`,tags:["Content Moderation"]},{id:"pangea",name:"Pangea Guardrail",description:"Pangea's AI guardrails for secure, compliant, and trustworthy AI applications.",category:"partner",logo:`${ai}pangea.png`,tags:["Compliance","Security"]},{id:"enkryptai",name:"EnkryptAI",description:"AI security and governance platform for enterprise AI safety and compliance.",category:"partner",logo:`${ai}enkrypt_ai.avif`,tags:["Enterprise","Governance"]},{id:"javelin",name:"Javelin Guardrails",description:"AI gateway with built-in guardrails for secure and compliant AI operations.",category:"partner",logo:`${ai}javelin.png`,tags:["Gateway","Security"]},{id:"pillar",name:"Pillar Guardrail",description:"AI safety platform for monitoring, testing, and securing AI systems.",category:"partner",logo:`${ai}pillar.jpeg`,tags:["Monitoring","Safety"]},{id:"akto",name:"Akto Guardrail",description:"AI security platform from Akto.io with automatic monitoring and guardrails for AI/ML applications.",category:"partner",logo:`${ai}akto.svg`,tags:["Security","Safety","Monitoring"]},{id:"promptguard",name:"PromptGuard",description:"AI security gateway with prompt injection detection, PII redaction, topic filtering, entity blocklists, and hallucination detection. Self-hostable with drop-in proxy integration.",category:"partner",logo:`${ai}promptguard.svg`,tags:["Security","Prompt Injection","PII"],providerKey:"Promptguard",eval:{f1:94.9,precision:100,recall:90.4,testCases:5384,latency:"~150ms"}},{id:"xecguard",name:"XecGuard",description:"CyCraft XecGuard AI security gateway. Multi-policy scanning (prompt injection, harmful content, PII, system-prompt enforcement) plus RAG context grounding.",category:"partner",logo:`${ai}xecguard.svg`,tags:["Security","Policy","Grounding","RAG"],providerKey:"Xecguard"},{id:"repelloai",name:"RepelloAI Argus",description:"RepelloAI Argus scans prompts and responses against policies configured per asset in the Repello dashboard.",category:"partner",logo:`${ai}repelloai.png`,tags:["Security","Policy","Prompt Injection"],providerKey:"Repelloai"}];var an=e.i(826910);let ao=({src:e,name:t})=>{let[a,i]=(0,r.useState)(!1);return a||!e?(0,l.jsx)("div",{style:{width:28,height:28,borderRadius:6,backgroundColor:"#e5e7eb",display:"flex",alignItems:"center",justifyContent:"center",fontSize:13,fontWeight:600,color:"#6b7280",flexShrink:0},children:t?.charAt(0)||"?"}):(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(e),alt:"",style:{width:28,height:28,borderRadius:6,objectFit:"contain",flexShrink:0},onError:()=>i(!0)})},ad=({card:e,onClick:t})=>{let[a,i]=(0,r.useState)(!1);return(0,l.jsxs)("div",{onClick:t,onMouseEnter:()=>i(!0),onMouseLeave:()=>i(!1),style:{borderRadius:12,border:a?"1px solid #93c5fd":"1px solid #e5e7eb",backgroundColor:"#ffffff",padding:"20px 20px 16px 20px",cursor:"pointer",transition:"border-color 0.15s, box-shadow 0.15s",display:"flex",flexDirection:"column",minHeight:170,boxShadow:a?"0 1px 6px rgba(59,130,246,0.08)":"none"},children:[(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:10},children:[(0,l.jsx)(ao,{src:e.logo,name:e.name}),(0,l.jsx)("span",{style:{fontSize:14,fontWeight:600,color:"#111827",lineHeight:1.3},children:e.name})]}),(0,l.jsx)("p",{className:"line-clamp-3",style:{fontSize:12,color:"#6b7280",lineHeight:1.6,margin:0,flex:1},children:e.description}),e.eval&&(0,l.jsxs)("div",{style:{marginTop:10,display:"flex",alignItems:"center",gap:4},children:[(0,l.jsx)(an.CheckCircleFilled,{style:{color:"#16a34a",fontSize:12}}),(0,l.jsxs)("span",{style:{fontSize:11,color:"#16a34a",fontWeight:500},children:["F1: ",e.eval.f1,"% · ",e.eval.testCases," test cases"]})]})]})};var ac=e.i(447566);let am={cf_denied_financial:{provider:"LitellmContentFilter",categoryName:"denied_financial_advice",guardrailNameSuggestion:"Denied Financial Advice",mode:"pre_call",defaultOn:!1},cf_denied_legal:{provider:"LitellmContentFilter",categoryName:"denied_legal_advice",guardrailNameSuggestion:"Denied Legal Advice",mode:"pre_call",defaultOn:!1},cf_denied_medical:{provider:"LitellmContentFilter",categoryName:"denied_medical_advice",guardrailNameSuggestion:"Denied Medical Advice",mode:"pre_call",defaultOn:!1},cf_denied_insults:{provider:"LitellmContentFilter",categoryName:"denied_insults",guardrailNameSuggestion:"Insults & Personal Attacks",mode:"pre_call",defaultOn:!1},cf_harmful_violence:{provider:"LitellmContentFilter",categoryName:"harmful_violence",guardrailNameSuggestion:"Harmful Violence",mode:"pre_call",defaultOn:!1},cf_harmful_self_harm:{provider:"LitellmContentFilter",categoryName:"harmful_self_harm",guardrailNameSuggestion:"Harmful Self-Harm",mode:"pre_call",defaultOn:!1},cf_harmful_child_safety:{provider:"LitellmContentFilter",categoryName:"harmful_child_safety",guardrailNameSuggestion:"Harmful Child Safety",mode:"pre_call",defaultOn:!1},cf_harmful_illegal_weapons:{provider:"LitellmContentFilter",categoryName:"harmful_illegal_weapons",guardrailNameSuggestion:"Harmful Illegal Weapons",mode:"pre_call",defaultOn:!1},cf_bias_gender:{provider:"LitellmContentFilter",categoryName:"bias_gender",guardrailNameSuggestion:"Bias: Gender",mode:"pre_call",defaultOn:!1},cf_bias_racial:{provider:"LitellmContentFilter",categoryName:"bias_racial",guardrailNameSuggestion:"Bias: Racial",mode:"pre_call",defaultOn:!1},cf_bias_religious:{provider:"LitellmContentFilter",categoryName:"bias_religious",guardrailNameSuggestion:"Bias: Religious",mode:"pre_call",defaultOn:!1},cf_bias_sexual_orientation:{provider:"LitellmContentFilter",categoryName:"bias_sexual_orientation",guardrailNameSuggestion:"Bias: Sexual Orientation",mode:"pre_call",defaultOn:!1},cf_prompt_injection_jailbreak:{provider:"LitellmContentFilter",categoryName:"prompt_injection_jailbreak",guardrailNameSuggestion:"Prompt Injection: Jailbreak",mode:"pre_call",defaultOn:!1},cf_prompt_injection_data_exfil:{provider:"LitellmContentFilter",categoryName:"prompt_injection_data_exfiltration",guardrailNameSuggestion:"Prompt Injection: Data Exfiltration",mode:"pre_call",defaultOn:!1},cf_prompt_injection_sql:{provider:"LitellmContentFilter",categoryName:"prompt_injection_sql",guardrailNameSuggestion:"Prompt Injection: SQL",mode:"pre_call",defaultOn:!1},cf_prompt_injection_malicious_code:{provider:"LitellmContentFilter",categoryName:"prompt_injection_malicious_code",guardrailNameSuggestion:"Prompt Injection: Malicious Code",mode:"pre_call",defaultOn:!1},cf_prompt_injection_system_prompt:{provider:"LitellmContentFilter",categoryName:"prompt_injection_system_prompt",guardrailNameSuggestion:"Prompt Injection: System Prompt",mode:"pre_call",defaultOn:!1},cf_toxic_abuse:{provider:"LitellmContentFilter",categoryName:"harm_toxic_abuse",guardrailNameSuggestion:"Toxic & Abusive Language",mode:"pre_call",defaultOn:!1},cf_patterns:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Pattern Matching",mode:"pre_call",defaultOn:!1},cf_keywords:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Keyword Blocking",mode:"pre_call",defaultOn:!1},block_code_execution:{provider:"BlockCodeExecution",guardrailNameSuggestion:"Block Code Execution",mode:"pre_call",defaultOn:!1},cf_competitor_intent:{provider:"LitellmContentFilter",guardrailNameSuggestion:"Competitor Name Blocking",mode:"pre_call",defaultOn:!1},presidio:{provider:"PresidioPII",guardrailNameSuggestion:"Presidio PII",mode:"pre_call",defaultOn:!1},bedrock:{provider:"Bedrock",guardrailNameSuggestion:"Bedrock Guardrail",mode:"pre_call",defaultOn:!1},lakera:{provider:"Lakera",guardrailNameSuggestion:"Lakera",mode:"pre_call",defaultOn:!1},openai_moderation:{provider:"OpenaiModeration",guardrailNameSuggestion:"OpenAI Moderation",mode:"pre_call",defaultOn:!1},google_model_armor:{provider:"ModelArmor",guardrailNameSuggestion:"Google Cloud Model Armor",mode:"pre_call",defaultOn:!1},guardrails_ai:{provider:"GuardrailsAi",guardrailNameSuggestion:"Guardrails AI",mode:"pre_call",defaultOn:!1},zscaler:{provider:"ZscalerAiGuard",guardrailNameSuggestion:"Zscaler AI Guard",mode:"pre_call",defaultOn:!1},panw:{provider:"PanwPrismaAirs",guardrailNameSuggestion:"PANW Prisma AIRS",mode:"pre_call",defaultOn:!1},cisco_ai_defense:{provider:"CiscoAiDefense",guardrailNameSuggestion:"Cisco AI Defense",mode:"pre_call",defaultOn:!1},noma:{provider:"Noma",guardrailNameSuggestion:"Noma Security",mode:"pre_call",defaultOn:!1},aporia:{provider:"AporiaAi",guardrailNameSuggestion:"Aporia AI",mode:"pre_call",defaultOn:!1},aim:{provider:"Aim",guardrailNameSuggestion:"AIM Guardrail",mode:"pre_call",defaultOn:!1},cato_networks:{provider:"Cato Networks",guardrailNameSuggestion:"Cato Networks Guardrail",mode:"pre_call",defaultOn:!1},prompt_security:{provider:"PromptSecurity",guardrailNameSuggestion:"Prompt Security",mode:"pre_call",defaultOn:!1},lasso:{provider:"Lasso",guardrailNameSuggestion:"Lasso Guardrail",mode:"pre_call",defaultOn:!1},pangea:{provider:"Pangea",guardrailNameSuggestion:"Pangea Guardrail",mode:"pre_call",defaultOn:!1},enkryptai:{provider:"Enkryptai",guardrailNameSuggestion:"EnkryptAI",mode:"pre_call",defaultOn:!1},javelin:{provider:"Javelin",guardrailNameSuggestion:"Javelin Guardrails",mode:"pre_call",defaultOn:!1},pillar:{provider:"Pillar",guardrailNameSuggestion:"Pillar Guardrail",mode:"pre_call",defaultOn:!1},akto:{provider:"Akto",guardrailNameSuggestion:"Akto Guardrail",mode:"pre_call",defaultOn:!1},promptguard:{provider:"Promptguard",guardrailNameSuggestion:"PromptGuard",mode:"pre_call",defaultOn:!1},xecguard:{provider:"Xecguard",guardrailNameSuggestion:"XecGuard",mode:"pre_call",defaultOn:!1},repelloai:{provider:"Repelloai",guardrailNameSuggestion:"RepelloAI Argus",mode:"pre_call",defaultOn:!1}},au=({card:e,onBack:t,accessToken:a,onGuardrailCreated:s})=>{let[n,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)("overview"),m=[{property:"Provider",value:"litellm"===e.category?"LiteLLM Content Filter":"Partner Guardrail"},...e.subcategory?[{property:"Subcategory",value:e.subcategory}]:[],..."litellm"===e.category?[{property:"Cost",value:"$0 / request"}]:[],..."litellm"===e.category?[{property:"External Dependencies",value:"None"}]:[],..."litellm"===e.category?[{property:"Latency",value:e.eval?.latency||"<1ms"}]:[]],u=e.eval?[{metric:"Precision",value:`${e.eval.precision}%`},{metric:"Recall",value:`${e.eval.recall}%`},{metric:"F1 Score",value:`${e.eval.f1}%`},{metric:"Test Cases",value:String(e.eval.testCases)},{metric:"False Positives",value:"0"},{metric:"False Negatives",value:"0"},{metric:"Latency (p50)",value:e.eval.latency}]:[],p=[{key:"overview",label:"Overview"},...e.eval?[{key:"eval",label:"Eval Results"}]:[]];return(0,l.jsxs)("div",{style:{maxWidth:960,margin:"0 auto"},children:[(0,l.jsxs)("div",{onClick:t,style:{display:"inline-flex",alignItems:"center",gap:6,color:"#5f6368",cursor:"pointer",fontSize:14,marginBottom:24},children:[(0,l.jsx)(ac.ArrowLeftOutlined,{style:{fontSize:11}}),(0,l.jsx)("span",{children:e.name})]}),(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:16,marginBottom:8},children:[(0,l.jsx)("img",{src:(0,ea.resolveLogoSrc)(e.logo),alt:"",style:{width:40,height:40,borderRadius:8,objectFit:"contain"},onError:e=>{e.target.style.display="none"}}),(0,l.jsx)("h1",{style:{fontSize:28,fontWeight:400,color:"#202124",margin:0,lineHeight:1.2},children:e.name})]}),(0,l.jsx)("p",{style:{fontSize:14,color:"#5f6368",margin:"0 0 20px 0",lineHeight:1.6},children:e.description}),(0,l.jsx)("div",{style:{display:"flex",gap:10,marginBottom:32},children:(0,l.jsx)(i.Button,{onClick:()=>o(!0),style:{borderRadius:20,padding:"4px 20px",height:36,borderColor:"#dadce0",color:"#1a73e8",fontWeight:500,fontSize:14},children:"Create Guardrail"})}),(0,l.jsx)("div",{style:{borderBottom:"1px solid #dadce0",marginBottom:28},children:(0,l.jsx)("div",{style:{display:"flex",gap:0},children:p.map(e=>(0,l.jsx)("div",{onClick:()=>c(e.key),style:{padding:"12px 20px",fontSize:14,color:d===e.key?"#1a73e8":"#5f6368",borderBottom:d===e.key?"3px solid #1a73e8":"3px solid transparent",cursor:"pointer",fontWeight:d===e.key?500:400,marginBottom:-1},children:e.label},e.key))})}),"overview"===d&&(0,l.jsxs)("div",{style:{display:"flex",gap:64},children:[(0,l.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 12px 0"},children:"Overview"}),(0,l.jsx)("p",{style:{fontSize:14,color:"#3c4043",lineHeight:1.7,margin:"0 0 32px 0"},children:e.description}),(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 4px 0"},children:"Guardrail Details"}),(0,l.jsx)("p",{style:{fontSize:13,color:"#5f6368",margin:"0 0 16px 0"},children:"Details are as follows"}),(0,l.jsxs)("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:14},children:[(0,l.jsx)("thead",{children:(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #dadce0"},children:[(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500,width:200},children:"Property"}),(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500},children:e.name})]})}),(0,l.jsx)("tbody",{children:m.map((e,t)=>(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #f1f3f4"},children:[(0,l.jsx)("td",{style:{padding:"12px 0",color:"#3c4043"},children:e.property}),(0,l.jsx)("td",{style:{padding:"12px 0",color:"#202124"},children:e.value})]},t))})]})]}),(0,l.jsxs)("div",{style:{width:240,flexShrink:0},children:[(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Guardrail ID"}),(0,l.jsxs)("div",{style:{fontSize:13,color:"#202124",wordBreak:"break-all"},children:["litellm/",e.id]})]}),(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Type"}),(0,l.jsx)("div",{style:{fontSize:13,color:"#202124"},children:"litellm"===e.category?"Content Filter":"Partner"})]}),e.tags.length>0&&(0,l.jsxs)("div",{style:{marginBottom:28},children:[(0,l.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:8},children:"Tags"}),(0,l.jsx)("div",{style:{display:"flex",flexWrap:"wrap",gap:6},children:e.tags.map(e=>(0,l.jsx)("span",{style:{fontSize:12,padding:"4px 12px",borderRadius:16,border:"1px solid #dadce0",color:"#3c4043",backgroundColor:"#fff"},children:e},e))})]})]})]}),"eval"===d&&(0,l.jsxs)("div",{children:[(0,l.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 16px 0"},children:"Eval Results"}),(0,l.jsxs)("table",{style:{width:"100%",maxWidth:560,borderCollapse:"collapse",fontSize:14},children:[(0,l.jsx)("thead",{children:(0,l.jsxs)("tr",{style:{backgroundColor:"#f8f9fa",borderBottom:"1px solid #dadce0"},children:[(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 16px",color:"#5f6368",fontWeight:500},children:"Metric"}),(0,l.jsx)("th",{style:{textAlign:"left",padding:"12px 16px",color:"#5f6368",fontWeight:500},children:"Value"})]})}),(0,l.jsx)("tbody",{children:u.map((e,t)=>(0,l.jsxs)("tr",{style:{borderBottom:"1px solid #f1f3f4"},children:[(0,l.jsx)("td",{style:{padding:"12px 16px",color:"#3c4043"},children:e.metric}),(0,l.jsx)("td",{style:{padding:"12px 16px",color:"#202124",fontWeight:500},children:e.value})]},t))})]})]}),(0,l.jsx)(e5,{visible:n,onClose:()=>o(!1),accessToken:a,onSuccess:()=>{o(!1),s()},preset:am[e.id]})]})},ap=({accessToken:e,onGuardrailCreated:t})=>{let[a,i]=(0,r.useState)(""),[s,n]=(0,r.useState)(null),[o,d]=(0,r.useState)(!1),c=as.filter(e=>{if(!a)return!0;let t=a.toLowerCase();return e.name.toLowerCase().includes(t)||e.description.toLowerCase().includes(t)||e.tags.some(e=>e.toLowerCase().includes(t))}),m=c.filter(e=>"litellm"===e.category),u=c.filter(e=>"partner"===e.category);return s?(0,l.jsx)(au,{card:s,onBack:()=>n(null),accessToken:e,onGuardrailCreated:t}):(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{style:{marginBottom:24},children:(0,l.jsx)(p.Input,{size:"large",placeholder:"Search guardrails",prefix:(0,l.jsx)(t4.SearchOutlined,{style:{color:"#9ca3af"}}),value:a,onChange:e=>i(e.target.value),style:{borderRadius:8}})}),(0,l.jsxs)("div",{style:{marginBottom:40},children:[(0,l.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:4},children:[(0,l.jsx)("h2",{style:{fontSize:20,fontWeight:600,color:"#111827",margin:0},children:"LiteLLM Content Filter"}),(0,l.jsx)("span",{style:{display:"inline-flex",alignItems:"center",gap:6,fontSize:14,color:"#1a73e8",cursor:"pointer"},onClick:()=>d(!o),children:o?(0,l.jsx)(l.Fragment,{children:"Show less"}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(ar.ArrowRightOutlined,{style:{fontSize:12}}),`Show all (${m.length})`]})})]}),(0,l.jsx)("p",{style:{fontSize:13,color:"#6b7280",margin:"4px 0 20px 0"},children:"Built-in guardrails powered by LiteLLM. Zero latency, no external dependencies, no additional cost."}),(0,l.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:16},children:(o?m:m.slice(0,10)).map(e=>(0,l.jsx)(ad,{card:e,onClick:()=>n(e)},e.id))})]}),(0,l.jsxs)("div",{style:{marginBottom:40},children:[(0,l.jsx)("h2",{style:{fontSize:20,fontWeight:600,color:"#111827",margin:"0 0 4px 0"},children:"Partner Guardrails"}),(0,l.jsx)("p",{style:{fontSize:13,color:"#6b7280",margin:"4px 0 20px 0"},children:"Third-party guardrail integrations from leading AI security providers."}),(0,l.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:16},children:u.map(e=>(0,l.jsx)(ad,{card:e,onClick:()=>n(e)},e.id))})]})]})};var ag=e.i(988846),ax=e.i(837007),ah=e.i(409797),af=e.i(54131),ay=e.i(995926),aj=e.i(634831),a_=e.i(438100),ab=e.i(302202),av=e.i(328196),aw=e.i(168118),aC=e.i(663435),aN=e.i(954616),ak=e.i(912598),aS=e.i(431703),aI=e.i(135214),aA=e.i(243652);let aO=async(e,t)=>{let a=(0,m.getProxyBaseUrl)(),l=`${a}/guardrails/register`,r=await fetch(l,{method:"POST",headers:{[(0,m.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok){let e=await r.json().catch(()=>({})),t=(0,aS.deriveErrorMessage)(e);throw(0,m.handleError)(t),Error(t)}return r.json()},aT=(0,aA.createQueryKeys)("guardrails");function aP(e){var t;let a=e.litellm_params??{},l=e.guardrail_info??{},r=a.headers,i=Array.isArray(r)?r.map(e=>({key:(e.key??e.name??"").toString(),value:String(e.value??"")})):"object"==typeof r&&null!==r?Object.entries(r).map(([e,t])=>({key:e,value:String(t??"")})):[],s=a.api_base??a.url??"",n=l.model??a.model??"—",o=a.forward_api_key??!0,d=Array.isArray(a.extra_headers)?a.extra_headers.filter(e=>"string"==typeof e):[];return{id:e.guardrail_id,team:e.team_id??"—",name:e.guardrail_name,endpoint:s,status:"pending_review"===(t=e.status)?"pending":"active"===t||"rejected"===t?t:"active",model:n,forwardKey:o,description:l.description??"",method:a.method??"POST",customHeaders:i,extraHeaders:d,submittedAt:function(e){if(!e)return"—";try{let t=new Date(e);return isNaN(t.getTime())?e:t.toISOString().slice(0,10)}catch{return e}}(e.submitted_at),submittedBy:e.submitted_by_email??e.submitted_by_user_id??"—",mode:a.mode,unreachable_fallback:a.unreachable_fallback,additionalProviderParams:a.additional_provider_specific_params,guardrailType:a.guardrail}}let aL={active:{label:"Active",bg:"bg-green-50",text:"text-green-700",dot:"bg-green-500"},pending:{label:"Pending Review",bg:"bg-yellow-50",text:"text-yellow-700",dot:"bg-yellow-500"},rejected:{label:"Rejected",bg:"bg-red-50",text:"text-red-700",dot:"bg-red-500"}},aB={"ML Platform":"bg-purple-100 text-purple-700","Data Science":"bg-blue-100 text-blue-700",Security:"bg-red-100 text-red-700","Customer Success":"bg-orange-100 text-orange-700",Legal:"bg-gray-100 text-gray-700",Finance:"bg-green-100 text-green-700"};function aF({label:e,value:t,color:a}){return(0,l.jsxs)("div",{className:"bg-white border border-gray-200 rounded-lg px-4 py-3",children:[(0,l.jsx)("div",{className:`text-2xl font-bold ${a}`,children:t}),(0,l.jsx)("div",{className:"text-xs text-gray-500 mt-0.5",children:e})]})}function a$({enabled:e,onToggle:t}){return(0,l.jsx)("button",{type:"button",onClick:t,role:"switch","aria-checked":e,className:`relative inline-flex h-5 w-9 items-center rounded-full transition-colors focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 ${e?"bg-blue-500":"bg-gray-200"}`,children:(0,l.jsx)("span",{className:`inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow transition-transform ${e?"translate-x-4":"translate-x-0.5"}`})})}function aE({guardrail:e,isSelected:t,isHeadersExpanded:a,onSelect:r,onToggleForwardKey:i,onToggleHeaders:s,onApprove:n,onReject:o}){let d=aL[e.status],c=aB[e.team]??"bg-gray-100 text-gray-700";return(0,l.jsxs)("div",{className:`bg-white border rounded-lg p-4 transition-all ${t?"border-blue-400 ring-1 ring-blue-200":"border-gray-200"}`,children:[(0,l.jsxs)("div",{className:"flex items-start justify-between gap-4",children:[(0,l.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-1.5 flex-wrap",children:[(0,l.jsxs)("span",{className:`text-xs font-medium px-2 py-0.5 rounded-full ${c}`,children:["Team: ",e.team]}),(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium px-2 py-0.5 rounded-full ${d.bg} ${d.text}`,children:[(0,l.jsx)("span",{className:`w-1.5 h-1.5 rounded-full ${d.dot}`}),d.label]})]}),(0,l.jsx)("h3",{className:"text-sm font-semibold text-gray-900 mb-1",children:e.name}),(0,l.jsx)("p",{className:"text-xs text-gray-500 mb-2 line-clamp-1",children:e.description}),(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)(ab.ServerIcon,{className:"h-3.5 w-3.5 text-gray-400 shrink-0"}),(0,l.jsx)("code",{className:"text-xs text-gray-500 font-mono truncate",children:e.endpoint})]}),(0,l.jsxs)("div",{className:"flex items-center gap-4 text-xs text-gray-500",children:[(0,l.jsxs)("span",{children:["Model: ",(0,l.jsx)("span",{className:"font-medium text-gray-700",children:e.model})]}),(0,l.jsxs)("span",{children:["Submitted: ",(0,l.jsx)("span",{className:"font-medium text-gray-700",children:e.submittedAt})]})]})]}),(0,l.jsxs)("div",{className:"flex flex-col items-end gap-2 shrink-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("span",{className:"text-xs text-gray-500 whitespace-nowrap",children:"Forward API Key"}),(0,l.jsx)(a$,{enabled:e.forwardKey,onToggle:i})]}),(0,l.jsxs)("div",{className:"flex items-center gap-2 mt-1",children:[(0,l.jsx)("button",{type:"button",onClick:r,className:"text-xs border border-gray-300 text-gray-600 hover:bg-gray-50 px-3 py-1.5 rounded-md transition-colors font-medium",children:t?"Close":"Review"}),"pending"===e.status&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("button",{type:"button",onClick:n,className:"text-xs bg-green-500 hover:bg-green-600 text-white px-3 py-1.5 rounded-md transition-colors font-medium",children:"Approve"}),(0,l.jsx)("button",{type:"button",onClick:o,className:"text-xs border border-red-300 text-red-600 hover:bg-red-50 px-3 py-1.5 rounded-md transition-colors font-medium",children:"Reject"})]})]})]})]}),(0,l.jsxs)("div",{className:"mt-3 pt-3 border-t border-gray-100",children:[(0,l.jsxs)("button",{type:"button",onClick:s,className:"flex items-center gap-1.5 text-xs text-gray-500 hover:text-gray-700 transition-colors",children:[a?(0,l.jsx)(af.ChevronUpIcon,{className:"h-3.5 w-3.5"}):(0,l.jsx)(ah.ChevronDownIcon,{className:"h-3.5 w-3.5"}),"Static headers",e.customHeaders.length>0&&(0,l.jsx)("span",{className:"ml-1 bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.customHeaders.length})]}),a&&(0,l.jsx)("div",{className:"mt-2",children:0===e.customHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic",children:"No static headers configured."}):(0,l.jsx)("div",{className:"space-y-1",children:e.customHeaders.map((e,t)=>(0,l.jsxs)("div",{className:"flex items-center gap-2 text-xs font-mono",children:[(0,l.jsx)("span",{className:"text-gray-500 bg-gray-50 border border-gray-200 rounded-sm px-2 py-0.5",children:e.key}),(0,l.jsx)("span",{className:"text-gray-400",children:":"}),(0,l.jsx)("span",{className:"text-gray-700 bg-gray-50 border border-gray-200 rounded-sm px-2 py-0.5",children:e.value})]},`${e.key}-${t}`))})})]})]})}function aM({label:e,children:t}){return(0,l.jsxs)("div",{children:[(0,l.jsx)("div",{className:"text-xs font-semibold text-gray-500 mb-1",children:e}),(0,l.jsx)("div",{children:t})]})}function aR({guardrail:e,onClose:t,onApprove:a,onReject:i,onToggleForwardKey:s,onUpdateCustomHeaders:n,onUpdateExtraHeaders:o}){let[d,c]=(0,r.useState)(!1),[m,u]=(0,r.useState)(""),[p,g]=(0,r.useState)(""),[x,h]=(0,r.useState)(""),f=aL[e.status],y=aB[e.team]??"bg-gray-100 text-gray-700";return(0,l.jsx)("div",{className:"w-96 shrink-0 bg-white overflow-auto",children:(0,l.jsxs)("div",{className:"p-5",children:[(0,l.jsxs)("div",{className:"flex items-start justify-between mb-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-2 mb-1",children:[(0,l.jsxs)("span",{className:`text-xs font-medium px-2 py-0.5 rounded-full ${y}`,children:["Team: ",e.team]}),(0,l.jsxs)("span",{className:`inline-flex items-center gap-1.5 text-xs font-medium px-2 py-0.5 rounded-full ${f.bg} ${f.text}`,children:[(0,l.jsx)("span",{className:`w-1.5 h-1.5 rounded-full ${f.dot}`}),f.label]})]}),(0,l.jsx)("h2",{className:"text-base font-semibold text-gray-900",children:e.name}),(0,l.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:["Submitted by ",e.submittedBy," on ",e.submittedAt]})]}),(0,l.jsx)("button",{type:"button",onClick:t,className:"text-gray-400 hover:text-gray-600 transition-colors","aria-label":"Close detail panel",children:(0,l.jsx)(ay.XIcon,{className:"h-4 w-4"})})]}),(0,l.jsx)("p",{className:"text-sm text-gray-600 mb-5",children:e.description}),(0,l.jsxs)("div",{className:"space-y-4",children:[(0,l.jsx)(aM,{label:"Endpoint",children:(0,l.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,l.jsx)("code",{className:"text-xs font-mono text-gray-700 break-all",children:e.endpoint}),(0,l.jsx)("a",{href:e.endpoint,target:"_blank",rel:"noopener noreferrer",className:"text-gray-400 hover:text-blue-500 shrink-0",children:(0,l.jsx)(aj.ExternalLinkIcon,{className:"h-3.5 w-3.5"})})]})}),(0,l.jsx)(aM,{label:"Method",children:(0,l.jsx)("span",{className:"text-xs font-mono font-medium text-gray-700 bg-gray-100 px-2 py-0.5 rounded-sm",children:e.method})}),(0,l.jsxs)("div",{className:"border border-blue-100 bg-blue-50 rounded-lg p-3",children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-2",children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,l.jsx)(a_.KeyIcon,{className:"h-3.5 w-3.5 text-blue-500"}),(0,l.jsx)("span",{className:"text-xs font-semibold text-blue-800",children:"Forward LiteLLM API Key"})]}),(0,l.jsx)(a$,{enabled:e.forwardKey,onToggle:s})]}),(0,l.jsxs)("p",{className:"text-xs text-blue-700 leading-relaxed",children:["When enabled, the caller's LiteLLM API key is forwarded as an"," ",(0,l.jsx)("code",{className:"font-mono bg-blue-100 px-1 rounded-sm",children:"Authorization"}),"header to your guardrail endpoint. This allows your guardrail to authenticate model calls using the original caller's credentials."]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-700",children:"Static headers"}),e.customHeaders.length>0&&(0,l.jsx)("span",{className:"bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.customHeaders.length})]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Sent with every request to the guardrail."}),0===e.customHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic mb-2",children:"No static headers configured."}):(0,l.jsx)("ul",{className:"list-none space-y-1 mb-2",children:e.customHeaders.map((t,a)=>(0,l.jsxs)("li",{className:"flex items-center justify-between gap-2 text-xs font-mono bg-gray-50 border border-gray-200 rounded-sm px-2 py-1.5",children:[(0,l.jsxs)("span",{className:"text-gray-700 truncate",children:[t.key,": ",t.value]}),(0,l.jsx)("button",{type:"button",onClick:()=>n(e.customHeaders.filter((e,t)=>t!==a)),className:"text-gray-400 hover:text-red-600 shrink-0","aria-label":`Remove ${t.key}`,children:(0,l.jsx)(ay.XIcon,{className:"h-3.5 w-3.5"})})]},`${t.key}-${a}`))}),(0,l.jsxs)("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-end",children:[(0,l.jsx)("input",{type:"text",value:p,onChange:e=>g(e.target.value),placeholder:"Header name (e.g. X-API-Key)",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=p.trim(),l=x.trim();a&&!e.customHeaders.some(e=>e.key.toLowerCase()===a.toLowerCase())&&(n([...e.customHeaders,{key:a,value:l}]),g(""),h(""))}}}),(0,l.jsx)("input",{type:"text",value:x,onChange:e=>h(e.target.value),placeholder:"Value",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=p.trim(),l=x.trim();a&&!e.customHeaders.some(e=>e.key.toLowerCase()===a.toLowerCase())&&(n([...e.customHeaders,{key:a,value:l}]),g(""),h(""))}}}),(0,l.jsx)("button",{type:"button",onClick:()=>{let t=p.trim(),a=x.trim();t&&!e.customHeaders.some(e=>e.key.toLowerCase()===t.toLowerCase())&&(n([...e.customHeaders,{key:t,value:a}]),g(""),h(""))},className:"text-xs font-medium text-blue-600 hover:text-blue-700 border border-blue-200 bg-blue-50 hover:bg-blue-100 px-2 py-1.5 rounded-sm transition-colors shrink-0",children:"Add"})]})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"flex items-center gap-1.5 mb-2",children:[(0,l.jsx)("span",{className:"text-xs font-semibold text-gray-700",children:"Forward client headers"}),e.extraHeaders.length>0&&(0,l.jsx)("span",{className:"bg-gray-100 text-gray-600 rounded-full px-1.5 py-0.5 text-xs",children:e.extraHeaders.length})]}),(0,l.jsx)("p",{className:"text-xs text-gray-400 mb-2",children:"Allowed header names to forward from the client request to the guardrail (e.g. x-request-id)."}),0===e.extraHeaders.length?(0,l.jsx)("p",{className:"text-xs text-gray-400 italic mb-2",children:"No forward client headers configured."}):(0,l.jsx)("ul",{className:"list-none space-y-1 mb-2",children:e.extraHeaders.map((t,a)=>(0,l.jsxs)("li",{className:"flex items-center justify-between gap-2 text-xs font-mono bg-gray-50 border border-gray-200 rounded-sm px-2 py-1.5",children:[(0,l.jsx)("span",{className:"text-gray-700 truncate",children:t}),(0,l.jsx)("button",{type:"button",onClick:()=>o(e.extraHeaders.filter((e,t)=>t!==a)),className:"text-gray-400 hover:text-red-600 shrink-0","aria-label":`Remove ${t}`,children:(0,l.jsx)(ay.XIcon,{className:"h-3.5 w-3.5"})})]},`${t}-${a}`))}),(0,l.jsxs)("div",{className:"flex gap-2",children:[(0,l.jsx)("input",{type:"text",value:m,onChange:e=>u(e.target.value),placeholder:"e.g. x-request-id",className:"flex-1 min-w-0 text-xs font-mono border border-gray-200 rounded-sm px-2 py-1.5 text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500",onKeyDown:t=>{if("Enter"===t.key){t.preventDefault();let a=m.trim().toLowerCase();a&&!e.extraHeaders.map(e=>e.toLowerCase()).includes(a)&&(o([...e.extraHeaders,a]),u(""))}}}),(0,l.jsx)("button",{type:"button",onClick:()=>{let t=m.trim().toLowerCase();t&&!e.extraHeaders.map(e=>e.toLowerCase()).includes(t)&&(o([...e.extraHeaders,t]),u(""))},className:"text-xs font-medium text-blue-600 hover:text-blue-700 border border-blue-200 bg-blue-50 hover:bg-blue-100 px-2 py-1.5 rounded-sm transition-colors",children:"Add"})]})]}),(0,l.jsxs)("div",{className:"border border-gray-200 rounded-lg overflow-hidden",children:[(0,l.jsxs)("button",{type:"button",onClick:()=>c(!d),className:"w-full flex items-center justify-between px-3 py-2 text-left text-xs font-semibold text-gray-700 bg-gray-50 hover:bg-gray-100 transition-colors",children:[(0,l.jsx)("span",{children:"Equivalent config"}),d?(0,l.jsx)(af.ChevronUpIcon,{className:"h-3.5 w-3.5 text-gray-500"}):(0,l.jsx)(ah.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-500"})]}),d&&(0,l.jsx)("pre",{className:"p-3 text-xs font-mono text-gray-700 bg-white border-t border-gray-200 overflow-x-auto whitespace-pre-wrap break-all",children:function(e){let t=["litellm_settings:"," guardrails:",` - guardrail_name: "${e.name.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`," litellm_params:",` guardrail: ${e.guardrailType??"generic_guardrail_api"}`,` mode: ${e.mode??"pre_call"} # or post_call, during_call`,` api_base: ${e.endpoint||"https://your-guardrail-api.com"}`," api_key: os.environ/YOUR_GUARDRAIL_API_KEY # optional",` unreachable_fallback: ${e.unreachable_fallback??"fail_closed"} # default: fail_closed. Set to fail_open to proceed if the guardrail endpoint is unreachable.`,` forward_api_key: ${e.forwardKey}`];if(e.model&&"—"!==e.model&&t.push(` model: "${e.model}" # LLM model name sent to the guardrail for context`),e.customHeaders.length>0)for(let a of(t.push(" headers: # static headers (sent with every request)"),e.customHeaders))t.push(` ${a.key}: "${String(a.value).replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`);if(e.extraHeaders.length>0)for(let a of(t.push(" extra_headers: # forward these client request headers to the guardrail"),e.extraHeaders))t.push(` - ${a}`);if(e.additionalProviderParams&&Object.keys(e.additionalProviderParams).length>0)for(let[a,l]of(t.push(" additional_provider_specific_params:"),Object.entries(e.additionalProviderParams))){let e="string"==typeof l?`"${l}"`:String(l);t.push(` ${a}: ${e}`)}return t.join("\n")}(e)})]}),(0,l.jsxs)("div",{className:"flex items-start gap-2 bg-gray-50 border border-gray-200 rounded-lg p-3",children:[(0,l.jsx)(aw.InfoIcon,{className:"h-3.5 w-3.5 text-gray-400 shrink-0 mt-0.5"}),(0,l.jsxs)("p",{className:"text-xs text-gray-500 leading-relaxed",children:["This guardrail runs on a separate instance. It receives the user request and forwards the result to the next step in the pipeline. See"," ",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/adding_provider/generic_guardrail_api",target:"_blank",rel:"noopener noreferrer",className:"text-blue-500 hover:underline",children:"LiteLLM Generic Guardrail API docs"})," ","for configuration details."]})]})]}),(0,l.jsxs)("div",{className:"mt-5 pt-4 border-t border-gray-100 space-y-2",children:[(0,l.jsxs)("button",{type:"button",className:"w-full flex items-center justify-center gap-2 border border-gray-300 text-gray-700 hover:bg-gray-50 text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(aj.ExternalLinkIcon,{className:"h-4 w-4"}),"Test Endpoint"]}),"pending"===e.status&&(0,l.jsxs)("div",{className:"flex gap-2",children:[(0,l.jsxs)("button",{type:"button",onClick:a,className:"flex-1 flex items-center justify-center gap-1.5 bg-green-500 hover:bg-green-600 text-white text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(tO.CheckIcon,{className:"h-4 w-4"}),"Approve"]}),(0,l.jsxs)("button",{type:"button",onClick:i,className:"flex-1 flex items-center justify-center gap-1.5 border border-red-300 text-red-600 hover:bg-red-50 text-sm font-medium py-2 rounded-md transition-colors",children:[(0,l.jsx)(ay.XIcon,{className:"h-4 w-4"}),"Reject"]})]})]})]})})}function aG({action:e,guardrailName:t,onConfirm:a,onCancel:r}){let i="approve"===e;return(0,l.jsx)("div",{className:"fixed inset-0 bg-black/30 flex items-center justify-center z-50",children:(0,l.jsxs)("div",{className:"bg-white rounded-xl shadow-xl p-6 max-w-sm w-full mx-4",children:[(0,l.jsx)("div",{className:`w-10 h-10 rounded-full flex items-center justify-center mb-4 ${i?"bg-green-100":"bg-red-100"}`,children:i?(0,l.jsx)(tO.CheckIcon,{className:"h-5 w-5 text-green-600"}):(0,l.jsx)(av.AlertCircleIcon,{className:"h-5 w-5 text-red-600"})}),(0,l.jsx)("h3",{className:"text-base font-semibold text-gray-900 mb-1",children:i?"Approve Guardrail":"Reject Guardrail"}),(0,l.jsxs)("p",{className:"text-sm text-gray-500 mb-5",children:["Are you sure you want to ",e," ",(0,l.jsxs)("span",{className:"font-medium text-gray-700",children:['"',t,'"']}),"?"," ",i?"This will make it active and available for use.":"This will mark it as rejected and notify the team."]}),(0,l.jsxs)("div",{className:"flex gap-3",children:[(0,l.jsx)("button",{type:"button",onClick:r,className:"flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 text-sm font-medium py-2 rounded-md transition-colors",children:"Cancel"}),(0,l.jsx)("button",{type:"button",onClick:a,className:`flex-1 text-white text-sm font-medium py-2 rounded-md transition-colors ${i?"bg-green-500 hover:bg-green-600":"bg-red-500 hover:bg-red-600"}`,children:i?"Approve":"Reject"})]})]})})}function az({accessToken:e}){let[t,a]=(0,r.useState)([]),[i,s]=(0,r.useState)({total:0,pending_review:0,active:0,rejected:0}),[n,o]=(0,r.useState)(""),[d,c]=(0,r.useState)("all"),[h,f]=(0,r.useState)(null),[j,_]=(0,r.useState)(new Set),[b,v]=(0,r.useState)(null),[w,C]=(0,r.useState)(!0),[N,k]=(0,r.useState)(null),[S,I]=(0,r.useState)(""),[A,O]=(0,r.useState)(!1),[T]=u.Form.useForm(),P=(()=>{let{accessToken:e}=(0,aI.default)(),t=(0,ak.useQueryClient)();return(0,aN.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return aO(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:aT.all})}})})();(0,r.useEffect)(()=>{let e=setTimeout(()=>I(n),300);return()=>clearTimeout(e)},[n]);let L=(0,r.useCallback)(async()=>{if(!e)return void C(!1);C(!0),k(null);try{let t="all"===d?void 0:"pending"===d?"pending_review":d,l=await (0,m.listGuardrailSubmissions)(e,{status:t,search:S.trim()||void 0});a(l.submissions.map(aP)),s(l.summary)}catch(e){k(e instanceof Error?e.message:"Failed to load submissions"),a([])}finally{C(!1)}},[e,d,S]);(0,r.useEffect)(()=>{L()},[L]);let B=t.find(e=>e.id===h)??null,F=i.total,$=i.pending_review,E=i.active,M=i.rejected;async function R(l){if(!e)return;let r=t.find(e=>e.id===l);if(!r)return;let i=!r.forwardKey;try{await (0,m.updateGuardrailCall)(e,l,{litellm_params:{forward_api_key:i}}),a(e=>e.map(e=>e.id===l?{...e,forwardKey:i}:e)),y.default.success(i?"Forward API key enabled":"Forward API key disabled")}catch{y.default.fromBackend("Failed to update forward API key")}}async function G(t,l){if(!e)return;let r={};for(let{key:e,value:t}of l)e.trim()&&(r[e.trim()]=t);try{await (0,m.updateGuardrailCall)(e,t,{litellm_params:{headers:r}}),a(e=>e.map(e=>e.id===t?{...e,customHeaders:l.filter(e=>e.key.trim())}:e)),y.default.success("Static headers updated")}catch{y.default.fromBackend("Failed to update static headers")}}async function z(t,l){if(e)try{await (0,m.updateGuardrailCall)(e,t,{litellm_params:{extra_headers:l}}),a(e=>e.map(e=>e.id===t?{...e,extraHeaders:l}:e)),y.default.success("Forward client headers updated")}catch{y.default.fromBackend("Failed to update forward client headers")}}async function D(t){if(e)try{await (0,m.approveGuardrailSubmission)(e,t),v(null),h===t&&f(null),await L(),y.default.success("Guardrail approved")}catch{y.default.fromBackend("Failed to approve guardrail")}}async function K(t){if(e)try{await (0,m.rejectGuardrailSubmission)(e,t),v(null),h===t&&f(null),await L(),y.default.success("Guardrail rejected")}catch{y.default.fromBackend("Failed to reject guardrail")}}return(0,l.jsxs)("div",{className:"flex h-full",children:[(0,l.jsxs)("div",{className:`flex-1 min-w-0 p-6 overflow-auto ${B?"border-r border-gray-200":""}`,children:[(0,l.jsxs)("div",{className:"grid grid-cols-4 gap-4 mb-6",children:[(0,l.jsx)(aF,{label:"Total Submitted",value:F,color:"text-gray-900"}),(0,l.jsx)(aF,{label:"Pending Review",value:$,color:"text-yellow-600"}),(0,l.jsx)(aF,{label:"Active",value:E,color:"text-green-600"}),(0,l.jsx)(aF,{label:"Rejected",value:M,color:"text-red-600"})]}),(0,l.jsxs)("div",{className:"flex items-center gap-3 mb-5",children:[(0,l.jsxs)("div",{className:"relative flex-1 max-w-xs",children:[(0,l.jsx)(ag.SearchIcon,{className:"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400"}),(0,l.jsx)("input",{type:"text",placeholder:"Search guardrails...",value:n,onChange:e=>o(e.target.value),className:"w-full pl-9 pr-4 py-2 border border-gray-200 rounded-md text-sm text-gray-700 placeholder-gray-400 focus:outline-hidden focus:ring-1 focus:ring-blue-500 focus:border-blue-500"})]}),(0,l.jsxs)("select",{value:d,onChange:e=>c(e.target.value),className:"border border-gray-200 rounded-md px-3 py-2 text-sm text-gray-700 focus:outline-hidden focus:ring-1 focus:ring-blue-500 focus:border-blue-500 bg-white",children:[(0,l.jsx)("option",{value:"all",children:"All Status"}),(0,l.jsx)("option",{value:"pending",children:"Pending Review"}),(0,l.jsx)("option",{value:"active",children:"Active"}),(0,l.jsx)("option",{value:"rejected",children:"Rejected"})]}),(0,l.jsxs)("button",{type:"button",onClick:()=>O(!0),className:"ml-auto flex items-center gap-2 bg-blue-500 hover:bg-blue-600 text-white text-sm font-medium px-4 py-2 rounded-md transition-colors",children:[(0,l.jsx)(ax.PlusIcon,{className:"h-4 w-4"}),"Add Guardrail"]})]}),(0,l.jsxs)("div",{className:"space-y-3",children:[w&&(0,l.jsx)("div",{className:"text-center py-12 text-gray-500 text-sm",children:"Loading submissions…"}),N&&(0,l.jsx)("div",{className:"text-center py-12 text-red-600 text-sm",children:N}),!w&&!N&&0===t.length&&(0,l.jsx)("div",{className:"text-center py-12 text-gray-400 text-sm",children:"No guardrails match your filters."}),!w&&!N&&t.map(e=>(0,l.jsx)(aE,{guardrail:e,isSelected:h===e.id,isHeadersExpanded:j.has(e.id),onSelect:()=>f(h===e.id?null:e.id),onToggleForwardKey:()=>R(e.id),onToggleHeaders:()=>{var t;return t=e.id,void _(e=>{let a=new Set(e);return a.has(t)?a.delete(t):a.add(t),a})},onApprove:()=>v({id:e.id,action:"approve"}),onReject:()=>v({id:e.id,action:"reject"})},e.id))]})]}),B&&(0,l.jsx)(aR,{guardrail:B,onClose:()=>f(null),onApprove:()=>v({id:B.id,action:"approve"}),onReject:()=>v({id:B.id,action:"reject"}),onToggleForwardKey:()=>R(B.id),onUpdateCustomHeaders:e=>G(B.id,e),onUpdateExtraHeaders:e=>z(B.id,e)}),b&&(0,l.jsx)(aG,{action:b.action,guardrailName:t.find(e=>e.id===b.id)?.name??"",onConfirm:()=>"approve"===b.action?D(b.id):K(b.id),onCancel:()=>v(null)}),(0,l.jsxs)(g.Modal,{title:"Submit Guardrail for Review",open:A,onCancel:()=>{O(!1),T.resetFields()},onOk:()=>T.submit(),okText:"Submit for Review",children:[(0,l.jsx)("div",{className:"rounded-md bg-blue-50 border border-blue-200 px-4 py-3 text-sm text-blue-800 mb-4",children:"Your guardrail will be sent for admin review before it becomes active."}),(0,l.jsxs)(u.Form,{form:T,layout:"vertical",initialValues:{mode:"pre_call"},onFinish:async e=>{let t={...e.extra_litellm_params?JSON.parse(e.extra_litellm_params):{},guardrail:"generic_guardrail_api",mode:e.mode,api_base:e.api_base};try{await P.mutateAsync({team_id:e.team_id,guardrail_name:e.guardrail_name,litellm_params:t,guardrail_info:e.guardrail_info?JSON.parse(e.guardrail_info):void 0}),y.default.success("Guardrail submitted for review"),O(!1),T.resetFields(),L()}catch{}},children:[(0,l.jsx)(u.Form.Item,{label:"Team",name:"team_id",rules:[{required:!0,message:"Select a team"}],children:(0,l.jsx)(aC.default,{})}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Name",name:"guardrail_name",rules:[{required:!0,message:"Enter a guardrail name"}],children:(0,l.jsx)(p.Input,{placeholder:"e.g. pii-detection"})}),(0,l.jsx)(u.Form.Item,{label:"Mode",name:"mode",rules:[{required:!0,message:"Select a mode"}],children:(0,l.jsxs)(x.Select,{children:[(0,l.jsx)(x.Select.Option,{value:"pre_call",children:"Pre Call"}),(0,l.jsx)(x.Select.Option,{value:"post_call",children:"Post Call"}),(0,l.jsx)(x.Select.Option,{value:"during_call",children:"During Call"})]})}),(0,l.jsx)(u.Form.Item,{label:"API Base URL",name:"api_base",rules:[{required:!0,message:"Enter the API base URL"},{type:"url",message:"Must be a valid URL"}],children:(0,l.jsx)(p.Input,{placeholder:"https://your-guardrail-api.com/v1/check",className:"font-mono"})}),(0,l.jsx)(u.Form.Item,{label:"Additional litellm_params (optional)",name:"extra_litellm_params",tooltip:"JSON object merged into litellm_params. e.g. forward_api_key, headers, model, unreachable_fallback",rules:[{validator:(e,t)=>{if(!t)return Promise.resolve();try{let e=JSON.parse(t);if("object"!=typeof e||Array.isArray(e))return Promise.reject("Must be a JSON object");return Promise.resolve()}catch{return Promise.reject("Invalid JSON")}}}],children:(0,l.jsx)(p.Input.TextArea,{rows:3,className:"font-mono text-xs",placeholder:'{"forward_api_key": true, "headers": {"X-Custom": "value"}}'})}),(0,l.jsx)(u.Form.Item,{label:"Guardrail Info (optional)",name:"guardrail_info",rules:[{validator:(e,t)=>{if(!t)return Promise.resolve();try{return JSON.parse(t),Promise.resolve()}catch{return Promise.reject("Invalid JSON")}}}],children:(0,l.jsx)(p.Input.TextArea,{rows:3,className:"font-mono text-xs",placeholder:'{"description": "Detects PII in requests"}'})})]})]})]})}let aD=({accessToken:e,userRole:t})=>{let[a,u]=(0,r.useState)([]),[p,g]=(0,r.useState)(!1),[x,h]=(0,r.useState)(!1),[f,j]=(0,r.useState)(!1),[_,b]=(0,r.useState)(!1),[v,w]=(0,r.useState)(null),[C,N]=(0,r.useState)(!1),[k,S]=(0,r.useState)(null),I=!!t&&(0,tj.isAdminRole)(t),A=async()=>{if(e){j(!0);try{let t=await (0,m.getGuardrailsList)(e);u(t.guardrails)}catch(e){console.error("Error fetching guardrails:",e)}finally{j(!1)}}};(0,r.useEffect)(()=>{A()},[e]);let O=()=>{A()},T=async()=>{if(v&&e){b(!0);try{await (0,m.deleteGuardrailCall)(e,v.guardrail_id),y.default.success(`Guardrail "${v.guardrail_name}" deleted successfully`),await A()}catch(e){console.error("Error deleting guardrail:",e),y.default.fromBackend("Failed to delete guardrail")}finally{b(!1),N(!1),w(null)}}},P=v&&v.litellm_params?eh(v.litellm_params.guardrail).displayName:void 0;return(0,l.jsx)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:(0,l.jsx)(n.Tabs,{defaultActiveKey:"guardrails",items:[...I?[{key:"garden",label:"Guardrail Garden",children:(0,l.jsx)(ap,{accessToken:e,onGuardrailCreated:O})},{key:"guardrails",label:"Guardrails",children:(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("div",{className:"flex justify-between items-center mb-4",children:(0,l.jsx)(s.Dropdown,{menu:{items:[{key:"provider",icon:(0,l.jsx)(d.PlusOutlined,{}),label:"Add Provider Guardrail",onClick:()=>{k&&S(null),g(!0)}},{key:"custom_code",icon:(0,l.jsx)(c.CodeOutlined,{}),label:"Create Custom Code Guardrail",onClick:()=>{k&&S(null),h(!0)}}]},trigger:["click"],disabled:!e,children:(0,l.jsxs)(i.Button,{disabled:!e,children:["+ Add New Guardrail ",(0,l.jsx)(o.DownOutlined,{className:"ml-2"})]})})}),k?(0,l.jsx)(t0,{guardrailId:k,onClose:()=>S(null),accessToken:e,isAdmin:I}):(0,l.jsx)(ty,{guardrailsList:a,isLoading:f,onDeleteClick:(e,t)=>{w(a.find(t=>t.guardrail_id===e)||null),N(!0)},accessToken:e,onGuardrailUpdated:A,isAdmin:I,onGuardrailClick:e=>S(e)}),(0,l.jsx)(e5,{visible:p,onClose:()=>{g(!1)},accessToken:e,onSuccess:O}),(0,l.jsx)(tZ,{visible:x,onClose:()=>{h(!1)},accessToken:e,onSuccess:O}),(0,l.jsx)(al.default,{isOpen:C,title:"Delete Guardrail",message:`Are you sure you want to delete guardrail: ${v?.guardrail_name}? This action cannot be undone.`,resourceInformationTitle:"Guardrail Information",resourceInformation:[{label:"Name",value:v?.guardrail_name},{label:"ID",value:v?.guardrail_id,code:!0},{label:"Provider",value:P},{label:"Mode",value:v?.litellm_params.mode},{label:"Default On",value:v?.litellm_params.default_on?"Yes":"No"}],onCancel:()=>{N(!1),w(null)},onOk:T,confirmLoading:_})]})},{key:"playground",label:"Test Playground",disabled:!e,children:(0,l.jsx)(aa,{guardrailsList:a,isLoading:f,accessToken:e,onClose:()=>{}})}]:[],{key:"submitted",label:"Submitted Guardrails",children:(0,l.jsx)(az,{accessToken:e})}]})})};e.s(["default",0,function(){let{accessToken:e,userRole:t}=(0,aI.default)();return(0,l.jsx)(aD,{accessToken:e,userRole:t})}],509345)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01jbmgk~h02uq.js b/litellm/proxy/_experimental/out/_next/static/chunks/01jbmgk~h02uq.js new file mode 100644 index 00000000000..6f0b448504e --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01jbmgk~h02uq.js @@ -0,0 +1,420 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,602073,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64L128 192v384c0 212.1 171.9 384 384 384s384-171.9 384-384V192L512 64zm312 512c0 172.3-139.7 312-312 312S200 748.3 200 576V246l312-110 312 110v330z"}},{tag:"path",attrs:{d:"M378.4 475.1a35.91 35.91 0 00-50.9 0 35.91 35.91 0 000 50.9l129.4 129.4 2.1 2.1a33.98 33.98 0 0048.1 0L730.6 434a33.98 33.98 0 000-48.1l-2.8-2.8a33.98 33.98 0 00-48.1 0L483 579.7 378.4 475.1z"}}]},name:"safety",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["SafetyOutlined",0,n],602073)},283713,e=>{"use strict";var t=e.i(271645),i=e.i(602869),a=e.i(612256);let o="litellm_selected_worker_id";e.s(["useWorker",0,()=>{let{data:e}=(0,a.useUIConfig)(),n=e?.is_control_plane??!1,r=e?.workers??[],[s,l]=(0,t.useState)(()=>localStorage.getItem(o));(0,t.useEffect)(()=>{if(!s||0===r.length)return;let e=r.find(e=>e.worker_id===s);e&&(0,i.switchToWorkerUrl)(e.url)},[s,r]);let d=r.find(e=>e.worker_id===s)??null,p=(0,t.useCallback)(e=>{let t=r.find(t=>t.worker_id===e);t&&(l(e),localStorage.setItem(o,e),(0,i.switchToWorkerUrl)(t.url))},[r]);return{isControlPlane:n,workers:r,selectedWorkerId:s,selectedWorker:d,selectWorker:p,disconnectFromWorker:(0,t.useCallback)(()=>{l(null),localStorage.removeItem(o),(0,i.switchToWorkerUrl)(null)},[])}}])},295320,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M704 446H320c-4.4 0-8 3.6-8 8v402c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8V454c0-4.4-3.6-8-8-8zm-328 64h272v117H376V510zm272 290H376V683h272v117z"}},{tag:"path",attrs:{d:"M424 748a32 32 0 1064 0 32 32 0 10-64 0zm0-178a32 32 0 1064 0 32 32 0 10-64 0z"}},{tag:"path",attrs:{d:"M811.4 368.9C765.6 248 648.9 162 512.2 162S258.8 247.9 213 368.8C126.9 391.5 63.5 470.2 64 563.6 64.6 668 145.6 752.9 247.6 762c4.7.4 8.7-3.3 8.7-8v-60.4c0-4-3-7.4-7-7.9-27-3.4-52.5-15.2-72.1-34.5-24-23.5-37.2-55.1-37.2-88.6 0-28 9.1-54.4 26.2-76.4 16.7-21.4 40.2-36.9 66.1-43.7l37.9-10 13.9-36.7c8.6-22.8 20.6-44.2 35.7-63.5 14.9-19.2 32.6-36 52.4-50 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.3c19.9 14 37.5 30.8 52.4 50 15.1 19.3 27.1 40.7 35.7 63.5l13.8 36.6 37.8 10c54.2 14.4 92.1 63.7 92.1 120 0 33.6-13.2 65.1-37.2 88.6-19.5 19.2-44.9 31.1-71.9 34.5-4 .5-6.9 3.9-6.9 7.9V754c0 4.7 4.1 8.4 8.8 8 101.7-9.2 182.5-94 183.2-198.2.6-93.4-62.7-172.1-148.6-194.9z"}}]},name:"cloud-server",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["CloudServerOutlined",0,n],295320)},477189,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"}}]},name:"appstore",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["AppstoreOutlined",0,n],477189)},326373,e=>{"use strict";var t=e.i(21539);e.s(["Dropdown",()=>t.default])},344523,e=>{"use strict";let t=(0,e.i(475254).default)("chevrons-up-down",[["path",{d:"m7 15 5 5 5-5",key:"1hf1tw"}],["path",{d:"m7 9 5-5 5 5",key:"sgt6xg"}]]);e.s(["ChevronsUpDown",0,t],344523)},100486,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M899.6 276.5L705 396.4 518.4 147.5a8.06 8.06 0 00-12.9 0L319 396.4 124.3 276.5c-5.7-3.5-13.1 1.2-12.2 7.9L188.5 865c1.1 7.9 7.9 14 16 14h615.1c8 0 14.9-6 15.9-14l76.4-580.6c.8-6.7-6.5-11.4-12.3-7.9zm-126 534.1H250.3l-53.8-409.4 139.8 86.1L512 252.9l175.7 234.4 139.8-86.1-53.9 409.4zM512 509c-62.1 0-112.6 50.5-112.6 112.6S449.9 734.2 512 734.2s112.6-50.5 112.6-112.6S574.1 509 512 509zm0 160.9c-26.6 0-48.2-21.6-48.2-48.3 0-26.6 21.6-48.3 48.2-48.3s48.2 21.6 48.2 48.3c0 26.6-21.6 48.3-48.2 48.3z"}}]},name:"crown",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["CrownOutlined",0,n],100486)},879664,e=>{"use strict";let t=(0,e.i(475254).default)("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);e.s(["default",0,t])},596239,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M574 665.4a8.03 8.03 0 00-11.3 0L446.5 781.6c-53.8 53.8-144.6 59.5-204 0-59.5-59.5-53.8-150.2 0-204l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3l-39.8-39.8a8.03 8.03 0 00-11.3 0L191.4 526.5c-84.6 84.6-84.6 221.5 0 306s221.5 84.6 306 0l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3L574 665.4zm258.6-474c-84.6-84.6-221.5-84.6-306 0L410.3 307.6a8.03 8.03 0 000 11.3l39.7 39.7c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c53.8-53.8 144.6-59.5 204 0 59.5 59.5 53.8 150.2 0 204L665.3 562.6a8.03 8.03 0 000 11.3l39.8 39.8c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c84.5-84.6 84.5-221.5 0-306.1zM610.1 372.3a8.03 8.03 0 00-11.3 0L372.3 598.7a8.03 8.03 0 000 11.3l39.6 39.6c3.1 3.1 8.2 3.1 11.3 0l226.4-226.4c3.1-3.1 3.1-8.2 0-11.3l-39.5-39.6z"}}]},name:"link",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["LinkOutlined",0,n],596239)},652272,209261,e=>{"use strict";var t=e.i(843476),i=e.i(271645),a=e.i(447566),o=e.i(166406),n=e.i(492030),r=e.i(596239);let s=/^[a-zA-Z0-9][a-zA-Z0-9._-]*(\/[a-zA-Z0-9][a-zA-Z0-9._-]*)*$/,l=e=>e.trim().replace(/\/+$/,""),d=/\.(md|markdown|txt|json|ya?ml|toml)$/i,p=/^\d{1,3}(\.\d{1,3}){3}$/,c=/^[A-Za-z0-9-]+$/,m=/^[A-Za-z0-9._-]+$/,u=e=>e.pathname.split("/").filter(e=>""!==e),g=e=>{let t=e.split("/").filter(e=>""!==e);return t[t.length-1]??""},f=e=>e.toLowerCase().replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,""),h=e=>{let{source:t}=e;return"github"===t.source&&t.repo?`/plugin marketplace add ${t.repo}`:("url"===t.source||"git-subdir"===t.source)&&t.url?`/plugin marketplace add ${t.url}`:`/plugin marketplace add ${e.name}`};e.s(["formatInstallCommand",0,h,"getCategoryBadgeColor",0,e=>{if(!e)return"gray";let t=e.toLowerCase();if(t.includes("development")||t.includes("dev"))return"blue";if(t.includes("productivity")||t.includes("workflow"))return"green";if(t.includes("learning")||t.includes("education"))return"purple";if(t.includes("security")||t.includes("safety"))return"red";if(t.includes("data")||t.includes("analytics"))return"orange";else if(t.includes("integration")||t.includes("api"))return"yellow";return"gray"},"isValidEmail",0,e=>!e||/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e),"isValidSemanticVersion",0,e=>!e||/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(e),"isValidSubPath",0,e=>{let t=l(e);return""!==t&&s.test(t)},"isValidUrl",0,e=>{if(!e)return!0;try{return new URL(e),!0}catch{return!1}},"parseKeywords",0,e=>e&&""!==e.trim()?e.split(",").map(e=>e.trim()).filter(e=>""!==e):[],"parseSkillSource",0,(e,t)=>{let i=(e=>{let t,i=e.trim();if(""===i||i.startsWith("//"))return null;let a=/^[a-z][a-z0-9+.-]*:\/\//i.test(i)?i:`https://${i}`;try{t=new URL(a)}catch{return null}return"https:"!==t.protocol||""!==t.username||""!==t.password||!t.hostname.includes(".")||t.hostname.startsWith("[")||p.test(t.hostname)?null:t})(e);if(!i)return null;if("github.com"===i.hostname.replace(/^www\./,""))return((e,t)=>{let i=u(e);if(i.length<2)return null;let a=i[0],o=i[1].replace(/\.git$/,"");if(!c.test(a)||!m.test(o))return null;let n=`${a}/${o}`,r=`https://github.com/${n}`,p={parsed:{source:"github",repo:n},label:`GitHub repo — ${n}`,suggestedName:f(o)};if(i.length>=4&&("tree"===i[2]||"blob"===i[2])){let e=i.slice(4),t=g(e.join("/")),a=d.test(t)?e.slice(0,-1):e;if(0===a.length)return p;let o=l(a.join("/"));return s.test(o)?{parsed:{source:"git-subdir",url:r,path:o},label:`GitHub subdir — ${n} @ ${o}`,suggestedName:f(g(o))}:null}if(2!==i.length)return null;let h=l(t??"");return""!==h?s.test(h)?{parsed:{source:"git-subdir",url:r,path:h},label:`GitHub subdir — ${n} @ ${h}`,suggestedName:f(g(h))}:null:p})(i,t);if(u(i).length<2)return null;let a=`${i.protocol}//${i.host}${i.pathname.replace(/\/+$/,"")}`,o=l(t??"");return""!==o?s.test(o)?{parsed:{source:"git-subdir",url:a,path:o},label:`Git subdir — ${a} @ ${o}`,suggestedName:f(g(o))}:null:{parsed:{source:"url",url:a},label:`Git repo — ${a}`,suggestedName:f(g(i.pathname).replace(/\.git$/,""))}},"validatePluginName",0,e=>!!e&&""!==e.trim()&&/^[a-z0-9-]+$/.test(e)],209261),e.s(["default",0,({skill:e,onBack:s})=>{let l,[d,p]=(0,i.useState)("overview"),[c,m]=(0,i.useState)(null),u=(e,t)=>{navigator.clipboard.writeText(e),m(t),setTimeout(()=>m(null),2e3)},g="github"===(l=e.source).source&&l.repo?`https://github.com/${l.repo}`:"git-subdir"===l.source&&l.url?l.path?`${l.url}/tree/main/${l.path}`:l.url:"url"===l.source&&l.url?l.url:null,f=h(e),_=[...e.category?[{property:"Category",value:e.category}]:[],...e.domain?[{property:"Domain",value:e.domain}]:[],...e.namespace?[{property:"Namespace",value:e.namespace}]:[],...e.version?[{property:"Version",value:e.version}]:[],...e.author?.name?[{property:"Author",value:e.author.name}]:[],...e.created_at?[{property:"Added",value:new Date(e.created_at).toLocaleDateString()}]:[]];return(0,t.jsxs)("div",{style:{padding:"24px 32px 24px 0"},children:[(0,t.jsxs)("div",{onClick:s,style:{display:"inline-flex",alignItems:"center",gap:6,color:"#5f6368",cursor:"pointer",fontSize:14,marginBottom:24},children:[(0,t.jsx)(a.ArrowLeftOutlined,{style:{fontSize:11}}),(0,t.jsx)("span",{children:"Skills"})]}),(0,t.jsxs)("div",{style:{marginBottom:8},children:[(0,t.jsx)("h1",{style:{fontSize:28,fontWeight:400,color:"#202124",margin:0,lineHeight:1.2},children:e.name}),e.description&&(0,t.jsx)("p",{style:{fontSize:14,color:"#5f6368",margin:"8px 0 0 0",lineHeight:1.6},children:e.description})]}),(0,t.jsx)("div",{style:{borderBottom:"1px solid #dadce0",marginBottom:28,marginTop:24},children:(0,t.jsx)("div",{style:{display:"flex",gap:0},children:[{key:"overview",label:"Overview"},{key:"usage",label:"How to Use"}].map(e=>(0,t.jsx)("div",{onClick:()=>p(e.key),style:{padding:"12px 20px",fontSize:14,color:d===e.key?"#1a73e8":"#5f6368",borderBottom:d===e.key?"3px solid #1a73e8":"3px solid transparent",cursor:"pointer",fontWeight:d===e.key?500:400,marginBottom:-1},children:e.label},e.key))})}),"overview"===d&&(0,t.jsxs)("div",{style:{display:"flex",gap:64},children:[(0,t.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,t.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 4px 0"},children:"Skill Details"}),(0,t.jsx)("p",{style:{fontSize:13,color:"#5f6368",margin:"0 0 16px 0"},children:"Metadata registered with this skill"}),(0,t.jsxs)("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:14},children:[(0,t.jsx)("thead",{children:(0,t.jsxs)("tr",{style:{borderBottom:"1px solid #dadce0"},children:[(0,t.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500,width:160},children:"Property"}),(0,t.jsx)("th",{style:{textAlign:"left",padding:"12px 0",color:"#5f6368",fontWeight:500},children:e.name})]})}),(0,t.jsx)("tbody",{children:_.map((e,i)=>(0,t.jsxs)("tr",{style:{borderBottom:"1px solid #f1f3f4"},children:[(0,t.jsx)("td",{style:{padding:"12px 0",color:"#3c4043"},children:e.property}),(0,t.jsx)("td",{style:{padding:"12px 0",color:"#202124"},children:e.value})]},i))})]})]}),(0,t.jsxs)("div",{style:{width:240,flexShrink:0},children:[(0,t.jsxs)("div",{style:{marginBottom:24},children:[(0,t.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Status"}),(0,t.jsx)("span",{style:{fontSize:12,padding:"3px 10px",borderRadius:12,backgroundColor:e.enabled?"#e6f4ea":"#f1f3f4",color:e.enabled?"#137333":"#5f6368",fontWeight:500},children:e.enabled?"Public":"Draft"})]}),g&&(0,t.jsxs)("div",{style:{marginBottom:24},children:[(0,t.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Source"}),(0,t.jsxs)("a",{href:g,target:"_blank",rel:"noopener noreferrer",style:{fontSize:13,color:"#1a73e8",wordBreak:"break-all",display:"flex",alignItems:"center",gap:4},children:[g.replace("https://",""),(0,t.jsx)(r.LinkOutlined,{style:{fontSize:11,flexShrink:0}})]})]}),e.keywords&&e.keywords.length>0&&(0,t.jsxs)("div",{style:{marginBottom:24},children:[(0,t.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:8},children:"Tags"}),(0,t.jsx)("div",{style:{display:"flex",flexWrap:"wrap",gap:6},children:e.keywords.map(e=>(0,t.jsx)("span",{style:{fontSize:12,padding:"4px 12px",borderRadius:16,border:"1px solid #dadce0",color:"#3c4043",backgroundColor:"#fff"},children:e},e))})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{style:{fontSize:12,color:"#5f6368",marginBottom:4},children:"Skill ID"}),(0,t.jsx)("div",{style:{fontSize:12,fontFamily:"monospace",color:"#3c4043",wordBreak:"break-all"},children:e.id})]})]})]}),"usage"===d&&(0,t.jsxs)("div",{style:{maxWidth:640},children:[(0,t.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 8px 0"},children:"Using this skill"}),(0,t.jsx)("p",{style:{fontSize:14,color:"#5f6368",margin:"0 0 24px 0",lineHeight:1.6},children:"Once your proxy is set as a marketplace, enable this skill in Claude Code with one command:"}),(0,t.jsxs)("div",{style:{border:"1px solid #dadce0",borderRadius:8,overflow:"hidden",marginBottom:24},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"10px 16px",backgroundColor:"#f8f9fa",borderBottom:"1px solid #dadce0"},children:[(0,t.jsx)("span",{style:{fontSize:13,color:"#3c4043",fontWeight:500},children:"Run in Claude Code"}),(0,t.jsxs)("button",{onClick:()=>u(f,"install"),style:{display:"flex",alignItems:"center",gap:4,fontSize:12,color:"install"===c?"#137333":"#1a73e8",background:"none",border:"none",cursor:"pointer",padding:0},children:["install"===c?(0,t.jsx)(n.CheckOutlined,{}):(0,t.jsx)(o.CopyOutlined,{}),"install"===c?"Copied":"Copy"]})]}),(0,t.jsx)("pre",{style:{margin:0,padding:"14px 16px",fontSize:14,fontFamily:"monospace",color:"#202124",backgroundColor:"#fff"},children:f})]}),(0,t.jsxs)("p",{style:{fontSize:13,color:"#5f6368",lineHeight:1.6,margin:0},children:["Don't have the marketplace configured yet?"," ",(0,t.jsx)("span",{onClick:()=>p("setup"),style:{color:"#1a73e8",cursor:"pointer"},children:"See one-time setup →"})]})]}),"setup"===d&&(0,t.jsxs)("div",{style:{maxWidth:640},children:[(0,t.jsx)("h2",{style:{fontSize:18,fontWeight:400,color:"#202124",margin:"0 0 8px 0"},children:"One-time marketplace setup"}),(0,t.jsxs)("p",{style:{fontSize:14,color:"#5f6368",margin:"0 0 24px 0",lineHeight:1.6},children:["Add this to"," ",(0,t.jsx)("code",{style:{fontSize:13,backgroundColor:"#f1f3f4",padding:"1px 6px",borderRadius:4},children:"~/.claude/settings.json"})," ","to point Claude Code at your proxy:"]}),(0,t.jsxs)("div",{style:{border:"1px solid #dadce0",borderRadius:8,overflow:"hidden"},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"10px 16px",backgroundColor:"#f8f9fa",borderBottom:"1px solid #dadce0"},children:[(0,t.jsx)("span",{style:{fontSize:13,color:"#3c4043",fontWeight:500},children:"~/.claude/settings.json"}),(0,t.jsxs)("button",{onClick:()=>{u(JSON.stringify({extraKnownMarketplaces:{"my-org":{source:"url",url:`${window.location.origin}/claude-code/marketplace.json`}}},null,2),"settings")},style:{display:"flex",alignItems:"center",gap:4,fontSize:12,color:"settings"===c?"#137333":"#1a73e8",background:"none",border:"none",cursor:"pointer",padding:0},children:["settings"===c?(0,t.jsx)(n.CheckOutlined,{}):(0,t.jsx)(o.CopyOutlined,{}),"settings"===c?"Copied":"Copy"]})]}),(0,t.jsx)("pre",{style:{margin:0,padding:"14px 16px",fontSize:13,fontFamily:"monospace",color:"#202124",backgroundColor:"#fff"},children:JSON.stringify({extraKnownMarketplaces:{"my-org":{source:"url",url:`${window.location.origin}/claude-code/marketplace.json`}}},null,2)})]})]})]})}],652272)},798496,e=>{"use strict";var t=e.i(843476),i=e.i(152990),a=e.i(682830),o=e.i(271645),n=e.i(269200),r=e.i(427612),s=e.i(64848),l=e.i(942232),d=e.i(496020),p=e.i(977572),c=e.i(94629),m=e.i(360820),u=e.i(871943);e.s(["ModelDataTable",0,function({data:e=[],columns:g,isLoading:f=!1,defaultSorting:h=[],pagination:_,onPaginationChange:x,enablePagination:b=!1,onRowClick:y}){let[v,w]=o.default.useState(h),[j]=o.default.useState("onChange"),[S,k]=o.default.useState({}),[C,z]=o.default.useState({}),I=(0,i.useReactTable)({data:e,columns:g,state:{sorting:v,columnSizing:S,columnVisibility:C,...b&&_?{pagination:_}:{}},columnResizeMode:j,onSortingChange:w,onColumnSizingChange:k,onColumnVisibilityChange:z,...b&&x?{onPaginationChange:x}:{},getCoreRowModel:(0,a.getCoreRowModel)(),getSortedRowModel:(0,a.getSortedRowModel)(),...b?{getPaginationRowModel:(0,a.getPaginationRowModel)()}:{},enableSorting:!0,enableColumnResizing:!0,defaultColumn:{minSize:40,maxSize:500}});return(0,t.jsx)("div",{className:"rounded-lg custom-border relative",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsx)("div",{className:"relative min-w-full",children:(0,t.jsxs)(n.Table,{className:"[&_td]:py-2 [&_th]:py-2",style:{width:I.getTotalSize(),minWidth:"100%",tableLayout:"fixed"},children:[(0,t.jsx)(r.TableHead,{children:I.getHeaderGroups().map(e=>(0,t.jsx)(d.TableRow,{children:e.headers.map(e=>(0,t.jsxs)(s.TableHeaderCell,{className:`py-1 h-8 relative ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)] w-[120px] ml-8":""} ${e.column.columnDef.meta?.className||""}`,style:{width:"actions"===e.id?120:e.getSize(),position:"actions"===e.id?"sticky":"relative",right:"actions"===e.id?0:"auto"},onClick:e.column.getCanSort()?e.column.getToggleSortingHandler():void 0,children:[(0,t.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,t.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,i.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&e.column.getCanSort()&&(0,t.jsx)("div",{className:"w-4",children:e.column.getIsSorted()?({asc:(0,t.jsx)(m.ChevronUpIcon,{className:"h-4 w-4 text-blue-500"}),desc:(0,t.jsx)(u.ChevronDownIcon,{className:"h-4 w-4 text-blue-500"})})[e.column.getIsSorted()]:(0,t.jsx)(c.SwitchVerticalIcon,{className:"h-4 w-4 text-gray-400"})})]}),e.column.getCanResize()&&(0,t.jsx)("div",{onMouseDown:e.getResizeHandler(),onTouchStart:e.getResizeHandler(),className:`absolute right-0 top-0 h-full w-2 cursor-col-resize select-none touch-none ${e.column.getIsResizing()?"bg-blue-500":"hover:bg-blue-200"}`})]},e.id))},e.id))}),(0,t.jsx)(l.TableBody,{children:f?(0,t.jsx)(d.TableRow,{children:(0,t.jsx)(p.TableCell,{colSpan:g.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"🚅 Loading models..."})})})}):I.getRowModel().rows.length>0?I.getRowModel().rows.map(e=>(0,t.jsx)(d.TableRow,{onClick:()=>y?.(e.original),className:y?"cursor-pointer hover:bg-gray-50":"",children:e.getVisibleCells().map(e=>(0,t.jsx)(p.TableCell,{className:`py-0.5 overflow-hidden ${"actions"===e.column.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)] w-[120px] ml-8":""} ${e.column.columnDef.meta?.className||""}`,style:{width:"actions"===e.column.id?120:e.column.getSize(),position:"actions"===e.column.id?"sticky":"relative",right:"actions"===e.column.id?0:"auto"},children:(0,i.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,t.jsx)(d.TableRow,{children:(0,t.jsx)(p.TableCell,{colSpan:g.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"No models found"})})})})})]})})})})}])},339019,865361,e=>{"use strict";var t,i,a=((t={}).AUDIO_SPEECH="audio_speech",t.AUDIO_TRANSCRIPTION="audio_transcription",t.IMAGE_GENERATION="image_generation",t.VIDEO_GENERATION="video_generation",t.CHAT="chat",t.RESPONSES="responses",t.IMAGE_EDITS="image_edits",t.ANTHROPIC_MESSAGES="anthropic_messages",t.EMBEDDING="embedding",t),o=((i={}).IMAGE="image",i.VIDEO="video",i.CHAT="chat",i.RESPONSES="responses",i.IMAGE_EDITS="image_edits",i.ANTHROPIC_MESSAGES="anthropic_messages",i.EMBEDDINGS="embeddings",i.SPEECH="speech",i.TRANSCRIPTION="transcription",i.A2A_AGENTS="a2a_agents",i.MCP="mcp",i.REALTIME="realtime",i.INTERACTIONS="interactions",i);let n={image_generation:"image",video_generation:"video",chat:"chat",responses:"responses",image_edits:"image_edits",anthropic_messages:"anthropic_messages",audio_speech:"speech",audio_transcription:"transcription",embedding:"embeddings"};e.s(["EndpointType",()=>o,"getEndpointType",0,e=>Object.values(a).includes(e)?n[e]:"chat"],865361),e.s(["generateCodeSnippet",0,e=>{let t,{apiKeySource:i,accessToken:a,apiKey:n,inputMessage:r,chatHistory:s,selectedTags:l,selectedVectorStores:d,selectedGuardrails:p,selectedPolicies:c,selectedMCPServers:m,mcpServers:u,mcpServerToolRestrictions:g,selectedVoice:f,endpointType:h,selectedModel:_,selectedSdk:x,proxySettings:b}=e,y="session"===i?a:n,v=window.location.origin,w=b?.LITELLM_UI_API_DOC_BASE_URL;w&&w.trim()?v=w:b?.PROXY_BASE_URL&&(v=b.PROXY_BASE_URL);let j=r||"Your prompt here",S=j.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n"),k=s.filter(e=>!e.isImage).map(({role:e,content:t})=>({role:e,content:t})),C={};l.length>0&&(C.tags=l),d.length>0&&(C.vector_stores=d),p.length>0&&(C.guardrails=p),c.length>0&&(C.policies=c);let z=_||"your-model-name",I="azure"===x?`import openai + +client = openai.AzureOpenAI( + api_key="${y||"YOUR_LITELLM_API_KEY"}", + azure_endpoint="${v}", + api_version="2024-02-01" +)`:`import openai + +client = openai.OpenAI( + api_key="${y||"YOUR_LITELLM_API_KEY"}", + base_url="${v}" +)`;switch(h){case o.CHAT:{let e=Object.keys(C).length>0,i="";if(e){let e=JSON.stringify({metadata:C},null,2).split("\n").map(e=>" ".repeat(4)+e).join("\n").trim();i=`, + extra_body=${e}`}let a=k.length>0?k:[{role:"user",content:j}];t=` +import base64 + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# Example with text only +response = client.chat.completions.create( + model="${z}", + messages=${JSON.stringify(a,null,4)}${i} +) + +print(response) + +# Example with image or PDF (uncomment and provide file path to use) +# base64_file = encode_image("path/to/your/file.jpg") # or .pdf +# response_with_file = client.chat.completions.create( +# model="${z}", +# messages=[ +# { +# "role": "user", +# "content": [ +# { +# "type": "text", +# "text": "${S}" +# }, +# { +# "type": "image_url", +# "image_url": { +# "url": f"data:image/jpeg;base64,{base64_file}" # or data:application/pdf;base64,{base64_file} +# } +# } +# ] +# } +# ]${i} +# ) +# print(response_with_file) +`;break}case o.RESPONSES:{let e=Object.keys(C).length>0,i="";if(e){let e=JSON.stringify({metadata:C},null,2).split("\n").map(e=>" ".repeat(4)+e).join("\n").trim();i=`, + extra_body=${e}`}let a=k.length>0?k:[{role:"user",content:j}];t=` +import base64 + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# Example with text only +response = client.responses.create( + model="${z}", + input=${JSON.stringify(a,null,4)}${i} +) + +print(response.output_text) + +# Example with image or PDF (uncomment and provide file path to use) +# base64_file = encode_image("path/to/your/file.jpg") # or .pdf +# response_with_file = client.responses.create( +# model="${z}", +# input=[ +# { +# "role": "user", +# "content": [ +# {"type": "input_text", "text": "${S}"}, +# { +# "type": "input_image", +# "image_url": f"data:image/jpeg;base64,{base64_file}", # or data:application/pdf;base64,{base64_file} +# }, +# ], +# } +# ]${i} +# ) +# print(response_with_file.output_text) +`;break}case o.IMAGE:t="azure"===x?` +# NOTE: The Azure SDK does not have a direct equivalent to the multi-modal 'responses.create' method shown for OpenAI. +# This snippet uses 'client.images.generate' and will create a new image based on your prompt. +# It does not use the uploaded image, as 'client.images.generate' does not support image inputs in this context. +import os +import requests +import json +import time +from PIL import Image + +result = client.images.generate( + model="${z}", + prompt="${r}", + n=1 +) + +json_response = json.loads(result.model_dump_json()) + +# Set the directory for the stored image +image_dir = os.path.join(os.curdir, 'images') + +# If the directory doesn't exist, create it +if not os.path.isdir(image_dir): + os.mkdir(image_dir) + +# Initialize the image path +image_filename = f"generated_image_{int(time.time())}.png" +image_path = os.path.join(image_dir, image_filename) + +try: + # Retrieve the generated image + if json_response.get("data") && len(json_response["data"]) > 0 && json_response["data"][0].get("url"): + image_url = json_response["data"][0]["url"] + generated_image = requests.get(image_url).content + with open(image_path, "wb") as image_file: + image_file.write(generated_image) + + print(f"Image saved to {image_path}") + # Display the image + image = Image.open(image_path) + image.show() + else: + print("Could not find image URL in response.") + print("Full response:", json_response) +except Exception as e: + print(f"An error occurred: {e}") + print("Full response:", json_response) +`:` +import base64 +import os +import time +import json +from PIL import Image +import requests + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# Helper function to create a file (simplified for this example) +def create_file(image_path): + # In a real implementation, this would upload the file to OpenAI + # For this example, we'll just return a placeholder ID + return f"file_{os.path.basename(image_path).replace('.', '_')}" + +# The prompt entered by the user +prompt = "${S}" + +# Encode images to base64 +base64_image1 = encode_image("body-lotion.png") +base64_image2 = encode_image("soap.png") + +# Create file IDs +file_id1 = create_file("body-lotion.png") +file_id2 = create_file("incense-kit.png") + +response = client.responses.create( + model="${z}", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": prompt}, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image1}", + }, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image2}", + }, + { + "type": "input_image", + "file_id": file_id1, + }, + { + "type": "input_image", + "file_id": file_id2, + } + ], + } + ], + tools=[{"type": "image_generation"}], +) + +# Process the response +image_generation_calls = [ + output + for output in response.output + if output.type == "image_generation_call" +] + +image_data = [output.result for output in image_generation_calls] + +if image_data: + image_base64 = image_data[0] + image_filename = f"edited_image_{int(time.time())}.png" + with open(image_filename, "wb") as f: + f.write(base64.b64decode(image_base64)) + print(f"Image saved to {image_filename}") +else: + # If no image is generated, there might be a text response with an explanation + text_response = [output.text for output in response.output if hasattr(output, 'text')] + if text_response: + print("No image generated. Model response:") + print("\\n".join(text_response)) + else: + print("No image data found in response.") + print("Full response for debugging:") + print(response) +`;break;case o.IMAGE_EDITS:t="azure"===x?` +import base64 +import os +import time +import json +from PIL import Image +import requests + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# The prompt entered by the user +prompt = "${S}" + +# Encode images to base64 +base64_image1 = encode_image("body-lotion.png") +base64_image2 = encode_image("soap.png") + +# Create file IDs +file_id1 = create_file("body-lotion.png") +file_id2 = create_file("incense-kit.png") + +response = client.responses.create( + model="${z}", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": prompt}, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image1}", + }, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image2}", + }, + { + "type": "input_image", + "file_id": file_id1, + }, + { + "type": "input_image", + "file_id": file_id2, + } + ], + } + ], + tools=[{"type": "image_generation"}], +) + +# Process the response +image_generation_calls = [ + output + for output in response.output + if output.type == "image_generation_call" +] + +image_data = [output.result for output in image_generation_calls] + +if image_data: + image_base64 = image_data[0] + image_filename = f"edited_image_{int(time.time())}.png" + with open(image_filename, "wb") as f: + f.write(base64.b64decode(image_base64)) + print(f"Image saved to {image_filename}") +else: + # If no image is generated, there might be a text response with an explanation + text_response = [output.text for output in response.output if hasattr(output, 'text')] + if text_response: + print("No image generated. Model response:") + print("\\n".join(text_response)) + else: + print("No image data found in response.") + print("Full response for debugging:") + print(response) +`:` +import base64 +import os +import time + +# Helper function to encode images to base64 +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') + +# Helper function to create a file (simplified for this example) +def create_file(image_path): + # In a real implementation, this would upload the file to OpenAI + # For this example, we'll just return a placeholder ID + return f"file_{os.path.basename(image_path).replace('.', '_')}" + +# The prompt entered by the user +prompt = "${S}" + +# Encode images to base64 +base64_image1 = encode_image("body-lotion.png") +base64_image2 = encode_image("soap.png") + +# Create file IDs +file_id1 = create_file("body-lotion.png") +file_id2 = create_file("incense-kit.png") + +response = client.responses.create( + model="${z}", + input=[ + { + "role": "user", + "content": [ + {"type": "input_text", "text": prompt}, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image1}", + }, + { + "type": "input_image", + "image_url": f"data:image/jpeg;base64,{base64_image2}", + }, + { + "type": "input_image", + "file_id": file_id1, + }, + { + "type": "input_image", + "file_id": file_id2, + } + ], + } + ], + tools=[{"type": "image_generation"}], +) + +# Process the response +image_generation_calls = [ + output + for output in response.output + if output.type == "image_generation_call" +] + +image_data = [output.result for output in image_generation_calls] + +if image_data: + image_base64 = image_data[0] + image_filename = f"edited_image_{int(time.time())}.png" + with open(image_filename, "wb") as f: + f.write(base64.b64decode(image_base64)) + print(f"Image saved to {image_filename}") +else: + # If no image is generated, there might be a text response with an explanation + text_response = [output.text for output in response.output if hasattr(output, 'text')] + if text_response: + print("No image generated. Model response:") + print("\\n".join(text_response)) + else: + print("No image data found in response.") + print("Full response for debugging:") + print(response) +`;break;case o.EMBEDDINGS:t=` +response = client.embeddings.create( + input="${r||"Your string here"}", + model="${z}", + encoding_format="base64" # or "float" +) + +print(response.data[0].embedding) +`;break;case o.TRANSCRIPTION:t=` +# Open the audio file +audio_file = open("path/to/your/audio/file.mp3", "rb") + +# Make the transcription request +response = client.audio.transcriptions.create( + model="${z}", + file=audio_file${r?`, + prompt="${r.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:""} +) + +print(response.text) +`;break;case o.SPEECH:t=` +# Make the text-to-speech request +response = client.audio.speech.create( + model="${z}", + input="${r||"Your text to convert to speech here"}", + voice="${f}" # Options: alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer +) + +# Save the audio to a file +output_filename = "output_speech.mp3" +response.stream_to_file(output_filename) +print(f"Audio saved to {output_filename}") + +# Optional: Customize response format and speed +# response = client.audio.speech.create( +# model="${z}", +# input="${r||"Your text to convert to speech here"}", +# voice="alloy", +# response_format="mp3", # Options: mp3, opus, aac, flac, wav, pcm +# speed=1.0 # Range: 0.25 to 4.0 +# ) +# response.stream_to_file("output_speech.mp3") +`;break;default:t="\n# Code generation for this endpoint is not implemented yet."}return`${I} +${t}`}],339019)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01m7lab3u92-v.js b/litellm/proxy/_experimental/out/_next/static/chunks/01m7lab3u92-v.js new file mode 100644 index 00000000000..dd0196da59e --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01m7lab3u92-v.js @@ -0,0 +1,13 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,244451,e=>{"use strict";let t;e.i(247167);var i=e.i(271645),n=e.i(343794),o=e.i(242064),a=e.i(763731),l=e.i(174428);let r=80*Math.PI,c=e=>{let{dotClassName:t,style:o,hasCircleCls:a}=e;return i.createElement("circle",{className:(0,n.default)(`${t}-circle`,{[`${t}-circle-bg`]:a}),r:40,cx:50,cy:50,strokeWidth:20,style:o})},s=({percent:e,prefixCls:t})=>{let o=`${t}-dot`,a=`${o}-holder`,s=`${a}-hidden`,[d,u]=i.useState(!1);(0,l.default)(()=>{0!==e&&u(!0)},[0!==e]);let m=Math.max(Math.min(e,100),0);if(!d)return null;let p={strokeDashoffset:`${r/4}`,strokeDasharray:`${r*m/100} ${r*(100-m)/100}`};return i.createElement("span",{className:(0,n.default)(a,`${o}-progress`,m<=0&&s)},i.createElement("svg",{viewBox:"0 0 100 100",role:"progressbar","aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":m},i.createElement(c,{dotClassName:o,hasCircleCls:!0}),i.createElement(c,{dotClassName:o,style:p})))};function d(e){let{prefixCls:t,percent:o=0}=e,a=`${t}-dot`,l=`${a}-holder`,r=`${l}-hidden`;return i.createElement(i.Fragment,null,i.createElement("span",{className:(0,n.default)(l,o>0&&r)},i.createElement("span",{className:(0,n.default)(a,`${t}-dot-spin`)},[1,2,3,4].map(e=>i.createElement("i",{className:`${t}-dot-item`,key:e})))),i.createElement(s,{prefixCls:t,percent:o}))}function u(e){var t;let{prefixCls:o,indicator:l,percent:r}=e,c=`${o}-dot`;return l&&i.isValidElement(l)?(0,a.cloneElement)(l,{className:(0,n.default)(null==(t=l.props)?void 0:t.className,c),percent:r}):i.createElement(d,{prefixCls:o,percent:r})}e.i(296059);var m=e.i(694758),p=e.i(183293),g=e.i(246422),f=e.i(838378);let b=new m.Keyframes("antSpinMove",{to:{opacity:1}}),h=new m.Keyframes("antRotate",{to:{transform:"rotate(405deg)"}}),v=(0,g.genStyleHooks)("Spin",e=>(e=>{let{componentCls:t,calc:i}=e;return{[t]:Object.assign(Object.assign({},(0,p.resetComponent)(e)),{position:"absolute",display:"none",color:e.colorPrimary,fontSize:0,textAlign:"center",verticalAlign:"middle",opacity:0,transition:`transform ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`,"&-spinning":{position:"relative",display:"inline-block",opacity:1},[`${t}-text`]:{fontSize:e.fontSize,paddingTop:i(i(e.dotSize).sub(e.fontSize)).div(2).add(2).equal()},"&-fullscreen":{position:"fixed",width:"100vw",height:"100vh",backgroundColor:e.colorBgMask,zIndex:e.zIndexPopupBase,inset:0,display:"flex",alignItems:"center",flexDirection:"column",justifyContent:"center",opacity:0,visibility:"hidden",transition:`all ${e.motionDurationMid}`,"&-show":{opacity:1,visibility:"visible"},[t]:{[`${t}-dot-holder`]:{color:e.colorWhite},[`${t}-text`]:{color:e.colorTextLightSolid}}},"&-nested-loading":{position:"relative",[`> div > ${t}`]:{position:"absolute",top:0,insetInlineStart:0,zIndex:4,display:"block",width:"100%",height:"100%",maxHeight:e.contentHeight,[`${t}-dot`]:{position:"absolute",top:"50%",insetInlineStart:"50%",margin:i(e.dotSize).mul(-1).div(2).equal()},[`${t}-text`]:{position:"absolute",top:"50%",width:"100%",textShadow:`0 1px 2px ${e.colorBgContainer}`},[`&${t}-show-text ${t}-dot`]:{marginTop:i(e.dotSize).div(2).mul(-1).sub(10).equal()},"&-sm":{[`${t}-dot`]:{margin:i(e.dotSizeSM).mul(-1).div(2).equal()},[`${t}-text`]:{paddingTop:i(i(e.dotSizeSM).sub(e.fontSize)).div(2).add(2).equal()},[`&${t}-show-text ${t}-dot`]:{marginTop:i(e.dotSizeSM).div(2).mul(-1).sub(10).equal()}},"&-lg":{[`${t}-dot`]:{margin:i(e.dotSizeLG).mul(-1).div(2).equal()},[`${t}-text`]:{paddingTop:i(i(e.dotSizeLG).sub(e.fontSize)).div(2).add(2).equal()},[`&${t}-show-text ${t}-dot`]:{marginTop:i(e.dotSizeLG).div(2).mul(-1).sub(10).equal()}}},[`${t}-container`]:{position:"relative",transition:`opacity ${e.motionDurationSlow}`,"&::after":{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,zIndex:10,width:"100%",height:"100%",background:e.colorBgContainer,opacity:0,transition:`all ${e.motionDurationSlow}`,content:'""',pointerEvents:"none"}},[`${t}-blur`]:{clear:"both",opacity:.5,userSelect:"none",pointerEvents:"none","&::after":{opacity:.4,pointerEvents:"auto"}}},"&-tip":{color:e.spinDotDefault},[`${t}-dot-holder`]:{width:"1em",height:"1em",fontSize:e.dotSize,display:"inline-block",transition:`transform ${e.motionDurationSlow} ease, opacity ${e.motionDurationSlow} ease`,transformOrigin:"50% 50%",lineHeight:1,color:e.colorPrimary,"&-hidden":{transform:"scale(0.3)",opacity:0}},[`${t}-dot-progress`]:{position:"absolute",inset:0},[`${t}-dot`]:{position:"relative",display:"inline-block",fontSize:e.dotSize,width:"1em",height:"1em","&-item":{position:"absolute",display:"block",width:i(e.dotSize).sub(i(e.marginXXS).div(2)).div(2).equal(),height:i(e.dotSize).sub(i(e.marginXXS).div(2)).div(2).equal(),background:"currentColor",borderRadius:"100%",transform:"scale(0.75)",transformOrigin:"50% 50%",opacity:.3,animationName:b,animationDuration:"1s",animationIterationCount:"infinite",animationTimingFunction:"linear",animationDirection:"alternate","&:nth-child(1)":{top:0,insetInlineStart:0,animationDelay:"0s"},"&:nth-child(2)":{top:0,insetInlineEnd:0,animationDelay:"0.4s"},"&:nth-child(3)":{insetInlineEnd:0,bottom:0,animationDelay:"0.8s"},"&:nth-child(4)":{bottom:0,insetInlineStart:0,animationDelay:"1.2s"}},"&-spin":{transform:"rotate(45deg)",animationName:h,animationDuration:"1.2s",animationIterationCount:"infinite",animationTimingFunction:"linear"},"&-circle":{strokeLinecap:"round",transition:["stroke-dashoffset","stroke-dasharray","stroke","stroke-width","opacity"].map(t=>`${t} ${e.motionDurationSlow} ease`).join(","),fillOpacity:0,stroke:"currentcolor"},"&-circle-bg":{stroke:e.colorFillSecondary}},[`&-sm ${t}-dot`]:{"&, &-holder":{fontSize:e.dotSizeSM}},[`&-sm ${t}-dot-holder`]:{i:{width:i(i(e.dotSizeSM).sub(i(e.marginXXS).div(2))).div(2).equal(),height:i(i(e.dotSizeSM).sub(i(e.marginXXS).div(2))).div(2).equal()}},[`&-lg ${t}-dot`]:{"&, &-holder":{fontSize:e.dotSizeLG}},[`&-lg ${t}-dot-holder`]:{i:{width:i(i(e.dotSizeLG).sub(e.marginXXS)).div(2).equal(),height:i(i(e.dotSizeLG).sub(e.marginXXS)).div(2).equal()}},[`&${t}-show-text ${t}-text`]:{display:"block"}})}})((0,f.mergeToken)(e,{spinDotDefault:e.colorTextDescription})),e=>{let{controlHeightLG:t,controlHeight:i}=e;return{contentHeight:400,dotSize:t/2,dotSizeSM:.35*t,dotSizeLG:i}}),S=[[30,.05],[70,.03],[96,.01]];var $=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};let y=e=>{var a;let{prefixCls:l,spinning:r=!0,delay:c=0,className:s,rootClassName:d,size:m="default",tip:p,wrapperClassName:g,style:f,children:b,fullscreen:h=!1,indicator:y,percent:C}=e,k=$(e,["prefixCls","spinning","delay","className","rootClassName","size","tip","wrapperClassName","style","children","fullscreen","indicator","percent"]),{getPrefixCls:x,direction:z,className:E,style:N,indicator:w}=(0,o.useComponentConfig)("spin"),j=x("spin",l),[I,O,M]=v(j),[B,D]=i.useState(()=>r&&(!r||!c||!!Number.isNaN(Number(c)))),T=function(e,t){let[n,o]=i.useState(0),a=i.useRef(null),l="auto"===t;return i.useEffect(()=>(l&&e&&(o(0),a.current=setInterval(()=>{o(e=>{let t=100-e;for(let i=0;i{a.current&&(clearInterval(a.current),a.current=null)}),[l,e]),l?n:t}(B,C);i.useEffect(()=>{if(r){let e=function(e,t,i){var n,o=i||{},a=o.noTrailing,l=void 0!==a&&a,r=o.noLeading,c=void 0!==r&&r,s=o.debounceMode,d=void 0===s?void 0:s,u=!1,m=0;function p(){n&&clearTimeout(n)}function g(){for(var i=arguments.length,o=Array(i),a=0;ae?c?(m=Date.now(),l||(n=setTimeout(d?f:g,e))):g():!0!==l&&(n=setTimeout(d?f:g,void 0===d?e-s:e)))}return g.cancel=function(e){var t=(e||{}).upcomingOnly;p(),u=!(void 0!==t&&t)},g}(c,()=>{D(!0)},{debounceMode:false});return e(),()=>{var t;null==(t=null==e?void 0:e.cancel)||t.call(e)}}D(!1)},[c,r]);let P=i.useMemo(()=>void 0!==b&&!h,[b,h]),H=(0,n.default)(j,E,{[`${j}-sm`]:"small"===m,[`${j}-lg`]:"large"===m,[`${j}-spinning`]:B,[`${j}-show-text`]:!!p,[`${j}-rtl`]:"rtl"===z},s,!h&&d,O,M),A=(0,n.default)(`${j}-container`,{[`${j}-blur`]:B}),q=null!=(a=null!=y?y:w)?a:t,R=Object.assign(Object.assign({},N),f),_=i.createElement("div",Object.assign({},k,{style:R,className:H,"aria-live":"polite","aria-busy":B}),i.createElement(u,{prefixCls:j,indicator:q,percent:T}),p&&(P||h)?i.createElement("div",{className:`${j}-text`},p):null);return I(P?i.createElement("div",Object.assign({},k,{className:(0,n.default)(`${j}-nested-loading`,g,O,M)}),B&&i.createElement("div",{key:"loading"},_),i.createElement("div",{className:A,key:"container"},b)):h?i.createElement("div",{className:(0,n.default)(`${j}-fullscreen`,{[`${j}-fullscreen-show`]:B},d,O,M)},_):_)};y.setDefaultIndicator=e=>{t=e},e.s(["default",0,y],244451)},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},165370,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(931067);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M272.9 512l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512zm304 0l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"}}]},name:"double-left",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,a){return t.createElement(o.default,(0,i.default)({},e,{ref:a,icon:n}))});let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"}}]},name:"double-right",theme:"outlined"};var r=t.forwardRef(function(e,n){return t.createElement(o.default,(0,i.default)({},e,{ref:n,icon:l}))}),c=e.i(801312),s=e.i(286612),d=e.i(343794),u=e.i(211577),m=e.i(410160),p=e.i(209428),g=e.i(392221),f=e.i(914949),b=e.i(404948),h=e.i(244009);e.i(883110);let v={items_per_page:"条/页",jump_to:"跳至",jump_to_confirm:"确定",page:"页",prev_page:"上一页",next_page:"下一页",prev_5:"向前 5 页",next_5:"向后 5 页",prev_3:"向前 3 页",next_3:"向后 3 页",page_size:"页码"};var S=[10,20,50,100];let $=function(e){var i=e.pageSizeOptions,n=void 0===i?S:i,o=e.locale,a=e.changeSize,l=e.pageSize,r=e.goButton,c=e.quickGo,s=e.rootPrefixCls,d=e.disabled,u=e.buildOptionText,m=e.showSizeChanger,p=e.sizeChangerRender,f=t.default.useState(""),h=(0,g.default)(f,2),v=h[0],$=h[1],y=function(){return!v||Number.isNaN(v)?void 0:Number(v)},C="function"==typeof u?u:function(e){return"".concat(e," ").concat(o.items_per_page)},k=function(e){""!==v&&(e.keyCode===b.default.ENTER||"click"===e.type)&&($(""),null==c||c(y()))},x="".concat(s,"-options");if(!m&&!c)return null;var z=null,E=null,N=null;return m&&p&&(z=p({disabled:d,size:l,onSizeChange:function(e){null==a||a(Number(e))},"aria-label":o.page_size,className:"".concat(x,"-size-changer"),options:(n.some(function(e){return e.toString()===l.toString()})?n:n.concat([l]).sort(function(e,t){return(Number.isNaN(Number(e))?0:Number(e))-(Number.isNaN(Number(t))?0:Number(t))})).map(function(e){return{label:C(e),value:e}})})),c&&(r&&(N="boolean"==typeof r?t.default.createElement("button",{type:"button",onClick:k,onKeyUp:k,disabled:d,className:"".concat(x,"-quick-jumper-button")},o.jump_to_confirm):t.default.createElement("span",{onClick:k,onKeyUp:k},r)),E=t.default.createElement("div",{className:"".concat(x,"-quick-jumper")},o.jump_to,t.default.createElement("input",{disabled:d,type:"text",value:v,onChange:function(e){$(e.target.value)},onKeyUp:k,onBlur:function(e){r||""===v||($(""),e.relatedTarget&&(e.relatedTarget.className.indexOf("".concat(s,"-item-link"))>=0||e.relatedTarget.className.indexOf("".concat(s,"-item"))>=0)||null==c||c(y()))},"aria-label":o.page}),o.page,N)),t.default.createElement("li",{className:x},z,E)},y=function(e){var i=e.rootPrefixCls,n=e.page,o=e.active,a=e.className,l=e.showTitle,r=e.onClick,c=e.onKeyPress,s=e.itemRender,m="".concat(i,"-item"),p=(0,d.default)(m,"".concat(m,"-").concat(n),(0,u.default)((0,u.default)({},"".concat(m,"-active"),o),"".concat(m,"-disabled"),!n),a),g=s(n,"page",t.default.createElement("a",{rel:"nofollow"},n));return g?t.default.createElement("li",{title:l?String(n):null,className:p,onClick:function(){r(n)},onKeyDown:function(e){c(e,r,n)},tabIndex:0},g):null};var C=function(e,t,i){return i};function k(){}function x(e){var t=Number(e);return"number"==typeof t&&!Number.isNaN(t)&&isFinite(t)&&Math.floor(t)===t}function z(e,t,i){return Math.floor((i-1)/(void 0===e?t:e))+1}let E=function(e){var n,o,a,l,r=e.prefixCls,c=void 0===r?"rc-pagination":r,s=e.selectPrefixCls,S=e.className,E=e.current,N=e.defaultCurrent,w=e.total,j=void 0===w?0:w,I=e.pageSize,O=e.defaultPageSize,M=e.onChange,B=void 0===M?k:M,D=e.hideOnSinglePage,T=e.align,P=e.showPrevNextJumpers,H=e.showQuickJumper,A=e.showLessItems,q=e.showTitle,R=void 0===q||q,_=e.onShowSizeChange,L=void 0===_?k:_,X=e.locale,W=void 0===X?v:X,K=e.style,F=e.totalBoundaryShowSizeChanger,G=e.disabled,U=e.simple,J=e.showTotal,V=e.showSizeChanger,Q=void 0===V?j>(void 0===F?50:F):V,Y=e.sizeChangerRender,Z=e.pageSizeOptions,ee=e.itemRender,et=void 0===ee?C:ee,ei=e.jumpPrevIcon,en=e.jumpNextIcon,eo=e.prevIcon,ea=e.nextIcon,el=t.default.useRef(null),er=(0,f.default)(10,{value:I,defaultValue:void 0===O?10:O}),ec=(0,g.default)(er,2),es=ec[0],ed=ec[1],eu=(0,f.default)(1,{value:E,defaultValue:void 0===N?1:N,postState:function(e){return Math.max(1,Math.min(e,z(void 0,es,j)))}}),em=(0,g.default)(eu,2),ep=em[0],eg=em[1],ef=t.default.useState(ep),eb=(0,g.default)(ef,2),eh=eb[0],ev=eb[1];(0,t.useEffect)(function(){ev(ep)},[ep]);var eS=Math.max(1,ep-(A?3:5)),e$=Math.min(z(void 0,es,j),ep+(A?3:5));function ey(i,n){var o=i||t.default.createElement("button",{type:"button","aria-label":n,className:"".concat(c,"-item-link")});return"function"==typeof i&&(o=t.default.createElement(i,(0,p.default)({},e))),o}function eC(e){var t=e.target.value,i=z(void 0,es,j);return""===t?t:Number.isNaN(Number(t))?eh:t>=i?i:Number(t)}var ek=j>es&&H;function ex(e){var t=eC(e);switch(t!==eh&&ev(t),e.keyCode){case b.default.ENTER:ez(t);break;case b.default.UP:ez(t-1);break;case b.default.DOWN:ez(t+1)}}function ez(e){if(x(e)&&e!==ep&&x(j)&&j>0&&!G){var t=z(void 0,es,j),i=e;return e>t?i=t:e<1&&(i=1),i!==eh&&ev(i),eg(i),null==B||B(i,es),i}return ep}var eE=ep>1,eN=ep2?i-2:0),o=2;oj?j:ep*es])),eH=null,eA=z(void 0,es,j);if(D&&j<=es)return null;var eq=[],eR={rootPrefixCls:c,onClick:ez,onKeyPress:eM,showTitle:R,itemRender:et,page:-1},e_=ep-1>0?ep-1:0,eL=ep+1=2*eG&&3!==ep&&(eq[0]=t.default.cloneElement(eq[0],{className:(0,d.default)("".concat(c,"-item-after-jump-prev"),eq[0].props.className)}),eq.unshift(eD)),eA-ep>=2*eG&&ep!==eA-2){var e2=eq[eq.length-1];eq[eq.length-1]=t.default.cloneElement(e2,{className:(0,d.default)("".concat(c,"-item-before-jump-next"),e2.props.className)}),eq.push(eH)}1!==eZ&&eq.unshift(t.default.createElement(y,(0,i.default)({},eR,{key:1,page:1}))),e0!==eA&&eq.push(t.default.createElement(y,(0,i.default)({},eR,{key:eA,page:eA})))}var e3=(n=et(e_,"prev",ey(eo,"prev page")),t.default.isValidElement(n)?t.default.cloneElement(n,{disabled:!eE}):n);if(e3){var e4=!eE||!eA;e3=t.default.createElement("li",{title:R?W.prev_page:null,onClick:ew,tabIndex:e4?null:0,onKeyDown:function(e){eM(e,ew)},className:(0,d.default)("".concat(c,"-prev"),(0,u.default)({},"".concat(c,"-disabled"),e4)),"aria-disabled":e4},e3)}var e9=(o=et(eL,"next",ey(ea,"next page")),t.default.isValidElement(o)?t.default.cloneElement(o,{disabled:!eN}):o);e9&&(U?(a=!eN,l=eE?0:null):l=(a=!eN||!eA)?null:0,e9=t.default.createElement("li",{title:R?W.next_page:null,onClick:ej,tabIndex:l,onKeyDown:function(e){eM(e,ej)},className:(0,d.default)("".concat(c,"-next"),(0,u.default)({},"".concat(c,"-disabled"),a)),"aria-disabled":a},e9));var e5=(0,d.default)(c,S,(0,u.default)((0,u.default)((0,u.default)((0,u.default)((0,u.default)({},"".concat(c,"-start"),"start"===T),"".concat(c,"-center"),"center"===T),"".concat(c,"-end"),"end"===T),"".concat(c,"-simple"),U),"".concat(c,"-disabled"),G));return t.default.createElement("ul",(0,i.default)({className:e5,style:K,ref:el},eT),eP,e3,U?eF:eq,e9,t.default.createElement($,{locale:W,rootPrefixCls:c,disabled:G,selectPrefixCls:void 0===s?"rc-select":s,changeSize:function(e){var t=z(e,es,j),i=ep>t&&0!==t?t:ep;ed(e),ev(i),null==L||L(ep,e),eg(i),null==B||B(i,e)},pageSize:es,pageSizeOptions:Z,quickGo:ek?ez:null,goButton:eK,showSizeChanger:Q,sizeChangerRender:Y}))};var N=e.i(727214),w=e.i(242064),j=e.i(517455),I=e.i(150073),O=e.i(408850),M=e.i(327494),B=e.i(104458);e.i(296059);var D=e.i(915654),T=e.i(349942),P=e.i(517458),H=e.i(889943),A=e.i(183293),q=e.i(246422),R=e.i(838378);let _=e=>Object.assign({itemBg:e.colorBgContainer,itemSize:e.controlHeight,itemSizeSM:e.controlHeightSM,itemActiveBg:e.colorBgContainer,itemActiveColor:e.colorPrimary,itemActiveColorHover:e.colorPrimaryHover,itemLinkBg:e.colorBgContainer,itemActiveColorDisabled:e.colorTextDisabled,itemActiveBgDisabled:e.controlItemBgActiveDisabled,itemInputBg:e.colorBgContainer,miniOptionsSizeChangerTop:0},(0,P.initComponentToken)(e)),L=e=>(0,R.mergeToken)(e,{inputOutlineOffset:0,quickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.25).equal(),paginationMiniOptionsMarginInlineStart:e.calc(e.marginXXS).div(2).equal(),paginationMiniQuickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.1).equal(),paginationItemPaddingInline:e.calc(e.marginXXS).mul(1.5).equal(),paginationEllipsisLetterSpacing:e.calc(e.marginXXS).div(2).equal(),paginationSlashMarginInlineStart:e.marginSM,paginationSlashMarginInlineEnd:e.marginSM,paginationEllipsisTextIndent:"0.13em"},(0,P.initInputToken)(e)),X=(0,q.genStyleHooks)("Pagination",e=>{let t=L(e);return[(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,A.resetComponent)(e)),{display:"flex",flexWrap:"wrap",rowGap:e.paddingXS,"&-start":{justifyContent:"start"},"&-center":{justifyContent:"center"},"&-end":{justifyContent:"end"},"ul, ol":{margin:0,padding:0,listStyle:"none"},"&::after":{display:"block",clear:"both",height:0,overflow:"hidden",visibility:"hidden",content:'""'},[`${t}-total-text`]:{display:"inline-block",height:e.itemSize,marginInlineEnd:e.marginXS,lineHeight:(0,D.unit)(e.calc(e.itemSize).sub(2).equal()),verticalAlign:"middle"}}),(e=>{let{componentCls:t}=e;return{[`${t}-item`]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,marginInlineEnd:e.marginXS,fontFamily:e.fontFamily,lineHeight:(0,D.unit)(e.calc(e.itemSize).sub(2).equal()),textAlign:"center",verticalAlign:"middle",listStyle:"none",backgroundColor:e.itemBg,border:`${(0,D.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:0,cursor:"pointer",userSelect:"none",a:{display:"block",padding:`0 ${(0,D.unit)(e.paginationItemPaddingInline)}`,color:e.colorText,"&:hover":{textDecoration:"none"}},[`&:not(${t}-item-active)`]:{"&:hover":{transition:`all ${e.motionDurationMid}`,backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive}},"&-active":{fontWeight:e.fontWeightStrong,backgroundColor:e.itemActiveBg,borderColor:e.colorPrimary,a:{color:e.itemActiveColor},"&:hover":{borderColor:e.colorPrimaryHover},"&:hover a":{color:e.itemActiveColorHover}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-jump-prev, ${t}-jump-next`]:{outline:0,[`${t}-item-container`]:{position:"relative",[`${t}-item-link-icon`]:{color:e.colorPrimary,fontSize:e.fontSizeSM,opacity:0,transition:`all ${e.motionDurationMid}`,"&-svg":{top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,margin:"auto"}},[`${t}-item-ellipsis`]:{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,display:"block",margin:"auto",color:e.colorTextDisabled,letterSpacing:e.paginationEllipsisLetterSpacing,textAlign:"center",textIndent:e.paginationEllipsisTextIndent,opacity:1,transition:`all ${e.motionDurationMid}`}},"&:hover":{[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}}},[` + ${t}-prev, + ${t}-jump-prev, + ${t}-jump-next + `]:{marginInlineEnd:e.marginXS},[` + ${t}-prev, + ${t}-next, + ${t}-jump-prev, + ${t}-jump-next + `]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,color:e.colorText,fontFamily:e.fontFamily,lineHeight:(0,D.unit)(e.itemSize),textAlign:"center",verticalAlign:"middle",listStyle:"none",borderRadius:e.borderRadius,cursor:"pointer",transition:`all ${e.motionDurationMid}`},[`${t}-prev, ${t}-next`]:{outline:0,button:{color:e.colorText,cursor:"pointer",userSelect:"none"},[`${t}-item-link`]:{display:"block",width:"100%",height:"100%",padding:0,fontSize:e.fontSizeSM,textAlign:"center",backgroundColor:"transparent",border:`${(0,D.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:"none",transition:`all ${e.motionDurationMid}`},[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover`]:{[`${t}-item-link`]:{backgroundColor:"transparent"}}},[`${t}-slash`]:{marginInlineEnd:e.paginationSlashMarginInlineEnd,marginInlineStart:e.paginationSlashMarginInlineStart},[`${t}-options`]:{display:"inline-block",marginInlineStart:e.margin,verticalAlign:"middle","&-size-changer":{display:"inline-block",width:"auto"},"&-quick-jumper":{display:"inline-block",height:e.controlHeight,marginInlineStart:e.marginXS,lineHeight:(0,D.unit)(e.controlHeight),verticalAlign:"top",input:Object.assign(Object.assign(Object.assign({},(0,T.genBasicInputStyle)(e)),(0,H.genBaseOutlinedStyle)(e,{borderColor:e.colorBorder,hoverBorderColor:e.colorPrimaryHover,activeBorderColor:e.colorPrimary,activeShadow:e.activeShadow})),{"&[disabled]":Object.assign({},(0,H.genDisabledStyle)(e)),width:e.quickJumperInputWidth,height:e.controlHeight,boxSizing:"border-box",margin:0,marginInlineStart:e.marginXS,marginInlineEnd:e.marginXS})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-simple`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSize,lineHeight:(0,D.unit)(e.itemSize),verticalAlign:"top",[`${t}-item-link`]:{height:e.itemSize,backgroundColor:"transparent",border:0,"&:hover":{backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive},"&::after":{height:e.itemSize,lineHeight:(0,D.unit)(e.itemSize)}}},[`${t}-simple-pager`]:{display:"inline-flex",alignItems:"center",height:e.itemSize,marginInlineEnd:e.marginXS,input:{boxSizing:"border-box",height:"100%",width:e.quickJumperInputWidth,padding:`0 ${(0,D.unit)(e.paginationItemPaddingInline)}`,textAlign:"center",backgroundColor:e.itemInputBg,border:`${(0,D.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadius,outline:"none",transition:`border-color ${e.motionDurationMid}`,color:"inherit","&:hover":{borderColor:e.colorPrimary},"&:focus":{borderColor:e.colorPrimaryHover,boxShadow:`${(0,D.unit)(e.inputOutlineOffset)} 0 ${(0,D.unit)(e.controlOutlineWidth)} ${e.controlOutline}`},"&[disabled]":{color:e.colorTextDisabled,backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,cursor:"not-allowed"}}},[`&${t}-disabled`]:{[`${t}-prev, ${t}-next`]:{[`${t}-item-link`]:{"&:hover, &:active":{backgroundColor:"transparent"}}}},[`&${t}-mini`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM),[`${t}-item-link`]:{height:e.itemSizeSM,"&::after":{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM)}}},[`${t}-simple-pager`]:{height:e.itemSizeSM,input:{width:e.paginationMiniQuickJumperInputWidth}}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-mini ${t}-total-text, &${t}-mini ${t}-simple-pager`]:{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-item`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,D.unit)(e.calc(e.itemSizeSM).sub(2).equal())},[`&${t}-mini ${t}-prev, &${t}-mini ${t}-next`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,D.unit)(e.itemSizeSM)},[`&${t}-mini:not(${t}-disabled)`]:{[`${t}-prev, ${t}-next`]:{[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover ${t}-item-link`]:{backgroundColor:"transparent"}}},[` + &${t}-mini ${t}-prev ${t}-item-link, + &${t}-mini ${t}-next ${t}-item-link + `]:{backgroundColor:"transparent",borderColor:"transparent","&::after":{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM)}},[`&${t}-mini ${t}-jump-prev, &${t}-mini ${t}-jump-next`]:{height:e.itemSizeSM,marginInlineEnd:0,lineHeight:(0,D.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-options`]:{marginInlineStart:e.paginationMiniOptionsMarginInlineStart,"&-size-changer":{top:e.miniOptionsSizeChangerTop},"&-quick-jumper":{height:e.itemSizeSM,lineHeight:(0,D.unit)(e.itemSizeSM),input:Object.assign(Object.assign({},(0,T.genInputSmallStyle)(e)),{width:e.paginationMiniQuickJumperInputWidth,height:e.controlHeightSM})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-disabled`]:{"&, &:hover":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}},"&:focus-visible":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}}},[`&${t}-disabled`]:{cursor:"not-allowed",[`${t}-item`]:{cursor:"not-allowed",backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"},a:{color:e.colorTextDisabled,backgroundColor:"transparent",border:"none",cursor:"not-allowed"},"&-active":{borderColor:e.colorBorder,backgroundColor:e.itemActiveBgDisabled,"&:hover, &:active":{backgroundColor:e.itemActiveBgDisabled},a:{color:e.itemActiveColorDisabled}}},[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:hover, &:active":{backgroundColor:"transparent"},[`${t}-simple&`]:{backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"}}},[`${t}-simple-pager`]:{color:e.colorTextDisabled},[`${t}-jump-prev, ${t}-jump-next`]:{[`${t}-item-link-icon`]:{opacity:0},[`${t}-item-ellipsis`]:{opacity:1}}}}})(e)),{[`@media only screen and (max-width: ${e.screenLG}px)`]:{[`${t}-item`]:{"&-after-jump-prev, &-before-jump-next":{display:"none"}}},[`@media only screen and (max-width: ${e.screenSM}px)`]:{[`${t}-options`]:{display:"none"}}}),[`&${e.componentCls}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t}=e;return{[`${t}:not(${t}-disabled)`]:{[`${t}-item`]:Object.assign({},(0,A.genFocusStyle)(e)),[`${t}-jump-prev, ${t}-jump-next`]:{"&:focus-visible":Object.assign({[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}},(0,A.genFocusOutline)(e))},[`${t}-prev, ${t}-next`]:{[`&:focus-visible ${t}-item-link`]:(0,A.genFocusOutline)(e)}}}})(t)]},_),W=(0,q.genSubStyleComponent)(["Pagination","bordered"],e=>(e=>{let{componentCls:t}=e;return{[`${t}${t}-bordered${t}-disabled:not(${t}-mini)`]:{"&, &:hover":{[`${t}-item-link`]:{borderColor:e.colorBorder}},"&:focus-visible":{[`${t}-item-link`]:{borderColor:e.colorBorder}},[`${t}-item, ${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,[`&:hover:not(${t}-item-active)`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,a:{color:e.colorTextDisabled}},[`&${t}-item-active`]:{backgroundColor:e.itemActiveBgDisabled}},[`${t}-prev, ${t}-next`]:{"&:hover button":{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,color:e.colorTextDisabled},[`${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder}}},[`${t}${t}-bordered:not(${t}-mini)`]:{[`${t}-prev, ${t}-next`]:{"&:hover button":{borderColor:e.colorPrimaryHover,backgroundColor:e.itemBg},[`${t}-item-link`]:{backgroundColor:e.itemLinkBg,borderColor:e.colorBorder},[`&:hover ${t}-item-link`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,color:e.colorPrimary},[`&${t}-disabled`]:{[`${t}-item-link`]:{borderColor:e.colorBorder,color:e.colorTextDisabled}}},[`${t}-item`]:{backgroundColor:e.itemBg,border:`${(0,D.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,[`&:hover:not(${t}-item-active)`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,a:{color:e.colorPrimary}},"&-active":{borderColor:e.colorPrimary}}}}})(L(e)),_);function K(e){return(0,t.useMemo)(()=>"boolean"==typeof e?[e,{}]:e&&"object"==typeof e?[!0,e]:[void 0,void 0],[e])}var F=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};e.s(["default",0,e=>{let{align:i,prefixCls:n,selectPrefixCls:o,className:l,rootClassName:u,style:m,size:p,locale:g,responsive:f,showSizeChanger:b,selectComponentClass:h,pageSizeOptions:v}=e,S=F(e,["align","prefixCls","selectPrefixCls","className","rootClassName","style","size","locale","responsive","showSizeChanger","selectComponentClass","pageSizeOptions"]),{xs:$}=(0,I.default)(f),[,y]=(0,B.useToken)(),{getPrefixCls:C,direction:k,showSizeChanger:x,className:z,style:D}=(0,w.useComponentConfig)("pagination"),T=C("pagination",n),[P,H,A]=X(T),q=(0,j.default)(p),R="small"===q||!!($&&!q&&f),[_]=(0,O.useLocale)("Pagination",N.default),L=Object.assign(Object.assign({},_),g),[G,U]=K(b),[J,V]=K(x),Q=null!=U?U:V,Y=h||M.default,Z=t.useMemo(()=>v?v.map(e=>Number(e)):void 0,[v]),ee=t.useMemo(()=>{let e=t.createElement("span",{className:`${T}-item-ellipsis`},"•••"),i=t.createElement("button",{className:`${T}-item-link`,type:"button",tabIndex:-1},"rtl"===k?t.createElement(s.default,null):t.createElement(c.default,null)),n=t.createElement("button",{className:`${T}-item-link`,type:"button",tabIndex:-1},"rtl"===k?t.createElement(c.default,null):t.createElement(s.default,null));return{prevIcon:i,nextIcon:n,jumpPrevIcon:t.createElement("a",{className:`${T}-item-link`},t.createElement("div",{className:`${T}-item-container`},"rtl"===k?t.createElement(r,{className:`${T}-item-link-icon`}):t.createElement(a,{className:`${T}-item-link-icon`}),e)),jumpNextIcon:t.createElement("a",{className:`${T}-item-link`},t.createElement("div",{className:`${T}-item-container`},"rtl"===k?t.createElement(a,{className:`${T}-item-link-icon`}):t.createElement(r,{className:`${T}-item-link-icon`}),e))}},[k,T]),et=C("select",o),ei=(0,d.default)({[`${T}-${i}`]:!!i,[`${T}-mini`]:R,[`${T}-rtl`]:"rtl"===k,[`${T}-bordered`]:y.wireframe},z,l,u,H,A),en=Object.assign(Object.assign({},D),m);return P(t.createElement(t.Fragment,null,y.wireframe&&t.createElement(W,{prefixCls:T}),t.createElement(E,Object.assign({},ee,S,{style:en,prefixCls:T,selectPrefixCls:et,className:ei,locale:L,pageSizeOptions:Z,showSizeChanger:null!=G?G:J,sizeChangerRender:e=>{var i;let{disabled:n,size:o,onSizeChange:a,"aria-label":l,className:r,options:c}=e,{className:s,onChange:u}=Q||{},m=null==(i=c.find(e=>String(e.value)===String(o)))?void 0:i.value;return t.createElement(Y,Object.assign({disabled:n,showSearch:!0,popupMatchSelectWidth:!1,getPopupContainer:e=>e.parentNode,"aria-label":l,options:c},Q,{value:m,onChange:(e,t)=>{null==a||a(e),null==u||u(e,t)},size:R?"small":"middle",className:(0,d.default)(r,s)}))}}))))}],165370)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01ut.srbq8~b9.js b/litellm/proxy/_experimental/out/_next/static/chunks/01ut.srbq8~b9.js new file mode 100644 index 00000000000..cfc8e6ddd0d --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01ut.srbq8~b9.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,44121,186515,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 000 13.8z"}}]},name:"menu-fold",theme:"outlined"};var s=e.i(9583),r=a.forwardRef(function(e,r){return a.createElement(s.default,(0,t.default)({},e,{ref:r,icon:l}))});e.s(["MenuFoldOutlined",0,r],44121);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 000-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0014.4 7z"}}]},name:"menu-unfold",theme:"outlined"};var n=a.forwardRef(function(e,l){return a.createElement(s.default,(0,t.default)({},e,{ref:l,icon:i}))});e.s(["MenuUnfoldOutlined",0,n],186515)},251773,276701,771243,895335,e=>{"use strict";var t=e.i(843476),a=e.i(731565),l=e.i(602869),s=e.i(266027);async function r(){let e=(0,l.getProxyBaseUrl)(),t=await fetch(`${e}/public/litellm_blog_posts`);if(!t.ok)throw Error(`Failed to fetch blog posts: ${t.statusText}`);return t.json()}let i="inline-flex h-9 shrink-0 items-center justify-center gap-1 rounded-md px-2 text-sm font-medium leading-none text-gray-800 transition-colors hover:bg-gray-100 hover:text-gray-950";e.s(["NAV_PRODUCT_LINK_CLASS",0,i],276701);var n=e.i(755151),o=e.i(56456),c=e.i(464571),d=e.i(326373),m=e.i(770914),h=e.i(898586);let{Text:u,Title:g,Paragraph:x}=h.Typography;e.s(["BlogDropdown",0,()=>{let e,l=(0,a.useDisableBlogPosts)(),{data:h,isLoading:p,isError:f,refetch:b}=(0,s.useQuery)({queryKey:["blogPosts"],queryFn:r,staleTime:36e5,retry:1,retryDelay:0});return l?null:(e=p?[{key:"loading",label:(0,t.jsx)(o.LoadingOutlined,{}),disabled:!0}]:f?[{key:"error",label:(0,t.jsxs)(m.Space,{children:[(0,t.jsx)(u,{type:"danger",children:"Failed to load posts"}),(0,t.jsx)(c.Button,{size:"small",onClick:()=>b(),children:"Retry"})]}),disabled:!0}]:h&&0!==h.posts.length?[...h.posts.slice(0,5).map(e=>({key:e.url,label:(0,t.jsxs)("a",{href:e.url,target:"_blank",rel:"noopener noreferrer",style:{display:"block",width:380},children:[(0,t.jsx)(g,{level:5,style:{marginBottom:2},children:e.title}),(0,t.jsx)(u,{type:"secondary",style:{fontSize:11},children:new Date(e.date+"T00:00:00").toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}),(0,t.jsx)(x,{ellipsis:{rows:2},children:e.description})]})})),{type:"divider"},{key:"view-all",label:(0,t.jsx)("a",{href:"https://docs.litellm.ai/blog",target:"_blank",rel:"noopener noreferrer",children:"View all posts"})}]:[{key:"empty",label:(0,t.jsx)(u,{type:"secondary",children:"No posts available"}),disabled:!0}],(0,t.jsx)(d.Dropdown,{menu:{items:e},trigger:["hover"],placement:"bottomRight",children:(0,t.jsxs)(c.Button,{type:"text",className:`${i} border-0! bg-transparent!`,children:["Blog",(0,t.jsx)(n.DownOutlined,{className:"text-[10px] text-gray-500","aria-hidden":!0})]})}))}],251773);var p=e.i(636772);e.i(247167);var f=e.i(931067),b=e.i(271645);let y={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9a127.5 127.5 0 0138.1 91v112.5c.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z"}}]},name:"github",theme:"outlined"};var j=e.i(9583),v=b.forwardRef(function(e,t){return b.createElement(j.default,(0,f.default)({},e,{ref:t,icon:y}))});let w={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M409.4 128c-42.4 0-76.7 34.4-76.7 76.8 0 20.3 8.1 39.9 22.4 54.3a76.74 76.74 0 0054.3 22.5h76.7v-76.8c0-42.3-34.3-76.7-76.7-76.8zm0 204.8H204.7c-42.4 0-76.7 34.4-76.7 76.8s34.4 76.8 76.7 76.8h204.6c42.4 0 76.7-34.4 76.7-76.8.1-42.4-34.3-76.8-76.6-76.8zM614 486.4c42.4 0 76.8-34.4 76.7-76.8V204.8c0-42.4-34.3-76.8-76.7-76.8-42.4 0-76.7 34.4-76.7 76.8v204.8c0 42.5 34.3 76.8 76.7 76.8zm281.4-76.8c0-42.4-34.4-76.8-76.7-76.8S742 367.2 742 409.6v76.8h76.7c42.3 0 76.7-34.4 76.7-76.8zm-76.8 128H614c-42.4 0-76.7 34.4-76.7 76.8 0 20.3 8.1 39.9 22.4 54.3a76.74 76.74 0 0054.3 22.5h204.6c42.4 0 76.7-34.4 76.7-76.8.1-42.4-34.3-76.7-76.7-76.8zM614 742.4h-76.7v76.8c0 42.4 34.4 76.8 76.7 76.8 42.4 0 76.8-34.4 76.7-76.8.1-42.4-34.3-76.7-76.7-76.8zM409.4 537.6c-42.4 0-76.7 34.4-76.7 76.8v204.8c0 42.4 34.4 76.8 76.7 76.8 42.4 0 76.8-34.4 76.7-76.8V614.4c0-20.3-8.1-39.9-22.4-54.3a76.92 76.92 0 00-54.3-22.5zM128 614.4c0 20.3 8.1 39.9 22.4 54.3a76.74 76.74 0 0054.3 22.5c42.4 0 76.8-34.4 76.7-76.8v-76.8h-76.7c-42.3 0-76.7 34.4-76.7 76.8z"}}]},name:"slack",theme:"outlined"};var k=b.forwardRef(function(e,t){return b.createElement(j.default,(0,f.default)({},e,{ref:t,icon:w}))}),S=e.i(592968);let N="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md border-0 bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 cursor-pointer";e.s(["CommunityEngagementButtons",0,()=>(0,p.useDisableShowPrompts)()?null:(0,t.jsxs)("div",{className:"flex items-center gap-0.5 rounded-md border border-gray-200/80 bg-gray-50 px-0.5 py-0","aria-label":"Community links",children:[(0,t.jsx)(S.Tooltip,{title:"LiteLLM Slack community",children:(0,t.jsx)("a",{href:"https://www.litellm.ai/support",target:"_blank",rel:"noopener noreferrer",className:N,"aria-label":"Join Slack",children:(0,t.jsx)(k,{className:"text-lg"})})}),(0,t.jsx)(S.Tooltip,{title:"LiteLLM on GitHub",children:(0,t.jsx)("a",{href:"https://github.com/BerriAI/litellm",target:"_blank",rel:"noopener noreferrer",className:N,"aria-label":"LiteLLM on GitHub",children:(0,t.jsx)(v,{className:"text-lg"})})})]})],771243);var C=e.i(115571);let L="litellmHideAgentPlatformBanner";function B(e){let t=t=>{t.key===L&&e()},a=t=>{let{key:a}=t.detail;a===L&&e()};return window.addEventListener("storage",t),window.addEventListener(C.LOCAL_STORAGE_EVENT,a),()=>{window.removeEventListener("storage",t),window.removeEventListener(C.LOCAL_STORAGE_EVENT,a)}}function z(){return"true"===(0,C.getLocalStorageItem)(L)}let _={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M816 768h-24V428c0-141.1-104.3-257.7-240-277.1V112c0-22.1-17.9-40-40-40s-40 17.9-40 40v38.9c-135.7 19.4-240 136-240 277.1v340h-24c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h216c0 61.8 50.2 112 112 112s112-50.2 112-112h216c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM512 888c-26.5 0-48-21.5-48-48h96c0 26.5-21.5 48-48 48zM304 768V428c0-55.6 21.6-107.8 60.9-147.1S456.4 220 512 220c55.6 0 107.8 21.6 147.1 60.9S720 372.4 720 428v340H304z"}}]},name:"bell",theme:"outlined"};var I=b.forwardRef(function(e,t){return b.createElement(j.default,(0,f.default)({},e,{ref:t,icon:_}))}),A=e.i(906579),P=e.i(282786);e.s(["NotificationsBell",0,()=>{let e=!(0,b.useSyncExternalStore)(B,z),[a,l]=(0,b.useState)(!1),s=(0,t.jsxs)("div",{className:"max-w-[280px]",children:[(0,t.jsx)(h.Typography.Title,{level:5,className:"mt-0! mb-2!",children:"LiteLLM Agent Platform"}),(0,t.jsx)(h.Typography.Paragraph,{type:"secondary",className:"mb-3! text-sm leading-snug",children:"Open-source agent infra — sandboxes, durable sessions, and workers on AWS Fargate."}),(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,t.jsx)(c.Button,{type:"primary",size:"small",href:"https://github.com/BerriAI/litellm-agent-platform",target:"_blank",rel:"noopener noreferrer",children:"GitHub"}),e?(0,t.jsx)(c.Button,{type:"link",size:"small",className:"px-1!",onClick:()=>{(0,C.setLocalStorageItem)(L,"true"),(0,C.emitLocalStorageChange)(L),l(!1)},children:"Mark as read"}):null]})]});return(0,t.jsx)(P.Popover,{content:s,trigger:"click",open:a,onOpenChange:l,placement:"bottomRight",children:(0,t.jsx)(c.Button,{type:"text",className:"flex! h-9! w-9! items-center justify-center rounded-md! text-gray-600 transition-colors hover:bg-gray-100! hover:text-gray-900!","aria-label":"Notifications",children:(0,t.jsx)(A.Badge,{dot:e,color:"#1677ff",size:"small",offset:[8,2],children:(0,t.jsx)(I,{className:"text-base","aria-hidden":!0})})})})}],895335)},641141,e=>{"use strict";var t=e.i(843476),a=e.i(135214),l=e.i(731565),s=e.i(912089),r=e.i(636772),i=e.i(371401),n=e.i(115571),o=e.i(222038),c=e.i(100486),d=e.i(755151);e.i(247167);var m=e.i(931067),h=e.i(271645);let u={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M868 732h-70.3c-4.8 0-9.3 2.1-12.3 5.8-7 8.5-14.5 16.7-22.4 24.5a353.84 353.84 0 01-112.7 75.9A352.8 352.8 0 01512.4 866c-47.9 0-94.3-9.4-137.9-27.8a353.84 353.84 0 01-112.7-75.9 353.28 353.28 0 01-76-112.5C167.3 606.2 158 559.9 158 512s9.4-94.2 27.8-137.8c17.8-42.1 43.4-80 76-112.5s70.5-58.1 112.7-75.9c43.6-18.4 90-27.8 137.9-27.8 47.9 0 94.3 9.3 137.9 27.8 42.2 17.8 80.1 43.4 112.7 75.9 7.9 7.9 15.3 16.1 22.4 24.5 3 3.7 7.6 5.8 12.3 5.8H868c6.3 0 10.2-7 6.7-12.3C798 160.5 663.8 81.6 511.3 82 271.7 82.6 79.6 277.1 82 516.4 84.4 751.9 276.2 942 512.4 942c152.1 0 285.7-78.8 362.3-197.7 3.4-5.3-.4-12.3-6.7-12.3zm88.9-226.3L815 393.7c-5.3-4.2-13-.4-13 6.3v76H488c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h314v76c0 6.7 7.8 10.5 13 6.3l141.9-112a8 8 0 000-12.6z"}}]},name:"logout",theme:"outlined"};var g=e.i(9583),x=h.forwardRef(function(e,t){return h.createElement(g.default,(0,m.default)({},e,{ref:t,icon:u}))});let p={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32zm-40 110.8V792H136V270.8l-27.6-21.5 39.3-50.5 42.8 33.3h643.1l42.8-33.3 39.3 50.5-27.7 21.5zM833.6 232L512 482 190.4 232l-42.8-33.3-39.3 50.5 27.6 21.5 341.6 265.6a55.99 55.99 0 0068.7 0L888 270.8l27.6-21.5-39.3-50.5-42.7 33.2z"}}]},name:"mail",theme:"outlined"};var f=h.forwardRef(function(e,t){return h.createElement(g.default,(0,m.default)({},e,{ref:t,icon:p}))}),b=e.i(602073),y=e.i(771674),j=e.i(464571),v=e.i(312361),w=e.i(326373),k=e.i(770914),S=e.i(790848),N=e.i(262218),C=e.i(592968),L=e.i(898586),B=e.i(344523),z=e.i(799676),_=e.i(115504);let{Text:I}=L.Typography;e.s(["default",0,({onLogout:e,variant:m="navbar",collapsed:u=!1})=>{let{userId:g,userEmail:p,userRole:L,premiumUser:A}=(0,a.default)(),P=(0,r.useDisableShowPrompts)(),T=(0,i.useDisableUsageIndicator)(),U=(0,l.useDisableBlogPosts)(),M=(0,s.useDisableBouncingIcon)(),[D,O]=(0,h.useState)(!1);(0,h.useEffect)(()=>{O("true"===(0,n.getLocalStorageItem)("disableShowNewBadge"))},[]);let H=[{key:"logout",label:(0,t.jsxs)(k.Space,{children:[(0,t.jsx)(x,{}),"Logout"]}),onClick:e}],E=p||g||"user",R=function(e,t){let a=e?.split("@")[0]?.trim();if(a){let e=a.replace(/[^a-zA-Z0-9]+/g," ").trim().split(/\s+/).filter(Boolean);if(e.length>=2)return`${e[0].charAt(0)}${e[1].charAt(0)}`.toUpperCase();if(1===e.length){let t=e[0];return t.length>=2?t.slice(0,2).toUpperCase():`${t.charAt(0)}`.toUpperCase()}}return t&&t.length>=2?t.slice(0,2).toUpperCase():t&&1===t.length?`${t.toUpperCase()}•`:"?"}(p,g),$=function(e){let t=0;for(let a=0;a(0,t.jsxs)("div",{className:"rounded-lg bg-white shadow-lg","data-testid":"user-dropdown-panel",children:[(0,t.jsxs)(k.Space,{direction:"vertical",size:"small",style:{width:"100%",padding:"12px"},children:[(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsxs)(k.Space,{children:[(0,t.jsx)(f,{}),(0,t.jsx)(I,{type:"secondary",children:p||"-"})]}),A?(0,t.jsx)(N.Tag,{icon:(0,t.jsx)(c.CrownOutlined,{}),color:"gold",children:"Premium"}):(0,t.jsx)(C.Tooltip,{title:"Upgrade to Premium for advanced features",placement:"left",children:(0,t.jsx)(N.Tag,{icon:(0,t.jsx)(c.CrownOutlined,{}),children:"Standard"})})]}),(0,t.jsx)(v.Divider,{style:{margin:"8px 0"}}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsxs)(k.Space,{children:[(0,t.jsx)(y.UserOutlined,{}),(0,t.jsx)(I,{type:"secondary",children:"User ID"})]}),(0,t.jsx)(I,{copyable:!0,ellipsis:!0,style:{maxWidth:"150px"},title:g||"-",children:g||"-"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsxs)(k.Space,{children:[(0,t.jsx)(b.SafetyOutlined,{}),(0,t.jsx)(I,{type:"secondary",children:"Role"})]}),(0,t.jsx)(I,{children:L})]}),(0,t.jsx)(v.Divider,{style:{margin:"8px 0"}}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide New Feature Indicators"}),(0,t.jsx)(S.Switch,{size:"small",checked:D,onChange:e=>{O(e),e?(0,n.setLocalStorageItem)("disableShowNewBadge","true"):(0,n.removeLocalStorageItem)("disableShowNewBadge"),(0,n.emitLocalStorageChange)("disableShowNewBadge")},"aria-label":"Toggle hide new feature indicators"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide All Prompts"}),(0,t.jsx)(S.Switch,{size:"small",checked:P,onChange:e=>{e?(0,n.setLocalStorageItem)("disableShowPrompts","true"):(0,n.removeLocalStorageItem)("disableShowPrompts"),(0,n.emitLocalStorageChange)("disableShowPrompts")},"aria-label":"Toggle hide all prompts"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide Usage Indicator"}),(0,t.jsx)(S.Switch,{size:"small",checked:T,onChange:e=>{e?(0,n.setLocalStorageItem)("disableUsageIndicator","true"):(0,n.removeLocalStorageItem)("disableUsageIndicator"),(0,n.emitLocalStorageChange)("disableUsageIndicator")},"aria-label":"Toggle hide usage indicator"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide Blog Posts"}),(0,t.jsx)(S.Switch,{size:"small",checked:U,onChange:e=>{e?(0,n.setLocalStorageItem)("disableBlogPosts","true"):(0,n.removeLocalStorageItem)("disableBlogPosts"),(0,n.emitLocalStorageChange)("disableBlogPosts")},"aria-label":"Toggle hide blog posts"})]}),(0,t.jsxs)(k.Space,{style:{width:"100%",justifyContent:"space-between"},children:[(0,t.jsx)(I,{type:"secondary",children:"Hide Bouncing Icon"}),(0,t.jsx)(S.Switch,{size:"small",checked:M,onChange:e=>{e?(0,n.setLocalStorageItem)("disableBouncingIcon","true"):(0,n.removeLocalStorageItem)("disableBouncingIcon"),(0,n.emitLocalStorageChange)("disableBouncingIcon")},"aria-label":"Toggle hide bouncing icon"})]})]}),(0,t.jsx)(v.Divider,{style:{margin:0}}),h.default.cloneElement(e,{style:{boxShadow:"none"}})]}),children:"sidebar"===m?(0,t.jsxs)("button",{type:"button",className:(0,_.cn)("flex w-full items-center rounded-lg border border-transparent transition-colors hover:bg-sidebar-accent",u?"justify-center px-0 py-1":"gap-2.5 px-2 py-1.5 text-left"),"aria-label":`Account menu — ${L??"Unknown role"} — signed in as ${p||g||"unknown"}`,"aria-haspopup":"menu",title:u?V:void 0,children:[(0,t.jsx)(z.Avatar,{className:"size-[30px] shadow-inner ring-1 ring-black/5","aria-hidden":!0,children:(0,t.jsx)(z.AvatarFallback,{className:"font-semibold text-white",style:{backgroundColor:`hsl(${$} 46% 38%)`},children:R})}),!u&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("span",{className:"min-w-0 flex-1 leading-tight",children:[(0,t.jsx)("span",{className:"block truncate text-[13px] font-medium text-sidebar-foreground",children:V}),L&&(0,t.jsx)("span",{className:"block truncate text-[11px] text-muted-foreground",children:L})]}),(0,t.jsx)(B.ChevronsUpDown,{size:16,strokeWidth:1.75,className:"shrink-0 text-muted-foreground","aria-hidden":!0})]})]}):(0,t.jsxs)(j.Button,{type:"text",className:"flex! max-w-[min(200px,34vw)] items-center gap-2 rounded-md! py-0.5! pl-1! pr-2! transition-colors hover:bg-gray-100!","aria-label":`Account menu — ${L??"Unknown role"} — signed in as ${p||g||"unknown"}`,"aria-haspopup":"menu",children:[(0,t.jsx)(z.Avatar,{className:"shadow-inner ring-1 ring-black/5","aria-hidden":!0,children:(0,t.jsx)(z.AvatarFallback,{className:"font-semibold text-white",style:{backgroundColor:`hsl(${$} 46% 38%)`},children:R})}),(0,t.jsx)("span",{className:"hidden min-w-0 truncate text-left text-sm font-medium leading-none text-gray-900 md:inline",children:V}),(0,t.jsx)(d.DownOutlined,{className:"hidden shrink-0 text-[10px] text-gray-400 md:inline","aria-hidden":!0})]})})}],641141)},853295,658140,383862,e=>{"use strict";var t=e.i(843476),a=e.i(618566),l=e.i(326373),s=e.i(477189),r=e.i(492030),i=e.i(344523),n=e.i(271645),o=e.i(431703),c=e.i(602869);let d=(0,n.createContext)({mode:"ai-gateway",setMode:()=>{},plugins:[],activePlugin:null}),m="litellm_plugin_mode",h=(0,o.createApiClient)({getBaseUrl:()=>(0,c.getProxyBaseUrl)()??""});function u(){return localStorage.getItem(m)??"ai-gateway"}function g(){return(0,n.useContext)(d)}e.s(["PluginModeProvider",0,function({children:e,accessToken:a}){let[l,s]=(0,n.useState)(u),[r,i]=(0,n.useState)([]),[o,c]=(0,n.useState)(!1);(0,n.useEffect)(()=>{a&&h.get("/api/plugins",{accessToken:a}).then(e=>{i(Array.isArray(e)?e:[])}).catch(()=>{}).finally(()=>c(!0))},[a]);let g="ai-gateway"!==l&&o&&!r.some(e=>e.name===l)?"ai-gateway":l,x=r.find(e=>e.name===g)??null;return(0,t.jsx)(d.Provider,{value:{mode:g,setMode:e=>{s(e),localStorage.setItem(m,e)},plugins:r,activePlugin:x},children:e})},"usePluginMode",0,g],658140);var x=e.i(292639),p=e.i(571353);let f="chat";e.s(["default",0,function(){let{mode:e,setMode:n,plugins:o}=g(),{data:c}=(0,x.useUISettings)(),d=(0,a.usePathname)(),m=!!c?.values?.enable_chat_ui,h=(0,p.migratedHref)(f),u=(d??"").replace(/\/+$/,""),b=m&&(u===h||u.startsWith(`${h}/`)),y=b?"Chat":o.find(t=>t.name===e)?.display_name??"AI Gateway",j=[{key:"ai-gateway",label:"AI Gateway"},...o.map(e=>({key:e.name,label:e.display_name}))],v=m?{key:f,label:(0,t.jsxs)("div",{className:"flex items-center justify-between gap-6 py-0.5",children:[(0,t.jsx)("span",{className:"font-medium",children:"Chat"}),b&&(0,t.jsx)(r.CheckOutlined,{className:"text-blue-600"})]})}:{key:f,disabled:!0,label:(0,t.jsxs)("div",{className:"flex max-w-[220px] flex-col py-0.5",children:[(0,t.jsx)("span",{className:"font-medium",children:"Chat"}),(0,t.jsx)("span",{className:"whitespace-normal text-xs leading-snug text-muted-foreground",children:"Admins can enable in Settings"})]})},w=[...j.map(a=>({key:a.key,label:(0,t.jsxs)("div",{className:"flex items-center justify-between gap-6 py-0.5",children:[(0,t.jsx)("span",{className:"font-medium",children:a.label}),!b&&a.key===e&&(0,t.jsx)(r.CheckOutlined,{className:"text-blue-600"})]})})),v];return(0,t.jsx)(l.Dropdown,{menu:{items:w,onClick:({key:e})=>{e===f?window.location.assign((0,p.migratedHref)(f)):(n(e),b&&window.location.assign((0,p.migratedHref)("")))},selectedKeys:[b?f:e]},trigger:["click"],children:(0,t.jsxs)("button",{type:"button",className:"flex h-8 max-w-[220px] items-center gap-1.5 rounded-md border border-border bg-background pl-1.5 pr-2 text-sm font-medium text-foreground transition-colors hover:bg-accent",children:[(0,t.jsx)("span",{className:"flex size-5 flex-none items-center justify-center rounded bg-muted text-muted-foreground",children:(0,t.jsx)(s.AppstoreOutlined,{className:"text-[13px]"})}),(0,t.jsx)("span",{className:"truncate",children:y}),(0,t.jsx)(i.ChevronsUpDown,{className:"size-3.5 flex-none text-muted-foreground"})]})})}],853295);var b=e.i(199133),y=e.i(295320),j=e.i(283713);e.s(["default",0,({onWorkerSwitch:e})=>{let{isControlPlane:a,selectedWorker:l,workers:s}=(0,j.useWorker)();return a&&l?(0,t.jsx)(b.Select,{showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),value:l.worker_id,style:{minWidth:180},suffixIcon:(0,t.jsx)(y.CloudServerOutlined,{}),options:s.map(e=>({label:e.name,value:e.worker_id,disabled:e.worker_id===l.worker_id})),onChange:t=>{e(t)}}):null}],383862)},402874,e=>{"use strict";var t=e.i(843476),a=e.i(143488),l=e.i(912089),s=e.i(636772),r=e.i(283713),i=e.i(602869),n=e.i(275144),o=e.i(268004),c=e.i(321836),d=e.i(592392),m=e.i(755151),h=e.i(44121),u=e.i(186515),g=e.i(262218),x=e.i(522016),p=e.i(251773),f=e.i(771243),b=e.i(276701),y=e.i(895335),j=e.i(641141),v=e.i(853295),w=e.i(383862);e.s(["default",0,({accessToken:e,isPublicPage:k=!1,sidebarCollapsed:S=!1,onToggleSidebar:N})=>{let C=(0,i.getProxyBaseUrl)(),L=(0,d.default)(e),{logoUrl:B}=(0,n.useTheme)(),{data:z}=(0,a.useHealthReadinessDetails)(e),_=z?.litellm_version,I=(0,l.useDisableBouncingIcon)(),A=(0,s.useDisableShowPrompts)(),{isControlPlane:P,selectedWorker:T}=(0,r.useWorker)(),U=P&&null!==T,M=B||`${C}/get_image`;return(0,t.jsx)("nav",{className:"sticky top-0 z-10 border-b border-gray-200 bg-white",children:(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)("div",{className:"flex h-14 items-center px-4",children:[(0,t.jsxs)("div",{className:"flex shrink-0 items-center",children:[N&&(0,t.jsx)("button",{onClick:N,className:"mr-2 flex h-9 w-9 items-center justify-center rounded-md text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900",title:S?"Expand sidebar":"Collapse sidebar",children:(0,t.jsx)("span",{className:"text-lg",children:S?(0,t.jsx)(u.MenuUnfoldOutlined,{}):(0,t.jsx)(h.MenuFoldOutlined,{})})}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(x.default,{href:C||"/",className:"flex items-center",children:(0,t.jsx)("div",{className:"relative",children:(0,t.jsx)("div",{className:"flex h-10 max-w-48 items-center justify-center overflow-hidden",children:(0,t.jsx)("img",{src:M,alt:"LiteLLM Brand",className:"h-auto max-h-full w-auto max-w-full object-contain"})})})}),_&&(0,t.jsxs)("div",{className:"relative",children:[!I&&(0,t.jsx)("span",{className:"absolute -left-2 -top-1 animate-bounce text-lg",style:{animationDuration:"2s"},title:"Thanks for using LiteLLM!",children:"🌑"}),(0,t.jsx)(g.Tag,{className:"relative z-10 cursor-pointer text-xs font-medium",children:(0,t.jsxs)("a",{href:"https://docs.litellm.ai/release_notes",target:"_blank",rel:"noopener noreferrer",className:"shrink-0",children:["v",_]})})]})]})]}),!k&&(0,t.jsx)("div",{className:"ml-4 flex shrink-0 items-center border-l border-gray-200 pl-4",children:(0,t.jsx)(v.default,{})}),(0,t.jsxs)("div",{className:"ml-auto flex min-w-0 flex-1 items-center justify-end gap-4",children:[U&&(0,t.jsx)("div",{className:"flex shrink-0 items-center",children:(0,t.jsx)(w.default,{onWorkerSwitch:e=>{(0,o.clearTokenCookies)(),(0,c.clearStoredReturnUrl)(),localStorage.removeItem("litellm_selected_worker_id"),localStorage.removeItem("litellm_worker_url"),window.location.href=`/ui/login?worker=${encodeURIComponent(e)}`}})}),(0,t.jsxs)("nav",{"aria-label":"Product documentation",className:`flex min-w-0 items-center gap-2 ${U?"border-l border-gray-200 pl-4":""}`,children:[(0,t.jsxs)("a",{href:"https://docs.litellm.ai/docs/",target:"_blank",rel:"noopener noreferrer",className:b.NAV_PRODUCT_LINK_CLASS,children:["Docs",(0,t.jsx)(m.DownOutlined,{className:"pointer-events-none text-[10px] opacity-0","aria-hidden":!0})]}),(0,t.jsx)(p.BlogDropdown,{})]}),!A&&(0,t.jsx)("div",{className:"flex shrink-0 items-center border-l border-gray-200 pl-4",children:(0,t.jsx)(f.CommunityEngagementButtons,{})}),!k&&(0,t.jsx)("div",{className:"flex shrink-0 items-center border-l border-gray-200 pl-4",children:(0,t.jsxs)("div",{className:"flex items-center gap-0.5 rounded-lg bg-gray-50 px-1 py-0 transition-colors hover:bg-gray-100",children:[(0,t.jsx)(y.NotificationsBell,{}),(0,t.jsx)("span",{className:"mx-0.5 h-6 w-px shrink-0 bg-gray-200","aria-hidden":!0}),(0,t.jsx)(j.default,{onLogout:()=>{(0,o.clearTokenCookies)(),localStorage.removeItem("litellm_selected_worker_id"),localStorage.removeItem("litellm_worker_url"),window.location.href=L.PROXY_LOGOUT_URL||""}})]})})]})]})})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/01y._o853f7le.js b/litellm/proxy/_experimental/out/_next/static/chunks/01y._o853f7le.js new file mode 100644 index 00000000000..cf74c1c9c1f --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/01y._o853f7le.js @@ -0,0 +1,4 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,783222,433336,80758,402155,368578,544508,746725,835696,941444,914189,394487,e=>{"use strict";let t;e.i(247167);let r=e=>e?.ownerDocument??document,n=e=>e&&"window"in e&&e.window===e?e:r(e).defaultView||window;function o(e,t){return!!t&&!!e&&e.contains(t)}function s(e){return e.target}let a=null;"u">typeof Element&&Element.prototype;let i=["input:not([disabled]):not([type=hidden])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","a[href]","area[href]","summary","iframe","object","embed","audio[controls]","video[controls]",'[contenteditable]:not([contenteditable^="false"])',"permission"];i.join(":not([hidden]),"),i.push('[tabindex]:not([tabindex="-1"]):not([disabled])'),i.join(':not([hidden]):not([tabindex="-1"]),');var l=e.i(271645);let u="u">typeof document?l.default.useLayoutEffect:()=>{};function c(e){return e.nativeEvent=e,e.isDefaultPrevented=()=>e.defaultPrevented,e.isPropagationStopped=()=>e.cancelBubble,e.persist=()=>{},e}function d(e){let t=(0,l.useRef)({isFocused:!1,observer:null});return u(()=>{let e=t.current;return()=>{e.observer&&(e.observer.disconnect(),e.observer=null)}},[]),(0,l.useCallback)(r=>{let n=s(r);(n instanceof HTMLButtonElement||n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement)&&(t.current.isFocused=!0,n.addEventListener("focusout",r=>{if(t.current.isFocused=!1,n.disabled){let t=c(r);e?.(t)}t.current.observer&&(t.current.observer.disconnect(),t.current.observer=null)},{once:!0}),t.current.observer=new MutationObserver(()=>{if(t.current.isFocused&&n.disabled){t.current.observer?.disconnect();let e=n===((e=document)=>e.activeElement)()?null:((e=document)=>e.activeElement)();n.dispatchEvent(new FocusEvent("blur",{relatedTarget:e})),n.dispatchEvent(new FocusEvent("focusout",{bubbles:!0,relatedTarget:e}))}}),t.current.observer.observe(n,{attributes:!0,attributeFilter:["disabled"]}))},[e])}function f(e){if("u"e.test(t.brand))||e.test(window.navigator.userAgent)}function p(e){return"u">typeof window&&null!=window.navigator&&e.test(window.navigator.userAgentData?.platform||window.navigator.platform)}function m(e){let t=null;return()=>(null==t&&(t=e()),t)}let b=m(function(){return p(/^Mac/i)}),v=m(function(){return p(/^iPhone/i)}),h=m(function(){return p(/^iPad/i)||b()&&navigator.maxTouchPoints>1}),g=m(function(){return v()||h()});m(function(){return b()||g()});let y=m(function(){return f(/AppleWebKit/i)&&!E()}),E=m(function(){return f(/Chrome/i)}),T=m(function(){return f(/Android/i)}),w=m(function(){return f(/Firefox/i)});function x(e,t,r=!0){let{metaKey:n,ctrlKey:o,altKey:s,shiftKey:i}=t;w()&&window.event?.type?.startsWith("key")&&"_blank"===e.target&&(b()?n=!0:o=!0);let l=y()&&b()&&!h()&&1?new KeyboardEvent("keydown",{keyIdentifier:"Enter",metaKey:n,ctrlKey:o,altKey:s,shiftKey:i}):new MouseEvent("click",{metaKey:n,ctrlKey:o,altKey:s,shiftKey:i,detail:1,bubbles:!0,cancelable:!0});x.isOpening=r;if(function(){if(null==a){a=!1;try{document.createElement("div").focus({get preventScroll(){return a=!0,!0}})}catch{}}return a}())e.focus({preventScroll:!0});else{let t=function(e){let t=e.parentNode,r=[],n=document.scrollingElement||document.documentElement;for(;t instanceof HTMLElement&&t!==n;)(t.offsetHeighttypeof window&&window.document&&window.document.createElement,new WeakMap;l.default.useId;let P=null,k=new Set,L=new Map,N=!1,C=!1,I={Tab:!0,Escape:!0};function S(e,t){for(let r of k)r(e,t)}function A(e){N=!0,x.isOpening||e.metaKey||!b()&&e.altKey||e.ctrlKey||"Control"===e.key||"Shift"===e.key||"Meta"===e.key||(P="keyboard",S("keyboard",e))}function M(e){P="pointer","pointerType"in e&&e.pointerType,("mousedown"===e.type||"pointerdown"===e.type)&&(N=!0,S("pointer",e))}function R(e){x.isOpening||(""!==e.pointerType||!e.isTrusted)&&(T()&&e.pointerType?"click"!==e.type||1!==e.buttons:0!==e.detail||e.pointerType)||(N=!0,P="virtual")}function O(e){let t=n(s(e)),o=r(s(e));s(e)!==t&&s(e)!==o&&e.isTrusted&&(N||C||(P="virtual",S("virtual",e)),N=!1,C=!1)}function D(){N=!1,C=!0}function H(e){if("u"typeof PointerEvent&&(o.addEventListener("pointerdown",M,!0),o.addEventListener("pointermove",M,!0),o.addEventListener("pointerup",M,!0)),t.addEventListener("beforeunload",()=>{j(e)},{once:!0}),L.set(t,{focus:s})}let j=(e,t)=>{let o=n(e),s=r(e);t&&s.removeEventListener("DOMContentLoaded",t),L.has(o)&&(o.HTMLElement.prototype.focus=L.get(o).focus,s.removeEventListener("keydown",A,!0),s.removeEventListener("keyup",A,!0),s.removeEventListener("click",R,!0),o.removeEventListener("focus",O,!0),o.removeEventListener("blur",D,!1),"u">typeof PointerEvent&&(s.removeEventListener("pointerdown",M,!0),s.removeEventListener("pointermove",M,!0),s.removeEventListener("pointerup",M,!0)),L.delete(o))};function K(){return"pointer"!==P}"u">typeof document&&("loading"!==(t=r(void 0)).readyState?H(void 0):t.addEventListener("DOMContentLoaded",()=>{H(void 0)}));let W=new Set(["checkbox","radio","range","color","file","image","button","submit","reset"]);function B(){let e=(0,l.useRef)(new Map),t=(0,l.useCallback)((t,r,n,o)=>{let s=o?.once?(...t)=>{e.current.delete(n),n(...t)}:n;e.current.set(n,{type:r,eventTarget:t,fn:s,options:o}),t.addEventListener(r,s,o)},[]),r=(0,l.useCallback)((t,r,n,o)=>{let s=e.current.get(n)?.fn||n;t.removeEventListener(r,s,o),e.current.delete(n)},[]),n=(0,l.useCallback)(()=>{e.current.forEach((e,t)=>{r(e.eventTarget,e.type,t,e.options)})},[r]);return(0,l.useEffect)(()=>n,[n]),{addGlobalListener:t,removeGlobalListener:r,removeAllGlobalListeners:n}}e.s(["useFocusRing",0,function(e={}){var t;let{autoFocus:a=!1,isTextInput:i,within:u}=e,f=(0,l.useRef)({isFocused:!1,isFocusVisible:a||K()}),[p,m]=(0,l.useState)(!1),[b,v]=(0,l.useState)(()=>f.current.isFocused&&f.current.isFocusVisible),h=(0,l.useCallback)(()=>v(f.current.isFocused&&f.current.isFocusVisible),[]),g=(0,l.useCallback)(e=>{f.current.isFocused=e,f.current.isFocusVisible=K(),m(e),h()},[h]);t={enabled:p,isTextInput:i},H(),(0,l.useEffect)(()=>{if(t?.enabled===!1)return;let e=(e,o)=>{var a;let i,l,u,c,d,p,m,b;a=!!t?.isTextInput,l=r(i=o?s(o):void 0),c=void 0!==(u=n(i))?u.HTMLInputElement:HTMLInputElement,d=void 0!==u?u.HTMLTextAreaElement:HTMLTextAreaElement,p=void 0!==u?u.HTMLElement:HTMLElement,m=void 0!==u?u.KeyboardEvent:KeyboardEvent,b=((e=document)=>e.activeElement)(l),(a=a||b instanceof c&&!W.has(b.type)||b instanceof d||b instanceof p&&b.isContentEditable)&&"keyboard"===e&&o instanceof m&&!I[o.key]||(e=>{f.current.isFocusVisible=e,h()})(K())};return k.add(e),()=>{k.delete(e)}},[i,p]);let{focusProps:y}=function(e){let{isDisabled:t,onFocus:n,onBlur:o,onFocusChange:a}=e,i=(0,l.useCallback)(e=>{if(s(e)===e.currentTarget)return o&&o(e),a&&a(!1),!0},[o,a]),u=d(i),c=(0,l.useCallback)(e=>{let t=s(e),o=r(t),i=o?((e=document)=>e.activeElement)(o):((e=document)=>e.activeElement)();t===e.currentTarget&&t===i&&(n&&n(e),a&&a(!0),u(e))},[a,n,u]);return{focusProps:{onFocus:!t&&(n||a||o)?c:void 0,onBlur:!t&&(o||a)?i:void 0}}}({isDisabled:u,onFocusChange:g}),{focusWithinProps:E}=function(e){let{isDisabled:t,onBlurWithin:n,onFocusWithin:a,onFocusWithinChange:i}=e,u=(0,l.useRef)({isFocusWithin:!1}),{addGlobalListener:f,removeAllGlobalListeners:p}=B(),m=(0,l.useCallback)(e=>{o(e.currentTarget,s(e))&&u.current.isFocusWithin&&!o(e.currentTarget,e.relatedTarget)&&(u.current.isFocusWithin=!1,p(),n&&n(e),i&&i(!1))},[n,i,u,p]),b=d(m),v=(0,l.useCallback)(e=>{if(!o(e.currentTarget,s(e)))return;let t=s(e),n=r(t),l=((e=document)=>e.activeElement)(n);if(!u.current.isFocusWithin&&l===t){a&&a(e),i&&i(!0),u.current.isFocusWithin=!0,b(e);let t=e.currentTarget;f(n,"focus",e=>{let r=s(e);if(u.current.isFocusWithin&&!o(t,r)){let e=new n.defaultView.FocusEvent("blur",{relatedTarget:r});Object.defineProperty(e,"target",{value:t}),Object.defineProperty(e,"currentTarget",{value:t}),m(c(e))}},{capture:!0})}},[a,i,b,f,m]);return t?{focusWithinProps:{onFocus:void 0,onBlur:void 0}}:{focusWithinProps:{onFocus:v,onBlur:m}}}({isDisabled:!u,onFocusWithinChange:g});return{isFocused:p,isFocusVisible:b,focusProps:u?E:y}}],783222);let V=!1,_=0;function G(e){"touch"===e.pointerType&&(V=!0,setTimeout(()=>{V=!1},500))}function U(){let e=r(null);if(void 0!==e)return 0===_&&"u">typeof PointerEvent&&e.addEventListener("pointerup",G),_++,()=>{!(--_>0)&&"u">typeof PointerEvent&&e.removeEventListener("pointerup",G)}}e.s(["useHover",0,function(e){let{onHoverStart:t,onHoverChange:n,onHoverEnd:a,isDisabled:i}=e,[u,c]=(0,l.useState)(!1),d=(0,l.useRef)({isHovered:!1,ignoreEmulatedMouseEvents:!1,pointerType:"",target:null}).current;(0,l.useEffect)(U,[]);let{addGlobalListener:f,removeAllGlobalListeners:p}=B(),{hoverProps:m,triggerHoverEnd:b}=(0,l.useMemo)(()=>{let e=(e,t)=>{let r=d.target;d.pointerType="",d.target=null,"touch"!==t&&d.isHovered&&r&&(d.isHovered=!1,p(),a&&a({type:"hoverend",target:r,pointerType:t}),n&&n(!1),c(!1))},l={};return"u">typeof PointerEvent&&(l.onPointerEnter=a=>{V&&"mouse"===a.pointerType||((a,l)=>{if(d.pointerType=l,i||"touch"===l||d.isHovered||!o(a.currentTarget,s(a)))return;d.isHovered=!0;let u=a.currentTarget;d.target=u,f(r(s(a)),"pointerover",t=>{d.isHovered&&d.target&&!o(d.target,s(t))&&e(t,t.pointerType)},{capture:!0}),t&&t({type:"hoverstart",target:u,pointerType:l}),n&&n(!0),c(!0)})(a,a.pointerType)},l.onPointerLeave=t=>{!i&&o(t.currentTarget,s(t))&&e(t,t.pointerType)}),{hoverProps:l,triggerHoverEnd:e}},[t,n,a,i,d,f,p]);return(0,l.useEffect)(()=>{i&&b({currentTarget:d.target},d.pointerType)},[i]),{hoverProps:m,isHovered:u}}],433336);var $=Object.defineProperty,q=(e,t,r)=>{let n;return(n="symbol"!=typeof t?t+"":t)in e?$(e,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[n]=r,r};let X=new class{constructor(){q(this,"current",this.detect()),q(this,"handoffState","pending"),q(this,"currentId",0)}set(e){this.current!==e&&(this.handoffState="pending",this.currentId=0,this.current=e)}reset(){this.set(this.detect())}nextId(){return++this.currentId}get isServer(){return"server"===this.current}get isClient(){return"client"===this.current}detect(){return"u"setTimeout(()=>{throw e}))}function Z(){let e=[],t={addEventListener:(e,r,n,o)=>(e.addEventListener(r,n,o),t.add(()=>e.removeEventListener(r,n,o))),requestAnimationFrame(...e){let r=requestAnimationFrame(...e);return t.add(()=>cancelAnimationFrame(r))},nextFrame:(...e)=>t.requestAnimationFrame(()=>t.requestAnimationFrame(...e)),setTimeout(...e){let r=setTimeout(...e);return t.add(()=>clearTimeout(r))},microTask(...e){let r={current:!0};return z(()=>{r.current&&e[0]()}),t.add(()=>{r.current=!1})},style(e,t,r){let n=e.style.getPropertyValue(t);return Object.assign(e.style,{[t]:r}),this.add(()=>{Object.assign(e.style,{[t]:n})})},group(e){let t=Z();return e(t),this.add(()=>t.dispose())},add:t=>(e.includes(t)||e.push(t),()=>{let r=e.indexOf(t);if(r>=0)for(let t of e.splice(r,1))t()}),dispose(){for(let t of e.splice(0))t()}};return t}function J(){let[e]=(0,l.useState)(Z);return(0,l.useEffect)(()=>()=>e.dispose(),[e]),e}e.s(["env",0,X],80758),e.s(["getOwnerDocument",0,Y],402155),e.s(["microTask",0,z],368578),e.s(["disposables",0,Z],544508),e.s(["useDisposables",0,J],746725);let Q=(e,t)=>{X.isServer?(0,l.useEffect)(e,t):(0,l.useLayoutEffect)(e,t)};function ee(e){let t=(0,l.useRef)(e);return Q(()=>{t.current=e},[e]),t}e.s(["useIsoMorphicEffect",0,Q],835696),e.s(["useLatestValue",0,ee],941444);let et=function(e){let t=ee(e);return l.default.useCallback((...e)=>t.current(...e),[t])};e.s(["useEvent",0,et],914189),e.s(["useActivePress",0,function({disabled:e=!1}={}){let t=(0,l.useRef)(null),[r,n]=(0,l.useState)(!1),o=J(),s=et(()=>{t.current=null,n(!1),o.dispose()}),a=et(e=>{if(o.dispose(),null===t.current){t.current=e.currentTarget,n(!0);{let r=Y(e.currentTarget);o.addEventListener(r,"pointerup",s,!1),o.addEventListener(r,"pointermove",e=>{if(t.current){var r,o;let s,a;n((s=e.width/2,a=e.height/2,r={top:e.clientY-a,right:e.clientX+s,bottom:e.clientY+a,left:e.clientX-s},o=t.current.getBoundingClientRect(),!(!r||!o||r.righto.right||r.bottomo.bottom)))}},!1),o.addEventListener(r,"pointercancel",s,!1)}}});return{pressed:r,pressProps:e?{}:{onPointerDown:a,onPointerUp:s,onClick:s}}}],394487)},397701,e=>{"use strict";e.s(["match",0,function e(t,r,...n){if(t in r){let e=r[t];return"function"==typeof e?e(...n):e}let o=Error(`Tried to handle "${t}" but there is no handler defined. Only defined handlers are: ${Object.keys(r).map(e=>`"${e}"`).join(", ")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(o,e),o}])},652265,e=>{"use strict";let t,r,n,o,s;e.i(544508);var a=e.i(397701),i=e.i(402155);let l=["[contentEditable=true]","[tabindex]","a[href]","area[href]","button:not([disabled])","iframe","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].map(e=>`${e}:not([tabindex='-1'])`).join(","),u=["[data-autofocus]"].map(e=>`${e}:not([tabindex='-1'])`).join(",");var c=((t=c||{})[t.First=1]="First",t[t.Previous=2]="Previous",t[t.Next=4]="Next",t[t.Last=8]="Last",t[t.WrapAround=16]="WrapAround",t[t.NoScroll=32]="NoScroll",t[t.AutoFocus=64]="AutoFocus",t),d=((r=d||{})[r.Error=0]="Error",r[r.Overflow=1]="Overflow",r[r.Success=2]="Success",r[r.Underflow=3]="Underflow",r),f=((n=f||{})[n.Previous=-1]="Previous",n[n.Next=1]="Next",n);function p(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(l)).sort((e,t)=>Math.sign((e.tabIndex||Number.MAX_SAFE_INTEGER)-(t.tabIndex||Number.MAX_SAFE_INTEGER)))}var m=((o=m||{})[o.Strict=0]="Strict",o[o.Loose=1]="Loose",o),b=((s=b||{})[s.Keyboard=0]="Keyboard",s[s.Mouse=1]="Mouse",s);function v(e,t=e=>e){return e.slice().sort((e,r)=>{let n=t(e),o=t(r);if(null===n||null===o)return 0;let s=n.compareDocumentPosition(o);return s&Node.DOCUMENT_POSITION_FOLLOWING?-1:s&Node.DOCUMENT_POSITION_PRECEDING?1:0})}function h(e,t,{sorted:r=!0,relativeTo:n=null,skipElements:o=[]}={}){var s,a,i;let l=Array.isArray(e)?e.length>0?e[0].ownerDocument:document:e.ownerDocument,c=Array.isArray(e)?r?v(e):e:64&t?function(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(u)).sort((e,t)=>Math.sign((e.tabIndex||Number.MAX_SAFE_INTEGER)-(t.tabIndex||Number.MAX_SAFE_INTEGER)))}(e):p(e);o.length>0&&c.length>1&&(c=c.filter(e=>!o.some(t=>null!=t&&"current"in t?(null==t?void 0:t.current)===e:t===e))),n=null!=n?n:l.activeElement;let d=(()=>{if(5&t)return 1;if(10&t)return -1;throw Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),f=(()=>{if(1&t)return 0;if(2&t)return Math.max(0,c.indexOf(n))-1;if(4&t)return Math.max(0,c.indexOf(n))+1;if(8&t)return c.length-1;throw Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),m=32&t?{preventScroll:!0}:{},b=0,g=c.length,y;do{if(b>=g||b+g<=0)return 0;let e=f+b;if(16&t)e=(e+g)%g;else{if(e<0)return 3;if(e>=g)return 1}null==(y=c[e])||y.focus(m),b+=d}while(y!==l.activeElement)return 6&t&&null!=(i=null==(a=null==(s=y)?void 0:s.matches)?void 0:a.call(s,"textarea,input"))&&i&&y.select(),2}"u">typeof window&&"u">typeof document&&(document.addEventListener("keydown",e=>{e.metaKey||e.altKey||e.ctrlKey||(document.documentElement.dataset.headlessuiFocusVisible="")},!0),document.addEventListener("click",e=>{1===e.detail?delete document.documentElement.dataset.headlessuiFocusVisible:0===e.detail&&(document.documentElement.dataset.headlessuiFocusVisible="")},!0)),e.s(["Focus",0,c,"FocusResult",0,d,"FocusableMode",0,m,"focusFrom",0,function(e,t){return h(p(),t,{relativeTo:e})},"focusIn",0,h,"getFocusableElements",0,p,"isFocusableElement",0,function(e,t=0){var r;return e!==(null==(r=(0,i.getOwnerDocument)(e))?void 0:r.body)&&(0,a.match)(t,{0:()=>e.matches(l),1(){let t=e;for(;null!==t;){if(t.matches(l))return!0;t=t.parentElement}return!1}})},"sortByDomNode",0,v])},144279,294316,e=>{"use strict";var t=e.i(271645);e.s(["useResolveButtonType",0,function(e,r){return(0,t.useMemo)(()=>{var t;if(e.type)return e.type;let n=null!=(t=e.as)?t:"button";if("string"==typeof n&&"button"===n.toLowerCase()||(null==r?void 0:r.tagName)==="BUTTON"&&!r.hasAttribute("type"))return"button"},[e.type,e.as,r])}],144279);var r=e.i(914189);let n=Symbol();e.s(["optionalRef",0,function(e,t=!0){return Object.assign(e,{[n]:t})},"useSyncRefs",0,function(...e){let o=(0,t.useRef)(e);(0,t.useEffect)(()=>{o.current=e},[e]);let s=(0,r.useEvent)(e=>{for(let t of o.current)null!=t&&("function"==typeof t?t(e):t.current=e)});return e.every(e=>null==e||(null==e?void 0:e[n]))?void 0:s}],294316)},732607,e=>{"use strict";e.s(["classNames",0,function(...e){return Array.from(new Set(e.flatMap(e=>"string"==typeof e?e.split(" "):[]))).filter(Boolean).join(" ")}])},700020,e=>{"use strict";let t,r;var n=e.i(271645),o=e.i(732607),s=e.i(397701),a=((t=a||{})[t.None=0]="None",t[t.RenderStrategy=1]="RenderStrategy",t[t.Static=2]="Static",t),i=((r=i||{})[r.Unmount=0]="Unmount",r[r.Hidden=1]="Hidden",r);function l(e,t={},r,s,a){let{as:i=r,children:u,refName:p="ref",...m}=f(e,["unmount","static"]),b=void 0!==e.ref?{[p]:e.ref}:{},v="function"==typeof u?u(t):u;"className"in m&&m.className&&"function"==typeof m.className&&(m.className=m.className(t)),m["aria-labelledby"]&&m["aria-labelledby"]===m.id&&(m["aria-labelledby"]=void 0);let h={};if(t){let e=!1,r=[];for(let[n,o]of Object.entries(t))"boolean"==typeof o&&(e=!0),!0===o&&r.push(n.replace(/([A-Z])/g,e=>`-${e.toLowerCase()}`));if(e)for(let e of(h["data-headlessui-state"]=r.join(" "),r))h[`data-${e}`]=""}if(i===n.Fragment&&(Object.keys(d(m)).length>0||Object.keys(d(h)).length>0))if(!(0,n.isValidElement)(v)||Array.isArray(v)&&v.length>1){if(Object.keys(d(m)).length>0)throw Error(['Passing props on "Fragment"!',"",`The current component <${s} /> is rendering a "Fragment".`,"However we need to passthrough the following props:",Object.keys(d(m)).concat(Object.keys(d(h))).map(e=>` - ${e}`).join(` +`),"","You can apply a few solutions:",['Add an `as="..."` prop, to ensure that we render an actual element instead of a "Fragment".',"Render a single element as the child so that we can forward the props onto that element."].map(e=>` - ${e}`).join(` +`)].join(` +`))}else{var g;let e=v.props,t=null==e?void 0:e.className,r="function"==typeof t?(...e)=>(0,o.classNames)(t(...e),m.className):(0,o.classNames)(t,m.className),s=c(v.props,d(f(m,["ref"])));for(let e in h)e in s&&delete h[e];return(0,n.cloneElement)(v,Object.assign({},s,h,b,{ref:a((g=v,n.default.version.split(".")[0]>="19"?g.props.ref:g.ref),b.ref)},r?{className:r}:{}))}return(0,n.createElement)(i,Object.assign({},f(m,["ref"]),i!==n.Fragment&&b,i!==n.Fragment&&h),v)}function u(...e){return e.every(e=>null==e)?void 0:t=>{for(let r of e)null!=r&&("function"==typeof r?r(t):r.current=t)}}function c(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},r={};for(let n of e)for(let e in n)e.startsWith("on")&&"function"==typeof n[e]?(null!=r[e]||(r[e]=[]),r[e].push(n[e])):t[e]=n[e];if(t.disabled||t["aria-disabled"])for(let e in r)/^(on(?:Click|Pointer|Mouse|Key)(?:Down|Up|Press)?)$/.test(e)&&(r[e]=[e=>{var t;return null==(t=null==e?void 0:e.preventDefault)?void 0:t.call(e)}]);for(let e in r)Object.assign(t,{[e](t,...n){for(let o of r[e]){if((t instanceof Event||(null==t?void 0:t.nativeEvent)instanceof Event)&&t.defaultPrevented)return;o(t,...n)}}});return t}function d(e){let t=Object.assign({},e);for(let e in t)void 0===t[e]&&delete t[e];return t}function f(e,t=[]){let r=Object.assign({},e);for(let e of t)e in r&&delete r[e];return r}e.s(["RenderFeatures",0,a,"RenderStrategy",0,i,"compact",0,d,"forwardRefWithAs",0,function(e){var t;return Object.assign((0,n.forwardRef)(e),{displayName:null!=(t=e.displayName)?t:e.name})},"mergeProps",0,function(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},r={};for(let n of e)for(let e in n)e.startsWith("on")&&"function"==typeof n[e]?(null!=r[e]||(r[e]=[]),r[e].push(n[e])):t[e]=n[e];for(let e in r)Object.assign(t,{[e](...t){for(let n of r[e])null==n||n(...t)}});return t},"useRender",0,function(){let e,t,r=(e=(0,n.useRef)([]),t=(0,n.useCallback)(t=>{for(let r of e.current)null!=r&&("function"==typeof r?r(t):r.current=t)},[]),(...r)=>{if(!r.every(e=>null==e))return e.current=r,t});return(0,n.useCallback)(e=>(function({ourProps:e,theirProps:t,slot:r,defaultTag:n,features:o,visible:a=!0,name:i,mergeRefs:d}){d=null!=d?d:u;let f=c(t,e);if(a)return l(f,r,n,i,d);let p=null!=o?o:0;if(2&p){let{static:e=!1,...t}=f;if(e)return l(t,r,n,i,d)}if(1&p){let{unmount:e=!0,...t}=f;return(0,s.match)(+!e,{0:()=>null,1:()=>l({...t,hidden:!0,style:{display:"none"}},r,n,i,d)})}return l(f,r,n,i,d)})({mergeRefs:r,...e}),[r])}])},2788,e=>{"use strict";let t;var r=e.i(700020),n=((t=n||{})[t.None=1]="None",t[t.Focusable=2]="Focusable",t[t.Hidden=4]="Hidden",t);let o=(0,r.forwardRefWithAs)(function(e,t){var n;let{features:o=1,...s}=e,a={ref:t,"aria-hidden":(2&o)==2||(null!=(n=s["aria-hidden"])?n:void 0),hidden:(4&o)==4||void 0,style:{position:"fixed",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",...(4&o)==4&&(2&o)!=2&&{display:"none"}}};return(0,r.useRender)()({ourProps:a,theirProps:s,slot:{},defaultTag:"span",name:"Hidden"})});e.s(["Hidden",0,o,"HiddenFeatures",0,n])},998348,e=>{"use strict";let t;var r=((t=r||{}).Space=" ",t.Enter="Enter",t.Escape="Escape",t.Backspace="Backspace",t.Delete="Delete",t.ArrowLeft="ArrowLeft",t.ArrowUp="ArrowUp",t.ArrowRight="ArrowRight",t.ArrowDown="ArrowDown",t.Home="Home",t.End="End",t.PageUp="PageUp",t.PageDown="PageDown",t.Tab="Tab",t);e.s(["Keys",0,r])},553521,e=>{"use strict";var t=e.i(271645),r=e.i(835696);e.s(["useIsMounted",0,function(){let e=(0,t.useRef)(!1);return(0,r.useIsoMorphicEffect)(()=>(e.current=!0,()=>{e.current=!1}),[]),e}])},751734,e=>{"use strict";let t=(0,e.i(271645).createContext)(0);e.s(["default",0,t])},144582,e=>{"use strict";let t=(0,e.i(271645).createContext)({selectedValue:void 0,handleValueChange:void 0});e.s(["default",0,t])},404206,e=>{"use strict";var t=e.i(290571),r=e.i(751734),n=e.i(144582),o=e.i(444755),s=e.i(673706),a=e.i(271645);let i=(0,s.makeClassName)("TabPanel"),l=a.default.forwardRef((e,s)=>{let{children:l,className:u}=e,c=(0,t.__rest)(e,["children","className"]),{selectedValue:d}=(0,a.useContext)(n.default),f=d===(0,a.useContext)(r.default);return a.default.createElement("div",Object.assign({ref:s,className:(0,o.tremorTwMerge)(i("root"),"w-full mt-2",f?"":"hidden",u),"aria-selected":f?"true":"false"},c),l)});l.displayName="TabPanel",e.s(["TabPanel",0,l],404206)},640497,e=>{"use strict";var t=e.i(271645),r=e.i(553521),n=e.i(2788);e.s(["FocusSentinel",0,function({onFocus:e}){let[o,s]=(0,t.useState)(!0),a=(0,r.useIsMounted)();return o?t.default.createElement(n.Hidden,{as:"button",type:"button",features:n.HiddenFeatures.Focusable,onFocus:t=>{t.preventDefault();let r,n=50;r=requestAnimationFrame(function t(){if(n--<=0){r&&cancelAnimationFrame(r);return}if(e()){if(cancelAnimationFrame(r),!a.current)return;s(!1);return}r=requestAnimationFrame(t)})}}):null}])},963703,e=>{"use strict";var t=e.i(271645);let r=t.createContext(null);e.s(["StableCollection",0,function({children:e}){let n=t.useRef({groups:new Map,get(e,t){var r;let n=this.groups.get(e);n||(n=new Map,this.groups.set(e,n));let o=null!=(r=n.get(t))?r:0;return n.set(t,o+1),[Array.from(n.keys()).indexOf(t),function(){let e=n.get(t);e>1?n.set(t,e-1):n.delete(t)}]}});return t.createElement(r.Provider,{value:n},e)},"useStableCollectionIndex",0,function(e){let n=t.useContext(r);if(!n)throw Error("You must wrap your component in a ");let o=t.useId(),[s,a]=n.current.get(e,o);return t.useEffect(()=>a,[]),s}])},970554,e=>{"use strict";let t,r,n;var o=e.i(783222),s=e.i(433336),a=e.i(271645),i=e.i(394487),l=e.i(914189),u=e.i(835696),c=e.i(941444),d=e.i(144279),f=e.i(294316),p=e.i(640497),m=e.i(2788),b=e.i(652265),v=e.i(397701),h=e.i(368578),g=e.i(402155),y=e.i(700020),E=e.i(963703),T=e.i(998348),w=((t=w||{})[t.Forwards=0]="Forwards",t[t.Backwards=1]="Backwards",t),x=((r=x||{})[r.Less=-1]="Less",r[r.Equal=0]="Equal",r[r.Greater=1]="Greater",r),F=((n=F||{})[n.SetSelectedIndex=0]="SetSelectedIndex",n[n.RegisterTab=1]="RegisterTab",n[n.UnregisterTab=2]="UnregisterTab",n[n.RegisterPanel=3]="RegisterPanel",n[n.UnregisterPanel=4]="UnregisterPanel",n);let P={0(e,t){var r;let n=(0,b.sortByDomNode)(e.tabs,e=>e.current),o=(0,b.sortByDomNode)(e.panels,e=>e.current),s=n.filter(e=>{var t;return!(null!=(t=e.current)&&t.hasAttribute("disabled"))}),a={...e,tabs:n,panels:o};if(t.index<0||t.index>n.length-1){let r=(0,v.match)(Math.sign(t.index-e.selectedIndex),{[-1]:()=>1,0:()=>(0,v.match)(Math.sign(t.index),{[-1]:()=>0,0:()=>0,1:()=>1}),1:()=>0});if(0===s.length)return a;let o=(0,v.match)(r,{0:()=>n.indexOf(s[0]),1:()=>n.indexOf(s[s.length-1])});return{...a,selectedIndex:-1===o?e.selectedIndex:o}}let i=n.slice(0,t.index),l=[...n.slice(t.index),...i].find(e=>s.includes(e));if(!l)return a;let u=null!=(r=n.indexOf(l))?r:e.selectedIndex;return -1===u&&(u=e.selectedIndex),{...a,selectedIndex:u}},1(e,t){if(e.tabs.includes(t.tab))return e;let r=e.tabs[e.selectedIndex],n=(0,b.sortByDomNode)([...e.tabs,t.tab],e=>e.current),o=e.selectedIndex;return e.info.current.isControlled||-1===(o=n.indexOf(r))&&(o=e.selectedIndex),{...e,tabs:n,selectedIndex:o}},2:(e,t)=>({...e,tabs:e.tabs.filter(e=>e!==t.tab)}),3:(e,t)=>e.panels.includes(t.panel)?e:{...e,panels:(0,b.sortByDomNode)([...e.panels,t.panel],e=>e.current)},4:(e,t)=>({...e,panels:e.panels.filter(e=>e!==t.panel)})},k=(0,a.createContext)(null);function L(e){let t=(0,a.useContext)(k);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,L),t}return t}k.displayName="TabsDataContext";let N=(0,a.createContext)(null);function C(e){let t=(0,a.useContext)(N);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,C),t}return t}function I(e,t){return(0,v.match)(t.type,P,e,t)}N.displayName="TabsActionsContext";let S=y.RenderFeatures.RenderStrategy|y.RenderFeatures.Static,A=Object.assign((0,y.forwardRefWithAs)(function(e,t){var r,n;let c=(0,a.useId)(),{id:p=`headlessui-tabs-tab-${c}`,disabled:m=!1,autoFocus:w=!1,...x}=e,{orientation:F,activation:P,selectedIndex:k,tabs:N,panels:I}=L("Tab"),S=C("Tab"),A=L("Tab"),[M,R]=(0,a.useState)(null),O=(0,a.useRef)(null),D=(0,f.useSyncRefs)(O,t,R);(0,u.useIsoMorphicEffect)(()=>S.registerTab(O),[S,O]);let H=(0,E.useStableCollectionIndex)("tabs"),j=N.indexOf(O);-1===j&&(j=H);let K=j===k,W=(0,l.useEvent)(e=>{var t;let r=e();if(r===b.FocusResult.Success&&"auto"===P){let e=null==(t=(0,g.getOwnerDocument)(O))?void 0:t.activeElement,r=A.tabs.findIndex(t=>t.current===e);-1!==r&&S.change(r)}return r}),B=(0,l.useEvent)(e=>{let t=N.map(e=>e.current).filter(Boolean);if(e.key===T.Keys.Space||e.key===T.Keys.Enter){e.preventDefault(),e.stopPropagation(),S.change(j);return}switch(e.key){case T.Keys.Home:case T.Keys.PageUp:return e.preventDefault(),e.stopPropagation(),W(()=>(0,b.focusIn)(t,b.Focus.First));case T.Keys.End:case T.Keys.PageDown:return e.preventDefault(),e.stopPropagation(),W(()=>(0,b.focusIn)(t,b.Focus.Last))}if(W(()=>(0,v.match)(F,{vertical:()=>e.key===T.Keys.ArrowUp?(0,b.focusIn)(t,b.Focus.Previous|b.Focus.WrapAround):e.key===T.Keys.ArrowDown?(0,b.focusIn)(t,b.Focus.Next|b.Focus.WrapAround):b.FocusResult.Error,horizontal:()=>e.key===T.Keys.ArrowLeft?(0,b.focusIn)(t,b.Focus.Previous|b.Focus.WrapAround):e.key===T.Keys.ArrowRight?(0,b.focusIn)(t,b.Focus.Next|b.Focus.WrapAround):b.FocusResult.Error}))===b.FocusResult.Success)return e.preventDefault()}),V=(0,a.useRef)(!1),_=(0,l.useEvent)(()=>{var e;V.current||(V.current=!0,null==(e=O.current)||e.focus({preventScroll:!0}),S.change(j),(0,h.microTask)(()=>{V.current=!1}))}),G=(0,l.useEvent)(e=>{e.preventDefault()}),{isFocusVisible:U,focusProps:$}=(0,o.useFocusRing)({autoFocus:w}),{isHovered:q,hoverProps:X}=(0,s.useHover)({isDisabled:m}),{pressed:Y,pressProps:z}=(0,i.useActivePress)({disabled:m}),Z=(0,a.useMemo)(()=>({selected:K,hover:q,active:Y,focus:U,autofocus:w,disabled:m}),[K,q,U,Y,w,m]),J=(0,y.mergeProps)({ref:D,onKeyDown:B,onMouseDown:G,onClick:_,id:p,role:"tab",type:(0,d.useResolveButtonType)(e,M),"aria-controls":null==(n=null==(r=I[j])?void 0:r.current)?void 0:n.id,"aria-selected":K,tabIndex:K?0:-1,disabled:m||void 0,autoFocus:w},$,X,z);return(0,y.useRender)()({ourProps:J,theirProps:x,slot:Z,defaultTag:"button",name:"Tabs.Tab"})}),{Group:(0,y.forwardRefWithAs)(function(e,t){let{defaultIndex:r=0,vertical:n=!1,manual:o=!1,onChange:s,selectedIndex:i=null,...d}=e,m=n?"vertical":"horizontal",v=o?"manual":"auto",h=null!==i,g=(0,c.useLatestValue)({isControlled:h}),T=(0,f.useSyncRefs)(t),[w,x]=(0,a.useReducer)(I,{info:g,selectedIndex:null!=i?i:r,tabs:[],panels:[]}),F=(0,a.useMemo)(()=>({selectedIndex:w.selectedIndex}),[w.selectedIndex]),P=(0,c.useLatestValue)(s||(()=>{})),L=(0,c.useLatestValue)(w.tabs),C=(0,a.useMemo)(()=>({orientation:m,activation:v,...w}),[m,v,w]),S=(0,l.useEvent)(e=>(x({type:1,tab:e}),()=>x({type:2,tab:e}))),A=(0,l.useEvent)(e=>(x({type:3,panel:e}),()=>x({type:4,panel:e}))),M=(0,l.useEvent)(e=>{R.current!==e&&P.current(e),h||x({type:0,index:e})}),R=(0,c.useLatestValue)(h?e.selectedIndex:w.selectedIndex),O=(0,a.useMemo)(()=>({registerTab:S,registerPanel:A,change:M}),[]);(0,u.useIsoMorphicEffect)(()=>{x({type:0,index:null!=i?i:r})},[i]),(0,u.useIsoMorphicEffect)(()=>{if(void 0===R.current||w.tabs.length<=0)return;let e=(0,b.sortByDomNode)(w.tabs,e=>e.current);e.some((e,t)=>w.tabs[t]!==e)&&M(e.indexOf(w.tabs[R.current]))});let D=(0,y.useRender)();return a.default.createElement(E.StableCollection,null,a.default.createElement(N.Provider,{value:O},a.default.createElement(k.Provider,{value:C},C.tabs.length<=0&&a.default.createElement(p.FocusSentinel,{onFocus:()=>{var e,t;for(let r of L.current)if((null==(e=r.current)?void 0:e.tabIndex)===0)return null==(t=r.current)||t.focus(),!0;return!1}}),D({ourProps:{ref:T},theirProps:d,slot:F,defaultTag:"div",name:"Tabs"}))))}),List:(0,y.forwardRefWithAs)(function(e,t){let{orientation:r,selectedIndex:n}=L("Tab.List"),o=(0,f.useSyncRefs)(t),s=(0,a.useMemo)(()=>({selectedIndex:n}),[n]);return(0,y.useRender)()({ourProps:{ref:o,role:"tablist","aria-orientation":r},theirProps:e,slot:s,defaultTag:"div",name:"Tabs.List"})}),Panels:(0,y.forwardRefWithAs)(function(e,t){let{selectedIndex:r}=L("Tab.Panels"),n=(0,f.useSyncRefs)(t),o=(0,a.useMemo)(()=>({selectedIndex:r}),[r]);return(0,y.useRender)()({ourProps:{ref:n},theirProps:e,slot:o,defaultTag:"div",name:"Tabs.Panels"})}),Panel:(0,y.forwardRefWithAs)(function(e,t){var r,n,s,i;let l=(0,a.useId)(),{id:c=`headlessui-tabs-panel-${l}`,tabIndex:d=0,...p}=e,{selectedIndex:b,tabs:v,panels:h}=L("Tab.Panel"),g=C("Tab.Panel"),T=(0,a.useRef)(null),w=(0,f.useSyncRefs)(T,t);(0,u.useIsoMorphicEffect)(()=>g.registerPanel(T),[g,T]);let x=(0,E.useStableCollectionIndex)("panels"),F=h.indexOf(T);-1===F&&(F=x);let P=F===b,{isFocusVisible:k,focusProps:N}=(0,o.useFocusRing)(),I=(0,a.useMemo)(()=>({selected:P,focus:k}),[P,k]),A=(0,y.mergeProps)({ref:w,id:c,role:"tabpanel","aria-labelledby":null==(n=null==(r=v[F])?void 0:r.current)?void 0:n.id,tabIndex:P?d:-1},N),M=(0,y.useRender)();return P||null!=(s=p.unmount)&&!s||null!=(i=p.static)&&i?M({ourProps:A,theirProps:p,slot:I,defaultTag:"div",features:S,visible:P,name:"Tabs.Panel"}):a.default.createElement(m.Hidden,{"aria-hidden":"true",...A})})});e.s(["Tab",0,A])},723731,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(751734),o=e.i(144582),s=e.i(444755),a=e.i(673706),i=e.i(271645);let l=(0,a.makeClassName)("TabPanels"),u=i.default.forwardRef((e,a)=>{let{children:u,className:c}=e,d=(0,t.__rest)(e,["children","className"]);return i.default.createElement(r.Tab.Panels,Object.assign({as:"div",ref:a,className:(0,s.tremorTwMerge)(l("root"),"w-full",c)},d),({selectedIndex:e})=>i.default.createElement(o.default.Provider,{value:{selectedValue:e}},i.default.Children.map(u,(e,t)=>i.default.createElement(n.default.Provider,{value:t},e))))});u.displayName="TabPanels",e.s(["TabPanels",0,u],723731)},653824,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(444755),o=e.i(673706),s=e.i(271645);let a=(0,o.makeClassName)("TabGroup"),i=s.default.forwardRef((e,o)=>{let{defaultIndex:i,index:l,onIndexChange:u,children:c,className:d}=e,f=(0,t.__rest)(e,["defaultIndex","index","onIndexChange","children","className"]);return s.default.createElement(r.Tab.Group,Object.assign({as:"div",ref:o,defaultIndex:i,selectedIndex:l,onChange:u,className:(0,n.tremorTwMerge)(a("root"),"w-full",d)},f),c)});i.displayName="TabGroup",e.s(["TabGroup",0,i],653824)},405371,910342,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(480731);let o=(0,r.createContext)(n.BaseColors.Blue);e.s(["default",0,o],910342);var s=e.i(970554),a=e.i(444755);let i=(0,e.i(673706).makeClassName)("TabList"),l=(0,r.createContext)("line"),u={line:(0,a.tremorTwMerge)("flex border-b space-x-4","border-tremor-border","dark:border-dark-tremor-border"),solid:(0,a.tremorTwMerge)("inline-flex p-0.5 rounded-tremor-default space-x-1.5","bg-tremor-background-subtle","dark:bg-dark-tremor-background-subtle")},c=r.default.forwardRef((e,n)=>{let{color:c,variant:d="line",children:f,className:p}=e,m=(0,t.__rest)(e,["color","variant","children","className"]);return r.default.createElement(s.Tab.List,Object.assign({ref:n,className:(0,a.tremorTwMerge)(i("root"),"justify-start overflow-x-clip",u[d],p)},m),r.default.createElement(l.Provider,{value:d},r.default.createElement(o.Provider,{value:c},f)))});c.displayName="TabList",e.s(["TabVariantContext",0,l,"default",0,c],405371)},881073,e=>{"use strict";var t=e.i(405371);e.s(["TabList",()=>t.default])},197647,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(95779),o=e.i(444755),s=e.i(673706),a=e.i(271645),i=e.i(405371),l=e.i(910342);let u=(0,s.makeClassName)("Tab"),c=a.default.forwardRef((e,c)=>{let{icon:d,className:f,children:p}=e,m=(0,t.__rest)(e,["icon","className","children"]),b=(0,a.useContext)(i.TabVariantContext),v=(0,a.useContext)(l.default);return a.default.createElement(r.Tab,Object.assign({ref:c,className:(0,o.tremorTwMerge)(u("root"),"flex whitespace-nowrap truncate max-w-xs outline-none data-focus-visible:ring text-tremor-default transition duration-100",function(e,t){switch(e){case"line":return(0,o.tremorTwMerge)("data-[selected]:border-b-2 hover:border-b-2 border-transparent transition duration-100 -mb-px px-2 py-2","hover:border-tremor-content hover:text-tremor-content-emphasis text-tremor-content","[&:not([data-selected])]:dark:hover:border-dark-tremor-content-emphasis [&:not([data-selected])]:dark:hover:text-dark-tremor-content-emphasis [&:not([data-selected])]:dark:text-dark-tremor-content",t?(0,s.getColorClassNames)(t,n.colorPalette.border).selectBorderColor:["data-[selected]:border-tremor-brand data-[selected]:text-tremor-brand","data-[selected]:dark:border-dark-tremor-brand data-[selected]:dark:text-dark-tremor-brand"]);case"solid":return(0,o.tremorTwMerge)("border-transparent border rounded-tremor-small px-2.5 py-1","data-[selected]:border-tremor-border data-[selected]:bg-tremor-background data-[selected]:shadow-tremor-input [&:not([data-selected])]:hover:text-tremor-content-emphasis data-[selected]:text-tremor-brand [&:not([data-selected])]:text-tremor-content","dark:data-[selected]:border-dark-tremor-border dark:data-[selected]:bg-dark-tremor-background dark:data-[selected]:shadow-dark-tremor-input dark:[&:not([data-selected])]:hover:text-dark-tremor-content-emphasis dark:data-[selected]:text-dark-tremor-brand dark:[&:not([data-selected])]:text-dark-tremor-content",t?(0,s.getColorClassNames)(t,n.colorPalette.text).selectTextColor:"text-tremor-content dark:text-dark-tremor-content")}}(b,v),f,v&&(0,s.getColorClassNames)(v,n.colorPalette.text).selectTextColor)},m),d?a.default.createElement(d,{className:(0,o.tremorTwMerge)(u("icon"),"flex-none h-5 w-5",p?"mr-2":"")}):null,p?a.default.createElement("span",null,p):null)});c.displayName="Tab",e.s(["Tab",0,c],197647)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02-2~p5k.ielz.js b/litellm/proxy/_experimental/out/_next/static/chunks/02-2~p5k.ielz.js new file mode 100644 index 00000000000..b18990d8cf2 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02-2~p5k.ielz.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,312361,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),r=e.i(242064),i=e.i(517455);e.i(296059);var a=e.i(915654),l=e.i(183293),o=e.i(246422),c=e.i(838378);let s=(0,o.genStyleHooks)("Divider",e=>{let t=(0,c.mergeToken)(e,{dividerHorizontalWithTextGutterMargin:e.margin,sizePaddingEdgeHorizontal:0});return[(e=>{let{componentCls:t,sizePaddingEdgeHorizontal:n,colorSplit:r,lineWidth:i,textPaddingInline:o,orientationMargin:c,verticalMarginInline:s}=e;return{[t]:Object.assign(Object.assign({},(0,l.resetComponent)(e)),{borderBlockStart:`${(0,a.unit)(i)} solid ${r}`,"&-vertical":{position:"relative",top:"-0.06em",display:"inline-block",height:"0.9em",marginInline:s,marginBlock:0,verticalAlign:"middle",borderTop:0,borderInlineStart:`${(0,a.unit)(i)} solid ${r}`},"&-horizontal":{display:"flex",clear:"both",width:"100%",minWidth:"100%",margin:`${(0,a.unit)(e.marginLG)} 0`},[`&-horizontal${t}-with-text`]:{display:"flex",alignItems:"center",margin:`${(0,a.unit)(e.dividerHorizontalWithTextGutterMargin)} 0`,color:e.colorTextHeading,fontWeight:500,fontSize:e.fontSizeLG,whiteSpace:"nowrap",textAlign:"center",borderBlockStart:`0 ${r}`,"&::before, &::after":{position:"relative",width:"50%",borderBlockStart:`${(0,a.unit)(i)} solid transparent`,borderBlockStartColor:"inherit",borderBlockEnd:0,transform:"translateY(50%)",content:"''"}},[`&-horizontal${t}-with-text-start`]:{"&::before":{width:`calc(${c} * 100%)`},"&::after":{width:`calc(100% - ${c} * 100%)`}},[`&-horizontal${t}-with-text-end`]:{"&::before":{width:`calc(100% - ${c} * 100%)`},"&::after":{width:`calc(${c} * 100%)`}},[`${t}-inner-text`]:{display:"inline-block",paddingBlock:0,paddingInline:o},"&-dashed":{background:"none",borderColor:r,borderStyle:"dashed",borderWidth:`${(0,a.unit)(i)} 0 0`},[`&-horizontal${t}-with-text${t}-dashed`]:{"&::before, &::after":{borderStyle:"dashed none none"}},[`&-vertical${t}-dashed`]:{borderInlineStartWidth:i,borderInlineEnd:0,borderBlockStart:0,borderBlockEnd:0},"&-dotted":{background:"none",borderColor:r,borderStyle:"dotted",borderWidth:`${(0,a.unit)(i)} 0 0`},[`&-horizontal${t}-with-text${t}-dotted`]:{"&::before, &::after":{borderStyle:"dotted none none"}},[`&-vertical${t}-dotted`]:{borderInlineStartWidth:i,borderInlineEnd:0,borderBlockStart:0,borderBlockEnd:0},[`&-plain${t}-with-text`]:{color:e.colorText,fontWeight:"normal",fontSize:e.fontSize},[`&-horizontal${t}-with-text-start${t}-no-default-orientation-margin-start`]:{"&::before":{width:0},"&::after":{width:"100%"},[`${t}-inner-text`]:{paddingInlineStart:n}},[`&-horizontal${t}-with-text-end${t}-no-default-orientation-margin-end`]:{"&::before":{width:"100%"},"&::after":{width:0},[`${t}-inner-text`]:{paddingInlineEnd:n}}})}})(t),(e=>{let{componentCls:t}=e;return{[t]:{"&-horizontal":{[`&${t}`]:{"&-sm":{marginBlock:e.marginXS},"&-md":{marginBlock:e.margin}}}}}})(t)]},e=>({textPaddingInline:"1em",orientationMargin:.05,verticalMarginInline:e.marginXS}),{unitless:{orientationMargin:!0}});var d=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let u={small:"sm",middle:"md"};e.s(["Divider",0,e=>{let{getPrefixCls:a,direction:l,className:o,style:c}=(0,r.useComponentConfig)("divider"),{prefixCls:g,type:p="horizontal",orientation:m="center",orientationMargin:f,className:b,rootClassName:h,children:$,dashed:y,variant:v="solid",plain:S,style:C,size:k}=e,w=d(e,["prefixCls","type","orientation","orientationMargin","className","rootClassName","children","dashed","variant","plain","style","size"]),O=a("divider",g),[x,I,E]=s(O),j=u[(0,i.default)(k)],z=!!$,N=t.useMemo(()=>"left"===m?"rtl"===l?"end":"start":"right"===m?"rtl"===l?"start":"end":m,[l,m]),P="start"===N&&null!=f,M="end"===N&&null!=f,T=(0,n.default)(O,o,I,E,`${O}-${p}`,{[`${O}-with-text`]:z,[`${O}-with-text-${N}`]:z,[`${O}-dashed`]:!!y,[`${O}-${v}`]:"solid"!==v,[`${O}-plain`]:!!S,[`${O}-rtl`]:"rtl"===l,[`${O}-no-default-orientation-margin-start`]:P,[`${O}-no-default-orientation-margin-end`]:M,[`${O}-${j}`]:!!j},b,h),B=t.useMemo(()=>"number"==typeof f?f:/^\d+$/.test(f)?Number(f):f,[f]);return x(t.createElement("div",Object.assign({className:T,style:Object.assign(Object.assign({},c),C)},w,{role:"separator"}),$&&"vertical"!==p&&t.createElement("span",{className:`${O}-inner-text`,style:{marginInlineStart:P?B:void 0,marginInlineEnd:M?B:void 0}},$)))}],312361)},56456,e=>{"use strict";var t=e.i(739295);e.s(["LoadingOutlined",()=>t.default])},801312,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"}}]},name:"left",theme:"outlined"};var i=e.i(9583),a=n.forwardRef(function(e,a){return n.createElement(i.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["default",0,a],801312)},38243,908286,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),r=e.i(876556);function i(e){return["small","middle","large"].includes(e)}function a(e){return!!e&&"number"==typeof e&&!Number.isNaN(e)}e.s(["isPresetSize",0,i,"isValidGapNumber",0,a],908286);var l=e.i(242064),o=e.i(249616),c=e.i(372409),s=e.i(246422);let d=(0,s.genStyleHooks)(["Space","Addon"],e=>[(e=>{let{componentCls:t,borderRadius:n,paddingSM:r,colorBorder:i,paddingXS:a,fontSizeLG:l,fontSizeSM:o,borderRadiusLG:s,borderRadiusSM:d,colorBgContainerDisabled:u,lineWidth:g}=e;return{[t]:[{display:"inline-flex",alignItems:"center",gap:0,paddingInline:r,margin:0,background:u,borderWidth:g,borderStyle:"solid",borderColor:i,borderRadius:n,"&-large":{fontSize:l,borderRadius:s},"&-small":{paddingInline:a,borderRadius:d,fontSize:o},"&-compact-last-item":{borderEndStartRadius:0,borderStartStartRadius:0},"&-compact-first-item":{borderEndEndRadius:0,borderStartEndRadius:0},"&-compact-item:not(:first-child):not(:last-child)":{borderRadius:0},"&-compact-item:not(:last-child)":{borderInlineEndWidth:0}},(0,c.genCompactItemStyle)(e,{focus:!1})]}})(e)]);var u=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let g=t.default.forwardRef((e,r)=>{let{className:i,children:a,style:c,prefixCls:s}=e,g=u(e,["className","children","style","prefixCls"]),{getPrefixCls:p,direction:m}=t.default.useContext(l.ConfigContext),f=p("space-addon",s),[b,h,$]=d(f),{compactItemClassnames:y,compactSize:v}=(0,o.useCompactItemContext)(f,m),S=(0,n.default)(f,h,y,$,{[`${f}-${v}`]:v},i);return b(t.default.createElement("div",Object.assign({ref:r,className:S,style:c},g),a))}),p=t.default.createContext({latestIndex:0}),m=p.Provider,f=({className:e,index:n,children:r,split:i,style:a})=>{let{latestIndex:l}=t.useContext(p);return null==r?null:t.createElement(t.Fragment,null,t.createElement("div",{className:e,style:a},r),n{let t=(0,b.mergeToken)(e,{spaceGapSmallSize:e.paddingXS,spaceGapMiddleSize:e.padding,spaceGapLargeSize:e.paddingLG});return[(e=>{let{componentCls:t,antCls:n}=e;return{[t]:{display:"inline-flex","&-rtl":{direction:"rtl"},"&-vertical":{flexDirection:"column"},"&-align":{flexDirection:"column","&-center":{alignItems:"center"},"&-start":{alignItems:"flex-start"},"&-end":{alignItems:"flex-end"},"&-baseline":{alignItems:"baseline"}},[`${t}-item:empty`]:{display:"none"},[`${t}-item > ${n}-badge-not-a-wrapper:only-child`]:{display:"block"}}}})(t),(e=>{let{componentCls:t}=e;return{[t]:{"&-gap-row-small":{rowGap:e.spaceGapSmallSize},"&-gap-row-middle":{rowGap:e.spaceGapMiddleSize},"&-gap-row-large":{rowGap:e.spaceGapLargeSize},"&-gap-col-small":{columnGap:e.spaceGapSmallSize},"&-gap-col-middle":{columnGap:e.spaceGapMiddleSize},"&-gap-col-large":{columnGap:e.spaceGapLargeSize}}}})(t)]},()=>({}),{resetStyle:!1});var $=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let y=t.forwardRef((e,o)=>{var c;let{getPrefixCls:s,direction:d,size:u,className:g,style:p,classNames:b,styles:y}=(0,l.useComponentConfig)("space"),{size:v=null!=u?u:"small",align:S,className:C,rootClassName:k,children:w,direction:O="horizontal",prefixCls:x,split:I,style:E,wrap:j=!1,classNames:z,styles:N}=e,P=$(e,["size","align","className","rootClassName","children","direction","prefixCls","split","style","wrap","classNames","styles"]),[M,T]=Array.isArray(v)?v:[v,v],B=i(T),H=i(M),R=a(T),W=a(M),D=(0,r.default)(w,{keepEmpty:!0}),q=void 0===S&&"horizontal"===O?"center":S,G=s("space",x),[L,A,F]=h(G),X=(0,n.default)(G,g,A,`${G}-${O}`,{[`${G}-rtl`]:"rtl"===d,[`${G}-align-${q}`]:q,[`${G}-gap-row-${T}`]:B,[`${G}-gap-col-${M}`]:H},C,k,F),V=(0,n.default)(`${G}-item`,null!=(c=null==z?void 0:z.item)?c:b.item),K=Object.assign(Object.assign({},y.item),null==N?void 0:N.item),U=D.map((e,n)=>{let r=(null==e?void 0:e.key)||`${V}-${n}`;return t.createElement(f,{className:V,key:r,index:n,split:I,style:K},e)}),_=t.useMemo(()=>({latestIndex:D.reduce((e,t,n)=>null!=t?n:e,0)}),[D]);if(0===D.length)return null;let Q={};return j&&(Q.flexWrap="wrap"),!H&&W&&(Q.columnGap=M),!B&&R&&(Q.rowGap=T),L(t.createElement("div",Object.assign({ref:o,className:X,style:Object.assign(Object.assign(Object.assign({},Q),p),E)},P),t.createElement(m,{value:_},U)))});y.Compact=o.default,y.Addon=g,e.s(["default",0,y],38243)},262218,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),r=e.i(529681),i=e.i(702779),a=e.i(563113),l=e.i(763731),o=e.i(121872),c=e.i(242064);e.i(296059);var s=e.i(915654),d=e.i(135551),u=e.i(183293),g=e.i(246422),p=e.i(838378);let m=e=>{let{lineWidth:t,fontSizeIcon:n,calc:r}=e,i=e.fontSizeSM;return(0,p.mergeToken)(e,{tagFontSize:i,tagLineHeight:(0,s.unit)(r(e.lineHeightSM).mul(i).equal()),tagIconSize:r(n).sub(r(t).mul(2)).equal(),tagPaddingHorizontal:8,tagBorderlessBg:e.defaultBg})},f=e=>({defaultBg:new d.FastColor(e.colorFillQuaternary).onBackground(e.colorBgContainer).toHexString(),defaultColor:e.colorText}),b=(0,g.genStyleHooks)("Tag",e=>(e=>{let{paddingXXS:t,lineWidth:n,tagPaddingHorizontal:r,componentCls:i,calc:a}=e,l=a(r).sub(n).equal(),o=a(t).sub(n).equal();return{[i]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{display:"inline-block",height:"auto",marginInlineEnd:e.marginXS,paddingInline:l,fontSize:e.tagFontSize,lineHeight:e.tagLineHeight,whiteSpace:"nowrap",background:e.defaultBg,border:`${(0,s.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadiusSM,opacity:1,transition:`all ${e.motionDurationMid}`,textAlign:"start",position:"relative",[`&${i}-rtl`]:{direction:"rtl"},"&, a, a:hover":{color:e.defaultColor},[`${i}-close-icon`]:{marginInlineStart:o,fontSize:e.tagIconSize,color:e.colorIcon,cursor:"pointer",transition:`all ${e.motionDurationMid}`,"&:hover":{color:e.colorTextHeading}},[`&${i}-has-color`]:{borderColor:"transparent",[`&, a, a:hover, ${e.iconCls}-close, ${e.iconCls}-close:hover`]:{color:e.colorTextLightSolid}},"&-checkable":{backgroundColor:"transparent",borderColor:"transparent",cursor:"pointer",[`&:not(${i}-checkable-checked):hover`]:{color:e.colorPrimary,backgroundColor:e.colorFillSecondary},"&:active, &-checked":{color:e.colorTextLightSolid},"&-checked":{backgroundColor:e.colorPrimary,"&:hover":{backgroundColor:e.colorPrimaryHover}},"&:active":{backgroundColor:e.colorPrimaryActive}},"&-hidden":{display:"none"},[`> ${e.iconCls} + span, > span + ${e.iconCls}`]:{marginInlineStart:l}}),[`${i}-borderless`]:{borderColor:"transparent",background:e.tagBorderlessBg}}})(m(e)),f);var h=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let $=t.forwardRef((e,r)=>{let{prefixCls:i,style:a,className:l,checked:o,children:s,icon:d,onChange:u,onClick:g}=e,p=h(e,["prefixCls","style","className","checked","children","icon","onChange","onClick"]),{getPrefixCls:m,tag:f}=t.useContext(c.ConfigContext),$=m("tag",i),[y,v,S]=b($),C=(0,n.default)($,`${$}-checkable`,{[`${$}-checkable-checked`]:o},null==f?void 0:f.className,l,v,S);return y(t.createElement("span",Object.assign({},p,{ref:r,style:Object.assign(Object.assign({},a),null==f?void 0:f.style),className:C,onClick:e=>{null==u||u(!o),null==g||g(e)}}),d,t.createElement("span",null,s)))});var y=e.i(403541);let v=(0,g.genSubStyleComponent)(["Tag","preset"],e=>{let t;return t=m(e),(0,y.genPresetColor)(t,(e,{textColor:n,lightBorderColor:r,lightColor:i,darkColor:a})=>({[`${t.componentCls}${t.componentCls}-${e}`]:{color:n,background:i,borderColor:r,"&-inverse":{color:t.colorTextLightSolid,background:a,borderColor:a},[`&${t.componentCls}-borderless`]:{borderColor:"transparent"}}}))},f),S=(e,t,n)=>{let r="string"!=typeof n?n:n.charAt(0).toUpperCase()+n.slice(1);return{[`${e.componentCls}${e.componentCls}-${t}`]:{color:e[`color${n}`],background:e[`color${r}Bg`],borderColor:e[`color${r}Border`],[`&${e.componentCls}-borderless`]:{borderColor:"transparent"}}}},C=(0,g.genSubStyleComponent)(["Tag","status"],e=>{let t=m(e);return[S(t,"success","Success"),S(t,"processing","Info"),S(t,"error","Error"),S(t,"warning","Warning")]},f);var k=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let w=t.forwardRef((e,s)=>{let{prefixCls:d,className:u,rootClassName:g,style:p,children:m,icon:f,color:h,onClose:$,bordered:y=!0,visible:S}=e,w=k(e,["prefixCls","className","rootClassName","style","children","icon","color","onClose","bordered","visible"]),{getPrefixCls:O,direction:x,tag:I}=t.useContext(c.ConfigContext),[E,j]=t.useState(!0),z=(0,r.default)(w,["closeIcon","closable"]);t.useEffect(()=>{void 0!==S&&j(S)},[S]);let N=(0,i.isPresetColor)(h),P=(0,i.isPresetStatusColor)(h),M=N||P,T=Object.assign(Object.assign({backgroundColor:h&&!M?h:void 0},null==I?void 0:I.style),p),B=O("tag",d),[H,R,W]=b(B),D=(0,n.default)(B,null==I?void 0:I.className,{[`${B}-${h}`]:M,[`${B}-has-color`]:h&&!M,[`${B}-hidden`]:!E,[`${B}-rtl`]:"rtl"===x,[`${B}-borderless`]:!y},u,g,R,W),q=e=>{e.stopPropagation(),null==$||$(e),e.defaultPrevented||j(!1)},[,G]=(0,a.useClosable)((0,a.pickClosable)(e),(0,a.pickClosable)(I),{closable:!1,closeIconRender:e=>{let r=t.createElement("span",{className:`${B}-close-icon`,onClick:q},e);return(0,l.replaceElement)(e,r,e=>({onClick:t=>{var n;null==(n=null==e?void 0:e.onClick)||n.call(e,t),q(t)},className:(0,n.default)(null==e?void 0:e.className,`${B}-close-icon`)}))}}),L="function"==typeof w.onClick||m&&"a"===m.type,A=f||null,F=A?t.createElement(t.Fragment,null,A,m&&t.createElement("span",null,m)):m,X=t.createElement("span",Object.assign({},z,{ref:s,className:D,style:T}),F,G,N&&t.createElement(v,{key:"preset",prefixCls:B}),P&&t.createElement(C,{key:"status",prefixCls:B}));return H(L?t.createElement(o.default,{component:"Tag"},X):X)});w.CheckableTag=$,e.s(["Tag",0,w],262218)},770914,e=>{"use strict";var t=e.i(38243);e.s(["Space",()=>t.default])},790848,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(739295),r=e.i(343794),i=e.i(931067),a=e.i(211577),l=e.i(392221),o=e.i(703923),c=e.i(914949),s=e.i(404948),d=["prefixCls","className","checked","defaultChecked","disabled","loadingIcon","checkedChildren","unCheckedChildren","onClick","onChange","onKeyDown"],u=t.forwardRef(function(e,n){var u,g=e.prefixCls,p=void 0===g?"rc-switch":g,m=e.className,f=e.checked,b=e.defaultChecked,h=e.disabled,$=e.loadingIcon,y=e.checkedChildren,v=e.unCheckedChildren,S=e.onClick,C=e.onChange,k=e.onKeyDown,w=(0,o.default)(e,d),O=(0,c.default)(!1,{value:f,defaultValue:b}),x=(0,l.default)(O,2),I=x[0],E=x[1];function j(e,t){var n=I;return h||(E(n=e),null==C||C(n,t)),n}var z=(0,r.default)(p,m,(u={},(0,a.default)(u,"".concat(p,"-checked"),I),(0,a.default)(u,"".concat(p,"-disabled"),h),u));return t.createElement("button",(0,i.default)({},w,{type:"button",role:"switch","aria-checked":I,disabled:h,className:z,ref:n,onKeyDown:function(e){e.which===s.default.LEFT?j(!1,e):e.which===s.default.RIGHT&&j(!0,e),null==k||k(e)},onClick:function(e){var t=j(!I,e);null==S||S(t,e)}}),$,t.createElement("span",{className:"".concat(p,"-inner")},t.createElement("span",{className:"".concat(p,"-inner-checked")},y),t.createElement("span",{className:"".concat(p,"-inner-unchecked")},v)))});u.displayName="Switch";var g=e.i(121872),p=e.i(242064),m=e.i(937328),f=e.i(517455);e.i(296059);var b=e.i(915654),h=e.i(135551),$=e.i(183293),y=e.i(246422),v=e.i(838378);let S=(0,y.genStyleHooks)("Switch",e=>{let t=(0,v.mergeToken)(e,{switchDuration:e.motionDurationMid,switchColor:e.colorPrimary,switchDisabledOpacity:e.opacityLoading,switchLoadingIconSize:e.calc(e.fontSizeIcon).mul(.75).equal(),switchLoadingIconColor:`rgba(0, 0, 0, ${e.opacityLoading})`,switchHandleActiveInset:"-30%"});return[(e=>{let{componentCls:t,trackHeight:n,trackMinWidth:r}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign({},(0,$.resetComponent)(e)),{position:"relative",display:"inline-block",boxSizing:"border-box",minWidth:r,height:n,lineHeight:(0,b.unit)(n),verticalAlign:"middle",background:e.colorTextQuaternary,border:"0",borderRadius:100,cursor:"pointer",transition:`all ${e.motionDurationMid}`,userSelect:"none",[`&:hover:not(${t}-disabled)`]:{background:e.colorTextTertiary}}),(0,$.genFocusStyle)(e)),{[`&${t}-checked`]:{background:e.switchColor,[`&:hover:not(${t}-disabled)`]:{background:e.colorPrimaryHover}},[`&${t}-loading, &${t}-disabled`]:{cursor:"not-allowed",opacity:e.switchDisabledOpacity,"*":{boxShadow:"none",cursor:"not-allowed"}},[`&${t}-rtl`]:{direction:"rtl"}})}})(t),(e=>{let{componentCls:t,trackHeight:n,trackPadding:r,innerMinMargin:i,innerMaxMargin:a,handleSize:l,calc:o}=e,c=`${t}-inner`,s=(0,b.unit)(o(l).add(o(r).mul(2)).equal()),d=(0,b.unit)(o(a).mul(2).equal());return{[t]:{[c]:{display:"block",overflow:"hidden",borderRadius:100,height:"100%",paddingInlineStart:a,paddingInlineEnd:i,transition:`padding-inline-start ${e.switchDuration} ease-in-out, padding-inline-end ${e.switchDuration} ease-in-out`,[`${c}-checked, ${c}-unchecked`]:{display:"block",color:e.colorTextLightSolid,fontSize:e.fontSizeSM,transition:`margin-inline-start ${e.switchDuration} ease-in-out, margin-inline-end ${e.switchDuration} ease-in-out`,pointerEvents:"none",minHeight:n},[`${c}-checked`]:{marginInlineStart:`calc(-100% + ${s} - ${d})`,marginInlineEnd:`calc(100% - ${s} + ${d})`},[`${c}-unchecked`]:{marginTop:o(n).mul(-1).equal(),marginInlineStart:0,marginInlineEnd:0}},[`&${t}-checked ${c}`]:{paddingInlineStart:i,paddingInlineEnd:a,[`${c}-checked`]:{marginInlineStart:0,marginInlineEnd:0},[`${c}-unchecked`]:{marginInlineStart:`calc(100% - ${s} + ${d})`,marginInlineEnd:`calc(-100% + ${s} - ${d})`}},[`&:not(${t}-disabled):active`]:{[`&:not(${t}-checked) ${c}`]:{[`${c}-unchecked`]:{marginInlineStart:o(r).mul(2).equal(),marginInlineEnd:o(r).mul(-1).mul(2).equal()}},[`&${t}-checked ${c}`]:{[`${c}-checked`]:{marginInlineStart:o(r).mul(-1).mul(2).equal(),marginInlineEnd:o(r).mul(2).equal()}}}}}})(t),(e=>{let{componentCls:t,trackPadding:n,handleBg:r,handleShadow:i,handleSize:a,calc:l}=e,o=`${t}-handle`;return{[t]:{[o]:{position:"absolute",top:n,insetInlineStart:n,width:a,height:a,transition:`all ${e.switchDuration} ease-in-out`,"&::before":{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,backgroundColor:r,borderRadius:l(a).div(2).equal(),boxShadow:i,transition:`all ${e.switchDuration} ease-in-out`,content:'""'}},[`&${t}-checked ${o}`]:{insetInlineStart:`calc(100% - ${(0,b.unit)(l(a).add(n).equal())})`},[`&:not(${t}-disabled):active`]:{[`${o}::before`]:{insetInlineEnd:e.switchHandleActiveInset,insetInlineStart:0},[`&${t}-checked ${o}::before`]:{insetInlineEnd:0,insetInlineStart:e.switchHandleActiveInset}}}}})(t),(e=>{let{componentCls:t,handleSize:n,calc:r}=e;return{[t]:{[`${t}-loading-icon${e.iconCls}`]:{position:"relative",top:r(r(n).sub(e.fontSize)).div(2).equal(),color:e.switchLoadingIconColor,verticalAlign:"top"},[`&${t}-checked ${t}-loading-icon`]:{color:e.switchColor}}}})(t),(e=>{let{componentCls:t,trackHeightSM:n,trackPadding:r,trackMinWidthSM:i,innerMinMarginSM:a,innerMaxMarginSM:l,handleSizeSM:o,calc:c}=e,s=`${t}-inner`,d=(0,b.unit)(c(o).add(c(r).mul(2)).equal()),u=(0,b.unit)(c(l).mul(2).equal());return{[t]:{[`&${t}-small`]:{minWidth:i,height:n,lineHeight:(0,b.unit)(n),[`${t}-inner`]:{paddingInlineStart:l,paddingInlineEnd:a,[`${s}-checked, ${s}-unchecked`]:{minHeight:n},[`${s}-checked`]:{marginInlineStart:`calc(-100% + ${d} - ${u})`,marginInlineEnd:`calc(100% - ${d} + ${u})`},[`${s}-unchecked`]:{marginTop:c(n).mul(-1).equal(),marginInlineStart:0,marginInlineEnd:0}},[`${t}-handle`]:{width:o,height:o},[`${t}-loading-icon`]:{top:c(c(o).sub(e.switchLoadingIconSize)).div(2).equal(),fontSize:e.switchLoadingIconSize},[`&${t}-checked`]:{[`${t}-inner`]:{paddingInlineStart:a,paddingInlineEnd:l,[`${s}-checked`]:{marginInlineStart:0,marginInlineEnd:0},[`${s}-unchecked`]:{marginInlineStart:`calc(100% - ${d} + ${u})`,marginInlineEnd:`calc(-100% + ${d} - ${u})`}},[`${t}-handle`]:{insetInlineStart:`calc(100% - ${(0,b.unit)(c(o).add(r).equal())})`}},[`&:not(${t}-disabled):active`]:{[`&:not(${t}-checked) ${s}`]:{[`${s}-unchecked`]:{marginInlineStart:c(e.marginXXS).div(2).equal(),marginInlineEnd:c(e.marginXXS).mul(-1).div(2).equal()}},[`&${t}-checked ${s}`]:{[`${s}-checked`]:{marginInlineStart:c(e.marginXXS).mul(-1).div(2).equal(),marginInlineEnd:c(e.marginXXS).div(2).equal()}}}}}}})(t)]},e=>{let{fontSize:t,lineHeight:n,controlHeight:r,colorWhite:i}=e,a=t*n,l=r/2,o=a-4,c=l-4;return{trackHeight:a,trackHeightSM:l,trackMinWidth:2*o+8,trackMinWidthSM:2*c+4,trackPadding:2,handleBg:i,handleSize:o,handleSizeSM:c,handleShadow:`0 2px 4px 0 ${new h.FastColor("#00230b").setA(.2).toRgbString()}`,innerMinMargin:o/2,innerMaxMargin:o+2+4,innerMinMarginSM:c/2,innerMaxMarginSM:c+2+4}});var C=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let k=t.forwardRef((e,i)=>{let{prefixCls:a,size:l,disabled:o,loading:s,className:d,rootClassName:b,style:h,checked:$,value:y,defaultChecked:v,defaultValue:k,onChange:w}=e,O=C(e,["prefixCls","size","disabled","loading","className","rootClassName","style","checked","value","defaultChecked","defaultValue","onChange"]),[x,I]=(0,c.default)(!1,{value:null!=$?$:y,defaultValue:null!=v?v:k}),{getPrefixCls:E,direction:j,switch:z}=t.useContext(p.ConfigContext),N=t.useContext(m.default),P=(null!=o?o:N)||s,M=E("switch",a),T=t.createElement("div",{className:`${M}-handle`},s&&t.createElement(n.default,{className:`${M}-loading-icon`})),[B,H,R]=S(M),W=(0,f.default)(l),D=(0,r.default)(null==z?void 0:z.className,{[`${M}-small`]:"small"===W,[`${M}-loading`]:s,[`${M}-rtl`]:"rtl"===j},d,b,H,R),q=Object.assign(Object.assign({},null==z?void 0:z.style),h);return B(t.createElement(g.default,{component:"Switch",disabled:P},t.createElement(u,Object.assign({},O,{checked:x,onChange:(...e)=>{I(e[0]),null==w||w.apply(void 0,e)},prefixCls:M,className:D,style:q,disabled:P,ref:i,loadingIcon:T}))))});k.__ANT_SWITCH=!0,e.s(["Switch",0,k],790848)},292639,e=>{"use strict";var t=e.i(602869),n=e.i(266027);let r=(0,e.i(243652).createQueryKeys)("uiSettings");e.s(["useUISettings",0,()=>(0,n.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,t.getUiSettings)(),staleTime:36e5,gcTime:36e5})])},492030,e=>{"use strict";var t=e.i(121229);e.s(["CheckOutlined",()=>t.default])},829672,836938,310730,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),r=e.i(914949),i=e.i(404948);let a=e=>e?"function"==typeof e?e():e:null;e.s(["getRenderPropValue",0,a],836938);var l=e.i(613541),o=e.i(763731),c=e.i(242064),s=e.i(491816);e.i(793154);var d=e.i(880476),u=e.i(183293),g=e.i(717356),p=e.i(320560),m=e.i(307358),f=e.i(246422),b=e.i(838378),h=e.i(617933);let $=(0,f.genStyleHooks)("Popover",e=>{let{colorBgElevated:t,colorText:n}=e,r=(0,b.mergeToken)(e,{popoverBg:t,popoverColor:n});return[(e=>{let{componentCls:t,popoverColor:n,titleMinWidth:r,fontWeightStrong:i,innerPadding:a,boxShadowSecondary:l,colorTextHeading:o,borderRadiusLG:c,zIndexPopup:s,titleMarginBottom:d,colorBgElevated:g,popoverBg:m,titleBorderBottom:f,innerContentPadding:b,titlePadding:h}=e;return[{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"absolute",top:0,left:{_skip_check_:!0,value:0},zIndex:s,fontWeight:"normal",whiteSpace:"normal",textAlign:"start",cursor:"auto",userSelect:"text","--valid-offset-x":"var(--arrow-offset-horizontal, var(--arrow-x))",transformOrigin:"var(--valid-offset-x, 50%) var(--arrow-y, 50%)","--antd-arrow-background-color":g,width:"max-content",maxWidth:"100vw","&-rtl":{direction:"rtl"},"&-hidden":{display:"none"},[`${t}-content`]:{position:"relative"},[`${t}-inner`]:{backgroundColor:m,backgroundClip:"padding-box",borderRadius:c,boxShadow:l,padding:a},[`${t}-title`]:{minWidth:r,marginBottom:d,color:o,fontWeight:i,borderBottom:f,padding:h},[`${t}-inner-content`]:{color:n,padding:b}})},(0,p.default)(e,"var(--antd-arrow-background-color)"),{[`${t}-pure`]:{position:"relative",maxWidth:"none",margin:e.sizePopupArrow,display:"inline-block",[`${t}-content`]:{display:"inline-block"}}}]})(r),(e=>{let{componentCls:t}=e;return{[t]:h.PresetColors.map(n=>{let r=e[`${n}6`];return{[`&${t}-${n}`]:{"--antd-arrow-background-color":r,[`${t}-inner`]:{backgroundColor:r},[`${t}-arrow`]:{background:"transparent"}}}})}})(r),(0,g.initZoomMotion)(r,"zoom-big")]},e=>{let{lineWidth:t,controlHeight:n,fontHeight:r,padding:i,wireframe:a,zIndexPopupBase:l,borderRadiusLG:o,marginXS:c,lineType:s,colorSplit:d,paddingSM:u}=e,g=n-r;return Object.assign(Object.assign(Object.assign({titleMinWidth:177,zIndexPopup:l+30},(0,m.getArrowToken)(e)),(0,p.getArrowOffsetToken)({contentRadius:o,limitVerticalRadius:!0})),{innerPadding:12*!a,titleMarginBottom:a?0:c,titlePadding:a?`${g/2}px ${i}px ${g/2-t}px`:0,titleBorderBottom:a?`${t}px ${s} ${d}`:"none",innerContentPadding:a?`${u}px ${i}px`:0})},{resetStyle:!1,deprecatedTokens:[["width","titleMinWidth"],["minWidth","titleMinWidth"]]});var y=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let v=({title:e,content:n,prefixCls:r})=>e||n?t.createElement(t.Fragment,null,e&&t.createElement("div",{className:`${r}-title`},e),n&&t.createElement("div",{className:`${r}-inner-content`},n)):null,S=e=>{let{hashId:r,prefixCls:i,className:l,style:o,placement:c="top",title:s,content:u,children:g}=e,p=a(s),m=a(u),f=(0,n.default)(r,i,`${i}-pure`,`${i}-placement-${c}`,l);return t.createElement("div",{className:f,style:o},t.createElement("div",{className:`${i}-arrow`}),t.createElement(d.Popup,Object.assign({},e,{className:r,prefixCls:i}),g||t.createElement(v,{prefixCls:i,title:p,content:m})))},C=e=>{let{prefixCls:r,className:i}=e,a=y(e,["prefixCls","className"]),{getPrefixCls:l}=t.useContext(c.ConfigContext),o=l("popover",r),[s,d,u]=$(o);return s(t.createElement(S,Object.assign({},a,{prefixCls:o,hashId:d,className:(0,n.default)(i,u)})))};e.s(["Overlay",0,v,"default",0,C],310730);var k=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n};let w=t.forwardRef((e,d)=>{var u,g;let{prefixCls:p,title:m,content:f,overlayClassName:b,placement:h="top",trigger:y="hover",children:S,mouseEnterDelay:C=.1,mouseLeaveDelay:w=.1,onOpenChange:O,overlayStyle:x={},styles:I,classNames:E}=e,j=k(e,["prefixCls","title","content","overlayClassName","placement","trigger","children","mouseEnterDelay","mouseLeaveDelay","onOpenChange","overlayStyle","styles","classNames"]),{getPrefixCls:z,className:N,style:P,classNames:M,styles:T}=(0,c.useComponentConfig)("popover"),B=z("popover",p),[H,R,W]=$(B),D=z(),q=(0,n.default)(b,R,W,N,M.root,null==E?void 0:E.root),G=(0,n.default)(M.body,null==E?void 0:E.body),[L,A]=(0,r.default)(!1,{value:null!=(u=e.open)?u:e.visible,defaultValue:null!=(g=e.defaultOpen)?g:e.defaultVisible}),F=(e,t)=>{A(e,!0),null==O||O(e,t)},X=a(m),V=a(f);return H(t.createElement(s.default,Object.assign({placement:h,trigger:y,mouseEnterDelay:C,mouseLeaveDelay:w},j,{prefixCls:B,classNames:{root:q,body:G},styles:{root:Object.assign(Object.assign(Object.assign(Object.assign({},T.root),P),x),null==I?void 0:I.root),body:Object.assign(Object.assign({},T.body),null==I?void 0:I.body)},ref:d,open:L,onOpenChange:e=>{F(e)},overlay:X||V?t.createElement(v,{prefixCls:B,title:X,content:V}):null,transitionName:(0,l.getTransitionName)(D,"zoom-big",j.transitionName),"data-popover-inject":!0}),(0,o.cloneElement)(S,{onKeyDown:e=>{var n,r;(0,t.isValidElement)(S)&&(null==(r=null==S?void 0:(n=S.props).onKeyDown)||r.call(n,e)),e.keyCode===i.default.ESC&&F(!1,e)}})))});w._InternalPanelDoNotUseOrYouWillBeFired=C,e.s(["default",0,w],829672)},282786,e=>{"use strict";var t=e.i(829672);e.s(["Popover",()=>t.default])},771674,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"}}]},name:"user",theme:"outlined"};var i=e.i(9583),a=n.forwardRef(function(e,a){return n.createElement(i.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["UserOutlined",0,a],771674)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02-u6qtmsnqn0.js b/litellm/proxy/_experimental/out/_next/static/chunks/02-u6qtmsnqn0.js new file mode 100644 index 00000000000..e1a7a779038 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02-u6qtmsnqn0.js @@ -0,0 +1,4 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,152990,682830,e=>{"use strict";var t=e.i(271645);function l(e,t){return"function"==typeof e?e(t):e}function n(e,t){return n=>{t.setState(t=>({...t,[e]:l(n,t[e])}))}}function o(e){return e instanceof Function}function i(e,t,l){let n,o=[];return i=>{let r,a;l.key&&l.debug&&(r=Date.now());let u=e(i);if(!(u.length!==o.length||u.some((e,t)=>o[t]!==e)))return n;if(o=u,l.key&&l.debug&&(a=Date.now()),n=t(...u),null==l||null==l.onChange||l.onChange(n),l.key&&l.debug&&null!=l&&l.debug()){let e=Math.round((Date.now()-r)*100)/100,t=Math.round((Date.now()-a)*100)/100,n=t/16,o=(e,t)=>{for(e=String(e);e.length{var l;return null!=(l=null==e?void 0:e.debugAll)?l:e[t]},key:!1,onChange:n}}e.i(247167);let a="debugHeaders";function u(e,t,l){var n;let o={id:null!=(n=l.id)?n:t.id,column:t,index:l.index,isPlaceholder:!!l.isPlaceholder,placeholderId:l.placeholderId,depth:l.depth,subHeaders:[],colSpan:0,rowSpan:0,headerGroup:null,getLeafHeaders:()=>{let e=[],t=l=>{l.subHeaders&&l.subHeaders.length&&l.subHeaders.map(t),e.push(l)};return t(o),e},getContext:()=>({table:e,header:o,column:t})};return e._features.forEach(t=>{null==t.createHeader||t.createHeader(o,e)}),o}function g(e,t,l,n){var o,i;let r=0,a=function(e,t){void 0===t&&(t=1),r=Math.max(r,t),e.filter(e=>e.getIsVisible()).forEach(e=>{var l;null!=(l=e.columns)&&l.length&&a(e.columns,t+1)},0)};a(e);let g=[],s=(e,t)=>{let o={depth:t,id:[n,`${t}`].filter(Boolean).join("_"),headers:[]},i=[];e.forEach(e=>{let r,a=[...i].reverse()[0],g=e.column.depth===o.depth,s=!1;if(g&&e.column.parent?r=e.column.parent:(r=e.column,s=!0),a&&(null==a?void 0:a.column)===r)a.subHeaders.push(e);else{let o=u(l,r,{id:[n,t,r.id,null==e?void 0:e.id].filter(Boolean).join("_"),isPlaceholder:s,placeholderId:s?`${i.filter(e=>e.column===r).length}`:void 0,depth:t,index:i.length});o.subHeaders.push(e),i.push(o)}o.headers.push(e),e.headerGroup=o}),g.push(o),t>0&&s(i,t-1)};s(t.map((e,t)=>u(l,e,{depth:r,index:t})),r-1),g.reverse();let d=e=>e.filter(e=>e.column.getIsVisible()).map(e=>{let t=0,l=0,n=[0];return e.subHeaders&&e.subHeaders.length?(n=[],d(e.subHeaders).forEach(e=>{let{colSpan:l,rowSpan:o}=e;t+=l,n.push(o)})):t=1,l+=Math.min(...n),e.colSpan=t,e.rowSpan=l,{colSpan:t,rowSpan:l}});return d(null!=(o=null==(i=g[0])?void 0:i.headers)?o:[]),g}let s=(e,t,l,n,o,a,u)=>{let g={id:t,index:n,original:l,depth:o,parentId:u,_valuesCache:{},_uniqueValuesCache:{},getValue:t=>{if(g._valuesCache.hasOwnProperty(t))return g._valuesCache[t];let l=e.getColumn(t);if(null!=l&&l.accessorFn)return g._valuesCache[t]=l.accessorFn(g.original,n),g._valuesCache[t]},getUniqueValues:t=>{if(g._uniqueValuesCache.hasOwnProperty(t))return g._uniqueValuesCache[t];let l=e.getColumn(t);if(null!=l&&l.accessorFn)return l.columnDef.getUniqueValues?g._uniqueValuesCache[t]=l.columnDef.getUniqueValues(g.original,n):g._uniqueValuesCache[t]=[g.getValue(t)],g._uniqueValuesCache[t]},renderValue:t=>{var l;return null!=(l=g.getValue(t))?l:e.options.renderFallbackValue},subRows:null!=a?a:[],getLeafRows:()=>{var e,t;let l,n;return e=g.subRows,t=e=>e.subRows,l=[],(n=e=>{e.forEach(e=>{l.push(e);let o=t(e);null!=o&&o.length&&n(o)})})(e),l},getParentRow:()=>g.parentId?e.getRow(g.parentId,!0):void 0,getParentRows:()=>{let e=[],t=g;for(;;){let l=t.getParentRow();if(!l)break;e.push(l),t=l}return e.reverse()},getAllCells:i(()=>[e.getAllLeafColumns()],t=>t.map(t=>{var l;let n;return l=t.id,n={id:`${g.id}_${t.id}`,row:g,column:t,getValue:()=>g.getValue(l),renderValue:()=>{var t;return null!=(t=n.getValue())?t:e.options.renderFallbackValue},getContext:i(()=>[e,t,g,n],(e,t,l,n)=>({table:e,column:t,row:l,cell:n,getValue:n.getValue,renderValue:n.renderValue}),r(e.options,"debugCells","cell.getContext"))},e._features.forEach(l=>{null==l.createCell||l.createCell(n,t,g,e)},{}),n}),r(e.options,"debugRows","getAllCells")),_getAllCellsByColumnId:i(()=>[g.getAllCells()],e=>e.reduce((e,t)=>(e[t.column.id]=t,e),{}),r(e.options,"debugRows","getAllCellsByColumnId"))};for(let t=0;t{var n,o;let i=null==l||null==(n=l.toString())?void 0:n.toLowerCase();return!!(null==(o=e.getValue(t))||null==(o=o.toString())||null==(o=o.toLowerCase())?void 0:o.includes(i))};d.autoRemove=e=>S(e);let p=(e,t,l)=>{var n;return!!(null==(n=e.getValue(t))||null==(n=n.toString())?void 0:n.includes(l))};p.autoRemove=e=>S(e);let c=(e,t,l)=>{var n;return(null==(n=e.getValue(t))||null==(n=n.toString())?void 0:n.toLowerCase())===(null==l?void 0:l.toLowerCase())};c.autoRemove=e=>S(e);let f=(e,t,l)=>{var n;return null==(n=e.getValue(t))?void 0:n.includes(l)};f.autoRemove=e=>S(e);let m=(e,t,l)=>!l.some(l=>{var n;return!(null!=(n=e.getValue(t))&&n.includes(l))});m.autoRemove=e=>S(e)||!(null!=e&&e.length);let C=(e,t,l)=>l.some(l=>{var n;return null==(n=e.getValue(t))?void 0:n.includes(l)});C.autoRemove=e=>S(e)||!(null!=e&&e.length);let w=(e,t,l)=>e.getValue(t)===l;w.autoRemove=e=>S(e);let R=(e,t,l)=>e.getValue(t)==l;R.autoRemove=e=>S(e);let h=(e,t,l)=>{let[n,o]=l,i=e.getValue(t);return i>=n&&i<=o};h.resolveFilterValue=e=>{let[t,l]=e,n="number"!=typeof t?parseFloat(t):t,o="number"!=typeof l?parseFloat(l):l,i=null===t||Number.isNaN(n)?-1/0:n,r=null===l||Number.isNaN(o)?1/0:o;if(i>r){let e=i;i=r,r=e}return[i,r]},h.autoRemove=e=>S(e)||S(e[0])&&S(e[1]);let v={includesString:d,includesStringSensitive:p,equalsString:c,arrIncludes:f,arrIncludesAll:m,arrIncludesSome:C,equals:w,weakEquals:R,inNumberRange:h};function S(e){return null==e||""===e}function b(e,t,l){return!!e&&!!e.autoRemove&&e.autoRemove(t,l)||void 0===t||"string"==typeof t&&!t}let F={sum:(e,t,l)=>l.reduce((t,l)=>{let n=l.getValue(e);return t+("number"==typeof n?n:0)},0),min:(e,t,l)=>{let n;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(n>l||void 0===n&&l>=l)&&(n=l)}),n},max:(e,t,l)=>{let n;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(n=l)&&(n=l)}),n},extent:(e,t,l)=>{let n,o;return l.forEach(t=>{let l=t.getValue(e);null!=l&&(void 0===n?l>=l&&(n=o=l):(n>l&&(n=l),o{let l=0,n=0;if(t.forEach(t=>{let o=t.getValue(e);null!=o&&(o*=1)>=o&&(++l,n+=o)}),l)return n/l},median:(e,t)=>{if(!t.length)return;let l=t.map(t=>t.getValue(e));if(!(Array.isArray(l)&&l.every(e=>"number"==typeof e)))return;if(1===l.length)return l[0];let n=Math.floor(l.length/2),o=l.sort((e,t)=>e-t);return l.length%2!=0?o[n]:(o[n-1]+o[n])/2},unique:(e,t)=>Array.from(new Set(t.map(t=>t.getValue(e))).values()),uniqueCount:(e,t)=>new Set(t.map(t=>t.getValue(e))).size,count:(e,t)=>t.length},M=()=>({left:[],right:[]}),V={size:150,minSize:20,maxSize:Number.MAX_SAFE_INTEGER},P=()=>({startOffset:null,startSize:null,deltaOffset:null,deltaPercentage:null,isResizingColumn:!1,columnSizingStart:[]}),I=null;function x(e){return"touchstart"===e.type}function _(e,t){return t?"center"===t?e.getCenterVisibleLeafColumns():"left"===t?e.getLeftVisibleLeafColumns():e.getRightVisibleLeafColumns():e.getVisibleLeafColumns()}let y=()=>({pageIndex:0,pageSize:10}),E=()=>({top:[],bottom:[]}),G=(e,t,l,n,o)=>{var i;let r=o.getRow(t,!0);l?(r.getCanMultiSelect()||Object.keys(e).forEach(t=>delete e[t]),r.getCanSelect()&&(e[t]=!0)):delete e[t],n&&null!=(i=r.subRows)&&i.length&&r.getCanSelectSubRows()&&r.subRows.forEach(t=>G(e,t.id,l,n,o))};function L(e,t){let l=e.getState().rowSelection,n=[],o={},i=function(e,t){return e.map(e=>{var t;let r=A(e,l);if(r&&(n.push(e),o[e.id]=e),null!=(t=e.subRows)&&t.length&&(e={...e,subRows:i(e.subRows)}),r)return e}).filter(Boolean)};return{rows:i(t.rows),flatRows:n,rowsById:o}}function A(e,t){var l;return null!=(l=t[e.id])&&l}function H(e,t,l){var n;if(!(null!=(n=e.subRows)&&n.length))return!1;let o=!0,i=!1;return e.subRows.forEach(e=>{if((!i||o)&&(e.getCanSelect()&&(A(e,t)?i=!0:o=!1),e.subRows&&e.subRows.length)){let l=H(e,t);"all"===l?i=!0:("some"===l&&(i=!0),o=!1)}}),o?"all":!!i&&"some"}let D=/([0-9]+)/gm;function z(e,t){return e===t?0:e>t?1:-1}function O(e){return"number"==typeof e?isNaN(e)||e===1/0||e===-1/0?"":String(e):"string"==typeof e?e:""}function T(e,t){let l=e.split(D).filter(Boolean),n=t.split(D).filter(Boolean);for(;l.length&&n.length;){let e=l.shift(),t=n.shift(),o=parseInt(e,10),i=parseInt(t,10),r=[o,i].sort();if(isNaN(r[0])){if(e>t)return 1;if(t>e)return -1;continue}if(isNaN(r[1]))return isNaN(o)?-1:1;if(o>i)return 1;if(i>o)return -1}return l.length-n.length}let B={alphanumeric:(e,t,l)=>T(O(e.getValue(l)).toLowerCase(),O(t.getValue(l)).toLowerCase()),alphanumericCaseSensitive:(e,t,l)=>T(O(e.getValue(l)),O(t.getValue(l))),text:(e,t,l)=>z(O(e.getValue(l)).toLowerCase(),O(t.getValue(l)).toLowerCase()),textCaseSensitive:(e,t,l)=>z(O(e.getValue(l)),O(t.getValue(l))),datetime:(e,t,l)=>{let n=e.getValue(l),o=t.getValue(l);return n>o?1:nz(e.getValue(l),t.getValue(l))},q=[{createTable:e=>{e.getHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(t,l,n,o)=>{var i,r;let a=null!=(i=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?i:[],u=null!=(r=null==o?void 0:o.map(e=>l.find(t=>t.id===e)).filter(Boolean))?r:[];return g(t,[...a,...l.filter(e=>!(null!=n&&n.includes(e.id))&&!(null!=o&&o.includes(e.id))),...u],e)},r(e.options,a,"getHeaderGroups")),e.getCenterHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(t,l,n,o)=>g(t,l=l.filter(e=>!(null!=n&&n.includes(e.id))&&!(null!=o&&o.includes(e.id))),e,"center"),r(e.options,a,"getCenterHeaderGroups")),e.getLeftHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.left],(t,l,n)=>{var o;return g(t,null!=(o=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?o:[],e,"left")},r(e.options,a,"getLeftHeaderGroups")),e.getRightHeaderGroups=i(()=>[e.getAllColumns(),e.getVisibleLeafColumns(),e.getState().columnPinning.right],(t,l,n)=>{var o;return g(t,null!=(o=null==n?void 0:n.map(e=>l.find(t=>t.id===e)).filter(Boolean))?o:[],e,"right")},r(e.options,a,"getRightHeaderGroups")),e.getFooterGroups=i(()=>[e.getHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getFooterGroups")),e.getLeftFooterGroups=i(()=>[e.getLeftHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getLeftFooterGroups")),e.getCenterFooterGroups=i(()=>[e.getCenterHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getCenterFooterGroups")),e.getRightFooterGroups=i(()=>[e.getRightHeaderGroups()],e=>[...e].reverse(),r(e.options,a,"getRightFooterGroups")),e.getFlatHeaders=i(()=>[e.getHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getFlatHeaders")),e.getLeftFlatHeaders=i(()=>[e.getLeftHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getLeftFlatHeaders")),e.getCenterFlatHeaders=i(()=>[e.getCenterHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getCenterFlatHeaders")),e.getRightFlatHeaders=i(()=>[e.getRightHeaderGroups()],e=>e.map(e=>e.headers).flat(),r(e.options,a,"getRightFlatHeaders")),e.getCenterLeafHeaders=i(()=>[e.getCenterFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getCenterLeafHeaders")),e.getLeftLeafHeaders=i(()=>[e.getLeftFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getLeftLeafHeaders")),e.getRightLeafHeaders=i(()=>[e.getRightFlatHeaders()],e=>e.filter(e=>{var t;return!(null!=(t=e.subHeaders)&&t.length)}),r(e.options,a,"getRightLeafHeaders")),e.getLeafHeaders=i(()=>[e.getLeftHeaderGroups(),e.getCenterHeaderGroups(),e.getRightHeaderGroups()],(e,t,l)=>{var n,o,i,r,a,u;return[...null!=(n=null==(o=e[0])?void 0:o.headers)?n:[],...null!=(i=null==(r=t[0])?void 0:r.headers)?i:[],...null!=(a=null==(u=l[0])?void 0:u.headers)?a:[]].map(e=>e.getLeafHeaders()).flat()},r(e.options,a,"getLeafHeaders"))}},{getInitialState:e=>({columnVisibility:{},...e}),getDefaultOptions:e=>({onColumnVisibilityChange:n("columnVisibility",e)}),createColumn:(e,t)=>{e.toggleVisibility=l=>{e.getCanHide()&&t.setColumnVisibility(t=>({...t,[e.id]:null!=l?l:!e.getIsVisible()}))},e.getIsVisible=()=>{var l,n;let o=e.columns;return null==(l=o.length?o.some(e=>e.getIsVisible()):null==(n=t.getState().columnVisibility)?void 0:n[e.id])||l},e.getCanHide=()=>{var l,n;return(null==(l=e.columnDef.enableHiding)||l)&&(null==(n=t.options.enableHiding)||n)},e.getToggleVisibilityHandler=()=>t=>{null==e.toggleVisibility||e.toggleVisibility(t.target.checked)}},createRow:(e,t)=>{e._getAllVisibleCells=i(()=>[e.getAllCells(),t.getState().columnVisibility],e=>e.filter(e=>e.column.getIsVisible()),r(t.options,"debugRows","_getAllVisibleCells")),e.getVisibleCells=i(()=>[e.getLeftVisibleCells(),e.getCenterVisibleCells(),e.getRightVisibleCells()],(e,t,l)=>[...e,...t,...l],r(t.options,"debugRows","getVisibleCells"))},createTable:e=>{let t=(t,l)=>i(()=>[l(),l().filter(e=>e.getIsVisible()).map(e=>e.id).join("_")],e=>e.filter(e=>null==e.getIsVisible?void 0:e.getIsVisible()),r(e.options,"debugColumns",t));e.getVisibleFlatColumns=t("getVisibleFlatColumns",()=>e.getAllFlatColumns()),e.getVisibleLeafColumns=t("getVisibleLeafColumns",()=>e.getAllLeafColumns()),e.getLeftVisibleLeafColumns=t("getLeftVisibleLeafColumns",()=>e.getLeftLeafColumns()),e.getRightVisibleLeafColumns=t("getRightVisibleLeafColumns",()=>e.getRightLeafColumns()),e.getCenterVisibleLeafColumns=t("getCenterVisibleLeafColumns",()=>e.getCenterLeafColumns()),e.setColumnVisibility=t=>null==e.options.onColumnVisibilityChange?void 0:e.options.onColumnVisibilityChange(t),e.resetColumnVisibility=t=>{var l;e.setColumnVisibility(t?{}:null!=(l=e.initialState.columnVisibility)?l:{})},e.toggleAllColumnsVisible=t=>{var l;t=null!=(l=t)?l:!e.getIsAllColumnsVisible(),e.setColumnVisibility(e.getAllLeafColumns().reduce((e,l)=>({...e,[l.id]:t||!(null!=l.getCanHide&&l.getCanHide())}),{}))},e.getIsAllColumnsVisible=()=>!e.getAllLeafColumns().some(e=>!(null!=e.getIsVisible&&e.getIsVisible())),e.getIsSomeColumnsVisible=()=>e.getAllLeafColumns().some(e=>null==e.getIsVisible?void 0:e.getIsVisible()),e.getToggleAllColumnsVisibilityHandler=()=>t=>{var l;e.toggleAllColumnsVisible(null==(l=t.target)?void 0:l.checked)}}},{getInitialState:e=>({columnOrder:[],...e}),getDefaultOptions:e=>({onColumnOrderChange:n("columnOrder",e)}),createColumn:(e,t)=>{e.getIndex=i(e=>[_(t,e)],t=>t.findIndex(t=>t.id===e.id),r(t.options,"debugColumns","getIndex")),e.getIsFirstColumn=l=>{var n;return(null==(n=_(t,l)[0])?void 0:n.id)===e.id},e.getIsLastColumn=l=>{var n;let o=_(t,l);return(null==(n=o[o.length-1])?void 0:n.id)===e.id}},createTable:e=>{e.setColumnOrder=t=>null==e.options.onColumnOrderChange?void 0:e.options.onColumnOrderChange(t),e.resetColumnOrder=t=>{var l;e.setColumnOrder(t?[]:null!=(l=e.initialState.columnOrder)?l:[])},e._getOrderColumnsFn=i(()=>[e.getState().columnOrder,e.getState().grouping,e.options.groupedColumnMode],(e,t,l)=>n=>{let o=[];if(null!=e&&e.length){let t=[...e],l=[...n];for(;l.length&&t.length;){let e=t.shift(),n=l.findIndex(t=>t.id===e);n>-1&&o.push(l.splice(n,1)[0])}o=[...o,...l]}else o=n;var i=o;if(!(null!=t&&t.length)||!l)return i;let r=i.filter(e=>!t.includes(e.id));return"remove"===l?r:[...t.map(e=>i.find(t=>t.id===e)).filter(Boolean),...r]},r(e.options,"debugTable","_getOrderColumnsFn"))}},{getInitialState:e=>({columnPinning:M(),...e}),getDefaultOptions:e=>({onColumnPinningChange:n("columnPinning",e)}),createColumn:(e,t)=>{e.pin=l=>{let n=e.getLeafColumns().map(e=>e.id).filter(Boolean);t.setColumnPinning(e=>{var t,o,i,r,a,u;return"right"===l?{left:(null!=(i=null==e?void 0:e.left)?i:[]).filter(e=>!(null!=n&&n.includes(e))),right:[...(null!=(r=null==e?void 0:e.right)?r:[]).filter(e=>!(null!=n&&n.includes(e))),...n]}:"left"===l?{left:[...(null!=(a=null==e?void 0:e.left)?a:[]).filter(e=>!(null!=n&&n.includes(e))),...n],right:(null!=(u=null==e?void 0:e.right)?u:[]).filter(e=>!(null!=n&&n.includes(e)))}:{left:(null!=(t=null==e?void 0:e.left)?t:[]).filter(e=>!(null!=n&&n.includes(e))),right:(null!=(o=null==e?void 0:e.right)?o:[]).filter(e=>!(null!=n&&n.includes(e)))}})},e.getCanPin=()=>e.getLeafColumns().some(e=>{var l,n,o;return(null==(l=e.columnDef.enablePinning)||l)&&(null==(n=null!=(o=t.options.enableColumnPinning)?o:t.options.enablePinning)||n)}),e.getIsPinned=()=>{let l=e.getLeafColumns().map(e=>e.id),{left:n,right:o}=t.getState().columnPinning,i=l.some(e=>null==n?void 0:n.includes(e)),r=l.some(e=>null==o?void 0:o.includes(e));return i?"left":!!r&&"right"},e.getPinnedIndex=()=>{var l,n;let o=e.getIsPinned();return o?null!=(l=null==(n=t.getState().columnPinning)||null==(n=n[o])?void 0:n.indexOf(e.id))?l:-1:0}},createRow:(e,t)=>{e.getCenterVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.left,t.getState().columnPinning.right],(e,t,l)=>{let n=[...null!=t?t:[],...null!=l?l:[]];return e.filter(e=>!n.includes(e.column.id))},r(t.options,"debugRows","getCenterVisibleCells")),e.getLeftVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.left],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.column.id===t)).filter(Boolean).map(e=>({...e,position:"left"})),r(t.options,"debugRows","getLeftVisibleCells")),e.getRightVisibleCells=i(()=>[e._getAllVisibleCells(),t.getState().columnPinning.right],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.column.id===t)).filter(Boolean).map(e=>({...e,position:"right"})),r(t.options,"debugRows","getRightVisibleCells"))},createTable:e=>{e.setColumnPinning=t=>null==e.options.onColumnPinningChange?void 0:e.options.onColumnPinningChange(t),e.resetColumnPinning=t=>{var l,n;return e.setColumnPinning(t?M():null!=(l=null==(n=e.initialState)?void 0:n.columnPinning)?l:M())},e.getIsSomeColumnsPinned=t=>{var l,n,o;let i=e.getState().columnPinning;return t?!!(null==(l=i[t])?void 0:l.length):!!((null==(n=i.left)?void 0:n.length)||(null==(o=i.right)?void 0:o.length))},e.getLeftLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.left],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.id===t)).filter(Boolean),r(e.options,"debugColumns","getLeftLeafColumns")),e.getRightLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.right],(e,t)=>(null!=t?t:[]).map(t=>e.find(e=>e.id===t)).filter(Boolean),r(e.options,"debugColumns","getRightLeafColumns")),e.getCenterLeafColumns=i(()=>[e.getAllLeafColumns(),e.getState().columnPinning.left,e.getState().columnPinning.right],(e,t,l)=>{let n=[...null!=t?t:[],...null!=l?l:[]];return e.filter(e=>!n.includes(e.id))},r(e.options,"debugColumns","getCenterLeafColumns"))}},{createColumn:(e,t)=>{e._getFacetedRowModel=t.options.getFacetedRowModel&&t.options.getFacetedRowModel(t,e.id),e.getFacetedRowModel=()=>e._getFacetedRowModel?e._getFacetedRowModel():t.getPreFilteredRowModel(),e._getFacetedUniqueValues=t.options.getFacetedUniqueValues&&t.options.getFacetedUniqueValues(t,e.id),e.getFacetedUniqueValues=()=>e._getFacetedUniqueValues?e._getFacetedUniqueValues():new Map,e._getFacetedMinMaxValues=t.options.getFacetedMinMaxValues&&t.options.getFacetedMinMaxValues(t,e.id),e.getFacetedMinMaxValues=()=>{if(e._getFacetedMinMaxValues)return e._getFacetedMinMaxValues()}}},{getDefaultColumnDef:()=>({filterFn:"auto"}),getInitialState:e=>({columnFilters:[],...e}),getDefaultOptions:e=>({onColumnFiltersChange:n("columnFilters",e),filterFromLeafRows:!1,maxLeafRowFilterDepth:100}),createColumn:(e,t)=>{e.getAutoFilterFn=()=>{let l=t.getCoreRowModel().flatRows[0],n=null==l?void 0:l.getValue(e.id);return"string"==typeof n?v.includesString:"number"==typeof n?v.inNumberRange:"boolean"==typeof n||null!==n&&"object"==typeof n?v.equals:Array.isArray(n)?v.arrIncludes:v.weakEquals},e.getFilterFn=()=>{var l,n;return o(e.columnDef.filterFn)?e.columnDef.filterFn:"auto"===e.columnDef.filterFn?e.getAutoFilterFn():null!=(l=null==(n=t.options.filterFns)?void 0:n[e.columnDef.filterFn])?l:v[e.columnDef.filterFn]},e.getCanFilter=()=>{var l,n,o;return(null==(l=e.columnDef.enableColumnFilter)||l)&&(null==(n=t.options.enableColumnFilters)||n)&&(null==(o=t.options.enableFilters)||o)&&!!e.accessorFn},e.getIsFiltered=()=>e.getFilterIndex()>-1,e.getFilterValue=()=>{var l;return null==(l=t.getState().columnFilters)||null==(l=l.find(t=>t.id===e.id))?void 0:l.value},e.getFilterIndex=()=>{var l,n;return null!=(l=null==(n=t.getState().columnFilters)?void 0:n.findIndex(t=>t.id===e.id))?l:-1},e.setFilterValue=n=>{t.setColumnFilters(t=>{var o,i;let r=e.getFilterFn(),a=null==t?void 0:t.find(t=>t.id===e.id),u=l(n,a?a.value:void 0);if(b(r,u,e))return null!=(o=null==t?void 0:t.filter(t=>t.id!==e.id))?o:[];let g={id:e.id,value:u};return a?null!=(i=null==t?void 0:t.map(t=>t.id===e.id?g:t))?i:[]:null!=t&&t.length?[...t,g]:[g]})}},createRow:(e,t)=>{e.columnFilters={},e.columnFiltersMeta={}},createTable:e=>{e.setColumnFilters=t=>{let n=e.getAllLeafColumns();null==e.options.onColumnFiltersChange||e.options.onColumnFiltersChange(e=>{var o;return null==(o=l(t,e))?void 0:o.filter(e=>{let t=n.find(t=>t.id===e.id);return!(t&&b(t.getFilterFn(),e.value,t))&&!0})})},e.resetColumnFilters=t=>{var l,n;e.setColumnFilters(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.columnFilters)?l:[])},e.getPreFilteredRowModel=()=>e.getCoreRowModel(),e.getFilteredRowModel=()=>(!e._getFilteredRowModel&&e.options.getFilteredRowModel&&(e._getFilteredRowModel=e.options.getFilteredRowModel(e)),e.options.manualFiltering||!e._getFilteredRowModel)?e.getPreFilteredRowModel():e._getFilteredRowModel()}},{createTable:e=>{e._getGlobalFacetedRowModel=e.options.getFacetedRowModel&&e.options.getFacetedRowModel(e,"__global__"),e.getGlobalFacetedRowModel=()=>e.options.manualFiltering||!e._getGlobalFacetedRowModel?e.getPreFilteredRowModel():e._getGlobalFacetedRowModel(),e._getGlobalFacetedUniqueValues=e.options.getFacetedUniqueValues&&e.options.getFacetedUniqueValues(e,"__global__"),e.getGlobalFacetedUniqueValues=()=>e._getGlobalFacetedUniqueValues?e._getGlobalFacetedUniqueValues():new Map,e._getGlobalFacetedMinMaxValues=e.options.getFacetedMinMaxValues&&e.options.getFacetedMinMaxValues(e,"__global__"),e.getGlobalFacetedMinMaxValues=()=>{if(e._getGlobalFacetedMinMaxValues)return e._getGlobalFacetedMinMaxValues()}}},{getInitialState:e=>({globalFilter:void 0,...e}),getDefaultOptions:e=>({onGlobalFilterChange:n("globalFilter",e),globalFilterFn:"auto",getColumnCanGlobalFilter:t=>{var l;let n=null==(l=e.getCoreRowModel().flatRows[0])||null==(l=l._getAllCellsByColumnId()[t.id])?void 0:l.getValue();return"string"==typeof n||"number"==typeof n}}),createColumn:(e,t)=>{e.getCanGlobalFilter=()=>{var l,n,o,i;return(null==(l=e.columnDef.enableGlobalFilter)||l)&&(null==(n=t.options.enableGlobalFilter)||n)&&(null==(o=t.options.enableFilters)||o)&&(null==(i=null==t.options.getColumnCanGlobalFilter?void 0:t.options.getColumnCanGlobalFilter(e))||i)&&!!e.accessorFn}},createTable:e=>{e.getGlobalAutoFilterFn=()=>v.includesString,e.getGlobalFilterFn=()=>{var t,l;let{globalFilterFn:n}=e.options;return o(n)?n:"auto"===n?e.getGlobalAutoFilterFn():null!=(t=null==(l=e.options.filterFns)?void 0:l[n])?t:v[n]},e.setGlobalFilter=t=>{null==e.options.onGlobalFilterChange||e.options.onGlobalFilterChange(t)},e.resetGlobalFilter=t=>{e.setGlobalFilter(t?void 0:e.initialState.globalFilter)}}},{getInitialState:e=>({sorting:[],...e}),getDefaultColumnDef:()=>({sortingFn:"auto",sortUndefined:1}),getDefaultOptions:e=>({onSortingChange:n("sorting",e),isMultiSortEvent:e=>e.shiftKey}),createColumn:(e,t)=>{e.getAutoSortingFn=()=>{let l=t.getFilteredRowModel().flatRows.slice(10),n=!1;for(let t of l){let l=null==t?void 0:t.getValue(e.id);if("[object Date]"===Object.prototype.toString.call(l))return B.datetime;if("string"==typeof l&&(n=!0,l.split(D).length>1))return B.alphanumeric}return n?B.text:B.basic},e.getAutoSortDir=()=>{let l=t.getFilteredRowModel().flatRows[0];return"string"==typeof(null==l?void 0:l.getValue(e.id))?"asc":"desc"},e.getSortingFn=()=>{var l,n;if(!e)throw Error();return o(e.columnDef.sortingFn)?e.columnDef.sortingFn:"auto"===e.columnDef.sortingFn?e.getAutoSortingFn():null!=(l=null==(n=t.options.sortingFns)?void 0:n[e.columnDef.sortingFn])?l:B[e.columnDef.sortingFn]},e.toggleSorting=(l,n)=>{let o=e.getNextSortingOrder(),i=null!=l;t.setSorting(r=>{let a,u=null==r?void 0:r.find(t=>t.id===e.id),g=null==r?void 0:r.findIndex(t=>t.id===e.id),s=[],d=i?l:"desc"===o;if("toggle"!=(a=null!=r&&r.length&&e.getCanMultiSort()&&n?u?"toggle":"add":null!=r&&r.length&&g!==r.length-1?"replace":u?"toggle":"replace")||i||o||(a="remove"),"add"===a){var p;(s=[...r,{id:e.id,desc:d}]).splice(0,s.length-(null!=(p=t.options.maxMultiSortColCount)?p:Number.MAX_SAFE_INTEGER))}else s="toggle"===a?r.map(t=>t.id===e.id?{...t,desc:d}:t):"remove"===a?r.filter(t=>t.id!==e.id):[{id:e.id,desc:d}];return s})},e.getFirstSortDir=()=>{var l,n;return(null!=(l=null!=(n=e.columnDef.sortDescFirst)?n:t.options.sortDescFirst)?l:"desc"===e.getAutoSortDir())?"desc":"asc"},e.getNextSortingOrder=l=>{var n,o;let i=e.getFirstSortDir(),r=e.getIsSorted();return r?(r===i||null!=(n=t.options.enableSortingRemoval)&&!n||!!l&&null!=(o=t.options.enableMultiRemove)&&!o)&&("desc"===r?"asc":"desc"):i},e.getCanSort=()=>{var l,n;return(null==(l=e.columnDef.enableSorting)||l)&&(null==(n=t.options.enableSorting)||n)&&!!e.accessorFn},e.getCanMultiSort=()=>{var l,n;return null!=(l=null!=(n=e.columnDef.enableMultiSort)?n:t.options.enableMultiSort)?l:!!e.accessorFn},e.getIsSorted=()=>{var l;let n=null==(l=t.getState().sorting)?void 0:l.find(t=>t.id===e.id);return!!n&&(n.desc?"desc":"asc")},e.getSortIndex=()=>{var l,n;return null!=(l=null==(n=t.getState().sorting)?void 0:n.findIndex(t=>t.id===e.id))?l:-1},e.clearSorting=()=>{t.setSorting(t=>null!=t&&t.length?t.filter(t=>t.id!==e.id):[])},e.getToggleSortingHandler=()=>{let l=e.getCanSort();return n=>{l&&(null==n.persist||n.persist(),null==e.toggleSorting||e.toggleSorting(void 0,!!e.getCanMultiSort()&&(null==t.options.isMultiSortEvent?void 0:t.options.isMultiSortEvent(n))))}}},createTable:e=>{e.setSorting=t=>null==e.options.onSortingChange?void 0:e.options.onSortingChange(t),e.resetSorting=t=>{var l,n;e.setSorting(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.sorting)?l:[])},e.getPreSortedRowModel=()=>e.getGroupedRowModel(),e.getSortedRowModel=()=>(!e._getSortedRowModel&&e.options.getSortedRowModel&&(e._getSortedRowModel=e.options.getSortedRowModel(e)),e.options.manualSorting||!e._getSortedRowModel)?e.getPreSortedRowModel():e._getSortedRowModel()}},{getDefaultColumnDef:()=>({aggregatedCell:e=>{var t,l;return null!=(t=null==(l=e.getValue())||null==l.toString?void 0:l.toString())?t:null},aggregationFn:"auto"}),getInitialState:e=>({grouping:[],...e}),getDefaultOptions:e=>({onGroupingChange:n("grouping",e),groupedColumnMode:"reorder"}),createColumn:(e,t)=>{e.toggleGrouping=()=>{t.setGrouping(t=>null!=t&&t.includes(e.id)?t.filter(t=>t!==e.id):[...null!=t?t:[],e.id])},e.getCanGroup=()=>{var l,n;return(null==(l=e.columnDef.enableGrouping)||l)&&(null==(n=t.options.enableGrouping)||n)&&(!!e.accessorFn||!!e.columnDef.getGroupingValue)},e.getIsGrouped=()=>{var l;return null==(l=t.getState().grouping)?void 0:l.includes(e.id)},e.getGroupedIndex=()=>{var l;return null==(l=t.getState().grouping)?void 0:l.indexOf(e.id)},e.getToggleGroupingHandler=()=>{let t=e.getCanGroup();return()=>{t&&e.toggleGrouping()}},e.getAutoAggregationFn=()=>{let l=t.getCoreRowModel().flatRows[0],n=null==l?void 0:l.getValue(e.id);return"number"==typeof n?F.sum:"[object Date]"===Object.prototype.toString.call(n)?F.extent:void 0},e.getAggregationFn=()=>{var l,n;if(!e)throw Error();return o(e.columnDef.aggregationFn)?e.columnDef.aggregationFn:"auto"===e.columnDef.aggregationFn?e.getAutoAggregationFn():null!=(l=null==(n=t.options.aggregationFns)?void 0:n[e.columnDef.aggregationFn])?l:F[e.columnDef.aggregationFn]}},createTable:e=>{e.setGrouping=t=>null==e.options.onGroupingChange?void 0:e.options.onGroupingChange(t),e.resetGrouping=t=>{var l,n;e.setGrouping(t?[]:null!=(l=null==(n=e.initialState)?void 0:n.grouping)?l:[])},e.getPreGroupedRowModel=()=>e.getFilteredRowModel(),e.getGroupedRowModel=()=>(!e._getGroupedRowModel&&e.options.getGroupedRowModel&&(e._getGroupedRowModel=e.options.getGroupedRowModel(e)),e.options.manualGrouping||!e._getGroupedRowModel)?e.getPreGroupedRowModel():e._getGroupedRowModel()},createRow:(e,t)=>{e.getIsGrouped=()=>!!e.groupingColumnId,e.getGroupingValue=l=>{if(e._groupingValuesCache.hasOwnProperty(l))return e._groupingValuesCache[l];let n=t.getColumn(l);return null!=n&&n.columnDef.getGroupingValue?(e._groupingValuesCache[l]=n.columnDef.getGroupingValue(e.original),e._groupingValuesCache[l]):e.getValue(l)},e._groupingValuesCache={}},createCell:(e,t,l,n)=>{e.getIsGrouped=()=>t.getIsGrouped()&&t.id===l.groupingColumnId,e.getIsPlaceholder=()=>!e.getIsGrouped()&&t.getIsGrouped(),e.getIsAggregated=()=>{var t;return!e.getIsGrouped()&&!e.getIsPlaceholder()&&!!(null!=(t=l.subRows)&&t.length)}}},{getInitialState:e=>({expanded:{},...e}),getDefaultOptions:e=>({onExpandedChange:n("expanded",e),paginateExpandedRows:!0}),createTable:e=>{let t=!1,l=!1;e._autoResetExpanded=()=>{var n,o;if(!t)return void e._queue(()=>{t=!0});if(null!=(n=null!=(o=e.options.autoResetAll)?o:e.options.autoResetExpanded)?n:!e.options.manualExpanding){if(l)return;l=!0,e._queue(()=>{e.resetExpanded(),l=!1})}},e.setExpanded=t=>null==e.options.onExpandedChange?void 0:e.options.onExpandedChange(t),e.toggleAllRowsExpanded=t=>{(null!=t?t:!e.getIsAllRowsExpanded())?e.setExpanded(!0):e.setExpanded({})},e.resetExpanded=t=>{var l,n;e.setExpanded(t?{}:null!=(l=null==(n=e.initialState)?void 0:n.expanded)?l:{})},e.getCanSomeRowsExpand=()=>e.getPrePaginationRowModel().flatRows.some(e=>e.getCanExpand()),e.getToggleAllRowsExpandedHandler=()=>t=>{null==t.persist||t.persist(),e.toggleAllRowsExpanded()},e.getIsSomeRowsExpanded=()=>{let t=e.getState().expanded;return!0===t||Object.values(t).some(Boolean)},e.getIsAllRowsExpanded=()=>{let t=e.getState().expanded;return"boolean"==typeof t?!0===t:!(!Object.keys(t).length||e.getRowModel().flatRows.some(e=>!e.getIsExpanded()))},e.getExpandedDepth=()=>{let t=0;return(!0===e.getState().expanded?Object.keys(e.getRowModel().rowsById):Object.keys(e.getState().expanded)).forEach(e=>{let l=e.split(".");t=Math.max(t,l.length)}),t},e.getPreExpandedRowModel=()=>e.getSortedRowModel(),e.getExpandedRowModel=()=>(!e._getExpandedRowModel&&e.options.getExpandedRowModel&&(e._getExpandedRowModel=e.options.getExpandedRowModel(e)),e.options.manualExpanding||!e._getExpandedRowModel)?e.getPreExpandedRowModel():e._getExpandedRowModel()},createRow:(e,t)=>{e.toggleExpanded=l=>{t.setExpanded(n=>{var o;let i=!0===n||!!(null!=n&&n[e.id]),r={};if(!0===n?Object.keys(t.getRowModel().rowsById).forEach(e=>{r[e]=!0}):r=n,l=null!=(o=l)?o:!i,!i&&l)return{...r,[e.id]:!0};if(i&&!l){let{[e.id]:t,...l}=r;return l}return n})},e.getIsExpanded=()=>{var l;let n=t.getState().expanded;return!!(null!=(l=null==t.options.getIsRowExpanded?void 0:t.options.getIsRowExpanded(e))?l:!0===n||(null==n?void 0:n[e.id]))},e.getCanExpand=()=>{var l,n,o;return null!=(l=null==t.options.getRowCanExpand?void 0:t.options.getRowCanExpand(e))?l:(null==(n=t.options.enableExpanding)||n)&&!!(null!=(o=e.subRows)&&o.length)},e.getIsAllParentsExpanded=()=>{let l=!0,n=e;for(;l&&n.parentId;)l=(n=t.getRow(n.parentId,!0)).getIsExpanded();return l},e.getToggleExpandedHandler=()=>{let t=e.getCanExpand();return()=>{t&&e.toggleExpanded()}}}},{getInitialState:e=>({...e,pagination:{...y(),...null==e?void 0:e.pagination}}),getDefaultOptions:e=>({onPaginationChange:n("pagination",e)}),createTable:e=>{let t=!1,n=!1;e._autoResetPageIndex=()=>{var l,o;if(!t)return void e._queue(()=>{t=!0});if(null!=(l=null!=(o=e.options.autoResetAll)?o:e.options.autoResetPageIndex)?l:!e.options.manualPagination){if(n)return;n=!0,e._queue(()=>{e.resetPageIndex(),n=!1})}},e.setPagination=t=>null==e.options.onPaginationChange?void 0:e.options.onPaginationChange(e=>l(t,e)),e.resetPagination=t=>{var l;e.setPagination(t?y():null!=(l=e.initialState.pagination)?l:y())},e.setPageIndex=t=>{e.setPagination(n=>{let o=l(t,n.pageIndex);return o=Math.max(0,Math.min(o,void 0===e.options.pageCount||-1===e.options.pageCount?Number.MAX_SAFE_INTEGER:e.options.pageCount-1)),{...n,pageIndex:o}})},e.resetPageIndex=t=>{var l,n;e.setPageIndex(t?0:null!=(l=null==(n=e.initialState)||null==(n=n.pagination)?void 0:n.pageIndex)?l:0)},e.resetPageSize=t=>{var l,n;e.setPageSize(t?10:null!=(l=null==(n=e.initialState)||null==(n=n.pagination)?void 0:n.pageSize)?l:10)},e.setPageSize=t=>{e.setPagination(e=>{let n=Math.max(1,l(t,e.pageSize)),o=Math.floor(e.pageSize*e.pageIndex/n);return{...e,pageIndex:o,pageSize:n}})},e.setPageCount=t=>e.setPagination(n=>{var o;let i=l(t,null!=(o=e.options.pageCount)?o:-1);return"number"==typeof i&&(i=Math.max(-1,i)),{...n,pageCount:i}}),e.getPageOptions=i(()=>[e.getPageCount()],e=>{let t=[];return e&&e>0&&(t=[...Array(e)].fill(null).map((e,t)=>t)),t},r(e.options,"debugTable","getPageOptions")),e.getCanPreviousPage=()=>e.getState().pagination.pageIndex>0,e.getCanNextPage=()=>{let{pageIndex:t}=e.getState().pagination,l=e.getPageCount();return -1===l||0!==l&&te.setPageIndex(e=>e-1),e.nextPage=()=>e.setPageIndex(e=>e+1),e.firstPage=()=>e.setPageIndex(0),e.lastPage=()=>e.setPageIndex(e.getPageCount()-1),e.getPrePaginationRowModel=()=>e.getExpandedRowModel(),e.getPaginationRowModel=()=>(!e._getPaginationRowModel&&e.options.getPaginationRowModel&&(e._getPaginationRowModel=e.options.getPaginationRowModel(e)),e.options.manualPagination||!e._getPaginationRowModel)?e.getPrePaginationRowModel():e._getPaginationRowModel(),e.getPageCount=()=>{var t;return null!=(t=e.options.pageCount)?t:Math.ceil(e.getRowCount()/e.getState().pagination.pageSize)},e.getRowCount=()=>{var t;return null!=(t=e.options.rowCount)?t:e.getPrePaginationRowModel().rows.length}}},{getInitialState:e=>({rowPinning:E(),...e}),getDefaultOptions:e=>({onRowPinningChange:n("rowPinning",e)}),createRow:(e,t)=>{e.pin=(l,n,o)=>{let i=n?e.getLeafRows().map(e=>{let{id:t}=e;return t}):[],r=new Set([...o?e.getParentRows().map(e=>{let{id:t}=e;return t}):[],e.id,...i]);t.setRowPinning(e=>{var t,n,o,i,a,u;return"bottom"===l?{top:(null!=(o=null==e?void 0:e.top)?o:[]).filter(e=>!(null!=r&&r.has(e))),bottom:[...(null!=(i=null==e?void 0:e.bottom)?i:[]).filter(e=>!(null!=r&&r.has(e))),...Array.from(r)]}:"top"===l?{top:[...(null!=(a=null==e?void 0:e.top)?a:[]).filter(e=>!(null!=r&&r.has(e))),...Array.from(r)],bottom:(null!=(u=null==e?void 0:e.bottom)?u:[]).filter(e=>!(null!=r&&r.has(e)))}:{top:(null!=(t=null==e?void 0:e.top)?t:[]).filter(e=>!(null!=r&&r.has(e))),bottom:(null!=(n=null==e?void 0:e.bottom)?n:[]).filter(e=>!(null!=r&&r.has(e)))}})},e.getCanPin=()=>{var l;let{enableRowPinning:n,enablePinning:o}=t.options;return"function"==typeof n?n(e):null==(l=null!=n?n:o)||l},e.getIsPinned=()=>{let l=[e.id],{top:n,bottom:o}=t.getState().rowPinning,i=l.some(e=>null==n?void 0:n.includes(e)),r=l.some(e=>null==o?void 0:o.includes(e));return i?"top":!!r&&"bottom"},e.getPinnedIndex=()=>{var l,n;let o=e.getIsPinned();if(!o)return -1;let i=null==(l="top"===o?t.getTopRows():t.getBottomRows())?void 0:l.map(e=>{let{id:t}=e;return t});return null!=(n=null==i?void 0:i.indexOf(e.id))?n:-1}},createTable:e=>{e.setRowPinning=t=>null==e.options.onRowPinningChange?void 0:e.options.onRowPinningChange(t),e.resetRowPinning=t=>{var l,n;return e.setRowPinning(t?E():null!=(l=null==(n=e.initialState)?void 0:n.rowPinning)?l:E())},e.getIsSomeRowsPinned=t=>{var l,n,o;let i=e.getState().rowPinning;return t?!!(null==(l=i[t])?void 0:l.length):!!((null==(n=i.top)?void 0:n.length)||(null==(o=i.bottom)?void 0:o.length))},e._getPinnedRows=(t,l,n)=>{var o;return(null==(o=e.options.keepPinnedRows)||o?(null!=l?l:[]).map(t=>{let l=e.getRow(t,!0);return l.getIsAllParentsExpanded()?l:null}):(null!=l?l:[]).map(e=>t.find(t=>t.id===e))).filter(Boolean).map(e=>({...e,position:n}))},e.getTopRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.top],(t,l)=>e._getPinnedRows(t,l,"top"),r(e.options,"debugRows","getTopRows")),e.getBottomRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.bottom],(t,l)=>e._getPinnedRows(t,l,"bottom"),r(e.options,"debugRows","getBottomRows")),e.getCenterRows=i(()=>[e.getRowModel().rows,e.getState().rowPinning.top,e.getState().rowPinning.bottom],(e,t,l)=>{let n=new Set([...null!=t?t:[],...null!=l?l:[]]);return e.filter(e=>!n.has(e.id))},r(e.options,"debugRows","getCenterRows"))}},{getInitialState:e=>({rowSelection:{},...e}),getDefaultOptions:e=>({onRowSelectionChange:n("rowSelection",e),enableRowSelection:!0,enableMultiRowSelection:!0,enableSubRowSelection:!0}),createTable:e=>{e.setRowSelection=t=>null==e.options.onRowSelectionChange?void 0:e.options.onRowSelectionChange(t),e.resetRowSelection=t=>{var l;return e.setRowSelection(t?{}:null!=(l=e.initialState.rowSelection)?l:{})},e.toggleAllRowsSelected=t=>{e.setRowSelection(l=>{t=void 0!==t?t:!e.getIsAllRowsSelected();let n={...l},o=e.getPreGroupedRowModel().flatRows;return t?o.forEach(e=>{e.getCanSelect()&&(n[e.id]=!0)}):o.forEach(e=>{delete n[e.id]}),n})},e.toggleAllPageRowsSelected=t=>e.setRowSelection(l=>{let n=void 0!==t?t:!e.getIsAllPageRowsSelected(),o={...l};return e.getRowModel().rows.forEach(t=>{G(o,t.id,n,!0,e)}),o}),e.getPreSelectedRowModel=()=>e.getCoreRowModel(),e.getSelectedRowModel=i(()=>[e.getState().rowSelection,e.getCoreRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getSelectedRowModel")),e.getFilteredSelectedRowModel=i(()=>[e.getState().rowSelection,e.getFilteredRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getFilteredSelectedRowModel")),e.getGroupedSelectedRowModel=i(()=>[e.getState().rowSelection,e.getSortedRowModel()],(t,l)=>Object.keys(t).length?L(e,l):{rows:[],flatRows:[],rowsById:{}},r(e.options,"debugTable","getGroupedSelectedRowModel")),e.getIsAllRowsSelected=()=>{let t=e.getFilteredRowModel().flatRows,{rowSelection:l}=e.getState(),n=!!(t.length&&Object.keys(l).length);return n&&t.some(e=>e.getCanSelect()&&!l[e.id])&&(n=!1),n},e.getIsAllPageRowsSelected=()=>{let t=e.getPaginationRowModel().flatRows.filter(e=>e.getCanSelect()),{rowSelection:l}=e.getState(),n=!!t.length;return n&&t.some(e=>!l[e.id])&&(n=!1),n},e.getIsSomeRowsSelected=()=>{var t;let l=Object.keys(null!=(t=e.getState().rowSelection)?t:{}).length;return l>0&&l{let t=e.getPaginationRowModel().flatRows;return!e.getIsAllPageRowsSelected()&&t.filter(e=>e.getCanSelect()).some(e=>e.getIsSelected()||e.getIsSomeSelected())},e.getToggleAllRowsSelectedHandler=()=>t=>{e.toggleAllRowsSelected(t.target.checked)},e.getToggleAllPageRowsSelectedHandler=()=>t=>{e.toggleAllPageRowsSelected(t.target.checked)}},createRow:(e,t)=>{e.toggleSelected=(l,n)=>{let o=e.getIsSelected();t.setRowSelection(i=>{var r;if(l=void 0!==l?l:!o,e.getCanSelect()&&o===l)return i;let a={...i};return G(a,e.id,l,null==(r=null==n?void 0:n.selectChildren)||r,t),a})},e.getIsSelected=()=>{let{rowSelection:l}=t.getState();return A(e,l)},e.getIsSomeSelected=()=>{let{rowSelection:l}=t.getState();return"some"===H(e,l)},e.getIsAllSubRowsSelected=()=>{let{rowSelection:l}=t.getState();return"all"===H(e,l)},e.getCanSelect=()=>{var l;return"function"==typeof t.options.enableRowSelection?t.options.enableRowSelection(e):null==(l=t.options.enableRowSelection)||l},e.getCanSelectSubRows=()=>{var l;return"function"==typeof t.options.enableSubRowSelection?t.options.enableSubRowSelection(e):null==(l=t.options.enableSubRowSelection)||l},e.getCanMultiSelect=()=>{var l;return"function"==typeof t.options.enableMultiRowSelection?t.options.enableMultiRowSelection(e):null==(l=t.options.enableMultiRowSelection)||l},e.getToggleSelectedHandler=()=>{let t=e.getCanSelect();return l=>{var n;t&&e.toggleSelected(null==(n=l.target)?void 0:n.checked)}}}},{getDefaultColumnDef:()=>V,getInitialState:e=>({columnSizing:{},columnSizingInfo:P(),...e}),getDefaultOptions:e=>({columnResizeMode:"onEnd",columnResizeDirection:"ltr",onColumnSizingChange:n("columnSizing",e),onColumnSizingInfoChange:n("columnSizingInfo",e)}),createColumn:(e,t)=>{e.getSize=()=>{var l,n,o;let i=t.getState().columnSizing[e.id];return Math.min(Math.max(null!=(l=e.columnDef.minSize)?l:V.minSize,null!=(n=null!=i?i:e.columnDef.size)?n:V.size),null!=(o=e.columnDef.maxSize)?o:V.maxSize)},e.getStart=i(e=>[e,_(t,e),t.getState().columnSizing],(t,l)=>l.slice(0,e.getIndex(t)).reduce((e,t)=>e+t.getSize(),0),r(t.options,"debugColumns","getStart")),e.getAfter=i(e=>[e,_(t,e),t.getState().columnSizing],(t,l)=>l.slice(e.getIndex(t)+1).reduce((e,t)=>e+t.getSize(),0),r(t.options,"debugColumns","getAfter")),e.resetSize=()=>{t.setColumnSizing(t=>{let{[e.id]:l,...n}=t;return n})},e.getCanResize=()=>{var l,n;return(null==(l=e.columnDef.enableResizing)||l)&&(null==(n=t.options.enableColumnResizing)||n)},e.getIsResizing=()=>t.getState().columnSizingInfo.isResizingColumn===e.id},createHeader:(e,t)=>{e.getSize=()=>{let t=0,l=e=>{if(e.subHeaders.length)e.subHeaders.forEach(l);else{var n;t+=null!=(n=e.column.getSize())?n:0}};return l(e),t},e.getStart=()=>{if(e.index>0){let t=e.headerGroup.headers[e.index-1];return t.getStart()+t.getSize()}return 0},e.getResizeHandler=l=>{let n=t.getColumn(e.column.id),o=null==n?void 0:n.getCanResize();return i=>{if(!n||!o||(null==i.persist||i.persist(),x(i)&&i.touches&&i.touches.length>1))return;let r=e.getSize(),a=e?e.getLeafHeaders().map(e=>[e.column.id,e.column.getSize()]):[[n.id,n.getSize()]],u=x(i)?Math.round(i.touches[0].clientX):i.clientX,g={},s=(e,l)=>{"number"==typeof l&&(t.setColumnSizingInfo(e=>{var n,o;let i="rtl"===t.options.columnResizeDirection?-1:1,r=(l-(null!=(n=null==e?void 0:e.startOffset)?n:0))*i,a=Math.max(r/(null!=(o=null==e?void 0:e.startSize)?o:0),-.999999);return e.columnSizingStart.forEach(e=>{let[t,l]=e;g[t]=Math.round(100*Math.max(l+l*a,0))/100}),{...e,deltaOffset:r,deltaPercentage:a}}),("onChange"===t.options.columnResizeMode||"end"===e)&&t.setColumnSizing(e=>({...e,...g})))},d=e=>{s("end",e),t.setColumnSizingInfo(e=>({...e,isResizingColumn:!1,startOffset:null,startSize:null,deltaOffset:null,deltaPercentage:null,columnSizingStart:[]}))},p=l||("u">typeof document?document:null),c={moveHandler:e=>s("move",e.clientX),upHandler:e=>{null==p||p.removeEventListener("mousemove",c.moveHandler),null==p||p.removeEventListener("mouseup",c.upHandler),d(e.clientX)}},f={moveHandler:e=>(e.cancelable&&(e.preventDefault(),e.stopPropagation()),s("move",e.touches[0].clientX),!1),upHandler:e=>{var t;null==p||p.removeEventListener("touchmove",f.moveHandler),null==p||p.removeEventListener("touchend",f.upHandler),e.cancelable&&(e.preventDefault(),e.stopPropagation()),d(null==(t=e.touches[0])?void 0:t.clientX)}},m=!!function(){if("boolean"==typeof I)return I;let e=!1;try{let t=()=>{};window.addEventListener("test",t,{get passive(){return e=!0,!1}}),window.removeEventListener("test",t)}catch(t){e=!1}return I=e}()&&{passive:!1};x(i)?(null==p||p.addEventListener("touchmove",f.moveHandler,m),null==p||p.addEventListener("touchend",f.upHandler,m)):(null==p||p.addEventListener("mousemove",c.moveHandler,m),null==p||p.addEventListener("mouseup",c.upHandler,m)),t.setColumnSizingInfo(e=>({...e,startOffset:u,startSize:r,deltaOffset:0,deltaPercentage:0,columnSizingStart:a,isResizingColumn:n.id}))}}},createTable:e=>{e.setColumnSizing=t=>null==e.options.onColumnSizingChange?void 0:e.options.onColumnSizingChange(t),e.setColumnSizingInfo=t=>null==e.options.onColumnSizingInfoChange?void 0:e.options.onColumnSizingInfoChange(t),e.resetColumnSizing=t=>{var l;e.setColumnSizing(t?{}:null!=(l=e.initialState.columnSizing)?l:{})},e.resetHeaderSizeInfo=t=>{var l;e.setColumnSizingInfo(t?P():null!=(l=e.initialState.columnSizingInfo)?l:P())},e.getTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getLeftTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getLeftHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getCenterTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getCenterHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0},e.getRightTotalSize=()=>{var t,l;return null!=(t=null==(l=e.getRightHeaderGroups()[0])?void 0:l.headers.reduce((e,t)=>e+t.getSize(),0))?t:0}}}];function k(e){var t,n;let o=[...q,...null!=(t=e._features)?t:[]],a={_features:o},u=a._features.reduce((e,t)=>Object.assign(e,null==t.getDefaultOptions?void 0:t.getDefaultOptions(a)),{}),g={...null!=(n=e.initialState)?n:{}};a._features.forEach(e=>{var t;g=null!=(t=null==e.getInitialState?void 0:e.getInitialState(g))?t:g});let s=[],d=!1,p={_features:o,options:{...u,...e},initialState:g,_queue:e=>{s.push(e),d||(d=!0,Promise.resolve().then(()=>{for(;s.length;)s.shift()();d=!1}).catch(e=>setTimeout(()=>{throw e})))},reset:()=>{a.setState(a.initialState)},setOptions:e=>{var t;t=l(e,a.options),a.options=a.options.mergeOptions?a.options.mergeOptions(u,t):{...u,...t}},getState:()=>a.options.state,setState:e=>{null==a.options.onStateChange||a.options.onStateChange(e)},_getRowId:(e,t,l)=>{var n;return null!=(n=null==a.options.getRowId?void 0:a.options.getRowId(e,t,l))?n:`${l?[l.id,t].join("."):t}`},getCoreRowModel:()=>(a._getCoreRowModel||(a._getCoreRowModel=a.options.getCoreRowModel(a)),a._getCoreRowModel()),getRowModel:()=>a.getPaginationRowModel(),getRow:(e,t)=>{let l=(t?a.getPrePaginationRowModel():a.getRowModel()).rowsById[e];if(!l&&!(l=a.getCoreRowModel().rowsById[e]))throw Error();return l},_getDefaultColumnDef:i(()=>[a.options.defaultColumn],e=>{var t;return e=null!=(t=e)?t:{},{header:e=>{let t=e.header.column.columnDef;return t.accessorKey?t.accessorKey:t.accessorFn?t.id:null},cell:e=>{var t,l;return null!=(t=null==(l=e.renderValue())||null==l.toString?void 0:l.toString())?t:null},...a._features.reduce((e,t)=>Object.assign(e,null==t.getDefaultColumnDef?void 0:t.getDefaultColumnDef()),{}),...e}},r(e,"debugColumns","_getDefaultColumnDef")),_getColumnDefs:()=>a.options.columns,getAllColumns:i(()=>[a._getColumnDefs()],e=>{let t=function(e,l,n){return void 0===n&&(n=0),e.map(e=>{let o=function(e,t,l,n){var o,a;let u,g={...e._getDefaultColumnDef(),...t},s=g.accessorKey,d=null!=(o=null!=(a=g.id)?a:s?"function"==typeof String.prototype.replaceAll?s.replaceAll(".","_"):s.replace(/\./g,"_"):void 0)?o:"string"==typeof g.header?g.header:void 0;if(g.accessorFn?u=g.accessorFn:s&&(u=s.includes(".")?e=>{let t=e;for(let e of s.split(".")){var l;t=null==(l=t)?void 0:l[e]}return t}:e=>e[g.accessorKey]),!d)throw Error();let p={id:`${String(d)}`,accessorFn:u,parent:n,depth:l,columnDef:g,columns:[],getFlatColumns:i(()=>[!0],()=>{var e;return[p,...null==(e=p.columns)?void 0:e.flatMap(e=>e.getFlatColumns())]},r(e.options,"debugColumns","column.getFlatColumns")),getLeafColumns:i(()=>[e._getOrderColumnsFn()],e=>{var t;return null!=(t=p.columns)&&t.length?e(p.columns.flatMap(e=>e.getLeafColumns())):[p]},r(e.options,"debugColumns","column.getLeafColumns"))};for(let t of e._features)null==t.createColumn||t.createColumn(p,e);return p}(a,e,n,l);return o.columns=e.columns?t(e.columns,o,n+1):[],o})};return t(e)},r(e,"debugColumns","getAllColumns")),getAllFlatColumns:i(()=>[a.getAllColumns()],e=>e.flatMap(e=>e.getFlatColumns()),r(e,"debugColumns","getAllFlatColumns")),_getAllFlatColumnsById:i(()=>[a.getAllFlatColumns()],e=>e.reduce((e,t)=>(e[t.id]=t,e),{}),r(e,"debugColumns","getAllFlatColumnsById")),getAllLeafColumns:i(()=>[a.getAllColumns(),a._getOrderColumnsFn()],(e,t)=>t(e.flatMap(e=>e.getLeafColumns())),r(e,"debugColumns","getAllLeafColumns")),getColumn:e=>a._getAllFlatColumnsById()[e]};Object.assign(a,p);for(let e=0;e{var n;t.push(e),null!=(n=e.subRows)&&n.length&&e.getIsExpanded()&&e.subRows.forEach(l)};return e.rows.forEach(l),{rows:t,flatRows:e.flatRows,rowsById:e.rowsById}}e.s(["createTable",0,k,"getCoreRowModel",0,function(){return e=>i(()=>[e.options.data],t=>{let l={rows:[],flatRows:[],rowsById:{}},n=function(t,o,i){void 0===o&&(o=0);let r=[];for(let u=0;ue._autoResetPageIndex()))},"getExpandedRowModel",0,function(){return e=>i(()=>[e.getState().expanded,e.getPreExpandedRowModel(),e.options.paginateExpandedRows],(e,t,l)=>t.rows.length&&(!0===e||Object.keys(null!=e?e:{}).length)&&l?j(t):t,r(e.options,"debugTable","getExpandedRowModel"))},"getFilteredRowModel",0,function(){return e=>i(()=>[e.getPreFilteredRowModel(),e.getState().columnFilters,e.getState().globalFilter],(t,l,n)=>{var o,i,r,a,u,g,d,p,c,f;let m,C,w,R,h,v,S,b,F,M;if(!t.rows.length||!(null!=l&&l.length)&&!n){for(let e=0;e{var l;let n=e.getColumn(t.id);if(!n)return;let o=n.getFilterFn();o&&V.push({id:t.id,filterFn:o,resolvedValue:null!=(l=null==o.resolveFilterValue?void 0:o.resolveFilterValue(t.value))?l:t.value})});let I=(null!=l?l:[]).map(e=>e.id),x=e.getGlobalFilterFn(),_=e.getAllLeafColumns().filter(e=>e.getCanGlobalFilter());n&&x&&_.length&&(I.push("__global__"),_.forEach(e=>{var t;P.push({id:e.id,filterFn:x,resolvedValue:null!=(t=null==x.resolveFilterValue?void 0:x.resolveFilterValue(n))?t:n})}));for(let e=0;e{l.columnFiltersMeta[t]=e})}if(P.length){for(let e=0;e{l.columnFiltersMeta[t]=e})){l.columnFilters.__global__=!0;break}}!0!==l.columnFilters.__global__&&(l.columnFilters.__global__=!1)}}return o=t.rows,i=e=>{for(let t=0;te._autoResetPageIndex()))},"getPaginationRowModel",0,function(e){return e=>i(()=>[e.getState().pagination,e.getPrePaginationRowModel(),e.options.paginateExpandedRows?void 0:e.getState().expanded],(t,l)=>{let n;if(!l.rows.length)return l;let{pageSize:o,pageIndex:i}=t,{rows:r,flatRows:a,rowsById:u}=l,g=o*i;r=r.slice(g,g+o),(n=e.options.paginateExpandedRows?{rows:r,flatRows:a,rowsById:u}:j({rows:r,flatRows:a,rowsById:u})).flatRows=[];let s=e=>{n.flatRows.push(e),e.subRows.length&&e.subRows.forEach(s)};return n.rows.forEach(s),n},r(e.options,"debugTable","getPaginationRowModel"))},"getSortedRowModel",0,function(){return e=>i(()=>[e.getState().sorting,e.getPreSortedRowModel()],(t,l)=>{if(!l.rows.length||!(null!=t&&t.length))return l;let n=e.getState().sorting,o=[],i=n.filter(t=>{var l;return null==(l=e.getColumn(t.id))?void 0:l.getCanSort()}),r={};i.forEach(t=>{let l=e.getColumn(t.id);l&&(r[t.id]={sortUndefined:l.columnDef.sortUndefined,invertSorting:l.columnDef.invertSorting,sortingFn:l.getSortingFn()})});let a=e=>{let t=e.map(e=>({...e}));return t.sort((e,t)=>{for(let n=0;n{var t;o.push(e),null!=(t=e.subRows)&&t.length&&(e.subRows=a(e.subRows))}),t};return{rows:a(l.rows),flatRows:o,rowsById:l.rowsById}},r(e.options,"debugTable","getSortedRowModel",()=>e._autoResetPageIndex()))}],682830),e.s(["flexRender",0,function(e,l){var n,o,i;let r;return e?"function"==typeof(o=n=e)&&(r=Object.getPrototypeOf(o)).prototype&&r.prototype.isReactComponent||"function"==typeof n||"object"==typeof(i=n)&&"symbol"==typeof i.$$typeof&&["react.memo","react.forward_ref"].includes(i.$$typeof.description)?t.createElement(e,l):e:null},"useReactTable",0,function(e){let l={state:{},onStateChange:()=>{},renderFallbackValue:null,...e},[n]=t.useState(()=>({current:k(l)})),[o,i]=t.useState(()=>n.current.initialState);return n.current.setOptions(t=>({...t,...e,state:{...o,...e.state},onStateChange:t=>{i(t),null==e.onStateChange||e.onStateChange(t)}})),n.current}],152990)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/022.sz94ycw4x.js b/litellm/proxy/_experimental/out/_next/static/chunks/022.sz94ycw4x.js new file mode 100644 index 00000000000..746b869a2c6 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/022.sz94ycw4x.js @@ -0,0 +1,4 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,751734,e=>{"use strict";let t=(0,e.i(271645).createContext)(0);e.s(["default",0,t])},144582,e=>{"use strict";let t=(0,e.i(271645).createContext)({selectedValue:void 0,handleValueChange:void 0});e.s(["default",0,t])},404206,e=>{"use strict";var t=e.i(290571),r=e.i(751734),n=e.i(144582),o=e.i(444755),s=e.i(673706),a=e.i(271645);let i=(0,s.makeClassName)("TabPanel"),l=a.default.forwardRef((e,s)=>{let{children:l,className:u}=e,c=(0,t.__rest)(e,["children","className"]),{selectedValue:d}=(0,a.useContext)(n.default),f=d===(0,a.useContext)(r.default);return a.default.createElement("div",Object.assign({ref:s,className:(0,o.tremorTwMerge)(i("root"),"w-full mt-2",f?"":"hidden",u),"aria-selected":f?"true":"false"},c),l)});l.displayName="TabPanel",e.s(["TabPanel",0,l],404206)},783222,433336,80758,402155,368578,544508,746725,835696,941444,914189,394487,e=>{"use strict";let t;e.i(247167);let r=e=>e?.ownerDocument??document,n=e=>e&&"window"in e&&e.window===e?e:r(e).defaultView||window;function o(e,t){return!!t&&!!e&&e.contains(t)}function s(e){return e.target}let a=null;"u">typeof Element&&Element.prototype;let i=["input:not([disabled]):not([type=hidden])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","a[href]","area[href]","summary","iframe","object","embed","audio[controls]","video[controls]",'[contenteditable]:not([contenteditable^="false"])',"permission"];i.join(":not([hidden]),"),i.push('[tabindex]:not([tabindex="-1"]):not([disabled])'),i.join(':not([hidden]):not([tabindex="-1"]),');var l=e.i(271645);let u="u">typeof document?l.default.useLayoutEffect:()=>{};function c(e){return e.nativeEvent=e,e.isDefaultPrevented=()=>e.defaultPrevented,e.isPropagationStopped=()=>e.cancelBubble,e.persist=()=>{},e}function d(e){let t=(0,l.useRef)({isFocused:!1,observer:null});return u(()=>{let e=t.current;return()=>{e.observer&&(e.observer.disconnect(),e.observer=null)}},[]),(0,l.useCallback)(r=>{let n=s(r);(n instanceof HTMLButtonElement||n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement)&&(t.current.isFocused=!0,n.addEventListener("focusout",r=>{if(t.current.isFocused=!1,n.disabled){let t=c(r);e?.(t)}t.current.observer&&(t.current.observer.disconnect(),t.current.observer=null)},{once:!0}),t.current.observer=new MutationObserver(()=>{if(t.current.isFocused&&n.disabled){t.current.observer?.disconnect();let e=n===((e=document)=>e.activeElement)()?null:((e=document)=>e.activeElement)();n.dispatchEvent(new FocusEvent("blur",{relatedTarget:e})),n.dispatchEvent(new FocusEvent("focusout",{bubbles:!0,relatedTarget:e}))}}),t.current.observer.observe(n,{attributes:!0,attributeFilter:["disabled"]}))},[e])}function f(e){if("u"e.test(t.brand))||e.test(window.navigator.userAgent)}function p(e){return"u">typeof window&&null!=window.navigator&&e.test(window.navigator.userAgentData?.platform||window.navigator.platform)}function m(e){let t=null;return()=>(null==t&&(t=e()),t)}let b=m(function(){return p(/^Mac/i)}),v=m(function(){return p(/^iPhone/i)}),h=m(function(){return p(/^iPad/i)||b()&&navigator.maxTouchPoints>1}),g=m(function(){return v()||h()});m(function(){return b()||g()});let y=m(function(){return f(/AppleWebKit/i)&&!E()}),E=m(function(){return f(/Chrome/i)}),T=m(function(){return f(/Android/i)}),w=m(function(){return f(/Firefox/i)});function x(e,t,r=!0){let{metaKey:n,ctrlKey:o,altKey:s,shiftKey:i}=t;w()&&window.event?.type?.startsWith("key")&&"_blank"===e.target&&(b()?n=!0:o=!0);let l=y()&&b()&&!h()&&1?new KeyboardEvent("keydown",{keyIdentifier:"Enter",metaKey:n,ctrlKey:o,altKey:s,shiftKey:i}):new MouseEvent("click",{metaKey:n,ctrlKey:o,altKey:s,shiftKey:i,detail:1,bubbles:!0,cancelable:!0});x.isOpening=r;if(function(){if(null==a){a=!1;try{document.createElement("div").focus({get preventScroll(){return a=!0,!0}})}catch{}}return a}())e.focus({preventScroll:!0});else{let t=function(e){let t=e.parentNode,r=[],n=document.scrollingElement||document.documentElement;for(;t instanceof HTMLElement&&t!==n;)(t.offsetHeighttypeof window&&window.document&&window.document.createElement,new WeakMap;l.default.useId;let P=null,k=new Set,L=new Map,N=!1,C=!1,I={Tab:!0,Escape:!0};function S(e,t){for(let r of k)r(e,t)}function A(e){N=!0,x.isOpening||e.metaKey||!b()&&e.altKey||e.ctrlKey||"Control"===e.key||"Shift"===e.key||"Meta"===e.key||(P="keyboard",S("keyboard",e))}function M(e){P="pointer","pointerType"in e&&e.pointerType,("mousedown"===e.type||"pointerdown"===e.type)&&(N=!0,S("pointer",e))}function R(e){x.isOpening||(""!==e.pointerType||!e.isTrusted)&&(T()&&e.pointerType?"click"!==e.type||1!==e.buttons:0!==e.detail||e.pointerType)||(N=!0,P="virtual")}function O(e){let t=n(s(e)),o=r(s(e));s(e)!==t&&s(e)!==o&&e.isTrusted&&(N||C||(P="virtual",S("virtual",e)),N=!1,C=!1)}function D(){N=!1,C=!0}function H(e){if("u"typeof PointerEvent&&(o.addEventListener("pointerdown",M,!0),o.addEventListener("pointermove",M,!0),o.addEventListener("pointerup",M,!0)),t.addEventListener("beforeunload",()=>{j(e)},{once:!0}),L.set(t,{focus:s})}let j=(e,t)=>{let o=n(e),s=r(e);t&&s.removeEventListener("DOMContentLoaded",t),L.has(o)&&(o.HTMLElement.prototype.focus=L.get(o).focus,s.removeEventListener("keydown",A,!0),s.removeEventListener("keyup",A,!0),s.removeEventListener("click",R,!0),o.removeEventListener("focus",O,!0),o.removeEventListener("blur",D,!1),"u">typeof PointerEvent&&(s.removeEventListener("pointerdown",M,!0),s.removeEventListener("pointermove",M,!0),s.removeEventListener("pointerup",M,!0)),L.delete(o))};function K(){return"pointer"!==P}"u">typeof document&&("loading"!==(t=r(void 0)).readyState?H(void 0):t.addEventListener("DOMContentLoaded",()=>{H(void 0)}));let W=new Set(["checkbox","radio","range","color","file","image","button","submit","reset"]);function B(){let e=(0,l.useRef)(new Map),t=(0,l.useCallback)((t,r,n,o)=>{let s=o?.once?(...t)=>{e.current.delete(n),n(...t)}:n;e.current.set(n,{type:r,eventTarget:t,fn:s,options:o}),t.addEventListener(r,s,o)},[]),r=(0,l.useCallback)((t,r,n,o)=>{let s=e.current.get(n)?.fn||n;t.removeEventListener(r,s,o),e.current.delete(n)},[]),n=(0,l.useCallback)(()=>{e.current.forEach((e,t)=>{r(e.eventTarget,e.type,t,e.options)})},[r]);return(0,l.useEffect)(()=>n,[n]),{addGlobalListener:t,removeGlobalListener:r,removeAllGlobalListeners:n}}e.s(["useFocusRing",0,function(e={}){var t;let{autoFocus:a=!1,isTextInput:i,within:u}=e,f=(0,l.useRef)({isFocused:!1,isFocusVisible:a||K()}),[p,m]=(0,l.useState)(!1),[b,v]=(0,l.useState)(()=>f.current.isFocused&&f.current.isFocusVisible),h=(0,l.useCallback)(()=>v(f.current.isFocused&&f.current.isFocusVisible),[]),g=(0,l.useCallback)(e=>{f.current.isFocused=e,f.current.isFocusVisible=K(),m(e),h()},[h]);t={enabled:p,isTextInput:i},H(),(0,l.useEffect)(()=>{if(t?.enabled===!1)return;let e=(e,o)=>{var a;let i,l,u,c,d,p,m,b;a=!!t?.isTextInput,l=r(i=o?s(o):void 0),c=void 0!==(u=n(i))?u.HTMLInputElement:HTMLInputElement,d=void 0!==u?u.HTMLTextAreaElement:HTMLTextAreaElement,p=void 0!==u?u.HTMLElement:HTMLElement,m=void 0!==u?u.KeyboardEvent:KeyboardEvent,b=((e=document)=>e.activeElement)(l),(a=a||b instanceof c&&!W.has(b.type)||b instanceof d||b instanceof p&&b.isContentEditable)&&"keyboard"===e&&o instanceof m&&!I[o.key]||(e=>{f.current.isFocusVisible=e,h()})(K())};return k.add(e),()=>{k.delete(e)}},[i,p]);let{focusProps:y}=function(e){let{isDisabled:t,onFocus:n,onBlur:o,onFocusChange:a}=e,i=(0,l.useCallback)(e=>{if(s(e)===e.currentTarget)return o&&o(e),a&&a(!1),!0},[o,a]),u=d(i),c=(0,l.useCallback)(e=>{let t=s(e),o=r(t),i=o?((e=document)=>e.activeElement)(o):((e=document)=>e.activeElement)();t===e.currentTarget&&t===i&&(n&&n(e),a&&a(!0),u(e))},[a,n,u]);return{focusProps:{onFocus:!t&&(n||a||o)?c:void 0,onBlur:!t&&(o||a)?i:void 0}}}({isDisabled:u,onFocusChange:g}),{focusWithinProps:E}=function(e){let{isDisabled:t,onBlurWithin:n,onFocusWithin:a,onFocusWithinChange:i}=e,u=(0,l.useRef)({isFocusWithin:!1}),{addGlobalListener:f,removeAllGlobalListeners:p}=B(),m=(0,l.useCallback)(e=>{o(e.currentTarget,s(e))&&u.current.isFocusWithin&&!o(e.currentTarget,e.relatedTarget)&&(u.current.isFocusWithin=!1,p(),n&&n(e),i&&i(!1))},[n,i,u,p]),b=d(m),v=(0,l.useCallback)(e=>{if(!o(e.currentTarget,s(e)))return;let t=s(e),n=r(t),l=((e=document)=>e.activeElement)(n);if(!u.current.isFocusWithin&&l===t){a&&a(e),i&&i(!0),u.current.isFocusWithin=!0,b(e);let t=e.currentTarget;f(n,"focus",e=>{let r=s(e);if(u.current.isFocusWithin&&!o(t,r)){let e=new n.defaultView.FocusEvent("blur",{relatedTarget:r});Object.defineProperty(e,"target",{value:t}),Object.defineProperty(e,"currentTarget",{value:t}),m(c(e))}},{capture:!0})}},[a,i,b,f,m]);return t?{focusWithinProps:{onFocus:void 0,onBlur:void 0}}:{focusWithinProps:{onFocus:v,onBlur:m}}}({isDisabled:!u,onFocusWithinChange:g});return{isFocused:p,isFocusVisible:b,focusProps:u?E:y}}],783222);let V=!1,_=0;function G(e){"touch"===e.pointerType&&(V=!0,setTimeout(()=>{V=!1},500))}function U(){let e=r(null);if(void 0!==e)return 0===_&&"u">typeof PointerEvent&&e.addEventListener("pointerup",G),_++,()=>{!(--_>0)&&"u">typeof PointerEvent&&e.removeEventListener("pointerup",G)}}e.s(["useHover",0,function(e){let{onHoverStart:t,onHoverChange:n,onHoverEnd:a,isDisabled:i}=e,[u,c]=(0,l.useState)(!1),d=(0,l.useRef)({isHovered:!1,ignoreEmulatedMouseEvents:!1,pointerType:"",target:null}).current;(0,l.useEffect)(U,[]);let{addGlobalListener:f,removeAllGlobalListeners:p}=B(),{hoverProps:m,triggerHoverEnd:b}=(0,l.useMemo)(()=>{let e=(e,t)=>{let r=d.target;d.pointerType="",d.target=null,"touch"!==t&&d.isHovered&&r&&(d.isHovered=!1,p(),a&&a({type:"hoverend",target:r,pointerType:t}),n&&n(!1),c(!1))},l={};return"u">typeof PointerEvent&&(l.onPointerEnter=a=>{V&&"mouse"===a.pointerType||((a,l)=>{if(d.pointerType=l,i||"touch"===l||d.isHovered||!o(a.currentTarget,s(a)))return;d.isHovered=!0;let u=a.currentTarget;d.target=u,f(r(s(a)),"pointerover",t=>{d.isHovered&&d.target&&!o(d.target,s(t))&&e(t,t.pointerType)},{capture:!0}),t&&t({type:"hoverstart",target:u,pointerType:l}),n&&n(!0),c(!0)})(a,a.pointerType)},l.onPointerLeave=t=>{!i&&o(t.currentTarget,s(t))&&e(t,t.pointerType)}),{hoverProps:l,triggerHoverEnd:e}},[t,n,a,i,d,f,p]);return(0,l.useEffect)(()=>{i&&b({currentTarget:d.target},d.pointerType)},[i]),{hoverProps:m,isHovered:u}}],433336);var $=Object.defineProperty,q=(e,t,r)=>{let n;return(n="symbol"!=typeof t?t+"":t)in e?$(e,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[n]=r,r};let X=new class{constructor(){q(this,"current",this.detect()),q(this,"handoffState","pending"),q(this,"currentId",0)}set(e){this.current!==e&&(this.handoffState="pending",this.currentId=0,this.current=e)}reset(){this.set(this.detect())}nextId(){return++this.currentId}get isServer(){return"server"===this.current}get isClient(){return"client"===this.current}detect(){return"u"setTimeout(()=>{throw e}))}function Z(){let e=[],t={addEventListener:(e,r,n,o)=>(e.addEventListener(r,n,o),t.add(()=>e.removeEventListener(r,n,o))),requestAnimationFrame(...e){let r=requestAnimationFrame(...e);return t.add(()=>cancelAnimationFrame(r))},nextFrame:(...e)=>t.requestAnimationFrame(()=>t.requestAnimationFrame(...e)),setTimeout(...e){let r=setTimeout(...e);return t.add(()=>clearTimeout(r))},microTask(...e){let r={current:!0};return z(()=>{r.current&&e[0]()}),t.add(()=>{r.current=!1})},style(e,t,r){let n=e.style.getPropertyValue(t);return Object.assign(e.style,{[t]:r}),this.add(()=>{Object.assign(e.style,{[t]:n})})},group(e){let t=Z();return e(t),this.add(()=>t.dispose())},add:t=>(e.includes(t)||e.push(t),()=>{let r=e.indexOf(t);if(r>=0)for(let t of e.splice(r,1))t()}),dispose(){for(let t of e.splice(0))t()}};return t}function J(){let[e]=(0,l.useState)(Z);return(0,l.useEffect)(()=>()=>e.dispose(),[e]),e}e.s(["env",0,X],80758),e.s(["getOwnerDocument",0,Y],402155),e.s(["microTask",0,z],368578),e.s(["disposables",0,Z],544508),e.s(["useDisposables",0,J],746725);let Q=(e,t)=>{X.isServer?(0,l.useEffect)(e,t):(0,l.useLayoutEffect)(e,t)};function ee(e){let t=(0,l.useRef)(e);return Q(()=>{t.current=e},[e]),t}e.s(["useIsoMorphicEffect",0,Q],835696),e.s(["useLatestValue",0,ee],941444);let et=function(e){let t=ee(e);return l.default.useCallback((...e)=>t.current(...e),[t])};e.s(["useEvent",0,et],914189),e.s(["useActivePress",0,function({disabled:e=!1}={}){let t=(0,l.useRef)(null),[r,n]=(0,l.useState)(!1),o=J(),s=et(()=>{t.current=null,n(!1),o.dispose()}),a=et(e=>{if(o.dispose(),null===t.current){t.current=e.currentTarget,n(!0);{let r=Y(e.currentTarget);o.addEventListener(r,"pointerup",s,!1),o.addEventListener(r,"pointermove",e=>{if(t.current){var r,o;let s,a;n((s=e.width/2,a=e.height/2,r={top:e.clientY-a,right:e.clientX+s,bottom:e.clientY+a,left:e.clientX-s},o=t.current.getBoundingClientRect(),!(!r||!o||r.righto.right||r.bottomo.bottom)))}},!1),o.addEventListener(r,"pointercancel",s,!1)}}});return{pressed:r,pressProps:e?{}:{onPointerDown:a,onPointerUp:s,onClick:s}}}],394487)},397701,e=>{"use strict";e.s(["match",0,function e(t,r,...n){if(t in r){let e=r[t];return"function"==typeof e?e(...n):e}let o=Error(`Tried to handle "${t}" but there is no handler defined. Only defined handlers are: ${Object.keys(r).map(e=>`"${e}"`).join(", ")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(o,e),o}])},652265,e=>{"use strict";let t,r,n,o,s;e.i(544508);var a=e.i(397701),i=e.i(402155);let l=["[contentEditable=true]","[tabindex]","a[href]","area[href]","button:not([disabled])","iframe","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].map(e=>`${e}:not([tabindex='-1'])`).join(","),u=["[data-autofocus]"].map(e=>`${e}:not([tabindex='-1'])`).join(",");var c=((t=c||{})[t.First=1]="First",t[t.Previous=2]="Previous",t[t.Next=4]="Next",t[t.Last=8]="Last",t[t.WrapAround=16]="WrapAround",t[t.NoScroll=32]="NoScroll",t[t.AutoFocus=64]="AutoFocus",t),d=((r=d||{})[r.Error=0]="Error",r[r.Overflow=1]="Overflow",r[r.Success=2]="Success",r[r.Underflow=3]="Underflow",r),f=((n=f||{})[n.Previous=-1]="Previous",n[n.Next=1]="Next",n);function p(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(l)).sort((e,t)=>Math.sign((e.tabIndex||Number.MAX_SAFE_INTEGER)-(t.tabIndex||Number.MAX_SAFE_INTEGER)))}var m=((o=m||{})[o.Strict=0]="Strict",o[o.Loose=1]="Loose",o),b=((s=b||{})[s.Keyboard=0]="Keyboard",s[s.Mouse=1]="Mouse",s);function v(e,t=e=>e){return e.slice().sort((e,r)=>{let n=t(e),o=t(r);if(null===n||null===o)return 0;let s=n.compareDocumentPosition(o);return s&Node.DOCUMENT_POSITION_FOLLOWING?-1:s&Node.DOCUMENT_POSITION_PRECEDING?1:0})}function h(e,t,{sorted:r=!0,relativeTo:n=null,skipElements:o=[]}={}){var s,a,i;let l=Array.isArray(e)?e.length>0?e[0].ownerDocument:document:e.ownerDocument,c=Array.isArray(e)?r?v(e):e:64&t?function(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(u)).sort((e,t)=>Math.sign((e.tabIndex||Number.MAX_SAFE_INTEGER)-(t.tabIndex||Number.MAX_SAFE_INTEGER)))}(e):p(e);o.length>0&&c.length>1&&(c=c.filter(e=>!o.some(t=>null!=t&&"current"in t?(null==t?void 0:t.current)===e:t===e))),n=null!=n?n:l.activeElement;let d=(()=>{if(5&t)return 1;if(10&t)return -1;throw Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),f=(()=>{if(1&t)return 0;if(2&t)return Math.max(0,c.indexOf(n))-1;if(4&t)return Math.max(0,c.indexOf(n))+1;if(8&t)return c.length-1;throw Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),m=32&t?{preventScroll:!0}:{},b=0,g=c.length,y;do{if(b>=g||b+g<=0)return 0;let e=f+b;if(16&t)e=(e+g)%g;else{if(e<0)return 3;if(e>=g)return 1}null==(y=c[e])||y.focus(m),b+=d}while(y!==l.activeElement)return 6&t&&null!=(i=null==(a=null==(s=y)?void 0:s.matches)?void 0:a.call(s,"textarea,input"))&&i&&y.select(),2}"u">typeof window&&"u">typeof document&&(document.addEventListener("keydown",e=>{e.metaKey||e.altKey||e.ctrlKey||(document.documentElement.dataset.headlessuiFocusVisible="")},!0),document.addEventListener("click",e=>{1===e.detail?delete document.documentElement.dataset.headlessuiFocusVisible:0===e.detail&&(document.documentElement.dataset.headlessuiFocusVisible="")},!0)),e.s(["Focus",0,c,"FocusResult",0,d,"FocusableMode",0,m,"focusFrom",0,function(e,t){return h(p(),t,{relativeTo:e})},"focusIn",0,h,"getFocusableElements",0,p,"isFocusableElement",0,function(e,t=0){var r;return e!==(null==(r=(0,i.getOwnerDocument)(e))?void 0:r.body)&&(0,a.match)(t,{0:()=>e.matches(l),1(){let t=e;for(;null!==t;){if(t.matches(l))return!0;t=t.parentElement}return!1}})},"sortByDomNode",0,v])},144279,294316,e=>{"use strict";var t=e.i(271645);e.s(["useResolveButtonType",0,function(e,r){return(0,t.useMemo)(()=>{var t;if(e.type)return e.type;let n=null!=(t=e.as)?t:"button";if("string"==typeof n&&"button"===n.toLowerCase()||(null==r?void 0:r.tagName)==="BUTTON"&&!r.hasAttribute("type"))return"button"},[e.type,e.as,r])}],144279);var r=e.i(914189);let n=Symbol();e.s(["optionalRef",0,function(e,t=!0){return Object.assign(e,{[n]:t})},"useSyncRefs",0,function(...e){let o=(0,t.useRef)(e);(0,t.useEffect)(()=>{o.current=e},[e]);let s=(0,r.useEvent)(e=>{for(let t of o.current)null!=t&&("function"==typeof t?t(e):t.current=e)});return e.every(e=>null==e||(null==e?void 0:e[n]))?void 0:s}],294316)},732607,e=>{"use strict";e.s(["classNames",0,function(...e){return Array.from(new Set(e.flatMap(e=>"string"==typeof e?e.split(" "):[]))).filter(Boolean).join(" ")}])},700020,e=>{"use strict";let t,r;var n=e.i(271645),o=e.i(732607),s=e.i(397701),a=((t=a||{})[t.None=0]="None",t[t.RenderStrategy=1]="RenderStrategy",t[t.Static=2]="Static",t),i=((r=i||{})[r.Unmount=0]="Unmount",r[r.Hidden=1]="Hidden",r);function l(e,t={},r,s,a){let{as:i=r,children:u,refName:p="ref",...m}=f(e,["unmount","static"]),b=void 0!==e.ref?{[p]:e.ref}:{},v="function"==typeof u?u(t):u;"className"in m&&m.className&&"function"==typeof m.className&&(m.className=m.className(t)),m["aria-labelledby"]&&m["aria-labelledby"]===m.id&&(m["aria-labelledby"]=void 0);let h={};if(t){let e=!1,r=[];for(let[n,o]of Object.entries(t))"boolean"==typeof o&&(e=!0),!0===o&&r.push(n.replace(/([A-Z])/g,e=>`-${e.toLowerCase()}`));if(e)for(let e of(h["data-headlessui-state"]=r.join(" "),r))h[`data-${e}`]=""}if(i===n.Fragment&&(Object.keys(d(m)).length>0||Object.keys(d(h)).length>0))if(!(0,n.isValidElement)(v)||Array.isArray(v)&&v.length>1){if(Object.keys(d(m)).length>0)throw Error(['Passing props on "Fragment"!',"",`The current component <${s} /> is rendering a "Fragment".`,"However we need to passthrough the following props:",Object.keys(d(m)).concat(Object.keys(d(h))).map(e=>` - ${e}`).join(` +`),"","You can apply a few solutions:",['Add an `as="..."` prop, to ensure that we render an actual element instead of a "Fragment".',"Render a single element as the child so that we can forward the props onto that element."].map(e=>` - ${e}`).join(` +`)].join(` +`))}else{var g;let e=v.props,t=null==e?void 0:e.className,r="function"==typeof t?(...e)=>(0,o.classNames)(t(...e),m.className):(0,o.classNames)(t,m.className),s=c(v.props,d(f(m,["ref"])));for(let e in h)e in s&&delete h[e];return(0,n.cloneElement)(v,Object.assign({},s,h,b,{ref:a((g=v,n.default.version.split(".")[0]>="19"?g.props.ref:g.ref),b.ref)},r?{className:r}:{}))}return(0,n.createElement)(i,Object.assign({},f(m,["ref"]),i!==n.Fragment&&b,i!==n.Fragment&&h),v)}function u(...e){return e.every(e=>null==e)?void 0:t=>{for(let r of e)null!=r&&("function"==typeof r?r(t):r.current=t)}}function c(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},r={};for(let n of e)for(let e in n)e.startsWith("on")&&"function"==typeof n[e]?(null!=r[e]||(r[e]=[]),r[e].push(n[e])):t[e]=n[e];if(t.disabled||t["aria-disabled"])for(let e in r)/^(on(?:Click|Pointer|Mouse|Key)(?:Down|Up|Press)?)$/.test(e)&&(r[e]=[e=>{var t;return null==(t=null==e?void 0:e.preventDefault)?void 0:t.call(e)}]);for(let e in r)Object.assign(t,{[e](t,...n){for(let o of r[e]){if((t instanceof Event||(null==t?void 0:t.nativeEvent)instanceof Event)&&t.defaultPrevented)return;o(t,...n)}}});return t}function d(e){let t=Object.assign({},e);for(let e in t)void 0===t[e]&&delete t[e];return t}function f(e,t=[]){let r=Object.assign({},e);for(let e of t)e in r&&delete r[e];return r}e.s(["RenderFeatures",0,a,"RenderStrategy",0,i,"compact",0,d,"forwardRefWithAs",0,function(e){var t;return Object.assign((0,n.forwardRef)(e),{displayName:null!=(t=e.displayName)?t:e.name})},"mergeProps",0,function(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},r={};for(let n of e)for(let e in n)e.startsWith("on")&&"function"==typeof n[e]?(null!=r[e]||(r[e]=[]),r[e].push(n[e])):t[e]=n[e];for(let e in r)Object.assign(t,{[e](...t){for(let n of r[e])null==n||n(...t)}});return t},"useRender",0,function(){let e,t,r=(e=(0,n.useRef)([]),t=(0,n.useCallback)(t=>{for(let r of e.current)null!=r&&("function"==typeof r?r(t):r.current=t)},[]),(...r)=>{if(!r.every(e=>null==e))return e.current=r,t});return(0,n.useCallback)(e=>(function({ourProps:e,theirProps:t,slot:r,defaultTag:n,features:o,visible:a=!0,name:i,mergeRefs:d}){d=null!=d?d:u;let f=c(t,e);if(a)return l(f,r,n,i,d);let p=null!=o?o:0;if(2&p){let{static:e=!1,...t}=f;if(e)return l(t,r,n,i,d)}if(1&p){let{unmount:e=!0,...t}=f;return(0,s.match)(+!e,{0:()=>null,1:()=>l({...t,hidden:!0,style:{display:"none"}},r,n,i,d)})}return l(f,r,n,i,d)})({mergeRefs:r,...e}),[r])}])},2788,e=>{"use strict";let t;var r=e.i(700020),n=((t=n||{})[t.None=1]="None",t[t.Focusable=2]="Focusable",t[t.Hidden=4]="Hidden",t);let o=(0,r.forwardRefWithAs)(function(e,t){var n;let{features:o=1,...s}=e,a={ref:t,"aria-hidden":(2&o)==2||(null!=(n=s["aria-hidden"])?n:void 0),hidden:(4&o)==4||void 0,style:{position:"fixed",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",...(4&o)==4&&(2&o)!=2&&{display:"none"}}};return(0,r.useRender)()({ourProps:a,theirProps:s,slot:{},defaultTag:"span",name:"Hidden"})});e.s(["Hidden",0,o,"HiddenFeatures",0,n])},998348,e=>{"use strict";let t;var r=((t=r||{}).Space=" ",t.Enter="Enter",t.Escape="Escape",t.Backspace="Backspace",t.Delete="Delete",t.ArrowLeft="ArrowLeft",t.ArrowUp="ArrowUp",t.ArrowRight="ArrowRight",t.ArrowDown="ArrowDown",t.Home="Home",t.End="End",t.PageUp="PageUp",t.PageDown="PageDown",t.Tab="Tab",t);e.s(["Keys",0,r])},553521,e=>{"use strict";var t=e.i(271645),r=e.i(835696);e.s(["useIsMounted",0,function(){let e=(0,t.useRef)(!1);return(0,r.useIsoMorphicEffect)(()=>(e.current=!0,()=>{e.current=!1}),[]),e}])},640497,e=>{"use strict";var t=e.i(271645),r=e.i(553521),n=e.i(2788);e.s(["FocusSentinel",0,function({onFocus:e}){let[o,s]=(0,t.useState)(!0),a=(0,r.useIsMounted)();return o?t.default.createElement(n.Hidden,{as:"button",type:"button",features:n.HiddenFeatures.Focusable,onFocus:t=>{t.preventDefault();let r,n=50;r=requestAnimationFrame(function t(){if(n--<=0){r&&cancelAnimationFrame(r);return}if(e()){if(cancelAnimationFrame(r),!a.current)return;s(!1);return}r=requestAnimationFrame(t)})}}):null}])},963703,e=>{"use strict";var t=e.i(271645);let r=t.createContext(null);e.s(["StableCollection",0,function({children:e}){let n=t.useRef({groups:new Map,get(e,t){var r;let n=this.groups.get(e);n||(n=new Map,this.groups.set(e,n));let o=null!=(r=n.get(t))?r:0;return n.set(t,o+1),[Array.from(n.keys()).indexOf(t),function(){let e=n.get(t);e>1?n.set(t,e-1):n.delete(t)}]}});return t.createElement(r.Provider,{value:n},e)},"useStableCollectionIndex",0,function(e){let n=t.useContext(r);if(!n)throw Error("You must wrap your component in a ");let o=t.useId(),[s,a]=n.current.get(e,o);return t.useEffect(()=>a,[]),s}])},970554,e=>{"use strict";let t,r,n;var o=e.i(783222),s=e.i(433336),a=e.i(271645),i=e.i(394487),l=e.i(914189),u=e.i(835696),c=e.i(941444),d=e.i(144279),f=e.i(294316),p=e.i(640497),m=e.i(2788),b=e.i(652265),v=e.i(397701),h=e.i(368578),g=e.i(402155),y=e.i(700020),E=e.i(963703),T=e.i(998348),w=((t=w||{})[t.Forwards=0]="Forwards",t[t.Backwards=1]="Backwards",t),x=((r=x||{})[r.Less=-1]="Less",r[r.Equal=0]="Equal",r[r.Greater=1]="Greater",r),F=((n=F||{})[n.SetSelectedIndex=0]="SetSelectedIndex",n[n.RegisterTab=1]="RegisterTab",n[n.UnregisterTab=2]="UnregisterTab",n[n.RegisterPanel=3]="RegisterPanel",n[n.UnregisterPanel=4]="UnregisterPanel",n);let P={0(e,t){var r;let n=(0,b.sortByDomNode)(e.tabs,e=>e.current),o=(0,b.sortByDomNode)(e.panels,e=>e.current),s=n.filter(e=>{var t;return!(null!=(t=e.current)&&t.hasAttribute("disabled"))}),a={...e,tabs:n,panels:o};if(t.index<0||t.index>n.length-1){let r=(0,v.match)(Math.sign(t.index-e.selectedIndex),{[-1]:()=>1,0:()=>(0,v.match)(Math.sign(t.index),{[-1]:()=>0,0:()=>0,1:()=>1}),1:()=>0});if(0===s.length)return a;let o=(0,v.match)(r,{0:()=>n.indexOf(s[0]),1:()=>n.indexOf(s[s.length-1])});return{...a,selectedIndex:-1===o?e.selectedIndex:o}}let i=n.slice(0,t.index),l=[...n.slice(t.index),...i].find(e=>s.includes(e));if(!l)return a;let u=null!=(r=n.indexOf(l))?r:e.selectedIndex;return -1===u&&(u=e.selectedIndex),{...a,selectedIndex:u}},1(e,t){if(e.tabs.includes(t.tab))return e;let r=e.tabs[e.selectedIndex],n=(0,b.sortByDomNode)([...e.tabs,t.tab],e=>e.current),o=e.selectedIndex;return e.info.current.isControlled||-1===(o=n.indexOf(r))&&(o=e.selectedIndex),{...e,tabs:n,selectedIndex:o}},2:(e,t)=>({...e,tabs:e.tabs.filter(e=>e!==t.tab)}),3:(e,t)=>e.panels.includes(t.panel)?e:{...e,panels:(0,b.sortByDomNode)([...e.panels,t.panel],e=>e.current)},4:(e,t)=>({...e,panels:e.panels.filter(e=>e!==t.panel)})},k=(0,a.createContext)(null);function L(e){let t=(0,a.useContext)(k);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,L),t}return t}k.displayName="TabsDataContext";let N=(0,a.createContext)(null);function C(e){let t=(0,a.useContext)(N);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,C),t}return t}function I(e,t){return(0,v.match)(t.type,P,e,t)}N.displayName="TabsActionsContext";let S=y.RenderFeatures.RenderStrategy|y.RenderFeatures.Static,A=Object.assign((0,y.forwardRefWithAs)(function(e,t){var r,n;let c=(0,a.useId)(),{id:p=`headlessui-tabs-tab-${c}`,disabled:m=!1,autoFocus:w=!1,...x}=e,{orientation:F,activation:P,selectedIndex:k,tabs:N,panels:I}=L("Tab"),S=C("Tab"),A=L("Tab"),[M,R]=(0,a.useState)(null),O=(0,a.useRef)(null),D=(0,f.useSyncRefs)(O,t,R);(0,u.useIsoMorphicEffect)(()=>S.registerTab(O),[S,O]);let H=(0,E.useStableCollectionIndex)("tabs"),j=N.indexOf(O);-1===j&&(j=H);let K=j===k,W=(0,l.useEvent)(e=>{var t;let r=e();if(r===b.FocusResult.Success&&"auto"===P){let e=null==(t=(0,g.getOwnerDocument)(O))?void 0:t.activeElement,r=A.tabs.findIndex(t=>t.current===e);-1!==r&&S.change(r)}return r}),B=(0,l.useEvent)(e=>{let t=N.map(e=>e.current).filter(Boolean);if(e.key===T.Keys.Space||e.key===T.Keys.Enter){e.preventDefault(),e.stopPropagation(),S.change(j);return}switch(e.key){case T.Keys.Home:case T.Keys.PageUp:return e.preventDefault(),e.stopPropagation(),W(()=>(0,b.focusIn)(t,b.Focus.First));case T.Keys.End:case T.Keys.PageDown:return e.preventDefault(),e.stopPropagation(),W(()=>(0,b.focusIn)(t,b.Focus.Last))}if(W(()=>(0,v.match)(F,{vertical:()=>e.key===T.Keys.ArrowUp?(0,b.focusIn)(t,b.Focus.Previous|b.Focus.WrapAround):e.key===T.Keys.ArrowDown?(0,b.focusIn)(t,b.Focus.Next|b.Focus.WrapAround):b.FocusResult.Error,horizontal:()=>e.key===T.Keys.ArrowLeft?(0,b.focusIn)(t,b.Focus.Previous|b.Focus.WrapAround):e.key===T.Keys.ArrowRight?(0,b.focusIn)(t,b.Focus.Next|b.Focus.WrapAround):b.FocusResult.Error}))===b.FocusResult.Success)return e.preventDefault()}),V=(0,a.useRef)(!1),_=(0,l.useEvent)(()=>{var e;V.current||(V.current=!0,null==(e=O.current)||e.focus({preventScroll:!0}),S.change(j),(0,h.microTask)(()=>{V.current=!1}))}),G=(0,l.useEvent)(e=>{e.preventDefault()}),{isFocusVisible:U,focusProps:$}=(0,o.useFocusRing)({autoFocus:w}),{isHovered:q,hoverProps:X}=(0,s.useHover)({isDisabled:m}),{pressed:Y,pressProps:z}=(0,i.useActivePress)({disabled:m}),Z=(0,a.useMemo)(()=>({selected:K,hover:q,active:Y,focus:U,autofocus:w,disabled:m}),[K,q,U,Y,w,m]),J=(0,y.mergeProps)({ref:D,onKeyDown:B,onMouseDown:G,onClick:_,id:p,role:"tab",type:(0,d.useResolveButtonType)(e,M),"aria-controls":null==(n=null==(r=I[j])?void 0:r.current)?void 0:n.id,"aria-selected":K,tabIndex:K?0:-1,disabled:m||void 0,autoFocus:w},$,X,z);return(0,y.useRender)()({ourProps:J,theirProps:x,slot:Z,defaultTag:"button",name:"Tabs.Tab"})}),{Group:(0,y.forwardRefWithAs)(function(e,t){let{defaultIndex:r=0,vertical:n=!1,manual:o=!1,onChange:s,selectedIndex:i=null,...d}=e,m=n?"vertical":"horizontal",v=o?"manual":"auto",h=null!==i,g=(0,c.useLatestValue)({isControlled:h}),T=(0,f.useSyncRefs)(t),[w,x]=(0,a.useReducer)(I,{info:g,selectedIndex:null!=i?i:r,tabs:[],panels:[]}),F=(0,a.useMemo)(()=>({selectedIndex:w.selectedIndex}),[w.selectedIndex]),P=(0,c.useLatestValue)(s||(()=>{})),L=(0,c.useLatestValue)(w.tabs),C=(0,a.useMemo)(()=>({orientation:m,activation:v,...w}),[m,v,w]),S=(0,l.useEvent)(e=>(x({type:1,tab:e}),()=>x({type:2,tab:e}))),A=(0,l.useEvent)(e=>(x({type:3,panel:e}),()=>x({type:4,panel:e}))),M=(0,l.useEvent)(e=>{R.current!==e&&P.current(e),h||x({type:0,index:e})}),R=(0,c.useLatestValue)(h?e.selectedIndex:w.selectedIndex),O=(0,a.useMemo)(()=>({registerTab:S,registerPanel:A,change:M}),[]);(0,u.useIsoMorphicEffect)(()=>{x({type:0,index:null!=i?i:r})},[i]),(0,u.useIsoMorphicEffect)(()=>{if(void 0===R.current||w.tabs.length<=0)return;let e=(0,b.sortByDomNode)(w.tabs,e=>e.current);e.some((e,t)=>w.tabs[t]!==e)&&M(e.indexOf(w.tabs[R.current]))});let D=(0,y.useRender)();return a.default.createElement(E.StableCollection,null,a.default.createElement(N.Provider,{value:O},a.default.createElement(k.Provider,{value:C},C.tabs.length<=0&&a.default.createElement(p.FocusSentinel,{onFocus:()=>{var e,t;for(let r of L.current)if((null==(e=r.current)?void 0:e.tabIndex)===0)return null==(t=r.current)||t.focus(),!0;return!1}}),D({ourProps:{ref:T},theirProps:d,slot:F,defaultTag:"div",name:"Tabs"}))))}),List:(0,y.forwardRefWithAs)(function(e,t){let{orientation:r,selectedIndex:n}=L("Tab.List"),o=(0,f.useSyncRefs)(t),s=(0,a.useMemo)(()=>({selectedIndex:n}),[n]);return(0,y.useRender)()({ourProps:{ref:o,role:"tablist","aria-orientation":r},theirProps:e,slot:s,defaultTag:"div",name:"Tabs.List"})}),Panels:(0,y.forwardRefWithAs)(function(e,t){let{selectedIndex:r}=L("Tab.Panels"),n=(0,f.useSyncRefs)(t),o=(0,a.useMemo)(()=>({selectedIndex:r}),[r]);return(0,y.useRender)()({ourProps:{ref:n},theirProps:e,slot:o,defaultTag:"div",name:"Tabs.Panels"})}),Panel:(0,y.forwardRefWithAs)(function(e,t){var r,n,s,i;let l=(0,a.useId)(),{id:c=`headlessui-tabs-panel-${l}`,tabIndex:d=0,...p}=e,{selectedIndex:b,tabs:v,panels:h}=L("Tab.Panel"),g=C("Tab.Panel"),T=(0,a.useRef)(null),w=(0,f.useSyncRefs)(T,t);(0,u.useIsoMorphicEffect)(()=>g.registerPanel(T),[g,T]);let x=(0,E.useStableCollectionIndex)("panels"),F=h.indexOf(T);-1===F&&(F=x);let P=F===b,{isFocusVisible:k,focusProps:N}=(0,o.useFocusRing)(),I=(0,a.useMemo)(()=>({selected:P,focus:k}),[P,k]),A=(0,y.mergeProps)({ref:w,id:c,role:"tabpanel","aria-labelledby":null==(n=null==(r=v[F])?void 0:r.current)?void 0:n.id,tabIndex:P?d:-1},N),M=(0,y.useRender)();return P||null!=(s=p.unmount)&&!s||null!=(i=p.static)&&i?M({ourProps:A,theirProps:p,slot:I,defaultTag:"div",features:S,visible:P,name:"Tabs.Panel"}):a.default.createElement(m.Hidden,{"aria-hidden":"true",...A})})});e.s(["Tab",0,A])},405371,910342,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(480731);let o=(0,r.createContext)(n.BaseColors.Blue);e.s(["default",0,o],910342);var s=e.i(970554),a=e.i(444755);let i=(0,e.i(673706).makeClassName)("TabList"),l=(0,r.createContext)("line"),u={line:(0,a.tremorTwMerge)("flex border-b space-x-4","border-tremor-border","dark:border-dark-tremor-border"),solid:(0,a.tremorTwMerge)("inline-flex p-0.5 rounded-tremor-default space-x-1.5","bg-tremor-background-subtle","dark:bg-dark-tremor-background-subtle")},c=r.default.forwardRef((e,n)=>{let{color:c,variant:d="line",children:f,className:p}=e,m=(0,t.__rest)(e,["color","variant","children","className"]);return r.default.createElement(s.Tab.List,Object.assign({ref:n,className:(0,a.tremorTwMerge)(i("root"),"justify-start overflow-x-clip",u[d],p)},m),r.default.createElement(l.Provider,{value:d},r.default.createElement(o.Provider,{value:c},f)))});c.displayName="TabList",e.s(["TabVariantContext",0,l,"default",0,c],405371)},197647,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(95779),o=e.i(444755),s=e.i(673706),a=e.i(271645),i=e.i(405371),l=e.i(910342);let u=(0,s.makeClassName)("Tab"),c=a.default.forwardRef((e,c)=>{let{icon:d,className:f,children:p}=e,m=(0,t.__rest)(e,["icon","className","children"]),b=(0,a.useContext)(i.TabVariantContext),v=(0,a.useContext)(l.default);return a.default.createElement(r.Tab,Object.assign({ref:c,className:(0,o.tremorTwMerge)(u("root"),"flex whitespace-nowrap truncate max-w-xs outline-none data-focus-visible:ring text-tremor-default transition duration-100",function(e,t){switch(e){case"line":return(0,o.tremorTwMerge)("data-[selected]:border-b-2 hover:border-b-2 border-transparent transition duration-100 -mb-px px-2 py-2","hover:border-tremor-content hover:text-tremor-content-emphasis text-tremor-content","[&:not([data-selected])]:dark:hover:border-dark-tremor-content-emphasis [&:not([data-selected])]:dark:hover:text-dark-tremor-content-emphasis [&:not([data-selected])]:dark:text-dark-tremor-content",t?(0,s.getColorClassNames)(t,n.colorPalette.border).selectBorderColor:["data-[selected]:border-tremor-brand data-[selected]:text-tremor-brand","data-[selected]:dark:border-dark-tremor-brand data-[selected]:dark:text-dark-tremor-brand"]);case"solid":return(0,o.tremorTwMerge)("border-transparent border rounded-tremor-small px-2.5 py-1","data-[selected]:border-tremor-border data-[selected]:bg-tremor-background data-[selected]:shadow-tremor-input [&:not([data-selected])]:hover:text-tremor-content-emphasis data-[selected]:text-tremor-brand [&:not([data-selected])]:text-tremor-content","dark:data-[selected]:border-dark-tremor-border dark:data-[selected]:bg-dark-tremor-background dark:data-[selected]:shadow-dark-tremor-input dark:[&:not([data-selected])]:hover:text-dark-tremor-content-emphasis dark:data-[selected]:text-dark-tremor-brand dark:[&:not([data-selected])]:text-dark-tremor-content",t?(0,s.getColorClassNames)(t,n.colorPalette.text).selectTextColor:"text-tremor-content dark:text-dark-tremor-content")}}(b,v),f,v&&(0,s.getColorClassNames)(v,n.colorPalette.text).selectTextColor)},m),d?a.default.createElement(d,{className:(0,o.tremorTwMerge)(u("icon"),"flex-none h-5 w-5",p?"mr-2":"")}):null,p?a.default.createElement("span",null,p):null)});c.displayName="Tab",e.s(["Tab",0,c],197647)},653824,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(444755),o=e.i(673706),s=e.i(271645);let a=(0,o.makeClassName)("TabGroup"),i=s.default.forwardRef((e,o)=>{let{defaultIndex:i,index:l,onIndexChange:u,children:c,className:d}=e,f=(0,t.__rest)(e,["defaultIndex","index","onIndexChange","children","className"]);return s.default.createElement(r.Tab.Group,Object.assign({as:"div",ref:o,defaultIndex:i,selectedIndex:l,onChange:u,className:(0,n.tremorTwMerge)(a("root"),"w-full",d)},f),c)});i.displayName="TabGroup",e.s(["TabGroup",0,i],653824)},881073,e=>{"use strict";var t=e.i(405371);e.s(["TabList",()=>t.default])},723731,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(751734),o=e.i(144582),s=e.i(444755),a=e.i(673706),i=e.i(271645);let l=(0,a.makeClassName)("TabPanels"),u=i.default.forwardRef((e,a)=>{let{children:u,className:c}=e,d=(0,t.__rest)(e,["children","className"]);return i.default.createElement(r.Tab.Panels,Object.assign({as:"div",ref:a,className:(0,s.tremorTwMerge)(l("root"),"w-full",c)},d),({selectedIndex:e})=>i.default.createElement(o.default.Provider,{value:{selectedValue:e}},i.default.Children.map(u,(e,t)=>i.default.createElement(n.default.Provider,{value:t},e))))});u.displayName="TabPanels",e.s(["TabPanels",0,u],723731)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/027d2u2cl335o.js b/litellm/proxy/_experimental/out/_next/static/chunks/027d2u2cl335o.js new file mode 100644 index 00000000000..b410c1ed1a1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/027d2u2cl335o.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,772345,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let s={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M168 504.2c1-43.7 10-86.1 26.9-126 17.3-41 42.1-77.7 73.7-109.4S337 212.3 378 195c42.4-17.9 87.4-27 133.9-27s91.5 9.1 133.8 27A341.5 341.5 0 01755 268.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.7 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c0-6.7-7.7-10.5-12.9-6.3l-56.4 44.1C765.8 155.1 646.2 92 511.8 92 282.7 92 96.3 275.6 92 503.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8zm756 7.8h-60c-4.4 0-7.9 3.5-8 7.8-1 43.7-10 86.1-26.9 126-17.3 41-42.1 77.8-73.7 109.4A342.45 342.45 0 01512.1 856a342.24 342.24 0 01-243.2-100.8c-9.9-9.9-19.2-20.4-27.8-31.4l60.2-47a8 8 0 00-3-14.1l-175.7-43c-5-1.2-9.9 2.6-9.9 7.7l-.7 181c0 6.7 7.7 10.5 12.9 6.3l56.4-44.1C258.2 868.9 377.8 932 512.2 932c229.2 0 415.5-183.7 419.8-411.8a8 8 0 00-8-8.2z"}}]},name:"sync",theme:"outlined"};var l=e.i(9583),i=a.forwardRef(function(e,i){return a.createElement(l.default,(0,t.default)({},e,{ref:i,icon:s}))});e.s(["SyncOutlined",0,i],772345)},11751,e=>{"use strict";e.s(["mapEmptyStringToNull",0,function(e){return""===e?null:e}])},643449,e=>{"use strict";var t=e.i(843476),a=e.i(262218),s=e.i(810757),l=e.i(477386),i=e.i(557662),r=e.i(555987);e.s(["default",0,function({loggingConfigs:e=[],disabledCallbacks:n=[],variant:o="card",className:d=""}){let c=(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(s.CogIcon,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900",children:"Logging Integrations"}),(0,t.jsx)(a.Tag,{color:"blue",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"space-y-3",children:e.map((e,l)=>{var n;let o=(n=e.callback_name,Object.entries(i.callback_map).find(([e,t])=>t===n)?.[0]||n),d=(0,r.resolveLogoSrc)(i.callbackInfo[o]?.logo);return(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[d?(0,t.jsx)("img",{src:d,alt:o,className:"w-5 h-5 object-contain"}):(0,t.jsx)(s.CogIcon,{className:"h-5 w-5 text-gray-400"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-medium text-blue-800",children:o}),(0,t.jsxs)("span",{className:"block text-xs text-blue-600",children:[Object.keys(e.callback_vars).length," parameters configured"]})]})]}),(0,t.jsx)(a.Tag,{color:(e=>{switch(e){case"success":return"green";case"failure":return"red";case"success_and_failure":return"blue";default:return}})(e.callback_type),children:(e=>{switch(e){case"success":return"Success Only";case"failure":return"Failure Only";case"success_and_failure":return"Success & Failure";default:return e}})(e.callback_type)})]},l)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(s.CogIcon,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)("span",{className:"text-gray-500 text-sm",children:"No logging integrations configured"})]})]}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l.BanIcon,{className:"h-4 w-4 text-red-600"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900",children:"Disabled Callbacks"}),(0,t.jsx)(a.Tag,{color:"red",children:n.length})]}),n.length>0?(0,t.jsx)("div",{className:"space-y-3",children:n.map((e,s)=>{let n=i.reverse_callback_map[e]||e,o=(0,r.resolveLogoSrc)(i.callbackInfo[n]?.logo);return(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[o?(0,t.jsx)("img",{src:o,alt:n,className:"w-5 h-5 object-contain"}):(0,t.jsx)(l.BanIcon,{className:"h-5 w-5 text-gray-400"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-medium text-red-800",children:n}),(0,t.jsx)("span",{className:"block text-xs text-red-600",children:"Disabled for this key"})]})]}),(0,t.jsx)(a.Tag,{color:"red",children:"Disabled"})]},s)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l.BanIcon,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)("span",{className:"text-gray-500 text-sm",children:"No callbacks disabled"})]})]})]});return"card"===o?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${d}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-semibold text-gray-900",children:"Logging Settings"}),(0,t.jsx)("span",{className:"block text-xs text-gray-500",children:"Active logging integrations and disabled callbacks for this key"})]})}),c]}):(0,t.jsxs)("div",{className:`${d}`,children:[(0,t.jsx)("span",{className:"block font-medium text-gray-900 mb-3",children:"Logging Settings"}),c]})}])},183588,e=>{"use strict";var t=e.i(843476),a=e.i(266484);e.s(["default",0,({value:e,onChange:s,disabledCallbacks:l=[],onDisabledCallbacksChange:i})=>(0,t.jsx)(a.default,{value:e,onChange:s,disabledCallbacks:l,onDisabledCallbacksChange:i})])},214541,e=>{"use strict";var t=e.i(271645),a=e.i(135214),s=e.i(270345);e.s(["default",0,()=>{let[e,l]=(0,t.useState)([]),{accessToken:i,userId:r,userRole:n}=(0,a.default)();return(0,t.useEffect)(()=>{(async()=>{l(await (0,s.fetchTeams)(i,r,n,null))})()},[i,r,n]),{teams:e,setTeams:l}}])},784647,505022,721929,e=>{"use strict";var t=e.i(843476),a=e.i(464571),s=e.i(898586),l=e.i(592968),i=e.i(770914),r=e.i(312361),n=e.i(525720),o=e.i(282786),d=e.i(447566),c=e.i(772345),m=e.i(955135),u=e.i(646563),x=e.i(771674),p=e.i(72713),g=e.i(637235),h=e.i(962944);e.i(247167);var _=e.i(931067),j=e.i(271645);let y={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M866.9 169.9L527.1 54.1C523 52.7 517.5 52 512 52s-11 .7-15.1 2.1L157.1 169.9c-8.3 2.8-15.1 12.4-15.1 21.2v482.4c0 8.8 5.7 20.4 12.6 25.9L499.3 968c3.5 2.7 8 4.1 12.6 4.1s9.2-1.4 12.6-4.1l344.7-268.6c6.9-5.4 12.6-17 12.6-25.9V191.1c.2-8.8-6.6-18.3-14.9-21.2zM810 654.3L512 886.5 214 654.3V226.7l298-101.6 298 101.6v427.6zm-405.8-201c-3-4.1-7.8-6.6-13-6.6H336c-6.5 0-10.3 7.4-6.5 12.7l126.4 174a16.1 16.1 0 0026 0l212.6-292.7c3.8-5.3 0-12.7-6.5-12.7h-55.2c-5.1 0-10 2.5-13 6.6L468.9 542.4l-64.7-89.1z"}}]},name:"safety-certificate",theme:"outlined"};var b=e.i(9583),f=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:y}))});let v={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M668.6 320c0-4.4-3.6-8-8-8h-54.5c-3 0-5.8 1.7-7.1 4.4l-84.7 168.8H511l-84.7-168.8a8 8 0 00-7.1-4.4h-55.7c-1.3 0-2.6.3-3.8 1-3.9 2.1-5.3 7-3.2 10.8l103.9 191.6h-57c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76v39h-76c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76V704c0 4.4 3.6 8 8 8h49.9c4.4 0 8-3.6 8-8v-63.5h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8h-76.3v-39h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8H564l103.7-191.6c.5-1.1.9-2.4.9-3.7zM157.9 504.2a352.7 352.7 0 01103.5-242.4c32.5-32.5 70.3-58.1 112.4-75.9 43.6-18.4 89.9-27.8 137.6-27.8 47.8 0 94.1 9.3 137.6 27.8 42.1 17.8 79.9 43.4 112.4 75.9 10 10 19.3 20.5 27.9 31.4l-50 39.1a8 8 0 003 14.1l156.8 38.3c5 1.2 9.9-2.6 9.9-7.7l.8-161.5c0-6.7-7.7-10.5-12.9-6.3l-47.8 37.4C770.7 146.3 648.6 82 511.5 82 277 82 86.3 270.1 82 503.8a8 8 0 008 8.2h60c4.3 0 7.8-3.5 7.9-7.8zM934 512h-60c-4.3 0-7.9 3.5-8 7.8a352.7 352.7 0 01-103.5 242.4 352.57 352.57 0 01-112.4 75.9c-43.6 18.4-89.9 27.8-137.6 27.8s-94.1-9.3-137.6-27.8a352.57 352.57 0 01-112.4-75.9c-10-10-19.3-20.5-27.9-31.4l49.9-39.1a8 8 0 00-3-14.1l-156.8-38.3c-5-1.2-9.9 2.6-9.9 7.7l-.8 161.7c0 6.7 7.7 10.5 12.9 6.3l47.8-37.4C253.3 877.7 375.4 942 512.5 942 747 942 937.7 753.9 942 520.2a8 8 0 00-8-8.2z"}}]},name:"transaction",theme:"outlined"};var k=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:v}))}),N={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"defs",attrs:{},children:[{tag:"style",attrs:{}}]},{tag:"path",attrs:{d:"M945 412H689c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h256c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM811 548H689c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h122c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM477.3 322.5H434c-6.2 0-11.2 5-11.2 11.2v248c0 3.6 1.7 6.9 4.6 9l148.9 108.6c5 3.6 12 2.6 15.6-2.4l25.7-35.1v-.1c3.6-5 2.5-12-2.5-15.6l-126.7-91.6V333.7c.1-6.2-5-11.2-11.1-11.2z"}},{tag:"path",attrs:{d:"M804.8 673.9H747c-5.6 0-10.9 2.9-13.9 7.7a321 321 0 01-44.5 55.7 317.17 317.17 0 01-101.3 68.3c-39.3 16.6-81 25-124 25-43.1 0-84.8-8.4-124-25-37.9-16-72-39-101.3-68.3s-52.3-63.4-68.3-101.3c-16.6-39.2-25-80.9-25-124 0-43.1 8.4-84.7 25-124 16-37.9 39-72 68.3-101.3 29.3-29.3 63.4-52.3 101.3-68.3 39.2-16.6 81-25 124-25 43.1 0 84.8 8.4 124 25 37.9 16 72 39 101.3 68.3a321 321 0 0144.5 55.7c3 4.8 8.3 7.7 13.9 7.7h57.8c6.9 0 11.3-7.2 8.2-13.3-65.2-129.7-197.4-214-345-215.7-216.1-2.7-395.6 174.2-396 390.1C71.6 727.5 246.9 903 463.2 903c149.5 0 283.9-84.6 349.8-215.8a9.18 9.18 0 00-8.2-13.3z"}}]},name:"field-time",theme:"outlined"},T=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:N}))}),w=e.i(304911);let{Text:S}=s.Typography;function C({label:e,value:a,icon:s,truncate:l=!1,copyable:r=!1,defaultUserIdCheck:n=!1}){let o=!a,d=n&&"default_user_id"===a,c=d?(0,t.jsx)(w.default,{userId:a}):(0,t.jsx)(S,{strong:!0,copyable:!!(r&&!o&&!d)&&{tooltips:[`Copy ${e}`,"Copied!"]},ellipsis:l,style:l?{maxWidth:160,display:"block"}:void 0,children:o?"-":a});return(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.Space,{size:4,children:[(0,t.jsx)(S,{type:"secondary",children:s}),(0,t.jsx)(S,{type:"secondary",style:{fontSize:12,textTransform:"uppercase",letterSpacing:"0.05em"},children:e})]}),(0,t.jsx)("div",{children:c})]})}let{Title:I,Text:A}=s.Typography;function F({userAlias:e,userEmail:a,userId:l}){let r=(0,t.jsxs)(i.Space,{size:4,children:[(0,t.jsx)(A,{type:"secondary",children:(0,t.jsx)(x.UserOutlined,{})}),(0,t.jsx)(A,{type:"secondary",style:{fontSize:12,textTransform:"uppercase",letterSpacing:"0.05em"},children:"User"})]});if(!e&&!a&&!l)return(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(A,{strong:!0,children:"-"})})]});let n="default_user_id"===l,d=e||a||l,c=(0,t.jsx)("div",{className:"flex flex-col gap-2 text-xs min-w-[200px] max-w-[300px]",children:[{label:"User Alias",value:e??null},{label:"User Email",value:a||null},{label:"User ID",value:l||null}].map(({label:e,value:a})=>(0,t.jsxs)("div",{className:"flex flex-col min-w-0",children:[(0,t.jsx)("span",{className:"text-gray-400",children:e}),a?(0,t.jsx)(s.Typography.Text,{className:"font-mono text-xs",style:{maxWidth:220},ellipsis:{tooltip:a},copyable:!0,children:a}):(0,t.jsx)("span",{className:"font-mono",children:"-"})]},e))});return!n||e||a?(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(o.Popover,{content:c,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)(A,{strong:!0,ellipsis:!0,style:{cursor:"default",maxWidth:200,display:"block"},children:d})})})]}):(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(o.Popover,{content:c,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"cursor-default",children:(0,t.jsx)(w.default,{userId:l})})})})]})}e.s(["KeyInfoHeader",0,function({data:e,onBack:s,onCreateNew:o,onRegenerate:x,onDelete:_,onResetSpend:j,canModifyKey:y=!0,backButtonText:b="Back to Keys",regenerateDisabled:v=!1,regenerateTooltip:N}){return(0,t.jsxs)("div",{children:[o&&(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(a.Button,{type:"primary",icon:(0,t.jsx)(u.PlusOutlined,{}),onClick:o,children:"Create New Key"})}),(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(a.Button,{type:"text",icon:(0,t.jsx)(d.ArrowLeftOutlined,{}),onClick:s,children:b})}),(0,t.jsxs)(n.Flex,{justify:"space-between",align:"start",style:{marginBottom:20},children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(I,{level:3,copyable:{tooltips:["Copy Key Alias","Copied!"]},style:{margin:0},children:e.keyName}),(0,t.jsxs)(A,{type:"secondary",copyable:{text:e.keyId,tooltips:["Copy Key ID","Copied!"]},children:["Key ID: ",e.keyId]})]}),y&&(0,t.jsxs)(i.Space,{children:[(0,t.jsx)(l.Tooltip,{title:N||"",children:(0,t.jsx)("span",{children:(0,t.jsx)(a.Button,{icon:(0,t.jsx)(c.SyncOutlined,{}),onClick:x,disabled:v,children:"Regenerate Key"})})}),j&&(0,t.jsx)(a.Button,{danger:!0,icon:(0,t.jsx)(k,{}),onClick:j,children:"Reset Spend"}),(0,t.jsx)(a.Button,{danger:!0,icon:(0,t.jsx)(m.DeleteOutlined,{}),onClick:_,children:"Delete Key"})]})]}),(0,t.jsxs)(n.Flex,{align:"stretch",gap:40,style:{marginBottom:40},children:[(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(F,{userAlias:e.userAlias,userEmail:e.userEmail,userId:e.userId}),(0,t.jsx)(C,{label:"Expires",value:e.expires,icon:(0,t.jsx)(T,{})})]}),(0,t.jsx)(r.Divider,{type:"vertical",style:{height:"auto"}}),(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(C,{label:"Created At",value:e.createdAt,icon:(0,t.jsx)(p.CalendarOutlined,{})}),(0,t.jsx)(C,{label:"Created By",value:e.createdBy,icon:(0,t.jsx)(f,{}),truncate:!0,copyable:!0,defaultUserIdCheck:!0})]}),(0,t.jsx)(r.Divider,{type:"vertical",style:{height:"auto"}}),(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(C,{label:"Last Updated",value:e.lastUpdated,icon:(0,t.jsx)(g.ClockCircleOutlined,{})}),(0,t.jsx)(C,{label:"Last Active",value:e.lastActive,icon:(0,t.jsx)(h.ThunderboltOutlined,{})})]})]})]})}],784647);var M=e.i(599724),L=e.i(389083),R=e.i(278587);let E=j.forwardRef(function(e,t){return j.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),j.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["default",0,({autoRotate:e=!1,rotationInterval:a,lastRotationAt:s,keyRotationAt:l,nextRotationAt:i,variant:r="card",className:n=""})=>{let o=e=>{let t=new Date(e),a=t.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),s=t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});return`${a} at ${s}`},d=(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsx)("div",{className:"space-y-3",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(R.RefreshIcon,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(M.Text,{className:"font-semibold text-gray-900",children:"Auto-Rotation"}),(0,t.jsx)(L.Badge,{color:e?"green":"gray",size:"xs",children:e?"Enabled":"Disabled"}),e&&a&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(M.Text,{className:"text-gray-400",children:"•"}),(0,t.jsxs)(M.Text,{className:"text-sm text-gray-600",children:["Every ",a]})]})]})}),(e||s||l||i)&&(0,t.jsxs)("div",{className:"space-y-3",children:[s&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-200 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-700",children:"Last Rotation"}),(0,t.jsx)(M.Text,{className:"text-sm text-gray-600",children:o(s)})]})]}),(l||i)&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-200 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-700",children:"Next Scheduled Rotation"}),(0,t.jsx)(M.Text,{className:"text-sm text-gray-600",children:o(i||l||"")})]})]}),e&&!s&&!l&&!i&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-100 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsx)(M.Text,{className:"text-gray-600",children:"No rotation history available"})]})]}),!e&&!s&&!l&&!i&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-100 rounded-md",children:[(0,t.jsx)(R.RefreshIcon,{className:"w-4 h-4 text-gray-400"}),(0,t.jsx)(M.Text,{className:"text-gray-600",children:"Auto-rotation is not enabled for this key"})]})]});return"card"===r?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${n}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(M.Text,{className:"font-semibold text-gray-900",children:"Auto-Rotation"}),(0,t.jsx)(M.Text,{className:"text-xs text-gray-500",children:"Automatic key rotation settings and status for this key"})]})}),d]}):(0,t.jsxs)("div",{className:`${n}`,children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-900 mb-3",children:"Auto-Rotation"}),d]})}],505022);let P=["logging"];e.s(["extractLoggingSettings",0,e=>e&&"object"==typeof e&&Array.isArray(e.logging)?e.logging:[],"formatMetadataForDisplay",0,(e,t=2)=>JSON.stringify(e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([e])=>!P.includes(e))):{},null,t),"stripTagsFromMetadata",0,e=>{if(!e||"object"!=typeof e)return e;let{tags:t,...a}=e;return a}],721929)},65932,272753,e=>{"use strict";var t=e.i(954616),a=e.i(912598),s=e.i(602869),l=e.i(431703),i=e.i(135214),r=e.i(207082);let n=async(e,t)=>{let a=(0,s.getProxyBaseUrl)(),i=`${a?`${a}/key/${t}/reset_spend`:`/key/${t}/reset_spend`}`,r=await fetch(i,{method:"POST",headers:{[(0,s.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({reset_to:0})});if(!r.ok){let e=await r.json(),t=(0,l.deriveErrorMessage)(e);throw(0,s.handleError)(t),Error(t)}return r.json()};e.s(["useResetKeySpend",0,()=>{let{accessToken:e}=(0,i.default)(),s=(0,a.useQueryClient)();return(0,t.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return n(e,t)},onSuccess:()=>{s.invalidateQueries({queryKey:r.keyKeys.all})}})}],65932);var o=e.i(843476),d=e.i(492030),c=e.i(166406),m=e.i(772345),u=e.i(560445),x=e.i(464571),p=e.i(178654),g=e.i(525720),h=e.i(808613),_=e.i(311451),j=e.i(28651),y=e.i(212931),b=e.i(621192),f=e.i(770914),v=e.i(898586),k=e.i(271645),N=e.i(237016),T=e.i(727749),w=e.i(24529);let{Text:S}=v.Typography,C={pattern:/^(\d+(s|m|h|d|w|mo))?$/,message:"Must be a duration like 30s, 30m, 24h, 2d, 1w, or 1mo"};e.s(["RegenerateKeyModal",0,function({selectedToken:e,visible:t,onClose:a,onKeyUpdate:l}){let{accessToken:r}=(0,i.default)(),[n]=h.Form.useForm(),[v,I]=(0,k.useState)(null),[A,F]=(0,k.useState)(!1),[M,L]=(0,k.useState)(!1),R=(0,w.isKeyExpired)(e?.expires),E=h.Form.useWatch("duration",n),P=R?[{required:!0,message:"Expiration is required for expired keys"},C]:[C];(0,k.useEffect)(()=>{t&&e&&r&&n.setFieldsValue({key_alias:e.key_alias,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,duration:e.duration||"",grace_period:""})},[t,e,n,r]);let O=E?(0,w.calculateExpiryPreviewFromDuration)(E):null,B=async()=>{if(e&&r){F(!0);try{let t=await n.validateFields(),a=await (0,s.regenerateKeyCall)(r,e.token||e.token_id,t);I(a.key),T.default.success("Virtual Key regenerated successfully");let i={...a,token:a.token||a.key_id||e.token,key_name:a.key,max_budget:t.max_budget,tpm_limit:t.tpm_limit,rpm_limit:t.rpm_limit,expires:a.expires??e.expires};l&&l(i),F(!1)}catch(e){if(F(!1),e&&"object"==typeof e&&"errorFields"in e)return;console.error("Error regenerating key:",e),T.default.fromBackend(e)}}},D=()=>{I(null),F(!1),L(!1),n.resetFields(),a()};return(0,o.jsx)(y.Modal,{title:"Regenerate Virtual Key",open:t,onCancel:D,width:520,maskClosable:!1,footer:v?[(0,o.jsxs)(f.Space,{children:[(0,o.jsx)(x.Button,{onClick:D,children:"Close"}),(0,o.jsx)(N.CopyToClipboard,{text:v,onCopy:()=>{L(!0)},children:(0,o.jsx)(x.Button,{type:"primary",icon:M?(0,o.jsx)(d.CheckOutlined,{}):(0,o.jsx)(c.CopyOutlined,{}),children:M?"Copied":"Copy Key"})})]},"footer-actions")]:[(0,o.jsxs)(f.Space,{children:[(0,o.jsx)(x.Button,{onClick:D,children:"Cancel"}),(0,o.jsx)(x.Button,{type:"primary",icon:(0,o.jsx)(m.SyncOutlined,{}),onClick:B,loading:A,children:"Regenerate"})]},"footer-actions")],children:v?(0,o.jsxs)(g.Flex,{vertical:!0,gap:"middle",children:[(0,o.jsx)(u.Alert,{type:"warning",showIcon:!0,message:"Save it now, you will not see it again"}),(0,o.jsxs)(g.Flex,{vertical:!0,gap:2,children:[(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Key Alias"}),(0,o.jsx)(S,{children:e?.key_alias||"No alias set"})]}),(0,o.jsxs)(g.Flex,{vertical:!0,gap:6,children:[(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Virtual Key"}),(0,o.jsx)("div",{style:{background:"#f5f5f5",border:"1px solid #e8e8e8",borderRadius:6,padding:"14px 16px",fontFamily:"SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace",fontSize:16,wordBreak:"break-all",color:"#262626"},children:v})]})]}):(0,o.jsxs)(h.Form,{form:n,layout:"vertical",style:{marginTop:4},children:[(0,o.jsx)(h.Form.Item,{name:"key_alias",label:"Key Alias",children:(0,o.jsx)(_.Input,{disabled:!0})}),(0,o.jsxs)(b.Row,{gutter:12,children:[(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,o.jsx)(j.InputNumber,{step:.01,precision:2,style:{width:"100%"}})})}),(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"tpm_limit",label:"TPM Limit",children:(0,o.jsx)(j.InputNumber,{style:{width:"100%"}})})}),(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"rpm_limit",label:"RPM Limit",children:(0,o.jsx)(j.InputNumber,{style:{width:"100%"}})})})]}),(0,o.jsxs)(b.Row,{gutter:12,children:[(0,o.jsx)(p.Col,{span:12,children:(0,o.jsx)(h.Form.Item,{name:"duration",label:"Expire Key",rules:P,extra:(0,o.jsxs)(g.Flex,{vertical:!0,gap:2,children:[(0,o.jsxs)(S,{type:R?"danger":"secondary",style:{fontSize:12},children:["Current expiry: ",e?.expires?(0,w.formatExpiresUtc)(e.expires):"Never",R&&" (expired)"]}),O&&(0,o.jsxs)(S,{type:"success",style:{fontSize:12},children:["New expiry: ",O]})]}),children:(0,o.jsx)(_.Input,{placeholder:"e.g. 30s, 30h, 30d"})})}),(0,o.jsx)(p.Col,{span:12,children:(0,o.jsx)(h.Form.Item,{name:"grace_period",label:"Grace Period",tooltip:"Keep the old key valid for this duration after rotation. Both keys work during this period for seamless cutover. Empty = immediate revoke.",extra:(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Recommended: 24h to 72h for production keys"}),rules:[C],children:(0,o.jsx)(_.Input,{placeholder:"e.g. 24h, 2d"})})})]})]})})}],272753)},20147,e=>{"use strict";var t=e.i(843476),a=e.i(135214),s=e.i(510674),l=e.i(292639),i=e.i(214541),r=e.i(500330),n=e.i(11751),o=e.i(530212),d=e.i(389083),c=e.i(994388),m=e.i(304967),u=e.i(350967),x=e.i(197647),p=e.i(653824),g=e.i(881073),h=e.i(404206),_=e.i(723731),j=e.i(599724),y=e.i(629569),b=e.i(808613),f=e.i(212931),v=e.i(262218),k=e.i(784647),N=e.i(271645),T=e.i(708347),w=e.i(557662),S=e.i(505022),C=e.i(127952),I=e.i(721929),A=e.i(643449),F=e.i(727749),M=e.i(602869),L=e.i(65932),R=e.i(207082),E=e.i(912598),P=e.i(384767),O=e.i(272753),B=e.i(190702),D=e.i(891547),z=e.i(109799),K=e.i(921511),$=e.i(827252),U=e.i(779241),V=e.i(311451),W=e.i(199133),G=e.i(790848),q=e.i(592968),H=e.i(552130),J=e.i(9314),Q=e.i(392110),Y=e.i(844565),X=e.i(939510),Z=e.i(363256),ee=e.i(128233),et=e.i(319312),ea=e.i(833400),es=e.i(355619),el=e.i(75921),ei=e.i(234713),er=e.i(390605),en=e.i(702597),eo=e.i(435451),ed=e.i(183588),ec=e.i(916940);function em({keyData:e,onCancel:a,onSubmit:i,teams:r,accessToken:n,userID:o,userRole:d,premiumUser:m=!1}){let u=m||null!=d&&T.rolesWithWriteAccess.includes(d),[x]=b.Form.useForm(),[p,g]=(0,N.useState)([]),[h,_]=(0,N.useState)({}),j=r?.find(t=>t.team_id===e.team_id),[y,f]=(0,N.useState)([]),[v,k]=(0,N.useState)(Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[]),[S,C]=(0,N.useState)(e.organization_id||null),[A,L]=(0,N.useState)(e.auto_rotate||!1),[R,E]=(0,N.useState)(e.rotation_interval||""),[P,O]=(0,N.useState)(!e.expires),[B,eu]=(0,N.useState)(!1),[ex,ep]=(0,N.useState)(Array.isArray(e.budget_limits)?e.budget_limits:[]),[eg,eh]=(0,N.useState)((0,ea.tagLimitsToRows)(e.metadata?.tag_rpm_limit)),[e_,ej]=(0,N.useState)(e.budget_fallbacks&&"object"==typeof e.budget_fallbacks?e.budget_fallbacks:{}),{data:ey,isLoading:eb}=(0,z.useOrganizations)(),{data:ef}=(0,s.useProjects)(),{data:ev}=(0,l.useUISettings)(),ek=!!ev?.values?.enable_projects_ui,eN=!!e.project_id,eT=(()=>{if(!e.project_id)return null;let t=ef?.find(t=>t.project_id===e.project_id);return t?.project_alias?`${t.project_alias} (${e.project_id})`:e.project_id})();(0,N.useEffect)(()=>{let t=async()=>{if(o&&d&&n)try{if(null===e.team_id){let e=(await (0,M.modelAvailableCall)(n,o,d)).data.map(e=>e.id);f((0,es.excludeProxyWideSentinel)(e))}else if(j?.team_id){let e=await (0,en.fetchTeamModels)(o,d,n,j.team_id);f((0,es.excludeProxyWideSentinel)(Array.from(new Set([...j.models,...e]))))}}catch(e){console.error("Error fetching models:",e)}};(async()=>{if(n)try{let e=await (0,M.getPromptsList)(n);g(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}})(),t()},[o,d,n,j,e.team_id]),(0,N.useEffect)(()=>{x.setFieldValue("disabled_callbacks",v)},[x,v]);let ew=e=>e&&({"24h":"daily","7d":"weekly","30d":"monthly"})[e]||null,eS={...e,token:e.token||e.token_id,budget_duration:ew(e.budget_duration),metadata:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(e.metadata)),guardrails:e.metadata?.guardrails,disable_global_guardrails:e.metadata?.disable_global_guardrails||!1,throttle_on_budget_exceeded:e.metadata?.throttle_on_budget_exceeded||!1,prompts:e.metadata?.prompts,tags:e.metadata?.tags,vector_stores:e.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:e.object_permission?.mcp_servers||[],accessGroups:e.object_permission?.mcp_access_groups||[]},mcp_tool_permissions:e.object_permission?.mcp_tool_permissions||{},agents_and_groups:{agents:e.object_permission?.agents||[],accessGroups:e.object_permission?.agent_access_groups||[]},logging_settings:(0,I.extractLoggingSettings)(e.metadata),disabled_callbacks:Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[],access_group_ids:e.access_group_ids||[],auto_rotate:e.auto_rotate||!1,...e.rotation_interval&&{rotation_interval:e.rotation_interval},allowed_routes:Array.isArray(e.allowed_routes)&&e.allowed_routes.length>0?e.allowed_routes.join(", "):""};(0,N.useEffect)(()=>{x.setFieldsValue({...e,token:e.token||e.token_id,budget_duration:ew(e.budget_duration),metadata:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(e.metadata)),guardrails:e.metadata?.guardrails,disable_global_guardrails:e.metadata?.disable_global_guardrails||!1,prompts:e.metadata?.prompts,tags:e.metadata?.tags,vector_stores:e.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:e.object_permission?.mcp_servers||[],accessGroups:e.object_permission?.mcp_access_groups||[]},mcp_tool_permissions:e.object_permission?.mcp_tool_permissions||{},throttle_on_budget_exceeded:e.metadata?.throttle_on_budget_exceeded||!1,logging_settings:(0,I.extractLoggingSettings)(e.metadata),disabled_callbacks:Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[],access_group_ids:e.access_group_ids||[],auto_rotate:e.auto_rotate||!1,...e.rotation_interval&&{rotation_interval:e.rotation_interval},allowed_routes:Array.isArray(e.allowed_routes)&&e.allowed_routes.length>0?e.allowed_routes.join(", "):""})},[e,x]),(0,N.useEffect)(()=>{x.setFieldValue("auto_rotate",A)},[A,x]),(0,N.useEffect)(()=>{R&&x.setFieldValue("rotation_interval",R)},[R,x]),(0,N.useEffect)(()=>{(async()=>{if(n)try{let e=await (0,M.tagListCall)(n);_(e)}catch(e){F.default.fromBackend("Error fetching tags: "+e)}})()},[n]);let eC=async t=>{try{if(eu(!0),"string"==typeof t.allowed_routes){let e=t.allowed_routes.trim();""===e?t.allowed_routes=[]:t.allowed_routes=e.split(",").map(e=>e.trim()).filter(e=>e.length>0)}let a=new Set(Array.isArray(e.allowed_routes)?e.allowed_routes:[]),s=new Set(Array.isArray(t.allowed_routes)?t.allowed_routes:[]);a.size===s.size&&[...s].every(e=>a.has(e))&&delete t.allowed_routes,P&&(t.duration=null);let l=ex.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);l.length>0?t.budget_limits=l:0===ex.length&&(t.budget_limits=[]);let{tag_rpm_limit:r}=(0,ea.tagRowsToLimits)(eg);t.tag_rpm_limit=r;let n=null!=e.budget_fallbacks&&Object.keys(e.budget_fallbacks).length>0;Object.keys(e_).length>0?t.budget_fallbacks=e_:n&&(t.budget_fallbacks={}),await i(t)}finally{eu(!1)}};return(0,t.jsxs)(b.Form,{form:x,onFinish:eC,initialValues:eS,layout:"vertical",children:[(0,t.jsx)(b.Form.Item,{label:"Key Alias",name:"key_alias",children:(0,t.jsx)(U.TextInput,{})}),(0,t.jsx)(b.Form.Item,{label:"Models",name:"models",children:(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_routes!==t.allowed_routes||e.models!==t.models,children:({getFieldValue:a,setFieldValue:s})=>{let l=a("allowed_routes")||"",i="string"==typeof l&&""!==l.trim()?l.split(",").map(e=>e.trim()).filter(e=>e.length>0):[],r=i.includes("management_routes")||i.includes("info_routes"),n=a("models")||[];return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(W.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:r,value:r?[]:n,onChange:e=>{e.includes("all-team-models")?s("models",["all-team-models"]):e.includes("all-proxy-models")?s("models",["all-proxy-models"]):s("models",e)},children:[null!=e.team_id?null!=j&&(0,t.jsx)(W.Select.Option,{value:"all-team-models",children:"All Team Models"}):(0,t.jsx)(W.Select.Option,{value:"all-proxy-models",children:"All Proxy Models"}),y.map(e=>(0,t.jsx)(W.Select.Option,{value:e,disabled:(0,es.hasAllModelsSentinel)(n),children:e},e))]}),r&&(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Models field is disabled for this key type"})]})}})}),(0,t.jsx)(b.Form.Item,{label:"Key Type",children:(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_routes!==t.allowed_routes,children:({getFieldValue:e,setFieldValue:a})=>{var s;let l=e("allowed_routes")||"",i=(s="string"==typeof l&&""!==l.trim()?l.split(",").map(e=>e.trim()).filter(e=>e.length>0):[])&&0!==s.length?s.includes("llm_api_routes")?"llm_api":s.includes("management_routes")?"management":s.includes("info_routes")?"read_only":"default":"default";return(0,t.jsxs)(W.Select,{placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",value:i,onChange:e=>{switch(e){case"default":a("allowed_routes","");break;case"llm_api":a("allowed_routes","llm_api_routes");break;case"management":a("allowed_routes","management_routes"),a("models",[])}},children:[(0,t.jsx)(W.Select.Option,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Full Access"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})}),(0,t.jsx)(W.Select.Option,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"AI APIs"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(W.Select.Option,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Management"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call only management routes (user/team/key management)"})]})})]})}})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Routes"," ",(0,t.jsx)(q.Tooltip,{title:"List of allowed routes for the key (comma-separated). Can be specific routes (e.g., '/chat/completions') or route patterns (e.g., 'llm_api_routes', 'management_routes', '/keys/*'). Leave empty to allow all routes.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_routes",children:(0,t.jsx)(V.Input,{placeholder:"Enter allowed routes (comma-separated). Special values: llm_api_routes, management_routes. Examples: llm_api_routes, /chat/completions, /keys/*. Leave empty to allow all routes"})}),(0,t.jsx)(b.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(eo.default,{step:.01,style:{width:"100%"},placeholder:"Enter a numerical value"})}),(0,t.jsx)(b.Form.Item,{label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(W.Select,{placeholder:"n/a",children:[(0,t.jsx)(W.Select.Option,{value:"daily",children:"Daily"}),(0,t.jsx)(W.Select.Option,{value:"weekly",children:"Weekly"}),(0,t.jsx)(W.Select.Option,{value:"monthly",children:"Monthly"})]})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(q.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(et.BudgetWindowsEditor,{value:ex,onChange:ep})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(q.Tooltip,{title:"When a model exceeds its per-model budget, requests automatically reroute to fallback models instead of failing",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(ee.BudgetFallbacksEditor,{value:e_,onChange:ej,availableModels:y})}),(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(X.default,{type:"tpm",name:"tpm_limit_type",showDetailedDescriptions:!1}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(X.default,{type:"rpm",name:"rpm_limit_type",showDetailedDescriptions:!1}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Throttle on budget exceeded"," ",(0,t.jsx)(q.Tooltip,{title:"When this key exceeds its max budget, throttle its TPM/RPM to the globally configured percentage instead of blocking access entirely. Requires budget_exceeded_throttle_percentage in litellm_settings and a TPM/RPM limit on the key.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"throttle_on_budget_exceeded",valuePropName:"checked",children:(0,t.jsx)(G.Switch,{checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(b.Form.Item,{label:"Max Parallel Requests",name:"max_parallel_requests",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(b.Form.Item,{label:"Model TPM Limit",name:"model_tpm_limit",children:(0,t.jsx)(V.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}'})}),(0,t.jsx)(b.Form.Item,{label:"Model RPM Limit",name:"model_rpm_limit",children:(0,t.jsx)(V.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}'})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Per-Tag Rate Limits"," ",(0,t.jsx)(q.Tooltip,{title:"Scope rate limits to a request tag so each tag (e.g. a cell or group) gets its own RPM counter. Requests without a matching tag fall back to the key-level limit.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(ea.TagRateLimitEditor,{value:eg,onChange:eh})}),(0,t.jsx)(b.Form.Item,{label:"Guardrails",name:"guardrails",children:n&&(0,t.jsx)(D.default,{onChange:e=>{x.setFieldValue("guardrails",e)},accessToken:n,disabled:!u})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(q.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"disable_global_guardrails",valuePropName:"checked",children:(0,t.jsx)(G.Switch,{disabled:!u,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(q.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"policies",children:n&&(0,t.jsx)(K.default,{onChange:e=>{x.setFieldValue("policies",e)},accessToken:n,disabled:!m})}),(0,t.jsx)(b.Form.Item,{label:"Tags",name:"tags",children:(0,t.jsx)(W.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",options:Object.values(h).map(e=>({value:e.name,label:e.name,title:e.description||e.name}))})}),(0,t.jsx)(b.Form.Item,{label:"Prompts",name:"prompts",children:(0,t.jsx)(q.Tooltip,{title:m?"":"Setting prompts by key is a premium feature",placement:"top",children:(0,t.jsx)(W.Select,{mode:"tags",style:{width:"100%"},disabled:!m,placeholder:m?Array.isArray(e.metadata?.prompts)&&e.metadata.prompts.length>0?`Current: ${e.metadata.prompts.join(", ")}`:"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:p.map(e=>({value:e,label:e}))})})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(q.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",children:(0,t.jsx)(J.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{label:"Allowed Pass Through Routes",name:"allowed_passthrough_routes",children:(0,t.jsx)(q.Tooltip,{title:m?"":"Setting allowed pass through routes by key is a premium feature",placement:"top",children:(0,t.jsx)(Y.default,{onChange:e=>x.setFieldValue("allowed_passthrough_routes",e),value:x.getFieldValue("allowed_passthrough_routes"),accessToken:n||"",placeholder:m?Array.isArray(e.metadata?.allowed_passthrough_routes)&&e.metadata.allowed_passthrough_routes.length>0?`Current: ${e.metadata.allowed_passthrough_routes.join(", ")}`:"Select or enter allowed pass through routes":"Premium feature - Upgrade to set allowed pass through routes by key",disabled:!m})})}),(0,t.jsx)(b.Form.Item,{label:"Vector Stores",name:"vector_stores",children:(0,t.jsx)(ec.default,{onChange:e=>x.setFieldValue("vector_stores",e),value:x.getFieldValue("vector_stores"),accessToken:n||"",placeholder:"Select vector stores"})}),(0,t.jsx)(b.Form.Item,{label:"MCP Servers / Access Groups",name:"mcp_servers_and_groups",children:(0,t.jsx)(el.default,{onChange:e=>x.setFieldValue("mcp_servers_and_groups",e),value:x.getFieldValue("mcp_servers_and_groups"),accessToken:n||"",placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(b.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(V.Input,{type:"hidden"})}),(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.mcp_servers_and_groups!==t.mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsx)(er.default,{accessToken:n||"",selectedServers:(x.getFieldValue("mcp_servers_and_groups")?.servers||[]).filter(e=>e!==ei.NO_MCP_SERVERS_SENTINEL),toolPermissions:x.getFieldValue("mcp_tool_permissions")||{},onChange:e=>x.setFieldsValue({mcp_tool_permissions:e})})})}),(0,t.jsx)(b.Form.Item,{label:"Agents / Access Groups",name:"agents_and_groups",children:(0,t.jsx)(H.default,{onChange:e=>x.setFieldValue("agents_and_groups",e),value:x.getFieldValue("agents_and_groups"),accessToken:n||"",placeholder:"Select agents or access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(q.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",children:(0,t.jsx)(Z.default,{organizations:ey,loading:eb,disabled:"Admin"!==d,onChange:e=>{C(e||null),x.setFieldValue("team_id",void 0)}})}),(0,t.jsx)(b.Form.Item,{label:"Team ID",name:"team_id",help:ek&&eN?"Team is locked because this key belongs to a project":void 0,children:(0,t.jsx)(W.Select,{placeholder:"Select team",showSearch:!0,disabled:ek&&eN,style:{width:"100%"},onChange:e=>{let t=r?.find(t=>t.team_id===e)||null;t?.organization_id?(C(t.organization_id),x.setFieldValue("organization_id",t.organization_id)):e||(C(null),x.setFieldValue("organization_id",void 0))},filterOption:(e,t)=>{let a=S?r?.filter(e=>e.organization_id===S):r,s=a?.find(e=>e.team_id===t?.value);return!!s&&(s.team_alias?.toLowerCase().includes(e.toLowerCase())??!1)},children:(S?r?.filter(e=>e.organization_id===S):r)?.map(e=>(0,t.jsx)(W.Select.Option,{value:e.team_id,children:`${e.team_alias} (${e.team_id})`},e.team_id))})}),ek&&eN&&(0,t.jsx)(b.Form.Item,{label:"Project",children:(0,t.jsx)(V.Input,{value:eT??"",disabled:!0})}),(0,t.jsx)(b.Form.Item,{label:"Logging Settings",name:"logging_settings",children:(0,t.jsx)(ed.default,{value:x.getFieldValue("logging_settings"),onChange:e=>x.setFieldValue("logging_settings",e),disabledCallbacks:v,onDisabledCallbacksChange:e=>{k((0,w.mapInternalToDisplayNames)(e)),x.setFieldValue("disabled_callbacks",e)}})}),(0,t.jsx)(b.Form.Item,{label:"Metadata",name:"metadata",children:(0,t.jsx)(V.Input.TextArea,{rows:10})}),(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(Q.default,{form:x,autoRotationEnabled:A,onAutoRotationChange:L,rotationInterval:R,onRotationIntervalChange:E,neverExpire:P,onNeverExpireChange:O}),(0,t.jsx)(b.Form.Item,{name:"duration",hidden:!0,initialValue:"",children:(0,t.jsx)(V.Input,{})})]}),(0,t.jsx)(b.Form.Item,{name:"token",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"disabled_callbacks",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"auto_rotate",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"rotation_interval",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)("div",{className:"sticky z-10 bg-white p-4 border-t border-gray-200 -bottom-6 -inset-x-6",children:(0,t.jsxs)("div",{className:"flex justify-end items-center gap-2",children:[(0,t.jsx)(c.Button,{variant:"secondary",onClick:a,disabled:B,children:"Cancel"}),(0,t.jsx)(c.Button,{type:"submit",loading:B,children:"Save Changes"})]})})]})}let eu=["policies","guardrails","prompts","tags","allowed_passthrough_routes"],ex=e=>null==e||Array.isArray(e)&&0===e.length||"string"==typeof e&&""===e.trim();e.s(["default",0,function({onClose:e,keyData:D,teams:z,onKeyDataUpdate:K,onDelete:$,backButtonText:U="Back to Keys"}){let V,{accessToken:W,userId:G,userRole:q,premiumUser:H}=(0,a.default)(),J=(0,E.useQueryClient)(),Q=H||null!=q&&T.rolesWithWriteAccess.includes(q),{teams:Y}=(0,i.default)(),{data:X}=(0,s.useProjects)(),{data:Z}=(0,l.useUISettings)(),ee=!!Z?.values?.enable_projects_ui,[et,ea]=(0,N.useState)(!1),[es]=b.Form.useForm(),[el,ei]=(0,N.useState)(!1),[er,en]=(0,N.useState)(!1),[eo,ed]=(0,N.useState)(""),[ec,ep]=(0,N.useState)(!1),[eg,eh]=(0,N.useState)(!1),{mutate:e_,isPending:ej}=(0,L.useResetKeySpend)(),[ey,eb]=(0,N.useState)(D),[ef,ev]=(0,N.useState)(null),[ek,eN]=(0,N.useState)(!1),[eT,ew]=(0,N.useState)({}),[eS,eC]=(0,N.useState)(!1);if((0,N.useEffect)(()=>{D&&eb(D)},[D]),(0,N.useEffect)(()=>{(async()=>{let e=ey?.metadata?.policies;if(!W||!e||!Array.isArray(e)||0===e.length)return;eC(!0);let t={};try{await Promise.all(e.map(async e=>{try{let a=await (0,M.getPolicyInfoWithGuardrails)(W,e);t[e]=a.resolved_guardrails||[]}catch(a){console.error(`Failed to fetch guardrails for policy ${e}:`,a),t[e]=[]}})),ew(t)}catch(e){console.error("Failed to fetch policy guardrails:",e)}finally{eC(!1)}})()},[W,ey?.metadata?.policies]),(0,N.useEffect)(()=>{if(ek){let e=setTimeout(()=>{eN(!1)},5e3);return()=>clearTimeout(e)}},[ek]),!ey)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)(c.Button,{icon:o.ArrowLeftIcon,variant:"light",onClick:e,className:"mb-4",children:U}),(0,t.jsx)(j.Text,{children:"Key not found"})]});let eI=async e=>{try{if(!W)return;let t=e.token;for(let a of(e.key=t,Q||(delete e.guardrails,delete e.prompts),eu)){let t=ey.metadata?.[a]??ey[a];ex(e[a])&&ex(t)&&delete e[a]}let a=!!ey.metadata?.disable_global_guardrails;if(!!e.disable_global_guardrails===a&&delete e.disable_global_guardrails,e.max_budget=(0,n.mapEmptyStringToNull)(e.max_budget),void 0!==e.vector_stores&&(e.object_permission={...ey.object_permission,vector_stores:e.vector_stores||[]},delete e.vector_stores),void 0!==e.mcp_servers_and_groups){let{servers:t,accessGroups:a,toolsets:s}=e.mcp_servers_and_groups||{servers:[],accessGroups:[],toolsets:[]};e.object_permission={...ey.object_permission,mcp_servers:t||[],mcp_access_groups:a||[],mcp_toolsets:s||[]},delete e.mcp_servers_and_groups}if(void 0!==e.mcp_tool_permissions){let t=e.mcp_tool_permissions||{};Object.keys(t).length>0&&(e.object_permission={...e.object_permission,mcp_tool_permissions:t}),delete e.mcp_tool_permissions}if(void 0!==e.agents_and_groups){let{agents:t,accessGroups:a}=e.agents_and_groups||{agents:[],accessGroups:[]};e.object_permission={...e.object_permission,agents:t||[],agent_access_groups:a||[]},delete e.agents_and_groups}if(e.max_budget=(0,n.mapEmptyStringToNull)(e.max_budget),e.tpm_limit=(0,n.mapEmptyStringToNull)(e.tpm_limit),e.rpm_limit=(0,n.mapEmptyStringToNull)(e.rpm_limit),e.max_parallel_requests=(0,n.mapEmptyStringToNull)(e.max_parallel_requests),e.metadata&&"string"==typeof e.metadata)try{let t=JSON.parse(e.metadata);"tags"in t&&delete t.tags,e.metadata={...t,...Array.isArray(e.tags)&&e.tags.length>0?{tags:e.tags}:{},...e.guardrails?.length>0?{guardrails:e.guardrails}:{},...Array.isArray(e.logging_settings)&&e.logging_settings.length>0?{logging:e.logging_settings}:{},...e.disabled_callbacks?.length>0?{litellm_disabled_callbacks:(0,w.mapDisplayToInternalNames)(e.disabled_callbacks)}:{}}}catch(e){console.error("Error parsing metadata JSON:",e),F.default.error("Invalid metadata JSON");return}else{let{tags:t,...a}=e.metadata||{};e.metadata={...a,...Array.isArray(e.tags)&&e.tags.length>0?{tags:e.tags}:{},...e.guardrails?.length>0?{guardrails:e.guardrails}:{},...Array.isArray(e.logging_settings)&&e.logging_settings.length>0?{logging:e.logging_settings}:{},...e.disabled_callbacks?.length>0?{litellm_disabled_callbacks:(0,w.mapDisplayToInternalNames)(e.disabled_callbacks)}:{}}}"tags"in e&&delete e.tags,delete e.logging_settings,e.budget_duration&&(e.budget_duration=({daily:"24h",weekly:"7d",monthly:"30d"})[e.budget_duration]);let s=await (0,M.keyUpdateCall)(W,e);eb(e=>e?{...e,...s}:void 0),K&&K(s),F.default.success("Key updated successfully"),ea(!1)}catch(e){F.default.fromBackend((0,B.parseErrorMessage)(e)),console.error("Error updating key:",e)}},eA=async()=>{try{if(en(!0),!W)return;await (0,M.keyDeleteCall)(W,ey.token||ey.token_id),F.default.success("Key deleted successfully"),await J.invalidateQueries({queryKey:R.keyKeys.lists()}),$&&$(),e()}catch(e){console.error("Error deleting the key:",e),F.default.fromBackend(e)}finally{en(!1),ei(!1),ed("")}},eF=e=>{let t=new Date(e),a=t.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),s=t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});return`${a} at ${s}`},eM=(0,T.isProxyAdminRole)(q||"")||Y&&(0,T.isUserTeamAdminForSingleTeam)(Y?.filter(e=>e.team_id===ey.team_id)[0]?.members_with_roles,G||"")||G===ey.user_id&&"Internal Viewer"!==q,eL=(0,T.isProxyAdminRole)(q||"")||Y&&(0,T.isUserTeamAdminForSingleTeam)(Y?.filter(e=>e.team_id===ey.team_id)[0]?.members_with_roles,G||""),eR=ey.team_id?Y?.find(e=>e.team_id===ey.team_id):null,eE=null!==ey.max_budget?`$${(0,r.formatNumberWithCommas)(ey.max_budget,2)}`:eR?.max_budget!=null?`$${(0,r.formatNumberWithCommas)(eR.max_budget,2)} (Team: ${eR.team_alias||eR.team_id}${eR.budget_duration?` / ${eR.budget_duration}`:""})`:"Unlimited";return(0,t.jsxs)("div",{className:"w-full h-full overflow-y-auto p-4",children:[(0,t.jsx)(k.KeyInfoHeader,{data:{keyName:ey.key_alias||"Virtual Key",keyId:ey.token_id||ey.token,userId:ey.user_id||"",userEmail:ey.user_email||"",userAlias:ey.user?.user_alias??null,createdBy:ey.created_by_user?.user_alias||ey.created_by_user?.user_email||ey.created_by||"",createdAt:ey.created_at?eF(ey.created_at):"",lastUpdated:ey.updated_at?eF(ey.updated_at):"",lastActive:ey.last_active?eF(ey.last_active):"Never",expires:ey.expires?eF(ey.expires):"Never"},onBack:e,onRegenerate:()=>ep(!0),onDelete:()=>ei(!0),onResetSpend:eL?()=>eh(!0):void 0,canModifyKey:eM,backButtonText:U,regenerateDisabled:!H,regenerateTooltip:H?void 0:"This is a LiteLLM Enterprise feature, and requires a valid key to use."}),(0,t.jsx)(O.RegenerateKeyModal,{selectedToken:ey,visible:ec,onClose:()=>ep(!1),onKeyUpdate:e=>{eb(t=>{if(t)return{...t,...e,created_at:new Date().toLocaleString()}}),ev(new Date),eN(!0),K&&K({...e,created_at:new Date().toLocaleString()})}}),(0,t.jsx)(C.default,{isOpen:el,title:"Delete Key",alertMessage:"This action is irreversible and will immediately revoke access for any applications using this key.",message:"Are you sure you want to delete this Virtual Key?",resourceInformationTitle:"Key Information",resourceInformation:[{label:"Key Alias",value:ey?.key_alias||"-"},{label:"Key ID",value:ey?.token_id||ey?.token||"-",code:!0},{label:"Team ID",value:ey?.team_id||"-",code:!0},{label:"Spend",value:ey?.spend?`$${(0,r.formatNumberWithCommas)(ey.spend,4)}`:"$0.0000"}],onCancel:()=>{ei(!1),ed("")},onOk:eA,confirmLoading:er,requiredConfirmation:ey?.key_alias}),(0,t.jsxs)(f.Modal,{title:"Reset Key Spend",open:eg,onOk:()=>{e_(ey.token||ey.token_id,{onSuccess:()=>{eb(e=>e?{...e,spend:0}:void 0),K&&K({spend:0}),F.default.success("Key spend reset to $0"),eh(!1)},onError:e=>{F.default.fromBackend((0,B.parseErrorMessage)(e)),console.error("Error resetting key spend:",e)}})},onCancel:()=>eh(!1),okText:"Reset",okButtonProps:{danger:!0},confirmLoading:ej,children:[(0,t.jsxs)("p",{children:["Reset spend for ",(0,t.jsx)("strong",{children:ey?.key_alias||ey?.token_id||"this key"})," to"," ",(0,t.jsx)("strong",{children:"$0"}),"?"]}),(0,t.jsxs)("p",{style:{color:"#666",fontSize:"0.875rem",marginTop:8},children:["Current spend: ",(0,t.jsxs)("strong",{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)]}),". Spend history is preserved in logs. This resets the current period spend counter, the same as an automatic budget reset."]})]}),(0,t.jsxs)(p.TabGroup,{children:[(0,t.jsxs)(g.TabList,{className:"mb-4",children:[(0,t.jsx)(x.Tab,{children:"Overview"}),(0,t.jsx)(x.Tab,{children:"Settings"})]}),(0,t.jsxs)(_.TabPanels,{children:[(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(u.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Spend"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(y.Title,{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)]}),(0,t.jsxs)(j.Text,{children:["of ",eE]})]})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Rate Limits"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(j.Text,{children:["TPM: ",null!==ey.tpm_limit?ey.tpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["RPM: ",null!==ey.rpm_limit?ey.rpm_limit:"Unlimited"]}),!!ey.metadata?.throttle_on_budget_exceeded&&(0,t.jsx)(j.Text,{children:"Throttle on budget exceeded: Yes"})]})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Models"}),(0,t.jsx)("div",{className:"mt-2 flex flex-wrap gap-2",children:ey.models&&ey.models.length>0?ey.models.map((e,a)=>(0,t.jsx)(d.Badge,{color:"red",children:e},a)):(0,t.jsx)(j.Text,{children:"No models specified"})})]}),(0,t.jsx)(m.Card,{children:(0,t.jsx)(P.default,{objectPermission:ey.object_permission,variant:"inline",accessToken:W})}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{className:"font-medium mb-3",children:"Guardrails"}),Array.isArray(ey.metadata?.guardrails)&&ey.metadata.guardrails.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:ey.metadata.guardrails.map((e,a)=>(0,t.jsx)(d.Badge,{color:"blue",children:e},a))}):(0,t.jsx)(j.Text,{className:"text-gray-500",children:"No guardrails configured"}),"boolean"==typeof ey.metadata?.disable_global_guardrails&&!0===ey.metadata.disable_global_guardrails&&(0,t.jsx)("div",{className:"mt-3 pt-3 border-t border-gray-200",children:(0,t.jsx)(d.Badge,{color:"yellow",children:"Global Guardrails Disabled"})})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{className:"font-medium mb-3",children:"Policies"}),Array.isArray(ey.metadata?.policies)&&ey.metadata.policies.length>0?(0,t.jsx)("div",{className:"space-y-4",children:ey.metadata.policies.map((e,a)=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(d.Badge,{color:"purple",children:e}),eS&&(0,t.jsx)(j.Text,{className:"text-xs text-gray-400",children:"Loading guardrails..."})]}),!eS&&eT[e]&&eT[e].length>0&&(0,t.jsxs)("div",{className:"ml-4 pl-3 border-l-2 border-gray-200",children:[(0,t.jsx)(j.Text,{className:"text-xs text-gray-500 mb-1",children:"Resolved Guardrails:"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:eT[e].map((e,a)=>(0,t.jsx)(d.Badge,{color:"blue",size:"xs",children:e},a))})]})]},a))}):(0,t.jsx)(j.Text,{className:"text-gray-500",children:"No policies configured"})]}),(0,t.jsx)(A.default,{loggingConfigs:(0,I.extractLoggingSettings)(ey.metadata),disabledCallbacks:Array.isArray(ey.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(ey.metadata.litellm_disabled_callbacks):[],variant:"card"}),(0,t.jsx)(S.default,{autoRotate:ey.auto_rotate,rotationInterval:ey.rotation_interval,lastRotationAt:ey.last_rotation_at,keyRotationAt:ey.key_rotation_at,nextRotationAt:ey.next_rotation_at,variant:"card"})]})}),(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(m.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(y.Title,{children:"Key Settings"}),!et&&eM&&(0,t.jsx)(c.Button,{onClick:()=>ea(!0),children:"Edit Settings"})]}),et?(0,t.jsx)(em,{keyData:ey,onCancel:()=>ea(!1),onSubmit:eI,teams:z,accessToken:W,userID:G,userRole:q,premiumUser:H}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Key ID"}),(0,t.jsx)(j.Text,{className:"font-mono",children:ey.token_id||ey.token})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Key Alias"}),(0,t.jsx)(j.Text,{children:ey.key_alias||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Secret Key"}),(0,t.jsx)(j.Text,{className:"font-mono",children:ey.key_name})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Team ID"}),(0,t.jsx)(j.Text,{children:ey.team_id||"Not Set"})]}),ee&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Project"}),(0,t.jsx)(j.Text,{children:ey.project_id?(V=X?.find(e=>e.project_id===ey.project_id),V?.project_alias?`${V.project_alias} (${ey.project_id})`:ey.project_id):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Organization"}),(0,t.jsx)(j.Text,{children:(ey.organization_id??ey.org_id)||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Created"}),(0,t.jsx)(j.Text,{children:eF(ey.created_at)})]}),ef&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Last Regenerated"}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(j.Text,{children:eF(ef)}),(0,t.jsx)(d.Badge,{color:"green",size:"xs",children:"Recent"})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Expires"}),(0,t.jsx)(j.Text,{children:ey.expires?eF(ey.expires):"Never"})]}),(0,t.jsx)(S.default,{autoRotate:ey.auto_rotate,rotationInterval:ey.rotation_interval,lastRotationAt:ey.last_rotation_at,keyRotationAt:ey.key_rotation_at,nextRotationAt:ey.next_rotation_at,variant:"inline",className:"pt-4 border-t border-gray-200"}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Spend"}),(0,t.jsxs)(j.Text,{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)," USD"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Budget"}),(0,t.jsx)(j.Text,{children:null!==ey.max_budget?`$${(0,r.formatNumberWithCommas)(ey.max_budget,2)}`:"Unlimited"})]}),ey.budget_fallbacks&&Object.keys(ey.budget_fallbacks).length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Budget Fallbacks"}),(0,t.jsx)("div",{className:"mt-1 space-y-1",children:Object.entries(ey.budget_fallbacks).map(([e,a])=>(0,t.jsxs)("div",{className:"text-xs text-gray-600",children:[(0,t.jsx)("span",{className:"font-medium",children:e}),(0,t.jsx)("span",{className:"mx-1 text-gray-400",children:"->"}),a.join(", ")]},e))})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Tags"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:Array.isArray(ey.metadata?.tags)&&ey.metadata.tags.length>0?ey.metadata.tags.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No tags specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Prompts"}),(0,t.jsx)(j.Text,{children:Array.isArray(ey.metadata?.prompts)&&ey.metadata.prompts.length>0?ey.metadata.prompts.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No prompts specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Allowed Routes"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:Array.isArray(ey.allowed_routes)&&ey.allowed_routes.length>0?ey.allowed_routes.map((e,a)=>(0,t.jsx)("span",{className:"px-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):(0,t.jsx)(v.Tag,{color:"green",children:"All routes allowed"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Allowed Pass Through Routes"}),(0,t.jsx)(j.Text,{children:Array.isArray(ey.metadata?.allowed_passthrough_routes)&&ey.metadata.allowed_passthrough_routes.length>0?ey.metadata.allowed_passthrough_routes.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No pass through routes specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Disable Global Guardrails"}),(0,t.jsx)(j.Text,{children:ey.metadata?.disable_global_guardrails===!0?(0,t.jsx)(d.Badge,{color:"yellow",children:"Enabled - Global guardrails bypassed"}):(0,t.jsx)(d.Badge,{color:"green",children:"Disabled - Global guardrails active"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Models"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:ey.models&&ey.models.length>0?ey.models.map((e,a)=>(0,t.jsx)("span",{className:"px-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):(0,t.jsx)(j.Text,{children:"No models specified"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Rate Limits"}),(0,t.jsxs)(j.Text,{children:["TPM: ",null!==ey.tpm_limit?ey.tpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["RPM: ",null!==ey.rpm_limit?ey.rpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Max Parallel Requests:"," ",null!==ey.max_parallel_requests?ey.max_parallel_requests:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Model TPM Limits:"," ",ey.metadata?.model_tpm_limit?JSON.stringify(ey.metadata.model_tpm_limit):"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Model RPM Limits:"," ",ey.metadata?.model_rpm_limit?JSON.stringify(ey.metadata.model_rpm_limit):"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Tag RPM Limits:"," ",ey.metadata?.tag_rpm_limit&&Object.keys(ey.metadata.tag_rpm_limit).length>0?JSON.stringify(ey.metadata.tag_rpm_limit):"Unlimited"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Metadata"}),(0,t.jsx)("pre",{className:"bg-gray-100 p-2 rounded-sm text-xs overflow-auto mt-1",children:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(ey.metadata))})]}),(0,t.jsx)(P.default,{objectPermission:ey.object_permission,variant:"inline",className:"pt-4 border-t border-gray-200",accessToken:W}),(0,t.jsx)(A.default,{loggingConfigs:(0,I.extractLoggingSettings)(ey.metadata),disabledCallbacks:Array.isArray(ey.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(ey.metadata.litellm_disabled_callbacks):[],variant:"inline",className:"pt-4 border-t border-gray-200"})]})]})})]})]})]})}],20147)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02dxw4eubg_rq.js b/litellm/proxy/_experimental/out/_next/static/chunks/02dxw4eubg_rq.js new file mode 100644 index 00000000000..0b24a6199e3 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02dxw4eubg_rq.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),o=e.i(271645);let n=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],i=e=>({_s:e,status:n[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),a=e=>e?6:5,l=(e,t,r,o,n)=>{clearTimeout(o.current);let a=i(e);t(a),r.current=a,n&&n({current:a})};var s=e.i(480731),c=e.i(444755),d=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return o.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),o.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var g=e.i(95779);let m={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},p=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,d.getColorClassNames)(t,g.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,d.getColorClassNames)(t,g.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,d.getColorClassNames)(t,g.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,d.getColorClassNames)(t,g.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,d.getColorClassNames)(t,g.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,g.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,c.tremorTwMerge)((0,d.getColorClassNames)(t,g.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,d.getColorClassNames)(t,g.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,d.getColorClassNames)(t,g.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,g.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},f=(0,d.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:n,needMargin:i,transitionStatus:a})=>{let l=i?r===s.HorizontalPositions.Left?(0,c.tremorTwMerge)("-ml-1","mr-1.5"):(0,c.tremorTwMerge)("-mr-1","ml-1.5"):"",d=(0,c.tremorTwMerge)("w-0 h-0"),g={default:d,entering:d,entered:t,exiting:t,exited:d};return e?o.default.createElement(u,{className:(0,c.tremorTwMerge)(f("icon"),"animate-spin shrink-0",l,g.default,g[a]),style:{transition:"width 150ms"}}):o.default.createElement(n,{className:(0,c.tremorTwMerge)(f("icon"),"shrink-0",t,l)})},b=o.default.forwardRef((e,n)=>{let{icon:u,iconPosition:g=s.HorizontalPositions.Left,size:b=s.Sizes.SM,color:C,variant:x="primary",disabled:k,loading:v=!1,loadingText:y,children:$,tooltip:w,className:S}=e,E=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),N=v||k,B=void 0!==u||v,z=v&&y,O=!(!$&&!z),T=(0,c.tremorTwMerge)(m[b].height,m[b].width),P="light"!==x?(0,c.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",j=p(x,C),M=("light"!==x?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:I,getReferenceProps:R}=(0,r.useTooltip)(300),[A,W]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:n,timeout:s,initialEntered:c,mountOnEnter:d,unmountOnExit:u,onStateChange:g}={})=>{let[m,p]=(0,o.useState)(()=>i(c?2:a(d))),f=(0,o.useRef)(m),h=(0,o.useRef)(0),[b,C]="object"==typeof s?[s.enter,s.exit]:[s,s],x=(0,o.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return a(t)}})(f.current._s,u);e&&l(e,p,f,h,g)},[g,u]);return[m,(0,o.useCallback)(o=>{let i=e=>{switch(l(e,p,f,h,g),e){case 1:b>=0&&(h.current=((...e)=>setTimeout(...e))(x,b));break;case 4:C>=0&&(h.current=((...e)=>setTimeout(...e))(x,C));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||i(e+1)},0)}},s=f.current.isEnter;"boolean"!=typeof o&&(o=!s),o?s||i(e?+!r:2):s&&i(t?n?3:4:a(u))},[x,g,e,t,r,n,b,C,u]),x]})({timeout:50});return(0,o.useEffect)(()=>{W(v)},[v]),o.default.createElement("button",Object.assign({ref:(0,d.mergeRefs)([n,I.refs.setReference]),className:(0,c.tremorTwMerge)(f("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",P,M.paddingX,M.paddingY,M.fontSize,j.textColor,j.bgColor,j.borderColor,j.hoverBorderColor,N?"opacity-50 cursor-not-allowed":(0,c.tremorTwMerge)(p(x,C).hoverTextColor,p(x,C).hoverBgColor,p(x,C).hoverBorderColor),S),disabled:N},R,E),o.default.createElement(r.default,Object.assign({text:w},I)),B&&g!==s.HorizontalPositions.Right?o.default.createElement(h,{loading:v,iconSize:T,iconPosition:g,Icon:u,transitionStatus:A.status,needMargin:O}):null,z||$?o.default.createElement("span",{className:(0,c.tremorTwMerge)(f("text"),"text-tremor-default whitespace-nowrap")},z?y:$):null,B&&g===s.HorizontalPositions.Right?o.default.createElement(h,{loading:v,iconSize:T,iconPosition:g,Icon:u,transitionStatus:A.status,needMargin:O}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),o=e.i(673706),n=e.i(271645);let i=n.default.forwardRef((e,i)=>{let{color:a,className:l,children:s}=e;return n.default.createElement("p",{ref:i,className:(0,r.tremorTwMerge)("text-tremor-default",a?(0,o.getColorClassNames)(a,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),l)},s)});i.displayName="Text",e.s(["default",0,i],936325),e.s(["Text",0,i],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(480731),n=e.i(95779),i=e.i(444755),a=e.i(673706);let l=(0,a.makeClassName)("Card"),s=r.default.forwardRef((e,s)=>{let{decoration:c="",decorationColor:d,children:u,className:g}=e,m=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:s,className:(0,i.tremorTwMerge)(l("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",d?(0,a.getColorClassNames)(d,n.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case o.HorizontalPositions.Left:return"border-l-4";case o.VerticalPositions.Top:return"border-t-4";case o.HorizontalPositions.Right:return"border-r-4";case o.VerticalPositions.Bottom:return"border-b-4";default:return""}})(c),g)},m),u)});s.displayName="Card",e.s(["Card",0,s],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),o=e.i(444755),n=e.i(673706),i=e.i(271645);let a=i.default.forwardRef((e,a)=>{let{color:l,children:s,className:c}=e,d=(0,t.__rest)(e,["color","children","className"]);return i.default.createElement("p",Object.assign({ref:a,className:(0,o.tremorTwMerge)("font-medium text-tremor-title",l?(0,n.getColorClassNames)(l,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",c)},d),s)});a.displayName="Title",e.s(["Title",0,a],629569)},312361,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),o=e.i(242064),n=e.i(517455);e.i(296059);var i=e.i(915654),a=e.i(183293),l=e.i(246422),s=e.i(838378);let c=(0,l.genStyleHooks)("Divider",e=>{let t=(0,s.mergeToken)(e,{dividerHorizontalWithTextGutterMargin:e.margin,sizePaddingEdgeHorizontal:0});return[(e=>{let{componentCls:t,sizePaddingEdgeHorizontal:r,colorSplit:o,lineWidth:n,textPaddingInline:l,orientationMargin:s,verticalMarginInline:c}=e;return{[t]:Object.assign(Object.assign({},(0,a.resetComponent)(e)),{borderBlockStart:`${(0,i.unit)(n)} solid ${o}`,"&-vertical":{position:"relative",top:"-0.06em",display:"inline-block",height:"0.9em",marginInline:c,marginBlock:0,verticalAlign:"middle",borderTop:0,borderInlineStart:`${(0,i.unit)(n)} solid ${o}`},"&-horizontal":{display:"flex",clear:"both",width:"100%",minWidth:"100%",margin:`${(0,i.unit)(e.marginLG)} 0`},[`&-horizontal${t}-with-text`]:{display:"flex",alignItems:"center",margin:`${(0,i.unit)(e.dividerHorizontalWithTextGutterMargin)} 0`,color:e.colorTextHeading,fontWeight:500,fontSize:e.fontSizeLG,whiteSpace:"nowrap",textAlign:"center",borderBlockStart:`0 ${o}`,"&::before, &::after":{position:"relative",width:"50%",borderBlockStart:`${(0,i.unit)(n)} solid transparent`,borderBlockStartColor:"inherit",borderBlockEnd:0,transform:"translateY(50%)",content:"''"}},[`&-horizontal${t}-with-text-start`]:{"&::before":{width:`calc(${s} * 100%)`},"&::after":{width:`calc(100% - ${s} * 100%)`}},[`&-horizontal${t}-with-text-end`]:{"&::before":{width:`calc(100% - ${s} * 100%)`},"&::after":{width:`calc(${s} * 100%)`}},[`${t}-inner-text`]:{display:"inline-block",paddingBlock:0,paddingInline:l},"&-dashed":{background:"none",borderColor:o,borderStyle:"dashed",borderWidth:`${(0,i.unit)(n)} 0 0`},[`&-horizontal${t}-with-text${t}-dashed`]:{"&::before, &::after":{borderStyle:"dashed none none"}},[`&-vertical${t}-dashed`]:{borderInlineStartWidth:n,borderInlineEnd:0,borderBlockStart:0,borderBlockEnd:0},"&-dotted":{background:"none",borderColor:o,borderStyle:"dotted",borderWidth:`${(0,i.unit)(n)} 0 0`},[`&-horizontal${t}-with-text${t}-dotted`]:{"&::before, &::after":{borderStyle:"dotted none none"}},[`&-vertical${t}-dotted`]:{borderInlineStartWidth:n,borderInlineEnd:0,borderBlockStart:0,borderBlockEnd:0},[`&-plain${t}-with-text`]:{color:e.colorText,fontWeight:"normal",fontSize:e.fontSize},[`&-horizontal${t}-with-text-start${t}-no-default-orientation-margin-start`]:{"&::before":{width:0},"&::after":{width:"100%"},[`${t}-inner-text`]:{paddingInlineStart:r}},[`&-horizontal${t}-with-text-end${t}-no-default-orientation-margin-end`]:{"&::before":{width:"100%"},"&::after":{width:0},[`${t}-inner-text`]:{paddingInlineEnd:r}}})}})(t),(e=>{let{componentCls:t}=e;return{[t]:{"&-horizontal":{[`&${t}`]:{"&-sm":{marginBlock:e.marginXS},"&-md":{marginBlock:e.margin}}}}}})(t)]},e=>({textPaddingInline:"1em",orientationMargin:.05,verticalMarginInline:e.marginXS}),{unitless:{orientationMargin:!0}});var d=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,o=Object.getOwnPropertySymbols(e);nt.indexOf(o[n])&&Object.prototype.propertyIsEnumerable.call(e,o[n])&&(r[o[n]]=e[o[n]]);return r};let u={small:"sm",middle:"md"};e.s(["Divider",0,e=>{let{getPrefixCls:i,direction:a,className:l,style:s}=(0,o.useComponentConfig)("divider"),{prefixCls:g,type:m="horizontal",orientation:p="center",orientationMargin:f,className:h,rootClassName:b,children:C,dashed:x,variant:k="solid",plain:v,style:y,size:$}=e,w=d(e,["prefixCls","type","orientation","orientationMargin","className","rootClassName","children","dashed","variant","plain","style","size"]),S=i("divider",g),[E,N,B]=c(S),z=u[(0,n.default)($)],O=!!C,T=t.useMemo(()=>"left"===p?"rtl"===a?"end":"start":"right"===p?"rtl"===a?"start":"end":p,[a,p]),P="start"===T&&null!=f,j="end"===T&&null!=f,M=(0,r.default)(S,l,N,B,`${S}-${m}`,{[`${S}-with-text`]:O,[`${S}-with-text-${T}`]:O,[`${S}-dashed`]:!!x,[`${S}-${k}`]:"solid"!==k,[`${S}-plain`]:!!v,[`${S}-rtl`]:"rtl"===a,[`${S}-no-default-orientation-margin-start`]:P,[`${S}-no-default-orientation-margin-end`]:j,[`${S}-${z}`]:!!z},h,b),I=t.useMemo(()=>"number"==typeof f?f:/^\d+$/.test(f)?Number(f):f,[f]);return E(t.createElement("div",Object.assign({className:M,style:Object.assign(Object.assign({},s),y)},w,{role:"separator"}),C&&"vertical"!==m&&t.createElement("span",{className:`${S}-inner-text`,style:{marginInlineStart:P?I:void 0,marginInlineEnd:j?I:void 0}},C)))}],312361)},56456,e=>{"use strict";var t=e.i(739295);e.s(["LoadingOutlined",()=>t.default])},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),o=e.i(201072),n=e.i(121229),i=e.i(726289),a=e.i(864517),l=e.i(343794),s=e.i(529681),c=e.i(242064),d=e.i(931067),u=e.i(209428),g=e.i(703923),m={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},p=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),o=!1;e.current.forEach(function(e){if(e){o=!0;var n=e.style;n.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(n.transitionDuration="0s, 0s")}}),o&&(r.current=Date.now())}),e.current},f=e.i(410160),h=e.i(392221),b=e.i(654310),C=0,x=(0,b.default)();let k=function(e){var r=t.useState(),o=(0,h.default)(r,2),n=o[0],i=o[1];return t.useEffect(function(){var e;i("rc_progress_".concat((x?(e=C,C+=1):e="TEST_OR_SSR",e)))},[]),e||n};var v=function(e){var r=e.bg,o=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},o)};function y(e,t){return Object.keys(e).map(function(r){var o=parseFloat(r),n="".concat(Math.floor(o*t),"%");return"".concat(e[r]," ").concat(n)})}var $=t.forwardRef(function(e,r){var o=e.prefixCls,n=e.color,i=e.gradientId,a=e.radius,l=e.style,s=e.ptg,c=e.strokeLinecap,d=e.strokeWidth,u=e.size,g=e.gapDegree,m=n&&"object"===(0,f.default)(n),p=u/2,h=t.createElement("circle",{className:"".concat(o,"-circle-path"),r:a,cx:p,cy:p,stroke:m?"#FFF":void 0,strokeLinecap:c,strokeWidth:d,opacity:+(0!==s),style:l,ref:r});if(!m)return h;var b="".concat(i,"-conic"),C=y(n,(360-g)/360),x=y(n,1),k="conic-gradient(from ".concat(g?"".concat(180+g/2,"deg"):"0deg",", ").concat(C.join(", "),")"),$="linear-gradient(to ".concat(g?"bottom":"top",", ").concat(x.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:b},h),t.createElement("foreignObject",{x:0,y:0,width:u,height:u,mask:"url(#".concat(b,")")},t.createElement(v,{bg:$},t.createElement(v,{bg:k}))))}),w=function(e,t,r,o,n,i,a,l,s,c){var d=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,u=(100-o)/100*t;return"round"===s&&100!==o&&(u+=c/2)>=t&&(u=t-.01),{stroke:"string"==typeof l?l:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:u+d,transform:"rotate(".concat(n+r/100*360*((360-i)/360)+(0===i?0:({bottom:0,top:180,left:90,right:-90})[a]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},S=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function E(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let N=function(e){var r,o,n,i,a=(0,u.default)((0,u.default)({},m),e),s=a.id,c=a.prefixCls,h=a.steps,b=a.strokeWidth,C=a.trailWidth,x=a.gapDegree,v=void 0===x?0:x,y=a.gapPosition,N=a.trailColor,B=a.strokeLinecap,z=a.style,O=a.className,T=a.strokeColor,P=a.percent,j=(0,g.default)(a,S),M=k(s),I="".concat(M,"-gradient"),R=50-b/2,A=2*Math.PI*R,W=v>0?90+v/2:-90,X=(360-v)/360*A,D="object"===(0,f.default)(h)?h:{count:h,gap:2},L=D.count,H=D.gap,_=E(P),F=E(T),Y=F.find(function(e){return e&&"object"===(0,f.default)(e)}),G=Y&&"object"===(0,f.default)(Y)?"butt":B,K=w(A,X,0,100,W,v,y,N,G,b),V=p();return t.createElement("svg",(0,d.default)({className:(0,l.default)("".concat(c,"-circle"),O),viewBox:"0 0 ".concat(100," ").concat(100),style:z,id:s,role:"presentation"},j),!L&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:R,cx:50,cy:50,stroke:N,strokeLinecap:G,strokeWidth:C||b,style:K}),L?(r=Math.round(L*(_[0]/100)),o=100/L,n=0,Array(L).fill(null).map(function(e,i){var a=i<=r-1?F[0]:N,l=a&&"object"===(0,f.default)(a)?"url(#".concat(I,")"):void 0,s=w(A,X,n,o,W,v,y,a,"butt",b,H);return n+=(X-s.strokeDashoffset+H)*100/X,t.createElement("circle",{key:i,className:"".concat(c,"-circle-path"),r:R,cx:50,cy:50,stroke:l,strokeWidth:b,opacity:1,style:s,ref:function(e){V[i]=e}})})):(i=0,_.map(function(e,r){var o=F[r]||F[F.length-1],n=w(A,X,i,e,W,v,y,o,G,b);return i+=e,t.createElement($,{key:r,color:o,ptg:e,radius:R,prefixCls:c,gradientId:I,style:n,strokeLinecap:G,strokeWidth:b,gapDegree:v,ref:function(e){V[r]=e},size:100})}).reverse()))};var B=e.i(491816);e.i(765846);var z=e.i(896091);function O(e){return!e||e<0?0:e>100?100:e}function T({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let P=(e,t,r)=>{var o,n,i,a;let l=-1,s=-1;if("step"===t){let t=r.steps,o=r.strokeWidth;"string"==typeof e||void 0===e?(l="small"===e?2:14,s=null!=o?o:8):"number"==typeof e?[l,s]=[e,e]:[l=14,s=8]=Array.isArray(e)?e:[e.width,e.height],l*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?s=t||("small"===e?6:8):"number"==typeof e?[l,s]=[e,e]:[l=-1,s=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[l,s]="small"===e?[60,60]:[120,120]:"number"==typeof e?[l,s]=[e,e]:Array.isArray(e)&&(l=null!=(n=null!=(o=e[0])?o:e[1])?n:120,s=null!=(a=null!=(i=e[0])?i:e[1])?a:120));return[l,s]},j=e=>{let{prefixCls:r,trailColor:o=null,strokeLinecap:n="round",gapPosition:i,gapDegree:a,width:s=120,type:c,children:d,success:u,size:g=s,steps:m}=e,[p,f]=P(g,"circle"),{strokeWidth:h}=e;void 0===h&&(h=Math.max(3/p*100,6));let b=t.useMemo(()=>a||0===a?a:"dashboard"===c?75:void 0,[a,c]),C=(({percent:e,success:t,successPercent:r})=>{let o=O(T({success:t,successPercent:r}));return[o,O(O(e)-o)]})(e),x="[object Object]"===Object.prototype.toString.call(e.strokeColor),k=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||z.presetPrimaryColors.green,t||null]})({success:u,strokeColor:e.strokeColor}),v=(0,l.default)(`${r}-inner`,{[`${r}-circle-gradient`]:x}),y=t.createElement(N,{steps:m,percent:m?C[1]:C,strokeWidth:h,trailWidth:h,strokeColor:m?k[1]:k,strokeLinecap:n,trailColor:o,prefixCls:r,gapDegree:b,gapPosition:i||"dashboard"===c&&"bottom"||void 0}),$=p<=20,w=t.createElement("div",{className:v,style:{width:p,height:f,fontSize:.15*p+6}},y,!$&&d);return $?t.createElement(B.default,{title:d},w):w};e.i(296059);var M=e.i(694758),I=e.i(915654),R=e.i(183293),A=e.i(246422),W=e.i(838378);let X="--progress-line-stroke-color",D="--progress-percent",L=e=>{let t=e?"100%":"-100%";return new M.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},H=(0,A.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,W.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,R.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${X})`]},height:"100%",width:`calc(1 / var(${D}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,I.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:L(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:L(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var _=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,o=Object.getOwnPropertySymbols(e);nt.indexOf(o[n])&&Object.prototype.propertyIsEnumerable.call(e,o[n])&&(r[o[n]]=e[o[n]]);return r};let F=e=>{let{prefixCls:r,direction:o,percent:n,size:i,strokeWidth:a,strokeColor:s,strokeLinecap:c="round",children:d,trailColor:u=null,percentPosition:g,success:m}=e,{align:p,type:f}=g,h=s&&"string"!=typeof s?((e,t)=>{let{from:r=z.presetPrimaryColors.blue,to:o=z.presetPrimaryColors.blue,direction:n="rtl"===t?"to left":"to right"}=e,i=_(e,["from","to","direction"]);if(0!==Object.keys(i).length){let e,t=(e=[],Object.keys(i).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:i[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${n}, ${t})`;return{background:r,[X]:r}}let a=`linear-gradient(${n}, ${r}, ${o})`;return{background:a,[X]:a}})(s,o):{[X]:s,background:s},b="square"===c||"butt"===c?0:void 0,[C,x]=P(null!=i?i:[-1,a||("small"===i?6:8)],"line",{strokeWidth:a}),k=Object.assign(Object.assign({width:`${O(n)}%`,height:x,borderRadius:b},h),{[D]:O(n)/100}),v=T(e),y={width:`${O(v)}%`,height:x,borderRadius:b,backgroundColor:null==m?void 0:m.strokeColor},$=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:u||void 0,borderRadius:b}},t.createElement("div",{className:(0,l.default)(`${r}-bg`,`${r}-bg-${f}`),style:k},"inner"===f&&d),void 0!==v&&t.createElement("div",{className:`${r}-success-bg`,style:y})),w="outer"===f&&"start"===p,S="outer"===f&&"end"===p;return"outer"===f&&"center"===p?t.createElement("div",{className:`${r}-layout-bottom`},$,d):t.createElement("div",{className:`${r}-outer`,style:{width:C<0?"100%":C}},w&&d,$,S&&d)},Y=e=>{let{size:r,steps:o,rounding:n=Math.round,percent:i=0,strokeWidth:a=8,strokeColor:s,trailColor:c=null,prefixCls:d,children:u}=e,g=n(i/100*o),[m,p]=P(null!=r?r:["small"===r?2:14,a],"step",{steps:o,strokeWidth:a}),f=m/o,h=Array.from({length:o});for(let e=0;et.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,o=Object.getOwnPropertySymbols(e);nt.indexOf(o[n])&&Object.prototype.propertyIsEnumerable.call(e,o[n])&&(r[o[n]]=e[o[n]]);return r};let K=["normal","exception","active","success"],V=t.forwardRef((e,d)=>{let u,{prefixCls:g,className:m,rootClassName:p,steps:f,strokeColor:h,percent:b=0,size:C="default",showInfo:x=!0,type:k="line",status:v,format:y,style:$,percentPosition:w={}}=e,S=G(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:E="end",type:N="outer"}=w,B=Array.isArray(h)?h[0]:h,z="string"==typeof h||Array.isArray(h)?h:void 0,M=t.useMemo(()=>{if(B){let e="string"==typeof B?B:Object.values(B)[0];return new r.FastColor(e).isLight()}return!1},[h]),I=t.useMemo(()=>{var t,r;let o=T(e);return Number.parseInt(void 0!==o?null==(t=null!=o?o:0)?void 0:t.toString():null==(r=null!=b?b:0)?void 0:r.toString(),10)},[b,e.success,e.successPercent]),R=t.useMemo(()=>!K.includes(v)&&I>=100?"success":v||"normal",[v,I]),{getPrefixCls:A,direction:W,progress:X}=t.useContext(c.ConfigContext),D=A("progress",g),[L,_,V]=H(D),q="line"===k,U=q&&!f,Q=t.useMemo(()=>{let r;if(!x)return null;let s=T(e),c=y||(e=>`${e}%`),d=q&&M&&"inner"===N;return"inner"===N||y||"exception"!==R&&"success"!==R?r=c(O(b),O(s)):"exception"===R?r=q?t.createElement(i.default,null):t.createElement(a.default,null):"success"===R&&(r=q?t.createElement(o.default,null):t.createElement(n.default,null)),t.createElement("span",{className:(0,l.default)(`${D}-text`,{[`${D}-text-bright`]:d,[`${D}-text-${E}`]:U,[`${D}-text-${N}`]:U}),title:"string"==typeof r?r:void 0},r)},[x,b,I,R,k,D,y]);"line"===k?u=f?t.createElement(Y,Object.assign({},e,{strokeColor:z,prefixCls:D,steps:"object"==typeof f?f.count:f}),Q):t.createElement(F,Object.assign({},e,{strokeColor:B,prefixCls:D,direction:W,percentPosition:{align:E,type:N}}),Q):("circle"===k||"dashboard"===k)&&(u=t.createElement(j,Object.assign({},e,{strokeColor:B,prefixCls:D,progressStatus:R}),Q));let Z=(0,l.default)(D,`${D}-status-${R}`,{[`${D}-${"dashboard"===k&&"circle"||k}`]:"line"!==k,[`${D}-inline-circle`]:"circle"===k&&P(C,"circle")[0]<=20,[`${D}-line`]:U,[`${D}-line-align-${E}`]:U,[`${D}-line-position-${N}`]:U,[`${D}-steps`]:f,[`${D}-show-info`]:x,[`${D}-${C}`]:"string"==typeof C,[`${D}-rtl`]:"rtl"===W},null==X?void 0:X.className,m,p,_,V);return L(t.createElement("div",Object.assign({ref:d,style:Object.assign(Object.assign({},null==X?void 0:X.style),$),className:Z,role:"progressbar","aria-valuenow":I,"aria-valuemin":0,"aria-valuemax":100},(0,s.default)(S,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),u))});e.s(["default",0,V],309821)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02hq_0zk6htur.js b/litellm/proxy/_experimental/out/_next/static/chunks/02hq_0zk6htur.js new file mode 100644 index 00000000000..fb07f00e01b --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02hq_0zk6htur.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),o=e.i(271645);let a=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:a[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),n=e=>e?6:5,s=(e,t,r,o,a)=>{clearTimeout(o.current);let n=l(e);t(n),r.current=n,a&&a({current:n})};var i=e.i(480731),c=e.i(444755),d=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return o.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),o.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},p=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,d.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,c.tremorTwMerge)((0,d.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},f=(0,d.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:a,needMargin:l,transitionStatus:n})=>{let s=l?r===i.HorizontalPositions.Left?(0,c.tremorTwMerge)("-ml-1","mr-1.5"):(0,c.tremorTwMerge)("-mr-1","ml-1.5"):"",d=(0,c.tremorTwMerge)("w-0 h-0"),m={default:d,entering:d,entered:t,exiting:t,exited:d};return e?o.default.createElement(u,{className:(0,c.tremorTwMerge)(f("icon"),"animate-spin shrink-0",s,m.default,m[n]),style:{transition:"width 150ms"}}):o.default.createElement(a,{className:(0,c.tremorTwMerge)(f("icon"),"shrink-0",t,s)})},b=o.default.forwardRef((e,a)=>{let{icon:u,iconPosition:m=i.HorizontalPositions.Left,size:b=i.Sizes.SM,color:v,variant:C="primary",disabled:x,loading:k=!1,loadingText:y,children:w,tooltip:$,className:S}=e,E=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),N=k||x,T=void 0!==u||k,B=k&&y,M=!(!w&&!B),z=(0,c.tremorTwMerge)(g[b].height,g[b].width),j="light"!==C?(0,c.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",P=p(C,v),O=("light"!==C?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:R,getReferenceProps:I}=(0,r.useTooltip)(300),[A,L]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:a,timeout:i,initialEntered:c,mountOnEnter:d,unmountOnExit:u,onStateChange:m}={})=>{let[g,p]=(0,o.useState)(()=>l(c?2:n(d))),f=(0,o.useRef)(g),h=(0,o.useRef)(0),[b,v]="object"==typeof i?[i.enter,i.exit]:[i,i],C=(0,o.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return n(t)}})(f.current._s,u);e&&s(e,p,f,h,m)},[m,u]);return[g,(0,o.useCallback)(o=>{let l=e=>{switch(s(e,p,f,h,m),e){case 1:b>=0&&(h.current=((...e)=>setTimeout(...e))(C,b));break;case 4:v>=0&&(h.current=((...e)=>setTimeout(...e))(C,v));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},i=f.current.isEnter;"boolean"!=typeof o&&(o=!i),o?i||l(e?+!r:2):i&&l(t?a?3:4:n(u))},[C,m,e,t,r,a,b,v,u]),C]})({timeout:50});return(0,o.useEffect)(()=>{L(k)},[k]),o.default.createElement("button",Object.assign({ref:(0,d.mergeRefs)([a,R.refs.setReference]),className:(0,c.tremorTwMerge)(f("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",j,O.paddingX,O.paddingY,O.fontSize,P.textColor,P.bgColor,P.borderColor,P.hoverBorderColor,N?"opacity-50 cursor-not-allowed":(0,c.tremorTwMerge)(p(C,v).hoverTextColor,p(C,v).hoverBgColor,p(C,v).hoverBorderColor),S),disabled:N},I,E),o.default.createElement(r.default,Object.assign({text:$},R)),T&&m!==i.HorizontalPositions.Right?o.default.createElement(h,{loading:k,iconSize:z,iconPosition:m,Icon:u,transitionStatus:A.status,needMargin:M}):null,B||w?o.default.createElement("span",{className:(0,c.tremorTwMerge)(f("text"),"text-tremor-default whitespace-nowrap")},B?y:w):null,T&&m===i.HorizontalPositions.Right?o.default.createElement(h,{loading:k,iconSize:z,iconPosition:m,Icon:u,transitionStatus:A.status,needMargin:M}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),o=e.i(673706),a=e.i(271645);let l=a.default.forwardRef((e,l)=>{let{color:n,className:s,children:i}=e;return a.default.createElement("p",{ref:l,className:(0,r.tremorTwMerge)("text-tremor-default",n?(0,o.getColorClassNames)(n,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),s)},i)});l.displayName="Text",e.s(["default",0,l],936325),e.s(["Text",0,l],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(480731),a=e.i(95779),l=e.i(444755),n=e.i(673706);let s=(0,n.makeClassName)("Card"),i=r.default.forwardRef((e,i)=>{let{decoration:c="",decorationColor:d,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:i,className:(0,l.tremorTwMerge)(s("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",d?(0,n.getColorClassNames)(d,a.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case o.HorizontalPositions.Left:return"border-l-4";case o.VerticalPositions.Top:return"border-t-4";case o.HorizontalPositions.Right:return"border-r-4";case o.VerticalPositions.Bottom:return"border-b-4";default:return""}})(c),m)},g),u)});i.displayName="Card",e.s(["Card",0,i],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),o=e.i(444755),a=e.i(673706),l=e.i(271645);let n=l.default.forwardRef((e,n)=>{let{color:s,children:i,className:c}=e,d=(0,t.__rest)(e,["color","children","className"]);return l.default.createElement("p",Object.assign({ref:n,className:(0,o.tremorTwMerge)("font-medium text-tremor-title",s?(0,a.getColorClassNames)(s,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",c)},d),i)});n.displayName="Title",e.s(["Title",0,n],629569)},350967,46757,e=>{"use strict";var t=e.i(290571),r=e.i(444755),o=e.i(673706),a=e.i(271645);let l={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},n={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},s={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},i={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,l,"gridColsLg",0,i,"gridColsMd",0,s,"gridColsSm",0,n],46757);let c=(0,o.makeClassName)("Grid"),d=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=a.default.forwardRef((e,o)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:g,numItemsLg:p,children:f,className:h}=e,b=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),v=d(u,l),C=d(m,n),x=d(g,s),k=d(p,i),y=(0,r.tremorTwMerge)(v,C,x,k);return a.default.createElement("div",Object.assign({ref:o,className:(0,r.tremorTwMerge)(c("root"),"grid",y,h)},b),f)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},983561,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M300 328a60 60 0 10120 0 60 60 0 10-120 0zM852 64H172c-17.7 0-32 14.3-32 32v660c0 17.7 14.3 32 32 32h680c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-32 660H204V128h616v596zM604 328a60 60 0 10120 0 60 60 0 10-120 0zm250.2 556H169.8c-16.5 0-29.8 14.3-29.8 32v36c0 4.4 3.3 8 7.4 8h729.1c4.1 0 7.4-3.6 7.4-8v-36c.1-17.7-13.2-32-29.7-32zM664 508H360c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"robot",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["RobotOutlined",0,l],983561)},797672,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,r],797672)},992619,e=>{"use strict";var t=e.i(843476),r=e.i(271645),o=e.i(779241),a=e.i(599724),l=e.i(199133),n=e.i(983561),s=e.i(695411);e.s(["default",0,({accessToken:e,value:i,placeholder:c="Select a Model",onChange:d,disabled:u=!1,style:m,className:g,showLabel:p=!0,labelText:f="Select Model"})=>{let[h,b]=(0,r.useState)(i),[v,C]=(0,r.useState)(!1),[x,k]=(0,r.useState)([]),y=(0,r.useRef)(null);return(0,r.useEffect)(()=>{b(i)},[i]),(0,r.useEffect)(()=>{e&&(async()=>{try{let t=await (0,s.fetchAvailableModels)(e);t.length>0&&k(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[p&&(0,t.jsxs)(a.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(n.RobotOutlined,{className:"mr-2"})," ",f]}),(0,t.jsx)(l.Select,{value:h,placeholder:c,onChange:e=>{"custom"===e?(C(!0),b(void 0)):(C(!1),b(e),d&&d(e))},options:[...Array.from(new Set(x.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${g||""}`,disabled:u}),v&&(0,t.jsx)(o.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{y.current&&clearTimeout(y.current),y.current=setTimeout(()=>{b(e),d&&d(e)},500)},disabled:u})]})}])},603908,e=>{"use strict";let t=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,t])},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),o=e.i(201072),a=e.i(121229),l=e.i(726289),n=e.i(864517),s=e.i(343794),i=e.i(529681),c=e.i(242064),d=e.i(931067),u=e.i(209428),m=e.i(703923),g={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},p=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),o=!1;e.current.forEach(function(e){if(e){o=!0;var a=e.style;a.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(a.transitionDuration="0s, 0s")}}),o&&(r.current=Date.now())}),e.current},f=e.i(410160),h=e.i(392221),b=e.i(654310),v=0,C=(0,b.default)();let x=function(e){var r=t.useState(),o=(0,h.default)(r,2),a=o[0],l=o[1];return t.useEffect(function(){var e;l("rc_progress_".concat((C?(e=v,v+=1):e="TEST_OR_SSR",e)))},[]),e||a};var k=function(e){var r=e.bg,o=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},o)};function y(e,t){return Object.keys(e).map(function(r){var o=parseFloat(r),a="".concat(Math.floor(o*t),"%");return"".concat(e[r]," ").concat(a)})}var w=t.forwardRef(function(e,r){var o=e.prefixCls,a=e.color,l=e.gradientId,n=e.radius,s=e.style,i=e.ptg,c=e.strokeLinecap,d=e.strokeWidth,u=e.size,m=e.gapDegree,g=a&&"object"===(0,f.default)(a),p=u/2,h=t.createElement("circle",{className:"".concat(o,"-circle-path"),r:n,cx:p,cy:p,stroke:g?"#FFF":void 0,strokeLinecap:c,strokeWidth:d,opacity:+(0!==i),style:s,ref:r});if(!g)return h;var b="".concat(l,"-conic"),v=y(a,(360-m)/360),C=y(a,1),x="conic-gradient(from ".concat(m?"".concat(180+m/2,"deg"):"0deg",", ").concat(v.join(", "),")"),w="linear-gradient(to ".concat(m?"bottom":"top",", ").concat(C.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:b},h),t.createElement("foreignObject",{x:0,y:0,width:u,height:u,mask:"url(#".concat(b,")")},t.createElement(k,{bg:w},t.createElement(k,{bg:x}))))}),$=function(e,t,r,o,a,l,n,s,i,c){var d=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,u=(100-o)/100*t;return"round"===i&&100!==o&&(u+=c/2)>=t&&(u=t-.01),{stroke:"string"==typeof s?s:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:u+d,transform:"rotate(".concat(a+r/100*360*((360-l)/360)+(0===l?0:({bottom:0,top:180,left:90,right:-90})[n]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},S=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function E(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let N=function(e){var r,o,a,l,n=(0,u.default)((0,u.default)({},g),e),i=n.id,c=n.prefixCls,h=n.steps,b=n.strokeWidth,v=n.trailWidth,C=n.gapDegree,k=void 0===C?0:C,y=n.gapPosition,N=n.trailColor,T=n.strokeLinecap,B=n.style,M=n.className,z=n.strokeColor,j=n.percent,P=(0,m.default)(n,S),O=x(i),R="".concat(O,"-gradient"),I=50-b/2,A=2*Math.PI*I,L=k>0?90+k/2:-90,H=(360-k)/360*A,X="object"===(0,f.default)(h)?h:{count:h,gap:2},D=X.count,W=X.gap,_=E(j),F=E(z),V=F.find(function(e){return e&&"object"===(0,f.default)(e)}),Y=V&&"object"===(0,f.default)(V)?"butt":T,G=$(A,H,0,100,L,k,y,N,Y,b),K=p();return t.createElement("svg",(0,d.default)({className:(0,s.default)("".concat(c,"-circle"),M),viewBox:"0 0 ".concat(100," ").concat(100),style:B,id:i,role:"presentation"},P),!D&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:I,cx:50,cy:50,stroke:N,strokeLinecap:Y,strokeWidth:v||b,style:G}),D?(r=Math.round(D*(_[0]/100)),o=100/D,a=0,Array(D).fill(null).map(function(e,l){var n=l<=r-1?F[0]:N,s=n&&"object"===(0,f.default)(n)?"url(#".concat(R,")"):void 0,i=$(A,H,a,o,L,k,y,n,"butt",b,W);return a+=(H-i.strokeDashoffset+W)*100/H,t.createElement("circle",{key:l,className:"".concat(c,"-circle-path"),r:I,cx:50,cy:50,stroke:s,strokeWidth:b,opacity:1,style:i,ref:function(e){K[l]=e}})})):(l=0,_.map(function(e,r){var o=F[r]||F[F.length-1],a=$(A,H,l,e,L,k,y,o,Y,b);return l+=e,t.createElement(w,{key:r,color:o,ptg:e,radius:I,prefixCls:c,gradientId:R,style:a,strokeLinecap:Y,strokeWidth:b,gapDegree:k,ref:function(e){K[r]=e},size:100})}).reverse()))};var T=e.i(491816);e.i(765846);var B=e.i(896091);function M(e){return!e||e<0?0:e>100?100:e}function z({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let j=(e,t,r)=>{var o,a,l,n;let s=-1,i=-1;if("step"===t){let t=r.steps,o=r.strokeWidth;"string"==typeof e||void 0===e?(s="small"===e?2:14,i=null!=o?o:8):"number"==typeof e?[s,i]=[e,e]:[s=14,i=8]=Array.isArray(e)?e:[e.width,e.height],s*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?i=t||("small"===e?6:8):"number"==typeof e?[s,i]=[e,e]:[s=-1,i=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[s,i]="small"===e?[60,60]:[120,120]:"number"==typeof e?[s,i]=[e,e]:Array.isArray(e)&&(s=null!=(a=null!=(o=e[0])?o:e[1])?a:120,i=null!=(n=null!=(l=e[0])?l:e[1])?n:120));return[s,i]},P=e=>{let{prefixCls:r,trailColor:o=null,strokeLinecap:a="round",gapPosition:l,gapDegree:n,width:i=120,type:c,children:d,success:u,size:m=i,steps:g}=e,[p,f]=j(m,"circle"),{strokeWidth:h}=e;void 0===h&&(h=Math.max(3/p*100,6));let b=t.useMemo(()=>n||0===n?n:"dashboard"===c?75:void 0,[n,c]),v=(({percent:e,success:t,successPercent:r})=>{let o=M(z({success:t,successPercent:r}));return[o,M(M(e)-o)]})(e),C="[object Object]"===Object.prototype.toString.call(e.strokeColor),x=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||B.presetPrimaryColors.green,t||null]})({success:u,strokeColor:e.strokeColor}),k=(0,s.default)(`${r}-inner`,{[`${r}-circle-gradient`]:C}),y=t.createElement(N,{steps:g,percent:g?v[1]:v,strokeWidth:h,trailWidth:h,strokeColor:g?x[1]:x,strokeLinecap:a,trailColor:o,prefixCls:r,gapDegree:b,gapPosition:l||"dashboard"===c&&"bottom"||void 0}),w=p<=20,$=t.createElement("div",{className:k,style:{width:p,height:f,fontSize:.15*p+6}},y,!w&&d);return w?t.createElement(T.default,{title:d},$):$};e.i(296059);var O=e.i(694758),R=e.i(915654),I=e.i(183293),A=e.i(246422),L=e.i(838378);let H="--progress-line-stroke-color",X="--progress-percent",D=e=>{let t=e?"100%":"-100%";return new O.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},W=(0,A.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,L.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,I.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${H})`]},height:"100%",width:`calc(1 / var(${X}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,R.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:D(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:D(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var _=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let F=e=>{let{prefixCls:r,direction:o,percent:a,size:l,strokeWidth:n,strokeColor:i,strokeLinecap:c="round",children:d,trailColor:u=null,percentPosition:m,success:g}=e,{align:p,type:f}=m,h=i&&"string"!=typeof i?((e,t)=>{let{from:r=B.presetPrimaryColors.blue,to:o=B.presetPrimaryColors.blue,direction:a="rtl"===t?"to left":"to right"}=e,l=_(e,["from","to","direction"]);if(0!==Object.keys(l).length){let e,t=(e=[],Object.keys(l).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:l[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${a}, ${t})`;return{background:r,[H]:r}}let n=`linear-gradient(${a}, ${r}, ${o})`;return{background:n,[H]:n}})(i,o):{[H]:i,background:i},b="square"===c||"butt"===c?0:void 0,[v,C]=j(null!=l?l:[-1,n||("small"===l?6:8)],"line",{strokeWidth:n}),x=Object.assign(Object.assign({width:`${M(a)}%`,height:C,borderRadius:b},h),{[X]:M(a)/100}),k=z(e),y={width:`${M(k)}%`,height:C,borderRadius:b,backgroundColor:null==g?void 0:g.strokeColor},w=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:u||void 0,borderRadius:b}},t.createElement("div",{className:(0,s.default)(`${r}-bg`,`${r}-bg-${f}`),style:x},"inner"===f&&d),void 0!==k&&t.createElement("div",{className:`${r}-success-bg`,style:y})),$="outer"===f&&"start"===p,S="outer"===f&&"end"===p;return"outer"===f&&"center"===p?t.createElement("div",{className:`${r}-layout-bottom`},w,d):t.createElement("div",{className:`${r}-outer`,style:{width:v<0?"100%":v}},$&&d,w,S&&d)},V=e=>{let{size:r,steps:o,rounding:a=Math.round,percent:l=0,strokeWidth:n=8,strokeColor:i,trailColor:c=null,prefixCls:d,children:u}=e,m=a(l/100*o),[g,p]=j(null!=r?r:["small"===r?2:14,n],"step",{steps:o,strokeWidth:n}),f=g/o,h=Array.from({length:o});for(let e=0;et.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let G=["normal","exception","active","success"],K=t.forwardRef((e,d)=>{let u,{prefixCls:m,className:g,rootClassName:p,steps:f,strokeColor:h,percent:b=0,size:v="default",showInfo:C=!0,type:x="line",status:k,format:y,style:w,percentPosition:$={}}=e,S=Y(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:E="end",type:N="outer"}=$,T=Array.isArray(h)?h[0]:h,B="string"==typeof h||Array.isArray(h)?h:void 0,O=t.useMemo(()=>{if(T){let e="string"==typeof T?T:Object.values(T)[0];return new r.FastColor(e).isLight()}return!1},[h]),R=t.useMemo(()=>{var t,r;let o=z(e);return Number.parseInt(void 0!==o?null==(t=null!=o?o:0)?void 0:t.toString():null==(r=null!=b?b:0)?void 0:r.toString(),10)},[b,e.success,e.successPercent]),I=t.useMemo(()=>!G.includes(k)&&R>=100?"success":k||"normal",[k,R]),{getPrefixCls:A,direction:L,progress:H}=t.useContext(c.ConfigContext),X=A("progress",m),[D,_,K]=W(X),U="line"===x,q=U&&!f,Q=t.useMemo(()=>{let r;if(!C)return null;let i=z(e),c=y||(e=>`${e}%`),d=U&&O&&"inner"===N;return"inner"===N||y||"exception"!==I&&"success"!==I?r=c(M(b),M(i)):"exception"===I?r=U?t.createElement(l.default,null):t.createElement(n.default,null):"success"===I&&(r=U?t.createElement(o.default,null):t.createElement(a.default,null)),t.createElement("span",{className:(0,s.default)(`${X}-text`,{[`${X}-text-bright`]:d,[`${X}-text-${E}`]:q,[`${X}-text-${N}`]:q}),title:"string"==typeof r?r:void 0},r)},[C,b,R,I,x,X,y]);"line"===x?u=f?t.createElement(V,Object.assign({},e,{strokeColor:B,prefixCls:X,steps:"object"==typeof f?f.count:f}),Q):t.createElement(F,Object.assign({},e,{strokeColor:T,prefixCls:X,direction:L,percentPosition:{align:E,type:N}}),Q):("circle"===x||"dashboard"===x)&&(u=t.createElement(P,Object.assign({},e,{strokeColor:T,prefixCls:X,progressStatus:I}),Q));let Z=(0,s.default)(X,`${X}-status-${I}`,{[`${X}-${"dashboard"===x&&"circle"||x}`]:"line"!==x,[`${X}-inline-circle`]:"circle"===x&&j(v,"circle")[0]<=20,[`${X}-line`]:q,[`${X}-line-align-${E}`]:q,[`${X}-line-position-${N}`]:q,[`${X}-steps`]:f,[`${X}-show-info`]:C,[`${X}-${v}`]:"string"==typeof v,[`${X}-rtl`]:"rtl"===L},null==H?void 0:H.className,g,p,_,K);return D(t.createElement("div",Object.assign({ref:d,style:Object.assign(Object.assign({},null==H?void 0:H.style),w),className:Z,role:"progressbar","aria-valuenow":R,"aria-valuemin":0,"aria-valuemax":100},(0,i.default)(S,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),u))});e.s(["default",0,K],309821)},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["default",0,l],597440)},184163,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"download",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["default",0,l],184163)},519756,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"upload",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["UploadOutlined",0,l],519756)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02u6qkt2tomg4.js b/litellm/proxy/_experimental/out/_next/static/chunks/02u6qkt2tomg4.js new file mode 100644 index 00000000000..175f9aa4611 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02u6qkt2tomg4.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),o=e.i(480731),n=e.i(444755),s=e.i(673706),i=e.i(95779);let l={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},u={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},c=(0,s.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:p,variant:f="simple",tooltip:g,size:h=o.Sizes.SM,color:b,className:v}=e,x=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),C=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,s.getColorClassNames)(t,i.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,n.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(f,b),{tooltipProps:y,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,s.mergeRefs)([m,y.refs.setReference]),className:(0,n.tremorTwMerge)(c("root"),"inline-flex shrink-0 items-center justify-center",C.bgColor,C.textColor,C.borderColor,C.ringColor,u[f].rounded,u[f].border,u[f].shadow,u[f].ring,l[h].paddingX,l[h].paddingY,v)},w,x),r.default.createElement(a.default,Object.assign({text:g},y)),r.default.createElement(p,{className:(0,n.tremorTwMerge)(c("icon"),"shrink-0",d[h].height,d[h].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},434626,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,r],434626)},122577,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,r],122577)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},902555,e=>{"use strict";var t=e.i(843476),r=e.i(591935),a=e.i(122577),o=e.i(278587),n=e.i(68155),s=e.i(360820),i=e.i(871943),l=e.i(434626),d=e.i(551332),u=e.i(592968),c=e.i(115504),m=e.i(752978);function p({icon:e,onClick:r,className:a,disabled:o,dataTestId:n}){return o?(0,t.jsx)(m.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":n}):(0,t.jsx)(m.Icon,{icon:e,size:"sm",onClick:r,className:(0,c.cx)("cursor-pointer",a),"data-testid":n})}let f={Edit:{icon:r.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:n.TrashIcon,className:"hover:text-red-600"},Test:{icon:a.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:o.RefreshIcon,className:"hover:text-green-600"},Up:{icon:s.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:i.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:l.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:r,disabled:a=!1,disabledTooltipText:o,dataTestId:n,variant:s}){let{icon:i,className:l}=f[s];return(0,t.jsx)(u.Tooltip,{title:a?o:r,children:(0,t.jsx)("span",{children:(0,t.jsx)(p,{icon:i,onClick:e,className:l,disabled:a,dataTestId:n})})})}],902555)},564897,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M696 480H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"minus-circle",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["MinusCircleOutlined",0,n],564897)},91979,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M909.1 209.3l-56.4 44.1C775.8 155.1 656.2 92 521.9 92 290 92 102.3 279.5 102 511.5 101.7 743.7 289.8 932 521.9 932c181.3 0 335.8-115 394.6-276.1 1.5-4.2-.7-8.9-4.9-10.3l-56.7-19.5a8 8 0 00-10.1 4.8c-1.8 5-3.8 10-5.9 14.9-17.3 41-42.1 77.8-73.7 109.4A344.77 344.77 0 01655.9 829c-42.3 17.9-87.4 27-133.8 27-46.5 0-91.5-9.1-133.8-27A341.5 341.5 0 01279 755.2a342.16 342.16 0 01-73.7-109.4c-17.9-42.4-27-87.4-27-133.9s9.1-91.5 27-133.9c17.3-41 42.1-77.8 73.7-109.4 31.6-31.6 68.4-56.4 109.3-73.8 42.3-17.9 87.4-27 133.8-27 46.5 0 91.5 9.1 133.8 27a341.5 341.5 0 01109.3 73.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.6 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c-.1-6.6-7.8-10.3-13-6.2z"}}]},name:"reload",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["ReloadOutlined",0,n],91979)},751904,e=>{"use strict";var t=e.i(401361);e.s(["EditOutlined",()=>t.default])},678784,e=>{"use strict";var t=e.i(678745);e.s(["CheckIcon",()=>t.default])},118366,e=>{"use strict";var t=e.i(991124);e.s(["CopyIcon",()=>t.default])},987432,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M893.3 293.3L730.7 130.7c-7.5-7.5-16.7-13-26.7-16V112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V338.5c0-17-6.7-33.2-18.7-45.2zM384 184h256v104H384V184zm456 656H184V184h136v136c0 17.7 14.3 32 32 32h320c17.7 0 32-14.3 32-32V205.8l136 136V840zM512 442c-79.5 0-144 64.5-144 144s64.5 144 144 144 144-64.5 144-144-64.5-144-144-144zm0 224c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z"}}]},name:"save",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["SaveOutlined",0,n],987432)},21548,e=>{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},54131,399219,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-up",[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]]);e.s(["default",0,t],399219),e.s(["ChevronUpIcon",0,t],54131)},211576,e=>{"use strict";var t=e.i(131757);e.s(["Col",()=>t.default])},178654,e=>{"use strict";let t=e.i(211576).Col;e.s(["Col",0,t],178654)},621192,e=>{"use strict";let t=e.i(281256).Row;e.s(["Row",0,t],621192)},263147,e=>{"use strict";var t=e.i(266027),r=e.i(243652),a=e.i(602869),o=e.i(431703),n=e.i(708347),s=e.i(135214);let i=(0,r.createQueryKeys)("accessGroups"),l=async e=>{let t=(0,a.getProxyBaseUrl)(),r=`${t}/v1/access_group`,n=await fetch(r,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!n.ok){let e=await n.json(),t=(0,o.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return n.json()};e.s(["accessGroupKeys",0,i,"useAccessGroups",0,()=>{let{accessToken:e,userRole:r}=(0,s.default)();return(0,t.useQuery)({queryKey:i.list({}),queryFn:async()=>l(e),enabled:!!e&&n.all_admin_roles.includes(r||"")})}])},304911,e=>{"use strict";var t=e.i(843476),r=e.i(262218);let{Text:a}=e.i(898586).Typography;e.s(["default",0,function({userId:e}){return"default_user_id"===e?(0,t.jsx)(r.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,t.jsx)(a,{children:e})}])},166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},447566,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["ArrowLeftOutlined",0,n],447566)},502547,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,r],502547)},637235,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"}}]},name:"clock-circle",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["ClockCircleOutlined",0,n],637235)},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["default",0,n],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let o=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],n=e=>({_s:e,status:o[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),s=e=>e?6:5,i=(e,t,r,a,o)=>{clearTimeout(a.current);let s=n(e);t(s),r.current=s,o&&o({current:s})};var l=e.i(480731),d=e.i(444755),u=e.i(673706);let c=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let p={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},f=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,u.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,u.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,u.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,u.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,u.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,u.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,u.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,u.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,u.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,u.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,u.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,u.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,u.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,u.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},g=(0,u.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:o,needMargin:n,transitionStatus:s})=>{let i=n?r===l.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",u=(0,d.tremorTwMerge)("w-0 h-0"),m={default:u,entering:u,entered:t,exiting:t,exited:u};return e?a.default.createElement(c,{className:(0,d.tremorTwMerge)(g("icon"),"animate-spin shrink-0",i,m.default,m[s]),style:{transition:"width 150ms"}}):a.default.createElement(o,{className:(0,d.tremorTwMerge)(g("icon"),"shrink-0",t,i)})},b=a.default.forwardRef((e,o)=>{let{icon:c,iconPosition:m=l.HorizontalPositions.Left,size:b=l.Sizes.SM,color:v,variant:x="primary",disabled:C,loading:y=!1,loadingText:w,children:S,tooltip:k,className:N}=e,M=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),D=y||C,O=void 0!==c||y,E=y&&w,R=!(!S&&!E),P=(0,d.tremorTwMerge)(p[b].height,p[b].width),T="light"!==x?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",j=f(x,v),I=("light"!==x?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:_,getReferenceProps:$}=(0,r.useTooltip)(300),[B,z]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:o,timeout:l,initialEntered:d,mountOnEnter:u,unmountOnExit:c,onStateChange:m}={})=>{let[p,f]=(0,a.useState)(()=>n(d?2:s(u))),g=(0,a.useRef)(p),h=(0,a.useRef)(0),[b,v]="object"==typeof l?[l.enter,l.exit]:[l,l],x=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return s(t)}})(g.current._s,c);e&&i(e,f,g,h,m)},[m,c]);return[p,(0,a.useCallback)(a=>{let n=e=>{switch(i(e,f,g,h,m),e){case 1:b>=0&&(h.current=((...e)=>setTimeout(...e))(x,b));break;case 4:v>=0&&(h.current=((...e)=>setTimeout(...e))(x,v));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||n(e+1)},0)}},l=g.current.isEnter;"boolean"!=typeof a&&(a=!l),a?l||n(e?+!r:2):l&&n(t?o?3:4:s(c))},[x,m,e,t,r,o,b,v,c]),x]})({timeout:50});return(0,a.useEffect)(()=>{z(y)},[y]),a.default.createElement("button",Object.assign({ref:(0,u.mergeRefs)([o,_.refs.setReference]),className:(0,d.tremorTwMerge)(g("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",T,I.paddingX,I.paddingY,I.fontSize,j.textColor,j.bgColor,j.borderColor,j.hoverBorderColor,D?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(f(x,v).hoverTextColor,f(x,v).hoverBgColor,f(x,v).hoverBorderColor),N),disabled:D},$,M),a.default.createElement(r.default,Object.assign({text:k},_)),O&&m!==l.HorizontalPositions.Right?a.default.createElement(h,{loading:y,iconSize:P,iconPosition:m,Icon:c,transitionStatus:B.status,needMargin:R}):null,E||S?a.default.createElement("span",{className:(0,d.tremorTwMerge)(g("text"),"text-tremor-default whitespace-nowrap")},E?w:S):null,O&&m===l.HorizontalPositions.Right?a.default.createElement(h,{loading:y,iconSize:P,iconPosition:m,Icon:c,transitionStatus:B.status,needMargin:R}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),o=e.i(271645);let n=o.default.forwardRef((e,n)=>{let{color:s,className:i,children:l}=e;return o.default.createElement("p",{ref:n,className:(0,r.tremorTwMerge)("text-tremor-default",s?(0,a.getColorClassNames)(s,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},l)});n.displayName="Text",e.s(["default",0,n],936325),e.s(["Text",0,n],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),o=e.i(95779),n=e.i(444755),s=e.i(673706);let i=(0,s.makeClassName)("Card"),l=r.default.forwardRef((e,l)=>{let{decoration:d="",decorationColor:u,children:c,className:m}=e,p=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:l,className:(0,n.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",u?(0,s.getColorClassNames)(u,o.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},p),c)});l.displayName="Card",e.s(["Card",0,l],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),a=e.i(444755),o=e.i(673706),n=e.i(271645);let s=n.default.forwardRef((e,s)=>{let{color:i,children:l,className:d}=e,u=(0,t.__rest)(e,["color","children","className"]);return n.default.createElement("p",Object.assign({ref:s,className:(0,a.tremorTwMerge)("font-medium text-tremor-title",i?(0,o.getColorClassNames)(i,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},u),l)});s.displayName="Title",e.s(["Title",0,s],629569)},891547,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(199133),o=e.i(602869);e.s(["default",0,({onChange:e,value:n,className:s,accessToken:i,disabled:l})=>{let[d,u]=(0,r.useState)([]),[c,m]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(i){m(!0);try{let e=await (0,o.getGuardrailsList)(i);e.guardrails&&u(e.guardrails)}catch(e){console.error("Error fetching guardrails:",e)}finally{m(!1)}}})()},[i]),(0,t.jsx)("div",{children:(0,t.jsx)(a.Select,{mode:"multiple",disabled:l,placeholder:l?"Setting guardrails is a premium feature.":"Select guardrails",onChange:t=>{e(t)},value:n,loading:c,className:s,allowClear:!0,options:d.map(e=>({label:`${e.guardrail_name}`,value:e.guardrail_name})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"}})})}])},921511,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(199133),o=e.i(602869);function n(e){return e.filter(e=>(e.version_status??"draft")!=="draft").map(e=>{var t;let r=e.version_number??1,a=e.version_status??"draft";return{label:`${e.policy_name} — v${r} (${a})${e.description?` — ${e.description}`:""}`,value:"production"===a?e.policy_name:e.policy_id?(t=e.policy_id,`policy_${t}`):e.policy_name}})}e.s(["default",0,({onChange:e,value:s,className:i,accessToken:l,disabled:d,onPoliciesLoaded:u})=>{let[c,m]=(0,r.useState)([]),[p,f]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(l){f(!0);try{let e=await (0,o.getPoliciesList)(l);e.policies&&(m(e.policies),u?.(e.policies))}catch(e){console.error("Error fetching policies:",e)}finally{f(!1)}}})()},[l,u]),(0,t.jsx)("div",{children:(0,t.jsx)(a.Select,{mode:"multiple",disabled:d,placeholder:d?"Setting policies is a premium feature.":"Select policies (production or published versions)",onChange:t=>{e(t)},value:s,loading:p,className:i,allowClear:!0,options:n(c),optionFilterProp:"label",showSearch:!0,style:{width:"100%"}})})},"getPolicyOptionEntries",0,n])},595727,234662,677241,281092,688594,e=>{"use strict";let t=Symbol.for("constructDateFrom");function r(e,r){return"function"==typeof e?e(r):e&&"object"==typeof e&&t in e?e[t](r):e instanceof Date?new e.constructor(r):new Date(r)}function a(e,t){return r(t||e,e)}e.s(["constructFromSymbol",0,t,"millisecondsInDay",0,864e5,"millisecondsInHour",0,36e5,"millisecondsInMinute",0,6e4,"millisecondsInSecond",0,1e3,"millisecondsInWeek",0,6048e5],234662),e.s(["constructFrom",0,r],677241),e.s(["toDate",0,a],281092),e.s(["addDays",0,function(e,t,o){let n=a(e,o?.in);return isNaN(t)?r(o?.in||e,NaN):(t&&n.setDate(n.getDate()+t),n)}],595727),e.s(["addMonths",0,function(e,t,o){let n=a(e,o?.in);if(isNaN(t))return r(o?.in||e,NaN);if(!t)return n;let s=n.getDate(),i=r(o?.in||e,n.getTime());return(i.setMonth(n.getMonth()+t+1,0),s>=i.getDate())?i:(n.setFullYear(i.getFullYear(),i.getMonth(),s),n)}],688594)},24529,e=>{"use strict";var t=e.i(595727),r=e.i(688594),a=e.i(677241),o=e.i(281092);function n(e,n,s){let{years:i=0,months:l=0,weeks:d=0,days:u=0,hours:c=0,minutes:m=0,seconds:p=0}=n,f=(0,o.toDate)(e,s?.in),g=l||i?(0,r.addMonths)(f,l+12*i):f,h=u||d?(0,t.addDays)(g,u+7*d):g;return(0,a.constructFrom)(s?.in||e,+h+1e3*(p+60*(m+60*c)))}let s=/[zZ]$|[+-]\d{2}:?\d{2}$/;function i(e){return Date.parse(s.test(e)?e:`${e}Z`)}e.s(["calculateExpiryPreviewFromDuration",0,function(e){if(!e)return null;try{let t,r=parseInt(e);if(Number.isNaN(r))throw Error("Invalid duration format");let a=new Date;if(e.endsWith("mo"))t=n(a,{months:r});else if(e.endsWith("s"))t=n(a,{seconds:r});else if(e.endsWith("m"))t=n(a,{minutes:r});else if(e.endsWith("h"))t=n(a,{hours:r});else if(e.endsWith("d"))t=n(a,{days:r});else if(e.endsWith("w"))t=n(a,{weeks:r});else throw Error("Invalid duration format");return t.toLocaleString()}catch{return null}},"formatExpiresUtc",0,function(e){let t=i(e);return Number.isNaN(t)?e:new Date(t).toLocaleString()},"isKeyExpired",0,function(e){if(!e)return!1;let t=i(e);return!Number.isNaN(t)&&t{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M848 359.3H627.7L825.8 109c4.1-5.3.4-13-6.3-13H436c-2.8 0-5.5 1.5-6.9 4L170 547.5c-3.1 5.3.7 12 6.9 12h174.4l-89.4 357.6c-1.9 7.8 7.5 13.3 13.3 7.7L853.5 373c5.2-4.9 1.7-13.7-5.5-13.7zM378.2 732.5l60.3-241H281.1l189.6-327.4h224.6L487 427.4h211L378.2 732.5z"}}]},name:"thunderbolt",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["ThunderboltOutlined",0,n],962944)},72713,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"}}]},name:"calendar",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["CalendarOutlined",0,n],72713)},140928,e=>{"use strict";var t=e.i(271645),r=e.i(152473);e.s(["useDebouncedValue",0,function(e,a){let[o,n,s]=(0,r.useDebouncedState)(e,a);return(0,t.useEffect)(()=>(n(e),()=>{s.cancel()}),[e,n,s]),[o,s]}])},995926,e=>{"use strict";var t=e.i(841947);e.s(["XIcon",()=>t.default])},108821,e=>{"use strict";var t=e.i(733332),r=e.i(271645);let a=r.createContext(!1),o=r.createContext(void 0);e.s(["DialogRootContext",0,o,"IsDrawerContext",0,a,"useDialogRootContext",0,function(e){let a=r.useContext(o);if(!1===e&&void 0===a)throw Error((0,t.default)(27));return a}])},402820,156736,209793,625834,784324,264951,e=>{"use strict";e.i(247167);var t,r,a=e.i(271645),o=e.i(108821),n=e.i(552245),s=e.i(405005),i=e.i(209407);let l={...s.popupStateMapping,...i.transitionStatusMapping},d=a.forwardRef(function(e,t){let{render:r,className:a,style:s,forceRender:i=!1,...d}=e,{store:u}=(0,o.useDialogRootContext)(),c=u.useState("open"),m=u.useState("nested"),p=u.useState("mounted"),f=u.useState("transitionStatus");return(0,n.useRenderElement)("div",e,{state:{open:c,transitionStatus:f},ref:[u.context.backdropRef,t],stateAttributesMapping:l,props:[{role:"presentation",hidden:!p,style:{userSelect:"none",WebkitUserSelect:"none"}},d],enabled:i||!m})});e.s(["DialogBackdrop",0,d],402820);var u=e.i(540886),c=e.i(675606),m=e.i(56434);let p=a.forwardRef(function(e,t){let{render:r,className:a,style:s,disabled:i=!1,nativeButton:l=!0,...d}=e,{store:p}=(0,o.useDialogRootContext)(),f=p.useState("open"),{getButtonProps:g,buttonRef:h}=(0,u.useButton)({disabled:i,native:l});return(0,n.useRenderElement)("button",e,{state:{disabled:i},ref:[t,h],props:[{onClick:function(e){f&&p.setOpen(!1,(0,c.createChangeEventDetails)(m.REASONS.closePress,e.nativeEvent))}},d,g]})});e.s(["DialogClose",0,p],156736);var f=e.i(788015);let g=a.forwardRef(function(e,t){let{render:r,className:a,style:s,id:i,...l}=e,{store:d}=(0,o.useDialogRootContext)(),u=(0,f.useBaseUiId)(i);return d.useSyncedValueWithCleanup("descriptionElementId",u),(0,n.useRenderElement)("p",e,{ref:t,props:[{id:u},l]})});e.s(["DialogDescription",0,g],209793);var h=e.i(61487);let b=((t={}).nestedDialogs="--nested-dialogs",t),v=((r={})[r.open=s.CommonPopupDataAttributes.open]="open",r[r.closed=s.CommonPopupDataAttributes.closed]="closed",r[r.startingStyle=s.CommonPopupDataAttributes.startingStyle]="startingStyle",r[r.endingStyle=s.CommonPopupDataAttributes.endingStyle]="endingStyle",r.nested="data-nested",r.nestedDialogOpen="data-nested-dialog-open",r);var x=e.i(733332);let C=a.createContext(void 0);function y(){let e=a.useContext(C);if(void 0===e)throw Error((0,x.default)(26));return e}e.s(["DialogPortalContext",0,C,"useDialogPortalContext",0,y],625834);var w=e.i(137584),S=e.i(673327),k=e.i(264111),N=e.i(843476);let M={...s.popupStateMapping,...i.transitionStatusMapping,nestedDialogOpen:e=>e?{[v.nestedDialogOpen]:""}:null},D=a.forwardRef(function(e,t){let{render:r,className:a,style:s,finalFocus:i,initialFocus:l,...d}=e,{store:u}=(0,o.useDialogRootContext)(),c=u.useState("descriptionElementId"),m=u.useState("disablePointerDismissal"),p=u.useState("floatingRootContext"),f=u.useState("popupProps"),g=u.useState("modal"),v=u.useState("mounted"),x=u.useState("nested"),C=u.useState("nestedOpenDialogCount"),D=u.useState("open"),O=u.useState("openMethod"),E=u.useState("titleElementId"),R=u.useState("transitionStatus"),P=u.useState("role"),T=p.useState("floatingId"),j=d.id??T;y(),(0,w.useOpenChangeComplete)({open:D,ref:u.context.popupRef,onComplete(){D&&u.context.onOpenChangeComplete?.(!0)}});let I=void 0===l?(0,k.createDefaultInitialFocus)(u.context.popupRef):l,_=u.useStateSetter("popupElement"),$=(0,n.useRenderElement)("div",e,{state:{open:D,nested:x,transitionStatus:R,nestedDialogOpen:C>0},props:[f,{id:j,"aria-labelledby":E??void 0,"aria-describedby":c??void 0,role:P,...k.FOCUSABLE_POPUP_PROPS,hidden:!v,onKeyDown(e){S.COMPOSITE_KEYS.has(e.key)&&e.stopPropagation()},style:{[b.nestedDialogs]:C}},d],ref:[t,u.context.popupRef,_],stateAttributesMapping:M});return(0,N.jsx)(h.FloatingFocusManager,{context:p,openInteractionType:O,disabled:!v,closeOnFocusOut:!m,initialFocus:I,returnFocus:i,modal:!1!==g,restoreFocus:"popup",children:$})});e.s(["DialogPopup",0,D],784324);var O=e.i(144394),E=e.i(726674),R=e.i(426);let P=a.forwardRef(function(e,t){let{keepMounted:r=!1,...a}=e,{store:n}=(0,o.useDialogRootContext)(),s=n.useState("mounted"),i=n.useState("modal"),l=n.useState("open");return s||r?(0,N.jsx)(C.Provider,{value:r,children:(0,N.jsxs)(E.FloatingPortal,{ref:t,...a,children:[s&&!0===i&&(0,N.jsx)(R.InternalBackdrop,{ref:n.context.internalBackdropRef,inert:(0,O.inertValue)(!l)}),e.children]})}):null});e.s(["DialogPortal",0,P],264951)},67530,e=>{"use strict";var t=e.i(271645),r=e.i(145484),a=e.i(956789),o=e.i(17989),n=e.i(647554),s=e.i(675606),i=e.i(56434),l=e.i(264111);e.s(["DialogInteractions",0,function({store:e,parentContext:s,isDrawer:i}){let d=e.useState("open"),u=e.useState("disablePointerDismissal"),c=e.useState("modal"),m=e.useState("popupElement"),p=e.useState("floatingRootContext"),[f,g]=t.useState(0),[h,b]=t.useState(0),v=0===f,x=(0,o.useDismiss)(p,{outsidePressEvent:()=>e.context.internalBackdropRef.current||e.context.backdropRef.current?"intentional":{mouse:"trap-focus"===c?"sloppy":"intentional",touch:"sloppy"},outsidePress(t){if(!e.context.outsidePressEnabledRef.current||"button"in t&&0!==t.button||"touches"in t&&1!==t.touches.length)return!1;let r=(0,n.getTarget)(t);return!!v&&!u&&(!c||!e.context.internalBackdropRef.current&&!e.context.backdropRef.current||e.context.internalBackdropRef.current===r||e.context.backdropRef.current===r||(0,n.contains)(r,m)&&!r?.hasAttribute("data-base-ui-portal"))},escapeKey:v});(0,r.useScrollLock)(d&&!0===c,m),e.useContextCallback("onNestedDialogOpen",(e,t)=>{g(e),b(t)}),e.useContextCallback("onNestedDialogClose",()=>{g(0),b(0)}),t.useEffect(()=>(s?.onNestedDialogOpen&&d&&s.onNestedDialogOpen(f+1,h+ +!!i),s?.onNestedDialogClose&&!d&&s.onNestedDialogClose(),()=>{s?.onNestedDialogClose&&d&&s.onNestedDialogClose()}),[i,d,f,h,s]);let C=x.reference??a.EMPTY_OBJECT,y=x.trigger??a.EMPTY_OBJECT,w=x.floating??a.EMPTY_OBJECT;return(0,l.usePopupInteractionProps)(e,{activeTriggerProps:C,inactiveTriggerProps:y,popupProps:w,nestedOpenDialogCount:f,nestedOpenDrawerCount:h}),null},"useDialogRoot",0,function(e){let{store:r,actionsRef:a}=e,o=r.useState("open");(0,l.usePopupRootSync)(r,o),(0,l.useImplicitActiveTrigger)(r);let{forceUnmount:n}=(0,l.useOpenStateTransitions)(o,r),d=t.useCallback(()=>{r.setOpen(!1,(0,s.createChangeEventDetails)(i.REASONS.imperativeAction))},[r]);t.useImperativeHandle(a,()=>({unmount:n,close:d}),[n,d])}])},366250,301807,e=>{"use strict";var t=e.i(271645),r=e.i(713203),a=e.i(67530),o=e.i(108821),n=e.i(616269),s=e.i(301252),i=e.i(116786),l=e.i(990627),d=e.i(264111);let u={...i.popupStoreSelectors,modal:(0,n.createSelector)(e=>e.modal),nested:(0,n.createSelector)(e=>e.nested),nestedOpenDialogCount:(0,n.createSelector)(e=>e.nestedOpenDialogCount),nestedOpenDrawerCount:(0,n.createSelector)(e=>e.nestedOpenDrawerCount),disablePointerDismissal:(0,n.createSelector)(e=>e.disablePointerDismissal),openMethod:(0,n.createSelector)(e=>e.openMethod),descriptionElementId:(0,n.createSelector)(e=>e.descriptionElementId),titleElementId:(0,n.createSelector)(e=>e.titleElementId),viewportElement:(0,n.createSelector)(e=>e.viewportElement),role:(0,n.createSelector)(e=>e.role)};class c extends s.ReactStore{constructor(e,r,a=!1){const o=new l.PopupTriggerMap,n=function(e={}){return{...(0,i.createInitialPopupStoreState)(),modal:!0,disablePointerDismissal:!1,popupElement:null,viewportElement:null,descriptionElementId:void 0,titleElementId:void 0,openMethod:null,nested:!1,nestedOpenDialogCount:0,nestedOpenDrawerCount:0,role:"dialog",...e}}(e);n.floatingRootContext=(0,i.createPopupFloatingRootContext)(o,r,a),super(n,{popupRef:t.createRef(),backdropRef:t.createRef(),internalBackdropRef:t.createRef(),outsidePressEnabledRef:{current:!0},triggerElements:o,onOpenChange:void 0,onOpenChangeComplete:void 0},u)}setOpen=(e,t)=>{if(t.preventUnmountOnClose=()=>{this.set("preventUnmountingOnClose",!0)},e||null!=t.trigger||null==this.state.activeTriggerId||(t.trigger=this.state.activeTriggerElement??void 0),this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let r={open:e};(0,d.setPopupOpenState)(r,e,t.trigger),this.update(r)};static useStore(e,t){return(0,d.usePopupStore)(e,(e,r)=>new c(t,e,r),!0).store}}e.s(["DialogStore",0,c],301807);var m=e.i(843476);e.s(["useRenderDialogRoot",0,function(e,n="dialog"){let{children:s,open:i,defaultOpen:l=!1,onOpenChange:d,onOpenChangeComplete:u,disablePointerDismissal:p=!1,modal:f=!0,actionsRef:g,handle:h,triggerId:b,defaultTriggerId:v=null}=e,x="alert-dialog"===n,C=(0,o.useDialogRootContext)(!0),y={modal:!!x||f,disablePointerDismissal:x||p,nested:!!C,role:x?"alertdialog":"dialog"},w=c.useStore(h?.store,{open:l,openProp:i,activeTriggerId:v,triggerIdProp:b,...y});(0,r.useOnFirstRender)(()=>{let e=void 0===i&&!1===w.state.open&&!0===l?{open:!0,activeTriggerId:v}:null;x?w.update(e?{...y,...e}:y):e&&w.update(e)}),w.useControlledProp("openProp",i),w.useControlledProp("triggerIdProp",b),w.useSyncedValues(y),w.useContextCallback("onOpenChange",d),w.useContextCallback("onOpenChangeComplete",u);let S=w.useState("open"),k=w.useState("mounted"),N=w.useState("payload");(0,a.useDialogRoot)({store:w,actionsRef:g});let M=t.useMemo(()=>({store:w}),[w]);return(0,m.jsx)(o.IsDrawerContext.Provider,{value:!1,children:(0,m.jsxs)(o.DialogRootContext.Provider,{value:M,children:[(S||k)&&(0,m.jsx)(a.DialogInteractions,{store:w,parentContext:C?.store.context,isDrawer:"drawer"===n}),"function"==typeof s?s({payload:N}):s]})})}],366250)},974217,e=>{"use strict";e.i(247167);var t,r=e.i(271645),a=e.i(552245),o=e.i(405005),n=e.i(209407),s=e.i(108821),i=e.i(625834);let l=((t={})[t.open=o.CommonPopupDataAttributes.open]="open",t[t.closed=o.CommonPopupDataAttributes.closed]="closed",t[t.startingStyle=o.CommonPopupDataAttributes.startingStyle]="startingStyle",t[t.endingStyle=o.CommonPopupDataAttributes.endingStyle]="endingStyle",t.nested="data-nested",t.nestedDialogOpen="data-nested-dialog-open",t),d={...o.popupStateMapping,...n.transitionStatusMapping,nested:e=>e?{[l.nested]:""}:null,nestedDialogOpen:e=>e?{[l.nestedDialogOpen]:""}:null},u=r.forwardRef(function(e,t){let{render:r,className:o,style:n,children:l,...u}=e,c=(0,i.useDialogPortalContext)(),{store:m}=(0,s.useDialogRootContext)(),p=m.useState("open"),f=m.useState("nested"),g=m.useState("transitionStatus"),h=m.useState("nestedOpenDialogCount"),b=m.useState("mounted"),v=m.useStateSetter("viewportElement");return(0,a.useRenderElement)("div",e,{enabled:c||b,state:{open:p,nested:f,transitionStatus:g,nestedDialogOpen:h>0},ref:[t,v],stateAttributesMapping:d,props:[{role:"presentation",hidden:!b,style:{pointerEvents:p?void 0:"none"},children:l},u]})});e.s(["DialogViewport",0,u],974217)},77173,313488,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(108821),a=e.i(552245),o=e.i(788015);let n=t.forwardRef(function(e,t){let{render:n,className:s,style:i,id:l,...d}=e,{store:u}=(0,r.useDialogRootContext)(),c=(0,o.useBaseUiId)(l);return u.useSyncedValueWithCleanup("titleElementId",c),(0,a.useRenderElement)("h2",e,{ref:t,props:[{id:c},d]})});e.s(["DialogTitle",0,n],77173);var s=e.i(733332),i=e.i(540886),l=e.i(405005),d=e.i(638396),u=e.i(264111),c=e.i(385689),m=e.i(32199);let p=t.forwardRef(function(e,n){let{render:p,className:f,style:g,disabled:h=!1,nativeButton:b=!0,id:v,payload:x,handle:C,...y}=e,w=(0,r.useDialogRootContext)(!0),S=C?.store??w?.store;if(!S)throw Error((0,s.default)(79));let k=(0,o.useBaseUiId)(v),N=S.useState("floatingRootContext"),M=S.useState("isOpenedByTrigger",k),D=S.useState("triggerPopupId",k),O=t.useRef(null),{registerTrigger:E,isMountedByThisTrigger:R}=(0,u.useTriggerDataForwarding)(k,O,S,{payload:x}),{getButtonProps:P,buttonRef:T}=(0,i.useButton)({disabled:h,native:b}),j=(0,c.useClick)(N,{enabled:null!=N}),I=(0,m.useOpenMethodTriggerProps)(()=>S.select("open"),e=>{S.set("openMethod",e)}),_=S.useState("triggerProps",R);return(0,a.useRenderElement)("button",e,{state:{disabled:h,open:M},ref:[T,n,E,O],props:[j.reference,_,I,{[d.CLICK_TRIGGER_IDENTIFIER]:"",id:k,"aria-haspopup":"dialog","aria-expanded":M,"aria-controls":D},y,P],stateAttributesMapping:l.triggerOpenStateMapping})});e.s(["DialogTrigger",0,p],313488)},325326,e=>{"use strict";var t=e.i(301807),r=e.i(675606),a=e.i(56434);class o{constructor(e){this.store=e??new t.DialogStore}open(e){let t=e?this.store.context.triggerElements.getById(e):void 0;this.store.setOpen(!0,(0,r.createChangeEventDetails)(a.REASONS.imperativeAction,void 0,t))}openWithPayload(e){this.store.set("payload",e),this.store.setOpen(!0,(0,r.createChangeEventDetails)(a.REASONS.imperativeAction,void 0,void 0))}close(){this.store.setOpen(!1,(0,r.createChangeEventDetails)(a.REASONS.imperativeAction,void 0,void 0))}get isOpen(){return this.store.select("open")}}e.s(["DialogHandle",0,o,"createDialogHandle",0,function(){return new o}])},353753,e=>{"use strict";e.s([],914651),e.i(914651);var t=e.i(402820),r=e.i(156736),a=e.i(209793),o=e.i(784324),n=e.i(264951),s=e.i(271645),i=e.i(108821),l=e.i(366250),d=e.i(974217),u=e.i(77173),c=e.i(313488),m=e.i(325326);e.s(["Backdrop",()=>t.DialogBackdrop,"Close",()=>r.DialogClose,"Description",()=>a.DialogDescription,"Handle",()=>m.DialogHandle,"Popup",()=>o.DialogPopup,"Portal",()=>n.DialogPortal,"Root",0,function(e){let t=s.useContext(i.IsDrawerContext)?"drawer":"dialog";return(0,l.useRenderDialogRoot)(e,t)},"Title",()=>u.DialogTitle,"Trigger",()=>c.DialogTrigger,"Viewport",()=>d.DialogViewport,"createHandle",()=>m.createDialogHandle],828376);var p=e.i(828376);e.s(["Dialog",0,p],353753)},793479,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let o=r.forwardRef(({className:e,type:r,...o},n)=>(0,t.jsx)("input",{type:r,"data-slot":"input",className:(0,a.cn)("h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30","focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50","aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",e),ref:n,...o}));o.displayName="Input",e.s(["Input",0,o])},172410,e=>{"use strict";var t=e.i(271645);let r=t.createContext(void 0),a={disableStyleElements:!1};e.s(["useCSPContext",0,function(){return t.useContext(r)??a}])},550896,e=>{"use strict";var t=e.i(201675);e.s(["SCROLL_EDGE_TOLERANCE_PX",0,1,"getMaxScrollOffset",0,function(e,t){return Math.max(0,e-t)},"normalizeScrollOffset",0,function(e,r){if(r<=0)return 0;let a=(0,t.clamp)(e,0,r),o=r-a,n=a<=1,s=o<=1;return n&&s?a<=o?0:r:n?0:s?r:a}])},60837,e=>{"use strict";var t=e.i(843476);let r="base-ui-disable-scrollbar";e.s(["styleDisableScrollbar",0,{className:r,getElement:e=>(0,t.jsx)("style",{nonce:e,href:r,precedence:"base-ui:low",children:`.${r}{scrollbar-width:none}.${r}::-webkit-scrollbar{display:none}`})}])},822315,(e,t,r)=>{e.e,t.exports=function(){"use strict";var e="millisecond",t="second",r="minute",a="hour",o="week",n="month",s="quarter",i="year",l="date",d="Invalid Date",u=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,c=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,m=function(e,t,r){var a=String(e);return!a||a.length>=t?e:""+Array(t+1-a.length).join(r)+e},p="en",f={};f[p]={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(e){var t=["th","st","nd","rd"],r=e%100;return"["+e+(t[(r-20)%10]||t[r]||t[0])+"]"}};var g="$isDayjsObject",h=function(e){return e instanceof C||!(!e||!e[g])},b=function e(t,r,a){var o;if(!t)return p;if("string"==typeof t){var n=t.toLowerCase();f[n]&&(o=n),r&&(f[n]=r,o=n);var s=t.split("-");if(!o&&s.length>1)return e(s[0])}else{var i=t.name;f[i]=t,o=i}return!a&&o&&(p=o),o||!a&&p},v=function(e,t){if(h(e))return e.clone();var r="object"==typeof t?t:{};return r.date=e,r.args=arguments,new C(r)},x={s:m,z:function(e){var t=-e.utcOffset(),r=Math.abs(t);return(t<=0?"+":"-")+m(Math.floor(r/60),2,"0")+":"+m(r%60,2,"0")},m:function e(t,r){if(t.date(){"use strict";var t=e.i(309821);e.s(["Progress",()=>t.default])},438100,e=>{"use strict";let t=(0,e.i(475254).default)("key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]]);e.s(["KeyIcon",0,t],438100)},988846,e=>{"use strict";var t=e.i(54943);e.s(["SearchIcon",()=>t.default])},95684,e=>{"use strict";var t=e.i(165370);e.s(["Pagination",()=>t.default])},372943,897565,166452,e=>{"use strict";e.i(247167);var t=e.i(8211),r=e.i(271645),a=e.i(343794),o=e.i(529681),n=e.i(242064),s=e.i(704914),i=e.i(876556),l=e.i(290224),d=e.i(251224),u=function(e,t){var r={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(r[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,a=Object.getOwnPropertySymbols(e);ot.indexOf(a[o])&&Object.prototype.propertyIsEnumerable.call(e,a[o])&&(r[a[o]]=e[a[o]]);return r};function c({suffixCls:e,tagName:t,displayName:a}){return a=>r.forwardRef((o,n)=>r.createElement(a,Object.assign({ref:n,suffixCls:e,tagName:t},o)))}let m=r.forwardRef((e,t)=>{let{prefixCls:o,suffixCls:s,className:i,tagName:l}=e,c=u(e,["prefixCls","suffixCls","className","tagName"]),{getPrefixCls:m}=r.useContext(n.ConfigContext),p=m("layout",o),[f,g,h]=(0,d.default)(p),b=s?`${p}-${s}`:p;return f(r.createElement(l,Object.assign({className:(0,a.default)(o||b,i,g,h),ref:t},c)))}),p=r.forwardRef((e,c)=>{let{direction:m}=r.useContext(n.ConfigContext),[p,f]=r.useState([]),{prefixCls:g,className:h,rootClassName:b,children:v,hasSider:x,tagName:C,style:y}=e,w=u(e,["prefixCls","className","rootClassName","children","hasSider","tagName","style"]),S=(0,o.default)(w,["suffixCls"]),{getPrefixCls:k,className:N,style:M}=(0,n.useComponentConfig)("layout"),D=k("layout",g),O="boolean"==typeof x?x:!!p.length||(0,i.default)(v).some(e=>e.type===l.default),[E,R,P]=(0,d.default)(D),T=(0,a.default)(D,{[`${D}-has-sider`]:O,[`${D}-rtl`]:"rtl"===m},N,h,b,R,P),j=r.useMemo(()=>({siderHook:{addSider:e=>{f(r=>[].concat((0,t.default)(r),[e]))},removeSider:e=>{f(t=>t.filter(t=>t!==e))}}}),[]);return E(r.createElement(s.LayoutContext.Provider,{value:j},r.createElement(C,Object.assign({ref:c,className:T,style:Object.assign(Object.assign({},M),y)},S),v)))}),f=c({tagName:"div",displayName:"Layout"})(p),g=c({suffixCls:"header",tagName:"header",displayName:"Header"})(m),h=c({suffixCls:"footer",tagName:"footer",displayName:"Footer"})(m),b=c({suffixCls:"content",tagName:"main",displayName:"Content"})(m);f.Header=g,f.Footer=h,f.Content=b,f.Sider=l.default,f._InternalSiderContext=l.SiderContext,e.s(["Layout",0,f],372943);let v=(0,e.i(475254).default)("layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);e.s(["LayersIcon",0,v],897565);var x=e.i(98740);e.s(["UsersIcon",()=>x.default],166452)},160818,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M854.4 800.9c.2-.3.5-.6.7-.9C920.6 722.1 960 621.7 960 512s-39.4-210.1-104.8-288c-.2-.3-.5-.5-.7-.8-1.1-1.3-2.1-2.5-3.2-3.7-.4-.5-.8-.9-1.2-1.4l-4.1-4.7-.1-.1c-1.5-1.7-3.1-3.4-4.6-5.1l-.1-.1c-3.2-3.4-6.4-6.8-9.7-10.1l-.1-.1-4.8-4.8-.3-.3c-1.5-1.5-3-2.9-4.5-4.3-.5-.5-1-1-1.6-1.5-1-1-2-1.9-3-2.8-.3-.3-.7-.6-1-1C736.4 109.2 629.5 64 512 64s-224.4 45.2-304.3 119.2c-.3.3-.7.6-1 1-1 .9-2 1.9-3 2.9-.5.5-1 1-1.6 1.5-1.5 1.4-3 2.9-4.5 4.3l-.3.3-4.8 4.8-.1.1c-3.3 3.3-6.5 6.7-9.7 10.1l-.1.1c-1.6 1.7-3.1 3.4-4.6 5.1l-.1.1c-1.4 1.5-2.8 3.1-4.1 4.7-.4.5-.8.9-1.2 1.4-1.1 1.2-2.1 2.5-3.2 3.7-.2.3-.5.5-.7.8C103.4 301.9 64 402.3 64 512s39.4 210.1 104.8 288c.2.3.5.6.7.9l3.1 3.7c.4.5.8.9 1.2 1.4l4.1 4.7c0 .1.1.1.1.2 1.5 1.7 3 3.4 4.6 5l.1.1c3.2 3.4 6.4 6.8 9.6 10.1l.1.1c1.6 1.6 3.1 3.2 4.7 4.7l.3.3c3.3 3.3 6.7 6.5 10.1 9.6 80.1 74 187 119.2 304.5 119.2s224.4-45.2 304.3-119.2a300 300 0 0010-9.6l.3-.3c1.6-1.6 3.2-3.1 4.7-4.7l.1-.1c3.3-3.3 6.5-6.7 9.6-10.1l.1-.1c1.5-1.7 3.1-3.3 4.6-5 0-.1.1-.1.1-.2 1.4-1.5 2.8-3.1 4.1-4.7.4-.5.8-.9 1.2-1.4a99 99 0 003.3-3.7zm4.1-142.6c-13.8 32.6-32 62.8-54.2 90.2a444.07 444.07 0 00-81.5-55.9c11.6-46.9 18.8-98.4 20.7-152.6H887c-3 40.9-12.6 80.6-28.5 118.3zM887 484H743.5c-1.9-54.2-9.1-105.7-20.7-152.6 29.3-15.6 56.6-34.4 81.5-55.9A373.86 373.86 0 01887 484zM658.3 165.5c39.7 16.8 75.8 40 107.6 69.2a394.72 394.72 0 01-59.4 41.8c-15.7-45-35.8-84.1-59.2-115.4 3.7 1.4 7.4 2.9 11 4.4zm-90.6 700.6c-9.2 7.2-18.4 12.7-27.7 16.4V697a389.1 389.1 0 01115.7 26.2c-8.3 24.6-17.9 47.3-29 67.8-17.4 32.4-37.8 58.3-59 75.1zm59-633.1c11 20.6 20.7 43.3 29 67.8A389.1 389.1 0 01540 327V141.6c9.2 3.7 18.5 9.1 27.7 16.4 21.2 16.7 41.6 42.6 59 75zM540 640.9V540h147.5c-1.6 44.2-7.1 87.1-16.3 127.8l-.3 1.2A445.02 445.02 0 00540 640.9zm0-156.9V383.1c45.8-2.8 89.8-12.5 130.9-28.1l.3 1.2c9.2 40.7 14.7 83.5 16.3 127.8H540zm-56 56v100.9c-45.8 2.8-89.8 12.5-130.9 28.1l-.3-1.2c-9.2-40.7-14.7-83.5-16.3-127.8H484zm-147.5-56c1.6-44.2 7.1-87.1 16.3-127.8l.3-1.2c41.1 15.6 85 25.3 130.9 28.1V484H336.5zM484 697v185.4c-9.2-3.7-18.5-9.1-27.7-16.4-21.2-16.7-41.7-42.7-59.1-75.1-11-20.6-20.7-43.3-29-67.8 37.2-14.6 75.9-23.3 115.8-26.1zm0-370a389.1 389.1 0 01-115.7-26.2c8.3-24.6 17.9-47.3 29-67.8 17.4-32.4 37.8-58.4 59.1-75.1 9.2-7.2 18.4-12.7 27.7-16.4V327zM365.7 165.5c3.7-1.5 7.3-3 11-4.4-23.4 31.3-43.5 70.4-59.2 115.4-21-12-40.9-26-59.4-41.8 31.8-29.2 67.9-52.4 107.6-69.2zM165.5 365.7c13.8-32.6 32-62.8 54.2-90.2 24.9 21.5 52.2 40.3 81.5 55.9-11.6 46.9-18.8 98.4-20.7 152.6H137c3-40.9 12.6-80.6 28.5-118.3zM137 540h143.5c1.9 54.2 9.1 105.7 20.7 152.6a444.07 444.07 0 00-81.5 55.9A373.86 373.86 0 01137 540zm228.7 318.5c-39.7-16.8-75.8-40-107.6-69.2 18.5-15.8 38.4-29.7 59.4-41.8 15.7 45 35.8 84.1 59.2 115.4-3.7-1.4-7.4-2.9-11-4.4zm292.6 0c-3.7 1.5-7.3 3-11 4.4 23.4-31.3 43.5-70.4 59.2-115.4 21 12 40.9 26 59.4 41.8a373.81 373.81 0 01-107.6 69.2z"}}]},name:"global",theme:"outlined"};var o=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(o.default,(0,t.default)({},e,{ref:n,icon:a}))});e.s(["GlobalOutlined",0,n],160818)},784774,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let o=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("div",{"data-slot":"table-container",className:"relative w-full overflow-x-auto",children:(0,t.jsx)("table",{ref:o,"data-slot":"table",className:(0,a.cn)("w-full caption-bottom text-sm",e),...r})}));o.displayName="Table";let n=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("thead",{ref:o,"data-slot":"table-header",className:(0,a.cn)("[&_tr]:border-b",e),...r}));n.displayName="TableHeader";let s=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("tbody",{ref:o,"data-slot":"table-body",className:(0,a.cn)("[&_tr:last-child]:border-0",e),...r}));s.displayName="TableBody";let i=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("tfoot",{ref:o,"data-slot":"table-footer",className:(0,a.cn)("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",e),...r}));i.displayName="TableFooter";let l=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("tr",{ref:o,"data-slot":"table-row",className:(0,a.cn)("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",e),...r}));l.displayName="TableRow";let d=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("th",{ref:o,"data-slot":"table-head",className:(0,a.cn)("h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",e),...r}));d.displayName="TableHead";let u=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("td",{ref:o,"data-slot":"table-cell",className:(0,a.cn)("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",e),...r}));u.displayName="TableCell",r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("caption",{ref:o,"data-slot":"table-caption",className:(0,a.cn)("mt-4 text-sm text-muted-foreground",e),...r})).displayName="TableCaption",e.s(["Table",0,o,"TableBody",0,s,"TableCell",0,u,"TableFooter",0,i,"TableHead",0,d,"TableHeader",0,n,"TableRow",0,l])},302747,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let o=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("div",{ref:o,"data-slot":"skeleton",className:(0,a.cn)("animate-pulse rounded-md bg-accent",e),...r}));o.displayName="Skeleton",e.s(["Skeleton",0,o])},110204,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let o=r.forwardRef(({className:e,...r},o)=>(0,t.jsx)("label",{ref:o,"data-slot":"label",className:(0,a.cn)("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",e),...r}));o.displayName="Label",e.s(["Label",0,o])},16715,e=>{"use strict";let t=(0,e.i(475254).default)("refresh-cw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);e.s(["RefreshCw",0,t],16715)},590803,e=>{"use strict";e.s(["isElementDisabled",0,function(e){return null==e||e.hasAttribute("disabled")||"true"===e.getAttribute("aria-disabled")}])},53687,673553,395530,e=>{"use strict";var t,r=e.i(271645),a=e.i(921374),o=e.i(667865),n=e.i(146376);e.i(247167);let s=r.createContext({register:()=>{},unregister:()=>{},subscribeMapChange:()=>()=>{},elementsRef:{current:[]},nextIndexRef:{current:0}});var i=e.i(843476);function l(){return new Map}function d(){return new Set}function u(e,t){let r=e.compareDocumentPosition(t);return r&Node.DOCUMENT_POSITION_FOLLOWING||r&Node.DOCUMENT_POSITION_CONTAINED_BY?-1:r&Node.DOCUMENT_POSITION_PRECEDING||r&Node.DOCUMENT_POSITION_CONTAINS?1:0}e.s(["CompositeList",0,function(e){let{children:t,elementsRef:c,labelsRef:m,onMapChange:p}=e,f=(0,o.useStableCallback)(p),g=r.useRef(0),h=(0,a.useRefWithInit)(d).current,b=(0,a.useRefWithInit)(l).current,[v,x]=r.useState(0),C=r.useRef(v),y=(0,o.useStableCallback)((e,t)=>{b.set(e,t??null),C.current+=1,x(C.current)}),w=(0,o.useStableCallback)(e=>{b.delete(e),C.current+=1,x(C.current)}),S=r.useMemo(()=>{let e=new Map;return Array.from(b.keys()).filter(e=>e.isConnected).sort(u).forEach((t,r)=>{let a=b.get(t)??{};e.set(t,{...a,index:r})}),e},[b,v]);(0,n.useIsoLayoutEffect)(()=>{if("function"!=typeof MutationObserver||0===S.size)return;let e=new MutationObserver(e=>{let t=new Set,r=e=>t.has(e)?t.delete(e):t.add(e);e.forEach(e=>{e.removedNodes.forEach(r),e.addedNodes.forEach(r)}),0===t.size&&(C.current+=1,x(C.current))});return S.forEach((t,r)=>{r.parentElement&&e.observe(r.parentElement,{childList:!0})}),()=>{e.disconnect()}},[S]),(0,n.useIsoLayoutEffect)(()=>{C.current===v&&(c.current.length!==S.size&&(c.current.length=S.size),m&&m.current.length!==S.size&&(m.current.length=S.size),g.current=S.size),f(S)},[f,S,c,m,v]),(0,n.useIsoLayoutEffect)(()=>()=>{c.current=[]},[c]),(0,n.useIsoLayoutEffect)(()=>()=>{m&&(m.current=[])},[m]);let k=(0,o.useStableCallback)(e=>(h.add(e),()=>{h.delete(e)}));(0,n.useIsoLayoutEffect)(()=>{h.forEach(e=>e(S))},[h,S]);let N=r.useMemo(()=>({register:y,unregister:w,subscribeMapChange:k,elementsRef:c,labelsRef:m,nextIndexRef:g}),[y,w,k,c,m,g]);return(0,i.jsx)(s.Provider,{value:N,children:t})}],53687);var c=e.i(828918),m=e.i(838452);let p=((t={})[t.None=0]="None",t[t.GuessFromOrder=1]="GuessFromOrder",t);function f(e={}){let{label:t,metadata:a,textRef:o,indexGuessBehavior:i,index:l}=e,{register:d,unregister:u,subscribeMapChange:c,elementsRef:m,labelsRef:g,nextIndexRef:h}=r.useContext(s),b=r.useRef(-1),[v,x]=r.useState(l??(i===p.GuessFromOrder?()=>{if(-1===b.current){let e=h.current;h.current+=1,b.current=e}return b.current}:-1)),C=r.useRef(null),y=r.useCallback(e=>{if(C.current=e,-1!==v&&null!==e&&(m.current[v]=e,g)){let r=void 0!==t;g.current[v]=r?t:o?.current?.textContent??e.textContent}},[v,m,g,t,o]);return(0,n.useIsoLayoutEffect)(()=>{if(null!=l)return;let e=C.current;if(e)return d(e,a),()=>{u(e)}},[l,d,u,a]),(0,n.useIsoLayoutEffect)(()=>{if(null==l)return c(e=>{let t=C.current?e.get(C.current)?.index:null;null!=t&&x(t)})},[l,c,x]),{ref:y,index:v}}e.s(["IndexGuessBehavior",0,p,"useCompositeListItem",0,f],673553),e.s(["useCompositeItem",0,function(e={}){let{highlightItemOnHover:t,highlightedIndex:a,onHighlightedIndexChange:o}=(0,m.useCompositeRootContext)(),{ref:n,index:s}=f(e),i=a===s,l=r.useRef(null),d=(0,c.useMergedRefs)(n,l);return{compositeProps:{tabIndex:i?0:-1,onFocus(){o(s)},onMouseMove(){let e=l.current;if(!t||!e)return;let r=e.hasAttribute("disabled")||"true"===e.ariaDisabled;i||r||e.focus()}},compositeRef:d,index:s}}],395530)},625901,e=>{"use strict";var t=e.i(266027),r=e.i(621482),a=e.i(243652),o=e.i(602869),n=e.i(135214);let s=(0,a.createQueryKeys)("models"),i=(0,a.createQueryKeys)("modelHub"),l=(0,a.createQueryKeys)("allProxyModels");(0,a.createQueryKeys)("selectedTeamModels");let d=(0,a.createQueryKeys)("infiniteModels"),u=(0,a.createQueryKeys)("userModels");e.s(["useAllProxyModels",0,()=>{let{accessToken:e,userId:r,userRole:a}=(0,n.default)();return(0,t.useQuery)({queryKey:l.list({}),queryFn:async()=>await (0,o.modelAvailableCall)(e,r,a,!0,null,!0,!1,"expand"),enabled:!!(e&&r&&a)})},"useInfiniteModelInfo",0,(e=50,t)=>{let{accessToken:a,userId:s,userRole:i}=(0,n.default)();return(0,r.useInfiniteQuery)({queryKey:d.list({filters:{...s&&{userId:s},...i&&{userRole:i},size:e,...t&&{search:t}}}),queryFn:async({pageParam:r})=>await (0,o.modelInfoCall)(a,s,i,r,e,t),initialPageParam:1,getNextPageParam:e=>{if(e.current_page{let{accessToken:e}=(0,n.default)();return(0,t.useQuery)({queryKey:i.list({}),queryFn:async()=>await (0,o.modelHubCall)(e),enabled:!!e})},"useModelsInfo",0,(e=1,r=50,a,i,l,d,u)=>{let{accessToken:c,userId:m,userRole:p}=(0,n.default)();return(0,t.useQuery)({queryKey:s.list({filters:{...m&&{userId:m},...p&&{userRole:p},page:e,size:r,...a&&{search:a},...i&&{modelId:i},...l&&{teamId:l},...d&&{sortBy:d},...u&&{sortOrder:u}}}),queryFn:async()=>await (0,o.modelInfoCall)(c,m,p,e,r,a,i,l,d,u),enabled:!!(c&&m&&p)})},"useUserModels",0,()=>{let{accessToken:e,userId:r,userRole:a}=(0,n.default)();return(0,t.useQuery)({queryKey:u.list({}),queryFn:async()=>(await (0,o.modelAvailableCall)(e,r,a)).data.map(e=>e.id),enabled:!!(e&&r&&a)})}])},738014,e=>{"use strict";var t=e.i(135214),r=e.i(602869),a=e.i(266027);let o=(0,e.i(243652).createQueryKeys)("users");e.s(["useCurrentUser",0,()=>{let{accessToken:e,userId:n}=(0,t.default)();return(0,a.useQuery)({queryKey:o.detail(n),queryFn:async()=>await (0,r.userGetInfoV2)(e),enabled:!!(e&&n)})}])},162386,e=>{"use strict";var t=e.i(843476),r=e.i(625901),a=e.i(109799),o=e.i(785242),n=e.i(738014),s=e.i(199133),i=e.i(981339),l=e.i(592968);let d={label:"All Proxy Models",value:"all-proxy-models"},u={label:"No Default Models",value:"no-default-models"},c=[d,u],m={user:({allProxyModels:e,userModels:t,options:r})=>t&&r?.includeUserModels?t:[],team:({allProxyModels:e,selectedOrganization:t,userModels:r})=>t?t.models.includes(d.value)||0===t.models.length?e:e.filter(e=>t.models.includes(e)):e??[],organization:({allProxyModels:e})=>e,global:({allProxyModels:e})=>e};e.s(["ModelSelect",0,e=>{let{teamID:p,organizationID:f,options:g,context:h,dataTestId:b,value:v=[],onChange:x,style:C}=e,{includeUserModels:y,showAllTeamModelsOption:w,showAllProxyModelsOverride:S,includeSpecialOptions:k}=g||{},{data:N,isLoading:M}=(0,r.useAllProxyModels)(),{data:D,isLoading:O}=(0,o.useTeam)(p),{data:E,isLoading:R}=(0,a.useOrganization)(f),{data:P,isLoading:T}=(0,n.useCurrentUser)(),j=e=>c.some(t=>t.value===e),I=v.some(j),_=E?.models.includes(d.value)||E?.models.length===0;if(M||O||R||T)return(0,t.jsx)(i.Skeleton.Input,{active:!0,block:!0});let{wildcard:$,regular:B}=(e=>{let t=[],r=[];for(let a of e)a.endsWith("/*")?t.push(a):r.push(a);return{wildcard:t,regular:r}})(((e,t,r)=>{let a=Array.from(new Map(e.map(e=>[e.id,e])).values()).map(e=>e.id);if(t.options?.showAllProxyModelsOverride)return a;let o=m[t.context];return o?o({allProxyModels:a,...r,options:t.options}):[]})(N?.data??[],e,{selectedTeam:D,selectedOrganization:E,userModels:P?.models}));return(0,t.jsx)(s.Select,{"data-testid":b,value:v,onChange:e=>{let t=e.filter(j);x(t.length>0?[t[t.length-1]]:e)},style:C,options:[...k?[{label:(0,t.jsx)("span",{children:"Special Options"}),title:"Special Options",options:[...S||_&&k||"global"===h?[{label:(0,t.jsx)("span",{children:"All Proxy Models"}),value:d.value,disabled:v.length>0&&v.some(e=>j(e)&&e!==d.value),key:d.value}]:[],{label:(0,t.jsx)("span",{children:"No Default Models"}),value:u.value,disabled:v.length>0&&v.some(e=>j(e)&&e!==u.value),key:u.value}]}]:[],...$.length>0?[{label:(0,t.jsx)("span",{children:"Wildcard Options"}),title:"Wildcard Options",options:$.map(e=>{let r=e.replace("/*",""),a=r.charAt(0).toUpperCase()+r.slice(1);return{label:(0,t.jsx)("span",{children:`All ${a} models`}),value:e,disabled:I}})}]:[],{label:(0,t.jsx)("span",{children:"Models"}),title:"Models",options:B.map(e=>({label:(0,t.jsx)("span",{children:e}),value:e,disabled:I}))}],mode:"multiple",placeholder:"Select Models",allowClear:!0,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(l.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})})}],162386)},907308,276173,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(212931),o=e.i(808613),n=e.i(464571),s=e.i(199133),i=e.i(592968),l=e.i(213205),d=e.i(374009),u=e.i(602869);e.s(["default",0,({isVisible:e,onCancel:c,onSubmit:m,accessToken:p,title:f="Add Team Member",roles:g=[{label:"admin",value:"admin",description:"Admin role. Can create team keys, add members, and manage settings."},{label:"user",value:"user",description:"User role. Can view team info, but not manage it."}],defaultRole:h="user",teamId:b})=>{let[v]=o.Form.useForm(),[x,C]=(0,r.useState)([]),[y,w]=(0,r.useState)(!1),[S,k]=(0,r.useState)("user_email"),[N,M]=(0,r.useState)(!1),D=async(e,t)=>{if(!e)return void C([]);w(!0);try{let r=new URLSearchParams;if(r.append(t,e),b&&r.append("team_id",b),null==p)return;let a=(await (0,u.userFilterUICall)(p,r)).map(e=>({label:"user_email"===t?`${e.user_email}`:`${e.user_id}`,value:"user_email"===t?e.user_email:e.user_id,user:e}));C(a)}catch(e){console.error("Error fetching users:",e)}finally{w(!1)}},O=(0,r.useCallback)((0,d.default)((e,t)=>D(e,t),300),[]),E=(e,t)=>{k(t),O(e,t)},R=(e,t)=>{let r=t.user;v.setFieldsValue({user_email:r.user_email,user_id:r.user_id,role:v.getFieldValue("role")})},P=async e=>{M(!0);try{await m(e)}finally{M(!1)}};return(0,t.jsx)(a.Modal,{title:f,open:e,onCancel:()=>{v.resetFields(),C([]),c()},footer:null,width:800,maskClosable:!N,children:(0,t.jsxs)(o.Form,{form:v,onFinish:P,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:{role:h},children:[(0,t.jsx)(o.Form.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,t.jsx)(s.Select,{showSearch:!0,className:"w-full",placeholder:"Search by email",filterOption:!1,onSearch:e=>E(e,"user_email"),onSelect:(e,t)=>R(e,t),options:"user_email"===S?x:[],loading:y,allowClear:!0,"data-testid":"member-email-search"})}),(0,t.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,t.jsx)(o.Form.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,t.jsx)(s.Select,{showSearch:!0,className:"w-full",placeholder:"Search by user ID",filterOption:!1,onSearch:e=>E(e,"user_id"),onSelect:(e,t)=>R(e,t),options:"user_id"===S?x:[],loading:y,allowClear:!0})}),(0,t.jsx)(o.Form.Item,{label:"Member Role",name:"role",className:"mb-4",children:(0,t.jsx)(s.Select,{defaultValue:h,children:g.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:(0,t.jsxs)(i.Tooltip,{title:e.description,children:[(0,t.jsx)("span",{className:"font-medium",children:e.label}),(0,t.jsxs)("span",{className:"ml-2 text-gray-500 text-sm",children:["- ",e.description]})]})},e.value))})}),(0,t.jsx)("div",{className:"text-right mt-4",children:(0,t.jsx)(n.Button,{type:"primary",htmlType:"submit",icon:(0,t.jsx)(l.UserAddOutlined,{}),loading:N,children:N?"Adding...":"Add Member"})})]})})}],907308);var c=e.i(599724),m=e.i(779241),p=e.i(435451),f=e.i(860585);e.s(["default",0,({visible:e,onCancel:i,onSubmit:l,initialData:d,mode:u,config:g})=>{let h,[b]=o.Form.useForm(),[v,x]=(0,r.useState)(!1);(0,r.useEffect)(()=>{if(e)if("edit"===u&&d){let e={...d,role:d.role||g.defaultRole,max_budget_in_team:d.max_budget_in_team||null,tpm_limit:d.tpm_limit||null,rpm_limit:d.rpm_limit||null,budget_duration:d.budget_duration||null,allowed_models:d.allowed_models||[]};b.setFieldsValue(e)}else b.resetFields(),b.setFieldsValue({role:g.defaultRole||g.roleOptions[0]?.value})},[e,d,u,b,g.defaultRole,g.roleOptions]);let C=async e=>{try{x(!0);let t=Object.entries(e).reduce((e,[t,r])=>{if("string"==typeof r){let a=r.trim();return""===a&&("max_budget_in_team"===t||"tpm_limit"===t||"rpm_limit"===t)?{...e,[t]:null}:{...e,[t]:a}}return{...e,[t]:r}},{});await Promise.resolve(l(t)),b.resetFields()}catch(e){console.error("Form submission error:",e)}finally{x(!1)}};return(0,t.jsx)(a.Modal,{title:g.title||("add"===u?"Add Member":"Edit Member"),open:e,width:1e3,footer:null,onCancel:i,children:(0,t.jsxs)(o.Form,{form:b,onFinish:C,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[g.showEmail&&(0,t.jsx)(o.Form.Item,{label:"Email",name:"user_email",className:"mb-4",rules:[{type:"email",message:"Please enter a valid email!"}],children:(0,t.jsx)(m.TextInput,{placeholder:"user@example.com"})}),g.showEmail&&g.showUserId&&(0,t.jsx)("div",{className:"text-center mb-4",children:(0,t.jsx)(c.Text,{children:"OR"})}),g.showUserId&&(0,t.jsx)(o.Form.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,t.jsx)(m.TextInput,{placeholder:"user_123"})}),(0,t.jsx)(o.Form.Item,{label:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{children:"Role"}),"edit"===u&&d&&(0,t.jsxs)("span",{className:"text-gray-500 text-sm",children:["(Current: ",(h=d.role,g.roleOptions.find(e=>e.value===h)?.label||h),")"]})]}),name:"role",className:"mb-4",rules:[{required:!0,message:"Please select a role!"}],children:(0,t.jsx)(s.Select,{children:"edit"===u&&d?[...g.roleOptions.filter(e=>e.value===d.role),...g.roleOptions.filter(e=>e.value!==d.role)].map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:e.label},e.value)):g.roleOptions.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:e.label},e.value))})}),g.additionalFields?.map(e=>(0,t.jsx)(o.Form.Item,{label:e.label,name:e.name,className:"mb-4",rules:e.rules,children:(e=>{switch(e.type){case"input":return(0,t.jsx)(m.TextInput,{placeholder:e.placeholder});case"numerical":return(0,t.jsx)(p.default,{step:e.step||1,min:e.min||0,style:{width:"100%"},placeholder:e.placeholder||"Enter a numerical value"});case"select":return(0,t.jsx)(s.Select,{children:e.options?.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:e.label},e.value))});case"multi-select":return(0,t.jsx)(s.Select,{mode:"multiple",placeholder:e.placeholder||"Select options",options:e.options,allowClear:!0});case"budget-duration":return(0,t.jsx)(f.default,{});default:return null}})(e)},e.name)),(0,t.jsxs)("div",{className:"text-right mt-6",children:[(0,t.jsx)(n.Button,{onClick:i,className:"mr-2",disabled:v,children:"Cancel"}),(0,t.jsx)(n.Button,{type:"default",htmlType:"submit",loading:v,children:"add"===u?v?"Adding...":"Add Member":v?"Saving...":"Save Changes"})]})]})})}],276173)},294612,e=>{"use strict";var t=e.i(843476),r=e.i(100486),a=e.i(827252),o=e.i(213205),n=e.i(771674),s=e.i(464571),i=e.i(770914),l=e.i(291542),d=e.i(262218),u=e.i(592968),c=e.i(898586),m=e.i(902555);let{Text:p}=c.Typography;e.s(["default",0,function({members:e,canEdit:c,onEdit:f,onDelete:g,onAddMember:h,roleColumnTitle:b="Role",roleTooltip:v,extraColumns:x=[],showDeleteForMember:C,emptyText:y}){let w=[{title:"User Email",dataIndex:"user_email",key:"user_email",render:e=>(0,t.jsx)(p,{children:e||"-"})},{title:"User ID",dataIndex:"user_id",key:"user_id",render:e=>"default_user_id"===e?(0,t.jsx)(d.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,t.jsx)(p,{children:e||"-"})},{title:v?(0,t.jsxs)(i.Space,{direction:"horizontal",children:[b,(0,t.jsx)(u.Tooltip,{title:v,children:(0,t.jsx)(a.InfoCircleOutlined,{})})]}):b,dataIndex:"role",key:"role",render:e=>(0,t.jsxs)(i.Space,{children:[e?.toLowerCase()==="admin"||e?.toLowerCase()==="org_admin"?(0,t.jsx)(r.CrownOutlined,{}):(0,t.jsx)(n.UserOutlined,{}),(0,t.jsx)(p,{style:{textTransform:"capitalize"},children:e||"-"})]})},...x,{title:"Actions",key:"actions",fixed:"right",width:120,render:(e,r)=>c?(0,t.jsxs)(i.Space,{children:[(0,t.jsx)(m.default,{variant:"Edit",tooltipText:"Edit member",dataTestId:"edit-member",onClick:()=>f(r)}),(!C||C(r))&&(0,t.jsx)(m.default,{variant:"Delete",tooltipText:"Delete member",dataTestId:"delete-member",onClick:()=>g(r)})]}):null}];return(0,t.jsxs)(i.Space,{direction:"vertical",style:{width:"100%"},children:[(0,t.jsxs)("span",{className:"inline-flex text-sm text-gray-700",children:[e.length," Member",1!==e.length?"s":""]}),(0,t.jsx)(l.Table,{columns:w,dataSource:e,rowKey:e=>e.user_id??e.user_email??JSON.stringify(e),pagination:!1,size:"small",scroll:{x:"max-content"},locale:y?{emptyText:y}:void 0}),h&&c&&(0,t.jsx)(s.Button,{icon:(0,t.jsx)(o.UserAddOutlined,{}),type:"primary",onClick:h,children:"Add Member"})]})}])},86827,e=>{"use strict";var t=e.i(843476),r=e.i(482725),a=e.i(56456);e.s(["AntDLoadingSpinner",0,function({size:e,fontSize:o}){let n=(0,t.jsx)(a.LoadingOutlined,{style:o?{fontSize:o}:void 0,spin:!0});return(0,t.jsx)(r.Spin,{indicator:n,size:e})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/02zbkoezzcnn1.js b/litellm/proxy/_experimental/out/_next/static/chunks/02zbkoezzcnn1.js new file mode 100644 index 00000000000..62af09f3759 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/02zbkoezzcnn1.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let o=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:o[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),n=e=>e?6:5,i=(e,t,r,a,o)=>{clearTimeout(a.current);let n=l(e);t(n),r.current=n,o&&o({current:n})};var s=e.i(480731),d=e.i(444755),c=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},b=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},f=(0,c.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:o,needMargin:l,transitionStatus:n})=>{let i=l?r===s.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?a.default.createElement(u,{className:(0,d.tremorTwMerge)(f("icon"),"animate-spin shrink-0",i,m.default,m[n]),style:{transition:"width 150ms"}}):a.default.createElement(o,{className:(0,d.tremorTwMerge)(f("icon"),"shrink-0",t,i)})},p=a.default.forwardRef((e,o)=>{let{icon:u,iconPosition:m=s.HorizontalPositions.Left,size:p=s.Sizes.SM,color:C,variant:x="primary",disabled:k,loading:v=!1,loadingText:w,children:$,tooltip:N,className:j}=e,y=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),T=v||k,B=void 0!==u||v,E=v&&w,O=!(!$&&!E),M=(0,d.tremorTwMerge)(g[p].height,g[p].width),S="light"!==x?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",R=b(x,C),P=("light"!==x?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[p],{tooltipProps:z,getReferenceProps:H}=(0,r.useTooltip)(300),[q,A]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:o,timeout:s,initialEntered:d,mountOnEnter:c,unmountOnExit:u,onStateChange:m}={})=>{let[g,b]=(0,a.useState)(()=>l(d?2:n(c))),f=(0,a.useRef)(g),h=(0,a.useRef)(0),[p,C]="object"==typeof s?[s.enter,s.exit]:[s,s],x=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return n(t)}})(f.current._s,u);e&&i(e,b,f,h,m)},[m,u]);return[g,(0,a.useCallback)(a=>{let l=e=>{switch(i(e,b,f,h,m),e){case 1:p>=0&&(h.current=((...e)=>setTimeout(...e))(x,p));break;case 4:C>=0&&(h.current=((...e)=>setTimeout(...e))(x,C));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},s=f.current.isEnter;"boolean"!=typeof a&&(a=!s),a?s||l(e?+!r:2):s&&l(t?o?3:4:n(u))},[x,m,e,t,r,o,p,C,u]),x]})({timeout:50});return(0,a.useEffect)(()=>{A(v)},[v]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([o,z.refs.setReference]),className:(0,d.tremorTwMerge)(f("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",S,P.paddingX,P.paddingY,P.fontSize,R.textColor,R.bgColor,R.borderColor,R.hoverBorderColor,T?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(b(x,C).hoverTextColor,b(x,C).hoverBgColor,b(x,C).hoverBorderColor),j),disabled:T},H,y),a.default.createElement(r.default,Object.assign({text:N},z)),B&&m!==s.HorizontalPositions.Right?a.default.createElement(h,{loading:v,iconSize:M,iconPosition:m,Icon:u,transitionStatus:q.status,needMargin:O}):null,E||$?a.default.createElement("span",{className:(0,d.tremorTwMerge)(f("text"),"text-tremor-default whitespace-nowrap")},E?w:$):null,B&&m===s.HorizontalPositions.Right?a.default.createElement(h,{loading:v,iconSize:M,iconPosition:m,Icon:u,transitionStatus:q.status,needMargin:O}):null)});p.displayName="Button",e.s(["Button",0,p],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),o=e.i(271645);let l=o.default.forwardRef((e,l)=>{let{color:n,className:i,children:s}=e;return o.default.createElement("p",{ref:l,className:(0,r.tremorTwMerge)("text-tremor-default",n?(0,a.getColorClassNames)(n,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},s)});l.displayName="Text",e.s(["default",0,l],936325),e.s(["Text",0,l],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),o=e.i(95779),l=e.i(444755),n=e.i(673706);let i=(0,n.makeClassName)("Card"),s=r.default.forwardRef((e,s)=>{let{decoration:d="",decorationColor:c,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:s,className:(0,l.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,n.getColorClassNames)(c,o.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},g),u)});s.displayName="Card",e.s(["Card",0,s],304967)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(o("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),n))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},s),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",i)},s),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},s),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},s),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("row"),i)},s),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),o=e.i(529681);let l=e=>{let{prefixCls:a,className:o,style:l,size:n,shape:i}=e,s=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),d=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),c=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,s,d,o),style:Object.assign(Object.assign({},c),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),s=e.i(246422),d=e.i(838378);let c=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),u=e=>({height:e,lineHeight:(0,i.unit)(e)}),m=e=>Object.assign({width:e},u(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},u(e)),b=e=>Object.assign({width:e},u(e)),f=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},u(e)),p=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:o,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:s,controlHeightLG:d,controlHeightSM:u,gradientFromColor:p,padding:C,marginSM:x,borderRadius:k,titleHeight:v,blockRadius:w,paragraphLiHeight:$,controlHeightXS:N,paragraphMarginTop:j}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:C,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:p},m(s)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},m(d)),[`${r}-sm`]:Object.assign({},m(u))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:v,background:p,borderRadius:w,[`+ ${o}`]:{marginBlockStart:u}},[o]:{padding:0,"> li":{width:"100%",height:$,listStyle:"none",background:p,borderRadius:w,"+ li":{marginBlockStart:N}}},[`${o}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${o} > li`]:{borderRadius:k}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:x,[`+ ${o}`]:{marginBlockStart:j}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},f(e,a,r)),{[`${r}-lg`]:Object.assign({},h(o,i))}),f(e,o,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),f(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},m(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},m(o)),[`${t}${t}-sm`]:Object.assign({},m(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},g(t,i)),[`${a}-lg`]:Object.assign({},g(o,i)),[`${a}-sm`]:Object.assign({},g(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:o,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:o},b(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${o} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),C=e=>{let{prefixCls:a,className:o,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,o),style:l},i)},x=({prefixCls:e,className:a,width:o,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:o},l)});function k(e){return e&&"object"==typeof e?e:{}}let v=e=>{let{prefixCls:o,loading:n,className:i,rootClassName:s,style:d,children:c,avatar:u=!1,title:m=!0,paragraph:g=!0,active:b,round:f}=e,{getPrefixCls:h,direction:v,className:w,style:$}=(0,a.useComponentConfig)("skeleton"),N=h("skeleton",o),[j,y,T]=p(N);if(n||!("loading"in e)){let e,a,o=!!u,n=!!m,c=!!g;if(o){let r=Object.assign(Object.assign({prefixCls:`${N}-avatar`},n&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),k(u));e=t.createElement("div",{className:`${N}-header`},t.createElement(l,Object.assign({},r)))}if(n||c){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${N}-title`},!o&&c?{width:"38%"}:o&&c?{width:"50%"}:{}),k(m));e=t.createElement(x,Object.assign({},r))}if(c){let e,a=Object.assign(Object.assign({prefixCls:`${N}-paragraph`},(e={},o&&n||(e.width="61%"),!o&&n?e.rows=3:e.rows=2,e)),k(g));r=t.createElement(C,Object.assign({},a))}a=t.createElement("div",{className:`${N}-content`},e,r)}let h=(0,r.default)(N,{[`${N}-with-avatar`]:o,[`${N}-active`]:b,[`${N}-rtl`]:"rtl"===v,[`${N}-round`]:f},w,i,s,y,T);return j(t.createElement("div",{className:h,style:Object.assign(Object.assign({},$),d)},e,a))}return null!=c?c:null};v.Button=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c=!1,size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",n),[b,f,h]=p(g),C=(0,o.default)(e,["prefixCls"]),x=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d,[`${g}-block`]:c},i,s,f,h);return b(t.createElement("div",{className:x},t.createElement(l,Object.assign({prefixCls:`${g}-button`,size:u},C))))},v.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,shape:c="circle",size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",n),[b,f,h]=p(g),C=(0,o.default)(e,["prefixCls","className"]),x=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d},i,s,f,h);return b(t.createElement("div",{className:x},t.createElement(l,Object.assign({prefixCls:`${g}-avatar`,shape:c,size:u},C))))},v.Input=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c,size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",n),[b,f,h]=p(g),C=(0,o.default)(e,["prefixCls"]),x=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d,[`${g}-block`]:c},i,s,f,h);return b(t.createElement("div",{className:x},t.createElement(l,Object.assign({prefixCls:`${g}-input`,size:u},C))))},v.Image=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),c=d("skeleton",o),[u,m,g]=p(c),b=(0,r.default)(c,`${c}-element`,{[`${c}-active`]:s},l,n,m,g);return u(t.createElement("div",{className:b},t.createElement("div",{className:(0,r.default)(`${c}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},v.Node=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s,children:d}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),u=c("skeleton",o),[m,g,b]=p(u),f=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:s},g,l,n,b);return m(t.createElement("div",{className:f},t.createElement("div",{className:(0,r.default)(`${u}-image`,l),style:i},d)))},e.s(["default",0,v],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let o=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(o),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let o={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",o);let l=e<0?"-":"",n=Math.abs(e),i=n,s="";return n>=1e6?(i=n/1e6,s="M"):n>=1e3&&(i=n/1e3,s="K"),`${l}${i.toLocaleString("en-US",o)}${s}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return o(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),o(e,r)}},o=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let o=document.execCommand("copy");if(document.body.removeChild(a),o)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),o=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(o.TooltipProvider,{delay:300,children:(0,t.jsxs)(o.Tooltip,{children:[(0,t.jsx)(o.TooltipTrigger,{render:r}),(0,t.jsx)(o.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:o,tooltip:i,dataTestId:s}){let d=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":s,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:o});return i?(0,t.jsx)(l,{content:i,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],o=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,s,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`,`${s}, ${d} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${a[c.getMonth()]} ${c.getDate()}, ${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:o,copyable:d=!1,truncate:c=!0,fallback:u="-",tooltip:m,disabled:g=!1,dataTestId:b,className:f}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:u});let h=!!o&&!g,p=(0,n.cn)(s[a].base,h&&s[a].clickable,c&&"block max-w-[15ch] truncate",g&&"opacity-50",f),C=h?(0,r.jsx)("button",{type:"button",className:p,"data-testid":b,onClick:()=>o(e),children:e}):(0,r.jsx)("span",{className:p,"data-testid":b,children:e}),x=(0,r.jsx)(t.CellTooltip,{content:m??e,trigger:C});return d?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[x,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):x}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:o=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?o?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03-4f3.602g1r.js b/litellm/proxy/_experimental/out/_next/static/chunks/03-4f3.602g1r.js new file mode 100644 index 00000000000..642068f78e4 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03-4f3.602g1r.js @@ -0,0 +1,13 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,555987,e=>{"use strict";var t=e.i(221688),i=e.i(950643);let n=/^(https?:|data:|blob:|\/\/)/i;e.s(["resolveLogoSrc",0,(e,o=t.serverRootPath)=>{if(e){let t;return n.test(e)?e:(t=(0,i.normalizeRootPath)(o),`${t}${e.startsWith("/")?e:`/${e}`}`)}}])},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},165370,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(931067);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M272.9 512l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512zm304 0l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"}}]},name:"double-left",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,a){return t.createElement(o.default,(0,i.default)({},e,{ref:a,icon:n}))});let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"}}]},name:"double-right",theme:"outlined"};var r=t.forwardRef(function(e,n){return t.createElement(o.default,(0,i.default)({},e,{ref:n,icon:l}))}),c=e.i(801312),u=e.i(286612),s=e.i(343794),d=e.i(211577),m=e.i(410160),g=e.i(209428),p=e.i(392221),f=e.i(914949),b=e.i(404948),v=e.i(244009);e.i(883110);let h={items_per_page:"条/页",jump_to:"跳至",jump_to_confirm:"确定",page:"页",prev_page:"上一页",next_page:"下一页",prev_5:"向前 5 页",next_5:"向后 5 页",prev_3:"向前 3 页",next_3:"向后 3 页",page_size:"页码"};var $=[10,20,50,100];let S=function(e){var i=e.pageSizeOptions,n=void 0===i?$:i,o=e.locale,a=e.changeSize,l=e.pageSize,r=e.goButton,c=e.quickGo,u=e.rootPrefixCls,s=e.disabled,d=e.buildOptionText,m=e.showSizeChanger,g=e.sizeChangerRender,f=t.default.useState(""),v=(0,p.default)(f,2),h=v[0],S=v[1],C=function(){return!h||Number.isNaN(h)?void 0:Number(h)},k="function"==typeof d?d:function(e){return"".concat(e," ").concat(o.items_per_page)},y=function(e){""!==h&&(e.keyCode===b.default.ENTER||"click"===e.type)&&(S(""),null==c||c(C()))},x="".concat(u,"-options");if(!m&&!c)return null;var z=null,E=null,N=null;return m&&g&&(z=g({disabled:s,size:l,onSizeChange:function(e){null==a||a(Number(e))},"aria-label":o.page_size,className:"".concat(x,"-size-changer"),options:(n.some(function(e){return e.toString()===l.toString()})?n:n.concat([l]).sort(function(e,t){return(Number.isNaN(Number(e))?0:Number(e))-(Number.isNaN(Number(t))?0:Number(t))})).map(function(e){return{label:k(e),value:e}})})),c&&(r&&(N="boolean"==typeof r?t.default.createElement("button",{type:"button",onClick:y,onKeyUp:y,disabled:s,className:"".concat(x,"-quick-jumper-button")},o.jump_to_confirm):t.default.createElement("span",{onClick:y,onKeyUp:y},r)),E=t.default.createElement("div",{className:"".concat(x,"-quick-jumper")},o.jump_to,t.default.createElement("input",{disabled:s,type:"text",value:h,onChange:function(e){S(e.target.value)},onKeyUp:y,onBlur:function(e){r||""===h||(S(""),e.relatedTarget&&(e.relatedTarget.className.indexOf("".concat(u,"-item-link"))>=0||e.relatedTarget.className.indexOf("".concat(u,"-item"))>=0)||null==c||c(C()))},"aria-label":o.page}),o.page,N)),t.default.createElement("li",{className:x},z,E)},C=function(e){var i=e.rootPrefixCls,n=e.page,o=e.active,a=e.className,l=e.showTitle,r=e.onClick,c=e.onKeyPress,u=e.itemRender,m="".concat(i,"-item"),g=(0,s.default)(m,"".concat(m,"-").concat(n),(0,d.default)((0,d.default)({},"".concat(m,"-active"),o),"".concat(m,"-disabled"),!n),a),p=u(n,"page",t.default.createElement("a",{rel:"nofollow"},n));return p?t.default.createElement("li",{title:l?String(n):null,className:g,onClick:function(){r(n)},onKeyDown:function(e){c(e,r,n)},tabIndex:0},p):null};var k=function(e,t,i){return i};function y(){}function x(e){var t=Number(e);return"number"==typeof t&&!Number.isNaN(t)&&isFinite(t)&&Math.floor(t)===t}function z(e,t,i){return Math.floor((i-1)/(void 0===e?t:e))+1}let E=function(e){var n,o,a,l,r=e.prefixCls,c=void 0===r?"rc-pagination":r,u=e.selectPrefixCls,$=e.className,E=e.current,N=e.defaultCurrent,j=e.total,B=void 0===j?0:j,M=e.pageSize,O=e.defaultPageSize,w=e.onChange,I=void 0===w?y:w,T=e.hideOnSinglePage,P=e.align,D=e.showPrevNextJumpers,H=e.showQuickJumper,A=e.showLessItems,R=e.showTitle,_=void 0===R||R,W=e.onShowSizeChange,L=void 0===W?y:W,q=e.locale,K=void 0===q?h:q,X=e.style,U=e.totalBoundaryShowSizeChanger,F=e.disabled,J=e.simple,G=e.showTotal,V=e.showSizeChanger,Q=void 0===V?B>(void 0===U?50:U):V,Y=e.sizeChangerRender,Z=e.pageSizeOptions,ee=e.itemRender,et=void 0===ee?k:ee,ei=e.jumpPrevIcon,en=e.jumpNextIcon,eo=e.prevIcon,ea=e.nextIcon,el=t.default.useRef(null),er=(0,f.default)(10,{value:M,defaultValue:void 0===O?10:O}),ec=(0,p.default)(er,2),eu=ec[0],es=ec[1],ed=(0,f.default)(1,{value:E,defaultValue:void 0===N?1:N,postState:function(e){return Math.max(1,Math.min(e,z(void 0,eu,B)))}}),em=(0,p.default)(ed,2),eg=em[0],ep=em[1],ef=t.default.useState(eg),eb=(0,p.default)(ef,2),ev=eb[0],eh=eb[1];(0,t.useEffect)(function(){eh(eg)},[eg]);var e$=Math.max(1,eg-(A?3:5)),eS=Math.min(z(void 0,eu,B),eg+(A?3:5));function eC(i,n){var o=i||t.default.createElement("button",{type:"button","aria-label":n,className:"".concat(c,"-item-link")});return"function"==typeof i&&(o=t.default.createElement(i,(0,g.default)({},e))),o}function ek(e){var t=e.target.value,i=z(void 0,eu,B);return""===t?t:Number.isNaN(Number(t))?ev:t>=i?i:Number(t)}var ey=B>eu&&H;function ex(e){var t=ek(e);switch(t!==ev&&eh(t),e.keyCode){case b.default.ENTER:ez(t);break;case b.default.UP:ez(t-1);break;case b.default.DOWN:ez(t+1)}}function ez(e){if(x(e)&&e!==eg&&x(B)&&B>0&&!F){var t=z(void 0,eu,B),i=e;return e>t?i=t:e<1&&(i=1),i!==ev&&eh(i),ep(i),null==I||I(i,eu),i}return eg}var eE=eg>1,eN=eg2?i-2:0),o=2;oB?B:eg*eu])),eH=null,eA=z(void 0,eu,B);if(T&&B<=eu)return null;var eR=[],e_={rootPrefixCls:c,onClick:ez,onKeyPress:ew,showTitle:_,itemRender:et,page:-1},eW=eg-1>0?eg-1:0,eL=eg+1=2*eF&&3!==eg&&(eR[0]=t.default.cloneElement(eR[0],{className:(0,s.default)("".concat(c,"-item-after-jump-prev"),eR[0].props.className)}),eR.unshift(eT)),eA-eg>=2*eF&&eg!==eA-2){var e2=eR[eR.length-1];eR[eR.length-1]=t.default.cloneElement(e2,{className:(0,s.default)("".concat(c,"-item-before-jump-next"),e2.props.className)}),eR.push(eH)}1!==eZ&&eR.unshift(t.default.createElement(C,(0,i.default)({},e_,{key:1,page:1}))),e0!==eA&&eR.push(t.default.createElement(C,(0,i.default)({},e_,{key:eA,page:eA})))}var e4=(n=et(eW,"prev",eC(eo,"prev page")),t.default.isValidElement(n)?t.default.cloneElement(n,{disabled:!eE}):n);if(e4){var e6=!eE||!eA;e4=t.default.createElement("li",{title:_?K.prev_page:null,onClick:ej,tabIndex:e6?null:0,onKeyDown:function(e){ew(e,ej)},className:(0,s.default)("".concat(c,"-prev"),(0,d.default)({},"".concat(c,"-disabled"),e6)),"aria-disabled":e6},e4)}var e3=(o=et(eL,"next",eC(ea,"next page")),t.default.isValidElement(o)?t.default.cloneElement(o,{disabled:!eN}):o);e3&&(J?(a=!eN,l=eE?0:null):l=(a=!eN||!eA)?null:0,e3=t.default.createElement("li",{title:_?K.next_page:null,onClick:eB,tabIndex:l,onKeyDown:function(e){ew(e,eB)},className:(0,s.default)("".concat(c,"-next"),(0,d.default)({},"".concat(c,"-disabled"),a)),"aria-disabled":a},e3));var e9=(0,s.default)(c,$,(0,d.default)((0,d.default)((0,d.default)((0,d.default)((0,d.default)({},"".concat(c,"-start"),"start"===P),"".concat(c,"-center"),"center"===P),"".concat(c,"-end"),"end"===P),"".concat(c,"-simple"),J),"".concat(c,"-disabled"),F));return t.default.createElement("ul",(0,i.default)({className:e9,style:X,ref:el},eP),eD,e4,J?eU:eR,e3,t.default.createElement(S,{locale:K,rootPrefixCls:c,disabled:F,selectPrefixCls:void 0===u?"rc-select":u,changeSize:function(e){var t=z(e,eu,B),i=eg>t&&0!==t?t:eg;es(e),eh(i),null==L||L(eg,e),ep(i),null==I||I(i,e)},pageSize:eu,pageSizeOptions:Z,quickGo:ey?ez:null,goButton:eX,showSizeChanger:Q,sizeChangerRender:Y}))};var N=e.i(727214),j=e.i(242064),B=e.i(517455),M=e.i(150073),O=e.i(408850),w=e.i(327494),I=e.i(104458);e.i(296059);var T=e.i(915654),P=e.i(349942),D=e.i(517458),H=e.i(889943),A=e.i(183293),R=e.i(246422),_=e.i(838378);let W=e=>Object.assign({itemBg:e.colorBgContainer,itemSize:e.controlHeight,itemSizeSM:e.controlHeightSM,itemActiveBg:e.colorBgContainer,itemActiveColor:e.colorPrimary,itemActiveColorHover:e.colorPrimaryHover,itemLinkBg:e.colorBgContainer,itemActiveColorDisabled:e.colorTextDisabled,itemActiveBgDisabled:e.controlItemBgActiveDisabled,itemInputBg:e.colorBgContainer,miniOptionsSizeChangerTop:0},(0,D.initComponentToken)(e)),L=e=>(0,_.mergeToken)(e,{inputOutlineOffset:0,quickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.25).equal(),paginationMiniOptionsMarginInlineStart:e.calc(e.marginXXS).div(2).equal(),paginationMiniQuickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.1).equal(),paginationItemPaddingInline:e.calc(e.marginXXS).mul(1.5).equal(),paginationEllipsisLetterSpacing:e.calc(e.marginXXS).div(2).equal(),paginationSlashMarginInlineStart:e.marginSM,paginationSlashMarginInlineEnd:e.marginSM,paginationEllipsisTextIndent:"0.13em"},(0,D.initInputToken)(e)),q=(0,R.genStyleHooks)("Pagination",e=>{let t=L(e);return[(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,A.resetComponent)(e)),{display:"flex",flexWrap:"wrap",rowGap:e.paddingXS,"&-start":{justifyContent:"start"},"&-center":{justifyContent:"center"},"&-end":{justifyContent:"end"},"ul, ol":{margin:0,padding:0,listStyle:"none"},"&::after":{display:"block",clear:"both",height:0,overflow:"hidden",visibility:"hidden",content:'""'},[`${t}-total-text`]:{display:"inline-block",height:e.itemSize,marginInlineEnd:e.marginXS,lineHeight:(0,T.unit)(e.calc(e.itemSize).sub(2).equal()),verticalAlign:"middle"}}),(e=>{let{componentCls:t}=e;return{[`${t}-item`]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,marginInlineEnd:e.marginXS,fontFamily:e.fontFamily,lineHeight:(0,T.unit)(e.calc(e.itemSize).sub(2).equal()),textAlign:"center",verticalAlign:"middle",listStyle:"none",backgroundColor:e.itemBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:0,cursor:"pointer",userSelect:"none",a:{display:"block",padding:`0 ${(0,T.unit)(e.paginationItemPaddingInline)}`,color:e.colorText,"&:hover":{textDecoration:"none"}},[`&:not(${t}-item-active)`]:{"&:hover":{transition:`all ${e.motionDurationMid}`,backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive}},"&-active":{fontWeight:e.fontWeightStrong,backgroundColor:e.itemActiveBg,borderColor:e.colorPrimary,a:{color:e.itemActiveColor},"&:hover":{borderColor:e.colorPrimaryHover},"&:hover a":{color:e.itemActiveColorHover}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-jump-prev, ${t}-jump-next`]:{outline:0,[`${t}-item-container`]:{position:"relative",[`${t}-item-link-icon`]:{color:e.colorPrimary,fontSize:e.fontSizeSM,opacity:0,transition:`all ${e.motionDurationMid}`,"&-svg":{top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,margin:"auto"}},[`${t}-item-ellipsis`]:{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,display:"block",margin:"auto",color:e.colorTextDisabled,letterSpacing:e.paginationEllipsisLetterSpacing,textAlign:"center",textIndent:e.paginationEllipsisTextIndent,opacity:1,transition:`all ${e.motionDurationMid}`}},"&:hover":{[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}}},[` + ${t}-prev, + ${t}-jump-prev, + ${t}-jump-next + `]:{marginInlineEnd:e.marginXS},[` + ${t}-prev, + ${t}-next, + ${t}-jump-prev, + ${t}-jump-next + `]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,color:e.colorText,fontFamily:e.fontFamily,lineHeight:(0,T.unit)(e.itemSize),textAlign:"center",verticalAlign:"middle",listStyle:"none",borderRadius:e.borderRadius,cursor:"pointer",transition:`all ${e.motionDurationMid}`},[`${t}-prev, ${t}-next`]:{outline:0,button:{color:e.colorText,cursor:"pointer",userSelect:"none"},[`${t}-item-link`]:{display:"block",width:"100%",height:"100%",padding:0,fontSize:e.fontSizeSM,textAlign:"center",backgroundColor:"transparent",border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:"none",transition:`all ${e.motionDurationMid}`},[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover`]:{[`${t}-item-link`]:{backgroundColor:"transparent"}}},[`${t}-slash`]:{marginInlineEnd:e.paginationSlashMarginInlineEnd,marginInlineStart:e.paginationSlashMarginInlineStart},[`${t}-options`]:{display:"inline-block",marginInlineStart:e.margin,verticalAlign:"middle","&-size-changer":{display:"inline-block",width:"auto"},"&-quick-jumper":{display:"inline-block",height:e.controlHeight,marginInlineStart:e.marginXS,lineHeight:(0,T.unit)(e.controlHeight),verticalAlign:"top",input:Object.assign(Object.assign(Object.assign({},(0,P.genBasicInputStyle)(e)),(0,H.genBaseOutlinedStyle)(e,{borderColor:e.colorBorder,hoverBorderColor:e.colorPrimaryHover,activeBorderColor:e.colorPrimary,activeShadow:e.activeShadow})),{"&[disabled]":Object.assign({},(0,H.genDisabledStyle)(e)),width:e.quickJumperInputWidth,height:e.controlHeight,boxSizing:"border-box",margin:0,marginInlineStart:e.marginXS,marginInlineEnd:e.marginXS})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-simple`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSize,lineHeight:(0,T.unit)(e.itemSize),verticalAlign:"top",[`${t}-item-link`]:{height:e.itemSize,backgroundColor:"transparent",border:0,"&:hover":{backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive},"&::after":{height:e.itemSize,lineHeight:(0,T.unit)(e.itemSize)}}},[`${t}-simple-pager`]:{display:"inline-flex",alignItems:"center",height:e.itemSize,marginInlineEnd:e.marginXS,input:{boxSizing:"border-box",height:"100%",width:e.quickJumperInputWidth,padding:`0 ${(0,T.unit)(e.paginationItemPaddingInline)}`,textAlign:"center",backgroundColor:e.itemInputBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadius,outline:"none",transition:`border-color ${e.motionDurationMid}`,color:"inherit","&:hover":{borderColor:e.colorPrimary},"&:focus":{borderColor:e.colorPrimaryHover,boxShadow:`${(0,T.unit)(e.inputOutlineOffset)} 0 ${(0,T.unit)(e.controlOutlineWidth)} ${e.controlOutline}`},"&[disabled]":{color:e.colorTextDisabled,backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,cursor:"not-allowed"}}},[`&${t}-disabled`]:{[`${t}-prev, ${t}-next`]:{[`${t}-item-link`]:{"&:hover, &:active":{backgroundColor:"transparent"}}}},[`&${t}-mini`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM),[`${t}-item-link`]:{height:e.itemSizeSM,"&::after":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)}}},[`${t}-simple-pager`]:{height:e.itemSizeSM,input:{width:e.paginationMiniQuickJumperInputWidth}}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-mini ${t}-total-text, &${t}-mini ${t}-simple-pager`]:{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-item`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,T.unit)(e.calc(e.itemSizeSM).sub(2).equal())},[`&${t}-mini ${t}-prev, &${t}-mini ${t}-next`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini:not(${t}-disabled)`]:{[`${t}-prev, ${t}-next`]:{[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover ${t}-item-link`]:{backgroundColor:"transparent"}}},[` + &${t}-mini ${t}-prev ${t}-item-link, + &${t}-mini ${t}-next ${t}-item-link + `]:{backgroundColor:"transparent",borderColor:"transparent","&::after":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)}},[`&${t}-mini ${t}-jump-prev, &${t}-mini ${t}-jump-next`]:{height:e.itemSizeSM,marginInlineEnd:0,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-options`]:{marginInlineStart:e.paginationMiniOptionsMarginInlineStart,"&-size-changer":{top:e.miniOptionsSizeChangerTop},"&-quick-jumper":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM),input:Object.assign(Object.assign({},(0,P.genInputSmallStyle)(e)),{width:e.paginationMiniQuickJumperInputWidth,height:e.controlHeightSM})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-disabled`]:{"&, &:hover":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}},"&:focus-visible":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}}},[`&${t}-disabled`]:{cursor:"not-allowed",[`${t}-item`]:{cursor:"not-allowed",backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"},a:{color:e.colorTextDisabled,backgroundColor:"transparent",border:"none",cursor:"not-allowed"},"&-active":{borderColor:e.colorBorder,backgroundColor:e.itemActiveBgDisabled,"&:hover, &:active":{backgroundColor:e.itemActiveBgDisabled},a:{color:e.itemActiveColorDisabled}}},[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:hover, &:active":{backgroundColor:"transparent"},[`${t}-simple&`]:{backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"}}},[`${t}-simple-pager`]:{color:e.colorTextDisabled},[`${t}-jump-prev, ${t}-jump-next`]:{[`${t}-item-link-icon`]:{opacity:0},[`${t}-item-ellipsis`]:{opacity:1}}}}})(e)),{[`@media only screen and (max-width: ${e.screenLG}px)`]:{[`${t}-item`]:{"&-after-jump-prev, &-before-jump-next":{display:"none"}}},[`@media only screen and (max-width: ${e.screenSM}px)`]:{[`${t}-options`]:{display:"none"}}}),[`&${e.componentCls}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t}=e;return{[`${t}:not(${t}-disabled)`]:{[`${t}-item`]:Object.assign({},(0,A.genFocusStyle)(e)),[`${t}-jump-prev, ${t}-jump-next`]:{"&:focus-visible":Object.assign({[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}},(0,A.genFocusOutline)(e))},[`${t}-prev, ${t}-next`]:{[`&:focus-visible ${t}-item-link`]:(0,A.genFocusOutline)(e)}}}})(t)]},W),K=(0,R.genSubStyleComponent)(["Pagination","bordered"],e=>(e=>{let{componentCls:t}=e;return{[`${t}${t}-bordered${t}-disabled:not(${t}-mini)`]:{"&, &:hover":{[`${t}-item-link`]:{borderColor:e.colorBorder}},"&:focus-visible":{[`${t}-item-link`]:{borderColor:e.colorBorder}},[`${t}-item, ${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,[`&:hover:not(${t}-item-active)`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,a:{color:e.colorTextDisabled}},[`&${t}-item-active`]:{backgroundColor:e.itemActiveBgDisabled}},[`${t}-prev, ${t}-next`]:{"&:hover button":{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,color:e.colorTextDisabled},[`${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder}}},[`${t}${t}-bordered:not(${t}-mini)`]:{[`${t}-prev, ${t}-next`]:{"&:hover button":{borderColor:e.colorPrimaryHover,backgroundColor:e.itemBg},[`${t}-item-link`]:{backgroundColor:e.itemLinkBg,borderColor:e.colorBorder},[`&:hover ${t}-item-link`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,color:e.colorPrimary},[`&${t}-disabled`]:{[`${t}-item-link`]:{borderColor:e.colorBorder,color:e.colorTextDisabled}}},[`${t}-item`]:{backgroundColor:e.itemBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,[`&:hover:not(${t}-item-active)`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,a:{color:e.colorPrimary}},"&-active":{borderColor:e.colorPrimary}}}}})(L(e)),W);function X(e){return(0,t.useMemo)(()=>"boolean"==typeof e?[e,{}]:e&&"object"==typeof e?[!0,e]:[void 0,void 0],[e])}var U=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};e.s(["default",0,e=>{let{align:i,prefixCls:n,selectPrefixCls:o,className:l,rootClassName:d,style:m,size:g,locale:p,responsive:f,showSizeChanger:b,selectComponentClass:v,pageSizeOptions:h}=e,$=U(e,["align","prefixCls","selectPrefixCls","className","rootClassName","style","size","locale","responsive","showSizeChanger","selectComponentClass","pageSizeOptions"]),{xs:S}=(0,M.default)(f),[,C]=(0,I.useToken)(),{getPrefixCls:k,direction:y,showSizeChanger:x,className:z,style:T}=(0,j.useComponentConfig)("pagination"),P=k("pagination",n),[D,H,A]=q(P),R=(0,B.default)(g),_="small"===R||!!(S&&!R&&f),[W]=(0,O.useLocale)("Pagination",N.default),L=Object.assign(Object.assign({},W),p),[F,J]=X(b),[G,V]=X(x),Q=null!=J?J:V,Y=v||w.default,Z=t.useMemo(()=>h?h.map(e=>Number(e)):void 0,[h]),ee=t.useMemo(()=>{let e=t.createElement("span",{className:`${P}-item-ellipsis`},"•••"),i=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===y?t.createElement(u.default,null):t.createElement(c.default,null)),n=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===y?t.createElement(c.default,null):t.createElement(u.default,null));return{prevIcon:i,nextIcon:n,jumpPrevIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===y?t.createElement(r,{className:`${P}-item-link-icon`}):t.createElement(a,{className:`${P}-item-link-icon`}),e)),jumpNextIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===y?t.createElement(a,{className:`${P}-item-link-icon`}):t.createElement(r,{className:`${P}-item-link-icon`}),e))}},[y,P]),et=k("select",o),ei=(0,s.default)({[`${P}-${i}`]:!!i,[`${P}-mini`]:_,[`${P}-rtl`]:"rtl"===y,[`${P}-bordered`]:C.wireframe},z,l,d,H,A),en=Object.assign(Object.assign({},T),m);return D(t.createElement(t.Fragment,null,C.wireframe&&t.createElement(K,{prefixCls:P}),t.createElement(E,Object.assign({},ee,$,{style:en,prefixCls:P,selectPrefixCls:et,className:ei,locale:L,pageSizeOptions:Z,showSizeChanger:null!=F?F:G,sizeChangerRender:e=>{var i;let{disabled:n,size:o,onSizeChange:a,"aria-label":l,className:r,options:c}=e,{className:u,onChange:d}=Q||{},m=null==(i=c.find(e=>String(e.value)===String(o)))?void 0:i.value;return t.createElement(Y,Object.assign({disabled:n,showSearch:!0,popupMatchSelectWidth:!1,getPopupContainer:e=>e.parentNode,"aria-label":l,options:c},Q,{value:m,onChange:(e,t)=>{null==a||a(e),null==d||d(e,t)},size:_?"small":"middle",className:(0,s.default)(r,u)}))}}))))}],165370)},285027,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 720a48 48 0 1096 0 48 48 0 10-96 0zm16-304v184c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V416c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8zm475.7 440l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zm-783.5-27.9L512 239.9l339.8 588.2H172.2z"}}]},name:"warning",theme:"outlined"};var o=e.i(9583),a=i.forwardRef(function(e,a){return i.createElement(o.default,(0,t.default)({},e,{ref:a,icon:n}))});e.s(["WarningOutlined",0,a],285027)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/032wf1_8kb1mb.js b/litellm/proxy/_experimental/out/_next/static/chunks/032wf1_8kb1mb.js new file mode 100644 index 00000000000..d56acb17af6 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/032wf1_8kb1mb.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,40992,t=>{"use strict";t.s(["default",0,function(t,n){if(!t)throw Error("Invariant failed")}])},475058,t=>{"use strict";t.s(["default",0,function(t){return function(){return t}}])},216888,t=>{"use strict";let n=Math.PI,e=2*n,r=e-1e-6;function i(t){this._+=t[0];for(let n=1,e=t.length;n=0))throw Error(`invalid digits: ${t}`);if(n>15)return i;let e=10**n;return function(t){this._+=t[0];for(let n=1,r=t.length;n1e-6)if(Math.abs(f*s-l*c)>1e-6&&o){let g=r-u,d=i-a,p=s*s+l*l,y=Math.sqrt(p),x=Math.sqrt(h),v=o*Math.tan((n-Math.acos((p+h-(g*g+d*d))/(2*y*x)))/2),_=v/x,m=v/y;Math.abs(_-1)>1e-6&&this._append`L${t+_*c},${e+_*f}`,this._append`A${o},${o},0,0,${+(f*g>c*d)},${this._x1=t+m*s},${this._y1=e+m*l}`}else this._append`L${this._x1=t},${this._y1=e}`}arc(t,i,o,u,a,s){if(t*=1,i*=1,o*=1,s=!!s,o<0)throw Error(`negative radius: ${o}`);let l=o*Math.cos(u),c=o*Math.sin(u),f=t+l,h=i+c,g=1^s,d=s?u-a:a-u;null===this._x1?this._append`M${f},${h}`:(Math.abs(this._x1-f)>1e-6||Math.abs(this._y1-h)>1e-6)&&this._append`L${f},${h}`,o&&(d<0&&(d=d%e+e),d>r?this._append`A${o},${o},0,1,${g},${t-l},${i-c}A${o},${o},0,1,${g},${this._x1=f},${this._y1=h}`:d>1e-6&&this._append`A${o},${o},0,${+(d>=n)},${g},${this._x1=t+o*Math.cos(a)},${this._y1=i+o*Math.sin(a)}`)}rect(t,n,e,r){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${e*=1}v${+r}h${-e}Z`}toString(){return this._}}o.prototype,t.s(["withPath",0,function(t){let n=3;return t.digits=function(e){if(!arguments.length)return n;if(null==e)n=null;else{let t=Math.floor(e);if(!(t>=0))throw RangeError(`invalid digits: ${e}`);n=t}return t},()=>new o(n)}],216888)},464085,274035,486529,219517,315939,749502,841748,415333,t=>{"use strict";var n=t.i(475058),e=t.i(216888);let r=Math.cos,i=Math.sin,o=Math.sqrt,u=Math.PI,a=2*u;o(3);let s={draw(t,n){let e=o(n/u);t.moveTo(e,0),t.arc(0,0,e,0,a)}},l=o(1/3),c=2*l,f=i(u/10)/i(7*u/10),h=i(a/10)*f,g=-r(a/10)*f,d=o(3);o(3);let p=o(3)/2,y=1/o(12),x=(y/2+1)*3;t.s(["symbol",0,function(t,r){let i=null,o=(0,e.withPath)(u);function u(){let n;if(i||(i=n=o()),t.apply(this,arguments).draw(i,+r.apply(this,arguments)),n)return i=null,n+""||null}return t="function"==typeof t?t:(0,n.default)(t||s),r="function"==typeof r?r:(0,n.default)(void 0===r?64:+r),u.type=function(e){return arguments.length?(t="function"==typeof e?e:(0,n.default)(e),u):t},u.size=function(t){return arguments.length?(r="function"==typeof t?t:(0,n.default)(+t),u):r},u.context=function(t){return arguments.length?(i=null==t?null:t,u):i},u}],464085),t.s(["symbolCircle",0,s],274035),t.s(["symbolCross",0,{draw(t,n){let e=o(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}}],486529),t.s(["symbolDiamond",0,{draw(t,n){let e=o(n/c),r=e*l;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}}],219517),t.s(["symbolSquare",0,{draw(t,n){let e=o(n),r=-e/2;t.rect(r,r,e,e)}}],315939),t.s(["symbolStar",0,{draw(t,n){let e=o(.8908130915292852*n),u=h*e,s=g*e;t.moveTo(0,-e),t.lineTo(u,s);for(let n=1;n<5;++n){let o=a*n/5,l=r(o),c=i(o);t.lineTo(c*e,-l*e),t.lineTo(l*u-c*s,c*u+l*s)}t.closePath()}}],749502),t.s(["symbolTriangle",0,{draw(t,n){let e=-o(n/(3*d));t.moveTo(0,2*e),t.lineTo(-d*e,-e),t.lineTo(d*e,-e),t.closePath()}}],841748),t.s(["symbolWye",0,{draw(t,n){let e=o(n/x),r=e/2,i=e*y,u=e*y+e,a=-r;t.moveTo(r,i),t.lineTo(r,u),t.lineTo(a,u),t.lineTo(-.5*r-p*i,p*r+-.5*i),t.lineTo(-.5*r-p*u,p*r+-.5*u),t.lineTo(-.5*a-p*u,p*a+-.5*u),t.lineTo(-.5*r+p*i,-.5*i-p*r),t.lineTo(-.5*r+p*u,-.5*u-p*r),t.lineTo(-.5*a+p*u,-.5*u-p*a),t.closePath()}}],415333)},182984,365332,65232,t=>{"use strict";function n(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}t.s(["initInterpolator",0,function(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this},"initRange",0,n],365332);class e extends Map{constructor(t,n=i){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(r(this,t))}has(t){return super.has(r(this,t))}set(t,n){return super.set(function({_intern:t,_key:n},e){let r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}(this,t),n)}delete(t){return super.delete(function({_intern:t,_key:n},e){let r=n(e);return t.has(r)&&(e=t.get(r),t.delete(r)),e}(this,t))}}function r({_intern:t,_key:n},e){let i=n(e);return t.has(i)?t.get(i):e}function i(t){return null!==t&&"object"==typeof t?t.valueOf():t}let o=Symbol("implicit");function u(){var t=new e,r=[],i=[],a=o;function s(n){let e=t.get(n);if(void 0===e){if(a!==o)return a;t.set(n,e=r.push(n)-1)}return i[e%i.length]}return s.domain=function(n){if(!arguments.length)return r.slice();for(let i of(r=[],t=new e,n))t.has(i)||t.set(i,r.push(i)-1);return s},s.range=function(t){return arguments.length?(i=Array.from(t),s):i.slice()},s.unknown=function(t){return arguments.length?(a=t,s):a},s.copy=function(){return u(r,i).unknown(a)},n.apply(s,arguments),s}function a(){var t,e,r=u().unknown(void 0),i=r.domain,o=r.range,s=0,l=1,c=!1,f=0,h=0,g=.5;function d(){var n=i().length,r=l{"use strict";t.s([])},429061,t=>{"use strict";t.i(267155);var n,e,r,i,o,u,a,s=t.i(182984);let l=Math.sqrt(50),c=Math.sqrt(10),f=Math.sqrt(2);function h(t,n,e){let r,i,o,u=(n-t)/Math.max(0,e),a=Math.floor(Math.log10(u)),s=u/Math.pow(10,a),g=s>=l?10:s>=c?5:s>=f?2:1;return(a<0?(r=Math.round(t*(o=Math.pow(10,-a)/g)),i=Math.round(n*o),r/on&&--i,o=-o):(r=Math.round(t/(o=Math.pow(10,a)*g)),i=Math.round(n/o),r*on&&--i),i0))return[];if(t===n)return[t];let r=n=i))return[];let a=o-i+1,s=Array(a);if(r)if(u<0)for(let t=0;tn?1:t>=n?0:NaN}function x(t,n){return null==t||null==n?NaN:nt?1:n>=t?0:NaN}function v(t){let n,e,r;function i(t,r,o=0,u=t.length){if(o>>1;0>e(t[n],r)?o=n+1:u=n}while(oy(t(n),e),r=(n,e)=>t(n)-e):(n=t===y||t===x?t:_,e=t,r=t),{left:i,center:function(t,n,e=0,o=t.length){let u=i(t,n,e,o-1);return u>e&&r(t[u-1],n)>-r(t[u],n)?u-1:u},right:function(t,r,i=0,o=t.length){if(i>>1;0>=e(t[n],r)?i=n+1:o=n}while(i>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?R(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?R(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=U.exec(t))?new j(n[1],n[2],n[3],1):(n=E.exec(t))?new j(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=S.exec(t))?R(n[1],n[2],n[3],n[4]):(n=A.exec(t))?R(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=F.exec(t))?X(n[1],n[2]/100,n[3]/100,1):(n=O.exec(t))?X(n[1],n[2]/100,n[3]/100,n[4]):q.hasOwnProperty(t)?Y(q[t]):"transparent"===t?new j(NaN,NaN,NaN,0):null}function Y(t){return new j(t>>16&255,t>>8&255,255&t,1)}function R(t,n,e,r){return r<=0&&(t=n=e=NaN),new j(t,n,e,r)}function I(t,n,e,r){var i;return 1==arguments.length?((i=t)instanceof N||(i=H(i)),i)?new j((i=i.rgb()).r,i.g,i.b,i.opacity):new j:new j(t,n,e,null==r?1:r)}function j(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function z(){return`#${V(this.r)}${V(this.g)}${V(this.b)}`}function Z(){let t=B(this.opacity);return`${1===t?"rgb(":"rgba("}${W(this.r)}, ${W(this.g)}, ${W(this.b)}${1===t?")":`, ${t})`}`}function B(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function W(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function V(t){return((t=W(t))<16?"0":"")+t.toString(16)}function X(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new J(t,n,e,r)}function Q(t){if(t instanceof J)return new J(t.h,t.s,t.l,t.opacity);if(t instanceof N||(t=H(t)),!t)return new J;if(t instanceof J)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,s=(o+i)/2;return a?(u=n===o?(e-r)/a+(e0&&s<1?0:u,new J(u,a,s,t.opacity)}function J(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function G(t){return(t=(t||0)%360)<0?t+360:t}function K(t){return Math.max(0,Math.min(1,t||0))}function tt(t,n,e){return(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)*255}function tn(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}b(N,H,{copy(t){return Object.assign(new this.constructor,this,t)},displayable(){return this.rgb().displayable()},hex:P,formatHex:P,formatHex8:function(){return this.rgb().formatHex8()},formatHsl:function(){return Q(this).formatHsl()},formatRgb:L,toString:L}),b(j,I,T(N,{brighter(t){return t=null==t?1.4285714285714286:Math.pow(1.4285714285714286,t),new j(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=null==t?.7:Math.pow(.7,t),new j(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new j(W(this.r),W(this.g),W(this.b),B(this.opacity))},displayable(){return -.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:z,formatHex:z,formatHex8:function(){return`#${V(this.r)}${V(this.g)}${V(this.b)}${V((isNaN(this.opacity)?1:this.opacity)*255)}`},formatRgb:Z,toString:Z})),b(J,function(t,n,e,r){return 1==arguments.length?Q(t):new J(t,n,e,null==r?1:r)},T(N,{brighter(t){return t=null==t?1.4285714285714286:Math.pow(1.4285714285714286,t),new J(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=null==t?.7:Math.pow(.7,t),new J(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new j(tt(t>=240?t-240:t+120,i,r),tt(t,i,r),tt(t<120?t+240:t-120,i,r),this.opacity)},clamp(){return new J(G(this.h),K(this.s),K(this.l),B(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){let t=B(this.opacity);return`${1===t?"hsl(":"hsla("}${G(this.h)}, ${100*K(this.s)}%, ${100*K(this.l)}%${1===t?")":`, ${t})`}`}}));let te=t=>()=>t;function tr(t,n){var e=n-t;return e?function(n){return t+n*e}:te(isNaN(t)?n:t)}let ti=function t(n){var e,r=1==(e=+n)?tr:function(t,n){var r,i,o;return n-t?(r=t,i=n,r=Math.pow(r,o=e),i=Math.pow(i,o)-r,o=1/o,function(t){return Math.pow(r+t*i,o)}):te(isNaN(t)?n:t)};function i(t,n){var e=r((t=I(t)).r,(n=I(n)).r),i=r(t.g,n.g),o=r(t.b,n.b),u=tr(t.opacity,n.opacity);return function(n){return t.r=e(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}return i.gamma=t,i}(1);function to(t){return function(n){var e,r,i=n.length,o=Array(i),u=Array(i),a=Array(i);for(e=0;e=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=ra&&(u=n.slice(a,u),l[s]?l[s]+=u:l[++s]=u),(i=i[0])===(o=o[0])?l[s]?l[s]+=o:l[++s]=o:(l[++s]=null,c.push({i:s,x:tu(i,o)})),a=ts.lastIndex;return an&&(e=t,t=n,n=e),l=function(e){return Math.max(t,Math.min(n,e))}),r=s>2?ty:tp,i=o=null,f}function f(n){return null==n||isNaN(n*=1)?e:(i||(i=r(u.map(t),a,s)))(t(l(n)))}return f.invert=function(e){return l(n((o||(o=r(a,u.map(t),tu)))(e)))},f.domain=function(t){return arguments.length?(u=Array.from(t,tf),c()):u.slice()},f.range=function(t){return arguments.length?(a=Array.from(t),c()):a.slice()},f.rangeRound=function(t){return a=Array.from(t),s=tc,c()},f.clamp=function(t){return arguments.length?(l=!!t||tg,c()):l!==tg},f.interpolate=function(t){return arguments.length?(s=t,c()):s},f.unknown=function(t){return arguments.length?(e=t,f):e},function(e,r){return t=e,n=r,c()}}function t_(){return tv()(tg,tg)}var tm=t.i(365332);function tM(t,n){if(!isFinite(t)||0===t)return null;var e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf("e"),r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function tw(t){return(t=tM(Math.abs(t)))?t[1]:NaN}var tb=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function tT(t){var n;if(!(n=tb.exec(t)))throw Error("invalid format: "+t);return new tN({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function tN(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function tk(t,n){var e=tM(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+Array(i-r.length+2).join("0")}tT.prototype=tN.prototype,tN.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};let t$={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>tk(100*t,n),r:tk,s:function(t,e){var r=tM(t,e);if(!r)return n=void 0,t.toPrecision(e);var i=r[0],o=r[1],u=o-(n=3*Math.max(-8,Math.min(8,Math.floor(o/3))))+1,a=i.length;return u===a?i:u>a?i+Array(u-a+1).join("0"):u>0?i.slice(0,u)+"."+i.slice(u):"0."+Array(1-u).join("0")+tM(t,Math.max(0,e+u-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function tC(t){return t}var tD=Array.prototype.map,tU=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function tE(t,n,e,o){var u,a,s=p(t,n,e);switch((o=tT(null==o?",f":o)).type){case"s":var l=Math.max(Math.abs(t),Math.abs(n));return null!=o.precision||isNaN(a=Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(tw(l)/3)))-tw(Math.abs(s))))||(o.precision=a),i(o,l);case"":case"e":case"g":case"p":case"r":null!=o.precision||isNaN(a=Math.max(0,tw(Math.abs(Math.max(Math.abs(t),Math.abs(n)))-(u=Math.abs(u=s)))-tw(u))+1)||(o.precision=a-("e"===o.type));break;case"f":case"%":null!=o.precision||isNaN(a=Math.max(0,-tw(Math.abs(s))))||(o.precision=a-("%"===o.type)*2)}return r(o)}function tS(t){var n=t.domain;return t.ticks=function(t){var e=n();return g(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return tE(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),u=0,a=o.length-1,s=o[u],l=o[a],c=10;for(l0;){if((i=d(s,l,e))===r)return o[u]=s,o[a]=l,n(o);if(i>0)s=Math.floor(s/i)*i,l=Math.ceil(l/i)*i;else if(i<0)s=Math.ceil(s*i)/i,l=Math.floor(l*i)/i;else break;r=i}return t},t}function tA(t,n){t=t.slice();var e,r=0,i=t.length-1,o=t[r],u=t[i];return u-t(-n,e)}function tY(t){let n,e,i=t(tF,tO),o=i.domain,u=10;function a(){var r,a;return n=(r=u)===Math.E?Math.log:10===r&&Math.log10||2===r&&Math.log2||(r=Math.log(r),t=>Math.log(t)/r),e=10===(a=u)?tL:a===Math.E?Math.exp:t=>Math.pow(a,t),o()[0]<0?(n=tH(n),e=tH(e),t(tq,tP)):t(tF,tO),i}return i.base=function(t){return arguments.length?(u=+t,a()):u},i.domain=function(t){return arguments.length?(o(t),a()):o()},i.ticks=t=>{let r,i,a=o(),s=a[0],l=a[a.length-1],c=l0){for(;f<=h;++f)for(r=1;rl)break;p.push(i)}}else for(;f<=h;++f)for(r=u-1;r>=1;--r)if(!((i=f>0?r/e(-f):r*e(f))l)break;p.push(i)}2*p.length{if(null==t&&(t=10),null==o&&(o=10===u?"s":","),"function"!=typeof o&&(u%1||null!=(o=tT(o)).precision||(o.trim=!0),o=r(o)),t===1/0)return o;let a=Math.max(1,u*t/i.ticks().length);return t=>{let r=t/e(Math.round(n(t)));return r*uo(tA(o(),{floor:t=>e(Math.floor(n(t))),ceil:t=>e(Math.ceil(n(t)))})),i}function tR(t){return function(n){return Math.sign(n)*Math.log1p(Math.abs(n/t))}}function tI(t){return function(n){return Math.sign(n)*Math.expm1(Math.abs(n))*t}}function tj(t){var n=1,e=t(tR(1),tI(n));return e.constant=function(e){return arguments.length?t(tR(n=+e),tI(n)):n},tS(e)}r=(e=function(t){var e,r,i,o=void 0===t.grouping||void 0===t.thousands?tC:(e=tD.call(t.grouping,Number),r=t.thousands+"",function(t,n){for(var i=t.length,o=[],u=0,a=e[0],s=0;i>0&&a>0&&(s+a+1>n&&(a=Math.max(1,n-s)),o.push(t.substring(i-=a,i+a)),!((s+=a+1)>n));)a=e[u=(u+1)%e.length];return o.reverse().join(r)}),u=void 0===t.currency?"":t.currency[0]+"",a=void 0===t.currency?"":t.currency[1]+"",s=void 0===t.decimal?".":t.decimal+"",l=void 0===t.numerals?tC:(i=tD.call(t.numerals,String),function(t){return t.replace(/[0-9]/g,function(t){return i[+t]})}),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",h=void 0===t.nan?"NaN":t.nan+"";function g(t,e){var r=(t=tT(t)).fill,i=t.align,g=t.sign,d=t.symbol,p=t.zero,y=t.width,x=t.comma,v=t.precision,_=t.trim,m=t.type;"n"===m?(x=!0,m="g"):t$[m]||(void 0===v&&(v=12),_=!0,m="g"),(p||"0"===r&&"="===i)&&(p=!0,r="0",i="=");var M=(e&&void 0!==e.prefix?e.prefix:"")+("$"===d?u:"#"===d&&/[boxX]/.test(m)?"0"+m.toLowerCase():""),w=("$"===d?a:/[%p]/.test(m)?c:"")+(e&&void 0!==e.suffix?e.suffix:""),b=t$[m],T=/[defgprs%]/.test(m);function N(t){var e,u,a,c=M,d=w;if("c"===m)d=b(t)+d,t="";else{var N=(t*=1)<0||1/t<0;if(t=isNaN(t)?h:b(Math.abs(t),v),_&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),N&&0==+t&&"+"!==g&&(N=!1),c=(N?"("===g?g:f:"-"===g||"("===g?"":g)+c,d=("s"!==m||isNaN(t)||void 0===n?"":tU[8+n/3])+d+(N&&"("===g?")":""),T){for(e=-1,u=t.length;++e(a=t.charCodeAt(e))||a>57){d=(46===a?s+t.slice(e+1):t.slice(e))+d,t=t.slice(0,e);break}}}x&&!p&&(t=o(t,1/0));var k=c.length+t.length+d.length,$=k>1)+c+t+d+$.slice(k);break;default:t=$+c+t+d}return l(t)}return v=void 0===v?6:/[gprs]/.test(m)?Math.max(1,Math.min(21,v)):Math.max(0,Math.min(20,v)),N.toString=function(){return t+""},N}return{format:g,formatPrefix:function(t,n){var e=3*Math.max(-8,Math.min(8,Math.floor(tw(n)/3))),r=Math.pow(10,-e),i=g(((t=tT(t)).type="f",t),{suffix:tU[8+e/3]});return function(t){return i(r*t)}}}}({thousands:",",grouping:[3],currency:["$",""]})).format,i=e.formatPrefix;var tz=t.i(65232);function tZ(t){return function(n){return n<0?-Math.pow(-n,t):Math.pow(n,t)}}function tB(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function tW(t){return t<0?-t*t:t*t}function tV(t){var n=t(tg,tg),e=1;return n.exponent=function(n){return arguments.length?1==(e=+n)?t(tg,tg):.5===e?t(tB,tW):t(tZ(e),tZ(1/e)):e},tS(n)}function tX(){var t=tV(tv());return t.copy=function(){return tx(t,tX()).exponent(t.exponent())},tm.initRange.apply(t,arguments),t}function tQ(t){return Math.sign(t)*t*t}function tJ(t,n){let e;if(void 0===n)for(let n of t)null!=n&&(e=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function tG(t,n){let e;if(void 0===n)for(let n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function tK(t,n){return(null==t||!(t>=t))-(null==n||!(n>=n))||(tn))}function t0(t,n,e){let r=t[n];t[n]=t[e],t[e]=r}let t1=new Date,t2=new Date;function t5(t,n,e,r){function i(n){return t(n=0==arguments.length?new Date:new Date(+n)),n}return i.floor=n=>(t(n=new Date(+n)),n),i.ceil=e=>(t(e=new Date(e-1)),n(e,1),t(e),e),i.round=t=>{let n=i(t),e=i.ceil(t);return t-n(n(t=new Date(+t),null==e?1:Math.floor(e)),t),i.range=(e,r,o)=>{let u,a=[];if(e=i.ceil(e),o=null==o?1:Math.floor(o),!(e0))return a;do a.push(u=new Date(+e)),n(e,o),t(e);while(ut5(n=>{if(n>=n)for(;t(n),!e(n);)n.setTime(n-1)},(t,r)=>{if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););}),e&&(i.count=(n,r)=>(t1.setTime(+n),t2.setTime(+r),t(t1),t(t2),Math.floor(e(t1,t2))),i.every=t=>isFinite(t=Math.floor(t))&&t>0?t>1?i.filter(r?n=>r(n)%t==0:n=>i.count(0,n)%t==0):i:null),i}let t3=t5(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,n)=>{t.setFullYear(t.getFullYear()+n)},(t,n)=>n.getFullYear()-t.getFullYear(),t=>t.getFullYear());t3.every=t=>isFinite(t=Math.floor(t))&&t>0?t5(n=>{n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},(n,e)=>{n.setFullYear(n.getFullYear()+e*t)}):null,t3.range;let t4=t5(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCFullYear(t.getUTCFullYear()+n)},(t,n)=>n.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());t4.every=t=>isFinite(t=Math.floor(t))&&t>0?t5(n=>{n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},(n,e)=>{n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null,t4.range;let t8=t5(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,n)=>{t.setMonth(t.getMonth()+n)},(t,n)=>n.getMonth()-t.getMonth()+(n.getFullYear()-t.getFullYear())*12,t=>t.getMonth());t8.range;let t6=t5(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCMonth(t.getUTCMonth()+n)},(t,n)=>n.getUTCMonth()-t.getUTCMonth()+(n.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth());t6.range;function t7(t){return t5(n=>{n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},(t,n)=>{t.setDate(t.getDate()+7*n)},(t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/6048e5)}let t9=t7(0),nt=t7(1),nn=t7(2),ne=t7(3),nr=t7(4),ni=t7(5),no=t7(6);function nu(t){return t5(n=>{n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCDate(t.getUTCDate()+7*n)},(t,n)=>(n-t)/6048e5)}t9.range,nt.range,nn.range,ne.range,nr.range,ni.range,no.range;let na=nu(0),ns=nu(1),nl=nu(2),nc=nu(3),nf=nu(4),nh=nu(5),ng=nu(6);na.range,ns.range,nl.range,nc.range,nf.range,nh.range,ng.range;let nd=t5(t=>t.setHours(0,0,0,0),(t,n)=>t.setDate(t.getDate()+n),(t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5,t=>t.getDate()-1);nd.range;let np=t5(t=>{t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCDate(t.getUTCDate()+n)},(t,n)=>(n-t)/864e5,t=>t.getUTCDate()-1);np.range;let ny=t5(t=>{t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCDate(t.getUTCDate()+n)},(t,n)=>(n-t)/864e5,t=>Math.floor(t/864e5));ny.range;let nx=t5(t=>{t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds()-6e4*t.getMinutes())},(t,n)=>{t.setTime(+t+36e5*n)},(t,n)=>(n-t)/36e5,t=>t.getHours());nx.range;let nv=t5(t=>{t.setUTCMinutes(0,0,0)},(t,n)=>{t.setTime(+t+36e5*n)},(t,n)=>(n-t)/36e5,t=>t.getUTCHours());nv.range;let n_=t5(t=>{t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds())},(t,n)=>{t.setTime(+t+6e4*n)},(t,n)=>(n-t)/6e4,t=>t.getMinutes());n_.range;let nm=t5(t=>{t.setUTCSeconds(0,0)},(t,n)=>{t.setTime(+t+6e4*n)},(t,n)=>(n-t)/6e4,t=>t.getUTCMinutes());nm.range;let nM=t5(t=>{t.setTime(t-t.getMilliseconds())},(t,n)=>{t.setTime(+t+1e3*n)},(t,n)=>(n-t)/1e3,t=>t.getUTCSeconds());nM.range;let nw=t5(()=>{},(t,n)=>{t.setTime(+t+n)},(t,n)=>n-t);function nb(t,n,e,r,i,o){let u=[[nM,1,1e3],[nM,5,5e3],[nM,15,15e3],[nM,30,3e4],[o,1,6e4],[o,5,3e5],[o,15,9e5],[o,30,18e5],[i,1,36e5],[i,3,108e5],[i,6,216e5],[i,12,432e5],[r,1,864e5],[r,2,1728e5],[e,1,6048e5],[n,1,2592e6],[n,3,7776e6],[t,1,31536e6]];function a(n,e,r){let i=Math.abs(e-n)/r,o=v(([,,t])=>t).right(u,i);if(o===u.length)return t.every(p(n/31536e6,e/31536e6,r));if(0===o)return nw.every(Math.max(p(n,e,r),1));let[a,s]=u[i/u[o-1][2]isFinite(t=Math.floor(t))&&t>0?t>1?t5(n=>{n.setTime(Math.floor(n/t)*t)},(n,e)=>{n.setTime(+n+e*t)},(n,e)=>(e-n)/t):nw:null,nw.range;let[nT,nN]=nb(t4,t6,na,ny,nv,nm),[nk,n$]=nb(t3,t8,t9,nd,nx,n_);function nC(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function nD(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function nU(t,n,e){return{y:t,m:n,d:e,H:0,M:0,S:0,L:0}}var nE={"-":"",_:" ",0:"0"},nS=/^\s*\d+/,nA=/^%/,nF=/[\\^$*+?|[\]().{}]/g;function nO(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o[t.toLowerCase(),n]))}function nH(t,n,e){var r=nS.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function nY(t,n,e){var r=nS.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function nR(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function nI(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function nj(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function nz(t,n,e){var r=nS.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function nZ(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function nB(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function nW(t,n,e){var r=nS.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function nV(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function nX(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function nQ(t,n,e){var r=nS.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function nJ(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function nG(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function nK(t,n,e){var r=nS.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function n0(t,n,e){var r=nS.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function n1(t,n,e){var r=nS.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function n2(t,n,e){var r=nA.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function n5(t,n,e){var r=nS.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function n3(t,n,e){var r=nS.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function n4(t,n){return nO(t.getDate(),n,2)}function n8(t,n){return nO(t.getHours(),n,2)}function n6(t,n){return nO(t.getHours()%12||12,n,2)}function n7(t,n){return nO(1+nd.count(t3(t),t),n,3)}function n9(t,n){return nO(t.getMilliseconds(),n,3)}function et(t,n){return n9(t,n)+"000"}function en(t,n){return nO(t.getMonth()+1,n,2)}function ee(t,n){return nO(t.getMinutes(),n,2)}function er(t,n){return nO(t.getSeconds(),n,2)}function ei(t){var n=t.getDay();return 0===n?7:n}function eo(t,n){return nO(t9.count(t3(t)-1,t),n,2)}function eu(t){var n=t.getDay();return n>=4||0===n?nr(t):nr.ceil(t)}function ea(t,n){return t=eu(t),nO(nr.count(t3(t),t)+(4===t3(t).getDay()),n,2)}function es(t){return t.getDay()}function el(t,n){return nO(nt.count(t3(t)-1,t),n,2)}function ec(t,n){return nO(t.getFullYear()%100,n,2)}function ef(t,n){return nO((t=eu(t)).getFullYear()%100,n,2)}function eh(t,n){return nO(t.getFullYear()%1e4,n,4)}function eg(t,n){var e=t.getDay();return nO((t=e>=4||0===e?nr(t):nr.ceil(t)).getFullYear()%1e4,n,4)}function ed(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+nO(n/60|0,"0",2)+nO(n%60,"0",2)}function ep(t,n){return nO(t.getUTCDate(),n,2)}function ey(t,n){return nO(t.getUTCHours(),n,2)}function ex(t,n){return nO(t.getUTCHours()%12||12,n,2)}function ev(t,n){return nO(1+np.count(t4(t),t),n,3)}function e_(t,n){return nO(t.getUTCMilliseconds(),n,3)}function em(t,n){return e_(t,n)+"000"}function eM(t,n){return nO(t.getUTCMonth()+1,n,2)}function ew(t,n){return nO(t.getUTCMinutes(),n,2)}function eb(t,n){return nO(t.getUTCSeconds(),n,2)}function eT(t){var n=t.getUTCDay();return 0===n?7:n}function eN(t,n){return nO(na.count(t4(t)-1,t),n,2)}function ek(t){var n=t.getUTCDay();return n>=4||0===n?nf(t):nf.ceil(t)}function e$(t,n){return t=ek(t),nO(nf.count(t4(t),t)+(4===t4(t).getUTCDay()),n,2)}function eC(t){return t.getUTCDay()}function eD(t,n){return nO(ns.count(t4(t)-1,t),n,2)}function eU(t,n){return nO(t.getUTCFullYear()%100,n,2)}function eE(t,n){return nO((t=ek(t)).getUTCFullYear()%100,n,2)}function eS(t,n){return nO(t.getUTCFullYear()%1e4,n,4)}function eA(t,n){var e=t.getUTCDay();return nO((t=e>=4||0===e?nf(t):nf.ceil(t)).getUTCFullYear()%1e4,n,4)}function eF(){return"+0000"}function eO(){return"%"}function eq(t){return+t}function eP(t){return Math.floor(t/1e3)}function eL(t){return new Date(t)}function eH(t){return t instanceof Date?+t:+new Date(+t)}function eY(t,n,e,r,i,o,u,a,s,l){var c=t_(),f=c.invert,h=c.domain,g=l(".%L"),d=l(":%S"),p=l("%I:%M"),y=l("%I %p"),x=l("%a %d"),v=l("%b %d"),_=l("%B"),m=l("%Y");function M(t){return(s(t)=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:eq,s:eP,S:er,u:ei,U:eo,V:ea,w:es,W:el,x:null,X:null,y:ec,Y:eh,Z:ed,"%":eO},m={a:function(t){return u[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return s[t.getUTCMonth()]},B:function(t){return a[t.getUTCMonth()]},c:null,d:ep,e:ep,f:em,g:eE,G:eA,H:ey,I:ex,j:ev,L:e_,m:eM,M:ew,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:eq,s:eP,S:eb,u:eT,U:eN,V:e$,w:eC,W:eD,x:null,X:null,y:eU,Y:eS,Z:eF,"%":eO},M={a:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.w=d.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=x.exec(n.slice(e));return r?(t.m=v.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=p.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return T(t,n,e,r)},d:nX,e:nX,f:n1,g:nZ,G:nz,H:nJ,I:nJ,j:nQ,L:n0,m:nV,M:nG,p:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.p=c.get(r[0].toLowerCase()),e+r[0].length):-1},q:nW,Q:n5,s:n3,S:nK,u:nY,U:nR,V:nI,w:nH,W:nj,x:function(t,n,r){return T(t,e,n,r)},X:function(t,n,e){return T(t,r,n,e)},y:nZ,Y:nz,Z:nB,"%":n2};function w(t,n){return function(e){var r,i,o,u=[],a=-1,s=0,l=t.length;for(e instanceof Date||(e=new Date(+e));++a53)return null;"w"in o||(o.w=1),"Z"in o?(r=(i=(r=nD(nU(o.y,0,1))).getUTCDay())>4||0===i?ns.ceil(r):ns(r),r=np.offset(r,(o.V-1)*7),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(r=(i=(r=nC(nU(o.y,0,1))).getDay())>4||0===i?nt.ceil(r):nt(r),r=nd.offset(r,(o.V-1)*7),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:+("W"in o)),i="Z"in o?nD(nU(o.y,0,1)).getUTCDay():nC(nU(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,nD(o)):nC(o)}}function T(t,n,e,r){for(var i,o,u=0,a=n.length,s=e.length;u=s)return -1;if(37===(i=n.charCodeAt(u++))){if(!(o=M[(i=n.charAt(u++))in nE?n.charAt(u++):i])||(r=o(t,e,r))<0)return -1}else if(i!=e.charCodeAt(r++))return -1}return r}return _.x=w(e,_),_.X=w(r,_),_.c=w(n,_),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",_);return n.toString=function(){return t},n},parse:function(t){var n=b(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=b(t+="",!0);return n.toString=function(){return t},n}}}({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]})).format,o.parse,a=o.utcFormat,o.utcParse,t.s(["scaleBand",()=>s.default,"scaleDiverging",0,function t(){var n=tS(ez()(tg));return n.copy=function(){return eI(n,t())},tm.initInterpolator.apply(n,arguments)},"scaleDivergingLog",0,function t(){var n=tY(ez()).domain([.1,1,10]);return n.copy=function(){return eI(n,t()).base(n.base())},tm.initInterpolator.apply(n,arguments)},"scaleDivergingPow",0,eZ,"scaleDivergingSqrt",0,function(){return eZ.apply(null,arguments).exponent(.5)},"scaleDivergingSymlog",0,function t(){var n=tj(ez());return n.copy=function(){return eI(n,t()).constant(n.constant())},tm.initInterpolator.apply(n,arguments)},"scaleIdentity",0,function t(n){var e;function r(t){return null==t||isNaN(t*=1)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,tf),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,tf):[0,1],tS(r)},"scaleImplicit",()=>tz.implicit,"scaleLinear",0,function t(){var n=t_();return n.copy=function(){return tx(n,t())},tm.initRange.apply(n,arguments),tS(n)},"scaleLog",0,function t(){let n=tY(tv()).domain([1,10]);return n.copy=()=>tx(n,t()).base(n.base()),tm.initRange.apply(n,arguments),n},"scaleOrdinal",()=>tz.default,"scalePoint",()=>s.point,"scalePow",0,tX,"scaleQuantile",0,function t(){var n,e=[],r=[],i=[];function o(){var t=0,n=Math.max(1,r.length);for(i=Array(n-1);++t=1)return+e(t[r-1],r-1,t);var r,i=(r-1)*n,o=Math.floor(i),u=+e(t[o],o,t);return u+(e(t[o+1],o+1,t)-u)*(i-o)}}(e,t/n);return u}function u(t){return null==t||isNaN(t*=1)?n:r[w(i,t)]}return u.invertExtent=function(t){var n=r.indexOf(t);return n<0?[NaN,NaN]:[n>0?i[n-1]:e[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},a.unknown=function(t){return arguments.length&&(n=t),a},a.thresholds=function(){return o.slice()},a.copy=function(){return t().domain([e,r]).range(u).unknown(n)},tm.initRange.apply(tS(a),arguments)},"scaleRadial",0,function t(){var n,e=t_(),r=[0,1],i=!1;function o(t){var r,o=Math.sign(r=e(t))*Math.sqrt(Math.abs(r));return isNaN(o)?n:i?Math.round(o):o}return o.invert=function(t){return e.invert(tQ(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,tf)).map(tQ)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},tm.initRange.apply(o,arguments),tS(o)},"scaleSequential",0,function t(){var n=tS(eR()(tg));return n.copy=function(){return eI(n,t())},tm.initInterpolator.apply(n,arguments)},"scaleSequentialLog",0,function t(){var n=tY(eR()).domain([1,10]);return n.copy=function(){return eI(n,t()).base(n.base())},tm.initInterpolator.apply(n,arguments)},"scaleSequentialPow",0,ej,"scaleSequentialQuantile",0,function t(){var n=[],e=tg;function r(t){if(null!=t&&!isNaN(t*=1))return e((w(n,t,1)-1)/(n.length-1))}return r.domain=function(t){if(!arguments.length)return n.slice();for(let e of(n=[],t))null==e||isNaN(e*=1)||n.push(e);return n.sort(y),r},r.interpolator=function(t){return arguments.length?(e=t,r):e},r.range=function(){return n.map((t,r)=>e(r/(n.length-1)))},r.quantiles=function(t){return Array.from({length:t+1},(e,r)=>(function(t,n){if(!(!(e=(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n*=1)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r*=1)>=r&&(yield r)}}(t,void 0))).length)||isNaN(n*=1))){if(n<=0||e<2)return tG(t);if(n>=1)return tJ(t);var e,r=(e-1)*n,i=Math.floor(r),o=tJ((function t(n,e,r=0,i=1/0,o){if(e=Math.floor(e),r=Math.floor(Math.max(0,r)),i=Math.floor(Math.min(n.length-1,i)),!(r<=e&&e<=i))return n;for(o=void 0===o?tK:function(t=y){if(t===y)return tK;if("function"!=typeof t)throw TypeError("compare is not a function");return(n,e)=>{let r=t(n,e);return r||0===r?r:(0===t(e,e))-(0===t(n,n))}}(o);i>r;){if(i-r>600){let u=i-r+1,a=e-r+1,s=Math.log(u),l=.5*Math.exp(2*s/3),c=.5*Math.sqrt(s*l*(u-l)/u)*(a-u/2<0?-1:1),f=Math.max(r,Math.floor(e-a*l/u+c)),h=Math.min(i,Math.floor(e+(u-a)*l/u+c));t(n,e,f,h,o)}let u=n[e],a=r,s=i;for(t0(n,r,e),o(n[i],u)>0&&t0(n,r,i);ao(n[a],u);)++a;for(;o(n[s],u)>0;)--s}0===o(n[r],u)?t0(n,r,s):t0(n,++s,i),s<=e&&(r=s+1),e<=s&&(i=s-1)}return n})(t,i).subarray(0,i+1));return o+(tG(t.subarray(i+1))-o)*(r-i)}})(n,r/t))},r.copy=function(){return t(e).domain(n)},tm.initInterpolator.apply(r,arguments)},"scaleSequentialSqrt",0,function(){return ej.apply(null,arguments).exponent(.5)},"scaleSequentialSymlog",0,function t(){var n=tj(eR());return n.copy=function(){return eI(n,t()).constant(n.constant())},tm.initInterpolator.apply(n,arguments)},"scaleSqrt",0,function(){return tX.apply(null,arguments).exponent(.5)},"scaleSymlog",0,function t(){var n=tj(tv());return n.copy=function(){return tx(n,t()).constant(n.constant())},tm.initRange.apply(n,arguments)},"scaleThreshold",0,function t(){var n,e=[.5],r=[0,1],i=1;function o(t){return null!=t&&t<=t?r[w(e,t,0,i)]:n}return o.domain=function(t){return arguments.length?(i=Math.min((e=Array.from(t)).length,r.length-1),o):e.slice()},o.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),o):r.slice()},o.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t().domain(e).range(r).unknown(n)},tm.initRange.apply(o,arguments)},"scaleTime",0,function(){return tm.initRange.apply(eY(nk,n$,t3,t8,t9,nd,nx,n_,nM,u).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},"scaleUtc",0,function(){return tm.initRange.apply(eY(nT,nN,t4,t6,na,np,nv,nm,nM,a).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},"tickFormat",0,tE],429061)},62990,t=>{"use strict";Array.prototype.slice,t.s(["default",0,function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}])},867719,517306,610010,516039,9506,261770,t=>{"use strict";var n=t.i(62990),e=t.i(475058);function r(t,n){if((i=t.length)>1)for(var e,r,i,o=1,u=t[n[0]],a=u.length;o=0;)e[n]=n;return e}function o(t,n){return t[n]}function u(t){let n=[];return n.key=t,n}t.s(["stack",0,function(){var t=(0,e.default)([]),a=i,s=r,l=o;function c(e){var r,i,o=Array.from(t.apply(this,arguments),u),c=o.length,f=-1;for(let t of e)for(r=0,++f;r0){for(var e,i,o,u=0,a=t[0].length;u0){for(var e,i=0,o=t[n[0]],u=o.length;i0&&(i=(e=t[n[0]]).length)>0){for(var e,i,o,u=0,a=1;a{!function(e){"use strict";var r,i={precision:20,rounding:4,toExpNeg:-7,toExpPos:21,LN10:"2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341967784042286"},o=!0,u="[DecimalError] ",a=u+"Invalid argument: ",s=u+"Exponent out of range: ",l=Math.floor,c=Math.pow,f=/^(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,h=l(1286742750677284.5),g={};function d(t,n){var e,r,i,u,a,s,l,c,f=t.constructor,h=f.precision;if(!t.s||!n.s)return n.s||(n=new f(t)),o?T(n,h):n;if(l=t.d,c=n.d,a=t.e,i=n.e,l=l.slice(),u=a-i){for(u<0?(r=l,u=-u,s=c.length):(r=c,i=a,s=l.length),u>(s=(a=Math.ceil(h/7))>s?a+1:s+1)&&(u=s,r.length=1),r.reverse();u--;)r.push(0);r.reverse()}for((s=l.length)-(u=c.length)<0&&(u=s,r=c,c=l,l=r),e=0;u;)e=(l[--u]=l[u]+c[u]+e)/1e7|0,l[u]%=1e7;for(e&&(l.unshift(e),++i),s=l.length;0==l[--s];)l.pop();return n.d=l,n.e=i,o?T(n,h):n}function p(t,n,e){if(t!==~~t||te)throw Error(a+t)}function y(t){var n,e,r,i=t.length-1,o="",u=t[0];if(i>0){for(o+=u,n=1;nt.e^this.s<0?1:-1;for(n=0,e=(r=this.d.length)<(i=t.d.length)?r:i;nt.d[n]^this.s<0?1:-1;return r===i?0:r>i^this.s<0?1:-1},g.decimalPlaces=g.dp=function(){var t=this.d.length-1,n=(t-this.e)*7;if(t=this.d[t])for(;t%10==0;t/=10)n--;return n<0?0:n},g.dividedBy=g.div=function(t){return x(this,new this.constructor(t))},g.dividedToIntegerBy=g.idiv=function(t){var n=this.constructor;return T(x(this,new n(t),0,1),n.precision)},g.equals=g.eq=function(t){return!this.cmp(t)},g.exponent=function(){return _(this)},g.greaterThan=g.gt=function(t){return this.cmp(t)>0},g.greaterThanOrEqualTo=g.gte=function(t){return this.cmp(t)>=0},g.isInteger=g.isint=function(){return this.e>this.d.length-2},g.isNegative=g.isneg=function(){return this.s<0},g.isPositive=g.ispos=function(){return this.s>0},g.isZero=function(){return 0===this.s},g.lessThan=g.lt=function(t){return 0>this.cmp(t)},g.lessThanOrEqualTo=g.lte=function(t){return 1>this.cmp(t)},g.logarithm=g.log=function(t){var n,e=this.constructor,i=e.precision,a=i+5;if(void 0===t)t=new e(10);else if((t=new e(t)).s<1||t.eq(r))throw Error(u+"NaN");if(this.s<1)throw Error(u+(this.s?"NaN":"-Infinity"));return this.eq(r)?new e(0):(o=!1,n=x(w(this,a),w(t,a),a),o=!0,T(n,i))},g.minus=g.sub=function(t){return t=new this.constructor(t),this.s==t.s?N(this,t):d(this,(t.s=-t.s,t))},g.modulo=g.mod=function(t){var n,e=this.constructor,r=e.precision;if(!(t=new e(t)).s)throw Error(u+"NaN");return this.s?(o=!1,n=x(this,t,0,1).times(t),o=!0,this.minus(n)):T(new e(this),r)},g.naturalExponential=g.exp=function(){return v(this)},g.naturalLogarithm=g.ln=function(){return w(this)},g.negated=g.neg=function(){var t=new this.constructor(this);return t.s=-t.s||0,t},g.plus=g.add=function(t){return t=new this.constructor(t),this.s==t.s?d(this,t):N(this,(t.s=-t.s,t))},g.precision=g.sd=function(t){var n,e,r;if(void 0!==t&&!!t!==t&&1!==t&&0!==t)throw Error(a+t);if(n=_(this)+1,e=7*(r=this.d.length-1)+1,r=this.d[r]){for(;r%10==0;r/=10)e--;for(r=this.d[0];r>=10;r/=10)e++}return t&&n>e?n:e},g.squareRoot=g.sqrt=function(){var t,n,e,r,i,a,s,c=this.constructor;if(this.s<1){if(!this.s)return new c(0);throw Error(u+"NaN")}for(t=_(this),o=!1,0==(i=Math.sqrt(+this))||i==1/0?(((n=y(this.d)).length+t)%2==0&&(n+="0"),i=Math.sqrt(n),t=l((t+1)/2)-(t<0||t%2),r=new c(n=i==1/0?"5e"+t:(n=i.toExponential()).slice(0,n.indexOf("e")+1)+t)):r=new c(i.toString()),i=s=(e=c.precision)+3;;)if(r=(a=r).plus(x(this,a,s+2)).times(.5),y(a.d).slice(0,s)===(n=y(r.d)).slice(0,s)){if(n=n.slice(s-3,s+1),i==s&&"4999"==n){if(T(a,e+1,0),a.times(a).eq(this)){r=a;break}}else if("9999"!=n)break;s+=4}return o=!0,T(r,e)},g.times=g.mul=function(t){var n,e,r,i,u,a,s,l,c,f=this.constructor,h=this.d,g=(t=new f(t)).d;if(!this.s||!t.s)return new f(0);for(t.s*=this.s,e=this.e+t.e,(l=h.length)<(c=g.length)&&(u=h,h=g,g=u,a=l,l=c,c=a),u=[],r=a=l+c;r--;)u.push(0);for(r=c;--r>=0;){for(n=0,i=l+r;i>r;)s=u[i]+g[r]*h[i-r-1]+n,u[i--]=s%1e7|0,n=s/1e7|0;u[i]=(u[i]+n)%1e7|0}for(;!u[--a];)u.pop();return n?++e:u.shift(),t.d=u,t.e=e,o?T(t,f.precision):t},g.toDecimalPlaces=g.todp=function(t,n){var e=this,r=e.constructor;return(e=new r(e),void 0===t)?e:(p(t,0,1e9),void 0===n?n=r.rounding:p(n,0,8),T(e,t+_(e)+1,n))},g.toExponential=function(t,n){var e,r=this,i=r.constructor;return void 0===t?e=k(r,!0):(p(t,0,1e9),void 0===n?n=i.rounding:p(n,0,8),e=k(r=T(new i(r),t+1,n),!0,t+1)),e},g.toFixed=function(t,n){var e,r,i=this.constructor;return void 0===t?k(this):(p(t,0,1e9),void 0===n?n=i.rounding:p(n,0,8),e=k((r=T(new i(this),t+_(this)+1,n)).abs(),!1,t+_(r)+1),this.isneg()&&!this.isZero()?"-"+e:e)},g.toInteger=g.toint=function(){var t=this.constructor;return T(new t(this),_(this)+1,t.rounding)},g.toNumber=function(){return+this},g.toPower=g.pow=function(t){var n,e,i,a,s,c,f=this,h=f.constructor,g=+(t=new h(t));if(!t.s)return new h(r);if(!(f=new h(f)).s){if(t.s<1)throw Error(u+"Infinity");return f}if(f.eq(r))return f;if(i=h.precision,t.eq(r))return T(f,i);if(c=(n=t.e)>=(e=t.d.length-1),s=f.s,c){if((e=g<0?-g:g)<=0x1fffffffffffff){for(a=new h(r),n=Math.ceil(i/7+4),o=!1;e%2&&$((a=a.times(f)).d,n),0!==(e=l(e/2));)$((f=f.times(f)).d,n);return o=!0,t.s<0?new h(r).div(a):T(a,i)}}else if(s<0)throw Error(u+"NaN");return s=s<0&&1&t.d[Math.max(n,e)]?-1:1,f.s=1,o=!1,a=t.times(w(f,i+12)),o=!0,(a=v(a)).s=s,a},g.toPrecision=function(t,n){var e,r,i=this,o=i.constructor;return void 0===t?(e=_(i),r=k(i,e<=o.toExpNeg||e>=o.toExpPos)):(p(t,1,1e9),void 0===n?n=o.rounding:p(n,0,8),e=_(i=T(new o(i),t,n)),r=k(i,t<=e||e<=o.toExpNeg,t)),r},g.toSignificantDigits=g.tosd=function(t,n){var e=this.constructor;return void 0===t?(t=e.precision,n=e.rounding):(p(t,1,1e9),void 0===n?n=e.rounding:p(n,0,8)),T(new e(this),t,n)},g.toString=g.valueOf=g.val=g.toJSON=function(){var t=_(this),n=this.constructor;return k(this,t<=n.toExpNeg||t>=n.toExpPos)};var x=function(){function t(t,n){var e,r=0,i=t.length;for(t=t.slice();i--;)e=t[i]*n+r,t[i]=e%1e7|0,r=e/1e7|0;return r&&t.unshift(r),t}function n(t,n,e,r){var i,o;if(e!=r)o=e>r?1:-1;else for(i=o=0;in[i]?1:-1;break}return o}function e(t,n,e){for(var r=0;e--;)t[e]-=r,r=+(t[e]1;)t.shift()}return function(r,i,o,a){var s,l,c,f,h,g,d,p,y,x,v,m,M,w,b,N,k,$,C=r.constructor,D=r.s==i.s?1:-1,U=r.d,E=i.d;if(!r.s)return new C(r);if(!i.s)throw Error(u+"Division by zero");for(c=0,l=r.e-i.e,k=E.length,b=U.length,p=(d=new C(D)).d=[];E[c]==(U[c]||0);)++c;if(E[c]>(U[c]||0)&&--l,(m=null==o?o=C.precision:a?o+(_(r)-_(i))+1:o)<0)return new C(0);if(m=m/7+2|0,c=0,1==k)for(f=0,E=E[0],m++;(c1&&(E=t(E,f),U=t(U,f),k=E.length,b=U.length),w=k,x=(y=U.slice(0,k)).length;x=1e7/2&&++N;do f=0,(s=n(E,y,k,x))<0?(v=y[0],k!=x&&(v=1e7*v+(y[1]||0)),(f=v/N|0)>1?(f>=1e7&&(f=1e7-1),g=(h=t(E,f)).length,x=y.length,1==(s=n(h,y,g,x))&&(f--,e(h,k16)throw Error(s+_(t));if(!t.s)return new g(r);for(null==n?(o=!1,l=d):l=n,a=new g(.03125);t.abs().gte(.1);)t=t.times(a),h+=5;for(l+=Math.log(c(2,h))/Math.LN10*2+5|0,e=i=u=new g(r),g.precision=l;;){if(i=T(i.times(t),l),e=e.times(++f),y((a=u.plus(x(i,e,l))).d).slice(0,l)===y(u.d).slice(0,l)){for(;h--;)u=T(u.times(u),l);return g.precision=d,null==n?(o=!0,T(u,d)):u}u=a}}function _(t){for(var n=7*t.e,e=t.d[0];e>=10;e/=10)n++;return n}function m(t,n,e){if(n>t.LN10.sd())throw o=!0,e&&(t.precision=e),Error(u+"LN10 precision limit exceeded");return T(new t(t.LN10),n)}function M(t){for(var n="";t--;)n+="0";return n}function w(t,n){var e,i,a,s,l,c,f,h,g,d=1,p=t,v=p.d,M=p.constructor,b=M.precision;if(p.s<1)throw Error(u+(p.s?"NaN":"-Infinity"));if(p.eq(r))return new M(0);if(null==n?(o=!1,h=b):h=n,p.eq(10))return null==n&&(o=!0),m(M,h);if(M.precision=h+=10,i=(e=y(v)).charAt(0),!(15e14>Math.abs(s=_(p))))return f=m(M,h+2,b).times(s+""),p=w(new M(i+"."+e.slice(1)),h-10).plus(f),M.precision=b,null==n?(o=!0,T(p,b)):p;for(;i<7&&1!=i||1==i&&e.charAt(1)>3;)i=(e=y((p=p.times(t)).d)).charAt(0),d++;for(s=_(p),i>1?(p=new M("0."+e),s++):p=new M(i+"."+e.slice(1)),c=l=p=x(p.minus(r),p.plus(r),h),g=T(p.times(p),h),a=3;;){if(l=T(l.times(g),h),y((f=c.plus(x(l,new M(a),h))).d).slice(0,h)===y(c.d).slice(0,h))return c=c.times(2),0!==s&&(c=c.plus(m(M,h+2,b).times(s+""))),c=x(c,new M(d),h),M.precision=b,null==n?(o=!0,T(c,b)):c;c=f,a+=2}}function b(t,n){var e,r,i;for((e=n.indexOf("."))>-1&&(n=n.replace(".","")),(r=n.search(/e/i))>0?(e<0&&(e=r),e+=+n.slice(r+1),n=n.substring(0,r)):e<0&&(e=n.length),r=0;48===n.charCodeAt(r);)++r;for(i=n.length;48===n.charCodeAt(i-1);)--i;if(n=n.slice(r,i)){if(i-=r,t.e=l((e=e-r-1)/7),t.d=[],r=(e+1)%7,e<0&&(r+=7),rh||t.e<-h))throw Error(s+e)}else t.s=0,t.e=0,t.d=[0];return t}function T(t,n,e){var r,i,u,a,f,g,d,p,y=t.d;for(a=1,u=y[0];u>=10;u/=10)a++;if((r=n-a)<0)r+=7,i=n,d=y[p=0];else{if((p=Math.ceil((r+1)/7))>=(u=y.length))return t;for(a=1,d=u=y[p];u>=10;u/=10)a++;r%=7,i=r-7+a}if(void 0!==e&&(f=d/(u=c(10,a-i-1))%10|0,g=n<0||void 0!==y[p+1]||d%u,g=e<4?(f||g)&&(0==e||e==(t.s<0?3:2)):f>5||5==f&&(4==e||g||6==e&&(r>0?i>0?d/c(10,a-i):0:y[p-1])%10&1||e==(t.s<0?8:7))),n<1||!y[0])return g?(u=_(t),y.length=1,n=n-u-1,y[0]=c(10,(7-n%7)%7),t.e=l(-n/7)||0):(y.length=1,y[0]=t.e=t.s=0),t;if(0==r?(y.length=p,u=1,p--):(y.length=p+1,u=c(10,7-r),y[p]=i>0?(d/c(10,a-i)%c(10,i)|0)*u:0),g)for(;;)if(0==p){1e7==(y[0]+=u)&&(y[0]=1,++t.e);break}else{if(y[p]+=u,1e7!=y[p])break;y[p--]=0,u=1}for(r=y.length;0===y[--r];)y.pop();if(o&&(t.e>h||t.e<-h))throw Error(s+_(t));return t}function N(t,n){var e,r,i,u,a,s,l,c,f,h,g=t.constructor,d=g.precision;if(!t.s||!n.s)return n.s?n.s=-n.s:n=new g(t),o?T(n,d):n;if(l=t.d,h=n.d,r=n.e,c=t.e,l=l.slice(),a=c-r){for((f=a<0)?(e=l,a=-a,s=h.length):(e=h,r=c,s=l.length),a>(i=Math.max(Math.ceil(d/7),s)+2)&&(a=i,e.length=1),e.reverse(),i=a;i--;)e.push(0);e.reverse()}else{for((f=(i=l.length)<(s=h.length))&&(s=i),i=0;i0;--i)l[s++]=0;for(i=h.length;i>a;){if(l[--i]0?o=o.charAt(0)+"."+o.slice(1)+M(r):u>1&&(o=o.charAt(0)+"."+o.slice(1)),o=o+(i<0?"e":"e+")+i):i<0?(o="0."+M(-i-1)+o,e&&(r=e-u)>0&&(o+=M(r))):i>=u?(o+=M(i+1-u),e&&(r=e-i-1)>0&&(o=o+"."+M(r))):((r=i+1)0&&(i+1===u&&(o+="."),o+=M(r))),t.s<0?"-"+o:o}function $(t,n){if(t.length>n)return t.length=n,!0}function C(t){if(!t||"object"!=typeof t)throw Error(u+"Object expected");var n,e,r,i=["precision",1,1e9,"rounding",0,8,"toExpNeg",-1/0,0,"toExpPos",0,1/0];for(n=0;n=i[n+1]&&r<=i[n+2])this[e]=r;else throw Error(a+e+": "+r);if(void 0!==(r=t[e="LN10"]))if(r==Math.LN10)this[e]=new this(r);else throw Error(a+e+": "+r);return this}if((i=function t(n){var e,r,i;function o(t){if(!(this instanceof o))return new o(t);if(this.constructor=o,t instanceof o){this.s=t.s,this.e=t.e,this.d=(t=t.d)?t.slice():t;return}if("number"==typeof t){if(0*t!=0)throw Error(a+t);if(t>0)this.s=1;else if(t<0)t=-t,this.s=-1;else{this.s=0,this.e=0,this.d=[0];return}if(t===~~t&&t<1e7){this.e=0,this.d=[t];return}return b(this,t.toString())}if("string"!=typeof t)throw Error(a+t);if(45===t.charCodeAt(0)?(t=t.slice(1),this.s=-1):this.s=1,f.test(t))b(this,t);else throw Error(a+t)}if(o.prototype=g,o.ROUND_UP=0,o.ROUND_DOWN=1,o.ROUND_CEIL=2,o.ROUND_FLOOR=3,o.ROUND_HALF_UP=4,o.ROUND_HALF_DOWN=5,o.ROUND_HALF_EVEN=6,o.ROUND_HALF_CEIL=7,o.ROUND_HALF_FLOOR=8,o.clone=t,o.config=o.set=C,void 0===n&&(n={}),n)for(e=0,i=["precision","rounding","toExpNeg","toExpPos","LN10"];etypeof self&&self&&self.self==self?self:Function("return this")()),e.Decimal=i)}(t.e)},48114,t=>{"use strict";function n(t){this._context=t}n.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}},t.s(["default",0,function(t){return new n(t)}])},159843,885532,t=>{"use strict";var n=t.i(62990),e=t.i(475058),r=t.i(48114),i=t.i(216888);function o(t){return t[0]}function u(t){return t[1]}t.s(["x",0,o,"y",0,u],885532),t.s(["default",0,function(t,a){var s=(0,e.default)(!0),l=null,c=r.default,f=null,h=(0,i.withPath)(g);function g(e){var r,i,o,u=(e=(0,n.default)(e)).length,g=!1;for(null==l&&(f=c(o=h())),r=0;r<=u;++r)!(r{"use strict";var n=t.i(159843);t.s(["line",()=>n.default])},999173,t=>{"use strict";var n=t.i(62990),e=t.i(475058),r=t.i(48114),i=t.i(159843),o=t.i(216888),u=t.i(885532);t.s(["area",0,function(t,a,s){var l=null,c=(0,e.default)(!0),f=null,h=r.default,g=null,d=(0,o.withPath)(p);function p(e){var r,i,o,u,p,y=(e=(0,n.default)(e)).length,x=!1,v=Array(y),_=Array(y);for(null==f&&(g=h(p=d())),r=0;r<=y;++r){if(!(r=i;--o)g.point(v[o],_[o]);g.lineEnd(),g.areaEnd()}x&&(v[r]=+t(u,r,e),_[r]=+a(u,r,e),g.point(l?+l(u,r,e):v[r],s?+s(u,r,e):_[r]))}if(p)return g=null,p+""||null}function y(){return(0,i.default)().defined(c).curve(h).context(f)}return t="function"==typeof t?t:void 0===t?u.x:(0,e.default)(+t),a="function"==typeof a?a:void 0===a?(0,e.default)(0):(0,e.default)(+a),s="function"==typeof s?s:void 0===s?u.y:(0,e.default)(+s),p.x=function(n){return arguments.length?(t="function"==typeof n?n:(0,e.default)(+n),l=null,p):t},p.x0=function(n){return arguments.length?(t="function"==typeof n?n:(0,e.default)(+n),p):t},p.x1=function(t){return arguments.length?(l=null==t?null:"function"==typeof t?t:(0,e.default)(+t),p):l},p.y=function(t){return arguments.length?(a="function"==typeof t?t:(0,e.default)(+t),s=null,p):a},p.y0=function(t){return arguments.length?(a="function"==typeof t?t:(0,e.default)(+t),p):a},p.y1=function(t){return arguments.length?(s=null==t?null:"function"==typeof t?t:(0,e.default)(+t),p):s},p.lineX0=p.lineY0=function(){return y().x(t).y(a)},p.lineY1=function(){return y().x(t).y(s)},p.lineX1=function(){return y().x(l).y(a)},p.defined=function(t){return arguments.length?(c="function"==typeof t?t:(0,e.default)(!!t),p):c},p.curve=function(t){return arguments.length?(h=t,null!=f&&(g=h(f)),p):h},p.context=function(t){return arguments.length?(null==t?f=g=null:g=h(f=t),p):f},p}],999173)},810489,t=>{"use strict";t.s(["default",0,function(){}])},677304,t=>{"use strict";function n(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function e(t){this._context=t}e.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:n(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t*=1,e*=1,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:n(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},t.s(["default",0,function(t){return new e(t)},"point",0,n])},910118,593866,t=>{"use strict";var n=t.i(810489),e=t.i(677304);function r(t){this._context=t}function i(t){this._context=t}r.prototype={areaStart:n.default,areaEnd:n.default,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:(0,e.point)(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},t.s(["curveBasisClosed",0,function(t){return new r(t)}],910118),i.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var r=(this._x0+4*this._x1+t)/6,i=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(r,i):this._context.moveTo(r,i);break;case 3:this._point=4;default:(0,e.point)(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},t.s(["curveBasisOpen",0,function(t){return new i(t)}],593866)},600104,t=>{"use strict";var n=t.i(677304);t.s(["curveBasis",()=>n.default])},872200,722914,t=>{"use strict";class n{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}t.s(["curveBumpX",0,function(t){return new n(t,!0)}],872200),t.s(["curveBumpY",0,function(t){return new n(t,!1)}],722914)},821641,t=>{"use strict";var n=t.i(810489);function e(t){this._context=t}e.prototype={areaStart:n.default,areaEnd:n.default,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,n){t*=1,n*=1,this._point?this._context.lineTo(t,n):(this._point=1,this._context.moveTo(t,n))}},t.s(["curveLinearClosed",0,function(t){return new e(t)}],821641)},851262,t=>{"use strict";var n=t.i(48114);t.s(["curveLinear",()=>n.default])},363823,992536,226619,381142,700357,163018,t=>{"use strict";function n(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),u=(e-t._y1)/(i||r<0&&-0);return((o<0?-1:1)+(u<0?-1:1))*Math.min(Math.abs(o),Math.abs(u),.5*Math.abs((o*i+u*r)/(r+i)))||0}function e(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function r(t,n,e){var r=t._x0,i=t._y0,o=t._x1,u=t._y1,a=(o-r)/3;t._context.bezierCurveTo(r+a,i+a*n,o-a,u-a*e,o,u)}function i(t){this._context=t}function o(t){this._context=new u(t)}function u(t){this._context=t}function a(t){this._context=t}function s(t){var n,e,r=t.length-1,i=Array(r),o=Array(r),u=Array(r);for(i[0]=0,o[0]=2,u[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(u[n]-i[n+1])/o[n];for(n=0,o[r-1]=(t[r]+i[r-1])/2;n=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t*=1,n*=1,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}},t.s(["curveStep",0,function(t){return new l(t,.5)}],381142),t.s(["curveStepAfter",0,function(t){return new l(t,1)}],700357),t.s(["curveStepBefore",0,function(t){return new l(t,0)}],163018)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/035e9knuui_xh.js b/litellm/proxy/_experimental/out/_next/static/chunks/035e9knuui_xh.js new file mode 100644 index 00000000000..318832bc8ea --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/035e9knuui_xh.js @@ -0,0 +1,10 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(829087),o=e.i(480731),a=e.i(444755),l=e.i(673706),i=e.i(95779);let s={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,l.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:g,variant:b="simple",tooltip:p,size:h=o.Sizes.SM,color:f,className:y}=e,v=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),x=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,l.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,l.getColorClassNames)(t,i.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,a.tremorTwMerge)((0,l.getColorClassNames)(t,i.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(b,f),{tooltipProps:O,getReferenceProps:C}=(0,n.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,l.mergeRefs)([m,O.refs.setReference]),className:(0,a.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",x.bgColor,x.textColor,x.borderColor,x.ringColor,c[b].rounded,c[b].border,c[b].shadow,c[b].ring,s[h].paddingX,s[h].paddingY,y)},C,v),r.default.createElement(n.default,Object.assign({text:p},O)),r.default.createElement(g,{className:(0,a.tremorTwMerge)(u("icon"),"shrink-0",d[h].height,d[h].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},434626,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,r],434626)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},122577,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,r],122577)},902555,e=>{"use strict";var t=e.i(843476),r=e.i(591935),n=e.i(122577),o=e.i(278587),a=e.i(68155),l=e.i(360820),i=e.i(871943),s=e.i(434626),d=e.i(551332),c=e.i(592968),u=e.i(115504),m=e.i(752978);function g({icon:e,onClick:r,className:n,disabled:o,dataTestId:a}){return o?(0,t.jsx)(m.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":a}):(0,t.jsx)(m.Icon,{icon:e,size:"sm",onClick:r,className:(0,u.cx)("cursor-pointer",n),"data-testid":a})}let b={Edit:{icon:r.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:a.TrashIcon,className:"hover:text-red-600"},Test:{icon:n.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:o.RefreshIcon,className:"hover:text-green-600"},Up:{icon:l.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:i.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:s.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:r,disabled:n=!1,disabledTooltipText:o,dataTestId:a,variant:l}){let{icon:i,className:s}=b[l];return(0,t.jsx)(c.Tooltip,{title:n?o:r,children:(0,t.jsx)("span",{children:(0,t.jsx)(g,{icon:i,onClick:e,className:s,disabled:n,dataTestId:a})})})}],902555)},954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),n=e.i(540143),o=e.i(915823),a=e.i(619273),l=class extends o.Subscribable{#e;#t=void 0;#r;#n;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#o()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,a.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,a.hashKey)(t.mutationKey)!==(0,a.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#o(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#o(),this.#a()}mutate(e,t){return this.#n=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#o(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#a(e){n.notifyManager.batch(()=>{if(this.#n&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#n.onSuccess?.(e.data,t,r,n)}catch(e){Promise.reject(e)}try{this.#n.onSettled?.(e.data,null,t,r,n)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#n.onError?.(e.error,t,r,n)}catch(e){Promise.reject(e)}try{this.#n.onSettled?.(void 0,e.error,t,r,n)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},i=e.i(912598);e.s(["useMutation",0,function(e,r){let o=(0,i.useQueryClient)(r),[s]=t.useState(()=>new l(o,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let d=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(n.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),c=t.useCallback((e,t)=>{s.mutate(e,t).catch(a.noop)},[s]);if(d.error&&(0,a.shouldThrowError)(s.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:c,mutateAsync:d.mutate}}],954616)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var o=e.i(9583),a=r.forwardRef(function(e,a){return r.createElement(o.default,(0,t.default)({},e,{ref:a,icon:n}))});e.s(["ExclamationCircleOutlined",0,a],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(529681),o=e.i(242064),a=e.i(517455),l=e.i(185793),i=e.i(721369),s=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let d=e=>{var{prefixCls:n,className:a,hoverable:l=!0}=e,i=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(o.ConfigContext),c=d("card",n),u=(0,r.default)(`${c}-grid`,a,{[`${c}-grid-hoverable`]:l});return t.createElement("div",Object.assign({},i,{className:u}))};e.i(296059);var c=e.i(915654),u=e.i(183293),m=e.i(246422),g=e.i(838378);let b=(0,m.genStyleHooks)("Card",e=>{let t=(0,g.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:r,cardHeadPadding:n,colorBorderSecondary:o,boxShadowTertiary:a,bodyPadding:l,extraColor:i}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:a},[`${t}-head`]:(e=>{let{antCls:t,componentCls:r,headerHeight:n,headerPadding:o,tabsMarginBottom:a}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:n,marginBottom:-1,padding:`0 ${(0,c.unit)(o)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${r}-typography, + > ${r}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:a,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:i,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:l,borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:r,cardShadow:n,lineWidth:o}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,c.unit)(o)} 0 0 0 ${r}, + 0 ${(0,c.unit)(o)} 0 0 ${r}, + ${(0,c.unit)(o)} ${(0,c.unit)(o)} 0 0 ${r}, + ${(0,c.unit)(o)} 0 0 0 ${r} inset, + 0 ${(0,c.unit)(o)} 0 0 ${r} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:n}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:r,actionsLiMargin:n,cardActionsIconSize:o,colorBorderSecondary:a,actionsBg:l}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:l,borderTop:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`,display:"flex",borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:n,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${r}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,c.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${r}`]:{fontSize:o,lineHeight:(0,c.unit)(e.calc(o).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,c.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${o}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:r}},[`${t}-contain-grid`]:{borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:n}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:r,headerPadding:n,bodyPadding:o}=e;return{[`${t}-head`]:{padding:`0 ${(0,c.unit)(n)}`,background:r,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,c.unit)(e.padding)} ${(0,c.unit)(o)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:r,headerPaddingSM:n,headerHeightSM:o,headerFontSizeSM:a}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:o,padding:`0 ${(0,c.unit)(n)}`,fontSize:a,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:r}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,r;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(r=e.headerPadding)?r:e.paddingLG}});var p=e.i(792812),h=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let f=e=>{let{actionClasses:r,actions:n=[],actionStyle:o}=e;return t.createElement("ul",{className:r,style:o},n.map((e,r)=>{let o=`action-${r}`;return t.createElement("li",{style:{width:`${100/n.length}%`},key:o},t.createElement("span",null,e))}))},y=t.forwardRef((e,s)=>{let c,{prefixCls:u,className:m,rootClassName:g,style:y,extra:v,headStyle:x={},bodyStyle:O={},title:C,loading:$,bordered:j,variant:k,size:w,type:S,cover:E,actions:N,tabList:P,children:M,activeTabKey:T,defaultActiveTabKey:R,tabBarExtraContent:z,hoverable:B,tabProps:I={},classNames:L,styles:H}=e,D=h(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:W,direction:G,card:F}=t.useContext(o.ConfigContext),[A]=(0,p.default)("card",k,j),X=e=>{var t;return(0,r.default)(null==(t=null==F?void 0:F.classNames)?void 0:t[e],null==L?void 0:L[e])},K=e=>{var t;return Object.assign(Object.assign({},null==(t=null==F?void 0:F.styles)?void 0:t[e]),null==H?void 0:H[e])},q=t.useMemo(()=>{let e=!1;return t.Children.forEach(M,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[M]),V=W("card",u),[U,Y,_]=b(V),Q=t.createElement(l.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},M),J=void 0!==T,Z=Object.assign(Object.assign({},I),{[J?"activeKey":"defaultActiveKey"]:J?T:R,tabBarExtraContent:z}),ee=(0,a.default)(w),et=ee&&"default"!==ee?ee:"large",er=P?t.createElement(i.default,Object.assign({size:et},Z,{className:`${V}-head-tabs`,onChange:t=>{var r;null==(r=e.onTabChange)||r.call(e,t)},items:P.map(e=>{var{tab:t}=e;return Object.assign({label:t},h(e,["tab"]))})})):null;if(C||v||er){let e=(0,r.default)(`${V}-head`,X("header")),n=(0,r.default)(`${V}-head-title`,X("title")),o=(0,r.default)(`${V}-extra`,X("extra")),a=Object.assign(Object.assign({},x),K("header"));c=t.createElement("div",{className:e,style:a},t.createElement("div",{className:`${V}-head-wrapper`},C&&t.createElement("div",{className:n,style:K("title")},C),v&&t.createElement("div",{className:o,style:K("extra")},v)),er)}let en=(0,r.default)(`${V}-cover`,X("cover")),eo=E?t.createElement("div",{className:en,style:K("cover")},E):null,ea=(0,r.default)(`${V}-body`,X("body")),el=Object.assign(Object.assign({},O),K("body")),ei=t.createElement("div",{className:ea,style:el},$?Q:M),es=(0,r.default)(`${V}-actions`,X("actions")),ed=(null==N?void 0:N.length)?t.createElement(f,{actionClasses:es,actionStyle:K("actions"),actions:N}):null,ec=(0,n.default)(D,["onTabChange"]),eu=(0,r.default)(V,null==F?void 0:F.className,{[`${V}-loading`]:$,[`${V}-bordered`]:"borderless"!==A,[`${V}-hoverable`]:B,[`${V}-contain-grid`]:q,[`${V}-contain-tabs`]:null==P?void 0:P.length,[`${V}-${ee}`]:ee,[`${V}-type-${S}`]:!!S,[`${V}-rtl`]:"rtl"===G},m,g,Y,_),em=Object.assign(Object.assign({},null==F?void 0:F.style),y);return U(t.createElement("div",Object.assign({ref:s},ec,{className:eu,style:em}),c,eo,ei,ed))});var v=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};y.Grid=d,y.Meta=e=>{let{prefixCls:n,className:a,avatar:l,title:i,description:s}=e,d=v(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:c}=t.useContext(o.ConfigContext),u=c("card",n),m=(0,r.default)(`${u}-meta`,a),g=l?t.createElement("div",{className:`${u}-meta-avatar`},l):null,b=i?t.createElement("div",{className:`${u}-meta-title`},i):null,p=s?t.createElement("div",{className:`${u}-meta-description`},s):null,h=b||p?t.createElement("div",{className:`${u}-meta-detail`},b,p):null;return t.createElement("div",Object.assign({},d,{className:m}),g,h)},e.s(["Card",0,y],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(908206),o=e.i(242064),a=e.i(517455),l=e.i(150073);let i={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var d=e.i(876556),c=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r},u=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let m=e=>{let{itemPrefixCls:n,component:o,span:a,className:l,style:i,labelStyle:d,contentStyle:c,bordered:u,label:m,content:g,colon:b,type:p,styles:h}=e,{classNames:f}=t.useContext(s),y=Object.assign(Object.assign({},d),null==h?void 0:h.label),v=Object.assign(Object.assign({},c),null==h?void 0:h.content);if(u)return t.createElement(o,{colSpan:a,style:i,className:(0,r.default)(l,{[`${n}-item-${p}`]:"label"===p||"content"===p,[null==f?void 0:f.label]:(null==f?void 0:f.label)&&"label"===p,[null==f?void 0:f.content]:(null==f?void 0:f.content)&&"content"===p})},null!=m&&t.createElement("span",{style:y},m),null!=g&&t.createElement("span",{style:v},g));return t.createElement(o,{colSpan:a,style:i,className:(0,r.default)(`${n}-item`,l)},t.createElement("div",{className:`${n}-item-container`},null!=m&&t.createElement("span",{style:y,className:(0,r.default)(`${n}-item-label`,null==f?void 0:f.label,{[`${n}-item-no-colon`]:!b})},m),null!=g&&t.createElement("span",{style:v,className:(0,r.default)(`${n}-item-content`,null==f?void 0:f.content)},g)))};function g(e,{colon:r,prefixCls:n,bordered:o},{component:a,type:l,showLabel:i,showContent:s,labelStyle:d,contentStyle:c,styles:u}){return e.map(({label:e,children:g,prefixCls:b=n,className:p,style:h,labelStyle:f,contentStyle:y,span:v=1,key:x,styles:O},C)=>"string"==typeof a?t.createElement(m,{key:`${l}-${x||C}`,className:p,style:h,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),f),null==O?void 0:O.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),y),null==O?void 0:O.content)},span:v,colon:r,component:a,itemPrefixCls:b,bordered:o,label:i?e:null,content:s?g:null,type:l}):[t.createElement(m,{key:`label-${x||C}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),h),f),null==O?void 0:O.label),span:1,colon:r,component:a[0],itemPrefixCls:b,bordered:o,label:e,type:"label"}),t.createElement(m,{key:`content-${x||C}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),h),y),null==O?void 0:O.content),span:2*v-1,component:a[1],itemPrefixCls:b,bordered:o,content:g,type:"content"})])}let b=e=>{let r=t.useContext(s),{prefixCls:n,vertical:o,row:a,index:l,bordered:i}=e;return o?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${l}`,className:`${n}-row`},g(a,e,Object.assign({component:"th",type:"label",showLabel:!0},r))),t.createElement("tr",{key:`content-${l}`,className:`${n}-row`},g(a,e,Object.assign({component:"td",type:"content",showContent:!0},r)))):t.createElement("tr",{key:l,className:`${n}-row`},g(a,e,Object.assign({component:i?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},r)))};e.i(296059);var p=e.i(915654),h=e.i(183293),f=e.i(246422),y=e.i(838378);let v=(0,f.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:r,itemPaddingBottom:n,itemPaddingEnd:o,colonMarginRight:a,colonMarginLeft:l,titleMarginBottom:i}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,h.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:r}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.padding)} ${(0,p.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:r,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingSM)} ${(0,p.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingXS)} ${(0,p.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:i},[`${t}-title`]:Object.assign(Object.assign({},h.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:r,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:n,paddingInlineEnd:o},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,p.unit)(l)} ${(0,p.unit)(a)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,y.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var x=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let O=e=>{let m,{prefixCls:g,title:p,extra:h,column:f,colon:y=!0,bordered:O,layout:C,children:$,className:j,rootClassName:k,style:w,size:S,labelStyle:E,contentStyle:N,styles:P,items:M,classNames:T}=e,R=x(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:z,direction:B,className:I,style:L,classNames:H,styles:D}=(0,o.useComponentConfig)("descriptions"),W=z("descriptions",g),G=(0,l.default)(),F=t.useMemo(()=>{var e;return"number"==typeof f?f:null!=(e=(0,n.matchScreen)(G,Object.assign(Object.assign({},i),f)))?e:3},[G,f]),A=(m=t.useMemo(()=>M||(0,d.default)($).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[M,$]),t.useMemo(()=>m.map(e=>{var{span:t}=e,r=c(e,["span"]);return"filled"===t?Object.assign(Object.assign({},r),{filled:!0}):Object.assign(Object.assign({},r),{span:"number"==typeof t?t:(0,n.matchScreen)(G,t)})}),[m,G])),X=(0,a.default)(S),K=((e,r)=>{let[n,o]=(0,t.useMemo)(()=>{let t,n,o,a;return t=[],n=[],o=!1,a=0,r.filter(e=>e).forEach(r=>{let{filled:l}=r,i=u(r,["filled"]);if(l){n.push(i),t.push(n),n=[],a=0;return}let s=e-a;(a+=r.span||1)>=e?(a>e?(o=!0,n.push(Object.assign(Object.assign({},i),{span:s}))):n.push(i),t.push(n),n=[],a=0):n.push(i)}),n.length>0&&t.push(n),[t=t.map(t=>{let r=t.reduce((e,t)=>e+(t.span||1),0);if(r({labelStyle:E,contentStyle:N,styles:{content:Object.assign(Object.assign({},D.content),null==P?void 0:P.content),label:Object.assign(Object.assign({},D.label),null==P?void 0:P.label)},classNames:{label:(0,r.default)(H.label,null==T?void 0:T.label),content:(0,r.default)(H.content,null==T?void 0:T.content)}}),[E,N,P,T,H,D]);return q(t.createElement(s.Provider,{value:Y},t.createElement("div",Object.assign({className:(0,r.default)(W,I,H.root,null==T?void 0:T.root,{[`${W}-${X}`]:X&&"default"!==X,[`${W}-bordered`]:!!O,[`${W}-rtl`]:"rtl"===B},j,k,V,U),style:Object.assign(Object.assign(Object.assign(Object.assign({},L),D.root),null==P?void 0:P.root),w)},R),(p||h)&&t.createElement("div",{className:(0,r.default)(`${W}-header`,H.header,null==T?void 0:T.header),style:Object.assign(Object.assign({},D.header),null==P?void 0:P.header)},p&&t.createElement("div",{className:(0,r.default)(`${W}-title`,H.title,null==T?void 0:T.title),style:Object.assign(Object.assign({},D.title),null==P?void 0:P.title)},p),h&&t.createElement("div",{className:(0,r.default)(`${W}-extra`,H.extra,null==T?void 0:T.extra),style:Object.assign(Object.assign({},D.extra),null==P?void 0:P.extra)},h)),t.createElement("div",{className:`${W}-view`},t.createElement("table",null,t.createElement("tbody",null,K.map((e,r)=>t.createElement(b,{key:r,index:r,colon:y,prefixCls:W,vertical:"vertical"===C,bordered:O,row:e}))))))))};O.Item=({children:e})=>e,e.s(["Descriptions",0,O],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),r=e.i(732961),n=e.i(289882),o=e.i(170517),a=e.i(628882),l=e.i(320890),i=e.i(104458),s=e.i(722319),d=e.i(8398),c=e.i(279728);e.i(765846);var u=e.i(602716),m=e.i(328052),g=e.i(135551);let b=(e,t)=>new g.FastColor(e).setA(t).toRgbString(),p=(e,t)=>new g.FastColor(e).lighten(t).toHexString(),h=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},f=(e,t)=>{let r=e||"#000",n=t||"#fff";return{colorBgBase:r,colorTextBase:n,colorText:b(n,.85),colorTextSecondary:b(n,.65),colorTextTertiary:b(n,.45),colorTextQuaternary:b(n,.25),colorFill:b(n,.18),colorFillSecondary:b(n,.12),colorFillTertiary:b(n,.08),colorFillQuaternary:b(n,.04),colorBgSolid:b(n,.95),colorBgSolidHover:b(n,1),colorBgSolidActive:b(n,.9),colorBgElevated:p(r,12),colorBgContainer:p(r,8),colorBgLayout:p(r,0),colorBgSpotlight:p(r,26),colorBgBlur:b(n,.04),colorBorder:p(r,26),colorBorderSecondary:p(r,19)}},y={defaultSeed:l.defaultConfig.token,useToken:function(){let[e,t,r]=(0,i.useToken)();return{theme:e,token:t,hashId:r}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let r=Object.keys(o.defaultPresetColors).map(t=>{let r=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,n,o)=>(e[`${t}-${o+1}`]=r[o],e[`${t}${o+1}`]=r[o],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),n=null!=t?t:(0,s.default)(e),a=(0,m.default)(e,{generateColorPalettes:h,generateNeutralColorPalettes:f});return Object.assign(Object.assign(Object.assign(Object.assign({},n),r),a),{colorPrimaryBg:a.colorPrimaryBorder,colorPrimaryBgHover:a.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let r=null!=t?t:(0,s.default)(e),n=r.fontSizeSM,o=r.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},r),function(e){let{sizeUnit:t,sizeStep:r}=e,n=r-2;return{sizeXXL:t*(n+10),sizeXL:t*(n+6),sizeLG:t*(n+2),sizeMD:t*(n+2),sizeMS:t*(n+1),size:t*n,sizeSM:t*n,sizeXS:t*(n-1),sizeXXS:t*(n-1)}}(null!=t?t:e)),(0,c.default)(n)),{controlHeight:o}),(0,d.default)(Object.assign(Object.assign({},r),{controlHeight:o})))},getDesignToken:e=>{let l=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):n.default,i=Object.assign(Object.assign({},o.default),null==e?void 0:e.token);return(0,r.getComputedToken)(i,{override:null==e?void 0:e.token},l,a.default)},defaultConfig:l.defaultConfig,_internalContext:l.DesignTokenContext};e.s(["theme",0,y],368869)},127952,e=>{"use strict";var t=e.i(843476),r=e.i(560445),n=e.i(175712),o=e.i(869216),a=e.i(311451),l=e.i(212931),i=e.i(898586),s=e.i(368869),d=e.i(270377),c=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:m,message:g,resourceInformationTitle:b,resourceInformation:p,onCancel:h,onOk:f,confirmLoading:y,requiredConfirmation:v}){let{Title:x,Text:O}=i.Typography,{token:C}=s.theme.useToken(),[$,j]=(0,c.useState)("");return(0,c.useEffect)(()=>{e&&j("")},[e]),(0,t.jsx)(l.Modal,{title:u,open:e,onOk:f,onCancel:h,confirmLoading:y,okText:y?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!v&&$!==v||y},cancelButtonProps:{disabled:y},children:(0,t.jsxs)("div",{className:"space-y-4",children:[m&&(0,t.jsx)(r.Alert,{message:m,type:"warning"}),(0,t.jsx)(n.Card,{title:b,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:C.colorErrorBg,borderColor:C.colorErrorBorder}},style:{backgroundColor:C.colorErrorBg,borderColor:C.colorErrorBorder},children:(0,t.jsx)(o.Descriptions,{column:1,size:"small",children:p&&p.map(({label:e,value:r,...n})=>(0,t.jsx)(o.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(O,{...n,children:r??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(O,{children:g})}),v&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(O,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(O,{children:"Type "}),(0,t.jsx)(O,{strong:!0,type:"danger",children:v}),(0,t.jsx)(O,{children:" to confirm deletion:"})]}),(0,t.jsx)(a.Input,{value:$,onChange:e=>j(e.target.value),placeholder:v,className:"rounded-md",prefix:(0,t.jsx)(d.ExclamationCircleOutlined,{style:{color:C.colorError}}),autoFocus:!0})]})]})})}])},21548,e=>{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},727612,e=>{"use strict";let t=(0,e.i(475254).default)("trash-2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);e.s(["Trash2",0,t],727612)},823429,e=>{"use strict";let t=(0,e.i(475254).default)("square-pen",[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",key:"1m0v6g"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",key:"ohrbg2"}]]);e.s(["default",0,t])},888288,e=>{"use strict";var t=e.i(271645);e.s(["default",0,(e,r)=>{let n=void 0!==r,[o,a]=(0,t.useState)(e);return[n?r:o,e=>{n||a(e)}]}])},793130,e=>{"use strict";var t=e.i(290571),r=e.i(783222),n=e.i(433336),o=e.i(271645),a=e.i(394487),l=e.i(503269),i=e.i(214520),s=e.i(746725),d=e.i(914189),c=e.i(144279),u=e.i(294316),m=e.i(601893),g=e.i(140721),b=e.i(942803),p=e.i(233538),h=e.i(694421),f=e.i(700020),y=e.i(35889),v=e.i(998348),x=e.i(722678);let O=(0,o.createContext)(null);O.displayName="GroupContext";let C=o.Fragment,$=Object.assign((0,f.forwardRefWithAs)(function(e,t){var C;let $=(0,o.useId)(),j=(0,b.useProvidedId)(),k=(0,m.useDisabled)(),{id:w=j||`headlessui-switch-${$}`,disabled:S=k||!1,checked:E,defaultChecked:N,onChange:P,name:M,value:T,form:R,autoFocus:z=!1,...B}=e,I=(0,o.useContext)(O),[L,H]=(0,o.useState)(null),D=(0,o.useRef)(null),W=(0,u.useSyncRefs)(D,t,null===I?null:I.setSwitch,H),G=(0,i.useDefaultValue)(N),[F,A]=(0,l.useControllable)(E,P,null!=G&&G),X=(0,s.useDisposables)(),[K,q]=(0,o.useState)(!1),V=(0,d.useEvent)(()=>{q(!0),null==A||A(!F),X.nextFrame(()=>{q(!1)})}),U=(0,d.useEvent)(e=>{if((0,p.isDisabledReactIssue7711)(e.currentTarget))return e.preventDefault();e.preventDefault(),V()}),Y=(0,d.useEvent)(e=>{e.key===v.Keys.Space?(e.preventDefault(),V()):e.key===v.Keys.Enter&&(0,h.attemptSubmit)(e.currentTarget)}),_=(0,d.useEvent)(e=>e.preventDefault()),Q=(0,x.useLabelledBy)(),J=(0,y.useDescribedBy)(),{isFocusVisible:Z,focusProps:ee}=(0,r.useFocusRing)({autoFocus:z}),{isHovered:et,hoverProps:er}=(0,n.useHover)({isDisabled:S}),{pressed:en,pressProps:eo}=(0,a.useActivePress)({disabled:S}),ea=(0,o.useMemo)(()=>({checked:F,disabled:S,hover:et,focus:Z,active:en,autofocus:z,changing:K}),[F,et,Z,en,S,K,z]),el=(0,f.mergeProps)({id:w,ref:W,role:"switch",type:(0,c.useResolveButtonType)(e,L),tabIndex:-1===e.tabIndex?0:null!=(C=e.tabIndex)?C:0,"aria-checked":F,"aria-labelledby":Q,"aria-describedby":J,disabled:S||void 0,autoFocus:z,onClick:U,onKeyUp:Y,onKeyPress:_},ee,er,eo),ei=(0,o.useCallback)(()=>{if(void 0!==G)return null==A?void 0:A(G)},[A,G]),es=(0,f.useRender)();return o.default.createElement(o.default.Fragment,null,null!=M&&o.default.createElement(g.FormFields,{disabled:S,data:{[M]:T||"on"},overrides:{type:"checkbox",checked:F},form:R,onReset:ei}),es({ourProps:el,theirProps:B,slot:ea,defaultTag:"button",name:"Switch"}))}),{Group:function(e){var t;let[r,n]=(0,o.useState)(null),[a,l]=(0,x.useLabels)(),[i,s]=(0,y.useDescriptions)(),d=(0,o.useMemo)(()=>({switch:r,setSwitch:n}),[r,n]),c=(0,f.useRender)();return o.default.createElement(s,{name:"Switch.Description",value:i},o.default.createElement(l,{name:"Switch.Label",value:a,props:{htmlFor:null==(t=d.switch)?void 0:t.id,onClick(e){r&&(e.currentTarget instanceof HTMLLabelElement&&e.preventDefault(),r.click(),r.focus({preventScroll:!0}))}}},o.default.createElement(O.Provider,{value:d},c({ourProps:{},theirProps:e,slot:{},defaultTag:C,name:"Switch.Group"}))))},Label:x.Label,Description:y.Description});var j=e.i(888288),k=e.i(95779),w=e.i(444755),S=e.i(673706),E=e.i(829087);let N=(0,S.makeClassName)("Switch"),P=o.default.forwardRef((e,r)=>{let{checked:n,defaultChecked:a=!1,onChange:l,color:i,name:s,error:d,errorMessage:c,disabled:u,required:m,tooltip:g,id:b}=e,p=(0,t.__rest)(e,["checked","defaultChecked","onChange","color","name","error","errorMessage","disabled","required","tooltip","id"]),h={bgColor:i?(0,S.getColorClassNames)(i,k.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",ringColor:i?(0,S.getColorClassNames)(i,k.colorPalette.ring).ringColor:"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"},[f,y]=(0,j.default)(a,n),[v,x]=(0,o.useState)(!1),{tooltipProps:O,getReferenceProps:C}=(0,E.useTooltip)(300);return o.default.createElement("div",{className:"flex flex-row items-center justify-start"},o.default.createElement(E.default,Object.assign({text:g},O)),o.default.createElement("div",Object.assign({ref:(0,S.mergeRefs)([r,O.refs.setReference]),className:(0,w.tremorTwMerge)(N("root"),"flex flex-row relative h-5")},p,C),o.default.createElement("input",{type:"checkbox",className:(0,w.tremorTwMerge)(N("input"),"absolute w-5 h-5 cursor-pointer left-0 top-0 opacity-0"),name:s,required:m,checked:f,onChange:e=>{e.preventDefault()}}),o.default.createElement($,{checked:f,onChange:e=>{y(e),null==l||l(e)},disabled:u,className:(0,w.tremorTwMerge)(N("switch"),"w-10 h-5 group relative inline-flex shrink-0 cursor-pointer items-center justify-center rounded-tremor-full","focus:outline-none",u?"cursor-not-allowed":""),onFocus:()=>x(!0),onBlur:()=>x(!1),id:b},o.default.createElement("span",{className:(0,w.tremorTwMerge)(N("sr-only"),"sr-only")},"Switch ",f?"on":"off"),o.default.createElement("span",{"aria-hidden":"true",className:(0,w.tremorTwMerge)(N("background"),f?h.bgColor:"bg-tremor-border dark:bg-dark-tremor-border","pointer-events-none absolute mx-auto h-3 w-9 rounded-tremor-full transition-colors duration-100 ease-in-out")}),o.default.createElement("span",{"aria-hidden":"true",className:(0,w.tremorTwMerge)(N("round"),f?(0,w.tremorTwMerge)(h.bgColor,"translate-x-5 border-tremor-background dark:border-dark-tremor-background"):"translate-x-0 bg-tremor-border dark:bg-dark-tremor-border border-tremor-background dark:border-dark-tremor-background","pointer-events-none absolute left-0 inline-block h-5 w-5 transform rounded-tremor-full border-2 shadow-tremor-input duration-100 ease-in-out transition",v?(0,w.tremorTwMerge)("ring-2",h.ringColor):"")}))),d&&c?o.default.createElement("p",{className:(0,w.tremorTwMerge)(N("errorMessage"),"text-sm text-red-500 mt-1 ")},c):null)});P.displayName="Switch",e.s(["Switch",0,P],793130)},688511,e=>{"use strict";var t=e.i(823429);e.s(["Edit",()=>t.default])},269638,e=>{"use strict";let t=(0,e.i(475254).default)("circle-check-big",[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]]);e.s(["CheckCircle",0,t],269638)},431343,569074,e=>{"use strict";var t=e.i(475254);let r=(0,t.default)("play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);e.s(["Play",0,r],431343);let n=(0,t.default)("upload",[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m17 8-5-5-5 5",key:"7q97r8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}]]);e.s(["Upload",0,n],569074)},883552,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(562901),n=e.i(343794),o=e.i(914949),a=e.i(529681),l=e.i(242064),i=e.i(829672),s=e.i(285781),d=e.i(836938),c=e.i(920228),u=e.i(62405),m=e.i(408850),g=e.i(87414),b=e.i(310730);let p=(0,e.i(246422).genStyleHooks)("Popconfirm",e=>(e=>{let{componentCls:t,iconCls:r,antCls:n,zIndexPopup:o,colorText:a,colorWarning:l,marginXXS:i,marginXS:s,fontSize:d,fontWeightStrong:c,colorTextHeading:u}=e;return{[t]:{zIndex:o,[`&${n}-popover`]:{fontSize:d},[`${t}-message`]:{marginBottom:s,display:"flex",flexWrap:"nowrap",alignItems:"start",[`> ${t}-message-icon ${r}`]:{color:l,fontSize:d,lineHeight:1,marginInlineEnd:s},[`${t}-title`]:{fontWeight:c,color:u,"&:only-child":{fontWeight:"normal"}},[`${t}-description`]:{marginTop:i,color:a}},[`${t}-buttons`]:{textAlign:"end",whiteSpace:"nowrap",button:{marginInlineStart:s}}}}})(e),e=>{let{zIndexPopupBase:t}=e;return{zIndexPopup:t+60}},{resetStyle:!1});var h=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let f=e=>{let{prefixCls:n,okButtonProps:o,cancelButtonProps:a,title:i,description:b,cancelText:p,okText:h,okType:f="primary",icon:y=t.createElement(r.default,null),showCancel:v=!0,close:x,onConfirm:O,onCancel:C,onPopupClick:$}=e,{getPrefixCls:j}=t.useContext(l.ConfigContext),[k]=(0,m.useLocale)("Popconfirm",g.default.Popconfirm),w=(0,d.getRenderPropValue)(i),S=(0,d.getRenderPropValue)(b);return t.createElement("div",{className:`${n}-inner-content`,onClick:$},t.createElement("div",{className:`${n}-message`},y&&t.createElement("span",{className:`${n}-message-icon`},y),t.createElement("div",{className:`${n}-message-text`},w&&t.createElement("div",{className:`${n}-title`},w),S&&t.createElement("div",{className:`${n}-description`},S))),t.createElement("div",{className:`${n}-buttons`},v&&t.createElement(c.default,Object.assign({onClick:C,size:"small"},a),p||(null==k?void 0:k.cancelText)),t.createElement(s.default,{buttonProps:Object.assign(Object.assign({size:"small"},(0,u.convertLegacyProps)(f)),o),actionFn:O,close:x,prefixCls:j("btn"),quitOnNullishReturnValue:!0,emitEvent:!0},h||(null==k?void 0:k.okText))))};var y=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let v=t.forwardRef((e,s)=>{var d,c;let{prefixCls:u,placement:m="top",trigger:g="click",okType:b="primary",icon:h=t.createElement(r.default,null),children:v,overlayClassName:x,onOpenChange:O,onVisibleChange:C,overlayStyle:$,styles:j,classNames:k}=e,w=y(e,["prefixCls","placement","trigger","okType","icon","children","overlayClassName","onOpenChange","onVisibleChange","overlayStyle","styles","classNames"]),{getPrefixCls:S,className:E,style:N,classNames:P,styles:M}=(0,l.useComponentConfig)("popconfirm"),[T,R]=(0,o.default)(!1,{value:null!=(d=e.open)?d:e.visible,defaultValue:null!=(c=e.defaultOpen)?c:e.defaultVisible}),z=(e,t)=>{R(e,!0),null==C||C(e),null==O||O(e,t)},B=S("popconfirm",u),I=(0,n.default)(B,E,x,P.root,null==k?void 0:k.root),L=(0,n.default)(P.body,null==k?void 0:k.body),[H]=p(B);return H(t.createElement(i.default,Object.assign({},(0,a.default)(w,["title"]),{trigger:g,placement:m,onOpenChange:(t,r)=>{let{disabled:n=!1}=e;n||z(t,r)},open:T,ref:s,classNames:{root:I,body:L},styles:{root:Object.assign(Object.assign(Object.assign(Object.assign({},M.root),N),$),null==j?void 0:j.root),body:Object.assign(Object.assign({},M.body),null==j?void 0:j.body)},content:t.createElement(f,Object.assign({okType:b,icon:h},e,{prefixCls:B,close:e=>{z(!1,e)},onConfirm:t=>{var r;return null==(r=e.onConfirm)?void 0:r.call(void 0,t)},onCancel:t=>{var r;z(!1,t),null==(r=e.onCancel)||r.call(void 0,t)}})),"data-popover-inject":!0}),v))});v._InternalPanelDoNotUseOrYouWillBeFired=e=>{let{prefixCls:r,placement:o,className:a,style:i}=e,s=h(e,["prefixCls","placement","className","style"]),{getPrefixCls:d}=t.useContext(l.ConfigContext),c=d("popconfirm",r),[u]=p(c);return u(t.createElement(b.default,{placement:o,className:(0,n.default)(c,a),style:i,content:t.createElement(f,Object.assign({prefixCls:c},s))}))},e.s(["Popconfirm",0,v],883552)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/036wlkuzplhfz.js b/litellm/proxy/_experimental/out/_next/static/chunks/036wlkuzplhfz.js new file mode 100644 index 00000000000..e090152ea51 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/036wlkuzplhfz.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,649222,(e,a,t)=>{e.e,e.r(166540).defineLocale("af",{months:"Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des".split("_"),weekdays:"Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag".split("_"),weekdaysShort:"Son_Maa_Din_Woe_Don_Vry_Sat".split("_"),weekdaysMin:"So_Ma_Di_Wo_Do_Vr_Sa".split("_"),meridiemParse:/vm|nm/i,isPM:function(e){return/^nm$/i.test(e)},meridiem:function(e,a,t){return e<12?t?"vm":"VM":t?"nm":"NM"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Vandag om] LT",nextDay:"[Môre om] LT",nextWeek:"dddd [om] LT",lastDay:"[Gister om] LT",lastWeek:"[Laas] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oor %s",past:"%s gelede",s:"'n paar sekondes",ss:"%d sekondes",m:"'n minuut",mm:"%d minute",h:"'n uur",hh:"%d ure",d:"'n dag",dd:"%d dae",M:"'n maand",MM:"%d maande",y:"'n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})},50997,(e,a,t)=>{e.e,function(e){"use strict";var a=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},t={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},s=function(e){return function(s,n,r,d){var i=a(s),_=t[e][a(s)];return 2===i&&(_=_[+!n]),_.replace(/%d/i,s)}},n=["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويلية","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"];e.defineLocale("ar-dz",{months:n,monthsShort:n,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,a,t){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:s("s"),ss:s("s"),m:s("m"),mm:s("m"),h:s("h"),hh:s("h"),d:s("d"),dd:s("d"),M:s("M"),MM:s("M"),y:s("y"),yy:s("y")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:0,doy:4}})}(e.r(166540))},818181,(e,a,t)=>{e.e,e.r(166540).defineLocale("ar-kw",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:0,doy:12}})},392472,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",0:"0"},t=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},s={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},n=function(e){return function(a,n,r,d){var i=t(a),_=s[e][t(a)];return 2===i&&(_=_[+!n]),_.replace(/%d/i,a)}},r=["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"];e.defineLocale("ar-ly",{months:r,monthsShort:r,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,a,t){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:n("s"),ss:n("s"),m:n("m"),mm:n("m"),h:n("h"),hh:n("h"),d:n("d"),dd:n("d"),M:n("M"),MM:n("M"),y:n("y"),yy:n("y")},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},week:{dow:6,doy:12}})}(e.r(166540))},48840,(e,a,t)=>{e.e,e.r(166540).defineLocale("ar-ma",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اثنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:1,doy:4}})},561871,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},t={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};e.defineLocale("ar-ps",{months:"كانون الثاني_شباط_آذار_نيسان_أيّار_حزيران_تمّوز_آب_أيلول_تشري الأوّل_تشرين الثاني_كانون الأوّل".split("_"),monthsShort:"ك٢_شباط_آذار_نيسان_أيّار_حزيران_تمّوز_آب_أيلول_ت١_ت٢_ك١".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,a,t){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(e){return e.replace(/[٣٤٥٦٧٨٩٠]/g,function(e){return t[e]}).split("").reverse().join("").replace(/[١٢](?![\u062a\u0643])/g,function(e){return t[e]}).split("").reverse().join("").replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},week:{dow:0,doy:6}})}(e.r(166540))},566848,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},t={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};e.defineLocale("ar-sa",{months:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,a,t){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(e){return t[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},week:{dow:0,doy:6}})}(e.r(166540))},892109,(e,a,t)=>{e.e,e.r(166540).defineLocale("ar-tn",{months:"جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:1,doy:4}})},617209,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},t={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},s=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},n={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},r=function(e){return function(a,t,r,d){var i=s(a),_=n[e][s(a)];return 2===i&&(_=_[+!t]),_.replace(/%d/i,a)}},d=["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"];e.defineLocale("ar",{months:d,monthsShort:d,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,a,t){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:r("s"),ss:r("s"),m:r("m"),mm:r("m"),h:r("h"),hh:r("h"),d:r("d"),dd:r("d"),M:r("M"),MM:r("M"),y:r("y"),yy:r("y")},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(e){return t[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},week:{dow:6,doy:12}})}(e.r(166540))},627551,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"-inci",5:"-inci",8:"-inci",70:"-inci",80:"-inci",2:"-nci",7:"-nci",20:"-nci",50:"-nci",3:"-üncü",4:"-üncü",100:"-üncü",6:"-ncı",9:"-uncu",10:"-uncu",30:"-uncu",60:"-ıncı",90:"-ıncı"};e.defineLocale("az",{months:"yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"),monthsShort:"yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"),weekdays:"Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə".split("_"),weekdaysShort:"Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən".split("_"),weekdaysMin:"Bz_BE_ÇA_Çə_CA_Cü_Şə".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[sabah saat] LT",nextWeek:"[gələn həftə] dddd [saat] LT",lastDay:"[dünən] LT",lastWeek:"[keçən həftə] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s əvvəl",s:"bir neçə saniyə",ss:"%d saniyə",m:"bir dəqiqə",mm:"%d dəqiqə",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir il",yy:"%d il"},meridiemParse:/gecə|səhər|gündüz|axşam/,isPM:function(e){return/^(gündüz|axşam)$/.test(e)},meridiem:function(e,a,t){return e<4?"gecə":e<12?"səhər":e<17?"gündüz":"axşam"},dayOfMonthOrdinalParse:/\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/,ordinal:function(e){if(0===e)return e+"-ıncı";var t=e%10;return e+(a[t]||a[e%100-t]||a[e>=100?100:null])},week:{dow:1,doy:7}})}(e.r(166540))},416502,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t){var s,n,r;return"m"===t?a?"хвіліна":"хвіліну":"h"===t?a?"гадзіна":"гадзіну":e+" "+(s=({ss:a?"секунда_секунды_секунд":"секунду_секунды_секунд",mm:a?"хвіліна_хвіліны_хвілін":"хвіліну_хвіліны_хвілін",hh:a?"гадзіна_гадзіны_гадзін":"гадзіну_гадзіны_гадзін",dd:"дзень_дні_дзён",MM:"месяц_месяцы_месяцаў",yy:"год_гады_гадоў"})[t],n=+e,r=s.split("_"),n%10==1&&n%100!=11?r[0]:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?r[1]:r[2])}e.defineLocale("be",{months:{format:"студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня".split("_"),standalone:"студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань".split("_")},monthsShort:"студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж".split("_"),weekdays:{format:"нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу".split("_"),standalone:"нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота".split("_"),isFormat:/\[ ?[Ууў] ?(?:мінулую|наступную)? ?\] ?dddd/},weekdaysShort:"нд_пн_ат_ср_чц_пт_сб".split("_"),weekdaysMin:"нд_пн_ат_ср_чц_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., HH:mm",LLLL:"dddd, D MMMM YYYY г., HH:mm"},calendar:{sameDay:"[Сёння ў] LT",nextDay:"[Заўтра ў] LT",lastDay:"[Учора ў] LT",nextWeek:function(){return"[У] dddd [ў] LT"},lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return"[У мінулую] dddd [ў] LT";case 1:case 2:case 4:return"[У мінулы] dddd [ў] LT"}},sameElse:"L"},relativeTime:{future:"праз %s",past:"%s таму",s:"некалькі секунд",m:a,mm:a,h:a,hh:a,d:"дзень",dd:a,M:"месяц",MM:a,y:"год",yy:a},meridiemParse:/ночы|раніцы|дня|вечара/,isPM:function(e){return/^(дня|вечара)$/.test(e)},meridiem:function(e,a,t){return e<4?"ночы":e<12?"раніцы":e<17?"дня":"вечара"},dayOfMonthOrdinalParse:/\d{1,2}-(і|ы|га)/,ordinal:function(e,a){switch(a){case"M":case"d":case"DDD":case"w":case"W":return(e%10==2||e%10==3)&&e%100!=12&&e%100!=13?e+"-і":e+"-ы";case"D":return e+"-га";default:return e}},week:{dow:1,doy:7}})}(e.r(166540))},231241,(e,a,t)=>{e.e,e.r(166540).defineLocale("bg",{months:"януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"),monthsShort:"яну_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"),weekdays:"неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"),weekdaysShort:"нед_пон_вто_сря_чет_пет_съб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Днес в] LT",nextDay:"[Утре в] LT",nextWeek:"dddd [в] LT",lastDay:"[Вчера в] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Миналата] dddd [в] LT";case 1:case 2:case 4:case 5:return"[Миналия] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"след %s",past:"преди %s",s:"няколко секунди",ss:"%d секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дена",w:"седмица",ww:"%d седмици",M:"месец",MM:"%d месеца",y:"година",yy:"%d години"},dayOfMonthOrdinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(e){var a=e%10,t=e%100;if(0===e)return e+"-ев";if(0===t)return e+"-ен";if(t>10&&t<20)return e+"-ти";if(1===a)return e+"-ви";if(2===a)return e+"-ри";else if(7===a||8===a)return e+"-ми";else return e+"-ти"},week:{dow:1,doy:7}})},909549,(e,a,t)=>{e.e,e.r(166540).defineLocale("bm",{months:"Zanwuyekalo_Fewuruyekalo_Marisikalo_Awirilikalo_Mɛkalo_Zuwɛnkalo_Zuluyekalo_Utikalo_Sɛtanburukalo_ɔkutɔburukalo_Nowanburukalo_Desanburukalo".split("_"),monthsShort:"Zan_Few_Mar_Awi_Mɛ_Zuw_Zul_Uti_Sɛt_ɔku_Now_Des".split("_"),weekdays:"Kari_Ntɛnɛn_Tarata_Araba_Alamisa_Juma_Sibiri".split("_"),weekdaysShort:"Kar_Ntɛ_Tar_Ara_Ala_Jum_Sib".split("_"),weekdaysMin:"Ka_Nt_Ta_Ar_Al_Ju_Si".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"MMMM [tile] D [san] YYYY",LLL:"MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm",LLLL:"dddd MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm"},calendar:{sameDay:"[Bi lɛrɛ] LT",nextDay:"[Sini lɛrɛ] LT",nextWeek:"dddd [don lɛrɛ] LT",lastDay:"[Kunu lɛrɛ] LT",lastWeek:"dddd [tɛmɛnen lɛrɛ] LT",sameElse:"L"},relativeTime:{future:"%s kɔnɔ",past:"a bɛ %s bɔ",s:"sanga dama dama",ss:"sekondi %d",m:"miniti kelen",mm:"miniti %d",h:"lɛrɛ kelen",hh:"lɛrɛ %d",d:"tile kelen",dd:"tile %d",M:"kalo kelen",MM:"kalo %d",y:"san kelen",yy:"san %d"},week:{dow:1,doy:4}})},939441,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"১",2:"২",3:"৩",4:"৪",5:"৫",6:"৬",7:"৭",8:"৮",9:"৯",0:"০"},t={"১":"1","২":"2","৩":"3","৪":"4","৫":"5","৬":"6","৭":"7","৮":"8","৯":"9","০":"0"};e.defineLocale("bn-bd",{months:"জানুয়ারি_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর".split("_"),monthsShort:"জানু_ফেব্রু_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্ট_অক্টো_নভে_ডিসে".split("_"),weekdays:"রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার".split("_"),weekdaysShort:"রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি".split("_"),weekdaysMin:"রবি_সোম_মঙ্গল_বুধ_বৃহ_শুক্র_শনি".split("_"),longDateFormat:{LT:"A h:mm সময়",LTS:"A h:mm:ss সময়",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm সময়",LLLL:"dddd, D MMMM YYYY, A h:mm সময়"},calendar:{sameDay:"[আজ] LT",nextDay:"[আগামীকাল] LT",nextWeek:"dddd, LT",lastDay:"[গতকাল] LT",lastWeek:"[গত] dddd, LT",sameElse:"L"},relativeTime:{future:"%s পরে",past:"%s আগে",s:"কয়েক সেকেন্ড",ss:"%d সেকেন্ড",m:"এক মিনিট",mm:"%d মিনিট",h:"এক ঘন্টা",hh:"%d ঘন্টা",d:"এক দিন",dd:"%d দিন",M:"এক মাস",MM:"%d মাস",y:"এক বছর",yy:"%d বছর"},preparse:function(e){return e.replace(/[১২৩৪৫৬৭৮৯০]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/রাত|ভোর|সকাল|দুপুর|বিকাল|সন্ধ্যা|রাত/,meridiemHour:function(e,a){if(12===e&&(e=0),"রাত"===a)return e<4?e:e+12;if("ভোর"===a)return e;if("সকাল"===a)return e;if("দুপুর"===a)return e>=3?e:e+12;if("বিকাল"===a)return e+12;else if("সন্ধ্যা"===a)return e+12},meridiem:function(e,a,t){if(e<4)return"রাত";if(e<6)return"ভোর";if(e<12)return"সকাল";if(e<15)return"দুপুর";if(e<18)return"বিকাল";else if(e<20)return"সন্ধ্যা";else return"রাত"},week:{dow:0,doy:6}})}(e.r(166540))},557613,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"১",2:"২",3:"৩",4:"৪",5:"৫",6:"৬",7:"৭",8:"৮",9:"৯",0:"০"},t={"১":"1","২":"2","৩":"3","৪":"4","৫":"5","৬":"6","৭":"7","৮":"8","৯":"9","০":"0"};e.defineLocale("bn",{months:"জানুয়ারি_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর".split("_"),monthsShort:"জানু_ফেব্রু_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্ট_অক্টো_নভে_ডিসে".split("_"),weekdays:"রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার".split("_"),weekdaysShort:"রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি".split("_"),weekdaysMin:"রবি_সোম_মঙ্গল_বুধ_বৃহ_শুক্র_শনি".split("_"),longDateFormat:{LT:"A h:mm সময়",LTS:"A h:mm:ss সময়",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm সময়",LLLL:"dddd, D MMMM YYYY, A h:mm সময়"},calendar:{sameDay:"[আজ] LT",nextDay:"[আগামীকাল] LT",nextWeek:"dddd, LT",lastDay:"[গতকাল] LT",lastWeek:"[গত] dddd, LT",sameElse:"L"},relativeTime:{future:"%s পরে",past:"%s আগে",s:"কয়েক সেকেন্ড",ss:"%d সেকেন্ড",m:"এক মিনিট",mm:"%d মিনিট",h:"এক ঘন্টা",hh:"%d ঘন্টা",d:"এক দিন",dd:"%d দিন",M:"এক মাস",MM:"%d মাস",y:"এক বছর",yy:"%d বছর"},preparse:function(e){return e.replace(/[১২৩৪৫৬৭৮৯০]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/রাত|সকাল|দুপুর|বিকাল|রাত/,meridiemHour:function(e,a){return(12===e&&(e=0),"রাত"===a&&e>=4||"দুপুর"===a&&e<5||"বিকাল"===a)?e+12:e},meridiem:function(e,a,t){return e<4?"রাত":e<10?"সকাল":e<17?"দুপুর":e<20?"বিকাল":"রাত"},week:{dow:0,doy:6}})}(e.r(166540))},447113,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"༡",2:"༢",3:"༣",4:"༤",5:"༥",6:"༦",7:"༧",8:"༨",9:"༩",0:"༠"},t={"༡":"1","༢":"2","༣":"3","༤":"4","༥":"5","༦":"6","༧":"7","༨":"8","༩":"9","༠":"0"};e.defineLocale("bo",{months:"ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ".split("_"),monthsShort:"ཟླ་1_ཟླ་2_ཟླ་3_ཟླ་4_ཟླ་5_ཟླ་6_ཟླ་7_ཟླ་8_ཟླ་9_ཟླ་10_ཟླ་11_ཟླ་12".split("_"),monthsShortRegex:/^(ཟླ་\d{1,2})/,monthsParseExact:!0,weekdays:"གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་".split("_"),weekdaysShort:"ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་".split("_"),weekdaysMin:"ཉི_ཟླ_མིག_ལྷག_ཕུར_སངས_སྤེན".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[དི་རིང] LT",nextDay:"[སང་ཉིན] LT",nextWeek:"[བདུན་ཕྲག་རྗེས་མ], LT",lastDay:"[ཁ་སང] LT",lastWeek:"[བདུན་ཕྲག་མཐའ་མ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ལ་",past:"%s སྔན་ལ",s:"ལམ་སང",ss:"%d སྐར་ཆ།",m:"སྐར་མ་གཅིག",mm:"%d སྐར་མ",h:"ཆུ་ཚོད་གཅིག",hh:"%d ཆུ་ཚོད",d:"ཉིན་གཅིག",dd:"%d ཉིན་",M:"ཟླ་བ་གཅིག",MM:"%d ཟླ་བ",y:"ལོ་གཅིག",yy:"%d ལོ"},preparse:function(e){return e.replace(/[༡༢༣༤༥༦༧༨༩༠]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/,meridiemHour:function(e,a){return(12===e&&(e=0),"མཚན་མོ"===a&&e>=4||"ཉིན་གུང"===a&&e<5||"དགོང་དག"===a)?e+12:e},meridiem:function(e,a,t){return e<4?"མཚན་མོ":e<10?"ཞོགས་ཀས":e<17?"ཉིན་གུང":e<20?"དགོང་དག":"མཚན་མོ"},week:{dow:0,doy:6}})}(e.r(166540))},964028,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t){var s,n,r;return e+" "+(s=({mm:"munutenn",MM:"miz",dd:"devezh"})[t],2===e?void 0===(r={m:"v",b:"v",d:"z"})[(n=s).charAt(0)]?n:r[n.charAt(0)]+n.substring(1):s)}var t=[/^gen/i,/^c[ʼ\']hwe/i,/^meu/i,/^ebr/i,/^mae/i,/^(mez|eve)/i,/^gou/i,/^eos/i,/^gwe/i,/^her/i,/^du/i,/^ker/i],s=/^(genver|c[ʼ\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu|gen|c[ʼ\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,n=[/^Su/i,/^Lu/i,/^Me([^r]|$)/i,/^Mer/i,/^Ya/i,/^Gw/i,/^Sa/i];e.defineLocale("br",{months:"Genver_Cʼhwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),monthsShort:"Gen_Cʼhwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),weekdays:"Sul_Lun_Meurzh_Mercʼher_Yaou_Gwener_Sadorn".split("_"),weekdaysShort:"Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),weekdaysMin:"Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),weekdaysParse:n,fullWeekdaysParse:[/^sul/i,/^lun/i,/^meurzh/i,/^merc[ʼ\']her/i,/^yaou/i,/^gwener/i,/^sadorn/i],shortWeekdaysParse:[/^Sul/i,/^Lun/i,/^Meu/i,/^Mer/i,/^Yao/i,/^Gwe/i,/^Sad/i],minWeekdaysParse:n,monthsRegex:s,monthsShortRegex:s,monthsStrictRegex:/^(genver|c[ʼ\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu)/i,monthsShortStrictRegex:/^(gen|c[ʼ\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [a viz] MMMM YYYY",LLL:"D [a viz] MMMM YYYY HH:mm",LLLL:"dddd, D [a viz] MMMM YYYY HH:mm"},calendar:{sameDay:"[Hiziv da] LT",nextDay:"[Warcʼhoazh da] LT",nextWeek:"dddd [da] LT",lastDay:"[Decʼh da] LT",lastWeek:"dddd [paset da] LT",sameElse:"L"},relativeTime:{future:"a-benn %s",past:"%s ʼzo",s:"un nebeud segondennoù",ss:"%d eilenn",m:"ur vunutenn",mm:a,h:"un eur",hh:"%d eur",d:"un devezh",dd:a,M:"ur miz",MM:a,y:"ur bloaz",yy:function(e){switch(function e(a){return a>9?e(a%10):a}(e)){case 1:case 3:case 4:case 5:case 9:return e+" bloaz";default:return e+" vloaz"}}},dayOfMonthOrdinalParse:/\d{1,2}(añ|vet)/,ordinal:function(e){return e+(1===e?"añ":"vet")},week:{dow:1,doy:4},meridiemParse:/a.m.|g.m./,isPM:function(e){return"g.m."===e},meridiem:function(e,a,t){return e<12?"a.m.":"g.m."}})}(e.r(166540))},529619,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t){var s=e+" ";switch(t){case"ss":return 1===e?s+="sekunda":2===e||3===e||4===e?s+="sekunde":s+="sekundi",s;case"mm":return 1===e?s+="minuta":2===e||3===e||4===e?s+="minute":s+="minuta",s;case"h":return"jedan sat";case"hh":return 1===e?s+="sat":2===e||3===e||4===e?s+="sata":s+="sati",s;case"dd":return 1===e?s+="dan":s+="dana",s;case"MM":return 1===e?s+="mjesec":2===e||3===e||4===e?s+="mjeseca":s+="mjeseci",s;case"yy":return 1===e?s+="godina":2===e||3===e||4===e?s+="godine":s+="godina",s}}e.defineLocale("bs",{months:"januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:a,m:function(e,a,t,s){if("m"===t)return a?"jedna minuta":s?"jednu minutu":"jedne minute"},mm:a,h:a,hh:a,d:"dan",dd:a,M:"mjesec",MM:a,y:"godinu",yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(e.r(166540))},586721,(e,a,t)=>{e.e,e.r(166540).defineLocale("ca",{months:{standalone:"gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),format:"de gener_de febrer_de març_d'abril_de maig_de juny_de juliol_d'agost_de setembre_d'octubre_de novembre_de desembre".split("_"),isFormat:/D[oD]?(\s)+MMMM/},monthsShort:"gen._febr._març_abr._maig_juny_jul._ag._set._oct._nov._des.".split("_"),monthsParseExact:!0,weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"dg_dl_dt_dc_dj_dv_ds".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [de] YYYY",ll:"D MMM YYYY",LLL:"D MMMM [de] YYYY [a les] H:mm",lll:"D MMM YYYY, H:mm",LLLL:"dddd D MMMM [de] YYYY [a les] H:mm",llll:"ddd D MMM YYYY, H:mm"},calendar:{sameDay:function(){return"[avui a "+(1!==this.hours()?"les":"la")+"] LT"},nextDay:function(){return"[demà a "+(1!==this.hours()?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(1!==this.hours()?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(1!==this.hours()?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(1!==this.hours()?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"d'aquí %s",past:"fa %s",s:"uns segons",ss:"%d segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},dayOfMonthOrdinalParse:/\d{1,2}(r|n|t|è|a)/,ordinal:function(e,a){var t=1===e?"r":2===e?"n":3===e?"r":4===e?"t":"è";return("w"===a||"W"===a)&&(t="a"),e+t},week:{dow:1,doy:4}})},586162,(e,a,t)=>{e.e,function(e){"use strict";var a=[/^led/i,/^úno/i,/^bře/i,/^dub/i,/^kvě/i,/^(čvn|červen$|června)/i,/^(čvc|červenec|července)/i,/^srp/i,/^zář/i,/^říj/i,/^lis/i,/^pro/i],t=/^(leden|únor|březen|duben|květen|červenec|července|červen|června|srpen|září|říjen|listopad|prosinec|led|úno|bře|dub|kvě|čvn|čvc|srp|zář|říj|lis|pro)/i;function s(e){return e>1&&e<5&&1!=~~(e/10)}function n(e,a,t,n){var r=e+" ";switch(t){case"s":return a||n?"pár sekund":"pár sekundami";case"ss":if(a||n)return r+(s(e)?"sekundy":"sekund");return r+"sekundami";case"m":return a?"minuta":n?"minutu":"minutou";case"mm":if(a||n)return r+(s(e)?"minuty":"minut");return r+"minutami";case"h":return a?"hodina":n?"hodinu":"hodinou";case"hh":if(a||n)return r+(s(e)?"hodiny":"hodin");return r+"hodinami";case"d":return a||n?"den":"dnem";case"dd":if(a||n)return r+(s(e)?"dny":"dní");return r+"dny";case"M":return a||n?"měsíc":"měsícem";case"MM":if(a||n)return r+(s(e)?"měsíce":"měsíců");return r+"měsíci";case"y":return a||n?"rok":"rokem";case"yy":if(a||n)return r+(s(e)?"roky":"let");return r+"lety"}}e.defineLocale("cs",{months:{standalone:"leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_"),format:"ledna_února_března_dubna_května_června_července_srpna_září_října_listopadu_prosince".split("_"),isFormat:/DD?[o.]?(\[[^\[\]]*\]|\s)+MMMM/},monthsShort:"led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_"),monthsRegex:t,monthsShortRegex:t,monthsStrictRegex:/^(leden|ledna|února|únor|březen|března|duben|dubna|květen|května|červenec|července|červen|června|srpen|srpna|září|říjen|října|listopadu|listopad|prosinec|prosince)/i,monthsShortStrictRegex:/^(led|úno|bře|dub|kvě|čvn|čvc|srp|zář|říj|lis|pro)/i,monthsParse:a,longMonthsParse:a,shortMonthsParse:a,weekdays:"neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_čt_pá_so".split("_"),weekdaysMin:"ne_po_út_st_čt_pá_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm",l:"D. M. YYYY"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zítra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:n,ss:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},745143,(e,a,t)=>{e.e,e.r(166540).defineLocale("cv",{months:"кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав".split("_"),monthsShort:"кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш".split("_"),weekdays:"вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун".split("_"),weekdaysShort:"выр_тун_ытл_юн_кӗҫ_эрн_шӑм".split("_"),weekdaysMin:"вр_тн_ыт_юн_кҫ_эр_шм".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]",LLL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm",LLLL:"dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm"},calendar:{sameDay:"[Паян] LT [сехетре]",nextDay:"[Ыран] LT [сехетре]",lastDay:"[Ӗнер] LT [сехетре]",nextWeek:"[Ҫитес] dddd LT [сехетре]",lastWeek:"[Иртнӗ] dddd LT [сехетре]",sameElse:"L"},relativeTime:{future:function(e){var a=/сехет$/i.exec(e)?"рен":/ҫул$/i.exec(e)?"тан":"ран";return e+a},past:"%s каялла",s:"пӗр-ик ҫеккунт",ss:"%d ҫеккунт",m:"пӗр минут",mm:"%d минут",h:"пӗр сехет",hh:"%d сехет",d:"пӗр кун",dd:"%d кун",M:"пӗр уйӑх",MM:"%d уйӑх",y:"пӗр ҫул",yy:"%d ҫул"},dayOfMonthOrdinalParse:/\d{1,2}-мӗш/,ordinal:"%d-мӗш",week:{dow:1,doy:7}})},608170,(e,a,t)=>{e.e,e.r(166540).defineLocale("cy",{months:"Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),monthsShort:"Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),weekdays:"Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),weekdaysShort:"Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),weekdaysMin:"Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Heddiw am] LT",nextDay:"[Yfory am] LT",nextWeek:"dddd [am] LT",lastDay:"[Ddoe am] LT",lastWeek:"dddd [diwethaf am] LT",sameElse:"L"},relativeTime:{future:"mewn %s",past:"%s yn ôl",s:"ychydig eiliadau",ss:"%d eiliad",m:"munud",mm:"%d munud",h:"awr",hh:"%d awr",d:"diwrnod",dd:"%d diwrnod",M:"mis",MM:"%d mis",y:"blwyddyn",yy:"%d flynedd"},dayOfMonthOrdinalParse:/\d{1,2}(fed|ain|af|il|ydd|ed|eg)/,ordinal:function(e){var a="";return e>20?a=40===e||50===e||60===e||80===e||100===e?"fed":"ain":e>0&&(a=["","af","il","ydd","ydd","ed","ed","ed","fed","fed","fed","eg","fed","eg","eg","fed","eg","eg","fed","eg","fed"][e]),e+a},week:{dow:1,doy:4}})},596740,(e,a,t)=>{e.e,e.r(166540).defineLocale("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd [d.] D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"på dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[i] dddd[s kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",ss:"%d sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})},346346,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return a?n[t][0]:n[t][1]}e.defineLocale("de-at",{months:"Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jän._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:a,mm:"%d Minuten",h:a,hh:"%d Stunden",d:a,dd:a,w:a,ww:"%d Wochen",M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},700088,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return a?n[t][0]:n[t][1]}e.defineLocale("de-ch",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:a,mm:"%d Minuten",h:a,hh:"%d Stunden",d:a,dd:a,w:a,ww:"%d Wochen",M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},486428,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return a?n[t][0]:n[t][1]}e.defineLocale("de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:a,mm:"%d Minuten",h:a,hh:"%d Stunden",d:a,dd:a,w:a,ww:"%d Wochen",M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},31113,(e,a,t)=>{e.e,function(e){"use strict";var a=["ޖެނުއަރީ","ފެބްރުއަރީ","މާރިޗު","އޭޕްރީލު","މޭ","ޖޫން","ޖުލައި","އޯގަސްޓު","ސެޕްޓެމްބަރު","އޮކްޓޯބަރު","ނޮވެމްބަރު","ޑިސެމްބަރު"],t=["އާދިއްތަ","ހޯމަ","އަންގާރަ","ބުދަ","ބުރާސްފަތި","ހުކުރު","ހޮނިހިރު"];e.defineLocale("dv",{months:a,monthsShort:a,weekdays:t,weekdaysShort:t,weekdaysMin:"އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/M/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/މކ|މފ/,isPM:function(e){return"މފ"===e},meridiem:function(e,a,t){return e<12?"މކ":"މފ"},calendar:{sameDay:"[މިއަދު] LT",nextDay:"[މާދަމާ] LT",nextWeek:"dddd LT",lastDay:"[އިއްޔެ] LT",lastWeek:"[ފާއިތުވި] dddd LT",sameElse:"L"},relativeTime:{future:"ތެރޭގައި %s",past:"ކުރިން %s",s:"ސިކުންތުކޮޅެއް",ss:"d% ސިކުންތު",m:"މިނިޓެއް",mm:"މިނިޓު %d",h:"ގަޑިއިރެއް",hh:"ގަޑިއިރު %d",d:"ދުވަހެއް",dd:"ދުވަސް %d",M:"މަހެއް",MM:"މަސް %d",y:"އަހަރެއް",yy:"އަހަރު %d"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:7,doy:12}})}(e.r(166540))},550841,(e,a,t)=>{e.e,e.r(166540).defineLocale("el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(e,a){return e?"string"==typeof a&&/D/.test(a.substring(0,a.indexOf("MMMM")))?this._monthsGenitiveEl[e.month()]:this._monthsNominativeEl[e.month()]:this._monthsNominativeEl},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(e,a,t){return e>11?t?"μμ":"ΜΜ":t?"πμ":"ΠΜ"},isPM:function(e){return"μ"===(e+"").toLowerCase()[0]},meridiemParse:/[ΠΜ]\.?Μ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){return 6===this.day()?"[το προηγούμενο] dddd [{}] LT":"[την προηγούμενη] dddd [{}] LT"},sameElse:"L"},calendar:function(e,a){var t,s=this._calendarEl[e],n=a&&a.hours();return t=s,("u">typeof Function&&t instanceof Function||"[object Function]"===Object.prototype.toString.call(t))&&(s=s.apply(a)),s.replace("{}",n%12==1?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"λίγα δευτερόλεπτα",ss:"%d δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},dayOfMonthOrdinalParse:/\d{1,2}η/,ordinal:"%dη",week:{dow:1,doy:4}})},884432,(e,a,t)=>{e.e,e.r(166540).defineLocale("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:0,doy:4}})},448736,(e,a,t)=>{e.e,e.r(166540).defineLocale("en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"YYYY-MM-DD",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t}})},828502,(e,a,t)=>{e.e,e.r(166540).defineLocale("en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}})},421205,(e,a,t)=>{e.e,e.r(166540).defineLocale("en-ie",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}})},621015,(e,a,t)=>{e.e,e.r(166540).defineLocale("en-il",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t}})},162743,(e,a,t)=>{e.e,e.r(166540).defineLocale("en-in",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:0,doy:6}})},370661,(e,a,t)=>{e.e,e.r(166540).defineLocale("en-nz",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}})},113826,(e,a,t)=>{e.e,e.r(166540).defineLocale("en-sg",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}})},633517,(e,a,t)=>{e.e,e.r(166540).defineLocale("eo",{months:"januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro".split("_"),monthsShort:"jan_feb_mart_apr_maj_jun_jul_aŭg_sept_okt_nov_dec".split("_"),weekdays:"dimanĉo_lundo_mardo_merkredo_ĵaŭdo_vendredo_sabato".split("_"),weekdaysShort:"dim_lun_mard_merk_ĵaŭ_ven_sab".split("_"),weekdaysMin:"di_lu_ma_me_ĵa_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"[la] D[-an de] MMMM, YYYY",LLL:"[la] D[-an de] MMMM, YYYY HH:mm",LLLL:"dddd[n], [la] D[-an de] MMMM, YYYY HH:mm",llll:"ddd, [la] D[-an de] MMM, YYYY HH:mm"},meridiemParse:/[ap]\.t\.m/i,isPM:function(e){return"p"===e.charAt(0).toLowerCase()},meridiem:function(e,a,t){return e>11?t?"p.t.m.":"P.T.M.":t?"a.t.m.":"A.T.M."},calendar:{sameDay:"[Hodiaŭ je] LT",nextDay:"[Morgaŭ je] LT",nextWeek:"dddd[n je] LT",lastDay:"[Hieraŭ je] LT",lastWeek:"[pasintan] dddd[n je] LT",sameElse:"L"},relativeTime:{future:"post %s",past:"antaŭ %s",s:"kelkaj sekundoj",ss:"%d sekundoj",m:"unu minuto",mm:"%d minutoj",h:"unu horo",hh:"%d horoj",d:"unu tago",dd:"%d tagoj",M:"unu monato",MM:"%d monatoj",y:"unu jaro",yy:"%d jaroj"},dayOfMonthOrdinalParse:/\d{1,2}a/,ordinal:"%da",week:{dow:1,doy:7}})},954e3,(e,a,t)=>{e.e,function(e){"use strict";var a="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),t="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),s=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],n=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;e.defineLocale("es-do",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,s){return e?/-MMM-/.test(s)?t[e.month()]:a[e.month()]:a},monthsRegex:n,monthsShortRegex:n,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:s,longMonthsParse:s,shortMonthsParse:s,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})}(e.r(166540))},120137,(e,a,t)=>{e.e,function(e){"use strict";var a="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),t="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),s=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],n=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;e.defineLocale("es-mx",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,s){return e?/-MMM-/.test(s)?t[e.month()]:a[e.month()]:a},monthsRegex:n,monthsShortRegex:n,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:s,longMonthsParse:s,shortMonthsParse:s,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:0,doy:4},invalidDate:"Fecha inválida"})}(e.r(166540))},528845,(e,a,t)=>{e.e,function(e){"use strict";var a="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),t="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),s=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],n=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;e.defineLocale("es-us",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,s){return e?/-MMM-/.test(s)?t[e.month()]:a[e.month()]:a},monthsRegex:n,monthsShortRegex:n,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:s,longMonthsParse:s,shortMonthsParse:s,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"MM/DD/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:0,doy:6}})}(e.r(166540))},753818,(e,a,t)=>{e.e,function(e){"use strict";var a="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),t="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),s=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],n=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;e.defineLocale("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,s){return e?/-MMM-/.test(s)?t[e.month()]:a[e.month()]:a},monthsRegex:n,monthsShortRegex:n,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:s,longMonthsParse:s,shortMonthsParse:s,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4},invalidDate:"Fecha inválida"})}(e.r(166540))},54306,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={s:["mõne sekundi","mõni sekund","paar sekundit"],ss:[e+"sekundi",e+"sekundit"],m:["ühe minuti","üks minut"],mm:[e+" minuti",e+" minutit"],h:["ühe tunni","tund aega","üks tund"],hh:[e+" tunni",e+" tundi"],d:["ühe päeva","üks päev"],M:["kuu aja","kuu aega","üks kuu"],MM:[e+" kuu",e+" kuud"],y:["ühe aasta","aasta","üks aasta"],yy:[e+" aasta",e+" aastat"]};return a?n[t][2]?n[t][2]:n[t][1]:s?n[t][0]:n[t][1]}e.defineLocale("et",{months:"jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),monthsShort:"jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),weekdays:"pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev".split("_"),weekdaysShort:"P_E_T_K_N_R_L".split("_"),weekdaysMin:"P_E_T_K_N_R_L".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[Täna,] LT",nextDay:"[Homme,] LT",nextWeek:"[Järgmine] dddd LT",lastDay:"[Eile,] LT",lastWeek:"[Eelmine] dddd LT",sameElse:"L"},relativeTime:{future:"%s pärast",past:"%s tagasi",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:"%d päeva",M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},430810,(e,a,t)=>{e.e,e.r(166540).defineLocale("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),monthsParseExact:!0,weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] HH:mm",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] HH:mm",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] HH:mm",llll:"ddd, YYYY[ko] MMM D[a] HH:mm"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",ss:"%d segundo",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat",dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})},374902,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"۱",2:"۲",3:"۳",4:"۴",5:"۵",6:"۶",7:"۷",8:"۸",9:"۹",0:"۰"},t={"۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","۰":"0"};e.defineLocale("fa",{months:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ی_د_س_چ_پ_ج_ش".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/قبل از ظهر|بعد از ظهر/,isPM:function(e){return/بعد از ظهر/.test(e)},meridiem:function(e,a,t){return e<12?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[فردا ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چند ثانیه",ss:"%d ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(e){return e.replace(/[۰-۹]/g,function(e){return t[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},dayOfMonthOrdinalParse:/\d{1,2}م/,ordinal:"%dم",week:{dow:6,doy:12}})}(e.r(166540))},412450,(e,a,t)=>{e.e,function(e){"use strict";var a="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),t=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",a[7],a[8],a[9]];function s(e,s,n,r){var d,i,_="";switch(n){case"s":return r?"muutaman sekunnin":"muutama sekunti";case"ss":_=r?"sekunnin":"sekuntia";break;case"m":return r?"minuutin":"minuutti";case"mm":_=r?"minuutin":"minuuttia";break;case"h":return r?"tunnin":"tunti";case"hh":_=r?"tunnin":"tuntia";break;case"d":return r?"päivän":"päivä";case"dd":_=r?"päivän":"päivää";break;case"M":return r?"kuukauden":"kuukausi";case"MM":_=r?"kuukauden":"kuukautta";break;case"y":return r?"vuoden":"vuosi";case"yy":_=r?"vuoden":"vuotta"}return d=e,i=r,(d<10?i?t[d]:a[d]:d)+" "+_}e.defineLocale("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] HH.mm",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] HH.mm",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] HH.mm",llll:"ddd, Do MMM YYYY, [klo] HH.mm"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:s,ss:s,m:s,mm:s,h:s,hh:s,d:s,dd:s,M:s,MM:s,y:s,yy:s},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},321329,(e,a,t)=>{e.e,e.r(166540).defineLocale("fil",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"LT [ngayong araw]",nextDay:"[Bukas ng] LT",nextWeek:"LT [sa susunod na] dddd",lastDay:"LT [kahapon]",lastWeek:"LT [noong nakaraang] dddd",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",ss:"%d segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})},473679,(e,a,t)=>{e.e,e.r(166540).defineLocale("fo",{months:"januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur".split("_"),weekdaysShort:"sun_mán_týs_mik_hós_frí_ley".split("_"),weekdaysMin:"su_má_tý_mi_hó_fr_le".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D. MMMM, YYYY HH:mm"},calendar:{sameDay:"[Í dag kl.] LT",nextDay:"[Í morgin kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[Í gjár kl.] LT",lastWeek:"[síðstu] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"um %s",past:"%s síðani",s:"fá sekund",ss:"%d sekundir",m:"ein minuttur",mm:"%d minuttir",h:"ein tími",hh:"%d tímar",d:"ein dagur",dd:"%d dagar",M:"ein mánaður",MM:"%d mánaðir",y:"eitt ár",yy:"%d ár"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})},874573,(e,a,t)=>{e.e,e.r(166540).defineLocale("fr-ca",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd’hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|e)/,ordinal:function(e,a){switch(a){default:case"M":case"Q":case"D":case"DDD":case"d":return e+(1===e?"er":"e");case"w":case"W":return e+(1===e?"re":"e")}}})},639994,(e,a,t)=>{e.e,e.r(166540).defineLocale("fr-ch",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd’hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|e)/,ordinal:function(e,a){switch(a){default:case"M":case"Q":case"D":case"DDD":case"d":return e+(1===e?"er":"e");case"w":case"W":return e+(1===e?"re":"e")}},week:{dow:1,doy:4}})},618184,(e,a,t)=>{e.e,function(e){"use strict";var a=/(janv\.?|févr\.?|mars|avr\.?|mai|juin|juil\.?|août|sept\.?|oct\.?|nov\.?|déc\.?|janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre)/i,t=[/^janv/i,/^févr/i,/^mars/i,/^avr/i,/^mai/i,/^juin/i,/^juil/i,/^août/i,/^sept/i,/^oct/i,/^nov/i,/^déc/i];e.defineLocale("fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsRegex:a,monthsShortRegex:a,monthsStrictRegex:/^(janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre)/i,monthsShortStrictRegex:/(janv\.?|févr\.?|mars|avr\.?|mai|juin|juil\.?|août|sept\.?|oct\.?|nov\.?|déc\.?)/i,monthsParse:t,longMonthsParse:t,shortMonthsParse:t,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd’hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",w:"une semaine",ww:"%d semaines",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|)/,ordinal:function(e,a){switch(a){case"D":return e+(1===e?"er":"");default:case"M":case"Q":case"DDD":case"d":return e+(1===e?"er":"e");case"w":case"W":return e+(1===e?"re":"e")}},week:{dow:1,doy:4}})}(e.r(166540))},439552,(e,a,t)=>{e.e,function(e){"use strict";var a="jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.".split("_"),t="jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_");e.defineLocale("fy",{months:"jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber".split("_"),monthsShort:function(e,s){return e?/-MMM-/.test(s)?t[e.month()]:a[e.month()]:a},monthsParseExact:!0,weekdays:"snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon".split("_"),weekdaysShort:"si._mo._ti._wo._to._fr._so.".split("_"),weekdaysMin:"Si_Mo_Ti_Wo_To_Fr_So".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[hjoed om] LT",nextDay:"[moarn om] LT",nextWeek:"dddd [om] LT",lastDay:"[juster om] LT",lastWeek:"[ôfrûne] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oer %s",past:"%s lyn",s:"in pear sekonden",ss:"%d sekonden",m:"ien minút",mm:"%d minuten",h:"ien oere",hh:"%d oeren",d:"ien dei",dd:"%d dagen",M:"ien moanne",MM:"%d moannen",y:"ien jier",yy:"%d jierren"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(e.r(166540))},866284,(e,a,t)=>{e.e,e.r(166540).defineLocale("ga",{months:["Eanáir","Feabhra","Márta","Aibreán","Bealtaine","Meitheamh","Iúil","Lúnasa","Meán Fómhair","Deireadh Fómhair","Samhain","Nollaig"],monthsShort:["Ean","Feabh","Márt","Aib","Beal","Meith","Iúil","Lún","M.F.","D.F.","Samh","Noll"],monthsParseExact:!0,weekdays:["Dé Domhnaigh","Dé Luain","Dé Máirt","Dé Céadaoin","Déardaoin","Dé hAoine","Dé Sathairn"],weekdaysShort:["Domh","Luan","Máirt","Céad","Déar","Aoine","Sath"],weekdaysMin:["Do","Lu","Má","Cé","Dé","A","Sa"],longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Inniu ag] LT",nextDay:"[Amárach ag] LT",nextWeek:"dddd [ag] LT",lastDay:"[Inné ag] LT",lastWeek:"dddd [seo caite] [ag] LT",sameElse:"L"},relativeTime:{future:"i %s",past:"%s ó shin",s:"cúpla soicind",ss:"%d soicind",m:"nóiméad",mm:"%d nóiméad",h:"uair an chloig",hh:"%d uair an chloig",d:"lá",dd:"%d lá",M:"mí",MM:"%d míonna",y:"bliain",yy:"%d bliain"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){return e+(1===e?"d":e%10==2?"na":"mh")},week:{dow:1,doy:4}})},810136,(e,a,t)=>{e.e,e.r(166540).defineLocale("gd",{months:["Am Faoilleach","An Gearran","Am Màrt","An Giblean","An Cèitean","An t-Ògmhios","An t-Iuchar","An Lùnastal","An t-Sultain","An Dàmhair","An t-Samhain","An Dùbhlachd"],monthsShort:["Faoi","Gear","Màrt","Gibl","Cèit","Ògmh","Iuch","Lùn","Sult","Dàmh","Samh","Dùbh"],monthsParseExact:!0,weekdays:["Didòmhnaich","Diluain","Dimàirt","Diciadain","Diardaoin","Dihaoine","Disathairne"],weekdaysShort:["Did","Dil","Dim","Dic","Dia","Dih","Dis"],weekdaysMin:["Dò","Lu","Mà","Ci","Ar","Ha","Sa"],longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[An-diugh aig] LT",nextDay:"[A-màireach aig] LT",nextWeek:"dddd [aig] LT",lastDay:"[An-dè aig] LT",lastWeek:"dddd [seo chaidh] [aig] LT",sameElse:"L"},relativeTime:{future:"ann an %s",past:"bho chionn %s",s:"beagan diogan",ss:"%d diogan",m:"mionaid",mm:"%d mionaidean",h:"uair",hh:"%d uairean",d:"latha",dd:"%d latha",M:"mìos",MM:"%d mìosan",y:"bliadhna",yy:"%d bliadhna"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){return e+(1===e?"d":e%10==2?"na":"mh")},week:{dow:1,doy:4}})},703131,(e,a,t)=>{e.e,e.r(166540).defineLocale("gl",{months:"xaneiro_febreiro_marzo_abril_maio_xuño_xullo_agosto_setembro_outubro_novembro_decembro".split("_"),monthsShort:"xan._feb._mar._abr._mai._xuñ._xul._ago._set._out._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"domingo_luns_martes_mércores_xoves_venres_sábado".split("_"),weekdaysShort:"dom._lun._mar._mér._xov._ven._sáb.".split("_"),weekdaysMin:"do_lu_ma_mé_xo_ve_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoxe "+(1!==this.hours()?"ás":"á")+"] LT"},nextDay:function(){return"[mañá "+(1!==this.hours()?"ás":"á")+"] LT"},nextWeek:function(){return"dddd ["+(1!==this.hours()?"ás":"a")+"] LT"},lastDay:function(){return"[onte "+(1!==this.hours()?"á":"a")+"] LT"},lastWeek:function(){return"[o] dddd [pasado "+(1!==this.hours()?"ás":"a")+"] LT"},sameElse:"L"},relativeTime:{future:function(e){return 0===e.indexOf("un")?"n"+e:"en "+e},past:"hai %s",s:"uns segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"unha hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})},56861,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={s:["थोडया सॅकंडांनी","थोडे सॅकंड"],ss:[e+" सॅकंडांनी",e+" सॅकंड"],m:["एका मिणटान","एक मिनूट"],mm:[e+" मिणटांनी",e+" मिणटां"],h:["एका वरान","एक वर"],hh:[e+" वरांनी",e+" वरां"],d:["एका दिसान","एक दीस"],dd:[e+" दिसांनी",e+" दीस"],M:["एका म्हयन्यान","एक म्हयनो"],MM:[e+" म्हयन्यानी",e+" म्हयने"],y:["एका वर्सान","एक वर्स"],yy:[e+" वर्सांनी",e+" वर्सां"]};return s?n[t][0]:n[t][1]}e.defineLocale("gom-deva",{months:{standalone:"जानेवारी_फेब्रुवारी_मार्च_एप्रील_मे_जून_जुलय_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर".split("_"),format:"जानेवारीच्या_फेब्रुवारीच्या_मार्चाच्या_एप्रीलाच्या_मेयाच्या_जूनाच्या_जुलयाच्या_ऑगस्टाच्या_सप्टेंबराच्या_ऑक्टोबराच्या_नोव्हेंबराच्या_डिसेंबराच्या".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"जाने._फेब्रु._मार्च_एप्री._मे_जून_जुल._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.".split("_"),monthsParseExact:!0,weekdays:"आयतार_सोमार_मंगळार_बुधवार_बिरेस्तार_सुक्रार_शेनवार".split("_"),weekdaysShort:"आयत._सोम._मंगळ._बुध._ब्रेस्त._सुक्र._शेन.".split("_"),weekdaysMin:"आ_सो_मं_बु_ब्रे_सु_शे".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"A h:mm [वाजतां]",LTS:"A h:mm:ss [वाजतां]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [वाजतां]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [वाजतां]",llll:"ddd, D MMM YYYY, A h:mm [वाजतां]"},calendar:{sameDay:"[आयज] LT",nextDay:"[फाल्यां] LT",nextWeek:"[फुडलो] dddd[,] LT",lastDay:"[काल] LT",lastWeek:"[फाटलो] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s आदीं",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}(वेर)/,ordinal:function(e,a){return"D"===a?e+"वेर":e},week:{dow:0,doy:3},meridiemParse:/राती|सकाळीं|दनपारां|सांजे/,meridiemHour:function(e,a){return(12===e&&(e=0),"राती"===a)?e<4?e:e+12:"सकाळीं"===a?e:"दनपारां"===a?e>12?e:e+12:"सांजे"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"राती":e<12?"सकाळीं":e<16?"दनपारां":e<20?"सांजे":"राती"}})}(e.r(166540))},227159,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={s:["thoddea sekondamni","thodde sekond"],ss:[e+" sekondamni",e+" sekond"],m:["eka mintan","ek minut"],mm:[e+" mintamni",e+" mintam"],h:["eka voran","ek vor"],hh:[e+" voramni",e+" voram"],d:["eka disan","ek dis"],dd:[e+" disamni",e+" dis"],M:["eka mhoinean","ek mhoino"],MM:[e+" mhoineamni",e+" mhoine"],y:["eka vorsan","ek voros"],yy:[e+" vorsamni",e+" vorsam"]};return s?n[t][0]:n[t][1]}e.defineLocale("gom-latn",{months:{standalone:"Janer_Febrer_Mars_Abril_Mai_Jun_Julai_Agost_Setembr_Otubr_Novembr_Dezembr".split("_"),format:"Janerachea_Febrerachea_Marsachea_Abrilachea_Maiachea_Junachea_Julaiachea_Agostachea_Setembrachea_Otubrachea_Novembrachea_Dezembrachea".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"Jan._Feb._Mars_Abr._Mai_Jun_Jul._Ago._Set._Otu._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Aitar_Somar_Mongllar_Budhvar_Birestar_Sukrar_Son'var".split("_"),weekdaysShort:"Ait._Som._Mon._Bud._Bre._Suk._Son.".split("_"),weekdaysMin:"Ai_Sm_Mo_Bu_Br_Su_Sn".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"A h:mm [vazta]",LTS:"A h:mm:ss [vazta]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [vazta]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [vazta]",llll:"ddd, D MMM YYYY, A h:mm [vazta]"},calendar:{sameDay:"[Aiz] LT",nextDay:"[Faleam] LT",nextWeek:"[Fuddlo] dddd[,] LT",lastDay:"[Kal] LT",lastWeek:"[Fattlo] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s adim",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}(er)/,ordinal:function(e,a){return"D"===a?e+"er":e},week:{dow:0,doy:3},meridiemParse:/rati|sokallim|donparam|sanje/,meridiemHour:function(e,a){return(12===e&&(e=0),"rati"===a)?e<4?e:e+12:"sokallim"===a?e:"donparam"===a?e>12?e:e+12:"sanje"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"rati":e<12?"sokallim":e<16?"donparam":e<20?"sanje":"rati"}})}(e.r(166540))},277496,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"૧",2:"૨",3:"૩",4:"૪",5:"૫",6:"૬",7:"૭",8:"૮",9:"૯",0:"૦"},t={"૧":"1","૨":"2","૩":"3","૪":"4","૫":"5","૬":"6","૭":"7","૮":"8","૯":"9","૦":"0"};e.defineLocale("gu",{months:"જાન્યુઆરી_ફેબ્રુઆરી_માર્ચ_એપ્રિલ_મે_જૂન_જુલાઈ_ઑગસ્ટ_સપ્ટેમ્બર_ઑક્ટ્બર_નવેમ્બર_ડિસેમ્બર".split("_"),monthsShort:"જાન્યુ._ફેબ્રુ._માર્ચ_એપ્રિ._મે_જૂન_જુલા._ઑગ._સપ્ટે._ઑક્ટ્._નવે._ડિસે.".split("_"),monthsParseExact:!0,weekdays:"રવિવાર_સોમવાર_મંગળવાર_બુધ્વાર_ગુરુવાર_શુક્રવાર_શનિવાર".split("_"),weekdaysShort:"રવિ_સોમ_મંગળ_બુધ્_ગુરુ_શુક્ર_શનિ".split("_"),weekdaysMin:"ર_સો_મં_બુ_ગુ_શુ_શ".split("_"),longDateFormat:{LT:"A h:mm વાગ્યે",LTS:"A h:mm:ss વાગ્યે",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm વાગ્યે",LLLL:"dddd, D MMMM YYYY, A h:mm વાગ્યે"},calendar:{sameDay:"[આજ] LT",nextDay:"[કાલે] LT",nextWeek:"dddd, LT",lastDay:"[ગઇકાલે] LT",lastWeek:"[પાછલા] dddd, LT",sameElse:"L"},relativeTime:{future:"%s મા",past:"%s પહેલા",s:"અમુક પળો",ss:"%d સેકંડ",m:"એક મિનિટ",mm:"%d મિનિટ",h:"એક કલાક",hh:"%d કલાક",d:"એક દિવસ",dd:"%d દિવસ",M:"એક મહિનો",MM:"%d મહિનો",y:"એક વર્ષ",yy:"%d વર્ષ"},preparse:function(e){return e.replace(/[૧૨૩૪૫૬૭૮૯૦]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/રાત|બપોર|સવાર|સાંજ/,meridiemHour:function(e,a){return(12===e&&(e=0),"રાત"===a)?e<4?e:e+12:"સવાર"===a?e:"બપોર"===a?e>=10?e:e+12:"સાંજ"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"રાત":e<10?"સવાર":e<17?"બપોર":e<20?"સાંજ":"રાત"},week:{dow:0,doy:6}})}(e.r(166540))},796669,(e,a,t)=>{e.e,e.r(166540).defineLocale("he",{months:"ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר".split("_"),monthsShort:"ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳".split("_"),weekdays:"ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת".split("_"),weekdaysShort:"א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"א_ב_ג_ד_ה_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY HH:mm",LLLL:"dddd, D [ב]MMMM YYYY HH:mm",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[היום ב־]LT",nextDay:"[מחר ב־]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[אתמול ב־]LT",lastWeek:"[ביום] dddd [האחרון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"לפני %s",s:"מספר שניות",ss:"%d שניות",m:"דקה",mm:"%d דקות",h:"שעה",hh:function(e){return 2===e?"שעתיים":e+" שעות"},d:"יום",dd:function(e){return 2===e?"יומיים":e+" ימים"},M:"חודש",MM:function(e){return 2===e?"חודשיים":e+" חודשים"},y:"שנה",yy:function(e){return 2===e?"שנתיים":e%10==0&&10!==e?e+" שנה":e+" שנים"}},meridiemParse:/אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i,isPM:function(e){return/^(אחה"צ|אחרי הצהריים|בערב)$/.test(e)},meridiem:function(e,a,t){return e<5?"לפנות בוקר":e<10?"בבוקר":e<12?t?'לפנה"צ':"לפני הצהריים":e<18?t?'אחה"צ':"אחרי הצהריים":"בערב"}})},725949,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},t={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},s=[/^जन/i,/^फ़र|फर/i,/^मार्च/i,/^अप्रै/i,/^मई/i,/^जून/i,/^जुल/i,/^अग/i,/^सितं|सित/i,/^अक्टू/i,/^नव|नवं/i,/^दिसं|दिस/i];e.defineLocale("hi",{months:{format:"जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर".split("_"),standalone:"जनवरी_फरवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितंबर_अक्टूबर_नवंबर_दिसंबर".split("_")},monthsShort:"जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.".split("_"),weekdays:"रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm बजे",LTS:"A h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm बजे",LLLL:"dddd, D MMMM YYYY, A h:mm बजे"},monthsParse:s,longMonthsParse:s,shortMonthsParse:[/^जन/i,/^फ़र/i,/^मार्च/i,/^अप्रै/i,/^मई/i,/^जून/i,/^जुल/i,/^अग/i,/^सित/i,/^अक्टू/i,/^नव/i,/^दिस/i],monthsRegex:/^(जनवरी|जन\.?|फ़रवरी|फरवरी|फ़र\.?|मार्च?|अप्रैल|अप्रै\.?|मई?|जून?|जुलाई|जुल\.?|अगस्त|अग\.?|सितम्बर|सितंबर|सित\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर|नव\.?|दिसम्बर|दिसंबर|दिस\.?)/i,monthsShortRegex:/^(जनवरी|जन\.?|फ़रवरी|फरवरी|फ़र\.?|मार्च?|अप्रैल|अप्रै\.?|मई?|जून?|जुलाई|जुल\.?|अगस्त|अग\.?|सितम्बर|सितंबर|सित\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर|नव\.?|दिसम्बर|दिसंबर|दिस\.?)/i,monthsStrictRegex:/^(जनवरी?|फ़रवरी|फरवरी?|मार्च?|अप्रैल?|मई?|जून?|जुलाई?|अगस्त?|सितम्बर|सितंबर|सित?\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर?|दिसम्बर|दिसंबर?)/i,monthsShortStrictRegex:/^(जन\.?|फ़र\.?|मार्च?|अप्रै\.?|मई?|जून?|जुल\.?|अग\.?|सित\.?|अक्टू\.?|नव\.?|दिस\.?)/i,calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कुछ ही क्षण",ss:"%d सेकंड",m:"एक मिनट",mm:"%d मिनट",h:"एक घंटा",hh:"%d घंटे",d:"एक दिन",dd:"%d दिन",M:"एक महीने",MM:"%d महीने",y:"एक वर्ष",yy:"%d वर्ष"},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/रात|सुबह|दोपहर|शाम/,meridiemHour:function(e,a){return(12===e&&(e=0),"रात"===a)?e<4?e:e+12:"सुबह"===a?e:"दोपहर"===a?e>=10?e:e+12:"शाम"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"रात":e<10?"सुबह":e<17?"दोपहर":e<20?"शाम":"रात"},week:{dow:0,doy:6}})}(e.r(166540))},863164,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t){var s=e+" ";switch(t){case"ss":return 1===e?s+="sekunda":2===e||3===e||4===e?s+="sekunde":s+="sekundi",s;case"m":return a?"jedna minuta":"jedne minute";case"mm":return 1===e?s+="minuta":2===e||3===e||4===e?s+="minute":s+="minuta",s;case"h":return a?"jedan sat":"jednog sata";case"hh":return 1===e?s+="sat":2===e||3===e||4===e?s+="sata":s+="sati",s;case"dd":return 1===e?s+="dan":s+="dana",s;case"MM":return 1===e?s+="mjesec":2===e||3===e||4===e?s+="mjeseca":s+="mjeseci",s;case"yy":return 1===e?s+="godina":2===e||3===e||4===e?s+="godine":s+="godina",s}}e.defineLocale("hr",{months:{format:"siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca".split("_"),standalone:"siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_")},monthsShort:"sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"Do MMMM YYYY",LLL:"Do MMMM YYYY H:mm",LLLL:"dddd, Do MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:return"[prošlu] [nedjelju] [u] LT";case 3:return"[prošlu] [srijedu] [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:a,m:a,mm:a,h:a,hh:a,d:"dan",dd:a,M:"mjesec",MM:a,y:"godinu",yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(e.r(166540))},491161,(e,a,t)=>{e.e,function(e){"use strict";var a="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");function t(e,a,t,s){switch(t){case"s":return s||a?"néhány másodperc":"néhány másodperce";case"ss":return e+(s||a)?" másodperc":" másodperce";case"m":return"egy"+(s||a?" perc":" perce");case"mm":return e+(s||a?" perc":" perce");case"h":return"egy"+(s||a?" óra":" órája");case"hh":return e+(s||a?" óra":" órája");case"d":return"egy"+(s||a?" nap":" napja");case"dd":return e+(s||a?" nap":" napja");case"M":return"egy"+(s||a?" hónap":" hónapja");case"MM":return e+(s||a?" hónap":" hónapja");case"y":return"egy"+(s||a?" év":" éve");case"yy":return e+(s||a?" év":" éve")}return""}function s(e){return(e?"":"[múlt] ")+"["+a[this.day()]+"] LT[-kor]"}e.defineLocale("hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan._feb._márc._ápr._máj._jún._júl._aug._szept._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D. H:mm",LLLL:"YYYY. MMMM D., dddd H:mm"},meridiemParse:/de|du/i,isPM:function(e){return"u"===e.charAt(1).toLowerCase()},meridiem:function(e,a,t){return e<12?!0===t?"de":"DE":!0===t?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return s.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return s.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},122472,(e,a,t)=>{e.e,e.r(166540).defineLocale("hy-am",{months:{format:"հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի".split("_"),standalone:"հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր".split("_")},monthsShort:"հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ".split("_"),weekdays:"կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ".split("_"),weekdaysShort:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),weekdaysMin:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY թ.",LLL:"D MMMM YYYY թ., HH:mm",LLLL:"dddd, D MMMM YYYY թ., HH:mm"},calendar:{sameDay:"[այսօր] LT",nextDay:"[վաղը] LT",lastDay:"[երեկ] LT",nextWeek:function(){return"dddd [օրը ժամը] LT"},lastWeek:function(){return"[անցած] dddd [օրը ժամը] LT"},sameElse:"L"},relativeTime:{future:"%s հետո",past:"%s առաջ",s:"մի քանի վայրկյան",ss:"%d վայրկյան",m:"րոպե",mm:"%d րոպե",h:"ժամ",hh:"%d ժամ",d:"օր",dd:"%d օր",M:"ամիս",MM:"%d ամիս",y:"տարի",yy:"%d տարի"},meridiemParse:/գիշերվա|առավոտվա|ցերեկվա|երեկոյան/,isPM:function(e){return/^(ցերեկվա|երեկոյան)$/.test(e)},meridiem:function(e){return e<4?"գիշերվա":e<12?"առավոտվա":e<17?"ցերեկվա":"երեկոյան"},dayOfMonthOrdinalParse:/\d{1,2}|\d{1,2}-(ին|րդ)/,ordinal:function(e,a){switch(a){case"DDD":case"w":case"W":case"DDDo":if(1===e)return e+"-ին";return e+"-րդ";default:return e}},week:{dow:1,doy:7}})},261476,(e,a,t)=>{e.e,e.r(166540).defineLocale("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Agt_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(e,a){return(12===e&&(e=0),"pagi"===a)?e:"siang"===a?e>=11?e:e+12:"sore"===a||"malam"===a?e+12:void 0},meridiem:function(e,a,t){return e<11?"pagi":e<15?"siang":e<19?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",ss:"%d detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:0,doy:6}})},595500,(e,a,t)=>{e.e,function(e){"use strict";function a(e){if(e%100==11);else if(e%10==1)return!1;return!0}function t(e,t,s,n){var r=e+" ";switch(s){case"s":return t||n?"nokkrar sekúndur":"nokkrum sekúndum";case"ss":if(a(e))return r+(t||n?"sekúndur":"sekúndum");return r+"sekúnda";case"m":return t?"mínúta":"mínútu";case"mm":if(a(e))return r+(t||n?"mínútur":"mínútum");if(t)return r+"mínúta";return r+"mínútu";case"hh":if(a(e))return r+(t||n?"klukkustundir":"klukkustundum");return r+"klukkustund";case"d":if(t)return"dagur";return n?"dag":"degi";case"dd":if(a(e)){if(t)return r+"dagar";return r+(n?"daga":"dögum")}if(t)return r+"dagur";return r+(n?"dag":"degi");case"M":if(t)return"mánuður";return n?"mánuð":"mánuði";case"MM":if(a(e)){if(t)return r+"mánuðir";return r+(n?"mánuði":"mánuðum")}if(t)return r+"mánuður";return r+(n?"mánuð":"mánuði");case"y":return t||n?"ár":"ári";case"yy":if(a(e))return r+(t||n?"ár":"árum");return r+(t||n?"ár":"ári")}}e.defineLocale("is",{months:"janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des".split("_"),weekdays:"sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"),weekdaysShort:"sun_mán_þri_mið_fim_fös_lau".split("_"),weekdaysMin:"Su_Má_Þr_Mi_Fi_Fö_La".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd, D. MMMM YYYY [kl.] H:mm"},calendar:{sameDay:"[í dag kl.] LT",nextDay:"[á morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[í gær kl.] LT",lastWeek:"[síðasta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s síðan",s:t,ss:t,m:t,mm:t,h:"klukkustund",hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},351426,(e,a,t)=>{e.e,e.r(166540).defineLocale("it-ch",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_lunedì_martedì_mercoledì_giovedì_venerdì_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){return 0===this.day()?"[la scorsa] dddd [alle] LT":"[lo scorso] dddd [alle] LT"},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})},988869,(e,a,t)=>{e.e,e.r(166540).defineLocale("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_lunedì_martedì_mercoledì_giovedì_venerdì_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:function(){return"[Oggi a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},nextDay:function(){return"[Domani a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},nextWeek:function(){return"dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},lastDay:function(){return"[Ieri a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},lastWeek:function(){return 0===this.day()?"[La scorsa] dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT":"[Lo scorso] dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},sameElse:"L"},relativeTime:{future:"tra %s",past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",w:"una settimana",ww:"%d settimane",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})},622116,(e,a,t)=>{e.e,e.r(166540).defineLocale("ja",{eras:[{since:"2019-05-01",offset:1,name:"令和",narrow:"㋿",abbr:"R"},{since:"1989-01-08",until:"2019-04-30",offset:1,name:"平成",narrow:"㍻",abbr:"H"},{since:"1926-12-25",until:"1989-01-07",offset:1,name:"昭和",narrow:"㍼",abbr:"S"},{since:"1912-07-30",until:"1926-12-24",offset:1,name:"大正",narrow:"㍽",abbr:"T"},{since:"1873-01-01",until:"1912-07-29",offset:6,name:"明治",narrow:"㍾",abbr:"M"},{since:"0001-01-01",until:"1873-12-31",offset:1,name:"西暦",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"紀元前",narrow:"BC",abbr:"BC"}],eraYearOrdinalRegex:/(元|\d+)年/,eraYearOrdinalParse:function(e,a){return"元"===a[1]?1:parseInt(a[1]||e,10)},months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日 dddd HH:mm",l:"YYYY/MM/DD",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日(ddd) HH:mm"},meridiemParse:/午前|午後/i,isPM:function(e){return"午後"===e},meridiem:function(e,a,t){return e<12?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:function(e){return e.week()!==this.week()?"[来週]dddd LT":"dddd LT"},lastDay:"[昨日] LT",lastWeek:function(e){return this.week()!==e.week()?"[先週]dddd LT":"dddd LT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}日/,ordinal:function(e,a){switch(a){case"y":return 1===e?"元年":e+"年";case"d":case"D":case"DDD":return e+"日";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"数秒",ss:"%d秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}})},874383,(e,a,t)=>{e.e,e.r(166540).defineLocale("jv",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des".split("_"),weekdays:"Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu".split("_"),weekdaysShort:"Min_Sen_Sel_Reb_Kem_Jem_Sep".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sp".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/enjing|siyang|sonten|ndalu/,meridiemHour:function(e,a){return(12===e&&(e=0),"enjing"===a)?e:"siyang"===a?e>=11?e:e+12:"sonten"===a||"ndalu"===a?e+12:void 0},meridiem:function(e,a,t){return e<11?"enjing":e<15?"siyang":e<19?"sonten":"ndalu"},calendar:{sameDay:"[Dinten puniko pukul] LT",nextDay:"[Mbenjang pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kala wingi pukul] LT",lastWeek:"dddd [kepengker pukul] LT",sameElse:"L"},relativeTime:{future:"wonten ing %s",past:"%s ingkang kepengker",s:"sawetawis detik",ss:"%d detik",m:"setunggal menit",mm:"%d menit",h:"setunggal jam",hh:"%d jam",d:"sedinten",dd:"%d dinten",M:"sewulan",MM:"%d wulan",y:"setaun",yy:"%d taun"},week:{dow:1,doy:7}})},11842,(e,a,t)=>{e.e,e.r(166540).defineLocale("ka",{months:"იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი".split("_"),monthsShort:"იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ".split("_"),weekdays:{standalone:"კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი".split("_"),format:"კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს".split("_"),isFormat:/(წინა|შემდეგ)/},weekdaysShort:"კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ".split("_"),weekdaysMin:"კვ_ორ_სა_ოთ_ხუ_პა_შა".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[დღეს] LT[-ზე]",nextDay:"[ხვალ] LT[-ზე]",lastDay:"[გუშინ] LT[-ზე]",nextWeek:"[შემდეგ] dddd LT[-ზე]",lastWeek:"[წინა] dddd LT-ზე",sameElse:"L"},relativeTime:{future:function(e){return e.replace(/(წამ|წუთ|საათ|წელ|დღ|თვ)(ი|ე)/,function(e,a,t){return"ი"===t?a+"ში":a+t+"ში"})},past:function(e){return/(წამი|წუთი|საათი|დღე|თვე)/.test(e)?e.replace(/(ი|ე)$/,"ის წინ"):/წელი/.test(e)?e.replace(/წელი$/,"წლის წინ"):e},s:"რამდენიმე წამი",ss:"%d წამი",m:"წუთი",mm:"%d წუთი",h:"საათი",hh:"%d საათი",d:"დღე",dd:"%d დღე",M:"თვე",MM:"%d თვე",y:"წელი",yy:"%d წელი"},dayOfMonthOrdinalParse:/0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/,ordinal:function(e){return 0===e?e:1===e?e+"-ლი":e<20||e<=100&&e%20==0||e%100==0?"მე-"+e:e+"-ე"},week:{dow:1,doy:7}})},613970,(e,a,t)=>{e.e,function(e){"use strict";var a={0:"-ші",1:"-ші",2:"-ші",3:"-ші",4:"-ші",5:"-ші",6:"-шы",7:"-ші",8:"-ші",9:"-шы",10:"-шы",20:"-шы",30:"-шы",40:"-шы",50:"-ші",60:"-шы",70:"-ші",80:"-ші",90:"-шы",100:"-ші"};e.defineLocale("kk",{months:"қаңтар_ақпан_наурыз_сәуір_мамыр_маусым_шілде_тамыз_қыркүйек_қазан_қараша_желтоқсан".split("_"),monthsShort:"қаң_ақп_нау_сәу_мам_мау_шіл_там_қыр_қаз_қар_жел".split("_"),weekdays:"жексенбі_дүйсенбі_сейсенбі_сәрсенбі_бейсенбі_жұма_сенбі".split("_"),weekdaysShort:"жек_дүй_сей_сәр_бей_жұм_сен".split("_"),weekdaysMin:"жк_дй_сй_ср_бй_жм_сн".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Бүгін сағат] LT",nextDay:"[Ертең сағат] LT",nextWeek:"dddd [сағат] LT",lastDay:"[Кеше сағат] LT",lastWeek:"[Өткен аптаның] dddd [сағат] LT",sameElse:"L"},relativeTime:{future:"%s ішінде",past:"%s бұрын",s:"бірнеше секунд",ss:"%d секунд",m:"бір минут",mm:"%d минут",h:"бір сағат",hh:"%d сағат",d:"бір күн",dd:"%d күн",M:"бір ай",MM:"%d ай",y:"бір жыл",yy:"%d жыл"},dayOfMonthOrdinalParse:/\d{1,2}-(ші|шы)/,ordinal:function(e){return e+(a[e]||a[e%10]||a[e>=100?100:null])},week:{dow:1,doy:7}})}(e.r(166540))},621412,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"១",2:"២",3:"៣",4:"៤",5:"៥",6:"៦",7:"៧",8:"៨",9:"៩",0:"០"},t={"១":"1","២":"2","៣":"3","៤":"4","៥":"5","៦":"6","៧":"7","៨":"8","៩":"9","០":"0"};e.defineLocale("km",{months:"មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),monthsShort:"មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),weekdays:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),weekdaysShort:"អា_ច_អ_ព_ព្រ_សុ_ស".split("_"),weekdaysMin:"អា_ច_អ_ព_ព្រ_សុ_ស".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/ព្រឹក|ល្ងាច/,isPM:function(e){return"ល្ងាច"===e},meridiem:function(e,a,t){return e<12?"ព្រឹក":"ល្ងាច"},calendar:{sameDay:"[ថ្ងៃនេះ ម៉ោង] LT",nextDay:"[ស្អែក ម៉ោង] LT",nextWeek:"dddd [ម៉ោង] LT",lastDay:"[ម្សិលមិញ ម៉ោង] LT",lastWeek:"dddd [សប្តាហ៍មុន] [ម៉ោង] LT",sameElse:"L"},relativeTime:{future:"%sទៀត",past:"%sមុន",s:"ប៉ុន្មានវិនាទី",ss:"%d វិនាទី",m:"មួយនាទី",mm:"%d នាទី",h:"មួយម៉ោង",hh:"%d ម៉ោង",d:"មួយថ្ងៃ",dd:"%d ថ្ងៃ",M:"មួយខែ",MM:"%d ខែ",y:"មួយឆ្នាំ",yy:"%d ឆ្នាំ"},dayOfMonthOrdinalParse:/ទី\d{1,2}/,ordinal:"ទី%d",preparse:function(e){return e.replace(/[១២៣៤៥៦៧៨៩០]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},week:{dow:1,doy:4}})}(e.r(166540))},978630,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"೧",2:"೨",3:"೩",4:"೪",5:"೫",6:"೬",7:"೭",8:"೮",9:"೯",0:"೦"},t={"೧":"1","೨":"2","೩":"3","೪":"4","೫":"5","೬":"6","೭":"7","೮":"8","೯":"9","೦":"0"};e.defineLocale("kn",{months:"ಜನವರಿ_ಫೆಬ್ರವರಿ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂಬರ್_ಅಕ್ಟೋಬರ್_ನವೆಂಬರ್_ಡಿಸೆಂಬರ್".split("_"),monthsShort:"ಜನ_ಫೆಬ್ರ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂ_ಅಕ್ಟೋ_ನವೆಂ_ಡಿಸೆಂ".split("_"),monthsParseExact:!0,weekdays:"ಭಾನುವಾರ_ಸೋಮವಾರ_ಮಂಗಳವಾರ_ಬುಧವಾರ_ಗುರುವಾರ_ಶುಕ್ರವಾರ_ಶನಿವಾರ".split("_"),weekdaysShort:"ಭಾನು_ಸೋಮ_ಮಂಗಳ_ಬುಧ_ಗುರು_ಶುಕ್ರ_ಶನಿ".split("_"),weekdaysMin:"ಭಾ_ಸೋ_ಮಂ_ಬು_ಗು_ಶು_ಶ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ಇಂದು] LT",nextDay:"[ನಾಳೆ] LT",nextWeek:"dddd, LT",lastDay:"[ನಿನ್ನೆ] LT",lastWeek:"[ಕೊನೆಯ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ನಂತರ",past:"%s ಹಿಂದೆ",s:"ಕೆಲವು ಕ್ಷಣಗಳು",ss:"%d ಸೆಕೆಂಡುಗಳು",m:"ಒಂದು ನಿಮಿಷ",mm:"%d ನಿಮಿಷ",h:"ಒಂದು ಗಂಟೆ",hh:"%d ಗಂಟೆ",d:"ಒಂದು ದಿನ",dd:"%d ದಿನ",M:"ಒಂದು ತಿಂಗಳು",MM:"%d ತಿಂಗಳು",y:"ಒಂದು ವರ್ಷ",yy:"%d ವರ್ಷ"},preparse:function(e){return e.replace(/[೧೨೩೪೫೬೭೮೯೦]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/ರಾತ್ರಿ|ಬೆಳಿಗ್ಗೆ|ಮಧ್ಯಾಹ್ನ|ಸಂಜೆ/,meridiemHour:function(e,a){return(12===e&&(e=0),"ರಾತ್ರಿ"===a)?e<4?e:e+12:"ಬೆಳಿಗ್ಗೆ"===a?e:"ಮಧ್ಯಾಹ್ನ"===a?e>=10?e:e+12:"ಸಂಜೆ"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"ರಾತ್ರಿ":e<10?"ಬೆಳಿಗ್ಗೆ":e<17?"ಮಧ್ಯಾಹ್ನ":e<20?"ಸಂಜೆ":"ರಾತ್ರಿ"},dayOfMonthOrdinalParse:/\d{1,2}(ನೇ)/,ordinal:function(e){return e+"ನೇ"},week:{dow:0,doy:6}})}(e.r(166540))},73893,(e,a,t)=>{e.e,e.r(166540).defineLocale("ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 A h:mm",LLLL:"YYYY년 MMMM D일 dddd A h:mm",l:"YYYY.MM.DD.",ll:"YYYY년 MMMM D일",lll:"YYYY년 MMMM D일 A h:mm",llll:"YYYY년 MMMM D일 dddd A h:mm"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇 초",ss:"%d초",m:"1분",mm:"%d분",h:"한 시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한 달",MM:"%d달",y:"일 년",yy:"%d년"},dayOfMonthOrdinalParse:/\d{1,2}(일|월|주)/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"일";case"M":return e+"월";case"w":case"W":return e+"주";default:return e}},meridiemParse:/오전|오후/,isPM:function(e){return"오후"===e},meridiem:function(e,a,t){return e<12?"오전":"오후"}})},531990,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={s:["çend sanîye","çend sanîyeyan"],ss:[e+" sanîye",e+" sanîyeyan"],m:["deqîqeyek","deqîqeyekê"],mm:[e+" deqîqe",e+" deqîqeyan"],h:["saetek","saetekê"],hh:[e+" saet",e+" saetan"],d:["rojek","rojekê"],dd:[e+" roj",e+" rojan"],w:["hefteyek","hefteyekê"],ww:[e+" hefte",e+" hefteyan"],M:["mehek","mehekê"],MM:[e+" meh",e+" mehan"],y:["salek","salekê"],yy:[e+" sal",e+" salan"]};return a?n[t][0]:n[t][1]}e.defineLocale("ku-kmr",{months:"Rêbendan_Sibat_Adar_Nîsan_Gulan_Hezîran_Tîrmeh_Tebax_Îlon_Cotmeh_Mijdar_Berfanbar".split("_"),monthsShort:"Rêb_Sib_Ada_Nîs_Gul_Hez_Tîr_Teb_Îlo_Cot_Mij_Ber".split("_"),monthsParseExact:!0,weekdays:"Yekşem_Duşem_Sêşem_Çarşem_Pêncşem_În_Şemî".split("_"),weekdaysShort:"Yek_Du_Sê_Çar_Pên_În_Şem".split("_"),weekdaysMin:"Ye_Du_Sê_Ça_Pê_În_Şe".split("_"),meridiem:function(e,a,t){return e<12?t?"bn":"BN":t?"pn":"PN"},meridiemParse:/bn|BN|pn|PN/,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"Do MMMM[a] YYYY[an]",LLL:"Do MMMM[a] YYYY[an] HH:mm",LLLL:"dddd, Do MMMM[a] YYYY[an] HH:mm",ll:"Do MMM[.] YYYY[an]",lll:"Do MMM[.] YYYY[an] HH:mm",llll:"ddd[.], Do MMM[.] YYYY[an] HH:mm"},calendar:{sameDay:"[Îro di saet] LT [de]",nextDay:"[Sibê di saet] LT [de]",nextWeek:"dddd [di saet] LT [de]",lastDay:"[Duh di saet] LT [de]",lastWeek:"dddd[a borî di saet] LT [de]",sameElse:"L"},relativeTime:{future:"di %s de",past:"berî %s",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,w:a,ww:a,M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}(?:yê|ê|\.)/,ordinal:function(e,a){var t,s,n,r=a.toLowerCase();return r.includes("w")||r.includes("m")?e+".":e+(s=(t=""+(t=e)).substring(t.length-1),12!=(n=t.length>1?t.substring(t.length-2):"")&&13!=n&&("2"==s||"3"==s||"50"==n||"70"==s||"80"==s)?"yê":"ê")},week:{dow:1,doy:4}})}(e.r(166540))},327383,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},t={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},s=["کانونی دووەم","شوبات","ئازار","نیسان","ئایار","حوزەیران","تەمموز","ئاب","ئەیلوول","تشرینی یەكەم","تشرینی دووەم","كانونی یەکەم"];e.defineLocale("ku",{months:s,monthsShort:s,weekdays:"یه‌كشه‌ممه‌_دووشه‌ممه‌_سێشه‌ممه‌_چوارشه‌ممه‌_پێنجشه‌ممه‌_هه‌ینی_شه‌ممه‌".split("_"),weekdaysShort:"یه‌كشه‌م_دووشه‌م_سێشه‌م_چوارشه‌م_پێنجشه‌م_هه‌ینی_شه‌ممه‌".split("_"),weekdaysMin:"ی_د_س_چ_پ_ه_ش".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/ئێواره‌|به‌یانی/,isPM:function(e){return/ئێواره‌/.test(e)},meridiem:function(e,a,t){return e<12?"به‌یانی":"ئێواره‌"},calendar:{sameDay:"[ئه‌مرۆ كاتژمێر] LT",nextDay:"[به‌یانی كاتژمێر] LT",nextWeek:"dddd [كاتژمێر] LT",lastDay:"[دوێنێ كاتژمێر] LT",lastWeek:"dddd [كاتژمێر] LT",sameElse:"L"},relativeTime:{future:"له‌ %s",past:"%s",s:"چه‌ند چركه‌یه‌ك",ss:"چركه‌ %d",m:"یه‌ك خوله‌ك",mm:"%d خوله‌ك",h:"یه‌ك كاتژمێر",hh:"%d كاتژمێر",d:"یه‌ك ڕۆژ",dd:"%d ڕۆژ",M:"یه‌ك مانگ",MM:"%d مانگ",y:"یه‌ك ساڵ",yy:"%d ساڵ"},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(e){return t[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},week:{dow:6,doy:12}})}(e.r(166540))},913233,(e,a,t)=>{e.e,function(e){"use strict";var a={0:"-чү",1:"-чи",2:"-чи",3:"-чү",4:"-чү",5:"-чи",6:"-чы",7:"-чи",8:"-чи",9:"-чу",10:"-чу",20:"-чы",30:"-чу",40:"-чы",50:"-чү",60:"-чы",70:"-чи",80:"-чи",90:"-чу",100:"-чү"};e.defineLocale("ky",{months:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),monthsShort:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),weekdays:"Жекшемби_Дүйшөмбү_Шейшемби_Шаршемби_Бейшемби_Жума_Ишемби".split("_"),weekdaysShort:"Жек_Дүй_Шей_Шар_Бей_Жум_Ише".split("_"),weekdaysMin:"Жк_Дй_Шй_Шр_Бй_Жм_Иш".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Бүгүн саат] LT",nextDay:"[Эртең саат] LT",nextWeek:"dddd [саат] LT",lastDay:"[Кечээ саат] LT",lastWeek:"[Өткөн аптанын] dddd [күнү] [саат] LT",sameElse:"L"},relativeTime:{future:"%s ичинде",past:"%s мурун",s:"бирнече секунд",ss:"%d секунд",m:"бир мүнөт",mm:"%d мүнөт",h:"бир саат",hh:"%d саат",d:"бир күн",dd:"%d күн",M:"бир ай",MM:"%d ай",y:"бир жыл",yy:"%d жыл"},dayOfMonthOrdinalParse:/\d{1,2}-(чи|чы|чү|чу)/,ordinal:function(e){return e+(a[e]||a[e%10]||a[e>=100?100:null])},week:{dow:1,doy:7}})}(e.r(166540))},535403,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={m:["eng Minutt","enger Minutt"],h:["eng Stonn","enger Stonn"],d:["een Dag","engem Dag"],M:["ee Mount","engem Mount"],y:["ee Joer","engem Joer"]};return a?n[t][0]:n[t][1]}function t(e){if(isNaN(e=parseInt(e,10)))return!1;if(e<0)return!0;if(e<10)return!!(4<=e)&&!!(e<=7);if(e<100){var a=e%10,s=e/10;return 0===a?t(s):t(a)}if(!(e<1e4))return t(e/=1e3);for(;e>=10;)e/=10;return t(e)}e.defineLocale("lb",{months:"Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),weekdaysShort:"So._Mé._Dë._Më._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mé_Dë_Më_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm [Auer]",LTS:"H:mm:ss [Auer]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm [Auer]",LLLL:"dddd, D. MMMM YYYY H:mm [Auer]"},calendar:{sameDay:"[Haut um] LT",sameElse:"L",nextDay:"[Muer um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gëschter um] LT",lastWeek:function(){switch(this.day()){case 2:case 4:return"[Leschten] dddd [um] LT";default:return"[Leschte] dddd [um] LT"}}},relativeTime:{future:function(e){return t(e.substr(0,e.indexOf(" ")))?"a "+e:"an "+e},past:function(e){return t(e.substr(0,e.indexOf(" ")))?"viru "+e:"virun "+e},s:"e puer Sekonnen",ss:"%d Sekonnen",m:a,mm:"%d Minutten",h:a,hh:"%d Stonnen",d:a,dd:"%d Deeg",M:a,MM:"%d Méint",y:a,yy:"%d Joer"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},17373,(e,a,t)=>{e.e,e.r(166540).defineLocale("lo",{months:"ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ".split("_"),monthsShort:"ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ".split("_"),weekdays:"ອາທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ".split("_"),weekdaysShort:"ທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ".split("_"),weekdaysMin:"ທ_ຈ_ອຄ_ພ_ພຫ_ສກ_ສ".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ວັນdddd D MMMM YYYY HH:mm"},meridiemParse:/ຕອນເຊົ້າ|ຕອນແລງ/,isPM:function(e){return"ຕອນແລງ"===e},meridiem:function(e,a,t){return e<12?"ຕອນເຊົ້າ":"ຕອນແລງ"},calendar:{sameDay:"[ມື້ນີ້ເວລາ] LT",nextDay:"[ມື້ອື່ນເວລາ] LT",nextWeek:"[ວັນ]dddd[ໜ້າເວລາ] LT",lastDay:"[ມື້ວານນີ້ເວລາ] LT",lastWeek:"[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT",sameElse:"L"},relativeTime:{future:"ອີກ %s",past:"%sຜ່ານມາ",s:"ບໍ່ເທົ່າໃດວິນາທີ",ss:"%d ວິນາທີ",m:"1 ນາທີ",mm:"%d ນາທີ",h:"1 ຊົ່ວໂມງ",hh:"%d ຊົ່ວໂມງ",d:"1 ມື້",dd:"%d ມື້",M:"1 ເດືອນ",MM:"%d ເດືອນ",y:"1 ປີ",yy:"%d ປີ"},dayOfMonthOrdinalParse:/(ທີ່)\d{1,2}/,ordinal:function(e){return"ທີ່"+e}})},409583,(e,a,t)=>{e.e,function(e){"use strict";var a={ss:"sekundė_sekundžių_sekundes",m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesį",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"};function t(e,a,t,s){return a?n(t)[0]:s?n(t)[1]:n(t)[2]}function s(e){return e%10==0||e>10&&e<20}function n(e){return a[e].split("_")}function r(e,a,r,d){var i=e+" ";return 1===e?i+t(e,a,r[0],d):a?i+(s(e)?n(r)[1]:n(r)[0]):d?i+n(r)[1]:i+(s(e)?n(r)[1]:n(r)[2])}e.defineLocale("lt",{months:{format:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),standalone:"sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjūtis_rugsėjis_spalis_lapkritis_gruodis".split("_"),isFormat:/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?/},monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:{format:"sekmadienį_pirmadienį_antradienį_trečiadienį_ketvirtadienį_penktadienį_šeštadienį".split("_"),standalone:"sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_"),isFormat:/dddd HH:mm/},weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Š".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], HH:mm [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], HH:mm [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]"},calendar:{sameDay:"[Šiandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[Praėjusį] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieš %s",s:function(e,a,t,s){return a?"kelios sekundės":s?"kelių sekundžių":"kelias sekundes"},ss:r,m:t,mm:r,h:t,hh:r,d:t,dd:r,M:t,MM:r,y:t,yy:r},dayOfMonthOrdinalParse:/\d{1,2}-oji/,ordinal:function(e){return e+"-oji"},week:{dow:1,doy:4}})}(e.r(166540))},407912,(e,a,t)=>{e.e,function(e){"use strict";var a={ss:"sekundes_sekundēm_sekunde_sekundes".split("_"),m:"minūtes_minūtēm_minūte_minūtes".split("_"),mm:"minūtes_minūtēm_minūte_minūtes".split("_"),h:"stundas_stundām_stunda_stundas".split("_"),hh:"stundas_stundām_stunda_stundas".split("_"),d:"dienas_dienām_diena_dienas".split("_"),dd:"dienas_dienām_diena_dienas".split("_"),M:"mēneša_mēnešiem_mēnesis_mēneši".split("_"),MM:"mēneša_mēnešiem_mēnesis_mēneši".split("_"),y:"gada_gadiem_gads_gadi".split("_"),yy:"gada_gadiem_gads_gadi".split("_")};function t(e,a,t){return t?a%10==1&&a%100!=11?e[2]:e[3]:a%10==1&&a%100!=11?e[0]:e[1]}function s(e,s,n){return e+" "+t(a[n],e,s)}function n(e,s,n){return t(a[n],e,s)}e.defineLocale("lv",{months:"janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY.",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, HH:mm",LLLL:"YYYY. [gada] D. MMMM, dddd, HH:mm"},calendar:{sameDay:"[Šodien pulksten] LT",nextDay:"[Rīt pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[Pagājušā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"pēc %s",past:"pirms %s",s:function(e,a){return a?"dažas sekundes":"dažām sekundēm"},ss:s,m:n,mm:s,h:n,hh:s,d:n,dd:s,M:n,MM:s,y:n,yy:s},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},545267,(e,a,t)=>{e.e,function(e){"use strict";var a={words:{ss:["sekund","sekunda","sekundi"],m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mjesec","mjeseca","mjeseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,a){return 1===e?a[0]:e>=2&&e<=4?a[1]:a[2]},translate:function(e,t,s){var n=a.words[s];return 1===s.length?t?n[0]:n[1]:e+" "+a.correctGrammaticalCase(e,n)}};e.defineLocale("me",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sjutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){return["[prošle] [nedjelje] [u] LT","[prošlog] [ponedjeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srijede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"][this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"nekoliko sekundi",ss:a.translate,m:a.translate,mm:a.translate,h:a.translate,hh:a.translate,d:"dan",dd:a.translate,M:"mjesec",MM:a.translate,y:"godinu",yy:a.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(e.r(166540))},961705,(e,a,t)=>{e.e,e.r(166540).defineLocale("mi",{months:"Kohi-tāte_Hui-tanguru_Poutū-te-rangi_Paenga-whāwhā_Haratua_Pipiri_Hōngoingoi_Here-turi-kōkā_Mahuru_Whiringa-ā-nuku_Whiringa-ā-rangi_Hakihea".split("_"),monthsShort:"Kohi_Hui_Pou_Pae_Hara_Pipi_Hōngoi_Here_Mahu_Whi-nu_Whi-ra_Haki".split("_"),monthsRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,2}/i,weekdays:"Rātapu_Mane_Tūrei_Wenerei_Tāite_Paraire_Hātarei".split("_"),weekdaysShort:"Ta_Ma_Tū_We_Tāi_Pa_Hā".split("_"),weekdaysMin:"Ta_Ma_Tū_We_Tāi_Pa_Hā".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [i] HH:mm",LLLL:"dddd, D MMMM YYYY [i] HH:mm"},calendar:{sameDay:"[i teie mahana, i] LT",nextDay:"[apopo i] LT",nextWeek:"dddd [i] LT",lastDay:"[inanahi i] LT",lastWeek:"dddd [whakamutunga i] LT",sameElse:"L"},relativeTime:{future:"i roto i %s",past:"%s i mua",s:"te hēkona ruarua",ss:"%d hēkona",m:"he meneti",mm:"%d meneti",h:"te haora",hh:"%d haora",d:"he ra",dd:"%d ra",M:"he marama",MM:"%d marama",y:"he tau",yy:"%d tau"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})},354402,(e,a,t)=>{e.e,e.r(166540).defineLocale("mk",{months:"јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември".split("_"),monthsShort:"јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек".split("_"),weekdays:"недела_понеделник_вторник_среда_четврток_петок_сабота".split("_"),weekdaysShort:"нед_пон_вто_сре_чет_пет_саб".split("_"),weekdaysMin:"нe_пo_вт_ср_че_пе_сa".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Денес во] LT",nextDay:"[Утре во] LT",nextWeek:"[Во] dddd [во] LT",lastDay:"[Вчера во] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Изминатата] dddd [во] LT";case 1:case 2:case 4:case 5:return"[Изминатиот] dddd [во] LT"}},sameElse:"L"},relativeTime:{future:"за %s",past:"пред %s",s:"неколку секунди",ss:"%d секунди",m:"една минута",mm:"%d минути",h:"еден час",hh:"%d часа",d:"еден ден",dd:"%d дена",M:"еден месец",MM:"%d месеци",y:"една година",yy:"%d години"},dayOfMonthOrdinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(e){var a=e%10,t=e%100;if(0===e)return e+"-ев";if(0===t)return e+"-ен";if(t>10&&t<20)return e+"-ти";if(1===a)return e+"-ви";if(2===a)return e+"-ри";else if(7===a||8===a)return e+"-ми";else return e+"-ти"},week:{dow:1,doy:7}})},624201,(e,a,t)=>{e.e,e.r(166540).defineLocale("ml",{months:"ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ".split("_"),monthsShort:"ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.".split("_"),monthsParseExact:!0,weekdays:"ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച".split("_"),weekdaysShort:"ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി".split("_"),weekdaysMin:"ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ".split("_"),longDateFormat:{LT:"A h:mm -നു",LTS:"A h:mm:ss -നു",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm -നു",LLLL:"dddd, D MMMM YYYY, A h:mm -നു"},calendar:{sameDay:"[ഇന്ന്] LT",nextDay:"[നാളെ] LT",nextWeek:"dddd, LT",lastDay:"[ഇന്നലെ] LT",lastWeek:"[കഴിഞ്ഞ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s കഴിഞ്ഞ്",past:"%s മുൻപ്",s:"അൽപ നിമിഷങ്ങൾ",ss:"%d സെക്കൻഡ്",m:"ഒരു മിനിറ്റ്",mm:"%d മിനിറ്റ്",h:"ഒരു മണിക്കൂർ",hh:"%d മണിക്കൂർ",d:"ഒരു ദിവസം",dd:"%d ദിവസം",M:"ഒരു മാസം",MM:"%d മാസം",y:"ഒരു വർഷം",yy:"%d വർഷം"},meridiemParse:/രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i,meridiemHour:function(e,a){return(12===e&&(e=0),"രാത്രി"===a&&e>=4||"ഉച്ച കഴിഞ്ഞ്"===a||"വൈകുന്നേരം"===a)?e+12:e},meridiem:function(e,a,t){return e<4?"രാത്രി":e<12?"രാവിലെ":e<17?"ഉച്ച കഴിഞ്ഞ്":e<20?"വൈകുന്നേരം":"രാത്രി"}})},969668,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){switch(t){case"s":return a?"хэдхэн секунд":"хэдхэн секундын";case"ss":return e+(a?" секунд":" секундын");case"m":case"mm":return e+(a?" минут":" минутын");case"h":case"hh":return e+(a?" цаг":" цагийн");case"d":case"dd":return e+(a?" өдөр":" өдрийн");case"M":case"MM":return e+(a?" сар":" сарын");case"y":case"yy":return e+(a?" жил":" жилийн");default:return e}}e.defineLocale("mn",{months:"Нэгдүгээр сар_Хоёрдугаар сар_Гуравдугаар сар_Дөрөвдүгээр сар_Тавдугаар сар_Зургадугаар сар_Долдугаар сар_Наймдугаар сар_Есдүгээр сар_Аравдугаар сар_Арван нэгдүгээр сар_Арван хоёрдугаар сар".split("_"),monthsShort:"1 сар_2 сар_3 сар_4 сар_5 сар_6 сар_7 сар_8 сар_9 сар_10 сар_11 сар_12 сар".split("_"),monthsParseExact:!0,weekdays:"Ням_Даваа_Мягмар_Лхагва_Пүрэв_Баасан_Бямба".split("_"),weekdaysShort:"Ням_Дав_Мяг_Лха_Пүр_Баа_Бям".split("_"),weekdaysMin:"Ня_Да_Мя_Лх_Пү_Ба_Бя".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY оны MMMMын D",LLL:"YYYY оны MMMMын D HH:mm",LLLL:"dddd, YYYY оны MMMMын D HH:mm"},meridiemParse:/ҮӨ|ҮХ/i,isPM:function(e){return"ҮХ"===e},meridiem:function(e,a,t){return e<12?"ҮӨ":"ҮХ"},calendar:{sameDay:"[Өнөөдөр] LT",nextDay:"[Маргааш] LT",nextWeek:"[Ирэх] dddd LT",lastDay:"[Өчигдөр] LT",lastWeek:"[Өнгөрсөн] dddd LT",sameElse:"L"},relativeTime:{future:"%s дараа",past:"%s өмнө",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2} өдөр/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+" өдөр";default:return e}}})}(e.r(166540))},417366,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},t={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};function s(e,a,t,s){var n="";if(a)switch(t){case"s":n="काही सेकंद";break;case"ss":n="%d सेकंद";break;case"m":n="एक मिनिट";break;case"mm":n="%d मिनिटे";break;case"h":n="एक तास";break;case"hh":n="%d तास";break;case"d":n="एक दिवस";break;case"dd":n="%d दिवस";break;case"M":n="एक महिना";break;case"MM":n="%d महिने";break;case"y":n="एक वर्ष";break;case"yy":n="%d वर्षे"}else switch(t){case"s":n="काही सेकंदां";break;case"ss":n="%d सेकंदां";break;case"m":n="एका मिनिटा";break;case"mm":n="%d मिनिटां";break;case"h":n="एका तासा";break;case"hh":n="%d तासां";break;case"d":n="एका दिवसा";break;case"dd":n="%d दिवसां";break;case"M":n="एका महिन्या";break;case"MM":n="%d महिन्यां";break;case"y":n="एका वर्षा";break;case"yy":n="%d वर्षां"}return n.replace(/%d/i,e)}e.defineLocale("mr",{months:"जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर".split("_"),monthsShort:"जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.".split("_"),monthsParseExact:!0,weekdays:"रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm वाजता",LTS:"A h:mm:ss वाजता",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm वाजता",LLLL:"dddd, D MMMM YYYY, A h:mm वाजता"},calendar:{sameDay:"[आज] LT",nextDay:"[उद्या] LT",nextWeek:"dddd, LT",lastDay:"[काल] LT",lastWeek:"[मागील] dddd, LT",sameElse:"L"},relativeTime:{future:"%sमध्ये",past:"%sपूर्वी",s:s,ss:s,m:s,mm:s,h:s,hh:s,d:s,dd:s,M:s,MM:s,y:s,yy:s},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/पहाटे|सकाळी|दुपारी|सायंकाळी|रात्री/,meridiemHour:function(e,a){return(12===e&&(e=0),"पहाटे"===a||"सकाळी"===a)?e:"दुपारी"===a||"सायंकाळी"===a||"रात्री"===a?e>=12?e:e+12:void 0},meridiem:function(e,a,t){return e>=0&&e<6?"पहाटे":e<12?"सकाळी":e<17?"दुपारी":e<20?"सायंकाळी":"रात्री"},week:{dow:0,doy:6}})}(e.r(166540))},538640,(e,a,t)=>{e.e,e.r(166540).defineLocale("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,a){return(12===e&&(e=0),"pagi"===a)?e:"tengahari"===a?e>=11?e:e+12:"petang"===a||"malam"===a?e+12:void 0},meridiem:function(e,a,t){return e<11?"pagi":e<15?"tengahari":e<19?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})},367856,(e,a,t)=>{e.e,e.r(166540).defineLocale("ms",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,a){return(12===e&&(e=0),"pagi"===a)?e:"tengahari"===a?e>=11?e:e+12:"petang"===a||"malam"===a?e+12:void 0},meridiem:function(e,a,t){return e<11?"pagi":e<15?"tengahari":e<19?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})},157692,(e,a,t)=>{e.e,e.r(166540).defineLocale("mt",{months:"Jannar_Frar_Marzu_April_Mejju_Ġunju_Lulju_Awwissu_Settembru_Ottubru_Novembru_Diċembru".split("_"),monthsShort:"Jan_Fra_Mar_Apr_Mej_Ġun_Lul_Aww_Set_Ott_Nov_Diċ".split("_"),weekdays:"Il-Ħadd_It-Tnejn_It-Tlieta_L-Erbgħa_Il-Ħamis_Il-Ġimgħa_Is-Sibt".split("_"),weekdaysShort:"Ħad_Tne_Tli_Erb_Ħam_Ġim_Sib".split("_"),weekdaysMin:"Ħa_Tn_Tl_Er_Ħa_Ġi_Si".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Illum fil-]LT",nextDay:"[Għada fil-]LT",nextWeek:"dddd [fil-]LT",lastDay:"[Il-bieraħ fil-]LT",lastWeek:"dddd [li għadda] [fil-]LT",sameElse:"L"},relativeTime:{future:"f’ %s",past:"%s ilu",s:"ftit sekondi",ss:"%d sekondi",m:"minuta",mm:"%d minuti",h:"siegħa",hh:"%d siegħat",d:"ġurnata",dd:"%d ġranet",M:"xahar",MM:"%d xhur",y:"sena",yy:"%d sni"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})},222310,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"၁",2:"၂",3:"၃",4:"၄",5:"၅",6:"၆",7:"၇",8:"၈",9:"၉",0:"၀"},t={"၁":"1","၂":"2","၃":"3","၄":"4","၅":"5","၆":"6","၇":"7","၈":"8","၉":"9","၀":"0"};e.defineLocale("my",{months:"ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ".split("_"),monthsShort:"ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ".split("_"),weekdays:"တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ".split("_"),weekdaysShort:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),weekdaysMin:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ယနေ.] LT [မှာ]",nextDay:"[မနက်ဖြန်] LT [မှာ]",nextWeek:"dddd LT [မှာ]",lastDay:"[မနေ.က] LT [မှာ]",lastWeek:"[ပြီးခဲ့သော] dddd LT [မှာ]",sameElse:"L"},relativeTime:{future:"လာမည့် %s မှာ",past:"လွန်ခဲ့သော %s က",s:"စက္ကန်.အနည်းငယ်",ss:"%d စက္ကန့်",m:"တစ်မိနစ်",mm:"%d မိနစ်",h:"တစ်နာရီ",hh:"%d နာရီ",d:"တစ်ရက်",dd:"%d ရက်",M:"တစ်လ",MM:"%d လ",y:"တစ်နှစ်",yy:"%d နှစ်"},preparse:function(e){return e.replace(/[၁၂၃၄၅၆၇၈၉၀]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},week:{dow:1,doy:4}})}(e.r(166540))},441867,(e,a,t)=>{e.e,e.r(166540).defineLocale("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:!0,weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"sø._ma._ti._on._to._fr._lø.".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] HH:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"noen sekunder",ss:"%d sekunder",m:"ett minutt",mm:"%d minutter",h:"én time",hh:"%d timer",d:"én dag",dd:"%d dager",w:"én uke",ww:"%d uker",M:"én måned",MM:"%d måneder",y:"ett år",yy:"%d år"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})},899103,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},t={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};e.defineLocale("ne",{months:"जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर".split("_"),monthsShort:"जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.".split("_"),monthsParseExact:!0,weekdays:"आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार".split("_"),weekdaysShort:"आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.".split("_"),weekdaysMin:"आ._सो._मं._बु._बि._शु._श.".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"Aको h:mm बजे",LTS:"Aको h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, Aको h:mm बजे",LLLL:"dddd, D MMMM YYYY, Aको h:mm बजे"},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/राति|बिहान|दिउँसो|साँझ/,meridiemHour:function(e,a){return(12===e&&(e=0),"राति"===a)?e<4?e:e+12:"बिहान"===a?e:"दिउँसो"===a?e>=10?e:e+12:"साँझ"===a?e+12:void 0},meridiem:function(e,a,t){return e<3?"राति":e<12?"बिहान":e<16?"दिउँसो":e<20?"साँझ":"राति"},calendar:{sameDay:"[आज] LT",nextDay:"[भोलि] LT",nextWeek:"[आउँदो] dddd[,] LT",lastDay:"[हिजो] LT",lastWeek:"[गएको] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%sमा",past:"%s अगाडि",s:"केही क्षण",ss:"%d सेकेण्ड",m:"एक मिनेट",mm:"%d मिनेट",h:"एक घण्टा",hh:"%d घण्टा",d:"एक दिन",dd:"%d दिन",M:"एक महिना",MM:"%d महिना",y:"एक बर्ष",yy:"%d बर्ष"},week:{dow:0,doy:6}})}(e.r(166540))},775136,(e,a,t)=>{e.e,function(e){"use strict";var a="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),t="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),s=[/^jan/i,/^feb/i,/^(maart|mrt\.?)$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],n=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;e.defineLocale("nl-be",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,s){return e?/-MMM-/.test(s)?t[e.month()]:a[e.month()]:a},monthsRegex:n,monthsShortRegex:n,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:s,longMonthsParse:s,shortMonthsParse:s,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(e.r(166540))},618264,(e,a,t)=>{e.e,function(e){"use strict";var a="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),t="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),s=[/^jan/i,/^feb/i,/^(maart|mrt\.?)$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],n=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;e.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,s){return e?/-MMM-/.test(s)?t[e.month()]:a[e.month()]:a},monthsRegex:n,monthsShortRegex:n,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:s,longMonthsParse:s,shortMonthsParse:s,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",w:"één week",ww:"%d weken",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(e.r(166540))},876976,(e,a,t)=>{e.e,e.r(166540).defineLocale("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:!0,weekdays:"sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"su._må._ty._on._to._fr._lau.".split("_"),weekdaysMin:"su_må_ty_on_to_fr_la".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I går klokka] LT",lastWeek:"[Føregåande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s sidan",s:"nokre sekund",ss:"%d sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",w:"ei veke",ww:"%d veker",M:"ein månad",MM:"%d månader",y:"eit år",yy:"%d år"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})},225313,(e,a,t)=>{e.e,e.r(166540).defineLocale("oc-lnc",{months:{standalone:"genièr_febrièr_març_abril_mai_junh_julhet_agost_setembre_octòbre_novembre_decembre".split("_"),format:"de genièr_de febrièr_de març_d'abril_de mai_de junh_de julhet_d'agost_de setembre_d'octòbre_de novembre_de decembre".split("_"),isFormat:/D[oD]?(\s)+MMMM/},monthsShort:"gen._febr._març_abr._mai_junh_julh._ago._set._oct._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"dimenge_diluns_dimars_dimècres_dijòus_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dm._dc._dj._dv._ds.".split("_"),weekdaysMin:"dg_dl_dm_dc_dj_dv_ds".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [de] YYYY",ll:"D MMM YYYY",LLL:"D MMMM [de] YYYY [a] H:mm",lll:"D MMM YYYY, H:mm",LLLL:"dddd D MMMM [de] YYYY [a] H:mm",llll:"ddd D MMM YYYY, H:mm"},calendar:{sameDay:"[uèi a] LT",nextDay:"[deman a] LT",nextWeek:"dddd [a] LT",lastDay:"[ièr a] LT",lastWeek:"dddd [passat a] LT",sameElse:"L"},relativeTime:{future:"d'aquí %s",past:"fa %s",s:"unas segondas",ss:"%d segondas",m:"una minuta",mm:"%d minutas",h:"una ora",hh:"%d oras",d:"un jorn",dd:"%d jorns",M:"un mes",MM:"%d meses",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(r|n|t|è|a)/,ordinal:function(e,a){var t=1===e?"r":2===e?"n":3===e?"r":4===e?"t":"è";return("w"===a||"W"===a)&&(t="a"),e+t},week:{dow:1,doy:4}})},368431,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"੧",2:"੨",3:"੩",4:"੪",5:"੫",6:"੬",7:"੭",8:"੮",9:"੯",0:"੦"},t={"੧":"1","੨":"2","੩":"3","੪":"4","੫":"5","੬":"6","੭":"7","੮":"8","੯":"9","੦":"0"};e.defineLocale("pa-in",{months:"ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ".split("_"),monthsShort:"ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ".split("_"),weekdays:"ਐਤਵਾਰ_ਸੋਮਵਾਰ_ਮੰਗਲਵਾਰ_ਬੁਧਵਾਰ_ਵੀਰਵਾਰ_ਸ਼ੁੱਕਰਵਾਰ_ਸ਼ਨੀਚਰਵਾਰ".split("_"),weekdaysShort:"ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ".split("_"),weekdaysMin:"ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ".split("_"),longDateFormat:{LT:"A h:mm ਵਜੇ",LTS:"A h:mm:ss ਵਜੇ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ਵਜੇ",LLLL:"dddd, D MMMM YYYY, A h:mm ਵਜੇ"},calendar:{sameDay:"[ਅਜ] LT",nextDay:"[ਕਲ] LT",nextWeek:"[ਅਗਲਾ] dddd, LT",lastDay:"[ਕਲ] LT",lastWeek:"[ਪਿਛਲੇ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ਵਿੱਚ",past:"%s ਪਿਛਲੇ",s:"ਕੁਝ ਸਕਿੰਟ",ss:"%d ਸਕਿੰਟ",m:"ਇਕ ਮਿੰਟ",mm:"%d ਮਿੰਟ",h:"ਇੱਕ ਘੰਟਾ",hh:"%d ਘੰਟੇ",d:"ਇੱਕ ਦਿਨ",dd:"%d ਦਿਨ",M:"ਇੱਕ ਮਹੀਨਾ",MM:"%d ਮਹੀਨੇ",y:"ਇੱਕ ਸਾਲ",yy:"%d ਸਾਲ"},preparse:function(e){return e.replace(/[੧੨੩੪੫੬੭੮੯੦]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/ਰਾਤ|ਸਵੇਰ|ਦੁਪਹਿਰ|ਸ਼ਾਮ/,meridiemHour:function(e,a){return(12===e&&(e=0),"ਰਾਤ"===a)?e<4?e:e+12:"ਸਵੇਰ"===a?e:"ਦੁਪਹਿਰ"===a?e>=10?e:e+12:"ਸ਼ਾਮ"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"ਰਾਤ":e<10?"ਸਵੇਰ":e<17?"ਦੁਪਹਿਰ":e<20?"ਸ਼ਾਮ":"ਰਾਤ"},week:{dow:0,doy:6}})}(e.r(166540))},657968,(e,a,t)=>{e.e,function(e){"use strict";var a="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),t="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_"),s=[/^sty/i,/^lut/i,/^mar/i,/^kwi/i,/^maj/i,/^cze/i,/^lip/i,/^sie/i,/^wrz/i,/^paź/i,/^lis/i,/^gru/i];function n(e){return e%10<5&&e%10>1&&~~(e/10)%10!=1}function r(e,a,t){var s=e+" ";switch(t){case"ss":return s+(n(e)?"sekundy":"sekund");case"m":return a?"minuta":"minutę";case"mm":return s+(n(e)?"minuty":"minut");case"h":return a?"godzina":"godzinę";case"hh":return s+(n(e)?"godziny":"godzin");case"ww":return s+(n(e)?"tygodnie":"tygodni");case"MM":return s+(n(e)?"miesiące":"miesięcy");case"yy":return s+(n(e)?"lata":"lat")}}e.defineLocale("pl",{months:function(e,s){return e?/D MMMM/.test(s)?t[e.month()]:a[e.month()]:a},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),monthsParse:s,longMonthsParse:s,shortMonthsParse:s,weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"ndz_pon_wt_śr_czw_pt_sob".split("_"),weekdaysMin:"Nd_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:function(){switch(this.day()){case 0:return"[W niedzielę o] LT";case 2:return"[We wtorek o] LT";case 3:return"[W środę o] LT";case 6:return"[W sobotę o] LT";default:return"[W] dddd [o] LT"}},lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",ss:r,m:r,mm:r,h:r,hh:r,d:"1 dzień",dd:"%d dni",w:"tydzień",ww:r,M:"miesiąc",MM:r,y:"rok",yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},736919,(e,a,t)=>{e.e,e.r(166540).defineLocale("pt-br",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"do_2ª_3ª_4ª_5ª_6ª_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY [às] HH:mm"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"poucos segundos",ss:"%d segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",invalidDate:"Data inválida"})},493062,(e,a,t)=>{e.e,e.r(166540).defineLocale("pt",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_Sáb".split("_"),weekdaysMin:"Do_2ª_3ª_4ª_5ª_6ª_Sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY HH:mm"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"segundos",ss:"%d segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",w:"uma semana",ww:"%d semanas",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})},869377,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t){var s=" ";return(e%100>=20||e>=100&&e%100==0)&&(s=" de "),e+s+({ss:"secunde",mm:"minute",hh:"ore",dd:"zile",ww:"săptămâni",MM:"luni",yy:"ani"})[t]}e.defineLocale("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._feb._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",ss:a,m:"un minut",mm:a,h:"o oră",hh:a,d:"o zi",dd:a,w:"o săptămână",ww:a,M:"o lună",MM:a,y:"un an",yy:a},week:{dow:1,doy:7}})}(e.r(166540))},498262,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t){var s,n,r;return"m"===t?a?"минута":"минуту":e+" "+(s=({ss:a?"секунда_секунды_секунд":"секунду_секунды_секунд",mm:a?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",ww:"неделя_недели_недель",MM:"месяц_месяца_месяцев",yy:"год_года_лет"})[t],n=+e,r=s.split("_"),n%10==1&&n%100!=11?r[0]:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?r[1]:r[2])}var t=[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[йя]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i];e.defineLocale("ru",{months:{format:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_"),standalone:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_")},monthsShort:{format:"янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.".split("_"),standalone:"янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.".split("_")},weekdays:{standalone:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),format:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_"),isFormat:/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?] ?dddd/},weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:t,longMonthsParse:t,shortMonthsParse:t,monthsRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,monthsShortRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,monthsStrictRegex:/^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i,monthsShortStrictRegex:/^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., H:mm",LLLL:"dddd, D MMMM YYYY г., H:mm"},calendar:{sameDay:"[Сегодня, в] LT",nextDay:"[Завтра, в] LT",lastDay:"[Вчера, в] LT",nextWeek:function(e){if(e.week()!==this.week())switch(this.day()){case 0:return"[В следующее] dddd, [в] LT";case 1:case 2:case 4:return"[В следующий] dddd, [в] LT";case 3:case 5:case 6:return"[В следующую] dddd, [в] LT"}else if(2===this.day())return"[Во] dddd, [в] LT";else return"[В] dddd, [в] LT"},lastWeek:function(e){if(e.week()!==this.week())switch(this.day()){case 0:return"[В прошлое] dddd, [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd, [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd, [в] LT"}else if(2===this.day())return"[Во] dddd, [в] LT";else return"[В] dddd, [в] LT"},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",ss:a,m:a,mm:a,h:"час",hh:a,d:"день",dd:a,w:"неделя",ww:a,M:"месяц",MM:a,y:"год",yy:a},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(e){return/^(дня|вечера)$/.test(e)},meridiem:function(e,a,t){return e<4?"ночи":e<12?"утра":e<17?"дня":"вечера"},dayOfMonthOrdinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(e,a){switch(a){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:4}})}(e.r(166540))},137750,(e,a,t)=>{e.e,function(e){"use strict";var a=["جنوري","فيبروري","مارچ","اپريل","مئي","جون","جولاءِ","آگسٽ","سيپٽمبر","آڪٽوبر","نومبر","ڊسمبر"],t=["آچر","سومر","اڱارو","اربع","خميس","جمع","ڇنڇر"];e.defineLocale("sd",{months:a,monthsShort:a,weekdays:t,weekdaysShort:t,weekdaysMin:t,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd، D MMMM YYYY HH:mm"},meridiemParse:/صبح|شام/,isPM:function(e){return"شام"===e},meridiem:function(e,a,t){return e<12?"صبح":"شام"},calendar:{sameDay:"[اڄ] LT",nextDay:"[سڀاڻي] LT",nextWeek:"dddd [اڳين هفتي تي] LT",lastDay:"[ڪالهه] LT",lastWeek:"[گزريل هفتي] dddd [تي] LT",sameElse:"L"},relativeTime:{future:"%s پوء",past:"%s اڳ",s:"چند سيڪنڊ",ss:"%d سيڪنڊ",m:"هڪ منٽ",mm:"%d منٽ",h:"هڪ ڪلاڪ",hh:"%d ڪلاڪ",d:"هڪ ڏينهن",dd:"%d ڏينهن",M:"هڪ مهينو",MM:"%d مهينا",y:"هڪ سال",yy:"%d سال"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:1,doy:4}})}(e.r(166540))},455308,(e,a,t)=>{e.e,e.r(166540).defineLocale("se",{months:"ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu".split("_"),monthsShort:"ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov".split("_"),weekdays:"sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat".split("_"),weekdaysShort:"sotn_vuos_maŋ_gask_duor_bear_láv".split("_"),weekdaysMin:"s_v_m_g_d_b_L".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"MMMM D. [b.] YYYY",LLL:"MMMM D. [b.] YYYY [ti.] HH:mm",LLLL:"dddd, MMMM D. [b.] YYYY [ti.] HH:mm"},calendar:{sameDay:"[otne ti] LT",nextDay:"[ihttin ti] LT",nextWeek:"dddd [ti] LT",lastDay:"[ikte ti] LT",lastWeek:"[ovddit] dddd [ti] LT",sameElse:"L"},relativeTime:{future:"%s geažes",past:"maŋit %s",s:"moadde sekunddat",ss:"%d sekunddat",m:"okta minuhta",mm:"%d minuhtat",h:"okta diimmu",hh:"%d diimmut",d:"okta beaivi",dd:"%d beaivvit",M:"okta mánnu",MM:"%d mánut",y:"okta jahki",yy:"%d jagit"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})},303364,(e,a,t)=>{e.e,e.r(166540).defineLocale("si",{months:"ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්".split("_"),monthsShort:"ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ".split("_"),weekdays:"ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා".split("_"),weekdaysShort:"ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන".split("_"),weekdaysMin:"ඉ_ස_අ_බ_බ්‍ර_සි_සෙ".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"a h:mm",LTS:"a h:mm:ss",L:"YYYY/MM/DD",LL:"YYYY MMMM D",LLL:"YYYY MMMM D, a h:mm",LLLL:"YYYY MMMM D [වැනි] dddd, a h:mm:ss"},calendar:{sameDay:"[අද] LT[ට]",nextDay:"[හෙට] LT[ට]",nextWeek:"dddd LT[ට]",lastDay:"[ඊයේ] LT[ට]",lastWeek:"[පසුගිය] dddd LT[ට]",sameElse:"L"},relativeTime:{future:"%sකින්",past:"%sකට පෙර",s:"තත්පර කිහිපය",ss:"තත්පර %d",m:"මිනිත්තුව",mm:"මිනිත්තු %d",h:"පැය",hh:"පැය %d",d:"දිනය",dd:"දින %d",M:"මාසය",MM:"මාස %d",y:"වසර",yy:"වසර %d"},dayOfMonthOrdinalParse:/\d{1,2} වැනි/,ordinal:function(e){return e+" වැනි"},meridiemParse:/පෙර වරු|පස් වරු|පෙ.ව|ප.ව./,isPM:function(e){return"ප.ව."===e||"පස් වරු"===e},meridiem:function(e,a,t){return e>11?t?"ප.ව.":"පස් වරු":t?"පෙ.ව.":"පෙර වරු"}})},195013,(e,a,t)=>{e.e,function(e){"use strict";function a(e){return e>1&&e<5}function t(e,t,s,n){var r=e+" ";switch(s){case"s":return t||n?"pár sekúnd":"pár sekundami";case"ss":if(t||n)return r+(a(e)?"sekundy":"sekúnd");return r+"sekundami";case"m":return t?"minúta":n?"minútu":"minútou";case"mm":if(t||n)return r+(a(e)?"minúty":"minút");return r+"minútami";case"h":return t?"hodina":n?"hodinu":"hodinou";case"hh":if(t||n)return r+(a(e)?"hodiny":"hodín");return r+"hodinami";case"d":return t||n?"deň":"dňom";case"dd":if(t||n)return r+(a(e)?"dni":"dní");return r+"dňami";case"M":return t||n?"mesiac":"mesiacom";case"MM":if(t||n)return r+(a(e)?"mesiace":"mesiacov");return r+"mesiacmi";case"y":return t||n?"rok":"rokom";case"yy":if(t||n)return r+(a(e)?"roky":"rokov");return r+"rokmi"}}e.defineLocale("sk",{months:"január_február_marec_apríl_máj_jún_júl_august_september_október_november_december".split("_"),monthsShort:"jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_"),weekdays:"nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_št_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_št_pi_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo štvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulú nedeľu o] LT";case 1:case 2:case 4:case 5:return"[minulý] dddd [o] LT";case 3:return"[minulú stredu o] LT";case 6:return"[minulú sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},575550,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n=e+" ";switch(t){case"s":return a||s?"nekaj sekund":"nekaj sekundami";case"ss":return 1===e?n+=a?"sekundo":"sekundi":2===e?n+=a||s?"sekundi":"sekundah":e<5?n+=a||s?"sekunde":"sekundah":n+="sekund",n;case"m":return a?"ena minuta":"eno minuto";case"mm":return 1===e?n+=a?"minuta":"minuto":2===e?n+=a||s?"minuti":"minutama":e<5?n+=a||s?"minute":"minutami":n+=a||s?"minut":"minutami",n;case"h":return a?"ena ura":"eno uro";case"hh":return 1===e?n+=a?"ura":"uro":2===e?n+=a||s?"uri":"urama":e<5?n+=a||s?"ure":"urami":n+=a||s?"ur":"urami",n;case"d":return a||s?"en dan":"enim dnem";case"dd":return 1===e?n+=a||s?"dan":"dnem":2===e?n+=a||s?"dni":"dnevoma":n+=a||s?"dni":"dnevi",n;case"M":return a||s?"en mesec":"enim mesecem";case"MM":return 1===e?n+=a||s?"mesec":"mesecem":2===e?n+=a||s?"meseca":"mesecema":e<5?n+=a||s?"mesece":"meseci":n+=a||s?"mesecev":"meseci",n;case"y":return a||s?"eno leto":"enim letom";case"yy":return 1===e?n+=a||s?"leto":"letom":2===e?n+=a||s?"leti":"letoma":e<5?n+=a||s?"leta":"leti":n+=a||s?"let":"leti",n}}e.defineLocale("sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:return"[prejšnjo] [nedeljo] [ob] LT";case 3:return"[prejšnjo] [sredo] [ob] LT";case 6:return"[prejšnjo] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[prejšnji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"pred %s",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(e.r(166540))},813013,(e,a,t)=>{e.e,e.r(166540).defineLocale("sq",{months:"Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor".split("_"),monthsShort:"Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj".split("_"),weekdays:"E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë".split("_"),weekdaysShort:"Die_Hën_Mar_Mër_Enj_Pre_Sht".split("_"),weekdaysMin:"D_H_Ma_Më_E_P_Sh".split("_"),weekdaysParseExact:!0,meridiemParse:/PD|MD/,isPM:function(e){return"M"===e.charAt(0)},meridiem:function(e,a,t){return e<12?"PD":"MD"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Sot në] LT",nextDay:"[Nesër në] LT",nextWeek:"dddd [në] LT",lastDay:"[Dje në] LT",lastWeek:"dddd [e kaluar në] LT",sameElse:"L"},relativeTime:{future:"në %s",past:"%s më parë",s:"disa sekonda",ss:"%d sekonda",m:"një minutë",mm:"%d minuta",h:"një orë",hh:"%d orë",d:"një ditë",dd:"%d ditë",M:"një muaj",MM:"%d muaj",y:"një vit",yy:"%d vite"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})},423039,(e,a,t)=>{e.e,function(e){"use strict";var a={words:{ss:["секунда","секунде","секунди"],m:["један минут","једног минута"],mm:["минут","минута","минута"],h:["један сат","једног сата"],hh:["сат","сата","сати"],d:["један дан","једног дана"],dd:["дан","дана","дана"],M:["један месец","једног месеца"],MM:["месец","месеца","месеци"],y:["једну годину","једне године"],yy:["годину","године","година"]},correctGrammaticalCase:function(e,a){return e%10>=1&&e%10<=4&&(e%100<10||e%100>=20)?e%10==1?a[0]:a[1]:a[2]},translate:function(e,t,s,n){var r,d=a.words[s];return 1===s.length?"y"===s&&t?"једна година":n||t?d[0]:d[1]:(r=a.correctGrammaticalCase(e,d),"yy"===s&&t&&"годину"===r)?e+" година":e+" "+r}};e.defineLocale("sr-cyrl",{months:"јануар_фебруар_март_април_мај_јун_јул_август_септембар_октобар_новембар_децембар".split("_"),monthsShort:"јан._феб._мар._апр._мај_јун_јул_авг._сеп._окт._нов._дец.".split("_"),monthsParseExact:!0,weekdays:"недеља_понедељак_уторак_среда_четвртак_петак_субота".split("_"),weekdaysShort:"нед._пон._уто._сре._чет._пет._суб.".split("_"),weekdaysMin:"не_по_ут_ср_че_пе_су".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[данас у] LT",nextDay:"[сутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [среду] [у] LT";case 6:return"[у] [суботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){return["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [среде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [суботе] [у] LT"][this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико секунди",ss:a.translate,m:a.translate,mm:a.translate,h:a.translate,hh:a.translate,d:a.translate,dd:a.translate,M:a.translate,MM:a.translate,y:a.translate,yy:a.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(e.r(166540))},654301,(e,a,t)=>{e.e,function(e){"use strict";var a={words:{ss:["sekunda","sekunde","sekundi"],m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],d:["jedan dan","jednog dana"],dd:["dan","dana","dana"],M:["jedan mesec","jednog meseca"],MM:["mesec","meseca","meseci"],y:["jednu godinu","jedne godine"],yy:["godinu","godine","godina"]},correctGrammaticalCase:function(e,a){return e%10>=1&&e%10<=4&&(e%100<10||e%100>=20)?e%10==1?a[0]:a[1]:a[2]},translate:function(e,t,s,n){var r,d=a.words[s];return 1===s.length?"y"===s&&t?"jedna godina":n||t?d[0]:d[1]:(r=a.correctGrammaticalCase(e,d),"yy"===s&&t&&"godinu"===r)?e+" godina":e+" "+r}};e.defineLocale("sr",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sre._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){return["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"][this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",ss:a.translate,m:a.translate,mm:a.translate,h:a.translate,hh:a.translate,d:a.translate,dd:a.translate,M:a.translate,MM:a.translate,y:a.translate,yy:a.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(e.r(166540))},492305,(e,a,t)=>{e.e,e.r(166540).defineLocale("ss",{months:"Bhimbidvwane_Indlovana_Indlov'lenkhulu_Mabasa_Inkhwekhweti_Inhlaba_Kholwane_Ingci_Inyoni_Imphala_Lweti_Ingongoni".split("_"),monthsShort:"Bhi_Ina_Inu_Mab_Ink_Inh_Kho_Igc_Iny_Imp_Lwe_Igo".split("_"),weekdays:"Lisontfo_Umsombuluko_Lesibili_Lesitsatfu_Lesine_Lesihlanu_Umgcibelo".split("_"),weekdaysShort:"Lis_Umb_Lsb_Les_Lsi_Lsh_Umg".split("_"),weekdaysMin:"Li_Us_Lb_Lt_Ls_Lh_Ug".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Namuhla nga] LT",nextDay:"[Kusasa nga] LT",nextWeek:"dddd [nga] LT",lastDay:"[Itolo nga] LT",lastWeek:"dddd [leliphelile] [nga] LT",sameElse:"L"},relativeTime:{future:"nga %s",past:"wenteka nga %s",s:"emizuzwana lomcane",ss:"%d mzuzwana",m:"umzuzu",mm:"%d emizuzu",h:"lihora",hh:"%d emahora",d:"lilanga",dd:"%d emalanga",M:"inyanga",MM:"%d tinyanga",y:"umnyaka",yy:"%d iminyaka"},meridiemParse:/ekuseni|emini|entsambama|ebusuku/,meridiem:function(e,a,t){return e<11?"ekuseni":e<15?"emini":e<19?"entsambama":"ebusuku"},meridiemHour:function(e,a){return(12===e&&(e=0),"ekuseni"===a)?e:"emini"===a?e>=11?e:e+12:"entsambama"===a||"ebusuku"===a?0===e?0:e+12:void 0},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:"%d",week:{dow:1,doy:4}})},937057,(e,a,t)=>{e.e,e.r(166540).defineLocale("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [kl.] HH:mm",LLLL:"dddd D MMMM YYYY [kl.] HH:mm",lll:"D MMM YYYY HH:mm",llll:"ddd D MMM YYYY HH:mm"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"[På] dddd LT",lastWeek:"[I] dddd[s] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",ss:"%d sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},dayOfMonthOrdinalParse:/\d{1,2}(\:e|\:a)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?":e":1===a||2===a?":a":":e";return e+t},week:{dow:1,doy:4}})},771953,(e,a,t)=>{e.e,e.r(166540).defineLocale("sw",{months:"Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des".split("_"),weekdays:"Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi".split("_"),weekdaysShort:"Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos".split("_"),weekdaysMin:"J2_J3_J4_J5_Al_Ij_J1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"hh:mm A",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[leo saa] LT",nextDay:"[kesho saa] LT",nextWeek:"[wiki ijayo] dddd [saat] LT",lastDay:"[jana] LT",lastWeek:"[wiki iliyopita] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s baadaye",past:"tokea %s",s:"hivi punde",ss:"sekunde %d",m:"dakika moja",mm:"dakika %d",h:"saa limoja",hh:"masaa %d",d:"siku moja",dd:"siku %d",M:"mwezi mmoja",MM:"miezi %d",y:"mwaka mmoja",yy:"miaka %d"},week:{dow:1,doy:7}})},271953,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"௧",2:"௨",3:"௩",4:"௪",5:"௫",6:"௬",7:"௭",8:"௮",9:"௯",0:"௦"},t={"௧":"1","௨":"2","௩":"3","௪":"4","௫":"5","௬":"6","௭":"7","௮":"8","௯":"9","௦":"0"};e.defineLocale("ta",{months:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),monthsShort:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),weekdays:"ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை".split("_"),weekdaysShort:"ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி".split("_"),weekdaysMin:"ஞா_தி_செ_பு_வி_வெ_ச".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, HH:mm",LLLL:"dddd, D MMMM YYYY, HH:mm"},calendar:{sameDay:"[இன்று] LT",nextDay:"[நாளை] LT",nextWeek:"dddd, LT",lastDay:"[நேற்று] LT",lastWeek:"[கடந்த வாரம்] dddd, LT",sameElse:"L"},relativeTime:{future:"%s இல்",past:"%s முன்",s:"ஒரு சில விநாடிகள்",ss:"%d விநாடிகள்",m:"ஒரு நிமிடம்",mm:"%d நிமிடங்கள்",h:"ஒரு மணி நேரம்",hh:"%d மணி நேரம்",d:"ஒரு நாள்",dd:"%d நாட்கள்",M:"ஒரு மாதம்",MM:"%d மாதங்கள்",y:"ஒரு வருடம்",yy:"%d ஆண்டுகள்"},dayOfMonthOrdinalParse:/\d{1,2}வது/,ordinal:function(e){return e+"வது"},preparse:function(e){return e.replace(/[௧௨௩௪௫௬௭௮௯௦]/g,function(e){return t[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiemParse:/யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/,meridiem:function(e,a,t){if(e<2)return" யாமம்";if(e<6)return" வைகறை";if(e<10)return" காலை";if(e<14)return" நண்பகல்";if(e<18)return" எற்பாடு";else if(e<22)return" மாலை";else return" யாமம்"},meridiemHour:function(e,a){return(12===e&&(e=0),"யாமம்"===a)?e<2?e:e+12:"வைகறை"===a||"காலை"===a?e:"நண்பகல்"===a?e>=10?e:e+12:e+12},week:{dow:0,doy:6}})}(e.r(166540))},749731,(e,a,t)=>{e.e,e.r(166540).defineLocale("te",{months:"జనవరి_ఫిబ్రవరి_మార్చి_ఏప్రిల్_మే_జూన్_జులై_ఆగస్టు_సెప్టెంబర్_అక్టోబర్_నవంబర్_డిసెంబర్".split("_"),monthsShort:"జన._ఫిబ్ర._మార్చి_ఏప్రి._మే_జూన్_జులై_ఆగ._సెప్._అక్టో._నవ._డిసె.".split("_"),monthsParseExact:!0,weekdays:"ఆదివారం_సోమవారం_మంగళవారం_బుధవారం_గురువారం_శుక్రవారం_శనివారం".split("_"),weekdaysShort:"ఆది_సోమ_మంగళ_బుధ_గురు_శుక్ర_శని".split("_"),weekdaysMin:"ఆ_సో_మం_బు_గు_శు_శ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[నేడు] LT",nextDay:"[రేపు] LT",nextWeek:"dddd, LT",lastDay:"[నిన్న] LT",lastWeek:"[గత] dddd, LT",sameElse:"L"},relativeTime:{future:"%s లో",past:"%s క్రితం",s:"కొన్ని క్షణాలు",ss:"%d సెకన్లు",m:"ఒక నిమిషం",mm:"%d నిమిషాలు",h:"ఒక గంట",hh:"%d గంటలు",d:"ఒక రోజు",dd:"%d రోజులు",M:"ఒక నెల",MM:"%d నెలలు",y:"ఒక సంవత్సరం",yy:"%d సంవత్సరాలు"},dayOfMonthOrdinalParse:/\d{1,2}వ/,ordinal:"%dవ",meridiemParse:/రాత్రి|ఉదయం|మధ్యాహ్నం|సాయంత్రం/,meridiemHour:function(e,a){return(12===e&&(e=0),"రాత్రి"===a)?e<4?e:e+12:"ఉదయం"===a?e:"మధ్యాహ్నం"===a?e>=10?e:e+12:"సాయంత్రం"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"రాత్రి":e<10?"ఉదయం":e<17?"మధ్యాహ్నం":e<20?"సాయంత్రం":"రాత్రి"},week:{dow:0,doy:6}})},165002,(e,a,t)=>{e.e,e.r(166540).defineLocale("tet",{months:"Janeiru_Fevereiru_Marsu_Abril_Maiu_Juñu_Jullu_Agustu_Setembru_Outubru_Novembru_Dezembru".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingu_Segunda_Tersa_Kuarta_Kinta_Sesta_Sabadu".split("_"),weekdaysShort:"Dom_Seg_Ters_Kua_Kint_Sest_Sab".split("_"),weekdaysMin:"Do_Seg_Te_Ku_Ki_Ses_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ohin iha] LT",nextDay:"[Aban iha] LT",nextWeek:"dddd [iha] LT",lastDay:"[Horiseik iha] LT",lastWeek:"dddd [semana kotuk] [iha] LT",sameElse:"L"},relativeTime:{future:"iha %s",past:"%s liuba",s:"segundu balun",ss:"segundu %d",m:"minutu ida",mm:"minutu %d",h:"oras ida",hh:"oras %d",d:"loron ida",dd:"loron %d",M:"fulan ida",MM:"fulan %d",y:"tinan ida",yy:"tinan %d"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}})},580104,(e,a,t)=>{e.e,function(e){"use strict";var a={0:"-ум",1:"-ум",2:"-юм",3:"-юм",4:"-ум",5:"-ум",6:"-ум",7:"-ум",8:"-ум",9:"-ум",10:"-ум",12:"-ум",13:"-ум",20:"-ум",30:"-юм",40:"-ум",50:"-ум",60:"-ум",70:"-ум",80:"-ум",90:"-ум",100:"-ум"};e.defineLocale("tg",{months:{format:"январи_феврали_марти_апрели_майи_июни_июли_августи_сентябри_октябри_ноябри_декабри".split("_"),standalone:"январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр".split("_")},monthsShort:"янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"),weekdays:"якшанбе_душанбе_сешанбе_чоршанбе_панҷшанбе_ҷумъа_шанбе".split("_"),weekdaysShort:"яшб_дшб_сшб_чшб_пшб_ҷум_шнб".split("_"),weekdaysMin:"яш_дш_сш_чш_пш_ҷм_шб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Имрӯз соати] LT",nextDay:"[Фардо соати] LT",lastDay:"[Дирӯз соати] LT",nextWeek:"dddd[и] [ҳафтаи оянда соати] LT",lastWeek:"dddd[и] [ҳафтаи гузашта соати] LT",sameElse:"L"},relativeTime:{future:"баъди %s",past:"%s пеш",s:"якчанд сония",m:"як дақиқа",mm:"%d дақиқа",h:"як соат",hh:"%d соат",d:"як рӯз",dd:"%d рӯз",M:"як моҳ",MM:"%d моҳ",y:"як сол",yy:"%d сол"},meridiemParse:/шаб|субҳ|рӯз|бегоҳ/,meridiemHour:function(e,a){return(12===e&&(e=0),"шаб"===a)?e<4?e:e+12:"субҳ"===a?e:"рӯз"===a?e>=11?e:e+12:"бегоҳ"===a?e+12:void 0},meridiem:function(e,a,t){return e<4?"шаб":e<11?"субҳ":e<16?"рӯз":e<19?"бегоҳ":"шаб"},dayOfMonthOrdinalParse:/\d{1,2}-(ум|юм)/,ordinal:function(e){return e+(a[e]||a[e%10]||a[e>=100?100:null])},week:{dow:1,doy:7}})}(e.r(166540))},768313,(e,a,t)=>{e.e,e.r(166540).defineLocale("th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"ม.ค._ก.พ._มี.ค._เม.ย._พ.ค._มิ.ย._ก.ค._ส.ค._ก.ย._ต.ค._พ.ย._ธ.ค.".split("_"),monthsParseExact:!0,weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา H:mm",LLLL:"วันddddที่ D MMMM YYYY เวลา H:mm"},meridiemParse:/ก่อนเที่ยง|หลังเที่ยง/,isPM:function(e){return"หลังเที่ยง"===e},meridiem:function(e,a,t){return e<12?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",ss:"%d วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",w:"1 สัปดาห์",ww:"%d สัปดาห์",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}})},291616,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"'inji",5:"'inji",8:"'inji",70:"'inji",80:"'inji",2:"'nji",7:"'nji",20:"'nji",50:"'nji",3:"'ünji",4:"'ünji",100:"'ünji",6:"'njy",9:"'unjy",10:"'unjy",30:"'unjy",60:"'ynjy",90:"'ynjy"};e.defineLocale("tk",{months:"Ýanwar_Fewral_Mart_Aprel_Maý_Iýun_Iýul_Awgust_Sentýabr_Oktýabr_Noýabr_Dekabr".split("_"),monthsShort:"Ýan_Few_Mar_Apr_Maý_Iýn_Iýl_Awg_Sen_Okt_Noý_Dek".split("_"),weekdays:"Ýekşenbe_Duşenbe_Sişenbe_Çarşenbe_Penşenbe_Anna_Şenbe".split("_"),weekdaysShort:"Ýek_Duş_Siş_Çar_Pen_Ann_Şen".split("_"),weekdaysMin:"Ýk_Dş_Sş_Çr_Pn_An_Şn".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugün sagat] LT",nextDay:"[ertir sagat] LT",nextWeek:"[indiki] dddd [sagat] LT",lastDay:"[düýn] LT",lastWeek:"[geçen] dddd [sagat] LT",sameElse:"L"},relativeTime:{future:"%s soň",past:"%s öň",s:"birnäçe sekunt",m:"bir minut",mm:"%d minut",h:"bir sagat",hh:"%d sagat",d:"bir gün",dd:"%d gün",M:"bir aý",MM:"%d aý",y:"bir ýyl",yy:"%d ýyl"},ordinal:function(e,t){switch(t){case"d":case"D":case"Do":case"DD":return e;default:if(0===e)return e+"'unjy";var s=e%10;return e+(a[s]||a[e%100-s]||a[e>=100?100:null])}},week:{dow:1,doy:7}})}(e.r(166540))},317895,(e,a,t)=>{e.e,e.r(166540).defineLocale("tl-ph",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"LT [ngayong araw]",nextDay:"[Bukas ng] LT",nextWeek:"LT [sa susunod na] dddd",lastDay:"LT [kahapon]",lastWeek:"LT [noong nakaraang] dddd",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",ss:"%d segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})},955799,(e,a,t)=>{e.e,function(e){"use strict";var a="pagh_wa’_cha’_wej_loS_vagh_jav_Soch_chorgh_Hut".split("_");function t(e,t,s,n){var r,d,i,_,o,m=(d=Math.floor((r=e)%1e3/100),i=Math.floor(r%100/10),_=r%10,o="",d>0&&(o+=a[d]+"vatlh"),i>0&&(o+=(""!==o?" ":"")+a[i]+"maH"),_>0&&(o+=(""!==o?" ":"")+a[_]),""===o?"pagh":o);switch(s){case"ss":return m+" lup";case"mm":return m+" tup";case"hh":return m+" rep";case"dd":return m+" jaj";case"MM":return m+" jar";case"yy":return m+" DIS"}}e.defineLocale("tlh",{months:"tera’ jar wa’_tera’ jar cha’_tera’ jar wej_tera’ jar loS_tera’ jar vagh_tera’ jar jav_tera’ jar Soch_tera’ jar chorgh_tera’ jar Hut_tera’ jar wa’maH_tera’ jar wa’maH wa’_tera’ jar wa’maH cha’".split("_"),monthsShort:"jar wa’_jar cha’_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar wa’maH_jar wa’maH wa’_jar wa’maH cha’".split("_"),monthsParseExact:!0,weekdays:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysShort:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysMin:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[DaHjaj] LT",nextDay:"[wa’leS] LT",nextWeek:"LLL",lastDay:"[wa’Hu’] LT",lastWeek:"LLL",sameElse:"L"},relativeTime:{future:function(e){var a=e;return -1!==e.indexOf("jaj")?a.slice(0,-3)+"leS":-1!==e.indexOf("jar")?a.slice(0,-3)+"waQ":-1!==e.indexOf("DIS")?a.slice(0,-3)+"nem":a+" pIq"},past:function(e){var a=e;return -1!==e.indexOf("jaj")?a.slice(0,-3)+"Hu’":-1!==e.indexOf("jar")?a.slice(0,-3)+"wen":-1!==e.indexOf("DIS")?a.slice(0,-3)+"ben":a+" ret"},s:"puS lup",ss:t,m:"wa’ tup",mm:t,h:"wa’ rep",hh:t,d:"wa’ jaj",dd:t,M:"wa’ jar",MM:t,y:"wa’ DIS",yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},515252,(e,a,t)=>{e.e,function(e){"use strict";var a={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"};e.defineLocale("tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pzt_Sal_Çar_Per_Cum_Cmt".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),meridiem:function(e,a,t){return e<12?t?"öö":"ÖÖ":t?"ös":"ÖS"},meridiemParse:/öö|ÖÖ|ös|ÖS/,isPM:function(e){return"ös"===e||"ÖS"===e},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[gelecek] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",ss:"%d saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",w:"bir hafta",ww:"%d hafta",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinal:function(e,t){switch(t){case"d":case"D":case"Do":case"DD":return e;default:if(0===e)return e+"'ıncı";var s=e%10;return e+(a[s]||a[e%100-s]||a[e>=100?100:null])}},week:{dow:1,doy:7}})}(e.r(166540))},568087,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t,s){var n={s:["viensas secunds","'iensas secunds"],ss:[e+" secunds",""+e+" secunds"],m:["'n míut","'iens míut"],mm:[e+" míuts",""+e+" míuts"],h:["'n þora","'iensa þora"],hh:[e+" þoras",""+e+" þoras"],d:["'n ziua","'iensa ziua"],dd:[e+" ziuas",""+e+" ziuas"],M:["'n mes","'iens mes"],MM:[e+" mesen",""+e+" mesen"],y:["'n ar","'iens ar"],yy:[e+" ars",""+e+" ars"]};return s||a?n[t][0]:n[t][1]}e.defineLocale("tzl",{months:"Januar_Fevraglh_Març_Avrïu_Mai_Gün_Julia_Guscht_Setemvar_Listopäts_Noemvar_Zecemvar".split("_"),monthsShort:"Jan_Fev_Mar_Avr_Mai_Gün_Jul_Gus_Set_Lis_Noe_Zec".split("_"),weekdays:"Súladi_Lúneçi_Maitzi_Márcuri_Xhúadi_Viénerçi_Sáturi".split("_"),weekdaysShort:"Súl_Lún_Mai_Már_Xhú_Vié_Sát".split("_"),weekdaysMin:"Sú_Lú_Ma_Má_Xh_Vi_Sá".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"D. MMMM [dallas] YYYY",LLL:"D. MMMM [dallas] YYYY HH.mm",LLLL:"dddd, [li] D. MMMM [dallas] YYYY HH.mm"},meridiemParse:/d\'o|d\'a/i,isPM:function(e){return"d'o"===e.toLowerCase()},meridiem:function(e,a,t){return e>11?t?"d'o":"D'O":t?"d'a":"D'A"},calendar:{sameDay:"[oxhi à] LT",nextDay:"[demà à] LT",nextWeek:"dddd [à] LT",lastDay:"[ieiri à] LT",lastWeek:"[sür el] dddd [lasteu à] LT",sameElse:"L"},relativeTime:{future:"osprei %s",past:"ja%s",s:a,ss:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(e.r(166540))},542954,(e,a,t)=>{e.e,e.r(166540).defineLocale("tzm-latn",{months:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),monthsShort:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),weekdays:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysShort:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysMin:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[asdkh g] LT",nextDay:"[aska g] LT",nextWeek:"dddd [g] LT",lastDay:"[assant g] LT",lastWeek:"dddd [g] LT",sameElse:"L"},relativeTime:{future:"dadkh s yan %s",past:"yan %s",s:"imik",ss:"%d imik",m:"minuḍ",mm:"%d minuḍ",h:"saɛa",hh:"%d tassaɛin",d:"ass",dd:"%d ossan",M:"ayowr",MM:"%d iyyirn",y:"asgas",yy:"%d isgasn"},week:{dow:6,doy:12}})},267123,(e,a,t)=>{e.e,e.r(166540).defineLocale("tzm",{months:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),monthsShort:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),weekdays:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysShort:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysMin:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ⴰⵙⴷⵅ ⴴ] LT",nextDay:"[ⴰⵙⴽⴰ ⴴ] LT",nextWeek:"dddd [ⴴ] LT",lastDay:"[ⴰⵚⴰⵏⵜ ⴴ] LT",lastWeek:"dddd [ⴴ] LT",sameElse:"L"},relativeTime:{future:"ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s",past:"ⵢⴰⵏ %s",s:"ⵉⵎⵉⴽ",ss:"%d ⵉⵎⵉⴽ",m:"ⵎⵉⵏⵓⴺ",mm:"%d ⵎⵉⵏⵓⴺ",h:"ⵙⴰⵄⴰ",hh:"%d ⵜⴰⵙⵙⴰⵄⵉⵏ",d:"ⴰⵙⵙ",dd:"%d oⵙⵙⴰⵏ",M:"ⴰⵢoⵓⵔ",MM:"%d ⵉⵢⵢⵉⵔⵏ",y:"ⴰⵙⴳⴰⵙ",yy:"%d ⵉⵙⴳⴰⵙⵏ"},week:{dow:6,doy:12}})},468227,(e,a,t)=>{e.e,e.r(166540).defineLocale("ug-cn",{months:"يانۋار_فېۋرال_مارت_ئاپرېل_ماي_ئىيۇن_ئىيۇل_ئاۋغۇست_سېنتەبىر_ئۆكتەبىر_نويابىر_دېكابىر".split("_"),monthsShort:"يانۋار_فېۋرال_مارت_ئاپرېل_ماي_ئىيۇن_ئىيۇل_ئاۋغۇست_سېنتەبىر_ئۆكتەبىر_نويابىر_دېكابىر".split("_"),weekdays:"يەكشەنبە_دۈشەنبە_سەيشەنبە_چارشەنبە_پەيشەنبە_جۈمە_شەنبە".split("_"),weekdaysShort:"يە_دۈ_سە_چا_پە_جۈ_شە".split("_"),weekdaysMin:"يە_دۈ_سە_چا_پە_جۈ_شە".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY-يىلىM-ئاينىڭD-كۈنى",LLL:"YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm",LLLL:"dddd، YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm"},meridiemParse:/يېرىم كېچە|سەھەر|چۈشتىن بۇرۇن|چۈش|چۈشتىن كېيىن|كەچ/,meridiemHour:function(e,a){return(12===e&&(e=0),"يېرىم كېچە"===a||"سەھەر"===a||"چۈشتىن بۇرۇن"===a)?e:"چۈشتىن كېيىن"===a||"كەچ"===a?e+12:e>=11?e:e+12},meridiem:function(e,a,t){var s=100*e+a;if(s<600)return"يېرىم كېچە";if(s<900)return"سەھەر";if(s<1130)return"چۈشتىن بۇرۇن";if(s<1230)return"چۈش";if(s<1800)return"چۈشتىن كېيىن";else return"كەچ"},calendar:{sameDay:"[بۈگۈن سائەت] LT",nextDay:"[ئەتە سائەت] LT",nextWeek:"[كېلەركى] dddd [سائەت] LT",lastDay:"[تۆنۈگۈن] LT",lastWeek:"[ئالدىنقى] dddd [سائەت] LT",sameElse:"L"},relativeTime:{future:"%s كېيىن",past:"%s بۇرۇن",s:"نەچچە سېكونت",ss:"%d سېكونت",m:"بىر مىنۇت",mm:"%d مىنۇت",h:"بىر سائەت",hh:"%d سائەت",d:"بىر كۈن",dd:"%d كۈن",M:"بىر ئاي",MM:"%d ئاي",y:"بىر يىل",yy:"%d يىل"},dayOfMonthOrdinalParse:/\d{1,2}(-كۈنى|-ئاي|-ھەپتە)/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"-كۈنى";case"w":case"W":return e+"-ھەپتە";default:return e}},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:1,doy:7}})},557418,(e,a,t)=>{e.e,function(e){"use strict";function a(e,a,t){var s,n,r;return"m"===t?a?"хвилина":"хвилину":"h"===t?a?"година":"годину":e+" "+(s=({ss:a?"секунда_секунди_секунд":"секунду_секунди_секунд",mm:a?"хвилина_хвилини_хвилин":"хвилину_хвилини_хвилин",hh:a?"година_години_годин":"годину_години_годин",dd:"день_дні_днів",MM:"місяць_місяці_місяців",yy:"рік_роки_років"})[t],n=+e,r=s.split("_"),n%10==1&&n%100!=11?r[0]:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?r[1]:r[2])}function t(e){return function(){return e+"о"+(11===this.hours()?"б":"")+"] LT"}}e.defineLocale("uk",{months:{format:"січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня".split("_"),standalone:"січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень".split("_")},monthsShort:"січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),weekdays:function(e,a){var t={nominative:"неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота".split("_"),accusative:"неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу".split("_"),genitive:"неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи".split("_")};return!0===e?t.nominative.slice(1,7).concat(t.nominative.slice(0,1)):e?t[/(\[[ВвУу]\]) ?dddd/.test(a)?"accusative":/\[?(?:минулої|наступної)? ?\] ?dddd/.test(a)?"genitive":"nominative"][e.day()]:t.nominative},weekdaysShort:"нд_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., HH:mm",LLLL:"dddd, D MMMM YYYY р., HH:mm"},calendar:{sameDay:t("[Сьогодні "),nextDay:t("[Завтра "),lastDay:t("[Вчора "),nextWeek:t("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return t("[Минулої] dddd [").call(this);case 1:case 2:case 4:return t("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька секунд",ss:a,m:a,mm:a,h:"годину",hh:a,d:"день",dd:a,M:"місяць",MM:a,y:"рік",yy:a},meridiemParse:/ночі|ранку|дня|вечора/,isPM:function(e){return/^(дня|вечора)$/.test(e)},meridiem:function(e,a,t){return e<4?"ночі":e<12?"ранку":e<17?"дня":"вечора"},dayOfMonthOrdinalParse:/\d{1,2}-(й|го)/,ordinal:function(e,a){switch(a){case"M":case"d":case"DDD":case"w":case"W":return e+"-й";case"D":return e+"-го";default:return e}},week:{dow:1,doy:7}})}(e.r(166540))},721396,(e,a,t)=>{e.e,function(e){"use strict";var a=["جنوری","فروری","مارچ","اپریل","مئی","جون","جولائی","اگست","ستمبر","اکتوبر","نومبر","دسمبر"],t=["اتوار","پیر","منگل","بدھ","جمعرات","جمعہ","ہفتہ"];e.defineLocale("ur",{months:a,monthsShort:a,weekdays:t,weekdaysShort:t,weekdaysMin:t,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd، D MMMM YYYY HH:mm"},meridiemParse:/صبح|شام/,isPM:function(e){return"شام"===e},meridiem:function(e,a,t){return e<12?"صبح":"شام"},calendar:{sameDay:"[آج بوقت] LT",nextDay:"[کل بوقت] LT",nextWeek:"dddd [بوقت] LT",lastDay:"[گذشتہ روز بوقت] LT",lastWeek:"[گذشتہ] dddd [بوقت] LT",sameElse:"L"},relativeTime:{future:"%s بعد",past:"%s قبل",s:"چند سیکنڈ",ss:"%d سیکنڈ",m:"ایک منٹ",mm:"%d منٹ",h:"ایک گھنٹہ",hh:"%d گھنٹے",d:"ایک دن",dd:"%d دن",M:"ایک ماہ",MM:"%d ماہ",y:"ایک سال",yy:"%d سال"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:1,doy:4}})}(e.r(166540))},647658,(e,a,t)=>{e.e,e.r(166540).defineLocale("uz-latn",{months:"Yanvar_Fevral_Mart_Aprel_May_Iyun_Iyul_Avgust_Sentabr_Oktabr_Noyabr_Dekabr".split("_"),monthsShort:"Yan_Fev_Mar_Apr_May_Iyun_Iyul_Avg_Sen_Okt_Noy_Dek".split("_"),weekdays:"Yakshanba_Dushanba_Seshanba_Chorshanba_Payshanba_Juma_Shanba".split("_"),weekdaysShort:"Yak_Dush_Sesh_Chor_Pay_Jum_Shan".split("_"),weekdaysMin:"Ya_Du_Se_Cho_Pa_Ju_Sha".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Bugun soat] LT [da]",nextDay:"[Ertaga] LT [da]",nextWeek:"dddd [kuni soat] LT [da]",lastDay:"[Kecha soat] LT [da]",lastWeek:"[O'tgan] dddd [kuni soat] LT [da]",sameElse:"L"},relativeTime:{future:"Yaqin %s ichida",past:"Bir necha %s oldin",s:"soniya",ss:"%d soniya",m:"bir daqiqa",mm:"%d daqiqa",h:"bir soat",hh:"%d soat",d:"bir kun",dd:"%d kun",M:"bir oy",MM:"%d oy",y:"bir yil",yy:"%d yil"},week:{dow:1,doy:7}})},298424,(e,a,t)=>{e.e,e.r(166540).defineLocale("uz",{months:"январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр".split("_"),monthsShort:"янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"),weekdays:"Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба".split("_"),weekdaysShort:"Якш_Душ_Сеш_Чор_Пай_Жум_Шан".split("_"),weekdaysMin:"Як_Ду_Се_Чо_Па_Жу_Ша".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Бугун соат] LT [да]",nextDay:"[Эртага] LT [да]",nextWeek:"dddd [куни соат] LT [да]",lastDay:"[Кеча соат] LT [да]",lastWeek:"[Утган] dddd [куни соат] LT [да]",sameElse:"L"},relativeTime:{future:"Якин %s ичида",past:"Бир неча %s олдин",s:"фурсат",ss:"%d фурсат",m:"бир дакика",mm:"%d дакика",h:"бир соат",hh:"%d соат",d:"бир кун",dd:"%d кун",M:"бир ой",MM:"%d ой",y:"бир йил",yy:"%d йил"},week:{dow:1,doy:7}})},377647,(e,a,t)=>{e.e,e.r(166540).defineLocale("vi",{months:"tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),monthsShort:"Thg 01_Thg 02_Thg 03_Thg 04_Thg 05_Thg 06_Thg 07_Thg 08_Thg 09_Thg 10_Thg 11_Thg 12".split("_"),monthsParseExact:!0,weekdays:"chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysParseExact:!0,meridiemParse:/sa|ch/i,isPM:function(e){return/^ch$/i.test(e)},meridiem:function(e,a,t){return e<12?t?"sa":"SA":t?"ch":"CH"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY HH:mm",LLLL:"dddd, D MMMM [năm] YYYY HH:mm",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[Hôm nay lúc] LT",nextDay:"[Ngày mai lúc] LT",nextWeek:"dddd [tuần tới lúc] LT",lastDay:"[Hôm qua lúc] LT",lastWeek:"dddd [tuần trước lúc] LT",sameElse:"L"},relativeTime:{future:"%s tới",past:"%s trước",s:"vài giây",ss:"%d giây",m:"một phút",mm:"%d phút",h:"một giờ",hh:"%d giờ",d:"một ngày",dd:"%d ngày",w:"một tuần",ww:"%d tuần",M:"một tháng",MM:"%d tháng",y:"một năm",yy:"%d năm"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})},321194,(e,a,t)=>{e.e,e.r(166540).defineLocale("x-pseudo",{months:"J~áñúá~rý_F~ébrú~árý_~Márc~h_Áp~ríl_~Máý_~Júñé~_Júl~ý_Áú~gúst~_Sép~témb~ér_Ó~ctób~ér_Ñ~óvém~bér_~Décé~mbér".split("_"),monthsShort:"J~áñ_~Féb_~Már_~Ápr_~Máý_~Júñ_~Júl_~Áúg_~Sép_~Óct_~Ñóv_~Déc".split("_"),monthsParseExact:!0,weekdays:"S~úñdá~ý_Mó~ñdáý~_Túé~sdáý~_Wéd~ñésd~áý_T~húrs~dáý_~Fríd~áý_S~átúr~dáý".split("_"),weekdaysShort:"S~úñ_~Móñ_~Túé_~Wéd_~Thú_~Frí_~Sát".split("_"),weekdaysMin:"S~ú_Mó~_Tú_~Wé_T~h_Fr~_Sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[T~ódá~ý át] LT",nextDay:"[T~ómó~rró~w át] LT",nextWeek:"dddd [át] LT",lastDay:"[Ý~ést~érdá~ý át] LT",lastWeek:"[L~ást] dddd [át] LT",sameElse:"L"},relativeTime:{future:"í~ñ %s",past:"%s á~gó",s:"á ~féw ~sécó~ñds",ss:"%d s~écóñ~ds",m:"á ~míñ~úté",mm:"%d m~íñú~tés",h:"á~ñ hó~úr",hh:"%d h~óúrs",d:"á ~dáý",dd:"%d d~áýs",M:"á ~móñ~th",MM:"%d m~óñt~hs",y:"á ~ýéár",yy:"%d ý~éárs"},dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var a=e%10,t=1==~~(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t},week:{dow:1,doy:4}})},424446,(e,a,t)=>{e.e,e.r(166540).defineLocale("yo",{months:"Sẹ́rẹ́_Èrèlè_Ẹrẹ̀nà_Ìgbé_Èbibi_Òkùdu_Agẹmo_Ògún_Owewe_Ọ̀wàrà_Bélú_Ọ̀pẹ̀̀".split("_"),monthsShort:"Sẹ́r_Èrl_Ẹrn_Ìgb_Èbi_Òkù_Agẹ_Ògú_Owe_Ọ̀wà_Bél_Ọ̀pẹ̀̀".split("_"),weekdays:"Àìkú_Ajé_Ìsẹ́gun_Ọjọ́rú_Ọjọ́bọ_Ẹtì_Àbámẹ́ta".split("_"),weekdaysShort:"Àìk_Ajé_Ìsẹ́_Ọjr_Ọjb_Ẹtì_Àbá".split("_"),weekdaysMin:"Àì_Aj_Ìs_Ọr_Ọb_Ẹt_Àb".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Ònì ni] LT",nextDay:"[Ọ̀la ni] LT",nextWeek:"dddd [Ọsẹ̀ tón'bọ] [ni] LT",lastDay:"[Àna ni] LT",lastWeek:"dddd [Ọsẹ̀ tólọ́] [ni] LT",sameElse:"L"},relativeTime:{future:"ní %s",past:"%s kọjá",s:"ìsẹjú aayá die",ss:"aayá %d",m:"ìsẹjú kan",mm:"ìsẹjú %d",h:"wákati kan",hh:"wákati %d",d:"ọjọ́ kan",dd:"ọjọ́ %d",M:"osù kan",MM:"osù %d",y:"ọdún kan",yy:"ọdún %d"},dayOfMonthOrdinalParse:/ọjọ́\s\d{1,2}/,ordinal:"ọjọ́ %d",week:{dow:1,doy:4}})},536655,(e,a,t)=>{e.e,e.r(166540).defineLocale("zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日Ah点mm分",LLLL:"YYYY年M月D日ddddAh点mm分",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,a){return(12===e&&(e=0),"凌晨"===a||"早上"===a||"上午"===a)?e:"下午"===a||"晚上"===a?e+12:e>=11?e:e+12},meridiem:function(e,a,t){var s=100*e+a;if(s<600)return"凌晨";if(s<900)return"早上";if(s<1130)return"上午";if(s<1230)return"中午";if(s<1800)return"下午";else return"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:function(e){return e.week()!==this.week()?"[下]dddLT":"[本]dddLT"},lastDay:"[昨天]LT",lastWeek:function(e){return this.week()!==e.week()?"[上]dddLT":"[本]dddLT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|周)/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"周";default:return e}},relativeTime:{future:"%s后",past:"%s前",s:"几秒",ss:"%d 秒",m:"1 分钟",mm:"%d 分钟",h:"1 小时",hh:"%d 小时",d:"1 天",dd:"%d 天",w:"1 周",ww:"%d 周",M:"1 个月",MM:"%d 个月",y:"1 年",yy:"%d 年"},week:{dow:1,doy:4}})},446820,(e,a,t)=>{e.e,e.r(166540).defineLocale("zh-hk",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,a){return(12===e&&(e=0),"凌晨"===a||"早上"===a||"上午"===a)?e:"中午"===a?e>=11?e:e+12:"下午"===a||"晚上"===a?e+12:void 0},meridiem:function(e,a,t){var s=100*e+a;if(s<600)return"凌晨";if(s<900)return"早上";if(s<1200)return"上午";if(1200===s)return"中午";if(s<1800)return"下午";else return"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})},659396,(e,a,t)=>{e.e,e.r(166540).defineLocale("zh-mo",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"D/M/YYYY",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,a){return(12===e&&(e=0),"凌晨"===a||"早上"===a||"上午"===a)?e:"中午"===a?e>=11?e:e+12:"下午"===a||"晚上"===a?e+12:void 0},meridiem:function(e,a,t){var s=100*e+a;if(s<600)return"凌晨";if(s<900)return"早上";if(s<1130)return"上午";if(s<1230)return"中午";if(s<1800)return"下午";else return"晚上"},calendar:{sameDay:"[今天] LT",nextDay:"[明天] LT",nextWeek:"[下]dddd LT",lastDay:"[昨天] LT",lastWeek:"[上]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s內",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})},738643,(e,a,t)=>{e.e,e.r(166540).defineLocale("zh-tw",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,a){return(12===e&&(e=0),"凌晨"===a||"早上"===a||"上午"===a)?e:"中午"===a?e>=11?e:e+12:"下午"===a||"晚上"===a?e+12:void 0},meridiem:function(e,a,t){var s=100*e+a;if(s<600)return"凌晨";if(s<900)return"早上";if(s<1130)return"上午";if(s<1230)return"中午";if(s<1800)return"下午";else return"晚上"},calendar:{sameDay:"[今天] LT",nextDay:"[明天] LT",nextWeek:"[下]dddd LT",lastDay:"[昨天] LT",lastWeek:"[上]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,a){switch(a){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})},166540,(e,a,t)=>{e.e,a.exports=function(){"use strict";function t(){return R.apply(null,arguments)}function s(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function n(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function r(e,a){return Object.prototype.hasOwnProperty.call(e,a)}function d(e){var a;if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;for(a in e)if(r(e,a))return!1;return!0}function i(e){return void 0===e}function _(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function o(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function m(e,a){var t,s=[],n=e.length;for(t=0;t>>0;for(a=0;a0)for(t=0;ttypeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function g(e,a){var s=!0;return l(function(){if(null!=t.deprecationHandler&&t.deprecationHandler(null,e),s){var n,d,i,_=[],o=arguments.length;for(d=0;dtypeof Function&&e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function H(e,a){var t,s=l({},e);for(t in a)r(a,t)&&(n(e[t])&&n(a[t])?(s[t]={},l(s[t],e[t]),l(s[t],a[t])):null!=a[t]?s[t]=a[t]:delete s[t]);for(t in e)r(e,t)&&!r(a,t)&&n(e[t])&&(s[t]=l({},s[t]));return s}function S(e){null!=e&&this.set(e)}function j(e,a,t){var s=""+Math.abs(e);return(e>=0?t?"+":"":"-")+Math.pow(10,Math.max(0,a-s.length)).toString().substr(1)+s}t.suppressDeprecationWarnings=!1,t.deprecationHandler=null;var x=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,P=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,O={},W={};function A(e,a,t,s){var n=s;"string"==typeof s&&(n=function(){return this[s]()}),e&&(W[e]=n),a&&(W[a[0]]=function(){return j(n.apply(this,arguments),a[1],a[2])}),t&&(W[t]=function(){return this.localeData().ordinal(n.apply(this,arguments),e)})}function E(e,a){return e.isValid()?(O[a=F(a,e.localeData())]=O[a]||function(e){var a,t,s,n=e.match(x);for(t=0,s=n.length;t=0&&P.test(e);)e=e.replace(P,s),P.lastIndex=0,t-=1;return e}var z={D:"date",dates:"date",date:"date",d:"day",days:"day",day:"day",e:"weekday",weekdays:"weekday",weekday:"weekday",E:"isoWeekday",isoweekdays:"isoWeekday",isoweekday:"isoWeekday",DDD:"dayOfYear",dayofyears:"dayOfYear",dayofyear:"dayOfYear",h:"hour",hours:"hour",hour:"hour",ms:"millisecond",milliseconds:"millisecond",millisecond:"millisecond",m:"minute",minutes:"minute",minute:"minute",M:"month",months:"month",month:"month",Q:"quarter",quarters:"quarter",quarter:"quarter",s:"second",seconds:"second",second:"second",gg:"weekYear",weekyears:"weekYear",weekyear:"weekYear",GG:"isoWeekYear",isoweekyears:"isoWeekYear",isoweekyear:"isoWeekYear",w:"week",weeks:"week",week:"week",W:"isoWeek",isoweeks:"isoWeek",isoweek:"isoWeek",y:"year",years:"year",year:"year"};function N(e){return"string"==typeof e?z[e]||z[e.toLowerCase()]:void 0}function J(e){var a,t,s={};for(t in e)r(e,t)&&(a=N(t))&&(s[a]=e[t]);return s}var R,C,I,U={date:9,day:11,weekday:11,isoWeekday:11,dayOfYear:4,hour:13,millisecond:16,minute:14,month:8,quarter:7,second:15,weekYear:1,isoWeekYear:1,week:5,isoWeek:5,year:1},G=Object.keys?Object.keys:function(e){var a,t=[];for(a in e)r(e,a)&&t.push(a);return t},V=/\d/,q=/\d\d/,B=/\d{3}/,K=/\d{4}/,Z=/[+-]?\d{6}/,$=/\d\d?/,Q=/\d\d\d\d?/,X=/\d\d\d\d\d\d?/,ee=/\d{1,3}/,ea=/\d{1,4}/,et=/[+-]?\d{1,6}/,es=/\d+/,en=/[+-]?\d+/,er=/Z|[+-]\d\d:?\d\d/gi,ed=/Z|[+-]\d\d(?::?\d\d)?/gi,ei=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,e_=/^[1-9]\d?/,eo=/^([1-9]\d|\d)/;function em(e,a,t){I[e]=b(a)?a:function(e,s){return e&&t?t:a}}function el(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function eu(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function eM(e){var a=+e,t=0;return 0!==a&&isFinite(a)&&(t=eu(a)),t}I={};var eh={};function ec(e,a){var t,s,n=a;for("string"==typeof e&&(e=[e]),_(a)&&(n=function(e,t){t[a]=eM(e)}),s=e.length,t=0;t68?1900:2e3)};var ef=ek("FullYear",!0);function ek(e,a){return function(s){return null!=s?(eD(this,e,s),t.updateOffset(this,a),this):ep(this,e)}}function ep(e,a){if(!e.isValid())return NaN;var t=e._d,s=e._isUTC;switch(a){case"Milliseconds":return s?t.getUTCMilliseconds():t.getMilliseconds();case"Seconds":return s?t.getUTCSeconds():t.getSeconds();case"Minutes":return s?t.getUTCMinutes():t.getMinutes();case"Hours":return s?t.getUTCHours():t.getHours();case"Date":return s?t.getUTCDate():t.getDate();case"Day":return s?t.getUTCDay():t.getDay();case"Month":return s?t.getUTCMonth():t.getMonth();case"FullYear":return s?t.getUTCFullYear():t.getFullYear();default:return NaN}}function eD(e,a,t){var s,n,r,d;if(!(!e.isValid()||isNaN(t))){switch(s=e._d,n=e._isUTC,a){case"Milliseconds":return void(n?s.setUTCMilliseconds(t):s.setMilliseconds(t));case"Seconds":return void(n?s.setUTCSeconds(t):s.setSeconds(t));case"Minutes":return void(n?s.setUTCMinutes(t):s.setMinutes(t));case"Hours":return void(n?s.setUTCHours(t):s.setHours(t));case"Date":return void(n?s.setUTCDate(t):s.setDate(t));case"FullYear":break;default:return}r=e.month(),d=29!==(d=e.date())||1!==r||eY(t)?d:28,n?s.setUTCFullYear(t,r,d):s.setFullYear(t,r,d)}}function eT(e,a){if(isNaN(e)||isNaN(a))return NaN;var t=(a%12+12)%12;return e+=(a-t)/12,1===t?eY(e)?29:28:31-t%7%2}eI=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var a;for(a=0;a=0?isFinite((i=new Date(e+400,a,t,s,n,r,d)).getFullYear())&&i.setFullYear(e):i=new Date(e,a,t,s,n,r,d),i}function ex(e){var a,t;return e<100&&e>=0?(t=Array.prototype.slice.call(arguments),t[0]=e+400,isFinite((a=new Date(Date.UTC.apply(null,t))).getUTCFullYear())&&a.setUTCFullYear(e)):a=new Date(Date.UTC.apply(null,arguments)),a}function eP(e,a,t){var s=7+a-t;return-((7+ex(e,0,s).getUTCDay()-a)%7)+s-1}function eO(e,a,t,s,n){var r,d,i=1+7*(a-1)+(7+t-s)%7+eP(e,s,n);return i<=0?d=ey(r=e-1)+i:i>ey(e)?(r=e+1,d=i-ey(e)):(r=e,d=i),{year:r,dayOfYear:d}}function eW(e,a,t){var s,n,r=eP(e.year(),a,t),d=Math.floor((e.dayOfYear()-r-1)/7)+1;return d<1?s=d+eA(n=e.year()-1,a,t):d>eA(e.year(),a,t)?(s=d-eA(e.year(),a,t),n=e.year()+1):(n=e.year(),s=d),{week:s,year:n}}function eA(e,a,t){var s=eP(e,a,t),n=eP(e+1,a,t);return(ey(e)-s+n)/7}function eE(e,a){return e.slice(a,7).concat(e.slice(0,a))}A("w",["ww",2],"wo","week"),A("W",["WW",2],"Wo","isoWeek"),em("w",$,e_),em("ww",$,q),em("W",$,e_),em("WW",$,q),eL(["w","ww","W","WW"],function(e,a,t,s){a[s.substr(0,1)]=eM(e)}),A("d",0,"do","day"),A("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),A("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),A("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),A("e",0,0,"weekday"),A("E",0,0,"isoWeekday"),em("d",$),em("e",$),em("E",$),em("dd",function(e,a){return a.weekdaysMinRegex(e)}),em("ddd",function(e,a){return a.weekdaysShortRegex(e)}),em("dddd",function(e,a){return a.weekdaysRegex(e)}),eL(["dd","ddd","dddd"],function(e,a,t,s){var n=t._locale.weekdaysParse(e,s,t._strict);null!=n?a.d=n:M(t).invalidWeekday=e}),eL(["d","e","E"],function(e,a,t,s){a[s]=eM(e)});var eF="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");function ez(e,a,t){var s,n,r,d=e.toLocaleLowerCase();if(!this._weekdaysParse)for(s=0,this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[];s<7;++s)r=u([2e3,1]).day(s),this._minWeekdaysParse[s]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[s]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[s]=this.weekdays(r,"").toLocaleLowerCase();if(t)if("dddd"===a)return -1!==(n=eI.call(this._weekdaysParse,d))?n:null;else if("ddd"===a)return -1!==(n=eI.call(this._shortWeekdaysParse,d))?n:null;else return -1!==(n=eI.call(this._minWeekdaysParse,d))?n:null;return"dddd"===a?-1!==(n=eI.call(this._weekdaysParse,d))||-1!==(n=eI.call(this._shortWeekdaysParse,d))||-1!==(n=eI.call(this._minWeekdaysParse,d))?n:null:"ddd"===a?-1!==(n=eI.call(this._shortWeekdaysParse,d))||-1!==(n=eI.call(this._weekdaysParse,d))||-1!==(n=eI.call(this._minWeekdaysParse,d))?n:null:-1!==(n=eI.call(this._minWeekdaysParse,d))||-1!==(n=eI.call(this._weekdaysParse,d))||-1!==(n=eI.call(this._shortWeekdaysParse,d))?n:null}function eN(){function e(e,a){return a.length-e.length}var a,t,s,n,r,d=[],i=[],_=[],o=[];for(a=0;a<7;a++)t=u([2e3,1]).day(a),s=el(this.weekdaysMin(t,"")),n=el(this.weekdaysShort(t,"")),r=el(this.weekdays(t,"")),d.push(s),i.push(n),_.push(r),o.push(s),o.push(n),o.push(r);d.sort(e),i.sort(e),_.sort(e),o.sort(e),this._weekdaysRegex=RegExp("^("+o.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=RegExp("^("+_.join("|")+")","i"),this._weekdaysShortStrictRegex=RegExp("^("+i.join("|")+")","i"),this._weekdaysMinStrictRegex=RegExp("^("+d.join("|")+")","i")}function eJ(){return this.hours()%12||12}function eR(e,a){A(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),a)})}function eC(e,a){return a._meridiemParse}A("H",["HH",2],0,"hour"),A("h",["hh",2],0,eJ),A("k",["kk",2],0,function(){return this.hours()||24}),A("hmm",0,0,function(){return""+eJ.apply(this)+j(this.minutes(),2)}),A("hmmss",0,0,function(){return""+eJ.apply(this)+j(this.minutes(),2)+j(this.seconds(),2)}),A("Hmm",0,0,function(){return""+this.hours()+j(this.minutes(),2)}),A("Hmmss",0,0,function(){return""+this.hours()+j(this.minutes(),2)+j(this.seconds(),2)}),eR("a",!0),eR("A",!1),em("a",eC),em("A",eC),em("H",$,eo),em("h",$,e_),em("k",$,e_),em("HH",$,q),em("hh",$,q),em("kk",$,q),em("hmm",Q),em("hmmss",X),em("Hmm",Q),em("Hmmss",X),ec(["H","HH"],3),ec(["k","kk"],function(e,a,t){var s=eM(e);a[3]=24===s?0:s}),ec(["a","A"],function(e,a,t){t._isPm=t._locale.isPM(e),t._meridiem=e}),ec(["h","hh"],function(e,a,t){a[3]=eM(e),M(t).bigHour=!0}),ec("hmm",function(e,a,t){var s=e.length-2;a[3]=eM(e.substr(0,s)),a[4]=eM(e.substr(s)),M(t).bigHour=!0}),ec("hmmss",function(e,a,t){var s=e.length-4,n=e.length-2;a[3]=eM(e.substr(0,s)),a[4]=eM(e.substr(s,2)),a[5]=eM(e.substr(n)),M(t).bigHour=!0}),ec("Hmm",function(e,a,t){var s=e.length-2;a[3]=eM(e.substr(0,s)),a[4]=eM(e.substr(s))}),ec("Hmmss",function(e,a,t){var s=e.length-4,n=e.length-2;a[3]=eM(e.substr(0,s)),a[4]=eM(e.substr(s,2)),a[5]=eM(e.substr(n))});var eI,eU,eG=ek("Hours",!0),eV={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:eg,week:{dow:0,doy:6},weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysShort:eF,meridiemParse:/[ap]\.?m?\.?/i},eq={},eB={};function eK(e){return e?e.toLowerCase().replace("_","-"):e}function eZ(t){var s=null;if(void 0===eq[t]&&a&&a.exports&&t&&t.match("^[^/\\\\]*$"))try{s=eU._abbr,e.t,e.f({"./locale/af.js":{id:()=>649222,module:()=>e.r(649222)},"./locale/af":{id:()=>649222,module:()=>e.r(649222)},"./locale/ar-dz.js":{id:()=>50997,module:()=>e.r(50997)},"./locale/ar-dz":{id:()=>50997,module:()=>e.r(50997)},"./locale/ar-kw.js":{id:()=>818181,module:()=>e.r(818181)},"./locale/ar-kw":{id:()=>818181,module:()=>e.r(818181)},"./locale/ar-ly.js":{id:()=>392472,module:()=>e.r(392472)},"./locale/ar-ly":{id:()=>392472,module:()=>e.r(392472)},"./locale/ar-ma.js":{id:()=>48840,module:()=>e.r(48840)},"./locale/ar-ma":{id:()=>48840,module:()=>e.r(48840)},"./locale/ar-ps.js":{id:()=>561871,module:()=>e.r(561871)},"./locale/ar-ps":{id:()=>561871,module:()=>e.r(561871)},"./locale/ar-sa.js":{id:()=>566848,module:()=>e.r(566848)},"./locale/ar-sa":{id:()=>566848,module:()=>e.r(566848)},"./locale/ar-tn.js":{id:()=>892109,module:()=>e.r(892109)},"./locale/ar-tn":{id:()=>892109,module:()=>e.r(892109)},"./locale/ar.js":{id:()=>617209,module:()=>e.r(617209)},"./locale/ar":{id:()=>617209,module:()=>e.r(617209)},"./locale/az.js":{id:()=>627551,module:()=>e.r(627551)},"./locale/az":{id:()=>627551,module:()=>e.r(627551)},"./locale/be.js":{id:()=>416502,module:()=>e.r(416502)},"./locale/be":{id:()=>416502,module:()=>e.r(416502)},"./locale/bg.js":{id:()=>231241,module:()=>e.r(231241)},"./locale/bg":{id:()=>231241,module:()=>e.r(231241)},"./locale/bm.js":{id:()=>909549,module:()=>e.r(909549)},"./locale/bm":{id:()=>909549,module:()=>e.r(909549)},"./locale/bn-bd.js":{id:()=>939441,module:()=>e.r(939441)},"./locale/bn-bd":{id:()=>939441,module:()=>e.r(939441)},"./locale/bn.js":{id:()=>557613,module:()=>e.r(557613)},"./locale/bn":{id:()=>557613,module:()=>e.r(557613)},"./locale/bo.js":{id:()=>447113,module:()=>e.r(447113)},"./locale/bo":{id:()=>447113,module:()=>e.r(447113)},"./locale/br.js":{id:()=>964028,module:()=>e.r(964028)},"./locale/br":{id:()=>964028,module:()=>e.r(964028)},"./locale/bs.js":{id:()=>529619,module:()=>e.r(529619)},"./locale/bs":{id:()=>529619,module:()=>e.r(529619)},"./locale/ca.js":{id:()=>586721,module:()=>e.r(586721)},"./locale/ca":{id:()=>586721,module:()=>e.r(586721)},"./locale/cs.js":{id:()=>586162,module:()=>e.r(586162)},"./locale/cs":{id:()=>586162,module:()=>e.r(586162)},"./locale/cv.js":{id:()=>745143,module:()=>e.r(745143)},"./locale/cv":{id:()=>745143,module:()=>e.r(745143)},"./locale/cy.js":{id:()=>608170,module:()=>e.r(608170)},"./locale/cy":{id:()=>608170,module:()=>e.r(608170)},"./locale/da.js":{id:()=>596740,module:()=>e.r(596740)},"./locale/da":{id:()=>596740,module:()=>e.r(596740)},"./locale/de-at.js":{id:()=>346346,module:()=>e.r(346346)},"./locale/de-at":{id:()=>346346,module:()=>e.r(346346)},"./locale/de-ch.js":{id:()=>700088,module:()=>e.r(700088)},"./locale/de-ch":{id:()=>700088,module:()=>e.r(700088)},"./locale/de.js":{id:()=>486428,module:()=>e.r(486428)},"./locale/de":{id:()=>486428,module:()=>e.r(486428)},"./locale/dv.js":{id:()=>31113,module:()=>e.r(31113)},"./locale/dv":{id:()=>31113,module:()=>e.r(31113)},"./locale/el.js":{id:()=>550841,module:()=>e.r(550841)},"./locale/el":{id:()=>550841,module:()=>e.r(550841)},"./locale/en-au.js":{id:()=>884432,module:()=>e.r(884432)},"./locale/en-au":{id:()=>884432,module:()=>e.r(884432)},"./locale/en-ca.js":{id:()=>448736,module:()=>e.r(448736)},"./locale/en-ca":{id:()=>448736,module:()=>e.r(448736)},"./locale/en-gb.js":{id:()=>828502,module:()=>e.r(828502)},"./locale/en-gb":{id:()=>828502,module:()=>e.r(828502)},"./locale/en-ie.js":{id:()=>421205,module:()=>e.r(421205)},"./locale/en-ie":{id:()=>421205,module:()=>e.r(421205)},"./locale/en-il.js":{id:()=>621015,module:()=>e.r(621015)},"./locale/en-il":{id:()=>621015,module:()=>e.r(621015)},"./locale/en-in.js":{id:()=>162743,module:()=>e.r(162743)},"./locale/en-in":{id:()=>162743,module:()=>e.r(162743)},"./locale/en-nz.js":{id:()=>370661,module:()=>e.r(370661)},"./locale/en-nz":{id:()=>370661,module:()=>e.r(370661)},"./locale/en-sg.js":{id:()=>113826,module:()=>e.r(113826)},"./locale/en-sg":{id:()=>113826,module:()=>e.r(113826)},"./locale/eo.js":{id:()=>633517,module:()=>e.r(633517)},"./locale/eo":{id:()=>633517,module:()=>e.r(633517)},"./locale/es-do.js":{id:()=>954e3,module:()=>e.r(954e3)},"./locale/es-do":{id:()=>954e3,module:()=>e.r(954e3)},"./locale/es-mx.js":{id:()=>120137,module:()=>e.r(120137)},"./locale/es-mx":{id:()=>120137,module:()=>e.r(120137)},"./locale/es-us.js":{id:()=>528845,module:()=>e.r(528845)},"./locale/es-us":{id:()=>528845,module:()=>e.r(528845)},"./locale/es.js":{id:()=>753818,module:()=>e.r(753818)},"./locale/es":{id:()=>753818,module:()=>e.r(753818)},"./locale/et.js":{id:()=>54306,module:()=>e.r(54306)},"./locale/et":{id:()=>54306,module:()=>e.r(54306)},"./locale/eu.js":{id:()=>430810,module:()=>e.r(430810)},"./locale/eu":{id:()=>430810,module:()=>e.r(430810)},"./locale/fa.js":{id:()=>374902,module:()=>e.r(374902)},"./locale/fa":{id:()=>374902,module:()=>e.r(374902)},"./locale/fi.js":{id:()=>412450,module:()=>e.r(412450)},"./locale/fi":{id:()=>412450,module:()=>e.r(412450)},"./locale/fil.js":{id:()=>321329,module:()=>e.r(321329)},"./locale/fil":{id:()=>321329,module:()=>e.r(321329)},"./locale/fo.js":{id:()=>473679,module:()=>e.r(473679)},"./locale/fo":{id:()=>473679,module:()=>e.r(473679)},"./locale/fr-ca.js":{id:()=>874573,module:()=>e.r(874573)},"./locale/fr-ca":{id:()=>874573,module:()=>e.r(874573)},"./locale/fr-ch.js":{id:()=>639994,module:()=>e.r(639994)},"./locale/fr-ch":{id:()=>639994,module:()=>e.r(639994)},"./locale/fr.js":{id:()=>618184,module:()=>e.r(618184)},"./locale/fr":{id:()=>618184,module:()=>e.r(618184)},"./locale/fy.js":{id:()=>439552,module:()=>e.r(439552)},"./locale/fy":{id:()=>439552,module:()=>e.r(439552)},"./locale/ga.js":{id:()=>866284,module:()=>e.r(866284)},"./locale/ga":{id:()=>866284,module:()=>e.r(866284)},"./locale/gd.js":{id:()=>810136,module:()=>e.r(810136)},"./locale/gd":{id:()=>810136,module:()=>e.r(810136)},"./locale/gl.js":{id:()=>703131,module:()=>e.r(703131)},"./locale/gl":{id:()=>703131,module:()=>e.r(703131)},"./locale/gom-deva.js":{id:()=>56861,module:()=>e.r(56861)},"./locale/gom-deva":{id:()=>56861,module:()=>e.r(56861)},"./locale/gom-latn.js":{id:()=>227159,module:()=>e.r(227159)},"./locale/gom-latn":{id:()=>227159,module:()=>e.r(227159)},"./locale/gu.js":{id:()=>277496,module:()=>e.r(277496)},"./locale/gu":{id:()=>277496,module:()=>e.r(277496)},"./locale/he.js":{id:()=>796669,module:()=>e.r(796669)},"./locale/he":{id:()=>796669,module:()=>e.r(796669)},"./locale/hi.js":{id:()=>725949,module:()=>e.r(725949)},"./locale/hi":{id:()=>725949,module:()=>e.r(725949)},"./locale/hr.js":{id:()=>863164,module:()=>e.r(863164)},"./locale/hr":{id:()=>863164,module:()=>e.r(863164)},"./locale/hu.js":{id:()=>491161,module:()=>e.r(491161)},"./locale/hu":{id:()=>491161,module:()=>e.r(491161)},"./locale/hy-am.js":{id:()=>122472,module:()=>e.r(122472)},"./locale/hy-am":{id:()=>122472,module:()=>e.r(122472)},"./locale/id.js":{id:()=>261476,module:()=>e.r(261476)},"./locale/id":{id:()=>261476,module:()=>e.r(261476)},"./locale/is.js":{id:()=>595500,module:()=>e.r(595500)},"./locale/is":{id:()=>595500,module:()=>e.r(595500)},"./locale/it-ch.js":{id:()=>351426,module:()=>e.r(351426)},"./locale/it-ch":{id:()=>351426,module:()=>e.r(351426)},"./locale/it.js":{id:()=>988869,module:()=>e.r(988869)},"./locale/it":{id:()=>988869,module:()=>e.r(988869)},"./locale/ja.js":{id:()=>622116,module:()=>e.r(622116)},"./locale/ja":{id:()=>622116,module:()=>e.r(622116)},"./locale/jv.js":{id:()=>874383,module:()=>e.r(874383)},"./locale/jv":{id:()=>874383,module:()=>e.r(874383)},"./locale/ka.js":{id:()=>11842,module:()=>e.r(11842)},"./locale/ka":{id:()=>11842,module:()=>e.r(11842)},"./locale/kk.js":{id:()=>613970,module:()=>e.r(613970)},"./locale/kk":{id:()=>613970,module:()=>e.r(613970)},"./locale/km.js":{id:()=>621412,module:()=>e.r(621412)},"./locale/km":{id:()=>621412,module:()=>e.r(621412)},"./locale/kn.js":{id:()=>978630,module:()=>e.r(978630)},"./locale/kn":{id:()=>978630,module:()=>e.r(978630)},"./locale/ko.js":{id:()=>73893,module:()=>e.r(73893)},"./locale/ko":{id:()=>73893,module:()=>e.r(73893)},"./locale/ku-kmr.js":{id:()=>531990,module:()=>e.r(531990)},"./locale/ku-kmr":{id:()=>531990,module:()=>e.r(531990)},"./locale/ku.js":{id:()=>327383,module:()=>e.r(327383)},"./locale/ku":{id:()=>327383,module:()=>e.r(327383)},"./locale/ky.js":{id:()=>913233,module:()=>e.r(913233)},"./locale/ky":{id:()=>913233,module:()=>e.r(913233)},"./locale/lb.js":{id:()=>535403,module:()=>e.r(535403)},"./locale/lb":{id:()=>535403,module:()=>e.r(535403)},"./locale/lo.js":{id:()=>17373,module:()=>e.r(17373)},"./locale/lo":{id:()=>17373,module:()=>e.r(17373)},"./locale/lt.js":{id:()=>409583,module:()=>e.r(409583)},"./locale/lt":{id:()=>409583,module:()=>e.r(409583)},"./locale/lv.js":{id:()=>407912,module:()=>e.r(407912)},"./locale/lv":{id:()=>407912,module:()=>e.r(407912)},"./locale/me.js":{id:()=>545267,module:()=>e.r(545267)},"./locale/me":{id:()=>545267,module:()=>e.r(545267)},"./locale/mi.js":{id:()=>961705,module:()=>e.r(961705)},"./locale/mi":{id:()=>961705,module:()=>e.r(961705)},"./locale/mk.js":{id:()=>354402,module:()=>e.r(354402)},"./locale/mk":{id:()=>354402,module:()=>e.r(354402)},"./locale/ml.js":{id:()=>624201,module:()=>e.r(624201)},"./locale/ml":{id:()=>624201,module:()=>e.r(624201)},"./locale/mn.js":{id:()=>969668,module:()=>e.r(969668)},"./locale/mn":{id:()=>969668,module:()=>e.r(969668)},"./locale/mr.js":{id:()=>417366,module:()=>e.r(417366)},"./locale/mr":{id:()=>417366,module:()=>e.r(417366)},"./locale/ms-my.js":{id:()=>538640,module:()=>e.r(538640)},"./locale/ms-my":{id:()=>538640,module:()=>e.r(538640)},"./locale/ms.js":{id:()=>367856,module:()=>e.r(367856)},"./locale/ms":{id:()=>367856,module:()=>e.r(367856)},"./locale/mt.js":{id:()=>157692,module:()=>e.r(157692)},"./locale/mt":{id:()=>157692,module:()=>e.r(157692)},"./locale/my.js":{id:()=>222310,module:()=>e.r(222310)},"./locale/my":{id:()=>222310,module:()=>e.r(222310)},"./locale/nb.js":{id:()=>441867,module:()=>e.r(441867)},"./locale/nb":{id:()=>441867,module:()=>e.r(441867)},"./locale/ne.js":{id:()=>899103,module:()=>e.r(899103)},"./locale/ne":{id:()=>899103,module:()=>e.r(899103)},"./locale/nl-be.js":{id:()=>775136,module:()=>e.r(775136)},"./locale/nl-be":{id:()=>775136,module:()=>e.r(775136)},"./locale/nl.js":{id:()=>618264,module:()=>e.r(618264)},"./locale/nl":{id:()=>618264,module:()=>e.r(618264)},"./locale/nn.js":{id:()=>876976,module:()=>e.r(876976)},"./locale/nn":{id:()=>876976,module:()=>e.r(876976)},"./locale/oc-lnc.js":{id:()=>225313,module:()=>e.r(225313)},"./locale/oc-lnc":{id:()=>225313,module:()=>e.r(225313)},"./locale/pa-in.js":{id:()=>368431,module:()=>e.r(368431)},"./locale/pa-in":{id:()=>368431,module:()=>e.r(368431)},"./locale/pl.js":{id:()=>657968,module:()=>e.r(657968)},"./locale/pl":{id:()=>657968,module:()=>e.r(657968)},"./locale/pt-br.js":{id:()=>736919,module:()=>e.r(736919)},"./locale/pt-br":{id:()=>736919,module:()=>e.r(736919)},"./locale/pt.js":{id:()=>493062,module:()=>e.r(493062)},"./locale/pt":{id:()=>493062,module:()=>e.r(493062)},"./locale/ro.js":{id:()=>869377,module:()=>e.r(869377)},"./locale/ro":{id:()=>869377,module:()=>e.r(869377)},"./locale/ru.js":{id:()=>498262,module:()=>e.r(498262)},"./locale/ru":{id:()=>498262,module:()=>e.r(498262)},"./locale/sd.js":{id:()=>137750,module:()=>e.r(137750)},"./locale/sd":{id:()=>137750,module:()=>e.r(137750)},"./locale/se.js":{id:()=>455308,module:()=>e.r(455308)},"./locale/se":{id:()=>455308,module:()=>e.r(455308)},"./locale/si.js":{id:()=>303364,module:()=>e.r(303364)},"./locale/si":{id:()=>303364,module:()=>e.r(303364)},"./locale/sk.js":{id:()=>195013,module:()=>e.r(195013)},"./locale/sk":{id:()=>195013,module:()=>e.r(195013)},"./locale/sl.js":{id:()=>575550,module:()=>e.r(575550)},"./locale/sl":{id:()=>575550,module:()=>e.r(575550)},"./locale/sq.js":{id:()=>813013,module:()=>e.r(813013)},"./locale/sq":{id:()=>813013,module:()=>e.r(813013)},"./locale/sr-cyrl.js":{id:()=>423039,module:()=>e.r(423039)},"./locale/sr-cyrl":{id:()=>423039,module:()=>e.r(423039)},"./locale/sr.js":{id:()=>654301,module:()=>e.r(654301)},"./locale/sr":{id:()=>654301,module:()=>e.r(654301)},"./locale/ss.js":{id:()=>492305,module:()=>e.r(492305)},"./locale/ss":{id:()=>492305,module:()=>e.r(492305)},"./locale/sv.js":{id:()=>937057,module:()=>e.r(937057)},"./locale/sv":{id:()=>937057,module:()=>e.r(937057)},"./locale/sw.js":{id:()=>771953,module:()=>e.r(771953)},"./locale/sw":{id:()=>771953,module:()=>e.r(771953)},"./locale/ta.js":{id:()=>271953,module:()=>e.r(271953)},"./locale/ta":{id:()=>271953,module:()=>e.r(271953)},"./locale/te.js":{id:()=>749731,module:()=>e.r(749731)},"./locale/te":{id:()=>749731,module:()=>e.r(749731)},"./locale/tet.js":{id:()=>165002,module:()=>e.r(165002)},"./locale/tet":{id:()=>165002,module:()=>e.r(165002)},"./locale/tg.js":{id:()=>580104,module:()=>e.r(580104)},"./locale/tg":{id:()=>580104,module:()=>e.r(580104)},"./locale/th.js":{id:()=>768313,module:()=>e.r(768313)},"./locale/th":{id:()=>768313,module:()=>e.r(768313)},"./locale/tk.js":{id:()=>291616,module:()=>e.r(291616)},"./locale/tk":{id:()=>291616,module:()=>e.r(291616)},"./locale/tl-ph.js":{id:()=>317895,module:()=>e.r(317895)},"./locale/tl-ph":{id:()=>317895,module:()=>e.r(317895)},"./locale/tlh.js":{id:()=>955799,module:()=>e.r(955799)},"./locale/tlh":{id:()=>955799,module:()=>e.r(955799)},"./locale/tr.js":{id:()=>515252,module:()=>e.r(515252)},"./locale/tr":{id:()=>515252,module:()=>e.r(515252)},"./locale/tzl.js":{id:()=>568087,module:()=>e.r(568087)},"./locale/tzl":{id:()=>568087,module:()=>e.r(568087)},"./locale/tzm-latn.js":{id:()=>542954,module:()=>e.r(542954)},"./locale/tzm-latn":{id:()=>542954,module:()=>e.r(542954)},"./locale/tzm.js":{id:()=>267123,module:()=>e.r(267123)},"./locale/tzm":{id:()=>267123,module:()=>e.r(267123)},"./locale/ug-cn.js":{id:()=>468227,module:()=>e.r(468227)},"./locale/ug-cn":{id:()=>468227,module:()=>e.r(468227)},"./locale/uk.js":{id:()=>557418,module:()=>e.r(557418)},"./locale/uk":{id:()=>557418,module:()=>e.r(557418)},"./locale/ur.js":{id:()=>721396,module:()=>e.r(721396)},"./locale/ur":{id:()=>721396,module:()=>e.r(721396)},"./locale/uz-latn.js":{id:()=>647658,module:()=>e.r(647658)},"./locale/uz-latn":{id:()=>647658,module:()=>e.r(647658)},"./locale/uz.js":{id:()=>298424,module:()=>e.r(298424)},"./locale/uz":{id:()=>298424,module:()=>e.r(298424)},"./locale/vi.js":{id:()=>377647,module:()=>e.r(377647)},"./locale/vi":{id:()=>377647,module:()=>e.r(377647)},"./locale/x-pseudo.js":{id:()=>321194,module:()=>e.r(321194)},"./locale/x-pseudo":{id:()=>321194,module:()=>e.r(321194)},"./locale/yo.js":{id:()=>424446,module:()=>e.r(424446)},"./locale/yo":{id:()=>424446,module:()=>e.r(424446)},"./locale/zh-cn.js":{id:()=>536655,module:()=>e.r(536655)},"./locale/zh-cn":{id:()=>536655,module:()=>e.r(536655)},"./locale/zh-hk.js":{id:()=>446820,module:()=>e.r(446820)},"./locale/zh-hk":{id:()=>446820,module:()=>e.r(446820)},"./locale/zh-mo.js":{id:()=>659396,module:()=>e.r(659396)},"./locale/zh-mo":{id:()=>659396,module:()=>e.r(659396)},"./locale/zh-tw.js":{id:()=>738643,module:()=>e.r(738643)},"./locale/zh-tw":{id:()=>738643,module:()=>e.r(738643)}})("./locale/"+t),e$(s)}catch(e){eq[t]=null}return eq[t]}function e$(e,a){var t;return e&&((t=i(a)?eX(e):eQ(e,a))?eU=t:"u">typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),eU._abbr}function eQ(e,a){if(null===a)return delete eq[e],null;var t,s=eV;if(a.abbr=e,null!=eq[e])v("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=eq[e]._config;else if(null!=a.parentLocale)if(null!=eq[a.parentLocale])s=eq[a.parentLocale]._config;else{if(null==(t=eZ(a.parentLocale)))return eB[a.parentLocale]||(eB[a.parentLocale]=[]),eB[a.parentLocale].push({name:e,config:a}),null;s=t._config}return eq[e]=new S(H(s,a)),eB[e]&&eB[e].forEach(function(e){eQ(e.name,e.config)}),e$(e),eq[e]}function eX(e){var a;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return eU;if(!s(e)){if(a=eZ(e))return a;e=[e]}return function(e){for(var a,t,s,n,r=0;r0;){if(s=eZ(n.slice(0,a).join("-")))return s;if(t&&t.length>=a&&function(e,a){var t,s=Math.min(e.length,a.length);for(t=0;t=a-1)break;a--}r++}return eU}(e)}function e1(e){var a,t=e._a;return t&&-2===M(e).overflow&&(a=t[1]<0||t[1]>11?1:t[2]<1||t[2]>eT(t[0],t[1])?2:t[3]<0||t[3]>24||24===t[3]&&(0!==t[4]||0!==t[5]||0!==t[6])?3:t[4]<0||t[4]>59?4:t[5]<0||t[5]>59?5:t[6]<0||t[6]>999?6:-1,M(e)._overflowDayOfYear&&(a<0||a>2)&&(a=2),M(e)._overflowWeeks&&-1===a&&(a=7),M(e)._overflowWeekday&&-1===a&&(a=8),M(e).overflow=a),e}var e0=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,e2=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,e6=/Z|[+-]\d\d(?::?\d\d)?/,e4=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/],["YYYYMM",/\d{6}/,!1],["YYYY",/\d{4}/,!1]],e3=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],e5=/^\/?Date\((-?\d+)/i,e7=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,e9={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function e8(e){var a,t,s,n,r,d,i=e._i,_=e0.exec(i)||e2.exec(i),o=e4.length,m=e3.length;if(_){for(a=0,M(e).iso=!0,t=o;a7)&&(m=!0)):(i=a._locale._week.dow,_=a._locale._week.doy,l=eW(ad(),i,_),n=aa(s.gg,a._a[0],l.year),r=aa(s.w,l.week),null!=s.d?((d=s.d)<0||d>6)&&(m=!0):null!=s.e?(d=s.e+i,(s.e<0||s.e>6)&&(m=!0)):d=i),r<1||r>eA(n,i,_)?M(a)._overflowWeeks=!0:null!=m?M(a)._overflowWeekday=!0:(o=eO(n,r,d,i,_),a._a[0]=o.year,a._dayOfYear=o.dayOfYear)),null!=e._dayOfYear&&(y=aa(e._a[0],L[0]),(e._dayOfYear>ey(y)||0===e._dayOfYear)&&(M(e)._overflowDayOfYear=!0),c=ex(y,0,e._dayOfYear),e._a[1]=c.getUTCMonth(),e._a[2]=c.getUTCDate()),h=0;h<3&&null==e._a[h];++h)e._a[h]=f[h]=L[h];for(;h<7;h++)e._a[h]=f[h]=null==e._a[h]?+(2===h):e._a[h];24===e._a[3]&&0===e._a[4]&&0===e._a[5]&&0===e._a[6]&&(e._nextDay=!0,e._a[3]=0),e._d=(e._useUTC?ex:ej).apply(null,f),Y=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[3]=24),e._w&&void 0!==e._w.d&&e._w.d!==Y&&(M(e).weekdayMismatch=!0)}}function as(e){if(e._f===t.ISO_8601)return void e8(e);if(e._f===t.RFC_2822)return void ae(e);e._a=[],M(e).empty=!0;var a,s,n,d,i,_,o,m,l,u,h,c=""+e._i,L=c.length,Y=0;for(i=0,h=(o=F(e._f,e._locale).match(x)||[]).length;i0&&M(e).unusedInput.push(l),c=c.slice(c.indexOf(_)+_.length),Y+=_.length),W[m])_?M(e).empty=!1:M(e).unusedTokens.push(m),null!=_&&r(eh,m)&&eh[m](_,e._a,e,m);else e._strict&&!_&&M(e).unusedTokens.push(m);M(e).charsLeftOver=L-Y,c.length>0&&M(e).unusedInput.push(c),e._a[3]<=12&&!0===M(e).bigHour&&e._a[3]>0&&(M(e).bigHour=void 0),M(e).parsedDateParts=e._a.slice(0),M(e).meridiem=e._meridiem,e._a[3]=(a=e._locale,s=e._a[3],null==(n=e._meridiem)?s:null!=a.meridiemHour?a.meridiemHour(s,n):(null!=a.isPM&&((d=a.isPM(n))&&s<12&&(s+=12),d||12!==s||(s=0)),s)),null!==(u=M(e).era)&&(e._a[0]=e._locale.erasConvertYear(u,e._a[0])),at(e),e1(e)}function an(e){var a=e._i,r=e._f;return(e._locale=e._locale||eX(e._l),null===a||void 0===r&&""===a)?c({nullInput:!0}):("string"==typeof a&&(e._i=a=e._locale.preparse(a)),D(a))?new p(e1(a)):(o(a)?e._d=a:s(r)?!function(e){var a,t,s,n,r,d,i=!1,_=e._f.length;if(0===_){M(e).invalidFormat=!0,e._d=new Date(NaN);return}for(n=0;n<_;n++)r=0,d=!1,a=k({},e),null!=e._useUTC&&(a._useUTC=e._useUTC),a._f=e._f[n],as(a),h(a)&&(d=!0),r+=M(a).charsLeftOver,r+=10*M(a).unusedTokens.length,M(a).score=r,i?rthis?this:e:c()});function ao(e,a){var t,n;if(1===a.length&&s(a[0])&&(a=a[0]),!a.length)return ad();for(n=1,t=a[0];n=0?new Date(e+400,a,t)-126227808e5:new Date(e,a,t).valueOf()}function aA(e,a,t){return e<100&&e>=0?Date.UTC(e+400,a,t)-126227808e5:Date.UTC(e,a,t)}function aE(e,a){return a.erasAbbrRegex(e)}function aF(){var e,a,t,s,n,r=[],d=[],i=[],_=[],o=this.eras();for(e=0,a=o.length;e(r=eA(e,s,n))&&(a=r),aJ.call(this,e,a,t,s,n))}function aJ(e,a,t,s,n){var r=eO(e,a,t,s,n),d=ex(r.year,0,r.dayOfYear);return this.year(d.getUTCFullYear()),this.month(d.getUTCMonth()),this.date(d.getUTCDate()),this}A("N",0,0,"eraAbbr"),A("NN",0,0,"eraAbbr"),A("NNN",0,0,"eraAbbr"),A("NNNN",0,0,"eraName"),A("NNNNN",0,0,"eraNarrow"),A("y",["y",1],"yo","eraYear"),A("y",["yy",2],0,"eraYear"),A("y",["yyy",3],0,"eraYear"),A("y",["yyyy",4],0,"eraYear"),em("N",aE),em("NN",aE),em("NNN",aE),em("NNNN",function(e,a){return a.erasNameRegex(e)}),em("NNNNN",function(e,a){return a.erasNarrowRegex(e)}),ec(["N","NN","NNN","NNNN","NNNNN"],function(e,a,t,s){var n=t._locale.erasParse(e,s,t._strict);n?M(t).era=n:M(t).invalidEra=e}),em("y",es),em("yy",es),em("yyy",es),em("yyyy",es),em("yo",function(e,a){return a._eraYearOrdinalRegex||es}),ec(["y","yy","yyy","yyyy"],0),ec(["yo"],function(e,a,t,s){var n;t._locale._eraYearOrdinalRegex&&(n=e.match(t._locale._eraYearOrdinalRegex)),t._locale.eraYearOrdinalParse?a[0]=t._locale.eraYearOrdinalParse(e,n):a[0]=parseInt(e,10)}),A(0,["gg",2],0,function(){return this.weekYear()%100}),A(0,["GG",2],0,function(){return this.isoWeekYear()%100}),az("gggg","weekYear"),az("ggggg","weekYear"),az("GGGG","isoWeekYear"),az("GGGGG","isoWeekYear"),em("G",en),em("g",en),em("GG",$,q),em("gg",$,q),em("GGGG",ea,K),em("gggg",ea,K),em("GGGGG",et,Z),em("ggggg",et,Z),eL(["gggg","ggggg","GGGG","GGGGG"],function(e,a,t,s){a[s.substr(0,2)]=eM(e)}),eL(["gg","GG"],function(e,a,s,n){a[n]=t.parseTwoDigitYear(e)}),A("Q",0,"Qo","quarter"),em("Q",V),ec("Q",function(e,a){a[1]=(eM(e)-1)*3}),A("D",["DD",2],"Do","date"),em("D",$,e_),em("DD",$,q),em("Do",function(e,a){return e?a._dayOfMonthOrdinalParse||a._ordinalParse:a._dayOfMonthOrdinalParseLenient}),ec(["D","DD"],2),ec("Do",function(e,a){a[2]=eM(e.match($)[0])});var aR=ek("Date",!0);A("DDD",["DDDD",3],"DDDo","dayOfYear"),em("DDD",ee),em("DDDD",B),ec(["DDD","DDDD"],function(e,a,t){t._dayOfYear=eM(e)}),A("m",["mm",2],0,"minute"),em("m",$,eo),em("mm",$,q),ec(["m","mm"],4);var aC=ek("Minutes",!1);A("s",["ss",2],0,"second"),em("s",$,eo),em("ss",$,q),ec(["s","ss"],5);var aI=ek("Seconds",!1);for(A("S",0,0,function(){return~~(this.millisecond()/100)}),A(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),A(0,["SSS",3],0,"millisecond"),A(0,["SSSS",4],0,function(){return 10*this.millisecond()}),A(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),A(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),A(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),A(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),A(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),em("S",ee,V),em("SS",ee,q),em("SSS",ee,B),L="SSSS";L.length<=9;L+="S")em(L,es);function aU(e,a){a[6]=eM(("0."+e)*1e3)}for(L="S";L.length<=9;L+="S")ec(L,aU);Y=ek("Milliseconds",!1),A("z",0,0,"zoneAbbr"),A("zz",0,0,"zoneName");var aG=p.prototype;function aV(e){return e}aG.add=ab,aG.calendar=function(e,a){if(1==arguments.length)if(arguments[0]){var i,m,l,u;if(i=arguments[0],D(i)||o(i)||aS(i)||_(i)||(l=s(m=i),u=!1,l&&(u=0===m.filter(function(e){return!_(e)&&aS(m)}).length),l&&u)||function(e){var a,t,s=n(e)&&!d(e),i=!1,_=["years","year","y","months","month","M","days","day","d","dates","date","D","hours","hour","h","minutes","minute","m","seconds","second","s","milliseconds","millisecond","ms"],o=_.length;for(a=0;at.valueOf():t.valueOf()t.year()||t.year()>9999)return E(t,a?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ");if(b(Date.prototype.toISOString))if(a)return this.toDate().toISOString();else return new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",E(t,"Z"));return E(t,a?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},aG.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e,a,t,s="moment",n="";return this.isLocal()||(s=0===this.utcOffset()?"moment.utc":"moment.parseZone",n="Z"),e="["+s+'("]',a=0<=this.year()&&9999>=this.year()?"YYYY":"YYYYYY",t=n+'[")]',this.format(e+a+"-MM-DD[T]HH:mm:ss.SSS"+t)},"u">typeof Symbol&&null!=Symbol.for&&(aG[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"}),aG.toJSON=function(){return this.isValid()?this.toISOString():null},aG.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},aG.unix=function(){return Math.floor(this.valueOf()/1e3)},aG.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},aG.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},aG.eraName=function(){var e,a,t,s=this.localeData().eras();for(e=0,a=s.length;eMath.abs(e)&&!s&&(e*=60);return!this._isUTC&&a&&(n=ay(this)),this._offset=e,this._isUTC=!0,null!=n&&this.add(n,"m"),r!==e&&(!a||this._changeInProgress?av(this,aD(e-r,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,t.updateOffset(this,!0),this._changeInProgress=null)),this},aG.utc=function(e){return this.utcOffset(0,e)},aG.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(ay(this),"m")),this},aG.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var e=aL(er,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},aG.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?ad(e).utcOffset():0,(this.utcOffset()-e)%60==0)},aG.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},aG.isLocal=function(){return!!this.isValid()&&!this._isUTC},aG.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},aG.isUtc=af,aG.isUTC=af,aG.zoneAbbr=function(){return this._isUTC?"UTC":""},aG.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},aG.dates=g("dates accessor is deprecated. Use date instead.",aR),aG.months=g("months accessor is deprecated. Use month instead",eH),aG.years=g("years accessor is deprecated. Use year instead",ef),aG.zone=g("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,a){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,a),this):-this.utcOffset()}),aG.isDSTShifted=g("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!i(this._isDSTShifted))return this._isDSTShifted;var e,a={};return k(a,this),(a=an(a))._a?(e=a._isUTC?u(a._a):ad(a._a),this._isDSTShifted=this.isValid()&&function(e,a){var t,s=Math.min(e.length,a.length),n=Math.abs(e.length-a.length),r=0;for(t=0;t0):this._isDSTShifted=!1,this._isDSTShifted});var aq=S.prototype;function aB(e,a,t,s){var n=eX(),r=u().set(s,a);return n[t](r,e)}function aK(e,a,t){if(_(e)&&(a=e,e=void 0),e=e||"",null!=a)return aB(e,a,t,"month");var s,n=[];for(s=0;s<12;s++)n[s]=aB(e,s,t,"month");return n}function aZ(e,a,t,s){"boolean"==typeof e||(t=a=e,e=!1),_(a)&&(t=a,a=void 0),a=a||"";var n,r=eX(),d=e?r._week.dow:0,i=[];if(null!=t)return aB(a,(t+d)%7,s,"day");for(n=0;n<7;n++)i[n]=aB(a,(n+d)%7,s,"day");return i}aq.calendar=function(e,a,t){var s=this._calendar[e]||this._calendar.sameElse;return b(s)?s.call(a,t):s},aq.longDateFormat=function(e){var a=this._longDateFormat[e],t=this._longDateFormat[e.toUpperCase()];return a||!t?a:(this._longDateFormat[e]=t.match(x).map(function(e){return"MMMM"===e||"MM"===e||"DD"===e||"dddd"===e?e.slice(1):e}).join(""),this._longDateFormat[e])},aq.invalidDate=function(){return this._invalidDate},aq.ordinal=function(e){return this._ordinal.replace("%d",e)},aq.preparse=aV,aq.postformat=aV,aq.relativeTime=function(e,a,t,s){var n=this._relativeTime[t];return b(n)?n(e,a,t,s):n.replace(/%d/i,e)},aq.pastFuture=function(e,a){var t=this._relativeTime[e>0?"future":"past"];return b(t)?t(a):t.replace(/%s/i,a)},aq.set=function(e){var a,t;for(t in e)r(e,t)&&(b(a=e[t])?this[t]=a:this["_"+t]=a);this._config=e,this._dayOfMonthOrdinalParseLenient=RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},aq.eras=function(e,a){var s,n,r,d=this._eras||eX("en")._eras;for(s=0,n=d.length;s=0)return _[s]},aq.erasConvertYear=function(e,a){var s=e.since<=e.until?1:-1;return void 0===a?t(e.since).year():t(e.since).year()+(a-e.offset)*s},aq.erasAbbrRegex=function(e){return r(this,"_erasAbbrRegex")||aF.call(this),e?this._erasAbbrRegex:this._erasRegex},aq.erasNameRegex=function(e){return r(this,"_erasNameRegex")||aF.call(this),e?this._erasNameRegex:this._erasRegex},aq.erasNarrowRegex=function(e){return r(this,"_erasNarrowRegex")||aF.call(this),e?this._erasNarrowRegex:this._erasRegex},aq.months=function(e,a){return e?s(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||ew).test(a)?"format":"standalone"][e.month()]:s(this._months)?this._months:this._months.standalone},aq.monthsShort=function(e,a){return e?s(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[ew.test(a)?"format":"standalone"][e.month()]:s(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},aq.monthsParse=function(e,a,t){var s,n,r;if(this._monthsParseExact)return ev.call(this,e,a,t);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;s<12;s++){if(n=u([2e3,s]),t&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=RegExp("^"+this.months(n,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=RegExp("^"+this.monthsShort(n,"").replace(".","")+"$","i")),t||this._monthsParse[s]||(r="^"+this.months(n,"")+"|^"+this.monthsShort(n,""),this._monthsParse[s]=RegExp(r.replace(".",""),"i")),t&&"MMMM"===a&&this._longMonthsParse[s].test(e))return s;if(t&&"MMM"===a&&this._shortMonthsParse[s].test(e))return s;if(!t&&this._monthsParse[s].test(e))return s}},aq.monthsRegex=function(e){return this._monthsParseExact?(r(this,"_monthsRegex")||eS.call(this),e)?this._monthsStrictRegex:this._monthsRegex:(r(this,"_monthsRegex")||(this._monthsRegex=ei),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},aq.monthsShortRegex=function(e){return this._monthsParseExact?(r(this,"_monthsRegex")||eS.call(this),e)?this._monthsShortStrictRegex:this._monthsShortRegex:(r(this,"_monthsShortRegex")||(this._monthsShortRegex=ei),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},aq.week=function(e){return eW(e,this._week.dow,this._week.doy).week},aq.firstDayOfYear=function(){return this._week.doy},aq.firstDayOfWeek=function(){return this._week.dow},aq.weekdays=function(e,a){var t=s(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(a)?"format":"standalone"];return!0===e?eE(t,this._week.dow):e?t[e.day()]:t},aq.weekdaysMin=function(e){return!0===e?eE(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},aq.weekdaysShort=function(e){return!0===e?eE(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},aq.weekdaysParse=function(e,a,t){var s,n,r;if(this._weekdaysParseExact)return ez.call(this,e,a,t);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),s=0;s<7;s++){if(n=u([2e3,1]).day(s),t&&!this._fullWeekdaysParse[s]&&(this._fullWeekdaysParse[s]=RegExp("^"+this.weekdays(n,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[s]=RegExp("^"+this.weekdaysShort(n,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[s]=RegExp("^"+this.weekdaysMin(n,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[s]||(r="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[s]=RegExp(r.replace(".",""),"i")),t&&"dddd"===a&&this._fullWeekdaysParse[s].test(e))return s;if(t&&"ddd"===a&&this._shortWeekdaysParse[s].test(e))return s;if(t&&"dd"===a&&this._minWeekdaysParse[s].test(e))return s;else if(!t&&this._weekdaysParse[s].test(e))return s}},aq.weekdaysRegex=function(e){return this._weekdaysParseExact?(r(this,"_weekdaysRegex")||eN.call(this),e)?this._weekdaysStrictRegex:this._weekdaysRegex:(r(this,"_weekdaysRegex")||(this._weekdaysRegex=ei),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},aq.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(r(this,"_weekdaysRegex")||eN.call(this),e)?this._weekdaysShortStrictRegex:this._weekdaysShortRegex:(r(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ei),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},aq.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(r(this,"_weekdaysRegex")||eN.call(this),e)?this._weekdaysMinStrictRegex:this._weekdaysMinRegex:(r(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=ei),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},aq.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},aq.meridiem=function(e,a,t){return e>11?t?"pm":"PM":t?"am":"AM"},e$("en",{eras:[{since:"0001-01-01",until:Infinity,offset:1,name:"Anno Domini",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"Before Christ",narrow:"BC",abbr:"BC"}],dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var a=e%10,t=1===eM(e%100/10)?"th":1===a?"st":2===a?"nd":3===a?"rd":"th";return e+t}}),t.lang=g("moment.lang is deprecated. Use moment.locale instead.",e$),t.langData=g("moment.langData is deprecated. Use moment.localeData instead.",eX);var a$=Math.abs;function aQ(e,a,t,s){var n=aD(a,t);return e._milliseconds+=s*n._milliseconds,e._days+=s*n._days,e._months+=s*n._months,e._bubble()}function aX(e){return e<0?Math.floor(e):Math.ceil(e)}function a1(e){return 4800*e/146097}function a0(e){return 146097*e/4800}function a2(e){return function(){return this.as(e)}}var a6=a2("ms"),a4=a2("s"),a3=a2("m"),a5=a2("h"),a7=a2("d"),a9=a2("w"),a8=a2("M"),te=a2("Q"),ta=a2("y");function tt(e){return function(){return this.isValid()?this._data[e]:NaN}}var ts=tt("milliseconds"),tn=tt("seconds"),tr=tt("minutes"),td=tt("hours"),ti=tt("days"),t_=tt("months"),to=tt("years"),tm=Math.round,tl={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function tu(e,a,t,s,n){return n.relativeTime(a||1,!!t,e,s)}var tM=Math.abs;function th(e){return(e>0)-(e<0)||+e}function tc(){if(!this.isValid())return this.localeData().invalidDate();var e,a,t,s,n,r,d,i,_=tM(this._milliseconds)/1e3,o=tM(this._days),m=tM(this._months),l=this.asSeconds();return l?(e=eu(_/60),a=eu(e/60),_%=60,e%=60,t=eu(m/12),m%=12,s=_?_.toFixed(3).replace(/\.?0+$/,""):"",n=l<0?"-":"",r=th(this._months)!==th(l)?"-":"",d=th(this._days)!==th(l)?"-":"",i=th(this._milliseconds)!==th(l)?"-":"",n+"P"+(t?r+t+"Y":"")+(m?r+m+"M":"")+(o?d+o+"D":"")+(a||e||_?"T":"")+(a?i+a+"H":"")+(e?i+e+"M":"")+(_?i+s+"S":"")):"P0D"}var tL=al.prototype;return tL.isValid=function(){return this._isValid},tL.abs=function(){var e=this._data;return this._milliseconds=a$(this._milliseconds),this._days=a$(this._days),this._months=a$(this._months),e.milliseconds=a$(e.milliseconds),e.seconds=a$(e.seconds),e.minutes=a$(e.minutes),e.hours=a$(e.hours),e.months=a$(e.months),e.years=a$(e.years),this},tL.add=function(e,a){return aQ(this,e,a,1)},tL.subtract=function(e,a){return aQ(this,e,a,-1)},tL.as=function(e){if(!this.isValid())return NaN;var a,t,s=this._milliseconds;if("month"===(e=N(e))||"quarter"===e||"year"===e)switch(a=this._days+s/864e5,t=this._months+a1(a),e){case"month":return t;case"quarter":return t/3;case"year":return t/12}else switch(a=this._days+Math.round(a0(this._months)),e){case"week":return a/7+s/6048e5;case"day":return a+s/864e5;case"hour":return 24*a+s/36e5;case"minute":return 1440*a+s/6e4;case"second":return 86400*a+s/1e3;case"millisecond":return Math.floor(864e5*a)+s;default:throw Error("Unknown unit "+e)}},tL.asMilliseconds=a6,tL.asSeconds=a4,tL.asMinutes=a3,tL.asHours=a5,tL.asDays=a7,tL.asWeeks=a9,tL.asMonths=a8,tL.asQuarters=te,tL.asYears=ta,tL.valueOf=a6,tL._bubble=function(){var e,a,t,s,n,r=this._milliseconds,d=this._days,i=this._months,_=this._data;return r>=0&&d>=0&&i>=0||r<=0&&d<=0&&i<=0||(r+=864e5*aX(a0(i)+d),d=0,i=0),_.milliseconds=r%1e3,_.seconds=(e=eu(r/1e3))%60,_.minutes=(a=eu(e/60))%60,_.hours=(t=eu(a/60))%24,d+=eu(t/24),i+=n=eu(a1(d)),d-=aX(a0(n)),s=eu(i/12),i%=12,_.days=d,_.months=i,_.years=s,this},tL.clone=function(){return aD(this)},tL.get=function(e){return e=N(e),this.isValid()?this[e+"s"]():NaN},tL.milliseconds=ts,tL.seconds=tn,tL.minutes=tr,tL.hours=td,tL.days=ti,tL.weeks=function(){return eu(this.days()/7)},tL.months=t_,tL.years=to,tL.humanize=function(e,a){if(!this.isValid())return this.localeData().invalidDate();var t,s,n,r,d,i,_,o,m,l,u,M,h,c=!1,L=tl;return"object"==typeof e&&(a=e,e=!1),"boolean"==typeof e&&(c=e),"object"==typeof a&&(L=Object.assign({},tl,a),null!=a.s&&null==a.ss&&(L.ss=a.s-1)),M=this.localeData(),t=!c,s=L,n=aD(this).abs(),r=tm(n.as("s")),d=tm(n.as("m")),i=tm(n.as("h")),_=tm(n.as("d")),o=tm(n.as("M")),m=tm(n.as("w")),l=tm(n.as("y")),u=r<=s.ss&&["s",r]||r0,u[4]=M,h=tu.apply(null,u),c&&(h=M.pastFuture(+this,h)),M.postformat(h)},tL.toISOString=tc,tL.toString=tc,tL.toJSON=tc,tL.locale=ax,tL.localeData=aO,tL.toIsoString=g("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",tc),tL.lang=aP,A("X",0,0,"unix"),A("x",0,0,"valueOf"),em("x",en),em("X",/[+-]?\d+(\.\d{1,3})?/),ec("X",function(e,a,t){t._d=new Date(1e3*parseFloat(e))}),ec("x",function(e,a,t){t._d=new Date(eM(e))}),t.version="2.30.1",R=ad,t.fn=aG,t.min=function(){var e=[].slice.call(arguments,0);return ao("isBefore",e)},t.max=function(){var e=[].slice.call(arguments,0);return ao("isAfter",e)},t.now=function(){return Date.now?Date.now():+new Date},t.utc=u,t.unix=function(e){return ad(1e3*e)},t.months=function(e,a){return aK(e,a,"months")},t.isDate=o,t.locale=e$,t.invalid=c,t.duration=aD,t.isMoment=D,t.weekdays=function(e,a,t){return aZ(e,a,t,"weekdays")},t.parseZone=function(){return ad.apply(null,arguments).parseZone()},t.localeData=eX,t.isDuration=au,t.monthsShort=function(e,a){return aK(e,a,"monthsShort")},t.weekdaysMin=function(e,a,t){return aZ(e,a,t,"weekdaysMin")},t.defineLocale=eQ,t.updateLocale=function(e,a){if(null!=a){var t,s,n=eV;null!=eq[e]&&null!=eq[e].parentLocale?eq[e].set(H(eq[e]._config,a)):(null!=(s=eZ(e))&&(n=s._config),a=H(n,a),null==s&&(a.abbr=e),(t=new S(a)).parentLocale=eq[e],eq[e]=t),e$(e)}else null!=eq[e]&&(null!=eq[e].parentLocale?(eq[e]=eq[e].parentLocale,e===e$()&&e$(e)):null!=eq[e]&&delete eq[e]);return eq[e]},t.locales=function(){return G(eq)},t.weekdaysShort=function(e,a,t){return aZ(e,a,t,"weekdaysShort")},t.normalizeUnits=N,t.relativeTimeRounding=function(e){return void 0===e?tm:"function"==typeof e&&(tm=e,!0)},t.relativeTimeThreshold=function(e,a){return void 0!==tl[e]&&(void 0===a?tl[e]:(tl[e]=a,"s"===e&&(tl.ss=a-1),!0))},t.calendarFormat=function(e,a){var t=e.diff(a,"days",!0);return t<-6?"sameElse":t<-1?"lastWeek":t<0?"lastDay":t<1?"sameDay":t<2?"nextDay":t<7?"nextWeek":"sameElse"},t.prototype=aG,t.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},t}()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03e_5nw.1urn4.js b/litellm/proxy/_experimental/out/_next/static/chunks/03e_5nw.1urn4.js new file mode 100644 index 00000000000..d6539931fe4 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03e_5nw.1urn4.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,250980,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,l],250980)},309426,e=>{"use strict";var t=e.i(290571),l=e.i(444755),r=e.i(673706),s=e.i(271645),a=e.i(46757);let n=(0,r.makeClassName)("Col"),i=s.default.forwardRef((e,r)=>{let i,o,c,d,{numColSpan:u=1,numColSpanSm:m,numColSpanMd:p,numColSpanLg:g,children:f,className:h}=e,x=(0,t.__rest)(e,["numColSpan","numColSpanSm","numColSpanMd","numColSpanLg","children","className"]),v=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"";return s.default.createElement("div",Object.assign({ref:r,className:(0,l.tremorTwMerge)(n("root"),(i=v(u,a.colSpan),o=v(m,a.colSpanSm),c=v(p,a.colSpanMd),d=v(g,a.colSpanLg),(0,l.tremorTwMerge)(i,o,c,d)),h)},x),f)});i.displayName="Col",e.s(["Col",0,i],309426)},950724,(e,t,l)=>{t.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},100236,(e,t,l)=>{t.exports=e.g&&e.g.Object===Object&&e.g},139088,(e,t,l)=>{var r=e.r(100236),s="object"==typeof self&&self&&self.Object===Object&&self;t.exports=r||s||Function("return this")()},631926,(e,t,l)=>{var r=e.r(139088);t.exports=function(){return r.Date.now()}},748891,(e,t,l)=>{var r=/\s/;t.exports=function(e){for(var t=e.length;t--&&r.test(e.charAt(t)););return t}},830364,(e,t,l)=>{var r=e.r(748891),s=/^\s+/;t.exports=function(e){return e?e.slice(0,r(e)+1).replace(s,""):e}},630353,(e,t,l)=>{t.exports=e.r(139088).Symbol},243436,(e,t,l)=>{var r=e.r(630353),s=Object.prototype,a=s.hasOwnProperty,n=s.toString,i=r?r.toStringTag:void 0;t.exports=function(e){var t=a.call(e,i),l=e[i];try{e[i]=void 0;var r=!0}catch(e){}var s=n.call(e);return r&&(t?e[i]=l:delete e[i]),s}},223243,(e,t,l)=>{var r=Object.prototype.toString;t.exports=function(e){return r.call(e)}},377684,(e,t,l)=>{var r=e.r(630353),s=e.r(243436),a=e.r(223243),n=r?r.toStringTag:void 0;t.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":n&&n in Object(e)?s(e):a(e)}},877289,(e,t,l)=>{t.exports=function(e){return null!=e&&"object"==typeof e}},361884,(e,t,l)=>{var r=e.r(377684),s=e.r(877289);t.exports=function(e){return"symbol"==typeof e||s(e)&&"[object Symbol]"==r(e)}},773759,(e,t,l)=>{var r=e.r(830364),s=e.r(950724),a=e.r(361884),n=0/0,i=/^[-+]0x[0-9a-f]+$/i,o=/^0b[01]+$/i,c=/^0o[0-7]+$/i,d=parseInt;t.exports=function(e){if("number"==typeof e)return e;if(a(e))return n;if(s(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=s(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var l=o.test(e);return l||c.test(e)?d(e.slice(2),l?2:8):i.test(e)?n:+e}},374009,(e,t,l)=>{var r=e.r(950724),s=e.r(631926),a=e.r(773759),n=Math.max,i=Math.min;t.exports=function(e,t,l){var o,c,d,u,m,p,g=0,f=!1,h=!1,x=!0;if("function"!=typeof e)throw TypeError("Expected a function");function v(t){var l=o,r=c;return o=c=void 0,g=t,u=e.apply(r,l)}function y(e){var l=e-p,r=e-g;return void 0===p||l>=t||l<0||h&&r>=d}function b(){var e,l,r,a=s();if(y(a))return w(a);m=setTimeout(b,(e=a-p,l=a-g,r=t-e,h?i(r,d-l):r))}function w(e){return(m=void 0,x&&o)?v(e):(o=c=void 0,u)}function j(){var e,l=s(),r=y(l);if(o=arguments,c=this,p=l,r){if(void 0===m)return g=e=p,m=setTimeout(b,t),f?v(e):u;if(h)return clearTimeout(m),m=setTimeout(b,t),v(p)}return void 0===m&&(m=setTimeout(b,t)),u}return t=a(t)||0,r(l)&&(f=!!l.leading,d=(h="maxWait"in l)?n(a(l.maxWait)||0,t):d,x="trailing"in l?!!l.trailing:x),j.cancel=function(){void 0!==m&&clearTimeout(m),g=0,o=p=c=m=void 0},j.flush=function(){return void 0===m?u:w(s())},j}},435451,e=>{"use strict";var t=e.i(843476),l=e.i(290571),r=e.i(271645);let s=e=>{var t=(0,l.__rest)(e,[]);return r.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),r.default.createElement("path",{d:"M12 4v16m8-8H4"}))},a=e=>{var t=(0,l.__rest)(e,[]);return r.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),r.default.createElement("path",{d:"M20 12H4"}))};var n=e.i(444755),i=e.i(673706),o=e.i(677955);let c="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",d="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",u=r.default.forwardRef((e,t)=>{let{onSubmit:u,enableStepper:m=!0,disabled:p,onValueChange:g,onChange:f}=e,h=(0,l.__rest)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),x=(0,r.useRef)(null),[v,y]=r.default.useState(!1),b=r.default.useCallback(()=>{y(!0)},[]),w=r.default.useCallback(()=>{y(!1)},[]),[j,N]=r.default.useState(!1),S=r.default.useCallback(()=>{N(!0)},[]),k=r.default.useCallback(()=>{N(!1)},[]);return r.default.createElement(o.default,Object.assign({type:"number",ref:(0,i.mergeRefs)([x,t]),disabled:p,makeInputClassName:(0,i.makeClassName)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null==(t=x.current)?void 0:t.value;null==u||u(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&b(),"ArrowUp"===e.key&&S()},onKeyUp:e=>{"ArrowDown"===e.key&&w(),"ArrowUp"===e.key&&k()},onChange:e=>{p||(null==g||g(parseFloat(e.target.value)),null==f||f(e))},stepper:m?r.default.createElement("div",{className:(0,n.tremorTwMerge)("flex justify-center align-middle")},r.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;p||(null==(e=x.current)||e.stepDown(),null==(t=x.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,n.tremorTwMerge)(!p&&d,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},r.default.createElement(a,{"data-testid":"step-down",className:(v?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),r.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;p||(null==(e=x.current)||e.stepUp(),null==(t=x.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,n.tremorTwMerge)(!p&&d,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},r.default.createElement(s,{"data-testid":"step-up",className:(j?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},h))});u.displayName="NumberInput",e.s(["default",0,({step:e=.01,style:l={width:"100%"},placeholder:r="Enter a numerical value",min:s,max:a,onChange:n,...i})=>(0,t.jsx)(u,{onWheel:e=>e.currentTarget.blur(),step:e,style:l,placeholder:r,min:s,max:a,onChange:n,...i})],435451)},355619,e=>{"use strict";var t=e.i(602869);let l=async(e,l,r)=>{try{if(null===e||null===l)return;if(null!==r){let s=(await (0,t.modelAvailableCall)(r,e,l,!0,null,!0)).data.map(e=>e.id),a=[],n=[];return s.forEach(e=>{e.endsWith("/*")?a.push(e):n.push(e)}),[...a,...n]}}catch(e){console.error("Error fetching user models:",e)}};e.s(["excludeProxyWideSentinel",0,e=>e.filter(e=>"all-proxy-models"!==e),"fetchAvailableModelsForTeamOrKey",0,l,"getModelDisplayName",0,e=>{if("all-proxy-models"===e)return"All Proxy Models";if(e.endsWith("/*")){let t=e.replace("/*","");return`All ${t} models`}return e},"hasAllModelsSentinel",0,e=>e.includes("all-proxy-models")||e.includes("all-team-models"),"unfurlWildcardModelsInList",0,(e,t)=>{let l=[],r=[];return e.forEach(e=>{if(e.endsWith("/*")){let s=e.replace("/*",""),a=t.filter(e=>e.startsWith(s+"/"));r.push(...a),l.push(e)}else r.push(e)}),[...l,...r].filter((e,t,l)=>l.indexOf(e)===t)}])},860585,e=>{"use strict";var t=e.i(843476),l=e.i(199133);let{Option:r}=l.Select;e.s(["default",0,({value:e,onChange:s,className:a="",style:n={}})=>(0,t.jsxs)(l.Select,{style:{width:"100%",...n},value:e||void 0,onChange:s,className:a,placeholder:"n/a",allowClear:!0,children:[(0,t.jsx)(r,{value:"1h",children:"hourly"}),(0,t.jsx)(r,{value:"24h",children:"daily"}),(0,t.jsx)(r,{value:"7d",children:"weekly"}),(0,t.jsx)(r,{value:"30d",children:"monthly"})]}),"getBudgetDurationLabel",0,e=>e?({"1h":"hourly","24h":"daily","7d":"weekly","30d":"monthly"})[e]||e:"Not set"])},213205,e=>{"use strict";e.i(247167);var t=e.i(931067),l=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M678.3 642.4c24.2-13 51.9-20.4 81.4-20.4h.1c3 0 4.4-3.6 2.2-5.6a371.67 371.67 0 00-103.7-65.8c-.4-.2-.8-.3-1.2-.5C719.2 505 759.6 431.7 759.6 349c0-137-110.8-248-247.5-248S264.7 212 264.7 349c0 82.7 40.4 156 102.6 201.1-.4.2-.8.3-1.2.5-44.7 18.9-84.8 46-119.3 80.6a373.42 373.42 0 00-80.4 119.5A373.6 373.6 0 00137 888.8a8 8 0 008 8.2h59.9c4.3 0 7.9-3.5 8-7.8 2-77.2 32.9-149.5 87.6-204.3C357 628.2 432.2 597 512.2 597c56.7 0 111.1 15.7 158 45.1a8.1 8.1 0 008.1.3zM512.2 521c-45.8 0-88.9-17.9-121.4-50.4A171.2 171.2 0 01340.5 349c0-45.9 17.9-89.1 50.3-121.6S466.3 177 512.2 177s88.9 17.9 121.4 50.4A171.2 171.2 0 01683.9 349c0 45.9-17.9 89.1-50.3 121.6C601.1 503.1 558 521 512.2 521zM880 759h-84v-84c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v84h-84c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h84v84c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-84h84c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"}}]},name:"user-add",theme:"outlined"};var s=e.i(9583),a=l.forwardRef(function(e,a){return l.createElement(s.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["UserAddOutlined",0,a],213205)},350967,46757,e=>{"use strict";var t=e.i(290571),l=e.i(444755),r=e.i(673706),s=e.i(271645);let a={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},n={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},i={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},o={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,a,"gridColsLg",0,o,"gridColsMd",0,i,"gridColsSm",0,n],46757);let c=(0,r.makeClassName)("Grid"),d=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=s.default.forwardRef((e,r)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:p,numItemsLg:g,children:f,className:h}=e,x=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),v=d(u,a),y=d(m,n),b=d(p,i),w=d(g,o),j=(0,l.tremorTwMerge)(v,y,b,w);return s.default.createElement("div",Object.assign({ref:r,className:(0,l.tremorTwMerge)(c("root"),"grid",j,h)},x),f)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},500727,e=>{"use strict";var t=e.i(266027),l=e.i(243652),r=e.i(602869),s=e.i(135214);let a=(0,l.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:l}=(0,s.default)();return(0,t.useQuery)({queryKey:a.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,r.fetchMCPServers)(l,e),enabled:!!l})}])},699857,e=>{"use strict";var t=e.i(266027),l=e.i(243652),r=e.i(602869),s=e.i(135214);let a=(0,l.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,s.default)();return(0,t.useQuery)({queryKey:a.list(),queryFn:async()=>await (0,r.fetchMCPToolsets)(e),enabled:!!e})}])},916940,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(199133),s=e.i(602869);e.s(["default",0,({onChange:e,value:a,className:n,accessToken:i,placeholder:o="Select vector stores",disabled:c=!1})=>{let[d,u]=(0,l.useState)([]),[m,p]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{(async()=>{if(i){p(!0);try{let e=await (0,s.vectorStoreListCall)(i);e.data&&u(e.data)}catch(e){console.error("Error fetching vector stores:",e)}finally{p(!1)}}})()},[i]),(0,t.jsx)("div",{children:(0,t.jsx)(r.Select,{mode:"multiple",placeholder:o,onChange:e,value:a,loading:m,className:n,allowClear:!0,options:d.map(e=>({label:`${e.vector_store_name||e.vector_store_id} (${e.vector_store_id})`,value:e.vector_store_id,title:e.vector_store_description||e.vector_store_id})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:c})})}])},75921,e=>{"use strict";var t=e.i(843476),l=e.i(266027),r=e.i(243652),s=e.i(602869),a=e.i(135214);let n=(0,r.createQueryKeys)("mcpAccessGroups");var i=e.i(500727),o=e.i(699857),c=e.i(199133),d=e.i(234713);let u="toolset:";e.s(["default",0,({onChange:e,value:r,className:m,accessToken:p,placeholder:g="Select MCP servers",disabled:f=!1,teamId:h,allowNoMcpServers:x=!1,allowAllProxyMcpServers:v=!1})=>{let{data:y=[],isLoading:b}=(0,i.useMCPServers)(h),{data:w=[],isLoading:j}=(()=>{let{accessToken:e}=(0,a.default)();return(0,l.useQuery)({queryKey:n.list({}),queryFn:async()=>await (0,s.fetchMCPAccessGroups)(e),enabled:!!e})})(),{data:N=[],isLoading:S}=(0,o.useMCPToolsets)(),k=new Set(w),C=[...w.map(e=>({label:e,value:e,type:"accessGroup",searchText:`${e} Access Group`})),...y.map(e=>({label:`${e.server_name||e.server_id} (${e.server_id})`,value:e.server_id,type:"server",searchText:`${e.server_name||e.server_id} ${e.server_id} MCP Server`})),...N.map(e=>({label:e.toolset_name,value:`${u}${e.toolset_id}`,type:"toolset",searchText:`${e.toolset_name} ${e.toolset_id} Toolset`}))],_={accessGroup:"#52c41a",server:"#1890ff",toolset:"#722ed1"},M={accessGroup:"Access Group",server:"MCP Server",toolset:"Toolset"},E=[...r?.servers||[],...r?.accessGroups||[],...(r?.toolsets||[]).map(e=>`${u}${e}`)],T=x&&E.includes(d.NO_MCP_SERVERS_SENTINEL),L=E.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL);return(0,t.jsx)("div",{children:(0,t.jsxs)(c.Select,{mode:"multiple",placeholder:g,onChange:t=>{if(v&&t.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL))return void e({servers:[d.ALL_PROXY_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});if(x&&t.includes(d.NO_MCP_SERVERS_SENTINEL))return void e({servers:[d.NO_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});let l=t.filter(e=>e.startsWith(u)).map(e=>e.slice(u.length)),r=t.filter(e=>!e.startsWith(u));e({servers:r.filter(e=>!k.has(e)),accessGroups:r.filter(e=>k.has(e)),toolsets:l})},value:E,loading:b||j||S,className:m,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:f,filterOption:(e,t)=>t?.value===d.NO_MCP_SERVERS_SENTINEL||t?.value===d.ALL_PROXY_MCP_SERVERS_SENTINEL||(C.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:[(v||L)&&(0,t.jsx)(c.Select.Option,{value:d.ALL_PROXY_MCP_SERVERS_SENTINEL,label:"All Proxy MCP Servers",children:(0,t.jsx)("span",{style:{color:"#1890ff",fontWeight:500},children:"All Proxy MCP Servers"})},d.ALL_PROXY_MCP_SERVERS_SENTINEL),x&&(0,t.jsx)(c.Select.Option,{value:d.NO_MCP_SERVERS_SENTINEL,label:"No MCP Servers",children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{flex:1},children:"No MCP Servers"}),(0,t.jsx)("span",{style:{color:"#8c8c8c",fontSize:"12px",fontWeight:500,opacity:.8},children:"Block all"})]})},d.NO_MCP_SERVERS_SENTINEL),C.map(e=>(0,t.jsx)(c.Select.Option,{value:e.value,label:e.label,disabled:T||L,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:_[e.type],flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:_[e.type],fontSize:"12px",fontWeight:500,opacity:.8},children:M[e.type]})]})},e.value))]})})}],75921)},983561,e=>{"use strict";e.i(247167);var t=e.i(931067),l=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M300 328a60 60 0 10120 0 60 60 0 10-120 0zM852 64H172c-17.7 0-32 14.3-32 32v660c0 17.7 14.3 32 32 32h680c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-32 660H204V128h616v596zM604 328a60 60 0 10120 0 60 60 0 10-120 0zm250.2 556H169.8c-16.5 0-29.8 14.3-29.8 32v36c0 4.4 3.3 8 7.4 8h729.1c4.1 0 7.4-3.6 7.4-8v-36c.1-17.7-13.2-32-29.7-32zM664 508H360c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"robot",theme:"outlined"};var s=e.i(9583),a=l.forwardRef(function(e,a){return l.createElement(s.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["RobotOutlined",0,a],983561)},797672,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,l],797672)},992619,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(779241),s=e.i(599724),a=e.i(199133),n=e.i(983561),i=e.i(695411);e.s(["default",0,({accessToken:e,value:o,placeholder:c="Select a Model",onChange:d,disabled:u=!1,style:m,className:p,showLabel:g=!0,labelText:f="Select Model"})=>{let[h,x]=(0,l.useState)(o),[v,y]=(0,l.useState)(!1),[b,w]=(0,l.useState)([]),j=(0,l.useRef)(null);return(0,l.useEffect)(()=>{x(o)},[o]),(0,l.useEffect)(()=>{e&&(async()=>{try{let t=await (0,i.fetchAvailableModels)(e);t.length>0&&w(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[g&&(0,t.jsxs)(s.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(n.RobotOutlined,{className:"mr-2"})," ",f]}),(0,t.jsx)(a.Select,{value:h,placeholder:c,onChange:e=>{"custom"===e?(y(!0),x(void 0)):(y(!1),x(e),d&&d(e))},options:[...Array.from(new Set(b.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${p||""}`,disabled:u}),v&&(0,t.jsx)(r.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{j.current&&clearTimeout(j.current),j.current=setTimeout(()=>{x(e),d&&d(e)},500)},disabled:u})]})}])},409797,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDownIcon",()=>t.default])},361653,e=>{"use strict";let t=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,t])},91739,e=>{"use strict";var t=e.i(544195);e.s(["Radio",()=>t.default])},988297,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,l],988297)},531516,696609,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(536916),s=e.i(599724),a=e.i(409797),n=e.i(246349),n=n;let i=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,o=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,c=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,d=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function u(e,t=""){let l=e.toLowerCase();if(d.test(l))return"read";if(i.test(l))return"delete";if(c.test(l))return"update";if(o.test(l))return"create";if(t){let e=t.toLowerCase();if(d.test(e))return"read";if(i.test(e))return"delete";if(c.test(e))return"update";if(o.test(e))return"create"}return"unknown"}function m(e){let t={read:[],create:[],update:[],delete:[],unknown:[]};for(let l of e)t[u(l.name,l.description)].push(l);return t}let p={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,p,"classifyToolOp",0,u,"groupToolsByCrud",0,m],696609);let g=["read","create","update","delete","unknown"],f={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},h={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},x={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:i,onChange:o,readOnly:c=!1,searchFilter:d=""})=>{let[u,v]=(0,l.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),y=(0,l.useMemo)(()=>m(e),[e]),b=(0,l.useMemo)(()=>new Set(void 0===i?e.map(e=>e.name):i),[i,e]),w=e=>{if(c)return;let t=new Set(b);t.has(e)?t.delete(e):t.add(e),o(Array.from(t))};return 0===e.length?null:(0,t.jsx)("div",{className:"space-y-3",children:g.map(e=>{let l,i=y[e];if(0===i.length)return null;if(d){let e=d.toLowerCase();if(!i.some(t=>t.name.toLowerCase().includes(e)||(t.description??"").toLowerCase().includes(e)))return null}let m=p[e],g=(l=y[e]).length>0&&l.every(e=>b.has(e.name)),j=(e=>{let t=y[e];if(0===t.length)return!1;let l=t.filter(e=>b.has(e.name)).length;return l>0&&l{v(t=>({...t,[e]:!t[e]}))},children:[N?(0,t.jsx)(n.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,t.jsx)(a.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:m.label}),(0,t.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${f[m.risk]}`,children:"high"===m.risk?"High Risk":"medium"===m.risk?"Medium Risk":"low"===m.risk?"Safe":"Unclassified"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[i.filter(e=>b.has(e.name)).length,"/",i.length," allowed"]})]}),!c&&(0,t.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,t.jsx)(s.Text,{className:"text-xs text-gray-500",children:g?"All on":j?"Partial":"All off"}),(0,t.jsx)(r.Checkbox,{checked:g,indeterminate:j,onChange:t=>((e,t)=>{if(c)return;let l=new Set(b);for(let r of y[e])t?l.add(r.name):l.delete(r.name);o(Array.from(l))})(e,t.target.checked),onClick:e=>e.stopPropagation()})]})]}),!N&&(0,t.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:m.description}),!N&&(0,t.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:i.filter(e=>!d||e.name.toLowerCase().includes(d.toLowerCase())||(e.description??"").toLowerCase().includes(d.toLowerCase())).map(e=>{let l,a=(l=e.name,b.has(l));return(0,t.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!c?"cursor-pointer":""} ${a?"":"opacity-60"}`,onClick:()=>w(e.name),children:[(0,t.jsx)(r.Checkbox,{checked:a,onChange:()=>w(e.name),disabled:c,onClick:e=>e.stopPropagation()}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)(s.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,t.jsx)(s.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,t.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${a?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:a?"on":"off"})]},e.name)})})]},e)})})}],531516)},37727,e=>{"use strict";var t=e.i(841947);e.s(["X",()=>t.default])},841947,e=>{"use strict";let t=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,t])},603908,e=>{"use strict";let t=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,t])},107233,e=>{"use strict";var t=e.i(603908);e.s(["Plus",()=>t.default])},425063,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);e.s(["ArrowDown",0,t],425063)},158392,63209,e=>{"use strict";var t=e.i(843476),l=e.i(311451);let r={ttl:3600,lowest_latency_buffer:0},s=({routingStrategyArgs:e})=>{let s={ttl:"Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Latency-Based Configuration"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Fine-tune latency-based routing behavior"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e||r).map(([e,r])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:e.replace(/_/g," ")}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:s[e]||""}),(0,t.jsx)(l.Input,{name:e,defaultValue:"object"==typeof r?JSON.stringify(r,null,2):r?.toString(),className:"font-mono text-sm w-full"})]})},e))})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"})]})},a=({routerSettings:e,routerFieldsMetadata:r})=>(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Reliability & Retries"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure retry logic and failure handling"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e).filter(([e])=>"fallbacks"!=e&&"context_window_fallbacks"!=e&&"routing_strategy_args"!=e&&"routing_strategy"!=e&&"enable_tag_filtering"!=e&&"retry_policy"!=e&&"model_group_retry_policy"!=e&&"routing_groups"!=e).map(([e,s])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:r[e]?.ui_field_name||e}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r[e]?.field_description||""}),(0,t.jsx)(l.Input,{name:e,defaultValue:null==s||"null"===s?"":"object"==typeof s?JSON.stringify(s,null,2):s?.toString()||"",placeholder:"—",className:"font-mono text-sm w-full"})]})},e))})]});var n=e.i(199133);let i=({selectedStrategy:e,availableStrategies:l,routingStrategyDescriptions:r,routerFieldsMetadata:s,onStrategyChange:a})=>(0,t.jsxs)("div",{className:"space-y-2 max-w-3xl",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:s.routing_strategy?.ui_field_name||"Routing Strategy"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:s.routing_strategy?.field_description||""})]}),(0,t.jsx)("div",{className:"routing-strategy-select max-w-3xl",children:(0,t.jsx)(n.Select,{value:e,onChange:a,style:{width:"100%"},size:"large",children:l.map(e=>(0,t.jsx)(n.Select.Option,{value:e,label:e,children:(0,t.jsxs)("div",{className:"flex flex-col gap-0.5 py-1",children:[(0,t.jsx)("span",{className:"font-mono text-sm font-medium",children:e}),r[e]&&(0,t.jsx)("span",{className:"text-xs text-gray-500 font-normal",children:r[e]})]})},e))})})]});var o=e.i(790848);let c=({enabled:e,routerFieldsMetadata:l,onToggle:r})=>(0,t.jsx)("div",{className:"space-y-3 max-w-3xl",children:(0,t.jsxs)("div",{className:"flex items-start justify-between",children:[(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:l.enable_tag_filtering?.ui_field_name||"Enable Tag Filtering"}),(0,t.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:[l.enable_tag_filtering?.field_description||"",l.enable_tag_filtering?.link&&(0,t.jsxs)(t.Fragment,{children:[" ",(0,t.jsx)("a",{href:l.enable_tag_filtering.link,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"Learn more"})]})]})]}),(0,t.jsx)(o.Switch,{checked:e,onChange:r,className:"ml-4"})]})});e.s(["default",0,({value:e,onChange:l,routerFieldsMetadata:r,availableRoutingStrategies:n,routingStrategyDescriptions:o})=>(0,t.jsxs)("div",{className:"w-full space-y-8 py-2",children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Routing Settings"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure how requests are routed to deployments"})]}),n.length>0&&(0,t.jsx)(i,{selectedStrategy:e.selectedStrategy||e.routerSettings.routing_strategy||null,availableStrategies:n,routingStrategyDescriptions:o,routerFieldsMetadata:r,onStrategyChange:t=>{l({...e,selectedStrategy:t})}}),(0,t.jsx)(c,{enabled:e.enableTagFiltering,routerFieldsMetadata:r,onToggle:t=>{l({...e,enableTagFiltering:t})}})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"}),"latency-based-routing"===e.selectedStrategy&&(0,t.jsx)(s,{routingStrategyArgs:e.routerSettings.routing_strategy_args}),(0,t.jsx)(a,{routerSettings:e.routerSettings,routerFieldsMetadata:r})]})],158392);var d=e.i(361653);e.s(["AlertCircle",()=>d.default],63209)},419470,e=>{"use strict";var t=e.i(843476),l=e.i(994388),r=e.i(653496),s=e.i(107233),a=e.i(271645),n=e.i(888259),i=e.i(199133),o=e.i(592968),c=e.i(63209),d=e.i(425063),u=e.i(37727);function m({group:e,onChange:l,availableModels:r,maxFallbacks:s}){let a=r.filter(t=>t!==e.primaryModel),n=e.fallbackModels.length{let r=[...e.fallbackModels];r.includes(t)&&(r=r.filter(e=>e!==t)),l({...e,primaryModel:t,fallbackModels:r})},showSearch:!0,getPopupContainer:e=>e.parentElement||document.body,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:r.map(e=>({label:e,value:e}))}),!e.primaryModel&&(0,t.jsxs)("div",{className:"mt-2 flex items-center gap-2 text-amber-600 text-xs bg-amber-50 p-2 rounded-sm",children:[(0,t.jsx)(c.AlertCircle,{className:"w-4 h-4"}),(0,t.jsx)("span",{children:"Select a model to begin configuring fallbacks"})]})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-4 z-10",children:(0,t.jsxs)("div",{className:"bg-indigo-50 text-indigo-500 px-4 py-1 rounded-full text-xs font-bold border border-indigo-100 flex items-center gap-2 shadow-xs",children:[(0,t.jsx)(d.ArrowDown,{className:"w-4 h-4"}),"IF FAILS, TRY..."]})}),(0,t.jsxs)("div",{className:`transition-opacity duration-300 ${!e.primaryModel?"opacity-50 pointer-events-none":"opacity-100"}`,children:[(0,t.jsxs)("label",{className:"block text-sm font-semibold text-gray-700 mb-2",children:["Fallback Chain ",(0,t.jsx)("span",{className:"text-red-500",children:"*"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 font-normal ml-2",children:["(Max ",s," fallbacks at a time)"]})]}),(0,t.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 border border-gray-200",children:[(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(i.Select,{mode:"multiple",className:"w-full",size:"large",placeholder:n?"Select fallback models to add...":`Maximum ${s} fallbacks reached`,value:e.fallbackModels,onChange:t=>{let r=t.slice(0,s);l({...e,fallbackModels:r})},disabled:!e.primaryModel,getPopupContainer:e=>e.parentElement||document.body,options:a.map(e=>({label:e,value:e})),optionRender:(l,r)=>{let s=e.fallbackModels.includes(l.value),a=s?e.fallbackModels.indexOf(l.value)+1:null;return(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[s&&null!==a&&(0,t.jsx)("span",{className:"flex items-center justify-center w-5 h-5 rounded-sm bg-indigo-100 text-indigo-600 text-xs font-bold",children:a}),(0,t.jsx)("span",{children:l.label})]})},maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(o.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})}),showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase())}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1 ml-1",children:n?`Search and select multiple models. Selected models will appear below in order. (${e.fallbackModels.length}/${s} used)`:`Maximum ${s} fallbacks reached. Remove some to add more.`})]}),(0,t.jsx)("div",{className:"space-y-2 min-h-[100px]",children:0===e.fallbackModels.length?(0,t.jsxs)("div",{className:"h-32 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center text-gray-400",children:[(0,t.jsx)("span",{className:"text-sm",children:"No fallback models selected"}),(0,t.jsx)("span",{className:"text-xs mt-1",children:"Add models from the dropdown above"})]}):e.fallbackModels.map((r,s)=>(0,t.jsxs)("div",{className:"group flex items-center justify-between p-3 bg-white rounded-lg border border-gray-200 hover:border-indigo-300 hover:shadow-xs transition-all",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)("div",{className:"flex items-center justify-center w-6 h-6 rounded-sm bg-gray-100 text-gray-400 group-hover:text-indigo-500 group-hover:bg-indigo-50",children:(0,t.jsx)("span",{className:"text-xs font-bold",children:s+1})}),(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"font-medium text-gray-800",children:r})})]}),(0,t.jsx)("button",{type:"button",onClick:()=>{let t;return t=e.fallbackModels.filter((e,t)=>t!==s),void l({...e,fallbackModels:t})},className:"opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 hover:text-red-500 p-1",children:(0,t.jsx)(u.X,{className:"w-4 h-4"})})]},`${r}-${s}`))})]})]})]})}e.s(["FallbackSelectionForm",0,function({groups:e,onGroupsChange:i,availableModels:o,maxFallbacks:c=10,maxGroups:d=5}){let[u,p]=(0,a.useState)(e.length>0?e[0].id:"1");(0,a.useEffect)(()=>{e.length>0?e.some(e=>e.id===u)||p(e[0].id):p("1")},[e]);let g=()=>{if(e.length>=d)return;let t=Date.now().toString();i([...e,{id:t,primaryModel:null,fallbackModels:[]}]),p(t)},f=t=>{i(e.map(e=>e.id===t.id?t:e))},h=e.map((l,r)=>{let s=l.primaryModel?l.primaryModel:`Group ${r+1}`;return{key:l.id,label:s,closable:e.length>1,children:(0,t.jsx)(m,{group:l,onChange:f,availableModels:o,maxFallbacks:c})}});return 0===e.length?(0,t.jsxs)("div",{className:"text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300",children:[(0,t.jsx)("p",{className:"text-gray-500 mb-4",children:"No fallback groups configured"}),(0,t.jsx)(l.Button,{variant:"primary",onClick:g,icon:()=>(0,t.jsx)(s.Plus,{className:"w-4 h-4"}),children:"Create First Group"})]}):(0,t.jsx)(r.Tabs,{type:"editable-card",activeKey:u,onChange:p,onEdit:(t,l)=>{"add"===l?g():"remove"===l&&e.length>1&&(t=>{if(1===e.length)return n.default.warning("At least one group is required");let l=e.filter(e=>e.id!==t);i(l),u===t&&l.length>0&&p(l[l.length-1].id)})(t)},items:h,className:"fallback-tabs",tabBarStyle:{marginBottom:0},hideAdd:e.length>=d})}],419470)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03m16pvgn6tls.js b/litellm/proxy/_experimental/out/_next/static/chunks/03m16pvgn6tls.js new file mode 100644 index 00000000000..604bf07d6dd --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03m16pvgn6tls.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,298805,e=>{"use strict";var t=e.i(843476),s=e.i(271645),a=e.i(994388),l=e.i(304967),i=e.i(269200),r=e.i(942232),n=e.i(977572),o=e.i(427612),c=e.i(64848),d=e.i(496020),m=e.i(389083),p=e.i(599724),u=e.i(212931),h=e.i(560445),x=e.i(592968),g=e.i(981339),_=e.i(790848),j=e.i(245704),y=e.i(602869),b=e.i(808613),f=e.i(199133),v=e.i(311451),k=e.i(280898),N=e.i(91739),w=e.i(262218),C=e.i(312361),S=e.i(28651),I=e.i(888259),T=e.i(555987),A=e.i(826910),F=e.i(438957),L=e.i(983561),D=e.i(477189),M=e.i(827252),P=e.i(364769),R=e.i(135214),U=e.i(355619),O=e.i(663435),E=e.i(362024),q=e.i(770914),V=e.i(464571),B=e.i(646563),$=e.i(564897);let H={basic:{key:"basic",title:"Basic Information",defaultExpanded:!0,fields:[{name:"name",label:"Display Name",type:"text",required:!0,placeholder:"e.g., Customer Support Agent"},{name:"description",label:"Description",type:"textarea",required:!0,placeholder:"Describe what this agent does...",rows:3},{name:"url",label:"URL",type:"url",required:!1,placeholder:"http://localhost:9999/",tooltip:"Base URL where the agent is hosted (optional)"},{name:"version",label:"Version",type:"text",placeholder:"1.0.0",defaultValue:"1.0.0"},{name:"protocolVersion",label:"Protocol Version",type:"select",options:["1.0","0.3"],defaultValue:"1.0",tooltip:"The A2A protocol version LiteLLM serves to clients for this agent. LiteLLM converts the upstream agent's responses to this version, so clients always see the version you pick here regardless of the original agent's version.",helpText:"LiteLLM serves this version to clients and converts the upstream agent's responses to match it, regardless of the original agent's version."}]},skills:{key:"skills",title:"Skills",fields:[{name:"skills",label:"Skills",type:"list",defaultValue:[]}]},capabilities:{key:"capabilities",title:"Capabilities",fields:[{name:"streaming",label:"Streaming",type:"switch",defaultValue:!1},{name:"pushNotifications",label:"Push Notifications",type:"switch"},{name:"stateTransitionHistory",label:"State Transition History",type:"switch"}]},optional:{key:"optional",title:"Optional Settings",fields:[{name:"iconUrl",label:"Icon URL",type:"url",placeholder:"https://example.com/icon.png"},{name:"documentationUrl",label:"Documentation URL",type:"url",placeholder:"https://docs.example.com"},{name:"supportsAuthenticatedExtendedCard",label:"Supports Authenticated Extended Card",type:"switch"}]},litellm:{key:"litellm",title:"LiteLLM Parameters",fields:[{name:"model",label:"Model (Optional)",type:"text"},{name:"make_public",label:"Make Public",type:"switch"}]},cost:{key:"cost",title:"Cost Configuration",fields:[{name:"cost_per_query",label:"Cost Per Query ($)",type:"text",placeholder:"0.0",tooltip:"Fixed cost per query"},{name:"input_cost_per_token",label:"Input Cost Per Token ($)",type:"text",placeholder:"0.000001",tooltip:"Cost per input token"},{name:"output_cost_per_token",label:"Output Cost Per Token ($)",type:"text",placeholder:"0.000002",tooltip:"Cost per output token"}]},tracing:{key:"tracing",title:"Tracing",fields:[{name:"enable_tracing",label:"Enable Tracing",type:"switch",defaultValue:!1,tooltip:"Enable request tracing for this agent"}]}},z="Skill ID",G=!0,K="e.g., hello_world",W="Skill Name",Y=!0,J="e.g., Returns hello world",Q="Description",X=!0,Z="What this skill does",ee=2,et="Tags",es=!0,ea="Type a tag and press Enter",el="Examples",ei="Type an example and press Enter",er=(e,t)=>{let s={agent_name:e.agent_name,agent_card_params:{protocolVersion:e.protocolVersion||"1.0",name:e.name||e.agent_name,description:e.description||"",url:e.url||"",version:e.version||"1.0.0",defaultInputModes:t?.agent_card_params?.defaultInputModes||["text"],defaultOutputModes:t?.agent_card_params?.defaultOutputModes||["text"],capabilities:{streaming:!0===e.streaming,...void 0!==e.pushNotifications&&{pushNotifications:e.pushNotifications},...void 0!==e.stateTransitionHistory&&{stateTransitionHistory:e.stateTransitionHistory}},skills:e.skills||[],...e.iconUrl&&{iconUrl:e.iconUrl},...e.documentationUrl&&{documentationUrl:e.documentationUrl},...void 0!==e.supportsAuthenticatedExtendedCard&&{supportsAuthenticatedExtendedCard:e.supportsAuthenticatedExtendedCard}}},a={};if(e.model&&(a.model=e.model),void 0!==e.make_public&&(a.make_public=e.make_public),e.cost_per_query&&(a.cost_per_query=parseFloat(e.cost_per_query)),e.input_cost_per_token&&(a.input_cost_per_token=parseFloat(e.input_cost_per_token)),e.output_cost_per_token&&(a.output_cost_per_token=parseFloat(e.output_cost_per_token)),Object.keys(a).length>0&&(s.litellm_params=a),null!=e.tpm_limit&&(s.tpm_limit=e.tpm_limit),null!=e.rpm_limit&&(s.rpm_limit=e.rpm_limit),null!=e.session_tpm_limit&&(s.session_tpm_limit=e.session_tpm_limit),null!=e.session_rpm_limit&&(s.session_rpm_limit=e.session_rpm_limit),Array.isArray(e.static_headers)&&e.static_headers.length>0){let t={};e.static_headers.forEach(e=>{let s=e?.header?.trim();s&&(t[s]=e?.value??"")}),Object.keys(t).length>0&&(s.static_headers=t)}return Array.isArray(e.extra_headers)&&e.extra_headers.length>0&&(s.extra_headers=e.extra_headers),s},en=e=>{let t=e.agent_card_params?.skills?.map(e=>({...e,tags:e.tags,examples:e.examples||[]}))||[];return{agent_name:e.agent_name,name:e.agent_card_params?.name,description:e.agent_card_params?.description,url:e.agent_card_params?.url,version:e.agent_card_params?.version,protocolVersion:e.agent_card_params?.protocolVersion,streaming:e.agent_card_params?.capabilities?.streaming,pushNotifications:e.agent_card_params?.capabilities?.pushNotifications,stateTransitionHistory:e.agent_card_params?.capabilities?.stateTransitionHistory,skills:t,iconUrl:e.agent_card_params?.iconUrl,documentationUrl:e.agent_card_params?.documentationUrl,supportsAuthenticatedExtendedCard:e.agent_card_params?.supportsAuthenticatedExtendedCard,model:e.litellm_params?.model,make_public:e.litellm_params?.make_public,cost_per_query:e.litellm_params?.cost_per_query,input_cost_per_token:e.litellm_params?.input_cost_per_token,output_cost_per_token:e.litellm_params?.output_cost_per_token,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,session_tpm_limit:e.session_tpm_limit,session_rpm_limit:e.session_rpm_limit,static_headers:e.static_headers?Object.entries(e.static_headers).map(([e,t])=>({header:e,value:t})):[],extra_headers:e.extra_headers??[]}},eo=()=>(0,t.jsx)(t.Fragment,{children:H.cost.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,tooltip:e.tooltip,children:(0,t.jsx)(v.Input,{placeholder:e.placeholder,type:"number",step:"0.000001"})},e.name))}),{Panel:ec}=E.Collapse,ed=({showAgentName:e=!0,visiblePanels:s})=>{let a=e=>!s||s.includes(e);return(0,t.jsxs)(t.Fragment,{children:[e&&(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter a unique agent name"}],tooltip:"Unique identifier for the agent",children:(0,t.jsx)(v.Input,{placeholder:"e.g., customer-support-agent"})}),(0,t.jsxs)(E.Collapse,{defaultActiveKey:["basic"],style:{marginBottom:16},children:[a(H.basic.key)&&(0,t.jsx)(ec,{header:`${H.basic.title} (Required)`,children:H.basic.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,rules:e.required?[{required:!0,message:`Please enter ${e.label.toLowerCase()}`}]:void 0,tooltip:e.tooltip,extra:e.helpText,children:"textarea"===e.type?(0,t.jsx)(v.Input.TextArea,{rows:e.rows,placeholder:e.placeholder}):"select"===e.type?(0,t.jsx)(f.Select,{placeholder:e.placeholder,children:(e.options??[]).map(e=>(0,t.jsx)(f.Select.Option,{value:e,children:e},e))}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.basic.key),a(H.skills.key)&&(0,t.jsx)(ec,{header:`${H.skills.title}`,children:(0,t.jsx)(b.Form.List,{name:"skills",children:(e,{add:s,remove:a})=>(0,t.jsxs)(t.Fragment,{children:[e.map(e=>(0,t.jsxs)("div",{style:{marginBottom:16,padding:16,border:"1px solid #d9d9d9",borderRadius:4},children:[(0,t.jsx)(b.Form.Item,{...e,label:z,name:[e.name,"id"],rules:[{required:G,message:"Required"}],children:(0,t.jsx)(v.Input,{placeholder:K})}),(0,t.jsx)(b.Form.Item,{...e,label:W,name:[e.name,"name"],rules:[{required:Y,message:"Required"}],children:(0,t.jsx)(v.Input,{placeholder:J})}),(0,t.jsx)(b.Form.Item,{...e,label:Q,name:[e.name,"description"],rules:[{required:X,message:"Required"}],children:(0,t.jsx)(v.Input.TextArea,{rows:ee,placeholder:Z})}),(0,t.jsx)(b.Form.Item,{...e,label:et,name:[e.name,"tags"],rules:[{required:es,message:"Required"}],children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},tokenSeparators:[","],placeholder:ea})}),(0,t.jsx)(b.Form.Item,{...e,label:el,name:[e.name,"examples"],children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},tokenSeparators:[","],placeholder:ei})}),(0,t.jsx)(V.Button,{type:"link",danger:!0,onClick:()=>a(e.name),icon:(0,t.jsx)($.MinusCircleOutlined,{}),children:"Remove Skill"})]},e.key)),(0,t.jsx)(V.Button,{type:"dashed",onClick:()=>s(),icon:(0,t.jsx)(B.PlusOutlined,{}),style:{width:"100%"},children:"Add Skill"})]})})},H.skills.key),a(H.capabilities.key)&&(0,t.jsx)(ec,{header:H.capabilities.title,children:H.capabilities.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"checked",children:(0,t.jsx)(_.Switch,{})},e.name))},H.capabilities.key),a(H.optional.key)&&(0,t.jsx)(ec,{header:H.optional.title,children:H.optional.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"switch"===e.type?"checked":void 0,children:"switch"===e.type?(0,t.jsx)(_.Switch,{}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.optional.key),a(H.cost.key)&&(0,t.jsx)(ec,{header:H.cost.title,children:(0,t.jsx)(eo,{})},H.cost.key),a(H.litellm.key)&&(0,t.jsx)(ec,{header:H.litellm.title,children:H.litellm.fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.name,valuePropName:"switch"===e.type?"checked":void 0,children:"switch"===e.type?(0,t.jsx)(_.Switch,{}):(0,t.jsx)(v.Input,{placeholder:e.placeholder})},e.name))},H.litellm.key),a("auth_headers")&&(0,t.jsxs)(ec,{header:"Authentication Headers",children:[(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Static Headers"," ",(0,t.jsx)(x.Tooltip,{title:"Headers always sent to the backend agent, regardless of the client request. Admin-configured, static wins on conflict.",children:(0,t.jsx)(M.InfoCircleOutlined,{style:{color:"#8c8c8c"}})})]}),children:(0,t.jsx)(b.Form.List,{name:"static_headers",children:(e,{add:s,remove:a})=>(0,t.jsxs)(t.Fragment,{children:[e.map(({key:e,name:s,...l})=>(0,t.jsxs)(q.Space,{style:{display:"flex",marginBottom:8},align:"baseline",children:[(0,t.jsx)(b.Form.Item,{...l,name:[s,"header"],rules:[{required:!0,message:"Header name required"}],children:(0,t.jsx)(v.Input,{placeholder:"Header name (e.g. Authorization)",style:{width:220}})}),(0,t.jsx)(b.Form.Item,{...l,name:[s,"value"],rules:[{required:!0,message:"Value required"}],children:(0,t.jsx)(v.Input,{placeholder:"Value (e.g. Bearer token123)",style:{width:260}})}),(0,t.jsx)($.MinusCircleOutlined,{onClick:()=>a(s),style:{color:"#ff4d4f"}})]},e)),(0,t.jsx)(V.Button,{type:"dashed",onClick:()=>s(),icon:(0,t.jsx)(B.PlusOutlined,{}),style:{width:"100%"},children:"Add Static Header"})]})})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Forward Client Headers"," ",(0,t.jsx)(x.Tooltip,{title:"Header names to extract from the client's request and forward to the agent. Type a name and press Enter.",children:(0,t.jsx)(M.InfoCircleOutlined,{style:{color:"#8c8c8c"}})})]}),name:"extra_headers",children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},placeholder:"e.g. x-api-key, Authorization",tokenSeparators:[","]})})]},"auth_headers")]})]})};var em=e.i(536916),ep=e.i(21548),eu=e.i(482725),eh=e.i(898586);e.i(247167);var ex=e.i(931067);let eg={icon:function(e,t){return{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z",fill:e}},{tag:"path",attrs:{d:"M512 140c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zm193.4 225.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.3 0 19.9 5 25.9 13.3l71.2 98.8 157.2-218c6-8.4 15.7-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.4 12.7z",fill:t}},{tag:"path",attrs:{d:"M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z",fill:e}}]}},name:"check-circle",theme:"twotone"};var e_=e.i(9583),ej=s.forwardRef(function(e,t){return s.createElement(e_.default,(0,ex.default)({},e,{ref:t,icon:eg}))}),ey=e.i(596239),eb=e.i(91979),ef=e.i(928685);let ev=(e,t)=>e?.id??e?.name??`skill-${t}`,ek=["streaming"],eN=e=>e?ek.reduce((t,s)=>(s in e&&(t[s]=!!e[s]),t),{}):{},ew=(e,t)=>t?{...e,agent_card_params:{...e.agent_card_params,name:t.name??e.agent_card_params?.name,description:t.description??e.agent_card_params?.description,...Array.isArray(t.skills)&&{skills:t.skills},...t.capabilities&&{capabilities:t.capabilities},...Array.isArray(t.defaultInputModes)&&t.defaultInputModes.length>0&&{defaultInputModes:t.defaultInputModes},...Array.isArray(t.defaultOutputModes)&&t.defaultOutputModes.length>0&&{defaultOutputModes:t.defaultOutputModes},...t.provider&&{provider:t.provider},...t.iconUrl&&{iconUrl:t.iconUrl},...t.documentationUrl&&{documentationUrl:t.documentationUrl}}}:e,eC=(e,t,s)=>{let a=e=>(e??"").toString().trim();if("langgraph"===e){let e=a(t.api_base).replace(/\/+$/,""),s=a(t.assistant_id);if(!e||!s)return;let l=`?assistant_id=${encodeURIComponent(s)}`;return{url:e,discovery_mode:"langgraph_platform",params:{assistant_id:s},display_url:`${e}/.well-known/agent-card.json${l}`}}if("a2a"===e||s?.use_a2a_form_fields){let e=a(t.url).replace(/\/+$/,"");if(!e)return;return{url:e,discovery_mode:"well_known_fallback",display_url:`${e}/.well-known/agent-card.json`}}},{Text:eS,Paragraph:eI}=eh.Typography,{Panel:eT}=E.Collapse,eA=({accessToken:e,onApply:a,discoveryRequest:l,savedAgentCard:i})=>{let[r,n]=(0,s.useState)(""),[o,c]=(0,s.useState)(!1),[d,m]=(0,s.useState)(null),[p,u]=(0,s.useState)(null),g=void 0!==l,j=g?l.url:r,[b,f]=(0,s.useState)(""),[k,N]=(0,s.useState)(""),[C,S]=(0,s.useState)(new Set),[I,T]=(0,s.useState)({}),A=(0,s.useRef)(a);A.current=a;let F=(0,s.useRef)(0),L=(0,s.useRef)(null),D=(0,s.useRef)(l);D.current=l;let P=(0,s.useRef)(i);P.current=i;let R=l?.discovery_mode,U=(0,s.useMemo)(()=>JSON.stringify(l?.params??null),[l?.params]),O=(0,s.useCallback)(async()=>{if(!e){m("No access token available"),A.current(null);return}let t=j.trim();if(!t){m(g?"Fill in the agent's connection details above first":"Enter the agent's base URL first"),u(null),A.current(null);return}let s=D.current,a=++F.current;c(!0),m(null);try{var l;let i,r,n,o=await (0,y.discoverAgentCardCall)(e,t,g&&s?{discovery_mode:s.discovery_mode,params:s.params}:void 0);if(a!==F.current)return;L.current=null,u(o.agent_card),l=o.agent_card,n=(i=P.current)?((e,t)=>{let s=e.skills??[],a=t?.skills??[],l=new Set(a.map(e=>e?.id).filter(Boolean)),i=new Set(a.map(e=>e?.name).filter(Boolean)),r=new Set;s.forEach((e,t)=>{let s=ev(e,t),a=e.id&&l.has(e.id),n=e.name&&i.has(e.name);(a||n)&&r.add(s)});let n=eN(e.capabilities);if(t?.capabilities)for(let e of ek)e in t.capabilities&&(n[e]=!!t.capabilities[e]);return{editedName:t?.name??e.name??"",editedDescription:t?.description??e.description??"",selectedSkillIds:r,selectedCapabilities:n}})(l,i):(r=l.skills??[],{editedName:l.name??"",editedDescription:l.description??"",selectedSkillIds:new Set(r.map((e,t)=>ev(e,t))),selectedCapabilities:eN(l.capabilities)}),f(n.editedName),N(n.editedDescription),S(n.selectedSkillIds),T(n.selectedCapabilities)}catch(e){if(a!==F.current)return;m(e?.message?String(e.message):"Failed to discover agent card"),u(null),L.current=null,A.current(null)}finally{a===F.current&&c(!1)}},[e,j,g,R,U]);(0,s.useEffect)(()=>{if(!e)return;if(!j.trim()){u(null),m(null),L.current=null,A.current(null);return}let t=window.setTimeout(()=>{O()},400);return()=>window.clearTimeout(t)},[e,j,O]);let B=(0,s.useCallback)(()=>{if(!p)return null;let e=(p.skills??[]).filter((e,t)=>C.has(ev(e,t))),t={...p,name:b,description:k,skills:e,capabilities:{...I}};return{raw_card:p,selected_card:t,upstream_url:j.trim()}},[p,k,b,j,I,C]);(0,s.useEffect)(()=>{if(!p)return;let e=B(),t=JSON.stringify(e);L.current!==t&&(L.current=t,A.current(e))},[B,p]);let $=p?.skills?.length??0,H=C.size;return(0,t.jsxs)("div",{className:"border border-gray-200 rounded-lg p-4 bg-gray-50 mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-2",children:[(0,t.jsx)(ey.LinkOutlined,{className:"text-indigo-600"}),(0,t.jsx)(eS,{strong:!0,children:"Discover from agent URL"}),(0,t.jsx)(x.Tooltip,{title:"LiteLLM will fetch /.well-known/agent-card.json from this URL and let you pick which skills and capabilities to expose through the proxy.",children:(0,t.jsx)(M.InfoCircleOutlined,{className:"text-gray-400"})})]}),g?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(eI,{className:"text-xs text-gray-500 mb-2",children:"Using the connection details you entered above. We'll fetch:"}),(0,t.jsx)("div",{className:"bg-white border border-gray-200 rounded-sm px-3 py-2 mb-3 font-mono text-xs text-gray-700 break-all",children:l.display_url||j||(0,t.jsx)("span",{className:"text-gray-400 italic",children:"Fill in the fields above first"})}),(0,t.jsx)("div",{className:"flex justify-end",children:(0,t.jsx)(V.Button,{type:"primary",icon:p?(0,t.jsx)(eb.ReloadOutlined,{}):(0,t.jsx)(ef.SearchOutlined,{}),loading:o,onClick:O,disabled:!j.trim(),children:p?"Re-discover":"Discover"})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(eI,{className:"text-xs text-gray-500 mb-3",children:["Paste the upstream agent's base URL. We'll try ",(0,t.jsx)("code",{children:"/.well-known/agent-card.json"}),","," ",(0,t.jsx)("code",{children:"/.well-known/agent.json"}),", and ",(0,t.jsx)("code",{children:"/agent.json"})," in order."]}),(0,t.jsxs)(q.Space.Compact,{style:{width:"100%"},children:[(0,t.jsx)(v.Input,{placeholder:"https://upstream-agent.example.com",value:r,onChange:e=>n(e.target.value),onPressEnter:O,allowClear:!0,disabled:o}),(0,t.jsx)(V.Button,{type:"primary",icon:p?(0,t.jsx)(eb.ReloadOutlined,{}):(0,t.jsx)(ef.SearchOutlined,{}),loading:o,onClick:O,children:p?"Re-discover":"Discover"})]})]}),d&&(0,t.jsx)(h.Alert,{className:"mt-3",type:"error",message:"Discovery failed",description:d,showIcon:!0,closable:!0,onClose:()=>m(null)}),o&&!p&&(0,t.jsx)("div",{className:"flex items-center justify-center py-8",children:(0,t.jsx)(eu.Spin,{})}),p&&(0,t.jsxs)("div",{className:"mt-4 bg-white border border-gray-200 rounded-lg p-4",children:[(0,t.jsx)("div",{className:"flex items-center justify-between mb-3",children:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(ej,{twoToneColor:"#52c41a"}),(0,t.jsx)(eS,{strong:!0,children:"Upstream card loaded"}),p.version&&(0,t.jsxs)(w.Tag,{color:"blue",children:["v",p.version]}),p.provider?.organization&&(0,t.jsx)(w.Tag,{color:"purple",children:p.provider.organization})]})}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3 mb-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-600 block mb-1",children:"Name (shown to API clients)"}),(0,t.jsx)(v.Input,{value:b,onChange:e=>f(e.target.value),placeholder:"Agent name"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-600 block mb-1",children:"Description"}),(0,t.jsx)(v.Input.TextArea,{value:k,onChange:e=>N(e.target.value),rows:2,placeholder:"What this agent does"})]})]}),(0,t.jsxs)(E.Collapse,{defaultActiveKey:["skills","capabilities"],ghost:!0,className:"bg-transparent",children:[(0,t.jsx)(eT,{header:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(eS,{strong:!0,children:"Skills"}),(0,t.jsxs)(w.Tag,{children:[H," / ",$," selected"]})]}),children:0===$?(0,t.jsx)(ep.Empty,{image:ep.Empty.PRESENTED_IMAGE_SIMPLE,description:"Upstream card has no skills"}):(0,t.jsx)("div",{className:"space-y-2",children:(p.skills??[]).map((e,s)=>{let a=ev(e,s),l=C.has(a);return(0,t.jsxs)("label",{className:`flex items-start gap-3 p-3 border rounded cursor-pointer transition-colors ${l?"border-indigo-300 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,children:[(0,t.jsx)(em.Checkbox,{checked:l,onChange:e=>{var t;return t=e.target.checked,void S(e=>{let s=new Set(e);return t?s.add(a):s.delete(a),s})}}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-wrap",children:[(0,t.jsx)(eS,{strong:!0,children:e.name||a}),e.id&&(0,t.jsx)(w.Tag,{style:{marginLeft:0},children:e.id}),(e.tags??[]).map(e=>(0,t.jsx)(w.Tag,{color:"geekblue",children:e},e))]}),e.description&&(0,t.jsx)(eI,{className:"text-xs text-gray-500 mt-1 mb-0",ellipsis:{rows:2,expandable:!0,symbol:"more"},children:e.description})]})]},a)})})},"skills"),(0,t.jsx)(eT,{header:(0,t.jsxs)(q.Space,{children:[(0,t.jsx)(eS,{strong:!0,children:"Capabilities"}),(0,t.jsx)(x.Tooltip,{title:"Only capabilities LiteLLM can faithfully proxy today are listed. Others (push notifications, extensions) are coming soon.",children:(0,t.jsx)(M.InfoCircleOutlined,{className:"text-gray-400"})})]}),children:(0,t.jsx)("div",{className:"space-y-2",children:ek.map(e=>{let s=!!p.capabilities?.[e];return(0,t.jsxs)("div",{className:"flex items-center justify-between p-2 border border-gray-200 rounded-sm bg-white",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(eS,{strong:!0,className:"capitalize",children:e}),!s&&(0,t.jsx)(w.Tag,{className:"ml-2",color:"default",children:"not advertised upstream"})]}),(0,t.jsx)(_.Switch,{checked:!!I[e],onChange:t=>T(s=>({...s,[e]:t}))})]},e)})})},"capabilities")]})]})]})},{Panel:eF}=E.Collapse,eL=(e,t)=>{let s={...t.litellm_params_template||{}};for(let a of t.credential_fields){let t=e[a.key];t&&!1!==a.include_in_litellm_params&&(s[a.key]=t)}if(e.cost_per_query&&(s.cost_per_query=parseFloat(e.cost_per_query)),e.input_cost_per_token&&(s.input_cost_per_token=parseFloat(e.input_cost_per_token)),e.output_cost_per_token&&(s.output_cost_per_token=parseFloat(e.output_cost_per_token)),t.model_template){let a=t.model_template;for(let s of t.credential_fields){let t=`{${s.key}}`;a.includes(t)&&e[s.key]&&(a=a.replace(t,e[s.key]))}s.model=a}let a={agent_name:e.agent_name,agent_card_params:{protocolVersion:"1.0",name:e.display_name||e.agent_name,description:e.description||`${t.agent_type_display_name} agent`,url:e.api_base||"",version:"1.0.0",defaultInputModes:["text"],defaultOutputModes:["text"],capabilities:{streaming:!0},skills:[{id:"chat",name:"Chat",description:"General chat capability",tags:["chat","conversation"]}]},litellm_params:s};return null!=e.tpm_limit&&(a.tpm_limit=e.tpm_limit),null!=e.rpm_limit&&(a.rpm_limit=e.rpm_limit),null!=e.session_tpm_limit&&(a.session_tpm_limit=e.session_tpm_limit),null!=e.session_rpm_limit&&(a.session_rpm_limit=e.session_rpm_limit),a},eD=({agentTypeInfo:e})=>(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter a unique agent name"}],tooltip:"Unique identifier for the agent",children:(0,t.jsx)(v.Input,{placeholder:"e.g., my-langgraph-agent"})}),(0,t.jsx)(b.Form.Item,{label:"Description",name:"description",tooltip:"Brief description of what this agent does",children:(0,t.jsx)(v.Input.TextArea,{rows:2,placeholder:"Describe what this agent does..."})}),e.credential_fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.key,rules:e.required?[{required:!0,message:`Please enter ${e.label}`}]:void 0,tooltip:e.tooltip,initialValue:e.default_value,children:"password"===e.field_type?(0,t.jsx)(v.Input.Password,{placeholder:e.placeholder||""}):"textarea"===e.field_type?(0,t.jsx)(v.Input.TextArea,{rows:3,placeholder:e.placeholder||""}):"select"===e.field_type&&e.options?(0,t.jsx)(f.Select,{placeholder:e.placeholder||"",children:e.options.map(e=>(0,t.jsx)(f.Select.Option,{value:e,children:e},e))}):(0,t.jsx)(v.Input,{placeholder:e.placeholder||""})},e.key)),(0,t.jsx)(E.Collapse,{style:{marginBottom:16},children:(0,t.jsx)(eF,{header:H.cost.title,children:(0,t.jsx)(eo,{})},H.cost.key)})]});var eM=e.i(75921),eP=e.i(390605),eR=e.i(891547);let{Step:eU}=k.Steps,eO="custom",eE=({visible:e,onClose:l,accessToken:i,onSuccess:r,teams:n})=>{let o,c,{userId:d,userRole:m}=(0,R.default)(),[p]=b.Form.useForm(),[h,x]=(0,s.useState)(0),[g,j]=(0,s.useState)(!1),[E,q]=(0,s.useState)("a2a"),[V,B]=(0,s.useState)([]),[$,z]=(0,s.useState)(!1),[G,K]=(0,s.useState)("create_new"),[W,Y]=(0,s.useState)(""),[J,Q]=(0,s.useState)([]),[X,Z]=(0,s.useState)([]),[ee,et]=(0,s.useState)(null),[es,ea]=(0,s.useState)(!1),[el,ei]=(0,s.useState)([]),[en,eo]=(0,s.useState)(!1),[ec,em]=(0,s.useState)([]),[ep,eu]=(0,s.useState)(!1),[eh,ex]=(0,s.useState)(""),[eg,e_]=(0,s.useState)(null),[ej,ey]=(0,s.useState)(null),[eb,ef]=(0,s.useState)(!1),[ev,ek]=(0,s.useState)(!1),[eN,eS]=(0,s.useState)(null),[eI,eT]=(0,s.useState)(null),[eF,eE]=(0,s.useState)(null);(0,s.useEffect)(()=>{(async()=>{z(!0);try{let e=await (0,y.getAgentCreateMetadata)();B(e)}catch(e){console.error("Error fetching agent metadata:",e)}finally{z(!1)}})()},[]),(0,s.useEffect)(()=>{3===h&&i&&0===X.length&&(async()=>{ea(!0);try{let e=await (0,y.keyListCall)(i,null,null,null,null,null,1,100);Z(e?.keys||[])}catch(e){console.error("Error fetching keys:",e)}finally{ea(!1)}})()},[h,i]),(0,s.useEffect)(()=>{if(1!==h&&3!==h||!i||!d||!m)return;let e=!1;return eo(!0),(0,y.modelAvailableCall)(i,d,m).then(t=>{e||ei((t?.data??(Array.isArray(t)?t:[])).map(e=>e.id??e.model_name).filter(Boolean))}).catch(t=>{e||console.error("Error fetching models:",t)}).finally(()=>{e||eo(!1)}),()=>{e=!0}},[h,i,d,m]),(0,s.useEffect)(()=>{if(1!==h||!i)return;let e=!1;return eu(!0),(0,y.getAgentsList)(i).then(t=>{e||em((t?.agents??[]).map(e=>({agent_id:e.agent_id,agent_name:e.agent_name})))}).catch(t=>{e||console.error("Error fetching agents:",t)}).finally(()=>{e||eu(!1)}),()=>{e=!0}},[h,i]);let eq=V.find(e=>e.agent_type===E),eV=b.Form.useWatch([],p),eB=s.default.useMemo(()=>eC(E,eV||{},eq),[eV,eq,E]),e$=async()=>{try{if(0===h){await p.validateFields();let e=p.getFieldValue("agent_name");e&&!W&&Y(`${e}-key`)}x(e=>e+1)}catch{}},eH=async()=>{if(!i)return void I.default.error("No access token available");j(!0);try{await p.validateFields();let e={...p.getFieldsValue(!0)},t=(e=>{let t;if(E===eO)return{agent_name:e.agent_name,agent_card_params:{protocolVersion:"1.0",name:e.agent_name,description:e.description||"",url:"",version:"1.0.0",defaultInputModes:["text"],defaultOutputModes:["text"],capabilities:{streaming:!1},skills:[]}};if("a2a"===E)t=er(e);else if(eq?.use_a2a_form_fields)for(let s of(t=er(e),eq.litellm_params_template&&(t.litellm_params={...t.litellm_params,...eq.litellm_params_template}),eq.credential_fields)){let a=e[s.key];a&&!1!==s.include_in_litellm_params&&(t.litellm_params[s.key]=a)}else{if(!eq)return null;t=eL(e,eq)}return ew(t,eF?.selected_card)})(e);if(!t){I.default.error("Failed to build agent data"),j(!1);return}let s=e.allowed_mcp_servers_and_groups,a=e.mcp_tool_permissions||{},l=e.entitlement_models||[],n=e.entitlement_agents||[];(s?.servers?.length>0||s?.accessGroups?.length>0||Object.keys(a).length>0||l.length>0||n.length>0)&&(t.object_permission={},s?.servers?.length>0&&(t.object_permission.mcp_servers=s.servers),s?.accessGroups?.length>0&&(t.object_permission.mcp_access_groups=s.accessGroups),Object.keys(a).length>0&&(t.object_permission.mcp_tool_permissions=a),l.length>0&&(t.object_permission.models=l),n.length>0&&(t.object_permission.agents=n)),(eb||ev)&&(t.litellm_params||(t.litellm_params={}),eb&&(t.litellm_params.require_trace_id_on_calls_to_agent=!0),ev&&(t.litellm_params.require_trace_id_on_calls_by_agent=!0,eN&&(t.litellm_params.max_iterations=eN),eI&&(t.litellm_params.max_budget_per_session=eI)));let o=e.guardrails||[];o.length>0&&(t.litellm_params||(t.litellm_params={}),t.litellm_params.guardrails=o);let c=e.team_id||null;c&&(t.team_id=c);let d=await (0,y.createAgentCall)(i,t),m=d.agent_id,u=d.agent_name||e.agent_name||m;if(ex(u),"create_new"===G&&W){let e=await (0,y.keyCreateForAgentCall)(i,m,W,J,void 0,c);e_(e.key||null)}else if("existing_key"===G){if(!ee){I.default.error("Please select an existing key to assign"),j(!1);return}await (0,y.keyUpdateCall)(i,{key:ee,agent_id:m});let e=X.find(e=>e.token===ee);ey(e?.key_alias||ee.slice(0,12)+"…")}x(4),r()}catch(t){console.error("Error creating agent:",t);let e=t instanceof Error?t.message:String(t);I.default.error(e?`Failed to create agent: ${e}`:"Failed to create agent")}finally{j(!1)}},ez=()=>{p.resetFields(),q("a2a"),x(0),K("create_new"),Y(""),Q([]),et(null),ex(""),e_(null),ey(null),ef(!1),ek(!1),eS(null),eT(null),eE(null),l()},eG=e=>{q(e),p.resetFields(),eE(null)},eK=E===eO?null:eq?.logo_url||V.find(e=>"a2a"===e.agent_type)?.logo_url;return(0,t.jsx)(u.Modal,{title:(0,t.jsxs)("div",{className:"flex items-center space-x-3 pb-4 border-b border-gray-100",children:[eK&&h<1&&(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(eK),alt:"Agent",className:"w-6 h-6 object-contain"}),(0,t.jsx)("h2",{className:"text-xl font-semibold text-gray-900",children:"Add New Agent"})]}),open:e,onCancel:ez,footer:null,width:900,className:"top-8",styles:{body:{padding:"24px"},header:{padding:"24px 24px 0 24px",border:"none"}},children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)(k.Steps,{current:h,size:"small",className:"mb-8",children:[(0,t.jsx)(eU,{title:"Configure"}),(0,t.jsx)(eU,{title:"Entitlements"}),(0,t.jsx)(eU,{title:"Governance"}),(0,t.jsx)(eU,{title:"Agent Management"}),(0,t.jsx)(eU,{title:"Ready"})]}),(0,t.jsxs)(b.Form,{form:p,layout:"vertical",initialValues:"a2a"===E?{...(o={defaultInputModes:["text"],defaultOutputModes:["text"]},Object.values(H).forEach(e=>{e.fields.forEach(e=>{void 0!==e.defaultValue&&(o[e.name]=e.defaultValue)})}),o),allowed_mcp_servers_and_groups:{servers:[],accessGroups:[]},mcp_tool_permissions:{},entitlement_models:[],entitlement_agents:[],guardrails:[]}:{allowed_mcp_servers_and_groups:{servers:[],accessGroups:[]},mcp_tool_permissions:{},entitlement_models:[],entitlement_agents:[],guardrails:[]},className:"space-y-4",children:[0===h&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Agent Type"}),required:!0,tooltip:"Select the type of agent you want to create",children:(0,t.jsx)(f.Select,{value:E,onChange:eG,size:"large",style:{width:"100%"},optionLabelProp:"label",dropdownRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,(0,t.jsx)(C.Divider,{style:{margin:"4px 0"}}),(0,t.jsxs)("div",{className:"px-2 py-1",children:[(0,t.jsx)("div",{className:"text-xs text-gray-400 font-medium mb-1 uppercase tracking-wide px-2",children:"Not listed?"}),(0,t.jsxs)("div",{className:`flex items-center gap-3 px-2 py-2 rounded cursor-pointer transition-colors ${E===eO?"bg-amber-50":"hover:bg-amber-50"}`,onClick:()=>eG(eO),children:[(0,t.jsx)(D.AppstoreOutlined,{className:"text-amber-600 text-lg"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"font-medium text-amber-700",children:"Custom / Other"}),(0,t.jsx)(w.Tag,{color:"orange",style:{fontSize:10,padding:"0 4px"},children:"GENERIC"})]}),(0,t.jsx)("div",{className:"text-xs text-amber-600",children:"For agents that don't follow a standard protocol — just needs a virtual key"})]})]})]})]}),children:V.map(e=>(0,t.jsx)(f.Select.Option,{value:e.agent_type,label:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(e.logo_url)??"",alt:"",className:"w-4 h-4 object-contain"}),(0,t.jsx)("span",{children:e.agent_type_display_name})]}),children:(0,t.jsxs)("div",{className:"flex items-center gap-3 py-1",children:[(0,t.jsx)("img",{src:(0,T.resolveLogoSrc)(e.logo_url)??"",alt:e.agent_type_display_name,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"font-medium",children:e.agent_type_display_name}),e.description&&(0,t.jsx)("div",{className:"text-xs text-gray-500",children:e.description})]})]})},e.agent_type))})}),(0,t.jsxs)("div",{className:"mt-4",children:[E===eO?(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)(b.Form.Item,{label:"Agent Name",name:"agent_name",rules:[{required:!0,message:"Please enter an agent name"}],children:(0,t.jsx)(v.Input,{placeholder:"e.g. my-custom-agent"})}),(0,t.jsx)(b.Form.Item,{label:"Description",name:"description",children:(0,t.jsx)(v.Input.TextArea,{placeholder:"Describe what this agent does…",rows:3})})]}):"a2a"===E?(0,t.jsx)(ed,{showAgentName:!0}):eq?.use_a2a_form_fields?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(ed,{showAgentName:!0}),eq.credential_fields.length>0&&(0,t.jsxs)("div",{className:"mt-4 p-4 border border-gray-200 rounded-lg",children:[(0,t.jsxs)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:[eq.agent_type_display_name," Settings"]}),eq.credential_fields.map(e=>(0,t.jsx)(b.Form.Item,{label:e.label,name:e.key,rules:e.required?[{required:!0,message:`Please enter ${e.label}`}]:void 0,tooltip:e.tooltip,initialValue:e.default_value,children:"password"===e.field_type?(0,t.jsx)(v.Input.Password,{placeholder:e.placeholder||""}):(0,t.jsx)(v.Input,{placeholder:e.placeholder||""})},e.key))]})]}):eq?(0,t.jsx)(eD,{agentTypeInfo:eq}):null,E!==eO&&(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eA,{accessToken:i,onApply:e=>{if(eE(e),!e)return;let{selected_card:t,upstream_url:s}=e,a=(t.skills??[]).map(e=>({id:e.id??"",name:e.name??"",description:e.description??"",tags:e.tags??[],examples:e.examples??[]})),l=p.getFieldValue("agent_name")||t.name||t.provider?.organization||"",i={agent_name:l,name:t.name,description:t.description,url:s,version:t.version,protocolVersion:t.protocolVersion??"1.0",streaming:!!t.capabilities?.streaming,skills:a,iconUrl:t.iconUrl,documentationUrl:t.documentationUrl};for(let e of(eq?.credential_fields??[]).map(e=>e.key).filter(e=>/(^|_)(url|api_base|endpoint)$/i.test(e)))i[e]=s;p.setFieldsValue(i),!W&&l&&Y(`${l}-key`)},discoveryRequest:eB})})]})]}),1===h&&(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("p",{className:"text-sm text-gray-600",children:"Configure which models, agents, and MCP tools this agent is allowed to use. Leave fields empty to allow all (subject to key/team permissions)."}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Allowed Models"}),name:"entitlement_models",tooltip:"Restrict which models this agent can call. Leave empty to allow all.",children:(0,t.jsx)(f.Select,{mode:"tags",style:{width:"100%"},placeholder:en?"Loading models...":"Select models (leave empty for all)",tokenSeparators:[","],loading:en,showSearch:!0,options:el.map(e=>({label:(0,U.getModelDisplayName)(e),value:e}))})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Allowed Agents (Sub-Agents)"}),name:"entitlement_agents",tooltip:"Restrict which other agents this agent can invoke as sub-agents. Leave empty to allow all.",children:(0,t.jsx)(f.Select,{mode:"multiple",style:{width:"100%"},placeholder:ep?"Loading agents...":"Select agents (leave empty for all)",loading:ep,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:ec.map(e=>({label:e.agent_name,value:e.agent_id}))})}),(0,t.jsx)(C.Divider,{className:"my-2"}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(M.InfoCircleOutlined,{title:"Select which MCP servers or access groups this agent can access",style:{marginLeft:"4px"}})]}),name:"allowed_mcp_servers_and_groups",initialValue:{servers:[],accessGroups:[]},children:(0,t.jsx)(eM.default,{onChange:e=>p.setFieldValue("allowed_mcp_servers_and_groups",e),value:p.getFieldValue("allowed_mcp_servers_and_groups")||{servers:[],accessGroups:[]},accessToken:i??"",placeholder:"Select MCP servers or access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eP.default,{accessToken:i??"",selectedServers:p.getFieldValue("allowed_mcp_servers_and_groups")?.servers??[],toolPermissions:p.getFieldValue("mcp_tool_permissions")??{},onChange:e=>p.setFieldsValue({mcp_tool_permissions:e})})})})]}),2===h&&(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Tracing"}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Require x-litellm-trace-id on calls TO this agent"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Only accept this agent being invoked with a trace-id (e.g. when used as a sub-agent)."})]}),(0,t.jsx)(_.Switch,{checked:eb,onChange:ef})]}),(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Require x-litellm-trace-id on calls BY this agent"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Requires LLM/MCP calls made by this agent to include x-litellm-trace-id for session tracking."})]}),(0,t.jsx)(_.Switch,{checked:ev,onChange:e=>{ek(e),e||(eS(null),eT(null))}})]})]})]}),(0,t.jsx)(C.Divider,{className:"my-0"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Budgets & Rate Limits"}),(0,t.jsxs)("div",{className:"space-y-4",children:[!ev&&(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg text-sm text-yellow-800",children:'Enable "Require x-litellm-trace-id on calls BY this agent" in Tracing to configure budgets and rate limits.'}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700",children:"Session Budgets"}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Max Iterations"}),(0,t.jsx)(S.InputNumber,{className:"w-full",min:1,placeholder:"e.g. 25",disabled:!ev,value:eN,onChange:e=>eS(e)}),(0,t.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"Hard cap on LLM calls per session"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Max Budget Per Session ($)"}),(0,t.jsx)(S.InputNumber,{className:"w-full",min:.01,step:.5,placeholder:"e.g. 5.00",disabled:!ev,value:eI,onChange:e=>eT(e)}),(0,t.jsx)("p",{className:"text-xs text-gray-400 mt-1",children:"Max spend per trace before returning 429"})]})]}),(0,t.jsx)(C.Divider,{className:"my-2"}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700",children:"Agent Rate Limits"}),(0,t.jsx)("p",{className:"text-xs text-gray-500",children:"Global rate limits applied across all callers of this agent."}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 100000",disabled:!ev})}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 100",disabled:!ev})})]}),(0,t.jsx)("div",{className:"text-sm font-medium text-gray-700 mt-4",children:"Per-Session Rate Limits"}),(0,t.jsx)("p",{className:"text-xs text-gray-500",children:"Rate limits per session (x-litellm-trace-id). Each session gets its own counters."}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"Session TPM Limit",name:"session_tpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 10000",disabled:!ev})}),(0,t.jsx)(b.Form.Item,{label:"Session RPM Limit",name:"session_rpm_limit",className:"mb-0",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"e.g. 20",disabled:!ev})})]})]})]}),(0,t.jsx)(C.Divider,{className:"my-0"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-3",children:"Guardrails"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mb-3",children:"Apply guardrails to this agent. Selected guardrails will run on all calls made by this agent."}),(0,t.jsx)(b.Form.Item,{name:"guardrails",initialValue:[],children:(0,t.jsx)(eR.default,{accessToken:i??"",value:p.getFieldValue("guardrails")??[],onChange:e=>p.setFieldsValue({guardrails:e})})})]})]}),3===h&&(c=p.getFieldValue("agent_name")||"your-agent",(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"flex justify-center mb-6",children:(0,t.jsx)(w.Tag,{icon:(0,t.jsx)(L.RobotOutlined,{}),color:"purple",className:"px-3 py-1 text-sm",children:c})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Assign to Team"}),name:"team_id",tooltip:"Optionally assign this agent to a team. The agent and its key will belong to the selected team.",children:(0,t.jsx)(O.default,{})}),(0,t.jsx)(C.Divider,{className:"my-4"}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsx)("div",{className:`p-4 border-2 rounded-lg cursor-pointer transition-colors ${"create_new"===G?"border-indigo-600 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,onClick:()=>K("create_new"),children:(0,t.jsxs)("div",{className:"flex items-start justify-between",children:[(0,t.jsxs)("div",{className:"flex items-start gap-3 flex-1",children:[(0,t.jsx)(N.Radio,{value:"create_new",checked:"create_new"===G,onChange:()=>K("create_new")}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-indigo-600"}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:"Create a new key for this agent"})]}),(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"A dedicated key scoped to this agent."}),"create_new"===G&&(0,t.jsx)("div",{className:"mt-3 space-y-3",onClick:e=>e.stopPropagation(),children:(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-sm text-gray-600 block mb-1",children:"Key Name"}),(0,t.jsx)(v.Input,{value:W,onChange:e=>Y(e.target.value),placeholder:"e.g. my-agent-key"})]})})]})]}),(0,t.jsx)(w.Tag,{color:"green",children:"Recommended"})]})}),(0,t.jsx)("div",{className:`p-4 border-2 rounded-lg cursor-pointer transition-colors ${"existing_key"===G?"border-indigo-600 bg-indigo-50":"border-gray-200 bg-white hover:border-gray-300"}`,onClick:()=>K("existing_key"),children:(0,t.jsxs)("div",{className:"flex items-start gap-3",children:[(0,t.jsx)(N.Radio,{value:"existing_key",checked:"existing_key"===G,onChange:()=>K("existing_key")}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-gray-500"}),(0,t.jsx)("span",{className:"font-medium text-gray-900",children:"Assign an existing key"})]}),(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-1",children:"Re-assign a key you already have to this agent."}),"existing_key"===G&&(0,t.jsx)("div",{className:"mt-3",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(f.Select,{showSearch:!0,style:{width:"100%"},placeholder:"Search by key name…",loading:es,value:ee,onChange:e=>et(e),filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:X.map(e=>({label:e.key_alias||e.token?.slice(0,12)+"…",value:e.token}))})})]})]})})]}),(0,t.jsx)("div",{className:"text-center mt-4",children:(0,t.jsx)("button",{type:"button",className:"text-sm text-gray-500 underline hover:text-gray-700",onClick:()=>K("skip"),children:"Skip for now — I'll assign a key later"})})]})),4===h&&(0,t.jsxs)("div",{className:"text-center py-6",children:[(0,t.jsx)(A.CheckCircleFilled,{className:"text-5xl text-green-500 mb-4",style:{fontSize:48}}),(0,t.jsx)("h3",{className:"text-xl font-semibold text-gray-900 mb-2",children:"Agent Created!"}),(0,t.jsx)("div",{className:"flex justify-center mb-4",children:(0,t.jsx)(w.Tag,{icon:(0,t.jsx)(L.RobotOutlined,{}),color:"purple",className:"px-3 py-1 text-sm",children:eh})}),eg&&(0,t.jsx)("div",{className:"mt-4 text-left max-w-md mx-auto",children:(0,t.jsx)(P.default,{apiKey:eg})}),ej&&(0,t.jsxs)("p",{className:"text-sm text-gray-600 mt-2",children:["Key ",(0,t.jsx)("span",{className:"font-medium",children:ej})," has been assigned to this agent."]}),!eg&&!ej&&"skip"===G&&(0,t.jsx)("p",{className:"text-sm text-gray-500 mt-2",children:"No key assigned. You can create one from the Virtual Keys page."})]})]}),(0,t.jsxs)("div",{className:"flex items-center justify-between pt-6 border-t border-gray-100 mt-6",children:[(0,t.jsx)("div",{children:h>0&&h<4&&(0,t.jsx)("button",{type:"button",onClick:()=>{x(e=>Math.max(0,e-1))},className:"text-sm text-gray-600 border border-gray-300 rounded-sm px-4 py-2 hover:bg-gray-50",children:"← Back"})}),(0,t.jsxs)("div",{className:"flex gap-3",children:[h<4&&(0,t.jsx)(a.Button,{variant:"secondary",onClick:ez,children:"Cancel"}),0===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),1===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),2===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:e$,children:"Next →"}),3===h&&(0,t.jsx)(a.Button,{variant:"primary",loading:g,onClick:eH,children:g?"Creating...":"Create Agent →"}),4===h&&(0,t.jsx)(a.Button,{variant:"primary",onClick:ez,children:"Done"})]})]})]})})};var eq=e.i(708347),eV=e.i(629569),eB=e.i(197647),e$=e.i(653824),eH=e.i(881073),ez=e.i(404206),eG=e.i(723731),eK=e.i(869216),eW=e.i(530212),eY=e.i(207082),eJ=e.i(20147);let{Title:eQ,Text:eX}=eh.Typography,eZ=({keys:e,isLoading:s,onKeyClick:a})=>(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eQ,{level:4,children:"Virtual Keys"}),s?(0,t.jsx)(eX,{className:"mt-2 block",children:"Loading keys..."}):0===e.length?(0,t.jsx)(eX,{className:"mt-2 block text-gray-500",children:"No virtual key assigned to this agent."}):(0,t.jsx)("div",{className:"mt-3 flex flex-col gap-2",children:e.map(e=>(0,t.jsxs)("div",{className:"flex items-center gap-3 border border-gray-100 rounded-sm px-3 py-2",children:[(0,t.jsx)(F.KeyOutlined,{className:"text-gray-400"}),(0,t.jsx)("span",{className:"font-medium",children:e.key_alias||"Unnamed key"}),e.key_name&&(0,t.jsx)("span",{className:"font-mono text-xs text-gray-500",children:e.key_name}),(0,t.jsx)(x.Tooltip,{title:e.token,children:(0,t.jsxs)(V.Button,{size:"small",type:"link",className:"font-mono text-blue-500 ml-auto",onClick:()=>a(e),children:[e.token?.slice(0,12),"..."]})})]},e.token))})]}),e0=({agent:e})=>{let s=e.litellm_params;return s?.cost_per_query===void 0&&s?.input_cost_per_token===void 0&&s?.output_cost_per_token===void 0?null:(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eV.Title,{children:"Cost Configuration"}),(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:[void 0!==s.cost_per_query&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Cost Per Query",children:["$",s.cost_per_query]}),void 0!==s.input_cost_per_token&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Input Cost Per Token",children:["$",s.input_cost_per_token]}),void 0!==s.output_cost_per_token&&(0,t.jsxs)(eK.Descriptions.Item,{label:"Output Cost Per Token",children:["$",s.output_cost_per_token]})]})]})},e1=e=>{let t=e.litellm_params?.model||"",s=e.litellm_params?.custom_llm_provider;return"langflow"===s?"langflow":"langgraph"===s?"langgraph":"azure_ai"===s?"azure_ai_foundry":"bedrock"===s?"bedrock_agentcore":t.startsWith("langflow/")?"langflow":t.startsWith("langgraph/")?"langgraph":t.startsWith("azure_ai/agents/")?"azure_ai_foundry":t.startsWith("bedrock/agentcore/")?"bedrock_agentcore":"a2a"},e2=(e,t)=>{let s={agent_name:e.agent_name,description:e.agent_card_params?.description||""};for(let a of t.credential_fields)if(!1!==a.include_in_litellm_params)s[a.key]=e.litellm_params?.[a.key]||a.default_value||"";else if(t.model_template&&e.litellm_params?.model){let l=e.litellm_params.model,i=t.model_template.split("/"),r=l.split("/");i.forEach((e,t)=>{e===`{${a.key}}`&&r[t]&&(s[a.key]=r[t])})}return s.cost_per_query=e.litellm_params?.cost_per_query,s.input_cost_per_token=e.litellm_params?.input_cost_per_token,s.output_cost_per_token=e.litellm_params?.output_cost_per_token,s},e4=({agentId:e,onClose:i,accessToken:r,isAdmin:n})=>{let[o,c]=(0,s.useState)(null),[d,m]=(0,s.useState)(null),{data:u,isLoading:h,refetch:x}=(0,eY.useKeys)(1,100,{agentID:e}),g=u?.keys??[],[_,j]=(0,s.useState)(!0),[f,k]=(0,s.useState)(!1),[N,w]=(0,s.useState)(!1),[T]=b.Form.useForm(),[A,F]=(0,s.useState)([]),[L,D]=(0,s.useState)("a2a"),[M,P]=(0,s.useState)(null);(0,s.useEffect)(()=>{(async()=>{try{let e=await (0,y.getAgentCreateMetadata)();F(e)}catch(e){console.error("Error fetching agent metadata:",e)}})()},[]),(0,s.useEffect)(()=>{R()},[e,r]);let R=async()=>{if(r){j(!0);try{let t=await (0,y.getAgentInfo)(r,e);c(t);let s=e1(t);if(D(s),"a2a"===s)T.setFieldsValue(en(t));else{let e=A.find(e=>e.agent_type===s);e?T.setFieldsValue(e2(t,e)):T.setFieldsValue(en(t))}}catch(e){console.error("Error fetching agent info:",e),I.default.error("Failed to load agent information")}finally{j(!1)}}};(0,s.useEffect)(()=>{if(o&&A.length>0){let e=e1(o);if("a2a"!==e){let t=A.find(t=>t.agent_type===e);t&&T.setFieldsValue(e2(o,t))}}},[A,o]);let U=A.find(e=>e.agent_type===L),O=b.Form.useWatch([],T),E=(0,s.useMemo)(()=>eC(L,O||{},U),[O,U,L]),q=async t=>{if(r&&o){w(!0);try{let s;"a2a"===L?s=er(t,o):U?(s=eL(t,U)).agent_name=t.agent_name:s=er(t,o),M&&(s=ew(s,M.selected_card)),await (0,y.patchAgentCall)(r,e,s),I.default.success("Agent updated successfully"),k(!1),R()}catch(e){console.error("Error updating agent:",e),I.default.error("Failed to update agent")}finally{w(!1)}}};if(_)return(0,t.jsx)("div",{className:"p-4",children:(0,t.jsx)("div",{className:"flex justify-center items-center h-64",children:(0,t.jsx)(eu.Spin,{size:"large"})})});if(!o)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)("div",{className:"text-center",children:"Agent not found"}),(0,t.jsx)(a.Button,{onClick:i,className:"mt-4",children:"Back to Agents List"})]});let B=e=>e?new Date(e).toLocaleString():"-";return d?(0,t.jsx)(eJ.default,{keyId:d.token,keyData:d,onClose:()=>m(null),onDelete:()=>{m(null),x()},teams:null,backButtonText:"Back to Agent"}):(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Button,{icon:eW.ArrowLeftIcon,variant:"light",onClick:i,className:"mb-4",children:"Back to Agents"}),(0,t.jsx)(eV.Title,{children:o.agent_name||"Unnamed Agent"}),(0,t.jsx)(p.Text,{className:"text-gray-500 font-mono",children:o.agent_id})]}),(0,t.jsxs)(e$.TabGroup,{children:[(0,t.jsxs)(eH.TabList,{className:"mb-4",children:[(0,t.jsx)(eB.Tab,{children:"Overview"},"overview"),n?(0,t.jsx)(eB.Tab,{children:"Settings"},"settings"):(0,t.jsx)(t.Fragment,{})]}),(0,t.jsxs)(eG.TabPanels,{children:[(0,t.jsxs)(ez.TabPanel,{children:[(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,children:[(0,t.jsx)(eK.Descriptions.Item,{label:"Agent ID",children:o.agent_id}),(0,t.jsx)(eK.Descriptions.Item,{label:"Agent Name",children:o.agent_name}),(0,t.jsx)(eK.Descriptions.Item,{label:"Display Name",children:o.agent_card_params?.name||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Description",children:o.agent_card_params?.description||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"URL",children:o.agent_card_params?.url||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Version",children:o.agent_card_params?.version||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Protocol Version",children:o.agent_card_params?.protocolVersion||"-"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Streaming",children:o.agent_card_params?.capabilities?.streaming?"Yes":"No"}),o.agent_card_params?.capabilities?.pushNotifications&&(0,t.jsx)(eK.Descriptions.Item,{label:"Push Notifications",children:"Yes"}),o.agent_card_params?.capabilities?.stateTransitionHistory&&(0,t.jsx)(eK.Descriptions.Item,{label:"State Transition History",children:"Yes"}),(0,t.jsxs)(eK.Descriptions.Item,{label:"Skills",children:[o.agent_card_params?.skills?.length||0," configured"]}),o.litellm_params?.model&&(0,t.jsx)(eK.Descriptions.Item,{label:"Model",children:o.litellm_params.model}),o.litellm_params?.make_public!==void 0&&(0,t.jsx)(eK.Descriptions.Item,{label:"Make Public",children:o.litellm_params.make_public?"Yes":"No"}),o.agent_card_params?.iconUrl&&(0,t.jsx)(eK.Descriptions.Item,{label:"Icon URL",children:o.agent_card_params.iconUrl}),o.agent_card_params?.documentationUrl&&(0,t.jsx)(eK.Descriptions.Item,{label:"Documentation URL",children:o.agent_card_params.documentationUrl}),(0,t.jsx)(eK.Descriptions.Item,{label:"TPM Limit",children:o.tpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"RPM Limit",children:o.rpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Session TPM Limit",children:o.session_tpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Session RPM Limit",children:o.session_rpm_limit??"Unlimited"}),(0,t.jsx)(eK.Descriptions.Item,{label:"Created At",children:B(o.created_at)}),(0,t.jsx)(eK.Descriptions.Item,{label:"Updated At",children:B(o.updated_at)})]}),(0,t.jsx)(eZ,{keys:g,isLoading:h,onKeyClick:m}),o.object_permission&&(o.object_permission.mcp_servers?.length||o.object_permission.mcp_access_groups?.length||o.object_permission.mcp_tool_permissions&&Object.keys(o.object_permission.mcp_tool_permissions).length>0)&&(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eV.Title,{children:"MCP Tool Permissions"}),(0,t.jsxs)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:[o.object_permission.mcp_servers&&o.object_permission.mcp_servers.length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"MCP Servers",children:o.object_permission.mcp_servers.join(", ")}),o.object_permission.mcp_access_groups&&o.object_permission.mcp_access_groups.length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"MCP Access Groups",children:o.object_permission.mcp_access_groups.join(", ")}),o.object_permission.mcp_tool_permissions&&Object.keys(o.object_permission.mcp_tool_permissions).length>0&&(0,t.jsx)(eK.Descriptions.Item,{label:"Tool permissions per server",children:(0,t.jsx)("div",{className:"space-y-1",children:Object.entries(o.object_permission.mcp_tool_permissions).map(([e,s])=>(0,t.jsxs)("div",{children:[(0,t.jsxs)("span",{className:"font-medium",children:[e,":"]})," ",Array.isArray(s)?s.join(", "):String(s)]},e))})})]})]}),(0,t.jsx)(e0,{agent:o}),o.agent_card_params?.skills&&o.agent_card_params.skills.length>0&&(0,t.jsxs)("div",{style:{marginTop:24},children:[(0,t.jsx)(eV.Title,{children:"Skills"}),(0,t.jsx)(eK.Descriptions,{bordered:!0,column:1,style:{marginTop:16},children:o.agent_card_params.skills.map((e,s)=>(0,t.jsx)(eK.Descriptions.Item,{label:e.name||`Skill ${s+1}`,children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"ID:"})," ",e.id]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Description:"})," ",e.description]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Tags:"})," ",Array.isArray(e.tags)?e.tags.join(", "):e.tags]}),e.examples&&e.examples.length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)("strong",{children:"Examples:"})," ",Array.isArray(e.examples)?e.examples.join(", "):e.examples]})]})},s))})]})]}),n&&(0,t.jsx)(ez.TabPanel,{children:(0,t.jsxs)(l.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(eV.Title,{children:"Agent Settings"}),!f&&(0,t.jsx)(a.Button,{onClick:()=>{P(null),k(!0)},children:"Edit Settings"})]}),f?(0,t.jsxs)(b.Form,{form:T,layout:"vertical",onFinish:q,children:[(0,t.jsx)(b.Form.Item,{label:"Agent ID",children:(0,t.jsx)(v.Input,{value:o.agent_id,disabled:!0})}),"a2a"===L?(0,t.jsx)(ed,{showAgentName:!0}):U?(0,t.jsx)(eD,{agentTypeInfo:U}):(0,t.jsx)(ed,{showAgentName:!0}),E&&(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(eA,{accessToken:r,onApply:e=>{if(P(e),!e)return;let{selected_card:t}=e,s=(t.skills??[]).map(e=>({id:e.id??"",name:e.name??"",description:e.description??"",tags:e.tags??[],examples:e.examples??[]})),a={name:t.name,description:t.description,url:e.upstream_url,streaming:!!t.capabilities?.streaming,skills:s,iconUrl:t.iconUrl,documentationUrl:t.documentationUrl};for(let t of(U?.credential_fields??[]).map(e=>e.key).filter(e=>/(^|_)(url|api_base|endpoint)$/i.test(e)))a[t]=e.upstream_url;T.setFieldsValue(a)},discoveryRequest:E,savedAgentCard:o.agent_card_params??null})}),(0,t.jsx)(C.Divider,{}),(0,t.jsx)(eV.Title,{className:"mb-4",children:"Rate Limits"}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})})]}),(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)(b.Form.Item,{label:"Session TPM Limit",name:"session_tpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})}),(0,t.jsx)(b.Form.Item,{label:"Session RPM Limit",name:"session_rpm_limit",children:(0,t.jsx)(S.InputNumber,{className:"w-full",min:0,placeholder:"Unlimited"})})]}),(0,t.jsxs)("div",{className:"flex justify-end gap-2 mt-6",children:[(0,t.jsx)(V.Button,{onClick:()=>{P(null),k(!1),R()},children:"Cancel"}),(0,t.jsx)(a.Button,{loading:N,children:"Save Changes"})]})]}):(0,t.jsx)(p.Text,{children:'Click "Edit Settings" to modify agent configuration.'})]})})]})]})]})};var e3=e.i(727749);e.i(622826);var e6=e.i(200208),e5=e.i(399536),e7=e.i(964471),e9=e.i(112179),e8=e.i(902555);let te=({accessToken:e,userRole:b,teams:f})=>{let[v,k]=(0,s.useState)([]),[N,w]=(0,s.useState)(!1),[C,S]=(0,s.useState)(!1),[I,T]=(0,s.useState)(!1),[A,F]=(0,s.useState)(null),[L,D]=(0,s.useState)(null),[M,P]=(0,s.useState)(!1),R=!!b&&(0,eq.isAdminRole)(b),U=async t=>{if(e){S(!0);try{let s=await (0,y.getAgentsList)(e,t??M);k(s.agents||[])}catch(e){console.error("Error fetching agents:",e)}finally{S(!1)}}};(0,s.useEffect)(()=>{U()},[e]);let O=async()=>{if(A&&e){T(!0);try{await (0,y.deleteAgentCall)(e,A.id),e3.default.success(`Agent "${A.name}" deleted successfully`),U()}catch(e){console.error("Error deleting agent:",e),e3.default.fromBackend("Failed to delete agent")}finally{T(!1),F(null)}}},E=[...v].sort((e,t)=>{let s=e.created_at?new Date(e.created_at).getTime():0;return(t.created_at?new Date(t.created_at).getTime():0)-s}),q=R?7:6;return(0,t.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[(0,t.jsxs)("div",{className:"flex flex-col gap-2 mb-4",children:[(0,t.jsx)("h1",{className:"text-2xl font-bold",children:"Agents"}),(0,t.jsx)("p",{className:"text-sm text-gray-600",children:"List of A2A-spec agents that are available to be used in your organization. Go to AI Hub, to make agents public."}),(0,t.jsx)(h.Alert,{message:"Why do agents need keys?",description:"Keys scope access to an agent and allow it to call MCP tools. Assign a key when creating an agent or from the Virtual Keys page.",type:"info",showIcon:!0,className:"mb-3"}),(0,t.jsxs)("div",{className:"mt-2 flex items-center gap-4",children:[R&&(0,t.jsx)(a.Button,{onClick:()=>{L&&D(null),w(!0)},disabled:!e,children:"+ Add New Agent"}),(0,t.jsx)(x.Tooltip,{title:"When enabled, only agents with reachable URLs are shown",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(j.CheckCircleOutlined,{className:M?"text-green-500":"text-gray-400"}),(0,t.jsx)("span",{className:"text-sm text-gray-600",children:"Health Check"}),(0,t.jsx)(_.Switch,{size:"small",checked:M,onChange:e=>{P(e),U(e)},loading:C&&M})]})})]})]}),L?(0,t.jsx)(e4,{agentId:L,onClose:()=>D(null),accessToken:e,isAdmin:R}):(0,t.jsx)(l.Card,{children:C?(0,t.jsx)(g.Skeleton,{active:!0,paragraph:{rows:3}}):(0,t.jsxs)(i.Table,{children:[(0,t.jsx)(o.TableHead,{children:(0,t.jsxs)(d.TableRow,{children:[(0,t.jsx)(c.TableHeaderCell,{children:"Agent Name"}),(0,t.jsx)(c.TableHeaderCell,{children:"Agent ID"}),(0,t.jsx)(c.TableHeaderCell,{children:"Spend (USD)"}),(0,t.jsx)(c.TableHeaderCell,{children:"Model"}),(0,t.jsx)(c.TableHeaderCell,{children:"Created"}),(0,t.jsx)(c.TableHeaderCell,{children:"Status"}),R&&(0,t.jsx)(c.TableHeaderCell,{children:"Actions"})]})}),(0,t.jsx)(r.TableBody,{children:0===E.length?(0,t.jsx)(d.TableRow,{children:(0,t.jsx)(n.TableCell,{colSpan:q,children:(0,t.jsx)(p.Text,{className:"text-center",children:'No agents found. Click "+ Add New Agent" to create one.'})})}):E.map(e=>(0,t.jsxs)(d.TableRow,{children:[(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(p.Text,{children:e.agent_name})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e5.IdCell,{value:e.agent_id,onClick:e=>D(e)})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e7.MoneyCell,{value:e.spend,decimals:4})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(m.Badge,{size:"xs",color:"blue",children:e.litellm_params?.model||"N/A"})}),(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e6.DateCell,{value:e.created_at,precision:"date"})}),(0,t.jsx)(n.TableCell,{children:(e.keys?.length??0)>0?(0,t.jsx)(e9.StatusBadge,{tone:"success",label:"Active"}):(0,t.jsx)(e9.StatusBadge,{tone:"warning",label:"Needs Setup"})}),R&&(0,t.jsx)(n.TableCell,{children:(0,t.jsx)(e8.default,{variant:"Delete",onClick:()=>{F({id:e.agent_id,name:e.agent_name})}})})]},e.agent_id))})]})}),(0,t.jsx)(eE,{visible:N,onClose:()=>{w(!1)},accessToken:e,onSuccess:()=>{U()},teams:f}),A&&(0,t.jsxs)(u.Modal,{title:"Delete Agent",open:null!==A,onOk:O,onCancel:()=>{F(null)},confirmLoading:I,okText:"Delete",okButtonProps:{danger:!0},children:[(0,t.jsxs)("p",{children:["Are you sure you want to delete agent: ",A.name,"?"]}),(0,t.jsx)("p",{children:"This action cannot be undone."})]})]})};var tt=e.i(785242);e.s(["default",0,function(){let{accessToken:e,userRole:s}=(0,R.default)(),{data:a}=(0,tt.useTeams)();return(0,t.jsx)(te,{accessToken:e,userRole:s,teams:a??null})}],298805)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03rw9i0cxdgdj.js b/litellm/proxy/_experimental/out/_next/static/chunks/03rw9i0cxdgdj.js new file mode 100644 index 00000000000..bc5c4dfbdbd --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03rw9i0cxdgdj.js @@ -0,0 +1,17 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,270377,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var l=e.i(9583),n=a.forwardRef(function(e,n){return a.createElement(l.default,(0,t.default)({},e,{ref:n,icon:r}))});e.s(["ExclamationCircleOutlined",0,n],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),r=e.i(529681),l=e.i(242064),n=e.i(517455),i=e.i(185793),o=e.i(721369),s=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};let d=e=>{var{prefixCls:r,className:n,hoverable:i=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(l.ConfigContext),c=d("card",r),u=(0,a.default)(`${c}-grid`,n,{[`${c}-grid-hoverable`]:i});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var c=e.i(915654),u=e.i(183293),g=e.i(246422),m=e.i(838378);let b=(0,g.genStyleHooks)("Card",e=>{let t=(0,m.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:a,cardHeadPadding:r,colorBorderSecondary:l,boxShadowTertiary:n,bodyPadding:i,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:n},[`${t}-head`]:(e=>{let{antCls:t,componentCls:a,headerHeight:r,headerPadding:l,tabsMarginBottom:n}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:r,marginBottom:-1,padding:`0 ${(0,c.unit)(l)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${a}-typography, + > ${a}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:n,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:i,borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:a,cardShadow:r,lineWidth:l}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,c.unit)(l)} 0 0 0 ${a}, + 0 ${(0,c.unit)(l)} 0 0 ${a}, + ${(0,c.unit)(l)} ${(0,c.unit)(l)} 0 0 ${a}, + ${(0,c.unit)(l)} 0 0 0 ${a} inset, + 0 ${(0,c.unit)(l)} 0 0 ${a} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:r}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:a,actionsLiMargin:r,cardActionsIconSize:l,colorBorderSecondary:n,actionsBg:i}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:i,borderTop:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${n}`,display:"flex",borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:r,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${a}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,c.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${a}`]:{fontSize:l,lineHeight:(0,c.unit)(e.calc(l).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${n}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,c.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${l}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:a}},[`${t}-contain-grid`]:{borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:r}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:a,headerPadding:r,bodyPadding:l}=e;return{[`${t}-head`]:{padding:`0 ${(0,c.unit)(r)}`,background:a,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,c.unit)(e.padding)} ${(0,c.unit)(l)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:a,headerPaddingSM:r,headerHeightSM:l,headerFontSizeSM:n}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:l,padding:`0 ${(0,c.unit)(r)}`,fontSize:n,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:a}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,a;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(a=e.headerPadding)?a:e.paddingLG}});var p=e.i(792812),f=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};let h=e=>{let{actionClasses:a,actions:r=[],actionStyle:l}=e;return t.createElement("ul",{className:a,style:l},r.map((e,a)=>{let l=`action-${a}`;return t.createElement("li",{style:{width:`${100/r.length}%`},key:l},t.createElement("span",null,e))}))},$=t.forwardRef((e,s)=>{let c,{prefixCls:u,className:g,rootClassName:m,style:$,extra:y,headStyle:x={},bodyStyle:v={},title:j,loading:O,bordered:w,variant:C,size:S,type:k,cover:N,actions:E,tabList:T,children:B,activeTabKey:z,defaultActiveTabKey:M,tabBarExtraContent:R,hoverable:P,tabProps:L={},classNames:H,styles:I}=e,F=f(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:A,direction:W,card:D}=t.useContext(l.ConfigContext),[G]=(0,p.default)("card",C,w),q=e=>{var t;return(0,a.default)(null==(t=null==D?void 0:D.classNames)?void 0:t[e],null==H?void 0:H[e])},X=e=>{var t;return Object.assign(Object.assign({},null==(t=null==D?void 0:D.styles)?void 0:t[e]),null==I?void 0:I[e])},_=t.useMemo(()=>{let e=!1;return t.Children.forEach(B,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[B]),K=A("card",u),[Y,U,J]=b(K),Q=t.createElement(i.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},B),V=void 0!==z,Z=Object.assign(Object.assign({},L),{[V?"activeKey":"defaultActiveKey"]:V?z:M,tabBarExtraContent:R}),ee=(0,n.default)(S),et=ee&&"default"!==ee?ee:"large",ea=T?t.createElement(o.default,Object.assign({size:et},Z,{className:`${K}-head-tabs`,onChange:t=>{var a;null==(a=e.onTabChange)||a.call(e,t)},items:T.map(e=>{var{tab:t}=e;return Object.assign({label:t},f(e,["tab"]))})})):null;if(j||y||ea){let e=(0,a.default)(`${K}-head`,q("header")),r=(0,a.default)(`${K}-head-title`,q("title")),l=(0,a.default)(`${K}-extra`,q("extra")),n=Object.assign(Object.assign({},x),X("header"));c=t.createElement("div",{className:e,style:n},t.createElement("div",{className:`${K}-head-wrapper`},j&&t.createElement("div",{className:r,style:X("title")},j),y&&t.createElement("div",{className:l,style:X("extra")},y)),ea)}let er=(0,a.default)(`${K}-cover`,q("cover")),el=N?t.createElement("div",{className:er,style:X("cover")},N):null,en=(0,a.default)(`${K}-body`,q("body")),ei=Object.assign(Object.assign({},v),X("body")),eo=t.createElement("div",{className:en,style:ei},O?Q:B),es=(0,a.default)(`${K}-actions`,q("actions")),ed=(null==E?void 0:E.length)?t.createElement(h,{actionClasses:es,actionStyle:X("actions"),actions:E}):null,ec=(0,r.default)(F,["onTabChange"]),eu=(0,a.default)(K,null==D?void 0:D.className,{[`${K}-loading`]:O,[`${K}-bordered`]:"borderless"!==G,[`${K}-hoverable`]:P,[`${K}-contain-grid`]:_,[`${K}-contain-tabs`]:null==T?void 0:T.length,[`${K}-${ee}`]:ee,[`${K}-type-${k}`]:!!k,[`${K}-rtl`]:"rtl"===W},g,m,U,J),eg=Object.assign(Object.assign({},null==D?void 0:D.style),$);return Y(t.createElement("div",Object.assign({ref:s},ec,{className:eu,style:eg}),c,el,eo,ed))});var y=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};$.Grid=d,$.Meta=e=>{let{prefixCls:r,className:n,avatar:i,title:o,description:s}=e,d=y(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:c}=t.useContext(l.ConfigContext),u=c("card",r),g=(0,a.default)(`${u}-meta`,n),m=i?t.createElement("div",{className:`${u}-meta-avatar`},i):null,b=o?t.createElement("div",{className:`${u}-meta-title`},o):null,p=s?t.createElement("div",{className:`${u}-meta-description`},s):null,f=b||p?t.createElement("div",{className:`${u}-meta-detail`},b,p):null;return t.createElement("div",Object.assign({},d,{className:g}),m,f)},e.s(["Card",0,$],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),r=e.i(908206),l=e.i(242064),n=e.i(517455),i=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var d=e.i(876556),c=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a},u=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};let g=e=>{let{itemPrefixCls:r,component:l,span:n,className:i,style:o,labelStyle:d,contentStyle:c,bordered:u,label:g,content:m,colon:b,type:p,styles:f}=e,{classNames:h}=t.useContext(s),$=Object.assign(Object.assign({},d),null==f?void 0:f.label),y=Object.assign(Object.assign({},c),null==f?void 0:f.content);if(u)return t.createElement(l,{colSpan:n,style:o,className:(0,a.default)(i,{[`${r}-item-${p}`]:"label"===p||"content"===p,[null==h?void 0:h.label]:(null==h?void 0:h.label)&&"label"===p,[null==h?void 0:h.content]:(null==h?void 0:h.content)&&"content"===p})},null!=g&&t.createElement("span",{style:$},g),null!=m&&t.createElement("span",{style:y},m));return t.createElement(l,{colSpan:n,style:o,className:(0,a.default)(`${r}-item`,i)},t.createElement("div",{className:`${r}-item-container`},null!=g&&t.createElement("span",{style:$,className:(0,a.default)(`${r}-item-label`,null==h?void 0:h.label,{[`${r}-item-no-colon`]:!b})},g),null!=m&&t.createElement("span",{style:y,className:(0,a.default)(`${r}-item-content`,null==h?void 0:h.content)},m)))};function m(e,{colon:a,prefixCls:r,bordered:l},{component:n,type:i,showLabel:o,showContent:s,labelStyle:d,contentStyle:c,styles:u}){return e.map(({label:e,children:m,prefixCls:b=r,className:p,style:f,labelStyle:h,contentStyle:$,span:y=1,key:x,styles:v},j)=>"string"==typeof n?t.createElement(g,{key:`${i}-${x||j}`,className:p,style:f,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),h),null==v?void 0:v.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),$),null==v?void 0:v.content)},span:y,colon:a,component:n,itemPrefixCls:b,bordered:l,label:o?e:null,content:s?m:null,type:i}):[t.createElement(g,{key:`label-${x||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),f),h),null==v?void 0:v.label),span:1,colon:a,component:n[0],itemPrefixCls:b,bordered:l,label:e,type:"label"}),t.createElement(g,{key:`content-${x||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),f),$),null==v?void 0:v.content),span:2*y-1,component:n[1],itemPrefixCls:b,bordered:l,content:m,type:"content"})])}let b=e=>{let a=t.useContext(s),{prefixCls:r,vertical:l,row:n,index:i,bordered:o}=e;return l?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${i}`,className:`${r}-row`},m(n,e,Object.assign({component:"th",type:"label",showLabel:!0},a))),t.createElement("tr",{key:`content-${i}`,className:`${r}-row`},m(n,e,Object.assign({component:"td",type:"content",showContent:!0},a)))):t.createElement("tr",{key:i,className:`${r}-row`},m(n,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},a)))};e.i(296059);var p=e.i(915654),f=e.i(183293),h=e.i(246422),$=e.i(838378);let y=(0,h.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:a,itemPaddingBottom:r,itemPaddingEnd:l,colonMarginRight:n,colonMarginLeft:i,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,f.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:a}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.padding)} ${(0,p.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:a,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingSM)} ${(0,p.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingXS)} ${(0,p.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},f.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:a,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:r,paddingInlineEnd:l},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,p.unit)(i)} ${(0,p.unit)(n)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,$.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var x=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(a[r[l]]=e[r[l]]);return a};let v=e=>{let g,{prefixCls:m,title:p,extra:f,column:h,colon:$=!0,bordered:v,layout:j,children:O,className:w,rootClassName:C,style:S,size:k,labelStyle:N,contentStyle:E,styles:T,items:B,classNames:z}=e,M=x(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:R,direction:P,className:L,style:H,classNames:I,styles:F}=(0,l.useComponentConfig)("descriptions"),A=R("descriptions",m),W=(0,i.default)(),D=t.useMemo(()=>{var e;return"number"==typeof h?h:null!=(e=(0,r.matchScreen)(W,Object.assign(Object.assign({},o),h)))?e:3},[W,h]),G=(g=t.useMemo(()=>B||(0,d.default)(O).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[B,O]),t.useMemo(()=>g.map(e=>{var{span:t}=e,a=c(e,["span"]);return"filled"===t?Object.assign(Object.assign({},a),{filled:!0}):Object.assign(Object.assign({},a),{span:"number"==typeof t?t:(0,r.matchScreen)(W,t)})}),[g,W])),q=(0,n.default)(k),X=((e,a)=>{let[r,l]=(0,t.useMemo)(()=>{let t,r,l,n;return t=[],r=[],l=!1,n=0,a.filter(e=>e).forEach(a=>{let{filled:i}=a,o=u(a,["filled"]);if(i){r.push(o),t.push(r),r=[],n=0;return}let s=e-n;(n+=a.span||1)>=e?(n>e?(l=!0,r.push(Object.assign(Object.assign({},o),{span:s}))):r.push(o),t.push(r),r=[],n=0):r.push(o)}),r.length>0&&t.push(r),[t=t.map(t=>{let a=t.reduce((e,t)=>e+(t.span||1),0);if(a({labelStyle:N,contentStyle:E,styles:{content:Object.assign(Object.assign({},F.content),null==T?void 0:T.content),label:Object.assign(Object.assign({},F.label),null==T?void 0:T.label)},classNames:{label:(0,a.default)(I.label,null==z?void 0:z.label),content:(0,a.default)(I.content,null==z?void 0:z.content)}}),[N,E,T,z,I,F]);return _(t.createElement(s.Provider,{value:U},t.createElement("div",Object.assign({className:(0,a.default)(A,L,I.root,null==z?void 0:z.root,{[`${A}-${q}`]:q&&"default"!==q,[`${A}-bordered`]:!!v,[`${A}-rtl`]:"rtl"===P},w,C,K,Y),style:Object.assign(Object.assign(Object.assign(Object.assign({},H),F.root),null==T?void 0:T.root),S)},M),(p||f)&&t.createElement("div",{className:(0,a.default)(`${A}-header`,I.header,null==z?void 0:z.header),style:Object.assign(Object.assign({},F.header),null==T?void 0:T.header)},p&&t.createElement("div",{className:(0,a.default)(`${A}-title`,I.title,null==z?void 0:z.title),style:Object.assign(Object.assign({},F.title),null==T?void 0:T.title)},p),f&&t.createElement("div",{className:(0,a.default)(`${A}-extra`,I.extra,null==z?void 0:z.extra),style:Object.assign(Object.assign({},F.extra),null==T?void 0:T.extra)},f)),t.createElement("div",{className:`${A}-view`},t.createElement("table",null,t.createElement("tbody",null,X.map((e,a)=>t.createElement(b,{key:a,index:a,colon:$,prefixCls:A,vertical:"vertical"===j,bordered:v,row:e}))))))))};v.Item=({children:e})=>e,e.s(["Descriptions",0,v],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),a=e.i(732961),r=e.i(289882),l=e.i(170517),n=e.i(628882),i=e.i(320890),o=e.i(104458),s=e.i(722319),d=e.i(8398),c=e.i(279728);e.i(765846);var u=e.i(602716),g=e.i(328052),m=e.i(135551);let b=(e,t)=>new m.FastColor(e).setA(t).toRgbString(),p=(e,t)=>new m.FastColor(e).lighten(t).toHexString(),f=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},h=(e,t)=>{let a=e||"#000",r=t||"#fff";return{colorBgBase:a,colorTextBase:r,colorText:b(r,.85),colorTextSecondary:b(r,.65),colorTextTertiary:b(r,.45),colorTextQuaternary:b(r,.25),colorFill:b(r,.18),colorFillSecondary:b(r,.12),colorFillTertiary:b(r,.08),colorFillQuaternary:b(r,.04),colorBgSolid:b(r,.95),colorBgSolidHover:b(r,1),colorBgSolidActive:b(r,.9),colorBgElevated:p(a,12),colorBgContainer:p(a,8),colorBgLayout:p(a,0),colorBgSpotlight:p(a,26),colorBgBlur:b(r,.04),colorBorder:p(a,26),colorBorderSecondary:p(a,19)}},$={defaultSeed:i.defaultConfig.token,useToken:function(){let[e,t,a]=(0,o.useToken)();return{theme:e,token:t,hashId:a}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let a=Object.keys(l.defaultPresetColors).map(t=>{let a=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,r,l)=>(e[`${t}-${l+1}`]=a[l],e[`${t}${l+1}`]=a[l],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),r=null!=t?t:(0,s.default)(e),n=(0,g.default)(e,{generateColorPalettes:f,generateNeutralColorPalettes:h});return Object.assign(Object.assign(Object.assign(Object.assign({},r),a),n),{colorPrimaryBg:n.colorPrimaryBorder,colorPrimaryBgHover:n.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let a=null!=t?t:(0,s.default)(e),r=a.fontSizeSM,l=a.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},a),function(e){let{sizeUnit:t,sizeStep:a}=e,r=a-2;return{sizeXXL:t*(r+10),sizeXL:t*(r+6),sizeLG:t*(r+2),sizeMD:t*(r+2),sizeMS:t*(r+1),size:t*r,sizeSM:t*r,sizeXS:t*(r-1),sizeXXS:t*(r-1)}}(null!=t?t:e)),(0,c.default)(r)),{controlHeight:l}),(0,d.default)(Object.assign(Object.assign({},a),{controlHeight:l})))},getDesignToken:e=>{let i=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):r.default,o=Object.assign(Object.assign({},l.default),null==e?void 0:e.token);return(0,a.getComputedToken)(o,{override:null==e?void 0:e.token},i,n.default)},defaultConfig:i.defaultConfig,_internalContext:i.DesignTokenContext};e.s(["theme",0,$],368869)},127952,e=>{"use strict";var t=e.i(843476),a=e.i(560445),r=e.i(175712),l=e.i(869216),n=e.i(311451),i=e.i(212931),o=e.i(898586),s=e.i(368869),d=e.i(270377),c=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:g,message:m,resourceInformationTitle:b,resourceInformation:p,onCancel:f,onOk:h,confirmLoading:$,requiredConfirmation:y}){let{Title:x,Text:v}=o.Typography,{token:j}=s.theme.useToken(),[O,w]=(0,c.useState)("");return(0,c.useEffect)(()=>{e&&w("")},[e]),(0,t.jsx)(i.Modal,{title:u,open:e,onOk:h,onCancel:f,confirmLoading:$,okText:$?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!y&&O!==y||$},cancelButtonProps:{disabled:$},children:(0,t.jsxs)("div",{className:"space-y-4",children:[g&&(0,t.jsx)(a.Alert,{message:g,type:"warning"}),(0,t.jsx)(r.Card,{title:b,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder}},style:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder},children:(0,t.jsx)(l.Descriptions,{column:1,size:"small",children:p&&p.map(({label:e,value:a,...r})=>(0,t.jsx)(l.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(v,{...r,children:a??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(v,{children:m})}),y&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(v,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(v,{children:"Type "}),(0,t.jsx)(v,{strong:!0,type:"danger",children:y}),(0,t.jsx)(v,{children:" to confirm deletion:"})]}),(0,t.jsx)(n.Input,{value:O,onChange:e=>w(e.target.value),placeholder:y,className:"rounded-md",prefix:(0,t.jsx)(d.ExclamationCircleOutlined,{style:{color:j.colorError}}),autoFocus:!0})]})]})})}])},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),r=e.i(242064),l=e.i(529681);let n=e=>{let{prefixCls:r,className:l,style:n,size:i,shape:o}=e,s=(0,a.default)({[`${r}-lg`]:"large"===i,[`${r}-sm`]:"small"===i}),d=(0,a.default)({[`${r}-circle`]:"circle"===o,[`${r}-square`]:"square"===o,[`${r}-round`]:"round"===o}),c=t.useMemo(()=>"number"==typeof i?{width:i,height:i,lineHeight:`${i}px`}:{},[i]);return t.createElement("span",{className:(0,a.default)(r,s,d,l),style:Object.assign(Object.assign({},c),n)})};e.i(296059);var i=e.i(694758),o=e.i(915654),s=e.i(246422),d=e.i(838378);let c=new i.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),u=e=>({height:e,lineHeight:(0,o.unit)(e)}),g=e=>Object.assign({width:e},u(e)),m=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},u(e)),b=e=>Object.assign({width:e},u(e)),p=(e,t,a)=>{let{skeletonButtonCls:r}=e;return{[`${a}${r}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${a}${r}-round`]:{borderRadius:t}}},f=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},u(e)),h=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:a}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:a,skeletonTitleCls:r,skeletonParagraphCls:l,skeletonButtonCls:n,skeletonInputCls:i,skeletonImageCls:o,controlHeight:s,controlHeightLG:d,controlHeightSM:u,gradientFromColor:h,padding:$,marginSM:y,borderRadius:x,titleHeight:v,blockRadius:j,paragraphLiHeight:O,controlHeightXS:w,paragraphMarginTop:C}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:$,verticalAlign:"top",[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:h},g(s)),[`${a}-circle`]:{borderRadius:"50%"},[`${a}-lg`]:Object.assign({},g(d)),[`${a}-sm`]:Object.assign({},g(u))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[r]:{width:"100%",height:v,background:h,borderRadius:j,[`+ ${l}`]:{marginBlockStart:u}},[l]:{padding:0,"> li":{width:"100%",height:O,listStyle:"none",background:h,borderRadius:j,"+ li":{marginBlockStart:w}}},[`${l}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${r}, ${l} > li`]:{borderRadius:x}}},[`${t}-with-avatar ${t}-content`]:{[r]:{marginBlockStart:y,[`+ ${l}`]:{marginBlockStart:C}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:a,controlHeight:r,controlHeightLG:l,controlHeightSM:n,gradientFromColor:i,calc:o}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:t,width:o(r).mul(2).equal(),minWidth:o(r).mul(2).equal()},f(r,o))},p(e,r,a)),{[`${a}-lg`]:Object.assign({},f(l,o))}),p(e,l,`${a}-lg`)),{[`${a}-sm`]:Object.assign({},f(n,o))}),p(e,n,`${a}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:a,controlHeight:r,controlHeightLG:l,controlHeightSM:n}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:a},g(r)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},g(l)),[`${t}${t}-sm`]:Object.assign({},g(n))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:a,skeletonInputCls:r,controlHeightLG:l,controlHeightSM:n,gradientFromColor:i,calc:o}=e;return{[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:a},m(t,o)),[`${r}-lg`]:Object.assign({},m(l,o)),[`${r}-sm`]:Object.assign({},m(n,o))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:a,gradientFromColor:r,borderRadiusSM:l,calc:n}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:r,borderRadius:l},b(n(a).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(a)),{maxWidth:n(a).mul(4).equal(),maxHeight:n(a).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[n]:{width:"100%"},[i]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${r}, + ${l} > li, + ${a}, + ${n}, + ${i}, + ${o} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:a(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:a}=e;return{color:t,colorGradientEnd:a,gradientFromColor:t,gradientToColor:a,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),$=e=>{let{prefixCls:r,className:l,style:n,rows:i=0}=e,o=Array.from({length:i}).map((a,r)=>t.createElement("li",{key:r,style:{width:((e,t)=>{let{width:a,rows:r=2}=t;return Array.isArray(a)?a[e]:r-1===e?a:void 0})(r,e)}}));return t.createElement("ul",{className:(0,a.default)(r,l),style:n},o)},y=({prefixCls:e,className:r,width:l,style:n})=>t.createElement("h3",{className:(0,a.default)(e,r),style:Object.assign({width:l},n)});function x(e){return e&&"object"==typeof e?e:{}}let v=e=>{let{prefixCls:l,loading:i,className:o,rootClassName:s,style:d,children:c,avatar:u=!1,title:g=!0,paragraph:m=!0,active:b,round:p}=e,{getPrefixCls:f,direction:v,className:j,style:O}=(0,r.useComponentConfig)("skeleton"),w=f("skeleton",l),[C,S,k]=h(w);if(i||!("loading"in e)){let e,r,l=!!u,i=!!g,c=!!m;if(l){let a=Object.assign(Object.assign({prefixCls:`${w}-avatar`},i&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),x(u));e=t.createElement("div",{className:`${w}-header`},t.createElement(n,Object.assign({},a)))}if(i||c){let e,a;if(i){let a=Object.assign(Object.assign({prefixCls:`${w}-title`},!l&&c?{width:"38%"}:l&&c?{width:"50%"}:{}),x(g));e=t.createElement(y,Object.assign({},a))}if(c){let e,r=Object.assign(Object.assign({prefixCls:`${w}-paragraph`},(e={},l&&i||(e.width="61%"),!l&&i?e.rows=3:e.rows=2,e)),x(m));a=t.createElement($,Object.assign({},r))}r=t.createElement("div",{className:`${w}-content`},e,a)}let f=(0,a.default)(w,{[`${w}-with-avatar`]:l,[`${w}-active`]:b,[`${w}-rtl`]:"rtl"===v,[`${w}-round`]:p},j,o,s,S,k);return C(t.createElement("div",{className:f,style:Object.assign(Object.assign({},O),d)},e,r))}return null!=c?c:null};v.Button=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,block:c=!1,size:u="default"}=e,{getPrefixCls:g}=t.useContext(r.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,l.default)(e,["prefixCls"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d,[`${m}-block`]:c},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-button`,size:u},$))))},v.Avatar=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,shape:c="circle",size:u="default"}=e,{getPrefixCls:g}=t.useContext(r.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,l.default)(e,["prefixCls","className"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-avatar`,shape:c,size:u},$))))},v.Input=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,block:c,size:u="default"}=e,{getPrefixCls:g}=t.useContext(r.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,l.default)(e,["prefixCls"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d,[`${m}-block`]:c},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-input`,size:u},$))))},v.Image=e=>{let{prefixCls:l,className:n,rootClassName:i,style:o,active:s}=e,{getPrefixCls:d}=t.useContext(r.ConfigContext),c=d("skeleton",l),[u,g,m]=h(c),b=(0,a.default)(c,`${c}-element`,{[`${c}-active`]:s},n,i,g,m);return u(t.createElement("div",{className:b},t.createElement("div",{className:(0,a.default)(`${c}-image`,n),style:o},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},v.Node=e=>{let{prefixCls:l,className:n,rootClassName:i,style:o,active:s,children:d}=e,{getPrefixCls:c}=t.useContext(r.ConfigContext),u=c("skeleton",l),[g,m,b]=h(u),p=(0,a.default)(u,`${u}-element`,{[`${u}-active`]:s},m,n,i,b);return g(t.createElement("div",{className:p},t.createElement("div",{className:(0,a.default)(`${u}-image`,n),style:o},d)))},e.s(["default",0,v],185793)},922611,e=>{"use strict";var t=e.i(271645),a=e.i(175066);function r(){}let l=t.createContext({add:r,remove:r});e.s(["usePanelRef",0,function(e){let r=t.useContext(l),n=t.useRef(null);return(0,a.default)(t=>{if(t){let a=e?t.querySelector(e):t;a&&(r.add(a),n.current=a)}else r.remove(n.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let a=(e,t=0,a=!1,r=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!r)return"-";let l={minimumFractionDigits:t,maximumFractionDigits:t};if(!a)return e.toLocaleString("en-US",l);let n=e<0?"-":"",i=Math.abs(e),o=i,s="";return i>=1e6?(o=i/1e6,s="M"):i>=1e3&&(o=i/1e3,s="K"),`${n}${o.toLocaleString("en-US",l)}${s}`},r=async(e,a="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return l(e,a);try{return await navigator.clipboard.writeText(e),t.default.success(a),!0}catch(t){return console.error("Clipboard API failed: ",t),l(e,a)}},l=(e,a)=>{try{let r=document.createElement("textarea");r.value=e,r.style.position="fixed",r.style.left="-999999px",r.style.top="-999999px",r.setAttribute("readonly",""),document.body.appendChild(r),r.focus(),r.select();let l=document.execCommand("copy");if(document.body.removeChild(r),l)return t.default.success(a),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,r,"formatNumberWithCommas",0,a,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let r=a(e,t,!1,!1);if(0===Number(r.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${r}`},"updateExistingKeys",0,function(e,t){let a=structuredClone(e);for(let[e,r]of Object.entries(t))e in a&&(a[e]=r);return a}])},112179,581070,e=>{"use strict";var t=e.i(843476),a=e.i(487486),r=e.i(115504),l=e.i(746798);function n({content:e,trigger:a}){return(0,t.jsx)(l.TooltipProvider,{delay:300,children:(0,t.jsxs)(l.Tooltip,{children:[(0,t.jsx)(l.TooltipTrigger,{render:a}),(0,t.jsx)(l.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,n],581070);let i={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:l,tooltip:o,dataTestId:s}){let d=(0,t.jsx)(a.Badge,{variant:"outline","data-testid":s,className:(0,r.cn)("whitespace-nowrap font-normal",i[e]),children:l});return o?(0,t.jsx)(n,{content:o,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),a=e.i(843476);let r=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],l=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:n="datetime",fallback:i="-"}){let o,s,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,a.jsx)("span",{className:"text-muted-foreground",children:i}):(0,a.jsx)(t.CellTooltip,{content:(o=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${r[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${l(c.getHours())}:${l(c.getMinutes())}:${l(c.getSeconds())}`,`${s}, ${d} (${o})`),trigger:(0,a.jsx)("span",{className:"whitespace-nowrap",children:"date"===n?`${r[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${r[c.getMonth()]} ${c.getDate()}, ${l(c.getHours())}:${l(c.getMinutes())}:${l(c.getSeconds())}`})})}],200208);var n=e.i(174886),i=e.i(115504),o=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:r="pill",onClick:l,copyable:d=!1,truncate:c=!0,fallback:u="-",tooltip:g,disabled:m=!1,dataTestId:b,className:p}){if(!e)return(0,a.jsx)("span",{className:"text-muted-foreground",children:u});let f=!!l&&!m,h=(0,i.cn)(s[r].base,f&&s[r].clickable,c&&"block max-w-[15ch] truncate",m&&"opacity-50",p),$=f?(0,a.jsx)("button",{type:"button",className:h,"data-testid":b,onClick:()=>l(e),children:e}):(0,a.jsx)("span",{className:h,"data-testid":b,children:e}),y=(0,a.jsx)(t.CellTooltip,{content:g??e,trigger:$});return d?(0,a.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[y,(0,a.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,o.copyToClipboard)(e)},children:(0,a.jsx)(n.Copy,{className:"size-3"})})]}):y}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:r="-",showZero:l=!1}){return null==e||Number.isNaN(e)?(0,a.jsx)("span",{className:"text-muted-foreground",children:r}):0===e?l?(0,a.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,o.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,a.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,a.jsx)("span",{className:"whitespace-nowrap",children:(0,o.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,a],68155)},360820,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,a],360820)},871943,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,a],871943)},269200,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("Table"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement("div",{className:(0,r.tremorTwMerge)(l("root"),"overflow-auto",o)},a.default.createElement("table",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),i))});n.displayName="Table",e.s(["Table",0,n],269200)},427612,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableHead"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("thead",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",o)},s),i))});n.displayName="TableHead",e.s(["TableHead",0,n],427612)},64848,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableHeaderCell"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("th",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",o)},s),i))});n.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,n],64848)},942232,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableBody"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("tbody",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",o)},s),i))});n.displayName="TableBody",e.s(["TableBody",0,n],942232)},496020,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableRow"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("tr",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("row"),o)},s),i))});n.displayName="TableRow",e.s(["TableRow",0,n],496020)},977572,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableCell"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("td",Object.assign({ref:n,className:(0,r.tremorTwMerge)(l("root"),"align-middle whitespace-nowrap text-left p-4",o)},s),i))});n.displayName="TableCell",e.s(["TableCell",0,n],977572)},389083,e=>{"use strict";var t=e.i(290571),a=e.i(271645),r=e.i(829087),l=e.i(480731),n=e.i(95779),i=e.i(444755),o=e.i(673706);let s={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},d={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},c=(0,o.makeClassName)("Badge"),u=a.default.forwardRef((e,u)=>{let{color:g,icon:m,size:b=l.Sizes.SM,tooltip:p,className:f,children:h}=e,$=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),y=m||null,{tooltipProps:x,getReferenceProps:v}=(0,r.useTooltip)();return a.default.createElement("span",Object.assign({ref:(0,o.mergeRefs)([u,x.refs.setReference]),className:(0,i.tremorTwMerge)(c("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",g?(0,i.tremorTwMerge)((0,o.getColorClassNames)(g,n.colorPalette.background).bgColor,(0,o.getColorClassNames)(g,n.colorPalette.iconText).textColor,(0,o.getColorClassNames)(g,n.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,i.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),s[b].paddingX,s[b].paddingY,s[b].fontSize,f)},v,$),a.default.createElement(r.default,Object.assign({text:p},x)),y?a.default.createElement(y,{className:(0,i.tremorTwMerge)(c("icon"),"shrink-0 -ml-1 mr-1.5",d[b].height,d[b].width)}):null,a.default.createElement("span",{className:(0,i.tremorTwMerge)(c("text"),"whitespace-nowrap")},h))});u.displayName="Badge",e.s(["Badge",0,u],389083)},530212,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,a],530212)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03sib2ibxxpji.js b/litellm/proxy/_experimental/out/_next/static/chunks/03sib2ibxxpji.js new file mode 100644 index 00000000000..ee0c24f0032 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03sib2ibxxpji.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),s=e.i(529681);let l=e=>{let{prefixCls:a,className:s,style:l,size:n,shape:i}=e,o=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),c=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),d=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,o,c,s),style:Object.assign(Object.assign({},d),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),o=e.i(246422),c=e.i(838378);let d=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),m=e=>({height:e,lineHeight:(0,i.unit)(e)}),u=e=>Object.assign({width:e},m(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},m(e)),p=e=>Object.assign({width:e},m(e)),x=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},m(e)),f=(0,o.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:s,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:o,controlHeightLG:c,controlHeightSM:m,gradientFromColor:f,padding:b,marginSM:v,borderRadius:j,titleHeight:w,blockRadius:N,paragraphLiHeight:y,controlHeightXS:k,paragraphMarginTop:$}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:b,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:f},u(o)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},u(c)),[`${r}-sm`]:Object.assign({},u(m))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:w,background:f,borderRadius:N,[`+ ${s}`]:{marginBlockStart:m}},[s]:{padding:0,"> li":{width:"100%",height:y,listStyle:"none",background:f,borderRadius:N,"+ li":{marginBlockStart:k}}},[`${s}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${s} > li`]:{borderRadius:j}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:v,[`+ ${s}`]:{marginBlockStart:$}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},x(e,a,r)),{[`${r}-lg`]:Object.assign({},h(s,i))}),x(e,s,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),x(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},u(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},u(s)),[`${t}${t}-sm`]:Object.assign({},u(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},g(t,i)),[`${a}-lg`]:Object.assign({},g(s,i)),[`${a}-sm`]:Object.assign({},g(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:s,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:s},p(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},p(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${s} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:d,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,c.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),b=e=>{let{prefixCls:a,className:s,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,s),style:l},i)},v=({prefixCls:e,className:a,width:s,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:s},l)});function j(e){return e&&"object"==typeof e?e:{}}let w=e=>{let{prefixCls:s,loading:n,className:i,rootClassName:o,style:c,children:d,avatar:m=!1,title:u=!0,paragraph:g=!0,active:p,round:x}=e,{getPrefixCls:h,direction:w,className:N,style:y}=(0,a.useComponentConfig)("skeleton"),k=h("skeleton",s),[$,C,T]=f(k);if(n||!("loading"in e)){let e,a,s=!!m,n=!!u,d=!!g;if(s){let r=Object.assign(Object.assign({prefixCls:`${k}-avatar`},n&&!d?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),j(m));e=t.createElement("div",{className:`${k}-header`},t.createElement(l,Object.assign({},r)))}if(n||d){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${k}-title`},!s&&d?{width:"38%"}:s&&d?{width:"50%"}:{}),j(u));e=t.createElement(v,Object.assign({},r))}if(d){let e,a=Object.assign(Object.assign({prefixCls:`${k}-paragraph`},(e={},s&&n||(e.width="61%"),!s&&n?e.rows=3:e.rows=2,e)),j(g));r=t.createElement(b,Object.assign({},a))}a=t.createElement("div",{className:`${k}-content`},e,r)}let h=(0,r.default)(k,{[`${k}-with-avatar`]:s,[`${k}-active`]:p,[`${k}-rtl`]:"rtl"===w,[`${k}-round`]:x},N,i,o,C,T);return $(t.createElement("div",{className:h,style:Object.assign(Object.assign({},y),c)},e,a))}return null!=d?d:null};w.Button=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d=!1,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-button`,size:m},b))))},w.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,shape:d="circle",size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls","className"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-avatar`,shape:d,size:m},b))))},w.Input=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-input`,size:m},b))))},w.Image=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),d=c("skeleton",s),[m,u,g]=f(d),p=(0,r.default)(d,`${d}-element`,{[`${d}-active`]:o},l,n,u,g);return m(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${d}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${d}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${d}-image-path`})))))},w.Node=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o,children:c}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),m=d("skeleton",s),[u,g,p]=f(m),x=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:o},g,l,n,p);return u(t.createElement("div",{className:x},t.createElement("div",{className:(0,r.default)(`${m}-image`,l),style:i},c)))},e.s(["default",0,w],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let s=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(s),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let s={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",s);let l=e<0?"-":"",n=Math.abs(e),i=n,o="";return n>=1e6?(i=n/1e6,o="M"):n>=1e3&&(i=n/1e3,o="K"),`${l}${i.toLocaleString("en-US",s)}${o}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return s(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),s(e,r)}},s=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let s=document.execCommand("copy");if(document.body.removeChild(a),s)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),s=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(s.TooltipProvider,{delay:300,children:(0,t.jsxs)(s.Tooltip,{children:[(0,t.jsx)(s.TooltipTrigger,{render:r}),(0,t.jsx)(s.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:s,tooltip:i,dataTestId:o}){let c=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":o,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:s});return i?(0,t.jsx)(l,{content:i,trigger:c}):c}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],s=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,o,c,d=e?new Date(e):null;return!d||Number.isNaN(d.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,o=`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`,c=`${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`,`${o}, ${c} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`:`${a[d.getMonth()]} ${d.getDate()}, ${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let o={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:s,copyable:c=!1,truncate:d=!0,fallback:m="-",tooltip:u,disabled:g=!1,dataTestId:p,className:x}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:m});let h=!!s&&!g,f=(0,n.cn)(o[a].base,h&&o[a].clickable,d&&"block max-w-[15ch] truncate",g&&"opacity-50",x),b=h?(0,r.jsx)("button",{type:"button",className:f,"data-testid":p,onClick:()=>s(e),children:e}):(0,r.jsx)("span",{className:f,"data-testid":p,children:e}),v=(0,r.jsx)(t.CellTooltip,{content:u??e,trigger:b});return c?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[v,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):v}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:s=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?s?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(s("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},o),n))});l.displayName="Table",e.s(["Table",0,l],269200)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},o),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("row"),i)},o),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},o),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},o),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-middle whitespace-nowrap text-left p-4",i)},o),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),s=e.i(480731),l=e.i(95779),n=e.i(444755),i=e.i(673706);let o={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},c={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},d=(0,i.makeClassName)("Badge"),m=r.default.forwardRef((e,m)=>{let{color:u,icon:g,size:p=s.Sizes.SM,tooltip:x,className:h,children:f}=e,b=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),v=g||null,{tooltipProps:j,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,j.refs.setReference]),className:(0,n.tremorTwMerge)(d("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",u?(0,n.tremorTwMerge)((0,i.getColorClassNames)(u,l.colorPalette.background).bgColor,(0,i.getColorClassNames)(u,l.colorPalette.iconText).textColor,(0,i.getColorClassNames)(u,l.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),o[p].paddingX,o[p].paddingY,o[p].fontSize,h)},w,b),r.default.createElement(a.default,Object.assign({text:x},j)),v?r.default.createElement(v,{className:(0,n.tremorTwMerge)(d("icon"),"shrink-0 -ml-1 mr-1.5",c[p].height,c[p].width)}):null,r.default.createElement("span",{className:(0,n.tremorTwMerge)(d("text"),"whitespace-nowrap")},f))});m.displayName="Badge",e.s(["Badge",0,m],389083)},502547,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,r],502547)},384767,e=>{"use strict";var t=e.i(843476),r=e.i(599724),a=e.i(271645),s=e.i(389083);let l=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"}))});var n=e.i(602869);let i=function({vectorStores:e,accessToken:i}){let[o,c]=(0,a.useState)([]);return(0,a.useEffect)(()=>{(async()=>{if(i&&0!==e.length)try{let e=await (0,n.vectorStoreListCall)(i);e.data&&c(e.data.map(e=>({vector_store_id:e.vector_store_id,vector_store_name:e.vector_store_name})))}catch(e){console.error("Error fetching vector stores:",e)}})()},[i,e.length]),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Vector Stores"}),(0,t.jsx)(s.Badge,{color:"blue",size:"xs",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map((e,r)=>{let a;return(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-sm font-medium",children:(a=o.find(t=>t.vector_store_id===e))?`${a.vector_store_name||a.vector_store_id} (${a.vector_store_id})`:e},r)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No vector stores configured"})]})]})},o=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"}))});var c=e.i(871943),d=e.i(502547),m=e.i(592968),u=e.i(234713);let g=function({mcpServers:e,mcpAccessGroups:l=[],mcpToolPermissions:i={},mcpToolsets:g=[],accessToken:p}){let[x,h]=(0,a.useState)([]),[f,b]=(0,a.useState)([]),[v,j]=(0,a.useState)(new Set),[w,N]=(0,a.useState)(new Set);(0,a.useEffect)(()=>{(async()=>{if(p&&e.length>0)try{let e=await (0,n.fetchMCPServers)(p);e&&Array.isArray(e)?h(e):e.data&&Array.isArray(e.data)&&h(e.data)}catch(e){console.error("Error fetching MCP servers:",e)}})()},[p,e.length]),(0,a.useEffect)(()=>{(async()=>{if(p&&g.length>0)try{let e=await (0,n.fetchMCPToolsets)(p),t=Array.isArray(e)?e.filter(e=>g.includes(e.toolset_id)):[];b(t)}catch(e){console.error("Error fetching toolsets:",e)}})()},[p,g.length]);let y=e.includes(u.NO_MCP_SERVERS_SENTINEL),k=e.includes(u.ALL_PROXY_MCP_SERVERS_SENTINEL),$=[...e.filter(e=>e!==u.NO_MCP_SERVERS_SENTINEL&&e!==u.ALL_PROXY_MCP_SERVERS_SENTINEL).map(e=>({type:"server",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],C=$.length+g.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"MCP Servers"}),(0,t.jsx)(s.Badge,{color:y?"red":"blue",size:"xs",children:y?"Blocked":k?"All":C})]}),y?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-red-400"}),(0,t.jsx)(r.Text,{className:"text-red-700 text-sm",children:"No MCP servers — this key is blocked from all MCP servers, including its team's servers"})]}):k?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-400"}),(0,t.jsx)(r.Text,{className:"text-blue-700 text-sm",children:"All Proxy MCP Servers"})]}):C>0?(0,t.jsxs)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:[$.map((e,r)=>{let a="server"===e.type?i[e.value]:void 0,s=a&&a.length>0,l=v.has(e.value);return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>{var t;return s&&(t=e.value,void j(e=>{let r=new Set(e);return r.has(t)?r.delete(t):r.add(t),r}))},className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 transition-all ${s?"cursor-pointer hover:bg-gray-50 hover:border-gray-300":"bg-white"}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"server"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-blue-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=x.find(t=>t.server_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.alias} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})}),s&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:a.length}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===a.length?"tool":"tools"}),l?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),s&&l&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-blue-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.map((e,r)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-xs font-medium",children:e},r))})})]},r)}),g.length>0&&g.map((e,r)=>{let a=f.find(t=>t.toolset_id===e),s=w.has(e),l=a?.tools.length??0;return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>l>0&&void N(t=>{let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}),className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-purple-200 transition-all ${l>0?"cursor-pointer hover:bg-purple-50 hover:border-purple-300":"bg-white"}`,children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:a?.toolset_name??e}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-purple-600 bg-purple-50 border border-purple-200 rounded-sm uppercase tracking-wide shrink-0",children:"Toolset"})]}),l>0&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:l}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===l?"tool":"tools"}),s?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),l>0&&s&&a&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-purple-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.tools.map((e,r)=>(0,t.jsxs)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-purple-50 border border-purple-200 text-purple-800 text-xs font-medium",children:[(0,t.jsxs)("span",{className:"text-purple-400 mr-1 text-[10px]",children:[e.server_id.slice(0,6),"…"]}),e.tool_name]},r))})})]},`toolset-${r}`)})]}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No MCP servers, access groups, or toolsets configured"})]})]})},p=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"}))}),x=function({agents:e,agentAccessGroups:l=[],accessToken:i}){let[o,c]=(0,a.useState)([]);(0,a.useEffect)(()=>{(async()=>{if(i&&e.length>0)try{let e=await (0,n.getAgentsList)(i);e&&e.agents&&Array.isArray(e.agents)&&c(e.agents)}catch(e){console.error("Error fetching agents:",e)}})()},[i,e.length]);let d=[...e.map(e=>({type:"agent",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],u=d.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-purple-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Agents"}),(0,t.jsx)(s.Badge,{color:"purple",size:"xs",children:u})]}),u>0?(0,t.jsx)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:d.map((e,r)=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsx)("div",{className:"flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 bg-white",children:(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"agent"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=o.find(t=>t.agent_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.agent_name} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})})})},r))}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No agents or access groups configured"})]})]})};e.s(["default",0,function({objectPermission:e,variant:a="card",className:s="",accessToken:l}){let n=e?.vector_stores||[],o=e?.mcp_servers||[],c=e?.mcp_access_groups||[],d=e?.mcp_tool_permissions||{},m=e?.mcp_toolsets||[],u=e?.agents||[],p=e?.agent_access_groups||[],h=e?.search_tools||[],f=(0,t.jsxs)("div",{className:"card"===a?"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6":"space-y-4",children:[(0,t.jsx)(i,{vectorStores:n,accessToken:l}),(0,t.jsx)(g,{mcpServers:o,mcpAccessGroups:c,mcpToolPermissions:d,mcpToolsets:m,accessToken:l}),(0,t.jsx)(x,{agents:u,agentAccessGroups:p,accessToken:l}),(0,t.jsxs)("div",{className:"rounded-md border border-gray-100 p-4",children:[(0,t.jsx)(r.Text,{className:"text-sm font-medium text-gray-800",children:"Search tools"}),0===h.length?(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-500",children:"No restriction — all configured search tools are allowed for this team."}):(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-700",children:h.join(", ")})]})]});return"card"===a?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${s}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Object Permissions"}),(0,t.jsx)(r.Text,{className:"text-xs text-gray-500",children:"Access control for Vector Stores and MCP Servers"})]})}),f]}):(0,t.jsxs)("div",{className:`${s}`,children:[(0,t.jsx)(r.Text,{className:"font-medium text-gray-900 mb-3",children:"Object Permissions"}),f]})}],384767)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03zxkn.2-qj65.js b/litellm/proxy/_experimental/out/_next/static/chunks/03zxkn.2-qj65.js new file mode 100644 index 00000000000..b275472205d --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03zxkn.2-qj65.js @@ -0,0 +1,31 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,389543,e=>{"use strict";var t=e.i(843476),r=e.i(271645),l=e.i(304967),a=e.i(269200),s=e.i(427612),n=e.i(496020),i=e.i(64848),o=e.i(977572),d=e.i(942232),c=e.i(599724),u=e.i(994388),g=e.i(752978),m=e.i(793130),p=e.i(404206),f=e.i(723731),h=e.i(653824),y=e.i(881073),x=e.i(197647),b=e.i(602869),_=e.i(28651),j=e.i(68155);e.i(622826);var w=e.i(112179),C=e.i(464571),S=e.i(727749),k=e.i(158392);let v=({accessToken:e,userRole:l,userID:a})=>{let[s,n]=(0,r.useState)({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),[i,o]=(0,r.useState)([]),[d,c]=(0,r.useState)({}),[u,g]=(0,r.useState)({});(0,r.useEffect)(()=>{e&&l&&a&&((0,b.getCallbacksCall)(e,a,l).then(e=>{let t=e.router_settings;"model_group_retry_policy"in t&&delete t.model_group_retry_policy;let r=t.routing_strategy||null;n(e=>({...e,routerSettings:t,selectedStrategy:r}))}),(0,b.getRouterSettingsCall)(e).then(e=>{if(e.fields){let t={};e.fields.forEach(e=>{t[e.field_name]={ui_field_name:e.ui_field_name,field_description:e.field_description,options:e.options,link:e.link}}),c(t);let r=e.fields.find(e=>"routing_strategy"===e.field_name);r?.options&&o(r.options),e.routing_strategy_descriptions&&g(e.routing_strategy_descriptions);let l=e.fields.find(e=>"enable_tag_filtering"===e.field_name);l?.field_value!==null&&l?.field_value!==void 0&&n(e=>({...e,enableTagFiltering:l.field_value}))}}))},[e,l,a]);let m=async()=>{if(!e)return;let t=s.routerSettings,r=new Set(["allowed_fails","cooldown_time","num_retries","timeout","retry_after"]),l=new Set(["model_group_alias"]),a=new Set(["retry_policy","model_group_retry_policy","routing_groups"]),n=Object.fromEntries(Object.entries({...t,enable_tag_filtering:s.enableTagFiltering}).map(([e,t])=>{if(a.has(e))return null;if("routing_strategy_args"!==e&&"routing_strategy"!==e&&"enable_tag_filtering"!==e){let a=document.querySelector(`input[name="${e}"]`),s=((e,t,a)=>{if(void 0===t)return a;let s=t.trim();if("null"===s.toLowerCase())return null;if(r.has(e)){let e=Number(s);return Number.isNaN(e)?a:e}if(l.has(e)){if(""===s)return null;try{return JSON.parse(s)}catch{return a}}return"true"===s.toLowerCase()||"false"!==s.toLowerCase()&&s})(e,a?.value,t);return[e,s]}if("routing_strategy"===e)return[e,s.selectedStrategy];if("enable_tag_filtering"===e)return[e,s.enableTagFiltering];if("routing_strategy_args"===e&&"latency-based-routing"===s.selectedStrategy){let e={},t=document.querySelector('input[name="lowest_latency_buffer"]'),r=document.querySelector('input[name="ttl"]');return t?.value&&(e.lowest_latency_buffer=Number(t.value)),r?.value&&(e.ttl=Number(r.value)),["routing_strategy_args",e]}return null}).filter(e=>null!=e));try{await (0,b.setCallbacksCall)(e,{router_settings:n}),S.default.success("router settings updated successfully")}catch(e){S.default.fromBackend("Failed to update router settings: "+e)}};return e?(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsx)(k.default,{value:s,onChange:n,routerFieldsMetadata:d,availableRoutingStrategies:i,routingStrategyDescriptions:u}),(0,t.jsxs)("div",{className:"border-t border-gray-200 pt-6 flex justify-end gap-3",children:[(0,t.jsx)(C.Button,{onClick:()=>window.location.reload(),children:"Reset"}),(0,t.jsx)(C.Button,{type:"primary",onClick:m,children:"Save Changes"})]})]}):null};e.i(247167);var T=e.i(368670);let N=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14 5l7 7m0 0l-7 7m7-7H3"}))});var A=e.i(122577),I=e.i(592968),F=e.i(898586),L=e.i(356449),M=e.i(127952),O=e.i(418371),B=e.i(708347),E=e.i(888259),R=e.i(695411),P=e.i(212931);let D=(0,e.i(475254).default)("arrow-right",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]]);function $({open:e,onCancel:r,children:l}){return(0,t.jsx)(P.Modal,{title:(0,t.jsx)("div",{className:"pb-4 border-b border-gray-100",children:(0,t.jsxs)("div",{className:"flex items-center gap-2 text-gray-800",children:[(0,t.jsx)("div",{className:"p-2 bg-indigo-50 rounded-lg",children:(0,t.jsx)(D,{className:"w-5 h-5 text-indigo-600"})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h2",{className:"text-lg font-bold m-0",children:"Configure Model Fallbacks"}),(0,t.jsx)("p",{className:"text-sm text-gray-500 font-normal m-0",children:"Manage multiple fallback chains for different models (up to 5 groups at a time)"})]})]})}),open:e,width:900,footer:null,onCancel:r,maskClosable:!1,className:"top-8",styles:{body:{padding:"24px"},header:{padding:"24px 24px 0 24px",border:"none"}},children:(0,t.jsx)("div",{className:"mt-6",children:l})})}var G=e.i(419470);function H({accessToken:e,value:l=[],onChange:a}){let[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)([]),[d,c]=(0,r.useState)(0),[g,m]=(0,r.useState)(!1),[p,f]=(0,r.useState)([{id:"1",primaryModel:null,fallbackModels:[]}]);(0,r.useEffect)(()=>{s&&(f([{id:"1",primaryModel:null,fallbackModels:[]}]),c(e=>e+1))},[s]),(0,r.useEffect)(()=>{let t=async()=>{try{let t=await (0,R.fetchAvailableModels)(e);o(t)}catch(e){console.error("Error fetching model info for fallbacks:",e)}};s&&t()},[e,s]);let h=Array.from(new Set(i.map(e=>e.model_group))).sort(),y=()=>{n(!1),f([{id:"1",primaryModel:null,fallbackModels:[]}])},x=async()=>{let e=p.filter(e=>!e.primaryModel||0===e.fallbackModels.length);if(e.length>0)return void E.default.error(`Please complete configuration for all groups. ${e.length} group(s) incomplete.`);let t=[...l||[],...p.map(e=>({[e.primaryModel]:e.fallbackModels}))];if(a){m(!0);try{await a(t),S.default.success(`${p.length} fallback configuration(s) added successfully!`),y()}catch(e){console.error("Error saving fallbacks:",e)}finally{m(!1)}}else S.default.fromBackend("onChange callback not provided")};return(0,t.jsxs)("div",{children:[(0,t.jsx)(u.Button,{className:"mx-auto",onClick:()=>n(!0),icon:()=>(0,t.jsx)("span",{className:"mr-1",children:"+"}),children:"Add Fallbacks"}),(0,t.jsxs)($,{open:s,onCancel:y,children:[(0,t.jsx)(G.FallbackSelectionForm,{groups:p,onGroupsChange:f,availableModels:h,maxFallbacks:10,maxGroups:5},d),p.length>0&&(0,t.jsxs)("div",{className:"flex items-center justify-end space-x-3 pt-6 mt-6 border-t border-gray-100",children:[(0,t.jsx)(C.Button,{type:"default",onClick:y,disabled:g,children:"Cancel"}),(0,t.jsx)(C.Button,{type:"default",onClick:x,disabled:0===p.length||g,loading:g,children:g?"Saving Configuration...":"Save All Configurations"})]})]})]})}let K="inline-flex items-center gap-2 px-2.5 py-1 rounded-md border border-gray-200 bg-gray-50 text-sm font-medium text-gray-800 shrink-0";async function U(e,r){console.log=function(){};let l=window.location.origin,a=new L.default.OpenAI({apiKey:r,baseURL:l,dangerouslyAllowBrowser:!0});try{S.default.info("Testing fallback model response...");let r=await a.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});S.default.success((0,t.jsxs)("span",{children:["Test model=",(0,t.jsx)("strong",{children:e}),", received model=",(0,t.jsx)("strong",{children:r.model}),". See"," ",(0,t.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){S.default.fromBackend(`Error occurred while generating model response. Please try again. Error: ${e}`)}}let q=({accessToken:e,userRole:l,userID:c})=>{let[u,m]=(0,r.useState)({}),[p,f]=(0,r.useState)(!1),[h,y]=(0,r.useState)(null),[x,_]=(0,r.useState)(!1),{data:w}=(0,T.useModelCostMap)(),C=e=>null!=w&&"object"==typeof w&&e in w?w[e].litellm_provider??"":"";(0,r.useEffect)(()=>{e&&l&&c&&(0,b.getCallbacksCall)(e,c,l).then(e=>{let t=e.router_settings;"model_group_retry_policy"in t&&delete t.model_group_retry_policy,m(t)})},[e,l,c]);let k=e=>{y(e),_(!0)},v=async()=>{if(!h||!e)return;let t=Object.keys(h)[0];if(!t)return;f(!0);let r=u.fallbacks.map(e=>{let r={...e};return t in r&&Array.isArray(r[t])&&delete r[t],r}).filter(e=>Object.keys(e).length>0),l={...u,fallbacks:r};try{await (0,b.setCallbacksCall)(e,{router_settings:l}),m(l),S.default.success("Router settings updated successfully")}catch(e){S.default.fromBackend("Failed to update router settings: "+e)}finally{f(!1),_(!1),y(null)}};if(!e)return null;let L=async t=>{if(!e)return;let r={...u,fallbacks:t};try{await (0,b.setCallbacksCall)(e,{router_settings:r}),m(r)}catch(t){throw S.default.fromBackend("Failed to update router settings: "+t),e&&l&&c&&(0,b.getCallbacksCall)(e,c,l).then(e=>{let t=e.router_settings;"model_group_retry_policy"in t&&delete t.model_group_retry_policy,m(t)}),t}},E=Array.isArray(u.fallbacks)&&u.fallbacks.length>0,R=(0,B.isProxyAdminRole)(l??"");return(0,t.jsxs)(t.Fragment,{children:[R&&(0,t.jsx)(H,{accessToken:e||"",value:u.fallbacks||[],onChange:L}),E?(0,t.jsxs)(a.Table,{children:[(0,t.jsx)(s.TableHead,{children:(0,t.jsxs)(n.TableRow,{children:[(0,t.jsx)(i.TableHeaderCell,{children:"Model Name"}),(0,t.jsx)(i.TableHeaderCell,{children:"Fallbacks"}),(0,t.jsx)(i.TableHeaderCell,{children:"Actions"})]})}),(0,t.jsx)(d.TableBody,{children:u.fallbacks.map((l,a)=>Object.entries(l).map(([s,i])=>{let d;return(0,t.jsxs)(n.TableRow,{children:[(0,t.jsx)(o.TableCell,{className:"align-top",children:(d=C?.(s)??s,(0,t.jsxs)("span",{className:K,children:[(0,t.jsx)(O.ProviderLogo,{provider:d,className:"w-4 h-4 shrink-0"}),(0,t.jsx)("span",{children:s})]}))}),(0,t.jsx)(o.TableCell,{className:"align-top",children:function(e,l){let a=Array.isArray(e)?e:[];if(0===a.length)return null;let s=({modelName:e})=>{let r=l?.(e)??e;return(0,t.jsxs)("span",{className:K,children:[(0,t.jsx)(O.ProviderLogo,{provider:r,className:"w-4 h-4 shrink-0"}),(0,t.jsx)("span",{children:e})]})};return(0,t.jsxs)("span",{className:"grid grid-cols-[auto_1fr] items-start gap-x-2 w-full min-w-0",children:[(0,t.jsx)("span",{className:"inline-flex items-center justify-center w-8 h-8 shrink-0 self-start text-blue-600","aria-hidden":!0,children:(0,t.jsx)(N,{className:"w-5 h-5 stroke-[2.5]"})}),(0,t.jsx)("span",{className:"flex flex-wrap items-start gap-1 min-w-0",children:a.map((e,l)=>(0,t.jsxs)(r.default.Fragment,{children:[l>0&&(0,t.jsx)(g.Icon,{icon:N,size:"xs",className:"shrink-0 text-gray-400"}),(0,t.jsx)(s,{modelName:e})]},e))})]})}(Array.isArray(i)?i:[],C)}),(0,t.jsx)(o.TableCell,{className:"align-top",children:R&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(I.Tooltip,{title:"Test fallback",children:(0,t.jsx)(g.Icon,{icon:A.PlayIcon,size:"sm",onClick:()=>U(Object.keys(l)[0],e||""),className:"cursor-pointer hover:text-blue-600"})}),(0,t.jsx)(I.Tooltip,{title:"Delete fallback",children:(0,t.jsx)("span",{"data-testid":"delete-fallback-button",role:"button",tabIndex:0,onClick:()=>k(l),onKeyDown:e=>"Enter"===e.key&&k(l),className:"cursor-pointer inline-flex",children:(0,t.jsx)(g.Icon,{icon:j.TrashIcon,size:"sm",className:"hover:text-red-600"})})})]})})]},a.toString()+s)}))})]}):(0,t.jsx)("div",{className:"rounded-lg border border-gray-200 bg-gray-50 px-4 py-6 text-center",children:(0,t.jsx)(F.Typography.Text,{type:"secondary",children:"No fallbacks configured. Add fallbacks to automatically try another model when the primary fails."})}),(0,t.jsx)(M.default,{isOpen:x,title:"Delete Fallback?",message:"Are you sure you want to delete this fallback? This action cannot be undone.",resourceInformationTitle:"Fallback Information",resourceInformation:[{label:"Model Name",value:h?Object.keys(h)[0]:"",code:!0}],onCancel:()=>{_(!1),y(null)},onOk:v,confirmLoading:p})]})};var z=e.i(175712),J=e.i(525720),Q=e.i(311451),Y=e.i(770914),V=e.i(646563),X=e.i(91979),W=e.i(928685),Z=e.i(135214),ee=e.i(954616),et=e.i(266027),er=e.i(912598),el=e.i(243652);let ea=(0,el.createQueryKeys)("routingGroups"),es=async e=>{let t=await (0,b.getRouterSettingsCall)(e),r=t?.current_values??{},l=(Array.isArray(t?.fields)?t.fields:[]).find(e=>e?.field_name==="routing_strategy");return{routingGroups:Array.isArray(r.routing_groups)?r.routing_groups:[],routingStrategy:r.routing_strategy??null,availableStrategies:Array.isArray(l?.options)?l.options:[]}},en=(0,el.createQueryKeys)("routerFields"),ei=async e=>{try{let t=b.proxyBaseUrl?`${b.proxyBaseUrl}/router/fields`:"/router/fields",r=await fetch(t,{method:"GET",headers:{[(0,b.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=e?.error&&(e.error.message||e.error)||e?.message||e?.detail||e?.error||JSON.stringify(e);throw Error(t)}return await r.json()}catch(e){throw console.error("Failed to fetch router fields:",e),e}};var eo=e.i(625901),ed=e.i(592392),ec=e.i(291542),eu=e.i(653496),eg=e.i(262218),em=e.i(539677),ep=e.i(955135),ef=e.i(751904),eh=e.i(245094);let{Text:ey,Paragraph:ex}=F.Typography,eb=e=>{switch(e){case"simple-shuffle":return"Simple Shuffle";case"least-busy":return"Least Busy";case"usage-based-routing":return"Usage Based";case"latency-based-routing":return"Latency Based";default:return e}},e_=e=>e.models[0]??"",ej={backgroundColor:"#111827",color:"#f3f4f6",borderRadius:6,padding:16,fontSize:12,whiteSpace:"pre",overflowX:"auto"},ew=({group:e,baseUrl:l})=>{let a={curl:`curl -X POST '${l}/v1/chat/completions' \\ + -H 'Content-Type: application/json' \\ + -H 'Authorization: Bearer $LITELLM_API_KEY' \\ + -d '{ + "model": "${e_(e)}", + "messages": [{"role": "user", "content": "Hello!"}] + }'`,python:`from openai import OpenAI + +client = OpenAI( + api_key="$LITELLM_API_KEY", + base_url="${l}", +) + +response = client.chat.completions.create( + model="${e_(e)}", + messages=[{"role": "user", "content": "Hello!"}], +) + +print(response)`,javascript:`import OpenAI from "openai"; + +const client = new OpenAI({ + apiKey: process.env.LITELLM_API_KEY, + baseURL: "${l}", +}); + +const response = await client.chat.completions.create({ + model: "${e_(e)}", + messages: [{ role: "user", content: "Hello!" }], +}); + +console.log(response);`},[s,n]=(0,r.useState)("curl"),i=[{key:"curl",label:"cURL"},{key:"python",label:"Python (OpenAI SDK)"},{key:"javascript",label:"JavaScript (OpenAI SDK)"}].map(({key:e,label:r})=>({key:e,label:r,children:(0,t.jsx)(ex,{code:!0,className:"mb-0!",style:ej,children:a[e]})}));return(0,t.jsx)(eu.Tabs,{size:"small",activeKey:s,onChange:e=>n(e),items:i,tabBarExtraContent:(0,t.jsx)(ex,{copyable:{text:a[s],tooltips:["Copy","Copied"]},className:"mb-0!"})})},eC=({groups:e,loading:l,onEdit:a,onDelete:s,proxyBaseUrl:n})=>{let[i,o]=(0,r.useState)([]),d=n&&n.trim()?n:window.location?.origin?window.location.origin:"",c=[{title:"GROUP NAME",dataIndex:"group_name",key:"group_name",render:e=>(0,t.jsx)(ey,{strong:!0,className:"text-blue-600",children:e})},{title:"MODELS",dataIndex:"models",key:"models",render:e=>(0,t.jsx)(J.Flex,{wrap:"wrap",gap:4,children:e.map(e=>(0,t.jsx)(eg.Tag,{children:e},e))})},{title:"STRATEGY",dataIndex:"routing_strategy",key:"routing_strategy",render:e=>(0,t.jsxs)("span",{className:"inline-flex items-center gap-1.5",children:[(0,t.jsx)(em.BranchesOutlined,{className:"text-gray-400"}),(0,t.jsx)(ey,{children:eb(e)})]})},{title:"ACTIONS",key:"actions",width:120,align:"right",render:(e,r)=>(0,t.jsxs)(J.Flex,{justify:"flex-end",align:"center",gap:8,children:[(0,t.jsx)(I.Tooltip,{title:"Edit",children:(0,t.jsx)(C.Button,{type:"text",icon:(0,t.jsx)(ef.EditOutlined,{}),onClick:e=>{e.stopPropagation(),a(r)}})}),(0,t.jsx)(I.Tooltip,{title:"Delete",children:(0,t.jsx)(C.Button,{type:"text",danger:!0,icon:(0,t.jsx)(ep.DeleteOutlined,{}),onClick:e=>{e.stopPropagation(),s(r)}})})]})}];return(0,t.jsx)(ec.Table,{rowKey:"group_name",columns:c,dataSource:e,loading:l,pagination:!1,expandable:{expandedRowKeys:i,onExpandedRowsChange:e=>o([...e]),expandedRowRender:e=>(0,t.jsxs)("div",{className:"bg-gray-50 border border-gray-200 rounded-md p-4 my-2",children:[(0,t.jsxs)(J.Flex,{align:"center",gap:8,className:"mb-2",children:[(0,t.jsx)(eh.CodeOutlined,{className:"text-blue-500"}),(0,t.jsx)(ey,{strong:!0,children:"How routing works for this group"})]}),(0,t.jsxs)(ex,{className:"text-sm text-gray-600 mb-3",children:["Callers request any model in the group by name — LiteLLM picks a deployment behind the scenes using the"," ",(0,t.jsx)(ey,{strong:!0,children:eb(e.routing_strategy)})," strategy."]}),(0,t.jsx)(ew,{group:e,baseUrl:d})]})}})};var eS=e.i(808613),ek=e.i(199133);let{Text:ev,Paragraph:eT}=F.Typography,eN=new Set(["latency-based-routing","usage-based-routing"]),eA=/^[A-Za-z0-9._-]+$/,eI=({open:e,mode:l,initialValue:a,availableStrategies:s,strategyDescriptions:n,modelOptions:i,existingGroupNames:o,onClose:d,onSubmit:c,saving:u})=>{let[g]=eS.Form.useForm(),m=eS.Form.useWatch("routing_strategy",g),p={group_name:a?.group_name??"",models:a?.models??[],routing_strategy:a?.routing_strategy??s[0]??"simple-shuffle",routing_strategy_args:a?.routing_strategy_args?JSON.stringify(a.routing_strategy_args,null,2):""},f=(0,r.useMemo)(()=>new Set(o.filter(e=>e!==a?.group_name).map(e=>e.toLowerCase())),[o,a]),h=async()=>{let e=await g.validateFields(),t=eN.has(String(e.routing_strategy)),r=null;if(t&&e.routing_strategy_args&&e.routing_strategy_args.trim())try{r=JSON.parse(e.routing_strategy_args)}catch{g.setFields([{name:"routing_strategy_args",errors:["Must be valid JSON"]}]);return}await c({group_name:e.group_name.trim(),models:e.models,routing_strategy:e.routing_strategy,routing_strategy_args:r})};return(0,t.jsx)(P.Modal,{title:"create"===l?"Create Routing Group":`Edit ${a?.group_name??""}`,open:e,onCancel:d,onOk:h,okText:"create"===l?"Create Group":"Save Changes",cancelText:"Cancel",confirmLoading:u,destroyOnClose:!0,width:560,children:(0,t.jsxs)(eS.Form,{form:g,layout:"vertical",preserve:!1,initialValues:p,children:[(0,t.jsx)(eS.Form.Item,{label:"Group Name",name:"group_name",rules:[{required:!0,message:"Group name is required"},{max:64,message:"Must be 64 characters or fewer"},{pattern:eA,message:"Only letters, numbers, dot, underscore, and dash are allowed"},{validator:(e,t)=>t&&f.has(t.trim().toLowerCase())?Promise.reject(Error("A group with this name already exists")):Promise.resolve()}],extra:"Use this name as the model in API calls — LiteLLM routes the request to one of the group's models.",children:(0,t.jsx)(Q.Input,{placeholder:"fast-chat",disabled:"edit"===l})}),(0,t.jsx)(eS.Form.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Select at least one model"}],extra:"Models from your model list that this group routes between.",children:(0,t.jsx)(ek.Select,{mode:"multiple",allowClear:!0,placeholder:"Select models",options:i.map(e=>({label:e,value:e})),optionFilterProp:"label"})}),(0,t.jsx)(eS.Form.Item,{label:"Routing Strategy",name:"routing_strategy",rules:[{required:!0,message:"Strategy is required"}],children:(0,t.jsx)(ek.Select,{options:s.map(e=>({label:e,value:e})),placeholder:"Select strategy"})}),m&&n[m]&&(0,t.jsx)(eT,{className:"text-xs text-gray-500 -mt-2 mb-4",children:n[m]}),eN.has(String(m))&&(0,t.jsx)(eS.Form.Item,{label:"Strategy Arguments (JSON)",name:"routing_strategy_args",extra:"latency-based-routing"===m?'Example: { "ttl": 3600, "lowest_latency_buffer": 0 }':'Example: { "ttl": 60 }',children:(0,t.jsx)(Q.Input.TextArea,{rows:4,placeholder:'{ "ttl": 3600 }',className:"font-mono text-xs"})}),(0,t.jsx)(Y.Space,{direction:"vertical",className:"w-full mt-2",children:(0,t.jsx)(ev,{type:"secondary",className:"text-xs",children:"Models not claimed by an explicit group fall through to the proxy's top-level routing strategy."})})]},"edit"===l?`edit-${a?.group_name??""}`:"create")})},{Text:eF}=F.Typography,eL=()=>{let{data:e,isLoading:l,refetch:a,isFetching:s}=(()=>{let{accessToken:e,userId:t,userRole:r}=(0,Z.default)();return(0,et.useQuery)({queryKey:ea.lists(),queryFn:()=>es(e),enabled:!!(e&&t&&r)})})(),{data:n}=(()=>{let{accessToken:e,userId:t,userRole:r}=(0,Z.default)();return(0,et.useQuery)({queryKey:en.detail("fields"),queryFn:async()=>await ei(e),enabled:!!(e&&t&&r)})})(),{data:i}=(0,eo.useModelHub)(),{accessToken:o}=(0,Z.default)(),d=(0,ed.default)(o),c=(()=>{let{accessToken:e}=(0,Z.default)(),t=(0,er.useQueryClient)();return(0,ee.useMutation)({mutationFn:t=>(0,b.setCallbacksCall)(e,{router_settings:{routing_groups:t}}),onSuccess:()=>{t.invalidateQueries({queryKey:ea.lists()})}})})(),[u,g]=(0,r.useState)(""),[m,p]=(0,r.useState)(!1),[f,h]=(0,r.useState)("create"),[y,x]=(0,r.useState)(null),[_,j]=(0,r.useState)(null),w=e?.routingGroups??[],k=(0,r.useMemo)(()=>{let e=u.trim().toLowerCase();return e?w.filter(t=>t.group_name.toLowerCase().includes(e)||t.routing_strategy.toLowerCase().includes(e)||t.models.some(t=>t.toLowerCase().includes(e))):w},[w,u]),v=(0,r.useMemo)(()=>e?.availableStrategies?.length?e.availableStrategies:n?.fields?.find(e=>"routing_strategy"===e.field_name)?.options??[],[e?.availableStrategies,n]),T=n?.routing_strategy_descriptions??{},N=(0,r.useMemo)(()=>Array.from(new Set((i?.data??[]).map(e=>e.model_group).filter(e=>!!e))),[i]),A=async e=>{let t="create"===f?[...w,e]:w.map(t=>t.group_name===y?.group_name?e:t);try{await c.mutateAsync(t),S.default.success("create"===f?`Created routing group "${e.group_name}"`:`Updated routing group "${e.group_name}"`),p(!1)}catch(e){S.default.error(e instanceof Error?e.message:"Failed to save routing group")}},I=async()=>{if(!_)return;let e=w.filter(e=>e.group_name!==_.group_name);try{await c.mutateAsync(e),S.default.success(`Deleted routing group "${_.group_name}"`),j(null)}catch(e){S.default.error(e instanceof Error?e.message:"Failed to delete routing group")}};return(0,t.jsxs)(Y.Space,{direction:"vertical",size:16,className:"w-full",children:[(0,t.jsxs)(z.Card,{bodyStyle:{padding:16},children:[(0,t.jsxs)(J.Flex,{justify:"space-between",align:"center",gap:12,className:"mb-4",children:[(0,t.jsx)(Q.Input,{allowClear:!0,prefix:(0,t.jsx)(W.SearchOutlined,{className:"text-gray-400"}),placeholder:"Search groups...",value:u,onChange:e=>g(e.target.value),className:"max-w-sm"}),(0,t.jsxs)(J.Flex,{align:"center",gap:12,children:[(0,t.jsx)(C.Button,{icon:(0,t.jsx)(X.ReloadOutlined,{}),onClick:()=>a(),loading:s&&!l,children:"Refresh"}),(0,t.jsx)(C.Button,{type:"primary",icon:(0,t.jsx)(V.PlusOutlined,{}),onClick:()=>{h("create"),x(null),p(!0)},children:"Create Group"}),(0,t.jsxs)(eF,{type:"secondary",className:"text-sm whitespace-nowrap",children:["Showing ",k.length," ",1===k.length?"result":"results"]})]})]}),(0,t.jsx)(eC,{groups:k,loading:l,onEdit:e=>{h("edit"),x(e),p(!0)},onDelete:e=>j(e),proxyBaseUrl:d.LITELLM_UI_API_DOC_BASE_URL?.trim()||d.PROXY_BASE_URL||""})]}),(0,t.jsx)(eI,{open:m,mode:f,initialValue:y,availableStrategies:v,strategyDescriptions:T,modelOptions:N,existingGroupNames:w.map(e=>e.group_name),onClose:()=>p(!1),onSubmit:A,saving:c.isPending}),(0,t.jsx)(P.Modal,{open:!!_,title:"Delete routing group?",okText:"Delete",okButtonProps:{danger:!0,loading:c.isPending},cancelText:"Cancel",onOk:I,onCancel:()=>j(null),children:(0,t.jsxs)(eF,{children:["Models in ",(0,t.jsx)(eF,{strong:!0,children:_?.group_name}),"will fall back to the proxy's top-level routing strategy. This cannot be undone."]})})]})},eM=({accessToken:e,userRole:C,userID:S})=>{let[k,T]=(0,r.useState)([]);(0,r.useEffect)(()=>{e&&(0,b.getGeneralSettingsCall)(e).then(e=>{T(e)})},[e]);let N=(e,t)=>{T(k.map(r=>r.field_name===e?{...r,field_value:t}:r))};return e?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)(h.TabGroup,{className:"h-[75vh] w-full",children:[(0,t.jsxs)(y.TabList,{variant:"line",defaultValue:"1",className:"px-8 pt-4",children:[(0,t.jsx)(x.Tab,{value:"1",children:"Loadbalancing"}),(0,t.jsx)(x.Tab,{value:"2",children:"Routing Groups"}),(0,t.jsx)(x.Tab,{value:"3",children:"Fallbacks"}),(0,t.jsx)(x.Tab,{value:"4",children:"General"})]}),(0,t.jsxs)(f.TabPanels,{className:"px-8 py-6",children:[(0,t.jsx)(p.TabPanel,{children:(0,t.jsx)(v,{accessToken:e,userRole:C,userID:S})}),(0,t.jsx)(p.TabPanel,{children:(0,t.jsx)(eL,{})}),(0,t.jsx)(p.TabPanel,{children:(0,t.jsx)(q,{accessToken:e,userRole:C,userID:S})}),(0,t.jsx)(p.TabPanel,{children:(0,t.jsx)(l.Card,{children:(0,t.jsxs)(a.Table,{children:[(0,t.jsx)(s.TableHead,{children:(0,t.jsxs)(n.TableRow,{children:[(0,t.jsx)(i.TableHeaderCell,{children:"Setting"}),(0,t.jsx)(i.TableHeaderCell,{children:"Value"}),(0,t.jsx)(i.TableHeaderCell,{children:"Status"}),(0,t.jsx)(i.TableHeaderCell,{children:"Action"})]})}),(0,t.jsx)(d.TableBody,{children:k.filter(e=>"TypedDictionary"!==e.field_type).map((r,l)=>(0,t.jsxs)(n.TableRow,{children:[(0,t.jsxs)(o.TableCell,{children:[(0,t.jsx)(c.Text,{children:r.field_name}),(0,t.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:r.field_description})]}),(0,t.jsx)(o.TableCell,{children:"Integer"==r.field_type?(0,t.jsx)(_.InputNumber,{step:1,value:r.field_value,onChange:e=>N(r.field_name,e)}):"Boolean"==r.field_type?(0,t.jsx)(m.Switch,{checked:!0===r.field_value||"true"===r.field_value,onChange:e=>N(r.field_name,e)}):"Float"==r.field_type?(0,t.jsx)(_.InputNumber,{min:0,max:1,step:.05,value:r.field_value,onChange:e=>N(r.field_name,e)}):null}),(0,t.jsx)(o.TableCell,{children:!0==r.stored_in_db?(0,t.jsx)(w.StatusBadge,{tone:"success",label:"In DB"}):!1==r.stored_in_db?(0,t.jsx)(w.StatusBadge,{tone:"neutral",label:"In Config"}):(0,t.jsx)(w.StatusBadge,{tone:"neutral",label:"Not Set"})}),(0,t.jsxs)(o.TableCell,{children:[(0,t.jsx)(u.Button,{onClick:()=>((t,r)=>{if(!e)return;let l=k[r].field_value;if(null!=l&&void 0!=l)try{(0,b.updateConfigFieldSetting)(e,t,l);let r=k.map(e=>e.field_name===t?{...e,stored_in_db:!0}:e);T(r)}catch(e){}})(r.field_name,l),children:"Update"}),(0,t.jsx)(g.Icon,{icon:j.TrashIcon,color:"red",onClick:()=>(t=>{if(e)try{(0,b.deleteConfigFieldSetting)(e,t);let r=k.map(e=>e.field_name===t?{...e,stored_in_db:null,field_value:null}:e);T(r)}catch(e){}})(r.field_name),children:"Reset"})]})]},l))})]})})})]})]})}):null};e.s(["default",0,function(){let{accessToken:e,userRole:r,userId:l}=(0,Z.default)();return(0,t.jsx)(eM,{userID:l,userRole:r,accessToken:e})}],389543)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/03~yq9q893hmn.js b/litellm/proxy/_experimental/out/_next/static/chunks/03~yq9q893hmn.js new file mode 100644 index 00000000000..ab422b94a4f --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/03~yq9q893hmn.js @@ -0,0 +1 @@ +!function(){var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function e(t){var e={exports:{}};return t(e,e.exports),e.exports}var r,n,o=function(t){return t&&t.Math===Math&&t},i=o("object"==typeof globalThis&&globalThis)||o("object"==typeof window&&window)||o("object"==typeof self&&self)||o("object"==typeof t&&t)||o("object"==typeof t&&t)||function(){return this}()||Function("return this")(),a=function(t){try{return!!t()}catch(t){return!0}},u=!a(function(){return 7!==Object.defineProperty({},1,{get:function(){return 7}})[1]}),s=!a(function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}),c=Function.prototype.call,f=s?c.bind(c):function(){return c.apply(c,arguments)},l={}.propertyIsEnumerable,h=Object.getOwnPropertyDescriptor,p=h&&!l.call({1:2},1)?function(t){var e=h(this,t);return!!e&&e.enumerable}:l,v={f:p},d=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},g=Function.prototype,y=g.call,m=s&&g.bind.bind(y,y),b=s?m:function(t){return function(){return y.apply(t,arguments)}},w=b({}.toString),S=b("".slice),E=function(t){return S(w(t),8,-1)},O=Object,x=b("".split),R=a(function(){return!O("z").propertyIsEnumerable(0)})?function(t){return"String"===E(t)?x(t,""):O(t)}:O,P=function(t){return null==t},A=TypeError,j=function(t){if(P(t))throw new A("Can't call method on "+t);return t},k=function(t){return R(j(t))},I="object"==typeof document&&document.all,T=void 0===I&&void 0!==I?function(t){return"function"==typeof t||t===I}:function(t){return"function"==typeof t},M=function(t){return"object"==typeof t?null!==t:T(t)},L=function(t,e){return arguments.length<2?T(r=i[t])?r:void 0:i[t]&&i[t][e];var r},U=b({}.isPrototypeOf),N=i.navigator,C=N&&N.userAgent,_=C?String(C):"",F=i.process,B=i.Deno,D=F&&F.versions||B&&B.version,z=D&&D.v8;z&&(n=(r=z.split("."))[0]>0&&r[0]<4?1:+(r[0]+r[1])),!n&&_&&(!(r=_.match(/Edge\/(\d+)/))||r[1]>=74)&&(r=_.match(/Chrome\/(\d+)/))&&(n=+r[1]);var W=n,q=i.String,H=!!Object.getOwnPropertySymbols&&!a(function(){var t=Symbol("symbol detection");return!q(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&W&&W<41}),$=H&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,K=Object,G=$?function(t){return"symbol"==typeof t}:function(t){var e=L("Symbol");return T(e)&&U(e.prototype,K(t))},V=String,Y=function(t){try{return V(t)}catch(t){return"Object"}},X=TypeError,J=function(t){if(T(t))return t;throw new X(Y(t)+" is not a function")},Q=function(t,e){var r=t[e];return P(r)?void 0:J(r)},Z=TypeError,tt=Object.defineProperty,et=function(t,e){try{tt(i,t,{value:e,configurable:!0,writable:!0})}catch(r){i[t]=e}return e},rt=e(function(t){var e="__core-js_shared__",r=t.exports=i[e]||et(e,{});(r.versions||(r.versions=[])).push({version:"3.38.1",mode:"global",copyright:"© 2014-2024 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.38.1/LICENSE",source:"https://github.com/zloirock/core-js"})}),nt=function(t,e){return rt[t]||(rt[t]=e||{})},ot=Object,it=function(t){return ot(j(t))},at=b({}.hasOwnProperty),ut=Object.hasOwn||function(t,e){return at(it(t),e)},st=0,ct=Math.random(),ft=b(1..toString),lt=function(t){return"Symbol("+(void 0===t?"":t)+")_"+ft(++st+ct,36)},ht=i.Symbol,pt=nt("wks"),vt=$?ht.for||ht:ht&&ht.withoutSetter||lt,dt=function(t){return ut(pt,t)||(pt[t]=H&&ut(ht,t)?ht[t]:vt("Symbol."+t)),pt[t]},gt=TypeError,yt=dt("toPrimitive"),mt=function(t,e){if(!M(t)||G(t))return t;var r,n=Q(t,yt);if(n){if(void 0===e&&(e="default"),r=f(n,t,e),!M(r)||G(r))return r;throw new gt("Can't convert object to primitive value")}return void 0===e&&(e="number"),function(t,e){var r,n;if("string"===e&&T(r=t.toString)&&!M(n=f(r,t)))return n;if(T(r=t.valueOf)&&!M(n=f(r,t)))return n;if("string"!==e&&T(r=t.toString)&&!M(n=f(r,t)))return n;throw new Z("Can't convert object to primitive value")}(t,e)},bt=function(t){var e=mt(t,"string");return G(e)?e:e+""},wt=i.document,St=M(wt)&&M(wt.createElement),Et=function(t){return St?wt.createElement(t):{}},Ot=!u&&!a(function(){return 7!==Object.defineProperty(Et("div"),"a",{get:function(){return 7}}).a}),xt=Object.getOwnPropertyDescriptor,Rt={f:u?xt:function(t,e){if(t=k(t),e=bt(e),Ot)try{return xt(t,e)}catch(t){}if(ut(t,e))return d(!f(v.f,t,e),t[e])}},Pt=u&&a(function(){return 42!==Object.defineProperty(function(){},"prototype",{value:42,writable:!1}).prototype}),At=String,jt=TypeError,kt=function(t){if(M(t))return t;throw new jt(At(t)+" is not an object")},It=TypeError,Tt=Object.defineProperty,Mt=Object.getOwnPropertyDescriptor,Lt="enumerable",Ut="configurable",Nt="writable",Ct={f:u?Pt?function(t,e,r){if(kt(t),e=bt(e),kt(r),"function"==typeof t&&"prototype"===e&&"value"in r&&Nt in r&&!r[Nt]){var n=Mt(t,e);n&&n[Nt]&&(t[e]=r.value,r={configurable:Ut in r?r[Ut]:n[Ut],enumerable:Lt in r?r[Lt]:n[Lt],writable:!1})}return Tt(t,e,r)}:Tt:function(t,e,r){if(kt(t),e=bt(e),kt(r),Ot)try{return Tt(t,e,r)}catch(t){}if("get"in r||"set"in r)throw new It("Accessors not supported");return"value"in r&&(t[e]=r.value),t}},_t=u?function(t,e,r){return Ct.f(t,e,d(1,r))}:function(t,e,r){return t[e]=r,t},Ft=Function.prototype,Bt=u&&Object.getOwnPropertyDescriptor,Dt=ut(Ft,"name"),zt={EXISTS:Dt,PROPER:Dt&&"something"===function(){}.name,CONFIGURABLE:Dt&&(!u||u&&Bt(Ft,"name").configurable)},Wt=b(Function.toString);T(rt.inspectSource)||(rt.inspectSource=function(t){return Wt(t)});var qt,Ht,$t,Kt=rt.inspectSource,Gt=i.WeakMap,Vt=T(Gt)&&/native code/.test(String(Gt)),Yt=nt("keys"),Xt=function(t){return Yt[t]||(Yt[t]=lt(t))},Jt={},Qt="Object already initialized",Zt=i.TypeError;if(Vt||rt.state){var te=rt.state||(rt.state=new(0,i.WeakMap));te.get=te.get,te.has=te.has,te.set=te.set,qt=function(t,e){if(te.has(t))throw new Zt(Qt);return e.facade=t,te.set(t,e),e},Ht=function(t){return te.get(t)||{}},$t=function(t){return te.has(t)}}else{var ee=Xt("state");Jt[ee]=!0,qt=function(t,e){if(ut(t,ee))throw new Zt(Qt);return e.facade=t,_t(t,ee,e),e},Ht=function(t){return ut(t,ee)?t[ee]:{}},$t=function(t){return ut(t,ee)}}var re,ne={set:qt,get:Ht,has:$t,enforce:function(t){return $t(t)?Ht(t):qt(t,{})},getterFor:function(t){return function(e){var r;if(!M(e)||(r=Ht(e)).type!==t)throw new Zt("Incompatible receiver, "+t+" required");return r}}},oe=e(function(t){var e=zt.CONFIGURABLE,r=ne.enforce,n=ne.get,o=String,i=Object.defineProperty,s=b("".slice),c=b("".replace),f=b([].join),l=u&&!a(function(){return 8!==i(function(){},"length",{value:8}).length}),h=String(String).split("String"),p=t.exports=function(t,n,a){"Symbol("===s(o(n),0,7)&&(n="["+c(o(n),/^Symbol\(([^)]*)\).*$/,"$1")+"]"),a&&a.getter&&(n="get "+n),a&&a.setter&&(n="set "+n),(!ut(t,"name")||e&&t.name!==n)&&(u?i(t,"name",{value:n,configurable:!0}):t.name=n),l&&a&&ut(a,"arity")&&t.length!==a.arity&&i(t,"length",{value:a.arity});try{a&&ut(a,"constructor")&&a.constructor?u&&i(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var p=r(t);return ut(p,"source")||(p.source=f(h,"string"==typeof n?n:"")),t};Function.prototype.toString=p(function(){return T(this)&&n(this).source||Kt(this)},"toString")}),ie=function(t,e,r,n){n||(n={});var o=n.enumerable,i=void 0!==n.name?n.name:e;if(T(r)&&oe(r,i,n),n.global)o?t[e]=r:et(e,r);else{try{n.unsafe?t[e]&&(o=!0):delete t[e]}catch(t){}o?t[e]=r:Ct.f(t,e,{value:r,enumerable:!1,configurable:!n.nonConfigurable,writable:!n.nonWritable})}return t},ae=Math.ceil,ue=Math.floor,se=Math.trunc||function(t){var e=+t;return(e>0?ue:ae)(e)},ce=function(t){var e=+t;return e!=e||0===e?0:se(e)},fe=Math.max,le=Math.min,he=function(t,e){var r=ce(t);return r<0?fe(r+e,0):le(r,e)},pe=Math.min,ve=function(t){var e=ce(t);return e>0?pe(e,9007199254740991):0},de=function(t){return ve(t.length)},ge=function(t){return function(e,r,n){var o=k(e),i=de(o);if(0===i)return!t&&-1;var a,u=he(n,i);if(t&&r!=r){for(;i>u;)if((a=o[u++])!=a)return!0}else for(;i>u;u++)if((t||u in o)&&o[u]===r)return t||u||0;return!t&&-1}},ye={includes:ge(!0),indexOf:ge(!1)},me=ye.indexOf,be=b([].push),we=function(t,e){var r,n=k(t),o=0,i=[];for(r in n)!ut(Jt,r)&&ut(n,r)&&be(i,r);for(;e.length>o;)ut(n,r=e[o++])&&(~me(i,r)||be(i,r));return i},Se=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Ee=Se.concat("length","prototype"),Oe={f:Object.getOwnPropertyNames||function(t){return we(t,Ee)}},xe={f:Object.getOwnPropertySymbols},Re=b([].concat),Pe=L("Reflect","ownKeys")||function(t){var e=Oe.f(kt(t)),r=xe.f;return r?Re(e,r(t)):e},Ae=function(t,e,r){for(var n=Pe(e),o=Ct.f,i=Rt.f,a=0;aa;)Ct.f(t,r=o[a++],n[r]);return t},Be={f:Fe},De=L("document","documentElement"),ze="prototype",We="script",qe=Xt("IE_PROTO"),He=function(){},$e=function(t){return"<"+We+">"+t+""},Ke=function(t){t.write($e("")),t.close();var e=t.parentWindow.Object;return t=null,e},Ge=function(){try{re=new ActiveXObject("htmlfile")}catch(t){}var t,e,r;Ge="undefined"!=typeof document?document.domain&&re?Ke(re):(e=Et("iframe"),r="java"+We+":",e.style.display="none",De.appendChild(e),e.src=String(r),(t=e.contentWindow.document).open(),t.write($e("document.F=Object")),t.close(),t.F):Ke(re);for(var n=Se.length;n--;)delete Ge[ze][Se[n]];return Ge()};Jt[qe]=!0;var Ve=Object.create||function(t,e){var r;return null!==t?(He[ze]=kt(t),r=new He,He[ze]=null,r[qe]=t):r=Ge(),void 0===e?r:Be.f(r,e)},Ye=Ct.f,Xe=dt("unscopables"),Je=Array.prototype;void 0===Je[Xe]&&Ye(Je,Xe,{configurable:!0,value:Ve(null)});var Qe=function(t){Je[Xe][t]=!0};Ce({target:"Array",proto:!0},{at:function(t){var e=it(this),r=de(e),n=ce(t),o=n>=0?n:r+n;return o<0||o>=r?void 0:e[o]}}),Qe("at");var Ze=function(t,e){return b(i[t].prototype[e])},tr=(Ze("Array","at"),TypeError),er=function(t,e){if(!delete t[e])throw new tr("Cannot delete property "+Y(e)+" of "+Y(t))},rr=Math.min,nr=[].copyWithin||function(t,e){var r=it(this),n=de(r),o=he(t,n),i=he(e,n),a=arguments.length>2?arguments[2]:void 0,u=rr((void 0===a?n:he(a,n))-i,n-o),s=1;for(i0;)i in r?r[o]=r[i]:er(r,o),o+=s,i+=s;return r};Ce({target:"Array",proto:!0},{copyWithin:nr}),Qe("copyWithin"),Ze("Array","copyWithin"),Ce({target:"Array",proto:!0},{fill:function(t){for(var e=it(this),r=de(e),n=arguments.length,o=he(n>1?arguments[1]:void 0,r),i=n>2?arguments[2]:void 0,a=void 0===i?r:he(i,r);a>o;)e[o++]=t;return e}}),Qe("fill"),Ze("Array","fill");var or=function(t){if("Function"===E(t))return b(t)},ir=or(or.bind),ar=function(t,e){return J(t),void 0===e?t:s?ir(t,e):function(){return t.apply(e,arguments)}},ur=Array.isArray||function(t){return"Array"===E(t)},sr={};sr[dt("toStringTag")]="z";var cr="[object z]"===String(sr),fr=dt("toStringTag"),lr=Object,hr="Arguments"===E(function(){return arguments}()),pr=cr?E:function(t){var e,r,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=function(t,e){try{return t[e]}catch(t){}}(e=lr(t),fr))?r:hr?E(e):"Object"===(n=E(e))&&T(e.callee)?"Arguments":n},vr=function(){},dr=L("Reflect","construct"),gr=/^\s*(?:class|function)\b/,yr=b(gr.exec),mr=!gr.test(vr),br=function(t){if(!T(t))return!1;try{return dr(vr,[],t),!0}catch(t){return!1}},wr=function(t){if(!T(t))return!1;switch(pr(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return mr||!!yr(gr,Kt(t))}catch(t){return!0}};wr.sham=!0;var Sr=!dr||a(function(){var t;return br(br.call)||!br(Object)||!br(function(){t=!0})||t})?wr:br,Er=dt("species"),Or=Array,xr=function(t,e){return new(function(t){var e;return ur(t)&&(Sr(e=t.constructor)&&(e===Or||ur(e.prototype))||M(e)&&null===(e=e[Er]))&&(e=void 0),void 0===e?Or:e}(t))(0===e?0:e)},Rr=b([].push),Pr=function(t){var e=1===t,r=2===t,n=3===t,o=4===t,i=6===t,a=7===t,u=5===t||i;return function(s,c,f,l){for(var h,p,v=it(s),d=R(v),g=de(d),y=ar(c,f),m=0,b=l||xr,w=e?b(s,g):r||a?b(s,0):void 0;g>m;m++)if((u||m in d)&&(p=y(h=d[m],m,v),t))if(e)w[m]=p;else if(p)switch(t){case 3:return!0;case 5:return h;case 6:return m;case 2:Rr(w,h)}else switch(t){case 4:return!1;case 7:Rr(w,h)}return i?-1:n||o?o:w}},Ar={forEach:Pr(0),map:Pr(1),filter:Pr(2),some:Pr(3),every:Pr(4),find:Pr(5),findIndex:Pr(6),filterReject:Pr(7)},jr=Ar.find,kr="find",Ir=!0;kr in[]&&Array(1)[kr](function(){Ir=!1}),Ce({target:"Array",proto:!0,forced:Ir},{find:function(t){return jr(this,t,arguments.length>1?arguments[1]:void 0)}}),Qe(kr),Ze("Array","find");var Tr=Ar.findIndex,Mr="findIndex",Lr=!0;Mr in[]&&Array(1)[Mr](function(){Lr=!1}),Ce({target:"Array",proto:!0,forced:Lr},{findIndex:function(t){return Tr(this,t,arguments.length>1?arguments[1]:void 0)}}),Qe(Mr),Ze("Array","findIndex");var Ur=TypeError,Nr=function(t){if(t>9007199254740991)throw Ur("Maximum allowed index exceeded");return t},Cr=function(t,e,r,n,o,i,a,u){for(var s,c,f=o,l=0,h=!!a&&ar(a,u);l0&&ur(s)?(c=de(s),f=Cr(t,e,s,c,f,i-1)-1):(Nr(f+1),t[f]=s),f++),l++;return f},_r=Cr;Ce({target:"Array",proto:!0},{flatMap:function(t){var e,r=it(this),n=de(r);return J(t),(e=xr(r,0)).length=_r(e,r,r,n,0,1,t,arguments.length>1?arguments[1]:void 0),e}}),Qe("flatMap"),Ze("Array","flatMap"),Ce({target:"Array",proto:!0},{flat:function(){var t=arguments.length?arguments[0]:void 0,e=it(this),r=de(e),n=xr(e,0);return n.length=_r(n,e,e,r,0,void 0===t?1:ce(t)),n}}),Qe("flat"),Ze("Array","flat");var Fr,Br,Dr,zr=String,Wr=function(t){if("Symbol"===pr(t))throw new TypeError("Cannot convert a Symbol value to a string");return zr(t)},qr=b("".charAt),Hr=b("".charCodeAt),$r=b("".slice),Kr=function(t){return function(e,r){var n,o,i=Wr(j(e)),a=ce(r),u=i.length;return a<0||a>=u?t?"":void 0:(n=Hr(i,a))<55296||n>56319||a+1===u||(o=Hr(i,a+1))<56320||o>57343?t?qr(i,a):n:t?$r(i,a,a+2):o-56320+(n-55296<<10)+65536}},Gr={codeAt:Kr(!1),charAt:Kr(!0)},Vr=!a(function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}),Yr=Xt("IE_PROTO"),Xr=Object,Jr=Xr.prototype,Qr=Vr?Xr.getPrototypeOf:function(t){var e=it(t);if(ut(e,Yr))return e[Yr];var r=e.constructor;return T(r)&&e instanceof r?r.prototype:e instanceof Xr?Jr:null},Zr=dt("iterator"),tn=!1;[].keys&&("next"in(Dr=[].keys())?(Br=Qr(Qr(Dr)))!==Object.prototype&&(Fr=Br):tn=!0);var en=!M(Fr)||a(function(){var t={};return Fr[Zr].call(t)!==t});en&&(Fr={}),T(Fr[Zr])||ie(Fr,Zr,function(){return this});var rn={IteratorPrototype:Fr,BUGGY_SAFARI_ITERATORS:tn},nn=Ct.f,on=dt("toStringTag"),an=function(t,e,r){t&&!r&&(t=t.prototype),t&&!ut(t,on)&&nn(t,on,{configurable:!0,value:e})},un={},sn=rn.IteratorPrototype,cn=function(){return this},fn=function(t,e,r,n){var o=e+" Iterator";return t.prototype=Ve(sn,{next:d(+!n,r)}),an(t,o,!1),un[o]=cn,t},ln=function(t,e,r){try{return b(J(Object.getOwnPropertyDescriptor(t,e)[r]))}catch(t){}},hn=String,pn=TypeError,vn=function(t){if(function(t){return M(t)||null===t}(t))return t;throw new pn("Can't set "+hn(t)+" as a prototype")},dn=Object.setPrototypeOf||("__proto__"in{}?function(){var t,e=!1,r={};try{(t=ln(Object.prototype,"__proto__","set"))(r,[]),e=r instanceof Array}catch(t){}return function(r,n){return j(r),vn(n),M(r)?(e?t(r,n):r.__proto__=n,r):r}}():void 0),gn=zt.PROPER,yn=zt.CONFIGURABLE,mn=rn.IteratorPrototype,bn=rn.BUGGY_SAFARI_ITERATORS,wn=dt("iterator"),Sn="keys",En="values",On="entries",xn=function(){return this},Rn=function(t,e,r,n,o,i,a){fn(r,e,n);var u,s,c,l=function(t){if(t===o&&g)return g;if(!bn&&t&&t in v)return v[t];switch(t){case Sn:case En:case On:return function(){return new r(this,t)}}return function(){return new r(this)}},h=e+" Iterator",p=!1,v=t.prototype,d=v[wn]||v["@@iterator"]||o&&v[o],g=!bn&&d||l(o),y="Array"===e&&v.entries||d;if(y&&(u=Qr(y.call(new t)))!==Object.prototype&&u.next&&(Qr(u)!==mn&&(dn?dn(u,mn):T(u[wn])||ie(u,wn,xn)),an(u,h,!0)),gn&&o===En&&d&&d.name!==En&&(yn?_t(v,"name",En):(p=!0,g=function(){return f(d,this)})),o)if(s={values:l(En),keys:i?g:l(Sn),entries:l(On)},a)for(c in s)(bn||p||!(c in v))&&ie(v,c,s[c]);else Ce({target:e,proto:!0,forced:bn||p},s);return v[wn]!==g&&ie(v,wn,g,{name:o}),un[e]=g,s},Pn=function(t,e){return{value:t,done:e}},An=Gr.charAt,jn="String Iterator",kn=ne.set,In=ne.getterFor(jn);Rn(String,"String",function(t){kn(this,{type:jn,string:Wr(t),index:0})},function(){var t,e=In(this),r=e.string,n=e.index;return n>=r.length?Pn(void 0,!0):(t=An(r,n),e.index+=t.length,Pn(t,!1))});var Tn=function(t,e,r){var n,o;kt(t);try{if(!(n=Q(t,"return"))){if("throw"===e)throw r;return r}n=f(n,t)}catch(t){o=!0,n=t}if("throw"===e)throw r;if(o)throw n;return kt(n),r},Mn=function(t,e,r,n){try{return n?e(kt(r)[0],r[1]):e(r)}catch(e){Tn(t,"throw",e)}},Ln=dt("iterator"),Un=Array.prototype,Nn=function(t){return void 0!==t&&(un.Array===t||Un[Ln]===t)},Cn=function(t,e,r){u?Ct.f(t,e,d(0,r)):t[e]=r},_n=dt("iterator"),Fn=function(t){if(!P(t))return Q(t,_n)||Q(t,"@@iterator")||un[pr(t)]},Bn=TypeError,Dn=function(t,e){var r=arguments.length<2?Fn(t):e;if(J(r))return kt(f(r,t));throw new Bn(Y(t)+" is not iterable")},zn=Array,Wn=function(t){var e=it(t),r=Sr(this),n=arguments.length,o=n>1?arguments[1]:void 0,i=void 0!==o;i&&(o=ar(o,n>2?arguments[2]:void 0));var a,u,s,c,l,h,p=Fn(e),v=0;if(!p||this===zn&&Nn(p))for(a=de(e),u=r?new this(a):zn(a);a>v;v++)h=i?o(e[v],v):e[v],Cn(u,v,h);else for(u=r?new this:[],l=(c=Dn(e,p)).next;!(s=f(l,c)).done;v++)h=i?Mn(c,o,[s.value,v],!0):s.value,Cn(u,v,h);return u.length=v,u},qn=dt("iterator"),Hn=!1;try{var $n=0,Kn={next:function(){return{done:!!$n++}},return:function(){Hn=!0}};Kn[qn]=function(){return this},Array.from(Kn,function(){throw 2})}catch(t){}var Gn=function(t,e){try{if(!e&&!Hn)return!1}catch(t){return!1}var r=!1;try{var n={};n[qn]=function(){return{next:function(){return{done:r=!0}}}},t(n)}catch(t){}return r},Vn=!Gn(function(t){Array.from(t)});Ce({target:"Array",stat:!0,forced:Vn},{from:Wn});var Yn=i,Xn=ye.includes,Jn=a(function(){return!Array(1).includes()});Ce({target:"Array",proto:!0,forced:Jn},{includes:function(t){return Xn(this,t,arguments.length>1?arguments[1]:void 0)}}),Qe("includes"),Ze("Array","includes");var Qn=Ct.f,Zn="Array Iterator",to=ne.set,eo=ne.getterFor(Zn),ro=Rn(Array,"Array",function(t,e){to(this,{type:Zn,target:k(t),index:0,kind:e})},function(){var t=eo(this),e=t.target,r=t.index++;if(!e||r>=e.length)return t.target=null,Pn(void 0,!0);switch(t.kind){case"keys":return Pn(r,!1);case"values":return Pn(e[r],!1)}return Pn([r,e[r]],!1)},"values"),no=un.Arguments=un.Array;if(Qe("keys"),Qe("values"),Qe("entries"),u&&"values"!==no.name)try{Qn(no,"name",{value:"values"})}catch(t){}cr||ie(Object.prototype,"toString",cr?{}.toString:function(){return"[object "+pr(this)+"]"},{unsafe:!0}),Ze("Array","values");var oo=Array,io=a(function(){function t(){}return!(oo.of.call(t)instanceof t)});Ce({target:"Array",stat:!0,forced:io},{of:function(){for(var t=0,e=arguments.length,r=new(Sr(this)?this:oo)(e);e>t;)Cn(r,t,arguments[t++]);return r.length=e,r}});var ao=dt("hasInstance"),uo=Function.prototype;ao in uo||Ct.f(uo,ao,{value:oe(function(t){if(!T(this)||!M(t))return!1;var e=this.prototype;return M(e)?U(e,t):t instanceof this},ao)}),dt("hasInstance");var so=function(t,e,r){return r.get&&oe(r.get,e,{getter:!0}),r.set&&oe(r.set,e,{setter:!0}),Ct.f(t,e,r)},co=zt.EXISTS,fo=Function.prototype,lo=b(fo.toString),ho=/function\b(?:\s|\/\*[\S\s]*?\*\/|\/\/[^\n\r]*[\n\r]+)*([^\s(/]*)/,po=b(ho.exec);u&&!co&&so(fo,"name",{configurable:!0,get:function(){try{return po(ho,lo(this))[1]}catch(t){return""}}});var vo=b([].slice),go=Oe.f,yo="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],mo={f:function(t){return yo&&"Window"===E(t)?function(t){try{return go(t)}catch(t){return vo(yo)}}(t):go(k(t))}},bo=a(function(){if("function"==typeof ArrayBuffer){var t=new ArrayBuffer(8);Object.isExtensible(t)&&Object.defineProperty(t,"a",{value:8})}}),wo=Object.isExtensible,So=a(function(){wo(1)})||bo?function(t){return!!M(t)&&(!bo||"ArrayBuffer"!==E(t))&&(!wo||wo(t))}:wo,Eo=!a(function(){return Object.isExtensible(Object.preventExtensions({}))}),Oo=e(function(t){var e=Ct.f,r=!1,n=lt("meta"),o=0,i=function(t){e(t,n,{value:{objectID:"O"+o++,weakData:{}}})},a=t.exports={enable:function(){a.enable=function(){},r=!0;var t=Oe.f,e=b([].splice),o={};o[n]=1,t(o).length&&(Oe.f=function(r){for(var o=t(r),i=0,a=o.length;ii;i++)if((u=y(t[i]))&&U(Po,u))return u;return new Ro(!1)}n=Dn(t,o)}for(s=h?t.next:n.next;!(c=f(s,n)).done;){try{u=y(c.value)}catch(t){Tn(n,"throw",t)}if("object"==typeof u&&u&&U(Po,u))return u}return new Ro(!1)},jo=TypeError,ko=function(t,e){if(U(e,t))return t;throw new jo("Incorrect invocation")},Io=function(t,e,r){var n,o;return dn&&T(n=e.constructor)&&n!==r&&M(o=n.prototype)&&o!==r.prototype&&dn(t,o),t},To=function(t,e,r){var n=-1!==t.indexOf("Map"),o=-1!==t.indexOf("Weak"),u=n?"set":"add",s=i[t],c=s&&s.prototype,f=s,l={},h=function(t){var e=b(c[t]);ie(c,t,"add"===t?function(t){return e(this,0===t?0:t),this}:"delete"===t?function(t){return!(o&&!M(t))&&e(this,0===t?0:t)}:"get"===t?function(t){return o&&!M(t)?void 0:e(this,0===t?0:t)}:"has"===t?function(t){return!(o&&!M(t))&&e(this,0===t?0:t)}:function(t,r){return e(this,0===t?0:t,r),this})};if(Ue(t,!T(s)||!(o||c.forEach&&!a(function(){(new s).entries().next()}))))f=r.getConstructor(e,t,n,u),Oo.enable();else if(Ue(t,!0)){var p=new f,v=p[u](o?{}:-0,1)!==p,d=a(function(){p.has(1)}),g=Gn(function(t){new s(t)}),y=!o&&a(function(){for(var t=new s,e=5;e--;)t[u](e,e);return!t.has(-0)});g||((f=e(function(t,e){ko(t,c);var r=Io(new s,t,f);return P(e)||Ao(e,r[u],{that:r,AS_ENTRIES:n}),r})).prototype=c,c.constructor=f),(d||y)&&(h("delete"),h("has"),n&&h("get")),(y||v)&&h(u),o&&c.clear&&delete c.clear}return l[t]=f,Ce({global:!0,constructor:!0,forced:f!==s},l),an(f,t),o||r.setStrong(f,t,n),f},Mo=function(t,e,r){for(var n in e)ie(t,n,e[n],r);return t},Lo=dt("species"),Uo=function(t){var e=L(t);u&&e&&!e[Lo]&&so(e,Lo,{configurable:!0,get:function(){return this}})},No=Oo.fastKey,Co=ne.set,_o=ne.getterFor,Fo={getConstructor:function(t,e,r,n){var o=t(function(t,o){ko(t,i),Co(t,{type:e,index:Ve(null),first:null,last:null,size:0}),u||(t.size=0),P(o)||Ao(o,t[n],{that:t,AS_ENTRIES:r})}),i=o.prototype,a=_o(e),s=function(t,e,r){var n,o,i=a(t),s=c(t,e);return s?s.value=r:(i.last=s={index:o=No(e,!0),key:e,value:r,previous:n=i.last,next:null,removed:!1},i.first||(i.first=s),n&&(n.next=s),u?i.size++:t.size++,"F"!==o&&(i.index[o]=s)),t},c=function(t,e){var r,n=a(t),o=No(e);if("F"!==o)return n.index[o];for(r=n.first;r;r=r.next)if(r.key===e)return r};return Mo(i,{clear:function(){for(var t=a(this),e=t.first;e;)e.removed=!0,e.previous&&(e.previous=e.previous.next=null),e=e.next;t.first=t.last=null,t.index=Ve(null),u?t.size=0:this.size=0},delete:function(t){var e=this,r=a(e),n=c(e,t);if(n){var o=n.next,i=n.previous;delete r.index[n.index],n.removed=!0,i&&(i.next=o),o&&(o.previous=i),r.first===n&&(r.first=o),r.last===n&&(r.last=i),u?r.size--:e.size--}return!!n},forEach:function(t){for(var e,r=a(this),n=ar(t,arguments.length>1?arguments[1]:void 0);e=e?e.next:r.first;)for(n(e.value,e.key,this);e&&e.removed;)e=e.previous},has:function(t){return!!c(this,t)}}),Mo(i,r?{get:function(t){var e=c(this,t);return e&&e.value},set:function(t,e){return s(this,0===t?0:t,e)}}:{add:function(t){return s(this,t=0===t?0:t,t)}}),u&&so(i,"size",{configurable:!0,get:function(){return a(this).size}}),o},setStrong:function(t,e,r){var n=e+" Iterator",o=_o(e),i=_o(n);Rn(t,e,function(t,e){Co(this,{type:n,target:t,state:o(t),kind:e,last:null})},function(){for(var t=i(this),e=t.kind,r=t.last;r&&r.removed;)r=r.previous;return t.target&&(t.last=r=r?r.next:t.state.first)?Pn("keys"===e?r.key:"values"===e?r.value:[r.key,r.value],!1):(t.target=null,Pn(void 0,!0))},r?"entries":"values",!r,!0),Uo(e)}};To("Map",function(t){return function(){return t(this,arguments.length?arguments[0]:void 0)}},Fo);var Bo=Map.prototype,Do={Map:Map,set:b(Bo.set),get:b(Bo.get),has:b(Bo.has),remove:b(Bo.delete),proto:Bo},zo=Do.Map,Wo=Do.has,qo=Do.get,Ho=Do.set,$o=b([].push),Ko=a(function(){return 1!==zo.groupBy("ab",function(t){return t}).get("a").length});Ce({target:"Map",stat:!0,forced:Ko},{groupBy:function(t,e){j(t),J(e);var r=new zo,n=0;return Ao(t,function(t){var o=e(t,n++);Wo(r,o)?$o(qo(r,o),t):Ho(r,o,[t])}),r}});var Go={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0},Vo=Et("span").classList,Yo=Vo&&Vo.constructor&&Vo.constructor.prototype,Xo=Yo===Object.prototype?void 0:Yo,Jo=dt("iterator"),Qo=ro.values,Zo=function(t,e){if(t){if(t[Jo]!==Qo)try{_t(t,Jo,Qo)}catch(e){t[Jo]=Qo}if(an(t,e,!0),Go[e])for(var r in ro)if(t[r]!==ro[r])try{_t(t,r,ro[r])}catch(e){t[r]=ro[r]}}};for(var ti in Go)Zo(i[ti]&&i[ti].prototype,ti);Zo(Xo,"DOMTokenList");var ei=function(t,e,r){return function(n){var o=it(n),i=arguments.length,a=i>1?arguments[1]:void 0,u=void 0!==a,s=u?ar(a,i>2?arguments[2]:void 0):void 0,c=new t,f=0;return Ao(o,function(t){var n=u?s(t,f++):t;r?e(c,kt(n)[0],n[1]):e(c,n)}),c}};Ce({target:"Map",stat:!0,forced:!0},{from:ei(Do.Map,Do.set,!0)});var ri=function(t,e,r){return function(){for(var n=new t,o=arguments.length,i=0;i1?arguments[1]:void 0);return!1!==di(e,function(t,n){if(!r(t,n,e))return!1},!0)}});var gi=Do.Map,yi=Do.set;Ce({target:"Map",proto:!0,real:!0,forced:!0},{filter:function(t){var e=oi(this),r=ar(t,arguments.length>1?arguments[1]:void 0),n=new gi;return di(e,function(t,o){r(t,o,e)&&yi(n,o,t)}),n}}),Ce({target:"Map",proto:!0,real:!0,forced:!0},{find:function(t){var e=oi(this),r=ar(t,arguments.length>1?arguments[1]:void 0),n=di(e,function(t,n){if(r(t,n,e))return{value:t}},!0);return n&&n.value}}),Ce({target:"Map",proto:!0,real:!0,forced:!0},{findKey:function(t){var e=oi(this),r=ar(t,arguments.length>1?arguments[1]:void 0),n=di(e,function(t,n){if(r(t,n,e))return{key:n}},!0);return n&&n.key}}),Ce({target:"Map",proto:!0,real:!0,forced:!0},{includes:function(t){return!0===di(oi(this),function(e){if((r=e)===(n=t)||r!=r&&n!=n)return!0;var r,n},!0)}});var mi=Do.Map;Ce({target:"Map",stat:!0,forced:!0},{keyBy:function(t,e){var r=new(T(this)?this:mi);J(e);var n=J(r.set);return Ao(t,function(t){f(n,r,e(t),t)}),r}}),Ce({target:"Map",proto:!0,real:!0,forced:!0},{keyOf:function(t){var e=di(oi(this),function(e,r){if(e===t)return{key:r}},!0);return e&&e.key}});var bi=Do.Map,wi=Do.set;Ce({target:"Map",proto:!0,real:!0,forced:!0},{mapKeys:function(t){var e=oi(this),r=ar(t,arguments.length>1?arguments[1]:void 0),n=new bi;return di(e,function(t,o){wi(n,r(t,o,e),t)}),n}});var Si=Do.Map,Ei=Do.set;Ce({target:"Map",proto:!0,real:!0,forced:!0},{mapValues:function(t){var e=oi(this),r=ar(t,arguments.length>1?arguments[1]:void 0),n=new Si;return di(e,function(t,o){Ei(n,o,r(t,o,e))}),n}});var Oi=Do.set;Ce({target:"Map",proto:!0,real:!0,arity:1,forced:!0},{merge:function(t){for(var e=oi(this),r=arguments.length,n=0;n1?arguments[1]:void 0);return!0===di(e,function(t,n){if(r(t,n,e))return!0},!0)}});var Ri=TypeError,Pi=Do.get,Ai=Do.has,ji=Do.set;Ce({target:"Map",proto:!0,real:!0,forced:!0},{update:function(t,e){var r=oi(this),n=arguments.length;J(e);var o=Ai(r,t);if(!o&&n<3)throw new Ri("Updating absent value");var i=o?Pi(r,t):J(n>2?arguments[2]:void 0)(t,r);return ji(r,t,e(i,t,r)),r}});var ki=TypeError,Ii=function(t,e){var r,n=kt(this),o=J(n.get),i=J(n.has),a=J(n.set),u=arguments.length>2?arguments[2]:void 0;if(!T(e)&&!T(u))throw new ki("At least one callback required");return f(i,n,t)?(r=f(o,n,t),T(e)&&(r=e(r),f(a,n,t,r))):T(u)&&(r=u(),f(a,n,t,r)),r};Ce({target:"Map",proto:!0,real:!0,forced:!0},{upsert:Ii}),Ce({target:"Map",proto:!0,real:!0,name:"upsert",forced:!0},{updateOrInsert:Ii});var Ti=b(1..valueOf),Mi="\t\n\v\f\r                 \u2028\u2029\ufeff",Li=b("".replace),Ui=RegExp("^["+Mi+"]+"),Ni=RegExp("(^|[^"+Mi+"])["+Mi+"]+$"),Ci=function(t){return function(e){var r=Wr(j(e));return 1&t&&(r=Li(r,Ui,"")),2&t&&(r=Li(r,Ni,"$1")),r}},_i={start:Ci(1),end:Ci(2),trim:Ci(3)},Fi=Oe.f,Bi=Rt.f,Di=Ct.f,zi=_i.trim,Wi="Number",qi=i[Wi],Hi=qi.prototype,$i=i.TypeError,Ki=b("".slice),Gi=b("".charCodeAt),Vi=Ue(Wi,!qi(" 0o1")||!qi("0b1")||qi("+0x1")),Yi=function(t){var e,r=arguments.length<1?0:qi(function(t){var e=mt(t,"number");return"bigint"==typeof e?e:function(t){var e,r,n,o,i,a,u,s,c=mt(t,"number");if(G(c))throw new $i("Cannot convert a Symbol value to a number");if("string"==typeof c&&c.length>2)if(c=zi(c),43===(e=Gi(c,0))||45===e){if(88===(r=Gi(c,2))||120===r)return NaN}else if(48===e){switch(Gi(c,1)){case 66:case 98:n=2,o=49;break;case 79:case 111:n=8,o=55;break;default:return+c}for(a=(i=Ki(c,2)).length,u=0;uo)return NaN;return parseInt(i,n)}return+c}(e)}(t));return U(Hi,e=this)&&a(function(){Ti(e)})?Io(Object(r),this,Yi):r};Yi.prototype=Hi,Vi&&(Hi.constructor=Yi),Ce({global:!0,constructor:!0,wrap:!0,forced:Vi},{Number:Yi}),Vi&&function(t,e){for(var r,n=u?Fi(e):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,isFinite,isInteger,isNaN,isSafeInteger,parseFloat,parseInt,fromString,range".split(","),o=0;n.length>o;o++)ut(e,r=n[o])&&!ut(t,r)&&Di(t,r,Bi(e,r))}(Yn[Wi],qi),Ce({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{EPSILON:Math.pow(2,-52)});var Xi=i.isFinite;Ce({target:"Number",stat:!0},{isFinite:Number.isFinite||function(t){return"number"==typeof t&&Xi(t)}});var Ji=Math.floor,Qi=Number.isInteger||function(t){return!M(t)&&isFinite(t)&&Ji(t)===t};Ce({target:"Number",stat:!0},{isInteger:Qi}),Ce({target:"Number",stat:!0},{isNaN:function(t){return t!=t}});var Zi=Math.abs;Ce({target:"Number",stat:!0},{isSafeInteger:function(t){return Qi(t)&&Zi(t)<=9007199254740991}}),Ce({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{MAX_SAFE_INTEGER:9007199254740991}),Ce({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{MIN_SAFE_INTEGER:-9007199254740991});var ta=_i.trim,ea=b("".charAt),ra=i.parseFloat,na=i.Symbol,oa=na&&na.iterator,ia=1/ra(Mi+"-0")!=-Infinity||oa&&!a(function(){ra(Object(oa))})?function(t){var e=ta(Wr(t)),r=ra(e);return 0===r&&"-"===ea(e,0)?-0:r}:ra;Ce({target:"Number",stat:!0,forced:Number.parseFloat!==ia},{parseFloat:ia});var aa=_i.trim,ua=i.parseInt,sa=i.Symbol,ca=sa&&sa.iterator,fa=/^[+-]?0x/i,la=b(fa.exec),ha=8!==ua(Mi+"08")||22!==ua(Mi+"0x16")||ca&&!a(function(){ua(Object(ca))})?function(t,e){var r=aa(Wr(t));return ua(r,e>>>0||(la(fa,r)?16:10))}:ua;Ce({target:"Number",stat:!0,forced:Number.parseInt!==ha},{parseInt:ha});var pa=b(v.f),va=b([].push),da=u&&a(function(){var t=Object.create(null);return t[2]=2,!pa(t,2)}),ga=function(t){return function(e){for(var r,n=k(e),o=_e(n),i=da&&null===Qr(n),a=o.length,s=0,c=[];a>s;)r=o[s++],u&&!(i?r in n:pa(n,r))||va(c,t?[r,n[r]]:n[r]);return c}},ya={entries:ga(!0),values:ga(!1)},ma=ya.entries;Ce({target:"Object",stat:!0},{entries:function(t){return ma(t)}}),Ce({target:"Object",stat:!0,sham:!u},{getOwnPropertyDescriptors:function(t){for(var e,r,n=k(t),o=Rt.f,i=Pe(n),a={},u=0;i.length>u;)void 0!==(r=o(n,e=i[u++]))&&Cn(a,e,r);return a}});var ba=a(function(){_e(1)});Ce({target:"Object",stat:!0,forced:ba},{keys:function(t){return _e(it(t))}});var wa=Object.is||function(t,e){return t===e?0!==t||1/t==1/e:t!=t&&e!=e};Ce({target:"Object",stat:!0},{is:wa});var Sa=ya.values;Ce({target:"Object",stat:!0},{values:function(t){return Sa(t)}}),Ce({target:"Object",stat:!0},{hasOwn:ut});var Ea=Function.prototype,Oa=Ea.apply,xa=Ea.call,Ra="object"==typeof Reflect&&Reflect.apply||(s?xa.bind(Oa):function(){return xa.apply(Oa,arguments)}),Pa=!a(function(){Reflect.apply(function(){})});Ce({target:"Reflect",stat:!0,forced:Pa},{apply:function(t,e,r){return Ra(J(t),e,kt(r))}});var Aa=Function,ja=b([].concat),ka=b([].join),Ia={},Ta=s?Aa.bind:function(t){var e=J(this),r=e.prototype,n=vo(arguments,1),o=function(){var r=ja(n,vo(arguments));return this instanceof o?function(t,e,r){if(!ut(Ia,e)){for(var n=[],o=0;ob)","g");return"b"!==t.exec("b").groups.a||"bc"!=="b".replace(t,"$c")}),gs=Oe.f,ys=ne.enforce,ms=dt("match"),bs=i.RegExp,ws=bs.prototype,Ss=i.SyntaxError,Es=b(ws.exec),Os=b("".charAt),xs=b("".replace),Rs=b("".indexOf),Ps=b("".slice),As=/^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/,js=/a/g,ks=/a/g,Is=new bs(js)!==js,Ts=cs.MISSED_STICKY,Ms=cs.UNSUPPORTED_Y,Ls=u&&(!Is||Ts||ps||ds||a(function(){return ks[ms]=!1,bs(js)!==js||bs(ks)===ks||"/a/i"!==String(bs(js,"i"))}));if(Ue("RegExp",Ls)){for(var Us=function(t,e){var r,n,o,i,a,u,s=U(ws,this),c=es(t),f=void 0===e,l=[],h=t;if(!s&&c&&f&&t.constructor===Us)return t;if((c||U(ws,t))&&(t=t.source,f&&(e=os(h))),t=void 0===t?"":Wr(t),e=void 0===e?"":Wr(e),h=t,ps&&"dotAll"in js&&(n=!!e&&Rs(e,"s")>-1)&&(e=xs(e,/s/g,"")),r=e,Ts&&"sticky"in js&&(o=!!e&&Rs(e,"y")>-1)&&Ms&&(e=xs(e,/y/g,"")),ds&&(i=function(t){for(var e,r=t.length,n=0,o="",i=[],a=Ve(null),u=!1,s=!1,c=0,f="";n<=r;n++){if("\\"===(e=Os(t,n)))e+=Os(t,++n);else if("]"===e)u=!1;else if(!u)switch(!0){case"["===e:u=!0;break;case"("===e:if(o+=e,"?:"===Ps(t,n+1,n+3))continue;Es(As,Ps(t,n+1))&&(n+=2,s=!0),c++;continue;case">"===e&&s:if(""===f||ut(a,f))throw new Ss("Invalid capture group name");a[f]=!0,i[i.length]=[f,c],s=!1,f="";continue}s?f+=e:o+=e}return[o,i]}(t),t=i[0],l=i[1]),a=Io(bs(t,e),s?this:ws,Us),(n||o||l.length)&&(u=ys(a),n&&(u.dotAll=!0,u.raw=Us(function(t){for(var e,r=t.length,n=0,o="",i=!1;n<=r;n++)"\\"!==(e=Os(t,n))?i||"."!==e?("["===e?i=!0:"]"===e&&(i=!1),o+=e):o+="[\\s\\S]":o+=e+Os(t,++n);return o}(t),r)),o&&(u.sticky=!0),l.length&&(u.groups=l)),t!==h)try{_t(a,"source",""===h?"(?:)":h)}catch(t){}return a},Ns=gs(bs),Cs=0;Ns.length>Cs;)ls(Us,bs,Ns[Cs++]);ws.constructor=Us,Us.prototype=ws,ie(i,"RegExp",Us,{constructor:!0})}Uo("RegExp");var _s=zt.PROPER,Fs="toString",Bs=RegExp.prototype,Ds=Bs[Fs];(a(function(){return"/a/b"!==Ds.call({source:"a",flags:"b"})})||_s&&Ds.name!==Fs)&&ie(Bs,Fs,function(){var t=kt(this);return"/"+Wr(t.source)+"/"+Wr(os(t))},{unsafe:!0});var zs=ne.get,Ws=RegExp.prototype,qs=TypeError;u&&ps&&so(Ws,"dotAll",{configurable:!0,get:function(){if(this!==Ws){if("RegExp"===E(this))return!!zs(this).dotAll;throw new qs("Incompatible receiver, RegExp required")}}});var Hs=ne.get,$s=nt("native-string-replace",String.prototype.replace),Ks=RegExp.prototype.exec,Gs=Ks,Vs=b("".charAt),Ys=b("".indexOf),Xs=b("".replace),Js=b("".slice),Qs=function(){var t=/a/,e=/b*/g;return f(Ks,t,"a"),f(Ks,e,"a"),0!==t.lastIndex||0!==e.lastIndex}(),Zs=cs.BROKEN_CARET,tc=void 0!==/()??/.exec("")[1];(Qs||tc||Zs||ps||ds)&&(Gs=function(t){var e,r,n,o,i,a,u,s=this,c=Hs(s),l=Wr(t),h=c.raw;if(h)return h.lastIndex=s.lastIndex,e=f(Gs,h,l),s.lastIndex=h.lastIndex,e;var p=c.groups,v=Zs&&s.sticky,d=f(rs,s),g=s.source,y=0,m=l;if(v&&(d=Xs(d,"y",""),-1===Ys(d,"g")&&(d+="g"),m=Js(l,s.lastIndex),s.lastIndex>0&&(!s.multiline||s.multiline&&"\n"!==Vs(l,s.lastIndex-1))&&(g="(?: "+g+")",m=" "+m,y++),r=new RegExp("^(?:"+g+")",d)),tc&&(r=new RegExp("^"+g+"$(?!\\s)",d)),Qs&&(n=s.lastIndex),o=f(Ks,v?r:s,m),v?o?(o.input=Js(o.input,y),o[0]=Js(o[0],y),o.index=s.lastIndex,s.lastIndex+=o[0].length):s.lastIndex=0:Qs&&o&&(s.lastIndex=s.global?o.index+o[0].length:n),tc&&o&&o.length>1&&f($s,o[0],r,function(){for(i=1;i]*>)/g,Oc=/\$([$&'`]|\d{1,2})/g,xc=function(t,e,r,n,o,i){var a=r+t.length,u=n.length,s=Oc;return void 0!==o&&(o=it(o),s=Ec),wc(i,s,function(i,s){var c;switch(bc(s,0)){case"$":return"$";case"&":return t;case"`":return Sc(e,0,r);case"'":return Sc(e,a);case"<":c=o[Sc(s,1,-1)];break;default:var f=+s;if(0===f)return i;if(f>u){var l=mc(f/10);return 0===l?i:l<=u?void 0===n[l-1]?bc(s,1):n[l-1]+bc(s,1):i}c=n[f-1]}return void 0===c?"":c})},Rc=dt("replace"),Pc=Math.max,Ac=Math.min,jc=b([].concat),kc=b([].push),Ic=b("".indexOf),Tc=b("".slice),Mc="$0"==="a".replace(/./,"$0"),Lc=!!/./[Rc]&&""===/./[Rc]("a","$0"),Uc=!a(function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")});pc("replace",function(t,e,r){var n=Lc?"$":"$0";return[function(t,r){var n=j(this),o=P(t)?void 0:Q(t,Rc);return o?f(o,t,n,r):f(e,Wr(n),t,r)},function(t,o){var i=kt(this),a=Wr(t);if("string"==typeof o&&-1===Ic(o,n)&&-1===Ic(o,"$<")){var u=r(e,i,a,o);if(u.done)return u.value}var s=T(o);s||(o=Wr(o));var c,f=i.global;f&&(c=i.unicode,i.lastIndex=0);for(var l,h=[];null!==(l=yc(i,a))&&(kc(h,l),f);)""===Wr(l[0])&&(i.lastIndex=dc(a,ve(i.lastIndex),c));for(var p,v="",d=0,g=0;g=d&&(v+=Tc(a,d,b)+y,d=b+m.length)}return v+Tc(a,d)}]},!Uc||!Mc||Lc),pc("search",function(t,e,r){return[function(e){var r=j(this),n=P(e)?void 0:Q(e,t);return n?f(n,e,r):new RegExp(e)[t](Wr(r))},function(t){var n=kt(this),o=Wr(t),i=r(e,n,o);if(i.done)return i.value;var a=n.lastIndex;wa(a,0)||(n.lastIndex=0);var u=yc(n,o);return wa(n.lastIndex,a)||(n.lastIndex=a),null===u?-1:u.index}]});var Nc=dt("species"),Cc=function(t,e){var r,n=kt(t).constructor;return void 0===n||P(r=kt(n)[Nc])?e:La(r)},_c=cs.UNSUPPORTED_Y,Fc=Math.min,Bc=b([].push),Dc=b("".slice),zc=!a(function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var r="ab".split(t);return 2!==r.length||"a"!==r[0]||"b"!==r[1]}),Wc="c"==="abbc".split(/(b)*/)[1]||4!=="test".split(/(?:)/,-1).length||2!=="ab".split(/(?:ab)*/).length||4!==".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length;pc("split",function(t,e,r){var n="0".split(void 0,0).length?function(t,r){return void 0===t&&0===r?[]:f(e,this,t,r)}:e;return[function(e,r){var o=j(this),i=P(e)?void 0:Q(e,t);return i?f(i,e,o,r):f(n,Wr(o),e,r)},function(t,o){var i=kt(this),a=Wr(t);if(!Wc){var u=r(n,i,a,o,n!==e);if(u.done)return u.value}var s=Cc(i,RegExp),c=i.unicode,f=new s(_c?"^(?:"+i.source+")":i,(i.ignoreCase?"i":"")+(i.multiline?"m":"")+(i.unicode?"u":"")+(_c?"g":"y")),l=void 0===o?4294967295:o>>>0;if(0===l)return[];if(0===a.length)return null===yc(f,a)?[a]:[];for(var h=0,p=0,v=[];p0;(n>>>=1)&&(e+=e))1&n&&(r+=e);return r},Kc=b($c),Gc=b("".slice),Vc=Math.ceil,Yc=function(t){return function(e,r,n){var o,i,a=Wr(j(e)),u=ve(r),s=a.length,c=void 0===n?" ":Wr(n);return u<=s||""===c?a:((i=Kc(c,Vc((o=u-s)/c.length))).length>o&&(i=Gc(i,0,o)),t?a+i:i+a)}},Xc={start:Yc(!1),end:Yc(!0)},Jc=Xc.start,Qc=Array,Zc=RegExp.escape,tf=b("".charAt),ef=b("".charCodeAt),rf=b(1.1.toString),nf=b([].join),of=/^[0-9a-z]/i,af=/^[$()*+./?[\\\]^{|}]/,uf=RegExp("^[!\"#%&',\\-:;<=>@`~"+Mi+"]"),sf=b(of.exec),cf={"\t":"t","\n":"n","\v":"v","\f":"f","\r":"r"},ff=function(t){var e=rf(ef(t,0),16);return e.length<3?"\\x"+Jc(e,2,"0"):"\\u"+Jc(e,4,"0")},lf=!Zc||"\\x61b"!==Zc("ab");Ce({target:"RegExp",stat:!0,forced:lf},{escape:function(t){!function(t){if("string"==typeof t)return t;throw new qc("Argument is not a string")}(t);for(var e=t.length,r=Qc(e),n=0;n=56320||n+1>=e||56320!=(64512&ef(t,n+1))?r[n]=ff(o):(r[n]=o,r[++n]=tf(t,n))}}return nf(r,"")}}),To("Set",function(t){return function(){return t(this,arguments.length?arguments[0]:void 0)}},Fo);var hf=Set.prototype,pf={Set:Set,add:b(hf.add),has:b(hf.has),remove:b(hf.delete),proto:hf},vf=pf.has,df=function(t){return vf(t),t},gf=pf.Set,yf=pf.proto,mf=b(yf.forEach),bf=b(yf.keys),wf=bf(new gf).next,Sf=function(t,e,r){return r?ci({iterator:bf(t),next:wf},e):mf(t,e)},Ef=pf.Set,Of=pf.add,xf=function(t){var e=new Ef;return Sf(t,function(t){Of(e,t)}),e},Rf=ln(pf.proto,"size","get")||function(t){return t.size},Pf="Invalid size",Af=RangeError,jf=TypeError,kf=Math.max,If=function(t,e){this.set=t,this.size=kf(e,0),this.has=J(t.has),this.keys=J(t.keys)};If.prototype={getIterator:function(){return{iterator:t=kt(f(this.keys,this.set)),next:t.next,done:!1};var t},includes:function(t){return f(this.has,this.set,t)}};var Tf=function(t){kt(t);var e=+t.size;if(e!=e)throw new jf(Pf);var r=ce(e);if(r<0)throw new Af(Pf);return new If(t,r)},Mf=pf.has,Lf=pf.remove,Uf=function(t){var e=df(this),r=Tf(t),n=xf(e);return Rf(e)<=r.size?Sf(e,function(t){r.includes(t)&&Lf(n,t)}):ci(r.getIterator(),function(t){Mf(e,t)&&Lf(n,t)}),n},Nf=function(t){return{size:t,has:function(){return!1},keys:function(){return{next:function(){return{done:!0}}}}}},Cf=function(t){var e=L("Set");try{(new e)[t](Nf(0));try{return(new e)[t](Nf(-1)),!1}catch(t){return!0}}catch(t){return!1}};Ce({target:"Set",proto:!0,real:!0,forced:!Cf("difference")},{difference:Uf});var _f=pf.Set,Ff=pf.add,Bf=pf.has,Df=function(t){var e=df(this),r=Tf(t),n=new _f;return Rf(e)>r.size?ci(r.getIterator(),function(t){Bf(e,t)&&Ff(n,t)}):Sf(e,function(t){r.includes(t)&&Ff(n,t)}),n},zf=!Cf("intersection")||a(function(){return"3,2"!==String(Array.from(new Set([1,2,3]).intersection(new Set([3,2]))))});Ce({target:"Set",proto:!0,real:!0,forced:zf},{intersection:Df});var Wf=pf.has,qf=function(t){var e=df(this),r=Tf(t);if(Rf(e)<=r.size)return!1!==Sf(e,function(t){if(r.includes(t))return!1},!0);var n=r.getIterator();return!1!==ci(n,function(t){if(Wf(e,t))return Tn(n,"normal",!1)})};Ce({target:"Set",proto:!0,real:!0,forced:!Cf("isDisjointFrom")},{isDisjointFrom:qf});var Hf=function(t){var e=df(this),r=Tf(t);return!(Rf(e)>r.size)&&!1!==Sf(e,function(t){if(!r.includes(t))return!1},!0)};Ce({target:"Set",proto:!0,real:!0,forced:!Cf("isSubsetOf")},{isSubsetOf:Hf});var $f=pf.has,Kf=function(t){var e=df(this),r=Tf(t);if(Rf(e)1?arguments[1]:void 0);return!1!==Sf(e,function(t){if(!r(t,t,e))return!1},!0)}});var el=dt("iterator"),rl=Object,nl=L("Set"),ol=function(t){return function(t){return M(t)&&"number"==typeof t.size&&T(t.has)&&T(t.keys)}(t)?t:function(t){if(P(t))return!1;var e=rl(t);return void 0!==e[el]||"@@iterator"in e||ut(un,pr(e))}(t)?new nl(t):t};Ce({target:"Set",proto:!0,real:!0,forced:!0},{difference:function(t){return f(Uf,this,ol(t))}});var il=pf.Set,al=pf.add;Ce({target:"Set",proto:!0,real:!0,forced:!0},{filter:function(t){var e=df(this),r=ar(t,arguments.length>1?arguments[1]:void 0),n=new il;return Sf(e,function(t){r(t,t,e)&&al(n,t)}),n}}),Ce({target:"Set",proto:!0,real:!0,forced:!0},{find:function(t){var e=df(this),r=ar(t,arguments.length>1?arguments[1]:void 0),n=Sf(e,function(t){if(r(t,t,e))return{value:t}},!0);return n&&n.value}}),Ce({target:"Set",proto:!0,real:!0,forced:!0},{intersection:function(t){return f(Df,this,ol(t))}}),Ce({target:"Set",proto:!0,real:!0,forced:!0},{isDisjointFrom:function(t){return f(qf,this,ol(t))}}),Ce({target:"Set",proto:!0,real:!0,forced:!0},{isSubsetOf:function(t){return f(Hf,this,ol(t))}}),Ce({target:"Set",proto:!0,real:!0,forced:!0},{isSupersetOf:function(t){return f(Kf,this,ol(t))}});var ul=b([].join),sl=b([].push);Ce({target:"Set",proto:!0,real:!0,forced:!0},{join:function(t){var e=df(this),r=void 0===t?",":Wr(t),n=[];return Sf(e,function(t){sl(n,t)}),ul(n,r)}});var cl=pf.Set,fl=pf.add;Ce({target:"Set",proto:!0,real:!0,forced:!0},{map:function(t){var e=df(this),r=ar(t,arguments.length>1?arguments[1]:void 0),n=new cl;return Sf(e,function(t){fl(n,r(t,t,e))}),n}});var ll=TypeError;Ce({target:"Set",proto:!0,real:!0,forced:!0},{reduce:function(t){var e=df(this),r=arguments.length<2,n=r?void 0:arguments[1];if(J(t),Sf(e,function(o){r?(r=!1,n=o):n=t(n,o,o,e)}),r)throw new ll("Reduce of empty set with no initial value");return n}}),Ce({target:"Set",proto:!0,real:!0,forced:!0},{some:function(t){var e=df(this),r=ar(t,arguments.length>1?arguments[1]:void 0);return!0===Sf(e,function(t){if(r(t,t,e))return!0},!0)}}),Ce({target:"Set",proto:!0,real:!0,forced:!0},{symmetricDifference:function(t){return f(Xf,this,ol(t))}}),Ce({target:"Set",proto:!0,real:!0,forced:!0},{union:function(t){return f(Qf,this,ol(t))}});var hl=dt("species"),pl=dt("isConcatSpreadable"),vl=W>=51||!a(function(){var t=[];return t[pl]=!1,t.concat()[0]!==t}),dl=function(t){if(!M(t))return!1;var e=t[pl];return void 0!==e?!!e:ur(t)},gl=!(vl&&(W>=51||!a(function(){var t=[];return(t.constructor={})[hl]=function(){return{foo:1}},1!==t.concat(Boolean).foo})));Ce({target:"Array",proto:!0,arity:1,forced:gl},{concat:function(t){var e,r,n,o,i,a=it(this),u=xr(a,0),s=0;for(e=-1,n=arguments.length;e1?arguments[1]:void 0,n=e.length,o=void 0===r?n:ip(ve(r),n),i=Wr(t);return op(e,o-i.length,o)===i}}),Ze("String","endsWith");var sp=RangeError,cp=String.fromCharCode,fp=String.fromCodePoint,lp=b([].join);Ce({target:"String",stat:!0,arity:1,forced:!!fp&&1!==fp.length},{fromCodePoint:function(t){for(var e,r=[],n=arguments.length,o=0;n>o;){if(e=+arguments[o++],he(e,1114111)!==e)throw new sp(e+" is not a valid code point");r[o]=e<65536?cp(e):cp(55296+((e-=65536)>>10),e%1024+56320)}return lp(r,"")}});var hp=b("".indexOf);Ce({target:"String",proto:!0,forced:!rp("includes")},{includes:function(t){return!!~hp(Wr(j(this)),Wr(tp(t)),arguments.length>1?arguments[1]:void 0)}}),Ze("String","includes"),b(un.String);var pp=/Version\/10(?:\.\d+){1,2}(?: [\w./]+)?(?: Mobile\/\w+)? Safari\//.test(_),vp=Xc.start;Ce({target:"String",proto:!0,forced:pp},{padStart:function(t){return vp(this,t,arguments.length>1?arguments[1]:void 0)}}),Ze("String","padStart");var dp=Xc.end;Ce({target:"String",proto:!0,forced:pp},{padEnd:function(t){return dp(this,t,arguments.length>1?arguments[1]:void 0)}}),Ze("String","padEnd");var gp=b([].push),yp=b([].join);Ce({target:"String",stat:!0},{raw:function(t){var e=k(it(t).raw),r=de(e);if(!r)return"";for(var n=arguments.length,o=[],i=0;;){if(gp(o,Wr(e[i++])),i===r)return yp(o,"");i1?arguments[1]:void 0,e.length)),n=Wr(t);return bp(e,r,r+n.length)===n}}),Ze("String","startsWith");var Op=zt.PROPER,xp=function(t){return a(function(){return!!Mi[t]()||"​…᠎"!=="​…᠎"[t]()||Op&&Mi[t].name!==t})},Rp=_i.start,Pp=xp("trimStart")?function(){return Rp(this)}:"".trimStart;Ce({target:"String",proto:!0,name:"trimStart",forced:"".trimLeft!==Pp},{trimLeft:Pp}),Ce({target:"String",proto:!0,name:"trimStart",forced:"".trimStart!==Pp},{trimStart:Pp}),Ze("String","trimLeft");var Ap=_i.end,jp=xp("trimEnd")?function(){return Ap(this)}:"".trimEnd;Ce({target:"String",proto:!0,name:"trimEnd",forced:"".trimRight!==jp},{trimRight:jp}),Ce({target:"String",proto:!0,name:"trimEnd",forced:"".trimEnd!==jp},{trimEnd:jp}),Ze("String","trimRight");var kp=Object.getOwnPropertyDescriptor,Ip=function(t){if(!u)return i[t];var e=kp(i,t);return e&&e.value},Tp=dt("iterator"),Mp=!a(function(){var t=new URL("b?a=1&b=2&c=3","https://a"),e=t.searchParams,r=new URLSearchParams("a=1&a=2&b=3"),n="";return t.pathname="c%20d",e.forEach(function(t,r){e.delete("b"),n+=r+t}),r.delete("a",2),r.delete("b",void 0),!e.size&&!u||!e.sort||"https://a/c%20d?a=1&c=3"!==t.href||"3"!==e.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!e[Tp]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("https://тест").host||"#%D0%B1"!==new URL("https://a#б").hash||"a1c3"!==n||"x"!==new URL("https://x",void 0).host}),Lp=TypeError,Up=function(t,e){if(t0;)t[o]=t[--o];o!==i++&&(t[o]=n)}else for(var a=Np(r/2),u=Cp(vo(t,0,a),e),s=Cp(vo(t,a),e),c=u.length,f=s.length,l=0,h=0;l0&&0!=(t&r);r>>=1)e++;return e},pv=function(t){var e=null;switch(t.length){case 1:e=t[0];break;case 2:e=(31&t[0])<<6|63&t[1];break;case 3:e=(15&t[0])<<12|(63&t[1])<<6|63&t[2];break;case 4:e=(7&t[0])<<18|(63&t[1])<<12|(63&t[2])<<6|63&t[3]}return e>1114111?null:e},vv=function(t){for(var e=(t=nv(t,cv," ")).length,r="",n=0;ne){r+="%",n++;continue}var i=lv(t,n+1);if(i!=i){r+=o,n++;continue}n+=2;var a=hv(i);if(0===a)o=Jp(i);else{if(1===a||a>4){r+="�",n++;continue}for(var u=[i],s=1;se||"%"!==tv(t,n));){var c=lv(t,n+1);if(c!=c){n+=3;break}if(c>191||c<128)break;rv(u,c),n+=2,s++}if(u.length!==a){r+="�";continue}var f=pv(u);null===f?r+="�":o=Qp(f)}}r+=o,n++}return r},dv=/[!'()~]|%20/g,gv={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"},yv=function(t){return gv[t]},mv=function(t){return nv(Xp(t),dv,yv)},bv=fn(function(t,e){zp(this,{type:Dp,target:Wp(t).entries,index:0,kind:e})},Bp,function(){var t=qp(this),e=t.target,r=t.index++;if(!e||r>=e.length)return t.target=null,Pn(void 0,!0);var n=e[r];switch(t.kind){case"keys":return Pn(n.key,!1);case"values":return Pn(n.value,!1)}return Pn([n.key,n.value],!1)},!0),wv=function(t){this.entries=[],this.url=null,void 0!==t&&(M(t)?this.parseObject(t):this.parseQuery("string"==typeof t?"?"===tv(t,0)?uv(t,1):t:Wr(t)))};wv.prototype={type:Bp,bindURL:function(t){this.url=t,this.update()},parseObject:function(t){var e,r,n,o,i,a,u,s=this.entries,c=Fn(t);if(c)for(r=(e=Dn(t,c)).next;!(n=f(r,e)).done;){if(o=Dn(kt(n.value)),(a=f(i=o.next,o)).done||(u=f(i,o)).done||!f(i,o).done)throw new Yp("Expected sequence with length 2");rv(s,{key:Wr(a.value),value:Wr(u.value)})}else for(var l in t)ut(t,l)&&rv(s,{key:l,value:Wr(t[l])})},parseQuery:function(t){if(t)for(var e,r,n=this.entries,o=av(t,"&"),i=0;i0?arguments[0]:void 0));u||(this.size=t.entries.length)},Ev=Sv.prototype;if(Mo(Ev,{append:function(t,e){var r=Wp(this);Up(arguments.length,2),rv(r.entries,{key:Wr(t),value:Wr(e)}),u||this.length++,r.updateURL()},delete:function(t){for(var e=Wp(this),r=Up(arguments.length,1),n=e.entries,o=Wr(t),i=r<2?void 0:arguments[1],a=void 0===i?i:Wr(i),s=0;se.key?1:-1}),t.updateURL()},forEach:function(t){for(var e,r=Wp(this).entries,n=ar(t,arguments.length>1?arguments[1]:void 0),o=0;o1?Rv(arguments[1]):{})}}),T($p)){var Pv=function(t){return ko(this,Gp),new $p(t,arguments.length>1?Rv(arguments[1]):{})};Gp.constructor=Pv,Pv.prototype=Gp,Ce({global:!0,constructor:!0,dontCallGetSet:!0,forced:!0},{Request:Pv})}}var Av={URLSearchParams:Sv,getState:Wp},jv=URLSearchParams,kv=jv.prototype,Iv=b(kv.append),Tv=b(kv.delete),Mv=b(kv.forEach),Lv=b([].push),Uv=new jv("a=1&a=2&b=3");Uv.delete("a",1),Uv.delete("b",void 0),Uv+""!="a=2"&&ie(kv,"delete",function(t){var e=arguments.length,r=e<2?void 0:arguments[1];if(e&&void 0===r)return Tv(this,t);var n=[];Mv(this,function(t,e){Lv(n,{key:e,value:t})}),Up(e,1);for(var o,i=Wr(t),a=Wr(r),u=0,s=0,c=!1,f=n.length;uo;)for(var s,c=R(arguments[o++]),l=i?$v(_e(c),i(c)):_e(c),h=l.length,p=0;h>p;)s=l[p++],u&&!f(a,c,s)||(r[s]=c[s]);return r}:qv,Gv=2147483647,Vv=/[^\0-\u007E]/,Yv=/[.\u3002\uFF0E\uFF61]/g,Xv="Overflow: input needs wider integers to process",Jv=RangeError,Qv=b(Yv.exec),Zv=Math.floor,td=String.fromCharCode,ed=b("".charCodeAt),rd=b([].join),nd=b([].push),od=b("".replace),id=b("".split),ad=b("".toLowerCase),ud=function(t){return t+22+75*(t<26)},sd=function(t,e,r){var n=0;for(t=r?Zv(t/700):t>>1,t+=Zv(t/e);t>455;)t=Zv(t/35),n+=36;return Zv(n+36*t/(t+38))},cd=function(t){var e=[];t=function(t){for(var e=[],r=0,n=t.length;r=55296&&o<=56319&&r=i&&nZv((Gv-a)/l))throw new Jv(Xv);for(a+=(f-i)*l,i=f,r=0;rGv)throw new Jv(Xv);if(n===i){for(var h=a,p=36;;){var v=p<=u?1:p>=u+26?26:p-u;if(h?@[\\\]^|]/,qd=/[\0\t\n\r #/:<>?@[\\\]^|]/,Hd=/^[\u0000-\u0020]+/,$d=/(^|[^\u0000-\u0020])[\u0000-\u0020]+$/,Kd=/[\t\n\r]/g,Gd=function(t){var e,r,n,o;if("number"==typeof t){for(e=[],r=0;r<4;r++)Td(e,t%256),t=md(t/256);return Ed(e,".")}if("object"==typeof t){for(e="",n=function(t){for(var e=null,r=1,n=null,o=0,i=0;i<8;i++)0!==t[i]?(o>r&&(e=n,r=o),n=null,o=0):(null===n&&(n=i),++o);return o>r?n:e}(t),r=0;r<8;r++)o&&0===t[r]||(o&&(o=!1),n===r?(e+=r?":":"::",o=!0):(e+=Od(t[r],16),r<7&&(e+=":")));return"["+e+"]"}return t},Vd={},Yd=Kv({},Vd,{" ":1,'"':1,"<":1,">":1,"`":1}),Xd=Kv({},Yd,{"#":1,"?":1,"{":1,"}":1}),Jd=Kv({},Xd,{"/":1,":":1,";":1,"=":1,"@":1,"[":1,"\\":1,"]":1,"^":1,"|":1}),Qd=function(t,e){var r=fd(t,0);return r>32&&r<127&&!ut(e,t)?t:encodeURIComponent(t)},Zd={ftp:21,file:null,http:80,https:443,ws:80,wss:443},tg=function(t,e){var r;return 2===t.length&&Sd(Nd,wd(t,0))&&(":"===(r=wd(t,1))||!e&&"|"===r)},eg=function(t){var e;return t.length>1&&tg(kd(t,0,2))&&(2===t.length||"/"===(e=wd(t,2))||"\\"===e||"?"===e||"#"===e)},rg=function(t){return"."===t||"%2e"===Id(t)},ng={},og={},ig={},ag={},ug={},sg={},cg={},fg={},lg={},hg={},pg={},vg={},dg={},gg={},yg={},mg={},bg={},wg={},Sg={},Eg={},Og={},xg=function(t,e,r){var n,o,i,a=Wr(t);if(e){if(o=this.parse(a))throw new gd(o);this.searchParams=null}else{if(void 0!==r&&(n=new xg(r,!0)),o=this.parse(a,null,n))throw new gd(o);(i=vd(new pd)).bindURL(this),this.searchParams=i}};xg.prototype={type:"URL",parse:function(t,e,r){var n,o,i,a,u,s=this,c=e||ng,f=0,l="",h=!1,p=!1,v=!1;for(t=Wr(t),e||(s.scheme="",s.username="",s.password="",s.host=null,s.port=null,s.path=[],s.query=null,s.fragment=null,s.cannotBeABaseURL=!1,t=Pd(t,Hd,""),t=Pd(t,$d,"$1")),t=Pd(t,Kd,""),n=Wn(t);f<=n.length;){switch(o=n[f],c){case ng:if(!o||!Sd(Nd,o)){if(e)return Md;c=ig;continue}l+=Id(o),c=og;break;case og:if(o&&(Sd(Cd,o)||"+"===o||"-"===o||"."===o))l+=Id(o);else{if(":"!==o){if(e)return Md;l="",c=ig,f=0;continue}if(e&&(s.isSpecial()!==ut(Zd,l)||"file"===l&&(s.includesCredentials()||null!==s.port)||"file"===s.scheme&&!s.host))return;if(s.scheme=l,e)return void(s.isSpecial()&&Zd[s.scheme]===s.port&&(s.port=null));l="","file"===s.scheme?c=gg:s.isSpecial()&&r&&r.scheme===s.scheme?c=ag:s.isSpecial()?c=fg:"/"===n[f+1]?(c=ug,f++):(s.cannotBeABaseURL=!0,Rd(s.path,""),c=Sg)}break;case ig:if(!r||r.cannotBeABaseURL&&"#"!==o)return Md;if(r.cannotBeABaseURL&&"#"===o){s.scheme=r.scheme,s.path=vo(r.path),s.query=r.query,s.fragment="",s.cannotBeABaseURL=!0,c=Og;break}c="file"===r.scheme?gg:sg;continue;case ag:if("/"!==o||"/"!==n[f+1]){c=sg;continue}c=lg,f++;break;case ug:if("/"===o){c=hg;break}c=wg;continue;case sg:if(s.scheme=r.scheme,o===Wv)s.username=r.username,s.password=r.password,s.host=r.host,s.port=r.port,s.path=vo(r.path),s.query=r.query;else if("/"===o||"\\"===o&&s.isSpecial())c=cg;else if("?"===o)s.username=r.username,s.password=r.password,s.host=r.host,s.port=r.port,s.path=vo(r.path),s.query="",c=Eg;else{if("#"!==o){s.username=r.username,s.password=r.password,s.host=r.host,s.port=r.port,s.path=vo(r.path),s.path.length--,c=wg;continue}s.username=r.username,s.password=r.password,s.host=r.host,s.port=r.port,s.path=vo(r.path),s.query=r.query,s.fragment="",c=Og}break;case cg:if(!s.isSpecial()||"/"!==o&&"\\"!==o){if("/"!==o){s.username=r.username,s.password=r.password,s.host=r.host,s.port=r.port,c=wg;continue}c=hg}else c=lg;break;case fg:if(c=lg,"/"!==o||"/"!==wd(l,f+1))continue;f++;break;case lg:if("/"!==o&&"\\"!==o){c=hg;continue}break;case hg:if("@"===o){h&&(l="%40"+l),h=!0,i=Wn(l);for(var d=0;d65535)return Ud;s.port=s.isSpecial()&&m===Zd[s.scheme]?null:m,l=""}if(e)return;c=bg;continue}return Ud}l+=o;break;case gg:if(s.scheme="file","/"===o||"\\"===o)c=yg;else{if(!r||"file"!==r.scheme){c=wg;continue}switch(o){case Wv:s.host=r.host,s.path=vo(r.path),s.query=r.query;break;case"?":s.host=r.host,s.path=vo(r.path),s.query="",c=Eg;break;case"#":s.host=r.host,s.path=vo(r.path),s.query=r.query,s.fragment="",c=Og;break;default:eg(Ed(vo(n,f),""))||(s.host=r.host,s.path=vo(r.path),s.shortenPath()),c=wg;continue}}break;case yg:if("/"===o||"\\"===o){c=mg;break}r&&"file"===r.scheme&&!eg(Ed(vo(n,f),""))&&(tg(r.path[0],!0)?Rd(s.path,r.path[0]):s.host=r.host),c=wg;continue;case mg:if(o===Wv||"/"===o||"\\"===o||"?"===o||"#"===o){if(!e&&tg(l))c=wg;else if(""===l){if(s.host="",e)return;c=bg}else{if(a=s.parseHost(l))return a;if("localhost"===s.host&&(s.host=""),e)return;l="",c=bg}continue}l+=o;break;case bg:if(s.isSpecial()){if(c=wg,"/"!==o&&"\\"!==o)continue}else if(e||"?"!==o)if(e||"#"!==o){if(o!==Wv&&(c=wg,"/"!==o))continue}else s.fragment="",c=Og;else s.query="",c=Eg;break;case wg:if(o===Wv||"/"===o||"\\"===o&&s.isSpecial()||!e&&("?"===o||"#"===o)){if(".."===(u=Id(u=l))||"%2e."===u||".%2e"===u||"%2e%2e"===u?(s.shortenPath(),"/"===o||"\\"===o&&s.isSpecial()||Rd(s.path,"")):rg(l)?"/"===o||"\\"===o&&s.isSpecial()||Rd(s.path,""):("file"===s.scheme&&!s.path.length&&tg(l)&&(s.host&&(s.host=""),l=wd(l,0)+":"),Rd(s.path,l)),l="","file"===s.scheme&&(o===Wv||"?"===o||"#"===o))for(;s.path.length>1&&""===s.path[0];)Ad(s.path);"?"===o?(s.query="",c=Eg):"#"===o&&(s.fragment="",c=Og)}else l+=Qd(o,Xd);break;case Sg:"?"===o?(s.query="",c=Eg):"#"===o?(s.fragment="",c=Og):o!==Wv&&(s.path[0]+=Qd(o,Vd));break;case Eg:e||"#"!==o?o!==Wv&&("'"===o&&s.isSpecial()?s.query+="%27":s.query+="#"===o?"%23":Qd(o,Vd)):(s.fragment="",c=Og);break;case Og:o!==Wv&&(s.fragment+=Qd(o,Yd))}f++}},parseHost:function(t){var e,r,n;if("["===wd(t,0)){if("]"!==wd(t,t.length-1))return Ld;if(e=function(t){var e,r,n,o,i,a,u,s=[0,0,0,0,0,0,0,0],c=0,f=null,l=0,h=function(){return wd(t,l)};if(":"===h()){if(":"!==wd(t,1))return;l+=2,f=++c}for(;h();){if(8===c)return;if(":"!==h()){for(e=r=0;r<4&&Sd(zd,h());)e=16*e+yd(h(),16),l++,r++;if("."===h()){if(0===r)return;if(l-=r,c>6)return;for(n=0;h();){if(o=null,n>0){if(!("."===h()&&n<4))return;l++}if(!Sd(_d,h()))return;for(;Sd(_d,h());){if(i=yd(h(),10),null===o)o=i;else{if(0===o)return;o=10*o+i}if(o>255)return;l++}s[c]=256*s[c]+o,2!=++n&&4!==n||c++}if(4!==n)return;break}if(":"===h()){if(l++,!h())return}else if(h())return;s[c++]=e}else{if(null!==f)return;l++,f=++c}}if(null!==f)for(a=c-f,c=7;0!==c&&a>0;)u=s[c],s[c--]=s[f+a-1],s[f+--a]=u;else if(8!==c)return;return s}(kd(t,1,-1)),!e)return Ld;this.host=e}else if(this.isSpecial()){if(t=function(t){var e,r,n=[],o=id(od(ad(t),Yv,"."),".");for(e=0;e4)return t;for(r=[],n=0;n1&&"0"===wd(o,0)&&(i=Sd(Fd,o)?16:8,o=kd(o,8===i?1:2)),""===o)a=0;else{if(!Sd(10===i?Dd:8===i?Bd:zd,o))return t;a=yd(o,i)}Rd(r,a)}for(n=0;n=bd(256,5-e))return null}else if(a>255)return null;for(u=xd(r),n=0;n1?arguments[1]:void 0,n=ld(e,new xg(t,!1,r));u||(e.href=n.serialize(),e.origin=n.getOrigin(),e.protocol=n.getProtocol(),e.username=n.getUsername(),e.password=n.getPassword(),e.host=n.getHost(),e.hostname=n.getHostname(),e.port=n.getPort(),e.pathname=n.getPathname(),e.search=n.getSearch(),e.searchParams=n.getSearchParams(),e.hash=n.getHash())},Pg=Rg.prototype,Ag=function(t,e){return{get:function(){return hd(this)[t]()},set:e&&function(t){return hd(this)[e](t)},configurable:!0,enumerable:!0}};if(u&&(so(Pg,"href",Ag("serialize","setHref")),so(Pg,"origin",Ag("getOrigin")),so(Pg,"protocol",Ag("getProtocol","setProtocol")),so(Pg,"username",Ag("getUsername","setUsername")),so(Pg,"password",Ag("getPassword","setPassword")),so(Pg,"host",Ag("getHost","setHost")),so(Pg,"hostname",Ag("getHostname","setHostname")),so(Pg,"port",Ag("getPort","setPort")),so(Pg,"pathname",Ag("getPathname","setPathname")),so(Pg,"search",Ag("getSearch","setSearch")),so(Pg,"searchParams",Ag("getSearchParams")),so(Pg,"hash",Ag("getHash","setHash"))),ie(Pg,"toJSON",function(){return hd(this).serialize()},{enumerable:!0}),ie(Pg,"toString",function(){return hd(this).serialize()},{enumerable:!0}),dd){var jg=dd.createObjectURL,kg=dd.revokeObjectURL;jg&&ie(Rg,"createObjectURL",ar(jg,dd)),kg&&ie(Rg,"revokeObjectURL",ar(kg,dd))}an(Rg,"URL"),Ce({global:!0,constructor:!0,forced:!Mp,sham:!u},{URL:Rg});var Ig=L("URL"),Tg=Mp&&a(function(){Ig.canParse()}),Mg=a(function(){return 1!==Ig.canParse.length});Ce({target:"URL",stat:!0,forced:!Tg||Mg},{canParse:function(t){var e=Up(arguments.length,1),r=Wr(t),n=e<2||void 0===arguments[1]?void 0:Wr(arguments[1]);try{return!!new Ig(r,n)}catch(t){return!1}}});var Lg=L("URL");Ce({target:"URL",stat:!0,forced:!Mp},{parse:function(t){var e=Up(arguments.length,1),r=Wr(t),n=e<2||void 0===arguments[1]?void 0:Wr(arguments[1]);try{return new Lg(r,n)}catch(t){return null}}}),Ce({target:"URL",proto:!0,enumerable:!0},{toJSON:function(){return f(URL.prototype.toString,this)}});var Ug=WeakMap.prototype,Ng={WeakMap:WeakMap,set:b(Ug.set),get:b(Ug.get),has:b(Ug.has),remove:b(Ug.delete)},Cg=Ng.has,_g=function(t){return Cg(t),t},Fg=Ng.get,Bg=Ng.has,Dg=Ng.set;Ce({target:"WeakMap",proto:!0,real:!0,forced:!0},{emplace:function(t,e){var r,n,o=_g(this);return Bg(o,t)?(r=Fg(o,t),"update"in e&&(r=e.update(r,t,o),Dg(o,t,r)),r):(n=e.insert(t,o),Dg(o,t,n),n)}}),Ce({target:"WeakMap",stat:!0,forced:!0},{from:ei(Ng.WeakMap,Ng.set,!0)}),Ce({target:"WeakMap",stat:!0,forced:!0},{of:ri(Ng.WeakMap,Ng.set,!0)});var zg=Ng.remove;Ce({target:"WeakMap",proto:!0,real:!0,forced:!0},{deleteAll:function(){for(var t,e=_g(this),r=!0,n=0,o=arguments.length;n2&&(n=r,M(o=arguments[2])&&"cause"in o&&_t(n,"cause",o.cause));var s=[];return Ao(t,ny,{that:s}),_t(r,"errors",s),r};dn?dn(oy,ry):Ae(oy,ry,{name:!0});var iy=oy.prototype=Ve(ry.prototype,{constructor:d(1,oy),message:d(1,""),name:d(1,"AggregateError")});Ce({global:!0,constructor:!0,arity:2},{AggregateError:oy});var ay,uy,sy,cy,fy=function(t){return _.slice(0,t.length)===t},ly=fy("Bun/")?"BUN":fy("Cloudflare-Workers")?"CLOUDFLARE":fy("Deno/")?"DENO":fy("Node.js/")?"NODE":i.Bun&&"string"==typeof Bun.version?"BUN":i.Deno&&"object"==typeof Deno.version?"DENO":"process"===E(i.process)?"NODE":i.window&&i.document?"BROWSER":"REST",hy="NODE"===ly,py=/(?:ipad|iphone|ipod).*applewebkit/i.test(_),vy=i.setImmediate,dy=i.clearImmediate,gy=i.process,yy=i.Dispatch,my=i.Function,by=i.MessageChannel,wy=i.String,Sy=0,Ey={},Oy="onreadystatechange";a(function(){ay=i.location});var xy=function(t){if(ut(Ey,t)){var e=Ey[t];delete Ey[t],e()}},Ry=function(t){return function(){xy(t)}},Py=function(t){xy(t.data)},Ay=function(t){i.postMessage(wy(t),ay.protocol+"//"+ay.host)};vy&&dy||(vy=function(t){Up(arguments.length,1);var e=T(t)?t:my(t),r=vo(arguments,1);return Ey[++Sy]=function(){Ra(e,void 0,r)},uy(Sy),Sy},dy=function(t){delete Ey[t]},hy?uy=function(t){gy.nextTick(Ry(t))}:yy&&yy.now?uy=function(t){yy.now(Ry(t))}:by&&!py?(cy=(sy=new by).port2,sy.port1.onmessage=Py,uy=ar(cy.postMessage,cy)):i.addEventListener&&T(i.postMessage)&&!i.importScripts&&ay&&"file:"!==ay.protocol&&!a(Ay)?(uy=Ay,i.addEventListener("message",Py,!1)):uy=Oy in Et("script")?function(t){De.appendChild(Et("script"))[Oy]=function(){De.removeChild(this),xy(t)}}:function(t){setTimeout(Ry(t),0)});var jy={set:vy,clear:dy},ky=function(){this.head=null,this.tail=null};ky.prototype={add:function(t){var e={item:t,next:null},r=this.tail;r?r.next=e:this.head=e,this.tail=e},get:function(){var t=this.head;if(t)return null===(this.head=t.next)&&(this.tail=null),t.item}};var Iy,Ty,My,Ly,Uy,Ny=ky,Cy=/ipad|iphone|ipod/i.test(_)&&"undefined"!=typeof Pebble,_y=/web0s(?!.*chrome)/i.test(_),Fy=jy.set,By=i.MutationObserver||i.WebKitMutationObserver,Dy=i.document,zy=i.process,Wy=i.Promise,qy=Ip("queueMicrotask");if(!qy){var Hy=new Ny,$y=function(){var t,e;for(hy&&(t=zy.domain)&&t.exit();e=Hy.get();)try{e()}catch(t){throw Hy.head&&Iy(),t}t&&t.enter()};py||hy||_y||!By||!Dy?!Cy&&Wy&&Wy.resolve?((Ly=Wy.resolve(void 0)).constructor=Wy,Uy=ar(Ly.then,Ly),Iy=function(){Uy($y)}):hy?Iy=function(){zy.nextTick($y)}:(Fy=ar(Fy,i),Iy=function(){Fy($y)}):(Ty=!0,My=Dy.createTextNode(""),new By($y).observe(My,{characterData:!0}),Iy=function(){My.data=Ty=!Ty}),qy=function(t){Hy.head||Iy(),Hy.add(t)}}var Ky,Gy,Vy,Yy=qy,Xy=function(t){try{return{error:!1,value:t()}}catch(t){return{error:!0,value:t}}},Jy=i.Promise,Qy=dt("species"),Zy=!1,tm=T(i.PromiseRejectionEvent),em=Ue("Promise",function(){var t=Kt(Jy),e=t!==String(Jy);if(!e&&66===W)return!0;if(!W||W<51||!/native code/.test(t)){var r=new Jy(function(t){t(1)}),n=function(t){t(function(){},function(){})};if((r.constructor={})[Qy]=n,!(Zy=r.then(function(){})instanceof n))return!0}return!(e||"BROWSER"!==ly&&"DENO"!==ly||tm)}),rm={CONSTRUCTOR:em,REJECTION_EVENT:tm,SUBCLASSING:Zy},nm=TypeError,om=function(t){var e,r;this.promise=new t(function(t,n){if(void 0!==e||void 0!==r)throw new nm("Bad Promise constructor");e=t,r=n}),this.resolve=J(e),this.reject=J(r)},im={f:function(t){return new om(t)}},am=jy.set,um="Promise",sm=rm.CONSTRUCTOR,cm=rm.REJECTION_EVENT,fm=rm.SUBCLASSING,lm=ne.getterFor(um),hm=ne.set,pm=Jy&&Jy.prototype,vm=Jy,dm=pm,gm=i.TypeError,ym=i.document,mm=i.process,bm=im.f,wm=bm,Sm=!!(ym&&ym.createEvent&&i.dispatchEvent),Em="unhandledrejection",Om=function(t){var e;return!(!M(t)||!T(e=t.then))&&e},xm=function(t,e){var r,n,o,i=e.value,a=1===e.state,u=a?t.ok:t.fail,s=t.resolve,c=t.reject,l=t.domain;try{u?(a||(2===e.rejection&&km(e),e.rejection=1),!0===u?r=i:(l&&l.enter(),r=u(i),l&&(l.exit(),o=!0)),r===t.promise?c(new gm("Promise-chain cycle")):(n=Om(r))?f(n,r,s,c):s(r)):c(i)}catch(t){l&&!o&&l.exit(),c(t)}},Rm=function(t,e){t.notified||(t.notified=!0,Yy(function(){for(var r,n=t.reactions;r=n.get();)xm(r,t);t.notified=!1,e&&!t.rejection&&Am(t)}))},Pm=function(t,e,r){var n,o;Sm?((n=ym.createEvent("Event")).promise=e,n.reason=r,n.initEvent(t,!1,!0),i.dispatchEvent(n)):n={promise:e,reason:r},!cm&&(o=i["on"+t])?o(n):t===Em&&function(t,e){try{1===arguments.length?console.error(t):console.error(t,e)}catch(t){}}("Unhandled promise rejection",r)},Am=function(t){f(am,i,function(){var e,r=t.facade,n=t.value;if(jm(t)&&(e=Xy(function(){hy?mm.emit("unhandledRejection",n,r):Pm(Em,r,n)}),t.rejection=hy||jm(t)?2:1,e.error))throw e.value})},jm=function(t){return 1!==t.rejection&&!t.parent},km=function(t){f(am,i,function(){var e=t.facade;hy?mm.emit("rejectionHandled",e):Pm("rejectionhandled",e,t.value)})},Im=function(t,e,r){return function(n){t(e,n,r)}},Tm=function(t,e,r){t.done||(t.done=!0,r&&(t=r),t.value=e,t.state=2,Rm(t,!0))},Mm=function(t,e,r){if(!t.done){t.done=!0,r&&(t=r);try{if(t.facade===e)throw new gm("Promise can't be resolved itself");var n=Om(e);n?Yy(function(){var r={done:!1};try{f(n,e,Im(Mm,r,t),Im(Tm,r,t))}catch(e){Tm(r,e,t)}}):(t.value=e,t.state=1,Rm(t,!1))}catch(e){Tm({done:!1},e,t)}}};if(sm&&(vm=function(t){ko(this,dm),J(t),f(Ky,this);var e=lm(this);try{t(Im(Mm,e),Im(Tm,e))}catch(t){Tm(e,t)}},(Ky=function(t){hm(this,{type:um,done:!1,notified:!1,parent:!1,reactions:new Ny,rejection:!1,state:0,value:null})}).prototype=ie(dm=vm.prototype,"then",function(t,e){var r=lm(this),n=bm(Cc(this,vm));return r.parent=!0,n.ok=!T(t)||t,n.fail=T(e)&&e,n.domain=hy?mm.domain:void 0,0===r.state?r.reactions.add(n):Yy(function(){xm(n,r)}),n.promise}),Gy=function(){var t=new Ky,e=lm(t);this.promise=t,this.resolve=Im(Mm,e),this.reject=Im(Tm,e)},im.f=bm=function(t){return t===vm||void 0===t?new Gy(t):wm(t)},T(Jy)&&pm!==Object.prototype)){Vy=pm.then,fm||ie(pm,"then",function(t,e){var r=this;return new vm(function(t,e){f(Vy,r,t,e)}).then(t,e)},{unsafe:!0});try{delete pm.constructor}catch(t){}dn&&dn(pm,dm)}Ce({global:!0,constructor:!0,wrap:!0,forced:sm},{Promise:vm}),an(vm,um,!1),Uo(um);var Lm=rm.CONSTRUCTOR||!Gn(function(t){Jy.all(t).then(void 0,function(){})});Ce({target:"Promise",stat:!0,forced:Lm},{all:function(t){var e=this,r=im.f(e),n=r.resolve,o=r.reject,i=Xy(function(){var r=J(e.resolve),i=[],a=0,u=1;Ao(t,function(t){var s=a++,c=!1;u++,f(r,e,t).then(function(t){c||(c=!0,i[s]=t,--u||n(i))},o)}),--u||n(i)});return i.error&&o(i.value),r.promise}});var Um=Jy&&Jy.prototype;if(Ce({target:"Promise",proto:!0,forced:rm.CONSTRUCTOR,real:!0},{catch:function(t){return this.then(void 0,t)}}),T(Jy)){var Nm=L("Promise").prototype.catch;Um.catch!==Nm&&ie(Um,"catch",Nm,{unsafe:!0})}Ce({target:"Promise",stat:!0,forced:Lm},{race:function(t){var e=this,r=im.f(e),n=r.reject,o=Xy(function(){var o=J(e.resolve);Ao(t,function(t){f(o,e,t).then(r.resolve,n)})});return o.error&&n(o.value),r.promise}}),Ce({target:"Promise",stat:!0,forced:rm.CONSTRUCTOR},{reject:function(t){var e=im.f(this);return(0,e.reject)(t),e.promise}});var Cm=function(t,e){if(kt(t),M(e)&&e.constructor===t)return e;var r=im.f(t);return(0,r.resolve)(e),r.promise};Ce({target:"Promise",stat:!0,forced:rm.CONSTRUCTOR},{resolve:function(t){return Cm(this,t)}}),Ce({target:"Promise",stat:!0,forced:Lm},{allSettled:function(t){var e=this,r=im.f(e),n=r.resolve,o=r.reject,i=Xy(function(){var r=J(e.resolve),o=[],i=0,a=1;Ao(t,function(t){var u=i++,s=!1;a++,f(r,e,t).then(function(t){s||(s=!0,o[u]={status:"fulfilled",value:t},--a||n(o))},function(t){s||(s=!0,o[u]={status:"rejected",reason:t},--a||n(o))})}),--a||n(o)});return i.error&&o(i.value),r.promise}});var _m="No one promise resolved";Ce({target:"Promise",stat:!0,forced:Lm},{any:function(t){var e=this,r=L("AggregateError"),n=im.f(e),o=n.resolve,i=n.reject,a=Xy(function(){var n=J(e.resolve),a=[],u=0,s=1,c=!1;Ao(t,function(t){var l=u++,h=!1;s++,f(n,e,t).then(function(t){h||c||(c=!0,o(t))},function(t){h||c||(h=!0,a[l]=t,--s||i(new r(a,_m)))})}),--s||i(new r(a,_m))});return a.error&&i(a.value),n.promise}}),Ce({target:"Promise",stat:!0},{withResolvers:function(){var t=im.f(this);return{promise:t.promise,resolve:t.resolve,reject:t.reject}}});var Fm=Jy&&Jy.prototype,Bm=!!Jy&&a(function(){Fm.finally.call({then:function(){}},function(){})});if(Ce({target:"Promise",proto:!0,real:!0,forced:Bm},{finally:function(t){var e=Cc(this,L("Promise")),r=T(t);return this.then(r?function(r){return Cm(e,t()).then(function(){return r})}:t,r?function(r){return Cm(e,t()).then(function(){throw r})}:t)}}),T(Jy)){var Dm=L("Promise").prototype.finally;Fm.finally!==Dm&&ie(Fm,"finally",Dm,{unsafe:!0})}var zm=i.Promise,Wm=!1,qm=!zm||!zm.try||Xy(function(){zm.try(function(t){Wm=8===t},8)}).error||!Wm;Ce({target:"Promise",stat:!0,forced:qm},{try:function(t){var e=arguments.length>1?vo(arguments,1):[],r=im.f(this),n=Xy(function(){return Ra(J(t),void 0,e)});return(n.error?r.reject:r.resolve)(n.value),r.promise}}),Ze("Promise","finally");var Hm="URLSearchParams"in self,$m="Symbol"in self&&"iterator"in Symbol,Km="FileReader"in self&&"Blob"in self&&function(){try{return new Blob,!0}catch(t){return!1}}(),Gm="FormData"in self,Vm="ArrayBuffer"in self;if(Vm)var Ym=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],Xm=ArrayBuffer.isView||function(t){return t&&Ym.indexOf(Object.prototype.toString.call(t))>-1};function Jm(t){if("string"!=typeof t&&(t=String(t)),/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(t))throw new TypeError("Invalid character in header field name");return t.toLowerCase()}function Qm(t){return"string"!=typeof t&&(t=String(t)),t}function Zm(t){var e={next:function(){var e=t.shift();return{done:void 0===e,value:e}}};return $m&&(e[Symbol.iterator]=function(){return e}),e}function tb(t){this.map={},t instanceof tb?t.forEach(function(t,e){this.append(e,t)},this):Array.isArray(t)?t.forEach(function(t){this.append(t[0],t[1])},this):t&&Object.getOwnPropertyNames(t).forEach(function(e){this.append(e,t[e])},this)}function eb(t){if(t.bodyUsed)return Promise.reject(new TypeError("Already read"));t.bodyUsed=!0}function rb(t){return new Promise(function(e,r){t.onload=function(){e(t.result)},t.onerror=function(){r(t.error)}})}function nb(t){var e=new FileReader,r=rb(e);return e.readAsArrayBuffer(t),r}function ob(t){if(t.slice)return t.slice(0);var e=new Uint8Array(t.byteLength);return e.set(new Uint8Array(t)),e.buffer}function ib(){return this.bodyUsed=!1,this._initBody=function(t){var e;this._bodyInit=t,t?"string"==typeof t?this._bodyText=t:Km&&Blob.prototype.isPrototypeOf(t)?this._bodyBlob=t:Gm&&FormData.prototype.isPrototypeOf(t)?this._bodyFormData=t:Hm&&URLSearchParams.prototype.isPrototypeOf(t)?this._bodyText=t.toString():Vm&&Km&&(e=t)&&DataView.prototype.isPrototypeOf(e)?(this._bodyArrayBuffer=ob(t.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):Vm&&(ArrayBuffer.prototype.isPrototypeOf(t)||Xm(t))?this._bodyArrayBuffer=ob(t):this._bodyText=t=Object.prototype.toString.call(t):this._bodyText="",this.headers.get("content-type")||("string"==typeof t?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):Hm&&URLSearchParams.prototype.isPrototypeOf(t)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},Km&&(this.blob=function(){var t=eb(this);if(t)return t;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?eb(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(nb)}),this.text=function(){var t=eb(this);if(t)return t;if(this._bodyBlob)return function(t){var e=new FileReader,r=rb(e);return e.readAsText(t),r}(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(function(t){for(var e=new Uint8Array(t),r=new Array(e.length),n=0;n-1?e:t}(e.method||this.method||"GET"),this.mode=e.mode||this.mode||null,this.signal=e.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&r)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(r)}function sb(t){var e=new FormData;return t.trim().split("&").forEach(function(t){if(t){var r=t.split("="),n=r.shift().replace(/\+/g," "),o=r.join("=").replace(/\+/g," ");e.append(decodeURIComponent(n),decodeURIComponent(o))}}),e}function cb(t,e){e||(e={}),this.type="default",this.status=void 0===e.status?200:e.status,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in e?e.statusText:"OK",this.headers=new tb(e.headers),this.url=e.url||"",this._initBody(t)}ub.prototype.clone=function(){return new ub(this,{body:this._bodyInit})},ib.call(ub.prototype),ib.call(cb.prototype),cb.prototype.clone=function(){return new cb(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new tb(this.headers),url:this.url})},cb.error=function(){var t=new cb(null,{status:0,statusText:""});return t.type="error",t};var fb=[301,302,303,307,308];cb.redirect=function(t,e){if(-1===fb.indexOf(e))throw new RangeError("Invalid status code");return new cb(null,{status:e,headers:{location:t}})};var lb=self.DOMException;try{new lb}catch(t){(lb=function(t,e){this.message=t,this.name=e;var r=Error(t);this.stack=r.stack}).prototype=Object.create(Error.prototype),lb.prototype.constructor=lb}function hb(t,e){return new Promise(function(r,n){var o=new ub(t,e);if(o.signal&&o.signal.aborted)return n(new lb("Aborted","AbortError"));var i=new XMLHttpRequest;function a(){i.abort()}i.onload=function(){var t,e,n={status:i.status,statusText:i.statusText,headers:(t=i.getAllResponseHeaders()||"",e=new tb,t.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(t){var r=t.split(":"),n=r.shift().trim();if(n){var o=r.join(":").trim();e.append(n,o)}}),e)};n.url="responseURL"in i?i.responseURL:n.headers.get("X-Request-URL"),r(new cb("response"in i?i.response:i.responseText,n))},i.onerror=function(){n(new TypeError("Network request failed"))},i.ontimeout=function(){n(new TypeError("Network request failed"))},i.onabort=function(){n(new lb("Aborted","AbortError"))},i.open(o.method,o.url,!0),"include"===o.credentials?i.withCredentials=!0:"omit"===o.credentials&&(i.withCredentials=!1),"responseType"in i&&Km&&(i.responseType="blob"),o.headers.forEach(function(t,e){i.setRequestHeader(e,t)}),o.signal&&(o.signal.addEventListener("abort",a),i.onreadystatechange=function(){4===i.readyState&&o.signal.removeEventListener("abort",a)}),i.send(void 0===o._bodyInit?null:o._bodyInit)})}hb.polyfill=!0,self.fetch||(self.fetch=hb,self.Headers=tb,self.Request=ub,self.Response=cb);var pb=Object.getOwnPropertySymbols,vb=Object.prototype.hasOwnProperty,db=Object.prototype.propertyIsEnumerable,gb=function(){try{if(!Object.assign)return!1;var t=new String("abc");if(t[5]="de","5"===Object.getOwnPropertyNames(t)[0])return!1;for(var e={},r=0;r<10;r++)e["_"+String.fromCharCode(r)]=r;if("0123456789"!==Object.getOwnPropertyNames(e).map(function(t){return e[t]}).join(""))return!1;var n={};return"abcdefghijklmnopqrst".split("").forEach(function(t){n[t]=t}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},n)).join("")}catch(t){return!1}}()?Object.assign:function(t,e){for(var r,n,o=function(t){if(null==t)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(t)}(t),i=1;i{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let o=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:o[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),n=e=>e?6:5,i=(e,t,r,a,o)=>{clearTimeout(a.current);let n=l(e);t(n),r.current=n,o&&o({current:n})};var s=e.i(480731),d=e.i(444755),c=e.i(673706);let g=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let u={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},b=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},p=(0,c.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:o,needMargin:l,transitionStatus:n})=>{let i=l?r===s.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?a.default.createElement(g,{className:(0,d.tremorTwMerge)(p("icon"),"animate-spin shrink-0",i,m.default,m[n]),style:{transition:"width 150ms"}}):a.default.createElement(o,{className:(0,d.tremorTwMerge)(p("icon"),"shrink-0",t,i)})},f=a.default.forwardRef((e,o)=>{let{icon:g,iconPosition:m=s.HorizontalPositions.Left,size:f=s.Sizes.SM,color:x,variant:C="primary",disabled:k,loading:w=!1,loadingText:v,children:N,tooltip:$,className:y}=e,j=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),T=w||k,B=void 0!==g||w,E=w&&v,O=!(!N&&!E),S=(0,d.tremorTwMerge)(u[f].height,u[f].width),M="light"!==C?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",R=b(C,x),z=("light"!==C?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[f],{tooltipProps:P,getReferenceProps:H}=(0,r.useTooltip)(300),[q,F]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:o,timeout:s,initialEntered:d,mountOnEnter:c,unmountOnExit:g,onStateChange:m}={})=>{let[u,b]=(0,a.useState)(()=>l(d?2:n(c))),p=(0,a.useRef)(u),h=(0,a.useRef)(0),[f,x]="object"==typeof s?[s.enter,s.exit]:[s,s],C=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return n(t)}})(p.current._s,g);e&&i(e,b,p,h,m)},[m,g]);return[u,(0,a.useCallback)(a=>{let l=e=>{switch(i(e,b,p,h,m),e){case 1:f>=0&&(h.current=((...e)=>setTimeout(...e))(C,f));break;case 4:x>=0&&(h.current=((...e)=>setTimeout(...e))(C,x));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},s=p.current.isEnter;"boolean"!=typeof a&&(a=!s),a?s||l(e?+!r:2):s&&l(t?o?3:4:n(g))},[C,m,e,t,r,o,f,x,g]),C]})({timeout:50});return(0,a.useEffect)(()=>{F(w)},[w]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([o,P.refs.setReference]),className:(0,d.tremorTwMerge)(p("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",M,z.paddingX,z.paddingY,z.fontSize,R.textColor,R.bgColor,R.borderColor,R.hoverBorderColor,T?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(b(C,x).hoverTextColor,b(C,x).hoverBgColor,b(C,x).hoverBorderColor),y),disabled:T},H,j),a.default.createElement(r.default,Object.assign({text:$},P)),B&&m!==s.HorizontalPositions.Right?a.default.createElement(h,{loading:w,iconSize:S,iconPosition:m,Icon:g,transitionStatus:q.status,needMargin:O}):null,E||N?a.default.createElement("span",{className:(0,d.tremorTwMerge)(p("text"),"text-tremor-default whitespace-nowrap")},E?v:N):null,B&&m===s.HorizontalPositions.Right?a.default.createElement(h,{loading:w,iconSize:S,iconPosition:m,Icon:g,transitionStatus:q.status,needMargin:O}):null)});f.displayName="Button",e.s(["Button",0,f],994388)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),o=e.i(95779),l=e.i(444755),n=e.i(673706);let i=(0,n.makeClassName)("Card"),s=r.default.forwardRef((e,s)=>{let{decoration:d="",decorationColor:c,children:g,className:m}=e,u=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:s,className:(0,l.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,n.getColorClassNames)(c,o.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},u),g)});s.displayName="Card",e.s(["Card",0,s],304967)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),o=e.i(529681);let l=e=>{let{prefixCls:a,className:o,style:l,size:n,shape:i}=e,s=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),d=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),c=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,s,d,o),style:Object.assign(Object.assign({},c),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),s=e.i(246422),d=e.i(838378);let c=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),g=e=>({height:e,lineHeight:(0,i.unit)(e)}),m=e=>Object.assign({width:e},g(e)),u=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},g(e)),b=e=>Object.assign({width:e},g(e)),p=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},g(e)),f=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:o,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:s,controlHeightLG:d,controlHeightSM:g,gradientFromColor:f,padding:x,marginSM:C,borderRadius:k,titleHeight:w,blockRadius:v,paragraphLiHeight:N,controlHeightXS:$,paragraphMarginTop:y}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:x,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:f},m(s)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},m(d)),[`${r}-sm`]:Object.assign({},m(g))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:w,background:f,borderRadius:v,[`+ ${o}`]:{marginBlockStart:g}},[o]:{padding:0,"> li":{width:"100%",height:N,listStyle:"none",background:f,borderRadius:v,"+ li":{marginBlockStart:$}}},[`${o}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${o} > li`]:{borderRadius:k}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:C,[`+ ${o}`]:{marginBlockStart:y}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},p(e,a,r)),{[`${r}-lg`]:Object.assign({},h(o,i))}),p(e,o,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),p(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},m(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},m(o)),[`${t}${t}-sm`]:Object.assign({},m(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},u(t,i)),[`${a}-lg`]:Object.assign({},u(o,i)),[`${a}-sm`]:Object.assign({},u(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:o,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:o},b(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${o} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),x=e=>{let{prefixCls:a,className:o,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,o),style:l},i)},C=({prefixCls:e,className:a,width:o,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:o},l)});function k(e){return e&&"object"==typeof e?e:{}}let w=e=>{let{prefixCls:o,loading:n,className:i,rootClassName:s,style:d,children:c,avatar:g=!1,title:m=!0,paragraph:u=!0,active:b,round:p}=e,{getPrefixCls:h,direction:w,className:v,style:N}=(0,a.useComponentConfig)("skeleton"),$=h("skeleton",o),[y,j,T]=f($);if(n||!("loading"in e)){let e,a,o=!!g,n=!!m,c=!!u;if(o){let r=Object.assign(Object.assign({prefixCls:`${$}-avatar`},n&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),k(g));e=t.createElement("div",{className:`${$}-header`},t.createElement(l,Object.assign({},r)))}if(n||c){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${$}-title`},!o&&c?{width:"38%"}:o&&c?{width:"50%"}:{}),k(m));e=t.createElement(C,Object.assign({},r))}if(c){let e,a=Object.assign(Object.assign({prefixCls:`${$}-paragraph`},(e={},o&&n||(e.width="61%"),!o&&n?e.rows=3:e.rows=2,e)),k(u));r=t.createElement(x,Object.assign({},a))}a=t.createElement("div",{className:`${$}-content`},e,r)}let h=(0,r.default)($,{[`${$}-with-avatar`]:o,[`${$}-active`]:b,[`${$}-rtl`]:"rtl"===w,[`${$}-round`]:p},v,i,s,j,T);return y(t.createElement("div",{className:h,style:Object.assign(Object.assign({},N),d)},e,a))}return null!=c?c:null};w.Button=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c=!1,size:g="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),u=m("skeleton",n),[b,p,h]=f(u),x=(0,o.default)(e,["prefixCls"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d,[`${u}-block`]:c},i,s,p,h);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-button`,size:g},x))))},w.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,shape:c="circle",size:g="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),u=m("skeleton",n),[b,p,h]=f(u),x=(0,o.default)(e,["prefixCls","className"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d},i,s,p,h);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-avatar`,shape:c,size:g},x))))},w.Input=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c,size:g="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),u=m("skeleton",n),[b,p,h]=f(u),x=(0,o.default)(e,["prefixCls"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d,[`${u}-block`]:c},i,s,p,h);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-input`,size:g},x))))},w.Image=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),c=d("skeleton",o),[g,m,u]=f(c),b=(0,r.default)(c,`${c}-element`,{[`${c}-active`]:s},l,n,m,u);return g(t.createElement("div",{className:b},t.createElement("div",{className:(0,r.default)(`${c}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},w.Node=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s,children:d}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),g=c("skeleton",o),[m,u,b]=f(g),p=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:s},u,l,n,b);return m(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${g}-image`,l),style:i},d)))},e.s(["default",0,w],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let o=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(o),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let o={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",o);let l=e<0?"-":"",n=Math.abs(e),i=n,s="";return n>=1e6?(i=n/1e6,s="M"):n>=1e3&&(i=n/1e3,s="K"),`${l}${i.toLocaleString("en-US",o)}${s}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return o(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),o(e,r)}},o=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let o=document.execCommand("copy");if(document.body.removeChild(a),o)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),o=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(o.TooltipProvider,{delay:300,children:(0,t.jsxs)(o.Tooltip,{children:[(0,t.jsx)(o.TooltipTrigger,{render:r}),(0,t.jsx)(o.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:o,tooltip:i,dataTestId:s}){let d=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":s,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:o});return i?(0,t.jsx)(l,{content:i,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],o=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,s,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`,`${s}, ${d} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${a[c.getMonth()]} ${c.getDate()}, ${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:o,copyable:d=!1,truncate:c=!0,fallback:g="-",tooltip:m,disabled:u=!1,dataTestId:b,className:p}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:g});let h=!!o&&!u,f=(0,n.cn)(s[a].base,h&&s[a].clickable,c&&"block max-w-[15ch] truncate",u&&"opacity-50",p),x=h?(0,r.jsx)("button",{type:"button",className:f,"data-testid":b,onClick:()=>o(e),children:e}):(0,r.jsx)("span",{className:f,"data-testid":b,children:e}),C=(0,r.jsx)(t.CellTooltip,{content:m??e,trigger:x});return d?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[C,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):C}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:o=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?o?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(o("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),n))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},s),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",i)},s),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},s),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},s),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("row"),i)},s),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),o=e.i(480731),l=e.i(95779),n=e.i(444755),i=e.i(673706);let s={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},d={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},c=(0,i.makeClassName)("Badge"),g=r.default.forwardRef((e,g)=>{let{color:m,icon:u,size:b=o.Sizes.SM,tooltip:p,className:h,children:f}=e,x=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),C=u||null,{tooltipProps:k,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([g,k.refs.setReference]),className:(0,n.tremorTwMerge)(c("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",m?(0,n.tremorTwMerge)((0,i.getColorClassNames)(m,l.colorPalette.background).bgColor,(0,i.getColorClassNames)(m,l.colorPalette.iconText).textColor,(0,i.getColorClassNames)(m,l.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),s[b].paddingX,s[b].paddingY,s[b].fontSize,h)},w,x),r.default.createElement(a.default,Object.assign({text:p},k)),C?r.default.createElement(C,{className:(0,n.tremorTwMerge)(c("icon"),"shrink-0 -ml-1 mr-1.5",d[b].height,d[b].width)}):null,r.default.createElement("span",{className:(0,n.tremorTwMerge)(c("text"),"whitespace-nowrap")},f))});g.displayName="Badge",e.s(["Badge",0,g],389083)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/043q3g5-5-aju.js b/litellm/proxy/_experimental/out/_next/static/chunks/043q3g5-5-aju.js new file mode 100644 index 00000000000..f1cf0386519 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/043q3g5-5-aju.js @@ -0,0 +1,55 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,138540,356061,e=>{"use strict";e.s(["default",0,e=>"object"!=typeof e&&"function"!=typeof e||null===e],138540);var t=e.i(983409);e.s(["ItemGroup",()=>t.default],356061)},741273,e=>{"use strict";e.i(247167);var t=e.i(931067),o=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M912 192H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM104 228a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0z"}}]},name:"bars",theme:"outlined"};var i=e.i(9583),r=o.forwardRef(function(e,r){return o.createElement(i.default,(0,t.default)({},e,{ref:r,icon:n}))});e.s(["default",0,r],741273)},704914,e=>{"use strict";let t=e.i(271645).createContext({siderHook:{addSider:()=>null,removeSider:()=>null}});e.s(["LayoutContext",0,t])},290224,251224,e=>{"use strict";let t;e.i(247167);var o=e.i(271645),n=e.i(741273),i=e.i(801312),r=e.i(286612),l=e.i(343794),a=e.i(529681),d=e.i(958503),s=e.i(242064),u=e.i(704914);e.i(296059);var c=e.i(915654),m=e.i(246422);let p=e=>{let{colorBgLayout:t,controlHeight:o,controlHeightLG:n,colorText:i,controlHeightSM:r,marginXXS:l,colorTextLightSolid:a,colorBgContainer:d}=e,s=1.25*n;return{colorBgHeader:"#001529",colorBgBody:t,colorBgTrigger:"#002140",bodyBg:t,headerBg:"#001529",headerHeight:2*o,headerPadding:`0 ${s}px`,headerColor:i,footerPadding:`${r}px ${s}px`,footerBg:t,siderBg:"#001529",triggerHeight:n+2*l,triggerBg:"#002140",triggerColor:a,zeroTriggerWidth:n,zeroTriggerHeight:n,lightSiderBg:d,lightTriggerBg:d,lightTriggerColor:i}},g=[["colorBgBody","bodyBg"],["colorBgHeader","headerBg"],["colorBgTrigger","triggerBg"]],$=(0,m.genStyleHooks)("Layout",e=>{let{antCls:t,componentCls:o,colorText:n,footerBg:i,headerHeight:r,headerPadding:l,headerColor:a,footerPadding:d,fontSize:s,bodyBg:u,headerBg:m}=e;return{[o]:{display:"flex",flex:"auto",flexDirection:"column",minHeight:0,background:u,"&, *":{boxSizing:"border-box"},[`&${o}-has-sider`]:{flexDirection:"row",[`> ${o}, > ${o}-content`]:{width:0}},[`${o}-header, &${o}-footer`]:{flex:"0 0 auto"},"&-rtl":{direction:"rtl"}},[`${o}-header`]:{height:r,padding:l,color:a,lineHeight:(0,c.unit)(r),background:m,[`${t}-menu`]:{lineHeight:"inherit"}},[`${o}-footer`]:{padding:d,color:n,fontSize:s,background:i},[`${o}-content`]:{flex:"auto",color:n,minHeight:0}}},p,{deprecatedTokens:g});e.s(["DEPRECATED_TOKENS",0,g,"default",0,$,"prepareComponentToken",0,p],251224);let b=(0,m.genStyleHooks)(["Layout","Sider"],e=>{let{componentCls:t,siderBg:o,motionDurationMid:n,motionDurationSlow:i,antCls:r,triggerHeight:l,triggerColor:a,triggerBg:d,headerHeight:s,zeroTriggerWidth:u,zeroTriggerHeight:m,borderRadiusLG:p,lightSiderBg:g,lightTriggerColor:$,lightTriggerBg:b,bodyBg:f}=e;return{[t]:{position:"relative",minWidth:0,background:o,transition:`all ${n}, background 0s`,"&-has-trigger":{paddingBottom:l},"&-right":{order:1},[`${t}-children`]:{height:"100%",marginTop:-.1,paddingTop:.1,[`${r}-menu${r}-menu-inline-collapsed`]:{width:"auto"}},[`&-zero-width ${t}-children`]:{overflow:"hidden"},[`${t}-trigger`]:{position:"fixed",bottom:0,zIndex:1,height:l,color:a,lineHeight:(0,c.unit)(l),textAlign:"center",background:d,cursor:"pointer",transition:`all ${n}`},[`${t}-zero-width-trigger`]:{position:"absolute",top:s,insetInlineEnd:e.calc(u).mul(-1).equal(),zIndex:1,width:u,height:m,color:a,fontSize:e.fontSizeXL,display:"flex",alignItems:"center",justifyContent:"center",background:o,borderRadius:`0 ${(0,c.unit)(p)} ${(0,c.unit)(p)} 0`,cursor:"pointer",transition:`background ${i} ease`,"&::after":{position:"absolute",inset:0,background:"transparent",transition:`all ${i}`,content:'""'},"&:hover::after":{background:"rgba(255, 255, 255, 0.2)"},"&-right":{insetInlineStart:e.calc(u).mul(-1).equal(),borderRadius:`${(0,c.unit)(p)} 0 0 ${(0,c.unit)(p)}`}},"&-light":{background:g,[`${t}-trigger`]:{color:$,background:b},[`${t}-zero-width-trigger`]:{color:$,background:b,border:`1px solid ${f}`,borderInlineStart:0}}}}},p,{deprecatedTokens:g});var f=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(o[n[i]]=e[n[i]]);return o};let v={xs:"479.98px",sm:"575.98px",md:"767.98px",lg:"991.98px",xl:"1199.98px",xxl:"1599.98px"},h=o.createContext({}),x=(t=0,(e="")=>(t+=1,`${e}${t}`)),C=o.forwardRef((e,t)=>{let{prefixCls:c,className:m,trigger:p,children:g,defaultCollapsed:$=!1,theme:C="dark",style:I={},collapsible:y=!1,reverseArrow:S=!1,width:w=200,collapsedWidth:B=80,zeroWidthTriggerStyle:O,breakpoint:k,onCollapse:E,onBreakpoint:H}=e,j=f(e,["prefixCls","className","trigger","children","defaultCollapsed","theme","style","collapsible","reverseArrow","width","collapsedWidth","zeroWidthTriggerStyle","breakpoint","onCollapse","onBreakpoint"]),{siderHook:z}=(0,o.useContext)(u.LayoutContext),[T,N]=(0,o.useState)("collapsed"in e?e.collapsed:$),[R,P]=(0,o.useState)(!1);(0,o.useEffect)(()=>{"collapsed"in e&&N(e.collapsed)},[e.collapsed]);let M=(t,o)=>{"collapsed"in e||N(t),null==E||E(t,o)},{getPrefixCls:D,direction:A}=(0,o.useContext)(s.ConfigContext),L=D("layout-sider",c),[W,q,X]=b(L),F=(0,o.useRef)(null);F.current=e=>{P(e.matches),null==H||H(e.matches),T!==e.matches&&M(e.matches,"responsive")},(0,o.useEffect)(()=>{let e;function t(e){var t;return null==(t=F.current)?void 0:t.call(F,e)}return void 0!==(null==window?void 0:window.matchMedia)&&k&&k in v&&(e=window.matchMedia(`screen and (max-width: ${v[k]})`),(0,d.addMediaQueryListener)(e,t),t(e)),()=>{(0,d.removeMediaQueryListener)(e,t)}},[k]),(0,o.useEffect)(()=>{let e=x("ant-sider-");return z.addSider(e),()=>z.removeSider(e)},[]);let Y=()=>{M(!T,"clickTrigger")},G=(0,a.default)(j,["collapsed"]),_=T?B:w,U=!Number.isNaN(Number.parseFloat(_))&&Number.isFinite(Number(_))?`${_}px`:String(_),V=0===Number.parseFloat(String(B||0))?o.createElement("span",{onClick:Y,className:(0,l.default)(`${L}-zero-width-trigger`,`${L}-zero-width-trigger-${S?"right":"left"}`),style:O},p||o.createElement(n.default,null)):null,Z="rtl"===A==!S,K={expanded:Z?o.createElement(r.default,null):o.createElement(i.default,null),collapsed:Z?o.createElement(i.default,null):o.createElement(r.default,null)}[T?"collapsed":"expanded"],Q=null!==p?V||o.createElement("div",{className:`${L}-trigger`,onClick:Y,style:{width:U}},p||K):null,J=Object.assign(Object.assign({},I),{flex:`0 0 ${U}`,maxWidth:U,minWidth:U,width:U}),ee=(0,l.default)(L,`${L}-${C}`,{[`${L}-collapsed`]:!!T,[`${L}-has-trigger`]:y&&null!==p&&!V,[`${L}-below`]:!!R,[`${L}-zero-width`]:0===Number.parseFloat(U)},m,q,X),et=o.useMemo(()=>({siderCollapsed:T}),[T]);return W(o.createElement(h.Provider,{value:et},o.createElement("aside",Object.assign({className:ee},G,{style:J,ref:t}),o.createElement("div",{className:`${L}-children`},g),y||R&&V?Q:null)))});e.s(["SiderContext",0,h,"default",0,C],290224)},21539,652199,60699,e=>{"use strict";e.i(247167);var t=e.i(271645),o=e.i(801312),n=e.i(286612),i=e.i(343794),r=e.i(878081),l=e.i(175066),a=e.i(914949),d=e.i(529681),s=e.i(122767),u=e.i(138540),c=e.i(805984),m=e.i(805484),p=e.i(763731),g=e.i(747656),$=e.i(340010),b=e.i(242064),f=e.i(321883),v=e.i(375565),h=e.i(356061),x=e.i(290224),C=e.i(867384),I=e.i(613541);let y=(0,t.createContext)({prefixCls:"",firstLevel:!0,inlineCollapsed:!1});var S=e.i(259792),S=S,w=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(o[n[i]]=e[n[i]]);return o};let B=e=>{let{prefixCls:o,className:n,dashed:r}=e,l=w(e,["prefixCls","className","dashed"]),{getPrefixCls:a}=t.useContext(b.ConfigContext),d=a("menu",o),s=(0,i.default)({[`${d}-item-divider-dashed`]:!!r},n);return t.createElement(S.default,Object.assign({className:s},l))};var O=e.i(452741),O=O,k=e.i(876556),E=e.i(491816);let H=e=>{var o;let n,r,{className:l,children:a,icon:s,title:u,danger:c,extra:m}=e,{prefixCls:g,firstLevel:$,direction:b,disableMenuItemTitleTooltip:f,inlineCollapsed:v}=t.useContext(y),{siderCollapsed:h}=t.useContext(x.SiderContext),C=u;void 0===u?C=$?a:"":!1===u&&(C="");let I={title:C};h||v||(I.title=null,I.open=!1);let S=(0,k.default)(a).length,w=t.createElement(O.default,Object.assign({},(0,d.default)(e,["title","icon","danger"]),{className:(0,i.default)({[`${g}-item-danger`]:c,[`${g}-item-only-child`]:(s?S+1:S)===1},l),title:"string"==typeof u?u:void 0}),(0,p.cloneElement)(s,{className:(0,i.default)(t.isValidElement(s)?null==(o=s.props)?void 0:o.className:void 0,`${g}-item-icon`)}),(n=null==a?void 0:a[0],r=t.createElement("span",{className:(0,i.default)(`${g}-title-content`,{[`${g}-title-content-with-extra`]:!!m||0===m})},a),(!s||t.isValidElement(a)&&"span"===a.type)&&a&&v&&$&&"string"==typeof n?t.createElement("div",{className:`${g}-inline-collapsed-noicon`},n.charAt(0)):r));return f||(w=t.createElement(E.default,Object.assign({},I,{placement:"rtl"===b?"left":"right",classNames:{root:`${g}-inline-collapsed-tooltip`}}),w)),w};var j=e.i(611935),z=e.i(617206),T=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(o[n[i]]=e[n[i]]);return o};let N=t.createContext(null),R=t.forwardRef((e,o)=>{let{children:n}=e,i=T(e,["children"]),r=t.useContext(N),l=t.useMemo(()=>Object.assign(Object.assign({},r),i),[r,i.prefixCls,i.mode,i.selectable,i.rootClassName]),a=(0,j.supportNodeRef)(n),d=(0,j.useComposeRef)(o,a?(0,j.getNodeRef)(n):null);return t.createElement(N.Provider,{value:l},t.createElement(z.default,{space:!0},a?t.cloneElement(n,{ref:d}):n))});e.s(["OverrideProvider",0,R,"default",0,N],652199),e.i(296059);var P=e.i(915654),M=e.i(135551),D=e.i(183293),A=e.i(447580),L=e.i(664142),W=e.i(717356),q=e.i(246422),X=e.i(838378);let F=e=>(0,D.genFocusOutline)(e),Y=(e,t)=>{let{componentCls:o,itemColor:n,itemSelectedColor:i,subMenuItemSelectedColor:r,groupTitleColor:l,itemBg:a,subMenuItemBg:d,itemSelectedBg:s,activeBarHeight:u,activeBarWidth:c,activeBarBorderWidth:m,motionDurationSlow:p,motionEaseInOut:g,motionEaseOut:$,itemPaddingInline:b,motionDurationMid:f,itemHoverColor:v,lineType:h,colorSplit:x,itemDisabledColor:C,dangerItemColor:I,dangerItemHoverColor:y,dangerItemSelectedColor:S,dangerItemActiveBg:w,dangerItemSelectedBg:B,popupBg:O,itemHoverBg:k,itemActiveBg:E,menuSubMenuBg:H,horizontalItemSelectedColor:j,horizontalItemSelectedBg:z,horizontalItemBorderRadius:T,horizontalItemHoverBg:N}=e;return{[`${o}-${t}, ${o}-${t} > ${o}`]:{color:n,background:a,[`&${o}-root:focus-visible`]:Object.assign({},F(e)),[`${o}-item`]:{"&-group-title, &-extra":{color:l}},[`${o}-submenu-selected > ${o}-submenu-title`]:{color:r},[`${o}-item, ${o}-submenu-title`]:{color:n,[`&:not(${o}-item-disabled):focus-visible`]:Object.assign({},F(e))},[`${o}-item-disabled, ${o}-submenu-disabled`]:{color:`${C} !important`},[`${o}-item:not(${o}-item-selected):not(${o}-submenu-selected)`]:{[`&:hover, > ${o}-submenu-title:hover`]:{color:v}},[`&:not(${o}-horizontal)`]:{[`${o}-item:not(${o}-item-selected)`]:{"&:hover":{backgroundColor:k},"&:active":{backgroundColor:E}},[`${o}-submenu-title`]:{"&:hover":{backgroundColor:k},"&:active":{backgroundColor:E}}},[`${o}-item-danger`]:{color:I,[`&${o}-item:hover`]:{[`&:not(${o}-item-selected):not(${o}-submenu-selected)`]:{color:y}},[`&${o}-item:active`]:{background:w}},[`${o}-item a`]:{"&, &:hover":{color:"inherit"}},[`${o}-item-selected`]:{color:i,[`&${o}-item-danger`]:{color:S},"a, a:hover":{color:"inherit"}},[`& ${o}-item-selected`]:{backgroundColor:s,[`&${o}-item-danger`]:{backgroundColor:B}},[`&${o}-submenu > ${o}`]:{backgroundColor:H},[`&${o}-popup > ${o}`]:{backgroundColor:O},[`&${o}-submenu-popup > ${o}`]:{backgroundColor:O},[`&${o}-horizontal`]:Object.assign(Object.assign({},"dark"===t?{borderBottom:0}:{}),{[`> ${o}-item, > ${o}-submenu`]:{top:m,marginTop:e.calc(m).mul(-1).equal(),marginBottom:0,borderRadius:T,"&::after":{position:"absolute",insetInline:b,bottom:0,borderBottom:`${(0,P.unit)(u)} solid transparent`,transition:`border-color ${p} ${g}`,content:'""'},"&:hover, &-active, &-open":{background:N,"&::after":{borderBottomWidth:u,borderBottomColor:j}},"&-selected":{color:j,backgroundColor:z,"&:hover":{backgroundColor:z},"&::after":{borderBottomWidth:u,borderBottomColor:j}}}}),[`&${o}-root`]:{[`&${o}-inline, &${o}-vertical`]:{borderInlineEnd:`${(0,P.unit)(m)} ${h} ${x}`}},[`&${o}-inline`]:{[`${o}-sub${o}-inline`]:{background:d},[`${o}-item`]:{position:"relative","&::after":{position:"absolute",insetBlock:0,insetInlineEnd:0,borderInlineEnd:`${(0,P.unit)(c)} solid ${i}`,transform:"scaleY(0.0001)",opacity:0,transition:`transform ${f} ${$},opacity ${f} ${$}`,content:'""'},[`&${o}-item-danger`]:{"&::after":{borderInlineEndColor:S}}},[`${o}-selected, ${o}-item-selected`]:{"&::after":{transform:"scaleY(1)",opacity:1,transition:`transform ${f} ${g},opacity ${f} ${g}`}}}}}},G=e=>{let{componentCls:t,itemHeight:o,itemMarginInline:n,padding:i,menuArrowSize:r,marginXS:l,itemMarginBlock:a,itemWidth:d,itemPaddingInline:s}=e,u=e.calc(r).add(i).add(l).equal();return{[`${t}-item`]:{position:"relative",overflow:"hidden"},[`${t}-item, ${t}-submenu-title`]:{height:o,lineHeight:(0,P.unit)(o),paddingInline:s,overflow:"hidden",textOverflow:"ellipsis",marginInline:n,marginBlock:a,width:d},[`> ${t}-item, + > ${t}-submenu > ${t}-submenu-title`]:{height:o,lineHeight:(0,P.unit)(o)},[`${t}-item-group-list ${t}-submenu-title, + ${t}-submenu-title`]:{paddingInlineEnd:u}}},_=e=>{let{componentCls:t,motionDurationSlow:o,motionDurationMid:n,motionEaseInOut:i,motionEaseOut:r,iconCls:l,iconSize:a,iconMarginInlineEnd:d}=e;return{[`${t}-item, ${t}-submenu-title`]:{position:"relative",display:"block",margin:0,whiteSpace:"nowrap",cursor:"pointer",transition:`border-color ${o},background ${o},padding calc(${o} + 0.1s) ${i}`,[`${t}-item-icon, ${l}`]:{minWidth:a,fontSize:a,transition:`font-size ${n} ${r},margin ${o} ${i},color ${o}`,"+ span":{marginInlineStart:d,opacity:1,transition:`opacity ${o} ${i},margin ${o},color ${o}`}},[`${t}-item-icon`]:Object.assign({},(0,D.resetIcon)()),[`&${t}-item-only-child`]:{[`> ${l}, > ${t}-item-icon`]:{marginInlineEnd:0}}},[`${t}-item-disabled, ${t}-submenu-disabled`]:{background:"none !important",cursor:"not-allowed","&::after":{borderColor:"transparent !important"},a:{color:"inherit !important",cursor:"not-allowed",pointerEvents:"none"},[`> ${t}-submenu-title`]:{color:"inherit !important",cursor:"not-allowed"}}}},U=e=>{let{componentCls:t,motionDurationSlow:o,motionEaseInOut:n,borderRadius:i,menuArrowSize:r,menuArrowOffset:l}=e;return{[`${t}-submenu`]:{"&-expand-icon, &-arrow":{position:"absolute",top:"50%",insetInlineEnd:e.margin,width:r,color:"currentcolor",transform:"translateY(-50%)",transition:`transform ${o} ${n}, opacity ${o}`},"&-arrow":{"&::before, &::after":{position:"absolute",width:e.calc(r).mul(.6).equal(),height:e.calc(r).mul(.15).equal(),backgroundColor:"currentcolor",borderRadius:i,transition:`background ${o} ${n},transform ${o} ${n},top ${o} ${n},color ${o} ${n}`,content:'""'},"&::before":{transform:`rotate(45deg) translateY(${(0,P.unit)(e.calc(l).mul(-1).equal())})`},"&::after":{transform:`rotate(-45deg) translateY(${(0,P.unit)(l)})`}}}}},V=e=>{var t,o,n;let{colorPrimary:i,colorError:r,colorTextDisabled:l,colorErrorBg:a,colorText:d,colorTextDescription:s,colorBgContainer:u,colorFillAlter:c,colorFillContent:m,lineWidth:p,lineWidthBold:g,controlItemBgActive:$,colorBgTextHover:b,controlHeightLG:f,lineHeight:v,colorBgElevated:h,marginXXS:x,padding:C,fontSize:I,controlHeightSM:y,fontSizeLG:S,colorTextLightSolid:w,colorErrorHover:B}=e,O=null!=(t=e.activeBarWidth)?t:0,k=null!=(o=e.activeBarBorderWidth)?o:p,E=null!=(n=e.itemMarginInline)?n:e.marginXXS,H=new M.FastColor(w).setA(.65).toRgbString();return{dropdownWidth:160,zIndexPopup:e.zIndexPopupBase+50,radiusItem:e.borderRadiusLG,itemBorderRadius:e.borderRadiusLG,radiusSubMenuItem:e.borderRadiusSM,subMenuItemBorderRadius:e.borderRadiusSM,colorItemText:d,itemColor:d,colorItemTextHover:d,itemHoverColor:d,colorItemTextHoverHorizontal:i,horizontalItemHoverColor:i,colorGroupTitle:s,groupTitleColor:s,colorItemTextSelected:i,itemSelectedColor:i,subMenuItemSelectedColor:i,colorItemTextSelectedHorizontal:i,horizontalItemSelectedColor:i,colorItemBg:u,itemBg:u,colorItemBgHover:b,itemHoverBg:b,colorItemBgActive:m,itemActiveBg:$,colorSubItemBg:c,subMenuItemBg:c,colorItemBgSelected:$,itemSelectedBg:$,colorItemBgSelectedHorizontal:"transparent",horizontalItemSelectedBg:"transparent",colorActiveBarWidth:0,activeBarWidth:O,colorActiveBarHeight:g,activeBarHeight:g,colorActiveBarBorderSize:p,activeBarBorderWidth:k,colorItemTextDisabled:l,itemDisabledColor:l,colorDangerItemText:r,dangerItemColor:r,colorDangerItemTextHover:r,dangerItemHoverColor:r,colorDangerItemTextSelected:r,dangerItemSelectedColor:r,colorDangerItemBgActive:a,dangerItemActiveBg:a,colorDangerItemBgSelected:a,dangerItemSelectedBg:a,itemMarginInline:E,horizontalItemBorderRadius:0,horizontalItemHoverBg:"transparent",itemHeight:f,groupTitleLineHeight:v,collapsedWidth:2*f,popupBg:h,itemMarginBlock:x,itemPaddingInline:C,horizontalLineHeight:`${1.15*f}px`,iconSize:I,iconMarginInlineEnd:y-I,collapsedIconSize:S,groupTitleFontSize:I,darkItemDisabledColor:new M.FastColor(w).setA(.25).toRgbString(),darkItemColor:H,darkDangerItemColor:r,darkItemBg:"#001529",darkPopupBg:"#001529",darkSubMenuItemBg:"#000c17",darkItemSelectedColor:w,darkItemSelectedBg:i,darkDangerItemSelectedBg:r,darkItemHoverBg:"transparent",darkGroupTitleColor:H,darkItemHoverColor:w,darkDangerItemHoverColor:B,darkDangerItemSelectedColor:w,darkDangerItemActiveBg:r,itemWidth:O?`calc(100% + ${k}px)`:`calc(100% - ${2*E}px)`}};var Z=e.i(905054),Z=Z,K=e.i(465394);let Q=e=>{var o;let n,{popupClassName:r,icon:l,title:a,theme:u}=e,c=t.useContext(y),{prefixCls:m,inlineCollapsed:g,theme:$}=c,b=(0,K.useFullPath)();if(l){let e=t.isValidElement(a)&&"span"===a.type;n=t.createElement(t.Fragment,null,(0,p.cloneElement)(l,{className:(0,i.default)(t.isValidElement(l)?null==(o=l.props)?void 0:o.className:void 0,`${m}-item-icon`)}),e?a:t.createElement("span",{className:`${m}-title-content`},a))}else n=g&&!b.length&&a&&"string"==typeof a?t.createElement("div",{className:`${m}-inline-collapsed-noicon`},a.charAt(0)):t.createElement("span",{className:`${m}-title-content`},a);let f=t.useMemo(()=>Object.assign(Object.assign({},c),{firstLevel:!1}),[c]),[v]=(0,s.useZIndex)("Menu");return t.createElement(y.Provider,{value:f},t.createElement(Z.default,Object.assign({},(0,d.default)(e,["icon"]),{title:n,popupClassName:(0,i.default)(m,r,`${m}-${u||$}`),popupStyle:Object.assign({zIndex:v},e.popupStyle)})))};var J=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(o[n[i]]=e[n[i]]);return o};function ee(e){return null===e||!1===e}let et={item:H,submenu:Q,divider:B},eo=(0,t.forwardRef)((e,o)=>{var n;let r=t.useContext(N),a=r||{},{getPrefixCls:s,getPopupContainer:u,direction:c,menu:m}=t.useContext(b.ConfigContext),g=s(),{prefixCls:$,className:h,style:x,theme:S="light",expandIcon:w,_internalDisableMenuItemTitleTooltip:B,inlineCollapsed:O,siderCollapsed:k,rootClassName:E,mode:H,selectable:j,onClick:z,overflowedIndicatorPopupClassName:T}=e,R=J(e,["prefixCls","className","style","theme","expandIcon","_internalDisableMenuItemTitleTooltip","inlineCollapsed","siderCollapsed","rootClassName","mode","selectable","onClick","overflowedIndicatorPopupClassName"]),M=(0,d.default)(R,["collapsedWidth"]);null==(n=a.validator)||n.call(a,{mode:H});let F=(0,l.default)((...e)=>{var t;null==z||z.apply(void 0,e),null==(t=a.onClick)||t.call(a)}),Z=a.mode||H,K=null!=j?j:a.selectable,Q=null!=O?O:k,eo={horizontal:{motionName:`${g}-slide-up`},inline:(0,I.default)(g),other:{motionName:`${g}-zoom-big`}},en=s("menu",$||a.prefixCls),ei=(0,f.default)(en),[er,el,ea]=((e,t=e,o=!0)=>(0,q.genStyleHooks)("Menu",e=>{let{colorBgElevated:t,controlHeightLG:o,fontSize:n,darkItemColor:i,darkDangerItemColor:r,darkItemBg:l,darkSubMenuItemBg:a,darkItemSelectedColor:d,darkItemSelectedBg:s,darkDangerItemSelectedBg:u,darkItemHoverBg:c,darkGroupTitleColor:m,darkItemHoverColor:p,darkItemDisabledColor:g,darkDangerItemHoverColor:$,darkDangerItemSelectedColor:b,darkDangerItemActiveBg:f,popupBg:v,darkPopupBg:h}=e,x=e.calc(n).div(7).mul(5).equal(),C=(0,X.mergeToken)(e,{menuArrowSize:x,menuHorizontalHeight:e.calc(o).mul(1.15).equal(),menuArrowOffset:e.calc(x).mul(.25).equal(),menuSubMenuBg:t,calc:e.calc,popupBg:v}),I=(0,X.mergeToken)(C,{itemColor:i,itemHoverColor:p,groupTitleColor:m,itemSelectedColor:d,subMenuItemSelectedColor:d,itemBg:l,popupBg:h,subMenuItemBg:a,itemActiveBg:"transparent",itemSelectedBg:s,activeBarHeight:0,activeBarBorderWidth:0,itemHoverBg:c,itemDisabledColor:g,dangerItemColor:r,dangerItemHoverColor:$,dangerItemSelectedColor:b,dangerItemActiveBg:f,dangerItemSelectedBg:u,menuSubMenuBg:a,horizontalItemSelectedColor:d,horizontalItemSelectedBg:s});return[(e=>{let{antCls:t,componentCls:o,fontSize:n,motionDurationSlow:i,motionDurationMid:r,motionEaseInOut:l,paddingXS:a,padding:d,colorSplit:s,lineWidth:u,zIndexPopup:c,borderRadiusLG:m,subMenuItemBorderRadius:p,menuArrowSize:g,menuArrowOffset:$,lineType:b,groupTitleLineHeight:f,groupTitleFontSize:v}=e;return[{"":{[o]:Object.assign(Object.assign({},(0,D.clearFix)()),{"&-hidden":{display:"none"}})},[`${o}-submenu-hidden`]:{display:"none"}},{[o]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,D.resetComponent)(e)),(0,D.clearFix)()),{marginBottom:0,paddingInlineStart:0,fontSize:n,lineHeight:0,listStyle:"none",outline:"none",transition:`width ${i} cubic-bezier(0.2, 0, 0, 1) 0s`,"ul, ol":{margin:0,padding:0,listStyle:"none"},"&-overflow":{display:"flex",[`${o}-item`]:{flex:"none"}},[`${o}-item, ${o}-submenu, ${o}-submenu-title`]:{borderRadius:e.itemBorderRadius},[`${o}-item-group-title`]:{padding:`${(0,P.unit)(a)} ${(0,P.unit)(d)}`,fontSize:v,lineHeight:f,transition:`all ${i}`},[`&-horizontal ${o}-submenu`]:{transition:`border-color ${i} ${l},background ${i} ${l}`},[`${o}-submenu, ${o}-submenu-inline`]:{transition:`border-color ${i} ${l},background ${i} ${l},padding ${r} ${l}`},[`${o}-submenu ${o}-sub`]:{cursor:"initial",transition:`background ${i} ${l},padding ${i} ${l}`},[`${o}-title-content`]:{transition:`color ${i}`,"&-with-extra":{display:"inline-flex",alignItems:"center",width:"100%"},[`> ${t}-typography-ellipsis-single-line`]:{display:"inline",verticalAlign:"unset"},[`${o}-item-extra`]:{marginInlineStart:"auto",paddingInlineStart:e.padding}},[`${o}-item a`]:{"&::before":{position:"absolute",inset:0,backgroundColor:"transparent",content:'""'}},[`${o}-item-divider`]:{overflow:"hidden",lineHeight:0,borderColor:s,borderStyle:b,borderWidth:0,borderTopWidth:u,marginBlock:u,padding:0,"&-dashed":{borderStyle:"dashed"}}}),_(e)),{[`${o}-item-group`]:{[`${o}-item-group-list`]:{margin:0,padding:0,[`${o}-item, ${o}-submenu-title`]:{paddingInline:`${(0,P.unit)(e.calc(n).mul(2).equal())} ${(0,P.unit)(d)}`}}},"&-submenu":{"&-popup":{position:"absolute",zIndex:c,borderRadius:m,boxShadow:"none",transformOrigin:"0 0",[`&${o}-submenu`]:{background:"transparent"},"&::before":{position:"absolute",inset:0,zIndex:-1,width:"100%",height:"100%",opacity:0,content:'""'},[`> ${o}`]:Object.assign(Object.assign(Object.assign({borderRadius:m},_(e)),U(e)),{[`${o}-item, ${o}-submenu > ${o}-submenu-title`]:{borderRadius:p},[`${o}-submenu-title::after`]:{transition:`transform ${i} ${l}`}})},[` + &-placement-leftTop, + &-placement-bottomRight, + `]:{transformOrigin:"100% 0"},[` + &-placement-leftBottom, + &-placement-topRight, + `]:{transformOrigin:"100% 100%"},[` + &-placement-rightBottom, + &-placement-topLeft, + `]:{transformOrigin:"0 100%"},[` + &-placement-bottomLeft, + &-placement-rightTop, + `]:{transformOrigin:"0 0"},[` + &-placement-leftTop, + &-placement-leftBottom + `]:{paddingInlineEnd:e.paddingXS},[` + &-placement-rightTop, + &-placement-rightBottom + `]:{paddingInlineStart:e.paddingXS},[` + &-placement-topRight, + &-placement-topLeft + `]:{paddingBottom:e.paddingXS},[` + &-placement-bottomRight, + &-placement-bottomLeft + `]:{paddingTop:e.paddingXS}}}),U(e)),{[`&-inline-collapsed ${o}-submenu-arrow, + &-inline ${o}-submenu-arrow`]:{"&::before":{transform:`rotate(-45deg) translateX(${(0,P.unit)($)})`},"&::after":{transform:`rotate(45deg) translateX(${(0,P.unit)(e.calc($).mul(-1).equal())})`}},[`${o}-submenu-open${o}-submenu-inline > ${o}-submenu-title > ${o}-submenu-arrow`]:{transform:`translateY(${(0,P.unit)(e.calc(g).mul(.2).mul(-1).equal())})`,"&::after":{transform:`rotate(-45deg) translateX(${(0,P.unit)(e.calc($).mul(-1).equal())})`},"&::before":{transform:`rotate(45deg) translateX(${(0,P.unit)($)})`}}})},{[`${t}-layout-header`]:{[o]:{lineHeight:"inherit"}}}]})(C),(e=>{let{componentCls:t,motionDurationSlow:o,horizontalLineHeight:n,colorSplit:i,lineWidth:r,lineType:l,itemPaddingInline:a}=e;return{[`${t}-horizontal`]:{lineHeight:n,border:0,borderBottom:`${(0,P.unit)(r)} ${l} ${i}`,boxShadow:"none","&::after":{display:"block",clear:"both",height:0,content:'"\\20"'},[`${t}-item, ${t}-submenu`]:{position:"relative",display:"inline-block",verticalAlign:"bottom",paddingInline:a},[`> ${t}-item:hover, + > ${t}-item-active, + > ${t}-submenu ${t}-submenu-title:hover`]:{backgroundColor:"transparent"},[`${t}-item, ${t}-submenu-title`]:{transition:`border-color ${o},background ${o}`},[`${t}-submenu-arrow`]:{display:"none"}}}})(C),(e=>{let{componentCls:t,iconCls:o,itemHeight:n,colorTextLightSolid:i,dropdownWidth:r,controlHeightLG:l,motionEaseOut:a,paddingXL:d,itemMarginInline:s,fontSizeLG:u,motionDurationFast:c,motionDurationSlow:m,paddingXS:p,boxShadowSecondary:g,collapsedWidth:$,collapsedIconSize:b}=e,f={height:n,lineHeight:(0,P.unit)(n),listStylePosition:"inside",listStyleType:"disc"};return[{[t]:{"&-inline, &-vertical":Object.assign({[`&${t}-root`]:{boxShadow:"none"}},G(e))},[`${t}-submenu-popup`]:{[`${t}-vertical`]:Object.assign(Object.assign({},G(e)),{boxShadow:g})}},{[`${t}-submenu-popup ${t}-vertical${t}-sub`]:{minWidth:r,maxHeight:`calc(100vh - ${(0,P.unit)(e.calc(l).mul(2.5).equal())})`,padding:"0",overflow:"hidden",borderInlineEnd:0,"&:not([class*='-active'])":{overflowX:"hidden",overflowY:"auto"}}},{[`${t}-inline`]:{width:"100%",[`&${t}-root`]:{[`${t}-item, ${t}-submenu-title`]:{display:"flex",alignItems:"center",transition:`border-color ${m},background ${m},padding ${c} ${a}`,[`> ${t}-title-content`]:{flex:"auto",minWidth:0,overflow:"hidden",textOverflow:"ellipsis"},"> *":{flex:"none"}}},[`${t}-sub${t}-inline`]:{padding:0,border:0,borderRadius:0,boxShadow:"none",[`& > ${t}-submenu > ${t}-submenu-title`]:f,[`& ${t}-item-group-title`]:{paddingInlineStart:d}},[`${t}-item`]:f}},{[`${t}-inline-collapsed`]:{width:$,[`&${t}-root`]:{[`${t}-item, ${t}-submenu ${t}-submenu-title`]:{[`> ${t}-inline-collapsed-noicon`]:{fontSize:u,textAlign:"center"}}},[`> ${t}-item, + > ${t}-item-group > ${t}-item-group-list > ${t}-item, + > ${t}-item-group > ${t}-item-group-list > ${t}-submenu > ${t}-submenu-title, + > ${t}-submenu > ${t}-submenu-title`]:{insetInlineStart:0,paddingInline:`calc(50% - ${(0,P.unit)(e.calc(b).div(2).equal())} - ${(0,P.unit)(s)})`,textOverflow:"clip",[` + ${t}-submenu-arrow, + ${t}-submenu-expand-icon + `]:{opacity:0},[`${t}-item-icon, ${o}`]:{margin:0,fontSize:b,lineHeight:(0,P.unit)(n),"+ span":{display:"inline-block",opacity:0}}},[`${t}-item-icon, ${o}`]:{display:"inline-block"},"&-tooltip":{pointerEvents:"none",[`${t}-item-icon, ${o}`]:{display:"none"},"a, a:hover":{color:i}},[`${t}-item-group-title`]:Object.assign(Object.assign({},D.textEllipsis),{paddingInline:p})}}]})(C),Y(C,"light"),Y(I,"dark"),(({componentCls:e,menuArrowOffset:t,calc:o})=>({[`${e}-rtl`]:{direction:"rtl"},[`${e}-submenu-rtl`]:{transformOrigin:"100% 0"},[`${e}-rtl${e}-vertical, + ${e}-submenu-rtl ${e}-vertical`]:{[`${e}-submenu-arrow`]:{"&::before":{transform:`rotate(-45deg) translateY(${(0,P.unit)(o(t).mul(-1).equal())})`},"&::after":{transform:`rotate(45deg) translateY(${(0,P.unit)(t)})`}}}}))(C),(0,A.genCollapseMotion)(C),(0,L.initSlideMotion)(C,"slide-up"),(0,L.initSlideMotion)(C,"slide-down"),(0,W.initZoomMotion)(C,"zoom-big")]},V,{deprecatedTokens:[["colorGroupTitle","groupTitleColor"],["radiusItem","itemBorderRadius"],["radiusSubMenuItem","subMenuItemBorderRadius"],["colorItemText","itemColor"],["colorItemTextHover","itemHoverColor"],["colorItemTextHoverHorizontal","horizontalItemHoverColor"],["colorItemTextSelected","itemSelectedColor"],["colorItemTextSelectedHorizontal","horizontalItemSelectedColor"],["colorItemTextDisabled","itemDisabledColor"],["colorDangerItemText","dangerItemColor"],["colorDangerItemTextHover","dangerItemHoverColor"],["colorDangerItemTextSelected","dangerItemSelectedColor"],["colorDangerItemBgActive","dangerItemActiveBg"],["colorDangerItemBgSelected","dangerItemSelectedBg"],["colorItemBg","itemBg"],["colorItemBgHover","itemHoverBg"],["colorSubItemBg","subMenuItemBg"],["colorItemBgActive","itemActiveBg"],["colorItemBgSelectedHorizontal","horizontalItemSelectedBg"],["colorActiveBarWidth","activeBarWidth"],["colorActiveBarHeight","activeBarHeight"],["colorActiveBarBorderSize","activeBarBorderWidth"],["colorItemBgSelected","itemSelectedBg"]],injectStyle:o,unitless:{groupTitleLineHeight:!0}})(e,t))(en,ei,!r),ed=(0,i.default)(`${en}-${S}`,null==m?void 0:m.className,h),es=t.useMemo(()=>{var e,o;if("function"==typeof w||ee(w))return w||null;if("function"==typeof a.expandIcon||ee(a.expandIcon))return a.expandIcon||null;if("function"==typeof(null==m?void 0:m.expandIcon)||ee(null==m?void 0:m.expandIcon))return(null==m?void 0:m.expandIcon)||null;let n=null!=(e=null!=w?w:null==a?void 0:a.expandIcon)?e:null==m?void 0:m.expandIcon;return(0,p.cloneElement)(n,{className:(0,i.default)(`${en}-submenu-expand-icon`,t.isValidElement(n)?null==(o=n.props)?void 0:o.className:void 0)})},[w,null==a?void 0:a.expandIcon,null==m?void 0:m.expandIcon,en]),eu=t.useMemo(()=>({prefixCls:en,inlineCollapsed:Q||!1,direction:c,firstLevel:!0,theme:S,mode:Z,disableMenuItemTitleTooltip:B}),[en,Q,c,B,S]);return er(t.createElement(N.Provider,{value:null},t.createElement(y.Provider,{value:eu},t.createElement(v.default,Object.assign({getPopupContainer:u,overflowedIndicator:t.createElement(C.default,null),overflowedIndicatorPopupClassName:(0,i.default)(en,`${en}-${S}`,T),mode:Z,selectable:K,onClick:F},M,{inlineCollapsed:Q,style:Object.assign(Object.assign({},null==m?void 0:m.style),x),className:ed,prefixCls:en,direction:c,defaultMotions:eo,expandIcon:es,ref:o,rootClassName:(0,i.default)(E,el,a.rootClassName,ea,ei),_internalComponents:et})))))}),en=(0,t.forwardRef)((e,o)=>{let n=(0,t.useRef)(null),i=t.useContext(x.SiderContext);return(0,t.useImperativeHandle)(o,()=>({menu:n.current,focus:e=>{var t;null==(t=n.current)||t.focus(e)}})),t.createElement(eo,Object.assign({ref:n},e,i))});en.Item=H,en.SubMenu=Q,en.Divider=B,en.ItemGroup=h.ItemGroup,e.s(["default",0,en],60699);var ei=e.i(104458),er=e.i(777489),el=e.i(320560),ea=e.i(307358);let ed=(0,q.genStyleHooks)("Dropdown",e=>{let{marginXXS:t,sizePopupArrow:o,paddingXXS:n,componentCls:i}=e,r=(0,X.mergeToken)(e,{menuCls:`${i}-menu`,dropdownArrowDistance:e.calc(o).div(2).add(t).equal(),dropdownEdgeChildPadding:n});return[(e=>{let{componentCls:t,menuCls:o,zIndexPopup:n,dropdownArrowDistance:i,sizePopupArrow:r,antCls:l,iconCls:a,motionDurationMid:d,paddingBlock:s,fontSize:u,dropdownEdgeChildPadding:c,colorTextDisabled:m,fontSizeIcon:p,controlPaddingHorizontal:g,colorBgElevated:$}=e;return[{[t]:{position:"absolute",top:-9999,left:{_skip_check_:!0,value:-9999},zIndex:n,display:"block","&::before":{position:"absolute",insetBlock:e.calc(r).div(2).sub(i).equal(),zIndex:-9999,opacity:1e-4,content:'""'},"&-menu-vertical":{maxHeight:"100vh",overflowY:"auto"},[`&-trigger${l}-btn`]:{[`& > ${a}-down, & > ${l}-btn-icon > ${a}-down`]:{fontSize:p}},[`${t}-wrap`]:{position:"relative",[`${l}-btn > ${a}-down`]:{fontSize:p},[`${a}-down::before`]:{transition:`transform ${d}`}},[`${t}-wrap-open`]:{[`${a}-down::before`]:{transform:"rotate(180deg)"}},[` + &-hidden, + &-menu-hidden, + &-menu-submenu-hidden + `]:{display:"none"},[`&${l}-slide-down-enter${l}-slide-down-enter-active${t}-placement-bottomLeft, + &${l}-slide-down-appear${l}-slide-down-appear-active${t}-placement-bottomLeft, + &${l}-slide-down-enter${l}-slide-down-enter-active${t}-placement-bottom, + &${l}-slide-down-appear${l}-slide-down-appear-active${t}-placement-bottom, + &${l}-slide-down-enter${l}-slide-down-enter-active${t}-placement-bottomRight, + &${l}-slide-down-appear${l}-slide-down-appear-active${t}-placement-bottomRight`]:{animationName:L.slideUpIn},[`&${l}-slide-up-enter${l}-slide-up-enter-active${t}-placement-topLeft, + &${l}-slide-up-appear${l}-slide-up-appear-active${t}-placement-topLeft, + &${l}-slide-up-enter${l}-slide-up-enter-active${t}-placement-top, + &${l}-slide-up-appear${l}-slide-up-appear-active${t}-placement-top, + &${l}-slide-up-enter${l}-slide-up-enter-active${t}-placement-topRight, + &${l}-slide-up-appear${l}-slide-up-appear-active${t}-placement-topRight`]:{animationName:L.slideDownIn},[`&${l}-slide-down-leave${l}-slide-down-leave-active${t}-placement-bottomLeft, + &${l}-slide-down-leave${l}-slide-down-leave-active${t}-placement-bottom, + &${l}-slide-down-leave${l}-slide-down-leave-active${t}-placement-bottomRight`]:{animationName:L.slideUpOut},[`&${l}-slide-up-leave${l}-slide-up-leave-active${t}-placement-topLeft, + &${l}-slide-up-leave${l}-slide-up-leave-active${t}-placement-top, + &${l}-slide-up-leave${l}-slide-up-leave-active${t}-placement-topRight`]:{animationName:L.slideDownOut}}},(0,el.default)(e,$,{arrowPlacement:{top:!0,bottom:!0}}),{[`${t} ${o}`]:{position:"relative",margin:0},[`${o}-submenu-popup`]:{position:"absolute",zIndex:n,background:"transparent",boxShadow:"none",transformOrigin:"0 0","ul, li":{listStyle:"none",margin:0}},[`${t}, ${t}-menu-submenu`]:Object.assign(Object.assign({},(0,D.resetComponent)(e)),{[o]:Object.assign(Object.assign({padding:c,listStyleType:"none",backgroundColor:$,backgroundClip:"padding-box",borderRadius:e.borderRadiusLG,outline:"none",boxShadow:e.boxShadowSecondary},(0,D.genFocusStyle)(e)),{"&:empty":{padding:0,boxShadow:"none"},[`${o}-item-group-title`]:{padding:`${(0,P.unit)(s)} ${(0,P.unit)(g)}`,color:e.colorTextDescription,transition:`all ${d}`},[`${o}-item`]:{position:"relative",display:"flex",alignItems:"center"},[`${o}-item-icon`]:{minWidth:u,marginInlineEnd:e.marginXS,fontSize:e.fontSizeSM},[`${o}-title-content`]:{flex:"auto","&-with-extra":{display:"inline-flex",alignItems:"center",width:"100%"},"> a":{color:"inherit",transition:`all ${d}`,"&:hover":{color:"inherit"},"&::after":{position:"absolute",inset:0,content:'""'}},[`${o}-item-extra`]:{paddingInlineStart:e.padding,marginInlineStart:"auto",fontSize:e.fontSizeSM,color:e.colorTextDescription}},[`${o}-item, ${o}-submenu-title`]:Object.assign(Object.assign({display:"flex",margin:0,padding:`${(0,P.unit)(s)} ${(0,P.unit)(g)}`,color:e.colorText,fontWeight:"normal",fontSize:u,lineHeight:e.lineHeight,cursor:"pointer",transition:`all ${d}`,borderRadius:e.borderRadiusSM,"&:hover, &-active":{backgroundColor:e.controlItemBgHover}},(0,D.genFocusStyle)(e)),{"&-selected":{color:e.colorPrimary,backgroundColor:e.controlItemBgActive,"&:hover, &-active":{backgroundColor:e.controlItemBgActiveHover}},"&-disabled":{color:m,cursor:"not-allowed","&:hover":{color:m,backgroundColor:$,cursor:"not-allowed"},a:{pointerEvents:"none"}},"&-divider":{height:1,margin:`${(0,P.unit)(e.marginXXS)} 0`,overflow:"hidden",lineHeight:0,backgroundColor:e.colorSplit},[`${t}-menu-submenu-expand-icon`]:{position:"absolute",insetInlineEnd:e.paddingXS,[`${t}-menu-submenu-arrow-icon`]:{marginInlineEnd:"0 !important",color:e.colorIcon,fontSize:p,fontStyle:"normal"}}}),[`${o}-item-group-list`]:{margin:`0 ${(0,P.unit)(e.marginXS)}`,padding:0,listStyle:"none"},[`${o}-submenu-title`]:{paddingInlineEnd:e.calc(g).add(e.fontSizeSM).equal()},[`${o}-submenu-vertical`]:{position:"relative"},[`${o}-submenu${o}-submenu-disabled ${t}-menu-submenu-title`]:{[`&, ${t}-menu-submenu-arrow-icon`]:{color:m,backgroundColor:$,cursor:"not-allowed"}},[`${o}-submenu-selected ${t}-menu-submenu-title`]:{color:e.colorPrimary}})})},[(0,L.initSlideMotion)(e,"slide-up"),(0,L.initSlideMotion)(e,"slide-down"),(0,er.initMoveMotion)(e,"move-up"),(0,er.initMoveMotion)(e,"move-down"),(0,W.initZoomMotion)(e,"zoom-big")]]})(r),(e=>{let{componentCls:t,menuCls:o,colorError:n,colorTextLightSolid:i}=e,r=`${o}-item`;return{[`${t}, ${t}-menu-submenu`]:{[`${o} ${r}`]:{[`&${r}-danger:not(${r}-disabled)`]:{color:n,"&:hover":{color:i,backgroundColor:n}}}}}})(r)]},e=>Object.assign(Object.assign({zIndexPopup:e.zIndexPopupBase+50,paddingBlock:(e.controlHeight-e.fontSize*e.lineHeight)/2},(0,el.getArrowOffsetToken)({contentRadius:e.borderRadiusLG,limitVerticalRadius:!0})),(0,ea.getArrowToken)(e)),{resetStyle:!1}),es=e=>{var m;let{menu:v,arrow:h,prefixCls:x,children:C,trigger:I,disabled:y,dropdownRender:S,popupRender:w,getPopupContainer:B,overlayClassName:O,rootClassName:k,overlayStyle:E,open:H,onOpenChange:j,visible:z,onVisibleChange:T,mouseEnterDelay:N=.15,mouseLeaveDelay:P=.1,autoAdjustOverflow:M=!0,placement:D="",overlay:A,transitionName:L,destroyOnHidden:W,destroyPopupOnHide:q}=e,{getPopupContainer:X,getPrefixCls:F,direction:Y,dropdown:G}=t.useContext(b.ConfigContext),_=w||S;(0,g.devUseWarning)("Dropdown");let U=t.useMemo(()=>{let e=F();return void 0!==L?L:D.includes("top")?`${e}-slide-down`:`${e}-slide-up`},[F,D,L]),V=t.useMemo(()=>D?D.includes("Center")?D.slice(0,D.indexOf("Center")):D:"rtl"===Y?"bottomRight":"bottomLeft",[D,Y]),Z=F("dropdown",x),K=(0,f.default)(Z),[Q,J,ee]=ed(Z,K),[,et]=(0,ei.useToken)(),eo=t.Children.only((0,u.default)(C)?t.createElement("span",null,C):C),er=(0,p.cloneElement)(eo,{className:(0,i.default)(`${Z}-trigger`,{[`${Z}-rtl`]:"rtl"===Y},eo.props.className),disabled:null!=(m=eo.props.disabled)?m:y}),el=y?[]:I,ea=!!(null==el?void 0:el.includes("contextMenu")),[es,eu]=(0,a.default)(!1,{value:null!=H?H:z}),ec=(0,l.default)(e=>{null==j||j(e,{source:"trigger"}),null==T||T(e),eu(e)}),em=(0,i.default)(O,k,J,ee,K,null==G?void 0:G.className,{[`${Z}-rtl`]:"rtl"===Y}),ep=(0,c.default)({arrowPointAtCenter:"object"==typeof h&&h.pointAtCenter,autoAdjustOverflow:M,offset:et.marginXXS,arrowWidth:h?et.sizePopupArrow:0,borderRadius:et.borderRadius}),eg=(0,l.default)(()=>{null!=v&&v.selectable&&null!=v&&v.multiple||(null==j||j(!1,{source:"menu"}),eu(!1))}),[e$,eb]=(0,s.useZIndex)("Dropdown",null==E?void 0:E.zIndex),ef=t.createElement(r.default,Object.assign({alignPoint:ea},(0,d.default)(e,["rootClassName"]),{mouseEnterDelay:N,mouseLeaveDelay:P,visible:es,builtinPlacements:ep,arrow:!!h,overlayClassName:em,prefixCls:Z,getPopupContainer:B||X,transitionName:U,trigger:el,overlay:()=>{let e;return e=(null==v?void 0:v.items)?t.createElement(en,Object.assign({},v)):"function"==typeof A?A():A,_&&(e=_(e)),e=t.Children.only("string"==typeof e?t.createElement("span",null,e):e),t.createElement(R,{prefixCls:`${Z}-menu`,rootClassName:(0,i.default)(ee,K),expandIcon:t.createElement("span",{className:`${Z}-menu-submenu-arrow`},"rtl"===Y?t.createElement(o.default,{className:`${Z}-menu-submenu-arrow-icon`}):t.createElement(n.default,{className:`${Z}-menu-submenu-arrow-icon`})),mode:"vertical",selectable:!1,onClick:eg,validator:({mode:e})=>{}},e)},placement:V,onVisibleChange:ec,overlayStyle:Object.assign(Object.assign(Object.assign({},null==G?void 0:G.style),E),{zIndex:e$}),autoDestroy:null!=W?W:q}),er);return e$&&(ef=t.createElement($.default.Provider,{value:eb},ef)),Q(ef)},eu=(0,m.default)(es,"align",void 0,"dropdown",e=>e);es._InternalPanelDoNotUseOrYouWillBeFired=e=>t.createElement(eu,Object.assign({},e),t.createElement("span",null));var ec=e.i(920228),em=e.i(38243),ep=e.i(249616),eg=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(o[n[i]]=e[n[i]]);return o};let e$=e=>{let{getPopupContainer:o,getPrefixCls:n,direction:r}=t.useContext(b.ConfigContext),{prefixCls:l,type:a="default",danger:d,disabled:s,loading:u,onClick:c,htmlType:m,children:p,className:g,menu:$,arrow:f,autoFocus:v,overlay:h,trigger:x,align:I,open:y,onOpenChange:S,placement:w,getPopupContainer:B,href:O,icon:k=t.createElement(C.default,null),title:E,buttonsRender:H=e=>e,mouseEnterDelay:j,mouseLeaveDelay:z,overlayClassName:T,overlayStyle:N,destroyOnHidden:R,destroyPopupOnHide:P,dropdownRender:M,popupRender:D}=e,A=eg(e,["prefixCls","type","danger","disabled","loading","onClick","htmlType","children","className","menu","arrow","autoFocus","overlay","trigger","align","open","onOpenChange","placement","getPopupContainer","href","icon","title","buttonsRender","mouseEnterDelay","mouseLeaveDelay","overlayClassName","overlayStyle","destroyOnHidden","destroyPopupOnHide","dropdownRender","popupRender"]),L=n("dropdown",l),W=`${L}-button`,q={menu:$,arrow:f,autoFocus:v,align:I,disabled:s,trigger:s?[]:x,onOpenChange:S,getPopupContainer:B||o,mouseEnterDelay:j,mouseLeaveDelay:z,overlayClassName:T,overlayStyle:N,destroyOnHidden:R,popupRender:D||M},{compactSize:X,compactItemClassnames:F}=(0,ep.useCompactItemContext)(L,r),Y=(0,i.default)(W,F,g);"destroyPopupOnHide"in e&&(q.destroyPopupOnHide=P),"overlay"in e&&(q.overlay=h),"open"in e&&(q.open=y),"placement"in e?q.placement=w:q.placement="rtl"===r?"bottomLeft":"bottomRight";let[G,_]=H([t.createElement(ec.default,{type:a,danger:d,disabled:s,loading:u,onClick:c,htmlType:m,href:O,title:E},p),t.createElement(ec.default,{type:a,danger:d,icon:k})]);return t.createElement(em.default.Compact,Object.assign({className:Y,size:X,block:!0},A),G,t.createElement(es,Object.assign({},q),_))};e$.__ANT_BUTTON=!0,es.Button=e$,e.s(["default",0,es],21539)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/04s-iyzsr4cq~.js b/litellm/proxy/_experimental/out/_next/static/chunks/04s-iyzsr4cq~.js new file mode 100644 index 00000000000..1a419a22465 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/04s-iyzsr4cq~.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,343084,e=>{"use strict";let t=["top","right","bottom","left"],n=t.reduce((e,t)=>e.concat(t,t+"-start",t+"-end"),[]),i=Math.min,l=Math.max,o=Math.round,r=Math.floor,a={left:"right",right:"left",bottom:"top",top:"bottom"};function s(e){return e.split("-")[0]}function f(e){return e.split("-")[1]}function c(e){return"x"===e?"y":"x"}function u(e){return"y"===e?"height":"width"}function g(e){let t=e[0];return"t"===t||"b"===t?"y":"x"}function d(e){return c(g(e))}function m(e){return e.includes("start")?e.replace("start","end"):e.replace("end","start")}let h=["left","right"],p=["right","left"],x=["top","bottom"],v=["bottom","top"];function w(e){let t=s(e);return a[t]+e.slice(t.length)}e.s(["clamp",0,function(e,t,n){return l(e,i(t,n))},"createCoords",0,e=>({x:e,y:e}),"evaluate",0,function(e,t){return"function"==typeof e?e(t):e},"floor",0,r,"getAlignment",0,f,"getAlignmentAxis",0,d,"getAlignmentSides",0,function(e,t,n){void 0===n&&(n=!1);let i=f(e),l=d(e),o=u(l),r="x"===l?i===(n?"end":"start")?"right":"left":"start"===i?"bottom":"top";return t.reference[o]>t.floating[o]&&(r=w(r)),[r,w(r)]},"getAxisLength",0,u,"getExpandedPlacements",0,function(e){let t=w(e);return[m(e),t,m(t)]},"getOppositeAlignmentPlacement",0,m,"getOppositeAxis",0,c,"getOppositeAxisPlacements",0,function(e,t,n,i){let l=f(e),o=function(e,t,n){switch(e){case"top":case"bottom":if(n)return t?p:h;return t?h:p;case"left":case"right":return t?x:v;default:return[]}}(s(e),"start"===n,i);return l&&(o=o.map(e=>e+"-"+l),t&&(o=o.concat(o.map(m)))),o},"getOppositePlacement",0,w,"getPaddingObject",0,function(e){return"number"!=typeof e?{top:0,right:0,bottom:0,left:0,...e}:{top:e,right:e,bottom:e,left:e}},"getSide",0,s,"getSideAxis",0,g,"max",0,l,"min",0,i,"placements",0,n,"rectToClientRect",0,function(e){let{x:t,y:n,width:i,height:l}=e;return{width:i,height:l,top:n,left:t,right:t+i,bottom:n+l,x:t,y:n}},"round",0,o,"sides",0,t])},953760,e=>{"use strict";var t=e.i(343084);function n(e,n,i){let l,{reference:o,floating:r}=e,a=(0,t.getSideAxis)(n),s=(0,t.getAlignmentAxis)(n),f=(0,t.getAxisLength)(s),c=(0,t.getSide)(n),u="y"===a,g=o.x+o.width/2-r.width/2,d=o.y+o.height/2-r.height/2,m=o[f]/2-r[f]/2;switch(c){case"top":l={x:g,y:o.y-r.height};break;case"bottom":l={x:g,y:o.y+o.height};break;case"right":l={x:o.x+o.width,y:d};break;case"left":l={x:o.x-r.width,y:d};break;default:l={x:o.x,y:o.y}}switch((0,t.getAlignment)(n)){case"start":l[s]-=m*(i&&u?-1:1);break;case"end":l[s]+=m*(i&&u?-1:1)}return l}async function i(e,n){var i;void 0===n&&(n={});let{x:l,y:o,platform:r,rects:a,elements:s,strategy:f}=e,{boundary:c="clippingAncestors",rootBoundary:u="viewport",elementContext:g="floating",altBoundary:d=!1,padding:m=0}=(0,t.evaluate)(n,e),h=(0,t.getPaddingObject)(m),p=s[d?"floating"===g?"reference":"floating":g],x=(0,t.rectToClientRect)(await r.getClippingRect({element:null==(i=await (null==r.isElement?void 0:r.isElement(p)))||i?p:p.contextElement||await (null==r.getDocumentElement?void 0:r.getDocumentElement(s.floating)),boundary:c,rootBoundary:u,strategy:f})),v="floating"===g?{x:l,y:o,width:a.floating.width,height:a.floating.height}:a.reference,w=await (null==r.getOffsetParent?void 0:r.getOffsetParent(s.floating)),y=await (null==r.isElement?void 0:r.isElement(w))&&await (null==r.getScale?void 0:r.getScale(w))||{x:1,y:1},b=(0,t.rectToClientRect)(r.convertOffsetParentRelativeRectToViewportRelativeRect?await r.convertOffsetParentRelativeRectToViewportRelativeRect({elements:s,rect:v,offsetParent:w,strategy:f}):v);return{top:(x.top-b.top+h.top)/y.y,bottom:(b.bottom-x.bottom+h.bottom)/y.y,left:(x.left-b.left+h.left)/y.x,right:(b.right-x.right+h.right)/y.x}}let l=async(e,t,l)=>{let{placement:o="bottom",strategy:r="absolute",middleware:a=[],platform:s}=l,f=s.detectOverflow?s:{...s,detectOverflow:i},c=await (null==s.isRTL?void 0:s.isRTL(t)),u=await s.getElementRects({reference:e,floating:t,strategy:r}),{x:g,y:d}=n(u,o,c),m=o,h=0,p={};for(let i=0;ie[t]>=0)}function a(e){let n=(0,t.min)(...e.map(e=>e.left)),i=(0,t.min)(...e.map(e=>e.top));return{x:n,y:i,width:(0,t.max)(...e.map(e=>e.right))-n,height:(0,t.max)(...e.map(e=>e.bottom))-i}}let s=new Set(["left","top"]);async function f(e,n){let{placement:i,platform:l,elements:o}=e,r=await (null==l.isRTL?void 0:l.isRTL(o.floating)),a=(0,t.getSide)(i),f=(0,t.getAlignment)(i),c="y"===(0,t.getSideAxis)(i),u=s.has(a)?-1:1,g=r&&c?-1:1,d=(0,t.evaluate)(n,e),{mainAxis:m,crossAxis:h,alignmentAxis:p}="number"==typeof d?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:d.mainAxis||0,crossAxis:d.crossAxis||0,alignmentAxis:d.alignmentAxis};return f&&"number"==typeof p&&(h="end"===f?-1*p:p),c?{x:h*g,y:m*u}:{x:m*u,y:h*g}}var c=e.i(229315);function u(e){let n=(0,c.getComputedStyle)(e),i=parseFloat(n.width)||0,l=parseFloat(n.height)||0,o=(0,c.isHTMLElement)(e),r=o?e.offsetWidth:i,a=o?e.offsetHeight:l,s=(0,t.round)(i)!==r||(0,t.round)(l)!==a;return s&&(i=r,l=a),{width:i,height:l,$:s}}function g(e){return(0,c.isElement)(e)?e:e.contextElement}function d(e){let n=g(e);if(!(0,c.isHTMLElement)(n))return(0,t.createCoords)(1);let i=n.getBoundingClientRect(),{width:l,height:o,$:r}=u(n),a=(r?(0,t.round)(i.width):i.width)/l,s=(r?(0,t.round)(i.height):i.height)/o;return a&&Number.isFinite(a)||(a=1),s&&Number.isFinite(s)||(s=1),{x:a,y:s}}let m=(0,t.createCoords)(0);function h(e){let t=(0,c.getWindow)(e);return(0,c.isWebKit)()&&t.visualViewport?{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}:m}function p(e,n,i,l){var o;void 0===n&&(n=!1),void 0===i&&(i=!1);let r=e.getBoundingClientRect(),a=g(e),s=(0,t.createCoords)(1);n&&(l?(0,c.isElement)(l)&&(s=d(l)):s=d(e));let f=(void 0===(o=i)&&(o=!1),l&&(!o||l===(0,c.getWindow)(a))&&o)?h(a):(0,t.createCoords)(0),u=(r.left+f.x)/s.x,m=(r.top+f.y)/s.y,p=r.width/s.x,x=r.height/s.y;if(a){let e=(0,c.getWindow)(a),t=l&&(0,c.isElement)(l)?(0,c.getWindow)(l):l,n=e,i=(0,c.getFrameElement)(n);for(;i&&l&&t!==n;){let e=d(i),t=i.getBoundingClientRect(),l=(0,c.getComputedStyle)(i),o=t.left+(i.clientLeft+parseFloat(l.paddingLeft))*e.x,r=t.top+(i.clientTop+parseFloat(l.paddingTop))*e.y;u*=e.x,m*=e.y,p*=e.x,x*=e.y,u+=o,m+=r,n=(0,c.getWindow)(i),i=(0,c.getFrameElement)(n)}}return(0,t.rectToClientRect)({width:p,height:x,x:u,y:m})}function x(e,t){let n=(0,c.getNodeScroll)(e).scrollLeft;return t?t.left+n:p((0,c.getDocumentElement)(e)).left+n}function v(e,t){let n=e.getBoundingClientRect();return{x:n.left+t.scrollLeft-x(e,n),y:n.top+t.scrollTop}}function w(e,n,i){var l;let o;if("viewport"===n)o=function(e,t){let n=(0,c.getWindow)(e),i=(0,c.getDocumentElement)(e),l=n.visualViewport,o=i.clientWidth,r=i.clientHeight,a=0,s=0;if(l){o=l.width,r=l.height;let e=(0,c.isWebKit)();(!e||e&&"fixed"===t)&&(a=l.offsetLeft,s=l.offsetTop)}let f=x(i);if(f<=0){let e=i.ownerDocument,t=e.body,n=getComputedStyle(t),l="CSS1Compat"===e.compatMode&&parseFloat(n.marginLeft)+parseFloat(n.marginRight)||0,r=Math.abs(i.clientWidth-t.clientWidth-l);r<=25&&(o-=r)}else f<=25&&(o+=f);return{width:o,height:r,x:a,y:s}}(e,i);else if("document"===n){let n,i,r,a,s,f,u;l=(0,c.getDocumentElement)(e),n=(0,c.getDocumentElement)(l),i=(0,c.getNodeScroll)(l),r=l.ownerDocument.body,a=(0,t.max)(n.scrollWidth,n.clientWidth,r.scrollWidth,r.clientWidth),s=(0,t.max)(n.scrollHeight,n.clientHeight,r.scrollHeight,r.clientHeight),f=-i.scrollLeft+x(l),u=-i.scrollTop,"rtl"===(0,c.getComputedStyle)(r).direction&&(f+=(0,t.max)(n.clientWidth,r.clientWidth)-a),o={width:a,height:s,x:f,y:u}}else if((0,c.isElement)(n)){let e,l,r,a,s,f;l=(e=p(n,!0,"fixed"===i)).top+n.clientTop,r=e.left+n.clientLeft,a=(0,c.isHTMLElement)(n)?d(n):(0,t.createCoords)(1),s=n.clientWidth*a.x,f=n.clientHeight*a.y,o={width:s,height:f,x:r*a.x,y:l*a.y}}else{let t=h(e);o={x:n.x-t.x,y:n.y-t.y,width:n.width,height:n.height}}return(0,t.rectToClientRect)(o)}function y(e){return"static"===(0,c.getComputedStyle)(e).position}function b(e,t){if(!(0,c.isHTMLElement)(e)||"fixed"===(0,c.getComputedStyle)(e).position)return null;if(t)return t(e);let n=e.offsetParent;return(0,c.getDocumentElement)(e)===n&&(n=n.ownerDocument.body),n}function A(e,t){let n=(0,c.getWindow)(e);if((0,c.isTopLayer)(e))return n;if(!(0,c.isHTMLElement)(e)){let t=(0,c.getParentNode)(e);for(;t&&!(0,c.isLastTraversableNode)(t);){if((0,c.isElement)(t)&&!y(t))return t;t=(0,c.getParentNode)(t)}return n}let i=b(e,t);for(;i&&(0,c.isTableElement)(i)&&y(i);)i=b(i,t);return i&&(0,c.isLastTraversableNode)(i)&&y(i)&&!(0,c.isContainingBlock)(i)?n:i||(0,c.getContainingBlock)(e)||n}let T=async function(e){let n=this.getOffsetParent||A,i=this.getDimensions,l=await i(e.floating);return{reference:function(e,n,i){let l=(0,c.isHTMLElement)(n),o=(0,c.getDocumentElement)(n),r="fixed"===i,a=p(e,!0,r,n),s={scrollLeft:0,scrollTop:0},f=(0,t.createCoords)(0);if(l||!l&&!r)if(("body"!==(0,c.getNodeName)(n)||(0,c.isOverflowElement)(o))&&(s=(0,c.getNodeScroll)(n)),l){let e=p(n,!0,r,n);f.x=e.x+n.clientLeft,f.y=e.y+n.clientTop}else o&&(f.x=x(o));r&&!l&&o&&(f.x=x(o));let u=!o||l||r?(0,t.createCoords)(0):v(o,s);return{x:a.left+s.scrollLeft-f.x-u.x,y:a.top+s.scrollTop-f.y-u.y,width:a.width,height:a.height}}(e.reference,await n(e.floating),e.strategy),floating:{x:0,y:0,width:l.width,height:l.height}}},E={convertOffsetParentRelativeRectToViewportRelativeRect:function(e){let{elements:n,rect:i,offsetParent:l,strategy:o}=e,r="fixed"===o,a=(0,c.getDocumentElement)(l),s=!!n&&(0,c.isTopLayer)(n.floating);if(l===a||s&&r)return i;let f={scrollLeft:0,scrollTop:0},u=(0,t.createCoords)(1),g=(0,t.createCoords)(0),m=(0,c.isHTMLElement)(l);if((m||!m&&!r)&&(("body"!==(0,c.getNodeName)(l)||(0,c.isOverflowElement)(a))&&(f=(0,c.getNodeScroll)(l)),m)){let e=p(l);u=d(l),g.x=e.x+l.clientLeft,g.y=e.y+l.clientTop}let h=!a||m||r?(0,t.createCoords)(0):v(a,f);return{width:i.width*u.x,height:i.height*u.y,x:i.x*u.x-f.scrollLeft*u.x+g.x+h.x,y:i.y*u.y-f.scrollTop*u.y+g.y+h.y}},getDocumentElement:c.getDocumentElement,getClippingRect:function(e){let{element:n,boundary:i,rootBoundary:l,strategy:o}=e,r=[..."clippingAncestors"===i?(0,c.isTopLayer)(n)?[]:function(e,t){let n=t.get(e);if(n)return n;let i=(0,c.getOverflowAncestors)(e,[],!1).filter(e=>(0,c.isElement)(e)&&"body"!==(0,c.getNodeName)(e)),l=null,o="fixed"===(0,c.getComputedStyle)(e).position,r=o?(0,c.getParentNode)(e):e;for(;(0,c.isElement)(r)&&!(0,c.isLastTraversableNode)(r);){let t=(0,c.getComputedStyle)(r),n=(0,c.isContainingBlock)(r);n||"fixed"!==t.position||(l=null),(o?n||l:!(!n&&"static"===t.position&&l&&("absolute"===l.position||"fixed"===l.position)||(0,c.isOverflowElement)(r)&&!n&&function e(t,n){let i=(0,c.getParentNode)(t);return!(i===n||!(0,c.isElement)(i)||(0,c.isLastTraversableNode)(i))&&("fixed"===(0,c.getComputedStyle)(i).position||e(i,n))}(e,r)))?l=t:i=i.filter(e=>e!==r),r=(0,c.getParentNode)(r)}return t.set(e,i),i}(n,this._c):[].concat(i),l],a=w(n,r[0],o),s=a.top,f=a.right,u=a.bottom,g=a.left;for(let e=1;e({name:"arrow",options:e,async fn(n){let{x:i,y:l,placement:o,rects:r,platform:a,elements:s,middlewareData:f}=n,{element:c,padding:u=0}=(0,t.evaluate)(e,n)||{};if(null==c)return{};let g=(0,t.getPaddingObject)(u),d={x:i,y:l},m=(0,t.getAlignmentAxis)(o),h=(0,t.getAxisLength)(m),p=await a.getDimensions(c),x="y"===m,v=x?"clientHeight":"clientWidth",w=r.reference[h]+r.reference[m]-d[m]-r.floating[h],y=d[m]-r.reference[m],b=await (null==a.getOffsetParent?void 0:a.getOffsetParent(c)),A=b?b[v]:0;A&&await (null==a.isElement?void 0:a.isElement(b))||(A=s.floating[v]||r.floating[h]);let T=A/2-p[h]/2-1,E=(0,t.min)(g[x?"top":"left"],T),R=(0,t.min)(g[x?"bottom":"right"],T),C=A-p[h]-R,L=A/2-p[h]/2+(w/2-y/2),O=(0,t.clamp)(E,L,C),S=!f.arrow&&null!=(0,t.getAlignment)(o)&&L!==O&&r.reference[h]/2-(L(0,t.getAlignment)(e)===r),...m.filter(e=>(0,t.getAlignment)(e)!==r)]:m.filter(e=>(0,t.getSide)(e)===e)).filter(e=>!r||(0,t.getAlignment)(e)===r||!!h&&(0,t.getOppositeAlignmentPlacement)(e)!==e):m,v=await c.detectOverflow(n,p),w=(null==(i=s.autoPlacement)?void 0:i.index)||0,y=x[w];if(null==y)return{};let b=(0,t.getAlignmentSides)(y,a,await (null==c.isRTL?void 0:c.isRTL(u.floating)));if(f!==y)return{reset:{placement:x[0]}};let A=[v[(0,t.getSide)(y)],v[b[0]],v[b[1]]],T=[...(null==(l=s.autoPlacement)?void 0:l.overflows)||[],{placement:y,overflows:A}],E=x[w+1];if(E)return{data:{index:w+1,overflows:T},reset:{placement:E}};let R=T.map(e=>{let n=(0,t.getAlignment)(e.placement);return[e.placement,n&&g?e.overflows.slice(0,2).reduce((e,t)=>e+t,0):e.overflows[0],e.overflows]}).sort((e,t)=>e[1]-t[1]),C=(null==(o=R.filter(e=>e[2].slice(0,(0,t.getAlignment)(e[0])?2:3).every(e=>e<=0))[0])?void 0:o[0])||R[0][0];return C!==f?{data:{index:w+1,overflows:T},reset:{placement:C}}:{}}}},"autoUpdate",0,function(e,n,i,l){let o;void 0===l&&(l={});let{ancestorScroll:r=!0,ancestorResize:a=!0,elementResize:s="function"==typeof ResizeObserver,layoutShift:f="function"==typeof IntersectionObserver,animationFrame:u=!1}=l,d=g(e),m=r||a?[...d?(0,c.getOverflowAncestors)(d):[],...n?(0,c.getOverflowAncestors)(n):[]]:[];m.forEach(e=>{r&&e.addEventListener("scroll",i,{passive:!0}),a&&e.addEventListener("resize",i)});let h=d&&f?function(e,n){let i,l=null,o=(0,c.getDocumentElement)(e);function r(){var e;clearTimeout(i),null==(e=l)||e.disconnect(),l=null}return!function a(s,f){void 0===s&&(s=!1),void 0===f&&(f=1),r();let c=e.getBoundingClientRect(),{left:u,top:g,width:d,height:m}=c;if(s||n(),!d||!m)return;let h={rootMargin:-(0,t.floor)(g)+"px "+-(0,t.floor)(o.clientWidth-(u+d))+"px "+-(0,t.floor)(o.clientHeight-(g+m))+"px "+-(0,t.floor)(u)+"px",threshold:(0,t.max)(0,(0,t.min)(1,f))||1},p=!0;function x(t){let n=t[0].intersectionRatio;if(n!==f){if(!p)return a();n?a(!1,n):i=setTimeout(()=>{a(!1,1e-7)},1e3)}1!==n||R(c,e.getBoundingClientRect())||a(),p=!1}try{l=new IntersectionObserver(x,{...h,root:o.ownerDocument})}catch(e){l=new IntersectionObserver(x,h)}l.observe(e)}(!0),r}(d,i):null,x=-1,v=null;s&&(v=new ResizeObserver(e=>{let[t]=e;t&&t.target===d&&v&&n&&(v.unobserve(n),cancelAnimationFrame(x),x=requestAnimationFrame(()=>{var e;null==(e=v)||e.observe(n)})),i()}),d&&!u&&v.observe(d),n&&v.observe(n));let w=u?p(e):null;return u&&function t(){let n=p(e);w&&!R(w,n)&&i(),w=n,o=requestAnimationFrame(t)}(),i(),()=>{var e;m.forEach(e=>{r&&e.removeEventListener("scroll",i),a&&e.removeEventListener("resize",i)}),null==h||h(),null==(e=v)||e.disconnect(),v=null,u&&cancelAnimationFrame(o)}},"computePosition",0,(e,t,n)=>{let i=new Map,o={platform:E,...n},r={...o.platform,_c:i};return l(e,t,{...o,platform:r})},"detectOverflow",0,i,"flip",0,function(e){return void 0===e&&(e={}),{name:"flip",options:e,async fn(n){var i,l,o,r,a;let{placement:s,middlewareData:f,rects:c,initialPlacement:u,platform:g,elements:d}=n,{mainAxis:m=!0,crossAxis:h=!0,fallbackPlacements:p,fallbackStrategy:x="bestFit",fallbackAxisSideDirection:v="none",flipAlignment:w=!0,...y}=(0,t.evaluate)(e,n);if(null!=(i=f.arrow)&&i.alignmentOffset)return{};let b=(0,t.getSide)(s),A=(0,t.getSideAxis)(u),T=(0,t.getSide)(u)===u,E=await (null==g.isRTL?void 0:g.isRTL(d.floating)),R=p||(T||!w?[(0,t.getOppositePlacement)(u)]:(0,t.getExpandedPlacements)(u)),C="none"!==v;!p&&C&&R.push(...(0,t.getOppositeAxisPlacements)(u,w,v,E));let L=[u,...R],O=await g.detectOverflow(n,y),S=[],P=(null==(l=f.flip)?void 0:l.overflows)||[];if(m&&S.push(O[b]),h){let e=(0,t.getAlignmentSides)(s,c,E);S.push(O[e[0]],O[e[1]])}if(P=[...P,{placement:s,overflows:S}],!S.every(e=>e<=0)){let e=((null==(o=f.flip)?void 0:o.index)||0)+1,n=L[e];if(n&&("alignment"!==h||A===(0,t.getSideAxis)(n)||P.every(e=>(0,t.getSideAxis)(e.placement)!==A||e.overflows[0]>0)))return{data:{index:e,overflows:P},reset:{placement:n}};let i=null==(r=P.filter(e=>e.overflows[0]<=0).sort((e,t)=>e.overflows[1]-t.overflows[1])[0])?void 0:r.placement;if(!i)switch(x){case"bestFit":{let e=null==(a=P.filter(e=>{if(C){let n=(0,t.getSideAxis)(e.placement);return n===A||"y"===n}return!0}).map(e=>[e.placement,e.overflows.filter(e=>e>0).reduce((e,t)=>e+t,0)]).sort((e,t)=>e[1]-t[1])[0])?void 0:a[0];e&&(i=e);break}case"initialPlacement":i=u}if(s!==i)return{reset:{placement:i}}}return{}}}},"hide",0,function(e){return void 0===e&&(e={}),{name:"hide",options:e,async fn(n){let{rects:i,platform:l}=n,{strategy:a="referenceHidden",...s}=(0,t.evaluate)(e,n);switch(a){case"referenceHidden":{let e=o(await l.detectOverflow(n,{...s,elementContext:"reference"}),i.reference);return{data:{referenceHiddenOffsets:e,referenceHidden:r(e)}}}case"escaped":{let e=o(await l.detectOverflow(n,{...s,altBoundary:!0}),i.floating);return{data:{escapedOffsets:e,escaped:r(e)}}}default:return{}}}}},"inline",0,function(e){return void 0===e&&(e={}),{name:"inline",options:e,async fn(n){let{placement:i,elements:l,rects:o,platform:r,strategy:s}=n,{padding:f=2,x:c,y:u}=(0,t.evaluate)(e,n),g=Array.from(await (null==r.getClientRects?void 0:r.getClientRects(l.reference))||[]),d=function(e){let n=e.slice().sort((e,t)=>e.y-t.y),i=[],l=null;for(let e=0;el.height/2?i.push([t]):i[i.length-1].push(t),l=t}return i.map(e=>(0,t.rectToClientRect)(a(e)))}(g),m=(0,t.rectToClientRect)(a(g)),h=(0,t.getPaddingObject)(f),p=await r.getElementRects({reference:{getBoundingClientRect:function(){if(2===d.length&&d[0].left>d[1].right&&null!=c&&null!=u)return d.find(e=>c>e.left-h.left&&ce.top-h.top&&u=2){if("y"===(0,t.getSideAxis)(i)){let e=d[0],n=d[d.length-1],l="top"===(0,t.getSide)(i),o=e.top,r=n.bottom,a=l?e.left:n.left,s=l?e.right:n.right;return{top:o,bottom:r,left:a,right:s,width:s-a,height:r-o,x:a,y:o}}let e="left"===(0,t.getSide)(i),n=(0,t.max)(...d.map(e=>e.right)),l=(0,t.min)(...d.map(e=>e.left)),o=d.filter(t=>e?t.left===l:t.right===n),r=o[0].top,a=o[o.length-1].bottom;return{top:r,bottom:a,left:l,right:n,width:n-l,height:a-r,x:l,y:r}}return m}},floating:l.floating,strategy:s});return o.reference.x!==p.reference.x||o.reference.y!==p.reference.y||o.reference.width!==p.reference.width||o.reference.height!==p.reference.height?{reset:{rects:p}}:{}}}},"limitShift",0,function(e){return void 0===e&&(e={}),{options:e,fn(n){let{x:i,y:l,placement:o,rects:r,middlewareData:a}=n,{offset:f=0,mainAxis:c=!0,crossAxis:u=!0}=(0,t.evaluate)(e,n),g={x:i,y:l},d=(0,t.getSideAxis)(o),m=(0,t.getOppositeAxis)(d),h=g[m],p=g[d],x=(0,t.evaluate)(f,n),v="number"==typeof x?{mainAxis:x,crossAxis:0}:{mainAxis:0,crossAxis:0,...x};if(c){let e="y"===m?"height":"width",t=r.reference[m]-r.floating[e]+v.mainAxis,n=r.reference[m]+r.reference[e]-v.mainAxis;hn&&(h=n)}if(u){var w,y;let e="y"===m?"width":"height",n=s.has((0,t.getSide)(o)),i=r.reference[d]-r.floating[e]+(n&&(null==(w=a.offset)?void 0:w[d])||0)+(n?0:v.crossAxis),l=r.reference[d]+r.reference[e]+(n?0:(null==(y=a.offset)?void 0:y[d])||0)-(n?v.crossAxis:0);pl&&(p=l)}return{[m]:h,[d]:p}}}},"offset",0,function(e){return void 0===e&&(e=0),{name:"offset",options:e,async fn(t){var n,i;let{x:l,y:o,placement:r,middlewareData:a}=t,s=await f(t,e);return r===(null==(n=a.offset)?void 0:n.placement)&&null!=(i=a.arrow)&&i.alignmentOffset?{}:{x:l+s.x,y:o+s.y,data:{...s,placement:r}}}}},"platform",0,E,"shift",0,function(e){return void 0===e&&(e={}),{name:"shift",options:e,async fn(n){let{x:i,y:l,placement:o,platform:r}=n,{mainAxis:a=!0,crossAxis:s=!1,limiter:f={fn:e=>{let{x:t,y:n}=e;return{x:t,y:n}}},...c}=(0,t.evaluate)(e,n),u={x:i,y:l},g=await r.detectOverflow(n,c),d=(0,t.getSideAxis)((0,t.getSide)(o)),m=(0,t.getOppositeAxis)(d),h=u[m],p=u[d];if(a){let e="y"===m?"top":"left",n="y"===m?"bottom":"right",i=h+g[e],l=h-g[n];h=(0,t.clamp)(i,h,l)}if(s){let e="y"===d?"top":"left",n="y"===d?"bottom":"right",i=p+g[e],l=p-g[n];p=(0,t.clamp)(i,p,l)}let x=f.fn({...n,[m]:h,[d]:p});return{...x,data:{x:x.x-i,y:x.y-l,enabled:{[m]:a,[d]:s}}}}}},"size",0,function(e){return void 0===e&&(e={}),{name:"size",options:e,async fn(n){var i,l;let o,r,{placement:a,rects:s,platform:f,elements:c}=n,{apply:u=()=>{},...g}=(0,t.evaluate)(e,n),d=await f.detectOverflow(n,g),m=(0,t.getSide)(a),h=(0,t.getAlignment)(a),p="y"===(0,t.getSideAxis)(a),{width:x,height:v}=s.floating;"top"===m||"bottom"===m?(o=m,r=h===(await (null==f.isRTL?void 0:f.isRTL(c.floating))?"start":"end")?"left":"right"):(r=m,o="end"===h?"top":"bottom");let w=v-d.top-d.bottom,y=x-d.left-d.right,b=(0,t.min)(v-d[o],w),A=(0,t.min)(x-d[r],y),T=!n.middlewareData.shift,E=b,R=A;if(null!=(i=n.middlewareData.shift)&&i.enabled.x&&(R=y),null!=(l=n.middlewareData.shift)&&l.enabled.y&&(E=w),T&&!h){let e=(0,t.max)(d.left,0),n=(0,t.max)(d.right,0),i=(0,t.max)(d.top,0),l=(0,t.max)(d.bottom,0);p?R=x-2*(0!==e||0!==n?e+n:(0,t.max)(d.left,d.right)):E=v-2*(0!==i||0!==l?i+l:(0,t.max)(d.top,d.bottom))}await u({...n,availableWidth:R,availableHeight:E});let C=await f.getDimensions(c.floating);return x!==C.width||v!==C.height?{reset:{rects:!0}}:{}}}}],953760)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/04tc3ssviv_6d.js b/litellm/proxy/_experimental/out/_next/static/chunks/04tc3ssviv_6d.js new file mode 100644 index 00000000000..f867c51a8a2 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/04tc3ssviv_6d.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,166406,e=>{"use strict";var r=e.i(190144);e.s(["CopyOutlined",()=>r.default])},94629,e=>{"use strict";var r=e.i(271645);let o=r.forwardRef(function(e,o){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:o},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,o],94629)},728889,e=>{"use strict";var r=e.i(290571),o=e.i(271645),t=e.i(829087),a=e.i(480731),l=e.i(444755),s=e.i(673706),n=e.i(95779);let i={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},g=(0,s.makeClassName)("Icon"),m=o.default.forwardRef((e,m)=>{let{icon:u,variant:p="simple",tooltip:C,size:h=a.Sizes.SM,color:b,className:A}=e,v=(0,r.__rest)(e,["icon","variant","tooltip","size","color","className"]),f=((e,r)=>{switch(e){case"simple":return{textColor:r?(0,s.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:r?(0,s.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,l.tremorTwMerge)((0,s.getColorClassNames)(r,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:r?(0,s.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,l.tremorTwMerge)((0,s.getColorClassNames)(r,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:r?(0,s.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:r?(0,l.tremorTwMerge)((0,s.getColorClassNames)(r,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:r?(0,s.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,l.tremorTwMerge)((0,s.getColorClassNames)(r,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:r?(0,s.getColorClassNames)(r,n.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:r?(0,l.tremorTwMerge)((0,s.getColorClassNames)(r,n.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(p,b),{tooltipProps:x,getReferenceProps:k}=(0,t.useTooltip)();return o.default.createElement("span",Object.assign({ref:(0,s.mergeRefs)([m,x.refs.setReference]),className:(0,l.tremorTwMerge)(g("root"),"inline-flex shrink-0 items-center justify-center",f.bgColor,f.textColor,f.borderColor,f.ringColor,c[p].rounded,c[p].border,c[p].shadow,c[p].ring,i[h].paddingX,i[h].paddingY,A)},k,v),o.default.createElement(t.default,Object.assign({text:C},x)),o.default.createElement(u,{className:(0,l.tremorTwMerge)(g("icon"),"shrink-0",d[h].height,d[h].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var r=e.i(728889);e.s(["Icon",()=>r.default])},278587,e=>{"use strict";var r=e.i(271645);let o=r.forwardRef(function(e,o){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:o},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,o],278587)},591935,e=>{"use strict";var r=e.i(271645);let o=r.forwardRef(function(e,o){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:o},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,o],591935)},434626,e=>{"use strict";var r=e.i(271645);let o=r.forwardRef(function(e,o){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:o},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,o],434626)},551332,e=>{"use strict";var r=e.i(271645);let o=r.forwardRef(function(e,o){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:o},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,o],551332)},122577,e=>{"use strict";var r=e.i(271645);let o=r.forwardRef(function(e,o){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:o},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,o],122577)},902555,e=>{"use strict";var r=e.i(843476),o=e.i(591935),t=e.i(122577),a=e.i(278587),l=e.i(68155),s=e.i(360820),n=e.i(871943),i=e.i(434626),d=e.i(551332),c=e.i(592968),g=e.i(115504),m=e.i(752978);function u({icon:e,onClick:o,className:t,disabled:a,dataTestId:l}){return a?(0,r.jsx)(m.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":l}):(0,r.jsx)(m.Icon,{icon:e,size:"sm",onClick:o,className:(0,g.cx)("cursor-pointer",t),"data-testid":l})}let p={Edit:{icon:o.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:l.TrashIcon,className:"hover:text-red-600"},Test:{icon:t.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:a.RefreshIcon,className:"hover:text-green-600"},Up:{icon:s.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:n.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:i.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:o,disabled:t=!1,disabledTooltipText:a,dataTestId:l,variant:s}){let{icon:n,className:i}=p[s];return(0,r.jsx)(c.Tooltip,{title:t?a:o,children:(0,r.jsx)("span",{children:(0,r.jsx)(u,{icon:n,onClick:e,className:i,disabled:t,dataTestId:l})})})}],902555)},95779,e=>{"use strict";var r=e.i(480731);let o=[r.BaseColors.Blue,r.BaseColors.Cyan,r.BaseColors.Sky,r.BaseColors.Indigo,r.BaseColors.Violet,r.BaseColors.Purple,r.BaseColors.Fuchsia,r.BaseColors.Slate,r.BaseColors.Gray,r.BaseColors.Zinc,r.BaseColors.Neutral,r.BaseColors.Stone,r.BaseColors.Red,r.BaseColors.Orange,r.BaseColors.Amber,r.BaseColors.Yellow,r.BaseColors.Lime,r.BaseColors.Green,r.BaseColors.Emerald,r.BaseColors.Teal,r.BaseColors.Pink,r.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,o])},994388,e=>{"use strict";var r=e.i(290571),o=e.i(829087),t=e.i(271645);let a=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:a[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),s=e=>e?6:5,n=(e,r,o,t,a)=>{clearTimeout(t.current);let s=l(e);r(s),o.current=s,a&&a({current:s})};var i=e.i(480731),d=e.i(444755),c=e.i(673706);let g=e=>{var o=(0,r.__rest)(e,[]);return t.default.createElement("svg",Object.assign({},o,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),t.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),t.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let u={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},p=(e,r)=>{switch(e){case"primary":return{textColor:r?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:r?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:r?(0,c.getColorClassNames)(r,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:r?(0,c.getColorClassNames)(r,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:r?(0,c.getColorClassNames)(r,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:r?(0,c.getColorClassNames)(r,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:r?(0,c.getColorClassNames)(r,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:r?(0,c.getColorClassNames)(r,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:r?(0,d.tremorTwMerge)((0,c.getColorClassNames)(r,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:r?(0,c.getColorClassNames)(r,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:r?(0,c.getColorClassNames)(r,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:r?(0,c.getColorClassNames)(r,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},C=(0,c.makeClassName)("Button"),h=({loading:e,iconSize:r,iconPosition:o,Icon:a,needMargin:l,transitionStatus:s})=>{let n=l?o===i.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:r,exiting:r,exited:c};return e?t.default.createElement(g,{className:(0,d.tremorTwMerge)(C("icon"),"animate-spin shrink-0",n,m.default,m[s]),style:{transition:"width 150ms"}}):t.default.createElement(a,{className:(0,d.tremorTwMerge)(C("icon"),"shrink-0",r,n)})},b=t.default.forwardRef((e,a)=>{let{icon:g,iconPosition:m=i.HorizontalPositions.Left,size:b=i.Sizes.SM,color:A,variant:v="primary",disabled:f,loading:x=!1,loadingText:k,children:I,tooltip:w,className:T}=e,E=(0,r.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),O=x||f,_=void 0!==g||x,L=x&&k,M=!(!I&&!L),N=(0,d.tremorTwMerge)(u[b].height,u[b].width),S="light"!==v?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",R=p(v,A),y=("light"!==v?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:P,getReferenceProps:B}=(0,o.useTooltip)(300),[$,z]=(({enter:e=!0,exit:r=!0,preEnter:o,preExit:a,timeout:i,initialEntered:d,mountOnEnter:c,unmountOnExit:g,onStateChange:m}={})=>{let[u,p]=(0,t.useState)(()=>l(d?2:s(c))),C=(0,t.useRef)(u),h=(0,t.useRef)(0),[b,A]="object"==typeof i?[i.enter,i.exit]:[i,i],v=(0,t.useCallback)(()=>{let e=((e,r)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return s(r)}})(C.current._s,g);e&&n(e,p,C,h,m)},[m,g]);return[u,(0,t.useCallback)(t=>{let l=e=>{switch(n(e,p,C,h,m),e){case 1:b>=0&&(h.current=((...e)=>setTimeout(...e))(v,b));break;case 4:A>=0&&(h.current=((...e)=>setTimeout(...e))(v,A));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},i=C.current.isEnter;"boolean"!=typeof t&&(t=!i),t?i||l(e?+!o:2):i&&l(r?a?3:4:s(g))},[v,m,e,r,o,a,b,A,g]),v]})({timeout:50});return(0,t.useEffect)(()=>{z(x)},[x]),t.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([a,P.refs.setReference]),className:(0,d.tremorTwMerge)(C("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",S,y.paddingX,y.paddingY,y.fontSize,R.textColor,R.bgColor,R.borderColor,R.hoverBorderColor,O?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(p(v,A).hoverTextColor,p(v,A).hoverBgColor,p(v,A).hoverBorderColor),T),disabled:O},B,E),t.default.createElement(o.default,Object.assign({text:w},P)),_&&m!==i.HorizontalPositions.Right?t.default.createElement(h,{loading:x,iconSize:N,iconPosition:m,Icon:g,transitionStatus:$.status,needMargin:M}):null,L||I?t.default.createElement("span",{className:(0,d.tremorTwMerge)(C("text"),"text-tremor-default whitespace-nowrap")},L?k:I):null,_&&m===i.HorizontalPositions.Right?t.default.createElement(h,{loading:x,iconSize:N,iconPosition:m,Icon:g,transitionStatus:$.status,needMargin:M}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},599724,936325,e=>{"use strict";var r=e.i(95779),o=e.i(444755),t=e.i(673706),a=e.i(271645);let l=a.default.forwardRef((e,l)=>{let{color:s,className:n,children:i}=e;return a.default.createElement("p",{ref:l,className:(0,o.tremorTwMerge)("text-tremor-default",s?(0,t.getColorClassNames)(s,r.colorPalette.text).textColor:(0,o.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),n)},i)});l.displayName="Text",e.s(["default",0,l],936325),e.s(["Text",0,l],599724)},304967,e=>{"use strict";var r=e.i(290571),o=e.i(271645),t=e.i(480731),a=e.i(95779),l=e.i(444755),s=e.i(673706);let n=(0,s.makeClassName)("Card"),i=o.default.forwardRef((e,i)=>{let{decoration:d="",decorationColor:c,children:g,className:m}=e,u=(0,r.__rest)(e,["decoration","decorationColor","children","className"]);return o.default.createElement("div",Object.assign({ref:i,className:(0,l.tremorTwMerge)(n("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,s.getColorClassNames)(c,a.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case t.HorizontalPositions.Left:return"border-l-4";case t.VerticalPositions.Top:return"border-t-4";case t.HorizontalPositions.Right:return"border-r-4";case t.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},u),g)});i.displayName="Card",e.s(["Card",0,i],304967)},629569,e=>{"use strict";var r=e.i(290571),o=e.i(95779),t=e.i(444755),a=e.i(673706),l=e.i(271645);let s=l.default.forwardRef((e,s)=>{let{color:n,children:i,className:d}=e,c=(0,r.__rest)(e,["color","children","className"]);return l.default.createElement("p",Object.assign({ref:s,className:(0,t.tremorTwMerge)("font-medium text-tremor-title",n?(0,a.getColorClassNames)(n,o.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),i)});s.displayName="Title",e.s(["Title",0,s],629569)},350967,46757,e=>{"use strict";var r=e.i(290571),o=e.i(444755),t=e.i(673706),a=e.i(271645);let l={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},s={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},n={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},i={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,l,"gridColsLg",0,i,"gridColsMd",0,n,"gridColsSm",0,s],46757);let d=(0,t.makeClassName)("Grid"),c=(e,r)=>e&&Object.keys(r).includes(String(e))?r[e]:"",g=a.default.forwardRef((e,t)=>{let{numItems:g=1,numItemsSm:m,numItemsMd:u,numItemsLg:p,children:C,className:h}=e,b=(0,r.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),A=c(g,l),v=c(m,s),f=c(u,n),x=c(p,i),k=(0,o.tremorTwMerge)(A,v,f,x);return a.default.createElement("div",Object.assign({ref:t,className:(0,o.tremorTwMerge)(d("root"),"grid",k,h)},b),C)});g.displayName="Grid",e.s(["Grid",0,g],350967)},530212,e=>{"use strict";var r=e.i(271645);let o=r.forwardRef(function(e,o){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:o},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,o],530212)},695411,e=>{"use strict";var r=e.i(602869);let o=async e=>{try{let o=await (0,r.modelHubCall)(e);if(o?.data.length>0){let e=o.data.map(e=>({model_group:e.model_group,mode:e?.mode}));return e.sort((e,r)=>e.model_group.localeCompare(r.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}};e.s(["fetchAvailableModels",0,o])},597440,e=>{"use strict";e.i(247167);var r=e.i(931067),o=e.i(271645);let t={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var a=e.i(9583),l=o.forwardRef(function(e,l){return o.createElement(a.default,(0,r.default)({},e,{ref:l,icon:t}))});e.s(["default",0,l],597440)},955135,e=>{"use strict";var r=e.i(597440);e.s(["DeleteOutlined",()=>r.default])},309426,e=>{"use strict";var r=e.i(290571),o=e.i(444755),t=e.i(673706),a=e.i(271645),l=e.i(46757);let s=(0,t.makeClassName)("Col"),n=a.default.forwardRef((e,t)=>{let n,i,d,c,{numColSpan:g=1,numColSpanSm:m,numColSpanMd:u,numColSpanLg:p,children:C,className:h}=e,b=(0,r.__rest)(e,["numColSpan","numColSpanSm","numColSpanMd","numColSpanLg","children","className"]),A=(e,r)=>e&&Object.keys(r).includes(String(e))?r[e]:"";return a.default.createElement("div",Object.assign({ref:t,className:(0,o.tremorTwMerge)(s("root"),(n=A(g,l.colSpan),i=A(m,l.colSpanSm),d=A(u,l.colSpanMd),c=A(p,l.colSpanLg),(0,o.tremorTwMerge)(n,i,d,c)),h)},b),C)});n.displayName="Col",e.s(["Col",0,n],309426)},916925,e=>{"use strict";var r,o=e.i(555987),t=((r={}).A2A_Agent="A2A Agent",r.AI21="Ai21",r.AI21_CHAT="Ai21 Chat",r.AIML="AI/ML API",r.AIOHTTP_OPENAI="Aiohttp Openai",r.Anthropic="Anthropic",r.ANTHROPIC_TEXT="Anthropic Text",r.AssemblyAI="AssemblyAI",r.AUTO_ROUTER="Auto Router",r.Bedrock="Amazon Bedrock",r.BedrockMantle="Amazon Bedrock Mantle",r.SageMaker="AWS SageMaker",r.Azure="Azure",r.Azure_AI_Studio="Azure AI Foundry (Studio)",r.AZURE_TEXT="Azure Text",r.BASETEN="Baseten",r.BYTEZ="Bytez",r.Cerebras="Cerebras",r.CLARIFAI="Clarifai",r.CLOUDFLARE="Cloudflare",r.CODESTRAL="Codestral",r.Cohere="Cohere",r.COHERE_CHAT="Cohere Chat",r.COMETAPI="Cometapi",r.COMPACTIFAI="Compactifai",r.Cursor="Cursor",r.Dashscope="Dashscope",r.Databricks="Databricks (Qwen API)",r.DATAROBOT="Datarobot",r.DeepInfra="DeepInfra",r.Deepgram="Deepgram",r.Deepseek="Deepseek",r.DOCKER_MODEL_RUNNER="Docker Model Runner",r.DOTPROMPT="Dotprompt",r.ElevenLabs="ElevenLabs",r.EMPOWER="Empower",r.FalAI="Fal AI",r.FEATHERLESS_AI="Featherless Ai",r.FireworksAI="Fireworks AI",r.FRIENDLIAI="Friendliai",r.GALADRIEL="Galadriel",r.GITHUB_COPILOT="Github Copilot",r.Google_AI_Studio="Google AI Studio",r.GradientAI="GradientAI",r.Groq="Groq",r.HEROKU="Heroku",r.Hosted_Vllm="vllm",r.HUGGINGFACE="Huggingface",r.HYPERBOLIC="Hyperbolic",r.Infinity="Infinity",r.JinaAI="Jina AI",r.LAMBDA_AI="Lambda Ai",r.LEMONADE="Lemonade",r.LLAMAFILE="Llamafile",r.LM_STUDIO="Lm Studio",r.LLAMA="Meta Llama",r.MARITALK="Maritalk",r.MiniMax="MiniMax",r.MistralAI="Mistral AI",r.MOONSHOT="Moonshot",r.MORPH="Morph",r.NEBIUS="Nebius",r.NLP_CLOUD="Nlp Cloud",r.NOVITA="Novita",r.NSCALE="Nscale",r.NVIDIA_NIM="Nvidia Nim",r.Ollama="Ollama",r.OLLAMA_CHAT="Ollama Chat",r.OOBABOOGA="Oobabooga",r.OpenAI="OpenAI",r.OPENAI_LIKE="Openai Like",r.OpenAI_Compatible="OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)",r.OpenAI_Text="OpenAI Text Completion",r.OpenAI_Text_Compatible="OpenAI-Compatible Completions (legacy /v1/completions)",r.Openrouter="Openrouter",r.Oracle="Oracle Cloud Infrastructure (OCI)",r.OVHCLOUD="Ovhcloud",r.Perplexity="Perplexity",r.PETALS="Petals",r.PG_VECTOR="Pg Vector",r.PREDIBASE="Predibase",r.RECRAFT="Recraft",r.REPLICATE="Replicate",r.RunwayML="RunwayML",r.SAGEMAKER_LEGACY="Sagemaker",r.Sambanova="Sambanova",r.SAP="SAP Generative AI Hub",r.Snowflake="Snowflake",r.Soniox="Soniox",r.TEXT_COMPLETION_CODESTRAL="Text-Completion-Codestral",r.TogetherAI="TogetherAI",r.TOPAZ="Topaz",r.Triton="Triton",r.V0="V0",r.VERCEL_AI_GATEWAY="Vercel Ai Gateway",r.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",r.VERTEX_AI_BETA="Vertex Ai Beta",r.VLLM="Vllm",r.VolcEngine="VolcEngine",r.Voyage="Voyage AI",r.WANDB="Wandb",r.WATSONX="Watsonx",r.WATSONX_TEXT="Watsonx Text",r.xAI="xAI",r.XINFERENCE="Xinference",r.ZAI="Z.AI (Zhipu AI)",r);let a={A2A_Agent:"a2a_agent",AI21:"ai21",AI21_CHAT:"ai21_chat",AIML:"aiml",AIOHTTP_OPENAI:"aiohttp_openai",Anthropic:"anthropic",ANTHROPIC_TEXT:"anthropic_text",AssemblyAI:"assemblyai",AUTO_ROUTER:"auto_router",Azure:"azure",Azure_AI_Studio:"azure_ai",AZURE_TEXT:"azure_text",BASETEN:"baseten",Bedrock:"bedrock",BedrockMantle:"bedrock_mantle",BYTEZ:"bytez",Cerebras:"cerebras",CLARIFAI:"clarifai",CLOUDFLARE:"cloudflare",CODESTRAL:"codestral",Cohere:"cohere",COHERE_CHAT:"cohere_chat",COMETAPI:"cometapi",COMPACTIFAI:"compactifai",Cursor:"cursor",Dashscope:"dashscope",Databricks:"databricks",DATAROBOT:"datarobot",DeepInfra:"deepinfra",Deepgram:"deepgram",Deepseek:"deepseek",DOCKER_MODEL_RUNNER:"docker_model_runner",DOTPROMPT:"dotprompt",ElevenLabs:"elevenlabs",EMPOWER:"empower",FalAI:"fal_ai",FEATHERLESS_AI:"featherless_ai",FireworksAI:"fireworks_ai",FRIENDLIAI:"friendliai",GALADRIEL:"galadriel",GITHUB_COPILOT:"github_copilot",Google_AI_Studio:"gemini",GradientAI:"gradient_ai",Groq:"groq",HEROKU:"heroku",Hosted_Vllm:"hosted_vllm",HUGGINGFACE:"huggingface",HYPERBOLIC:"hyperbolic",Infinity:"infinity",JinaAI:"jina_ai",LAMBDA_AI:"lambda_ai",LEMONADE:"lemonade",LLAMAFILE:"llamafile",LLAMA:"meta_llama",LM_STUDIO:"lm_studio",MARITALK:"maritalk",MiniMax:"minimax",MistralAI:"mistral",MOONSHOT:"moonshot",MORPH:"morph",NEBIUS:"nebius",NLP_CLOUD:"nlp_cloud",NOVITA:"novita",NSCALE:"nscale",NVIDIA_NIM:"nvidia_nim",Ollama:"ollama",OLLAMA_CHAT:"ollama_chat",OOBABOOGA:"oobabooga",OpenAI:"openai",OPENAI_LIKE:"openai_like",OpenAI_Compatible:"openai",OpenAI_Text:"text-completion-openai",OpenAI_Text_Compatible:"text-completion-openai",Openrouter:"openrouter",Oracle:"oci",OVHCLOUD:"ovhcloud",Perplexity:"perplexity",PETALS:"petals",PG_VECTOR:"pg_vector",PREDIBASE:"predibase",RECRAFT:"recraft",REPLICATE:"replicate",RunwayML:"runwayml",SAGEMAKER_LEGACY:"sagemaker",SageMaker:"sagemaker_chat",Sambanova:"sambanova",SAP:"sap",Snowflake:"snowflake",Soniox:"soniox",TEXT_COMPLETION_CODESTRAL:"text-completion-codestral",TogetherAI:"together_ai",TOPAZ:"topaz",Triton:"triton",V0:"v0",VERCEL_AI_GATEWAY:"vercel_ai_gateway",Vertex_AI:"vertex_ai",VERTEX_AI_BETA:"vertex_ai_beta",VLLM:"vllm",VolcEngine:"volcengine",Voyage:"voyage",WANDB:"wandb",WATSONX:"watsonx",WATSONX_TEXT:"watsonx_text",xAI:"xai",XINFERENCE:"xinference",ZAI:"zai"},l=new Set(["bedrock_mantle"]),s="/ui/assets/logos/",n={"A2A Agent":`${s}a2a_agent.png`,Ai21:`${s}ai21.svg`,"Ai21 Chat":`${s}ai21.svg`,"AI/ML API":`${s}aiml_api.svg`,"Aiohttp Openai":`${s}openai_small.svg`,Anthropic:`${s}anthropic.svg`,"Anthropic Text":`${s}anthropic.svg`,AssemblyAI:`${s}assemblyai_small.png`,Azure:`${s}microsoft_azure.svg`,"Azure AI Foundry (Studio)":`${s}microsoft_azure.svg`,"Azure Text":`${s}microsoft_azure.svg`,Baseten:`${s}baseten.svg`,"Amazon Bedrock":`${s}bedrock.svg`,"Amazon Bedrock Mantle":`${s}bedrock.svg`,"AWS SageMaker":`${s}bedrock.svg`,Cerebras:`${s}cerebras.svg`,Cloudflare:`${s}cloudflare.svg`,Codestral:`${s}mistral.svg`,Cohere:`${s}cohere.svg`,"Cohere Chat":`${s}cohere.svg`,Cometapi:`${s}cometapi.svg`,Cursor:`${s}cursor.svg`,"Databricks (Qwen API)":`${s}databricks.svg`,Dashscope:`${s}dashscope.svg`,Deepseek:`${s}deepseek.svg`,Deepgram:`${s}deepgram.png`,DeepInfra:`${s}deepinfra.png`,ElevenLabs:`${s}elevenlabs.png`,"Fal AI":`${s}fal_ai.jpg`,"Featherless Ai":`${s}featherless.svg`,"Fireworks AI":`${s}fireworks.svg`,Friendliai:`${s}friendli.svg`,"Github Copilot":`${s}github_copilot.svg`,"Google AI Studio":`${s}google.svg`,GradientAI:`${s}gradientai.svg`,Groq:`${s}groq.svg`,vllm:`${s}vllm.png`,Huggingface:`${s}huggingface.svg`,Hyperbolic:`${s}hyperbolic.svg`,Infinity:`${s}infinity.png`,"Jina AI":`${s}jina.png`,"Lambda Ai":`${s}lambda.svg`,"Lm Studio":`${s}lmstudio.svg`,"Meta Llama":`${s}meta_llama.svg`,MiniMax:`${s}minimax.svg`,"Mistral AI":`${s}mistral.svg`,Moonshot:`${s}moonshot.svg`,Morph:`${s}morph.svg`,Nebius:`${s}nebius.svg`,Novita:`${s}novita.svg`,"Nvidia Nim":`${s}nvidia_nim.svg`,Ollama:`${s}ollama.svg`,"Ollama Chat":`${s}ollama.svg`,Oobabooga:`${s}openai_small.svg`,OpenAI:`${s}openai_small.svg`,"Openai Like":`${s}openai_small.svg`,"OpenAI Text Completion":`${s}openai_small.svg`,"OpenAI-Compatible Completions (legacy /v1/completions)":`${s}openai_small.svg`,"OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)":`${s}openai_small.svg`,Openrouter:`${s}openrouter.svg`,"Oracle Cloud Infrastructure (OCI)":`${s}oracle.svg`,Perplexity:`${s}perplexity-ai.svg`,Recraft:`${s}recraft.svg`,Replicate:`${s}replicate.svg`,RunwayML:`${s}runwayml.png`,Sagemaker:`${s}bedrock.svg`,Sambanova:`${s}sambanova.svg`,"SAP Generative AI Hub":`${s}sap.png`,Snowflake:`${s}snowflake.svg`,Soniox:`${s}soniox.svg`,"Text-Completion-Codestral":`${s}mistral.svg`,TogetherAI:`${s}togetherai.svg`,Topaz:`${s}topaz.svg`,Triton:`${s}nvidia_triton.png`,V0:`${s}v0.svg`,"Vercel Ai Gateway":`${s}vercel.svg`,"Vertex AI (Anthropic, Gemini, etc.)":`${s}google.svg`,"Vertex Ai Beta":`${s}google.svg`,Vllm:`${s}vllm.png`,VolcEngine:`${s}volcengine.png`,"Voyage AI":`${s}voyage.webp`,Watsonx:`${s}watsonx.svg`,"Watsonx Text":`${s}watsonx.svg`,xAI:`${s}xai.svg`,Xinference:`${s}xinference.svg`};e.s(["Providers",()=>t,"getPlaceholder",0,e=>{if("AI/ML API"===e)return"aiml/flux-pro/v1.1";if("Vertex AI (Anthropic, Gemini, etc.)"===e)return"gemini-pro";if("Anthropic"==e)return"claude-3-opus";if("Amazon Bedrock"==e)return"claude-3-opus";if("AWS SageMaker"==e)return"sagemaker/jumpstart-dft-meta-textgeneration-llama-2-7b";else if("Google AI Studio"==e)return"gemini-pro";else if("Azure AI Foundry (Studio)"==e)return"azure_ai/command-r-plus";else if("Azure"==e)return"my-deployment";else if("Oracle Cloud Infrastructure (OCI)"==e)return"oci/xai.grok-4";else if("Snowflake"==e)return"snowflake/mistral-7b";else if("Voyage AI"==e)return"voyage/";else if("Jina AI"==e)return"jina_ai/";else if("VolcEngine"==e)return"volcengine/";else if("DeepInfra"==e)return"deepinfra/";else if("Fal AI"==e)return"fal_ai/fal-ai/flux-pro/v1.1-ultra";else if("RunwayML"==e)return"runwayml/gen4_turbo";else if("Watsonx"===e)return"watsonx/ibm/granite-3-3-8b-instruct";else if("Cursor"===e)return"cursor/claude-4-sonnet";else if("Z.AI (Zhipu AI)"===e)return"zai/glm-4.5";else return"gpt-3.5-turbo"},"getProviderLogoAndName",0,e=>{if(!e)return{logo:"",displayName:"-"};if("gemini"===e.toLowerCase()){let e="Google AI Studio";return{logo:(0,o.resolveLogoSrc)(n[e])??"",displayName:e}}let r=Object.keys(a).find(r=>a[r].toLowerCase()===e.toLowerCase())??Object.keys(a).find(r=>r.toLowerCase()===e.toLowerCase());if(!r)return{logo:"",displayName:e};let l=t[r];return{logo:(0,o.resolveLogoSrc)(n[l])??"",displayName:l}},"getProviderModels",0,(e,r)=>{let o=a[e],t=[];return e&&"object"==typeof r&&(Object.entries(r).forEach(([e,r])=>{if(null!==r&&"object"==typeof r&&"litellm_provider"in r){let a=r.litellm_provider,s="string"==typeof a&&(a.startsWith(`${o}_`)||a.startsWith(`${o}-`));(a===o||s&&!l.has(a))&&t.push(e)}}),"Cohere"==e&&Object.entries(r).forEach(([e,r])=>{null!==r&&"object"==typeof r&&"litellm_provider"in r&&"cohere_chat"===r.litellm_provider&&t.push(e)}),"AWS SageMaker"==e&&Object.entries(r).forEach(([e,r])=>{null!==r&&"object"==typeof r&&"litellm_provider"in r&&"sagemaker_chat"===r.litellm_provider&&t.push(e)})),t},"providerLogoMap",0,n,"provider_map",0,a])},240647,e=>{"use strict";var r=e.i(286612);e.s(["RightOutlined",()=>r.default])},123521,e=>{"use strict";var r=e.i(984125);e.s(["EyeOutlined",()=>r.default])},210612,e=>{"use strict";e.i(247167);var r=e.i(931067),o=e.i(271645);let t={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 64H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-600 72h560v208H232V136zm560 480H232V408h560v208zm0 272H232V680h560v208zM304 240a40 40 0 1080 0 40 40 0 10-80 0zm0 272a40 40 0 1080 0 40 40 0 10-80 0zm0 272a40 40 0 1080 0 40 40 0 10-80 0z"}}]},name:"database",theme:"outlined"};var a=e.i(9583),l=o.forwardRef(function(e,l){return o.createElement(a.default,(0,r.default)({},e,{ref:l,icon:t}))});e.s(["DatabaseOutlined",0,l],210612)},84899,e=>{"use strict";e.i(247167);var r=e.i(931067),o=e.i(271645),t={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"defs",attrs:{},children:[{tag:"style",attrs:{}}]},{tag:"path",attrs:{d:"M931.4 498.9L94.9 79.5c-3.4-1.7-7.3-2.1-11-1.2a15.99 15.99 0 00-11.7 19.3l86.2 352.2c1.3 5.3 5.2 9.6 10.4 11.3l147.7 50.7-147.6 50.7c-5.2 1.8-9.1 6-10.3 11.3L72.2 926.5c-.9 3.7-.5 7.6 1.2 10.9 3.9 7.9 13.5 11.1 21.5 7.2l836.5-417c3.1-1.5 5.6-4.1 7.2-7.1 3.9-8 .7-17.6-7.2-21.6zM170.8 826.3l50.3-205.6 295.2-101.3c2.3-.8 4.2-2.6 5-5 1.4-4.2-.8-8.7-5-10.2L221.1 403 171 198.2l628 314.9-628.2 313.2z"}}]},name:"send",theme:"outlined"},a=e.i(9583),l=o.forwardRef(function(e,l){return o.createElement(a.default,(0,r.default)({},e,{ref:l,icon:t}))});e.s(["SendOutlined",0,l],84899)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/052zw1.u.as-x.js b/litellm/proxy/_experimental/out/_next/static/chunks/052zw1.u.as-x.js new file mode 100644 index 00000000000..bf7544546c8 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/052zw1.u.as-x.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var r=e.i(290571),l=e.i(271645),t=e.i(829087),s=e.i(480731),a=e.i(444755),i=e.i(673706),n=e.i(95779);let o={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},m=(0,i.makeClassName)("Icon"),u=l.default.forwardRef((e,u)=>{let{icon:x,variant:g="simple",tooltip:h,size:p=s.Sizes.SM,color:b,className:j}=e,v=(0,r.__rest)(e,["icon","variant","tooltip","size","color","className"]),_=((e,r)=>{switch(e){case"simple":return{textColor:r?(0,i.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:r?(0,i.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:r?(0,i.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:r?(0,i.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:r?(0,i.getColorClassNames)(r,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:r?(0,i.getColorClassNames)(r,n.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,n.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(g,b),{tooltipProps:f,getReferenceProps:y}=(0,t.useTooltip)();return l.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([u,f.refs.setReference]),className:(0,a.tremorTwMerge)(m("root"),"inline-flex shrink-0 items-center justify-center",_.bgColor,_.textColor,_.borderColor,_.ringColor,c[g].rounded,c[g].border,c[g].shadow,c[g].ring,o[p].paddingX,o[p].paddingY,j)},y,v),l.default.createElement(t.default,Object.assign({text:h},f)),l.default.createElement(x,{className:(0,a.tremorTwMerge)(m("icon"),"shrink-0",d[p].height,d[p].width)}))});u.displayName="Icon",e.s(["default",0,u],728889)},752978,e=>{"use strict";var r=e.i(728889);e.s(["Icon",()=>r.default])},278587,e=>{"use strict";var r=e.i(271645);let l=r.forwardRef(function(e,l){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,l],278587)},591935,e=>{"use strict";var r=e.i(271645);let l=r.forwardRef(function(e,l){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,l],591935)},434626,e=>{"use strict";var r=e.i(271645);let l=r.forwardRef(function(e,l){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,l],434626)},551332,e=>{"use strict";var r=e.i(271645);let l=r.forwardRef(function(e,l){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,l],551332)},122577,e=>{"use strict";var r=e.i(271645);let l=r.forwardRef(function(e,l){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,l],122577)},902555,e=>{"use strict";var r=e.i(843476),l=e.i(591935),t=e.i(122577),s=e.i(278587),a=e.i(68155),i=e.i(360820),n=e.i(871943),o=e.i(434626),d=e.i(551332),c=e.i(592968),m=e.i(115504),u=e.i(752978);function x({icon:e,onClick:l,className:t,disabled:s,dataTestId:a}){return s?(0,r.jsx)(u.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":a}):(0,r.jsx)(u.Icon,{icon:e,size:"sm",onClick:l,className:(0,m.cx)("cursor-pointer",t),"data-testid":a})}let g={Edit:{icon:l.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:a.TrashIcon,className:"hover:text-red-600"},Test:{icon:t.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:s.RefreshIcon,className:"hover:text-green-600"},Up:{icon:i.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:n.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:o.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:l,disabled:t=!1,disabledTooltipText:s,dataTestId:a,variant:i}){let{icon:n,className:o}=g[i];return(0,r.jsx)(c.Tooltip,{title:t?s:l,children:(0,r.jsx)("span",{children:(0,r.jsx)(x,{icon:n,onClick:e,className:o,disabled:t,dataTestId:a})})})}],902555)},678784,e=>{"use strict";var r=e.i(678745);e.s(["CheckIcon",()=>r.default])},118366,e=>{"use strict";var r=e.i(991124);e.s(["CopyIcon",()=>r.default])},502547,e=>{"use strict";var r=e.i(271645);let l=r.forwardRef(function(e,l){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,l],502547)},384767,e=>{"use strict";var r=e.i(843476),l=e.i(599724),t=e.i(271645),s=e.i(389083);let a=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"}))});var i=e.i(602869);let n=function({vectorStores:e,accessToken:n}){let[o,d]=(0,t.useState)([]);return(0,t.useEffect)(()=>{(async()=>{if(n&&0!==e.length)try{let e=await (0,i.vectorStoreListCall)(n);e.data&&d(e.data.map(e=>({vector_store_id:e.vector_store_id,vector_store_name:e.vector_store_name})))}catch(e){console.error("Error fetching vector stores:",e)}})()},[n,e.length]),(0,r.jsxs)("div",{className:"space-y-3",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)(a,{className:"h-4 w-4 text-blue-600"}),(0,r.jsx)(l.Text,{className:"font-semibold text-gray-900",children:"Vector Stores"}),(0,r.jsx)(s.Badge,{color:"blue",size:"xs",children:e.length})]}),e.length>0?(0,r.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map((e,l)=>{let t;return(0,r.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-sm font-medium",children:(t=o.find(r=>r.vector_store_id===e))?`${t.vector_store_name||t.vector_store_id} (${t.vector_store_id})`:e},l)})}):(0,r.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,r.jsx)(a,{className:"h-4 w-4 text-gray-400"}),(0,r.jsx)(l.Text,{className:"text-gray-500 text-sm",children:"No vector stores configured"})]})]})},o=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"}))});var d=e.i(871943),c=e.i(502547),m=e.i(592968),u=e.i(234713);let x=function({mcpServers:e,mcpAccessGroups:a=[],mcpToolPermissions:n={},mcpToolsets:x=[],accessToken:g}){let[h,p]=(0,t.useState)([]),[b,j]=(0,t.useState)([]),[v,_]=(0,t.useState)(new Set),[f,y]=(0,t.useState)(new Set);(0,t.useEffect)(()=>{(async()=>{if(g&&e.length>0)try{let e=await (0,i.fetchMCPServers)(g);e&&Array.isArray(e)?p(e):e.data&&Array.isArray(e.data)&&p(e.data)}catch(e){console.error("Error fetching MCP servers:",e)}})()},[g,e.length]),(0,t.useEffect)(()=>{(async()=>{if(g&&x.length>0)try{let e=await (0,i.fetchMCPToolsets)(g),r=Array.isArray(e)?e.filter(e=>x.includes(e.toolset_id)):[];j(r)}catch(e){console.error("Error fetching toolsets:",e)}})()},[g,x.length]);let w=e.includes(u.NO_MCP_SERVERS_SENTINEL),C=e.includes(u.ALL_PROXY_MCP_SERVERS_SENTINEL),N=[...e.filter(e=>e!==u.NO_MCP_SERVERS_SENTINEL&&e!==u.ALL_PROXY_MCP_SERVERS_SENTINEL).map(e=>({type:"server",value:e})),...a.map(e=>({type:"accessGroup",value:e}))],k=N.length+x.length;return(0,r.jsxs)("div",{className:"space-y-3",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)(o,{className:"h-4 w-4 text-blue-600"}),(0,r.jsx)(l.Text,{className:"font-semibold text-gray-900",children:"MCP Servers"}),(0,r.jsx)(s.Badge,{color:w?"red":"blue",size:"xs",children:w?"Blocked":C?"All":k})]}),w?(0,r.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 border border-red-200",children:[(0,r.jsx)(o,{className:"h-4 w-4 text-red-400"}),(0,r.jsx)(l.Text,{className:"text-red-700 text-sm",children:"No MCP servers — this key is blocked from all MCP servers, including its team's servers"})]}):C?(0,r.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-50 border border-blue-200",children:[(0,r.jsx)(o,{className:"h-4 w-4 text-blue-400"}),(0,r.jsx)(l.Text,{className:"text-blue-700 text-sm",children:"All Proxy MCP Servers"})]}):k>0?(0,r.jsxs)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:[N.map((e,l)=>{let t="server"===e.type?n[e.value]:void 0,s=t&&t.length>0,a=v.has(e.value);return(0,r.jsxs)("div",{className:"space-y-2",children:[(0,r.jsxs)("div",{onClick:()=>{var r;return s&&(r=e.value,void _(e=>{let l=new Set(e);return l.has(r)?l.delete(r):l.add(r),l}))},className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 transition-all ${s?"cursor-pointer hover:bg-gray-50 hover:border-gray-300":"bg-white"}`,children:[(0,r.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"server"===e.type?(0,r.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,r.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,r.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-blue-500 rounded-full shrink-0"}),(0,r.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let r=h.find(r=>r.server_id===e);if(r){let l=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${r.alias} (${l})`}return e})(e.value)})]})}):(0,r.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,r.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,r.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,r.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})}),s&&(0,r.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,r.jsx)("span",{className:"text-xs font-medium text-gray-600",children:t.length}),(0,r.jsx)("span",{className:"text-xs text-gray-500",children:1===t.length?"tool":"tools"}),a?(0,r.jsx)(d.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,r.jsx)(c.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),s&&a&&(0,r.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-blue-200 pb-1",children:(0,r.jsx)("div",{className:"flex flex-wrap gap-1.5",children:t.map((e,l)=>(0,r.jsx)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-xs font-medium",children:e},l))})})]},l)}),x.length>0&&x.map((e,l)=>{let t=b.find(r=>r.toolset_id===e),s=f.has(e),a=t?.tools.length??0;return(0,r.jsxs)("div",{className:"space-y-2",children:[(0,r.jsxs)("div",{onClick:()=>a>0&&void y(r=>{let l=new Set(r);return l.has(e)?l.delete(e):l.add(e),l}),className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-purple-200 transition-all ${a>0?"cursor-pointer hover:bg-purple-50 hover:border-purple-300":"bg-white"}`,children:[(0,r.jsxs)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:[(0,r.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,r.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:t?.toolset_name??e}),(0,r.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-purple-600 bg-purple-50 border border-purple-200 rounded-sm uppercase tracking-wide shrink-0",children:"Toolset"})]}),a>0&&(0,r.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,r.jsx)("span",{className:"text-xs font-medium text-gray-600",children:a}),(0,r.jsx)("span",{className:"text-xs text-gray-500",children:1===a?"tool":"tools"}),s?(0,r.jsx)(d.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,r.jsx)(c.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),a>0&&s&&t&&(0,r.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-purple-200 pb-1",children:(0,r.jsx)("div",{className:"flex flex-wrap gap-1.5",children:t.tools.map((e,l)=>(0,r.jsxs)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-purple-50 border border-purple-200 text-purple-800 text-xs font-medium",children:[(0,r.jsxs)("span",{className:"text-purple-400 mr-1 text-[10px]",children:[e.server_id.slice(0,6),"…"]}),e.tool_name]},l))})})]},`toolset-${l}`)})]}):(0,r.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,r.jsx)(o,{className:"h-4 w-4 text-gray-400"}),(0,r.jsx)(l.Text,{className:"text-gray-500 text-sm",children:"No MCP servers, access groups, or toolsets configured"})]})]})},g=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"}))}),h=function({agents:e,agentAccessGroups:a=[],accessToken:n}){let[o,d]=(0,t.useState)([]);(0,t.useEffect)(()=>{(async()=>{if(n&&e.length>0)try{let e=await (0,i.getAgentsList)(n);e&&e.agents&&Array.isArray(e.agents)&&d(e.agents)}catch(e){console.error("Error fetching agents:",e)}})()},[n,e.length]);let c=[...e.map(e=>({type:"agent",value:e})),...a.map(e=>({type:"accessGroup",value:e}))],u=c.length;return(0,r.jsxs)("div",{className:"space-y-3",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)(g,{className:"h-4 w-4 text-purple-600"}),(0,r.jsx)(l.Text,{className:"font-semibold text-gray-900",children:"Agents"}),(0,r.jsx)(s.Badge,{color:"purple",size:"xs",children:u})]}),u>0?(0,r.jsx)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:c.map((e,l)=>(0,r.jsx)("div",{className:"space-y-2",children:(0,r.jsx)("div",{className:"flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 bg-white",children:(0,r.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"agent"===e.type?(0,r.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,r.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,r.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,r.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let r=o.find(r=>r.agent_id===e);if(r){let l=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${r.agent_name} (${l})`}return e})(e.value)})]})}):(0,r.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,r.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,r.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,r.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})})})},l))}):(0,r.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,r.jsx)(g,{className:"h-4 w-4 text-gray-400"}),(0,r.jsx)(l.Text,{className:"text-gray-500 text-sm",children:"No agents or access groups configured"})]})]})};e.s(["default",0,function({objectPermission:e,variant:t="card",className:s="",accessToken:a}){let i=e?.vector_stores||[],o=e?.mcp_servers||[],d=e?.mcp_access_groups||[],c=e?.mcp_tool_permissions||{},m=e?.mcp_toolsets||[],u=e?.agents||[],g=e?.agent_access_groups||[],p=e?.search_tools||[],b=(0,r.jsxs)("div",{className:"card"===t?"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6":"space-y-4",children:[(0,r.jsx)(n,{vectorStores:i,accessToken:a}),(0,r.jsx)(x,{mcpServers:o,mcpAccessGroups:d,mcpToolPermissions:c,mcpToolsets:m,accessToken:a}),(0,r.jsx)(h,{agents:u,agentAccessGroups:g,accessToken:a}),(0,r.jsxs)("div",{className:"rounded-md border border-gray-100 p-4",children:[(0,r.jsx)(l.Text,{className:"text-sm font-medium text-gray-800",children:"Search tools"}),0===p.length?(0,r.jsx)(l.Text,{className:"mt-1 block text-xs text-gray-500",children:"No restriction — all configured search tools are allowed for this team."}):(0,r.jsx)(l.Text,{className:"mt-1 block text-xs text-gray-700",children:p.join(", ")})]})]});return"card"===t?(0,r.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${s}`,children:[(0,r.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,r.jsxs)("div",{children:[(0,r.jsx)(l.Text,{className:"font-semibold text-gray-900",children:"Object Permissions"}),(0,r.jsx)(l.Text,{className:"text-xs text-gray-500",children:"Access control for Vector Stores and MCP Servers"})]})}),b]}):(0,r.jsxs)("div",{className:`${s}`,children:[(0,r.jsx)(l.Text,{className:"font-medium text-gray-900 mb-3",children:"Object Permissions"}),b]})}],384767)},625901,e=>{"use strict";var r=e.i(266027),l=e.i(621482),t=e.i(243652),s=e.i(602869),a=e.i(135214);let i=(0,t.createQueryKeys)("models"),n=(0,t.createQueryKeys)("modelHub"),o=(0,t.createQueryKeys)("allProxyModels");(0,t.createQueryKeys)("selectedTeamModels");let d=(0,t.createQueryKeys)("infiniteModels"),c=(0,t.createQueryKeys)("userModels");e.s(["useAllProxyModels",0,()=>{let{accessToken:e,userId:l,userRole:t}=(0,a.default)();return(0,r.useQuery)({queryKey:o.list({}),queryFn:async()=>await (0,s.modelAvailableCall)(e,l,t,!0,null,!0,!1,"expand"),enabled:!!(e&&l&&t)})},"useInfiniteModelInfo",0,(e=50,r)=>{let{accessToken:t,userId:i,userRole:n}=(0,a.default)();return(0,l.useInfiniteQuery)({queryKey:d.list({filters:{...i&&{userId:i},...n&&{userRole:n},size:e,...r&&{search:r}}}),queryFn:async({pageParam:l})=>await (0,s.modelInfoCall)(t,i,n,l,e,r),initialPageParam:1,getNextPageParam:e=>{if(e.current_page{let{accessToken:e}=(0,a.default)();return(0,r.useQuery)({queryKey:n.list({}),queryFn:async()=>await (0,s.modelHubCall)(e),enabled:!!e})},"useModelsInfo",0,(e=1,l=50,t,n,o,d,c)=>{let{accessToken:m,userId:u,userRole:x}=(0,a.default)();return(0,r.useQuery)({queryKey:i.list({filters:{...u&&{userId:u},...x&&{userRole:x},page:e,size:l,...t&&{search:t},...n&&{modelId:n},...o&&{teamId:o},...d&&{sortBy:d},...c&&{sortOrder:c}}}),queryFn:async()=>await (0,s.modelInfoCall)(m,u,x,e,l,t,n,o,d,c),enabled:!!(m&&u&&x)})},"useUserModels",0,()=>{let{accessToken:e,userId:l,userRole:t}=(0,a.default)();return(0,r.useQuery)({queryKey:c.list({}),queryFn:async()=>(await (0,s.modelAvailableCall)(e,l,t)).data.map(e=>e.id),enabled:!!(e&&l&&t)})}])},738014,e=>{"use strict";var r=e.i(135214),l=e.i(602869),t=e.i(266027);let s=(0,e.i(243652).createQueryKeys)("users");e.s(["useCurrentUser",0,()=>{let{accessToken:e,userId:a}=(0,r.default)();return(0,t.useQuery)({queryKey:s.detail(a),queryFn:async()=>await (0,l.userGetInfoV2)(e),enabled:!!(e&&a)})}])},162386,e=>{"use strict";var r=e.i(843476),l=e.i(625901),t=e.i(109799),s=e.i(785242),a=e.i(738014),i=e.i(199133),n=e.i(981339),o=e.i(592968);let d={label:"All Proxy Models",value:"all-proxy-models"},c={label:"No Default Models",value:"no-default-models"},m=[d,c],u={user:({allProxyModels:e,userModels:r,options:l})=>r&&l?.includeUserModels?r:[],team:({allProxyModels:e,selectedOrganization:r,userModels:l})=>r?r.models.includes(d.value)||0===r.models.length?e:e.filter(e=>r.models.includes(e)):e??[],organization:({allProxyModels:e})=>e,global:({allProxyModels:e})=>e};e.s(["ModelSelect",0,e=>{let{teamID:x,organizationID:g,options:h,context:p,dataTestId:b,value:j=[],onChange:v,style:_}=e,{includeUserModels:f,showAllTeamModelsOption:y,showAllProxyModelsOverride:w,includeSpecialOptions:C}=h||{},{data:N,isLoading:k}=(0,l.useAllProxyModels)(),{data:T,isLoading:S}=(0,s.useTeam)(x),{data:M,isLoading:z}=(0,t.useOrganization)(g),{data:I,isLoading:O}=(0,a.useCurrentUser)(),F=e=>m.some(r=>r.value===e),P=j.some(F),A=M?.models.includes(d.value)||M?.models.length===0;if(k||S||z||O)return(0,r.jsx)(n.Skeleton.Input,{active:!0,block:!0});let{wildcard:E,regular:L}=(e=>{let r=[],l=[];for(let t of e)t.endsWith("/*")?r.push(t):l.push(t);return{wildcard:r,regular:l}})(((e,r,l)=>{let t=Array.from(new Map(e.map(e=>[e.id,e])).values()).map(e=>e.id);if(r.options?.showAllProxyModelsOverride)return t;let s=u[r.context];return s?s({allProxyModels:t,...l,options:r.options}):[]})(N?.data??[],e,{selectedTeam:T,selectedOrganization:M,userModels:I?.models}));return(0,r.jsx)(i.Select,{"data-testid":b,value:j,onChange:e=>{let r=e.filter(F);v(r.length>0?[r[r.length-1]]:e)},style:_,options:[...C?[{label:(0,r.jsx)("span",{children:"Special Options"}),title:"Special Options",options:[...w||A&&C||"global"===p?[{label:(0,r.jsx)("span",{children:"All Proxy Models"}),value:d.value,disabled:j.length>0&&j.some(e=>F(e)&&e!==d.value),key:d.value}]:[],{label:(0,r.jsx)("span",{children:"No Default Models"}),value:c.value,disabled:j.length>0&&j.some(e=>F(e)&&e!==c.value),key:c.value}]}]:[],...E.length>0?[{label:(0,r.jsx)("span",{children:"Wildcard Options"}),title:"Wildcard Options",options:E.map(e=>{let l=e.replace("/*",""),t=l.charAt(0).toUpperCase()+l.slice(1);return{label:(0,r.jsx)("span",{children:`All ${t} models`}),value:e,disabled:P}})}]:[],{label:(0,r.jsx)("span",{children:"Models"}),title:"Models",options:L.map(e=>({label:(0,r.jsx)("span",{children:e}),value:e,disabled:P}))}],mode:"multiple",placeholder:"Select Models",allowClear:!0,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,r.jsx)(o.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,r.jsxs)("span",{children:["+",e.length," more"]})})})}],162386)},294612,e=>{"use strict";var r=e.i(843476),l=e.i(100486),t=e.i(827252),s=e.i(213205),a=e.i(771674),i=e.i(464571),n=e.i(770914),o=e.i(291542),d=e.i(262218),c=e.i(592968),m=e.i(898586),u=e.i(902555);let{Text:x}=m.Typography;e.s(["default",0,function({members:e,canEdit:m,onEdit:g,onDelete:h,onAddMember:p,roleColumnTitle:b="Role",roleTooltip:j,extraColumns:v=[],showDeleteForMember:_,emptyText:f}){let y=[{title:"User Email",dataIndex:"user_email",key:"user_email",render:e=>(0,r.jsx)(x,{children:e||"-"})},{title:"User ID",dataIndex:"user_id",key:"user_id",render:e=>"default_user_id"===e?(0,r.jsx)(d.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,r.jsx)(x,{children:e||"-"})},{title:j?(0,r.jsxs)(n.Space,{direction:"horizontal",children:[b,(0,r.jsx)(c.Tooltip,{title:j,children:(0,r.jsx)(t.InfoCircleOutlined,{})})]}):b,dataIndex:"role",key:"role",render:e=>(0,r.jsxs)(n.Space,{children:[e?.toLowerCase()==="admin"||e?.toLowerCase()==="org_admin"?(0,r.jsx)(l.CrownOutlined,{}):(0,r.jsx)(a.UserOutlined,{}),(0,r.jsx)(x,{style:{textTransform:"capitalize"},children:e||"-"})]})},...v,{title:"Actions",key:"actions",fixed:"right",width:120,render:(e,l)=>m?(0,r.jsxs)(n.Space,{children:[(0,r.jsx)(u.default,{variant:"Edit",tooltipText:"Edit member",dataTestId:"edit-member",onClick:()=>g(l)}),(!_||_(l))&&(0,r.jsx)(u.default,{variant:"Delete",tooltipText:"Delete member",dataTestId:"delete-member",onClick:()=>h(l)})]}):null}];return(0,r.jsxs)(n.Space,{direction:"vertical",style:{width:"100%"},children:[(0,r.jsxs)("span",{className:"inline-flex text-sm text-gray-700",children:[e.length," Member",1!==e.length?"s":""]}),(0,r.jsx)(o.Table,{columns:y,dataSource:e,rowKey:e=>e.user_id??e.user_email??JSON.stringify(e),pagination:!1,size:"small",scroll:{x:"max-content"},locale:f?{emptyText:f}:void 0}),p&&m&&(0,r.jsx)(i.Button,{icon:(0,r.jsx)(s.UserAddOutlined,{}),type:"primary",onClick:p,children:"Add Member"})]})}])},907308,276173,e=>{"use strict";var r=e.i(843476),l=e.i(271645),t=e.i(212931),s=e.i(808613),a=e.i(464571),i=e.i(199133),n=e.i(592968),o=e.i(213205),d=e.i(374009),c=e.i(602869);e.s(["default",0,({isVisible:e,onCancel:m,onSubmit:u,accessToken:x,title:g="Add Team Member",roles:h=[{label:"admin",value:"admin",description:"Admin role. Can create team keys, add members, and manage settings."},{label:"user",value:"user",description:"User role. Can view team info, but not manage it."}],defaultRole:p="user",teamId:b})=>{let[j]=s.Form.useForm(),[v,_]=(0,l.useState)([]),[f,y]=(0,l.useState)(!1),[w,C]=(0,l.useState)("user_email"),[N,k]=(0,l.useState)(!1),T=async(e,r)=>{if(!e)return void _([]);y(!0);try{let l=new URLSearchParams;if(l.append(r,e),b&&l.append("team_id",b),null==x)return;let t=(await (0,c.userFilterUICall)(x,l)).map(e=>({label:"user_email"===r?`${e.user_email}`:`${e.user_id}`,value:"user_email"===r?e.user_email:e.user_id,user:e}));_(t)}catch(e){console.error("Error fetching users:",e)}finally{y(!1)}},S=(0,l.useCallback)((0,d.default)((e,r)=>T(e,r),300),[]),M=(e,r)=>{C(r),S(e,r)},z=(e,r)=>{let l=r.user;j.setFieldsValue({user_email:l.user_email,user_id:l.user_id,role:j.getFieldValue("role")})},I=async e=>{k(!0);try{await u(e)}finally{k(!1)}};return(0,r.jsx)(t.Modal,{title:g,open:e,onCancel:()=>{j.resetFields(),_([]),m()},footer:null,width:800,maskClosable:!N,children:(0,r.jsxs)(s.Form,{form:j,onFinish:I,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:{role:p},children:[(0,r.jsx)(s.Form.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(i.Select,{showSearch:!0,className:"w-full",placeholder:"Search by email",filterOption:!1,onSearch:e=>M(e,"user_email"),onSelect:(e,r)=>z(e,r),options:"user_email"===w?v:[],loading:f,allowClear:!0,"data-testid":"member-email-search"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(s.Form.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(i.Select,{showSearch:!0,className:"w-full",placeholder:"Search by user ID",filterOption:!1,onSearch:e=>M(e,"user_id"),onSelect:(e,r)=>z(e,r),options:"user_id"===w?v:[],loading:f,allowClear:!0})}),(0,r.jsx)(s.Form.Item,{label:"Member Role",name:"role",className:"mb-4",children:(0,r.jsx)(i.Select,{defaultValue:p,children:h.map(e=>(0,r.jsx)(i.Select.Option,{value:e.value,children:(0,r.jsxs)(n.Tooltip,{title:e.description,children:[(0,r.jsx)("span",{className:"font-medium",children:e.label}),(0,r.jsxs)("span",{className:"ml-2 text-gray-500 text-sm",children:["- ",e.description]})]})},e.value))})}),(0,r.jsx)("div",{className:"text-right mt-4",children:(0,r.jsx)(a.Button,{type:"primary",htmlType:"submit",icon:(0,r.jsx)(o.UserAddOutlined,{}),loading:N,children:N?"Adding...":"Add Member"})})]})})}],907308);var m=e.i(599724),u=e.i(779241),x=e.i(435451),g=e.i(860585);e.s(["default",0,({visible:e,onCancel:n,onSubmit:o,initialData:d,mode:c,config:h})=>{let p,[b]=s.Form.useForm(),[j,v]=(0,l.useState)(!1);(0,l.useEffect)(()=>{if(e)if("edit"===c&&d){let e={...d,role:d.role||h.defaultRole,max_budget_in_team:d.max_budget_in_team||null,tpm_limit:d.tpm_limit||null,rpm_limit:d.rpm_limit||null,budget_duration:d.budget_duration||null,allowed_models:d.allowed_models||[]};b.setFieldsValue(e)}else b.resetFields(),b.setFieldsValue({role:h.defaultRole||h.roleOptions[0]?.value})},[e,d,c,b,h.defaultRole,h.roleOptions]);let _=async e=>{try{v(!0);let r=Object.entries(e).reduce((e,[r,l])=>{if("string"==typeof l){let t=l.trim();return""===t&&("max_budget_in_team"===r||"tpm_limit"===r||"rpm_limit"===r)?{...e,[r]:null}:{...e,[r]:t}}return{...e,[r]:l}},{});await Promise.resolve(o(r)),b.resetFields()}catch(e){console.error("Form submission error:",e)}finally{v(!1)}};return(0,r.jsx)(t.Modal,{title:h.title||("add"===c?"Add Member":"Edit Member"),open:e,width:1e3,footer:null,onCancel:n,children:(0,r.jsxs)(s.Form,{form:b,onFinish:_,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[h.showEmail&&(0,r.jsx)(s.Form.Item,{label:"Email",name:"user_email",className:"mb-4",rules:[{type:"email",message:"Please enter a valid email!"}],children:(0,r.jsx)(u.TextInput,{placeholder:"user@example.com"})}),h.showEmail&&h.showUserId&&(0,r.jsx)("div",{className:"text-center mb-4",children:(0,r.jsx)(m.Text,{children:"OR"})}),h.showUserId&&(0,r.jsx)(s.Form.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(u.TextInput,{placeholder:"user_123"})}),(0,r.jsx)(s.Form.Item,{label:(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)("span",{children:"Role"}),"edit"===c&&d&&(0,r.jsxs)("span",{className:"text-gray-500 text-sm",children:["(Current: ",(p=d.role,h.roleOptions.find(e=>e.value===p)?.label||p),")"]})]}),name:"role",className:"mb-4",rules:[{required:!0,message:"Please select a role!"}],children:(0,r.jsx)(i.Select,{children:"edit"===c&&d?[...h.roleOptions.filter(e=>e.value===d.role),...h.roleOptions.filter(e=>e.value!==d.role)].map(e=>(0,r.jsx)(i.Select.Option,{value:e.value,children:e.label},e.value)):h.roleOptions.map(e=>(0,r.jsx)(i.Select.Option,{value:e.value,children:e.label},e.value))})}),h.additionalFields?.map(e=>(0,r.jsx)(s.Form.Item,{label:e.label,name:e.name,className:"mb-4",rules:e.rules,children:(e=>{switch(e.type){case"input":return(0,r.jsx)(u.TextInput,{placeholder:e.placeholder});case"numerical":return(0,r.jsx)(x.default,{step:e.step||1,min:e.min||0,style:{width:"100%"},placeholder:e.placeholder||"Enter a numerical value"});case"select":return(0,r.jsx)(i.Select,{children:e.options?.map(e=>(0,r.jsx)(i.Select.Option,{value:e.value,children:e.label},e.value))});case"multi-select":return(0,r.jsx)(i.Select,{mode:"multiple",placeholder:e.placeholder||"Select options",options:e.options,allowClear:!0});case"budget-duration":return(0,r.jsx)(g.default,{});default:return null}})(e)},e.name)),(0,r.jsxs)("div",{className:"text-right mt-6",children:[(0,r.jsx)(a.Button,{onClick:n,className:"mr-2",disabled:j,children:"Cancel"}),(0,r.jsx)(a.Button,{type:"default",htmlType:"submit",loading:j,children:"add"===c?j?"Adding...":"Add Member":j?"Saving...":"Save Changes"})]})]})})}],276173)},980187,e=>{"use strict";e.s(["createTeamAliasMap",0,e=>e?e.reduce((e,r)=>(e[r.team_id]=r.team_alias,e),{}):{},"resolveTeamAliasFromTeamID",0,(e,r)=>{let l=r.find(r=>r.team_id===e);return l?l.team_alias:null}])},367240,e=>{"use strict";let r=(0,e.i(475254).default)("rotate-ccw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]);e.s(["RotateCcw",0,r],367240)},655913,38419,78334,e=>{"use strict";var r=e.i(843476),l=e.i(115504),t=e.i(311451),s=e.i(374009),a=e.i(271645);e.s(["FilterInput",0,({placeholder:e,value:i,onChange:n,icon:o,className:d})=>{let[c,m]=(0,a.useState)(i);(0,a.useEffect)(()=>{m(i)},[i]);let u=(0,a.useMemo)(()=>(0,s.default)(e=>n(e),300),[n]);(0,a.useEffect)(()=>()=>{u.cancel()},[u]);let x=(0,a.useCallback)(e=>{let r=e.target.value;m(r),u(r)},[u]);return(0,r.jsx)(t.Input,{placeholder:e,value:c,onChange:x,prefix:o?(0,r.jsx)(o,{size:16,className:"text-gray-500"}):void 0,className:(0,l.cx)("w-64",d)})}],655913);var i=e.i(906579),n=e.i(464571);let o=(0,e.i(475254).default)("funnel",[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z",key:"sc7q7i"}]]);e.s(["FiltersButton",0,({onClick:e,active:l,hasActiveFilters:t,label:s="Filters"})=>(0,r.jsx)(i.Badge,{color:"blue",dot:t,children:(0,r.jsx)(n.Button,{type:"default",onClick:e,icon:(0,r.jsx)(o,{size:16}),className:l?"bg-gray-100":"",children:s})})],38419);var d=e.i(367240);e.s(["ResetFiltersButton",0,({onClick:e,label:l="Reset Filters"})=>(0,r.jsx)(n.Button,{type:"default",onClick:e,icon:(0,r.jsx)(d.RotateCcw,{size:16}),children:l})],78334)},526612,e=>{"use strict";var r=e.i(843476),l=e.i(109799),t=e.i(625901),s=e.i(655913),a=e.i(38419),i=e.i(78334),n=e.i(555436),o=e.i(284614);let d=({filters:e,showFilters:l,onToggleFilters:t,onChange:d,onReset:c})=>{let m=!!(e.org_id||e.org_alias);return(0,r.jsxs)("div",{className:"flex flex-col space-y-4",children:[(0,r.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[(0,r.jsx)(s.FilterInput,{placeholder:"Search by Organization Name",value:e.org_alias,onChange:e=>d("org_alias",e),icon:n.Search,className:"w-64"}),(0,r.jsx)(a.FiltersButton,{onClick:()=>t(!l),active:l,hasActiveFilters:m}),(0,r.jsx)(i.ResetFiltersButton,{onClick:c})]}),l&&(0,r.jsx)("div",{className:"flex flex-wrap items-center gap-3 mt-3",children:(0,r.jsx)(s.FilterInput,{placeholder:"Search by Organization ID",value:e.org_id,onChange:e=>d("org_id",e),icon:o.User,className:"w-64"})})]})};var c=e.i(827252),m=e.i(871943),u=e.i(502547),x=e.i(278587),g=e.i(389083),h=e.i(994388),p=e.i(304967),b=e.i(309426),j=e.i(350967),v=e.i(752978),_=e.i(197647),f=e.i(653824),y=e.i(269200),w=e.i(942232),C=e.i(977572),N=e.i(427612),k=e.i(64848),T=e.i(496020),S=e.i(881073),M=e.i(404206),z=e.i(723731),I=e.i(599724),O=e.i(779241),F=e.i(808613),P=e.i(311451),A=e.i(212931),E=e.i(199133),L=e.i(592968),R=e.i(912598),B=e.i(271645);e.i(622826);var D=e.i(200208),U=e.i(399536),V=e.i(964471),q=e.i(127952),$=e.i(902555),K=e.i(355619),H=e.i(75921),Q=e.i(162386),G=e.i(727749),W=e.i(602869),Y=e.i(785242),X=e.i(500330),J=e.i(980187),Z=e.i(530212),ee=e.i(629569),er=e.i(464571),el=e.i(653496),et=e.i(898586),es=e.i(678784),ea=e.i(118366),ei=e.i(294612),en=e.i(907308),eo=e.i(384767),ed=e.i(435451),ec=e.i(276173),em=e.i(916940);let eu=({organizationId:e,onClose:t,accessToken:s,is_org_admin:a,is_proxy_admin:i,userModels:n,editOrg:o})=>{let d=(0,R.useQueryClient)(),{data:c,isLoading:m}=(0,l.useOrganization)(e),[u]=F.Form.useForm(),[x,b]=(0,B.useState)(!1),[v,_]=(0,B.useState)(!1),[f,y]=(0,B.useState)(!1),[w,C]=(0,B.useState)(null),[N,k]=(0,B.useState)({}),[T,S]=(0,B.useState)(!1),M=a||i,{data:z}=(0,Y.useTeams)(),A=(0,B.useMemo)(()=>(0,J.createTeamAliasMap)(z),[z]),L=async r=>{try{if(null==s)return;let t={user_email:r.user_email,user_id:r.user_id,role:r.role};await (0,W.organizationMemberAddCall)(s,e,t),G.default.success("Organization member added successfully"),_(!1),u.resetFields(),d.invalidateQueries({queryKey:l.organizationKeys.all})}catch(e){G.default.fromBackend("Failed to add organization member"),console.error("Error adding organization member:",e)}},D=async r=>{try{if(!s)return;let t={user_email:r.user_email,user_id:r.user_id,role:r.role};await (0,W.organizationMemberUpdateCall)(s,e,t),G.default.success("Organization member updated successfully"),y(!1),u.resetFields(),d.invalidateQueries({queryKey:l.organizationKeys.all})}catch(e){G.default.fromBackend("Failed to update organization member"),console.error("Error updating organization member:",e)}},U=async r=>{try{if(!s)return;await (0,W.organizationMemberDeleteCall)(s,e,r.user_id),G.default.success("Organization member deleted successfully"),y(!1),u.resetFields(),d.invalidateQueries({queryKey:l.organizationKeys.all})}catch(e){G.default.fromBackend("Failed to delete organization member"),console.error("Error deleting organization member:",e)}},q=async r=>{try{if(!s)return;S(!0);let t={organization_id:e,organization_alias:r.organization_alias,models:r.models,litellm_budget_table:{tpm_limit:r.tpm_limit,rpm_limit:r.rpm_limit,max_budget:r.max_budget,budget_duration:r.budget_duration},metadata:r.metadata?JSON.parse(r.metadata):null};if((void 0!==r.vector_stores||void 0!==r.mcp_servers_and_groups)&&(t.object_permission={...c?.object_permission,vector_stores:r.vector_stores||[]},void 0!==r.mcp_servers_and_groups)){let{servers:e,accessGroups:l}=r.mcp_servers_and_groups||{servers:[],accessGroups:[]};e&&e.length>0&&(t.object_permission.mcp_servers=e),l&&l.length>0&&(t.object_permission.mcp_access_groups=l)}await (0,W.organizationUpdateCall)(s,t),G.default.success("Organization settings updated successfully"),b(!1),d.invalidateQueries({queryKey:l.organizationKeys.all})}catch(e){G.default.fromBackend("Failed to update organization settings"),console.error("Error updating organization:",e)}finally{S(!1)}};if(m)return(0,r.jsx)("div",{className:"p-4",children:"Loading..."});if(!c)return(0,r.jsx)("div",{className:"p-4",children:"Organization not found"});let $=async(e,r)=>{await (0,X.copyToClipboard)(e)&&(k(e=>({...e,[r]:!0})),setTimeout(()=>{k(e=>({...e,[r]:!1}))},2e3))},K=[{title:"Spend (USD)",key:"spend",render:(e,l)=>{let t=null!=l.user_id?(c.members||[]).find(e=>e.user_id===l.user_id):void 0;return(0,r.jsx)(V.MoneyCell,{value:t?.spend,decimals:4})}},{title:"Created At",key:"created_at",render:(e,l)=>{let t=null!=l.user_id?(c.members||[]).find(e=>e.user_id===l.user_id):void 0;return(0,r.jsx)(et.Typography.Text,{children:t?.created_at?new Date(t.created_at).toLocaleString():"-"})}}];return(0,r.jsxs)("div",{className:"w-full h-screen p-4 bg-white",children:[(0,r.jsx)("div",{className:"flex justify-between items-center mb-6",children:(0,r.jsxs)("div",{children:[(0,r.jsx)(h.Button,{icon:Z.ArrowLeftIcon,onClick:t,variant:"light",className:"mb-4",children:"Back to Organizations"}),(0,r.jsx)(ee.Title,{children:c.organization_alias}),(0,r.jsxs)("div",{className:"flex items-center cursor-pointer",children:[(0,r.jsx)(I.Text,{className:"text-gray-500 font-mono",children:c.organization_id}),(0,r.jsx)(er.Button,{type:"text",size:"small",icon:N["org-id"]?(0,r.jsx)(es.CheckIcon,{size:12}):(0,r.jsx)(ea.CopyIcon,{size:12}),onClick:()=>$(c.organization_id,"org-id"),className:`left-2 z-10 transition-all duration-200 ${N["org-id"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]})]})}),(0,r.jsx)(el.Tabs,{defaultActiveKey:o?"settings":"overview",className:"mb-4",items:[{key:"overview",label:"Overview",children:(0,r.jsxs)(j.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,r.jsxs)(p.Card,{children:[(0,r.jsx)(I.Text,{children:"Organization Details"}),(0,r.jsxs)("div",{className:"mt-2",children:[(0,r.jsxs)(I.Text,{children:["Created: ",new Date(c.created_at).toLocaleDateString()]}),(0,r.jsxs)(I.Text,{children:["Updated: ",new Date(c.updated_at).toLocaleDateString()]}),(0,r.jsxs)(I.Text,{children:["Created By: ",c.created_by]})]})]}),(0,r.jsxs)(p.Card,{children:[(0,r.jsx)(I.Text,{children:"Budget Status"}),(0,r.jsxs)("div",{className:"mt-2",children:[(0,r.jsxs)(ee.Title,{children:["$",(0,X.formatNumberWithCommas)(c.spend,4)]}),(0,r.jsxs)(I.Text,{children:["of"," ",null===c.litellm_budget_table.max_budget?"Unlimited":`$${(0,X.formatNumberWithCommas)(c.litellm_budget_table.max_budget,4)}`]}),c.litellm_budget_table.budget_duration&&(0,r.jsxs)(I.Text,{className:"text-gray-500",children:["Reset: ",c.litellm_budget_table.budget_duration]})]})]}),(0,r.jsxs)(p.Card,{children:[(0,r.jsx)(I.Text,{children:"Rate Limits"}),(0,r.jsxs)("div",{className:"mt-2",children:[(0,r.jsxs)(I.Text,{children:["TPM: ",c.litellm_budget_table.tpm_limit||"Unlimited"]}),(0,r.jsxs)(I.Text,{children:["RPM: ",c.litellm_budget_table.rpm_limit||"Unlimited"]}),c.litellm_budget_table.max_parallel_requests&&(0,r.jsxs)(I.Text,{children:["Max Parallel Requests: ",c.litellm_budget_table.max_parallel_requests]})]})]}),(0,r.jsxs)(p.Card,{children:[(0,r.jsx)(I.Text,{children:"Models"}),(0,r.jsx)("div",{className:"mt-2 flex flex-wrap gap-2",children:0===c.models.length?(0,r.jsx)(g.Badge,{color:"red",children:"All proxy models"}):c.models.map((e,l)=>(0,r.jsx)(g.Badge,{color:"red",children:e},l))})]}),(0,r.jsxs)(p.Card,{children:[(0,r.jsx)(I.Text,{children:"Teams"}),(0,r.jsx)("div",{className:"mt-2 flex flex-wrap gap-2",children:c.teams?.map((e,l)=>(0,r.jsx)(g.Badge,{color:"red",children:A[e.team_id]||e.team_id},l))})]}),(0,r.jsx)(eo.default,{objectPermission:c.object_permission,variant:"card",accessToken:s})]})},{key:"members",label:"Members",children:(0,r.jsx)("div",{className:"space-y-4",children:(0,r.jsx)(ei.default,{members:(c.members||[]).map(e=>({role:e.user_role||"",user_id:e.user_id,user_email:e.user_email})),canEdit:M,onEdit:e=>{C(e),y(!0)},onDelete:e=>U(e),onAddMember:()=>_(!0),roleColumnTitle:"Organization Role",extraColumns:K,emptyText:"No members found"})})},{key:"settings",label:"Settings",children:(0,r.jsxs)(p.Card,{className:"overflow-y-auto max-h-[65vh]",children:[(0,r.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,r.jsx)(ee.Title,{children:"Organization Settings"}),M&&!x&&(0,r.jsx)(h.Button,{onClick:()=>b(!0),children:"Edit Settings"})]}),x?(0,r.jsxs)(F.Form,{form:u,onFinish:q,initialValues:{organization_alias:c.organization_alias,models:c.models,tpm_limit:c.litellm_budget_table.tpm_limit,rpm_limit:c.litellm_budget_table.rpm_limit,max_budget:c.litellm_budget_table.max_budget,budget_duration:c.litellm_budget_table.budget_duration,metadata:c.metadata?JSON.stringify(c.metadata,null,2):"",vector_stores:c.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:c.object_permission?.mcp_servers||[],accessGroups:c.object_permission?.mcp_access_groups||[]}},layout:"vertical",children:[(0,r.jsx)(F.Form.Item,{label:"Organization Name",name:"organization_alias",rules:[{required:!0,message:"Please input an organization name"}],children:(0,r.jsx)(O.TextInput,{})}),(0,r.jsx)(F.Form.Item,{label:"Models",name:"models",children:(0,r.jsx)(Q.ModelSelect,{value:u.getFieldValue("models"),onChange:e=>u.setFieldValue("models",e),context:"organization",options:{includeSpecialOptions:!0,showAllProxyModelsOverride:!0}})}),(0,r.jsx)(F.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ed.default,{step:.01,precision:2,style:{width:"100%"}})}),(0,r.jsx)(F.Form.Item,{label:"Reset Budget",name:"budget_duration",children:(0,r.jsxs)(E.Select,{placeholder:"n/a",children:[(0,r.jsx)(E.Select.Option,{value:"24h",children:"daily"}),(0,r.jsx)(E.Select.Option,{value:"7d",children:"weekly"}),(0,r.jsx)(E.Select.Option,{value:"30d",children:"monthly"})]})}),(0,r.jsx)(F.Form.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ed.default,{step:1,style:{width:"100%"}})}),(0,r.jsx)(F.Form.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ed.default,{step:1,style:{width:"100%"}})}),(0,r.jsx)(F.Form.Item,{label:"Vector Stores",name:"vector_stores",children:(0,r.jsx)(em.default,{onChange:e=>u.setFieldValue("vector_stores",e),value:u.getFieldValue("vector_stores"),accessToken:s||"",placeholder:"Select vector stores"})}),(0,r.jsx)(F.Form.Item,{label:"MCP Servers & Access Groups",name:"mcp_servers_and_groups",children:(0,r.jsx)(H.default,{onChange:e=>u.setFieldValue("mcp_servers_and_groups",e),value:u.getFieldValue("mcp_servers_and_groups"),accessToken:s||"",placeholder:"Select MCP servers and access groups"})}),(0,r.jsx)(F.Form.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(P.Input.TextArea,{rows:4})}),(0,r.jsx)("div",{className:"sticky z-10 bg-white p-4 border-t border-gray-200 -bottom-6 -inset-x-6",children:(0,r.jsxs)("div",{className:"flex justify-end items-center gap-2",children:[(0,r.jsx)(h.Button,{variant:"secondary",onClick:()=>b(!1),disabled:T,children:"Cancel"}),(0,r.jsx)(h.Button,{type:"submit",loading:T,children:"Save Changes"})]})})]}):(0,r.jsxs)("div",{className:"space-y-4",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)(I.Text,{className:"font-medium",children:"Organization Name"}),(0,r.jsx)("div",{children:c.organization_alias})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)(I.Text,{className:"font-medium",children:"Organization ID"}),(0,r.jsx)("div",{className:"font-mono",children:c.organization_id})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)(I.Text,{className:"font-medium",children:"Created At"}),(0,r.jsx)("div",{children:new Date(c.created_at).toLocaleString()})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)(I.Text,{className:"font-medium",children:"Models"}),(0,r.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:c.models.map((e,l)=>(0,r.jsx)(g.Badge,{color:"red",children:e},l))})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)(I.Text,{className:"font-medium",children:"Rate Limits"}),(0,r.jsxs)("div",{children:["TPM: ",c.litellm_budget_table.tpm_limit||"Unlimited"]}),(0,r.jsxs)("div",{children:["RPM: ",c.litellm_budget_table.rpm_limit||"Unlimited"]})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)(I.Text,{className:"font-medium",children:"Budget"}),(0,r.jsxs)("div",{children:["Max:"," ",null!==c.litellm_budget_table.max_budget?`$${(0,X.formatNumberWithCommas)(c.litellm_budget_table.max_budget,4)}`:"No Limit"]}),(0,r.jsxs)("div",{children:["Reset: ",c.litellm_budget_table.budget_duration||"Never"]})]}),(0,r.jsx)(eo.default,{objectPermission:c.object_permission,variant:"inline",className:"pt-4 border-t border-gray-200",accessToken:s})]})]})}]}),(0,r.jsx)(en.default,{isVisible:v,onCancel:()=>_(!1),onSubmit:L,accessToken:s,title:"Add Organization Member",roles:[{label:"org_admin",value:"org_admin",description:"Can add and remove members, and change their roles."},{label:"internal_user",value:"internal_user",description:"Can view/create keys for themselves within organization."},{label:"internal_user_viewer",value:"internal_user_viewer",description:"Can only view their keys within organization."}],defaultRole:"internal_user"}),(0,r.jsx)(ec.default,{visible:f,onCancel:()=>y(!1),onSubmit:D,initialData:w,mode:"edit",config:{title:"Edit Member",showEmail:!0,showUserId:!0,roleOptions:[{label:"Org Admin",value:"org_admin"},{label:"Internal User",value:"internal_user"},{label:"Internal User Viewer",value:"internal_user_viewer"}]}})]})},ex=({userRole:e,accessToken:s,lastRefreshed:a,handleRefreshClick:i,premiumUser:n})=>{let[o,Y]=(0,B.useState)(null),[X,J]=(0,B.useState)(!1),[Z,ee]=(0,B.useState)(!1),[er,el]=(0,B.useState)(null),[et,es]=(0,B.useState)(!1),[ea,ei]=(0,B.useState)(!1),[en]=F.Form.useForm(),[eo,ec]=(0,B.useState)({}),[ex,eg]=(0,B.useState)(!1),[eh,ep]=(0,B.useState)({org_id:"",org_alias:"",sort_by:"created_at",sort_order:"desc"}),eb=(0,R.useQueryClient)(),{data:ej=[]}=(0,l.useOrganizations)({org_id:eh.org_id,org_alias:eh.org_alias}),{data:ev=[]}=(0,t.useUserModels)(),e_=()=>eb.invalidateQueries({queryKey:l.organizationKeys.lists()}),ef=async()=>{if(er&&s)try{es(!0),await (0,W.organizationDeleteCall)(s,er),G.default.success("Organization deleted successfully"),ee(!1),el(null),await e_()}catch(e){console.error("Error deleting organization:",e)}finally{es(!1)}},ey=async e=>{try{if(!s)return;(e.allowed_vector_store_ids&&e.allowed_vector_store_ids.length>0||e.allowed_mcp_servers_and_groups&&(e.allowed_mcp_servers_and_groups.servers?.length>0||e.allowed_mcp_servers_and_groups.accessGroups?.length>0))&&(e.object_permission={},e.allowed_vector_store_ids&&e.allowed_vector_store_ids.length>0&&(e.object_permission.vector_stores=e.allowed_vector_store_ids,delete e.allowed_vector_store_ids),e.allowed_mcp_servers_and_groups&&(e.allowed_mcp_servers_and_groups.servers?.length>0&&(e.object_permission.mcp_servers=e.allowed_mcp_servers_and_groups.servers),e.allowed_mcp_servers_and_groups.accessGroups?.length>0&&(e.object_permission.mcp_access_groups=e.allowed_mcp_servers_and_groups.accessGroups),delete e.allowed_mcp_servers_and_groups)),await (0,W.organizationCreateCall)(s,e),G.default.success("Organization created successfully"),ei(!1),en.resetFields(),await e_()}catch(e){console.error("Error creating organization:",e)}};return n?(0,r.jsxs)("div",{className:"w-full mx-4 h-[75vh]",children:[(0,r.jsx)(j.Grid,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,r.jsxs)(b.Col,{numColSpan:1,className:"flex flex-col gap-2",children:[("Admin"===e||"Org Admin"===e)&&(0,r.jsx)(h.Button,{className:"w-fit",onClick:()=>ei(!0),children:"+ Create New Organization"}),o?(0,r.jsx)(eu,{organizationId:o,onClose:()=>{Y(null),J(!1)},accessToken:s,is_org_admin:!0,is_proxy_admin:"Admin"===e,userModels:ev,editOrg:X}):(0,r.jsxs)(f.TabGroup,{className:"gap-2 h-[75vh] w-full",children:[(0,r.jsxs)(S.TabList,{className:"flex justify-between mt-2 w-full items-center",children:[(0,r.jsx)("div",{className:"flex",children:(0,r.jsx)(_.Tab,{children:"Your Organizations"})}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[a&&(0,r.jsxs)(I.Text,{children:["Last Refreshed: ",a]}),(0,r.jsx)(v.Icon,{icon:x.RefreshIcon,variant:"shadow",size:"xs",className:"self-center",onClick:i})]})]}),(0,r.jsx)(z.TabPanels,{children:(0,r.jsxs)(M.TabPanel,{children:[(0,r.jsx)(I.Text,{children:"Click on “Organization ID” to view organization details."}),(0,r.jsx)(j.Grid,{numItems:1,className:"gap-2 pt-2 pb-2 h-[75vh] w-full mt-2",children:(0,r.jsx)(b.Col,{numColSpan:1,children:(0,r.jsxs)(p.Card,{className:"w-full mx-auto flex-auto overflow-hidden overflow-y-auto max-h-[50vh]",children:[(0,r.jsx)("div",{className:"border-b px-6 py-4",children:(0,r.jsx)("div",{className:"flex flex-col space-y-4",children:(0,r.jsx)(d,{filters:eh,showFilters:ex,onToggleFilters:eg,onChange:(e,r)=>{ep(l=>({...l,[e]:r}))},onReset:()=>{ep({org_id:"",org_alias:"",sort_by:"created_at",sort_order:"desc"})}})})}),(0,r.jsxs)(y.Table,{children:[(0,r.jsx)(N.TableHead,{children:(0,r.jsxs)(T.TableRow,{children:[(0,r.jsx)(k.TableHeaderCell,{children:"Organization ID"}),(0,r.jsx)(k.TableHeaderCell,{children:"Organization Name"}),(0,r.jsx)(k.TableHeaderCell,{children:"Created"}),(0,r.jsx)(k.TableHeaderCell,{children:"Spend (USD)"}),(0,r.jsx)(k.TableHeaderCell,{children:"Budget (USD)"}),(0,r.jsx)(k.TableHeaderCell,{children:"Models"}),(0,r.jsx)(k.TableHeaderCell,{children:"TPM / RPM Limits"}),(0,r.jsx)(k.TableHeaderCell,{children:"Info"}),(0,r.jsx)(k.TableHeaderCell,{children:"Actions"})]})}),(0,r.jsx)(w.TableBody,{children:ej&&ej.length>0?ej.sort((e,r)=>new Date(r.created_at).getTime()-new Date(e.created_at).getTime()).map(l=>(0,r.jsxs)(T.TableRow,{children:[(0,r.jsx)(C.TableCell,{children:(0,r.jsx)(U.IdCell,{value:l.organization_id,onClick:Y})}),(0,r.jsx)(C.TableCell,{children:l.organization_alias}),(0,r.jsx)(C.TableCell,{children:(0,r.jsx)(D.DateCell,{value:l.created_at,precision:"date"})}),(0,r.jsx)(C.TableCell,{children:(0,r.jsx)(V.MoneyCell,{value:l.spend,decimals:4})}),(0,r.jsx)(C.TableCell,{children:(0,r.jsx)(V.MoneyCell,{value:l.litellm_budget_table?.max_budget,decimals:2,emptyText:"Unlimited",showZero:!0})}),(0,r.jsx)(C.TableCell,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},className:l.models.length>3?"px-0":"",children:(0,r.jsx)("div",{className:"flex flex-col",children:Array.isArray(l.models)?(0,r.jsx)("div",{className:"flex flex-col",children:0===l.models.length?(0,r.jsx)(g.Badge,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(I.Text,{children:"All Proxy Models"})}):(0,r.jsx)(r.Fragment,{children:(0,r.jsxs)("div",{className:"flex items-start",children:[l.models.length>3&&(0,r.jsx)("div",{children:(0,r.jsx)(v.Icon,{icon:eo[l.organization_id||""]?m.ChevronDownIcon:u.ChevronRightIcon,className:"cursor-pointer",size:"xs",onClick:()=>{ec(e=>({...e,[l.organization_id||""]:!e[l.organization_id||""]}))}})}),(0,r.jsxs)("div",{className:"flex flex-wrap gap-1",children:[l.models.slice(0,3).map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(g.Badge,{size:"xs",color:"red",children:(0,r.jsx)(I.Text,{children:"All Proxy Models"})},l):(0,r.jsx)(g.Badge,{size:"xs",color:"blue",children:(0,r.jsx)(I.Text,{children:e.length>30?`${(0,K.getModelDisplayName)(e).slice(0,30)}...`:(0,K.getModelDisplayName)(e)})},l)),l.models.length>3&&!eo[l.organization_id||""]&&(0,r.jsx)(g.Badge,{size:"xs",color:"gray",className:"cursor-pointer",children:(0,r.jsxs)(I.Text,{children:["+",l.models.length-3," ",l.models.length-3==1?"more model":"more models"]})}),eo[l.organization_id||""]&&(0,r.jsx)("div",{className:"flex flex-wrap gap-1",children:l.models.slice(3).map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(g.Badge,{size:"xs",color:"red",children:(0,r.jsx)(I.Text,{children:"All Proxy Models"})},l+3):(0,r.jsx)(g.Badge,{size:"xs",color:"blue",children:(0,r.jsx)(I.Text,{children:e.length>30?`${(0,K.getModelDisplayName)(e).slice(0,30)}...`:(0,K.getModelDisplayName)(e)})},l+3))})]})]})})}):null})}),(0,r.jsx)(C.TableCell,{children:(0,r.jsxs)(I.Text,{children:["TPM:"," ",l.litellm_budget_table?.tpm_limit?l.litellm_budget_table?.tpm_limit:"Unlimited",(0,r.jsx)("br",{}),"RPM:"," ",l.litellm_budget_table?.rpm_limit?l.litellm_budget_table?.rpm_limit:"Unlimited"]})}),(0,r.jsx)(C.TableCell,{children:(0,r.jsxs)(I.Text,{children:[l.members?.length||0," Members"]})}),(0,r.jsx)(C.TableCell,{children:"Admin"===e&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)($.default,{variant:"Edit",tooltipText:"Edit organization",onClick:()=>{Y(l.organization_id),J(!0)}}),(0,r.jsx)($.default,{variant:"Delete",tooltipText:"Delete organization",onClick:()=>{var e;(e=l.organization_id)&&(el(e),ee(!0))}})]})})]},l.organization_id)):null})]})]})})})]})})]})]})}),(0,r.jsx)(A.Modal,{title:"Create Organization",visible:ea,width:800,footer:null,onCancel:()=>{ei(!1),en.resetFields()},children:(0,r.jsxs)(F.Form,{form:en,onFinish:ey,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsx)(F.Form.Item,{label:"Organization Name",name:"organization_alias",rules:[{required:!0,message:"Please input an organization name"}],children:(0,r.jsx)(O.TextInput,{placeholder:""})}),(0,r.jsx)(F.Form.Item,{label:"Models",name:"models",children:(0,r.jsx)(Q.ModelSelect,{options:{showAllProxyModelsOverride:!0,includeSpecialOptions:!0},value:en.getFieldValue("models"),onChange:e=>en.setFieldValue("models",e),context:"organization"})}),(0,r.jsx)(F.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ed.default,{step:.01,precision:2,width:200})}),(0,r.jsx)(F.Form.Item,{label:"Reset Budget",name:"budget_duration",children:(0,r.jsxs)(E.Select,{defaultValue:null,placeholder:"n/a",children:[(0,r.jsx)(E.Select.Option,{value:"24h",children:"daily"}),(0,r.jsx)(E.Select.Option,{value:"7d",children:"weekly"}),(0,r.jsx)(E.Select.Option,{value:"30d",children:"monthly"})]})}),(0,r.jsx)(F.Form.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ed.default,{step:1,width:400})}),(0,r.jsx)(F.Form.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ed.default,{step:1,width:400})}),(0,r.jsx)(F.Form.Item,{label:(0,r.jsxs)("span",{children:["Allowed Vector Stores"," ",(0,r.jsx)(L.Tooltip,{title:"Select which vector stores this organization can access by default. Leave empty for access to all vector stores",children:(0,r.jsx)(c.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_vector_store_ids",className:"mt-4",help:"Select vector stores this organization can access. Leave empty for access to all vector stores",children:(0,r.jsx)(em.default,{onChange:e=>en.setFieldValue("allowed_vector_store_ids",e),value:en.getFieldValue("allowed_vector_store_ids"),accessToken:s||"",placeholder:"Select vector stores (optional)"})}),(0,r.jsx)(F.Form.Item,{label:(0,r.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,r.jsx)(L.Tooltip,{title:"Select which MCP servers and access groups this organization can access by default.",children:(0,r.jsx)(c.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_mcp_servers_and_groups",className:"mt-4",help:"Select MCP servers and access groups this organization can access.",children:(0,r.jsx)(H.default,{onChange:e=>en.setFieldValue("allowed_mcp_servers_and_groups",e),value:en.getFieldValue("allowed_mcp_servers_and_groups"),accessToken:s||"",placeholder:"Select MCP servers and access groups (optional)"})}),(0,r.jsx)(F.Form.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(P.Input.TextArea,{rows:4})}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(h.Button,{type:"submit",children:"Create Organization"})})]})}),(0,r.jsx)(q.default,{isOpen:Z,title:"Delete Organization?",message:"Are you sure you want to delete this organization? This action cannot be undone.",resourceInformationTitle:"Organization Information",resourceInformation:[{label:"Organization ID",value:er,code:!0}],onCancel:()=>{ee(!1),el(null)},onOk:ef,confirmLoading:et})]}):(0,r.jsx)("div",{children:(0,r.jsxs)(I.Text,{children:["This is a LiteLLM Enterprise feature, and requires a valid key to use. Get a trial key"," ",(0,r.jsx)("a",{href:"https://www.litellm.ai/#pricing",target:"_blank",rel:"noopener noreferrer",children:"here"}),"."]})})};var eg=e.i(135214);e.s(["default",0,function(){let{accessToken:e,userRole:l,premiumUser:t}=(0,eg.default)();return(0,r.jsx)(ex,{userRole:l??"",accessToken:e,premiumUser:t??!1})}],526612)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/05efmcn18yevj.js b/litellm/proxy/_experimental/out/_next/static/chunks/05efmcn18yevj.js new file mode 100644 index 00000000000..932c4733760 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/05efmcn18yevj.js @@ -0,0 +1,17 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,270377,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var i=e.i(9583),a=n.forwardRef(function(e,a){return n.createElement(i.default,(0,t.default)({},e,{ref:a,icon:l}))});e.s(["ExclamationCircleOutlined",0,a],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),l=e.i(529681),i=e.i(242064),a=e.i(517455),r=e.i(185793),o=e.i(721369),s=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,l=Object.getOwnPropertySymbols(e);it.indexOf(l[i])&&Object.prototype.propertyIsEnumerable.call(e,l[i])&&(n[l[i]]=e[l[i]]);return n};let c=e=>{var{prefixCls:l,className:a,hoverable:r=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:c}=t.useContext(i.ConfigContext),d=c("card",l),u=(0,n.default)(`${d}-grid`,a,{[`${d}-grid-hoverable`]:r});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var d=e.i(915654),u=e.i(183293),g=e.i(246422),m=e.i(838378);let b=(0,g.genStyleHooks)("Card",e=>{let t=(0,m.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:n,cardHeadPadding:l,colorBorderSecondary:i,boxShadowTertiary:a,bodyPadding:r,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:a},[`${t}-head`]:(e=>{let{antCls:t,componentCls:n,headerHeight:l,headerPadding:i,tabsMarginBottom:a}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:l,marginBottom:-1,padding:`0 ${(0,d.unit)(i)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${n}-typography, + > ${n}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:a,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:r,borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:n,cardShadow:l,lineWidth:i}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,d.unit)(i)} 0 0 0 ${n}, + 0 ${(0,d.unit)(i)} 0 0 ${n}, + ${(0,d.unit)(i)} ${(0,d.unit)(i)} 0 0 ${n}, + ${(0,d.unit)(i)} 0 0 0 ${n} inset, + 0 ${(0,d.unit)(i)} 0 0 ${n} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:l}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:n,actionsLiMargin:l,cardActionsIconSize:i,colorBorderSecondary:a,actionsBg:r}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:r,borderTop:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${a}`,display:"flex",borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:l,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${n}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,d.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${n}`]:{fontSize:i,lineHeight:(0,d.unit)(e.calc(i).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${a}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,d.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${i}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:n}},[`${t}-contain-grid`]:{borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:l}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:n,headerPadding:l,bodyPadding:i}=e;return{[`${t}-head`]:{padding:`0 ${(0,d.unit)(l)}`,background:n,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,d.unit)(e.padding)} ${(0,d.unit)(i)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:n,headerPaddingSM:l,headerHeightSM:i,headerFontSizeSM:a}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:i,padding:`0 ${(0,d.unit)(l)}`,fontSize:a,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:n}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,n;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(n=e.headerPadding)?n:e.paddingLG}});var p=e.i(792812),f=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,l=Object.getOwnPropertySymbols(e);it.indexOf(l[i])&&Object.prototype.propertyIsEnumerable.call(e,l[i])&&(n[l[i]]=e[l[i]]);return n};let h=e=>{let{actionClasses:n,actions:l=[],actionStyle:i}=e;return t.createElement("ul",{className:n,style:i},l.map((e,n)=>{let i=`action-${n}`;return t.createElement("li",{style:{width:`${100/l.length}%`},key:i},t.createElement("span",null,e))}))},$=t.forwardRef((e,s)=>{let d,{prefixCls:u,className:g,rootClassName:m,style:$,extra:y,headStyle:v={},bodyStyle:x={},title:j,loading:O,bordered:C,variant:S,size:w,type:k,cover:E,actions:N,tabList:T,children:B,activeTabKey:z,defaultActiveTabKey:M,tabBarExtraContent:L,hoverable:P,tabProps:R={},classNames:I,styles:H}=e,A=f(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:W,direction:D,card:F}=t.useContext(i.ConfigContext),[G]=(0,p.default)("card",S,C),q=e=>{var t;return(0,n.default)(null==(t=null==F?void 0:F.classNames)?void 0:t[e],null==I?void 0:I[e])},X=e=>{var t;return Object.assign(Object.assign({},null==(t=null==F?void 0:F.styles)?void 0:t[e]),null==H?void 0:H[e])},K=t.useMemo(()=>{let e=!1;return t.Children.forEach(B,t=>{(null==t?void 0:t.type)===c&&(e=!0)}),e},[B]),U=W("card",u),[J,Q,V]=b(U),Y=t.createElement(r.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},B),Z=void 0!==z,_=Object.assign(Object.assign({},R),{[Z?"activeKey":"defaultActiveKey"]:Z?z:M,tabBarExtraContent:L}),ee=(0,a.default)(w),et=ee&&"default"!==ee?ee:"large",en=T?t.createElement(o.default,Object.assign({size:et},_,{className:`${U}-head-tabs`,onChange:t=>{var n;null==(n=e.onTabChange)||n.call(e,t)},items:T.map(e=>{var{tab:t}=e;return Object.assign({label:t},f(e,["tab"]))})})):null;if(j||y||en){let e=(0,n.default)(`${U}-head`,q("header")),l=(0,n.default)(`${U}-head-title`,q("title")),i=(0,n.default)(`${U}-extra`,q("extra")),a=Object.assign(Object.assign({},v),X("header"));d=t.createElement("div",{className:e,style:a},t.createElement("div",{className:`${U}-head-wrapper`},j&&t.createElement("div",{className:l,style:X("title")},j),y&&t.createElement("div",{className:i,style:X("extra")},y)),en)}let el=(0,n.default)(`${U}-cover`,q("cover")),ei=E?t.createElement("div",{className:el,style:X("cover")},E):null,ea=(0,n.default)(`${U}-body`,q("body")),er=Object.assign(Object.assign({},x),X("body")),eo=t.createElement("div",{className:ea,style:er},O?Y:B),es=(0,n.default)(`${U}-actions`,q("actions")),ec=(null==N?void 0:N.length)?t.createElement(h,{actionClasses:es,actionStyle:X("actions"),actions:N}):null,ed=(0,l.default)(A,["onTabChange"]),eu=(0,n.default)(U,null==F?void 0:F.className,{[`${U}-loading`]:O,[`${U}-bordered`]:"borderless"!==G,[`${U}-hoverable`]:P,[`${U}-contain-grid`]:K,[`${U}-contain-tabs`]:null==T?void 0:T.length,[`${U}-${ee}`]:ee,[`${U}-type-${k}`]:!!k,[`${U}-rtl`]:"rtl"===D},g,m,Q,V),eg=Object.assign(Object.assign({},null==F?void 0:F.style),$);return J(t.createElement("div",Object.assign({ref:s},ed,{className:eu,style:eg}),d,ei,eo,ec))});var y=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,l=Object.getOwnPropertySymbols(e);it.indexOf(l[i])&&Object.prototype.propertyIsEnumerable.call(e,l[i])&&(n[l[i]]=e[l[i]]);return n};$.Grid=c,$.Meta=e=>{let{prefixCls:l,className:a,avatar:r,title:o,description:s}=e,c=y(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:d}=t.useContext(i.ConfigContext),u=d("card",l),g=(0,n.default)(`${u}-meta`,a),m=r?t.createElement("div",{className:`${u}-meta-avatar`},r):null,b=o?t.createElement("div",{className:`${u}-meta-title`},o):null,p=s?t.createElement("div",{className:`${u}-meta-description`},s):null,f=b||p?t.createElement("div",{className:`${u}-meta-detail`},b,p):null;return t.createElement("div",Object.assign({},c,{className:g}),m,f)},e.s(["Card",0,$],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),l=e.i(908206),i=e.i(242064),a=e.i(517455),r=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var c=e.i(876556),d=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,l=Object.getOwnPropertySymbols(e);it.indexOf(l[i])&&Object.prototype.propertyIsEnumerable.call(e,l[i])&&(n[l[i]]=e[l[i]]);return n},u=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,l=Object.getOwnPropertySymbols(e);it.indexOf(l[i])&&Object.prototype.propertyIsEnumerable.call(e,l[i])&&(n[l[i]]=e[l[i]]);return n};let g=e=>{let{itemPrefixCls:l,component:i,span:a,className:r,style:o,labelStyle:c,contentStyle:d,bordered:u,label:g,content:m,colon:b,type:p,styles:f}=e,{classNames:h}=t.useContext(s),$=Object.assign(Object.assign({},c),null==f?void 0:f.label),y=Object.assign(Object.assign({},d),null==f?void 0:f.content);if(u)return t.createElement(i,{colSpan:a,style:o,className:(0,n.default)(r,{[`${l}-item-${p}`]:"label"===p||"content"===p,[null==h?void 0:h.label]:(null==h?void 0:h.label)&&"label"===p,[null==h?void 0:h.content]:(null==h?void 0:h.content)&&"content"===p})},null!=g&&t.createElement("span",{style:$},g),null!=m&&t.createElement("span",{style:y},m));return t.createElement(i,{colSpan:a,style:o,className:(0,n.default)(`${l}-item`,r)},t.createElement("div",{className:`${l}-item-container`},null!=g&&t.createElement("span",{style:$,className:(0,n.default)(`${l}-item-label`,null==h?void 0:h.label,{[`${l}-item-no-colon`]:!b})},g),null!=m&&t.createElement("span",{style:y,className:(0,n.default)(`${l}-item-content`,null==h?void 0:h.content)},m)))};function m(e,{colon:n,prefixCls:l,bordered:i},{component:a,type:r,showLabel:o,showContent:s,labelStyle:c,contentStyle:d,styles:u}){return e.map(({label:e,children:m,prefixCls:b=l,className:p,style:f,labelStyle:h,contentStyle:$,span:y=1,key:v,styles:x},j)=>"string"==typeof a?t.createElement(g,{key:`${r}-${v||j}`,className:p,style:f,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),h),null==x?void 0:x.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),$),null==x?void 0:x.content)},span:y,colon:n,component:a,itemPrefixCls:b,bordered:i,label:o?e:null,content:s?m:null,type:r}):[t.createElement(g,{key:`label-${v||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),f),h),null==x?void 0:x.label),span:1,colon:n,component:a[0],itemPrefixCls:b,bordered:i,label:e,type:"label"}),t.createElement(g,{key:`content-${v||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),f),$),null==x?void 0:x.content),span:2*y-1,component:a[1],itemPrefixCls:b,bordered:i,content:m,type:"content"})])}let b=e=>{let n=t.useContext(s),{prefixCls:l,vertical:i,row:a,index:r,bordered:o}=e;return i?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${r}`,className:`${l}-row`},m(a,e,Object.assign({component:"th",type:"label",showLabel:!0},n))),t.createElement("tr",{key:`content-${r}`,className:`${l}-row`},m(a,e,Object.assign({component:"td",type:"content",showContent:!0},n)))):t.createElement("tr",{key:r,className:`${l}-row`},m(a,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},n)))};e.i(296059);var p=e.i(915654),f=e.i(183293),h=e.i(246422),$=e.i(838378);let y=(0,h.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:n,itemPaddingBottom:l,itemPaddingEnd:i,colonMarginRight:a,colonMarginLeft:r,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,f.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:n}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.padding)} ${(0,p.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:n,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingSM)} ${(0,p.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingXS)} ${(0,p.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},f.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:n,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:l,paddingInlineEnd:i},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,p.unit)(r)} ${(0,p.unit)(a)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,$.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var v=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,l=Object.getOwnPropertySymbols(e);it.indexOf(l[i])&&Object.prototype.propertyIsEnumerable.call(e,l[i])&&(n[l[i]]=e[l[i]]);return n};let x=e=>{let g,{prefixCls:m,title:p,extra:f,column:h,colon:$=!0,bordered:x,layout:j,children:O,className:C,rootClassName:S,style:w,size:k,labelStyle:E,contentStyle:N,styles:T,items:B,classNames:z}=e,M=v(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:L,direction:P,className:R,style:I,classNames:H,styles:A}=(0,i.useComponentConfig)("descriptions"),W=L("descriptions",m),D=(0,r.default)(),F=t.useMemo(()=>{var e;return"number"==typeof h?h:null!=(e=(0,l.matchScreen)(D,Object.assign(Object.assign({},o),h)))?e:3},[D,h]),G=(g=t.useMemo(()=>B||(0,c.default)(O).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[B,O]),t.useMemo(()=>g.map(e=>{var{span:t}=e,n=d(e,["span"]);return"filled"===t?Object.assign(Object.assign({},n),{filled:!0}):Object.assign(Object.assign({},n),{span:"number"==typeof t?t:(0,l.matchScreen)(D,t)})}),[g,D])),q=(0,a.default)(k),X=((e,n)=>{let[l,i]=(0,t.useMemo)(()=>{let t,l,i,a;return t=[],l=[],i=!1,a=0,n.filter(e=>e).forEach(n=>{let{filled:r}=n,o=u(n,["filled"]);if(r){l.push(o),t.push(l),l=[],a=0;return}let s=e-a;(a+=n.span||1)>=e?(a>e?(i=!0,l.push(Object.assign(Object.assign({},o),{span:s}))):l.push(o),t.push(l),l=[],a=0):l.push(o)}),l.length>0&&t.push(l),[t=t.map(t=>{let n=t.reduce((e,t)=>e+(t.span||1),0);if(n({labelStyle:E,contentStyle:N,styles:{content:Object.assign(Object.assign({},A.content),null==T?void 0:T.content),label:Object.assign(Object.assign({},A.label),null==T?void 0:T.label)},classNames:{label:(0,n.default)(H.label,null==z?void 0:z.label),content:(0,n.default)(H.content,null==z?void 0:z.content)}}),[E,N,T,z,H,A]);return K(t.createElement(s.Provider,{value:Q},t.createElement("div",Object.assign({className:(0,n.default)(W,R,H.root,null==z?void 0:z.root,{[`${W}-${q}`]:q&&"default"!==q,[`${W}-bordered`]:!!x,[`${W}-rtl`]:"rtl"===P},C,S,U,J),style:Object.assign(Object.assign(Object.assign(Object.assign({},I),A.root),null==T?void 0:T.root),w)},M),(p||f)&&t.createElement("div",{className:(0,n.default)(`${W}-header`,H.header,null==z?void 0:z.header),style:Object.assign(Object.assign({},A.header),null==T?void 0:T.header)},p&&t.createElement("div",{className:(0,n.default)(`${W}-title`,H.title,null==z?void 0:z.title),style:Object.assign(Object.assign({},A.title),null==T?void 0:T.title)},p),f&&t.createElement("div",{className:(0,n.default)(`${W}-extra`,H.extra,null==z?void 0:z.extra),style:Object.assign(Object.assign({},A.extra),null==T?void 0:T.extra)},f)),t.createElement("div",{className:`${W}-view`},t.createElement("table",null,t.createElement("tbody",null,X.map((e,n)=>t.createElement(b,{key:n,index:n,colon:$,prefixCls:W,vertical:"vertical"===j,bordered:x,row:e}))))))))};x.Item=({children:e})=>e,e.s(["Descriptions",0,x],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),n=e.i(732961),l=e.i(289882),i=e.i(170517),a=e.i(628882),r=e.i(320890),o=e.i(104458),s=e.i(722319),c=e.i(8398),d=e.i(279728);e.i(765846);var u=e.i(602716),g=e.i(328052),m=e.i(135551);let b=(e,t)=>new m.FastColor(e).setA(t).toRgbString(),p=(e,t)=>new m.FastColor(e).lighten(t).toHexString(),f=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},h=(e,t)=>{let n=e||"#000",l=t||"#fff";return{colorBgBase:n,colorTextBase:l,colorText:b(l,.85),colorTextSecondary:b(l,.65),colorTextTertiary:b(l,.45),colorTextQuaternary:b(l,.25),colorFill:b(l,.18),colorFillSecondary:b(l,.12),colorFillTertiary:b(l,.08),colorFillQuaternary:b(l,.04),colorBgSolid:b(l,.95),colorBgSolidHover:b(l,1),colorBgSolidActive:b(l,.9),colorBgElevated:p(n,12),colorBgContainer:p(n,8),colorBgLayout:p(n,0),colorBgSpotlight:p(n,26),colorBgBlur:b(l,.04),colorBorder:p(n,26),colorBorderSecondary:p(n,19)}},$={defaultSeed:r.defaultConfig.token,useToken:function(){let[e,t,n]=(0,o.useToken)();return{theme:e,token:t,hashId:n}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let n=Object.keys(i.defaultPresetColors).map(t=>{let n=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,l,i)=>(e[`${t}-${i+1}`]=n[i],e[`${t}${i+1}`]=n[i],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),l=null!=t?t:(0,s.default)(e),a=(0,g.default)(e,{generateColorPalettes:f,generateNeutralColorPalettes:h});return Object.assign(Object.assign(Object.assign(Object.assign({},l),n),a),{colorPrimaryBg:a.colorPrimaryBorder,colorPrimaryBgHover:a.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let n=null!=t?t:(0,s.default)(e),l=n.fontSizeSM,i=n.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},n),function(e){let{sizeUnit:t,sizeStep:n}=e,l=n-2;return{sizeXXL:t*(l+10),sizeXL:t*(l+6),sizeLG:t*(l+2),sizeMD:t*(l+2),sizeMS:t*(l+1),size:t*l,sizeSM:t*l,sizeXS:t*(l-1),sizeXXS:t*(l-1)}}(null!=t?t:e)),(0,d.default)(l)),{controlHeight:i}),(0,c.default)(Object.assign(Object.assign({},n),{controlHeight:i})))},getDesignToken:e=>{let r=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):l.default,o=Object.assign(Object.assign({},i.default),null==e?void 0:e.token);return(0,n.getComputedToken)(o,{override:null==e?void 0:e.token},r,a.default)},defaultConfig:r.defaultConfig,_internalContext:r.DesignTokenContext};e.s(["theme",0,$],368869)},127952,e=>{"use strict";var t=e.i(843476),n=e.i(560445),l=e.i(175712),i=e.i(869216),a=e.i(311451),r=e.i(212931),o=e.i(898586),s=e.i(368869),c=e.i(270377),d=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:g,message:m,resourceInformationTitle:b,resourceInformation:p,onCancel:f,onOk:h,confirmLoading:$,requiredConfirmation:y}){let{Title:v,Text:x}=o.Typography,{token:j}=s.theme.useToken(),[O,C]=(0,d.useState)("");return(0,d.useEffect)(()=>{e&&C("")},[e]),(0,t.jsx)(r.Modal,{title:u,open:e,onOk:h,onCancel:f,confirmLoading:$,okText:$?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!y&&O!==y||$},cancelButtonProps:{disabled:$},children:(0,t.jsxs)("div",{className:"space-y-4",children:[g&&(0,t.jsx)(n.Alert,{message:g,type:"warning"}),(0,t.jsx)(l.Card,{title:b,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder}},style:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder},children:(0,t.jsx)(i.Descriptions,{column:1,size:"small",children:p&&p.map(({label:e,value:n,...l})=>(0,t.jsx)(i.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(x,{...l,children:n??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(x,{children:m})}),y&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(x,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(x,{children:"Type "}),(0,t.jsx)(x,{strong:!0,type:"danger",children:y}),(0,t.jsx)(x,{children:" to confirm deletion:"})]}),(0,t.jsx)(a.Input,{value:O,onChange:e=>C(e.target.value),placeholder:y,className:"rounded-md",prefix:(0,t.jsx)(c.ExclamationCircleOutlined,{style:{color:j.colorError}}),autoFocus:!0})]})]})})}])},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),l=e.i(242064),i=e.i(529681);let a=e=>{let{prefixCls:l,className:i,style:a,size:r,shape:o}=e,s=(0,n.default)({[`${l}-lg`]:"large"===r,[`${l}-sm`]:"small"===r}),c=(0,n.default)({[`${l}-circle`]:"circle"===o,[`${l}-square`]:"square"===o,[`${l}-round`]:"round"===o}),d=t.useMemo(()=>"number"==typeof r?{width:r,height:r,lineHeight:`${r}px`}:{},[r]);return t.createElement("span",{className:(0,n.default)(l,s,c,i),style:Object.assign(Object.assign({},d),a)})};e.i(296059);var r=e.i(694758),o=e.i(915654),s=e.i(246422),c=e.i(838378);let d=new r.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),u=e=>({height:e,lineHeight:(0,o.unit)(e)}),g=e=>Object.assign({width:e},u(e)),m=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},u(e)),b=e=>Object.assign({width:e},u(e)),p=(e,t,n)=>{let{skeletonButtonCls:l}=e;return{[`${n}${l}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${n}${l}-round`]:{borderRadius:t}}},f=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},u(e)),h=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:n}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:n,skeletonTitleCls:l,skeletonParagraphCls:i,skeletonButtonCls:a,skeletonInputCls:r,skeletonImageCls:o,controlHeight:s,controlHeightLG:c,controlHeightSM:u,gradientFromColor:h,padding:$,marginSM:y,borderRadius:v,titleHeight:x,blockRadius:j,paragraphLiHeight:O,controlHeightXS:C,paragraphMarginTop:S}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:$,verticalAlign:"top",[n]:Object.assign({display:"inline-block",verticalAlign:"top",background:h},g(s)),[`${n}-circle`]:{borderRadius:"50%"},[`${n}-lg`]:Object.assign({},g(c)),[`${n}-sm`]:Object.assign({},g(u))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[l]:{width:"100%",height:x,background:h,borderRadius:j,[`+ ${i}`]:{marginBlockStart:u}},[i]:{padding:0,"> li":{width:"100%",height:O,listStyle:"none",background:h,borderRadius:j,"+ li":{marginBlockStart:C}}},[`${i}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${l}, ${i} > li`]:{borderRadius:v}}},[`${t}-with-avatar ${t}-content`]:{[l]:{marginBlockStart:y,[`+ ${i}`]:{marginBlockStart:S}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:n,controlHeight:l,controlHeightLG:i,controlHeightSM:a,gradientFromColor:r,calc:o}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[n]:Object.assign({display:"inline-block",verticalAlign:"top",background:r,borderRadius:t,width:o(l).mul(2).equal(),minWidth:o(l).mul(2).equal()},f(l,o))},p(e,l,n)),{[`${n}-lg`]:Object.assign({},f(i,o))}),p(e,i,`${n}-lg`)),{[`${n}-sm`]:Object.assign({},f(a,o))}),p(e,a,`${n}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:n,controlHeight:l,controlHeightLG:i,controlHeightSM:a}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:n},g(l)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},g(i)),[`${t}${t}-sm`]:Object.assign({},g(a))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:n,skeletonInputCls:l,controlHeightLG:i,controlHeightSM:a,gradientFromColor:r,calc:o}=e;return{[l]:Object.assign({display:"inline-block",verticalAlign:"top",background:r,borderRadius:n},m(t,o)),[`${l}-lg`]:Object.assign({},m(i,o)),[`${l}-sm`]:Object.assign({},m(a,o))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:n,gradientFromColor:l,borderRadiusSM:i,calc:a}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:l,borderRadius:i},b(a(n).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(n)),{maxWidth:a(n).mul(4).equal(),maxHeight:a(n).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[a]:{width:"100%"},[r]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${l}, + ${i} > li, + ${n}, + ${a}, + ${r}, + ${o} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:d,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,c.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:n(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:n}=e;return{color:t,colorGradientEnd:n,gradientFromColor:t,gradientToColor:n,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),$=e=>{let{prefixCls:l,className:i,style:a,rows:r=0}=e,o=Array.from({length:r}).map((n,l)=>t.createElement("li",{key:l,style:{width:((e,t)=>{let{width:n,rows:l=2}=t;return Array.isArray(n)?n[e]:l-1===e?n:void 0})(l,e)}}));return t.createElement("ul",{className:(0,n.default)(l,i),style:a},o)},y=({prefixCls:e,className:l,width:i,style:a})=>t.createElement("h3",{className:(0,n.default)(e,l),style:Object.assign({width:i},a)});function v(e){return e&&"object"==typeof e?e:{}}let x=e=>{let{prefixCls:i,loading:r,className:o,rootClassName:s,style:c,children:d,avatar:u=!1,title:g=!0,paragraph:m=!0,active:b,round:p}=e,{getPrefixCls:f,direction:x,className:j,style:O}=(0,l.useComponentConfig)("skeleton"),C=f("skeleton",i),[S,w,k]=h(C);if(r||!("loading"in e)){let e,l,i=!!u,r=!!g,d=!!m;if(i){let n=Object.assign(Object.assign({prefixCls:`${C}-avatar`},r&&!d?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),v(u));e=t.createElement("div",{className:`${C}-header`},t.createElement(a,Object.assign({},n)))}if(r||d){let e,n;if(r){let n=Object.assign(Object.assign({prefixCls:`${C}-title`},!i&&d?{width:"38%"}:i&&d?{width:"50%"}:{}),v(g));e=t.createElement(y,Object.assign({},n))}if(d){let e,l=Object.assign(Object.assign({prefixCls:`${C}-paragraph`},(e={},i&&r||(e.width="61%"),!i&&r?e.rows=3:e.rows=2,e)),v(m));n=t.createElement($,Object.assign({},l))}l=t.createElement("div",{className:`${C}-content`},e,n)}let f=(0,n.default)(C,{[`${C}-with-avatar`]:i,[`${C}-active`]:b,[`${C}-rtl`]:"rtl"===x,[`${C}-round`]:p},j,o,s,w,k);return S(t.createElement("div",{className:f,style:Object.assign(Object.assign({},O),c)},e,l))}return null!=d?d:null};x.Button=e=>{let{prefixCls:r,className:o,rootClassName:s,active:c,block:d=!1,size:u="default"}=e,{getPrefixCls:g}=t.useContext(l.ConfigContext),m=g("skeleton",r),[b,p,f]=h(m),$=(0,i.default)(e,["prefixCls"]),y=(0,n.default)(m,`${m}-element`,{[`${m}-active`]:c,[`${m}-block`]:d},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(a,Object.assign({prefixCls:`${m}-button`,size:u},$))))},x.Avatar=e=>{let{prefixCls:r,className:o,rootClassName:s,active:c,shape:d="circle",size:u="default"}=e,{getPrefixCls:g}=t.useContext(l.ConfigContext),m=g("skeleton",r),[b,p,f]=h(m),$=(0,i.default)(e,["prefixCls","className"]),y=(0,n.default)(m,`${m}-element`,{[`${m}-active`]:c},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(a,Object.assign({prefixCls:`${m}-avatar`,shape:d,size:u},$))))},x.Input=e=>{let{prefixCls:r,className:o,rootClassName:s,active:c,block:d,size:u="default"}=e,{getPrefixCls:g}=t.useContext(l.ConfigContext),m=g("skeleton",r),[b,p,f]=h(m),$=(0,i.default)(e,["prefixCls"]),y=(0,n.default)(m,`${m}-element`,{[`${m}-active`]:c,[`${m}-block`]:d},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(a,Object.assign({prefixCls:`${m}-input`,size:u},$))))},x.Image=e=>{let{prefixCls:i,className:a,rootClassName:r,style:o,active:s}=e,{getPrefixCls:c}=t.useContext(l.ConfigContext),d=c("skeleton",i),[u,g,m]=h(d),b=(0,n.default)(d,`${d}-element`,{[`${d}-active`]:s},a,r,g,m);return u(t.createElement("div",{className:b},t.createElement("div",{className:(0,n.default)(`${d}-image`,a),style:o},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${d}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${d}-image-path`})))))},x.Node=e=>{let{prefixCls:i,className:a,rootClassName:r,style:o,active:s,children:c}=e,{getPrefixCls:d}=t.useContext(l.ConfigContext),u=d("skeleton",i),[g,m,b]=h(u),p=(0,n.default)(u,`${u}-element`,{[`${u}-active`]:s},m,a,r,b);return g(t.createElement("div",{className:p},t.createElement("div",{className:(0,n.default)(`${u}-image`,a),style:o},c)))},e.s(["default",0,x],185793)},922611,e=>{"use strict";var t=e.i(271645),n=e.i(175066);function l(){}let i=t.createContext({add:l,remove:l});e.s(["usePanelRef",0,function(e){let l=t.useContext(i),a=t.useRef(null);return(0,n.default)(t=>{if(t){let n=e?t.querySelector(e):t;n&&(l.add(n),a.current=n)}else l.remove(a.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let n=(e,t=0,n=!1,l=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!l)return"-";let i={minimumFractionDigits:t,maximumFractionDigits:t};if(!n)return e.toLocaleString("en-US",i);let a=e<0?"-":"",r=Math.abs(e),o=r,s="";return r>=1e6?(o=r/1e6,s="M"):r>=1e3&&(o=r/1e3,s="K"),`${a}${o.toLocaleString("en-US",i)}${s}`},l=async(e,n="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return i(e,n);try{return await navigator.clipboard.writeText(e),t.default.success(n),!0}catch(t){return console.error("Clipboard API failed: ",t),i(e,n)}},i=(e,n)=>{try{let l=document.createElement("textarea");l.value=e,l.style.position="fixed",l.style.left="-999999px",l.style.top="-999999px",l.setAttribute("readonly",""),document.body.appendChild(l),l.focus(),l.select();let i=document.execCommand("copy");if(document.body.removeChild(l),i)return t.default.success(n),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,l,"formatNumberWithCommas",0,n,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let l=n(e,t,!1,!1);if(0===Number(l.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${l}`},"updateExistingKeys",0,function(e,t){let n=structuredClone(e);for(let[e,l]of Object.entries(t))e in n&&(n[e]=l);return n}])},112179,581070,e=>{"use strict";var t=e.i(843476),n=e.i(487486),l=e.i(115504),i=e.i(746798);function a({content:e,trigger:n}){return(0,t.jsx)(i.TooltipProvider,{delay:300,children:(0,t.jsxs)(i.Tooltip,{children:[(0,t.jsx)(i.TooltipTrigger,{render:n}),(0,t.jsx)(i.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,a],581070);let r={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:i,tooltip:o,dataTestId:s}){let c=(0,t.jsx)(n.Badge,{variant:"outline","data-testid":s,className:(0,l.cn)("whitespace-nowrap font-normal",r[e]),children:i});return o?(0,t.jsx)(a,{content:o,trigger:c}):c}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),n=e.i(843476);let l=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],i=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:a="datetime",fallback:r="-"}){let o,s,c,d=e?new Date(e):null;return!d||Number.isNaN(d.getTime())?(0,n.jsx)("span",{className:"text-muted-foreground",children:r}):(0,n.jsx)(t.CellTooltip,{content:(o=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${l[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`,c=`${i(d.getHours())}:${i(d.getMinutes())}:${i(d.getSeconds())}`,`${s}, ${c} (${o})`),trigger:(0,n.jsx)("span",{className:"whitespace-nowrap",children:"date"===a?`${l[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`:`${l[d.getMonth()]} ${d.getDate()}, ${i(d.getHours())}:${i(d.getMinutes())}:${i(d.getSeconds())}`})})}],200208);var a=e.i(174886),r=e.i(115504),o=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:l="pill",onClick:i,copyable:c=!1,truncate:d=!0,fallback:u="-",tooltip:g,disabled:m=!1,dataTestId:b,className:p}){if(!e)return(0,n.jsx)("span",{className:"text-muted-foreground",children:u});let f=!!i&&!m,h=(0,r.cn)(s[l].base,f&&s[l].clickable,d&&"block max-w-[15ch] truncate",m&&"opacity-50",p),$=f?(0,n.jsx)("button",{type:"button",className:h,"data-testid":b,onClick:()=>i(e),children:e}):(0,n.jsx)("span",{className:h,"data-testid":b,children:e}),y=(0,n.jsx)(t.CellTooltip,{content:g??e,trigger:$});return c?(0,n.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[y,(0,n.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,o.copyToClipboard)(e)},children:(0,n.jsx)(a.Copy,{className:"size-3"})})]}):y}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:l="-",showZero:i=!1}){return null==e||Number.isNaN(e)?(0,n.jsx)("span",{className:"text-muted-foreground",children:l}):0===e?i?(0,n.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,o.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,n.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,n.jsx)("span",{className:"whitespace-nowrap",children:(0,o.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let n=t.forwardRef(function(e,n){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:n},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,n],68155)},360820,e=>{"use strict";var t=e.i(271645);let n=t.forwardRef(function(e,n){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:n},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,n],360820)},871943,e=>{"use strict";var t=e.i(271645);let n=t.forwardRef(function(e,n){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:n},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,n],871943)},278587,e=>{"use strict";var t=e.i(271645);let n=t.forwardRef(function(e,n){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:n},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,n],278587)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/05q6y.kb.q2s..js b/litellm/proxy/_experimental/out/_next/static/chunks/05q6y.kb.q2s..js new file mode 100644 index 00000000000..84eea1fd5cb --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/05q6y.kb.q2s..js @@ -0,0 +1,2 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,285027,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 720a48 48 0 1096 0 48 48 0 10-96 0zm16-304v184c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V416c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8zm475.7 440l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zm-783.5-27.9L512 239.9l339.8 588.2H172.2z"}}]},name:"warning",theme:"outlined"};var i=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(i.default,(0,t.default)({},e,{ref:s,icon:n}))});e.s(["WarningOutlined",0,s],285027)},743151,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.CopyToClipboard=void 0;var n=a(e.r(844343)),i=a(e.r(271645)),s=["text","onCopy","options","children"];function a(e){return e&&e.__esModule?e:{default:e}}function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function c(e){for(var t=1;t{"use strict";var n=e.r(743151).CopyToClipboard;n.CopyToClipboard=n,t.exports=n},663435,152473,e=>{"use strict";var t=e.i(843476),r=e.i(271645),n=e.i(199133),i=e.i(898586),s=e.i(56456);let a={enabled:!0,leading:!1,trailing:!0,wait:0,onExecute:()=>{}};class l{constructor(e,t){this.fn=e,this._canLeadingExecute=!0,this._isPending=!1,this._executionCount=0,this._options={...a,...t}}setOptions(e){return this._options={...this._options,...e},this._options.enabled||(this._isPending=!1),this._options}getOptions(){return this._options}maybeExecute(...e){this._options.leading&&this._canLeadingExecute&&(this.executeFunction(...e),this._canLeadingExecute=!1),(this._options.leading||this._options.trailing)&&(this._isPending=!0),this._timeoutId&&clearTimeout(this._timeoutId),this._timeoutId=setTimeout(()=>{this._canLeadingExecute=!0,this._isPending=!1,this._options.trailing&&this.executeFunction(...e)},this._options.wait)}executeFunction(...e){this._options.enabled&&(this.fn(...e),this._executionCount++,this._options.onExecute(this))}cancel(){this._timeoutId&&(clearTimeout(this._timeoutId),this._canLeadingExecute=!0,this._isPending=!1)}getExecutionCount(){return this._executionCount}getIsPending(){return this._options.enabled&&this._isPending}}function o(e,t){let[n,i]=(0,r.useState)(e),s=function(e,t){let[n]=(0,r.useState)(()=>{var r;return Object.getOwnPropertyNames(Object.getPrototypeOf(r=new l(e,t))).filter(e=>"function"==typeof r[e]).reduce((e,t)=>{let n=r[t];return"function"==typeof n&&(e[t]=n.bind(r)),e},{})});return n.setOptions(t),n}(i,t);return[n,s.maybeExecute,s]}e.s(["useDebouncedState",0,o],152473);var c=e.i(785242);let{Text:u}=i.Typography;e.s(["default",0,({value:e,onChange:i,onTeamSelect:a,disabled:l,organizationId:d,pageSize:m=20})=>{let[f,h]=(0,r.useState)(""),[p,g]=o("",{wait:300}),{data:x,fetchNextPage:y,hasNextPage:v,isFetchingNextPage:b,isLoading:_}=(0,c.useInfiniteTeams)(m,p||void 0,d),j=(0,r.useMemo)(()=>{if(!x?.pages)return[];let e=new Set,t=[];for(let r of x.pages)for(let n of r.teams)e.has(n.team_id)||(e.add(n.team_id),t.push(n));return t},[x]);return(0,t.jsx)(n.Select,{showSearch:!0,placeholder:"Search or select a team",value:e||void 0,onChange:e=>{i?.(e??""),a&&a(e?j.find(t=>t.team_id===e)??null:null)},disabled:l,allowClear:!0,filterOption:!1,onSearch:e=>{h(e),g(e)},searchValue:f,onPopupScroll:e=>{let t=e.currentTarget;(t.scrollTop+t.clientHeight)/t.scrollHeight>=.8&&v&&!b&&y()},loading:_,notFoundContent:_?(0,t.jsx)(s.LoadingOutlined,{spin:!0}):"No teams found","data-testid":"team-dropdown",popupRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,b&&(0,t.jsx)("div",{style:{textAlign:"center",padding:8},children:(0,t.jsx)(s.LoadingOutlined,{spin:!0})})]}),children:j.map(e=>(0,t.jsxs)(n.Select.Option,{value:e.team_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.team_alias})," ",(0,t.jsxs)(u,{type:"secondary",children:["(",e.team_id,")"]})]},e.team_id))})}],663435)},184163,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"download",theme:"outlined"};var i=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(i.default,(0,t.default)({},e,{ref:s,icon:n}))});e.s(["default",0,s],184163)},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),n=e.i(201072),i=e.i(121229),s=e.i(726289),a=e.i(864517),l=e.i(343794),o=e.i(529681),c=e.i(242064),u=e.i(931067),d=e.i(209428),m=e.i(703923),f={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},h=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),n=!1;e.current.forEach(function(e){if(e){n=!0;var i=e.style;i.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(i.transitionDuration="0s, 0s")}}),n&&(r.current=Date.now())}),e.current},p=e.i(410160),g=e.i(392221),x=e.i(654310),y=0,v=(0,x.default)();let b=function(e){var r=t.useState(),n=(0,g.default)(r,2),i=n[0],s=n[1];return t.useEffect(function(){var e;s("rc_progress_".concat((v?(e=y,y+=1):e="TEST_OR_SSR",e)))},[]),e||i};var _=function(e){var r=e.bg,n=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},n)};function j(e,t){return Object.keys(e).map(function(r){var n=parseFloat(r),i="".concat(Math.floor(n*t),"%");return"".concat(e[r]," ").concat(i)})}var w=t.forwardRef(function(e,r){var n=e.prefixCls,i=e.color,s=e.gradientId,a=e.radius,l=e.style,o=e.ptg,c=e.strokeLinecap,u=e.strokeWidth,d=e.size,m=e.gapDegree,f=i&&"object"===(0,p.default)(i),h=d/2,g=t.createElement("circle",{className:"".concat(n,"-circle-path"),r:a,cx:h,cy:h,stroke:f?"#FFF":void 0,strokeLinecap:c,strokeWidth:u,opacity:+(0!==o),style:l,ref:r});if(!f)return g;var x="".concat(s,"-conic"),y=j(i,(360-m)/360),v=j(i,1),b="conic-gradient(from ".concat(m?"".concat(180+m/2,"deg"):"0deg",", ").concat(y.join(", "),")"),w="linear-gradient(to ".concat(m?"bottom":"top",", ").concat(v.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:x},g),t.createElement("foreignObject",{x:0,y:0,width:d,height:d,mask:"url(#".concat(x,")")},t.createElement(_,{bg:w},t.createElement(_,{bg:b}))))}),k=function(e,t,r,n,i,s,a,l,o,c){var u=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,d=(100-n)/100*t;return"round"===o&&100!==n&&(d+=c/2)>=t&&(d=t-.01),{stroke:"string"==typeof l?l:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:d+u,transform:"rotate(".concat(i+r/100*360*((360-s)/360)+(0===s?0:({bottom:0,top:180,left:90,right:-90})[a]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},C=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function S(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let E=function(e){var r,n,i,s,a=(0,d.default)((0,d.default)({},f),e),o=a.id,c=a.prefixCls,g=a.steps,x=a.strokeWidth,y=a.trailWidth,v=a.gapDegree,_=void 0===v?0:v,j=a.gapPosition,E=a.trailColor,O=a.strokeLinecap,N=a.style,I=a.className,T=a.strokeColor,R=a.percent,P=(0,m.default)(a,C),$=b(o),D="".concat($,"-gradient"),F=50-x/2,A=2*Math.PI*F,L=_>0?90+_/2:-90,M=(360-_)/360*A,B="object"===(0,p.default)(g)?g:{count:g,gap:2},z=B.count,U=B.gap,V=S(R),H=S(T),W=H.find(function(e){return e&&"object"===(0,p.default)(e)}),q=W&&"object"===(0,p.default)(W)?"butt":O,K=k(A,M,0,100,L,_,j,E,q,x),X=h();return t.createElement("svg",(0,u.default)({className:(0,l.default)("".concat(c,"-circle"),I),viewBox:"0 0 ".concat(100," ").concat(100),style:N,id:o,role:"presentation"},P),!z&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:F,cx:50,cy:50,stroke:E,strokeLinecap:q,strokeWidth:y||x,style:K}),z?(r=Math.round(z*(V[0]/100)),n=100/z,i=0,Array(z).fill(null).map(function(e,s){var a=s<=r-1?H[0]:E,l=a&&"object"===(0,p.default)(a)?"url(#".concat(D,")"):void 0,o=k(A,M,i,n,L,_,j,a,"butt",x,U);return i+=(M-o.strokeDashoffset+U)*100/M,t.createElement("circle",{key:s,className:"".concat(c,"-circle-path"),r:F,cx:50,cy:50,stroke:l,strokeWidth:x,opacity:1,style:o,ref:function(e){X[s]=e}})})):(s=0,V.map(function(e,r){var n=H[r]||H[H.length-1],i=k(A,M,s,e,L,_,j,n,q,x);return s+=e,t.createElement(w,{key:r,color:n,ptg:e,radius:F,prefixCls:c,gradientId:D,style:i,strokeLinecap:q,strokeWidth:x,gapDegree:_,ref:function(e){X[r]=e},size:100})}).reverse()))};var O=e.i(491816);e.i(765846);var N=e.i(896091);function I(e){return!e||e<0?0:e>100?100:e}function T({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let R=(e,t,r)=>{var n,i,s,a;let l=-1,o=-1;if("step"===t){let t=r.steps,n=r.strokeWidth;"string"==typeof e||void 0===e?(l="small"===e?2:14,o=null!=n?n:8):"number"==typeof e?[l,o]=[e,e]:[l=14,o=8]=Array.isArray(e)?e:[e.width,e.height],l*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?o=t||("small"===e?6:8):"number"==typeof e?[l,o]=[e,e]:[l=-1,o=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[l,o]="small"===e?[60,60]:[120,120]:"number"==typeof e?[l,o]=[e,e]:Array.isArray(e)&&(l=null!=(i=null!=(n=e[0])?n:e[1])?i:120,o=null!=(a=null!=(s=e[0])?s:e[1])?a:120));return[l,o]},P=e=>{let{prefixCls:r,trailColor:n=null,strokeLinecap:i="round",gapPosition:s,gapDegree:a,width:o=120,type:c,children:u,success:d,size:m=o,steps:f}=e,[h,p]=R(m,"circle"),{strokeWidth:g}=e;void 0===g&&(g=Math.max(3/h*100,6));let x=t.useMemo(()=>a||0===a?a:"dashboard"===c?75:void 0,[a,c]),y=(({percent:e,success:t,successPercent:r})=>{let n=I(T({success:t,successPercent:r}));return[n,I(I(e)-n)]})(e),v="[object Object]"===Object.prototype.toString.call(e.strokeColor),b=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||N.presetPrimaryColors.green,t||null]})({success:d,strokeColor:e.strokeColor}),_=(0,l.default)(`${r}-inner`,{[`${r}-circle-gradient`]:v}),j=t.createElement(E,{steps:f,percent:f?y[1]:y,strokeWidth:g,trailWidth:g,strokeColor:f?b[1]:b,strokeLinecap:i,trailColor:n,prefixCls:r,gapDegree:x,gapPosition:s||"dashboard"===c&&"bottom"||void 0}),w=h<=20,k=t.createElement("div",{className:_,style:{width:h,height:p,fontSize:.15*h+6}},j,!w&&u);return w?t.createElement(O.default,{title:u},k):k};e.i(296059);var $=e.i(694758),D=e.i(915654),F=e.i(183293),A=e.i(246422),L=e.i(838378);let M="--progress-line-stroke-color",B="--progress-percent",z=e=>{let t=e?"100%":"-100%";return new $.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},U=(0,A.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,L.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,F.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${M})`]},height:"100%",width:`calc(1 / var(${B}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,D.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:z(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:z(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var V=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(r[n[i]]=e[n[i]]);return r};let H=e=>{let{prefixCls:r,direction:n,percent:i,size:s,strokeWidth:a,strokeColor:o,strokeLinecap:c="round",children:u,trailColor:d=null,percentPosition:m,success:f}=e,{align:h,type:p}=m,g=o&&"string"!=typeof o?((e,t)=>{let{from:r=N.presetPrimaryColors.blue,to:n=N.presetPrimaryColors.blue,direction:i="rtl"===t?"to left":"to right"}=e,s=V(e,["from","to","direction"]);if(0!==Object.keys(s).length){let e,t=(e=[],Object.keys(s).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:s[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${i}, ${t})`;return{background:r,[M]:r}}let a=`linear-gradient(${i}, ${r}, ${n})`;return{background:a,[M]:a}})(o,n):{[M]:o,background:o},x="square"===c||"butt"===c?0:void 0,[y,v]=R(null!=s?s:[-1,a||("small"===s?6:8)],"line",{strokeWidth:a}),b=Object.assign(Object.assign({width:`${I(i)}%`,height:v,borderRadius:x},g),{[B]:I(i)/100}),_=T(e),j={width:`${I(_)}%`,height:v,borderRadius:x,backgroundColor:null==f?void 0:f.strokeColor},w=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:d||void 0,borderRadius:x}},t.createElement("div",{className:(0,l.default)(`${r}-bg`,`${r}-bg-${p}`),style:b},"inner"===p&&u),void 0!==_&&t.createElement("div",{className:`${r}-success-bg`,style:j})),k="outer"===p&&"start"===h,C="outer"===p&&"end"===h;return"outer"===p&&"center"===h?t.createElement("div",{className:`${r}-layout-bottom`},w,u):t.createElement("div",{className:`${r}-outer`,style:{width:y<0?"100%":y}},k&&u,w,C&&u)},W=e=>{let{size:r,steps:n,rounding:i=Math.round,percent:s=0,strokeWidth:a=8,strokeColor:o,trailColor:c=null,prefixCls:u,children:d}=e,m=i(s/100*n),[f,h]=R(null!=r?r:["small"===r?2:14,a],"step",{steps:n,strokeWidth:a}),p=f/n,g=Array.from({length:n});for(let e=0;et.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(r[n[i]]=e[n[i]]);return r};let K=["normal","exception","active","success"],X=t.forwardRef((e,u)=>{let d,{prefixCls:m,className:f,rootClassName:h,steps:p,strokeColor:g,percent:x=0,size:y="default",showInfo:v=!0,type:b="line",status:_,format:j,style:w,percentPosition:k={}}=e,C=q(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:S="end",type:E="outer"}=k,O=Array.isArray(g)?g[0]:g,N="string"==typeof g||Array.isArray(g)?g:void 0,$=t.useMemo(()=>{if(O){let e="string"==typeof O?O:Object.values(O)[0];return new r.FastColor(e).isLight()}return!1},[g]),D=t.useMemo(()=>{var t,r;let n=T(e);return Number.parseInt(void 0!==n?null==(t=null!=n?n:0)?void 0:t.toString():null==(r=null!=x?x:0)?void 0:r.toString(),10)},[x,e.success,e.successPercent]),F=t.useMemo(()=>!K.includes(_)&&D>=100?"success":_||"normal",[_,D]),{getPrefixCls:A,direction:L,progress:M}=t.useContext(c.ConfigContext),B=A("progress",m),[z,V,X]=U(B),Q="line"===b,J=Q&&!p,Y=t.useMemo(()=>{let r;if(!v)return null;let o=T(e),c=j||(e=>`${e}%`),u=Q&&$&&"inner"===E;return"inner"===E||j||"exception"!==F&&"success"!==F?r=c(I(x),I(o)):"exception"===F?r=Q?t.createElement(s.default,null):t.createElement(a.default,null):"success"===F&&(r=Q?t.createElement(n.default,null):t.createElement(i.default,null)),t.createElement("span",{className:(0,l.default)(`${B}-text`,{[`${B}-text-bright`]:u,[`${B}-text-${S}`]:J,[`${B}-text-${E}`]:J}),title:"string"==typeof r?r:void 0},r)},[v,x,D,F,b,B,j]);"line"===b?d=p?t.createElement(W,Object.assign({},e,{strokeColor:N,prefixCls:B,steps:"object"==typeof p?p.count:p}),Y):t.createElement(H,Object.assign({},e,{strokeColor:O,prefixCls:B,direction:L,percentPosition:{align:S,type:E}}),Y):("circle"===b||"dashboard"===b)&&(d=t.createElement(P,Object.assign({},e,{strokeColor:O,prefixCls:B,progressStatus:F}),Y));let G=(0,l.default)(B,`${B}-status-${F}`,{[`${B}-${"dashboard"===b&&"circle"||b}`]:"line"!==b,[`${B}-inline-circle`]:"circle"===b&&R(y,"circle")[0]<=20,[`${B}-line`]:J,[`${B}-line-align-${S}`]:J,[`${B}-line-position-${E}`]:J,[`${B}-steps`]:p,[`${B}-show-info`]:v,[`${B}-${y}`]:"string"==typeof y,[`${B}-rtl`]:"rtl"===L},null==M?void 0:M.className,f,h,V,X);return z(t.createElement("div",Object.assign({ref:u,style:Object.assign(Object.assign({},null==M?void 0:M.style),w),className:G,role:"progressbar","aria-valuenow":D,"aria-valuemin":0,"aria-valuemax":100},(0,o.default)(C,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),d))});e.s(["default",0,X],309821)},519756,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"upload",theme:"outlined"};var i=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(i.default,(0,t.default)({},e,{ref:s,icon:n}))});e.s(["UploadOutlined",0,s],519756)},233538,e=>{"use strict";e.s(["isDisabledReactIssue7711",0,function(e){let t=e.parentElement,r=null;for(;t&&!(t instanceof HTMLFieldSetElement);)t instanceof HTMLLegendElement&&(r=t),t=t.parentElement;let n=(null==t?void 0:t.getAttribute("disabled"))==="";return!(n&&function(e){if(!e)return!1;let t=e.previousElementSibling;for(;null!==t;){if(t instanceof HTMLLegendElement)return!1;t=t.previousElementSibling}return!0}(r))&&n}])},83733,233137,e=>{"use strict";let t,r;var n,i,s=e.i(247167),a=e.i(271645),l=e.i(544508),o=e.i(746725),c=e.i(835696);void 0!==s.default&&"u">typeof globalThis&&"u">typeof Element&&(null==(n=null==s.default?void 0:s.default.env)?void 0:n.NODE_ENV)==="test"&&void 0===(null==(i=null==Element?void 0:Element.prototype)?void 0:i.getAnimations)&&(Element.prototype.getAnimations=function(){return console.warn(["Headless UI has polyfilled `Element.prototype.getAnimations` for your tests.","Please install a proper polyfill e.g. `jsdom-testing-mocks`, to silence these warnings.","","Example usage:","```js","import { mockAnimationsApi } from 'jsdom-testing-mocks'","mockAnimationsApi()","```"].join(` +`)),[]});var u=((t=u||{})[t.None=0]="None",t[t.Closed=1]="Closed",t[t.Enter=2]="Enter",t[t.Leave=4]="Leave",t);e.s(["transitionDataAttributes",0,function(e){let t={};for(let r in e)!0===e[r]&&(t[`data-${r}`]="");return t},"useTransition",0,function(e,t,r,n){let[i,s]=(0,a.useState)(r),{hasFlag:u,addFlag:d,removeFlag:m}=function(e=0){let[t,r]=(0,a.useState)(e),n=(0,a.useCallback)(e=>r(e),[t]),i=(0,a.useCallback)(e=>r(t=>t|e),[t]),s=(0,a.useCallback)(e=>(t&e)===e,[t]);return{flags:t,setFlag:n,addFlag:i,hasFlag:s,removeFlag:(0,a.useCallback)(e=>r(t=>t&~e),[r]),toggleFlag:(0,a.useCallback)(e=>r(t=>t^e),[r])}}(e&&i?3:0),f=(0,a.useRef)(!1),h=(0,a.useRef)(!1),p=(0,o.useDisposables)();return(0,c.useIsoMorphicEffect)(()=>{var i;if(e){if(r&&s(!0),!t){r&&d(3);return}return null==(i=null==n?void 0:n.start)||i.call(n,r),function(e,{prepare:t,run:r,done:n,inFlight:i}){let s=(0,l.disposables)();return function(e,{inFlight:t,prepare:r}){if(null!=t&&t.current)return r();let n=e.style.transition;e.style.transition="none",r(),e.offsetHeight,e.style.transition=n}(e,{prepare:t,inFlight:i}),s.nextFrame(()=>{r(),s.requestAnimationFrame(()=>{s.add(function(e,t){var r,n;let i=(0,l.disposables)();if(!e)return i.dispose;let s=!1;i.add(()=>{s=!0});let a=null!=(n=null==(r=e.getAnimations)?void 0:r.call(e).filter(e=>e instanceof CSSTransition))?n:[];return 0===a.length?t():Promise.allSettled(a.map(e=>e.finished)).then(()=>{s||t()}),i.dispose}(e,n))})}),s.dispose}(t,{inFlight:f,prepare(){h.current?h.current=!1:h.current=f.current,f.current=!0,h.current||(r?(d(3),m(4)):(d(4),m(2)))},run(){h.current?r?(m(3),d(4)):(m(4),d(3)):r?m(1):d(1)},done(){var e;h.current&&"function"==typeof t.getAnimations&&t.getAnimations().length>0||(f.current=!1,m(7),r||s(!1),null==(e=null==n?void 0:n.end)||e.call(n,r))}})}},[e,r,t,p]),e?[i,{closed:u(1),enter:u(2),leave:u(4),transition:u(2)||u(4)}]:[r,{closed:void 0,enter:void 0,leave:void 0,transition:void 0}]}],83733);let d=(0,a.createContext)(null);d.displayName="OpenClosedContext";var m=((r=m||{})[r.Open=1]="Open",r[r.Closed=2]="Closed",r[r.Closing=4]="Closing",r[r.Opening=8]="Opening",r);e.s(["OpenClosedProvider",0,function({value:e,children:t}){return a.default.createElement(d.Provider,{value:e},t)},"ResetOpenClosedProvider",0,function({children:e}){return a.default.createElement(d.Provider,{value:null},e)},"State",0,m,"useOpenClosed",0,function(){return(0,a.useContext)(d)}],233137)},677667,674175,886148,543086,e=>{"use strict";let t,r;var n,i=e.i(290571),s=e.i(783222),a=e.i(433336),l=e.i(271645),o=e.i(394487),c=e.i(914189),u=e.i(144279),d=e.i(294316),m=e.i(83733);let f=(0,l.createContext)(()=>{});function h({value:e,children:t}){return l.default.createElement(f.Provider,{value:e},t)}e.s(["CloseProvider",0,h],674175);var p=e.i(233137),g=e.i(233538),x=e.i(397701),y=e.i(402155),v=e.i(700020);let b=null!=(n=l.default.startTransition)?n:function(e){e()};var _=e.i(998348),j=((t=j||{})[t.Open=0]="Open",t[t.Closed=1]="Closed",t),w=((r=w||{})[r.ToggleDisclosure=0]="ToggleDisclosure",r[r.CloseDisclosure=1]="CloseDisclosure",r[r.SetButtonId=2]="SetButtonId",r[r.SetPanelId=3]="SetPanelId",r[r.SetButtonElement=4]="SetButtonElement",r[r.SetPanelElement=5]="SetPanelElement",r);let k={0:e=>({...e,disclosureState:(0,x.match)(e.disclosureState,{0:1,1:0})}),1:e=>1===e.disclosureState?e:{...e,disclosureState:1},2:(e,t)=>e.buttonId===t.buttonId?e:{...e,buttonId:t.buttonId},3:(e,t)=>e.panelId===t.panelId?e:{...e,panelId:t.panelId},4:(e,t)=>e.buttonElement===t.element?e:{...e,buttonElement:t.element},5:(e,t)=>e.panelElement===t.element?e:{...e,panelElement:t.element}},C=(0,l.createContext)(null);function S(e){let t=(0,l.useContext)(C);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,S),t}return t}C.displayName="DisclosureContext";let E=(0,l.createContext)(null);E.displayName="DisclosureAPIContext";let O=(0,l.createContext)(null);function N(e,t){return(0,x.match)(t.type,k,e,t)}O.displayName="DisclosurePanelContext";let I=l.Fragment,T=v.RenderFeatures.RenderStrategy|v.RenderFeatures.Static,R=Object.assign((0,v.forwardRefWithAs)(function(e,t){let{defaultOpen:r=!1,...n}=e,i=(0,l.useRef)(null),s=(0,d.useSyncRefs)(t,(0,d.optionalRef)(e=>{i.current=e},void 0===e.as||e.as===l.Fragment)),a=(0,l.useReducer)(N,{disclosureState:+!r,buttonElement:null,panelElement:null,buttonId:null,panelId:null}),[{disclosureState:o,buttonId:u},m]=a,f=(0,c.useEvent)(e=>{m({type:1});let t=(0,y.getOwnerDocument)(i);if(!t||!u)return;let r=e?e instanceof HTMLElement?e:e.current instanceof HTMLElement?e.current:t.getElementById(u):t.getElementById(u);null==r||r.focus()}),g=(0,l.useMemo)(()=>({close:f}),[f]),b=(0,l.useMemo)(()=>({open:0===o,close:f}),[o,f]),_=(0,v.useRender)();return l.default.createElement(C.Provider,{value:a},l.default.createElement(E.Provider,{value:g},l.default.createElement(h,{value:f},l.default.createElement(p.OpenClosedProvider,{value:(0,x.match)(o,{0:p.State.Open,1:p.State.Closed})},_({ourProps:{ref:s},theirProps:n,slot:b,defaultTag:I,name:"Disclosure"})))))}),{Button:(0,v.forwardRefWithAs)(function(e,t){let r=(0,l.useId)(),{id:n=`headlessui-disclosure-button-${r}`,disabled:i=!1,autoFocus:m=!1,...f}=e,[h,p]=S("Disclosure.Button"),x=(0,l.useContext)(O),y=null!==x&&x===h.panelId,b=(0,l.useRef)(null),j=(0,d.useSyncRefs)(b,t,(0,c.useEvent)(e=>{if(!y)return p({type:4,element:e})}));(0,l.useEffect)(()=>{if(!y)return p({type:2,buttonId:n}),()=>{p({type:2,buttonId:null})}},[n,p,y]);let w=(0,c.useEvent)(e=>{var t;if(y){if(1===h.disclosureState)return;switch(e.key){case _.Keys.Space:case _.Keys.Enter:e.preventDefault(),e.stopPropagation(),p({type:0}),null==(t=h.buttonElement)||t.focus()}}else switch(e.key){case _.Keys.Space:case _.Keys.Enter:e.preventDefault(),e.stopPropagation(),p({type:0})}}),k=(0,c.useEvent)(e=>{e.key===_.Keys.Space&&e.preventDefault()}),C=(0,c.useEvent)(e=>{var t;(0,g.isDisabledReactIssue7711)(e.currentTarget)||i||(y?(p({type:0}),null==(t=h.buttonElement)||t.focus()):p({type:0}))}),{isFocusVisible:E,focusProps:N}=(0,s.useFocusRing)({autoFocus:m}),{isHovered:I,hoverProps:T}=(0,a.useHover)({isDisabled:i}),{pressed:R,pressProps:P}=(0,o.useActivePress)({disabled:i}),$=(0,l.useMemo)(()=>({open:0===h.disclosureState,hover:I,active:R,disabled:i,focus:E,autofocus:m}),[h,I,R,E,i,m]),D=(0,u.useResolveButtonType)(e,h.buttonElement),F=y?(0,v.mergeProps)({ref:j,type:D,disabled:i||void 0,autoFocus:m,onKeyDown:w,onClick:C},N,T,P):(0,v.mergeProps)({ref:j,id:n,type:D,"aria-expanded":0===h.disclosureState,"aria-controls":h.panelElement?h.panelId:void 0,disabled:i||void 0,autoFocus:m,onKeyDown:w,onKeyUp:k,onClick:C},N,T,P);return(0,v.useRender)()({ourProps:F,theirProps:f,slot:$,defaultTag:"button",name:"Disclosure.Button"})}),Panel:(0,v.forwardRefWithAs)(function(e,t){let r=(0,l.useId)(),{id:n=`headlessui-disclosure-panel-${r}`,transition:i=!1,...s}=e,[a,o]=S("Disclosure.Panel"),{close:u}=function e(t){let r=(0,l.useContext)(E);if(null===r){let r=Error(`<${t} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(r,e),r}return r}("Disclosure.Panel"),[f,h]=(0,l.useState)(null),g=(0,d.useSyncRefs)(t,(0,c.useEvent)(e=>{b(()=>o({type:5,element:e}))}),h);(0,l.useEffect)(()=>(o({type:3,panelId:n}),()=>{o({type:3,panelId:null})}),[n,o]);let x=(0,p.useOpenClosed)(),[y,_]=(0,m.useTransition)(i,f,null!==x?(x&p.State.Open)===p.State.Open:0===a.disclosureState),j=(0,l.useMemo)(()=>({open:0===a.disclosureState,close:u}),[a.disclosureState,u]),w={ref:g,id:n,...(0,m.transitionDataAttributes)(_)},k=(0,v.useRender)();return l.default.createElement(p.ResetOpenClosedProvider,null,l.default.createElement(O.Provider,{value:a.panelId},k({ourProps:w,theirProps:s,slot:j,defaultTag:"div",features:T,visible:y,name:"Disclosure.Panel"})))})});e.s(["Disclosure",0,R],886148);let P=(0,l.createContext)(void 0);var $=e.i(444755);let D=(0,e.i(673706).makeClassName)("Accordion"),F=(0,l.createContext)({isOpen:!1}),A=l.default.forwardRef((e,t)=>{var r;let{defaultOpen:n=!1,children:s,className:a}=e,o=(0,i.__rest)(e,["defaultOpen","children","className"]),c=null!=(r=(0,l.useContext)(P))?r:(0,$.tremorTwMerge)("rounded-tremor-default border");return l.default.createElement(R,Object.assign({as:"div",ref:t,className:(0,$.tremorTwMerge)(D("root"),"overflow-hidden","bg-tremor-background border-tremor-border","dark:bg-dark-tremor-background dark:border-dark-tremor-border",c,a),defaultOpen:n},o),({open:e})=>l.default.createElement(F.Provider,{value:{isOpen:e}},s))});A.displayName="Accordion",e.s(["OpenContext",0,F,"default",0,A],543086),e.s(["Accordion",0,A],677667)},130643,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(886148),i=e.i(444755);let s=(0,e.i(673706).makeClassName)("AccordionBody"),a=r.default.forwardRef((e,a)=>{let{children:l,className:o}=e,c=(0,t.__rest)(e,["children","className"]);return r.default.createElement(n.Disclosure.Panel,Object.assign({ref:a,className:(0,i.tremorTwMerge)(s("root"),"w-full text-tremor-default px-4 pb-3","text-tremor-content","dark:text-dark-tremor-content",o)},c),l)});a.displayName="AccordionBody",e.s(["AccordionBody",0,a],130643)},898667,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(886148);let i=e=>{var n=(0,t.__rest)(e,[]);return r.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},n),r.default.createElement("path",{d:"M11.9999 10.8284L7.0502 15.7782L5.63599 14.364L11.9999 8L18.3639 14.364L16.9497 15.7782L11.9999 10.8284Z"}))};var s=e.i(543086),a=e.i(444755);let l=(0,e.i(673706).makeClassName)("AccordionHeader"),o=r.default.forwardRef((e,o)=>{let{children:c,className:u}=e,d=(0,t.__rest)(e,["children","className"]),{isOpen:m}=(0,r.useContext)(s.OpenContext);return r.default.createElement(n.Disclosure.Button,Object.assign({ref:o,className:(0,a.tremorTwMerge)(l("root"),"w-full flex items-center justify-between px-4 py-3","text-tremor-content-emphasis","dark:text-dark-tremor-content-emphasis",u)},d),r.default.createElement("div",{className:(0,a.tremorTwMerge)(l("children"),"flex flex-1 text-inherit mr-4")},c),r.default.createElement("div",null,r.default.createElement(i,{className:(0,a.tremorTwMerge)(l("arrowIcon"),"h-5 w-5 -mr-1","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle",m?"transition-all":"transition-all -rotate-180")})))});o.displayName="AccordionHeader",e.s(["AccordionHeader",0,o],898667)},220508,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["CheckCircleIcon",0,r],220508)},503269,214520,601893,694421,140721,942803,35889,722678,e=>{"use strict";var t=e.i(271645),r=e.i(914189);e.s(["useControllable",0,function(e,n,i){let[s,a]=(0,t.useState)(i),l=void 0!==e,o=(0,t.useRef)(l),c=(0,t.useRef)(!1),u=(0,t.useRef)(!1);return!l||o.current||c.current?l||!o.current||u.current||(u.current=!0,o.current=l,console.error("A component is changing from controlled to uncontrolled. This may be caused by the value changing from a defined value to undefined, which should not happen.")):(c.current=!0,o.current=l,console.error("A component is changing from uncontrolled to controlled. This may be caused by the value changing from undefined to a defined value, which should not happen.")),[l?e:s,(0,r.useEvent)(e=>(l||a(e),null==n?void 0:n(e)))]}],503269),e.s(["useDefaultValue",0,function(e){let[r]=(0,t.useState)(e);return r}],214520);let n=(0,t.createContext)(void 0);function i(){return(0,t.useContext)(n)}e.s(["useDisabled",0,i],601893);var s=e.i(174080),a=e.i(746725);function l(e={},t=null,r=[]){for(let[n,i]of Object.entries(e))!function e(t,r,n){if(Array.isArray(n))for(let[i,s]of n.entries())e(t,o(r,i.toString()),s);else n instanceof Date?t.push([r,n.toISOString()]):"boolean"==typeof n?t.push([r,n?"1":"0"]):"string"==typeof n?t.push([r,n]):"number"==typeof n?t.push([r,`${n}`]):null==n?t.push([r,""]):l(n,r,t)}(r,o(t,n),i);return r}function o(e,t){return e?e+"["+t+"]":t}e.s(["attemptSubmit",0,function(e){var t,r;let n=null!=(t=null==e?void 0:e.form)?t:e.closest("form");if(n){for(let t of n.elements)if(t!==e&&("INPUT"===t.tagName&&"submit"===t.type||"BUTTON"===t.tagName&&"submit"===t.type||"INPUT"===t.nodeName&&"image"===t.type))return void t.click();null==(r=n.requestSubmit)||r.call(n)}},"objectToFormEntries",0,l],694421);var c=e.i(700020),u=e.i(2788);let d=(0,t.createContext)(null);function m({children:e}){let r=(0,t.useContext)(d);if(!r)return t.default.createElement(t.default.Fragment,null,e);let{target:n}=r;return n?(0,s.createPortal)(t.default.createElement(t.default.Fragment,null,e),n):null}function f({setForm:e,formId:r}){return(0,t.useEffect)(()=>{if(r){let t=document.getElementById(r);t&&e(t)}},[e,r]),r?null:t.default.createElement(u.Hidden,{features:u.HiddenFeatures.Hidden,as:"input",type:"hidden",hidden:!0,readOnly:!0,ref:t=>{if(!t)return;let r=t.closest("form");r&&e(r)}})}e.s(["FormFields",0,function({data:e,form:r,disabled:n,onReset:i,overrides:s}){let[o,d]=(0,t.useState)(null),h=(0,a.useDisposables)();return(0,t.useEffect)(()=>{if(i&&o)return h.addEventListener(o,"reset",i)},[o,r,i]),t.default.createElement(m,null,t.default.createElement(f,{setForm:d,formId:r}),l(e).map(([e,i])=>t.default.createElement(u.Hidden,{features:u.HiddenFeatures.Hidden,...(0,c.compact)({key:e,as:"input",type:"hidden",hidden:!0,readOnly:!0,form:r,disabled:n,name:e,value:i,...s})})))}],140721);let h=(0,t.createContext)(void 0);function p(){return(0,t.useContext)(h)}e.s(["useProvidedId",0,p],942803);var g=e.i(835696),x=e.i(294316);let y=(0,t.createContext)(null);y.displayName="DescriptionContext";let v=Object.assign((0,c.forwardRefWithAs)(function(e,r){let n=(0,t.useId)(),s=i(),{id:a=`headlessui-description-${n}`,...l}=e,o=function e(){let r=(0,t.useContext)(y);if(null===r){let t=Error("You used a component, but it is not inside a relevant parent.");throw Error.captureStackTrace&&Error.captureStackTrace(t,e),t}return r}(),u=(0,x.useSyncRefs)(r);(0,g.useIsoMorphicEffect)(()=>o.register(a),[a,o.register]);let d=s||!1,m=(0,t.useMemo)(()=>({...o.slot,disabled:d}),[o.slot,d]),f={ref:u,...o.props,id:a};return(0,c.useRender)()({ourProps:f,theirProps:l,slot:m,defaultTag:"p",name:o.name||"Description"})}),{});e.s(["Description",0,v,"useDescribedBy",0,function(){var e,r;return null!=(r=null==(e=(0,t.useContext)(y))?void 0:e.value)?r:void 0},"useDescriptions",0,function(){let[e,n]=(0,t.useState)([]);return[e.length>0?e.join(" "):void 0,(0,t.useMemo)(()=>function(e){let i=(0,r.useEvent)(e=>(n(t=>[...t,e]),()=>n(t=>{let r=t.slice(),n=r.indexOf(e);return -1!==n&&r.splice(n,1),r}))),s=(0,t.useMemo)(()=>({register:i,slot:e.slot,name:e.name,props:e.props,value:e.value}),[i,e.slot,e.name,e.props,e.value]);return t.default.createElement(y.Provider,{value:s},e.children)},[n])]}],35889);let b=(0,t.createContext)(null);function _(e){var r,n,i;let s=null!=(n=null==(r=(0,t.useContext)(b))?void 0:r.value)?n:void 0;return(null!=(i=null==e?void 0:e.length)?i:0)>0?[s,...e].filter(Boolean).join(" "):s}b.displayName="LabelContext";let j=Object.assign((0,c.forwardRefWithAs)(function(e,n){var s;let a=(0,t.useId)(),l=function e(){let r=(0,t.useContext)(b);if(null===r){let t=Error("You used a ` tag."),"__NEXT_ERROR_CODE",{value:"E863",enumerable:!1,configurable:!0});a=l.default.Children.only(o)}let K=P?a&&"object"==typeof a&&a.ref:A,W=l.default.useCallback(e=>(null!==M&&(w.current=(0,g.mountLinkInstance)(e,U,M,z,F,E)),()=>{w.current&&((0,g.unmountLinkForCurrentNavigation)(w.current),w.current=null),(0,g.unmountPrefetchableInstance)(e)}),[F,U,M,z,E]),H={ref:(0,c.useMergedRef)(W,K),onClick(t){P||"function"!=typeof N||N(t),P&&a.props&&"function"==typeof a.props.onClick&&a.props.onClick(t),!M||t.defaultPrevented||function(t,r,n,o,a,i,s){if("u">typeof window){let u,{nodeName:c}=t.currentTarget;if("A"===c.toUpperCase()&&((u=t.currentTarget.getAttribute("target"))&&"_self"!==u||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.nativeEvent&&2===t.nativeEvent.which)||t.currentTarget.hasAttribute("download"))return;if(!(0,p.isLocalURL)(r)){o&&(t.preventDefault(),location.replace(r));return}if(t.preventDefault(),i){let e=!1;if(i({preventDefault:()=>{e=!0}}),e)return}let{dispatchNavigateAction:d}=e.r(699781);l.default.startTransition(()=>{d(r,o?"replace":"push",!1===a?m.ScrollBehavior.NoScroll:m.ScrollBehavior.Default,n.current,s)})}}(t,U,w,x,T,_,I)},onMouseEnter(e){P||"function"!=typeof L||L(e),P&&a.props&&"function"==typeof a.props.onMouseEnter&&a.props.onMouseEnter(e),M&&F&&(0,g.onNavigationIntent)(e.currentTarget,!0===B)},onTouchStart:function(e){P||"function"!=typeof R||R(e),P&&a.props&&"function"==typeof a.props.onTouchStart&&a.props.onTouchStart(e),M&&F&&(0,g.onNavigationIntent)(e.currentTarget,!0===B)}};return(0,d.isAbsoluteUrl)(U)?H.href=U:P&&!j&&("a"!==a.type||"href"in a.props)||(H.href=(0,f.addBasePath)(U)),h=P?l.default.cloneElement(a,H):(0,i.jsx)("a",{...D,...H,children:o}),(0,i.jsx)(y.Provider,{value:v,children:h})}e.r(284508);let y=(0,l.createContext)(g.IDLE_LINK_STATUS),v=()=>(0,l.useContext)(y);("function"==typeof r.default||"object"==typeof r.default&&null!==r.default)&&void 0===r.default.__esModule&&(Object.defineProperty(r.default,"__esModule",{value:!0}),Object.assign(r.default,r),t.exports=r.default)},906579,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(361275),o=e.i(702779),a=e.i(763731),i=e.i(242064);e.i(296059);var l=e.i(915654),s=e.i(694758),u=e.i(183293),c=e.i(403541),d=e.i(246422),f=e.i(838378);let m=new s.Keyframes("antStatusProcessing",{"0%":{transform:"scale(0.8)",opacity:.5},"100%":{transform:"scale(2.4)",opacity:0}}),g=new s.Keyframes("antZoomBadgeIn",{"0%":{transform:"scale(0) translate(50%, -50%)",opacity:0},"100%":{transform:"scale(1) translate(50%, -50%)"}}),p=new s.Keyframes("antZoomBadgeOut",{"0%":{transform:"scale(1) translate(50%, -50%)"},"100%":{transform:"scale(0) translate(50%, -50%)",opacity:0}}),b=new s.Keyframes("antNoWrapperZoomBadgeIn",{"0%":{transform:"scale(0)",opacity:0},"100%":{transform:"scale(1)"}}),h=new s.Keyframes("antNoWrapperZoomBadgeOut",{"0%":{transform:"scale(1)"},"100%":{transform:"scale(0)",opacity:0}}),y=new s.Keyframes("antBadgeLoadingCircle",{"0%":{transformOrigin:"50%"},"100%":{transform:"translate(50%, -50%) rotate(360deg)",transformOrigin:"50%"}}),v=e=>{let{fontHeight:t,lineWidth:r,marginXS:n,colorBorderBg:o}=e,a=e.colorTextLightSolid,i=e.colorError,l=e.colorErrorHover;return(0,f.mergeToken)(e,{badgeFontHeight:t,badgeShadowSize:r,badgeTextColor:a,badgeColor:i,badgeColorHover:l,badgeShadowColor:o,badgeProcessingDuration:"1.2s",badgeRibbonOffset:n,badgeRibbonCornerTransform:"scaleY(0.75)",badgeRibbonCornerFilter:"brightness(75%)"})},E=e=>{let{fontSize:t,lineHeight:r,fontSizeSM:n,lineWidth:o}=e;return{indicatorZIndex:"auto",indicatorHeight:Math.round(t*r)-2*o,indicatorHeightSM:t,dotSize:n/2,textFontSize:n,textFontSizeSM:n,textFontWeight:"normal",statusSize:n/2}},w=(0,d.genStyleHooks)("Badge",e=>(e=>{let{componentCls:t,iconCls:r,antCls:n,badgeShadowSize:o,textFontSize:a,textFontSizeSM:i,statusSize:s,dotSize:d,textFontWeight:f,indicatorHeight:v,indicatorHeightSM:E,marginXS:w,calc:S}=e,O=`${n}-scroll-number`,$=(0,c.genPresetColor)(e,(e,{darkColor:r})=>({[`&${t} ${t}-color-${e}`]:{background:r,[`&:not(${t}-count)`]:{color:r},"a:hover &":{background:r}}}));return{[t]:Object.assign(Object.assign(Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",display:"inline-block",width:"fit-content",lineHeight:1,[`${t}-count`]:{display:"inline-flex",justifyContent:"center",zIndex:e.indicatorZIndex,minWidth:v,height:v,color:e.badgeTextColor,fontWeight:f,fontSize:a,lineHeight:(0,l.unit)(v),whiteSpace:"nowrap",textAlign:"center",background:e.badgeColor,borderRadius:S(v).div(2).equal(),boxShadow:`0 0 0 ${(0,l.unit)(o)} ${e.badgeShadowColor}`,transition:`background ${e.motionDurationMid}`,a:{color:e.badgeTextColor},"a:hover":{color:e.badgeTextColor},"a:hover &":{background:e.badgeColorHover}},[`${t}-count-sm`]:{minWidth:E,height:E,fontSize:i,lineHeight:(0,l.unit)(E),borderRadius:S(E).div(2).equal()},[`${t}-multiple-words`]:{padding:`0 ${(0,l.unit)(e.paddingXS)}`,bdi:{unicodeBidi:"plaintext"}},[`${t}-dot`]:{zIndex:e.indicatorZIndex,width:d,minWidth:d,height:d,background:e.badgeColor,borderRadius:"100%",boxShadow:`0 0 0 ${(0,l.unit)(o)} ${e.badgeShadowColor}`},[`${t}-count, ${t}-dot, ${O}-custom-component`]:{position:"absolute",top:0,insetInlineEnd:0,transform:"translate(50%, -50%)",transformOrigin:"100% 0%",[`&${r}-spin`]:{animationName:y,animationDuration:"1s",animationIterationCount:"infinite",animationTimingFunction:"linear"}},[`&${t}-status`]:{lineHeight:"inherit",verticalAlign:"baseline",[`${t}-status-dot`]:{position:"relative",top:-1,display:"inline-block",width:s,height:s,verticalAlign:"middle",borderRadius:"50%"},[`${t}-status-success`]:{backgroundColor:e.colorSuccess},[`${t}-status-processing`]:{overflow:"visible",color:e.colorInfo,backgroundColor:e.colorInfo,borderColor:"currentcolor","&::after":{position:"absolute",top:0,insetInlineStart:0,width:"100%",height:"100%",borderWidth:o,borderStyle:"solid",borderColor:"inherit",borderRadius:"50%",animationName:m,animationDuration:e.badgeProcessingDuration,animationIterationCount:"infinite",animationTimingFunction:"ease-in-out",content:'""'}},[`${t}-status-default`]:{backgroundColor:e.colorTextPlaceholder},[`${t}-status-error`]:{backgroundColor:e.colorError},[`${t}-status-warning`]:{backgroundColor:e.colorWarning},[`${t}-status-text`]:{marginInlineStart:w,color:e.colorText,fontSize:e.fontSize}}}),$),{[`${t}-zoom-appear, ${t}-zoom-enter`]:{animationName:g,animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseOutBack,animationFillMode:"both"},[`${t}-zoom-leave`]:{animationName:p,animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseOutBack,animationFillMode:"both"},[`&${t}-not-a-wrapper`]:{[`${t}-zoom-appear, ${t}-zoom-enter`]:{animationName:b,animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseOutBack},[`${t}-zoom-leave`]:{animationName:h,animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseOutBack},[`&:not(${t}-status)`]:{verticalAlign:"middle"},[`${O}-custom-component, ${t}-count`]:{transform:"none"},[`${O}-custom-component, ${O}`]:{position:"relative",top:"auto",display:"block",transformOrigin:"50% 50%"}},[O]:{overflow:"hidden",transition:`all ${e.motionDurationMid} ${e.motionEaseOutBack}`,[`${O}-only`]:{position:"relative",display:"inline-block",height:v,transition:`all ${e.motionDurationSlow} ${e.motionEaseOutBack}`,WebkitTransformStyle:"preserve-3d",WebkitBackfaceVisibility:"hidden",[`> p${O}-only-unit`]:{height:v,margin:0,WebkitTransformStyle:"preserve-3d",WebkitBackfaceVisibility:"hidden"}},[`${O}-symbol`]:{verticalAlign:"top"}},"&-rtl":{direction:"rtl",[`${t}-count, ${t}-dot, ${O}-custom-component`]:{transform:"translate(-50%, -50%)"}}})}})(v(e)),E),S=(0,d.genStyleHooks)(["Badge","Ribbon"],e=>(e=>{let{antCls:t,badgeFontHeight:r,marginXS:n,badgeRibbonOffset:o,calc:a}=e,i=`${t}-ribbon`,s=`${t}-ribbon-wrapper`,d=(0,c.genPresetColor)(e,(e,{darkColor:t})=>({[`&${i}-color-${e}`]:{background:t,color:t}}));return{[s]:{position:"relative"},[i]:Object.assign(Object.assign(Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"absolute",top:n,padding:`0 ${(0,l.unit)(e.paddingXS)}`,color:e.colorPrimary,lineHeight:(0,l.unit)(r),whiteSpace:"nowrap",backgroundColor:e.colorPrimary,borderRadius:e.borderRadiusSM,[`${i}-text`]:{color:e.badgeTextColor},[`${i}-corner`]:{position:"absolute",top:"100%",width:o,height:o,color:"currentcolor",border:`${(0,l.unit)(a(o).div(2).equal())} solid`,transform:e.badgeRibbonCornerTransform,transformOrigin:"top",filter:e.badgeRibbonCornerFilter}}),d),{[`&${i}-placement-end`]:{insetInlineEnd:a(o).mul(-1).equal(),borderEndEndRadius:0,[`${i}-corner`]:{insetInlineEnd:0,borderInlineEndColor:"transparent",borderBlockEndColor:"transparent"}},[`&${i}-placement-start`]:{insetInlineStart:a(o).mul(-1).equal(),borderEndStartRadius:0,[`${i}-corner`]:{insetInlineStart:0,borderBlockEndColor:"transparent",borderInlineStartColor:"transparent"}},"&-rtl":{direction:"rtl"}})}})(v(e)),E),O=e=>{let n,{prefixCls:o,value:a,current:i,offset:l=0}=e;return l&&(n={position:"absolute",top:`${l}00%`,left:0}),t.createElement("span",{style:n,className:(0,r.default)(`${o}-only-unit`,{current:i})},a)},$=e=>{let r,n,{prefixCls:o,count:a,value:i}=e,l=Number(i),s=Math.abs(a),[u,c]=t.useState(l),[d,f]=t.useState(s),m=()=>{c(l),f(s)};if(t.useEffect(()=>{let e=setTimeout(m,1e3);return()=>clearTimeout(e)},[l]),u===l||Number.isNaN(l)||Number.isNaN(u))r=[t.createElement(O,Object.assign({},e,{key:l,current:!0}))],n={transition:"none"};else{r=[];let o=l+10,a=[];for(let e=l;e<=o;e+=1)a.push(e);let i=de%10===u);r=(i<0?a.slice(0,c+1):a.slice(c)).map((r,n)=>t.createElement(O,Object.assign({},e,{key:r,value:r%10,offset:i<0?n-c:n,current:n===c}))),n={transform:`translateY(${-function(e,t,r){let n=e,o=0;for(;(n+10)%10!==t;)n+=r,o+=r;return o}(u,l,i)}00%)`}}return t.createElement("span",{className:`${o}-only`,style:n,onTransitionEnd:m},r)};var C=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let j=t.forwardRef((e,n)=>{let{prefixCls:o,count:l,className:s,motionClassName:u,style:c,title:d,show:f,component:m="sup",children:g}=e,p=C(e,["prefixCls","count","className","motionClassName","style","title","show","component","children"]),{getPrefixCls:b}=t.useContext(i.ConfigContext),h=b("scroll-number",o),y=Object.assign(Object.assign({},p),{"data-show":f,style:c,className:(0,r.default)(h,s,u),title:d}),v=l;if(l&&Number(l)%1==0){let e=String(l).split("");v=t.createElement("bdi",null,e.map((r,n)=>t.createElement($,{prefixCls:h,count:Number(l),value:r,key:e.length-n})))}return((null==c?void 0:c.borderColor)&&(y.style=Object.assign(Object.assign({},c),{boxShadow:`0 0 0 1px ${c.borderColor} inset`})),g)?(0,a.cloneElement)(g,e=>({className:(0,r.default)(`${h}-custom-component`,null==e?void 0:e.className,u)})):t.createElement(m,Object.assign({},y,{ref:n}),v)});var x=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let k=t.forwardRef((e,l)=>{var s,u,c,d,f;let{prefixCls:m,scrollNumberPrefixCls:g,children:p,status:b,text:h,color:y,count:v=null,overflowCount:E=99,dot:S=!1,size:O="default",title:$,offset:C,style:k,className:T,rootClassName:N,classNames:L,styles:R,showZero:P=!1}=e,_=x(e,["prefixCls","scrollNumberPrefixCls","children","status","text","color","count","overflowCount","dot","size","title","offset","style","className","rootClassName","classNames","styles","showZero"]),{getPrefixCls:I,direction:A,badge:B}=t.useContext(i.ConfigContext),D=I("badge",m),[M,F,z]=w(D),U=v>E?`${E}+`:v,K="0"===U||0===U||"0"===h||0===h,W=null===v||K&&!P,H=(null!=b||null!=y)&&W,G=null!=b||!K,q=S&&!K,V=q?"":U,Z=(0,t.useMemo)(()=>((null==V||""===V)&&(null==h||""===h)||K&&!P)&&!q,[V,K,P,q,h]),X=(0,t.useRef)(v);Z||(X.current=v);let Q=X.current,Y=(0,t.useRef)(V);Z||(Y.current=V);let J=Y.current,ee=(0,t.useRef)(q);Z||(ee.current=q);let et=(0,t.useMemo)(()=>{if(!C)return Object.assign(Object.assign({},null==B?void 0:B.style),k);let e={marginTop:C[1]};return"rtl"===A?e.left=Number.parseInt(C[0],10):e.right=-Number.parseInt(C[0],10),Object.assign(Object.assign(Object.assign({},e),null==B?void 0:B.style),k)},[A,C,k,null==B?void 0:B.style]),er=null!=$?$:"string"==typeof Q||"number"==typeof Q?Q:void 0,en=!Z&&(0===h?P:!!h&&!0!==h),eo=en?t.createElement("span",{className:`${D}-status-text`},h):null,ea=Q&&"object"==typeof Q?(0,a.cloneElement)(Q,e=>({style:Object.assign(Object.assign({},et),e.style)})):void 0,ei=(0,o.isPresetColor)(y,!1),el=(0,r.default)(null==L?void 0:L.indicator,null==(s=null==B?void 0:B.classNames)?void 0:s.indicator,{[`${D}-status-dot`]:H,[`${D}-status-${b}`]:!!b,[`${D}-color-${y}`]:ei}),es={};y&&!ei&&(es.color=y,es.background=y);let eu=(0,r.default)(D,{[`${D}-status`]:H,[`${D}-not-a-wrapper`]:!p,[`${D}-rtl`]:"rtl"===A},T,N,null==B?void 0:B.className,null==(u=null==B?void 0:B.classNames)?void 0:u.root,null==L?void 0:L.root,F,z);if(!p&&H&&(h||G||!W)){let e=et.color;return M(t.createElement("span",Object.assign({},_,{className:eu,style:Object.assign(Object.assign(Object.assign({},null==R?void 0:R.root),null==(c=null==B?void 0:B.styles)?void 0:c.root),et)}),t.createElement("span",{className:el,style:Object.assign(Object.assign(Object.assign({},null==R?void 0:R.indicator),null==(d=null==B?void 0:B.styles)?void 0:d.indicator),es)}),en&&t.createElement("span",{style:{color:e},className:`${D}-status-text`},h)))}return M(t.createElement("span",Object.assign({ref:l},_,{className:eu,style:Object.assign(Object.assign({},null==(f=null==B?void 0:B.styles)?void 0:f.root),null==R?void 0:R.root)}),p,t.createElement(n.default,{visible:!Z,motionName:`${D}-zoom`,motionAppear:!1,motionDeadline:1e3},({className:e})=>{var n,o;let a=I("scroll-number",g),i=ee.current,l=(0,r.default)(null==L?void 0:L.indicator,null==(n=null==B?void 0:B.classNames)?void 0:n.indicator,{[`${D}-dot`]:i,[`${D}-count`]:!i,[`${D}-count-sm`]:"small"===O,[`${D}-multiple-words`]:!i&&J&&J.toString().length>1,[`${D}-status-${b}`]:!!b,[`${D}-color-${y}`]:ei}),s=Object.assign(Object.assign(Object.assign({},null==R?void 0:R.indicator),null==(o=null==B?void 0:B.styles)?void 0:o.indicator),et);return y&&!ei&&((s=s||{}).background=y),t.createElement(j,{prefixCls:a,show:!Z,motionClassName:e,className:l,count:J,title:er,style:s,key:"scrollNumber"},ea)}),eo))});k.Ribbon=e=>{let{className:n,prefixCls:a,style:l,color:s,children:u,text:c,placement:d="end",rootClassName:f}=e,{getPrefixCls:m,direction:g}=t.useContext(i.ConfigContext),p=m("ribbon",a),b=`${p}-wrapper`,[h,y,v]=S(p,b),E=(0,o.isPresetColor)(s,!1),w=(0,r.default)(p,`${p}-placement-${d}`,{[`${p}-rtl`]:"rtl"===g,[`${p}-color-${s}`]:E},n),O={},$={};return s&&!E&&(O.background=s,$.color=s),h(t.createElement("div",{className:(0,r.default)(b,f,y,v)},u,t.createElement("div",{className:(0,r.default)(w,y),style:Object.assign(Object.assign({},O),l)},t.createElement("span",{className:`${p}-text`},c),t.createElement("div",{className:`${p}-corner`,style:$}))))},e.s(["Badge",0,k],906579)},115571,e=>{"use strict";let t="local-storage-change";e.s(["LOCAL_STORAGE_EVENT",0,t,"emitLocalStorageChange",0,function(e){window.dispatchEvent(new CustomEvent(t,{detail:{key:e}}))},"getLocalStorageItem",0,function(e){try{return window.localStorage.getItem(e)}catch(t){return console.warn(`Error reading localStorage key "${e}":`,t),null}},"removeLocalStorageItem",0,function(e){try{window.localStorage.removeItem(e)}catch(t){console.warn(`Error removing localStorage key "${e}":`,t)}},"setLocalStorageItem",0,function(e,t){try{window.localStorage.setItem(e,t)}catch(t){console.warn(`Error setting localStorage key "${e}":`,t)}}])},275144,e=>{"use strict";var t=e.i(843476),r=e.i(271645),n=e.i(602869);let o=(0,r.createContext)(void 0);e.s(["ThemeProvider",0,({children:e,accessToken:a})=>{let[i,l]=(0,r.useState)(null),[s,u]=(0,r.useState)(null);return(0,r.useEffect)(()=>{(async()=>{try{let e=(0,n.getProxyBaseUrl)(),t=e?`${e}/get/ui_theme_settings`:"/get/ui_theme_settings",r=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json"}});if(r.ok){let e=await r.json();e.values?.logo_url&&l(e.values.logo_url),e.values?.favicon_url&&u(e.values.favicon_url)}}catch(e){console.warn("Failed to load theme settings from backend:",e)}})()},[]),(0,r.useEffect)(()=>{if(s){let e=document.querySelectorAll("link[rel*='icon']");if(e.length>0)e.forEach(e=>{e.href=s});else{let e=document.createElement("link");e.rel="icon",e.href=s,document.head.appendChild(e)}}},[s]),(0,t.jsx)(o.Provider,{value:{logoUrl:i,setLogoUrl:l,faviconUrl:s,setFaviconUrl:u},children:e})},"useTheme",0,()=>{let e=(0,r.useContext)(o);if(!e)throw Error("useTheme must be used within a ThemeProvider");return e}])},143488,e=>{"use strict";var t=e.i(266027),r=e.i(602869);let n=(0,e.i(243652).createQueryKeys)("healthReadinessDetails"),o=async e=>{let t=(0,r.getProxyBaseUrl)(),n=await fetch(`${t}/health/readiness/details`,{method:"GET",headers:{[(0,r.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!n.ok)throw Error(`Failed to fetch health readiness details: ${n.statusText}`);return n.json()};e.s(["useHealthReadinessDetails",0,e=>(0,t.useQuery)({queryKey:n.detail("readiness"),queryFn:()=>o(e),enabled:!!e,staleTime:3e5,retry:!1})])},912089,636772,e=>{"use strict";var t=e.i(115571),r=e.i(271645);function n(e){let r=t=>{"disableBouncingIcon"===t.key&&e()},n=t=>{let{key:r}=t.detail;"disableBouncingIcon"===r&&e()};return window.addEventListener("storage",r),window.addEventListener(t.LOCAL_STORAGE_EVENT,n),()=>{window.removeEventListener("storage",r),window.removeEventListener(t.LOCAL_STORAGE_EVENT,n)}}function o(){return"true"===(0,t.getLocalStorageItem)("disableBouncingIcon")}function a(e){let r=t=>{"disableShowPrompts"===t.key&&e()},n=t=>{let{key:r}=t.detail;"disableShowPrompts"===r&&e()};return window.addEventListener("storage",r),window.addEventListener(t.LOCAL_STORAGE_EVENT,n),()=>{window.removeEventListener("storage",r),window.removeEventListener(t.LOCAL_STORAGE_EVENT,n)}}function i(){return"true"===(0,t.getLocalStorageItem)("disableShowPrompts")}e.s(["useDisableBouncingIcon",0,function(){return(0,r.useSyncExternalStore)(n,o)}],912089),e.s(["useDisableShowPrompts",0,function(){return(0,r.useSyncExternalStore)(a,i)}],636772)},731565,e=>{"use strict";var t=e.i(115571),r=e.i(271645);function n(e){let r=t=>{"disableBlogPosts"===t.key&&e()},n=t=>{let{key:r}=t.detail;"disableBlogPosts"===r&&e()};return window.addEventListener("storage",r),window.addEventListener(t.LOCAL_STORAGE_EVENT,n),()=>{window.removeEventListener("storage",r),window.removeEventListener(t.LOCAL_STORAGE_EVENT,n)}}function o(){return"true"===(0,t.getLocalStorageItem)("disableBlogPosts")}e.s(["useDisableBlogPosts",0,function(){return(0,r.useSyncExternalStore)(n,o)}])},371401,222038,799676,e=>{"use strict";var t=e.i(115571),r=e.i(271645);function n(e){let r=t=>{"disableUsageIndicator"===t.key&&e()},n=t=>{let{key:r}=t.detail;"disableUsageIndicator"===r&&e()};return window.addEventListener("storage",r),window.addEventListener(t.LOCAL_STORAGE_EVENT,n),()=>{window.removeEventListener("storage",r),window.removeEventListener(t.LOCAL_STORAGE_EVENT,n)}}function o(){return"true"===(0,t.getLocalStorageItem)("disableUsageIndicator")}e.s(["useDisableUsageIndicator",0,function(){return(0,r.useSyncExternalStore)(n,o)}],371401),e.s(["navAccountDisplayName",0,function(e,t){let r=e?.trim();if(r)return r;let n=t?.trim();return!n||/^default[_\s-]?user[_\s-]?id$/i.test(n)?"Account":n}],222038);var a=e.i(843476);e.s([],704824),e.i(704824),e.i(247167);var i=e.i(552245),l=e.i(733332);let s=r.createContext(void 0);function u(){let e=r.useContext(s);if(void 0===e)throw Error((0,l.default)(13));return e}let c={imageLoadingStatus:()=>null},d=r.forwardRef(function(e,t){let{className:n,render:o,style:l,...u}=e,[d,f]=r.useState("idle"),m=r.useMemo(()=>({imageLoadingStatus:d,setImageLoadingStatus:f}),[d,f]),g=(0,i.useRenderElement)("span",e,{state:{imageLoadingStatus:d},ref:t,props:u,stateAttributesMapping:c});return(0,a.jsx)(s.Provider,{value:m,children:g})});var f=e.i(667865),m=e.i(146376),g=e.i(137584),p=e.i(209407),b=e.i(223910),h=e.i(956789);let y={...c,...p.transitionStatusMapping},v=r.forwardRef(function(e,t){let{className:n,render:o,onLoadingStatusChange:a,style:l,...s}=e,{setImageLoadingStatus:c}=u(),d=function(e,{referrerPolicy:t,crossOrigin:n,sizes:o,srcSet:a}){let[i,l]=r.useState("idle");return(0,m.useIsoLayoutEffect)(()=>{if(!e&&!a)return l("error"),h.NOOP;let r=!0,i=new window.Image,s=e=>()=>{r&&l(e)};return l("loading"),i.onload=s("loaded"),i.onerror=s("error"),t&&(i.referrerPolicy=t),i.crossOrigin=n??null,o&&(i.sizes=o),a&&(i.srcset=a),e&&(i.src=e),i.complete&&l(i.naturalWidth>0?"loaded":"error"),()=>{r=!1}},[e,a,o,n,t]),i}(s.src,s),p="loaded"===d,{mounted:v,transitionStatus:E,setMounted:w}=(0,b.useTransitionStatus)(p),S=r.useRef(null),O=(0,f.useStableCallback)(e=>{a?.(e),c(e)});(0,m.useIsoLayoutEffect)(()=>{"idle"!==d&&O(d)},[d,O]),(0,m.useIsoLayoutEffect)(()=>()=>c("idle"),[c]),(0,g.useOpenChangeComplete)({open:p,ref:S,onComplete(){p||w(!1)}});let $=(0,i.useRenderElement)("img",e,{state:{imageLoadingStatus:d,transitionStatus:E},ref:[t,S],props:s,stateAttributesMapping:y,enabled:v});return v?$:null});var E=e.i(439957);let w=r.forwardRef(function(e,t){let{className:n,render:o,delay:a,style:l,...s}=e,{imageLoadingStatus:d}=u(),[f,m]=r.useState(void 0===a),g=(0,E.useTimeout)();return r.useEffect(()=>(void 0!==a?g.start(a,()=>m(!0)):m(!0),g.clear),[g,a]),(0,i.useRenderElement)("span",e,{state:{imageLoadingStatus:d},ref:t,props:s,stateAttributesMapping:c,enabled:"loaded"!==d&&(void 0===a||f)})});e.s(["Fallback",0,w,"Image",0,v,"Root",0,d],514751);var S=e.i(514751),S=S,O=e.i(115504);let $=r.forwardRef(({className:e,...t},r)=>(0,a.jsx)(S.Root,{ref:r,"data-slot":"avatar",className:(0,O.cn)("relative flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-full",e),...t}));$.displayName="Avatar",r.forwardRef(({className:e,...t},r)=>(0,a.jsx)(S.Image,{ref:r,"data-slot":"avatar-image",className:(0,O.cn)("size-full object-cover",e),...t})).displayName="AvatarImage";let C=r.forwardRef(({className:e,...t},r)=>(0,a.jsx)(S.Fallback,{ref:r,"data-slot":"avatar-fallback",className:(0,O.cn)("flex size-full items-center justify-center rounded-full text-xs font-medium",e),...t}));C.displayName="AvatarFallback",e.s(["Avatar",0,$,"AvatarFallback",0,C],799676)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0nb9hn_5vp72z.js b/litellm/proxy/_experimental/out/_next/static/chunks/0nb9hn_5vp72z.js new file mode 100644 index 00000000000..e61510b7eeb --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0nb9hn_5vp72z.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,56567,838932,788259,e=>{"use strict";var t=e.i(843476),l=e.i(135214),a=e.i(109799),s=e.i(912598),i=e.i(907308),r=e.i(602869),n=e.i(266027);let o=(0,e.i(243652).createQueryKeys)("guardrails"),d=()=>{let{accessToken:e,userId:t,userRole:a}=(0,l.default)();return(0,n.useQuery)({queryKey:o.list({}),queryFn:async()=>(0,r.getGuardrailsList)(e),enabled:!!(e&&t&&a),select:e=>{let t=e?.guardrails??[],l=new Set,a=new Set;for(let e of t)e.litellm_params?.default_on?l.add(e.guardrail_name):a.add(e.guardrail_name);return{guardrails:t,globalGuardrailNames:l,optionalGuardrailNames:a}}})};e.s(["useGuardrails",0,d],838932);var m=e.i(500330),c=e.i(11751),u=e.i(708347),g=e.i(751904),h=e.i(160818),p=e.i(827252),_=e.i(564897),x=e.i(646563),b=e.i(987432),j=e.i(530212),y=e.i(677667),f=e.i(130643),v=e.i(898667),T=e.i(389083),S=e.i(304967),w=e.i(350967),N=e.i(599724),C=e.i(779241),k=e.i(629569),M=e.i(464571),I=e.i(808613),F=e.i(311451),A=e.i(28651),P=e.i(199133),D=e.i(770914),z=e.i(790848),O=e.i(653496),L=e.i(262218),B=e.i(592968),R=e.i(888259),U=e.i(678784),V=e.i(118366),E=e.i(271645),G=e.i(9314),K=e.i(552130),$=e.i(127952);function W({className:e,value:l,onChange:a}){return(0,t.jsxs)(P.Select,{className:e,value:l,onChange:a,children:[(0,t.jsx)(P.Select.Option,{value:"24h",children:"Daily"}),(0,t.jsx)(P.Select.Option,{value:"7d",children:"Weekly"}),(0,t.jsx)(P.Select.Option,{value:"30d",children:"Monthly"})]})}var q=e.i(844565),H=e.i(355619);let J=function({globalGuardrailNames:e,teamGuardrails:l=[],optedOutGlobalGuardrails:a=[],killSwitchOn:s=!1,variant:i="card",className:r=""}){let n=new Set(a),o=Array.from(e).filter(e=>!n.has(e)),d=l.filter(t=>!e.has(t)),m=s||0!==o.length||0!==d.length?(0,t.jsxs)("div",{className:"flex flex-col gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)("span",{className:"block text-sm font-medium text-gray-700 mb-2",children:[(0,t.jsx)(h.GlobalOutlined,{style:{marginInlineEnd:4},"aria-label":"Global guardrail"}),"Global"]}),s?(0,t.jsx)(L.Tag,{color:"gold",children:"Bypassed for this team"}):o.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:o.map(e=>(0,t.jsx)(L.Tag,{color:"blue",children:e},e))}):(0,t.jsx)("span",{className:"block text-sm text-gray-500",children:"None configured"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block text-sm font-medium text-gray-700 mb-2",children:"Team-specific"}),d.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:d.map(e=>(0,t.jsx)(L.Tag,{color:"blue",children:e},e))}):(0,t.jsx)("span",{className:"block text-sm text-gray-500",children:"None configured"})]})]}):(0,t.jsx)("span",{className:"block text-gray-500",children:"No guardrails configured"});return"card"===i?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${r}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-semibold text-gray-900",children:"Guardrails Settings"}),(0,t.jsx)("span",{className:"block text-xs text-gray-500",children:"Global and team-specific guardrails applied to this team"})]})}),m]}):(0,t.jsxs)("div",{className:`${r}`,children:[(0,t.jsx)("span",{className:"block font-medium text-gray-900 mb-3",children:"Guardrails Settings"}),m]})};var Y=e.i(643449),Q=e.i(75921),Z=e.i(390605),X=e.i(162386),ee=e.i(727749),et=e.i(384767),el=e.i(435451),ea=e.i(916940);let es=({onChange:e,value:l,className:a,accessToken:s,placeholder:i="Select search tools (optional)",disabled:n=!1})=>{let[o,d]=(0,E.useState)([]),[m,c]=(0,E.useState)(!1);return(0,E.useEffect)(()=>{(async()=>{if(s){c(!0);try{let e=await (0,r.fetchSearchTools)(s),t=Array.isArray(e?.search_tools)?e.search_tools:Array.isArray(e?.data)?e.data:[];d(t.map(e=>e?.search_tool_name).filter(e=>"string"==typeof e&&e.length>0).map(e=>({label:e,value:e})))}catch(e){console.error("Failed to load search tools:",e)}finally{c(!1)}}})()},[s]),(0,t.jsx)(P.Select,{mode:"multiple",allowClear:!0,showSearch:!0,optionFilterProp:"label",placeholder:i,onChange:e,value:l,loading:m,className:a,options:o,style:{width:"100%"},disabled:n})};e.s(["default",0,es],788259);var ei=e.i(183588),er=e.i(460285),en=e.i(276173),eo=e.i(91979),ed=e.i(269200),em=e.i(942232),ec=e.i(977572),eu=e.i(427612),eg=e.i(64848),eh=e.i(496020),ep=e.i(536916),e_=e.i(21548);let ex={"/key/generate":"Member can generate a virtual key for this team","/key/service-account/generate":"Member can generate a service account key (not belonging to any user) for this team","/key/update":"Member can update a virtual key belonging to this team","/key/delete":"Member can delete a virtual key belonging to this team","/key/info":"Member can get info about a virtual key belonging to this team","/key/regenerate":"Member can regenerate a virtual key belonging to this team","/key/{key_id}/regenerate":"Member can regenerate a virtual key belonging to this team","/key/list":"Member can list virtual keys belonging to this team","/key/block":"Member can block a virtual key belonging to this team","/key/unblock":"Member can unblock a virtual key belonging to this team","/key/access_group_assignment":"Member can assign access groups to virtual keys for this team","/team/daily/activity":"Member can view all team usage data (not just their own)","/spend/logs":"Member can view spend logs for the entire team (not just their own)"},eb=({teamId:e,accessToken:l,canEditTeam:a})=>{let[s,i]=(0,E.useState)([]),[n,o]=(0,E.useState)([]),[d,m]=(0,E.useState)(!0),[c,u]=(0,E.useState)(!1),[g,h]=(0,E.useState)(!1),p=async()=>{try{if(m(!0),!l)return;let t=await (0,r.getTeamPermissionsCall)(l,e),a=t.all_available_permissions||[];i(a);let s=t.team_member_permissions||[];o(s),h(!1)}catch(e){ee.default.fromBackend("Failed to load permissions"),console.error("Error fetching permissions:",e)}finally{m(!1)}};(0,E.useEffect)(()=>{p()},[e,l]);let _=async()=>{try{if(!l)return;u(!0),await (0,r.teamPermissionsUpdateCall)(l,e,n),ee.default.success("Permissions updated successfully"),h(!1)}catch(e){ee.default.fromBackend("Failed to update permissions"),console.error("Error updating permissions:",e)}finally{u(!1)}};if(d)return(0,t.jsx)("div",{className:"p-6 text-center",children:"Loading permissions..."});let x=s.length>0;return(0,t.jsxs)(S.Card,{className:"bg-white shadow-md rounded-md p-6",children:[(0,t.jsxs)("div",{className:"flex flex-col sm:flex-row justify-between items-start sm:items-center border-b pb-4 mb-6",children:[(0,t.jsx)(k.Title,{className:"mb-2 sm:mb-0",children:"Member Permissions"}),a&&g&&(0,t.jsxs)("div",{className:"flex gap-3",children:[(0,t.jsx)(M.Button,{icon:(0,t.jsx)(eo.ReloadOutlined,{}),onClick:()=>{p()},children:"Reset"}),(0,t.jsx)(M.Button,{onClick:_,loading:c,type:"primary",icon:(0,t.jsx)(b.SaveOutlined,{}),children:"Save Changes"})]})]}),(0,t.jsx)(N.Text,{className:"mb-6 text-gray-600",children:"Control what team members can do when they are not team admins."}),x?(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(ed.Table,{className:" min-w-full",children:[(0,t.jsx)(eu.TableHead,{children:(0,t.jsxs)(eh.TableRow,{children:[(0,t.jsx)(eg.TableHeaderCell,{children:"Method"}),(0,t.jsx)(eg.TableHeaderCell,{children:"Endpoint"}),(0,t.jsx)(eg.TableHeaderCell,{children:"Description"}),(0,t.jsx)(eg.TableHeaderCell,{className:"sticky right-0 bg-white shadow-[-4px_0_4px_-4px_rgba(0,0,0,0.1)] text-center",children:"Allow Access"})]})}),(0,t.jsx)(em.TableBody,{children:s.map(e=>{let l=(e=>{let t=e.includes("/info")||e.includes("/list")||e.includes("/activity")||"/spend/logs"===e?"GET":"POST",l=ex[e];if(!l){for(let[t,a]of Object.entries(ex))if(e.includes(t)){l=a;break}}return l||(l=`Access ${e}`),{method:t,endpoint:e,description:l,route:e}})(e);return(0,t.jsxs)(eh.TableRow,{className:"hover:bg-gray-50 transition-colors",children:[(0,t.jsx)(ec.TableCell,{children:(0,t.jsx)("span",{className:`px-2 py-1 rounded text-xs font-medium ${"GET"===l.method?"bg-blue-100 text-blue-800":"bg-green-100 text-green-800"}`,children:l.method})}),(0,t.jsx)(ec.TableCell,{children:(0,t.jsx)("span",{className:"font-mono text-sm text-gray-800",children:l.endpoint})}),(0,t.jsx)(ec.TableCell,{className:"text-gray-700",children:l.description}),(0,t.jsx)(ec.TableCell,{className:"sticky right-0 bg-white shadow-[-4px_0_4px_-4px_rgba(0,0,0,0.1)] text-center",children:(0,t.jsx)(ep.Checkbox,{checked:n.includes(e),onChange:t=>{o(t.target.checked?[...n,e]:n.filter(t=>t!==e)),h(!0)},disabled:!a})})]},e)})})]})}):(0,t.jsx)("div",{className:"py-12",children:(0,t.jsx)(e_.Empty,{description:"No permissions available"})})]})};var ej=e.i(822315),ey=e.i(175712),ef=e.i(178654),ev=e.i(621192),eT=e.i(898586),eS=e.i(431703);let ew=async(e,t)=>{let l=(0,r.getProxyBaseUrl)(),a=l?`${l}/team/${encodeURIComponent(t)}/members/me`:`/team/${encodeURIComponent(t)}/members/me`,s=await fetch(a,{method:"GET",headers:{[(0,r.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(404===s.status)return null;if(!s.ok){let e=await s.json().catch(()=>({}));throw Error((0,eS.deriveErrorMessage)(e))}return await s.json()},eN=(e,l)=>(0,t.jsxs)(D.Space,{size:4,children:[(0,t.jsx)(eT.Typography.Text,{type:"secondary",children:e}),(0,t.jsx)(B.Tooltip,{title:l,children:(0,t.jsx)(p.InfoCircleOutlined,{style:{color:"#8c8c8c"}})})]}),eC=(e,t=4)=>null==e?"0":(0,m.formatNumberWithCommas)(e,t),ek=e=>null==e?"Unlimited":(0,m.formatNumberWithCommas)(e,0);function eM({teamId:e}){let{data:a,isLoading:s,error:i}=(e=>{let{accessToken:t}=(0,l.default)();return(0,n.useQuery)({queryKey:["team",e,"members","me"],queryFn:()=>ew(t,e),enabled:!!(t&&e)})})(e);if(s)return(0,t.jsx)(ey.Card,{children:(0,t.jsx)(eT.Typography.Text,{type:"secondary",children:"Loading your membership info…"})});if(i)return(0,t.jsx)(ey.Card,{children:(0,t.jsx)(eT.Typography.Text,{type:"danger",children:i instanceof Error?i.message:"Failed to load your membership info for this team."})});if(!a)return(0,t.jsx)(ey.Card,{children:(0,t.jsx)(eT.Typography.Text,{type:"secondary",children:"No membership info available for the current user in this team."})});let r=a.litellm_budget_table??null,o=r?.max_budget??null,d=a.spend??0,m=a.total_spend??0,c=r?.tpm_limit??null,u=r?.rpm_limit??null,g=function(e){if(!e)return null;let t=(0,ej.default)(e);return t.isValid()?t.format("MMM D, YYYY"):null}(r?.budget_reset_at),h=r?.allowed_models??null;return(0,t.jsxs)(D.Space,{direction:"vertical",size:"middle",style:{width:"100%"},children:[(0,t.jsx)(ey.Card,{children:(0,t.jsxs)(ev.Row,{gutter:[24,16],children:[(0,t.jsxs)(ef.Col,{xs:24,sm:12,md:8,children:[(0,t.jsx)(eT.Typography.Text,{type:"secondary",children:"User"}),(0,t.jsx)("div",{style:{marginTop:4},children:(0,t.jsx)(eT.Typography.Text,{strong:!0,children:a.user_email||a.user_id})}),(0,t.jsx)(eT.Typography.Text,{type:"secondary",style:{fontSize:12,fontFamily:"monospace"},children:a.user_id})]}),(0,t.jsxs)(ef.Col,{xs:24,sm:12,md:8,children:[(0,t.jsx)(eT.Typography.Text,{type:"secondary",children:"Team Role"}),(0,t.jsx)("div",{style:{marginTop:4},children:(0,t.jsx)(L.Tag,{color:"admin"===a.role?"blue":"default",children:a.role||"user"})})]})]})}),(0,t.jsxs)(ev.Row,{gutter:[16,16],children:[(0,t.jsx)(ef.Col,{xs:24,md:12,children:(0,t.jsxs)(ey.Card,{children:[eN("Current Cycle Spend (USD)","Spend for the current budget cycle. Resets to $0 when the budget window rolls over."),(0,t.jsxs)("div",{style:{marginTop:8},children:[(0,t.jsxs)(eT.Typography.Title,{level:3,style:{margin:0},children:["$",eC(d,4)]}),(0,t.jsxs)(eT.Typography.Text,{type:"secondary",children:["of ",null===o?"Unlimited":`$${eC(o,4)}`]})]}),g&&(0,t.jsx)("div",{style:{marginTop:4},children:(0,t.jsxs)(eT.Typography.Text,{type:"secondary",children:["Resets ",g]})})]})}),(0,t.jsx)(ef.Col,{xs:24,md:12,children:(0,t.jsxs)(ey.Card,{children:[eN("Rate Limits","Your per-member rate limits within this team."),(0,t.jsxs)("div",{style:{marginTop:8},children:[(0,t.jsxs)(eT.Typography.Text,{children:["TPM: ",ek(c)]}),(0,t.jsx)("br",{}),(0,t.jsxs)(eT.Typography.Text,{children:["RPM: ",ek(u)]})]})]})}),(0,t.jsx)(ef.Col,{xs:24,md:12,children:(0,t.jsxs)(ey.Card,{children:[eN("Total Spend (USD)","Cumulative spend across all budget cycles within this team."),(0,t.jsx)("div",{style:{marginTop:8},children:(0,t.jsxs)(eT.Typography.Title,{level:4,style:{margin:0},children:["$",eC(m,4)]})})]})}),(0,t.jsx)(ef.Col,{xs:24,md:12,children:(0,t.jsxs)(ey.Card,{children:[eN("Model Scope","Models you can access within this team."),(0,t.jsx)("div",{style:{marginTop:8},children:h&&h.length>0?(0,t.jsx)(D.Space,{wrap:!0,children:h.map(e=>(0,t.jsx)(L.Tag,{children:e},e))}):(0,t.jsx)(eT.Typography.Text,{children:"All Team Models"})})]})})]})]})}let eI="overview",eF="my-user",eA="virtual-keys",eP="members",eD="member-permissions",ez="settings",eO={[eI]:"Overview",[eF]:"My User",[eA]:"Virtual Keys",[eP]:"Members",[eD]:"Member Permissions",[ez]:"Settings"};var eL=e.i(292639);e.i(622826);var eB=e.i(200208),eR=e.i(964471),eU=e.i(294612);function eV({teamData:e,canEditTeam:a,handleMemberDelete:s,setSelectedEditMember:i,setIsEditMemberModalVisible:r,setIsAddMemberModalVisible:n}){let o=e=>{if(null==e)return"0";if("number"==typeof e){let t=Number(e);return t===Math.floor(t)?t.toString():(0,m.formatNumberWithCommas)(t,8).replace(/\.?0+$/,"")}return"0"},{data:d}=(0,eL.useUISettings)(),{userId:c,userRole:g}=(0,l.default)(),h=!!d?.values?.disable_team_admin_delete_team_user,_=(0,u.isUserTeamAdminForSingleTeam)(e.team_info.members_with_roles,c||""),x=(0,u.isProxyAdminRole)(g||""),b=[{title:(0,t.jsxs)(D.Space,{direction:"horizontal",children:["Model Scope",(0,t.jsx)(B.Tooltip,{title:"Models this member can access. Empty means they inherit all team models.",children:(0,t.jsx)(p.InfoCircleOutlined,{})})]}),key:"model_scope",render:(l,a)=>{let s=(t=>{if(!t)return null;let l=e.team_memberships.find(e=>e.user_id===t),a=l?.litellm_budget_table?.allowed_models;return a&&a.length>0?a:null})(a.user_id);if(!s)return(0,t.jsx)(eT.Typography.Text,{type:"secondary",children:"(all team models)"});let i=s.slice(0,2),r=s.length-i.length;return(0,t.jsxs)(D.Space,{wrap:!0,children:[i.map(e=>(0,t.jsx)(eT.Typography.Text,{code:!0,style:{fontSize:"12px"},children:e},e)),r>0&&(0,t.jsx)(B.Tooltip,{title:s.slice(2).join(", "),children:(0,t.jsxs)(eT.Typography.Text,{type:"secondary",children:["+",r," more"]})})]})}},{title:(0,t.jsxs)(D.Space,{direction:"horizontal",children:["Current Cycle Spend (USD)",(0,t.jsx)(B.Tooltip,{title:"Spend for the current budget cycle. Resets to $0 when the member's budget window rolls over. This is the value checked against the member's budget.",children:(0,t.jsx)(p.InfoCircleOutlined,{})})]}),key:"spend",render:(l,a)=>(0,t.jsx)(eR.MoneyCell,{value:(t=>{if(!t)return 0;let l=e.team_memberships.find(e=>e.user_id===t);return l?.spend??0})(a.user_id),decimals:4})},{title:(0,t.jsxs)(D.Space,{direction:"horizontal",children:["Total Spend (USD)",(0,t.jsx)(B.Tooltip,{title:"Cumulative spend by this member within this team, across all budget cycles. Tracking began 2026-04-21; spend from before that date is not included.",children:(0,t.jsx)(p.InfoCircleOutlined,{})})]}),key:"total_spend",render:(l,a)=>(0,t.jsx)(eR.MoneyCell,{value:(t=>{if(!t)return 0;let l=e.team_memberships.find(e=>e.user_id===t);return l?.total_spend??0})(a.user_id),decimals:4})},{title:"Team Member Budget (USD)",key:"budget",render:(l,a)=>(0,t.jsx)(eR.MoneyCell,{value:(t=>{if(!t)return null;let l=e.team_memberships.find(e=>e.user_id===t);return l?.litellm_budget_table?.max_budget??null})(a.user_id),decimals:4,emptyText:"Unlimited",showZero:!0})},{title:"Budget Reset",key:"budget_reset",render:(l,a)=>(0,t.jsx)(eB.DateCell,{value:(t=>{if(!t)return null;let l=e.team_memberships.find(e=>e.user_id===t);return l?.litellm_budget_table?.budget_reset_at??null})(a.user_id),precision:"date"})},{title:(0,t.jsxs)(D.Space,{direction:"horizontal",children:["Team Member Rate Limits",(0,t.jsx)(B.Tooltip,{title:"Rate limits for this member's usage within this team.",children:(0,t.jsx)(p.InfoCircleOutlined,{})})]}),key:"rate_limits",render:(l,a)=>(0,t.jsx)(eT.Typography.Text,{children:(t=>{if(!t)return"No Limits";let l=e.team_memberships.find(e=>e.user_id===t),a=l?.litellm_budget_table?.rpm_limit,s=l?.litellm_budget_table?.tpm_limit,i=[a?`${o(a)} RPM`:null,s?`${o(s)} TPM`:null].filter(Boolean);return i.length>0?i.join(" / "):"No Limits"})(a.user_id)})}];return(0,t.jsx)(eU.default,{members:e.team_info.members_with_roles,canEdit:a,onEdit:t=>{let l=e.team_memberships.find(e=>e.user_id===t.user_id);i({...t,max_budget_in_team:l?.litellm_budget_table?.max_budget||null,tpm_limit:l?.litellm_budget_table?.tpm_limit||null,rpm_limit:l?.litellm_budget_table?.rpm_limit||null,budget_duration:l?.litellm_budget_table?.budget_duration||null,allowed_models:l?.litellm_budget_table?.allowed_models||[]}),r(!0)},onDelete:s,onAddMember:()=>n(!0),roleColumnTitle:"Team Role",roleTooltip:"This role applies only to this team and is independent from the user's proxy-level role.",extraColumns:b,showDeleteForMember:()=>x||a&&!_||_&&!h})}var eE=e.i(207082),eG=e.i(399536);e.i(707701);var eK=e.i(807235),e$=e.i(981080),eW=e.i(494862),eq=e.i(531649),eH=e.i(793479),eJ=e.i(871943),eY=e.i(502547),eQ=e.i(140928),eZ=e.i(752978),eX=e.i(282786),e0=e.i(304911),e1=e.i(20147);let e2=[{id:"created_at",desc:!0}];function e4({teamId:e,teamAlias:l,organization:a}){let[s,i]=(0,E.useState)(null),[r,n]=(0,E.useState)(e2),[o,d]=(0,E.useState)({pageIndex:0,pageSize:50}),[m,c]=(0,E.useState)([]),[u,g]=(0,E.useState)(!1),[h,p]=(0,E.useState)(""),[_]=(0,eQ.useDebouncedValue)(h,{wait:300}),x=(0,E.useCallback)(e=>{p(e),d(e=>({...e,pageIndex:0}))},[]),b=(0,E.useCallback)(e=>{let t=m.find(t=>t.id===e);return"string"==typeof t?.value&&t.value.trim()?t.value.trim():void 0},[m]),j=r.length>0?r[0].id:"created_at",y=r.length>0?r[0].desc?"desc":"asc":"desc",f=o.pageIndex,v=o.pageSize,{data:S,isPending:w,isFetching:C,refetch:k}=(0,eE.useKeys)(f+1,v,{teamID:e,selectedKeyAlias:_.trim()||void 0,userID:b("user_id"),sortBy:j||void 0,sortOrder:y||void 0,expand:"user"}),M=(0,E.useMemo)(()=>{let e=S?.keys||[],t=a?.organization_id;return t?e.map(e=>({...e,organization_id:(e.organization_id??e.org_id)||t})):e},[S?.keys,a?.organization_id]),I=S?.total_count??0,[F,A]=(0,E.useState)({}),P=(0,E.useMemo)(()=>({team_id:e,team_alias:l||e,models:[],max_budget:null,budget_duration:null,tpm_limit:null,rpm_limit:null,organization_id:a?.organization_id||"",created_at:"",keys:[],members_with_roles:[],spend:0}),[e,l,a]),D=(0,E.useCallback)(()=>{k?.()},[k]);(0,E.useEffect)(()=>(window.addEventListener("storage",D),()=>window.removeEventListener("storage",D)),[D]);let z=(0,E.useCallback)(e=>{c(e),d(e=>({...e,pageIndex:0}))},[]),O=(0,E.useMemo)(()=>[{id:"token",accessorKey:"token",meta:{title:"Key ID"},header:({column:e})=>(0,t.jsx)(eW.DataTableSortHeader,{column:e,title:"Key ID",variant:"header-cycle"}),size:120,enableSorting:!0,cell:e=>(0,t.jsx)(eG.IdCell,{value:e.getValue(),onClick:()=>i(e.row.original)})},{id:"key_alias",accessorKey:"key_alias",meta:{title:"Key Alias"},header:({column:e})=>(0,t.jsx)(eW.DataTableSortHeader,{column:e,title:"Key Alias",variant:"header-cycle"}),size:150,enableSorting:!0,cell:e=>{let l=e.getValue(),a=e.cell.column.getSize();return(0,t.jsx)(B.Tooltip,{title:l,children:(0,t.jsx)("span",{className:"font-mono text-xs truncate block",style:{maxWidth:a,overflow:"hidden"},children:l??"-"})})}},{id:"key_name",accessorKey:"key_name",header:"Secret Key",size:120,enableSorting:!1,cell:e=>(0,t.jsx)("span",{className:"font-mono text-xs",children:e.getValue()})},{id:"organization_id",accessorKey:"organization_id",header:"Organization ID",size:140,enableSorting:!1,cell:e=>e.getValue()?e.renderValue():"-"},{id:"user_email",accessorKey:"user",header:"User Email",size:160,enableSorting:!1,cell:e=>{let l=e.getValue(),a=l?.user_email,s=e.cell.column.getSize();return(0,t.jsx)(B.Tooltip,{title:a,children:(0,t.jsx)("span",{className:"font-mono text-xs truncate block",style:{maxWidth:s,overflow:"hidden"},children:a??"-"})})}},{id:"user_id",accessorKey:"user_id",header:"User ID",size:70,enableSorting:!1,cell:e=>{let l=e.getValue(),a="default_user_id"===l?"Default Proxy Admin":l,s=e.cell.column.getSize();return(0,t.jsx)(B.Tooltip,{title:a,children:(0,t.jsx)("span",{className:"font-mono text-xs truncate block",style:{maxWidth:s,overflow:"hidden"},children:a??"-"})})}},{id:"created_at",accessorKey:"created_at",meta:{title:"Created At"},header:({column:e})=>(0,t.jsx)(eW.DataTableSortHeader,{column:e,title:"Created At",variant:"header-cycle"}),size:120,enableSorting:!0,cell:e=>(0,t.jsx)(eB.DateCell,{value:e.getValue(),precision:"date"})},{id:"created_by",accessorKey:"created_by",header:"Created By",size:130,enableSorting:!1,cell:e=>{let l=e.getValue();if(!l)return"-";let{created_by_user:a}=e.row.original,s=a?.user_alias??null,i=a?.user_email??null,r="default_user_id"===l,n=s||i||l,o=e.cell.column.getSize(),d=(0,t.jsx)("div",{className:"flex flex-col gap-2 text-xs min-w-[200px] max-w-[300px]",children:[{label:"User Alias",value:s},{label:"User Email",value:i},{label:"User ID",value:l}].map(({label:e,value:l})=>(0,t.jsxs)("div",{className:"flex flex-col min-w-0",children:[(0,t.jsx)("span",{className:"text-gray-400",children:e}),l?(0,t.jsx)(eT.Typography.Text,{className:"font-mono text-xs",ellipsis:{tooltip:l},copyable:!0,children:l}):(0,t.jsx)("span",{className:"font-mono",children:"-"})]},e))});return!r||s||i?(0,t.jsx)(eX.Popover,{content:d,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"font-mono text-xs truncate block cursor-default",style:{maxWidth:o,overflow:"hidden"},children:n})}):(0,t.jsx)(eX.Popover,{content:d,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"cursor-default",children:(0,t.jsx)(e0.default,{userId:l})})})}},{id:"updated_at",accessorKey:"updated_at",meta:{title:"Updated At"},header:({column:e})=>(0,t.jsx)(eW.DataTableSortHeader,{column:e,title:"Updated At",variant:"header-cycle"}),size:120,enableSorting:!0,cell:e=>(0,t.jsx)(eB.DateCell,{value:e.getValue(),precision:"date",fallback:"Never"})},{id:"last_active",accessorKey:"last_active",header:"Last Active",size:130,enableSorting:!1,cell:e=>(0,t.jsx)(eB.DateCell,{value:e.getValue(),precision:"date",fallback:"Unknown"})},{id:"expires",accessorKey:"expires",header:"Expires",size:120,enableSorting:!1,cell:e=>(0,t.jsx)(eB.DateCell,{value:e.getValue(),precision:"date",fallback:"Never"})},{id:"spend",accessorKey:"spend",meta:{title:"Spend (USD)"},header:({column:e})=>(0,t.jsx)(eW.DataTableSortHeader,{column:e,title:"Spend (USD)",variant:"header-cycle"}),size:100,enableSorting:!0,cell:e=>(0,t.jsx)(eR.MoneyCell,{value:e.getValue(),decimals:4})},{id:"max_budget",accessorKey:"max_budget",meta:{title:"Budget (USD)"},header:({column:e})=>(0,t.jsx)(eW.DataTableSortHeader,{column:e,title:"Budget (USD)",variant:"header-cycle"}),size:110,enableSorting:!0,cell:e=>(0,t.jsx)(eR.MoneyCell,{value:e.getValue(),decimals:0,emptyText:"Unlimited",showZero:!0})},{id:"budget_reset_at",accessorKey:"budget_reset_at",header:"Budget Reset",size:130,enableSorting:!1,cell:e=>(0,t.jsx)(eB.DateCell,{value:e.getValue(),fallback:"Never"})},{id:"models",accessorKey:"models",header:"Models",size:200,enableSorting:!1,cell:e=>{let l=e.getValue();return(0,t.jsx)("div",{className:"flex flex-col py-2",children:Array.isArray(l)?(0,t.jsx)("div",{className:"flex flex-col",children:0===l.length?(0,t.jsx)(T.Badge,{size:"xs",className:"mb-1",color:"red",children:(0,t.jsx)(N.Text,{children:"All Proxy Models"})}):(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)("div",{className:"flex items-start",children:[l.length>3&&(0,t.jsx)("div",{children:(0,t.jsx)(eZ.Icon,{icon:F[e.row.id]?eJ.ChevronDownIcon:eY.ChevronRightIcon,className:"cursor-pointer",size:"xs",onClick:()=>A(t=>({...t,[e.row.id]:!t[e.row.id]}))})}),(0,t.jsxs)("div",{className:"flex flex-wrap gap-1",children:[l.slice(0,3).map((e,l)=>"all-proxy-models"===e?(0,t.jsx)(T.Badge,{size:"xs",color:"red",children:(0,t.jsx)(N.Text,{children:"All Proxy Models"})},l):(0,t.jsx)(T.Badge,{size:"xs",color:"blue",children:(0,t.jsx)(N.Text,{children:e.length>30?`${(0,H.getModelDisplayName)(e).slice(0,30)}...`:(0,H.getModelDisplayName)(e)})},l)),l.length>3&&!F[e.row.id]&&(0,t.jsx)(T.Badge,{size:"xs",color:"gray",className:"cursor-pointer",children:(0,t.jsxs)(N.Text,{children:["+",l.length-3," ",l.length-3==1?"more model":"more models"]})}),F[e.row.id]&&(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:l.slice(3).map((e,l)=>"all-proxy-models"===e?(0,t.jsx)(T.Badge,{size:"xs",color:"red",children:(0,t.jsx)(N.Text,{children:"All Proxy Models"})},l+3):(0,t.jsx)(T.Badge,{size:"xs",color:"blue",children:(0,t.jsx)(N.Text,{children:e.length>30?`${(0,H.getModelDisplayName)(e).slice(0,30)}...`:(0,H.getModelDisplayName)(e)})},l+3))})]})]})})}):null})}},{id:"rate_limits",header:"Rate Limits",size:140,enableSorting:!1,cell:({row:e})=>{let l=e.original;return(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{children:["TPM: ",null!==l.tpm_limit?l.tpm_limit:"Unlimited"]}),(0,t.jsxs)("div",{children:["RPM: ",null!==l.rpm_limit?l.rpm_limit:"Unlimited"]})]})}}],[F]),L=(0,E.useCallback)(e=>{n(e),d(e=>({...e,pageIndex:0}))},[]);return(0,t.jsx)("div",{className:"w-full h-full overflow-hidden",children:s?(0,t.jsx)(e1.default,{keyId:s.token,onClose:()=>i(null),keyData:s,teams:[P],onDelete:k}):(0,t.jsx)("div",{className:"py-4 flex-1 overflow-hidden",children:(0,t.jsx)(eK.DataTable,{data:M,columns:O,sortingMode:"server",sorting:r,onSortingChange:L,paginationMode:"server",pagination:o,onPaginationChange:d,rowCount:I,filterMode:"server",columnFilters:m,onColumnFiltersChange:z,enableColumnResizing:!0,columnResizeMode:"onChange",isLoading:w||C,loadingMessage:"Loading keys...",maxBodyHeight:"75vh",size:"compact",toolbar:e=>(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(eq.DataTableToolbar,{table:e,searchValue:h,onSearchChange:x,searchPlaceholder:"Search by key alias…",onRefresh:()=>k?.(),isRefreshing:C,onOpenFilters:()=>g(!0),filterLabels:{user_id:"User ID"}}),(0,t.jsx)(e$.DataTableFilterDrawer,{table:e,open:u,onOpenChange:g,title:"Filters",description:`Narrow down keys for ${l??"this team"}`,children:({get:e,set:l})=>(0,t.jsx)(e$.DataTableFilterField,{label:"User ID",children:(0,t.jsx)(eH.Input,{value:e("user_id")??"",onChange:e=>l("user_id",e.target.value),placeholder:"Filter by user ID…"})})})]})})})})}e.s(["default",0,({teamId:e,onClose:n,accessToken:o,is_team_admin:eo,is_proxy_admin:ed,is_org_admin:em=!1,userModels:ec,editTeam:eu,premiumUser:eg=!1,onUpdate:eh})=>{let ep,e_,ex,ej,ey,ef,[ev,eT]=(0,E.useState)(null),[eS,ew]=(0,E.useState)(!0),[eN,eC]=(0,E.useState)(!1),[ek]=I.Form.useForm(),[eL,eB]=(0,E.useState)(!1),[eR,eU]=(0,E.useState)(null),[eE,eG]=(0,E.useState)(!1),[eK,e$]=(0,E.useState)([]),[eW,eq]=(0,E.useState)(!1),[eH,eJ]=(0,E.useState)({}),{data:eY,isLoading:eQ}=d(),eZ=eY?.globalGuardrailNames??new Set,[eX,e0]=(0,E.useState)([]),[e1,e2]=(0,E.useState)({}),[e6,e3]=(0,E.useState)(!1),[e8,e5]=(0,E.useState)(null),[e7,e9]=(0,E.useState)(!1),[te,tt]=(0,E.useState)(!1),[tl,ta]=(0,E.useState)(!1),ts=E.default.useRef(null),[ti,tr]=(0,E.useState)(null),{userRole:tn,userId:to}=(0,l.default)(),{data:td=[]}=(0,a.useOrganizations)(),tm=(0,s.useQueryClient)(),tc=(0,E.useMemo)(()=>{let e=ev?.team_info?.organization_id;if(!e||!to)return!1;let t=td.find(t=>t.organization_id===e);return t?.members?.some(e=>e.user_id===to&&"org_admin"===e.user_role)??!1},[ev,td,to]),tu=I.Form.useWatch("models",ek),tg=I.Form.useWatch("disable_global_guardrails",ek),th=(0,E.useMemo)(()=>{let e=tu??ev?.team_info?.models??[];return e.includes("all-proxy-models")||e.includes("all-team-models")?ec:(0,H.unfurlWildcardModelsInList)(e,ec)},[tu,ev,ec]),tp=eo||ed||em||tc,t_=(0,E.useMemo)(()=>{let e;return e=[eI,eF,eA],tp?[...e,eP,eD,ez]:e},[tp]),tx=(0,E.useMemo)(()=>eu&&tp?ez:eI,[eu,tp]),tb=async()=>{try{if(ew(!0),!o)return;let t=await (0,r.teamInfoCall)(o,e);eT(t)}catch(e){ee.default.fromBackend("Failed to load team information"),console.error("Error fetching team info:",e)}finally{ew(!1)}};(0,E.useEffect)(()=>{tb()},[e,o]),(0,E.useEffect)(()=>{(async()=>{if(!o||!ev?.team_info?.organization_id)return tr(null);try{let e=await (0,r.organizationInfoCall)(o,ev.team_info.organization_id);tr(e)}catch(e){console.error("Error fetching organization info:",e),tr(null)}})()},[o,ev?.team_info?.organization_id]),(0,E.useMemo)(()=>{let e;return e=[],e=ti?ti.models.includes("all-proxy-models")?ec:ti.models.length>0?ti.models:ec:ec,(0,H.unfurlWildcardModelsInList)(e,ec)},[ti,ec]),(0,E.useEffect)(()=>{(async()=>{try{if(!o)return;let e=(await (0,r.getPoliciesList)(o)).policies.map(e=>e.policy_name);e0(e)}catch(e){console.error("Failed to fetch policies:",e)}})()},[o]),(0,E.useEffect)(()=>{(async()=>{if(!o||!ev?.team_info?.policies||0===ev.team_info.policies.length)return;e3(!0);let e={};try{await Promise.all(ev.team_info.policies.map(async t=>{try{let l=await (0,r.getPolicyInfoWithGuardrails)(o,t);e[t]=l.resolved_guardrails||[]}catch(l){console.error(`Failed to fetch guardrails for policy ${t}:`,l),e[t]=[]}})),e2(e)}catch(e){console.error("Failed to fetch policy guardrails:",e)}finally{e3(!1)}})()},[o,ev?.team_info?.policies]);let tj=async t=>{try{if(null==o)return;let l={user_email:t.user_email,user_id:t.user_id,role:t.role};await (0,r.teamMemberAddCall)(o,e,l),ee.default.success("Team member added successfully"),eC(!1),ek.resetFields();let a=await (0,r.teamInfoCall)(o,e);eT(a),eh(a)}catch(t){let e="Failed to add team member";t?.raw?.detail?.error?.includes("Assigning team admins is a premium feature")?e="Assigning admins is an enterprise-only feature. Please upgrade your LiteLLM plan to enable this.":t?.message&&(e=t.message),ee.default.fromBackend(e),console.error("Error adding team member:",t)}},ty=async t=>{try{if(null==o)return;let l={user_email:t.user_email,user_id:t.user_id,role:t.role,max_budget_in_team:t.max_budget_in_team,tpm_limit:t.tpm_limit,rpm_limit:t.rpm_limit,budget_duration:t.budget_duration,allowed_models:t.allowed_models};R.default.destroy(),await (0,r.teamMemberUpdateCall)(o,e,l),ee.default.success("Team member updated successfully"),eB(!1);let a=await (0,r.teamInfoCall)(o,e);eT(a),eh(a)}catch(t){let e="Failed to update team member";t?.raw?.detail?.includes("Assigning team admins is a premium feature")?e="Assigning admins is an enterprise-only feature. Please upgrade your LiteLLM plan to enable this.":t?.message&&(e=t.message),eB(!1),R.default.destroy(),ee.default.fromBackend(e),console.error("Error updating team member:",t)}},tf=async()=>{if(e8&&o){tt(!0);try{await (0,r.teamMemberDeleteCall)(o,e,e8),ee.default.success("Team member removed successfully");let t=await (0,r.teamInfoCall)(o,e);eT(t),eh(t)}catch(e){ee.default.fromBackend("Failed to remove team member"),console.error("Error removing team member:",e)}finally{tt(!1),e9(!1),e5(null)}}},tv=async t=>{try{let l;if(!o)return;ta(!0);let s={};try{let{soft_budget_alerting_emails:e,...l}=t.metadata?JSON.parse(t.metadata):{};s=l}catch(e){ee.default.fromBackend("Invalid JSON in metadata field");return}if("string"==typeof t.secret_manager_settings&&t.secret_manager_settings.trim().length>0)try{l=JSON.parse(t.secret_manager_settings)}catch(e){ee.default.fromBackend("Invalid JSON in secret manager settings");return}let i=e=>null==e||"string"==typeof e&&""===e.trim()||"number"==typeof e&&Number.isNaN(e)?null:e,n={},d={};for(let e of t.modelLimits??[])e?.model&&(null!=e.tpm&&(n[e.model]=e.tpm),null!=e.rpm&&(d[e.model]=e.rpm));let m=!0===t.disable_global_guardrails,u=m?Array.from(eZ):Array.from(eZ).filter(e=>!(t.guardrails||[]).includes(e)),g=ed?{allowed_passthrough_routes:t.allowed_passthrough_routes||[]}:tT.metadata?.allowed_passthrough_routes?{allowed_passthrough_routes:tT.metadata.allowed_passthrough_routes}:{},h={team_id:e,team_alias:t.team_alias,models:t.models,tpm_limit:i(t.tpm_limit),rpm_limit:i(t.rpm_limit),model_tpm_limit:n,model_rpm_limit:d,max_budget:t.max_budget,soft_budget:i(t.soft_budget),budget_duration:t.budget_duration,metadata:{...s,...g,guardrails:(t.guardrails||[]).filter(e=>!eZ.has(e)),opted_out_global_guardrails:u,...t.logging_settings?.length>0?{logging:t.logging_settings}:{},disable_global_guardrails:m,soft_budget_alerting_emails:"string"==typeof t.soft_budget_alerting_emails?t.soft_budget_alerting_emails.split(",").map(e=>e.trim()).filter(e=>e.length>0):t.soft_budget_alerting_emails||[],...void 0!==l?{secret_manager_settings:l}:{}},...t.policies?.length>0?{policies:t.policies}:{},...t.organization_id!==tT.organization_id?{organization_id:t.organization_id??null}:{}};h.max_budget=(0,c.mapEmptyStringToNull)(h.max_budget),h.team_member_budget_duration=t.team_member_budget_duration,void 0!==t.team_member_budget&&(h.team_member_budget=Number(t.team_member_budget)),void 0!==t.team_member_key_duration&&(h.team_member_key_duration=t.team_member_key_duration),(void 0!==t.team_member_tpm_limit||void 0!==t.team_member_rpm_limit)&&(h.team_member_tpm_limit=i(t.team_member_tpm_limit),h.team_member_rpm_limit=i(t.team_member_rpm_limit));let{servers:p,accessGroups:_,toolsets:x}=t.mcp_servers_and_groups||{servers:[],accessGroups:[],toolsets:[]},b=new Set(p||[]),j=Object.fromEntries(Object.entries(t.mcp_tool_permissions||{}).filter(([e])=>b.has(e)));h.object_permission={},p&&(h.object_permission.mcp_servers=p),_&&(h.object_permission.mcp_access_groups=_),j&&(h.object_permission.mcp_tool_permissions=j),x&&(h.object_permission.mcp_toolsets=x),delete t.mcp_servers_and_groups,delete t.mcp_tool_permissions;let{agents:y,accessGroups:f}=t.agents_and_groups||{agents:[],accessGroups:[]};y&&y.length>0&&(h.object_permission.agents=y),f&&f.length>0&&(h.object_permission.agent_access_groups=f),delete t.agents_and_groups,t.vector_stores&&t.vector_stores.length>0&&(h.object_permission.vector_stores=t.vector_stores),Array.isArray(t.object_permission_search_tools)&&(h.object_permission.search_tools=t.object_permission_search_tools),void 0!==t.access_group_ids&&(h.access_group_ids=t.access_group_ids),void 0!==t.default_team_member_models&&(h.default_team_member_models=t.default_team_member_models);let v=ts.current?.getValue();if(v?.router_settings){let e=e=>null!=e&&""!==e&&!1!==e&&!(Array.isArray(e)&&0===e.length),t=Object.values(v.router_settings).some(e),l=tT.router_settings&&Object.values(tT.router_settings).some(e);(t||l)&&(h.router_settings=v.router_settings)}await (0,r.teamUpdateCall)(o,h),tm.invalidateQueries({queryKey:a.organizationKeys.all}),ee.default.success("Team settings updated successfully"),eG(!1),tb()}catch(e){console.error("Error updating team:",e)}finally{ta(!1)}};if(eS)return(0,t.jsx)("div",{className:"p-4",children:"Loading..."});if(!ev?.team_info)return(0,t.jsx)("div",{className:"p-4",children:"Team not found"});let{team_info:tT}=ev,tS=tT.metadata?.disable_global_guardrails===!0,tw=new Set(Array.isArray(tT.metadata?.opted_out_global_guardrails)?tT.metadata.opted_out_global_guardrails:[]),tN=(Array.isArray(tT.metadata?.guardrails)?tT.metadata.guardrails:[]).filter(e=>!eZ.has(e)),tC=tS?tN:[...Array.from(eZ).filter(e=>!tw.has(e)),...tN],tk=e=>{e.preventDefault(),e.stopPropagation()},tM=async(e,t)=>{await (0,m.copyToClipboard)(e)&&(eJ(e=>({...e,[t]:!0})),setTimeout(()=>{eJ(e=>({...e,[t]:!1}))},2e3))};return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)("div",{className:"flex justify-between items-center mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(M.Button,{type:"text",icon:(0,t.jsx)(j.ArrowLeftIcon,{className:"h-4 w-4"}),onClick:n,className:"mb-4",children:"Back to Teams"}),(0,t.jsx)(k.Title,{children:tT.team_alias}),(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)(N.Text,{className:"text-gray-500 font-mono",children:tT.team_id}),(0,t.jsx)(M.Button,{type:"text",size:"small",icon:eH["team-id"]?(0,t.jsx)(U.CheckIcon,{size:12}):(0,t.jsx)(V.CopyIcon,{size:12}),onClick:()=>tM(tT.team_id,"team-id"),className:`left-2 z-10 transition-all duration-200 ${eH["team-id"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]})]})}),(0,t.jsx)(O.Tabs,{defaultActiveKey:tx,className:"mb-4",items:[{key:eI,label:eO[eI],children:(0,t.jsxs)(w.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,t.jsxs)(S.Card,{children:[(0,t.jsx)(N.Text,{children:"Budget Status"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(k.Title,{children:["$",(0,m.formatNumberWithCommas)(tT.spend,4)]}),(0,t.jsxs)(N.Text,{children:["of ",null===tT.max_budget?"Unlimited":`$${(0,m.formatNumberWithCommas)(tT.max_budget,4)}`]}),tT.budget_duration&&(0,t.jsxs)(N.Text,{className:"text-gray-500",children:["Reset: ",tT.budget_duration]}),(0,t.jsx)("br",{}),tT.team_member_budget_table&&(0,t.jsxs)(N.Text,{className:"text-gray-500",children:["Team Member Budget: $",(0,m.formatNumberWithCommas)(tT.team_member_budget_table.max_budget,4)]})]})]}),(0,t.jsxs)(S.Card,{children:[(0,t.jsx)(N.Text,{children:"Rate Limits"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(N.Text,{children:["TPM: ",tT.tpm_limit||"Unlimited"]}),(0,t.jsxs)(N.Text,{children:["RPM: ",tT.rpm_limit||"Unlimited"]}),tT.max_parallel_requests&&(0,t.jsxs)(N.Text,{children:["Max Parallel Requests: ",tT.max_parallel_requests]}),(ep=tT.metadata?.model_tpm_limit??{},e_=tT.metadata?.model_rpm_limit??{},0===(ex=Array.from(new Set([...Object.keys(ep),...Object.keys(e_)]))).length?null:(0,t.jsxs)("div",{className:"mt-3",children:[(0,t.jsx)(N.Text,{className:"text-gray-500",children:"Per-model limits:"}),ex.map(e=>(0,t.jsxs)(N.Text,{className:"text-xs",children:[e,": TPM ",ep[e]??"—",", RPM ",e_[e]??"—"]},e))]}))]})]}),(0,t.jsxs)(S.Card,{children:[(0,t.jsx)(N.Text,{children:"Models"}),(0,t.jsx)("div",{className:"mt-2 flex flex-wrap gap-2",children:0===tT.models.length||tT.models.includes("all-proxy-models")?(0,t.jsx)(T.Badge,{color:"red",children:"All proxy models"}):(0,t.jsxs)(t.Fragment,{children:[tT.models.map((e,l)=>(0,t.jsx)(T.Badge,{color:"blue",children:e},`direct-${l}`)),(tT.access_group_models||[]).map((e,l)=>(0,t.jsx)(T.Badge,{color:"green",title:"From access group",children:e},`ag-${l}`))]})})]}),(0,t.jsxs)(S.Card,{children:[(0,t.jsx)(N.Text,{className:"font-semibold text-gray-900",children:"Virtual Keys"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(N.Text,{children:["User Keys: ",ev.keys.filter(e=>e.user_id).length]}),(0,t.jsxs)(N.Text,{children:["Service Account Keys: ",ev.keys.filter(e=>!e.user_id).length]}),(0,t.jsxs)(N.Text,{className:"text-gray-500",children:["Total: ",ev.keys.length]})]})]}),(0,t.jsx)(et.default,{objectPermission:tT.object_permission,variant:"card",accessToken:o}),(0,t.jsx)(S.Card,{children:(0,t.jsx)(J,{globalGuardrailNames:eZ,teamGuardrails:Array.isArray(tT.metadata?.guardrails)?tT.metadata.guardrails:[],optedOutGlobalGuardrails:Array.isArray(tT.metadata?.opted_out_global_guardrails)?tT.metadata.opted_out_global_guardrails:[],killSwitchOn:tS,variant:"inline"})}),(0,t.jsxs)(S.Card,{children:[(0,t.jsx)(N.Text,{className:"font-semibold text-gray-900 mb-3",children:"Policies"}),tT.policies&&tT.policies.length>0?(0,t.jsx)("div",{className:"space-y-4",children:tT.policies.map((e,l)=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(T.Badge,{color:"purple",children:e}),e6&&(0,t.jsx)(N.Text,{className:"text-xs text-gray-400",children:"Loading guardrails..."})]}),!e6&&e1[e]&&e1[e].length>0&&(0,t.jsxs)("div",{className:"ml-4 pl-3 border-l-2 border-gray-200",children:[(0,t.jsx)(N.Text,{className:"text-xs text-gray-500 mb-1",children:"Resolved Guardrails:"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:e1[e].map((e,l)=>(0,t.jsx)(T.Badge,{color:"blue",size:"xs",children:e},l))})]})]},l))}):(0,t.jsx)(N.Text,{className:"text-gray-500",children:"No policies configured"})]}),(0,t.jsx)(Y.default,{loggingConfigs:tT.metadata?.logging||[],disabledCallbacks:[],variant:"card"})]})},{key:eF,label:eO[eF],children:(0,t.jsx)(eM,{teamId:e})},{key:eA,label:eO[eA],children:(0,t.jsx)(e4,{teamId:e,teamAlias:tT.team_alias,organization:ti})},{key:eP,label:eO[eP],children:(0,t.jsx)(eV,{teamData:ev,canEditTeam:tp,handleMemberDelete:e=>{e5(e),e9(!0)},setSelectedEditMember:eU,setIsEditMemberModalVisible:eB,setIsAddMemberModalVisible:eC})},{key:eD,label:eO[eD],children:(0,t.jsx)(eb,{teamId:e,accessToken:o,canEditTeam:tp})},{key:ez,label:eO[ez],children:(0,t.jsxs)(S.Card,{className:"overflow-y-auto max-h-[65vh]",children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(k.Title,{children:"Team Settings"}),tp&&!eE&&(0,t.jsx)(M.Button,{icon:(0,t.jsx)(g.EditOutlined,{className:"h-4 w-4"}),onClick:()=>eG(!0),children:"Edit Settings"})]}),eE&&eQ?(0,t.jsx)("div",{className:"p-4",children:"Loading..."}):eE?(0,t.jsxs)(I.Form,{form:ek,onFinish:tv,onValuesChange:e=>{if("disable_global_guardrails"in e){let t=!0===e.disable_global_guardrails,l=(ek.getFieldValue("guardrails")||[]).filter(e=>!eZ.has(e));ek.setFieldValue("guardrails",t?l:[...Array.from(eZ),...l])}},initialValues:{...tT,team_alias:tT.team_alias,models:tT.models,tpm_limit:tT.tpm_limit,rpm_limit:tT.rpm_limit,object_permission_search_tools:tT.object_permission?.search_tools||[],modelLimits:Array.from(new Set([...Object.keys(tT.metadata?.model_tpm_limit??{}),...Object.keys(tT.metadata?.model_rpm_limit??{})])).map(e=>({model:e,tpm:tT.metadata?.model_tpm_limit?.[e],rpm:tT.metadata?.model_rpm_limit?.[e]})),max_budget:tT.max_budget,soft_budget:tT.soft_budget,budget_duration:tT.budget_duration,team_member_tpm_limit:tT.team_member_budget_table?.tpm_limit,team_member_rpm_limit:tT.team_member_budget_table?.rpm_limit,team_member_budget:tT.team_member_budget_table?.max_budget,team_member_budget_duration:tT.team_member_budget_table?.budget_duration,guardrails:tC,policies:tT.policies||[],disable_global_guardrails:tT.metadata?.disable_global_guardrails||!1,soft_budget_alerting_emails:Array.isArray(tT.metadata?.soft_budget_alerting_emails)?tT.metadata.soft_budget_alerting_emails.join(", "):"",metadata:tT.metadata?JSON.stringify((({logging:e,secret_manager_settings:t,soft_budget_alerting_emails:l,model_tpm_limit:a,model_rpm_limit:s,allowed_passthrough_routes:i,...r})=>r)(tT.metadata),null,2):"",logging_settings:tT.metadata?.logging||[],secret_manager_settings:tT.metadata?.secret_manager_settings?JSON.stringify(tT.metadata.secret_manager_settings,null,2):"",organization_id:tT.organization_id,vector_stores:tT.object_permission?.vector_stores||[],mcp_servers:tT.object_permission?.mcp_servers||[],mcp_access_groups:tT.object_permission?.mcp_access_groups||[],mcp_servers_and_groups:{servers:tT.object_permission?.mcp_servers||[],accessGroups:tT.object_permission?.mcp_access_groups||[],toolsets:tT.object_permission?.mcp_toolsets||[]},mcp_tool_permissions:tT.object_permission?.mcp_tool_permissions||{},agents_and_groups:{agents:tT.object_permission?.agents||[],accessGroups:tT.object_permission?.agent_access_groups||[]},access_group_ids:tT.access_group_ids||[],default_team_member_models:tT.default_team_member_models||[],allowed_passthrough_routes:tT.metadata?.allowed_passthrough_routes||[]},layout:"vertical",children:[(0,t.jsx)(I.Form.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,t.jsx)(F.Input,{type:""})}),(0,t.jsx)(I.Form.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select at least one model"}],children:(0,t.jsx)(X.ModelSelect,{value:ek.getFieldValue("models")||[],onChange:e=>ek.setFieldValue("models",e),teamID:e,organizationID:ev?.team_info?.organization_id||void 0,options:{includeSpecialOptions:!0,includeUserModels:!ev?.team_info?.organization_id,showAllProxyModelsOverride:(0,u.isProxyAdminRole)(tn)&&!ev?.team_info?.organization_id},context:"team",dataTestId:"models-select"})}),(0,t.jsx)(I.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(el.default,{step:.01,precision:2,style:{width:"100%"}})}),(0,t.jsx)(I.Form.Item,{label:"Soft Budget (USD)",name:"soft_budget",children:(0,t.jsx)(el.default,{step:.01,precision:2,style:{width:"100%"}})}),(0,t.jsx)(I.Form.Item,{label:"Soft Budget Alerting Emails",name:"soft_budget_alerting_emails",tooltip:"Comma-separated email addresses to receive alerts when the soft budget is reached",children:(0,t.jsx)(F.Input,{placeholder:"example1@test.com, example2@test.com"})}),(0,t.jsxs)(y.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(v.AccordionHeader,{children:(0,t.jsx)("b",{children:"Team Member Settings"})}),(0,t.jsxs)(f.AccordionBody,{children:[(0,t.jsx)(N.Text,{className:"text-xs text-gray-500 mb-4",children:"Optional defaults applied when members join this team. All fields can be overridden per member."}),(0,t.jsx)(I.Form.Item,{label:(0,t.jsxs)("span",{children:["Default Model Access"," ",(0,t.jsx)(B.Tooltip,{title:"Optional. If set, new members can only access these models by default. Must be a subset of the team's models above. Leave empty to give all members access to all team models.",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"default_team_member_models",children:(0,t.jsx)(I.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.models!==t.models,children:({getFieldValue:e})=>{let l=e("models")||tT.models||[];return(0,t.jsx)(P.Select,{mode:"multiple",placeholder:"Leave empty — all team models accessible to every member",value:ek.getFieldValue("default_team_member_models")||[],onChange:e=>ek.setFieldValue("default_team_member_models",e),options:l.map(e=>({label:e,value:e}))})}})}),(0,t.jsx)(I.Form.Item,{label:"Default Budget (USD)",name:"team_member_budget",tooltip:"Default spend budget for each member in this team.",children:(0,t.jsx)(el.default,{step:.01,precision:2,style:{width:"100%"}})}),(0,t.jsx)(I.Form.Item,{label:"Default Budget Duration",name:"team_member_budget_duration",children:(0,t.jsx)(W,{onChange:e=>ek.setFieldValue("team_member_budget_duration",e),value:ek.getFieldValue("team_member_budget_duration")})}),(0,t.jsx)(I.Form.Item,{label:"Default Key Duration (eg: 1d, 1mo)",name:"team_member_key_duration",tooltip:"Set a limit to the duration of a team member's key. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days), 1mo (month)",children:(0,t.jsx)(C.TextInput,{placeholder:"e.g., 30d"})}),(0,t.jsx)(I.Form.Item,{label:"Default TPM Limit",name:"team_member_tpm_limit",tooltip:"Default tokens per minute limit for each member. Can be overridden per member.",children:(0,t.jsx)(el.default,{step:1,style:{width:"100%"},placeholder:"e.g., 1000"})}),(0,t.jsx)(I.Form.Item,{label:"Default RPM Limit",name:"team_member_rpm_limit",tooltip:"Default requests per minute limit for each member. Can be overridden per member.",children:(0,t.jsx)(el.default,{step:1,style:{width:"100%"},placeholder:"e.g., 100"})})]})]}),(0,t.jsx)(I.Form.Item,{label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(P.Select,{placeholder:"n/a",children:[(0,t.jsx)(P.Select.Option,{value:"24h",children:"daily"}),(0,t.jsx)(P.Select.Option,{value:"7d",children:"weekly"}),(0,t.jsx)(P.Select.Option,{value:"30d",children:"monthly"})]})}),(0,t.jsx)(I.Form.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,t.jsx)(el.default,{step:1,style:{width:"100%"}})}),(0,t.jsx)(I.Form.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,t.jsx)(el.default,{step:1,style:{width:"100%"}})}),(0,t.jsx)(I.Form.Item,{label:"Model-Specific Rate Limits",tooltip:"Set per-model TPM/RPM limits that apply across the whole team.",children:(0,t.jsx)(I.Form.List,{name:"modelLimits",children:(e,{add:l,remove:a})=>(0,t.jsxs)(t.Fragment,{children:[e.map(({key:e,name:l,...s})=>(0,t.jsxs)(D.Space,{style:{display:"flex",marginBottom:8},align:"baseline",children:[(0,t.jsx)(I.Form.Item,{...s,name:[l,"model"],rules:[{required:!0,message:"Missing model"},{validator:(e,t)=>t&&(ek.getFieldValue("modelLimits")??[]).filter(e=>e?.model===t).length>1?Promise.reject(Error("Duplicate model")):Promise.resolve()}],style:{minWidth:240},children:(0,t.jsx)(P.Select,{showSearch:!0,placeholder:"Select model",allowClear:!0,options:th.map(e=>({value:e,label:e}))})}),(0,t.jsx)(I.Form.Item,{...s,name:[l,"tpm"],rules:[{validator:async(e,t)=>{let a=(ek.getFieldValue("modelLimits")??[])[l]??{};return a.model&&null==t&&null==a.rpm?Promise.reject(Error("Set at least one of TPM or RPM")):Promise.resolve()}}],children:(0,t.jsx)(A.InputNumber,{placeholder:"TPM Limit",min:0})}),(0,t.jsx)(I.Form.Item,{...s,name:[l,"rpm"],children:(0,t.jsx)(A.InputNumber,{placeholder:"RPM Limit",min:0})}),(0,t.jsx)(_.MinusCircleOutlined,{onClick:()=>a(l),style:{color:"#ef4444"}})]},e)),(0,t.jsx)(I.Form.Item,{children:(0,t.jsx)(M.Button,{type:"dashed",onClick:()=>l(),block:!0,icon:(0,t.jsx)(x.PlusOutlined,{}),children:"Add Model Limit"})})]})})}),(0,t.jsx)(I.Form.Item,{label:"Router Settings",children:(0,t.jsx)(er.default,{ref:ts,accessToken:o||"",value:tT.router_settings?{router_settings:tT.router_settings}:void 0})}),(0,t.jsx)(I.Form.Item,{label:(0,t.jsxs)("span",{children:["Guardrails"," ",(0,t.jsx)(B.Tooltip,{title:"Select which guardrails apply to this team. Global guardrails are enabled by default — uncheck to opt out. Other guardrails are opt-in.",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"guardrails",children:(0,t.jsxs)(P.Select,{mode:"multiple",placeholder:"Select guardrails",optionLabelProp:"label",tagRender:({label:e,value:l,closable:a,onClose:s})=>{let i=eZ.has(l);return(0,t.jsxs)(L.Tag,{color:"blue",closable:a,onClose:s,onMouseDown:tk,style:{marginInlineEnd:4},children:[i&&(0,t.jsx)(h.GlobalOutlined,{style:{marginInlineEnd:4},"aria-label":"Global guardrail"}),e]})},children:[(0,t.jsx)(P.Select.OptGroup,{label:(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(h.GlobalOutlined,{style:{marginInlineEnd:4}}),"Global"]}),children:(eY?.guardrails??[]).filter(e=>e.litellm_params?.default_on).map(e=>(0,t.jsx)(P.Select.Option,{value:e.guardrail_name,label:e.guardrail_name,disabled:tg,children:e.guardrail_name},e.guardrail_name))}),(0,t.jsx)(P.Select.OptGroup,{label:"Other",children:(eY?.guardrails??[]).filter(e=>!e.litellm_params?.default_on).map(e=>(0,t.jsx)(P.Select.Option,{value:e.guardrail_name,label:e.guardrail_name,children:e.guardrail_name},e.guardrail_name))})]})}),(0,t.jsx)(I.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable all global guardrails"," ",(0,t.jsx)(B.Tooltip,{title:"Kill switch: bypass every global guardrail for this team, including any added in the future. For per-guardrail opt-out instead, use the Guardrails dropdown above.",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"disable_global_guardrails",valuePropName:"checked",children:(0,t.jsx)(z.Switch,{checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(I.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(B.Tooltip,{title:"Apply policies to this team to control guardrails and other settings",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"policies",children:(0,t.jsx)(P.Select,{mode:"tags",placeholder:"Select or enter policies",options:eX.map(e=>({value:e,label:e}))})}),(0,t.jsx)(I.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(B.Tooltip,{title:"Assign access groups to this team. Access groups control which models, MCP servers, and agents this team can use",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",children:(0,t.jsx)(G.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(I.Form.Item,{label:"Vector Stores",name:"vector_stores","aria-label":"Vector Stores",children:(0,t.jsx)(ea.default,{onChange:e=>ek.setFieldValue("vector_stores",e),value:ek.getFieldValue("vector_stores"),accessToken:o||"",placeholder:"Select vector stores"})}),(0,t.jsx)(I.Form.Item,{label:"Allowed Pass Through Routes",name:"allowed_passthrough_routes",children:(0,t.jsx)(B.Tooltip,{title:eg?ed?"":"Only proxy admins can set allowed pass through routes":"Premium feature - Upgrade to set allowed pass through routes",placement:"top",children:(0,t.jsx)(q.default,{onChange:e=>ek.setFieldValue("allowed_passthrough_routes",e),value:ek.getFieldValue("allowed_passthrough_routes"),accessToken:o||"",placeholder:"Select pass through routes",disabled:!eg||!ed})})}),(0,t.jsx)(I.Form.Item,{label:"MCP Servers / Access Groups",name:"mcp_servers_and_groups",children:(0,t.jsx)(Q.default,{onChange:e=>ek.setFieldValue("mcp_servers_and_groups",e),value:ek.getFieldValue("mcp_servers_and_groups"),accessToken:o||"",placeholder:"Select MCP servers or access groups (optional)",allowAllProxyMcpServers:ed})}),(0,t.jsx)(I.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(F.Input,{type:"hidden"})}),(0,t.jsx)(I.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.mcp_servers_and_groups!==t.mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsx)(Z.default,{accessToken:o||"",selectedServers:ek.getFieldValue("mcp_servers_and_groups")?.servers||[],toolPermissions:ek.getFieldValue("mcp_tool_permissions")||{},onChange:e=>ek.setFieldsValue({mcp_tool_permissions:e})})})}),(0,t.jsx)(I.Form.Item,{label:"Agents / Access Groups",name:"agents_and_groups",children:(0,t.jsx)(K.default,{onChange:e=>ek.setFieldValue("agents_and_groups",e),value:ek.getFieldValue("agents_and_groups"),accessToken:o||"",placeholder:"Select agents or access groups (optional)"})}),(0,t.jsxs)(y.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(v.AccordionHeader,{children:(0,t.jsx)("b",{children:"Search Tool Settings"})}),(0,t.jsx)(f.AccordionBody,{children:(0,t.jsx)(I.Form.Item,{label:"Allowed Search Tools",name:"object_permission_search_tools",tooltip:"Select which search tools this team can access. Leave empty to allow all search tools.",children:(0,t.jsx)(es,{onChange:e=>ek.setFieldValue("object_permission_search_tools",e),value:ek.getFieldValue("object_permission_search_tools"),accessToken:o||"",placeholder:"Select search tools (optional, empty = all allowed)"})})})]}),(0,t.jsx)(I.Form.Item,{label:"Organization",name:"organization_id",children:(0,t.jsx)(P.Select,{allowClear:!0,placeholder:"Select an organization",showSearch:!0,optionFilterProp:"label",options:td.map(e=>({value:e.organization_id,label:e.organization_alias||e.organization_id}))})}),(0,t.jsx)(I.Form.Item,{label:"Logging Settings",name:"logging_settings",children:(0,t.jsx)(ei.default,{value:ek.getFieldValue("logging_settings"),onChange:e=>ek.setFieldValue("logging_settings",e)})}),(0,t.jsx)(I.Form.Item,{label:"Secret Manager Settings",name:"secret_manager_settings",help:eg?"Enter secret manager configuration as a JSON object.":"Premium feature - Upgrade to manage secret manager settings.",rules:[{validator:async(e,t)=>{if(!t)return Promise.resolve();try{return JSON.parse(t),Promise.resolve()}catch(e){return Promise.reject(Error("Please enter valid JSON"))}}}],children:(0,t.jsx)(F.Input.TextArea,{rows:6,placeholder:'{"namespace": "admin", "mount": "secret", "path_prefix": "litellm"}',disabled:!eg})}),(0,t.jsx)(I.Form.Item,{label:"Metadata",name:"metadata",children:(0,t.jsx)(F.Input.TextArea,{rows:10})}),(0,t.jsx)("div",{className:"sticky z-10 bg-white p-4 pr-0 border-t border-gray-200 -bottom-6 -inset-x-6",children:(0,t.jsxs)("div",{className:"flex justify-end items-center gap-2",children:[(0,t.jsx)(M.Button,{onClick:()=>eG(!1),disabled:tl,children:"Cancel"}),(0,t.jsx)(M.Button,{icon:(0,t.jsx)(b.SaveOutlined,{className:"h-4 w-4"}),type:"primary",htmlType:"submit",loading:tl,children:"Save Changes"})]})})]}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Team Name"}),(0,t.jsx)("div",{children:tT.team_alias})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Team ID"}),(0,t.jsx)("div",{className:"font-mono",children:tT.team_id})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Created At"}),(0,t.jsx)("div",{children:new Date(tT.created_at).toLocaleString()})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Models"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:tT.models.map((e,l)=>(0,t.jsx)(T.Badge,{color:"red",children:e},l))})]}),tT.default_team_member_models&&tT.default_team_member_models.length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Default Member Models"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:tT.default_team_member_models.map((e,l)=>(0,t.jsx)(T.Badge,{color:"blue",children:e},l))})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Rate Limits"}),(0,t.jsxs)("div",{children:["TPM: ",tT.tpm_limit||"Unlimited"]}),(0,t.jsxs)("div",{children:["RPM: ",tT.rpm_limit||"Unlimited"]}),(ej=tT.metadata?.model_tpm_limit??{},ey=tT.metadata?.model_rpm_limit??{},0===(ef=Array.from(new Set([...Object.keys(ej),...Object.keys(ey)]))).length?null:(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsx)(N.Text,{className:"text-gray-500",children:"Per-model limits:"}),ef.map(e=>(0,t.jsxs)("div",{className:"text-xs ml-2",children:[e,": TPM ",ej[e]??"—",", RPM ",ey[e]??"—"]},e))]}))]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Team Budget"}),(0,t.jsxs)("div",{children:["Max Budget:"," ",null!==tT.max_budget?`$${(0,m.formatNumberWithCommas)(tT.max_budget,4)}`:"No Limit"]}),(0,t.jsxs)("div",{children:["Soft Budget:"," ",null!==tT.soft_budget&&void 0!==tT.soft_budget?`$${(0,m.formatNumberWithCommas)(tT.soft_budget,4)}`:"No Limit"]}),(0,t.jsxs)("div",{children:["Budget Reset: ",tT.budget_duration||"Never"]}),tT.metadata?.soft_budget_alerting_emails&&Array.isArray(tT.metadata.soft_budget_alerting_emails)&&tT.metadata.soft_budget_alerting_emails.length>0&&(0,t.jsxs)("div",{children:["Soft Budget Alerting Emails: ",tT.metadata.soft_budget_alerting_emails.join(", ")]})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(N.Text,{className:"font-medium",children:["Team Member Settings"," ",(0,t.jsx)(B.Tooltip,{title:"These are limits on individual team members",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),(0,t.jsxs)("div",{children:["Max Budget: ",tT.team_member_budget_table?.max_budget||"No Limit"]}),(0,t.jsxs)("div",{children:["Budget Duration: ",tT.team_member_budget_table?.budget_duration||"No Limit"]}),(0,t.jsxs)("div",{children:["Key Duration: ",tT.metadata?.team_member_key_duration||"No Limit"]}),(0,t.jsxs)("div",{children:["TPM Limit: ",tT.team_member_budget_table?.tpm_limit||"No Limit"]}),(0,t.jsxs)("div",{children:["RPM Limit: ",tT.team_member_budget_table?.rpm_limit||"No Limit"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Router Settings"}),tT.router_settings&&Object.values(tT.router_settings).some(e=>null!=e&&""!==e&&!(Array.isArray(e)&&0===e.length))?(0,t.jsxs)("div",{className:"mt-1 space-y-1",children:[tT.router_settings.routing_strategy&&(0,t.jsxs)("div",{children:["Routing Strategy: ",(0,t.jsx)(T.Badge,{color:"blue",children:tT.router_settings.routing_strategy})]}),null!=tT.router_settings.num_retries&&(0,t.jsxs)("div",{children:["Number of Retries: ",tT.router_settings.num_retries]}),null!=tT.router_settings.allowed_fails&&(0,t.jsxs)("div",{children:["Allowed Failures: ",tT.router_settings.allowed_fails]}),null!=tT.router_settings.cooldown_time&&(0,t.jsxs)("div",{children:["Cooldown Time: ",tT.router_settings.cooldown_time,"s"]}),null!=tT.router_settings.timeout&&(0,t.jsxs)("div",{children:["Timeout: ",tT.router_settings.timeout,"s"]}),null!=tT.router_settings.retry_after&&(0,t.jsxs)("div",{children:["Retry After: ",tT.router_settings.retry_after,"s"]}),tT.router_settings.fallbacks&&Array.isArray(tT.router_settings.fallbacks)&&tT.router_settings.fallbacks.length>0&&(0,t.jsxs)("div",{children:["Fallbacks: ",tT.router_settings.fallbacks.length," configured"]}),tT.router_settings.enable_tag_filtering&&(0,t.jsx)("div",{children:"Tag Filtering: Enabled"})]}):(0,t.jsx)("div",{className:"text-gray-400",children:"No router settings configured"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Organization ID"}),(0,t.jsx)("div",{children:tT.organization_id})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Status"}),(0,t.jsx)(T.Badge,{color:tT.blocked?"red":"green",children:tT.blocked?"Blocked":"Active"})]}),(0,t.jsx)(et.default,{objectPermission:tT.object_permission,variant:"inline",className:"pt-4 border-t border-gray-200",accessToken:o}),(0,t.jsx)(J,{globalGuardrailNames:eZ,teamGuardrails:Array.isArray(tT.metadata?.guardrails)?tT.metadata.guardrails:[],optedOutGlobalGuardrails:Array.isArray(tT.metadata?.opted_out_global_guardrails)?tT.metadata.opted_out_global_guardrails:[],killSwitchOn:tS,variant:"inline",className:"pt-4 border-t border-gray-200"}),(0,t.jsx)(Y.default,{loggingConfigs:tT.metadata?.logging||[],disabledCallbacks:[],variant:"inline",className:"pt-4 border-t border-gray-200"}),tT.metadata?.secret_manager_settings&&(0,t.jsxs)("div",{className:"pt-4 border-t border-gray-200",children:[(0,t.jsx)(N.Text,{className:"font-medium",children:"Secret Manager Settings"}),(0,t.jsx)("pre",{className:"mt-2 bg-gray-50 p-3 rounded-sm text-xs overflow-x-auto",children:JSON.stringify(tT.metadata.secret_manager_settings,null,2)})]})]})]})}].filter(e=>t_.includes(e.key))}),(0,t.jsx)(en.default,{visible:eL,onCancel:()=>eB(!1),onSubmit:ty,initialData:eR,mode:"edit",config:{title:"Edit Member",showEmail:!0,showUserId:!0,roleOptions:[{label:"Admin",value:"admin"},{label:"User",value:"user"}],additionalFields:[{name:"max_budget_in_team",label:(0,t.jsxs)("span",{children:["Team Member Budget (USD)"," ",(0,t.jsx)(B.Tooltip,{title:"Maximum amount in USD this member can spend within this team. This is separate from any global user budget limits",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),type:"numerical",step:.01,min:0,placeholder:"Budget limit for this member within this team"},{name:"budget_duration",label:(0,t.jsxs)("span",{children:["Budget Reset Period"," ",(0,t.jsx)(B.Tooltip,{title:"How often this member's budget resets within the team. Leave unset and the budget never resets.",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),type:"budget-duration"},{name:"tpm_limit",label:(0,t.jsxs)("span",{children:["Team Member TPM Limit"," ",(0,t.jsx)(B.Tooltip,{title:"Maximum tokens per minute this member can use within this team. This is separate from any global user TPM limit",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),type:"numerical",step:1,min:0,placeholder:"Tokens per minute limit for this member in this team"},{name:"rpm_limit",label:(0,t.jsxs)("span",{children:["Team Member RPM Limit"," ",(0,t.jsx)(B.Tooltip,{title:"Maximum requests per minute this member can make within this team. This is separate from any global user RPM limit",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),type:"numerical",step:1,min:0,placeholder:"Requests per minute limit for this member in this team"},{name:"allowed_models",label:(0,t.jsxs)("span",{children:["Allowed Models"," ",(0,t.jsx)(B.Tooltip,{title:"Models this member can access within this team. Leave empty to inherit all team models.",children:(0,t.jsx)(p.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),type:"multi-select",options:(tT.models||[]).map(e=>({label:e,value:e})),placeholder:"Leave empty to inherit all team models"}]}}),(0,t.jsx)(i.default,{isVisible:eN,onCancel:()=>eC(!1),onSubmit:tj,accessToken:o,teamId:e}),(0,t.jsx)($.default,{isOpen:e7,title:"Delete Team Member",alertMessage:"Removing team members will also delete any keys created by or created for this member.",message:"Are you sure you want to remove this member from the team? This action cannot be undone.",resourceInformationTitle:"Team Member Information",resourceInformation:[{label:"User ID",value:e8?.user_id,code:!0},{label:"Email",value:e8?.user_email},{label:"Role",value:e8?.role}],onCancel:()=>{e9(!1),e5(null)},onOk:tf,confirmLoading:te})]})}],56567)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0nht59ws0elww.js b/litellm/proxy/_experimental/out/_next/static/chunks/0nht59ws0elww.js new file mode 100644 index 00000000000..d00758f6183 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0nht59ws0elww.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,337822,e=>{"use strict";var t,n=e.i(843476);e.s([],158421),e.i(158421);var i=e.i(271645),r=e.i(956789),a=e.i(17989),o=e.i(46420);e.i(247167);var s=e.i(733332);let l=i.createContext(void 0);function u(e){let t=i.useContext(l);if(void 0===t&&!e)throw Error((0,s.default)(47));return t}var d=e.i(174080),c=e.i(301252),p=e.i(616269),f=e.i(439957),g=e.i(56434),h=e.i(264111),v=e.i(116786),m=e.i(990627),S=e.i(638396);let b={...v.popupStoreSelectors,disabled:(0,p.createSelector)(e=>e.disabled),instantType:(0,p.createSelector)(e=>e.instantType),openMethod:(0,p.createSelector)(e=>e.openMethod),openChangeReason:(0,p.createSelector)(e=>e.openChangeReason),modal:(0,p.createSelector)(e=>e.modal),focusManagerModal:(0,p.createSelector)(e=>e.focusManagerModal),stickIfOpen:(0,p.createSelector)(e=>e.stickIfOpen),titleElementId:(0,p.createSelector)(e=>e.titleElementId),descriptionElementId:(0,p.createSelector)(e=>e.descriptionElementId),openOnHover:(0,p.createSelector)(e=>e.openOnHover),closeDelay:(0,p.createSelector)(e=>e.closeDelay),hasViewport:(0,p.createSelector)(e=>e.hasViewport)};class R extends c.ReactStore{constructor(e,t,n=!1){const r={...(0,v.createInitialPopupStoreState)(),disabled:!1,modal:!1,focusManagerModal:!1,instantType:void 0,openMethod:null,openChangeReason:null,titleElementId:void 0,descriptionElementId:void 0,stickIfOpen:!0,nested:!1,openOnHover:!1,closeDelay:0,hasViewport:!1,...e},a=new m.PopupTriggerMap;r.open&&e?.mounted===void 0&&(r.mounted=!0),r.floatingRootContext=(0,v.createPopupFloatingRootContext)(a,t,n),super(r,{popupRef:i.createRef(),backdropRef:i.createRef(),internalBackdropRef:i.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerFocusTargetRef:i.createRef(),beforeContentFocusGuardRef:i.createRef(),stickIfOpenTimeout:new f.Timeout,triggerElements:a},b)}setOpen=(e,t)=>{let n=t.reason===g.REASONS.triggerHover,i=t.reason===g.REASONS.triggerPress&&0===t.event.detail,r=!e&&(t.reason===g.REASONS.escapeKey||null==t.reason),a=(0,h.attachPreventUnmountOnClose)(t),o=this.select("activeTriggerId");if(e||t.reason!==g.REASONS.closePress||null!=t.trigger||null==o||(t.trigger=this.context.triggerElements.getById(o)??this.select("activeTriggerElement")??void 0),this.context.onOpenChange?.(e,t),t.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(e,t);let s=()=>{let n={open:e,openChangeReason:t.reason};(0,h.setPopupOpenState)(n,e,t.trigger,a()),this.update(n)};n?(this.set("stickIfOpen",!0),this.context.stickIfOpenTimeout.start(S.PATIENT_CLICK_THRESHOLD,()=>{this.set("stickIfOpen",!1)}),d.flushSync(s)):s(),i||r?this.set("instantType",i?"click":"dismiss"):t.reason===g.REASONS.focusOut?this.set("instantType","focus"):this.set("instantType",void 0)};static useStore(e,t){let{store:n,internalStore:r}=(0,h.usePopupStore)(e,(e,n)=>new R(t,e,n));return i.useEffect(()=>r?.disposeEffect(),[r]),n}disposeEffect=()=>this.context.stickIfOpenTimeout.disposeEffect()}var C=e.i(675606),y=e.i(176782);function E({props:e}){let{children:t,open:r,defaultOpen:a=!1,onOpenChange:s,onOpenChangeComplete:u,modal:d=!1,handle:c,triggerId:p,defaultTriggerId:f=null}=e,v=R.useStore(c?.store,{modal:d,open:a,openProp:r,activeTriggerId:f,triggerIdProp:p});(0,h.useInitialOpenSync)(v,r,a,f),v.useControlledProp("openProp",r),v.useControlledProp("triggerIdProp",p);let m=v.useState("open"),S=v.useState("mounted"),b=v.useState("payload"),y=null!=(0,o.useFloatingParentNodeId)();v.useContextCallback("onOpenChange",s),v.useContextCallback("onOpenChangeComplete",u),(0,h.usePopupRootSync)(v,m),(0,h.useImplicitActiveTrigger)(v);let{forceUnmount:P}=(0,h.useOpenStateTransitions)(m,v,()=>{v.update({stickIfOpen:!0,openChangeReason:null})});v.useSyncedValues({modal:d,nested:y}),i.useEffect(()=>{m||v.context.stickIfOpenTimeout.clear()},[v,m]);let O=i.useCallback(()=>{v.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction))},[v]);i.useImperativeHandle(e.actionsRef,()=>({unmount:P,close:O}),[P,O]);let k=m||S,I=i.useMemo(()=>({store:v}),[v]);return(0,n.jsxs)(l.Provider,{value:I,children:[k&&(0,n.jsx)(x,{store:v,modal:d}),"function"==typeof t?t({payload:b}):t]})}function x({store:e,modal:t}){let n=e.useState("floatingRootContext"),o=(0,a.useDismiss)(n,{outsidePressEvent:{mouse:"trap-focus"===t?"sloppy":"intentional",touch:"sloppy"}}),s=o.reference??r.EMPTY_OBJECT,l=o.trigger??r.EMPTY_OBJECT,u=i.useMemo(()=>(0,y.mergeProps)(h.FOCUSABLE_POPUP_PROPS,o.floating),[o.floating]);return(0,h.usePopupInteractionProps)(e,{activeTriggerProps:s,inactiveTriggerProps:l,popupProps:u}),null}var P=e.i(540886),O=e.i(405005),k=e.i(552245),I=e.i(650316),w=e.i(385689),T=e.i(872135),M=e.i(788015),A=e.i(152535),F=e.i(346570),j=e.i(32199);let N=i.forwardRef(function(e,t){let{render:r,className:a,style:o,disabled:l=!1,nativeButton:d=!0,handle:c,payload:p,openOnHover:f=!1,delay:v=300,closeDelay:m=0,id:b,...R}=e,C=u(!0),y=c?.store??C?.store;if(!y)throw Error((0,s.default)(74));let E=(0,M.useBaseUiId)(b),x=y.useState("isTriggerActive",E),N=y.useState("floatingRootContext"),D=y.useState("isOpenedByTrigger",E),B=y.useState("triggerPopupId",E),H=i.useRef(null),{registerTrigger:L,isMountedByThisTrigger:V}=(0,h.useTriggerDataForwarding)(E,H,y,{payload:p,disabled:l,openOnHover:f,closeDelay:m}),z=y.useState("openChangeReason"),U=y.useState("stickIfOpen"),K=y.useState("openMethod"),_=y.useState("focusManagerModal"),W=(0,T.useHoverReferenceInteraction)(N,{enabled:!l&&null!=N&&f&&("touch"!==K||z!==g.REASONS.triggerPress),mouseOnly:!0,move:!1,handleClose:(0,I.safePolygon)(),restMs:v,delay:{close:m},triggerElementRef:H,isActiveTrigger:x,isClosing:()=>"ending"===y.select("transitionStatus")}),G=(0,w.useClick)(N,{enabled:null!=N,stickIfOpen:U}),q=(0,j.useOpenMethodTriggerProps)(()=>y.select("open"),e=>{y.set("openMethod",e)}),Y=y.useState("triggerProps",V),{getButtonProps:J,buttonRef:$}=(0,P.useButton)({disabled:l,native:d}),{preFocusGuardRef:Q,handlePreFocusGuardFocus:X,handleFocusTargetFocus:Z}=(0,F.useTriggerFocusGuards)(y,H),ee=(0,k.useRenderElement)("button",e,{state:{disabled:l,open:D},ref:[$,t,L,H],props:[G.reference,W,Y,q,{[S.CLICK_TRIGGER_IDENTIFIER]:"",id:E,"aria-haspopup":"dialog","aria-expanded":D,"aria-controls":B},R,J],stateAttributesMapping:{open:e=>e&&z===g.REASONS.triggerPress?O.pressableTriggerOpenStateMapping.open(e):O.triggerOpenStateMapping.open(e)}});return V&&!_?(0,n.jsxs)(i.Fragment,{children:[(0,n.jsx)(A.FocusGuard,{ref:Q,onFocus:X}),(0,n.jsx)(i.Fragment,{children:ee},E),(0,n.jsx)(A.FocusGuard,{ref:y.context.triggerFocusTargetRef,onFocus:Z})]}):(0,n.jsx)(i.Fragment,{children:ee},E)});var D=e.i(726674);let B=i.createContext(void 0),H=i.forwardRef(function(e,t){let{keepMounted:i=!1,...r}=e,{store:a}=u();return a.useState("mounted")||i?(0,n.jsx)(B.Provider,{value:i,children:(0,n.jsx)(D.FloatingPortal,{ref:t,...r})}):null});var L=e.i(144394),V=e.i(146376);let z=i.createContext(void 0);function U(){let e=i.useContext(z);if(!e)throw Error((0,s.default)(46));return e}var K=e.i(329365),_=e.i(426),W=e.i(222640),G=e.i(360495),q=e.i(789579),Y=e.i(33383);let J=i.forwardRef(function(e,t){let{render:r,className:a,style:l,anchor:d,positionMethod:c="absolute",side:p="bottom",align:f="center",sideOffset:h=0,alignOffset:v=0,collisionBoundary:m="clipping-ancestors",collisionPadding:b=5,arrowPadding:R=5,sticky:C=!1,disableAnchorTracking:y=!1,collisionAvoidance:E=S.POPUP_COLLISION_AVOIDANCE,...x}=e,{store:P}=u(),O=function(){let e=i.useContext(B);if(void 0===e)throw Error((0,s.default)(45));return e}(),k=(0,o.useFloatingNodeId)(),I=P.useState("floatingRootContext"),w=P.useState("mounted"),T=P.useState("open"),M=P.useState("openChangeReason"),A=P.useState("activeTriggerElement"),F=P.useState("modal"),j=P.useState("openMethod"),N=P.useState("positionerElement"),D=P.useState("instantType"),H=P.useState("transitionStatus"),U=P.useState("hasViewport"),J=i.useRef(null),$=(0,W.useAnimationsFinished)(N,!1,!1),Q=(0,K.useAnchorPositioning)({anchor:d,floatingRootContext:I,positionMethod:c,mounted:w,side:p,sideOffset:h,align:f,alignOffset:v,arrowPadding:R,collisionBoundary:m,collisionPadding:b,sticky:C,disableAnchorTracking:y,keepMounted:O,nodeId:k,collisionAvoidance:E,adaptiveOrigin:U?G.adaptiveOrigin:void 0}),X=I.useState("domReferenceElement");(0,V.useIsoLayoutEffect)(()=>{let e=J.current;if(X&&(J.current=X),e&&X&&X!==e){P.set("instantType",void 0);let e=new AbortController;return $(()=>{P.set("instantType","trigger-change")},e.signal),()=>{e.abort()}}},[X,$,P]),(0,Y.useAnchoredPopupScrollLock)(T&&!0===F&&M!==g.REASONS.triggerHover,"touch"===j,N,A);let Z=i.useCallback(e=>{P.set("positionerElement",e)},[P]),ee={open:T,side:Q.side,align:Q.align,anchorHidden:Q.anchorHidden,instant:D},et=(0,q.usePositioner)(e,ee,{styles:Q.positionerStyles,transitionStatus:H,props:x,refs:[t,Z],hidden:!w,inert:!T});return(0,n.jsxs)(z.Provider,{value:Q,children:[w&&!0===F&&M!==g.REASONS.triggerHover&&(0,n.jsx)(_.InternalBackdrop,{ref:P.context.internalBackdropRef,inert:(0,L.inertValue)(!T),cutout:A}),(0,n.jsx)(o.FloatingNode,{id:k,children:et})]})});var $=e.i(229315),Q=e.i(61487),X=e.i(431157),Z=e.i(209407),ee=e.i(137584),et=e.i(673327),en=e.i(96533),ei=e.i(815982),er=e.i(667865);let ea=i.createContext(void 0);function eo(e){let{value:t,children:i}=e;return(0,n.jsx)(ea.Provider,{value:t,children:i})}let es={...O.popupStateMapping,...Z.transitionStatusMapping},el=i.forwardRef(function(e,t){let{render:r,className:a,style:o,initialFocus:s,finalFocus:l,...d}=e,{store:c}=u(),p=U(),f=null!=(0,en.useToolbarRootContext)(!0),{context:v,hasClosePart:m}=function(){let[e,t]=i.useState(0),n=(0,er.useStableCallback)(()=>(t(e=>e+1),()=>{t(e=>Math.max(0,e-1))}));return{context:i.useMemo(()=>({register:n}),[n]),hasClosePart:e>0}}(),S=c.useState("open"),b=c.useState("openMethod"),R=c.useState("instantType"),C=c.useState("transitionStatus"),y=c.useState("popupProps"),E=c.useState("titleElementId"),x=c.useState("descriptionElementId"),P=c.useState("modal"),O=c.useState("mounted"),I=c.useState("openChangeReason"),w=c.useState("activeTriggerElement"),T=c.useState("floatingRootContext"),M=T.useState("floatingId"),A=c.useState("disabled"),F=c.useState("openOnHover"),j=c.useState("closeDelay"),N=d.id??M;(0,ee.useOpenChangeComplete)({open:S,ref:c.context.popupRef,onComplete(){S&&c.context.onOpenChangeComplete?.(!0)}}),(0,X.useHoverFloatingInteraction)(T,{enabled:F&&!A,closeDelay:j});let D=void 0===s?(0,h.createDefaultInitialFocus)(c.context.popupRef):s,B=!1!==P&&m;c.useSyncedValue("focusManagerModal",B);let H=i.useCallback(e=>{c.set("popupElement",e)},[c]),L={open:S,side:p.side,align:p.align,instant:R,transitionStatus:C},V=(0,k.useRenderElement)("div",e,{state:L,ref:[t,c.context.popupRef,H],props:[y,{id:N,role:"dialog",...h.FOCUSABLE_POPUP_PROPS,"aria-labelledby":E,"aria-describedby":x,onKeyDown(e){f&&et.COMPOSITE_KEYS.has(e.key)&&e.stopPropagation()}},(0,ei.getDisabledMountTransitionStyles)(C),d],stateAttributesMapping:es});return(0,n.jsx)(Q.FloatingFocusManager,{context:T,openInteractionType:b,modal:B,disabled:!O||I===g.REASONS.triggerHover,initialFocus:D,returnFocus:l,restoreFocus:"popup",previousFocusableElement:(0,$.isHTMLElement)(w)?w:void 0,nextFocusableElement:c.context.triggerFocusTargetRef,beforeContentFocusGuardRef:c.context.beforeContentFocusGuardRef,children:(0,n.jsx)(eo,{value:v,children:V})})}),eu=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=o.useState("open"),{arrowRef:l,side:d,align:c,arrowUncentered:p,arrowStyles:f}=U();return(0,k.useRenderElement)("div",e,{state:{open:s,side:d,align:c,uncentered:p},ref:[t,l],props:[{style:f,"aria-hidden":!0},a],stateAttributesMapping:O.popupStateMapping})}),ed={...O.popupStateMapping,...Z.transitionStatusMapping},ec=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=o.useState("open"),l=o.useState("mounted"),d=o.useState("transitionStatus"),c=o.useState("openChangeReason");return(0,k.useRenderElement)("div",e,{state:{open:s,transitionStatus:d},ref:[o.context.backdropRef,t],props:[{role:"presentation",hidden:!l,style:{pointerEvents:c===g.REASONS.triggerHover?"none":void 0,userSelect:"none",WebkitUserSelect:"none"}},a],stateAttributesMapping:ed})}),ep=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=(0,M.useBaseUiId)(a.id);return o.useSyncedValueWithCleanup("titleElementId",s),(0,k.useRenderElement)("h2",e,{ref:t,props:[{id:s},a]})}),ef=i.forwardRef(function(e,t){let{render:n,className:i,style:r,...a}=e,{store:o}=u(),s=(0,M.useBaseUiId)(a.id);return o.useSyncedValueWithCleanup("descriptionElementId",s),(0,k.useRenderElement)("p",e,{ref:t,props:[{id:s},a]})}),eg=i.forwardRef(function(e,t){let n,{render:r,className:a,style:o,disabled:s=!1,nativeButton:l=!0,...d}=e,{buttonRef:c,getButtonProps:p}=(0,P.useButton)({disabled:s,focusableWhenDisabled:!1,native:l}),{store:f}=u();return n=i.useContext(ea),(0,V.useIsoLayoutEffect)(()=>n?.register(),[n]),(0,k.useRenderElement)("button",e,{ref:[t,c],props:[{onClick(e){f.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.closePress,e.nativeEvent))}},d,p]})}),eh=((t={}).popupWidth="--popup-width",t.popupHeight="--popup-height",t);var ev=e.i(818390);let em={activationDirection:e=>e?{"data-activation-direction":e}:null},eS=i.forwardRef(function(e,t){let{render:n,className:i,style:r,children:a,...o}=e,{store:s}=u(),{side:l}=U(),d=s.useState("instantType"),{children:c,state:p}=(0,ev.usePopupViewport)({store:s,side:l,cssVars:eh,children:a}),f={activationDirection:p.activationDirection,transitioning:p.transitioning,instant:d};return(0,k.useRenderElement)("div",e,{state:f,ref:t,props:[o,{children:c}],stateAttributesMapping:em})});class eb{constructor(){this.store=new R}open(e){let t=e?this.store.context.triggerElements.getById(e)??void 0:void 0;if(e&&!t)throw Error((0,s.default)(80,e));this.store.setOpen(!0,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction,void 0,t))}close(){this.store.setOpen(!1,(0,C.createChangeEventDetails)(g.REASONS.imperativeAction,void 0,void 0))}get isOpen(){return this.store.select("open")}}e.s(["Arrow",0,eu,"Backdrop",0,ec,"Close",0,eg,"Description",0,ef,"Handle",0,eb,"Popup",0,el,"Portal",0,H,"Positioner",0,J,"Root",0,function(e){return u(!0)?(0,n.jsx)(E,{props:e}):(0,n.jsx)(o.FloatingTree,{children:(0,n.jsx)(E,{props:e})})},"Title",0,ep,"Trigger",0,N,"Viewport",0,eS,"createHandle",0,function(){return new eb}],466914);var eR=e.i(466914),eR=eR,eC=e.i(115504);e.s(["Popover",0,function({...e}){return(0,n.jsx)(eR.Root,{"data-slot":"popover",...e})},"PopoverContent",0,function({className:e,align:t="center",alignOffset:i=0,side:r="bottom",sideOffset:a=4,...o}){return(0,n.jsx)(eR.Portal,{children:(0,n.jsx)(eR.Positioner,{align:t,alignOffset:i,side:r,sideOffset:a,className:"isolate z-50",children:(0,n.jsx)(eR.Popup,{"data-slot":"popover-content",className:(0,eC.cn)("z-50 flex w-72 origin-(--transform-origin) flex-col gap-4 rounded-md bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",e),...o})})})},"PopoverTrigger",0,function({...e}){return(0,n.jsx)(eR.Trigger,{"data-slot":"popover-trigger",...e})}],337822)},699375,e=>{"use strict";var t,n=e.i(843476);e.s([],924305),e.i(924305),e.i(247167);var i=e.i(271645),r=e.i(951437),a=e.i(828918),o=e.i(146376),s=e.i(502077),l=e.i(956789),u=e.i(333848),d=e.i(552245),c=e.i(176782),p=e.i(788015),f=e.i(540886),g=e.i(733332);let h=i.createContext(void 0);var v=e.i(875812);let m=((t={}).checked="data-checked",t.unchecked="data-unchecked",t.disabled="data-disabled",t.readonly="data-readonly",t.required="data-required",t.valid="data-valid",t.invalid="data-invalid",t.touched="data-touched",t.dirty="data-dirty",t.filled="data-filled",t.focused="data-focused",t),S={...v.fieldValidityMapping,checked:e=>e?{[m.checked]:""}:{[m.unchecked]:""}};var b=e.i(469690),R=e.i(381104),C=e.i(884708),y=e.i(247778),E=e.i(538489),x=e.i(675606),P=e.i(56434),O=e.i(606039);let k=i.forwardRef(function(e,t){let{checked:g,className:v,defaultChecked:m,"aria-labelledby":k,form:I,id:w,inputRef:T,name:M,nativeButton:A=!1,onCheckedChange:F,readOnly:j=!1,required:N=!1,disabled:D=!1,render:B,uncheckedValue:H,value:L,style:V,...z}=e,{clearErrors:U}=(0,C.useFormContext)(),{state:K,setTouched:_,setDirty:W,validityData:G,setFilled:q,setFocused:Y,validationMode:J,disabled:$,name:Q,validation:X}=(0,b.useFieldRootContext)(),{labelId:Z}=(0,y.useLabelableContext)(),ee=$||D,et=Q??M,en=i.useRef(null),ei=(0,a.useMergedRefs)(en,T,X.inputRef),er=i.useRef(null),ea=(0,p.useBaseUiId)(),eo=(0,E.useLabelableId)({id:w,implicit:!1,controlRef:er}),es=A?void 0:eo,[el,eu]=(0,r.useControlled)({controlled:g,default:!!m,name:"Switch",state:"checked"});(0,R.useRegisterFieldControl)(er,ea,el,void 0,!ee,M),(0,o.useIsoLayoutEffect)(()=>{en.current&&q(en.current.checked)},[en,q]),(0,O.useValueChanged)(el,()=>{U(et),W(el!==G.initialValue),q(el),X.change(el)});let{getButtonProps:ed,buttonRef:ec}=(0,f.useButton)({disabled:ee,native:A}),ep=function(e,t,n,r=!0,a){let[s,l]=i.useState(),u=(0,p.useBaseUiId)(a?`${a}-label`:void 0),d=e??t??s;return(0,o.useIsoLayoutEffect)(()=>{let i=e||t||!r?void 0:function(e,t){let n=function(e){if(!e)return;let t=e.parentElement;if(t&&"LABEL"===t.tagName)return t;let n=e.id;if(n){let t=e.nextElementSibling;if(t&&t.htmlFor===n)return t}let i=e.labels;return i&&i[0]}(e);if(n)return!n.id&&t&&(n.id=t),n.id||void 0}(n.current,u);s!==i&&l(i)}),d}(k,Z,en,!A,es),ef=(0,c.mergeProps)({checked:el,disabled:ee,form:I,id:es,name:et,required:N,style:et?s.visuallyHiddenInput:s.visuallyHidden,tabIndex:-1,type:"checkbox","aria-hidden":!0,ref:ei,onChange(e){if(e.nativeEvent.defaultPrevented)return;if(j)return void e.preventDefault();let t=e.currentTarget.checked,n=(0,x.createChangeEventDetails)(P.REASONS.none,e.nativeEvent);F?.(t,n),n.isCanceled||eu(t)},onFocus(){er.current?.focus()}},e=>X.getValidationProps(ee,e),void 0!==L?{value:L}:l.EMPTY_OBJECT),eg=i.useMemo(()=>({...K,checked:el,disabled:ee,readOnly:j,required:N}),[K,el,ee,j,N]),eh=(0,d.useRenderElement)("span",e,{state:eg,ref:[t,er,ec],props:[{id:A?eo:ea,role:"switch","aria-checked":el,"aria-readonly":j||void 0,"aria-required":N||void 0,"aria-labelledby":ep,onFocus(){ee||Y(!0)},onBlur(){let e=en.current;e&&!ee&&(_(!0),Y(!1),"onBlur"===J&&X.commit(e.checked))},onClick(e){if(j||ee)return;e.preventDefault();let t=en.current;t&&t.dispatchEvent(new((0,u.ownerWindow)(t)).PointerEvent("click",{bubbles:!0,shiftKey:e.shiftKey,ctrlKey:e.ctrlKey,altKey:e.altKey,metaKey:e.metaKey}))}},z,ed,e=>X.getValidationProps(ee,e)],stateAttributesMapping:S});return(0,n.jsxs)(h.Provider,{value:eg,children:[eh,!el&&et&&void 0!==H&&(0,n.jsx)("input",{type:"hidden",form:I,name:et,value:H,disabled:ee}),(0,n.jsx)("input",{...ef,suppressHydrationWarning:!0})]})}),I=i.forwardRef(function(e,t){let{render:n,className:r,style:a,...o}=e,s=function(){let e=i.useContext(h);if(void 0===e)throw Error((0,g.default)(63));return e}();return(0,d.useRenderElement)("span",e,{state:s,ref:t,stateAttributesMapping:S,props:o})});e.s(["Root",0,k,"Thumb",0,I],450994);var w=e.i(450994),w=w,T=e.i(115504);e.s(["Switch",0,function({className:e,size:t="default",...i}){return(0,n.jsx)(w.Root,{"data-slot":"switch","data-size":t,className:(0,T.cn)("peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",e),...i,children:(0,n.jsx)(w.Thumb,{"data-slot":"switch-thumb",className:"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"})})}],699375)},204258,e=>{"use strict";var t,n,i,r=e.i(843476);e.s([],958842),e.i(958842),e.i(247167);var a=e.i(271645),o=e.i(667865),s=e.i(552245),l=e.i(951437),u=e.i(788015),d=e.i(675606),c=e.i(56434),p=e.i(223910),f=e.i(733332);let g=a.createContext(void 0);function h(){let e=a.useContext(g);if(void 0===e)throw Error((0,f.default)(15));return e}var v=e.i(209407);let m=((t={}).open="data-open",t.closed="data-closed",t[t.startingStyle=v.TransitionStatusDataAttributes.startingStyle]="startingStyle",t[t.endingStyle=v.TransitionStatusDataAttributes.endingStyle]="endingStyle",t),S=((n={}).panelOpen="data-panel-open",n),b={[m.open]:""},R={[m.closed]:""},C={open:e=>e?b:R,...v.transitionStatusMapping},y=a.forwardRef(function(e,t){let{render:n,className:i,defaultOpen:f=!1,disabled:h=!1,onOpenChange:v,open:m,style:S,...b}=e,R=(0,o.useStableCallback)(v),y=function(e){let{open:t,defaultOpen:n,onOpenChange:i,disabled:r}=e,[s,f]=(0,l.useControlled)({controlled:t,default:n,name:"Collapsible",state:"open"}),{mounted:g,setMounted:h,transitionStatus:v}=(0,p.useTransitionStatus)(s,!0,!0),m=(0,u.useBaseUiId)(),[S,b]=a.useState(),R=S??m,C=(0,o.useStableCallback)(e=>{let t=!s,n=(0,d.createChangeEventDetails)(c.REASONS.triggerPress,e.nativeEvent);i(t,n),n.isCanceled||f(t)});return a.useMemo(()=>({disabled:r,handleTrigger:C,mounted:g,open:s,panelId:R,setMounted:h,setOpen:f,setPanelIdState:b,transitionStatus:v}),[r,C,g,s,R,h,f,b,v])}({open:m,defaultOpen:f,onOpenChange:R,disabled:h}),E=a.useMemo(()=>({open:y.open,disabled:y.disabled,transitionStatus:y.transitionStatus}),[y.open,y.disabled,y.transitionStatus]),x=a.useMemo(()=>({...y,onOpenChange:R,state:E}),[y,R,E]),P=(0,s.useRenderElement)("div",e,{state:E,ref:t,props:b,stateAttributesMapping:C});return(0,r.jsx)(g.Provider,{value:x,children:P})});var E=e.i(540886);let x={open:e=>e?{[S.panelOpen]:""}:null,...v.transitionStatusMapping},P=a.forwardRef(function(e,t){let{panelId:n,open:i,handleTrigger:r,state:a,disabled:o}=h(),{className:l,disabled:u=o,render:d,nativeButton:c=!0,style:p,...f}=e,{getButtonProps:g,buttonRef:v}=(0,E.useButton)({disabled:u,focusableWhenDisabled:!0,native:c});return(0,s.useRenderElement)("button",e,{state:a,ref:[t,v],props:[{"aria-controls":i?n:void 0,"aria-expanded":i,onClick:r},f,g],stateAttributesMapping:x})});var O=e.i(146376),k=e.i(377570),I=e.i(574735),w=e.i(828918),T=e.i(708445),M=e.i(446265),A=e.i(333848),F=e.i(137584),j=e.i(222640);let N={height:void 0,width:void 0};function D(e){return{height:e.scrollHeight,width:e.scrollWidth}}function B(e){return e.split(",").map(e=>e.trim()).some(e=>""!==e&&Number.parseFloat(e)>0)}function H(e,t,n){let i=e.style.getPropertyValue(t),r=e.style.getPropertyPriority(t);return e.style.setProperty(t,n),()=>{""===i?e.style.removeProperty(t):e.style.setProperty(t,i,r)}}let L=((i={}).collapsiblePanelHeight="--collapsible-panel-height",i.collapsiblePanelWidth="--collapsible-panel-width",i),V=a.forwardRef(function(e,t){let{className:n,hiddenUntilFound:i,keepMounted:r,render:l,id:u,style:p,...f}=e,{mounted:g,onOpenChange:v,open:S,panelId:b,setMounted:R,setPanelIdState:y,setOpen:E,state:x,transitionStatus:P}=h();(0,O.useIsoLayoutEffect)(()=>{if(u)return y(u),()=>{y(void 0)}},[u,y]);let{height:V,props:z,ref:U,shouldPreventOpenAnimation:K,shouldRender:_,transitionStatus:W,width:G}=function(e){let{externalRef:t,hiddenUntilFound:n,id:i,keepMounted:r,mounted:s,onOpenChange:l,open:u,setMounted:p,setOpen:f,transitionStatus:g}=e,h=a.useRef(null),v=a.useRef(null),[S,b]=a.useState(N),R=a.useRef(N),C=a.useRef(!1),y=a.useRef(u),E=a.useRef(!1),[x,P]=a.useState(!1),k=a.useRef(null),L=(0,w.useMergedRefs)(t,h),V=(0,M.useValueAsRef)({mounted:s,open:u}),z=(0,j.useAnimationsFinished)(h,!1,!1),U=!u&&!s,K=x?"idle":g,_=u&&(y.current||E.current),W=!u&&s&&"css-animation"===v.current&&void 0===S.height&&void 0===S.width?R.current:S,G=n&&U&&"css-animation"!==v.current,q=(0,o.useStableCallback)((e,t=!0)=>{t&&(R.current=e),b(e)}),Y=(0,o.useStableCallback)(()=>{k.current?.(),k.current=null}),J=(0,o.useStableCallback)(e=>{Y(),k.current=()=>{k.current=null,e()}}),$=(0,o.useStableCallback)(()=>{u&&s&&"css-animation"===v.current&&(E.current=!0)});(0,O.useIsoLayoutEffect)(()=>{x&&"starting"!==g&&P(!1)},[x,g]),a.useEffect(()=>()=>{$(),Y()},[$,Y]),(0,O.useIsoLayoutEffect)(()=>{let e=h.current;if(!e)return;!u&&k.current&&Y();let t=function(e,t=!1){let n=(0,A.ownerWindow)(e).getComputedStyle(e),i=(n.animationName.split(",").map(e=>e.trim()).some(e=>""!==e&&"none"!==e)||t)&&B(n.animationDuration),r=B(n.transitionDuration);return i&&r||r?"css-transition":i?"css-animation":"none"}(e,_);if(v.current=t,u&&"idle"===g&&y.current&&"css-animation"===t){R.current=D(e);return}if(u&&"starting"===g){let n=C.current;if(C.current=!1,"none"===t){q(D(e)),P(!0);return}if("css-transition"===t){let t=function(e){let t={"justify-content":e.style.justifyContent,"align-items":e.style.alignItems,"align-content":e.style.alignContent,"justify-items":e.style.justifyItems};function n(){Object.entries(t).forEach(([t,n])=>{""===n?e.style.removeProperty(t):e.style.setProperty(t,n)})}Object.keys(t).forEach(t=>{e.style.setProperty(t,"initial","important")});let i=T.AnimationFrame.request(n);return()=>{T.AnimationFrame.cancel(i),n()}}(e);return q(D(e)),n&&(J(H(e,"transition-duration","0s")),P(!0)),t}if("css-animation"===t){if(q(D(e)),!n)return void H(e,"animation-name","none")();let t=H(e,"animation-name","none"),i=H(e,"animation-duration","0s");return t(),J(i),P(!0),void 0}}if(!u&&s&&("idle"===g||"starting"===g)){if(y.current=!1,E.current=!1,"none"===t){q(N,!1),p(!1);return}q(D(e));return}if("ending"!==g)return;if("none"===t)return void p(!1);let n=D(e);(n.height??0)>0||(n.width??0)>0?(q(n),"css-animation"===t&&H(e,"animation-name","none")()):p(!1)},[s,u,Y,q,p,J,_,g]),(0,F.useOpenChangeComplete)({enabled:u&&s&&"idle"===K,open:!0,ref:h,onComplete(){u&&q(N,!1)}}),a.useEffect(()=>{if(u||!s||"ending"!==K||!h.current)return;let e=new AbortController,t=-1;function n(){V.current.open||(p(!1),q(N,!1))}return t=T.AnimationFrame.request(()=>{e.signal.aborted||z(n,e.signal)}),()=>{T.AnimationFrame.cancel(t),e.abort()}},[V,s,u,K,z,q,p]),(0,O.useIsoLayoutEffect)(()=>{let e=h.current;e&&n&&U&&e.setAttribute("hidden","until-found")},[U,n]),a.useEffect(function(){let e=h.current;if(e)return(0,I.addEventListener)(e,"beforematch",function(e){let t=(0,d.createChangeEventDetails)(c.REASONS.none,e);l(!0,t),t.isCanceled||(C.current=!0,f(!0))})},[l,f]);let Q=r||n||s||u;return{height:W.height,props:{...G?{[m.startingStyle]:""}:void 0,hidden:U,id:i},ref:L,shouldPreventOpenAnimation:_,shouldRender:Q,transitionStatus:K,width:W.width}}({externalRef:t,hiddenUntilFound:i??!1,id:b,keepMounted:r??!1,mounted:g,onOpenChange:v,open:S,setMounted:R,setOpen:E,transitionStatus:P}),q={...x,transitionStatus:W},Y=(0,k.resolveStyle)(p,q),J=(0,s.useRenderElement)("div",{...e,style:void 0},{state:q,ref:U,props:[z,{style:{[L.collapsiblePanelHeight]:void 0===V?"auto":`${V}px`,[L.collapsiblePanelWidth]:void 0===G?"auto":`${G}px`}},f,Y?{style:Y}:void 0,K?{style:{animationName:"none"}}:void 0],stateAttributesMapping:C});return _?J:null});e.s(["Panel",0,V,"Root",0,y,"Trigger",0,P],596315);var z=e.i(596315),z=z;e.s(["Collapsible",0,function({...e}){return(0,r.jsx)(z.Root,{"data-slot":"collapsible",...e})},"CollapsibleContent",0,function({...e}){return(0,r.jsx)(z.Panel,{"data-slot":"collapsible-content",...e})},"CollapsibleTrigger",0,function({...e}){return(0,r.jsx)(z.Trigger,{"data-slot":"collapsible-trigger",...e})}],204258)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0nk7-_~gcxbz0.js b/litellm/proxy/_experimental/out/_next/static/chunks/0nk7-_~gcxbz0.js new file mode 100644 index 00000000000..abf9344f182 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0nk7-_~gcxbz0.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,250980,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,l],250980)},309426,e=>{"use strict";var t=e.i(290571),l=e.i(444755),r=e.i(673706),s=e.i(271645),a=e.i(46757);let n=(0,r.makeClassName)("Col"),i=s.default.forwardRef((e,r)=>{let i,o,c,d,{numColSpan:u=1,numColSpanSm:m,numColSpanMd:p,numColSpanLg:g,children:f,className:h}=e,x=(0,t.__rest)(e,["numColSpan","numColSpanSm","numColSpanMd","numColSpanLg","children","className"]),v=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"";return s.default.createElement("div",Object.assign({ref:r,className:(0,l.tremorTwMerge)(n("root"),(i=v(u,a.colSpan),o=v(m,a.colSpanSm),c=v(p,a.colSpanMd),d=v(g,a.colSpanLg),(0,l.tremorTwMerge)(i,o,c,d)),h)},x),f)});i.displayName="Col",e.s(["Col",0,i],309426)},950724,(e,t,l)=>{t.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},100236,(e,t,l)=>{t.exports=e.g&&e.g.Object===Object&&e.g},139088,(e,t,l)=>{var r=e.r(100236),s="object"==typeof self&&self&&self.Object===Object&&self;t.exports=r||s||Function("return this")()},631926,(e,t,l)=>{var r=e.r(139088);t.exports=function(){return r.Date.now()}},748891,(e,t,l)=>{var r=/\s/;t.exports=function(e){for(var t=e.length;t--&&r.test(e.charAt(t)););return t}},830364,(e,t,l)=>{var r=e.r(748891),s=/^\s+/;t.exports=function(e){return e?e.slice(0,r(e)+1).replace(s,""):e}},630353,(e,t,l)=>{t.exports=e.r(139088).Symbol},243436,(e,t,l)=>{var r=e.r(630353),s=Object.prototype,a=s.hasOwnProperty,n=s.toString,i=r?r.toStringTag:void 0;t.exports=function(e){var t=a.call(e,i),l=e[i];try{e[i]=void 0;var r=!0}catch(e){}var s=n.call(e);return r&&(t?e[i]=l:delete e[i]),s}},223243,(e,t,l)=>{var r=Object.prototype.toString;t.exports=function(e){return r.call(e)}},377684,(e,t,l)=>{var r=e.r(630353),s=e.r(243436),a=e.r(223243),n=r?r.toStringTag:void 0;t.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":n&&n in Object(e)?s(e):a(e)}},877289,(e,t,l)=>{t.exports=function(e){return null!=e&&"object"==typeof e}},361884,(e,t,l)=>{var r=e.r(377684),s=e.r(877289);t.exports=function(e){return"symbol"==typeof e||s(e)&&"[object Symbol]"==r(e)}},773759,(e,t,l)=>{var r=e.r(830364),s=e.r(950724),a=e.r(361884),n=0/0,i=/^[-+]0x[0-9a-f]+$/i,o=/^0b[01]+$/i,c=/^0o[0-7]+$/i,d=parseInt;t.exports=function(e){if("number"==typeof e)return e;if(a(e))return n;if(s(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=s(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var l=o.test(e);return l||c.test(e)?d(e.slice(2),l?2:8):i.test(e)?n:+e}},374009,(e,t,l)=>{var r=e.r(950724),s=e.r(631926),a=e.r(773759),n=Math.max,i=Math.min;t.exports=function(e,t,l){var o,c,d,u,m,p,g=0,f=!1,h=!1,x=!0;if("function"!=typeof e)throw TypeError("Expected a function");function v(t){var l=o,r=c;return o=c=void 0,g=t,u=e.apply(r,l)}function y(e){var l=e-p,r=e-g;return void 0===p||l>=t||l<0||h&&r>=d}function b(){var e,l,r,a=s();if(y(a))return w(a);m=setTimeout(b,(e=a-p,l=a-g,r=t-e,h?i(r,d-l):r))}function w(e){return(m=void 0,x&&o)?v(e):(o=c=void 0,u)}function j(){var e,l=s(),r=y(l);if(o=arguments,c=this,p=l,r){if(void 0===m)return g=e=p,m=setTimeout(b,t),f?v(e):u;if(h)return clearTimeout(m),m=setTimeout(b,t),v(p)}return void 0===m&&(m=setTimeout(b,t)),u}return t=a(t)||0,r(l)&&(f=!!l.leading,d=(h="maxWait"in l)?n(a(l.maxWait)||0,t):d,x="trailing"in l?!!l.trailing:x),j.cancel=function(){void 0!==m&&clearTimeout(m),g=0,o=p=c=m=void 0},j.flush=function(){return void 0===m?u:w(s())},j}},435451,e=>{"use strict";var t=e.i(843476),l=e.i(290571),r=e.i(271645);let s=e=>{var t=(0,l.__rest)(e,[]);return r.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),r.default.createElement("path",{d:"M12 4v16m8-8H4"}))},a=e=>{var t=(0,l.__rest)(e,[]);return r.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),r.default.createElement("path",{d:"M20 12H4"}))};var n=e.i(444755),i=e.i(673706),o=e.i(677955);let c="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",d="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",u=r.default.forwardRef((e,t)=>{let{onSubmit:u,enableStepper:m=!0,disabled:p,onValueChange:g,onChange:f}=e,h=(0,l.__rest)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),x=(0,r.useRef)(null),[v,y]=r.default.useState(!1),b=r.default.useCallback(()=>{y(!0)},[]),w=r.default.useCallback(()=>{y(!1)},[]),[j,N]=r.default.useState(!1),S=r.default.useCallback(()=>{N(!0)},[]),k=r.default.useCallback(()=>{N(!1)},[]);return r.default.createElement(o.default,Object.assign({type:"number",ref:(0,i.mergeRefs)([x,t]),disabled:p,makeInputClassName:(0,i.makeClassName)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null==(t=x.current)?void 0:t.value;null==u||u(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&b(),"ArrowUp"===e.key&&S()},onKeyUp:e=>{"ArrowDown"===e.key&&w(),"ArrowUp"===e.key&&k()},onChange:e=>{p||(null==g||g(parseFloat(e.target.value)),null==f||f(e))},stepper:m?r.default.createElement("div",{className:(0,n.tremorTwMerge)("flex justify-center align-middle")},r.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;p||(null==(e=x.current)||e.stepDown(),null==(t=x.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,n.tremorTwMerge)(!p&&d,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},r.default.createElement(a,{"data-testid":"step-down",className:(v?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),r.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;p||(null==(e=x.current)||e.stepUp(),null==(t=x.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,n.tremorTwMerge)(!p&&d,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},r.default.createElement(s,{"data-testid":"step-up",className:(j?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},h))});u.displayName="NumberInput",e.s(["default",0,({step:e=.01,style:l={width:"100%"},placeholder:r="Enter a numerical value",min:s,max:a,onChange:n,...i})=>(0,t.jsx)(u,{onWheel:e=>e.currentTarget.blur(),step:e,style:l,placeholder:r,min:s,max:a,onChange:n,...i})],435451)},860585,e=>{"use strict";var t=e.i(843476),l=e.i(199133);let{Option:r}=l.Select;e.s(["default",0,({value:e,onChange:s,className:a="",style:n={}})=>(0,t.jsxs)(l.Select,{style:{width:"100%",...n},value:e||void 0,onChange:s,className:a,placeholder:"n/a",allowClear:!0,children:[(0,t.jsx)(r,{value:"1h",children:"hourly"}),(0,t.jsx)(r,{value:"24h",children:"daily"}),(0,t.jsx)(r,{value:"7d",children:"weekly"}),(0,t.jsx)(r,{value:"30d",children:"monthly"})]}),"getBudgetDurationLabel",0,e=>e?({"1h":"hourly","24h":"daily","7d":"weekly","30d":"monthly"})[e]||e:"Not set"])},213205,e=>{"use strict";e.i(247167);var t=e.i(931067),l=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M678.3 642.4c24.2-13 51.9-20.4 81.4-20.4h.1c3 0 4.4-3.6 2.2-5.6a371.67 371.67 0 00-103.7-65.8c-.4-.2-.8-.3-1.2-.5C719.2 505 759.6 431.7 759.6 349c0-137-110.8-248-247.5-248S264.7 212 264.7 349c0 82.7 40.4 156 102.6 201.1-.4.2-.8.3-1.2.5-44.7 18.9-84.8 46-119.3 80.6a373.42 373.42 0 00-80.4 119.5A373.6 373.6 0 00137 888.8a8 8 0 008 8.2h59.9c4.3 0 7.9-3.5 8-7.8 2-77.2 32.9-149.5 87.6-204.3C357 628.2 432.2 597 512.2 597c56.7 0 111.1 15.7 158 45.1a8.1 8.1 0 008.1.3zM512.2 521c-45.8 0-88.9-17.9-121.4-50.4A171.2 171.2 0 01340.5 349c0-45.9 17.9-89.1 50.3-121.6S466.3 177 512.2 177s88.9 17.9 121.4 50.4A171.2 171.2 0 01683.9 349c0 45.9-17.9 89.1-50.3 121.6C601.1 503.1 558 521 512.2 521zM880 759h-84v-84c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v84h-84c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h84v84c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-84h84c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"}}]},name:"user-add",theme:"outlined"};var s=e.i(9583),a=l.forwardRef(function(e,a){return l.createElement(s.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["UserAddOutlined",0,a],213205)},355619,e=>{"use strict";var t=e.i(602869);let l=async(e,l,r)=>{try{if(null===e||null===l)return;if(null!==r){let s=(await (0,t.modelAvailableCall)(r,e,l,!0,null,!0)).data.map(e=>e.id),a=[],n=[];return s.forEach(e=>{e.endsWith("/*")?a.push(e):n.push(e)}),[...a,...n]}}catch(e){console.error("Error fetching user models:",e)}};e.s(["excludeProxyWideSentinel",0,e=>e.filter(e=>"all-proxy-models"!==e),"fetchAvailableModelsForTeamOrKey",0,l,"getModelDisplayName",0,e=>{if("all-proxy-models"===e)return"All Proxy Models";if(e.endsWith("/*")){let t=e.replace("/*","");return`All ${t} models`}return e},"hasAllModelsSentinel",0,e=>e.includes("all-proxy-models")||e.includes("all-team-models"),"unfurlWildcardModelsInList",0,(e,t)=>{let l=[],r=[];return e.forEach(e=>{if(e.endsWith("/*")){let s=e.replace("/*",""),a=t.filter(e=>e.startsWith(s+"/"));r.push(...a),l.push(e)}else r.push(e)}),[...l,...r].filter((e,t,l)=>l.indexOf(e)===t)}])},350967,46757,e=>{"use strict";var t=e.i(290571),l=e.i(444755),r=e.i(673706),s=e.i(271645);let a={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},n={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},i={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},o={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,a,"gridColsLg",0,o,"gridColsMd",0,i,"gridColsSm",0,n],46757);let c=(0,r.makeClassName)("Grid"),d=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=s.default.forwardRef((e,r)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:p,numItemsLg:g,children:f,className:h}=e,x=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),v=d(u,a),y=d(m,n),b=d(p,i),w=d(g,o),j=(0,l.tremorTwMerge)(v,y,b,w);return s.default.createElement("div",Object.assign({ref:r,className:(0,l.tremorTwMerge)(c("root"),"grid",j,h)},x),f)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},500727,e=>{"use strict";var t=e.i(266027),l=e.i(243652),r=e.i(602869),s=e.i(135214);let a=(0,l.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:l}=(0,s.default)();return(0,t.useQuery)({queryKey:a.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,r.fetchMCPServers)(l,e),enabled:!!l})}])},699857,e=>{"use strict";var t=e.i(266027),l=e.i(243652),r=e.i(602869),s=e.i(135214);let a=(0,l.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,s.default)();return(0,t.useQuery)({queryKey:a.list(),queryFn:async()=>await (0,r.fetchMCPToolsets)(e),enabled:!!e})}])},916940,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(199133),s=e.i(602869);e.s(["default",0,({onChange:e,value:a,className:n,accessToken:i,placeholder:o="Select vector stores",disabled:c=!1})=>{let[d,u]=(0,l.useState)([]),[m,p]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{(async()=>{if(i){p(!0);try{let e=await (0,s.vectorStoreListCall)(i);e.data&&u(e.data)}catch(e){console.error("Error fetching vector stores:",e)}finally{p(!1)}}})()},[i]),(0,t.jsx)("div",{children:(0,t.jsx)(r.Select,{mode:"multiple",placeholder:o,onChange:e,value:a,loading:m,className:n,allowClear:!0,options:d.map(e=>({label:`${e.vector_store_name||e.vector_store_id} (${e.vector_store_id})`,value:e.vector_store_id,title:e.vector_store_description||e.vector_store_id})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:c})})}])},75921,e=>{"use strict";var t=e.i(843476),l=e.i(266027),r=e.i(243652),s=e.i(602869),a=e.i(135214);let n=(0,r.createQueryKeys)("mcpAccessGroups");var i=e.i(500727),o=e.i(699857),c=e.i(199133),d=e.i(234713);let u="toolset:";e.s(["default",0,({onChange:e,value:r,className:m,accessToken:p,placeholder:g="Select MCP servers",disabled:f=!1,teamId:h,allowNoMcpServers:x=!1,allowAllProxyMcpServers:v=!1})=>{let{data:y=[],isLoading:b}=(0,i.useMCPServers)(h),{data:w=[],isLoading:j}=(()=>{let{accessToken:e}=(0,a.default)();return(0,l.useQuery)({queryKey:n.list({}),queryFn:async()=>await (0,s.fetchMCPAccessGroups)(e),enabled:!!e})})(),{data:N=[],isLoading:S}=(0,o.useMCPToolsets)(),k=new Set(w),C=[...w.map(e=>({label:e,value:e,type:"accessGroup",searchText:`${e} Access Group`})),...y.map(e=>({label:`${e.server_name||e.server_id} (${e.server_id})`,value:e.server_id,type:"server",searchText:`${e.server_name||e.server_id} ${e.server_id} MCP Server`})),...N.map(e=>({label:e.toolset_name,value:`${u}${e.toolset_id}`,type:"toolset",searchText:`${e.toolset_name} ${e.toolset_id} Toolset`}))],_={accessGroup:"#52c41a",server:"#1890ff",toolset:"#722ed1"},M={accessGroup:"Access Group",server:"MCP Server",toolset:"Toolset"},E=[...r?.servers||[],...r?.accessGroups||[],...(r?.toolsets||[]).map(e=>`${u}${e}`)],T=x&&E.includes(d.NO_MCP_SERVERS_SENTINEL),L=E.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL);return(0,t.jsx)("div",{children:(0,t.jsxs)(c.Select,{mode:"multiple",placeholder:g,onChange:t=>{if(v&&t.includes(d.ALL_PROXY_MCP_SERVERS_SENTINEL))return void e({servers:[d.ALL_PROXY_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});if(x&&t.includes(d.NO_MCP_SERVERS_SENTINEL))return void e({servers:[d.NO_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});let l=t.filter(e=>e.startsWith(u)).map(e=>e.slice(u.length)),r=t.filter(e=>!e.startsWith(u));e({servers:r.filter(e=>!k.has(e)),accessGroups:r.filter(e=>k.has(e)),toolsets:l})},value:E,loading:b||j||S,className:m,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:f,filterOption:(e,t)=>t?.value===d.NO_MCP_SERVERS_SENTINEL||t?.value===d.ALL_PROXY_MCP_SERVERS_SENTINEL||(C.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:[(v||L)&&(0,t.jsx)(c.Select.Option,{value:d.ALL_PROXY_MCP_SERVERS_SENTINEL,label:"All Proxy MCP Servers",children:(0,t.jsx)("span",{style:{color:"#1890ff",fontWeight:500},children:"All Proxy MCP Servers"})},d.ALL_PROXY_MCP_SERVERS_SENTINEL),x&&(0,t.jsx)(c.Select.Option,{value:d.NO_MCP_SERVERS_SENTINEL,label:"No MCP Servers",children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{flex:1},children:"No MCP Servers"}),(0,t.jsx)("span",{style:{color:"#8c8c8c",fontSize:"12px",fontWeight:500,opacity:.8},children:"Block all"})]})},d.NO_MCP_SERVERS_SENTINEL),C.map(e=>(0,t.jsx)(c.Select.Option,{value:e.value,label:e.label,disabled:T||L,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:_[e.type],flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:_[e.type],fontSize:"12px",fontWeight:500,opacity:.8},children:M[e.type]})]})},e.value))]})})}],75921)},983561,e=>{"use strict";e.i(247167);var t=e.i(931067),l=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M300 328a60 60 0 10120 0 60 60 0 10-120 0zM852 64H172c-17.7 0-32 14.3-32 32v660c0 17.7 14.3 32 32 32h680c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-32 660H204V128h616v596zM604 328a60 60 0 10120 0 60 60 0 10-120 0zm250.2 556H169.8c-16.5 0-29.8 14.3-29.8 32v36c0 4.4 3.3 8 7.4 8h729.1c4.1 0 7.4-3.6 7.4-8v-36c.1-17.7-13.2-32-29.7-32zM664 508H360c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"robot",theme:"outlined"};var s=e.i(9583),a=l.forwardRef(function(e,a){return l.createElement(s.default,(0,t.default)({},e,{ref:a,icon:r}))});e.s(["RobotOutlined",0,a],983561)},797672,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,l],797672)},992619,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(779241),s=e.i(599724),a=e.i(199133),n=e.i(983561),i=e.i(695411);e.s(["default",0,({accessToken:e,value:o,placeholder:c="Select a Model",onChange:d,disabled:u=!1,style:m,className:p,showLabel:g=!0,labelText:f="Select Model"})=>{let[h,x]=(0,l.useState)(o),[v,y]=(0,l.useState)(!1),[b,w]=(0,l.useState)([]),j=(0,l.useRef)(null);return(0,l.useEffect)(()=>{x(o)},[o]),(0,l.useEffect)(()=>{e&&(async()=>{try{let t=await (0,i.fetchAvailableModels)(e);t.length>0&&w(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[g&&(0,t.jsxs)(s.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(n.RobotOutlined,{className:"mr-2"})," ",f]}),(0,t.jsx)(a.Select,{value:h,placeholder:c,onChange:e=>{"custom"===e?(y(!0),x(void 0)):(y(!1),x(e),d&&d(e))},options:[...Array.from(new Set(b.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${p||""}`,disabled:u}),v&&(0,t.jsx)(r.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{j.current&&clearTimeout(j.current),j.current=setTimeout(()=>{x(e),d&&d(e)},500)},disabled:u})]})}])},409797,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDownIcon",()=>t.default])},361653,e=>{"use strict";let t=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,t])},91739,e=>{"use strict";var t=e.i(544195);e.s(["Radio",()=>t.default])},988297,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,l],988297)},531516,696609,e=>{"use strict";var t=e.i(843476),l=e.i(271645),r=e.i(536916),s=e.i(599724),a=e.i(409797),n=e.i(246349),n=n;let i=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,o=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,c=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,d=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function u(e,t=""){let l=e.toLowerCase();if(d.test(l))return"read";if(i.test(l))return"delete";if(c.test(l))return"update";if(o.test(l))return"create";if(t){let e=t.toLowerCase();if(d.test(e))return"read";if(i.test(e))return"delete";if(c.test(e))return"update";if(o.test(e))return"create"}return"unknown"}function m(e){let t={read:[],create:[],update:[],delete:[],unknown:[]};for(let l of e)t[u(l.name,l.description)].push(l);return t}let p={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,p,"classifyToolOp",0,u,"groupToolsByCrud",0,m],696609);let g=["read","create","update","delete","unknown"],f={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},h={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},x={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:i,onChange:o,readOnly:c=!1,searchFilter:d=""})=>{let[u,v]=(0,l.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),y=(0,l.useMemo)(()=>m(e),[e]),b=(0,l.useMemo)(()=>new Set(void 0===i?e.map(e=>e.name):i),[i,e]),w=e=>{if(c)return;let t=new Set(b);t.has(e)?t.delete(e):t.add(e),o(Array.from(t))};return 0===e.length?null:(0,t.jsx)("div",{className:"space-y-3",children:g.map(e=>{let l,i=y[e];if(0===i.length)return null;if(d){let e=d.toLowerCase();if(!i.some(t=>t.name.toLowerCase().includes(e)||(t.description??"").toLowerCase().includes(e)))return null}let m=p[e],g=(l=y[e]).length>0&&l.every(e=>b.has(e.name)),j=(e=>{let t=y[e];if(0===t.length)return!1;let l=t.filter(e=>b.has(e.name)).length;return l>0&&l{v(t=>({...t,[e]:!t[e]}))},children:[N?(0,t.jsx)(n.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,t.jsx)(a.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:m.label}),(0,t.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${f[m.risk]}`,children:"high"===m.risk?"High Risk":"medium"===m.risk?"Medium Risk":"low"===m.risk?"Safe":"Unclassified"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[i.filter(e=>b.has(e.name)).length,"/",i.length," allowed"]})]}),!c&&(0,t.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,t.jsx)(s.Text,{className:"text-xs text-gray-500",children:g?"All on":j?"Partial":"All off"}),(0,t.jsx)(r.Checkbox,{checked:g,indeterminate:j,onChange:t=>((e,t)=>{if(c)return;let l=new Set(b);for(let r of y[e])t?l.add(r.name):l.delete(r.name);o(Array.from(l))})(e,t.target.checked),onClick:e=>e.stopPropagation()})]})]}),!N&&(0,t.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:m.description}),!N&&(0,t.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:i.filter(e=>!d||e.name.toLowerCase().includes(d.toLowerCase())||(e.description??"").toLowerCase().includes(d.toLowerCase())).map(e=>{let l,a=(l=e.name,b.has(l));return(0,t.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!c?"cursor-pointer":""} ${a?"":"opacity-60"}`,onClick:()=>w(e.name),children:[(0,t.jsx)(r.Checkbox,{checked:a,onChange:()=>w(e.name),disabled:c,onClick:e=>e.stopPropagation()}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)(s.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,t.jsx)(s.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,t.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${a?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:a?"on":"off"})]},e.name)})})]},e)})})}],531516)},37727,e=>{"use strict";var t=e.i(841947);e.s(["X",()=>t.default])},841947,e=>{"use strict";let t=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,t])},603908,e=>{"use strict";let t=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,t])},107233,e=>{"use strict";var t=e.i(603908);e.s(["Plus",()=>t.default])},158392,63209,e=>{"use strict";var t=e.i(843476),l=e.i(311451);let r={ttl:3600,lowest_latency_buffer:0},s=({routingStrategyArgs:e})=>{let s={ttl:"Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Latency-Based Configuration"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Fine-tune latency-based routing behavior"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e||r).map(([e,r])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:e.replace(/_/g," ")}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:s[e]||""}),(0,t.jsx)(l.Input,{name:e,defaultValue:"object"==typeof r?JSON.stringify(r,null,2):r?.toString(),className:"font-mono text-sm w-full"})]})},e))})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"})]})},a=({routerSettings:e,routerFieldsMetadata:r})=>(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Reliability & Retries"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure retry logic and failure handling"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e).filter(([e])=>"fallbacks"!=e&&"context_window_fallbacks"!=e&&"routing_strategy_args"!=e&&"routing_strategy"!=e&&"enable_tag_filtering"!=e&&"retry_policy"!=e&&"model_group_retry_policy"!=e&&"routing_groups"!=e).map(([e,s])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:r[e]?.ui_field_name||e}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:r[e]?.field_description||""}),(0,t.jsx)(l.Input,{name:e,defaultValue:null==s||"null"===s?"":"object"==typeof s?JSON.stringify(s,null,2):s?.toString()||"",placeholder:"—",className:"font-mono text-sm w-full"})]})},e))})]});var n=e.i(199133);let i=({selectedStrategy:e,availableStrategies:l,routingStrategyDescriptions:r,routerFieldsMetadata:s,onStrategyChange:a})=>(0,t.jsxs)("div",{className:"space-y-2 max-w-3xl",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:s.routing_strategy?.ui_field_name||"Routing Strategy"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:s.routing_strategy?.field_description||""})]}),(0,t.jsx)("div",{className:"routing-strategy-select max-w-3xl",children:(0,t.jsx)(n.Select,{value:e,onChange:a,style:{width:"100%"},size:"large",children:l.map(e=>(0,t.jsx)(n.Select.Option,{value:e,label:e,children:(0,t.jsxs)("div",{className:"flex flex-col gap-0.5 py-1",children:[(0,t.jsx)("span",{className:"font-mono text-sm font-medium",children:e}),r[e]&&(0,t.jsx)("span",{className:"text-xs text-gray-500 font-normal",children:r[e]})]})},e))})})]});var o=e.i(790848);let c=({enabled:e,routerFieldsMetadata:l,onToggle:r})=>(0,t.jsx)("div",{className:"space-y-3 max-w-3xl",children:(0,t.jsxs)("div",{className:"flex items-start justify-between",children:[(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:l.enable_tag_filtering?.ui_field_name||"Enable Tag Filtering"}),(0,t.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:[l.enable_tag_filtering?.field_description||"",l.enable_tag_filtering?.link&&(0,t.jsxs)(t.Fragment,{children:[" ",(0,t.jsx)("a",{href:l.enable_tag_filtering.link,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"Learn more"})]})]})]}),(0,t.jsx)(o.Switch,{checked:e,onChange:r,className:"ml-4"})]})});e.s(["default",0,({value:e,onChange:l,routerFieldsMetadata:r,availableRoutingStrategies:n,routingStrategyDescriptions:o})=>(0,t.jsxs)("div",{className:"w-full space-y-8 py-2",children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Routing Settings"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure how requests are routed to deployments"})]}),n.length>0&&(0,t.jsx)(i,{selectedStrategy:e.selectedStrategy||e.routerSettings.routing_strategy||null,availableStrategies:n,routingStrategyDescriptions:o,routerFieldsMetadata:r,onStrategyChange:t=>{l({...e,selectedStrategy:t})}}),(0,t.jsx)(c,{enabled:e.enableTagFiltering,routerFieldsMetadata:r,onToggle:t=>{l({...e,enableTagFiltering:t})}})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"}),"latency-based-routing"===e.selectedStrategy&&(0,t.jsx)(s,{routingStrategyArgs:e.routerSettings.routing_strategy_args}),(0,t.jsx)(a,{routerSettings:e.routerSettings,routerFieldsMetadata:r})]})],158392);var d=e.i(361653);e.s(["AlertCircle",()=>d.default],63209)},425063,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);e.s(["ArrowDown",0,t],425063)},419470,e=>{"use strict";var t=e.i(843476),l=e.i(994388),r=e.i(653496),s=e.i(107233),a=e.i(271645),n=e.i(888259),i=e.i(199133),o=e.i(592968),c=e.i(63209),d=e.i(425063),u=e.i(37727);function m({group:e,onChange:l,availableModels:r,maxFallbacks:s}){let a=r.filter(t=>t!==e.primaryModel),n=e.fallbackModels.length{let r=[...e.fallbackModels];r.includes(t)&&(r=r.filter(e=>e!==t)),l({...e,primaryModel:t,fallbackModels:r})},showSearch:!0,getPopupContainer:e=>e.parentElement||document.body,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:r.map(e=>({label:e,value:e}))}),!e.primaryModel&&(0,t.jsxs)("div",{className:"mt-2 flex items-center gap-2 text-amber-600 text-xs bg-amber-50 p-2 rounded-sm",children:[(0,t.jsx)(c.AlertCircle,{className:"w-4 h-4"}),(0,t.jsx)("span",{children:"Select a model to begin configuring fallbacks"})]})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-4 z-10",children:(0,t.jsxs)("div",{className:"bg-indigo-50 text-indigo-500 px-4 py-1 rounded-full text-xs font-bold border border-indigo-100 flex items-center gap-2 shadow-xs",children:[(0,t.jsx)(d.ArrowDown,{className:"w-4 h-4"}),"IF FAILS, TRY..."]})}),(0,t.jsxs)("div",{className:`transition-opacity duration-300 ${!e.primaryModel?"opacity-50 pointer-events-none":"opacity-100"}`,children:[(0,t.jsxs)("label",{className:"block text-sm font-semibold text-gray-700 mb-2",children:["Fallback Chain ",(0,t.jsx)("span",{className:"text-red-500",children:"*"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 font-normal ml-2",children:["(Max ",s," fallbacks at a time)"]})]}),(0,t.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 border border-gray-200",children:[(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(i.Select,{mode:"multiple",className:"w-full",size:"large",placeholder:n?"Select fallback models to add...":`Maximum ${s} fallbacks reached`,value:e.fallbackModels,onChange:t=>{let r=t.slice(0,s);l({...e,fallbackModels:r})},disabled:!e.primaryModel,getPopupContainer:e=>e.parentElement||document.body,options:a.map(e=>({label:e,value:e})),optionRender:(l,r)=>{let s=e.fallbackModels.includes(l.value),a=s?e.fallbackModels.indexOf(l.value)+1:null;return(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[s&&null!==a&&(0,t.jsx)("span",{className:"flex items-center justify-center w-5 h-5 rounded-sm bg-indigo-100 text-indigo-600 text-xs font-bold",children:a}),(0,t.jsx)("span",{children:l.label})]})},maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(o.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})}),showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase())}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1 ml-1",children:n?`Search and select multiple models. Selected models will appear below in order. (${e.fallbackModels.length}/${s} used)`:`Maximum ${s} fallbacks reached. Remove some to add more.`})]}),(0,t.jsx)("div",{className:"space-y-2 min-h-[100px]",children:0===e.fallbackModels.length?(0,t.jsxs)("div",{className:"h-32 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center text-gray-400",children:[(0,t.jsx)("span",{className:"text-sm",children:"No fallback models selected"}),(0,t.jsx)("span",{className:"text-xs mt-1",children:"Add models from the dropdown above"})]}):e.fallbackModels.map((r,s)=>(0,t.jsxs)("div",{className:"group flex items-center justify-between p-3 bg-white rounded-lg border border-gray-200 hover:border-indigo-300 hover:shadow-xs transition-all",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)("div",{className:"flex items-center justify-center w-6 h-6 rounded-sm bg-gray-100 text-gray-400 group-hover:text-indigo-500 group-hover:bg-indigo-50",children:(0,t.jsx)("span",{className:"text-xs font-bold",children:s+1})}),(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"font-medium text-gray-800",children:r})})]}),(0,t.jsx)("button",{type:"button",onClick:()=>{let t;return t=e.fallbackModels.filter((e,t)=>t!==s),void l({...e,fallbackModels:t})},className:"opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 hover:text-red-500 p-1",children:(0,t.jsx)(u.X,{className:"w-4 h-4"})})]},`${r}-${s}`))})]})]})]})}e.s(["FallbackSelectionForm",0,function({groups:e,onGroupsChange:i,availableModels:o,maxFallbacks:c=10,maxGroups:d=5}){let[u,p]=(0,a.useState)(e.length>0?e[0].id:"1");(0,a.useEffect)(()=>{e.length>0?e.some(e=>e.id===u)||p(e[0].id):p("1")},[e]);let g=()=>{if(e.length>=d)return;let t=Date.now().toString();i([...e,{id:t,primaryModel:null,fallbackModels:[]}]),p(t)},f=t=>{i(e.map(e=>e.id===t.id?t:e))},h=e.map((l,r)=>{let s=l.primaryModel?l.primaryModel:`Group ${r+1}`;return{key:l.id,label:s,closable:e.length>1,children:(0,t.jsx)(m,{group:l,onChange:f,availableModels:o,maxFallbacks:c})}});return 0===e.length?(0,t.jsxs)("div",{className:"text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300",children:[(0,t.jsx)("p",{className:"text-gray-500 mb-4",children:"No fallback groups configured"}),(0,t.jsx)(l.Button,{variant:"primary",onClick:g,icon:()=>(0,t.jsx)(s.Plus,{className:"w-4 h-4"}),children:"Create First Group"})]}):(0,t.jsx)(r.Tabs,{type:"editable-card",activeKey:u,onChange:p,onEdit:(t,l)=>{"add"===l?g():"remove"===l&&e.length>1&&(t=>{if(1===e.length)return n.default.warning("At least one group is required");let l=e.filter(e=>e.id!==t);i(l),u===t&&l.length>0&&p(l[l.length-1].id)})(t)},items:h,className:"fallback-tabs",tabBarStyle:{marginBottom:0},hideAdd:e.length>=d})}],419470)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0nnx~7-7e5t~1.js b/litellm/proxy/_experimental/out/_next/static/chunks/0nnx~7-7e5t~1.js new file mode 100644 index 00000000000..5b90272ca5b --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0nnx~7-7e5t~1.js @@ -0,0 +1,5 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,467034,(e,t,r)=>{var s={675:function(e,t){"use strict";t.byteLength=function(e){var t=l(e),r=t[0],s=t[1];return(r+s)*3/4-s},t.toByteArray=function(e){var t,r,i=l(e),a=i[0],o=i[1],u=new n((a+o)*3/4-o),c=0,h=o>0?a-4:a;for(r=0;r>16&255,u[c++]=t>>8&255,u[c++]=255&t;return 2===o&&(t=s[e.charCodeAt(r)]<<2|s[e.charCodeAt(r+1)]>>4,u[c++]=255&t),1===o&&(t=s[e.charCodeAt(r)]<<10|s[e.charCodeAt(r+1)]<<4|s[e.charCodeAt(r+2)]>>2,u[c++]=t>>8&255,u[c++]=255&t),u},t.fromByteArray=function(e){for(var t,s=e.length,n=s%3,i=[],a=0,o=s-n;a>18&63]+r[n>>12&63]+r[n>>6&63]+r[63&n]);return i.join("")}(e,a,a+16383>o?o:a+16383));return 1===n?i.push(r[(t=e[s-1])>>2]+r[t<<4&63]+"=="):2===n&&i.push(r[(t=(e[s-2]<<8)+e[s-1])>>10]+r[t>>4&63]+r[t<<2&63]+"="),i.join("")};for(var r=[],s=[],n="u">typeof Uint8Array?Uint8Array:Array,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0,o=i.length;a0)throw Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");-1===r&&(r=t);var s=r===t?0:4-r%4;return[r,s]}s[45]=62,s[95]=63},72:function(e,t,r){"use strict";var s=r(675),n=r(783),i="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;function a(e){if(e>0x7fffffff)throw RangeError('The value "'+e+'" is invalid for option "size"');var t=new Uint8Array(e);return Object.setPrototypeOf(t,o.prototype),t}function o(e,t,r){if("number"==typeof e){if("string"==typeof t)throw TypeError('The "string" argument must be of type string. Received type number');return c(e)}return l(e,t,r)}function l(e,t,r){if("string"==typeof e){var s=e,n=t;if(("string"!=typeof n||""===n)&&(n="utf8"),!o.isEncoding(n))throw TypeError("Unknown encoding: "+n);var i=0|d(s,n),l=a(i),u=l.write(s,n);return u!==i&&(l=l.slice(0,u)),l}if(ArrayBuffer.isView(e))return h(e);if(null==e)throw TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);if($(e,ArrayBuffer)||e&&$(e.buffer,ArrayBuffer)||"u">typeof SharedArrayBuffer&&($(e,SharedArrayBuffer)||e&&$(e.buffer,SharedArrayBuffer)))return function(e,t,r){var s;if(t<0||e.byteLengthtypeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof e[Symbol.toPrimitive])return o.from(e[Symbol.toPrimitive]("string"),t,r);throw TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e)}function u(e){if("number"!=typeof e)throw TypeError('"size" argument must be of type number');if(e<0)throw RangeError('The value "'+e+'" is invalid for option "size"')}function c(e){return u(e),a(e<0?0:0|f(e))}function h(e){for(var t=e.length<0?0:0|f(e.length),r=a(t),s=0;stypeof console&&"function"==typeof console.error&&console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support."),Object.defineProperty(o.prototype,"parent",{enumerable:!0,get:function(){if(o.isBuffer(this))return this.buffer}}),Object.defineProperty(o.prototype,"offset",{enumerable:!0,get:function(){if(o.isBuffer(this))return this.byteOffset}}),o.poolSize=8192,o.from=function(e,t,r){return l(e,t,r)},Object.setPrototypeOf(o.prototype,Uint8Array.prototype),Object.setPrototypeOf(o,Uint8Array),o.alloc=function(e,t,r){return(u(e),e<=0)?a(e):void 0!==t?"string"==typeof r?a(e).fill(t,r):a(e).fill(t):a(e)},o.allocUnsafe=function(e){return c(e)},o.allocUnsafeSlow=function(e){return c(e)};function f(e){if(e>=0x7fffffff)throw RangeError("Attempt to allocate Buffer larger than maximum size: 0x7fffffff bytes");return 0|e}function d(e,t){if(o.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||$(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);var r=e.length,s=arguments.length>2&&!0===arguments[2];if(!s&&0===r)return 0;for(var n=!1;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":return E(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return R(e).length;default:if(n)return s?-1:E(e).length;t=(""+t).toLowerCase(),n=!0}}function p(e,t,r){var n,i,a,o=!1;if((void 0===t||t<0)&&(t=0),t>this.length||((void 0===r||r>this.length)&&(r=this.length),r<=0||(r>>>=0)<=(t>>>=0)))return"";for(e||(e="utf8");;)switch(e){case"hex":return function(e,t,r){var s=e.length;(!t||t<0)&&(t=0),(!r||r<0||r>s)&&(r=s);for(var n="",i=t;i0x7fffffff?r=0x7fffffff:r<-0x80000000&&(r=-0x80000000),(i=r*=1)!=i&&(r=n?0:e.length-1),r<0&&(r=e.length+r),r>=e.length)if(n)return -1;else r=e.length-1;else if(r<0)if(!n)return -1;else r=0;if("string"==typeof t&&(t=o.from(t,s)),o.isBuffer(t))return 0===t.length?-1:y(e,t,r,s,n);if("number"==typeof t){if(t&=255,"function"==typeof Uint8Array.prototype.indexOf)if(n)return Uint8Array.prototype.indexOf.call(e,t,r);else return Uint8Array.prototype.lastIndexOf.call(e,t,r);return y(e,[t],r,s,n)}throw TypeError("val must be string, number or Buffer")}function y(e,t,r,s,n){var i,a=1,o=e.length,l=t.length;if(void 0!==s&&("ucs2"===(s=String(s).toLowerCase())||"ucs-2"===s||"utf16le"===s||"utf-16le"===s)){if(e.length<2||t.length<2)return -1;a=2,o/=2,l/=2,r/=2}function u(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(n){var c=-1;for(i=r;io&&(r=o-l),i=r;i>=0;i--){for(var h=!0,f=0;fr&&(e+=" ... "),""},i&&(o.prototype[i]=o.prototype.inspect),o.prototype.compare=function(e,t,r,s,n){if($(e,Uint8Array)&&(e=o.from(e,e.offset,e.byteLength)),!o.isBuffer(e))throw TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===s&&(s=0),void 0===n&&(n=this.length),t<0||r>e.length||s<0||n>this.length)throw RangeError("out of range index");if(s>=n&&t>=r)return 0;if(s>=n)return -1;if(t>=r)return 1;if(t>>>=0,r>>>=0,s>>>=0,n>>>=0,this===e)return 0;for(var i=n-s,a=r-t,l=Math.min(i,a),u=this.slice(s,n),c=e.slice(t,r),h=0;h239?4:u>223?3:u>191?2:1;if(n+h<=r)switch(h){case 1:u<128&&(c=u);break;case 2:(192&(i=e[n+1]))==128&&(l=(31&u)<<6|63&i)>127&&(c=l);break;case 3:i=e[n+1],a=e[n+2],(192&i)==128&&(192&a)==128&&(l=(15&u)<<12|(63&i)<<6|63&a)>2047&&(l<55296||l>57343)&&(c=l);break;case 4:i=e[n+1],a=e[n+2],o=e[n+3],(192&i)==128&&(192&a)==128&&(192&o)==128&&(l=(15&u)<<18|(63&i)<<12|(63&a)<<6|63&o)>65535&&l<1114112&&(c=l)}null===c?(c=65533,h=1):c>65535&&(c-=65536,s.push(c>>>10&1023|55296),c=56320|1023&c),s.push(c),n+=h}var f=s,d=f.length;if(d<=4096)return String.fromCharCode.apply(String,f);for(var p="",m=0;mr)throw RangeError("Trying to access beyond buffer length")}function _(e,t,r,s,n,i){if(!o.isBuffer(e))throw TypeError('"buffer" argument must be a Buffer instance');if(t>n||te.length)throw RangeError("Index out of range")}function v(e,t,r,s,n,i){if(r+s>e.length||r<0)throw RangeError("Index out of range")}function x(e,t,r,s,i){return t*=1,r>>>=0,i||v(e,t,r,4,34028234663852886e22,-34028234663852886e22),n.write(e,t,r,s,23,4),r+4}function A(e,t,r,s,i){return t*=1,r>>>=0,i||v(e,t,r,8,17976931348623157e292,-17976931348623157e292),n.write(e,t,r,s,52,8),r+8}o.prototype.write=function(e,t,r,s){if(void 0===t)s="utf8",r=this.length,t=0;else if(void 0===r&&"string"==typeof t)s=t,r=this.length,t=0;else if(isFinite(t))t>>>=0,isFinite(r)?(r>>>=0,void 0===s&&(s="utf8")):(s=r,r=void 0);else throw Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");var n,i,a,o,l,u,c,h,f=this.length-t;if((void 0===r||r>f)&&(r=f),e.length>0&&(r<0||t<0)||t>this.length)throw RangeError("Attempt to write outside buffer bounds");s||(s="utf8");for(var d=!1;;)switch(s){case"hex":return function(e,t,r,s){r=Number(r)||0;var n=e.length-r;s?(s=Number(s))>n&&(s=n):s=n;var i=t.length;s>i/2&&(s=i/2);for(var a=0;a>8,n.push(r%256),n.push(s);return n}(e,this.length-c),this,c,h);default:if(d)throw TypeError("Unknown encoding: "+s);s=(""+s).toLowerCase(),d=!0}},o.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},o.prototype.slice=function(e,t){var r=this.length;e=~~e,t=void 0===t?r:~~t,e<0?(e+=r)<0&&(e=0):e>r&&(e=r),t<0?(t+=r)<0&&(t=0):t>r&&(t=r),t>>=0,t>>>=0,r||b(e,t,this.length);for(var s=this[e],n=1,i=0;++i>>=0,t>>>=0,r||b(e,t,this.length);for(var s=this[e+--t],n=1;t>0&&(n*=256);)s+=this[e+--t]*n;return s},o.prototype.readUInt8=function(e,t){return e>>>=0,t||b(e,1,this.length),this[e]},o.prototype.readUInt16LE=function(e,t){return e>>>=0,t||b(e,2,this.length),this[e]|this[e+1]<<8},o.prototype.readUInt16BE=function(e,t){return e>>>=0,t||b(e,2,this.length),this[e]<<8|this[e+1]},o.prototype.readUInt32LE=function(e,t){return e>>>=0,t||b(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+0x1000000*this[e+3]},o.prototype.readUInt32BE=function(e,t){return e>>>=0,t||b(e,4,this.length),0x1000000*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},o.prototype.readIntLE=function(e,t,r){e>>>=0,t>>>=0,r||b(e,t,this.length);for(var s=this[e],n=1,i=0;++i=(n*=128)&&(s-=Math.pow(2,8*t)),s},o.prototype.readIntBE=function(e,t,r){e>>>=0,t>>>=0,r||b(e,t,this.length);for(var s=t,n=1,i=this[e+--s];s>0&&(n*=256);)i+=this[e+--s]*n;return i>=(n*=128)&&(i-=Math.pow(2,8*t)),i},o.prototype.readInt8=function(e,t){return(e>>>=0,t||b(e,1,this.length),128&this[e])?-((255-this[e]+1)*1):this[e]},o.prototype.readInt16LE=function(e,t){e>>>=0,t||b(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?0xffff0000|r:r},o.prototype.readInt16BE=function(e,t){e>>>=0,t||b(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?0xffff0000|r:r},o.prototype.readInt32LE=function(e,t){return e>>>=0,t||b(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},o.prototype.readInt32BE=function(e,t){return e>>>=0,t||b(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},o.prototype.readFloatLE=function(e,t){return e>>>=0,t||b(e,4,this.length),n.read(this,e,!0,23,4)},o.prototype.readFloatBE=function(e,t){return e>>>=0,t||b(e,4,this.length),n.read(this,e,!1,23,4)},o.prototype.readDoubleLE=function(e,t){return e>>>=0,t||b(e,8,this.length),n.read(this,e,!0,52,8)},o.prototype.readDoubleBE=function(e,t){return e>>>=0,t||b(e,8,this.length),n.read(this,e,!1,52,8)},o.prototype.writeUIntLE=function(e,t,r,s){if(e*=1,t>>>=0,r>>>=0,!s){var n=Math.pow(2,8*r)-1;_(this,e,t,r,n,0)}var i=1,a=0;for(this[t]=255&e;++a>>=0,r>>>=0,!s){var n=Math.pow(2,8*r)-1;_(this,e,t,r,n,0)}var i=r-1,a=1;for(this[t+i]=255&e;--i>=0&&(a*=256);)this[t+i]=e/a&255;return t+r},o.prototype.writeUInt8=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,1,255,0),this[t]=255&e,t+1},o.prototype.writeUInt16LE=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},o.prototype.writeUInt16BE=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},o.prototype.writeUInt32LE=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,4,0xffffffff,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},o.prototype.writeUInt32BE=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,4,0xffffffff,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},o.prototype.writeIntLE=function(e,t,r,s){if(e*=1,t>>>=0,!s){var n=Math.pow(2,8*r-1);_(this,e,t,r,n-1,-n)}var i=0,a=1,o=0;for(this[t]=255&e;++i>>=0,!s){var n=Math.pow(2,8*r-1);_(this,e,t,r,n-1,-n)}var i=r-1,a=1,o=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===o&&0!==this[t+i+1]&&(o=1),this[t+i]=(e/a|0)-o&255;return t+r},o.prototype.writeInt8=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},o.prototype.writeInt16LE=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},o.prototype.writeInt16BE=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},o.prototype.writeInt32LE=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,4,0x7fffffff,-0x80000000),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},o.prototype.writeInt32BE=function(e,t,r){return e*=1,t>>>=0,r||_(this,e,t,4,0x7fffffff,-0x80000000),e<0&&(e=0xffffffff+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},o.prototype.writeFloatLE=function(e,t,r){return x(this,e,t,!0,r)},o.prototype.writeFloatBE=function(e,t,r){return x(this,e,t,!1,r)},o.prototype.writeDoubleLE=function(e,t,r){return A(this,e,t,!0,r)},o.prototype.writeDoubleBE=function(e,t,r){return A(this,e,t,!1,r)},o.prototype.copy=function(e,t,r,s){if(!o.isBuffer(e))throw TypeError("argument should be a Buffer");if(r||(r=0),s||0===s||(s=this.length),t>=e.length&&(t=e.length),t||(t=0),s>0&&s=this.length)throw RangeError("Index out of range");if(s<0)throw RangeError("sourceEnd out of bounds");s>this.length&&(s=this.length),e.length-t=0;--i)e[i+t]=this[i+r];else Uint8Array.prototype.set.call(e,this.subarray(r,s),t);return n},o.prototype.fill=function(e,t,r,s){if("string"==typeof e){if("string"==typeof t?(s=t,t=0,r=this.length):"string"==typeof r&&(s=r,r=this.length),void 0!==s&&"string"!=typeof s)throw TypeError("encoding must be a string");if("string"==typeof s&&!o.isEncoding(s))throw TypeError("Unknown encoding: "+s);if(1===e.length){var n,i=e.charCodeAt(0);("utf8"===s&&i<128||"latin1"===s)&&(e=i)}}else"number"==typeof e?e&=255:"boolean"==typeof e&&(e=Number(e));if(t<0||this.length>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(n=t;n55295&&r<57344){if(!n){if(r>56319||a+1===s){(t-=3)>-1&&i.push(239,191,189);continue}n=r;continue}if(r<56320){(t-=3)>-1&&i.push(239,191,189),n=r;continue}r=(n-55296<<10|r-56320)+65536}else n&&(t-=3)>-1&&i.push(239,191,189);if(n=null,r<128){if((t-=1)<0)break;i.push(r)}else if(r<2048){if((t-=2)<0)break;i.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;i.push(r>>12|224,r>>6&63|128,63&r|128)}else if(r<1114112){if((t-=4)<0)break;i.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}else throw Error("Invalid code point")}return i}function P(e){for(var t=[],r=0;r=t.length)&&!(n>=e.length);++n)t[n+r]=e[n];return n}function $(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}var O=function(){for(var e="0123456789abcdef",t=Array(256),r=0;r<16;++r)for(var s=16*r,n=0;n<16;++n)t[s+n]=e[r]+e[n];return t}()},783:function(e,t){t.read=function(e,t,r,s,n){var i,a,o=8*n-s-1,l=(1<>1,c=-7,h=r?n-1:0,f=r?-1:1,d=e[t+h];for(h+=f,i=d&(1<<-c)-1,d>>=-c,c+=o;c>0;i=256*i+e[t+h],h+=f,c-=8);for(a=i&(1<<-c)-1,i>>=-c,c+=s;c>0;a=256*a+e[t+h],h+=f,c-=8);if(0===i)i=1-u;else{if(i===l)return a?NaN:1/0*(d?-1:1);a+=Math.pow(2,s),i-=u}return(d?-1:1)*a*Math.pow(2,i-s)},t.write=function(e,t,r,s,n,i){var a,o,l,u=8*i-n-1,c=(1<>1,f=5960464477539062e-23*(23===n),d=s?0:i-1,p=s?1:-1,m=+(t<0||0===t&&1/t<0);for(isNaN(t=Math.abs(t))||t===1/0?(o=+!!isNaN(t),a=c):(a=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-a))<1&&(a--,l*=2),a+h>=1?t+=f/l:t+=f*Math.pow(2,1-h),t*l>=2&&(a++,l/=2),a+h>=c?(o=0,a=c):a+h>=1?(o=(t*l-1)*Math.pow(2,n),a+=h):(o=t*Math.pow(2,h-1)*Math.pow(2,n),a=0));n>=8;e[r+d]=255&o,d+=p,o/=256,n-=8);for(a=a<0;e[r+d]=255&a,d+=p,a/=256,u-=8);e[r+d-p]|=128*m}}},n={};function i(e){var t=n[e];if(void 0!==t)return t.exports;var r=n[e]={exports:{}},a=!0;try{s[e](r,r.exports,i),a=!1}finally{a&&delete n[e]}return r.exports}i.ab="/ROOT/node_modules/next/dist/compiled/buffer/",t.exports=i(72)},356449,e=>{"use strict";let t,r,s,n,i,a,o,l,u,c;var h,f,d,p,m,g,y,w,b,_,v,x,A,S,E,P,R,I,$,O,C,k,T,B,M,j,L,N,U,D,F,W,q,X,J,H,V,K,z,Q,Y,G,Z,ee,et,er,es,en,ei,ea,eo,el,eu,ec,eh,ef,ed,ep,em,eg,ey,ew,eb,e_,ev,ex=e.i(247167);let eA="RFC3986",eS={RFC1738:e=>String(e).replace(/%20/g,"+"),RFC3986:e=>String(e)};Object.prototype.hasOwnProperty;let eE=Array.isArray,eP=(()=>{let e=[];for(let t=0;t<256;++t)e.push("%"+((t<16?"0":"")+t.toString(16)).toUpperCase());return e})();function eR(e,t){if(eE(e)){let r=[];for(let s=0;sString(e)+"[]",comma:"comma",indices:(e,t)=>String(e)+"["+t+"]",repeat:e=>String(e)},eO=Array.isArray,eC=Array.prototype.push,ek=function(e,t){eC.apply(e,eO(t)?t:[t])},eT=Date.prototype.toISOString,eB={addQueryPrefix:!1,allowDots:!1,allowEmptyArrays:!1,arrayFormat:"indices",charset:"utf-8",charsetSentinel:!1,delimiter:"&",encode:!0,encodeDotInKeys:!1,encoder:(e,t,r,s,n)=>{if(0===e.length)return e;let i=e;if("symbol"==typeof e?i=Symbol.prototype.toString.call(e):"string"!=typeof e&&(i=String(e)),"iso-8859-1"===r)return escape(i).replace(/%u[0-9a-f]{4}/gi,function(e){return"%26%23"+parseInt(e.slice(2),16)+"%3B"});let a="";for(let e=0;e=1024?i.slice(e,e+1024):i,r=[];for(let e=0;e=48&&s<=57||s>=65&&s<=90||s>=97&&s<=122||"RFC1738"===n&&(40===s||41===s)){r[r.length]=t.charAt(e);continue}if(s<128){r[r.length]=eP[s];continue}if(s<2048){r[r.length]=eP[192|s>>6]+eP[128|63&s];continue}if(s<55296||s>=57344){r[r.length]=eP[224|s>>12]+eP[128|s>>6&63]+eP[128|63&s];continue}e+=1,s=65536+((1023&s)<<10|1023&t.charCodeAt(e)),r[r.length]=eP[240|s>>18]+eP[128|s>>12&63]+eP[128|s>>6&63]+eP[128|63&s]}a+=r.join("")}return a},encodeValuesOnly:!1,format:eA,formatter:eS[eA],indices:!1,serializeDate:e=>eT.call(e),skipNulls:!1,strictNullHandling:!1},eM={};var ej=e.i(467034);let eL="4.104.0",eN=!1;class eU{constructor(e){this.body=e}get[Symbol.toStringTag](){return"MultipartBody"}}let eD=()=>{r||function(e,t={auto:!1}){if(eN)throw Error(`you must \`import 'openai/shims/${e.kind}'\` before importing anything else from openai`);if(r)throw Error(`can't \`import 'openai/shims/${e.kind}'\` after \`import 'openai/shims/${r}'\``);eN=t.auto,r=e.kind,s=e.fetch,e.Request,e.Response,e.Headers,n=e.FormData,e.Blob,i=e.File,a=e.ReadableStream,o=e.getMultipartRequestOptions,l=e.getDefaultAgent,u=e.fileFromPath,c=e.isFsReadStream}(function({manuallyImported:e}={}){let t,r,s,n,i=e?"You may need to use polyfills":"Add one of these imports before your first `import … from 'openai'`:\n- `import 'openai/shims/node'` (if you're running on Node)\n- `import 'openai/shims/web'` (otherwise)\n";try{t=fetch,r=Request,s=Response,n=Headers}catch(e){throw Error(`this environment is missing the following Web Fetch API type: ${e.message}. ${i}`)}return{kind:"web",fetch:t,Request:r,Response:s,Headers:n,FormData:"u">typeof FormData?FormData:class{constructor(){throw Error(`file uploads aren't supported in this environment yet as 'FormData' is undefined. ${i}`)}},Blob:"u">typeof Blob?Blob:class{constructor(){throw Error(`file uploads aren't supported in this environment yet as 'Blob' is undefined. ${i}`)}},File:"u">typeof File?File:class{constructor(){throw Error(`file uploads aren't supported in this environment yet as 'File' is undefined. ${i}`)}},ReadableStream:"u">typeof ReadableStream?ReadableStream:class{constructor(){throw Error(`streaming isn't supported in this environment yet as 'ReadableStream' is undefined. ${i}`)}},getMultipartRequestOptions:async(e,t)=>({...t,body:new eU(e)}),getDefaultAgent:e=>void 0,fileFromPath:()=>{throw Error("The `fileFromPath` function is only supported in Node. See the README for more details: https://www.github.com/openai/openai-node#file-uploads")},isFsReadStream:e=>!1}}(),{auto:!0})};eD();class eF extends Error{}class eW extends eF{constructor(e,t,r,s){super(`${eW.makeMessage(e,t,r)}`),this.status=e,this.headers=s,this.request_id=s?.["x-request-id"],this.error=t,this.code=t?.code,this.param=t?.param,this.type=t?.type}static makeMessage(e,t,r){let s=t?.message?"string"==typeof t.message?t.message:JSON.stringify(t.message):t?JSON.stringify(t):r;return e&&s?`${e} ${s}`:e?`${e} status code (no body)`:s||"(no status code or body)"}static generate(e,t,r,s){if(!e||!s)return new eX({message:r,cause:tO(t)});let n=t?.error;return 400===e?new eH(e,n,r,s):401===e?new eV(e,n,r,s):403===e?new eK(e,n,r,s):404===e?new ez(e,n,r,s):409===e?new eQ(e,n,r,s):422===e?new eY(e,n,r,s):429===e?new eG(e,n,r,s):e>=500?new eZ(e,n,r,s):new eW(e,n,r,s)}}class eq extends eW{constructor({message:e}={}){super(void 0,void 0,e||"Request was aborted.",void 0)}}class eX extends eW{constructor({message:e,cause:t}){super(void 0,void 0,e||"Connection error.",void 0),t&&(this.cause=t)}}class eJ extends eX{constructor({message:e}={}){super({message:e??"Request timed out."})}}class eH extends eW{}class eV extends eW{}class eK extends eW{}class ez extends eW{}class eQ extends eW{}class eY extends eW{}class eG extends eW{}class eZ extends eW{}class e0 extends eF{constructor(){super("Could not parse response content as the length limit was reached")}}class e1 extends eF{constructor(){super("Could not parse response content as the request was rejected by the content filter")}}var e2=function(e,t,r,s,n){if("m"===s)throw TypeError("Private method is not writable");if("a"===s&&!n)throw TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!n:!t.has(e))throw TypeError("Cannot write private member to an object whose class did not declare it");return"a"===s?n.call(e,r):n?n.value=r:t.set(e,r),r},e8=function(e,t,r,s){if("a"===r&&!s)throw TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!s:!t.has(e))throw TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(e):s?s.value:t.get(e)};class e6{constructor(){h.set(this,void 0),this.buffer=new Uint8Array,e2(this,h,null,"f")}decode(e){let t;if(null==e)return[];let r=e instanceof ArrayBuffer?new Uint8Array(e):"string"==typeof e?new TextEncoder().encode(e):e,s=new Uint8Array(this.buffer.length+r.length);s.set(this.buffer),s.set(r,this.buffer.length),this.buffer=s;let n=[];for(;null!=(t=function(e,t){for(let r=t??0;rtypeof TextDecoder){if(e instanceof Uint8Array||e instanceof ArrayBuffer)return this.textDecoder??(this.textDecoder=new TextDecoder("utf8")),this.textDecoder.decode(e);throw new eF(`Unexpected: received non-Uint8Array/ArrayBuffer (${e.constructor.name}) in a web platform. Please report this error.`)}throw new eF("Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.")}flush(){return this.buffer.length?this.decode("\n"):[]}}function e5(e){if(e[Symbol.asyncIterator])return e;let t=e.getReader();return{async next(){try{let e=await t.read();return e?.done&&t.releaseLock(),e}catch(e){throw t.releaseLock(),e}},async return(){let e=t.cancel();return t.releaseLock(),await e,{done:!0,value:void 0}},[Symbol.asyncIterator](){return this}}}h=new WeakMap,e6.NEWLINE_CHARS=new Set(["\n","\r"]),e6.NEWLINE_REGEXP=/\r\n|[\n\r]/g;class e3{constructor(e,t){this.iterator=e,this.controller=t}static fromSSEResponse(e,t){let r=!1;async function*s(){if(r)throw Error("Cannot iterate over a consumed stream, use `.tee()` to split the stream.");r=!0;let s=!1;try{for await(let r of e4(e,t))if(!s){if(r.data.startsWith("[DONE]")){s=!0;continue}if(null===r.event||r.event.startsWith("response.")||r.event.startsWith("transcript.")){let t;try{t=JSON.parse(r.data)}catch(e){throw console.error("Could not parse message into JSON:",r.data),console.error("From chunk:",r.raw),e}if(t&&t.error)throw new eW(void 0,t.error,void 0,tb(e.headers));yield t}else{let e;try{e=JSON.parse(r.data)}catch(e){throw console.error("Could not parse message into JSON:",r.data),console.error("From chunk:",r.raw),e}if("error"==r.event)throw new eW(void 0,e.error,e.message,void 0);yield{event:r.event,data:e}}}s=!0}catch(e){if(e instanceof Error&&"AbortError"===e.name)return;throw e}finally{s||t.abort()}}return new e3(s,t)}static fromReadableStream(e,t){let r=!1;async function*s(){let t=new e6;for await(let r of e5(e))for(let e of t.decode(r))yield e;for(let e of t.flush())yield e}return new e3(async function*(){if(r)throw Error("Cannot iterate over a consumed stream, use `.tee()` to split the stream.");r=!0;let e=!1;try{for await(let t of s())!e&&t&&(yield JSON.parse(t));e=!0}catch(e){if(e instanceof Error&&"AbortError"===e.name)return;throw e}finally{e||t.abort()}},t)}[Symbol.asyncIterator](){return this.iterator()}tee(){let e=[],t=[],r=this.iterator(),s=s=>({next:()=>{if(0===s.length){let s=r.next();e.push(s),t.push(s)}return s.shift()}});return[new e3(()=>s(e),this.controller),new e3(()=>s(t),this.controller)]}toReadableStream(){let e,t=this,r=new TextEncoder;return new a({async start(){e=t[Symbol.asyncIterator]()},async pull(t){try{let{value:s,done:n}=await e.next();if(n)return t.close();let i=r.encode(JSON.stringify(s)+"\n");t.enqueue(i)}catch(e){t.error(e)}},async cancel(){await e.return?.()}})}}async function*e4(e,t){if(!e.body)throw t.abort(),new eF("Attempted to iterate over a response with no body");let r=new e7,s=new e6;for await(let t of e9(e5(e.body)))for(let e of s.decode(t)){let t=r.decode(e);t&&(yield t)}for(let e of s.flush()){let t=r.decode(e);t&&(yield t)}}async function*e9(e){let t=new Uint8Array;for await(let r of e){let e;if(null==r)continue;let s=r instanceof ArrayBuffer?new Uint8Array(r):"string"==typeof r?new TextEncoder().encode(r):r,n=new Uint8Array(t.length+s.length);for(n.set(t),n.set(s,t.length),t=n;-1!==(e=function(e){for(let t=0;t0&&(yield t)}class e7{constructor(){this.event=null,this.data=[],this.chunks=[]}decode(e){var t;let r;if(e.endsWith("\r")&&(e=e.substring(0,e.length-1)),!e){if(!this.event&&!this.data.length)return null;let e={event:this.event,data:this.data.join("\n"),raw:this.chunks};return this.event=null,this.data=[],this.chunks=[],e}if(this.chunks.push(e),e.startsWith(":"))return null;let[s,n,i]=-1!==(r=(t=e).indexOf(":"))?[t.substring(0,r),":",t.substring(r+1)]:[t,"",""];return i.startsWith(" ")&&(i=i.substring(1)),"event"===s?this.event=i:"data"===s&&this.data.push(i),null}}let te=e=>null!=e&&"object"==typeof e&&"string"==typeof e.url&&"function"==typeof e.blob,tt=e=>null!=e&&"object"==typeof e&&"string"==typeof e.name&&"number"==typeof e.lastModified&&tr(e),tr=e=>null!=e&&"object"==typeof e&&"number"==typeof e.size&&"string"==typeof e.type&&"function"==typeof e.text&&"function"==typeof e.slice&&"function"==typeof e.arrayBuffer;async function ts(e,t,r){var s;if(tt(e=await e))return e;if(te(e)){let s=await e.blob();t||(t=new URL(e.url).pathname.split(/[\\/]/).pop()??"unknown_file");let n=tr(s)?[await s.arrayBuffer()]:[s];return new i(n,t,r)}let n=await tn(e);if(t||(t=(ti((s=e).name)||ti(s.filename)||ti(s.path)?.split(/[\\/]/).pop())??"unknown_file"),!r?.type){let e=n[0]?.type;"string"==typeof e&&(r={...r,type:e})}return new i(n,t,r)}async function tn(e){let t=[];if("string"==typeof e||ArrayBuffer.isView(e)||e instanceof ArrayBuffer)t.push(e);else if(tr(e))t.push(await e.arrayBuffer());else if(ta(e))for await(let r of e)t.push(r);else{let t;throw Error(`Unexpected data type: ${typeof e}; constructor: ${e?.constructor?.name}; props: ${(t=Object.getOwnPropertyNames(e),`[${t.map(e=>`"${e}"`).join(", ")}]`)}`)}return t}let ti=e=>"string"==typeof e?e:void 0!==ej.Buffer&&e instanceof ej.Buffer?String(e):void 0,ta=e=>null!=e&&"object"==typeof e&&"function"==typeof e[Symbol.asyncIterator],to=e=>e&&"object"==typeof e&&e.body&&"MultipartBody"===e[Symbol.toStringTag],tl=async e=>{let t=await tu(e.body);return o(t,e)},tu=async e=>{let t=new n;return await Promise.all(Object.entries(e||{}).map(([e,r])=>tc(t,e,r))),t},tc=async(e,t,r)=>{if(void 0!==r){if(null==r)throw TypeError(`Received null for "${t}"; to pass null in FormData, you must use the string 'null'`);if("string"==typeof r||"number"==typeof r||"boolean"==typeof r)e.append(t,String(r));else{let s;if(tt(s=r)||te(s)||c(s)){let s=await ts(r);e.append(t,s)}else if(Array.isArray(r))await Promise.all(r.map(r=>tc(e,t+"[]",r)));else if("object"==typeof r)await Promise.all(Object.entries(r).map(([r,s])=>tc(e,`${t}[${r}]`,s)));else throw TypeError(`Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${r} instead`)}}};var th=function(e,t,r,s,n){if("m"===s)throw TypeError("Private method is not writable");if("a"===s&&!n)throw TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!n:!t.has(e))throw TypeError("Cannot write private member to an object whose class did not declare it");return"a"===s?n.call(e,r):n?n.value=r:t.set(e,r),r},tf=function(e,t,r,s){if("a"===r&&!s)throw TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!s:!t.has(e))throw TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(e):s?s.value:t.get(e)};async function td(e){let{response:t}=e;if(e.options.stream)return(tj("response",t.status,t.url,t.headers,t.body),e.options.__streamClass)?e.options.__streamClass.fromSSEResponse(t,e.controller):e3.fromSSEResponse(t,e.controller);if(204===t.status)return null;if(e.options.__binaryResponse)return t;let r=t.headers.get("content-type"),s=r?.split(";")[0]?.trim();if(s?.includes("application/json")||s?.endsWith("+json")){let e=await t.json();return tj("response",t.status,t.url,t.headers,e),tp(e,t)}let n=await t.text();return tj("response",t.status,t.url,t.headers,n),n}function tp(e,t){return!e||"object"!=typeof e||Array.isArray(e)?e:Object.defineProperty(e,"_request_id",{value:t.headers.get("x-request-id"),enumerable:!1})}eD();class tm extends Promise{constructor(e,t=td){super(e=>{e(null)}),this.responsePromise=e,this.parseResponse=t}_thenUnwrap(e){return new tm(this.responsePromise,async t=>tp(e(await this.parseResponse(t),t),t.response))}asResponse(){return this.responsePromise.then(e=>e.response)}async withResponse(){let[e,t]=await Promise.all([this.parse(),this.asResponse()]);return{data:e,response:t,request_id:t.headers.get("x-request-id")}}parse(){return this.parsedPromise||(this.parsedPromise=this.responsePromise.then(this.parseResponse)),this.parsedPromise}then(e,t){return this.parse().then(e,t)}catch(e){return this.parse().catch(e)}finally(e){return this.parse().finally(e)}}class tg{constructor({baseURL:e,maxRetries:t=2,timeout:r=6e5,httpAgent:n,fetch:i}){this.baseURL=e,this.maxRetries=t$("maxRetries",t),this.timeout=t$("timeout",r),this.httpAgent=n,this.fetch=i??s}authHeaders(e){return{}}defaultHeaders(e){return{Accept:"application/json","Content-Type":"application/json","User-Agent":this.getUserAgent(),...tS(),...this.authHeaders(e)}}validateHeaders(e,t){}defaultIdempotencyKey(){return`stainless-node-retry-${tL()}`}get(e,t){return this.methodRequest("get",e,t)}post(e,t){return this.methodRequest("post",e,t)}patch(e,t){return this.methodRequest("patch",e,t)}put(e,t){return this.methodRequest("put",e,t)}delete(e,t){return this.methodRequest("delete",e,t)}methodRequest(e,t,r){return this.request(Promise.resolve(r).then(async r=>{let s=r&&tr(r?.body)?new DataView(await r.body.arrayBuffer()):r?.body instanceof DataView?r.body:r?.body instanceof ArrayBuffer?new DataView(r.body):r&&ArrayBuffer.isView(r?.body)?new DataView(r.body.buffer):r?.body;return{method:e,path:t,...r,body:s}}))}getAPIList(e,t,r){return this.requestAPIList(t,{method:"get",path:e,...r})}calculateContentLength(e){if("string"==typeof e){if(void 0!==ej.Buffer)return ej.Buffer.byteLength(e,"utf8").toString();if("u">typeof TextEncoder)return new TextEncoder().encode(e).length.toString()}else if(ArrayBuffer.isView(e))return e.byteLength.toString();return null}buildRequest(e,{retryCount:t=0}={}){let r={...e},{method:s,path:n,query:i,headers:a={}}=r,o=ArrayBuffer.isView(r.body)||r.__binaryRequest&&"string"==typeof r.body?r.body:to(r.body)?r.body.body:r.body?JSON.stringify(r.body,null,2):null,u=this.calculateContentLength(o),c=this.buildURL(n,i);"timeout"in r&&t$("timeout",r.timeout),r.timeout=r.timeout??this.timeout;let h=r.httpAgent??this.httpAgent??l(c),f=r.timeout+1e3;"number"==typeof h?.options?.timeout&&f>(h.options.timeout??0)&&(h.options.timeout=f),this.idempotencyHeader&&"get"!==s&&(e.idempotencyKey||(e.idempotencyKey=this.defaultIdempotencyKey()),a[this.idempotencyHeader]=e.idempotencyKey);let d=this.buildHeaders({options:r,headers:a,contentLength:u,retryCount:t});return{req:{method:s,...o&&{body:o},headers:d,...h&&{agent:h},signal:r.signal??null},url:c,timeout:r.timeout}}buildHeaders({options:e,headers:t,contentLength:s,retryCount:n}){let i={};s&&(i["content-length"]=s);let a=this.defaultHeaders(e);return tB(i,a),tB(i,t),to(e.body)&&"node"!==r&&delete i["content-type"],void 0===tN(a,"x-stainless-retry-count")&&void 0===tN(t,"x-stainless-retry-count")&&(i["x-stainless-retry-count"]=String(n)),void 0===tN(a,"x-stainless-timeout")&&void 0===tN(t,"x-stainless-timeout")&&e.timeout&&(i["x-stainless-timeout"]=String(Math.trunc(e.timeout/1e3))),this.validateHeaders(i,t),i}async prepareOptions(e){}async prepareRequest(e,{url:t,options:r}){}parseHeaders(e){return e?Symbol.iterator in e?Object.fromEntries(Array.from(e).map(e=>[...e])):{...e}:{}}makeStatusError(e,t,r,s){return eW.generate(e,t,r,s)}request(e,t=null){return new tm(this.makeRequest(e,t))}async makeRequest(e,t){let r=await e,s=r.maxRetries??this.maxRetries;null==t&&(t=s),await this.prepareOptions(r);let{req:n,url:i,timeout:a}=this.buildRequest(r,{retryCount:s-t});if(await this.prepareRequest(n,{url:i,options:r}),tj("request",i,r,n.headers),r.signal?.aborted)throw new eq;let o=new AbortController,l=await this.fetchWithTimeout(i,n,a,o).catch(tO);if(l instanceof Error){if(r.signal?.aborted)throw new eq;if(t)return this.retryRequest(r,t);if("AbortError"===l.name)throw new eJ;throw new eX({cause:l})}let u=tb(l.headers);if(!l.ok){if(t&&this.shouldRetry(l)){let e=`retrying, ${t} attempts remaining`;return tj(`response (error; ${e})`,l.status,i,u),this.retryRequest(r,t,u)}let e=await l.text().catch(e=>tO(e).message),s=tE(e),n=s?void 0:e,a=t?"(error; no more retries left)":"(error; not retryable)";throw tj(`response (error; ${a})`,l.status,i,u,n),this.makeStatusError(l.status,s,n,u)}return{response:l,options:r,controller:o}}requestAPIList(e,t){return new tw(this,this.makeRequest(t,null),e)}buildURL(e,t){let r=new URL(tR(e)?e:this.baseURL+(this.baseURL.endsWith("/")&&e.startsWith("/")?e.slice(1):e)),s=this.defaultQuery();return tk(s)||(t={...s,...t}),"object"==typeof t&&t&&!Array.isArray(t)&&(r.search=this.stringifyQuery(t)),r.toString()}stringifyQuery(e){return Object.entries(e).filter(([e,t])=>void 0!==t).map(([e,t])=>{if("string"==typeof t||"number"==typeof t||"boolean"==typeof t)return`${encodeURIComponent(e)}=${encodeURIComponent(t)}`;if(null===t)return`${encodeURIComponent(e)}=`;throw new eF(`Cannot stringify type ${typeof t}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`)}).join("&")}async fetchWithTimeout(e,t,r,s){let{signal:n,...i}=t||{};n&&n.addEventListener("abort",()=>s.abort());let a=setTimeout(()=>s.abort(),r),o={signal:s.signal,...i};return o.method&&(o.method=o.method.toUpperCase()),this.fetch.call(void 0,e,o).finally(()=>{clearTimeout(a)})}shouldRetry(e){let t=e.headers.get("x-should-retry");return"true"===t||"false"!==t&&(408===e.status||409===e.status||429===e.status||!!(e.status>=500))}async retryRequest(e,t,r){let s,n=r?.["retry-after-ms"];if(n){let e=parseFloat(n);Number.isNaN(e)||(s=e)}let i=r?.["retry-after"];if(i&&!s){let e=parseFloat(i);s=Number.isNaN(e)?Date.parse(i)-Date.now():1e3*e}if(!(s&&0<=s&&s<6e4)){let r=e.maxRetries??this.maxRetries;s=this.calculateDefaultRetryTimeoutMillis(t,r)}return await tI(s),this.makeRequest(e,t-1)}calculateDefaultRetryTimeoutMillis(e,t){return Math.min(.5*Math.pow(2,t-e),8)*(1-.25*Math.random())*1e3}getUserAgent(){return`${this.constructor.name}/JS ${eL}`}}class ty{constructor(e,t,r,s){f.set(this,void 0),th(this,f,e,"f"),this.options=s,this.response=t,this.body=r}hasNextPage(){return!!this.getPaginatedItems().length&&null!=this.nextPageInfo()}async getNextPage(){let e=this.nextPageInfo();if(!e)throw new eF("No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.");let t={...this.options};if("params"in e&&"object"==typeof t.query)t.query={...t.query,...e.params};else if("url"in e){for(let[r,s]of[...Object.entries(t.query||{}),...e.url.searchParams.entries()])e.url.searchParams.set(r,s);t.query=void 0,t.path=e.url.toString()}return await tf(this,f,"f").requestAPIList(this.constructor,t)}async *iterPages(){let e=this;for(yield e;e.hasNextPage();)e=await e.getNextPage(),yield e}async *[(f=new WeakMap,Symbol.asyncIterator)](){for await(let e of this.iterPages())for(let t of e.getPaginatedItems())yield t}}class tw extends tm{constructor(e,t,r){super(t,async t=>new r(e,t.response,await td(t),t.options))}async *[Symbol.asyncIterator](){for await(let e of(await this))yield e}}let tb=e=>new Proxy(Object.fromEntries(e.entries()),{get(e,t){let r=t.toString();return e[r.toLowerCase()]||e[r]}}),t_={method:!0,path:!0,query:!0,body:!0,headers:!0,maxRetries:!0,stream:!0,timeout:!0,httpAgent:!0,signal:!0,idempotencyKey:!0,__metadata:!0,__binaryRequest:!0,__binaryResponse:!0,__streamClass:!0},tv=e=>"object"==typeof e&&null!==e&&!tk(e)&&Object.keys(e).every(e=>tT(t_,e)),tx=e=>"x32"===e?"x32":"x86_64"===e||"x64"===e?"x64":"arm"===e?"arm":"aarch64"===e||"arm64"===e?"arm64":e?`other:${e}`:"unknown",tA=e=>(e=e.toLowerCase()).includes("ios")?"iOS":"android"===e?"Android":"darwin"===e?"MacOS":"win32"===e?"Windows":"freebsd"===e?"FreeBSD":"openbsd"===e?"OpenBSD":"linux"===e?"Linux":e?`Other:${e}`:"Unknown",tS=()=>t??(t=(()=>{if("u">typeof Deno&&null!=Deno.build)return{"X-Stainless-Lang":"js","X-Stainless-Package-Version":eL,"X-Stainless-OS":tA(Deno.build.os),"X-Stainless-Arch":tx(Deno.build.arch),"X-Stainless-Runtime":"deno","X-Stainless-Runtime-Version":"string"==typeof Deno.version?Deno.version:Deno.version?.deno??"unknown"};if("u">typeof EdgeRuntime)return{"X-Stainless-Lang":"js","X-Stainless-Package-Version":eL,"X-Stainless-OS":"Unknown","X-Stainless-Arch":`other:${EdgeRuntime}`,"X-Stainless-Runtime":"edge","X-Stainless-Runtime-Version":ex.default.version};if("[object process]"===Object.prototype.toString.call(void 0!==ex.default?ex.default:0))return{"X-Stainless-Lang":"js","X-Stainless-Package-Version":eL,"X-Stainless-OS":tA(ex.default.platform),"X-Stainless-Arch":tx(ex.default.arch),"X-Stainless-Runtime":"node","X-Stainless-Runtime-Version":ex.default.version};let e=function(){if("u"{try{return JSON.parse(e)}catch(e){return}},tP=/^[a-z][a-z0-9+.-]*:/i,tR=e=>tP.test(e),tI=e=>new Promise(t=>setTimeout(t,e)),t$=(e,t)=>{if("number"!=typeof t||!Number.isInteger(t))throw new eF(`${e} must be an integer`);if(t<0)throw new eF(`${e} must be a positive integer`);return t},tO=e=>{if(e instanceof Error)return e;if("object"==typeof e&&null!==e)try{return Error(JSON.stringify(e))}catch{}return Error(e)},tC=e=>void 0!==ex.default?ex.default.env?.[e]?.trim()??void 0:"u">typeof Deno?Deno.env?.get?.(e)?.trim():void 0;function tk(e){if(!e)return!0;for(let t in e)return!1;return!0}function tT(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function tB(e,t){for(let r in t){if(!tT(t,r))continue;let s=r.toLowerCase();if(!s)continue;let n=t[r];null===n?delete e[s]:void 0!==n&&(e[s]=n)}}let tM=new Set(["authorization","api-key"]);function tj(e,...t){void 0!==ex.default&&ex.default?.env?.DEBUG==="true"&&console.log(`OpenAI:DEBUG:${e}`,...t.map(e=>{if(!e)return e;if(e.headers){let t={...e,headers:{...e.headers}};for(let r in e.headers)tM.has(r.toLowerCase())&&(t.headers[r]="REDACTED");return t}let t=null;for(let r in e)tM.has(r.toLowerCase())&&(t??(t={...e}),t[r]="REDACTED");return t??e}))}let tL=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)}),tN=(e,t)=>{let r=t.toLowerCase();if("function"==typeof e?.get){let s=t[0]?.toUpperCase()+t.substring(1).replace(/([^\w])(\w)/g,(e,t,r)=>t+r.toUpperCase());for(let n of[t,r,t.toUpperCase(),s]){let t=e.get(n);if(t)return t}}for(let[s,n]of Object.entries(e))if(s.toLowerCase()===r){if(Array.isArray(n)){if(n.length<=1)return n[0];return console.warn(`Received ${n.length} entries for the ${t} header, using the first entry.`),n[0]}return n}};function tU(e){return null!=e&&"object"==typeof e&&!Array.isArray(e)}class tD{constructor(e){this._client=e}}class tF extends tD{create(e,t){return this._client.post("/completions",{body:e,...t,stream:e.stream??!1})}}class tW extends tD{list(e,t={},r){return tv(t)?this.list(e,{},t):this._client.getAPIList(`/chat/completions/${e}/messages`,tV,{query:t,...r})}}class tq extends ty{constructor(e,t,r,s){super(e,t,r,s),this.data=r.data||[],this.object=r.object}getPaginatedItems(){return this.data??[]}nextPageParams(){return null}nextPageInfo(){return null}}class tX extends ty{constructor(e,t,r,s){super(e,t,r,s),this.data=r.data||[],this.has_more=r.has_more||!1}getPaginatedItems(){return this.data??[]}hasNextPage(){return!1!==this.has_more&&super.hasNextPage()}nextPageParams(){let e=this.nextPageInfo();if(!e)return null;if("params"in e)return e.params;let t=Object.fromEntries(e.url.searchParams);return Object.keys(t).length?t:null}nextPageInfo(){let e=this.getPaginatedItems();if(!e.length)return null;let t=e[e.length-1]?.id;return t?{params:{after:t}}:null}}class tJ extends tD{constructor(){super(...arguments),this.messages=new tW(this._client)}create(e,t){return this._client.post("/chat/completions",{body:e,...t,stream:e.stream??!1})}retrieve(e,t){return this._client.get(`/chat/completions/${e}`,t)}update(e,t,r){return this._client.post(`/chat/completions/${e}`,{body:t,...r})}list(e={},t){return tv(e)?this.list({},e):this._client.getAPIList("/chat/completions",tH,{query:e,...t})}del(e,t){return this._client.delete(`/chat/completions/${e}`,t)}}class tH extends tX{}class tV extends tX{}tJ.ChatCompletionsPage=tH,tJ.Messages=tW;class tK extends tD{constructor(){super(...arguments),this.completions=new tJ(this._client)}}tK.Completions=tJ,tK.ChatCompletionsPage=tH;class tz extends tD{create(e,t){let r=!!e.encoding_format,s=r?e.encoding_format:"base64";r&&tj("Request","User defined encoding_format:",e.encoding_format);let n=this._client.post("/embeddings",{body:{...e,encoding_format:s},...t});return r?n:(tj("response","Decoding base64 embeddings to float32 array"),n._thenUnwrap(e=>(e&&e.data&&e.data.forEach(e=>{let t=e.embedding;e.embedding=(e=>{if(void 0!==ej.Buffer){let t=ej.Buffer.from(e,"base64");return Array.from(new Float32Array(t.buffer,t.byteOffset,t.length/Float32Array.BYTES_PER_ELEMENT))}{let t=atob(e),r=t.length,s=new Uint8Array(r);for(let e=0;er)throw new eJ({message:`Giving up on waiting for file ${e} to finish processing after ${r} milliseconds.`});return i}}class tY extends tX{}tQ.FileObjectsPage=tY;class tG extends tD{createVariation(e,t){return this._client.post("/images/variations",tl({body:e,...t}))}edit(e,t){return this._client.post("/images/edits",tl({body:e,...t}))}generate(e,t){return this._client.post("/images/generations",{body:e,...t})}}class tZ extends tD{create(e,t){return this._client.post("/audio/speech",{body:e,...t,headers:{Accept:"application/octet-stream",...t?.headers},__binaryResponse:!0})}}class t0 extends tD{create(e,t){return this._client.post("/audio/transcriptions",tl({body:e,...t,stream:e.stream??!1,__metadata:{model:e.model}}))}}class t1 extends tD{create(e,t){return this._client.post("/audio/translations",tl({body:e,...t,__metadata:{model:e.model}}))}}class t2 extends tD{constructor(){super(...arguments),this.transcriptions=new t0(this._client),this.translations=new t1(this._client),this.speech=new tZ(this._client)}}t2.Transcriptions=t0,t2.Translations=t1,t2.Speech=tZ;class t8 extends tD{create(e,t){return this._client.post("/moderations",{body:e,...t})}}class t6 extends tD{retrieve(e,t){return this._client.get(`/models/${e}`,t)}list(e){return this._client.getAPIList("/models",t5,e)}del(e,t){return this._client.delete(`/models/${e}`,t)}}class t5 extends tq{}t6.ModelsPage=t5;class t3 extends tD{}class t4 extends tD{run(e,t){return this._client.post("/fine_tuning/alpha/graders/run",{body:e,...t})}validate(e,t){return this._client.post("/fine_tuning/alpha/graders/validate",{body:e,...t})}}class t9 extends tD{constructor(){super(...arguments),this.graders=new t4(this._client)}}t9.Graders=t4;class t7 extends tD{create(e,t,r){return this._client.getAPIList(`/fine_tuning/checkpoints/${e}/permissions`,re,{body:t,method:"post",...r})}retrieve(e,t={},r){return tv(t)?this.retrieve(e,{},t):this._client.get(`/fine_tuning/checkpoints/${e}/permissions`,{query:t,...r})}del(e,t,r){return this._client.delete(`/fine_tuning/checkpoints/${e}/permissions/${t}`,r)}}class re extends tq{}t7.PermissionCreateResponsesPage=re;class rt extends tD{constructor(){super(...arguments),this.permissions=new t7(this._client)}}rt.Permissions=t7,rt.PermissionCreateResponsesPage=re;class rr extends tD{list(e,t={},r){return tv(t)?this.list(e,{},t):this._client.getAPIList(`/fine_tuning/jobs/${e}/checkpoints`,rs,{query:t,...r})}}class rs extends tX{}rr.FineTuningJobCheckpointsPage=rs;class rn extends tD{constructor(){super(...arguments),this.checkpoints=new rr(this._client)}create(e,t){return this._client.post("/fine_tuning/jobs",{body:e,...t})}retrieve(e,t){return this._client.get(`/fine_tuning/jobs/${e}`,t)}list(e={},t){return tv(e)?this.list({},e):this._client.getAPIList("/fine_tuning/jobs",ri,{query:e,...t})}cancel(e,t){return this._client.post(`/fine_tuning/jobs/${e}/cancel`,t)}listEvents(e,t={},r){return tv(t)?this.listEvents(e,{},t):this._client.getAPIList(`/fine_tuning/jobs/${e}/events`,ra,{query:t,...r})}pause(e,t){return this._client.post(`/fine_tuning/jobs/${e}/pause`,t)}resume(e,t){return this._client.post(`/fine_tuning/jobs/${e}/resume`,t)}}class ri extends tX{}class ra extends tX{}rn.FineTuningJobsPage=ri,rn.FineTuningJobEventsPage=ra,rn.Checkpoints=rr,rn.FineTuningJobCheckpointsPage=rs;class ro extends tD{constructor(){super(...arguments),this.methods=new t3(this._client),this.jobs=new rn(this._client),this.checkpoints=new rt(this._client),this.alpha=new t9(this._client)}}ro.Methods=t3,ro.Jobs=rn,ro.FineTuningJobsPage=ri,ro.FineTuningJobEventsPage=ra,ro.Checkpoints=rt,ro.Alpha=t9;class rl extends tD{}class ru extends tD{constructor(){super(...arguments),this.graderModels=new rl(this._client)}}ru.GraderModels=rl;let rc=async e=>{let t=await Promise.allSettled(e),r=t.filter(e=>"rejected"===e.status);if(r.length){for(let e of r)console.error(e.reason);throw Error(`${r.length} promise(s) failed - see the above errors`)}let s=[];for(let e of t)"fulfilled"===e.status&&s.push(e.value);return s};class rh extends tD{create(e,t,r){return this._client.post(`/vector_stores/${e}/files`,{body:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}retrieve(e,t,r){return this._client.get(`/vector_stores/${e}/files/${t}`,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}update(e,t,r,s){return this._client.post(`/vector_stores/${e}/files/${t}`,{body:r,...s,headers:{"OpenAI-Beta":"assistants=v2",...s?.headers}})}list(e,t={},r){return tv(t)?this.list(e,{},t):this._client.getAPIList(`/vector_stores/${e}/files`,rf,{query:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}del(e,t,r){return this._client.delete(`/vector_stores/${e}/files/${t}`,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}async createAndPoll(e,t,r){let s=await this.create(e,t,r);return await this.poll(e,s.id,r)}async poll(e,t,r){let s={...r?.headers,"X-Stainless-Poll-Helper":"true"};for(r?.pollIntervalMs&&(s["X-Stainless-Custom-Poll-Interval"]=r.pollIntervalMs.toString());;){let n=await this.retrieve(e,t,{...r,headers:s}).withResponse(),i=n.data;switch(i.status){case"in_progress":let a=5e3;if(r?.pollIntervalMs)a=r.pollIntervalMs;else{let e=n.response.headers.get("openai-poll-after-ms");if(e){let t=parseInt(e);isNaN(t)||(a=t)}}await tI(a);break;case"failed":case"completed":return i}}}async upload(e,t,r){let s=await this._client.files.create({file:t,purpose:"assistants"},r);return this.create(e,{file_id:s.id},r)}async uploadAndPoll(e,t,r){let s=await this.upload(e,t,r);return await this.poll(e,s.id,r)}content(e,t,r){return this._client.getAPIList(`/vector_stores/${e}/files/${t}/content`,rd,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}}class rf extends tX{}class rd extends tq{}rh.VectorStoreFilesPage=rf,rh.FileContentResponsesPage=rd;class rp extends tD{create(e,t,r){return this._client.post(`/vector_stores/${e}/file_batches`,{body:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}retrieve(e,t,r){return this._client.get(`/vector_stores/${e}/file_batches/${t}`,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}cancel(e,t,r){return this._client.post(`/vector_stores/${e}/file_batches/${t}/cancel`,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}async createAndPoll(e,t,r){let s=await this.create(e,t);return await this.poll(e,s.id,r)}listFiles(e,t,r={},s){return tv(r)?this.listFiles(e,t,{},r):this._client.getAPIList(`/vector_stores/${e}/file_batches/${t}/files`,rf,{query:r,...s,headers:{"OpenAI-Beta":"assistants=v2",...s?.headers}})}async poll(e,t,r){let s={...r?.headers,"X-Stainless-Poll-Helper":"true"};for(r?.pollIntervalMs&&(s["X-Stainless-Custom-Poll-Interval"]=r.pollIntervalMs.toString());;){let{data:n,response:i}=await this.retrieve(e,t,{...r,headers:s}).withResponse();switch(n.status){case"in_progress":let a=5e3;if(r?.pollIntervalMs)a=r.pollIntervalMs;else{let e=i.headers.get("openai-poll-after-ms");if(e){let t=parseInt(e);isNaN(t)||(a=t)}}await tI(a);break;case"failed":case"cancelled":case"completed":return n}}}async uploadAndPoll(e,{files:t,fileIds:r=[]},s){if(null==t||0==t.length)throw Error("No `files` provided to process. If you've already uploaded files you should use `.createAndPoll()` instead");let n=Math.min(s?.maxConcurrency??5,t.length),i=this._client,a=t.values(),o=[...r];async function l(e){for(let t of e){let e=await i.files.create({file:t,purpose:"assistants"},s);o.push(e.id)}}let u=Array(n).fill(a).map(l);return await rc(u),await this.createAndPoll(e,{file_ids:o})}}class rm extends tD{constructor(){super(...arguments),this.files=new rh(this._client),this.fileBatches=new rp(this._client)}create(e,t){return this._client.post("/vector_stores",{body:e,...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}retrieve(e,t){return this._client.get(`/vector_stores/${e}`,{...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}update(e,t,r){return this._client.post(`/vector_stores/${e}`,{body:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}list(e={},t){return tv(e)?this.list({},e):this._client.getAPIList("/vector_stores",rg,{query:e,...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}del(e,t){return this._client.delete(`/vector_stores/${e}`,{...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}search(e,t,r){return this._client.getAPIList(`/vector_stores/${e}/search`,ry,{body:t,method:"post",...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}}class rg extends tX{}class ry extends tq{}rm.VectorStoresPage=rg,rm.VectorStoreSearchResponsesPage=ry,rm.Files=rh,rm.VectorStoreFilesPage=rf,rm.FileContentResponsesPage=rd,rm.FileBatches=rp;class rw extends tD{create(e,t){return this._client.post("/assistants",{body:e,...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}retrieve(e,t){return this._client.get(`/assistants/${e}`,{...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}update(e,t,r){return this._client.post(`/assistants/${e}`,{body:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}list(e={},t){return tv(e)?this.list({},e):this._client.getAPIList("/assistants",rb,{query:e,...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}del(e,t){return this._client.delete(`/assistants/${e}`,{...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}}class rb extends tX{}function r_(e){return"function"==typeof e.parse}rw.AssistantsPage=rb;let rv=e=>e?.role==="assistant",rx=e=>e?.role==="function",rA=e=>e?.role==="tool";var rS=function(e,t,r,s,n){if("m"===s)throw TypeError("Private method is not writable");if("a"===s&&!n)throw TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!n:!t.has(e))throw TypeError("Cannot write private member to an object whose class did not declare it");return"a"===s?n.call(e,r):n?n.value=r:t.set(e,r),r},rE=function(e,t,r,s){if("a"===r&&!s)throw TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!s:!t.has(e))throw TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(e):s?s.value:t.get(e)};class rP{constructor(){d.add(this),this.controller=new AbortController,p.set(this,void 0),m.set(this,()=>{}),g.set(this,()=>{}),y.set(this,void 0),w.set(this,()=>{}),b.set(this,()=>{}),_.set(this,{}),v.set(this,!1),x.set(this,!1),A.set(this,!1),S.set(this,!1),rS(this,p,new Promise((e,t)=>{rS(this,m,e,"f"),rS(this,g,t,"f")}),"f"),rS(this,y,new Promise((e,t)=>{rS(this,w,e,"f"),rS(this,b,t,"f")}),"f"),rE(this,p,"f").catch(()=>{}),rE(this,y,"f").catch(()=>{})}_run(e){setTimeout(()=>{e().then(()=>{this._emitFinal(),this._emit("end")},rE(this,d,"m",E).bind(this))},0)}_connected(){this.ended||(rE(this,m,"f").call(this),this._emit("connect"))}get ended(){return rE(this,v,"f")}get errored(){return rE(this,x,"f")}get aborted(){return rE(this,A,"f")}abort(){this.controller.abort()}on(e,t){return(rE(this,_,"f")[e]||(rE(this,_,"f")[e]=[])).push({listener:t}),this}off(e,t){let r=rE(this,_,"f")[e];if(!r)return this;let s=r.findIndex(e=>e.listener===t);return s>=0&&r.splice(s,1),this}once(e,t){return(rE(this,_,"f")[e]||(rE(this,_,"f")[e]=[])).push({listener:t,once:!0}),this}emitted(e){return new Promise((t,r)=>{rS(this,S,!0,"f"),"error"!==e&&this.once("error",r),this.once(e,t)})}async done(){rS(this,S,!0,"f"),await rE(this,y,"f")}_emit(e,...t){if(rE(this,v,"f"))return;"end"===e&&(rS(this,v,!0,"f"),rE(this,w,"f").call(this));let r=rE(this,_,"f")[e];if(r&&(rE(this,_,"f")[e]=r.filter(e=>!e.once),r.forEach(({listener:e})=>e(...t))),"abort"===e){let e=t[0];rE(this,S,"f")||r?.length||Promise.reject(e),rE(this,g,"f").call(this,e),rE(this,b,"f").call(this,e),this._emit("end");return}if("error"===e){let e=t[0];rE(this,S,"f")||r?.length||Promise.reject(e),rE(this,g,"f").call(this,e),rE(this,b,"f").call(this,e),this._emit("end")}}_emitFinal(){}}function rR(e){return e?.$brand==="auto-parseable-response-format"}function rI(e){return e?.$brand==="auto-parseable-tool"}function r$(e,t){let r=e.choices.map(e=>{var r,s;if("length"===e.finish_reason)throw new e0;if("content_filter"===e.finish_reason)throw new e1;return{...e,message:{...e.message,...e.message.tool_calls?{tool_calls:e.message.tool_calls?.map(e=>{var r,s;let n;return r=t,s=e,n=r.tools?.find(e=>e.function?.name===s.function.name),{...s,function:{...s.function,parsed_arguments:rI(n)?n.$parseRaw(s.function.arguments):n?.function.strict?JSON.parse(s.function.arguments):null}}})??void 0}:void 0,parsed:e.message.content&&!e.message.refusal?(r=t,s=e.message.content,r.response_format?.type!=="json_schema"?null:r.response_format?.type==="json_schema"?"$parseRaw"in r.response_format?r.response_format.$parseRaw(s):JSON.parse(s):null):null}}});return{...e,choices:r}}function rO(e){return!!rR(e.response_format)||(e.tools?.some(e=>rI(e)||"function"===e.type&&!0===e.function.strict)??!1)}p=new WeakMap,m=new WeakMap,g=new WeakMap,y=new WeakMap,w=new WeakMap,b=new WeakMap,_=new WeakMap,v=new WeakMap,x=new WeakMap,A=new WeakMap,S=new WeakMap,d=new WeakSet,E=function(e){if(rS(this,x,!0,"f"),e instanceof Error&&"AbortError"===e.name&&(e=new eq),e instanceof eq)return rS(this,A,!0,"f"),this._emit("abort",e);if(e instanceof eF)return this._emit("error",e);if(e instanceof Error){let t=new eF(e.message);return t.cause=e,this._emit("error",t)}return this._emit("error",new eF(String(e)))};var rC=function(e,t,r,s){if("a"===r&&!s)throw TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!s:!t.has(e))throw TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(e):s?s.value:t.get(e)};class rk extends rP{constructor(){super(...arguments),P.add(this),this._chatCompletions=[],this.messages=[]}_addChatCompletion(e){this._chatCompletions.push(e),this._emit("chatCompletion",e);let t=e.choices[0]?.message;return t&&this._addMessage(t),e}_addMessage(e,t=!0){if("content"in e||(e.content=null),this.messages.push(e),t){if(this._emit("message",e),(rx(e)||rA(e))&&e.content)this._emit("functionCallResult",e.content);else if(rv(e)&&e.function_call)this._emit("functionCall",e.function_call);else if(rv(e)&&e.tool_calls)for(let t of e.tool_calls)"function"===t.type&&this._emit("functionCall",t.function)}}async finalChatCompletion(){await this.done();let e=this._chatCompletions[this._chatCompletions.length-1];if(!e)throw new eF("stream ended without producing a ChatCompletion");return e}async finalContent(){return await this.done(),rC(this,P,"m",R).call(this)}async finalMessage(){return await this.done(),rC(this,P,"m",I).call(this)}async finalFunctionCall(){return await this.done(),rC(this,P,"m",$).call(this)}async finalFunctionCallResult(){return await this.done(),rC(this,P,"m",O).call(this)}async totalUsage(){return await this.done(),rC(this,P,"m",C).call(this)}allChatCompletions(){return[...this._chatCompletions]}_emitFinal(){let e=this._chatCompletions[this._chatCompletions.length-1];e&&this._emit("finalChatCompletion",e);let t=rC(this,P,"m",I).call(this);t&&this._emit("finalMessage",t);let r=rC(this,P,"m",R).call(this);r&&this._emit("finalContent",r);let s=rC(this,P,"m",$).call(this);s&&this._emit("finalFunctionCall",s);let n=rC(this,P,"m",O).call(this);null!=n&&this._emit("finalFunctionCallResult",n),this._chatCompletions.some(e=>e.usage)&&this._emit("totalUsage",rC(this,P,"m",C).call(this))}async _createChatCompletion(e,t,r){let s=r?.signal;s&&(s.aborted&&this.controller.abort(),s.addEventListener("abort",()=>this.controller.abort())),rC(this,P,"m",k).call(this,t);let n=await e.chat.completions.create({...t,stream:!1},{...r,signal:this.controller.signal});return this._connected(),this._addChatCompletion(r$(n,t))}async _runChatCompletion(e,t,r){for(let e of t.messages)this._addMessage(e,!1);return await this._createChatCompletion(e,t,r)}async _runFunctions(e,t,r){let s="function",{function_call:n="auto",stream:i,...a}=t,o="string"!=typeof n&&n?.name,{maxChatCompletions:l=10}=r||{},u={};for(let e of t.functions)u[e.name||e.function.name]=e;let c=t.functions.map(e=>({name:e.name||e.function.name,parameters:e.parameters,description:e.description}));for(let e of t.messages)this._addMessage(e,!1);for(let t=0;tJSON.stringify(e.name)).join(", ")}. Please try again`;this._addMessage({role:s,name:h,content:e});continue}try{t=r_(d)?await d.parse(f):f}catch(e){this._addMessage({role:s,name:h,content:e instanceof Error?e.message:String(e)});continue}let p=await d.function(t,this),m=rC(this,P,"m",T).call(this,p);if(this._addMessage({role:s,name:h,content:m}),o)return}}async _runTools(e,t,r){let s="tool",{tool_choice:n="auto",stream:i,...a}=t,o="string"!=typeof n&&n?.function?.name,{maxChatCompletions:l=10}=r||{},u=t.tools.map(e=>{if(rI(e)){if(!e.$callback)throw new eF("Tool given to `.runTools()` that does not have an associated function");return{type:"function",function:{function:e.$callback,name:e.function.name,description:e.function.description||"",parameters:e.function.parameters,parse:e.$parseRaw,strict:!0}}}return e}),c={};for(let e of u)"function"===e.type&&(c[e.function.name||e.function.function.name]=e.function);let h="tools"in t?u.map(e=>"function"===e.type?{type:"function",function:{name:e.function.name||e.function.function.name,parameters:e.function.parameters,description:e.function.description,strict:e.function.strict}}:e):void 0;for(let e of t.messages)this._addMessage(e,!1);for(let t=0;tJSON.stringify(e)).join(", ")}. Please try again`;this._addMessage({role:s,tool_call_id:r,content:e});continue}try{t=r_(a)?await a.parse(i):i}catch(t){let e=t instanceof Error?t.message:String(t);this._addMessage({role:s,tool_call_id:r,content:e});continue}let l=await a.function(t,this),u=rC(this,P,"m",T).call(this,l);if(this._addMessage({role:s,tool_call_id:r,content:u}),o)return}}}}P=new WeakSet,R=function(){return rC(this,P,"m",I).call(this).content??null},I=function(){let e=this.messages.length;for(;e-- >0;){let t=this.messages[e];if(rv(t)){let{function_call:e,...r}=t,s={...r,content:t.content??null,refusal:t.refusal??null};return e&&(s.function_call=e),s}}throw new eF("stream ended without producing a ChatCompletionMessage with role=assistant")},$=function(){for(let e=this.messages.length-1;e>=0;e--){let t=this.messages[e];if(rv(t)&&t?.function_call)return t.function_call;if(rv(t)&&t?.tool_calls?.length)return t.tool_calls.at(-1)?.function}},O=function(){for(let e=this.messages.length-1;e>=0;e--){let t=this.messages[e];if(rx(t)&&null!=t.content||rA(t)&&null!=t.content&&"string"==typeof t.content&&this.messages.some(e=>"assistant"===e.role&&e.tool_calls?.some(e=>"function"===e.type&&e.id===t.tool_call_id)))return t.content}},C=function(){let e={completion_tokens:0,prompt_tokens:0,total_tokens:0};for(let{usage:t}of this._chatCompletions)t&&(e.completion_tokens+=t.completion_tokens,e.prompt_tokens+=t.prompt_tokens,e.total_tokens+=t.total_tokens);return e},k=function(e){if(null!=e.n&&e.n>1)throw new eF("ChatCompletion convenience helpers only support n=1 at this time. To use n>1, please use chat.completions.create() directly.")},T=function(e){return"string"==typeof e?e:void 0===e?"undefined":JSON.stringify(e)};class rT extends rk{static runFunctions(e,t,r){let s=new rT,n={...r,headers:{...r?.headers,"X-Stainless-Helper-Method":"runFunctions"}};return s._run(()=>s._runFunctions(e,t,n)),s}static runTools(e,t,r){let s=new rT,n={...r,headers:{...r?.headers,"X-Stainless-Helper-Method":"runTools"}};return s._run(()=>s._runTools(e,t,n)),s}_addMessage(e,t=!0){super._addMessage(e,t),rv(e)&&e.content&&this._emit("content",e.content)}}let rB=511;class rM extends Error{}class rj extends Error{}let rL=e=>(function(e,t=rB){var r,s;let n,i,a,o,l,u,c,h,f,d;if("string"!=typeof e)throw TypeError(`expecting str, got ${typeof e}`);if(!e.trim())throw Error(`${e} is empty`);return r=e.trim(),s=t,n=r.length,i=0,a=e=>{throw new rM(`${e} at position ${i}`)},o=e=>{throw new rj(`${e} at position ${i}`)},l=()=>(d(),i>=n&&a("Unexpected end of input"),'"'===r[i])?u():"{"===r[i]?c():"["===r[i]?h():"null"===r.substring(i,i+4)||16&s&&n-i<4&&"null".startsWith(r.substring(i))?(i+=4,null):"true"===r.substring(i,i+4)||32&s&&n-i<4&&"true".startsWith(r.substring(i))?(i+=4,!0):"false"===r.substring(i,i+5)||32&s&&n-i<5&&"false".startsWith(r.substring(i))?(i+=5,!1):"Infinity"===r.substring(i,i+8)||128&s&&n-i<8&&"Infinity".startsWith(r.substring(i))?(i+=8,1/0):"-Infinity"===r.substring(i,i+9)||256&s&&1{let e=i,t=!1;for(i++;i{i++,d();let e={};try{for(;"}"!==r[i];){if(d(),i>=n&&8&s)return e;let t=u();d(),i++;try{let r=l();Object.defineProperty(e,t,{value:r,writable:!0,enumerable:!0,configurable:!0})}catch(t){if(8&s)return e;throw t}d(),","===r[i]&&i++}}catch(t){if(8&s)return e;a("Expected '}' at end of object")}return i++,e},h=()=>{i++;let e=[];try{for(;"]"!==r[i];)e.push(l()),d(),","===r[i]&&i++}catch(t){if(4&s)return e;a("Expected ']' at end of array")}return i++,e},f=()=>{if(0===i){"-"===r&&2&s&&a("Not sure what '-' is");try{return JSON.parse(r)}catch(e){if(2&s)try{if("."===r[r.length-1])return JSON.parse(r.substring(0,r.lastIndexOf(".")));return JSON.parse(r.substring(0,r.lastIndexOf("e")))}catch(e){}o(String(e))}}let e=i;for("-"===r[i]&&i++;r[i]&&!",]}".includes(r[i]);)i++;i!=n||2&s||a("Unterminated number literal");try{return JSON.parse(r.substring(e,i))}catch(t){"-"===r.substring(e,i)&&2&s&&a("Not sure what '-' is");try{return JSON.parse(r.substring(e,r.lastIndexOf("e")))}catch(e){o(String(e))}}},d=()=>{for(;it._fromReadableStream(e)),t}static createChatCompletion(e,t,r){let s=new rD(t);return s._run(()=>s._runChatCompletion(e,{...t,stream:!0},{...r,headers:{...r?.headers,"X-Stainless-Helper-Method":"stream"}})),s}async _createChatCompletion(e,t,r){super._createChatCompletion;let s=r?.signal;s&&(s.aborted&&this.controller.abort(),s.addEventListener("abort",()=>this.controller.abort())),rU(this,B,"m",N).call(this);let n=await e.chat.completions.create({...t,stream:!0},{...r,signal:this.controller.signal});for await(let e of(this._connected(),n))rU(this,B,"m",D).call(this,e);if(n.controller.signal?.aborted)throw new eq;return this._addChatCompletion(rU(this,B,"m",q).call(this))}async _fromReadableStream(e,t){let r,s=t?.signal;s&&(s.aborted&&this.controller.abort(),s.addEventListener("abort",()=>this.controller.abort())),rU(this,B,"m",N).call(this),this._connected();let n=e3.fromReadableStream(e,this.controller);for await(let e of n)r&&r!==e.id&&this._addChatCompletion(rU(this,B,"m",q).call(this)),rU(this,B,"m",D).call(this,e),r=e.id;if(n.controller.signal?.aborted)throw new eq;return this._addChatCompletion(rU(this,B,"m",q).call(this))}[(M=new WeakMap,j=new WeakMap,L=new WeakMap,B=new WeakSet,N=function(){this.ended||rN(this,L,void 0,"f")},U=function(e){let t=rU(this,j,"f")[e.index];return t||(t={content_done:!1,refusal_done:!1,logprobs_content_done:!1,logprobs_refusal_done:!1,done_tool_calls:new Set,current_tool_call_index:null},rU(this,j,"f")[e.index]=t),t},D=function(e){if(this.ended)return;let t=rU(this,B,"m",J).call(this,e);for(let r of(this._emit("chunk",e,t),e.choices)){let e=t.choices[r.index];null!=r.delta.content&&e.message?.role==="assistant"&&e.message?.content&&(this._emit("content",r.delta.content,e.message.content),this._emit("content.delta",{delta:r.delta.content,snapshot:e.message.content,parsed:e.message.parsed})),null!=r.delta.refusal&&e.message?.role==="assistant"&&e.message?.refusal&&this._emit("refusal.delta",{delta:r.delta.refusal,snapshot:e.message.refusal}),r.logprobs?.content!=null&&e.message?.role==="assistant"&&this._emit("logprobs.content.delta",{content:r.logprobs?.content,snapshot:e.logprobs?.content??[]}),r.logprobs?.refusal!=null&&e.message?.role==="assistant"&&this._emit("logprobs.refusal.delta",{refusal:r.logprobs?.refusal,snapshot:e.logprobs?.refusal??[]});let s=rU(this,B,"m",U).call(this,e);for(let t of(e.finish_reason&&(rU(this,B,"m",W).call(this,e),null!=s.current_tool_call_index&&rU(this,B,"m",F).call(this,e,s.current_tool_call_index)),r.delta.tool_calls??[]))s.current_tool_call_index!==t.index&&(rU(this,B,"m",W).call(this,e),null!=s.current_tool_call_index&&rU(this,B,"m",F).call(this,e,s.current_tool_call_index)),s.current_tool_call_index=t.index;for(let t of r.delta.tool_calls??[]){let r=e.message.tool_calls?.[t.index];r?.type&&(r?.type==="function"?this._emit("tool_calls.function.arguments.delta",{name:r.function?.name,index:t.index,arguments:r.function.arguments,parsed_arguments:r.function.parsed_arguments,arguments_delta:t.function?.arguments??""}):rq(r?.type))}}},F=function(e,t){if(rU(this,B,"m",U).call(this,e).done_tool_calls.has(t))return;let r=e.message.tool_calls?.[t];if(!r)throw Error("no tool call snapshot");if(!r.type)throw Error("tool call snapshot missing `type`");if("function"===r.type){let e=rU(this,M,"f")?.tools?.find(e=>"function"===e.type&&e.function.name===r.function.name);this._emit("tool_calls.function.arguments.done",{name:r.function.name,index:t,arguments:r.function.arguments,parsed_arguments:rI(e)?e.$parseRaw(r.function.arguments):e?.function.strict?JSON.parse(r.function.arguments):null})}else rq(r.type)},W=function(e){let t=rU(this,B,"m",U).call(this,e);if(e.message.content&&!t.content_done){t.content_done=!0;let r=rU(this,B,"m",X).call(this);this._emit("content.done",{content:e.message.content,parsed:r?r.$parseRaw(e.message.content):null})}e.message.refusal&&!t.refusal_done&&(t.refusal_done=!0,this._emit("refusal.done",{refusal:e.message.refusal})),e.logprobs?.content&&!t.logprobs_content_done&&(t.logprobs_content_done=!0,this._emit("logprobs.content.done",{content:e.logprobs.content})),e.logprobs?.refusal&&!t.logprobs_refusal_done&&(t.logprobs_refusal_done=!0,this._emit("logprobs.refusal.done",{refusal:e.logprobs.refusal}))},q=function(){if(this.ended)throw new eF("stream has ended, this shouldn't happen");let e=rU(this,L,"f");if(!e)throw new eF("request ended without sending any chunks");return rN(this,L,void 0,"f"),rN(this,j,[],"f"),function(e,t){var r;let{id:s,choices:n,created:i,model:a,system_fingerprint:o,...l}=e;return r={...l,id:s,choices:n.map(({message:t,finish_reason:r,index:s,logprobs:n,...i})=>{if(!r)throw new eF(`missing finish_reason for choice ${s}`);let{content:a=null,function_call:o,tool_calls:l,...u}=t,c=t.role;if(!c)throw new eF(`missing role for choice ${s}`);if(o){let{arguments:e,name:l}=o;if(null==e)throw new eF(`missing function_call.arguments for choice ${s}`);if(!l)throw new eF(`missing function_call.name for choice ${s}`);return{...i,message:{content:a,function_call:{arguments:e,name:l},role:c,refusal:t.refusal??null},finish_reason:r,index:s,logprobs:n}}return l?{...i,index:s,finish_reason:r,logprobs:n,message:{...u,role:c,content:a,refusal:t.refusal??null,tool_calls:l.map((t,r)=>{let{function:n,type:i,id:a,...o}=t,{arguments:l,name:u,...c}=n||{};if(null==a)throw new eF(`missing choices[${s}].tool_calls[${r}].id +${rF(e)}`);if(null==i)throw new eF(`missing choices[${s}].tool_calls[${r}].type +${rF(e)}`);if(null==u)throw new eF(`missing choices[${s}].tool_calls[${r}].function.name +${rF(e)}`);if(null==l)throw new eF(`missing choices[${s}].tool_calls[${r}].function.arguments +${rF(e)}`);return{...o,id:a,type:i,function:{...c,name:u,arguments:l}}})}}:{...i,message:{...u,content:a,role:c,refusal:t.refusal??null},finish_reason:r,index:s,logprobs:n}}),created:i,model:a,object:"chat.completion",...o?{system_fingerprint:o}:{}},t&&rO(t)?r$(r,t):{...r,choices:r.choices.map(e=>({...e,message:{...e.message,parsed:null,...e.message.tool_calls?{tool_calls:e.message.tool_calls}:void 0}}))}}(e,rU(this,M,"f"))},X=function(){let e=rU(this,M,"f")?.response_format;return rR(e)?e:null},J=function(e){var t,r,s,n;let i=rU(this,L,"f"),{choices:a,...o}=e;for(let{delta:a,finish_reason:l,index:u,logprobs:c=null,...h}of(i?Object.assign(i,o):i=rN(this,L,{...o,choices:[]},"f"),e.choices)){let e=i.choices[u];if(e||(e=i.choices[u]={finish_reason:l,index:u,message:{},logprobs:c,...h}),c)if(e.logprobs){let{content:s,refusal:n,...i}=c;rW(i),Object.assign(e.logprobs,i),s&&((t=e.logprobs).content??(t.content=[]),e.logprobs.content.push(...s)),n&&((r=e.logprobs).refusal??(r.refusal=[]),e.logprobs.refusal.push(...n))}else e.logprobs=Object.assign({},c);if(l&&(e.finish_reason=l,rU(this,M,"f")&&rO(rU(this,M,"f")))){if("length"===l)throw new e0;if("content_filter"===l)throw new e1}if(Object.assign(e,h),!a)continue;let{content:o,refusal:f,function_call:d,role:p,tool_calls:m,...g}=a;if(rW(g),Object.assign(e.message,g),f&&(e.message.refusal=(e.message.refusal||"")+f),p&&(e.message.role=p),d&&(e.message.function_call?(d.name&&(e.message.function_call.name=d.name),d.arguments&&((s=e.message.function_call).arguments??(s.arguments=""),e.message.function_call.arguments+=d.arguments)):e.message.function_call=d),o&&(e.message.content=(e.message.content||"")+o,!e.message.refusal&&rU(this,B,"m",X).call(this)&&(e.message.parsed=rL(e.message.content))),m)for(let{index:t,id:r,type:s,function:i,...a}of(e.message.tool_calls||(e.message.tool_calls=[]),m)){let o=(n=e.message.tool_calls)[t]??(n[t]={});Object.assign(o,a),r&&(o.id=r),s&&(o.type=s),i&&(o.function??(o.function={name:i.name??"",arguments:""})),i?.name&&(o.function.name=i.name),i?.arguments&&(o.function.arguments+=i.arguments,function(e,t){if(!e)return!1;let r=e.tools?.find(e=>e.function?.name===t.function.name);return rI(r)||r?.function.strict||!1}(rU(this,M,"f"),o)&&(o.function.parsed_arguments=rL(o.function.arguments)))}}return i},Symbol.asyncIterator)](){let e=[],t=[],r=!1;return this.on("chunk",r=>{let s=t.shift();s?s.resolve(r):e.push(r)}),this.on("end",()=>{for(let e of(r=!0,t))e.resolve(void 0);t.length=0}),this.on("abort",e=>{for(let s of(r=!0,t))s.reject(e);t.length=0}),this.on("error",e=>{for(let s of(r=!0,t))s.reject(e);t.length=0}),{next:async()=>e.length?{value:e.shift(),done:!1}:r?{value:void 0,done:!0}:new Promise((e,r)=>t.push({resolve:e,reject:r})).then(e=>e?{value:e,done:!1}:{value:void 0,done:!0}),return:async()=>(this.abort(),{value:void 0,done:!0})}}toReadableStream(){return new e3(this[Symbol.asyncIterator].bind(this),this.controller).toReadableStream()}}function rF(e){return JSON.stringify(e)}function rW(e){}function rq(e){}class rX extends rD{static fromReadableStream(e){let t=new rX(null);return t._run(()=>t._fromReadableStream(e)),t}static runFunctions(e,t,r){let s=new rX(null),n={...r,headers:{...r?.headers,"X-Stainless-Helper-Method":"runFunctions"}};return s._run(()=>s._runFunctions(e,t,n)),s}static runTools(e,t,r){let s=new rX(t),n={...r,headers:{...r?.headers,"X-Stainless-Helper-Method":"runTools"}};return s._run(()=>s._runTools(e,t,n)),s}}class rJ extends tD{parse(e,t){for(let t of e.tools??[]){if("function"!==t.type)throw new eF(`Currently only \`function\` tool types support auto-parsing; Received \`${t.type}\``);if(!0!==t.function.strict)throw new eF(`The \`${t.function.name}\` tool is not marked with \`strict: true\`. Only strict function tools can be auto-parsed`)}return this._client.chat.completions.create(e,{...t,headers:{...t?.headers,"X-Stainless-Helper-Method":"beta.chat.completions.parse"}})._thenUnwrap(t=>r$(t,e))}runFunctions(e,t){return e.stream?rX.runFunctions(this._client,e,t):rT.runFunctions(this._client,e,t)}runTools(e,t){return e.stream?rX.runTools(this._client,e,t):rT.runTools(this._client,e,t)}stream(e,t){return rD.createChatCompletion(this._client,e,t)}}class rH extends tD{constructor(){super(...arguments),this.completions=new rJ(this._client)}}(rH||(rH={})).Completions=rJ;class rV extends tD{create(e,t){return this._client.post("/realtime/sessions",{body:e,...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}}class rK extends tD{create(e,t){return this._client.post("/realtime/transcription_sessions",{body:e,...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}}class rz extends tD{constructor(){super(...arguments),this.sessions=new rV(this._client),this.transcriptionSessions=new rK(this._client)}}rz.Sessions=rV,rz.TranscriptionSessions=rK;var rQ=function(e,t,r,s){if("a"===r&&!s)throw TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!s:!t.has(e))throw TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(e):s?s.value:t.get(e)},rY=function(e,t,r,s,n){if("m"===s)throw TypeError("Private method is not writable");if("a"===s&&!n)throw TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!n:!t.has(e))throw TypeError("Cannot write private member to an object whose class did not declare it");return"a"===s?n.call(e,r):n?n.value=r:t.set(e,r),r};class rG extends rP{constructor(){super(...arguments),H.add(this),V.set(this,[]),K.set(this,{}),z.set(this,{}),Q.set(this,void 0),Y.set(this,void 0),G.set(this,void 0),Z.set(this,void 0),ee.set(this,void 0),et.set(this,void 0),er.set(this,void 0),es.set(this,void 0),en.set(this,void 0)}[(V=new WeakMap,K=new WeakMap,z=new WeakMap,Q=new WeakMap,Y=new WeakMap,G=new WeakMap,Z=new WeakMap,ee=new WeakMap,et=new WeakMap,er=new WeakMap,es=new WeakMap,en=new WeakMap,H=new WeakSet,Symbol.asyncIterator)](){let e=[],t=[],r=!1;return this.on("event",r=>{let s=t.shift();s?s.resolve(r):e.push(r)}),this.on("end",()=>{for(let e of(r=!0,t))e.resolve(void 0);t.length=0}),this.on("abort",e=>{for(let s of(r=!0,t))s.reject(e);t.length=0}),this.on("error",e=>{for(let s of(r=!0,t))s.reject(e);t.length=0}),{next:async()=>e.length?{value:e.shift(),done:!1}:r?{value:void 0,done:!0}:new Promise((e,r)=>t.push({resolve:e,reject:r})).then(e=>e?{value:e,done:!1}:{value:void 0,done:!0}),return:async()=>(this.abort(),{value:void 0,done:!0})}}static fromReadableStream(e){let t=new rG;return t._run(()=>t._fromReadableStream(e)),t}async _fromReadableStream(e,t){let r=t?.signal;r&&(r.aborted&&this.controller.abort(),r.addEventListener("abort",()=>this.controller.abort())),this._connected();let s=e3.fromReadableStream(e,this.controller);for await(let e of s)rQ(this,H,"m",ei).call(this,e);if(s.controller.signal?.aborted)throw new eq;return this._addRun(rQ(this,H,"m",ea).call(this))}toReadableStream(){return new e3(this[Symbol.asyncIterator].bind(this),this.controller).toReadableStream()}static createToolAssistantStream(e,t,r,s,n){let i=new rG;return i._run(()=>i._runToolAssistantStream(e,t,r,s,{...n,headers:{...n?.headers,"X-Stainless-Helper-Method":"stream"}})),i}async _createToolAssistantStream(e,t,r,s,n){let i=n?.signal;i&&(i.aborted&&this.controller.abort(),i.addEventListener("abort",()=>this.controller.abort()));let a={...s,stream:!0},o=await e.submitToolOutputs(t,r,a,{...n,signal:this.controller.signal});for await(let e of(this._connected(),o))rQ(this,H,"m",ei).call(this,e);if(o.controller.signal?.aborted)throw new eq;return this._addRun(rQ(this,H,"m",ea).call(this))}static createThreadAssistantStream(e,t,r){let s=new rG;return s._run(()=>s._threadAssistantStream(e,t,{...r,headers:{...r?.headers,"X-Stainless-Helper-Method":"stream"}})),s}static createAssistantStream(e,t,r,s){let n=new rG;return n._run(()=>n._runAssistantStream(e,t,r,{...s,headers:{...s?.headers,"X-Stainless-Helper-Method":"stream"}})),n}currentEvent(){return rQ(this,er,"f")}currentRun(){return rQ(this,es,"f")}currentMessageSnapshot(){return rQ(this,Q,"f")}currentRunStepSnapshot(){return rQ(this,en,"f")}async finalRunSteps(){return await this.done(),Object.values(rQ(this,K,"f"))}async finalMessages(){return await this.done(),Object.values(rQ(this,z,"f"))}async finalRun(){if(await this.done(),!rQ(this,Y,"f"))throw Error("Final run was not received.");return rQ(this,Y,"f")}async _createThreadAssistantStream(e,t,r){let s=r?.signal;s&&(s.aborted&&this.controller.abort(),s.addEventListener("abort",()=>this.controller.abort()));let n={...t,stream:!0},i=await e.createAndRun(n,{...r,signal:this.controller.signal});for await(let e of(this._connected(),i))rQ(this,H,"m",ei).call(this,e);if(i.controller.signal?.aborted)throw new eq;return this._addRun(rQ(this,H,"m",ea).call(this))}async _createAssistantStream(e,t,r,s){let n=s?.signal;n&&(n.aborted&&this.controller.abort(),n.addEventListener("abort",()=>this.controller.abort()));let i={...r,stream:!0},a=await e.create(t,i,{...s,signal:this.controller.signal});for await(let e of(this._connected(),a))rQ(this,H,"m",ei).call(this,e);if(a.controller.signal?.aborted)throw new eq;return this._addRun(rQ(this,H,"m",ea).call(this))}static accumulateDelta(e,t){for(let[r,s]of Object.entries(t)){if(!e.hasOwnProperty(r)){e[r]=s;continue}let t=e[r];if(null==t||"index"===r||"type"===r){e[r]=s;continue}if("string"==typeof t&&"string"==typeof s)t+=s;else if("number"==typeof t&&"number"==typeof s)t+=s;else if(tU(t)&&tU(s))t=this.accumulateDelta(t,s);else if(Array.isArray(t)&&Array.isArray(s)){if(t.every(e=>"string"==typeof e||"number"==typeof e)){t.push(...s);continue}for(let e of s){if(!tU(e))throw Error(`Expected array delta entry to be an object but got: ${e}`);let r=e.index;if(null==r)throw console.error(e),Error("Expected array delta entry to have an `index` property");if("number"!=typeof r)throw Error(`Expected array delta entry \`index\` property to be a number but got ${r}`);let s=t[r];null==s?t.push(e):t[r]=this.accumulateDelta(s,e)}continue}else throw Error(`Unhandled record type: ${r}, deltaValue: ${s}, accValue: ${t}`);e[r]=t}return e}_addRun(e){return e}async _threadAssistantStream(e,t,r){return await this._createThreadAssistantStream(t,e,r)}async _runAssistantStream(e,t,r,s){return await this._createAssistantStream(t,e,r,s)}async _runToolAssistantStream(e,t,r,s,n){return await this._createToolAssistantStream(r,e,t,s,n)}}ei=function(e){if(!this.ended)switch(rY(this,er,e,"f"),rQ(this,H,"m",eu).call(this,e),e.event){case"thread.created":break;case"thread.run.created":case"thread.run.queued":case"thread.run.in_progress":case"thread.run.requires_action":case"thread.run.completed":case"thread.run.incomplete":case"thread.run.failed":case"thread.run.cancelling":case"thread.run.cancelled":case"thread.run.expired":rQ(this,H,"m",ed).call(this,e);break;case"thread.run.step.created":case"thread.run.step.in_progress":case"thread.run.step.delta":case"thread.run.step.completed":case"thread.run.step.failed":case"thread.run.step.cancelled":case"thread.run.step.expired":rQ(this,H,"m",el).call(this,e);break;case"thread.message.created":case"thread.message.in_progress":case"thread.message.delta":case"thread.message.completed":case"thread.message.incomplete":rQ(this,H,"m",eo).call(this,e);break;case"error":throw Error("Encountered an error event in event processing - errors should be processed earlier")}},ea=function(){if(this.ended)throw new eF("stream has ended, this shouldn't happen");if(!rQ(this,Y,"f"))throw Error("Final run has not been received");return rQ(this,Y,"f")},eo=function(e){let[t,r]=rQ(this,H,"m",eh).call(this,e,rQ(this,Q,"f"));for(let e of(rY(this,Q,t,"f"),rQ(this,z,"f")[t.id]=t,r)){let r=t.content[e.index];r?.type=="text"&&this._emit("textCreated",r.text)}switch(e.event){case"thread.message.created":this._emit("messageCreated",e.data);break;case"thread.message.in_progress":break;case"thread.message.delta":if(this._emit("messageDelta",e.data.delta,t),e.data.delta.content)for(let r of e.data.delta.content){if("text"==r.type&&r.text){let e=r.text,s=t.content[r.index];if(s&&"text"==s.type)this._emit("textDelta",e,s.text);else throw Error("The snapshot associated with this text delta is not text or missing")}if(r.index!=rQ(this,G,"f")){if(rQ(this,Z,"f"))switch(rQ(this,Z,"f").type){case"text":this._emit("textDone",rQ(this,Z,"f").text,rQ(this,Q,"f"));break;case"image_file":this._emit("imageFileDone",rQ(this,Z,"f").image_file,rQ(this,Q,"f"))}rY(this,G,r.index,"f")}rY(this,Z,t.content[r.index],"f")}break;case"thread.message.completed":case"thread.message.incomplete":if(void 0!==rQ(this,G,"f")){let t=e.data.content[rQ(this,G,"f")];if(t)switch(t.type){case"image_file":this._emit("imageFileDone",t.image_file,rQ(this,Q,"f"));break;case"text":this._emit("textDone",t.text,rQ(this,Q,"f"))}}rQ(this,Q,"f")&&this._emit("messageDone",e.data),rY(this,Q,void 0,"f")}},el=function(e){let t=rQ(this,H,"m",ec).call(this,e);switch(rY(this,en,t,"f"),e.event){case"thread.run.step.created":this._emit("runStepCreated",e.data);break;case"thread.run.step.delta":let r=e.data.delta;if(r.step_details&&"tool_calls"==r.step_details.type&&r.step_details.tool_calls&&"tool_calls"==t.step_details.type)for(let e of r.step_details.tool_calls)e.index==rQ(this,ee,"f")?this._emit("toolCallDelta",e,t.step_details.tool_calls[e.index]):(rQ(this,et,"f")&&this._emit("toolCallDone",rQ(this,et,"f")),rY(this,ee,e.index,"f"),rY(this,et,t.step_details.tool_calls[e.index],"f"),rQ(this,et,"f")&&this._emit("toolCallCreated",rQ(this,et,"f")));this._emit("runStepDelta",e.data.delta,t);break;case"thread.run.step.completed":case"thread.run.step.failed":case"thread.run.step.cancelled":case"thread.run.step.expired":rY(this,en,void 0,"f"),"tool_calls"==e.data.step_details.type&&rQ(this,et,"f")&&(this._emit("toolCallDone",rQ(this,et,"f")),rY(this,et,void 0,"f")),this._emit("runStepDone",e.data,t)}},eu=function(e){rQ(this,V,"f").push(e),this._emit("event",e)},ec=function(e){switch(e.event){case"thread.run.step.created":return rQ(this,K,"f")[e.data.id]=e.data,e.data;case"thread.run.step.delta":let t=rQ(this,K,"f")[e.data.id];if(!t)throw Error("Received a RunStepDelta before creation of a snapshot");let r=e.data;if(r.delta){let s=rG.accumulateDelta(t,r.delta);rQ(this,K,"f")[e.data.id]=s}return rQ(this,K,"f")[e.data.id];case"thread.run.step.completed":case"thread.run.step.failed":case"thread.run.step.cancelled":case"thread.run.step.expired":case"thread.run.step.in_progress":rQ(this,K,"f")[e.data.id]=e.data}if(rQ(this,K,"f")[e.data.id])return rQ(this,K,"f")[e.data.id];throw Error("No snapshot available")},eh=function(e,t){let r=[];switch(e.event){case"thread.message.created":return[e.data,r];case"thread.message.delta":if(!t)throw Error("Received a delta with no existing snapshot (there should be one from message creation)");let s=e.data;if(s.delta.content)for(let e of s.delta.content)if(e.index in t.content){let r=t.content[e.index];t.content[e.index]=rQ(this,H,"m",ef).call(this,e,r)}else t.content[e.index]=e,r.push(e);return[t,r];case"thread.message.in_progress":case"thread.message.completed":case"thread.message.incomplete":if(t)return[t,r];throw Error("Received thread message event with no existing snapshot")}throw Error("Tried to accumulate a non-message event")},ef=function(e,t){return rG.accumulateDelta(t,e)},ed=function(e){switch(rY(this,es,e.data,"f"),e.event){case"thread.run.created":case"thread.run.queued":case"thread.run.in_progress":break;case"thread.run.requires_action":case"thread.run.cancelled":case"thread.run.failed":case"thread.run.completed":case"thread.run.expired":rY(this,Y,e.data,"f"),rQ(this,et,"f")&&(this._emit("toolCallDone",rQ(this,et,"f")),rY(this,et,void 0,"f"))}};class rZ extends tD{create(e,t,r){return this._client.post(`/threads/${e}/messages`,{body:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}retrieve(e,t,r){return this._client.get(`/threads/${e}/messages/${t}`,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}update(e,t,r,s){return this._client.post(`/threads/${e}/messages/${t}`,{body:r,...s,headers:{"OpenAI-Beta":"assistants=v2",...s?.headers}})}list(e,t={},r){return tv(t)?this.list(e,{},t):this._client.getAPIList(`/threads/${e}/messages`,r0,{query:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}del(e,t,r){return this._client.delete(`/threads/${e}/messages/${t}`,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}}class r0 extends tX{}rZ.MessagesPage=r0;class r1 extends tD{retrieve(e,t,r,s={},n){return tv(s)?this.retrieve(e,t,r,{},s):this._client.get(`/threads/${e}/runs/${t}/steps/${r}`,{query:s,...n,headers:{"OpenAI-Beta":"assistants=v2",...n?.headers}})}list(e,t,r={},s){return tv(r)?this.list(e,t,{},r):this._client.getAPIList(`/threads/${e}/runs/${t}/steps`,r2,{query:r,...s,headers:{"OpenAI-Beta":"assistants=v2",...s?.headers}})}}class r2 extends tX{}r1.RunStepsPage=r2;class r8 extends tD{constructor(){super(...arguments),this.steps=new r1(this._client)}create(e,t,r){let{include:s,...n}=t;return this._client.post(`/threads/${e}/runs`,{query:{include:s},body:n,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers},stream:t.stream??!1})}retrieve(e,t,r){return this._client.get(`/threads/${e}/runs/${t}`,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}update(e,t,r,s){return this._client.post(`/threads/${e}/runs/${t}`,{body:r,...s,headers:{"OpenAI-Beta":"assistants=v2",...s?.headers}})}list(e,t={},r){return tv(t)?this.list(e,{},t):this._client.getAPIList(`/threads/${e}/runs`,r6,{query:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}cancel(e,t,r){return this._client.post(`/threads/${e}/runs/${t}/cancel`,{...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}async createAndPoll(e,t,r){let s=await this.create(e,t,r);return await this.poll(e,s.id,r)}createAndStream(e,t,r){return rG.createAssistantStream(e,this._client.beta.threads.runs,t,r)}async poll(e,t,r){let s={...r?.headers,"X-Stainless-Poll-Helper":"true"};for(r?.pollIntervalMs&&(s["X-Stainless-Custom-Poll-Interval"]=r.pollIntervalMs.toString());;){let{data:n,response:i}=await this.retrieve(e,t,{...r,headers:{...r?.headers,...s}}).withResponse();switch(n.status){case"queued":case"in_progress":case"cancelling":let a=5e3;if(r?.pollIntervalMs)a=r.pollIntervalMs;else{let e=i.headers.get("openai-poll-after-ms");if(e){let t=parseInt(e);isNaN(t)||(a=t)}}await tI(a);break;case"requires_action":case"incomplete":case"cancelled":case"completed":case"failed":case"expired":return n}}}stream(e,t,r){return rG.createAssistantStream(e,this._client.beta.threads.runs,t,r)}submitToolOutputs(e,t,r,s){return this._client.post(`/threads/${e}/runs/${t}/submit_tool_outputs`,{body:r,...s,headers:{"OpenAI-Beta":"assistants=v2",...s?.headers},stream:r.stream??!1})}async submitToolOutputsAndPoll(e,t,r,s){let n=await this.submitToolOutputs(e,t,r,s);return await this.poll(e,n.id,s)}submitToolOutputsStream(e,t,r,s){return rG.createToolAssistantStream(e,t,this._client.beta.threads.runs,r,s)}}class r6 extends tX{}r8.RunsPage=r6,r8.Steps=r1,r8.RunStepsPage=r2;class r5 extends tD{constructor(){super(...arguments),this.runs=new r8(this._client),this.messages=new rZ(this._client)}create(e={},t){return tv(e)?this.create({},e):this._client.post("/threads",{body:e,...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}retrieve(e,t){return this._client.get(`/threads/${e}`,{...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}update(e,t,r){return this._client.post(`/threads/${e}`,{body:t,...r,headers:{"OpenAI-Beta":"assistants=v2",...r?.headers}})}del(e,t){return this._client.delete(`/threads/${e}`,{...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers}})}createAndRun(e,t){return this._client.post("/threads/runs",{body:e,...t,headers:{"OpenAI-Beta":"assistants=v2",...t?.headers},stream:e.stream??!1})}async createAndRunPoll(e,t){let r=await this.createAndRun(e,t);return await this.runs.poll(r.thread_id,r.id,t)}createAndRunStream(e,t){return rG.createThreadAssistantStream(e,this._client.beta.threads,t)}}r5.Runs=r8,r5.RunsPage=r6,r5.Messages=rZ,r5.MessagesPage=r0;class r3 extends tD{constructor(){super(...arguments),this.realtime=new rz(this._client),this.chat=new rH(this._client),this.assistants=new rw(this._client),this.threads=new r5(this._client)}}r3.Realtime=rz,r3.Assistants=rw,r3.AssistantsPage=rb,r3.Threads=r5;class r4 extends tD{create(e,t){return this._client.post("/batches",{body:e,...t})}retrieve(e,t){return this._client.get(`/batches/${e}`,t)}list(e={},t){return tv(e)?this.list({},e):this._client.getAPIList("/batches",r9,{query:e,...t})}cancel(e,t){return this._client.post(`/batches/${e}/cancel`,t)}}class r9 extends tX{}r4.BatchesPage=r9;class r7 extends tD{create(e,t,r){return this._client.post(`/uploads/${e}/parts`,tl({body:t,...r}))}}class se extends tD{constructor(){super(...arguments),this.parts=new r7(this._client)}create(e,t){return this._client.post("/uploads",{body:e,...t})}cancel(e,t){return this._client.post(`/uploads/${e}/cancel`,t)}complete(e,t,r){return this._client.post(`/uploads/${e}/complete`,{body:t,...r})}}function st(e,t){let r=e.output.map(e=>{if("function_call"===e.type)return{...e,parsed_arguments:function(e,t){var r,s;let n=(r=e.tools??[],s=t.name,r.find(e=>"function"===e.type&&e.name===s));return{...t,...t,parsed_arguments:n?.$brand==="auto-parseable-tool"?n.$parseRaw(t.arguments):n?.strict?JSON.parse(t.arguments):null}}(t,e)};if("message"===e.type){let r=e.content.map(e=>{var r,s;return"output_text"===e.type?{...e,parsed:(r=t,s=e.text,r.text?.format?.type!=="json_schema"?null:"$parseRaw"in r.text?.format?(r.text?.format).$parseRaw(s):JSON.parse(s))}:e});return{...e,content:r}}return e}),s=Object.assign({},e,{output:r});return Object.getOwnPropertyDescriptor(e,"output_text")||sr(s),Object.defineProperty(s,"output_parsed",{enumerable:!0,get(){for(let e of s.output)if("message"===e.type){for(let t of e.content)if("output_text"===t.type&&null!==t.parsed)return t.parsed}return null}}),s}function sr(e){let t=[];for(let r of e.output)if("message"===r.type)for(let e of r.content)"output_text"===e.type&&t.push(e.text);e.output_text=t.join("")}se.Parts=r7;class ss extends tD{list(e,t={},r){return tv(t)?this.list(e,{},t):this._client.getAPIList(`/responses/${e}/input_items`,sl,{query:t,...r})}}var sn=function(e,t,r,s,n){if("m"===s)throw TypeError("Private method is not writable");if("a"===s&&!n)throw TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!n:!t.has(e))throw TypeError("Cannot write private member to an object whose class did not declare it");return"a"===s?n.call(e,r):n?n.value=r:t.set(e,r),r},si=function(e,t,r,s){if("a"===r&&!s)throw TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!s:!t.has(e))throw TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(e):s?s.value:t.get(e)};class sa extends rP{constructor(e){super(),ep.add(this),em.set(this,void 0),eg.set(this,void 0),ey.set(this,void 0),sn(this,em,e,"f")}static createResponse(e,t,r){let s=new sa(t);return s._run(()=>s._createOrRetrieveResponse(e,t,{...r,headers:{...r?.headers,"X-Stainless-Helper-Method":"stream"}})),s}async _createOrRetrieveResponse(e,t,r){let s,n=r?.signal;n&&(n.aborted&&this.controller.abort(),n.addEventListener("abort",()=>this.controller.abort())),si(this,ep,"m",ew).call(this);let i=null;for await(let n of("response_id"in t?(s=await e.responses.retrieve(t.response_id,{stream:!0},{...r,signal:this.controller.signal,stream:!0}),i=t.starting_after??null):s=await e.responses.create({...t,stream:!0},{...r,signal:this.controller.signal}),this._connected(),s))si(this,ep,"m",eb).call(this,n,i);if(s.controller.signal?.aborted)throw new eq;return si(this,ep,"m",e_).call(this)}[(em=new WeakMap,eg=new WeakMap,ey=new WeakMap,ep=new WeakSet,ew=function(){this.ended||sn(this,eg,void 0,"f")},eb=function(e,t){if(this.ended)return;let r=(e,r)=>{(null==t||r.sequence_number>t)&&this._emit(e,r)},s=si(this,ep,"m",ev).call(this,e);switch(r("event",e),e.type){case"response.output_text.delta":{let t=s.output[e.output_index];if(!t)throw new eF(`missing output at index ${e.output_index}`);if("message"===t.type){let s=t.content[e.content_index];if(!s)throw new eF(`missing content at index ${e.content_index}`);if("output_text"!==s.type)throw new eF(`expected content to be 'output_text', got ${s.type}`);r("response.output_text.delta",{...e,snapshot:s.text})}break}case"response.function_call_arguments.delta":{let t=s.output[e.output_index];if(!t)throw new eF(`missing output at index ${e.output_index}`);"function_call"===t.type&&r("response.function_call_arguments.delta",{...e,snapshot:t.arguments});break}default:r(e.type,e)}},e_=function(){if(this.ended)throw new eF("stream has ended, this shouldn't happen");let e=si(this,eg,"f");if(!e)throw new eF("request ended without sending any events");sn(this,eg,void 0,"f");let t=function(e,t){var r;return t&&(r=t,rR(r.text?.format))?st(e,t):{...e,output_parsed:null,output:e.output.map(e=>"function_call"===e.type?{...e,parsed_arguments:null}:"message"===e.type?{...e,content:e.content.map(e=>({...e,parsed:null}))}:e)}}(e,si(this,em,"f"));return sn(this,ey,t,"f"),t},ev=function(e){let t=si(this,eg,"f");if(!t){if("response.created"!==e.type)throw new eF(`When snapshot hasn't been set yet, expected 'response.created' event, got ${e.type}`);return sn(this,eg,e.response,"f")}switch(e.type){case"response.output_item.added":t.output.push(e.item);break;case"response.content_part.added":{let r=t.output[e.output_index];if(!r)throw new eF(`missing output at index ${e.output_index}`);"message"===r.type&&r.content.push(e.part);break}case"response.output_text.delta":{let r=t.output[e.output_index];if(!r)throw new eF(`missing output at index ${e.output_index}`);if("message"===r.type){let t=r.content[e.content_index];if(!t)throw new eF(`missing content at index ${e.content_index}`);if("output_text"!==t.type)throw new eF(`expected content to be 'output_text', got ${t.type}`);t.text+=e.delta}break}case"response.function_call_arguments.delta":{let r=t.output[e.output_index];if(!r)throw new eF(`missing output at index ${e.output_index}`);"function_call"===r.type&&(r.arguments+=e.delta);break}case"response.completed":sn(this,eg,e.response,"f")}return t},Symbol.asyncIterator)](){let e=[],t=[],r=!1;return this.on("event",r=>{let s=t.shift();s?s.resolve(r):e.push(r)}),this.on("end",()=>{for(let e of(r=!0,t))e.resolve(void 0);t.length=0}),this.on("abort",e=>{for(let s of(r=!0,t))s.reject(e);t.length=0}),this.on("error",e=>{for(let s of(r=!0,t))s.reject(e);t.length=0}),{next:async()=>e.length?{value:e.shift(),done:!1}:r?{value:void 0,done:!0}:new Promise((e,r)=>t.push({resolve:e,reject:r})).then(e=>e?{value:e,done:!1}:{value:void 0,done:!0}),return:async()=>(this.abort(),{value:void 0,done:!0})}}async finalResponse(){await this.done();let e=si(this,ey,"f");if(!e)throw new eF("stream ended without producing a ChatCompletion");return e}}class so extends tD{constructor(){super(...arguments),this.inputItems=new ss(this._client)}create(e,t){return this._client.post("/responses",{body:e,...t,stream:e.stream??!1})._thenUnwrap(e=>("object"in e&&"response"===e.object&&sr(e),e))}retrieve(e,t={},r){return this._client.get(`/responses/${e}`,{query:t,...r,stream:t?.stream??!1})}del(e,t){return this._client.delete(`/responses/${e}`,{...t,headers:{Accept:"*/*",...t?.headers}})}parse(e,t){return this._client.responses.create(e,t)._thenUnwrap(t=>st(t,e))}stream(e,t){return sa.createResponse(this._client,e,t)}cancel(e,t){return this._client.post(`/responses/${e}/cancel`,{...t,headers:{Accept:"*/*",...t?.headers}})}}class sl extends tX{}so.InputItems=ss;class su extends tD{retrieve(e,t,r,s){return this._client.get(`/evals/${e}/runs/${t}/output_items/${r}`,s)}list(e,t,r={},s){return tv(r)?this.list(e,t,{},r):this._client.getAPIList(`/evals/${e}/runs/${t}/output_items`,sc,{query:r,...s})}}class sc extends tX{}su.OutputItemListResponsesPage=sc;class sh extends tD{constructor(){super(...arguments),this.outputItems=new su(this._client)}create(e,t,r){return this._client.post(`/evals/${e}/runs`,{body:t,...r})}retrieve(e,t,r){return this._client.get(`/evals/${e}/runs/${t}`,r)}list(e,t={},r){return tv(t)?this.list(e,{},t):this._client.getAPIList(`/evals/${e}/runs`,sf,{query:t,...r})}del(e,t,r){return this._client.delete(`/evals/${e}/runs/${t}`,r)}cancel(e,t,r){return this._client.post(`/evals/${e}/runs/${t}`,r)}}class sf extends tX{}sh.RunListResponsesPage=sf,sh.OutputItems=su,sh.OutputItemListResponsesPage=sc;class sd extends tD{constructor(){super(...arguments),this.runs=new sh(this._client)}create(e,t){return this._client.post("/evals",{body:e,...t})}retrieve(e,t){return this._client.get(`/evals/${e}`,t)}update(e,t,r){return this._client.post(`/evals/${e}`,{body:t,...r})}list(e={},t){return tv(e)?this.list({},e):this._client.getAPIList("/evals",sp,{query:e,...t})}del(e,t){return this._client.delete(`/evals/${e}`,t)}}class sp extends tX{}sd.EvalListResponsesPage=sp,sd.Runs=sh,sd.RunListResponsesPage=sf;class sm extends tD{retrieve(e,t,r){return this._client.get(`/containers/${e}/files/${t}/content`,{...r,headers:{Accept:"application/binary",...r?.headers},__binaryResponse:!0})}}class sg extends tD{constructor(){super(...arguments),this.content=new sm(this._client)}create(e,t,r){return this._client.post(`/containers/${e}/files`,tl({body:t,...r}))}retrieve(e,t,r){return this._client.get(`/containers/${e}/files/${t}`,r)}list(e,t={},r){return tv(t)?this.list(e,{},t):this._client.getAPIList(`/containers/${e}/files`,sy,{query:t,...r})}del(e,t,r){return this._client.delete(`/containers/${e}/files/${t}`,{...r,headers:{Accept:"*/*",...r?.headers}})}}class sy extends tX{}sg.FileListResponsesPage=sy,sg.Content=sm;class sw extends tD{constructor(){super(...arguments),this.files=new sg(this._client)}create(e,t){return this._client.post("/containers",{body:e,...t})}retrieve(e,t){return this._client.get(`/containers/${e}`,t)}list(e={},t){return tv(e)?this.list({},e):this._client.getAPIList("/containers",sb,{query:e,...t})}del(e,t){return this._client.delete(`/containers/${e}`,{...t,headers:{Accept:"*/*",...t?.headers}})}}class sb extends tX{}sw.ContainerListResponsesPage=sb,sw.Files=sg,sw.FileListResponsesPage=sy;class s_ extends tg{constructor({baseURL:e=tC("OPENAI_BASE_URL"),apiKey:t=tC("OPENAI_API_KEY"),organization:r=tC("OPENAI_ORG_ID")??null,project:s=tC("OPENAI_PROJECT_ID")??null,...n}={}){if(void 0===t)throw new eF("The OPENAI_API_KEY environment variable is missing or empty; either provide it, or instantiate the OpenAI client with an apiKey option, like new OpenAI({ apiKey: 'My API Key' }).");const i={apiKey:t,organization:r,project:s,...n,baseURL:e||"https://api.openai.com/v1"};if(!i.dangerouslyAllowBrowser&&"u">typeof window&&void 0!==window.document&&"u">typeof navigator)throw new eF("It looks like you're running in a browser-like environment.\n\nThis is disabled by default, as it risks exposing your secret API credentials to attackers.\nIf you understand the risks and have appropriate mitigations in place,\nyou can set the `dangerouslyAllowBrowser` option to `true`, e.g.,\n\nnew OpenAI({ apiKey, dangerouslyAllowBrowser: true });\n\nhttps://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety\n");super({baseURL:i.baseURL,timeout:i.timeout??6e5,httpAgent:i.httpAgent,maxRetries:i.maxRetries,fetch:i.fetch}),this.completions=new tF(this),this.chat=new tK(this),this.embeddings=new tz(this),this.files=new tQ(this),this.images=new tG(this),this.audio=new t2(this),this.moderations=new t8(this),this.models=new t6(this),this.fineTuning=new ro(this),this.graders=new ru(this),this.vectorStores=new rm(this),this.beta=new r3(this),this.batches=new r4(this),this.uploads=new se(this),this.responses=new so(this),this.evals=new sd(this),this.containers=new sw(this),this._options=i,this.apiKey=t,this.organization=r,this.project=s}defaultQuery(){return this._options.defaultQuery}defaultHeaders(e){return{...super.defaultHeaders(e),"OpenAI-Organization":this.organization,"OpenAI-Project":this.project,...this._options.defaultHeaders}}authHeaders(e){return{Authorization:`Bearer ${this.apiKey}`}}stringifyQuery(e){return function(e,t={}){let r,s=e,n=function(e=eB){let t;if(void 0!==e.allowEmptyArrays&&"boolean"!=typeof e.allowEmptyArrays)throw TypeError("`allowEmptyArrays` option can only be `true` or `false`, when provided");if(void 0!==e.encodeDotInKeys&&"boolean"!=typeof e.encodeDotInKeys)throw TypeError("`encodeDotInKeys` option can only be `true` or `false`, when provided");if(null!==e.encoder&&void 0!==e.encoder&&"function"!=typeof e.encoder)throw TypeError("Encoder has to be a function.");let r=e.charset||eB.charset;if(void 0!==e.charset&&"utf-8"!==e.charset&&"iso-8859-1"!==e.charset)throw TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");let s=eA;if(void 0!==e.format){if(!eI.call(eS,e.format))throw TypeError("Unknown format option provided.");s=e.format}let n=eS[s],i=eB.filter;if(("function"==typeof e.filter||eO(e.filter))&&(i=e.filter),t=e.arrayFormat&&e.arrayFormat in e$?e.arrayFormat:"indices"in e?e.indices?"indices":"repeat":eB.arrayFormat,"commaRoundTrip"in e&&"boolean"!=typeof e.commaRoundTrip)throw TypeError("`commaRoundTrip` must be a boolean, or absent");let a=void 0===e.allowDots?!0==!!e.encodeDotInKeys||eB.allowDots:!!e.allowDots;return{addQueryPrefix:"boolean"==typeof e.addQueryPrefix?e.addQueryPrefix:eB.addQueryPrefix,allowDots:a,allowEmptyArrays:"boolean"==typeof e.allowEmptyArrays?!!e.allowEmptyArrays:eB.allowEmptyArrays,arrayFormat:t,charset:r,charsetSentinel:"boolean"==typeof e.charsetSentinel?e.charsetSentinel:eB.charsetSentinel,commaRoundTrip:!!e.commaRoundTrip,delimiter:void 0===e.delimiter?eB.delimiter:e.delimiter,encode:"boolean"==typeof e.encode?e.encode:eB.encode,encodeDotInKeys:"boolean"==typeof e.encodeDotInKeys?e.encodeDotInKeys:eB.encodeDotInKeys,encoder:"function"==typeof e.encoder?e.encoder:eB.encoder,encodeValuesOnly:"boolean"==typeof e.encodeValuesOnly?e.encodeValuesOnly:eB.encodeValuesOnly,filter:i,format:s,formatter:n,serializeDate:"function"==typeof e.serializeDate?e.serializeDate:eB.serializeDate,skipNulls:"boolean"==typeof e.skipNulls?e.skipNulls:eB.skipNulls,sort:"function"==typeof e.sort?e.sort:null,strictNullHandling:"boolean"==typeof e.strictNullHandling?e.strictNullHandling:eB.strictNullHandling}}(t);"function"==typeof n.filter?s=(0,n.filter)("",s):eO(n.filter)&&(r=n.filter);let i=[];if("object"!=typeof s||null===s)return"";let a=e$[n.arrayFormat],o="comma"===a&&n.commaRoundTrip;r||(r=Object.keys(s)),n.sort&&r.sort(n.sort);let l=new WeakMap;for(let e=0;e0?x.join(",")||null:void 0}];else if(eO(c))v=c;else{let e=Object.keys(x);v=h?e.sort(h):e}let R=l?String(r).replace(/\./g,"%2E"):String(r),I=n&&eO(x)&&1===x.length?R+"[]":R;if(i&&eO(x)&&0===x.length)return I+"[]";for(let r=0;r0?c+u:""}(e,{arrayFormat:"brackets"})}}s_.OpenAI=s_,s_.DEFAULT_TIMEOUT=6e5,s_.OpenAIError=eF,s_.APIError=eW,s_.APIConnectionError=eX,s_.APIConnectionTimeoutError=eJ,s_.APIUserAbortError=eq,s_.NotFoundError=ez,s_.ConflictError=eQ,s_.RateLimitError=eG,s_.BadRequestError=eH,s_.AuthenticationError=eV,s_.InternalServerError=eZ,s_.PermissionDeniedError=eK,s_.UnprocessableEntityError=eY,s_.toFile=ts,s_.fileFromPath=u,s_.Completions=tF,s_.Chat=tK,s_.ChatCompletionsPage=tH,s_.Embeddings=tz,s_.Files=tQ,s_.FileObjectsPage=tY,s_.Images=tG,s_.Audio=t2,s_.Moderations=t8,s_.Models=t6,s_.ModelsPage=t5,s_.FineTuning=ro,s_.Graders=ru,s_.VectorStores=rm,s_.VectorStoresPage=rg,s_.VectorStoreSearchResponsesPage=ry,s_.Beta=r3,s_.Batches=r4,s_.BatchesPage=r9,s_.Uploads=se,s_.Responses=so,s_.Evals=sd,s_.EvalListResponsesPage=sp,s_.Containers=sw,s_.ContainerListResponsesPage=sb,e.s(["default",0,s_],356449)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0noytyudtoxih.js b/litellm/proxy/_experimental/out/_next/static/chunks/0noytyudtoxih.js new file mode 100644 index 00000000000..51db10a94b3 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0noytyudtoxih.js @@ -0,0 +1,13 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,165370,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(931067);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M272.9 512l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512zm304 0l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"}}]},name:"double-left",theme:"outlined"};var o=e.i(9583),r=t.forwardRef(function(e,r){return t.createElement(o.default,(0,i.default)({},e,{ref:r,icon:n}))});let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"}}]},name:"double-right",theme:"outlined"};var a=t.forwardRef(function(e,n){return t.createElement(o.default,(0,i.default)({},e,{ref:n,icon:l}))}),c=e.i(801312),s=e.i(286612),u=e.i(343794),d=e.i(211577),m=e.i(410160),g=e.i(209428),p=e.i(392221),f=e.i(914949),b=e.i(404948),h=e.i(244009);e.i(883110);let v={items_per_page:"条/页",jump_to:"跳至",jump_to_confirm:"确定",page:"页",prev_page:"上一页",next_page:"下一页",prev_5:"向前 5 页",next_5:"向后 5 页",prev_3:"向前 3 页",next_3:"向后 3 页",page_size:"页码"};var S=[10,20,50,100];let C=function(e){var i=e.pageSizeOptions,n=void 0===i?S:i,o=e.locale,r=e.changeSize,l=e.pageSize,a=e.goButton,c=e.quickGo,s=e.rootPrefixCls,u=e.disabled,d=e.buildOptionText,m=e.showSizeChanger,g=e.sizeChangerRender,f=t.default.useState(""),h=(0,p.default)(f,2),v=h[0],C=h[1],$=function(){return!v||Number.isNaN(v)?void 0:Number(v)},k="function"==typeof d?d:function(e){return"".concat(e," ").concat(o.items_per_page)},y=function(e){""!==v&&(e.keyCode===b.default.ENTER||"click"===e.type)&&(C(""),null==c||c($()))},x="".concat(s,"-options");if(!m&&!c)return null;var j=null,z=null,E=null;return m&&g&&(j=g({disabled:u,size:l,onSizeChange:function(e){null==r||r(Number(e))},"aria-label":o.page_size,className:"".concat(x,"-size-changer"),options:(n.some(function(e){return e.toString()===l.toString()})?n:n.concat([l]).sort(function(e,t){return(Number.isNaN(Number(e))?0:Number(e))-(Number.isNaN(Number(t))?0:Number(t))})).map(function(e){return{label:k(e),value:e}})})),c&&(a&&(E="boolean"==typeof a?t.default.createElement("button",{type:"button",onClick:y,onKeyUp:y,disabled:u,className:"".concat(x,"-quick-jumper-button")},o.jump_to_confirm):t.default.createElement("span",{onClick:y,onKeyUp:y},a)),z=t.default.createElement("div",{className:"".concat(x,"-quick-jumper")},o.jump_to,t.default.createElement("input",{disabled:u,type:"text",value:v,onChange:function(e){C(e.target.value)},onKeyUp:y,onBlur:function(e){a||""===v||(C(""),e.relatedTarget&&(e.relatedTarget.className.indexOf("".concat(s,"-item-link"))>=0||e.relatedTarget.className.indexOf("".concat(s,"-item"))>=0)||null==c||c($()))},"aria-label":o.page}),o.page,E)),t.default.createElement("li",{className:x},j,z)},$=function(e){var i=e.rootPrefixCls,n=e.page,o=e.active,r=e.className,l=e.showTitle,a=e.onClick,c=e.onKeyPress,s=e.itemRender,m="".concat(i,"-item"),g=(0,u.default)(m,"".concat(m,"-").concat(n),(0,d.default)((0,d.default)({},"".concat(m,"-active"),o),"".concat(m,"-disabled"),!n),r),p=s(n,"page",t.default.createElement("a",{rel:"nofollow"},n));return p?t.default.createElement("li",{title:l?String(n):null,className:g,onClick:function(){a(n)},onKeyDown:function(e){c(e,a,n)},tabIndex:0},p):null};var k=function(e,t,i){return i};function y(){}function x(e){var t=Number(e);return"number"==typeof t&&!Number.isNaN(t)&&isFinite(t)&&Math.floor(t)===t}function j(e,t,i){return Math.floor((i-1)/(void 0===e?t:e))+1}let z=function(e){var n,o,r,l,a=e.prefixCls,c=void 0===a?"rc-pagination":a,s=e.selectPrefixCls,S=e.className,z=e.current,E=e.defaultCurrent,N=e.total,B=void 0===N?0:N,O=e.pageSize,T=e.defaultPageSize,M=e.onChange,w=void 0===M?y:M,I=e.hideOnSinglePage,P=e.align,D=e.showPrevNextJumpers,H=e.showQuickJumper,A=e.showLessItems,_=e.showTitle,R=void 0===_||_,L=e.onShowSizeChange,W=void 0===L?y:L,X=e.locale,q=void 0===X?v:X,F=e.style,K=e.totalBoundaryShowSizeChanger,U=e.disabled,J=e.simple,G=e.showTotal,Q=e.showSizeChanger,V=void 0===Q?B>(void 0===K?50:K):Q,Y=e.sizeChangerRender,Z=e.pageSizeOptions,ee=e.itemRender,et=void 0===ee?k:ee,ei=e.jumpPrevIcon,en=e.jumpNextIcon,eo=e.prevIcon,er=e.nextIcon,el=t.default.useRef(null),ea=(0,f.default)(10,{value:O,defaultValue:void 0===T?10:T}),ec=(0,p.default)(ea,2),es=ec[0],eu=ec[1],ed=(0,f.default)(1,{value:z,defaultValue:void 0===E?1:E,postState:function(e){return Math.max(1,Math.min(e,j(void 0,es,B)))}}),em=(0,p.default)(ed,2),eg=em[0],ep=em[1],ef=t.default.useState(eg),eb=(0,p.default)(ef,2),eh=eb[0],ev=eb[1];(0,t.useEffect)(function(){ev(eg)},[eg]);var eS=Math.max(1,eg-(A?3:5)),eC=Math.min(j(void 0,es,B),eg+(A?3:5));function e$(i,n){var o=i||t.default.createElement("button",{type:"button","aria-label":n,className:"".concat(c,"-item-link")});return"function"==typeof i&&(o=t.default.createElement(i,(0,g.default)({},e))),o}function ek(e){var t=e.target.value,i=j(void 0,es,B);return""===t?t:Number.isNaN(Number(t))?eh:t>=i?i:Number(t)}var ey=B>es&&H;function ex(e){var t=ek(e);switch(t!==eh&&ev(t),e.keyCode){case b.default.ENTER:ej(t);break;case b.default.UP:ej(t-1);break;case b.default.DOWN:ej(t+1)}}function ej(e){if(x(e)&&e!==eg&&x(B)&&B>0&&!U){var t=j(void 0,es,B),i=e;return e>t?i=t:e<1&&(i=1),i!==eh&&ev(i),ep(i),null==w||w(i,es),i}return eg}var ez=eg>1,eE=eg2?i-2:0),o=2;oB?B:eg*es])),eH=null,eA=j(void 0,es,B);if(I&&B<=es)return null;var e_=[],eR={rootPrefixCls:c,onClick:ej,onKeyPress:eM,showTitle:R,itemRender:et,page:-1},eL=eg-1>0?eg-1:0,eW=eg+1=2*eU&&3!==eg&&(e_[0]=t.default.cloneElement(e_[0],{className:(0,u.default)("".concat(c,"-item-after-jump-prev"),e_[0].props.className)}),e_.unshift(eI)),eA-eg>=2*eU&&eg!==eA-2){var e2=e_[e_.length-1];e_[e_.length-1]=t.default.cloneElement(e2,{className:(0,u.default)("".concat(c,"-item-before-jump-next"),e2.props.className)}),e_.push(eH)}1!==eZ&&e_.unshift(t.default.createElement($,(0,i.default)({},eR,{key:1,page:1}))),e0!==eA&&e_.push(t.default.createElement($,(0,i.default)({},eR,{key:eA,page:eA})))}var e6=(n=et(eL,"prev",e$(eo,"prev page")),t.default.isValidElement(n)?t.default.cloneElement(n,{disabled:!ez}):n);if(e6){var e4=!ez||!eA;e6=t.default.createElement("li",{title:R?q.prev_page:null,onClick:eN,tabIndex:e4?null:0,onKeyDown:function(e){eM(e,eN)},className:(0,u.default)("".concat(c,"-prev"),(0,d.default)({},"".concat(c,"-disabled"),e4)),"aria-disabled":e4},e6)}var e3=(o=et(eW,"next",e$(er,"next page")),t.default.isValidElement(o)?t.default.cloneElement(o,{disabled:!eE}):o);e3&&(J?(r=!eE,l=ez?0:null):l=(r=!eE||!eA)?null:0,e3=t.default.createElement("li",{title:R?q.next_page:null,onClick:eB,tabIndex:l,onKeyDown:function(e){eM(e,eB)},className:(0,u.default)("".concat(c,"-next"),(0,d.default)({},"".concat(c,"-disabled"),r)),"aria-disabled":r},e3));var e9=(0,u.default)(c,S,(0,d.default)((0,d.default)((0,d.default)((0,d.default)((0,d.default)({},"".concat(c,"-start"),"start"===P),"".concat(c,"-center"),"center"===P),"".concat(c,"-end"),"end"===P),"".concat(c,"-simple"),J),"".concat(c,"-disabled"),U));return t.default.createElement("ul",(0,i.default)({className:e9,style:F,ref:el},eP),eD,e6,J?eK:e_,e3,t.default.createElement(C,{locale:q,rootPrefixCls:c,disabled:U,selectPrefixCls:void 0===s?"rc-select":s,changeSize:function(e){var t=j(e,es,B),i=eg>t&&0!==t?t:eg;eu(e),ev(i),null==W||W(eg,e),ep(i),null==w||w(i,e)},pageSize:es,pageSizeOptions:Z,quickGo:ey?ej:null,goButton:eF,showSizeChanger:V,sizeChangerRender:Y}))};var E=e.i(727214),N=e.i(242064),B=e.i(517455),O=e.i(150073),T=e.i(408850),M=e.i(327494),w=e.i(104458);e.i(296059);var I=e.i(915654),P=e.i(349942),D=e.i(517458),H=e.i(889943),A=e.i(183293),_=e.i(246422),R=e.i(838378);let L=e=>Object.assign({itemBg:e.colorBgContainer,itemSize:e.controlHeight,itemSizeSM:e.controlHeightSM,itemActiveBg:e.colorBgContainer,itemActiveColor:e.colorPrimary,itemActiveColorHover:e.colorPrimaryHover,itemLinkBg:e.colorBgContainer,itemActiveColorDisabled:e.colorTextDisabled,itemActiveBgDisabled:e.controlItemBgActiveDisabled,itemInputBg:e.colorBgContainer,miniOptionsSizeChangerTop:0},(0,D.initComponentToken)(e)),W=e=>(0,R.mergeToken)(e,{inputOutlineOffset:0,quickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.25).equal(),paginationMiniOptionsMarginInlineStart:e.calc(e.marginXXS).div(2).equal(),paginationMiniQuickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.1).equal(),paginationItemPaddingInline:e.calc(e.marginXXS).mul(1.5).equal(),paginationEllipsisLetterSpacing:e.calc(e.marginXXS).div(2).equal(),paginationSlashMarginInlineStart:e.marginSM,paginationSlashMarginInlineEnd:e.marginSM,paginationEllipsisTextIndent:"0.13em"},(0,D.initInputToken)(e)),X=(0,_.genStyleHooks)("Pagination",e=>{let t=W(e);return[(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,A.resetComponent)(e)),{display:"flex",flexWrap:"wrap",rowGap:e.paddingXS,"&-start":{justifyContent:"start"},"&-center":{justifyContent:"center"},"&-end":{justifyContent:"end"},"ul, ol":{margin:0,padding:0,listStyle:"none"},"&::after":{display:"block",clear:"both",height:0,overflow:"hidden",visibility:"hidden",content:'""'},[`${t}-total-text`]:{display:"inline-block",height:e.itemSize,marginInlineEnd:e.marginXS,lineHeight:(0,I.unit)(e.calc(e.itemSize).sub(2).equal()),verticalAlign:"middle"}}),(e=>{let{componentCls:t}=e;return{[`${t}-item`]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,marginInlineEnd:e.marginXS,fontFamily:e.fontFamily,lineHeight:(0,I.unit)(e.calc(e.itemSize).sub(2).equal()),textAlign:"center",verticalAlign:"middle",listStyle:"none",backgroundColor:e.itemBg,border:`${(0,I.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:0,cursor:"pointer",userSelect:"none",a:{display:"block",padding:`0 ${(0,I.unit)(e.paginationItemPaddingInline)}`,color:e.colorText,"&:hover":{textDecoration:"none"}},[`&:not(${t}-item-active)`]:{"&:hover":{transition:`all ${e.motionDurationMid}`,backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive}},"&-active":{fontWeight:e.fontWeightStrong,backgroundColor:e.itemActiveBg,borderColor:e.colorPrimary,a:{color:e.itemActiveColor},"&:hover":{borderColor:e.colorPrimaryHover},"&:hover a":{color:e.itemActiveColorHover}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-jump-prev, ${t}-jump-next`]:{outline:0,[`${t}-item-container`]:{position:"relative",[`${t}-item-link-icon`]:{color:e.colorPrimary,fontSize:e.fontSizeSM,opacity:0,transition:`all ${e.motionDurationMid}`,"&-svg":{top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,margin:"auto"}},[`${t}-item-ellipsis`]:{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,display:"block",margin:"auto",color:e.colorTextDisabled,letterSpacing:e.paginationEllipsisLetterSpacing,textAlign:"center",textIndent:e.paginationEllipsisTextIndent,opacity:1,transition:`all ${e.motionDurationMid}`}},"&:hover":{[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}}},[` + ${t}-prev, + ${t}-jump-prev, + ${t}-jump-next + `]:{marginInlineEnd:e.marginXS},[` + ${t}-prev, + ${t}-next, + ${t}-jump-prev, + ${t}-jump-next + `]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,color:e.colorText,fontFamily:e.fontFamily,lineHeight:(0,I.unit)(e.itemSize),textAlign:"center",verticalAlign:"middle",listStyle:"none",borderRadius:e.borderRadius,cursor:"pointer",transition:`all ${e.motionDurationMid}`},[`${t}-prev, ${t}-next`]:{outline:0,button:{color:e.colorText,cursor:"pointer",userSelect:"none"},[`${t}-item-link`]:{display:"block",width:"100%",height:"100%",padding:0,fontSize:e.fontSizeSM,textAlign:"center",backgroundColor:"transparent",border:`${(0,I.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:"none",transition:`all ${e.motionDurationMid}`},[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover`]:{[`${t}-item-link`]:{backgroundColor:"transparent"}}},[`${t}-slash`]:{marginInlineEnd:e.paginationSlashMarginInlineEnd,marginInlineStart:e.paginationSlashMarginInlineStart},[`${t}-options`]:{display:"inline-block",marginInlineStart:e.margin,verticalAlign:"middle","&-size-changer":{display:"inline-block",width:"auto"},"&-quick-jumper":{display:"inline-block",height:e.controlHeight,marginInlineStart:e.marginXS,lineHeight:(0,I.unit)(e.controlHeight),verticalAlign:"top",input:Object.assign(Object.assign(Object.assign({},(0,P.genBasicInputStyle)(e)),(0,H.genBaseOutlinedStyle)(e,{borderColor:e.colorBorder,hoverBorderColor:e.colorPrimaryHover,activeBorderColor:e.colorPrimary,activeShadow:e.activeShadow})),{"&[disabled]":Object.assign({},(0,H.genDisabledStyle)(e)),width:e.quickJumperInputWidth,height:e.controlHeight,boxSizing:"border-box",margin:0,marginInlineStart:e.marginXS,marginInlineEnd:e.marginXS})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-simple`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSize,lineHeight:(0,I.unit)(e.itemSize),verticalAlign:"top",[`${t}-item-link`]:{height:e.itemSize,backgroundColor:"transparent",border:0,"&:hover":{backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive},"&::after":{height:e.itemSize,lineHeight:(0,I.unit)(e.itemSize)}}},[`${t}-simple-pager`]:{display:"inline-flex",alignItems:"center",height:e.itemSize,marginInlineEnd:e.marginXS,input:{boxSizing:"border-box",height:"100%",width:e.quickJumperInputWidth,padding:`0 ${(0,I.unit)(e.paginationItemPaddingInline)}`,textAlign:"center",backgroundColor:e.itemInputBg,border:`${(0,I.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadius,outline:"none",transition:`border-color ${e.motionDurationMid}`,color:"inherit","&:hover":{borderColor:e.colorPrimary},"&:focus":{borderColor:e.colorPrimaryHover,boxShadow:`${(0,I.unit)(e.inputOutlineOffset)} 0 ${(0,I.unit)(e.controlOutlineWidth)} ${e.controlOutline}`},"&[disabled]":{color:e.colorTextDisabled,backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,cursor:"not-allowed"}}},[`&${t}-disabled`]:{[`${t}-prev, ${t}-next`]:{[`${t}-item-link`]:{"&:hover, &:active":{backgroundColor:"transparent"}}}},[`&${t}-mini`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSizeSM,lineHeight:(0,I.unit)(e.itemSizeSM),[`${t}-item-link`]:{height:e.itemSizeSM,"&::after":{height:e.itemSizeSM,lineHeight:(0,I.unit)(e.itemSizeSM)}}},[`${t}-simple-pager`]:{height:e.itemSizeSM,input:{width:e.paginationMiniQuickJumperInputWidth}}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-mini ${t}-total-text, &${t}-mini ${t}-simple-pager`]:{height:e.itemSizeSM,lineHeight:(0,I.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-item`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,I.unit)(e.calc(e.itemSizeSM).sub(2).equal())},[`&${t}-mini ${t}-prev, &${t}-mini ${t}-next`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,I.unit)(e.itemSizeSM)},[`&${t}-mini:not(${t}-disabled)`]:{[`${t}-prev, ${t}-next`]:{[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover ${t}-item-link`]:{backgroundColor:"transparent"}}},[` + &${t}-mini ${t}-prev ${t}-item-link, + &${t}-mini ${t}-next ${t}-item-link + `]:{backgroundColor:"transparent",borderColor:"transparent","&::after":{height:e.itemSizeSM,lineHeight:(0,I.unit)(e.itemSizeSM)}},[`&${t}-mini ${t}-jump-prev, &${t}-mini ${t}-jump-next`]:{height:e.itemSizeSM,marginInlineEnd:0,lineHeight:(0,I.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-options`]:{marginInlineStart:e.paginationMiniOptionsMarginInlineStart,"&-size-changer":{top:e.miniOptionsSizeChangerTop},"&-quick-jumper":{height:e.itemSizeSM,lineHeight:(0,I.unit)(e.itemSizeSM),input:Object.assign(Object.assign({},(0,P.genInputSmallStyle)(e)),{width:e.paginationMiniQuickJumperInputWidth,height:e.controlHeightSM})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-disabled`]:{"&, &:hover":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}},"&:focus-visible":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}}},[`&${t}-disabled`]:{cursor:"not-allowed",[`${t}-item`]:{cursor:"not-allowed",backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"},a:{color:e.colorTextDisabled,backgroundColor:"transparent",border:"none",cursor:"not-allowed"},"&-active":{borderColor:e.colorBorder,backgroundColor:e.itemActiveBgDisabled,"&:hover, &:active":{backgroundColor:e.itemActiveBgDisabled},a:{color:e.itemActiveColorDisabled}}},[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:hover, &:active":{backgroundColor:"transparent"},[`${t}-simple&`]:{backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"}}},[`${t}-simple-pager`]:{color:e.colorTextDisabled},[`${t}-jump-prev, ${t}-jump-next`]:{[`${t}-item-link-icon`]:{opacity:0},[`${t}-item-ellipsis`]:{opacity:1}}}}})(e)),{[`@media only screen and (max-width: ${e.screenLG}px)`]:{[`${t}-item`]:{"&-after-jump-prev, &-before-jump-next":{display:"none"}}},[`@media only screen and (max-width: ${e.screenSM}px)`]:{[`${t}-options`]:{display:"none"}}}),[`&${e.componentCls}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t}=e;return{[`${t}:not(${t}-disabled)`]:{[`${t}-item`]:Object.assign({},(0,A.genFocusStyle)(e)),[`${t}-jump-prev, ${t}-jump-next`]:{"&:focus-visible":Object.assign({[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}},(0,A.genFocusOutline)(e))},[`${t}-prev, ${t}-next`]:{[`&:focus-visible ${t}-item-link`]:(0,A.genFocusOutline)(e)}}}})(t)]},L),q=(0,_.genSubStyleComponent)(["Pagination","bordered"],e=>(e=>{let{componentCls:t}=e;return{[`${t}${t}-bordered${t}-disabled:not(${t}-mini)`]:{"&, &:hover":{[`${t}-item-link`]:{borderColor:e.colorBorder}},"&:focus-visible":{[`${t}-item-link`]:{borderColor:e.colorBorder}},[`${t}-item, ${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,[`&:hover:not(${t}-item-active)`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,a:{color:e.colorTextDisabled}},[`&${t}-item-active`]:{backgroundColor:e.itemActiveBgDisabled}},[`${t}-prev, ${t}-next`]:{"&:hover button":{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,color:e.colorTextDisabled},[`${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder}}},[`${t}${t}-bordered:not(${t}-mini)`]:{[`${t}-prev, ${t}-next`]:{"&:hover button":{borderColor:e.colorPrimaryHover,backgroundColor:e.itemBg},[`${t}-item-link`]:{backgroundColor:e.itemLinkBg,borderColor:e.colorBorder},[`&:hover ${t}-item-link`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,color:e.colorPrimary},[`&${t}-disabled`]:{[`${t}-item-link`]:{borderColor:e.colorBorder,color:e.colorTextDisabled}}},[`${t}-item`]:{backgroundColor:e.itemBg,border:`${(0,I.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,[`&:hover:not(${t}-item-active)`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,a:{color:e.colorPrimary}},"&-active":{borderColor:e.colorPrimary}}}}})(W(e)),L);function F(e){return(0,t.useMemo)(()=>"boolean"==typeof e?[e,{}]:e&&"object"==typeof e?[!0,e]:[void 0,void 0],[e])}var K=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};e.s(["default",0,e=>{let{align:i,prefixCls:n,selectPrefixCls:o,className:l,rootClassName:d,style:m,size:g,locale:p,responsive:f,showSizeChanger:b,selectComponentClass:h,pageSizeOptions:v}=e,S=K(e,["align","prefixCls","selectPrefixCls","className","rootClassName","style","size","locale","responsive","showSizeChanger","selectComponentClass","pageSizeOptions"]),{xs:C}=(0,O.default)(f),[,$]=(0,w.useToken)(),{getPrefixCls:k,direction:y,showSizeChanger:x,className:j,style:I}=(0,N.useComponentConfig)("pagination"),P=k("pagination",n),[D,H,A]=X(P),_=(0,B.default)(g),R="small"===_||!!(C&&!_&&f),[L]=(0,T.useLocale)("Pagination",E.default),W=Object.assign(Object.assign({},L),p),[U,J]=F(b),[G,Q]=F(x),V=null!=J?J:Q,Y=h||M.default,Z=t.useMemo(()=>v?v.map(e=>Number(e)):void 0,[v]),ee=t.useMemo(()=>{let e=t.createElement("span",{className:`${P}-item-ellipsis`},"•••"),i=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===y?t.createElement(s.default,null):t.createElement(c.default,null)),n=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===y?t.createElement(c.default,null):t.createElement(s.default,null));return{prevIcon:i,nextIcon:n,jumpPrevIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===y?t.createElement(a,{className:`${P}-item-link-icon`}):t.createElement(r,{className:`${P}-item-link-icon`}),e)),jumpNextIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===y?t.createElement(r,{className:`${P}-item-link-icon`}):t.createElement(a,{className:`${P}-item-link-icon`}),e))}},[y,P]),et=k("select",o),ei=(0,u.default)({[`${P}-${i}`]:!!i,[`${P}-mini`]:R,[`${P}-rtl`]:"rtl"===y,[`${P}-bordered`]:$.wireframe},j,l,d,H,A),en=Object.assign(Object.assign({},I),m);return D(t.createElement(t.Fragment,null,$.wireframe&&t.createElement(q,{prefixCls:P}),t.createElement(z,Object.assign({},ee,S,{style:en,prefixCls:P,selectPrefixCls:et,className:ei,locale:W,pageSizeOptions:Z,showSizeChanger:null!=U?U:G,sizeChangerRender:e=>{var i;let{disabled:n,size:o,onSizeChange:r,"aria-label":l,className:a,options:c}=e,{className:s,onChange:d}=V||{},m=null==(i=c.find(e=>String(e.value)===String(o)))?void 0:i.value;return t.createElement(Y,Object.assign({disabled:n,showSearch:!0,popupMatchSelectWidth:!1,getPopupContainer:e=>e.parentNode,"aria-label":l,options:c},V,{value:m,onChange:(e,t)=>{null==r||r(e),null==d||d(e,t)},size:R?"small":"middle",className:(0,u.default)(a,s)}))}}))))}],165370)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var o=e.i(9583),r=i.forwardRef(function(e,r){return i.createElement(o.default,(0,t.default)({},e,{ref:r,icon:n}))});e.s(["ExclamationCircleOutlined",0,r],270377)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),i=e.i(732961),n=e.i(289882),o=e.i(170517),r=e.i(628882),l=e.i(320890),a=e.i(104458),c=e.i(722319),s=e.i(8398),u=e.i(279728);e.i(765846);var d=e.i(602716),m=e.i(328052),g=e.i(135551);let p=(e,t)=>new g.FastColor(e).setA(t).toRgbString(),f=(e,t)=>new g.FastColor(e).lighten(t).toHexString(),b=e=>{let t=(0,d.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},h=(e,t)=>{let i=e||"#000",n=t||"#fff";return{colorBgBase:i,colorTextBase:n,colorText:p(n,.85),colorTextSecondary:p(n,.65),colorTextTertiary:p(n,.45),colorTextQuaternary:p(n,.25),colorFill:p(n,.18),colorFillSecondary:p(n,.12),colorFillTertiary:p(n,.08),colorFillQuaternary:p(n,.04),colorBgSolid:p(n,.95),colorBgSolidHover:p(n,1),colorBgSolidActive:p(n,.9),colorBgElevated:f(i,12),colorBgContainer:f(i,8),colorBgLayout:f(i,0),colorBgSpotlight:f(i,26),colorBgBlur:p(n,.04),colorBorder:f(i,26),colorBorderSecondary:f(i,19)}},v={defaultSeed:l.defaultConfig.token,useToken:function(){let[e,t,i]=(0,a.useToken)();return{theme:e,token:t,hashId:i}},defaultAlgorithm:c.default,darkAlgorithm:(e,t)=>{let i=Object.keys(o.defaultPresetColors).map(t=>{let i=(0,d.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,n,o)=>(e[`${t}-${o+1}`]=i[o],e[`${t}${o+1}`]=i[o],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),n=null!=t?t:(0,c.default)(e),r=(0,m.default)(e,{generateColorPalettes:b,generateNeutralColorPalettes:h});return Object.assign(Object.assign(Object.assign(Object.assign({},n),i),r),{colorPrimaryBg:r.colorPrimaryBorder,colorPrimaryBgHover:r.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let i=null!=t?t:(0,c.default)(e),n=i.fontSizeSM,o=i.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},i),function(e){let{sizeUnit:t,sizeStep:i}=e,n=i-2;return{sizeXXL:t*(n+10),sizeXL:t*(n+6),sizeLG:t*(n+2),sizeMD:t*(n+2),sizeMS:t*(n+1),size:t*n,sizeSM:t*n,sizeXS:t*(n-1),sizeXXS:t*(n-1)}}(null!=t?t:e)),(0,u.default)(n)),{controlHeight:o}),(0,s.default)(Object.assign(Object.assign({},i),{controlHeight:o})))},getDesignToken:e=>{let l=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):n.default,a=Object.assign(Object.assign({},o.default),null==e?void 0:e.token);return(0,i.getComputedToken)(a,{override:null==e?void 0:e.token},l,r.default)},defaultConfig:l.defaultConfig,_internalContext:l.DesignTokenContext};e.s(["theme",0,v],368869)},127952,e=>{"use strict";var t=e.i(843476),i=e.i(560445),n=e.i(175712),o=e.i(869216),r=e.i(311451),l=e.i(212931),a=e.i(898586),c=e.i(368869),s=e.i(270377),u=e.i(271645);e.s(["default",0,function({isOpen:e,title:d,alertMessage:m,message:g,resourceInformationTitle:p,resourceInformation:f,onCancel:b,onOk:h,confirmLoading:v,requiredConfirmation:S}){let{Title:C,Text:$}=a.Typography,{token:k}=c.theme.useToken(),[y,x]=(0,u.useState)("");return(0,u.useEffect)(()=>{e&&x("")},[e]),(0,t.jsx)(l.Modal,{title:d,open:e,onOk:h,onCancel:b,confirmLoading:v,okText:v?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!S&&y!==S||v},cancelButtonProps:{disabled:v},children:(0,t.jsxs)("div",{className:"space-y-4",children:[m&&(0,t.jsx)(i.Alert,{message:m,type:"warning"}),(0,t.jsx)(n.Card,{title:p,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:k.colorErrorBg,borderColor:k.colorErrorBorder}},style:{backgroundColor:k.colorErrorBg,borderColor:k.colorErrorBorder},children:(0,t.jsx)(o.Descriptions,{column:1,size:"small",children:f&&f.map(({label:e,value:i,...n})=>(0,t.jsx)(o.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)($,{...n,children:i??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)($,{children:g})}),S&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)($,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)($,{children:"Type "}),(0,t.jsx)($,{strong:!0,type:"danger",children:S}),(0,t.jsx)($,{children:" to confirm deletion:"})]}),(0,t.jsx)(r.Input,{value:y,onChange:e=>x(e.target.value),placeholder:S,className:"rounded-md",prefix:(0,t.jsx)(s.ExclamationCircleOutlined,{style:{color:k.colorError}}),autoFocus:!0})]})]})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0nvi66x2vqzm2.js b/litellm/proxy/_experimental/out/_next/static/chunks/0nvi66x2vqzm2.js new file mode 100644 index 00000000000..3dec1617e24 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0nvi66x2vqzm2.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,364769,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(237016),a=e.i(464571),r=e.i(888259);e.s(["default",0,({apiKey:e})=>{let[i,n]=(0,l.useState)(!1);return(0,t.jsxs)("div",{children:[(0,t.jsxs)("p",{className:"mb-2",children:["Please save this secret key somewhere safe and accessible. For security reasons,"," ",(0,t.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]}),(0,t.jsx)("p",{className:"text-sm text-gray-600 mt-3 mb-1",children:"Virtual Key:"}),(0,t.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,t.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal",margin:0},children:e})}),(0,t.jsx)(s.CopyToClipboard,{text:e,onCopy:()=>{n(!0),r.default.success("Key copied to clipboard"),setTimeout(()=>n(!1),2e3)},children:(0,t.jsx)(a.Button,{type:"primary",style:{marginTop:12},children:i?"Copied!":"Copy Virtual Key"})})]})}])},390605,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(602869),a=e.i(599724),r=e.i(482725),i=e.i(91739),n=e.i(500727),o=e.i(531516),d=e.i(696609);e.s(["default",0,({accessToken:e,selectedServers:c,toolPermissions:u,onChange:m,disabled:p=!1})=>{let{data:g=[]}=(0,n.useMCPServers)(),[h,x]=(0,l.useState)({}),[y,f]=(0,l.useState)({}),[b,_]=(0,l.useState)({}),[j,v]=(0,l.useState)({}),w=(0,l.useRef)(u);(0,l.useEffect)(()=>{w.current=u},[u]);let k=(0,l.useMemo)(()=>0===c.length?[]:g.filter(e=>c.includes(e.server_id)),[g,c]),N=async(e,t)=>{f(t=>({...t,[e]:!0})),_(t=>({...t,[e]:""}));try{let l=await (0,s.listMCPTools)(t,e);if(l.error)_(t=>({...t,[e]:l.message||"Failed to fetch tools"})),x(t=>({...t,[e]:[]}));else{let t=l.tools||[];x(l=>({...l,[e]:t}));let s=w.current;if(!s[e]&&t.length>0){let l=t.filter(e=>"delete"!==(0,d.classifyToolOp)(e.name,e.description||"")).map(e=>e.name);m({...s,[e]:l})}}}catch(t){console.error(`Error fetching tools for server ${e}:`,t),_(t=>({...t,[e]:"Failed to fetch tools"})),x(t=>({...t,[e]:[]}))}finally{f(t=>({...t,[e]:!1}))}};(0,l.useEffect)(()=>{k.forEach(t=>{h[t.server_id]||y[t.server_id]||N(t.server_id,e)})},[k,e]);let S=(e,t)=>{m({...u,[e]:t})};return 0===c.length?null:(0,t.jsx)("div",{className:"space-y-4",children:k.map(e=>{let l=e.server_name||e.alias||e.server_id,s=h[e.server_id]||[],n=u[e.server_id]||[],d=y[e.server_id],c=b[e.server_id],g=j[e.server_id]??"crud";return(0,t.jsxs)("div",{className:"border rounded-lg bg-gray-50",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between p-4 border-b bg-white rounded-t-lg",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-semibold text-gray-900",children:l}),e.description&&(0,t.jsx)(a.Text,{className:"text-sm text-gray-500",children:e.description})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[!p&&s.length>0&&(0,t.jsx)(i.Radio.Group,{value:g,onChange:t=>v(l=>({...l,[e.server_id]:t.target.value})),size:"small",optionType:"button",buttonStyle:"solid",options:[{label:"Risk Groups",value:"crud"},{label:"Flat List",value:"flat"}]}),!p&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;let l;return l=h[t=e.server_id]||[],void m({...u,[t]:l.map(e=>e.name)})},disabled:d,children:"Select All"}),(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;return t=e.server_id,void m({...u,[t]:[]})},disabled:d,children:"Deselect All"})]})]})]}),(0,t.jsxs)("div",{className:"p-4",children:[d&&(0,t.jsxs)("div",{className:"flex items-center justify-center py-8",children:[(0,t.jsx)(r.Spin,{size:"large"}),(0,t.jsx)(a.Text,{className:"ml-3 text-gray-500",children:"Loading tools..."})]}),c&&!d&&(0,t.jsxs)("div",{className:"p-4 bg-red-50 border border-red-200 rounded-lg text-center",children:[(0,t.jsx)(a.Text,{className:"text-red-600 font-medium",children:"Unable to load tools"}),(0,t.jsx)(a.Text,{className:"text-sm text-red-500 mt-1",children:c})]}),!d&&!c&&s.length>0&&"crud"===g&&(0,t.jsx)(o.default,{tools:s,value:u[e.server_id]?n:void 0,onChange:t=>S(e.server_id,t),readOnly:p}),!d&&!c&&s.length>0&&"flat"===g&&(0,t.jsx)("div",{className:"space-y-2",children:s.map(l=>{let s=n.includes(l.name);return(0,t.jsxs)("div",{className:"flex items-start gap-2",children:[(0,t.jsx)("input",{type:"checkbox",checked:s,onChange:()=>{if(p)return;let t=s?n.filter(e=>e!==l.name):[...n,l.name];S(e.server_id,t)},disabled:p,className:"mt-0.5"}),(0,t.jsx)("div",{className:"flex-1 min-w-0",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900",children:l.name}),(0,t.jsxs)(a.Text,{className:"text-sm text-gray-500",children:["- ",l.description||"No description"]})]})})]},l.name)})}),!d&&!c&&0===s.length&&(0,t.jsx)("div",{className:"text-center py-6",children:(0,t.jsx)(a.Text,{className:"text-gray-500",children:"No tools available"})})]})]},e.server_id)})})}])},207082,e=>{"use strict";var t=e.i(619273),l=e.i(266027),s=e.i(243652),a=e.i(602869),r=e.i(431703),i=e.i(135214);let n=(0,s.createQueryKeys)("keys"),o=async(e,t,l,s={})=>{try{let i=(0,a.getProxyBaseUrl)(),n=new URLSearchParams(Object.entries({team_id:s.teamID,project_id:s.projectID,agent_id:s.agentID,organization_id:s.organizationID,key_alias:s.selectedKeyAlias,key_hash:s.keyHash,user_id:s.userID,page:t,size:l,sort_by:s.sortBy,sort_order:s.sortOrder,expand:s.expand,status:s.status,return_full_object:"true",include_team_keys:"true",include_created_by_keys:"true",substring_matching:"true"}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),o=`${i?`${i}/key/list`:"/key/list"}?${n}`,d=await fetch(o,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!d.ok){let e=await d.json(),t=(0,r.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return await d.json()}catch(e){throw console.error("Failed to list keys:",e),e}},d=(0,s.createQueryKeys)("deletedKeys");e.s(["keyKeys",0,n,"useDeletedKeys",0,(e,s,a={})=>{let{accessToken:r}=(0,i.default)();return(0,l.useQuery)({queryKey:d.list({page:e,limit:s,...a}),queryFn:async()=>await o(r,e,s,{...a,status:"deleted"}),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})},"useKeys",0,(e,s,a={})=>{let{accessToken:r}=(0,i.default)();return(0,l.useQuery)({queryKey:n.list({page:e,limit:s,...a}),queryFn:async()=>await o(r,e,s,a),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})}])},510674,e=>{"use strict";var t=e.i(266027),l=e.i(243652),s=e.i(602869),a=e.i(431703),r=e.i(135214),i=e.i(708347);let n=(0,l.createQueryKeys)("projects"),o=[...i.all_admin_roles,...i.internalUserRoles],d=async e=>{let t=(0,s.getProxyBaseUrl)(),l=`${t}/project/list`,r=await fetch(l,{method:"GET",headers:{[(0,s.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=(0,a.deriveErrorMessage)(e);throw(0,s.handleError)(t),Error(t)}return r.json()};e.s(["projectKeys",0,n,"useProjects",0,()=>{let{accessToken:e,userRole:l}=(0,r.default)();return(0,t.useQuery)({queryKey:n.list({}),queryFn:async()=>d(e),enabled:!!e&&o.includes(l)})}])},557662,e=>{"use strict";let t="/ui/assets/logos/",l=[{id:"arize",displayName:"Arize",logo:`${t}arize.png`,supports_key_team_logging:!0,dynamic_params:{arize_api_key:"password",arize_space_id:"password"},description:"Arize Logging Integration"},{id:"braintrust",displayName:"Braintrust",logo:`${t}braintrust.png`,supports_key_team_logging:!1,dynamic_params:{braintrust_api_key:"password",braintrust_project_name:"text"},description:"Braintrust Logging Integration"},{id:"custom_callback_api",displayName:"Custom Callback API",logo:`${t}custom.svg`,supports_key_team_logging:!0,dynamic_params:{custom_callback_api_url:"text",custom_callback_api_headers:"text"},description:"Custom Callback API Logging Integration"},{id:"galileo",displayName:"Galileo",logo:`${t}galileo.ico`,supports_key_team_logging:!1,dynamic_params:{GALILEO_API_KEY:"password",GALILEO_PROJECT_ID:"text",GALILEO_LOG_STREAM_ID:"text",GALILEO_BASE_URL:"text",GALILEO_USERNAME:"text",GALILEO_PASSWORD:"password"},description:"Galileo AI Observability Integration"},{id:"datadog",displayName:"Datadog",logo:`${t}datadog.png`,supports_key_team_logging:!1,dynamic_params:{dd_api_key:"password",dd_site:"text"},description:"Datadog Logging Integration"},{id:"lago",displayName:"Lago",logo:`${t}lago.svg`,supports_key_team_logging:!1,dynamic_params:{lago_api_url:"text",lago_api_key:"password"},description:"Lago Billing Logging Integration"},{id:"langfuse",displayName:"Langfuse",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v2 Logging Integration"},{id:"langfuse_otel",displayName:"Langfuse OTEL",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v3 OTEL Logging Integration"},{id:"langsmith",displayName:"LangSmith",logo:`${t}langsmith.png`,supports_key_team_logging:!0,dynamic_params:{langsmith_api_key:"password",langsmith_project:"text",langsmith_base_url:"text",langsmith_sampling_rate:"number"},description:"Langsmith Logging Integration"},{id:"openmeter",displayName:"OpenMeter",logo:`${t}openmeter.png`,supports_key_team_logging:!1,dynamic_params:{openmeter_api_key:"password",openmeter_base_url:"text"},description:"OpenMeter Logging Integration"},{id:"otel",displayName:"Open Telemetry",logo:`${t}otel.png`,supports_key_team_logging:!1,dynamic_params:{otel_endpoint:"text",otel_headers:"text"},description:"OpenTelemetry Logging Integration"},{id:"s3",displayName:"S3",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{s3_bucket_name:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"S3 Bucket (AWS) Logging Integration"},{id:"SQS",displayName:"SQS",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{sqs_queue_url:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"SQS Queue (AWS) Logging Integration"}],s=l.reduce((e,t)=>(e[t.displayName]=t,e),{}),a=l.reduce((e,t)=>(e[t.displayName]=t.id,e),{}),r=l.reduce((e,t)=>(e[t.id]=t.displayName,e),{});e.s(["callbackInfo",0,s,"callback_map",0,a,"mapDisplayToInternalNames",0,e=>e.map(e=>a[e]||e),"mapInternalToDisplayNames",0,e=>e.map(e=>r[e]||e),"reverse_callback_map",0,r])},810757,477386,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"}))});e.s(["CogIcon",0,l],810757);let s=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"}))});e.s(["BanIcon",0,s],477386)},552130,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select agents",disabled:d=!1})=>{let[c,u]=(0,l.useState)([]),[m,p]=(0,l.useState)([]),[g,h]=(0,l.useState)(!1);(0,l.useEffect)(()=>{(async()=>{if(n){h(!0);try{let e=await (0,a.getAgentsList)(n),t=e?.agents||[];u(t);let l=new Set;t.forEach(e=>{let t=e.agent_access_groups;t&&Array.isArray(t)&&t.forEach(e=>l.add(e))}),p(Array.from(l))}catch(e){console.error("Error fetching agents:",e)}finally{h(!1)}}})()},[n]);let x=[...m.map(e=>({label:e,value:`group:${e}`,isAccessGroup:!0,searchText:`${e} Access Group`})),...c.map(e=>({label:`${e.agent_name||e.agent_id}`,value:e.agent_id,isAccessGroup:!1,searchText:`${e.agent_name||e.agent_id} ${e.agent_id} Agent`}))],y=[...r?.agents||[],...(r?.accessGroups||[]).map(e=>`group:${e}`)];return(0,t.jsx)("div",{children:(0,t.jsx)(s.Select,{mode:"multiple",placeholder:o,onChange:t=>{e({agents:t.filter(e=>!e.startsWith("group:")),accessGroups:t.filter(e=>e.startsWith("group:")).map(e=>e.replace("group:",""))})},value:y,loading:g,className:i,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:d,filterOption:(e,t)=>(x.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:x.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,label:e.label,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:e.isAccessGroup?"#52c41a":"#722ed1",flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:e.isAccessGroup?"#52c41a":"#722ed1",fontSize:"12px",fontWeight:500,opacity:.8},children:e.isAccessGroup?"Access Group":"Agent"})]})},e.value))})})}])},9314,645526,e=>{"use strict";var t=e.i(843476),l=e.i(199133),s=e.i(981339);e.i(247167);var a=e.i(931067),r=e.i(271645);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M824.2 699.9a301.55 301.55 0 00-86.4-60.4C783.1 602.8 812 546.8 812 484c0-110.8-92.4-201.7-203.2-200-109.1 1.7-197 90.6-197 200 0 62.8 29 118.8 74.2 155.5a300.95 300.95 0 00-86.4 60.4C345 754.6 314 826.8 312 903.8a8 8 0 008 8.2h56c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5A226.62 226.62 0 01612 684c60.9 0 118.2 23.7 161.3 66.8C814.5 792 838 846.3 840 904.3c.1 4.3 3.7 7.7 8 7.7h56a8 8 0 008-8.2c-2-77-33-149.2-87.8-203.9zM612 612c-34.2 0-66.4-13.3-90.5-37.5a126.86 126.86 0 01-37.5-91.8c.3-32.8 13.4-64.5 36.3-88 24-24.6 56.1-38.3 90.4-38.7 33.9-.3 66.8 12.9 91 36.6 24.8 24.3 38.4 56.8 38.4 91.4 0 34.2-13.3 66.3-37.5 90.5A127.3 127.3 0 01612 612zM361.5 510.4c-.9-8.7-1.4-17.5-1.4-26.4 0-15.9 1.5-31.4 4.3-46.5.7-3.6-1.2-7.3-4.5-8.8-13.6-6.1-26.1-14.5-36.9-25.1a127.54 127.54 0 01-38.7-95.4c.9-32.1 13.8-62.6 36.3-85.6 24.7-25.3 57.9-39.1 93.2-38.7 31.9.3 62.7 12.6 86 34.4 7.9 7.4 14.7 15.6 20.4 24.4 2 3.1 5.9 4.4 9.3 3.2 17.6-6.1 36.2-10.4 55.3-12.4 5.6-.6 8.8-6.6 6.3-11.6-32.5-64.3-98.9-108.7-175.7-109.9-110.9-1.7-203.3 89.2-203.3 199.9 0 62.8 28.9 118.8 74.2 155.5-31.8 14.7-61.1 35-86.5 60.4-54.8 54.7-85.8 126.9-87.8 204a8 8 0 008 8.2h56.1c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5 29.4-29.4 65.4-49.8 104.7-59.7 3.9-1 6.5-4.7 6-8.7z"}}]},name:"team",theme:"outlined"};var n=e.i(9583),o=r.forwardRef(function(e,t){return r.createElement(n.default,(0,a.default)({},e,{ref:t,icon:i}))});e.s(["TeamOutlined",0,o],645526);var d=e.i(599724),c=e.i(263147);e.s(["default",0,({value:e,onChange:a,placeholder:r="Select access groups",disabled:i=!1,style:n,className:u,showLabel:m=!1,labelText:p="Access Group",allowClear:g=!0})=>{let{data:h,isLoading:x,isError:y}=(0,c.useAccessGroups)();if(x)return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(d.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(o,{className:"mr-2"})," ",p]}),(0,t.jsx)(s.Skeleton.Input,{active:!0,block:!0,style:{height:32,...n}})]});let f=(h??[]).map(e=>({label:(0,t.jsxs)("span",{children:[(0,t.jsx)("span",{className:"font-medium",children:e.access_group_name})," ",(0,t.jsxs)("span",{className:"text-gray-400 text-xs",children:["(",e.access_group_id,")"]})]}),value:e.access_group_id,selectedLabel:e.access_group_name,searchText:`${e.access_group_name} ${e.access_group_id}`}));return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(d.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(o,{className:"mr-2"})," ",p]}),(0,t.jsx)(l.Select,{mode:"multiple",value:e,placeholder:r,onChange:a,disabled:i,allowClear:g,showSearch:!0,style:{width:"100%",...n},className:`rounded-md ${u??""}`,notFoundContent:y?(0,t.jsx)("span",{className:"text-red-500",children:"Failed to load access groups"}):"No access groups found",filterOption:(e,t)=>(f.find(e=>e.value===t?.value)?.searchText??"").toLowerCase().includes(e.toLowerCase()),optionLabelProp:"selectedLabel",options:f.map(e=>({label:e.label,value:e.value,selectedLabel:e.selectedLabel}))})]})}],9314)},392110,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(199133),a=e.i(592968),r=e.i(312361),i=e.i(790848),n=e.i(536916),o=e.i(827252),d=e.i(779241);let{Option:c}=s.Select;e.s(["default",0,({form:e,autoRotationEnabled:u,onAutoRotationChange:m,rotationInterval:p,onRotationIntervalChange:g,isCreateMode:h=!1,neverExpire:x=!1,onNeverExpireChange:y})=>{let f=p&&!["7d","30d","90d","180d","365d"].includes(p),[b,_]=(0,l.useState)(f),[j,v]=(0,l.useState)(f?p:""),[w,k]=(0,l.useState)(e?.getFieldValue?.("duration")||"");return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Key Expiry Settings"}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Expire Key"}),(0,t.jsx)(a.Tooltip,{title:"Set when this key should expire. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days). Leave empty to keep the current expiry unchanged.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),!h&&y&&(0,t.jsx)(n.Checkbox,{checked:x,onChange:t=>{let l=t.target.checked;y(l),l&&(k(""),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",""):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:""}))},className:"ml-2 text-sm font-normal text-gray-600",children:"Never Expire"})]}),(0,t.jsx)(d.TextInput,{name:"duration",placeholder:h?"e.g., 30d or leave empty to never expire":"e.g., 30d",className:"w-full",value:w,onValueChange:t=>{k(t),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",t):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:t})},disabled:!h&&x})]})]}),(0,t.jsx)(r.Divider,{}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Auto-Rotation Settings"}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Enable Auto-Rotation"}),(0,t.jsx)(a.Tooltip,{title:"Key will automatically regenerate at the specified interval for enhanced security.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsx)(i.Switch,{checked:u,onChange:m,size:"default",className:u?"":"bg-gray-400"})]}),u&&(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Rotation Interval"}),(0,t.jsx)(a.Tooltip,{title:"How often the key should be automatically rotated. Choose the interval that best fits your security requirements.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)(s.Select,{value:b?"custom":p,onChange:e=>{"custom"===e?_(!0):(_(!1),v(""),g(e))},className:"w-full",placeholder:"Select interval",children:[(0,t.jsx)(c,{value:"7d",children:"7 days"}),(0,t.jsx)(c,{value:"30d",children:"30 days"}),(0,t.jsx)(c,{value:"90d",children:"90 days"}),(0,t.jsx)(c,{value:"180d",children:"180 days"}),(0,t.jsx)(c,{value:"365d",children:"365 days"}),(0,t.jsx)(c,{value:"custom",children:"Custom interval"})]}),b&&(0,t.jsxs)("div",{className:"space-y-1",children:[(0,t.jsx)(d.TextInput,{value:j,onChange:e=>{let t=e.target.value;v(t),g(t)},placeholder:"e.g., 1s, 5m, 2h, 14d"}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Supported formats: seconds (s), minutes (m), hours (h), days (d)"})]})]})]})]}),u&&(0,t.jsx)("div",{className:"bg-blue-50 p-3 rounded-md text-sm text-blue-700",children:"When rotation occurs, you'll receive a notification with the new key. The old key will be deactivated after a brief grace period."})]})]})}])},844565,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select pass through routes",disabled:d=!1,teamId:c})=>{let[u,m]=(0,l.useState)([]),[p,g]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,a.getPassThroughEndpointsCall)(n,c);if(e.endpoints){let t=e.endpoints.flatMap(e=>{let t=e.path,l=e.methods;return l&&l.length>0?l.map(e=>({label:`${e} ${t}`,value:t})):[{label:t,value:t}]});m(t)}}catch(e){console.error("Error fetching pass through routes:",e)}finally{g(!1)}}})()},[n,c]),(0,t.jsx)(s.Select,{mode:"tags",placeholder:o,onChange:e,value:r,loading:p,className:i,allowClear:!0,options:u,optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:d})}])},939510,e=>{"use strict";var t=e.i(843476),l=e.i(808613),s=e.i(199133),a=e.i(592968),r=e.i(827252);let{Option:i}=s.Select;e.s(["default",0,({type:e,name:n,showDetailedDescriptions:o=!0,className:d="",initialValue:c=null,form:u,onChange:m})=>{let p=e.toUpperCase(),g=e.toLowerCase(),h=`Select 'guaranteed_throughput' to prevent overallocating ${p} limit when the key belongs to a Team with specific ${p} limits.`;return(0,t.jsx)(l.Form.Item,{label:(0,t.jsxs)("span",{children:[p," Rate Limit Type"," ",(0,t.jsx)(a.Tooltip,{title:h,children:(0,t.jsx)(r.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:n,initialValue:c,className:d,children:(0,t.jsx)(s.Select,{defaultValue:o?"default":void 0,placeholder:"Select rate limit type",style:{width:"100%"},optionLabelProp:o?"label":void 0,onChange:e=>{u&&u.setFieldValue(n,e),m&&m(e)},children:o?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",label:"Default",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Default"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Best effort throughput - no error if we're overallocating ",g," (Team/Key Limits checked at runtime)."]})]})}),(0,t.jsx)(i,{value:"guaranteed_throughput",label:"Guaranteed throughput",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Guaranteed throughput"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Guaranteed throughput - raise an error if we're overallocating ",g," (also checks model-specific limits)"]})]})}),(0,t.jsx)(i,{value:"dynamic",label:"Dynamic",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Dynamic"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["If the key has a set ",p," (e.g. 2 ",p,") and there are no 429 errors, it can dynamically exceed the limit when the model being called is not erroring."]})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",children:"Best effort throughput"}),(0,t.jsx)(i,{value:"guaranteed_throughput",children:"Guaranteed throughput"}),(0,t.jsx)(i,{value:"dynamic",children:"Dynamic"})]})})})}])},363256,e=>{"use strict";var t=e.i(843476),l=e.i(199133);let{Text:s}=e.i(898586).Typography;e.s(["default",0,({organizations:e,value:a,onChange:r,disabled:i,loading:n,style:o})=>(0,t.jsx)(l.Select,{showSearch:!0,placeholder:"All Organizations",value:a,onChange:r,disabled:i,loading:n,allowClear:!0,style:{minWidth:280,...o},filterOption:(t,l)=>{if(!l)return!1;let s=e?.find(e=>e.organization_id===l.key);if(!s)return!1;let a=t.toLowerCase().trim(),r=(s.organization_alias||"").toLowerCase(),i=(s.organization_id||"").toLowerCase();return r.includes(a)||i.includes(a)},children:e?.map(e=>(0,t.jsxs)(l.Select.Option,{value:e.organization_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.organization_alias})," ",(0,t.jsxs)(s,{type:"secondary",children:["(",e.organization_id,")"]})]},e.organization_id))})])},128233,319312,833400,e=>{"use strict";var t=e.i(843476),l=e.i(464571),s=e.i(199133),a=e.i(592968),r=e.i(425063),i=e.i(107233),n=e.i(37727),o=e.i(271645);e.s(["BudgetFallbacksEditor",0,function({value:e,onChange:d,availableModels:c}){let[u,m]=(0,o.useState)(()=>{let t;return 0===(t=Object.keys(e)).length?[]:t.map((t,l)=>({id:String(l+1),primaryModel:t,fallbackModels:e[t]}))}),p=e=>{m(e),d(Object.fromEntries(e.filter(e=>null!==e.primaryModel&&e.fallbackModels.length>0).map(e=>[e.primaryModel,e.fallbackModels])))},g=()=>{p([...u,{id:Date.now().toString(),primaryModel:null,fallbackModels:[]}])},h=(e,t)=>{p(u.map(l=>l.id===e?{...l,...t}:l))},x=new Set(u.map(e=>e.primaryModel).filter(Boolean));return 0===u.length?(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-xs text-gray-500 mb-2",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),(0,t.jsx)(l.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),u.map(e=>{let l=c.filter(t=>t===e.primaryModel||!x.has(t)),i=c.filter(t=>t!==e.primaryModel);return(0,t.jsxs)("div",{className:"relative rounded-lg border border-gray-200 bg-gray-50 p-4",children:[(0,t.jsx)("button",{type:"button",onClick:()=>{var t;return t=e.id,void p(u.filter(e=>e.id!==t))},className:"absolute top-2 right-2 text-gray-400 hover:text-red-500 transition-colors p-1",children:(0,t.jsx)(n.X,{className:"w-4 h-4"})}),(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Primary Model"}),(0,t.jsx)(s.Select,{className:"w-full",placeholder:"Select model",value:e.primaryModel,onChange:t=>{let l=e.fallbackModels.filter(e=>e!==t);h(e.id,{primaryModel:t,fallbackModels:l})},showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:l.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-1 mb-2",children:(0,t.jsxs)("div",{className:"bg-amber-50 text-amber-600 px-3 py-0.5 rounded-full text-[10px] font-bold border border-amber-100 flex items-center gap-1",children:[(0,t.jsx)(r.ArrowDown,{className:"w-3 h-3"}),"IF BUDGET EXCEEDED, TRY"]})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Fallback Models"}),(0,t.jsx)(s.Select,{mode:"multiple",className:"w-full",placeholder:e.primaryModel?"Select fallback models":"Select a primary model first",value:e.fallbackModels,onChange:t=>h(e.id,{fallbackModels:t}),disabled:!e.primaryModel,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:i.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(a.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})}),e.fallbackModels.length>1&&(0,t.jsx)("div",{className:"text-[10px] text-gray-400 mt-1 ml-1",children:"Tried in order; first model still within its own budget is used"})]})]},e.id)}),(0,t.jsx)(l.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]})}],128233);var d=e.i(28651);let c=[{value:"1h",label:"Hourly",resetHint:"Resets every hour"},{value:"24h",label:"Daily",resetHint:"Resets daily at midnight UTC"},{value:"7d",label:"Weekly",resetHint:"Resets every Sunday at midnight UTC"},{value:"30d",label:"Monthly",resetHint:"Resets on the 1st of every month at midnight UTC"}];e.s(["BudgetWindowsEditor",0,function({value:e,onChange:a}){let r=(t,l,s)=>{a(e.map((e,a)=>a===t?{...e,[l]:s}:e))};return(0,t.jsxs)("div",{children:[e.map((i,n)=>{let o=c.find(e=>e.value===i.budget_duration)?.resetHint;return(0,t.jsxs)("div",{style:{marginBottom:12},children:[(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[(0,t.jsx)(s.Select,{value:i.budget_duration,onChange:e=>r(n,"budget_duration",e),style:{width:130},options:c.map(e=>({value:e.value,label:e.label}))}),(0,t.jsx)(d.InputNumber,{step:.01,min:0,precision:2,value:i.max_budget??void 0,onChange:e=>r(n,"max_budget",e??null),placeholder:"Max spend ($)",style:{width:160},prefix:"$"}),(0,t.jsx)(l.Button,{type:"text",danger:!0,size:"small",onClick:()=>{a(e.filter((e,t)=>t!==n))},style:{padding:"0 4px"},children:"✕"})]}),o&&(0,t.jsxs)("div",{style:{fontSize:11,color:"#888",marginTop:3,marginLeft:2},children:["↻ ",o]})]},n)}),(0,t.jsx)(l.Button,{size:"small",onClick:t=>{t.preventDefault(),a([...e,{budget_duration:"24h",max_budget:null}])},children:"+ Add Budget Window"})]})}],319312);var u=e.i(311451);let m=0,p=()=>`tag-row-${m++}`;e.s(["TagRateLimitEditor",0,function({value:e,onChange:s}){let a=(t,l,a)=>{s(e.map((e,s)=>s===t?{...e,[l]:a}:e))};return(0,t.jsxs)("div",{children:[e.map((r,i)=>(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center",marginBottom:12},children:[(0,t.jsx)(u.Input,{value:r.tag,onChange:e=>a(i,"tag",e.target.value),placeholder:"Tag (e.g. cell-1)",style:{width:180}}),(0,t.jsx)(d.InputNumber,{min:0,value:r.rpm_limit??void 0,onChange:e=>a(i,"rpm_limit",e??null),placeholder:"RPM",style:{width:120}}),(0,t.jsx)(l.Button,{type:"text",danger:!0,size:"small",onClick:()=>{s(e.filter((e,t)=>t!==i))},style:{padding:"0 4px"},children:"✕"})]},r.id)),(0,t.jsx)(l.Button,{size:"small",onClick:t=>{t.preventDefault(),s([...e,{id:p(),tag:"",rpm_limit:null}])},children:"+ Add Tag Limit"})]})},"tagLimitsToRows",0,e=>{let t=(e=>{if(!e||"object"!=typeof e)return{};let t={};return Object.entries(e).forEach(([e,l])=>{"number"==typeof l&&(t[e]=l)}),t})(e);return Object.keys(t).map(e=>({id:p(),tag:e,rpm_limit:t[e]}))},"tagRowsToLimits",0,e=>{let t={};return e.forEach(({tag:e,rpm_limit:l})=>{let s=e.trim();s&&"number"==typeof l&&(t[s]=l)}),{tag_rpm_limit:t}}],833400)},109034,e=>{"use strict";var t=e.i(266027),l=e.i(243652),s=e.i(602869),a=e.i(135214);let r=(0,l.createQueryKeys)("tags");e.s(["useTags",0,()=>{let{accessToken:e,userId:l,userRole:i}=(0,a.default)();return(0,t.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,s.tagListCall)(e),enabled:!!(e&&l&&i)})}])},533882,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(250980),a=e.i(797672),r=e.i(68155),i=e.i(304967),n=e.i(629569),o=e.i(599724),d=e.i(269200),c=e.i(427612),u=e.i(64848),m=e.i(942232),p=e.i(496020),g=e.i(977572),h=e.i(992619),x=e.i(727749);e.s(["default",0,({accessToken:e,initialModelAliases:y={},onAliasUpdate:f,showExampleConfig:b=!0})=>{let[_,j]=(0,l.useState)([]),[v,w]=(0,l.useState)({aliasName:"",targetModel:""}),[k,N]=(0,l.useState)(null);(0,l.useEffect)(()=>{j(Object.entries(y).map(([e,t],l)=>({id:`${l}-${e}`,aliasName:e,targetModel:t})))},[y]);let S=()=>{if(!k)return;if(!k.aliasName||!k.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.id!==k.id&&e.aliasName===k.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=_.map(e=>e.id===k.id?k:e);j(e),N(null);let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias updated successfully")},C=()=>{N(null)},T=_.reduce((e,t)=>(e[t.aliasName]=t.targetModel,e),{});return(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Add New Alias"}),(0,t.jsxs)("div",{className:"grid grid-cols-3 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Alias Name"}),(0,t.jsx)("input",{type:"text",value:v.aliasName,onChange:e=>w({...v,aliasName:e.target.value}),placeholder:"e.g., gpt-4o",className:"w-full px-3 py-2 border border-gray-300 rounded-md text-sm"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Target Model"}),(0,t.jsx)(h.default,{accessToken:e,value:v.targetModel,placeholder:"Select target model",onChange:e=>w({...v,targetModel:e}),showLabel:!1})]}),(0,t.jsx)("div",{className:"flex items-end",children:(0,t.jsxs)("button",{onClick:()=>{if(!v.aliasName||!v.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.aliasName===v.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=[..._,{id:`${Date.now()}-${v.aliasName}`,aliasName:v.aliasName,targetModel:v.targetModel}];j(e),w({aliasName:"",targetModel:""});let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias added successfully")},disabled:!v.aliasName||!v.targetModel,className:`flex items-center px-4 py-2 rounded-md text-sm ${!v.aliasName||!v.targetModel?"bg-gray-300 text-gray-500 cursor-not-allowed":"bg-green-600 text-white hover:bg-green-700"}`,children:[(0,t.jsx)(s.PlusCircleIcon,{className:"w-4 h-4 mr-1"}),"Add Alias"]})})]})]}),(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Manage Existing Aliases"}),(0,t.jsx)("div",{className:"rounded-lg custom-border relative mb-6",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(d.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(c.TableHead,{children:(0,t.jsxs)(p.TableRow,{children:[(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Alias Name"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Target Model"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Actions"})]})}),(0,t.jsxs)(m.TableBody,{children:[_.map(l=>(0,t.jsx)(p.TableRow,{className:"h-8",children:k&&k.id===l.id?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)("input",{type:"text",value:k.aliasName,onChange:e=>N({...k,aliasName:e.target.value}),className:"w-full px-2 py-1 border border-gray-300 rounded-md text-sm"})}),(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)(h.default,{accessToken:e,value:k.targetModel,onChange:e=>N({...k,targetModel:e}),showLabel:!1,style:{height:"32px"}})}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:S,className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:"Save"}),(0,t.jsx)("button",{onClick:C,className:"text-xs bg-gray-50 text-gray-600 px-2 py-1 rounded-sm hover:bg-gray-100",children:"Cancel"})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-900",children:l.aliasName}),(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-500",children:l.targetModel}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:()=>{N({...l})},className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:(0,t.jsx)(a.PencilIcon,{className:"w-3 h-3"})}),(0,t.jsx)("button",{onClick:()=>{var e;let t,s;return e=l.id,j(t=_.filter(t=>t.id!==e)),s={},void(t.forEach(e=>{s[e.aliasName]=e.targetModel}),f&&f(s),x.default.success("Alias deleted successfully"))},className:"text-xs bg-red-50 text-red-600 px-2 py-1 rounded-sm hover:bg-red-100",children:(0,t.jsx)(r.TrashIcon,{className:"w-3 h-3"})})]})})]})},l.id)),0===_.length&&(0,t.jsx)(p.TableRow,{children:(0,t.jsx)(g.TableCell,{colSpan:3,className:"py-0.5 text-sm text-gray-500 text-center",children:"No aliases added yet. Add a new alias above."})})]})]})})}),b&&(0,t.jsxs)(i.Card,{children:[(0,t.jsx)(n.Title,{className:"mb-4",children:"Configuration Example"}),(0,t.jsx)(o.Text,{className:"text-gray-600 mb-4",children:"Here's how your current aliases would look in the config:"}),(0,t.jsx)("div",{className:"bg-gray-100 rounded-lg p-4 font-mono text-sm",children:(0,t.jsxs)("div",{className:"text-gray-700",children:["model_aliases:",0===Object.keys(T).length?(0,t.jsxs)("span",{className:"text-gray-500",children:[(0,t.jsx)("br",{}),"  # No aliases configured yet"]}):Object.entries(T).map(([e,l])=>(0,t.jsxs)("span",{children:[(0,t.jsx)("br",{}),'  "',e,'": "',l,'"']},e))]})})]})]})}])},266484,e=>{"use strict";var t=e.i(843476),l=e.i(199133),s=e.i(592968),a=e.i(312361),r=e.i(827252),i=e.i(994388),n=e.i(304967),o=e.i(779241),d=e.i(988297),c=e.i(68155),u=e.i(810757),m=e.i(477386),p=e.i(557662),g=e.i(555987),h=e.i(435451);let{Option:x}=l.Select;e.s(["default",0,({value:e=[],onChange:y,disabledCallbacks:f=[],onDisabledCallbacksChange:b})=>{let _=Object.entries(p.callbackInfo).filter(([e,t])=>t.supports_key_team_logging).map(([e,t])=>e),j=Object.keys(p.callbackInfo),v=e=>{y?.(e)},w=(t,l,s)=>{let a=[...e];if("callback_name"===l){let e=p.callback_map[s]||s;a[t]={...a[t],[l]:e,callback_vars:{}}}else a[t]={...a[t],[l]:s};v(a)},k=(t,l,s)=>{let a=[...e];a[t]={...a[t],callback_vars:{...a[t].callback_vars,[l]:s}},v(a)};return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(m.BanIcon,{className:"w-5 h-5 text-red-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Disabled Callbacks"}),(0,t.jsx)(s.Tooltip,{title:"Select callbacks to disable for this key. Disabled callbacks will not receive any logging data.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Disabled Callbacks"}),(0,t.jsx)(l.Select,{mode:"multiple",placeholder:"Select callbacks to disable",value:f,onChange:e=>{let t=(0,p.mapDisplayToInternalNames)(e);b?.(t)},style:{width:"100%"},optionLabelProp:"label",children:j.map(e=>{let l=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(s.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[l&&(0,t.jsx)("img",{src:l,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let l=t.target,s=l.parentElement;if(s){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),s.replaceChild(t,l)}}}),(0,t.jsx)("span",{children:e})]})})},e)})}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Select callbacks that should be disabled for this key. These callbacks will not receive any logging data."})]})]}),(0,t.jsx)(a.Divider,{}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(u.CogIcon,{className:"w-5 h-5 text-blue-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Logging Integrations"}),(0,t.jsx)(s.Tooltip,{title:"Configure callback logging integrations for this team.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsx)(i.Button,{variant:"secondary",onClick:()=>{v([...e,{callback_name:"",callback_type:"success",callback_vars:{}}])},icon:d.PlusIcon,size:"sm",className:"hover:border-blue-400 hover:text-blue-500",type:"button",children:"Add Integration"})]}),(0,t.jsx)("div",{className:"space-y-4",children:e.map((a,d)=>{let u=a.callback_name?Object.entries(p.callback_map).find(([e,t])=>t===a.callback_name)?.[0]:void 0,m=u?(0,g.resolveLogoSrc)(p.callbackInfo[u]?.logo):null;return(0,t.jsxs)(n.Card,{className:"border border-gray-200 shadow-xs hover:shadow-md transition-shadow duration-200",decoration:"top",decorationColor:"blue",children:[(0,t.jsxs)("div",{className:"flex justify-between items-start mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[m&&(0,t.jsx)("img",{src:m,alt:u,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("span",{className:"text-sm font-medium",children:[u||"New Integration"," Configuration"]})]}),(0,t.jsx)(i.Button,{variant:"light",onClick:()=>{v(e.filter((e,t)=>t!==d))},icon:c.TrashIcon,size:"xs",color:"red",className:"hover:bg-red-50",type:"button",children:"Remove"})]}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Integration Type"}),(0,t.jsx)(l.Select,{value:u,placeholder:"Select integration",onChange:e=>w(d,"callback_name",e),className:"w-full",optionLabelProp:"label",children:_.map(e=>{let l=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(s.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[l&&(0,t.jsx)("img",{src:l,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let l=t.target,s=l.parentElement;if(s){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),s.replaceChild(t,l)}}}),(0,t.jsx)("span",{children:e})]})})},e)})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Event Type"}),(0,t.jsxs)(l.Select,{value:a.callback_type,onChange:e=>w(d,"callback_type",e),className:"w-full",children:[(0,t.jsx)(x,{value:"success",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-green-500 rounded-full"}),(0,t.jsx)("span",{children:"Success Only"})]})}),(0,t.jsx)(x,{value:"failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-red-500 rounded-full"}),(0,t.jsx)("span",{children:"Failure Only"})]})}),(0,t.jsx)(x,{value:"success_and_failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-blue-500 rounded-full"}),(0,t.jsx)("span",{children:"Success & Failure"})]})})]})]})]}),((e,l)=>{if(!e.callback_name)return null;let a=Object.entries(p.callback_map).find(([t,l])=>l===e.callback_name)?.[0];if(!a)return null;let i=p.callbackInfo[a]?.dynamic_params||{};return 0===Object.keys(i).length?null:(0,t.jsxs)("div",{className:"mt-6 pt-4 border-t border-gray-100",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2 mb-4",children:[(0,t.jsx)("div",{className:"w-3 h-3 bg-blue-100 rounded-full flex items-center justify-center",children:(0,t.jsx)("div",{className:"w-1.5 h-1.5 bg-blue-500 rounded-full"})}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Integration Parameters"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-4",children:Object.entries(i).map(([a,i])=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 capitalize flex items-center space-x-1",children:[(0,t.jsx)("span",{children:a.replace(/_/g," ")}),(0,t.jsx)(s.Tooltip,{title:`Environment variable reference recommended: os.environ/${a.toUpperCase()}`,children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),"password"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Sensitive"}),"number"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Number"})]}),"number"===i&&(0,t.jsx)("span",{className:"text-xs text-gray-500",children:"Value must be between 0 and 1"}),"number"===i?(0,t.jsx)(h.default,{step:.01,width:400,placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>k(l,a,e.target.value)}):(0,t.jsx)(o.TextInput,{type:"password"===i?"password":"text",placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>k(l,a,e.target.value)})]},a))})]})})(a,d)]})]},d)})}),0===e.length&&(0,t.jsxs)("div",{className:"text-center py-12 text-gray-500 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50",children:[(0,t.jsx)(u.CogIcon,{className:"w-12 h-12 text-gray-300 mb-3 mx-auto"}),(0,t.jsx)("div",{className:"text-base font-medium mb-1",children:"No logging integrations configured"}),(0,t.jsx)("div",{className:"text-sm text-gray-400",children:'Click "Add Integration" to configure logging for this team'})]})]})}])},651904,e=>{"use strict";var t=e.i(843476),l=e.i(599724),s=e.i(266484);e.s(["default",0,function({value:e,onChange:a,premiumUser:r=!1,disabledCallbacks:i=[],onDisabledCallbacksChange:n}){return r?(0,t.jsx)(s.default,{value:e,onChange:a,disabledCallbacks:i,onDisabledCallbacksChange:n}):(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex flex-wrap gap-2 mb-3",children:[(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ langfuse-logging"}),(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ datadog-logging"})]}),(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg",children:(0,t.jsxs)(l.Text,{className:"text-sm text-yellow-800",children:["Setting Key/Team logging settings is a LiteLLM Enterprise feature. Global Logging Settings are available for all free users. Get a trial key"," ",(0,t.jsx)("a",{href:"https://www.litellm.ai/#pricing",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"here"}),"."]})})]})}])},460285,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(404206),a=e.i(723731),r=e.i(653824),i=e.i(881073),n=e.i(197647),o=e.i(602869),d=e.i(158392),c=e.i(419470),u=e.i(695411);let m=(0,l.forwardRef)(({accessToken:e,value:m,onChange:p,modelData:g},h)=>{let[x,y]=(0,l.useState)({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),[f,b]=(0,l.useState)([]),[_,j]=(0,l.useState)([]),[v,w]=(0,l.useState)([]),[k,N]=(0,l.useState)([]),[S,C]=(0,l.useState)({}),[T,I]=(0,l.useState)({}),A=(0,l.useRef)(!1),L=(0,l.useRef)(null);(0,l.useEffect)(()=>{let e=m?.router_settings?JSON.stringify({routing_strategy:m.router_settings.routing_strategy,fallbacks:m.router_settings.fallbacks,enable_tag_filtering:m.router_settings.enable_tag_filtering}):null;if(A.current&&e===L.current){A.current=!1;return}if(A.current&&e!==L.current&&(A.current=!1),e!==L.current)if(L.current=e,m?.router_settings){let e=m.router_settings,{fallbacks:t,...l}=e;y({routerSettings:l,selectedStrategy:e.routing_strategy||null,enableTagFiltering:e.enable_tag_filtering??!1});let s=e.fallbacks||[];b(s),j(s&&0!==s.length?s.map((e,t)=>{let[l,s]=Object.entries(e)[0];return{id:(t+1).toString(),primaryModel:l||null,fallbackModels:s||[]}}):[{id:"1",primaryModel:null,fallbackModels:[]}])}else y({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),b([]),j([{id:"1",primaryModel:null,fallbackModels:[]}])},[m]),(0,l.useEffect)(()=>{e&&(0,o.getRouterSettingsCall)(e).then(e=>{if(e.fields){let t={};e.fields.forEach(e=>{t[e.field_name]={ui_field_name:e.ui_field_name,field_description:e.field_description,options:e.options,link:e.link}}),C(t);let l=e.fields.find(e=>"routing_strategy"===e.field_name);l?.options&&N(l.options),e.routing_strategy_descriptions&&I(e.routing_strategy_descriptions)}})},[e]),(0,l.useEffect)(()=>{e&&(async()=>{try{let t=await (0,u.fetchAvailableModels)(e);w(t)}catch(e){console.error("Error fetching model info for fallbacks:",e)}})()},[e]);let F=()=>{let e=new Set(["allowed_fails","cooldown_time","num_retries","timeout","retry_after"]),t=new Set(["model_group_alias","retry_policy"]),l=Object.fromEntries(Object.entries({...x.routerSettings,enable_tag_filtering:x.enableTagFiltering,routing_strategy:x.selectedStrategy,fallbacks:f.length>0?f:null}).map(([l,s])=>{if("routing_strategy_args"!==l&&"routing_strategy"!==l&&"enable_tag_filtering"!==l&&"fallbacks"!==l){let a=document.querySelector(`input[name="${l}"]`);if(a){if(void 0!==a.value&&""!==a.value){let r=((l,s,a)=>{if(null==s)return a;let r=String(s).trim();if(""===r||"null"===r.toLowerCase())return null;if(e.has(l)){let e=Number(r);return Number.isNaN(e)?a:e}if(t.has(l)){if(""===r)return null;try{return JSON.parse(r)}catch{return a}}return"true"===r.toLowerCase()||"false"!==r.toLowerCase()&&r})(l,a.value,s);return[l,r]}return[l,null]}}else if("routing_strategy"===l)return[l,x.selectedStrategy];else if("enable_tag_filtering"===l)return[l,x.enableTagFiltering];else if("fallbacks"===l)return[l,f.length>0?f:null];else if("routing_strategy_args"===l&&"latency-based-routing"===x.selectedStrategy){let e=document.querySelector('input[name="lowest_latency_buffer"]'),t=document.querySelector('input[name="ttl"]'),l={};return e?.value&&(l.lowest_latency_buffer=Number(e.value)),t?.value&&(l.ttl=Number(t.value)),["routing_strategy_args",Object.keys(l).length>0?l:null]}return[l,s]}).filter(e=>null!=e)),s=(e,t=!1)=>null==e||"object"==typeof e&&!Array.isArray(e)&&0===Object.keys(e).length||t&&("number"!=typeof e||Number.isNaN(e))?null:e;return{routing_strategy:s(l.routing_strategy),allowed_fails:s(l.allowed_fails,!0),cooldown_time:s(l.cooldown_time,!0),num_retries:s(l.num_retries,!0),timeout:s(l.timeout,!0),retry_after:s(l.retry_after,!0),fallbacks:f.length>0?f:null,context_window_fallbacks:s(l.context_window_fallbacks),retry_policy:s(l.retry_policy),model_group_alias:s(l.model_group_alias),enable_tag_filtering:x.enableTagFiltering,routing_strategy_args:s(l.routing_strategy_args)}};(0,l.useEffect)(()=>{if(!p)return;let e=setTimeout(()=>{A.current=!0,p({router_settings:F()})},100);return()=>clearTimeout(e)},[x,f]);let M=Array.from(new Set(v.map(e=>e.model_group))).sort();return((0,l.useImperativeHandle)(h,()=>({getValue:()=>({router_settings:F()})})),e)?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)(r.TabGroup,{className:"w-full",children:[(0,t.jsxs)(i.TabList,{variant:"line",defaultValue:"1",className:"px-8 pt-4",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Loadbalancing"}),(0,t.jsx)(n.Tab,{value:"2",children:"Fallbacks"})]}),(0,t.jsxs)(a.TabPanels,{className:"px-8 py-6",children:[(0,t.jsx)(s.TabPanel,{children:(0,t.jsx)(d.default,{value:x,onChange:y,routerFieldsMetadata:S,availableRoutingStrategies:k,routingStrategyDescriptions:T})}),(0,t.jsx)(s.TabPanel,{children:(0,t.jsx)(c.FallbackSelectionForm,{groups:_,onGroupsChange:e=>{j(e),b(e.filter(e=>e.primaryModel&&e.fallbackModels.length>0).map(e=>({[e.primaryModel]:e.fallbackModels})))},availableModels:M,maxGroups:5})})]})]})}):null});m.displayName="RouterSettingsAccordion",e.s(["default",0,m])},575260,e=>{"use strict";var t=e.i(843476),l=e.i(199133),s=e.i(482725),a=e.i(56456);e.s(["default",0,({projects:e,value:r,onChange:i,disabled:n,loading:o,teamId:d})=>{let c=d?e?.filter(e=>e.team_id===d):e;return(0,t.jsx)(l.Select,{showSearch:!0,placeholder:"Search or select a project",value:r,onChange:i,disabled:n,loading:o,allowClear:!0,notFoundContent:o?(0,t.jsx)(s.Spin,{indicator:(0,t.jsx)(a.LoadingOutlined,{spin:!0}),size:"small"}):void 0,filterOption:(e,t)=>{if(!t)return!1;let l=c?.find(e=>e.project_id===t.key);if(!l)return!1;let s=e.toLowerCase().trim(),a=(l.project_alias||"").toLowerCase(),r=(l.project_id||"").toLowerCase();return a.includes(s)||r.includes(s)},optionFilterProp:"children",children:!o&&c?.map(e=>(0,t.jsxs)(l.Select.Option,{value:e.project_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.project_alias||e.project_id})," ",(0,t.jsxs)("span",{className:"text-gray-500",children:["(",e.project_id,")"]})]},e.project_id))})}])},702597,e=>{"use strict";var t=e.i(843476),l=e.i(207082),s=e.i(109799),a=e.i(510674),r=e.i(109034),i=e.i(292639),n=e.i(135214),o=e.i(500330),d=e.i(827252),c=e.i(912598),u=e.i(677667),m=e.i(130643),p=e.i(898667),g=e.i(994388),h=e.i(309426),x=e.i(350967),y=e.i(599724),f=e.i(779241),b=e.i(629569),_=e.i(464571),j=e.i(808613),v=e.i(311451),w=e.i(212931),k=e.i(91739),N=e.i(199133),S=e.i(790848),C=e.i(262218),T=e.i(592968),I=e.i(898586),A=e.i(374009),L=e.i(271645),F=e.i(708347),M=e.i(552130),O=e.i(557662),E=e.i(9314),P=e.i(860585),R=e.i(82946),B=e.i(392110),$=e.i(533882),D=e.i(844565),V=e.i(651904),z=e.i(939510),U=e.i(460285),G=e.i(663435),K=e.i(363256),q=e.i(575260),W=e.i(371455),H=e.i(128233),Q=e.i(319312),J=e.i(833400),Y=e.i(355619),X=e.i(75921),Z=e.i(234713),ee=e.i(390605),et=e.i(727749),el=e.i(602869),es=e.i(364769),ea=e.i(435451),er=e.i(916940);let{Option:ei}=N.Select,en=async(e,t,l,s)=>{try{if(null===e||null===t)return[];if(null!==l)return(await (0,el.modelAvailableCall)(l,e,t,!0,s,!0)).data.map(e=>e.id);return[]}catch(e){return console.error("Error fetching user models:",e),[]}},eo=async(e,t,l,s)=>{try{if(null===e||null===t)return;if(null!==l){let a=(await (0,el.modelAvailableCall)(l,e,t)).data.map(e=>e.id);s(a)}}catch(e){console.error("Error fetching user models:",e)}};e.s(["default",0,({team:e,teams:ed,data:ec,addKey:eu,autoOpenCreate:em,prefillData:ep})=>{let{accessToken:eg,userId:eh,userRole:ex,premiumUser:ey}=(0,n.default)(),ef=ey||null!=ex&&F.rolesWithWriteAccess.includes(ex),{data:eb,isLoading:e_}=(0,s.useOrganizations)(),{data:ej,isLoading:ev}=(0,a.useProjects)(),{data:ew}=(0,i.useUISettings)(),{data:ek}=(0,r.useTags)(),eN=!!ew?.values?.enable_projects_ui,eS=!!ew?.values?.disable_custom_api_keys,eC=ek?Object.values(ek).map(e=>({value:e.name,label:e.name})):[],eT=(0,c.useQueryClient)(),[eI]=j.Form.useForm(),[eA,eL]=(0,L.useState)(!1),[eF,eM]=(0,L.useState)(null),[eO,eE]=(0,L.useState)(null),[eP,eR]=(0,L.useState)([]),[eB,e$]=(0,L.useState)([]),[eD,eV]=(0,L.useState)("you"),[ez,eU]=(0,L.useState)(!1),[eG,eK]=(0,L.useState)(null),[eq,eW]=(0,L.useState)([]),[eH,eQ]=(0,L.useState)([]),[eJ,eY]=(0,L.useState)([]),[eX,eZ]=(0,L.useState)([]),[e0,e1]=(0,L.useState)(e),[e4,e2]=(0,L.useState)(null),[e3,e6]=(0,L.useState)(null),[e5,e7]=(0,L.useState)(!1),[e8,e9]=(0,L.useState)(null),[te,tt]=(0,L.useState)({}),[tl,ts]=(0,L.useState)([]),[ta,tr]=(0,L.useState)(!1),[ti,tn]=(0,L.useState)([]),[to,td]=(0,L.useState)([]),[tc,tu]=(0,L.useState)("llm_api"),[tm,tp]=(0,L.useState)({}),[tg,th]=(0,L.useState)(!1),[tx,ty]=(0,L.useState)("30d"),[tf,tb]=(0,L.useState)(null),[t_,tj]=(0,L.useState)([]),[tv,tw]=(0,L.useState)([]),[tk,tN]=(0,L.useState)({}),[tS,tC]=(0,L.useState)(0),[tT,tI]=(0,L.useState)(0),[tA,tL]=(0,L.useState)([]),[tF,tM]=(0,L.useState)(null),tO=j.Form.useWatch("models",eI)??[],tE=()=>{eL(!1),eI.resetFields(),eZ([]),td([]),tu("llm_api"),tp({}),th(!1),ty("30d"),tb(null),tI(e=>e+1),tM(null),e2(null),e6(null),tj([]),tw([]),tN({}),tC(e=>e+1)},tP=()=>{eL(!1),eM(null),e1(null),eI.resetFields(),eZ([]),td([]),tu("llm_api"),tp({}),th(!1),ty("30d"),tb(null),tI(e=>e+1),tM(null),e2(null),e6(null),tj([]),tw([]),tN({}),tC(e=>e+1)};(0,L.useEffect)(()=>{eh&&ex&&eg&&eo(eh,ex,eg,eR)},[eg,eh,ex]),(0,L.useEffect)(()=>{eg&&(0,el.getAgentsList)(eg).then(e=>tL(e?.agents||[])).catch(()=>tL([]))},[eg]),(0,L.useEffect)(()=>{let e=async()=>{try{let e=(await (0,el.getPoliciesList)(eg)).policies.map(e=>e.policy_name);eQ(e)}catch(e){console.error("Failed to fetch policies:",e)}},t=async()=>{try{let e=await (0,el.getPromptsList)(eg);eY(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}};(async()=>{try{let e=(await (0,el.getGuardrailsList)(eg)).guardrails.map(e=>e.guardrail_name);eW(e)}catch(e){console.error("Failed to fetch guardrails:",e)}})(),e(),t()},[eg]),(0,L.useEffect)(()=>{(async()=>{try{if(eg){let e=sessionStorage.getItem("possibleUserRoles");if(e)tt(JSON.parse(e));else{let e=await (0,el.getPossibleUserRoles)(eg);sessionStorage.setItem("possibleUserRoles",JSON.stringify(e)),tt(e)}}}catch(e){console.error("Error fetching possible user roles:",e)}})()},[eg]),(0,L.useEffect)(()=>{if(em&&!ez&&ed&&ex&&F.rolesWithWriteAccess.includes(ex)&&(eL(!0),eU(!0),ep)){if(ep.owned_by&&("another_user"===ep.owned_by&&"Admin"!==ex?eV("you"):eV(ep.owned_by)),ep.team_id){let e=ed?.find(e=>e.team_id===ep.team_id)||null;e&&(e1(e),eI.setFieldsValue({team_id:ep.team_id}))}ep.key_alias&&eI.setFieldsValue({key_alias:ep.key_alias}),ep.models&&ep.models.length>0&&eK(ep.models),ep.key_type&&(tu(ep.key_type),eI.setFieldsValue({key_type:ep.key_type}))}},[em,ep,ed,ez,eI,ex]);let tR=eB.includes("no-default-models")&&!e0,tB=async e=>{try{let t,s=e?.key_alias??"",a=e?.team_id??null;if((ec?.filter(e=>e.team_id===a).map(e=>e.key_alias)??[]).includes(s))throw Error(`Key alias ${s} already exists for team with ID ${a}, please provide another key alias`);if(et.default.info("Making API Call"),eL(!0),"you"===eD)e.user_id=eh;else if("agent"===eD){if(!tF)return void et.default.fromBackend("Please select an agent");e.agent_id=tF}let r={};try{r=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}if("service_account"===eD&&(r.service_account_id=e.key_alias),eX.length>0&&(r={...r,logging:eX.filter(e=>e.callback_name)}),to.length>0){let e=(0,O.mapDisplayToInternalNames)(to);r={...r,litellm_disabled_callbacks:e}}if(tg&&(e.auto_rotate=!0,e.rotation_interval=tx),e.duration&&""!==e.duration.trim()||(e.duration=null),e.metadata=JSON.stringify(r),e.disable_global_guardrails||delete e.disable_global_guardrails,e.allowed_vector_store_ids&&e.allowed_vector_store_ids.length>0&&(e.object_permission={vector_stores:e.allowed_vector_store_ids},delete e.allowed_vector_store_ids),e.allowed_mcp_servers_and_groups&&(e.allowed_mcp_servers_and_groups.servers?.length>0||e.allowed_mcp_servers_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{servers:t,accessGroups:l}=e.allowed_mcp_servers_and_groups;t&&t.length>0&&(e.object_permission.mcp_servers=t),l&&l.length>0&&(e.object_permission.mcp_access_groups=l),delete e.allowed_mcp_servers_and_groups}let i=e.mcp_tool_permissions||{};if(Object.keys(i).length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_tool_permissions=i),delete e.mcp_tool_permissions,e.allowed_mcp_access_groups&&e.allowed_mcp_access_groups.length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_access_groups=e.allowed_mcp_access_groups,delete e.allowed_mcp_access_groups),e.allowed_agents_and_groups&&(e.allowed_agents_and_groups.agents?.length>0||e.allowed_agents_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{agents:t,accessGroups:l}=e.allowed_agents_and_groups;t&&t.length>0&&(e.object_permission.agents=t),l&&l.length>0&&(e.object_permission.agent_access_groups=l),delete e.allowed_agents_and_groups}Object.keys(tm).length>0&&(e.aliases=JSON.stringify(tm)),tf?.router_settings&&Object.values(tf.router_settings).some(e=>null!=e&&""!==e)&&(e.router_settings=tf.router_settings);let n=t_.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);n.length>0&&(e.budget_limits=n);let{tag_rpm_limit:o}=(0,J.tagRowsToLimits)(tv);Object.keys(o).length>0&&(e.tag_rpm_limit=o),Object.keys(tk).length>0&&(e.budget_fallbacks=tk),t="service_account"===eD?await (0,el.keyCreateServiceAccountCall)(eg,e):await (0,el.keyCreateCall)(eg,eh,e),eu(t),eT.invalidateQueries({queryKey:l.keyKeys.lists()}),eM(t.key),eE(t.soft_budget),et.default.success("Virtual Key Created"),eI.resetFields(),tj([]),tw([]),tN({}),tC(e=>e+1),localStorage.removeItem("userData"+eh)}catch(t){let e=(e=>{let t;if(!(t=!e||"object"!=typeof e||e instanceof Error?String(e):JSON.stringify(e)).includes("/key/generate")&&!t.includes("KeyManagementRoutes.KEY_GENERATE"))return`Error creating the key: ${e}`;let l=t;try{if(!e||"object"!=typeof e||e instanceof Error){let e=t.match(/\{[\s\S]*\}/);if(e){let t=JSON.parse(e[0]),s=t?.error||t;s?.message&&(l=s.message)}}else{let t=e?.error||e;t?.message&&(l=t.message)}}catch(e){}return t.includes("team_member_permission_error")||l.includes("Team member does not have permissions")?"Team member does not have permission to generate key for this team. Ask your proxy admin to configure the team member permission settings.":`Error creating the key: ${e}`})(t);et.default.fromBackend(e)}};(0,L.useEffect)(()=>{if(e3){let e=ej?.find(e=>e.project_id===e3);e$(e?.models??[]),eI.setFieldValue("models",[]);return}eh&&ex&&eg&&en(eh,ex,eg,e0?.team_id??null).then(e=>{e$((0,Y.excludeProxyWideSentinel)(Array.from(new Set([...e0?.models??[],...e]))))}),eG||eI.setFieldValue("models",[]),eI.setFieldValue("allowed_mcp_servers_and_groups",{servers:[],accessGroups:[]})},[e0,e3,eg,eh,ex,eI]),(0,L.useEffect)(()=>{if(!eG||0===eG.length||!eB||0===eB.length)return;let e=eG.filter(e=>eB.includes(e));e.length>0&&eI.setFieldsValue({models:e}),eK(null)},[eG,eB,eI]),(0,L.useEffect)(()=>{if(!e3||!ed)return;let e=ej?.find(e=>e.project_id===e3);if(!e?.team_id||e0?.team_id===e.team_id)return;let t=ed.find(t=>t.team_id===e.team_id)||null;t&&(e1(t),eI.setFieldValue("team_id",t.team_id))},[ed,e3,ej]);let t$=async e=>{if(!e)return void ts([]);tr(!0);try{let t=new URLSearchParams;if(t.append("user_email",e),null==eg)return;let l=(await (0,el.userFilterUICall)(eg,t)).map(e=>({label:`${e.user_email} (${e.user_id})`,value:e.user_id,user:e}));ts(l)}catch(e){console.error("Error fetching users:",e),et.default.fromBackend("Failed to search for users")}finally{tr(!1)}},tD=(0,L.useCallback)((0,A.default)(e=>t$(e),300),[eg]);return(0,t.jsxs)("div",{children:[ex&&F.rolesWithWriteAccess.includes(ex)&&(0,t.jsx)(g.Button,{className:"mx-auto",onClick:()=>eL(!0),"data-testid":"create-key-button",children:"+ Create New Key"}),(0,t.jsx)(w.Modal,{open:eA,width:1e3,footer:null,onOk:tE,onCancel:tP,children:(0,t.jsxs)(j.Form,{form:eI,onFinish:tB,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Ownership"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Owned By"," ",(0,t.jsx)(T.Tooltip,{title:"Select who will own this Virtual Key",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),className:"mb-4",children:(0,t.jsxs)(k.Radio.Group,{onChange:e=>eV(e.target.value),value:eD,children:[(0,t.jsx)(k.Radio,{value:"you",children:"You"}),(0,t.jsx)(k.Radio,{value:"service_account",children:"Service Account"}),"Admin"===ex&&(0,t.jsx)(k.Radio,{value:"another_user",children:"Another User"}),(0,t.jsxs)(k.Radio,{value:"agent",children:["Agent ",(0,t.jsx)(C.Tag,{color:"purple",children:"New"})]})]})}),"another_user"===eD&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["User ID"," ",(0,t.jsx)(T.Tooltip,{title:"The user who will own this key and be responsible for its usage",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"user_id",className:"mt-4",rules:[{required:"another_user"===eD,message:"Please input the user ID of the user you are assigning the key to"}],children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{style:{display:"flex",marginBottom:"8px"},children:[(0,t.jsx)(N.Select,{showSearch:!0,placeholder:"Type email to search for users",filterOption:!1,onSearch:e=>{tD(e)},onSelect:(e,t)=>{let l;return l=t.user,void eI.setFieldsValue({user_id:l.user_id})},options:tl,loading:ta,allowClear:!0,style:{width:"100%"},notFoundContent:ta?"Searching...":"No users found"}),(0,t.jsx)(_.Button,{onClick:()=>e7(!0),style:{marginLeft:"8px"},children:"Create User"})]}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Search by email to find users"})]})}),"agent"===eD&&(0,t.jsxs)("div",{className:"mt-4 p-4 bg-purple-50 border border-purple-200 rounded-md",children:[(0,t.jsx)("div",{className:"mb-3",children:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:["Select Agent ",(0,t.jsx)("span",{className:"text-red-500",children:"*"})]})}),(0,t.jsx)(N.Select,{showSearch:!0,placeholder:"Select an agent",style:{width:"100%"},value:tF,onChange:e=>tM(e),filterOption:(e,t)=>t?.label?.toLowerCase().includes(e.toLowerCase()),options:tA.map(e=>({label:e.agent_name||e.agent_id,value:e.agent_id}))}),(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-2",children:"This key will be used by the selected agent to make requests to LiteLLM"})]}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(T.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",className:"mt-4",children:(0,t.jsx)(K.default,{organizations:eb,loading:e_,disabled:"Admin"!==ex,onChange:e=>{e2(e||null),e1(null),e6(null),eI.setFieldValue("team_id",void 0),eI.setFieldValue("project_id",void 0)}})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Team"," ",(0,t.jsx)(T.Tooltip,{title:"The team this key belongs to, which determines available models and budget limits",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"team_id",initialValue:e?e.team_id:null,className:"mt-4",rules:[{required:"service_account"===eD,message:"Please select a team for the service account"}],help:"service_account"===eD?"required":"",children:(0,t.jsx)(G.default,{disabled:null!==e3,organizationId:e4,onTeamSelect:e=>{e1(e),e6(null),eI.setFieldValue("project_id",void 0),e?.organization_id?(e2(e.organization_id),eI.setFieldValue("organization_id",e.organization_id)):e||(e2(null),eI.setFieldValue("organization_id",void 0))}})}),eN&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Project"," ",(0,t.jsx)(T.Tooltip,{title:"Assign this key to a project. Selecting a project will lock the team to the project's team.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"project_id",className:"mt-4",children:(0,t.jsx)(q.default,{projects:ej,teamId:e0?.team_id,loading:ev||!ed,onChange:e=>{if(!e){e6(null),e1(null),eI.setFieldValue("team_id",void 0);return}e6(e)}})})]}),tR&&(0,t.jsx)("div",{className:"mb-8 p-4 bg-blue-50 border border-blue-200 rounded-md",children:(0,t.jsx)(y.Text,{className:"text-blue-800 text-sm",children:"Please select a team to continue configuring your Virtual Key. If you do not see any teams, please contact your Proxy Admin to either provide you with access to models or to add you to a team."})}),!tR&&(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Details"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["you"===eD||"another_user"===eD?"Key Name":"Service Account ID"," ",(0,t.jsx)(T.Tooltip,{title:"you"===eD||"another_user"===eD?"A descriptive name to identify this key":"Unique identifier for this service account",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_alias",rules:[{required:!0,message:`Please input a ${"you"===eD?"key name":"service account ID"}`}],help:"required",children:(0,t.jsx)(f.TextInput,{placeholder:""})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Models"," ",(0,t.jsx)(T.Tooltip,{title:"Select which models this key can access. Choose 'All Team Models' to grant access to all models available to the team. Leave empty to allow access to all models.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"models",rules:[],help:"management"===tc||"read_only"===tc?"Models field is disabled for this key type":"optional - leave empty to allow access to all models",className:"mt-4",children:(0,t.jsxs)(N.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:"management"===tc||"read_only"===tc,onChange:e=>{e.includes("all-team-models")?eI.setFieldsValue({models:["all-team-models"]}):e.includes("all-proxy-models")&&eI.setFieldsValue({models:["all-proxy-models"]})},children:[!e3&&e0&&(0,t.jsx)(ei,{value:"all-team-models",children:"All Team Models"},"all-team-models"),!e3&&!e0&&(0,t.jsx)(ei,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),eB.map(e=>(0,t.jsx)(ei,{value:e,disabled:(0,Y.hasAllModelsSentinel)(tO),children:(0,Y.getModelDisplayName)(e)},e))]})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Key Type"," ",(0,t.jsx)(T.Tooltip,{title:"Select the type of key to determine what routes and operations this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_type",initialValue:"llm_api",className:"mt-4",children:(0,t.jsxs)(N.Select,{defaultValue:"llm_api",placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",onChange:e=>{tu(e),("management"===e||"read_only"===e)&&eI.setFieldsValue({models:[]})},children:[(0,t.jsx)(ei,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"AI APIs"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(ei,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Management"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only management routes (user/team/key management)"})]})}),(0,t.jsx)(ei,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Full Access"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})})]})})]}),!tR&&(0,t.jsx)("div",{className:"mb-8",children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)(b.Title,{className:"m-0",children:"Optional Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum amount in USD this key can spend. When reached, the key will be blocked from making further requests",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",help:`Budget cannot exceed team max budget: $${e?.max_budget!==null&&e?.max_budget!==void 0?e?.max_budget:"unlimited"}`,rules:[{validator:async(t,l)=>{if(l&&e&&null!==e.max_budget&&l>e.max_budget)throw Error(`Budget cannot exceed team max budget: $${(0,o.formatNumberWithCommas)(e.max_budget,4)}`)}}],children:(0,t.jsx)(ea.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(T.Tooltip,{title:"How often the budget should reset. For example, setting 'daily' will reset the budget every 24 hours",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",help:`Team Reset Budget: ${e?.budget_duration!==null&&e?.budget_duration!==void 0?e?.budget_duration:"None"}`,children:(0,t.jsx)(P.default,{onChange:e=>eI.setFieldValue("budget_duration",e)})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(T.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(Q.BudgetWindowsEditor,{value:t_,onChange:tj})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(T.Tooltip,{title:"When a model exceeds its per-model budget (model_max_budget), requests automatically reroute to fallback models instead of failing. Configure per-model budgets in Advanced Settings.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(H.BudgetFallbacksEditor,{value:tk,onChange:tN,availableModels:eB},tS)}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Tokens per minute Limit (TPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of tokens this key can process per minute. Helps control usage and costs",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tpm_limit",help:`TPM cannot exceed team TPM limit: ${e?.tpm_limit!==null&&e?.tpm_limit!==void 0?e?.tpm_limit:"unlimited"}`,rules:[{validator:async(t,l)=>{if(l&&e&&null!==e.tpm_limit&&l>e.tpm_limit)throw Error(`TPM limit cannot exceed team TPM limit: ${e.tpm_limit}`)}}],children:(0,t.jsx)(ea.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"tpm",name:"tpm_limit_type",className:"mt-4",initialValue:null,form:eI,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Requests per minute Limit (RPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of API requests this key can make per minute. Helps prevent abuse and manage load",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"rpm_limit",help:`RPM cannot exceed team RPM limit: ${e?.rpm_limit!==null&&e?.rpm_limit!==void 0?e?.rpm_limit:"unlimited"}`,rules:[{validator:async(t,l)=>{if(l&&e&&null!==e.rpm_limit&&l>e.rpm_limit)throw Error(`RPM limit cannot exceed team RPM limit: ${e.rpm_limit}`)}}],children:(0,t.jsx)(ea.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"rpm",name:"rpm_limit_type",className:"mt-4",initialValue:null,form:eI,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Per-Tag Rate Limits"," ",(0,t.jsx)(T.Tooltip,{title:"Scope rate limits to a request tag so each tag (e.g. a cell or group) gets its own RPM counter. Requests without a matching tag fall back to the key-level limit.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(J.TagRateLimitEditor,{value:tv,onChange:tw})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Throttle on budget exceeded"," ",(0,t.jsx)(T.Tooltip,{title:"When this key exceeds its max budget, throttle its TPM/RPM to the globally configured percentage instead of blocking access entirely. Requires budget_exceeded_throttle_percentage in litellm_settings and a TPM/RPM limit on the key.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"throttle_on_budget_exceeded",valuePropName:"checked",children:(0,t.jsx)(S.Switch,{checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"Apply safety guardrails to this key to filter content or enforce policies",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"guardrails",className:"mt-4",help:ef?"Select existing guardrails or enter new ones":"Premium feature - Upgrade to set guardrails by key",children:(0,t.jsx)(N.Select,{mode:"tags",style:{width:"100%"},disabled:!ef,placeholder:ef?"Select or enter guardrails":"Premium feature - Upgrade to set guardrails by key",options:eq.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"disable_global_guardrails",className:"mt-4",valuePropName:"checked",help:ef?"Bypass global guardrails for this key":"Premium feature - Upgrade to disable global guardrails by key",children:(0,t.jsx)(S.Switch,{disabled:!ef,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(T.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"policies",className:"mt-4",help:ey?"Select existing policies or enter new ones":"Premium feature - Upgrade to set policies by key",children:(0,t.jsx)(N.Select,{mode:"tags",style:{width:"100%"},disabled:!ey,placeholder:ey?"Select or enter policies":"Premium feature - Upgrade to set policies by key",options:eH.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Prompts"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific prompt templates",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/prompt_management",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"prompts",className:"mt-4",help:ey?"Select existing prompts or enter new ones":"Premium feature - Upgrade to set prompts by key",children:(0,t.jsx)(N.Select,{mode:"tags",style:{width:"100%"},disabled:!ey,placeholder:ey?"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:eJ.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(T.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",className:"mt-4",help:"Select access groups to assign to this key",children:(0,t.jsx)(E.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Pass Through Routes"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific pass through routes",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/pass_through",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"allowed_passthrough_routes",className:"mt-4",help:ey?"Select existing pass through routes or enter new ones":"Premium feature - Upgrade to set pass through routes by key",children:(0,t.jsx)(D.default,{onChange:e=>eI.setFieldValue("allowed_passthrough_routes",e),value:eI.getFieldValue("allowed_passthrough_routes"),accessToken:eg,placeholder:ey?"Select or enter pass through routes":"Premium feature - Upgrade to set pass through routes by key",disabled:!ey,teamId:e0?e0.team_id:null})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Vector Stores"," ",(0,t.jsx)(T.Tooltip,{title:"Select which vector stores this key can access. If none selected, the key will have access to all available vector stores",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_vector_store_ids",className:"mt-4",help:"Select vector stores this key can access. Leave empty for access to all vector stores",children:(0,t.jsx)(er.default,{onChange:e=>eI.setFieldValue("allowed_vector_store_ids",e),value:eI.getFieldValue("allowed_vector_store_ids"),accessToken:eg,placeholder:"Select vector stores (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Metadata"," ",(0,t.jsx)(T.Tooltip,{title:"JSON object with additional information about this key. Used for tracking or custom logic",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"metadata",className:"mt-4",children:(0,t.jsx)(v.Input.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Tags"," ",(0,t.jsx)(T.Tooltip,{title:"Tags for tracking spend and/or doing tag-based routing. Used for analytics and filtering",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tags",className:"mt-4",help:"Tags for tracking spend and/or doing tag-based routing.",children:(0,t.jsx)(N.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",tokenSeparators:[","],options:eC})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"MCP Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(T.Tooltip,{title:"Select which MCP servers or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_mcp_servers_and_groups",help:"Select MCP servers or access groups this key can access",children:(0,t.jsx)(X.default,{onChange:e=>eI.setFieldValue("allowed_mcp_servers_and_groups",e),value:eI.getFieldValue("allowed_mcp_servers_and_groups"),accessToken:eg,teamId:e0?.team_id??null,placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(j.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(j.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-6",children:(0,t.jsx)(ee.default,{accessToken:eg,selectedServers:(eI.getFieldValue("allowed_mcp_servers_and_groups")?.servers||[]).filter(e=>e!==Z.NO_MCP_SERVERS_SENTINEL),toolPermissions:eI.getFieldValue("mcp_tool_permissions")||{},onChange:e=>eI.setFieldsValue({mcp_tool_permissions:e})})})})]})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Agent Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Agents"," ",(0,t.jsx)(T.Tooltip,{title:"Select which agents or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_agents_and_groups",help:"Select agents or access groups this key can access",children:(0,t.jsx)(M.default,{onChange:e=>eI.setFieldValue("allowed_agents_and_groups",e),value:eI.getFieldValue("allowed_agents_and_groups"),accessToken:eg,placeholder:"Select agents or access groups (optional)"})})})]}),ey?(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eX,onChange:eZ,premiumUser:!0,disabledCallbacks:to,onDisabledCallbacksChange:td})})})]}):(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Key-level logging settings is an enterprise feature, get in touch -",(0,t.jsx)("a",{href:"https://www.litellm.ai/enterprise",target:"_blank",children:"https://www.litellm.ai/enterprise"})]}),placement:"top",children:(0,t.jsxs)("div",{style:{position:"relative"},children:[(0,t.jsx)("div",{style:{opacity:.5},children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eX,onChange:eZ,premiumUser:!1,disabledCallbacks:to,onDisabledCallbacksChange:td})})})]})}),(0,t.jsx)("div",{style:{position:"absolute",inset:0,cursor:"not-allowed"}})]})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Router Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4 w-full",children:(0,t.jsx)(U.default,{accessToken:eg||"",value:tf||void 0,onChange:tb,modelData:eP.length>0?{data:eP.map(e=>({model_name:e}))}:void 0},tT)})})]},`router-settings-accordion-${tT}`),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Model Aliases"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsx)(y.Text,{className:"text-sm text-gray-600 mb-4",children:"Create custom aliases for models that can be used in API calls. This allows you to create shortcuts for specific models."}),(0,t.jsx)($.default,{accessToken:eg,initialModelAliases:tm,onAliasUpdate:tp,showExampleConfig:!1})]})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Key Lifecycle"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(B.default,{form:eI,autoRotationEnabled:tg,onAutoRotationChange:th,rotationInterval:tx,onRotationIntervalChange:ty,isCreateMode:!0})})}),(0,t.jsx)(j.Form.Item,{name:"duration",hidden:!0,initialValue:null,children:(0,t.jsx)(v.Input,{})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("b",{children:"Advanced Settings"}),(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Learn more about advanced settings in our"," ",(0,t.jsx)("a",{href:el.proxyBaseUrl?`${el.proxyBaseUrl}/#/key%20management/generate_key_fn_key_generate_post`:"/#/key%20management/generate_key_fn_key_generate_post",target:"_blank",rel:"noopener noreferrer",className:"text-blue-400 hover:text-blue-300",children:"documentation"})]}),children:(0,t.jsx)(d.InfoCircleOutlined,{className:"text-gray-400 hover:text-gray-300 cursor-help"})})]})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(R.default,{schemaComponent:"GenerateKeyRequest",form:eI,excludedFields:["key_alias","team_id","organization_id","models","duration","metadata","tags","guardrails","max_budget","budget_duration","tpm_limit","rpm_limit",...eS?["key"]:[]]})})]})]})]})}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(_.Button,{htmlType:"submit",disabled:tR,style:{opacity:tR?.5:1},children:"Create Key"})})]})}),e5&&(0,t.jsx)(w.Modal,{title:"Create New User",open:e5,onCancel:()=>e7(!1),footer:null,width:800,children:(0,t.jsx)(W.CreateUserButton,{userID:eh,accessToken:eg,teams:ed,possibleUIRoles:te,onUserCreated:e=>{e9(e),eI.setFieldsValue({user_id:e}),e7(!1)},isEmbedded:!0})}),eF&&(0,t.jsx)(w.Modal,{open:eA,onOk:tE,onCancel:tP,footer:null,children:(0,t.jsxs)(x.Grid,{numItems:1,className:"gap-2 w-full",children:[(0,t.jsx)(b.Title,{children:"Save your Key"}),(0,t.jsx)(h.Col,{numColSpan:1,children:null!=eF?(0,t.jsx)(es.default,{apiKey:eF}):(0,t.jsx)(y.Text,{children:"Key being created, this might take 30s"})})]})})]})},"fetchTeamModels",0,en,"fetchUserModels",0,eo],702597)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0n~wn5hor8~tu.js b/litellm/proxy/_experimental/out/_next/static/chunks/0n~wn5hor8~tu.js new file mode 100644 index 00000000000..94b44f6c85b --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0n~wn5hor8~tu.js @@ -0,0 +1,17 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,270377,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var r=e.i(9583),n=a.forwardRef(function(e,n){return a.createElement(r.default,(0,t.default)({},e,{ref:n,icon:l}))});e.s(["ExclamationCircleOutlined",0,n],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),l=e.i(529681),r=e.i(242064),n=e.i(517455),i=e.i(185793),o=e.i(721369),s=function(e,t){var a={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(a[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(a[l[r]]=e[l[r]]);return a};let d=e=>{var{prefixCls:l,className:n,hoverable:i=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(r.ConfigContext),c=d("card",l),u=(0,a.default)(`${c}-grid`,n,{[`${c}-grid-hoverable`]:i});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var c=e.i(915654),u=e.i(183293),g=e.i(246422),m=e.i(838378);let b=(0,g.genStyleHooks)("Card",e=>{let t=(0,m.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:a,cardHeadPadding:l,colorBorderSecondary:r,boxShadowTertiary:n,bodyPadding:i,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:n},[`${t}-head`]:(e=>{let{antCls:t,componentCls:a,headerHeight:l,headerPadding:r,tabsMarginBottom:n}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:l,marginBottom:-1,padding:`0 ${(0,c.unit)(r)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${a}-typography, + > ${a}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:n,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:i,borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:a,cardShadow:l,lineWidth:r}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,c.unit)(r)} 0 0 0 ${a}, + 0 ${(0,c.unit)(r)} 0 0 ${a}, + ${(0,c.unit)(r)} ${(0,c.unit)(r)} 0 0 ${a}, + ${(0,c.unit)(r)} 0 0 0 ${a} inset, + 0 ${(0,c.unit)(r)} 0 0 ${a} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:l}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:a,actionsLiMargin:l,cardActionsIconSize:r,colorBorderSecondary:n,actionsBg:i}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:i,borderTop:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${n}`,display:"flex",borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:l,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${a}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,c.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${a}`]:{fontSize:r,lineHeight:(0,c.unit)(e.calc(r).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${n}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,c.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${r}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:a}},[`${t}-contain-grid`]:{borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:l}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:a,headerPadding:l,bodyPadding:r}=e;return{[`${t}-head`]:{padding:`0 ${(0,c.unit)(l)}`,background:a,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,c.unit)(e.padding)} ${(0,c.unit)(r)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:a,headerPaddingSM:l,headerHeightSM:r,headerFontSizeSM:n}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:r,padding:`0 ${(0,c.unit)(l)}`,fontSize:n,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:a}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,a;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(a=e.headerPadding)?a:e.paddingLG}});var p=e.i(792812),f=function(e,t){var a={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(a[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(a[l[r]]=e[l[r]]);return a};let h=e=>{let{actionClasses:a,actions:l=[],actionStyle:r}=e;return t.createElement("ul",{className:a,style:r},l.map((e,a)=>{let r=`action-${a}`;return t.createElement("li",{style:{width:`${100/l.length}%`},key:r},t.createElement("span",null,e))}))},$=t.forwardRef((e,s)=>{let c,{prefixCls:u,className:g,rootClassName:m,style:$,extra:y,headStyle:x={},bodyStyle:v={},title:j,loading:O,bordered:C,variant:w,size:S,type:k,cover:N,actions:E,tabList:T,children:z,activeTabKey:B,defaultActiveTabKey:M,tabBarExtraContent:R,hoverable:P,tabProps:L={},classNames:H,styles:I}=e,F=f(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:A,direction:W,card:D}=t.useContext(r.ConfigContext),[G]=(0,p.default)("card",w,C),q=e=>{var t;return(0,a.default)(null==(t=null==D?void 0:D.classNames)?void 0:t[e],null==H?void 0:H[e])},X=e=>{var t;return Object.assign(Object.assign({},null==(t=null==D?void 0:D.styles)?void 0:t[e]),null==I?void 0:I[e])},_=t.useMemo(()=>{let e=!1;return t.Children.forEach(z,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[z]),K=A("card",u),[Y,U,J]=b(K),Q=t.createElement(i.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},z),V=void 0!==B,Z=Object.assign(Object.assign({},L),{[V?"activeKey":"defaultActiveKey"]:V?B:M,tabBarExtraContent:R}),ee=(0,n.default)(S),et=ee&&"default"!==ee?ee:"large",ea=T?t.createElement(o.default,Object.assign({size:et},Z,{className:`${K}-head-tabs`,onChange:t=>{var a;null==(a=e.onTabChange)||a.call(e,t)},items:T.map(e=>{var{tab:t}=e;return Object.assign({label:t},f(e,["tab"]))})})):null;if(j||y||ea){let e=(0,a.default)(`${K}-head`,q("header")),l=(0,a.default)(`${K}-head-title`,q("title")),r=(0,a.default)(`${K}-extra`,q("extra")),n=Object.assign(Object.assign({},x),X("header"));c=t.createElement("div",{className:e,style:n},t.createElement("div",{className:`${K}-head-wrapper`},j&&t.createElement("div",{className:l,style:X("title")},j),y&&t.createElement("div",{className:r,style:X("extra")},y)),ea)}let el=(0,a.default)(`${K}-cover`,q("cover")),er=N?t.createElement("div",{className:el,style:X("cover")},N):null,en=(0,a.default)(`${K}-body`,q("body")),ei=Object.assign(Object.assign({},v),X("body")),eo=t.createElement("div",{className:en,style:ei},O?Q:z),es=(0,a.default)(`${K}-actions`,q("actions")),ed=(null==E?void 0:E.length)?t.createElement(h,{actionClasses:es,actionStyle:X("actions"),actions:E}):null,ec=(0,l.default)(F,["onTabChange"]),eu=(0,a.default)(K,null==D?void 0:D.className,{[`${K}-loading`]:O,[`${K}-bordered`]:"borderless"!==G,[`${K}-hoverable`]:P,[`${K}-contain-grid`]:_,[`${K}-contain-tabs`]:null==T?void 0:T.length,[`${K}-${ee}`]:ee,[`${K}-type-${k}`]:!!k,[`${K}-rtl`]:"rtl"===W},g,m,U,J),eg=Object.assign(Object.assign({},null==D?void 0:D.style),$);return Y(t.createElement("div",Object.assign({ref:s},ec,{className:eu,style:eg}),c,er,eo,ed))});var y=function(e,t){var a={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(a[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(a[l[r]]=e[l[r]]);return a};$.Grid=d,$.Meta=e=>{let{prefixCls:l,className:n,avatar:i,title:o,description:s}=e,d=y(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:c}=t.useContext(r.ConfigContext),u=c("card",l),g=(0,a.default)(`${u}-meta`,n),m=i?t.createElement("div",{className:`${u}-meta-avatar`},i):null,b=o?t.createElement("div",{className:`${u}-meta-title`},o):null,p=s?t.createElement("div",{className:`${u}-meta-description`},s):null,f=b||p?t.createElement("div",{className:`${u}-meta-detail`},b,p):null;return t.createElement("div",Object.assign({},d,{className:g}),m,f)},e.s(["Card",0,$],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),l=e.i(908206),r=e.i(242064),n=e.i(517455),i=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var d=e.i(876556),c=function(e,t){var a={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(a[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(a[l[r]]=e[l[r]]);return a},u=function(e,t){var a={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(a[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(a[l[r]]=e[l[r]]);return a};let g=e=>{let{itemPrefixCls:l,component:r,span:n,className:i,style:o,labelStyle:d,contentStyle:c,bordered:u,label:g,content:m,colon:b,type:p,styles:f}=e,{classNames:h}=t.useContext(s),$=Object.assign(Object.assign({},d),null==f?void 0:f.label),y=Object.assign(Object.assign({},c),null==f?void 0:f.content);if(u)return t.createElement(r,{colSpan:n,style:o,className:(0,a.default)(i,{[`${l}-item-${p}`]:"label"===p||"content"===p,[null==h?void 0:h.label]:(null==h?void 0:h.label)&&"label"===p,[null==h?void 0:h.content]:(null==h?void 0:h.content)&&"content"===p})},null!=g&&t.createElement("span",{style:$},g),null!=m&&t.createElement("span",{style:y},m));return t.createElement(r,{colSpan:n,style:o,className:(0,a.default)(`${l}-item`,i)},t.createElement("div",{className:`${l}-item-container`},null!=g&&t.createElement("span",{style:$,className:(0,a.default)(`${l}-item-label`,null==h?void 0:h.label,{[`${l}-item-no-colon`]:!b})},g),null!=m&&t.createElement("span",{style:y,className:(0,a.default)(`${l}-item-content`,null==h?void 0:h.content)},m)))};function m(e,{colon:a,prefixCls:l,bordered:r},{component:n,type:i,showLabel:o,showContent:s,labelStyle:d,contentStyle:c,styles:u}){return e.map(({label:e,children:m,prefixCls:b=l,className:p,style:f,labelStyle:h,contentStyle:$,span:y=1,key:x,styles:v},j)=>"string"==typeof n?t.createElement(g,{key:`${i}-${x||j}`,className:p,style:f,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),h),null==v?void 0:v.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),$),null==v?void 0:v.content)},span:y,colon:a,component:n,itemPrefixCls:b,bordered:r,label:o?e:null,content:s?m:null,type:i}):[t.createElement(g,{key:`label-${x||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),f),h),null==v?void 0:v.label),span:1,colon:a,component:n[0],itemPrefixCls:b,bordered:r,label:e,type:"label"}),t.createElement(g,{key:`content-${x||j}`,className:p,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),f),$),null==v?void 0:v.content),span:2*y-1,component:n[1],itemPrefixCls:b,bordered:r,content:m,type:"content"})])}let b=e=>{let a=t.useContext(s),{prefixCls:l,vertical:r,row:n,index:i,bordered:o}=e;return r?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${i}`,className:`${l}-row`},m(n,e,Object.assign({component:"th",type:"label",showLabel:!0},a))),t.createElement("tr",{key:`content-${i}`,className:`${l}-row`},m(n,e,Object.assign({component:"td",type:"content",showContent:!0},a)))):t.createElement("tr",{key:i,className:`${l}-row`},m(n,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},a)))};e.i(296059);var p=e.i(915654),f=e.i(183293),h=e.i(246422),$=e.i(838378);let y=(0,h.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:a,itemPaddingBottom:l,itemPaddingEnd:r,colonMarginRight:n,colonMarginLeft:i,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,f.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:a}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.padding)} ${(0,p.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,p.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:a,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingSM)} ${(0,p.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,p.unit)(e.paddingXS)} ${(0,p.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},f.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:a,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:l,paddingInlineEnd:r},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,p.unit)(i)} ${(0,p.unit)(n)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,$.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var x=function(e,t){var a={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(a[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(a[l[r]]=e[l[r]]);return a};let v=e=>{let g,{prefixCls:m,title:p,extra:f,column:h,colon:$=!0,bordered:v,layout:j,children:O,className:C,rootClassName:w,style:S,size:k,labelStyle:N,contentStyle:E,styles:T,items:z,classNames:B}=e,M=x(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:R,direction:P,className:L,style:H,classNames:I,styles:F}=(0,r.useComponentConfig)("descriptions"),A=R("descriptions",m),W=(0,i.default)(),D=t.useMemo(()=>{var e;return"number"==typeof h?h:null!=(e=(0,l.matchScreen)(W,Object.assign(Object.assign({},o),h)))?e:3},[W,h]),G=(g=t.useMemo(()=>z||(0,d.default)(O).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[z,O]),t.useMemo(()=>g.map(e=>{var{span:t}=e,a=c(e,["span"]);return"filled"===t?Object.assign(Object.assign({},a),{filled:!0}):Object.assign(Object.assign({},a),{span:"number"==typeof t?t:(0,l.matchScreen)(W,t)})}),[g,W])),q=(0,n.default)(k),X=((e,a)=>{let[l,r]=(0,t.useMemo)(()=>{let t,l,r,n;return t=[],l=[],r=!1,n=0,a.filter(e=>e).forEach(a=>{let{filled:i}=a,o=u(a,["filled"]);if(i){l.push(o),t.push(l),l=[],n=0;return}let s=e-n;(n+=a.span||1)>=e?(n>e?(r=!0,l.push(Object.assign(Object.assign({},o),{span:s}))):l.push(o),t.push(l),l=[],n=0):l.push(o)}),l.length>0&&t.push(l),[t=t.map(t=>{let a=t.reduce((e,t)=>e+(t.span||1),0);if(a({labelStyle:N,contentStyle:E,styles:{content:Object.assign(Object.assign({},F.content),null==T?void 0:T.content),label:Object.assign(Object.assign({},F.label),null==T?void 0:T.label)},classNames:{label:(0,a.default)(I.label,null==B?void 0:B.label),content:(0,a.default)(I.content,null==B?void 0:B.content)}}),[N,E,T,B,I,F]);return _(t.createElement(s.Provider,{value:U},t.createElement("div",Object.assign({className:(0,a.default)(A,L,I.root,null==B?void 0:B.root,{[`${A}-${q}`]:q&&"default"!==q,[`${A}-bordered`]:!!v,[`${A}-rtl`]:"rtl"===P},C,w,K,Y),style:Object.assign(Object.assign(Object.assign(Object.assign({},H),F.root),null==T?void 0:T.root),S)},M),(p||f)&&t.createElement("div",{className:(0,a.default)(`${A}-header`,I.header,null==B?void 0:B.header),style:Object.assign(Object.assign({},F.header),null==T?void 0:T.header)},p&&t.createElement("div",{className:(0,a.default)(`${A}-title`,I.title,null==B?void 0:B.title),style:Object.assign(Object.assign({},F.title),null==T?void 0:T.title)},p),f&&t.createElement("div",{className:(0,a.default)(`${A}-extra`,I.extra,null==B?void 0:B.extra),style:Object.assign(Object.assign({},F.extra),null==T?void 0:T.extra)},f)),t.createElement("div",{className:`${A}-view`},t.createElement("table",null,t.createElement("tbody",null,X.map((e,a)=>t.createElement(b,{key:a,index:a,colon:$,prefixCls:A,vertical:"vertical"===j,bordered:v,row:e}))))))))};v.Item=({children:e})=>e,e.s(["Descriptions",0,v],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),a=e.i(732961),l=e.i(289882),r=e.i(170517),n=e.i(628882),i=e.i(320890),o=e.i(104458),s=e.i(722319),d=e.i(8398),c=e.i(279728);e.i(765846);var u=e.i(602716),g=e.i(328052),m=e.i(135551);let b=(e,t)=>new m.FastColor(e).setA(t).toRgbString(),p=(e,t)=>new m.FastColor(e).lighten(t).toHexString(),f=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},h=(e,t)=>{let a=e||"#000",l=t||"#fff";return{colorBgBase:a,colorTextBase:l,colorText:b(l,.85),colorTextSecondary:b(l,.65),colorTextTertiary:b(l,.45),colorTextQuaternary:b(l,.25),colorFill:b(l,.18),colorFillSecondary:b(l,.12),colorFillTertiary:b(l,.08),colorFillQuaternary:b(l,.04),colorBgSolid:b(l,.95),colorBgSolidHover:b(l,1),colorBgSolidActive:b(l,.9),colorBgElevated:p(a,12),colorBgContainer:p(a,8),colorBgLayout:p(a,0),colorBgSpotlight:p(a,26),colorBgBlur:b(l,.04),colorBorder:p(a,26),colorBorderSecondary:p(a,19)}},$={defaultSeed:i.defaultConfig.token,useToken:function(){let[e,t,a]=(0,o.useToken)();return{theme:e,token:t,hashId:a}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let a=Object.keys(r.defaultPresetColors).map(t=>{let a=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,l,r)=>(e[`${t}-${r+1}`]=a[r],e[`${t}${r+1}`]=a[r],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),l=null!=t?t:(0,s.default)(e),n=(0,g.default)(e,{generateColorPalettes:f,generateNeutralColorPalettes:h});return Object.assign(Object.assign(Object.assign(Object.assign({},l),a),n),{colorPrimaryBg:n.colorPrimaryBorder,colorPrimaryBgHover:n.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let a=null!=t?t:(0,s.default)(e),l=a.fontSizeSM,r=a.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},a),function(e){let{sizeUnit:t,sizeStep:a}=e,l=a-2;return{sizeXXL:t*(l+10),sizeXL:t*(l+6),sizeLG:t*(l+2),sizeMD:t*(l+2),sizeMS:t*(l+1),size:t*l,sizeSM:t*l,sizeXS:t*(l-1),sizeXXS:t*(l-1)}}(null!=t?t:e)),(0,c.default)(l)),{controlHeight:r}),(0,d.default)(Object.assign(Object.assign({},a),{controlHeight:r})))},getDesignToken:e=>{let i=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):l.default,o=Object.assign(Object.assign({},r.default),null==e?void 0:e.token);return(0,a.getComputedToken)(o,{override:null==e?void 0:e.token},i,n.default)},defaultConfig:i.defaultConfig,_internalContext:i.DesignTokenContext};e.s(["theme",0,$],368869)},127952,e=>{"use strict";var t=e.i(843476),a=e.i(560445),l=e.i(175712),r=e.i(869216),n=e.i(311451),i=e.i(212931),o=e.i(898586),s=e.i(368869),d=e.i(270377),c=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:g,message:m,resourceInformationTitle:b,resourceInformation:p,onCancel:f,onOk:h,confirmLoading:$,requiredConfirmation:y}){let{Title:x,Text:v}=o.Typography,{token:j}=s.theme.useToken(),[O,C]=(0,c.useState)("");return(0,c.useEffect)(()=>{e&&C("")},[e]),(0,t.jsx)(i.Modal,{title:u,open:e,onOk:h,onCancel:f,confirmLoading:$,okText:$?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!y&&O!==y||$},cancelButtonProps:{disabled:$},children:(0,t.jsxs)("div",{className:"space-y-4",children:[g&&(0,t.jsx)(a.Alert,{message:g,type:"warning"}),(0,t.jsx)(l.Card,{title:b,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder}},style:{backgroundColor:j.colorErrorBg,borderColor:j.colorErrorBorder},children:(0,t.jsx)(r.Descriptions,{column:1,size:"small",children:p&&p.map(({label:e,value:a,...l})=>(0,t.jsx)(r.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(v,{...l,children:a??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(v,{children:m})}),y&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(v,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(v,{children:"Type "}),(0,t.jsx)(v,{strong:!0,type:"danger",children:y}),(0,t.jsx)(v,{children:" to confirm deletion:"})]}),(0,t.jsx)(n.Input,{value:O,onChange:e=>C(e.target.value),placeholder:y,className:"rounded-md",prefix:(0,t.jsx)(d.ExclamationCircleOutlined,{style:{color:j.colorError}}),autoFocus:!0})]})]})})}])},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),l=e.i(242064),r=e.i(529681);let n=e=>{let{prefixCls:l,className:r,style:n,size:i,shape:o}=e,s=(0,a.default)({[`${l}-lg`]:"large"===i,[`${l}-sm`]:"small"===i}),d=(0,a.default)({[`${l}-circle`]:"circle"===o,[`${l}-square`]:"square"===o,[`${l}-round`]:"round"===o}),c=t.useMemo(()=>"number"==typeof i?{width:i,height:i,lineHeight:`${i}px`}:{},[i]);return t.createElement("span",{className:(0,a.default)(l,s,d,r),style:Object.assign(Object.assign({},c),n)})};e.i(296059);var i=e.i(694758),o=e.i(915654),s=e.i(246422),d=e.i(838378);let c=new i.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),u=e=>({height:e,lineHeight:(0,o.unit)(e)}),g=e=>Object.assign({width:e},u(e)),m=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},u(e)),b=e=>Object.assign({width:e},u(e)),p=(e,t,a)=>{let{skeletonButtonCls:l}=e;return{[`${a}${l}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${a}${l}-round`]:{borderRadius:t}}},f=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},u(e)),h=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:a}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:a,skeletonTitleCls:l,skeletonParagraphCls:r,skeletonButtonCls:n,skeletonInputCls:i,skeletonImageCls:o,controlHeight:s,controlHeightLG:d,controlHeightSM:u,gradientFromColor:h,padding:$,marginSM:y,borderRadius:x,titleHeight:v,blockRadius:j,paragraphLiHeight:O,controlHeightXS:C,paragraphMarginTop:w}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:$,verticalAlign:"top",[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:h},g(s)),[`${a}-circle`]:{borderRadius:"50%"},[`${a}-lg`]:Object.assign({},g(d)),[`${a}-sm`]:Object.assign({},g(u))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[l]:{width:"100%",height:v,background:h,borderRadius:j,[`+ ${r}`]:{marginBlockStart:u}},[r]:{padding:0,"> li":{width:"100%",height:O,listStyle:"none",background:h,borderRadius:j,"+ li":{marginBlockStart:C}}},[`${r}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${l}, ${r} > li`]:{borderRadius:x}}},[`${t}-with-avatar ${t}-content`]:{[l]:{marginBlockStart:y,[`+ ${r}`]:{marginBlockStart:w}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:a,controlHeight:l,controlHeightLG:r,controlHeightSM:n,gradientFromColor:i,calc:o}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:t,width:o(l).mul(2).equal(),minWidth:o(l).mul(2).equal()},f(l,o))},p(e,l,a)),{[`${a}-lg`]:Object.assign({},f(r,o))}),p(e,r,`${a}-lg`)),{[`${a}-sm`]:Object.assign({},f(n,o))}),p(e,n,`${a}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:a,controlHeight:l,controlHeightLG:r,controlHeightSM:n}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:a},g(l)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},g(r)),[`${t}${t}-sm`]:Object.assign({},g(n))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:a,skeletonInputCls:l,controlHeightLG:r,controlHeightSM:n,gradientFromColor:i,calc:o}=e;return{[l]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:a},m(t,o)),[`${l}-lg`]:Object.assign({},m(r,o)),[`${l}-sm`]:Object.assign({},m(n,o))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:a,gradientFromColor:l,borderRadiusSM:r,calc:n}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:l,borderRadius:r},b(n(a).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(a)),{maxWidth:n(a).mul(4).equal(),maxHeight:n(a).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[n]:{width:"100%"},[i]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${l}, + ${r} > li, + ${a}, + ${n}, + ${i}, + ${o} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:a(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:a}=e;return{color:t,colorGradientEnd:a,gradientFromColor:t,gradientToColor:a,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),$=e=>{let{prefixCls:l,className:r,style:n,rows:i=0}=e,o=Array.from({length:i}).map((a,l)=>t.createElement("li",{key:l,style:{width:((e,t)=>{let{width:a,rows:l=2}=t;return Array.isArray(a)?a[e]:l-1===e?a:void 0})(l,e)}}));return t.createElement("ul",{className:(0,a.default)(l,r),style:n},o)},y=({prefixCls:e,className:l,width:r,style:n})=>t.createElement("h3",{className:(0,a.default)(e,l),style:Object.assign({width:r},n)});function x(e){return e&&"object"==typeof e?e:{}}let v=e=>{let{prefixCls:r,loading:i,className:o,rootClassName:s,style:d,children:c,avatar:u=!1,title:g=!0,paragraph:m=!0,active:b,round:p}=e,{getPrefixCls:f,direction:v,className:j,style:O}=(0,l.useComponentConfig)("skeleton"),C=f("skeleton",r),[w,S,k]=h(C);if(i||!("loading"in e)){let e,l,r=!!u,i=!!g,c=!!m;if(r){let a=Object.assign(Object.assign({prefixCls:`${C}-avatar`},i&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),x(u));e=t.createElement("div",{className:`${C}-header`},t.createElement(n,Object.assign({},a)))}if(i||c){let e,a;if(i){let a=Object.assign(Object.assign({prefixCls:`${C}-title`},!r&&c?{width:"38%"}:r&&c?{width:"50%"}:{}),x(g));e=t.createElement(y,Object.assign({},a))}if(c){let e,l=Object.assign(Object.assign({prefixCls:`${C}-paragraph`},(e={},r&&i||(e.width="61%"),!r&&i?e.rows=3:e.rows=2,e)),x(m));a=t.createElement($,Object.assign({},l))}l=t.createElement("div",{className:`${C}-content`},e,a)}let f=(0,a.default)(C,{[`${C}-with-avatar`]:r,[`${C}-active`]:b,[`${C}-rtl`]:"rtl"===v,[`${C}-round`]:p},j,o,s,S,k);return w(t.createElement("div",{className:f,style:Object.assign(Object.assign({},O),d)},e,l))}return null!=c?c:null};v.Button=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,block:c=!1,size:u="default"}=e,{getPrefixCls:g}=t.useContext(l.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,r.default)(e,["prefixCls"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d,[`${m}-block`]:c},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-button`,size:u},$))))},v.Avatar=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,shape:c="circle",size:u="default"}=e,{getPrefixCls:g}=t.useContext(l.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,r.default)(e,["prefixCls","className"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-avatar`,shape:c,size:u},$))))},v.Input=e=>{let{prefixCls:i,className:o,rootClassName:s,active:d,block:c,size:u="default"}=e,{getPrefixCls:g}=t.useContext(l.ConfigContext),m=g("skeleton",i),[b,p,f]=h(m),$=(0,r.default)(e,["prefixCls"]),y=(0,a.default)(m,`${m}-element`,{[`${m}-active`]:d,[`${m}-block`]:c},o,s,p,f);return b(t.createElement("div",{className:y},t.createElement(n,Object.assign({prefixCls:`${m}-input`,size:u},$))))},v.Image=e=>{let{prefixCls:r,className:n,rootClassName:i,style:o,active:s}=e,{getPrefixCls:d}=t.useContext(l.ConfigContext),c=d("skeleton",r),[u,g,m]=h(c),b=(0,a.default)(c,`${c}-element`,{[`${c}-active`]:s},n,i,g,m);return u(t.createElement("div",{className:b},t.createElement("div",{className:(0,a.default)(`${c}-image`,n),style:o},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},v.Node=e=>{let{prefixCls:r,className:n,rootClassName:i,style:o,active:s,children:d}=e,{getPrefixCls:c}=t.useContext(l.ConfigContext),u=c("skeleton",r),[g,m,b]=h(u),p=(0,a.default)(u,`${u}-element`,{[`${u}-active`]:s},m,n,i,b);return g(t.createElement("div",{className:p},t.createElement("div",{className:(0,a.default)(`${u}-image`,n),style:o},d)))},e.s(["default",0,v],185793)},922611,e=>{"use strict";var t=e.i(271645),a=e.i(175066);function l(){}let r=t.createContext({add:l,remove:l});e.s(["usePanelRef",0,function(e){let l=t.useContext(r),n=t.useRef(null);return(0,a.default)(t=>{if(t){let a=e?t.querySelector(e):t;a&&(l.add(a),n.current=a)}else l.remove(n.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let a=(e,t=0,a=!1,l=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!l)return"-";let r={minimumFractionDigits:t,maximumFractionDigits:t};if(!a)return e.toLocaleString("en-US",r);let n=e<0?"-":"",i=Math.abs(e),o=i,s="";return i>=1e6?(o=i/1e6,s="M"):i>=1e3&&(o=i/1e3,s="K"),`${n}${o.toLocaleString("en-US",r)}${s}`},l=async(e,a="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return r(e,a);try{return await navigator.clipboard.writeText(e),t.default.success(a),!0}catch(t){return console.error("Clipboard API failed: ",t),r(e,a)}},r=(e,a)=>{try{let l=document.createElement("textarea");l.value=e,l.style.position="fixed",l.style.left="-999999px",l.style.top="-999999px",l.setAttribute("readonly",""),document.body.appendChild(l),l.focus(),l.select();let r=document.execCommand("copy");if(document.body.removeChild(l),r)return t.default.success(a),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,l,"formatNumberWithCommas",0,a,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let l=a(e,t,!1,!1);if(0===Number(l.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${l}`},"updateExistingKeys",0,function(e,t){let a=structuredClone(e);for(let[e,l]of Object.entries(t))e in a&&(a[e]=l);return a}])},112179,581070,e=>{"use strict";var t=e.i(843476),a=e.i(487486),l=e.i(115504),r=e.i(746798);function n({content:e,trigger:a}){return(0,t.jsx)(r.TooltipProvider,{delay:300,children:(0,t.jsxs)(r.Tooltip,{children:[(0,t.jsx)(r.TooltipTrigger,{render:a}),(0,t.jsx)(r.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,n],581070);let i={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:r,tooltip:o,dataTestId:s}){let d=(0,t.jsx)(a.Badge,{variant:"outline","data-testid":s,className:(0,l.cn)("whitespace-nowrap font-normal",i[e]),children:r});return o?(0,t.jsx)(n,{content:o,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),a=e.i(843476);let l=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],r=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:n="datetime",fallback:i="-"}){let o,s,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,a.jsx)("span",{className:"text-muted-foreground",children:i}):(0,a.jsx)(t.CellTooltip,{content:(o=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${l[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${r(c.getHours())}:${r(c.getMinutes())}:${r(c.getSeconds())}`,`${s}, ${d} (${o})`),trigger:(0,a.jsx)("span",{className:"whitespace-nowrap",children:"date"===n?`${l[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${l[c.getMonth()]} ${c.getDate()}, ${r(c.getHours())}:${r(c.getMinutes())}:${r(c.getSeconds())}`})})}],200208);var n=e.i(174886),i=e.i(115504),o=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:l="pill",onClick:r,copyable:d=!1,truncate:c=!0,fallback:u="-",tooltip:g,disabled:m=!1,dataTestId:b,className:p}){if(!e)return(0,a.jsx)("span",{className:"text-muted-foreground",children:u});let f=!!r&&!m,h=(0,i.cn)(s[l].base,f&&s[l].clickable,c&&"block max-w-[15ch] truncate",m&&"opacity-50",p),$=f?(0,a.jsx)("button",{type:"button",className:h,"data-testid":b,onClick:()=>r(e),children:e}):(0,a.jsx)("span",{className:h,"data-testid":b,children:e}),y=(0,a.jsx)(t.CellTooltip,{content:g??e,trigger:$});return d?(0,a.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[y,(0,a.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,o.copyToClipboard)(e)},children:(0,a.jsx)(n.Copy,{className:"size-3"})})]}):y}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:l="-",showZero:r=!1}){return null==e||Number.isNaN(e)?(0,a.jsx)("span",{className:"text-muted-foreground",children:l}):0===e?r?(0,a.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,o.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,a.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,a.jsx)("span",{className:"whitespace-nowrap",children:(0,o.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,a],68155)},871943,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,a],871943)},360820,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,a],360820)},269200,e=>{"use strict";var t=e.i(290571),a=e.i(271645),l=e.i(444755);let r=(0,e.i(673706).makeClassName)("Table"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement("div",{className:(0,l.tremorTwMerge)(r("root"),"overflow-auto",o)},a.default.createElement("table",Object.assign({ref:n,className:(0,l.tremorTwMerge)(r("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),i))});n.displayName="Table",e.s(["Table",0,n],269200)},942232,e=>{"use strict";var t=e.i(290571),a=e.i(271645),l=e.i(444755);let r=(0,e.i(673706).makeClassName)("TableBody"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("tbody",Object.assign({ref:n,className:(0,l.tremorTwMerge)(r("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",o)},s),i))});n.displayName="TableBody",e.s(["TableBody",0,n],942232)},977572,e=>{"use strict";var t=e.i(290571),a=e.i(271645),l=e.i(444755);let r=(0,e.i(673706).makeClassName)("TableCell"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("td",Object.assign({ref:n,className:(0,l.tremorTwMerge)(r("root"),"align-middle whitespace-nowrap text-left p-4",o)},s),i))});n.displayName="TableCell",e.s(["TableCell",0,n],977572)},427612,e=>{"use strict";var t=e.i(290571),a=e.i(271645),l=e.i(444755);let r=(0,e.i(673706).makeClassName)("TableHead"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("thead",Object.assign({ref:n,className:(0,l.tremorTwMerge)(r("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",o)},s),i))});n.displayName="TableHead",e.s(["TableHead",0,n],427612)},64848,e=>{"use strict";var t=e.i(290571),a=e.i(271645),l=e.i(444755);let r=(0,e.i(673706).makeClassName)("TableHeaderCell"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("th",Object.assign({ref:n,className:(0,l.tremorTwMerge)(r("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",o)},s),i))});n.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,n],64848)},496020,e=>{"use strict";var t=e.i(290571),a=e.i(271645),l=e.i(444755);let r=(0,e.i(673706).makeClassName)("TableRow"),n=a.default.forwardRef((e,n)=>{let{children:i,className:o}=e,s=(0,t.__rest)(e,["children","className"]);return a.default.createElement(a.default.Fragment,null,a.default.createElement("tr",Object.assign({ref:n,className:(0,l.tremorTwMerge)(r("row"),o)},s),i))});n.displayName="TableRow",e.s(["TableRow",0,n],496020)},389083,e=>{"use strict";var t=e.i(290571),a=e.i(271645),l=e.i(829087),r=e.i(480731),n=e.i(95779),i=e.i(444755),o=e.i(673706);let s={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},d={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},c=(0,o.makeClassName)("Badge"),u=a.default.forwardRef((e,u)=>{let{color:g,icon:m,size:b=r.Sizes.SM,tooltip:p,className:f,children:h}=e,$=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),y=m||null,{tooltipProps:x,getReferenceProps:v}=(0,l.useTooltip)();return a.default.createElement("span",Object.assign({ref:(0,o.mergeRefs)([u,x.refs.setReference]),className:(0,i.tremorTwMerge)(c("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",g?(0,i.tremorTwMerge)((0,o.getColorClassNames)(g,n.colorPalette.background).bgColor,(0,o.getColorClassNames)(g,n.colorPalette.iconText).textColor,(0,o.getColorClassNames)(g,n.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,i.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),s[b].paddingX,s[b].paddingY,s[b].fontSize,f)},v,$),a.default.createElement(l.default,Object.assign({text:p},x)),y?a.default.createElement(y,{className:(0,i.tremorTwMerge)(c("icon"),"shrink-0 -ml-1 mr-1.5",d[b].height,d[b].width)}):null,a.default.createElement("span",{className:(0,i.tremorTwMerge)(c("text"),"whitespace-nowrap")},h))});u.displayName="Badge",e.s(["Badge",0,u],389083)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0oeiq~0bevyfo.js b/litellm/proxy/_experimental/out/_next/static/chunks/0oeiq~0bevyfo.js new file mode 100644 index 00000000000..6e886fc0480 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0oeiq~0bevyfo.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),l=e.i(480731),s=e.i(444755),i=e.i(673706),n=e.i(95779);let o={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},m=(0,i.makeClassName)("Icon"),u=r.default.forwardRef((e,u)=>{let{icon:g,variant:h="simple",tooltip:x,size:p=l.Sizes.SM,color:b,className:f}=e,j=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),w=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,i.getColorClassNames)(t,n.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(h,b),{tooltipProps:C,getReferenceProps:v}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([u,C.refs.setReference]),className:(0,s.tremorTwMerge)(m("root"),"inline-flex shrink-0 items-center justify-center",w.bgColor,w.textColor,w.borderColor,w.ringColor,c[h].rounded,c[h].border,c[h].shadow,c[h].ring,o[p].paddingX,o[p].paddingY,f)},v,j),r.default.createElement(a.default,Object.assign({text:x},C)),r.default.createElement(g,{className:(0,s.tremorTwMerge)(m("icon"),"shrink-0",d[p].height,d[p].width)}))});u.displayName="Icon",e.s(["default",0,u],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),l=e.i(529681);let s=e=>{let{prefixCls:a,className:l,style:s,size:i,shape:n}=e,o=(0,r.default)({[`${a}-lg`]:"large"===i,[`${a}-sm`]:"small"===i}),d=(0,r.default)({[`${a}-circle`]:"circle"===n,[`${a}-square`]:"square"===n,[`${a}-round`]:"round"===n}),c=t.useMemo(()=>"number"==typeof i?{width:i,height:i,lineHeight:`${i}px`}:{},[i]);return t.createElement("span",{className:(0,r.default)(a,o,d,l),style:Object.assign(Object.assign({},c),s)})};e.i(296059);var i=e.i(694758),n=e.i(915654),o=e.i(246422),d=e.i(838378);let c=new i.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),m=e=>({height:e,lineHeight:(0,n.unit)(e)}),u=e=>Object.assign({width:e},m(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},m(e)),h=e=>Object.assign({width:e},m(e)),x=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},p=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},m(e)),b=(0,o.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:l,skeletonButtonCls:s,skeletonInputCls:i,skeletonImageCls:n,controlHeight:o,controlHeightLG:d,controlHeightSM:m,gradientFromColor:b,padding:f,marginSM:j,borderRadius:w,titleHeight:C,blockRadius:v,paragraphLiHeight:y,controlHeightXS:N,paragraphMarginTop:k}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:f,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:b},u(o)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},u(d)),[`${r}-sm`]:Object.assign({},u(m))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:C,background:b,borderRadius:v,[`+ ${l}`]:{marginBlockStart:m}},[l]:{padding:0,"> li":{width:"100%",height:y,listStyle:"none",background:b,borderRadius:v,"+ li":{marginBlockStart:N}}},[`${l}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${l} > li`]:{borderRadius:w}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:j,[`+ ${l}`]:{marginBlockStart:k}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:l,controlHeightSM:s,gradientFromColor:i,calc:n}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:t,width:n(a).mul(2).equal(),minWidth:n(a).mul(2).equal()},p(a,n))},x(e,a,r)),{[`${r}-lg`]:Object.assign({},p(l,n))}),x(e,l,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},p(s,n))}),x(e,s,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:l,controlHeightSM:s}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},u(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},u(l)),[`${t}${t}-sm`]:Object.assign({},u(s))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:l,controlHeightSM:s,gradientFromColor:i,calc:n}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:r},g(t,n)),[`${a}-lg`]:Object.assign({},g(l,n)),[`${a}-sm`]:Object.assign({},g(s,n))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:l,calc:s}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:l},h(s(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},h(r)),{maxWidth:s(r).mul(4).equal(),maxHeight:s(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[s]:{width:"100%"},[i]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${l} > li, + ${r}, + ${s}, + ${i}, + ${n} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),f=e=>{let{prefixCls:a,className:l,style:s,rows:i=0}=e,n=Array.from({length:i}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,l),style:s},n)},j=({prefixCls:e,className:a,width:l,style:s})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:l},s)});function w(e){return e&&"object"==typeof e?e:{}}let C=e=>{let{prefixCls:l,loading:i,className:n,rootClassName:o,style:d,children:c,avatar:m=!1,title:u=!0,paragraph:g=!0,active:h,round:x}=e,{getPrefixCls:p,direction:C,className:v,style:y}=(0,a.useComponentConfig)("skeleton"),N=p("skeleton",l),[k,T,$]=b(N);if(i||!("loading"in e)){let e,a,l=!!m,i=!!u,c=!!g;if(l){let r=Object.assign(Object.assign({prefixCls:`${N}-avatar`},i&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),w(m));e=t.createElement("div",{className:`${N}-header`},t.createElement(s,Object.assign({},r)))}if(i||c){let e,r;if(i){let r=Object.assign(Object.assign({prefixCls:`${N}-title`},!l&&c?{width:"38%"}:l&&c?{width:"50%"}:{}),w(u));e=t.createElement(j,Object.assign({},r))}if(c){let e,a=Object.assign(Object.assign({prefixCls:`${N}-paragraph`},(e={},l&&i||(e.width="61%"),!l&&i?e.rows=3:e.rows=2,e)),w(g));r=t.createElement(f,Object.assign({},a))}a=t.createElement("div",{className:`${N}-content`},e,r)}let p=(0,r.default)(N,{[`${N}-with-avatar`]:l,[`${N}-active`]:h,[`${N}-rtl`]:"rtl"===C,[`${N}-round`]:x},v,n,o,T,$);return k(t.createElement("div",{className:p,style:Object.assign(Object.assign({},y),d)},e,a))}return null!=c?c:null};C.Button=e=>{let{prefixCls:i,className:n,rootClassName:o,active:d,block:c=!1,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",i),[h,x,p]=b(g),f=(0,l.default)(e,["prefixCls"]),j=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d,[`${g}-block`]:c},n,o,x,p);return h(t.createElement("div",{className:j},t.createElement(s,Object.assign({prefixCls:`${g}-button`,size:m},f))))},C.Avatar=e=>{let{prefixCls:i,className:n,rootClassName:o,active:d,shape:c="circle",size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",i),[h,x,p]=b(g),f=(0,l.default)(e,["prefixCls","className"]),j=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d},n,o,x,p);return h(t.createElement("div",{className:j},t.createElement(s,Object.assign({prefixCls:`${g}-avatar`,shape:c,size:m},f))))},C.Input=e=>{let{prefixCls:i,className:n,rootClassName:o,active:d,block:c,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",i),[h,x,p]=b(g),f=(0,l.default)(e,["prefixCls"]),j=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:d,[`${g}-block`]:c},n,o,x,p);return h(t.createElement("div",{className:j},t.createElement(s,Object.assign({prefixCls:`${g}-input`,size:m},f))))},C.Image=e=>{let{prefixCls:l,className:s,rootClassName:i,style:n,active:o}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),c=d("skeleton",l),[m,u,g]=b(c),h=(0,r.default)(c,`${c}-element`,{[`${c}-active`]:o},s,i,u,g);return m(t.createElement("div",{className:h},t.createElement("div",{className:(0,r.default)(`${c}-image`,s),style:n},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},C.Node=e=>{let{prefixCls:l,className:s,rootClassName:i,style:n,active:o,children:d}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),m=c("skeleton",l),[u,g,h]=b(m),x=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:o},g,s,i,h);return u(t.createElement("div",{className:x},t.createElement("div",{className:(0,r.default)(`${m}-image`,s),style:n},d)))},e.s(["default",0,C],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let l=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(l),s=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),s.current=r)}else a.remove(s.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let l={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",l);let s=e<0?"-":"",i=Math.abs(e),n=i,o="";return i>=1e6?(n=i/1e6,o="M"):i>=1e3&&(n=i/1e3,o="K"),`${s}${n.toLocaleString("en-US",l)}${o}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return l(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),l(e,r)}},l=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let l=document.execCommand("copy");if(document.body.removeChild(a),l)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),l=e.i(746798);function s({content:e,trigger:r}){return(0,t.jsx)(l.TooltipProvider,{delay:300,children:(0,t.jsxs)(l.Tooltip,{children:[(0,t.jsx)(l.TooltipTrigger,{render:r}),(0,t.jsx)(l.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,s],581070);let i={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:l,tooltip:n,dataTestId:o}){let d=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":o,className:(0,a.cn)("whitespace-nowrap font-normal",i[e]),children:l});return n?(0,t.jsx)(s,{content:n,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],l=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:s="datetime",fallback:i="-"}){let n,o,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:i}):(0,r.jsx)(t.CellTooltip,{content:(n=Intl.DateTimeFormat().resolvedOptions().timeZone,o=`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${l(c.getHours())}:${l(c.getMinutes())}:${l(c.getSeconds())}`,`${o}, ${d} (${n})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===s?`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${a[c.getMonth()]} ${c.getDate()}, ${l(c.getHours())}:${l(c.getMinutes())}:${l(c.getSeconds())}`})})}],200208);var s=e.i(174886),i=e.i(115504),n=e.i(500330);let o={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:l,copyable:d=!1,truncate:c=!0,fallback:m="-",tooltip:u,disabled:g=!1,dataTestId:h,className:x}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:m});let p=!!l&&!g,b=(0,i.cn)(o[a].base,p&&o[a].clickable,c&&"block max-w-[15ch] truncate",g&&"opacity-50",x),f=p?(0,r.jsx)("button",{type:"button",className:b,"data-testid":h,onClick:()=>l(e),children:e}):(0,r.jsx)("span",{className:b,"data-testid":h,children:e}),j=(0,r.jsx)(t.CellTooltip,{content:u??e,trigger:f});return d?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[j,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,n.copyToClipboard)(e)},children:(0,r.jsx)(s.Copy,{className:"size-3"})})]}):j}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:l=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?l?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,n.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,n.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let l=(0,e.i(673706).makeClassName)("Table"),s=r.default.forwardRef((e,s)=>{let{children:i,className:n}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(l("root"),"overflow-auto",n)},r.default.createElement("table",Object.assign({ref:s,className:(0,a.tremorTwMerge)(l("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},o),i))});s.displayName="Table",e.s(["Table",0,s],269200)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableHead"),s=r.default.forwardRef((e,s)=>{let{children:i,className:n}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:s,className:(0,a.tremorTwMerge)(l("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",n)},o),i))});s.displayName="TableHead",e.s(["TableHead",0,s],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableHeaderCell"),s=r.default.forwardRef((e,s)=>{let{children:i,className:n}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:s,className:(0,a.tremorTwMerge)(l("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",n)},o),i))});s.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,s],64848)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableBody"),s=r.default.forwardRef((e,s)=>{let{children:i,className:n}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:s,className:(0,a.tremorTwMerge)(l("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",n)},o),i))});s.displayName="TableBody",e.s(["TableBody",0,s],942232)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableRow"),s=r.default.forwardRef((e,s)=>{let{children:i,className:n}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:s,className:(0,a.tremorTwMerge)(l("row"),n)},o),i))});s.displayName="TableRow",e.s(["TableRow",0,s],496020)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let l=(0,e.i(673706).makeClassName)("TableCell"),s=r.default.forwardRef((e,s)=>{let{children:i,className:n}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:s,className:(0,a.tremorTwMerge)(l("root"),"align-middle whitespace-nowrap text-left p-4",n)},o),i))});s.displayName="TableCell",e.s(["TableCell",0,s],977572)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),l=e.i(480731),s=e.i(95779),i=e.i(444755),n=e.i(673706);let o={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},d={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},c=(0,n.makeClassName)("Badge"),m=r.default.forwardRef((e,m)=>{let{color:u,icon:g,size:h=l.Sizes.SM,tooltip:x,className:p,children:b}=e,f=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),j=g||null,{tooltipProps:w,getReferenceProps:C}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,n.mergeRefs)([m,w.refs.setReference]),className:(0,i.tremorTwMerge)(c("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",u?(0,i.tremorTwMerge)((0,n.getColorClassNames)(u,s.colorPalette.background).bgColor,(0,n.getColorClassNames)(u,s.colorPalette.iconText).textColor,(0,n.getColorClassNames)(u,s.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,i.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),o[h].paddingX,o[h].paddingY,o[h].fontSize,p)},C,f),r.default.createElement(a.default,Object.assign({text:x},w)),j?r.default.createElement(j,{className:(0,i.tremorTwMerge)(c("icon"),"shrink-0 -ml-1 mr-1.5",d[h].height,d[h].width)}):null,r.default.createElement("span",{className:(0,i.tremorTwMerge)(c("text"),"whitespace-nowrap")},b))});m.displayName="Badge",e.s(["Badge",0,m],389083)},94629,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,r],94629)},678784,e=>{"use strict";var t=e.i(678745);e.s(["CheckIcon",()=>t.default])},118366,e=>{"use strict";var t=e.i(991124);e.s(["CopyIcon",()=>t.default])},601757,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(752978),l=e.i(994388),s=e.i(309426),i=e.i(599724),n=e.i(350967),o=e.i(278587),d=e.i(304967),c=e.i(629569),m=e.i(389083),u=e.i(677667),g=e.i(898667),h=e.i(130643),x=e.i(808613),p=e.i(311451),b=e.i(199133),f=e.i(592968),j=e.i(827252),w=e.i(702597),C=e.i(355619),v=e.i(602869),y=e.i(727749),N=e.i(435451),k=e.i(860585),T=e.i(500330),$=e.i(678784),_=e.i(118366),S=e.i(464571);let E=({tagId:e,onClose:a,accessToken:s,is_admin:n,editTag:o})=>{let[E]=x.Form.useForm(),[M,O]=(0,r.useState)(null),[I,R]=(0,r.useState)(o),[B,L]=(0,r.useState)([]),[F,A]=(0,r.useState)({}),D=async(e,t)=>{await (0,T.copyToClipboard)(e)&&(A(e=>({...e,[t]:!0})),setTimeout(()=>{A(e=>({...e,[t]:!1}))},2e3))},P=async()=>{if(s)try{let t=(await (0,v.tagInfoCall)(s,[e]))[e];t&&(O(t),o&&E.setFieldsValue({name:t.name,description:t.description,models:t.models,max_budget:t.litellm_budget_table?.max_budget,budget_duration:t.litellm_budget_table?.budget_duration}))}catch(e){console.error("Error fetching tag details:",e),y.default.fromBackend("Error fetching tag details: "+e)}};(0,r.useEffect)(()=>{P()},[e,s]),(0,r.useEffect)(()=>{s&&(0,w.fetchUserModels)("dummy-user","Admin",s,L)},[s]);let z=async e=>{if(s)try{await (0,v.tagUpdateCall)(s,{name:e.name,description:e.description,models:e.models,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,budget_duration:e.budget_duration}),y.default.success("Tag updated successfully"),R(!1),P()}catch(e){console.error("Error updating tag:",e),y.default.fromBackend("Error updating tag: "+e)}};return M?(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-6",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(l.Button,{onClick:a,className:"mb-4",children:"← Back to Tags"}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Tag Name:"}),(0,t.jsx)("span",{className:"font-mono px-2 py-1 bg-gray-100 rounded-sm text-sm border border-gray-200",children:M.name}),(0,t.jsx)(S.Button,{type:"text",size:"small",icon:F["tag-name"]?(0,t.jsx)($.CheckIcon,{size:12}):(0,t.jsx)(_.CopyIcon,{size:12}),onClick:()=>D(M.name,"tag-name"),className:`transition-all duration-200 ${F["tag-name"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]}),(0,t.jsx)(i.Text,{className:"text-gray-500",children:M.description||"No description"})]}),n&&!I&&(0,t.jsx)(l.Button,{onClick:()=>R(!0),children:"Edit Tag"})]}),I?(0,t.jsx)(d.Card,{children:(0,t.jsxs)(x.Form,{form:E,onFinish:z,layout:"vertical",initialValues:M,children:[(0,t.jsx)(x.Form.Item,{label:"Tag Name",name:"name",rules:[{required:!0,message:"Please input a tag name"}],children:(0,t.jsx)(p.Input,{className:"rounded-md border-gray-300"})}),(0,t.jsx)(x.Form.Item,{label:"Description",name:"description",children:(0,t.jsx)(p.Input.TextArea,{rows:4})}),(0,t.jsx)(x.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Models",(0,t.jsx)(f.Tooltip,{title:"Select which models are allowed to process this type of data",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"models",children:(0,t.jsx)(b.Select,{mode:"multiple",placeholder:"Select Models",children:B.map(e=>(0,t.jsx)(b.Select.Option,{value:e,children:(0,C.getModelDisplayName)(e)},e))})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(g.AccordionHeader,{children:(0,t.jsx)(c.Title,{className:"m-0",children:"Budget & Rate Limits"})}),(0,t.jsxs)(h.AccordionBody,{children:[(0,t.jsx)(x.Form.Item,{label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(f.Tooltip,{title:"Maximum amount in USD this tag can spend",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",children:(0,t.jsx)(N.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(x.Form.Item,{label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(f.Tooltip,{title:"How often the budget should reset",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",children:(0,t.jsx)(k.default,{onChange:e=>E.setFieldValue("budget_duration",e)})}),(0,t.jsx)("div",{className:"mt-4 p-3 bg-gray-50 rounded-md border border-gray-200",children:(0,t.jsxs)("p",{className:"text-sm text-gray-600",children:["TPM/RPM limits for tags are not currently supported. If you need this feature, please"," ",(0,t.jsx)("a",{href:"https://github.com/BerriAI/litellm/issues/new",target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"create a GitHub issue"}),"."]})})]})]}),(0,t.jsxs)("div",{className:"flex justify-end space-x-2",children:[(0,t.jsx)(l.Button,{onClick:()=>R(!1),children:"Cancel"}),(0,t.jsx)(l.Button,{type:"submit",children:"Save Changes"})]})]})}):(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)(d.Card,{children:[(0,t.jsx)(c.Title,{children:"Tag Details"}),(0,t.jsxs)("div",{className:"space-y-4 mt-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Name"}),(0,t.jsx)(i.Text,{children:M.name})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Description"}),(0,t.jsx)(i.Text,{children:M.description||"-"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Allowed Models"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-2",children:M.models&&0!==M.models.length?M.models.map(e=>(0,t.jsx)(m.Badge,{color:"blue",children:(0,t.jsx)(f.Tooltip,{title:`ID: ${e}`,children:M.model_info?.[e]||e})},e)):(0,t.jsx)(m.Badge,{color:"red",children:"All Models"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Created"}),(0,t.jsx)(i.Text,{children:M.created_at?new Date(M.created_at).toLocaleString():"-"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Last Updated"}),(0,t.jsx)(i.Text,{children:M.updated_at?new Date(M.updated_at).toLocaleString():"-"})]})]})]}),M.litellm_budget_table&&(0,t.jsxs)(d.Card,{children:[(0,t.jsx)(c.Title,{children:"Budget & Rate Limits"}),(0,t.jsxs)("div",{className:"space-y-4 mt-4",children:[void 0!==M.litellm_budget_table.max_budget&&null!==M.litellm_budget_table.max_budget&&(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Max Budget"}),(0,t.jsxs)(i.Text,{children:["$",M.litellm_budget_table.max_budget]})]}),M.litellm_budget_table.budget_duration&&(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"Budget Duration"}),(0,t.jsx)(i.Text,{children:M.litellm_budget_table.budget_duration})]}),void 0!==M.litellm_budget_table.tpm_limit&&null!==M.litellm_budget_table.tpm_limit&&(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"TPM Limit"}),(0,t.jsx)(i.Text,{children:M.litellm_budget_table.tpm_limit.toLocaleString()})]}),void 0!==M.litellm_budget_table.rpm_limit&&null!==M.litellm_budget_table.rpm_limit&&(0,t.jsxs)("div",{children:[(0,t.jsx)(i.Text,{className:"font-medium",children:"RPM Limit"}),(0,t.jsx)(i.Text,{children:M.litellm_budget_table.rpm_limit.toLocaleString()})]})]})]})]})]}):(0,t.jsx)("div",{children:"Loading..."})};var M=e.i(871943),O=e.i(360820),I=e.i(591935),R=e.i(94629),B=e.i(68155),L=e.i(152990),F=e.i(682830),A=e.i(269200),D=e.i(942232),P=e.i(977572),z=e.i(427612),H=e.i(64848),q=e.i(496020);e.i(622826);var Y=e.i(200208),W=e.i(399536);let X="This is just a spend tag that was passed dynamically in a request. It does not control any LLM models.",U=({data:e,onEdit:l,onDelete:s,onSelectTag:n})=>{let[o,d]=r.default.useState([{id:"created_at",desc:!0}]),c=[{header:"Tag Name",accessorKey:"name",cell:({row:e})=>{let r=e.original,a=r.description===X;return(0,t.jsx)("div",{className:"overflow-hidden",children:(0,t.jsx)(W.IdCell,{value:r.name,truncate:!1,onClick:n,disabled:a,tooltip:a?"You cannot view the information of a dynamically generated spend tag":r.name})})}},{header:"Description",accessorKey:"description",cell:({row:e})=>{let r=e.original;return(0,t.jsx)(f.Tooltip,{title:r.description,children:(0,t.jsx)("span",{className:"text-xs",children:r.description||"-"})})}},{header:"Allowed Models",accessorKey:"models",cell:({row:e})=>{let r=e.original;return(0,t.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:r?.models?.length===0?(0,t.jsx)(m.Badge,{size:"xs",className:"mb-1",color:"red",children:"All Models"}):r?.models?.map(e=>(0,t.jsx)(m.Badge,{size:"xs",className:"mb-1",color:"blue",children:(0,t.jsx)(f.Tooltip,{title:`ID: ${e}`,children:(0,t.jsx)(i.Text,{children:r.model_info?.[e]||e})})},e))})}},{header:"Created",accessorKey:"created_at",sortingFn:"datetime",cell:({row:e})=>(0,t.jsx)(Y.DateCell,{value:e.original.created_at,precision:"date"})},{id:"actions",header:"Actions",cell:({row:e})=>{let r=e.original,i=r.description===X;return(0,t.jsxs)("div",{className:"flex space-x-2",children:[i?(0,t.jsx)(f.Tooltip,{title:"Dynamically generated spend tags cannot be edited",children:(0,t.jsx)(a.Icon,{icon:I.PencilAltIcon,size:"sm",className:"opacity-50 cursor-not-allowed","aria-label":"Edit tag (disabled)"})}):(0,t.jsx)(f.Tooltip,{title:"Edit tag",children:(0,t.jsx)(a.Icon,{icon:I.PencilAltIcon,size:"sm",onClick:()=>l(r),className:"cursor-pointer hover:text-blue-500"})}),i?(0,t.jsx)(f.Tooltip,{title:"Dynamically generated spend tags cannot be deleted",children:(0,t.jsx)(a.Icon,{icon:B.TrashIcon,size:"sm",className:"opacity-50 cursor-not-allowed","aria-label":"Delete tag (disabled)"})}):(0,t.jsx)(f.Tooltip,{title:"Delete tag",children:(0,t.jsx)(a.Icon,{icon:B.TrashIcon,size:"sm",onClick:()=>s(r.name),className:"cursor-pointer hover:text-red-500"})})]})}}],u=(0,L.useReactTable)({data:e,columns:c,state:{sorting:o},onSortingChange:d,getCoreRowModel:(0,F.getCoreRowModel)(),getSortedRowModel:(0,F.getSortedRowModel)(),enableSorting:!0});return(0,t.jsx)("div",{className:"rounded-lg custom-border relative",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(A.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(z.TableHead,{children:u.getHeaderGroups().map(e=>(0,t.jsx)(q.TableRow,{children:e.headers.map(e=>(0,t.jsx)(H.TableHeaderCell,{className:`py-1 h-8 ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,onClick:e.column.getToggleSortingHandler(),children:(0,t.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,t.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,L.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&(0,t.jsx)("div",{className:"w-4",children:e.column.getIsSorted()?({asc:(0,t.jsx)(O.ChevronUpIcon,{className:"h-4 w-4 text-blue-500"}),desc:(0,t.jsx)(M.ChevronDownIcon,{className:"h-4 w-4 text-blue-500"})})[e.column.getIsSorted()]:(0,t.jsx)(R.SwitchVerticalIcon,{className:"h-4 w-4 text-gray-400"})})]})},e.id))},e.id))}),(0,t.jsx)(D.TableBody,{children:u.getRowModel().rows.length>0?u.getRowModel().rows.map(e=>(0,t.jsx)(q.TableRow,{className:"h-8",children:e.getVisibleCells().map(e=>(0,t.jsx)(P.TableCell,{className:`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap ${"actions"===e.column.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,children:(0,L.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,t.jsx)(q.TableRow,{children:(0,t.jsx)(P.TableCell,{colSpan:c.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"No tags found"})})})})})]})})})};var K=e.i(779241),V=e.i(212931);let G=({visible:e,onCancel:r,onSubmit:a,availableModels:s})=>{let[i]=x.Form.useForm();return(0,t.jsx)(V.Modal,{title:"Create New Tag",open:e,width:800,footer:null,onCancel:()=>{i.resetFields(),r()},children:(0,t.jsxs)(x.Form,{form:i,onFinish:e=>{a(e),i.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsx)(x.Form.Item,{label:"Tag Name",name:"tag_name",rules:[{required:!0,message:"Please input a tag name"}],children:(0,t.jsx)(K.TextInput,{})}),(0,t.jsx)(x.Form.Item,{label:"Description",name:"description",children:(0,t.jsx)(p.Input.TextArea,{rows:4})}),(0,t.jsx)(x.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Models",(0,t.jsx)(f.Tooltip,{title:"Select which models are allowed to process requests from this tag",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_llms",children:(0,t.jsx)(b.Select,{mode:"multiple",placeholder:"Select Models",children:s.map(e=>(0,t.jsx)(b.Select.Option,{value:e.model_info.id,children:(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{children:e.model_name}),(0,t.jsxs)("span",{className:"text-gray-400 ml-2",children:["(",e.model_info.id,")"]})]})},e.model_info.id))})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(g.AccordionHeader,{children:(0,t.jsx)(c.Title,{className:"m-0",children:"Budget & Rate Limits (Optional)"})}),(0,t.jsxs)(h.AccordionBody,{children:[(0,t.jsx)(x.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(f.Tooltip,{title:"Maximum amount in USD this tag can spend. When reached, requests with this tag will be blocked",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",children:(0,t.jsx)(N.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(x.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(f.Tooltip,{title:"How often the budget should reset. For example, setting 'daily' will reset the budget every 24 hours",children:(0,t.jsx)(j.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",children:(0,t.jsx)(k.default,{onChange:e=>i.setFieldValue("budget_duration",e)})}),(0,t.jsx)("div",{className:"mt-4 p-3 bg-gray-50 rounded-md border border-gray-200",children:(0,t.jsxs)("p",{className:"text-sm text-gray-600",children:["TPM/RPM limits for tags are not currently supported. If you need this feature, please"," ",(0,t.jsx)("a",{href:"https://github.com/BerriAI/litellm/issues/new",target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"create a GitHub issue"}),"."]})})]})]}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(l.Button,{type:"submit",children:"Create Tag"})})]})})},J=({accessToken:e,userID:d,userRole:c})=>{let[m,u]=(0,r.useState)([]),[g,h]=(0,r.useState)(!1),[x,p]=(0,r.useState)(null),[b,f]=(0,r.useState)(!1),[j,w]=(0,r.useState)(!1),[C,N]=(0,r.useState)(null),[k,T]=(0,r.useState)(""),[$,_]=(0,r.useState)([]),S=async()=>{if(e)try{let t=await (0,v.tagListCall)(e);u(Object.values(t))}catch(e){console.error("Error fetching tags:",e),y.default.fromBackend("Error fetching tags: "+e)}},M=async t=>{if(e)try{await (0,v.tagCreateCall)(e,{name:t.tag_name,description:t.description,models:t.allowed_llms,max_budget:t.max_budget,soft_budget:t.soft_budget,tpm_limit:t.tpm_limit,rpm_limit:t.rpm_limit,budget_duration:t.budget_duration}),y.default.success("Tag created successfully"),h(!1),S()}catch(e){console.error("Error creating tag:",e),y.default.fromBackend("Error creating tag: "+e)}},O=async e=>{N(e),w(!0)},I=async()=>{if(e&&C){try{await (0,v.tagDeleteCall)(e,C),y.default.success("Tag deleted successfully"),S()}catch(e){console.error("Error deleting tag:",e),y.default.fromBackend("Error deleting tag: "+e)}w(!1),N(null)}};return(0,r.useEffect)(()=>{d&&c&&e&&(async()=>{try{let t=await (0,v.modelInfoCall)(e,d,c);t&&t.data&&_(t.data)}catch(e){console.error("Error fetching models:",e),y.default.fromBackend("Error fetching models: "+e)}})()},[e,d,c]),(0,r.useEffect)(()=>{S()},[e]),(0,t.jsx)("div",{className:"w-full mx-4 h-[75vh]",children:x?(0,t.jsx)(E,{tagId:x,onClose:()=>{p(null),f(!1)},accessToken:e,is_admin:"Admin"===c,editTag:b}):(0,t.jsxs)("div",{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,t.jsxs)("div",{className:"flex justify-between mt-2 w-full items-center mb-4",children:[(0,t.jsx)("h1",{children:"Tag Management"}),(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[k&&(0,t.jsxs)(i.Text,{children:["Last Refreshed: ",k]}),(0,t.jsx)(a.Icon,{icon:o.RefreshIcon,variant:"shadow",size:"xs",className:"self-center cursor-pointer",onClick:()=>{S(),T(new Date().toLocaleString())}})]})]}),(0,t.jsxs)(i.Text,{className:"mb-4",children:["Click on a tag name to view and edit its details.",(0,t.jsxs)("p",{children:["You can use tags to restrict the usage of certain LLMs based on tags passed in the request. Read more about tag routing"," ",(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/tag_routing",target:"_blank",rel:"noopener noreferrer",children:"here"}),"."]})]}),(0,t.jsx)(l.Button,{className:"mb-4",onClick:()=>h(!0),children:"+ Create New Tag"}),(0,t.jsx)(n.Grid,{numItems:1,className:"gap-2 pt-2 pb-2 h-[75vh] w-full mt-2",children:(0,t.jsx)(s.Col,{numColSpan:1,children:(0,t.jsx)(U,{data:m,onEdit:e=>{p(e.name),f(!0)},onDelete:O,onSelectTag:p})})}),(0,t.jsx)(G,{visible:g,onCancel:()=>h(!1),onSubmit:M,availableModels:$}),j&&(0,t.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,t.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,t.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,t.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,t.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,t.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,t.jsx)("div",{className:"sm:flex sm:items-start",children:(0,t.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,t.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Tag"}),(0,t.jsx)("div",{className:"mt-2",children:(0,t.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this tag?"})})]})})}),(0,t.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,t.jsx)(l.Button,{onClick:I,color:"red",className:"ml-2",children:"Delete"}),(0,t.jsx)(l.Button,{onClick:()=>{w(!1),N(null)},children:"Cancel"})]})]})]})})]})})};var Z=e.i(135214);e.s(["default",0,function(){let{accessToken:e,userRole:r,userId:a}=(0,Z.default)();return(0,t.jsx)(J,{accessToken:e,userRole:r,userID:a})}],601757)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0onea0n77pqw1.js b/litellm/proxy/_experimental/out/_next/static/chunks/0onea0n77pqw1.js new file mode 100644 index 00000000000..89db65ad002 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0onea0n77pqw1.js @@ -0,0 +1,86 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,291542,e=>{"use strict";e.i(247167);var t=e.i(271645),n={},r="rc-table-internal-hook",l=e.i(392221),o=e.i(175066),a=e.i(174428),i=e.i(929123),d=e.i(174080);function c(e){var n=t.createContext(void 0);return{Context:n,Provider:function(e){var r=e.value,o=e.children,i=t.useRef(r);i.current=r;var c=t.useState(function(){return{getValue:function(){return i.current},listeners:new Set}}),u=(0,l.default)(c,1)[0];return(0,a.default)(function(){(0,d.unstable_batchedUpdates)(function(){u.listeners.forEach(function(e){e(r)})})},[r]),t.createElement(n.Provider,{value:u},o)},defaultValue:e}}function u(e,n){var r=(0,o.default)("function"==typeof n?n:function(e){if(void 0===n)return e;if(!Array.isArray(n))return e[n];var t={};return n.forEach(function(n){t[n]=e[n]}),t}),d=t.useContext(null==e?void 0:e.Context),c=d||{},u=c.listeners,s=c.getValue,f=t.useRef();f.current=r(d?s():null==e?void 0:e.defaultValue);var p=t.useState({}),m=(0,l.default)(p,2)[1];return(0,a.default)(function(){if(d)return u.add(e),function(){u.delete(e)};function e(e){var t=r(e);(0,i.default)(f.current,t,!0)||m({})}},[d]),f.current}var s=e.i(931067),f=e.i(611935);function p(){var e=t.createContext(null);function n(){return t.useContext(e)}return{makeImmutable:function(r,l){var o=(0,f.supportRef)(r),a=function(a,i){var d=o?{ref:i}:{},c=t.useRef(0),u=t.useRef(a);return null!==n()?t.createElement(r,(0,s.default)({},a,d)):((!l||l(u.current,a))&&(c.current+=1),u.current=a,t.createElement(e.Provider,{value:c.current},t.createElement(r,(0,s.default)({},a,d))))};return o?t.forwardRef(a):a},responseImmutable:function(e,r){var l=(0,f.supportRef)(e),o=function(r,o){return n(),t.createElement(e,(0,s.default)({},r,l?{ref:o}:{}))};return l?t.memo(t.forwardRef(o),r):t.memo(o,r)},useImmutableMark:n}}var m=p();m.makeImmutable,m.responseImmutable,m.useImmutableMark;var h=p(),g=h.makeImmutable,v=h.responseImmutable,y=h.useImmutableMark,b=c(),x=e.i(410160),w=e.i(209428),C=e.i(211577),E=e.i(343794),k=e.i(182585),S=e.i(657791),N=e.i(883110),$=t.createContext({renderWithProps:!1});function K(e){var t=[],n={};return e.forEach(function(e){for(var r=e||{},l=r.key,o=r.dataIndex,a=l||(null==o?[]:Array.isArray(o)?o:[o]).join("-")||"RC_TABLE_KEY";n[a];)a="".concat(a,"_next");n[a]=!0,t.push(a)}),t}e.i(62664);var O=e.i(697539),R=function(e){var n,r=e.ellipsis,l=e.rowType,o=e.children,a=!0===r?{showTitle:!0}:r;return a&&(a.showTitle||"header"===l)&&("string"==typeof o||"number"==typeof o?n=o.toString():t.isValidElement(o)&&"string"==typeof o.props.children&&(n=o.props.children)),n};let I=t.memo(function(e){var n,r,o,a,d,c,f,p,m,h,g=e.component,v=e.children,N=e.ellipsis,K=e.scope,I=e.prefixCls,P=e.className,T=e.align,M=e.record,D=e.render,L=e.dataIndex,j=e.renderIndex,B=e.shouldCellUpdate,H=e.index,A=e.rowType,z=e.colSpan,W=e.rowSpan,F=e.fixLeft,_=e.fixRight,q=e.firstFixLeft,V=e.lastFixLeft,U=e.firstFixRight,X=e.lastFixRight,G=e.appendNode,Y=e.additionalProps,J=void 0===Y?{}:Y,Q=e.isSticky,Z="".concat(I,"-cell"),ee=u(b,["supportSticky","allColumnsFixedLeft","rowHoverable"]),et=ee.supportSticky,en=ee.allColumnsFixedLeft,er=ee.rowHoverable,el=(n=t.useContext($),r=y(),(0,k.default)(function(){if(null!=v)return[v];var e=null==L||""===L?[]:Array.isArray(L)?L:[L],r=(0,S.default)(M,e),l=r,o=void 0;if(D){var a=D(r,M,j);!a||"object"!==(0,x.default)(a)||Array.isArray(a)||t.isValidElement(a)?l=a:(l=a.children,o=a.props,n.renderWithProps=!0)}return[l,o]},[r,M,v,L,D,j],function(e,t){if(B){var r=(0,l.default)(e,2)[1];return B((0,l.default)(t,2)[1],r)}return!!n.renderWithProps||!(0,i.default)(e,t,!0)})),eo=(0,l.default)(el,2),ea=eo[0],ei=eo[1],ed={},ec="number"==typeof F&&et,eu="number"==typeof _&&et;ec&&(ed.position="sticky",ed.left=F),eu&&(ed.position="sticky",ed.right=_);var es=null!=(o=null!=(a=null!=(d=null==ei?void 0:ei.colSpan)?d:J.colSpan)?a:z)?o:1,ef=null!=(c=null!=(f=null!=(p=null==ei?void 0:ei.rowSpan)?p:J.rowSpan)?f:W)?c:1,ep=u(b,function(e){var t,n;return[(t=ef||1,n=e.hoverStartRow,H<=e.hoverEndRow&&H+t-1>=n),e.onHover]}),em=(0,l.default)(ep,2),eh=em[0],eg=em[1],ev=(0,O.useEvent)(function(e){var t;M&&eg(H,H+ef-1),null==J||null==(t=J.onMouseEnter)||t.call(J,e)}),ey=(0,O.useEvent)(function(e){var t;M&&eg(-1,-1),null==J||null==(t=J.onMouseLeave)||t.call(J,e)});if(0===es||0===ef)return null;var eb=null!=(m=J.title)?m:R({rowType:A,ellipsis:N,children:ea}),ex=(0,E.default)(Z,P,(h={},(0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)(h,"".concat(Z,"-fix-left"),ec&&et),"".concat(Z,"-fix-left-first"),q&&et),"".concat(Z,"-fix-left-last"),V&&et),"".concat(Z,"-fix-left-all"),V&&en&&et),"".concat(Z,"-fix-right"),eu&&et),"".concat(Z,"-fix-right-first"),U&&et),"".concat(Z,"-fix-right-last"),X&&et),"".concat(Z,"-ellipsis"),N),"".concat(Z,"-with-append"),G),"".concat(Z,"-fix-sticky"),(ec||eu)&&Q&&et),(0,C.default)(h,"".concat(Z,"-row-hover"),!ei&&eh)),J.className,null==ei?void 0:ei.className),ew={};T&&(ew.textAlign=T);var eC=(0,w.default)((0,w.default)((0,w.default)((0,w.default)({},null==ei?void 0:ei.style),ed),ew),J.style),eE=ea;return"object"!==(0,x.default)(eE)||Array.isArray(eE)||t.isValidElement(eE)||(eE=null),N&&(V||U)&&(eE=t.createElement("span",{className:"".concat(Z,"-content")},eE)),t.createElement(g,(0,s.default)({},ei,J,{className:ex,style:eC,title:eb,scope:K,onMouseEnter:er?ev:void 0,onMouseLeave:er?ey:void 0,colSpan:1!==es?es:null,rowSpan:1!==ef?ef:null}),G,eE)});function P(e,t,n,r,l){var o,a,i=n[e]||{},d=n[t]||{};"left"===i.fixed?o=r.left["rtl"===l?t:e]:"right"===d.fixed&&(a=r.right["rtl"===l?e:t]);var c=!1,u=!1,s=!1,f=!1,p=n[t+1],m=n[e-1],h=p&&!p.fixed||m&&!m.fixed||n.every(function(e){return"left"===e.fixed});return"rtl"===l?void 0!==o?f=!(m&&"left"===m.fixed)&&h:void 0!==a&&(s=!(p&&"right"===p.fixed)&&h):void 0!==o?c=!(p&&"left"===p.fixed)&&h:void 0!==a&&(u=!(m&&"right"===m.fixed)&&h),{fixLeft:o,fixRight:a,lastFixLeft:c,firstFixRight:u,lastFixRight:s,firstFixLeft:f,isSticky:r.isSticky}}var T=t.createContext({}),M=e.i(703923),D=["children"];function L(e){return e.children}L.Row=function(e){var n=e.children,r=(0,M.default)(e,D);return t.createElement("tr",r,n)},L.Cell=function(e){var n=e.className,r=e.index,l=e.children,o=e.colSpan,a=void 0===o?1:o,i=e.rowSpan,d=e.align,c=u(b,["prefixCls","direction"]),f=c.prefixCls,p=c.direction,m=t.useContext(T),h=m.scrollColumnIndex,g=m.stickyOffsets,v=m.flattenColumns,y=r+a-1+1===h?a+1:a,x=P(r,r+y-1,v,g,p);return t.createElement(I,(0,s.default)({className:n,index:r,component:"td",prefixCls:f,record:null,dataIndex:null,align:d,colSpan:y,rowSpan:i,render:function(){return l}},x))};let j=v(function(e){var n=e.children,r=e.stickyOffsets,l=e.flattenColumns,o=u(b,"prefixCls"),a=l.length-1,i=l[a],d=t.useMemo(function(){return{stickyOffsets:r,flattenColumns:l,scrollColumnIndex:null!=i&&i.scrollbar?a:null}},[i,l,a,r]);return t.createElement(T.Provider,{value:d},t.createElement("tfoot",{className:"".concat(o,"-summary")},n))});var B=e.i(430073),H=e.i(735049),A=e.i(815289),z=e.i(244009);function W(e,n,r,l){return t.useMemo(function(){if(null!=r&&r.size){for(var t=[],o=0;o<(null==e?void 0:e.length);o+=1)!function e(t,n,r,l,o,a,i){var d=a(n,i);t.push({record:n,indent:r,index:i,rowKey:d});var c=null==o?void 0:o.has(d);if(n&&Array.isArray(n[l])&&c)for(var u=0;u1?n-1:0),l=1;l5&&void 0!==arguments[5]?arguments[5]:[],c=arguments.length>6&&void 0!==arguments[6]?arguments[6]:0,u=e.record,s=e.prefixCls,f=e.columnsKey,p=e.fixedInfoList,m=e.expandIconColumnIndex,h=e.nestExpandable,g=e.indentSize,v=e.expandIcon,y=e.expanded,b=e.hasNestChildren,x=e.onTriggerExpand,w=e.expandable,C=e.expandedKeys,E=f[r],k=p[r];r===(m||0)&&h&&(i=t.createElement(t.Fragment,null,t.createElement("span",{style:{paddingLeft:"".concat(g*l,"px")},className:"".concat(s,"-row-indent indent-level-").concat(l)}),v({prefixCls:s,expanded:y,expandable:b,record:u,onExpand:x})));var S=(null==(a=n.onCell)?void 0:a.call(n,u,o))||{};if(c){var N=S.rowSpan,$=void 0===N?1:N;if(w&&$&&r=1)),style:(0,w.default)((0,w.default)({},l),null==S?void 0:S.style)}),b.map(function(e,n){var r=e.render,l=e.dataIndex,d=e.className,u=U(v,e,n,f,a,c,null==g?void 0:g.offset),p=u.key,b=u.fixedInfo,x=u.appendCellNode,w=u.additionalCellProps;return t.createElement(I,(0,s.default)({className:d,ellipsis:e.ellipsis,align:e.align,scope:e.rowScope,component:e.rowScope?h:m,prefixCls:y,key:p,record:o,index:a,renderIndex:i,dataIndex:l,render:r,shouldCellUpdate:e.shouldCellUpdate},b,{appendNode:x,additionalProps:w}))}));if($&&(K.current||N)){var P=k(o,a,f+1,N);n=t.createElement(_,{expanded:N,className:(0,E.default)("".concat(y,"-expanded-row"),"".concat(y,"-expanded-row-level-").concat(f+1),O),prefixCls:y,component:p,cellComponent:m,colSpan:g?g.colSpan:b.length,stickyOffset:null==g?void 0:g.sticky,isEmpty:!1},P)}return t.createElement(t.Fragment,null,R,n)});function G(e){var n=e.columnKey,r=e.onColumnResize,l=e.prefixCls,o=e.title,i=t.useRef();return(0,a.default)(function(){i.current&&r(n,i.current.offsetWidth)},[]),t.createElement(B.default,{data:n},t.createElement("th",{ref:i,className:"".concat(l,"-measure-cell")},t.createElement("div",{className:"".concat(l,"-measure-cell-content")},o||" ")))}var Y=e.i(606262);function J(e){var n=e.prefixCls,r=e.columnsKey,l=e.onColumnResize,o=e.columns,a=t.useRef(null),i=u(b,["measureRowRender"]).measureRowRender,d=t.createElement("tr",{"aria-hidden":"true",className:"".concat(n,"-measure-row"),ref:a,tabIndex:-1},t.createElement(B.default.Collection,{onBatchResize:function(e){(0,Y.default)(a.current)&&e.forEach(function(e){l(e.data,e.size.offsetWidth)})}},r.map(function(e){var r=o.find(function(t){return t.key===e}),a=null==r?void 0:r.title,i=t.isValidElement(a)?t.cloneElement(a,{ref:null}):a;return t.createElement(G,{prefixCls:n,key:e,columnKey:e,onColumnResize:l,title:i})})));return i?i(d):d}let Q=v(function(e){var n,r=e.data,l=e.measureColumnWidth,o=u(b,["prefixCls","getComponent","onColumnResize","flattenColumns","getRowKey","expandedKeys","childrenColumnName","emptyNode","expandedRowOffset","fixedInfoList","colWidths"]),a=o.prefixCls,i=o.getComponent,d=o.onColumnResize,c=o.flattenColumns,s=o.getRowKey,f=o.expandedKeys,p=o.childrenColumnName,m=o.emptyNode,h=o.expandedRowOffset,g=void 0===h?0:h,v=o.colWidths,y=W(r,p,f,s),x=t.useMemo(function(){return y.map(function(e){return e.rowKey})},[y]),w=t.useRef({renderWithProps:!1}),C=t.useMemo(function(){for(var e=c.length-g,t=0,n=0;n=0;c-=1){var f=n[c],p=r&&r[c],m=void 0,h=void 0;if(p&&(m=p[ee],"auto"===o&&(h=p.minWidth)),f||h||m||d){var g=m||{},v=(g.columnType,(0,M.default)(g,et));a.unshift(t.createElement("col",(0,s.default)({key:c,style:{width:f,minWidth:h}},v))),d=!0}}return a.length>0?t.createElement("colgroup",null,a):null};var er=e.i(8211),el=["className","noData","columns","flattenColumns","colWidths","colGroup","columCount","stickyOffsets","direction","fixHeader","stickyTopOffset","stickyBottomOffset","stickyClassName","scrollX","tableLayout","onScroll","children"],eo=t.forwardRef(function(e,n){var r=e.className,l=e.noData,o=e.columns,a=e.flattenColumns,i=e.colWidths,d=e.colGroup,c=e.columCount,s=e.stickyOffsets,p=e.direction,m=e.fixHeader,h=e.stickyTopOffset,g=e.stickyBottomOffset,v=e.stickyClassName,y=e.scrollX,x=e.tableLayout,k=e.onScroll,S=e.children,N=(0,M.default)(e,el),$=u(b,["prefixCls","scrollbarSize","isSticky","getComponent"]),K=$.prefixCls,O=$.scrollbarSize,R=$.isSticky,I=(0,$.getComponent)(["header","table"],"table"),P=R&&!m?0:O,T=t.useRef(null),D=t.useCallback(function(e){(0,f.fillRef)(n,e),(0,f.fillRef)(T,e)},[]);t.useEffect(function(){function e(e){var t=e.currentTarget,n=e.deltaX;n&&(k({currentTarget:t,scrollLeft:t.scrollLeft+n}),e.preventDefault())}var t=T.current;return null==t||t.addEventListener("wheel",e,{passive:!1}),function(){null==t||t.removeEventListener("wheel",e)}},[]);var L=a[a.length-1],j={fixed:L?L.fixed:null,scrollbar:!0,onHeaderCell:function(){return{className:"".concat(K,"-cell-scrollbar")}}},B=(0,t.useMemo)(function(){return P?[].concat((0,er.default)(o),[j]):o},[P,o]),H=(0,t.useMemo)(function(){return P?[].concat((0,er.default)(a),[j]):a},[P,a]),A=(0,t.useMemo)(function(){var e=s.right,t=s.left;return(0,w.default)((0,w.default)({},s),{},{left:"rtl"===p?[].concat((0,er.default)(t.map(function(e){return e+P})),[0]):t,right:"rtl"===p?e:[].concat((0,er.default)(e.map(function(e){return e+P})),[0]),isSticky:R})},[P,s,R]),z=(0,t.useMemo)(function(){for(var e=[],t=0;t1?"colgroup":"col":null,ellipsis:a.ellipsis,align:a.align,component:i,prefixCls:p,key:h[n]},d,{additionalProps:r,rowType:"header"}))}))},ed=v(function(e){var n=e.stickyOffsets,r=e.columns,l=e.flattenColumns,o=e.onHeaderRow,a=u(b,["prefixCls","getComponent"]),i=a.prefixCls,d=a.getComponent,c=t.useMemo(function(){var e=[];!function t(n,r){var l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;e[l]=e[l]||[];var o=r;return n.filter(Boolean).map(function(n){var r={key:n.key,className:n.className||"",children:n.title,column:n,colStart:o},a=1,i=n.children;return i&&i.length>0&&(a=t(i,o,l+1).reduce(function(e,t){return e+t},0),r.hasSubColumns=!0),"colSpan"in n&&(a=n.colSpan),"rowSpan"in n&&(r.rowSpan=n.rowSpan),r.colSpan=a,r.colEnd=r.colStart+a-1,e[l].push(r),o+=a,a})}(r,0);for(var t=e.length,n=function(n){e[n].forEach(function(e){"rowSpan"in e||e.hasSubColumns||(e.rowSpan=t-n)})},l=0;l1&&void 0!==arguments[1]?arguments[1]:"";return"number"==typeof t?t:t.endsWith("%")?e*parseFloat(t)/100:null}var es=["children"],ef=["fixed"];function ep(e){return(0,ec.default)(e).filter(function(e){return t.isValidElement(e)}).map(function(e){var t=e.key,n=e.props,r=n.children,l=(0,M.default)(n,es),o=(0,w.default)({key:t},l);return r&&(o.children=ep(r)),o})}function em(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"key";return e.filter(function(e){return e&&"object"===(0,x.default)(e)}).reduce(function(e,n,r){var l=n.fixed,o=!0===l?"left":l,a="".concat(t,"-").concat(r),i=n.children;return i&&i.length>0?[].concat((0,er.default)(e),(0,er.default)(em(i,a).map(function(e){var t;return(0,w.default)((0,w.default)({},e),{},{fixed:null!=(t=e.fixed)?t:o})}))):[].concat((0,er.default)(e),[(0,w.default)((0,w.default)({key:a},n),{},{fixed:o})])},[])}let eh=function(e,r){var o=e.prefixCls,a=e.columns,i=e.children,d=e.expandable,c=e.expandedKeys,u=e.columnTitle,s=e.getRowKey,f=e.onTriggerExpand,p=e.expandIcon,m=e.rowExpandable,h=e.expandIconColumnIndex,g=e.expandedRowOffset,v=void 0===g?0:g,y=e.direction,b=e.expandRowByClick,E=e.columnWidth,k=e.fixed,S=e.scrollWidth,N=e.clientWidth,$=t.useMemo(function(){return function e(t){return t.filter(function(e){return e&&"object"===(0,x.default)(e)&&!e.hidden}).map(function(t){var n=t.children;return n&&n.length>0?(0,w.default)((0,w.default)({},t),{},{children:e(n)}):t})}((a||ep(i)||[]).slice())},[a,i]),K=t.useMemo(function(){if(d){var e,r=$.slice();if(!r.includes(n)){var l=h||0,a=0===l&&"right"===k?$.length:l;a>=0&&r.splice(a,0,n)}var i=r.indexOf(n);r=r.filter(function(e,t){return e!==n||t===i});var g=$[i];e=k||(g?g.fixed:null);var y=(0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)({},ee,{className:"".concat(o,"-expand-icon-col"),columnType:"EXPAND_COLUMN"}),"title",u),"fixed",e),"className","".concat(o,"-row-expand-icon-cell")),"width",E),"render",function(e,n,r){var l=s(n,r),a=p({prefixCls:o,expanded:c.has(l),expandable:!m||m(n),record:n,onExpand:f});return b?t.createElement("span",{onClick:function(e){return e.stopPropagation()}},a):a});return r.map(function(e,t){var r=e===n?y:e;return t=0;t-=1){var n=R[t].fixed;if("left"===n||!0===n){e=t;break}}if(e>=0)for(var r=0;r<=e;r+=1){var l=R[r].fixed;if("left"!==l&&!0!==l)return!0}var o=R.findIndex(function(e){return"right"===e.fixed});if(o>=0){for(var a=o;a0){var e=0,t=0;R.forEach(function(n){var r=eu(S,n.width);r?e+=r:t+=1});var n=Math.max(S,N),r=Math.max(n-e,t),l=t,o=r/t,a=0,i=R.map(function(e){var t=(0,w.default)({},e),n=eu(S,t.width);if(n)t.width=n;else{var i=Math.floor(o);t.width=1===l?r:i,r-=i,l-=1}return a+=t.width,t});if(a=n-h})})}})},z=function(e){R(function(t){return(0,w.default)((0,w.default)({},t),{},{scrollLeft:x?e/x*k:0})})};return(t.useImperativeHandle(n,function(){return{setScrollLeft:z,checkScrollBarVisible:H}}),t.useEffect(function(){var e=ey(document.body,"mouseup",j,!1),t=ey(document.body,"mousemove",B,!1);return H(),function(){e.remove(),t.remove()}},[S,M]),t.useEffect(function(){if(p.current){for(var e=[],t=(0,ex.getDOM)(p.current);t;)e.push(t),t=t.parentElement;return e.forEach(function(e){return e.addEventListener("scroll",H,!1)}),window.addEventListener("resize",H,!1),window.addEventListener("scroll",H,!1),g.addEventListener("scroll",H,!1),function(){e.forEach(function(e){return e.removeEventListener("scroll",H)}),window.removeEventListener("resize",H),window.removeEventListener("scroll",H),g.removeEventListener("scroll",H)}}},[g]),t.useEffect(function(){O.isHiddenScrollBar||R(function(e){var t=p.current;return t?(0,w.default)((0,w.default)({},e),{},{scrollLeft:t.scrollLeft/t.scrollWidth*t.clientWidth}):e})},[O.isHiddenScrollBar]),x<=k||!S||O.isHiddenScrollBar)?null:t.createElement("div",{style:{height:(0,A.default)(),width:k,bottom:h},className:"".concat(y,"-sticky-scroll")},t.createElement("div",{onMouseDown:function(e){e.persist(),I.current.delta=e.pageX-O.scrollLeft,I.current.x=0,D(!0),e.preventDefault()},ref:N,className:(0,E.default)("".concat(y,"-sticky-scroll-bar"),(0,C.default)({},"".concat(y,"-sticky-scroll-bar-active"),M)),style:{width:"".concat(S,"px"),transform:"translate3d(".concat(O.scrollLeft,"px, 0, 0)")}}))});var eE="rc-table",ek=[],eS={};function eN(){return"No Data"}var e$=t.forwardRef(function(e,n){var d,c=(0,w.default)({rowKey:"key",prefixCls:eE,emptyText:eN},e),u=c.prefixCls,f=c.className,p=c.rowClassName,m=c.style,h=c.data,g=c.rowKey,v=c.scroll,y=c.tableLayout,N=c.direction,$=c.title,O=c.footer,R=c.summary,I=c.caption,T=c.id,D=c.showHeader,W=c.components,F=c.emptyText,_=c.onRow,V=c.onHeaderRow,U=c.measureRowRender,X=c.onScroll,G=c.internalHooks,Y=c.transformColumns,J=c.internalRefs,ee=c.tailor,et=c.getContainerWidth,el=c.sticky,eo=c.rowHoverable,ei=void 0===eo||eo,ec=h||ek,eu=!!ec.length,es=G===r,ef=t.useCallback(function(e,t){return(0,S.default)(W,e)||t},[W]),ep=t.useMemo(function(){return"function"==typeof g?g:function(e){return e&&e[g]}},[g]),em=ef(["body"]),ey=(tX=t.useState(-1),tY=(tG=(0,l.default)(tX,2))[0],tJ=tG[1],tQ=t.useState(-1),t0=(tZ=(0,l.default)(tQ,2))[0],t1=tZ[1],[tY,t0,t.useCallback(function(e,t){tJ(e),t1(t)},[])]),eb=(0,l.default)(ey,3),ew=eb[0],e$=eb[1],eK=eb[2],eO=(t6=(t3=c.expandable,t4=(0,M.default)(c,Z),!1===(t2="expandable"in c?(0,w.default)((0,w.default)({},t4),t3):t4).showExpandColumn&&(t2.expandIconColumnIndex=-1),t8=t2).expandIcon,t5=t8.expandedRowKeys,t7=t8.defaultExpandedRowKeys,t9=t8.defaultExpandAllRows,ne=t8.expandedRowRender,nt=t8.onExpand,nn=t8.onExpandedRowsChange,nr=t8.childrenColumnName||"children",nl=t.useMemo(function(){return ne?"row":!!(c.expandable&&c.internalHooks===r&&c.expandable.__PARENT_RENDER_ICON__||ec.some(function(e){return e&&"object"===(0,x.default)(e)&&e[nr]}))&&"nest"},[!!ne,ec]),no=t.useState(function(){if(t7)return t7;if(t9){var e;return e=[],!function t(n){(n||[]).forEach(function(n,r){e.push(ep(n,r)),t(n[nr])})}(ec),e}return[]}),ni=(na=(0,l.default)(no,2))[0],nd=na[1],nc=t.useMemo(function(){return new Set(t5||ni||[])},[t5,ni]),nu=t.useCallback(function(e){var t,n=ep(e,ec.indexOf(e)),r=nc.has(n);r?(nc.delete(n),t=(0,er.default)(nc)):t=[].concat((0,er.default)(nc),[n]),nd(t),nt&&nt(!r,e),nn&&nn(t)},[ep,nc,ec,nt,nn]),[t8,nl,nc,t6||q,nr,nu]),eR=(0,l.default)(eO,6),eI=eR[0],eP=eR[1],eT=eR[2],eM=eR[3],eD=eR[4],eL=eR[5],ej=null==v?void 0:v.x,eB=t.useState(0),eH=(0,l.default)(eB,2),eA=eH[0],ez=eH[1],eW=eh((0,w.default)((0,w.default)((0,w.default)({},c),eI),{},{expandable:!!eI.expandedRowRender,columnTitle:eI.columnTitle,expandedKeys:eT,getRowKey:ep,onTriggerExpand:eL,expandIcon:eM,expandIconColumnIndex:eI.expandIconColumnIndex,direction:N,scrollWidth:es&&ee&&"number"==typeof ej?ej:null,clientWidth:eA}),es?Y:null),eF=(0,l.default)(eW,4),e_=eF[0],eq=eF[1],eV=eF[2],eU=eF[3],eX=null!=eV?eV:ej,eG=t.useMemo(function(){return{columns:e_,flattenColumns:eq}},[e_,eq]),eY=t.useRef(),eJ=t.useRef(),eQ=t.useRef(),eZ=t.useRef();t.useImperativeHandle(n,function(){return{nativeElement:eY.current,scrollTo:function(e){var t;if(eQ.current instanceof HTMLElement){var n=e.index,r=e.top,l=e.key;if("number"!=typeof r||Number.isNaN(r)){var o,a,i=null!=l?l:ep(ec[n]);null==(a=eQ.current.querySelector('[data-row-key="'.concat(i,'"]')))||a.scrollIntoView()}else null==(o=eQ.current)||o.scrollTo({top:r})}else null!=(t=eQ.current)&&t.scrollTo&&eQ.current.scrollTo(e)}}});var e0=t.useRef(),e1=t.useState(!1),e2=(0,l.default)(e1,2),e3=e2[0],e4=e2[1],e8=t.useState(!1),e6=(0,l.default)(e8,2),e5=e6[0],e7=e6[1],e9=t.useState(new Map),te=(0,l.default)(e9,2),tt=te[0],tn=te[1],tr=K(eq).map(function(e){return tt.get(e)}),tl=t.useMemo(function(){return tr},[tr.join("_")]),to=(0,t.useMemo)(function(){var e=eq.length,t=function(e,t,n){for(var r=[],l=0,o=e;o!==t;o+=n)r.push(l),eq[o].fixed&&(l+=tl[o]||0);return r},n=t(0,e,1),r=t(e-1,-1,-1).reverse();return"rtl"===N?{left:r,right:n}:{left:n,right:r}},[tl,eq,N]),ta=v&&null!=v.y,ti=v&&null!=eX||!!eI.fixed,td=ti&&eq.some(function(e){return e.fixed}),tc=t.useRef(),tu=(np=void 0===(nf=(ns="object"===(0,x.default)(el)?el:{}).offsetHeader)?0:nf,nh=void 0===(nm=ns.offsetSummary)?0:nm,nv=void 0===(ng=ns.offsetScroll)?0:ng,nb=(void 0===(ny=ns.getContainer)?function(){return eg}:ny)()||eg,nx=!!el,t.useMemo(function(){return{isSticky:nx,stickyClassName:nx?"".concat(u,"-sticky-holder"):"",offsetHeader:np,offsetSummary:nh,offsetScroll:nv,container:nb}},[nx,nv,np,nh,u,nb])),ts=tu.isSticky,tf=tu.offsetHeader,tp=tu.offsetSummary,tm=tu.offsetScroll,th=tu.stickyClassName,tg=tu.container,tv=t.useMemo(function(){return null==R?void 0:R(ec)},[R,ec]),ty=(ta||ts)&&t.isValidElement(tv)&&tv.type===L&&tv.props.fixed;ta&&(nC={overflowY:eu?"scroll":"auto",maxHeight:v.y}),ti&&(nw={overflowX:"auto"},ta||(nC={overflowY:"hidden"}),nE={width:!0===eX?"auto":eX,minWidth:"100%"});var tb=t.useCallback(function(e,t){tn(function(n){if(n.get(e)!==t){var r=new Map(n);return r.set(e,t),r}return n})},[]),tx=function(){var e=(0,t.useRef)(null),n=(0,t.useRef)();function r(){window.clearTimeout(n.current)}return(0,t.useEffect)(function(){return r},[]),[function(t){e.current=t,r(),n.current=window.setTimeout(function(){e.current=null,n.current=void 0},100)},function(){return e.current}]}(),tw=(0,l.default)(tx,2),tC=tw[0],tE=tw[1];function tk(e,t){t&&("function"==typeof t?t(e):t.scrollLeft!==e&&(t.scrollLeft=e,t.scrollLeft!==e&&setTimeout(function(){t.scrollLeft=e},0)))}var tS=(0,o.default)(function(e){var t,n=e.currentTarget,r=e.scrollLeft,l="rtl"===N,o="number"==typeof r?r:n.scrollLeft,a=n||eS;tE()&&tE()!==a||(tC(a),tk(o,eJ.current),tk(o,eQ.current),tk(o,e0.current),tk(o,null==(t=tc.current)?void 0:t.setScrollLeft));var i=n||eJ.current;if(i){var d=es&&ee&&"number"==typeof eX?eX:i.scrollWidth,c=i.clientWidth;if(d===c){e4(!1),e7(!1);return}l?(e4(-o0)):(e4(o>0),e7(o1?x-D:0,pointerEvents:"auto"}),j=t.useMemo(function(){return h?M<=1:0===P||0===M||M>1},[M,P,h]);j?L.visibility="hidden":h&&(L.height=null==g?void 0:g(M));var B={};return(0===M||0===P)&&(B.rowSpan=1,B.colSpan=1),t.createElement(I,(0,s.default)({className:(0,E.default)(b,m),ellipsis:l.ellipsis,align:l.align,scope:l.rowScope,component:d,prefixCls:r.prefixCls,key:S,record:f,index:i,renderIndex:c,dataIndex:y,render:j?function(){return null}:v,shouldCellUpdate:l.shouldCellUpdate},N,{appendNode:$,additionalProps:(0,w.default)((0,w.default)({},K),{},{style:L},B)}))};var eT=["data","index","className","rowKey","style","extra","getHeight"],eM=v(t.forwardRef(function(e,n){var r,l=e.data,o=e.index,a=e.className,i=e.rowKey,d=e.style,c=e.extra,f=e.getHeight,p=(0,M.default)(e,eT),m=l.record,h=l.indent,g=l.index,v=u(b,["prefixCls","flattenColumns","fixColumn","componentWidth","scrollX"]),y=v.scrollX,x=v.flattenColumns,k=v.prefixCls,S=v.fixColumn,N=v.componentWidth,$=u(eR,["getComponent"]).getComponent,K=F(m,i,o,h),O=$(["body","row"],"div"),R=$(["body","cell"],"div"),P=K.rowSupportExpand,T=K.expanded,D=K.rowProps,L=K.expandedRowRender,j=K.expandedRowClassName;if(P&&T){var B=L(m,o,h+1,T),H=V(j,m,o,h),A={};S&&(A={style:(0,C.default)({},"--virtual-width","".concat(N,"px"))});var z="".concat(k,"-expanded-row-cell");r=t.createElement(O,{className:(0,E.default)("".concat(k,"-expanded-row"),"".concat(k,"-expanded-row-level-").concat(h+1),H)},t.createElement(I,{component:R,prefixCls:k,className:(0,E.default)(z,(0,C.default)({},"".concat(z,"-fixed"),S)),additionalProps:A},B))}var W=(0,w.default)((0,w.default)({},d),{},{width:y});c&&(W.position="absolute",W.pointerEvents="none");var _=t.createElement(O,(0,s.default)({},D,p,{"data-row-key":i,ref:P?null:n,className:(0,E.default)(a,"".concat(k,"-row"),null==D?void 0:D.className,(0,C.default)({},"".concat(k,"-row-extra"),c)),style:(0,w.default)((0,w.default)({},W),null==D?void 0:D.style)}),x.map(function(e,n){return t.createElement(eP,{key:n,component:R,rowInfo:K,column:e,colIndex:n,indent:h,index:o,renderIndex:g,record:m,inverse:c,getHeight:f})}));return P?t.createElement("div",{ref:n},_,r):_})),eD=v(t.forwardRef(function(e,n){var r=e.data,o=e.onScroll,a=u(b,["flattenColumns","onColumnResize","getRowKey","prefixCls","expandedKeys","childrenColumnName","scrollX","direction"]),i=a.flattenColumns,d=a.onColumnResize,c=a.getRowKey,s=a.expandedKeys,f=a.prefixCls,p=a.childrenColumnName,m=a.scrollX,h=a.direction,g=u(eR),v=g.sticky,y=g.scrollY,w=g.listItemHeight,C=g.getComponent,E=g.onScroll,k=t.useRef(),S=W(r,p,s,c),N=t.useMemo(function(){var e=0;return i.map(function(t){var n=t.width,r=t.minWidth,l=t.key,o=Math.max(n||0,r||0);return e+=o,[l,o,e]})},[i]),$=t.useMemo(function(){return N.map(function(e){return e[2]})},[N]);t.useEffect(function(){N.forEach(function(e){var t=(0,l.default)(e,2);d(t[0],t[1])})},[N]),t.useImperativeHandle(n,function(){var e,t={scrollTo:function(e){var t;null==(t=k.current)||t.scrollTo(e)},nativeElement:null==(e=k.current)?void 0:e.nativeElement};return Object.defineProperty(t,"scrollLeft",{get:function(){var e;return(null==(e=k.current)?void 0:e.getScrollInfo().x)||0},set:function(e){var t;null==(t=k.current)||t.scrollTo({left:e})}}),Object.defineProperty(t,"scrollTop",{get:function(){var e;return(null==(e=k.current)?void 0:e.getScrollInfo().y)||0},set:function(e){var t;null==(t=k.current)||t.scrollTo({top:e})}}),t});var K=function(e,t){var n=null==(l=S[t])?void 0:l.record,r=e.onCell;if(r){var l,o,a=r(n,t);return null!=(o=null==a?void 0:a.rowSpan)?o:1}return 1},O=t.useMemo(function(){return{columnsOffset:$}},[$]),R="".concat(f,"-tbody"),I=C(["body","wrapper"]),P={};return v&&(P.position="sticky",P.bottom=0,"object"===(0,x.default)(v)&&v.offsetScroll&&(P.bottom=v.offsetScroll)),t.createElement(eI.Provider,{value:O},t.createElement(eO.default,{fullHeight:!1,ref:k,prefixCls:"".concat(R,"-virtual"),styles:{horizontalScrollBar:P},className:R,height:y,itemHeight:w||24,data:S,itemKey:function(e){return c(e.record)},component:I,scrollWidth:m,direction:h,onVirtualScroll:function(e){var t,n=e.x;o({currentTarget:null==(t=k.current)?void 0:t.nativeElement,scrollLeft:n})},onScroll:E,extraRender:function(e){var n=e.start,r=e.end,l=e.getSize,o=e.offsetY;if(r<0)return null;for(var a=i.filter(function(e){return 0===K(e,n)}),d=n,u=function(e){if(!(a=a.filter(function(t){return 0===K(t,e)})).length)return d=e,1},s=n;s>=0&&!u(s);s-=1);for(var f=i.filter(function(e){return 1!==K(e,r)}),p=r,m=function(e){if(!(f=f.filter(function(t){return 1!==K(t,e)})).length)return p=Math.max(e-1,r),1},h=r;h1})&&g.push(e)},y=d;y<=p;y+=1)if(v(y))continue;return g.map(function(e){var n=S[e],r=c(n.record,e),a=l(r);return t.createElement(eM,{key:e,data:n,rowKey:r,index:e,style:{top:-o+a.top},extra:!0,getHeight:function(t){var n=e+t-1,o=l(r,c(S[n].record,n));return o.bottom-o.top}})})}},function(e,n,r){var l=c(e.record,n);return t.createElement(eM,{data:e,rowKey:l,index:n,style:r.style})}))})),eL=function(e,n){var r=n.ref,l=n.onScroll;return t.createElement(eD,{ref:r,data:e,onScroll:l})},ej=t.forwardRef(function(e,n){var l=e.data,o=e.columns,a=e.scroll,i=e.sticky,d=e.prefixCls,c=void 0===d?eE:d,u=e.className,f=e.listItemHeight,p=e.components,m=e.onScroll,h=a||{},g=h.x,v=h.y;"number"!=typeof g&&(g=1),"number"!=typeof v&&(v=500);var y=(0,O.useEvent)(function(e,t){return(0,S.default)(p,e)||t}),b=(0,O.useEvent)(m),x=t.useMemo(function(){return{sticky:i,scrollY:v,listItemHeight:f,getComponent:y,onScroll:b}},[i,v,f,y,b]);return t.createElement(eR.Provider,{value:x},t.createElement(eK,(0,s.default)({},e,{className:(0,E.default)(u,"".concat(c,"-virtual")),scroll:(0,w.default)((0,w.default)({},a),{},{x:g}),components:(0,w.default)((0,w.default)({},p),{},{body:null!=l&&l.length?eL:void 0}),columns:o,internalHooks:r,tailor:!0,ref:n})))});g(ej,void 0);var eB=e.i(247153),eH=t.createContext(null),eA=t.createContext({});let ez=t.memo(function(e){for(var n=e.prefixCls,r=e.level,l=e.isStart,o=e.isEnd,a="".concat(n,"-indent-unit"),i=[],d=0;d1&&void 0!==arguments[1]?arguments[1]:null;return n.map(function(c,u){for(var s,f=e_(r?r.pos:"0",u),p=eq(c[o],f),m=0;m1&&void 0!==arguments[1]?arguments[1]:{},f=s.initWrapper,p=s.processEntity,m=s.onProcessFinished,h=s.externalGetKey,g=s.childrenPropName,v=s.fieldNames,y=arguments.length>2?arguments[2]:void 0,b={},w={},C={posEntities:b,keyEntities:w};return f&&(C=f(C)||C),t=function(e){var t=e.node,n=e.index,r=e.pos,l=e.key,o=e.parentPos,a=e.level,i={node:t,nodes:e.nodes,index:n,key:l,pos:r,level:a},d=eq(l,r);b[r]=i,w[d]=i,i.parent=b[o],i.parent&&(i.parent.children=i.parent.children||[],i.parent.children.push(i)),p&&p(i,C)},n={externalGetKey:h||y,childrenPropName:g,fieldNames:v},o=(l=("object"===(0,x.default)(n)?n:{externalGetKey:n})||{}).childrenPropName,a=l.externalGetKey,d=(i=eV(l.fieldNames)).key,c=i.children,u=o||c,a?"string"==typeof a?r=function(e){return e[a]}:"function"==typeof a&&(r=function(e){return a(e)}):r=function(e,t){return eq(e[d],t)},function n(l,o,a,i){var d=l?l[u]:e,c=l?e_(a.pos,o):"0",s=l?[].concat((0,er.default)(i),[l]):[];if(l){var f=r(l,c);t({node:l,index:o,pos:c,key:f,parentPos:a.node?a.pos:null,level:a.level+1,nodes:s})}d&&d.forEach(function(e,t){n(e,t,{node:l,pos:c,level:a?a.level+1:-1},s)})}(null),m&&m(C),C}function eY(e,t){var n=t.expandedKeys,r=t.selectedKeys,l=t.loadedKeys,o=t.loadingKeys,a=t.checkedKeys,i=t.halfCheckedKeys,d=t.dragOverNodeKey,c=t.dropPosition,u=t.keyEntities[e];return{eventKey:e,expanded:-1!==n.indexOf(e),selected:-1!==r.indexOf(e),loaded:-1!==l.indexOf(e),loading:-1!==o.indexOf(e),checked:-1!==a.indexOf(e),halfChecked:-1!==i.indexOf(e),pos:String(u?u.pos:""),dragOver:d===e&&0===c,dragOverGapTop:d===e&&-1===c,dragOverGapBottom:d===e&&1===c}}function eJ(e){var t=e.data,n=e.expanded,r=e.selected,l=e.checked,o=e.loaded,a=e.loading,i=e.halfChecked,d=e.dragOver,c=e.dragOverGapTop,u=e.dragOverGapBottom,s=e.pos,f=e.active,p=e.eventKey,m=(0,w.default)((0,w.default)({},t),{},{expanded:n,selected:r,checked:l,loaded:o,loading:a,halfChecked:i,dragOver:d,dragOverGapTop:c,dragOverGapBottom:u,pos:s,active:f,key:p});return"props"in m||Object.defineProperty(m,"props",{get:function(){return(0,N.default)(!1,"Second param return from event is node data instead of TreeNode instance. Please read value directly instead of reading from `props`."),e}}),m}var eQ=["eventKey","className","style","dragOver","dragOverGapTop","dragOverGapBottom","isLeaf","isStart","isEnd","expanded","selected","checked","halfChecked","loading","domRef","active","data","onMouseMove","selectable"],eZ="open",e0="close",e1=function(e){var n,r,o,a=e.eventKey,i=e.className,d=e.style,c=e.dragOver,u=e.dragOverGapTop,f=e.dragOverGapBottom,p=e.isLeaf,m=e.isStart,h=e.isEnd,g=e.expanded,v=e.selected,y=e.checked,b=e.halfChecked,x=e.loading,k=e.domRef,S=e.active,N=e.data,$=e.onMouseMove,K=e.selectable,O=(0,M.default)(e,eQ),R=t.default.useContext(eH),I=t.default.useContext(eA),P=t.default.useRef(null),T=t.default.useState(!1),D=(0,l.default)(T,2),L=D[0],j=D[1],B=!!(R.disabled||e.disabled||null!=(n=I.nodeDisabled)&&n.call(I,N)),H=t.default.useMemo(function(){return!!R.checkable&&!1!==e.checkable&&R.checkable},[R.checkable,e.checkable]),A=function(t){B||R.onNodeSelect(t,eJ(e))},W=function(t){B||H&&!e.disableCheckbox&&R.onNodeCheck(t,eJ(e),!y)},F=t.default.useMemo(function(){return"boolean"==typeof K?K:R.selectable},[K,R.selectable]),_=function(t){R.onNodeClick(t,eJ(e)),F?A(t):W(t)},q=function(t){R.onNodeDoubleClick(t,eJ(e))},V=function(t){R.onNodeMouseEnter(t,eJ(e))},U=function(t){R.onNodeMouseLeave(t,eJ(e))},X=function(t){R.onNodeContextMenu(t,eJ(e))},G=t.default.useMemo(function(){return!!(R.draggable&&(!R.draggable.nodeDraggable||R.draggable.nodeDraggable(N)))},[R.draggable,N]),Y=function(t){x||R.onNodeExpand(t,eJ(e))},J=t.default.useMemo(function(){return!!((R.keyEntities[a]||{}).children||[]).length},[R.keyEntities,a]),Q=t.default.useMemo(function(){return!1!==p&&(p||!R.loadData&&!J||R.loadData&&e.loaded&&!J)},[p,R.loadData,J,e.loaded]);t.default.useEffect(function(){!x&&("function"!=typeof R.loadData||!g||Q||e.loaded||R.onNodeLoad(eJ(e)))},[x,R.loadData,R.onNodeLoad,g,Q,e]);var Z=t.default.useMemo(function(){var e;return null!=(e=R.draggable)&&e.icon?t.default.createElement("span",{className:"".concat(R.prefixCls,"-draggable-icon")},R.draggable.icon):null},[R.draggable]),ee=function(t){var n=e.switcherIcon||R.switcherIcon;return"function"==typeof n?n((0,w.default)((0,w.default)({},e),{},{isLeaf:t})):n},et=t.default.useMemo(function(){if(!H)return null;var n="boolean"!=typeof H?H:null;return t.default.createElement("span",{className:(0,E.default)("".concat(R.prefixCls,"-checkbox"),(0,C.default)((0,C.default)((0,C.default)({},"".concat(R.prefixCls,"-checkbox-checked"),y),"".concat(R.prefixCls,"-checkbox-indeterminate"),!y&&b),"".concat(R.prefixCls,"-checkbox-disabled"),B||e.disableCheckbox)),onClick:W,role:"checkbox","aria-checked":b?"mixed":y,"aria-disabled":B||e.disableCheckbox,"aria-label":"Select ".concat("string"==typeof e.title?e.title:"tree node")},n)},[H,y,b,B,e.disableCheckbox,e.title]),en=t.default.useMemo(function(){return Q?null:g?eZ:e0},[Q,g]),er=t.default.useMemo(function(){return t.default.createElement("span",{className:(0,E.default)("".concat(R.prefixCls,"-iconEle"),"".concat(R.prefixCls,"-icon__").concat(en||"docu"),(0,C.default)({},"".concat(R.prefixCls,"-icon_loading"),x))})},[R.prefixCls,en,x]),el=t.default.useMemo(function(){var t=!!R.draggable;return!e.disabled&&t&&R.dragOverNodeKey===a?R.dropIndicatorRender({dropPosition:R.dropPosition,dropLevelOffset:R.dropLevelOffset,indent:R.indent,prefixCls:R.prefixCls,direction:R.direction}):null},[R.dropPosition,R.dropLevelOffset,R.indent,R.prefixCls,R.direction,R.draggable,R.dragOverNodeKey,R.dropIndicatorRender]),eo=t.default.useMemo(function(){var n,r,l=e.title,o=void 0===l?"---":l,a="".concat(R.prefixCls,"-node-content-wrapper");if(R.showIcon){var i=e.icon||R.icon;n=i?t.default.createElement("span",{className:(0,E.default)("".concat(R.prefixCls,"-iconEle"),"".concat(R.prefixCls,"-icon__customize"))},"function"==typeof i?i(e):i):er}else R.loadData&&x&&(n=er);return r="function"==typeof o?o(N):R.titleRender?R.titleRender(N):o,t.default.createElement("span",{ref:P,title:"string"==typeof o?o:"",className:(0,E.default)(a,"".concat(a,"-").concat(en||"normal"),(0,C.default)({},"".concat(R.prefixCls,"-node-selected"),!B&&(v||L))),onMouseEnter:V,onMouseLeave:U,onContextMenu:X,onClick:_,onDoubleClick:q},n,t.default.createElement("span",{className:"".concat(R.prefixCls,"-title")},r),el)},[R.prefixCls,R.showIcon,e,R.icon,er,R.titleRender,N,en,V,U,X,_,q]),ea=(0,z.default)(O,{aria:!0,data:!0}),ei=(R.keyEntities[a]||{}).level,ed=h[h.length-1],ec=!B&&G,eu=R.draggingNodeKey===a;return t.default.createElement("div",(0,s.default)({ref:k,role:"treeitem","aria-expanded":p?void 0:g,className:(0,E.default)(i,"".concat(R.prefixCls,"-treenode"),(o={},(0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)(o,"".concat(R.prefixCls,"-treenode-disabled"),B),"".concat(R.prefixCls,"-treenode-switcher-").concat(g?"open":"close"),!p),"".concat(R.prefixCls,"-treenode-checkbox-checked"),y),"".concat(R.prefixCls,"-treenode-checkbox-indeterminate"),b),"".concat(R.prefixCls,"-treenode-selected"),v),"".concat(R.prefixCls,"-treenode-loading"),x),"".concat(R.prefixCls,"-treenode-active"),S),"".concat(R.prefixCls,"-treenode-leaf-last"),ed),"".concat(R.prefixCls,"-treenode-draggable"),G),"dragging",eu),(0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)((0,C.default)(o,"drop-target",R.dropTargetKey===a),"drop-container",R.dropContainerKey===a),"drag-over",!B&&c),"drag-over-gap-top",!B&&u),"drag-over-gap-bottom",!B&&f),"filter-node",null==(r=R.filterTreeNode)?void 0:r.call(R,eJ(e))),"".concat(R.prefixCls,"-treenode-leaf"),Q))),style:d,draggable:ec,onDragStart:ec?function(t){t.stopPropagation(),j(!0),R.onNodeDragStart(t,e);try{t.dataTransfer.setData("text/plain","")}catch(e){}}:void 0,onDragEnter:G?function(t){t.preventDefault(),t.stopPropagation(),R.onNodeDragEnter(t,e)}:void 0,onDragOver:G?function(t){t.preventDefault(),t.stopPropagation(),R.onNodeDragOver(t,e)}:void 0,onDragLeave:G?function(t){t.stopPropagation(),R.onNodeDragLeave(t,e)}:void 0,onDrop:G?function(t){t.preventDefault(),t.stopPropagation(),j(!1),R.onNodeDrop(t,e)}:void 0,onDragEnd:G?function(t){t.stopPropagation(),j(!1),R.onNodeDragEnd(t,e)}:void 0,onMouseMove:$},void 0!==K?{"aria-selected":!!K}:void 0,ea),t.default.createElement(ez,{prefixCls:R.prefixCls,level:ei,isStart:m,isEnd:h}),Z,function(){if(Q){var e=ee(!0);return!1!==e?t.default.createElement("span",{className:(0,E.default)("".concat(R.prefixCls,"-switcher"),"".concat(R.prefixCls,"-switcher-noop"))},e):null}var n=ee(!1);return!1!==n?t.default.createElement("span",{onClick:Y,className:(0,E.default)("".concat(R.prefixCls,"-switcher"),"".concat(R.prefixCls,"-switcher_").concat(g?eZ:e0))},n):null}(),et,eo)};function e2(e,t){if(!e)return[];var n=e.slice(),r=n.indexOf(t);return r>=0&&n.splice(r,1),n}function e3(e,t){var n=(e||[]).slice();return -1===n.indexOf(t)&&n.push(t),n}function e4(e){return e.split("-")}function e8(e,t,n,r,l,o,a,i,d,c){var u,s,f=e.clientX,p=e.clientY,m=e.target.getBoundingClientRect(),h=m.top,g=m.height,v=(("rtl"===c?-1:1)*(((null==l?void 0:l.x)||0)-f)-12)/r,y=d.filter(function(e){var t;return null==(t=i[e])||null==(t=t.children)?void 0:t.length}),b=i[n.eventKey];if(p-1.5?o({dragNode:$,dropNode:K,dropPosition:1})?k=1:O=!1:o({dragNode:$,dropNode:K,dropPosition:0})?k=0:o({dragNode:$,dropNode:K,dropPosition:1})?k=1:O=!1:o({dragNode:$,dropNode:K,dropPosition:1})?k=1:O=!1,{dropPosition:k,dropLevelOffset:S,dropTargetKey:b.key,dropTargetPos:b.pos,dragOverNodeKey:E,dropContainerKey:0===k?null:(null==(s=b.parent)?void 0:s.key)||null,dropAllowed:O}}function e6(e,t){if(e)return t.multiple?e.slice():e.length?[e[0]]:e}function e5(e){var t;if(!e)return null;if(Array.isArray(e))t={checkedKeys:e,halfCheckedKeys:void 0};else{if("object"!==(0,x.default)(e))return(0,N.default)(!1,"`checkedKeys` is not an array or an object"),null;t={checkedKeys:e.checked||void 0,halfCheckedKeys:e.halfChecked||void 0}}return t}function e7(e,t){var n=new Set;return(e||[]).forEach(function(e){!function e(r){if(!n.has(r)){var l=t[r];if(l){n.add(r);var o=l.parent;!l.node.disabled&&o&&e(o.key)}}}(e)}),(0,er.default)(n)}function e9(e,t){var n=new Set;return e.forEach(function(e){t.has(e)||n.add(e)}),n}function te(e){var t=e||{},n=t.disabled,r=t.disableCheckbox,l=t.checkable;return!!(n||r)||!1===l}function tt(e,t,n,r){var l,o=[];l=r||te;var a=new Set(e.filter(function(e){var t=!!n[e];return t||o.push(e),t})),i=new Map,d=0;return Object.keys(n).forEach(function(e){var t=n[e],r=t.level,l=i.get(r);l||(l=new Set,i.set(r,l)),l.add(t),d=Math.max(d,r)}),(0,N.default)(!o.length,"Tree missing follow keys: ".concat(o.slice(0,100).map(function(e){return"'".concat(e,"'")}).join(", "))),!0===t?function(e,t,n,r){for(var l=new Set(e),o=new Set,a=0;a<=n;a+=1)(t.get(a)||new Set).forEach(function(e){var t=e.key,n=e.node,o=e.children,a=void 0===o?[]:o;l.has(t)&&!r(n)&&a.filter(function(e){return!r(e.node)}).forEach(function(e){l.add(e.key)})});for(var i=new Set,d=n;d>=0;d-=1)(t.get(d)||new Set).forEach(function(e){var t=e.parent;if(!(r(e.node)||!e.parent||i.has(e.parent.key))){if(r(e.parent.node))return void i.add(t.key);var n=!0,a=!1;(t.children||[]).filter(function(e){return!r(e.node)}).forEach(function(e){var t=e.key,r=l.has(t);n&&!r&&(n=!1),!a&&(r||o.has(t))&&(a=!0)}),n&&l.add(t.key),a&&o.add(t.key),i.add(t.key)}});return{checkedKeys:Array.from(l),halfCheckedKeys:Array.from(e9(o,l))}}(a,i,d,l):function(e,t,n,r,l){for(var o=new Set(e),a=new Set(t),i=0;i<=r;i+=1)(n.get(i)||new Set).forEach(function(e){var t=e.key,n=e.node,r=e.children,i=void 0===r?[]:r;o.has(t)||a.has(t)||l(n)||i.filter(function(e){return!l(e.node)}).forEach(function(e){o.delete(e.key)})});a=new Set;for(var d=new Set,c=r;c>=0;c-=1)(n.get(c)||new Set).forEach(function(e){var t=e.parent;if(!(l(e.node)||!e.parent||d.has(e.parent.key))){if(l(e.parent.node))return void d.add(t.key);var n=!0,r=!1;(t.children||[]).filter(function(e){return!l(e.node)}).forEach(function(e){var t=e.key,l=o.has(t);n&&!l&&(n=!1),!r&&(l||a.has(t))&&(r=!0)}),n||o.delete(t.key),r&&a.add(t.key),d.add(t.key)}});return{checkedKeys:Array.from(o),halfCheckedKeys:Array.from(e9(a,o))}}(a,t.halfCheckedKeys,i,d,l)}e1.isTreeNode=1;var tn=e.i(914949),tr=e.i(747656),tl=e.i(374276),to=e.i(21539),ta=e.i(544195);let ti={},td="SELECT_ALL",tc="SELECT_INVERT",tu="SELECT_NONE",ts=[],tf=(e,t,n=[])=>((t||[]).forEach(t=>{n.push(t),t&&"object"==typeof t&&e in t&&tf(e,t[e],n)}),n);function tp(e){return null!=e&&e===e.window}var tm=e.i(609587),th=e.i(242064),tg=e.i(721132),tv=e.i(321883),ty=e.i(517455),tb=e.i(150073),tx=e.i(87414),tw=e.i(165370),tC=e.i(244451),tE=e.i(104458);let tk=(e,t)=>"key"in e&&void 0!==e.key&&null!==e.key?e.key:e.dataIndex?Array.isArray(e.dataIndex)?e.dataIndex.join("."):e.dataIndex:t;function tS(e,t){return t?`${t}-${e}`:`${e}`}let tN=(e,t)=>"function"==typeof e?e(t):e,t$={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M349 838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V642H349v196zm531.1-684H143.9c-24.5 0-39.8 26.7-27.5 48l221.3 376h348.8l221.3-376c12.1-21.3-3.2-48-27.7-48z"}}]},name:"filter",theme:"filled"};var tK=e.i(9583),tO=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:t$}))}),tR=e.i(887719),tI=e.i(149809),tP=e.i(920228),tT=e.i(616303),tM=e.i(60699),tD=e.i(652199),tL=e.i(278409),tj=e.i(233848),tB=e.i(971151),tH=e.i(868917),tA=e.i(674813),tz=e.i(404948);function tW(e){if(null==e)throw TypeError("Cannot destructure "+e)}var tF=e.i(361275);let t_=function(e,n){var r=t.useState(!1),o=(0,l.default)(r,2),i=o[0],d=o[1];(0,a.default)(function(){if(i)return e(),function(){n()}},[i]),(0,a.default)(function(){return d(!0),function(){d(!1)}},[])};var tq=["className","style","motion","motionNodes","motionType","onMotionStart","onMotionEnd","active","treeNodeRequiredProps"],tV=t.forwardRef(function(e,n){var r=e.className,o=e.style,i=e.motion,d=e.motionNodes,c=e.motionType,u=e.onMotionStart,f=e.onMotionEnd,p=e.active,m=e.treeNodeRequiredProps,h=(0,M.default)(e,tq),g=t.useState(!0),v=(0,l.default)(g,2),y=v[0],b=v[1],x=t.useContext(eH).prefixCls,w=d&&"hide"!==c;(0,a.default)(function(){d&&w!==y&&b(w)},[d]);var C=t.useRef(!1),k=function(){d&&!C.current&&(C.current=!0,f())};return(t_(function(){d&&u()},k),d)?t.createElement(tF.default,(0,s.default)({ref:n,visible:y},i,{motionAppear:"show"===c,onVisibleChanged:function(e){w===e&&k()}}),function(e,n){var r=e.className,l=e.style;return t.createElement("div",{ref:n,className:(0,E.default)("".concat(x,"-treenode-motion"),r),style:l},d.map(function(e){var n=Object.assign({},(tW(e.data),e.data)),r=e.title,l=e.key,o=e.isStart,a=e.isEnd;delete n.children;var i=eY(l,m);return t.createElement(e1,(0,s.default)({},n,i,{title:r,active:p,data:e.data,key:l,isStart:o,isEnd:a}))}))}):t.createElement(e1,(0,s.default)({domRef:n,className:r,style:o},h,{active:p}))});function tU(e,t,n){var r=e.findIndex(function(e){return e.key===n}),l=e[r+1],o=t.findIndex(function(e){return e.key===n});if(l){var a=t.findIndex(function(e){return e.key===l.key});return t.slice(o+1,a)}return t.slice(o+1)}var tX=["prefixCls","data","selectable","checkable","expandedKeys","selectedKeys","checkedKeys","loadedKeys","loadingKeys","halfCheckedKeys","keyEntities","disabled","dragging","dragOverNodeKey","dropPosition","motion","height","itemHeight","virtual","scrollWidth","focusable","activeItem","focused","tabIndex","onKeyDown","onFocus","onBlur","onActiveChange","onListChangeStart","onListChangeEnd"],tG={width:0,height:0,display:"flex",overflow:"hidden",opacity:0,border:0,padding:0,margin:0},tY=function(){},tJ="RC_TREE_MOTION_".concat(Math.random()),tQ={key:tJ},tZ={key:tJ,level:0,index:0,pos:"0",node:tQ,nodes:[tQ]},t0={parent:null,children:[],pos:tZ.pos,data:tQ,title:null,key:tJ,isStart:[],isEnd:[]};function t1(e,t,n,r){return!1!==t&&n?e.slice(0,Math.ceil(n/r)+1):e}function t2(e){return eq(e.key,e.pos)}var t3=t.forwardRef(function(e,n){var r=e.prefixCls,o=e.data,i=(e.selectable,e.checkable,e.expandedKeys),d=e.selectedKeys,c=e.checkedKeys,u=e.loadedKeys,f=e.loadingKeys,p=e.halfCheckedKeys,m=e.keyEntities,h=e.disabled,g=e.dragging,v=e.dragOverNodeKey,y=e.dropPosition,b=e.motion,x=e.height,w=e.itemHeight,C=e.virtual,E=e.scrollWidth,k=e.focusable,S=e.activeItem,N=e.focused,$=e.tabIndex,K=e.onKeyDown,O=e.onFocus,R=e.onBlur,I=e.onActiveChange,P=e.onListChangeStart,T=e.onListChangeEnd,D=(0,M.default)(e,tX),L=t.useRef(null),j=t.useRef(null);t.useImperativeHandle(n,function(){return{scrollTo:function(e){L.current.scrollTo(e)},getIndentWidth:function(){return j.current.offsetWidth}}});var B=t.useState(i),H=(0,l.default)(B,2),A=H[0],z=H[1],W=t.useState(o),F=(0,l.default)(W,2),_=F[0],q=F[1],V=t.useState(o),U=(0,l.default)(V,2),X=U[0],G=U[1],Y=t.useState([]),J=(0,l.default)(Y,2),Q=J[0],Z=J[1],ee=t.useState(null),et=(0,l.default)(ee,2),en=et[0],er=et[1],el=t.useRef(o);function eo(){var e=el.current;q(e),G(e),Z([]),er(null),T()}el.current=o,(0,a.default)(function(){z(i);var e=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=e.length,r=t.length;if(1!==Math.abs(n-r))return{add:!1,key:null};function l(e,t){var n=new Map;e.forEach(function(e){n.set(e,!0)});var r=t.filter(function(e){return!n.has(e)});return 1===r.length?r[0]:null}return n ").concat(t);return t}(S)),t.createElement("div",null,t.createElement("input",{style:tG,disabled:!1===k||h,tabIndex:!1!==k?$:null,onKeyDown:K,onFocus:O,onBlur:R,value:"",onChange:tY,"aria-label":"for screen reader"})),t.createElement("div",{className:"".concat(r,"-treenode"),"aria-hidden":!0,style:{position:"absolute",pointerEvents:"none",visibility:"hidden",height:0,overflow:"hidden",border:0,padding:0}},t.createElement("div",{className:"".concat(r,"-indent")},t.createElement("div",{ref:j,className:"".concat(r,"-indent-unit")}))),t.createElement(eO.default,(0,s.default)({},D,{data:ea,itemKey:t2,height:x,fullHeight:!1,virtual:C,itemHeight:w,scrollWidth:E,prefixCls:"".concat(r,"-list"),ref:L,role:"tree",onVisibleChange:function(e){e.every(function(e){return t2(e)!==tJ})&&eo()}}),function(e){var n=e.pos,r=Object.assign({},(tW(e.data),e.data)),l=e.title,o=e.key,a=e.isStart,i=e.isEnd,d=eq(o,n);delete r.key,delete r.children;var c=eY(d,ei);return t.createElement(tV,(0,s.default)({},r,c,{title:l,active:!!S&&o===S.key,pos:n,data:e.data,isStart:a,isEnd:i,motion:b,motionNodes:o===tJ?Q:null,motionType:en,onMotionStart:P,onMotionEnd:eo,treeNodeRequiredProps:ei,onMouseMove:function(){I(null)}}))}))}),t4=function(e){(0,tH.default)(r,e);var n=(0,tA.default)(r);function r(){var e;(0,tL.default)(this,r);for(var l=arguments.length,o=Array(l),a=0;a0&&void 0!==arguments[0]?arguments[0]:[];t.forEach(function(t){var n=t.key,l=t.children;r.push(n),e(l)})}(a[d].children),r),indent:e.listRef.current.getIndentWidth()}),e.setExpandedKeys(c),window.addEventListener("dragend",e.onWindowDragEnd),null==i||i({event:t,node:eJ(n)})}),(0,C.default)((0,tB.default)(e),"onNodeDragEnter",function(t,n){var r=e.state,l=r.expandedKeys,o=r.keyEntities,a=r.dragChildrenKeys,i=r.flattenNodes,d=r.indent,c=e.props,u=c.onDragEnter,s=c.onExpand,f=c.allowDrop,p=c.direction,m=n.pos,h=n.eventKey;if(e.currentMouseOverDroppableNodeKey!==h&&(e.currentMouseOverDroppableNodeKey=h),!e.dragNodeProps)return void e.resetDragState();var g=e8(t,e.dragNodeProps,n,d,e.dragStartMousePosition,f,i,o,l,p),v=g.dropPosition,y=g.dropLevelOffset,b=g.dropTargetKey,x=g.dropContainerKey,w=g.dropTargetPos,C=g.dropAllowed,E=g.dragOverNodeKey;a.includes(b)||!C||(e.delayedDragEnterLogic||(e.delayedDragEnterLogic={}),Object.keys(e.delayedDragEnterLogic).forEach(function(t){clearTimeout(e.delayedDragEnterLogic[t])}),e.dragNodeProps.eventKey!==n.eventKey&&(t.persist(),e.delayedDragEnterLogic[m]=window.setTimeout(function(){if(null!==e.state.draggingNodeKey){var r=(0,er.default)(l),a=o[n.eventKey];a&&(a.children||[]).length&&(r=e3(l,n.eventKey)),e.props.hasOwnProperty("expandedKeys")||e.setExpandedKeys(r),null==s||s(r,{node:eJ(n),expanded:!0,nativeEvent:t.nativeEvent})}},800)),e.dragNodeProps.eventKey===b&&0===y)?e.resetDragState():(e.setState({dragOverNodeKey:E,dropPosition:v,dropLevelOffset:y,dropTargetKey:b,dropContainerKey:x,dropTargetPos:w,dropAllowed:C}),null==u||u({event:t,node:eJ(n),expandedKeys:l}))}),(0,C.default)((0,tB.default)(e),"onNodeDragOver",function(t,n){var r=e.state,l=r.dragChildrenKeys,o=r.flattenNodes,a=r.keyEntities,i=r.expandedKeys,d=r.indent,c=e.props,u=c.onDragOver,s=c.allowDrop,f=c.direction;if(e.dragNodeProps){var p=e8(t,e.dragNodeProps,n,d,e.dragStartMousePosition,s,o,a,i,f),m=p.dropPosition,h=p.dropLevelOffset,g=p.dropTargetKey,v=p.dropContainerKey,y=p.dropTargetPos,b=p.dropAllowed,x=p.dragOverNodeKey;!l.includes(g)&&b&&(e.dragNodeProps.eventKey===g&&0===h?(null!==e.state.dropPosition||null!==e.state.dropLevelOffset||null!==e.state.dropTargetKey||null!==e.state.dropContainerKey||null!==e.state.dropTargetPos||!1!==e.state.dropAllowed||null!==e.state.dragOverNodeKey)&&e.resetDragState():(m!==e.state.dropPosition||h!==e.state.dropLevelOffset||g!==e.state.dropTargetKey||v!==e.state.dropContainerKey||y!==e.state.dropTargetPos||b!==e.state.dropAllowed||x!==e.state.dragOverNodeKey)&&e.setState({dropPosition:m,dropLevelOffset:h,dropTargetKey:g,dropContainerKey:v,dropTargetPos:y,dropAllowed:b,dragOverNodeKey:x}),null==u||u({event:t,node:eJ(n)}))}}),(0,C.default)((0,tB.default)(e),"onNodeDragLeave",function(t,n){e.currentMouseOverDroppableNodeKey!==n.eventKey||t.currentTarget.contains(t.relatedTarget)||(e.resetDragState(),e.currentMouseOverDroppableNodeKey=null);var r=e.props.onDragLeave;null==r||r({event:t,node:eJ(n)})}),(0,C.default)((0,tB.default)(e),"onWindowDragEnd",function(t){e.onNodeDragEnd(t,null,!0),window.removeEventListener("dragend",e.onWindowDragEnd)}),(0,C.default)((0,tB.default)(e),"onNodeDragEnd",function(t,n){var r=e.props.onDragEnd;e.setState({dragOverNodeKey:null}),e.cleanDragState(),null==r||r({event:t,node:eJ(n)}),e.dragNodeProps=null,window.removeEventListener("dragend",e.onWindowDragEnd)}),(0,C.default)((0,tB.default)(e),"onNodeDrop",function(t,n){var r,l=arguments.length>2&&void 0!==arguments[2]&&arguments[2],o=e.state,a=o.dragChildrenKeys,i=o.dropPosition,d=o.dropTargetKey,c=o.dropTargetPos;if(o.dropAllowed){var u=e.props.onDrop;if(e.setState({dragOverNodeKey:null}),e.cleanDragState(),null!==d){var s=(0,w.default)((0,w.default)({},eY(d,e.getTreeNodeRequiredProps())),{},{active:(null==(r=e.getActiveItem())?void 0:r.key)===d,data:e.state.keyEntities[d].node}),f=a.includes(d);(0,N.default)(!f,"Can not drop to dragNode's children node. This is a bug of rc-tree. Please report an issue.");var p=e4(c),m={event:t,node:eJ(s),dragNode:e.dragNodeProps?eJ(e.dragNodeProps):null,dragNodesKeys:[e.dragNodeProps.eventKey].concat(a),dropToGap:0!==i,dropPosition:i+Number(p[p.length-1])};l||null==u||u(m),e.dragNodeProps=null}}}),(0,C.default)((0,tB.default)(e),"cleanDragState",function(){null!==e.state.draggingNodeKey&&e.setState({draggingNodeKey:null,dropPosition:null,dropContainerKey:null,dropTargetKey:null,dropLevelOffset:null,dropAllowed:!0,dragOverNodeKey:null}),e.dragStartMousePosition=null,e.currentMouseOverDroppableNodeKey=null}),(0,C.default)((0,tB.default)(e),"triggerExpandActionExpand",function(t,n){var r=e.state,l=r.expandedKeys,o=r.flattenNodes,a=n.expanded,i=n.key;if(!n.isLeaf&&!t.shiftKey&&!t.metaKey&&!t.ctrlKey){var d=o.filter(function(e){return e.key===i})[0],c=eJ((0,w.default)((0,w.default)({},eY(i,e.getTreeNodeRequiredProps())),{},{data:d.data}));e.setExpandedKeys(a?e2(l,i):e3(l,i)),e.onNodeExpand(t,c)}}),(0,C.default)((0,tB.default)(e),"onNodeClick",function(t,n){var r=e.props,l=r.onClick;"click"===r.expandAction&&e.triggerExpandActionExpand(t,n),null==l||l(t,n)}),(0,C.default)((0,tB.default)(e),"onNodeDoubleClick",function(t,n){var r=e.props,l=r.onDoubleClick;"doubleClick"===r.expandAction&&e.triggerExpandActionExpand(t,n),null==l||l(t,n)}),(0,C.default)((0,tB.default)(e),"onNodeSelect",function(t,n){var r=e.state.selectedKeys,l=e.state,o=l.keyEntities,a=l.fieldNames,i=e.props,d=i.onSelect,c=i.multiple,u=n.selected,s=n[a.key],f=!u,p=(r=f?c?e3(r,s):[s]:e2(r,s)).map(function(e){var t=o[e];return t?t.node:null}).filter(Boolean);e.setUncontrolledState({selectedKeys:r}),null==d||d(r,{event:"select",selected:f,node:n,selectedNodes:p,nativeEvent:t.nativeEvent})}),(0,C.default)((0,tB.default)(e),"onNodeCheck",function(t,n,r){var l,o=e.state,a=o.keyEntities,i=o.checkedKeys,d=o.halfCheckedKeys,c=e.props,u=c.checkStrictly,s=c.onCheck,f=n.key,p={event:"check",node:n,checked:r,nativeEvent:t.nativeEvent};if(u){var m=r?e3(i,f):e2(i,f);l={checked:m,halfChecked:e2(d,f)},p.checkedNodes=m.map(function(e){return a[e]}).filter(Boolean).map(function(e){return e.node}),e.setUncontrolledState({checkedKeys:m})}else{var h=tt([].concat((0,er.default)(i),[f]),!0,a),g=h.checkedKeys,v=h.halfCheckedKeys;if(!r){var y=new Set(g);y.delete(f);var b=tt(Array.from(y),{checked:!1,halfCheckedKeys:v},a);g=b.checkedKeys,v=b.halfCheckedKeys}l=g,p.checkedNodes=[],p.checkedNodesPositions=[],p.halfCheckedKeys=v,g.forEach(function(e){var t=a[e];if(t){var n=t.node,r=t.pos;p.checkedNodes.push(n),p.checkedNodesPositions.push({node:n,pos:r})}}),e.setUncontrolledState({checkedKeys:g},!1,{halfCheckedKeys:v})}null==s||s(l,p)}),(0,C.default)((0,tB.default)(e),"onNodeLoad",function(t){var n,r=t.key,l=e.state.keyEntities[r];if(null==l||null==(n=l.children)||!n.length){var o=new Promise(function(n,l){e.setState(function(o){var a=o.loadedKeys,i=o.loadingKeys,d=void 0===i?[]:i,c=e.props,u=c.loadData,s=c.onLoad;return!u||(void 0===a?[]:a).includes(r)||d.includes(r)?null:(u(t).then(function(){var l=e3(e.state.loadedKeys,r);null==s||s(l,{event:"load",node:t}),e.setUncontrolledState({loadedKeys:l}),e.setState(function(e){return{loadingKeys:e2(e.loadingKeys,r)}}),n()}).catch(function(t){if(e.setState(function(e){return{loadingKeys:e2(e.loadingKeys,r)}}),e.loadingRetryTimes[r]=(e.loadingRetryTimes[r]||0)+1,e.loadingRetryTimes[r]>=10){var o=e.state.loadedKeys;(0,N.default)(!1,"Retry for `loadData` many times but still failed. No more retry."),e.setUncontrolledState({loadedKeys:e3(o,r)}),n()}l(t)}),{loadingKeys:e3(d,r)})})});return o.catch(function(){}),o}}),(0,C.default)((0,tB.default)(e),"onNodeMouseEnter",function(t,n){var r=e.props.onMouseEnter;null==r||r({event:t,node:n})}),(0,C.default)((0,tB.default)(e),"onNodeMouseLeave",function(t,n){var r=e.props.onMouseLeave;null==r||r({event:t,node:n})}),(0,C.default)((0,tB.default)(e),"onNodeContextMenu",function(t,n){var r=e.props.onRightClick;r&&(t.preventDefault(),r({event:t,node:n}))}),(0,C.default)((0,tB.default)(e),"onFocus",function(){var t=e.props.onFocus;e.setState({focused:!0});for(var n=arguments.length,r=Array(n),l=0;l1&&void 0!==arguments[1]&&arguments[1],r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(!e.destroyed){var l=!1,o=!0,a={};Object.keys(t).forEach(function(n){if(e.props.hasOwnProperty(n)){o=!1;return}l=!0,a[n]=t[n]}),l&&(!n||o)&&e.setState((0,w.default)((0,w.default)({},a),r))}}),(0,C.default)((0,tB.default)(e),"scrollTo",function(t){e.listRef.current.scrollTo(t)}),e}return(0,tj.default)(r,[{key:"componentDidMount",value:function(){this.destroyed=!1,this.onUpdated()}},{key:"componentDidUpdate",value:function(){this.onUpdated()}},{key:"onUpdated",value:function(){var e=this.props,t=e.activeKey,n=e.itemScrollOffset;void 0!==t&&t!==this.state.activeKey&&(this.setState({activeKey:t}),null!==t&&this.scrollTo({key:t,offset:void 0===n?0:n}))}},{key:"componentWillUnmount",value:function(){window.removeEventListener("dragend",this.onWindowDragEnd),this.destroyed=!0}},{key:"resetDragState",value:function(){this.setState({dragOverNodeKey:null,dropPosition:null,dropLevelOffset:null,dropTargetKey:null,dropContainerKey:null,dropTargetPos:null,dropAllowed:!1})}},{key:"render",value:function(){var e,n=this.state,r=n.focused,l=n.flattenNodes,o=n.keyEntities,a=n.draggingNodeKey,i=n.activeKey,d=n.dropLevelOffset,c=n.dropContainerKey,u=n.dropTargetKey,f=n.dropPosition,p=n.dragOverNodeKey,m=n.indent,h=this.props,g=h.prefixCls,v=h.className,y=h.style,b=h.showLine,w=h.focusable,k=h.tabIndex,S=h.selectable,N=h.showIcon,$=h.icon,K=h.switcherIcon,O=h.draggable,R=h.checkable,I=h.checkStrictly,P=h.disabled,T=h.motion,M=h.loadData,D=h.filterTreeNode,L=h.height,j=h.itemHeight,B=h.scrollWidth,H=h.virtual,A=h.titleRender,W=h.dropIndicatorRender,F=h.onContextMenu,_=h.onScroll,q=h.direction,V=h.rootClassName,U=h.rootStyle,X=(0,z.default)(this.props,{aria:!0,data:!0});O&&(e="object"===(0,x.default)(O)?O:"function"==typeof O?{nodeDraggable:O}:{});var G={prefixCls:g,selectable:S,showIcon:N,icon:$,switcherIcon:K,draggable:e,draggingNodeKey:a,checkable:R,checkStrictly:I,disabled:P,keyEntities:o,dropLevelOffset:d,dropContainerKey:c,dropTargetKey:u,dropPosition:f,dragOverNodeKey:p,indent:m,direction:q,dropIndicatorRender:W,loadData:M,filterTreeNode:D,titleRender:A,onNodeClick:this.onNodeClick,onNodeDoubleClick:this.onNodeDoubleClick,onNodeExpand:this.onNodeExpand,onNodeSelect:this.onNodeSelect,onNodeCheck:this.onNodeCheck,onNodeLoad:this.onNodeLoad,onNodeMouseEnter:this.onNodeMouseEnter,onNodeMouseLeave:this.onNodeMouseLeave,onNodeContextMenu:this.onNodeContextMenu,onNodeDragStart:this.onNodeDragStart,onNodeDragEnter:this.onNodeDragEnter,onNodeDragOver:this.onNodeDragOver,onNodeDragLeave:this.onNodeDragLeave,onNodeDragEnd:this.onNodeDragEnd,onNodeDrop:this.onNodeDrop};return t.createElement(eH.Provider,{value:G},t.createElement("div",{className:(0,E.default)(g,v,V,(0,C.default)((0,C.default)((0,C.default)({},"".concat(g,"-show-line"),b),"".concat(g,"-focused"),r),"".concat(g,"-active-focused"),null!==i)),style:U},t.createElement(t3,(0,s.default)({ref:this.listRef,prefixCls:g,style:y,data:l,disabled:P,selectable:S,checkable:!!R,motion:T,dragging:null!==a,height:L,itemHeight:j,virtual:H,focusable:w,focused:r,tabIndex:void 0===k?0:k,activeItem:this.getActiveItem(),onFocus:this.onFocus,onBlur:this.onBlur,onKeyDown:this.onKeyDown,onActiveChange:this.onActiveChange,onListChangeStart:this.onListChangeStart,onListChangeEnd:this.onListChangeEnd,onContextMenu:F,onScroll:_,scrollWidth:B},this.getTreeNodeRequiredProps(),X))))}}],[{key:"getDerivedStateFromProps",value:function(e,t){var n,r,l=t.prevProps,o={prevProps:e};function a(t){return!l&&e.hasOwnProperty(t)||l&&l[t]!==e[t]}var i=t.fieldNames;if(a("fieldNames")&&(o.fieldNames=i=eV(e.fieldNames)),a("treeData")?n=e.treeData:a("children")&&((0,N.default)(!1,"`children` of Tree is deprecated. Please use `treeData` instead."),n=eU(e.children)),n){o.treeData=n;var d=eG(n,{fieldNames:i});o.keyEntities=(0,w.default)((0,C.default)({},tJ,tZ),d.keyEntities)}var c=o.keyEntities||t.keyEntities;if(a("expandedKeys")||l&&a("autoExpandParent"))o.expandedKeys=e.autoExpandParent||!l&&e.defaultExpandParent?e7(e.expandedKeys,c):e.expandedKeys;else if(!l&&e.defaultExpandAll){var u=(0,w.default)({},c);delete u[tJ];var s=[];Object.keys(u).forEach(function(e){var t=u[e];t.children&&t.children.length&&s.push(t.key)}),o.expandedKeys=s}else!l&&e.defaultExpandedKeys&&(o.expandedKeys=e.autoExpandParent||e.defaultExpandParent?e7(e.defaultExpandedKeys,c):e.defaultExpandedKeys);if(o.expandedKeys||delete o.expandedKeys,n||o.expandedKeys){var f=eX(n||t.treeData,o.expandedKeys||t.expandedKeys,i);o.flattenNodes=f}if(e.selectable&&(a("selectedKeys")?o.selectedKeys=e6(e.selectedKeys,e):!l&&e.defaultSelectedKeys&&(o.selectedKeys=e6(e.defaultSelectedKeys,e))),e.checkable&&(a("checkedKeys")?r=e5(e.checkedKeys)||{}:!l&&e.defaultCheckedKeys?r=e5(e.defaultCheckedKeys)||{}:n&&(r=e5(e.checkedKeys)||{checkedKeys:t.checkedKeys,halfCheckedKeys:t.halfCheckedKeys}),r)){var p=r,m=p.checkedKeys,h=void 0===m?[]:m,g=p.halfCheckedKeys,v=void 0===g?[]:g;if(!e.checkStrictly){var y=tt(h,!0,c);h=y.checkedKeys,v=y.halfCheckedKeys}o.checkedKeys=h,o.halfCheckedKeys=v}return a("loadedKeys")&&(o.loadedKeys=e.loadedKeys),o}}]),r}(t.Component);(0,C.default)(t4,"defaultProps",{prefixCls:"rc-tree",showLine:!1,showIcon:!0,selectable:!0,multiple:!1,checkable:!1,disabled:!1,checkStrictly:!1,draggable:!1,defaultExpandParent:!0,autoExpandParent:!1,defaultExpandAll:!1,defaultExpandedKeys:[],defaultCheckedKeys:[],defaultSelectedKeys:[],dropIndicatorRender:function(e){var n=e.dropPosition,r=e.dropLevelOffset,l=e.indent,o={pointerEvents:"none",position:"absolute",right:0,backgroundColor:"red",height:2};switch(n){case -1:o.top=0,o.left=-r*l;break;case 1:o.bottom=0,o.left=-r*l;break;case 0:o.bottom=0,o.left=l}return t.default.createElement("div",{style:o})},allowDrop:function(){return!0},expandAction:!1}),(0,C.default)(t4,"TreeNode",e1);let t8={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"}}]},name:"file",theme:"outlined"};var t6=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:t8}))});let t5={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M928 444H820V330.4c0-17.7-14.3-32-32-32H473L355.7 186.2a8.15 8.15 0 00-5.5-2.2H96c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h698c13 0 24.8-7.9 29.7-20l134-332c1.5-3.8 2.3-7.9 2.3-12 0-17.7-14.3-32-32-32zM136 256h188.5l119.6 114.4H748V444H238c-13 0-24.8 7.9-29.7 20L136 643.2V256zm635.3 512H159l103.3-256h612.4L771.3 768z"}}]},name:"folder-open",theme:"outlined"};var t7=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:t5}))});let t9={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"}}]},name:"folder",theme:"outlined"};var ne=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:t9}))});let nt={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M300 276.5a56 56 0 1056-97 56 56 0 00-56 97zm0 284a56 56 0 1056-97 56 56 0 00-56 97zM640 228a56 56 0 10112 0 56 56 0 00-112 0zm0 284a56 56 0 10112 0 56 56 0 00-112 0zM300 844.5a56 56 0 1056-97 56 56 0 00-56 97zM640 796a56 56 0 10112 0 56 56 0 00-112 0z"}}]},name:"holder",theme:"outlined"};var nn=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:nt}))}),nr=e.i(613541),nl=e.i(937328);e.i(296059);var no=e.i(694758),na=e.i(915654),ni=e.i(236836),nd=e.i(183293),nc=e.i(447580),nu=e.i(246422),ns=e.i(838378);let nf=new no.Keyframes("ant-tree-node-fx-do-not-use",{"0%":{opacity:0},"100%":{opacity:1}}),np=(0,nu.genStyleHooks)("Tree",(e,{prefixCls:t})=>[{[e.componentCls]:(0,ni.getStyle)(`${t}-checkbox`,e)},((e,t,n=!0)=>{let r=`.${e}`,l=`${r}-treenode`,o=t.calc(t.paddingXS).div(2).equal(),a=(0,ns.mergeToken)(t,{treeCls:r,treeNodeCls:l,treeNodePadding:o});return[((e,t)=>{let{treeCls:n,treeNodeCls:r,treeNodePadding:l,titleHeight:o,indentSize:a,nodeSelectedBg:i,nodeHoverBg:d,colorTextQuaternary:c,controlItemBgActiveDisabled:u}=t;return{[n]:Object.assign(Object.assign({},(0,nd.resetComponent)(t)),{"--rc-virtual-list-scrollbar-bg":t.colorSplit,background:t.colorBgContainer,borderRadius:t.borderRadius,transition:`background-color ${t.motionDurationSlow}`,"&-rtl":{direction:"rtl"},[`&${n}-rtl ${n}-switcher_close ${n}-switcher-icon svg`]:{transform:"rotate(90deg)"},[`&-focused:not(:hover):not(${n}-active-focused)`]:(0,nd.genFocusOutline)(t),[`${n}-list-holder-inner`]:{alignItems:"flex-start"},[`&${n}-block-node`]:{[`${n}-list-holder-inner`]:{alignItems:"stretch",[`${n}-node-content-wrapper`]:{flex:"auto"},[`${r}.dragging:after`]:{position:"absolute",inset:0,border:`1px solid ${t.colorPrimary}`,opacity:0,animationName:nf,animationDuration:t.motionDurationSlow,animationPlayState:"running",animationFillMode:"forwards",content:'""',pointerEvents:"none",borderRadius:t.borderRadius}}},[r]:{display:"flex",alignItems:"flex-start",marginBottom:l,lineHeight:(0,na.unit)(o),position:"relative","&:before":{content:'""',position:"absolute",zIndex:1,insetInlineStart:0,width:"100%",top:"100%",height:l},[`&-disabled ${n}-node-content-wrapper`]:{color:t.colorTextDisabled,cursor:"not-allowed","&:hover":{background:"transparent"}},[`${n}-checkbox-disabled + ${n}-node-selected,&${r}-disabled${r}-selected ${n}-node-content-wrapper`]:{backgroundColor:u},[`${n}-checkbox-disabled`]:{pointerEvents:"unset"},[`&:not(${r}-disabled)`]:{[`${n}-node-content-wrapper`]:{"&:hover":{color:t.nodeHoverColor}}},[`&-active ${n}-node-content-wrapper`]:{background:t.controlItemBgHover},[`&:not(${r}-disabled).filter-node ${n}-title`]:{color:t.colorPrimary,fontWeight:t.fontWeightStrong},"&-draggable":{cursor:"grab",[`${n}-draggable-icon`]:{flexShrink:0,width:o,textAlign:"center",visibility:"visible",color:c},[`&${r}-disabled ${n}-draggable-icon`]:{visibility:"hidden"}}},[`${n}-indent`]:{alignSelf:"stretch",whiteSpace:"nowrap",userSelect:"none","&-unit":{display:"inline-block",width:a}},[`${n}-draggable-icon`]:{visibility:"hidden"},[`${n}-switcher, ${n}-checkbox`]:{marginInlineEnd:t.calc(t.calc(o).sub(t.controlInteractiveSize)).div(2).equal()},[`${n}-switcher`]:Object.assign(Object.assign({},{[`.${e}-switcher-icon`]:{display:"inline-block",fontSize:10,verticalAlign:"baseline",svg:{transition:`transform ${t.motionDurationSlow}`}}}),{position:"relative",flex:"none",alignSelf:"stretch",width:o,textAlign:"center",cursor:"pointer",userSelect:"none",transition:`all ${t.motionDurationSlow}`,"&-noop":{cursor:"unset"},"&:before":{pointerEvents:"none",content:'""',width:o,height:o,position:"absolute",left:{_skip_check_:!0,value:0},top:0,borderRadius:t.borderRadius,transition:`all ${t.motionDurationSlow}`},[`&:not(${n}-switcher-noop):hover:before`]:{backgroundColor:t.colorBgTextHover},[`&_close ${n}-switcher-icon svg`]:{transform:"rotate(-90deg)"},"&-loading-icon":{color:t.colorPrimary},"&-leaf-line":{position:"relative",zIndex:1,display:"inline-block",width:"100%",height:"100%","&:before":{position:"absolute",top:0,insetInlineEnd:t.calc(o).div(2).equal(),bottom:t.calc(l).mul(-1).equal(),marginInlineStart:-1,borderInlineEnd:`1px solid ${t.colorBorder}`,content:'""'},"&:after":{position:"absolute",width:t.calc(t.calc(o).div(2).equal()).mul(.8).equal(),height:t.calc(o).div(2).equal(),borderBottom:`1px solid ${t.colorBorder}`,content:'""'}}}),[`${n}-node-content-wrapper`]:Object.assign(Object.assign({position:"relative",minHeight:o,paddingBlock:0,paddingInline:t.paddingXS,background:"transparent",borderRadius:t.borderRadius,cursor:"pointer",transition:`all ${t.motionDurationMid}, border 0s, line-height 0s, box-shadow 0s`},{[`.${e}-drop-indicator`]:{position:"absolute",zIndex:1,height:2,backgroundColor:t.colorPrimary,borderRadius:1,pointerEvents:"none","&:after":{position:"absolute",top:-3,insetInlineStart:-6,width:8,height:8,backgroundColor:"transparent",border:`${(0,na.unit)(t.lineWidthBold)} solid ${t.colorPrimary}`,borderRadius:"50%",content:'""'}}}),{"&:hover":{backgroundColor:d},[`&${n}-node-selected`]:{color:t.nodeSelectedColor,backgroundColor:i},[`${n}-iconEle`]:{display:"inline-block",width:o,height:o,textAlign:"center",verticalAlign:"top","&:empty":{display:"none"}}}),[`${n}-unselectable ${n}-node-content-wrapper:hover`]:{backgroundColor:"transparent"},[`${r}.drop-container > [draggable]`]:{boxShadow:`0 0 0 2px ${t.colorPrimary}`},"&-show-line":{[`${n}-indent-unit`]:{position:"relative",height:"100%","&:before":{position:"absolute",top:0,insetInlineEnd:t.calc(o).div(2).equal(),bottom:t.calc(l).mul(-1).equal(),borderInlineEnd:`1px solid ${t.colorBorder}`,content:'""'},"&-end:before":{display:"none"}},[`${n}-switcher`]:{background:"transparent","&-line-icon":{verticalAlign:"-0.15em"}}},[`${r}-leaf-last ${n}-switcher-leaf-line:before`]:{top:"auto !important",bottom:"auto !important",height:`${(0,na.unit)(t.calc(o).div(2).equal())} !important`}})}})(e,a),n&&(({treeCls:e,treeNodeCls:t,directoryNodeSelectedBg:n,directoryNodeSelectedColor:r,motionDurationMid:l,borderRadius:o,controlItemBgHover:a})=>({[`${e}${e}-directory ${t}`]:{[`${e}-node-content-wrapper`]:{position:"static",[`&:has(${e}-drop-indicator)`]:{position:"relative"},[`> *:not(${e}-drop-indicator)`]:{position:"relative"},"&:hover":{background:"transparent"},"&:before":{position:"absolute",inset:0,transition:`background-color ${l}`,content:'""',borderRadius:o},"&:hover:before":{background:a}},[`${e}-switcher, ${e}-checkbox, ${e}-draggable-icon`]:{zIndex:1},"&-selected":{background:n,borderRadius:o,[`${e}-switcher, ${e}-draggable-icon`]:{color:r},[`${e}-node-content-wrapper`]:{color:r,background:"transparent","&, &:hover":{color:r},"&:before, &:hover:before":{background:n}}}}}))(a)].filter(Boolean)})(t,e),(0,nc.genCollapseMotion)(e)],e=>{let{colorTextLightSolid:t,colorPrimary:n}=e;return Object.assign(Object.assign({},(e=>{let{controlHeightSM:t,controlItemBgHover:n,controlItemBgActive:r}=e;return{titleHeight:t,indentSize:t,nodeHoverBg:n,nodeHoverColor:e.colorText,nodeSelectedBg:r,nodeSelectedColor:e.colorText}})(e)),{directoryNodeSelectedColor:t,directoryNodeSelectedBg:n})}),nm=function(e){let{dropPosition:n,dropLevelOffset:r,prefixCls:l,indent:o,direction:a="ltr"}=e,i="ltr"===a?"left":"right",d={[i]:-r*o+4,["ltr"===a?"right":"left"]:0};switch(n){case -1:d.top=-3;break;case 1:d.bottom=-3;break;default:d.bottom=-3,d[i]=o+4}return t.default.createElement("div",{style:d,className:`${l}-drop-indicator`})},nh={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"}}]},name:"caret-down",theme:"filled"};var ng=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:nh}))}),nv=e.i(739295);let ny={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M328 544h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"}},{tag:"path",attrs:{d:"M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"}}]},name:"minus-square",theme:"outlined"};var nb=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:ny}))});let nx={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"}},{tag:"path",attrs:{d:"M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"}}]},name:"plus-square",theme:"outlined"};var nw=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:nx}))}),nC=e.i(763731);let nE=e=>{var n,r;let l,{prefixCls:o,switcherIcon:a,treeNodeProps:i,showLine:d,switcherLoadingIcon:c}=e,{isLeaf:u,expanded:s,loading:f}=i;if(f)return t.isValidElement(c)?c:t.createElement(nv.default,{className:`${o}-switcher-loading-icon`});if(d&&"object"==typeof d&&(l=d.showLeafIcon),u){if(!d)return null;if("boolean"!=typeof l&&l){let e="function"==typeof l?l(i):l,r=`${o}-switcher-line-custom-icon`;return t.isValidElement(e)?(0,nC.cloneElement)(e,{className:(0,E.default)(null==(n=e.props)?void 0:n.className,r)}):e}return l?t.createElement(t6,{className:`${o}-switcher-line-icon`}):t.createElement("span",{className:`${o}-switcher-leaf-line`})}let p=`${o}-switcher-icon`,m="function"==typeof a?a(i):a;return t.isValidElement(m)?(0,nC.cloneElement)(m,{className:(0,E.default)(null==(r=m.props)?void 0:r.className,p)}):void 0!==m?m:d?s?t.createElement(nb,{className:`${o}-switcher-line-icon`}):t.createElement(nw,{className:`${o}-switcher-line-icon`}):t.createElement(ng,{className:p})},nk=t.default.forwardRef((e,n)=>{var r;let{getPrefixCls:l,direction:o,virtual:a,tree:i}=t.default.useContext(th.ConfigContext),{prefixCls:d,className:c,showIcon:u=!1,showLine:s,switcherIcon:f,switcherLoadingIcon:p,blockNode:m=!1,children:h,checkable:g=!1,selectable:v=!0,draggable:y,disabled:b,motion:x,style:w}=e,C=l("tree",d),k=l(),S=t.default.useContext(nl.default),N=null!=b?b:S,$=null!=x?x:Object.assign(Object.assign({},(0,nr.default)(k)),{motionAppear:!1}),K=Object.assign(Object.assign({},e),{checkable:g,selectable:v,showIcon:u,motion:$,blockNode:m,disabled:N,showLine:!!s,dropIndicatorRender:nm}),[O,R,I]=np(C),[,P]=(0,tE.useToken)(),T=P.paddingXS/2+((null==(r=P.Tree)?void 0:r.titleHeight)||P.controlHeightSM),M=t.default.useMemo(()=>{if(!y)return!1;let e={};switch(typeof y){case"function":e.nodeDraggable=y;break;case"object":e=Object.assign({},y)}return!1!==e.icon&&(e.icon=e.icon||t.default.createElement(nn,null)),e},[y]);return O(t.default.createElement(t4,Object.assign({itemHeight:T,ref:n,virtual:a},K,{style:Object.assign(Object.assign({},null==i?void 0:i.style),w),prefixCls:C,className:(0,E.default)({[`${C}-icon-hide`]:!u,[`${C}-block-node`]:m,[`${C}-unselectable`]:!v,[`${C}-rtl`]:"rtl"===o,[`${C}-disabled`]:N},null==i?void 0:i.className,c,R,I),direction:o,checkable:g?t.default.createElement("span",{className:`${C}-checkbox-inner`}):g,selectable:v,switcherIcon:e=>t.default.createElement(nE,{prefixCls:C,switcherIcon:f,switcherLoadingIcon:p,treeNodeProps:e,showLine:s}),draggable:M}),h))});function nS(e,t,n){let{key:r,children:l}=n;e.forEach(function(e){let o=e[r],a=e[l];!1!==t(o,e)&&nS(a||[],t,n)})}var nN=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(n[r[l]]=e[r[l]]);return n};function n$(e){let{isLeaf:n,expanded:r}=e;return n?t.createElement(t6,null):r?t.createElement(t7,null):t.createElement(ne,null)}let nK=t.forwardRef((e,n)=>{var{defaultExpandAll:r,defaultExpandParent:l,defaultExpandedKeys:o}=e,a=nN(e,["defaultExpandAll","defaultExpandParent","defaultExpandedKeys"]);let i=t.useRef(null),d=t.useRef(null),[c,u]=t.useState(a.selectedKeys||a.defaultSelectedKeys||[]),[s,f]=t.useState(()=>(()=>{let{keyEntities:e}=eG(function({treeData:e,children:t}){return e||eU(t)}(a),{fieldNames:a.fieldNames});return r?Object.keys(e):l?e7(a.expandedKeys||o||[],e):a.expandedKeys||o||[]})());t.useEffect(()=>{"selectedKeys"in a&&u(a.selectedKeys)},[a.selectedKeys]),t.useEffect(()=>{"expandedKeys"in a&&f(a.expandedKeys)},[a.expandedKeys]);let{getPrefixCls:p,direction:m}=t.useContext(th.ConfigContext),{prefixCls:h,className:g,showIcon:v=!0,expandAction:y="click"}=a,b=nN(a,["prefixCls","className","showIcon","expandAction"]),x=p("tree",h),w=(0,E.default)(`${x}-directory`,{[`${x}-directory-rtl`]:"rtl"===m},g);return t.createElement(nk,Object.assign({icon:n$,ref:n,blockNode:!0},b,{showIcon:v,expandAction:y,prefixCls:x,className:w,expandedKeys:s,selectedKeys:c,onSelect:(e,t)=>{var n,r,l,o;let c,f,p,{multiple:m,fieldNames:h}=a,{node:g,nativeEvent:v}=t,{key:y=""}=g,b=function({treeData:e,children:t}){return e||eU(t)}(a),x=Object.assign(Object.assign({},t),{selected:!0}),w=(null==v?void 0:v.ctrlKey)||(null==v?void 0:v.metaKey),C=null==v?void 0:v.shiftKey;m&&w?(p=e,i.current=y,d.current=p):m&&C?p=Array.from(new Set([].concat((0,er.default)(d.current||[]),(0,er.default)(function({treeData:e,expandedKeys:t,startKey:n,endKey:r,fieldNames:l}){let o=[],a=0;return n&&n===r?[n]:n&&r?(nS(e,e=>{if(2===a)return!1;if(e===n||e===r){if(o.push(e),0===a)a=1;else if(1===a)return a=2,!1}else 1===a&&o.push(e);return t.includes(e)},eV(l)),o):[]}({treeData:b,expandedKeys:s,startKey:y,endKey:i.current,fieldNames:h}))))):(p=[y],i.current=y,d.current=p),r=b,l=p,o=h,c=(0,er.default)(l),f=[],nS(r,(e,t)=>{let n=c.indexOf(e);return -1!==n&&(f.push(t),c.splice(n,1)),!!c.length},eV(o)),x.selectedNodes=f,null==(n=a.onSelect)||n.call(a,p,x),"selectedKeys"in a||u(p)},onExpand:(e,t)=>{var n;return"expandedKeys"in a||f(e),null==(n=a.onExpand)?void 0:n.call(a,e,t)}}))});nk.DirectoryTree=nK,nk.TreeNode=e1;var nO=e.i(38953),nR=e.i(90635);let nI=e=>{let{value:n,filterSearch:r,tablePrefixCls:l,locale:o,onChange:a}=e;return r?t.createElement("div",{className:`${l}-filter-dropdown-search`},t.createElement(nR.default,{prefix:t.createElement(nO.default,null),placeholder:o.filterSearchPlaceholder,onChange:a,value:n,htmlSize:1,className:`${l}-filter-dropdown-search-input`})):null},nP=e=>{let{keyCode:t}=e;t===tz.default.ENTER&&e.stopPropagation()},nT=t.forwardRef((e,n)=>t.createElement("div",{className:e.className,onClick:e=>e.stopPropagation(),onKeyDown:nP,ref:n},e.children));function nM(e){let t=[];return(e||[]).forEach(({value:e,children:n})=>{t.push(e),n&&(t=[].concat((0,er.default)(t),(0,er.default)(nM(n))))}),t}function nD(e,t){return("string"==typeof t||"number"==typeof t)&&(null==t?void 0:t.toString().toLowerCase().includes(e.trim().toLowerCase()))}let nL=e=>{var n,r,l,o;let a,d,{tablePrefixCls:c,prefixCls:u,column:s,dropdownPrefixCls:f,columnKey:p,filterOnClose:m,filterMultiple:h,filterMode:g="menu",filterSearch:v=!1,filterState:y,triggerFilter:b,locale:x,children:w,getPopupContainer:C,rootClassName:k}=e,{filterResetToDefaultFilteredValue:S,defaultFilteredValue:N,filterDropdownProps:$={},filterDropdownOpen:K,filterDropdownVisible:O,onFilterDropdownVisibleChange:R,onFilterDropdownOpenChange:I}=s,[P,T]=t.useState(!1),M=!!(y&&((null==(n=y.filteredKeys)?void 0:n.length)||y.forceFiltered)),D=e=>{var t;T(e),null==(t=$.onOpenChange)||t.call($,e),null==I||I(e),null==R||R(e)},L=null!=(o=null!=(l=null!=(r=$.open)?r:K)?l:O)?o:P,j=null==y?void 0:y.filteredKeys,[B,H]=(e=>{let n=t.useRef(e),[,r]=(0,tI.useForceUpdate)();return[()=>n.current,e=>{n.current=e,r()}]})(j||[]),A=({selectedKeys:e})=>{H(e)},z=(e,{node:t,checked:n})=>{h?A({selectedKeys:e}):A({selectedKeys:n&&t.key?[t.key]:[]})};t.useEffect(()=>{P&&A({selectedKeys:j||[]})},[j]);let[W,F]=t.useState([]),_=e=>{F(e)},[q,V]=t.useState(""),U=e=>{let{value:t}=e.target;V(t)};t.useEffect(()=>{P||V("")},[P]);let X=e=>{let t=(null==e?void 0:e.length)?e:null;if(null===t&&(!y||!y.filteredKeys)||(0,i.default)(t,null==y?void 0:y.filteredKeys,!0))return null;b({column:s,key:p,filteredKeys:t})},G=()=>{D(!1),X(B())},Y=({confirm:e,closeDropdown:t}={confirm:!1,closeDropdown:!1})=>{e&&X([]),t&&D(!1),V(""),S?H((N||[]).map(e=>String(e))):H([])},J=(0,E.default)({[`${f}-menu-without-submenu`]:!(s.filters||[]).some(({children:e})=>e)}),Q=e=>{e.target.checked?H(nM(null==s?void 0:s.filters).map(e=>String(e))):H([])},Z=({filters:e})=>(e||[]).map((e,t)=>{let n=String(e.value),r={title:e.text,key:void 0!==e.value?n:String(t)};return e.children&&(r.children=Z({filters:e.children})),r}),ee=e=>{var t;return Object.assign(Object.assign({},e),{text:e.title,value:e.key,children:(null==(t=e.children)?void 0:t.map(e=>ee(e)))||[]})},{direction:et,renderEmpty:en}=t.useContext(th.ConfigContext);if("function"==typeof s.filterDropdown)a=s.filterDropdown({prefixCls:`${f}-custom`,setSelectedKeys:e=>A({selectedKeys:e}),selectedKeys:B(),confirm:({closeDropdown:e}={closeDropdown:!0})=>{e&&D(!1),X(B())},clearFilters:Y,filters:s.filters,visible:L,close:()=>{D(!1)}});else if(s.filterDropdown)a=s.filterDropdown;else{let e=B()||[];a=t.createElement(t.Fragment,null,(()=>{var n,r;let l=null!=(n=null==en?void 0:en("Table.filter"))?n:t.createElement(tT.default,{image:tT.default.PRESENTED_IMAGE_SIMPLE,description:x.filterEmptyText,styles:{image:{height:24}},style:{margin:0,padding:"16px 0"}});if(0===(s.filters||[]).length)return l;if("tree"===g)return t.createElement(t.Fragment,null,t.createElement(nI,{filterSearch:v,value:q,onChange:U,tablePrefixCls:c,locale:x}),t.createElement("div",{className:`${c}-filter-dropdown-tree`},h?t.createElement(tl.default,{checked:e.length===nM(s.filters).length,indeterminate:e.length>0&&e.length"function"==typeof v?v(q,ee(e)):nD(q,e.title):void 0})));let o=function e({filters:n,prefixCls:r,filteredKeys:l,filterMultiple:o,searchValue:a,filterSearch:i}){return n.map((n,d)=>{let c=String(n.value);if(n.children)return{key:c||d,label:n.text,popupClassName:`${r}-dropdown-submenu`,children:e({filters:n.children,prefixCls:r,filteredKeys:l,filterMultiple:o,searchValue:a,filterSearch:i})};let u=o?tl.default:ta.default,s={key:void 0!==n.value?c:d,label:t.createElement(t.Fragment,null,t.createElement(u,{checked:l.includes(c)}),t.createElement("span",null,n.text))};return a.trim()?"function"==typeof i?i(a,n)?s:null:nD(a,n.text)?s:null:s})}({filters:s.filters||[],filterSearch:v,prefixCls:u,filteredKeys:B(),filterMultiple:h,searchValue:q}),a=o.every(e=>null===e);return t.createElement(t.Fragment,null,t.createElement(nI,{filterSearch:v,value:q,onChange:U,tablePrefixCls:c,locale:x}),a?l:t.createElement(tM.default,{selectable:!0,multiple:h,prefixCls:`${f}-menu`,className:J,onSelect:A,onDeselect:A,selectedKeys:e,getPopupContainer:C,openKeys:W,onOpenChange:_,items:o}))})(),t.createElement("div",{className:`${u}-dropdown-btns`},t.createElement(tP.default,{type:"link",size:"small",disabled:S?(0,i.default)((N||[]).map(e=>String(e)),e,!0):0===e.length,onClick:()=>Y()},x.filterReset),t.createElement(tP.default,{type:"primary",size:"small",onClick:G},x.filterConfirm)))}s.filterDropdown&&(a=t.createElement(tD.OverrideProvider,{selectable:void 0},a)),a=t.createElement(nT,{className:`${u}-dropdown`},a);let er=(0,tR.default)({trigger:["click"],placement:"rtl"===et?"bottomLeft":"bottomRight",children:(d="function"==typeof s.filterIcon?s.filterIcon(M):s.filterIcon?s.filterIcon:t.createElement(tO,null),t.createElement("span",{role:"button",tabIndex:-1,className:(0,E.default)(`${u}-trigger`,{active:M}),onClick:e=>{e.stopPropagation()}},d)),getPopupContainer:C},Object.assign(Object.assign({},$),{rootClassName:(0,E.default)(k,$.rootClassName),open:L,onOpenChange:(e,t)=>{"trigger"===t.source&&(e&&void 0!==j&&H(j||[]),D(e),e||s.filterDropdown||!m||G())},popupRender:()=>"function"==typeof(null==$?void 0:$.dropdownRender)?$.dropdownRender(a):a}));return t.createElement("div",{className:`${u}-column`},t.createElement("span",{className:`${c}-column-title`},w),t.createElement(to.default,Object.assign({},er)))},nj=(e,t,n)=>{let r=[];return(e||[]).forEach((e,l)=>{var o;let a=tS(l,n),i=void 0!==e.filterDropdown;if(e.filters||i||"onFilter"in e)if("filteredValue"in e){let t=e.filteredValue;i||(t=null!=(o=null==t?void 0:t.map(String))?o:t),r.push({column:e,key:tk(e,a),filteredKeys:t,forceFiltered:e.filtered})}else r.push({column:e,key:tk(e,a),filteredKeys:t&&e.defaultFilteredValue?e.defaultFilteredValue:void 0,forceFiltered:e.filtered});"children"in e&&(r=[].concat((0,er.default)(r),(0,er.default)(nj(e.children,t,a))))}),r},nB=e=>{let t={};return e.forEach(({key:e,filteredKeys:n,column:r})=>{let{filters:l,filterDropdown:o}=r;if(o)t[e]=n||null;else if(Array.isArray(n)){let r=nM(l);t[e]=r.filter(e=>n.includes(String(e)))}else t[e]=null}),t},nH=(e,t,n)=>t.reduce((e,r)=>{let{column:{onFilter:l,filters:o},filteredKeys:a}=r;return l&&a&&a.length?e.map(e=>Object.assign({},e)).filter(e=>a.some(r=>{let a=nM(o),i=a.findIndex(e=>String(e)===String(r)),d=-1!==i?a[i]:r;return e[n]&&(e[n]=nH(e[n],t,n)),l(d,e)})):e},e),nA=e=>e.flatMap(e=>"children"in e?[e].concat((0,er.default)(nA(e.children||[]))):[e]);var nz=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,r=Object.getOwnPropertySymbols(e);lt.indexOf(r[l])&&Object.prototype.propertyIsEnumerable.call(e,r[l])&&(n[r[l]]=e[r[l]]);return n};let nW=function(e,n,r){let l=r&&"object"==typeof r?r:{},{total:o=0}=l,a=nz(l,["total"]),[i,d]=(0,t.useState)(()=>({current:"defaultCurrent"in a?a.defaultCurrent:1,pageSize:"defaultPageSize"in a?a.defaultPageSize:10})),c=(0,tR.default)(i,a,{total:o>0?o:e}),u=Math.ceil((o||e)/c.pageSize);c.current>u&&(c.current=u||1);let s=(e,t)=>{d({current:null!=e?e:1,pageSize:t||c.pageSize})};return!1===r?[{},()=>{}]:[Object.assign(Object.assign({},c),{onChange:(e,t)=>{var l;r&&(null==(l=r.onChange)||l.call(r,e,t)),s(e,t),n(e,t||(null==c?void 0:c.pageSize))}}),s]},nF={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"}}]},name:"caret-down",theme:"outlined"};var n_=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:nF}))});let nq={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M858.9 689L530.5 308.2c-9.4-10.9-27.5-10.9-37 0L165.1 689c-12.2 14.2-1.2 35 18.5 35h656.8c19.7 0 30.7-20.8 18.5-35z"}}]},name:"caret-up",theme:"outlined"};var nV=t.forwardRef(function(e,n){return t.createElement(tK.default,(0,s.default)({},e,{ref:n,icon:nq}))}),nU=e.i(491816);let nX="ascend",nG="descend",nY=e=>"object"==typeof e.sorter&&"number"==typeof e.sorter.multiple&&e.sorter.multiple,nJ=e=>"function"==typeof e?e:!!e&&"object"==typeof e&&!!e.compare&&e.compare,nQ=(e,t,n)=>{let r=[],l=(e,t)=>{r.push({column:e,key:tk(e,t),multiplePriority:nY(e),sortOrder:e.sortOrder})};return(e||[]).forEach((e,o)=>{let a=tS(o,n);e.children?("sortOrder"in e&&l(e,a),r=[].concat((0,er.default)(r),(0,er.default)(nQ(e.children,t,a)))):e.sorter&&("sortOrder"in e?l(e,a):t&&e.defaultSortOrder&&r.push({column:e,key:tk(e,a),multiplePriority:nY(e),sortOrder:e.defaultSortOrder}))}),r},nZ=(e,n,r,l,o,a,i,d)=>(n||[]).map((n,c)=>{let u=tS(c,d),s=n;if(s.sorter){let d,c=s.sortDirections||o,f=void 0===s.showSorterTooltip?i:s.showSorterTooltip,p=tk(s,u),m=r.find(({key:e})=>e===p),h=m?m.sortOrder:null,g=h?c[c.indexOf(h)+1]:c[0];if(n.sortIcon)d=n.sortIcon({sortOrder:h});else{let n=c.includes(nX)&&t.createElement(nV,{className:(0,E.default)(`${e}-column-sorter-up`,{active:h===nX})}),r=c.includes(nG)&&t.createElement(n_,{className:(0,E.default)(`${e}-column-sorter-down`,{active:h===nG})});d=t.createElement("span",{className:(0,E.default)(`${e}-column-sorter`,{[`${e}-column-sorter-full`]:!!(n&&r)})},t.createElement("span",{className:`${e}-column-sorter-inner`,"aria-hidden":"true"},n,r))}let{cancelSort:v,triggerAsc:y,triggerDesc:b}=a||{},x=v;g===nG?x=b:g===nX&&(x=y);let w="object"==typeof f?Object.assign({title:x},f):{title:x};s=Object.assign(Object.assign({},s),{className:(0,E.default)(s.className,{[`${e}-column-sort`]:h}),title:r=>{let l=`${e}-column-sorters`,o=t.createElement("span",{className:`${e}-column-title`},tN(n.title,r)),a=t.createElement("div",{className:l},o,d);return f?"boolean"!=typeof f&&(null==f?void 0:f.target)==="sorter-icon"?t.createElement("div",{className:(0,E.default)(l,`${l}-tooltip-target-sorter`)},o,t.createElement(nU.default,Object.assign({},w),d)):t.createElement(nU.default,Object.assign({},w),a):a},onHeaderCell:t=>{var r;let o,a=(null==(r=n.onHeaderCell)?void 0:r.call(n,t))||{},i=a.onClick,d=a.onKeyDown;a.onClick=e=>{l({column:n,key:p,sortOrder:g,multiplePriority:nY(n)}),null==i||i(e)},a.onKeyDown=e=>{e.keyCode===tz.default.ENTER&&(l({column:n,key:p,sortOrder:g,multiplePriority:nY(n)}),null==d||d(e))};let c=(o=tN(n.title,{}),"[object Object]"===Object.prototype.toString.call(o)?"":o),u=null==c?void 0:c.toString();return h&&(a["aria-sort"]="ascend"===h?"ascending":"descending"),a["aria-label"]=u||"",a.className=(0,E.default)(a.className,`${e}-column-has-sorters`),a.tabIndex=0,n.ellipsis&&(a.title=(null!=c?c:"").toString()),a}})}return"children"in s&&(s=Object.assign(Object.assign({},s),{children:nZ(e,s.children,r,l,o,a,i,u)})),s}),n0=e=>{let{column:t,sortOrder:n}=e;return{column:t,order:n,field:t.dataIndex,columnKey:t.key}},n1=e=>{let t=e.filter(({sortOrder:e})=>e).map(n0);if(0===t.length&&e.length){let t=e.length-1;return Object.assign(Object.assign({},n0(e[t])),{column:void 0,order:void 0,field:void 0,columnKey:void 0})}return t.length<=1?t[0]||{}:t},n2=(e,t,n)=>{let r=t.slice().sort((e,t)=>t.multiplePriority-e.multiplePriority),l=e.slice(),o=r.filter(({column:{sorter:e},sortOrder:t})=>nJ(e)&&t);return o.length?l.sort((e,t)=>{for(let n=0;n{let r=e[n];return r?Object.assign(Object.assign({},e),{[n]:n2(r,t,n)}):e}):l},n3=(e,t)=>e.map(e=>{let n=Object.assign({},e);return n.title=tN(e.title,t),"children"in n&&(n.children=n3(n.children,t)),n}),n4=g(e$,(e,t)=>{let{_renderTimes:n}=e,{_renderTimes:r}=t;return n!==r}),n8=g(ej,(e,t)=>{let{_renderTimes:n}=e,{_renderTimes:r}=t;return n!==r});var n6=e.i(135551);let n5=e=>{let{componentCls:t,lineWidth:n,tableBorderColor:r,calc:l}=e,o=`${(0,na.unit)(n)} ${e.lineType} ${r}`;return{[`${t}-wrapper`]:{[`${t}-summary`]:{position:"relative",zIndex:e.zIndexTableFixed,background:e.tableBg,"> tr":{"> th, > td":{borderBottom:o}}},[`div${t}-summary`]:{boxShadow:`0 ${(0,na.unit)(l(n).mul(-1).equal())} 0 ${r}`}}}},n7=(0,nu.genStyleHooks)("Table",e=>{let{colorTextHeading:t,colorSplit:n,colorBgContainer:r,controlInteractiveSize:l,headerBg:o,headerColor:a,headerSortActiveBg:i,headerSortHoverBg:d,bodySortBg:c,rowHoverBg:u,rowSelectedBg:s,rowSelectedHoverBg:f,rowExpandedBg:p,cellPaddingBlock:m,cellPaddingInline:h,cellPaddingBlockMD:g,cellPaddingInlineMD:v,cellPaddingBlockSM:y,cellPaddingInlineSM:b,borderColor:x,footerBg:w,footerColor:C,headerBorderRadius:E,cellFontSize:k,cellFontSizeMD:S,cellFontSizeSM:N,headerSplitColor:$,fixedHeaderSortActiveBg:K,headerFilterHoverBg:O,filterDropdownBg:R,expandIconBg:I,selectionColumnWidth:P,stickyScrollBarBg:T,calc:M}=e,D=(0,ns.mergeToken)(e,{tableFontSize:k,tableBg:r,tableRadius:E,tablePaddingVertical:m,tablePaddingHorizontal:h,tablePaddingVerticalMiddle:g,tablePaddingHorizontalMiddle:v,tablePaddingVerticalSmall:y,tablePaddingHorizontalSmall:b,tableBorderColor:x,tableHeaderTextColor:a,tableHeaderBg:o,tableFooterTextColor:C,tableFooterBg:w,tableHeaderCellSplitColor:$,tableHeaderSortBg:i,tableHeaderSortHoverBg:d,tableBodySortBg:c,tableFixedHeaderSortActiveBg:K,tableHeaderFilterActiveBg:O,tableFilterDropdownBg:R,tableRowHoverBg:u,tableSelectedRowBg:s,tableSelectedRowHoverBg:f,zIndexTableFixed:2,zIndexTableSticky:M(2).add(1).equal({unit:!1}),tableFontSizeMiddle:S,tableFontSizeSmall:N,tableSelectionColumnWidth:P,tableExpandIconBg:I,tableExpandColumnWidth:M(l).add(M(e.padding).mul(2)).equal(),tableExpandedRowBg:p,tableFilterDropdownWidth:120,tableFilterDropdownHeight:264,tableFilterDropdownSearchWidth:140,tableScrollThumbSize:8,tableScrollThumbBg:T,tableScrollThumbBgHover:t,tableScrollBg:n});return[(e=>{let{componentCls:t,fontWeightStrong:n,tablePaddingVertical:r,tablePaddingHorizontal:l,tableExpandColumnWidth:o,lineWidth:a,lineType:i,tableBorderColor:d,tableFontSize:c,tableBg:u,tableRadius:s,tableHeaderTextColor:f,motionDurationMid:p,tableHeaderBg:m,tableHeaderCellSplitColor:h,tableFooterTextColor:g,tableFooterBg:v,calc:y}=e,b=`${(0,na.unit)(a)} ${i} ${d}`;return{[`${t}-wrapper`]:Object.assign(Object.assign({clear:"both",maxWidth:"100%","--rc-virtual-list-scrollbar-bg":e.tableScrollBg},(0,nd.clearFix)()),{[t]:Object.assign(Object.assign({},(0,nd.resetComponent)(e)),{fontSize:c,background:u,borderRadius:`${(0,na.unit)(s)} ${(0,na.unit)(s)} 0 0`,scrollbarColor:`${e.tableScrollThumbBg} ${e.tableScrollBg}`}),table:{width:"100%",textAlign:"start",borderRadius:`${(0,na.unit)(s)} ${(0,na.unit)(s)} 0 0`,borderCollapse:"separate",borderSpacing:0},[` + ${t}-cell, + ${t}-thead > tr > th, + ${t}-tbody > tr > th, + ${t}-tbody > tr > td, + tfoot > tr > th, + tfoot > tr > td + `]:{position:"relative",padding:`${(0,na.unit)(r)} ${(0,na.unit)(l)}`,overflowWrap:"break-word"},[`${t}-title`]:{padding:`${(0,na.unit)(r)} ${(0,na.unit)(l)}`},[`${t}-thead`]:{[` + > tr > th, + > tr > td + `]:{position:"relative",color:f,fontWeight:n,textAlign:"start",background:m,borderBottom:b,transition:`background ${p} ease`,"&[colspan]:not([colspan='1'])":{textAlign:"center"},[`&:not(:last-child):not(${t}-selection-column):not(${t}-row-expand-icon-cell):not([colspan])::before`]:{position:"absolute",top:"50%",insetInlineEnd:0,width:1,height:"1.6em",backgroundColor:h,transform:"translateY(-50%)",transition:`background-color ${p}`,content:'""'}},"> tr:not(:last-child) > th[colspan]":{borderBottom:0}},[`${t}-tbody`]:{"> tr":{"> th, > td":{transition:`background ${p}, border-color ${p}`,borderBottom:b,[` + > ${t}-wrapper:only-child, + > ${t}-expanded-row-fixed > ${t}-wrapper:only-child + `]:{[t]:{marginBlock:(0,na.unit)(y(r).mul(-1).equal()),marginInline:`${(0,na.unit)(y(o).sub(l).equal())} + ${(0,na.unit)(y(l).mul(-1).equal())}`,[`${t}-tbody > tr:last-child > td`]:{borderBottomWidth:0,"&:first-child, &:last-child":{borderRadius:0}}}}},"> th":{position:"relative",color:f,fontWeight:n,textAlign:"start",background:m,borderBottom:b,transition:`background ${p} ease`},[`& > ${t}-measure-cell`]:{paddingBlock:"0 !important",borderBlock:"0 !important",[`${t}-measure-cell-content`]:{height:0,overflow:"hidden",pointerEvents:"none"}}}},[`${t}-footer`]:{padding:`${(0,na.unit)(r)} ${(0,na.unit)(l)}`,color:g,background:v}})}})(D),(e=>{let{componentCls:t,antCls:n,margin:r}=e;return{[`${t}-wrapper ${t}-pagination${n}-pagination`]:{margin:`${(0,na.unit)(r)} 0`}}})(D),n5(D),(e=>{let{componentCls:t,marginXXS:n,fontSizeIcon:r,headerIconColor:l,headerIconHoverColor:o}=e;return{[`${t}-wrapper`]:{[`${t}-thead th${t}-column-has-sorters`]:{outline:"none",cursor:"pointer",transition:`all ${e.motionDurationSlow}, left 0s`,"&:hover":{background:e.tableHeaderSortHoverBg,"&::before":{backgroundColor:"transparent !important"}},"&:focus-visible":{color:e.colorPrimary},[` + &${t}-cell-fix-left:hover, + &${t}-cell-fix-right:hover + `]:{background:e.tableFixedHeaderSortActiveBg}},[`${t}-thead th${t}-column-sort`]:{background:e.tableHeaderSortBg,"&::before":{backgroundColor:"transparent !important"}},[`td${t}-column-sort`]:{background:e.tableBodySortBg},[`${t}-column-title`]:{position:"relative",zIndex:1,flex:1,minWidth:0},[`${t}-column-sorters`]:{display:"flex",flex:"auto",alignItems:"center",justifyContent:"space-between","&::after":{position:"absolute",inset:0,width:"100%",height:"100%",content:'""'}},[`${t}-column-sorters-tooltip-target-sorter`]:{"&::after":{content:"none"}},[`${t}-column-sorter`]:{marginInlineStart:n,color:l,fontSize:0,transition:`color ${e.motionDurationSlow}`,"&-inner":{display:"inline-flex",flexDirection:"column",alignItems:"center"},"&-up, &-down":{fontSize:r,"&.active":{color:e.colorPrimary}},[`${t}-column-sorter-up + ${t}-column-sorter-down`]:{marginTop:"-0.3em"}},[`${t}-column-sorters:hover ${t}-column-sorter`]:{color:o}}}})(D),(e=>{let{componentCls:t,antCls:n,iconCls:r,tableFilterDropdownWidth:l,tableFilterDropdownSearchWidth:o,paddingXXS:a,paddingXS:i,colorText:d,lineWidth:c,lineType:u,tableBorderColor:s,headerIconColor:f,fontSizeSM:p,tablePaddingHorizontal:m,borderRadius:h,motionDurationSlow:g,colorIcon:v,colorPrimary:y,tableHeaderFilterActiveBg:b,colorTextDisabled:x,tableFilterDropdownBg:w,tableFilterDropdownHeight:C,controlItemBgHover:E,controlItemBgActive:k,boxShadowSecondary:S,filterDropdownMenuBg:N,calc:$}=e,K=`${n}-dropdown`,O=`${t}-filter-dropdown`,R=`${n}-tree`,I=`${(0,na.unit)(c)} ${u} ${s}`;return[{[`${t}-wrapper`]:{[`${t}-filter-column`]:{display:"flex",justifyContent:"space-between"},[`${t}-filter-trigger`]:{position:"relative",display:"flex",alignItems:"center",marginBlock:$(a).mul(-1).equal(),marginInline:`${(0,na.unit)(a)} ${(0,na.unit)($(m).div(2).mul(-1).equal())}`,padding:`0 ${(0,na.unit)(a)}`,color:f,fontSize:p,borderRadius:h,cursor:"pointer",transition:`all ${g}`,"&:hover":{color:v,background:b},"&.active":{color:y}}}},{[`${n}-dropdown`]:{[O]:Object.assign(Object.assign({},(0,nd.resetComponent)(e)),{minWidth:l,backgroundColor:w,borderRadius:h,boxShadow:S,overflow:"hidden",[`${K}-menu`]:{maxHeight:C,overflowX:"hidden",border:0,boxShadow:"none",borderRadius:"unset",backgroundColor:N,"&:empty::after":{display:"block",padding:`${(0,na.unit)(i)} 0`,color:x,fontSize:p,textAlign:"center",content:'"Not Found"'}},[`${O}-tree`]:{paddingBlock:`${(0,na.unit)(i)} 0`,paddingInline:i,[R]:{padding:0},[`${R}-treenode ${R}-node-content-wrapper:hover`]:{backgroundColor:E},[`${R}-treenode-checkbox-checked ${R}-node-content-wrapper`]:{"&, &:hover":{backgroundColor:k}}},[`${O}-search`]:{padding:i,borderBottom:I,"&-input":{input:{minWidth:o},[r]:{color:x}}},[`${O}-checkall`]:{width:"100%",marginBottom:a,marginInlineStart:a},[`${O}-btns`]:{display:"flex",justifyContent:"space-between",padding:`${(0,na.unit)($(i).sub(c).equal())} ${(0,na.unit)(i)}`,overflow:"hidden",borderTop:I}})}},{[`${n}-dropdown ${O}, ${O}-submenu`]:{[`${n}-checkbox-wrapper + span`]:{paddingInlineStart:i,color:d},"> ul":{maxHeight:"calc(100vh - 130px)",overflowX:"hidden",overflowY:"auto"}}}]})(D),(e=>{let{componentCls:t,lineWidth:n,lineType:r,tableBorderColor:l,tableHeaderBg:o,tablePaddingVertical:a,tablePaddingHorizontal:i,calc:d}=e,c=`${(0,na.unit)(n)} ${r} ${l}`,u=(e,r,l)=>({[`&${t}-${e}`]:{[`> ${t}-container`]:{[`> ${t}-content, > ${t}-body`]:{[` + > table > tbody > tr > th, + > table > tbody > tr > td + `]:{[`> ${t}-expanded-row-fixed`]:{margin:`${(0,na.unit)(d(r).mul(-1).equal())} + ${(0,na.unit)(d(d(l).add(n)).mul(-1).equal())}`}}}}}});return{[`${t}-wrapper`]:{[`${t}${t}-bordered`]:Object.assign(Object.assign(Object.assign({[`> ${t}-title`]:{border:c,borderBottom:0},[`> ${t}-container`]:{borderInlineStart:c,borderTop:c,[` + > ${t}-content, + > ${t}-header, + > ${t}-body, + > ${t}-summary + `]:{"> table":{[` + > thead > tr > th, + > thead > tr > td, + > tbody > tr > th, + > tbody > tr > td, + > tfoot > tr > th, + > tfoot > tr > td + `]:{borderInlineEnd:c},"> thead":{"> tr:not(:last-child) > th":{borderBottom:c},"> tr > th::before":{backgroundColor:"transparent !important"}},[` + > thead > tr, + > tbody > tr, + > tfoot > tr + `]:{[`> ${t}-cell-fix-right-first::after`]:{borderInlineEnd:c}},[` + > tbody > tr > th, + > tbody > tr > td + `]:{[`> ${t}-expanded-row-fixed`]:{margin:`${(0,na.unit)(d(a).mul(-1).equal())} ${(0,na.unit)(d(d(i).add(n)).mul(-1).equal())}`,"&::after":{position:"absolute",top:0,insetInlineEnd:n,bottom:0,borderInlineEnd:c,content:'""'}}}}}},[`&${t}-scroll-horizontal`]:{[`> ${t}-container > ${t}-body`]:{"> table > tbody":{[` + > tr${t}-expanded-row, + > tr${t}-placeholder + `]:{"> th, > td":{borderInlineEnd:0}}}}}},u("middle",e.tablePaddingVerticalMiddle,e.tablePaddingHorizontalMiddle)),u("small",e.tablePaddingVerticalSmall,e.tablePaddingHorizontalSmall)),{[`> ${t}-footer`]:{border:c,borderTop:0}}),[`${t}-cell`]:{[`${t}-container:first-child`]:{borderTop:0},"&-scrollbar:not([rowspan])":{boxShadow:`0 ${(0,na.unit)(n)} 0 ${(0,na.unit)(n)} ${o}`}},[`${t}-bordered ${t}-cell-scrollbar`]:{borderInlineEnd:c}}}})(D),(e=>{let{componentCls:t,tableRadius:n}=e;return{[`${t}-wrapper`]:{[t]:{[`${t}-title, ${t}-header`]:{borderRadius:`${(0,na.unit)(n)} ${(0,na.unit)(n)} 0 0`},[`${t}-title + ${t}-container`]:{borderStartStartRadius:0,borderStartEndRadius:0,[`${t}-header, table`]:{borderRadius:0},"table > thead > tr:first-child":{"th:first-child, th:last-child, td:first-child, td:last-child":{borderRadius:0}}},"&-container":{borderStartStartRadius:n,borderStartEndRadius:n,"table > thead > tr:first-child":{"> *:first-child":{borderStartStartRadius:n},"> *:last-child":{borderStartEndRadius:n}}},"&-footer":{borderRadius:`0 0 ${(0,na.unit)(n)} ${(0,na.unit)(n)}`}}}}})(D),(e=>{let{componentCls:t,antCls:n,motionDurationSlow:r,lineWidth:l,paddingXS:o,lineType:a,tableBorderColor:i,tableExpandIconBg:d,tableExpandColumnWidth:c,borderRadius:u,tablePaddingVertical:s,tablePaddingHorizontal:f,tableExpandedRowBg:p,paddingXXS:m,expandIconMarginTop:h,expandIconSize:g,expandIconHalfInner:v,expandIconScale:y,calc:b}=e,x=`${(0,na.unit)(l)} ${a} ${i}`,w=b(m).sub(l).equal();return{[`${t}-wrapper`]:{[`${t}-expand-icon-col`]:{width:c},[`${t}-row-expand-icon-cell`]:{textAlign:"center",[`${t}-row-expand-icon`]:{display:"inline-flex",float:"none",verticalAlign:"sub"}},[`${t}-row-indent`]:{height:1,float:"left"},[`${t}-row-expand-icon`]:Object.assign(Object.assign({},(0,nd.operationUnit)(e)),{position:"relative",float:"left",width:g,height:g,color:"inherit",lineHeight:(0,na.unit)(g),background:d,border:x,borderRadius:u,transform:`scale(${y})`,"&:focus, &:hover, &:active":{borderColor:"currentcolor"},"&::before, &::after":{position:"absolute",background:"currentcolor",transition:`transform ${r} ease-out`,content:'""'},"&::before":{top:v,insetInlineEnd:w,insetInlineStart:w,height:l},"&::after":{top:w,bottom:w,insetInlineStart:v,width:l,transform:"rotate(90deg)"},"&-collapsed::before":{transform:"rotate(-180deg)"},"&-collapsed::after":{transform:"rotate(0deg)"},"&-spaced":{"&::before, &::after":{display:"none",content:"none"},background:"transparent",border:0,visibility:"hidden"}}),[`${t}-row-indent + ${t}-row-expand-icon`]:{marginTop:h,marginInlineEnd:o},[`tr${t}-expanded-row`]:{"&, &:hover":{"> th, > td":{background:p}},[`${n}-descriptions-view`]:{display:"flex",table:{flex:"auto",width:"100%"}}},[`${t}-expanded-row-fixed`]:{position:"relative",margin:`${(0,na.unit)(b(s).mul(-1).equal())} ${(0,na.unit)(b(f).mul(-1).equal())}`,padding:`${(0,na.unit)(s)} ${(0,na.unit)(f)}`}}}})(D),n5(D),(e=>{let{componentCls:t}=e;return{[`${t}-wrapper`]:{[`${t}-tbody > tr${t}-placeholder`]:{textAlign:"center",color:e.colorTextDisabled,[` + &:hover > th, + &:hover > td, + `]:{background:e.colorBgContainer}}}}})(D),(e=>{let{componentCls:t,antCls:n,iconCls:r,fontSizeIcon:l,padding:o,paddingXS:a,headerIconColor:i,headerIconHoverColor:d,tableSelectionColumnWidth:c,tableSelectedRowBg:u,tableSelectedRowHoverBg:s,tableRowHoverBg:f,tablePaddingHorizontal:p,calc:m}=e;return{[`${t}-wrapper`]:{[`${t}-selection-col`]:{width:c,[`&${t}-selection-col-with-dropdown`]:{width:m(c).add(l).add(m(o).div(4)).equal()}},[`${t}-bordered ${t}-selection-col`]:{width:m(c).add(m(a).mul(2)).equal(),[`&${t}-selection-col-with-dropdown`]:{width:m(c).add(l).add(m(o).div(4)).add(m(a).mul(2)).equal()}},[` + table tr th${t}-selection-column, + table tr td${t}-selection-column, + ${t}-selection-column + `]:{paddingInlineEnd:e.paddingXS,paddingInlineStart:e.paddingXS,textAlign:"center",[`${n}-radio-wrapper`]:{marginInlineEnd:0}},[`table tr th${t}-selection-column${t}-cell-fix-left`]:{zIndex:m(e.zIndexTableFixed).add(1).equal({unit:!1})},[`table tr th${t}-selection-column::after`]:{backgroundColor:"transparent !important"},[`${t}-selection`]:{position:"relative",display:"inline-flex",flexDirection:"column"},[`${t}-selection-extra`]:{position:"absolute",top:0,zIndex:1,cursor:"pointer",transition:`all ${e.motionDurationSlow}`,marginInlineStart:"100%",paddingInlineStart:(0,na.unit)(m(p).div(4).equal()),[r]:{color:i,fontSize:l,verticalAlign:"baseline","&:hover":{color:d}}},[`${t}-tbody`]:{[`${t}-row`]:{[`&${t}-row-selected`]:{[`> ${t}-cell`]:{background:u,"&-row-hover":{background:s}}},[`> ${t}-cell-row-hover`]:{background:f}}}}}})(D),(e=>{let{componentCls:t,lineWidth:n,colorSplit:r,motionDurationSlow:l,zIndexTableFixed:o,tableBg:a,zIndexTableSticky:i,calc:d}=e;return{[`${t}-wrapper`]:{[` + ${t}-cell-fix-left, + ${t}-cell-fix-right + `]:{position:"sticky !important",zIndex:o,background:a},[` + ${t}-cell-fix-left-first::after, + ${t}-cell-fix-left-last::after + `]:{position:"absolute",top:0,right:{_skip_check_:!0,value:0},bottom:d(n).mul(-1).equal(),width:30,transform:"translateX(100%)",transition:`box-shadow ${l}`,content:'""',pointerEvents:"none",willChange:"transform"},[`${t}-cell-fix-left-all::after`]:{display:"none"},[` + ${t}-cell-fix-right-first::after, + ${t}-cell-fix-right-last::after + `]:{position:"absolute",top:0,bottom:d(n).mul(-1).equal(),left:{_skip_check_:!0,value:0},width:30,transform:"translateX(-100%)",transition:`box-shadow ${l}`,content:'""',pointerEvents:"none"},[`${t}-container`]:{position:"relative","&::before, &::after":{position:"absolute",top:0,bottom:0,zIndex:d(i).add(1).equal({unit:!1}),width:30,transition:`box-shadow ${l}`,content:'""',pointerEvents:"none"},"&::before":{insetInlineStart:0},"&::after":{insetInlineEnd:0}},[`${t}-ping-left`]:{[`&:not(${t}-has-fix-left) ${t}-container::before`]:{boxShadow:`inset 10px 0 8px -8px ${r}`},[` + ${t}-cell-fix-left-first::after, + ${t}-cell-fix-left-last::after + `]:{boxShadow:`inset 10px 0 8px -8px ${r}`},[`${t}-cell-fix-left-last::before`]:{backgroundColor:"transparent !important"}},[`${t}-ping-right`]:{[`&:not(${t}-has-fix-right) ${t}-container::after`]:{boxShadow:`inset -10px 0 8px -8px ${r}`},[` + ${t}-cell-fix-right-first::after, + ${t}-cell-fix-right-last::after + `]:{boxShadow:`inset -10px 0 8px -8px ${r}`}},[`${t}-fixed-column-gapped`]:{[` + ${t}-cell-fix-left-first::after, + ${t}-cell-fix-left-last::after, + ${t}-cell-fix-right-first::after, + ${t}-cell-fix-right-last::after + `]:{boxShadow:"none"}}}}})(D),(e=>{let{componentCls:t,opacityLoading:n,tableScrollThumbBg:r,tableScrollThumbBgHover:l,tableScrollThumbSize:o,tableScrollBg:a,zIndexTableSticky:i,stickyScrollBarBorderRadius:d,lineWidth:c,lineType:u,tableBorderColor:s}=e,f=`${(0,na.unit)(c)} ${u} ${s}`;return{[`${t}-wrapper`]:{[`${t}-sticky`]:{"&-holder":{position:"sticky",zIndex:i,background:e.colorBgContainer},"&-scroll":{position:"sticky",bottom:0,height:`${(0,na.unit)(o)} !important`,zIndex:i,display:"flex",alignItems:"center",background:a,borderTop:f,opacity:n,"&:hover":{transformOrigin:"center bottom"},"&-bar":{height:o,backgroundColor:r,borderRadius:d,transition:`all ${e.motionDurationSlow}, transform 0s`,position:"absolute",bottom:0,"&:hover, &-active":{backgroundColor:l}}}}}}})(D),(e=>{let{componentCls:t}=e;return{[`${t}-wrapper`]:{[`${t}-cell-ellipsis`]:Object.assign(Object.assign({},nd.textEllipsis),{wordBreak:"keep-all",[` + &${t}-cell-fix-left-last, + &${t}-cell-fix-right-first + `]:{overflow:"visible",[`${t}-cell-content`]:{display:"block",overflow:"hidden",textOverflow:"ellipsis"}},[`${t}-column-title`]:{overflow:"hidden",textOverflow:"ellipsis",wordBreak:"keep-all"}})}}})(D),(e=>{let{componentCls:t,tableExpandColumnWidth:n,calc:r}=e,l=(e,l,o,a)=>({[`${t}${t}-${e}`]:{fontSize:a,[` + ${t}-title, + ${t}-footer, + ${t}-cell, + ${t}-thead > tr > th, + ${t}-tbody > tr > th, + ${t}-tbody > tr > td, + tfoot > tr > th, + tfoot > tr > td + `]:{padding:`${(0,na.unit)(l)} ${(0,na.unit)(o)}`},[`${t}-filter-trigger`]:{marginInlineEnd:(0,na.unit)(r(o).div(2).mul(-1).equal())},[`${t}-expanded-row-fixed`]:{margin:`${(0,na.unit)(r(l).mul(-1).equal())} ${(0,na.unit)(r(o).mul(-1).equal())}`},[`${t}-tbody`]:{[`${t}-wrapper:only-child ${t}`]:{marginBlock:(0,na.unit)(r(l).mul(-1).equal()),marginInline:`${(0,na.unit)(r(n).sub(o).equal())} ${(0,na.unit)(r(o).mul(-1).equal())}`}},[`${t}-selection-extra`]:{paddingInlineStart:(0,na.unit)(r(o).div(4).equal())}}});return{[`${t}-wrapper`]:Object.assign(Object.assign({},l("middle",e.tablePaddingVerticalMiddle,e.tablePaddingHorizontalMiddle,e.tableFontSizeMiddle)),l("small",e.tablePaddingVerticalSmall,e.tablePaddingHorizontalSmall,e.tableFontSizeSmall))}})(D),(e=>{let{componentCls:t}=e;return{[`${t}-wrapper-rtl`]:{direction:"rtl",table:{direction:"rtl"},[`${t}-pagination-left`]:{justifyContent:"flex-end"},[`${t}-pagination-right`]:{justifyContent:"flex-start"},[`${t}-row-expand-icon`]:{float:"right","&::after":{transform:"rotate(-90deg)"},"&-collapsed::before":{transform:"rotate(180deg)"},"&-collapsed::after":{transform:"rotate(0deg)"}},[`${t}-container`]:{"&::before":{insetInlineStart:"unset",insetInlineEnd:0},"&::after":{insetInlineStart:0,insetInlineEnd:"unset"},[`${t}-row-indent`]:{float:"right"}}}}})(D),(e=>{let{componentCls:t,motionDurationMid:n,lineWidth:r,lineType:l,tableBorderColor:o,calc:a}=e,i=`${(0,na.unit)(r)} ${l} ${o}`,d=`${t}-expanded-row-cell`;return{[`${t}-wrapper`]:{[`${t}-tbody-virtual`]:{[`${t}-tbody-virtual-holder-inner`]:{[` + & > ${t}-row, + & > div:not(${t}-row) > ${t}-row + `]:{display:"flex",boxSizing:"border-box",width:"100%"}},[`${t}-cell`]:{borderBottom:i,transition:`background ${n}`},[`${t}-expanded-row`]:{[`${d}${d}-fixed`]:{position:"sticky",insetInlineStart:0,overflow:"hidden",width:`calc(var(--virtual-width) - ${(0,na.unit)(r)})`,borderInlineEnd:"none"}}},[`${t}-bordered`]:{[`${t}-tbody-virtual`]:{"&:after":{content:'""',insetInline:0,bottom:0,borderBottom:i,position:"absolute"},[`${t}-cell`]:{borderInlineEnd:i,[`&${t}-cell-fix-right-first:before`]:{content:'""',position:"absolute",insetBlock:0,insetInlineStart:a(r).mul(-1).equal(),borderInlineStart:i}}},[`&${t}-virtual`]:{[`${t}-placeholder ${t}-cell`]:{borderInlineEnd:i,borderBottom:i}}}}}})(D)]},e=>{let{colorFillAlter:t,colorBgContainer:n,colorTextHeading:r,colorFillSecondary:l,colorFillContent:o,controlItemBgActive:a,controlItemBgActiveHover:i,padding:d,paddingSM:c,paddingXS:u,colorBorderSecondary:s,borderRadiusLG:f,controlHeight:p,colorTextPlaceholder:m,fontSize:h,fontSizeSM:g,lineHeight:v,lineWidth:y,colorIcon:b,colorIconHover:x,opacityLoading:w,controlInteractiveSize:C}=e,E=new n6.FastColor(l).onBackground(n).toHexString(),k=new n6.FastColor(o).onBackground(n).toHexString(),S=new n6.FastColor(t).onBackground(n).toHexString(),N=new n6.FastColor(b),$=new n6.FastColor(x),K=C/2-y,O=2*K+3*y;return{headerBg:S,headerColor:r,headerSortActiveBg:E,headerSortHoverBg:k,bodySortBg:S,rowHoverBg:S,rowSelectedBg:a,rowSelectedHoverBg:i,rowExpandedBg:t,cellPaddingBlock:d,cellPaddingInline:d,cellPaddingBlockMD:c,cellPaddingInlineMD:u,cellPaddingBlockSM:u,cellPaddingInlineSM:u,borderColor:s,headerBorderRadius:f,footerBg:S,footerColor:r,cellFontSize:h,cellFontSizeMD:h,cellFontSizeSM:h,headerSplitColor:s,fixedHeaderSortActiveBg:E,headerFilterHoverBg:o,filterDropdownMenuBg:n,filterDropdownBg:n,expandIconBg:n,selectionColumnWidth:p,stickyScrollBarBg:m,stickyScrollBarBorderRadius:100,expandIconMarginTop:(h*v-3*y)/2-Math.ceil((1.4*g-3*y)/2),headerIconColor:N.clone().setA(N.a*w).toRgbString(),headerIconHoverColor:$.clone().setA($.a*w).toRgbString(),expandIconHalfInner:K,expandIconSize:O,expandIconScale:C/O}},{unitless:{expandIconScale:!0}}),n9=[],re=t.forwardRef((e,n)=>{var l,o,a;let i,d,{prefixCls:c,className:u,rootClassName:s,style:f,size:p,bordered:m,dropdownPrefixCls:h,dataSource:g,pagination:v,rowSelection:y,rowKey:b="key",rowClassName:x,columns:w,children:C,childrenColumnName:k,onChange:S,getPopupContainer:N,loading:$,expandIcon:K,expandable:O,expandedRowRender:R,expandIconColumnIndex:I,indentSize:P,scroll:T,sortDirections:M,locale:D,showSorterTooltip:L={target:"full-header"},virtual:j}=e;(0,tr.devUseWarning)("Table");let B=t.useMemo(()=>w||ep(C),[w,C]),H=t.useMemo(()=>B.some(e=>e.responsive),[B]),A=(0,tb.default)(H),z=t.useMemo(()=>{let e=new Set(Object.keys(A).filter(e=>A[e]));return B.filter(t=>!t.responsive||t.responsive.some(t=>e.has(t)))},[B,A]),W=(0,eW.default)(e,["className","style","columns"]),{locale:F=tx.default,direction:_,table:q,renderEmpty:V,getPrefixCls:U,getPopupContainer:X}=t.useContext(th.ConfigContext),G=(0,ty.default)(p),Y=Object.assign(Object.assign({},F.Table),D),J=g||n9,Q=U("table",c),Z=U("dropdown",h),[,et]=(0,tE.useToken)(),en=(0,tv.default)(Q),[el,eo,ea]=n7(Q,en),ei=Object.assign(Object.assign({childrenColumnName:k,expandIconColumnIndex:I},O),{expandIcon:null!=(l=null==O?void 0:O.expandIcon)?l:null==(o=null==q?void 0:q.expandable)?void 0:o.expandIcon}),{childrenColumnName:ed="children"}=ei,ec=t.useMemo(()=>J.some(e=>null==e?void 0:e[ed])?"nest":R||(null==O?void 0:O.expandedRowRender)?"row":null,[J]),eu={body:t.useRef(null)},es=(e,t)=>{let n=e.querySelector(`.${Q}-container`),r=t;if(n){let e=getComputedStyle(n);r=t-Number.parseInt(e.borderLeftWidth,10)-Number.parseInt(e.borderRightWidth,10)}return r},ef=t.useRef(null),em=t.useRef(null);(0,t.useImperativeHandle)(n,()=>{let e=(()=>Object.assign(Object.assign({},em.current),{nativeElement:ef.current}))(),{nativeElement:t}=e;return"u">typeof Proxy?new Proxy(t,{get:(t,n)=>e[n]?e[n]:Reflect.get(t,n)}):(t._antProxy=t._antProxy||{},Object.keys(e).forEach(n=>{if(!(n in t._antProxy)){let r=t[n];t._antProxy[n]=r,t[n]=e[n]}}),t)});let eh=t.useMemo(()=>"function"==typeof b?b:e=>null==e?void 0:e[b],[b]),[eg]=(i=t.useRef({}),[function(e){var t;if(!i.current||i.current.data!==J||i.current.childrenColumnName!==ed||i.current.getRowKey!==eh){let e=new Map;!function t(n){n.forEach((n,r)=>{let l=eh(n,r);e.set(l,n),n&&"object"==typeof n&&ed in n&&t(n[ed]||[])})}(J),i.current={data:J,childrenColumnName:ed,kvMap:e,getRowKey:eh}}return null==(t=i.current.kvMap)?void 0:t.get(e)}]),ev={},ey=(e,t,n=!1)=>{var r,l,o,a;let i=Object.assign(Object.assign({},ev),e);n&&(null==(r=ev.resetPagination)||r.call(ev),(null==(l=i.pagination)?void 0:l.current)&&(i.pagination.current=1),v&&(null==(o=v.onChange)||o.call(v,1,null==(a=i.pagination)?void 0:a.pageSize))),T&&!1!==T.scrollToFirstRowOnChange&&eu.body.current&&function(e={}){let{getContainer:t=()=>window,callback:n,duration:r=450}=e,l=t(),o=(e=>{var t,n;if("u"{var e;let t,d=Date.now()-a,c=(e=d>r?r:d,t=0-o,(e/=r/2)<1?t/2*e*e*e+o:t/2*((e-=2)*e*e+2)+o);tp(l)?l.scrollTo(window.pageXOffset,c):l instanceof Document||"HTMLDocument"===l.constructor.name?l.documentElement.scrollTop=c:l.scrollTop=c,deu.body.current}),null==S||S(i.pagination,i.filters,i.sorter,{currentDataSource:nH(n2(J,i.sorterStates,ed),i.filterStates,ed),action:t})},[ex,ew,eC,eE]=(e=>{let{prefixCls:n,mergedColumns:r,sortDirections:l,tableLocale:o,showSorterTooltip:a,onSorterChange:i}=e,[d,c]=t.useState(()=>nQ(r,!0)),u=(e,t)=>{let n=[];return e.forEach((e,r)=>{let l=tS(r,t);if(n.push(tk(e,l)),Array.isArray(e.children)){let t=u(e.children,l);n.push.apply(n,(0,er.default)(t))}}),n},s=t.useMemo(()=>{let e=!0,t=nQ(r,!1);if(!t.length){let e=u(r);return d.filter(({key:t})=>e.includes(t))}let n=[];function l(t){e?n.push(t):n.push(Object.assign(Object.assign({},t),{sortOrder:null}))}let o=null;return t.forEach(t=>{null===o?(l(t),t.sortOrder&&(!1===t.multiplePriority?e=!1:o=!0)):(o&&!1!==t.multiplePriority||(e=!1),l(t))}),n},[r,d]),f=t.useMemo(()=>{var e,t;let n=s.map(({column:e,sortOrder:t})=>({column:e,order:t}));return{sortColumns:n,sortColumn:null==(e=n[0])?void 0:e.column,sortOrder:null==(t=n[0])?void 0:t.order}},[s]),p=e=>{let t;c(t=!1!==e.multiplePriority&&s.length&&!1!==s[0].multiplePriority?[].concat((0,er.default)(s.filter(({key:t})=>t!==e.key)),[e]):[e]),i(n1(t),t)};return[e=>nZ(n,e,s,p,l,o,a),s,f,()=>n1(s)]})({prefixCls:Q,mergedColumns:z,onSorterChange:(e,t)=>{ey({sorter:e,sorterStates:t},"sort",!1)},sortDirections:M||["ascend","descend"],tableLocale:Y,showSorterTooltip:L}),ek=t.useMemo(()=>n2(J,ew,ed),[J,ew]);ev.sorter=eE(),ev.sorterStates=ew;let[eS,eN,e$]=(e=>{let{prefixCls:n,dropdownPrefixCls:r,mergedColumns:l,onFilterChange:o,getPopupContainer:a,locale:i,rootClassName:d}=e;(0,tr.devUseWarning)("Table");let c=t.useMemo(()=>nA(l||[]),[l]),[u,s]=t.useState(()=>nj(c,!0)),f=t.useMemo(()=>{let e=nj(c,!1);if(0===e.length)return e;let t=!0;if(e.forEach(({filteredKeys:e})=>{void 0!==e&&(t=!1)}),t){let e=(c||[]).map((e,t)=>tk(e,tS(t)));return u.filter(({key:t})=>e.includes(t)).map(t=>{let n=c[e.indexOf(t.key)];return Object.assign(Object.assign({},t),{column:Object.assign(Object.assign({},t.column),n),forceFiltered:n.filtered})})}return e},[c,u]),p=t.useMemo(()=>nB(f),[f]),m=e=>{let t=f.filter(({key:t})=>t!==e.key);t.push(e),s(t),o(nB(t),t)};return[e=>(function e(n,r,l,o,a,i,d,c,u){return l.map((l,s)=>{let f=tS(s,c),{filterOnClose:p=!0,filterMultiple:m=!0,filterMode:h,filterSearch:g}=l,v=l;if(v.filters||v.filterDropdown){let e=tk(v,f),c=o.find(({key:t})=>e===t);v=Object.assign(Object.assign({},v),{title:o=>t.createElement(nL,{tablePrefixCls:n,prefixCls:`${n}-filter`,dropdownPrefixCls:r,column:v,columnKey:e,filterState:c,filterOnClose:p,filterMultiple:m,filterMode:h,filterSearch:g,triggerFilter:i,locale:a,getPopupContainer:d,rootClassName:u},tN(l.title,o))})}return"children"in v&&(v=Object.assign(Object.assign({},v),{children:e(n,r,v.children,o,a,i,d,f,u)})),v})})(n,r,e,f,i,m,a,void 0,d),f,p]})({prefixCls:Q,locale:Y,dropdownPrefixCls:Z,mergedColumns:z,onFilterChange:(e,t)=>{ey({filters:e,filterStates:t},"filter",!0)},getPopupContainer:N||X,rootClassName:(0,E.default)(s,en)}),eK=nH(ek,eN,ed);ev.filters=e$,ev.filterStates=eN;let[eO]=(a=t.useMemo(()=>{let e={};return Object.keys(e$).forEach(t=>{null!==e$[t]&&(e[t]=e$[t])}),Object.assign(Object.assign({},eC),{filters:e})},[eC,e$]),[t.useCallback(e=>n3(e,a),[a])]),[eR,eI]=nW(eK.length,(e,t)=>{ey({pagination:Object.assign(Object.assign({},ev.pagination),{current:e,pageSize:t})},"paginate")},v);ev.pagination=!1===v?{}:(d={current:eR.current,pageSize:eR.pageSize},Object.keys(v&&"object"==typeof v?v:{}).forEach(e=>{let t=eR[e];"function"!=typeof t&&(d[e]=t)}),d),ev.resetPagination=eI;let eP=t.useMemo(()=>{if(!1===v||!eR.pageSize)return eK;let{current:e=1,total:t,pageSize:n=10}=eR;return eK.lengthn?eK.slice((e-1)*n,e*n):eK:eK.slice((e-1)*n,e*n)},[!!v,eK,null==eR?void 0:eR.current,null==eR?void 0:eR.pageSize,null==eR?void 0:eR.total]),[eT,eM]=((e,n)=>{let{preserveSelectedRowKeys:r,selectedRowKeys:l,defaultSelectedRowKeys:o,getCheckboxProps:a,getTitleCheckboxProps:i,onChange:d,onSelect:c,onSelectAll:u,onSelectInvert:s,onSelectNone:f,onSelectMultiple:p,columnWidth:m,type:h,selections:g,fixed:v,renderCell:y,hideSelectAll:b,checkStrictly:x=!0}=n||{},{prefixCls:w,data:C,pageData:k,getRecordByKey:S,getRowKey:N,expandType:$,childrenColumnName:K,locale:O,getPopupContainer:R}=e,I=(0,tr.devUseWarning)("Table"),[P,T]=(e=>{let[n,r]=(0,t.useState)(null);return[(0,t.useCallback)((t,l,o)=>{let a=null!=n?n:t,i=Math.min(a||0,t),d=Math.max(a||0,t),c=l.slice(i,d+1).map(e),u=c.some(e=>!o.has(e)),s=[];return c.forEach(e=>{u?(o.has(e)||s.push(e),o.add(e)):(o.delete(e),s.push(e))}),r(u?d:null),s},[n]),r]})(e=>e),[M,D]=(0,tn.default)(l||o||ts,{value:l}),L=t.useRef(new Map),j=(0,t.useCallback)(e=>{if(r){let t=new Map;e.forEach(e=>{let n=S(e);!n&&L.current.has(e)&&(n=L.current.get(e)),t.set(e,n)}),L.current=t}},[S,r]);t.useEffect(()=>{j(M)},[M]);let B=(0,t.useMemo)(()=>tf(K,k),[K,k]),{keyEntities:H}=(0,t.useMemo)(()=>{if(x)return{keyEntities:null};let e=C;if(r){let t=new Set(B.map((e,t)=>N(e,t))),n=Array.from(L.current).reduce((e,[n,r])=>t.has(n)?e:e.concat(r),[]);e=[].concat((0,er.default)(e),(0,er.default)(n))}return eG(e,{externalGetKey:N,childrenPropName:K})},[C,N,x,K,r,B]),A=(0,t.useMemo)(()=>{let e=new Map;return B.forEach((t,n)=>{let r=N(t,n),l=(a?a(t):null)||{};e.set(r,l)}),e},[B,N,a]),z=(0,t.useCallback)(e=>{let t,n=N(e);return!!(null==(t=A.has(n)?A.get(N(e)):a?a(e):void 0)?void 0:t.disabled)},[A,N]),[W,F]=(0,t.useMemo)(()=>{if(x)return[M||[],[]];let{checkedKeys:e,halfCheckedKeys:t}=tt(M,!0,H,z);return[e||[],t]},[M,x,H,z]),_=(0,t.useMemo)(()=>new Set("radio"===h?W.slice(0,1):W),[W,h]),q=(0,t.useMemo)(()=>"radio"===h?new Set:new Set(F),[F,h]);t.useEffect(()=>{n||D(ts)},[!!n]);let V=(0,t.useCallback)((e,t)=>{let n,l;j(e),r?(n=e,l=e.map(e=>L.current.get(e))):(n=[],l=[],e.forEach(e=>{let t=S(e);void 0!==t&&(n.push(e),l.push(t))})),D(n),null==d||d(n,l,{type:t})},[D,S,d,r]),U=(0,t.useCallback)((e,t,n,r)=>{if(c){let l=n.map(e=>S(e));c(S(e),t,l,r)}V(n,"single")},[c,S,V]),X=(0,t.useMemo)(()=>!g||b?null:(!0===g?[td,tc,tu]:g).map(e=>e===td?{key:"all",text:O.selectionAll,onSelect(){V(C.map((e,t)=>N(e,t)).filter(e=>{let t=A.get(e);return!(null==t?void 0:t.disabled)||_.has(e)}),"all")}}:e===tc?{key:"invert",text:O.selectInvert,onSelect(){let e=new Set(_);k.forEach((t,n)=>{let r=N(t,n),l=A.get(r);(null==l?void 0:l.disabled)||(e.has(r)?e.delete(r):e.add(r))});let t=Array.from(e);s&&(I.deprecated(!1,"onSelectInvert","onChange"),s(t)),V(t,"invert")}}:e===tu?{key:"none",text:O.selectNone,onSelect(){null==f||f(),V(Array.from(_).filter(e=>{let t=A.get(e);return null==t?void 0:t.disabled}),"none")}}:e).map(e=>Object.assign(Object.assign({},e),{onSelect:(...t)=>{var n;null==(n=e.onSelect)||n.call.apply(n,[e].concat(t)),T(null)}})),[g,_,k,N,s,V]);return[(0,t.useCallback)(e=>{var r;let l,o,a;if(!n)return e.filter(e=>e!==ti);let d=(0,er.default)(e),c=new Set(_),s=B.map(N).filter(e=>!A.get(e).disabled),f=s.every(e=>c.has(e)),C=s.some(e=>c.has(e));if("radio"!==h){let e;if(X){let n={getPopupContainer:R,items:X.map((e,t)=>{let{key:n,text:r,onSelect:l}=e;return{key:null!=n?n:t,onClick:()=>{null==l||l(s)},label:r}})};e=t.createElement("div",{className:`${w}-selection-extra`},t.createElement(to.default,{menu:n,getPopupContainer:R},t.createElement("span",null,t.createElement(eB.default,null))))}let n=B.map((e,t)=>{let n=N(e,t),r=A.get(n)||{};return Object.assign({checked:c.has(n)},r)}).filter(({disabled:e})=>e),r=!!n.length&&n.length===B.length,a=r&&n.every(({checked:e})=>e),d=r&&n.some(({checked:e})=>e),p=(null==i?void 0:i())||{},{onChange:m,disabled:h}=p;o=t.createElement(tl.default,Object.assign({"aria-label":e?"Custom selection":"Select all"},p,{checked:r?a:!!B.length&&f,indeterminate:r?!a&&d:!f&&C,onChange:e=>{let t,n;t=[],f?s.forEach(e=>{c.delete(e),t.push(e)}):s.forEach(e=>{c.has(e)||(c.add(e),t.push(e))}),n=Array.from(c),null==u||u(!f,n.map(e=>S(e)),t.map(e=>S(e))),V(n,"all"),T(null),null==m||m(e)},disabled:null!=h?h:0===B.length||r,skipGroup:!0})),l=!b&&t.createElement("div",{className:`${w}-selection`},o,e)}if(a="radio"===h?(e,n,r)=>{let l=N(n,r),o=c.has(l),a=A.get(l);return{node:t.createElement(ta.default,Object.assign({},a,{checked:o,onClick:e=>{var t;e.stopPropagation(),null==(t=null==a?void 0:a.onClick)||t.call(a,e)},onChange:e=>{var t;c.has(l)||U(l,!0,[l],e.nativeEvent),null==(t=null==a?void 0:a.onChange)||t.call(a,e)}})),checked:o}}:(e,n,r)=>{var l;let o,a=N(n,r),i=c.has(a),d=q.has(a),u=A.get(a);return o="nest"===$?d:null!=(l=null==u?void 0:u.indeterminate)?l:d,{node:t.createElement(tl.default,Object.assign({},u,{indeterminate:o,checked:i,skipGroup:!0,onClick:e=>{var t;e.stopPropagation(),null==(t=null==u?void 0:u.onClick)||t.call(u,e)},onChange:e=>{var t;let{nativeEvent:n}=e,{shiftKey:r}=n,l=s.indexOf(a),o=W.some(e=>s.includes(e));if(r&&x&&o){let e=P(l,s,c),t=Array.from(c);null==p||p(!i,t.map(e=>S(e)),e.map(e=>S(e))),V(t,"multiple")}else if(x){let e=i?e2(W,a):e3(W,a);U(a,!i,e,n)}else{let{checkedKeys:e,halfCheckedKeys:t}=tt([].concat((0,er.default)(W),[a]),!0,H,z),r=e;if(i){let n=new Set(e);n.delete(a),r=tt(Array.from(n),{checked:!1,halfCheckedKeys:t},H,z).checkedKeys}U(a,!i,r,n)}i?T(null):T(l),null==(t=null==u?void 0:u.onChange)||t.call(u,e)}})),checked:i}},!d.includes(ti))if(0===d.findIndex(e=>{var t;return(null==(t=e[ee])?void 0:t.columnType)==="EXPAND_COLUMN"})){let[e,...t]=d;d=[e,ti].concat((0,er.default)(t))}else d=[ti].concat((0,er.default)(d));let k=d.indexOf(ti),K=(d=d.filter((e,t)=>e!==ti||t===k))[k-1],O=d[k+1],I=v;void 0===I&&((null==O?void 0:O.fixed)!==void 0?I=O.fixed:(null==K?void 0:K.fixed)!==void 0&&(I=K.fixed)),I&&K&&(null==(r=K[ee])?void 0:r.columnType)==="EXPAND_COLUMN"&&void 0===K.fixed&&(K.fixed=I);let M=(0,E.default)(`${w}-selection-col`,{[`${w}-selection-col-with-dropdown`]:g&&"checkbox"===h}),D={fixed:I,width:m,className:`${w}-selection-column`,title:(null==n?void 0:n.columnTitle)?"function"==typeof n.columnTitle?n.columnTitle(o):n.columnTitle:l,render:(e,t,n)=>{let{node:r,checked:l}=a(e,t,n);return y?y(l,t,n,r):r},onCell:n.onCell,align:n.align,[ee]:{className:M}};return d.map(e=>e===ti?D:e)},[N,B,n,W,_,q,m,X,$,A,p,U,z]),_]})({prefixCls:Q,data:eK,pageData:eP,getRowKey:eh,getRecordByKey:eg,expandType:ec,childrenColumnName:ed,locale:Y,getPopupContainer:N||X},y);ei.__PARENT_RENDER_ICON__=ei.expandIcon,ei.expandIcon=ei.expandIcon||K||(e=>{let{prefixCls:n,onExpand:r,record:l,expanded:o,expandable:a}=e,i=`${n}-row-expand-icon`;return t.createElement("button",{type:"button",onClick:e=>{r(l,e),e.stopPropagation()},className:(0,E.default)(i,{[`${i}-spaced`]:!a,[`${i}-expanded`]:a&&o,[`${i}-collapsed`]:a&&!o}),"aria-label":o?Y.collapse:Y.expand,"aria-expanded":o})}),"nest"===ec&&void 0===ei.expandIconColumnIndex?ei.expandIconColumnIndex=+!!y:ei.expandIconColumnIndex>0&&y&&(ei.expandIconColumnIndex-=1),"number"!=typeof ei.indentSize&&(ei.indentSize="number"==typeof P?P:15);let eD=t.useCallback(e=>eO(eT(eS(ex(e)))),[ex,eS,eT]),eL=t.useMemo(()=>"boolean"==typeof $?{spinning:$}:"object"==typeof $&&null!==$?Object.assign({spinning:!0},$):void 0,[$]),ej=(0,E.default)(ea,en,`${Q}-wrapper`,null==q?void 0:q.className,{[`${Q}-wrapper-rtl`]:"rtl"===_},u,s,eo),eH=Object.assign(Object.assign({},null==q?void 0:q.style),f),eA=t.useMemo(()=>(null==eL?void 0:eL.spinning)&&J===n9?null:void 0!==(null==D?void 0:D.emptyText)?D.emptyText:(null==V?void 0:V("Table"))||t.createElement(tg.default,{componentName:"Table"}),[null==eL?void 0:eL.spinning,J,null==D?void 0:D.emptyText,V]),ez={},eF=t.useMemo(()=>{let{fontSize:e,lineHeight:t,lineWidth:n,padding:r,paddingXS:l,paddingSM:o}=et,a=Math.floor(e*t);switch(G){case"middle":return 2*o+a+n;case"small":return 2*l+a+n;default:return 2*r+a+n}},[et,G]);j&&(ez.listItemHeight=eF);let{top:e_,bottom:eq}=(()=>{if(!1===v||!(null==eR?void 0:eR.total))return{};let e=e=>t.createElement(tw.default,Object.assign({},eR,{align:eR.align||("left"===e?"start":"right"===e?"end":e),className:(0,E.default)(`${Q}-pagination`,eR.className),size:eR.size||("small"===G||"middle"===G?"small":void 0)})),n="rtl"===_?"left":"right",r=eR.position;if(null===r||!Array.isArray(r))return{bottom:e(n)};let l=r.find(e=>"string"==typeof e&&e.toLowerCase().includes("top")),o=r.find(e=>"string"==typeof e&&e.toLowerCase().includes("bottom")),a=r.every(e=>"none"==`${e}`),i=l?l.toLowerCase().replace("top",""):"",d=o?o.toLowerCase().replace("bottom",""):"",c=!l&&!o&&!a;return{top:i?e(i):void 0,bottom:d?e(d):c?e(n):void 0}})();return el(t.createElement("div",{ref:ef,className:ej,style:eH},t.createElement(tC.default,Object.assign({spinning:!1},eL),e_,t.createElement(j?n8:n4,Object.assign({},ez,W,{ref:em,columns:z,direction:_,expandable:ei,prefixCls:Q,className:(0,E.default)({[`${Q}-middle`]:"middle"===G,[`${Q}-small`]:"small"===G,[`${Q}-bordered`]:m,[`${Q}-empty`]:0===J.length},ea,en,eo),data:eP,rowKey:eh,rowClassName:(e,t,n)=>{let r;return r="function"==typeof x?(0,E.default)(x(e,t,n)):(0,E.default)(x),(0,E.default)({[`${Q}-row-selected`]:eM.has(eh(e,t))},r)},emptyText:eA,internalHooks:r,internalRefs:eu,transformColumns:eD,getContainerWidth:es,measureRowRender:e=>t.createElement(tm.default,{getPopupContainer:e=>e},e)})),eq)))}),rt=t.forwardRef((e,n)=>{let r=t.useRef(0);return r.current+=1,t.createElement(re,Object.assign({},e,{ref:n,_renderTimes:r.current}))});rt.SELECTION_COLUMN=ti,rt.EXPAND_COLUMN=n,rt.SELECTION_ALL=td,rt.SELECTION_INVERT=tc,rt.SELECTION_NONE=tu,rt.Column=e=>null,rt.ColumnGroup=e=>null,rt.Summary=L,e.s(["Table",0,rt],291542)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0op63kdo3uwng.js b/litellm/proxy/_experimental/out/_next/static/chunks/0op63kdo3uwng.js new file mode 100644 index 00000000000..84c6377978d --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0op63kdo3uwng.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,519455,838452,540886,527930,e=>{"use strict";var t=e.i(843476),r=e.i(271645);e.i(247167);var n=e.i(229315),o=e.i(667865),i=e.i(146376),l=e.i(176782),s=e.i(733332);let a=r.createContext(void 0);function u(e=!1){let t=r.useContext(a);if(void 0===t&&!e)throw Error((0,s.default)(16));return t}function c(e={}){let{disabled:t=!1,focusableWhenDisabled:n,tabIndex:s=0,native:a=!0,composite:f}=e,g=r.useRef(null),p=u(!0),v=f??void 0!==p,{props:b}=function(e){let{focusableWhenDisabled:t,disabled:n,composite:o=!1,tabIndex:i=0,isNativeButton:l}=e,s=o&&!1!==t,a=o&&!1===t;return{props:r.useMemo(()=>{let e={onKeyDown(e){n&&t&&"Tab"!==e.key&&e.preventDefault()}};return o||(e.tabIndex=i,!l&&n&&(e.tabIndex=t?i:-1)),(l&&(t||s)||!l&&n)&&(e["aria-disabled"]=n),l&&(!t||a)&&(e.disabled=n),e},[o,n,t,s,a,l,i])}}({focusableWhenDisabled:n,disabled:t,composite:v,tabIndex:s,isNativeButton:a}),m=r.useCallback(()=>{let e=g.current;d(e)&&v&&t&&void 0===b.disabled&&e.disabled&&(e.disabled=!1)},[t,b.disabled,v]);return(0,i.useIsoLayoutEffect)(m,[m]),{getButtonProps:r.useCallback((e={})=>{let{onClick:r,onMouseDown:n,onKeyUp:o,onKeyDown:i,onPointerDown:s,...u}=e;return(0,l.mergeProps)({onClick(e){t?e.preventDefault():r?.(e)},onMouseDown(e){t||n?.(e)},onKeyDown(e){var n;if(t||((0,l.makeEventPreventable)(e),i?.(e),e.baseUIHandlerPrevented))return;let o=e.target===e.currentTarget,s=e.currentTarget,u=d(s),c=!a&&(n=s,!!(n?.tagName==="A"&&n?.href)),f=o&&(a?u:!c),g="Enter"===e.key,p=" "===e.key,b=s.getAttribute("role"),m=b?.startsWith("menuitem")||"option"===b||"gridcell"===b;if(o&&v&&p){if(e.defaultPrevented&&m)return;e.preventDefault(),c||a&&u?(s.click(),e.preventBaseUIHandler()):f&&(r?.(e),e.preventBaseUIHandler());return}f&&(!a&&(p||g)&&e.preventDefault(),!a&&g&&r?.(e))},onKeyUp(e){t||(((0,l.makeEventPreventable)(e),o?.(e),e.target===e.currentTarget&&a&&v&&d(e.currentTarget)&&" "===e.key)?e.preventDefault():!e.baseUIHandlerPrevented&&(e.target!==e.currentTarget||a||v||" "!==e.key||r?.(e)))},onPointerDown(e){t?e.preventDefault():s?.(e)}},a?{type:"button"}:{role:"button"},b,u)},[t,b,v,a]),buttonRef:(0,o.useStableCallback)(e=>{g.current=e,m()})}}function d(e){return(0,n.isHTMLElement)(e)&&"BUTTON"===e.tagName}e.s(["CompositeRootContext",0,a,"useCompositeRootContext",0,u],838452),e.s(["useButton",0,c],540886);var f=e.i(552245);let g=r.forwardRef(function(e,t){let{render:r,className:n,disabled:o=!1,focusableWhenDisabled:i=!1,nativeButton:l=!0,style:s,...a}=e,{getButtonProps:u,buttonRef:d}=c({disabled:o,focusableWhenDisabled:i,native:l});return(0,f.useRenderElement)("button",e,{state:{disabled:o},ref:[t,d],props:[a,u]})});e.s(["Button",0,g],527930);var p=e.i(115504);let v=(0,p.cva)({base:"group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/80",outline:"border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",secondary:"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",ghost:"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",destructive:"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",xs:"h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",sm:"h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",lg:"h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",icon:"size-9","icon-xs":"size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3","icon-sm":"size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md","icon-lg":"size-10"}},defaultVariants:{variant:"default",size:"default"}}),b=r.forwardRef(({className:e,variant:r="default",size:n="default",...o},i)=>(0,t.jsx)(g,{ref:i,"data-slot":"button",className:(0,p.cn)(v({variant:r,size:n,className:e})),...o}));b.displayName="Button",e.s(["Button",0,b],519455)},673327,e=>{"use strict";var t=e.i(229315);let r="ArrowUp",n="ArrowDown",o="ArrowLeft",i="ArrowRight",l="Home",s=new Set([o,i]),a=new Set([o,i,l,"End"]),u=new Set([r,n]),c=new Set([r,n,l,"End"]),d=new Set([...s,...u]),f=new Set([...d,l,"End"]),g=new Set(["Shift","Control","Alt","Meta"]);function p(e,t,r){let n="left"===r?"offsetLeft":"offsetTop",o=0;for(;t.offsetParent&&(o+=t[n],t.offsetParent!==e);)t=t.offsetParent;return o}function v(e){let t=getComputedStyle(e);return{scrollMarginTop:parseFloat(t.scrollMarginTop)||0,scrollMarginRight:parseFloat(t.scrollMarginRight)||0,scrollMarginBottom:parseFloat(t.scrollMarginBottom)||0,scrollMarginLeft:parseFloat(t.scrollMarginLeft)||0,scrollPaddingTop:parseFloat(t.scrollPaddingTop)||0,scrollPaddingRight:parseFloat(t.scrollPaddingRight)||0,scrollPaddingBottom:parseFloat(t.scrollPaddingBottom)||0,scrollPaddingLeft:parseFloat(t.scrollPaddingLeft)||0}}e.s(["ARROW_DOWN",0,n,"ARROW_KEYS",0,d,"ARROW_LEFT",0,o,"ARROW_RIGHT",0,i,"ARROW_UP",0,r,"COMPOSITE_KEYS",0,f,"END",0,"End","HOME",0,l,"HORIZONTAL_KEYS",0,s,"HORIZONTAL_KEYS_WITH_EXTRA_KEYS",0,a,"MODIFIER_KEYS",0,g,"VERTICAL_KEYS",0,u,"VERTICAL_KEYS_WITH_EXTRA_KEYS",0,c,"isNativeInput",0,function(e){return!!((0,t.isHTMLElement)(e)&&"INPUT"===e.tagName&&null!=e.selectionStart||(0,t.isHTMLElement)(e)&&"TEXTAREA"===e.tagName)},"scrollIntoViewIfNeeded",0,function(e,t,r,n){if(!e||!t||!t.scrollTo)return;let o=e.scrollLeft,i=e.scrollTop,l=e.clientWidthe.scrollLeft+e.clientWidth-i.scrollPaddingRight?o=n+t.offsetWidth+l.scrollMarginRight-e.clientWidth+i.scrollPaddingRight:n-l.scrollMarginLefte.scrollLeft+e.clientWidth-i.scrollPaddingRight&&(o=n+t.offsetWidth+l.scrollMarginRight-e.clientWidth+i.scrollPaddingRight))}if(s&&"horizontal"!==n){let r=p(e,t,"top"),n=v(e),o=v(t);r-o.scrollMarginTope.scrollTop+e.clientHeight-n.scrollPaddingBottom&&(i=r+t.offsetHeight+o.scrollMarginBottom-e.clientHeight+n.scrollPaddingBottom)}e.scrollTo({left:o,top:i,behavior:"auto"})}])},385689,e=>{"use strict";var t=e.i(271645),r=e.i(708445),n=e.i(439957),o=e.i(956789),i=e.i(647554),l=e.i(596296),s=e.i(157940),a=e.i(675606),u=e.i(56434);e.s(["useClick",0,function(e,c={}){let{enabled:d=!0,event:f="click",toggle:g=!0,ignoreMouse:p=!1,stickIfOpen:v=!0,touchOpenDelay:b=0,reason:m=u.REASONS.triggerPress}=c,h="rootStore"in e?e.rootStore:e,y=h.context.dataRef,E=t.useRef(void 0),w=(0,r.useAnimationFrame)(),R=(0,n.useTimeout)(),T=t.useMemo(()=>{function e(e,t,r,n){let o=(0,a.createChangeEventDetails)(m,t,r);e&&"touch"===n&&b>0?R.start(b,()=>{h.setOpen(!0,o)}):h.setOpen(e,o)}function t(e,t,r){let n=y.current.openEvent,o=h.select("domReferenceElement")!==t;return!!e&&!!o||!e||!g||!!n&&!!v&&!r(n.type)}return{onPointerDown(e){E.current=e.pointerType},onMouseDown(r){let n=E.current,o=r.nativeEvent,a=h.select("open");if(0!==r.button||"click"===f||(0,s.isMouseLikePointerType)(n,!0)&&p)return;let u=t(a,r.currentTarget,e=>"click"===e||"mousedown"===e),c=(0,i.getTarget)(o);if((0,l.isTypeableElement)(c))return void e(u,o,c,n);let d=r.currentTarget;w.request(()=>{e(u,o,d,n)})},onClick(r){if("mousedown-only"===f)return;let n=E.current;if("mousedown"===f&&n){E.current=void 0;return}(0,s.isMouseLikePointerType)(n,!0)&&p||e(t(h.select("open"),r.currentTarget,e=>"click"===e||"mousedown"===e||"keydown"===e||"keyup"===e),r.nativeEvent,r.currentTarget,n)},onKeyDown(){E.current=void 0}}},[y,f,p,m,h,v,g,w,R,b]);return t.useMemo(()=>d?{reference:T}:o.EMPTY_OBJECT,[d,T])}])},427803,e=>{"use strict";var t=e.i(271645);e.s(["useEnhancedClickHandler",0,function(e){let r=t.useRef(""),n=t.useCallback(t=>{t.defaultPrevented||(r.current=t.pointerType,e(t,t.pointerType))},[e]);return{onClick:t.useCallback(t=>{0===t.detail?e(t,"keyboard"):("pointerType"in t?e(t,t.pointerType):e(t,r.current),r.current="")},[e]),onPointerDown:n}}])},606039,e=>{"use strict";var t=e.i(271645),r=e.i(146376),n=e.i(667865);e.s(["useValueChanged",0,function(e,o){let i=t.useRef(e),l=(0,n.useStableCallback)(o);(0,r.useIsoLayoutEffect)(()=>{i.current!==e&&l(i.current)},[e,l]),(0,r.useIsoLayoutEffect)(()=>{i.current=e},[e])}])},32199,e=>{"use strict";var t=e.i(271645),r=e.i(667865),n=e.i(427803),o=e.i(328744),i=e.i(606039);function l(e,i){let l=(0,r.useStableCallback)((t,r)=>{("function"==typeof e?e():e)||i(r||(o.platform.os.ios?"touch":""))}),{onClick:s,onPointerDown:a}=(0,n.useEnhancedClickHandler)(l);return t.useMemo(()=>({onClick:s,onPointerDown:a}),[s,a])}e.s(["useOpenInteractionType",0,function(e){let[r,n]=t.useState(null),o=l(e,n);return(0,i.useValueChanged)(e,t=>{t&&!e&&n(null)}),t.useMemo(()=>({openMethod:r,triggerProps:o}),[r,o])},"useOpenMethodTriggerProps",0,l])},426,e=>{"use strict";var t=e.i(271645),r=e.i(843476);let n=t.forwardRef(function(e,t){let n,{cutout:o,...i}=e;if(o){let e=o.getBoundingClientRect();n=`polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${e.left}px ${e.top}px,${e.left}px ${e.bottom}px,${e.right}px ${e.bottom}px,${e.right}px ${e.top}px,${e.left}px ${e.top}px)`}return(0,r.jsx)("div",{ref:t,role:"presentation","data-base-ui-inert":"",...i,style:{position:"fixed",inset:0,userSelect:"none",WebkitUserSelect:"none",clipPath:n}})});e.s(["InternalBackdrop",0,n])},145484,e=>{"use strict";var t=e.i(229315),r=e.i(574735),n=e.i(328744),o=e.i(108868),i=e.i(333848),l=e.i(146376),s=e.i(439957),a=e.i(708445),u=e.i(956789);let c={},d={},f="";class g{lockCount=0;restore=null;timeoutLock=s.Timeout.create();timeoutUnlock=s.Timeout.create();acquire(e){return this.lockCount+=1,1===this.lockCount&&null===this.restore&&this.timeoutLock.start(0,()=>this.lock(e)),this.release}release=()=>{this.lockCount-=1,0===this.lockCount&&this.restore&&this.timeoutUnlock.start(0,this.unlock)};unlock=()=>{0===this.lockCount&&this.restore&&(this.restore?.(),this.restore=null)};lock(e){let l,s,g,p,v;if(0===this.lockCount||null!==this.restore)return;let b=(0,o.ownerDocument)(e).documentElement,m=(0,i.ownerWindow)(b).getComputedStyle(b).overflowY;if("hidden"===m||"clip"===m){this.restore=u.NOOP;return}let h=n.platform.os.ios||!function(e){if("u"0}(e);this.restore=h?(s=(l=(0,o.ownerDocument)(e)).documentElement,g=l.body,v={overflowY:(p=(0,t.isOverflowElement)(s)?s:g).style.overflowY,overflowX:p.style.overflowX},Object.assign(p.style,{overflowY:"hidden",overflowX:"hidden"}),()=>{Object.assign(p.style,v)}):function(e){let l=(0,o.ownerDocument)(e),s=l.documentElement,u=l.body,g=(0,i.ownerWindow)(s),p=0,v=0,b=!1,m=a.AnimationFrame.create();if(n.platform.engine.webkit&&(g.visualViewport?.scale??1)!==1)return()=>{};function h(){let r=g.getComputedStyle(s),n=g.getComputedStyle(u),i=(r.scrollbarGutter||"").includes("both-edges")?"stable both-edges":"stable";p=s.scrollTop,v=s.scrollLeft,c={scrollbarGutter:s.style.scrollbarGutter,overflowY:s.style.overflowY,overflowX:s.style.overflowX},f=s.style.scrollBehavior,d={position:u.style.position,height:u.style.height,width:u.style.width,boxSizing:u.style.boxSizing,overflowY:u.style.overflowY,overflowX:u.style.overflowX,scrollBehavior:u.style.scrollBehavior};let l=s.scrollHeight>s.clientHeight,a=s.scrollWidth>s.clientWidth,m="scroll"===r.overflowY||"scroll"===n.overflowY,h="scroll"===r.overflowX||"scroll"===n.overflowX,y=Math.max(0,g.innerWidth-u.clientWidth),E=Math.max(0,g.innerHeight-u.clientHeight),w=parseFloat(n.marginTop)+parseFloat(n.marginBottom),R=parseFloat(n.marginLeft)+parseFloat(n.marginRight),T=(0,t.isOverflowElement)(s)?s:u;if(b=function(e){if(!("u">typeof CSS&&CSS.supports&&CSS.supports("scrollbar-gutter","stable"))||"u"{m.cancel(),y(),"function"==typeof g.removeEventListener&&E()}}(e)}}let p=new g;e.s(["useScrollLock",0,function(e=!0,t=null){(0,l.useIsoLayoutEffect)(()=>{if(e)return p.acquire(t)},[e,t])}])},503596,e=>{"use strict";var t=e.i(956789);let r=0;e.s(["enqueueFocus",0,function(e,n={}){let{preventScroll:o=!1,sync:i=!1,shouldFocus:l}=n;function s(){(!l||l())&&e?.focus({preventScroll:o})}if(cancelAnimationFrame(r),i)return s(),t.NOOP;let a=requestAnimationFrame(s);return r=a,()=>{r===a&&(cancelAnimationFrame(a),r=0)}}])},61487,e=>{"use strict";var t=e.i(271645),r=e.i(229315),n=e.i(574735),o=e.i(365420),i=e.i(828918),l=e.i(446265),s=e.i(667865),a=e.i(146376),u=e.i(439957),c=e.i(328744),d=e.i(708445),f=e.i(108868),g=e.i(333848),p=e.i(152535),v=e.i(647554),b=e.i(596296),m=e.i(157940),h=e.i(383976),y=e.i(958408),E=e.i(621082),w=e.i(675606),R=e.i(56434),T=e.i(451321),k=e.i(503596);let x={inert:new WeakMap,"aria-hidden":new WeakMap},S="data-base-ui-inert",A={inert:new WeakSet,"aria-hidden":new WeakSet},C=new WeakMap,M=0,L=(e,t)=>t.map(t=>{if(e.contains(t))return t;let n=function e(t){return t?(0,r.isShadowRoot)(t)?t.host:e(t.parentNode):null}(t);return e.contains(n)?n:null}).filter(e=>null!=e),O=e=>{let t=new Set;return e.forEach(e=>{let r=e;for(;r&&!t.has(r);)t.add(r),r=r.parentNode}),t},P=(e,t,n)=>{let o=[],i=e=>{!e||n.has(e)||Array.from(e.children).forEach(e=>{"script"!==(0,r.getNodeName)(e)&&(t.has(e)?i(e):o.push(e))})};return i(e),o};function W(e,t={}){let{ariaHidden:r=!1,inert:n=!1,mark:o=!0}=t,i=(0,f.ownerDocument)(e[0]).body;return function(e,t,r,n,{mark:o=!0}){let i=null;n?i="inert":r&&(i="aria-hidden");let l=null,s=null,a=L(t,e),u=o?P(t,O(a),new Set(a)):[],c=[],d=[];if(i){let e=x[i],r=A[i];s=r,l=e;let n=L(t,Array.from(t.querySelectorAll("[aria-live]"))),o=a.concat(n);P(t,O(o),new Set(o)).forEach(t=>{let n=t.getAttribute(i),o=null!==n&&"false"!==n,l=(e.get(t)||0)+1;e.set(t,l),c.push(t),1===l&&o&&r.add(t),o||t.setAttribute(i,"inert"===i?"":"true")})}return o&&u.forEach(e=>{let t=(C.get(e)||0)+1;C.set(e,t),d.push(e),1===t&&e.setAttribute(S,"")}),M+=1,()=>{l&&c.forEach(e=>{let t=(l.get(e)||0)-1;l.set(e,t),t||(!s?.has(e)&&i&&e.removeAttribute(i),s?.delete(e))}),o&&d.forEach(e=>{let t=(C.get(e)||0)-1;C.set(e,t),t||e.removeAttribute(S)}),(M-=1)||(x.inert=new WeakMap,x["aria-hidden"]=new WeakMap,A.inert=new WeakSet,A["aria-hidden"]=new WeakSet,C=new WeakMap)}}(e,i,r,n,{mark:o})}var I=e.i(726674),F=e.i(46420),N=e.i(638396),D=e.i(594603),H=e.i(843476);let B=[];function Y(){B=B.filter(e=>e.deref()?.isConnected)}function _(e){Y(),e&&"body"!==(0,r.getNodeName)(e)&&(B.push(new WeakRef(e)),B.length>20&&(B=B.slice(-20)))}function K(){return Y(),B[B.length-1]?.deref()}function z(e){if(e.hasAttribute("tabindex")&&!e.hasAttribute("data-tabindex")||!e.getAttribute("role")?.includes("dialog"))return;let t=(0,h.focusable)(e).filter(e=>{let t=e.getAttribute("data-tabindex")||"";return(0,h.isTabbable)(e)||e.hasAttribute("data-tabindex")&&!t.startsWith("-")}),r=e.getAttribute("tabindex");0===t.length?"0"!==r&&(e.setAttribute("tabindex","0"),e.setAttribute("data-tabindex","0")):("-1"!==r||e.hasAttribute("data-tabindex")&&"-1"!==e.getAttribute("data-tabindex"))&&(e.setAttribute("tabindex","-1"),e.setAttribute("data-tabindex","-1"))}e.s(["FloatingFocusManager",0,function(e){let{context:x,children:S,disabled:A=!1,initialFocus:C=!0,returnFocus:M=!0,restoreFocus:L=!1,modal:O=!0,closeOnFocusOut:P=!0,openInteractionType:B="",nextFocusableElement:q,previousFocusableElement:U,beforeContentFocusGuardRef:X,externalTree:V,getInsideElements:$}=e,j="rootStore"in x?x.rootStore:x,G=j.useState("open"),Z=j.useState("domReferenceElement"),J=j.useState("floatingElement"),{events:Q,dataRef:ee}=j.context,et=(0,s.useStableCallback)(()=>ee.current.floatingContext?.nodeId),er=(0,b.isTypeableCombobox)(Z)&&!1===C,en=(0,l.useValueAsRef)(C),eo=(0,l.useValueAsRef)(M),ei=(0,l.useValueAsRef)(B),el=(0,l.useValueAsRef)(G),es=(0,F.useFloatingTree)(V),ea=(0,I.usePortalContext)(),eu=t.useRef(!1),ec=t.useRef(!1),ed=t.useRef(!1),ef=t.useRef(null),eg=t.useRef(""),ep=t.useRef(""),ev=t.useRef(null),eb=t.useRef(null),em=(0,i.useMergedRefs)(ev,X,ea?.beforeInsideRef),eh=(0,i.useMergedRefs)(eb,ea?.afterInsideRef),ey=(0,u.useTimeout)(),eE=(0,u.useTimeout)(),ew=(0,d.useAnimationFrame)(),eR=null!=ea,eT=(0,b.getFloatingFocusElement)(J),ek=(0,s.useStableCallback)((e=eT)=>e?(0,h.tabbable)(e):[]),ex=(0,s.useStableCallback)(()=>$?.().filter(e=>null!=e)??[]);t.useEffect(()=>{if(A||!O)return;let e=(0,f.ownerDocument)(eT);return(0,n.addEventListener)(e,"keydown",function(e){"Tab"===e.key&&(0,v.contains)(eT,(0,v.activeElement)((0,f.ownerDocument)(eT)))&&0===ek().length&&!er&&(0,m.stopEvent)(e)})},[A,eT,O,er,ek]),t.useEffect(()=>{if(A||!G)return;let e=(0,f.ownerDocument)(eT);function t(){ed.current=!1}return(0,o.mergeCleanups)((0,n.addEventListener)(e,"pointerdown",function(e){let t=(0,v.getTarget)(e),r=ex();ed.current=!((0,v.contains)(J,t)||(0,v.contains)(Z,t)||(0,v.contains)(ea?.portalNode,t)||r.some(e=>e===t||(0,v.contains)(e,t))),ep.current=e.pointerType||"keyboard",t?.closest(`[${N.CLICK_TRIGGER_IDENTIFIER}]`)&&(ec.current=!0,eE.start(0,()=>{ec.current=!1}))},!0),(0,n.addEventListener)(e,"pointerup",t,!0),(0,n.addEventListener)(e,"pointercancel",t,!0),(0,n.addEventListener)(e,"keydown",function(){ep.current="keyboard"},!0),t)},[A,J,Z,eT,G,ea,eE,ex]),t.useEffect(()=>{if(A||!P)return;let e=(0,f.ownerDocument)(eT);function t(t){let n=t.relatedTarget,o=t.currentTarget,i=(0,v.getTarget)(t);O&&null==n&&null!=i&&(0,v.contains)(J,i)&&_(i),queueMicrotask(()=>{let l=et(),s=j.context.triggerElements,a=ex(),u=n?.hasAttribute((0,T.createAttribute)("focus-guard"))&&[ev.current,eb.current,ea?.beforeInsideRef.current,ea?.afterInsideRef.current,ea?.beforeOutsideRef.current,ea?.afterOutsideRef.current,(0,D.resolveRef)(U),(0,D.resolveRef)(q)].includes(n),c=!((0,v.contains)(Z,n)||(0,v.contains)(J,n)||(0,v.contains)(n,J)||(0,v.contains)(ea?.portalNode,n)||a.some(e=>e===n||(0,v.contains)(e,n))||null!=n&&s.hasElement(n)||s.hasMatchingElement(e=>(0,v.contains)(e,n))||u||es&&((0,y.getNodeChildren)(es.nodesRef.current,l).find(e=>(0,v.contains)(e.context?.elements.floating,n)||(0,v.contains)(e.context?.elements.domReference,n))||(0,y.getNodeAncestors)(es.nodesRef.current,l).find(e=>[e.context?.elements.floating,(0,b.getFloatingFocusElement)(e.context?.elements.floating)].includes(n)||e.context?.elements.domReference===n)));if(o===Z&&eT&&z(eT),L&&o!==Z&&!(0,E.isElementVisible)(i)&&(0,v.activeElement)(e)===e.body){if((0,r.isHTMLElement)(eT)&&(eT.focus(),"popup"===L))return void ew.request(()=>{eT.focus()});let e=ek(),t=ef.current,n=(t&&e.includes(t)?t:null)||e[e.length-1]||eT;(0,r.isHTMLElement)(n)&&n.focus()}if(ee.current.insideReactTree){ee.current.insideReactTree=!1;return}(er||!O)&&n&&c&&!ec.current&&(er||n!==K())&&(eu.current=!0,j.setOpen(!1,(0,w.createChangeEventDetails)(R.REASONS.focusOut,t)))})}let i=(0,r.isHTMLElement)(Z)?Z:null;if(J||i)return(0,o.mergeCleanups)(i&&(0,n.addEventListener)(i,"focusout",t),i&&(0,n.addEventListener)(i,"pointerdown",function(){ec.current=!0,eE.start(0,()=>{ec.current=!1})}),J&&(0,n.addEventListener)(J,"focusin",function(e){let t=(0,v.getTarget)(e);(0,h.isTabbable)(t)&&(ef.current=t)}),J&&(0,n.addEventListener)(J,"focusout",t),J&&ea&&(0,n.addEventListener)(J,"focusout",function(){ed.current||(ee.current.insideReactTree=!0,ey.start(0,()=>{ee.current.insideReactTree=!1}))},!0))},[A,Z,J,eT,O,es,ea,j,P,L,ek,er,et,ee,ey,eE,ew,q,U,ex]),t.useEffect(()=>{if(A||!J||!G)return;let e=Array.from(ea?.portalNode?.querySelectorAll(`[${(0,T.createAttribute)("portal")}]`)||[]),t=es?(0,y.getNodeAncestors)(es.nodesRef.current,et()):[],r=t.find(e=>(0,b.isTypeableCombobox)(e.context?.elements.domReference||null))?.context?.elements.domReference,n=W([J,...e,ev.current,eb.current,ea?.beforeOutsideRef.current,ea?.afterOutsideRef.current,...ex(),r,(0,D.resolveRef)(U),(0,D.resolveRef)(q),er?Z:null].filter(e=>null!=e),{ariaHidden:O||er,mark:!1}),o=W([J,...e].filter(e=>null!=e));return()=>{o(),n()}},[G,A,Z,J,O,ea,er,es,et,q,U,ex]),(0,a.useIsoLayoutEffect)(()=>{if(!G||A||!(0,r.isHTMLElement)(eT))return;let e=(0,f.ownerDocument)(eT),t=(0,v.activeElement)(e);queueMicrotask(()=>{let r,n=en.current,o="function"==typeof n?n(ei.current||""):n;if(void 0===o||!1===o||(0,v.contains)(eT,t))return;let i=null,l=()=>(null==i&&(i=ek(eT)),i[0]||eT);r=(r=!0===o||null===o?l():(0,D.resolveRef)(o))||l();let s=(0,v.contains)(eT,(0,v.activeElement)(e));(0,k.enqueueFocus)(r,{preventScroll:r===eT,shouldFocus(){if(!el.current)return!1;if(s)return!0;let t=(0,v.activeElement)(e);return!(t!==r&&(0,v.contains)(eT,t))}})})},[A,G,eT,ek,en,ei,el]),(0,a.useIsoLayoutEffect)(()=>{if(A||!eT)return;let e=(0,f.ownerDocument)(eT),t=(0,v.activeElement)(e),n=null==ei.current;function o(e){var t,r;let n;if(e.open||(t=e.nativeEvent,r=ep.current,n=(0,g.ownerWindow)((0,v.getTarget)(t)),eg.current=t instanceof n.KeyboardEvent?"keyboard":t instanceof n.FocusEvent?r||"keyboard":"pointerType"in t?t.pointerType||"keyboard":"touches"in t?"touch":t instanceof n.MouseEvent?r||(0===t.detail?"keyboard":"mouse"):""),e.reason===R.REASONS.triggerHover&&"mouseleave"===e.nativeEvent.type&&(eu.current=!0),e.reason===R.REASONS.outsidePress)if(e.nested)eu.current=!1;else if((0,m.isVirtualClick)(e.nativeEvent)||(0,m.isVirtualPointerEvent)(e.nativeEvent))eu.current=!1;else{let e=!1;(0,f.ownerDocument)(eT).createElement("div").focus({get preventScroll(){return e=!0,!1}}),e?eu.current=!1:eu.current=!0}}return _(t),Q.on("openchange",o),()=>{Q.off("openchange",o);let i=(0,v.activeElement)(e),l=ex(),s=(0,v.contains)(J,i)||l.some(e=>e===i||(0,v.contains)(e,i))||es&&(0,y.getNodeChildren)(es.nodesRef.current,et(),!1).some(e=>(0,v.contains)(e.context?.elements.floating,i)),a=eo.current,u=function(){let e=eo.current,o="function"==typeof e?e(eg.current):e;if(void 0===o||!1===o)return null;null===o&&(o=!0);let i=Z?.isConnected?Z:null,l=t?.isConnected&&"body"!==(0,r.getNodeName)(t)?t:null,s=n?l||i:i||l;return(s||(s=K()||null),"boolean"==typeof o)?s:(0,D.resolveRef)(o)||s||null}();queueMicrotask(()=>{let t=u?(0,h.isTabbable)(u)?u:(0,h.tabbable)(u)[0]||u:null;a&&!eu.current&&(0,r.isHTMLElement)(t)&&("boolean"!=typeof a||t===i||i===e.body||s)&&t.focus({preventScroll:!0}),eu.current=!1})}},[A,J,eT,eo,ei,Q,es,Z,et,ex]),(0,a.useIsoLayoutEffect)(()=>{if(!c.platform.engine.webkit||G||!J)return;let e=(0,v.activeElement)((0,f.ownerDocument)(J));(0,r.isHTMLElement)(e)&&(0,b.isTypeableElement)(e)&&(0,v.contains)(J,e)&&e.blur()},[G,J]),(0,a.useIsoLayoutEffect)(()=>{if(!A&&ea)return ea.setFocusManagerState({modal:O,closeOnFocusOut:P,open:G,onOpenChange:j.setOpen,domReference:Z}),()=>{ea.setFocusManagerState(null)}},[A,ea,O,G,j,P,Z]),(0,a.useIsoLayoutEffect)(()=>{if(!A&&eT)return z(eT),()=>{queueMicrotask(Y)}},[A,eT]);let eS=!A&&(!O||!er)&&(eR||O);return(0,H.jsxs)(t.Fragment,{children:[eS&&(0,H.jsx)(p.FocusGuard,{"data-type":"inside",ref:em,onFocus:e=>{if(O){let e=ek();(0,k.enqueueFocus)(e[e.length-1])}else if(ea?.portalNode)if(eu.current=!1,(0,h.isOutsideEvent)(e,ea.portalNode)){let e=(0,h.getNextTabbable)(Z);e?.focus()}else(0,D.resolveRef)(U??ea.beforeOutsideRef)?.focus()}}),S,eS&&(0,H.jsx)(p.FocusGuard,{"data-type":"inside",ref:eh,onFocus:e=>{if(O)(0,k.enqueueFocus)(ek()[0]);else if(ea?.portalNode)if(P&&(eu.current=!0),(0,h.isOutsideEvent)(e,ea.portalNode)){let e=(0,h.getPreviousTabbable)(Z);e?.focus()}else(0,D.resolveRef)(q??ea.afterOutsideRef)?.focus()}})]})}],61487)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0ovrnw54dbivd.js b/litellm/proxy/_experimental/out/_next/static/chunks/0ovrnw54dbivd.js new file mode 100644 index 00000000000..18c294b0ebb --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0ovrnw54dbivd.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,94629,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,i],94629)},728889,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(829087),o=e.i(480731),n=e.i(444755),a=e.i(673706),l=e.i(95779);let s={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},c={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},d={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},m=(0,a.makeClassName)("Icon"),u=i.default.forwardRef((e,u)=>{let{icon:g,variant:p="simple",tooltip:f,size:h=o.Sizes.SM,color:b,className:v}=e,$=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),C=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,a.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,a.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,a.getColorClassNames)(t,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,a.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,a.getColorClassNames)(t,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,a.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,n.tremorTwMerge)((0,a.getColorClassNames)(t,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,a.getColorClassNames)(t,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,n.tremorTwMerge)((0,a.getColorClassNames)(t,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,a.getColorClassNames)(t,l.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,n.tremorTwMerge)((0,a.getColorClassNames)(t,l.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(p,b),{tooltipProps:A,getReferenceProps:I}=(0,r.useTooltip)();return i.default.createElement("span",Object.assign({ref:(0,a.mergeRefs)([u,A.refs.setReference]),className:(0,n.tremorTwMerge)(m("root"),"inline-flex shrink-0 items-center justify-center",C.bgColor,C.textColor,C.borderColor,C.ringColor,d[p].rounded,d[p].border,d[p].shadow,d[p].ring,s[h].paddingX,s[h].paddingY,v)},I,$),i.default.createElement(r.default,Object.assign({text:f},A)),i.default.createElement(g,{className:(0,n.tremorTwMerge)(m("icon"),"shrink-0",c[h].height,c[h].width)}))});u.displayName="Icon",e.s(["default",0,u],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,i],278587)},591935,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,i],591935)},434626,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,i],434626)},551332,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,i],551332)},122577,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,i],122577)},902555,e=>{"use strict";var t=e.i(843476),i=e.i(591935),r=e.i(122577),o=e.i(278587),n=e.i(68155),a=e.i(360820),l=e.i(871943),s=e.i(434626),c=e.i(551332),d=e.i(592968),m=e.i(115504),u=e.i(752978);function g({icon:e,onClick:i,className:r,disabled:o,dataTestId:n}){return o?(0,t.jsx)(u.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":n}):(0,t.jsx)(u.Icon,{icon:e,size:"sm",onClick:i,className:(0,m.cx)("cursor-pointer",r),"data-testid":n})}let p={Edit:{icon:i.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:n.TrashIcon,className:"hover:text-red-600"},Test:{icon:r.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:o.RefreshIcon,className:"hover:text-green-600"},Up:{icon:a.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:l.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:s.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:c.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:i,disabled:r=!1,disabledTooltipText:o,dataTestId:n,variant:a}){let{icon:l,className:s}=p[a];return(0,t.jsx)(d.Tooltip,{title:r?o:i,children:(0,t.jsx)("span",{children:(0,t.jsx)(g,{icon:l,onClick:e,className:s,disabled:r,dataTestId:n})})})}],902555)},755151,e=>{"use strict";var t=e.i(247153);e.s(["DownOutlined",()=>t.default])},916925,e=>{"use strict";var t,i=e.i(555987),r=((t={}).A2A_Agent="A2A Agent",t.AI21="Ai21",t.AI21_CHAT="Ai21 Chat",t.AIML="AI/ML API",t.AIOHTTP_OPENAI="Aiohttp Openai",t.Anthropic="Anthropic",t.ANTHROPIC_TEXT="Anthropic Text",t.AssemblyAI="AssemblyAI",t.AUTO_ROUTER="Auto Router",t.Bedrock="Amazon Bedrock",t.BedrockMantle="Amazon Bedrock Mantle",t.SageMaker="AWS SageMaker",t.Azure="Azure",t.Azure_AI_Studio="Azure AI Foundry (Studio)",t.AZURE_TEXT="Azure Text",t.BASETEN="Baseten",t.BYTEZ="Bytez",t.Cerebras="Cerebras",t.CLARIFAI="Clarifai",t.CLOUDFLARE="Cloudflare",t.CODESTRAL="Codestral",t.Cohere="Cohere",t.COHERE_CHAT="Cohere Chat",t.COMETAPI="Cometapi",t.COMPACTIFAI="Compactifai",t.Cursor="Cursor",t.Dashscope="Dashscope",t.Databricks="Databricks (Qwen API)",t.DATAROBOT="Datarobot",t.DeepInfra="DeepInfra",t.Deepgram="Deepgram",t.Deepseek="Deepseek",t.DOCKER_MODEL_RUNNER="Docker Model Runner",t.DOTPROMPT="Dotprompt",t.ElevenLabs="ElevenLabs",t.EMPOWER="Empower",t.FalAI="Fal AI",t.FEATHERLESS_AI="Featherless Ai",t.FireworksAI="Fireworks AI",t.FRIENDLIAI="Friendliai",t.GALADRIEL="Galadriel",t.GITHUB_COPILOT="Github Copilot",t.Google_AI_Studio="Google AI Studio",t.GradientAI="GradientAI",t.Groq="Groq",t.HEROKU="Heroku",t.Hosted_Vllm="vllm",t.HUGGINGFACE="Huggingface",t.HYPERBOLIC="Hyperbolic",t.Infinity="Infinity",t.JinaAI="Jina AI",t.LAMBDA_AI="Lambda Ai",t.LEMONADE="Lemonade",t.LLAMAFILE="Llamafile",t.LM_STUDIO="Lm Studio",t.LLAMA="Meta Llama",t.MARITALK="Maritalk",t.MiniMax="MiniMax",t.MistralAI="Mistral AI",t.MOONSHOT="Moonshot",t.MORPH="Morph",t.NEBIUS="Nebius",t.NLP_CLOUD="Nlp Cloud",t.NOVITA="Novita",t.NSCALE="Nscale",t.NVIDIA_NIM="Nvidia Nim",t.Ollama="Ollama",t.OLLAMA_CHAT="Ollama Chat",t.OOBABOOGA="Oobabooga",t.OpenAI="OpenAI",t.OPENAI_LIKE="Openai Like",t.OpenAI_Compatible="OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)",t.OpenAI_Text="OpenAI Text Completion",t.OpenAI_Text_Compatible="OpenAI-Compatible Completions (legacy /v1/completions)",t.Openrouter="Openrouter",t.Oracle="Oracle Cloud Infrastructure (OCI)",t.OVHCLOUD="Ovhcloud",t.Perplexity="Perplexity",t.PETALS="Petals",t.PG_VECTOR="Pg Vector",t.PREDIBASE="Predibase",t.RECRAFT="Recraft",t.REPLICATE="Replicate",t.RunwayML="RunwayML",t.SAGEMAKER_LEGACY="Sagemaker",t.Sambanova="Sambanova",t.SAP="SAP Generative AI Hub",t.Snowflake="Snowflake",t.Soniox="Soniox",t.TEXT_COMPLETION_CODESTRAL="Text-Completion-Codestral",t.TogetherAI="TogetherAI",t.TOPAZ="Topaz",t.Triton="Triton",t.V0="V0",t.VERCEL_AI_GATEWAY="Vercel Ai Gateway",t.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",t.VERTEX_AI_BETA="Vertex Ai Beta",t.VLLM="Vllm",t.VolcEngine="VolcEngine",t.Voyage="Voyage AI",t.WANDB="Wandb",t.WATSONX="Watsonx",t.WATSONX_TEXT="Watsonx Text",t.xAI="xAI",t.XINFERENCE="Xinference",t.ZAI="Z.AI (Zhipu AI)",t);let o={A2A_Agent:"a2a_agent",AI21:"ai21",AI21_CHAT:"ai21_chat",AIML:"aiml",AIOHTTP_OPENAI:"aiohttp_openai",Anthropic:"anthropic",ANTHROPIC_TEXT:"anthropic_text",AssemblyAI:"assemblyai",AUTO_ROUTER:"auto_router",Azure:"azure",Azure_AI_Studio:"azure_ai",AZURE_TEXT:"azure_text",BASETEN:"baseten",Bedrock:"bedrock",BedrockMantle:"bedrock_mantle",BYTEZ:"bytez",Cerebras:"cerebras",CLARIFAI:"clarifai",CLOUDFLARE:"cloudflare",CODESTRAL:"codestral",Cohere:"cohere",COHERE_CHAT:"cohere_chat",COMETAPI:"cometapi",COMPACTIFAI:"compactifai",Cursor:"cursor",Dashscope:"dashscope",Databricks:"databricks",DATAROBOT:"datarobot",DeepInfra:"deepinfra",Deepgram:"deepgram",Deepseek:"deepseek",DOCKER_MODEL_RUNNER:"docker_model_runner",DOTPROMPT:"dotprompt",ElevenLabs:"elevenlabs",EMPOWER:"empower",FalAI:"fal_ai",FEATHERLESS_AI:"featherless_ai",FireworksAI:"fireworks_ai",FRIENDLIAI:"friendliai",GALADRIEL:"galadriel",GITHUB_COPILOT:"github_copilot",Google_AI_Studio:"gemini",GradientAI:"gradient_ai",Groq:"groq",HEROKU:"heroku",Hosted_Vllm:"hosted_vllm",HUGGINGFACE:"huggingface",HYPERBOLIC:"hyperbolic",Infinity:"infinity",JinaAI:"jina_ai",LAMBDA_AI:"lambda_ai",LEMONADE:"lemonade",LLAMAFILE:"llamafile",LLAMA:"meta_llama",LM_STUDIO:"lm_studio",MARITALK:"maritalk",MiniMax:"minimax",MistralAI:"mistral",MOONSHOT:"moonshot",MORPH:"morph",NEBIUS:"nebius",NLP_CLOUD:"nlp_cloud",NOVITA:"novita",NSCALE:"nscale",NVIDIA_NIM:"nvidia_nim",Ollama:"ollama",OLLAMA_CHAT:"ollama_chat",OOBABOOGA:"oobabooga",OpenAI:"openai",OPENAI_LIKE:"openai_like",OpenAI_Compatible:"openai",OpenAI_Text:"text-completion-openai",OpenAI_Text_Compatible:"text-completion-openai",Openrouter:"openrouter",Oracle:"oci",OVHCLOUD:"ovhcloud",Perplexity:"perplexity",PETALS:"petals",PG_VECTOR:"pg_vector",PREDIBASE:"predibase",RECRAFT:"recraft",REPLICATE:"replicate",RunwayML:"runwayml",SAGEMAKER_LEGACY:"sagemaker",SageMaker:"sagemaker_chat",Sambanova:"sambanova",SAP:"sap",Snowflake:"snowflake",Soniox:"soniox",TEXT_COMPLETION_CODESTRAL:"text-completion-codestral",TogetherAI:"together_ai",TOPAZ:"topaz",Triton:"triton",V0:"v0",VERCEL_AI_GATEWAY:"vercel_ai_gateway",Vertex_AI:"vertex_ai",VERTEX_AI_BETA:"vertex_ai_beta",VLLM:"vllm",VolcEngine:"volcengine",Voyage:"voyage",WANDB:"wandb",WATSONX:"watsonx",WATSONX_TEXT:"watsonx_text",xAI:"xai",XINFERENCE:"xinference",ZAI:"zai"},n=new Set(["bedrock_mantle"]),a="/ui/assets/logos/",l={"A2A Agent":`${a}a2a_agent.png`,Ai21:`${a}ai21.svg`,"Ai21 Chat":`${a}ai21.svg`,"AI/ML API":`${a}aiml_api.svg`,"Aiohttp Openai":`${a}openai_small.svg`,Anthropic:`${a}anthropic.svg`,"Anthropic Text":`${a}anthropic.svg`,AssemblyAI:`${a}assemblyai_small.png`,Azure:`${a}microsoft_azure.svg`,"Azure AI Foundry (Studio)":`${a}microsoft_azure.svg`,"Azure Text":`${a}microsoft_azure.svg`,Baseten:`${a}baseten.svg`,"Amazon Bedrock":`${a}bedrock.svg`,"Amazon Bedrock Mantle":`${a}bedrock.svg`,"AWS SageMaker":`${a}bedrock.svg`,Cerebras:`${a}cerebras.svg`,Cloudflare:`${a}cloudflare.svg`,Codestral:`${a}mistral.svg`,Cohere:`${a}cohere.svg`,"Cohere Chat":`${a}cohere.svg`,Cometapi:`${a}cometapi.svg`,Cursor:`${a}cursor.svg`,"Databricks (Qwen API)":`${a}databricks.svg`,Dashscope:`${a}dashscope.svg`,Deepseek:`${a}deepseek.svg`,Deepgram:`${a}deepgram.png`,DeepInfra:`${a}deepinfra.png`,ElevenLabs:`${a}elevenlabs.png`,"Fal AI":`${a}fal_ai.jpg`,"Featherless Ai":`${a}featherless.svg`,"Fireworks AI":`${a}fireworks.svg`,Friendliai:`${a}friendli.svg`,"Github Copilot":`${a}github_copilot.svg`,"Google AI Studio":`${a}google.svg`,GradientAI:`${a}gradientai.svg`,Groq:`${a}groq.svg`,vllm:`${a}vllm.png`,Huggingface:`${a}huggingface.svg`,Hyperbolic:`${a}hyperbolic.svg`,Infinity:`${a}infinity.png`,"Jina AI":`${a}jina.png`,"Lambda Ai":`${a}lambda.svg`,"Lm Studio":`${a}lmstudio.svg`,"Meta Llama":`${a}meta_llama.svg`,MiniMax:`${a}minimax.svg`,"Mistral AI":`${a}mistral.svg`,Moonshot:`${a}moonshot.svg`,Morph:`${a}morph.svg`,Nebius:`${a}nebius.svg`,Novita:`${a}novita.svg`,"Nvidia Nim":`${a}nvidia_nim.svg`,Ollama:`${a}ollama.svg`,"Ollama Chat":`${a}ollama.svg`,Oobabooga:`${a}openai_small.svg`,OpenAI:`${a}openai_small.svg`,"Openai Like":`${a}openai_small.svg`,"OpenAI Text Completion":`${a}openai_small.svg`,"OpenAI-Compatible Completions (legacy /v1/completions)":`${a}openai_small.svg`,"OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)":`${a}openai_small.svg`,Openrouter:`${a}openrouter.svg`,"Oracle Cloud Infrastructure (OCI)":`${a}oracle.svg`,Perplexity:`${a}perplexity-ai.svg`,Recraft:`${a}recraft.svg`,Replicate:`${a}replicate.svg`,RunwayML:`${a}runwayml.png`,Sagemaker:`${a}bedrock.svg`,Sambanova:`${a}sambanova.svg`,"SAP Generative AI Hub":`${a}sap.png`,Snowflake:`${a}snowflake.svg`,Soniox:`${a}soniox.svg`,"Text-Completion-Codestral":`${a}mistral.svg`,TogetherAI:`${a}togetherai.svg`,Topaz:`${a}topaz.svg`,Triton:`${a}nvidia_triton.png`,V0:`${a}v0.svg`,"Vercel Ai Gateway":`${a}vercel.svg`,"Vertex AI (Anthropic, Gemini, etc.)":`${a}google.svg`,"Vertex Ai Beta":`${a}google.svg`,Vllm:`${a}vllm.png`,VolcEngine:`${a}volcengine.png`,"Voyage AI":`${a}voyage.webp`,Watsonx:`${a}watsonx.svg`,"Watsonx Text":`${a}watsonx.svg`,xAI:`${a}xai.svg`,Xinference:`${a}xinference.svg`};e.s(["Providers",()=>r,"getPlaceholder",0,e=>{if("AI/ML API"===e)return"aiml/flux-pro/v1.1";if("Vertex AI (Anthropic, Gemini, etc.)"===e)return"gemini-pro";if("Anthropic"==e)return"claude-3-opus";if("Amazon Bedrock"==e)return"claude-3-opus";if("AWS SageMaker"==e)return"sagemaker/jumpstart-dft-meta-textgeneration-llama-2-7b";else if("Google AI Studio"==e)return"gemini-pro";else if("Azure AI Foundry (Studio)"==e)return"azure_ai/command-r-plus";else if("Azure"==e)return"my-deployment";else if("Oracle Cloud Infrastructure (OCI)"==e)return"oci/xai.grok-4";else if("Snowflake"==e)return"snowflake/mistral-7b";else if("Voyage AI"==e)return"voyage/";else if("Jina AI"==e)return"jina_ai/";else if("VolcEngine"==e)return"volcengine/";else if("DeepInfra"==e)return"deepinfra/";else if("Fal AI"==e)return"fal_ai/fal-ai/flux-pro/v1.1-ultra";else if("RunwayML"==e)return"runwayml/gen4_turbo";else if("Watsonx"===e)return"watsonx/ibm/granite-3-3-8b-instruct";else if("Cursor"===e)return"cursor/claude-4-sonnet";else if("Z.AI (Zhipu AI)"===e)return"zai/glm-4.5";else return"gpt-3.5-turbo"},"getProviderLogoAndName",0,e=>{if(!e)return{logo:"",displayName:"-"};if("gemini"===e.toLowerCase()){let e="Google AI Studio";return{logo:(0,i.resolveLogoSrc)(l[e])??"",displayName:e}}let t=Object.keys(o).find(t=>o[t].toLowerCase()===e.toLowerCase())??Object.keys(o).find(t=>t.toLowerCase()===e.toLowerCase());if(!t)return{logo:"",displayName:e};let n=r[t];return{logo:(0,i.resolveLogoSrc)(l[n])??"",displayName:n}},"getProviderModels",0,(e,t)=>{let i=o[e],r=[];return e&&"object"==typeof t&&(Object.entries(t).forEach(([e,t])=>{if(null!==t&&"object"==typeof t&&"litellm_provider"in t){let o=t.litellm_provider,a="string"==typeof o&&(o.startsWith(`${i}_`)||o.startsWith(`${i}-`));(o===i||a&&!n.has(o))&&r.push(e)}}),"Cohere"==e&&Object.entries(t).forEach(([e,t])=>{null!==t&&"object"==typeof t&&"litellm_provider"in t&&"cohere_chat"===t.litellm_provider&&r.push(e)}),"AWS SageMaker"==e&&Object.entries(t).forEach(([e,t])=>{null!==t&&"object"==typeof t&&"litellm_provider"in t&&"sagemaker_chat"===t.litellm_provider&&r.push(e)})),r},"providerLogoMap",0,l,"provider_map",0,o])},166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},492030,e=>{"use strict";var t=e.i(121229);e.s(["CheckOutlined",()=>t.default])},447566,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:r}))});e.s(["ArrowLeftOutlined",0,n],447566)},292639,e=>{"use strict";var t=e.i(602869),i=e.i(266027);let r=(0,e.i(243652).createQueryKeys)("uiSettings");e.s(["useUISettings",0,()=>(0,i.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,t.getUiSettings)(),staleTime:36e5,gcTime:36e5})])},829672,836938,310730,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(343794),r=e.i(914949),o=e.i(404948);let n=e=>e?"function"==typeof e?e():e:null;e.s(["getRenderPropValue",0,n],836938);var a=e.i(613541),l=e.i(763731),s=e.i(242064),c=e.i(491816);e.i(793154);var d=e.i(880476),m=e.i(183293),u=e.i(717356),g=e.i(320560),p=e.i(307358),f=e.i(246422),h=e.i(838378),b=e.i(617933);let v=(0,f.genStyleHooks)("Popover",e=>{let{colorBgElevated:t,colorText:i}=e,r=(0,h.mergeToken)(e,{popoverBg:t,popoverColor:i});return[(e=>{let{componentCls:t,popoverColor:i,titleMinWidth:r,fontWeightStrong:o,innerPadding:n,boxShadowSecondary:a,colorTextHeading:l,borderRadiusLG:s,zIndexPopup:c,titleMarginBottom:d,colorBgElevated:u,popoverBg:p,titleBorderBottom:f,innerContentPadding:h,titlePadding:b}=e;return[{[t]:Object.assign(Object.assign({},(0,m.resetComponent)(e)),{position:"absolute",top:0,left:{_skip_check_:!0,value:0},zIndex:c,fontWeight:"normal",whiteSpace:"normal",textAlign:"start",cursor:"auto",userSelect:"text","--valid-offset-x":"var(--arrow-offset-horizontal, var(--arrow-x))",transformOrigin:"var(--valid-offset-x, 50%) var(--arrow-y, 50%)","--antd-arrow-background-color":u,width:"max-content",maxWidth:"100vw","&-rtl":{direction:"rtl"},"&-hidden":{display:"none"},[`${t}-content`]:{position:"relative"},[`${t}-inner`]:{backgroundColor:p,backgroundClip:"padding-box",borderRadius:s,boxShadow:a,padding:n},[`${t}-title`]:{minWidth:r,marginBottom:d,color:l,fontWeight:o,borderBottom:f,padding:b},[`${t}-inner-content`]:{color:i,padding:h}})},(0,g.default)(e,"var(--antd-arrow-background-color)"),{[`${t}-pure`]:{position:"relative",maxWidth:"none",margin:e.sizePopupArrow,display:"inline-block",[`${t}-content`]:{display:"inline-block"}}}]})(r),(e=>{let{componentCls:t}=e;return{[t]:b.PresetColors.map(i=>{let r=e[`${i}6`];return{[`&${t}-${i}`]:{"--antd-arrow-background-color":r,[`${t}-inner`]:{backgroundColor:r},[`${t}-arrow`]:{background:"transparent"}}}})}})(r),(0,u.initZoomMotion)(r,"zoom-big")]},e=>{let{lineWidth:t,controlHeight:i,fontHeight:r,padding:o,wireframe:n,zIndexPopupBase:a,borderRadiusLG:l,marginXS:s,lineType:c,colorSplit:d,paddingSM:m}=e,u=i-r;return Object.assign(Object.assign(Object.assign({titleMinWidth:177,zIndexPopup:a+30},(0,p.getArrowToken)(e)),(0,g.getArrowOffsetToken)({contentRadius:l,limitVerticalRadius:!0})),{innerPadding:12*!n,titleMarginBottom:n?0:s,titlePadding:n?`${u/2}px ${o}px ${u/2-t}px`:0,titleBorderBottom:n?`${t}px ${c} ${d}`:"none",innerContentPadding:n?`${m}px ${o}px`:0})},{resetStyle:!1,deprecatedTokens:[["width","titleMinWidth"],["minWidth","titleMinWidth"]]});var $=function(e,t){var i={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(i[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,r=Object.getOwnPropertySymbols(e);ot.indexOf(r[o])&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(i[r[o]]=e[r[o]]);return i};let C=({title:e,content:i,prefixCls:r})=>e||i?t.createElement(t.Fragment,null,e&&t.createElement("div",{className:`${r}-title`},e),i&&t.createElement("div",{className:`${r}-inner-content`},i)):null,A=e=>{let{hashId:r,prefixCls:o,className:a,style:l,placement:s="top",title:c,content:m,children:u}=e,g=n(c),p=n(m),f=(0,i.default)(r,o,`${o}-pure`,`${o}-placement-${s}`,a);return t.createElement("div",{className:f,style:l},t.createElement("div",{className:`${o}-arrow`}),t.createElement(d.Popup,Object.assign({},e,{className:r,prefixCls:o}),u||t.createElement(C,{prefixCls:o,title:g,content:p})))},I=e=>{let{prefixCls:r,className:o}=e,n=$(e,["prefixCls","className"]),{getPrefixCls:a}=t.useContext(s.ConfigContext),l=a("popover",r),[c,d,m]=v(l);return c(t.createElement(A,Object.assign({},n,{prefixCls:l,hashId:d,className:(0,i.default)(o,m)})))};e.s(["Overlay",0,C,"default",0,I],310730);var w=function(e,t){var i={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(i[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,r=Object.getOwnPropertySymbols(e);ot.indexOf(r[o])&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(i[r[o]]=e[r[o]]);return i};let x=t.forwardRef((e,d)=>{var m,u;let{prefixCls:g,title:p,content:f,overlayClassName:h,placement:b="top",trigger:$="hover",children:A,mouseEnterDelay:I=.1,mouseLeaveDelay:x=.1,onOpenChange:k,overlayStyle:S={},styles:O,classNames:y}=e,E=w(e,["prefixCls","title","content","overlayClassName","placement","trigger","children","mouseEnterDelay","mouseLeaveDelay","onOpenChange","overlayStyle","styles","classNames"]),{getPrefixCls:T,className:N,style:_,classNames:L,styles:M}=(0,s.useComponentConfig)("popover"),R=T("popover",g),[P,j,z]=v(R),H=T(),B=(0,i.default)(h,j,z,N,L.root,null==y?void 0:y.root),D=(0,i.default)(L.body,null==y?void 0:y.body),[W,q]=(0,r.default)(!1,{value:null!=(m=e.open)?m:e.visible,defaultValue:null!=(u=e.defaultOpen)?u:e.defaultVisible}),X=(e,t)=>{q(e,!0),null==k||k(e,t)},G=n(p),V=n(f);return P(t.createElement(c.default,Object.assign({placement:b,trigger:$,mouseEnterDelay:I,mouseLeaveDelay:x},E,{prefixCls:R,classNames:{root:B,body:D},styles:{root:Object.assign(Object.assign(Object.assign(Object.assign({},M.root),_),S),null==O?void 0:O.root),body:Object.assign(Object.assign({},M.body),null==O?void 0:O.body)},ref:d,open:W,onOpenChange:e=>{X(e)},overlay:G||V?t.createElement(C,{prefixCls:R,title:G,content:V}):null,transitionName:(0,a.getTransitionName)(H,"zoom-big",E.transitionName),"data-popover-inject":!0}),(0,l.cloneElement)(A,{onKeyDown:e=>{var i,r;(0,t.isValidElement)(A)&&(null==(r=null==A?void 0:(i=A.props).onKeyDown)||r.call(i,e)),e.keyCode===o.default.ESC&&X(!1,e)}})))});x._InternalPanelDoNotUseOrYouWillBeFired=I,e.s(["default",0,x],829672)},282786,e=>{"use strict";var t=e.i(829672);e.s(["Popover",()=>t.default])},771674,e=>{"use strict";e.i(247167);var t=e.i(931067),i=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"}}]},name:"user",theme:"outlined"};var o=e.i(9583),n=i.forwardRef(function(e,n){return i.createElement(o.default,(0,t.default)({},e,{ref:n,icon:r}))});e.s(["UserOutlined",0,n],771674)},502547,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,i],502547)},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},68155,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,i],68155)},269200,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),n=i.default.forwardRef((e,n)=>{let{children:a,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement("div",{className:(0,r.tremorTwMerge)(o("root"),"overflow-auto",l)},i.default.createElement("table",Object.assign({ref:n,className:(0,r.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),a))});n.displayName="Table",e.s(["Table",0,n],269200)},942232,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),n=i.default.forwardRef((e,n)=>{let{children:a,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("tbody",Object.assign({ref:n,className:(0,r.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",l)},s),a))});n.displayName="TableBody",e.s(["TableBody",0,n],942232)},977572,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),n=i.default.forwardRef((e,n)=>{let{children:a,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("td",Object.assign({ref:n,className:(0,r.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",l)},s),a))});n.displayName="TableCell",e.s(["TableCell",0,n],977572)},427612,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),n=i.default.forwardRef((e,n)=>{let{children:a,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("thead",Object.assign({ref:n,className:(0,r.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",l)},s),a))});n.displayName="TableHead",e.s(["TableHead",0,n],427612)},64848,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),n=i.default.forwardRef((e,n)=>{let{children:a,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("th",Object.assign({ref:n,className:(0,r.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",l)},s),a))});n.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,n],64848)},496020,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),n=i.default.forwardRef((e,n)=>{let{children:a,className:l}=e,s=(0,t.__rest)(e,["children","className"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("tr",Object.assign({ref:n,className:(0,r.tremorTwMerge)(o("row"),l)},s),a))});n.displayName="TableRow",e.s(["TableRow",0,n],496020)},360820,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,i],360820)},871943,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,i],871943)},389083,e=>{"use strict";var t=e.i(290571),i=e.i(271645),r=e.i(829087),o=e.i(480731),n=e.i(95779),a=e.i(444755),l=e.i(673706);let s={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},c={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},d=(0,l.makeClassName)("Badge"),m=i.default.forwardRef((e,m)=>{let{color:u,icon:g,size:p=o.Sizes.SM,tooltip:f,className:h,children:b}=e,v=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),$=g||null,{tooltipProps:C,getReferenceProps:A}=(0,r.useTooltip)();return i.default.createElement("span",Object.assign({ref:(0,l.mergeRefs)([m,C.refs.setReference]),className:(0,a.tremorTwMerge)(d("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",u?(0,a.tremorTwMerge)((0,l.getColorClassNames)(u,n.colorPalette.background).bgColor,(0,l.getColorClassNames)(u,n.colorPalette.iconText).textColor,(0,l.getColorClassNames)(u,n.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,a.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),s[p].paddingX,s[p].paddingY,s[p].fontSize,h)},A,v),i.default.createElement(r.default,Object.assign({text:f},C)),$?i.default.createElement($,{className:(0,a.tremorTwMerge)(d("icon"),"shrink-0 -ml-1 mr-1.5",c[p].height,c[p].width)}):null,i.default.createElement("span",{className:(0,a.tremorTwMerge)(d("text"),"whitespace-nowrap")},b))});m.displayName="Badge",e.s(["Badge",0,m],389083)},250980,e=>{"use strict";var t=e.i(271645);let i=t.forwardRef(function(e,i){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:i},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,i],250980)},928685,e=>{"use strict";var t=e.i(38953);e.s(["SearchOutlined",()=>t.default])},280898,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(121229),r=e.i(864517),o=e.i(343794),n=e.i(931067),a=e.i(209428),l=e.i(211577),s=e.i(703923),c=e.i(404948),d=["className","prefixCls","style","active","status","iconPrefix","icon","wrapperStyle","stepNumber","disabled","description","title","subTitle","progressDot","stepIcon","tailContent","icons","stepIndex","onStepClick","onClick","render"];function m(e){return"string"==typeof e}let u=function(e){var i,r,u,g,p,f=e.className,h=e.prefixCls,b=e.style,v=e.active,$=e.status,C=e.iconPrefix,A=e.icon,I=(e.wrapperStyle,e.stepNumber),w=e.disabled,x=e.description,k=e.title,S=e.subTitle,O=e.progressDot,y=e.stepIcon,E=e.tailContent,T=e.icons,N=e.stepIndex,_=e.onStepClick,L=e.onClick,M=e.render,R=(0,s.default)(e,d),P={};_&&!w&&(P.role="button",P.tabIndex=0,P.onClick=function(e){null==L||L(e),_(N)},P.onKeyDown=function(e){var t=e.which;(t===c.default.ENTER||t===c.default.SPACE)&&_(N)});var j=$||"wait",z=(0,o.default)("".concat(h,"-item"),"".concat(h,"-item-").concat(j),f,(p={},(0,l.default)(p,"".concat(h,"-item-custom"),A),(0,l.default)(p,"".concat(h,"-item-active"),v),(0,l.default)(p,"".concat(h,"-item-disabled"),!0===w),p)),H=(0,a.default)({},b),B=t.createElement("div",(0,n.default)({},R,{className:z,style:H}),t.createElement("div",(0,n.default)({onClick:L},P,{className:"".concat(h,"-item-container")}),t.createElement("div",{className:"".concat(h,"-item-tail")},E),t.createElement("div",{className:"".concat(h,"-item-icon")},(u=(0,o.default)("".concat(h,"-icon"),"".concat(C,"icon"),(i={},(0,l.default)(i,"".concat(C,"icon-").concat(A),A&&m(A)),(0,l.default)(i,"".concat(C,"icon-check"),!A&&"finish"===$&&(T&&!T.finish||!T)),(0,l.default)(i,"".concat(C,"icon-cross"),!A&&"error"===$&&(T&&!T.error||!T)),i)),g=t.createElement("span",{className:"".concat(h,"-icon-dot")}),r=O?"function"==typeof O?t.createElement("span",{className:"".concat(h,"-icon")},O(g,{index:I-1,status:$,title:k,description:x})):t.createElement("span",{className:"".concat(h,"-icon")},g):A&&!m(A)?t.createElement("span",{className:"".concat(h,"-icon")},A):T&&T.finish&&"finish"===$?t.createElement("span",{className:"".concat(h,"-icon")},T.finish):T&&T.error&&"error"===$?t.createElement("span",{className:"".concat(h,"-icon")},T.error):A||"finish"===$||"error"===$?t.createElement("span",{className:u}):t.createElement("span",{className:"".concat(h,"-icon")},I),y&&(r=y({index:I-1,status:$,title:k,description:x,node:r})),r)),t.createElement("div",{className:"".concat(h,"-item-content")},t.createElement("div",{className:"".concat(h,"-item-title")},k,S&&t.createElement("div",{title:"string"==typeof S?S:void 0,className:"".concat(h,"-item-subtitle")},S)),x&&t.createElement("div",{className:"".concat(h,"-item-description")},x))));return M&&(B=M(B)||null),B};var g=["prefixCls","style","className","children","direction","type","labelPlacement","iconPrefix","status","size","current","progressDot","stepIcon","initial","icons","onChange","itemRender","items"];function p(e){var i,r=e.prefixCls,c=void 0===r?"rc-steps":r,d=e.style,m=void 0===d?{}:d,p=e.className,f=(e.children,e.direction),h=e.type,b=void 0===h?"default":h,v=e.labelPlacement,$=e.iconPrefix,C=void 0===$?"rc":$,A=e.status,I=void 0===A?"process":A,w=e.size,x=e.current,k=void 0===x?0:x,S=e.progressDot,O=e.stepIcon,y=e.initial,E=void 0===y?0:y,T=e.icons,N=e.onChange,_=e.itemRender,L=e.items,M=(0,s.default)(e,g),R="inline"===b,P=R||void 0!==S&&S,j=R||void 0===f?"horizontal":f,z=R?void 0:w,H=(0,o.default)(c,"".concat(c,"-").concat(j),p,(i={},(0,l.default)(i,"".concat(c,"-").concat(z),z),(0,l.default)(i,"".concat(c,"-label-").concat(P?"vertical":void 0===v?"horizontal":v),"horizontal"===j),(0,l.default)(i,"".concat(c,"-dot"),!!P),(0,l.default)(i,"".concat(c,"-navigation"),"navigation"===b),(0,l.default)(i,"".concat(c,"-inline"),R),i)),B=function(e){N&&k!==e&&N(e)};return t.default.createElement("div",(0,n.default)({className:H,style:m},M),(void 0===L?[]:L).filter(function(e){return e}).map(function(e,i){var r=(0,a.default)({},e),o=E+i;return"error"===I&&i===k-1&&(r.className="".concat(c,"-next-error")),r.status||(o===k?r.status=I:o{let i=`${t.componentCls}-item`,r=`${e}IconColor`,o=`${e}TitleColor`,n=`${e}DescriptionColor`,a=`${e}TailColor`,l=`${e}IconBgColor`,s=`${e}IconBorderColor`,c=`${e}DotColor`;return{[`${i}-${e} ${i}-icon`]:{backgroundColor:t[l],borderColor:t[s],[`> ${t.componentCls}-icon`]:{color:t[r],[`${t.componentCls}-icon-dot`]:{background:t[c]}}},[`${i}-${e}${i}-custom ${i}-icon`]:{[`> ${t.componentCls}-icon`]:{color:t[c]}},[`${i}-${e} > ${i}-container > ${i}-content > ${i}-title`]:{color:t[o],"&::after":{backgroundColor:t[a]}},[`${i}-${e} > ${i}-container > ${i}-content > ${i}-description`]:{color:t[n]},[`${i}-${e} > ${i}-container > ${i}-tail::after`]:{backgroundColor:t[a]}}},k=(0,I.genStyleHooks)("Steps",e=>{let{colorTextDisabled:t,controlHeightLG:i,colorTextLightSolid:r,colorText:o,colorPrimary:n,colorTextDescription:a,colorTextQuaternary:l,colorError:s,colorBorderSecondary:c,colorSplit:d}=e;return(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,A.resetComponent)(e)),{display:"flex",width:"100%",fontSize:0,textAlign:"initial"}),(e=>{let{componentCls:t,motionDurationSlow:i}=e,r=`${t}-item`,o=`${r}-icon`;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:{position:"relative",display:"inline-block",flex:1,overflow:"hidden",verticalAlign:"top","&:last-child":{flex:"none",[`> ${r}-container > ${r}-tail, > ${r}-container > ${r}-content > ${r}-title::after`]:{display:"none"}}},[`${r}-container`]:{outline:"none",[`&:focus-visible ${o}`]:(0,A.genFocusOutline)(e)},[`${o}, ${r}-content`]:{display:"inline-block",verticalAlign:"top"},[o]:{width:e.iconSize,height:e.iconSize,marginTop:0,marginBottom:0,marginInlineStart:0,marginInlineEnd:e.marginXS,fontSize:e.iconFontSize,fontFamily:e.fontFamily,lineHeight:(0,C.unit)(e.iconSize),textAlign:"center",borderRadius:e.iconSize,border:`${(0,C.unit)(e.lineWidth)} ${e.lineType} transparent`,transition:`background-color ${i}, border-color ${i}`,[`${t}-icon`]:{position:"relative",top:e.iconTop,color:e.colorPrimary,lineHeight:1}},[`${r}-tail`]:{position:"absolute",top:e.calc(e.iconSize).div(2).equal(),insetInlineStart:0,width:"100%","&::after":{display:"inline-block",width:"100%",height:e.lineWidth,background:e.colorSplit,borderRadius:e.lineWidth,transition:`background ${i}`,content:'""'}},[`${r}-title`]:{position:"relative",display:"inline-block",paddingInlineEnd:e.padding,color:e.colorText,fontSize:e.fontSizeLG,lineHeight:(0,C.unit)(e.titleLineHeight),"&::after":{position:"absolute",top:e.calc(e.titleLineHeight).div(2).equal(),insetInlineStart:"100%",display:"block",width:9999,height:e.lineWidth,background:e.processTailColor,content:'""'}},[`${r}-subtitle`]:{display:"inline",marginInlineStart:e.marginXS,color:e.colorTextDescription,fontWeight:"normal",fontSize:e.fontSize},[`${r}-description`]:{color:e.colorTextDescription,fontSize:e.fontSize}},x("wait",e)),x("process",e)),{[`${r}-process > ${r}-container > ${r}-title`]:{fontWeight:e.fontWeightStrong}}),x("finish",e)),x("error",e)),{[`${r}${t}-next-error > ${t}-item-title::after`]:{background:e.colorError},[`${r}-disabled`]:{cursor:"not-allowed"}})})(e)),(e=>{let{componentCls:t,motionDurationSlow:i}=e;return{[`& ${t}-item`]:{[`&:not(${t}-item-active)`]:{[`& > ${t}-item-container[role='button']`]:{cursor:"pointer",[`${t}-item`]:{[`&-title, &-subtitle, &-description, &-icon ${t}-icon`]:{transition:`color ${i}`}},"&:hover":{[`${t}-item`]:{"&-title, &-subtitle, &-description":{color:e.colorPrimary}}}},[`&:not(${t}-item-process)`]:{[`& > ${t}-item-container[role='button']:hover`]:{[`${t}-item`]:{"&-icon":{borderColor:e.colorPrimary,[`${t}-icon`]:{color:e.colorPrimary}}}}}}},[`&${t}-horizontal:not(${t}-label-vertical)`]:{[`${t}-item`]:{paddingInlineStart:e.padding,whiteSpace:"nowrap","&:first-child":{paddingInlineStart:0},[`&:last-child ${t}-item-title`]:{paddingInlineEnd:0},"&-tail":{display:"none"},"&-description":{maxWidth:e.descriptionMaxWidth,whiteSpace:"normal"}}}}})(e)),(e=>{let{componentCls:t,customIconTop:i,customIconSize:r,customIconFontSize:o}=e;return{[`${t}-item-custom`]:{[`> ${t}-item-container > ${t}-item-icon`]:{height:"auto",background:"none",border:0,[`> ${t}-icon`]:{top:i,width:r,height:r,fontSize:o,lineHeight:(0,C.unit)(r)}}},[`&:not(${t}-vertical)`]:{[`${t}-item-custom`]:{[`${t}-item-icon`]:{width:"auto",background:"none"}}}}})(e)),(e=>{let{componentCls:t,iconSizeSM:i,fontSizeSM:r,fontSize:o,colorTextDescription:n}=e;return{[`&${t}-small`]:{[`&${t}-horizontal:not(${t}-label-vertical) ${t}-item`]:{paddingInlineStart:e.paddingSM,"&:first-child":{paddingInlineStart:0}},[`${t}-item-icon`]:{width:i,height:i,marginTop:0,marginBottom:0,marginInline:`0 ${(0,C.unit)(e.marginXS)}`,fontSize:r,lineHeight:(0,C.unit)(i),textAlign:"center",borderRadius:i},[`${t}-item-title`]:{paddingInlineEnd:e.paddingSM,fontSize:o,lineHeight:(0,C.unit)(i),"&::after":{top:e.calc(i).div(2).equal()}},[`${t}-item-description`]:{color:n,fontSize:o},[`${t}-item-tail`]:{top:e.calc(i).div(2).sub(e.paddingXXS).equal()},[`${t}-item-custom ${t}-item-icon`]:{width:"inherit",height:"inherit",lineHeight:"inherit",background:"none",border:0,borderRadius:0,[`> ${t}-icon`]:{fontSize:i,lineHeight:(0,C.unit)(i),transform:"none"}}}}})(e)),(e=>{let{componentCls:t,iconSizeSM:i,iconSize:r}=e;return{[`&${t}-vertical`]:{display:"flex",flexDirection:"column",[`> ${t}-item`]:{display:"block",flex:"1 0 auto",paddingInlineStart:0,overflow:"visible",[`${t}-item-icon`]:{float:"left",marginInlineEnd:e.margin},[`${t}-item-content`]:{display:"block",minHeight:e.calc(e.controlHeight).mul(1.5).equal(),overflow:"hidden"},[`${t}-item-title`]:{lineHeight:(0,C.unit)(r)},[`${t}-item-description`]:{paddingBottom:e.paddingSM}},[`> ${t}-item > ${t}-item-container > ${t}-item-tail`]:{position:"absolute",top:0,insetInlineStart:e.calc(r).div(2).sub(e.lineWidth).equal(),width:e.lineWidth,height:"100%",padding:`${(0,C.unit)(e.calc(e.marginXXS).mul(1.5).add(r).equal())} 0 ${(0,C.unit)(e.calc(e.marginXXS).mul(1.5).equal())}`,"&::after":{width:e.lineWidth,height:"100%"}},[`> ${t}-item:not(:last-child) > ${t}-item-container > ${t}-item-tail`]:{display:"block"},[` > ${t}-item > ${t}-item-container > ${t}-item-content > ${t}-item-title`]:{"&::after":{display:"none"}},[`&${t}-small ${t}-item-container`]:{[`${t}-item-tail`]:{position:"absolute",top:0,insetInlineStart:e.calc(i).div(2).sub(e.lineWidth).equal(),padding:`${(0,C.unit)(e.calc(e.marginXXS).mul(1.5).add(i).equal())} 0 ${(0,C.unit)(e.calc(e.marginXXS).mul(1.5).equal())}`},[`${t}-item-title`]:{lineHeight:(0,C.unit)(i)}}}}})(e)),(e=>{let{componentCls:t}=e,i=`${t}-item`;return{[`${t}-horizontal`]:{[`${i}-tail`]:{transform:"translateY(-50%)"}}}})(e)),(e=>{let{componentCls:t,iconSize:i,lineHeight:r,iconSizeSM:o}=e;return{[`&${t}-label-vertical`]:{[`${t}-item`]:{overflow:"visible","&-tail":{marginInlineStart:e.calc(i).div(2).add(e.controlHeightLG).equal(),padding:`0 ${(0,C.unit)(e.paddingLG)}`},"&-content":{display:"block",width:e.calc(i).div(2).add(e.controlHeightLG).mul(2).equal(),marginTop:e.marginSM,textAlign:"center"},"&-icon":{display:"inline-block",marginInlineStart:e.controlHeightLG},"&-title":{paddingInlineEnd:0,paddingInlineStart:0,"&::after":{display:"none"}},"&-subtitle":{display:"block",marginBottom:e.marginXXS,marginInlineStart:0,lineHeight:r}},[`&${t}-small:not(${t}-dot)`]:{[`${t}-item`]:{"&-icon":{marginInlineStart:e.calc(i).sub(o).div(2).add(e.controlHeightLG).equal()}}}}}})(e)),(e=>{let{componentCls:t,descriptionMaxWidth:i,lineHeight:r,dotCurrentSize:o,dotSize:n,motionDurationSlow:a}=e;return{[`&${t}-dot, &${t}-dot${t}-small`]:{[`${t}-item`]:{"&-title":{lineHeight:r},"&-tail":{top:e.calc(e.dotSize).sub(e.calc(e.lineWidth).mul(3).equal()).div(2).equal(),width:"100%",marginTop:0,marginBottom:0,marginInline:`${(0,C.unit)(e.calc(i).div(2).equal())} 0`,padding:0,"&::after":{width:`calc(100% - ${(0,C.unit)(e.calc(e.marginSM).mul(2).equal())})`,height:e.calc(e.lineWidth).mul(3).equal(),marginInlineStart:e.marginSM}},"&-icon":{width:n,height:n,marginInlineStart:e.calc(e.descriptionMaxWidth).sub(n).div(2).equal(),paddingInlineEnd:0,lineHeight:(0,C.unit)(n),background:"transparent",border:0,[`${t}-icon-dot`]:{position:"relative",float:"left",width:"100%",height:"100%",borderRadius:100,transition:`all ${a}`,"&::after":{position:"absolute",top:e.calc(e.marginSM).mul(-1).equal(),insetInlineStart:e.calc(n).sub(e.calc(e.controlHeightLG).mul(1.5).equal()).div(2).equal(),width:e.calc(e.controlHeightLG).mul(1.5).equal(),height:e.controlHeight,background:"transparent",content:'""'}}},"&-content":{width:i},[`&-process ${t}-item-icon`]:{position:"relative",top:e.calc(n).sub(o).div(2).equal(),width:o,height:o,lineHeight:(0,C.unit)(o),background:"none",marginInlineStart:e.calc(e.descriptionMaxWidth).sub(o).div(2).equal()},[`&-process ${t}-icon`]:{[`&:first-child ${t}-icon-dot`]:{insetInlineStart:0}}}},[`&${t}-vertical${t}-dot`]:{[`${t}-item-icon`]:{marginTop:e.calc(e.controlHeight).sub(n).div(2).equal(),marginInlineStart:0,background:"none"},[`${t}-item-process ${t}-item-icon`]:{marginTop:e.calc(e.controlHeight).sub(o).div(2).equal(),top:0,insetInlineStart:e.calc(n).sub(o).div(2).equal(),marginInlineStart:0},[`${t}-item > ${t}-item-container > ${t}-item-tail`]:{top:e.calc(e.controlHeight).sub(n).div(2).equal(),insetInlineStart:0,margin:0,padding:`${(0,C.unit)(e.calc(n).add(e.paddingXS).equal())} 0 ${(0,C.unit)(e.paddingXS)}`,"&::after":{marginInlineStart:e.calc(n).sub(e.lineWidth).div(2).equal()}},[`&${t}-small`]:{[`${t}-item-icon`]:{marginTop:e.calc(e.controlHeightSM).sub(n).div(2).equal()},[`${t}-item-process ${t}-item-icon`]:{marginTop:e.calc(e.controlHeightSM).sub(o).div(2).equal()},[`${t}-item > ${t}-item-container > ${t}-item-tail`]:{top:e.calc(e.controlHeightSM).sub(n).div(2).equal()}},[`${t}-item:first-child ${t}-icon-dot`]:{insetInlineStart:0},[`${t}-item-content`]:{width:"inherit"}}}})(e)),(e=>{let{componentCls:t,navContentMaxWidth:i,navArrowColor:r,stepsNavActiveColor:o,motionDurationSlow:n}=e;return{[`&${t}-navigation`]:{paddingTop:e.paddingSM,[`&${t}-small`]:{[`${t}-item`]:{"&-container":{marginInlineStart:e.calc(e.marginSM).mul(-1).equal()}}},[`${t}-item`]:{overflow:"visible",textAlign:"center","&-container":{display:"inline-block",height:"100%",marginInlineStart:e.calc(e.margin).mul(-1).equal(),paddingBottom:e.paddingSM,textAlign:"start",transition:`opacity ${n}`,[`${t}-item-content`]:{maxWidth:i},[`${t}-item-title`]:Object.assign(Object.assign({maxWidth:"100%",paddingInlineEnd:0},A.textEllipsis),{"&::after":{display:"none"}})},[`&:not(${t}-item-active)`]:{[`${t}-item-container[role='button']`]:{cursor:"pointer","&:hover":{opacity:.85}}},"&:last-child":{flex:1,"&::after":{display:"none"}},"&::after":{position:"absolute",top:`calc(50% - ${(0,C.unit)(e.calc(e.paddingSM).div(2).equal())})`,insetInlineStart:"100%",display:"inline-block",width:e.fontSizeIcon,height:e.fontSizeIcon,borderTop:`${(0,C.unit)(e.lineWidth)} ${e.lineType} ${r}`,borderBottom:"none",borderInlineStart:"none",borderInlineEnd:`${(0,C.unit)(e.lineWidth)} ${e.lineType} ${r}`,transform:"translateY(-50%) translateX(-50%) rotate(45deg)",content:'""'},"&::before":{position:"absolute",bottom:0,insetInlineStart:"50%",display:"inline-block",width:0,height:e.lineWidthBold,backgroundColor:o,transition:`width ${n}, inset-inline-start ${n}`,transitionTimingFunction:"ease-out",content:'""'}},[`${t}-item${t}-item-active::before`]:{insetInlineStart:0,width:"100%"}},[`&${t}-navigation${t}-vertical`]:{[`> ${t}-item`]:{marginInlineEnd:0,"&::before":{display:"none"},[`&${t}-item-active::before`]:{top:0,insetInlineEnd:0,insetInlineStart:"unset",display:"block",width:e.calc(e.lineWidth).mul(3).equal(),height:`calc(100% - ${(0,C.unit)(e.marginLG)})`},"&::after":{position:"relative",insetInlineStart:"50%",display:"block",width:e.calc(e.controlHeight).mul(.25).equal(),height:e.calc(e.controlHeight).mul(.25).equal(),marginBottom:e.marginXS,textAlign:"center",transform:"translateY(-50%) translateX(-50%) rotate(135deg)"},"&:last-child":{"&::after":{display:"none"}},[`> ${t}-item-container > ${t}-item-tail`]:{visibility:"hidden"}}},[`&${t}-navigation${t}-horizontal`]:{[`> ${t}-item > ${t}-item-container > ${t}-item-tail`]:{visibility:"hidden"}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-rtl`]:{direction:"rtl",[`${t}-item`]:{"&-subtitle":{float:"left"}},[`&${t}-navigation`]:{[`${t}-item::after`]:{transform:"rotate(-45deg)"}},[`&${t}-vertical`]:{[`> ${t}-item`]:{"&::after":{transform:"rotate(225deg)"},[`${t}-item-icon`]:{float:"right"}}},[`&${t}-dot`]:{[`${t}-item-icon ${t}-icon-dot, &${t}-small ${t}-item-icon ${t}-icon-dot`]:{float:"right"}}}}})(e)),(e=>{let{antCls:t,componentCls:i,iconSize:r,iconSizeSM:o,processIconColor:n,marginXXS:a,lineWidthBold:l,lineWidth:s,paddingXXS:c}=e,d=e.calc(r).add(e.calc(l).mul(4).equal()).equal(),m=e.calc(o).add(e.calc(e.lineWidth).mul(4).equal()).equal();return{[`&${i}-with-progress`]:{[`${i}-item`]:{paddingTop:c,[`&-process ${i}-item-container ${i}-item-icon ${i}-icon`]:{color:n}},[`&${i}-vertical > ${i}-item `]:{paddingInlineStart:c,[`> ${i}-item-container > ${i}-item-tail`]:{top:a,insetInlineStart:e.calc(r).div(2).sub(s).add(c).equal()}},[`&, &${i}-small`]:{[`&${i}-horizontal ${i}-item:first-child`]:{paddingBottom:c,paddingInlineStart:c}},[`&${i}-small${i}-vertical > ${i}-item > ${i}-item-container > ${i}-item-tail`]:{insetInlineStart:e.calc(o).div(2).sub(s).add(c).equal()},[`&${i}-label-vertical ${i}-item ${i}-item-tail`]:{top:e.calc(r).div(2).add(c).equal()},[`${i}-item-icon`]:{position:"relative",[`${t}-progress`]:{position:"absolute",insetInlineStart:"50%",top:"50%",transform:"translate(-50%, -50%)","&-inner":{width:`${(0,C.unit)(d)} !important`,height:`${(0,C.unit)(d)} !important`}}},[`&${i}-small`]:{[`&${i}-label-vertical ${i}-item ${i}-item-tail`]:{top:e.calc(o).div(2).add(c).equal()},[`${i}-item-icon ${t}-progress-inner`]:{width:`${(0,C.unit)(m)} !important`,height:`${(0,C.unit)(m)} !important`}}}}})(e)),(e=>{let{componentCls:t,inlineDotSize:i,inlineTitleColor:r,inlineTailColor:o}=e,n=e.calc(e.paddingXS).add(e.lineWidth).equal(),a={[`${t}-item-container ${t}-item-content ${t}-item-title`]:{color:r}};return{[`&${t}-inline`]:{width:"auto",display:"inline-flex",[`${t}-item`]:{flex:"none","&-container":{padding:`${(0,C.unit)(n)} ${(0,C.unit)(e.paddingXXS)} 0`,margin:`0 ${(0,C.unit)(e.calc(e.marginXXS).div(2).equal())}`,borderRadius:e.borderRadiusSM,cursor:"pointer",transition:`background-color ${e.motionDurationMid}`,"&:hover":{background:e.controlItemBgHover},"&[role='button']:hover":{opacity:1}},"&-icon":{width:i,height:i,marginInlineStart:`calc(50% - ${(0,C.unit)(e.calc(i).div(2).equal())})`,[`> ${t}-icon`]:{top:0},[`${t}-icon-dot`]:{borderRadius:e.calc(e.fontSizeSM).div(4).equal(),"&::after":{display:"none"}}},"&-content":{width:"auto",marginTop:e.calc(e.marginXS).sub(e.lineWidth).equal()},"&-title":{color:r,fontSize:e.fontSizeSM,lineHeight:e.lineHeightSM,fontWeight:"normal",marginBottom:e.calc(e.marginXXS).div(2).equal()},"&-description":{display:"none"},"&-tail":{marginInlineStart:0,top:e.calc(i).div(2).add(n).equal(),transform:"translateY(-50%)","&:after":{width:"100%",height:e.lineWidth,borderRadius:0,marginInlineStart:0,background:o}},[`&:first-child ${t}-item-tail`]:{width:"50%",marginInlineStart:"50%"},[`&:last-child ${t}-item-tail`]:{display:"block",width:"50%"},"&-wait":Object.assign({[`${t}-item-icon ${t}-icon ${t}-icon-dot`]:{backgroundColor:e.colorBorderBg,border:`${(0,C.unit)(e.lineWidth)} ${e.lineType} ${o}`}},a),"&-finish":Object.assign({[`${t}-item-tail::after`]:{backgroundColor:o},[`${t}-item-icon ${t}-icon ${t}-icon-dot`]:{backgroundColor:o,border:`${(0,C.unit)(e.lineWidth)} ${e.lineType} ${o}`}},a),"&-error":a,"&-active, &-process":Object.assign({[`${t}-item-icon`]:{width:i,height:i,marginInlineStart:`calc(50% - ${(0,C.unit)(e.calc(i).div(2).equal())})`,top:0}},a),[`&:not(${t}-item-active) > ${t}-item-container[role='button']:hover`]:{[`${t}-item-title`]:{color:r}}}}}})(e))}})((0,w.mergeToken)(e,{processIconColor:r,processTitleColor:o,processDescriptionColor:o,processIconBgColor:n,processIconBorderColor:n,processDotColor:n,processTailColor:d,waitTitleColor:a,waitDescriptionColor:a,waitTailColor:d,waitDotColor:t,finishIconColor:n,finishTitleColor:o,finishDescriptionColor:a,finishTailColor:n,finishDotColor:n,errorIconColor:r,errorTitleColor:s,errorDescriptionColor:s,errorTailColor:d,errorIconBgColor:s,errorIconBorderColor:s,errorDotColor:s,stepsNavActiveColor:n,stepsProgressSize:i,inlineDotSize:6,inlineTitleColor:l,inlineTailColor:c}))},e=>({titleLineHeight:e.controlHeight,customIconSize:e.controlHeight,customIconTop:0,customIconFontSize:e.controlHeightSM,iconSize:e.controlHeight,iconTop:-.5,iconFontSize:e.fontSize,iconSizeSM:e.fontSizeHeading3,dotSize:e.controlHeight/4,dotCurrentSize:e.controlHeightLG/4,navArrowColor:e.colorTextDisabled,navContentMaxWidth:"unset",descriptionMaxWidth:140,waitIconColor:e.wireframe?e.colorTextDisabled:e.colorTextLabel,waitIconBgColor:e.wireframe?e.colorBgContainer:e.colorFillContent,waitIconBorderColor:e.wireframe?e.colorTextDisabled:"transparent",finishIconBgColor:e.wireframe?e.colorBgContainer:e.controlItemBgActive,finishIconBorderColor:e.wireframe?e.colorPrimary:e.controlItemBgActive}));var S=e.i(876556),O=function(e,t){var i={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(i[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,r=Object.getOwnPropertySymbols(e);ot.indexOf(r[o])&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(i[r[o]]=e[r[o]]);return i};let y=e=>{var n,a;let{percent:l,size:s,className:c,rootClassName:d,direction:m,items:u,responsive:g=!0,current:C=0,children:A,style:I}=e,w=O(e,["percent","size","className","rootClassName","direction","items","responsive","current","children","style"]),{xs:x}=(0,b.default)(g),{getPrefixCls:y,direction:E,className:T,style:N}=(0,f.useComponentConfig)("steps"),_=t.useMemo(()=>g&&x?"vertical":m,[g,x,m]),L=(0,h.default)(s),M=y("steps",e.prefixCls),[R,P,j]=k(M),z="inline"===e.type,H=y("",e.iconPrefix),B=(n=u,a=A,n?n:(0,S.default)(a).map(e=>{if(t.isValidElement(e)){let{props:t}=e;return Object.assign({},t)}return null}).filter(e=>e)),D=z?void 0:l,W=Object.assign(Object.assign({},N),I),q=(0,o.default)(T,{[`${M}-rtl`]:"rtl"===E,[`${M}-with-progress`]:void 0!==D},c,d,P,j),X={finish:t.createElement(i.default,{className:`${M}-finish-icon`}),error:t.createElement(r.default,{className:`${M}-error-icon`})};return R(t.createElement(p,Object.assign({icons:X},w,{style:W,current:C,size:L,items:B,itemRender:z?(e,i)=>e.description?t.createElement($.default,{title:e.description},i):i:void 0,stepIcon:({node:e,status:i})=>"process"===i&&void 0!==D?t.createElement("div",{className:`${M}-progress-icon`},t.createElement(v.default,{type:"circle",percent:D,size:"small"===L?32:40,strokeWidth:4,format:()=>null}),e):e,direction:_,prefixCls:M,iconPrefix:H,className:q})))};y.Step=p.Step,e.s(["Steps",0,y],280898)},86408,e=>{"use strict";var t=e.i(843476),i=e.i(271645),r=e.i(618566),o=e.i(934879);function n(){let e=(0,r.useSearchParams)().get("key"),[n,a]=(0,i.useState)(null);return(0,i.useEffect)(()=>{e&&a(e)},[e]),(0,t.jsx)(o.default,{accessToken:n,publicPage:!0,premiumUser:!1,userRole:null})}e.s(["default",0,function(){return(0,t.jsx)(i.Suspense,{fallback:(0,t.jsx)("div",{className:"flex items-center justify-center min-h-screen",children:"Loading..."}),children:(0,t.jsx)(n,{})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0p2cacg05iprd.js b/litellm/proxy/_experimental/out/_next/static/chunks/0p2cacg05iprd.js new file mode 100644 index 00000000000..a26ce9d9748 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0p2cacg05iprd.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),s=e.i(529681);let l=e=>{let{prefixCls:a,className:s,style:l,size:n,shape:i}=e,o=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),c=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),d=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,o,c,s),style:Object.assign(Object.assign({},d),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),o=e.i(246422),c=e.i(838378);let d=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),m=e=>({height:e,lineHeight:(0,i.unit)(e)}),u=e=>Object.assign({width:e},m(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},m(e)),p=e=>Object.assign({width:e},m(e)),x=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},h=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},m(e)),f=(0,o.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:s,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:o,controlHeightLG:c,controlHeightSM:m,gradientFromColor:f,padding:b,marginSM:v,borderRadius:j,titleHeight:w,blockRadius:N,paragraphLiHeight:y,controlHeightXS:k,paragraphMarginTop:$}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:b,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:f},u(o)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},u(c)),[`${r}-sm`]:Object.assign({},u(m))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:w,background:f,borderRadius:N,[`+ ${s}`]:{marginBlockStart:m}},[s]:{padding:0,"> li":{width:"100%",height:y,listStyle:"none",background:f,borderRadius:N,"+ li":{marginBlockStart:k}}},[`${s}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${s} > li`]:{borderRadius:j}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:v,[`+ ${s}`]:{marginBlockStart:$}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},h(a,i))},x(e,a,r)),{[`${r}-lg`]:Object.assign({},h(s,i))}),x(e,s,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},h(l,i))}),x(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:s,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},u(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},u(s)),[`${t}${t}-sm`]:Object.assign({},u(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:s,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},g(t,i)),[`${a}-lg`]:Object.assign({},g(s,i)),[`${a}-sm`]:Object.assign({},g(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:s,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:s},p(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},p(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${s} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:d,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,c.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),b=e=>{let{prefixCls:a,className:s,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,s),style:l},i)},v=({prefixCls:e,className:a,width:s,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:s},l)});function j(e){return e&&"object"==typeof e?e:{}}let w=e=>{let{prefixCls:s,loading:n,className:i,rootClassName:o,style:c,children:d,avatar:m=!1,title:u=!0,paragraph:g=!0,active:p,round:x}=e,{getPrefixCls:h,direction:w,className:N,style:y}=(0,a.useComponentConfig)("skeleton"),k=h("skeleton",s),[$,C,T]=f(k);if(n||!("loading"in e)){let e,a,s=!!m,n=!!u,d=!!g;if(s){let r=Object.assign(Object.assign({prefixCls:`${k}-avatar`},n&&!d?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),j(m));e=t.createElement("div",{className:`${k}-header`},t.createElement(l,Object.assign({},r)))}if(n||d){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${k}-title`},!s&&d?{width:"38%"}:s&&d?{width:"50%"}:{}),j(u));e=t.createElement(v,Object.assign({},r))}if(d){let e,a=Object.assign(Object.assign({prefixCls:`${k}-paragraph`},(e={},s&&n||(e.width="61%"),!s&&n?e.rows=3:e.rows=2,e)),j(g));r=t.createElement(b,Object.assign({},a))}a=t.createElement("div",{className:`${k}-content`},e,r)}let h=(0,r.default)(k,{[`${k}-with-avatar`]:s,[`${k}-active`]:p,[`${k}-rtl`]:"rtl"===w,[`${k}-round`]:x},N,i,o,C,T);return $(t.createElement("div",{className:h,style:Object.assign(Object.assign({},y),c)},e,a))}return null!=d?d:null};w.Button=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d=!1,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-button`,size:m},b))))},w.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,shape:d="circle",size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls","className"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-avatar`,shape:d,size:m},b))))},w.Input=e=>{let{prefixCls:n,className:i,rootClassName:o,active:c,block:d,size:m="default"}=e,{getPrefixCls:u}=t.useContext(a.ConfigContext),g=u("skeleton",n),[p,x,h]=f(g),b=(0,s.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},i,o,x,h);return p(t.createElement("div",{className:v},t.createElement(l,Object.assign({prefixCls:`${g}-input`,size:m},b))))},w.Image=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),d=c("skeleton",s),[m,u,g]=f(d),p=(0,r.default)(d,`${d}-element`,{[`${d}-active`]:o},l,n,u,g);return m(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${d}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${d}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${d}-image-path`})))))},w.Node=e=>{let{prefixCls:s,className:l,rootClassName:n,style:i,active:o,children:c}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),m=d("skeleton",s),[u,g,p]=f(m),x=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:o},g,l,n,p);return u(t.createElement("div",{className:x},t.createElement("div",{className:(0,r.default)(`${m}-image`,l),style:i},c)))},e.s(["default",0,w],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let s=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(s),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let s={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",s);let l=e<0?"-":"",n=Math.abs(e),i=n,o="";return n>=1e6?(i=n/1e6,o="M"):n>=1e3&&(i=n/1e3,o="K"),`${l}${i.toLocaleString("en-US",s)}${o}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return s(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),s(e,r)}},s=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let s=document.execCommand("copy");if(document.body.removeChild(a),s)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),s=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(s.TooltipProvider,{delay:300,children:(0,t.jsxs)(s.Tooltip,{children:[(0,t.jsx)(s.TooltipTrigger,{render:r}),(0,t.jsx)(s.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:s,tooltip:i,dataTestId:o}){let c=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":o,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:s});return i?(0,t.jsx)(l,{content:i,trigger:c}):c}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],s=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,o,c,d=e?new Date(e):null;return!d||Number.isNaN(d.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,o=`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`,c=`${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`,`${o}, ${c} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`:`${a[d.getMonth()]} ${d.getDate()}, ${s(d.getHours())}:${s(d.getMinutes())}:${s(d.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let o={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:s,copyable:c=!1,truncate:d=!0,fallback:m="-",tooltip:u,disabled:g=!1,dataTestId:p,className:x}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:m});let h=!!s&&!g,f=(0,n.cn)(o[a].base,h&&o[a].clickable,d&&"block max-w-[15ch] truncate",g&&"opacity-50",x),b=h?(0,r.jsx)("button",{type:"button",className:f,"data-testid":p,onClick:()=>s(e),children:e}):(0,r.jsx)("span",{className:f,"data-testid":p,children:e}),v=(0,r.jsx)(t.CellTooltip,{content:u??e,trigger:b});return c?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[v,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):v}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:s=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?s?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(s("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},o),n))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},o),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"align-middle whitespace-nowrap text-left p-4",i)},o),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},o),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},o),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let s=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,o=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(s("row"),i)},o),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),s=e.i(480731),l=e.i(95779),n=e.i(444755),i=e.i(673706);let o={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},c={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},d=(0,i.makeClassName)("Badge"),m=r.default.forwardRef((e,m)=>{let{color:u,icon:g,size:p=s.Sizes.SM,tooltip:x,className:h,children:f}=e,b=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),v=g||null,{tooltipProps:j,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,j.refs.setReference]),className:(0,n.tremorTwMerge)(d("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",u?(0,n.tremorTwMerge)((0,i.getColorClassNames)(u,l.colorPalette.background).bgColor,(0,i.getColorClassNames)(u,l.colorPalette.iconText).textColor,(0,i.getColorClassNames)(u,l.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),o[p].paddingX,o[p].paddingY,o[p].fontSize,h)},w,b),r.default.createElement(a.default,Object.assign({text:x},j)),v?r.default.createElement(v,{className:(0,n.tremorTwMerge)(d("icon"),"shrink-0 -ml-1 mr-1.5",c[p].height,c[p].width)}):null,r.default.createElement("span",{className:(0,n.tremorTwMerge)(d("text"),"whitespace-nowrap")},f))});m.displayName="Badge",e.s(["Badge",0,m],389083)},502547,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,r],502547)},384767,e=>{"use strict";var t=e.i(843476),r=e.i(599724),a=e.i(271645),s=e.i(389083);let l=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"}))});var n=e.i(602869);let i=function({vectorStores:e,accessToken:i}){let[o,c]=(0,a.useState)([]);return(0,a.useEffect)(()=>{(async()=>{if(i&&0!==e.length)try{let e=await (0,n.vectorStoreListCall)(i);e.data&&c(e.data.map(e=>({vector_store_id:e.vector_store_id,vector_store_name:e.vector_store_name})))}catch(e){console.error("Error fetching vector stores:",e)}})()},[i,e.length]),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Vector Stores"}),(0,t.jsx)(s.Badge,{color:"blue",size:"xs",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:e.map((e,r)=>{let a;return(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-sm font-medium",children:(a=o.find(t=>t.vector_store_id===e))?`${a.vector_store_name||a.vector_store_id} (${a.vector_store_id})`:e},r)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No vector stores configured"})]})]})},o=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"}))});var c=e.i(871943),d=e.i(502547),m=e.i(592968),u=e.i(234713);let g=function({mcpServers:e,mcpAccessGroups:l=[],mcpToolPermissions:i={},mcpToolsets:g=[],accessToken:p}){let[x,h]=(0,a.useState)([]),[f,b]=(0,a.useState)([]),[v,j]=(0,a.useState)(new Set),[w,N]=(0,a.useState)(new Set);(0,a.useEffect)(()=>{(async()=>{if(p&&e.length>0)try{let e=await (0,n.fetchMCPServers)(p);e&&Array.isArray(e)?h(e):e.data&&Array.isArray(e.data)&&h(e.data)}catch(e){console.error("Error fetching MCP servers:",e)}})()},[p,e.length]),(0,a.useEffect)(()=>{(async()=>{if(p&&g.length>0)try{let e=await (0,n.fetchMCPToolsets)(p),t=Array.isArray(e)?e.filter(e=>g.includes(e.toolset_id)):[];b(t)}catch(e){console.error("Error fetching toolsets:",e)}})()},[p,g.length]);let y=e.includes(u.NO_MCP_SERVERS_SENTINEL),k=e.includes(u.ALL_PROXY_MCP_SERVERS_SENTINEL),$=[...e.filter(e=>e!==u.NO_MCP_SERVERS_SENTINEL&&e!==u.ALL_PROXY_MCP_SERVERS_SENTINEL).map(e=>({type:"server",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],C=$.length+g.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"MCP Servers"}),(0,t.jsx)(s.Badge,{color:y?"red":"blue",size:"xs",children:y?"Blocked":k?"All":C})]}),y?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-red-400"}),(0,t.jsx)(r.Text,{className:"text-red-700 text-sm",children:"No MCP servers — this key is blocked from all MCP servers, including its team's servers"})]}):k?(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-blue-400"}),(0,t.jsx)(r.Text,{className:"text-blue-700 text-sm",children:"All Proxy MCP Servers"})]}):C>0?(0,t.jsxs)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:[$.map((e,r)=>{let a="server"===e.type?i[e.value]:void 0,s=a&&a.length>0,l=v.has(e.value);return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>{var t;return s&&(t=e.value,void j(e=>{let r=new Set(e);return r.has(t)?r.delete(t):r.add(t),r}))},className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 transition-all ${s?"cursor-pointer hover:bg-gray-50 hover:border-gray-300":"bg-white"}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"server"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-blue-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=x.find(t=>t.server_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.alias} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})}),s&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:a.length}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===a.length?"tool":"tools"}),l?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),s&&l&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-blue-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.map((e,r)=>(0,t.jsx)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-blue-50 border border-blue-200 text-blue-800 text-xs font-medium",children:e},r))})})]},r)}),g.length>0&&g.map((e,r)=>{let a=f.find(t=>t.toolset_id===e),s=w.has(e),l=a?.tools.length??0;return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{onClick:()=>l>0&&void N(t=>{let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}),className:`flex items-center gap-3 py-2 px-3 rounded-lg border border-purple-200 transition-all ${l>0?"cursor-pointer hover:bg-purple-50 hover:border-purple-300":"bg-white"}`,children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:a?.toolset_name??e}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-purple-600 bg-purple-50 border border-purple-200 rounded-sm uppercase tracking-wide shrink-0",children:"Toolset"})]}),l>0&&(0,t.jsxs)("div",{className:"flex items-center gap-1 shrink-0 whitespace-nowrap",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-600",children:l}),(0,t.jsx)("span",{className:"text-xs text-gray-500",children:1===l?"tool":"tools"}),s?(0,t.jsx)(c.ChevronDownIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"}):(0,t.jsx)(d.ChevronRightIcon,{className:"h-3.5 w-3.5 text-gray-400 ml-0.5"})]})]}),l>0&&s&&a&&(0,t.jsx)("div",{className:"ml-4 pl-4 border-l-2 border-purple-200 pb-1",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-1.5",children:a.tools.map((e,r)=>(0,t.jsxs)("span",{className:"inline-flex items-center px-2.5 py-1 rounded-lg bg-purple-50 border border-purple-200 text-purple-800 text-xs font-medium",children:[(0,t.jsxs)("span",{className:"text-purple-400 mr-1 text-[10px]",children:[e.server_id.slice(0,6),"…"]}),e.tool_name]},r))})})]},`toolset-${r}`)})]}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(o,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No MCP servers, access groups, or toolsets configured"})]})]})},p=a.forwardRef(function(e,t){return a.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),a.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"}))}),x=function({agents:e,agentAccessGroups:l=[],accessToken:i}){let[o,c]=(0,a.useState)([]);(0,a.useEffect)(()=>{(async()=>{if(i&&e.length>0)try{let e=await (0,n.getAgentsList)(i);e&&e.agents&&Array.isArray(e.agents)&&c(e.agents)}catch(e){console.error("Error fetching agents:",e)}})()},[i,e.length]);let d=[...e.map(e=>({type:"agent",value:e})),...l.map(e=>({type:"accessGroup",value:e}))],u=d.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-purple-600"}),(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Agents"}),(0,t.jsx)(s.Badge,{color:"purple",size:"xs",children:u})]}),u>0?(0,t.jsx)("div",{className:"max-h-[400px] overflow-y-auto space-y-2 pr-1",children:d.map((e,r)=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsx)("div",{className:"flex items-center gap-3 py-2 px-3 rounded-lg border border-gray-200 bg-white",children:(0,t.jsx)("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:"agent"===e.type?(0,t.jsx)(m.Tooltip,{title:`Full ID: ${e.value}`,placement:"top",children:(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-purple-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:(e=>{let t=o.find(t=>t.agent_id===e);if(t){let r=e.length>7?`${e.slice(0,3)}...${e.slice(-4)}`:e;return`${t.agent_name} (${r})`}return e})(e.value)})]})}):(0,t.jsxs)("div",{className:"inline-flex items-center gap-2 min-w-0",children:[(0,t.jsx)("span",{className:"inline-block w-1.5 h-1.5 bg-green-500 rounded-full shrink-0"}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900 truncate",children:e.value}),(0,t.jsx)("span",{className:"ml-1 px-1.5 py-0.5 text-[9px] font-semibold text-green-600 bg-green-50 border border-green-200 rounded-sm uppercase tracking-wide shrink-0",children:"Group"})]})})})},r))}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(p,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)(r.Text,{className:"text-gray-500 text-sm",children:"No agents or access groups configured"})]})]})};e.s(["default",0,function({objectPermission:e,variant:a="card",className:s="",accessToken:l}){let n=e?.vector_stores||[],o=e?.mcp_servers||[],c=e?.mcp_access_groups||[],d=e?.mcp_tool_permissions||{},m=e?.mcp_toolsets||[],u=e?.agents||[],p=e?.agent_access_groups||[],h=e?.search_tools||[],f=(0,t.jsxs)("div",{className:"card"===a?"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6":"space-y-4",children:[(0,t.jsx)(i,{vectorStores:n,accessToken:l}),(0,t.jsx)(g,{mcpServers:o,mcpAccessGroups:c,mcpToolPermissions:d,mcpToolsets:m,accessToken:l}),(0,t.jsx)(x,{agents:u,agentAccessGroups:p,accessToken:l}),(0,t.jsxs)("div",{className:"rounded-md border border-gray-100 p-4",children:[(0,t.jsx)(r.Text,{className:"text-sm font-medium text-gray-800",children:"Search tools"}),0===h.length?(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-500",children:"No restriction — all configured search tools are allowed for this team."}):(0,t.jsx)(r.Text,{className:"mt-1 block text-xs text-gray-700",children:h.join(", ")})]})]});return"card"===a?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${s}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(r.Text,{className:"font-semibold text-gray-900",children:"Object Permissions"}),(0,t.jsx)(r.Text,{className:"text-xs text-gray-500",children:"Access control for Vector Stores and MCP Servers"})]})}),f]}):(0,t.jsxs)("div",{className:`${s}`,children:[(0,t.jsx)(r.Text,{className:"font-medium text-gray-900 mb-3",children:"Object Permissions"}),f]})}],384767)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0ph0315t6aok1.js b/litellm/proxy/_experimental/out/_next/static/chunks/0ph0315t6aok1.js new file mode 100644 index 00000000000..b4ef507fc35 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0ph0315t6aok1.js @@ -0,0 +1,5 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,552210,(e,t,r)=>{"use strict";var n=60103,a=60106,i=60107,o=60108,l=60114,c=60109,u=60110,s=60112,d=60113,f=60120,p=60115,y=60116,v=60121,h=60122,m=60117,g=60129,b=60131;if("function"==typeof Symbol&&Symbol.for){var x=Symbol.for;n=x("react.element"),a=x("react.portal"),i=x("react.fragment"),o=x("react.strict_mode"),l=x("react.profiler"),c=x("react.provider"),u=x("react.context"),s=x("react.forward_ref"),d=x("react.suspense"),f=x("react.suspense_list"),p=x("react.memo"),y=x("react.lazy"),v=x("react.block"),h=x("react.server.block"),m=x("react.fundamental"),g=x("react.debug_trace_mode"),b=x("react.legacy_hidden")}function w(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case n:switch(e=e.type){case i:case l:case o:case d:case f:return e;default:switch(e=e&&e.$$typeof){case u:case s:case y:case p:case c:return e;default:return t}}case a:return t}}}var O=c,j=n,A=s,E=i,P=y,S=p,k=a,I=l,C=o,D=d;r.ContextConsumer=u,r.ContextProvider=O,r.Element=j,r.ForwardRef=A,r.Fragment=E,r.Lazy=P,r.Memo=S,r.Portal=k,r.Profiler=I,r.StrictMode=C,r.Suspense=D,r.isAsyncMode=function(){return!1},r.isConcurrentMode=function(){return!1},r.isContextConsumer=function(e){return w(e)===u},r.isContextProvider=function(e){return w(e)===c},r.isElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===n},r.isForwardRef=function(e){return w(e)===s},r.isFragment=function(e){return w(e)===i},r.isLazy=function(e){return w(e)===y},r.isMemo=function(e){return w(e)===p},r.isPortal=function(e){return w(e)===a},r.isProfiler=function(e){return w(e)===l},r.isStrictMode=function(e){return w(e)===o},r.isSuspense=function(e){return w(e)===d},r.isValidElementType=function(e){return"string"==typeof e||"function"==typeof e||e===i||e===l||e===g||e===o||e===d||e===f||e===b||"object"==typeof e&&null!==e&&(e.$$typeof===y||e.$$typeof===p||e.$$typeof===c||e.$$typeof===u||e.$$typeof===s||e.$$typeof===m||e.$$typeof===v||e[0]===h)||!1},r.typeOf=w},179684,(e,t,r)=>{"use strict";t.exports=e.r(552210)},614595,(e,t,r)=>{"use strict";var n=e.r(271645),a="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},i=n.useSyncExternalStore,o=n.useRef,l=n.useEffect,c=n.useMemo,u=n.useDebugValue;r.useSyncExternalStoreWithSelector=function(e,t,r,n,s){var d=o(null);if(null===d.current){var f={hasValue:!1,value:null};d.current=f}else f=d.current;var p=i(e,(d=c(function(){function e(e){if(!l){if(l=!0,i=e,e=n(e),void 0!==s&&f.hasValue){var t=f.value;if(s(t,e))return o=t}return o=e}if(t=o,a(i,e))return t;var r=n(e);return void 0!==s&&s(t,r)?(i=e,t):(i=e,o=r)}var i,o,l=!1,c=void 0===r?null:r;return[function(){return e(t())},null===c?void 0:function(){return e(c())}]},[t,r,n,s]))[0],d[1]);return l(function(){f.hasValue=!0,f.value=p},[p]),u(p),p}},313027,(e,t,r)=>{"use strict";t.exports=e.r(614595)},478492,(e,t,r)=>{"use strict";var n=Object.prototype.hasOwnProperty,a="~";function i(){}function o(e,t,r){this.fn=e,this.context=t,this.once=r||!1}function l(e,t,r,n,i){if("function"!=typeof r)throw TypeError("The listener must be a function");var l=new o(r,n||e,i),c=a?a+t:t;return e._events[c]?e._events[c].fn?e._events[c]=[e._events[c],l]:e._events[c].push(l):(e._events[c]=l,e._eventsCount++),e}function c(e,t){0==--e._eventsCount?e._events=new i:delete e._events[t]}function u(){this._events=new i,this._eventsCount=0}Object.create&&(i.prototype=Object.create(null),new i().__proto__||(a=!1)),u.prototype.eventNames=function(){var e,t,r=[];if(0===this._eventsCount)return r;for(t in e=this._events)n.call(e,t)&&r.push(a?t.slice(1):t);return Object.getOwnPropertySymbols?r.concat(Object.getOwnPropertySymbols(e)):r},u.prototype.listeners=function(e){var t=a?a+e:e,r=this._events[t];if(!r)return[];if(r.fn)return[r.fn];for(var n=0,i=r.length,o=Array(i);n{"use strict";var t,r,n,a,i,o,l,c,u,s,d,f,p,y,v,h,m,g,b,x,w,O,j,A=e.i(843476),E=e.i(271645),P=E,S=e.i(207670),k=["dangerouslySetInnerHTML","onCopy","onCopyCapture","onCut","onCutCapture","onPaste","onPasteCapture","onCompositionEnd","onCompositionEndCapture","onCompositionStart","onCompositionStartCapture","onCompositionUpdate","onCompositionUpdateCapture","onFocus","onFocusCapture","onBlur","onBlurCapture","onChange","onChangeCapture","onBeforeInput","onBeforeInputCapture","onInput","onInputCapture","onReset","onResetCapture","onSubmit","onSubmitCapture","onInvalid","onInvalidCapture","onLoad","onLoadCapture","onError","onErrorCapture","onKeyDown","onKeyDownCapture","onKeyPress","onKeyPressCapture","onKeyUp","onKeyUpCapture","onAbort","onAbortCapture","onCanPlay","onCanPlayCapture","onCanPlayThrough","onCanPlayThroughCapture","onDurationChange","onDurationChangeCapture","onEmptied","onEmptiedCapture","onEncrypted","onEncryptedCapture","onEnded","onEndedCapture","onLoadedData","onLoadedDataCapture","onLoadedMetadata","onLoadedMetadataCapture","onLoadStart","onLoadStartCapture","onPause","onPauseCapture","onPlay","onPlayCapture","onPlaying","onPlayingCapture","onProgress","onProgressCapture","onRateChange","onRateChangeCapture","onSeeked","onSeekedCapture","onSeeking","onSeekingCapture","onStalled","onStalledCapture","onSuspend","onSuspendCapture","onTimeUpdate","onTimeUpdateCapture","onVolumeChange","onVolumeChangeCapture","onWaiting","onWaitingCapture","onAuxClick","onAuxClickCapture","onClick","onClickCapture","onContextMenu","onContextMenuCapture","onDoubleClick","onDoubleClickCapture","onDrag","onDragCapture","onDragEnd","onDragEndCapture","onDragEnter","onDragEnterCapture","onDragExit","onDragExitCapture","onDragLeave","onDragLeaveCapture","onDragOver","onDragOverCapture","onDragStart","onDragStartCapture","onDrop","onDropCapture","onMouseDown","onMouseDownCapture","onMouseEnter","onMouseLeave","onMouseMove","onMouseMoveCapture","onMouseOut","onMouseOutCapture","onMouseOver","onMouseOverCapture","onMouseUp","onMouseUpCapture","onSelect","onSelectCapture","onTouchCancel","onTouchCancelCapture","onTouchEnd","onTouchEndCapture","onTouchMove","onTouchMoveCapture","onTouchStart","onTouchStartCapture","onPointerDown","onPointerDownCapture","onPointerMove","onPointerMoveCapture","onPointerUp","onPointerUpCapture","onPointerCancel","onPointerCancelCapture","onPointerEnter","onPointerEnterCapture","onPointerLeave","onPointerLeaveCapture","onPointerOver","onPointerOverCapture","onPointerOut","onPointerOutCapture","onGotPointerCapture","onGotPointerCaptureCapture","onLostPointerCapture","onLostPointerCaptureCapture","onScroll","onScrollCapture","onWheel","onWheelCapture","onAnimationStart","onAnimationStartCapture","onAnimationEnd","onAnimationEndCapture","onAnimationIteration","onAnimationIterationCapture","onTransitionEnd","onTransitionEndCapture"];function I(e){return"string"==typeof e&&k.includes(e)}var C=new Set(["aria-activedescendant","aria-atomic","aria-autocomplete","aria-busy","aria-checked","aria-colcount","aria-colindex","aria-colspan","aria-controls","aria-current","aria-describedby","aria-details","aria-disabled","aria-errormessage","aria-expanded","aria-flowto","aria-haspopup","aria-hidden","aria-invalid","aria-keyshortcuts","aria-label","aria-labelledby","aria-level","aria-live","aria-modal","aria-multiline","aria-multiselectable","aria-orientation","aria-owns","aria-placeholder","aria-posinset","aria-pressed","aria-readonly","aria-relevant","aria-required","aria-roledescription","aria-rowcount","aria-rowindex","aria-rowspan","aria-selected","aria-setsize","aria-sort","aria-valuemax","aria-valuemin","aria-valuenow","aria-valuetext","className","color","height","id","lang","max","media","method","min","name","style","target","width","role","tabIndex","accentHeight","accumulate","additive","alignmentBaseline","allowReorder","alphabetic","amplitude","arabicForm","ascent","attributeName","attributeType","autoReverse","azimuth","baseFrequency","baselineShift","baseProfile","bbox","begin","bias","by","calcMode","capHeight","clip","clipPath","clipPathUnits","clipRule","colorInterpolation","colorInterpolationFilters","colorProfile","colorRendering","contentScriptType","contentStyleType","cursor","cx","cy","d","decelerate","descent","diffuseConstant","direction","display","divisor","dominantBaseline","dur","dx","dy","edgeMode","elevation","enableBackground","end","exponent","externalResourcesRequired","fill","fillOpacity","fillRule","filter","filterRes","filterUnits","floodColor","floodOpacity","focusable","fontFamily","fontSize","fontSizeAdjust","fontStretch","fontStyle","fontVariant","fontWeight","format","from","fx","fy","g1","g2","glyphName","glyphOrientationHorizontal","glyphOrientationVertical","glyphRef","gradientTransform","gradientUnits","hanging","horizAdvX","horizOriginX","href","ideographic","imageRendering","in2","in","intercept","k1","k2","k3","k4","k","kernelMatrix","kernelUnitLength","kerning","keyPoints","keySplines","keyTimes","lengthAdjust","letterSpacing","lightingColor","limitingConeAngle","local","markerEnd","markerHeight","markerMid","markerStart","markerUnits","markerWidth","mask","maskContentUnits","maskUnits","mathematical","mode","numOctaves","offset","opacity","operator","order","orient","orientation","origin","overflow","overlinePosition","overlineThickness","paintOrder","panose1","pathLength","patternContentUnits","patternTransform","patternUnits","pointerEvents","pointsAtX","pointsAtY","pointsAtZ","preserveAlpha","preserveAspectRatio","primitiveUnits","r","radius","refX","refY","renderingIntent","repeatCount","repeatDur","requiredExtensions","requiredFeatures","restart","result","rotate","rx","ry","seed","shapeRendering","slope","spacing","specularConstant","specularExponent","speed","spreadMethod","startOffset","stdDeviation","stemh","stemv","stitchTiles","stopColor","stopOpacity","strikethroughPosition","strikethroughThickness","string","stroke","strokeDasharray","strokeDashoffset","strokeLinecap","strokeLinejoin","strokeMiterlimit","strokeOpacity","strokeWidth","surfaceScale","systemLanguage","tableValues","targetX","targetY","textAnchor","textDecoration","textLength","textRendering","to","transform","u1","u2","underlinePosition","underlineThickness","unicode","unicodeBidi","unicodeRange","unitsPerEm","vAlphabetic","values","vectorEffect","version","vertAdvY","vertOriginX","vertOriginY","vHanging","vIdeographic","viewTarget","visibility","vMathematical","widths","wordSpacing","writingMode","x1","x2","x","xChannelSelector","xHeight","xlinkActuate","xlinkArcrole","xlinkHref","xlinkRole","xlinkShow","xlinkTitle","xlinkType","xmlBase","xmlLang","xmlns","xmlnsXlink","xmlSpace","y1","y2","y","yChannelSelector","z","zoomAndPan","ref","key","angle"]);function D(e){return"string"==typeof e&&C.has(e)}function M(e){return"string"==typeof e&&e.startsWith("data-")}function N(e){if("object"!=typeof e||null===e)return{};var t={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(D(r)||M(r))&&(t[r]=e[r]);return t}function T(e){return null==e?null:(0,E.isValidElement)(e)&&"object"==typeof e.props&&null!==e.props?N(e.props):"object"!=typeof e||Array.isArray(e)?null:N(e)}function z(e){var t={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(D(r)||M(r)||I(r))&&(t[r]=e[r]);return t}var _=["children","className"];function R(){return(R=Object.assign.bind()).apply(null,arguments)}var L=E.forwardRef((e,t)=>{var r=e.children,n=e.className,a=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n1&&void 0!==arguments[1]?arguments[1]:4,r=10**t,n=Math.round(e*r)/r;return Object.is(n,-0)?0:n}function U(e){for(var t=arguments.length,r=Array(t>1?t-1:0),n=1;n{var a=r[n-1];return"string"==typeof a?e+a+t:void 0!==a?e+$(a)+t:e+t},"")}var H=e=>0===e?0:e>0?1:-1,G=e=>"number"==typeof e&&e!=+e,q=e=>"string"==typeof e&&e.length>1&&e.indexOf("%")===e.length-1,X=e=>("number"==typeof e||e instanceof Number)&&!G(e),Y=e=>X(e)||"string"==typeof e,Z=0,Q=e=>{var t=++Z;return"".concat(e||"").concat(t)},J=function(e,t){var r,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if(!X(e)&&"string"!=typeof e)return n;if(q(e)){if(null==t)return n;var i=e.indexOf("%");r=t*parseFloat(e.slice(0,i))/100}else r=+e;return G(r)&&(r=n),a&&null!=t&&r>t&&(r=t),r},ee=e=>{if(!Array.isArray(e))return!1;for(var t=e.length,r={},n=0;ne&&("function"==typeof t?t(e):V(e,t))===r)}var en=e=>null==e?e:"".concat(e.charAt(0).toUpperCase()).concat(e.slice(1));function ea(e){return null!=e}function ei(){}var eo={devToolsEnabled:!0,isSsr:!("u">typeof window&&window.document&&window.document.createElement&&window.setTimeout)};function el(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}var ec=function(e){for(var t=1;t=this.maxSize){var r=this.cache.keys().next().value;null!=r&&this.cache.delete(r)}this.cache.set(e,t)}clear(){this.cache.clear()}size(){return this.cache.size}}(ec.cacheSize),es={position:"absolute",top:"-20000px",left:0,padding:0,margin:0,border:"none",whiteSpace:"pre"},ed="recharts_measurement_span",ef=(e,t)=>{try{var r=document.getElementById(ed);r||((r=document.createElement("span")).setAttribute("id",ed),r.setAttribute("aria-hidden","true"),document.body.appendChild(r)),Object.assign(r.style,es,t),r.textContent="".concat(e);var n=r.getBoundingClientRect();return{width:n.width,height:n.height}}catch(e){return{width:0,height:0}}},ep=function(e){var t,r,n,a,i,o,l=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(null==e||eo.isSsr)return{width:0,height:0};if(!ec.enableCache)return ef(e,l);var c=(t=l.fontSize||"",r=l.fontFamily||"",n=l.fontWeight||"",a=l.fontStyle||"",i=l.letterSpacing||"",o=l.textTransform||"","".concat(e,"|").concat(t,"|").concat(r,"|").concat(n,"|").concat(a,"|").concat(i,"|").concat(o)),u=eu.get(c);if(u)return u;var s=ef(e,l);return eu.set(c,s),s};function ey(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],c=!0,u=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;c=!1}else for(;!(c=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);c=!0);}catch(e){u=!0,a=e}finally{try{if(!c&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(u)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return ev(e,t);var r=({}).toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?ev(e,t):void 0}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function ev(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r(void 0===e[r]&&void 0!==t[r]&&(e[r]=t[r]),e),r)}function ek(e){return Number.isFinite(e)}function eI(e){return"number"==typeof e&&e>0&&Number.isFinite(e)}var eC=["x","y","lineHeight","capHeight","fill","scaleToFit","textAnchor","verticalAnchor"],eD=["dx","dy","angle","className","breakAll"];function eM(){return(eM=Object.assign.bind()).apply(null,arguments)}function eN(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;ntypeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],c=!0,u=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;c=!1}else for(;!(c=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);c=!0);}catch(e){u=!0,a=e}finally{try{if(!c&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(u)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return ez(e,t);var r=({}).toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?ez(e,t):void 0}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function ez(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=e.children,r=e.breakAll,n=e.style;try{var a=[];null!=t&&(a=r?t.toString().split(""):t.toString().split(e_));var i=a.map(e=>({word:e,width:ep(e,n).width})),o=r?0:ep(" ",n).width;return{wordsWithComputedWidth:i,spaceWidth:o}}catch(e){return null}};function eL(e){return"start"===e||"middle"===e||"end"===e||"inherit"===e}var eB=(e,t,r,n)=>e.reduce((e,a)=>{var i=a.word,o=a.width,l=e[e.length-1];return l&&null!=o&&(null==t||n||l.width+o+re.reduce((e,t)=>e.width>t.width?e:t),eF=(e,t,r,n,a,i,o,l)=>{var c=eR({breakAll:r,style:n,children:e.slice(0,t)+"…"});if(!c)return[!1,[]];var u=eB(c.wordsWithComputedWidth,i,o,l);return[u.length>a||eK(u).width>Number(i),u]},eW=e=>[{words:null==e?[]:e.toString().split(e_),width:void 0}],eV="#808080",e$={angle:0,breakAll:!1,capHeight:"0.71em",fill:eV,lineHeight:"1em",scaleToFit:!1,textAnchor:"start",verticalAnchor:"end",x:0,y:0},eU=(0,E.forwardRef)((e,t)=>{var r,n=eS(e,e$),a=n.x,i=n.y,o=n.lineHeight,l=n.capHeight,c=n.fill,u=n.scaleToFit,s=n.textAnchor,d=n.verticalAnchor,f=eN(n,eC),p=(0,E.useMemo)(()=>(e=>{var t=e.width,r=e.scaleToFit,n=e.children,a=e.style,i=e.breakAll,o=e.maxLines;if((t||r)&&!eo.isSsr){var l=eR({breakAll:i,children:n,style:a});if(!l)return eW(n);var c=l.wordsWithComputedWidth,u=l.spaceWidth;return((e,t,r,n,a)=>{var i,o=e.maxLines,l=e.children,c=e.style,u=e.breakAll,s=X(o),d=String(l),f=eB(t,n,r,a);if(!s||a||!(f.length>o||eK(f).width>Number(n)))return f;for(var p=0,y=d.length-1,v=0;p<=y&&v<=d.length-1;){var h=Math.floor((p+y)/2),m=eT(eF(d,h-1,u,c,o,n,r,a),2),g=m[0],b=m[1],x=eT(eF(d,h,u,c,o,n,r,a),1)[0];if(g||x||(p=h+1),g&&x&&(y=h-1),!g&&x){i=b;break}v++}return i||f})({breakAll:i,children:n,maxLines:o,style:a},c,u,t,!!r)}return eW(n)})({breakAll:f.breakAll,children:f.children,maxLines:f.maxLines,scaleToFit:u,style:f.style,width:f.width}),[f.breakAll,f.children,f.maxLines,u,f.style,f.width]),y=f.dx,v=f.dy,h=f.angle,m=f.className,g=f.breakAll,b=eN(f,eD);if(!Y(a)||!Y(i)||0===p.length)return null;var x=Number(a)+(X(y)?y:0),w=Number(i)+(X(v)?v:0);if(!ek(x)||!ek(w))return null;switch(d){case"start":r=eE("calc(".concat(l,")"));break;case"middle":r=eE("calc(".concat((p.length-1)/2," * -").concat(o," + (").concat(l," / 2))"));break;default:r=eE("calc(".concat(p.length-1," * -").concat(o,")"))}var O=[],j=p[0];if(u&&null!=j){var A=j.width,P=f.width;O.push("scale(".concat(X(P)&&X(A)?P/A:1,")"))}return h&&O.push("rotate(".concat(h,", ").concat(x,", ").concat(w,")")),O.length&&(b.transform=O.join(" ")),E.createElement("text",eM({},z(b),{ref:t,x:x,y:w,className:(0,S.clsx)("recharts-text",m),textAnchor:s,fill:c.includes("url")?eV:c}),p.map((e,t)=>{var n=e.words.join(g?"":" ");return E.createElement("tspan",{x:x,dy:0===t?r:o,key:"".concat(n,"-").concat(t)},n)}))});function eH(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function eG(e){for(var t=1;t({x:e+Math.cos(-eq*n)*r,y:t+Math.sin(-eq*n)*r}),eY=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{top:0,right:0,bottom:0,left:0,width:0,height:0,brushBottom:0};return Math.min(Math.abs(e-(r.left||0)-(r.right||0)),Math.abs(t-(r.top||0)-(r.bottom||0)))/2},eZ=e.i(430224),eQ=(0,E.createContext)(null),eJ=e=>e,e0=()=>{var e=(0,E.useContext)(eQ);return e?e.store.dispatch:eJ},e1=()=>{},e2=()=>e1,e3=(e,t)=>e===t;function e6(e){var t=(0,E.useContext)(eQ),r=(0,E.useMemo)(()=>t?t=>{if(null!=t)return e(t)}:e1,[t,e]);return(0,eZ.useSyncExternalStoreWithSelector)(t?t.subscription.addNestedSub:e2,t?t.store.getState:e1,t?t.store.getState:e1,r,e3)}e.i(247167);var e5=Symbol.for("immer-nothing"),e8=Symbol.for("immer-draftable"),e4=Symbol.for("immer-state");function e9(e){throw Error(`[Immer] minified error nr: ${e}. Full error at: https://bit.ly/3cXEKWf`)}var e7=Object,te=e7.getPrototypeOf,tt="constructor",tr="prototype",tn="configurable",ta="enumerable",ti="writable",to="value",tl=e=>!!e&&!!e[e4];function tc(e){return!!e&&(td(e)||tm(e)||!!e[e8]||!!e[tt]?.[e8]||tg(e)||tb(e))}var tu=e7[tr][tt].toString(),ts=new WeakMap;function td(e){if(!e||!tx(e))return!1;let t=te(e);if(null===t||t===e7[tr])return!0;let r=e7.hasOwnProperty.call(t,tt)&&t[tt];if(r===Object)return!0;if(!tw(r))return!1;let n=ts.get(r);return void 0===n&&(n=Function.toString.call(r),ts.set(r,n)),n===tu}function tf(e,t,r=!0){0===tp(e)?(r?Reflect.ownKeys(e):e7.keys(e)).forEach(r=>{t(r,e[r],e)}):e.forEach((r,n)=>t(n,r,e))}function tp(e){let t=e[e4];return t?t.type_:tm(e)?1:tg(e)?2:3*!!tb(e)}var ty=(e,t,r=tp(e))=>2===r?e.has(t):e7[tr].hasOwnProperty.call(e,t),tv=(e,t,r=tp(e))=>2===r?e.get(t):e[t],th=(e,t,r,n=tp(e))=>{2===n?e.set(t,r):3===n?e.add(r):e[t]=r},tm=Array.isArray,tg=e=>e instanceof Map,tb=e=>e instanceof Set,tx=e=>"object"==typeof e,tw=e=>"function"==typeof e,tO=e=>e.modified_?e.copy_:e.base_;function tj(e,t){if(tg(e))return new Map(e);if(tb(e))return new Set(e);if(tm(e))return Array[tr].slice.call(e);let r=td(e);if(!0!==t&&("class_only"!==t||r)){let t=te(e);if(null!==t&&r)return{...e};let n=e7.create(t);return e7.assign(n,e)}{let t=e7.getOwnPropertyDescriptors(e);delete t[e4];let r=Reflect.ownKeys(t);for(let n=0;n1&&e7.defineProperties(e,{set:tE,add:tE,clear:tE,delete:tE}),e7.freeze(e),t&&tf(e,(e,t)=>{tA(t,!0)},!1)),e}var tE={[to]:function(){e9(2)}};function tP(e){return!(null!==e&&tx(e))||e7.isFrozen(e)}var tS="MapSet",tk="Patches",tI="ArrayMethods",tC={};function tD(e){let t=tC[e];return t||e9(0,e),t}var tM=e=>!!tC[e];function tN(e,t){t&&(e.patchPlugin_=tD(tk),e.patches_=[],e.inversePatches_=[],e.patchListener_=t)}function tT(e){tz(e),e.drafts_.forEach(tR),e.drafts_=null}function tz(e){e===i&&(i=e.parent_)}var t_=e=>i={drafts_:[],parent_:i,immer_:e,canAutoFreeze_:!0,unfinalizedDrafts_:0,handledSet_:new Set,processedForPatches_:new Set,mapSetPlugin_:tM(tS)?tD(tS):void 0,arrayMethodsPlugin_:tM(tI)?tD(tI):void 0};function tR(e){let t=e[e4];0===t.type_||1===t.type_?t.revoke_():t.revoked_=!0}function tL(e,t){t.unfinalizedDrafts_=t.drafts_.length;let r=t.drafts_[0];if(void 0!==e&&e!==r){r[e4].modified_&&(tT(t),e9(4)),tc(e)&&(e=tB(t,e));let{patchPlugin_:n}=t;n&&n.generateReplacementPatches_(r[e4].base_,e,t)}else e=tB(t,r);return function(e,t,r=!1){!e.parent_&&e.immer_.autoFreeze_&&e.canAutoFreeze_&&tA(t,r)}(t,e,!0),tT(t),t.patches_&&t.patchListener_(t.patches_,t.inversePatches_),e!==e5?e:void 0}function tB(e,t){if(tP(t))return t;let r=t[e4];if(!r)return tU(t,e.handledSet_,e);if(!tF(r,e))return t;if(!r.modified_)return r.base_;if(!r.finalized_){let{callbacks_:t}=r;if(t)for(;t.length>0;)t.pop()(e);t$(r,e)}return r.copy_}function tK(e){e.finalized_=!0,e.scope_.unfinalizedDrafts_--}var tF=(e,t)=>e.scope_===t,tW=[];function tV(e,t,r,n){let a=e.copy_||e.base_,i=e.type_;if(void 0!==n&&tv(a,n,i)===t)return void th(a,n,r,i);if(!e.draftLocations_){let t=e.draftLocations_=new Map;tf(a,(e,r)=>{if(tl(r)){let n=t.get(r)||[];n.push(e),t.set(r,n)}})}for(let n of e.draftLocations_.get(t)??tW)th(a,n,r,i)}function t$(e,t){if(e.modified_&&!e.finalized_&&(3===e.type_||1===e.type_&&e.allIndicesReassigned_||(e.assigned_?.size??0)>0)){let{patchPlugin_:r}=t;if(r){let n=r.getPath(e);n&&r.generatePatches_(e,n,t)}tK(e)}}function tU(e,t,r){return!r.immer_.autoFreeze_&&r.unfinalizedDrafts_<1||tl(e)||t.has(e)||!tc(e)||tP(e)||(t.add(e),tf(e,(n,a)=>{if(tl(a)){let t=a[e4];tF(t,r)&&(th(e,n,tO(t),e.type_),tK(t))}else tc(a)&&tU(a,t,r)})),e}var tH={get(e,t){let r;if(t===e4)return e;if("constructor"===t||"__proto__"===t)return new Proxy((e.copy_||e.base_)[t]||{},{get:(e,t)=>"__proto__"===t||"prototype"===t?Object.freeze(Object.create(null)):Reflect.get(e,t),set:()=>!0,apply:(e,t,r)=>Reflect.apply(e,t,r)});let n=e.scope_.arrayMethodsPlugin_,a=1===e.type_&&"string"==typeof t;if(a&&n?.isArrayOperationMethod(t))return n.createMethodInterceptor(e,t);let i=e.copy_||e.base_;if(!ty(i,t,e.type_)){var o;let r;return o=e,(r=tX(i,t))?to in r?r[to]:r.get?.call(o.draft_):void 0}let l=i[t];if(e.finalized_||!tc(l)||a&&e.operationMethod&&n?.isMutatingArrayMethod(e.operationMethod)&&Number.isInteger(r=+t)&&String(r)===t)return l;if(l===tq(e.base_,t)){tZ(e);let r=1===e.type_?+t:t,n=tQ(e.scope_,l,e,r);return e.copy_[r]=n}return l},has:(e,t)=>"constructor"!==t&&"__proto__"!==t&&"prototype"!==t&&t in(e.copy_||e.base_),ownKeys:e=>Reflect.ownKeys(e.copy_||e.base_),set(e,t,r){if("constructor"===t||"__proto__"===t||"prototype"===t)return!0;let n=tX(e.copy_||e.base_,t);if(n?.set)return n.set.call(e.draft_,r),!0;if(!e.modified_){let n=tq(e.copy_||e.base_,t),a=n?.[e4];if(a&&a.base_===r)return e.copy_[t]=r,e.assigned_.set(t,!1),!0;if((r===n?0!==r||1/r==1/n:r!=r&&n!=n)&&(void 0!==r||ty(e.base_,t,e.type_)))return!0;tZ(e),tY(e)}return!!(e.copy_[t]===r&&(void 0!==r||ty(e.copy_,t,e.type_))||Number.isNaN(r)&&Number.isNaN(e.copy_[t]))||(e.copy_[t]=r,e.assigned_.set(t,!0),!function(e,t,r){let{scope_:n}=e;if(tl(r)){let a=r[e4];tF(a,n)&&a.callbacks_.push(function(){tZ(e),tV(e,r,tO(a),t)})}else tc(r)&&e.callbacks_.push(function(){let a=e.copy_||e.base_;3===e.type_?a.has(r)&&tU(r,n.handledSet_,n):tv(a,t,e.type_)===r&&n.drafts_.length>1&&(e.assigned_.get(t)??!1)===!0&&e.copy_&&tU(tv(e.copy_,t,e.type_),n.handledSet_,n)})}(e,t,r),!0)},deleteProperty:(e,t)=>(tZ(e),void 0!==tq(e.base_,t)||t in e.base_?(e.assigned_.set(t,!1),tY(e)):e.assigned_.delete(t),e.copy_&&delete e.copy_[t],!0),getOwnPropertyDescriptor(e,t){let r=e.copy_||e.base_,n=Reflect.getOwnPropertyDescriptor(r,t);return n?{[ti]:!0,[tn]:1!==e.type_||"length"!==t,[ta]:n[ta],[to]:r[t]}:n},defineProperty(){e9(11)},getPrototypeOf:e=>te(e.base_),setPrototypeOf(){e9(12)}},tG={};for(let e in tH){let t=tH[e];tG[e]=function(){let e=arguments;return e[0]=e[0][0],t.apply(this,e)}}function tq(e,t){let r=e[e4];return(r?r.copy_||r.base_:e)[t]}function tX(e,t){if(!(t in e))return;let r=te(e);for(;r;){let e=Object.getOwnPropertyDescriptor(r,t);if(e)return e;r=te(r)}}function tY(e){!e.modified_&&(e.modified_=!0,e.parent_&&tY(e.parent_))}function tZ(e){e.copy_||(e.assigned_=new Map,e.copy_=tj(e.base_,e.scope_.immer_.useStrictShallowCopy_))}function tQ(e,t,r,n){let[a,o]=tg(t)?tD(tS).proxyMap_(t,r):tb(t)?tD(tS).proxySet_(t,r):function(e,t){let r=tm(e),n={type_:+!!r,scope_:t?t.scope_:i,modified_:!1,finalized_:!1,assigned_:void 0,parent_:t,base_:e,draft_:null,copy_:null,revoke_:null,isManual_:!1,callbacks_:void 0},a=n,o=tH;r&&(a=[n],o=tG);let{revoke:l,proxy:c}=Proxy.revocable(a,o);return n.draft_=c,n.revoke_=l,[c,n]}(t,r);if((r?.scope_??i).drafts_.push(a),o.callbacks_=r?.callbacks_??[],o.key_=n,r&&void 0!==n)r.callbacks_.push(function(e){if(!o||!tF(o,e))return;e.mapSetPlugin_?.fixSetContents(o);let t=tO(o);tV(r,o.draft_??o,t,n),t$(o,e)});else o.callbacks_.push(function(e){e.mapSetPlugin_?.fixSetContents(o);let{patchPlugin_:t}=e;o.modified_&&t&&t.generatePatches_(o,[],e)});return a}function tJ(e){return tl(e)||e9(10,e),function e(t){let r;if(!tc(t)||tP(t))return t;let n=t[e4],a=!0;if(n){if(!n.modified_)return n.base_;n.finalized_=!0,r=tj(t,n.scope_.immer_.useStrictShallowCopy_),a=n.scope_.immer_.shouldUseStrictIteration()}else r=tj(t,!0);return tf(r,(t,n)=>{th(r,t,e(n))},a),n&&(n.finalized_=!1),r}(e)}tG.deleteProperty=function(e,t){return tG.set.call(this,e,t,void 0)},tG.set=function(e,t,r){return tH.set.call(this,e[0],t,r,e[0])};var t0=new class{constructor(e){this.autoFreeze_=!0,this.useStrictShallowCopy_=!1,this.useStrictIteration_=!1,this.produce=(e,t,r)=>{let n;if(tw(e)&&!tw(t)){let r=t;t=e;let n=this;return function(e=r,...a){return n.produce(e,e=>t.call(this,e,...a))}}if(tw(t)||e9(6),void 0===r||tw(r)||e9(7),tc(e)){let a=t_(this),i=tQ(a,e,void 0),o=!0;try{n=t(i),o=!1}finally{o?tT(a):tz(a)}return tN(a,r),tL(n,a)}if(e&&tx(e))e9(1,e);else{if(void 0===(n=t(e))&&(n=e),n===e5&&(n=void 0),this.autoFreeze_&&tA(n,!0),r){let t=[],a=[];tD(tk).generateReplacementPatches_(e,n,{patches_:t,inversePatches_:a}),r(t,a)}return n}},this.produceWithPatches=(e,t)=>{let r,n;return tw(e)?(t,...r)=>this.produceWithPatches(t,t=>e(t,...r)):[this.produce(e,t,(e,t)=>{r=e,n=t}),r,n]},(e=>"boolean"==typeof e)(e?.autoFreeze)&&this.setAutoFreeze(e.autoFreeze),(e=>"boolean"==typeof e)(e?.useStrictShallowCopy)&&this.setUseStrictShallowCopy(e.useStrictShallowCopy),(e=>"boolean"==typeof e)(e?.useStrictIteration)&&this.setUseStrictIteration(e.useStrictIteration)}createDraft(e){tc(e)||e9(8),tl(e)&&(e=tJ(e));let t=t_(this),r=tQ(t,e,void 0);return r[e4].isManual_=!0,tz(t),r}finishDraft(e,t){let r=e&&e[e4];r&&r.isManual_||e9(9);let{scope_:n}=r;return tN(n,t),tL(void 0,n)}setAutoFreeze(e){this.autoFreeze_=e}setUseStrictShallowCopy(e){this.useStrictShallowCopy_=e}setUseStrictIteration(e){this.useStrictIteration_=e}shouldUseStrictIteration(){return this.useStrictIteration_}applyPatches(e,t){let r;for(r=t.length-1;r>=0;r--){let n=t[r];if(0===n.path.length&&"replace"===n.op){e=n.value;break}}r>-1&&(t=t.slice(r+1));let n=tD(tk).applyPatches_;return tl(e)?n(e,t):this.produce(e,e=>n(e,t))}}().produce,t1=e=>Array.isArray(e)?e:[e],t2=0,t3=class{revision=t2;_value;_lastValue;_isEqual=t6;constructor(e,t=t6){this._value=this._lastValue=e,this._isEqual=t}get value(){return this._value}set value(e){this.value!==e&&(this._value=e,this.revision=++t2)}};function t6(e,t){return e===t}function t5(e){return e instanceof t3||console.warn("Not a valid cell! ",e),e.value}var t8=(e,t)=>!1;function t4(){return function(e=t6){return new t3(null,e)}(t8)}var t9=e=>{let t=e.collectionTag;null===t&&(t=e.collectionTag=t4()),t5(t)},t7=0,re=Object.getPrototypeOf({}),rt=class{constructor(e){this.value=e,this.value=e,this.tag.value=e}proxy=new Proxy(this,rr);tag=t4();tags={};children={};collectionTag=null;id=t7++},rr={get:(e,t)=>(function(){let{value:r}=e,n=Reflect.get(r,t);if("symbol"==typeof t||t in re)return n;if("object"==typeof n&&null!==n){var a;let r=e.children[t];return void 0===r&&(r=e.children[t]=Array.isArray(a=n)?new rn(a):new rt(a)),r.tag&&t5(r.tag),r.proxy}{let r=e.tags[t];return void 0===r&&((r=e.tags[t]=t4()).value=n),t5(r),n}})(),ownKeys:e=>(t9(e),Reflect.ownKeys(e.value)),getOwnPropertyDescriptor:(e,t)=>Reflect.getOwnPropertyDescriptor(e.value,t),has:(e,t)=>Reflect.has(e.value,t)},rn=class{constructor(e){this.value=e,this.value=e,this.tag.value=e}proxy=new Proxy([this],ra);tag=t4();tags={};children={};collectionTag=null;id=t7++},ra={get:([e],t)=>("length"===t&&t9(e),rr.get(e,t)),ownKeys:([e])=>rr.ownKeys(e),getOwnPropertyDescriptor:([e],t)=>rr.getOwnPropertyDescriptor(e,t),has:([e],t)=>rr.has(e,t)},ri="u"{n=ro(),o.resetResultsCount()},o.resultsCount=()=>i,o.resetResultsCount=()=>{i=0},o}var rc=function(e,...t){let r="function"==typeof e?{memoize:e,memoizeOptions:t}:e,n=(...e)=>{let t,n,a=0,i=0,o={},l=e.pop();"object"==typeof l&&(o=l,l=e.pop()),function(e,t=`expected a function, instead received ${typeof e}`){if("function"!=typeof e)throw TypeError(t)}(l,`createSelector expects an output function after the inputs, but received: [${typeof l}]`);let{memoize:c,memoizeOptions:u=[],argsMemoize:s=rl,argsMemoizeOptions:d=[]}={...r,...o},f=t1(u),p=t1(d),y=(!function(e,t="expected all items to be functions, instead received the following types: "){if(!e.every(e=>"function"==typeof e)){let r=e.map(e=>"function"==typeof e?`function ${e.name||"unnamed"}()`:typeof e).join(", ");throw TypeError(`${t}[${r}]`)}}(t=Array.isArray(e[0])?e[0]:e,"createSelector expects all input-selectors to be functions, but received the following types: "),t),v=c(function(){return a++,l.apply(null,arguments)},...f);return Object.assign(s(function(){i++;let e=function(e,t){let r=[],{length:n}=e;for(let a=0;ai,resetDependencyRecomputations:()=>{i=0},lastResult:()=>n,recomputations:()=>a,resetRecomputations:()=>{a=0},memoize:c,argsMemoize:s})};return Object.assign(n,{withTypes:()=>n}),n}(rl),ru=Object.assign((e,t=rc)=>{!function(e,t=`expected an object, instead received ${typeof e}`){if("object"!=typeof e)throw TypeError(t)}(e,`createStructuredSelector expects first argument to be an object where each property is a selector, instead received a ${typeof e}`);let r=Object.keys(e);return t(r.map(t=>e[t]),(...e)=>e.reduce((e,t,n)=>(e[r[n]]=t,e),{}))},{withTypes:()=>ru});function rs(e){return`Minified Redux error #${e}; visit https://redux.js.org/Errors?code=${e} for the full message or use the non-minified dev environment for full errors. `}var rd="function"==typeof Symbol&&Symbol.observable||"@@observable",rf=()=>Math.random().toString(36).substring(7).split("").join("."),rp={INIT:`@@redux/INIT${rf()}`,REPLACE:`@@redux/REPLACE${rf()}`,PROBE_UNKNOWN_ACTION:()=>`@@redux/PROBE_UNKNOWN_ACTION${rf()}`};function ry(e){if("object"!=typeof e||null===e)return!1;let t=e;for(;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t||null===Object.getPrototypeOf(e)}function rv(e){let t,r=Object.keys(e),n={};for(let t=0;t{let t=n[e];if(void 0===t(void 0,{type:rp.INIT}))throw Error(rs(12));if(void 0===t(void 0,{type:rp.PROBE_UNKNOWN_ACTION()}))throw Error(rs(13))})}catch(e){t=e}return function(e={},r){if(t)throw t;let i=!1,o={};for(let t=0;te:1===e.length?e[0]:e.reduce((e,t)=>(...r)=>e(t(...r)))}function rm(e){return ry(e)&&"type"in e&&"string"==typeof e.type}function rg(e){return({dispatch:t,getState:r})=>n=>a=>"function"==typeof a?a(t,r,e):n(a)}var rb=rg(),rx="u">typeof window&&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__:function(){if(0!=arguments.length)return"object"==typeof arguments[0]?rh:rh.apply(null,arguments)};function rw(e,t){function r(...n){if(t){let r=t(...n);if(!r)throw Error(ne(0));return{type:e,payload:r.payload,..."meta"in r&&{meta:r.meta},..."error"in r&&{error:r.error}}}return{type:e,payload:n[0]}}return r.toString=()=>`${e}`,r.type=e,r.match=t=>rm(t)&&t.type===e,r}"u">typeof window&&window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__;var rO=class e extends Array{constructor(...t){super(...t),Object.setPrototypeOf(this,e.prototype)}static get[Symbol.species](){return e}concat(...e){return super.concat.apply(this,e)}prepend(...t){return 1===t.length&&Array.isArray(t[0])?new e(...t[0].concat(this)):new e(...t.concat(this))}};function rj(e){return tc(e)?t0(e,()=>{}):e}function rA(e,t,r){return e.has(t)?e.get(t):e.set(t,r(t)).get(t)}var rE="RTK_autoBatch",rP=()=>e=>({payload:e,meta:{[rE]:!0}}),rS=e=>t=>{setTimeout(t,e)},rk=(e={type:"raf"})=>t=>(...r)=>{let n,a=t(...r),i=!0,o=!1,l=!1,c=new Set,u="tick"===e.type?queueMicrotask:"raf"===e.type?"u">typeof window&&window.requestAnimationFrame?(n=window.requestAnimationFrame,e=>{let t=!1,r=()=>{t||(t=!0,cancelAnimationFrame(a),clearTimeout(i),e())},a=n(r),i=setTimeout(r,100)}):rS(10):"callback"===e.type?e.queueNotification:rS(e.timeout),s=()=>{l=!1,o&&(o=!1,c.forEach(e=>e()))};return Object.assign({},a,{subscribe(e){let t=a.subscribe(()=>i&&e());return c.add(e),()=>{t(),c.delete(e)}},dispatch(e){try{return(o=!(i=!e?.meta?.[rE]))&&!l&&(l=!0,u(s)),a.dispatch(e)}finally{i=!0}}})};function rI(e){let t,r={},n=[],a={addCase(e,t){let n="string"==typeof e?e:e.type;if(!n)throw Error(ne(28));if(n in r)throw Error(ne(29));return r[n]=t,a},addAsyncThunk:(e,t)=>(t.pending&&(r[e.pending.type]=t.pending),t.rejected&&(r[e.rejected.type]=t.rejected),t.fulfilled&&(r[e.fulfilled.type]=t.fulfilled),t.settled&&n.push({matcher:e.settled,reducer:t.settled}),a),addMatcher:(e,t)=>(n.push({matcher:e,reducer:t}),a),addDefaultCase:e=>(t=e,a)};return e(a),[r,n,t]}var rC=Symbol.for("rtk-slice-createasyncthunk"),rD=((a=rD||{}).reducer="reducer",a.reducerWithPrepare="reducerWithPrepare",a.asyncThunk="asyncThunk",a),rM=function({creators:e}={}){let t=e?.asyncThunk?.[rC];return function(e){let r,{name:n,reducerPath:a=n}=e;if(!n)throw Error(ne(11));let i=("function"==typeof e.reducers?e.reducers(function(){function e(e,t){return{_reducerDefinitionType:"asyncThunk",payloadCreator:e,...t}}return e.withTypes=()=>e,{reducer:e=>Object.assign({[e.name]:(...t)=>e(...t)}[e.name],{_reducerDefinitionType:"reducer"}),preparedReducer:(e,t)=>({_reducerDefinitionType:"reducerWithPrepare",prepare:e,reducer:t}),asyncThunk:e}}()):e.reducers)||{},o=Object.keys(i),l={},c={},u={},s=[],d={addCase(e,t){let r="string"==typeof e?e:e.type;if(!r)throw Error(ne(12));if(r in c)throw Error(ne(13));return c[r]=t,d},addMatcher:(e,t)=>(s.push({matcher:e,reducer:t}),d),exposeAction:(e,t)=>(u[e]=t,d),exposeCaseReducer:(e,t)=>(l[e]=t,d)};function f(){let[t={},r=[],n]="function"==typeof e.extraReducers?rI(e.extraReducers):[e.extraReducers],a={...t,...c};return function(e,t){let r,[n,a,i]=rI(t);if("function"==typeof e)r=()=>rj(e());else{let t=rj(e);r=()=>t}function o(e=r(),t){let l=[n[t.type],...a.filter(({matcher:e})=>e(t)).map(({reducer:e})=>e)];return 0===l.filter(e=>!!e).length&&(l=[i]),l.reduce((e,r)=>{if(r)if(tl(e)){let n=r(e,t);return void 0===n?e:n}else{if(tc(e))return t0(e,e=>r(e,t));let n=r(e,t);if(void 0===n){if(null===e)return e;throw Error("A case reducer on a non-draftable value must not return undefined")}return n}return e},e)}return o.getInitialState=r,o}(e.initialState,e=>{for(let t in a)e.addCase(t,a[t]);for(let t of s)e.addMatcher(t.matcher,t.reducer);for(let t of r)e.addMatcher(t.matcher,t.reducer);n&&e.addDefaultCase(n)})}o.forEach(r=>{let a=i[r],o={reducerName:r,type:`${n}/${r}`,createNotation:"function"==typeof e.reducers};"asyncThunk"===a._reducerDefinitionType?function({type:e,reducerName:t},r,n,a){if(!a)throw Error(ne(18));let{payloadCreator:i,fulfilled:o,pending:l,rejected:c,settled:u,options:s}=r,d=a(e,i,s);n.exposeAction(t,d),o&&n.addCase(d.fulfilled,o),l&&n.addCase(d.pending,l),c&&n.addCase(d.rejected,c),u&&n.addMatcher(d.settled,u),n.exposeCaseReducer(t,{fulfilled:o||rN,pending:l||rN,rejected:c||rN,settled:u||rN})}(o,a,d,t):function({type:e,reducerName:t,createNotation:r},n,a){let i,o;if("reducer"in n){if(r&&"reducerWithPrepare"!==n._reducerDefinitionType)throw Error(ne(17));i=n.reducer,o=n.prepare}else i=n;a.addCase(e,i).exposeCaseReducer(t,i).exposeAction(t,o?rw(e,o):rw(e))}(o,a,d)});let p=e=>e,y=new Map,v=new WeakMap;function h(e,t){return r||(r=f()),r(e,t)}function m(){return r||(r=f()),r.getInitialState()}function g(t,r=!1){function n(e){let a=e[t];return void 0===a&&r&&(a=rA(v,n,m)),a}function a(t=p){let n=rA(y,r,()=>new WeakMap);return rA(n,t,()=>{let n={};for(let[a,i]of Object.entries(e.selectors??{}))n[a]=function(e,t,r,n){function a(i,...o){let l=t(i);return void 0===l&&n&&(l=r()),e(l,...o)}return a.unwrapped=e,a}(i,t,()=>rA(v,t,m),r);return n})}return{reducerPath:t,getSelectors:a,get selectors(){return a(n)},selectSlice:n}}let b={name:n,reducer:h,actions:u,caseReducers:l,getInitialState:m,...g(a),injectInto(e,{reducerPath:t,...r}={}){let n=t??a;return e.inject({reducerPath:n,reducer:h},r),{...b,...g(n,!0)}}};return b}}();function rN(){}var rT="listener",rz="completed",r_="cancelled",rR=`task-${r_}`,rL=`task-${rz}`,rB=`${rT}-${r_}`,rK=`${rT}-${rz}`,rF=class{constructor(e){this.code=e,this.message=`task ${r_} (reason: ${e})`}code;name="TaskAbortError";message},rW=(e,t)=>{if("function"!=typeof e)throw TypeError(ne(32))},rV=()=>{},r$=(e,t=rV)=>(e.catch(t),e),rU=(e,t)=>(e.addEventListener("abort",t,{once:!0}),()=>e.removeEventListener("abort",t)),rH=e=>{if(e.aborted)throw new rF(e.reason)};function rG(e,t){let r=rV;return new Promise((n,a)=>{let i=()=>a(new rF(e.reason));e.aborted?i():(r=rU(e,i),t.finally(()=>r()).then(n,a))}).finally(()=>{r=rV})}var rq=async(e,t)=>{try{await Promise.resolve();let t=await e();return{status:"ok",value:t}}catch(e){return{status:e instanceof rF?"cancelled":"rejected",error:e}}finally{t?.()}},rX=e=>t=>r$(rG(e,t).then(t=>(rH(e),t))),rY=e=>{let t=rX(e);return e=>t(new Promise(t=>setTimeout(t,e)))},{assign:rZ}=Object,rQ={},rJ="listenerMiddleware",r0=e=>{let{type:t,actionCreator:r,matcher:n,predicate:a,effect:i}=e;if(t)a=rw(t).match;else if(r)t=r.type,a=r.match;else if(n)a=n;else if(a);else throw Error(ne(21));return rW(i,"options.listener"),{predicate:a,type:t,effect:i}},r1=rZ(e=>{let{type:t,predicate:r,effect:n}=r0(e);return{id:((e=21)=>{let t="",r=e;for(;r--;)t+="ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW"[64*Math.random()|0];return t})(),effect:n,type:t,predicate:r,pending:new Set,unsubscribe:()=>{throw Error(ne(22))}}},{withTypes:()=>r1}),r2=(e,t)=>{let{type:r,effect:n,predicate:a}=r0(t);return Array.from(e.values()).find(e=>("string"==typeof r?e.type===r:e.predicate===a)&&e.effect===n)},r3=e=>{e.pending.forEach(e=>{e.abort(rB)})},r6=(e,t,r)=>{try{e(t,r)}catch(e){setTimeout(()=>{throw e},0)}},r5=rZ(rw(`${rJ}/add`),{withTypes:()=>r5}),r8=rw(`${rJ}/removeAll`),r4=rZ(rw(`${rJ}/remove`),{withTypes:()=>r4}),r9=(...e)=>{console.error(`${rJ}/error`,...e)},r7=(e={})=>{let t=new Map,r=new Map,{extra:n,onError:a=r9}=e;rW(a,"onError");let i=e=>{var r;return(r=r2(t,e)??r1(e)).unsubscribe=()=>t.delete(r.id),t.set(r.id,r),e=>{r.unsubscribe(),e?.cancelActive&&r3(r)}};rZ(i,{withTypes:()=>i});let o=e=>{let r=r2(t,e);return r&&(r.unsubscribe(),e.cancelActive&&r3(r)),!!r};rZ(o,{withTypes:()=>o});let l=async(e,o,l,c)=>{var u,s;let d,f=new AbortController,p=(u=f.signal,d=async(e,t)=>{rH(u);let r=()=>{},n=[new Promise((t,n)=>{let a=i({predicate:e,effect:(e,r)=>{r.unsubscribe(),t([e,r.getState(),r.getOriginalState()])}});r=()=>{a(),n()}})];null!=t&&n.push(new Promise(e=>setTimeout(e,t,null)));try{let e=await rG(u,Promise.race(n));return rH(u),e}finally{r()}},(e,t)=>r$(d(e,t))),y=[];try{let a;e.pending.add(f),a=r.get(e)??0,r.set(e,a+1),await Promise.resolve(e.effect(o,rZ({},l,{getOriginalState:c,condition:(e,t)=>p(e,t).then(Boolean),take:p,delay:rY(f.signal),pause:rX(f.signal),extra:n,signal:f.signal,fork:(s=f.signal,(e,t)=>{rW(e,"taskExecutor");let r=new AbortController;rU(s,()=>r.abort(s.reason));let n=rq(async()=>{rH(s),rH(r.signal);let t=await e({pause:rX(r.signal),delay:rY(r.signal),signal:r.signal});return rH(r.signal),t},()=>r.abort(rL));return t?.autoJoin&&y.push(n.catch(rV)),{result:rX(s)(n),cancel(){r.abort(rR)}}}),unsubscribe:e.unsubscribe,subscribe:()=>{t.set(e.id,e)},cancelActiveListeners:()=>{e.pending.forEach((e,t,r)=>{e!==f&&(e.abort(rB),r.delete(e))})},cancel:()=>{f.abort(rB),e.pending.delete(f)},throwIfCancelled:()=>{rH(f.signal)}})))}catch(e){e instanceof rF||r6(a,e,{raisedBy:"effect"})}finally{let t;await Promise.all(y),f.abort(rK),1===(t=r.get(e)??1)?r.delete(e):r.set(e,t-1),e.pending.delete(f)}},c=()=>{for(let e of r.keys())r3(e);t.clear()};return{middleware:e=>r=>n=>{let u;if(!rm(n))return r(n);if(r5.match(n))return i(n.payload);if(r8.match(n))return void c();if(r4.match(n))return o(n.payload);let s=e.getState(),d=()=>{if(s===rQ)throw Error(ne(23));return s};try{if(u=r(n),t.size>0){let r=e.getState();for(let i of Array.from(t.values())){let t=!1;try{t=i.predicate(n,r,s)}catch(e){t=!1,r6(a,e,{raisedBy:"predicate"})}t&&l(i,n,e,d)}}}finally{s=rQ}return u},startListening:i,stopListening:o,clearListeners:c}};function ne(e){return`Minified Redux Toolkit error #${e}; visit https://redux-toolkit.js.org/Errors?code=${e} for the full message or use the non-minified dev environment for full errors. `}var nt=rM({name:"chartLayout",initialState:{layoutType:"horizontal",width:0,height:0,margin:{top:5,right:5,bottom:5,left:5},scale:1},reducers:{setLayout(e,t){e.layoutType=t.payload},setChartSize(e,t){e.width=t.payload.width,e.height=t.payload.height},setMargin(e,t){var r,n,a,i;e.margin.top=null!=(r=t.payload.top)?r:0,e.margin.right=null!=(n=t.payload.right)?n:0,e.margin.bottom=null!=(a=t.payload.bottom)?a:0,e.margin.left=null!=(i=t.payload.left)?i:0},setScale(e,t){e.scale=t.payload}}}),nr=nt.actions,nn=nr.setMargin,na=nr.setLayout,ni=nr.setChartSize,no=nr.setScale,nl=nt.reducer;function nc(e,t){return e===t||Number.isNaN(e)&&Number.isNaN(t)}function nu(e){var t;return null!=e&&"function"!=typeof e&&Number.isSafeInteger(t=e.length)&&t>=0}function ns(e){return null!==e&&("object"==typeof e||"function"==typeof e)}let nd=/^(?:0|[1-9]\d*)$/;function nf(e,t=Number.MAX_SAFE_INTEGER){switch(typeof e){case"number":return Number.isInteger(e)&&e>=0&&e{if(e!==t){let n=ny(e),a=ny(t);if(n===a&&0===n){if(et)return"desc"===r?-1:1}return"desc"===r?a-n:n-a}return 0};function nh(e){return"symbol"==typeof e||e instanceof Symbol}let nm=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,ng=/^\w*$/;function nb(e,...t){let r=t.length;return r>1&&np(e,t[0],t[1])?t=[]:r>2&&np(t[0],t[1],t[2])&&(t=[t[0]]),function(e,t,r){if(null==e)return[];Array.isArray(e)||(e=Object.values(e)),Array.isArray(t)||(t=null==t?[null]:[t]),0===t.length&&(t=[null]),Array.isArray(r)||(r=null==r?[]:[r]),r=r.map(e=>String(e));let n=(e,t)=>{let r=e;for(let e=0;e{var t;return(Array.isArray(e)&&1===e.length&&(e=e[0]),null==e||"function"==typeof e||Array.isArray(e)||!Array.isArray(t=e)&&("number"==typeof t||"boolean"==typeof t||null==t||nh(t)||"string"==typeof t&&(ng.test(t)||!nm.test(t))||0))?e:{key:e,path:W(e)}});return e.map(e=>({original:e,criteria:a.map(t=>{var r,a;return r=t,null==(a=e)||null==r?a:"object"==typeof r&&"key"in r?Object.hasOwn(a,r.key)?a[r.key]:n(a,r.path):"function"==typeof r?r(a):Array.isArray(r)?n(a,r):"object"==typeof a?a[r]:a})})).slice().sort((e,t)=>{for(let n=0;ne.original)}(e,function(e,t=1){let r=[],n=Math.floor(t),a=(e,t)=>{for(let i=0;ie.legend.settings,nw=rc([e=>e.legend.payload,nx],(e,t)=>{var r=t.itemSorter,n=e.flat(1);return r?nb(n,r):n}),nO=e.i(867719),nj=e.i(517306),nA=e.i(610010),nE=e.i(516039),nP=e.i(9506),nS=e.i(261770);function nk(e,t,r){return Array.isArray(e)&&e&&t+r!==0?e.slice(t,r+1):e}function nI(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function nC(e){for(var t=1;t"horizontal"===e&&"xAxis"===t||"vertical"===e&&"yAxis"===t||"centric"===e&&"angleAxis"===t||"radial"===e&&"radiusAxis"===t,nN=(e,t,r,n)=>{if(n)return e.map(e=>e.coordinate);var a,i,o=e.map(e=>(e.coordinate===t&&(a=!0),e.coordinate===r&&(i=!0),e.coordinate));return a||o.push(t),i||o.push(r),o},nT=(e,t,r)=>{if(!e)return null;var n=e.duplicateDomain,a=e.type,i=e.range,o=e.scale,l=e.realScaleType,c=e.isCategorical,u=e.categoricalDomain,s=e.tickCount,d=e.ticks,f=e.niceTicks,p=e.axisType;if(!o)return null;var y="scaleBand"===l&&o.bandwidth?o.bandwidth()/2:2,v=(t||r)&&"category"===a&&o.bandwidth?o.bandwidth()/y:0;return(v="angleAxis"===p&&i&&i.length>=2?2*H(i[0]-i[1])*v:v,t&&(d||f))?(d||f||[]).map((e,t)=>{var r=n?n.indexOf(e):e,a=o.map(r);return ek(a)?{coordinate:a+v,value:e,offset:v,index:t}:null}).filter(ea):c&&u?u.map((e,t)=>{var r=o.map(e);return ek(r)?{coordinate:r+v,value:e,index:t,offset:v}:null}).filter(ea):o.ticks&&!r&&null!=s?o.ticks(s).map((e,t)=>{var r=o.map(e);return ek(r)?{coordinate:r+v,value:e,index:t,offset:v}:null}).filter(ea):o.domain().map((e,t)=>{var r=o.map(e);return ek(r)?{coordinate:r+v,value:n?n[e]:e,index:t,offset:v}:null}).filter(ea)},nz={sign:e=>{var t,r=e.length;if(!(r<=0)){var n=null==(t=e[0])?void 0:t.length;if(null!=n&&!(n<=0))for(var a=0;a=0?(u[0]=i,i+=f,u[1]=i):(u[0]=o,o+=f,u[1]=o)}}}},expand:nj.stackOffsetExpand,none:nA.stackOffsetNone,silhouette:nE.stackOffsetSilhouette,wiggle:nP.stackOffsetWiggle,positive:e=>{var t,r=e.length;if(!(r<=0)){var n=null==(t=e[0])?void 0:t.length;if(null!=n&&!(n<=0))for(var a=0;a=0?(c[0]=i,i+=u,c[1]=i):(c[0]=0,c[1]=0)}}}}};function n_(e){return null==e?void 0:String(e)}function nR(e){var t=e.axis,r=e.ticks,n=e.bandSize,a=e.entry,i=e.index,o=e.dataKey;if("category"===t.type){if(!t.allowDuplicatedCategory&&t.dataKey&&null!=a[t.dataKey]){var l=er(r,"value",a[t.dataKey]);if(l)return l.coordinate+n/2}return null!=r&&r[i]?r[i].coordinate+n/2:null}var c=nD(a,null==o?t.dataKey:o),u=t.scale.map(c);return X(u)?u:null}var nL=e=>{var t=e.axis,r=e.ticks,n=e.offset,a=e.bandSize,i=e.entry,o=e.index;if("category"===t.type)return r[o]?r[o].coordinate+n:null;var l=nD(i,t.dataKey,t.scale.domain()[o]);if(null==l)return null;var c=t.scale.map(l);return X(c)?c-a/2+n:null},nB=/^dataMin[\s]*-[\s]*([0-9]+([.]{1}[0-9]+){0,1})$/,nK=/^dataMax[\s]*\+[\s]*([0-9]+([.]{1}[0-9]+){0,1})$/,nF=(e,t,r)=>{if(e&&e.scale&&e.scale.bandwidth){var n=e.scale.bandwidth();if(!r||n>0)return n}if(e&&t&&t.length>=2){for(var a=nb(t,e=>e.coordinate),i=1/0,o=1,l=a.length;oe.layout.width,nU=e=>e.layout.height,nH=e=>e.layout.scale,nG=e=>e.layout.margin,nq=rc(e=>e.cartesianAxis.xAxis,e=>Object.values(e)),nX=rc(e=>e.cartesianAxis.yAxis,e=>Object.values(e)),nY="data-recharts-item-index",nZ="data-recharts-item-id";function nQ(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function nJ(e){for(var t=1;te.brush.height,function(e){return nX(e).reduce((e,t)=>"left"!==t.orientation||t.mirror||t.hide?e:e+("number"==typeof t.width?t.width:60),0)},function(e){return nX(e).reduce((e,t)=>"right"!==t.orientation||t.mirror||t.hide?e:e+("number"==typeof t.width?t.width:60),0)},function(e){return nq(e).reduce((e,t)=>"top"!==t.orientation||t.mirror||t.hide?e:e+t.height,0)},function(e){return nq(e).reduce((e,t)=>"bottom"!==t.orientation||t.mirror||t.hide?e:e+t.height,0)},nx,e=>e.legend.size],(e,t,r,n,a,i,o,l,c,u)=>{var s={left:(r.left||0)+a,right:(r.right||0)+i},d=nJ(nJ({},{top:(r.top||0)+o,bottom:(r.bottom||0)+l}),s),f=d.bottom;d.bottom+=n;var p=e-(d=((e,t,r)=>{if(t&&r){var n=r.width,a=r.height,i=t.align,o=t.verticalAlign,l=t.layout;if(("vertical"===l||"horizontal"===l&&"middle"===o)&&"center"!==i&&X(e[i]))return nC(nC({},e),{},{[i]:e[i]+(n||0)});if(("horizontal"===l||"vertical"===l&&"center"===i)&&"middle"!==o&&X(e[o]))return nC(nC({},e),{},{[o]:e[o]+(a||0)})}return e})(d,c,u)).left-d.right,y=t-d.top-d.bottom;return nJ(nJ({brushBottom:f},d),{},{width:Math.max(p,0),height:Math.max(y,0)})}),n1=rc(n0,e=>({x:e.left,y:e.top,width:e.width,height:e.height})),n2=rc(n$,nU,(e,t)=>({x:0,y:0,width:e,height:t})),n3=(0,E.createContext)(null),n6=()=>null!=(0,E.useContext)(n3),n5=e=>e.brush,n8=rc([n5,n0,nG],(e,t,r)=>({height:e.height,x:X(e.x)?e.x:t.left,y:X(e.y)?e.y:t.top+t.height+t.brushBottom-((null==r?void 0:r.bottom)||0),width:X(e.width)?e.width:t.width})),n4=function(e,t){for(var r=arguments.length,n=Array(r>2?r-2:0),a=2;atypeof console&&console.warn&&(void 0===t&&console.warn("LogUtils requires an error message argument"),!e))if(void 0===t)console.warn("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var i=0;console.warn(t.replace(/%s/g,()=>n[i++]))}},n9="100%",n7="100%",ae={width:-1,height:-1},at=(e,t,r)=>{var n=r.width,a=void 0===n?n9:n,i=r.height,o=void 0===i?n7:i,l=r.aspect,c=r.maxHeight,u=q(a)?e:Number(a),s=q(o)?t:Number(o);return l&&l>0&&(u?s=u/l:s&&(u=s*l),c&&null!=s&&s>c&&(s=c)),{calculatedWidth:u,calculatedHeight:s}},ar={width:0,height:0,overflow:"visible"},an={width:0,overflowX:"visible"},aa={height:0,overflowY:"visible"},ai={},ao=["aspect","initialDimension","width","height","minWidth","minHeight","maxHeight","children","debounce","id","className","onResize","style"];function al(){return(al=Object.assign.bind()).apply(null,arguments)}function ac(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function au(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r({width:r,height:n}),[r,n]);return eI(a.width)&&eI(a.height)?E.createElement(ad.Provider,{value:a},t):null}var ap=()=>(0,E.useContext)(ad),ay=(0,E.forwardRef)((e,t)=>{var r,n,a,i,o,l,c=e.aspect,u=e.initialDimension,s=void 0===u?ae:u,d=e.width,f=e.height,p=e.minWidth,y=void 0===p?0:p,v=e.minHeight,h=e.maxHeight,m=e.children,g=e.debounce,b=void 0===g?0:g,x=e.id,w=e.className,O=e.onResize,j=e.style,A=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;nP.current);var I=function(e){if(Array.isArray(e))return e}(r=(0,E.useState)({containerWidth:s.width,containerHeight:s.height}))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(r)||function(e){if(e){if("string"==typeof e)return as(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?as(e,2):void 0}}(r)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),C=I[0],D=I[1],M=(0,E.useCallback)((e,t)=>{D(r=>{var n=Math.round(e),a=Math.round(t);return r.containerWidth===n&&r.containerHeight===a?r:{containerWidth:n,containerHeight:a}})},[]);(0,E.useEffect)(()=>{if(null==P.current||"u"{var t,r=e[0];if(null!=r){var n=r.contentRect,a=n.width,i=n.height;M(a,i),null==(t=k.current)||t.call(k,a,i)}};b>0&&(e=function(e,t=0,r={}){let{leading:n=!0,trailing:a=!0}=r;return function(e,t=0,r={}){let n;"object"!=typeof r&&(r={});let{leading:a=!1,trailing:i=!0,maxWait:o}=r,l=[,,];a&&(l[0]="leading"),i&&(l[1]="trailing");let c=null,u=function(e,t,{signal:r,edges:n}={}){let a,i=null,o=null!=n&&n.includes("leading"),l=null==n||n.includes("trailing"),c=()=>{null!==i&&(e.apply(a,i),a=void 0,i=null)},u=null,s=()=>{null!=u&&clearTimeout(u),u=setTimeout(()=>{u=null,l&&c(),d()},t)},d=()=>{null!==u&&(clearTimeout(u),u=null),a=void 0,i=null},f=function(...e){if(r?.aborted)return;a=this,i=e;let t=null==u;s(),o&&t&&c()};return f.schedule=s,f.cancel=d,f.flush=()=>{c()},r?.addEventListener("abort",d,{once:!0}),f}(function(...t){n=e.apply(this,t),c=null},t,{edges:l}),s=function(...t){return null!=o&&(null===c&&(c=Date.now()),Date.now()-c>=o)?(n=e.apply(this,t),c=Date.now(),u.cancel(),u.schedule(),n):(u.apply(this,t),n)};return s.cancel=u.cancel,s.flush=()=>(u.flush(),n),s}(e,t,{leading:n,maxWait:t,trailing:a})}(e,b,{trailing:!0,leading:!1}));var t=new ResizeObserver(e),r=P.current.getBoundingClientRect();return M(r.width,r.height),t.observe(P.current),()=>{t.disconnect()}},[M,b]);var N=C.containerWidth,T=C.containerHeight;n4(!c||c>0,"The aspect(%s) must be greater than zero.",c);var z=at(N,T,{width:d,height:f,aspect:c,maxHeight:h}),_=z.calculatedWidth,R=z.calculatedHeight;return n4(N<0||T<0||null!=_&&_>0||null!=R&&R>0,"The width(%s) and height(%s) of chart should be greater than 0,\n please check the style of container, or the props width(%s) and height(%s),\n or add a minWidth(%s) or minHeight(%s) or use aspect(%s) to control the\n height and width.",_,R,d,f,y,v,c),E.createElement("div",al({id:x?"".concat(x):void 0,className:(0,S.clsx)("recharts-responsive-container",w),style:au(au({},void 0===j?{}:j),{},{width:d,height:f,minWidth:y,minHeight:v,maxHeight:h}),ref:P},A),E.createElement("div",{style:(a=(n={width:d,height:f}).width,i=n.height,o=q(a),l=q(i),o&&l?ar:o?an:l?aa:ai)},E.createElement(af,{width:_,height:R},m)))}),av=(0,E.forwardRef)((e,t)=>{var r,n,a,i,o,l,c=ap();if(eI(c.width)&&eI(c.height))return e.children;var u=(n=(r={width:e.width,height:e.height,aspect:e.aspect}).width,a=r.height,i=r.aspect,o=n,l=a,void 0===o&&void 0===l?(o=n9,l=n7):void 0===o?o=i&&i>0?void 0:n9:void 0===l&&(l=i&&i>0?void 0:n7),{width:o,height:l}),s=u.width,d=u.height,f=at(void 0,void 0,{width:s,height:d,aspect:e.aspect,maxHeight:e.maxHeight}),p=f.calculatedWidth,y=f.calculatedHeight;return X(p)&&X(y)?E.createElement(af,{width:p,height:y},e.children):E.createElement(ay,al({},e,{width:s,height:d,ref:t}))});function ah(e){if(e)return{x:e.x,y:e.y,upperWidth:"upperWidth"in e?e.upperWidth:e.width,lowerWidth:"lowerWidth"in e?e.lowerWidth:e.width,width:e.width,height:e.height}}var am=()=>{var e,t=n6(),r=e6(n1),n=e6(n8),a=null==(e=e6(n5))?void 0:e.padding;return t&&n&&a?{width:n.width-a.left-a.right,height:n.height-a.top-a.bottom,x:a.left,y:a.top}:r},ag={top:0,bottom:0,left:0,right:0,width:0,height:0,brushBottom:0},ab=()=>{var e;return null!=(e=e6(n0))?e:ag},ax=e=>e.layout.layoutType,aw=()=>{var e=e6(ax);if("horizontal"===e||"vertical"===e)return e},aO=e=>{var t=e.layout.layoutType;if("centric"===t||"radial"===t)return t},aj=e=>{var t=e0(),r=n6(),n=e.width,a=e.height,i=ap(),o=n,l=a;return i&&(o=i.width>0?i.width:n,l=i.height>0?i.height:a),(0,E.useEffect)(()=>{!r&&eI(o)&&eI(l)&&t(ni({width:o,height:l}))},[t,r,o,l]),null},aA={grid:-100,barBackground:-50,area:100,cursorRectangle:200,bar:300,line:400,axis:500,scatter:600,activeBar:1e3,cursorLine:1100,activeDot:1200,label:2e3},aE={allowDecimals:!1,allowDuplicatedCategory:!0,allowDataOverflow:!1,angle:0,angleAxisId:0,axisLine:!0,axisLineType:"polygon",cx:0,cy:0,hide:!1,includeHidden:!1,label:!1,niceTicks:"auto",orientation:"outer",reversed:!1,scale:"auto",tick:!0,tickLine:!0,tickSize:8,type:"auto",zIndex:aA.axis},aP={allowDataOverflow:!1,allowDecimals:!1,allowDuplicatedCategory:!0,angle:0,axisLine:!0,includeHidden:!1,hide:!1,niceTicks:"auto",label:!1,orientation:"right",radiusAxisId:0,reversed:!1,scale:"auto",stroke:"#ccc",tick:!0,tickCount:5,tickLine:!0,type:"auto",zIndex:aA.axis},aS=(e,t)=>{if(e&&t)return null!=e&&e.reversed?[t[1],t[0]]:t};function ak(e,t,r){return"auto"!==r?r:null!=e?nM(e,t)?"category":"number":void 0}function aI(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function aC(e){for(var t=1;t{if(null!=t)return e.polarAxis.angleAxis[t]},aO],(e,t)=>{if(null!=e)return e;var r,n=null!=(r=ak(t,"angleAxis",aD.type))?r:"category";return aC(aC({},aD),{},{type:n})}),aT=rc([(e,t)=>e.polarAxis.radiusAxis[t],aO],(e,t)=>{if(null!=e)return e;var r,n=null!=(r=ak(t,"radiusAxis",aM.type))?r:"category";return aC(aC({},aM),{},{type:n})}),az=e=>e.polarOptions,a_=rc([n$,nU,n0],eY),aR=rc([az,a_],(e,t)=>{if(null!=e)return J(e.innerRadius,t,0)}),aL=rc([az,a_],(e,t)=>{if(null!=e)return J(e.outerRadius,t,.8*t)}),aB=rc([az],e=>null==e?[0,0]:[e.startAngle,e.endAngle]);rc([aN,aB],aS);var aK=rc([a_,aR,aL],(e,t,r)=>{if(null!=e&&null!=t&&null!=r)return[t,r]});rc([aT,aK],aS);var aF=rc([ax,az,aR,aL,n$,nU],(e,t,r,n,a,i)=>{if(("centric"===e||"radial"===e)&&null!=t&&null!=r&&null!=n){var o=t.cx,l=t.cy,c=t.startAngle,u=t.endAngle;return{cx:J(o,a,a/2),cy:J(l,i,i/2),innerRadius:r,outerRadius:n,startAngle:c,endAngle:u,clockWise:!1}}}),aW=e.i(174080);function aV(e,t){return!!(Array.isArray(e)&&Array.isArray(t))&&0===e.length&&0===t.length||e===t}var a$=rc(e=>e.zIndex.zIndexMap,(e,t)=>t,(e,t,r)=>r,(e,t,r)=>{if(null!=t){var n=e[t];if(null!=n)return r?n.panoramaElement:n.element}}),aU=rc(e=>e.zIndex.zIndexMap,e=>Array.from(new Set(Object.keys(e).map(e=>parseInt(e,10)).concat(Object.values(aA)))).sort((e,t)=>e-t),{memoizeOptions:{resultEqualityCheck:function(e,t){if(e.length===t.length){for(var r=0;raG(aG({},e),{},{[t]:{element:void 0,panoramaElement:void 0,consumers:0}}),{})},aX=new Set(Object.values(aA)),aY=rM({name:"zIndex",initialState:aq,reducers:{registerZIndexPortal:{reducer:(e,t)=>{var r=t.payload.zIndex;e.zIndexMap[r]?e.zIndexMap[r].consumers+=1:e.zIndexMap[r]={consumers:1,element:void 0,panoramaElement:void 0}},prepare:rP()},unregisterZIndexPortal:{reducer:(e,t)=>{var r=t.payload.zIndex;e.zIndexMap[r]&&(e.zIndexMap[r].consumers-=1,e.zIndexMap[r].consumers<=0&&!aX.has(r)&&delete e.zIndexMap[r])},prepare:rP()},registerZIndexPortalElement:{reducer:(e,t)=>{var r=t.payload,n=r.zIndex,a=r.element,i=r.isPanorama;e.zIndexMap[n]?i?e.zIndexMap[n].panoramaElement=a:e.zIndexMap[n].element=a:e.zIndexMap[n]={consumers:0,element:i?void 0:a,panoramaElement:i?a:void 0}},prepare:rP()},unregisterZIndexPortalElement:{reducer:(e,t)=>{var r=t.payload.zIndex;e.zIndexMap[r]&&(t.payload.isPanorama?e.zIndexMap[r].panoramaElement=void 0:e.zIndexMap[r].element=void 0)},prepare:rP()}}}),aZ=aY.actions,aQ=aZ.registerZIndexPortal,aJ=aZ.unregisterZIndexPortal,a0=aZ.registerZIndexPortalElement,a1=aZ.unregisterZIndexPortalElement,a2=aY.reducer;function a3(e){var t=e.zIndex,r=e.children,n=void 0!==e6(ax)&&void 0!==t&&0!==t,a=n6(),i=(0,E.useRef)(void 0),o=(0,E.useRef)(new Set),l=e0(),c=e6(e=>a$(e,t,a));if((0,E.useLayoutEffect)(()=>{if(!n){var e=o.current;e.forEach(e=>{l(aJ({zIndex:e}))}),e.clear(),i.current=void 0;return}if(o.current.has(t)||(l(aQ({zIndex:t})),o.current.add(t)),c){i.current=c;var r=o.current;r.forEach(e=>{e!==t&&(l(aJ({zIndex:e})),r.delete(e))})}},[l,t,n,c]),(0,E.useLayoutEffect)(()=>{var e=o.current;return()=>{e.forEach(e=>{l(aJ({zIndex:e}))}),e.clear()}},[l]),!n)return r;var u=null!=c?c:i.current;return u?(0,aW.createPortal)(r,u):null}function a6(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function a5(e){for(var t=1;t{var t=e.x,r=e.y,n=e.upperWidth,a=e.lowerWidth,i=e.width,o=e.height,l=e.children,c=(0,E.useMemo)(()=>({x:t,y:r,upperWidth:n,lowerWidth:a,width:i,height:o}),[t,r,n,a,i,o]);return E.createElement(ir.Provider,{value:c},l)},ii=()=>{var e=(0,E.useContext)(ir),t=am();return e||(t?ah(t):void 0)},io=(0,E.createContext)(null),il=e=>null!=e&&"function"==typeof e,ic=e=>null!=e&&"cx"in e&&X(e.cx),iu={angle:0,offset:5,zIndex:aA.label,position:"middle",textBreakAll:!1};function is(e){var t,r,n,a,i,o,l,c,u=eS(e,iu),s=u.viewBox,d=u.parentViewBox,f=u.position,p=u.value,y=u.children,v=u.content,h=u.className,m=u.textBreakAll,g=u.labelRef,b=(t=(0,E.useContext)(io),r=e6(aF),t||r),x=ii(),w=function(e){if(!ic(e))return e;var t=e.cx,r=e.cy,n=e.outerRadius,a=2*n;return{x:t-n,y:r-n,width:a,upperWidth:a,lowerWidth:a,height:a}}(o=null==s?"center"===f?x:null!=b?b:x:ic(s)?s:ah(s));if(!o||null==p&&null==y&&!(0,E.isValidElement)(v)&&"function"!=typeof v)return null;var O=ie(ie({},u),{},{viewBox:o});if((0,E.isValidElement)(v)){O.labelRef;var j=a9(O,a8);return(0,E.cloneElement)(v,j)}if("function"==typeof v){O.content;var A=a9(O,a4);if(l=(0,E.createElement)(v,A),(0,E.isValidElement)(l))return l}else n=u.value,a=u.formatter,i=null==u.children?n:u.children,l="function"==typeof a?a(i):i;var P=z(u);if(ic(o)){if("insideStart"===f||"insideEnd"===f||"end"===f)return((e,t,r,n,a)=>{var i,o,l=e.offset,c=e.className,u=a.cx,s=a.cy,d=a.innerRadius,f=a.outerRadius,p=a.startAngle,y=a.endAngle,v=a.clockWise,h=(d+f)/2,m=H(y-p)*Math.min(Math.abs(y-p),360),g=m>=0?1:-1;switch(t){case"insideStart":i=p+g*l,o=v;break;case"insideEnd":i=y-g*l,o=!v;break;case"end":i=y+g*l,o=v;break;default:throw Error("Unsupported position ".concat(t))}o=m<=0?o:!o;var b=eX(u,s,h,i),x=eX(u,s,h,i+(o?1:-1)*359),w="M".concat(b.x,",").concat(b.y,"\n A").concat(h,",").concat(h,",0,1,").concat(+!o,",\n ").concat(x.x,",").concat(x.y),O=null==e.id?Q("recharts-radial-line-"):e.id;return E.createElement("text",it({},n,{dominantBaseline:"central",className:(0,S.clsx)("recharts-radial-bar-label",c)}),E.createElement("defs",null,E.createElement("path",{id:O,d:w})),E.createElement("textPath",{xlinkHref:"#".concat(O)},r))})(u,f,l,P,o);c=((e,t,r)=>{var n=e.cx,a=e.cy,i=e.innerRadius,o=e.outerRadius,l=(e.startAngle+e.endAngle)/2;if("outside"===r){var c=eX(n,a,o+t,l),u=c.x;return{x:u,y:c.y,textAnchor:u>=n?"start":"end",verticalAnchor:"middle"}}if("center"===r)return{x:n,y:a,textAnchor:"middle",verticalAnchor:"middle"};if("centerTop"===r)return{x:n,y:a,textAnchor:"middle",verticalAnchor:"start"};if("centerBottom"===r)return{x:n,y:a,textAnchor:"middle",verticalAnchor:"end"};var s=eX(n,a,(i+o)/2,l);return{x:s.x,y:s.y,textAnchor:"middle",verticalAnchor:"middle"}})(o,u.offset,u.position)}else{if(!w)return null;var k=(e=>{var t=e.viewBox,r=e.position,n=e.offset,a=void 0===n?0:n,i=e.parentViewBox,o=e.clamp,l=ah(t),c=l.x,u=l.y,s=l.height,d=l.upperWidth,f=l.lowerWidth,p=c+(d-f)/2,y=(c+p)/2,v=(d+f)/2,h=s>=0?1:-1,m=h*a,g=h>0?"end":"start",b=h>0?"start":"end",x=d>=0?1:-1,w=x*a,O=x>0?"end":"start",j=x>0?"start":"end";if("top"===r){var A={x:c+d/2,y:u-m,horizontalAnchor:"middle",verticalAnchor:g};return o&&i&&(A.height=Math.max(u-i.y,0),A.width=d),A}if("bottom"===r){var E={x:p+f/2,y:u+s+m,horizontalAnchor:"middle",verticalAnchor:b};return o&&i&&(E.height=Math.max(i.y+i.height-(u+s),0),E.width=f),E}if("left"===r){var P={x:y-w,y:u+s/2,horizontalAnchor:O,verticalAnchor:"middle"};return o&&i&&(P.width=Math.max(P.x-i.x,0),P.height=s),P}if("right"===r){var S={x:y+v+w,y:u+s/2,horizontalAnchor:j,verticalAnchor:"middle"};return o&&i&&(S.width=Math.max(i.x+i.width-S.x,0),S.height=s),S}var k=o&&i?{width:v,height:s}:{};return"insideLeft"===r?a5({x:y+w,y:u+s/2,horizontalAnchor:j,verticalAnchor:"middle"},k):"insideRight"===r?a5({x:y+v-w,y:u+s/2,horizontalAnchor:O,verticalAnchor:"middle"},k):"insideTop"===r?a5({x:c+d/2,y:u+m,horizontalAnchor:"middle",verticalAnchor:b},k):"insideBottom"===r?a5({x:p+f/2,y:u+s-m,horizontalAnchor:"middle",verticalAnchor:g},k):"insideTopLeft"===r?a5({x:c+w,y:u+m,horizontalAnchor:j,verticalAnchor:b},k):"insideTopRight"===r?a5({x:c+d-w,y:u+m,horizontalAnchor:O,verticalAnchor:b},k):"insideBottomLeft"===r?a5({x:p+w,y:u+s-m,horizontalAnchor:j,verticalAnchor:g},k):"insideBottomRight"===r?a5({x:p+f-w,y:u+s-m,horizontalAnchor:O,verticalAnchor:g},k):r&&"object"==typeof r&&(X(r.x)||q(r.x))&&(X(r.y)||q(r.y))?a5({x:c+J(r.x,v),y:u+J(r.y,s),horizontalAnchor:"end",verticalAnchor:"end"},k):a5({x:c+d/2,y:u+s/2,horizontalAnchor:"middle",verticalAnchor:"middle"},k)})({viewBox:w,position:f,offset:u.offset,parentViewBox:ic(d)?void 0:d,clamp:!0});c=ie(ie({x:k.x,y:k.y,textAnchor:k.horizontalAnchor,verticalAnchor:k.verticalAnchor},void 0!==k.width?{width:k.width}:{}),void 0!==k.height?{height:k.height}:{})}return E.createElement(a3,{zIndex:u.zIndex},E.createElement(eU,it({ref:g,className:(0,S.clsx)("recharts-label",void 0===h?"":h)},P,c,{textAnchor:eL(P.textAnchor)?P.textAnchor:c.textAnchor,breakAll:m}),l))}function id(e){var t=e.label,r=e.labelRef;return((e,t,r)=>{if(!e)return null;var n={viewBox:t,labelRef:r};return!0===e?E.createElement(is,it({key:"label-implicit"},n)):Y(e)?E.createElement(is,it({key:"label-implicit",value:e},n)):(0,E.isValidElement)(e)?e.type===is?(0,E.cloneElement)(e,ie({key:"label-implicit"},n)):E.createElement(is,it({key:"label-implicit",content:e},n)):il(e)?E.createElement(is,it({key:"label-implicit",content:e},n)):e&&"object"==typeof e?E.createElement(is,it({},e,{key:"label-implicit"},n)):null})(t,ii(),r)||null}is.displayName="Label";var ip=["valueAccessor"],iy=["dataKey","clockWise","id","textBreakAll","zIndex"];function iv(){return(iv=Object.assign.bind()).apply(null,arguments)}function ih(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{var t=Array.isArray(e.value)?e.value[e.value.length-1]:e.value;if(null==t||"string"==typeof t||"number"==typeof t||"boolean"==typeof t)return t},ig=(0,E.createContext)(void 0),ib=ig.Provider,ix=(0,E.createContext)(void 0),iw=ix.Provider;function iO(e){var t=e.valueAccessor,r=void 0===t?im:t,n=ih(e,ip),a=n.dataKey,i=(n.clockWise,n.id),o=n.textBreakAll,l=n.zIndex,c=ih(n,iy),u=(0,E.useContext)(ig),s=(0,E.useContext)(ix),d=u||s;return d&&d.length?E.createElement(a3,{zIndex:null!=l?l:aA.label},E.createElement(L,{className:"recharts-label-list"},d.map((e,t)=>{var l,u=null==a?r(e,t):nD(e.payload,a),s=null==i?{}:{id:"".concat(i,"-").concat(t)};return E.createElement(is,iv({key:"label-".concat(t)},z(e),c,s,{fill:null!=(l=n.fill)?l:e.fill,parentViewBox:e.parentViewBox,value:u,textBreakAll:o,viewBox:e.viewBox,index:t,zIndex:0}))}))):null}function ij(e){var t=e.label;return t?!0===t?E.createElement(iO,{key:"labelList-implicit"}):E.isValidElement(t)||il(t)?E.createElement(iO,{key:"labelList-implicit",content:t}):"object"==typeof t?E.createElement(iO,iv({key:"labelList-implicit"},t,{type:String(t.type)})):null:null}iO.displayName="LabelList";var iA=e=>"radius"in e&&"startAngle"in e&&"endAngle"in e,iE=(e,t)=>{if(!e||"function"==typeof e||"boolean"==typeof e)return null;var r=e;if((0,E.isValidElement)(e)&&(r=e.props),"object"!=typeof r&&"function"!=typeof r)return null;var n={};return Object.keys(r).forEach(e=>{I(e)&&"function"==typeof r[e]&&(n[e]=t||(t=>r[e](r,t)))}),n},iP=(e,t,r)=>{if(null===e||"object"!=typeof e&&"function"!=typeof e)return null;var n=null;return Object.keys(e).forEach(a=>{var i=e[a];I(a)&&"function"==typeof i&&(n||(n={}),n[a]=e=>(i(t,r,e),null))}),n};function iS(){return(iS=Object.assign.bind()).apply(null,arguments)}var ik=e=>{var t=e.cx,r=e.cy,n=e.r,a=e.className,i=(0,S.clsx)("recharts-dot",a);return X(t)&&X(r)&&X(n)?E.createElement("circle",iS({},N(e),iE(e),{className:i,cx:t,cy:r,r:n})):null},iI=e.i(179684),iC=e=>"string"==typeof e?e:e?e.displayName||e.name||"Component":"",iD=null,iM=null,iN=e=>{if(e===iD&&Array.isArray(iM))return iM;var t=[];return E.Children.forEach(e,e=>{null!=e&&((0,iI.isFragment)(e)?t=t.concat(iN(e.props.children)):t.push(e))}),iM=t,iD=e,t};function iT(e,t){var r=[],n=[];return n=Array.isArray(t)?t.map(e=>iC(e)):[iC(t)],iN(e).forEach(e=>{var t=V(e,"type.displayName")||V(e,"type.name");t&&-1!==n.indexOf(t)&&r.push(e)}),r}var iz=e=>!e||"object"!=typeof e||!("clipDot"in e)||!!e.clipDot,i_=["points"];function iR(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function iL(e){for(var t=1;t{var l,c,u=iL(iL(iL({r:3},o),f),{},{index:n,cx:null!=(l=e.x)?l:void 0,cy:null!=(c=e.y)?c:void 0,dataKey:i,value:e.value,payload:e.payload,points:t});return E.createElement(iK,{key:"dot-".concat(n),option:r,dotProps:u,className:a})}),y={};return l&&null!=c&&(y.clipPath="url(#clipPath-".concat(d?"":"dots-").concat(c,")")),E.createElement(a3,{zIndex:s},E.createElement(L,iB({className:n},y),p))}function iW(e){var t;return e?(e=nh(t=e)?NaN:Number(t))===1/0||e===-1/0?(e<0?-1:1)*Number.MAX_VALUE:e==e?e:0:0===e?e:0}function iV(e,t,r){r&&"number"!=typeof r&&np(e,t,r)&&(t=r=void 0),e=iW(e),void 0===t?(t=e,e=0):t=iW(t),r=void 0===r?ee.chartData,iU=rc([i$],e=>{var t=null!=e.chartData?e.chartData.length-1:0;return{chartData:e.chartData,computedData:e.computedData,dataEndIndex:t,dataStartIndex:0}}),iH=(e,t,r,n)=>n?iU(e):i$(e),iG=(e,t,r)=>r?iU(e):i$(e),iq=rc([iH],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return null!=t?t.slice(r,n+1):[]}),iX=rc([iU],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return null!=t?t.slice(r,n+1):[]}),iY=rc([i$],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return null!=t?t.slice(r,n+1):[]});function iZ(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],c=!0,u=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;c=!1}else for(;!(c=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);c=!0);}catch(e){u=!0,a=e}finally{try{if(!c&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(u)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return iQ(e,t);var r=({}).toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?iQ(e,t):void 0}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function iQ(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);rtypeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],c=!0,u=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;c=!1}else for(;!(c=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);c=!0);}catch(e){u=!0,a=e}finally{try{if(!c&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(u)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return i8(e,t);var r=({}).toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?i8(e,t):void 0}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i8(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=i5(e,2),r=t[0],n=t[1],a=r,i=n;return r>n&&(a=n,i=r),[a,i]},i9=(e,t,r)=>{if(e.lte(0))return new i2.default(0);var n=i3(e.toNumber()),a=new i2.default(10).pow(n),i=e.div(a),o=1!==n?.05:.1,l=new i2.default(Math.ceil(i.div(o).toNumber())).add(r).mul(o).mul(a);return new i2.default(t?l.toNumber():Math.ceil(l.toNumber()))},i7=(e,t,r)=>{if(e.lte(0))return new i2.default(0);var n,a=[1,2,2.5,5],i=e.toNumber(),o=Math.floor(new i2.default(i).abs().log(10).toNumber()),l=new i2.default(10).pow(o),c=e.div(l).toNumber(),u=a.findIndex(e=>e>=c-1e-10);if(-1===u&&(l=l.mul(10),u=0),(u+=r)>=a.length){var s=Math.floor(u/a.length);u%=a.length,l=l.mul(new i2.default(10).pow(s))}var d=null!=(n=a[u])?n:1,f=new i2.default(d).mul(l);return t?f:new i2.default(Math.ceil(f.toNumber()))},oe=function(e,t,r,n){var a,i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,o=arguments.length>5&&void 0!==arguments[5]?arguments[5]:i9;if(!Number.isFinite((t-e)/(r-1)))return{step:new i2.default(0),tickMin:new i2.default(0),tickMax:new i2.default(0)};var l=o(new i2.default(t).sub(e).div(r-1),n,i),c=Math.ceil((a=e<=0&&t>=0?new i2.default(0):(a=new i2.default(e).add(t).div(2)).sub(new i2.default(a).mod(l))).sub(e).div(l).toNumber()),u=Math.ceil(new i2.default(t).sub(a).div(l).toNumber()),s=c+u+1;return s>r?oe(e,t,r,n,i+1,o):(s0?u+(r-s):u,c=t>0?c:c+(r-s)),{step:l,tickMin:a.sub(new i2.default(c).mul(l)),tickMax:a.add(new i2.default(u).mul(l))})},ot=function(e){var t=i5(e,2),r=t[0],n=t[1],a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:6,i=!(arguments.length>2)||void 0===arguments[2]||arguments[2],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"auto",l=Math.max(a,2),c=i5(i4([r,n]),2),u=c[0],s=c[1];if(u===-1/0||s===1/0){var d=s===1/0?[u,...Array(a-1).fill(1/0)]:[...Array(a-1).fill(-1/0),s];return r>n?d.reverse():d}if(u===s)return((e,t,r)=>{var n=new i2.default(1),a=new i2.default(e);if(!a.isint()&&r){var i=Math.abs(e);i<1?(n=new i2.default(10).pow(i3(e)-1),a=new i2.default(Math.floor(a.div(n).toNumber())).mul(n)):i>1&&(a=new i2.default(Math.floor(e)))}else 0===e?a=new i2.default(Math.floor((t-1)/2)):r||(a=new i2.default(Math.floor(e)));for(var o=Math.floor((t-1)/2),l=[],c=0;cn?y.reverse():y},or=function(e,t){var r=i5(e,2),n=r[0],a=r[1],i=!(arguments.length>2)||void 0===arguments[2]||arguments[2],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"auto",l=i5(i4([n,a]),2),c=l[0],u=l[1];if(c===-1/0||u===1/0)return[n,a];if(c===u)return[c];var s=Math.max(t,2),d=("snap125"===o?i7:i9)(new i2.default(u).sub(c).div(s-1),i,0),f=[...i6(new i2.default(c),new i2.default(u),d),u];if(!1===i){var p=(f=f.map(e=>Math.round(e))).length-1;p>0&&f[p]===f[p-1]&&(f=f.slice(0,p))}return n>a?f.reverse():f},on=e=>e.rootProps.maxBarSize,oa=e=>e.rootProps.barCategoryGap,oi=e=>e.rootProps.stackOffset,oo=e=>e.rootProps.reverseStackOrder,ol=e=>e.options.chartName,oc=e=>e.rootProps.syncId,ou=e=>e.rootProps.syncMethod,os=e=>e.options.eventEmitter,od=(e,t)=>t,of=(e,t,r)=>r;function op(e){return null==e?void 0:e.id}function oy(e,t,r){var n=t.chartData,a=void 0===n?[]:n,i=r.allowDuplicatedCategory,o=r.dataKey,l=new Map;return e.forEach(e=>{var t,r=null!=(t=e.data)?t:a;if(null!=r&&0!==r.length){var n=op(e);r.forEach((t,r)=>{var a,c=null==o||i?r:String(nD(t,o,null)),u=nD(t,e.dataKey,0);Object.assign(a=l.has(c)?l.get(c):{},{[n]:u}),l.set(c,a)})}}),Array.from(l.values())}function ov(e){return"stackId"in e&&null!=e.stackId&&null!=e.dataKey}var oh=(e,t)=>e===t||null!=e&&null!=t&&e[0]===t[0]&&e[1]===t[1],om=e=>{var t=ax(e);return"horizontal"===t?"xAxis":"vertical"===t?"yAxis":"centric"===t?"angleAxis":"radiusAxis"},og=e=>e.tooltip.settings.axisId;function ob(e){if(null!=e){var t=e.ticks,r=e.bandwidth,n=e.range(),a=[Math.min(...n),Math.max(...n)];return{domain:()=>e.domain(),range:function(e){function t(){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}(()=>a),rangeMin:()=>a[0],rangeMax:()=>a[1],isInRange(e){var t=a[0],r=a[1];return t<=r?e>=t&&e<=r:e>=r&&e<=t},bandwidth:r?()=>r.call(e):void 0,ticks:t?r=>t.call(e,r):void 0,map:(t,r)=>{var n=e(t);if(null!=n){if(e.bandwidth&&null!=r&&r.position){var a=e.bandwidth();switch(r.position){case"middle":n+=a/2;break;case"end":n+=a}}return n}}}}}var ox=(e,t)=>{if(null!=t)if("linear"!==e)return t;else{if(!iJ(t)){for(var r,n,a=0;an)&&(n=i))}return void 0!==r&&void 0!==n?[r,n]:void 0}return t}};e.s([],925212),e.i(925212);var ow=e.i(429061);e.s(["scaleBand",()=>ow.scaleBand,"scaleDiverging",()=>ow.scaleDiverging,"scaleDivergingLog",()=>ow.scaleDivergingLog,"scaleDivergingPow",()=>ow.scaleDivergingPow,"scaleDivergingSqrt",()=>ow.scaleDivergingSqrt,"scaleDivergingSymlog",()=>ow.scaleDivergingSymlog,"scaleIdentity",()=>ow.scaleIdentity,"scaleImplicit",()=>ow.scaleImplicit,"scaleLinear",()=>ow.scaleLinear,"scaleLog",()=>ow.scaleLog,"scaleOrdinal",()=>ow.scaleOrdinal,"scalePoint",()=>ow.scalePoint,"scalePow",()=>ow.scalePow,"scaleQuantile",()=>ow.scaleQuantile,"scaleQuantize",()=>ow.scaleQuantize,"scaleRadial",()=>ow.scaleRadial,"scaleSequential",()=>ow.scaleSequential,"scaleSequentialLog",()=>ow.scaleSequentialLog,"scaleSequentialPow",()=>ow.scaleSequentialPow,"scaleSequentialQuantile",()=>ow.scaleSequentialQuantile,"scaleSequentialSqrt",()=>ow.scaleSequentialSqrt,"scaleSequentialSymlog",()=>ow.scaleSequentialSymlog,"scaleSqrt",()=>ow.scaleSqrt,"scaleSymlog",()=>ow.scaleSymlog,"scaleThreshold",()=>ow.scaleThreshold,"scaleTime",()=>ow.scaleTime,"scaleUtc",()=>ow.scaleUtc,"tickFormat",()=>ow.tickFormat],979357);var oO=e.i(979357);function oj(e,t,r){if("function"==typeof e)return e.copy().domain(t).range(r);if(null!=e){var n=function(e){if(e in oO&&"function"==typeof oO[e])return oO[e]();var t="scale".concat(en(e));if(t in oO&&"function"==typeof oO[t])return oO[t]()}(e);if(null!=n)return n.domain(t).range(r),n}}function oA(e,t,r,n){if(null!=r&&null!=n)return"function"==typeof e.scale?oj(e.scale,r,n):oj(t,r,n)}var oE=(e,t,r)=>{if(null!=e){var n=e.scale,a=e.type;if("auto"===n)return"category"===a&&r&&(r.indexOf("LineChart")>=0||r.indexOf("AreaChart")>=0||r.indexOf("ComposedChart")>=0&&!t)?"point":"category"===a?"band":"linear";if("string"==typeof n)return"scale".concat(en(n))in oO?n:"point"}};function oP(e,t){if(e){var r=null!=t?t:e.domain(),n=r.map(t=>{var r;return null!=(r=e(t))?r:0}),a=e.range();if(0!==r.length&&!(a.length<2))return e=>{var t,a,i=function(e,t){for(var r=0,n=e.length,a=e[0]t)?r=i+1:n=i}return r}(n,e);return i<=0?r[0]:i>=r.length?r[r.length-1]:Math.abs(e-(null!=(t=n[i-1])?t:0))<=Math.abs(e-(null!=(a=n[i])?a:0))?r[i-1]:r[i]}}}function oS(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function ok(e){for(var t=1;ttypeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],c=!0,u=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;c=!1}else for(;!(c=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);c=!0);}catch(e){u=!0,a=e}finally{try{if(!c&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(u)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return oC(e,t);var r=({}).toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?oC(e,t):void 0}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function oC(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);re.cartesianAxis.xAxis[t],oT=(e,t)=>{var r=oN(e,t);return null==r?oM:r},oz={allowDataOverflow:!1,allowDecimals:!0,allowDuplicatedCategory:!0,angle:0,dataKey:void 0,domain:oD,hide:!0,id:0,includeHidden:!1,interval:"preserveEnd",minTickGap:5,mirror:!1,name:void 0,orientation:"left",padding:{top:0,bottom:0},reversed:!1,scale:"auto",tick:!0,tickCount:5,tickFormatter:void 0,ticks:void 0,type:"number",unit:void 0,niceTicks:"auto",width:60},o_=(e,t)=>e.cartesianAxis.yAxis[t],oR=(e,t)=>{var r=o_(e,t);return null==r?oz:r},oL={domain:[0,"auto"],includeHidden:!1,reversed:!1,allowDataOverflow:!1,allowDuplicatedCategory:!1,dataKey:void 0,id:0,name:"",range:[64,64],scale:"auto",type:"number",unit:""},oB=(e,t)=>{var r=e.cartesianAxis.zAxis[t];return null==r?oL:r},oK=(e,t,r)=>{switch(t){case"xAxis":return oT(e,r);case"yAxis":return oR(e,r);case"zAxis":return oB(e,r);case"angleAxis":return aN(e,r);case"radiusAxis":return aT(e,r);default:throw Error("Unexpected axis type: ".concat(t))}},oF=(e,t,r)=>{switch(t){case"xAxis":return oT(e,r);case"yAxis":return oR(e,r);case"angleAxis":return aN(e,r);case"radiusAxis":return aT(e,r);default:throw Error("Unexpected axis type: ".concat(t))}},oW=e=>e.graphicalItems.cartesianItems.some(e=>"bar"===e.type)||e.graphicalItems.polarItems.some(e=>"radialBar"===e.type);function oV(e,t){return r=>{switch(e){case"xAxis":return"xAxisId"in r&&r.xAxisId===t;case"yAxis":return"yAxisId"in r&&r.yAxisId===t;case"zAxis":return"zAxisId"in r&&r.zAxisId===t;case"angleAxis":return"angleAxisId"in r&&r.angleAxisId===t;case"radiusAxis":return"radiusAxisId"in r&&r.radiusAxisId===t;default:return!1}}}var o$=e=>e.graphicalItems.cartesianItems,oU=rc([od,of],oV),oH=(e,t,r)=>e.filter(r).filter(e=>(null==t?void 0:t.includeHidden)===!0||!e.hide),oG=rc([o$,oK,oU],oH,{memoizeOptions:{resultEqualityCheck:aV}}),oq=rc([oG],e=>e.filter(e=>"area"===e.type||"bar"===e.type).filter(ov)),oX=e=>e.filter(e=>!("stackId"in e)||void 0===e.stackId),oY=rc([oG],oX),oZ=e=>e.map(e=>e.data).filter(Boolean).flat(1),oQ=rc([oG],e=>e.some(e=>!e.data)),oJ=rc([oG],oZ,{memoizeOptions:{resultEqualityCheck:aV}}),o0=(e,t)=>{var r=t.chartData,n=t.dataStartIndex,a=t.dataEndIndex;return e.length>0?e:(void 0===r?[]:r).slice(n,a+1)},o1=rc([oJ,iH],o0),o2=(e,t,r)=>(null==t?void 0:t.dataKey)!=null?e.map(e=>({value:nD(e,t.dataKey)})):r.length>0?r.map(e=>e.dataKey).flatMap(t=>e.map(e=>({value:nD(e,t)}))):e.map(e=>({value:e})),o3=(e,t,r,n,a,i)=>{var o=n.chartData,l=n.dataStartIndex,c=n.dataEndIndex,u=o2(e,t,r);return a&&(null==t?void 0:t.dataKey)!=null&&i.length>0?[...(void 0===o?[]:o).slice(l,c+1).map(e=>({value:nD(e,t.dataKey)})).filter(e=>null!=e.value),...u]:u},o6=rc([o1,oK,oG,iH,oQ,oJ],o3);function o5(e){if(Y(e)||e instanceof Date){var t=Number(e);if(ek(t))return t}}function o8(e){if(Array.isArray(e)){var t=[o5(e[0]),o5(e[1])];return iJ(t)?t:void 0}var r=o5(e);if(null!=r)return[r,r]}function o4(e){return e.map(o5).filter(ea)}function o9(e,t){var r=o5(e),n=o5(t);return null==r&&null==n?0:null==r?-1:null==n?1:r-n}var o7=rc([o6],e=>null==e?void 0:e.map(e=>e.value).sort(o9));function le(e,t){switch(e){case"xAxis":return"x"===t.direction;case"yAxis":return"y"===t.direction;default:return!1}}var lt=e=>{var t=om(e),r=og(e);return oF(e,t,r)},lr=rc([lt],e=>null==e?void 0:e.dataKey),ln=rc([oq,iH,lt],oy),la=(e,t,r,n)=>Object.fromEntries(Object.entries(t.reduce((e,t)=>{if(null==t.stackId)return e;var r=e[t.stackId];return null==r&&(r=[]),r.push(t),e[t.stackId]=r,e},{})).map(t=>{var a,i,o,l=oI(t,2),c=l[0],u=l[1],s=n?[...u].reverse():u,d=s.map(op);return[c,{stackedData:(i=null!=(a=nz[r])?a:nA.stackOffsetNone,(o=(0,nO.stack)().keys(d).value((e,t)=>Number(nD(e,t,0))).order(nS.stackOrderNone).offset(i)(e)).forEach((t,r)=>{t.forEach((t,n)=>{var a=nD(e[n],d[r],0);Array.isArray(a)&&2===a.length&&X(a[0])&&X(a[1])&&(t[0]=a[0],t[1]=a[1])})}),o),graphicalItems:s}]})),li=rc([ln,oq,oi,oo],la),lo=(e,t,r,n)=>{var a=t.dataStartIndex,i=t.dataEndIndex;if(null==n&&"zAxis"!==r){if(null!=e&&0!==Object.keys(e).length){let t;return[(t=Object.keys(e).reduce((t,r)=>{var n=e[r];if(!n)return t;var o=n.stackedData.reduce((e,t)=>{var r,n=[Math.min(...r=nk(t,a,i).flat(2).filter(X)),Math.max(...r)];return ek(n[0])&&ek(n[1])?[Math.min(e[0],n[0]),Math.max(e[1],n[1])]:e},[1/0,-1/0]);return[Math.min(o[0],t[0]),Math.max(o[1],t[1])]},[1/0,-1/0]))[0]===1/0?0:t[0],t[1]===-1/0?0:t[1]]}return}},ll=rc([oK],e=>e.allowDataOverflow),lc=e=>{var t;if(null==e||!("domain"in e))return oD;if(null!=e.domain)return e.domain;if("ticks"in e&&null!=e.ticks){if("number"===e.type){var r=o4(e.ticks);return[Math.min(...r),Math.max(...r)]}if("category"===e.type)return e.ticks.map(String)}return null!=(t=null==e?void 0:e.domain)?t:oD},lu=rc([oK],lc),ls=rc([lu,ll],i1),ld=rc([li,i$,od,ls],lo,{memoizeOptions:{resultEqualityCheck:oh}}),lf=e=>e.errorBars,lp=function(){for(var e=arguments.length,t=Array(e),r=0;r5&&void 0!==arguments[5]?arguments[5]:[];if(r.length>0&&r.forEach(e=>{var r,c=null!=e.data?[...e.data]:l,u=null==(r=n[e.id])?void 0:r.filter(e=>le(a,e));c.forEach(r=>{var n,a=nD(r,null!=(n=t.dataKey)?n:e.dataKey),l=function(e,t,r){if(!r||!r.length)return[];if("number"!=typeof t||G(t)){if(Array.isArray(t)){var n,a=o4(t);a.length>0&&(n=Math.max(...a))}}else n=t;return null==n?[]:o4(r.flatMap(t=>{var r,a,i=nD(e,t.dataKey);if(Array.isArray(i)){var o=oI(i,2);r=o[0],a=o[1]}else r=a=i;if(ek(r)&&ek(a))return[n-r,n+a]}))}(r,a,u);if(l.length>=2){var c=Math.min(...l),s=Math.max(...l);(null==i||co)&&(o=s)}var d=o8(a);null!=d&&(i=null==i?d[0]:Math.min(i,d[0]),o=null==o?d[1]:Math.max(o,d[1]))})}),(null==t?void 0:t.dataKey)!=null&&0===r.length&&e.forEach(e=>{var r=o8(nD(e,t.dataKey));null!=r&&(i=null==i?r[0]:Math.min(i,r[0]),o=null==o?r[1]:Math.max(o,r[1]))}),ek(i)&&ek(o))return[i,o]},lv=rc([o1,oK,oY,lf,od,iq],ly,{memoizeOptions:{resultEqualityCheck:oh}});function lh(e){var t=e.value;if(Y(t)||t instanceof Date)return t}var lm=e=>e.referenceElements.dots,lg=(e,t,r)=>e.filter(e=>"extendDomain"===e.ifOverflow).filter(e=>"xAxis"===t?e.xAxisId===r:e.yAxisId===r),lb=rc([lm,od,of],lg),lx=e=>e.referenceElements.areas,lw=rc([lx,od,of],lg),lO=e=>e.referenceElements.lines,lj=rc([lO,od,of],lg),lA=(e,t)=>{if(null!=e){var r=o4(e.map(e=>"xAxis"===t?e.x:e.y));if(0!==r.length)return[Math.min(...r),Math.max(...r)]}},lE=rc(lb,od,lA),lP=(e,t)=>{if(null!=e){var r=o4(e.flatMap(e=>["xAxis"===t?e.x1:e.y1,"xAxis"===t?e.x2:e.y2]));if(0!==r.length)return[Math.min(...r),Math.max(...r)]}},lS=rc([lw,od],lP),lk=(e,t)=>{if(null!=e){var r=e.flatMap(e=>"xAxis"===t?function(e){if(null!=e.x)return o4([e.x]);var t,r=null==(t=e.segment)?void 0:t.map(e=>e.x);return null==r||0===r.length?[]:o4(r)}(e):function(e){if(null!=e.y)return o4([e.y]);var t,r=null==(t=e.segment)?void 0:t.map(e=>e.y);return null==r||0===r.length?[]:o4(r)}(e));if(0!==r.length)return[Math.min(...r),Math.max(...r)]}},lI=rc([lj,od],lk),lC=rc(lE,lI,lS,(e,t,r)=>lp(e,r,t)),lD=(e,t,r,n,a,i,o,l,c)=>{if(null!=r)return r;var u="vertical"===o&&"xAxis"===l||"horizontal"===o&&"yAxis"===l?lp(n,i,a):lp(i,a),s=function(e,t,r){if(r||null!=t){if("function"==typeof e&&null!=t)try{var n=e(t,r);if(iJ(n))return i0(n,t,r)}catch(e){}if(Array.isArray(e)&&2===e.length){var a,i,o=iZ(e,2),l=o[0],c=o[1];if("auto"===l)null!=t&&(a=Math.min(...t));else if(X(l))a=l;else if("function"==typeof l)try{null!=t&&(a=l(null==t?void 0:t[0]))}catch(e){}else if("string"==typeof l&&nB.test(l)){var u=nB.exec(l);if(null==u||null==u[1]||null==t)a=void 0;else{var s=+u[1];a=t[0]-s}}else a=null==t?void 0:t[0];if("auto"===c)null!=t&&(i=Math.max(...t));else if(X(c))i=c;else if("function"==typeof c)try{null!=t&&(i=c(null==t?void 0:t[1]))}catch(e){}else if("string"==typeof c&&nK.test(c)){var d=nK.exec(c);if(null==d||null==d[1]||null==t)i=void 0;else{var f=+d[1];i=t[1]+f}}else i=null==t?void 0:t[1];var p=[a,i];if(iJ(p))return null==t?p:i0(p,t,r)}}}(t,u,e.allowDataOverflow);return null!=s?s:e.allowDataOverflow&&null==u&&null!=c?c:s},lM=rc([oK],e=>{if(null!=e&&"number"===e.type&&"ticks"in e&&null!=e.ticks){var t=o4(e.ticks);if(0!==t.length)return[Math.min(...t),Math.max(...t)]}},{memoizeOptions:{resultEqualityCheck:oh}}),lN=rc([oK,lu,ls,ld,lv,lC,ax,od,lM],lD,{memoizeOptions:{resultEqualityCheck:oh}}),lT=[0,1],lz=(e,t,r,n,a,i,o)=>{if(null!=e&&null!=r&&0!==r.length||void 0!==o){var l,c,u=e.dataKey,s=e.type,d=nM(t,i);return d&&null==u?iV(0,null!=(c=null==r?void 0:r.length)?c:0):"category"===s?(l=n.map(lh).filter(e=>null!=e),d&&(null==e.dataKey||e.allowDuplicatedCategory&&ee(l))?iV(0,n.length):e.allowDuplicatedCategory?l:Array.from(new Set(l))):"expand"!==a||d?o:lT}},l_=rc([oK,ax,o1,o6,oi,od,lN],lz),lR=rc([oK,oW,ol],oE),lL=(e,t,r)=>{var n=t.niceTicks;if("none"!==n){var a=lc(t),i=Array.isArray(a)&&("auto"===a[0]||"auto"===a[1]);if(("snap125"===n||"adaptive"===n)&&null!=t&&t.tickCount&&iJ(e)){if(i)return ot(e,t.tickCount,t.allowDecimals,n);if("number"===t.type)return or(e,t.tickCount,t.allowDecimals,n)}if("auto"===n&&"linear"===r&&null!=t&&t.tickCount){if(i&&iJ(e))return ot(e,t.tickCount,t.allowDecimals,"adaptive");if("number"===t.type&&iJ(e))return or(e,t.tickCount,t.allowDecimals,"adaptive")}}},lB=rc([l_,oF,lR],lL),lK=(e,t,r,n)=>{if("angleAxis"!==n&&(null==e?void 0:e.type)==="number"&&iJ(t)&&Array.isArray(r)&&r.length>0){var a,i;return[Math.min(t[0],null!=(a=r[0])?a:0),Math.max(t[1],null!=(i=r[r.length-1])?i:0)]}return t},lF=rc([oK,l_,lB,od],lK),lW=rc(o6,oK,(e,t)=>{if(t&&"number"===t.type){var r=1/0,n=Array.from(o4(e.map(e=>e.value))).sort((e,t)=>e-t),a=n[0],i=n[n.length-1];if(null==a||null==i)return 1/0;var o=i-a;if(0===o)return 1/0;for(var l=0;la,(e,t,r,n,a)=>{if(!ek(e))return 0;var i="vertical"===t?n.height:n.width;if("gap"===a)return e*i/2;if("no-gap"===a){var o=J(r,e*i),l=e*i/2;return l-o-(l-o)/i*o}return 0}),l$=rc(oT,(e,t,r)=>{var n=oT(e,t);return null==n||"string"!=typeof n.padding?0:lV(e,"xAxis",t,r,n.padding)},(e,t)=>{if(null==e)return{left:0,right:0};var r,n,a=e.padding;return"string"==typeof a?{left:t,right:t}:{left:(null!=(r=a.left)?r:0)+t,right:(null!=(n=a.right)?n:0)+t}}),lU=rc(oR,(e,t,r)=>{var n=oR(e,t);return null==n||"string"!=typeof n.padding?0:lV(e,"yAxis",t,r,n.padding)},(e,t)=>{if(null==e)return{top:0,bottom:0};var r,n,a=e.padding;return"string"==typeof a?{top:t,bottom:t}:{top:(null!=(r=a.top)?r:0)+t,bottom:(null!=(n=a.bottom)?n:0)+t}}),lH=rc([n0,l$,n8,n5,(e,t,r)=>r],(e,t,r,n,a)=>{var i=n.padding;return a?[i.left,r.width-i.right]:[e.left+t.left,e.left+e.width-t.right]}),lG=rc([n0,ax,lU,n8,n5,(e,t,r)=>r],(e,t,r,n,a,i)=>{var o=a.padding;return i?[n.height-o.bottom,o.top]:"horizontal"===t?[e.top+e.height-r.bottom,e.top+r.top]:[e.top+r.top,e.top+e.height-r.bottom]}),lq=(e,t,r,n)=>{var a;switch(t){case"xAxis":return lH(e,r,n);case"yAxis":return lG(e,r,n);case"zAxis":return null==(a=oB(e,r))?void 0:a.range;case"angleAxis":return aB(e);case"radiusAxis":return aK(e,r);default:return}},lX=rc([oK,lq],aS),lY=rc([lR,lF],ox),lZ=rc([oK,lR,lY,lX],oA),lQ=(e,t,r,n)=>{if(null!=r&&null!=r.dataKey){var a=r.type,i=r.scale;if(nM(e,n)&&("number"===a||"auto"!==i))return t.map(e=>e.value)}},lJ=rc([ax,o6,oF,od],lQ),l0=rc([lZ],ob);function l1(e,t){return e.idt.id)}rc([lZ],function(e){if(null!=e)return"invert"in e&&"function"==typeof e.invert?e.invert.bind(e):oP(e,void 0)}),rc([lZ,o7],oP),rc([oG,lf,od],(e,t,r)=>e.flatMap(e=>t[e.id]).filter(Boolean).filter(e=>le(r,e)));var l2=(e,t)=>t,l3=(e,t,r)=>r,l6=rc(nq,l2,l3,(e,t,r)=>e.filter(e=>e.orientation===t).filter(e=>e.mirror===r).sort(l1)),l5=rc(nX,l2,l3,(e,t,r)=>e.filter(e=>e.orientation===t).filter(e=>e.mirror===r).sort(l1)),l8=(e,t)=>({width:e.width,height:t.height}),l4=rc(n0,oT,l8),l9=rc(nU,n0,l6,l2,l3,(e,t,r,n,a)=>{var i,o={};return r.forEach(r=>{var l=l8(t,r);null==i&&(i=((e,t,r)=>{switch(t){case"top":return e.top;case"bottom":return r-e.bottom;default:return 0}})(t,n,e));var c="top"===n&&!a||"bottom"===n&&a;o[r.id]=i-Number(c)*l.height,i+=(c?-1:1)*l.height}),o}),l7=rc(n$,n0,l5,l2,l3,(e,t,r,n,a)=>{var i,o={};return r.forEach(r=>{var l={width:"number"==typeof r.width?r.width:60,height:t.height};null==i&&(i=((e,t,r)=>{switch(t){case"left":return e.left;case"right":return r-e.right;default:return 0}})(t,n,e));var c="left"===n&&!a||"right"===n&&a;o[r.id]=i-Number(c)*l.width,i+=(c?-1:1)*l.width}),o}),ce=rc([n0,oT,(e,t)=>{var r=oT(e,t);if(null!=r)return l9(e,r.orientation,r.mirror)},(e,t)=>t],(e,t,r,n)=>{if(null!=t){var a=null==r?void 0:r[n];return null==a?{x:e.left,y:0}:{x:e.left,y:a}}}),ct=rc([n0,oR,(e,t)=>{var r=oR(e,t);if(null!=r)return l7(e,r.orientation,r.mirror)},(e,t)=>t],(e,t,r,n)=>{if(null!=t){var a=null==r?void 0:r[n];return null==a?{x:0,y:e.top}:{x:a,y:e.top}}}),cr=rc(n0,oR,(e,t)=>({width:"number"==typeof t.width?t.width:60,height:e.height})),cn=(e,t,r)=>{switch(t){case"xAxis":return l4(e,r).width;case"yAxis":return cr(e,r).height;default:return}},ca=(e,t,r,n)=>{if(null!=r){var a=r.allowDuplicatedCategory,i=r.type,o=r.dataKey,l=nM(e,n),c=t.map(e=>e.value),u=c.filter(e=>null!=e);if(o&&l&&"category"===i&&a&&ee(u))return c}},ci=rc([ax,o6,oK,od],ca),co=rc([ax,(e,t,r)=>{switch(t){case"xAxis":return oT(e,r);case"yAxis":return oR(e,r);default:throw Error("Unexpected axis type: ".concat(t))}},lR,l0,ci,lJ,lq,lB,od],(e,t,r,n,a,i,o,l,c)=>{if(null!=t){var u=nM(e,c);return{angle:t.angle,interval:t.interval,minTickGap:t.minTickGap,orientation:t.orientation,tick:t.tick,tickCount:t.tickCount,tickFormatter:t.tickFormatter,ticks:t.ticks,type:t.type,unit:t.unit,axisType:c,categoricalDomain:i,duplicateDomain:a,isCategorical:u,niceTicks:l,range:o,realScaleType:r,scale:n}}}),cl=rc([ax,oF,lR,l0,lB,lq,ci,lJ,od],(e,t,r,n,a,i,o,l,c)=>{if(null!=t&&null!=n){var u=nM(e,c),s=t.type,d=t.ticks,f=t.tickCount,p="scaleBand"===r&&"function"==typeof n.bandwidth?n.bandwidth()/2:2,y="category"===s&&n.bandwidth?n.bandwidth()/p:0;y="angleAxis"===c&&null!=i&&i.length>=2?2*H(i[0]-i[1])*y:y;var v=d||a;return v?v.map((e,t)=>{var r=o?o.indexOf(e):e,a=n.map(r);return ek(a)?{index:t,coordinate:a+y,value:e,offset:y}:null}).filter(ea):u&&l?l.map((e,t)=>{var r=n.map(e);return ek(r)?{coordinate:r+y,value:e,index:t,offset:y}:null}).filter(ea):n.ticks?n.ticks(f).map((e,t)=>{var r=n.map(e);return ek(r)?{coordinate:r+y,value:e,index:t,offset:y}:null}).filter(ea):n.domain().map((e,t)=>{var r=n.map(e);return ek(r)?{coordinate:r+y,value:o?o[e]:e,index:t,offset:y}:null}).filter(ea)}}),cc=rc([ax,oF,l0,lq,ci,lJ,od],(e,t,r,n,a,i,o)=>{if(null!=t&&null!=r&&null!=n&&n[0]!==n[1]){var l=nM(e,o),c=t.tickCount,u=0;return(u="angleAxis"===o&&(null==n?void 0:n.length)>=2?2*H(n[0]-n[1])*u:u,l&&i)?i.map((e,t)=>{var n=r.map(e);return ek(n)?{coordinate:n+u,value:e,index:t,offset:u}:null}).filter(ea):r.ticks?r.ticks(c).map((e,t)=>{var n=r.map(e);return ek(n)?{coordinate:n+u,value:e,index:t,offset:u}:null}).filter(ea):r.domain().map((e,t)=>{var n=r.map(e);return ek(n)?{coordinate:n+u,value:a?a[e]:e,index:t,offset:u}:null}).filter(ea)}}),cu=rc(oK,l0,(e,t)=>{if(null!=e&&null!=t)return ok(ok({},e),{},{scale:t})}),cs=rc([oK,lR,l_,lX],oA),cd=rc([cs],ob);rc((e,t,r)=>oB(e,r),cd,(e,t)=>{if(null!=e&&null!=t)return ok(ok({},e),{},{scale:t})});var cf=rc([ax,nq,nX],(e,t,r)=>{switch(e){case"horizontal":return t.some(e=>e.reversed)?"right-to-left":"left-to-right";case"vertical":return r.some(e=>e.reversed)?"bottom-to-top":"top-to-bottom";case"centric":case"radial":return"left-to-right";default:return}});rc([(e,t,r)=>{var n;return null==(n=e.renderedTicks[t])?void 0:n[r]}],e=>{if(e&&0!==e.length)return t=>{var r,n=1/0,a=e[0];for(var i of e){var o=Math.abs(i.coordinate-t);oe.options.defaultTooltipEventType,cy=e=>e.options.validateTooltipEventTypes;function cv(e,t,r){if(null==e)return t;var n=e?"axis":"item";return null==r?t:r.includes(n)?n:t}function ch(e,t){return cv(t,cp(e),cy(e))}var cm=(e,t)=>{var r,n=Number(t);if(!G(n)&&null!=t)return n>=0?null==e||null==(r=e[n])?void 0:r.value:void 0},cg={active:!1,index:null,dataKey:void 0,graphicalItemId:void 0,coordinate:void 0},cb=rM({name:"tooltip",initialState:{itemInteraction:{click:cg,hover:cg},axisInteraction:{click:cg,hover:cg},keyboardInteraction:cg,syncInteraction:{active:!1,index:null,dataKey:void 0,label:void 0,coordinate:void 0,sourceViewBox:void 0,graphicalItemId:void 0},tooltipItemPayloads:[],settings:{shared:void 0,trigger:"hover",axisId:0,active:!1,defaultIndex:void 0}},reducers:{addTooltipEntrySettings:{reducer(e,t){e.tooltipItemPayloads.push(t.payload)},prepare:rP()},replaceTooltipEntrySettings:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=tJ(e).tooltipItemPayloads.indexOf(n);i>-1&&(e.tooltipItemPayloads[i]=a)},prepare:rP()},removeTooltipEntrySettings:{reducer(e,t){var r=tJ(e).tooltipItemPayloads.indexOf(t.payload);r>-1&&e.tooltipItemPayloads.splice(r,1)},prepare:rP()},setTooltipSettingsState(e,t){e.settings=t.payload},setActiveMouseOverItemIndex(e,t){e.syncInteraction.active=!1,e.syncInteraction.sourceViewBox=void 0,e.keyboardInteraction.active=!1,e.itemInteraction.hover.active=!0,e.itemInteraction.hover.index=t.payload.activeIndex,e.itemInteraction.hover.dataKey=t.payload.activeDataKey,e.itemInteraction.hover.graphicalItemId=t.payload.activeGraphicalItemId,e.itemInteraction.hover.coordinate=t.payload.activeCoordinate},mouseLeaveChart(e){e.itemInteraction.hover.active=!1,e.axisInteraction.hover.active=!1},mouseLeaveItem(e){e.itemInteraction.hover.active=!1},setActiveClickItemIndex(e,t){e.syncInteraction.active=!1,e.syncInteraction.sourceViewBox=void 0,e.itemInteraction.click.active=!0,e.keyboardInteraction.active=!1,e.itemInteraction.click.index=t.payload.activeIndex,e.itemInteraction.click.dataKey=t.payload.activeDataKey,e.itemInteraction.click.graphicalItemId=t.payload.activeGraphicalItemId,e.itemInteraction.click.coordinate=t.payload.activeCoordinate},setMouseOverAxisIndex(e,t){e.syncInteraction.active=!1,e.syncInteraction.sourceViewBox=void 0,e.axisInteraction.hover.active=!0,e.keyboardInteraction.active=!1,e.axisInteraction.hover.index=t.payload.activeIndex,e.axisInteraction.hover.dataKey=t.payload.activeDataKey,e.axisInteraction.hover.coordinate=t.payload.activeCoordinate},setMouseClickAxisIndex(e,t){e.syncInteraction.active=!1,e.syncInteraction.sourceViewBox=void 0,e.keyboardInteraction.active=!1,e.axisInteraction.click.active=!0,e.axisInteraction.click.index=t.payload.activeIndex,e.axisInteraction.click.dataKey=t.payload.activeDataKey,e.axisInteraction.click.coordinate=t.payload.activeCoordinate},setSyncInteraction(e,t){e.syncInteraction=t.payload},setKeyboardInteraction(e,t){e.keyboardInteraction.active=t.payload.active,e.keyboardInteraction.index=t.payload.activeIndex,e.keyboardInteraction.coordinate=t.payload.activeCoordinate}}}),cx=cb.actions,cw=cx.addTooltipEntrySettings,cO=cx.replaceTooltipEntrySettings,cj=cx.removeTooltipEntrySettings,cA=cx.setTooltipSettingsState,cE=cx.setActiveMouseOverItemIndex,cP=cx.mouseLeaveItem,cS=cx.mouseLeaveChart,ck=cx.setActiveClickItemIndex,cI=cx.setMouseOverAxisIndex,cC=cx.setMouseClickAxisIndex,cD=cx.setSyncInteraction,cM=cx.setKeyboardInteraction,cN=cb.reducer;function cT(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function cz(e){for(var t=1;t{if(null==t)return cg;var a,i,o,l=(a=e,i=t,o=r,"axis"===i?"click"===o?a.axisInteraction.click:a.axisInteraction.hover:"click"===o?a.itemInteraction.click:a.itemInteraction.hover);if(null==l)return cg;if(l.active)return l;if(e.keyboardInteraction.active)return e.keyboardInteraction;if(e.syncInteraction.active&&null!=e.syncInteraction.index)return e.syncInteraction;var c=!0===e.settings.active;if(null!=l.index){if(c)return cz(cz({},l),{},{active:!0})}else if(null!=n)return{active:!0,coordinate:void 0,dataKey:void 0,index:n,graphicalItemId:void 0};return cz(cz({},cg),{},{coordinate:l.coordinate})},cR=(e,t,r,n)=>{var a=null==e?void 0:e.index;if(null==a)return null;var i=Number(a);if(!ek(i))return a;var o=Infinity;t.length>0&&(o=t.length-1);var l=Math.max(0,Math.min(i,o)),c=t[l];return null==c?String(l):!function(e,t,r){if(null==r||null==t)return!0;var n=nD(e,t);return!(null!=n&&iJ(r))||function(e,t){var r=function(e){if("number"==typeof e)return Number.isFinite(e)?e:void 0;if(e instanceof Date){var t=e.valueOf();return Number.isFinite(t)?t:void 0}var r=Number(e);return Number.isFinite(r)?r:void 0}(e),n=t[0],a=t[1];if(void 0===r)return!1;var i=Math.min(n,a),o=Math.max(n,a);return r>=i&&r<=o}(n,r)}(c,r,n)?null:String(l)},cL=(e,t,r,n,a,i,o)=>{if(null!=i){var l=o[0],c=null==l?void 0:l.getPosition(i);if(null!=c)return c;var u=null==a?void 0:a[Number(i)];if(u)if("horizontal"===r)return{x:u.coordinate,y:(n.top+t)/2};else return{x:(n.left+e)/2,y:u.coordinate}}},cB=(e,t,r,n)=>{if("axis"===t)return e.tooltipItemPayloads;if(0===e.tooltipItemPayloads.length)return[];if(a="hover"===r?e.itemInteraction.hover.graphicalItemId:e.itemInteraction.click.graphicalItemId,e.syncInteraction.active&&null==a)return e.tooltipItemPayloads;if(null==a&&(null!=n||e.keyboardInteraction.active)){var a,i=e.tooltipItemPayloads[0];return null!=i?[i]:[]}return e.tooltipItemPayloads.filter(e=>{var t;return(null==(t=e.settings)?void 0:t.graphicalItemId)===a})},cK=e=>e.options.tooltipPayloadSearcher,cF=e=>e.tooltip;function cW(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function cV(e){for(var t=1;t{if(null!=t&&null!=i){var l=r.chartData,c=r.computedData,u=r.dataStartIndex,s=r.dataEndIndex;return e.reduce((e,r)=>{var d,f,p,y=r.dataDefinedOnItem,v=r.settings,h=null!=y?y:l,m=Array.isArray(h)?nk(h,u,s):h,g=null!=(d=null==v?void 0:v.dataKey)?d:n,b=null==v?void 0:v.nameKey;return Array.isArray(f=n&&Array.isArray(m)&&!Array.isArray(m[0])&&"axis"===o?er(m,n,a):i(m,t,c,b))?f.forEach(t=>{var r,n,a=function(e){if(null!=e&&"object"==typeof e){var t,r="name"in e?function(e){if("string"==typeof e||"number"==typeof e)return e}(e.name):void 0,n="unit"in e?function(e){if("string"==typeof e||"number"==typeof e||"boolean"==typeof e)return e}(e.unit):void 0,a="dataKey"in e?"string"==typeof(t=e.dataKey)||"number"==typeof t?t:"function"==typeof t?e=>t(e):void 0:void 0,i="payload"in e?e.payload:void 0;return{name:r,unit:n,dataKey:a,payload:i,color:"color"in e?c$(e.color):void 0,fill:"fill"in e?c$(e.fill):void 0}}}(t),i=null==a?void 0:a.name,o=null==a?void 0:a.dataKey,l=null==a?void 0:a.payload,c=cV(cV({},v),{},{name:i,unit:null==a?void 0:a.unit,color:null!=(r=null==a?void 0:a.color)?r:null==v?void 0:v.color,fill:null!=(n=null==a?void 0:a.fill)?n:null==v?void 0:v.fill});e.push(nW({tooltipEntrySettings:c,dataKey:o,payload:l,value:nD(l,o),name:null==i?void 0:String(i)}))}):e.push(nW({tooltipEntrySettings:v,dataKey:g,payload:f,value:nD(f,g),name:null!=(p=nD(f,b))?p:null==v?void 0:v.name})),e},[])}},cH=rc([lt,oW,ol],oE),cG=rc([e=>e.graphicalItems.cartesianItems,e=>e.graphicalItems.polarItems],(e,t)=>[...e,...t]),cq=rc([om,og],oV),cX=rc([cG,lt,cq],oH,{memoizeOptions:{resultEqualityCheck:aV}}),cY=rc([cX],e=>e.filter(ov)),cZ=rc([cX],oZ,{memoizeOptions:{resultEqualityCheck:aV}}),cQ=rc([cX],e=>e.some(e=>!e.data)),cJ=rc([cZ,i$],o0),c0=rc([cY,i$,lt],oy),c1=rc([cJ,lt,cX,i$,cQ,cZ],o3),c2=rc([lt],lc),c3=rc([lt],e=>e.allowDataOverflow),c6=rc([c2,c3],i1),c5=rc([cX],e=>e.filter(ov)),c8=rc([c0,c5,oi,oo],la),c4=rc([c8,i$,om,c6],lo),c9=rc([cX],oX),c7=rc([cJ,lt,c9,lf,om,iY],ly,{memoizeOptions:{resultEqualityCheck:oh}}),ue=rc([lm,om,og],lg),ut=rc([ue,om],lA),ur=rc([lx,om,og],lg),un=rc([ur,om],lP),ua=rc([lO,om,og],lg),ui=rc([ua,om],lk),uo=rc([ut,ui,un],lp),ul=rc([lt,c2,c6,c4,c7,uo,ax,om],lD),uc=rc([lt,ax,cJ,c1,oi,om,ul],lz),uu=rc([uc,lt,cH],lL),us=rc([lt,uc,uu,om],lK),ud=e=>{var t=om(e),r=og(e);return lq(e,t,r,!1)},uf=rc([lt,ud],aS),up=rc([lt,cH,us,uf],oA),uy=rc([up],ob),uv=rc([ax,c1,lt,om],ca),uh=rc([ax,c1,lt,om],lQ),um=rc([ax,lt,cH,uy,ud,uv,uh,om],(e,t,r,n,a,i,o,l)=>{if(t){var c=t.type,u=nM(e,l);if(n){var s="scaleBand"===r&&n.bandwidth?n.bandwidth()/2:2,d="category"===c&&n.bandwidth?n.bandwidth()/s:0;return(d="angleAxis"===l&&null!=a&&(null==a?void 0:a.length)>=2?2*H(a[0]-a[1])*d:d,u&&o)?o.map((e,t)=>{var r=n.map(e);return ek(r)?{coordinate:r+d,value:e,index:t,offset:d}:null}).filter(ea):n.domain().map((e,t)=>{var r=n.map(e);return ek(r)?{coordinate:r+d,value:i?i[e]:e,index:t,offset:d}:null}).filter(ea)}}}),ug=rc([cp,cy,e=>e.tooltip.settings],(e,t,r)=>cv(r.shared,e,t)),ub=e=>e.tooltip.settings.trigger,ux=e=>e.tooltip.settings.defaultIndex,uw=rc([cF,ug,ub,ux],c_),uO=rc([uw,cJ,lr,uc],cR),uj=rc([um,uO],cm),uA=rc([uw],e=>{if(e)return e.dataKey}),uE=rc([uw],e=>{if(e)return e.graphicalItemId}),uP=rc([cF,ug,ub,ux],cB),uS=rc([n$,nU,ax,n0,um,ux,uP],cL),uk=rc([uw,uS],(e,t)=>null!=e&&e.coordinate?e.coordinate:t),uI=rc([uw],e=>{var t;return null!=(t=null==e?void 0:e.active)&&t}),uC=rc([uP,uO,i$,lr,uj,cK,ug],cU),uD=rc([uC],e=>{if(null!=e)return Array.from(new Set(e.map(e=>e.payload).filter(e=>null!=e)))});function uM(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function uN(e){for(var t=1;t=Math.abs(a-(null!=(o=l[0])?o:0)))return;var c=[...l,a].slice(-3);e.yAxis[n]=uN(uN({},i),{},{width:a,widthHistory:c})}}}}),uz=uT.actions,u_=uz.addXAxis,uR=uz.replaceXAxis,uL=uz.removeXAxis,uB=uz.addYAxis,uK=uz.replaceYAxis,uF=uz.removeYAxis,uW=(uz.addZAxis,uz.replaceZAxis,uz.removeZAxis,uz.updateYAxisWidth),uV=uT.reducer,u$=rc([n0],e=>({top:e.top,bottom:e.bottom,left:e.left,right:e.right})),uU=rc([u$,n$,nU],(e,t,r)=>{if(e&&null!=t&&null!=r)return{x:e.left,y:e.top,width:Math.max(0,t-e.left-e.right),height:Math.max(0,r-e.top-e.bottom)}});function uH(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function uG(e){for(var t=1;t{var t,r=e.point,n=e.childIndex,a=e.mainColor,i=e.activeDot,o=e.dataKey,l=e.clipPath;if(!1===i||null==r.x||null==r.y)return null;var c=uG(uG(uG({},{index:n,dataKey:o,cx:r.x,cy:r.y,r:4,fill:null!=a?a:"none",strokeWidth:2,stroke:"#fff",payload:r.payload,value:r.value}),T(i)),iE(i));return t=(0,E.isValidElement)(i)?(0,E.cloneElement)(i,c):"function"==typeof i?i(c):E.createElement(ik,c),E.createElement(L,{className:"recharts-active-dot",clipPath:l},t)};function uX(e){var t=e.points,r=e.mainColor,n=e.activeDot,a=e.itemDataKey,i=e.clipPath,o=e.zIndex,l=void 0===o?aA.activeDot:o,c=e6(uO),u=e6(uD);if(null==t||null==u)return null;var s=t.find(e=>u.includes(e.payload));return null==s?null:E.createElement(a3,{zIndex:l},E.createElement(uq,{point:s,childIndex:Number(c),mainColor:r,dataKey:a,activeDot:n,clipPath:i}))}function uY(e){var t=e.tooltipEntrySettings,r=e0(),n=n6(),a=(0,E.useRef)(null);return(0,E.useLayoutEffect)(()=>{n||(null===a.current?r(cw(t)):a.current!==t&&r(cO({prev:a.current,next:t})),a.current=t)},[t,r,n]),(0,E.useLayoutEffect)(()=>()=>{a.current&&(r(cj(a.current)),a.current=null)},[r]),null}function uZ(e,t){var r,n,a=e6(t=>oT(t,e)),i=e6(e=>oR(e,t)),o=null!=(r=null==a?void 0:a.allowDataOverflow)?r:oM.allowDataOverflow,l=null!=(n=null==i?void 0:i.allowDataOverflow)?n:oz.allowDataOverflow;return{needClip:o||l,needClipX:o,needClipY:l}}function uQ(e){var t=e.xAxisId,r=e.yAxisId,n=e.clipPathId,a=e6(uU),i=uZ(t,r),o=i.needClipX,l=i.needClipY,c=i.needClip,u=e6(e=>lH(e,t,!1)),s=e6(e=>lG(e,r,!1));if(!c||!a)return null;var d=a.x,f=a.y,p=a.width,y=a.height,v=o&&u?Math.min(u[0],u[1]):d-p/2,h=l&&s?Math.min(s[0],s[1]):f-y/2,m=o&&u?Math.abs(u[1]-u[0]):2*p,g=l&&s?Math.abs(s[1]-s[0]):2*y;return E.createElement("clipPath",{id:"clipPath-".concat(n)},E.createElement("rect",{x:v,y:h,width:m,height:g}))}function uJ(e,t){var r,n;return null!=(r=null==(n=e.graphicalItems.cartesianItems.find(e=>e.id===t))?void 0:n.xAxisId)?r:0}function u0(e,t){var r,n;return null!=(r=null==(n=e.graphicalItems.cartesianItems.find(e=>e.id===t))?void 0:n.yAxisId)?r:0}var u1=(e,t,r)=>cu(e,"xAxis",uJ(e,t),r),u2=(e,t,r)=>cc(e,"xAxis",uJ(e,t),r),u3=(e,t,r)=>cu(e,"yAxis",u0(e,t),r),u6=(e,t,r)=>cc(e,"yAxis",u0(e,t),r),u5=rc([ax,u1,u3,u2,u6],(e,t,r,n,a)=>nM(e,"xAxis")?nF(t,n,!1):nF(r,a,!1)),u8=rc([o$,(e,t)=>t],(e,t)=>e.filter(e=>"area"===e.type).find(e=>e.id===t)),u4=e=>nM(ax(e),"xAxis")?"yAxis":"xAxis",u9=rc([u8,(e,t,r)=>li(e,u4(e),"yAxis"===u4(e)?u0(e,t):uJ(e,t),r)],(e,t)=>{if(null!=e&&null!=t){var r,n=e.stackId,a=op(e);if(null!=n&&null!=a){var i=null==(r=t[n])?void 0:r.stackedData,o=null==i?void 0:i.find(e=>e.key===a);if(null!=o)return o.map(e=>[e[0],e[1]])}}}),u7=rc([ax,u1,u3,u2,u6,u9,iG,u5,u8,e=>e.rootProps.baseValue],(e,t,r,n,a,i,o,l,c,u)=>{var s,d=o.chartData,f=o.dataStartIndex,p=o.dataEndIndex;if(null!=c&&("horizontal"===e||"vertical"===e)&&null!=t&&null!=r&&null!=n&&null!=a&&0!==n.length&&0!==a.length&&null!=l){var y,v,h,m,g,b,x,w,O,j,A,E,P,S,k,I,C,D,M,N,T,z=c.data;if(null!=(s=z&&z.length>0?z:null==d?void 0:d.slice(f,p+1))){return m=(h=(y={layout:e,xAxis:t,yAxis:r,xAxisTicks:n,yAxisTicks:a,dataStartIndex:f,areaSettings:c,stackedData:i,displayedData:s,chartBaseValue:u,bandSize:l}).areaSettings).connectNulls,g=h.baseValue,b=h.dataKey,x=y.stackedData,w=y.layout,O=y.chartBaseValue,j=y.xAxis,A=y.yAxis,E=y.displayedData,P=y.dataStartIndex,S=y.xAxisTicks,k=y.yAxisTicks,I=y.bandSize,C=x&&x.length,D=((e,t,r,n,a)=>{var i=null!=r?r:t;if(X(i))return i;var o="horizontal"===e?a:n,l=o.scale.domain();if("number"===o.type){var c=Math.max(l[0],l[1]),u=Math.min(l[0],l[1]);return"dataMin"===i?u:"dataMax"===i||c<0?c:Math.max(Math.min(l[0],l[1]),0)}return"dataMin"===i?l[0]:"dataMax"===i?l[1]:l[0]})(w,O,g,j,A),M="horizontal"===w,N=!1,T=E.map((e,t)=>{if(C)i=x[P+t];else{var r,n,a,i,o,l=nD(e,b);Array.isArray(l)?(i=l,N=!0):i=[D,l]}var c=null!=(r=null==(n=i)?void 0:n[1])?r:null,u=null==c||C&&!m&&null==nD(e,b);return M?{x:nR({axis:j,ticks:S,bandSize:I,entry:e,index:t}),y:u?null:null!=(o=A.scale.map(c))?o:null,value:i,payload:e}:{x:u?null:null!=(a=j.scale.map(c))?a:null,y:nR({axis:A,ticks:k,bandSize:I,entry:e,index:t}),value:i,payload:e}}),v=C||N?T.map(e=>{var t,r,n=Array.isArray(e.value)?e.value[0]:null;return M?{x:e.x,y:null!=n&&null!=e.y&&null!=(r=A.scale.map(n))?r:null,payload:e.payload}:{x:null!=n&&null!=(t=j.scale.map(n))?t:null,y:e.y,payload:e.payload}}):M?A.scale.map(D):j.scale.map(D),{points:T,baseLine:null!=v?v:0,isRange:N}}}});function se(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function st(e){for(var t=1;t{var i=null!=(d=null==t?void 0:t.length)?d:0;if(i<=1||null==e)return 0;if("angleAxis"===n&&null!=a&&1e-6>=Math.abs(Math.abs(a[1]-a[0])-360))for(var o=0;o0?null==(f=r[o-1])?void 0:f.coordinate:null==(p=r[i-1])?void 0:p.coordinate,c=null==(y=r[o])?void 0:y.coordinate,u=o>=i-1?null==(v=r[0])?void 0:v.coordinate:null==(h=r[o+1])?void 0:h.coordinate,s=void 0;if(null!=l&&null!=c&&null!=u)if(H(c-l)!==H(u-c)){var d,f,p,y,v,h,m,g=[];if(H(u-c)===H(a[1]-a[0])){s=u;var b=c+a[1]-a[0];g[0]=Math.min(b,(b+l)/2),g[1]=Math.max(b,(b+l)/2)}else{s=l;var x=u+a[1]-a[0];g[0]=Math.min(c,(x+c)/2),g[1]=Math.max(c,(x+c)/2)}var w=[Math.min(c,(s+c)/2),Math.max(c,(s+c)/2)];if(e>w[0]&&e<=w[1]||e>=g[0]&&e<=g[1])return null==(m=r[o])?void 0:m.index}else{var O,j=Math.min(l,u),A=Math.max(l,u);if(e>(j+c)/2&&e<=(A+c)/2)return null==(O=r[o])?void 0:O.index}}else if(t)for(var E=0;E(P.coordinate+k.coordinate)/2||E>0&&E(P.coordinate+k.coordinate)/2&&e<=(P.coordinate+S.coordinate)/2)return P.index}}return -1},sn=(e,t)=>t,sa=(e,t,r)=>r,si=(e,t,r,n)=>n,so=rc(um,e=>nb(e,e=>e.coordinate)),sl=rc([cF,sn,sa,si],c_),sc=rc([sl,cJ,lr,uc],cR),su=rc([cF,sn,sa,si],cB),ss=rc([n$,nU,ax,n0,um,si,su],cL),sd=rc([sl,ss],(e,t)=>{var r;return null!=(r=e.coordinate)?r:t}),sf=rc([um,sc],cm),sp=rc([su,sc,i$,lr,sf,cK,sn],cU),sy=rc([sl,sc],(e,t)=>({isActive:e.active&&null!=t,activeIndex:t})),sv=rM({name:"legend",initialState:{settings:{layout:"horizontal",align:"center",verticalAlign:"bottom",itemSorter:"value"},size:{width:0,height:0},payload:[]},reducers:{setLegendSize(e,t){e.size.width=t.payload.width,e.size.height=t.payload.height},setLegendSettings(e,t){e.settings.align=t.payload.align,e.settings.layout=t.payload.layout,e.settings.verticalAlign=t.payload.verticalAlign,e.settings.itemSorter=t.payload.itemSorter},addLegendPayload:{reducer(e,t){e.payload.push(t.payload)},prepare:rP()},replaceLegendPayload:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=tJ(e).payload.indexOf(n);i>-1&&(e.payload[i]=a)},prepare:rP()},removeLegendPayload:{reducer(e,t){var r=tJ(e).payload.indexOf(t.payload);r>-1&&e.payload.splice(r,1)},prepare:rP()}}}),sh=sv.actions,sm=sh.setLegendSize,sg=sh.setLegendSettings,sb=sh.addLegendPayload,sx=sh.replaceLegendPayload,sw=sh.removeLegendPayload,sO=sv.reducer;function sj(e){var t=e.legendPayload,r=e0(),n=n6(),a=(0,E.useRef)(null);return(0,E.useLayoutEffect)(()=>{n||(null===a.current?r(sb(t)):a.current!==t&&r(sx({prev:a.current,next:t})),a.current=t)},[r,n,t]),(0,E.useLayoutEffect)(()=>()=>{a.current&&(r(sw(a.current)),a.current=null)},[r]),null}function sA(e){var t=e.legendPayload,r=e0(),n=e6(ax),a=(0,E.useRef)(null);return(0,E.useLayoutEffect)(()=>{("centric"===n||"radial"===n)&&(null===a.current?r(sb(t)):a.current!==t&&r(sx({prev:a.current,next:t})),a.current=t)},[r,n,t]),(0,E.useLayoutEffect)(()=>()=>{a.current&&(r(sw(a.current)),a.current=null)},[r]),null}var sE=(e,t)=>[0,3*e,3*t-6*e,3*e-3*t+1],sP=(e,t)=>e.map((e,r)=>e*t**r).reduce((e,t)=>e+t),sS=(e,t)=>r=>sP(sE(e,t),r),sk=function(){for(var e=arguments.length,t=Array(e),r=0;r{var t,r=e.split("(");if(2!==r.length||"cubic-bezier"!==r[0])return null;var n=null==(t=r[1])||null==(t=t.split(")")[0])?void 0:t.split(",");if(null==n||4!==n.length)return null;var a=n.map(e=>parseFloat(e));return[a[0],a[1],a[2],a[3]]})(t[0]);if(n)return n}return 4===t.length?t:[0,0,1,1]},sI=function(){return((e,t,r,n)=>{var a=sS(e,r),i=sS(t,n),o=t=>sP([...sE(e,r).map((e,t)=>e*t).slice(1),0],t),l=e=>e>1?1:e<0?0:e,c=e=>{for(var t=e>1?1:e,r=t,n=0;n<8;++n){var c=a(r)-t,u=o(r);if(1e-4>Math.abs(c-t)||u<1e-4)break;r=l(r-c/u)}return i(r)};return c.isStepper=!1,c})(...sk(...arguments))},sC=function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.stiff,r=void 0===t?100:t,n=e.damping,a=void 0===n?8:n,i=e.dt,o=void 0===i?16.67:i,l=[0],c=0,u=0,s=0;s<1e4;){var d=u*a;if(u+=(-(c-1)*r-d)*o/1e3,c+=u*o/1e3,l.push(c),1e-4>Math.abs(c-1)&&1e-4>Math.abs(u))break;s++}l[l.length-1]=1;var f=l.length-1;return e=>{if(e<=0)return 0;if(e>=1)return 1;var t,r,n,a=e*f,i=Math.floor(a);return(null!=(t=l[i])?t:0)+((null!=(r=l[i+1])?r:0)-(null!=(n=l[i])?n:0))*(a-i)}},sD=(0,E.createContext)((e,t,r)=>{var n,a=i=>{var o=t.tick(i);if("active"===t.getState()){if(r(t.getInterpolated()),1===t.getProgress()){t.complete(),n=void 0;return}n=e.setTimeout(a,o);return}n=e.setTimeout(a,o)};return n=e.setTimeout(a,0),()=>{var e;return null==(e=n)?void 0:e()}});function sM(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r!eo.isSsr&&!!window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(e)||function(e){if(e){if("string"==typeof e)return sM(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?sM(e,2):void 0}}(e)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),r=t[0],n=t[1];return(0,E.useEffect)(()=>{if(window.matchMedia){var e=window.matchMedia("(prefers-reduced-motion: reduce)"),t=()=>{n(e.matches)};return e.addEventListener("change",t),()=>{e.removeEventListener("change",t)}}},[]),r}sD.Provider;var sT="init",sz="pending",s_="active";function sR(e){return Math.max(0,e)}class sL{getAnimationStartedTime(){return this.animationStartedTime}getBeginStartedTime(){return this.beginStartedTime}constructor(e){var t;!function(e,t,r){var n;(t="symbol"==typeof(n=function(e,t){if("object"!=typeof e||!e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!=typeof n)return n;throw TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(t,"string"))?n:n+"")in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r}(this,"state",sT),this.animationId=e.animationId,this.onAnimationEnd=e.onAnimationEnd,this.animationDuration=sR(e.animationDuration),this.animationBegin=sR(e.animationBegin),this.progress=0,this.from=e.from,this.to=e.to,this.easing=e.easing,null==(t=e.onAnimationStart)||t.call(e)}getState(){return this.state}getEasing(){return this.easing}getAnimationDuration(){return this.animationDuration}tick(e){if(this.getState()===sT)return this.state=sz,this.beginStartedTime=e,this.animationBegin;if(this.getState()===sz){if(null==this.beginStartedTime)throw Error();var t=e-this.beginStartedTime;return t>=this.animationBegin?(this.state=s_,this.animationStartedTime=e,this.nextAnimationUpdate(0)):sR(this.animationBegin-t)}if(this.getState()===s_){if(null==this.animationStartedTime)throw Error();var r=e-this.animationStartedTime;return this.setProgress(r/this.animationDuration),this.nextAnimationUpdate(r)}return 0}setProgress(e){this.progress=Math.min(1,Math.max(0,e))}getProgress(){return this.progress}complete(){if(this.progress=1,"active"===this.state){var e;null==(e=this.onAnimationEnd)||e.call(this)}this.state="completed"}getFrom(){return this.from}getTo(){return this.to}getAnimationId(){return this.animationId}getAnimationBegin(){return this.animationBegin}}class sB extends sL{nextAnimationUpdate(){return 0}getInterpolated(){return this.easing(et(this.getFrom(),this.getTo(),this.getProgress()))}}class sK{setTimeout(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=performance.now(),n=null,a=i=>{i-r>=t?e(i):n=requestAnimationFrame(a)};return n=requestAnimationFrame(a),()=>{null!=n&&cancelAnimationFrame(n)}}}function sF(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{},onAnimationStart:()=>{}};function sV(e){var t,r,n,a=eS(e,sW),i=a.animationId,o=a.isActive,l=a.canBegin,c=a.duration,u=a.easing,s=a.begin,d=a.onAnimationEnd,f=a.onAnimationStart,p=a.children,y=sN(),v="auto"===o?!eo.isSsr&&!y:o,h=(t=a.animationController,r=(0,E.useContext)(sD),(0,E.useMemo)(()=>null!=t?t:r,[t,r])),m=function(e){if(Array.isArray(e))return e}(n=(0,E.useState)(+!v))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(n)||function(e){if(e){if("string"==typeof e)return sF(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?sF(e,2):void 0}}(n)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),g=m[0],b=m[1];return(0,E.useEffect)(()=>{v||b(1)},[v]),(0,E.useEffect)(()=>{var e=(e=>{if("string"==typeof e)switch(e){case"ease":case"ease-in-out":case"ease-out":case"ease-in":case"linear":return sI(e);case"spring":return sC();default:if("cubic-bezier"===e.split("(")[0])return sI(e)}return"function"==typeof e?e:null})(u);return v&&l&&null!=e?h(new sK,new sB({animationId:i,easing:e,animationDuration:c,animationBegin:s,onAnimationStart:f,onAnimationEnd:d,from:0,to:1}),b):ei},[h,i,v,l,c,u,s,f,d]),p(Number(g))}function s$(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"animation-",r=(0,E.useRef)(Q(t)),n=(0,E.useRef)(e);return n.current!==e&&(r.current=Q(t),n.current=e),r.current}function sU(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r2&&void 0!==arguments[2]?arguments[2]:[],n=[];for(var a of r)n.push({status:"removed",prev:a});for(var i=0;i({status:"added",next:e})):r===sH?(n=e.length/t.length,sq(t.map((t,r)=>e[Math.floor(r*n)]),t)):r===sG?sq(t.map((t,r)=>e[r]),t):function(e,t,r){var n=function(e,t){for(var r=new Map,n=0;n{var i=r(e,t);if(null!=i){var o=n.get(i);if(void 0!==o)return a.add(i),o}}),o=[];for(var l of n){var c=function(e){if(Array.isArray(e))return e}(l)||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(l)||function(e){if(e){if("string"==typeof e)return sU(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?sU(e,2):void 0}}(l)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),u=c[0],s=c[1];a.has(u)||o.push(s)}return sq(i,t,o)}(e,t,r)}function sY(e,t){var r=(0,E.useRef)(e),n=(0,E.useRef)(t.current),a=(0,E.useRef)(!0);r.current!==e&&(r.current=e,n.current=t.current,a.current=!1);var i=(0,E.useCallback)(function(e,r){var i=!(arguments.length>2)||void 0===arguments[2]||arguments[2];if(0===r){a.current=!0;return}1===r&&(n.current=e),r>0&&a.current&&i&&(t.current=e)},[t]);return{startValue:n.current,syncStepValue:i}}function sZ(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);rtypeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(r)||function(e){if(e){if("string"==typeof e)return sZ(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?sZ(e,2):void 0}}(r)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),a=n[0],i=n[1];return{isAnimating:a,handleAnimationStart:(0,E.useCallback)(()=>{"function"==typeof e&&e(),i(!0)},[e]),handleAnimationEnd:(0,E.useCallback)(()=>{"function"==typeof t&&t(),i(!1)},[t])}}function sJ(e){var t,r=e.animationInput,n=e.animationIdPrefix,a=e.items,i=e.previousItemsRef,o=e.isAnimationActive,l=e.animationBegin,c=e.animationDuration,u=e.animationEasing,s=e.onAnimationStart,d=e.onAnimationEnd,f=e.animationInterpolateFn,p=e.animationMatchBy,y=e.shouldUpdatePreviousRef,v=e.children,h=e.layout,m=s$(r,n),g=sY(m,i),b=null!=(t=g.startValue)?t:null,x=sX(b,a,null!=p?p:sH);return E.createElement(sV,{animationId:m,begin:l,duration:c,isActive:o,easing:u,onAnimationEnd:d,onAnimationStart:s,key:m},e=>{var t=null==a?a:f(x,e,h),r=y?y(e):e>0;return(g.syncStepValue(t,e,r),null==t)?null:v(t,e,null==b)})}function s0(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var e;return(function(e){if(Array.isArray(e))return e}(e=E.useState(()=>Q("uid-")))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),1!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(e)||function(e){if(e){if("string"==typeof e)return s0(e,1);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?s0(e,1):void 0}}(e)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}())[0]},s2=(0,E.createContext)(void 0),s3=e=>{var t,r,n,a=e.id,i=e.type,o=e.children,l=(t="recharts-".concat(i),r=a,n=s1(),r||(t?"".concat(t,"-").concat(n):n));return E.createElement(s2.Provider,{value:l},o(l))},s6=rM({name:"graphicalItems",initialState:{cartesianItems:[],polarItems:[]},reducers:{addCartesianGraphicalItem:{reducer(e,t){e.cartesianItems.push(t.payload)},prepare:rP()},replaceCartesianGraphicalItem:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=tJ(e).cartesianItems.indexOf(n);i>-1&&(e.cartesianItems[i]=a)},prepare:rP()},removeCartesianGraphicalItem:{reducer(e,t){var r=tJ(e).cartesianItems.indexOf(t.payload);r>-1&&e.cartesianItems.splice(r,1)},prepare:rP()},addPolarGraphicalItem:{reducer(e,t){e.polarItems.push(t.payload)},prepare:rP()},removePolarGraphicalItem:{reducer(e,t){var r=tJ(e).polarItems.indexOf(t.payload);r>-1&&e.polarItems.splice(r,1)},prepare:rP()},replacePolarGraphicalItem:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=tJ(e).polarItems.indexOf(n);i>-1&&(e.polarItems[i]=a)},prepare:rP()}}}),s5=s6.actions,s8=s5.addCartesianGraphicalItem,s4=s5.replaceCartesianGraphicalItem,s9=s5.removeCartesianGraphicalItem,s7=s5.addPolarGraphicalItem,de=s5.removePolarGraphicalItem,dt=s5.replacePolarGraphicalItem,dr=s6.reducer,dn=(0,E.memo)(e=>{var t=e0(),r=(0,E.useRef)(null);return(0,E.useLayoutEffect)(()=>{null===r.current?t(s8(e)):r.current!==e&&t(s4({prev:r.current,next:e})),r.current=e},[t,e]),(0,E.useLayoutEffect)(()=>()=>{r.current&&(t(s9(r.current)),r.current=null)},[t]),null}),da=(0,E.memo)(e=>{var t=e0(),r=(0,E.useRef)(null);return(0,E.useLayoutEffect)(()=>{null===r.current?t(s7(e)):r.current!==e&&t(dt({prev:r.current,next:e})),r.current=e},[t,e]),(0,E.useLayoutEffect)(()=>()=>{r.current&&(t(de(r.current)),r.current=null)},[t]),null});function di(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function dl(e){for(var t=1;t[]},ds="u">typeof window&&void 0!==window.document&&void 0!==window.document.createElement,dd="u">typeof navigator&&"ReactNative"===navigator.product,df=ds||dd?E.useLayoutEffect:E.useEffect;function dp(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}var dy=Symbol.for("react-redux-context"),dv="u">typeof globalThis?globalThis:{},dh=function(){if(!E.createContext)return{};let e=dv[dy]??=new Map,t=e.get(E.createContext);return t||(t=E.createContext(null),e.set(E.createContext,t)),t}(),dm=function(e){let{children:t,context:r,serverState:n,store:a}=e,i=E.useMemo(()=>{let e=function(e){let t,r=du,n=0,a=!1;function i(){c.onStateChange&&c.onStateChange()}function o(){if(n++,!t){let n,a;t=e.subscribe(i),n=null,a=null,r={clear(){n=null,a=null},notify(){let e=n;for(;e;)e.callback(),e=e.next},get(){let e=[],t=n;for(;t;)e.push(t),t=t.next;return e},subscribe(e){let t=!0,r=a={callback:e,next:null,prev:a};return r.prev?r.prev.next=r:n=r,function(){t&&null!==n&&(t=!1,r.next?r.next.prev=r.prev:a=r.prev,r.prev?r.prev.next=r.next:n=r.next)}}}}}function l(){n--,t&&0===n&&(t(),t=void 0,r.clear(),r=du)}let c={addNestedSub:function(e){o();let t=r.subscribe(e),n=!1;return()=>{n||(n=!0,t(),l())}},notifyNestedSubs:function(){r.notify()},handleChangeWrapper:i,isSubscribed:function(){return a},trySubscribe:function(){a||(a=!0,o())},tryUnsubscribe:function(){a&&(a=!1,l())},getListeners:()=>r};return c}(a);return{store:a,subscription:e,getServerState:n?()=>n:void 0}},[a,n]),o=E.useMemo(()=>a.getState(),[a]);return df(()=>{let{subscription:e}=i;return e.onStateChange=e.notifyNestedSubs,e.trySubscribe(),o!==a.getState()&&e.notifyNestedSubs(),()=>{e.tryUnsubscribe(),e.onStateChange=void 0}},[i,o]),E.createElement((r||dh).Provider,{value:i},t)};function dg(e=dh){return function(){return E.useContext(e)}}var db=dg(),dx=new Set(["axisLine","tickLine","activeBar","activeDot","activeLabel","activeShape","allowEscapeViewBox","background","cursor","dot","label","line","margin","padding","position","shape","style","tick","wrapperStyle","radius","throttledEvents"]);function dw(e,t){for(var r of new Set([...Object.keys(e),...Object.keys(t)]))if(dx.has(r)){if(null==e[r]&&null==t[r])continue;if(!function(e,t){if(dp(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;let r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(let n=0;nek(e.x)&&ek(e.y),dW=e=>null!=e.base&&dF(e.base)&&dF(e),dV=e=>e.x,d$=e=>e.y,dU=e=>{var t=e.className,r=e.points,n=e.path,a=e.pathRef,i=e6(ax);if((!r||!r.length)&&!n)return null;var o={type:e.type,points:e.points,baseLine:e.baseLine,layout:e.layout||i,connectNulls:e.connectNulls},l=r&&r.length?(e=>{var t=e.type,r=e.points,n=void 0===r?[]:r,a=e.baseLine,i=e.layout,o=e.connectNulls,l=void 0!==o&&o,c=((e,t)=>{if("function"==typeof e)return e;var r="curve".concat(en(e));if(("curveMonotone"===r||"curveBump"===r)&&t){var n=dK["".concat(r).concat("vertical"===t?"Y":"X")];if(n)return n}return dK[r]||dk.curveLinear})(void 0===t?"linear":t,i),u=l?n.filter(dF):n;if(Array.isArray(a)){var s=n.map((e,t)=>dB(dB({},e),{},{base:a[t]}));return("vertical"===i?(0,dO.area)().y(d$).x1(dV).x0(e=>e.base.x):(0,dO.area)().x(dV).y1(d$).y0(e=>e.base.y)).defined(dW).curve(c)(l?s.filter(dW):s)}return("vertical"===i&&X(a)?(0,dO.area)().y(d$).x1(dV).x0(a):X(a)?(0,dO.area)().x(dV).y1(d$).y0(a):(0,d_.line)().x(dV).y(d$)).defined(dF).curve(c)(u)})(o):n;return E.createElement("path",dR({},N(e),iE(e),{className:(0,S.clsx)("recharts-curve",t),d:null===l?void 0:l,ref:a}))},dH=["animationElapsedTime","isAnimating","isEntrance","layout","isRange","stroke","connectNulls"],dG=["id","baseLine"];function dq(){return(dq=Object.assign.bind()).apply(null,arguments)}function dX(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;ne.y||0));return(X(a)?s=Math.max(a,s):a&&Array.isArray(a)&&a.length&&(s=Math.max(...a.map(e=>e.y||0),s)),X(s))?E.createElement("rect",{x:le.x||0));return(X(a)?s=Math.max(a,s):a&&Array.isArray(a)&&a.length&&(s=Math.max(...a.map(e=>e.x||0),s)),X(s))?E.createElement("rect",{x:0,y:lnull==e?[]:1===t?e.flatMap(e=>"removed"===e.status?[]:[e.next]):e.flatMap(e=>"matched"===e.status?[d6(d6({},e.next),{},{x:et(e.prev.x,e.next.x,t),y:et(e.prev.y,e.next.y,t)})]:"added"===e.status?[e.next]:[]),connectNulls:!1,dot:!1,fill:"#3182bd",fillOpacity:.6,hide:!1,isAnimationActive:"auto",legendType:"line",stroke:"#3182bd",strokeWidth:1,type:"linear",label:!1,shape:function(e){var t,r=e.animationElapsedTime,n=void 0===r?1:r,a=e.isAnimating,i=e.isEntrance,o=e.layout,l=e.isRange,c=e.stroke,u=e.connectNulls,s=dX(e,dH),d="vertical"===o?"vertical":"horizontal",f=null!=u&&u,p=s1(),y=s.id,v=s.baseLine,h=N(dX(s,dG)),m=E.createElement(dU,dq({},s,{id:y,baseLine:v,connectNulls:f,stroke:"none",className:"recharts-area-area",layout:d})),g="none"!==c&&E.createElement(dU,dq({},h,{className:"recharts-area-curve",layout:d,type:s.type,connectNulls:f,fill:"none",stroke:c,points:s.points})),b="none"!==c&&l&&Array.isArray(v)&&E.createElement(dU,dq({},h,{className:"recharts-area-curve",layout:d,type:s.type,connectNulls:f,fill:"none",stroke:c,points:v}));return void 0!==i&&i&&(void 0!==a&&a||n<1)?E.createElement(L,null,E.createElement("defs",null,E.createElement("clipPath",{id:p},E.createElement(dQ,{alpha:n,points:null!=(t=s.points)?t:[],baseLine:v,layout:d,strokeWidth:s.strokeWidth}))),E.createElement(L,{clipPath:"url(#".concat(p,")")},m,g,b)):E.createElement(E.Fragment,null,m,g,b)},xAxisId:0,yAxisId:0,zIndex:aA.area};function d8(e,t){return e&&"none"!==e?e:t}var d4=P.memo(e=>{var t=e.dataKey,r=e.data,n=e.stroke,a=e.strokeWidth,i=e.fill,o=e.name,l=e.hide,c=e.unit,u=e.formatter,s=e.tooltipType,d=e.id,f={dataDefinedOnItem:r,getPosition:ei,settings:{stroke:n,strokeWidth:a,fill:i,dataKey:t,nameKey:void 0,name:nV(o,t),hide:l,type:s,color:d8(n,i),unit:c,formatter:u,graphicalItemId:d}};return P.createElement(uY,{tooltipEntrySettings:f})});function d9(e){var t=e.clipPathId,r=e.points,n=e.props,a=n.needClip,i=n.dot,o=n.dataKey,l=N(n);return P.createElement(iF,{points:r,dot:i,className:"recharts-area-dots",dotClassName:"recharts-area-dot",dataKey:o,baseProps:l,needClip:a,clipPathId:t})}function d7(e){var t=e.showLabels,r=e.children,n=e.points.map(e=>{var t,r,n={x:null!=(t=e.x)?t:0,y:null!=(r=e.y)?r:0,width:0,lowerWidth:0,upperWidth:0,height:0};return d6(d6({},n),{},{value:e.value,payload:e.payload,parentViewBox:void 0,viewBox:n,fill:void 0})});return P.createElement(ib,{value:t?n:void 0},r)}function fe(e){var t=e.points,r=e.baseLine,n=e.needClip,a=e.clipPathId,i=e.props,o=e.animationElapsedTime,l=e.isAnimating,c=e.isEntrance,u=i.layout,s=i.type,d=i.stroke,f=i.connectNulls,p=i.isRange,y=i.shape,v=i.id,h=d2(i,dJ),m=d6(d6({},z(h)),{},{id:v,points:t,connectNulls:f,type:s,baseLine:r,layout:u,stroke:d,isRange:p,animationElapsedTime:o,isAnimating:l,isEntrance:c});return P.createElement(P.Fragment,null,(null==t?void 0:t.length)>1&&P.createElement(L,{clipPath:n?"url(#clipPath-".concat(a,")"):void 0},P.createElement(dc,{option:y,DefaultShape:d5.shape,shapeProps:m})),P.createElement(d9,{points:t,props:h,clipPathId:a}))}function ft(e){var t,r=e.needClip,n=e.clipPathId,a=e.props,i=e.previousPointsRef,o=e.previousBaselineRef,l=a.points,c=a.baseLine,u=a.isAnimationActive,s=a.animationBegin,d=a.animationDuration,f=a.animationEasing,p=a.animationMatchBy,y=a.animationInterpolateFn,v=(0,P.useMemo)(()=>({points:l,baseLine:c}),[l,c]),h=sY(v,o),m=aw(),g=sQ(a.onAnimationStart,a.onAnimationEnd),b=g.isAnimating,x=g.handleAnimationStart,w=g.handleAnimationEnd,O=h.startValue;return null==m?null:(t=Array.isArray(c)&&Array.isArray(O)?sX(O,c,p):Array.isArray(c)?sX(null,c,p):null,P.createElement(sJ,{animationInput:v,animationIdPrefix:"recharts-area-",items:l,previousItemsRef:i,isAnimationActive:u,animationBegin:s,animationDuration:d,animationEasing:f,onAnimationStart:x,onAnimationEnd:w,animationInterpolateFn:y,animationMatchBy:p,layout:m},(e,i,o)=>{var u;return u=1===i?c:Array.isArray(c)?y(t,i,m):o?c:function(e,t,r){return X(e)?et(X(t)?t:void 0,e,r):null==e||G(e)?et(X(t)?t:void 0,0,r):e}(c,O,i),h.syncStepValue(u,i),P.createElement(d7,{showLabels:!b,points:l},a.children,P.createElement(fe,{points:e,baseLine:u,needClip:r,clipPathId:n,props:a,animationElapsedTime:i,isAnimating:b||i<1,isEntrance:o}),P.createElement(ij,{label:a.label}))}))}function fr(e){var t=e.needClip,r=e.clipPathId,n=e.props,a=(0,P.useRef)(null),i=(0,P.useRef)();return P.createElement(ft,{needClip:t,clipPathId:r,props:n,previousPointsRef:a,previousBaselineRef:i})}class fn extends P.PureComponent{render(){var e=this.props,t=e.hide,r=e.dot,n=e.points,a=e.className,i=e.top,o=e.left,l=e.needClip,c=e.xAxisId,u=e.yAxisId,s=e.width,d=e.height,f=e.id,p=e.baseLine,y=e.zIndex;if(t)return null;var v=(0,S.clsx)("recharts-area",a),h=function(e){var t=T(e);if(null!=t){var r=t.r,n=t.strokeWidth,a=Number(r),i=Number(n);return(Number.isNaN(a)||a<0)&&(a=3),(Number.isNaN(i)||i<0)&&(i=2),{r:a,strokeWidth:i}}return{r:3,strokeWidth:2}}(r),m=h.r,g=h.strokeWidth,b=iz(r),x=2*m+g,w=l?"url(#clipPath-".concat(b?"":"dots-").concat(f,")"):void 0;return P.createElement(a3,{zIndex:y},P.createElement(L,{className:v},l&&P.createElement("defs",null,P.createElement(uQ,{clipPathId:f,xAxisId:c,yAxisId:u}),!b&&P.createElement("clipPath",{id:"clipPath-dots-".concat(f)},P.createElement("rect",{x:o-x/2,y:i-x/2,width:s+x,height:d+x}))),P.createElement(fr,{needClip:l,clipPathId:f,props:this.props})),P.createElement(uX,{points:n,mainColor:d8(this.props.stroke,this.props.fill),itemDataKey:this.props.dataKey,activeDot:this.props.activeDot,clipPath:w}),this.props.isRange&&Array.isArray(p)&&P.createElement(uX,{points:p,mainColor:d8(this.props.stroke,this.props.fill),itemDataKey:this.props.dataKey,activeDot:this.props.activeDot,clipPath:w}))}}function fa(e){var t,r=e.activeDot,n=e.animationBegin,a=e.animationDuration,i=e.animationEasing,o=e.connectNulls,l=e.dot,c=e.fill,u=e.fillOpacity,s=e.hide,d=e.isAnimationActive,f=e.legendType,p=e.stroke,y=e.xAxisId,v=e.yAxisId,h=d2(e,d0),m=e6(ax),g=e6(ol),b=uZ(y,v).needClip,x=n6(),w=null!=(t=e6(t=>u7(t,e.id,x)))?t:{},O=w.points,j=w.isRange,A=w.baseLine,E=e6(uU);if("horizontal"!==m&&"vertical"!==m||null==E||"AreaChart"!==g&&"ComposedChart"!==g)return null;var S=E.height,k=E.width,I=E.x,C=E.y;return O&&O.length?P.createElement(fn,d1({},h,{activeDot:r,animationBegin:n,animationDuration:a,animationEasing:i,baseLine:A,connectNulls:o,dot:l,fill:c,fillOpacity:u,height:S,hide:s,layout:m,isAnimationActive:d,isRange:j,legendType:f,needClip:b,points:O,stroke:p,width:k,left:I,top:C,xAxisId:y,yAxisId:v})):null}var fi=P.memo(function(e){var t=eS(e,d5),r=n6();return P.createElement(s3,{id:t.id,type:"area"},e=>{var n,a,i,o,l;return P.createElement(P.Fragment,null,P.createElement(sj,{legendPayload:(n=t.dataKey,a=t.name,i=t.stroke,o=t.fill,l=t.legendType,[{inactive:t.hide,dataKey:n,type:l,color:d8(i,o),value:nV(a,n),payload:t}])}),P.createElement(d4,{dataKey:t.dataKey,data:t.data,stroke:t.stroke,strokeWidth:t.strokeWidth,fill:t.fill,name:t.name,hide:t.hide,unit:t.unit,formatter:t.formatter,tooltipType:t.tooltipType,id:e}),P.createElement(dn,{type:"area",id:e,data:t.data,dataKey:t.dataKey,xAxisId:t.xAxisId,yAxisId:t.yAxisId,zAxisId:0,stackId:n_(t.stackId),hide:t.hide,barSize:void 0,baseValue:t.baseValue,isPanorama:r,connectNulls:t.connectNulls}),P.createElement(fa,d1({},t,{id:e})))})},dw);fi.displayName="Area";var fo=(e,t)=>{if(t&&Array.isArray(e)){var r=Number.parseInt(t,10);if(!G(r))return e[r]}},fl=rM({name:"options",initialState:{chartName:"",tooltipPayloadSearcher:()=>void 0,eventEmitter:void 0,defaultTooltipEventType:"axis"},reducers:{createEventEmitter:e=>{null==e.eventEmitter&&(e.eventEmitter=Symbol("rechartsEventEmitter"))}}}),fc=fl.reducer,fu=fl.actions.createEventEmitter,fs=rM({name:"chartData",initialState:{chartData:void 0,computedData:void 0,dataStartIndex:0,dataEndIndex:0},reducers:{setChartData(e,t){if(e.chartData=t.payload,null==t.payload){e.dataStartIndex=0,e.dataEndIndex=0;return}t.payload.length>0&&e.dataEndIndex!==t.payload.length-1&&(e.dataEndIndex=t.payload.length-1)},setComputedData(e,t){e.computedData=t.payload},setDataStartEndIndexes(e,t){var r=t.payload,n=r.startIndex,a=r.endIndex;null!=n&&(e.dataStartIndex=n),null!=a&&(e.dataEndIndex=a)}}}),fd=fs.actions,ff=fd.setChartData,fp=fd.setDataStartEndIndexes;fd.setComputedData;var fy=fs.reducer,fv=rc([(e,t)=>t,ax,aF,om,uf,um,so,n0],(e,t,r,n,a,i,o,l)=>{if(e&&t&&n&&a&&i){if("horizontal"===t||"vertical"===t){var c=e,u=t,s=n,d=a,f=i,p=o,y=l;if(c&&s&&d&&f&&(v=c.relativeX,h=c.relativeY,v>=y.left&&v<=y.left+y.width&&h>=y.top&&h<=y.top+y.height)){var v,h,m=sr("horizontal"===u?c.relativeX:"vertical"===u?c.relativeY:void 0,p,f,s,d),g=((e,t,r,n)=>{var a=t.find(e=>e&&e.index===r);if(a){if("horizontal"===e)return{x:a.coordinate,y:n.relativeY};if("vertical"===e)return{x:n.relativeX,y:a.coordinate}}return{x:0,y:0}})(u,f,m,c);return{activeIndex:String(m),activeCoordinate:g}}return}if(e&&n&&a&&i&&r){var b=((e,t)=>{var r,n,a,i,o=((e,t)=>{var r,n,a,i,o=e.x,l=e.y,c=t.cx,u=t.cy,s=(r={x:o,y:l},n={x:c,y:u},a=r.x,i=r.y,Math.sqrt((a-n.x)**2+(i-n.y)**2));if(s<=0)return{radius:s,angle:0};var d=Math.acos((o-c)/s);return l>u&&(d=2*Math.PI-d),{radius:s,angle:180*d/Math.PI,angleInRadian:d}})({x:e.relativeX,y:e.relativeY},t),l=o.radius,c=o.angle,u=t.innerRadius,s=t.outerRadius;if(ls||0===l)return null;var d=(a=Math.min(Math.floor((r=t.startAngle)/360),Math.floor((n=t.endAngle)/360)),{startAngle:r-360*a,endAngle:n-360*a}),f=d.startAngle,p=d.endAngle,y=c;if(f<=p){for(;y>p;)y-=360;for(;y=f&&y<=p}else{for(;y>f;)y-=360;for(;y=p&&y<=f}return i?eG(eG({},t),{},{radius:l,angle:y+360*Math.min(Math.floor(t.startAngle/360),Math.floor(t.endAngle/360))}):null})(e,r);if(b){var x=sr("centric"===t?b.angle:b.radius,o,i,n,a),w=((e,t,r,n)=>{var a=t.find(e=>e&&e.index===r);if(a){if("centric"===e){var i=a.coordinate,o=n.radius;return st(st(st({},n),eX(n.cx,n.cy,o,i)),{},{angle:i,radius:o})}var l=a.coordinate,c=n.angle;return st(st(st({},n),eX(n.cx,n.cy,l,c)),{},{angle:c,radius:l})}return{angle:0,clockWise:!1,cx:0,cy:0,endAngle:0,innerRadius:0,outerRadius:0,radius:0,startAngle:0,x:0,y:0}})(t,i,x,b);return{activeIndex:String(x),activeCoordinate:w}}return}}});function fh(e){var t,r,n=e.currentTarget.getBoundingClientRect();if("getBBox"in e.currentTarget&&"function"==typeof e.currentTarget.getBBox){var a=e.currentTarget.getBBox();t=a.width>0?n.width/a.width:1,r=a.height>0?n.height/a.height:1}else{var i=e.currentTarget;t=i.offsetWidth>0?n.width/i.offsetWidth:1,r=i.offsetHeight>0?n.height/i.offsetHeight:1}var o=(e,a)=>({relativeX:Math.round((e-n.left)/t),relativeY:Math.round((a-n.top)/r)});return"touches"in e?Array.from(e.touches).map(e=>o(e.clientX,e.clientY)):o(e.clientX,e.clientY)}var fm=rw("mouseClick"),fg=r7();fg.startListening({actionCreator:fm,effect:(e,t)=>{var r=e.payload,n=fv(t.getState(),fh(r));(null==n?void 0:n.activeIndex)!=null&&t.dispatch(cC({activeIndex:n.activeIndex,activeDataKey:void 0,activeCoordinate:n.activeCoordinate}))}});var fb=rw("mouseMove"),fx=r7(),fw=null,fO=null,fj=null;function fA(e,t){return t instanceof HTMLElement?"HTMLElement <".concat(t.tagName,' class="').concat(t.className,'">'):t===window?"global.window":"children"===e&&"object"==typeof t&&null!==t?"<>":t}fx.startListening({actionCreator:fb,effect:(e,t)=>{var r=e.payload,n=t.getState().eventSettings,a=n.throttleDelay,i=n.throttledEvents,o="all"===i||(null==i?void 0:i.includes("mousemove"));null!==fw&&(cancelAnimationFrame(fw),fw=null),null===fO||"number"==typeof a&&o||(clearTimeout(fO),fO=null),fj=fh(r);var l=()=>{var e=t.getState(),r=ch(e,e.tooltip.settings.shared);if(!fj){fw=null,fO=null;return}if("axis"===r){var n=fv(e,fj);(null==n?void 0:n.activeIndex)!=null?t.dispatch(cI({activeIndex:n.activeIndex,activeDataKey:void 0,activeCoordinate:n.activeCoordinate})):t.dispatch(cS())}fw=null,fO=null};o?"raf"===a?fw=requestAnimationFrame(l):"number"==typeof a&&null===fO&&(fO=setTimeout(l,a)):l()}});var fE=rM({name:"referenceElements",initialState:{dots:[],areas:[],lines:[]},reducers:{addDot:(e,t)=>{e.dots.push(t.payload)},removeDot:(e,t)=>{var r=tJ(e).dots.findIndex(e=>e===t.payload);-1!==r&&e.dots.splice(r,1)},addArea:(e,t)=>{e.areas.push(t.payload)},removeArea:(e,t)=>{var r=tJ(e).areas.findIndex(e=>e===t.payload);-1!==r&&e.areas.splice(r,1)},addLine:(e,t)=>{e.lines.push(t.payload)},removeLine:(e,t)=>{var r=tJ(e).lines.findIndex(e=>e===t.payload);-1!==r&&e.lines.splice(r,1)}}}),fP=fE.actions;fP.addDot,fP.removeDot,fP.addArea,fP.removeArea,fP.addLine,fP.removeLine;var fS=fE.reducer,fk={x:0,y:0,width:0,height:0,padding:{top:0,right:0,bottom:0,left:0}},fI=rM({name:"brush",initialState:fk,reducers:{setBrushSettings:(e,t)=>null==t.payload?fk:t.payload}});fI.actions.setBrushSettings;var fC=fI.reducer,fD={accessibilityLayer:!0,barCategoryGap:"10%",barGap:4,barSize:void 0,className:void 0,maxBarSize:void 0,stackOffset:"none",syncId:void 0,syncMethod:"index",baseValue:void 0,reverseStackOrder:!1},fM=rM({name:"rootProps",initialState:fD,reducers:{updateOptions:(e,t)=>{var r;e.accessibilityLayer=t.payload.accessibilityLayer,e.barCategoryGap=t.payload.barCategoryGap,e.barGap=null!=(r=t.payload.barGap)?r:fD.barGap,e.barSize=t.payload.barSize,e.maxBarSize=t.payload.maxBarSize,e.stackOffset=t.payload.stackOffset,e.syncId=t.payload.syncId,e.syncMethod=t.payload.syncMethod,e.className=t.payload.className,e.baseValue=t.payload.baseValue,e.reverseStackOrder=t.payload.reverseStackOrder}}}),fN=fM.reducer,fT=fM.actions.updateOptions,fz=rM({name:"polarAxis",initialState:{radiusAxis:{},angleAxis:{}},reducers:{addRadiusAxis(e,t){e.radiusAxis[t.payload.id]=t.payload},removeRadiusAxis(e,t){delete e.radiusAxis[t.payload.id]},addAngleAxis(e,t){e.angleAxis[t.payload.id]=t.payload},removeAngleAxis(e,t){delete e.angleAxis[t.payload.id]}}}),f_=fz.actions;f_.addRadiusAxis,f_.removeRadiusAxis,f_.addAngleAxis,f_.removeAngleAxis;var fR=fz.reducer,fL=rM({name:"polarOptions",initialState:null,reducers:{updatePolarOptions:(e,t)=>null===e?t.payload:(e.startAngle=t.payload.startAngle,e.endAngle=t.payload.endAngle,e.cx=t.payload.cx,e.cy=t.payload.cy,e.innerRadius=t.payload.innerRadius,e.outerRadius=t.payload.outerRadius,e)}}),fB=fL.actions.updatePolarOptions,fK=fL.reducer,fF=rw("keyDown"),fW=rw("focus"),fV=rw("blur"),f$=r7(),fU=null,fH=null,fG=null;function fq(e){e.persist();var t=e.currentTarget;return new Proxy(e,{get:(e,r)=>{if("currentTarget"===r)return t;var n=Reflect.get(e,r);return"function"==typeof n?n.bind(e):n}})}f$.startListening({actionCreator:fF,effect:(e,t)=>{fG=e.payload,null!==fU&&(cancelAnimationFrame(fU),fU=null);var r=t.getState().eventSettings,n=r.throttleDelay,a=r.throttledEvents,i="all"===a||a.includes("keydown");null===fH||"number"==typeof n&&i||(clearTimeout(fH),fH=null);var o=()=>{try{var e,r=t.getState();if(!1===r.rootProps.accessibilityLayer)return;var n=r.tooltip.keyboardInteraction,a=fG;if("ArrowRight"!==a&&"ArrowLeft"!==a&&"Enter"!==a)return;var i=cR(n,cJ(r),lr(r),uc(r)),o=null==i?-1:Number(i),l=!Number.isFinite(o)||o<0,c=um(r),u=cJ(r),s=ch(r,r.tooltip.settings.shared);if("Enter"===a){if(l)return;var d=ss(r,s,"hover",String(n.index));t.dispatch(cM({active:!n.active,activeIndex:n.index,activeCoordinate:d}));return}var f=cf(r),p="left-to-right"===f?1:-1,y="ArrowRight"===a?1:-1;if(l){var v=lr(r),h=uc(r),m=e=>({active:!1,index:String(e),dataKey:void 0,graphicalItemId:void 0,coordinate:void 0});if(e=-1,y*p>0){for(var g=0;g=0;b--)if(null!=cR(m(b),u,v,h)){e=b;break}if(e<0)return}else{e=o+y*p;var x=(null==c?void 0:c.length)||u.length;if(0===x||e>=x||e<0)return}var w=ss(r,s,"hover",String(e));t.dispatch(cM({active:!0,activeIndex:e.toString(),activeCoordinate:w}))}finally{fU=null,fH=null}};i?"raf"===n?fU=requestAnimationFrame(o):"number"==typeof n&&null===fH&&(o(),fG=null,fH=setTimeout(()=>{fG?o():(fH=null,fU=null)},n)):o()}}),f$.startListening({actionCreator:fW,effect:(e,t)=>{var r=t.getState();if(!1!==r.rootProps.accessibilityLayer){var n=r.tooltip.keyboardInteraction;if(!n.active&&null==n.index){var a=ch(r,r.tooltip.settings.shared),i=ss(r,a,"hover",String("0"));t.dispatch(cM({active:!0,activeIndex:"0",activeCoordinate:i}))}}}}),f$.startListening({actionCreator:fV,effect:(e,t)=>{var r=t.getState();if(!1!==r.rootProps.accessibilityLayer){var n=r.tooltip.keyboardInteraction;n.active&&t.dispatch(cM({active:!1,activeIndex:n.index,activeCoordinate:n.coordinate}))}}});var fX=rw("externalEvent"),fY=r7(),fZ=new Map,fQ=new Map,fJ=new Map;fY.startListening({actionCreator:fX,effect:(e,t)=>{var r=e.payload,n=r.handler,a=r.reactEvent;if(null!=n){var i=a.type,o=fq(a);fJ.set(i,{handler:n,reactEvent:o});var l=fZ.get(i);void 0!==l&&(cancelAnimationFrame(l),fZ.delete(i));var c=t.getState().eventSettings,u=c.throttleDelay,s=c.throttledEvents,d="all"===s||(null==s?void 0:s.includes(i)),f=fQ.get(i);void 0===f||"number"==typeof u&&d||(clearTimeout(f),fQ.delete(i));var p=()=>{var e=fJ.get(i);try{if(!e)return;var r=e.handler,n=e.reactEvent,a=t.getState(),o={activeCoordinate:uk(a),activeDataKey:uA(a),activeIndex:uO(a),activeLabel:uj(a),activeTooltipIndex:uO(a),isTooltipActive:uI(a)};r&&r(o,n)}finally{fZ.delete(i),fQ.delete(i),fJ.delete(i)}};if(!d)return void p();if("raf"===u){var y=requestAnimationFrame(p);fZ.set(i,y)}else if("number"==typeof u){if(!fQ.has(i)){p();var v=setTimeout(p,u);fQ.set(i,v)}}else p()}}});var f0=rc([cF],e=>e.tooltipItemPayloads),f1=rc([f0,(e,t)=>t,(e,t,r)=>r],(e,t,r)=>{if(null!=t){var n=e.find(e=>e.settings.graphicalItemId===r);if(null!=n){var a=n.getPosition;if(null!=a)return a(t)}}}),f2=rw("touchMove"),f3=r7(),f6=null,f5=null,f8=null,f4=null;f3.startListening({actionCreator:f2,effect:(e,t)=>{var r=e.payload;if(null!=r.touches&&0!==r.touches.length){f4=fq(r);var n=t.getState().eventSettings,a=n.throttleDelay,i=n.throttledEvents,o="all"===i||i.includes("touchmove");null!==f6&&(cancelAnimationFrame(f6),f6=null),null===f5||"number"==typeof a&&o||(clearTimeout(f5),f5=null),f8=Array.from(r.touches).map(e=>fh({clientX:e.clientX,clientY:e.clientY,currentTarget:r.currentTarget}));var l=()=>{if(null!=f4){var e=t.getState(),r=ch(e,e.tooltip.settings.shared);if("axis"===r){var n,a=null==(n=f8)?void 0:n[0];if(null==a){f6=null,f5=null;return}var i=fv(e,a);(null==i?void 0:i.activeIndex)!=null&&t.dispatch(cI({activeIndex:i.activeIndex,activeDataKey:void 0,activeCoordinate:i.activeCoordinate}))}else if("item"===r){var o,l=f4.touches[0];if(null==document.elementFromPoint||null==l)return;var c=document.elementFromPoint(l.clientX,l.clientY);if(!c||!c.getAttribute)return;var u=c.getAttribute(nY),s=null!=(o=c.getAttribute(nZ))?o:void 0,d=cX(e).find(e=>e.id===s);if(null==u||null==d||null==s)return;var f=d.dataKey,p=f1(e,u,s);t.dispatch(cE({activeDataKey:f,activeIndex:u,activeCoordinate:p,activeGraphicalItemId:s}))}f6=null,f5=null}};if(!o)return void l();"raf"===a?f6=requestAnimationFrame(l):"number"==typeof a&&null===f5&&(l(),f4=null,f5=setTimeout(()=>{f4?l():(f5=null,f6=null)},a))}}});var f9=rM({name:"errorBars",initialState:{},reducers:{addErrorBar:(e,t)=>{var r=t.payload,n=r.itemId,a=r.errorBar;e[n]||(e[n]=[]),e[n].push(a)},replaceErrorBar:(e,t)=>{var r=t.payload,n=r.itemId,a=r.prev,i=r.next;e[n]&&(e[n]=e[n].map(e=>e.dataKey===a.dataKey&&e.direction===a.direction?i:e))},removeErrorBar:(e,t)=>{var r=t.payload,n=r.itemId,a=r.errorBar;e[n]&&(e[n]=e[n].filter(e=>e.dataKey!==a.dataKey||e.direction!==a.direction))}}}),f7=f9.actions;f7.addErrorBar,f7.replaceErrorBar,f7.removeErrorBar;var pe=f9.reducer,pt={throttleDelay:"raf",throttledEvents:["mousemove","touchmove","pointermove","scroll","wheel"]},pr=rM({name:"eventSettings",initialState:pt,reducers:{setEventSettings:(e,t)=>{null!=t.payload.throttleDelay&&(e.throttleDelay=t.payload.throttleDelay),null!=t.payload.throttledEvents&&(e.throttledEvents=t.payload.throttledEvents)}}}),pn=pr.actions.setEventSettings,pa=pr.reducer,pi=rM({name:"renderedTicks",initialState:{xAxis:{},yAxis:{}},reducers:{setRenderedTicks:(e,t)=>{var r=t.payload,n=r.axisType,a=r.axisId,i=r.ticks;e[n][a]=i},removeRenderedTicks:(e,t)=>{var r=t.payload,n=r.axisType,a=r.axisId;delete e[n][a]}}}),po=pi.actions,pl=po.setRenderedTicks,pc=po.removeRenderedTicks,pu=rv({brush:fC,cartesianAxis:uV,chartData:fy,errorBars:pe,eventSettings:pa,graphicalItems:dr,layout:nl,legend:sO,options:fc,polarAxis:fR,polarOptions:fK,referenceElements:fS,renderedTicks:pi.reducer,rootProps:fN,tooltip:cN,zIndex:a2}),ps=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Chart";return function(e){let t,r,n,a=function(e){let{thunk:t=!0,immutableCheck:r=!0,serializableCheck:n=!0,actionCreatorCheck:a=!0}=e??{},i=new rO;return t&&("boolean"==typeof t?i.push(rb):i.push(rg(t.extraArgument))),i},{reducer:i,middleware:o,devTools:l=!0,duplicateMiddlewareCheck:c=!0,preloadedState:u,enhancers:s}=e||{};if("function"==typeof i)t=i;else if(ry(i))t=rv(i);else throw Error(ne(1));r="function"==typeof o?o(a):a();let d=rh;l&&(d=rx({trace:!1,..."object"==typeof l&&l}));let f=(n=function(...e){return t=>(r,n)=>{let a=t(r,n),i=()=>{throw Error(rs(15))},o={getState:a.getState,dispatch:(e,...t)=>i(e,...t)};return i=rh(...e.map(e=>e(o)))(a.dispatch),{...a,dispatch:i}}}(...r),function(e){let{autoBatch:t=!0}=e??{},r=new rO(n);return t&&r.push(rk("object"==typeof t?t:void 0)),r});return function e(t,r,n){if("function"!=typeof t)throw Error(rs(2));if("function"==typeof r&&"function"==typeof n||"function"==typeof n&&"function"==typeof arguments[3])throw Error(rs(0));if("function"==typeof r&&void 0===n&&(n=r,r=void 0),void 0!==n){if("function"!=typeof n)throw Error(rs(1));return n(e)(t,r)}let a=t,i=r,o=new Map,l=o,c=0,u=!1;function s(){l===o&&(l=new Map,o.forEach((e,t)=>{l.set(t,e)}))}function d(){if(u)throw Error(rs(3));return i}function f(e){if("function"!=typeof e)throw Error(rs(4));if(u)throw Error(rs(5));let t=!0;s();let r=c++;return l.set(r,e),function(){if(t){if(u)throw Error(rs(6));t=!1,s(),l.delete(r),o=null}}}function p(e){if(!ry(e))throw Error(rs(7));if(void 0===e.type)throw Error(rs(8));if("string"!=typeof e.type)throw Error(rs(17));if(u)throw Error(rs(9));try{u=!0,i=a(i,e)}finally{u=!1}return(o=l).forEach(e=>{e()}),e}return p({type:rp.INIT}),{dispatch:p,subscribe:f,getState:d,replaceReducer:function(e){if("function"!=typeof e)throw Error(rs(10));a=e,p({type:rp.REPLACE})},[rd]:function(){return{subscribe(e){if("object"!=typeof e||null===e)throw Error(rs(11));function t(){e.next&&e.next(d())}return t(),{unsubscribe:f(t)}},[rd](){return this}}}}}(t,u,d(..."function"==typeof s?s(f):f()))}({reducer:pu,preloadedState:e,middleware:e=>e({serializableCheck:!1,immutableCheck:!["commonjs","es6","production"].includes("es6")}).concat([fg.middleware,fx.middleware,f$.middleware,fY.middleware,f3.middleware]),enhancers:e=>{var t=e;return"function"==typeof e&&(t=e()),t.concat(rk({type:"raf"}))},devTools:eo.devToolsEnabled&&{serialize:{replacer:fA},name:"recharts-".concat(t)}})};function pd(e){var t=e.preloadedState,r=e.children,n=e.reduxStoreName,a=n6(),i=(0,E.useRef)(null);return a?r:(null==i.current&&(i.current=ps(t,n)),E.createElement(dm,{context:eQ,store:i.current},r))}var pf=e=>{var t=e.chartData,r=e0(),n=n6();return(0,E.useEffect)(()=>n?()=>{}:(r(ff(t)),()=>{r(ff(void 0))}),[t,r,n]),null},pp=(0,E.memo)(function(e){var t=e.layout,r=e.margin,n=e0(),a=n6();return(0,E.useEffect)(()=>{a||(n(na(t)),n(nn(r)))},[n,a,t,r]),null},dw);function py(e){var t=e0();return(0,E.useEffect)(()=>{t(fT(e))},[t,e]),null}var pv=(0,E.memo)(e=>{var t=e0();return(0,E.useEffect)(()=>{t(pn(e))},[t,e]),null},dw),ph=()=>{var e;return null==(e=e6(e=>e.rootProps.accessibilityLayer))||e},pm=["children","width","height","viewBox","className","style","title","desc"];function pg(){return(pg=Object.assign.bind()).apply(null,arguments)}var pb=(0,E.forwardRef)((e,t)=>{var r=e.children,n=e.width,a=e.height,i=e.viewBox,o=e.className,l=e.style,c=e.title,u=e.desc,s=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n(n.current&&a(a0({zIndex:t,element:n.current,isPanorama:r})),()=>{a(a1({zIndex:t,isPanorama:r}))}),[a,t,r]),E.createElement("g",{tabIndex:-1,ref:n,className:"recharts-zIndex-layer_".concat(t)})}function pw(e){var t=e.children,r=e.isPanorama,n=e6(aU);if(!n||0===n.length)return t;var a=n.filter(e=>e<0),i=n.filter(e=>e>0);return E.createElement(E.Fragment,null,a.map(e=>E.createElement(px,{key:e,zIndex:e,isPanorama:r})),t,i.map(e=>E.createElement(px,{key:e,zIndex:e,isPanorama:r})))}var pO=["children"];function pj(){return(pj=Object.assign.bind()).apply(null,arguments)}var pA={width:"100%",height:"100%",display:"block"},pE=(0,E.forwardRef)((e,t)=>{var r,n,a=e6(n$),i=e6(nU),o=ph();if(!eI(a)||!eI(i))return null;var l=e.children,c=e.otherAttributes,u=e.title,s=e.desc;return null!=c&&(r="number"==typeof c.tabIndex?c.tabIndex:o?0:void 0,n="string"==typeof c.role?c.role:o?"application":void 0),E.createElement(pb,pj({},c,{title:u,desc:s,role:n,tabIndex:r,width:a,height:i,style:pA,ref:t}),l)}),pP=e=>{var t=e.children,r=e6(n8);if(!r)return null;var n=r.width,a=r.height,i=r.y,o=r.x;return E.createElement(pb,{width:n,height:a,x:o,y:i},t)},pS=(0,E.forwardRef)((e,t)=>{var r=e.children,n=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;ne.length)&&(t=e.length);for(var r=0,n=Array(t);rtypeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],c=!0,u=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;c=!1}else for(;!(c=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);c=!0);}catch(e){u=!0,a=e}finally{try{if(!c&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(u)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return pF(e,t);var r=({}).toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?pF(e,t):void 0}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function pF(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var e,t,r,n,a,i,o,l,c,u,s,d;return e=e0(),(0,E.useEffect)(()=>{e(fu())},[e]),t=e6(oc),r=e6(os),n=e0(),a=e6(ou),i=e6(um),o=e6(ax),l=am(),c=e6(e=>e.rootProps.className),(0,E.useEffect)(()=>{if(null==t)return ei;var e=(e,c,u)=>{if(r!==u&&t===e){if(!1===c.payload.active)return void n(cD({active:!1,coordinate:void 0,dataKey:void 0,index:null,label:void 0,sourceViewBox:void 0,graphicalItemId:void 0}));if("index"===a){if(l&&null!=c&&null!=(s=c.payload)&&s.coordinate&&c.payload.sourceViewBox){var s,d,f=c.payload.coordinate,p=f.x,y=f.y,v=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;nString(e.value)===c.payload.label));var j=c.payload.coordinate;if(null==j||null==l)return void n(cD({active:!1,coordinate:void 0,dataKey:void 0,index:null,label:void 0,sourceViewBox:void 0,graphicalItemId:void 0}));if(null==d)return void n(cD({active:!1,coordinate:void 0,dataKey:void 0,index:null,label:void 0,sourceViewBox:c.payload.sourceViewBox,graphicalItemId:void 0}));var A=j.x,E=j.y,P=Math.min(A,l.x+l.width),S=Math.min(E,l.y+l.height),k={x:"horizontal"===o?d.coordinate:P,y:"horizontal"===o?S:d.coordinate};n(cD({active:c.payload.active,coordinate:k,dataKey:c.payload.dataKey,index:String(d.index),label:c.payload.label,sourceViewBox:c.payload.sourceViewBox,graphicalItemId:c.payload.graphicalItemId}))}}};return pk.on(pI,e),()=>{pk.off(pI,e)}},[c,n,r,t,a,i,o,l]),u=e6(oc),s=e6(os),d=e0(),(0,E.useEffect)(()=>{if(null==u)return ei;var e=(e,t,r)=>{s!==r&&u===e&&d(fp(t))};return pk.on(pC,e),()=>{pk.off(pC,e)}},[d,s,u]),null};function pV(e){if("number"==typeof e)return e;if("string"==typeof e){var t=parseFloat(e);if(!Number.isNaN(t))return t}return 0}var p$=(0,E.forwardRef)((e,t)=>{var r,n,a=(0,E.useRef)(null),i=pK((0,E.useState)({containerWidth:pV(null==(r=e.style)?void 0:r.width),containerHeight:pV(null==(n=e.style)?void 0:n.height)}),2),o=i[0],l=i[1],c=(0,E.useCallback)((e,t)=>{l(r=>{var n=Math.round(e),a=Math.round(t);return r.containerWidth===n&&r.containerHeight===a?r:{containerWidth:n,containerHeight:a}})},[]),u=(0,E.useCallback)(e=>{if("function"==typeof t&&t(e),null!=a.current&&(a.current.disconnect(),a.current=null),null!=e&&"u">typeof ResizeObserver){var r=e.getBoundingClientRect();c(r.width,r.height);var n=new ResizeObserver(e=>{var t=e[0];if(null!=t){var r=t.contentRect;c(r.width,r.height)}});n.observe(e),a.current=n}},[t,c]);return(0,E.useEffect)(()=>()=>{var e=a.current;null!=e&&e.disconnect()},[c]),E.createElement(E.Fragment,null,E.createElement(aj,{width:o.containerWidth,height:o.containerHeight}),E.createElement("div",pB({ref:u},e)))}),pU=(0,E.forwardRef)((e,t)=>{var r=e.width,n=e.height,a=pK((0,E.useState)({containerWidth:pV(r),containerHeight:pV(n)}),2),i=a[0],o=a[1],l=(0,E.useCallback)((e,t)=>{o(r=>{var n=Math.round(e),a=Math.round(t);return r.containerWidth===n&&r.containerHeight===a?r:{containerWidth:n,containerHeight:a}})},[]),c=(0,E.useCallback)(e=>{if("function"==typeof t&&t(e),null!=e){var r=e.getBoundingClientRect();l(r.width,r.height)}},[t,l]);return E.createElement(E.Fragment,null,E.createElement(aj,{width:i.containerWidth,height:i.containerHeight}),E.createElement("div",pB({ref:c},e)))}),pH=(0,E.forwardRef)((e,t)=>{var r=e.width,n=e.height;return E.createElement(E.Fragment,null,E.createElement(aj,{width:r,height:n}),E.createElement("div",pB({ref:t},e)))}),pG=(0,E.forwardRef)((e,t)=>{var r=e.width,n=e.height;return"string"==typeof r||"string"==typeof n?E.createElement(pU,pB({},e,{ref:t})):"number"==typeof r&&"number"==typeof n?E.createElement(pH,pB({},e,{width:r,height:n,ref:t})):E.createElement(E.Fragment,null,E.createElement(aj,{width:r,height:n}),E.createElement("div",pB({ref:t},e)))}),pq=(0,E.forwardRef)((e,t)=>{var r,n,a,i,o,l,c=e.children,u=e.className,s=e.height,d=e.onClick,f=e.onContextMenu,p=e.onDoubleClick,y=e.onMouseDown,v=e.onMouseEnter,h=e.onMouseLeave,m=e.onMouseMove,g=e.onMouseUp,b=e.onTouchEnd,x=e.onTouchMove,w=e.onTouchStart,O=e.style,j=e.width,A=e.responsive,P=e.dispatchTouchEvents,k=void 0===P||P,I=(0,E.useRef)(null),C=e0(),D=pK((0,E.useState)(null),2),M=D[0],N=D[1],T=pK((0,E.useState)(null),2),z=T[0],_=T[1],R=(r=e0(),i=(a=function(e){if(Array.isArray(e))return e}(n=(0,E.useState)(null))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(n)||function(e){if(e){if("string"==typeof e)return pz(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?pz(e,2):void 0}}(n)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}())[0],o=a[1],l=e6(nH),(0,E.useEffect)(()=>{if(null!=i){var e=i.getBoundingClientRect().width/i.offsetWidth;ek(e)&&e!==l&&r(no(e))}},[i,r,l]),o),L=ap(),B=(null==L?void 0:L.width)>0?L.width:j,K=(null==L?void 0:L.height)>0?L.height:s,F=(0,E.useCallback)(e=>{R(e),"function"==typeof t&&t(e),N(e),_(e),null!=e&&(I.current=e)},[R,t,N,_]),W=(0,E.useCallback)(e=>{C(fm(e)),C(fX({handler:d,reactEvent:e}))},[C,d]),V=(0,E.useCallback)(e=>{C(fb(e)),C(fX({handler:v,reactEvent:e}))},[C,v]),$=(0,E.useCallback)(e=>{C(cS()),C(fX({handler:h,reactEvent:e}))},[C,h]),U=(0,E.useCallback)(e=>{C(fb(e)),C(fX({handler:m,reactEvent:e}))},[C,m]),H=(0,E.useCallback)(()=>{C(fW())},[C]),G=(0,E.useCallback)(()=>{C(fV())},[C]),q=(0,E.useCallback)(e=>{C(fF(e.key))},[C]),X=(0,E.useCallback)(e=>{C(fX({handler:f,reactEvent:e}))},[C,f]),Y=(0,E.useCallback)(e=>{C(fX({handler:p,reactEvent:e}))},[C,p]),Z=(0,E.useCallback)(e=>{C(fX({handler:y,reactEvent:e}))},[C,y]),Q=(0,E.useCallback)(e=>{C(fX({handler:g,reactEvent:e}))},[C,g]),J=(0,E.useCallback)(e=>{C(fX({handler:w,reactEvent:e}))},[C,w]),ee=(0,E.useCallback)(e=>{k&&C(f2(e)),C(fX({handler:x,reactEvent:e}))},[C,k,x]),et=(0,E.useCallback)(e=>{C(fX({handler:b,reactEvent:e}))},[C,b]);return E.createElement(p_.Provider,{value:M},E.createElement(pR.Provider,{value:z},E.createElement(A?p$:pG,{width:null!=B?B:null==O?void 0:O.width,height:null!=K?K:null==O?void 0:O.height,className:(0,S.clsx)("recharts-wrapper",u),style:function(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t,r=e.children,n=(function(e){if(Array.isArray(e))return e}(t=(0,E.useState)("".concat(Q("recharts"),"-clip")))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),1!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(t)||function(e){if(e){if("string"==typeof e)return pX(e,1);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?pX(e,1):void 0}}(t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}())[0],a=e6(uU);if(null==a)return null;var i=a.x,o=a.y,l=a.width,c=a.height;return E.createElement(pY.Provider,{value:n},E.createElement("defs",null,E.createElement("clipPath",{id:n},E.createElement("rect",{x:i,y:o,height:c,width:l}))),r)},pQ=["width","height","responsive","children","className","style","compact","title","desc"],pJ=(0,E.forwardRef)((e,t)=>{var r=e.width,n=e.height,a=e.responsive,i=e.children,o=e.className,l=e.style,c=e.compact,u=e.title,s=e.desc,d=N(function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;nE.createElement(p3,{chartName:"AreaChart",defaultTooltipEventType:"axis",validateTooltipEventTypes:p6,tooltipPayloadSearcher:fo,categoricalChartProps:e,ref:t})),p8=function(e){var t=e.width,r=e.height,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=(n%180+180)%180*Math.PI/180,i=Math.atan(r/t);return Math.abs(a>i&&ae*a)return!1;var i=r();return e*(t-e*i/2-n)>=0&&e*(t+e*i/2-a)<=0}function p7(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function ye(e){for(var t=1;t{var a,i="function"==typeof v?v(e.value,n):e.value;return"width"===g?(a=ep(i,{fontSize:t,letterSpacing:r}),p8({width:a.width+b.width,height:a.height+b.height},m)):ep(i,{fontSize:t,letterSpacing:r})[g]},w=s[0],O=s[1],j=s.length>=2&&null!=w&&null!=O?H(O.coordinate-w.coordinate):1,A=(n="width"===g,a=d.x,i=d.y,o=d.width,l=d.height,1===j?{start:n?a:i,end:n?a+o:i+l}:{start:n?a+o:i+l,end:n?a:i});return"equidistantPreserveStart"===y?function(e,t,r,n,a){for(var i,o=(n||[]).slice(),l=t.start,c=t.end,u=0,s=1,d=l;s<=o.length;)if(i=function(){var t,i=null==n?void 0:n[u];if(void 0===i)return{v:p4(n,s)};var o=u,f=()=>(void 0===t&&(t=r(i,o)),t),p=i.coordinate,y=0===u||p9(e,p,f,d,c);y||(u=0,d=l,s+=1),y&&(d=p+e*(f()/2+a),u+=s)}())return i.v;return[]}(j,A,x,s,f):"equidistantPreserveEnd"===y?function(e,t,r,n,a){var i=(n||[]).slice().length;if(0===i)return[];for(var o=t.start,l=t.end,c=1;c<=i;c++){for(var u,s=(i-1)%c,d=o,f=!0,p=s;p(void 0===t&&(t=r(i,o)),t),u=i.coordinate,y=p===s||p9(e,u,c,d,l);if(!y)return f=!1,1;y&&(d=u+e*(c()/2+a))}())||1!==u);p+=c);if(f){for(var y=[],v=s;v0?s.coordinate-f*e:s.coordinate}),null!=s.tickCoord&&p9(e,s.tickCoord,()=>d,c,u)&&(u=s.tickCoord-e*(d/2+a),o[l-1]=ye(ye({},s),{},{isShow:!0}))}}for(var p=i?l-1:l,y=function(t){var n,i=o[t];if(null==i)return 1;var l=i,s=()=>(void 0===n&&(n=r(i,t)),n);if(0===t){var d=e*(l.coordinate-e*s()/2-c);o[t]=l=ye(ye({},l),{},{tickCoord:d<0?l.coordinate-d*e:l.coordinate})}else o[t]=l=ye(ye({},l),{},{tickCoord:l.coordinate});null!=l.tickCoord&&p9(e,l.tickCoord,s,c,u)&&(c=l.tickCoord+e*(s()/2+a),o[t]=ye(ye({},l),{},{isShow:!0}))},v=0;v(void 0===n&&(n=r(u,t)),n);if(t===o-1){var f=e*(s.coordinate+e*d()/2-c);i[t]=s=ye(ye({},s),{},{tickCoord:f>0?s.coordinate-f*e:s.coordinate})}else i[t]=s=ye(ye({},s),{},{tickCoord:s.coordinate});null!=s.tickCoord&&p9(e,s.tickCoord,d,l,c)&&(c=s.tickCoord-e*(d()/2+a),i[t]=ye(ye({},s),{},{isShow:!0}))},s=o-1;s>=0;s--)if(u(s))continue;return i}(j,A,x,s,f)).filter(e=>e.isShow)}function yr(e){return e&&"object"==typeof e&&"className"in e&&"string"==typeof e.className?e.className:""}var yn=["axisLine","width","height","className","hide","ticks","axisType","axisId"];function ya(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],c=!0,u=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;c=!1}else for(;!(c=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);c=!0);}catch(e){u=!0,a=e}finally{try{if(!c&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(u)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return yi(e,t);var r=({}).toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?yi(e,t):void 0}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function yi(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);rnull==n||null==r?ei:(a(pl({ticks:t.map(e=>({value:e.value,coordinate:e.coordinate,offset:e.offset,index:e.index})),axisId:n,axisType:r})),()=>{a(pc({axisId:n,axisType:r}))}),[a,t,n,r]),null}var yp=(0,E.forwardRef)((e,t)=>{var r=e.ticks,n=e.tick,a=e.tickLine,i=e.stroke,o=e.tickFormatter,l=e.unit,c=e.padding,u=e.tickTextProps,s=e.orientation,d=e.mirror,f=e.x,p=e.y,y=e.width,v=e.height,h=e.tickSize,m=e.tickMargin,g=e.fontSize,b=e.letterSpacing,x=e.getTicksConfig,w=e.events,O=e.axisType,j=e.axisId,A=yt(yc(yc({},x),{},{ticks:void 0===r?[]:r}),g,b),P=N(x),k=T(n),I=eL(P.textAnchor)?P.textAnchor:function(e,t){switch(e){case"left":return t?"start":"end";case"right":return t?"end":"start";default:return"middle"}}(s,d),C=function(e,t){switch(e){case"left":case"right":return"middle";case"top":return t?"start":"end";default:return t?"end":"start"}}(s,d),D={};"object"==typeof a&&(D=a);var M=yc(yc({},P),{},{fill:"none"},D),z=A.map(e=>yc({entry:e},function(e,t,r,n,a,i,o,l,c){var u,s,d,f,p,y,v=l?-1:1,h=e.tickSize||o,m=X(e.tickCoord)?e.tickCoord:e.coordinate;switch(i){case"top":u=s=e.coordinate,y=(d=(f=r+!l*a)-v*h)-v*c,p=m;break;case"left":d=f=e.coordinate,p=(u=(s=t+!l*n)-v*h)-v*c,y=m;break;case"right":d=f=e.coordinate,p=(u=(s=t+l*n)+v*h)+v*c,y=m;break;default:u=s=e.coordinate,y=(d=(f=r+l*a)+v*h)+v*c,p=m}return{line:{x1:u,y1:d,x2:s,y2:f},tick:{x:p,y:y}}}(e,f,p,y,v,s,h,d,m))),_=z.map(e=>{var t=e.entry,r=e.line;return E.createElement(L,{className:"recharts-cartesian-axis-tick",key:"tick-".concat(t.value,"-").concat(t.coordinate,"-").concat(t.tickCoord)},a&&E.createElement("line",yo({},M,r,{className:(0,S.clsx)("recharts-cartesian-axis-tick-line",V(a,"className"))})))}),R=z.map((e,t)=>{var r,a,s=e.entry,d=e.tick,f=yc(yc(yc(yc({verticalAnchor:C},P),{},{textAnchor:I,stroke:"none",fill:i},d),{},{index:t,payload:s,visibleTicksCount:A.length,tickFormatter:o,padding:c},u),{},{angle:null!=(r=null!=(a=null==u?void 0:u.angle)?a:P.angle)?r:0}),p=yc(yc({},f),k);return E.createElement(L,yo({className:"recharts-cartesian-axis-tick-label",key:"tick-label-".concat(s.value,"-").concat(s.coordinate,"-").concat(s.tickCoord)},iP(w,s,t)),n&&E.createElement(yd,{option:n,tickProps:p,value:"".concat("function"==typeof o?o(s.value,t):s.value).concat(l||"")}))});return E.createElement("g",{className:"recharts-cartesian-axis-ticks recharts-".concat(O,"-ticks")},E.createElement(yf,{ticks:A,axisId:j,axisType:O}),R.length>0&&E.createElement(a3,{zIndex:aA.label},E.createElement("g",{className:"recharts-cartesian-axis-tick-labels recharts-".concat(O,"-tick-labels"),ref:t},R)),_.length>0&&E.createElement("g",{className:"recharts-cartesian-axis-tick-lines recharts-".concat(O,"-tick-lines")},_))}),yy=(0,E.forwardRef)((e,t)=>{var r=e.axisLine,n=e.width,a=e.height,i=e.className,o=e.hide,l=e.ticks,c=e.axisType,u=e.axisId,s=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n({getCalculatedWidth:()=>{var t;return(e=>{var t=e.ticks,r=e.label,n=e.labelGapWithTick,a=e.tickSize,i=e.tickMargin,o=0;if(t){Array.from(t).forEach(e=>{if(e){var t=e.getBoundingClientRect();t.width>o&&(o=t.width)}});var l=r?r.getBoundingClientRect().width:0;return Math.round(o+((void 0===a?0:a)+(void 0===i?0:i))+l+(r?void 0===n?5:n:0))}return 0})({ticks:m.current,label:null==(t=e.labelRef)?void 0:t.current,labelGapWithTick:5,tickSize:e.tickSize,tickMargin:e.tickMargin})}}));var g=(0,E.useCallback)(e=>{if(e){var t=e.getElementsByClassName("recharts-cartesian-axis-tick-value");m.current=t;var r=t[0];if(r){var n=window.getComputedStyle(r),a=n.fontSize,i=n.letterSpacing;(a!==f||i!==v)&&(p(a),h(i))}}},[f,v]);return o||null!=n&&n<=0||null!=a&&a<=0?null:E.createElement(a3,{zIndex:e.zIndex},E.createElement(L,{className:(0,S.clsx)("recharts-cartesian-axis",i)},E.createElement(ys,{x:e.x,y:e.y,width:n,height:a,orientation:e.orientation,mirror:e.mirror,axisLine:r,otherSvgProps:N(e)}),E.createElement(yp,{ref:g,axisType:c,events:s,fontSize:f,getTicksConfig:e,height:e.height,letterSpacing:v,mirror:e.mirror,orientation:e.orientation,padding:e.padding,stroke:e.stroke,tick:e.tick,tickFormatter:e.tickFormatter,tickLine:e.tickLine,tickMargin:e.tickMargin,tickSize:e.tickSize,tickTextProps:e.tickTextProps,ticks:l,unit:e.unit,width:e.width,x:e.x,y:e.y,axisId:u}),E.createElement(ia,{x:e.x,y:e.y,width:e.width,height:e.height,lowerWidth:e.width,upperWidth:e.width},E.createElement(id,{label:e.label,labelRef:e.labelRef}),e.children)))}),yv=E.forwardRef((e,t)=>{var r=eS(e,yu);return E.createElement(yy,yo({},r,{ref:t}))});yv.displayName="CartesianAxis";var yh=["x1","y1","x2","y2","key"],ym=["offset"],yg=["xAxisId","yAxisId"],yb=["xAxisId","yAxisId"];function yx(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function yw(e){for(var t=1;t{var t=e.fill;if(!t||"none"===t)return null;var r=e.fillOpacity,n=e.x,a=e.y,i=e.width,o=e.height,l=e.ry;return E.createElement("rect",{x:n,y:a,ry:l,width:i,height:o,stroke:"none",fill:t,fillOpacity:r,className:"recharts-cartesian-grid-bg"})};function yE(e){var t=e.option,r=e.lineItemProps;if(E.isValidElement(t))n=E.cloneElement(t,r);else if("function"==typeof t)n=t(r);else{var n,a,i=r.x1,o=r.y1,l=r.x2,c=r.y2,u=r.key,s=null!=(a=N(yj(r,yh)))?a:{},d=(s.offset,yj(s,ym));n=E.createElement("line",yO({},d,{x1:i,y1:o,x2:l,y2:c,fill:"none",key:u}))}return n}function yP(e){var t=e.x,r=e.width,n=e.horizontal,a=void 0===n||n,i=e.horizontalPoints;if(!a||!i||!i.length)return null;e.xAxisId,e.yAxisId;var o=yj(e,yg),l=i.map((e,n)=>{var i=yw(yw({},o),{},{x1:t,y1:e,x2:t+r,y2:e,key:"line-".concat(n),index:n});return E.createElement(yE,{key:"line-".concat(n),option:a,lineItemProps:i})});return E.createElement("g",{className:"recharts-cartesian-grid-horizontal"},l)}function yS(e){var t=e.y,r=e.height,n=e.vertical,a=void 0===n||n,i=e.verticalPoints;if(!a||!i||!i.length)return null;e.xAxisId,e.yAxisId;var o=yj(e,yb),l=i.map((e,n)=>{var i=yw(yw({},o),{},{x1:e,y1:t,x2:e,y2:t+r,key:"line-".concat(n),index:n});return E.createElement(yE,{option:a,lineItemProps:i,key:"line-".concat(n)})});return E.createElement("g",{className:"recharts-cartesian-grid-vertical"},l)}function yk(e){var t=e.horizontalFill,r=e.fillOpacity,n=e.x,a=e.y,i=e.width,o=e.height,l=e.horizontalPoints,c=e.horizontal;if(!(void 0===c||c)||!t||!t.length||null==l)return null;var u=l.map(e=>Math.round(e+a-a)).sort((e,t)=>e-t);a!==u[0]&&u.unshift(0);var s=u.map((e,l)=>{var c=u[l+1],s=null==c?a+o-e:c-e;if(s<=0)return null;var d=l%t.length;return E.createElement("rect",{key:"react-".concat(l),y:e,x:n,height:s,width:i,stroke:"none",fill:t[d],fillOpacity:r,className:"recharts-cartesian-grid-bg"})});return E.createElement("g",{className:"recharts-cartesian-gridstripes-horizontal"},s)}function yI(e){var t=e.vertical,r=e.verticalFill,n=e.fillOpacity,a=e.x,i=e.y,o=e.width,l=e.height,c=e.verticalPoints;if(!(void 0===t||t)||!r||!r.length)return null;var u=c.map(e=>Math.round(e+a-a)).sort((e,t)=>e-t);a!==u[0]&&u.unshift(0);var s=u.map((e,t)=>{var c=u[t+1],s=null==c?a+o-e:c-e;if(s<=0)return null;var d=t%r.length;return E.createElement("rect",{key:"react-".concat(t),x:e,y:i,width:s,height:l,stroke:"none",fill:r[d],fillOpacity:n,className:"recharts-cartesian-grid-bg"})});return E.createElement("g",{className:"recharts-cartesian-gridstripes-vertical"},s)}var yC=(e,t)=>{var r=e.xAxis,n=e.width,a=e.height,i=e.offset;return nN(yt(yw(yw(yw({},yu),r),{},{ticks:nT(r,!0),viewBox:{x:0,y:0,width:n,height:a}})),i.left,i.left+i.width,t)},yD=(e,t)=>{var r=e.yAxis,n=e.width,a=e.height,i=e.offset;return nN(yt(yw(yw(yw({},yu),r),{},{ticks:nT(r,!0),viewBox:{x:0,y:0,width:n,height:a}})),i.top,i.top+i.height,t)},yM={horizontal:!0,vertical:!0,horizontalPoints:[],verticalPoints:[],stroke:"#ccc",fill:"none",verticalFill:[],horizontalFill:[],xAxisId:0,yAxisId:0,syncWithTicks:!1,zIndex:aA.grid};function yN(e){var t=e6(n$),r=e6(nU),n=ab(),a=yw(yw({},eS(e,yM)),{},{x:X(e.x)?e.x:n.left,y:X(e.y)?e.y:n.top,width:X(e.width)?e.width:n.width,height:X(e.height)?e.height:n.height}),i=a.xAxisId,o=a.yAxisId,l=a.x,c=a.y,u=a.width,s=a.height,d=a.syncWithTicks,f=a.horizontalValues,p=a.verticalValues,y=n6(),v=e6(e=>co(e,"xAxis",i,y)),h=e6(e=>co(e,"yAxis",o,y));if(!eI(u)||!eI(s)||!X(l)||!X(c))return null;var m=a.verticalCoordinatesGenerator||yC,g=a.horizontalCoordinatesGenerator||yD,b=a.horizontalPoints,x=a.verticalPoints;if((!b||!b.length)&&"function"==typeof g){var w=f&&f.length,O=g({yAxis:h?yw(yw({},h),{},{ticks:w?f:h.ticks}):void 0,width:null!=t?t:u,height:null!=r?r:s,offset:n},!!w||d);n4(Array.isArray(O),"horizontalCoordinatesGenerator should return Array but instead it returned [".concat(typeof O,"]")),Array.isArray(O)&&(b=O)}if((!x||!x.length)&&"function"==typeof m){var j=p&&p.length,A=m({xAxis:v?yw(yw({},v),{},{ticks:j?p:v.ticks}):void 0,width:null!=t?t:u,height:null!=r?r:s,offset:n},!!j||d);n4(Array.isArray(A),"verticalCoordinatesGenerator should return Array but instead it returned [".concat(typeof A,"]")),Array.isArray(A)&&(x=A)}return E.createElement(a3,{zIndex:a.zIndex},E.createElement("g",{className:"recharts-cartesian-grid"},E.createElement(yA,{fill:a.fill,fillOpacity:a.fillOpacity,x:a.x,y:a.y,width:a.width,height:a.height,ry:a.ry}),E.createElement(yk,yO({},a,{horizontalPoints:b})),E.createElement(yI,yO({},a,{verticalPoints:x})),E.createElement(yP,yO({},a,{offset:n,horizontalPoints:b,xAxis:v,yAxis:h})),E.createElement(yS,yO({},a,{offset:n,verticalPoints:x,xAxis:v,yAxis:h}))))}yN.displayName="CartesianGrid";var yT=["domain","range"],yz=["domain","range"];function y_(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{if(null!=o)return y$(y$({},i),{},{type:o})},[i,o]);return(0,E.useLayoutEffect)(()=>{null!=l&&(null===r.current?t(u_(l)):r.current!==l&&t(uR({prev:r.current,next:l})),r.current=l)},[l,t]),(0,E.useLayoutEffect)(()=>()=>{r.current&&(t(uL(r.current)),r.current=null)},[t]),null}var yG=e=>{var t=e.xAxisId,r=e.className,n=e6(n2),a=n6(),i="xAxis",o=e6(e=>cl(e,i,t,a)),l=e6(e=>l4(e,t)),c=e6(e=>ce(e,t)),u=e6(e=>oN(e,t));if(null==l||null==c||null==u)return null;e.dangerouslySetInnerHTML,e.ticks,e.scale;var s=yU(e,yK);u.id,u.scale;var d=yU(u,yF);return E.createElement(yv,yW({},s,d,{x:c.x,y:c.y,width:l.width,height:l.height,className:(0,S.clsx)("recharts-".concat(i," ").concat(i),r),viewBox:n,ticks:o,axisType:i,axisId:t}))},yq={allowDataOverflow:oM.allowDataOverflow,allowDecimals:oM.allowDecimals,allowDuplicatedCategory:oM.allowDuplicatedCategory,angle:oM.angle,axisLine:yu.axisLine,height:oM.height,hide:!1,includeHidden:oM.includeHidden,interval:oM.interval,label:!1,minTickGap:oM.minTickGap,mirror:oM.mirror,orientation:oM.orientation,padding:oM.padding,reversed:oM.reversed,scale:oM.scale,tick:oM.tick,tickCount:oM.tickCount,tickLine:yu.tickLine,tickSize:yu.tickSize,type:oM.type,niceTicks:oM.niceTicks,xAxisId:0},yX=E.memo(e=>{var t=eS(e,yq);return E.createElement(E.Fragment,null,E.createElement(yH,{allowDataOverflow:t.allowDataOverflow,allowDecimals:t.allowDecimals,allowDuplicatedCategory:t.allowDuplicatedCategory,angle:t.angle,dataKey:t.dataKey,domain:t.domain,height:t.height,hide:t.hide,id:t.xAxisId,includeHidden:t.includeHidden,interval:t.interval,minTickGap:t.minTickGap,mirror:t.mirror,name:t.name,orientation:t.orientation,padding:t.padding,reversed:t.reversed,scale:t.scale,tick:t.tick,tickCount:t.tickCount,tickFormatter:t.tickFormatter,ticks:t.ticks,type:t.type,unit:t.unit,niceTicks:t.niceTicks}),E.createElement(yG,t))},yL);yX.displayName="XAxis";var yY=["type"],yZ=["dangerouslySetInnerHTML","ticks","scale"],yQ=["id","scale"];function yJ(){return(yJ=Object.assign.bind()).apply(null,arguments)}function y0(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function y1(e){for(var t=1;t{if(null!=o)return y1(y1({},i),{},{type:o})},[o,i]);return(0,E.useLayoutEffect)(()=>{null!=l&&(null===r.current?t(uB(l)):r.current!==l&&t(uK({prev:r.current,next:l})),r.current=l)},[l,t]),(0,E.useLayoutEffect)(()=>()=>{r.current&&(t(uF(r.current)),r.current=null)},[t]),null}function y6(e){var t=e.yAxisId,r=e.className,n=e.width,a=e.label,i=(0,E.useRef)(null),o=(0,E.useRef)(null),l=e6(n2),c=n6(),u=e0(),s="yAxis",d=e6(e=>cr(e,t)),f=e6(e=>ct(e,t)),p=e6(e=>cl(e,s,t,c)),y=e6(e=>o_(e,t));if((0,E.useLayoutEffect)(()=>{if(!("auto"!==n||!d||il(a)||(0,E.isValidElement)(a))&&null!=y){var e=i.current;if(e){var r=e.getCalculatedWidth();Math.round(d.width)!==Math.round(r)&&u(uW({id:t,width:r}))}}},[p,d,u,a,t,n,y]),null==d||null==f||null==y)return null;e.dangerouslySetInnerHTML,e.ticks,e.scale;var v=y2(e,yZ);y.id,y.scale;var h=y2(y,yQ);return E.createElement(yv,yJ({},v,h,{ref:i,labelRef:o,x:f.x,y:f.y,tickTextProps:"auto"===n?{width:void 0}:{width:n},width:d.width,height:d.height,className:(0,S.clsx)("recharts-".concat(s," ").concat(s),r),viewBox:l,ticks:p,axisType:s,axisId:t}))}var y5={allowDataOverflow:oz.allowDataOverflow,allowDecimals:oz.allowDecimals,allowDuplicatedCategory:oz.allowDuplicatedCategory,angle:oz.angle,axisLine:yu.axisLine,hide:!1,includeHidden:oz.includeHidden,interval:oz.interval,label:!1,minTickGap:oz.minTickGap,mirror:oz.mirror,orientation:oz.orientation,padding:oz.padding,reversed:oz.reversed,scale:oz.scale,tick:oz.tick,tickCount:oz.tickCount,tickLine:yu.tickLine,tickSize:yu.tickSize,type:oz.type,niceTicks:oz.niceTicks,width:oz.width,yAxisId:0},y8=E.memo(e=>{var t=eS(e,y5);return E.createElement(E.Fragment,null,E.createElement(y3,{interval:t.interval,id:t.yAxisId,scale:t.scale,type:t.type,domain:t.domain,allowDataOverflow:t.allowDataOverflow,dataKey:t.dataKey,allowDuplicatedCategory:t.allowDuplicatedCategory,allowDecimals:t.allowDecimals,tickCount:t.tickCount,padding:t.padding,includeHidden:t.includeHidden,reversed:t.reversed,ticks:t.ticks,width:t.width,orientation:t.orientation,mirror:t.mirror,hide:t.hide,unit:t.unit,name:t.name,angle:t.angle,minTickGap:t.minTickGap,tick:t.tick,tickFormatter:t.tickFormatter,niceTicks:t.niceTicks}),E.createElement(y6,t))},yL);function y4(){return(y4=Object.assign.bind()).apply(null,arguments)}function y9(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function y7(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=e.separator,r=void 0===t?" : ":t,n=e.contentStyle,a=e.itemStyle,i=e.labelStyle,o=e.payload,l=e.formatter,c=e.itemSorter,u=e.wrapperClassName,s=e.labelClassName,d=e.label,f=e.labelFormatter,p=e.accessibilityLayer,y=y7(y7({},vr),n),v=y7({margin:0},void 0===i?va:i),h=null!=d,m=h?d:"",g=(0,S.clsx)("recharts-default-tooltip",u),b=(0,S.clsx)("recharts-tooltip-label",s);return h&&f&&null!=o&&(m=f(d,o)),E.createElement("div",y4({className:g,style:y},void 0!==p&&p?{role:"status","aria-live":"assertive"}:{}),E.createElement("p",{className:b,style:v},E.isValidElement(m)?m:"".concat(m)),(()=>{if(o&&o.length){var e=(null==c?o:nb(o,c)).map((e,t)=>{if(!e||"none"===e.type)return null;var n=e.formatter||l||vt,i=e.value,c=e.name,u=i,s=c,d=n(i,c,e,t,o);if(Array.isArray(d)){var f=function(e){if(Array.isArray(e))return e}(d)||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(d)||function(e){if(e){if("string"==typeof e)return ve(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?ve(e,2):void 0}}(d)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}();u=f[0],s=f[1]}else{if(null==d)return null;u=d}var p=y7(y7({},vn),{},{color:e.color||vn.color},a);return E.createElement("li",{className:"recharts-tooltip-item",key:"tooltip-item-".concat(t),style:p},Y(s)?E.createElement("span",{className:"recharts-tooltip-item-name"},s):null,Y(s)?E.createElement("span",{className:"recharts-tooltip-item-separator"},r):null,E.createElement("span",{className:"recharts-tooltip-item-value"},u),E.createElement("span",{className:"recharts-tooltip-item-unit"},e.unit||""))});return E.createElement("ul",{className:"recharts-tooltip-item-list",style:{padding:0,margin:0}},e)}return null})())},vo="recharts-tooltip-wrapper",vl={visibility:"hidden"};function vc(e){var t=e.allowEscapeViewBox,r=e.coordinate,n=e.key,a=e.offset,i=e.position,o=e.reverseDirection,l=e.tooltipDimension,c=e.viewBox,u=e.viewBoxDimension;if(i&&X(i[n]))return i[n];var s=r[n]-l-(a>0?a:0),d=r[n]+a;if(t[n])return o[n]?s:d;var f=c[n];return null==f?0:o[n]?sf+u?Math.max(s,f):Math.max(d,f)}function vu(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function vs(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r({dismissed:!1,dismissedAtCoordinate:{x:0,y:0}})))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(w)||function(e){if(e){if("string"==typeof e)return vd(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?vd(e,2):void 0}}(w)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),M=D[0],N=D[1];E.useEffect(()=>{var t=t=>{if("Escape"===t.key){var r,n,a,i;N({dismissed:!0,dismissedAtCoordinate:{x:null!=(r=null==(n=e.coordinate)?void 0:n.x)?r:0,y:null!=(a=null==(i=e.coordinate)?void 0:i.y)?a:0}})}};return document.addEventListener("keydown",t),()=>{document.removeEventListener("keydown",t)}},[null==(O=e.coordinate)?void 0:O.x,null==(j=e.coordinate)?void 0:j.y]),M.dismissed&&((null!=(A=null==(P=e.coordinate)?void 0:P.x)?A:0)!==M.dismissedAtCoordinate.x||(null!=(k=null==(I=e.coordinate)?void 0:I.y)?k:0)!==M.dismissedAtCoordinate.y)&&N(vs(vs({},M),{},{dismissed:!1}));var T=(f=(t={allowEscapeViewBox:e.allowEscapeViewBox,coordinate:e.coordinate,offsetLeft:"number"==typeof e.offset?e.offset:e.offset.x,offsetTop:"number"==typeof e.offset?e.offset:e.offset.y,position:e.position,reverseDirection:e.reverseDirection,tooltipBox:{height:e.lastBoundingBox.height,width:e.lastBoundingBox.width},useTranslate3d:e.useTranslate3d,viewBox:e.viewBox}).allowEscapeViewBox,p=t.coordinate,y=t.offsetTop,v=t.offsetLeft,h=t.position,m=t.reverseDirection,g=t.tooltipBox,b=t.useTranslate3d,x=t.viewBox,g.height>0&&g.width>0&&p?(n=(r={translateX:s=vc({allowEscapeViewBox:f,coordinate:p,key:"x",offset:v,position:h,reverseDirection:m,tooltipDimension:g.width,viewBox:x,viewBoxDimension:x.width}),translateY:d=vc({allowEscapeViewBox:f,coordinate:p,key:"y",offset:y,position:h,reverseDirection:m,tooltipDimension:g.height,viewBox:x,viewBoxDimension:x.height}),useTranslate3d:b}).translateX,a=r.translateY,u={transform:r.useTranslate3d?"translate3d(".concat(n,"px, ").concat(a,"px, 0)"):"translate(".concat(n,"px, ").concat(a,"px)")}):u=vl,{cssProperties:u,cssClasses:(o=(i={translateX:s,translateY:d,coordinate:p}).coordinate,l=i.translateX,c=i.translateY,(0,S.clsx)(vo,{["".concat(vo,"-right")]:X(l)&&o&&X(o.x)&&l>=o.x,["".concat(vo,"-left")]:X(l)&&o&&X(o.x)&&l=o.y,["".concat(vo,"-top")]:X(c)&&o&&X(o.y)&&ctypeof SharedArrayBuffer&&e instanceof SharedArrayBuffer)return e.slice(0);if(e instanceof DataView){let t=new DataView(e.buffer.slice(0),e.byteOffset,e.byteLength);return n.set(e,t),vO(t,e,r,n,a),t}if("u">typeof File&&e instanceof File){let t=new File([e],e.name,{type:e.type});return n.set(e,t),vO(t,e,r,n,a),t}if("u">typeof Blob&&e instanceof Blob){let t=new Blob([e],{type:e.type});return n.set(e,t),vO(t,e,r,n,a),t}if(e instanceof Error){let t=structuredClone(e);return n.set(e,t),t.message=e.message,t.name=e.name,t.stack=e.stack,t.cause=e.cause,t.constructor=e.constructor,vO(t,e,r,n,a),t}if(e instanceof Boolean){let t=new Boolean(e.valueOf());return n.set(e,t),vO(t,e,r,n,a),t}if(e instanceof Number){let t=new Number(e.valueOf());return n.set(e,t),vO(t,e,r,n,a),t}if(e instanceof String){let t=new String(e.valueOf());return n.set(e,t),vO(t,e,r,n,a),t}if("object"==typeof e&&function(e){switch(vv(e)){case vb:case"[object Array]":case"[object ArrayBuffer]":case"[object DataView]":case vg:case"[object Date]":case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Map]":case vm:case"[object Object]":case"[object RegExp]":case"[object Set]":case vh:case"[object Symbol]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return!0;default:return!1}}(e)){let t=Object.create(Object.getPrototypeOf(e));return n.set(e,t),vO(t,e,r,n,a),t}return e}function vO(e,t,r=e,n,a){let i=[...Object.keys(t),...Object.getOwnPropertySymbols(t).filter(e=>Object.prototype.propertyIsEnumerable.call(t,e))];for(let o=0;o0)return vj(e,{...t},r,n,a);return nc(e,t);default:if(!ns(e))return nc(e,t);if(a){if("string"==typeof t)return""===t;return!0}return nc(e,t)}}function vA(e,t,r,n){if(0===t.length)return!0;if(!Array.isArray(e))return!1;let a=new Set;for(let i=0;ivoid 0):vj(t,r,function e(t,r,a,i,o,l){let c=n(t,r,a,i,o,l);return void 0!==c?!!c:vj(t,r,e,l,!1)},new Map,!0)}(e,t,()=>void 0)}function vP(e,t=vp){var r;return"object"==typeof e&&null!==e&&nu(e)?function(e,t){let r=new Map;for(let n=0;n{let i;if(void 0!==i)return i;if("object"==typeof r){if("[object Object]"===vv(r)&&"function"!=typeof r.constructor){let e={};return a.set(r,e),vO(e,r,n,a),e}switch(Object.prototype.toString.call(r)){case vm:case vh:case vg:{let e=new r.constructor(r?.valueOf());return vO(e,r),e}case vb:{let e={};return vO(e,r),e.length=r.length,e[Symbol.iterator]=r[Symbol.iterator],e}default:return}}},t=vw(n,void 0,n,new Map,a),function(r){let n=V(r,e);return void 0===n?function(e,t){let r;if(0===(r=Array.isArray(t)?t:"string"==typeof t&&K(t)&&e?.[t]==null?W(t):[t]).length)return!1;let n=e;for(let e=0;evE(e,t);case"string":case"symbol":case"number":return function(t){return V(t,e)}}}(t),function(...e){return r.apply(this,e.slice(0,1))})):[]}function vS(e,t,r){return!0===t?vP(e,r):"function"==typeof t?vP(e,t):e}function vk(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r1||Math.abs(e.left-t.left)>1||Math.abs(e.top-t.top)>1||Math.abs(e.width-t.width)>1}function vC(e){var t=e.getBoundingClientRect();return{height:t.height,left:t.left,top:t.top,width:t.width}}function vD(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],r=function(e){if(Array.isArray(e))return e}(e=(0,E.useState)({height:0,left:0,top:0,width:0}))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(e)||function(e){if(e){if("string"==typeof e)return vk(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?vk(e,2):void 0}}(e)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),n=r[0],a=r[1],i=(0,E.useRef)(null),o=(0,E.useRef)(n);o.current=n;var l=(0,E.useCallback)(e=>{if(null!=i.current&&(i.current.disconnect(),i.current=null),null!=e){var t=vC(e);if(vI(t,o.current)&&a(t),"u">typeof ResizeObserver){var r=new ResizeObserver(()=>{var t=vC(e);vI(t,o.current)&&a(t)});r.observe(e),i.current=r}}},[...t]);return(0,E.useEffect)(()=>()=>{var e;null==(e=i.current)||e.disconnect()},[]),[n,l]}var vM=["x","y","top","left","width","height","className"];function vN(){return(vN=Object.assign.bind()).apply(null,arguments)}function vT(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}var vz=e=>{var t=e.x,r=void 0===t?0:t,n=e.y,a=void 0===n?0:n,i=e.top,o=void 0===i?0:i,l=e.left,c=void 0===l?0:l,u=e.width,s=void 0===u?0:u,d=e.height,f=void 0===d?0:d,p=e.className,y=function(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{var i=$(r),o=$(n),m=Math.min(Math.abs(i)/2,Math.abs(o)/2),g=o>=0?1:-1,b=i>=0?1:-1,x=+(o>=0&&i>=0||o<0&&i<0);if(m>0&&Array.isArray(a)){for(var w=[0,0,0,0],O=0;O<4;O++){var j,A,E=null!=(A=a[O])?A:0;w[O]=E>m?m:E}j=U(l||(l=vV(["M",",",""])),e,t+g*w[0]),w[0]>0&&(j+=U(c||(c=vV(["A ",",",",0,0,",",",",",""])),w[0],w[0],x,e+b*w[0],t)),j+=U(u||(u=vV(["L ",",",""])),e+r-b*w[1],t),w[1]>0&&(j+=U(s||(s=vV(["A ",",",",0,0,",",\n ",",",""])),w[1],w[1],x,e+r,t+g*w[1])),j+=U(d||(d=vV(["L ",",",""])),e+r,t+n-g*w[2]),w[2]>0&&(j+=U(f||(f=vV(["A ",",",",0,0,",",\n ",",",""])),w[2],w[2],x,e+r-b*w[2],t+n)),j+=U(p||(p=vV(["L ",",",""])),e+b*w[3],t+n),w[3]>0&&(j+=U(y||(y=vV(["A ",",",",0,0,",",\n ",",",""])),w[3],w[3],x,e,t+n-g*w[3])),j+="Z"}else if(m>0&&a===+a&&a>0){var P=Math.min(m,a);j=U(v||(v=vV(["M ",",","\n A ",",",",0,0,",",",",","\n L ",",","\n A ",",",",0,0,",",",",","\n L ",",","\n A ",",",",0,0,",",",",","\n L ",",","\n A ",",",",0,0,",",",","," Z"])),e,t+g*P,P,P,x,e+b*P,t,e+r-b*P,t,P,P,x,e+r,t+g*P,e+r,t+n-g*P,P,P,x,e+r-b*P,t+n,e+b*P,t+n,P,P,x,e,t+n-g*P)}else j=U(h||(h=vV(["M ",","," h "," v "," h "," Z"])),e,t,r,n,-r);return j},vU={x:0,y:0,width:0,height:0,radius:0,isAnimationActive:!1,isUpdateAnimationActive:!1,animationBegin:0,animationDuration:1500,animationEasing:"ease"},vH=e=>{let t,r;var n,a=eS(e,vU),i=(0,E.useRef)(null),o=function(e){if(Array.isArray(e))return e}(n=(0,E.useState)(-1))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(n)||function(e){if(e){if("string"==typeof e)return vW(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?vW(e,2):void 0}}(n)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),l=o[0],c=o[1];(0,E.useEffect)(()=>{if(i.current&&i.current.getTotalLength)try{var e=i.current.getTotalLength();e&&c(e)}catch(e){}},[]);var u=a.x,s=a.y,d=a.width,f=a.height,p=a.radius,y=a.className,v=a.animationEasing,h=a.animationDuration,m=a.animationBegin,g=a.isAnimationActive,b=a.isUpdateAnimationActive,x=(0,E.useRef)(d),w=(0,E.useRef)(f),O=(0,E.useRef)(u),j=(0,E.useRef)(s),A=s$((0,E.useMemo)(()=>({x:u,y:s,width:d,height:f,radius:p}),[u,s,d,f,p]),"rectangle-");if(u!==+u||s!==+s||d!==+d||f!==+f||0===d||0===f)return null;var P=(0,S.clsx)("recharts-rectangle",y);if(!b){var k=z(a),I=(k.radius,vF(k,v_));return E.createElement("path",vK({},I,{x:$(u),y:$(s),width:$(d),height:$(f),radius:"number"==typeof p?p:void 0,className:P,d:v$(u,s,d,f,p)}))}var C=x.current,D=w.current,M=O.current,N=j.current,T="0px ".concat(-1===l?1:l,"px"),_="".concat(l,"px ").concat(l,"px"),R=(t=["strokeDasharray"],r="string"==typeof v?v:vU.animationEasing,t.map(e=>"".concat(e.replace(/([A-Z])/g,e=>"-".concat(e.toLowerCase()))," ").concat(h,"ms ").concat(r)).join(","));return E.createElement(sV,{animationId:A,key:A,canBegin:l>0,duration:h,easing:v,isActive:b,begin:m},e=>{var t,r=et(C,d,e),n=et(D,f,e),o=et(M,u,e),l=et(N,s,e);i.current&&(x.current=r,w.current=n,O.current=o,j.current=l),t=g?e>0?{transition:R,strokeDasharray:_}:{strokeDasharray:T}:{strokeDasharray:_};var c=z(a),y=(c.radius,vF(c,vR));return E.createElement("path",vK({},y,{radius:"number"==typeof p?p:void 0,className:P,d:v$(o,l,r,n,p),ref:i,style:vB(vB({},t),a.style)}))})};function vG(e){var t=e.cx,r=e.cy,n=e.radius,a=e.startAngle,i=e.endAngle;return{points:[eX(t,r,n,a),eX(t,r,n,i)],cx:t,cy:r,radius:n,startAngle:a,endAngle:i}}function vq(){return(vq=Object.assign.bind()).apply(null,arguments)}function vX(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}var vY=e=>{var t=e.cx,r=e.cy,n=e.radius,a=e.angle,i=e.sign,o=e.isExternal,l=e.cornerRadius,c=e.cornerIsExternal,u=l*(o?1:-1)+n,s=Math.asin(l/u)/eq,d=c?a:a+i*s,f=eX(t,r,u,d);return{center:f,circleTangency:eX(t,r,n,d),lineTangency:eX(t,r,u*Math.cos(s*eq),c?a-i*s:a),theta:s}},vZ=e=>{var t=e.cx,r=e.cy,n=e.innerRadius,a=e.outerRadius,i=e.startAngle,o=e.endAngle,l=H(o-i)*Math.min(Math.abs(o-i),359.999),c=i+l,u=eX(t,r,a,i),s=eX(t,r,a,c),d=U(m||(m=vX(["M ",",","\n A ",",",",0,\n ",",",",\n ",",","\n "])),u.x,u.y,a,a,+(Math.abs(l)>180),+(i>c),s.x,s.y);if(n>0){var f=eX(t,r,n,i),p=eX(t,r,n,c);d+=U(g||(g=vX(["L ",",","\n A ",",",",0,\n ",",",",\n ",","," Z"])),p.x,p.y,n,n,+(Math.abs(l)>180),+(i<=c),f.x,f.y)}else d+=U(b||(b=vX(["L ",","," Z"])),t,r);return d},vQ={cx:0,cy:0,innerRadius:0,outerRadius:0,startAngle:0,endAngle:0,cornerRadius:0,forceCornerRadius:!1,cornerIsExternal:!1},vJ=e=>{var t,r=eS(e,vQ),n=r.cx,a=r.cy,i=r.innerRadius,o=r.outerRadius,l=r.cornerRadius,c=r.forceCornerRadius,u=r.cornerIsExternal,s=r.startAngle,d=r.endAngle,f=r.className;if(o0&&360>Math.abs(s-d)?(e=>{var t=e.cx,r=e.cy,n=e.innerRadius,a=e.outerRadius,i=e.cornerRadius,o=e.forceCornerRadius,l=e.cornerIsExternal,c=e.startAngle,u=e.endAngle,s=H(u-c),d=vY({cx:t,cy:r,radius:a,angle:c,sign:s,cornerRadius:i,cornerIsExternal:l}),f=d.circleTangency,p=d.lineTangency,y=d.theta,v=vY({cx:t,cy:r,radius:a,angle:u,sign:-s,cornerRadius:i,cornerIsExternal:l}),h=v.circleTangency,m=v.lineTangency,g=v.theta,b=l?Math.abs(c-u):Math.abs(c-u)-y-g;if(b<0)return o?U(x||(x=vX(["M ",",","\n a",",",",0,0,1,",",0\n a",",",",0,0,1,",",0\n "])),p.x,p.y,i,i,2*i,i,i,-(2*i)):vZ({cx:t,cy:r,innerRadius:n,outerRadius:a,startAngle:c,endAngle:u});var A=U(w||(w=vX(["M ",",","\n A",",",",0,0,",",",",","\n A",",",",0,",",",",",",","\n A",",",",0,0,",",",",","\n "])),p.x,p.y,i,i,+(s<0),f.x,f.y,a,a,+(b>180),+(s<0),h.x,h.y,i,i,+(s<0),m.x,m.y);if(n>0){var E=vY({cx:t,cy:r,radius:n,angle:c,sign:s,isExternal:!0,cornerRadius:i,cornerIsExternal:l}),P=E.circleTangency,S=E.lineTangency,k=E.theta,I=vY({cx:t,cy:r,radius:n,angle:u,sign:-s,isExternal:!0,cornerRadius:i,cornerIsExternal:l}),C=I.circleTangency,D=I.lineTangency,M=I.theta,N=l?Math.abs(c-u):Math.abs(c-u)-k-M;if(N<0&&0===i)return"".concat(A,"L").concat(t,",").concat(r,"Z");A+=U(O||(O=vX(["L",",","\n A",",",",0,0,",",",",","\n A",",",",0,",",",",",",","\n A",",",",0,0,",",",",","Z"])),D.x,D.y,i,i,+(s<0),C.x,C.y,n,n,+(N>180),+(s>0),P.x,P.y,i,i,+(s<0),S.x,S.y)}else A+=U(j||(j=vX(["L",",","Z"])),t,r);return A})({cx:n,cy:a,innerRadius:i,outerRadius:o,cornerRadius:Math.min(v,y/2),forceCornerRadius:c,cornerIsExternal:u,startAngle:s,endAngle:d}):vZ({cx:n,cy:a,innerRadius:i,outerRadius:o,startAngle:s,endAngle:d}),E.createElement("path",vq({},z(r),{className:p,d:t}))};function v0(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function v1(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=e.type,r=void 0===t?"circle":t,n=e.size,a=void 0===n?64:n,i=e.sizeType,o=void 0===i?"area":i,l=hv(hv({},function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{var e,t=(e=c,hh["symbol".concat(en(e))]||hi.symbolCircle),r=(0,ha.symbol)().type(t).size(((e,t,r)=>{if("area"===t)return e;switch(r){case"cross":return 5*e*e/9;case"diamond":return .5*e*e/Math.sqrt(3);case"square":return e*e;case"star":var n=18*hm;return 1.25*e*e*(Math.tan(n)-Math.tan(2*n)*Math.tan(n)**2);case"triangle":return Math.sqrt(3)*e*e/4;case"wye":return(21-10*Math.sqrt(3))*e*e/8;default:return Math.PI*e*e/4}})(a,o,c))();if(null!==r)return r})()})):null};function hb(){return(hb=Object.assign.bind()).apply(null,arguments)}function hx(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function hw(e){for(var t=1;t{hh["symbol".concat(en(e))]=t};var hO={align:"center",iconSize:14,inactiveColor:"#ccc",layout:"horizontal",verticalAlign:"middle",labelStyle:{}};function hj(e){var t=e.data,r=e.iconType,n=e.inactiveColor,a=32/6,i=32/3,o=t.inactive?n:t.color,l=null!=r?r:t.type;if("none"===l)return null;if("plainline"===l)return E.createElement("line",{strokeWidth:4,fill:"none",stroke:o,strokeDasharray:function(e){if("object"==typeof e&&null!==e&&"strokeDasharray"in e)return String(e.strokeDasharray)}(t.payload),x1:0,y1:16,x2:32,y2:16,className:"recharts-legend-icon"});if("line"===l)return E.createElement("path",{strokeWidth:4,fill:"none",stroke:o,d:"M0,".concat(16,"h").concat(i,"\n A").concat(a,",").concat(a,",0,1,1,").concat(2*i,",").concat(16,"\n H").concat(32,"M").concat(2*i,",").concat(16,"\n A").concat(a,",").concat(a,",0,1,1,").concat(i,",").concat(16),className:"recharts-legend-icon"});if("rect"===l)return E.createElement("path",{stroke:"none",fill:o,d:"M0,".concat(4,"h").concat(32,"v").concat(24,"h").concat(-32,"z"),className:"recharts-legend-icon"});if(E.isValidElement(t.legendIcon)){var c=hw({},t);return delete c.legendIcon,E.cloneElement(t.legendIcon,c)}return E.createElement(hg,{fill:o,cx:16,cy:16,size:32,sizeType:"diameter",type:l})}function hA(e){var t=e.payload,r=e.iconSize,n=e.layout,a=e.formatter,i=e.inactiveColor,o=e.iconType,l=e.labelStyle,c={x:0,y:0,width:32,height:32},u={display:"horizontal"===n?"inline-block":"block",marginRight:10},s={display:"inline-block",verticalAlign:"middle",marginRight:4};return t.map((t,n)=>{var d=t.formatter||a,f=(0,S.clsx)({"recharts-legend-item":!0,["legend-item-".concat(n)]:!0,inactive:t.inactive});if("none"===t.type)return null;var p="object"==typeof l?hw({},l):{};p.color=t.inactive?i:p.color||t.color;var y=d?d(t.value,t,n):t.value;return E.createElement("li",hb({className:f,style:u,key:"legend-item-".concat(n)},iP(e,t,n)),E.createElement(pb,{width:r,height:r,viewBox:c,style:s,"aria-label":null==t.value?"legend icon":"".concat(t.value," legend icon")},E.createElement(hj,{data:t,iconType:o,inactiveColor:i})),E.createElement("span",{className:"recharts-legend-item-text",style:p},y))})}var hE=e=>{var t=eS(e,hO),r=t.payload,n=t.layout,a=t.align;return r&&r.length?E.createElement("ul",{className:"recharts-default-legend",style:{padding:0,margin:0,textAlign:"horizontal"===n?a:"left"}},E.createElement(hA,hb({},t,{payload:r}))):null},hP=["contextPayload"];function hS(){return(hS=Object.assign.bind()).apply(null,arguments)}function hk(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{i(sg({align:t,layout:r,verticalAlign:n,itemSorter:a}))},[i,t,r,n,a]),null}function hT(e){var t=e.width,r=e.height,n=e0();return(0,E.useLayoutEffect)(()=>{n(sm({width:t,height:r}))},[n,t,r]),(0,E.useLayoutEffect)(()=>()=>{n(sm({width:0,height:0}))},[n]),null}var hz={align:"center",iconSize:14,inactiveColor:"#ccc",itemSorter:"value",labelStyle:{},layout:"horizontal",verticalAlign:"bottom"},h_=E.memo(function(e){var t,r,n,a,i,o,l,c=eS(e,hz),u=e6(nw),s=(0,E.useContext)(pR),d=e6(e=>e.layout.margin),f=c.width,p=c.height,y=c.wrapperStyle,v=c.portal,h=function(e){if(Array.isArray(e))return e}(t=vD([u]))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(t)||function(e){if(e){if("string"==typeof e)return hk(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?hk(e,2):void 0}}(t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),m=h[0],g=h[1],b=e6(n$),x=e6(nU);if(null==b||null==x)return null;var w=b-((null==d?void 0:d.left)||0)-((null==d?void 0:d.right)||0),O=(r=c.layout,"vertical"===r&&null!=p?{height:p}:"horizontal"===r?{width:f||w}:null),j=v?y:hC(hC({position:"absolute",width:(null==O?void 0:O.width)||f||"auto",height:(null==O?void 0:O.height)||p||"auto"},(i=c.layout,o=c.align,l=c.verticalAlign,y&&(void 0!==y.left&&null!==y.left||void 0!==y.right&&null!==y.right)||(n="center"===o&&"vertical"===i?{left:((b||0)-m.width)/2}:"right"===o?{right:d&&d.right||0}:{left:d&&d.left||0}),y&&(void 0!==y.top&&null!==y.top||void 0!==y.bottom&&null!==y.bottom)||(a="middle"===l?{top:((x||0)-m.height)/2}:"bottom"===l?{bottom:d&&d.bottom||0}:{top:d&&d.top||0}),hC(hC({},n),a))),y),A=null!=v?v:s;if(null==A||null==u)return null;var P=E.createElement("div",{className:"recharts-legend-wrapper",style:j,ref:g},E.createElement(hN,{layout:c.layout,align:c.align,verticalAlign:c.verticalAlign,itemSorter:c.itemSorter}),!v&&E.createElement(hT,{width:m.width,height:m.height}),E.createElement(hM,hS({},c,O,{margin:d,chartWidth:b,chartHeight:x,contextPayload:u})));return(0,aW.createPortal)(P,A)},dw);h_.displayName="Legend";var hR=e.i(115504);let hL={light:"",dark:".dark"},hB={width:320,height:200},hK=E.createContext(null);function hF(){let e=E.useContext(hK);if(!e)throw Error("useChart must be used within a ");return e}let hW=E.forwardRef(({id:e,className:t,children:r,config:n,initialDimension:a=hB,...i},o)=>{let l=E.useId(),c=`chart-${e??l.replace(/:/g,"")}`;return(0,A.jsx)(hK.Provider,{value:{config:n},children:(0,A.jsxs)("div",{ref:o,"data-slot":"chart","data-chart":c,className:(0,hR.cn)("flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",t),...i,children:[(0,A.jsx)(hV,{id:c,config:n}),(0,A.jsx)(av,{initialDimension:a,children:r})]})})});hW.displayName="ChartContainer";let hV=({id:e,config:t})=>{let r=Object.entries(t).filter(([,e])=>e.theme??e.color);return r.length?(0,A.jsx)("style",{dangerouslySetInnerHTML:{__html:Object.entries(hL).map(([t,n])=>` +${n} [data-chart=${e}] { +${r.map(([e,r])=>{let n=r.theme?.[t]??r.color;return n?` --color-${e.replace(/[^a-zA-Z0-9_-]/g,"_")}: ${n.replace(/[;{}<>]/g,"")};`:null}).join("\n")} +} +`).join("\n")}}):null},h$=function(e){var t,r,n,a,i,o,l,c,u,s,d,f=eS(e,hn),p=f.active,y=f.allowEscapeViewBox,v=f.animationDuration,h=f.animationEasing,m=f.content,g=f.filterNull,b=f.isAnimationActive,x=f.offset,w=f.payloadUniqBy,O=f.position,j=f.reverseDirection,A=f.useTranslate3d,P=f.wrapperStyle,S=f.cursor,k=f.shared,I=f.trigger,C=f.defaultIndex,D=f.portal,M=f.axisId,N=e0(),T="number"==typeof C?String(C):C;(0,E.useEffect)(()=>{N(cA({shared:k,trigger:I,axisId:M,active:p,defaultIndex:T}))},[N,k,I,M,p,T]);var z=am(),_=ph(),R=e6(e=>ch(e,k)),L=null!=(s=e6(e=>sy(e,R,I,T)))?s:{},B=L.activeIndex,K=L.isActive,F=e6(e=>sp(e,R,I,T)),W=e6(e=>sf(e,R,I,T)),V=e6(e=>sd(e,R,I,T)),$=(0,E.useContext)(p_),U=null!=(d=null!=p?p:K)&&d,H=function(e){if(Array.isArray(e))return e}(t=vD([F,U]))||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(t)||function(e){if(e){if("string"==typeof e)return he(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?he(e,2):void 0}}(t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),G=H[0],q=H[1],X="axis"===R?W:void 0;r=e6(e=>((e,t,r)=>{if(null!=t){var n=cF(e);return"axis"===t?"hover"===r?n.axisInteraction.hover.dataKey:n.axisInteraction.click.dataKey:"hover"===r?n.itemInteraction.hover.dataKey:n.itemInteraction.click.dataKey}})(e,R,I)),n=e6(uE),a=e6(os),i=e6(oc),o=e6(ou),c=(null==(l=e6(pD))?void 0:l.sourceViewBox)!=null,u=am(),(0,E.useEffect)(()=>{if(!c&&null!=i&&null!=a){var e=cD({active:U,coordinate:V,dataKey:r,index:B,label:"number"==typeof X?String(X):X,sourceViewBox:u,graphicalItemId:n});pk.emit(pI,i,e,a)}},[c,V,r,n,B,X,a,i,o,U,u]);var Y=null!=D?D:$;if(null==Y||null==z||null==R)return null;var Z=null!=F?F:hr;U||(Z=hr),g&&Z.length&&(Z=vS(Z.filter(e=>null!=e.value&&(!0!==e.hide||f.includeHidden)),w,ht));var Q=Z.length>0,J=v7(v7({},f),{},{payload:Z,label:X,active:U,activeIndex:B,coordinate:V,accessibilityLayer:_}),ee=E.createElement(vf,{allowEscapeViewBox:y,animationDuration:v,animationEasing:h,isAnimationActive:b,active:U,coordinate:V,hasPayload:Q,offset:x,position:O,reverseDirection:j,useTranslate3d:A,viewBox:z,wrapperStyle:P,lastBoundingBox:G,innerRef:q,hasPortalFromProps:!!D},E.isValidElement(m)?E.cloneElement(m,J):"function"==typeof m?E.createElement(m,J):E.createElement(vi,J));return E.createElement(E.Fragment,null,(0,aW.createPortal)(ee,Y),U&&E.createElement(v4,{cursor:S,tooltipEventType:R,coordinate:V,payload:Z,index:B}))};E.forwardRef(({active:e,payload:t,className:r,indicator:n="dot",hideLabel:a=!1,hideIndicator:i=!1,label:o,labelFormatter:l,labelClassName:c,formatter:u,color:s,nameKey:d,labelKey:f},p)=>{let{config:y}=hF(),v=E.useMemo(()=>{if(a||!t?.length)return null;let[e]=t,r=`${f??e?.dataKey??e?.name??"value"}`,n=hH(y,e,r),i=f||"string"!=typeof o?n?.label:y[o]?.label??o;return l?(0,A.jsx)("div",{className:(0,hR.cn)("font-medium",c),children:l(i,t)}):i?(0,A.jsx)("div",{className:(0,hR.cn)("font-medium",c),children:i}):null},[o,l,t,a,c,y,f]);if(!e||!t?.length)return null;let h=1===t.length&&"dot"!==n;return(0,A.jsxs)("div",{ref:p,className:(0,hR.cn)("grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",r),children:[h?null:v,(0,A.jsx)("div",{className:"grid gap-1.5",children:t.filter(e=>"none"!==e.type).map((e,t)=>{let r=`${d??e.name??e.dataKey??"value"}`,a=hH(y,e,r),o=s??e.payload?.fill??e.color;return(0,A.jsx)("div",{className:(0,hR.cn)("flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground","dot"===n&&"items-center"),children:u&&e?.value!==void 0&&e.name?u(e.value,e.name,e,t,e.payload):(0,A.jsxs)(A.Fragment,{children:[a?.icon?(0,A.jsx)(a.icon,{}):!i&&(0,A.jsx)("div",{className:(0,hR.cn)("shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",{"h-2.5 w-2.5":"dot"===n,"w-1":"line"===n,"w-0 border-[1.5px] border-dashed bg-transparent":"dashed"===n,"my-0.5":h&&"dashed"===n}),style:{"--color-bg":o,"--color-border":o}}),(0,A.jsxs)("div",{className:(0,hR.cn)("flex flex-1 justify-between leading-none",h?"items-end":"items-center"),children:[(0,A.jsxs)("div",{className:"grid gap-1.5",children:[h?v:null,(0,A.jsx)("span",{className:"text-muted-foreground",children:a?.label??e.name})]}),null!=e.value&&(0,A.jsx)("span",{className:"font-mono font-medium text-foreground tabular-nums",children:"number"==typeof e.value?e.value.toLocaleString():String(e.value)})]})]})},t)})})]})}).displayName="ChartTooltipContent";let hU=E.forwardRef(({className:e,hideIcon:t=!1,payload:r,verticalAlign:n="bottom",nameKey:a},i)=>{let{config:o}=hF();return r?.length?(0,A.jsx)("div",{ref:i,className:(0,hR.cn)("flex items-center justify-center gap-4","top"===n?"pb-3":"pt-3",e),children:r.filter(e=>"none"!==e.type).map((e,r)=>{let n=`${a??e.dataKey??"value"}`,i=hH(o,e,n);return(0,A.jsxs)("div",{className:(0,hR.cn)("flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"),children:[i?.icon&&!t?(0,A.jsx)(i.icon,{}):(0,A.jsx)("div",{className:"h-2 w-2 shrink-0 rounded-[2px]",style:{backgroundColor:e.color}}),i?.label]},r)})}):null});function hH(e,t,r){if("object"!=typeof t||null===t)return;let n="payload"in t&&"object"==typeof t.payload&&null!==t.payload?t.payload:void 0,a=r;return r in t&&"string"==typeof t[r]?a=t[r]:n&&r in n&&"string"==typeof n[r]&&(a=n[r]),a in e?e[a]:e[r]}hU.displayName="ChartLegendContent";let hG=e=>e.replace("metrics.","").replace(/_/g," ").split(" ").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" "),hq=({active:e,payload:t,label:r,valueFormatter:n})=>e&&t&&0!==t.length?(0,A.jsxs)("div",{className:"min-w-32 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",children:[null!=r&&(0,A.jsx)("p",{className:"mb-1.5 font-medium text-foreground",children:String(r)}),(0,A.jsx)("div",{className:"grid gap-1.5",children:t.map((e,t)=>{var r;return(0,A.jsxs)("div",{className:"flex w-full items-center justify-between gap-4",children:[(0,A.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,A.jsx)("span",{className:"h-2.5 w-2.5 shrink-0 rounded-[2px]",style:{backgroundColor:e.color}}),(0,A.jsx)("span",{className:"text-muted-foreground",children:String(e.name??e.dataKey??"")})]}),(0,A.jsx)("span",{className:"font-mono font-medium tabular-nums text-foreground",children:"number"==typeof(r=e.value)?n?n(r):r.toLocaleString():null==r?"":String(r)})]},String(e.dataKey??e.name??t))})})]}):null;e.s(["CustomTooltip",0,({active:e,payload:t,label:r})=>e&&t&&0!==t.length?(0,A.jsxs)("div",{className:"w-56 rounded-lg border border-border/50 bg-background p-2 text-xs shadow-xl",children:[(0,A.jsx)("p",{className:"font-medium text-foreground",children:null==r?"":String(r)}),t.map(e=>{var t,r;let n=e.dataKey?.toString();if(!n||!e.payload)return null;let a=(t=((e,t)=>{if("object"!=typeof e||null===e||!("metrics"in e))return;let r=e.metrics;if("object"!=typeof r||null===r)return;let n=r[t.substring(t.indexOf(".")+1)];return"number"==typeof n?n:void 0})(e.payload,n),r=n.includes("spend"),void 0===t?"N/A":r?`$${t.toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:2})}`:t.toLocaleString());return(0,A.jsxs)("div",{className:"flex items-center justify-between space-x-4",children:[(0,A.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,A.jsx)("span",{className:"h-2 w-2 shrink-0 rounded-full ring-2 ring-white drop-shadow-md",style:{backgroundColor:e.color}}),(0,A.jsx)("p",{className:"font-medium text-muted-foreground",children:hG(n)})]}),(0,A.jsx)("p",{className:"font-medium text-foreground",children:a})]},n)})]}):null,"ValueTooltip",0,hq,"formatCategoryName",0,hG],378044);let hX={slate:"#64748b",gray:"#6b7280",zinc:"#71717a",neutral:"#737373",stone:"#78716c",red:"#ef4444",orange:"#f97316",amber:"#f59e0b",yellow:"#eab308",lime:"#84cc16",green:"#22c55e",emerald:"#10b981",teal:"#14b8a6",cyan:"#06b6d4",sky:"#0ea5e9",blue:"#3b82f6",indigo:"#6366f1",violet:"#8b5cf6",purple:"#a855f7",fuchsia:"#d946ef",pink:"#ec4899",rose:"#f43f5e"},hY=["blue","cyan","sky","indigo","violet","purple","fuchsia","slate","gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","pink","rose"],hZ=e=>`var(--color-${e}-500, ${hX[e]})`,hQ=(e,t)=>{let r=t&&t.length>0?t:hY;return Array.from({length:e},(e,t)=>hZ(r[t%r.length]))};e.s(["AreaChart",0,function({data:e,index:t,categories:r,colors:n,valueFormatter:a,yAxisWidth:i=56,showLegend:o=!0,showGridLines:l=!0,showTooltip:c=!0,customTooltip:u,className:s,style:d}){let f=E.useId().replace(/:/g,"");if(0===e.length)return(0,A.jsx)("div",{className:(0,hR.cn)("flex h-80 w-full items-center justify-center rounded-lg border border-dashed",s),style:d,children:(0,A.jsx)("p",{className:"text-sm text-muted-foreground",children:"No data"})});let p=hQ(r.length,n),y=Object.fromEntries(r.map(e=>[e,{label:e}])),v=u??hq;return(0,A.jsx)(hW,{config:y,className:(0,hR.cn)("aspect-auto h-80 w-full",s),style:d,children:(0,A.jsxs)(p5,{data:[...e],children:[(0,A.jsx)("defs",{children:r.map((e,t)=>(0,A.jsxs)("linearGradient",{id:`fill-${f}-${t}`,x1:"0",y1:"0",x2:"0",y2:"1",children:[(0,A.jsx)("stop",{offset:"5%",stopColor:p[t],stopOpacity:.4}),(0,A.jsx)("stop",{offset:"95%",stopColor:p[t],stopOpacity:0})]},e))}),l&&(0,A.jsx)(yN,{vertical:!1}),(0,A.jsx)(yX,{dataKey:t,tickLine:!1,axisLine:!1,minTickGap:5,interval:"equidistantPreserveStart"}),(0,A.jsx)(y8,{width:i,tickLine:!1,axisLine:!1,tickFormatter:a}),c&&(0,A.jsx)(h$,{content:({active:e,payload:t,label:r})=>(0,A.jsx)(v,{active:e,payload:t,label:r,...u?{}:{valueFormatter:a}})}),o&&(0,A.jsx)(h_,{verticalAlign:"top",content:(0,A.jsx)(hU,{className:"justify-end text-muted-foreground"})}),r.map((e,t)=>(0,A.jsx)(fi,{type:"linear",dataKey:e,stroke:p[t],strokeWidth:2,fill:`url(#fill-${f}-${t})`,fillOpacity:1,dot:!1,isAnimationActive:!1},e))]})})}],591025);var hJ=E,h0=e=>null;h0.displayName="Cell";var h1=e.i(40992),h2=["option"];function h3(e){var t=e.option,r=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n1&&void 0!==arguments[1]?arguments[1]:0;return(r,n)=>{if(X(e))return e;var a=X(r)||null==r;return a?e(r,n):(a||(0,h1.default)(!1,"minPointSize callback function received a value with type of ".concat(typeof r,". Currently only numbers or null/undefined are supported.")),t)}},h5=(e,t,r)=>{var n=e0();return(a,i)=>o=>{null==e||e(a,i,o),n(cE({activeIndex:String(i),activeDataKey:t,activeCoordinate:a.tooltipPosition,activeGraphicalItemId:r}))}},h8=e=>{var t=e0();return(r,n)=>a=>{null==e||e(r,n,a),t(cP())}},h4=(e,t,r)=>{var n=e0();return(a,i)=>o=>{null==e||e(a,i,o),n(ck({activeIndex:String(i),activeDataKey:t,activeCoordinate:a.tooltipPosition,activeGraphicalItemId:r}))}},h9=["children"],h7=(0,E.createContext)({data:[],xAxisId:"xAxis-0",yAxisId:"yAxis-0",dataPointFormatter:()=>({x:0,y:0,value:0}),errorBarOffset:0});function me(e){var t=e.children,r=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;ne.length)&&(t=e.length);for(var r=0,n=Array(t);r{var n=null!=r?r:e;if(null!=n)return J(n,t,0)};function mn(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function ma(e){for(var t=1;tt],(e,t)=>e.filter(e=>"bar"===e.type).find(e=>e.id===t)),mo=rc([mi],e=>null==e?void 0:e.maxBarSize),ml=rc([ax,o$,uJ,u0,(e,t,r)=>r],(e,t,r,n,a)=>t.filter(t=>"horizontal"===e?t.xAxisId===r:t.yAxisId===n).filter(e=>e.isPanorama===a).filter(e=>!1===e.hide).filter(e=>"bar"===e.type)),mc=rc([ml,e=>e.rootProps.barSize,(e,t)=>{var r=ax(e),n=uJ(e,t),a=u0(e,t);if(null!=n&&null!=a)return"horizontal"===r?cn(e,"xAxis",n):cn(e,"yAxis",a)}],(e,t,r)=>{var n=e.filter(ov),a=e.filter(e=>null==e.stackId);return[...Object.entries(n.reduce((e,t)=>{var r=e[t.stackId];return null==r&&(r=[]),r.push(t),e[t.stackId]=r,e},{})).map(e=>{var n,a=function(e){if(Array.isArray(e))return e}(e)||function(e){var t=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var r,n,a,i,o=[],l=!0,c=!1;try{a=(t=t.call(e)).next,!1;for(;!(l=(r=a.call(t)).done)&&(o.push(r.value),2!==o.length);l=!0);}catch(e){c=!0,n=e}finally{try{if(!l&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw n}}return o}}(e)||function(e){if(e){if("string"==typeof e)return mt(e,2);var t=({}).toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?mt(e,2):void 0}}(e)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),i=a[0],o=a[1];return{stackId:i,dataKeys:o.map(e=>e.dataKey),barSize:mr(t,r,null==(n=o[0])?void 0:n.barSize)}}),...a.map(e=>({stackId:void 0,dataKeys:[e.dataKey].filter(e=>null!=e),barSize:mr(t,r,e.barSize)}))]}),mu=(e,t,r)=>{var n,a,i=ax(e),o=uJ(e,t),l=u0(e,t);if(null!=o&&null!=l)return"horizontal"===i?(n=cu(e,"xAxis",o,r),a=cc(e,"xAxis",o,r)):(n=cu(e,"yAxis",l,r),a=cc(e,"yAxis",l,r)),nF(n,a)},ms=rc([mc,on,e=>e.rootProps.barGap,oa,(e,t,r)=>{var n,a,i,o,l=mi(e,t);if(null==l)return 0;var c=uJ(e,t),u=u0(e,t);if(null==c||null==u)return 0;var s=ax(e),d=on(e),f=l.maxBarSize;return"horizontal"===s?(i=cu(e,"xAxis",c,r),o=cc(e,"xAxis",c,r)):(i=cu(e,"yAxis",u,r),o=cc(e,"yAxis",u,r)),null!=(n=null!=(a=nF(i,o,!0))?a:null==f?d:f)?n:0},mu,mo],(e,t,r,n,a,i,o)=>{var l=function(e,t,r,n,a){var i,o,l=n.length;if(!(l<1)){var c=J(e,r,0,!0),u=[];if(ek(null==(i=n[0])?void 0:i.barSize)){var s=!1,d=r/l,f=n.reduce((e,t)=>e+(t.barSize||0),0);(f+=(l-1)*c)>=r&&(f-=(l-1)*c,c=0),f>=r&&d>0&&(s=!0,d*=.9,f=l*d);var p={offset:Math.round((r-f)/2)-c,size:0};o=n.reduce((e,t)=>{var r,n={stackId:t.stackId,dataKeys:t.dataKeys,position:{offset:p.offset+p.size+c,size:s?d:null!=(r=t.barSize)?r:0}},a=[...e,n];return p=n.position,a},u)}else{var y=J(t,r,0,!0);r-2*y-(l-1)*c<=0&&(c=0);var v=(r-2*y-(l-1)*c)/l;v>1&&(v=Math.round(v));var h=ek(a)?Math.min(v,a):v;o=n.reduce((e,t,r)=>[...e,{stackId:t.stackId,dataKeys:t.dataKeys,position:{offset:y+(v+c)*r+(v-h)/2,size:h}}],u)}return o}}(r,n,a!==i?a:i,e,null==o?t:o);return a!==i&&null!=l&&(l=l.map(e=>ma(ma({},e),{},{position:ma(ma({},e.position),{},{offset:e.position.offset-a/2})}))),l}),md=rc([ms,mi],(e,t)=>{if(null!=e&&null!=t){var r=e.find(e=>e.stackId===t.stackId&&null!=t.dataKey&&e.dataKeys.includes(t.dataKey));if(null!=r)return r.position}}),mf=rc([(e,t,r)=>{var n=ax(e),a=uJ(e,t),i=u0(e,t);if(null!=a&&null!=i)return"horizontal"===n?li(e,"yAxis",i,r):li(e,"xAxis",a,r)},mi],(e,t)=>{var r=op(t);if(!e||null==r||null==t)return;var n=t.stackId;if(null!=n){var a=e[n];if(a){var i=a.stackedData;if(i)return i.find(e=>e.key===r)}}}),mp=rc([n0,n2,(e,t,r)=>{var n=uJ(e,t);if(null!=n)return cu(e,"xAxis",n,r)},(e,t,r)=>{var n=u0(e,t);if(null!=n)return cu(e,"yAxis",n,r)},(e,t,r)=>{var n=uJ(e,t);if(null!=n)return cc(e,"xAxis",n,r)},(e,t,r)=>{var n=u0(e,t);if(null!=n)return cc(e,"yAxis",n,r)},md,ax,iG,mu,mf,mi,(e,t,r,n)=>n],(e,t,r,n,a,i,o,l,c,u,s,d,f)=>{var p,y=c.chartData,v=c.dataStartIndex,h=c.dataEndIndex;if(null!=d&&null!=o&&null!=t&&("horizontal"===l||"vertical"===l)&&null!=r&&null!=n&&null!=a&&null!=i&&null!=u){var m,g,b,x,w,O,j,A,E,P,S,k,I,C,D,M,N,T,z,_,R,L,B=d.data;if(null!=(p=null!=B&&B.length>0?B:null==y?void 0:y.slice(v,h+1))){return g=(m={layout:l,barSettings:d,pos:o,parentViewBox:t,bandSize:u,xAxis:r,yAxis:n,xAxisTicks:a,yAxisTicks:i,stackedData:s,displayedData:p,offset:e,cells:f,dataStartIndex:v}).layout,x=(b=m.barSettings).dataKey,w=b.minPointSize,O=b.hasCustomShape,j=m.pos,A=m.bandSize,E=m.xAxis,P=m.yAxis,S=m.xAxisTicks,k=m.yAxisTicks,I=m.stackedData,C=m.displayedData,D=m.offset,M=m.cells,N=m.parentViewBox,T=m.dataStartIndex,z="horizontal"===g?P:E,_=I?z.scale.domain():null,R=(e=>{var t=e.numericAxis,r=t.scale.domain();if("number"===t.type){var n=Math.min(r[0],r[1]),a=Math.max(r[0],r[1]);return n<=0&&a>=0?0:a<0?a:n}return r[0]})({numericAxis:z}),L=z.scale.map(R),C.map((e,t)=>{if(I){var r=I[t+T];if(null==r)return null;a=((e,t)=>{if(!t||2!==t.length||!X(t[0])||!X(t[1]))return e;var r=Math.min(t[0],t[1]),n=Math.max(t[0],t[1]),a=[e[0],e[1]];return(!X(e[0])||e[0]n)&&(a[1]=n),a[0]>n&&(a[0]=n),a[1]0&&Math.abs(c)0&&Math.abs(l)t,mv=(e,t,r)=>r,mh=rc([my,o$,mv],(e,t,r)=>t.filter(e=>"bar"===e.type).filter(t=>t.stackId===e).filter(e=>e.isPanorama===r).filter(e=>!e.hide)),mm=rc([mh],e=>e.map(e=>e.id)),mg=rc([e=>e,my,mv],(e,t,r)=>{var n=mm(e,t,r),a=[];return n.forEach(t=>{var n=mp(e,t,r,void 0);null==n||n.forEach(e=>{var t=e.originalDataIndex;a[t]=((e,t)=>{if(!e)return t;if(!t)return e;var r=Math.min(e.x,e.x+e.width,t.x,t.x+t.width),n=Math.min(e.y,e.y+e.height,t.y,t.y+t.height);return{x:r,y:n,width:Math.max(e.x,e.x+e.width,t.x,t.x+t.width)-r,height:Math.max(e.y,e.y+e.height,t.y,t.y+t.height)-n}})(a[t],e)})}),a}),mb=["index"];function mx(){return(mx=Object.assign.bind()).apply(null,arguments)}var mw=(0,E.createContext)(void 0),mO=(e,t)=>"recharts-bar-stack-clip-path-".concat(e,"-").concat(t),mj=e=>{var t=e.index,r=function(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{var t=(0,E.useContext)(mw);if(null!=t){var r=t.stackId;return"url(#".concat(mO(r,e),")")}})(t);return E.createElement(L,mx({className:"recharts-bar-stack-layer",clipPath:n},r))},mA=["onMouseEnter","onMouseLeave","onClick"],mE=["value","background","tooltipPosition"],mP=["id"],mS=["onMouseEnter","onClick","onMouseLeave"];function mk(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],c=!0,u=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;c=!1}else for(;!(c=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);c=!0);}catch(e){u=!0,a=e}finally{try{if(!c&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(u)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return mI(e,t);var r=({}).toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?mI(e,t):void 0}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function mI(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=e.dataKey,r=e.stroke,n=e.strokeWidth,a=e.fill,i=e.name,o=e.hide,l=e.unit,c=e.formatter,u=e.tooltipType,s=e.id,d={dataDefinedOnItem:void 0,getPosition:ei,settings:{stroke:r,strokeWidth:n,fill:a,dataKey:t,nameKey:void 0,name:nV(i,t),hide:o,type:u,color:a,unit:l,formatter:c,graphicalItemId:s}};return hJ.createElement(uY,{tooltipEntrySettings:d})});function mz(e){var t,r=e6(uO),n=e.data,a=e.dataKey,i=e.background,o=e.allOtherBarProps,l=o.onMouseEnter,c=o.onMouseLeave,u=o.onClick,s=mN(o,mA),d=h5(l,a,o.id),f=h8(c),p=h4(u,a,o.id);if(!i||null==n)return null;var y=T(i);return hJ.createElement(a3,{zIndex:(t=aA.barBackground,i&&"object"==typeof i&&"zIndex"in i&&"number"==typeof i.zIndex&&ek(i.zIndex)?i.zIndex:t)},n.map((e,t)=>{e.value;var n=e.background,o=(e.tooltipPosition,mN(e,mE));if(!n)return null;var l=d(e,e.originalDataIndex),c=f(e,e.originalDataIndex),u=p(e,e.originalDataIndex),v=mM(mM(mM(mM(mM({option:i,isActive:String(e.originalDataIndex)===r},o),{},{fill:"#eee"},n),y),iP(s,e,t)),{},{onMouseEnter:l,onMouseLeave:c,onClick:u,dataKey:a,index:t,className:"recharts-bar-background-rectangle"});return hJ.createElement(h3,mC({key:"background-bar-".concat(t)},v))}))}function m_(e){var t=e.showLabels,r=e.children,n=e.rects,a=null==n?void 0:n.map(e=>{var t={x:e.x,y:e.y,width:e.width,lowerWidth:e.width,upperWidth:e.width,height:e.height};return mM(mM({},t),{},{value:e.value,payload:e.payload,parentViewBox:e.parentViewBox,viewBox:t,fill:e.fill})});return hJ.createElement(ib,{value:t?a:void 0},r)}function mR(e){var t,r=e.shape,n=e.activeBar,a=e.baseProps,i=e.entry,o=e.index,l=e.dataKey,c=e6(uO),u=e6(uA),s=n&&String(i.originalDataIndex)===c&&(null==u||l===u),d=mk((0,hJ.useState)(!1),2),f=d[0],p=d[1],y=mk((0,hJ.useState)(!1),2),v=y[0],h=y[1];(0,hJ.useEffect)(()=>{var e;return s?(p(!0),e=requestAnimationFrame(()=>{h(!0)})):h(!1),()=>{cancelAnimationFrame(e)}},[s]);var m=(0,hJ.useCallback)(()=>{s||p(!1)},[s]),g=s&&v,b=s||f;t=s?!0===n?r:n:r;var x=hJ.createElement(h3,mC({},a,{name:String(a.name)},i,{isActive:g,option:t,index:o,dataKey:l,animationElapsedTime:e.animationElapsedTime,isAnimating:e.isAnimating,isEntrance:e.isEntrance,onTransitionEnd:m}));return b?hJ.createElement(a3,{zIndex:aA.activeBar},hJ.createElement(mj,{index:i.originalDataIndex},x)):x}function mL(e){var t=e.shape,r=e.baseProps,n=e.entry,a=e.index,i=e.dataKey;return hJ.createElement(h3,mC({},r,{name:String(r.name)},n,{isActive:!1,option:t,index:a,dataKey:i,animationElapsedTime:e.animationElapsedTime,isAnimating:e.isAnimating,isEntrance:e.isEntrance}))}function mB(e){var t,r=e.data,n=e.props,a=e.animationElapsedTime,i=e.isAnimating,o=e.isEntrance,l=null!=(t=N(n))?t:{},c=l.id,u=mN(l,mP),s=n.shape,d=n.dataKey,f=n.activeBar,p=n.onMouseEnter,y=n.onClick,v=n.onMouseLeave,h=mN(n,mS),m=h5(p,d,c),g=h8(v),b=h4(y,d,c);return r?hJ.createElement(hJ.Fragment,null,r.map((e,t)=>hJ.createElement(mj,mC({index:e.originalDataIndex,key:"rectangle-".concat(null==e?void 0:e.x,"-").concat(null==e?void 0:e.y,"-").concat(null==e?void 0:e.value,"-").concat(t),className:"recharts-bar-rectangle"},iP(h,e,t),{onMouseEnter:m(e,e.originalDataIndex),onMouseLeave:g(e,e.originalDataIndex),onClick:b(e,e.originalDataIndex)}),f?hJ.createElement(mR,{shape:s,activeBar:f,baseProps:u,entry:e,index:t,dataKey:d,animationElapsedTime:a,isAnimating:i,isEntrance:o}):hJ.createElement(mL,{shape:s,baseProps:u,entry:e,index:t,dataKey:d,animationElapsedTime:a,isAnimating:i,isEntrance:o})))):null}function mK(e){var t=e.props,r=e.previousRectanglesRef,n=t.data,a=t.isAnimationActive,i=t.animationBegin,o=t.animationDuration,l=t.animationEasing,c=t.animationInterpolateFn,u=t.layout,s=sQ(t.onAnimationStart,t.onAnimationEnd),d=s.isAnimating,f=s.handleAnimationStart,p=s.handleAnimationEnd;return hJ.createElement(m_,{showLabels:!d,rects:n},hJ.createElement(sJ,{animationInput:n,animationIdPrefix:"recharts-bar-",items:n,previousItemsRef:r,isAnimationActive:a,animationBegin:i,animationDuration:o,animationEasing:l,onAnimationStart:f,onAnimationEnd:p,animationInterpolateFn:c,animationMatchBy:t.animationMatchBy,layout:u},(e,r,n)=>hJ.createElement(L,null,hJ.createElement(mB,{props:t,data:e,animationElapsedTime:r,isAnimating:d||r<1,isEntrance:n}))),hJ.createElement(ij,{label:t.label}),t.children)}function mF(e){var t=(0,hJ.useRef)(null);return hJ.createElement(mK,{previousRectanglesRef:t,props:e})}var mW=(e,t)=>{var r=Array.isArray(e.value)?e.value[1]:e.value;return{x:e.x,y:e.y,value:r,errorVal:nD(e,t)}};class mV extends hJ.PureComponent{render(){var e=this.props,t=e.hide,r=e.data,n=e.dataKey,a=e.className,i=e.xAxisId,o=e.yAxisId,l=e.needClip,c=e.background,u=e.id;if(t||null==r)return null;var s=(0,S.clsx)("recharts-bar",a);return hJ.createElement(L,{className:s,id:u},l&&hJ.createElement("defs",null,hJ.createElement(uQ,{clipPathId:u,xAxisId:i,yAxisId:o})),hJ.createElement(L,{className:"recharts-bar-rectangles",clipPath:l?"url(#clipPath-".concat(u,")"):void 0},hJ.createElement(mz,{data:r,dataKey:n,background:c,allOtherBarProps:this.props}),hJ.createElement(mF,this.props)))}}var m$={activeBar:!1,animationBegin:0,animationDuration:400,animationEasing:"ease",animationInterpolateFn:(e,t,r)=>null==e?[]:1===t?e.flatMap(e=>"removed"===e.status?[]:[e.next]):e.flatMap(e=>{if("removed"===e.status)return"horizontal"===r?[mM(mM({},e.prev),{},{height:et(e.prev.height,0,t),y:et(e.prev.y,e.prev.y+e.prev.height,t)})]:[mM(mM({},e.prev),{},{width:et(e.prev.width,0,t)})];if("matched"===e.status)return[mM(mM({},e.next),{},{x:et(e.prev.x,e.next.x,t),y:et(e.prev.y,e.next.y,t),width:et(e.prev.width,e.next.width,t),height:et(e.prev.height,e.next.height,t)})];var n=e.next;return"horizontal"===r?[mM(mM({},n),{},{height:et(0,n.height,t),y:et(n.stackedBarStart,n.y,t)})]:[mM(mM({},n),{},{width:et(0,n.width,t),x:et(n.stackedBarStart,n.x,t)})]}),animationMatchBy:sG,background:!1,hide:!1,isAnimationActive:"auto",label:!1,legendType:"rect",minPointSize:0,shape:vH,xAxisId:0,yAxisId:0,zIndex:aA.bar};function mU(e){var t,r=e.xAxisId,n=e.yAxisId,a=e.hide,i=e.legendType,o=e.minPointSize,l=e.activeBar,c=e.animationBegin,u=e.animationDuration,s=e.animationEasing,d=e.isAnimationActive,f=uZ(r,n).needClip,p=e6(ax),y=n6(),v=iT(e.children,h0),h=e6(t=>mp(t,e.id,y,v));if("vertical"!==p&&"horizontal"!==p)return null;var m=null==h?void 0:h[0];return t=null==m||null==m.height||null==m.width?0:"vertical"===p?m.height/2:m.width/2,hJ.createElement(me,{xAxisId:r,yAxisId:n,data:h,dataPointFormatter:mW,errorBarOffset:t},hJ.createElement(mV,mC({},e,{layout:p,needClip:f,data:h,xAxisId:r,yAxisId:n,hide:a,legendType:i,minPointSize:o,activeBar:l,animationBegin:c,animationDuration:u,animationEasing:s,isAnimationActive:d})))}var mH=hJ.memo(function(e){var t,r,n=eS(e,m$),a=(t=n.stackId,null!=(r=(0,E.useContext)(mw))?r.stackId:null!=t?n_(t):void 0),i=n6();return hJ.createElement(s3,{id:n.id,type:"bar"},e=>{var t,r,o,l;return hJ.createElement(hJ.Fragment,null,hJ.createElement(sj,{legendPayload:(t=n.dataKey,r=n.name,o=n.fill,l=n.legendType,[{inactive:n.hide,dataKey:t,type:l,color:o,value:nV(r,t),payload:n}])}),hJ.createElement(mT,{dataKey:n.dataKey,stroke:n.stroke,strokeWidth:n.strokeWidth,fill:n.fill,name:n.name,hide:n.hide,unit:n.unit,formatter:n.formatter,tooltipType:n.tooltipType,id:e}),hJ.createElement(dn,{type:"bar",id:e,data:void 0,xAxisId:n.xAxisId,yAxisId:n.yAxisId,zAxisId:0,dataKey:n.dataKey,stackId:a,hide:n.hide,barSize:n.barSize,minPointSize:n.minPointSize,maxBarSize:n.maxBarSize,isPanorama:i,hasCustomShape:null!=n.shape&&n.shape!==vH}),hJ.createElement(a3,{zIndex:n.zIndex},hJ.createElement(mU,mC({},n,{id:e}))))})},dw);mH.displayName="Bar";var mG=["axis","item"],mq=(0,E.forwardRef)((e,t)=>E.createElement(p3,{chartName:"BarChart",defaultTooltipEventType:"axis",validateTooltipEventTypes:mG,tooltipPayloadSearcher:fo,categoricalChartProps:e,ref:t}));e.s(["BarChart",0,function({data:e,index:t,categories:r,colors:n,valueFormatter:a,stack:i=!1,layout:o="horizontal",yAxisWidth:l=56,tickGap:c=5,showLegend:u=!0,showXAxis:s=!0,showGridLines:d=!0,showTooltip:f=!0,customTooltip:p,onValueChange:y,className:v,style:h}){let m=hQ(r.length,n),g=Object.fromEntries(r.map(e=>[e,{label:e}])),b="vertical"===o,x=p??hq;return(0,A.jsx)(hW,{config:g,className:(0,hR.cn)("aspect-auto h-80 w-full",v),style:h,children:(0,A.jsxs)(mq,{data:[...e],layout:o,children:[d&&(0,A.jsx)(yN,{horizontal:!b,vertical:b}),b?(0,A.jsx)(yX,{type:"number",hide:!s,tickLine:!1,axisLine:!1,minTickGap:c,tickFormatter:a}):(0,A.jsx)(yX,{dataKey:t,hide:!s,tickLine:!1,axisLine:!1,minTickGap:c,interval:"equidistantPreserveStart"}),b?(0,A.jsx)(y8,{type:"category",dataKey:t,width:l,tickLine:!1,axisLine:!1,interval:0}):(0,A.jsx)(y8,{width:l,tickLine:!1,axisLine:!1,tickFormatter:a}),f&&(0,A.jsx)(h$,{content:({active:e,payload:t,label:r})=>(0,A.jsx)(x,{active:e,payload:t,label:r,...p?{}:{valueFormatter:a}})}),u&&(0,A.jsx)(h_,{verticalAlign:"top",content:(0,A.jsx)(hU,{className:"justify-end text-muted-foreground"})}),r.map((e,t)=>(0,A.jsx)(mH,{dataKey:e,fill:m[t],stackId:i?"stack":void 0,isAnimationActive:!1,onClick:y?t=>{t.payload&&y({...t.payload,categoryClicked:e})}:void 0},e))]})})}],343053),e.s(["CustomLegend",0,({categories:e,colors:t})=>(0,A.jsx)("div",{className:"flex items-center justify-end space-x-4",children:e.map((e,r)=>(0,A.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,A.jsx)("span",{className:"h-2 w-2 shrink-0 rounded-full ring-4 ring-white",style:{backgroundColor:hZ(t[r%t.length])}}),(0,A.jsx)("p",{className:"text-sm text-muted-foreground",children:hG(e)})]},e))})],594772);var mX=e=>e.graphicalItems.polarItems,mY=rc([od,of],oV),mZ=rc([mX,oK,mY],oH),mQ=rc([mZ],oZ),mJ=rc([mQ,iU],o0),m0=rc([mJ,oK,mZ],o2);rc([mJ,oK,mZ],(e,t,r)=>r.length>0?e.flatMap(e=>r.flatMap(r=>{var n;return{value:nD(e,null!=(n=t.dataKey)?n:r.dataKey),errorDomain:[]}})).filter(Boolean):(null==t?void 0:t.dataKey)!=null?e.map(e=>({value:nD(e,t.dataKey),errorDomain:[]})):e.map(e=>({value:e,errorDomain:[]})));var m1=()=>void 0,m2=rc([mJ,oK,mZ,lf,od,iX],ly),m3=rc([oK,lu,ls,m1,m2,m1,ax,od],lD),m6=rc([oK,ax,mJ,m0,oi,od,m3],lz),m5=rc([m6,oF,lR],lL),m8=rc([oK,m6,m5,od],lK);function m4(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function m9(e){for(var t=1;tt],(e,t)=>e.filter(e=>"pie"===e.type).find(e=>e.id===t)),ge=[],gt=(e,t,r)=>(null==r?void 0:r.length)===0?ge:r,gr=rc([iU,m7,gt],(e,t,r)=>{var n,a=e.chartData;if(null!=t&&((n=(null==t?void 0:t.data)!=null&&t.data.length>0?t.data:a)&&n.length||null==r||(n=r.map(e=>m9(m9({},t.presentationProps),e.props))),null!=n))return n}),gn=rc([gr,m7,gt],(e,t,r)=>{if(null!=e&&null!=t)return e.map((e,n)=>{var a,i,o=nD(e,t.nameKey,t.name);return i=null!=r&&null!=(a=r[n])&&null!=(a=a.props)&&a.fill?r[n].props.fill:"object"==typeof e&&null!=e&&"fill"in e?e.fill:t.fill,{value:nV(o,t.dataKey),dataKey:t.dataKey,color:i,payload:e,type:t.legendType}})}),ga=rc([gr,m7,gt,n0],(e,t,r,n)=>{if(null!=t&&null!=e)return function(e){var t,r,n,a=e.pieSettings,i=e.displayedData,o=e.cells,l=e.offset,c=a.cornerRadius,u=a.startAngle,s=a.endAngle,d=a.dataKey,f=a.nameKey,p=a.tooltipType,y=Math.abs(a.minAngle),v=H(s-u)*Math.min(Math.abs(s-u),360),h=Math.abs(v),m=i.length<=1?0:null!=(t=a.paddingAngle)?t:0,g=i.filter(e=>0!==nD(e,d,0)).length,b=i.reduce((e,t)=>{var r=nD(t,d,0);return e+(X(r)?r:0)},0),x=y>0&&b>0&&i.some(e=>{var t=nD(e,d,0),r=(X(t)?t:0)/b;return 0!==t&&r*h=360?g:g-1)*m;return b>0&&(r=i.map((e,t)=>{var r,i,s,y,h,g,O,j,A,E=nD(e,d,0),P=nD(e,f,t),S=(r=l.top,i=l.left,h=eY(s=l.width,y=l.height),g=i+J(a.cx,s,s/2),O=r+J(a.cy,y,y/2),{cx:g,cy:O,innerRadius:J(a.innerRadius,h,0),outerRadius:(j=a.outerRadius,"function"==typeof j?J(j(e),h,.8*h):J(j,h,.8*h)),maxRadius:a.maxRadius||Math.sqrt(s*s+y*y)/2}),k=(X(E)?E:0)/b,I=gf(gf({},e),o&&o[t]&&o[t].props),C=null!=I&&"fill"in I&&"string"==typeof I.fill?I.fill:a.fill,D=(A=t?n.endAngle+H(v)*m*(0!==E):u)+H(v)*((0!==E?x:0)+k*w),M=(A+D)/2,N=(S.innerRadius+S.outerRadius)/2,T=[{name:P,value:E,payload:I,dataKey:d,type:p,color:C,fill:C,graphicalItemId:a.id}],z=eX(S.cx,S.cy,N,M);return n=gf(gf(gf(gf({},a.presentationProps),{},{percent:k,cornerRadius:"string"==typeof c?parseFloat(c):c,name:P,tooltipPayload:T,midAngle:M,middleRadius:N,tooltipPosition:z},I),S),{},{value:E,dataKey:d,startAngle:A,endAngle:D,payload:I,paddingAngle:0!==E?H(v)*m:0})})),r}({offset:n,pieSettings:t,displayedData:e,cells:r})}),gi=["key"],go=["onMouseEnter","onClick","onMouseLeave"],gl=["id"],gc=["id"];function gu(){return(gu=Object.assign.bind()).apply(null,arguments)}function gs(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r={};for(var n in e)if(({}).hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;niT(e.children,h0),[e.children]),r=e6(r=>gn(r,e.id,t));return null==r?null:E.createElement(sA,{legendPayload:r})}var gy=E.memo(e=>{var t=e.dataKey,r=e.nameKey,n=e.sectors,a=e.stroke,i=e.strokeWidth,o=e.fill,l=e.name,c=e.hide,u=e.tooltipType,s=e.formatter,d=e.id,f=function(e){if(null!=e&&"boolean"!=typeof e&&"function"!=typeof e){if(E.isValidElement(e)){var t,r=null==(t=e.props)?void 0:t.fill;return"string"==typeof r?r:void 0}var n=e.fill;return"string"==typeof n?n:void 0}}(e.activeShape),p={dataDefinedOnItem:n.map(e=>{var t=e.tooltipPayload;return null==f||null==t?t:t.map(e=>gf(gf({},e),{},{color:f,fill:f}))}),getPosition:e=>{var t;return null==(t=n[Number(e)])?void 0:t.tooltipPosition},settings:{stroke:a,strokeWidth:i,fill:o,dataKey:t,nameKey:r,name:nV(l,t),hide:c,type:u,color:o,unit:"",formatter:s,graphicalItemId:d}};return E.createElement(uY,{tooltipEntrySettings:p})});function gv(e){var t=e.sectors,r=e.props,n=e.showLabels,a=r.label,i=r.labelLine,o=r.dataKey;if(!n||!a||!t)return null;var l=N(r),c=T(a),u=T(i),s="object"==typeof a&&"offsetRadius"in a&&"number"==typeof a.offsetRadius&&a.offsetRadius||20,d=t.map((e,t)=>{var r,n,d=(e.startAngle+e.endAngle)/2,f=eX(e.cx,e.cy,e.outerRadius+s,d),p=gf(gf(gf(gf({},l),e),{},{stroke:"none"},c),{},{index:t,textAnchor:(r=f.x)>(n=e.cx)?"start":r{if(E.isValidElement(e))return E.cloneElement(e,t);if("function"==typeof e)return e(t);var r=(0,S.clsx)("recharts-pie-label-line","boolean"!=typeof e?e.className:"");t.key;var n=gs(t,gi);return E.createElement(dU,gu({},n,{type:"linear",className:r}))})(i,y),((e,t,r)=>{if(E.isValidElement(e))return E.cloneElement(e,t);var n=r;if("function"==typeof e&&(n=e(t),E.isValidElement(n)))return n;var a=(0,S.clsx)("recharts-pie-label-text",yr(e));return E.createElement(eU,gu({},t,{alignmentBaseline:"middle",className:a}),n)})(a,p,nD(e,o))))});return E.createElement(L,{className:"recharts-pie-labels"},d)}function gh(e){var t=e.sectors,r=e.props,n=e.showLabels,a=r.label;return"object"==typeof a&&null!=a&&"position"in a?E.createElement(ij,{label:a}):E.createElement(gv,{sectors:t,props:r,showLabels:n})}function gm(e){var t=e.sectors,r=e.activeShape,n=e.inactiveShape,a=e.allOtherPieProps,i=e.shape,o=e.id,l=e.animationElapsedTime,c=e.isAnimating,u=e.isEntrance,s=e6(uO),d=e6(uA),f=e6(uE),p=a.onMouseEnter,y=a.onClick,v=a.onMouseLeave,h=gs(a,go),m=h5(p,a.dataKey,o),g=h8(v),b=h4(y,a.dataKey,o);return null==t||0===t.length?null:E.createElement(E.Fragment,null,t.map((e,p)=>{if((null==e?void 0:e.startAngle)===0&&(null==e?void 0:e.endAngle)===0&&1!==t.length)return null;var y=null==f||f===o,v=String(p)===s&&(null==d||a.dataKey===d)&&y,x=r&&v?r:s?n:null,w=gf(gf({},e),{},{stroke:e.stroke,tabIndex:-1,index:p,isActive:v,animationElapsedTime:l,isAnimating:c,isEntrance:u,[nY]:p,[nZ]:o});return E.createElement(L,gu({key:"sector-".concat(null==e?void 0:e.startAngle,"-").concat(null==e?void 0:e.endAngle,"-").concat(e.midAngle,"-").concat(p),tabIndex:-1,className:"recharts-pie-sector"},iP(h,e,p),{onMouseEnter:m(e,p),onMouseLeave:g(e,p),onClick:b(e,p)}),E.createElement(dc,{option:null!=x?x:i,DefaultShape:vJ,shapeProps:w}))}))}function gg(e){var t=e.showLabels,r=e.sectors,n=e.children,a=(0,E.useMemo)(()=>t&&r?r.map(e=>({value:e.value,payload:e.payload,clockWise:!1,parentViewBox:void 0,viewBox:{cx:e.cx,cy:e.cy,innerRadius:e.innerRadius,outerRadius:e.outerRadius,startAngle:e.startAngle,endAngle:e.endAngle,clockWise:!1},fill:e.fill})):[],[r,t]);return E.createElement(iw,{value:t?a:void 0},n)}function gb(e){var t=e.props,r=e.previousSectorsRef,n=e.id,a=t.sectors,i=t.activeShape,o=t.inactiveShape,l=t.animationInterpolateFn,c=sQ(t.onAnimationStart,t.onAnimationEnd),u=c.isAnimating,s=c.handleAnimationStart,d=c.handleAnimationEnd,f=e6(aO);return null==f?null:E.createElement(gg,{showLabels:!u,sectors:a},E.createElement(sJ,{animationInput:t,animationIdPrefix:"recharts-pie-",items:a,previousItemsRef:r,isAnimationActive:t.isAnimationActive,animationBegin:t.animationBegin,animationDuration:t.animationDuration,animationEasing:t.animationEasing,onAnimationStart:s,onAnimationEnd:d,animationInterpolateFn:l,animationMatchBy:t.animationMatchBy,layout:f},(e,r,a)=>E.createElement(L,null,E.createElement(gm,{sectors:e,activeShape:i,inactiveShape:o,allOtherPieProps:t,shape:t.shape,id:n,animationElapsedTime:r,isAnimating:u||r<1,isEntrance:a}))),E.createElement(gh,{showLabels:!u,sectors:a,props:t}),t.children)}var gx={animationBegin:400,animationDuration:1500,animationEasing:"ease",animationInterpolateFn:(e,t)=>{if(null==e)return[];var r=[],n=e.find(e=>"removed"!==e.status),a=n?n.next.startAngle:0;return e.forEach((e,n)=>{if("removed"!==e.status){var i=n>0?V(e.next,"paddingAngle",0):0;if("matched"===e.status){var o=et(e.prev.endAngle-e.prev.startAngle,e.next.endAngle-e.next.startAngle,t),l=gf(gf({},e.next),{},{startAngle:a+i,endAngle:a+o+i});r.push(l),a=l.endAngle}else{var c=et(0,e.next.endAngle-e.next.startAngle,t),u=gf(gf({},e.next),{},{startAngle:a+i,endAngle:a+c+i});r.push(u),a=u.endAngle}}}),r},animationMatchBy:sG,cx:"50%",cy:"50%",dataKey:"value",endAngle:360,fill:"#808080",hide:!1,innerRadius:0,isAnimationActive:"auto",label:!1,labelLine:!0,legendType:"rect",minAngle:0,nameKey:"name",outerRadius:"80%",paddingAngle:0,rootTabIndex:0,shape:vJ,startAngle:0,stroke:"#fff",zIndex:aA.area};function gw(e){var t=e.id,r=gs(e,gl),n=e.hide,a=e.className,i=e.rootTabIndex,o=(0,E.useMemo)(()=>iT(e.children,h0),[e.children]),l=e6(e=>ga(e,t,o)),c=(0,E.useRef)(null),u=(0,S.clsx)("recharts-pie",a);return n||null==l?(c.current=null,E.createElement(L,{tabIndex:i,className:u})):E.createElement(a3,{zIndex:e.zIndex},E.createElement(gy,{dataKey:e.dataKey,nameKey:e.nameKey,sectors:l,stroke:e.stroke,strokeWidth:e.strokeWidth,fill:e.fill,name:e.name,hide:e.hide,tooltipType:e.tooltipType,formatter:e.formatter,id:t,activeShape:e.activeShape}),E.createElement(L,{tabIndex:i,className:u},E.createElement(gb,{props:gf(gf({},r),{},{sectors:l}),previousSectorsRef:c,id:t})))}var gO=function(e){var t=eS(e,gx),r=t.id,n=gs(t,gc),a=N(n);return E.createElement(s3,{id:r,type:"pie"},e=>E.createElement(E.Fragment,null,E.createElement(da,{type:"pie",id:e,data:n.data,dataKey:n.dataKey,hide:n.hide,angleAxisId:0,radiusAxisId:0,name:n.name,nameKey:n.nameKey,tooltipType:n.tooltipType,legendType:n.legendType,fill:n.fill,cx:n.cx,cy:n.cy,startAngle:n.startAngle,endAngle:n.endAngle,paddingAngle:n.paddingAngle,minAngle:n.minAngle,innerRadius:n.innerRadius,outerRadius:n.outerRadius,cornerRadius:n.cornerRadius,presentationProps:a,maxRadius:t.maxRadius}),E.createElement(gp,gu({},n,{id:e})),E.createElement(gw,gu({},n,{id:e}))))};function gj(e){var t=e0();return(0,E.useEffect)(()=>{t(fB(e))},[t,e]),null}gO.displayName="Pie";var gA=["layout"];function gE(){return(gE=Object.assign.bind()).apply(null,arguments)}function gP(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}var gS=function(e){for(var t=1;t{var r=eS(e,gM);return E.createElement(gk,{chartName:"PieChart",defaultTooltipEventType:"item",validateTooltipEventTypes:gD,tooltipPayloadSearcher:fo,categoricalChartProps:r,ref:t})});e.s(["DonutChart",0,function({data:e,index:t,category:r,colors:n,variant:a="donut",valueFormatter:i,showTooltip:o=!0,showLabel:l=!1,label:c,startAngle:u=0,endAngle:s=360,className:d,style:f}){let p,y=hQ(e.length,n),v=Object.fromEntries(e.map((e,r)=>{let n=String(e[t]??r);return[n,{label:n}]})),h=l&&"donut"===a&&e.length>0;return(0,A.jsx)(hW,{config:v,className:(0,hR.cn)("aspect-auto h-40 w-full",d),style:f,children:(0,A.jsxs)(gN,{children:[o&&(0,A.jsx)(h$,{content:({active:e,payload:t,label:r})=>(0,A.jsx)(hq,{active:e,payload:t,label:r,valueFormatter:i})}),h&&(0,A.jsx)("text",{className:"fill-foreground text-base",x:"50%",y:"50%",textAnchor:"middle",dominantBaseline:"middle",children:c??(p=e.reduce((e,t)=>{let n=t[r];return e+("number"==typeof n?n:0)},0),i?i(p):String(p))}),(0,A.jsx)(gO,{data:[...e],dataKey:r,nameKey:t,innerRadius:"pie"===a?"0%":"75%",outerRadius:"100%",startAngle:u,endAngle:s,strokeWidth:1,isAnimationActive:!1,children:e.map((e,r)=>(0,A.jsx)(h0,{fill:y[r]},String(e[t]??r)))})]})})}],325738),e.s([],32117)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0pvvj8a2cte7e.js b/litellm/proxy/_experimental/out/_next/static/chunks/0pvvj8a2cte7e.js new file mode 100644 index 00000000000..91f66f57f32 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0pvvj8a2cte7e.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let o=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:o[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),n=e=>e?6:5,i=(e,t,r,a,o)=>{clearTimeout(a.current);let n=l(e);t(n),r.current=n,o&&o({current:n})};var s=e.i(480731),d=e.i(444755),c=e.i(673706);let m=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var g=e.i(95779);let u={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},b=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,g.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,g.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,g.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,g.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,g.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,g.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,g.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,g.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,g.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,g.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},p=(0,c.makeClassName)("Button"),f=({loading:e,iconSize:t,iconPosition:r,Icon:o,needMargin:l,transitionStatus:n})=>{let i=l?r===s.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),g={default:c,entering:c,entered:t,exiting:t,exited:c};return e?a.default.createElement(m,{className:(0,d.tremorTwMerge)(p("icon"),"animate-spin shrink-0",i,g.default,g[n]),style:{transition:"width 150ms"}}):a.default.createElement(o,{className:(0,d.tremorTwMerge)(p("icon"),"shrink-0",t,i)})},h=a.default.forwardRef((e,o)=>{let{icon:m,iconPosition:g=s.HorizontalPositions.Left,size:h=s.Sizes.SM,color:x,variant:C="primary",disabled:k,loading:w=!1,loadingText:v,children:N,tooltip:$,className:y}=e,T=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),j=w||k,B=void 0!==m||w,E=w&&v,O=!(!N&&!E),S=(0,d.tremorTwMerge)(u[h].height,u[h].width),M="light"!==C?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",R=b(C,x),z=("light"!==C?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[h],{tooltipProps:P,getReferenceProps:H}=(0,r.useTooltip)(300),[q,F]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:o,timeout:s,initialEntered:d,mountOnEnter:c,unmountOnExit:m,onStateChange:g}={})=>{let[u,b]=(0,a.useState)(()=>l(d?2:n(c))),p=(0,a.useRef)(u),f=(0,a.useRef)(0),[h,x]="object"==typeof s?[s.enter,s.exit]:[s,s],C=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return n(t)}})(p.current._s,m);e&&i(e,b,p,f,g)},[g,m]);return[u,(0,a.useCallback)(a=>{let l=e=>{switch(i(e,b,p,f,g),e){case 1:h>=0&&(f.current=((...e)=>setTimeout(...e))(C,h));break;case 4:x>=0&&(f.current=((...e)=>setTimeout(...e))(C,x));break;case 0:case 3:f.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},s=p.current.isEnter;"boolean"!=typeof a&&(a=!s),a?s||l(e?+!r:2):s&&l(t?o?3:4:n(m))},[C,g,e,t,r,o,h,x,m]),C]})({timeout:50});return(0,a.useEffect)(()=>{F(w)},[w]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([o,P.refs.setReference]),className:(0,d.tremorTwMerge)(p("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",M,z.paddingX,z.paddingY,z.fontSize,R.textColor,R.bgColor,R.borderColor,R.hoverBorderColor,j?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(b(C,x).hoverTextColor,b(C,x).hoverBgColor,b(C,x).hoverBorderColor),y),disabled:j},H,T),a.default.createElement(r.default,Object.assign({text:$},P)),B&&g!==s.HorizontalPositions.Right?a.default.createElement(f,{loading:w,iconSize:S,iconPosition:g,Icon:m,transitionStatus:q.status,needMargin:O}):null,E||N?a.default.createElement("span",{className:(0,d.tremorTwMerge)(p("text"),"text-tremor-default whitespace-nowrap")},E?v:N):null,B&&g===s.HorizontalPositions.Right?a.default.createElement(f,{loading:w,iconSize:S,iconPosition:g,Icon:m,transitionStatus:q.status,needMargin:O}):null)});h.displayName="Button",e.s(["Button",0,h],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),o=e.i(271645);let l=o.default.forwardRef((e,l)=>{let{color:n,className:i,children:s}=e;return o.default.createElement("p",{ref:l,className:(0,r.tremorTwMerge)("text-tremor-default",n?(0,a.getColorClassNames)(n,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},s)});l.displayName="Text",e.s(["default",0,l],936325),e.s(["Text",0,l],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),o=e.i(95779),l=e.i(444755),n=e.i(673706);let i=(0,n.makeClassName)("Card"),s=r.default.forwardRef((e,s)=>{let{decoration:d="",decorationColor:c,children:m,className:g}=e,u=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:s,className:(0,l.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,n.getColorClassNames)(c,o.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),g)},u),m)});s.displayName="Card",e.s(["Card",0,s],304967)},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,a.tremorTwMerge)(o("root"),"overflow-auto",i)},r.default.createElement("table",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},s),n))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",i)},s),n))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"align-middle whitespace-nowrap text-left p-4",i)},s),n))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",i)},s),n))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",i)},s),n))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(444755);let o=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:n,className:i}=e,s=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,a.tremorTwMerge)(o("row"),i)},s),n))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),o=e.i(529681);let l=e=>{let{prefixCls:a,className:o,style:l,size:n,shape:i}=e,s=(0,r.default)({[`${a}-lg`]:"large"===n,[`${a}-sm`]:"small"===n}),d=(0,r.default)({[`${a}-circle`]:"circle"===i,[`${a}-square`]:"square"===i,[`${a}-round`]:"round"===i}),c=t.useMemo(()=>"number"==typeof n?{width:n,height:n,lineHeight:`${n}px`}:{},[n]);return t.createElement("span",{className:(0,r.default)(a,s,d,o),style:Object.assign(Object.assign({},c),l)})};e.i(296059);var n=e.i(694758),i=e.i(915654),s=e.i(246422),d=e.i(838378);let c=new n.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),m=e=>({height:e,lineHeight:(0,i.unit)(e)}),g=e=>Object.assign({width:e},m(e)),u=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},m(e)),b=e=>Object.assign({width:e},m(e)),p=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},f=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},m(e)),h=(0,s.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:o,skeletonButtonCls:l,skeletonInputCls:n,skeletonImageCls:i,controlHeight:s,controlHeightLG:d,controlHeightSM:m,gradientFromColor:h,padding:x,marginSM:C,borderRadius:k,titleHeight:w,blockRadius:v,paragraphLiHeight:N,controlHeightXS:$,paragraphMarginTop:y}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:x,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:h},g(s)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},g(d)),[`${r}-sm`]:Object.assign({},g(m))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:w,background:h,borderRadius:v,[`+ ${o}`]:{marginBlockStart:m}},[o]:{padding:0,"> li":{width:"100%",height:N,listStyle:"none",background:h,borderRadius:v,"+ li":{marginBlockStart:$}}},[`${o}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${o} > li`]:{borderRadius:k}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:C,[`+ ${o}`]:{marginBlockStart:y}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:t,width:i(a).mul(2).equal(),minWidth:i(a).mul(2).equal()},f(a,i))},p(e,a,r)),{[`${r}-lg`]:Object.assign({},f(o,i))}),p(e,o,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},f(l,i))}),p(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:o,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},g(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},g(o)),[`${t}${t}-sm`]:Object.assign({},g(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:o,controlHeightSM:l,gradientFromColor:n,calc:i}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:n,borderRadius:r},u(t,i)),[`${a}-lg`]:Object.assign({},u(o,i)),[`${a}-sm`]:Object.assign({},u(l,i))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:o,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:o},b(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},b(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[n]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${o} > li, + ${r}, + ${l}, + ${n}, + ${i} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),x=e=>{let{prefixCls:a,className:o,style:l,rows:n=0}=e,i=Array.from({length:n}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,o),style:l},i)},C=({prefixCls:e,className:a,width:o,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:o},l)});function k(e){return e&&"object"==typeof e?e:{}}let w=e=>{let{prefixCls:o,loading:n,className:i,rootClassName:s,style:d,children:c,avatar:m=!1,title:g=!0,paragraph:u=!0,active:b,round:p}=e,{getPrefixCls:f,direction:w,className:v,style:N}=(0,a.useComponentConfig)("skeleton"),$=f("skeleton",o),[y,T,j]=h($);if(n||!("loading"in e)){let e,a,o=!!m,n=!!g,c=!!u;if(o){let r=Object.assign(Object.assign({prefixCls:`${$}-avatar`},n&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),k(m));e=t.createElement("div",{className:`${$}-header`},t.createElement(l,Object.assign({},r)))}if(n||c){let e,r;if(n){let r=Object.assign(Object.assign({prefixCls:`${$}-title`},!o&&c?{width:"38%"}:o&&c?{width:"50%"}:{}),k(g));e=t.createElement(C,Object.assign({},r))}if(c){let e,a=Object.assign(Object.assign({prefixCls:`${$}-paragraph`},(e={},o&&n||(e.width="61%"),!o&&n?e.rows=3:e.rows=2,e)),k(u));r=t.createElement(x,Object.assign({},a))}a=t.createElement("div",{className:`${$}-content`},e,r)}let f=(0,r.default)($,{[`${$}-with-avatar`]:o,[`${$}-active`]:b,[`${$}-rtl`]:"rtl"===w,[`${$}-round`]:p},v,i,s,T,j);return y(t.createElement("div",{className:f,style:Object.assign(Object.assign({},N),d)},e,a))}return null!=c?c:null};w.Button=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c=!1,size:m="default"}=e,{getPrefixCls:g}=t.useContext(a.ConfigContext),u=g("skeleton",n),[b,p,f]=h(u),x=(0,o.default)(e,["prefixCls"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d,[`${u}-block`]:c},i,s,p,f);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-button`,size:m},x))))},w.Avatar=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,shape:c="circle",size:m="default"}=e,{getPrefixCls:g}=t.useContext(a.ConfigContext),u=g("skeleton",n),[b,p,f]=h(u),x=(0,o.default)(e,["prefixCls","className"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d},i,s,p,f);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-avatar`,shape:c,size:m},x))))},w.Input=e=>{let{prefixCls:n,className:i,rootClassName:s,active:d,block:c,size:m="default"}=e,{getPrefixCls:g}=t.useContext(a.ConfigContext),u=g("skeleton",n),[b,p,f]=h(u),x=(0,o.default)(e,["prefixCls"]),C=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:d,[`${u}-block`]:c},i,s,p,f);return b(t.createElement("div",{className:C},t.createElement(l,Object.assign({prefixCls:`${u}-input`,size:m},x))))},w.Image=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),c=d("skeleton",o),[m,g,u]=h(c),b=(0,r.default)(c,`${c}-element`,{[`${c}-active`]:s},l,n,g,u);return m(t.createElement("div",{className:b},t.createElement("div",{className:(0,r.default)(`${c}-image`,l),style:i},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},w.Node=e=>{let{prefixCls:o,className:l,rootClassName:n,style:i,active:s,children:d}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),m=c("skeleton",o),[g,u,b]=h(m),p=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:s},u,l,n,b);return g(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${m}-image`,l),style:i},d)))},e.s(["default",0,w],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let o=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(o),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let o={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",o);let l=e<0?"-":"",n=Math.abs(e),i=n,s="";return n>=1e6?(i=n/1e6,s="M"):n>=1e3&&(i=n/1e3,s="K"),`${l}${i.toLocaleString("en-US",o)}${s}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return o(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),o(e,r)}},o=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let o=document.execCommand("copy");if(document.body.removeChild(a),o)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),o=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(o.TooltipProvider,{delay:300,children:(0,t.jsxs)(o.Tooltip,{children:[(0,t.jsx)(o.TooltipTrigger,{render:r}),(0,t.jsx)(o.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let n={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:o,tooltip:i,dataTestId:s}){let d=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":s,className:(0,a.cn)("whitespace-nowrap font-normal",n[e]),children:o});return i?(0,t.jsx)(l,{content:i,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],o=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:n="-"}){let i,s,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:n}):(0,r.jsx)(t.CellTooltip,{content:(i=Intl.DateTimeFormat().resolvedOptions().timeZone,s=`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`,`${s}, ${d} (${i})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${a[c.getMonth()]} ${c.getDate()}, ${o(c.getHours())}:${o(c.getMinutes())}:${o(c.getSeconds())}`})})}],200208);var l=e.i(174886),n=e.i(115504),i=e.i(500330);let s={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:o,copyable:d=!1,truncate:c=!0,fallback:m="-",tooltip:g,disabled:u=!1,dataTestId:b,className:p}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:m});let f=!!o&&!u,h=(0,n.cn)(s[a].base,f&&s[a].clickable,c&&"block max-w-[15ch] truncate",u&&"opacity-50",p),x=f?(0,r.jsx)("button",{type:"button",className:h,"data-testid":b,onClick:()=>o(e),children:e}):(0,r.jsx)("span",{className:h,"data-testid":b,children:e}),C=(0,r.jsx)(t.CellTooltip,{content:g??e,trigger:x});return d?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[C,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,i.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):C}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:o=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?o?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,i.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,i.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},389083,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),o=e.i(480731),l=e.i(95779),n=e.i(444755),i=e.i(673706);let s={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},d={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},c=(0,i.makeClassName)("Badge"),m=r.default.forwardRef((e,m)=>{let{color:g,icon:u,size:b=o.Sizes.SM,tooltip:p,className:f,children:h}=e,x=(0,t.__rest)(e,["color","icon","size","tooltip","className","children"]),C=u||null,{tooltipProps:k,getReferenceProps:w}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,k.refs.setReference]),className:(0,n.tremorTwMerge)(c("root"),"w-max shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-small ring-1 ring-inset",g?(0,n.tremorTwMerge)((0,i.getColorClassNames)(g,l.colorPalette.background).bgColor,(0,i.getColorClassNames)(g,l.colorPalette.iconText).textColor,(0,i.getColorClassNames)(g,l.colorPalette.iconRing).ringColor,"bg-opacity-10 ring-opacity-20","dark:bg-opacity-5 dark:ring-opacity-60"):(0,n.tremorTwMerge)("bg-tremor-brand-faint text-tremor-brand-emphasis ring-tremor-brand/20","dark:bg-dark-tremor-brand-muted/50 dark:text-dark-tremor-brand dark:ring-dark-tremor-subtle/20"),s[b].paddingX,s[b].paddingY,s[b].fontSize,f)},w,x),r.default.createElement(a.default,Object.assign({text:p},k)),C?r.default.createElement(C,{className:(0,n.tremorTwMerge)(c("icon"),"shrink-0 -ml-1 mr-1.5",d[b].height,d[b].width)}):null,r.default.createElement("span",{className:(0,n.tremorTwMerge)(c("text"),"whitespace-nowrap")},h))});m.displayName="Badge",e.s(["Badge",0,m],389083)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0q6~n4y84cejn.js b/litellm/proxy/_experimental/out/_next/static/chunks/0q6~n4y84cejn.js new file mode 100644 index 00000000000..b82e28e8264 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0q6~n4y84cejn.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,35983,992704,877891,401141,952744,605083,101852,919751,178677,635307,495470,333771,e=>{"use strict";let t,n,r,o,l;var i=e.i(290571),u=e.i(271645),s=e.i(783222),a=e.i(433336),c=e.i(174080),d=e.i(394487),f=e.i(503269),p=e.i(214520),m=e.i(835696),v=e.i(746725);function g(e,t=!1){let[n,r]=(0,u.useReducer)(()=>({}),{}),o=(0,u.useMemo)(()=>(function(e){if(null===e)return{width:0,height:0};let{width:t,height:n}=e.getBoundingClientRect();return{width:t,height:n}})(e),[e,n]);return(0,m.useIsoMorphicEffect)(()=>{if(!e)return;let t=new ResizeObserver(r);return t.observe(e),()=>{t.disconnect()}},[e]),t?{width:`${o.width}px`,height:`${o.height}px`}:o}e.s(["useElementSize",0,g],992704);var h=e.i(914189),b=e.i(544508),x=e.i(402155);class E extends Map{constructor(e){super(),this.factory=e}get(e){let t=super.get(e);return void 0===t&&(t=this.factory(e),this.set(e,t)),t}}function y(e,t){let n=e(),r=new Set;return{getSnapshot:()=>n,subscribe:e=>(r.add(e),()=>r.delete(e)),dispatch(e,...o){let l=t[e].call(n,...o);l&&(n=l,r.forEach(e=>e()))}}}function S(e){return(0,u.useSyncExternalStore)(e.subscribe,e.getSnapshot,e.getSnapshot)}let R=new E(()=>y(()=>[],{ADD(e){return this.includes(e)?this:[...this,e]},REMOVE(e){let t=this.indexOf(e);if(-1===t)return this;let n=this.slice();return n.splice(t,1),n}}));function w(e,t){let n=R.get(t),r=(0,u.useId)(),o=S(n);if((0,m.useIsoMorphicEffect)(()=>{if(e)return n.dispatch("ADD",r),()=>n.dispatch("REMOVE",r)},[n,e]),!e)return!1;let l=o.indexOf(r),i=o.length;return -1===l&&(l=i,i+=1),l===i-1}let O=new Map,P=new Map;function C(e){var t;let n=null!=(t=P.get(e))?t:0;return P.set(e,n+1),0!==n||(O.set(e,{"aria-hidden":e.getAttribute("aria-hidden"),inert:e.inert}),e.setAttribute("aria-hidden","true"),e.inert=!0),()=>(function(e){var t;let n=null!=(t=P.get(e))?t:1;if(1===n?P.delete(e):P.set(e,n-1),1!==n)return;let r=O.get(e);r&&(null===r["aria-hidden"]?e.removeAttribute("aria-hidden"):e.setAttribute("aria-hidden",r["aria-hidden"]),e.inert=r.inert,O.delete(e))})(e)}var M=e.i(941444);function I(e,t,n){let r=(0,M.useLatestValue)(e=>{let t=e.getBoundingClientRect();0===t.x&&0===t.y&&0===t.width&&0===t.height&&n()});(0,u.useEffect)(()=>{if(!e)return;let n=null===t?null:t instanceof HTMLElement?t:t.current;if(!n)return;let o=(0,b.disposables)();if("u">typeof ResizeObserver){let e=new ResizeObserver(()=>r.current(n));e.observe(n),o.add(()=>e.disconnect())}if("u">typeof IntersectionObserver){let e=new IntersectionObserver(()=>r.current(n));e.observe(n),o.add(()=>e.disconnect())}return()=>o.dispose()},[t,r,e])}e.s(["useOnDisappear",0,I],877891);var L=e.i(652265);function T(){return/iPhone/gi.test(window.navigator.platform)||/Mac/gi.test(window.navigator.platform)&&window.navigator.maxTouchPoints>0}function D(e,t,n,r){let o=(0,M.useLatestValue)(n);(0,u.useEffect)(()=>{if(e)return document.addEventListener(t,n,r),()=>document.removeEventListener(t,n,r);function n(e){o.current(e)}},[e,t,r])}function F(e,t,n,r){let o=(0,M.useLatestValue)(n);(0,u.useEffect)(()=>{if(e)return window.addEventListener(t,n,r),()=>window.removeEventListener(t,n,r);function n(e){o.current(e)}},[e,t,r])}function k(e,t,n){let r=w(e,"outside-click"),o=(0,M.useLatestValue)(n),l=(0,u.useCallback)(function(e,n){if(e.defaultPrevented)return;let r=n(e);if(null!==r&&r.getRootNode().contains(r)&&r.isConnected){for(let n of function e(t){return"function"==typeof t?e(t()):Array.isArray(t)||t instanceof Set?t:[t]}(t))if(null!==n&&(n.contains(r)||e.composed&&e.composedPath().includes(n)))return;return(0,L.isFocusableElement)(r,L.FocusableMode.Loose)||-1===r.tabIndex||e.preventDefault(),o.current(e,r)}},[o,t]),i=(0,u.useRef)(null);D(r,"pointerdown",e=>{var t,n;i.current=(null==(n=null==(t=e.composedPath)?void 0:t.call(e))?void 0:n[0])||e.target},!0),D(r,"mousedown",e=>{var t,n;i.current=(null==(n=null==(t=e.composedPath)?void 0:t.call(e))?void 0:n[0])||e.target},!0),D(r,"click",e=>{T()||/Android/gi.test(window.navigator.userAgent)||i.current&&(l(e,()=>i.current),i.current=null)},!0);let s=(0,u.useRef)({x:0,y:0});D(r,"touchstart",e=>{s.current.x=e.touches[0].clientX,s.current.y=e.touches[0].clientY},!0),D(r,"touchend",e=>{let t={x:e.changedTouches[0].clientX,y:e.changedTouches[0].clientY};if(!(Math.abs(t.x-s.current.x)>=30||Math.abs(t.y-s.current.y)>=30))return l(e,()=>e.target instanceof HTMLElement?e.target:null)},!0),F(r,"blur",e=>l(e,()=>window.document.activeElement instanceof HTMLIFrameElement?window.document.activeElement:null),!0)}function A(...e){return(0,u.useMemo)(()=>(0,x.getOwnerDocument)(...e),[...e])}e.s(["useWindowEvent",0,F],401141),e.s(["useOutsideClick",0,k],952744),e.s(["useOwnerDocument",0,A],605083);var H=e.i(144279);let N=y(()=>new Map,{PUSH(e,t){var n;let r=null!=(n=this.get(e))?n:{doc:e,count:0,d:(0,b.disposables)(),meta:new Set};return r.count++,r.meta.add(t),this.set(e,r),this},POP(e,t){let n=this.get(e);return n&&(n.count--,n.meta.delete(t)),this},SCROLL_PREVENT({doc:e,d:t,meta:n}){let r,o={doc:e,d:t,meta:function(e){let t={};for(let n of e)Object.assign(t,n(t));return t}(n)},l=[T()?{before({doc:e,d:t,meta:n}){function r(e){return n.containers.flatMap(e=>e()).some(t=>t.contains(e))}t.microTask(()=>{var n;if("auto"!==window.getComputedStyle(e.documentElement).scrollBehavior){let n=(0,b.disposables)();n.style(e.documentElement,"scrollBehavior","auto"),t.add(()=>t.microTask(()=>n.dispose()))}let o=null!=(n=window.scrollY)?n:window.pageYOffset,l=null;t.addEventListener(e,"click",t=>{if(t.target instanceof HTMLElement)try{let n=t.target.closest("a");if(!n)return;let{hash:o}=new URL(n.href),i=e.querySelector(o);i&&!r(i)&&(l=i)}catch{}},!0),t.addEventListener(e,"touchstart",e=>{if(e.target instanceof HTMLElement)if(r(e.target)){let n=e.target;for(;n.parentElement&&r(n.parentElement);)n=n.parentElement;t.style(n,"overscrollBehavior","contain")}else t.style(e.target,"touchAction","none")}),t.addEventListener(e,"touchmove",e=>{if(e.target instanceof HTMLElement&&"INPUT"!==e.target.tagName)if(r(e.target)){let t=e.target;for(;t.parentElement&&""!==t.dataset.headlessuiPortal&&!(t.scrollHeight>t.clientHeight||t.scrollWidth>t.clientWidth);)t=t.parentElement;""===t.dataset.headlessuiPortal&&e.preventDefault()}else e.preventDefault()},{passive:!1}),t.add(()=>{var e;o!==(null!=(e=window.scrollY)?e:window.pageYOffset)&&window.scrollTo(0,o),l&&l.isConnected&&(l.scrollIntoView({block:"nearest"}),l=null)})})}}:{},{before({doc:e}){var t;let n=e.documentElement;r=Math.max(0,(null!=(t=e.defaultView)?t:window).innerWidth-n.clientWidth)},after({doc:e,d:t}){let n=e.documentElement,o=Math.max(0,n.clientWidth-n.offsetWidth),l=Math.max(0,r-o);t.style(n,"paddingRight",`${l}px`)}},{before({doc:e,d:t}){t.style(e.documentElement,"overflow","hidden")}}];l.forEach(({before:e})=>null==e?void 0:e(o)),l.forEach(({after:e})=>null==e?void 0:e(o))},SCROLL_ALLOW({d:e}){e.dispose()},TEARDOWN({doc:e}){this.delete(e)}});function B(e,t,n=()=>[document.body]){!function(e,t,n=()=>({containers:[]})){let r=S(N),o=t?r.get(t):void 0;o&&o.count,(0,m.useIsoMorphicEffect)(()=>{if(!(!t||!e))return N.dispatch("PUSH",t,n),()=>N.dispatch("POP",t,n)},[e,t])}(w(e,"scroll-lock"),t,e=>{var t;return{containers:[...null!=(t=e.containers)?t:[],n]}})}N.subscribe(()=>{let e=N.getSnapshot(),t=new Map;for(let[n]of e)t.set(n,n.documentElement.style.overflow);for(let n of e.values()){let e="hidden"===t.get(n.doc),r=0!==n.count;(r&&!e||!r&&e)&&N.dispatch(n.count>0?"SCROLL_PREVENT":"SCROLL_ALLOW",n),0===n.count&&N.dispatch("TEARDOWN",n)}}),e.s(["useScrollLock",0,B],101852);var K=e.i(294316);let _=/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;function W(e){var t,n;let r=null!=(t=e.innerText)?t:"",o=e.cloneNode(!0);if(!(o instanceof HTMLElement))return r;let l=!1;for(let e of o.querySelectorAll('[hidden],[aria-hidden],[role="img"]'))e.remove(),l=!0;let i=l?null!=(n=o.innerText)?n:"":r;return _.test(i)&&(i=i.replace(_,"")),i}function V(e){return[e.screenX,e.screenY]}var j=e.i(83733),$=e.i(601893),U=e.i(953760),z="u">typeof document?u.useLayoutEffect:function(){};function Q(e,t){let n,r,o;if(e===t)return!0;if(typeof e!=typeof t)return!1;if("function"==typeof e&&e.toString()===t.toString())return!0;if(e&&t&&"object"==typeof e){if(Array.isArray(e)){if((n=e.length)!==t.length)return!1;for(r=n;0!=r--;)if(!Q(e[r],t[r]))return!1;return!0}if((n=(o=Object.keys(e)).length)!==Object.keys(t).length)return!1;for(r=n;0!=r--;)if(!({}).hasOwnProperty.call(t,o[r]))return!1;for(r=n;0!=r--;){let n=o[r];if(("_owner"!==n||!e.$$typeof)&&!Q(e[n],t[n]))return!1}return!0}return e!=e&&t!=t}function Y(e){return"u"{t.current=e}),t}let X=(e,t)=>{let n=(0,U.offset)(e);return{name:n.name,fn:n.fn,options:[e,t]}};e.i(247167);var J=e.i(229315),Z=e.i(343084);e.i(397126);let ee={...u},et=ee.useInsertionEffect||(e=>e());function en(e){let t=u.useRef(()=>{});return et(()=>{t.current=e}),u.useCallback(function(){for(var e=arguments.length,n=Array(e),r=0;rtypeof document?u.useLayoutEffect:u.useEffect;let eo=!1,el=0,ei=()=>"floating-ui-"+Math.random().toString(36).slice(2,6)+el++,eu=ee.useId||function(){let[e,t]=u.useState(()=>eo?ei():void 0);return er(()=>{null==e&&t(ei())},[]),u.useEffect(()=>{eo=!0},[]),e},es=u.createContext(null),ea=u.createContext(null),ec="active",ed="selected";function ef(e,t,n){let r=new Map,o="item"===n,l=e;if(o&&e){let{[ec]:t,[ed]:n,...r}=e;l=r}return{..."floating"===n&&{tabIndex:-1,"data-floating-ui-focusable":""},...l,...t.map(t=>{let r=t?t[n]:null;return"function"==typeof r?e?r(e):null:r}).concat(e).reduce((e,t)=>(t&&Object.entries(t).forEach(t=>{let[n,l]=t;if(!(o&&[ec,ed].includes(n)))if(0===n.indexOf("on")){if(r.has(n)||r.set(n,[]),"function"==typeof l){var i;null==(i=r.get(n))||i.push(l),e[n]=function(){for(var e,t=arguments.length,o=Array(t),l=0;le(...o)).find(e=>void 0!==e)}}}else e[n]=l}),e),{})}}function ep(e,t){return{...e,rects:{...e.rects,floating:{...e.rects.floating,height:t}}}}let em=(0,u.createContext)({styles:void 0,setReference:()=>{},setFloating:()=>{},getReferenceProps:()=>({}),getFloatingProps:()=>({}),slot:{}});em.displayName="FloatingContext";let ev=(0,u.createContext)(null);function eg(e){return(0,u.useMemo)(()=>e?"string"==typeof e?{to:e}:e:null,[e])}function eh(){return(0,u.useContext)(em).setReference}function eb(){return(0,u.useContext)(em).getReferenceProps}function ex(){let{getFloatingProps:e,slot:t}=(0,u.useContext)(em);return(0,u.useCallback)((...n)=>Object.assign({},e(...n),{"data-anchor":t.anchor}),[e,t])}function eE(e=null){!1===e&&(e=null),"string"==typeof e&&(e={to:e});let t=(0,u.useContext)(ev),n=(0,u.useMemo)(()=>e,[JSON.stringify(e,(e,t)=>{var n;return null!=(n=null==t?void 0:t.outerHTML)?n:t})]);(0,m.useIsoMorphicEffect)(()=>{null==t||t(null!=n?n:null)},[t,n]);let r=(0,u.useContext)(em);return(0,u.useMemo)(()=>[r.setFloating,e?r.styles:{}],[r.setFloating,e,r.styles])}function ey({children:e,enabled:t=!0}){var n,r,o,l,i,s,a,d,f,p,v,g,b;let x,E,y,S,R,w,O,P,C,M,I,L,T,[D,F]=(0,u.useState)(null),[k,A]=(0,u.useState)(0),H=(0,u.useRef)(null),[N,B]=(0,u.useState)(null);d=N,(0,m.useIsoMorphicEffect)(()=>{if(!d)return;let e=new MutationObserver(()=>{let e=window.getComputedStyle(d).maxHeight,t=parseFloat(e);if(isNaN(t))return;let n=parseInt(e);isNaN(n)||t!==n&&(d.style.maxHeight=`${Math.ceil(t)}px`)});return e.observe(d,{attributes:!0,attributeFilter:["style"]}),()=>{e.disconnect()}},[d]);let K=t&&null!==D&&null!==N,{to:_="bottom",gap:W=0,offset:V=0,padding:j=0,inner:$}=(f=D,p=N,x=eS(null!=(v=null==f?void 0:f.gap)?v:"var(--anchor-gap, 0)",p),E=eS(null!=(g=null==f?void 0:f.offset)?g:"var(--anchor-offset, 0)",p),y=eS(null!=(b=null==f?void 0:f.padding)?b:"var(--anchor-padding, 0)",p),{...f,gap:x,offset:E,padding:y}),[ee,et="center"]=_.split(" ");(0,m.useIsoMorphicEffect)(()=>{K&&A(0)},[K]);let{refs:eo,floatingStyles:el,context:ei}=function(e){void 0===e&&(e={});let{nodeId:t}=e,n=function(e){var t;let{open:n=!1,onOpenChange:r,elements:o}=e,l=eu(),i=u.useRef({}),[s]=u.useState(()=>{let e;return e=new Map,{emit(t,n){var r;null==(r=e.get(t))||r.forEach(e=>e(n))},on(t,n){e.set(t,[...e.get(t)||[],n])},off(t,n){var r;e.set(t,(null==(r=e.get(t))?void 0:r.filter(e=>e!==n))||[])}}}),a=null!=((null==(t=u.useContext(es))?void 0:t.id)||null),[c,d]=u.useState(o.reference),f=en((e,t,n)=>{i.current.openEvent=e?t:void 0,s.emit("openchange",{open:e,event:t,reason:n,nested:a}),null==r||r(e,t,n)}),p=u.useMemo(()=>({setPositionReference:d}),[]),m=u.useMemo(()=>({reference:c||o.reference||null,floating:o.floating||null,domReference:o.reference}),[c,o.reference,o.floating]);return u.useMemo(()=>({dataRef:i,open:n,onOpenChange:f,elements:m,events:s,floatingId:l,refs:p}),[n,f,m,s,l,p])}({...e,elements:{reference:null,floating:null,...e.elements}}),r=e.rootContext||n,o=r.elements,[l,i]=u.useState(null),[s,a]=u.useState(null),d=(null==o?void 0:o.domReference)||l,f=u.useRef(null),p=u.useContext(ea);er(()=>{d&&(f.current=d)},[d]);let m=function(e){void 0===e&&(e={});let{placement:t="bottom",strategy:n="absolute",middleware:r=[],platform:o,elements:{reference:l,floating:i}={},transform:s=!0,whileElementsMounted:a,open:d}=e,[f,p]=u.useState({x:0,y:0,strategy:n,placement:t,middlewareData:{},isPositioned:!1}),[m,v]=u.useState(r);Q(m,r)||v(r);let[g,h]=u.useState(null),[b,x]=u.useState(null),E=u.useCallback(e=>{e!==w.current&&(w.current=e,h(e))},[]),y=u.useCallback(e=>{e!==O.current&&(O.current=e,x(e))},[]),S=l||g,R=i||b,w=u.useRef(null),O=u.useRef(null),P=u.useRef(f),C=null!=a,M=G(a),I=G(o),L=G(d),T=u.useCallback(()=>{if(!w.current||!O.current)return;let e={placement:t,strategy:n,middleware:m};I.current&&(e.platform=I.current),(0,U.computePosition)(w.current,O.current,e).then(e=>{let t={...e,isPositioned:!1!==L.current};D.current&&!Q(P.current,t)&&(P.current=t,c.flushSync(()=>{p(t)}))})},[m,t,n,I,L]);z(()=>{!1===d&&P.current.isPositioned&&(P.current.isPositioned=!1,p(e=>({...e,isPositioned:!1})))},[d]);let D=u.useRef(!1);z(()=>(D.current=!0,()=>{D.current=!1}),[]),z(()=>{if(S&&(w.current=S),R&&(O.current=R),S&&R){if(M.current)return M.current(S,R,T);T()}},[S,R,T,M,C]);let F=u.useMemo(()=>({reference:w,floating:O,setReference:E,setFloating:y}),[E,y]),k=u.useMemo(()=>({reference:S,floating:R}),[S,R]),A=u.useMemo(()=>{let e={position:n,left:0,top:0};if(!k.floating)return e;let t=q(k.floating,f.x),r=q(k.floating,f.y);return s?{...e,transform:"translate("+t+"px, "+r+"px)",...Y(k.floating)>=1.5&&{willChange:"transform"}}:{position:n,left:t,top:r}},[n,s,k.floating,f.x,f.y]);return u.useMemo(()=>({...f,update:T,refs:F,elements:k,floatingStyles:A}),[f,T,F,k,A])}({...e,elements:{...o,...s&&{reference:s}}}),v=u.useCallback(e=>{let t=(0,J.isElement)(e)?{getBoundingClientRect:()=>e.getBoundingClientRect(),contextElement:e}:e;a(t),m.refs.setReference(t)},[m.refs]),g=u.useCallback(e=>{((0,J.isElement)(e)||null===e)&&(f.current=e,i(e)),((0,J.isElement)(m.refs.reference.current)||null===m.refs.reference.current||null!==e&&!(0,J.isElement)(e))&&m.refs.setReference(e)},[m.refs]),h=u.useMemo(()=>({...m.refs,setReference:g,setPositionReference:v,domReference:f}),[m.refs,g,v]),b=u.useMemo(()=>({...m.elements,domReference:d}),[m.elements,d]),x=u.useMemo(()=>({...m,...r,refs:h,elements:b,nodeId:t}),[m,h,b,t,r]);return er(()=>{r.dataRef.current.floatingContext=x;let e=null==p?void 0:p.nodesRef.current.find(e=>e.id===t);e&&(e.context=x)}),u.useMemo(()=>({...m,context:x,refs:h,elements:b}),[m,h,b,x])}({open:K,placement:"selection"===ee?"center"===et?"bottom":`bottom-${et}`:"center"===et?`${ee}`:`${ee}-${et}`,strategy:"absolute",transform:!1,middleware:[X({mainAxis:"selection"===ee?0:W,crossAxis:V}),(n={padding:j},{name:(S=(0,U.shift)(n)).name,fn:S.fn,options:[n,r]}),"selection"!==ee&&(o={padding:j},{name:(R=(0,U.flip)(o)).name,fn:R.fn,options:[o,l]}),"selection"===ee&&$?{name:"inner",options:w={...$,padding:j,overflowRef:H,offset:k,minItemsVisible:4,referenceOverflowThreshold:j,onFallbackChange(e){var t,n;if(!e)return;let r=ei.elements.floating;if(!r)return;let o=parseFloat(getComputedStyle(r).scrollPaddingBottom)||0,l=Math.min(4,r.childElementCount),i=0,u=0;for(let e of null!=(n=null==(t=ei.elements.floating)?void 0:t.childNodes)?n:[])if(e instanceof HTMLElement){let t=e.offsetTop,n=t+e.clientHeight+o,s=r.scrollTop,a=s+r.clientHeight;if(t>=s&&n<=a)l--;else{u=Math.max(0,Math.min(n,a)-Math.max(t,s)),i=e.clientHeight;break}}l>=1&&A(e=>{let t=i*l-u+o;return e>=t?e:t})}},async fn(e){let{listRef:t,overflowRef:n,onFallbackChange:r,offset:o=0,index:l=0,minItemsVisible:i=4,referenceOverflowThreshold:u=0,scrollRef:s,...a}=(0,Z.evaluate)(w,e),{rects:d,elements:{floating:f}}=e,p=t.current[l],m=(null==s?void 0:s.current)||f,v=f.clientTop||m.clientTop,g=0!==f.clientTop,h=0!==m.clientTop,b=f===m;if(!p)return{};let x={...e,...await X(-p.offsetTop-f.clientTop-d.reference.height/2-p.offsetHeight/2-o).fn(e)},E=await (0,U.detectOverflow)(ep(x,m.scrollHeight+v+f.clientTop),a),y=await (0,U.detectOverflow)(x,{...a,elementContext:"reference"}),S=(0,Z.max)(0,E.top),R=x.y+S,O=(m.scrollHeight>m.clientHeight?e=>e:Z.round)((0,Z.max)(0,m.scrollHeight+(g&&b||h?2*v:0)-S-(0,Z.max)(0,E.bottom)));if(m.style.maxHeight=O+"px",m.scrollTop=S,r){let e=m.offsetHeight=-u||y.bottom>=-u;c.flushSync(()=>r(e))}return n&&(n.current=await (0,U.detectOverflow)(ep({...x,y:R},m.offsetHeight+v+f.clientTop),a)),{y:R}}}:null,(i={padding:j,apply({availableWidth:e,availableHeight:t,elements:n}){Object.assign(n.floating.style,{overflow:"auto",maxWidth:`${e}px`,maxHeight:`min(var(--anchor-max-height, 100vh), ${t}px)`})}},{name:(O=(0,U.size)(i)).name,fn:O.fn,options:[i,s]})].filter(Boolean),whileElementsMounted:U.autoUpdate}),[ec=ee,ed=et]=ei.placement.split("-");"selection"===ee&&(ec="selection");let eg=(0,u.useMemo)(()=>({anchor:[ec,ed].filter(Boolean).join(" ")}),[ec,ed]),{getReferenceProps:eh,getFloatingProps:eb}=(P=(a=[function(e,t){let{open:n,elements:r}=e,{enabled:o=!0,overflowRef:l,scrollRef:i,onChange:s}=t,a=en(s),d=u.useRef(!1),f=u.useRef(null),p=u.useRef(null);u.useEffect(()=>{if(!o)return;function e(e){if(e.ctrlKey||!t||null==l.current)return;let n=e.deltaY,r=l.current.top>=-.5,o=l.current.bottom>=-.5,i=t.scrollHeight-t.clientHeight,u=n<0?-1:1,s=n<0?"max":"min";if(!(t.scrollHeight<=t.clientHeight))if(!r&&n>0||!o&&n<0)e.preventDefault(),c.flushSync(()=>{a(e=>e+Math[s](n,i*u))});else{let e;/firefox/i.test((e=navigator.userAgentData)&&Array.isArray(e.brands)?e.brands.map(e=>{let{brand:t,version:n}=e;return t+"/"+n}).join(" "):navigator.userAgent)&&(t.scrollTop+=n)}}let t=(null==i?void 0:i.current)||r.floating;if(n&&t)return t.addEventListener("wheel",e),requestAnimationFrame(()=>{f.current=t.scrollTop,null!=l.current&&(p.current={...l.current})}),()=>{f.current=null,p.current=null,t.removeEventListener("wheel",e)}},[o,n,r.floating,l,i,a]);let m=u.useMemo(()=>({onKeyDown(){d.current=!0},onWheel(){d.current=!1},onPointerMove(){d.current=!1},onScroll(){let e=(null==i?void 0:i.current)||r.floating;if(l.current&&e&&d.current){if(null!==f.current){let t=e.scrollTop-f.current;(l.current.bottom<-.5&&t<-1||l.current.top<-.5&&t>1)&&c.flushSync(()=>a(e=>e+t))}requestAnimationFrame(()=>{f.current=e.scrollTop})}}}),[r.floating,a,l,i]);return u.useMemo(()=>o?{floating:m}:{},[o,m])}(ei,{overflowRef:H,onChange:A})]).map(e=>null==e?void 0:e.reference),C=a.map(e=>null==e?void 0:e.floating),M=a.map(e=>null==e?void 0:e.item),I=u.useCallback(e=>ef(e,a,"reference"),P),L=u.useCallback(e=>ef(e,a,"floating"),C),T=u.useCallback(e=>ef(e,a,"item"),M),u.useMemo(()=>({getReferenceProps:I,getFloatingProps:L,getItemProps:T}),[I,L,T])),ex=(0,h.useEvent)(e=>{B(e),eo.setFloating(e)});return u.createElement(ev.Provider,{value:F},u.createElement(em.Provider,{value:{setFloating:ex,setReference:eo.setReference,styles:el,getReferenceProps:eh,getFloatingProps:eb,slot:eg}},e))}function eS(e,t,n){let r=(0,v.useDisposables)(),o=(0,h.useEvent)((e,t)=>{if(null==e)return[n,null];if("number"==typeof e)return[e,null];if("string"==typeof e){if(!t)return[n,null];let o=eR(e,t);return[o,n=>{let l=function e(t){let n=/var\((.*)\)/.exec(t);if(n){let t=n[1].indexOf(",");if(-1===t)return[n[1]];let r=n[1].slice(0,t).trim(),o=n[1].slice(t+1).trim();return o?[r,...e(o)]:[r]}return[]}(e);{let i=l.map(e=>window.getComputedStyle(t).getPropertyValue(e));r.requestAnimationFrame(function u(){r.nextFrame(u);let s=!1;for(let[e,n]of l.entries()){let r=window.getComputedStyle(t).getPropertyValue(n);if(i[e]!==r){i[e]=r,s=!0;break}}if(!s)return;let a=eR(e,t);o!==a&&(n(a),o=a)})}return r.dispose}]}return[n,null]}),l=(0,u.useMemo)(()=>o(e,t)[0],[e,t]),[i=l,s]=(0,u.useState)();return(0,m.useIsoMorphicEffect)(()=>{let[n,r]=o(e,t);if(s(n),r)return r(s)},[e,t]),i}function eR(e,t){let n=document.createElement("div");t.appendChild(n),n.style.setProperty("margin-top","0px","important"),n.style.setProperty("margin-top",e,"important");let r=parseFloat(window.getComputedStyle(n).marginTop)||0;return t.removeChild(n),r}ev.displayName="PlacementContext",e.s(["FloatingProvider",0,ey,"useFloatingPanel",0,eE,"useFloatingPanelProps",0,ex,"useFloatingReference",0,eh,"useFloatingReferenceProps",0,eb,"useResolvedAnchor",0,eg],919751);var ew=e.i(140721),eO=e.i(942803),eP=e.i(233137),eC=e.i(233538),eM=((t=eM||{})[t.First=0]="First",t[t.Previous=1]="Previous",t[t.Next=2]="Next",t[t.Last=3]="Last",t[t.Specific=4]="Specific",t[t.Nothing=5]="Nothing",t);function eI(e,t){let n=t.resolveItems();if(n.length<=0)return null;let r=t.resolveActiveIndex(),o=null!=r?r:-1;switch(e.focus){case 0:for(let e=0;e=0;--e)if(!t.resolveDisabled(n[e],e,n))return e;return r;case 2:for(let e=o+1;e=0;--e)if(!t.resolveDisabled(n[e],e,n))return e;return r;case 4:for(let r=0;r()=>{},()=>!1,()=>!e)),[n,r]=u.useState(eN.env.isHandoffComplete);return n&&!1===eN.env.isHandoffComplete&&r(!1),u.useEffect(()=>{!0!==n&&r(!0)},[n]),u.useEffect(()=>eN.env.handoff(),[]),!t&&n}e.s(["useServerHandoffComplete",0,eB],178677);let eK=(0,u.createContext)(!1),e_=u.Fragment,eW=(0,eD.forwardRefWithAs)(function(e,t){let n,r,o=(0,u.useRef)(null),l=(0,K.useSyncRefs)((0,K.optionalRef)(e=>{o.current=e}),t),i=A(o),s=function(e){let t=(0,u.useContext)(eK),n=(0,u.useContext)(ej),r=A(e),[o,l]=(0,u.useState)(()=>{var e;if(!t&&null!==n)return null!=(e=n.current)?e:null;if(eN.env.isServer)return null;let o=null==r?void 0:r.getElementById("headlessui-portal-root");if(o)return o;if(null===r)return null;let l=r.createElement("div");return l.setAttribute("id","headlessui-portal-root"),r.body.appendChild(l)});return(0,u.useEffect)(()=>{null!==o&&(null!=r&&r.body.contains(o)||null==r||r.body.appendChild(o))},[o,r]),(0,u.useEffect)(()=>{t||null!==n&&l(n.current)},[n,l,t]),o}(o),[a]=(0,u.useState)(()=>{var e;return eN.env.isServer?null:null!=(e=null==i?void 0:i.createElement("div"))?e:null}),d=(0,u.useContext)(e$),f=eB();(0,m.useIsoMorphicEffect)(()=>{!s||!a||s.contains(a)||(a.setAttribute("data-headlessui-portal",""),s.appendChild(a))},[s,a]),(0,m.useIsoMorphicEffect)(()=>{if(a&&d)return d.register(a)},[d,a]),n=(0,h.useEvent)(()=>{var e;s&&a&&(a instanceof Node&&s.contains(a)&&s.removeChild(a),s.childNodes.length<=0&&(null==(e=s.parentElement)||e.removeChild(s)))}),r=(0,u.useRef)(!1),(0,u.useEffect)(()=>(r.current=!1,()=>{r.current=!0,(0,eH.microTask)(()=>{r.current&&n()})}),[n]);let p=(0,eD.useRender)();return f&&s&&a?(0,c.createPortal)(p({ourProps:{ref:l},theirProps:e,slot:{},defaultTag:e_,name:"Portal"}),a):null}),eV=u.Fragment,ej=(0,u.createContext)(null),e$=(0,u.createContext)(null),eU=Object.assign((0,eD.forwardRefWithAs)(function(e,t){let n=(0,K.useSyncRefs)(t),{enabled:r=!0,...o}=e,l=(0,eD.useRender)();return r?u.default.createElement(eW,{...o,ref:n}):l({ourProps:{ref:n},theirProps:o,slot:{},defaultTag:e_,name:"Portal"})}),{Group:(0,eD.forwardRefWithAs)(function(e,t){let{target:n,...r}=e,o={ref:(0,K.useSyncRefs)(t)},l=(0,eD.useRender)();return u.default.createElement(ej.Provider,{value:n},l({ourProps:o,theirProps:r,defaultTag:eV,name:"Popover.Group"}))})});e.s(["Portal",0,eU,"useNestedPortals",0,function(){let e=(0,u.useContext)(e$),t=(0,u.useRef)([]),n=(0,h.useEvent)(n=>(t.current.push(n),e&&e.register(n),()=>r(n))),r=(0,h.useEvent)(n=>{let r=t.current.indexOf(n);-1!==r&&t.current.splice(r,1),e&&e.unregister(n)}),o=(0,u.useMemo)(()=>({register:n,unregister:r,portals:t}),[n,r,t]);return[t,(0,u.useMemo)(()=>function({children:e}){return u.default.createElement(e$.Provider,{value:o},e)},[o])]}],635307);var ez=((n=ez||{})[n.Open=0]="Open",n[n.Closed=1]="Closed",n),eQ=((r=eQ||{})[r.Single=0]="Single",r[r.Multi=1]="Multi",r),eY=((o=eY||{})[o.Pointer=0]="Pointer",o[o.Other=1]="Other",o),eq=((l=eq||{})[l.OpenListbox=0]="OpenListbox",l[l.CloseListbox=1]="CloseListbox",l[l.GoToOption=2]="GoToOption",l[l.Search=3]="Search",l[l.ClearSearch=4]="ClearSearch",l[l.RegisterOption=5]="RegisterOption",l[l.UnregisterOption=6]="UnregisterOption",l[l.SetButtonElement=7]="SetButtonElement",l[l.SetOptionsElement=8]="SetOptionsElement",l);function eG(e,t=e=>e){let n=null!==e.activeOptionIndex?e.options[e.activeOptionIndex]:null,r=(0,L.sortByDomNode)(t(e.options.slice()),e=>e.dataRef.current.domRef.current),o=n?r.indexOf(n):null;return -1===o&&(o=null),{options:r,activeOptionIndex:o}}let eX={1:e=>e.dataRef.current.disabled||1===e.listboxState?e:{...e,activeOptionIndex:null,listboxState:1,__demoMode:!1},0(e){if(e.dataRef.current.disabled||0===e.listboxState)return e;let t=e.activeOptionIndex,{isSelected:n}=e.dataRef.current,r=e.options.findIndex(e=>n(e.dataRef.current.value));return -1!==r&&(t=r),{...e,listboxState:0,activeOptionIndex:t,__demoMode:!1}},2(e,t){var n,r,o,l,i;if(e.dataRef.current.disabled||1===e.listboxState)return e;let u={...e,searchQuery:"",activationTrigger:null!=(n=t.trigger)?n:1,__demoMode:!1};if(t.focus===eM.Nothing)return{...u,activeOptionIndex:null};if(t.focus===eM.Specific)return{...u,activeOptionIndex:e.options.findIndex(e=>e.id===t.id)};if(t.focus===eM.Previous){let n=e.activeOptionIndex;if(null!==n){let l=e.options[n].dataRef.current.domRef,i=eI(t,{resolveItems:()=>e.options,resolveActiveIndex:()=>e.activeOptionIndex,resolveId:e=>e.id,resolveDisabled:e=>e.dataRef.current.disabled});if(null!==i){let t=e.options[i].dataRef.current.domRef;if((null==(r=l.current)?void 0:r.previousElementSibling)===t.current||(null==(o=t.current)?void 0:o.previousElementSibling)===null)return{...u,activeOptionIndex:i}}}}else if(t.focus===eM.Next){let n=e.activeOptionIndex;if(null!==n){let r=e.options[n].dataRef.current.domRef,o=eI(t,{resolveItems:()=>e.options,resolveActiveIndex:()=>e.activeOptionIndex,resolveId:e=>e.id,resolveDisabled:e=>e.dataRef.current.disabled});if(null!==o){let t=e.options[o].dataRef.current.domRef;if((null==(l=r.current)?void 0:l.nextElementSibling)===t.current||(null==(i=t.current)?void 0:i.nextElementSibling)===null)return{...u,activeOptionIndex:o}}}}let s=eG(e),a=eI(t,{resolveItems:()=>s.options,resolveActiveIndex:()=>s.activeOptionIndex,resolveId:e=>e.id,resolveDisabled:e=>e.dataRef.current.disabled});return{...u,...s,activeOptionIndex:a}},3:(e,t)=>{if(e.dataRef.current.disabled||1===e.listboxState)return e;let n=+(""===e.searchQuery),r=e.searchQuery+t.value.toLowerCase(),o=(null!==e.activeOptionIndex?e.options.slice(e.activeOptionIndex+n).concat(e.options.slice(0,e.activeOptionIndex+n)):e.options).find(e=>{var t;return!e.dataRef.current.disabled&&(null==(t=e.dataRef.current.textValue)?void 0:t.startsWith(r))}),l=o?e.options.indexOf(o):-1;return -1===l||l===e.activeOptionIndex?{...e,searchQuery:r}:{...e,searchQuery:r,activeOptionIndex:l,activationTrigger:1}},4:e=>e.dataRef.current.disabled||1===e.listboxState||""===e.searchQuery?e:{...e,searchQuery:""},5:(e,t)=>{let n={id:t.id,dataRef:t.dataRef},r=eG(e,e=>[...e,n]);return null===e.activeOptionIndex&&e.dataRef.current.isSelected(t.dataRef.current.value)&&(r.activeOptionIndex=r.options.indexOf(n)),{...e,...r}},6:(e,t)=>{let n=eG(e,e=>{let n=e.findIndex(e=>e.id===t.id);return -1!==n&&e.splice(n,1),e});return{...e,...n,activationTrigger:1}},7:(e,t)=>e.buttonElement===t.element?e:{...e,buttonElement:t.element},8:(e,t)=>e.optionsElement===t.element?e:{...e,optionsElement:t.element}},eJ=(0,u.createContext)(null);function eZ(e){let t=(0,u.useContext)(eJ);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,eZ),t}return t}eJ.displayName="ListboxActionsContext";let e0=(0,u.createContext)(null);function e1(e){let t=(0,u.useContext)(e0);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,e1),t}return t}function e3(e,t){return(0,eT.match)(t.type,eX,e,t)}e0.displayName="ListboxDataContext";let e7=u.Fragment,e4=(0,u.createContext)(!1),e5=eD.RenderFeatures.RenderStrategy|eD.RenderFeatures.Static,e2=u.Fragment,e8=(0,eD.forwardRefWithAs)(function(e,t){var n;let r=(0,$.useDisabled)(),{value:o,defaultValue:l,form:i,name:s,onChange:a,by:c,invalid:d=!1,disabled:g=r||!1,horizontal:b=!1,multiple:x=!1,__demoMode:E=!1,...y}=e,S=b?"horizontal":"vertical",R=(0,K.useSyncRefs)(t),w=(0,p.useDefaultValue)(l),[O=x?[]:void 0,P]=(0,f.useControllable)(o,a,w),[C,M]=(0,u.useReducer)(e3,{dataRef:(0,u.createRef)(),listboxState:+!E,options:[],searchQuery:"",activeOptionIndex:null,activationTrigger:1,optionsVisible:!1,buttonElement:null,optionsElement:null,__demoMode:E}),I=(0,u.useRef)({static:!1,hold:!1}),T=(0,u.useRef)(new Map),D=function(e=function(e,t){return null!==e&&null!==t&&"object"==typeof e&&"object"==typeof t&&"id"in e&&"id"in t?e.id===t.id:e===t}){return(0,u.useCallback)((t,n)=>"string"==typeof e?(null==t?void 0:t[e])===(null==n?void 0:n[e]):e(t,n),[e])}(c),F=(0,u.useCallback)(e=>(0,eT.match)(A.mode,{1:()=>O.some(t=>D(t,e)),0:()=>D(O,e)}),[O]),A=(0,u.useMemo)(()=>({...C,value:O,disabled:g,invalid:d,mode:+!!x,orientation:S,compare:D,isSelected:F,optionsPropsRef:I,listRef:T}),[O,g,d,x,C,T]);(0,m.useIsoMorphicEffect)(()=>{C.dataRef.current=A},[A]),k(0===A.listboxState,[A.buttonElement,A.optionsElement],(e,t)=>{var n;M({type:1}),(0,L.isFocusableElement)(t,L.FocusableMode.Loose)||(e.preventDefault(),null==(n=A.buttonElement)||n.focus())});let H=(0,u.useMemo)(()=>({open:0===A.listboxState,disabled:g,invalid:d,value:O}),[A,g,O,d]),N=(0,h.useEvent)(e=>{let t=A.options.find(t=>t.id===e);t&&z(t.dataRef.current.value)}),B=(0,h.useEvent)(()=>{if(null!==A.activeOptionIndex){let{dataRef:e,id:t}=A.options[A.activeOptionIndex];z(e.current.value),M({type:2,focus:eM.Specific,id:t})}}),_=(0,h.useEvent)(()=>M({type:0})),W=(0,h.useEvent)(()=>M({type:1})),V=(0,v.useDisposables)(),j=(0,h.useEvent)((e,t,n)=>{V.dispose(),V.microTask(()=>e===eM.Specific?M({type:2,focus:eM.Specific,id:t,trigger:n}):M({type:2,focus:e,trigger:n}))}),U=(0,h.useEvent)((e,t)=>(M({type:5,id:e,dataRef:t}),()=>M({type:6,id:e}))),z=(0,h.useEvent)(e=>(0,eT.match)(A.mode,{0:()=>null==P?void 0:P(e),1(){let t=A.value.slice(),n=t.findIndex(t=>D(t,e));return -1===n?t.push(e):t.splice(n,1),null==P?void 0:P(t)}})),Q=(0,h.useEvent)(e=>M({type:3,value:e})),Y=(0,h.useEvent)(()=>M({type:4})),q=(0,h.useEvent)(e=>{M({type:7,element:e})}),G=(0,h.useEvent)(e=>{M({type:8,element:e})}),X=(0,u.useMemo)(()=>({onChange:z,registerOption:U,goToOption:j,closeListbox:W,openListbox:_,selectActiveOption:B,selectOption:N,search:Q,clearSearch:Y,setButtonElement:q,setOptionsElement:G}),[]),[J,Z]=(0,eA.useLabels)({inherit:!0}),ee=(0,u.useCallback)(()=>{if(void 0!==w)return null==P?void 0:P(w)},[P,w]),et=(0,eD.useRender)();return u.default.createElement(Z,{value:J,props:{htmlFor:null==(n=A.buttonElement)?void 0:n.id},slot:{open:0===A.listboxState,disabled:g}},u.default.createElement(ey,null,u.default.createElement(eJ.Provider,{value:X},u.default.createElement(e0.Provider,{value:A},u.default.createElement(eP.OpenClosedProvider,{value:(0,eT.match)(A.listboxState,{0:eP.State.Open,1:eP.State.Closed})},null!=s&&null!=O&&u.default.createElement(ew.FormFields,{disabled:g,data:{[s]:O},form:i,onReset:ee}),et({ourProps:{ref:R},theirProps:y,slot:H,defaultTag:e7,name:"Listbox"}))))))}),e9=(0,eD.forwardRefWithAs)(function(e,t){var n;let r=e1("Listbox.Button"),o=eZ("Listbox.Button"),l=(0,u.useId)(),i=(0,eO.useProvidedId)(),{id:f=i||`headlessui-listbox-button-${l}`,disabled:p=r.disabled||!1,autoFocus:m=!1,...v}=e,g=(0,K.useSyncRefs)(t,eh(),o.setButtonElement),b=eb(),x=(0,h.useEvent)(e=>{switch(e.key){case ek.Keys.Enter:(0,eL.attemptSubmit)(e.currentTarget);break;case ek.Keys.Space:case ek.Keys.ArrowDown:e.preventDefault(),(0,c.flushSync)(()=>o.openListbox()),r.value||o.goToOption(eM.First);break;case ek.Keys.ArrowUp:e.preventDefault(),(0,c.flushSync)(()=>o.openListbox()),r.value||o.goToOption(eM.Last)}}),E=(0,h.useEvent)(e=>{e.key===ek.Keys.Space&&e.preventDefault()}),y=(0,h.useEvent)(e=>{var t;if((0,eC.isDisabledReactIssue7711)(e.currentTarget))return e.preventDefault();0===r.listboxState?((0,c.flushSync)(()=>o.closeListbox()),null==(t=r.buttonElement)||t.focus({preventScroll:!0})):(e.preventDefault(),o.openListbox())}),S=(0,h.useEvent)(e=>e.preventDefault()),R=(0,eA.useLabelledBy)([f]),w=(0,eF.useDescribedBy)(),{isFocusVisible:O,focusProps:P}=(0,s.useFocusRing)({autoFocus:m}),{isHovered:C,hoverProps:M}=(0,a.useHover)({isDisabled:p}),{pressed:I,pressProps:L}=(0,d.useActivePress)({disabled:p}),T=(0,u.useMemo)(()=>({open:0===r.listboxState,active:I||0===r.listboxState,disabled:p,invalid:r.invalid,value:r.value,hover:C,focus:O,autofocus:m}),[r.listboxState,r.value,p,C,O,I,r.invalid,m]),D=(0,eD.mergeProps)(b(),{ref:g,id:f,type:(0,H.useResolveButtonType)(e,r.buttonElement),"aria-haspopup":"listbox","aria-controls":null==(n=r.optionsElement)?void 0:n.id,"aria-expanded":0===r.listboxState,"aria-labelledby":R,"aria-describedby":w,disabled:p||void 0,autoFocus:m,onKeyDown:x,onKeyUp:E,onKeyPress:S,onClick:y},P,M,L);return(0,eD.useRender)()({ourProps:D,theirProps:v,slot:T,defaultTag:"button",name:"Listbox.Button"})}),e6=eA.Label,te=(0,eD.forwardRefWithAs)(function(e,t){var n,r;let o=(0,u.useId)(),{id:l=`headlessui-listbox-options-${o}`,anchor:i,portal:s=!1,modal:a=!0,transition:d=!1,...f}=e,p=eg(i),[E,y]=(0,u.useState)(null);p&&(s=!0);let S=e1("Listbox.Options"),R=eZ("Listbox.Options"),O=A(S.optionsElement),P=(0,eP.useOpenClosed)(),[M,T]=(0,j.useTransition)(d,E,null!==P?(P&eP.State.Open)===eP.State.Open:0===S.listboxState);I(M,S.buttonElement,R.closeListbox),B(!S.__demoMode&&a&&0===S.listboxState,O),function(e,{allowed:t,disallowed:n}={}){let r=w(e,"inert-others");(0,m.useIsoMorphicEffect)(()=>{var e,o;if(!r)return;let l=(0,b.disposables)();for(let t of null!=(e=null==n?void 0:n())?e:[])t&&l.add(C(t));let i=null!=(o=null==t?void 0:t())?o:[];for(let e of i){if(!e)continue;let t=(0,x.getOwnerDocument)(e);if(!t)continue;let n=e.parentElement;for(;n&&n!==t.body;){for(let e of n.children)i.some(t=>e.contains(t))||l.add(C(e));n=n.parentElement}}return l.dispose},[r,t,n])}(!S.__demoMode&&a&&0===S.listboxState,{allowed:(0,u.useCallback)(()=>[S.buttonElement,S.optionsElement],[S.buttonElement,S.optionsElement])});let D=!function(e,t){let n=(0,u.useRef)({left:0,top:0});if((0,m.useIsoMorphicEffect)(()=>{if(!t)return;let e=t.getBoundingClientRect();e&&(n.current=e)},[e,t]),null==t||!e||t===document.activeElement)return!1;let r=t.getBoundingClientRect();return r.top!==n.current.top||r.left!==n.current.left}(0!==S.listboxState,S.buttonElement)&&M,F=function(e,t){let[n,r]=(0,u.useState)(t);return e||n===t||r(t),e?n:t}(M&&1===S.listboxState,S.value),k=(0,h.useEvent)(e=>S.compare(F,e)),H=(0,u.useMemo)(()=>{var e;if(null==p||!(null!=(e=null==p?void 0:p.to)&&e.includes("selection")))return null;let t=S.options.findIndex(e=>k(e.dataRef.current.value));return -1===t&&(t=0),t},[p,S.options]),[N,_]=eE((()=>{if(null==p)return;if(null===H)return{...p,inner:void 0};let e=Array.from(S.listRef.current.values());return{...p,inner:{listRef:{current:e},index:H}}})()),W=ex(),V=(0,K.useSyncRefs)(t,p?N:null,R.setOptionsElement,y),$=(0,v.useDisposables)();(0,u.useEffect)(()=>{var e;let t=S.optionsElement;t&&0===S.listboxState&&t!==(null==(e=(0,x.getOwnerDocument)(t))?void 0:e.activeElement)&&(null==t||t.focus({preventScroll:!0}))},[S.listboxState,S.optionsElement]);let U=(0,h.useEvent)(e=>{var t,n;switch($.dispose(),e.key){case ek.Keys.Space:if(""!==S.searchQuery)return e.preventDefault(),e.stopPropagation(),R.search(e.key);case ek.Keys.Enter:if(e.preventDefault(),e.stopPropagation(),null!==S.activeOptionIndex){let{dataRef:e}=S.options[S.activeOptionIndex];R.onChange(e.current.value)}0===S.mode&&((0,c.flushSync)(()=>R.closeListbox()),null==(t=S.buttonElement)||t.focus({preventScroll:!0}));break;case(0,eT.match)(S.orientation,{vertical:ek.Keys.ArrowDown,horizontal:ek.Keys.ArrowRight}):return e.preventDefault(),e.stopPropagation(),R.goToOption(eM.Next);case(0,eT.match)(S.orientation,{vertical:ek.Keys.ArrowUp,horizontal:ek.Keys.ArrowLeft}):return e.preventDefault(),e.stopPropagation(),R.goToOption(eM.Previous);case ek.Keys.Home:case ek.Keys.PageUp:return e.preventDefault(),e.stopPropagation(),R.goToOption(eM.First);case ek.Keys.End:case ek.Keys.PageDown:return e.preventDefault(),e.stopPropagation(),R.goToOption(eM.Last);case ek.Keys.Escape:e.preventDefault(),e.stopPropagation(),(0,c.flushSync)(()=>R.closeListbox()),null==(n=S.buttonElement)||n.focus({preventScroll:!0});return;case ek.Keys.Tab:e.preventDefault(),e.stopPropagation(),(0,c.flushSync)(()=>R.closeListbox()),(0,L.focusFrom)(S.buttonElement,e.shiftKey?L.Focus.Previous:L.Focus.Next);break;default:1===e.key.length&&(R.search(e.key),$.setTimeout(()=>R.clearSearch(),350))}}),z=null==(n=S.buttonElement)?void 0:n.id,Q=(0,u.useMemo)(()=>({open:0===S.listboxState}),[S.listboxState]),Y=(0,eD.mergeProps)(p?W():{},{id:l,ref:V,"aria-activedescendant":null===S.activeOptionIndex||null==(r=S.options[S.activeOptionIndex])?void 0:r.id,"aria-multiselectable":1===S.mode||void 0,"aria-labelledby":z,"aria-orientation":S.orientation,onKeyDown:U,role:"listbox",tabIndex:0===S.listboxState?0:void 0,style:{...f.style,..._,"--button-width":g(S.buttonElement,!0).width},...(0,j.transitionDataAttributes)(T)}),q=(0,eD.useRender)();return u.default.createElement(eU,{enabled:!!s&&(e.static||M)},u.default.createElement(e0.Provider,{value:1===S.mode?S:{...S,isSelected:k}},q({ourProps:Y,theirProps:f,slot:Q,defaultTag:"div",features:e5,visible:D,name:"Listbox.Options"})))}),tt=(0,eD.forwardRefWithAs)(function(e,t){let n,r,o,l=(0,u.useId)(),{id:i=`headlessui-listbox-option-${l}`,disabled:s=!1,value:a,...d}=e,f=!0===(0,u.useContext)(e4),p=e1("Listbox.Option"),v=eZ("Listbox.Option"),g=null!==p.activeOptionIndex&&p.options[p.activeOptionIndex].id===i,x=p.isSelected(a),E=(0,u.useRef)(null),y=(n=(0,u.useRef)(""),r=(0,u.useRef)(""),(0,h.useEvent)(()=>{let e=E.current;if(!e)return"";let t=e.innerText;if(n.current===t)return r.current;let o=(function(e){let t=e.getAttribute("aria-label");if("string"==typeof t)return t.trim();let n=e.getAttribute("aria-labelledby");if(n){let e=n.split(" ").map(e=>{let t=document.getElementById(e);if(t){let e=t.getAttribute("aria-label");return"string"==typeof e?e.trim():W(t).trim()}return null}).filter(Boolean);if(e.length>0)return e.join(", ")}return W(e).trim()})(e).trim().toLowerCase();return n.current=t,r.current=o,o})),S=(0,M.useLatestValue)({disabled:s,value:a,domRef:E,get textValue(){return y()}}),R=(0,K.useSyncRefs)(t,E,e=>{e?p.listRef.current.set(i,e):p.listRef.current.delete(i)});(0,m.useIsoMorphicEffect)(()=>{if(!p.__demoMode&&0===p.listboxState&&g&&0!==p.activationTrigger)return(0,b.disposables)().requestAnimationFrame(()=>{var e,t;null==(t=null==(e=E.current)?void 0:e.scrollIntoView)||t.call(e,{block:"nearest"})})},[E,g,p.__demoMode,p.listboxState,p.activationTrigger,p.activeOptionIndex]),(0,m.useIsoMorphicEffect)(()=>{if(!f)return v.registerOption(i,S)},[S,i,f]);let w=(0,h.useEvent)(e=>{var t;if(s)return e.preventDefault();v.onChange(a),0===p.mode&&((0,c.flushSync)(()=>v.closeListbox()),null==(t=p.buttonElement)||t.focus({preventScroll:!0}))}),O=(0,h.useEvent)(()=>{if(s)return v.goToOption(eM.Nothing);v.goToOption(eM.Specific,i)}),P=(o=(0,u.useRef)([-1,-1]),{wasMoved(e){let t=V(e);return(o.current[0]!==t[0]||o.current[1]!==t[1])&&(o.current=t,!0)},update(e){o.current=V(e)}}),C=(0,h.useEvent)(e=>{P.update(e),!s&&(g||v.goToOption(eM.Specific,i,0))}),I=(0,h.useEvent)(e=>{P.wasMoved(e)&&(s||g||v.goToOption(eM.Specific,i,0))}),L=(0,h.useEvent)(e=>{P.wasMoved(e)&&(s||g&&v.goToOption(eM.Nothing))}),T=(0,u.useMemo)(()=>({active:g,focus:g,selected:x,disabled:s,selectedOption:x&&f}),[g,x,s,f]),D=f?{}:{id:i,ref:R,role:"option",tabIndex:!0===s?void 0:-1,"aria-disabled":!0===s||void 0,"aria-selected":x,disabled:void 0,onClick:w,onFocus:O,onPointerEnter:C,onMouseEnter:C,onPointerMove:I,onMouseMove:I,onPointerLeave:L,onMouseLeave:L},F=(0,eD.useRender)();return!x&&f?null:F({ourProps:D,theirProps:d,slot:T,defaultTag:"div",name:"Listbox.Option"})}),tn=Object.assign(e8,{Button:e9,Label:e6,Options:te,Option:tt,SelectedOption:(0,eD.forwardRefWithAs)(function(e,t){let{options:n,placeholder:r,...o}=e,l={ref:(0,K.useSyncRefs)(t)},i=e1("ListboxSelectedOption"),s=(0,u.useMemo)(()=>({}),[]),a=void 0===i.value||null===i.value||1===i.mode&&Array.isArray(i.value)&&0===i.value.length,c=(0,eD.useRender)();return u.default.createElement(e4.Provider,{value:!0},c({ourProps:l,theirProps:{...o,children:u.default.createElement(u.default.Fragment,null,r&&a?r:n)},slot:s,defaultTag:e2,name:"ListboxSelectedOption"}))})});e.s(["Listbox",0,tn,"ListboxButton",0,e9,"ListboxOption",0,tt,"ListboxOptions",0,te],495470);var tr=e.i(444755);let to=(0,e.i(673706).makeClassName)("SelectItem"),tl=u.default.forwardRef((e,t)=>{let{value:n,icon:r,className:o,children:l}=e,s=(0,i.__rest)(e,["value","icon","className","children"]);return u.default.createElement(tt,Object.assign({className:(0,tr.tremorTwMerge)(to("root"),"flex justify-start items-center cursor-default text-tremor-default px-2.5 py-2.5","data-[focus]:bg-tremor-background-muted data-[focus]:text-tremor-content-strong data-[selected]:text-tremor-content-strong data-[selected]:bg-tremor-background-muted text-tremor-content-emphasis","dark:data-[focus]:bg-dark-tremor-background-muted dark:data-[focus]:text-dark-tremor-content-strong dark:data-[selected]:text-dark-tremor-content-strong dark:data-[selected]:bg-dark-tremor-background-muted dark:text-dark-tremor-content-emphasis",o),ref:t,key:n,value:n},s),r&&u.default.createElement(r,{className:(0,tr.tremorTwMerge)(to("icon"),"flex-none w-5 h-5 mr-1.5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")}),u.default.createElement("span",{className:"whitespace-nowrap truncate"},null!=l?l:n))});tl.displayName="SelectItem",e.s(["default",0,tl],333771),e.s(["SelectItem",0,tl],35983)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0ql16xan6en_0.js b/litellm/proxy/_experimental/out/_next/static/chunks/0ql16xan6en_0.js new file mode 100644 index 00000000000..31d4e0660ed --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0ql16xan6en_0.js @@ -0,0 +1,10 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,211576,e=>{"use strict";var t=e.i(131757);e.s(["Col",()=>t.default])},178654,e=>{"use strict";let t=e.i(211576).Col;e.s(["Col",0,t],178654)},621192,e=>{"use strict";let t=e.i(281256).Row;e.s(["Row",0,t],621192)},954616,e=>{"use strict";var t=e.i(271645),n=e.i(114272),i=e.i(540143),l=e.i(915823),r=e.i(619273),a=class extends l.Subscribable{#e;#t=void 0;#n;#i;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#l()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,r.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,r.hashKey)(t.mutationKey)!==(0,r.hashKey)(this.options.mutationKey)?this.reset():this.#n?.state.status==="pending"&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#l(),this.#r(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#l(),this.#r()}mutate(e,t){return this.#i=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#l(){let e=this.#n?.state??(0,n.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#r(e){i.notifyManager.batch(()=>{if(this.#i&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,i={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#i.onSuccess?.(e.data,t,n,i)}catch(e){Promise.reject(e)}try{this.#i.onSettled?.(e.data,null,t,n,i)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#i.onError?.(e.error,t,n,i)}catch(e){Promise.reject(e)}try{this.#i.onSettled?.(void 0,e.error,t,n,i)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},o=e.i(912598);e.s(["useMutation",0,function(e,n){let l=(0,o.useQueryClient)(n),[s]=t.useState(()=>new a(l,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let c=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(i.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),d=t.useCallback((e,t)=>{s.mutate(e,t).catch(r.noop)},[s]);if(c.error&&(0,r.shouldThrowError)(s.options.throwOnError,[c.error]))throw c.error;return{...c,mutate:d,mutateAsync:c.mutate}}],954616)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(529681),l=e.i(242064),r=e.i(517455),a=e.i(185793),o=e.i(721369),s=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let c=e=>{var{prefixCls:i,className:r,hoverable:a=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:c}=t.useContext(l.ConfigContext),d=c("card",i),u=(0,n.default)(`${d}-grid`,r,{[`${d}-grid-hoverable`]:a});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var d=e.i(915654),u=e.i(183293),g=e.i(246422),b=e.i(838378);let p=(0,g.genStyleHooks)("Card",e=>{let t=(0,b.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:n,cardHeadPadding:i,colorBorderSecondary:l,boxShadowTertiary:r,bodyPadding:a,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:r},[`${t}-head`]:(e=>{let{antCls:t,componentCls:n,headerHeight:i,headerPadding:l,tabsMarginBottom:r}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:i,marginBottom:-1,padding:`0 ${(0,d.unit)(l)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${n}-typography, + > ${n}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:r,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:a,borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:n,cardShadow:i,lineWidth:l}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,d.unit)(l)} 0 0 0 ${n}, + 0 ${(0,d.unit)(l)} 0 0 ${n}, + ${(0,d.unit)(l)} ${(0,d.unit)(l)} 0 0 ${n}, + ${(0,d.unit)(l)} 0 0 0 ${n} inset, + 0 ${(0,d.unit)(l)} 0 0 ${n} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:i}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:n,actionsLiMargin:i,cardActionsIconSize:l,colorBorderSecondary:r,actionsBg:a}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:a,borderTop:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${r}`,display:"flex",borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:i,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${n}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,d.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${n}`]:{fontSize:l,lineHeight:(0,d.unit)(e.calc(l).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${r}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,d.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${l}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:n}},[`${t}-contain-grid`]:{borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:i}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:n,headerPadding:i,bodyPadding:l}=e;return{[`${t}-head`]:{padding:`0 ${(0,d.unit)(i)}`,background:n,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,d.unit)(e.padding)} ${(0,d.unit)(l)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:n,headerPaddingSM:i,headerHeightSM:l,headerFontSizeSM:r}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:l,padding:`0 ${(0,d.unit)(i)}`,fontSize:r,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:n}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,n;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(n=e.headerPadding)?n:e.paddingLG}});var m=e.i(792812),f=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let h=e=>{let{actionClasses:n,actions:i=[],actionStyle:l}=e;return t.createElement("ul",{className:n,style:l},i.map((e,n)=>{let l=`action-${n}`;return t.createElement("li",{style:{width:`${100/i.length}%`},key:l},t.createElement("span",null,e))}))},y=t.forwardRef((e,s)=>{let d,{prefixCls:u,className:g,rootClassName:b,style:y,extra:$,headStyle:O={},bodyStyle:v={},title:j,loading:x,bordered:S,variant:C,size:w,type:E,cover:P,actions:M,tabList:N,children:T,activeTabKey:z,defaultActiveTabKey:B,tabBarExtraContent:R,hoverable:k,tabProps:L={},classNames:G,styles:H}=e,I=f(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:W,direction:A,card:F}=t.useContext(l.ConfigContext),[K]=(0,m.default)("card",C,S),D=e=>{var t;return(0,n.default)(null==(t=null==F?void 0:F.classNames)?void 0:t[e],null==G?void 0:G[e])},X=e=>{var t;return Object.assign(Object.assign({},null==(t=null==F?void 0:F.styles)?void 0:t[e]),null==H?void 0:H[e])},q=t.useMemo(()=>{let e=!1;return t.Children.forEach(T,t=>{(null==t?void 0:t.type)===c&&(e=!0)}),e},[T]),U=W("card",u),[_,Q,J]=p(U),V=t.createElement(a.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},T),Y=void 0!==z,Z=Object.assign(Object.assign({},L),{[Y?"activeKey":"defaultActiveKey"]:Y?z:B,tabBarExtraContent:R}),ee=(0,r.default)(w),et=ee&&"default"!==ee?ee:"large",en=N?t.createElement(o.default,Object.assign({size:et},Z,{className:`${U}-head-tabs`,onChange:t=>{var n;null==(n=e.onTabChange)||n.call(e,t)},items:N.map(e=>{var{tab:t}=e;return Object.assign({label:t},f(e,["tab"]))})})):null;if(j||$||en){let e=(0,n.default)(`${U}-head`,D("header")),i=(0,n.default)(`${U}-head-title`,D("title")),l=(0,n.default)(`${U}-extra`,D("extra")),r=Object.assign(Object.assign({},O),X("header"));d=t.createElement("div",{className:e,style:r},t.createElement("div",{className:`${U}-head-wrapper`},j&&t.createElement("div",{className:i,style:X("title")},j),$&&t.createElement("div",{className:l,style:X("extra")},$)),en)}let ei=(0,n.default)(`${U}-cover`,D("cover")),el=P?t.createElement("div",{className:ei,style:X("cover")},P):null,er=(0,n.default)(`${U}-body`,D("body")),ea=Object.assign(Object.assign({},v),X("body")),eo=t.createElement("div",{className:er,style:ea},x?V:T),es=(0,n.default)(`${U}-actions`,D("actions")),ec=(null==M?void 0:M.length)?t.createElement(h,{actionClasses:es,actionStyle:X("actions"),actions:M}):null,ed=(0,i.default)(I,["onTabChange"]),eu=(0,n.default)(U,null==F?void 0:F.className,{[`${U}-loading`]:x,[`${U}-bordered`]:"borderless"!==K,[`${U}-hoverable`]:k,[`${U}-contain-grid`]:q,[`${U}-contain-tabs`]:null==N?void 0:N.length,[`${U}-${ee}`]:ee,[`${U}-type-${E}`]:!!E,[`${U}-rtl`]:"rtl"===A},g,b,Q,J),eg=Object.assign(Object.assign({},null==F?void 0:F.style),y);return _(t.createElement("div",Object.assign({ref:s},ed,{className:eu,style:eg}),d,el,eo,ec))});var $=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};y.Grid=c,y.Meta=e=>{let{prefixCls:i,className:r,avatar:a,title:o,description:s}=e,c=$(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:d}=t.useContext(l.ConfigContext),u=d("card",i),g=(0,n.default)(`${u}-meta`,r),b=a?t.createElement("div",{className:`${u}-meta-avatar`},a):null,p=o?t.createElement("div",{className:`${u}-meta-title`},o):null,m=s?t.createElement("div",{className:`${u}-meta-description`},s):null,f=p||m?t.createElement("div",{className:`${u}-meta-detail`},p,m):null;return t.createElement("div",Object.assign({},c,{className:g}),b,f)},e.s(["Card",0,y],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(908206),l=e.i(242064),r=e.i(517455),a=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var c=e.i(876556),d=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n},u=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let g=e=>{let{itemPrefixCls:i,component:l,span:r,className:a,style:o,labelStyle:c,contentStyle:d,bordered:u,label:g,content:b,colon:p,type:m,styles:f}=e,{classNames:h}=t.useContext(s),y=Object.assign(Object.assign({},c),null==f?void 0:f.label),$=Object.assign(Object.assign({},d),null==f?void 0:f.content);if(u)return t.createElement(l,{colSpan:r,style:o,className:(0,n.default)(a,{[`${i}-item-${m}`]:"label"===m||"content"===m,[null==h?void 0:h.label]:(null==h?void 0:h.label)&&"label"===m,[null==h?void 0:h.content]:(null==h?void 0:h.content)&&"content"===m})},null!=g&&t.createElement("span",{style:y},g),null!=b&&t.createElement("span",{style:$},b));return t.createElement(l,{colSpan:r,style:o,className:(0,n.default)(`${i}-item`,a)},t.createElement("div",{className:`${i}-item-container`},null!=g&&t.createElement("span",{style:y,className:(0,n.default)(`${i}-item-label`,null==h?void 0:h.label,{[`${i}-item-no-colon`]:!p})},g),null!=b&&t.createElement("span",{style:$,className:(0,n.default)(`${i}-item-content`,null==h?void 0:h.content)},b)))};function b(e,{colon:n,prefixCls:i,bordered:l},{component:r,type:a,showLabel:o,showContent:s,labelStyle:c,contentStyle:d,styles:u}){return e.map(({label:e,children:b,prefixCls:p=i,className:m,style:f,labelStyle:h,contentStyle:y,span:$=1,key:O,styles:v},j)=>"string"==typeof r?t.createElement(g,{key:`${a}-${O||j}`,className:m,style:f,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),h),null==v?void 0:v.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),y),null==v?void 0:v.content)},span:$,colon:n,component:r,itemPrefixCls:p,bordered:l,label:o?e:null,content:s?b:null,type:a}):[t.createElement(g,{key:`label-${O||j}`,className:m,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),f),h),null==v?void 0:v.label),span:1,colon:n,component:r[0],itemPrefixCls:p,bordered:l,label:e,type:"label"}),t.createElement(g,{key:`content-${O||j}`,className:m,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),f),y),null==v?void 0:v.content),span:2*$-1,component:r[1],itemPrefixCls:p,bordered:l,content:b,type:"content"})])}let p=e=>{let n=t.useContext(s),{prefixCls:i,vertical:l,row:r,index:a,bordered:o}=e;return l?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${a}`,className:`${i}-row`},b(r,e,Object.assign({component:"th",type:"label",showLabel:!0},n))),t.createElement("tr",{key:`content-${a}`,className:`${i}-row`},b(r,e,Object.assign({component:"td",type:"content",showContent:!0},n)))):t.createElement("tr",{key:a,className:`${i}-row`},b(r,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},n)))};e.i(296059);var m=e.i(915654),f=e.i(183293),h=e.i(246422),y=e.i(838378);let $=(0,h.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:n,itemPaddingBottom:i,itemPaddingEnd:l,colonMarginRight:r,colonMarginLeft:a,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,f.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:n}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.padding)} ${(0,m.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:n,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.paddingSM)} ${(0,m.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.paddingXS)} ${(0,m.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},f.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:n,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:i,paddingInlineEnd:l},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,m.unit)(a)} ${(0,m.unit)(r)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,y.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var O=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let v=e=>{let g,{prefixCls:b,title:m,extra:f,column:h,colon:y=!0,bordered:v,layout:j,children:x,className:S,rootClassName:C,style:w,size:E,labelStyle:P,contentStyle:M,styles:N,items:T,classNames:z}=e,B=O(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:R,direction:k,className:L,style:G,classNames:H,styles:I}=(0,l.useComponentConfig)("descriptions"),W=R("descriptions",b),A=(0,a.default)(),F=t.useMemo(()=>{var e;return"number"==typeof h?h:null!=(e=(0,i.matchScreen)(A,Object.assign(Object.assign({},o),h)))?e:3},[A,h]),K=(g=t.useMemo(()=>T||(0,c.default)(x).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[T,x]),t.useMemo(()=>g.map(e=>{var{span:t}=e,n=d(e,["span"]);return"filled"===t?Object.assign(Object.assign({},n),{filled:!0}):Object.assign(Object.assign({},n),{span:"number"==typeof t?t:(0,i.matchScreen)(A,t)})}),[g,A])),D=(0,r.default)(E),X=((e,n)=>{let[i,l]=(0,t.useMemo)(()=>{let t,i,l,r;return t=[],i=[],l=!1,r=0,n.filter(e=>e).forEach(n=>{let{filled:a}=n,o=u(n,["filled"]);if(a){i.push(o),t.push(i),i=[],r=0;return}let s=e-r;(r+=n.span||1)>=e?(r>e?(l=!0,i.push(Object.assign(Object.assign({},o),{span:s}))):i.push(o),t.push(i),i=[],r=0):i.push(o)}),i.length>0&&t.push(i),[t=t.map(t=>{let n=t.reduce((e,t)=>e+(t.span||1),0);if(n({labelStyle:P,contentStyle:M,styles:{content:Object.assign(Object.assign({},I.content),null==N?void 0:N.content),label:Object.assign(Object.assign({},I.label),null==N?void 0:N.label)},classNames:{label:(0,n.default)(H.label,null==z?void 0:z.label),content:(0,n.default)(H.content,null==z?void 0:z.content)}}),[P,M,N,z,H,I]);return q(t.createElement(s.Provider,{value:Q},t.createElement("div",Object.assign({className:(0,n.default)(W,L,H.root,null==z?void 0:z.root,{[`${W}-${D}`]:D&&"default"!==D,[`${W}-bordered`]:!!v,[`${W}-rtl`]:"rtl"===k},S,C,U,_),style:Object.assign(Object.assign(Object.assign(Object.assign({},G),I.root),null==N?void 0:N.root),w)},B),(m||f)&&t.createElement("div",{className:(0,n.default)(`${W}-header`,H.header,null==z?void 0:z.header),style:Object.assign(Object.assign({},I.header),null==N?void 0:N.header)},m&&t.createElement("div",{className:(0,n.default)(`${W}-title`,H.title,null==z?void 0:z.title),style:Object.assign(Object.assign({},I.title),null==N?void 0:N.title)},m),f&&t.createElement("div",{className:(0,n.default)(`${W}-extra`,H.extra,null==z?void 0:z.extra),style:Object.assign(Object.assign({},I.extra),null==N?void 0:N.extra)},f)),t.createElement("div",{className:`${W}-view`},t.createElement("table",null,t.createElement("tbody",null,X.map((e,n)=>t.createElement(p,{key:n,index:n,colon:y,prefixCls:W,vertical:"vertical"===j,bordered:v,row:e}))))))))};v.Item=({children:e})=>e,e.s(["Descriptions",0,v],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),n=e.i(732961),i=e.i(289882),l=e.i(170517),r=e.i(628882),a=e.i(320890),o=e.i(104458),s=e.i(722319),c=e.i(8398),d=e.i(279728);e.i(765846);var u=e.i(602716),g=e.i(328052),b=e.i(135551);let p=(e,t)=>new b.FastColor(e).setA(t).toRgbString(),m=(e,t)=>new b.FastColor(e).lighten(t).toHexString(),f=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},h=(e,t)=>{let n=e||"#000",i=t||"#fff";return{colorBgBase:n,colorTextBase:i,colorText:p(i,.85),colorTextSecondary:p(i,.65),colorTextTertiary:p(i,.45),colorTextQuaternary:p(i,.25),colorFill:p(i,.18),colorFillSecondary:p(i,.12),colorFillTertiary:p(i,.08),colorFillQuaternary:p(i,.04),colorBgSolid:p(i,.95),colorBgSolidHover:p(i,1),colorBgSolidActive:p(i,.9),colorBgElevated:m(n,12),colorBgContainer:m(n,8),colorBgLayout:m(n,0),colorBgSpotlight:m(n,26),colorBgBlur:p(i,.04),colorBorder:m(n,26),colorBorderSecondary:m(n,19)}},y={defaultSeed:a.defaultConfig.token,useToken:function(){let[e,t,n]=(0,o.useToken)();return{theme:e,token:t,hashId:n}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let n=Object.keys(l.defaultPresetColors).map(t=>{let n=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,i,l)=>(e[`${t}-${l+1}`]=n[l],e[`${t}${l+1}`]=n[l],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),i=null!=t?t:(0,s.default)(e),r=(0,g.default)(e,{generateColorPalettes:f,generateNeutralColorPalettes:h});return Object.assign(Object.assign(Object.assign(Object.assign({},i),n),r),{colorPrimaryBg:r.colorPrimaryBorder,colorPrimaryBgHover:r.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let n=null!=t?t:(0,s.default)(e),i=n.fontSizeSM,l=n.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},n),function(e){let{sizeUnit:t,sizeStep:n}=e,i=n-2;return{sizeXXL:t*(i+10),sizeXL:t*(i+6),sizeLG:t*(i+2),sizeMD:t*(i+2),sizeMS:t*(i+1),size:t*i,sizeSM:t*i,sizeXS:t*(i-1),sizeXXS:t*(i-1)}}(null!=t?t:e)),(0,d.default)(i)),{controlHeight:l}),(0,c.default)(Object.assign(Object.assign({},n),{controlHeight:l})))},getDesignToken:e=>{let a=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):i.default,o=Object.assign(Object.assign({},l.default),null==e?void 0:e.token);return(0,n.getComputedToken)(o,{override:null==e?void 0:e.token},a,r.default)},defaultConfig:a.defaultConfig,_internalContext:a.DesignTokenContext};e.s(["theme",0,y],368869)},525720,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(529681),l=e.i(908286),r=e.i(242064),a=e.i(246422),o=e.i(838378);let s=["wrap","nowrap","wrap-reverse"],c=["flex-start","flex-end","start","end","center","space-between","space-around","space-evenly","stretch","normal","left","right"],d=["center","start","end","flex-start","flex-end","self-start","self-end","baseline","normal","stretch"],u=function(e,t){let i,l,r;return(0,n.default)(Object.assign(Object.assign(Object.assign({},(i=!0===t.wrap?"wrap":t.wrap,{[`${e}-wrap-${i}`]:i&&s.includes(i)})),(l={},d.forEach(n=>{l[`${e}-align-${n}`]=t.align===n}),l[`${e}-align-stretch`]=!t.align&&!!t.vertical,l)),(r={},c.forEach(n=>{r[`${e}-justify-${n}`]=t.justify===n}),r)))},g=(0,a.genStyleHooks)("Flex",e=>{let{paddingXS:t,padding:n,paddingLG:i}=e,l=(0,o.mergeToken)(e,{flexGapSM:t,flexGap:n,flexGapLG:i});return[(e=>{let{componentCls:t}=e;return{[t]:{display:"flex",margin:0,padding:0,"&-vertical":{flexDirection:"column"},"&-rtl":{direction:"rtl"},"&:empty":{display:"none"}}}})(l),(e=>{let{componentCls:t}=e;return{[t]:{"&-gap-small":{gap:e.flexGapSM},"&-gap-middle":{gap:e.flexGap},"&-gap-large":{gap:e.flexGapLG}}}})(l),(e=>{let{componentCls:t}=e,n={};return s.forEach(e=>{n[`${t}-wrap-${e}`]={flexWrap:e}}),n})(l),(e=>{let{componentCls:t}=e,n={};return d.forEach(e=>{n[`${t}-align-${e}`]={alignItems:e}}),n})(l),(e=>{let{componentCls:t}=e,n={};return c.forEach(e=>{n[`${t}-justify-${e}`]={justifyContent:e}}),n})(l)]},()=>({}),{resetStyle:!1});var b=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let p=t.default.forwardRef((e,a)=>{let{prefixCls:o,rootClassName:s,className:c,style:d,flex:p,gap:m,vertical:f=!1,component:h="div",children:y}=e,$=b(e,["prefixCls","rootClassName","className","style","flex","gap","vertical","component","children"]),{flex:O,direction:v,getPrefixCls:j}=t.default.useContext(r.ConfigContext),x=j("flex",o),[S,C,w]=g(x),E=null!=f?f:null==O?void 0:O.vertical,P=(0,n.default)(c,s,null==O?void 0:O.className,x,C,w,u(x,e),{[`${x}-rtl`]:"rtl"===v,[`${x}-gap-${m}`]:(0,l.isPresetSize)(m),[`${x}-vertical`]:E}),M=Object.assign(Object.assign({},null==O?void 0:O.style),d);return p&&(M.flex=p),m&&!(0,l.isPresetSize)(m)&&(M.gap=m),S(t.default.createElement(h,Object.assign({ref:a,className:P,style:M},(0,i.default)($,["justify","wrap","align"])),y))});e.s(["Flex",0,p],525720)},263147,e=>{"use strict";var t=e.i(266027),n=e.i(243652),i=e.i(602869),l=e.i(431703),r=e.i(708347),a=e.i(135214);let o=(0,n.createQueryKeys)("accessGroups"),s=async e=>{let t=(0,i.getProxyBaseUrl)(),n=`${t}/v1/access_group`,r=await fetch(n,{method:"GET",headers:{[(0,i.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=(0,l.deriveErrorMessage)(e);throw(0,i.handleError)(t),Error(t)}return r.json()};e.s(["accessGroupKeys",0,o,"useAccessGroups",0,()=>{let{accessToken:e,userRole:n}=(0,a.default)();return(0,t.useQuery)({queryKey:o.list({}),queryFn:async()=>s(e),enabled:!!e&&r.all_admin_roles.includes(n||"")})}])},304911,e=>{"use strict";var t=e.i(843476),n=e.i(262218);let{Text:i}=e.i(898586).Typography;e.s(["default",0,function({userId:e}){return"default_user_id"===e?(0,t.jsx)(n.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,t.jsx)(i,{children:e})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0qofycjxzylqf.js b/litellm/proxy/_experimental/out/_next/static/chunks/0qofycjxzylqf.js new file mode 100644 index 00000000000..7105469e3f0 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0qofycjxzylqf.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var r=e.i(290571),t=e.i(271645),o=e.i(829087),s=e.i(480731),a=e.i(444755),i=e.i(673706),l=e.i(95779);let n={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,i.makeClassName)("Icon"),m=t.default.forwardRef((e,m)=>{let{icon:h,variant:p="simple",tooltip:g,size:x=s.Sizes.SM,color:f,className:b}=e,y=(0,r.__rest)(e,["icon","variant","tooltip","size","color","className"]),v=((e,r)=>{switch(e){case"simple":return{textColor:r?(0,i.getColorClassNames)(r,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:r?(0,i.getColorClassNames)(r,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:r?(0,i.getColorClassNames)(r,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:r?(0,i.getColorClassNames)(r,l.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:r?(0,i.getColorClassNames)(r,l.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,l.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:r?(0,i.getColorClassNames)(r,l.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:r?(0,a.tremorTwMerge)((0,i.getColorClassNames)(r,l.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(p,f),{tooltipProps:_,getReferenceProps:C}=(0,o.useTooltip)();return t.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,_.refs.setReference]),className:(0,a.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",v.bgColor,v.textColor,v.borderColor,v.ringColor,c[p].rounded,c[p].border,c[p].shadow,c[p].ring,n[x].paddingX,n[x].paddingY,b)},C,y),t.default.createElement(o.default,Object.assign({text:g},_)),t.default.createElement(h,{className:(0,a.tremorTwMerge)(u("icon"),"shrink-0",d[x].height,d[x].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var r=e.i(728889);e.s(["Icon",()=>r.default])},591935,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,t],591935)},122577,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,t],122577)},551332,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,t],551332)},434626,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,t],434626)},902555,e=>{"use strict";var r=e.i(843476),t=e.i(591935),o=e.i(122577),s=e.i(278587),a=e.i(68155),i=e.i(360820),l=e.i(871943),n=e.i(434626),d=e.i(551332),c=e.i(592968),u=e.i(115504),m=e.i(752978);function h({icon:e,onClick:t,className:o,disabled:s,dataTestId:a}){return s?(0,r.jsx)(m.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":a}):(0,r.jsx)(m.Icon,{icon:e,size:"sm",onClick:t,className:(0,u.cx)("cursor-pointer",o),"data-testid":a})}let p={Edit:{icon:t.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:a.TrashIcon,className:"hover:text-red-600"},Test:{icon:o.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:s.RefreshIcon,className:"hover:text-green-600"},Up:{icon:i.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:l.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:n.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:t,disabled:o=!1,disabledTooltipText:s,dataTestId:a,variant:i}){let{icon:l,className:n}=p[i];return(0,r.jsx)(c.Tooltip,{title:o?s:t,children:(0,r.jsx)("span",{children:(0,r.jsx)(h,{icon:l,onClick:e,className:n,disabled:o,dataTestId:a})})})}],902555)},928685,e=>{"use strict";var r=e.i(38953);e.s(["SearchOutlined",()=>r.default])},95779,e=>{"use strict";var r=e.i(480731);let t=[r.BaseColors.Blue,r.BaseColors.Cyan,r.BaseColors.Sky,r.BaseColors.Indigo,r.BaseColors.Violet,r.BaseColors.Purple,r.BaseColors.Fuchsia,r.BaseColors.Slate,r.BaseColors.Gray,r.BaseColors.Zinc,r.BaseColors.Neutral,r.BaseColors.Stone,r.BaseColors.Red,r.BaseColors.Orange,r.BaseColors.Amber,r.BaseColors.Yellow,r.BaseColors.Lime,r.BaseColors.Green,r.BaseColors.Emerald,r.BaseColors.Teal,r.BaseColors.Pink,r.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,t])},994388,e=>{"use strict";var r=e.i(290571),t=e.i(829087),o=e.i(271645);let s=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],a=e=>({_s:e,status:s[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),i=e=>e?6:5,l=(e,r,t,o,s)=>{clearTimeout(o.current);let i=a(e);r(i),t.current=i,s&&s({current:i})};var n=e.i(480731),d=e.i(444755),c=e.i(673706);let u=e=>{var t=(0,r.__rest)(e,[]);return o.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),o.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let h={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},p=(e,r)=>{switch(e){case"primary":return{textColor:r?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:r?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:r?(0,c.getColorClassNames)(r,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:r?(0,c.getColorClassNames)(r,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:r?(0,c.getColorClassNames)(r,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:r?(0,c.getColorClassNames)(r,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:r?(0,c.getColorClassNames)(r,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:r?(0,c.getColorClassNames)(r,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:r?(0,d.tremorTwMerge)((0,c.getColorClassNames)(r,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:r?(0,c.getColorClassNames)(r,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:r?(0,c.getColorClassNames)(r,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:r?(0,c.getColorClassNames)(r,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},g=(0,c.makeClassName)("Button"),x=({loading:e,iconSize:r,iconPosition:t,Icon:s,needMargin:a,transitionStatus:i})=>{let l=a?t===n.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:r,exiting:r,exited:c};return e?o.default.createElement(u,{className:(0,d.tremorTwMerge)(g("icon"),"animate-spin shrink-0",l,m.default,m[i]),style:{transition:"width 150ms"}}):o.default.createElement(s,{className:(0,d.tremorTwMerge)(g("icon"),"shrink-0",r,l)})},f=o.default.forwardRef((e,s)=>{let{icon:u,iconPosition:m=n.HorizontalPositions.Left,size:f=n.Sizes.SM,color:b,variant:y="primary",disabled:v,loading:_=!1,loadingText:C,children:j,tooltip:k,className:w}=e,S=(0,r.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),N=_||v,T=void 0!==u||_,I=_&&C,z=!(!j&&!I),P=(0,d.tremorTwMerge)(h[f].height,h[f].width),R="light"!==y?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",B=p(y,b),E=("light"!==y?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[f],{tooltipProps:F,getReferenceProps:A}=(0,t.useTooltip)(300),[M,L]=(({enter:e=!0,exit:r=!0,preEnter:t,preExit:s,timeout:n,initialEntered:d,mountOnEnter:c,unmountOnExit:u,onStateChange:m}={})=>{let[h,p]=(0,o.useState)(()=>a(d?2:i(c))),g=(0,o.useRef)(h),x=(0,o.useRef)(0),[f,b]="object"==typeof n?[n.enter,n.exit]:[n,n],y=(0,o.useCallback)(()=>{let e=((e,r)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return i(r)}})(g.current._s,u);e&&l(e,p,g,x,m)},[m,u]);return[h,(0,o.useCallback)(o=>{let a=e=>{switch(l(e,p,g,x,m),e){case 1:f>=0&&(x.current=((...e)=>setTimeout(...e))(y,f));break;case 4:b>=0&&(x.current=((...e)=>setTimeout(...e))(y,b));break;case 0:case 3:x.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||a(e+1)},0)}},n=g.current.isEnter;"boolean"!=typeof o&&(o=!n),o?n||a(e?+!t:2):n&&a(r?s?3:4:i(u))},[y,m,e,r,t,s,f,b,u]),y]})({timeout:50});return(0,o.useEffect)(()=>{L(_)},[_]),o.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([s,F.refs.setReference]),className:(0,d.tremorTwMerge)(g("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",R,E.paddingX,E.paddingY,E.fontSize,B.textColor,B.bgColor,B.borderColor,B.hoverBorderColor,N?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(p(y,b).hoverTextColor,p(y,b).hoverBgColor,p(y,b).hoverBorderColor),w),disabled:N},A,S),o.default.createElement(t.default,Object.assign({text:k},F)),T&&m!==n.HorizontalPositions.Right?o.default.createElement(x,{loading:_,iconSize:P,iconPosition:m,Icon:u,transitionStatus:M.status,needMargin:z}):null,I||j?o.default.createElement("span",{className:(0,d.tremorTwMerge)(g("text"),"text-tremor-default whitespace-nowrap")},I?C:j):null,T&&m===n.HorizontalPositions.Right?o.default.createElement(x,{loading:_,iconSize:P,iconPosition:m,Icon:u,transitionStatus:M.status,needMargin:z}):null)});f.displayName="Button",e.s(["Button",0,f],994388)},599724,936325,e=>{"use strict";var r=e.i(95779),t=e.i(444755),o=e.i(673706),s=e.i(271645);let a=s.default.forwardRef((e,a)=>{let{color:i,className:l,children:n}=e;return s.default.createElement("p",{ref:a,className:(0,t.tremorTwMerge)("text-tremor-default",i?(0,o.getColorClassNames)(i,r.colorPalette.text).textColor:(0,t.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),l)},n)});a.displayName="Text",e.s(["default",0,a],936325),e.s(["Text",0,a],599724)},304967,e=>{"use strict";var r=e.i(290571),t=e.i(271645),o=e.i(480731),s=e.i(95779),a=e.i(444755),i=e.i(673706);let l=(0,i.makeClassName)("Card"),n=t.default.forwardRef((e,n)=>{let{decoration:d="",decorationColor:c,children:u,className:m}=e,h=(0,r.__rest)(e,["decoration","decorationColor","children","className"]);return t.default.createElement("div",Object.assign({ref:n,className:(0,a.tremorTwMerge)(l("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,i.getColorClassNames)(c,s.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case o.HorizontalPositions.Left:return"border-l-4";case o.VerticalPositions.Top:return"border-t-4";case o.HorizontalPositions.Right:return"border-r-4";case o.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},h),u)});n.displayName="Card",e.s(["Card",0,n],304967)},629569,e=>{"use strict";var r=e.i(290571),t=e.i(95779),o=e.i(444755),s=e.i(673706),a=e.i(271645);let i=a.default.forwardRef((e,i)=>{let{color:l,children:n,className:d}=e,c=(0,r.__rest)(e,["color","children","className"]);return a.default.createElement("p",Object.assign({ref:i,className:(0,o.tremorTwMerge)("font-medium text-tremor-title",l?(0,s.getColorClassNames)(l,t.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),n)});i.displayName="Title",e.s(["Title",0,i],629569)},350967,46757,e=>{"use strict";var r=e.i(290571),t=e.i(444755),o=e.i(673706),s=e.i(271645);let a={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},i={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},l={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},n={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,a,"gridColsLg",0,n,"gridColsMd",0,l,"gridColsSm",0,i],46757);let d=(0,o.makeClassName)("Grid"),c=(e,r)=>e&&Object.keys(r).includes(String(e))?r[e]:"",u=s.default.forwardRef((e,o)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:h,numItemsLg:p,children:g,className:x}=e,f=(0,r.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),b=c(u,a),y=c(m,i),v=c(h,l),_=c(p,n),C=(0,t.tremorTwMerge)(b,y,v,_);return s.default.createElement("div",Object.assign({ref:o,className:(0,t.tremorTwMerge)(d("root"),"grid",C,x)},f),g)});u.displayName="Grid",e.s(["Grid",0,u],350967)},530212,e=>{"use strict";var r=e.i(271645);let t=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,t],530212)},678784,e=>{"use strict";var r=e.i(678745);e.s(["CheckIcon",()=>r.default])},118366,e=>{"use strict";var r=e.i(991124);e.s(["CopyIcon",()=>r.default])},516015,(e,r,t)=>{},898547,(e,r,t)=>{var o=e.i(247167);e.r(516015);var s=e.r(271645),a=s&&"object"==typeof s&&"default"in s?s:{default:s},i=void 0!==o.default&&o.default.env&&!0,l=function(e){return"[object String]"===Object.prototype.toString.call(e)},n=function(){function e(e){var r=void 0===e?{}:e,t=r.name,o=void 0===t?"stylesheet":t,s=r.optimizeForSpeed,a=void 0===s?i:s;d(l(o),"`name` must be a string"),this._name=o,this._deletedRulePlaceholder="#"+o+"-deleted-rule____{}",d("boolean"==typeof a,"`optimizeForSpeed` must be a boolean"),this._optimizeForSpeed=a,this._serverSheet=void 0,this._tags=[],this._injected=!1,this._rulesCount=0;var n="u">typeof window&&document.querySelector('meta[property="csp-nonce"]');this._nonce=n?n.getAttribute("content"):null}var r,t=e.prototype;return t.setOptimizeForSpeed=function(e){d("boolean"==typeof e,"`setOptimizeForSpeed` accepts a boolean"),d(0===this._rulesCount,"optimizeForSpeed cannot be when rules have already been inserted"),this.flush(),this._optimizeForSpeed=e,this.inject()},t.isOptimizeForSpeed=function(){return this._optimizeForSpeed},t.inject=function(){var e=this;if(d(!this._injected,"sheet already injected"),this._injected=!0,"u">typeof window&&this._optimizeForSpeed){this._tags[0]=this.makeStyleTag(this._name),this._optimizeForSpeed="insertRule"in this.getSheet(),this._optimizeForSpeed||(i||console.warn("StyleSheet: optimizeForSpeed mode not supported falling back to standard mode."),this.flush(),this._injected=!0);return}this._serverSheet={cssRules:[],insertRule:function(r,t){return"number"==typeof t?e._serverSheet.cssRules[t]={cssText:r}:e._serverSheet.cssRules.push({cssText:r}),t},deleteRule:function(r){e._serverSheet.cssRules[r]=null}}},t.getSheetForTag=function(e){if(e.sheet)return e.sheet;for(var r=0;rtypeof window?this.getSheet():this._serverSheet;if(r.trim()||(r=this._deletedRulePlaceholder),!t.cssRules[e])return e;t.deleteRule(e);try{t.insertRule(r,e)}catch(o){i||console.warn("StyleSheet: illegal rule: \n\n"+r+"\n\nSee https://stackoverflow.com/q/20007992 for more info"),t.insertRule(this._deletedRulePlaceholder,e)}}else{var o=this._tags[e];d(o,"old rule at index `"+e+"` not found"),o.textContent=r}return e},t.deleteRule=function(e){if("u"typeof window?(this._tags.forEach(function(e){return e&&e.parentNode.removeChild(e)}),this._tags=[]):this._serverSheet.cssRules=[]},t.cssRules=function(){var e=this;return"u">>0},u={};function m(e,r){if(!r)return"jsx-"+e;var t=String(r),o=e+t;return u[o]||(u[o]="jsx-"+c(e+"-"+t)),u[o]}function h(e,r){"u"typeof window&&!this._fromServer&&(this._fromServer=this.selectFromServer(),this._instancesCounts=Object.keys(this._fromServer).reduce(function(e,r){return e[r]=0,e},{}));var t=this.getIdAndRules(e),o=t.styleId,s=t.rules;if(o in this._instancesCounts){this._instancesCounts[o]+=1;return}var a=s.map(function(e){return r._sheet.insertRule(e)}).filter(function(e){return -1!==e});this._indices[o]=a,this._instancesCounts[o]=1},r.remove=function(e){var r=this,t=this.getIdAndRules(e).styleId;if(function(e,r){if(!e)throw Error("StyleSheetRegistry: "+r+".")}(t in this._instancesCounts,"styleId: `"+t+"` not found"),this._instancesCounts[t]-=1,this._instancesCounts[t]<1){var o=this._fromServer&&this._fromServer[t];o?(o.parentNode.removeChild(o),delete this._fromServer[t]):(this._indices[t].forEach(function(e){return r._sheet.deleteRule(e)}),delete this._indices[t]),delete this._instancesCounts[t]}},r.update=function(e,r){this.add(r),this.remove(e)},r.flush=function(){this._sheet.flush(),this._sheet.inject(),this._fromServer=void 0,this._indices={},this._instancesCounts={}},r.cssRules=function(){var e=this,r=this._fromServer?Object.keys(this._fromServer).map(function(r){return[r,e._fromServer[r]]}):[],t=this._sheet.cssRules();return r.concat(Object.keys(this._indices).map(function(r){return[r,e._indices[r].map(function(e){return t[e].cssText}).join(e._optimizeForSpeed?"":"\n")]}).filter(function(e){return!!e[1]}))},r.styles=function(e){var r,t;return r=this.cssRules(),void 0===(t=e)&&(t={}),r.map(function(e){var r=e[0],o=e[1];return a.default.createElement("style",{id:"__"+r,key:"__"+r,nonce:t.nonce?t.nonce:void 0,dangerouslySetInnerHTML:{__html:o}})})},r.getIdAndRules=function(e){var r=e.children,t=e.dynamic,o=e.id;if(t){var s=m(o,t);return{styleId:s,rules:Array.isArray(r)?r.map(function(e){return h(s,e)}):[h(s,r)]}}return{styleId:m(o),rules:Array.isArray(r)?r:[r]}},r.selectFromServer=function(){return Array.prototype.slice.call(document.querySelectorAll('[id^="__jsx-"]')).reduce(function(e,r){return e[r.id.slice(2)]=r,e},{})},e}(),g=s.createContext(null);function x(){return new p}function f(){return s.useContext(g)}g.displayName="StyleSheetContext";var b=a.default.useInsertionEffect||a.default.useLayoutEffect,y="u">typeof window?x():void 0;function v(e){var r=y||f();return r&&("u"{r.exports=e.r(898547).style},962296,e=>{"use strict";var r=e.i(843476),t=e.i(708347),o=e.i(56456),s=e.i(266027),a=e.i(994388),i=e.i(599724),l=e.i(629569),n=e.i(808613),d=e.i(311451),c=e.i(212931),u=e.i(199133),m=e.i(482725),h=e.i(291542),p=e.i(271645),g=e.i(127952),x=e.i(727749),f=e.i(602869),b=e.i(827252),y=e.i(779241),v=e.i(592968),_=e.i(898586),C=e.i(555987),j=e.i(437902),k=e.i(285027),w=e.i(464571),S=e.i(312361);let{Text:N}=_.Typography,T=({litellmParams:e,accessToken:t,onTestComplete:o})=>{let[s,a]=(0,p.useState)(!0),[i,l]=(0,p.useState)(null),[n,d]=(0,p.useState)(!1);(0,p.useEffect)(()=>{(async()=>{a(!0);try{let r=await (0,f.testSearchToolConnection)(t,e);l(r),"success"===r.status&&x.default.success("Connection test successful!")}catch(e){l({status:"error",message:e instanceof Error?e.message:"Unknown error occurred",error_type:"NetworkError"})}finally{a(!1),o&&o()}})()},[t,e,o]);let c=i?.message?(e=>{if(!e)return"Unknown error";let r=e.split("stack trace:")[0].trim().replace(/^litellm\.(.*?)Error:\s*/,"").replace(/^AuthenticationError:\s*/,"");if(r.includes("")||r.includes("(.*?)<\/title>/);return e?e[1]:r.includes("401")||r.includes("Authorization Required")?"Authentication failed: Invalid API key or credentials":"Authentication error - please check your API key"}return r.length>200?r.substring(0,200)+"...":r})(i.message):"Unknown error";return s?(0,r.jsx)("div",{style:{padding:"24px",borderRadius:"8px",backgroundColor:"#fff"},children:(0,r.jsxs)("div",{style:{textAlign:"center",padding:"32px 20px"},className:"jsx-dc9a0e2d897fe63b",children:[(0,r.jsx)("div",{style:{marginBottom:"16px"},className:"jsx-dc9a0e2d897fe63b loading-spinner",children:(0,r.jsx)("div",{style:{border:"3px solid #f3f3f3",borderTop:"3px solid #1890ff",borderRadius:"50%",width:"30px",height:"30px",animation:"spin 1s linear infinite",margin:"0 auto"},className:"jsx-dc9a0e2d897fe63b"})}),(0,r.jsxs)(N,{style:{fontSize:"16px"},children:["Testing connection to ",e.search_provider||"search provider","..."]}),(0,r.jsx)(j.default,{id:"dc9a0e2d897fe63b",children:"@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}"})]})}):i?(0,r.jsxs)("div",{style:{padding:"24px",borderRadius:"8px",backgroundColor:"#fff"},children:["success"===i.status?(0,r.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",padding:"32px 20px"},children:[(0,r.jsx)("div",{style:{color:"#52c41a",fontSize:"24px",display:"flex",alignItems:"center"},children:(0,r.jsx)("svg",{viewBox:"64 64 896 896",focusable:"false","data-icon":"check-circle",width:"1em",height:"1em",fill:"currentColor","aria-hidden":"true",children:(0,r.jsx)("path",{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"})})}),(0,r.jsxs)("div",{style:{marginLeft:"12px"},children:[(0,r.jsxs)(N,{type:"success",style:{fontSize:"18px",fontWeight:500,display:"block"},children:["Connection to ",e.search_provider," successful!"]}),i.test_query&&(0,r.jsxs)(N,{style:{fontSize:"14px",color:"#666",marginTop:"8px",display:"block"},children:["Test query:"," ",(0,r.jsx)("code",{style:{backgroundColor:"#f0f0f0",padding:"2px 6px",borderRadius:"4px"},children:i.test_query})]}),void 0!==i.results_count&&(0,r.jsxs)(N,{style:{fontSize:"14px",color:"#666",display:"block"},children:["Results retrieved: ",i.results_count]})]})]}):(0,r.jsx)(r.Fragment,{children:(0,r.jsxs)("div",{children:[(0,r.jsxs)("div",{style:{display:"flex",alignItems:"center",marginBottom:"20px"},children:[(0,r.jsx)(k.WarningOutlined,{style:{color:"#ff4d4f",fontSize:"24px",marginRight:"12px"}}),(0,r.jsxs)(N,{type:"danger",style:{fontSize:"18px",fontWeight:500},children:["Connection to ",e.search_provider||"search provider"," failed"]})]}),(0,r.jsxs)("div",{style:{backgroundColor:"#fff2f0",border:"1px solid #ffccc7",borderRadius:"8px",padding:"16px",marginBottom:"20px",boxShadow:"0 1px 2px rgba(0, 0, 0, 0.03)"},children:[(0,r.jsxs)(N,{strong:!0,style:{display:"block",marginBottom:"8px"},children:["Error:"," "]}),(0,r.jsx)(N,{type:"danger",style:{fontSize:"14px",lineHeight:"1.5"},children:c}),i.error_type&&(0,r.jsx)("div",{style:{marginTop:"8px"},children:(0,r.jsxs)(N,{style:{fontSize:"13px",color:"#666"},children:["Error type:"," ",(0,r.jsx)("code",{style:{backgroundColor:"#ffebee",padding:"2px 6px",borderRadius:"4px",color:"#d32f2f"},children:i.error_type})]})}),i.message&&(0,r.jsx)("div",{style:{marginTop:"12px"},children:(0,r.jsx)(w.Button,{type:"link",onClick:()=>d(!n),style:{paddingLeft:0,height:"auto"},children:n?"Hide Details":"Show Details"})})]}),n&&(0,r.jsxs)("div",{style:{marginBottom:"20px"},children:[(0,r.jsx)(N,{strong:!0,style:{display:"block",marginBottom:"8px",fontSize:"15px"},children:"Full Error Details"}),(0,r.jsx)("pre",{style:{backgroundColor:"#f5f5f5",padding:"16px",borderRadius:"8px",fontSize:"13px",maxHeight:"200px",overflow:"auto",border:"1px solid #e8e8e8",lineHeight:"1.5",whiteSpace:"pre-wrap",wordBreak:"break-word"},children:i.message})]}),(0,r.jsxs)("div",{style:{backgroundColor:"#fffbf0",border:"1px solid #ffe58f",borderLeft:"4px solid #faad14",borderRadius:"8px",padding:"16px"},children:[(0,r.jsx)(N,{strong:!0,style:{display:"block",marginBottom:"8px",color:"#d48806"},children:"Troubleshooting tips:"}),(0,r.jsxs)("ul",{style:{margin:"8px 0",paddingLeft:"20px",color:"#ad6800"},children:[(0,r.jsx)("li",{style:{marginBottom:"6px"},children:"Verify your API key is correct and active"}),(0,r.jsx)("li",{style:{marginBottom:"6px"},children:"Check if the search provider service is operational"}),(0,r.jsx)("li",{style:{marginBottom:"6px"},children:"Ensure you have sufficient credits/quota with the provider"}),(0,r.jsx)("li",{style:{marginBottom:"6px"},children:"Review the provider's documentation for any additional requirements"})]})]})]})}),(0,r.jsx)(S.Divider,{style:{margin:"24px 0 16px"}}),(0,r.jsx)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:(0,r.jsx)(w.Button,{type:"link",href:"https://docs.litellm.ai/docs/search",target:"_blank",icon:(0,r.jsx)(b.InfoCircleOutlined,{}),children:"View Search Documentation"})})]}):null},{TextArea:I}=d.Input,z=({providerName:e,displayName:t})=>(0,r.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[(0,r.jsx)("img",{src:(0,C.resolveLogoSrc)(`/ui/assets/logos/${e}.png`),alt:"",style:{width:"20px",height:"20px",marginRight:"8px",objectFit:"contain"},onError:e=>{e.currentTarget.style.display="none"}}),(0,r.jsx)("span",{children:t})]}),P=({userRole:e,accessToken:o,onCreateSuccess:i,isModalVisible:l,setModalVisible:d})=>{let[m]=n.Form.useForm(),[h,g]=(0,p.useState)(!1),[C,j]=(0,p.useState)({}),[k,w]=(0,p.useState)(!1),[S,N]=(0,p.useState)(!1),[P,R]=(0,p.useState)(""),{data:B,isLoading:E}=(0,s.useQuery)({queryKey:["searchProviders"],queryFn:()=>{if(!o)throw Error("Access Token required");return(0,f.fetchAvailableSearchProviders)(o)},enabled:!!o&&l}),F=B?.providers||[],A=async e=>{g(!0);try{let r={search_tool_name:e.search_tool_name,litellm_params:{search_provider:e.search_provider,api_key:e.api_key,api_base:e.api_base,timeout:e.timeout?parseFloat(e.timeout):void 0,max_retries:e.max_retries?parseInt(e.max_retries):void 0},search_tool_info:e.description?{description:e.description}:void 0};if(null!=o){let e=await (0,f.createSearchTool)(o,r);x.default.success("Search tool created successfully"),m.resetFields(),j({}),d(!1),i(e)}}catch(e){x.default.error("Error creating search tool: "+e)}finally{g(!1)}},M=async()=>{try{await m.validateFields(["search_provider","api_key"]),N(!0),R(`test-${Date.now()}`),w(!0)}catch(e){x.default.error("Please fill in Search Provider and API Key before testing")}};return(p.default.useEffect(()=>{l||j({})},[l]),(0,t.isAdminRole)(e))?(0,r.jsxs)(c.Modal,{title:(0,r.jsxs)("div",{className:"flex items-center space-x-3 pb-4 border-b border-gray-100",children:[(0,r.jsx)("span",{className:"text-2xl",children:"🔍"}),(0,r.jsx)("h2",{className:"text-xl font-semibold text-gray-900",children:"Add New Search Tool"})]}),open:l,width:800,onCancel:()=>{m.resetFields(),j({}),d(!1)},footer:null,className:"top-8",styles:{body:{padding:"24px"},header:{padding:"24px 24px 0 24px",border:"none"}},children:[(0,r.jsx)("div",{className:"mt-6",children:(0,r.jsxs)(n.Form,{form:m,onFinish:A,onValuesChange:(e,r)=>j(r),layout:"vertical",className:"space-y-6",children:[(0,r.jsxs)("div",{className:"grid grid-cols-1 gap-6",children:[(0,r.jsx)(n.Form.Item,{label:(0,r.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["Search Tool Name",(0,r.jsx)(v.Tooltip,{title:"A unique name to identify this search tool configuration (e.g., 'perplexity-search', 'tavily-news-search').",children:(0,r.jsx)(b.InfoCircleOutlined,{className:"ml-2 text-blue-400 hover:text-blue-600 cursor-help"})})]}),name:"search_tool_name",rules:[{required:!0,message:"Please enter a search tool name"},{pattern:/^[a-zA-Z0-9_-]+$/,message:"Name can only contain letters, numbers, hyphens, and underscores"}],children:(0,r.jsx)(y.TextInput,{placeholder:"e.g., perplexity-search, my-tavily-tool",className:"rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"})}),(0,r.jsx)(n.Form.Item,{label:(0,r.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["Search Provider",(0,r.jsx)(v.Tooltip,{title:"Select the search provider you want to use. Each provider has different capabilities and pricing.",children:(0,r.jsx)(b.InfoCircleOutlined,{className:"ml-2 text-blue-400 hover:text-blue-600 cursor-help"})})]}),name:"search_provider",rules:[{required:!0,message:"Please select a search provider"}],children:(0,r.jsx)(u.Select,{placeholder:"Select a search provider",className:"rounded-lg",size:"large",loading:E,showSearch:!0,optionFilterProp:"children",optionLabelProp:"label",children:F.map(e=>(0,r.jsx)(u.Select.Option,{value:e.provider_name,label:(0,r.jsx)(z,{providerName:e.provider_name,displayName:e.ui_friendly_name}),children:(0,r.jsx)(z,{providerName:e.provider_name,displayName:e.ui_friendly_name})},e.provider_name))})}),(0,r.jsx)(n.Form.Item,{label:(0,r.jsxs)("span",{className:"text-sm font-medium text-gray-700 flex items-center",children:["API Key",(0,r.jsx)(v.Tooltip,{title:"The API key for authenticating with the search provider. This will be securely stored.",children:(0,r.jsx)(b.InfoCircleOutlined,{className:"ml-2 text-blue-400 hover:text-blue-600 cursor-help"})})]}),name:"api_key",rules:[{required:!1,message:"Please enter an API key"}],children:(0,r.jsx)(y.TextInput,{type:"password",placeholder:"Enter your API key",className:"rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"})}),(0,r.jsx)(n.Form.Item,{label:(0,r.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Description (Optional)"}),name:"description",children:(0,r.jsx)(I,{rows:3,placeholder:"Brief description of this search tool's purpose",className:"rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"})})]}),(0,r.jsxs)("div",{className:"flex justify-between items-center pt-6 border-t border-gray-100",children:[(0,r.jsx)(v.Tooltip,{title:"Get help on our github",children:(0,r.jsx)(_.Typography.Link,{href:"https://github.com/BerriAI/litellm/issues",target:"_blank",children:"Need Help?"})}),(0,r.jsxs)("div",{className:"space-x-2",children:[(0,r.jsx)(a.Button,{onClick:M,loading:S,children:"Test Connection"}),(0,r.jsx)(a.Button,{loading:h,type:"submit",children:"Add Search Tool"})]})]})]})}),(0,r.jsx)(c.Modal,{title:"Connection Test Results",open:k,onCancel:()=>{w(!1),N(!1)},footer:[(0,r.jsx)(a.Button,{onClick:()=>{w(!1),N(!1)},children:"Close"},"close")],width:700,children:k&&o&&(0,r.jsx)(T,{litellmParams:{search_provider:C.search_provider,api_key:C.api_key,api_base:C.api_base},accessToken:o,onTestComplete:()=>N(!1)},P)})]}):null};var R=e.i(262218),B=e.i(902555);e.i(622826);var E=e.i(200208),F=e.i(399536),A=e.i(500330),M=e.i(530212),L=e.i(304967),O=e.i(350967),q=e.i(678784),D=e.i(118366),H=e.i(888259),Y=e.i(928685);let{Text:X}=_.Typography,W=({searchToolName:e,accessToken:t,className:s=""})=>{let[a,i]=(0,p.useState)(""),[n,c]=(0,p.useState)(!1),[u,h]=(0,p.useState)([]),[g,b]=(0,p.useState)({}),[y,v]=(0,p.useState)(!1),_=async()=>{if(!a.trim())return void H.default.warning("Please enter a search query");c(!0);let r=performance.now();try{let o=await (0,f.searchToolQueryCall)(t,e,a),s=performance.now(),i=Math.round(s-r),l={query:a,response:o,timestamp:Date.now(),latency:i};h(e=>[l,...e])}catch(e){console.error("Error querying search tool:",e),x.default.fromBackend("Failed to query search tool")}finally{c(!1)}},C=e=>new Date(e).toLocaleString(),j=(0,r.jsx)(o.LoadingOutlined,{style:{fontSize:24},spin:!0}),k=u.length>0?u[0]:null;return(0,r.jsxs)(L.Card,{className:"mt-6",children:[(0,r.jsx)("div",{className:"mb-6",children:(0,r.jsx)(l.Title,{children:"Test Search Tool"})}),(0,r.jsxs)("div",{className:"flex flex-col",style:{minHeight:"600px"},children:[(0,r.jsx)("div",{className:"mb-6",children:(0,r.jsxs)("div",{className:"flex items-stretch gap-3",children:[(0,r.jsxs)("div",{className:"flex items-center flex-1 bg-white rounded-lg px-4 transition-all duration-200",style:{border:y?"2px solid #3b82f6":"2px solid #e5e7eb",boxShadow:y?"0 0 0 3px rgba(59, 130, 246, 0.1)":"0 1px 2px 0 rgba(0, 0, 0, 0.05)",height:"48px"},children:[(0,r.jsx)(Y.SearchOutlined,{className:"text-gray-400 mr-3",style:{fontSize:"18px"}}),(0,r.jsx)(d.Input,{value:a,onChange:e=>i(e.target.value),onFocus:()=>v(!0),onBlur:()=>v(!1),onPressEnter:e=>{e.shiftKey||(e.preventDefault(),_())},placeholder:"Enter your search query...",disabled:n,bordered:!1,style:{fontSize:"15px",padding:0,height:"100%",boxShadow:"none"}})]}),(0,r.jsx)(w.Button,{type:"primary",onClick:_,disabled:n||!a.trim(),icon:(0,r.jsx)(Y.SearchOutlined,{}),loading:n,style:{height:"48px",paddingLeft:"24px",paddingRight:"24px",borderRadius:"8px",fontWeight:500,fontSize:"15px",backgroundColor:n||!a.trim()?void 0:"#1890ff",borderColor:n||!a.trim()?void 0:"#1890ff",boxShadow:"0 1px 2px 0 rgba(0, 0, 0, 0.05)"},children:"Search"})]})}),(0,r.jsx)("div",{className:"flex-1",children:k||n?(0,r.jsxs)("div",{children:[n&&(0,r.jsxs)("div",{className:"flex flex-col justify-center items-center py-16",children:[(0,r.jsx)(m.Spin,{indicator:j}),(0,r.jsx)(X,{className:"mt-4 text-gray-600 font-medium",children:"Searching..."})]}),k&&!n&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("div",{className:"mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg",style:{boxShadow:"0 1px 2px 0 rgba(0, 0, 0, 0.05)"},children:(0,r.jsxs)("div",{className:"flex items-center justify-between",children:[(0,r.jsxs)("div",{className:"flex-1",children:[(0,r.jsx)(X,{className:"text-xs font-semibold text-gray-500 uppercase tracking-wide",children:"Search Query"}),(0,r.jsx)("div",{className:"text-base font-semibold text-gray-900 mt-1.5",children:k.query})]}),(0,r.jsxs)("div",{className:"text-right ml-4",children:[(0,r.jsx)(X,{className:"text-xs text-gray-500",children:C(k.timestamp)}),(0,r.jsxs)("div",{className:"flex items-center gap-3 mt-1",children:[(0,r.jsxs)("div",{className:"text-sm font-semibold text-blue-600",children:[k.response?.results?.length||0," ",k.response?.results?.length===1?"result":"results"]}),void 0!==k.latency&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("span",{className:"text-gray-400",children:"•"}),(0,r.jsxs)("div",{className:"text-sm font-semibold text-green-600",children:[k.latency,"ms"]})]})]})]})]})}),k.response&&k.response.results&&k.response.results.length>0?(0,r.jsx)("div",{className:"space-y-3",children:k.response.results.map((e,t)=>{let o=g[`0-${t}`]||!1;return(0,r.jsx)("div",{className:"bg-white border border-gray-200 rounded-lg overflow-hidden transition-all duration-200",style:{boxShadow:"0 1px 2px 0 rgba(0, 0, 0, 0.05)"},onMouseEnter:e=>{e.currentTarget.style.boxShadow="0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",e.currentTarget.style.borderColor="#e0e7ff"},onMouseLeave:e=>{e.currentTarget.style.boxShadow="0 1px 2px 0 rgba(0, 0, 0, 0.05)",e.currentTarget.style.borderColor="#e5e7eb"},children:(0,r.jsxs)("div",{className:"p-5",children:[(0,r.jsxs)("div",{className:"flex items-start justify-between gap-3 mb-2",children:[(0,r.jsx)("a",{href:e.url,target:"_blank",rel:"noopener noreferrer",className:"text-lg font-semibold text-blue-600 hover:text-blue-700 flex-1 leading-snug",style:{textDecoration:"none"},onMouseEnter:e=>e.currentTarget.style.textDecoration="underline",onMouseLeave:e=>e.currentTarget.style.textDecoration="none",children:e.title}),(0,r.jsx)(w.Button,{type:"text",size:"small",className:"shrink-0",icon:(0,r.jsx)("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,r.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"})}),onClick:()=>window.open(e.url,"_blank"),style:{color:"#6b7280"}})]}),(0,r.jsx)("div",{className:"text-sm text-green-700 mb-3 truncate font-medium",children:e.url}),(0,r.jsx)("div",{className:"text-sm text-gray-700 leading-relaxed",children:o?e.snippet:`${e.snippet.substring(0,200)}${e.snippet.length>200?"...":""}`}),e.snippet.length>200&&(0,r.jsx)(w.Button,{type:"link",size:"small",className:"mt-3 p-0 h-auto",onClick:()=>{let e;return e=`0-${t}`,void b(r=>({...r,[e]:!r[e]}))},style:{fontSize:"13px",fontWeight:500,color:"#3b82f6"},children:o?"Show less":"Show more"})]})},t)})}):(0,r.jsxs)("div",{className:"text-center py-12 bg-gray-50 border border-gray-200 rounded-lg",children:[(0,r.jsx)("div",{className:"flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 mx-auto mb-4",children:(0,r.jsx)(Y.SearchOutlined,{style:{fontSize:"24px",color:"#9ca3af"}})}),(0,r.jsx)(X,{className:"text-gray-600 font-medium",children:"No results found"}),(0,r.jsx)(X,{className:"text-sm text-gray-500 mt-1",children:"Try a different search query"})]})]}),u.length>1&&(0,r.jsxs)("div",{className:"mt-8 pt-6 border-t border-gray-200",children:[(0,r.jsxs)("div",{className:"flex items-center justify-between mb-4",children:[(0,r.jsx)(X,{className:"text-sm font-semibold text-gray-700",children:"Previous Searches"}),(0,r.jsx)(w.Button,{onClick:()=>{h([]),b({}),x.default.success("Search history cleared")},size:"small",type:"link",style:{fontSize:"13px",fontWeight:500},children:"Clear All"})]}),(0,r.jsx)("div",{className:"space-y-2",children:u.slice(1,6).map((e,t)=>(0,r.jsxs)("div",{className:"p-3 bg-gray-50 border border-gray-200 rounded-lg cursor-pointer transition-all duration-200 hover:bg-gray-100 hover:border-gray-300",onClick:()=>{i(e.query)},children:[(0,r.jsx)("div",{className:"text-sm font-medium text-gray-800 truncate",children:e.query}),(0,r.jsxs)("div",{className:"text-xs text-gray-500 mt-1.5 flex items-center gap-2",children:[(0,r.jsxs)("span",{className:"font-medium text-blue-600",children:[e.response?.results?.length||0," ",e.response?.results?.length===1?"result":"results"]}),void 0!==e.latency&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("span",{children:"•"}),(0,r.jsxs)("span",{className:"font-medium text-green-600",children:[e.latency,"ms"]})]}),(0,r.jsx)("span",{children:"•"}),(0,r.jsx)("span",{children:C(e.timestamp)})]})]},t+1))})]})]}):(0,r.jsxs)("div",{className:"h-full flex flex-col items-center justify-center p-8",children:[(0,r.jsx)("div",{className:"flex items-center justify-center w-24 h-24 rounded-full bg-gray-100 mb-6",children:(0,r.jsx)(Y.SearchOutlined,{style:{fontSize:"48px",color:"#9ca3af"}})}),(0,r.jsx)(X,{className:"text-lg text-gray-600 font-medium",children:"Test your search tool"}),(0,r.jsx)(X,{className:"text-sm text-gray-500 mt-2",children:"Enter a query above to see search results"})]})})]})]})},K=({searchTool:e,onBack:t,isEditing:o,accessToken:s,availableProviders:n})=>{var d;let c,[u,m]=(0,p.useState)({}),h=async(e,r)=>{await (0,A.copyToClipboard)(e)&&(m(e=>({...e,[r]:!0})),setTimeout(()=>{m(e=>({...e,[r]:!1}))},2e3))};return(0,r.jsxs)("div",{className:"p-4 max-w-full",children:[(0,r.jsx)("div",{className:"flex justify-between items-center mb-6",children:(0,r.jsxs)("div",{children:[(0,r.jsx)(a.Button,{icon:M.ArrowLeftIcon,variant:"light",className:"mb-4",onClick:t,children:"Back to All Search Tools"}),(0,r.jsxs)("div",{className:"flex items-center cursor-pointer",children:[(0,r.jsx)(l.Title,{children:e.search_tool_name}),(0,r.jsx)(w.Button,{type:"text",size:"small",icon:u["search-tool-name"]?(0,r.jsx)(q.CheckIcon,{size:12}):(0,r.jsx)(D.CopyIcon,{size:12}),onClick:()=>h(e.search_tool_name,"search-tool-name"),className:`left-2 z-10 transition-all duration-200 ${u["search-tool-name"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]}),(0,r.jsxs)("div",{className:"flex items-center cursor-pointer",children:[(0,r.jsx)(i.Text,{className:"text-gray-500 font-mono",children:e.search_tool_id}),(0,r.jsx)(w.Button,{type:"text",size:"small",icon:u["search-tool-id"]?(0,r.jsx)(q.CheckIcon,{size:12}):(0,r.jsx)(D.CopyIcon,{size:12}),onClick:()=>h(e.search_tool_id,"search-tool-id"),className:`left-2 z-10 transition-all duration-200 ${u["search-tool-id"]?"text-green-600 bg-green-50 border-green-200":"text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`})]})]})}),(0,r.jsxs)(O.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,r.jsxs)(L.Card,{children:[(0,r.jsx)(i.Text,{children:"Provider"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)(l.Title,{children:(d=e.litellm_params.search_provider,c=n.find(e=>e.provider_name===d),c?.ui_friendly_name||d)})})]}),(0,r.jsxs)(L.Card,{children:[(0,r.jsx)(i.Text,{children:"API Key"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)(i.Text,{children:e.litellm_params.api_key?"****":"Not set"})})]}),(0,r.jsxs)(L.Card,{children:[(0,r.jsx)(i.Text,{children:"Created At"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)(i.Text,{children:e.created_at?new Date(e.created_at).toLocaleString():"Unknown"})})]})]}),e.search_tool_info?.description&&(0,r.jsxs)(L.Card,{className:"mt-6",children:[(0,r.jsx)(i.Text,{children:"Description"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)(i.Text,{children:e.search_tool_info.description})})]}),(0,r.jsx)("div",{className:"mt-6",children:s&&(0,r.jsx)(W,{searchToolName:e.search_tool_name,accessToken:s})})]})},U=({accessToken:e,userRole:b,userID:y})=>{let{data:v,isLoading:_,refetch:C}=(0,s.useQuery)({queryKey:["searchTools"],queryFn:()=>{if(!e)throw Error("Access Token required");return(0,f.fetchSearchTools)(e).then(e=>e.search_tools||[])},enabled:!!e}),{data:j,isLoading:k}=(0,s.useQuery)({queryKey:["searchProviders"],queryFn:()=>{if(!e)throw Error("Access Token required");return(0,f.fetchAvailableSearchProviders)(e)},enabled:!!e}),w=j?.providers||[],[S,N]=(0,p.useState)(null),[T,I]=(0,p.useState)(!1),[z,A]=(0,p.useState)(!1),[M,L]=(0,p.useState)(null),[O,q]=(0,p.useState)(!1),[D,H]=(0,p.useState)(!1),[Y,X]=(0,p.useState)(!1),[W]=n.Form.useForm(),U=p.default.useMemo(()=>{let e,t,o;return e=e=>{L(e),q(!1)},t=e=>{let r=v?.find(r=>r.search_tool_id===e);r&&(W.setFieldsValue({search_tool_name:r.search_tool_name,search_provider:r.litellm_params.search_provider,api_key:r.litellm_params.api_key,api_base:r.litellm_params.api_base,timeout:r.litellm_params.timeout,max_retries:r.litellm_params.max_retries,description:r.search_tool_info?.description}),L(e),X(!0))},o=V,[{title:"Search Tool ID",dataIndex:"search_tool_id",key:"search_tool_id",render:(t,o)=>o.is_from_config?(0,r.jsx)("span",{className:"text-xs",children:"-"}):(0,r.jsx)(F.IdCell,{value:o.search_tool_id,onClick:e})},{title:"Name",dataIndex:"search_tool_name",key:"search_tool_name",render:e=>(0,r.jsx)("span",{className:"font-medium",children:e})},{title:"Provider",key:"provider",render:(e,t)=>{let o=t.litellm_params.search_provider,s=w.find(e=>e.provider_name===o),a=s?.ui_friendly_name||o;return(0,r.jsx)("span",{className:"text-sm",children:a})}},{title:"Created At",dataIndex:"created_at",key:"created_at",render:(e,t)=>(0,r.jsx)(E.DateCell,{value:t.created_at,precision:"date"})},{title:"Updated At",dataIndex:"updated_at",key:"updated_at",render:(e,t)=>(0,r.jsx)(E.DateCell,{value:t.updated_at,precision:"date"})},{title:"Source",key:"source",render:(e,t)=>{let o=t.is_from_config??!1;return(0,r.jsx)(R.Tag,{color:o?"default":"blue",children:o?"Config":"DB"})}},{title:"Actions",key:"actions",render:(e,s)=>{let a=s.search_tool_id,i=s.is_from_config??!1;return(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)(B.default,{variant:"Edit",tooltipText:"Edit search tool",disabled:i,disabledTooltipText:"Config search tool cannot be edited on the dashboard. Please edit it from the config file.",onClick:()=>{a&&!i&&t(a)}}),(0,r.jsx)(B.default,{variant:"Delete",tooltipText:"Delete search tool",disabled:i,disabledTooltipText:"Config search tool cannot be deleted on the dashboard. Please delete it from the config file.",onClick:()=>{a&&!i&&o(a)}})]})}}]},[w,v,W]);function V(e){N(e),I(!0)}let $=async()=>{if(null!=S&&null!=e){A(!0);try{await (0,f.deleteSearchTool)(e,S),x.default.success("Deleted search tool successfully"),I(!1),N(null),C()}catch(e){console.error("Error deleting the search tool:",e),x.default.error("Failed to delete search tool")}finally{A(!1)}}},G=v?.find(e=>e.search_tool_id===S),Q=G?w.find(e=>e.provider_name===G.litellm_params.search_provider):null,Z=async()=>{if(e&&M)try{let r=await W.validateFields(),t={search_tool_name:r.search_tool_name,litellm_params:{search_provider:r.search_provider,api_key:r.api_key,api_base:r.api_base,timeout:r.timeout?parseFloat(r.timeout):void 0,max_retries:r.max_retries?parseInt(r.max_retries):void 0},search_tool_info:r.description?{description:r.description}:void 0};await (0,f.updateSearchTool)(e,M,t),x.default.success("Search tool updated successfully"),X(!1),W.resetFields(),L(null),C()}catch(e){console.error("Failed to update search tool:",e),x.default.error("Failed to update search tool")}};return e&&b&&y?(0,r.jsxs)("div",{className:"w-full h-full p-6",children:[(0,r.jsx)(g.default,{isOpen:T,title:"Delete Search Tool",message:"Are you sure you want to delete this search tool? This action cannot be undone.",resourceInformationTitle:"Search Tool Information",resourceInformation:G?[{label:"Name",value:G.search_tool_name},{label:"ID",value:G.search_tool_id,code:!0},{label:"Provider",value:Q?.ui_friendly_name||G.litellm_params.search_provider},{label:"Description",value:G.search_tool_info?.description||"-"}]:[],onCancel:()=>{I(!1),N(null)},onOk:$,confirmLoading:z}),(0,r.jsx)(P,{userRole:b,accessToken:e,onCreateSuccess:e=>{H(!1),C()},isModalVisible:D,setModalVisible:H}),(0,r.jsx)(c.Modal,{title:"Edit Search Tool",open:Y,onOk:Z,onCancel:()=>{X(!1),W.resetFields(),L(null)},width:600,children:(0,r.jsxs)(n.Form,{form:W,layout:"vertical",children:[(0,r.jsx)(n.Form.Item,{name:"search_tool_name",label:"Search Tool Name",rules:[{required:!0,message:"Please enter a search tool name"}],children:(0,r.jsx)(d.Input,{placeholder:"e.g., my-perplexity-search"})}),(0,r.jsx)(n.Form.Item,{name:"search_provider",label:"Search Provider",rules:[{required:!0,message:"Please select a search provider"}],children:(0,r.jsx)(u.Select,{placeholder:"Select a search provider",loading:k,children:w.map(e=>(0,r.jsx)(u.Select.Option,{value:e.provider_name,children:e.ui_friendly_name},e.provider_name))})}),(0,r.jsx)(n.Form.Item,{name:"api_key",label:"API Key",extra:"API key for the search provider",children:(0,r.jsx)(d.Input.Password,{placeholder:"Enter API key"})}),(0,r.jsx)(n.Form.Item,{name:"description",label:"Description",children:(0,r.jsx)(d.Input.TextArea,{rows:3,placeholder:"Description of this search tool"})})]})}),(0,r.jsx)(l.Title,{children:"Search Tools"}),(0,r.jsx)(i.Text,{className:"text-tremor-content mt-2",children:"Configure and manage your search providers"}),(0,t.isAdminRole)(b)&&(0,r.jsx)(a.Button,{className:"mt-4 mb-4",onClick:()=>H(!0),children:"+ Add New Search Tool"}),(0,r.jsx)(()=>M?(0,r.jsx)(K,{searchTool:v?.find(e=>e.search_tool_id===M)||{search_tool_id:"",search_tool_name:"",litellm_params:{search_provider:""}},onBack:()=>{q(!1),L(null),C()},isEditing:O,accessToken:e,availableProviders:w}):(0,r.jsx)("div",{className:"w-full h-full",children:(0,r.jsx)(m.Spin,{spinning:_,indicator:(0,r.jsx)(o.LoadingOutlined,{spin:!0}),size:"large",children:(0,r.jsx)(h.Table,{bordered:!0,dataSource:v||[],columns:U,rowKey:e=>e.search_tool_id||e.search_tool_name,pagination:!1,locale:{emptyText:"No search tools configured"},size:"small"})})}),{})]}):(0,r.jsx)("div",{className:"p-6 text-center text-gray-500",children:"Missing required authentication parameters."})};var V=e.i(135214);e.s(["default",0,function(){let{accessToken:e,userRole:t,userId:o}=(0,V.default)();return(0,r.jsx)(U,{accessToken:e,userRole:t,userID:o})}],962296)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0qulu-1pxxbt3.js b/litellm/proxy/_experimental/out/_next/static/chunks/0qulu-1pxxbt3.js new file mode 100644 index 00000000000..4d203167da1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0qulu-1pxxbt3.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),n=e.i(480731),o=e.i(444755),s=e.i(673706),i=e.i(95779);let l={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},u={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},c=(0,s.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:h,variant:f="simple",tooltip:g,size:p=n.Sizes.SM,color:b,className:v}=e,C=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),w=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,s.getColorClassNames)(t,i.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(f,b),{tooltipProps:x,getReferenceProps:y}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,s.mergeRefs)([m,x.refs.setReference]),className:(0,o.tremorTwMerge)(c("root"),"inline-flex shrink-0 items-center justify-center",w.bgColor,w.textColor,w.borderColor,w.ringColor,u[f].rounded,u[f].border,u[f].shadow,u[f].ring,l[p].paddingX,l[p].paddingY,v)},y,C),r.default.createElement(a.default,Object.assign({text:g},x)),r.default.createElement(h,{className:(0,o.tremorTwMerge)(c("icon"),"shrink-0",d[p].height,d[p].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),a=e.i(540143),n=e.i(915823),o=e.i(619273),s=class extends n.Subscribable{#e;#t=void 0;#r;#a;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#n()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,o.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,o.hashKey)(t.mutationKey)!==(0,o.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#n(),this.#o(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#n(),this.#o()}mutate(e,t){return this.#a=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#n(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#o(e){a.notifyManager.batch(()=>{if(this.#a&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,a={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#a.onSuccess?.(e.data,t,r,a)}catch(e){Promise.reject(e)}try{this.#a.onSettled?.(e.data,null,t,r,a)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#a.onError?.(e.error,t,r,a)}catch(e){Promise.reject(e)}try{this.#a.onSettled?.(void 0,e.error,t,r,a)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},i=e.i(912598);e.s(["useMutation",0,function(e,r){let n=(0,i.useQueryClient)(r),[l]=t.useState(()=>new s(n,e));t.useEffect(()=>{l.setOptions(e)},[l,e]);let d=t.useSyncExternalStore(t.useCallback(e=>l.subscribe(a.notifyManager.batchCalls(e)),[l]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),u=t.useCallback((e,t)=>{l.mutate(e,t).catch(o.noop)},[l]);if(d.error&&(0,o.shouldThrowError)(l.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:u,mutateAsync:d.mutate}}],954616)},595727,234662,677241,281092,688594,e=>{"use strict";let t=Symbol.for("constructDateFrom");function r(e,r){return"function"==typeof e?e(r):e&&"object"==typeof e&&t in e?e[t](r):e instanceof Date?new e.constructor(r):new Date(r)}function a(e,t){return r(t||e,e)}e.s(["constructFromSymbol",0,t,"millisecondsInDay",0,864e5,"millisecondsInHour",0,36e5,"millisecondsInMinute",0,6e4,"millisecondsInSecond",0,1e3,"millisecondsInWeek",0,6048e5],234662),e.s(["constructFrom",0,r],677241),e.s(["toDate",0,a],281092),e.s(["addDays",0,function(e,t,n){let o=a(e,n?.in);return isNaN(t)?r(n?.in||e,NaN):(t&&o.setDate(o.getDate()+t),o)}],595727),e.s(["addMonths",0,function(e,t,n){let o=a(e,n?.in);if(isNaN(t))return r(n?.in||e,NaN);if(!t)return o;let s=o.getDate(),i=r(n?.in||e,o.getTime());return(i.setMonth(o.getMonth()+t+1,0),s>=i.getDate())?i:(o.setFullYear(i.getFullYear(),i.getMonth(),s),o)}],688594)},888288,e=>{"use strict";var t=e.i(271645);e.s(["default",0,(e,r)=>{let a=void 0!==r,[n,o]=(0,t.useState)(e);return[a?r:n,e=>{a||o(e)}]}])},757440,e=>{"use strict";var t=e.i(290571),r=e.i(271645);e.s(["default",0,e=>{var a=(0,t.__rest)(e,[]);return r.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},a),r.default.createElement("path",{d:"M11.9999 13.1714L16.9497 8.22168L18.3639 9.63589L11.9999 15.9999L5.63599 9.63589L7.0502 8.22168L11.9999 13.1714Z"}))}])},446428,854056,e=>{"use strict";let t;var r=e.i(290571),a=e.i(271645);e.s(["default",0,e=>{var t=(0,r.__rest)(e,[]);return a.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),a.default.createElement("path",{d:"M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 10.5858L9.17157 7.75736L7.75736 9.17157L10.5858 12L7.75736 14.8284L9.17157 16.2426L12 13.4142L14.8284 16.2426L16.2426 14.8284L13.4142 12L16.2426 9.17157L14.8284 7.75736L12 10.5858Z"}))}],446428);var n=e.i(746725),o=e.i(914189),s=e.i(553521),i=e.i(835696),l=e.i(941444),d=e.i(178677),u=e.i(294316),c=e.i(83733),m=e.i(233137),h=e.i(732607),f=e.i(397701),g=e.i(700020);function p(e){var t;return!!(e.enter||e.enterFrom||e.enterTo||e.leave||e.leaveFrom||e.leaveTo)||(null!=(t=e.as)?t:y)!==a.Fragment||1===a.default.Children.count(e.children)}let b=(0,a.createContext)(null);b.displayName="TransitionContext";var v=((t=v||{}).Visible="visible",t.Hidden="hidden",t);let C=(0,a.createContext)(null);function w(e){return"children"in e?w(e.children):e.current.filter(({el:e})=>null!==e.current).filter(({state:e})=>"visible"===e).length>0}function x(e,t){let r=(0,l.useLatestValue)(e),i=(0,a.useRef)([]),d=(0,s.useIsMounted)(),u=(0,n.useDisposables)(),c=(0,o.useEvent)((e,t=g.RenderStrategy.Hidden)=>{let a=i.current.findIndex(({el:t})=>t===e);-1!==a&&((0,f.match)(t,{[g.RenderStrategy.Unmount](){i.current.splice(a,1)},[g.RenderStrategy.Hidden](){i.current[a].state="hidden"}}),u.microTask(()=>{var e;!w(i)&&d.current&&(null==(e=r.current)||e.call(r))}))}),m=(0,o.useEvent)(e=>{let t=i.current.find(({el:t})=>t===e);return t?"visible"!==t.state&&(t.state="visible"):i.current.push({el:e,state:"visible"}),()=>c(e,g.RenderStrategy.Unmount)}),h=(0,a.useRef)([]),p=(0,a.useRef)(Promise.resolve()),b=(0,a.useRef)({enter:[],leave:[]}),v=(0,o.useEvent)((e,r,a)=>{h.current.splice(0),t&&(t.chains.current[r]=t.chains.current[r].filter(([t])=>t!==e)),null==t||t.chains.current[r].push([e,new Promise(e=>{h.current.push(e)})]),null==t||t.chains.current[r].push([e,new Promise(e=>{Promise.all(b.current[r].map(([e,t])=>t)).then(()=>e())})]),"enter"===r?p.current=p.current.then(()=>null==t?void 0:t.wait.current).then(()=>a(r)):a(r)}),C=(0,o.useEvent)((e,t,r)=>{Promise.all(b.current[t].splice(0).map(([e,t])=>t)).then(()=>{var e;null==(e=h.current.shift())||e()}).then(()=>r(t))});return(0,a.useMemo)(()=>({children:i,register:m,unregister:c,onStart:v,onStop:C,wait:p,chains:b}),[m,c,i,v,C,b,p])}C.displayName="NestingContext";let y=a.Fragment,k=g.RenderFeatures.RenderStrategy,E=(0,g.forwardRefWithAs)(function(e,t){let{show:r,appear:n=!1,unmount:s=!0,...l}=e,c=(0,a.useRef)(null),h=p(e),f=(0,u.useSyncRefs)(...h?[c,t]:null===t?[]:[t]);(0,d.useServerHandoffComplete)();let v=(0,m.useOpenClosed)();if(void 0===r&&null!==v&&(r=(v&m.State.Open)===m.State.Open),void 0===r)throw Error("A is used but it is missing a `show={true | false}` prop.");let[y,E]=(0,a.useState)(r?"visible":"hidden"),N=x(()=>{r||E("hidden")}),[R,T]=(0,a.useState)(!0),S=(0,a.useRef)([r]);(0,i.useIsoMorphicEffect)(()=>{!1!==R&&S.current[S.current.length-1]!==r&&(S.current.push(r),T(!1))},[S,r]);let O=(0,a.useMemo)(()=>({show:r,appear:n,initial:R}),[r,n,R]);(0,i.useIsoMorphicEffect)(()=>{r?E("visible"):w(N)||null===c.current||E("hidden")},[r,N]);let P={unmount:s},L=(0,o.useEvent)(()=>{var t;R&&T(!1),null==(t=e.beforeEnter)||t.call(e)}),j=(0,o.useEvent)(()=>{var t;R&&T(!1),null==(t=e.beforeLeave)||t.call(e)}),I=(0,g.useRender)();return a.default.createElement(C.Provider,{value:N},a.default.createElement(b.Provider,{value:O},I({ourProps:{...P,as:a.Fragment,children:a.default.createElement(M,{ref:f,...P,...l,beforeEnter:L,beforeLeave:j})},theirProps:{},defaultTag:a.Fragment,features:k,visible:"visible"===y,name:"Transition"})))}),M=(0,g.forwardRefWithAs)(function(e,t){var r,n;let{transition:s=!0,beforeEnter:l,afterEnter:v,beforeLeave:E,afterLeave:M,enter:N,enterFrom:R,enterTo:T,entered:S,leave:O,leaveFrom:P,leaveTo:L,...j}=e,[I,F]=(0,a.useState)(null),D=(0,a.useRef)(null),A=p(e),H=(0,u.useSyncRefs)(...A?[D,t,F]:null===t?[]:[t]),K=null==(r=j.unmount)||r?g.RenderStrategy.Unmount:g.RenderStrategy.Hidden,{show:V,appear:_,initial:z}=function(){let e=(0,a.useContext)(b);if(null===e)throw Error("A is used but it is missing a parent or .");return e}(),[B,U]=(0,a.useState)(V?"visible":"hidden"),Y=function(){let e=(0,a.useContext)(C);if(null===e)throw Error("A is used but it is missing a parent or .");return e}(),{register:W,unregister:X}=Y;(0,i.useIsoMorphicEffect)(()=>W(D),[W,D]),(0,i.useIsoMorphicEffect)(()=>{if(K===g.RenderStrategy.Hidden&&D.current)return V&&"visible"!==B?void U("visible"):(0,f.match)(B,{hidden:()=>X(D),visible:()=>W(D)})},[B,D,W,X,V,K]);let q=(0,d.useServerHandoffComplete)();(0,i.useIsoMorphicEffect)(()=>{if(A&&q&&"visible"===B&&null===D.current)throw Error("Did you forget to passthrough the `ref` to the actual DOM node?")},[D,B,q,A]);let Z=z&&!_,Q=_&&V&&z,G=(0,a.useRef)(!1),J=x(()=>{G.current||(U("hidden"),X(D))},Y),$=(0,o.useEvent)(e=>{G.current=!0,J.onStart(D,e?"enter":"leave",e=>{"enter"===e?null==l||l():"leave"===e&&(null==E||E())})}),ee=(0,o.useEvent)(e=>{let t=e?"enter":"leave";G.current=!1,J.onStop(D,t,e=>{"enter"===e?null==v||v():"leave"===e&&(null==M||M())}),"leave"!==t||w(J)||(U("hidden"),X(D))});(0,a.useEffect)(()=>{A&&s||($(V),ee(V))},[V,A,s]);let et=!(!s||!A||!q||Z),[,er]=(0,c.useTransition)(et,I,V,{start:$,end:ee}),ea=(0,g.compact)({ref:H,className:(null==(n=(0,h.classNames)(j.className,Q&&N,Q&&R,er.enter&&N,er.enter&&er.closed&&R,er.enter&&!er.closed&&T,er.leave&&O,er.leave&&!er.closed&&P,er.leave&&er.closed&&L,!er.transition&&V&&S))?void 0:n.trim())||void 0,...(0,c.transitionDataAttributes)(er)}),en=0;"visible"===B&&(en|=m.State.Open),"hidden"===B&&(en|=m.State.Closed),er.enter&&(en|=m.State.Opening),er.leave&&(en|=m.State.Closing);let eo=(0,g.useRender)();return a.default.createElement(C.Provider,{value:J},a.default.createElement(m.OpenClosedProvider,{value:en},eo({ourProps:ea,theirProps:j,defaultTag:y,features:k,visible:"visible"===B,name:"Transition.Child"})))}),N=(0,g.forwardRefWithAs)(function(e,t){let r=null!==(0,a.useContext)(b),n=null!==(0,m.useOpenClosed)();return a.default.createElement(a.default.Fragment,null,!r&&n?a.default.createElement(E,{ref:t,...e}):a.default.createElement(M,{ref:t,...e}))}),R=Object.assign(E,{Child:N,Root:E});e.s(["Transition",0,R],854056)},206929,e=>{"use strict";var t=e.i(290571),r=e.i(757440),a=e.i(271645),n=e.i(446428),o=e.i(444755),s=e.i(673706),i=e.i(103471),l=e.i(495470),d=e.i(854056),u=e.i(888288);let c=(0,s.makeClassName)("Select"),m=a.default.forwardRef((e,s)=>{let{defaultValue:m="",value:h,onValueChange:f,placeholder:g="Select...",disabled:p=!1,icon:b,enableClear:v=!1,required:C,children:w,name:x,error:y=!1,errorMessage:k,className:E,id:M}=e,N=(0,t.__rest)(e,["defaultValue","value","onValueChange","placeholder","disabled","icon","enableClear","required","children","name","error","errorMessage","className","id"]),R=(0,a.useRef)(null),T=a.Children.toArray(w),[S,O]=(0,u.default)(m,h),P=(0,a.useMemo)(()=>{let e=a.default.Children.toArray(w).filter(a.isValidElement);return(0,i.constructValueToNameMapping)(e)},[w]);return a.default.createElement("div",{className:(0,o.tremorTwMerge)("w-full min-w-[10rem] text-tremor-default",E)},a.default.createElement("div",{className:"relative"},a.default.createElement("select",{title:"select-hidden",required:C,className:(0,o.tremorTwMerge)("h-full w-full absolute left-0 top-0 -z-10 opacity-0"),value:S,onChange:e=>{e.preventDefault()},name:x,disabled:p,id:M,onFocus:()=>{let e=R.current;e&&e.focus()}},a.default.createElement("option",{className:"hidden",value:"",disabled:!0,hidden:!0},g),T.map(e=>{let t=e.props.value,r=e.props.children;return a.default.createElement("option",{className:"hidden",key:t,value:t},r)})),a.default.createElement(l.Listbox,Object.assign({as:"div",ref:s,defaultValue:S,value:S,onChange:e=>{null==f||f(e),O(e)},disabled:p,id:M},N),({value:e})=>{var t;return a.default.createElement(a.default.Fragment,null,a.default.createElement(l.ListboxButton,{ref:R,className:(0,o.tremorTwMerge)("w-full outline-none text-left whitespace-nowrap truncate rounded-tremor-default focus:ring-2 transition duration-100 border pr-8 py-2","border-tremor-border shadow-tremor-input focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted","dark:border-dark-tremor-border dark:shadow-dark-tremor-input dark:focus:border-dark-tremor-brand-subtle dark:focus:ring-dark-tremor-brand-muted",b?"pl-10":"pl-3",(0,i.getSelectButtonColors)((0,i.hasValue)(e),p,y))},b&&a.default.createElement("span",{className:(0,o.tremorTwMerge)("absolute inset-y-0 left-0 flex items-center ml-px pl-2.5")},a.default.createElement(b,{className:(0,o.tremorTwMerge)(c("Icon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})),a.default.createElement("span",{className:"w-[90%] block truncate"},e&&null!=(t=P.get(e))?t:g),a.default.createElement("span",{className:(0,o.tremorTwMerge)("absolute inset-y-0 right-0 flex items-center mr-3")},a.default.createElement(r.default,{className:(0,o.tremorTwMerge)(c("arrowDownIcon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")}))),v&&S?a.default.createElement("button",{type:"button",className:(0,o.tremorTwMerge)("absolute inset-y-0 right-0 flex items-center mr-8"),onClick:e=>{e.preventDefault(),O(""),null==f||f("")}},a.default.createElement(n.default,{className:(0,o.tremorTwMerge)(c("clearIcon"),"flex-none h-4 w-4","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})):null,a.default.createElement(d.Transition,{enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},a.default.createElement(l.ListboxOptions,{anchor:"bottom start",className:(0,o.tremorTwMerge)("z-10 w-[var(--button-width)] divide-y overflow-y-auto outline-none rounded-tremor-default max-h-[228px] border [--anchor-gap:4px]","bg-tremor-background border-tremor-border divide-tremor-border shadow-tremor-dropdown","dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border dark:shadow-dark-tremor-dropdown")},w)))})),y&&k?a.default.createElement("p",{className:(0,o.tremorTwMerge)("errorMessage","text-sm text-rose-500 mt-1")},k):null)});m.displayName="Select",e.s(["Select",0,m],206929)},515288,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let n=r.forwardRef(({className:e,size:r="default",...n},o)=>(0,t.jsx)("div",{ref:o,"data-slot":"card","data-size":r,className:(0,a.cn)("group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 [--card-spacing:--spacing(6)] has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(4)] *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",e),...n}));n.displayName="Card";let o=r.forwardRef(({className:e,...r},n)=>(0,t.jsx)("div",{ref:n,"data-slot":"card-header",className:(0,a.cn)("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",e),...r}));o.displayName="CardHeader";let s=r.forwardRef(({className:e,...r},n)=>(0,t.jsx)("div",{ref:n,"data-slot":"card-title",className:(0,a.cn)("text-base leading-normal font-medium group-data-[size=sm]/card:text-sm",e),...r}));s.displayName="CardTitle",r.forwardRef(({className:e,...r},n)=>(0,t.jsx)("div",{ref:n,"data-slot":"card-description",className:(0,a.cn)("text-sm text-muted-foreground",e),...r})).displayName="CardDescription";let i=r.forwardRef(({className:e,...r},n)=>(0,t.jsx)("div",{ref:n,"data-slot":"card-action",className:(0,a.cn)("col-start-2 row-span-2 row-start-1 self-start justify-self-end",e),...r}));i.displayName="CardAction";let l=r.forwardRef(({className:e,...r},n)=>(0,t.jsx)("div",{ref:n,"data-slot":"card-content",className:(0,a.cn)("px-(--card-spacing)",e),...r}));l.displayName="CardContent",r.forwardRef(({className:e,...r},n)=>(0,t.jsx)("div",{ref:n,"data-slot":"card-footer",className:(0,a.cn)("flex items-center rounded-b-xl px-(--card-spacing) [.border-t]:pt-(--card-spacing)",e),...r})).displayName="CardFooter",e.s(["Card",0,n,"CardAction",0,i,"CardContent",0,l,"CardHeader",0,o,"CardTitle",0,s])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0r9irx-7_i6hr.js b/litellm/proxy/_experimental/out/_next/static/chunks/0r9irx-7_i6hr.js new file mode 100644 index 00000000000..a37f711c0d5 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0r9irx-7_i6hr.js @@ -0,0 +1,3 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,894660,283086,e=>{"use strict";var t=e.i(801312);e.s(["LeftOutlined",()=>t.default],894660);let s=(0,e.i(475254).default)("sparkles",[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",key:"4pj2yx"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M22 5h-4",key:"1gvqau"}],["path",{d:"M4 17v2",key:"vumght"}],["path",{d:"M5 18H3",key:"zchphs"}]]);e.s(["Sparkles",0,s],283086)},97859,e=>{"use strict";e.s(["AGENT_CALL_TYPES",0,["asend_message"],"ERROR_CODE_OPTIONS",0,[{label:"400 - Bad Request",value:"400"},{label:"401 - Invalid Authentication",value:"401"},{label:"403 - Permission Denied",value:"403"},{label:"404 - Not Found",value:"404"},{label:"408 - Request Timeout",value:"408"},{label:"422 - Unprocessable Entity",value:"422"},{label:"429 - Rate Limited",value:"429"},{label:"500 - Internal Server Error",value:"500"},{label:"502 - Bad Gateway",value:"502"},{label:"503 - Service Unavailable",value:"503"},{label:"529 - Overloaded",value:"529"}],"MCP_CALL_TYPES",0,["call_mcp_tool","list_mcp_tools"],"QUICK_SELECT_OPTIONS",0,[{label:"Last Minute",value:1,unit:"minutes"},{label:"Last 15 Minutes",value:15,unit:"minutes"},{label:"Last Hour",value:1,unit:"hours"},{label:"Last 4 Hours",value:4,unit:"hours"},{label:"Last 24 Hours",value:24,unit:"hours"},{label:"Last 7 Days",value:7,unit:"days"}]])},3565,502626,e=>{"use strict";var t=e.i(843476),s=e.i(271645),l=e.i(464571),a=e.i(608856),r=e.i(560025),n=e.i(492030),i=e.i(166406),o=e.i(894660),d=e.i(240647),c=e.i(531245),m=e.i(283086),x=e.i(195116),u=e.i(97859),p=e.i(770914),h=e.i(262218),g=e.i(592968),f=e.i(898586),y=e.i(149192),j=e.i(536591),j=j,v=e.i(755151),b=e.i(166540),N=e.i(916925);let _="24px",w="request",k="response",S="monospace",C="#f0f0f0",{Text:T}=f.Typography;function L({log:e,onClose:s,onPrevious:l,onNext:a,statusLabel:r,statusColor:n,environment:i}){let o=e.custom_llm_provider||"",d=o?(0,N.getProviderLogoAndName)(o):null;return(0,t.jsxs)("div",{style:{padding:"16px 24px",borderBottom:`1px solid ${C}`,backgroundColor:"#fff",position:"sticky",top:0,zIndex:10},children:[(0,t.jsx)(A,{model:e.model,providerLogo:d?.logo,providerName:d?.displayName}),(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:8},children:[(0,t.jsx)(E,{requestId:e.request_id}),(0,t.jsx)(M,{onPrevious:l,onNext:a,onClose:s})]}),(0,t.jsx)(I,{log:e,statusLabel:r,statusColor:n,environment:i})]})}function A({model:e,providerLogo:s,providerName:l}){return(0,t.jsxs)(p.Space,{size:8,style:{marginBottom:8},children:[s&&(0,t.jsx)("img",{src:s,alt:l||"Provider",style:{width:24,height:24},onError:e=>{e.target.style.display="none"}}),(0,t.jsxs)(p.Space,{size:8,direction:"horizontal",children:[(0,t.jsx)(T,{strong:!0,style:{fontSize:14},children:e}),l&&(0,t.jsx)(T,{type:"secondary",style:{fontSize:12},children:l})]})]})}function E({requestId:e}){return(0,t.jsx)("div",{style:{flex:1,minWidth:0},children:(0,t.jsx)(g.Tooltip,{title:e,children:(0,t.jsx)(T,{strong:!0,copyable:{text:e,tooltips:["Copy Request ID","Copied!"]},style:{fontSize:16,fontFamily:S,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",display:"block"},children:e})})})}function M({onPrevious:e,onNext:s,onClose:a}){let r={border:"1px solid #d9d9d9",borderRadius:4,padding:"0 4px",fontSize:12,fontFamily:"monospace",marginLeft:4,background:"#fafafa"};return(0,t.jsxs)(p.Space,{size:4,split:(0,t.jsx)("div",{style:{width:1,height:20,background:C}}),children:[(0,t.jsxs)(l.Button,{type:"text",size:"small",onClick:e,children:[(0,t.jsx)(j.default,{}),(0,t.jsx)("span",{style:r,children:"K"})]}),(0,t.jsxs)(l.Button,{type:"text",size:"small",onClick:s,children:[(0,t.jsx)(v.DownOutlined,{}),(0,t.jsx)("span",{style:r,children:"J"})]}),(0,t.jsx)(g.Tooltip,{title:"ESC to close",children:(0,t.jsx)(l.Button,{type:"text",icon:(0,t.jsx)(y.CloseOutlined,{}),onClick:a})})]})}function I({log:e,statusLabel:s,statusColor:l,environment:a}){return(0,t.jsxs)(p.Space,{size:12,children:[(0,t.jsx)(h.Tag,{color:l,children:s}),(0,t.jsxs)(h.Tag,{children:["Env: ",a]}),(0,t.jsxs)(p.Space,{size:8,children:[(0,t.jsx)(T,{type:"secondary",style:{fontSize:13},children:(0,b.default)(e.startTime).format("MMM D, YYYY h:mm:ss A")}),(0,t.jsxs)(T,{type:"secondary",style:{fontSize:13},children:["(",(0,b.default)(e.startTime).fromNow(),")"]})]})]})}var z=e.i(869216),D=e.i(175712),O=e.i(653496),B=e.i(560445),R=e.i(362024),F=e.i(91739),P=e.i(482725),q=e.i(500330);let $=e=>e>=.8?"text-green-600":"text-yellow-600",W=({entities:e})=>{let[l,a]=(0,s.useState)(!0),[r,n]=(0,s.useState)({});return e&&0!==e.length?(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("div",{className:"flex items-center mb-2 cursor-pointer",onClick:()=>a(!l),children:[(0,t.jsx)("svg",{className:`w-5 h-5 mr-2 transition-transform ${l?"transform rotate-90":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})}),(0,t.jsxs)("h4",{className:"font-medium",children:["Detected Entities (",e.length,")"]})]}),l&&(0,t.jsx)("div",{className:"space-y-2",children:e.map((e,s)=>{let l=r[s]||!1;return(0,t.jsxs)("div",{className:"border rounded-lg overflow-hidden",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 bg-gray-50 cursor-pointer hover:bg-gray-100",onClick:()=>{n(e=>({...e,[s]:!e[s]}))},children:[(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)("svg",{className:`w-5 h-5 mr-2 transition-transform ${l?"transform rotate-90":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})}),(0,t.jsx)("span",{className:"font-medium mr-2",children:e.entity_type}),(0,t.jsxs)("span",{className:`font-mono ${$(e.score)}`,children:["Score: ",e.score.toFixed(2)]})]}),(0,t.jsxs)("span",{className:"text-xs text-gray-500",children:["Position: ",e.start,"-",e.end]})]}),l&&(0,t.jsx)("div",{className:"p-3 border-t bg-white",children:(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4 mb-2",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Entity Type:"}),(0,t.jsx)("span",{children:e.entity_type})]}),(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Position:"}),(0,t.jsxs)("span",{children:["Characters ",e.start,"-",e.end]})]}),(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Confidence:"}),(0,t.jsx)("span",{className:$(e.score),children:e.score.toFixed(2)})]})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[e.recognition_metadata&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Recognizer:"}),(0,t.jsx)("span",{children:e.recognition_metadata.recognizer_name})]}),(0,t.jsxs)("div",{className:"flex overflow-hidden",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Identifier:"}),(0,t.jsx)("span",{className:"truncate text-xs font-mono",children:e.recognition_metadata.recognizer_identifier})]})]}),e.analysis_explanation&&(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Explanation:"}),(0,t.jsx)("span",{children:e.analysis_explanation})]})]})]})})]},s)})})]}):null},J=(e,s="slate")=>(0,t.jsx)("span",{className:`px-2 py-1 rounded-md text-xs font-medium inline-block ${{green:"bg-green-100 text-green-800",red:"bg-red-100 text-red-800",blue:"bg-blue-50 text-blue-700",slate:"bg-slate-100 text-slate-800",amber:"bg-amber-100 text-amber-800"}[s]}`,children:e}),H=e=>e?J("detected","red"):J("not detected","slate"),Y=({title:e,count:l,defaultOpen:a=!0,right:r,children:n})=>{let[i,o]=(0,s.useState)(a);return(0,t.jsxs)("div",{className:"border rounded-lg overflow-hidden",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 bg-gray-50 cursor-pointer hover:bg-gray-100",onClick:()=>o(e=>!e),children:[(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)("svg",{className:`w-5 h-5 mr-2 transition-transform ${i?"transform rotate-90":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})}),(0,t.jsxs)("h5",{className:"font-medium",children:[e," ","number"==typeof l&&(0,t.jsxs)("span",{className:"text-gray-500 font-normal",children:["(",l,")"]})]})]}),(0,t.jsx)("div",{children:r})]}),i&&(0,t.jsx)("div",{className:"p-3 border-t bg-white",children:n})]})},V=({label:e,children:s,mono:l})=>(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:e}),(0,t.jsx)("span",{className:l?"font-mono text-sm break-all":"",children:s})]}),G=()=>(0,t.jsx)("div",{className:"my-3 border-t"}),K=({response:e})=>{if(!e)return null;let s=e.outputs??e.output??[],l="GUARDRAIL_INTERVENED"===e.action?"red":"green",a=(0,t.jsxs)("div",{className:"flex flex-wrap gap-2",children:[e.guardrailCoverage?.textCharacters&&J(`text guarded ${e.guardrailCoverage.textCharacters.guarded??0}/${e.guardrailCoverage.textCharacters.total??0}`,"blue"),e.guardrailCoverage?.images&&J(`images guarded ${e.guardrailCoverage.images.guarded??0}/${e.guardrailCoverage.images.total??0}`,"blue")]}),r=e.usage&&(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:Object.entries(e.usage).map(([e,s])=>"number"==typeof s?(0,t.jsxs)("span",{className:"px-2 py-1 bg-slate-100 text-slate-800 rounded-md text-xs font-medium",children:[e,": ",s]},e):null)});return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"border rounded-lg p-4",children:[(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)(V,{label:"Action:",children:J(e.action??"N/A",l)}),e.actionReason&&(0,t.jsx)(V,{label:"Action Reason:",children:e.actionReason}),e.blockedResponse&&(0,t.jsx)(V,{label:"Blocked Response:",children:(0,t.jsx)("span",{className:"italic",children:e.blockedResponse})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)(V,{label:"Coverage:",children:a}),(0,t.jsx)(V,{label:"Usage:",children:r})]})]}),s.length>0&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(G,{}),(0,t.jsx)("h4",{className:"font-medium mb-2",children:"Outputs"}),(0,t.jsx)("div",{className:"space-y-2",children:s.map((e,s)=>(0,t.jsx)("div",{className:"p-3 bg-gray-50 rounded-md",children:(0,t.jsx)("div",{className:"text-sm whitespace-pre-wrap",children:e.text??(0,t.jsx)("em",{children:"(non-text output)"})})},s))})]})]}),e.assessments?.length?(0,t.jsx)("div",{className:"space-y-3",children:e.assessments.map((e,s)=>{let l=(0,t.jsxs)("div",{className:"flex flex-wrap gap-1",children:[e.wordPolicy&&J("word","slate"),e.contentPolicy&&J("content","slate"),e.topicPolicy&&J("topic","slate"),e.sensitiveInformationPolicy&&J("sensitive-info","slate"),e.contextualGroundingPolicy&&J("contextual-grounding","slate"),e.automatedReasoningPolicy&&J("automated-reasoning","slate")]});return(0,t.jsxs)(Y,{title:`Assessment #${s+1}`,defaultOpen:!0,right:(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[e.invocationMetrics?.guardrailProcessingLatency!=null&&J(`${e.invocationMetrics.guardrailProcessingLatency} ms`,"amber"),l]}),children:[e.wordPolicy&&(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("h6",{className:"font-medium mb-2",children:"Word Policy"}),(e.wordPolicy.customWords?.length??0)>0&&(0,t.jsx)(Y,{title:"Custom Words",defaultOpen:!0,children:(0,t.jsx)("div",{className:"space-y-2",children:e.wordPolicy.customWords.map((e,s)=>(0,t.jsxs)("div",{className:"flex justify-between items-center p-2 bg-gray-50 rounded-sm",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[J(e.action??"N/A",e.detected?"red":"slate"),(0,t.jsx)("span",{className:"font-mono text-sm break-all",children:e.match})]}),H(e.detected)]},s))})}),(e.wordPolicy.managedWordLists?.length??0)>0&&(0,t.jsx)(Y,{title:"Managed Word Lists",defaultOpen:!1,children:(0,t.jsx)("div",{className:"space-y-2",children:e.wordPolicy.managedWordLists.map((e,s)=>(0,t.jsxs)("div",{className:"flex justify-between items-center p-2 bg-gray-50 rounded-sm",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[J(e.action??"N/A",e.detected?"red":"slate"),(0,t.jsx)("span",{className:"font-mono text-sm break-all",children:e.match}),e.type&&J(e.type,"slate")]}),H(e.detected)]},s))})})]}),e.contentPolicy?.filters?.length?(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("h6",{className:"font-medium mb-2",children:"Content Policy"}),(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)("table",{className:"min-w-full text-sm",children:[(0,t.jsx)("thead",{children:(0,t.jsxs)("tr",{className:"text-left text-gray-600",children:[(0,t.jsx)("th",{className:"py-1 pr-4",children:"Type"}),(0,t.jsx)("th",{className:"py-1 pr-4",children:"Action"}),(0,t.jsx)("th",{className:"py-1 pr-4",children:"Detected"}),(0,t.jsx)("th",{className:"py-1 pr-4",children:"Strength"}),(0,t.jsx)("th",{className:"py-1 pr-4",children:"Confidence"})]})}),(0,t.jsx)("tbody",{children:e.contentPolicy.filters.map((e,s)=>(0,t.jsxs)("tr",{className:"border-t",children:[(0,t.jsx)("td",{className:"py-1 pr-4",children:e.type??"—"}),(0,t.jsx)("td",{className:"py-1 pr-4",children:J(e.action??"—",e.detected?"red":"slate")}),(0,t.jsx)("td",{className:"py-1 pr-4",children:H(e.detected)}),(0,t.jsx)("td",{className:"py-1 pr-4",children:e.filterStrength??"—"}),(0,t.jsx)("td",{className:"py-1 pr-4",children:e.confidence??"—"})]},s))})]})})]}):null,e.contextualGroundingPolicy?.filters?.length?(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("h6",{className:"font-medium mb-2",children:"Contextual Grounding"}),(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)("table",{className:"min-w-full text-sm",children:[(0,t.jsx)("thead",{children:(0,t.jsxs)("tr",{className:"text-left text-gray-600",children:[(0,t.jsx)("th",{className:"py-1 pr-4",children:"Type"}),(0,t.jsx)("th",{className:"py-1 pr-4",children:"Action"}),(0,t.jsx)("th",{className:"py-1 pr-4",children:"Detected"}),(0,t.jsx)("th",{className:"py-1 pr-4",children:"Score"}),(0,t.jsx)("th",{className:"py-1 pr-4",children:"Threshold"})]})}),(0,t.jsx)("tbody",{children:e.contextualGroundingPolicy.filters.map((e,s)=>(0,t.jsxs)("tr",{className:"border-t",children:[(0,t.jsx)("td",{className:"py-1 pr-4",children:e.type??"—"}),(0,t.jsx)("td",{className:"py-1 pr-4",children:J(e.action??"—",e.detected?"red":"slate")}),(0,t.jsx)("td",{className:"py-1 pr-4",children:H(e.detected)}),(0,t.jsx)("td",{className:"py-1 pr-4",children:e.score??"—"}),(0,t.jsx)("td",{className:"py-1 pr-4",children:e.threshold??"—"})]},s))})]})})]}):null,e.sensitiveInformationPolicy&&(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("h6",{className:"font-medium mb-2",children:"Sensitive Information"}),(e.sensitiveInformationPolicy.piiEntities?.length??0)>0&&(0,t.jsx)(Y,{title:"PII Entities",defaultOpen:!0,children:(0,t.jsx)("div",{className:"space-y-2",children:e.sensitiveInformationPolicy.piiEntities.map((e,s)=>(0,t.jsxs)("div",{className:"flex justify-between items-center p-2 bg-gray-50 rounded-sm",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[J(e.action??"N/A",e.detected?"red":"slate"),e.type&&J(e.type,"slate"),(0,t.jsx)("span",{className:"font-mono text-xs break-all",children:e.match})]}),H(e.detected)]},s))})}),(e.sensitiveInformationPolicy.regexes?.length??0)>0&&(0,t.jsx)(Y,{title:"Custom Regexes",defaultOpen:!1,children:(0,t.jsx)("div",{className:"space-y-2",children:e.sensitiveInformationPolicy.regexes.map((e,s)=>(0,t.jsxs)("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between p-2 bg-gray-50 rounded-sm gap-1",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[J(e.action??"N/A",e.detected?"red":"slate"),(0,t.jsx)("span",{className:"font-medium",children:e.name??"regex"}),(0,t.jsx)("span",{className:"font-mono text-xs break-all",children:e.regex})]}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[H(e.detected),e.match&&(0,t.jsx)("span",{className:"font-mono text-xs break-all",children:e.match})]})]},s))})})]}),e.topicPolicy?.topics?.length?(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("h6",{className:"font-medium mb-2",children:"Topic Policy"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:e.topicPolicy.topics.map((e,s)=>(0,t.jsx)("div",{className:"px-3 py-1.5 bg-gray-50 rounded-md text-xs",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[J(e.action??"N/A",e.detected?"red":"slate"),(0,t.jsx)("span",{className:"font-medium",children:e.name??"topic"}),e.type&&J(e.type,"slate"),H(e.detected)]})},s))})]}):null,e.invocationMetrics&&(0,t.jsx)(Y,{title:"Invocation Metrics",defaultOpen:!1,children:(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)(V,{label:"Latency (ms)",children:e.invocationMetrics.guardrailProcessingLatency??"—"}),(0,t.jsx)(V,{label:"Coverage:",children:(0,t.jsxs)("div",{className:"flex flex-wrap gap-2",children:[e.invocationMetrics.guardrailCoverage?.textCharacters&&J(`text ${e.invocationMetrics.guardrailCoverage.textCharacters.guarded??0}/${e.invocationMetrics.guardrailCoverage.textCharacters.total??0}`,"blue"),e.invocationMetrics.guardrailCoverage?.images&&J(`images ${e.invocationMetrics.guardrailCoverage.images.guarded??0}/${e.invocationMetrics.guardrailCoverage.images.total??0}`,"blue")]})})]}),(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsx)(V,{label:"Usage:",children:(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:e.invocationMetrics.usage&&Object.entries(e.invocationMetrics.usage).map(([e,s])=>"number"==typeof s?(0,t.jsxs)("span",{className:"px-2 py-1 bg-slate-100 text-slate-800 rounded-md text-xs font-medium",children:[e,": ",s]},e):null)})})})]})}),e.automatedReasoningPolicy?.findings?.length?(0,t.jsx)(Y,{title:"Automated Reasoning Findings",defaultOpen:!1,children:(0,t.jsx)("div",{className:"space-y-2",children:e.automatedReasoningPolicy.findings.map((e,s)=>(0,t.jsx)("pre",{className:"bg-gray-50 rounded-sm p-2 text-xs overflow-x-auto",children:JSON.stringify(e,null,2)},s))})}):null]},s)})}):null,(0,t.jsx)(Y,{title:"Raw Bedrock Guardrail Response",defaultOpen:!1,children:(0,t.jsx)("pre",{className:"bg-gray-50 rounded-sm p-3 text-xs overflow-x-auto",children:JSON.stringify(e,null,2)})})]})},U=(e,s="slate")=>(0,t.jsx)("span",{className:`px-2 py-1 rounded-md text-xs font-medium inline-block ${{green:"bg-green-100 text-green-800",red:"bg-red-100 text-red-800",blue:"bg-blue-50 text-blue-700",slate:"bg-slate-100 text-slate-800",amber:"bg-amber-100 text-amber-800"}[s]}`,children:e}),Q=({title:e,count:l,defaultOpen:a=!0,children:r})=>{let[n,i]=(0,s.useState)(a);return(0,t.jsxs)("div",{className:"border rounded-lg overflow-hidden",children:[(0,t.jsx)("div",{className:"flex items-center justify-between p-3 bg-gray-50 cursor-pointer hover:bg-gray-100",onClick:()=>i(e=>!e),children:(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)("svg",{className:`w-5 h-5 mr-2 transition-transform ${n?"transform rotate-90":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})}),(0,t.jsxs)("h5",{className:"font-medium",children:[e," ","number"==typeof l&&(0,t.jsxs)("span",{className:"text-gray-500 font-normal",children:["(",l,")"]})]})]})}),n&&(0,t.jsx)("div",{className:"p-3 border-t bg-white",children:r})]})},X=({label:e,children:s,mono:l})=>(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:e}),(0,t.jsx)("span",{className:l?"font-mono text-sm break-all":"",children:s})]}),Z=({response:e})=>{if(!e||"string"==typeof e)return"string"==typeof e&&e?(0,t.jsx)("div",{className:"bg-white rounded-lg border border-red-200 p-4",children:(0,t.jsxs)("div",{className:"text-red-800",children:[(0,t.jsx)("h5",{className:"font-medium mb-2",children:"Error"}),(0,t.jsx)("p",{className:"text-sm",children:e})]})}):null;let s=Array.isArray(e)?e:[];if(0===s.length)return(0,t.jsx)("div",{className:"bg-white rounded-lg border border-gray-200 p-4",children:(0,t.jsx)("div",{className:"text-gray-600 text-sm",children:"No detections found"})});let l=s.filter(e=>"pattern"===e.type),a=s.filter(e=>"blocked_word"===e.type),r=s.filter(e=>"category_keyword"===e.type),n=s.filter(e=>"BLOCK"===e.action).length,i=s.filter(e=>"MASK"===e.action).length,o=s.length;return(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsx)("div",{className:"bg-white rounded-lg border border-gray-200 p-4",children:(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)(X,{label:"Total Detections:",children:(0,t.jsx)("span",{className:"font-semibold",children:o})}),(0,t.jsx)(X,{label:"Actions:",children:(0,t.jsxs)("div",{className:"flex flex-wrap gap-2",children:[n>0&&U(`${n} blocked`,"red"),i>0&&U(`${i} masked`,"blue"),0===n&&0===i&&U("passed","green")]})})]}),(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsx)(X,{label:"By Type:",children:(0,t.jsxs)("div",{className:"flex flex-wrap gap-2",children:[l.length>0&&U(`${l.length} patterns`,"slate"),a.length>0&&U(`${a.length} keywords`,"slate"),r.length>0&&U(`${r.length} categories`,"slate")]})})})]})}),l.length>0&&(0,t.jsx)(Q,{title:"Patterns Matched",count:l.length,defaultOpen:!0,children:(0,t.jsx)("div",{className:"space-y-2",children:l.map((e,s)=>(0,t.jsx)("div",{className:"p-3 bg-gray-50 rounded-md",children:(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsx)("div",{className:"space-y-1",children:(0,t.jsx)(X,{label:"Pattern:",children:e.pattern_name||"unknown"})}),(0,t.jsx)("div",{className:"space-y-1",children:(0,t.jsx)(X,{label:"Action:",children:U(e.action,"BLOCK"===e.action?"red":"blue")})})]})},s))})}),a.length>0&&(0,t.jsx)(Q,{title:"Blocked Words Detected",count:a.length,defaultOpen:!0,children:(0,t.jsx)("div",{className:"space-y-2",children:a.map((e,s)=>(0,t.jsx)("div",{className:"p-3 bg-gray-50 rounded-md",children:(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-1",children:[(0,t.jsx)(X,{label:"Keyword:",mono:!0,children:e.keyword||"unknown"}),e.description&&(0,t.jsx)(X,{label:"Description:",children:e.description})]}),(0,t.jsx)("div",{className:"space-y-1",children:(0,t.jsx)(X,{label:"Action:",children:U(e.action,"BLOCK"===e.action?"red":"blue")})})]})},s))})}),r.length>0&&(0,t.jsx)(Q,{title:"Category Keywords Detected",count:r.length,defaultOpen:!0,children:(0,t.jsx)("div",{className:"space-y-2",children:r.map((e,s)=>(0,t.jsx)("div",{className:"p-3 bg-gray-50 rounded-md",children:(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-1",children:[(0,t.jsx)(X,{label:"Category:",children:e.category||"unknown"}),(0,t.jsx)(X,{label:"Keyword:",mono:!0,children:e.keyword||"unknown"}),e.severity&&(0,t.jsx)(X,{label:"Severity:",children:U(e.severity,"high"===e.severity?"red":"medium"===e.severity?"amber":"slate")})]}),(0,t.jsx)("div",{className:"space-y-1",children:(0,t.jsx)(X,{label:"Action:",children:U(e.action,"BLOCK"===e.action?"red":"blue")})})]})},s))})}),(0,t.jsx)(Q,{title:"Raw Detection Data",defaultOpen:!1,children:(0,t.jsx)("pre",{className:"bg-gray-50 rounded-sm p-3 text-xs overflow-x-auto",children:JSON.stringify(s,null,2)})})]})};var ee=e.i(602869);let et=()=>(0,t.jsxs)("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:[(0,t.jsx)("circle",{cx:"8",cy:"8",r:"7",stroke:"#16A34A",strokeWidth:"1.5",fill:"#F0FDF4"}),(0,t.jsx)("path",{d:"M5 8l2 2 4-4",stroke:"#16A34A",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})]}),es=()=>(0,t.jsxs)("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:[(0,t.jsx)("circle",{cx:"8",cy:"8",r:"7",stroke:"#DC2626",strokeWidth:"1.5",fill:"#FEF2F2"}),(0,t.jsx)("path",{d:"M6 6l4 4M10 6l-4 4",stroke:"#DC2626",strokeWidth:"1.5",strokeLinecap:"round"})]}),el=()=>(0,t.jsxs)("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",className:"animate-spin",children:[(0,t.jsx)("circle",{cx:"8",cy:"8",r:"6",stroke:"#D1D5DB",strokeWidth:"2"}),(0,t.jsx)("path",{d:"M8 2a6 6 0 0 1 6 6",stroke:"#6366F1",strokeWidth:"2",strokeLinecap:"round"})]}),ea=({title:e,data:l,loading:a,error:r})=>{let[n,i]=(0,s.useState)(!1);return(0,t.jsxs)("div",{className:"border border-gray-200 rounded-lg bg-white",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between px-4 py-3 cursor-pointer hover:bg-gray-50 transition-colors",onClick:()=>i(!n),children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[a?(0,t.jsx)(el,{}):r?(0,t.jsx)(g.Tooltip,{title:r,children:(0,t.jsx)("span",{className:"text-gray-400 text-sm",children:"--"})}):l?.compliant?(0,t.jsx)(et,{}):(0,t.jsx)(es,{}),(0,t.jsx)("span",{className:"font-medium text-sm text-gray-900",children:e})]}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[!a&&!r&&l&&(0,t.jsx)("span",{className:`px-2 py-0.5 rounded text-[11px] font-semibold uppercase ${l.compliant?"bg-green-100 text-green-700 border border-green-200":"bg-red-100 text-red-700 border border-red-200"}`,children:l.compliant?"COMPLIANT":"NON-COMPLIANT"}),r&&(0,t.jsx)("span",{className:"px-2 py-0.5 rounded-sm text-[11px] font-medium bg-gray-100 text-gray-500 border border-gray-200",children:"UNAVAILABLE"}),(0,t.jsx)("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",className:`transition-transform ${n?"rotate-180":""}`,children:(0,t.jsx)("path",{d:"M6 8l4 4 4-4",stroke:"#6B7280",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})]})]}),n&&(0,t.jsxs)("div",{className:"border-t border-gray-100 px-4 py-3",children:[a&&(0,t.jsx)("p",{className:"text-sm text-gray-500",children:"Checking compliance..."}),r&&(0,t.jsx)("p",{className:"text-sm text-red-600",children:r}),l&&(0,t.jsx)("div",{className:"space-y-2",children:l.checks.map((e,s)=>(0,t.jsxs)("div",{className:"flex items-start gap-2",children:[(0,t.jsx)("div",{className:"shrink-0 mt-0.5",children:e.passed?(0,t.jsx)(et,{}):(0,t.jsx)(es,{})}),(0,t.jsxs)("div",{className:"min-w-0",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-900",children:e.check_name}),(0,t.jsx)("span",{className:"text-[10px] font-mono text-gray-400",children:e.article})]}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5",children:e.detail})]})]},s))})]})]})},er=({accessToken:e,logEntry:l})=>{let[a,r]=(0,s.useState)(null),[n,i]=(0,s.useState)(null),[o,d]=(0,s.useState)(!1),[c,m]=(0,s.useState)(!1),[x,u]=(0,s.useState)(null),[p,h]=(0,s.useState)(null);return(0,s.useEffect)(()=>{if(!e||!l.request_id)return;let t={request_id:l.request_id,user_id:l.user,model:l.model,timestamp:l.startTime,guardrail_information:l.metadata?.guardrail_information};d(!0),u(null),(0,ee.checkEuAiActCompliance)(e,t).then(r).catch(e=>u(e.message||"Failed to check EU AI Act compliance")).finally(()=>d(!1)),m(!0),h(null),(0,ee.checkGdprCompliance)(e,t).then(i).catch(e=>h(e.message||"Failed to check GDPR compliance")).finally(()=>m(!1))},[e,l]),(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-4",children:"Regulatory Compliance"}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsx)(ea,{title:"EU AI Act",data:a,loading:o,error:x}),(0,t.jsx)(ea,{title:"GDPR",data:n,loading:c,error:p})]})]})},en=new Set(["presidio","bedrock","litellm_content_filter"]),ei=(e,t)=>{if(null==e)return!1;if("string"==typeof e)return e===t;if(Array.isArray(e))return e.includes(t);if("object"==typeof e&&"default"in e){let s=e.default;if("string"==typeof s)return s===t;if(Array.isArray(s))return s.some(e=>"string"==typeof e&&e===t)}return!1},eo=e=>Object.values(e.masked_entity_count||{}).reduce((e,t)=>e+("number"==typeof t?t:0),0),ed=e=>"success"===(e.guardrail_status??"").toLowerCase(),ec=e=>e.policy_template||e.guardrail_name,em=()=>(0,t.jsxs)("svg",{width:"40",height:"40",viewBox:"0 0 40 40",fill:"none",children:[(0,t.jsx)("circle",{cx:"20",cy:"20",r:"20",fill:"#EEF2FF"}),(0,t.jsx)("path",{d:"M20 10l8 4v6c0 5.25-3.4 10.15-8 11.5C15.4 30.15 12 25.25 12 20v-6l8-4z",stroke:"#6366F1",strokeWidth:"1.5",fill:"none"}),(0,t.jsx)("path",{d:"M16 20l3 3 5-6",stroke:"#6366F1",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",fill:"none"})]}),ex=({className:e})=>(0,t.jsxs)("svg",{width:"22",height:"22",viewBox:"0 0 22 22",fill:"none",className:e,children:[(0,t.jsx)("circle",{cx:"11",cy:"11",r:"10",stroke:"#16A34A",strokeWidth:"1.5",fill:"#F0FDF4"}),(0,t.jsx)("path",{d:"M7 11l3 3 5-6",stroke:"#16A34A",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})]}),eu=({className:e})=>(0,t.jsxs)("svg",{width:"22",height:"22",viewBox:"0 0 22 22",fill:"none",className:e,children:[(0,t.jsx)("circle",{cx:"11",cy:"11",r:"10",stroke:"#DC2626",strokeWidth:"1.5",fill:"#FEF2F2"}),(0,t.jsx)("path",{d:"M8 8l6 6M14 8l-6 6",stroke:"#DC2626",strokeWidth:"1.5",strokeLinecap:"round"})]}),ep=()=>(0,t.jsxs)("svg",{width:"22",height:"22",viewBox:"0 0 22 22",fill:"none",children:[(0,t.jsx)("circle",{cx:"11",cy:"11",r:"10",stroke:"#3B82F6",strokeWidth:"1.5",fill:"#EFF6FF"}),(0,t.jsx)("path",{d:"M9 7.5l6 3.5-6 3.5V7.5z",fill:"#3B82F6"})]}),eh=()=>(0,t.jsx)("svg",{width:"22",height:"22",viewBox:"0 0 22 22",fill:"none",children:(0,t.jsx)("circle",{cx:"11",cy:"11",r:"5",fill:"#9CA3AF"})}),eg=({expanded:e})=>(0,t.jsx)("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",className:`transition-transform ${e?"rotate-180":""}`,children:(0,t.jsx)("path",{d:"M6 8l4 4 4-4",stroke:"#6B7280",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})}),ef=()=>(0,t.jsx)("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:(0,t.jsx)("path",{d:"M8 2v8m0 0l-3-3m3 3l3-3M3 12h10",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})}),ey=({matchDetails:e})=>e&&0!==e.length?(0,t.jsxs)("div",{className:"mt-3",children:[(0,t.jsxs)("h5",{className:"text-sm font-medium mb-2 text-gray-700",children:["Match Details (",e.length,")"]}),(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)("table",{className:"w-full text-sm",children:[(0,t.jsx)("thead",{children:(0,t.jsxs)("tr",{className:"border-b text-left text-gray-500",children:[(0,t.jsx)("th",{className:"pb-2 pr-4 font-medium",children:"Type"}),(0,t.jsx)("th",{className:"pb-2 pr-4 font-medium",children:"Method"}),(0,t.jsx)("th",{className:"pb-2 pr-4 font-medium",children:"Action"}),(0,t.jsx)("th",{className:"pb-2 font-medium",children:"Detail"})]})}),(0,t.jsx)("tbody",{children:e.map((e,s)=>(0,t.jsxs)("tr",{className:"border-b border-gray-100",children:[(0,t.jsx)("td",{className:"py-2 pr-4",children:e.type}),(0,t.jsx)("td",{className:"py-2 pr-4",children:(0,t.jsx)("span",{className:"px-2 py-0.5 bg-slate-100 text-slate-700 rounded-sm text-xs",children:e.detection_method??"-"})}),(0,t.jsx)("td",{className:"py-2 pr-4",children:(0,t.jsx)("span",{className:`px-2 py-0.5 rounded text-xs font-medium ${"BLOCK"===e.action_taken?"bg-red-100 text-red-800":"bg-blue-50 text-blue-700"}`,children:e.action_taken??"-"})}),(0,t.jsxs)("td",{className:"py-2 font-mono text-xs text-gray-600 break-all",children:[e.category?`[${e.category}] `:"",e.snippet??"-"]})]},s))})]})})]}):null,ej=({response:e})=>{let[l,a]=(0,s.useState)(!1);return(0,t.jsx)("div",{className:"mt-3",children:(0,t.jsxs)("div",{className:"border rounded-lg overflow-hidden",children:[(0,t.jsx)("div",{className:"flex items-center justify-between p-3 bg-gray-50 cursor-pointer hover:bg-gray-100",onClick:()=>a(!l),children:(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)(eg,{expanded:l}),(0,t.jsx)("h5",{className:"font-medium text-sm ml-1",children:"Raw Guardrail Response"})]})}),l&&(0,t.jsx)("div",{className:"p-3 border-t bg-white",children:(0,t.jsx)("pre",{className:"bg-gray-50 rounded-sm p-3 text-xs overflow-x-auto",children:JSON.stringify(e,null,2)})})]})})},ev=({entries:e})=>{let l=(0,s.useMemo)(()=>[...e].sort((e,t)=>(e.start_time??0)-(t.start_time??0)),[e]),a=(0,s.useMemo)(()=>{if(0===l.length)return[];let e=l[0].start_time,t=[];t.push({type:"request",label:"Request received",offsetMs:0});let s=l.filter(e=>ei(e.guardrail_mode,"pre_call")),a=l.filter(e=>ei(e.guardrail_mode,"post_call")||ei(e.guardrail_mode,"logging_only")),r=l.filter(e=>ei(e.guardrail_mode,"during_call"));for(let l of s){let s=Math.round((l.end_time-e)*1e3);t.push({type:"guardrail",label:`Pre-call guardrail: ${ec(l)}`,offsetMs:s,status:ed(l)?"PASSED":"FAILED",isSuccess:ed(l)})}let n=s.length>0?Math.max(...s.map(e=>e.end_time)):e,i=Math.round((((a.length>0?Math.min(...a.map(e=>e.start_time)):void 0)??n+1)-e)*1e3);for(let s of(t.push({type:"llm",label:"LLM call",offsetMs:i}),r)){let l=Math.round((s.end_time-e)*1e3);t.push({type:"guardrail",label:`During-call guardrail: ${ec(s)}`,offsetMs:l,status:ed(s)?"PASSED":"FAILED",isSuccess:ed(s)})}for(let s of a){let l=Math.round((s.end_time-e)*1e3);t.push({type:"guardrail",label:`Post-call guardrail: ${ec(s)}`,offsetMs:l,status:ed(s)?"PASSED":"FAILED",isSuccess:ed(s)})}let o=Math.round((Math.max(...l.map(e=>e.end_time))-e)*1e3)+1;return t.push({type:"response",label:"Response returned",offsetMs:o}),t},[l]);return(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-4",children:"Request Lifecycle"}),(0,t.jsx)("div",{className:"relative",children:a.map((e,s)=>(0,t.jsxs)("div",{className:"flex items-start gap-3 relative",children:[(0,t.jsxs)("div",{className:"flex flex-col items-center",children:[(0,t.jsx)("div",{className:"shrink-0",children:"request"===e.type||"response"===e.type?(0,t.jsx)(eh,{}):"llm"===e.type?(0,t.jsx)(ep,{}):e.isSuccess?(0,t.jsx)(ex,{}):(0,t.jsx)(eu,{})}),s{let l,a,[r,n]=(0,s.useState)(!1),i=ed(e),o=eo(e),d=ec(e),c=(l=Math.round(1e3*e.duration),`${l}ms`),m=null==(a=(e=>{if(null==e)return null;if("string"==typeof e)return e;if(Array.isArray(e)){let t=e[0];return"string"==typeof t?t:null}if("object"==typeof e&&"default"in e){let t=e.default;if("string"==typeof t)return t;if(Array.isArray(t)){let e=t[0];return"string"==typeof e?e:null}}return null})(e.guardrail_mode))||""===a?"—":a.replace(/_/g,"-").toUpperCase(),x=(e=>{if(!ed(e))return null;if(null!=e.risk_score)return e.risk_score;let t=eo(e),s=e.patterns_checked??0,l=e.confidence_score??0;if(0===s&&0===l)return 0;let a=7*(s>0?t/s:0)+3*l;return t>0&&a<2&&(a=2),Math.min(10,Math.round(10*a)/10)})(e),u=e.guardrail_provider??"presidio",p=e.guardrail_response,h=Array.isArray(p)?p:[],f="bedrock"!==u||null===p||"object"!=typeof p||Array.isArray(p)?void 0:p,y=null!=e.patterns_checked?`${o}/${e.patterns_checked} matched`:o>0?`${o} matched`:null;return(0,t.jsxs)("div",{className:"border border-gray-200 rounded-lg bg-white",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3 px-4 py-3 cursor-pointer hover:bg-gray-50 transition-colors",onClick:()=>n(!r),children:[(0,t.jsx)("div",{className:"shrink-0",children:i?(0,t.jsx)(ex,{}):(0,t.jsx)(eu,{})}),(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-wrap flex-1 min-w-0",children:[(0,t.jsx)("span",{className:"font-semibold text-gray-900 text-sm truncate",children:d}),(0,t.jsx)("span",{className:"px-2 py-0.5 border border-blue-200 bg-blue-50 text-blue-700 rounded-sm text-[11px] font-semibold uppercase shrink-0",children:m}),(0,t.jsx)("span",{className:`px-2 py-0.5 rounded text-[11px] font-semibold uppercase shrink-0 ${i?"bg-green-100 text-green-700 border border-green-200":"bg-red-100 text-red-700 border border-red-200"}`,children:i?"PASSED":"FAILED"}),y&&(0,t.jsx)("span",{className:`px-2 py-0.5 rounded text-[11px] font-medium shrink-0 ${0===o?"bg-green-50 text-green-700 border border-green-200":"bg-amber-50 text-amber-700 border border-amber-200"}`,children:y}),null!=e.confidence_score&&(0,t.jsxs)("span",{className:"px-2 py-0.5 bg-gray-100 text-gray-600 border border-gray-200 rounded-sm text-[11px] font-medium shrink-0",children:[(100*e.confidence_score).toFixed(0),"% conf"]}),null!=x&&i&&(0,t.jsx)(g.Tooltip,{title:`Risk score: ${x}/10`,children:(0,t.jsxs)("span",{className:`px-2 py-0.5 border rounded-sm text-[11px] font-semibold shrink-0 ${x<=3?"text-green-600 bg-green-50 border-green-200":x<=6?"text-amber-600 bg-amber-50 border-amber-200":"text-red-600 bg-red-50 border-red-200"}`,children:["Risk ",x,"/10"]})})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3 shrink-0",children:[(0,t.jsx)("span",{className:"text-sm text-gray-500 font-mono",children:c}),e.detection_method&&(0,t.jsx)("span",{className:"px-2 py-0.5 bg-gray-100 text-gray-600 border border-gray-200 rounded-sm text-[11px] font-medium",children:e.detection_method.split(",")[0].trim()}),(0,t.jsx)(eg,{expanded:r})]})]}),r&&(0,t.jsxs)("div",{className:"border-t border-gray-100 px-4 py-3",children:[e.classification&&(0,t.jsxs)("div",{className:"mb-3 bg-gray-50 rounded-lg p-3 space-y-1",children:[(0,t.jsx)("h5",{className:"text-sm font-medium text-gray-700 mb-2",children:"Classification"}),e.classification.category&&(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"font-medium w-1/3 text-gray-500",children:"Category:"}),(0,t.jsx)("span",{children:e.classification.category})]}),e.classification.article_reference&&(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"font-medium w-1/3 text-gray-500",children:"Reference:"}),(0,t.jsx)("span",{className:"font-mono",children:e.classification.article_reference})]}),null!=e.classification.confidence&&(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"font-medium w-1/3 text-gray-500",children:"Confidence:"}),(0,t.jsxs)("span",{children:[(100*e.classification.confidence).toFixed(0),"%"]})]}),e.classification.reason&&(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"font-medium w-1/3 text-gray-500",children:"Reason:"}),(0,t.jsx)("span",{children:e.classification.reason})]})]}),e.match_details&&e.match_details.length>0&&(0,t.jsx)(ey,{matchDetails:e.match_details}),o>0&&(0,t.jsxs)("div",{className:"mt-3",children:[(0,t.jsx)("h5",{className:"text-sm font-medium text-gray-700 mb-2",children:"Masked Entities"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:Object.entries(e.masked_entity_count||{}).map(([e,s])=>(0,t.jsxs)("span",{className:"px-2 py-1 bg-blue-50 text-blue-700 rounded-sm text-xs font-medium",children:[e,": ",s]},e))})]}),"presidio"===u&&h.length>0&&(0,t.jsx)("div",{className:"mt-3",children:(0,t.jsx)(W,{entities:h})}),"bedrock"===u&&f&&(0,t.jsx)("div",{className:"mt-3",children:(0,t.jsx)(K,{response:f})}),"litellm_content_filter"===u&&p&&(0,t.jsx)("div",{className:"mt-3",children:(0,t.jsx)(Z,{response:p})}),u&&!en.has(u)&&p&&(0,t.jsx)(ej,{response:p})]})]})},eN=({data:e,accessToken:l,logEntry:a})=>{let r=(0,s.useMemo)(()=>Array.isArray(e)?e.filter(e=>!!e):e?[e]:[],[e]),n=r.filter(ed).length,i=n===r.length,o=(0,s.useMemo)(()=>Math.round(1e3*r.reduce((e,t)=>e+(t.duration??0),0)),[r]);return((0,s.useMemo)(()=>Array.from(new Set(r.map(e=>e.policy_template).filter(Boolean))),[r]),0===r.length)?null:(0,t.jsxs)("div",{className:"bg-white rounded-xl border border-gray-200 shadow-xs w-full max-w-full overflow-hidden mb-6",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between px-6 py-4 border-b border-gray-100",children:[(0,t.jsxs)("div",{className:"flex items-center gap-4",children:[(0,t.jsx)(em,{}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h3",{className:"text-lg font-semibold text-gray-900",children:"Guardrails & Policy Compliance"}),(0,t.jsxs)("div",{className:"flex items-center gap-2 mt-0.5",children:[(0,t.jsxs)("span",{className:"text-sm text-gray-500",children:[r.length," guardrail",1!==r.length?"s":""," evaluated"]}),(0,t.jsx)("span",{className:"text-gray-300",children:"|"}),(0,t.jsxs)("span",{className:`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold ${i?"bg-green-50 text-green-700 border border-green-200":"bg-red-50 text-red-700 border border-red-200"}`,children:[i?(0,t.jsx)("svg",{width:"12",height:"12",viewBox:"0 0 12 12",fill:"none",children:(0,t.jsx)("path",{d:"M3 6l2.5 2.5L9 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})}):null,n," Passed"]})]})]})]}),(0,t.jsxs)("div",{className:"flex items-center gap-6",children:[(0,t.jsx)("div",{className:"text-right",children:(0,t.jsxs)("div",{className:"text-sm font-medium text-gray-900",children:["Total: ",o,"ms overhead"]})}),(0,t.jsxs)("button",{onClick:()=>{let e=new Blob([JSON.stringify(r,null,2)],{type:"application/json"}),t=URL.createObjectURL(e),s=document.createElement("a");s.href=t,s.download=`guardrail-compliance-log-${new Date().toISOString().slice(0,10)}.json`,s.click(),URL.revokeObjectURL(t)},className:"inline-flex items-center gap-2 px-4 py-2 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 transition-colors",children:[(0,t.jsx)(ef,{}),"Export Compliance Log"]})]})]}),l&&a&&(0,t.jsx)("div",{className:"px-6 py-4 border-b border-gray-100",children:(0,t.jsx)(er,{accessToken:l,logEntry:a})}),(0,t.jsxs)("div",{className:"flex flex-col",children:[(0,t.jsx)("div",{className:"border-b border-gray-100 px-6 py-5",children:(0,t.jsx)(ev,{entries:r})}),(0,t.jsxs)("div",{className:"px-6 py-5",children:[(0,t.jsx)("h4",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider mb-4",children:"Evaluation Details"}),(0,t.jsx)("div",{className:"space-y-3",children:r.map((e,s)=>(0,t.jsx)(eb,{entry:e},`${e.guardrail_name??"guardrail"}-${s}`))})]})]})]})};var e_=e.i(291542),ew=e.i(245704),ek=e.i(518617),eS=e.i(19732);let{Text:eC}=f.Typography;function eT({data:e}){let s=Array.isArray(e)?e:[e];return s.length?(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:12},children:[(0,t.jsx)(eS.ExperimentOutlined,{style:{fontSize:16,color:"#6366f1"}}),(0,t.jsx)(eC,{strong:!0,style:{fontSize:15},children:"LLM Judge Results"})]}),s.map((e,s)=>(0,t.jsx)(eL,{entry:e},e.eval_id||s))]}):null}function eL({entry:e}){let s=e.passed,l=s?"#52c41a":"#ff4d4f",a=(e.verdicts||[]).filter(e=>"overall"!==(e.criterion_name||"").toLowerCase()),r=[{title:"Criterion",dataIndex:"criterion_name",key:"criterion_name",width:160,render:e=>(0,t.jsx)(eC,{strong:!0,style:{whiteSpace:"nowrap"},children:e})},{title:"Weight",dataIndex:"weight",key:"weight",width:65,render:e=>null!=e?(0,t.jsxs)(eC,{type:"secondary",style:{fontSize:12},children:[e,"%"]}):null},{title:"Score",dataIndex:"score",key:"score",width:65,render:e=>(0,t.jsx)(eC,{style:{color:e>=70?"#52c41a":e>=50?"#faad14":"#ff4d4f",fontWeight:600},children:e})},{title:(0,t.jsx)(g.Tooltip,{title:"Score × Weight — how much each criterion contributes to the final score",children:(0,t.jsx)("span",{style:{borderBottom:"1px dashed #aaa",cursor:"help"},children:"Weighted"})}),key:"weighted",width:75,render:(e,s)=>{if(null==s.weight)return null;let l=s.score*s.weight/100;return(0,t.jsx)(eC,{type:"secondary",style:{fontSize:12},children:l%1==0?l:l.toFixed(1)})}},{title:"Comment",dataIndex:"reasoning",key:"reasoning",ellipsis:{showTitle:!1},render:e=>(0,t.jsx)(g.Tooltip,{title:e,children:(0,t.jsx)("span",{style:{fontSize:12},children:e})})}];return(0,t.jsxs)(D.Card,{size:"small",className:"mb-3",style:{borderLeft:`3px solid ${l}`},title:(0,t.jsxs)(p.Space,{children:[s?(0,t.jsx)(ew.CheckCircleOutlined,{style:{color:"#52c41a"}}):(0,t.jsx)(ek.CloseCircleOutlined,{style:{color:"#ff4d4f"}}),(0,t.jsx)(eC,{strong:!0,children:e.eval_name}),(0,t.jsx)(h.Tag,{color:s?"success":"error",children:s?"PASSED":"FAILED"}),(0,t.jsx)(g.Tooltip,{title:"Weighted average of all criterion scores. Each criterion has a weight (%) set when the eval was created — higher-weight criteria count more toward the final score.",children:(0,t.jsxs)(eC,{type:"secondary",style:{fontSize:12,cursor:"help",borderBottom:"1px dashed #aaa"},children:[e.overall_score?.toFixed(0)," / 100",null!=e.threshold&&` (threshold: ${e.threshold})`]})})]}),extra:(0,t.jsxs)(p.Space,{size:"small",children:[e.judge_model&&(0,t.jsxs)(eC,{type:"secondary",style:{fontSize:12},children:["Judge: ",e.judge_model]}),null!=e.iteration&&(0,t.jsxs)(eC,{type:"secondary",style:{fontSize:12},children:["Iter: ",e.iteration+1]})]}),children:[e.eval_error&&(0,t.jsxs)(eC,{type:"warning",style:{display:"block",marginBottom:8,fontSize:12},children:["Judge error: ",e.eval_error]}),a.length>0?(0,t.jsx)(e_.Table,{dataSource:a,columns:r,pagination:!1,size:"small",rowKey:"criterion_name",scroll:{x:!0},summary:()=>{if(!a.some(e=>null!=e.weight))return null;let e=a.reduce((e,t)=>e+(null!=t.weight?t.score*t.weight/100:0),0);return(0,t.jsxs)(e_.Table.Summary.Row,{children:[(0,t.jsx)(e_.Table.Summary.Cell,{index:0,children:(0,t.jsx)(eC,{strong:!0,style:{fontSize:12},children:"Total"})}),(0,t.jsx)(e_.Table.Summary.Cell,{index:1}),(0,t.jsx)(e_.Table.Summary.Cell,{index:2}),(0,t.jsx)(e_.Table.Summary.Cell,{index:3,children:(0,t.jsx)(eC,{strong:!0,style:{fontSize:12,color:l},children:e%1==0?e:e.toFixed(1)})}),(0,t.jsx)(e_.Table.Summary.Cell,{index:4})]})}}):(0,t.jsxs)(eC,{type:"secondary",style:{fontSize:12},children:["Score: ",e.overall_score?.toFixed(1)," — no per-criterion breakdown available."]})]})}let eA=e=>null==e?"-":`$${(0,q.formatNumberWithCommas)(e,8)}`,eE=e=>null==e?"-":`${(100*e).toFixed(2)}%`,eM=({costBreakdown:e,totalSpend:s,promptTokens:l,completionTokens:a,cacheHit:r,rawInputTokens:n,cacheReadTokens:i,cacheCreationTokens:o})=>{let d=r?.toLowerCase()==="true",c=void 0!==l||void 0!==a,m=e?.input_cost!==void 0||e?.output_cost!==void 0,x=e?.additional_costs&&Object.entries(e.additional_costs).some(([,e])=>null!=e&&0!==e);if(!(m||c||x||e&&(void 0!==e.discount_percent&&0!==e.discount_percent||void 0!==e.discount_amount&&0!==e.discount_amount||void 0!==e.margin_percent&&0!==e.margin_percent||void 0!==e.margin_fixed_amount&&0!==e.margin_fixed_amount||void 0!==e.margin_total_amount&&0!==e.margin_total_amount)))return null;let u=e&&(void 0!==e.discount_percent&&0!==e.discount_percent||void 0!==e.discount_amount&&0!==e.discount_amount),p=e&&(void 0!==e.margin_percent&&0!==e.margin_percent||void 0!==e.margin_fixed_amount&&0!==e.margin_fixed_amount||void 0!==e.margin_total_amount&&0!==e.margin_total_amount),h=d?0:e?.input_cost,g=d?0:e?.output_cost,f=d?0:e?.original_cost,y=d?0:e?.total_cost??s;return(0,t.jsx)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden mb-6",children:(0,t.jsx)(R.Collapse,{expandIconPosition:"start",items:[{key:"1",label:(0,t.jsxs)("div",{className:"flex items-center justify-between w-full",children:[(0,t.jsx)("h3",{className:"text-lg font-medium text-gray-900",children:"Cost Breakdown"}),(0,t.jsxs)("div",{className:"flex items-center space-x-2 mr-4",children:[(0,t.jsx)("span",{className:"text-sm text-gray-500",children:"Total:"}),(0,t.jsxs)("span",{className:"text-sm font-semibold text-gray-900",children:[eA(s),d&&" (Cached)"]})]})]}),children:(0,t.jsxs)("div",{className:"p-6 space-y-4",children:[(0,t.jsxs)("div",{className:"space-y-2 max-w-2xl",children:[(()=>{if(e?.cache_read_cost!==void 0||e?.cache_creation_cost!==void 0){let s=d?0:(h??0)-(e?.cache_read_cost??0)-(e?.cache_creation_cost??0);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"text-gray-600 font-medium w-1/3",children:"Input Cost:"}),(0,t.jsxs)("span",{className:"text-gray-900",children:[eA(s),null!=n&&(0,t.jsxs)("span",{className:"text-gray-500 font-normal ml-1",children:["(",n.toLocaleString()," tokens)"]})]})]}),(e?.cache_read_cost??0)>0&&(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"text-gray-600 font-medium w-1/3",children:"Cache Read Cost:"}),(0,t.jsxs)("span",{className:"text-gray-900",children:[eA(d?0:e?.cache_read_cost),(i??0)>0&&(0,t.jsxs)("span",{className:"text-gray-500 font-normal ml-1",children:["(",(i??0).toLocaleString()," tokens)"]})]})]}),(e?.cache_creation_cost??0)>0&&(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"text-gray-600 font-medium w-1/3",children:"Cache Write Cost:"}),(0,t.jsxs)("span",{className:"text-gray-900",children:[eA(d?0:e?.cache_creation_cost),(o??0)>0&&(0,t.jsxs)("span",{className:"text-gray-500 font-normal ml-1",children:["(",(o??0).toLocaleString()," tokens)"]})]})]})]})}return(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"text-gray-600 font-medium w-1/3",children:"Input Cost:"}),(0,t.jsxs)("span",{className:"text-gray-900",children:[eA(h),void 0!==l&&(0,t.jsxs)("span",{className:"text-gray-500 font-normal ml-1",children:["(",l.toLocaleString()," prompt tokens)"]})]})]})})(),(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"text-gray-600 font-medium w-1/3",children:"Output Cost:"}),(0,t.jsxs)("span",{className:"text-gray-900",children:[eA(g),void 0!==a&&(0,t.jsxs)("span",{className:"text-gray-500 font-normal ml-1",children:["(",a.toLocaleString()," completion tokens)"]})]})]}),e?.tool_usage_cost!==void 0&&e.tool_usage_cost>0&&(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsx)("span",{className:"text-gray-600 font-medium w-1/3",children:"Tool Usage Cost:"}),(0,t.jsx)("span",{className:"text-gray-900",children:eA(e.tool_usage_cost)})]}),e?.additional_costs&&Object.entries(e.additional_costs).filter(([,e])=>null!=e&&0!==e).map(([e,s])=>(0,t.jsxs)("div",{className:"flex text-sm",children:[(0,t.jsxs)("span",{className:"text-gray-600 font-medium w-1/3",children:[e,":"]}),(0,t.jsx)("span",{className:"text-gray-900",children:eA(s)})]},e))]}),!d&&(0,t.jsx)("div",{className:"pt-2 border-t border-gray-100 max-w-2xl",children:(0,t.jsxs)("div",{className:"flex text-sm font-semibold",children:[(0,t.jsx)("span",{className:"text-gray-900 w-1/3",children:"Original LLM Cost:"}),(0,t.jsx)("span",{className:"text-gray-900",children:eA(f)})]})}),(u||p)&&(0,t.jsxs)("div",{className:"pt-2 space-y-2 max-w-2xl",children:[u&&(0,t.jsxs)("div",{className:"space-y-2",children:[void 0!==e.discount_percent&&0!==e.discount_percent&&(0,t.jsxs)("div",{className:"flex text-sm text-gray-600",children:[(0,t.jsxs)("span",{className:"font-medium w-1/3",children:["Discount (",eE(e.discount_percent),"):"]}),(0,t.jsxs)("span",{className:"text-gray-900",children:["-",eA(e.discount_amount)]})]}),void 0!==e.discount_amount&&void 0===e.discount_percent&&(0,t.jsxs)("div",{className:"flex text-sm text-gray-600",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Discount Amount:"}),(0,t.jsxs)("span",{className:"text-gray-900",children:["-",eA(e.discount_amount)]})]})]}),p&&(0,t.jsxs)("div",{className:"space-y-2",children:[void 0!==e.margin_percent&&0!==e.margin_percent&&(0,t.jsxs)("div",{className:"flex text-sm text-gray-600",children:[(0,t.jsxs)("span",{className:"font-medium w-1/3",children:["Margin (",eE(e.margin_percent),"):"]}),(0,t.jsxs)("span",{className:"text-gray-900",children:["+",eA((e.margin_total_amount||0)-(e.margin_fixed_amount||0))]})]}),void 0!==e.margin_fixed_amount&&0!==e.margin_fixed_amount&&(0,t.jsxs)("div",{className:"flex text-sm text-gray-600",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Margin:"}),(0,t.jsxs)("span",{className:"text-gray-900",children:["+",eA(e.margin_fixed_amount)]})]})]})]}),(0,t.jsx)("div",{className:"mt-4 pt-4 border-t border-gray-200 max-w-2xl",children:(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)("span",{className:"font-bold text-sm text-gray-900 w-1/3",children:"Final Calculated Cost:"}),(0,t.jsxs)("span",{className:"text-sm font-bold text-gray-900",children:[eA(y),d&&" (Cached)"]})]})})]})}]})})},eI=({show:e})=>e?(0,t.jsxs)("div",{className:"bg-blue-50 border border-blue-200 rounded-lg p-4 flex items-start",children:[(0,t.jsx)("div",{className:"text-blue-500 mr-3 shrink-0 mt-0.5",children:(0,t.jsxs)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,t.jsx)("circle",{cx:"12",cy:"12",r:"10"}),(0,t.jsx)("line",{x1:"12",y1:"16",x2:"12",y2:"12"}),(0,t.jsx)("line",{x1:"12",y1:"8",x2:"12.01",y2:"8"})]})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-blue-800",children:"Request/Response Data Not Available"}),(0,t.jsxs)("p",{className:"text-sm text-blue-700 mt-1",children:["To view request and response details, enable prompt storage in your LiteLLM configuration by adding the following to your ",(0,t.jsx)("code",{className:"bg-blue-100 px-1 py-0.5 rounded-sm",children:"proxy_config.yaml"})," file, or toggle the setting in ",(0,t.jsx)("strong",{children:"Admin Settings → Logging Settings"}),"."]}),(0,t.jsx)("pre",{className:"mt-2 bg-white p-3 rounded-sm border border-blue-200 text-xs font-mono overflow-auto",children:`general_settings: + store_model_in_db: true + store_prompts_in_spend_logs: true`}),(0,t.jsx)("p",{className:"text-xs text-blue-700 mt-2",children:"Note: This will only affect new requests after the configuration change."})]})]}):null;function ez({data:e}){let[l,a]=(0,s.useState)({});if(!e||0===e.length)return null;let r=e=>new Date(1e3*e).toLocaleString();return(0,t.jsx)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden mb-6",children:(0,t.jsx)(R.Collapse,{defaultActiveKey:["1"],expandIconPosition:"start",items:[{key:"1",label:(0,t.jsx)("h3",{className:"text-lg font-medium text-gray-900",children:"Vector Store Requests"}),children:(0,t.jsx)("div",{className:"p-4",children:e.map((e,s)=>{var n,i;return(0,t.jsxs)("div",{className:"mb-6 last:mb-0",children:[(0,t.jsx)("div",{className:"bg-white rounded-lg border p-4 mb-4",children:(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Query:"}),(0,t.jsx)("span",{className:"font-mono",children:e.query})]}),(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Vector Store ID:"}),(0,t.jsx)("span",{className:"font-mono",children:e.vector_store_id})]}),(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Provider:"}),(0,t.jsx)("span",{className:"flex items-center",children:(()=>{let{logo:s,displayName:l}=(0,N.getProviderLogoAndName)(e.custom_llm_provider);return(0,t.jsxs)(t.Fragment,{children:[s&&(0,t.jsx)("img",{src:s,alt:`${l} logo`,className:"h-5 w-5 mr-2"}),l]})})()})]})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Start Time:"}),(0,t.jsx)("span",{children:r(e.start_time)})]}),(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"End Time:"}),(0,t.jsx)("span",{children:r(e.end_time)})]}),(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("span",{className:"font-medium w-1/3",children:"Duration:"}),(0,t.jsx)("span",{children:(n=e.start_time,i=e.end_time,`${((i-n)*1e3).toFixed(2)}ms`)})]})]})]})}),(0,t.jsx)("h4",{className:"font-medium mb-2",children:"Search Results"}),(0,t.jsx)("div",{className:"space-y-2",children:e.vector_store_search_response.data.map((e,r)=>{let n=l[`${s}-${r}`]||!1;return(0,t.jsxs)("div",{className:"border rounded-lg overflow-hidden",children:[(0,t.jsxs)("div",{className:"flex items-center p-3 bg-gray-50 cursor-pointer",onClick:()=>{let e;return e=`${s}-${r}`,void a(t=>({...t,[e]:!t[e]}))},children:[(0,t.jsx)("svg",{className:`w-5 h-5 mr-2 transition-transform ${n?"transform rotate-90":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})}),(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsxs)("span",{className:"font-medium mr-2",children:["Result ",r+1]}),(0,t.jsxs)("span",{className:"text-gray-500 text-sm",children:["Score: ",(0,t.jsx)("span",{className:"font-mono",children:e.score.toFixed(4)})]})]})]}),n&&(0,t.jsx)("div",{className:"p-3 border-t bg-white",children:e.content.map((e,s)=>(0,t.jsxs)("div",{className:"mb-2 last:mb-0",children:[(0,t.jsx)("div",{className:"text-xs text-gray-500 mb-1",children:e.type}),(0,t.jsx)("pre",{className:"text-xs font-mono whitespace-pre-wrap break-all bg-gray-50 p-2 rounded-sm",children:e.text})]},s))})]},r)})})]},s)})})}]})})}let{Text:eD}=f.Typography;function eO({value:e,maxWidth:s=180}){return e?(0,t.jsx)(g.Tooltip,{title:e,children:(0,t.jsx)(eD,{copyable:{text:e,tooltips:["Copy","Copied!"]},style:{maxWidth:s,display:"inline-block",verticalAlign:"bottom",fontFamily:S,fontSize:12},ellipsis:!0,children:e})}):(0,t.jsx)(eD,{type:"secondary",children:"-"})}let{Text:eB}=f.Typography;function eR({prompt:e=0,completion:s=0,total:l=0}){return(0,t.jsxs)(eB,{children:[l.toLocaleString()," (",e.toLocaleString()," prompt tokens + ",s.toLocaleString()," completion tokens)"]})}let eF=e=>!!e&&e instanceof Date,eP=e=>"object"==typeof e&&null!==e,eq=e=>!!e&&e instanceof Object&&"function"==typeof e;function e$(e,t){return void 0===t&&(t=!1),!e||t?`"${e}"`:e}function eW(e){let{field:t,value:l,data:a,lastElement:r,openBracket:n,closeBracket:i,level:o,style:d,shouldExpandNode:c,clickToExpandNode:m,outerRef:x,beforeExpandChange:u}=e,p=(0,s.useRef)(!1),[h,g]=(0,s.useState)(()=>c(o,l,t)),f=(0,s.useRef)(null);(0,s.useEffect)(()=>{p.current?g(c(o,l,t)):p.current=!0},[c]);let y=(0,s.useId)();if(0===a.length)return function(e){let{field:t,openBracket:l,closeBracket:a,lastElement:r,style:n}=e;return(0,s.createElement)("div",{className:n.basicChildStyle,role:"treeitem","aria-selected":void 0},(t||""===t)&&(0,s.createElement)("span",{className:n.label},e$(t,n.quotesForFieldNames),":"),(0,s.createElement)("span",{className:n.punctuation},l),(0,s.createElement)("span",{className:n.punctuation},a),!r&&(0,s.createElement)("span",{className:n.punctuation},","))}({field:t,openBracket:n,closeBracket:i,lastElement:r,style:d});let j=h?d.collapseIcon:d.expandIcon,v=h?d.ariaLables.collapseJson:d.ariaLables.expandJson,b=o+1,N=a.length-1,_=e=>{h!==e&&(!u||u({level:o,value:l,field:t,newExpandValue:e}))&&g(e)},w=e=>{if("ArrowRight"===e.key||"ArrowLeft"===e.key)e.preventDefault(),_("ArrowRight"===e.key);else if("ArrowUp"===e.key||"ArrowDown"===e.key){e.preventDefault();let t="ArrowUp"===e.key?-1:1;if(!x.current)return;let s=x.current.querySelectorAll("[role=button]"),l=-1;for(let e=0;e{var e;_(!h);let t=f.current;if(!t)return;let s=null==(e=x.current)?void 0:e.querySelector('[role=button][tabindex="0"]');s&&(s.tabIndex=-1),t.tabIndex=0,t.focus()};return(0,s.createElement)("div",{className:d.basicChildStyle,role:"treeitem","aria-expanded":h,"aria-selected":void 0},(0,s.createElement)("span",{className:j,onClick:k,onKeyDown:w,role:"button","aria-label":v,"aria-expanded":h,"aria-controls":h?y:void 0,ref:f,tabIndex:0===o?0:-1}),(t||""===t)&&(m?(0,s.createElement)("span",{className:d.clickableLabel,onClick:k,onKeyDown:w},e$(t,d.quotesForFieldNames),":"):(0,s.createElement)("span",{className:d.label},e$(t,d.quotesForFieldNames),":")),(0,s.createElement)("span",{className:d.punctuation},n),h?(0,s.createElement)("ul",{id:y,role:"group",className:d.childFieldsContainer},a.map((e,t)=>(0,s.createElement)(eV,{key:e[0]||t,field:e[0],value:e[1],style:d,lastElement:t===N,level:b,shouldExpandNode:c,clickToExpandNode:m,beforeExpandChange:u,outerRef:x}))):(0,s.createElement)("span",{className:d.collapsedContent,onClick:k,onKeyDown:w}),(0,s.createElement)("span",{className:d.punctuation},i),!r&&(0,s.createElement)("span",{className:d.punctuation},","))}function eJ(e){let{field:t,value:s,style:l,lastElement:a,shouldExpandNode:r,clickToExpandNode:n,level:i,outerRef:o,beforeExpandChange:d}=e;return eW({field:t,value:s,lastElement:a||!1,level:i,openBracket:"{",closeBracket:"}",style:l,shouldExpandNode:r,clickToExpandNode:n,data:Object.keys(s).map(e=>[e,s[e]]),outerRef:o,beforeExpandChange:d})}function eH(e){let{field:t,value:s,style:l,lastElement:a,level:r,shouldExpandNode:n,clickToExpandNode:i,outerRef:o,beforeExpandChange:d}=e;return eW({field:t,value:s,lastElement:a||!1,level:r,openBracket:"[",closeBracket:"]",style:l,shouldExpandNode:n,clickToExpandNode:i,data:s.map(e=>[void 0,e]),outerRef:o,beforeExpandChange:d})}function eY(e){let t,{field:l,value:a,style:r,lastElement:n}=e,i=r.otherValue;if(null===a)t="null",i=r.nullValue;else if(void 0===a)t="undefined",i=r.undefinedValue;else if("string"==typeof a||a instanceof String){var o;o=!r.noQuotesForStringValues,t=r.stringifyStringValues?JSON.stringify(a):o?`"${a}"`:a,i=r.stringValue}else if("boolean"==typeof a||a instanceof Boolean)t=a?"true":"false",i=r.booleanValue;else if("number"==typeof a||a instanceof Number)t=a.toString(),i=r.numberValue;else"bigint"==typeof a||a instanceof BigInt?(t=`${a.toString()}n`,i=r.numberValue):t=eF(a)?a.toISOString():eq(a)?"function() { }":a.toString();return(0,s.createElement)("div",{className:r.basicChildStyle,role:"treeitem","aria-selected":void 0},(l||""===l)&&(0,s.createElement)("span",{className:r.label},e$(l,r.quotesForFieldNames),":"),(0,s.createElement)("span",{className:i},t),!n&&(0,s.createElement)("span",{className:r.punctuation},","))}function eV(e){let t=e.value;return Array.isArray(t)?(0,s.createElement)(eH,Object.assign({},e)):!eP(t)||eF(t)||eq(t)?(0,s.createElement)(eY,Object.assign({},e)):(0,s.createElement)(eJ,Object.assign({},e))}let eG={container:"_2IvMF _GzYRV",basicChildStyle:"_2bkNM",childFieldsContainer:"_1BXBN",label:"_1MGIk",clickableLabel:"_2YKJg _1MGIk _1MFti",nullValue:"_2T6PJ",undefinedValue:"_1Gho6",stringValue:"_vGjyY",booleanValue:"_3zQKs",numberValue:"_1bQdo",otherValue:"_1xvuR",punctuation:"_3uHL6 _3eOF8",collapseIcon:"_oLqym _f10Tu _1MFti _1LId0",expandIcon:"_2AXVT _f10Tu _1MFti _1UmXx",collapsedContent:"_2KJWg _1pNG9 _1MFti",noQuotesForStringValues:!1,quotesForFieldNames:!1,ariaLables:{collapseJson:"collapse JSON",expandJson:"expand JSON"},stringifyStringValues:!1},eK=()=>!0,eU=e=>{let{data:t,style:l=eG,shouldExpandNode:a=eK,clickToExpandNode:r=!1,beforeExpandChange:n,compactTopLevel:i,...o}=e,d=(0,s.useRef)(null);return(0,s.createElement)("div",Object.assign({"aria-label":"JSON view"},o,{className:l.container,ref:d,role:"tree"}),i&&eP(t)?Object.entries(t).map(e=>{let[t,i]=e;return(0,s.createElement)(eV,{key:t,field:t,value:i,style:{...eG,...l},lastElement:!0,level:1,shouldExpandNode:a,clickToExpandNode:r,beforeExpandChange:n,outerRef:d})}):(0,s.createElement)(eV,{value:t,style:{...eG,...l},lastElement:!0,level:0,shouldExpandNode:a,clickToExpandNode:r,outerRef:d,beforeExpandChange:n}))},{Text:eQ}=f.Typography;function eX({data:e}){return e?(0,t.jsx)("div",{style:{maxHeight:400,overflow:"auto",background:"#fafafa",padding:12,borderRadius:4},children:(0,t.jsx)("div",{className:"**:[[role='tree']]:bg-white **:[[role='tree']]:text-slate-900",children:(0,t.jsx)(eU,{data:e,style:eG,clickToExpandNode:!0})})}):(0,t.jsx)(eQ,{type:"secondary",children:"No data"})}let eZ=e=>e.request_duration_ms??Date.parse(e.endTime)-Date.parse(e.startTime);function e0(e){if("string"==typeof e)try{return JSON.parse(e)}catch{}return e}function e1(e){return Array.isArray(e)?e:e?[e]:[]}function e2(e){if("string"==typeof e)try{return JSON.parse(e)}catch{}return e}var e5=e.i(366308);let{Text:e4}=f.Typography;function e6({tool:e}){let s=Object.entries(e.parameters?.properties||{}).map(([t,s])=>({key:t,name:t,type:s.type||"any",description:s.description||"-",required:e.parameters?.required?.includes(t)||!1})),l=[{title:"Parameter",dataIndex:"name",key:"name",render:(e,s)=>(0,t.jsxs)(e4,{code:!0,children:[e,s.required&&(0,t.jsx)(e4,{type:"danger",children:"*"})]})},{title:"Type",dataIndex:"type",key:"type",render:e=>(0,t.jsx)(e4,{code:!0,style:{color:"#1890ff"},children:e})},{title:"Description",dataIndex:"description",key:"description",render:e=>(0,t.jsx)(e4,{type:"secondary",children:e})}];return(0,t.jsxs)("div",{children:[e.description&&(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(e4,{style:{lineHeight:1.6,whiteSpace:"pre-wrap"},children:e.description})}),s.length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)(e4,{type:"secondary",style:{fontSize:12,display:"block",marginBottom:8},children:"Parameters"}),(0,t.jsx)(e_.Table,{dataSource:s,columns:l,pagination:!1,size:"small",bordered:!0})]}),e.called&&e.callData&&(0,t.jsxs)("div",{style:{marginTop:16},children:[(0,t.jsx)(e4,{type:"secondary",style:{fontSize:12,display:"block",marginBottom:8},children:"Called With"}),(0,t.jsx)("div",{style:{background:"#f6ffed",border:"1px solid #b7eb8f",borderRadius:4,padding:12},children:(0,t.jsx)("pre",{style:{margin:0,fontSize:12,whiteSpace:"pre-wrap",wordBreak:"break-word"},children:JSON.stringify(e.callData.arguments,null,2)})})]})]})}function e3({tool:e}){let s={type:"function",function:{name:e.name,description:e.description,parameters:e.parameters}};return(0,t.jsx)("pre",{style:{margin:0,whiteSpace:"pre-wrap",wordBreak:"break-word",fontSize:12,background:"#fafafa",padding:12,borderRadius:4,maxHeight:300,overflow:"auto"},children:JSON.stringify(s,null,2)})}let{Text:e8}=f.Typography;function e9({tool:e}){let[l,a]=(0,s.useState)("formatted");return(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",marginBottom:12},children:[(0,t.jsx)(e8,{type:"secondary",style:{fontSize:12},children:"Description"}),(0,t.jsxs)(F.Radio.Group,{size:"small",value:l,onChange:e=>a(e.target.value),children:[(0,t.jsx)(F.Radio.Button,{value:"formatted",children:"Formatted"}),(0,t.jsx)(F.Radio.Button,{value:"json",children:"JSON"})]})]}),"formatted"===l?(0,t.jsx)(e6,{tool:e}):(0,t.jsx)(e3,{tool:e})]})}let{Text:e7}=f.Typography;function te({tool:e}){let[l,a]=(0,s.useState)(!1);return(0,t.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:8,overflow:"hidden"},children:[(0,t.jsxs)("div",{onClick:()=>a(!l),style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"12px 16px",cursor:"pointer",background:l?"#fafafa":"#fff",transition:"background 0.2s"},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:10},children:[(0,t.jsx)(e5.ToolOutlined,{style:{color:"#8c8c8c",fontSize:14}}),(0,t.jsxs)(e7,{style:{fontSize:14},children:[e.index,". ",e.name]})]}),(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:[(0,t.jsx)(h.Tag,{color:e.called?"blue":"default",children:e.called?"called":"not called"}),l?(0,t.jsx)(v.DownOutlined,{style:{fontSize:12,color:"#8c8c8c"}}):(0,t.jsx)(d.RightOutlined,{style:{fontSize:12,color:"#8c8c8c"}})]})]}),l&&(0,t.jsx)("div",{style:{padding:"16px",borderTop:"1px solid #f0f0f0",background:"#fff"},children:(0,t.jsx)(e9,{tool:e})})]})}let{Text:tt}=f.Typography;function ts({log:e}){let s=function(e){let t,s=!(t=e2(e.proxy_server_request||e.messages))||Array.isArray(t)?[]:"object"==typeof t&&t.tools&&Array.isArray(t.tools)?t.tools:[];if(0===s.length)return[];let l=function(e){let t=e2(e.response);if(!t||"object"!=typeof t)return[];let s=t.choices;if(Array.isArray(s)&&s.length>0){let e=s[0].message;if(e&&Array.isArray(e.tool_calls))return e.tool_calls}if(Array.isArray(t.content)){let e=t.content.filter(e=>"tool_use"===e.type);if(e.length>0)return e.map(e=>({id:e.id,type:"function",function:{name:e.name,arguments:JSON.stringify(e.input||{})}}))}if(Array.isArray(t.tool_calls))return t.tool_calls;if(Array.isArray(t.results)){let e=[];for(let s of t.results)if("response.done"===s.type&&s.response?.output)for(let t of s.response.output)"function_call"===t.type&&e.push({id:t.call_id||"",type:"function",function:{name:t.name||"",arguments:t.arguments||"{}"}});if(e.length>0)return e}return[]}(e),a=new Set(l.map(e=>e.function?.name).filter(Boolean)),r=new Map;return l.forEach(e=>{let t=e.function?.name;t&&r.set(t,{id:e.id,name:t,arguments:function(e){try{return JSON.parse(e)}catch{return{}}}(e.function?.arguments||"{}")})}),s.map((e,t)=>{let s=e.function?.name||e.name||`Tool ${t+1}`;return{index:t+1,name:s,description:e.function?.description||e.description||"",parameters:e.function?.parameters||e.input_schema||{},called:a.has(s),callData:r.get(s)}})}(e);if(0===s.length)return null;let l=s.length,a=s.filter(e=>e.called).length,r=s.slice(0,2).map(e=>e.name).join(", "),n=s.length>2;return(0,t.jsx)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden mb-6",children:(0,t.jsx)(R.Collapse,{expandIconPosition:"start",items:[{key:"1",label:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:12,flexWrap:"wrap"},children:[(0,t.jsx)("h3",{className:"text-lg font-medium text-gray-900",children:"Tools"}),(0,t.jsxs)(tt,{type:"secondary",style:{fontSize:14},children:[l," provided, ",a," called"]}),(0,t.jsxs)(tt,{type:"secondary",style:{fontSize:14},children:["• ",r,n&&"..."]})]}),children:(0,t.jsx)("div",{style:{display:"flex",flexDirection:"column",gap:8},children:s.map(e=>(0,t.jsx)(te,{tool:e},e.name))})}]})})}let tl=e=>{if(!e)return{};if("string"==typeof e)try{return JSON.parse(e)}catch{return{raw:e}}return e};var ta=e.i(888259);e.i(247167);var tr=e.i(931067);let tn={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 512a48 48 0 1096 0 48 48 0 10-96 0zm200 0a48 48 0 1096 0 48 48 0 10-96 0zm-400 0a48 48 0 1096 0 48 48 0 10-96 0zm661.2-173.6c-22.6-53.7-55-101.9-96.3-143.3a444.35 444.35 0 00-143.3-96.3C630.6 75.7 572.2 64 512 64h-2c-60.6.3-119.3 12.3-174.5 35.9a445.35 445.35 0 00-142 96.5c-40.9 41.3-73 89.3-95.2 142.8-23 55.4-34.6 114.3-34.3 174.9A449.4 449.4 0 00112 714v152a46 46 0 0046 46h152.1A449.4 449.4 0 00510 960h2.1c59.9 0 118-11.6 172.7-34.3a444.48 444.48 0 00142.8-95.2c41.3-40.9 73.8-88.7 96.5-142 23.6-55.2 35.6-113.9 35.9-174.5.3-60.9-11.5-120-34.8-175.6zm-151.1 438C704 845.8 611 884 512 884h-1.7c-60.3-.3-120.2-15.3-173.1-43.5l-8.4-4.5H188V695.2l-4.5-8.4C155.3 633.9 140.3 574 140 513.7c-.4-99.7 37.7-193.3 107.6-263.8 69.8-70.5 163.1-109.5 262.8-109.9h1.7c50 0 98.5 9.7 144.2 28.9 44.6 18.7 84.6 45.6 119 80 34.3 34.3 61.3 74.4 80 119 19.4 46.2 29.1 95.2 28.9 145.8-.6 99.6-39.7 192.9-110.1 262.7z"}}]},name:"message",theme:"outlined"};var ti=e.i(9583),to=s.forwardRef(function(e,t){return s.createElement(ti.default,(0,tr.default)({},e,{ref:t,icon:tn}))}),j=j;let{Text:td}=f.Typography;function tc({type:e,tokens:s,cost:a,onCopy:r,isCollapsed:n,onToggleCollapse:o,turnCount:d}){return(0,t.jsxs)("div",{onClick:o,style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"10px 16px",borderBottom:n?"none":"1px solid #f0f0f0",background:"#fafafa",cursor:o?"pointer":"default",transition:"background 0.15s ease"},onMouseEnter:e=>{o&&(e.currentTarget.style.background="#f5f5f5")},onMouseLeave:e=>{e.currentTarget.style.background="#fafafa"},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:16},children:[o&&(0,t.jsx)("div",{style:{display:"flex",alignItems:"center"},children:n?(0,t.jsx)(v.DownOutlined,{style:{fontSize:10,color:"#8c8c8c"}}):(0,t.jsx)(j.default,{style:{fontSize:10,color:"#8c8c8c"}})}),(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:["input"===e?(0,t.jsx)(to,{style:{color:"#8c8c8c",fontSize:14}}):(0,t.jsx)("span",{style:{fontSize:14,filter:"grayscale(1)",opacity:.6},children:"✨"}),(0,t.jsx)(td,{style:{fontWeight:500,fontSize:14},children:"input"===e?"Input":"Output"})]}),void 0!==s&&(0,t.jsxs)(td,{type:"secondary",style:{fontSize:12},children:["Tokens: ",s.toLocaleString()]}),void 0!==a&&(0,t.jsxs)(td,{type:"secondary",style:{fontSize:12},children:["Cost: $",a.toFixed(6)]}),void 0!==d&&d>0&&(0,t.jsxs)(td,{type:"secondary",style:{fontSize:12},children:["Turns: ",d]})]}),(0,t.jsx)(g.Tooltip,{title:"Copy",children:(0,t.jsx)(l.Button,{type:"text",size:"small",icon:(0,t.jsx)(i.CopyOutlined,{}),onClick:e=>{e.stopPropagation(),r()}})})]})}let{Text:tm}=f.Typography;function tx({label:e,content:l,defaultExpanded:a=!1}){let[r,n]=(0,s.useState)(a),[i,o]=(0,s.useState)(!1),c=l?.length||0;return l&&0!==c?(0,t.jsxs)("div",{style:{marginBottom:8},children:[(0,t.jsxs)("div",{onClick:()=>n(!r),onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),style:{display:"flex",alignItems:"center",gap:6,cursor:"pointer",padding:"4px 0",borderRadius:4,background:i?"#f5f5f5":"transparent",transition:"background 0.15s ease",marginBottom:4*!!r},children:[r?(0,t.jsx)(v.DownOutlined,{style:{fontSize:10,color:"#8c8c8c"}}):(0,t.jsx)(d.RightOutlined,{style:{fontSize:10,color:"#8c8c8c"}}),(0,t.jsx)(tm,{type:"secondary",style:{fontSize:10,letterSpacing:"0.5px",textTransform:"uppercase"},children:e}),(0,t.jsxs)(tm,{type:"secondary",style:{fontSize:10},children:["(",c.toLocaleString()," chars)"]})]}),(0,t.jsx)("div",{style:{maxHeight:r?"2000px":"0px",overflow:"hidden",transition:"max-height 0.2s ease-out, opacity 0.2s ease-out",opacity:+!!r},children:(0,t.jsx)("div",{style:{paddingLeft:16,fontSize:13,lineHeight:1.7,color:"#262626",borderLeft:"1px solid #f0f0f0",whiteSpace:"pre-wrap",wordBreak:"break-word"},children:l})})]}):null}let{Text:tu}=f.Typography;function tp({tool:e,compact:s=!1}){return(0,t.jsxs)("div",{style:{background:"#f8f9fa",border:"1px solid #e9ecef",borderRadius:6,padding:s?"6px 10px":"10px 14px",marginTop:8,fontFamily:"monospace",fontSize:12,position:"relative"},children:[(0,t.jsx)("div",{style:{position:"absolute",top:-8,left:12,background:"#fff",padding:"0 6px",fontSize:10,color:"#8c8c8c",border:"1px solid #e9ecef",borderRadius:3},children:"function"}),(0,t.jsx)(tu,{strong:!0,style:{fontSize:13,display:"block",marginBottom:6},children:e.name}),Object.keys(e.arguments).length>0&&(0,t.jsx)("div",{children:Object.entries(e.arguments).map(([e,s])=>(0,t.jsxs)("div",{style:{marginBottom:2},children:[(0,t.jsxs)(tu,{type:"secondary",style:{fontSize:12},children:[e,":"," "]}),(0,t.jsx)(tu,{style:{fontSize:12},children:JSON.stringify(s)})]},e))})]})}let{Text:th}=f.Typography;function tg({label:e,content:s,toolCalls:l,isCompact:a=!1}){let r=s&&"null"!==s&&s.length>0?s:null,n=l&&l.length>0;return r||n?(0,t.jsxs)("div",{style:{marginBottom:8*!!a},children:[(0,t.jsx)(th,{type:"secondary",style:{fontSize:10,letterSpacing:"0.5px",textTransform:"uppercase",display:"block",marginBottom:3},children:e}),r&&(0,t.jsx)("div",{style:{fontSize:13,lineHeight:1.7,color:"#262626",whiteSpace:"pre-wrap",wordBreak:"break-word",marginBottom:6*!!n},children:r}),n&&(0,t.jsx)("div",{children:l.map((e,s)=>(0,t.jsx)(tp,{tool:e,compact:a},e.id||s))})]}):null}let{Text:tf}=f.Typography;function ty({messages:e}){let[l,a]=(0,s.useState)(!1),[r,n]=(0,s.useState)(!1);return 0===e.length?null:(0,t.jsxs)("div",{style:{marginBottom:8},children:[(0,t.jsxs)("div",{onClick:()=>a(!l),onMouseEnter:()=>n(!0),onMouseLeave:()=>n(!1),style:{display:"flex",alignItems:"center",gap:6,cursor:"pointer",padding:"4px 0",borderRadius:4,background:r?"#f5f5f5":"transparent",transition:"background 0.15s ease",marginBottom:4*!!l},children:[l?(0,t.jsx)(v.DownOutlined,{style:{fontSize:10,color:"#8c8c8c"}}):(0,t.jsx)(d.RightOutlined,{style:{fontSize:10,color:"#8c8c8c"}}),(0,t.jsxs)(tf,{type:"secondary",style:{fontSize:10,letterSpacing:"0.5px",textTransform:"uppercase"},children:["HISTORY (",e.length," message",1!==e.length?"s":"",")"]})]}),(0,t.jsx)("div",{style:{maxHeight:l?"2000px":"0px",overflow:"hidden",transition:"max-height 0.2s ease-out, opacity 0.2s ease-out",opacity:+!!l},children:(0,t.jsx)("div",{style:{paddingLeft:16,borderLeft:"1px solid #f0f0f0"},children:e.map((e,s)=>(0,t.jsx)(tg,{label:e.role.toUpperCase(),content:e.content,toolCalls:e.toolCalls,isCompact:!0},s))})})]})}function tj({messages:e,promptTokens:l,inputCost:a}){let[r,n]=(0,s.useState)(!1);if(0===e.length)return null;let i=e.find(e=>"system"===e.role),o=e.filter(e=>"system"!==e.role),d=o.length>0?o[o.length-1]:null,c=o.slice(0,-1);return(0,t.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,marginBottom:8,overflow:"hidden"},children:[(0,t.jsx)(tc,{type:"input",tokens:l,cost:a,onCopy:()=>{let e=d?.content||"";navigator.clipboard.writeText(e),ta.default.success("Input copied")},isCollapsed:r,onToggleCollapse:()=>n(!r)}),(0,t.jsx)("div",{style:{maxHeight:r?"0px":"10000px",overflow:"hidden",transition:"max-height 0.3s ease-out, opacity 0.3s ease-out",opacity:+!r},children:(0,t.jsxs)("div",{style:{padding:"12px 16px"},children:[i&&(0,t.jsx)(tx,{label:"SYSTEM",content:i.content,defaultExpanded:!!(i.content&&i.content.length<200)}),c.length>0&&(0,t.jsx)(ty,{messages:c}),d&&(0,t.jsx)(tg,{label:d.role.toUpperCase(),content:d.content,toolCalls:d.toolCalls})]})})]})}let{Text:tv}=f.Typography;function tb({message:e,completionTokens:l,outputCost:a}){let[r,n]=(0,s.useState)(!1),i=()=>{if(!e)return;let t=e.content||"";navigator.clipboard.writeText(t),ta.default.success("Output copied")};return e?(0,t.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,overflow:"hidden"},children:[(0,t.jsx)(tc,{type:"output",tokens:l,cost:a,onCopy:i,isCollapsed:r,onToggleCollapse:()=>n(!r)}),(0,t.jsx)("div",{style:{maxHeight:r?"0px":"10000px",overflow:"hidden",transition:"max-height 0.3s ease-out, opacity 0.3s ease-out",opacity:+!r},children:(0,t.jsx)("div",{style:{padding:"12px 16px"},children:(0,t.jsx)(tg,{label:"ASSISTANT",content:e.content,toolCalls:e.toolCalls})})})]}):(0,t.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,overflow:"hidden"},children:[(0,t.jsx)(tc,{type:"output",tokens:l,cost:a,onCopy:i,isCollapsed:r,onToggleCollapse:()=>n(!r)}),(0,t.jsx)("div",{style:{maxHeight:r?"0px":"10000px",overflow:"hidden",transition:"max-height 0.3s ease-out, opacity 0.3s ease-out",opacity:+!r},children:(0,t.jsx)("div",{style:{padding:"12px 16px"},children:(0,t.jsx)(tv,{type:"secondary",style:{fontSize:13,fontStyle:"italic"},children:"No response data available"})})})]})}var tN=e.i(782273),t_=e.i(313603),tw=e.i(793916),j=j;let{Text:tk}=f.Typography;function tS({response:e,metrics:s}){let l=e?.results||[],a=e?.usage,r=l.find(e=>"session.created"===e.type||"session.updated"===e.type),n=l.filter(e=>"response.done"===e.type);return(0,t.jsxs)("div",{children:[r?.session&&(0,t.jsx)(tC,{session:r.session,turnCount:n.length}),n.length>0&&(0,t.jsx)(tT,{responses:n.map(e=>e.response).filter(Boolean),totalUsage:a,metrics:s}),!r&&0===n.length&&(0,t.jsx)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,padding:"16px",color:"#8c8c8c",fontStyle:"italic",fontSize:13},children:"No recognized realtime events found"})]})}function tC({session:e,turnCount:l}){let[a,r]=(0,s.useState)(!0);return(0,t.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,marginBottom:8,overflow:"hidden"},children:[(0,t.jsx)("div",{onClick:()=>r(!a),style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"10px 16px",borderBottom:a?"none":"1px solid #f0f0f0",background:"#fafafa",cursor:"pointer",transition:"background 0.15s ease"},onMouseEnter:e=>{e.currentTarget.style.background="#f5f5f5"},onMouseLeave:e=>{e.currentTarget.style.background="#fafafa"},children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:16},children:[(0,t.jsx)("div",{style:{display:"flex",alignItems:"center"},children:a?(0,t.jsx)(v.DownOutlined,{style:{fontSize:10,color:"#8c8c8c"}}):(0,t.jsx)(j.default,{style:{fontSize:10,color:"#8c8c8c"}})}),(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:[(0,t.jsx)(t_.SettingOutlined,{style:{color:"#8c8c8c",fontSize:14}}),(0,t.jsx)(tk,{style:{fontWeight:500,fontSize:14},children:"Session"})]}),(0,t.jsx)(tk,{type:"secondary",style:{fontSize:12},children:e.model}),l>0&&(0,t.jsxs)(h.Tag,{color:"purple",style:{margin:0,fontWeight:500},children:[l," ",1===l?"turn":"turns"]}),e.voice&&(0,t.jsxs)(h.Tag,{color:"blue",style:{margin:0},children:[(0,t.jsx)(tN.SoundOutlined,{})," ",e.voice]}),e.modalities&&(0,t.jsx)("div",{style:{display:"flex",gap:4},children:e.modalities.map(e=>(0,t.jsxs)(h.Tag,{style:{margin:0},children:["audio"===e?(0,t.jsx)(tw.AudioOutlined,{}):(0,t.jsx)(to,{})," ",e]},e))})]})}),(0,t.jsx)("div",{style:{maxHeight:a?"0px":"10000px",overflow:"hidden",transition:"max-height 0.3s ease-out, opacity 0.3s ease-out",opacity:+!a},children:(0,t.jsxs)("div",{style:{padding:"12px 16px"},children:[(0,t.jsxs)("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"8px 24px",fontSize:13},children:[(0,t.jsx)(tM,{label:"Model",value:e.model}),(0,t.jsx)(tM,{label:"Voice",value:e.voice}),(0,t.jsx)(tM,{label:"Temperature",value:e.temperature}),(0,t.jsx)(tM,{label:"Max Output Tokens",value:e.max_response_output_tokens}),(0,t.jsx)(tM,{label:"Input Audio Format",value:e.input_audio_format}),(0,t.jsx)(tM,{label:"Output Audio Format",value:e.output_audio_format}),e.turn_detection&&(0,t.jsx)(tM,{label:"Turn Detection",value:e.turn_detection.type}),e.tools&&e.tools.length>0&&(0,t.jsx)(tM,{label:"Tools",value:`${e.tools.length} tool(s)`})]}),e.instructions&&(0,t.jsxs)("div",{style:{marginTop:12},children:[(0,t.jsx)(tk,{type:"secondary",style:{fontSize:10,letterSpacing:"0.5px",textTransform:"uppercase",display:"block",marginBottom:4},children:"Instructions"}),(0,t.jsx)("div",{style:{fontSize:12,lineHeight:1.6,color:"#595959",background:"#fafafa",padding:"8px 12px",borderRadius:4,border:"1px solid #f0f0f0",whiteSpace:"pre-wrap",wordBreak:"break-word",maxHeight:120,overflowY:"auto"},children:e.instructions})]})]})})]})}function tT({responses:e,totalUsage:l,metrics:a}){let[r,n]=(0,s.useState)(!1),i=l?.total_tokens,o=e.length;return(0,t.jsxs)("div",{style:{border:"1px solid #f0f0f0",borderRadius:6,overflow:"hidden"},children:[(0,t.jsx)(tc,{type:"output",tokens:a?.completion_tokens??i,cost:a?.output_cost,onCopy:()=>{let t=e.flatMap(e=>(e.output||[]).flatMap(e=>(e.content||[]).map(t=>`${e.role}: ${t.transcript||t.text||""}`))).join("\n");navigator.clipboard.writeText(t)},isCollapsed:r,onToggleCollapse:()=>n(!r),turnCount:o}),(0,t.jsx)("div",{style:{maxHeight:r?"0px":"10000px",overflow:"hidden",transition:"max-height 0.3s ease-out, opacity 0.3s ease-out",opacity:+!r},children:(0,t.jsx)("div",{style:{padding:"12px 16px"},children:e.map((e,s)=>(0,t.jsx)(tL,{response:e,index:s},e.id||s))})})]})}function tL({response:e,index:s}){let l=e.output||[],a=e.usage;return(0,t.jsxs)("div",{style:{marginBottom:12,paddingBottom:12,borderBottom:"1px solid #f5f5f5"},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:8},children:[(0,t.jsx)(h.Tag,{color:"completed"===e.status?"green":"orange",style:{margin:0},children:e.status||"unknown"}),a&&(0,t.jsxs)(tk,{type:"secondary",style:{fontSize:11},children:[a.input_tokens??0," in / ",a.output_tokens??0," out tokens"]}),e.conversation_id&&(0,t.jsx)(g.Tooltip,{title:e.conversation_id,children:(0,t.jsxs)(tk,{type:"secondary",style:{fontSize:11,cursor:"help"},children:["conv: ",e.conversation_id.slice(0,12),"..."]})})]}),l.map((e,s)=>(0,t.jsx)(tA,{output:e},e.id||s)),a?.input_token_details&&(0,t.jsx)(tE,{label:"Input",details:a.input_token_details}),a?.output_token_details&&(0,t.jsx)(tE,{label:"Output",details:a.output_token_details})]})}function tA({output:e}){let s=e.content||[];return s.some(e=>e.transcript||e.text)?(0,t.jsxs)("div",{style:{marginBottom:8},children:[(0,t.jsx)(tk,{type:"secondary",style:{fontSize:10,letterSpacing:"0.5px",textTransform:"uppercase",display:"block",marginBottom:3},children:e.role?.toUpperCase()||"ASSISTANT"}),s.map((e,s)=>{let l=e.transcript||e.text;return l?(0,t.jsxs)("div",{style:{display:"flex",alignItems:"flex-start",gap:8,marginBottom:4},children:["audio"===e.type&&(0,t.jsx)(tw.AudioOutlined,{style:{color:"#8c8c8c",fontSize:12,marginTop:3,flexShrink:0}}),"text"===e.type&&(0,t.jsx)(to,{style:{color:"#8c8c8c",fontSize:12,marginTop:3,flexShrink:0}}),(0,t.jsx)("div",{style:{fontSize:13,lineHeight:1.7,color:"#262626",whiteSpace:"pre-wrap",wordBreak:"break-word"},children:l})]},s):null})]}):null}function tE({label:e,details:s}){let l=Object.entries(s).filter(([,e])=>"number"==typeof e||"object"==typeof e&&null!==e);return 0===l.length?null:(0,t.jsxs)("div",{style:{marginTop:4},children:[(0,t.jsxs)(tk,{type:"secondary",style:{fontSize:10,letterSpacing:"0.5px",textTransform:"uppercase"},children:[e," Token Breakdown"]}),(0,t.jsx)("div",{style:{display:"flex",flexWrap:"wrap",gap:8,marginTop:4},children:l.map(([e,s])=>"number"==typeof s?(0,t.jsxs)(h.Tag,{style:{margin:0},children:[e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase()),": ",s.toLocaleString()]},e):null)})]})}function tM({label:e,value:s}){return null==s?null:(0,t.jsxs)("div",{children:[(0,t.jsx)(tk,{type:"secondary",style:{fontSize:11},children:e}),(0,t.jsx)("div",{style:{fontSize:13,color:"#262626"},children:String(s)})]})}function tI({request:e,response:s,metrics:l}){let a,r,n;if(s&&s.results&&Array.isArray(s.results)&&0!==s.results.length&&s.results.some(e=>"session.created"===e.type||"session.updated"===e.type||"response.done"===e.type))return(0,t.jsx)(tS,{response:s,metrics:l});let{requestMessages:i,responseMessage:o}=(a=[],(Array.isArray(e)?e:Array.isArray(e?.messages)?e.messages:[]).forEach(e=>{let t;a.push({role:e.role||"user",content:"string"==typeof(t=e.content)?t:Array.isArray(t)?t.map(e=>"string"==typeof e?e:"text"===e.type?e.text:"image_url"===e.type?"[Image]":JSON.stringify(e)).join("\n"):JSON.stringify(t),toolCallId:e.tool_call_id})}),r=null,(n=s?.choices?.[0]?.message)&&(r={role:n.role||"assistant",content:n.content||"",toolCalls:(e=>{if(e&&Array.isArray(e))return e.map(e=>({id:e.id||"",name:e.function?.name||"unknown",arguments:tl(e.function?.arguments)}))})(n.tool_calls)}),{requestMessages:a,responseMessage:r});return(0,t.jsxs)("div",{children:[(0,t.jsx)(tj,{messages:i,promptTokens:l?.prompt_tokens,inputCost:l?.input_cost}),(0,t.jsx)(tb,{message:o,completionTokens:l?.completion_tokens,outputCost:l?.output_cost})]})}let{Text:tz}=f.Typography;function tD({logEntry:e,isLoadingDetails:s=!1,accessToken:l}){var a,r;let n=e.metadata||{},i="failure"===n.status,o=i?n.error_information:null,d=!!(a=e.messages)&&(Array.isArray(a)?a.length>0:"object"==typeof a&&Object.keys(a).length>0),c=!!(r=e.response)&&Object.keys(e0(r)).length>0,m=!d&&!c&&!i&&!s,x=n?.guardrail_information,u=e1(x),p=u.length>0,h=u.reduce((e,t)=>{let s=t?.masked_entity_count;return s?e+Object.values(s).reduce((e,t)=>"number"==typeof t?e+t:e,0):e},0),g=0===u.length?"-":1===u.length?u[0]?.guardrail_name??"-":`${u.length} guardrails`,f=n?.eval_information,y=n.vector_store_request_metadata&&Array.isArray(n.vector_store_request_metadata)&&n.vector_store_request_metadata.length>0;return(0,t.jsxs)("div",{style:{padding:`${_} ${_} 0`},children:[i&&o&&(0,t.jsx)(B.Alert,{type:"error",showIcon:!0,message:"Request Failed",description:(0,t.jsx)(tO,{errorInfo:o}),className:"mb-6"}),e.request_tags&&Object.keys(e.request_tags).length>0&&(0,t.jsx)(tB,{tags:e.request_tags}),(0,t.jsx)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden mb-6",children:(0,t.jsx)(D.Card,{title:"Request Details",size:"small",bordered:!1,style:{marginBottom:0},children:(0,t.jsxs)(z.Descriptions,{column:2,size:"small",children:[(0,t.jsx)(z.Descriptions.Item,{label:"Model",children:e.model}),(0,t.jsx)(z.Descriptions.Item,{label:"Provider",children:e.custom_llm_provider||"-"}),(0,t.jsx)(z.Descriptions.Item,{label:"Call Type",children:e.call_type}),(0,t.jsx)(z.Descriptions.Item,{label:"Model ID",children:(0,t.jsx)(eO,{value:e.model_id})}),(0,t.jsx)(z.Descriptions.Item,{label:"API Base",children:(0,t.jsx)(eO,{value:e.api_base,maxWidth:200})}),e.requester_ip_address&&(0,t.jsx)(z.Descriptions.Item,{label:"IP Address",children:e.requester_ip_address}),p&&(0,t.jsx)(z.Descriptions.Item,{label:"Guardrail",children:(0,t.jsx)(tR,{label:g,maskedCount:h})})]})})}),(0,t.jsx)(tF,{logEntry:e,metadata:n}),(0,t.jsx)(eM,{costBreakdown:n?.cost_breakdown,totalSpend:e.spend??0,promptTokens:e.prompt_tokens,completionTokens:e.completion_tokens,cacheHit:e.cache_hit,rawInputTokens:n?.additional_usage_values?.prompt_tokens_details?.text_tokens,cacheReadTokens:n?.additional_usage_values?.cache_read_input_tokens,cacheCreationTokens:n?.additional_usage_values?.cache_creation_input_tokens}),(0,t.jsx)(ts,{log:e}),m&&(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsx)(eI,{show:m})}),s?(0,t.jsxs)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden mb-6 p-8 text-center",children:[(0,t.jsx)(P.Spin,{size:"default"}),(0,t.jsx)("div",{style:{marginTop:8,color:"#999"},children:"Loading request & response data..."})]}):(0,t.jsx)(tP,{hasResponse:c,hasError:i,getRawRequest:()=>e0(e.proxy_server_request||e.messages),getFormattedResponse:()=>i&&o?{error:{message:o.error_message||"An error occurred",type:o.error_class||"error",code:o.error_code||"unknown",param:null}}:e0(e.response),logEntry:e}),p&&(0,t.jsx)("div",{id:"guardrail-section",children:(0,t.jsx)(eN,{data:x,accessToken:l??null,logEntry:{request_id:e.request_id,user:e.user,model:e.model,startTime:e.startTime,metadata:e.metadata}})}),null!=f&&(0,t.jsx)(eT,{data:f}),y&&(0,t.jsx)(ez,{data:n.vector_store_request_metadata}),e.metadata&&Object.keys(e.metadata).length>0&&(0,t.jsx)(t$,{metadata:e.metadata}),(0,t.jsx)("div",{style:{height:_}})]})}function tO({errorInfo:e}){return(0,t.jsxs)("div",{children:[e.error_code&&(0,t.jsxs)("div",{children:[(0,t.jsx)(tz,{strong:!0,children:"Error Code:"})," ",e.error_code]}),e.error_message&&(0,t.jsxs)("div",{children:[(0,t.jsx)(tz,{strong:!0,children:"Message:"})," ",e.error_message]})]})}function tB({tags:e}){return(0,t.jsxs)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden p-4 mb-6",children:[(0,t.jsx)(tz,{strong:!0,style:{display:"block",marginBottom:8,fontSize:16},children:"Tags"}),(0,t.jsx)(p.Space,{size:8,wrap:!0,children:Object.entries(e).map(([e,s])=>(0,t.jsxs)(h.Tag,{children:[e,": ",String(s)]},e))})]})}function tR({label:e,maskedCount:s}){return(0,t.jsxs)(p.Space,{size:8,children:[(0,t.jsx)("a",{onClick:()=>{let e=document.getElementById("guardrail-section");e&&e.scrollIntoView({behavior:"smooth"})},style:{cursor:"pointer"},children:e}),s>0&&(0,t.jsxs)(h.Tag,{color:"blue",children:[s," masked"]})]})}function tF({logEntry:e,metadata:s}){let l=e.completionStartTime,a=l&&l!==e.endTime?new Date(l).getTime()-new Date(e.startTime).getTime():null,r=e.cache_hit||s?.additional_usage_values?.cache_read_input_tokens&&s.additional_usage_values.cache_read_input_tokens>0,n=String(e.cache_hit??"None"),i="true"===n.toLowerCase()?"green":"false"===n.toLowerCase()?"red":"default",o=function(e){let t=e?.additional_usage_values?.prompt_tokens_details?.text_tokens??e?.usage_object?.prompt_tokens_details?.text_tokens;if(null==t)return;let s=Number(t);return Number.isFinite(s)?s:void 0}(s),d="anthropic_messages"===e.call_type&&void 0!==o;return(0,t.jsx)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden mb-6",children:(0,t.jsx)(D.Card,{title:"Metrics",size:"small",style:{marginBottom:0},children:(0,t.jsxs)(z.Descriptions,{column:2,size:"small",children:[d?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(z.Descriptions.Item,{label:"Input Tokens",children:(0,q.formatNumberWithCommas)(o)}),(0,t.jsx)(z.Descriptions.Item,{label:"Output Tokens",children:(0,q.formatNumberWithCommas)(e.completion_tokens)})]}):(0,t.jsx)(z.Descriptions.Item,{label:"Tokens",children:(0,t.jsx)(eR,{prompt:e.prompt_tokens,completion:e.completion_tokens,total:e.total_tokens})}),(0,t.jsxs)(z.Descriptions.Item,{label:"Cost",children:["$",(0,q.formatNumberWithCommas)(e.spend||0,8)]}),(0,t.jsxs)(z.Descriptions.Item,{label:"Duration",children:[null!=e.request_duration_ms?(e.request_duration_ms/1e3).toFixed(3):"-"," s"]}),null!=a&&a>0&&(0,t.jsxs)(z.Descriptions.Item,{label:"Time to First Token",children:[(a/1e3).toFixed(3)," s"]}),r&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(z.Descriptions.Item,{label:"Cache Hit",children:(0,t.jsx)(h.Tag,{color:i,children:n})}),s?.additional_usage_values?.cache_read_input_tokens>0&&(0,t.jsx)(z.Descriptions.Item,{label:"Cache Read Tokens",children:(0,q.formatNumberWithCommas)(s.additional_usage_values.cache_read_input_tokens)}),s?.additional_usage_values?.cache_creation_input_tokens>0&&(0,t.jsx)(z.Descriptions.Item,{label:"Cache Creation Tokens",children:(0,q.formatNumberWithCommas)(s.additional_usage_values.cache_creation_input_tokens)})]}),s?.litellm_overhead_time_ms!==void 0&&null!==s.litellm_overhead_time_ms&&(0,t.jsxs)(z.Descriptions.Item,{label:"LiteLLM Overhead",children:[s.litellm_overhead_time_ms.toFixed(2)," ms"]}),(0,t.jsx)(z.Descriptions.Item,{label:"Retries",children:s?.attempted_retries!==void 0&&s?.attempted_retries!==null?s.attempted_retries>0?(0,t.jsxs)(t.Fragment,{children:[s.attempted_retries,void 0!==s.max_retries&&null!==s.max_retries?` / ${s.max_retries}`:""]}):(0,t.jsx)(h.Tag,{color:"green",children:"None"}):"-"}),(0,t.jsx)(z.Descriptions.Item,{label:"Start Time",children:(0,b.default)(e.startTime).format("YYYY-MM-DDTHH:mm:ss.SSS[Z]")}),(0,t.jsx)(z.Descriptions.Item,{label:"End Time",children:(0,b.default)(e.endTime).format("YYYY-MM-DDTHH:mm:ss.SSS[Z]")})]})})})}function tP({hasResponse:e,hasError:l,getRawRequest:a,getFormattedResponse:r,logEntry:n}){let[i,o]=(0,s.useState)(w),[d,c]=(0,s.useState)("pretty"),m=n.spend??0,x=n.prompt_tokens||0,u=n.completion_tokens||0,p=x+u,h=n.metadata?.cost_breakdown,g=h?.input_cost!==void 0&&h?.output_cost!==void 0,f=g?h.input_cost??0:p>0?m*x/p:0,y=g?h.output_cost??0:p>0?m*u/p:0;return(0,t.jsx)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden mb-6",children:(0,t.jsx)(R.Collapse,{defaultActiveKey:["1"],expandIconPosition:"start",items:[{key:"1",label:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%"},onClick:e=>{e.target.closest(".ant-radio-group")&&e.stopPropagation()},children:[(0,t.jsx)("h3",{className:"text-lg font-medium text-gray-900",style:{margin:0},children:"Request & Response"}),(0,t.jsxs)(F.Radio.Group,{size:"small",value:d,onChange:e=>c(e.target.value),children:[(0,t.jsx)(F.Radio.Button,{value:"pretty",children:"Pretty"}),(0,t.jsx)(F.Radio.Button,{value:"json",children:"JSON"})]})]}),children:(0,t.jsx)("div",{children:"pretty"===d?(0,t.jsx)(tI,{request:a(),response:r(),metrics:{prompt_tokens:x,completion_tokens:u,input_cost:f,output_cost:y}}):(0,t.jsx)(O.Tabs,{activeKey:i,onChange:e=>o(e),tabBarExtraContent:(0,t.jsx)(tz,{copyable:{text:JSON.stringify(i===w?a():r(),null,2),tooltips:["Copy JSON","Copied!"]},disabled:i===k&&!e&&!l}),items:[{key:w,label:"Request",children:(0,t.jsx)("div",{style:{paddingTop:16,paddingBottom:16},children:(0,t.jsx)(eX,{data:a(),mode:"formatted"})})},{key:k,label:"Response",children:(0,t.jsx)("div",{style:{paddingTop:16,paddingBottom:16},children:e||l?(0,t.jsx)(eX,{data:r(),mode:"formatted"}):(0,t.jsx)("div",{style:{textAlign:"center",padding:20,color:"#999",fontStyle:"italic"},children:"Response data not available"})})}]})})}]})})}function tq({guardrailEntries:e}){let s=e.every(e=>{let t=e?.guardrail_status||e?.status;return"pass"===t||"passed"===t||"success"===t});return(0,t.jsx)("div",{style:{textAlign:"left",marginBottom:12},children:(0,t.jsxs)("div",{onClick:()=>{let e=document.getElementById("guardrail-section");e&&e.scrollIntoView({behavior:"smooth"})},style:{display:"inline-flex",alignItems:"center",gap:6,padding:"4px 12px",borderRadius:16,cursor:"pointer",fontSize:13,fontWeight:500,backgroundColor:s?"#f0fdf4":"#fef2f2",color:s?"#15803d":"#b91c1c",border:`1px solid ${s?"#bbf7d0":"#fecaca"}`},children:[s?"✓":"✗"," ",e.length," guardrail",1!==e.length?"s":""," ","evaluated",(0,t.jsx)("span",{style:{fontSize:11,opacity:.7},children:"↓"})]})})}function t$({metadata:e}){return(0,t.jsx)("div",{className:"bg-white rounded-lg shadow-sm w-full max-w-full overflow-hidden mb-6",children:(0,t.jsx)(R.Collapse,{defaultActiveKey:["1"],expandIconPosition:"start",items:[{key:"1",label:(0,t.jsx)("h3",{className:"text-lg font-medium text-gray-900",children:"Metadata"}),children:(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{style:{display:"flex",justifyContent:"flex-end",marginBottom:8},children:(0,t.jsx)(tz,{copyable:{text:JSON.stringify(e,null,2),tooltips:["Copy Metadata","Copied!"]}})}),(0,t.jsx)("pre",{style:{maxHeight:300,overflowY:"auto",fontSize:12,fontFamily:S,whiteSpace:"pre-wrap",wordBreak:"break-all",margin:0},children:JSON.stringify(e,null,2)})]})}]})})}var tW=e.i(266027),tJ=e.i(135214);function tH({row:e,isSelected:s,onClick:l}){let a=u.MCP_CALL_TYPES.includes(e.call_type),r=u.AGENT_CALL_TYPES.includes(e.call_type),n=null!=e.request_duration_ms?(e.request_duration_ms/1e3).toFixed(3):e.startTime&&e.endTime?((Date.parse(e.endTime)-Date.parse(e.startTime))/1e3).toFixed(3):"-";return(0,t.jsxs)("button",{type:"button",className:`w-full text-left pl-8 pr-2 py-1 transition-colors ${s?"bg-blue-50":"hover:bg-slate-100"}`,onClick:l,children:[(0,t.jsxs)("div",{className:"flex items-center gap-1",children:[a?(0,t.jsx)(x.Wrench,{size:12,className:"text-slate-500 shrink-0"}):r?(0,t.jsx)(c.Bot,{size:12,className:"text-slate-500 shrink-0"}):(0,t.jsx)(m.Sparkles,{size:12,className:"text-slate-500 shrink-0"}),(0,t.jsx)("span",{className:"text-xs font-medium text-slate-900 truncate",children:function(e,t){let s=(t||"").trim();if(u.MCP_CALL_TYPES.includes(e))return s.replace(/^mcp:\s*/i,"").split("/").pop()||s||"mcp_tool";let l=(s.split("/").pop()||s).replace(/-20\d{6}.*$/i,"").replace(/:.*$/,""),a=l.match(/claude-[a-z0-9-]+/i);return a?a[0]:l||"llm_call"}(e.call_type,e.model)})]}),(0,t.jsxs)("div",{className:"text-[10px] text-slate-500 mt-0 flex items-center gap-1.5 font-mono",children:[(0,t.jsxs)("span",{children:[n,"s"]}),e.spend?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{children:"·"}),(0,t.jsx)("span",{children:(0,q.getSpendString)(e.spend)})]}):null,e.total_tokens?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{children:"·"}),(0,t.jsxs)("span",{children:[e.total_tokens," tok"]})]}):null]})]})}e.s(["LogDetailsDrawer",0,function({open:e,onClose:c,logEntry:m,sessionId:x,accessToken:p,allLogs:h=[],onSelectLog:g,startTime:f}){let y=!!x,[j,v]=(0,s.useState)(null),[b,N]=(0,s.useState)("duration"),[_,w]=(0,s.useState)(!1),[k,S]=(0,s.useState)(!1),{data:C}=(0,tW.useQuery)({queryKey:["sessionLogs",x],queryFn:async()=>{if(!x||!p)return{logs:[],total:0};let e=await (0,ee.sessionSpendLogsCall)(p,x,1,100),t=e.data||e||[],s=Math.min(e.total_pages??1,50);if(s>1){let e=[];for(let t=2;t<=s;t+=5){let l=Math.min(t+5-1,s),a=await Promise.all(Array.from({length:l-t+1},(e,s)=>(0,ee.sessionSpendLogsCall)(p,x,t+s,100)));e.push(...a)}for(let s of e)t=t.concat(s.data||[])}let l=e.total??t.length;return{logs:t.map(e=>({...e,request_duration_ms:e.request_duration_ms??Date.parse(e.endTime)-Date.parse(e.startTime)})),total:l}},enabled:!!(e&&y&&x&&p)}),T=(0,s.useMemo)(()=>{var e;return e=C?.logs??[],"start_time"===b?[...e].sort((e,t)=>new Date(e.startTime).getTime()-new Date(t.startTime).getTime()):[...e].sort((e,t)=>eZ(t)-eZ(e))},[C,b]),A=C?.total??T.length,E=A>T.length,M=(0,s.useMemo)(()=>T.reduce((e,t)=>!e||new Date(t.startTime).getTime()>new Date(e.startTime).getTime()?t:e,null),[T]),I=(0,s.useMemo)(()=>{if(!y)return m;if(!T.length)return null;let e=M??T[0];return j?T.find(e=>e.request_id===j)||e:m?.request_id&&T.find(e=>e.request_id===m.request_id)||e},[y,m,j,T,M]);(0,s.useEffect)(()=>{y&&T.length&&(j&&T.some(e=>e.request_id===j)||v(m?.request_id&&T.some(e=>e.request_id===m.request_id)?m.request_id:(M??T[0]).request_id))},[y,m,j,T,M]),(0,s.useEffect)(()=>{e?w(!1):(y&&v(null),N("duration"),S(!1))},[e,y]);let{selectNextLog:z,selectPreviousLog:D}=function({isOpen:e,currentLog:t,allLogs:l,onClose:a,onSelectLog:r}){(0,s.useEffect)(()=>{let t=t=>{var s;if(!((s=t.target)instanceof HTMLInputElement||s instanceof HTMLTextAreaElement)&&e)switch(t.key){case"Escape":a();break;case"j":case"J":n();break;case"k":case"K":i()}};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[e,t,l]);let n=()=>{if(!t||!l.length||!r)return;let e=l.findIndex(e=>e.request_id===t.request_id);e{if(!t||!l.length||!r)return;let e=l.findIndex(e=>e.request_id===t.request_id);e>0&&r(l[e-1])};return{selectNextLog:n,selectPreviousLog:i}}({isOpen:e,currentLog:I,allLogs:y?T:h,onClose:c,onSelectLog:e=>{y&&v(e.request_id),g?.(e)}}),O=((e,t,s)=>{let{accessToken:l}=(0,tJ.default)();return(0,tW.useQuery)({queryKey:["logDetails",e,t,l],queryFn:async()=>l&&e&&t?await (0,ee.uiSpendLogDetailsCall)(l,e,t):null,enabled:s&&!!l&&!!e&&!!t,staleTime:6e5,gcTime:6e5})})(I?.request_id,f,e&&!!I?.request_id),B=O.data,R=O.isLoading,F=(0,s.useMemo)(()=>I?{...I,messages:B?.messages||I.messages,response:B?.response||I.response,proxy_server_request:B?.proxy_server_request||I.proxy_server_request}:null,[I,B]),P=I?.metadata||{},$="failure"===P.status?"Failure":"Success",W="failure"===P.status?"error":"success",J=P?.user_api_key_team_alias||"default",H=T.reduce((e,t)=>e+(t.spend||0),0),Y=T.length>0?new Date(Math.min(...T.map(e=>new Date(e.startTime).getTime()))):null,V=T.length>0?new Date(Math.max(...T.map(e=>new Date(e.endTime).getTime()))):null,G=Y&&V?((V.getTime()-Y.getTime())/1e3).toFixed(2):"0.00",K=T.filter(e=>!u.MCP_CALL_TYPES.includes(e.call_type)&&!u.AGENT_CALL_TYPES.includes(e.call_type)).length,U=T.filter(e=>u.AGENT_CALL_TYPES.includes(e.call_type)).length,Q=T.filter(e=>u.MCP_CALL_TYPES.includes(e.call_type)).length,X=y?T:I?[I]:[],Z=y?x||"":I?.request_id||"",et=Z.length>14?`${Z.slice(0,11)}...`:Z,es=async()=>{if(Z)try{await navigator.clipboard.writeText(Z),S(!0),setTimeout(()=>S(!1),1200)}catch{}};return I&&F?(0,t.jsx)(a.Drawer,{title:null,placement:"right",onClose:c,open:e,width:"60%",closable:!1,mask:!0,maskClosable:!0,styles:{body:{padding:0,overflow:"hidden"},header:{display:"none"}},children:(0,t.jsxs)("div",{style:{height:"100%"},className:"flex relative",children:[_?(0,t.jsx)(l.Button,{type:"text",size:"small",icon:(0,t.jsx)(d.RightOutlined,{}),onClick:()=>w(!1),className:"absolute top-2 left-2 z-20 bg-white! border! border-slate-200! rounded-md!","aria-label":"Expand trace sidebar"}):(0,t.jsx)(l.Button,{type:"text",size:"small",icon:(0,t.jsx)(o.LeftOutlined,{}),onClick:()=>w(!0),className:"absolute top-2 left-2 z-20 bg-white! border! border-slate-200! rounded-md!","aria-label":"Collapse trace sidebar"}),!_&&(0,t.jsxs)("div",{className:"border-r border-slate-200 bg-slate-50 flex flex-col",style:{width:224},children:[(0,t.jsxs)("div",{className:"pl-12 pr-3 py-2 border-b border-slate-200 bg-white",children:[(0,t.jsx)("div",{className:"flex items-start justify-between gap-2",children:(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-[10px] uppercase tracking-wide text-slate-500",children:y?"Session":"Trace"}),(0,t.jsxs)("div",{className:"font-mono text-[12px] text-slate-900 leading-tight flex items-center gap-1",children:[(0,t.jsx)("span",{className:"truncate",children:et}),(0,t.jsx)("button",{type:"button",onClick:es,className:"text-slate-400 hover:text-slate-600","aria-label":"Copy trace id",children:k?(0,t.jsx)(n.CheckOutlined,{className:"text-[11px]"}):(0,t.jsx)(i.CopyOutlined,{className:"text-[11px]"})})]})]})}),(0,t.jsxs)("div",{className:"mt-1 text-[11px] text-slate-500 font-mono",children:[X.length," req",[y?K:X.filter(e=>!u.MCP_CALL_TYPES.includes(e.call_type)&&!u.AGENT_CALL_TYPES.includes(e.call_type)).length,y?U:X.filter(e=>u.AGENT_CALL_TYPES.includes(e.call_type)).length,y?Q:X.filter(e=>u.MCP_CALL_TYPES.includes(e.call_type)).length].map((e,s)=>{let l=[" LLM"," Agent"," MCP"][s];return e>0?(0,t.jsxs)("span",{children:[(0,t.jsx)("span",{className:"mx-1.5",children:"·"}),e,l]},l):null}),(0,t.jsx)("span",{className:"mx-1.5",children:"·"}),y?(0,q.getSpendString)(H):(0,q.getSpendString)(I.spend||0),y&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"mx-1.5",children:"·"}),G,"s"]})]}),y&&E&&(0,t.jsxs)("div",{className:"mt-1 text-[11px] text-amber-600 font-mono",children:["Showing most recent ",X.length," of ",A]}),y&&(0,t.jsx)(r.Segmented,{block:!0,size:"small",className:"mt-1.5 [&_.ant-segmented-item-label]:text-[11px]",options:[{label:"Duration",value:"duration"},{label:"Start time",value:"start_time"}],value:b,onChange:e=>N(e)})]}),(0,t.jsxs)("div",{className:"flex-1 overflow-y-auto",children:[e1(P?.guardrail_information).length>0&&(0,t.jsx)("div",{className:"px-3 pt-2",children:(0,t.jsx)(tq,{guardrailEntries:e1(P?.guardrail_information)})}),y?(0,t.jsx)("div",{className:"py-1",children:(0,t.jsxs)("div",{className:"relative pl-2",children:[(0,t.jsx)("div",{className:"absolute left-4 top-1 bottom-1 border-l border-slate-300"}),X.map((e,s)=>{let l=s===X.length-1;return(0,t.jsxs)("div",{className:"relative",children:[(0,t.jsx)("div",{className:"absolute left-4 top-3 w-3 border-t border-slate-300"}),l&&(0,t.jsx)("div",{className:"absolute left-4 top-3 bottom-0 w-px bg-slate-50"}),(0,t.jsx)(tH,{row:e,isSelected:e.request_id===I.request_id,onClick:()=>{v(e.request_id),g?.(e)}})]},e.request_id)})]})}):(0,t.jsx)("div",{className:"py-1",children:X.map(e=>(0,t.jsx)(tH,{row:e,isSelected:e.request_id===I.request_id,onClick:()=>g?.(e)},e.request_id))})]})]}),(0,t.jsxs)("div",{className:"flex-1 flex flex-col overflow-hidden",children:[(0,t.jsx)(L,{log:I,onClose:c,onPrevious:D,onNext:z,statusLabel:$,statusColor:W,environment:J}),(0,t.jsx)("div",{className:"flex-1 overflow-y-auto",children:(0,t.jsx)(tD,{logEntry:F,isLoadingDetails:R,accessToken:p??null})})]})]})}):null}],502626),e.s([],3565)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0rcx~89hm.r_w.js b/litellm/proxy/_experimental/out/_next/static/chunks/0rcx~89hm.r_w.js new file mode 100644 index 00000000000..2d52692a3f8 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0rcx~89hm.r_w.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,896499,e=>{"use strict";let t;var n=e.i(271645),r=e.i(921374);let i=[];function o(e){let n=(n,o)=>{let u,a=(0,r.useRefWithInit)(s).current;try{for(let e of(t=a,i))e.before(a);for(let t of(u=e(n,o),i))t.after(a);a.didInitialize=!0}finally{t=void 0}return u};return n.displayName=e.displayName||e.name,n}function s(){return{didInitialize:!1}}e.s(["fastComponent",0,o,"fastComponentRef",0,function(e){return n.forwardRef(o(e))},"getInstance",0,function(){return t},"register",0,function(e){i.push(e)}])},574735,e=>{"use strict";e.s(["addEventListener",0,function(e,t,n,r){return e.addEventListener(t,n,r),()=>{e.removeEventListener(t,n,r)}}])},328744,e=>{"use strict";e.s([],564949),e.i(564949),e.i(247167);let{userAgent:t,platform:n,maxTouchPoints:r}="u"1,u="android",a=o===u||i.includes(u),l=!s&&o.startsWith("mac"),c=o.startsWith("win"),d=!a&&/^(linux|chrome os)/.test(o),f=l||s;e.s(["android",0,a,"apple",0,f,"ios",0,s,"linux",0,d,"mac",0,l,"windows",0,c],503720);var p=e.i(503720);let g="u">typeof CSS&&!!CSS.supports?.("-webkit-backdrop-filter:none"),m=!g&&i.includes("firefox"),h=!g&&i.includes("chrom");e.s(["blink",0,h,"gecko",0,m,"webkit",0,g],879850);var v=e.i(879850);e.s(["voiceOver",0,f],999170);var E=e.i(999170);let b=/jsdom|happydom/.test(i);e.s(["jsdom",0,b],736174);var S=e.i(736174);e.s(["engine",0,v,"env",0,S,"os",0,p,"screenReader",0,E],179214);var y=e.i(179214);e.s(["platform",0,y],328744)},157940,e=>{"use strict";var t=e.i(328744);e.s(["isClickLikeEvent",0,function(e){let t=e.type;return"click"===t||"mousedown"===t||"keydown"===t||"keyup"===t},"isMouseLikePointerType",0,function(e,t){let n=["mouse","pen"];return t||n.push("",void 0),n.includes(e)},"isReactEvent",0,function(e){return"nativeEvent"in e},"isVirtualClick",0,function(e){return""===e.pointerType&&!!e.isTrusted||(t.platform.os.android&&e.pointerType?"click"===e.type&&1===e.buttons:0===e.detail&&!e.pointerType)},"isVirtualPointerEvent",0,function(e){return!t.platform.env.jsdom&&(!t.platform.os.android&&0===e.width&&0===e.height||t.platform.os.android&&1===e.width&&1===e.height&&0===e.pressure&&0===e.detail&&"mouse"===e.pointerType||e.width<1&&e.height<1&&0===e.pressure&&0===e.detail&&"touch"===e.pointerType)},"stopEvent",0,function(e){e.preventDefault(),e.stopPropagation()}])},365420,e=>{"use strict";e.s(["mergeCleanups",0,function(...e){return()=>{for(let t=0;t{"use strict";var t=e.i(921374),n=e.i(626300);class r{static create(){return new r}currentId=0;start(e,t){this.clear(),this.currentId=setTimeout(()=>{this.currentId=0,t()},e)}isStarted(){return 0!==this.currentId}clear=()=>{0!==this.currentId&&(clearTimeout(this.currentId),this.currentId=0)};disposeEffect=()=>this.clear}e.s(["Timeout",0,r,"useTimeout",0,function(){let e=(0,t.useRefWithInit)(r.create).current;return(0,n.useOnMount)(e.disposeEffect),e}])},46420,661286,379248,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(883977),r=e.i(146376),i=e.i(921374);function o(){let e=new Map;return{emit(t,n){e.get(t)?.forEach(e=>e(n))},on(t,n){e.has(t)||e.set(t,new Set),e.get(t).add(n)},off(t,n){e.get(t)?.delete(n)}}}e.s(["createEventEmitter",0,o],661286);class s{nodesRef={current:[]};events=o();addNode(e){this.nodesRef.current.push(e)}removeNode(e){let t=this.nodesRef.current.findIndex(t=>t===e);-1!==t&&this.nodesRef.current.splice(t,1)}}e.s(["FloatingTreeStore",0,s],379248);var u=e.i(843476);let a=t.createContext(null),l=t.createContext(null),c=()=>t.useContext(a)?.id||null,d=e=>{let n=t.useContext(l);return e??n};e.s(["FloatingNode",0,function(e){let{children:n,id:r}=e,i=c();return(0,u.jsx)(a.Provider,{value:t.useMemo(()=>({id:r,parentId:i}),[r,i]),children:n})},"FloatingTree",0,function(e){let{children:t,externalTree:n}=e,r=(0,i.useRefWithInit)(()=>n??new s).current;return(0,u.jsx)(l.Provider,{value:r,children:t})},"useFloatingNodeId",0,function(e){let t=(0,n.useId)(),i=d(e),o=c();return(0,r.useIsoLayoutEffect)(()=>{if(!t)return;let e={id:t,parentId:o};return i?.addNode(e),()=>{i?.removeNode(e)}},[i,t,o]),t},"useFloatingParentNodeId",0,c,"useFloatingTree",0,d],46420)},451321,e=>{"use strict";e.s(["createAttribute",0,function(e){return`data-base-ui-${e}`}])},596296,449055,e=>{"use strict";var t=e.i(229315),n=e.i(328744);let r="data-base-ui-focusable",i="input:not([type='hidden']):not([disabled]),[contenteditable]:not([contenteditable='false']),textarea:not([disabled])";e.s(["ARROW_DOWN",0,"ArrowDown","ARROW_LEFT",0,"ArrowLeft","ARROW_RIGHT",0,"ArrowRight","ARROW_UP",0,"ArrowUp","FOCUSABLE_ATTRIBUTE",0,r,"TYPEABLE_SELECTOR",0,i],449055);var o=e.i(647554);function s(e){return(0,t.isHTMLElement)(e)&&e.matches(i)}e.s(["getFloatingFocusElement",0,function(e){return e?e.hasAttribute(r)?e:e.querySelector(`[${r}]`)||e:null},"isEventTargetWithin",0,function(e,t){return null!=t&&("composedPath"in e?e.composedPath().includes(t):null!=e.target&&t.contains(e.target))},"isInteractiveElement",0,function(e){return e?.closest(`button,a[href],[role="button"],select,[tabindex]:not([tabindex="-1"]),${i}`)!=null},"isRootElement",0,function(e){return e.matches("html,body")},"isTargetInsideEnabledTrigger",0,function(e,n){if(!(0,t.isElement)(e))return!1;if(n.hasElement(e))return!e.hasAttribute("data-trigger-disabled");for(let[,t]of n.entries())if((0,o.contains)(t,e))return!t.hasAttribute("data-trigger-disabled");return!1},"isTypeableCombobox",0,function(e){return!!e&&"combobox"===e.getAttribute("role")&&s(e)},"isTypeableElement",0,s,"matchesFocusVisible",0,function(e){if(!e||n.platform.env.jsdom)return!0;try{return e.matches(":focus-visible")}catch(e){return!0}}],596296)},958408,e=>{"use strict";e.s(["getNodeAncestors",0,function(e,t){let n=[],r=e.find(e=>e.id===t)?.parentId;for(;r;){let t=e.find(e=>e.id===r);r=t?.parentId,t&&(n=n.concat(t))}return n},"getNodeChildren",0,function e(t,n,r=!0){return t.filter(e=>e.parentId===n).flatMap(n=>[...!r||n.context?.open?[n]:[],...e(t,n.id,r)])}])},17989,e=>{"use strict";var t=e.i(271645),n=e.i(574735),r=e.i(365420),i=e.i(108868),o=e.i(667865),s=e.i(439957),u=e.i(229315),a=e.i(328744),l=e.i(46420),c=e.i(675606),d=e.i(56434),f=e.i(451321),p=e.i(647554),g=e.i(596296),m=e.i(157940),h=e.i(958408);function v(){return!1}e.s(["useDismiss",0,function(e,E={}){let{enabled:b=!0,escapeKey:S=!0,outsidePress:y=!0,outsidePressEvent:T="sloppy",referencePress:C=v,bubbles:O,externalTree:R}=E,I="rootStore"in e?e.rootStore:e,x=I.useState("open"),P=I.useState("floatingElement"),{dataRef:w}=I.context,A=(0,l.useFloatingTree)(R),L=(0,o.useStableCallback)("function"==typeof y?y:()=>!1),k="function"==typeof y?L:y,N=!1!==k,M=(0,o.useStableCallback)(()=>T),{escapeKey:D,outsidePress:F}={escapeKey:"boolean"==typeof O?O:O?.escapeKey??!1,outsidePress:"boolean"==typeof O?O:O?.outsidePress??!0},_=t.useRef(!1),H=t.useRef(!1),B=t.useRef(!1),W=t.useRef(!1),j=t.useRef(""),U=t.useRef(null),V=(0,s.useTimeout)(),Y=(0,s.useTimeout)(),K=(0,o.useStableCallback)(()=>{Y.clear(),w.current.insideReactTree=!1}),z=(0,o.useStableCallback)(e=>{let t=w.current.floatingContext?.nodeId;return(A?(0,h.getNodeChildren)(A.nodesRef.current,t):[]).some(t=>t.context?.open&&!t.context.dataRef.current[e])}),J=(0,o.useStableCallback)(e=>(0,g.isEventTargetWithin)(e,I.select("floatingElement"))||(0,g.isEventTargetWithin)(e,I.select("domReferenceElement"))),X=(0,o.useStableCallback)(e=>{C()&&I.setOpen(!1,(0,c.createChangeEventDetails)(d.REASONS.triggerPress,e.nativeEvent))}),G=(0,o.useStableCallback)(e=>{if(!x||!b||!S||"Escape"!==e.key||W.current||!D&&z("__escapeKeyBubbles"))return;let t=(0,m.isReactEvent)(e)?e.nativeEvent:e,n=(0,c.createChangeEventDetails)(d.REASONS.escapeKey,t);I.setOpen(!1,n),n.isCanceled||e.preventDefault(),D||n.isPropagationAllowed||e.stopPropagation()}),$=(0,o.useStableCallback)(()=>{w.current.insideReactTree=!0,Y.start(0,K)}),q=(0,o.useStableCallback)(e=>{if(!x||!b||0!==e.button)return;let t=(0,p.getTarget)(e.nativeEvent);(0,p.contains)(I.select("floatingElement"),t)&&(_.current||(_.current=!0,H.current=!1))}),Q=(0,o.useStableCallback)(e=>{!x||!b||(e.defaultPrevented||e.nativeEvent.defaultPrevented)&&_.current&&(H.current=!0)});t.useEffect(()=>{if(!x||!b)return;w.current.__escapeKeyBubbles=D,w.current.__outsidePressBubbles=F;let e=new s.Timeout,t=new s.Timeout;function o(){B.current=!0,t.start(0,()=>{B.current=!1})}function l(){_.current=!1,H.current=!1}function m(){let e=j.current,t=M(),n="function"==typeof t?t():t;return"string"==typeof n?n:n["pen"!==e&&e?e:"mouse"]}function v(e){let t=w.current.floatingContext?.nodeId,n=A&&(0,h.getNodeChildren)(A.nodesRef.current,t).some(t=>(0,g.isEventTargetWithin)(e,t.context?.elements.floating));return J(e)||n}function E(e){let n;if("intentional"===(n=m())&&"click"!==e.type||"sloppy"===n&&"click"===e.type){"click"===e.type||J(e)||(t.clear(),B.current=!1),K();return}if(w.current.insideReactTree)return void K();let r=(0,p.getTarget)(e),o=`[${(0,f.createAttribute)("inert")}]`,s=(0,u.isElement)(r)?r.getRootNode():null,a=Array.from(((0,u.isShadowRoot)(s)?s:(0,i.ownerDocument)(I.select("floatingElement"))).querySelectorAll(o)),l=I.context.triggerElements;if(r&&(l.hasElement(r)||l.hasMatchingElement(e=>(0,p.contains)(e,r))))return;let h=(0,u.isElement)(r)?r:null;for(;h&&!(0,u.isLastTraversableNode)(h);){let e=(0,u.getParentNode)(h);if((0,u.isLastTraversableNode)(e)||!(0,u.isElement)(e))break;h=e}if(!(a.length&&(0,u.isElement)(r)&&!(0,g.isRootElement)(r)&&!(0,p.contains)(r,I.select("floatingElement"))&&a.every(e=>!(0,p.contains)(h,e)))){if((0,u.isHTMLElement)(r)&&!("touches"in e)){let t=(0,u.isLastTraversableNode)(r),n=(0,u.getComputedStyle)(r),i=/auto|scroll/,o=t||i.test(n.overflowX),s=t||i.test(n.overflowY),a=o&&r.clientWidth>0&&r.scrollWidth>r.clientWidth,l=s&&r.clientHeight>0&&r.scrollHeight>r.clientHeight,c="rtl"===n.direction,d=l&&(c?e.offsetX<=r.offsetWidth-r.clientWidth:e.offsetX>r.clientWidth),f=a&&e.offsetY>r.clientHeight;if(d||f)return}if(!v(e)){if("intentional"===m()&&B.current){t.clear(),B.current=!1;return}"function"==typeof k&&!k(e)||z("__outsidePressBubbles")||(I.setOpen(!1,(0,c.createChangeEventDetails)(d.REASONS.outsidePress,e)),K())}}}function y(e){if("sloppy"!==m()||!I.select("open")||!b||J(e))return;let t=e.touches[0];t&&(U.current={startTime:Date.now(),startX:t.clientX,startY:t.clientY,dismissOnTouchEnd:!1,dismissOnMouseDown:!0},V.start(1e3,()=>{U.current&&(U.current.dismissOnTouchEnd=!1,U.current.dismissOnMouseDown=!1)}))}function T(e,t){let r=(0,p.getTarget)(e);if(!r)return;let i=(0,n.addEventListener)(r,e.type,()=>{t(e),i()})}function C(e){V.clear(),"pointerdown"===e.type&&(j.current=e.pointerType),("mousedown"!==e.type||!U.current||U.current.dismissOnMouseDown)&&T(e,e=>{if("pointerdown"===e.type)"sloppy"!==m()||"touch"===e.pointerType||!I.select("open")||!b||J(e)||E(e);else E(e)})}function O(e){if(!_.current)return;let n=H.current;if(l(),"intentional"===m()){if("pointercancel"===e.type){n&&o();return}v(e)||(n?o():("function"!=typeof k||k(e))&&(t.clear(),B.current=!0,K()))}}function R(e){if("sloppy"!==m()||!U.current||J(e))return;let t=e.touches[0];if(!t)return;let n=Math.abs(t.clientX-U.current.startX),r=Math.abs(t.clientY-U.current.startY),i=Math.sqrt(n*n+r*r);i>5&&(U.current.dismissOnTouchEnd=!0),i>10&&(E(e),V.clear(),U.current=null)}function L(e){"sloppy"!==m()||!U.current||J(e)||(U.current.dismissOnTouchEnd&&E(e),V.clear(),U.current=null)}let Y=(0,i.ownerDocument)(P),X=(0,r.mergeCleanups)(S&&(0,r.mergeCleanups)((0,n.addEventListener)(Y,"keydown",G),(0,n.addEventListener)(Y,"compositionstart",function(){e.clear(),W.current=!0}),(0,n.addEventListener)(Y,"compositionend",function(){e.start(5*!!a.platform.engine.webkit,()=>{W.current=!1})})),N&&(0,r.mergeCleanups)((0,n.addEventListener)(Y,"click",C,!0),(0,n.addEventListener)(Y,"pointerdown",C,!0),(0,n.addEventListener)(Y,"pointerup",O,!0),(0,n.addEventListener)(Y,"pointercancel",O,!0),(0,n.addEventListener)(Y,"mousedown",C,!0),(0,n.addEventListener)(Y,"mouseup",O,!0),(0,n.addEventListener)(Y,"touchstart",function(e){j.current="touch",T(e,y)},!0),(0,n.addEventListener)(Y,"touchmove",function(e){T(e,R)},!0),(0,n.addEventListener)(Y,"touchend",function(e){T(e,L)},!0)));return()=>{X(),e.clear(),t.clear(),l(),B.current=!1}},[w,P,S,N,k,x,b,D,F,G,K,M,z,J,A,I,V]),t.useEffect(K,[k,K]);let Z=t.useMemo(()=>({onKeyDown:G,onPointerDown:X,onClick:X}),[G,X]),ee=t.useMemo(()=>({onKeyDown:G,onPointerDown:Q,onMouseDown:Q,onClickCapture:$,onMouseDownCapture(e){$(),q(e)},onPointerDownCapture(e){$(),q(e)},onMouseUpCapture:$,onTouchEndCapture:$,onTouchMoveCapture:$}),[G,$,q,Q]);return t.useMemo(()=>b?{reference:Z,floating:ee,trigger:Z}:{},[b,Z,ee])}])},713203,e=>{"use strict";var t=e.i(271645);e.s(["useOnFirstRender",0,function(e){let n=t.useRef(!0);n.current&&(n.current=!1,e())}])},616269,e=>{"use strict";var t=e.i(733332);e.s(["createSelector",0,(e,n,r,i,o,s,...u)=>{let a;if(u.length>0)throw Error((0,t.default)(1));if(e&&n&&r&&i&&o&&s)a=(t,u,a,l)=>s(e(t,u,a,l),n(t,u,a,l),r(t,u,a,l),i(t,u,a,l),o(t,u,a,l),u,a,l);else if(e&&n&&r&&i&&o)a=(t,s,u,a)=>o(e(t,s,u,a),n(t,s,u,a),r(t,s,u,a),i(t,s,u,a),s,u,a);else if(e&&n&&r&&i)a=(t,o,s,u)=>i(e(t,o,s,u),n(t,o,s,u),r(t,o,s,u),o,s,u);else if(e&&n&&r)a=(t,i,o,s)=>r(e(t,i,o,s),n(t,i,o,s),i,o,s);else if(e&&n)a=(t,r,i,o)=>n(e(t,r,i,o),r,i,o);else if(e)a=e;else throw Error("Missing arguments");return a}])},714935,334346,e=>{"use strict";var t=e.i(271645),n=e.i(802239),r=e.i(430224),i=e.i(958321),o=e.i(896499);let s=(0,i.isReactVersionAtLeast)(19)?function(e,r,i,s,u){let a,l=(0,o.getInstance)();if(!l){let o;return o=t.useCallback(()=>r(e.getSnapshot(),i,s,u),[e,r,i,s,u]),(0,n.useSyncExternalStore)(e.subscribe,o,o)}let c=l.syncIndex;return l.syncIndex+=1,l.didInitialize?(a=l.syncHooks[c]).store===e&&a.selector===r&&Object.is(a.a1,i)&&Object.is(a.a2,s)&&Object.is(a.a3,u)||(a.store!==e&&(l.didChangeStore=!0),a.store=e,a.selector=r,a.a1=i,a.a2=s,a.a3=u,a.value=r(e.getSnapshot(),i,s,u)):(a={store:e,selector:r,a1:i,a2:s,a3:u,value:r(e.getSnapshot(),i,s,u)},l.syncHooks.push(a)),a.value}:function(e,t,n,i,o){return(0,r.useSyncExternalStoreWithSelector)(e.subscribe,e.getSnapshot,e.getSnapshot,e=>t(e,n,i,o))};function u(e,t,n,r,i){return s(e,t,n,r,i)}(0,o.register)({before(e){e.syncIndex=0,e.didInitialize||(e.syncTick=1,e.syncHooks=[],e.didChangeStore=!0,e.getSnapshot=()=>{let t=!1;for(let n=0;n0&&(e.didChangeStore&&(e.didChangeStore=!1,e.subscribe=t=>{let n=new Set;for(let t of e.syncHooks)n.add(t.store);let r=[];for(let e of n)r.push(e.subscribe(t));return()=>{for(let e of r)e()}}),(0,n.useSyncExternalStore)(e.subscribe,e.getSnapshot,e.getSnapshot))}}),e.s(["useStore",0,u],334346),e.s(["Store",0,class{constructor(e){this.state=e,this.listeners=new Set,this.updateTick=0}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});getSnapshot=()=>this.state;setState(e){if(this.state===e)return;this.state=e,this.updateTick+=1;let t=this.updateTick;for(let n of this.listeners){if(t!==this.updateTick)return;n(e)}}update(e){for(let t in e)if(!Object.is(this.state[t],e[t]))return void this.setState({...this.state,...e})}set(e,t){Object.is(this.state[e],t)||this.setState({...this.state,[e]:t})}notifyAll(){let e={...this.state};this.setState(e)}use(e,t,n,r){return u(this,e,t,n,r)}}],714935)},301252,e=>{"use strict";var t=e.i(271645),n=e.i(714935),r=e.i(334346),i=e.i(667865),o=e.i(146376),s=e.i(956789);class u extends n.Store{constructor(e,t={},n){super(e),this.context=t,this.selectors=n}useSyncedValue(e,n){t.useDebugValue(e);let r=this;(0,o.useIsoLayoutEffect)(()=>{r.state[e]!==n&&r.set(e,n)},[r,e,n])}useSyncedValueWithCleanup(e,t){let n=this;(0,o.useIsoLayoutEffect)(()=>(n.state[e]!==t&&n.set(e,t),()=>{n.set(e,void 0)}),[n,e,t])}useSyncedValues(e){let t=this,n=Object.values(e);(0,o.useIsoLayoutEffect)(()=>{t.update(e)},[t,...n])}useControlledProp(e,n){t.useDebugValue(e);let r=this,i=void 0!==n;(0,o.useIsoLayoutEffect)(()=>{i&&!Object.is(r.state[e],n)&&r.setState({...r.state,[e]:n})},[r,e,n,i])}select(e,t,n,r){return(0,this.selectors[e])(this.state,t,n,r)}useState(e,n,i,o){return t.useDebugValue(e),(0,r.useStore)(this,this.selectors[e],n,i,o)}useContextCallback(e,n){t.useDebugValue(e);let r=(0,i.useStableCallback)(n??s.NOOP);this.context[e]=r}useStateSetter(e){let n=t.useRef(void 0);return void 0===n.current&&(n.current=t=>{this.set(e,t)}),n.current}observe(e,t){let n,r=(n="function"==typeof e?e:this.selectors[e])(this.state);return t(r,r,this),this.subscribe(e=>{let i=n(e);if(!Object.is(r,i)){let e=r;r=i,t(i,e,this)}})}}e.s(["ReactStore",0,u])},264111,156341,350527,e=>{"use strict";var t=e.i(271645),n=e.i(174080),r=e.i(956789),i=e.i(883977),o=e.i(667865),s=e.i(146376),u=e.i(713203),a=e.i(449055),l=e.i(46420),c=e.i(229315),d=e.i(616269),f=e.i(301252),p=e.i(661286),g=e.i(157940);let m={open:(0,d.createSelector)(e=>e.open),transitionStatus:(0,d.createSelector)(e=>e.transitionStatus),domReferenceElement:(0,d.createSelector)(e=>e.domReferenceElement),referenceElement:(0,d.createSelector)(e=>e.positionReference??e.referenceElement),floatingElement:(0,d.createSelector)(e=>e.floatingElement),floatingId:(0,d.createSelector)(e=>e.floatingId)};class h extends f.ReactStore{constructor(e){const{syncOnly:t,nested:n,onOpenChange:r,triggerElements:i,...o}=e;super({...o,positionReference:o.referenceElement,domReferenceElement:o.referenceElement},{onOpenChange:r,dataRef:{current:{}},events:(0,p.createEventEmitter)(),nested:n,triggerElements:i},m),this.syncOnly=t}syncOpenEvent=(e,t)=>{(!e||!this.state.open||null!=t&&(0,g.isClickLikeEvent)(t))&&(this.context.dataRef.current.openEvent=e?t:void 0)};dispatchOpenChange=(e,t)=>{this.syncOpenEvent(e,t.event);let n={open:e,reason:t.reason,nativeEvent:t.event,nested:this.context.nested,triggerElement:t.trigger};this.context.events.emit("openchange",n)};setOpen=(e,t)=>{this.syncOnly||this.dispatchOpenChange(e,t),this.context.onOpenChange?.(e,t)}}function v(e){let{popupStore:n,treatPopupAsFloatingElement:r=!1,floatingRootContext:i,floatingId:o,nested:u,onOpenChange:a}=e,l=n.useState("open"),d=n.useState("activeTriggerElement"),f=n.useState(r?"popupElement":"positionerElement"),p=n.context.triggerElements,g=t.useRef(null);void 0===i&&null===g.current&&(g.current=new h({open:l,transitionStatus:void 0,referenceElement:d,floatingElement:f,triggerElements:p,onOpenChange:a,floatingId:o,syncOnly:!0,nested:u}));let m=i??g.current;return n.useSyncedValue("floatingId",o),(0,s.useIsoLayoutEffect)(()=>{let e={open:l,floatingId:o,referenceElement:d,floatingElement:f};(0,c.isElement)(d)&&(e.domReferenceElement=d),m.state.positionReference===m.state.referenceElement&&(e.positionReference=d),m.update(e)},[l,o,d,f,m]),m.context.onOpenChange=a,m.context.nested=u,m}e.s(["FloatingRootStore",0,h],156341),e.s(["useSyncedFloatingRootContext",0,v],350527);var E=e.i(223910),b=e.i(137584),S=e.i(675606),y=e.i(56434);let T={tabIndex:-1,[a.FOCUSABLE_ATTRIBUTE]:""};function C(e,n){let r=t.useRef(null),i=t.useRef(null);return t.useCallback(t=>{if(void 0===e)return;let o=!1;if(null!==r.current){let e=r.current,t=i.current,s=n.context.triggerElements.getById(e);t&&s===t&&(n.context.triggerElements.delete(e),o=!0),r.current=null,i.current=null}if(null!==t&&(r.current=e,i.current=t,n.context.triggerElements.add(e,t),o=!0),o){let e=n.context.triggerElements.size;n.select("open")&&n.state.triggerCount!==e&&n.set("triggerCount",e)}},[n,e])}function O(e,t,n,r=!1){t?e.preventUnmountingOnClose=!1:r&&(e.preventUnmountingOnClose=!0);let i=n?.id??null;(i||t)&&(e.activeTriggerId=i,e.activeTriggerElement=n??null)}function R(e){let t=!1;return e.preventUnmountOnClose=()=>{t=!0},()=>t}e.s(["FOCUSABLE_POPUP_PROPS",0,T,"applyPopupOpenChange",0,function(e,t,r,i={}){let o=r.reason,s=o===y.REASONS.triggerHover,u=t&&o===y.REASONS.triggerFocus,a=!t&&(o===y.REASONS.triggerPress||o===y.REASONS.escapeKey),l=R(r);if(e.context.onOpenChange?.(t,r),r.isCanceled)return;i.onBeforeDispatch?.(),e.state.floatingRootContext.dispatchOpenChange(t,r);let c=()=>{let n={...i.extraState,open:t};u?n.instantType="focus":a?n.instantType="dismiss":s&&(n.instantType=void 0),O(n,t,r.trigger,l()),e.update(n)};s?n.flushSync(c):c()},"attachPreventUnmountOnClose",0,R,"createDefaultInitialFocus",0,function(e){return t=>"touch"!==t||e.current},"setPopupOpenState",0,O,"useImplicitActiveTrigger",0,function(e,t={}){let{closeOnActiveTriggerUnmount:n=!1}=t,r=e.useState("open"),i=e.useState("triggerCount");(0,s.useIsoLayoutEffect)(()=>{if(!r){0!==e.state.triggerCount&&e.set("triggerCount",0);return}let t=e.context.triggerElements.size,i={};e.state.triggerCount!==t&&(i.triggerCount=t);let o=e.select("activeTriggerId"),s=null;if(o){let t=e.context.triggerElements.getById(o);t?t!==e.state.activeTriggerElement&&(i.activeTriggerElement=t):s=o}if(!s&&!o&&1===t){let t=e.context.triggerElements.entries().next();if(!t.done){let[e,n]=t.value;i.activeTriggerId=e,i.activeTriggerElement=n}}(void 0!==i.triggerCount||void 0!==i.activeTriggerId||void 0!==i.activeTriggerElement)&&e.update(i),s&&n&&queueMicrotask(()=>{if(e.select("open")&&e.select("activeTriggerId")===s&&!e.context.triggerElements.getById(s)){let t=(0,S.createChangeEventDetails)(y.REASONS.none);e.setOpen(!1,t),t.isCanceled||e.update({activeTriggerId:null,activeTriggerElement:null})}})},[r,e,i,n])},"useInitialOpenSync",0,function(e,t,n,r){(0,u.useOnFirstRender)(()=>{void 0===t&&!1===e.state.open&&n&&(e.state={...e.state,open:!0,activeTriggerId:r,preventUnmountingOnClose:!1})})},"useOpenStateTransitions",0,function(e,t,n){let{mounted:r,setMounted:i,transitionStatus:s}=(0,E.useTransitionStatus)(e),u=t.useState("preventUnmountingOnClose"),a=!e&&u;t.useSyncedValues({mounted:r,transitionStatus:s,preventUnmountingOnClose:a});let l=(0,o.useStableCallback)(()=>{i(!1),t.update({activeTriggerId:null,activeTriggerElement:null,mounted:!1,preventUnmountingOnClose:!1}),n?.(),t.context.onOpenChangeComplete?.(!1)});return(0,b.useOpenChangeComplete)({enabled:r&&!e&&!a,open:e,ref:t.context.popupRef,onComplete(){e||l()}}),{forceUnmount:l,transitionStatus:s}},"usePopupInteractionProps",0,function(e,t){e.useSyncedValues(t),(0,s.useIsoLayoutEffect)(()=>()=>{e.update({activeTriggerProps:r.EMPTY_OBJECT,inactiveTriggerProps:r.EMPTY_OBJECT,popupProps:r.EMPTY_OBJECT})},[e])},"usePopupRootSync",0,function(e,t){(0,s.useIsoLayoutEffect)(()=>{t||null===e.state.openMethod||e.set("openMethod",null)},[t,e]),(0,s.useIsoLayoutEffect)(()=>()=>{null!==e.state.openMethod&&e.set("openMethod",null)},[e])},"usePopupStore",0,function(e,n,r=!1){let o=(0,i.useId)(),s=null!=(0,l.useFloatingParentNodeId)(),u=t.useRef(null);void 0===e&&null===u.current&&(u.current=n(o,s));let a=e??u.current;return v({popupStore:a,treatPopupAsFloatingElement:r,floatingRootContext:a.state.floatingRootContext,floatingId:o,nested:s,onOpenChange:a.setOpen}),{store:a,internalStore:u.current}},"useTriggerDataForwarding",0,function(e,t,n,r){let i=n.useState("isMountedByTrigger",e),u=C(e,n),a=(0,o.useStableCallback)(t=>{if(u(t),!t)return;let i=n.select("open"),o=n.select("activeTriggerId");o===e?n.update({activeTriggerElement:t,...i?r:null}):null==o&&i&&n.update({activeTriggerId:e,activeTriggerElement:t,...r})});return(0,s.useIsoLayoutEffect)(()=>{i&&n.update({activeTriggerElement:t.current,...r})},[i,n,t,...Object.values(r)]),{registerTrigger:a,isMountedByThisTrigger:i}},"useTriggerRegistration",0,C],264111)},990627,e=>{"use strict";e.s(["PopupTriggerMap",0,class{constructor(){this.elementsSet=new Set,this.idMap=new Map}add(e,t){let n=this.idMap.get(e);n!==t&&(void 0!==n&&this.elementsSet.delete(n),this.elementsSet.add(t),this.idMap.set(e,t))}delete(e){let t=this.idMap.get(e);t&&(this.elementsSet.delete(t),this.idMap.delete(e))}hasElement(e){return this.elementsSet.has(e)}hasMatchingElement(e){for(let t of this.elementsSet)if(e(t))return!0;return!1}getById(e){return this.idMap.get(e)}entries(){return this.idMap.entries()}elements(){return this.elementsSet.values()}get size(){return this.idMap.size}}])},116786,e=>{"use strict";var t=e.i(616269),n=e.i(956789),r=e.i(156341),i=e.i(990627);let o=(0,t.createSelector)(e=>e.triggerIdProp??e.activeTriggerId),s=(0,t.createSelector)(e=>e.openProp??e.open),u=(0,t.createSelector)(e=>(e.popupElement?.id??e.floatingId)||void 0);function a(e,t){return void 0!==t&&s(e)&&o(e)===t}let l={open:s,mounted:(0,t.createSelector)(e=>e.mounted),transitionStatus:(0,t.createSelector)(e=>e.transitionStatus),floatingRootContext:(0,t.createSelector)(e=>e.floatingRootContext),triggerCount:(0,t.createSelector)(e=>e.triggerCount),preventUnmountingOnClose:(0,t.createSelector)(e=>e.preventUnmountingOnClose),payload:(0,t.createSelector)(e=>e.payload),activeTriggerId:o,activeTriggerElement:(0,t.createSelector)(e=>e.mounted?e.activeTriggerElement:null),popupId:u,isTriggerActive:(0,t.createSelector)((e,t)=>void 0!==t&&o(e)===t),isOpenedByTrigger:(0,t.createSelector)((e,t)=>a(e,t)),isMountedByTrigger:(0,t.createSelector)((e,t)=>void 0!==t&&o(e)===t&&e.mounted),triggerProps:(0,t.createSelector)((e,t)=>t?e.activeTriggerProps:e.inactiveTriggerProps),triggerPopupId:(0,t.createSelector)((e,t)=>a(e,t)||void 0!==t&&s(e)&&null==o(e)&&1===e.triggerCount?u(e):void 0),popupProps:(0,t.createSelector)(e=>e.popupProps),popupElement:(0,t.createSelector)(e=>e.popupElement),positionerElement:(0,t.createSelector)(e=>e.positionerElement)};e.s(["createInitialPopupStoreState",0,function(){return{open:!1,openProp:void 0,mounted:!1,transitionStatus:void 0,floatingRootContext:new r.FloatingRootStore({open:!1,transitionStatus:void 0,floatingElement:null,referenceElement:null,triggerElements:new i.PopupTriggerMap,floatingId:void 0,syncOnly:!1,nested:!1,onOpenChange:void 0}),floatingId:void 0,triggerCount:0,preventUnmountingOnClose:!1,payload:void 0,activeTriggerId:null,activeTriggerElement:null,triggerIdProp:void 0,popupElement:null,positionerElement:null,activeTriggerProps:n.EMPTY_OBJECT,inactiveTriggerProps:n.EMPTY_OBJECT,popupProps:n.EMPTY_OBJECT}},"createPopupFloatingRootContext",0,function(e,t,n=!1){return new r.FloatingRootStore({open:!1,transitionStatus:void 0,floatingElement:null,referenceElement:null,triggerElements:e,floatingId:t,syncOnly:!0,nested:n,onOpenChange:void 0})},"popupStoreSelectors",0,l],116786)},446265,e=>{"use strict";var t=e.i(146376),n=e.i(921374);function r(e){let t={current:e,next:e,effect:()=>{t.current=t.next}};return t}e.s(["useValueAsRef",0,function(e){let i=(0,n.useRefWithInit)(r,e).current;return i.next=e,(0,t.useIsoLayoutEffect)(i.effect),i}])},405005,e=>{"use strict";var t,n,r=e.i(209407);let i=((t={}).open="data-open",t.closed="data-closed",t[t.startingStyle=r.TransitionStatusDataAttributes.startingStyle]="startingStyle",t[t.endingStyle=r.TransitionStatusDataAttributes.endingStyle]="endingStyle",t.anchorHidden="data-anchor-hidden",t.side="data-side",t.align="data-align",t),o=((n={}).popupOpen="data-popup-open",n.pressed="data-pressed",n),s={[o.popupOpen]:""},u={[o.popupOpen]:"",[o.pressed]:""},a={[i.open]:""},l={[i.closed]:""},c={[i.anchorHidden]:""};e.s(["CommonPopupDataAttributes",0,i,"CommonTriggerDataAttributes",0,o,"popupStateMapping",0,{open:e=>e?a:l,anchorHidden:e=>e?c:null},"pressableTriggerOpenStateMapping",0,{open:e=>e?u:null},"triggerOpenStateMapping",0,{open:e=>e?s:null}])},502077,e=>{"use strict";let t={clipPath:"inset(50%)",overflow:"hidden",whiteSpace:"nowrap",border:0,padding:0,width:1,height:1,margin:-1},n={...t,position:"fixed",top:0,left:0},r={...t,position:"absolute"};e.s(["visuallyHidden",0,n,"visuallyHiddenInput",0,r])},152535,e=>{"use strict";var t=e.i(271645),n=e.i(146376),r=e.i(328744),i=e.i(502077),o=e.i(843476);let s=t.forwardRef(function(e,s){let[u,a]=t.useState();return(0,n.useIsoLayoutEffect)(()=>{r.platform.screenReader.voiceOver&&r.platform.engine.webkit&&a("button")},[]),(0,o.jsx)("span",{...e,ref:s,style:i.visuallyHidden,"aria-hidden":!u||void 0,...{tabIndex:0,role:u},"data-base-ui-focus-guard":""})});e.s(["FocusGuard",0,s])},383976,e=>{"use strict";var t=e.i(229315),n=e.i(108868),r=e.i(647554),i=e.i(621082);function o(e){for(let n of Array.from(e.children))if("summary"===(0,t.getNodeName)(n))return n;return null}function s(e){let n=e?(0,t.getNodeName)(e):"";return null!=e&&e.matches('a[href],button,input,select,textarea,summary,details,iframe,object,embed,[tabindex],[contenteditable]:not([contenteditable="false"]),audio[controls],video[controls]')&&("summary"!==n||null!=e.parentElement&&"details"===(0,t.getNodeName)(e.parentElement)&&o(e.parentElement)===e)&&("details"!==n||null==o(e))&&("input"!==n||"hidden"!==e.type)}function u(e){if(!s(e)||!e.isConnected||e.matches(":disabled"))return!1;for(let n=e;n;n=function(e){let n=e.assignedSlot;if(n)return n;if(e.parentElement)return e.parentElement;let r=e.getRootNode();return(0,t.isShadowRoot)(r)?r.host:null}(n)){let s=n!==e,u="slot"===(0,t.getNodeName)(n);if(n.hasAttribute("inert")||s&&"details"===(0,t.getNodeName)(n)&&!n.open&&!function(e,t){let n=o(t);return!!n&&(e===n||(0,r.contains)(n,e))}(e,n)||n.hasAttribute("hidden")||!u&&!function(e,n){let r=(0,t.getComputedStyle)(e);return n?"none"!==r.display:(0,i.isElementVisible)(e,r)}(n,s))return!1}return!0}function a(e){let n=e.tabIndex;if(n<0){let n=(0,t.getNodeName)(e);if("details"===n||"audio"===n||"video"===n||(0,t.isHTMLElement)(e)&&e.isContentEditable)return 0}return n}function l(e){return"input"!==(0,t.getNodeName)(e)?null:"radio"===e.type&&""!==e.name?e:null}function c(e){if((0,t.isHTMLElement)(e)&&"slot"===(0,t.getNodeName)(e)){let t=e.assignedElements({flatten:!0});if(t.length>0)return t}return(0,t.isHTMLElement)(e)&&e.shadowRoot?Array.from(e.shadowRoot.children):Array.from(e.children)}function d(e){let t=[];return!function e(t,n){c(t).forEach(t=>{s(t)&&n.push(t),e(t,n)})}(e,t),t.filter(u)}function f(e){let t=d(e);return t.filter(e=>a(e)>=0&&function(e,t){let n=l(e);if(!n)return!0;let r=t.find(e=>{let t=l(e);return t?.name===n.name&&t.form===n.form&&t.checked});return r?r===n:t.find(e=>{let t=l(e);return t?.name===n.name&&t.form===n.form})===n}(e,t))}function p(e,t){let i=f(e),o=i.length;if(0===o)return;let s=(0,r.activeElement)((0,n.ownerDocument)(e)),u=i.indexOf(s);return i[-1===u?1===t?0:o-1:u+t]}function g(e,t){if(!e)return null;let r=f((0,n.ownerDocument)(e).body),i=r.length;if(0===i)return null;let o=r.indexOf(e);return -1===o?null:r[(o+t+i)%i]}e.s(["disableFocusInside",0,function(e){f(e).forEach(e=>{e.dataset.tabindex=e.getAttribute("tabindex")||"",e.setAttribute("tabindex","-1")})},"enableFocusInside",0,function(e){let n=[];!function e(n,r,i){c(n).forEach(n=>{(0,t.isHTMLElement)(n)&&n.matches(r)&&i.push(n),e(n,r,i)})}(e,"[data-tabindex]",n),n.forEach(e=>{let t=e.dataset.tabindex;delete e.dataset.tabindex,t?e.setAttribute("tabindex",t):e.removeAttribute("tabindex")})},"focusable",0,d,"getNextTabbable",0,function(e){return p((0,n.ownerDocument)(e).body,1)||e},"getPreviousTabbable",0,function(e){return p((0,n.ownerDocument)(e).body,-1)||e},"getTabbableAfterElement",0,function(e){return g(e,1)},"getTabbableBeforeElement",0,function(e){return g(e,-1)},"isOutsideEvent",0,function(e,t){let n=t||e.currentTarget,i=e.relatedTarget;return!i||!(0,r.contains)(n,i)},"isTabbable",0,function(e){return u(e)&&a(e)>=0},"tabbable",0,f])},638396,e=>{"use strict";e.s(["CLICK_TRIGGER_IDENTIFIER",0,"data-base-ui-click-trigger","DISABLED_TRANSITIONS_STYLE",0,{style:{transition:"none"}},"DROPDOWN_COLLISION_AVOIDANCE",0,{fallbackAxisSide:"none"},"PATIENT_CLICK_THRESHOLD",0,500,"POPUP_COLLISION_AVOIDANCE",0,{fallbackAxisSide:"end"},"TYPEAHEAD_RESET_MS",0,500,"ownerVisuallyHidden",0,{clipPath:"inset(50%)",position:"fixed",top:0,left:0}])},726674,e=>{"use strict";var t=e.i(271645),n=e.i(174080),r=e.i(229315),i=e.i(574735),o=e.i(365420),s=e.i(883977),u=e.i(146376),a=e.i(667865),l=e.i(956789),c=e.i(152535),d=e.i(383976),f=e.i(675606),p=e.i(56434),g=e.i(451321),m=e.i(552245),h=e.i(638396),v=e.i(843476);let E=t.createContext(null),b=()=>t.useContext(E),S=(0,g.createAttribute)("portal");function y(e={}){let{ref:i,container:o,componentProps:c=l.EMPTY_OBJECT,elementProps:d}=e,f=(0,s.useId)(),p=b(),g=p?.portalNode,[h,v]=t.useState(null),[E,T]=t.useState(null),C=(0,a.useStableCallback)(e=>{null!==e&&T(e)}),O=t.useRef(null);(0,u.useIsoLayoutEffect)(()=>{if(null===o){O.current&&(O.current=null,T(null),v(null));return}if(null==f)return;let e=(o&&((0,r.isNode)(o)?o:o.current))??g??document.body;if(null==e){O.current&&(O.current=null,T(null),v(null));return}O.current!==e&&(O.current=e,T(null),v(e))},[o,g,f]);let R=(0,m.useRenderElement)("div",c,{ref:[i,C],props:[{id:f,[S]:""},d]});return{portalNode:E,portalSubtree:h&&R?n.createPortal(R,h):null}}let T=t.forwardRef(function(e,r){let{render:s,className:a,style:l,children:g,container:m,renderGuards:b,...S}=e,{portalNode:T,portalSubtree:C}=y({container:m,ref:r,componentProps:e,elementProps:S}),O=t.useRef(null),R=t.useRef(null),I=t.useRef(null),x=t.useRef(null),[P,w]=t.useState(null),A=t.useRef(!1),L=P?.modal,k=P?.open,N="boolean"==typeof b?b:!!P&&!P.modal&&P.open&&!!T;t.useEffect(()=>{if(T&&!L)return(0,o.mergeCleanups)((0,i.addEventListener)(T,"focusin",e,!0),(0,i.addEventListener)(T,"focusout",e,!0));function e(e){T&&e.relatedTarget&&(0,d.isOutsideEvent)(e)&&("focusin"===e.type?A.current&&((0,d.enableFocusInside)(T),A.current=!1):((0,d.disableFocusInside)(T),A.current=!0))}},[T,L]),(0,u.useIsoLayoutEffect)(()=>{T&&!0===k&&A.current&&((0,d.enableFocusInside)(T),A.current=!1)},[k,T]);let M=t.useMemo(()=>({beforeOutsideRef:O,afterOutsideRef:R,beforeInsideRef:I,afterInsideRef:x,portalNode:T,setFocusManagerState:w}),[T]);return(0,v.jsxs)(t.Fragment,{children:[C,(0,v.jsxs)(E.Provider,{value:M,children:[N&&T&&(0,v.jsx)(c.FocusGuard,{"data-type":"outside",ref:O,onFocus:e=>{if((0,d.isOutsideEvent)(e,T))I.current?.focus();else{let e=P?P.domReference:null,t=(0,d.getPreviousTabbable)(e);t?.focus()}}}),N&&T&&(0,v.jsx)("span",{"aria-owns":T.id,style:h.ownerVisuallyHidden}),T&&n.createPortal(g,T),N&&T&&(0,v.jsx)(c.FocusGuard,{"data-type":"outside",ref:R,onFocus:e=>{if((0,d.isOutsideEvent)(e,T))x.current?.focus();else{let t=P?P.domReference:null,n=(0,d.getNextTabbable)(t);n?.focus(),P?.closeOnFocusOut&&P?.onOpenChange(!1,(0,f.createChangeEventDetails)(p.REASONS.focusOut,e.nativeEvent))}}})]})]})});e.s(["FloatingPortal",0,T,"useFloatingPortalNode",0,y,"usePortalContext",0,b])},333848,e=>{"use strict";var t=e.i(229315);e.s(["ownerWindow",()=>t.getWindow])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0rehsq9xe1kde.js b/litellm/proxy/_experimental/out/_next/static/chunks/0rehsq9xe1kde.js new file mode 100644 index 00000000000..4d244531936 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0rehsq9xe1kde.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,772345,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let s={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M168 504.2c1-43.7 10-86.1 26.9-126 17.3-41 42.1-77.7 73.7-109.4S337 212.3 378 195c42.4-17.9 87.4-27 133.9-27s91.5 9.1 133.8 27A341.5 341.5 0 01755 268.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.7 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c0-6.7-7.7-10.5-12.9-6.3l-56.4 44.1C765.8 155.1 646.2 92 511.8 92 282.7 92 96.3 275.6 92 503.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8zm756 7.8h-60c-4.4 0-7.9 3.5-8 7.8-1 43.7-10 86.1-26.9 126-17.3 41-42.1 77.8-73.7 109.4A342.45 342.45 0 01512.1 856a342.24 342.24 0 01-243.2-100.8c-9.9-9.9-19.2-20.4-27.8-31.4l60.2-47a8 8 0 00-3-14.1l-175.7-43c-5-1.2-9.9 2.6-9.9 7.7l-.7 181c0 6.7 7.7 10.5 12.9 6.3l56.4-44.1C258.2 868.9 377.8 932 512.2 932c229.2 0 415.5-183.7 419.8-411.8a8 8 0 00-8-8.2z"}}]},name:"sync",theme:"outlined"};var l=e.i(9583),i=a.forwardRef(function(e,i){return a.createElement(l.default,(0,t.default)({},e,{ref:i,icon:s}))});e.s(["SyncOutlined",0,i],772345)},214541,e=>{"use strict";var t=e.i(271645),a=e.i(135214),s=e.i(270345);e.s(["default",0,()=>{let[e,l]=(0,t.useState)([]),{accessToken:i,userId:r,userRole:n}=(0,a.default)();return(0,t.useEffect)(()=>{(async()=>{l(await (0,s.fetchTeams)(i,r,n,null))})()},[i,r,n]),{teams:e,setTeams:l}}])},11751,e=>{"use strict";e.s(["mapEmptyStringToNull",0,function(e){return""===e?null:e}])},784647,505022,721929,e=>{"use strict";var t=e.i(843476),a=e.i(464571),s=e.i(898586),l=e.i(592968),i=e.i(770914),r=e.i(312361),n=e.i(525720),o=e.i(282786),d=e.i(447566),c=e.i(772345),m=e.i(955135),u=e.i(646563),x=e.i(771674),p=e.i(72713),g=e.i(637235),h=e.i(962944);e.i(247167);var _=e.i(931067),j=e.i(271645);let y={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M866.9 169.9L527.1 54.1C523 52.7 517.5 52 512 52s-11 .7-15.1 2.1L157.1 169.9c-8.3 2.8-15.1 12.4-15.1 21.2v482.4c0 8.8 5.7 20.4 12.6 25.9L499.3 968c3.5 2.7 8 4.1 12.6 4.1s9.2-1.4 12.6-4.1l344.7-268.6c6.9-5.4 12.6-17 12.6-25.9V191.1c.2-8.8-6.6-18.3-14.9-21.2zM810 654.3L512 886.5 214 654.3V226.7l298-101.6 298 101.6v427.6zm-405.8-201c-3-4.1-7.8-6.6-13-6.6H336c-6.5 0-10.3 7.4-6.5 12.7l126.4 174a16.1 16.1 0 0026 0l212.6-292.7c3.8-5.3 0-12.7-6.5-12.7h-55.2c-5.1 0-10 2.5-13 6.6L468.9 542.4l-64.7-89.1z"}}]},name:"safety-certificate",theme:"outlined"};var b=e.i(9583),f=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:y}))});let v={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M668.6 320c0-4.4-3.6-8-8-8h-54.5c-3 0-5.8 1.7-7.1 4.4l-84.7 168.8H511l-84.7-168.8a8 8 0 00-7.1-4.4h-55.7c-1.3 0-2.6.3-3.8 1-3.9 2.1-5.3 7-3.2 10.8l103.9 191.6h-57c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76v39h-76c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76V704c0 4.4 3.6 8 8 8h49.9c4.4 0 8-3.6 8-8v-63.5h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8h-76.3v-39h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8H564l103.7-191.6c.5-1.1.9-2.4.9-3.7zM157.9 504.2a352.7 352.7 0 01103.5-242.4c32.5-32.5 70.3-58.1 112.4-75.9 43.6-18.4 89.9-27.8 137.6-27.8 47.8 0 94.1 9.3 137.6 27.8 42.1 17.8 79.9 43.4 112.4 75.9 10 10 19.3 20.5 27.9 31.4l-50 39.1a8 8 0 003 14.1l156.8 38.3c5 1.2 9.9-2.6 9.9-7.7l.8-161.5c0-6.7-7.7-10.5-12.9-6.3l-47.8 37.4C770.7 146.3 648.6 82 511.5 82 277 82 86.3 270.1 82 503.8a8 8 0 008 8.2h60c4.3 0 7.8-3.5 7.9-7.8zM934 512h-60c-4.3 0-7.9 3.5-8 7.8a352.7 352.7 0 01-103.5 242.4 352.57 352.57 0 01-112.4 75.9c-43.6 18.4-89.9 27.8-137.6 27.8s-94.1-9.3-137.6-27.8a352.57 352.57 0 01-112.4-75.9c-10-10-19.3-20.5-27.9-31.4l49.9-39.1a8 8 0 00-3-14.1l-156.8-38.3c-5-1.2-9.9 2.6-9.9 7.7l-.8 161.7c0 6.7 7.7 10.5 12.9 6.3l47.8-37.4C253.3 877.7 375.4 942 512.5 942 747 942 937.7 753.9 942 520.2a8 8 0 00-8-8.2z"}}]},name:"transaction",theme:"outlined"};var k=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:v}))}),N={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"defs",attrs:{},children:[{tag:"style",attrs:{}}]},{tag:"path",attrs:{d:"M945 412H689c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h256c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM811 548H689c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h122c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM477.3 322.5H434c-6.2 0-11.2 5-11.2 11.2v248c0 3.6 1.7 6.9 4.6 9l148.9 108.6c5 3.6 12 2.6 15.6-2.4l25.7-35.1v-.1c3.6-5 2.5-12-2.5-15.6l-126.7-91.6V333.7c.1-6.2-5-11.2-11.1-11.2z"}},{tag:"path",attrs:{d:"M804.8 673.9H747c-5.6 0-10.9 2.9-13.9 7.7a321 321 0 01-44.5 55.7 317.17 317.17 0 01-101.3 68.3c-39.3 16.6-81 25-124 25-43.1 0-84.8-8.4-124-25-37.9-16-72-39-101.3-68.3s-52.3-63.4-68.3-101.3c-16.6-39.2-25-80.9-25-124 0-43.1 8.4-84.7 25-124 16-37.9 39-72 68.3-101.3 29.3-29.3 63.4-52.3 101.3-68.3 39.2-16.6 81-25 124-25 43.1 0 84.8 8.4 124 25 37.9 16 72 39 101.3 68.3a321 321 0 0144.5 55.7c3 4.8 8.3 7.7 13.9 7.7h57.8c6.9 0 11.3-7.2 8.2-13.3-65.2-129.7-197.4-214-345-215.7-216.1-2.7-395.6 174.2-396 390.1C71.6 727.5 246.9 903 463.2 903c149.5 0 283.9-84.6 349.8-215.8a9.18 9.18 0 00-8.2-13.3z"}}]},name:"field-time",theme:"outlined"},T=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:N}))}),w=e.i(304911);let{Text:S}=s.Typography;function C({label:e,value:a,icon:s,truncate:l=!1,copyable:r=!1,defaultUserIdCheck:n=!1}){let o=!a,d=n&&"default_user_id"===a,c=d?(0,t.jsx)(w.default,{userId:a}):(0,t.jsx)(S,{strong:!0,copyable:!!(r&&!o&&!d)&&{tooltips:[`Copy ${e}`,"Copied!"]},ellipsis:l,style:l?{maxWidth:160,display:"block"}:void 0,children:o?"-":a});return(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.Space,{size:4,children:[(0,t.jsx)(S,{type:"secondary",children:s}),(0,t.jsx)(S,{type:"secondary",style:{fontSize:12,textTransform:"uppercase",letterSpacing:"0.05em"},children:e})]}),(0,t.jsx)("div",{children:c})]})}let{Title:I,Text:A}=s.Typography;function F({userAlias:e,userEmail:a,userId:l}){let r=(0,t.jsxs)(i.Space,{size:4,children:[(0,t.jsx)(A,{type:"secondary",children:(0,t.jsx)(x.UserOutlined,{})}),(0,t.jsx)(A,{type:"secondary",style:{fontSize:12,textTransform:"uppercase",letterSpacing:"0.05em"},children:"User"})]});if(!e&&!a&&!l)return(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(A,{strong:!0,children:"-"})})]});let n="default_user_id"===l,d=e||a||l,c=(0,t.jsx)("div",{className:"flex flex-col gap-2 text-xs min-w-[200px] max-w-[300px]",children:[{label:"User Alias",value:e??null},{label:"User Email",value:a||null},{label:"User ID",value:l||null}].map(({label:e,value:a})=>(0,t.jsxs)("div",{className:"flex flex-col min-w-0",children:[(0,t.jsx)("span",{className:"text-gray-400",children:e}),a?(0,t.jsx)(s.Typography.Text,{className:"font-mono text-xs",style:{maxWidth:220},ellipsis:{tooltip:a},copyable:!0,children:a}):(0,t.jsx)("span",{className:"font-mono",children:"-"})]},e))});return!n||e||a?(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(o.Popover,{content:c,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)(A,{strong:!0,ellipsis:!0,style:{cursor:"default",maxWidth:200,display:"block"},children:d})})})]}):(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(o.Popover,{content:c,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"cursor-default",children:(0,t.jsx)(w.default,{userId:l})})})})]})}e.s(["KeyInfoHeader",0,function({data:e,onBack:s,onCreateNew:o,onRegenerate:x,onDelete:_,onResetSpend:j,canModifyKey:y=!0,backButtonText:b="Back to Keys",regenerateDisabled:v=!1,regenerateTooltip:N}){return(0,t.jsxs)("div",{children:[o&&(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(a.Button,{type:"primary",icon:(0,t.jsx)(u.PlusOutlined,{}),onClick:o,children:"Create New Key"})}),(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(a.Button,{type:"text",icon:(0,t.jsx)(d.ArrowLeftOutlined,{}),onClick:s,children:b})}),(0,t.jsxs)(n.Flex,{justify:"space-between",align:"start",style:{marginBottom:20},children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(I,{level:3,copyable:{tooltips:["Copy Key Alias","Copied!"]},style:{margin:0},children:e.keyName}),(0,t.jsxs)(A,{type:"secondary",copyable:{text:e.keyId,tooltips:["Copy Key ID","Copied!"]},children:["Key ID: ",e.keyId]})]}),y&&(0,t.jsxs)(i.Space,{children:[(0,t.jsx)(l.Tooltip,{title:N||"",children:(0,t.jsx)("span",{children:(0,t.jsx)(a.Button,{icon:(0,t.jsx)(c.SyncOutlined,{}),onClick:x,disabled:v,children:"Regenerate Key"})})}),j&&(0,t.jsx)(a.Button,{danger:!0,icon:(0,t.jsx)(k,{}),onClick:j,children:"Reset Spend"}),(0,t.jsx)(a.Button,{danger:!0,icon:(0,t.jsx)(m.DeleteOutlined,{}),onClick:_,children:"Delete Key"})]})]}),(0,t.jsxs)(n.Flex,{align:"stretch",gap:40,style:{marginBottom:40},children:[(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(F,{userAlias:e.userAlias,userEmail:e.userEmail,userId:e.userId}),(0,t.jsx)(C,{label:"Expires",value:e.expires,icon:(0,t.jsx)(T,{})})]}),(0,t.jsx)(r.Divider,{type:"vertical",style:{height:"auto"}}),(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(C,{label:"Created At",value:e.createdAt,icon:(0,t.jsx)(p.CalendarOutlined,{})}),(0,t.jsx)(C,{label:"Created By",value:e.createdBy,icon:(0,t.jsx)(f,{}),truncate:!0,copyable:!0,defaultUserIdCheck:!0})]}),(0,t.jsx)(r.Divider,{type:"vertical",style:{height:"auto"}}),(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(C,{label:"Last Updated",value:e.lastUpdated,icon:(0,t.jsx)(g.ClockCircleOutlined,{})}),(0,t.jsx)(C,{label:"Last Active",value:e.lastActive,icon:(0,t.jsx)(h.ThunderboltOutlined,{})})]})]})]})}],784647);var M=e.i(599724),L=e.i(389083),R=e.i(278587);let E=j.forwardRef(function(e,t){return j.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),j.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["default",0,({autoRotate:e=!1,rotationInterval:a,lastRotationAt:s,keyRotationAt:l,nextRotationAt:i,variant:r="card",className:n=""})=>{let o=e=>{let t=new Date(e),a=t.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),s=t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});return`${a} at ${s}`},d=(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsx)("div",{className:"space-y-3",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(R.RefreshIcon,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(M.Text,{className:"font-semibold text-gray-900",children:"Auto-Rotation"}),(0,t.jsx)(L.Badge,{color:e?"green":"gray",size:"xs",children:e?"Enabled":"Disabled"}),e&&a&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(M.Text,{className:"text-gray-400",children:"•"}),(0,t.jsxs)(M.Text,{className:"text-sm text-gray-600",children:["Every ",a]})]})]})}),(e||s||l||i)&&(0,t.jsxs)("div",{className:"space-y-3",children:[s&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-200 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-700",children:"Last Rotation"}),(0,t.jsx)(M.Text,{className:"text-sm text-gray-600",children:o(s)})]})]}),(l||i)&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-200 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-700",children:"Next Scheduled Rotation"}),(0,t.jsx)(M.Text,{className:"text-sm text-gray-600",children:o(i||l||"")})]})]}),e&&!s&&!l&&!i&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-100 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsx)(M.Text,{className:"text-gray-600",children:"No rotation history available"})]})]}),!e&&!s&&!l&&!i&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-100 rounded-md",children:[(0,t.jsx)(R.RefreshIcon,{className:"w-4 h-4 text-gray-400"}),(0,t.jsx)(M.Text,{className:"text-gray-600",children:"Auto-rotation is not enabled for this key"})]})]});return"card"===r?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${n}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(M.Text,{className:"font-semibold text-gray-900",children:"Auto-Rotation"}),(0,t.jsx)(M.Text,{className:"text-xs text-gray-500",children:"Automatic key rotation settings and status for this key"})]})}),d]}):(0,t.jsxs)("div",{className:`${n}`,children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-900 mb-3",children:"Auto-Rotation"}),d]})}],505022);let P=["logging"];e.s(["extractLoggingSettings",0,e=>e&&"object"==typeof e&&Array.isArray(e.logging)?e.logging:[],"formatMetadataForDisplay",0,(e,t=2)=>JSON.stringify(e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([e])=>!P.includes(e))):{},null,t),"stripTagsFromMetadata",0,e=>{if(!e||"object"!=typeof e)return e;let{tags:t,...a}=e;return a}],721929)},643449,e=>{"use strict";var t=e.i(843476),a=e.i(262218),s=e.i(810757),l=e.i(477386),i=e.i(557662),r=e.i(555987);e.s(["default",0,function({loggingConfigs:e=[],disabledCallbacks:n=[],variant:o="card",className:d=""}){let c=(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(s.CogIcon,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900",children:"Logging Integrations"}),(0,t.jsx)(a.Tag,{color:"blue",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"space-y-3",children:e.map((e,l)=>{var n;let o=(n=e.callback_name,Object.entries(i.callback_map).find(([e,t])=>t===n)?.[0]||n),d=(0,r.resolveLogoSrc)(i.callbackInfo[o]?.logo);return(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[d?(0,t.jsx)("img",{src:d,alt:o,className:"w-5 h-5 object-contain"}):(0,t.jsx)(s.CogIcon,{className:"h-5 w-5 text-gray-400"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-medium text-blue-800",children:o}),(0,t.jsxs)("span",{className:"block text-xs text-blue-600",children:[Object.keys(e.callback_vars).length," parameters configured"]})]})]}),(0,t.jsx)(a.Tag,{color:(e=>{switch(e){case"success":return"green";case"failure":return"red";case"success_and_failure":return"blue";default:return}})(e.callback_type),children:(e=>{switch(e){case"success":return"Success Only";case"failure":return"Failure Only";case"success_and_failure":return"Success & Failure";default:return e}})(e.callback_type)})]},l)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(s.CogIcon,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)("span",{className:"text-gray-500 text-sm",children:"No logging integrations configured"})]})]}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l.BanIcon,{className:"h-4 w-4 text-red-600"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900",children:"Disabled Callbacks"}),(0,t.jsx)(a.Tag,{color:"red",children:n.length})]}),n.length>0?(0,t.jsx)("div",{className:"space-y-3",children:n.map((e,s)=>{let n=i.reverse_callback_map[e]||e,o=(0,r.resolveLogoSrc)(i.callbackInfo[n]?.logo);return(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[o?(0,t.jsx)("img",{src:o,alt:n,className:"w-5 h-5 object-contain"}):(0,t.jsx)(l.BanIcon,{className:"h-5 w-5 text-gray-400"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-medium text-red-800",children:n}),(0,t.jsx)("span",{className:"block text-xs text-red-600",children:"Disabled for this key"})]})]}),(0,t.jsx)(a.Tag,{color:"red",children:"Disabled"})]},s)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l.BanIcon,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)("span",{className:"text-gray-500 text-sm",children:"No callbacks disabled"})]})]})]});return"card"===o?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${d}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-semibold text-gray-900",children:"Logging Settings"}),(0,t.jsx)("span",{className:"block text-xs text-gray-500",children:"Active logging integrations and disabled callbacks for this key"})]})}),c]}):(0,t.jsxs)("div",{className:`${d}`,children:[(0,t.jsx)("span",{className:"block font-medium text-gray-900 mb-3",children:"Logging Settings"}),c]})}])},65932,272753,e=>{"use strict";var t=e.i(954616),a=e.i(912598),s=e.i(602869),l=e.i(431703),i=e.i(135214),r=e.i(207082);let n=async(e,t)=>{let a=(0,s.getProxyBaseUrl)(),i=`${a?`${a}/key/${t}/reset_spend`:`/key/${t}/reset_spend`}`,r=await fetch(i,{method:"POST",headers:{[(0,s.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({reset_to:0})});if(!r.ok){let e=await r.json(),t=(0,l.deriveErrorMessage)(e);throw(0,s.handleError)(t),Error(t)}return r.json()};e.s(["useResetKeySpend",0,()=>{let{accessToken:e}=(0,i.default)(),s=(0,a.useQueryClient)();return(0,t.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return n(e,t)},onSuccess:()=>{s.invalidateQueries({queryKey:r.keyKeys.all})}})}],65932);var o=e.i(843476),d=e.i(492030),c=e.i(166406),m=e.i(772345),u=e.i(560445),x=e.i(464571),p=e.i(178654),g=e.i(525720),h=e.i(808613),_=e.i(311451),j=e.i(28651),y=e.i(212931),b=e.i(621192),f=e.i(770914),v=e.i(898586),k=e.i(271645),N=e.i(237016),T=e.i(727749),w=e.i(24529);let{Text:S}=v.Typography,C={pattern:/^(\d+(s|m|h|d|w|mo))?$/,message:"Must be a duration like 30s, 30m, 24h, 2d, 1w, or 1mo"};e.s(["RegenerateKeyModal",0,function({selectedToken:e,visible:t,onClose:a,onKeyUpdate:l}){let{accessToken:r}=(0,i.default)(),[n]=h.Form.useForm(),[v,I]=(0,k.useState)(null),[A,F]=(0,k.useState)(!1),[M,L]=(0,k.useState)(!1),R=(0,w.isKeyExpired)(e?.expires),E=h.Form.useWatch("duration",n),P=R?[{required:!0,message:"Expiration is required for expired keys"},C]:[C];(0,k.useEffect)(()=>{t&&e&&r&&n.setFieldsValue({key_alias:e.key_alias,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,duration:e.duration||"",grace_period:""})},[t,e,n,r]);let O=E?(0,w.calculateExpiryPreviewFromDuration)(E):null,B=async()=>{if(e&&r){F(!0);try{let t=await n.validateFields(),a=await (0,s.regenerateKeyCall)(r,e.token||e.token_id,t);I(a.key),T.default.success("Virtual Key regenerated successfully");let i={...a,token:a.token||a.key_id||e.token,key_name:a.key,max_budget:t.max_budget,tpm_limit:t.tpm_limit,rpm_limit:t.rpm_limit,expires:a.expires??e.expires};l&&l(i),F(!1)}catch(e){if(F(!1),e&&"object"==typeof e&&"errorFields"in e)return;console.error("Error regenerating key:",e),T.default.fromBackend(e)}}},D=()=>{I(null),F(!1),L(!1),n.resetFields(),a()};return(0,o.jsx)(y.Modal,{title:"Regenerate Virtual Key",open:t,onCancel:D,width:520,maskClosable:!1,footer:v?[(0,o.jsxs)(f.Space,{children:[(0,o.jsx)(x.Button,{onClick:D,children:"Close"}),(0,o.jsx)(N.CopyToClipboard,{text:v,onCopy:()=>{L(!0)},children:(0,o.jsx)(x.Button,{type:"primary",icon:M?(0,o.jsx)(d.CheckOutlined,{}):(0,o.jsx)(c.CopyOutlined,{}),children:M?"Copied":"Copy Key"})})]},"footer-actions")]:[(0,o.jsxs)(f.Space,{children:[(0,o.jsx)(x.Button,{onClick:D,children:"Cancel"}),(0,o.jsx)(x.Button,{type:"primary",icon:(0,o.jsx)(m.SyncOutlined,{}),onClick:B,loading:A,children:"Regenerate"})]},"footer-actions")],children:v?(0,o.jsxs)(g.Flex,{vertical:!0,gap:"middle",children:[(0,o.jsx)(u.Alert,{type:"warning",showIcon:!0,message:"Save it now, you will not see it again"}),(0,o.jsxs)(g.Flex,{vertical:!0,gap:2,children:[(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Key Alias"}),(0,o.jsx)(S,{children:e?.key_alias||"No alias set"})]}),(0,o.jsxs)(g.Flex,{vertical:!0,gap:6,children:[(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Virtual Key"}),(0,o.jsx)("div",{style:{background:"#f5f5f5",border:"1px solid #e8e8e8",borderRadius:6,padding:"14px 16px",fontFamily:"SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace",fontSize:16,wordBreak:"break-all",color:"#262626"},children:v})]})]}):(0,o.jsxs)(h.Form,{form:n,layout:"vertical",style:{marginTop:4},children:[(0,o.jsx)(h.Form.Item,{name:"key_alias",label:"Key Alias",children:(0,o.jsx)(_.Input,{disabled:!0})}),(0,o.jsxs)(b.Row,{gutter:12,children:[(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,o.jsx)(j.InputNumber,{step:.01,precision:2,style:{width:"100%"}})})}),(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"tpm_limit",label:"TPM Limit",children:(0,o.jsx)(j.InputNumber,{style:{width:"100%"}})})}),(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"rpm_limit",label:"RPM Limit",children:(0,o.jsx)(j.InputNumber,{style:{width:"100%"}})})})]}),(0,o.jsxs)(b.Row,{gutter:12,children:[(0,o.jsx)(p.Col,{span:12,children:(0,o.jsx)(h.Form.Item,{name:"duration",label:"Expire Key",rules:P,extra:(0,o.jsxs)(g.Flex,{vertical:!0,gap:2,children:[(0,o.jsxs)(S,{type:R?"danger":"secondary",style:{fontSize:12},children:["Current expiry: ",e?.expires?(0,w.formatExpiresUtc)(e.expires):"Never",R&&" (expired)"]}),O&&(0,o.jsxs)(S,{type:"success",style:{fontSize:12},children:["New expiry: ",O]})]}),children:(0,o.jsx)(_.Input,{placeholder:"e.g. 30s, 30h, 30d"})})}),(0,o.jsx)(p.Col,{span:12,children:(0,o.jsx)(h.Form.Item,{name:"grace_period",label:"Grace Period",tooltip:"Keep the old key valid for this duration after rotation. Both keys work during this period for seamless cutover. Empty = immediate revoke.",extra:(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Recommended: 24h to 72h for production keys"}),rules:[C],children:(0,o.jsx)(_.Input,{placeholder:"e.g. 24h, 2d"})})})]})]})})}],272753)},183588,e=>{"use strict";var t=e.i(843476),a=e.i(266484);e.s(["default",0,({value:e,onChange:s,disabledCallbacks:l=[],onDisabledCallbacksChange:i})=>(0,t.jsx)(a.default,{value:e,onChange:s,disabledCallbacks:l,onDisabledCallbacksChange:i})])},20147,e=>{"use strict";var t=e.i(843476),a=e.i(135214),s=e.i(510674),l=e.i(292639),i=e.i(214541),r=e.i(500330),n=e.i(11751),o=e.i(530212),d=e.i(389083),c=e.i(994388),m=e.i(304967),u=e.i(350967),x=e.i(197647),p=e.i(653824),g=e.i(881073),h=e.i(404206),_=e.i(723731),j=e.i(599724),y=e.i(629569),b=e.i(808613),f=e.i(212931),v=e.i(262218),k=e.i(784647),N=e.i(271645),T=e.i(708347),w=e.i(557662),S=e.i(505022),C=e.i(127952),I=e.i(721929),A=e.i(643449),F=e.i(727749),M=e.i(602869),L=e.i(65932),R=e.i(207082),E=e.i(912598),P=e.i(384767),O=e.i(272753),B=e.i(190702),D=e.i(891547),z=e.i(109799),K=e.i(921511),$=e.i(827252),U=e.i(779241),V=e.i(311451),W=e.i(199133),G=e.i(790848),q=e.i(592968),H=e.i(552130),J=e.i(9314),Q=e.i(392110),Y=e.i(844565),X=e.i(939510),Z=e.i(363256),ee=e.i(128233),et=e.i(319312),ea=e.i(833400),es=e.i(355619),el=e.i(75921),ei=e.i(234713),er=e.i(390605),en=e.i(702597),eo=e.i(435451),ed=e.i(183588),ec=e.i(916940);function em({keyData:e,onCancel:a,onSubmit:i,teams:r,accessToken:n,userID:o,userRole:d,premiumUser:m=!1}){let u=m||null!=d&&T.rolesWithWriteAccess.includes(d),[x]=b.Form.useForm(),[p,g]=(0,N.useState)([]),[h,_]=(0,N.useState)({}),j=r?.find(t=>t.team_id===e.team_id),[y,f]=(0,N.useState)([]),[v,k]=(0,N.useState)(Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[]),[S,C]=(0,N.useState)(e.organization_id||null),[A,L]=(0,N.useState)(e.auto_rotate||!1),[R,E]=(0,N.useState)(e.rotation_interval||""),[P,O]=(0,N.useState)(!e.expires),[B,eu]=(0,N.useState)(!1),[ex,ep]=(0,N.useState)(Array.isArray(e.budget_limits)?e.budget_limits:[]),[eg,eh]=(0,N.useState)((0,ea.tagLimitsToRows)(e.metadata?.tag_rpm_limit)),[e_,ej]=(0,N.useState)(e.budget_fallbacks&&"object"==typeof e.budget_fallbacks?e.budget_fallbacks:{}),{data:ey,isLoading:eb}=(0,z.useOrganizations)(),{data:ef}=(0,s.useProjects)(),{data:ev}=(0,l.useUISettings)(),ek=!!ev?.values?.enable_projects_ui,eN=!!e.project_id,eT=(()=>{if(!e.project_id)return null;let t=ef?.find(t=>t.project_id===e.project_id);return t?.project_alias?`${t.project_alias} (${e.project_id})`:e.project_id})();(0,N.useEffect)(()=>{let t=async()=>{if(o&&d&&n)try{if(null===e.team_id){let e=(await (0,M.modelAvailableCall)(n,o,d)).data.map(e=>e.id);f((0,es.excludeProxyWideSentinel)(e))}else if(j?.team_id){let e=await (0,en.fetchTeamModels)(o,d,n,j.team_id);f((0,es.excludeProxyWideSentinel)(Array.from(new Set([...j.models,...e]))))}}catch(e){console.error("Error fetching models:",e)}};(async()=>{if(n)try{let e=await (0,M.getPromptsList)(n);g(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}})(),t()},[o,d,n,j,e.team_id]),(0,N.useEffect)(()=>{x.setFieldValue("disabled_callbacks",v)},[x,v]);let ew=e=>e&&({"24h":"daily","7d":"weekly","30d":"monthly"})[e]||null,eS={...e,token:e.token||e.token_id,budget_duration:ew(e.budget_duration),metadata:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(e.metadata)),guardrails:e.metadata?.guardrails,disable_global_guardrails:e.metadata?.disable_global_guardrails||!1,throttle_on_budget_exceeded:e.metadata?.throttle_on_budget_exceeded||!1,prompts:e.metadata?.prompts,tags:e.metadata?.tags,vector_stores:e.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:e.object_permission?.mcp_servers||[],accessGroups:e.object_permission?.mcp_access_groups||[]},mcp_tool_permissions:e.object_permission?.mcp_tool_permissions||{},agents_and_groups:{agents:e.object_permission?.agents||[],accessGroups:e.object_permission?.agent_access_groups||[]},logging_settings:(0,I.extractLoggingSettings)(e.metadata),disabled_callbacks:Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[],access_group_ids:e.access_group_ids||[],auto_rotate:e.auto_rotate||!1,...e.rotation_interval&&{rotation_interval:e.rotation_interval},allowed_routes:Array.isArray(e.allowed_routes)&&e.allowed_routes.length>0?e.allowed_routes.join(", "):""};(0,N.useEffect)(()=>{x.setFieldsValue({...e,token:e.token||e.token_id,budget_duration:ew(e.budget_duration),metadata:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(e.metadata)),guardrails:e.metadata?.guardrails,disable_global_guardrails:e.metadata?.disable_global_guardrails||!1,prompts:e.metadata?.prompts,tags:e.metadata?.tags,vector_stores:e.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:e.object_permission?.mcp_servers||[],accessGroups:e.object_permission?.mcp_access_groups||[]},mcp_tool_permissions:e.object_permission?.mcp_tool_permissions||{},throttle_on_budget_exceeded:e.metadata?.throttle_on_budget_exceeded||!1,logging_settings:(0,I.extractLoggingSettings)(e.metadata),disabled_callbacks:Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[],access_group_ids:e.access_group_ids||[],auto_rotate:e.auto_rotate||!1,...e.rotation_interval&&{rotation_interval:e.rotation_interval},allowed_routes:Array.isArray(e.allowed_routes)&&e.allowed_routes.length>0?e.allowed_routes.join(", "):""})},[e,x]),(0,N.useEffect)(()=>{x.setFieldValue("auto_rotate",A)},[A,x]),(0,N.useEffect)(()=>{R&&x.setFieldValue("rotation_interval",R)},[R,x]),(0,N.useEffect)(()=>{(async()=>{if(n)try{let e=await (0,M.tagListCall)(n);_(e)}catch(e){F.default.fromBackend("Error fetching tags: "+e)}})()},[n]);let eC=async t=>{try{if(eu(!0),"string"==typeof t.allowed_routes){let e=t.allowed_routes.trim();""===e?t.allowed_routes=[]:t.allowed_routes=e.split(",").map(e=>e.trim()).filter(e=>e.length>0)}let a=new Set(Array.isArray(e.allowed_routes)?e.allowed_routes:[]),s=new Set(Array.isArray(t.allowed_routes)?t.allowed_routes:[]);a.size===s.size&&[...s].every(e=>a.has(e))&&delete t.allowed_routes,P&&(t.duration=null);let l=ex.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);l.length>0?t.budget_limits=l:0===ex.length&&(t.budget_limits=[]);let{tag_rpm_limit:r}=(0,ea.tagRowsToLimits)(eg);t.tag_rpm_limit=r;let n=null!=e.budget_fallbacks&&Object.keys(e.budget_fallbacks).length>0;Object.keys(e_).length>0?t.budget_fallbacks=e_:n&&(t.budget_fallbacks={}),await i(t)}finally{eu(!1)}};return(0,t.jsxs)(b.Form,{form:x,onFinish:eC,initialValues:eS,layout:"vertical",children:[(0,t.jsx)(b.Form.Item,{label:"Key Alias",name:"key_alias",children:(0,t.jsx)(U.TextInput,{})}),(0,t.jsx)(b.Form.Item,{label:"Models",name:"models",children:(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_routes!==t.allowed_routes||e.models!==t.models,children:({getFieldValue:a,setFieldValue:s})=>{let l=a("allowed_routes")||"",i="string"==typeof l&&""!==l.trim()?l.split(",").map(e=>e.trim()).filter(e=>e.length>0):[],r=i.includes("management_routes")||i.includes("info_routes"),n=a("models")||[];return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(W.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:r,value:r?[]:n,onChange:e=>{e.includes("all-team-models")?s("models",["all-team-models"]):e.includes("all-proxy-models")?s("models",["all-proxy-models"]):s("models",e)},children:[null!=e.team_id?null!=j&&(0,t.jsx)(W.Select.Option,{value:"all-team-models",children:"All Team Models"}):(0,t.jsx)(W.Select.Option,{value:"all-proxy-models",children:"All Proxy Models"}),y.map(e=>(0,t.jsx)(W.Select.Option,{value:e,disabled:(0,es.hasAllModelsSentinel)(n),children:e},e))]}),r&&(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Models field is disabled for this key type"})]})}})}),(0,t.jsx)(b.Form.Item,{label:"Key Type",children:(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_routes!==t.allowed_routes,children:({getFieldValue:e,setFieldValue:a})=>{var s;let l=e("allowed_routes")||"",i=(s="string"==typeof l&&""!==l.trim()?l.split(",").map(e=>e.trim()).filter(e=>e.length>0):[])&&0!==s.length?s.includes("llm_api_routes")?"llm_api":s.includes("management_routes")?"management":s.includes("info_routes")?"read_only":"default":"default";return(0,t.jsxs)(W.Select,{placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",value:i,onChange:e=>{switch(e){case"default":a("allowed_routes","");break;case"llm_api":a("allowed_routes","llm_api_routes");break;case"management":a("allowed_routes","management_routes"),a("models",[])}},children:[(0,t.jsx)(W.Select.Option,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Full Access"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})}),(0,t.jsx)(W.Select.Option,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"AI APIs"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(W.Select.Option,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Management"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call only management routes (user/team/key management)"})]})})]})}})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Routes"," ",(0,t.jsx)(q.Tooltip,{title:"List of allowed routes for the key (comma-separated). Can be specific routes (e.g., '/chat/completions') or route patterns (e.g., 'llm_api_routes', 'management_routes', '/keys/*'). Leave empty to allow all routes.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_routes",children:(0,t.jsx)(V.Input,{placeholder:"Enter allowed routes (comma-separated). Special values: llm_api_routes, management_routes. Examples: llm_api_routes, /chat/completions, /keys/*. Leave empty to allow all routes"})}),(0,t.jsx)(b.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(eo.default,{step:.01,style:{width:"100%"},placeholder:"Enter a numerical value"})}),(0,t.jsx)(b.Form.Item,{label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(W.Select,{placeholder:"n/a",children:[(0,t.jsx)(W.Select.Option,{value:"daily",children:"Daily"}),(0,t.jsx)(W.Select.Option,{value:"weekly",children:"Weekly"}),(0,t.jsx)(W.Select.Option,{value:"monthly",children:"Monthly"})]})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(q.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(et.BudgetWindowsEditor,{value:ex,onChange:ep})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(q.Tooltip,{title:"When a model exceeds its per-model budget, requests automatically reroute to fallback models instead of failing",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(ee.BudgetFallbacksEditor,{value:e_,onChange:ej,availableModels:y})}),(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(X.default,{type:"tpm",name:"tpm_limit_type",showDetailedDescriptions:!1}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(X.default,{type:"rpm",name:"rpm_limit_type",showDetailedDescriptions:!1}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Throttle on budget exceeded"," ",(0,t.jsx)(q.Tooltip,{title:"When this key exceeds its max budget, throttle its TPM/RPM to the globally configured percentage instead of blocking access entirely. Requires budget_exceeded_throttle_percentage in litellm_settings and a TPM/RPM limit on the key.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"throttle_on_budget_exceeded",valuePropName:"checked",children:(0,t.jsx)(G.Switch,{checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(b.Form.Item,{label:"Max Parallel Requests",name:"max_parallel_requests",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(b.Form.Item,{label:"Model TPM Limit",name:"model_tpm_limit",children:(0,t.jsx)(V.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}'})}),(0,t.jsx)(b.Form.Item,{label:"Model RPM Limit",name:"model_rpm_limit",children:(0,t.jsx)(V.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}'})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Per-Tag Rate Limits"," ",(0,t.jsx)(q.Tooltip,{title:"Scope rate limits to a request tag so each tag (e.g. a cell or group) gets its own RPM counter. Requests without a matching tag fall back to the key-level limit.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(ea.TagRateLimitEditor,{value:eg,onChange:eh})}),(0,t.jsx)(b.Form.Item,{label:"Guardrails",name:"guardrails",children:n&&(0,t.jsx)(D.default,{onChange:e=>{x.setFieldValue("guardrails",e)},accessToken:n,disabled:!u})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(q.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"disable_global_guardrails",valuePropName:"checked",children:(0,t.jsx)(G.Switch,{disabled:!u,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(q.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"policies",children:n&&(0,t.jsx)(K.default,{onChange:e=>{x.setFieldValue("policies",e)},accessToken:n,disabled:!m})}),(0,t.jsx)(b.Form.Item,{label:"Tags",name:"tags",children:(0,t.jsx)(W.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",options:Object.values(h).map(e=>({value:e.name,label:e.name,title:e.description||e.name}))})}),(0,t.jsx)(b.Form.Item,{label:"Prompts",name:"prompts",children:(0,t.jsx)(q.Tooltip,{title:m?"":"Setting prompts by key is a premium feature",placement:"top",children:(0,t.jsx)(W.Select,{mode:"tags",style:{width:"100%"},disabled:!m,placeholder:m?Array.isArray(e.metadata?.prompts)&&e.metadata.prompts.length>0?`Current: ${e.metadata.prompts.join(", ")}`:"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:p.map(e=>({value:e,label:e}))})})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(q.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",children:(0,t.jsx)(J.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{label:"Allowed Pass Through Routes",name:"allowed_passthrough_routes",children:(0,t.jsx)(q.Tooltip,{title:m?"":"Setting allowed pass through routes by key is a premium feature",placement:"top",children:(0,t.jsx)(Y.default,{onChange:e=>x.setFieldValue("allowed_passthrough_routes",e),value:x.getFieldValue("allowed_passthrough_routes"),accessToken:n||"",placeholder:m?Array.isArray(e.metadata?.allowed_passthrough_routes)&&e.metadata.allowed_passthrough_routes.length>0?`Current: ${e.metadata.allowed_passthrough_routes.join(", ")}`:"Select or enter allowed pass through routes":"Premium feature - Upgrade to set allowed pass through routes by key",disabled:!m})})}),(0,t.jsx)(b.Form.Item,{label:"Vector Stores",name:"vector_stores",children:(0,t.jsx)(ec.default,{onChange:e=>x.setFieldValue("vector_stores",e),value:x.getFieldValue("vector_stores"),accessToken:n||"",placeholder:"Select vector stores"})}),(0,t.jsx)(b.Form.Item,{label:"MCP Servers / Access Groups",name:"mcp_servers_and_groups",children:(0,t.jsx)(el.default,{onChange:e=>x.setFieldValue("mcp_servers_and_groups",e),value:x.getFieldValue("mcp_servers_and_groups"),accessToken:n||"",placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(b.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(V.Input,{type:"hidden"})}),(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.mcp_servers_and_groups!==t.mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsx)(er.default,{accessToken:n||"",selectedServers:(x.getFieldValue("mcp_servers_and_groups")?.servers||[]).filter(e=>e!==ei.NO_MCP_SERVERS_SENTINEL),toolPermissions:x.getFieldValue("mcp_tool_permissions")||{},onChange:e=>x.setFieldsValue({mcp_tool_permissions:e})})})}),(0,t.jsx)(b.Form.Item,{label:"Agents / Access Groups",name:"agents_and_groups",children:(0,t.jsx)(H.default,{onChange:e=>x.setFieldValue("agents_and_groups",e),value:x.getFieldValue("agents_and_groups"),accessToken:n||"",placeholder:"Select agents or access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(q.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",children:(0,t.jsx)(Z.default,{organizations:ey,loading:eb,disabled:"Admin"!==d,onChange:e=>{C(e||null),x.setFieldValue("team_id",void 0)}})}),(0,t.jsx)(b.Form.Item,{label:"Team ID",name:"team_id",help:ek&&eN?"Team is locked because this key belongs to a project":void 0,children:(0,t.jsx)(W.Select,{placeholder:"Select team",showSearch:!0,disabled:ek&&eN,style:{width:"100%"},onChange:e=>{let t=r?.find(t=>t.team_id===e)||null;t?.organization_id?(C(t.organization_id),x.setFieldValue("organization_id",t.organization_id)):e||(C(null),x.setFieldValue("organization_id",void 0))},filterOption:(e,t)=>{let a=S?r?.filter(e=>e.organization_id===S):r,s=a?.find(e=>e.team_id===t?.value);return!!s&&(s.team_alias?.toLowerCase().includes(e.toLowerCase())??!1)},children:(S?r?.filter(e=>e.organization_id===S):r)?.map(e=>(0,t.jsx)(W.Select.Option,{value:e.team_id,children:`${e.team_alias} (${e.team_id})`},e.team_id))})}),ek&&eN&&(0,t.jsx)(b.Form.Item,{label:"Project",children:(0,t.jsx)(V.Input,{value:eT??"",disabled:!0})}),(0,t.jsx)(b.Form.Item,{label:"Logging Settings",name:"logging_settings",children:(0,t.jsx)(ed.default,{value:x.getFieldValue("logging_settings"),onChange:e=>x.setFieldValue("logging_settings",e),disabledCallbacks:v,onDisabledCallbacksChange:e=>{k((0,w.mapInternalToDisplayNames)(e)),x.setFieldValue("disabled_callbacks",e)}})}),(0,t.jsx)(b.Form.Item,{label:"Metadata",name:"metadata",children:(0,t.jsx)(V.Input.TextArea,{rows:10})}),(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(Q.default,{form:x,autoRotationEnabled:A,onAutoRotationChange:L,rotationInterval:R,onRotationIntervalChange:E,neverExpire:P,onNeverExpireChange:O}),(0,t.jsx)(b.Form.Item,{name:"duration",hidden:!0,initialValue:"",children:(0,t.jsx)(V.Input,{})})]}),(0,t.jsx)(b.Form.Item,{name:"token",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"disabled_callbacks",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"auto_rotate",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"rotation_interval",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)("div",{className:"sticky z-10 bg-white p-4 border-t border-gray-200 -bottom-6 -inset-x-6",children:(0,t.jsxs)("div",{className:"flex justify-end items-center gap-2",children:[(0,t.jsx)(c.Button,{variant:"secondary",onClick:a,disabled:B,children:"Cancel"}),(0,t.jsx)(c.Button,{type:"submit",loading:B,children:"Save Changes"})]})})]})}let eu=["policies","guardrails","prompts","tags","allowed_passthrough_routes"],ex=e=>null==e||Array.isArray(e)&&0===e.length||"string"==typeof e&&""===e.trim();e.s(["default",0,function({onClose:e,keyData:D,teams:z,onKeyDataUpdate:K,onDelete:$,backButtonText:U="Back to Keys"}){let V,{accessToken:W,userId:G,userRole:q,premiumUser:H}=(0,a.default)(),J=(0,E.useQueryClient)(),Q=H||null!=q&&T.rolesWithWriteAccess.includes(q),{teams:Y}=(0,i.default)(),{data:X}=(0,s.useProjects)(),{data:Z}=(0,l.useUISettings)(),ee=!!Z?.values?.enable_projects_ui,[et,ea]=(0,N.useState)(!1),[es]=b.Form.useForm(),[el,ei]=(0,N.useState)(!1),[er,en]=(0,N.useState)(!1),[eo,ed]=(0,N.useState)(""),[ec,ep]=(0,N.useState)(!1),[eg,eh]=(0,N.useState)(!1),{mutate:e_,isPending:ej}=(0,L.useResetKeySpend)(),[ey,eb]=(0,N.useState)(D),[ef,ev]=(0,N.useState)(null),[ek,eN]=(0,N.useState)(!1),[eT,ew]=(0,N.useState)({}),[eS,eC]=(0,N.useState)(!1);if((0,N.useEffect)(()=>{D&&eb(D)},[D]),(0,N.useEffect)(()=>{(async()=>{let e=ey?.metadata?.policies;if(!W||!e||!Array.isArray(e)||0===e.length)return;eC(!0);let t={};try{await Promise.all(e.map(async e=>{try{let a=await (0,M.getPolicyInfoWithGuardrails)(W,e);t[e]=a.resolved_guardrails||[]}catch(a){console.error(`Failed to fetch guardrails for policy ${e}:`,a),t[e]=[]}})),ew(t)}catch(e){console.error("Failed to fetch policy guardrails:",e)}finally{eC(!1)}})()},[W,ey?.metadata?.policies]),(0,N.useEffect)(()=>{if(ek){let e=setTimeout(()=>{eN(!1)},5e3);return()=>clearTimeout(e)}},[ek]),!ey)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)(c.Button,{icon:o.ArrowLeftIcon,variant:"light",onClick:e,className:"mb-4",children:U}),(0,t.jsx)(j.Text,{children:"Key not found"})]});let eI=async e=>{try{if(!W)return;let t=e.token;for(let a of(e.key=t,Q||(delete e.guardrails,delete e.prompts),eu)){let t=ey.metadata?.[a]??ey[a];ex(e[a])&&ex(t)&&delete e[a]}let a=!!ey.metadata?.disable_global_guardrails;if(!!e.disable_global_guardrails===a&&delete e.disable_global_guardrails,e.max_budget=(0,n.mapEmptyStringToNull)(e.max_budget),void 0!==e.vector_stores&&(e.object_permission={...ey.object_permission,vector_stores:e.vector_stores||[]},delete e.vector_stores),void 0!==e.mcp_servers_and_groups){let{servers:t,accessGroups:a,toolsets:s}=e.mcp_servers_and_groups||{servers:[],accessGroups:[],toolsets:[]};e.object_permission={...ey.object_permission,mcp_servers:t||[],mcp_access_groups:a||[],mcp_toolsets:s||[]},delete e.mcp_servers_and_groups}if(void 0!==e.mcp_tool_permissions){let t=e.mcp_tool_permissions||{};Object.keys(t).length>0&&(e.object_permission={...e.object_permission,mcp_tool_permissions:t}),delete e.mcp_tool_permissions}if(void 0!==e.agents_and_groups){let{agents:t,accessGroups:a}=e.agents_and_groups||{agents:[],accessGroups:[]};e.object_permission={...e.object_permission,agents:t||[],agent_access_groups:a||[]},delete e.agents_and_groups}if(e.max_budget=(0,n.mapEmptyStringToNull)(e.max_budget),e.tpm_limit=(0,n.mapEmptyStringToNull)(e.tpm_limit),e.rpm_limit=(0,n.mapEmptyStringToNull)(e.rpm_limit),e.max_parallel_requests=(0,n.mapEmptyStringToNull)(e.max_parallel_requests),e.metadata&&"string"==typeof e.metadata)try{let t=JSON.parse(e.metadata);"tags"in t&&delete t.tags,e.metadata={...t,...Array.isArray(e.tags)&&e.tags.length>0?{tags:e.tags}:{},...e.guardrails?.length>0?{guardrails:e.guardrails}:{},...Array.isArray(e.logging_settings)&&e.logging_settings.length>0?{logging:e.logging_settings}:{},...e.disabled_callbacks?.length>0?{litellm_disabled_callbacks:(0,w.mapDisplayToInternalNames)(e.disabled_callbacks)}:{}}}catch(e){console.error("Error parsing metadata JSON:",e),F.default.error("Invalid metadata JSON");return}else{let{tags:t,...a}=e.metadata||{};e.metadata={...a,...Array.isArray(e.tags)&&e.tags.length>0?{tags:e.tags}:{},...e.guardrails?.length>0?{guardrails:e.guardrails}:{},...Array.isArray(e.logging_settings)&&e.logging_settings.length>0?{logging:e.logging_settings}:{},...e.disabled_callbacks?.length>0?{litellm_disabled_callbacks:(0,w.mapDisplayToInternalNames)(e.disabled_callbacks)}:{}}}"tags"in e&&delete e.tags,delete e.logging_settings,e.budget_duration&&(e.budget_duration=({daily:"24h",weekly:"7d",monthly:"30d"})[e.budget_duration]);let s=await (0,M.keyUpdateCall)(W,e);eb(e=>e?{...e,...s}:void 0),K&&K(s),F.default.success("Key updated successfully"),ea(!1)}catch(e){F.default.fromBackend((0,B.parseErrorMessage)(e)),console.error("Error updating key:",e)}},eA=async()=>{try{if(en(!0),!W)return;await (0,M.keyDeleteCall)(W,ey.token||ey.token_id),F.default.success("Key deleted successfully"),await J.invalidateQueries({queryKey:R.keyKeys.lists()}),$&&$(),e()}catch(e){console.error("Error deleting the key:",e),F.default.fromBackend(e)}finally{en(!1),ei(!1),ed("")}},eF=e=>{let t=new Date(e),a=t.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),s=t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});return`${a} at ${s}`},eM=(0,T.isProxyAdminRole)(q||"")||Y&&(0,T.isUserTeamAdminForSingleTeam)(Y?.filter(e=>e.team_id===ey.team_id)[0]?.members_with_roles,G||"")||G===ey.user_id&&"Internal Viewer"!==q,eL=(0,T.isProxyAdminRole)(q||"")||Y&&(0,T.isUserTeamAdminForSingleTeam)(Y?.filter(e=>e.team_id===ey.team_id)[0]?.members_with_roles,G||""),eR=ey.team_id?Y?.find(e=>e.team_id===ey.team_id):null,eE=null!==ey.max_budget?`$${(0,r.formatNumberWithCommas)(ey.max_budget,2)}`:eR?.max_budget!=null?`$${(0,r.formatNumberWithCommas)(eR.max_budget,2)} (Team: ${eR.team_alias||eR.team_id}${eR.budget_duration?` / ${eR.budget_duration}`:""})`:"Unlimited";return(0,t.jsxs)("div",{className:"w-full h-full overflow-y-auto p-4",children:[(0,t.jsx)(k.KeyInfoHeader,{data:{keyName:ey.key_alias||"Virtual Key",keyId:ey.token_id||ey.token,userId:ey.user_id||"",userEmail:ey.user_email||"",userAlias:ey.user?.user_alias??null,createdBy:ey.created_by_user?.user_alias||ey.created_by_user?.user_email||ey.created_by||"",createdAt:ey.created_at?eF(ey.created_at):"",lastUpdated:ey.updated_at?eF(ey.updated_at):"",lastActive:ey.last_active?eF(ey.last_active):"Never",expires:ey.expires?eF(ey.expires):"Never"},onBack:e,onRegenerate:()=>ep(!0),onDelete:()=>ei(!0),onResetSpend:eL?()=>eh(!0):void 0,canModifyKey:eM,backButtonText:U,regenerateDisabled:!H,regenerateTooltip:H?void 0:"This is a LiteLLM Enterprise feature, and requires a valid key to use."}),(0,t.jsx)(O.RegenerateKeyModal,{selectedToken:ey,visible:ec,onClose:()=>ep(!1),onKeyUpdate:e=>{eb(t=>{if(t)return{...t,...e,created_at:new Date().toLocaleString()}}),ev(new Date),eN(!0),K&&K({...e,created_at:new Date().toLocaleString()})}}),(0,t.jsx)(C.default,{isOpen:el,title:"Delete Key",alertMessage:"This action is irreversible and will immediately revoke access for any applications using this key.",message:"Are you sure you want to delete this Virtual Key?",resourceInformationTitle:"Key Information",resourceInformation:[{label:"Key Alias",value:ey?.key_alias||"-"},{label:"Key ID",value:ey?.token_id||ey?.token||"-",code:!0},{label:"Team ID",value:ey?.team_id||"-",code:!0},{label:"Spend",value:ey?.spend?`$${(0,r.formatNumberWithCommas)(ey.spend,4)}`:"$0.0000"}],onCancel:()=>{ei(!1),ed("")},onOk:eA,confirmLoading:er,requiredConfirmation:ey?.key_alias}),(0,t.jsxs)(f.Modal,{title:"Reset Key Spend",open:eg,onOk:()=>{e_(ey.token||ey.token_id,{onSuccess:()=>{eb(e=>e?{...e,spend:0}:void 0),K&&K({spend:0}),F.default.success("Key spend reset to $0"),eh(!1)},onError:e=>{F.default.fromBackend((0,B.parseErrorMessage)(e)),console.error("Error resetting key spend:",e)}})},onCancel:()=>eh(!1),okText:"Reset",okButtonProps:{danger:!0},confirmLoading:ej,children:[(0,t.jsxs)("p",{children:["Reset spend for ",(0,t.jsx)("strong",{children:ey?.key_alias||ey?.token_id||"this key"})," to"," ",(0,t.jsx)("strong",{children:"$0"}),"?"]}),(0,t.jsxs)("p",{style:{color:"#666",fontSize:"0.875rem",marginTop:8},children:["Current spend: ",(0,t.jsxs)("strong",{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)]}),". Spend history is preserved in logs. This resets the current period spend counter, the same as an automatic budget reset."]})]}),(0,t.jsxs)(p.TabGroup,{children:[(0,t.jsxs)(g.TabList,{className:"mb-4",children:[(0,t.jsx)(x.Tab,{children:"Overview"}),(0,t.jsx)(x.Tab,{children:"Settings"})]}),(0,t.jsxs)(_.TabPanels,{children:[(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(u.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Spend"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(y.Title,{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)]}),(0,t.jsxs)(j.Text,{children:["of ",eE]})]})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Rate Limits"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(j.Text,{children:["TPM: ",null!==ey.tpm_limit?ey.tpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["RPM: ",null!==ey.rpm_limit?ey.rpm_limit:"Unlimited"]}),!!ey.metadata?.throttle_on_budget_exceeded&&(0,t.jsx)(j.Text,{children:"Throttle on budget exceeded: Yes"})]})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Models"}),(0,t.jsx)("div",{className:"mt-2 flex flex-wrap gap-2",children:ey.models&&ey.models.length>0?ey.models.map((e,a)=>(0,t.jsx)(d.Badge,{color:"red",children:e},a)):(0,t.jsx)(j.Text,{children:"No models specified"})})]}),(0,t.jsx)(m.Card,{children:(0,t.jsx)(P.default,{objectPermission:ey.object_permission,variant:"inline",accessToken:W})}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{className:"font-medium mb-3",children:"Guardrails"}),Array.isArray(ey.metadata?.guardrails)&&ey.metadata.guardrails.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:ey.metadata.guardrails.map((e,a)=>(0,t.jsx)(d.Badge,{color:"blue",children:e},a))}):(0,t.jsx)(j.Text,{className:"text-gray-500",children:"No guardrails configured"}),"boolean"==typeof ey.metadata?.disable_global_guardrails&&!0===ey.metadata.disable_global_guardrails&&(0,t.jsx)("div",{className:"mt-3 pt-3 border-t border-gray-200",children:(0,t.jsx)(d.Badge,{color:"yellow",children:"Global Guardrails Disabled"})})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{className:"font-medium mb-3",children:"Policies"}),Array.isArray(ey.metadata?.policies)&&ey.metadata.policies.length>0?(0,t.jsx)("div",{className:"space-y-4",children:ey.metadata.policies.map((e,a)=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(d.Badge,{color:"purple",children:e}),eS&&(0,t.jsx)(j.Text,{className:"text-xs text-gray-400",children:"Loading guardrails..."})]}),!eS&&eT[e]&&eT[e].length>0&&(0,t.jsxs)("div",{className:"ml-4 pl-3 border-l-2 border-gray-200",children:[(0,t.jsx)(j.Text,{className:"text-xs text-gray-500 mb-1",children:"Resolved Guardrails:"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:eT[e].map((e,a)=>(0,t.jsx)(d.Badge,{color:"blue",size:"xs",children:e},a))})]})]},a))}):(0,t.jsx)(j.Text,{className:"text-gray-500",children:"No policies configured"})]}),(0,t.jsx)(A.default,{loggingConfigs:(0,I.extractLoggingSettings)(ey.metadata),disabledCallbacks:Array.isArray(ey.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(ey.metadata.litellm_disabled_callbacks):[],variant:"card"}),(0,t.jsx)(S.default,{autoRotate:ey.auto_rotate,rotationInterval:ey.rotation_interval,lastRotationAt:ey.last_rotation_at,keyRotationAt:ey.key_rotation_at,nextRotationAt:ey.next_rotation_at,variant:"card"})]})}),(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(m.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(y.Title,{children:"Key Settings"}),!et&&eM&&(0,t.jsx)(c.Button,{onClick:()=>ea(!0),children:"Edit Settings"})]}),et?(0,t.jsx)(em,{keyData:ey,onCancel:()=>ea(!1),onSubmit:eI,teams:z,accessToken:W,userID:G,userRole:q,premiumUser:H}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Key ID"}),(0,t.jsx)(j.Text,{className:"font-mono",children:ey.token_id||ey.token})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Key Alias"}),(0,t.jsx)(j.Text,{children:ey.key_alias||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Secret Key"}),(0,t.jsx)(j.Text,{className:"font-mono",children:ey.key_name})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Team ID"}),(0,t.jsx)(j.Text,{children:ey.team_id||"Not Set"})]}),ee&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Project"}),(0,t.jsx)(j.Text,{children:ey.project_id?(V=X?.find(e=>e.project_id===ey.project_id),V?.project_alias?`${V.project_alias} (${ey.project_id})`:ey.project_id):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Organization"}),(0,t.jsx)(j.Text,{children:(ey.organization_id??ey.org_id)||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Created"}),(0,t.jsx)(j.Text,{children:eF(ey.created_at)})]}),ef&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Last Regenerated"}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(j.Text,{children:eF(ef)}),(0,t.jsx)(d.Badge,{color:"green",size:"xs",children:"Recent"})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Expires"}),(0,t.jsx)(j.Text,{children:ey.expires?eF(ey.expires):"Never"})]}),(0,t.jsx)(S.default,{autoRotate:ey.auto_rotate,rotationInterval:ey.rotation_interval,lastRotationAt:ey.last_rotation_at,keyRotationAt:ey.key_rotation_at,nextRotationAt:ey.next_rotation_at,variant:"inline",className:"pt-4 border-t border-gray-200"}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Spend"}),(0,t.jsxs)(j.Text,{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)," USD"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Budget"}),(0,t.jsx)(j.Text,{children:null!==ey.max_budget?`$${(0,r.formatNumberWithCommas)(ey.max_budget,2)}`:"Unlimited"})]}),ey.budget_fallbacks&&Object.keys(ey.budget_fallbacks).length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Budget Fallbacks"}),(0,t.jsx)("div",{className:"mt-1 space-y-1",children:Object.entries(ey.budget_fallbacks).map(([e,a])=>(0,t.jsxs)("div",{className:"text-xs text-gray-600",children:[(0,t.jsx)("span",{className:"font-medium",children:e}),(0,t.jsx)("span",{className:"mx-1 text-gray-400",children:"->"}),a.join(", ")]},e))})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Tags"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:Array.isArray(ey.metadata?.tags)&&ey.metadata.tags.length>0?ey.metadata.tags.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No tags specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Prompts"}),(0,t.jsx)(j.Text,{children:Array.isArray(ey.metadata?.prompts)&&ey.metadata.prompts.length>0?ey.metadata.prompts.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No prompts specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Allowed Routes"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:Array.isArray(ey.allowed_routes)&&ey.allowed_routes.length>0?ey.allowed_routes.map((e,a)=>(0,t.jsx)("span",{className:"px-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):(0,t.jsx)(v.Tag,{color:"green",children:"All routes allowed"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Allowed Pass Through Routes"}),(0,t.jsx)(j.Text,{children:Array.isArray(ey.metadata?.allowed_passthrough_routes)&&ey.metadata.allowed_passthrough_routes.length>0?ey.metadata.allowed_passthrough_routes.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No pass through routes specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Disable Global Guardrails"}),(0,t.jsx)(j.Text,{children:ey.metadata?.disable_global_guardrails===!0?(0,t.jsx)(d.Badge,{color:"yellow",children:"Enabled - Global guardrails bypassed"}):(0,t.jsx)(d.Badge,{color:"green",children:"Disabled - Global guardrails active"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Models"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:ey.models&&ey.models.length>0?ey.models.map((e,a)=>(0,t.jsx)("span",{className:"px-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):(0,t.jsx)(j.Text,{children:"No models specified"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Rate Limits"}),(0,t.jsxs)(j.Text,{children:["TPM: ",null!==ey.tpm_limit?ey.tpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["RPM: ",null!==ey.rpm_limit?ey.rpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Max Parallel Requests:"," ",null!==ey.max_parallel_requests?ey.max_parallel_requests:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Model TPM Limits:"," ",ey.metadata?.model_tpm_limit?JSON.stringify(ey.metadata.model_tpm_limit):"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Model RPM Limits:"," ",ey.metadata?.model_rpm_limit?JSON.stringify(ey.metadata.model_rpm_limit):"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Tag RPM Limits:"," ",ey.metadata?.tag_rpm_limit&&Object.keys(ey.metadata.tag_rpm_limit).length>0?JSON.stringify(ey.metadata.tag_rpm_limit):"Unlimited"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Metadata"}),(0,t.jsx)("pre",{className:"bg-gray-100 p-2 rounded-sm text-xs overflow-auto mt-1",children:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(ey.metadata))})]}),(0,t.jsx)(P.default,{objectPermission:ey.object_permission,variant:"inline",className:"pt-4 border-t border-gray-200",accessToken:W}),(0,t.jsx)(A.default,{loggingConfigs:(0,I.extractLoggingSettings)(ey.metadata),disabledCallbacks:Array.isArray(ey.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(ey.metadata.litellm_disabled_callbacks):[],variant:"inline",className:"pt-4 border-t border-gray-200"})]})]})})]})]})]})}],20147)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0rle8dv-1hl2i.js b/litellm/proxy/_experimental/out/_next/static/chunks/0rle8dv-1hl2i.js new file mode 100644 index 00000000000..51f9104ee79 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0rle8dv-1hl2i.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,184163,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"download",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["default",0,i],184163)},309821,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(135551),n=e.i(201072),o=e.i(121229),i=e.i(726289),a=e.i(864517),l=e.i(343794),s=e.i(529681),c=e.i(242064),u=e.i(931067),d=e.i(209428),p=e.i(703923),f={percent:0,prefixCls:"rc-progress",strokeColor:"#2db7f5",strokeLinecap:"round",strokeWidth:1,trailColor:"#D9D9D9",trailWidth:1,gapPosition:"bottom"},g=function(){var e=(0,t.useRef)([]),r=(0,t.useRef)(null);return(0,t.useEffect)(function(){var t=Date.now(),n=!1;e.current.forEach(function(e){if(e){n=!0;var o=e.style;o.transitionDuration=".3s, .3s, .3s, .06s",r.current&&t-r.current<100&&(o.transitionDuration="0s, 0s")}}),n&&(r.current=Date.now())}),e.current},m=e.i(410160),h=e.i(392221),b=e.i(654310),v=0,y=(0,b.default)();let x=function(e){var r=t.useState(),n=(0,h.default)(r,2),o=n[0],i=n[1];return t.useEffect(function(){var e;i("rc_progress_".concat((y?(e=v,v+=1):e="TEST_OR_SSR",e)))},[]),e||o};var k=function(e){var r=e.bg,n=e.children;return t.createElement("div",{style:{width:"100%",height:"100%",background:r}},n)};function $(e,t){return Object.keys(e).map(function(r){var n=parseFloat(r),o="".concat(Math.floor(n*t),"%");return"".concat(e[r]," ").concat(o)})}var C=t.forwardRef(function(e,r){var n=e.prefixCls,o=e.color,i=e.gradientId,a=e.radius,l=e.style,s=e.ptg,c=e.strokeLinecap,u=e.strokeWidth,d=e.size,p=e.gapDegree,f=o&&"object"===(0,m.default)(o),g=d/2,h=t.createElement("circle",{className:"".concat(n,"-circle-path"),r:a,cx:g,cy:g,stroke:f?"#FFF":void 0,strokeLinecap:c,strokeWidth:u,opacity:+(0!==s),style:l,ref:r});if(!f)return h;var b="".concat(i,"-conic"),v=$(o,(360-p)/360),y=$(o,1),x="conic-gradient(from ".concat(p?"".concat(180+p/2,"deg"):"0deg",", ").concat(v.join(", "),")"),C="linear-gradient(to ".concat(p?"bottom":"top",", ").concat(y.join(", "),")");return t.createElement(t.Fragment,null,t.createElement("mask",{id:b},h),t.createElement("foreignObject",{x:0,y:0,width:d,height:d,mask:"url(#".concat(b,")")},t.createElement(k,{bg:C},t.createElement(k,{bg:x}))))}),w=function(e,t,r,n,o,i,a,l,s,c){var u=arguments.length>10&&void 0!==arguments[10]?arguments[10]:0,d=(100-n)/100*t;return"round"===s&&100!==n&&(d+=c/2)>=t&&(d=t-.01),{stroke:"string"==typeof l?l:void 0,strokeDasharray:"".concat(t,"px ").concat(e),strokeDashoffset:d+u,transform:"rotate(".concat(o+r/100*360*((360-i)/360)+(0===i?0:({bottom:0,top:180,left:90,right:-90})[a]),"deg)"),transformOrigin:"".concat(50,"px ").concat(50,"px"),transition:"stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s",fillOpacity:0}},S=["id","prefixCls","steps","strokeWidth","trailWidth","gapDegree","gapPosition","trailColor","strokeLinecap","style","className","strokeColor","percent"];function E(e){var t=null!=e?e:[];return Array.isArray(t)?t:[t]}let O=function(e){var r,n,o,i,a=(0,d.default)((0,d.default)({},f),e),s=a.id,c=a.prefixCls,h=a.steps,b=a.strokeWidth,v=a.trailWidth,y=a.gapDegree,k=void 0===y?0:y,$=a.gapPosition,O=a.trailColor,j=a.strokeLinecap,_=a.style,N=a.className,I=a.strokeColor,D=a.percent,M=(0,p.default)(a,S),P=x(s),A="".concat(P,"-gradient"),T=50-b/2,z=2*Math.PI*T,R=k>0?90+k/2:-90,W=(360-k)/360*z,L="object"===(0,m.default)(h)?h:{count:h,gap:2},F=L.count,H=L.gap,B=E(D),X=E(I),V=X.find(function(e){return e&&"object"===(0,m.default)(e)}),U=V&&"object"===(0,m.default)(V)?"butt":j,K=w(z,W,0,100,R,k,$,O,U,b),q=g();return t.createElement("svg",(0,u.default)({className:(0,l.default)("".concat(c,"-circle"),N),viewBox:"0 0 ".concat(100," ").concat(100),style:_,id:s,role:"presentation"},M),!F&&t.createElement("circle",{className:"".concat(c,"-circle-trail"),r:T,cx:50,cy:50,stroke:O,strokeLinecap:U,strokeWidth:v||b,style:K}),F?(r=Math.round(F*(B[0]/100)),n=100/F,o=0,Array(F).fill(null).map(function(e,i){var a=i<=r-1?X[0]:O,l=a&&"object"===(0,m.default)(a)?"url(#".concat(A,")"):void 0,s=w(z,W,o,n,R,k,$,a,"butt",b,H);return o+=(W-s.strokeDashoffset+H)*100/W,t.createElement("circle",{key:i,className:"".concat(c,"-circle-path"),r:T,cx:50,cy:50,stroke:l,strokeWidth:b,opacity:1,style:s,ref:function(e){q[i]=e}})})):(i=0,B.map(function(e,r){var n=X[r]||X[X.length-1],o=w(z,W,i,e,R,k,$,n,U,b);return i+=e,t.createElement(C,{key:r,color:n,ptg:e,radius:T,prefixCls:c,gradientId:A,style:o,strokeLinecap:U,strokeWidth:b,gapDegree:k,ref:function(e){q[r]=e},size:100})}).reverse()))};var j=e.i(491816);e.i(765846);var _=e.i(896091);function N(e){return!e||e<0?0:e>100?100:e}function I({success:e,successPercent:t}){let r=t;return e&&"progress"in e&&(r=e.progress),e&&"percent"in e&&(r=e.percent),r}let D=(e,t,r)=>{var n,o,i,a;let l=-1,s=-1;if("step"===t){let t=r.steps,n=r.strokeWidth;"string"==typeof e||void 0===e?(l="small"===e?2:14,s=null!=n?n:8):"number"==typeof e?[l,s]=[e,e]:[l=14,s=8]=Array.isArray(e)?e:[e.width,e.height],l*=t}else if("line"===t){let t=null==r?void 0:r.strokeWidth;"string"==typeof e||void 0===e?s=t||("small"===e?6:8):"number"==typeof e?[l,s]=[e,e]:[l=-1,s=8]=Array.isArray(e)?e:[e.width,e.height]}else("circle"===t||"dashboard"===t)&&("string"==typeof e||void 0===e?[l,s]="small"===e?[60,60]:[120,120]:"number"==typeof e?[l,s]=[e,e]:Array.isArray(e)&&(l=null!=(o=null!=(n=e[0])?n:e[1])?o:120,s=null!=(a=null!=(i=e[0])?i:e[1])?a:120));return[l,s]},M=e=>{let{prefixCls:r,trailColor:n=null,strokeLinecap:o="round",gapPosition:i,gapDegree:a,width:s=120,type:c,children:u,success:d,size:p=s,steps:f}=e,[g,m]=D(p,"circle"),{strokeWidth:h}=e;void 0===h&&(h=Math.max(3/g*100,6));let b=t.useMemo(()=>a||0===a?a:"dashboard"===c?75:void 0,[a,c]),v=(({percent:e,success:t,successPercent:r})=>{let n=N(I({success:t,successPercent:r}));return[n,N(N(e)-n)]})(e),y="[object Object]"===Object.prototype.toString.call(e.strokeColor),x=(({success:e={},strokeColor:t})=>{let{strokeColor:r}=e;return[r||_.presetPrimaryColors.green,t||null]})({success:d,strokeColor:e.strokeColor}),k=(0,l.default)(`${r}-inner`,{[`${r}-circle-gradient`]:y}),$=t.createElement(O,{steps:f,percent:f?v[1]:v,strokeWidth:h,trailWidth:h,strokeColor:f?x[1]:x,strokeLinecap:o,trailColor:n,prefixCls:r,gapDegree:b,gapPosition:i||"dashboard"===c&&"bottom"||void 0}),C=g<=20,w=t.createElement("div",{className:k,style:{width:g,height:m,fontSize:.15*g+6}},$,!C&&u);return C?t.createElement(j.default,{title:u},w):w};e.i(296059);var P=e.i(694758),A=e.i(915654),T=e.i(183293),z=e.i(246422),R=e.i(838378);let W="--progress-line-stroke-color",L="--progress-percent",F=e=>{let t=e?"100%":"-100%";return new P.Keyframes(`antProgress${e?"RTL":"LTR"}Active`,{"0%":{transform:`translateX(${t}) scaleX(0)`,opacity:.1},"20%":{transform:`translateX(${t}) scaleX(0)`,opacity:.5},to:{transform:"translateX(0) scaleX(1)",opacity:0}})},H=(0,z.genStyleHooks)("Progress",e=>{let t=e.calc(e.marginXXS).div(2).equal(),r=(0,R.mergeToken)(e,{progressStepMarginInlineEnd:t,progressStepMinWidth:t,progressActiveMotionDuration:"2.4s"});return[(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:Object.assign(Object.assign({},(0,T.resetComponent)(e)),{display:"inline-block","&-rtl":{direction:"rtl"},"&-line":{position:"relative",width:"100%",fontSize:e.fontSize},[`${t}-outer`]:{display:"inline-flex",alignItems:"center",width:"100%"},[`${t}-inner`]:{position:"relative",display:"inline-block",width:"100%",flex:1,overflow:"hidden",verticalAlign:"middle",backgroundColor:e.remainingColor,borderRadius:e.lineBorderRadius},[`${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.defaultColor}},[`${t}-success-bg, ${t}-bg`]:{position:"relative",background:e.defaultColor,borderRadius:e.lineBorderRadius,transition:`all ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`},[`${t}-layout-bottom`]:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",[`${t}-text`]:{width:"max-content",marginInlineStart:0,marginTop:e.marginXXS}},[`${t}-bg`]:{overflow:"hidden","&::after":{content:'""',background:{_multi_value_:!0,value:["inherit",`var(${W})`]},height:"100%",width:`calc(1 / var(${L}) * 100%)`,display:"block"},[`&${t}-bg-inner`]:{minWidth:"max-content","&::after":{content:"none"},[`${t}-text-inner`]:{color:e.colorWhite,[`&${t}-text-bright`]:{color:"rgba(0, 0, 0, 0.45)"}}}},[`${t}-success-bg`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,backgroundColor:e.colorSuccess},[`${t}-text`]:{display:"inline-block",marginInlineStart:e.marginXS,color:e.colorText,lineHeight:1,width:"2em",whiteSpace:"nowrap",textAlign:"start",verticalAlign:"middle",wordBreak:"normal",[r]:{fontSize:e.fontSize},[`&${t}-text-outer`]:{width:"max-content"},[`&${t}-text-outer${t}-text-start`]:{width:"max-content",marginInlineStart:0,marginInlineEnd:e.marginXS}},[`${t}-text-inner`]:{display:"flex",justifyContent:"center",alignItems:"center",width:"100%",height:"100%",marginInlineStart:0,padding:`0 ${(0,A.unit)(e.paddingXXS)}`,[`&${t}-text-start`]:{justifyContent:"start"},[`&${t}-text-end`]:{justifyContent:"end"}},[`&${t}-status-active`]:{[`${t}-bg::before`]:{position:"absolute",inset:0,backgroundColor:e.colorBgContainer,borderRadius:e.lineBorderRadius,opacity:0,animationName:F(),animationDuration:e.progressActiveMotionDuration,animationTimingFunction:e.motionEaseOutQuint,animationIterationCount:"infinite",content:'""'}},[`&${t}-rtl${t}-status-active`]:{[`${t}-bg::before`]:{animationName:F(!0)}},[`&${t}-status-exception`]:{[`${t}-bg`]:{backgroundColor:e.colorError},[`${t}-text`]:{color:e.colorError}},[`&${t}-status-exception ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorError}},[`&${t}-status-success`]:{[`${t}-bg`]:{backgroundColor:e.colorSuccess},[`${t}-text`]:{color:e.colorSuccess}},[`&${t}-status-success ${t}-inner:not(${t}-circle-gradient)`]:{[`${t}-circle-path`]:{stroke:e.colorSuccess}}})}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-circle-trail`]:{stroke:e.remainingColor},[`&${t}-circle ${t}-inner`]:{position:"relative",lineHeight:1,backgroundColor:"transparent"},[`&${t}-circle ${t}-text`]:{position:"absolute",insetBlockStart:"50%",insetInlineStart:0,width:"100%",margin:0,padding:0,color:e.circleTextColor,fontSize:e.circleTextFontSize,lineHeight:1,whiteSpace:"normal",textAlign:"center",transform:"translateY(-50%)",[r]:{fontSize:e.circleIconFontSize}},[`${t}-circle&-status-exception`]:{[`${t}-text`]:{color:e.colorError}},[`${t}-circle&-status-success`]:{[`${t}-text`]:{color:e.colorSuccess}}},[`${t}-inline-circle`]:{lineHeight:1,[`${t}-inner`]:{verticalAlign:"bottom"}}}})(r),(e=>{let{componentCls:t}=e;return{[t]:{[`${t}-steps`]:{display:"inline-block","&-outer":{display:"flex",flexDirection:"row",alignItems:"center"},"&-item":{flexShrink:0,minWidth:e.progressStepMinWidth,marginInlineEnd:e.progressStepMarginInlineEnd,backgroundColor:e.remainingColor,transition:`all ${e.motionDurationSlow}`,"&-active":{backgroundColor:e.defaultColor}}}}}})(r),(e=>{let{componentCls:t,iconCls:r}=e;return{[t]:{[`${t}-small&-line, ${t}-small&-line ${t}-text ${r}`]:{fontSize:e.fontSizeSM}}}})(r)]},e=>({circleTextColor:e.colorText,defaultColor:e.colorInfo,remainingColor:e.colorFillSecondary,lineBorderRadius:100,circleTextFontSize:"1em",circleIconFontSize:`${e.fontSize/e.fontSizeSM}em`}));var B=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let X=e=>{let{prefixCls:r,direction:n,percent:o,size:i,strokeWidth:a,strokeColor:s,strokeLinecap:c="round",children:u,trailColor:d=null,percentPosition:p,success:f}=e,{align:g,type:m}=p,h=s&&"string"!=typeof s?((e,t)=>{let{from:r=_.presetPrimaryColors.blue,to:n=_.presetPrimaryColors.blue,direction:o="rtl"===t?"to left":"to right"}=e,i=B(e,["from","to","direction"]);if(0!==Object.keys(i).length){let e,t=(e=[],Object.keys(i).forEach(t=>{let r=Number.parseFloat(t.replace(/%/g,""));Number.isNaN(r)||e.push({key:r,value:i[t]})}),(e=e.sort((e,t)=>e.key-t.key)).map(({key:e,value:t})=>`${t} ${e}%`).join(", ")),r=`linear-gradient(${o}, ${t})`;return{background:r,[W]:r}}let a=`linear-gradient(${o}, ${r}, ${n})`;return{background:a,[W]:a}})(s,n):{[W]:s,background:s},b="square"===c||"butt"===c?0:void 0,[v,y]=D(null!=i?i:[-1,a||("small"===i?6:8)],"line",{strokeWidth:a}),x=Object.assign(Object.assign({width:`${N(o)}%`,height:y,borderRadius:b},h),{[L]:N(o)/100}),k=I(e),$={width:`${N(k)}%`,height:y,borderRadius:b,backgroundColor:null==f?void 0:f.strokeColor},C=t.createElement("div",{className:`${r}-inner`,style:{backgroundColor:d||void 0,borderRadius:b}},t.createElement("div",{className:(0,l.default)(`${r}-bg`,`${r}-bg-${m}`),style:x},"inner"===m&&u),void 0!==k&&t.createElement("div",{className:`${r}-success-bg`,style:$})),w="outer"===m&&"start"===g,S="outer"===m&&"end"===g;return"outer"===m&&"center"===g?t.createElement("div",{className:`${r}-layout-bottom`},C,u):t.createElement("div",{className:`${r}-outer`,style:{width:v<0?"100%":v}},w&&u,C,S&&u)},V=e=>{let{size:r,steps:n,rounding:o=Math.round,percent:i=0,strokeWidth:a=8,strokeColor:s,trailColor:c=null,prefixCls:u,children:d}=e,p=o(i/100*n),[f,g]=D(null!=r?r:["small"===r?2:14,a],"step",{steps:n,strokeWidth:a}),m=f/n,h=Array.from({length:n});for(let e=0;et.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let K=["normal","exception","active","success"],q=t.forwardRef((e,u)=>{let d,{prefixCls:p,className:f,rootClassName:g,steps:m,strokeColor:h,percent:b=0,size:v="default",showInfo:y=!0,type:x="line",status:k,format:$,style:C,percentPosition:w={}}=e,S=U(e,["prefixCls","className","rootClassName","steps","strokeColor","percent","size","showInfo","type","status","format","style","percentPosition"]),{align:E="end",type:O="outer"}=w,j=Array.isArray(h)?h[0]:h,_="string"==typeof h||Array.isArray(h)?h:void 0,P=t.useMemo(()=>{if(j){let e="string"==typeof j?j:Object.values(j)[0];return new r.FastColor(e).isLight()}return!1},[h]),A=t.useMemo(()=>{var t,r;let n=I(e);return Number.parseInt(void 0!==n?null==(t=null!=n?n:0)?void 0:t.toString():null==(r=null!=b?b:0)?void 0:r.toString(),10)},[b,e.success,e.successPercent]),T=t.useMemo(()=>!K.includes(k)&&A>=100?"success":k||"normal",[k,A]),{getPrefixCls:z,direction:R,progress:W}=t.useContext(c.ConfigContext),L=z("progress",p),[F,B,q]=H(L),Q="line"===x,Y=Q&&!m,G=t.useMemo(()=>{let r;if(!y)return null;let s=I(e),c=$||(e=>`${e}%`),u=Q&&P&&"inner"===O;return"inner"===O||$||"exception"!==T&&"success"!==T?r=c(N(b),N(s)):"exception"===T?r=Q?t.createElement(i.default,null):t.createElement(a.default,null):"success"===T&&(r=Q?t.createElement(n.default,null):t.createElement(o.default,null)),t.createElement("span",{className:(0,l.default)(`${L}-text`,{[`${L}-text-bright`]:u,[`${L}-text-${E}`]:Y,[`${L}-text-${O}`]:Y}),title:"string"==typeof r?r:void 0},r)},[y,b,A,T,x,L,$]);"line"===x?d=m?t.createElement(V,Object.assign({},e,{strokeColor:_,prefixCls:L,steps:"object"==typeof m?m.count:m}),G):t.createElement(X,Object.assign({},e,{strokeColor:j,prefixCls:L,direction:R,percentPosition:{align:E,type:O}}),G):("circle"===x||"dashboard"===x)&&(d=t.createElement(M,Object.assign({},e,{strokeColor:j,prefixCls:L,progressStatus:T}),G));let J=(0,l.default)(L,`${L}-status-${T}`,{[`${L}-${"dashboard"===x&&"circle"||x}`]:"line"!==x,[`${L}-inline-circle`]:"circle"===x&&D(v,"circle")[0]<=20,[`${L}-line`]:Y,[`${L}-line-align-${E}`]:Y,[`${L}-line-position-${O}`]:Y,[`${L}-steps`]:m,[`${L}-show-info`]:y,[`${L}-${v}`]:"string"==typeof v,[`${L}-rtl`]:"rtl"===R},null==W?void 0:W.className,f,g,B,q);return F(t.createElement("div",Object.assign({ref:u,style:Object.assign(Object.assign({},null==W?void 0:W.style),C),className:J,role:"progressbar","aria-valuenow":A,"aria-valuemin":0,"aria-valuemax":100},(0,s.default)(S,["trailColor","strokeWidth","width","gapDegree","gapPosition","strokeLinecap","success","successPercent"])),d))});e.s(["default",0,q],309821)},993914,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"}}]},name:"file-text",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["FileTextOutlined",0,i],993914)},435451,e=>{"use strict";var t=e.i(843476),r=e.i(290571),n=e.i(271645);let o=e=>{var t=(0,r.__rest)(e,[]);return n.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),n.default.createElement("path",{d:"M12 4v16m8-8H4"}))},i=e=>{var t=(0,r.__rest)(e,[]);return n.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),n.default.createElement("path",{d:"M20 12H4"}))};var a=e.i(444755),l=e.i(673706),s=e.i(677955);let c="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",u="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",d=n.default.forwardRef((e,t)=>{let{onSubmit:d,enableStepper:p=!0,disabled:f,onValueChange:g,onChange:m}=e,h=(0,r.__rest)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),b=(0,n.useRef)(null),[v,y]=n.default.useState(!1),x=n.default.useCallback(()=>{y(!0)},[]),k=n.default.useCallback(()=>{y(!1)},[]),[$,C]=n.default.useState(!1),w=n.default.useCallback(()=>{C(!0)},[]),S=n.default.useCallback(()=>{C(!1)},[]);return n.default.createElement(s.default,Object.assign({type:"number",ref:(0,l.mergeRefs)([b,t]),disabled:f,makeInputClassName:(0,l.makeClassName)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null==(t=b.current)?void 0:t.value;null==d||d(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&x(),"ArrowUp"===e.key&&w()},onKeyUp:e=>{"ArrowDown"===e.key&&k(),"ArrowUp"===e.key&&S()},onChange:e=>{f||(null==g||g(parseFloat(e.target.value)),null==m||m(e))},stepper:p?n.default.createElement("div",{className:(0,a.tremorTwMerge)("flex justify-center align-middle")},n.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;f||(null==(e=b.current)||e.stepDown(),null==(t=b.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,a.tremorTwMerge)(!f&&u,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},n.default.createElement(i,{"data-testid":"step-down",className:(v?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),n.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;f||(null==(e=b.current)||e.stepUp(),null==(t=b.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,a.tremorTwMerge)(!f&&u,c,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},n.default.createElement(o,{"data-testid":"step-up",className:($?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},h))});d.displayName="NumberInput",e.s(["default",0,({step:e=.01,style:r={width:"100%"},placeholder:n="Enter a numerical value",min:o,max:i,onChange:a,...l})=>(0,t.jsx)(d,{onWheel:e=>e.currentTarget.blur(),step:e,style:r,placeholder:n,min:o,max:i,onChange:a,...l})],435451)},519756,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"upload",theme:"outlined"};var o=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(o.default,(0,t.default)({},e,{ref:i,icon:n}))});e.s(["UploadOutlined",0,i],519756)},663435,152473,e=>{"use strict";var t=e.i(843476),r=e.i(271645),n=e.i(199133),o=e.i(898586),i=e.i(56456);let a={enabled:!0,leading:!1,trailing:!0,wait:0,onExecute:()=>{}};class l{constructor(e,t){this.fn=e,this._canLeadingExecute=!0,this._isPending=!1,this._executionCount=0,this._options={...a,...t}}setOptions(e){return this._options={...this._options,...e},this._options.enabled||(this._isPending=!1),this._options}getOptions(){return this._options}maybeExecute(...e){this._options.leading&&this._canLeadingExecute&&(this.executeFunction(...e),this._canLeadingExecute=!1),(this._options.leading||this._options.trailing)&&(this._isPending=!0),this._timeoutId&&clearTimeout(this._timeoutId),this._timeoutId=setTimeout(()=>{this._canLeadingExecute=!0,this._isPending=!1,this._options.trailing&&this.executeFunction(...e)},this._options.wait)}executeFunction(...e){this._options.enabled&&(this.fn(...e),this._executionCount++,this._options.onExecute(this))}cancel(){this._timeoutId&&(clearTimeout(this._timeoutId),this._canLeadingExecute=!0,this._isPending=!1)}getExecutionCount(){return this._executionCount}getIsPending(){return this._options.enabled&&this._isPending}}function s(e,t){let[n,o]=(0,r.useState)(e),i=function(e,t){let[n]=(0,r.useState)(()=>{var r;return Object.getOwnPropertyNames(Object.getPrototypeOf(r=new l(e,t))).filter(e=>"function"==typeof r[e]).reduce((e,t)=>{let n=r[t];return"function"==typeof n&&(e[t]=n.bind(r)),e},{})});return n.setOptions(t),n}(o,t);return[n,i.maybeExecute,i]}e.s(["useDebouncedState",0,s],152473);var c=e.i(785242);let{Text:u}=o.Typography;e.s(["default",0,({value:e,onChange:o,onTeamSelect:a,disabled:l,organizationId:d,pageSize:p=20})=>{let[f,g]=(0,r.useState)(""),[m,h]=s("",{wait:300}),{data:b,fetchNextPage:v,hasNextPage:y,isFetchingNextPage:x,isLoading:k}=(0,c.useInfiniteTeams)(p,m||void 0,d),$=(0,r.useMemo)(()=>{if(!b?.pages)return[];let e=new Set,t=[];for(let r of b.pages)for(let n of r.teams)e.has(n.team_id)||(e.add(n.team_id),t.push(n));return t},[b]);return(0,t.jsx)(n.Select,{showSearch:!0,placeholder:"Search or select a team",value:e||void 0,onChange:e=>{o?.(e??""),a&&a(e?$.find(t=>t.team_id===e)??null:null)},disabled:l,allowClear:!0,filterOption:!1,onSearch:e=>{g(e),h(e)},searchValue:f,onPopupScroll:e=>{let t=e.currentTarget;(t.scrollTop+t.clientHeight)/t.scrollHeight>=.8&&y&&!x&&v()},loading:k,notFoundContent:k?(0,t.jsx)(i.LoadingOutlined,{spin:!0}):"No teams found","data-testid":"team-dropdown",popupRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,x&&(0,t.jsx)("div",{style:{textAlign:"center",padding:8},children:(0,t.jsx)(i.LoadingOutlined,{spin:!0})})]}),children:$.map(e=>(0,t.jsxs)(n.Select.Option,{value:e.team_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.team_alias})," ",(0,t.jsxs)(u,{type:"secondary",children:["(",e.team_id,")"]})]},e.team_id))})}],663435)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0ror7df3rm9k-.js b/litellm/proxy/_experimental/out/_next/static/chunks/0ror7df3rm9k-.js new file mode 100644 index 00000000000..fc6617d87ab --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0ror7df3rm9k-.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},637235,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"}}]},name:"clock-circle",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["ClockCircleOutlined",0,l],637235)},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["default",0,l],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),o=e.i(540143),a=e.i(915823),l=e.i(619273),s=class extends a.Subscribable{#e;#t=void 0;#r;#o;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#a()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,l.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,l.hashKey)(t.mutationKey)!==(0,l.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#a(),this.#l(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#a(),this.#l()}mutate(e,t){return this.#o=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#a(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#l(e){o.notifyManager.batch(()=>{if(this.#o&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,o={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#o.onSuccess?.(e.data,t,r,o)}catch(e){Promise.reject(e)}try{this.#o.onSettled?.(e.data,null,t,r,o)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#o.onError?.(e.error,t,r,o)}catch(e){Promise.reject(e)}try{this.#o.onSettled?.(void 0,e.error,t,r,o)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},n=e.i(912598);e.s(["useMutation",0,function(e,r){let a=(0,n.useQueryClient)(r),[i]=t.useState(()=>new s(a,e));t.useEffect(()=>{i.setOptions(e)},[i,e]);let c=t.useSyncExternalStore(t.useCallback(e=>i.subscribe(o.notifyManager.batchCalls(e)),[i]),()=>i.getCurrentResult(),()=>i.getCurrentResult()),d=t.useCallback((e,t)=>{i.mutate(e,t).catch(l.noop)},[i]);if(c.error&&(0,l.shouldThrowError)(i.options.throwOnError,[c.error]))throw c.error;return{...c,mutate:d,mutateAsync:c.mutate}}],954616)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var a=e.i(9583),l=r.forwardRef(function(e,l){return r.createElement(a.default,(0,t.default)({},e,{ref:l,icon:o}))});e.s(["ExclamationCircleOutlined",0,l],270377)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),r=e.i(732961),o=e.i(289882),a=e.i(170517),l=e.i(628882),s=e.i(320890),n=e.i(104458),i=e.i(722319),c=e.i(8398),d=e.i(279728);e.i(765846);var u=e.i(602716),m=e.i(328052),g=e.i(135551);let f=(e,t)=>new g.FastColor(e).setA(t).toRgbString(),p=(e,t)=>new g.FastColor(e).lighten(t).toHexString(),h=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},b=(e,t)=>{let r=e||"#000",o=t||"#fff";return{colorBgBase:r,colorTextBase:o,colorText:f(o,.85),colorTextSecondary:f(o,.65),colorTextTertiary:f(o,.45),colorTextQuaternary:f(o,.25),colorFill:f(o,.18),colorFillSecondary:f(o,.12),colorFillTertiary:f(o,.08),colorFillQuaternary:f(o,.04),colorBgSolid:f(o,.95),colorBgSolidHover:f(o,1),colorBgSolidActive:f(o,.9),colorBgElevated:p(r,12),colorBgContainer:p(r,8),colorBgLayout:p(r,0),colorBgSpotlight:p(r,26),colorBgBlur:f(o,.04),colorBorder:p(r,26),colorBorderSecondary:p(r,19)}},C={defaultSeed:s.defaultConfig.token,useToken:function(){let[e,t,r]=(0,n.useToken)();return{theme:e,token:t,hashId:r}},defaultAlgorithm:i.default,darkAlgorithm:(e,t)=>{let r=Object.keys(a.defaultPresetColors).map(t=>{let r=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,o,a)=>(e[`${t}-${a+1}`]=r[a],e[`${t}${a+1}`]=r[a],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),o=null!=t?t:(0,i.default)(e),l=(0,m.default)(e,{generateColorPalettes:h,generateNeutralColorPalettes:b});return Object.assign(Object.assign(Object.assign(Object.assign({},o),r),l),{colorPrimaryBg:l.colorPrimaryBorder,colorPrimaryBgHover:l.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let r=null!=t?t:(0,i.default)(e),o=r.fontSizeSM,a=r.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},r),function(e){let{sizeUnit:t,sizeStep:r}=e,o=r-2;return{sizeXXL:t*(o+10),sizeXL:t*(o+6),sizeLG:t*(o+2),sizeMD:t*(o+2),sizeMS:t*(o+1),size:t*o,sizeSM:t*o,sizeXS:t*(o-1),sizeXXS:t*(o-1)}}(null!=t?t:e)),(0,d.default)(o)),{controlHeight:a}),(0,c.default)(Object.assign(Object.assign({},r),{controlHeight:a})))},getDesignToken:e=>{let s=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):o.default,n=Object.assign(Object.assign({},a.default),null==e?void 0:e.token);return(0,r.getComputedToken)(n,{override:null==e?void 0:e.token},s,l.default)},defaultConfig:s.defaultConfig,_internalContext:s.DesignTokenContext};e.s(["theme",0,C],368869)},127952,e=>{"use strict";var t=e.i(843476),r=e.i(560445),o=e.i(175712),a=e.i(869216),l=e.i(311451),s=e.i(212931),n=e.i(898586),i=e.i(368869),c=e.i(270377),d=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:m,message:g,resourceInformationTitle:f,resourceInformation:p,onCancel:h,onOk:b,confirmLoading:C,requiredConfirmation:y}){let{Title:v,Text:x}=n.Typography,{token:w}=i.theme.useToken(),[k,O]=(0,d.useState)("");return(0,d.useEffect)(()=>{e&&O("")},[e]),(0,t.jsx)(s.Modal,{title:u,open:e,onOk:b,onCancel:h,confirmLoading:C,okText:C?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!y&&k!==y||C},cancelButtonProps:{disabled:C},children:(0,t.jsxs)("div",{className:"space-y-4",children:[m&&(0,t.jsx)(r.Alert,{message:m,type:"warning"}),(0,t.jsx)(o.Card,{title:f,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:w.colorErrorBg,borderColor:w.colorErrorBorder}},style:{backgroundColor:w.colorErrorBg,borderColor:w.colorErrorBorder},children:(0,t.jsx)(a.Descriptions,{column:1,size:"small",children:p&&p.map(({label:e,value:r,...o})=>(0,t.jsx)(a.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(x,{...o,children:r??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(x,{children:g})}),y&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(x,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(x,{children:"Type "}),(0,t.jsx)(x,{strong:!0,type:"danger",children:y}),(0,t.jsx)(x,{children:" to confirm deletion:"})]}),(0,t.jsx)(l.Input,{value:k,onChange:e=>O(e.target.value),placeholder:y,className:"rounded-md",prefix:(0,t.jsx)(c.ExclamationCircleOutlined,{style:{color:w.colorError}}),autoFocus:!0})]})]})})}])},525720,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),o=e.i(529681),a=e.i(908286),l=e.i(242064),s=e.i(246422),n=e.i(838378);let i=["wrap","nowrap","wrap-reverse"],c=["flex-start","flex-end","start","end","center","space-between","space-around","space-evenly","stretch","normal","left","right"],d=["center","start","end","flex-start","flex-end","self-start","self-end","baseline","normal","stretch"],u=function(e,t){let o,a,l;return(0,r.default)(Object.assign(Object.assign(Object.assign({},(o=!0===t.wrap?"wrap":t.wrap,{[`${e}-wrap-${o}`]:o&&i.includes(o)})),(a={},d.forEach(r=>{a[`${e}-align-${r}`]=t.align===r}),a[`${e}-align-stretch`]=!t.align&&!!t.vertical,a)),(l={},c.forEach(r=>{l[`${e}-justify-${r}`]=t.justify===r}),l)))},m=(0,s.genStyleHooks)("Flex",e=>{let{paddingXS:t,padding:r,paddingLG:o}=e,a=(0,n.mergeToken)(e,{flexGapSM:t,flexGap:r,flexGapLG:o});return[(e=>{let{componentCls:t}=e;return{[t]:{display:"flex",margin:0,padding:0,"&-vertical":{flexDirection:"column"},"&-rtl":{direction:"rtl"},"&:empty":{display:"none"}}}})(a),(e=>{let{componentCls:t}=e;return{[t]:{"&-gap-small":{gap:e.flexGapSM},"&-gap-middle":{gap:e.flexGap},"&-gap-large":{gap:e.flexGapLG}}}})(a),(e=>{let{componentCls:t}=e,r={};return i.forEach(e=>{r[`${t}-wrap-${e}`]={flexWrap:e}}),r})(a),(e=>{let{componentCls:t}=e,r={};return d.forEach(e=>{r[`${t}-align-${e}`]={alignItems:e}}),r})(a),(e=>{let{componentCls:t}=e,r={};return c.forEach(e=>{r[`${t}-justify-${e}`]={justifyContent:e}}),r})(a)]},()=>({}),{resetStyle:!1});var g=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let f=t.default.forwardRef((e,s)=>{let{prefixCls:n,rootClassName:i,className:c,style:d,flex:f,gap:p,vertical:h=!1,component:b="div",children:C}=e,y=g(e,["prefixCls","rootClassName","className","style","flex","gap","vertical","component","children"]),{flex:v,direction:x,getPrefixCls:w}=t.default.useContext(l.ConfigContext),k=w("flex",n),[O,T,j]=m(k),N=null!=h?h:null==v?void 0:v.vertical,S=(0,r.default)(c,i,null==v?void 0:v.className,k,T,j,u(k,e),{[`${k}-rtl`]:"rtl"===x,[`${k}-gap-${p}`]:(0,a.isPresetSize)(p),[`${k}-vertical`]:N}),P=Object.assign(Object.assign({},null==v?void 0:v.style),d);return f&&(P.flex=f),p&&!(0,a.isPresetSize)(p)&&(P.gap=p),O(t.default.createElement(b,Object.assign({ref:s,className:S,style:P},(0,o.default)(y,["justify","wrap","align"])),C))});e.s(["Flex",0,f],525720)},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),o=e.i(271645);let a=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],l=e=>({_s:e,status:a[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),s=e=>e?6:5,n=(e,t,r,o,a)=>{clearTimeout(o.current);let s=l(e);t(s),r.current=s,a&&a({current:s})};var i=e.i(480731),c=e.i(444755),d=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return o.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),o.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},f=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,d.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,d.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,c.tremorTwMerge)((0,d.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,d.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,d.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,d.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,d.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},p=(0,d.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:a,needMargin:l,transitionStatus:s})=>{let n=l?r===i.HorizontalPositions.Left?(0,c.tremorTwMerge)("-ml-1","mr-1.5"):(0,c.tremorTwMerge)("-mr-1","ml-1.5"):"",d=(0,c.tremorTwMerge)("w-0 h-0"),m={default:d,entering:d,entered:t,exiting:t,exited:d};return e?o.default.createElement(u,{className:(0,c.tremorTwMerge)(p("icon"),"animate-spin shrink-0",n,m.default,m[s]),style:{transition:"width 150ms"}}):o.default.createElement(a,{className:(0,c.tremorTwMerge)(p("icon"),"shrink-0",t,n)})},b=o.default.forwardRef((e,a)=>{let{icon:u,iconPosition:m=i.HorizontalPositions.Left,size:b=i.Sizes.SM,color:C,variant:y="primary",disabled:v,loading:x=!1,loadingText:w,children:k,tooltip:O,className:T}=e,j=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),N=x||v,S=void 0!==u||x,P=x&&w,B=!(!k&&!P),M=(0,c.tremorTwMerge)(g[b].height,g[b].width),E="light"!==y?(0,c.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",R=f(y,C),z=("light"!==y?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[b],{tooltipProps:_,getReferenceProps:H}=(0,r.useTooltip)(300),[$,L]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:a,timeout:i,initialEntered:c,mountOnEnter:d,unmountOnExit:u,onStateChange:m}={})=>{let[g,f]=(0,o.useState)(()=>l(c?2:s(d))),p=(0,o.useRef)(g),h=(0,o.useRef)(0),[b,C]="object"==typeof i?[i.enter,i.exit]:[i,i],y=(0,o.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return s(t)}})(p.current._s,u);e&&n(e,f,p,h,m)},[m,u]);return[g,(0,o.useCallback)(o=>{let l=e=>{switch(n(e,f,p,h,m),e){case 1:b>=0&&(h.current=((...e)=>setTimeout(...e))(y,b));break;case 4:C>=0&&(h.current=((...e)=>setTimeout(...e))(y,C));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||l(e+1)},0)}},i=p.current.isEnter;"boolean"!=typeof o&&(o=!i),o?i||l(e?+!r:2):i&&l(t?a?3:4:s(u))},[y,m,e,t,r,a,b,C,u]),y]})({timeout:50});return(0,o.useEffect)(()=>{L(x)},[x]),o.default.createElement("button",Object.assign({ref:(0,d.mergeRefs)([a,_.refs.setReference]),className:(0,c.tremorTwMerge)(p("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",E,z.paddingX,z.paddingY,z.fontSize,R.textColor,R.bgColor,R.borderColor,R.hoverBorderColor,N?"opacity-50 cursor-not-allowed":(0,c.tremorTwMerge)(f(y,C).hoverTextColor,f(y,C).hoverBgColor,f(y,C).hoverBorderColor),T),disabled:N},H,j),o.default.createElement(r.default,Object.assign({text:O},_)),S&&m!==i.HorizontalPositions.Right?o.default.createElement(h,{loading:x,iconSize:M,iconPosition:m,Icon:u,transitionStatus:$.status,needMargin:B}):null,P||k?o.default.createElement("span",{className:(0,c.tremorTwMerge)(p("text"),"text-tremor-default whitespace-nowrap")},P?w:k):null,S&&m===i.HorizontalPositions.Right?o.default.createElement(h,{loading:x,iconSize:M,iconPosition:m,Icon:u,transitionStatus:$.status,needMargin:B}):null)});b.displayName="Button",e.s(["Button",0,b],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),o=e.i(673706),a=e.i(271645);let l=a.default.forwardRef((e,l)=>{let{color:s,className:n,children:i}=e;return a.default.createElement("p",{ref:l,className:(0,r.tremorTwMerge)("text-tremor-default",s?(0,o.getColorClassNames)(s,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),n)},i)});l.displayName="Text",e.s(["default",0,l],936325),e.s(["Text",0,l],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(480731),a=e.i(95779),l=e.i(444755),s=e.i(673706);let n=(0,s.makeClassName)("Card"),i=r.default.forwardRef((e,i)=>{let{decoration:c="",decorationColor:d,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:i,className:(0,l.tremorTwMerge)(n("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",d?(0,s.getColorClassNames)(d,a.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case o.HorizontalPositions.Left:return"border-l-4";case o.VerticalPositions.Top:return"border-t-4";case o.HorizontalPositions.Right:return"border-r-4";case o.VerticalPositions.Bottom:return"border-b-4";default:return""}})(c),m)},g),u)});i.displayName="Card",e.s(["Card",0,i],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),o=e.i(444755),a=e.i(673706),l=e.i(271645);let s=l.default.forwardRef((e,s)=>{let{color:n,children:i,className:c}=e,d=(0,t.__rest)(e,["color","children","className"]);return l.default.createElement("p",Object.assign({ref:s,className:(0,o.tremorTwMerge)("font-medium text-tremor-title",n?(0,a.getColorClassNames)(n,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",c)},d),i)});s.displayName="Title",e.s(["Title",0,s],629569)},350967,46757,e=>{"use strict";var t=e.i(290571),r=e.i(444755),o=e.i(673706),a=e.i(271645);let l={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},s={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},n={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},i={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,l,"gridColsLg",0,i,"gridColsMd",0,n,"gridColsSm",0,s],46757);let c=(0,o.makeClassName)("Grid"),d=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=a.default.forwardRef((e,o)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:g,numItemsLg:f,children:p,className:h}=e,b=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),C=d(u,l),y=d(m,s),v=d(g,n),x=d(f,i),w=(0,r.tremorTwMerge)(C,y,v,x);return a.default.createElement("div",Object.assign({ref:o,className:(0,r.tremorTwMerge)(c("root"),"grid",w,h)},b),p)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},269200,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("Table"),l=r.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement("div",{className:(0,o.tremorTwMerge)(a("root"),"overflow-auto",n)},r.default.createElement("table",Object.assign({ref:l,className:(0,o.tremorTwMerge)(a("table"),"w-full text-tremor-default","text-tremor-content","dark:text-dark-tremor-content")},i),s))});l.displayName="Table",e.s(["Table",0,l],269200)},942232,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableBody"),l=r.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tbody",Object.assign({ref:l,className:(0,o.tremorTwMerge)(a("root"),"align-top divide-y","divide-tremor-border","dark:divide-dark-tremor-border",n)},i),s))});l.displayName="TableBody",e.s(["TableBody",0,l],942232)},977572,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableCell"),l=r.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("td",Object.assign({ref:l,className:(0,o.tremorTwMerge)(a("root"),"align-middle whitespace-nowrap text-left p-4",n)},i),s))});l.displayName="TableCell",e.s(["TableCell",0,l],977572)},427612,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableHead"),l=r.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("thead",Object.assign({ref:l,className:(0,o.tremorTwMerge)(a("root"),"text-left","text-tremor-content","dark:text-dark-tremor-content",n)},i),s))});l.displayName="TableHead",e.s(["TableHead",0,l],427612)},64848,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableHeaderCell"),l=r.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("th",Object.assign({ref:l,className:(0,o.tremorTwMerge)(a("root"),"whitespace-nowrap text-left font-semibold top-0 px-4 py-3.5","text-tremor-content-strong","dark:text-dark-tremor-content-strong",n)},i),s))});l.displayName="TableHeaderCell",e.s(["TableHeaderCell",0,l],64848)},496020,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(444755);let a=(0,e.i(673706).makeClassName)("TableRow"),l=r.default.forwardRef((e,l)=>{let{children:s,className:n}=e,i=(0,t.__rest)(e,["children","className"]);return r.default.createElement(r.default.Fragment,null,r.default.createElement("tr",Object.assign({ref:l,className:(0,o.tremorTwMerge)(a("row"),n)},i),s))});l.displayName="TableRow",e.s(["TableRow",0,l],496020)},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},743151,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.CopyToClipboard=void 0;var o=s(e.r(844343)),a=s(e.r(271645)),l=["text","onCopy","options","children"];function s(e){return e&&e.__esModule?e:{default:e}}function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,o)}return r}function c(e){for(var t=1;t{"use strict";var o=e.r(743151).CopyToClipboard;o.CopyToClipboard=o,t.exports=o}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0rvhrqi0s_~5q.js b/litellm/proxy/_experimental/out/_next/static/chunks/0rvhrqi0s_~5q.js new file mode 100644 index 00000000000..e1490760659 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0rvhrqi0s_~5q.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,928685,e=>{"use strict";var t=e.i(38953);e.s(["SearchOutlined",()=>t.default])},541202,e=>{"use strict";var t=e.i(843476),r=e.i(522016),a=e.i(560445);e.s(["DeprecationBanner",0,({featureName:e})=>(0,t.jsx)(a.Alert,{message:`${e} is on a draft deprecation list`,description:(0,t.jsxs)(t.Fragment,{children:[`${e} is one of several experimental features we're considering removing, potentially as early as September 1, 2026. This list is a draft and is not final. If you rely on this feature, please share feedback on the `,(0,t.jsx)(r.default,{href:"https://github.com/BerriAI/litellm/discussions/32090",target:"_blank",rel:"noopener noreferrer",children:"deprecation discussion"}),"."]}),type:"info",showIcon:!0,closable:!0,style:{marginBottom:16}})])},564897,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M696 480H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"minus-circle",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["MinusCircleOutlined",0,s],564897)},91979,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M909.1 209.3l-56.4 44.1C775.8 155.1 656.2 92 521.9 92 290 92 102.3 279.5 102 511.5 101.7 743.7 289.8 932 521.9 932c181.3 0 335.8-115 394.6-276.1 1.5-4.2-.7-8.9-4.9-10.3l-56.7-19.5a8 8 0 00-10.1 4.8c-1.8 5-3.8 10-5.9 14.9-17.3 41-42.1 77.8-73.7 109.4A344.77 344.77 0 01655.9 829c-42.3 17.9-87.4 27-133.8 27-46.5 0-91.5-9.1-133.8-27A341.5 341.5 0 01279 755.2a342.16 342.16 0 01-73.7-109.4c-17.9-42.4-27-87.4-27-133.9s9.1-91.5 27-133.9c17.3-41 42.1-77.8 73.7-109.4 31.6-31.6 68.4-56.4 109.3-73.8 42.3-17.9 87.4-27 133.8-27 46.5 0 91.5 9.1 133.8 27a341.5 341.5 0 01109.3 73.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.6 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c-.1-6.6-7.8-10.3-13-6.2z"}}]},name:"reload",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["ReloadOutlined",0,s],91979)},751904,e=>{"use strict";var t=e.i(401361);e.s(["EditOutlined",()=>t.default])},366308,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M876.6 239.5c-.5-.9-1.2-1.8-2-2.5-5-5-13.1-5-18.1 0L684.2 409.3l-67.9-67.9L788.7 169c.8-.8 1.4-1.6 2-2.5 3.6-6.1 1.6-13.9-4.5-17.5-98.2-58-226.8-44.7-311.3 39.7-67 67-89.2 162-66.5 247.4l-293 293c-3 3-2.8 7.9.3 11l169.7 169.7c3.1 3.1 8.1 3.3 11 .3l292.9-292.9c85.5 22.8 180.5.7 247.6-66.4 84.4-84.5 97.7-213.1 39.7-311.3zM786 499.8c-58.1 58.1-145.3 69.3-214.6 33.6l-8.8 8.8-.1-.1-274 274.1-79.2-79.2 230.1-230.1s0 .1.1.1l52.8-52.8c-35.7-69.3-24.5-156.5 33.6-214.6a184.2 184.2 0 01144-53.5L537 318.9a32.05 32.05 0 000 45.3l124.5 124.5a32.05 32.05 0 0045.3 0l132.8-132.8c3.7 51.8-14.4 104.8-53.6 143.9z"}}]},name:"tool",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["ToolOutlined",0,s],366308)},362024,e=>{"use strict";var t=e.i(988122);e.s(["Collapse",()=>t.default])},245704,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"check-circle",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["CheckCircleOutlined",0,s],245704)},149192,e=>{"use strict";var t=e.i(864517);e.s(["CloseOutlined",()=>t.default])},784774,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(115504);let l=r.forwardRef(({className:e,...r},l)=>(0,t.jsx)("div",{"data-slot":"table-container",className:"relative w-full overflow-x-auto",children:(0,t.jsx)("table",{ref:l,"data-slot":"table",className:(0,a.cn)("w-full caption-bottom text-sm",e),...r})}));l.displayName="Table";let s=r.forwardRef(({className:e,...r},l)=>(0,t.jsx)("thead",{ref:l,"data-slot":"table-header",className:(0,a.cn)("[&_tr]:border-b",e),...r}));s.displayName="TableHeader";let i=r.forwardRef(({className:e,...r},l)=>(0,t.jsx)("tbody",{ref:l,"data-slot":"table-body",className:(0,a.cn)("[&_tr:last-child]:border-0",e),...r}));i.displayName="TableBody";let n=r.forwardRef(({className:e,...r},l)=>(0,t.jsx)("tfoot",{ref:l,"data-slot":"table-footer",className:(0,a.cn)("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",e),...r}));n.displayName="TableFooter";let o=r.forwardRef(({className:e,...r},l)=>(0,t.jsx)("tr",{ref:l,"data-slot":"table-row",className:(0,a.cn)("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",e),...r}));o.displayName="TableRow";let c=r.forwardRef(({className:e,...r},l)=>(0,t.jsx)("th",{ref:l,"data-slot":"table-head",className:(0,a.cn)("h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",e),...r}));c.displayName="TableHead";let d=r.forwardRef(({className:e,...r},l)=>(0,t.jsx)("td",{ref:l,"data-slot":"table-cell",className:(0,a.cn)("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",e),...r}));d.displayName="TableCell",r.forwardRef(({className:e,...r},l)=>(0,t.jsx)("caption",{ref:l,"data-slot":"table-caption",className:(0,a.cn)("mt-4 text-sm text-muted-foreground",e),...r})).displayName="TableCaption",e.s(["Table",0,l,"TableBody",0,i,"TableCell",0,d,"TableFooter",0,n,"TableHead",0,c,"TableHeader",0,s,"TableRow",0,o])},211576,e=>{"use strict";var t=e.i(131757);e.s(["Col",()=>t.default])},178654,e=>{"use strict";let t=e.i(211576).Col;e.s(["Col",0,t],178654)},621192,e=>{"use strict";let t=e.i(281256).Row;e.s(["Row",0,t],621192)},695411,e=>{"use strict";var t=e.i(602869);let r=async e=>{try{let r=await (0,t.modelHubCall)(e);if(r?.data.length>0){let e=r.data.map(e=>({model_group:e.model_group,mode:e?.mode}));return e.sort((e,t)=>e.model_group.localeCompare(t.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}};e.s(["fetchAvailableModels",0,r])},841947,e=>{"use strict";let t=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,t])},983561,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M300 328a60 60 0 10120 0 60 60 0 10-120 0zM852 64H172c-17.7 0-32 14.3-32 32v660c0 17.7 14.3 32 32 32h680c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-32 660H204V128h616v596zM604 328a60 60 0 10120 0 60 60 0 10-120 0zm250.2 556H169.8c-16.5 0-29.8 14.3-29.8 32v36c0 4.4 3.3 8 7.4 8h729.1c4.1 0 7.4-3.6 7.4-8v-36c.1-17.7-13.2-32-29.7-32zM664 508H360c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"robot",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["RobotOutlined",0,s],983561)},797672,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,r],797672)},992619,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(779241),l=e.i(599724),s=e.i(199133),i=e.i(983561),n=e.i(695411);e.s(["default",0,({accessToken:e,value:o,placeholder:c="Select a Model",onChange:d,disabled:u=!1,style:m,className:g,showLabel:h=!0,labelText:f="Select Model"})=>{let[p,b]=(0,r.useState)(o),[x,v]=(0,r.useState)(!1),[y,w]=(0,r.useState)([]),C=(0,r.useRef)(null);return(0,r.useEffect)(()=>{b(o)},[o]),(0,r.useEffect)(()=>{e&&(async()=>{try{let t=await (0,n.fetchAvailableModels)(e);t.length>0&&w(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[h&&(0,t.jsxs)(l.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(i.RobotOutlined,{className:"mr-2"})," ",f]}),(0,t.jsx)(s.Select,{value:p,placeholder:c,onChange:e=>{"custom"===e?(v(!0),b(void 0)):(v(!1),b(e),d&&d(e))},options:[...Array.from(new Set(y.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${g||""}`,disabled:u}),x&&(0,t.jsx)(a.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{C.current&&clearTimeout(C.current),C.current=setTimeout(()=>{b(e),d&&d(e)},500)},disabled:u})]})}])},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},500727,e=>{"use strict";var t=e.i(266027),r=e.i(243652),a=e.i(602869),l=e.i(135214);let s=(0,r.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:r}=(0,l.default)();return(0,t.useQuery)({queryKey:s.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,a.fetchMCPServers)(r,e),enabled:!!r})}])},699857,e=>{"use strict";var t=e.i(266027),r=e.i(243652),a=e.i(602869),l=e.i(135214);let s=(0,r.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,l.default)();return(0,t.useQuery)({queryKey:s.list(),queryFn:async()=>await (0,a.fetchMCPToolsets)(e),enabled:!!e})}])},91739,e=>{"use strict";var t=e.i(544195);e.s(["Radio",()=>t.default])},988297,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,r],988297)},409797,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDownIcon",()=>t.default])},361653,e=>{"use strict";let t=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,t])},531516,696609,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(536916),l=e.i(599724),s=e.i(409797),i=e.i(246349),i=i;let n=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,o=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,c=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,d=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function u(e,t=""){let r=e.toLowerCase();if(d.test(r))return"read";if(n.test(r))return"delete";if(c.test(r))return"update";if(o.test(r))return"create";if(t){let e=t.toLowerCase();if(d.test(e))return"read";if(n.test(e))return"delete";if(c.test(e))return"update";if(o.test(e))return"create"}return"unknown"}function m(e){let t={read:[],create:[],update:[],delete:[],unknown:[]};for(let r of e)t[u(r.name,r.description)].push(r);return t}let g={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,g,"classifyToolOp",0,u,"groupToolsByCrud",0,m],696609);let h=["read","create","update","delete","unknown"],f={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},p={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},b={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:n,onChange:o,readOnly:c=!1,searchFilter:d=""})=>{let[u,x]=(0,r.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),v=(0,r.useMemo)(()=>m(e),[e]),y=(0,r.useMemo)(()=>new Set(void 0===n?e.map(e=>e.name):n),[n,e]),w=e=>{if(c)return;let t=new Set(y);t.has(e)?t.delete(e):t.add(e),o(Array.from(t))};return 0===e.length?null:(0,t.jsx)("div",{className:"space-y-3",children:h.map(e=>{let r,n=v[e];if(0===n.length)return null;if(d){let e=d.toLowerCase();if(!n.some(t=>t.name.toLowerCase().includes(e)||(t.description??"").toLowerCase().includes(e)))return null}let m=g[e],h=(r=v[e]).length>0&&r.every(e=>y.has(e.name)),C=(e=>{let t=v[e];if(0===t.length)return!1;let r=t.filter(e=>y.has(e.name)).length;return r>0&&r{x(t=>({...t,[e]:!t[e]}))},children:[k?(0,t.jsx)(i.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,t.jsx)(s.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:m.label}),(0,t.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${f[m.risk]}`,children:"high"===m.risk?"High Risk":"medium"===m.risk?"Medium Risk":"low"===m.risk?"Safe":"Unclassified"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[n.filter(e=>y.has(e.name)).length,"/",n.length," allowed"]})]}),!c&&(0,t.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,t.jsx)(l.Text,{className:"text-xs text-gray-500",children:h?"All on":C?"Partial":"All off"}),(0,t.jsx)(a.Checkbox,{checked:h,indeterminate:C,onChange:t=>((e,t)=>{if(c)return;let r=new Set(y);for(let a of v[e])t?r.add(a.name):r.delete(a.name);o(Array.from(r))})(e,t.target.checked),onClick:e=>e.stopPropagation()})]})]}),!k&&(0,t.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:m.description}),!k&&(0,t.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:n.filter(e=>!d||e.name.toLowerCase().includes(d.toLowerCase())||(e.description??"").toLowerCase().includes(d.toLowerCase())).map(e=>{let r,s=(r=e.name,y.has(r));return(0,t.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!c?"cursor-pointer":""} ${s?"":"opacity-60"}`,onClick:()=>w(e.name),children:[(0,t.jsx)(a.Checkbox,{checked:s,onChange:()=>w(e.name),disabled:c,onClick:e=>e.stopPropagation()}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)(l.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,t.jsx)(l.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,t.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${s?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:s?"on":"off"})]},e.name)})})]},e)})})}],531516)},962944,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M848 359.3H627.7L825.8 109c4.1-5.3.4-13-6.3-13H436c-2.8 0-5.5 1.5-6.9 4L170 547.5c-3.1 5.3.7 12 6.9 12h174.4l-89.4 357.6c-1.9 7.8 7.5 13.3 13.3 7.7L853.5 373c5.2-4.9 1.7-13.7-5.5-13.7zM378.2 732.5l60.3-241H281.1l189.6-327.4h224.6L487 427.4h211L378.2 732.5z"}}]},name:"thunderbolt",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["ThunderboltOutlined",0,s],962944)},149121,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(152990),l=e.i(682830),s=e.i(784774);e.s(["DataTable",0,function({data:e=[],columns:i,getRowId:n,onRowClick:o,renderSubComponent:c,getRowCanExpand:d,isLoading:u=!1,loadingMessage:m="Loading...",noDataMessage:g="No results",enableSorting:h=!1}){let f=!!c&&!!d,p=i.some(e=>void 0!==e.size),[b,x]=(0,r.useState)([]),v=(0,a.useReactTable)({data:e,columns:i,...h&&{state:{sorting:b},onSortingChange:x,enableSortingRemoval:!1},...f&&{getRowCanExpand:d},...n&&{getRowId:n},getCoreRowModel:(0,l.getCoreRowModel)(),...h&&{getSortedRowModel:(0,l.getSortedRowModel)()},...f&&{getExpandedRowModel:(0,l.getExpandedRowModel)()}}),y=p?{minWidth:v.getCenterTotalSize()}:{minWidth:"400px"};return(0,t.jsx)("div",{className:"rounded-lg custom-border overflow-hidden w-full max-w-full box-border",children:(0,t.jsxs)(s.Table,{className:p?"table-fixed":"table-fixed w-full box-border",style:y,children:[(0,t.jsx)(s.TableHeader,{children:v.getHeaderGroups().map(e=>(0,t.jsx)(s.TableRow,{className:"bg-muted/50 hover:bg-muted/50",children:e.headers.map(e=>{let r=h&&e.column.getCanSort(),l=e.column.getIsSorted(),i=e.column.columnDef.meta?.numeric;return(0,t.jsx)(s.TableHead,{className:`py-1 h-8 text-xs font-medium text-muted-foreground first:pl-4 last:pr-4 ${r?"cursor-pointer select-none hover:bg-muted":""}`,style:p?{width:e.getSize()}:void 0,onClick:r?e.column.getToggleSortingHandler():void 0,children:e.isPlaceholder?null:(0,t.jsxs)("div",{className:`flex items-center gap-1 ${i?"justify-end":""}`,children:[(0,a.flexRender)(e.column.columnDef.header,e.getContext()),r&&(0,t.jsx)("span",{className:"text-muted-foreground",children:"asc"===l?"↑":"desc"===l?"↓":"⇅"})]})},e.id)})},e.id))}),(0,t.jsx)(s.TableBody,{children:u?(0,t.jsx)(s.TableRow,{className:"hover:bg-transparent",children:(0,t.jsx)(s.TableCell,{colSpan:i.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-muted-foreground",children:(0,t.jsx)("p",{children:m})})})}):v.getRowModel().rows.length>0?v.getRowModel().rows.map(e=>(0,t.jsxs)(r.Fragment,{children:[(0,t.jsx)(s.TableRow,{className:`h-8 ${o?"cursor-pointer":""}`,onClick:()=>o?.(e.original),children:e.getVisibleCells().map(e=>(0,t.jsx)(s.TableCell,{className:`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap first:pl-4 last:pr-4 ${e.column.columnDef.meta?.numeric?"text-right tabular-nums":""}`,style:p?{width:e.column.getSize()}:void 0,children:(0,a.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))}),f&&e.getIsExpanded()&&c&&(0,t.jsx)(s.TableRow,{className:"hover:bg-transparent",children:(0,t.jsx)(s.TableCell,{colSpan:e.getVisibleCells().length,className:"p-0",children:(0,t.jsx)("div",{className:"w-full max-w-full overflow-hidden box-border",children:c({row:e})})})})]},e.id)):(0,t.jsx)(s.TableRow,{className:"hover:bg-transparent",children:(0,t.jsx)(s.TableCell,{colSpan:i.length,className:"h-24 text-center align-middle",children:(0,t.jsx)("p",{className:"text-sm text-muted-foreground",children:g})})})})]})})}])},434166,e=>{"use strict";e.s(["getSecureItem",0,function(e){try{let t=window.sessionStorage.getItem(e);if(null===t)return null;return decodeURIComponent(atob(t).split("").map(e=>"%"+e.charCodeAt(0).toString(16).padStart(2,"0")).join(""))}catch{return null}},"setSecureItem",0,function(e,t){window.sessionStorage.setItem(e,btoa(encodeURIComponent(t).replace(/%([0-9A-F]{2})/g,(e,t)=>String.fromCharCode(parseInt(t,16)))))}])},596239,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M574 665.4a8.03 8.03 0 00-11.3 0L446.5 781.6c-53.8 53.8-144.6 59.5-204 0-59.5-59.5-53.8-150.2 0-204l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3l-39.8-39.8a8.03 8.03 0 00-11.3 0L191.4 526.5c-84.6 84.6-84.6 221.5 0 306s221.5 84.6 306 0l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3L574 665.4zm258.6-474c-84.6-84.6-221.5-84.6-306 0L410.3 307.6a8.03 8.03 0 000 11.3l39.7 39.7c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c53.8-53.8 144.6-59.5 204 0 59.5 59.5 53.8 150.2 0 204L665.3 562.6a8.03 8.03 0 000 11.3l39.8 39.8c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c84.5-84.6 84.5-221.5 0-306.1zM610.1 372.3a8.03 8.03 0 00-11.3 0L372.3 598.7a8.03 8.03 0 000 11.3l39.6 39.6c3.1 3.1 8.2 3.1 11.3 0l226.4-226.4c3.1-3.1 3.1-8.2 0-11.3l-39.5-39.6z"}}]},name:"link",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["LinkOutlined",0,s],596239)},438957,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M608 112c-167.9 0-304 136.1-304 304 0 70.3 23.9 135 63.9 186.5l-41.1 41.1-62.3-62.3a8.15 8.15 0 00-11.4 0l-39.8 39.8a8.15 8.15 0 000 11.4l62.3 62.3-44.9 44.9-62.3-62.3a8.15 8.15 0 00-11.4 0l-39.8 39.8a8.15 8.15 0 000 11.4l62.3 62.3-65.3 65.3a8.03 8.03 0 000 11.3l42.3 42.3c3.1 3.1 8.2 3.1 11.3 0l253.6-253.6A304.06 304.06 0 00608 720c167.9 0 304-136.1 304-304S775.9 112 608 112zm161.2 465.2C726.2 620.3 668.9 644 608 644c-60.9 0-118.2-23.7-161.2-66.8-43.1-43-66.8-100.3-66.8-161.2 0-60.9 23.7-118.2 66.8-161.2 43-43.1 100.3-66.8 161.2-66.8 60.9 0 118.2 23.7 161.2 66.8 43.1 43 66.8 100.3 66.8 161.2 0 60.9-23.7 118.2-66.8 161.2z"}}]},name:"key",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["KeyOutlined",0,s],438957)},848725,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"}))});e.s(["EyeIcon",0,r],848725)},245094,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M516 673c0 4.4 3.4 8 7.5 8h185c4.1 0 7.5-3.6 7.5-8v-48c0-4.4-3.4-8-7.5-8h-185c-4.1 0-7.5 3.6-7.5 8v48zm-194.9 6.1l192-161c3.8-3.2 3.8-9.1 0-12.3l-192-160.9A7.95 7.95 0 00308 351v62.7c0 2.4 1 4.6 2.9 6.1L420.7 512l-109.8 92.2a8.1 8.1 0 00-2.9 6.1V673c0 6.8 7.9 10.5 13.1 6.1zM880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"}}]},name:"code",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["CodeOutlined",0,s],245094)},458505,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372zm47.7-395.2l-25.4-5.9V348.6c38 5.2 61.5 29 65.5 58.2.5 4 3.9 6.9 7.9 6.9h44.9c4.7 0 8.4-4.1 8-8.8-6.1-62.3-57.4-102.3-125.9-109.2V263c0-4.4-3.6-8-8-8h-28.1c-4.4 0-8 3.6-8 8v33c-70.8 6.9-126.2 46-126.2 119 0 67.6 49.8 100.2 102.1 112.7l24.7 6.3v142.7c-44.2-5.9-69-29.5-74.1-61.3-.6-3.8-4-6.6-7.9-6.6H363c-4.7 0-8.4 4-8 8.7 4.5 55 46.2 105.6 135.2 112.1V761c0 4.4 3.6 8 8 8h28.4c4.4 0 8-3.6 8-8.1l-.2-31.7c78.3-6.9 134.3-48.8 134.3-124-.1-69.4-44.2-100.4-109-116.4zm-68.6-16.2c-5.6-1.6-10.3-3.1-15-5-33.8-12.2-49.5-31.9-49.5-57.3 0-36.3 27.5-57 64.5-61.7v124zM534.3 677V543.3c3.1.9 5.9 1.6 8.8 2.2 47.3 14.4 63.2 34.4 63.2 65.1 0 39.1-29.4 62.6-72 66.4z"}}]},name:"dollar",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["DollarOutlined",0,s],458505)},440987,e=>{"use strict";var t=e.i(903446);e.s(["SettingsIcon",()=>t.default])},611052,2781,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(212931),l=e.i(311451),s=e.i(790848),i=e.i(888259),n=e.i(438957);e.i(247167);var o=e.i(931067);let c={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 464h-68V240c0-70.7-57.3-128-128-128H388c-70.7 0-128 57.3-128 128v224h-68c-17.7 0-32 14.3-32 32v384c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32zM332 240c0-30.9 25.1-56 56-56h248c30.9 0 56 25.1 56 56v224H332V240zm460 600H232V536h560v304zM484 701v53c0 4.4 3.6 8 8 8h40c4.4 0 8-3.6 8-8v-53a48.01 48.01 0 10-56 0z"}}]},name:"lock",theme:"outlined"};var d=e.i(9583),u=r.forwardRef(function(e,t){return r.createElement(d.default,(0,o.default)({},e,{ref:t,icon:c}))});e.s(["LockOutlined",0,u],2781);var m=e.i(492030),g=e.i(266537),h=e.i(447566),f=e.i(149192),p=e.i(596239);e.s(["ByokCredentialModal",0,({server:e,open:o,onClose:c,onSuccess:d,accessToken:b})=>{let[x,v]=(0,r.useState)(1),[y,w]=(0,r.useState)(""),[C,k]=(0,r.useState)(!0),[j,N]=(0,r.useState)(!1),O=e.alias||e.server_name||"Service",$=O.charAt(0).toUpperCase(),T=()=>{v(1),w(""),k(!0),N(!1),c()},S=async()=>{if(!y.trim())return void i.default.error("Please enter your API key");N(!0);try{let t=await fetch(`/v1/mcp/server/${e.server_id}/user-credential`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${b}`},body:JSON.stringify({credential:y.trim(),save:C})});if(!t.ok){let e=await t.json();throw Error(e?.detail?.error||"Failed to save credential")}i.default.success(`Connected to ${O}`),d(e.server_id),T()}catch(e){i.default.error(e.message||"Failed to connect")}finally{N(!1)}};return(0,t.jsx)(a.Modal,{open:o,onCancel:T,footer:null,width:480,closeIcon:null,className:"byok-modal",children:(0,t.jsxs)("div",{className:"relative p-2",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-6",children:[2===x?(0,t.jsxs)("button",{onClick:()=>v(1),className:"flex items-center gap-1 text-gray-500 hover:text-gray-800 text-sm",children:[(0,t.jsx)(h.ArrowLeftOutlined,{})," Back"]}):(0,t.jsx)("div",{}),(0,t.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,t.jsx)("div",{className:`w-2 h-2 rounded-full ${1===x?"bg-blue-500":"bg-gray-300"}`}),(0,t.jsx)("div",{className:`w-2 h-2 rounded-full ${2===x?"bg-blue-500":"bg-gray-300"}`})]}),(0,t.jsx)("button",{onClick:T,className:"text-gray-400 hover:text-gray-600",children:(0,t.jsx)(f.CloseOutlined,{})})]}),1===x?(0,t.jsxs)("div",{className:"text-center",children:[(0,t.jsxs)("div",{className:"flex items-center justify-center gap-3 mb-6",children:[(0,t.jsx)("div",{className:"w-14 h-14 rounded-xl bg-linear-to-br from-teal-400 to-cyan-600 flex items-center justify-center text-white font-bold text-xl shadow-sm",children:"L"}),(0,t.jsx)(g.ArrowRightOutlined,{className:"text-gray-400 text-lg"}),(0,t.jsx)("div",{className:"w-14 h-14 rounded-xl bg-linear-to-br from-blue-600 to-indigo-800 flex items-center justify-center text-white font-bold text-xl shadow-sm",children:$})]}),(0,t.jsxs)("h2",{className:"text-2xl font-bold text-gray-900 mb-2",children:["Connect ",O]}),(0,t.jsxs)("p",{className:"text-gray-500 mb-6",children:["LiteLLM needs access to ",O," to complete your request."]}),(0,t.jsx)("div",{className:"bg-gray-50 rounded-xl p-4 text-left mb-4",children:(0,t.jsxs)("div",{className:"flex items-start gap-3",children:[(0,t.jsx)("div",{className:"mt-0.5",children:(0,t.jsxs)("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",className:"text-gray-500",children:[(0,t.jsx)("rect",{x:"2",y:"4",width:"20",height:"16",rx:"2",stroke:"currentColor",strokeWidth:"2"}),(0,t.jsx)("path",{d:"M8 4v16M16 4v16",stroke:"currentColor",strokeWidth:"2"})]})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-semibold text-gray-800 mb-1",children:"How it works"}),(0,t.jsxs)("p",{className:"text-gray-500 text-sm",children:["LiteLLM acts as a secure bridge. Your requests are routed through our MCP client directly to"," ",O,"'s API."]})]})]})}),e.byok_description&&e.byok_description.length>0&&(0,t.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 text-left mb-6",children:[(0,t.jsxs)("p",{className:"text-xs font-semibold text-gray-500 uppercase tracking-widest mb-3 flex items-center gap-2",children:[(0,t.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",className:"text-green-500",children:[(0,t.jsx)("path",{d:"M12 2L12 22M2 12L22 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"}),(0,t.jsx)("circle",{cx:"12",cy:"12",r:"9",stroke:"currentColor",strokeWidth:"2"})]}),"Requested Access"]}),(0,t.jsx)("ul",{className:"space-y-2",children:e.byok_description.map((e,r)=>(0,t.jsxs)("li",{className:"flex items-center gap-2 text-sm text-gray-700",children:[(0,t.jsx)(m.CheckOutlined,{className:"text-green-500 shrink-0"}),e]},r))})]}),(0,t.jsxs)("button",{onClick:()=>v(2),className:"w-full bg-gray-900 hover:bg-gray-700 text-white font-medium py-3 px-6 rounded-xl flex items-center justify-center gap-2 transition-colors",children:["Continue to Authentication ",(0,t.jsx)(g.ArrowRightOutlined,{})]}),(0,t.jsx)("button",{onClick:T,className:"mt-3 w-full text-gray-400 hover:text-gray-600 text-sm py-2",children:"Cancel"})]}):(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"w-12 h-12 rounded-full bg-blue-50 flex items-center justify-center mb-4",children:(0,t.jsx)(n.KeyOutlined,{className:"text-blue-400 text-xl"})}),(0,t.jsx)("h2",{className:"text-2xl font-bold text-gray-900 mb-2",children:"Provide API Key"}),(0,t.jsxs)("p",{className:"text-gray-500 mb-6",children:["Enter your ",O," API key to authorize this connection."]}),(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsxs)("label",{className:"block text-sm font-semibold text-gray-800 mb-2",children:[O," API Key"]}),(0,t.jsx)(l.Input.Password,{placeholder:"Enter your API key",value:y,onChange:e=>w(e.target.value),size:"large",className:"rounded-lg"}),e.byok_api_key_help_url&&(0,t.jsxs)("a",{href:e.byok_api_key_help_url,target:"_blank",rel:"noopener noreferrer",className:"text-blue-500 hover:text-blue-700 text-sm mt-2 flex items-center gap-1",children:["Where do I find my API key? ",(0,t.jsx)(p.LinkOutlined,{})]})]}),(0,t.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 flex items-center justify-between mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",className:"text-gray-500",children:(0,t.jsx)("path",{d:"M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z",fill:"currentColor"})}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-800",children:"Save key for future use"})]}),(0,t.jsx)(s.Switch,{checked:C,onChange:k})]}),(0,t.jsxs)("div",{className:"bg-blue-50 rounded-xl p-4 flex items-start gap-3 mb-6",children:[(0,t.jsx)(u,{className:"text-blue-400 mt-0.5 shrink-0"}),(0,t.jsx)("p",{className:"text-sm text-blue-700",children:"Your key is stored securely and transmitted over HTTPS. It is never shared with third parties."})]}),(0,t.jsxs)("button",{onClick:S,disabled:j,className:"w-full bg-blue-500 hover:bg-blue-600 disabled:opacity-60 text-white font-medium py-3 px-6 rounded-xl flex items-center justify-center gap-2 transition-colors",children:[(0,t.jsx)(u,{}),"Connect & Authorize"]})]})]})})}],611052)},292335,122520,165615,779129,280024,e=>{"use strict";let t={NONE:"none",API_KEY:"api_key",BEARER_TOKEN:"bearer_token",TOKEN:"token",BASIC:"basic",OAUTH2:"oauth2",OAUTH2_TOKEN_EXCHANGE:"oauth2_token_exchange",AWS_SIGV4:"aws_sigv4",TRUE_PASSTHROUGH:"true_passthrough",OAUTH_DELEGATE:"oauth_delegate"},r={INTERACTIVE:"interactive",M2M:"m2m"},a=e=>{let t=e.credentials??{};return JSON.stringify({url:"string"==typeof e.url?e.url:null,spec_path:"string"==typeof e.spec_path?e.spec_path:null,auth_type:e.auth_type??null,oauth_flow_type:e.oauth_flow_type??null,client_id:t.client_id??null,client_secret:t.client_secret??null,scopes:t.scopes??null,authorization_url:e.authorization_url??null,token_url:e.token_url??null,registration_url:e.registration_url??null})},l=["client_id","client_secret"],s=["access_token","refresh_token","expires_in","scope"],i="client_credentials",n={SSE:"sse",HTTP:"http",STDIO:"stdio",OPENAPI:"openapi"};e.s(["AUTH_TYPE",0,t,"CLEARED_ON_INVALIDATION",0,["credentials"],"MCP_OAUTH2_FLOW_INTERACTIVE",0,"authorization_code","MCP_OAUTH2_FLOW_M2M",0,i,"OAUTH_FLOW",0,r,"TRANSPORT",0,n,"credentialAuthClass",0,e=>e===t.TRUE_PASSTHROUGH||e===t.OAUTH_DELEGATE?"client_forwarded":e??null,"getMcpOAuthMode",0,function(e){return e.auth_type===t.OAUTH2_TOKEN_EXCHANGE?"token_exchange":e.auth_type!==t.OAUTH2?null:e.oauth2_flow===i?"m2m":e.delegate_auth_to_upstream?"passthrough":"authorization_code"},"getOAuthAuthorizationIdentity",0,a,"handleAuth",0,e=>null==e?t.NONE:e,"handleTransport",0,(e,t)=>null==e?n.SSE:t&&e!==n.STDIO?n.OPENAPI:e,"isClientForwardedTokenMode",0,e=>e===t.TRUE_PASSTHROUGH||e===t.OAUTH_DELEGATE,"isHeldOAuthTokenStale",0,(e,t)=>void 0!==t&&a(e)!==t,"oauth2FlowToFormValue",0,function(e){return e===i?r.M2M:e?r.INTERACTIVE:void 0},"preservedDeclaredAppCredentials",0,e=>{if(!e)return;let t=Object.fromEntries(l.filter(t=>"string"==typeof e[t]&&""!==e[t]).map(t=>[t,e[t]]));return Object.keys(t).length>0?t:void 0},"withoutMintedTokenCredentials",0,e=>{if(!e)return;let t=Object.fromEntries(Object.entries(e).filter(([e])=>!s.includes(e)));return Object.keys(t).length>0?t:void 0}],292335);var o=e.i(271645),c=e.i(602869),d=e.i(727749);function u(e){if(e instanceof Error)return e.message;if(e&&"object"==typeof e){let t=e.detail;return"string"==typeof t?t:Array.isArray(t)?t.map(e=>e&&"object"==typeof e?"string"==typeof e.msg?e.msg:JSON.stringify(e):String(e)).join("; "):t&&"object"==typeof t&&"string"==typeof t.error?t.error:"string"==typeof e.message?e.message:JSON.stringify(e)}return String(e)}e.s(["extractErrorMessage",0,u],122520);let m=e=>{let t=new Uint8Array(e),r="";return t.forEach(e=>r+=String.fromCharCode(e)),btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")},g=()=>{let e=new Uint8Array(32);return window.crypto.getRandomValues(e),m(e.buffer)},h=async e=>{let t=new TextEncoder().encode(e);return m(await window.crypto.subtle.digest("SHA-256",t))};e.s(["generateCodeChallenge",0,h,"generateCodeVerifier",0,g],165615);var f=e.i(434166);let p=()=>{{let e=window.location.pathname||"",t=e.indexOf("/ui"),r=t>=0?e.slice(0,t+3).replace(/\/+$/,""):"";return`${window.location.origin}${r}/mcp/oauth/callback`}},b=(...e)=>{e.forEach(e=>{try{window.sessionStorage.removeItem(e)}catch(e){}})};e.s(["TOOLS_OAUTH_UI_STATE_KEY",0,"litellm-mcp-oauth-tools-state","buildCallbackUrl",0,p,"clearStorage",0,b],779129);let x="litellm-user-mcp-oauth-flow-state",v="litellm-user-mcp-oauth-result",y=(e,t)=>{(0,f.setSecureItem)(e,t)},w=e=>(0,f.getSecureItem)(e);e.s(["useUserMcpOAuthFlow",0,({accessToken:e,serverId:t,serverAlias:r,scopes:a,clientId:l,onSuccess:s})=>{let[i,n]=(0,o.useState)("idle"),[m,f]=(0,o.useState)(null),C=(0,o.useRef)(!1),k=(0,o.useCallback)(async()=>{try{let s;n("authorizing"),f(null);let i=l??void 0;if(!i)try{let a=await (0,c.registerMcpOAuthClient)(e,t,{client_name:r||t,grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"});i=a?.client_id,s=a?.client_secret}catch(e){}let o=g(),d=await h(o),u=crypto.randomUUID(),m=p(),b=a?.filter(e=>e.trim()).join(" "),v=(0,c.buildMcpOAuthAuthorizeUrl)({serverId:t,clientId:i,redirectUri:m,state:u,codeChallenge:d,scope:b}),w={state:u,codeVerifier:o,serverId:t,redirectUri:m,clientId:i,clientSecret:s,scopes:a};y(x,JSON.stringify(w));let C=new URL(window.location.href);C.searchParams.set("mcpOauthReturn","apps"),y("litellm-mcp-oauth-return-url",C.toString()),window.location.href=v}catch(t){let e=u(t);f(e),n("error"),d.default.error(e)}},[e,t,r,a,l]),j=(0,o.useCallback)(async()=>{if(C.current)return;let r=w(v);if(!r)return;let a=w(x);if(!a)return;try{let e=JSON.parse(a);if(e.serverId&&e.serverId!==t)return}catch(e){}C.current=!0,b(v);let l=null,i=null;try{l=JSON.parse(r);let e=w(x);i=e?JSON.parse(e):null}catch(e){f("Failed to resume OAuth flow. Please retry."),n("error"),C.current=!1,b(x);return}try{if(!i?.state||!i.codeVerifier||!i.serverId)throw Error("OAuth session state was lost. Please retry.");if(!l?.state||l.state!==i.state)throw Error("OAuth state mismatch. Please retry.");if(l.error)throw Error(l.error_description||l.error);if(!l.code)throw Error("Authorization code missing in callback.");n("exchanging");let t=await (0,c.exchangeMcpOAuthToken)({serverId:i.serverId,code:l.code,clientId:i.clientId,clientSecret:i.clientSecret,codeVerifier:i.codeVerifier,redirectUri:i.redirectUri,accessToken:e});await (0,c.storeMCPOAuthUserCredential)(e,i.serverId,{access_token:t.access_token,refresh_token:t.refresh_token,expires_in:t.expires_in,scopes:i.scopes}),n("success"),f(null),d.default.success("Connected successfully"),s()}catch(t){let e=u(t);f(e),n("error"),d.default.error(e)}finally{b(x),setTimeout(()=>{C.current=!1},1e3)}},[e,t,s]);return(0,o.useEffect)(()=>{j()},[j]),{startOAuthFlow:k,status:i,error:m}}],280024)},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),l=e.i(529681);let s=e=>{let{prefixCls:a,className:l,style:s,size:i,shape:n}=e,o=(0,r.default)({[`${a}-lg`]:"large"===i,[`${a}-sm`]:"small"===i}),c=(0,r.default)({[`${a}-circle`]:"circle"===n,[`${a}-square`]:"square"===n,[`${a}-round`]:"round"===n}),d=t.useMemo(()=>"number"==typeof i?{width:i,height:i,lineHeight:`${i}px`}:{},[i]);return t.createElement("span",{className:(0,r.default)(a,o,c,l),style:Object.assign(Object.assign({},d),s)})};e.i(296059);var i=e.i(694758),n=e.i(915654),o=e.i(246422),c=e.i(838378);let d=new i.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),u=e=>({height:e,lineHeight:(0,n.unit)(e)}),m=e=>Object.assign({width:e},u(e)),g=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},u(e)),h=e=>Object.assign({width:e},u(e)),f=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},p=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},u(e)),b=(0,o.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:l,skeletonButtonCls:s,skeletonInputCls:i,skeletonImageCls:n,controlHeight:o,controlHeightLG:c,controlHeightSM:u,gradientFromColor:b,padding:x,marginSM:v,borderRadius:y,titleHeight:w,blockRadius:C,paragraphLiHeight:k,controlHeightXS:j,paragraphMarginTop:N}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:x,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:b},m(o)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},m(c)),[`${r}-sm`]:Object.assign({},m(u))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:w,background:b,borderRadius:C,[`+ ${l}`]:{marginBlockStart:u}},[l]:{padding:0,"> li":{width:"100%",height:k,listStyle:"none",background:b,borderRadius:C,"+ li":{marginBlockStart:j}}},[`${l}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${l} > li`]:{borderRadius:y}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:v,[`+ ${l}`]:{marginBlockStart:N}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:l,controlHeightSM:s,gradientFromColor:i,calc:n}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:t,width:n(a).mul(2).equal(),minWidth:n(a).mul(2).equal()},p(a,n))},f(e,a,r)),{[`${r}-lg`]:Object.assign({},p(l,n))}),f(e,l,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},p(s,n))}),f(e,s,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:l,controlHeightSM:s}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},m(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},m(l)),[`${t}${t}-sm`]:Object.assign({},m(s))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:l,controlHeightSM:s,gradientFromColor:i,calc:n}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:r},g(t,n)),[`${a}-lg`]:Object.assign({},g(l,n)),[`${a}-sm`]:Object.assign({},g(s,n))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:l,calc:s}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:l},h(s(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},h(r)),{maxWidth:s(r).mul(4).equal(),maxHeight:s(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[s]:{width:"100%"},[i]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${l} > li, + ${r}, + ${s}, + ${i}, + ${n} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:d,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,c.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),x=e=>{let{prefixCls:a,className:l,style:s,rows:i=0}=e,n=Array.from({length:i}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,l),style:s},n)},v=({prefixCls:e,className:a,width:l,style:s})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:l},s)});function y(e){return e&&"object"==typeof e?e:{}}let w=e=>{let{prefixCls:l,loading:i,className:n,rootClassName:o,style:c,children:d,avatar:u=!1,title:m=!0,paragraph:g=!0,active:h,round:f}=e,{getPrefixCls:p,direction:w,className:C,style:k}=(0,a.useComponentConfig)("skeleton"),j=p("skeleton",l),[N,O,$]=b(j);if(i||!("loading"in e)){let e,a,l=!!u,i=!!m,d=!!g;if(l){let r=Object.assign(Object.assign({prefixCls:`${j}-avatar`},i&&!d?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),y(u));e=t.createElement("div",{className:`${j}-header`},t.createElement(s,Object.assign({},r)))}if(i||d){let e,r;if(i){let r=Object.assign(Object.assign({prefixCls:`${j}-title`},!l&&d?{width:"38%"}:l&&d?{width:"50%"}:{}),y(m));e=t.createElement(v,Object.assign({},r))}if(d){let e,a=Object.assign(Object.assign({prefixCls:`${j}-paragraph`},(e={},l&&i||(e.width="61%"),!l&&i?e.rows=3:e.rows=2,e)),y(g));r=t.createElement(x,Object.assign({},a))}a=t.createElement("div",{className:`${j}-content`},e,r)}let p=(0,r.default)(j,{[`${j}-with-avatar`]:l,[`${j}-active`]:h,[`${j}-rtl`]:"rtl"===w,[`${j}-round`]:f},C,n,o,O,$);return N(t.createElement("div",{className:p,style:Object.assign(Object.assign({},k),c)},e,a))}return null!=d?d:null};w.Button=e=>{let{prefixCls:i,className:n,rootClassName:o,active:c,block:d=!1,size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",i),[h,f,p]=b(g),x=(0,l.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},n,o,f,p);return h(t.createElement("div",{className:v},t.createElement(s,Object.assign({prefixCls:`${g}-button`,size:u},x))))},w.Avatar=e=>{let{prefixCls:i,className:n,rootClassName:o,active:c,shape:d="circle",size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",i),[h,f,p]=b(g),x=(0,l.default)(e,["prefixCls","className"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c},n,o,f,p);return h(t.createElement("div",{className:v},t.createElement(s,Object.assign({prefixCls:`${g}-avatar`,shape:d,size:u},x))))},w.Input=e=>{let{prefixCls:i,className:n,rootClassName:o,active:c,block:d,size:u="default"}=e,{getPrefixCls:m}=t.useContext(a.ConfigContext),g=m("skeleton",i),[h,f,p]=b(g),x=(0,l.default)(e,["prefixCls"]),v=(0,r.default)(g,`${g}-element`,{[`${g}-active`]:c,[`${g}-block`]:d},n,o,f,p);return h(t.createElement("div",{className:v},t.createElement(s,Object.assign({prefixCls:`${g}-input`,size:u},x))))},w.Image=e=>{let{prefixCls:l,className:s,rootClassName:i,style:n,active:o}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),d=c("skeleton",l),[u,m,g]=b(d),h=(0,r.default)(d,`${d}-element`,{[`${d}-active`]:o},s,i,m,g);return u(t.createElement("div",{className:h},t.createElement("div",{className:(0,r.default)(`${d}-image`,s),style:n},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${d}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${d}-image-path`})))))},w.Node=e=>{let{prefixCls:l,className:s,rootClassName:i,style:n,active:o,children:c}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),u=d("skeleton",l),[m,g,h]=b(u),f=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:o},g,s,i,h);return m(t.createElement("div",{className:f},t.createElement("div",{className:(0,r.default)(`${u}-image`,s),style:n},c)))},e.s(["default",0,w],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let l=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(l),s=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),s.current=r)}else a.remove(s.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let l={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",l);let s=e<0?"-":"",i=Math.abs(e),n=i,o="";return i>=1e6?(n=i/1e6,o="M"):i>=1e3&&(n=i/1e3,o="K"),`${s}${n.toLocaleString("en-US",l)}${o}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return l(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),l(e,r)}},l=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let l=document.execCommand("copy");if(document.body.removeChild(a),l)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),l=e.i(746798);function s({content:e,trigger:r}){return(0,t.jsx)(l.TooltipProvider,{delay:300,children:(0,t.jsxs)(l.Tooltip,{children:[(0,t.jsx)(l.TooltipTrigger,{render:r}),(0,t.jsx)(l.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,s],581070);let i={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:l,tooltip:n,dataTestId:o}){let c=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":o,className:(0,a.cn)("whitespace-nowrap font-normal",i[e]),children:l});return n?(0,t.jsx)(s,{content:n,trigger:c}):c}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],l=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:s="datetime",fallback:i="-"}){let n,o,c,d=e?new Date(e):null;return!d||Number.isNaN(d.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:i}):(0,r.jsx)(t.CellTooltip,{content:(n=Intl.DateTimeFormat().resolvedOptions().timeZone,o=`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`,c=`${l(d.getHours())}:${l(d.getMinutes())}:${l(d.getSeconds())}`,`${o}, ${c} (${n})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===s?`${a[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`:`${a[d.getMonth()]} ${d.getDate()}, ${l(d.getHours())}:${l(d.getMinutes())}:${l(d.getSeconds())}`})})}],200208);var s=e.i(174886),i=e.i(115504),n=e.i(500330);let o={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:l,copyable:c=!1,truncate:d=!0,fallback:u="-",tooltip:m,disabled:g=!1,dataTestId:h,className:f}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:u});let p=!!l&&!g,b=(0,i.cn)(o[a].base,p&&o[a].clickable,d&&"block max-w-[15ch] truncate",g&&"opacity-50",f),x=p?(0,r.jsx)("button",{type:"button",className:b,"data-testid":h,onClick:()=>l(e),children:e}):(0,r.jsx)("span",{className:b,"data-testid":h,children:e}),v=(0,r.jsx)(t.CellTooltip,{content:m??e,trigger:x});return c?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[v,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,n.copyToClipboard)(e)},children:(0,r.jsx)(s.Copy,{className:"size-3"})})]}):v}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:l=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?l?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,n.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,n.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),l=e.i(480731),s=e.i(444755),i=e.i(673706),n=e.i(95779);let o={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},c={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},d={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,i.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:g,variant:h="simple",tooltip:f,size:p=l.Sizes.SM,color:b,className:x}=e,v=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),y=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,i.getColorClassNames)(t,n.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,i.getColorClassNames)(t,n.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,s.tremorTwMerge)((0,i.getColorClassNames)(t,n.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(h,b),{tooltipProps:w,getReferenceProps:C}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([m,w.refs.setReference]),className:(0,s.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",y.bgColor,y.textColor,y.borderColor,y.ringColor,d[h].rounded,d[h].border,d[h].shadow,d[h].ring,o[p].paddingX,o[p].paddingY,x)},C,v),r.default.createElement(a.default,Object.assign({text:f},w)),r.default.createElement(g,{className:(0,s.tremorTwMerge)(u("icon"),"shrink-0",c[p].height,c[p].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},678784,e=>{"use strict";var t=e.i(678745);e.s(["CheckIcon",()=>t.default])},118366,e=>{"use strict";var t=e.i(991124);e.s(["CopyIcon",()=>t.default])},987432,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M893.3 293.3L730.7 130.7c-7.5-7.5-16.7-13-26.7-16V112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V338.5c0-17-6.7-33.2-18.7-45.2zM384 184h256v104H384V184zm456 656H184V184h136v136c0 17.7 14.3 32 32 32h320c17.7 0 32-14.3 32-32V205.8l136 136V840zM512 442c-79.5 0-144 64.5-144 144s64.5 144 144 144 144-64.5 144-144-64.5-144-144-144zm0 224c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z"}}]},name:"save",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["SaveOutlined",0,s],987432)},995926,e=>{"use strict";var t=e.i(841947);e.s(["XIcon",()=>t.default])},54131,399219,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-up",[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]]);e.s(["default",0,t],399219),e.s(["ChevronUpIcon",0,t],54131)},750113,e=>{"use strict";var t=e.i(684024);e.s(["QuestionCircleOutlined",()=>t.default])},788191,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M719.4 499.1l-296.1-215A15.9 15.9 0 00398 297v430c0 13.1 14.8 20.5 25.3 12.9l296.1-215a15.9 15.9 0 000-25.8zm-257.6 134V390.9L628.5 512 461.8 633.1z"}}]},name:"play-circle",theme:"outlined"};var l=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(l.default,(0,t.default)({},e,{ref:s,icon:a}))});e.s(["PlayCircleOutlined",0,s],788191)},21548,e=>{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},988846,e=>{"use strict";var t=e.i(54943);e.s(["SearchIcon",()=>t.default])},438100,e=>{"use strict";let t=(0,e.i(475254).default)("key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]]);e.s(["KeyIcon",0,t],438100)},302202,e=>{"use strict";var t=e.i(953651);e.s(["ServerIcon",()=>t.default])},328196,e=>{"use strict";var t=e.i(361653);e.s(["AlertCircleIcon",()=>t.default])},634831,e=>{"use strict";var t=e.i(546467);e.s(["ExternalLinkIcon",()=>t.default])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0s6wj75..ba9e.js b/litellm/proxy/_experimental/out/_next/static/chunks/0s6wj75..ba9e.js new file mode 100644 index 00000000000..a0859a94988 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0s6wj75..ba9e.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,360179,759684,e=>{"use strict";var t,r,l,n,o,a=e.i(843476),i=e.i(618566),s=e.i(107233),c=e.i(686311),u=e.i(373264),d=e.i(465261),h=e.i(319023),f=e.i(217923),x=e.i(519455),v=e.i(772436),p=e.i(571353),m=e.i(405033),g=e.i(271645),w=e.i(788699),y=e.i(727612),b=e.i(555436),S=e.i(793479),j=e.i(776639),E=e.i(868499),C=e.i(746798);e.s([],673176),e.i(673176),e.i(247167);var N=e.i(667865),A=e.i(439957),R=e.i(733332);let T=g.createContext(void 0);function k(){let e=g.useContext(T);if(void 0===e)throw Error((0,R.default)(53));return e}var P=e.i(552245);let O=((t={}).scrollAreaCornerHeight="--scroll-area-corner-height",t.scrollAreaCornerWidth="--scroll-area-corner-width",t);function D(e,t,r){if(!e)return 0;let l=getComputedStyle(e),n="x"===r?"Inline":"Block";return"x"===r&&"margin"===t?2*parseFloat(l[`${t}InlineStart`]):parseFloat(l[`${t}${n}Start`])+parseFloat(l[`${t}${n}End`])}let M=((r={}).orientation="data-orientation",r.hovering="data-hovering",r.scrolling="data-scrolling",r.hasOverflowX="data-has-overflow-x",r.hasOverflowY="data-has-overflow-y",r.overflowXStart="data-overflow-x-start",r.overflowXEnd="data-overflow-x-end",r.overflowYStart="data-overflow-y-start",r.overflowYEnd="data-overflow-y-end",r);var Y=e.i(60837),H=e.i(788015);let X=((l={}).scrolling="data-scrolling",l.hasOverflowX="data-has-overflow-x",l.hasOverflowY="data-has-overflow-y",l.overflowXStart="data-overflow-x-start",l.overflowXEnd="data-overflow-x-end",l.overflowYStart="data-overflow-y-start",l.overflowYEnd="data-overflow-y-end",l),W={hasOverflowX:e=>e?{[X.hasOverflowX]:""}:null,hasOverflowY:e=>e?{[X.hasOverflowY]:""}:null,overflowXStart:e=>e?{[X.overflowXStart]:""}:null,overflowXEnd:e=>e?{[X.overflowXEnd]:""}:null,overflowYStart:e=>e?{[X.overflowYStart]:""}:null,overflowYEnd:e=>e?{[X.overflowYEnd]:""}:null,cornerHidden:()=>null};var $=e.i(647554),I=e.i(172410);let L={x:0,y:0},z={width:0,height:0},B={xStart:!1,xEnd:!1,yStart:!1,yEnd:!1},K={x:!0,y:!0,corner:!0},U=g.forwardRef(function(e,t){let{render:r,className:l,overflowEdgeThreshold:n,style:o,...i}=e,{xStart:s,xEnd:c,yStart:u,yEnd:d}=function(e){if("number"==typeof e){let t=Math.max(0,e);return{xStart:t,xEnd:t,yStart:t,yEnd:t}}return{xStart:Math.max(0,e?.xStart||0),xEnd:Math.max(0,e?.xEnd||0),yStart:Math.max(0,e?.yStart||0),yEnd:Math.max(0,e?.yEnd||0)}}(n),h=(0,H.useBaseUiId)(),f=(0,A.useTimeout)(),x=(0,A.useTimeout)(),{nonce:v,disableStyleElements:p}=(0,I.useCSPContext)(),[m,w]=g.useState(!1),[y,b]=g.useState(!1),[S,j]=g.useState(!1),[E,C]=g.useState(!1),[R,k]=g.useState(!1),[X,U]=g.useState(z),[F,q]=g.useState(z),[V,_]=g.useState(B),[G,J]=g.useState(K),Q=g.useRef(null),Z=g.useRef(null),ee=g.useRef(null),et=g.useRef(null),er=g.useRef(null),el=g.useRef(null),en=g.useRef(null),eo=g.useRef(!1),ea=g.useRef(0),ei=g.useRef(0),es=g.useRef(0),ec=g.useRef(0),eu=g.useRef("vertical"),ed=g.useRef(L),eh=(0,N.useStableCallback)(e=>{let t=e.x-ed.current.x,r=e.y-ed.current.y;ed.current=e,0!==r&&(j(!0),f.start(500,()=>{j(!1)})),0!==t&&(b(!0),x.start(500,()=>{b(!1)}))}),ef=(0,N.useStableCallback)(e=>{0===e.button&&(eo.current=!0,ea.current=e.clientY,ei.current=e.clientX,eu.current=e.currentTarget.getAttribute(M.orientation),Z.current&&(es.current=Z.current.scrollTop,ec.current=Z.current.scrollLeft),er.current&&"vertical"===eu.current&&er.current.setPointerCapture(e.pointerId),el.current&&"horizontal"===eu.current&&el.current.setPointerCapture(e.pointerId))}),ex=(0,N.useStableCallback)(e=>{if(!eo.current)return;let t=e.clientY-ea.current,r=e.clientX-ei.current;if(Z.current){let l=Z.current.scrollHeight,n=Z.current.clientHeight,o=Z.current.scrollWidth,a=Z.current.clientWidth;if(er.current&&ee.current&&"vertical"===eu.current){let r=D(ee.current,"padding","y"),o=D(er.current,"margin","y"),a=er.current.offsetHeight,i=ee.current.offsetHeight-a-r-o;Z.current.scrollTop=es.current+t/i*(l-n),e.preventDefault(),j(!0),f.start(500,()=>{j(!1)})}if(el.current&&et.current&&"horizontal"===eu.current){let t=D(et.current,"padding","x"),l=D(el.current,"margin","x"),n=el.current.offsetWidth,i=et.current.offsetWidth-n-t-l;Z.current.scrollLeft=ec.current+r/i*(o-a),e.preventDefault(),b(!0),x.start(500,()=>{b(!1)})}}}),ev=(0,N.useStableCallback)(e=>{eo.current=!1,er.current&&"vertical"===eu.current&&er.current.hasPointerCapture(e.pointerId)&&er.current.releasePointerCapture(e.pointerId),el.current&&"horizontal"===eu.current&&el.current.hasPointerCapture(e.pointerId)&&el.current.releasePointerCapture(e.pointerId)});function ep(e){C("touch"===e.pointerType)}function em(e){ep(e),"touch"!==e.pointerType&&w((0,$.contains)(Q.current,e.target))}let eg=g.useMemo(()=>({scrolling:y||S,hasOverflowX:!G.x,hasOverflowY:!G.y,overflowXStart:V.xStart,overflowXEnd:V.xEnd,overflowYStart:V.yStart,overflowYEnd:V.yEnd,cornerHidden:G.corner}),[y,S,G.x,G.y,G.corner,V]),ew={role:"presentation",onPointerEnter:em,onPointerMove:em,onPointerDown:ep,onPointerLeave(){w(!1)},style:{position:"relative",[O.scrollAreaCornerHeight]:`${X.height}px`,[O.scrollAreaCornerWidth]:`${X.width}px`}},ey=(0,P.useRenderElement)("div",e,{state:eg,ref:[t,Q],props:[ew,i],stateAttributesMapping:W}),eb=g.useMemo(()=>({handlePointerDown:ef,handlePointerMove:ex,handlePointerUp:ev,handleScroll:eh,cornerSize:X,setCornerSize:U,thumbSize:F,setThumbSize:q,hasMeasuredScrollbar:R,setHasMeasuredScrollbar:k,touchModality:E,cornerRef:en,scrollingX:y,setScrollingX:b,scrollingY:S,setScrollingY:j,hovering:m,setHovering:w,viewportRef:Z,rootRef:Q,scrollbarYRef:ee,scrollbarXRef:et,thumbYRef:er,thumbXRef:el,rootId:h,hiddenState:G,setHiddenState:J,overflowEdges:V,setOverflowEdges:_,viewportState:eg,overflowEdgeThreshold:{xStart:s,xEnd:c,yStart:u,yEnd:d}}),[ef,ex,ev,eh,X,F,R,E,y,b,S,j,m,w,h,G,V,eg,s,c,u,d]);return(0,a.jsxs)(T.Provider,{value:eb,children:[!p&&Y.styleDisableScrollbar.getElement(v),ey]})});var F=e.i(146376),q=e.i(328744);let V=g.createContext(void 0);var _=e.i(872855),G=e.i(201675);let J=((n={}).scrollAreaOverflowXStart="--scroll-area-overflow-x-start",n.scrollAreaOverflowXEnd="--scroll-area-overflow-x-end",n.scrollAreaOverflowYStart="--scroll-area-overflow-y-start",n.scrollAreaOverflowYEnd="--scroll-area-overflow-y-end",n);var Q=e.i(550896);let Z=!1,ee=g.forwardRef(function(e,t){let{render:r,className:l,style:n,...o}=e,{viewportRef:i,scrollbarYRef:s,scrollbarXRef:c,thumbYRef:u,thumbXRef:d,cornerRef:h,cornerSize:f,setCornerSize:x,setThumbSize:v,rootId:p,setHiddenState:m,hiddenState:w,setHasMeasuredScrollbar:y,handleScroll:b,setHovering:S,setOverflowEdges:j,overflowEdges:E,overflowEdgeThreshold:C,scrollingX:R,scrollingY:T}=k(),O=(0,_.useDirection)(),M=g.useRef(!0),H=g.useRef([NaN,NaN,NaN,NaN]),X=(0,A.useTimeout)(),$=(0,A.useTimeout)(),I=(0,N.useStableCallback)(()=>{var e;let t,r,l=i.current,n=s.current,o=c.current,a=u.current,p=d.current,g=h.current;if(!l)return;let w=l.scrollHeight,b=l.scrollWidth,S=l.clientHeight,E=l.clientWidth,N=l.scrollTop,A=l.scrollLeft,R=H.current,T=Number.isNaN(R[0]);if(R[0]=S,R[1]=w,R[2]=E,R[3]=b,T&&y(!0),0===w||0===b)return;let k=(t=(e=l).clientHeight>=e.scrollHeight,{y:t,x:r=e.clientWidth>=e.scrollWidth,corner:t||r}),P=k.y,M=k.x,Y=E/b,X=S/w,W=Math.max(0,b-E),$=Math.max(0,w-S),I=0,L=0;if(!M){let e=0;e="rtl"===O?(0,G.clamp)(-A,0,W):(0,G.clamp)(A,0,W),I=(0,Q.normalizeScrollOffset)(e,W),L=W-I}let z=P?0:(0,G.clamp)(N,0,$),B=P?0:(0,Q.normalizeScrollOffset)(z,$),K=P?0:$-B,U=M?0:E,F=P?0:S,q=0,V=0;M||P||(q=n?.offsetWidth||0,V=o?.offsetHeight||0);let _=0===f.width&&0===f.height,Z=_?q:0,ee=_?V:0,et=D(o,"padding","x"),er=D(n,"padding","y"),el=D(p,"margin","x"),en=D(a,"margin","y"),eo=U-et-el,ea=F-er-en,ei=o?Math.min(o.offsetWidth-Z,eo):eo,es=n?Math.min(n.offsetHeight-ee,ea):ea,ec=Math.max(16,ei*Y),eu=Math.max(16,es*X);if(v(e=>e.height===eu&&e.width===ec?e:{width:ec,height:eu}),n&&a){let e=n.offsetHeight-eu-er-en,t=w-S,r=Math.min(e,Math.max(0,(0===t?0:N/t)*e));a.style.transform=`translate3d(0,${r}px,0)`}if(o&&p){let e=o.offsetWidth-ec-et-el,t=b-E,r=0===t?0:A/t,l="rtl"===O?(0,G.clamp)(r*e,-e,0):(0,G.clamp)(r*e,0,e);p.style.transform=`translate3d(${l}px,0,0)`}for(let[e,t]of[[J.scrollAreaOverflowXStart,I],[J.scrollAreaOverflowXEnd,L],[J.scrollAreaOverflowYStart,B],[J.scrollAreaOverflowYEnd,K]])l.style.setProperty(e,`${t}px`);g&&(M||P?x({width:0,height:0}):M||P||x({width:q,height:V})),m(e=>{var t,r;return t=e,r=k,t.y===r.y&&t.x===r.x&&t.corner===r.corner?t:r});let ed={xStart:!M&&I>C.xStart,xEnd:!M&&L>C.xEnd,yStart:!P&&B>C.yStart,yEnd:!P&&K>C.yEnd};j(e=>e.xStart===ed.xStart&&e.xEnd===ed.xEnd&&e.yStart===ed.yStart&&e.yEnd===ed.yEnd?e:ed)});function L(){M.current=!1}(0,F.useIsoLayoutEffect)(()=>{i.current&&(Z||q.platform.engine.webkit||("u">typeof CSS&&"registerProperty"in CSS&&[J.scrollAreaOverflowXStart,J.scrollAreaOverflowXEnd,J.scrollAreaOverflowYStart,J.scrollAreaOverflowYEnd].forEach(e=>{try{CSS.registerProperty({name:e,syntax:"",inherits:!1,initialValue:"0px"})}catch{}}),Z=!0))},[i]),(0,F.useIsoLayoutEffect)(()=>{queueMicrotask(I)},[I,w,O,C.xStart,C.xEnd,C.yStart,C.yEnd]),(0,F.useIsoLayoutEffect)(()=>{i.current?.matches(":hover")&&S(!0)},[i,S]),(0,F.useIsoLayoutEffect)(()=>{let e=i.current;if("u"{if(!t){t=!0;let r=H.current;if(r[0]===e.clientHeight&&r[1]===e.scrollHeight&&r[2]===e.clientWidth&&r[3]===e.scrollWidth)return}I()});return r.observe(e),$.start(0,()=>{let t=e.getAnimations({subtree:!0});0!==t.length&&Promise.allSettled(t.map(e=>e.finished)).then(I).catch(()=>{})}),()=>{r.disconnect(),$.clear()}},[I,i,$]);let z={role:"presentation",...p&&{"data-id":`${p}-viewport`},tabIndex:w.x&&w.y?-1:0,className:Y.styleDisableScrollbar.className,style:{overflow:"scroll"},onScroll(){i.current&&(I(),M.current||b({x:i.current.scrollLeft,y:i.current.scrollTop}),X.start(100,()=>{M.current=!0}))},onWheel:L,onTouchMove:L,onPointerMove:L,onPointerEnter:L,onKeyDown:L},B=g.useMemo(()=>({scrolling:R||T,hasOverflowX:!w.x,hasOverflowY:!w.y,overflowXStart:E.xStart,overflowXEnd:E.xEnd,overflowYStart:E.yStart,overflowYEnd:E.yEnd,cornerHidden:w.corner}),[R,T,w.x,w.y,w.corner,E]),K=(0,P.useRenderElement)("div",e,{ref:[t,i],state:B,props:[z,o],stateAttributesMapping:W}),U=g.useMemo(()=>({computeThumbPosition:I}),[I]);return(0,a.jsx)(V.Provider,{value:U,children:K})});var et=e.i(574735);let er=g.createContext(void 0),el=((o={}).scrollAreaThumbHeight="--scroll-area-thumb-height",o.scrollAreaThumbWidth="--scroll-area-thumb-width",o),en=g.forwardRef(function(e,t){let{render:r,className:l,orientation:n="vertical",keepMounted:o=!1,style:i,...s}=e,{hovering:c,scrollingX:u,scrollingY:d,hiddenState:h,overflowEdges:f,scrollbarYRef:x,scrollbarXRef:v,viewportRef:p,thumbYRef:m,thumbXRef:w,handlePointerDown:y,handlePointerUp:b,handleScroll:S,rootId:j,thumbSize:E,hasMeasuredScrollbar:C}=k(),N={hovering:c,scrolling:{horizontal:u,vertical:d}[n],orientation:n,hasOverflowX:!h.x,hasOverflowY:!h.y,overflowXStart:f.xStart,overflowXEnd:f.xEnd,overflowYStart:f.yStart,overflowYEnd:f.yEnd,cornerHidden:h.corner},A=(0,_.useDirection)(),R=!C&&!o,T="vertical"===n?h.y:h.x,M=o||!T;g.useEffect(()=>{if(!M)return;let e=p.current,t="vertical"===n?x.current:v.current;if(t)return(0,et.addEventListener)(t,"wheel",function(r){if(!e||!t||r.ctrlKey)return;let l="horizontal"===n,o=l?"scrollLeft":"scrollTop",a=l?r.deltaX:r.deltaY;if(0===a)return;let i=l?e.scrollWidth-e.clientWidth:e.scrollHeight-e.clientHeight,s=l&&"rtl"===A?-i:0,c=l&&"rtl"===A?0:i,u=e[o];u<=s&&a<0||u>=c&&a>0||(r.preventDefault(),e[o]=Math.min(c,Math.max(s,u+a)),S({x:e.scrollLeft,y:e.scrollTop}))},{passive:!1})},[A,S,n,v,x,M,p]);let Y={...j&&{"data-id":`${j}-scrollbar`},onPointerDown(e){if(0!==e.button)return;let t=(0,$.getTarget)(e.nativeEvent),r="vertical"===n?m.current:w.current;if(!(r&&(0,$.contains)(r,t))&&p.current){if(m.current&&x.current&&"vertical"===n){let t=D(m.current,"margin","y"),r=D(x.current,"padding","y"),l=m.current.offsetHeight,n=x.current.getBoundingClientRect(),o=e.clientY-n.top-l/2-r+t/2,a=p.current.scrollHeight,i=p.current.clientHeight,s=x.current.offsetHeight-l-r-t;p.current.scrollTop=o/s*(a-i)}if(w.current&&v.current&&"horizontal"===n){let t,r=D(w.current,"margin","x"),l=D(v.current,"padding","x"),n=w.current.offsetWidth,o=v.current.getBoundingClientRect(),a=e.clientX-o.left-n/2-l+r/2,i=p.current.scrollWidth,s=p.current.clientWidth,c=a/(v.current.offsetWidth-n-l-r);"rtl"===A?(t=(1-c)*(i-s),p.current.scrollLeft<=0&&(t=-t)):t=c*(i-s),p.current.scrollLeft=t}S({x:p.current.scrollLeft,y:p.current.scrollTop}),y(e)}},onPointerUp:b,onPointerCancel:b,style:{position:"absolute",touchAction:"none",WebkitUserSelect:"none",userSelect:"none",visibility:R?"hidden":void 0,..."vertical"===n&&{top:0,bottom:`var(${O.scrollAreaCornerHeight})`,insetInlineEnd:0,[el.scrollAreaThumbHeight]:`${E.height}px`},..."horizontal"===n&&{insetInlineStart:0,insetInlineEnd:`var(${O.scrollAreaCornerWidth})`,bottom:0,[el.scrollAreaThumbWidth]:`${E.width}px`}}},H=(0,P.useRenderElement)("div",e,{ref:[t,"vertical"===n?x:v],state:N,props:[Y,s],stateAttributesMapping:W}),X=g.useMemo(()=>({orientation:n}),[n]);return M?(0,a.jsx)(er.Provider,{value:X,children:H}):null}),eo=g.forwardRef(function(e,t){let{render:r,className:l,style:n,...o}=e,{computeThumbPosition:a}=function(){let e=g.useContext(V);if(void 0===e)throw Error((0,R.default)(55));return e}(),{hasMeasuredScrollbar:i,viewportState:s}=k(),c=g.useRef(null),u=g.useRef(i);return(0,F.useIsoLayoutEffect)(()=>{if("u"{(e||(e=!0,u.current))&&a()});return c.current&&t.observe(c.current),()=>{t.disconnect()}},[a]),(0,P.useRenderElement)("div",e,{ref:[t,c],state:s,stateAttributesMapping:W,props:[{role:"presentation",style:{minWidth:"fit-content"}},o]})}),ea=g.forwardRef(function(e,t){let{render:r,className:l,style:n,...o}=e,{thumbYRef:a,thumbXRef:i,handlePointerDown:s,handlePointerMove:c,handlePointerUp:u,setScrollingX:d,setScrollingY:h,scrollingX:f,scrollingY:x,hasMeasuredScrollbar:v}=k(),{orientation:p}=function(){let e=g.useContext(er);if(void 0===e)throw Error((0,R.default)(54));return e}();function m(e){"vertical"===p&&h(!1),"horizontal"===p&&d(!1),u(e)}return(0,P.useRenderElement)("div",e,{ref:[t,"vertical"===p?a:i],state:{scrolling:"horizontal"===p?f:x,orientation:p},props:[{onPointerDown:s,onPointerMove:c,onPointerUp:m,onPointerCancel:m,style:{visibility:v?void 0:"hidden",..."vertical"===p&&{height:`var(${el.scrollAreaThumbHeight})`},..."horizontal"===p&&{width:`var(${el.scrollAreaThumbWidth})`}}},o]})}),ei=g.forwardRef(function(e,t){let{render:r,className:l,style:n,...o}=e,{cornerRef:a,cornerSize:i,hiddenState:s}=k(),c=(0,P.useRenderElement)("div",e,{ref:[t,a],props:[{style:{position:"absolute",bottom:0,insetInlineEnd:0,width:i.width,height:i.height}},o]});return s.corner?null:c});e.s(["Content",0,eo,"Corner",0,ei,"Root",0,U,"Scrollbar",0,en,"Thumb",0,ea,"Viewport",0,ee],236093);var es=e.i(236093),es=es,ec=e.i(115504);function eu({className:e,children:t,...r}){return(0,a.jsxs)(es.Root,{"data-slot":"scroll-area",className:(0,ec.cn)("relative",e),...r,children:[(0,a.jsx)(es.Viewport,{"data-slot":"scroll-area-viewport",className:"size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",children:t}),(0,a.jsx)(ed,{}),(0,a.jsx)(es.Corner,{})]})}function ed({className:e,orientation:t="vertical",...r}){return(0,a.jsx)(es.Scrollbar,{"data-slot":"scroll-area-scrollbar","data-orientation":t,orientation:t,className:(0,ec.cn)("flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",e),...r,children:(0,a.jsx)(es.Thumb,{"data-slot":"scroll-area-thumb",className:"relative flex-1 rounded-full bg-border"})})}e.s(["ScrollArea",0,eu],759684);var eh=e.i(822315);let ef=e=>{let t=(0,eh.default)(),r=(0,eh.default)(e);return r.isSame(t,"day")?"Recents":r.isSame(t.subtract(1,"day"),"day")?"Yesterday":r.isAfter(t.subtract(7,"day"))?"Last 7 Days":"Older"},ex=["Recents","Yesterday","Last 7 Days","Older"],ev=({conv:e,isActive:t,onSelect:r,onDelete:l,onRename:n})=>{let[o,i]=(0,g.useState)(!1),[s,c]=(0,g.useState)(e.title),u=(0,g.useRef)(null);(0,g.useEffect)(()=>{o&&u.current&&(u.current.focus(),u.current.select())},[o]);let d=()=>{let t=s.trim();t&&t!==e.title&&n(e.id,t),i(!1)},h=e.title.length>40?e.title.slice(0,40)+"…":e.title;return(0,a.jsx)("div",{onClick:()=>!o&&r(e.id),className:`group flex items-center px-2 py-1.5 rounded-md cursor-pointer transition-colors min-h-[34px] relative ${t?"bg-accent text-accent-foreground":"hover:bg-accent/50"}`,children:o?(0,a.jsx)(S.Input,{ref:u,value:s,onChange:e=>c(e.target.value),onKeyDown:t=>{"Enter"===t.key?(t.preventDefault(),d()):"Escape"===t.key&&(t.preventDefault(),c(e.title),i(!1))},onBlur:d,onClick:e=>e.stopPropagation(),className:"h-7 text-[13px] flex-1"}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("span",{className:`flex-1 text-[13px] overflow-hidden whitespace-nowrap text-ellipsis ${t?"font-medium":""}`,title:e.title,children:h}),(0,a.jsxs)("div",{className:"flex gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity shrink-0",onClick:e=>e.stopPropagation(),children:[(0,a.jsx)(C.TooltipProvider,{delay:300,children:(0,a.jsxs)(C.Tooltip,{children:[(0,a.jsx)(C.TooltipTrigger,{render:(0,a.jsx)(x.Button,{onClick:t=>{t.stopPropagation(),c(e.title),i(!0)},variant:"ghost",size:"icon-xs",className:"text-muted-foreground",children:(0,a.jsx)(w.Pencil,{className:"h-3 w-3"})})}),(0,a.jsx)(C.TooltipContent,{side:"bottom",children:(0,a.jsx)("p",{children:"Rename"})})]})}),(0,a.jsxs)(E.AlertDialog,{children:[(0,a.jsx)(C.TooltipProvider,{delay:300,children:(0,a.jsxs)(C.Tooltip,{children:[(0,a.jsx)(C.TooltipTrigger,{render:(0,a.jsx)(E.AlertDialogTrigger,{render:(0,a.jsx)(x.Button,{variant:"ghost",size:"icon-xs",className:"text-muted-foreground hover:text-destructive",children:(0,a.jsx)(y.Trash2,{className:"h-3 w-3"})})})}),(0,a.jsx)(C.TooltipContent,{side:"bottom",children:(0,a.jsx)("p",{children:"Delete"})})]})}),(0,a.jsxs)(E.AlertDialogContent,{children:[(0,a.jsxs)(E.AlertDialogHeader,{children:[(0,a.jsx)(E.AlertDialogTitle,{children:"Delete this conversation?"}),(0,a.jsx)(E.AlertDialogDescription,{children:"This action cannot be undone"})]}),(0,a.jsxs)(E.AlertDialogFooter,{children:[(0,a.jsx)(E.AlertDialogCancel,{children:"Cancel"}),(0,a.jsx)(E.AlertDialogAction,{onClick:()=>l(e.id),className:"bg-destructive text-white hover:bg-destructive/90",children:"Delete"})]})]})]})]})]})})},ep=({open:e,conversations:t,onSelect:r,onClose:l})=>{let[n,o]=(0,g.useState)(""),[i,s]=(0,g.useState)(e);e!==i&&(s(e),e||o(""));let u=n.trim()?t.filter(e=>e.title.toLowerCase().includes(n.trim().toLowerCase())):t;return(0,a.jsx)(j.Dialog,{open:e,onOpenChange:e=>!e&&l(),children:(0,a.jsxs)(j.DialogContent,{className:"sm:max-w-[480px] p-4 gap-0",children:[(0,a.jsxs)("div",{className:"relative mb-3",children:[(0,a.jsx)(b.Search,{className:"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground"}),(0,a.jsx)(S.Input,{autoFocus:!0,placeholder:"Search conversations\\u2026",value:n,onChange:e=>o(e.target.value),className:"pl-9"})]}),(0,a.jsx)(eu,{className:"max-h-[320px]",children:0===u.length?(0,a.jsx)("div",{className:"text-center py-6 text-muted-foreground text-sm",children:"No conversations found"}):u.map(e=>{let t=e.title.length>55?e.title.slice(0,55)+"…":e.title;return(0,a.jsxs)("div",{onClick:()=>{r(e.id),l()},className:"flex items-center gap-2 px-2.5 py-2 rounded-md cursor-pointer transition-colors hover:bg-accent/50",children:[(0,a.jsx)(c.MessageSquare,{className:"h-4 w-4 text-muted-foreground shrink-0"}),(0,a.jsx)("span",{className:"text-[13px] flex-1 truncate",children:t}),(0,a.jsx)("span",{className:"text-[11px] text-muted-foreground shrink-0 ml-auto",children:(0,eh.default)(e.updatedAt).format("MMM D")})]},e.id)})})]})})},em=({conversations:e,activeConversationId:t,onSelect:r,onDelete:l,onRename:n})=>{let[o,i]=(0,g.useState)(!1),s=(0,g.useCallback)(e=>{"k"===e.key&&(e.metaKey||e.ctrlKey)&&(e.preventDefault(),i(e=>!e))},[]);(0,g.useEffect)(()=>(document.addEventListener("keydown",s),()=>document.removeEventListener("keydown",s)),[s]);let c=(e=>{let t=new Map;for(let r of e){let e=ef(r.updatedAt);t.has(e)||t.set(e,[]),t.get(e).push(r)}return ex.filter(e=>t.has(e)).map(e=>({group:e,items:t.get(e)}))})(e);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"flex flex-col h-full w-full overflow-hidden",children:(0,a.jsx)(eu,{className:"flex-1 h-0 px-1.5 pt-2",children:0===c.length?(0,a.jsxs)("div",{className:"text-center text-muted-foreground/60 text-xs mt-8 px-3",children:["No conversations yet",(0,a.jsx)("br",{}),"Start a new chat above"]}):c.map(({group:e,items:o})=>(0,a.jsxs)("div",{className:"mb-2",children:[(0,a.jsx)("div",{className:"text-[11px] font-semibold text-muted-foreground uppercase tracking-wider px-2 pt-2 pb-1",children:e}),o.map(e=>(0,a.jsx)(ev,{conv:e,isActive:e.id===t,onSelect:r,onDelete:l,onRename:n},e.id))]},e))})}),(0,a.jsx)(ep,{open:o,conversations:e,onSelect:r,onClose:()=>i(!1)})]})},eg=(0,p.migratedHref)("chat"),ew={chats:eg,integrations:`${eg}/integrations`,credentials:`${eg}/credentials`,apiKeys:`${eg}/api-keys`,usage:`${eg}/usage`};function ey({icon:e,label:t,onClick:r,active:l=!1}){return(0,a.jsxs)(x.Button,{onClick:r,variant:"ghost","aria-current":l?"page":void 0,className:`w-full justify-start gap-2.5 px-2.5 font-medium hover:bg-sidebar-accent ${l?"bg-sidebar-accent text-sidebar-accent-foreground":"text-muted-foreground"}`,children:[(0,a.jsx)("span",{className:"shrink-0",children:e}),(0,a.jsx)("span",{className:"flex-1 text-left",children:t})]})}e.s(["CHAT_ROUTES",0,ew,"default",0,({children:e})=>{var t;let r=(0,i.useRouter)(),l=(t=(0,i.usePathname)()??"").length>1?t.replace(/\/+$/,""):t,{conversations:n,activeConversationId:o,deleteConversation:p,renameConversation:g}=(0,m.useChatShell)(),w=l===ew.chats;return(0,a.jsxs)("div",{className:"flex h-full w-full flex-col bg-background overflow-hidden",children:[(0,a.jsxs)("div",{className:"shrink-0 border-b border-amber-200 bg-amber-50 px-4 py-1.5 text-center text-[13px] text-amber-800",children:["This is a pre-v0 feature. Do not use in production, it may change unexpectedly. Please share feedback"," ",(0,a.jsx)("a",{href:"https://github.com/BerriAI/litellm/discussions/32085",target:"_blank",rel:"noreferrer",className:"font-medium underline",children:"here"}),"."]}),(0,a.jsxs)("div",{className:"flex flex-1 min-h-0 overflow-hidden",children:[(0,a.jsxs)("div",{className:"shrink-0 bg-sidebar border-sidebar-border border-r flex flex-col overflow-hidden w-[260px]",children:[(0,a.jsx)("div",{className:"px-2 pt-3 pb-1 shrink-0",children:(0,a.jsxs)(x.Button,{onClick:()=>r.push(ew.chats),className:"w-full justify-start gap-2.5",children:[(0,a.jsx)(s.Plus,{className:"h-4 w-4"}),"New Chat"]})}),(0,a.jsx)(v.Separator,{className:"mx-2 mt-2 shrink-0"}),(0,a.jsxs)("div",{className:"px-2 py-1 shrink-0",children:[(0,a.jsx)(ey,{icon:(0,a.jsx)(c.MessageSquare,{className:"h-4 w-4"}),label:"Chats",onClick:()=>r.push(ew.chats),active:w}),(0,a.jsx)(ey,{icon:(0,a.jsx)(u.LayoutGrid,{className:"h-4 w-4"}),label:"Integrations",onClick:()=>r.push(ew.integrations),active:l===ew.integrations}),(0,a.jsx)(ey,{icon:(0,a.jsx)(d.KeyRound,{className:"h-4 w-4"}),label:"Credentials",onClick:()=>r.push(ew.credentials),active:l===ew.credentials}),(0,a.jsx)(ey,{icon:(0,a.jsx)(h.Lock,{className:"h-4 w-4"}),label:"API Keys",onClick:()=>r.push(ew.apiKeys),active:l===ew.apiKeys}),(0,a.jsx)(ey,{icon:(0,a.jsx)(f.BarChart3,{className:"h-4 w-4"}),label:"Usage",onClick:()=>r.push(ew.usage),active:l===ew.usage})]}),(0,a.jsx)(v.Separator,{className:"mx-2 shrink-0"}),(0,a.jsx)("div",{className:"flex-1 overflow-hidden flex flex-col",children:(0,a.jsx)(em,{conversations:n,activeConversationId:o,onSelect:e=>r.push(`${ew.chats}?id=${e}`),onDelete:e=>{p(e),e===o&&r.push(ew.chats)},onRename:g})})]}),(0,a.jsx)("div",{className:"flex-1 flex flex-col overflow-hidden min-w-0",children:e})]})]})}],360179)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0scfmfivwcppe.js b/litellm/proxy/_experimental/out/_next/static/chunks/0scfmfivwcppe.js new file mode 100644 index 00000000000..976367045b8 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0scfmfivwcppe.js @@ -0,0 +1,10 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},954616,e=>{"use strict";var t=e.i(271645),n=e.i(114272),i=e.i(540143),l=e.i(915823),r=e.i(619273),a=class extends l.Subscribable{#e;#t=void 0;#n;#i;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#l()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,r.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,r.hashKey)(t.mutationKey)!==(0,r.hashKey)(this.options.mutationKey)?this.reset():this.#n?.state.status==="pending"&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#l(),this.#r(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#l(),this.#r()}mutate(e,t){return this.#i=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#l(){let e=this.#n?.state??(0,n.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#r(e){i.notifyManager.batch(()=>{if(this.#i&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,i={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#i.onSuccess?.(e.data,t,n,i)}catch(e){Promise.reject(e)}try{this.#i.onSettled?.(e.data,null,t,n,i)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#i.onError?.(e.error,t,n,i)}catch(e){Promise.reject(e)}try{this.#i.onSettled?.(void 0,e.error,t,n,i)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},o=e.i(912598);e.s(["useMutation",0,function(e,n){let l=(0,o.useQueryClient)(n),[s]=t.useState(()=>new a(l,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let c=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(i.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),d=t.useCallback((e,t)=>{s.mutate(e,t).catch(r.noop)},[s]);if(c.error&&(0,r.shouldThrowError)(s.options.throwOnError,[c.error]))throw c.error;return{...c,mutate:d,mutateAsync:c.mutate}}],954616)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var l=e.i(9583),r=n.forwardRef(function(e,r){return n.createElement(l.default,(0,t.default)({},e,{ref:r,icon:i}))});e.s(["ExclamationCircleOutlined",0,r],270377)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(529681),l=e.i(242064),r=e.i(517455),a=e.i(185793),o=e.i(721369),s=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let c=e=>{var{prefixCls:i,className:r,hoverable:a=!0}=e,o=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:c}=t.useContext(l.ConfigContext),d=c("card",i),u=(0,n.default)(`${d}-grid`,r,{[`${d}-grid-hoverable`]:a});return t.createElement("div",Object.assign({},o,{className:u}))};e.i(296059);var d=e.i(915654),u=e.i(183293),g=e.i(246422),b=e.i(838378);let p=(0,g.genStyleHooks)("Card",e=>{let t=(0,b.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:n,cardHeadPadding:i,colorBorderSecondary:l,boxShadowTertiary:r,bodyPadding:a,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:r},[`${t}-head`]:(e=>{let{antCls:t,componentCls:n,headerHeight:i,headerPadding:l,tabsMarginBottom:r}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:i,marginBottom:-1,padding:`0 ${(0,d.unit)(l)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${n}-typography, + > ${n}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:r,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:a,borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:n,cardShadow:i,lineWidth:l}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,d.unit)(l)} 0 0 0 ${n}, + 0 ${(0,d.unit)(l)} 0 0 ${n}, + ${(0,d.unit)(l)} ${(0,d.unit)(l)} 0 0 ${n}, + ${(0,d.unit)(l)} 0 0 0 ${n} inset, + 0 ${(0,d.unit)(l)} 0 0 ${n} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:i}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:n,actionsLiMargin:i,cardActionsIconSize:l,colorBorderSecondary:r,actionsBg:a}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:a,borderTop:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${r}`,display:"flex",borderRadius:`0 0 ${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:i,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${n}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,d.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${n}`]:{fontSize:l,lineHeight:(0,d.unit)(e.calc(l).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${r}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,d.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,d.unit)(e.lineWidth)} ${e.lineType} ${l}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:n}},[`${t}-contain-grid`]:{borderRadius:`${(0,d.unit)(e.borderRadiusLG)} ${(0,d.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:i}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:n,headerPadding:i,bodyPadding:l}=e;return{[`${t}-head`]:{padding:`0 ${(0,d.unit)(i)}`,background:n,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,d.unit)(e.padding)} ${(0,d.unit)(l)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:n,headerPaddingSM:i,headerHeightSM:l,headerFontSizeSM:r}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:l,padding:`0 ${(0,d.unit)(i)}`,fontSize:r,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:n}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,n;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(n=e.headerPadding)?n:e.paddingLG}});var m=e.i(792812),h=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let f=e=>{let{actionClasses:n,actions:i=[],actionStyle:l}=e;return t.createElement("ul",{className:n,style:l},i.map((e,n)=>{let l=`action-${n}`;return t.createElement("li",{style:{width:`${100/i.length}%`},key:l},t.createElement("span",null,e))}))},y=t.forwardRef((e,s)=>{let d,{prefixCls:u,className:g,rootClassName:b,style:y,extra:$,headStyle:v={},bodyStyle:O={},title:x,loading:j,bordered:S,variant:C,size:E,type:w,cover:N,actions:z,tabList:P,children:M,activeTabKey:B,defaultActiveTabKey:T,tabBarExtraContent:k,hoverable:R,tabProps:L={},classNames:G,styles:I}=e,H=h(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:W,direction:D,card:A}=t.useContext(l.ConfigContext),[F]=(0,m.default)("card",C,S),X=e=>{var t;return(0,n.default)(null==(t=null==A?void 0:A.classNames)?void 0:t[e],null==G?void 0:G[e])},K=e=>{var t;return Object.assign(Object.assign({},null==(t=null==A?void 0:A.styles)?void 0:t[e]),null==I?void 0:I[e])},q=t.useMemo(()=>{let e=!1;return t.Children.forEach(M,t=>{(null==t?void 0:t.type)===c&&(e=!0)}),e},[M]),U=W("card",u),[Q,V,_]=p(U),J=t.createElement(a.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},M),Y=void 0!==B,Z=Object.assign(Object.assign({},L),{[Y?"activeKey":"defaultActiveKey"]:Y?B:T,tabBarExtraContent:k}),ee=(0,r.default)(E),et=ee&&"default"!==ee?ee:"large",en=P?t.createElement(o.default,Object.assign({size:et},Z,{className:`${U}-head-tabs`,onChange:t=>{var n;null==(n=e.onTabChange)||n.call(e,t)},items:P.map(e=>{var{tab:t}=e;return Object.assign({label:t},h(e,["tab"]))})})):null;if(x||$||en){let e=(0,n.default)(`${U}-head`,X("header")),i=(0,n.default)(`${U}-head-title`,X("title")),l=(0,n.default)(`${U}-extra`,X("extra")),r=Object.assign(Object.assign({},v),K("header"));d=t.createElement("div",{className:e,style:r},t.createElement("div",{className:`${U}-head-wrapper`},x&&t.createElement("div",{className:i,style:K("title")},x),$&&t.createElement("div",{className:l,style:K("extra")},$)),en)}let ei=(0,n.default)(`${U}-cover`,X("cover")),el=N?t.createElement("div",{className:ei,style:K("cover")},N):null,er=(0,n.default)(`${U}-body`,X("body")),ea=Object.assign(Object.assign({},O),K("body")),eo=t.createElement("div",{className:er,style:ea},j?J:M),es=(0,n.default)(`${U}-actions`,X("actions")),ec=(null==z?void 0:z.length)?t.createElement(f,{actionClasses:es,actionStyle:K("actions"),actions:z}):null,ed=(0,i.default)(H,["onTabChange"]),eu=(0,n.default)(U,null==A?void 0:A.className,{[`${U}-loading`]:j,[`${U}-bordered`]:"borderless"!==F,[`${U}-hoverable`]:R,[`${U}-contain-grid`]:q,[`${U}-contain-tabs`]:null==P?void 0:P.length,[`${U}-${ee}`]:ee,[`${U}-type-${w}`]:!!w,[`${U}-rtl`]:"rtl"===D},g,b,V,_),eg=Object.assign(Object.assign({},null==A?void 0:A.style),y);return Q(t.createElement("div",Object.assign({ref:s},ed,{className:eu,style:eg}),d,el,eo,ec))});var $=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};y.Grid=c,y.Meta=e=>{let{prefixCls:i,className:r,avatar:a,title:o,description:s}=e,c=$(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:d}=t.useContext(l.ConfigContext),u=d("card",i),g=(0,n.default)(`${u}-meta`,r),b=a?t.createElement("div",{className:`${u}-meta-avatar`},a):null,p=o?t.createElement("div",{className:`${u}-meta-title`},o):null,m=s?t.createElement("div",{className:`${u}-meta-description`},s):null,h=p||m?t.createElement("div",{className:`${u}-meta-detail`},p,m):null;return t.createElement("div",Object.assign({},c,{className:g}),b,h)},e.s(["Card",0,y],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(908206),l=e.i(242064),r=e.i(517455),a=e.i(150073);let o={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var c=e.i(876556),d=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n},u=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let g=e=>{let{itemPrefixCls:i,component:l,span:r,className:a,style:o,labelStyle:c,contentStyle:d,bordered:u,label:g,content:b,colon:p,type:m,styles:h}=e,{classNames:f}=t.useContext(s),y=Object.assign(Object.assign({},c),null==h?void 0:h.label),$=Object.assign(Object.assign({},d),null==h?void 0:h.content);if(u)return t.createElement(l,{colSpan:r,style:o,className:(0,n.default)(a,{[`${i}-item-${m}`]:"label"===m||"content"===m,[null==f?void 0:f.label]:(null==f?void 0:f.label)&&"label"===m,[null==f?void 0:f.content]:(null==f?void 0:f.content)&&"content"===m})},null!=g&&t.createElement("span",{style:y},g),null!=b&&t.createElement("span",{style:$},b));return t.createElement(l,{colSpan:r,style:o,className:(0,n.default)(`${i}-item`,a)},t.createElement("div",{className:`${i}-item-container`},null!=g&&t.createElement("span",{style:y,className:(0,n.default)(`${i}-item-label`,null==f?void 0:f.label,{[`${i}-item-no-colon`]:!p})},g),null!=b&&t.createElement("span",{style:$,className:(0,n.default)(`${i}-item-content`,null==f?void 0:f.content)},b)))};function b(e,{colon:n,prefixCls:i,bordered:l},{component:r,type:a,showLabel:o,showContent:s,labelStyle:c,contentStyle:d,styles:u}){return e.map(({label:e,children:b,prefixCls:p=i,className:m,style:h,labelStyle:f,contentStyle:y,span:$=1,key:v,styles:O},x)=>"string"==typeof r?t.createElement(g,{key:`${a}-${v||x}`,className:m,style:h,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),f),null==O?void 0:O.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),y),null==O?void 0:O.content)},span:$,colon:n,component:r,itemPrefixCls:p,bordered:l,label:o?e:null,content:s?b:null,type:a}):[t.createElement(g,{key:`label-${v||x}`,className:m,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.label),h),f),null==O?void 0:O.label),span:1,colon:n,component:r[0],itemPrefixCls:p,bordered:l,label:e,type:"label"}),t.createElement(g,{key:`content-${v||x}`,className:m,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.content),h),y),null==O?void 0:O.content),span:2*$-1,component:r[1],itemPrefixCls:p,bordered:l,content:b,type:"content"})])}let p=e=>{let n=t.useContext(s),{prefixCls:i,vertical:l,row:r,index:a,bordered:o}=e;return l?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${a}`,className:`${i}-row`},b(r,e,Object.assign({component:"th",type:"label",showLabel:!0},n))),t.createElement("tr",{key:`content-${a}`,className:`${i}-row`},b(r,e,Object.assign({component:"td",type:"content",showContent:!0},n)))):t.createElement("tr",{key:a,className:`${i}-row`},b(r,e,Object.assign({component:o?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},n)))};e.i(296059);var m=e.i(915654),h=e.i(183293),f=e.i(246422),y=e.i(838378);let $=(0,f.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:n,itemPaddingBottom:i,itemPaddingEnd:l,colonMarginRight:r,colonMarginLeft:a,titleMarginBottom:o}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,h.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:n}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.padding)} ${(0,m.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,m.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:n,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.paddingSM)} ${(0,m.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,m.unit)(e.paddingXS)} ${(0,m.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:o},[`${t}-title`]:Object.assign(Object.assign({},h.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:n,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:i,paddingInlineEnd:l},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,m.unit)(a)} ${(0,m.unit)(r)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,y.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var v=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let O=e=>{let g,{prefixCls:b,title:m,extra:h,column:f,colon:y=!0,bordered:O,layout:x,children:j,className:S,rootClassName:C,style:E,size:w,labelStyle:N,contentStyle:z,styles:P,items:M,classNames:B}=e,T=v(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:k,direction:R,className:L,style:G,classNames:I,styles:H}=(0,l.useComponentConfig)("descriptions"),W=k("descriptions",b),D=(0,a.default)(),A=t.useMemo(()=>{var e;return"number"==typeof f?f:null!=(e=(0,i.matchScreen)(D,Object.assign(Object.assign({},o),f)))?e:3},[D,f]),F=(g=t.useMemo(()=>M||(0,c.default)(j).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[M,j]),t.useMemo(()=>g.map(e=>{var{span:t}=e,n=d(e,["span"]);return"filled"===t?Object.assign(Object.assign({},n),{filled:!0}):Object.assign(Object.assign({},n),{span:"number"==typeof t?t:(0,i.matchScreen)(D,t)})}),[g,D])),X=(0,r.default)(w),K=((e,n)=>{let[i,l]=(0,t.useMemo)(()=>{let t,i,l,r;return t=[],i=[],l=!1,r=0,n.filter(e=>e).forEach(n=>{let{filled:a}=n,o=u(n,["filled"]);if(a){i.push(o),t.push(i),i=[],r=0;return}let s=e-r;(r+=n.span||1)>=e?(r>e?(l=!0,i.push(Object.assign(Object.assign({},o),{span:s}))):i.push(o),t.push(i),i=[],r=0):i.push(o)}),i.length>0&&t.push(i),[t=t.map(t=>{let n=t.reduce((e,t)=>e+(t.span||1),0);if(n({labelStyle:N,contentStyle:z,styles:{content:Object.assign(Object.assign({},H.content),null==P?void 0:P.content),label:Object.assign(Object.assign({},H.label),null==P?void 0:P.label)},classNames:{label:(0,n.default)(I.label,null==B?void 0:B.label),content:(0,n.default)(I.content,null==B?void 0:B.content)}}),[N,z,P,B,I,H]);return q(t.createElement(s.Provider,{value:V},t.createElement("div",Object.assign({className:(0,n.default)(W,L,I.root,null==B?void 0:B.root,{[`${W}-${X}`]:X&&"default"!==X,[`${W}-bordered`]:!!O,[`${W}-rtl`]:"rtl"===R},S,C,U,Q),style:Object.assign(Object.assign(Object.assign(Object.assign({},G),H.root),null==P?void 0:P.root),E)},T),(m||h)&&t.createElement("div",{className:(0,n.default)(`${W}-header`,I.header,null==B?void 0:B.header),style:Object.assign(Object.assign({},H.header),null==P?void 0:P.header)},m&&t.createElement("div",{className:(0,n.default)(`${W}-title`,I.title,null==B?void 0:B.title),style:Object.assign(Object.assign({},H.title),null==P?void 0:P.title)},m),h&&t.createElement("div",{className:(0,n.default)(`${W}-extra`,I.extra,null==B?void 0:B.extra),style:Object.assign(Object.assign({},H.extra),null==P?void 0:P.extra)},h)),t.createElement("div",{className:`${W}-view`},t.createElement("table",null,t.createElement("tbody",null,K.map((e,n)=>t.createElement(p,{key:n,index:n,colon:y,prefixCls:W,vertical:"vertical"===x,bordered:O,row:e}))))))))};O.Item=({children:e})=>e,e.s(["Descriptions",0,O],869216)},368869,e=>{"use strict";e.i(296059);var t=e.i(868297),n=e.i(732961),i=e.i(289882),l=e.i(170517),r=e.i(628882),a=e.i(320890),o=e.i(104458),s=e.i(722319),c=e.i(8398),d=e.i(279728);e.i(765846);var u=e.i(602716),g=e.i(328052),b=e.i(135551);let p=(e,t)=>new b.FastColor(e).setA(t).toRgbString(),m=(e,t)=>new b.FastColor(e).lighten(t).toHexString(),h=e=>{let t=(0,u.generate)(e,{theme:"dark"});return{1:t[0],2:t[1],3:t[2],4:t[3],5:t[6],6:t[5],7:t[4],8:t[6],9:t[5],10:t[4]}},f=(e,t)=>{let n=e||"#000",i=t||"#fff";return{colorBgBase:n,colorTextBase:i,colorText:p(i,.85),colorTextSecondary:p(i,.65),colorTextTertiary:p(i,.45),colorTextQuaternary:p(i,.25),colorFill:p(i,.18),colorFillSecondary:p(i,.12),colorFillTertiary:p(i,.08),colorFillQuaternary:p(i,.04),colorBgSolid:p(i,.95),colorBgSolidHover:p(i,1),colorBgSolidActive:p(i,.9),colorBgElevated:m(n,12),colorBgContainer:m(n,8),colorBgLayout:m(n,0),colorBgSpotlight:m(n,26),colorBgBlur:p(i,.04),colorBorder:m(n,26),colorBorderSecondary:m(n,19)}},y={defaultSeed:a.defaultConfig.token,useToken:function(){let[e,t,n]=(0,o.useToken)();return{theme:e,token:t,hashId:n}},defaultAlgorithm:s.default,darkAlgorithm:(e,t)=>{let n=Object.keys(l.defaultPresetColors).map(t=>{let n=(0,u.generate)(e[t],{theme:"dark"});return Array.from({length:10},()=>1).reduce((e,i,l)=>(e[`${t}-${l+1}`]=n[l],e[`${t}${l+1}`]=n[l],e),{})}).reduce((e,t)=>e=Object.assign(Object.assign({},e),t),{}),i=null!=t?t:(0,s.default)(e),r=(0,g.default)(e,{generateColorPalettes:h,generateNeutralColorPalettes:f});return Object.assign(Object.assign(Object.assign(Object.assign({},i),n),r),{colorPrimaryBg:r.colorPrimaryBorder,colorPrimaryBgHover:r.colorPrimaryBorderHover})},compactAlgorithm:(e,t)=>{let n=null!=t?t:(0,s.default)(e),i=n.fontSizeSM,l=n.controlHeight-4;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},n),function(e){let{sizeUnit:t,sizeStep:n}=e,i=n-2;return{sizeXXL:t*(i+10),sizeXL:t*(i+6),sizeLG:t*(i+2),sizeMD:t*(i+2),sizeMS:t*(i+1),size:t*i,sizeSM:t*i,sizeXS:t*(i-1),sizeXXS:t*(i-1)}}(null!=t?t:e)),(0,d.default)(i)),{controlHeight:l}),(0,c.default)(Object.assign(Object.assign({},n),{controlHeight:l})))},getDesignToken:e=>{let a=(null==e?void 0:e.algorithm)?(0,t.createTheme)(e.algorithm):i.default,o=Object.assign(Object.assign({},l.default),null==e?void 0:e.token);return(0,n.getComputedToken)(o,{override:null==e?void 0:e.token},a,r.default)},defaultConfig:a.defaultConfig,_internalContext:a.DesignTokenContext};e.s(["theme",0,y],368869)},127952,e=>{"use strict";var t=e.i(843476),n=e.i(560445),i=e.i(175712),l=e.i(869216),r=e.i(311451),a=e.i(212931),o=e.i(898586),s=e.i(368869),c=e.i(270377),d=e.i(271645);e.s(["default",0,function({isOpen:e,title:u,alertMessage:g,message:b,resourceInformationTitle:p,resourceInformation:m,onCancel:h,onOk:f,confirmLoading:y,requiredConfirmation:$}){let{Title:v,Text:O}=o.Typography,{token:x}=s.theme.useToken(),[j,S]=(0,d.useState)("");return(0,d.useEffect)(()=>{e&&S("")},[e]),(0,t.jsx)(a.Modal,{title:u,open:e,onOk:f,onCancel:h,confirmLoading:y,okText:y?"Deleting...":"Delete",cancelText:"Cancel",okButtonProps:{danger:!0,disabled:!!$&&j!==$||y},cancelButtonProps:{disabled:y},children:(0,t.jsxs)("div",{className:"space-y-4",children:[g&&(0,t.jsx)(n.Alert,{message:g,type:"warning"}),(0,t.jsx)(i.Card,{title:p,className:"mt-4",styles:{body:{padding:"16px"},header:{backgroundColor:x.colorErrorBg,borderColor:x.colorErrorBorder}},style:{backgroundColor:x.colorErrorBg,borderColor:x.colorErrorBorder},children:(0,t.jsx)(l.Descriptions,{column:1,size:"small",children:m&&m.map(({label:e,value:n,...i})=>(0,t.jsx)(l.Descriptions.Item,{label:(0,t.jsx)("span",{className:"font-semibold",children:e}),children:(0,t.jsx)(O,{...i,children:n??"-"})},e))})}),(0,t.jsx)("div",{children:(0,t.jsx)(O,{children:b})}),$&&(0,t.jsxs)("div",{className:"mb-6 mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:[(0,t.jsxs)(O,{className:"block text-base font-medium text-gray-700 dark:text-gray-300 mb-2",children:[(0,t.jsx)(O,{children:"Type "}),(0,t.jsx)(O,{strong:!0,type:"danger",children:$}),(0,t.jsx)(O,{children:" to confirm deletion:"})]}),(0,t.jsx)(r.Input,{value:j,onChange:e=>S(e.target.value),placeholder:$,className:"rounded-md",prefix:(0,t.jsx)(c.ExclamationCircleOutlined,{style:{color:x.colorError}}),autoFocus:!0})]})]})})}])},525720,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(343794),i=e.i(529681),l=e.i(908286),r=e.i(242064),a=e.i(246422),o=e.i(838378);let s=["wrap","nowrap","wrap-reverse"],c=["flex-start","flex-end","start","end","center","space-between","space-around","space-evenly","stretch","normal","left","right"],d=["center","start","end","flex-start","flex-end","self-start","self-end","baseline","normal","stretch"],u=function(e,t){let i,l,r;return(0,n.default)(Object.assign(Object.assign(Object.assign({},(i=!0===t.wrap?"wrap":t.wrap,{[`${e}-wrap-${i}`]:i&&s.includes(i)})),(l={},d.forEach(n=>{l[`${e}-align-${n}`]=t.align===n}),l[`${e}-align-stretch`]=!t.align&&!!t.vertical,l)),(r={},c.forEach(n=>{r[`${e}-justify-${n}`]=t.justify===n}),r)))},g=(0,a.genStyleHooks)("Flex",e=>{let{paddingXS:t,padding:n,paddingLG:i}=e,l=(0,o.mergeToken)(e,{flexGapSM:t,flexGap:n,flexGapLG:i});return[(e=>{let{componentCls:t}=e;return{[t]:{display:"flex",margin:0,padding:0,"&-vertical":{flexDirection:"column"},"&-rtl":{direction:"rtl"},"&:empty":{display:"none"}}}})(l),(e=>{let{componentCls:t}=e;return{[t]:{"&-gap-small":{gap:e.flexGapSM},"&-gap-middle":{gap:e.flexGap},"&-gap-large":{gap:e.flexGapLG}}}})(l),(e=>{let{componentCls:t}=e,n={};return s.forEach(e=>{n[`${t}-wrap-${e}`]={flexWrap:e}}),n})(l),(e=>{let{componentCls:t}=e,n={};return d.forEach(e=>{n[`${t}-align-${e}`]={alignItems:e}}),n})(l),(e=>{let{componentCls:t}=e,n={};return c.forEach(e=>{n[`${t}-justify-${e}`]={justifyContent:e}}),n})(l)]},()=>({}),{resetStyle:!1});var b=function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&0>t.indexOf(i)&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,i=Object.getOwnPropertySymbols(e);lt.indexOf(i[l])&&Object.prototype.propertyIsEnumerable.call(e,i[l])&&(n[i[l]]=e[i[l]]);return n};let p=t.default.forwardRef((e,a)=>{let{prefixCls:o,rootClassName:s,className:c,style:d,flex:p,gap:m,vertical:h=!1,component:f="div",children:y}=e,$=b(e,["prefixCls","rootClassName","className","style","flex","gap","vertical","component","children"]),{flex:v,direction:O,getPrefixCls:x}=t.default.useContext(r.ConfigContext),j=x("flex",o),[S,C,E]=g(j),w=null!=h?h:null==v?void 0:v.vertical,N=(0,n.default)(c,s,null==v?void 0:v.className,j,C,E,u(j,e),{[`${j}-rtl`]:"rtl"===O,[`${j}-gap-${m}`]:(0,l.isPresetSize)(m),[`${j}-vertical`]:w}),z=Object.assign(Object.assign({},null==v?void 0:v.style),d);return p&&(z.flex=p),m&&!(0,l.isPresetSize)(m)&&(z.gap=m),S(t.default.createElement(f,Object.assign({ref:a,className:N,style:z},(0,i.default)($,["justify","wrap","align"])),y))});e.s(["Flex",0,p],525720)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0skjxv866-8kr.js b/litellm/proxy/_experimental/out/_next/static/chunks/0skjxv866-8kr.js new file mode 100644 index 00000000000..4e6515b6c1a --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0skjxv866-8kr.js @@ -0,0 +1,10 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,954616,e=>{"use strict";var t=e.i(271645),i=e.i(114272),r=e.i(540143),n=e.i(915823),a=e.i(619273),s=class extends n.Subscribable{#e;#t=void 0;#i;#r;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#n()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,a.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#i,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,a.hashKey)(t.mutationKey)!==(0,a.hashKey)(this.options.mutationKey)?this.reset():this.#i?.state.status==="pending"&&this.#i.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#i?.removeObserver(this)}onMutationUpdate(e){this.#n(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#i?.removeObserver(this),this.#i=void 0,this.#n(),this.#a()}mutate(e,t){return this.#r=t,this.#i?.removeObserver(this),this.#i=this.#e.getMutationCache().build(this.#e,this.options),this.#i.addObserver(this),this.#i.execute(e)}#n(){let e=this.#i?.state??(0,i.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#a(e){r.notifyManager.batch(()=>{if(this.#r&&this.hasListeners()){let t=this.#t.variables,i=this.#t.context,r={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#r.onSuccess?.(e.data,t,i,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(e.data,null,t,i,r)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#r.onError?.(e.error,t,i,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(void 0,e.error,t,i,r)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},o=e.i(912598);e.s(["useMutation",0,function(e,i){let n=(0,o.useQueryClient)(i),[l]=t.useState(()=>new s(n,e));t.useEffect(()=>{l.setOptions(e)},[l,e]);let d=t.useSyncExternalStore(t.useCallback(e=>l.subscribe(r.notifyManager.batchCalls(e)),[l]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),u=t.useCallback((e,t)=>{l.mutate(e,t).catch(a.noop)},[l]);if(d.error&&(0,a.shouldThrowError)(l.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:u,mutateAsync:d.mutate}}],954616)},56456,e=>{"use strict";var t=e.i(739295);e.s(["LoadingOutlined",()=>t.default])},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(343794),r=e.i(529681),n=e.i(242064),a=e.i(517455),s=e.i(185793),o=e.i(721369),l=function(e,t){var i={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(i[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,r=Object.getOwnPropertySymbols(e);nt.indexOf(r[n])&&Object.prototype.propertyIsEnumerable.call(e,r[n])&&(i[r[n]]=e[r[n]]);return i};let d=e=>{var{prefixCls:r,className:a,hoverable:s=!0}=e,o=l(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(n.ConfigContext),u=d("card",r),c=(0,i.default)(`${u}-grid`,a,{[`${u}-grid-hoverable`]:s});return t.createElement("div",Object.assign({},o,{className:c}))};e.i(296059);var u=e.i(915654),c=e.i(183293),h=e.i(246422),m=e.i(838378);let p=(0,h.genStyleHooks)("Card",e=>{let t=(0,m.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:i,cardHeadPadding:r,colorBorderSecondary:n,boxShadowTertiary:a,bodyPadding:s,extraColor:o}=e;return{[t]:Object.assign(Object.assign({},(0,c.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:a},[`${t}-head`]:(e=>{let{antCls:t,componentCls:i,headerHeight:r,headerPadding:n,tabsMarginBottom:a}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:r,marginBottom:-1,padding:`0 ${(0,u.unit)(n)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,u.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,u.unit)(e.borderRadiusLG)} ${(0,u.unit)(e.borderRadiusLG)} 0 0`},(0,c.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},c.textEllipsis),{[` + > ${i}-typography, + > ${i}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:a,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,u.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:o,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:s,borderRadius:`0 0 ${(0,u.unit)(e.borderRadiusLG)} ${(0,u.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:i,cardShadow:r,lineWidth:n}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,u.unit)(n)} 0 0 0 ${i}, + 0 ${(0,u.unit)(n)} 0 0 ${i}, + ${(0,u.unit)(n)} ${(0,u.unit)(n)} 0 0 ${i}, + ${(0,u.unit)(n)} 0 0 0 ${i} inset, + 0 ${(0,u.unit)(n)} 0 0 ${i} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:r}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,u.unit)(e.borderRadiusLG)} ${(0,u.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:i,actionsLiMargin:r,cardActionsIconSize:n,colorBorderSecondary:a,actionsBg:s}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:s,borderTop:`${(0,u.unit)(e.lineWidth)} ${e.lineType} ${a}`,display:"flex",borderRadius:`0 0 ${(0,u.unit)(e.borderRadiusLG)} ${(0,u.unit)(e.borderRadiusLG)}`},(0,c.clearFix)()),{"& > li":{margin:r,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${i}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,u.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${i}`]:{fontSize:n,lineHeight:(0,u.unit)(e.calc(n).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,u.unit)(e.lineWidth)} ${e.lineType} ${a}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,u.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,c.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},c.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,u.unit)(e.lineWidth)} ${e.lineType} ${n}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:i}},[`${t}-contain-grid`]:{borderRadius:`${(0,u.unit)(e.borderRadiusLG)} ${(0,u.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:r}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:i,headerPadding:r,bodyPadding:n}=e;return{[`${t}-head`]:{padding:`0 ${(0,u.unit)(r)}`,background:i,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,u.unit)(e.padding)} ${(0,u.unit)(n)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:i,headerPaddingSM:r,headerHeightSM:n,headerFontSizeSM:a}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:n,padding:`0 ${(0,u.unit)(r)}`,fontSize:a,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:i}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,i;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(i=e.headerPadding)?i:e.paddingLG}});var g=e.i(792812),b=function(e,t){var i={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(i[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,r=Object.getOwnPropertySymbols(e);nt.indexOf(r[n])&&Object.prototype.propertyIsEnumerable.call(e,r[n])&&(i[r[n]]=e[r[n]]);return i};let y=e=>{let{actionClasses:i,actions:r=[],actionStyle:n}=e;return t.createElement("ul",{className:i,style:n},r.map((e,i)=>{let n=`action-${i}`;return t.createElement("li",{style:{width:`${100/r.length}%`},key:n},t.createElement("span",null,e))}))},f=t.forwardRef((e,l)=>{let u,{prefixCls:c,className:h,rootClassName:m,style:f,extra:$,headStyle:v={},bodyStyle:x={},title:S,loading:j,bordered:O,variant:w,size:C,type:E,cover:T,actions:P,tabList:M,children:R,activeTabKey:N,defaultActiveTabKey:L,tabBarExtraContent:z,hoverable:I,tabProps:k={},classNames:B,styles:G}=e,F=b(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:H,direction:W,card:K}=t.useContext(n.ConfigContext),[A]=(0,g.default)("card",w,O),U=e=>{var t;return(0,i.default)(null==(t=null==K?void 0:K.classNames)?void 0:t[e],null==B?void 0:B[e])},_=e=>{var t;return Object.assign(Object.assign({},null==(t=null==K?void 0:K.styles)?void 0:t[e]),null==G?void 0:G[e])},q=t.useMemo(()=>{let e=!1;return t.Children.forEach(R,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[R]),D=H("card",c),[X,Q,J]=p(D),V=t.createElement(s.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},R),Z=void 0!==N,Y=Object.assign(Object.assign({},k),{[Z?"activeKey":"defaultActiveKey"]:Z?N:L,tabBarExtraContent:z}),ee=(0,a.default)(C),et=ee&&"default"!==ee?ee:"large",ei=M?t.createElement(o.default,Object.assign({size:et},Y,{className:`${D}-head-tabs`,onChange:t=>{var i;null==(i=e.onTabChange)||i.call(e,t)},items:M.map(e=>{var{tab:t}=e;return Object.assign({label:t},b(e,["tab"]))})})):null;if(S||$||ei){let e=(0,i.default)(`${D}-head`,U("header")),r=(0,i.default)(`${D}-head-title`,U("title")),n=(0,i.default)(`${D}-extra`,U("extra")),a=Object.assign(Object.assign({},v),_("header"));u=t.createElement("div",{className:e,style:a},t.createElement("div",{className:`${D}-head-wrapper`},S&&t.createElement("div",{className:r,style:_("title")},S),$&&t.createElement("div",{className:n,style:_("extra")},$)),ei)}let er=(0,i.default)(`${D}-cover`,U("cover")),en=T?t.createElement("div",{className:er,style:_("cover")},T):null,ea=(0,i.default)(`${D}-body`,U("body")),es=Object.assign(Object.assign({},x),_("body")),eo=t.createElement("div",{className:ea,style:es},j?V:R),el=(0,i.default)(`${D}-actions`,U("actions")),ed=(null==P?void 0:P.length)?t.createElement(y,{actionClasses:el,actionStyle:_("actions"),actions:P}):null,eu=(0,r.default)(F,["onTabChange"]),ec=(0,i.default)(D,null==K?void 0:K.className,{[`${D}-loading`]:j,[`${D}-bordered`]:"borderless"!==A,[`${D}-hoverable`]:I,[`${D}-contain-grid`]:q,[`${D}-contain-tabs`]:null==M?void 0:M.length,[`${D}-${ee}`]:ee,[`${D}-type-${E}`]:!!E,[`${D}-rtl`]:"rtl"===W},h,m,Q,J),eh=Object.assign(Object.assign({},null==K?void 0:K.style),f);return X(t.createElement("div",Object.assign({ref:l},eu,{className:ec,style:eh}),u,en,eo,ed))});var $=function(e,t){var i={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(i[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,r=Object.getOwnPropertySymbols(e);nt.indexOf(r[n])&&Object.prototype.propertyIsEnumerable.call(e,r[n])&&(i[r[n]]=e[r[n]]);return i};f.Grid=d,f.Meta=e=>{let{prefixCls:r,className:a,avatar:s,title:o,description:l}=e,d=$(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:u}=t.useContext(n.ConfigContext),c=u("card",r),h=(0,i.default)(`${c}-meta`,a),m=s?t.createElement("div",{className:`${c}-meta-avatar`},s):null,p=o?t.createElement("div",{className:`${c}-meta-title`},o):null,g=l?t.createElement("div",{className:`${c}-meta-description`},l):null,b=p||g?t.createElement("div",{className:`${c}-meta-detail`},p,g):null;return t.createElement("div",Object.assign({},d,{className:h}),m,b)},e.s(["Card",0,f],175712)},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},566606,e=>{"use strict";var t=e.i(843476),i=e.i(271645),r=e.i(618566),n=e.i(947293),a=e.i(602869),s=e.i(954616),o=e.i(266027),l=e.i(612256);let d=(0,e.i(243652).createQueryKeys)("onboarding");var u=e.i(268004),c=e.i(482725),h=e.i(56456);function m(){return(0,t.jsx)("div",{className:"mx-auto w-full max-w-md mt-10 flex justify-center",children:(0,t.jsx)(c.Spin,{indicator:(0,t.jsx)(h.LoadingOutlined,{spin:!0}),size:"large"})})}var p=e.i(560445),g=e.i(464571);function b(){return(0,t.jsxs)("div",{className:"mx-auto w-full max-w-md mt-10",children:[(0,t.jsx)(p.Alert,{type:"error",message:"Failed to load invitation",description:"The invitation link may be invalid or expired.",showIcon:!0}),(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(g.Button,{href:"/ui/login",children:"Back to Login"})})]})}var y=e.i(175712),f=e.i(808613),$=e.i(311451),v=e.i(898586);function x({variant:e,userEmail:r,isPending:n,claimError:a,onSubmit:s}){let[o]=f.Form.useForm();return i.default.useEffect(()=>{r&&o.setFieldValue("user_email",r)},[r,o]),(0,t.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,t.jsxs)(y.Card,{children:[(0,t.jsx)(v.Typography.Title,{level:5,className:"text-center mb-5",children:"🚅 LiteLLM"}),(0,t.jsx)(v.Typography.Title,{level:3,children:"reset_password"===e?"Reset Password":"Sign Up"}),(0,t.jsx)(v.Typography.Text,{children:"reset_password"===e?"Reset your password to access Admin UI.":"Claim your user account to login to Admin UI."}),"signup"===e&&(0,t.jsx)(p.Alert,{className:"mt-4",type:"info",message:"SSO",description:(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)("span",{children:"SSO is under the Enterprise Tier."}),(0,t.jsx)(g.Button,{type:"primary",size:"small",href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})]}),showIcon:!0}),(0,t.jsxs)(f.Form,{className:"mt-10 mb-5",layout:"vertical",form:o,onFinish:e=>s({password:e.password}),children:[(0,t.jsx)(f.Form.Item,{label:"Email Address",name:"user_email",children:(0,t.jsx)($.Input,{type:"email",disabled:!0})}),(0,t.jsx)(f.Form.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"reset_password"===e?"Enter your new password":"Create a password for your account",children:(0,t.jsx)($.Input.Password,{})}),a&&(0,t.jsx)(p.Alert,{type:"error",message:a,showIcon:!0,className:"mb-4"}),(0,t.jsx)("div",{className:"mt-10",children:(0,t.jsx)(g.Button,{htmlType:"submit",loading:n,children:"reset_password"===e?"Reset Password":"Sign Up"})})]})]})})}function S({variant:e}){let c=(0,r.useSearchParams)().get("invitation_id"),[h,p]=i.default.useState(null),{data:g,isLoading:y,isError:f}=(e=>{let{isLoading:t}=(0,l.useUIConfig)();return(0,o.useQuery)({queryKey:d.detail(e??""),queryFn:async()=>{if(!e)throw Error("inviteId is required");return(0,a.getOnboardingCredentials)(e)},enabled:!!e&&!t})})(c),{mutate:$,isPending:v}=(0,s.useMutation)({mutationFn:async({accessToken:e,inviteId:t,userId:i,password:r})=>await (0,a.claimOnboardingToken)(e,t,i,r)}),j=g?.token?(0,n.jwtDecode)(g.token):null,O=j?.user_email??"",w=j?.user_id??null,C=j?.key??null;return y?(0,t.jsx)(m,{}):f?(0,t.jsx)(b,{}):(0,t.jsx)(x,{variant:e,userEmail:O,isPending:v,claimError:h,onSubmit:e=>{C&&w&&c&&(p(null),$({accessToken:C,inviteId:c,userId:w,password:e.password},{onSuccess:e=>{if(!e?.token)return void p("Failed to start session. Please try again.");(0,u.clearTokenCookies)(),(0,u.storeLoginToken)(e.token);let t=(0,a.getProxyBaseUrl)();window.location.href=t?`${t}/ui/?login=success`:"/ui/?login=success"},onError:e=>{p(e.message||"Failed to submit. Please try again.")}}))}})}function j(){let e=(0,r.useSearchParams)().get("action");return(0,t.jsx)(S,{variant:"reset_password"===e?"reset_password":"signup"})}e.s(["default",0,function(){return(0,t.jsx)(i.Suspense,{fallback:(0,t.jsx)("div",{className:"flex items-center justify-center min-h-screen",children:"Loading..."}),children:(0,t.jsx)(j,{})})}],566606)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0sqw622fcvsv4.js b/litellm/proxy/_experimental/out/_next/static/chunks/0sqw622fcvsv4.js new file mode 100644 index 00000000000..ea10147199f --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0sqw622fcvsv4.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,413082,e=>{"use strict";var t=e.i(271645),n=e.i(574735),r=e.i(328744),o=e.i(365420),i=e.i(108868),a=e.i(439957),s=e.i(229315),l=e.i(451321),u=e.i(647554),c=e.i(596296),d=e.i(675606),p=e.i(56434);let f=r.platform.os.mac&&r.platform.engine.webkit;e.s(["useFocus",0,function(e,r={}){let{enabled:g=!0,delay:v}=r,h="rootStore"in e?e.rootStore:e,{events:S,dataRef:m}=h.context,E=t.useRef(!1),C=t.useRef(null),y=t.useRef(!0),x=(0,a.useTimeout)();t.useEffect(()=>{let e=h.select("domReferenceElement");if(!g)return;let t=(0,s.getWindow)(e);return(0,o.mergeCleanups)((0,n.addEventListener)(t,"blur",function(){let e=h.select("domReferenceElement");!h.select("open")&&(0,s.isHTMLElement)(e)&&e===(0,u.activeElement)((0,i.ownerDocument)(e))&&(E.current=!0)}),f&&(0,n.addEventListener)(t,"keydown",function(){y.current=!0},!0),f&&(0,n.addEventListener)(t,"pointerdown",function(){y.current=!1},!0))},[h,g]),t.useEffect(()=>{if(g)return S.on("openchange",e),()=>{S.off("openchange",e)};function e(e){if(e.reason===p.REASONS.triggerPress||e.reason===p.REASONS.escapeKey){let e=h.select("domReferenceElement");(0,s.isElement)(e)&&(C.current=e,E.current=!0)}}},[S,g,h]);let R=t.useMemo(()=>{function e(){E.current=!1,C.current=null}return{onMouseLeave(){e()},onFocus(t){let n=t.currentTarget;if(E.current){if(C.current===n)return;e()}let r=(0,u.getTarget)(t.nativeEvent);if((0,s.isElement)(r)){if(f&&!t.relatedTarget){if(!y.current&&!(0,c.isTypeableElement)(r))return}else if(!(0,c.matchesFocusVisible)(r))return}let o=(0,c.isTargetInsideEnabledTrigger)(t.relatedTarget,h.context.triggerElements),{nativeEvent:i,currentTarget:a}=t,l="function"==typeof v?v():v;h.select("open")&&o||0===l||void 0===l?h.setOpen(!0,(0,d.createChangeEventDetails)(p.REASONS.triggerFocus,i,a)):x.start(l,()=>{E.current||h.setOpen(!0,(0,d.createChangeEventDetails)(p.REASONS.triggerFocus,i,a))})},onBlur(t){e();let n=t.relatedTarget,r=t.nativeEvent,o=(0,s.isElement)(n)&&n.hasAttribute((0,l.createAttribute)("focus-guard"))&&"outside"===n.getAttribute("data-type");x.start(0,()=>{let e=h.select("domReferenceElement"),t=(0,u.activeElement)((0,i.ownerDocument)(e));if(!n&&t===e||(0,u.contains)(m.current.floatingContext?.refs.floating.current,t)||(0,u.contains)(e,t)||o)return;let a=n??t;(0,c.isTargetInsideEnabledTrigger)(a,h.context.triggerElements)||h.setOpen(!1,(0,d.createChangeEventDetails)(p.REASONS.triggerFocus,r))})}}},[m,v,h,x]);return t.useMemo(()=>g?{reference:R,trigger:R}:{},[g,R])}])},746798,e=>{"use strict";var t,n,r=e.i(843476);e.s([],951047),e.i(951047),e.i(247167);var o=e.i(271645),i=e.i(896499),a=e.i(146376),s=e.i(733332);let l=o.createContext(void 0);function u(e){let t=o.useContext(l);if(void 0===t&&!e)throw Error((0,s.default)(72));return t}var c=e.i(574735),d=e.i(667865),p=e.i(229315),f=e.i(647554),g=e.i(157940);function v(e){return null!=e&&null!=e.clientX}var h=e.i(17989),S=e.i(675606),m=e.i(264111),E=e.i(176782),C=e.i(616269),y=e.i(301252),x=e.i(56434),R=e.i(116786),b=e.i(990627);let P={...R.popupStoreSelectors,disabled:(0,C.createSelector)(e=>e.disabled),instantType:(0,C.createSelector)(e=>e.instantType),isInstantPhase:(0,C.createSelector)(e=>e.isInstantPhase),trackCursorAxis:(0,C.createSelector)(e=>e.trackCursorAxis),disableHoverablePopup:(0,C.createSelector)(e=>e.disableHoverablePopup),lastOpenChangeReason:(0,C.createSelector)(e=>e.openChangeReason),closeOnClick:(0,C.createSelector)(e=>e.closeOnClick),closeDelay:(0,C.createSelector)(e=>e.closeDelay),hasViewport:(0,C.createSelector)(e=>e.hasViewport)};class O extends y.ReactStore{constructor(e,t,n=!1){const r=new b.PopupTriggerMap,i={...(0,R.createInitialPopupStoreState)(),disabled:!1,instantType:void 0,isInstantPhase:!1,trackCursorAxis:"none",disableHoverablePopup:!1,openChangeReason:null,closeOnClick:!0,closeDelay:0,hasViewport:!1,...e};i.floatingRootContext=(0,R.createPopupFloatingRootContext)(r,t,n),super(i,{popupRef:o.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerElements:r},P)}setOpen=(e,t)=>{(0,m.applyPopupOpenChange)(this,e,t,{extraState:{openChangeReason:t.reason}})};cancelPendingOpen(e){this.state.floatingRootContext.dispatchOpenChange(!1,(0,S.createChangeEventDetails)(x.REASONS.triggerPress,e))}static useStore(e,t){return(0,m.usePopupStore)(e,(e,n)=>new O(t,e,n)).store}}let T=(0,i.fastComponent)(function(e){let{disabled:t=!1,defaultOpen:n=!1,open:i,disableHoverablePopup:s=!1,trackCursorAxis:u="none",actionsRef:c,onOpenChange:d,onOpenChangeComplete:p,handle:f,triggerId:g,defaultTriggerId:v=null,children:h}=e,E=O.useStore(f?.store,{open:n,openProp:i,activeTriggerId:v,triggerIdProp:g});(0,m.useInitialOpenSync)(E,i,n,v),E.useControlledProp("openProp",i),E.useControlledProp("triggerIdProp",g),E.useContextCallback("onOpenChange",d),E.useContextCallback("onOpenChangeComplete",p);let C=E.useState("open"),y=!t&&C,R=E.useState("activeTriggerId"),b=E.useState("mounted"),P=E.useState("payload");E.useSyncedValues({trackCursorAxis:u,disableHoverablePopup:s}),E.useSyncedValue("disabled",t),(0,m.useImplicitActiveTrigger)(E,{closeOnActiveTriggerUnmount:!0});let{forceUnmount:T,transitionStatus:I}=(0,m.useOpenStateTransitions)(y,E),w=E.useState("isInstantPhase"),D=E.useState("instantType"),M=E.useState("lastOpenChangeReason"),k=o.useRef(null);(0,a.useIsoLayoutEffect)(()=>{C&&t&&E.setOpen(!1,(0,S.createChangeEventDetails)(x.REASONS.disabled))},[C,t,E]),(0,a.useIsoLayoutEffect)(()=>{"ending"===I&&M===x.REASONS.none||"ending"!==I&&w?("delay"!==D&&(k.current=D),E.set("instantType","delay")):null!==k.current&&(E.set("instantType",k.current),k.current=null)},[I,w,M,D,E]),(0,a.useIsoLayoutEffect)(()=>{y&&null==R&&E.set("payload",void 0)},[E,R,y]);let N=o.useCallback(()=>{E.setOpen(!1,(0,S.createChangeEventDetails)(x.REASONS.imperativeAction))},[E]);o.useImperativeHandle(c,()=>({unmount:T,close:N}),[T,N]);let L=y||b||!t&&"none"!==u;return(0,r.jsxs)(l.Provider,{value:E,children:[L&&(0,r.jsx)(A,{store:E,disabled:t,trackCursorAxis:u}),"function"==typeof h?h({payload:P}):h]})});function A({store:e,disabled:t,trackCursorAxis:n}){let r=e.useState("floatingRootContext"),i=(0,h.useDismiss)(r,{enabled:!t,referencePress:()=>e.select("closeOnClick")}),a=function(e,t={}){let{enabled:n=!0,axis:r="both"}=t,i="rootStore"in e?e.rootStore:e,a=i.useState("open"),s=i.useState("floatingElement"),l=i.useState("domReferenceElement"),u=i.context.dataRef,h=o.useRef(!1),S=o.useRef(null),[m,E]=o.useState(),[C,y]=o.useState([]),x=(0,d.useStableCallback)(e=>{i.set("positionReference",e)}),R=(0,d.useStableCallback)((e,t,n)=>{if(!h.current&&(!u.current.openEvent||v(u.current.openEvent))){var o,a;let s,c,d;i.set("positionReference",(o=n??l,a={x:e,y:t,axis:r,dataRef:u,pointerType:m},s=null,c=null,d=!1,{contextElement:o||void 0,getBoundingClientRect(){let e=o?.getBoundingClientRect()||{width:0,height:0,x:0,y:0},t="x"===a.axis||"both"===a.axis,n="y"===a.axis||"both"===a.axis,r=["mouseenter","mousemove"].includes(a.dataRef.current.openEvent?.type||"")&&"touch"!==a.pointerType,i=e.width,l=e.height,u=e.x,p=e.y;return null==s&&a.x&&t&&(s=e.x-a.x),null==c&&a.y&&n&&(c=e.y-a.y),u-=s||0,p-=c||0,i=0,l=0,!d||r?(i="y"===a.axis?e.width:0,l="x"===a.axis?e.height:0,u=t&&null!=a.x?a.x:u,p=n&&null!=a.y?a.y:p):d&&!r&&(l="x"===a.axis?e.height:l,i="y"===a.axis?e.width:i),d=!0,{width:i,height:l,x:u,y:p,top:p,right:u+i,bottom:p+l,left:u}}}))}}),b=(0,d.useStableCallback)(e=>{a?S.current||(R(e.clientX,e.clientY,e.currentTarget),y([])):R(e.clientX,e.clientY,e.currentTarget)}),P=(0,g.isMouseLikePointerType)(m)?s:a;o.useEffect(()=>{if(!n)return void x(l);if(!P)return;function e(){S.current?.(),S.current=null}let t=(0,p.getWindow)(s);return!u.current.openEvent||v(u.current.openEvent)?S.current=(0,c.addEventListener)(t,"mousemove",function(t){let n=(0,f.getTarget)(t);(0,f.contains)(s,n)?e():R(t.clientX,t.clientY)}):x(l),e},[P,n,s,u,l,i,R,x,C]),o.useEffect(()=>()=>{i.set("positionReference",null)},[i]),o.useEffect(()=>{n&&!s&&(h.current=!1)},[n,s]),o.useEffect(()=>{!n&&a&&(h.current=!0)},[n,a]);let O=o.useMemo(()=>{function e(e){E(e.pointerType)}return{onPointerDown:e,onPointerEnter:e,onMouseMove:b,onMouseEnter:b}},[b]);return o.useMemo(()=>n?{reference:O,trigger:O}:{},[n,O])}(r,{enabled:!t&&"none"!==n,axis:"none"===n?void 0:n}),s=o.useMemo(()=>(0,E.mergeProps)(a.reference,i.reference),[a.reference,i.reference]),l=o.useMemo(()=>(0,E.mergeProps)(a.trigger,i.trigger),[a.trigger,i.trigger]),u=o.useMemo(()=>(0,E.mergeProps)(m.FOCUSABLE_POPUP_PROPS,a.floating,i.floating),[a.floating,i.floating]);return(0,m.usePopupInteractionProps)(e,{activeTriggerProps:s,inactiveTriggerProps:l,popupProps:u}),null}var I=e.i(439957),w=e.i(446265),D=e.i(405005),M=e.i(552245),k=e.i(788015);let N=o.createContext(void 0);var L=e.i(650316),H=e.i(944681);let j=o.createContext({hasProvider:!1,timeoutMs:0,delayRef:{current:0},initialDelayRef:{current:0},timeout:new I.Timeout,currentIdRef:{current:null},currentContextRef:{current:null}});function F(e){let{children:t,delay:n,timeoutMs:i=0}=e,s=o.useRef(n),l=o.useRef(n),u=o.useRef(null),c=o.useRef(null),d=(0,I.useTimeout)();return(0,a.useIsoLayoutEffect)(()=>{if(l.current=n,!u.current){s.current=n;return}s.current={open:(0,H.getDelay)(s.current,"open"),close:(0,H.getDelay)(n,"close")}},[n,u,s,l]),(0,r.jsx)(j.Provider,{value:o.useMemo(()=>({hasProvider:!0,delayRef:s,initialDelayRef:l,currentIdRef:u,timeoutMs:i,currentContextRef:c,timeout:d}),[i,d]),children:t})}var V=e.i(413082),B=e.i(872135);let z=((t={})[t.popupOpen=D.CommonTriggerDataAttributes.popupOpen]="popupOpen",t.triggerDisabled="data-trigger-disabled",t);var U=e.i(673752);let X="data-base-ui-tooltip-trigger";function _(e){if("composedPath"in e){let t=e.composedPath();for(let e=0;e{h.current=n},[n]),(0,a.useIsoLayoutEffect)(()=>()=>{m.current=!0},[]),(0,a.useIsoLayoutEffect)(()=>{function e(){m.current||v(!1),d.current?.setIsInstantPhase(!1),s.current=null,d.current=null,l.current=c.current,f.clear()}if(s.current&&!n&&s.current===i){if(v(!1),u)return f.start(u,()=>{r.select("open")||s.current&&s.current!==i||e()}),()=>{(h.current||s.current!==i)&&f.clear()};e()}},[n,i,s,l,u,c,d,f,r]),(0,a.useIsoLayoutEffect)(()=>{if(!n)return;let e=d.current,t=s.current;f.clear(),d.current={onOpenChange:r.setOpen,setIsInstantPhase:v},s.current=i,l.current={open:0,close:(0,H.getDelay)(c.current,"close")},null!==t&&t!==i?(v(!0),e?.setIsInstantPhase(!0),e?.onOpenChange(!1,(0,S.createChangeEventDetails)(x.REASONS.none))):(v(!1),e?.setIsInstantPhase(!1))},[n,i,r,s,l,c,d,f]),(0,a.useIsoLayoutEffect)(()=>()=>{s.current===i&&(d.current=null,h.current)&&(s.current=null,l.current=c.current,f.clear())},[d,s,l,i,c,f]),o.useMemo(()=>({hasProvider:p,delayRef:l,isInstantPhase:g}),[p,l,g])}(A,{open:T}),$=(0,U.useHoverInteractionSharedState)(A);b.useSyncedValue("isInstantPhase",Q);let ee=b.useState("disabled"),et=d??ee,en=(0,w.useValueAsRef)(et),er=b.useState("trackCursorAxis"),eo=b.useState("disableHoverablePopup"),ei=o.useRef(!1),ea=(0,I.useTimeout)(),es=o.useRef(void 0);function el(){let e=G?.delay,t="object"==typeof J.current?J.current.open:void 0,n=K;return Z&&(n=0!==t?v??e??K:0),n}function eu(e){let t=F.current;if(!t||!e)return!1;let n=function(e){let t=e;for(;t;){if(t.hasAttribute(X))return t;let e=t.parentElement;if(e){t=e;continue}let n=t.getRootNode();t="host"in n&&(0,p.isElement)(n.host)?n.host:null}return null}(e);return null!==n&&n!==t&&(0,f.contains)(t,n)}let ec=(0,B.useHoverReferenceInteraction)(A,{enabled:!et,mouseOnly:!0,move:!1,handleClose:eo||"both"===er?null:(0,L.safePolygon)(),restMs:el,delay(){let e="object"==typeof J.current?J.current.close:void 0,t=W;return null==E&&Z&&(t=e),{close:t}},triggerElementRef:F,isActiveTrigger:O,isClosing:()=>"ending"===b.select("transitionStatus"),shouldOpen:()=>!ei.current}),ed=(0,V.useFocus)(A,{enabled:!et}).reference,ep=b.useState("triggerProps",q),ef=q||"none"!==er;return(0,M.useRenderElement)("button",e,{state:{open:T},ref:[t,Y,F],props:[ec,ed,ef?ep:void 0,{onMouseOver(e){(e=>{let t,n=ei.current,r=_(e),o=(ei.current=t=eu(r),t&&($.openChangeTimeout.clear(),$.restTimeout.clear(),$.restTimeoutPending=!1,ea.clear()),t),i=F.current,a=i&&r&&(0,f.contains)(i,r);if(o&&b.select("open")&&b.select("lastOpenChangeReason")===x.REASONS.triggerHover)return b.setOpen(!1,(0,S.createChangeEventDetails)(x.REASONS.triggerHover,e));if(n&&!o&&a&&!en.current&&!b.select("open")&&i&&(0,g.isMouseLikePointerType)(es.current)){let t=()=>{ei.current||en.current||b.select("open")||b.setOpen(!0,(0,S.createChangeEventDetails)(x.REASONS.triggerHover,e,i))},n=el();0===n?(ea.clear(),t()):ea.start(n,t)}})(e.nativeEvent)},onFocus(e){eu(_(e.nativeEvent))&&e.preventBaseUIHandler()},onMouseLeave(){ei.current=!1,ea.clear(),es.current=void 0},onPointerEnter(e){es.current=e.pointerType},onPointerDown(e){es.current=e.pointerType,b.set("closeOnClick",h),h&&!b.select("open")&&b.cancelPendingOpen(e.nativeEvent)},onClick(e){h&&!b.select("open")&&b.cancelPendingOpen(e.nativeEvent)},id:P,[z.triggerDisabled]:et?"":void 0,[X]:et?void 0:""},y],stateAttributesMapping:D.triggerOpenStateMapping})}),W=o.createContext(void 0);var Y=e.i(174080),q=e.i(726674);let G=o.forwardRef(function(e,t){let{children:n,container:i,className:a,render:s,style:l,...u}=e,{portalNode:c,portalSubtree:d}=(0,q.useFloatingPortalNode)({container:i,ref:t,componentProps:e,elementProps:u});return d||c?(0,r.jsxs)(o.Fragment,{children:[d,c&&Y.createPortal(n,c)]}):null}),J=o.forwardRef(function(e,t){let{keepMounted:n=!1,...o}=e;return u().useState("mounted")||n?(0,r.jsx)(W.Provider,{value:n,children:(0,r.jsx)(G,{ref:t,...o})}):null}),Q=o.createContext(void 0);function Z(){let e=o.useContext(Q);if(void 0===e)throw Error((0,s.default)(71));return e}var $=e.i(329365),ee=e.i(638396),et=e.i(360495),en=e.i(789579);let er=o.forwardRef(function(e,t){let{render:n,className:i,anchor:a,positionMethod:l="absolute",side:c="top",align:d="center",sideOffset:p=0,alignOffset:f=0,collisionBoundary:g="clipping-ancestors",collisionPadding:v=5,arrowPadding:h=5,sticky:S=!1,disableAnchorTracking:m=!1,collisionAvoidance:E=ee.POPUP_COLLISION_AVOIDANCE,style:C,...y}=e,x=u(),R=function(){let e=o.useContext(W);if(void 0===e)throw Error((0,s.default)(70));return e}(),b=x.useState("open"),P=x.useState("mounted"),O=x.useState("trackCursorAxis"),T=x.useState("disableHoverablePopup"),A=x.useState("floatingRootContext"),I=x.useState("instantType"),w=x.useState("transitionStatus"),D=x.useState("hasViewport"),M=(0,$.useAnchorPositioning)({anchor:a,positionMethod:l,floatingRootContext:A,mounted:P,side:c,sideOffset:p,align:d,alignOffset:f,collisionBoundary:g,collisionPadding:v,sticky:S,arrowPadding:h,disableAnchorTracking:m,keepMounted:R,collisionAvoidance:E,adaptiveOrigin:D?et.adaptiveOrigin:void 0}),k=o.useMemo(()=>({open:b,side:M.side,align:M.align,anchorHidden:M.anchorHidden,instant:"none"!==O?"tracking-cursor":I}),[b,M.side,M.align,M.anchorHidden,O,I]),N=(0,en.usePositioner)(e,k,{styles:M.positionerStyles,transitionStatus:w,props:y,refs:[t,x.useStateSetter("positionerElement")],hidden:!P,inert:!b||"both"===O||T});return(0,r.jsx)(Q.Provider,{value:M,children:N})});var eo=e.i(209407),ei=e.i(137584),ea=e.i(815982),es=e.i(431157);let el={...D.popupStateMapping,...eo.transitionStatusMapping},eu=o.forwardRef(function(e,t){let{render:n,className:r,style:o,...i}=e,a=u(),{side:s,align:l}=Z(),c=a.useState("open"),d=a.useState("instantType"),p=a.useState("transitionStatus"),f=a.useState("popupProps"),g=a.useState("floatingRootContext"),v=a.useState("disabled"),h=a.useState("closeDelay");(0,ei.useOpenChangeComplete)({open:c,ref:a.context.popupRef,onComplete(){c&&a.context.onOpenChangeComplete?.(!0)}}),(0,es.useHoverFloatingInteraction)(g,{enabled:!v,closeDelay:h});let S=a.useStateSetter("popupElement");return(0,M.useRenderElement)("div",e,{state:{open:c,side:s,align:l,instant:d,transitionStatus:p},ref:[t,a.context.popupRef,S],props:[f,(0,ea.getDisabledMountTransitionStyles)(p),i],stateAttributesMapping:el})}),ec=o.forwardRef(function(e,t){let{render:n,className:r,style:o,...i}=e,a=u(),{arrowRef:s,side:l,align:c,arrowUncentered:d,arrowStyles:p}=Z(),f=a.useState("open"),g=a.useState("instantType");return(0,M.useRenderElement)("div",e,{state:{open:f,side:l,align:c,uncentered:d,instant:g},ref:[t,s],props:[{style:p,"aria-hidden":!0},i],stateAttributesMapping:D.popupStateMapping})}),ed=((n={}).popupWidth="--popup-width",n.popupHeight="--popup-height",n);var ep=e.i(818390);let ef={activationDirection:e=>e?{"data-activation-direction":e}:null},eg=o.forwardRef(function(e,t){let{render:n,className:r,style:o,children:i,...a}=e,s=u(),l=Z(),c=s.useState("instantType"),{children:d,state:p}=(0,ep.usePopupViewport)({store:s,side:l.side,cssVars:ed,children:i}),f={activationDirection:p.activationDirection,transitioning:p.transitioning,instant:c};return(0,M.useRenderElement)("div",e,{state:f,ref:t,props:[a,{children:d}],stateAttributesMapping:ef})});class ev{constructor(){this.store=new O}open(e){let t=e?this.store.context.triggerElements.getById(e):void 0;if(e&&!t)throw Error((0,s.default)(81,e));this.store.setOpen(!0,(0,S.createChangeEventDetails)(x.REASONS.imperativeAction,void 0,t))}close(){this.store.setOpen(!1,(0,S.createChangeEventDetails)(x.REASONS.imperativeAction,void 0,void 0))}get isOpen(){return this.store.select("open")}}e.s(["Arrow",0,ec,"Handle",0,ev,"Popup",0,eu,"Portal",0,J,"Positioner",0,er,"Provider",0,function(e){let{delay:t,closeDelay:n,timeout:i=400}=e,a=o.useMemo(()=>({delay:t,closeDelay:n}),[t,n]),s=o.useMemo(()=>({open:t,close:n}),[t,n]);return(0,r.jsx)(N.Provider,{value:a,children:(0,r.jsx)(F,{delay:s,timeoutMs:i,children:e.children})})},"Root",0,T,"Trigger",0,K,"Viewport",0,eg,"createHandle",0,function(){return new ev}],599643);var eh=e.i(599643),eh=eh,eS=e.i(115504);e.s(["Tooltip",0,function({...e}){return(0,r.jsx)(eh.Root,{"data-slot":"tooltip",...e})},"TooltipContent",0,function({className:e,side:t="top",sideOffset:n=4,align:o="center",alignOffset:i=0,children:a,...s}){return(0,r.jsx)(eh.Portal,{children:(0,r.jsx)(eh.Positioner,{align:o,alignOffset:i,side:t,sideOffset:n,className:"isolate z-50",children:(0,r.jsxs)(eh.Popup,{"data-slot":"tooltip-content",className:(0,eS.cn)("z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",e),...s,children:[a,(0,r.jsx)(eh.Arrow,{className:"z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5"})]})})})},"TooltipProvider",0,function({delay:e=0,...t}){return(0,r.jsx)(eh.Provider,{"data-slot":"tooltip-provider",delay:e,...t})},"TooltipTrigger",0,function({...e}){return(0,r.jsx)(eh.Trigger,{"data-slot":"tooltip-trigger",...e})}],746798)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0sstlyp4g1tlt.js b/litellm/proxy/_experimental/out/_next/static/chunks/0sstlyp4g1tlt.js new file mode 100644 index 00000000000..e02d79cd2bc --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0sstlyp4g1tlt.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,94629,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,r],94629)},728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),n=e.i(480731),l=e.i(444755),i=e.i(673706),s=e.i(95779);let o={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,i.makeClassName)("Icon"),g=r.default.forwardRef((e,g)=>{let{icon:m,variant:p="simple",tooltip:h,size:f=n.Sizes.SM,color:x,className:b}=e,y=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),v=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,i.getColorClassNames)(t,s.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,i.getColorClassNames)(t,s.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,l.tremorTwMerge)((0,i.getColorClassNames)(t,s.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,i.getColorClassNames)(t,s.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,l.tremorTwMerge)((0,i.getColorClassNames)(t,s.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,i.getColorClassNames)(t,s.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,l.tremorTwMerge)((0,i.getColorClassNames)(t,s.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,i.getColorClassNames)(t,s.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,l.tremorTwMerge)((0,i.getColorClassNames)(t,s.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,i.getColorClassNames)(t,s.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,l.tremorTwMerge)((0,i.getColorClassNames)(t,s.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(p,x),{tooltipProps:C,getReferenceProps:j}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,i.mergeRefs)([g,C.refs.setReference]),className:(0,l.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",v.bgColor,v.textColor,v.borderColor,v.ringColor,c[p].rounded,c[p].border,c[p].shadow,c[p].ring,o[f].paddingX,o[f].paddingY,b)},j,y),r.default.createElement(a.default,Object.assign({text:h},C)),r.default.createElement(m,{className:(0,l.tremorTwMerge)(u("icon"),"shrink-0",d[f].height,d[f].width)}))});g.displayName="Icon",e.s(["default",0,g],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},185793,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),a=e.i(242064),n=e.i(529681);let l=e=>{let{prefixCls:a,className:n,style:l,size:i,shape:s}=e,o=(0,r.default)({[`${a}-lg`]:"large"===i,[`${a}-sm`]:"small"===i}),d=(0,r.default)({[`${a}-circle`]:"circle"===s,[`${a}-square`]:"square"===s,[`${a}-round`]:"round"===s}),c=t.useMemo(()=>"number"==typeof i?{width:i,height:i,lineHeight:`${i}px`}:{},[i]);return t.createElement("span",{className:(0,r.default)(a,o,d,n),style:Object.assign(Object.assign({},c),l)})};e.i(296059);var i=e.i(694758),s=e.i(915654),o=e.i(246422),d=e.i(838378);let c=new i.Keyframes("ant-skeleton-loading",{"0%":{backgroundPosition:"100% 50%"},"100%":{backgroundPosition:"0 50%"}}),u=e=>({height:e,lineHeight:(0,s.unit)(e)}),g=e=>Object.assign({width:e},u(e)),m=(e,t)=>Object.assign({width:t(e).mul(5).equal(),minWidth:t(e).mul(5).equal()},u(e)),p=e=>Object.assign({width:e},u(e)),h=(e,t,r)=>{let{skeletonButtonCls:a}=e;return{[`${r}${a}-circle`]:{width:t,minWidth:t,borderRadius:"50%"},[`${r}${a}-round`]:{borderRadius:t}}},f=(e,t)=>Object.assign({width:t(e).mul(2).equal(),minWidth:t(e).mul(2).equal()},u(e)),x=(0,o.genStyleHooks)("Skeleton",e=>{let{componentCls:t,calc:r}=e;return(e=>{let{componentCls:t,skeletonAvatarCls:r,skeletonTitleCls:a,skeletonParagraphCls:n,skeletonButtonCls:l,skeletonInputCls:i,skeletonImageCls:s,controlHeight:o,controlHeightLG:d,controlHeightSM:u,gradientFromColor:x,padding:b,marginSM:y,borderRadius:v,titleHeight:C,blockRadius:j,paragraphLiHeight:$,controlHeightXS:w,paragraphMarginTop:k}=e;return{[t]:{display:"table",width:"100%",[`${t}-header`]:{display:"table-cell",paddingInlineEnd:b,verticalAlign:"top",[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:x},g(o)),[`${r}-circle`]:{borderRadius:"50%"},[`${r}-lg`]:Object.assign({},g(d)),[`${r}-sm`]:Object.assign({},g(u))},[`${t}-content`]:{display:"table-cell",width:"100%",verticalAlign:"top",[a]:{width:"100%",height:C,background:x,borderRadius:j,[`+ ${n}`]:{marginBlockStart:u}},[n]:{padding:0,"> li":{width:"100%",height:$,listStyle:"none",background:x,borderRadius:j,"+ li":{marginBlockStart:w}}},[`${n}> li:last-child:not(:first-child):not(:nth-child(2))`]:{width:"61%"}},[`&-round ${t}-content`]:{[`${a}, ${n} > li`]:{borderRadius:v}}},[`${t}-with-avatar ${t}-content`]:{[a]:{marginBlockStart:y,[`+ ${n}`]:{marginBlockStart:k}}},[`${t}${t}-element`]:Object.assign(Object.assign(Object.assign(Object.assign({display:"inline-block",width:"auto"},(e=>{let{borderRadiusSM:t,skeletonButtonCls:r,controlHeight:a,controlHeightLG:n,controlHeightSM:l,gradientFromColor:i,calc:s}=e;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({[r]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:t,width:s(a).mul(2).equal(),minWidth:s(a).mul(2).equal()},f(a,s))},h(e,a,r)),{[`${r}-lg`]:Object.assign({},f(n,s))}),h(e,n,`${r}-lg`)),{[`${r}-sm`]:Object.assign({},f(l,s))}),h(e,l,`${r}-sm`))})(e)),(e=>{let{skeletonAvatarCls:t,gradientFromColor:r,controlHeight:a,controlHeightLG:n,controlHeightSM:l}=e;return{[t]:Object.assign({display:"inline-block",verticalAlign:"top",background:r},g(a)),[`${t}${t}-circle`]:{borderRadius:"50%"},[`${t}${t}-lg`]:Object.assign({},g(n)),[`${t}${t}-sm`]:Object.assign({},g(l))}})(e)),(e=>{let{controlHeight:t,borderRadiusSM:r,skeletonInputCls:a,controlHeightLG:n,controlHeightSM:l,gradientFromColor:i,calc:s}=e;return{[a]:Object.assign({display:"inline-block",verticalAlign:"top",background:i,borderRadius:r},m(t,s)),[`${a}-lg`]:Object.assign({},m(n,s)),[`${a}-sm`]:Object.assign({},m(l,s))}})(e)),(e=>{let{skeletonImageCls:t,imageSizeBase:r,gradientFromColor:a,borderRadiusSM:n,calc:l}=e;return{[t]:Object.assign(Object.assign({display:"inline-flex",alignItems:"center",justifyContent:"center",verticalAlign:"middle",background:a,borderRadius:n},p(l(r).mul(2).equal())),{[`${t}-path`]:{fill:"#bfbfbf"},[`${t}-svg`]:Object.assign(Object.assign({},p(r)),{maxWidth:l(r).mul(4).equal(),maxHeight:l(r).mul(4).equal()}),[`${t}-svg${t}-svg-circle`]:{borderRadius:"50%"}}),[`${t}${t}-circle`]:{borderRadius:"50%"}}})(e)),[`${t}${t}-block`]:{width:"100%",[l]:{width:"100%"},[i]:{width:"100%"}},[`${t}${t}-active`]:{[` + ${a}, + ${n} > li, + ${r}, + ${l}, + ${i}, + ${s} + `]:Object.assign({},{background:e.skeletonLoadingBackground,backgroundSize:"400% 100%",animationName:c,animationDuration:e.skeletonLoadingMotionDuration,animationTimingFunction:"ease",animationIterationCount:"infinite"})}}})((0,d.mergeToken)(e,{skeletonAvatarCls:`${t}-avatar`,skeletonTitleCls:`${t}-title`,skeletonParagraphCls:`${t}-paragraph`,skeletonButtonCls:`${t}-button`,skeletonInputCls:`${t}-input`,skeletonImageCls:`${t}-image`,imageSizeBase:r(e.controlHeight).mul(1.5).equal(),borderRadius:100,skeletonLoadingBackground:`linear-gradient(90deg, ${e.gradientFromColor} 25%, ${e.gradientToColor} 37%, ${e.gradientFromColor} 63%)`,skeletonLoadingMotionDuration:"1.4s"}))},e=>{let{colorFillContent:t,colorFill:r}=e;return{color:t,colorGradientEnd:r,gradientFromColor:t,gradientToColor:r,titleHeight:e.controlHeight/2,blockRadius:e.borderRadiusSM,paragraphMarginTop:e.marginLG+e.marginXXS,paragraphLiHeight:e.controlHeight/2}},{deprecatedTokens:[["color","gradientFromColor"],["colorGradientEnd","gradientToColor"]]}),b=e=>{let{prefixCls:a,className:n,style:l,rows:i=0}=e,s=Array.from({length:i}).map((r,a)=>t.createElement("li",{key:a,style:{width:((e,t)=>{let{width:r,rows:a=2}=t;return Array.isArray(r)?r[e]:a-1===e?r:void 0})(a,e)}}));return t.createElement("ul",{className:(0,r.default)(a,n),style:l},s)},y=({prefixCls:e,className:a,width:n,style:l})=>t.createElement("h3",{className:(0,r.default)(e,a),style:Object.assign({width:n},l)});function v(e){return e&&"object"==typeof e?e:{}}let C=e=>{let{prefixCls:n,loading:i,className:s,rootClassName:o,style:d,children:c,avatar:u=!1,title:g=!0,paragraph:m=!0,active:p,round:h}=e,{getPrefixCls:f,direction:C,className:j,style:$}=(0,a.useComponentConfig)("skeleton"),w=f("skeleton",n),[k,S,I]=x(w);if(i||!("loading"in e)){let e,a,n=!!u,i=!!g,c=!!m;if(n){let r=Object.assign(Object.assign({prefixCls:`${w}-avatar`},i&&!c?{size:"large",shape:"square"}:{size:"large",shape:"circle"}),v(u));e=t.createElement("div",{className:`${w}-header`},t.createElement(l,Object.assign({},r)))}if(i||c){let e,r;if(i){let r=Object.assign(Object.assign({prefixCls:`${w}-title`},!n&&c?{width:"38%"}:n&&c?{width:"50%"}:{}),v(g));e=t.createElement(y,Object.assign({},r))}if(c){let e,a=Object.assign(Object.assign({prefixCls:`${w}-paragraph`},(e={},n&&i||(e.width="61%"),!n&&i?e.rows=3:e.rows=2,e)),v(m));r=t.createElement(b,Object.assign({},a))}a=t.createElement("div",{className:`${w}-content`},e,r)}let f=(0,r.default)(w,{[`${w}-with-avatar`]:n,[`${w}-active`]:p,[`${w}-rtl`]:"rtl"===C,[`${w}-round`]:h},j,s,o,S,I);return k(t.createElement("div",{className:f,style:Object.assign(Object.assign({},$),d)},e,a))}return null!=c?c:null};C.Button=e=>{let{prefixCls:i,className:s,rootClassName:o,active:d,block:c=!1,size:u="default"}=e,{getPrefixCls:g}=t.useContext(a.ConfigContext),m=g("skeleton",i),[p,h,f]=x(m),b=(0,n.default)(e,["prefixCls"]),y=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:d,[`${m}-block`]:c},s,o,h,f);return p(t.createElement("div",{className:y},t.createElement(l,Object.assign({prefixCls:`${m}-button`,size:u},b))))},C.Avatar=e=>{let{prefixCls:i,className:s,rootClassName:o,active:d,shape:c="circle",size:u="default"}=e,{getPrefixCls:g}=t.useContext(a.ConfigContext),m=g("skeleton",i),[p,h,f]=x(m),b=(0,n.default)(e,["prefixCls","className"]),y=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:d},s,o,h,f);return p(t.createElement("div",{className:y},t.createElement(l,Object.assign({prefixCls:`${m}-avatar`,shape:c,size:u},b))))},C.Input=e=>{let{prefixCls:i,className:s,rootClassName:o,active:d,block:c,size:u="default"}=e,{getPrefixCls:g}=t.useContext(a.ConfigContext),m=g("skeleton",i),[p,h,f]=x(m),b=(0,n.default)(e,["prefixCls"]),y=(0,r.default)(m,`${m}-element`,{[`${m}-active`]:d,[`${m}-block`]:c},s,o,h,f);return p(t.createElement("div",{className:y},t.createElement(l,Object.assign({prefixCls:`${m}-input`,size:u},b))))},C.Image=e=>{let{prefixCls:n,className:l,rootClassName:i,style:s,active:o}=e,{getPrefixCls:d}=t.useContext(a.ConfigContext),c=d("skeleton",n),[u,g,m]=x(c),p=(0,r.default)(c,`${c}-element`,{[`${c}-active`]:o},l,i,g,m);return u(t.createElement("div",{className:p},t.createElement("div",{className:(0,r.default)(`${c}-image`,l),style:s},t.createElement("svg",{viewBox:"0 0 1098 1024",xmlns:"http://www.w3.org/2000/svg",className:`${c}-image-svg`},t.createElement("title",null,"Image placeholder"),t.createElement("path",{d:"M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z",className:`${c}-image-path`})))))},C.Node=e=>{let{prefixCls:n,className:l,rootClassName:i,style:s,active:o,children:d}=e,{getPrefixCls:c}=t.useContext(a.ConfigContext),u=c("skeleton",n),[g,m,p]=x(u),h=(0,r.default)(u,`${u}-element`,{[`${u}-active`]:o},m,l,i,p);return g(t.createElement("div",{className:h},t.createElement("div",{className:(0,r.default)(`${u}-image`,l),style:s},d)))},e.s(["default",0,C],185793)},922611,e=>{"use strict";var t=e.i(271645),r=e.i(175066);function a(){}let n=t.createContext({add:a,remove:a});e.s(["usePanelRef",0,function(e){let a=t.useContext(n),l=t.useRef(null);return(0,r.default)(t=>{if(t){let r=e?t.querySelector(e):t;r&&(a.add(r),l.current=r)}else a.remove(l.current)})}])},500330,e=>{"use strict";var t=e.i(727749);let r=(e,t=0,r=!1,a=!0)=>{if(null==e||!Number.isFinite(e)||0===e&&!a)return"-";let n={minimumFractionDigits:t,maximumFractionDigits:t};if(!r)return e.toLocaleString("en-US",n);let l=e<0?"-":"",i=Math.abs(e),s=i,o="";return i>=1e6?(s=i/1e6,o="M"):i>=1e3&&(s=i/1e3,o="K"),`${l}${s.toLocaleString("en-US",n)}${o}`},a=async(e,r="Copied to clipboard")=>{if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return n(e,r);try{return await navigator.clipboard.writeText(e),t.default.success(r),!0}catch(t){return console.error("Clipboard API failed: ",t),n(e,r)}},n=(e,r)=>{try{let a=document.createElement("textarea");a.value=e,a.style.position="fixed",a.style.left="-999999px",a.style.top="-999999px",a.setAttribute("readonly",""),document.body.appendChild(a),a.focus(),a.select();let n=document.execCommand("copy");if(document.body.removeChild(a),n)return t.default.success(r),!0;throw Error("execCommand failed")}catch(e){return t.default.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}};e.s(["copyToClipboard",0,a,"formatNumberWithCommas",0,r,"getSpendString",0,(e,t=6)=>{if(null==e||!Number.isFinite(e)||0===e)return"-";let a=r(e,t,!1,!1);if(0===Number(a.replace(/,/g,""))){let e=(1/10**t).toFixed(t);return`< $${e}`}return`$${a}`},"updateExistingKeys",0,function(e,t){let r=structuredClone(e);for(let[e,a]of Object.entries(t))e in r&&(r[e]=a);return r}])},112179,581070,e=>{"use strict";var t=e.i(843476),r=e.i(487486),a=e.i(115504),n=e.i(746798);function l({content:e,trigger:r}){return(0,t.jsx)(n.TooltipProvider,{delay:300,children:(0,t.jsxs)(n.Tooltip,{children:[(0,t.jsx)(n.TooltipTrigger,{render:r}),(0,t.jsx)(n.TooltipContent,{children:e})]})})}e.s(["CellTooltip",0,l],581070);let i={success:"border-green-200 bg-green-50 text-green-600",error:"border-red-200 bg-red-50 text-red-600",warning:"border-amber-200 bg-amber-50 text-amber-600",neutral:"border-gray-200 bg-gray-50 text-gray-600",info:"border-blue-200 bg-blue-50 text-blue-600"};e.s(["StatusBadge",0,function({tone:e,label:n,tooltip:s,dataTestId:o}){let d=(0,t.jsx)(r.Badge,{variant:"outline","data-testid":o,className:(0,a.cn)("whitespace-nowrap font-normal",i[e]),children:n});return s?(0,t.jsx)(l,{content:s,trigger:d}):d}],112179)},622826,200208,399536,964471,e=>{"use strict";var t=e.i(581070),r=e.i(843476);let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],n=e=>String(e).padStart(2,"0");e.s(["DateCell",0,function({value:e,precision:l="datetime",fallback:i="-"}){let s,o,d,c=e?new Date(e):null;return!c||Number.isNaN(c.getTime())?(0,r.jsx)("span",{className:"text-muted-foreground",children:i}):(0,r.jsx)(t.CellTooltip,{content:(s=Intl.DateTimeFormat().resolvedOptions().timeZone,o=`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`,d=`${n(c.getHours())}:${n(c.getMinutes())}:${n(c.getSeconds())}`,`${o}, ${d} (${s})`),trigger:(0,r.jsx)("span",{className:"whitespace-nowrap",children:"date"===l?`${a[c.getMonth()]} ${c.getDate()}, ${c.getFullYear()}`:`${a[c.getMonth()]} ${c.getDate()}, ${n(c.getHours())}:${n(c.getMinutes())}:${n(c.getSeconds())}`})})}],200208);var l=e.i(174886),i=e.i(115504),s=e.i(500330);let o={pill:{base:"font-mono text-xs font-normal px-2 py-0.5 rounded-md text-left bg-blue-50 text-blue-500",clickable:"hover:bg-blue-100 cursor-pointer"},plain:{base:"font-mono text-xs text-left",clickable:"hover:text-blue-600 cursor-pointer"}};e.s(["IdCell",0,function({value:e,variant:a="pill",onClick:n,copyable:d=!1,truncate:c=!0,fallback:u="-",tooltip:g,disabled:m=!1,dataTestId:p,className:h}){if(!e)return(0,r.jsx)("span",{className:"text-muted-foreground",children:u});let f=!!n&&!m,x=(0,i.cn)(o[a].base,f&&o[a].clickable,c&&"block max-w-[15ch] truncate",m&&"opacity-50",h),b=f?(0,r.jsx)("button",{type:"button",className:x,"data-testid":p,onClick:()=>n(e),children:e}):(0,r.jsx)("span",{className:x,"data-testid":p,children:e}),y=(0,r.jsx)(t.CellTooltip,{content:g??e,trigger:b});return d?(0,r.jsxs)("span",{className:"inline-flex max-w-full items-center gap-1",children:[y,(0,r.jsx)("button",{type:"button","aria-label":"Copy ID",className:"shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",onClick:t=>{t.stopPropagation(),(0,s.copyToClipboard)(e)},children:(0,r.jsx)(l.Copy,{className:"size-3"})})]}):y}],399536),e.s(["MoneyCell",0,function({value:e,decimals:t=4,emptyText:a="-",showZero:n=!1}){return null==e||Number.isNaN(e)?(0,r.jsx)("span",{className:"text-muted-foreground",children:a}):0===e?n?(0,r.jsx)("span",{className:"whitespace-nowrap",children:`$${(0,s.formatNumberWithCommas)(0,t,!1,!0)}`}):(0,r.jsx)("span",{className:"text-muted-foreground",children:"-"}):(0,r.jsx)("span",{className:"whitespace-nowrap",children:(0,s.getSpendString)(e,t)})}],964471),e.i(112179),e.s([],622826)},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},68155,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"}))});e.s(["TrashIcon",0,r],68155)},360820,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 15l7-7 7 7"}))});e.s(["ChevronUpIcon",0,r],360820)},871943,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"}))});e.s(["ChevronDownIcon",0,r],871943)},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},591935,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"}))});e.s(["PencilAltIcon",0,r],591935)},551332,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"}))});e.s(["ClipboardCopyIcon",0,r],551332)},434626,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,r],434626)},122577,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 12a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlayIcon",0,r],122577)},902555,e=>{"use strict";var t=e.i(843476),r=e.i(591935),a=e.i(122577),n=e.i(278587),l=e.i(68155),i=e.i(360820),s=e.i(871943),o=e.i(434626),d=e.i(551332),c=e.i(592968),u=e.i(115504),g=e.i(752978);function m({icon:e,onClick:r,className:a,disabled:n,dataTestId:l}){return n?(0,t.jsx)(g.Icon,{icon:e,size:"sm",className:"opacity-50 cursor-not-allowed","data-testid":l}):(0,t.jsx)(g.Icon,{icon:e,size:"sm",onClick:r,className:(0,u.cx)("cursor-pointer",a),"data-testid":l})}let p={Edit:{icon:r.PencilAltIcon,className:"hover:text-blue-600"},Delete:{icon:l.TrashIcon,className:"hover:text-red-600"},Test:{icon:a.PlayIcon,className:"hover:text-blue-600"},Regenerate:{icon:n.RefreshIcon,className:"hover:text-green-600"},Up:{icon:i.ChevronUpIcon,className:"hover:text-blue-600"},Down:{icon:s.ChevronDownIcon,className:"hover:text-blue-600"},Open:{icon:o.ExternalLinkIcon,className:"hover:text-green-600"},Copy:{icon:d.ClipboardCopyIcon,className:"hover:text-blue-600"}};e.s(["default",0,function({onClick:e,tooltipText:r,disabled:a=!1,disabledTooltipText:n,dataTestId:l,variant:i}){let{icon:s,className:o}=p[i];return(0,t.jsx)(c.Tooltip,{title:a?n:r,children:(0,t.jsx)("span",{children:(0,t.jsx)(m,{icon:s,onClick:e,className:o,disabled:a,dataTestId:l})})})}],902555)},879664,e=>{"use strict";let t=(0,e.i(475254).default)("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);e.s(["default",0,t])},823429,e=>{"use strict";let t=(0,e.i(475254).default)("square-pen",[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",key:"1m0v6g"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",key:"ohrbg2"}]]);e.s(["default",0,t])},836991,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 18L18 6M6 6l12 12"}))});e.s(["XIcon",0,r],836991)},446891,e=>{"use strict";var t=e.i(843476),r=e.i(464571),a=e.i(326373),n=e.i(94629),l=e.i(360820),i=e.i(871943),s=e.i(836991);e.s(["TableHeaderSortDropdown",0,({sortState:e,onSortChange:o})=>{let d=[{key:"asc",label:"Ascending",icon:(0,t.jsx)(l.ChevronUpIcon,{className:"h-4 w-4"})},{key:"desc",label:"Descending",icon:(0,t.jsx)(i.ChevronDownIcon,{className:"h-4 w-4"})},{key:"reset",label:"Reset",icon:(0,t.jsx)(s.XIcon,{className:"h-4 w-4"})}];return(0,t.jsx)(a.Dropdown,{menu:{items:d,onClick:({key:e})=>{"asc"===e?o("asc"):"desc"===e?o("desc"):"reset"===e&&o(!1)},selectable:!0,selectedKeys:e?[e]:[]},trigger:["click"],autoAdjustOverflow:!0,children:(0,t.jsx)(r.Button,{type:"text",onClick:e=>e.stopPropagation(),icon:"asc"===e?(0,t.jsx)(l.ChevronUpIcon,{className:"h-4 w-4"}):"desc"===e?(0,t.jsx)(i.ChevronDownIcon,{className:"h-4 w-4"}):(0,t.jsx)(n.SwitchVerticalIcon,{className:"h-4 w-4"}),className:e?"text-blue-500 hover:text-blue-600":"text-gray-400 hover:text-blue-500"})})}])},625901,e=>{"use strict";var t=e.i(266027),r=e.i(621482),a=e.i(243652),n=e.i(602869),l=e.i(135214);let i=(0,a.createQueryKeys)("models"),s=(0,a.createQueryKeys)("modelHub"),o=(0,a.createQueryKeys)("allProxyModels");(0,a.createQueryKeys)("selectedTeamModels");let d=(0,a.createQueryKeys)("infiniteModels"),c=(0,a.createQueryKeys)("userModels");e.s(["useAllProxyModels",0,()=>{let{accessToken:e,userId:r,userRole:a}=(0,l.default)();return(0,t.useQuery)({queryKey:o.list({}),queryFn:async()=>await (0,n.modelAvailableCall)(e,r,a,!0,null,!0,!1,"expand"),enabled:!!(e&&r&&a)})},"useInfiniteModelInfo",0,(e=50,t)=>{let{accessToken:a,userId:i,userRole:s}=(0,l.default)();return(0,r.useInfiniteQuery)({queryKey:d.list({filters:{...i&&{userId:i},...s&&{userRole:s},size:e,...t&&{search:t}}}),queryFn:async({pageParam:r})=>await (0,n.modelInfoCall)(a,i,s,r,e,t),initialPageParam:1,getNextPageParam:e=>{if(e.current_page{let{accessToken:e}=(0,l.default)();return(0,t.useQuery)({queryKey:s.list({}),queryFn:async()=>await (0,n.modelHubCall)(e),enabled:!!e})},"useModelsInfo",0,(e=1,r=50,a,s,o,d,c)=>{let{accessToken:u,userId:g,userRole:m}=(0,l.default)();return(0,t.useQuery)({queryKey:i.list({filters:{...g&&{userId:g},...m&&{userRole:m},page:e,size:r,...a&&{search:a},...s&&{modelId:s},...o&&{teamId:o},...d&&{sortBy:d},...c&&{sortOrder:c}}}),queryFn:async()=>await (0,n.modelInfoCall)(u,g,m,e,r,a,s,o,d,c),enabled:!!(u&&g&&m)})},"useUserModels",0,()=>{let{accessToken:e,userId:r,userRole:a}=(0,l.default)();return(0,t.useQuery)({queryKey:c.list({}),queryFn:async()=>(await (0,n.modelAvailableCall)(e,r,a)).data.map(e=>e.id),enabled:!!(e&&r&&a)})}])},738014,e=>{"use strict";var t=e.i(135214),r=e.i(602869),a=e.i(266027);let n=(0,e.i(243652).createQueryKeys)("users");e.s(["useCurrentUser",0,()=>{let{accessToken:e,userId:l}=(0,t.default)();return(0,a.useQuery)({queryKey:n.detail(l),queryFn:async()=>await (0,r.userGetInfoV2)(e),enabled:!!(e&&l)})}])},162386,e=>{"use strict";var t=e.i(843476),r=e.i(625901),a=e.i(109799),n=e.i(785242),l=e.i(738014),i=e.i(199133),s=e.i(981339),o=e.i(592968);let d={label:"All Proxy Models",value:"all-proxy-models"},c={label:"No Default Models",value:"no-default-models"},u=[d,c],g={user:({allProxyModels:e,userModels:t,options:r})=>t&&r?.includeUserModels?t:[],team:({allProxyModels:e,selectedOrganization:t,userModels:r})=>t?t.models.includes(d.value)||0===t.models.length?e:e.filter(e=>t.models.includes(e)):e??[],organization:({allProxyModels:e})=>e,global:({allProxyModels:e})=>e};e.s(["ModelSelect",0,e=>{let{teamID:m,organizationID:p,options:h,context:f,dataTestId:x,value:b=[],onChange:y,style:v}=e,{includeUserModels:C,showAllTeamModelsOption:j,showAllProxyModelsOverride:$,includeSpecialOptions:w}=h||{},{data:k,isLoading:S}=(0,r.useAllProxyModels)(),{data:I,isLoading:E}=(0,n.useTeam)(m),{data:N,isLoading:O}=(0,a.useOrganization)(p),{data:M,isLoading:T}=(0,l.useCurrentUser)(),L=e=>u.some(t=>t.value===e),B=b.some(L),_=N?.models.includes(d.value)||N?.models.length===0;if(S||E||O||T)return(0,t.jsx)(s.Skeleton.Input,{active:!0,block:!0});let{wildcard:z,regular:A}=(e=>{let t=[],r=[];for(let a of e)a.endsWith("/*")?t.push(a):r.push(a);return{wildcard:t,regular:r}})(((e,t,r)=>{let a=Array.from(new Map(e.map(e=>[e.id,e])).values()).map(e=>e.id);if(t.options?.showAllProxyModelsOverride)return a;let n=g[t.context];return n?n({allProxyModels:a,...r,options:t.options}):[]})(k?.data??[],e,{selectedTeam:I,selectedOrganization:N,userModels:M?.models}));return(0,t.jsx)(i.Select,{"data-testid":x,value:b,onChange:e=>{let t=e.filter(L);y(t.length>0?[t[t.length-1]]:e)},style:v,options:[...w?[{label:(0,t.jsx)("span",{children:"Special Options"}),title:"Special Options",options:[...$||_&&w||"global"===f?[{label:(0,t.jsx)("span",{children:"All Proxy Models"}),value:d.value,disabled:b.length>0&&b.some(e=>L(e)&&e!==d.value),key:d.value}]:[],{label:(0,t.jsx)("span",{children:"No Default Models"}),value:c.value,disabled:b.length>0&&b.some(e=>L(e)&&e!==c.value),key:c.value}]}]:[],...z.length>0?[{label:(0,t.jsx)("span",{children:"Wildcard Options"}),title:"Wildcard Options",options:z.map(e=>{let r=e.replace("/*",""),a=r.charAt(0).toUpperCase()+r.slice(1);return{label:(0,t.jsx)("span",{children:`All ${a} models`}),value:e,disabled:B}})}]:[],{label:(0,t.jsx)("span",{children:"Models"}),title:"Models",options:A.map(e=>({label:(0,t.jsx)("span",{children:e}),value:e,disabled:B}))}],mode:"multiple",placeholder:"Select Models",allowClear:!0,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(o.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})})}],162386)},289793,e=>{"use strict";var t=e.i(602869),r=e.i(266027),a=e.i(243652),n=e.i(708347),l=e.i(135214);let i=(0,a.createQueryKeys)("agents");e.s(["useAgents",0,()=>{let{accessToken:e,userRole:a}=(0,l.default)();return(0,r.useQuery)({queryKey:i.list({}),queryFn:async()=>await (0,t.getAgentsList)(e),enabled:!!e&&n.all_admin_roles.includes(a||"")})}])},95684,e=>{"use strict";var t=e.i(165370);e.s(["Pagination",()=>t.default])},372943,897565,166452,e=>{"use strict";e.i(247167);var t=e.i(8211),r=e.i(271645),a=e.i(343794),n=e.i(529681),l=e.i(242064),i=e.i(704914),s=e.i(876556),o=e.i(290224),d=e.i(251224),c=function(e,t){var r={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(r[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,a=Object.getOwnPropertySymbols(e);nt.indexOf(a[n])&&Object.prototype.propertyIsEnumerable.call(e,a[n])&&(r[a[n]]=e[a[n]]);return r};function u({suffixCls:e,tagName:t,displayName:a}){return a=>r.forwardRef((n,l)=>r.createElement(a,Object.assign({ref:l,suffixCls:e,tagName:t},n)))}let g=r.forwardRef((e,t)=>{let{prefixCls:n,suffixCls:i,className:s,tagName:o}=e,u=c(e,["prefixCls","suffixCls","className","tagName"]),{getPrefixCls:g}=r.useContext(l.ConfigContext),m=g("layout",n),[p,h,f]=(0,d.default)(m),x=i?`${m}-${i}`:m;return p(r.createElement(o,Object.assign({className:(0,a.default)(n||x,s,h,f),ref:t},u)))}),m=r.forwardRef((e,u)=>{let{direction:g}=r.useContext(l.ConfigContext),[m,p]=r.useState([]),{prefixCls:h,className:f,rootClassName:x,children:b,hasSider:y,tagName:v,style:C}=e,j=c(e,["prefixCls","className","rootClassName","children","hasSider","tagName","style"]),$=(0,n.default)(j,["suffixCls"]),{getPrefixCls:w,className:k,style:S}=(0,l.useComponentConfig)("layout"),I=w("layout",h),E="boolean"==typeof y?y:!!m.length||(0,s.default)(b).some(e=>e.type===o.default),[N,O,M]=(0,d.default)(I),T=(0,a.default)(I,{[`${I}-has-sider`]:E,[`${I}-rtl`]:"rtl"===g},k,f,x,O,M),L=r.useMemo(()=>({siderHook:{addSider:e=>{p(r=>[].concat((0,t.default)(r),[e]))},removeSider:e=>{p(t=>t.filter(t=>t!==e))}}}),[]);return N(r.createElement(i.LayoutContext.Provider,{value:L},r.createElement(v,Object.assign({ref:u,className:T,style:Object.assign(Object.assign({},S),C)},$),b)))}),p=u({tagName:"div",displayName:"Layout"})(m),h=u({suffixCls:"header",tagName:"header",displayName:"Header"})(g),f=u({suffixCls:"footer",tagName:"footer",displayName:"Footer"})(g),x=u({suffixCls:"content",tagName:"main",displayName:"Content"})(g);p.Header=h,p.Footer=f,p.Content=x,p.Sider=o.default,p._InternalSiderContext=o.SiderContext,e.s(["Layout",0,p],372943);let b=(0,e.i(475254).default)("layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);e.s(["LayersIcon",0,b],897565);var y=e.i(98740);e.s(["UsersIcon",()=>y.default],166452)},180127,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-left",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);e.s(["default",0,t])},516430,e=>{"use strict";var t=e.i(180127);e.s(["ArrowLeftIcon",()=>t.default])},44068,e=>{"use strict";var t=e.i(823429);e.s(["EditIcon",()=>t.default])},573421,e=>{"use strict";e.i(247167);var t=e.i(8211),r=e.i(271645),a=e.i(343794),n=e.i(887719),l=e.i(908206),i=e.i(242064),s=e.i(721132),o=e.i(517455),d=e.i(281256),c=e.i(150073),u=e.i(165370),g=e.i(244451);let m=r.default.createContext({});m.Consumer;var p=e.i(763731),h=e.i(211576),f=function(e,t){var r={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(r[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,a=Object.getOwnPropertySymbols(e);nt.indexOf(a[n])&&Object.prototype.propertyIsEnumerable.call(e,a[n])&&(r[a[n]]=e[a[n]]);return r};let x=r.default.forwardRef((e,t)=>{let n,{prefixCls:l,children:s,actions:o,extra:d,styles:c,className:u,classNames:g,colStyle:x}=e,b=f(e,["prefixCls","children","actions","extra","styles","className","classNames","colStyle"]),{grid:y,itemLayout:v}=(0,r.useContext)(m),{getPrefixCls:C,list:j}=(0,r.useContext)(i.ConfigContext),$=e=>{var t,r;return(0,a.default)(null==(r=null==(t=null==j?void 0:j.item)?void 0:t.classNames)?void 0:r[e],null==g?void 0:g[e])},w=e=>{var t,r;return Object.assign(Object.assign({},null==(r=null==(t=null==j?void 0:j.item)?void 0:t.styles)?void 0:r[e]),null==c?void 0:c[e])},k=C("list",l),S=o&&o.length>0&&r.default.createElement("ul",{className:(0,a.default)(`${k}-item-action`,$("actions")),key:"actions",style:w("actions")},o.map((e,t)=>r.default.createElement("li",{key:`${k}-item-action-${t}`},e,t!==o.length-1&&r.default.createElement("em",{className:`${k}-item-action-split`})))),I=r.default.createElement(y?"div":"li",Object.assign({},b,y?{}:{ref:t},{className:(0,a.default)(`${k}-item`,{[`${k}-item-no-flex`]:!("vertical"===v?!!d:(n=!1,r.Children.forEach(s,e=>{"string"==typeof e&&(n=!0)}),!(n&&r.Children.count(s)>1)))},u)}),"vertical"===v&&d?[r.default.createElement("div",{className:`${k}-item-main`,key:"content"},s,S),r.default.createElement("div",{className:(0,a.default)(`${k}-item-extra`,$("extra")),key:"extra",style:w("extra")},d)]:[s,S,(0,p.cloneElement)(d,{key:"extra"})]);return y?r.default.createElement(h.Col,{ref:t,flex:1,style:x},I):I});x.Meta=e=>{var{prefixCls:t,className:n,avatar:l,title:s,description:o}=e,d=f(e,["prefixCls","className","avatar","title","description"]);let{getPrefixCls:c}=(0,r.useContext)(i.ConfigContext),u=c("list",t),g=(0,a.default)(`${u}-item-meta`,n),m=r.default.createElement("div",{className:`${u}-item-meta-content`},s&&r.default.createElement("h4",{className:`${u}-item-meta-title`},s),o&&r.default.createElement("div",{className:`${u}-item-meta-description`},o));return r.default.createElement("div",Object.assign({},d,{className:g}),l&&r.default.createElement("div",{className:`${u}-item-meta-avatar`},l),(s||o)&&m)},e.i(296059);var b=e.i(915654),y=e.i(183293),v=e.i(246422),C=e.i(838378);let j=(0,v.genStyleHooks)("List",e=>{let t=(0,C.mergeToken)(e,{listBorderedCls:`${e.componentCls}-bordered`,minHeight:e.controlHeightLG});return[(e=>{let{componentCls:t,antCls:r,controlHeight:a,minHeight:n,paddingSM:l,marginLG:i,padding:s,itemPadding:o,colorPrimary:d,itemPaddingSM:c,itemPaddingLG:u,paddingXS:g,margin:m,colorText:p,colorTextDescription:h,motionDurationSlow:f,lineWidth:x,headerBg:v,footerBg:C,emptyTextPadding:j,metaMarginBottom:$,avatarMarginRight:w,titleMarginBottom:k,descriptionFontSize:S}=e;return{[t]:Object.assign(Object.assign({},(0,y.resetComponent)(e)),{position:"relative","--rc-virtual-list-scrollbar-bg":e.colorSplit,"*":{outline:"none"},[`${t}-header`]:{background:v},[`${t}-footer`]:{background:C},[`${t}-header, ${t}-footer`]:{paddingBlock:l},[`${t}-pagination`]:{marginBlockStart:i,[`${r}-pagination-options`]:{textAlign:"start"}},[`${t}-spin`]:{minHeight:n,textAlign:"center"},[`${t}-items`]:{margin:0,padding:0,listStyle:"none"},[`${t}-item`]:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:o,color:p,[`${t}-item-meta`]:{display:"flex",flex:1,alignItems:"flex-start",maxWidth:"100%",[`${t}-item-meta-avatar`]:{marginInlineEnd:w},[`${t}-item-meta-content`]:{flex:"1 0",width:0,color:p},[`${t}-item-meta-title`]:{margin:`0 0 ${(0,b.unit)(e.marginXXS)} 0`,color:p,fontSize:e.fontSize,lineHeight:e.lineHeight,"> a":{color:p,transition:`all ${f}`,"&:hover":{color:d}}},[`${t}-item-meta-description`]:{color:h,fontSize:S,lineHeight:e.lineHeight}},[`${t}-item-action`]:{flex:"0 0 auto",marginInlineStart:e.marginXXL,padding:0,fontSize:0,listStyle:"none","& > li":{position:"relative",display:"inline-block",padding:`0 ${(0,b.unit)(g)}`,color:h,fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"center","&:first-child":{paddingInlineStart:0}},[`${t}-item-action-split`]:{position:"absolute",insetBlockStart:"50%",insetInlineEnd:0,width:x,height:e.calc(e.fontHeight).sub(e.calc(e.marginXXS).mul(2)).equal(),transform:"translateY(-50%)",backgroundColor:e.colorSplit}}},[`${t}-empty`]:{padding:`${(0,b.unit)(s)} 0`,color:h,fontSize:e.fontSizeSM,textAlign:"center"},[`${t}-empty-text`]:{padding:j,color:e.colorTextDisabled,fontSize:e.fontSize,textAlign:"center"},[`${t}-item-no-flex`]:{display:"block"}}),[`${t}-grid ${r}-col > ${t}-item`]:{display:"block",maxWidth:"100%",marginBlockEnd:m,paddingBlock:0,borderBlockEnd:"none"},[`${t}-vertical ${t}-item`]:{alignItems:"initial",[`${t}-item-main`]:{display:"block",flex:1},[`${t}-item-extra`]:{marginInlineStart:i},[`${t}-item-meta`]:{marginBlockEnd:$,[`${t}-item-meta-title`]:{marginBlockStart:0,marginBlockEnd:k,color:p,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}},[`${t}-item-action`]:{marginBlockStart:s,marginInlineStart:"auto","> li":{padding:`0 ${(0,b.unit)(s)}`,"&:first-child":{paddingInlineStart:0}}}},[`${t}-split ${t}-item`]:{borderBlockEnd:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderBlockEnd:"none"}},[`${t}-split ${t}-header`]:{borderBlockEnd:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`},[`${t}-split${t}-empty ${t}-footer`]:{borderTop:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`},[`${t}-loading ${t}-spin-nested-loading`]:{minHeight:a},[`${t}-split${t}-something-after-last-item ${r}-spin-container > ${t}-items > ${t}-item:last-child`]:{borderBlockEnd:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`},[`${t}-lg ${t}-item`]:{padding:u},[`${t}-sm ${t}-item`]:{padding:c},[`${t}:not(${t}-vertical)`]:{[`${t}-item-no-flex`]:{[`${t}-item-action`]:{float:"right"}}}}})(t),(e=>{let{listBorderedCls:t,componentCls:r,paddingLG:a,margin:n,itemPaddingSM:l,itemPaddingLG:i,marginLG:s,borderRadiusLG:o}=e,d=(0,b.unit)(e.calc(o).sub(e.lineWidth).equal());return{[t]:{border:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:o,[`${r}-header`]:{borderRadius:`${d} ${d} 0 0`},[`${r}-footer`]:{borderRadius:`0 0 ${d} ${d}`},[`${r}-header,${r}-footer,${r}-item`]:{paddingInline:a},[`${r}-pagination`]:{margin:`${(0,b.unit)(n)} ${(0,b.unit)(s)}`}},[`${t}${r}-sm`]:{[`${r}-item,${r}-header,${r}-footer`]:{padding:l}},[`${t}${r}-lg`]:{[`${r}-item,${r}-header,${r}-footer`]:{padding:i}}}})(t),(e=>{let{componentCls:t,screenSM:r,screenMD:a,marginLG:n,marginSM:l,margin:i}=e;return{[`@media screen and (max-width:${a}px)`]:{[t]:{[`${t}-item`]:{[`${t}-item-action`]:{marginInlineStart:n}}},[`${t}-vertical`]:{[`${t}-item`]:{[`${t}-item-extra`]:{marginInlineStart:n}}}},[`@media screen and (max-width: ${r}px)`]:{[t]:{[`${t}-item`]:{flexWrap:"wrap",[`${t}-action`]:{marginInlineStart:l}}},[`${t}-vertical`]:{[`${t}-item`]:{flexWrap:"wrap-reverse",[`${t}-item-main`]:{minWidth:e.contentWidth},[`${t}-item-extra`]:{margin:`auto auto ${(0,b.unit)(i)}`}}}}}})(t)]},e=>({contentWidth:220,itemPadding:`${(0,b.unit)(e.paddingContentVertical)} 0`,itemPaddingSM:`${(0,b.unit)(e.paddingContentVerticalSM)} ${(0,b.unit)(e.paddingContentHorizontal)}`,itemPaddingLG:`${(0,b.unit)(e.paddingContentVerticalLG)} ${(0,b.unit)(e.paddingContentHorizontalLG)}`,headerBg:"transparent",footerBg:"transparent",emptyTextPadding:e.padding,metaMarginBottom:e.padding,avatarMarginRight:e.padding,titleMarginBottom:e.paddingSM,descriptionFontSize:e.fontSize}));var $=function(e,t){var r={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(r[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var n=0,a=Object.getOwnPropertySymbols(e);nt.indexOf(a[n])&&Object.prototype.propertyIsEnumerable.call(e,a[n])&&(r[a[n]]=e[a[n]]);return r};let w=r.forwardRef(function(e,p){let{pagination:h=!1,prefixCls:f,bordered:x=!1,split:b=!0,className:y,rootClassName:v,style:C,children:w,itemLayout:k,loadMore:S,grid:I,dataSource:E=[],size:N,header:O,footer:M,loading:T=!1,rowKey:L,renderItem:B,locale:_}=e,z=$(e,["pagination","prefixCls","bordered","split","className","rootClassName","style","children","itemLayout","loadMore","grid","dataSource","size","header","footer","loading","rowKey","renderItem","locale"]),A=h&&"object"==typeof h?h:{},[P,R]=r.useState(A.defaultCurrent||1),[F,D]=r.useState(A.defaultPageSize||10),{getPrefixCls:q,direction:H,className:G,style:K}=(0,i.useComponentConfig)("list"),{renderEmpty:W}=r.useContext(i.ConfigContext),U=e=>(t,r)=>{var a;R(t),D(r),h&&(null==(a=null==h?void 0:h[e])||a.call(h,t,r))},Q=U("onChange"),V=U("onShowSizeChange"),X=!!(S||h||M),Y=q("list",f),[J,Z,ee]=j(Y),et=T;"boolean"==typeof et&&(et={spinning:et});let er=!!(null==et?void 0:et.spinning),ea=(0,o.default)(N),en="";switch(ea){case"large":en="lg";break;case"small":en="sm"}let el=(0,a.default)(Y,{[`${Y}-vertical`]:"vertical"===k,[`${Y}-${en}`]:en,[`${Y}-split`]:b,[`${Y}-bordered`]:x,[`${Y}-loading`]:er,[`${Y}-grid`]:!!I,[`${Y}-something-after-last-item`]:X,[`${Y}-rtl`]:"rtl"===H},G,y,v,Z,ee),ei=(0,n.default)({current:1,total:0,position:"bottom"},{total:E.length,current:P,pageSize:F},h||{}),es=Math.ceil(ei.total/ei.pageSize);ei.current=Math.min(ei.current,es);let eo=h&&r.createElement("div",{className:(0,a.default)(`${Y}-pagination`)},r.createElement(u.default,Object.assign({align:"end"},ei,{onChange:Q,onShowSizeChange:V}))),ed=(0,t.default)(E);h&&E.length>(ei.current-1)*ei.pageSize&&(ed=(0,t.default)(E).splice((ei.current-1)*ei.pageSize,ei.pageSize));let ec=Object.keys(I||{}).some(e=>["xs","sm","md","lg","xl","xxl"].includes(e)),eu=(0,c.default)(ec),eg=r.useMemo(()=>{for(let e=0;e{if(!I)return;let e=eg&&I[eg]?I[eg]:I.column;if(e)return{width:`${100/e}%`,maxWidth:`${100/e}%`}},[JSON.stringify(I),eg]),ep=er&&r.createElement("div",{style:{minHeight:53}});if(ed.length>0){let e=ed.map((e,t)=>{let a;return B?((a="function"==typeof L?L(e):L?e[L]:e.key)||(a=`list-item-${t}`),r.createElement(r.Fragment,{key:a},B(e,t))):null});ep=I?r.createElement(d.Row,{gutter:I.gutter},r.Children.map(e,e=>r.createElement("div",{key:null==e?void 0:e.key,style:em},e))):r.createElement("ul",{className:`${Y}-items`},e)}else w||er||(ep=r.createElement("div",{className:`${Y}-empty-text`},(null==_?void 0:_.emptyText)||(null==W?void 0:W("List"))||r.createElement(s.default,{componentName:"List"})));let eh=ei.position,ef=r.useMemo(()=>({grid:I,itemLayout:k}),[JSON.stringify(I),k]);return J(r.createElement(m.Provider,{value:ef},r.createElement("div",Object.assign({ref:p,style:Object.assign(Object.assign({},K),C),className:el},z),("top"===eh||"both"===eh)&&eo,O&&r.createElement("div",{className:`${Y}-header`},O),r.createElement(g.default,Object.assign({},et),ep,w),M&&r.createElement("div",{className:`${Y}-footer`},M),S||("bottom"===eh||"both"===eh)&&eo)))});w.Item=x,e.s(["List",0,w],573421)},21548,e=>{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},988846,e=>{"use strict";var t=e.i(54943);e.s(["SearchIcon",()=>t.default])},438100,e=>{"use strict";let t=(0,e.i(475254).default)("key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]]);e.s(["KeyIcon",0,t],438100)},302202,e=>{"use strict";var t=e.i(953651);e.s(["ServerIcon",()=>t.default])},168118,e=>{"use strict";var t=e.i(879664);e.s(["InfoIcon",()=>t.default])},852119,e=>{"use strict";var t=e.i(843476),r=e.i(263147),a=e.i(954616),n=e.i(912598),l=e.i(602869),i=e.i(431703),s=e.i(135214);let o=async(e,t)=>{let r=(0,l.getProxyBaseUrl)(),a=`${r}/v1/access_group/${encodeURIComponent(t)}`,n=await fetch(a,{method:"DELETE",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!n.ok){let e=await n.json(),t=(0,i.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}};var d=e.i(646563),c=e.i(152990),u=e.i(682830),g=e.i(464571),m=e.i(175712),p=e.i(525720),h=e.i(311451),f=e.i(372943),x=e.i(95684),b=e.i(770914),y=e.i(291542),v=e.i(262218),C=e.i(368869),j=e.i(592968),$=e.i(898586),w=e.i(657150),w=w,k=e.i(897565),S=e.i(988846),I=e.i(302202),E=e.i(271645),N=e.i(127952),O=e.i(902555),M=e.i(446891);e.i(622826);var T=e.i(200208),L=e.i(399536),B=e.i(266027),_=e.i(708347);let z=async(e,t)=>{let r=(0,l.getProxyBaseUrl)(),a=`${r}/v1/access_group/${encodeURIComponent(t)}`,n=await fetch(a,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!n.ok){let e=await n.json(),t=(0,i.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return n.json()};var A=e.i(178654),P=e.i(869216),R=e.i(21548),F=e.i(573421),D=e.i(621192),q=e.i(482725),H=e.i(653496),G=e.i(516430),w=w,K=e.i(44068),W=e.i(438100),U=e.i(166452),Q=e.i(304911),V=e.i(212931),X=e.i(808613),Y=e.i(888259),J=e.i(289793),Z=e.i(500727),ee=e.i(162386),et=e.i(199133),w=w,er=e.i(168118);let{TextArea:ea}=h.Input;function en({form:e,isNameDisabled:r=!1}){let{data:a}=(0,J.useAgents)(),{data:n}=(0,Z.useMCPServers)(),l=a?.agents??[],i=[{key:"1",label:(0,t.jsxs)(b.Space,{align:"center",size:4,children:[(0,t.jsx)(er.InfoIcon,{size:16}),"General Info"]}),children:(0,t.jsxs)("div",{style:{paddingTop:16},children:[(0,t.jsx)(X.Form.Item,{name:"name",label:"Group Name",rules:[{required:!0,message:"Please enter the access group name"}],children:(0,t.jsx)(h.Input,{placeholder:"e.g. Engineering Team",disabled:r})}),(0,t.jsx)(X.Form.Item,{name:"description",label:"Description",children:(0,t.jsx)(ea,{rows:4,placeholder:"Describe the purpose of this access group..."})})]})},{key:"2",label:(0,t.jsxs)(b.Space,{align:"center",size:4,children:[(0,t.jsx)(k.LayersIcon,{size:16}),"Models"]}),children:(0,t.jsx)("div",{style:{paddingTop:16},children:(0,t.jsx)(X.Form.Item,{name:"modelIds",label:"Allowed Models",children:(0,t.jsx)(ee.ModelSelect,{context:"global",value:e.getFieldValue("modelIds")??[],onChange:t=>e.setFieldsValue({modelIds:t}),style:{width:"100%"}})})})},{key:"3",label:(0,t.jsxs)(b.Space,{align:"center",size:4,children:[(0,t.jsx)(I.ServerIcon,{size:16}),"MCP Servers"]}),children:(0,t.jsx)("div",{style:{paddingTop:16},children:(0,t.jsx)(X.Form.Item,{name:"mcpServerIds",label:"Allowed MCP Servers",children:(0,t.jsx)(et.Select,{mode:"multiple",placeholder:"Select MCP servers",style:{width:"100%"},optionFilterProp:"label",allowClear:!0,options:(n??[]).map(e=>({label:e.server_name??e.server_id,value:e.server_id}))})})})},{key:"4",label:(0,t.jsxs)(b.Space,{align:"center",size:4,children:[(0,t.jsx)(w.default,{size:16}),"Agents"]}),children:(0,t.jsx)("div",{style:{paddingTop:16},children:(0,t.jsx)(X.Form.Item,{name:"agentIds",label:"Allowed Agents",children:(0,t.jsx)(et.Select,{mode:"multiple",placeholder:"Select agents",style:{width:"100%"},optionFilterProp:"label",allowClear:!0,options:l.map(e=>({label:e.agent_name,value:e.agent_id}))})})})}];return(0,t.jsx)(X.Form,{form:e,layout:"vertical",name:"access_group_form",initialValues:{modelIds:[],mcpServerIds:[],agentIds:[]},children:(0,t.jsx)(H.Tabs,{defaultActiveKey:"1",items:i})})}let el=async(e,t,r)=>{let a=(0,l.getProxyBaseUrl)(),n=`${a}/v1/access_group/${encodeURIComponent(t)}`,s=await fetch(n,{method:"PUT",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!s.ok){let e=await s.json(),t=(0,i.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return s.json()};function ei({visible:e,accessGroup:l,onCancel:i,onSuccess:o}){let[d]=X.Form.useForm(),c=(()=>{let{accessToken:e}=(0,s.default)(),t=(0,n.useQueryClient)();return(0,a.useMutation)({mutationFn:async({accessGroupId:t,params:r})=>{if(!e)throw Error("Access token is required");return el(e,t,r)},onSuccess:(e,{accessGroupId:a})=>{t.invalidateQueries({queryKey:r.accessGroupKeys.all}),t.invalidateQueries({queryKey:r.accessGroupKeys.detail(a)})}})})();return(0,E.useEffect)(()=>{e&&l&&d.setFieldsValue({name:l.access_group_name,description:l.description??"",modelIds:l.access_model_names??[],mcpServerIds:l.access_mcp_server_ids??[],agentIds:l.access_agent_ids??[]})},[e,l,d]),(0,t.jsx)(V.Modal,{title:"Edit Access Group",open:e,onOk:()=>{d.validateFields().then(e=>{let t={access_group_name:e.name,description:e.description,access_model_names:e.modelIds,access_mcp_server_ids:e.mcpServerIds,access_agent_ids:e.agentIds};c.mutate({accessGroupId:l.access_group_id,params:t},{onSuccess:()=>{Y.default.success("Access group updated successfully"),o?.(),i()}})}).catch(e=>{})},onCancel:i,width:700,okText:"Save Changes",cancelText:"Cancel",confirmLoading:c.isPending,destroyOnHidden:!0,children:(0,t.jsx)(en,{form:d})})}let{Title:es,Text:eo}=$.Typography,{Content:ed}=f.Layout;function ec({accessGroupId:e,onBack:a}){let{data:l,isLoading:i}=(e=>{let{accessToken:t,userRole:a}=(0,s.default)(),l=(0,n.useQueryClient)();return(0,B.useQuery)({queryKey:r.accessGroupKeys.detail(e),queryFn:async()=>z(t,e),enabled:!!(t&&e)&&_.all_admin_roles.includes(a||""),initialData:()=>{if(!e)return;let t=l.getQueryData(r.accessGroupKeys.list({}));return t?.find(t=>t.access_group_id===e)}})})(e),{token:o}=C.theme.useToken(),[d,c]=(0,E.useState)(!1),[u,h]=(0,E.useState)(!1),[f,x]=(0,E.useState)(!1);if(i)return(0,t.jsx)(ed,{style:{padding:o.paddingLG,paddingInline:2*o.paddingLG},children:(0,t.jsx)(p.Flex,{justify:"center",align:"center",style:{minHeight:300},children:(0,t.jsx)(q.Spin,{size:"large"})})});if(!l)return(0,t.jsxs)(ed,{style:{padding:o.paddingLG,paddingInline:2*o.paddingLG},children:[(0,t.jsx)(g.Button,{icon:(0,t.jsx)(G.ArrowLeftIcon,{size:16}),onClick:a,type:"text",style:{marginBottom:16}}),(0,t.jsx)(R.Empty,{description:"Access group not found"})]});let b=l.access_model_names??[],y=l.access_mcp_server_ids??[],j=l.access_agent_ids??[],$=l.assigned_key_ids??[],S=l.assigned_team_ids??[],N=u?$:$.slice(0,5),O=f?S:S.slice(0,5),M=[{key:"models",label:(0,t.jsxs)(p.Flex,{align:"center",gap:8,children:[(0,t.jsx)(k.LayersIcon,{size:16}),"Models",(0,t.jsx)(v.Tag,{style:{marginInlineEnd:0},children:b?.length})]}),children:b?.length>0?(0,t.jsx)(F.List,{grid:{gutter:16,xs:1,sm:2,md:3,lg:4},dataSource:b,renderItem:e=>(0,t.jsx)(F.List.Item,{children:(0,t.jsx)(m.Card,{size:"small",children:(0,t.jsx)(eo,{code:!0,children:e})})})}):(0,t.jsx)(R.Empty,{description:"No models assigned to this group"})},{key:"mcp",label:(0,t.jsxs)(p.Flex,{align:"center",gap:8,children:[(0,t.jsx)(I.ServerIcon,{size:16}),"MCP Servers",(0,t.jsx)(v.Tag,{children:y?.length})]}),children:y?.length>0?(0,t.jsx)(F.List,{grid:{gutter:16,xs:1,sm:2,md:3,lg:4},dataSource:y,renderItem:e=>(0,t.jsx)(F.List.Item,{children:(0,t.jsx)(m.Card,{size:"small",children:(0,t.jsx)(eo,{code:!0,children:e})})})}):(0,t.jsx)(R.Empty,{description:"No MCP servers assigned to this group"})},{key:"agents",label:(0,t.jsxs)(p.Flex,{align:"center",gap:8,children:[(0,t.jsx)(w.default,{size:16}),"Agents",(0,t.jsx)(v.Tag,{children:j?.length})]}),children:j?.length>0?(0,t.jsx)(F.List,{grid:{gutter:16,xs:1,sm:2,md:3,lg:4},dataSource:j,renderItem:e=>(0,t.jsx)(F.List.Item,{children:(0,t.jsx)(m.Card,{size:"small",children:(0,t.jsx)(eo,{code:!0,children:e})})})}):(0,t.jsx)(R.Empty,{description:"No agents assigned to this group"})}];return(0,t.jsxs)(ed,{style:{padding:o.paddingLG,paddingInline:2*o.paddingLG},children:[(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:24},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:16},children:[(0,t.jsx)(g.Button,{icon:(0,t.jsx)(G.ArrowLeftIcon,{size:16}),onClick:a,type:"text"}),(0,t.jsxs)("div",{children:[(0,t.jsx)(es,{level:2,style:{margin:0},children:l.access_group_name}),(0,t.jsxs)(eo,{type:"secondary",children:["ID: ",(0,t.jsx)(eo,{copyable:!0,children:l.access_group_id})]})]})]}),(0,t.jsx)(g.Button,{type:"primary",icon:(0,t.jsx)(K.EditIcon,{size:16}),onClick:()=>{c(!0)},children:"Edit Access Group"})]}),(0,t.jsx)(D.Row,{style:{marginBottom:24},children:(0,t.jsx)(m.Card,{children:(0,t.jsxs)(P.Descriptions,{title:"Group Details",column:1,children:[(0,t.jsx)(P.Descriptions.Item,{label:"Description",children:l.description||"—"}),(0,t.jsxs)(P.Descriptions.Item,{label:"Created",children:[new Date(l.created_at).toLocaleString(),l.created_by&&(0,t.jsxs)(eo,{children:[" ","by"," ",(0,t.jsx)(Q.default,{userId:l.created_by})]})]}),(0,t.jsxs)(P.Descriptions.Item,{label:"Last Updated",children:[new Date(l.updated_at).toLocaleString(),l.updated_by&&(0,t.jsxs)(eo,{children:[" ","by"," ",(0,t.jsx)(Q.default,{userId:l.updated_by})]})]})]})})}),(0,t.jsxs)(D.Row,{gutter:[16,16],style:{marginBottom:24},children:[(0,t.jsx)(A.Col,{xs:24,lg:12,children:(0,t.jsx)(m.Card,{title:(0,t.jsxs)(p.Flex,{align:"center",gap:8,children:[(0,t.jsx)(W.KeyIcon,{size:16}),"Attached Keys",(0,t.jsx)(v.Tag,{children:$?.length})]}),extra:$?.length>5?(0,t.jsx)(g.Button,{type:"link",onClick:()=>h(!u),children:u?"Show Less":`View All (${$?.length})`}):null,children:$?.length>0?(0,t.jsx)(p.Flex,{wrap:"wrap",gap:8,children:N.map(e=>(0,t.jsx)(v.Tag,{children:(0,t.jsx)(eo,{code:!0,style:{fontSize:12},children:e.length>20?`${e.slice(0,10)}...${e.slice(-6)}`:e})},e))}):(0,t.jsx)(R.Empty,{description:"No keys attached",image:R.Empty.PRESENTED_IMAGE_SIMPLE})})}),(0,t.jsx)(A.Col,{xs:24,lg:12,children:(0,t.jsx)(m.Card,{title:(0,t.jsxs)(p.Flex,{align:"center",gap:8,children:[(0,t.jsx)(U.UsersIcon,{size:16}),"Attached Teams",(0,t.jsx)(v.Tag,{children:S?.length})]}),extra:S?.length>5?(0,t.jsx)(g.Button,{type:"link",onClick:()=>x(!f),children:f?"Show Less":`View All (${S?.length})`}):null,children:S?.length>0?(0,t.jsx)(p.Flex,{wrap:"wrap",gap:8,children:O.map(e=>(0,t.jsx)(v.Tag,{children:(0,t.jsx)(eo,{code:!0,style:{fontSize:12},children:e})},e))}):(0,t.jsx)(R.Empty,{description:"No teams attached",image:R.Empty.PRESENTED_IMAGE_SIMPLE})})})]}),(0,t.jsx)(m.Card,{children:(0,t.jsx)(H.Tabs,{defaultActiveKey:"models",items:M})}),(0,t.jsx)(ei,{visible:d,accessGroup:l,onCancel:()=>c(!1)})]})}let eu=async(e,t)=>{let r=(0,l.getProxyBaseUrl)(),a=`${r}/v1/access_group`,n=await fetch(a,{method:"POST",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(!n.ok){let e=await n.json(),t=(0,i.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return n.json()};function eg({visible:e,onCancel:l,onSuccess:i}){let[o]=X.Form.useForm(),d=(()=>{let{accessToken:e}=(0,s.default)(),t=(0,n.useQueryClient)();return(0,a.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return eu(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:r.accessGroupKeys.all})}})})();return(0,t.jsx)(V.Modal,{title:"Create Access Group",open:e,onOk:()=>{o.validateFields().then(e=>{let t={access_group_name:e.name,description:e.description,access_model_names:e.modelIds,access_mcp_server_ids:e.mcpServerIds,access_agent_ids:e.agentIds};d.mutate(t,{onSuccess:()=>{Y.default.success("Access group created successfully"),o.resetFields(),i?.(),l()}})}).catch(e=>{})},onCancel:l,width:700,okText:"Create Group",cancelText:"Cancel",confirmLoading:d.isPending,destroyOnClose:!0,children:(0,t.jsx)(en,{form:o})})}let{Title:em,Text:ep}=$.Typography,{Content:eh}=f.Layout;function ef(e){return{id:e.access_group_id,name:e.access_group_name,description:e.description??"",modelIds:e.access_model_names,mcpServerIds:e.access_mcp_server_ids,agentIds:e.access_agent_ids,keyIds:e.assigned_key_ids,teamIds:e.assigned_team_ids,createdAt:e.created_at,createdBy:e.created_by??"",updatedAt:e.updated_at,updatedBy:e.updated_by??""}}function ex(){let{token:e}=C.theme.useToken(),{userRole:l}=(0,s.default)(),i=(0,_.isProxyAdminRole)(l??""),{data:f,isLoading:$}=(0,r.useAccessGroups)(),B=(0,E.useMemo)(()=>(f??[]).map(ef),[f]),[z,A]=(0,E.useState)(null),[P,R]=(0,E.useState)(!1),[F,D]=(0,E.useState)(""),[q,H]=(0,E.useState)(1),[G,K]=(0,E.useState)([]),[W,U]=(0,E.useState)(null),Q=(()=>{let{accessToken:e}=(0,s.default)(),t=(0,n.useQueryClient)();return(0,a.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return o(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:r.accessGroupKeys.all})}})})();(0,E.useEffect)(()=>{H(1)},[F]);let V=(0,E.useMemo)(()=>B.filter(e=>e.name.toLowerCase().includes(F.toLowerCase())||e.id.toLowerCase().includes(F.toLowerCase())||e.description.toLowerCase().includes(F.toLowerCase())),[B,F]),X=(0,E.useMemo)(()=>[{id:"id",accessorKey:"id",header:()=>(0,t.jsx)("span",{children:"ID"}),enableSorting:!1,size:170,cell:({row:e})=>(0,t.jsx)(L.IdCell,{value:e.original.id,onClick:A})},{id:"name",accessorKey:"name",header:()=>(0,t.jsx)("span",{children:"Name"}),enableSorting:!0,cell:({getValue:e})=>e()},{id:"resources",header:()=>(0,t.jsx)("span",{children:"Resources"}),enableSorting:!1,cell:({row:e})=>{let r=e.original,a=r.modelIds??[],n=r.mcpServerIds??[],l=r.agentIds??[];return(0,t.jsxs)(p.Flex,{gap:12,align:"center",children:[(0,t.jsx)(j.Tooltip,{title:`${a?.length} Models`,children:(0,t.jsx)(v.Tag,{color:"blue",style:{fontSize:14,padding:"2px 8px",margin:0},children:(0,t.jsxs)(p.Flex,{align:"center",gap:6,children:[(0,t.jsx)(k.LayersIcon,{size:14}),a?.length]})})}),(0,t.jsx)(j.Tooltip,{title:`${n?.length} MCP Servers`,children:(0,t.jsx)(v.Tag,{color:"cyan",style:{fontSize:14,padding:"2px 8px",margin:0},children:(0,t.jsxs)(p.Flex,{align:"center",gap:6,children:[(0,t.jsx)(I.ServerIcon,{size:14}),n?.length]})})}),(0,t.jsx)(j.Tooltip,{title:`${l?.length} Agents`,children:(0,t.jsx)(v.Tag,{color:"purple",style:{fontSize:14,padding:"2px 8px",margin:0},children:(0,t.jsxs)(p.Flex,{align:"center",gap:6,children:[(0,t.jsx)(w.default,{size:14}),l?.length]})})})]})}},{id:"createdAt",accessorKey:"createdAt",header:()=>(0,t.jsx)("span",{children:"Created"}),enableSorting:!0,sortingFn:"datetime",cell:({getValue:e})=>(0,t.jsx)(T.DateCell,{value:e(),precision:"date"}),meta:{responsive:["lg"]}},{id:"updatedAt",accessorKey:"updatedAt",header:()=>(0,t.jsx)("span",{children:"Updated"}),enableSorting:!1,cell:({getValue:e})=>(0,t.jsx)(T.DateCell,{value:e(),precision:"date"}),meta:{responsive:["xl"]}},...i?[{id:"actions",header:()=>(0,t.jsx)("span",{children:"Actions"}),enableSorting:!1,cell:({row:e})=>(0,t.jsx)(b.Space,{children:(0,t.jsx)(O.default,{variant:"Delete",tooltipText:"Delete access group",onClick:()=>U(e.original)})})}]:[]],[i]),Y=(0,c.useReactTable)({data:V,columns:X,state:{sorting:G},onSortingChange:K,getCoreRowModel:(0,u.getCoreRowModel)(),getSortedRowModel:(0,u.getSortedRowModel)(),getRowId:e=>e.id}),J=Y.getRowModel().rows,Z=J.slice((q-1)*10,10*q),ee=(0,E.useMemo)(()=>new Map(Z.map(e=>[e.original.id,e])),[Z]),et=(Y.getHeaderGroups()[0]?.headers??[]).map(e=>{let r=e.column.getCanSort(),a=e.column.getIsSorted(),n=e.column.columnDef.meta,l={title:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:4},children:[e.isPlaceholder?null:(0,c.flexRender)(e.column.columnDef.header,e.getContext()),r&&(0,t.jsx)(M.TableHeaderSortDropdown,{sortState:!1!==a&&a,onSortChange:t=>{K(!1===t?[]:[{id:e.column.id,desc:"desc"===t}])},columnId:e.column.id})]}),key:e.id,width:e.column.columnDef.size,render:(t,r)=>{let a=ee.get(r.id);if(!a)return null;let n=a.getVisibleCells().find(t=>t.column.id===e.id);return n?(0,c.flexRender)(n.column.columnDef.cell,n.getContext()):null}};return n?.responsive&&(l.responsive=n.responsive),l}),er=Z.map(e=>e.original);return z?(0,t.jsx)(ec,{accessGroupId:z,onBack:()=>A(null)}):(0,t.jsxs)(eh,{style:{padding:e.paddingLG,paddingInline:2*e.paddingLG},children:[(0,t.jsxs)(p.Flex,{justify:"space-between",align:"center",style:{marginBottom:16},children:[(0,t.jsxs)(b.Space,{direction:"vertical",size:0,children:[(0,t.jsx)(em,{level:2,style:{margin:0},children:"Access Groups"}),(0,t.jsx)(ep,{type:"secondary",children:"Manage resource permissions for your organization"})]}),i&&(0,t.jsx)(g.Button,{type:"primary",icon:(0,t.jsx)(d.PlusOutlined,{}),onClick:()=>R(!0),children:"Create Access Group"})]}),(0,t.jsxs)(m.Card,{styles:{body:{padding:0}},children:[(0,t.jsxs)(p.Flex,{justify:"space-between",align:"center",style:{padding:"12px 16px"},children:[(0,t.jsx)(h.Input,{prefix:(0,t.jsx)(S.SearchIcon,{size:16}),placeholder:"Search groups by name, ID, or description...",style:{maxWidth:400},value:F,onChange:e=>D(e.target.value),allowClear:!0}),(0,t.jsx)(x.Pagination,{current:q,total:J?.length,pageSize:10,onChange:e=>H(e),size:"small",showTotal:e=>`${e} groups`,showSizeChanger:!1})]}),(0,t.jsx)(y.Table,{columns:et,dataSource:er,rowKey:"id",loading:$,pagination:!1})]}),(0,t.jsx)(eg,{visible:P,onCancel:()=>R(!1)}),(0,t.jsx)(N.default,{isOpen:!!W,title:"Delete Access Group",message:"Are you sure you want to delete this access group? This action cannot be undone.",resourceInformationTitle:"Access Group Information",resourceInformation:[{label:"ID",value:W?.id,code:!0},{label:"Name",value:W?.name},{label:"Description",value:W?.description||"—"}],onCancel:()=>U(null),onOk:()=>{W&&Q.mutate(W.id,{onSuccess:()=>{U(null)}})},confirmLoading:Q.isPending})]})}e.s(["default",0,function(){return(0,s.default)(),(0,t.jsx)(ex,{})}],852119)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0sx3mu2_l9g_y.js b/litellm/proxy/_experimental/out/_next/static/chunks/0sx3mu2_l9g_y.js new file mode 100644 index 00000000000..4e794015780 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0sx3mu2_l9g_y.js @@ -0,0 +1,21 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,515831,955719,e=>{"use strict";e.i(247167);var t,n=e.i(271645),r=e.i(8211),a=e.i(174080),i=e.i(343794),l=e.i(931067),o=e.i(278409),s=e.i(233848),u=e.i(971151),c=e.i(868917),d=e.i(674813),p=e.i(211577),f=e.i(209428),m=e.i(703923),h=e.i(410160),g=e.i(31575),b=e.i(33968),v=e.i(244009),y=e.i(883110);let $=function(e,t){if(e&&t){var n=Array.isArray(t)?t:t.split(","),r=e.name||"",a=e.type||"",i=a.replace(/\/.*$/,"");return n.some(function(e){var t=e.trim();if(/^\*(\/\*)?$/.test(e))return!0;if("."===t.charAt(0)){var n=r.toLowerCase(),l=t.toLowerCase(),o=[l];return(".jpg"===l||".jpeg"===l)&&(o=[".jpg",".jpeg"]),o.some(function(e){return n.endsWith(e)})}return/\/\*$/.test(t)?i===t.replace(/\/.*$/,""):a===t||!!/^\w+$/.test(t)&&((0,y.default)(!1,"Upload takes an invalidate 'accept' type '".concat(t,"'.Skip for check.")),!0)})}return!0};function w(e){var t=e.responseText||e.response;if(!t)return t;try{return JSON.parse(t)}catch(e){return t}}function E(e){var t=new XMLHttpRequest;e.onProgress&&t.upload&&(t.upload.onprogress=function(t){t.total>0&&(t.percent=t.loaded/t.total*100),e.onProgress(t)});var n=new FormData;e.data&&Object.keys(e.data).forEach(function(t){var r=e.data[t];Array.isArray(r)?r.forEach(function(e){n.append("".concat(t,"[]"),e)}):n.append(t,r)}),e.file instanceof Blob?n.append(e.filename,e.file,e.file.name):n.append(e.filename,e.file),t.onerror=function(t){e.onError(t)},t.onload=function(){if(t.status<200||t.status>=300){var n;return e.onError(((n=Error("cannot ".concat(e.method," ").concat(e.action," ").concat(t.status,"'"))).status=t.status,n.method=e.method,n.url=e.action,n),w(t))}return e.onSuccess(w(t),t)},t.open(e.method,e.action,!0),e.withCredentials&&"withCredentials"in t&&(t.withCredentials=!0);var r=e.headers||{};return null!==r["X-Requested-With"]&&t.setRequestHeader("X-Requested-With","XMLHttpRequest"),Object.keys(r).forEach(function(e){null!==r[e]&&t.setRequestHeader(e,r[e])}),t.send(n),{abort:function(){t.abort()}}}var x=(t=(0,b.default)((0,g.default)().mark(function e(t,n){var a,i,l,o,s,u;return(0,g.default)().wrap(function(e){for(;;)switch(e.prev=e.next){case 0:o=function(){return(o=(0,b.default)((0,g.default)().mark(function e(t){return(0,g.default)().wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",new Promise(function(e){t.file(function(r){n(r)?(t.fullPath&&!r.webkitRelativePath&&(Object.defineProperties(r,{webkitRelativePath:{writable:!0}}),r.webkitRelativePath=t.fullPath.replace(/^\//,""),Object.defineProperties(r,{webkitRelativePath:{writable:!1}})),e(r)):e(null)})}));case 1:case"end":return e.stop()}},e)}))).apply(this,arguments)},l=function(){return(l=(0,b.default)((0,g.default)().mark(function e(t){var n,r,a,i,l;return(0,g.default)().wrap(function(e){for(;;)switch(e.prev=e.next){case 0:n=t.createReader(),r=[];case 2:return e.next=5,new Promise(function(e){n.readEntries(e,function(){return e([])})});case 5:if(i=(a=e.sent).length){e.next=9;break}return e.abrupt("break",12);case 9:for(l=0;l0||u.some(function(e){return"file"===e.kind}))&&(null==a||a()),!s){t.next=11;break}return t.next=7,x(Array.prototype.slice.call(u),function(t){return $(t,e.props.accept)});case 7:c=t.sent,e.uploadFiles(c),t.next=14;break;case 11:d=(0,r.default)(c).filter(function(e){return $(e,o)}),!1===l&&(d=c.slice(0,1)),e.uploadFiles(d);case 14:case"end":return t.stop()}},t)})),function(e,t){return n.apply(this,arguments)})),(0,p.default)((0,u.default)(e),"onFilePaste",(i=(0,b.default)((0,g.default)().mark(function t(n){var r;return(0,g.default)().wrap(function(t){for(;;)switch(t.prev=t.next){case 0:if(e.props.pastable){t.next=3;break}return t.abrupt("return");case 3:if("paste"!==n.type){t.next=6;break}return r=n.clipboardData,t.abrupt("return",e.onDataTransferFiles(r,function(){n.preventDefault()}));case 6:case"end":return t.stop()}},t)})),function(e){return i.apply(this,arguments)})),(0,p.default)((0,u.default)(e),"onFileDragOver",function(e){e.preventDefault()}),(0,p.default)((0,u.default)(e),"onFileDrop",(l=(0,b.default)((0,g.default)().mark(function t(n){var r;return(0,g.default)().wrap(function(t){for(;;)switch(t.prev=t.next){case 0:if(n.preventDefault(),"drop"!==n.type){t.next=4;break}return r=n.dataTransfer,t.abrupt("return",e.onDataTransferFiles(r));case 4:case"end":return t.stop()}},t)})),function(e){return l.apply(this,arguments)})),(0,p.default)((0,u.default)(e),"uploadFiles",function(t){var n=(0,r.default)(t);Promise.all(n.map(function(t){return t.uid=C(),e.processFile(t,n)})).then(function(t){var n=e.props.onBatchStart;null==n||n(t.map(function(e){return{file:e.origin,parsedFile:e.parsedFile}})),t.filter(function(e){return null!==e.parsedFile}).forEach(function(t){e.post(t)})})}),(0,p.default)((0,u.default)(e),"processFile",(s=(0,b.default)((0,g.default)().mark(function t(n,r){var a,i,l,o,s,u,c,d;return(0,g.default)().wrap(function(t){for(;;)switch(t.prev=t.next){case 0:if(a=e.props.beforeUpload,i=n,!a){t.next=14;break}return t.prev=3,t.next=6,a(n,r);case 6:i=t.sent,t.next=12;break;case 9:t.prev=9,t.t0=t.catch(3),i=!1;case 12:if(!1!==i){t.next=14;break}return t.abrupt("return",{origin:n,parsedFile:null,action:null,data:null});case 14:if("function"!=typeof(l=e.props.action)){t.next=21;break}return t.next=18,l(n);case 18:o=t.sent,t.next=22;break;case 21:o=l;case 22:if("function"!=typeof(s=e.props.data)){t.next=29;break}return t.next=26,s(n);case 26:u=t.sent,t.next=30;break;case 29:u=s;case 30:return(d=(c=("object"===(0,h.default)(i)||"string"==typeof i)&&i?i:n)instanceof File?c:new File([c],n.name,{type:n.type})).uid=n.uid,t.abrupt("return",{origin:n,data:u,parsedFile:d,action:o});case 35:case"end":return t.stop()}},t,null,[[3,9]])})),function(e,t){return s.apply(this,arguments)})),(0,p.default)((0,u.default)(e),"saveFileInput",function(t){e.fileInput=t}),e}return(0,s.default)(a,[{key:"componentDidMount",value:function(){this._isMounted=!0,this.props.pastable&&document.addEventListener("paste",this.onFilePaste)}},{key:"componentWillUnmount",value:function(){this._isMounted=!1,this.abort(),document.removeEventListener("paste",this.onFilePaste)}},{key:"componentDidUpdate",value:function(e){var t=this.props.pastable;t&&!e.pastable?document.addEventListener("paste",this.onFilePaste):!t&&e.pastable&&document.removeEventListener("paste",this.onFilePaste)}},{key:"post",value:function(e){var t=this,n=e.data,r=e.origin,a=e.action,i=e.parsedFile;if(this._isMounted){var l=this.props,o=l.onStart,s=l.customRequest,u=l.name,c=l.headers,d=l.withCredentials,p=l.method,f=r.uid,m=s||E;o(r),this.reqs[f]=m({action:a,filename:u,data:n,file:i,headers:c,withCredentials:d,method:p||"post",onProgress:function(e){var n=t.props.onProgress;null==n||n(e,i)},onSuccess:function(e,n){var r=t.props.onSuccess;null==r||r(e,i,n),delete t.reqs[f]},onError:function(e,n){var r=t.props.onError;null==r||r(e,n,i),delete t.reqs[f]}},{defaultRequest:E})}}},{key:"reset",value:function(){this.setState({uid:C()})}},{key:"abort",value:function(e){var t=this.reqs;if(e){var n=e.uid?e.uid:e;t[n]&&t[n].abort&&t[n].abort(),delete t[n]}else Object.keys(t).forEach(function(e){t[e]&&t[e].abort&&t[e].abort(),delete t[e]})}},{key:"render",value:function(){var e=this.props,t=e.component,r=e.prefixCls,a=e.className,o=e.classNames,s=e.disabled,u=e.id,c=e.name,d=e.style,h=e.styles,g=e.multiple,b=e.accept,y=e.capture,$=e.children,w=e.directory,E=e.folder,x=e.openFileDialogOnClick,k=e.onMouseEnter,O=e.onMouseLeave,C=e.hasControlInside,j=(0,m.default)(e,S),D=(0,i.default)((0,p.default)((0,p.default)((0,p.default)({},r,!0),"".concat(r,"-disabled"),s),a,a)),F=s?{}:{onClick:x?this.onClick:function(){},onKeyDown:x?this.onKeyDown:function(){},onMouseEnter:k,onMouseLeave:O,onDrop:this.onFileDrop,onDragOver:this.onFileDragOver,tabIndex:C?void 0:"0"};return n.default.createElement(t,(0,l.default)({},F,{className:D,role:C?void 0:"button",style:d}),n.default.createElement("input",(0,l.default)({},(0,v.default)(j,{aria:!0,data:!0}),{id:u,name:c,disabled:s,type:"file",ref:this.saveFileInput,onClick:function(e){return e.stopPropagation()},key:this.state.uid,style:(0,f.default)({display:"none"},(void 0===h?{}:h).input),className:(void 0===o?{}:o).input,accept:b},w||E?{directory:"directory",webkitdirectory:"webkitdirectory"}:{},{multiple:g,onChange:this.onChange},null!=y?{capture:y}:{})),$)}}]),a}(n.Component);function D(){}var F=function(e){(0,c.default)(r,e);var t=(0,d.default)(r);function r(){var e;(0,o.default)(this,r);for(var n=arguments.length,a=Array(n),i=0;i{let{fontSizeHeading3:t,fontHeight:n,lineWidth:r,pictureCardSize:a,calc:i}=e,l=(0,A.mergeToken)(e,{uploadThumbnailSize:i(t).mul(2).equal(),uploadProgressOffset:i(i(n).div(2)).add(r).equal(),uploadPicCardSize:a});return[(e=>{let{componentCls:t,colorTextDisabled:n}=e;return{[`${t}-wrapper`]:Object.assign(Object.assign({},(0,M.resetComponent)(e)),{[t]:{outline:0,"input[type='file']":{cursor:"pointer"}},[`${t}-select`]:{display:"inline-block"},[`${t}-hidden`]:{display:"none"},[`${t}-disabled`]:{color:n,cursor:"not-allowed"}})}})(l),(e=>{let{componentCls:t,iconCls:n}=e;return{[`${t}-wrapper`]:{[`${t}-drag`]:{position:"relative",width:"100%",height:"100%",textAlign:"center",background:e.colorFillAlter,border:`${(0,q.unit)(e.lineWidth)} dashed ${e.colorBorder}`,borderRadius:e.borderRadiusLG,cursor:"pointer",transition:`border-color ${e.motionDurationSlow}`,[t]:{padding:e.padding},[`${t}-btn`]:{display:"table",width:"100%",height:"100%",outline:"none",borderRadius:e.borderRadiusLG,"&:focus-visible":{outline:`${(0,q.unit)(e.lineWidthFocus)} solid ${e.colorPrimaryBorder}`}},[`${t}-drag-container`]:{display:"table-cell",verticalAlign:"middle"},[` + &:not(${t}-disabled):hover, + &-hover:not(${t}-disabled) + `]:{borderColor:e.colorPrimaryHover},[`p${t}-drag-icon`]:{marginBottom:e.margin,[n]:{color:e.colorPrimary,fontSize:e.uploadThumbnailSize}},[`p${t}-text`]:{margin:`0 0 ${(0,q.unit)(e.marginXXS)}`,color:e.colorTextHeading,fontSize:e.fontSizeLG},[`p${t}-hint`]:{color:e.colorTextDescription,fontSize:e.fontSize},[`&${t}-disabled`]:{[`p${t}-drag-icon ${n}, + p${t}-text, + p${t}-hint + `]:{color:e.colorTextDisabled}}}}}})(l),(e=>{let{componentCls:t,iconCls:n,uploadThumbnailSize:r,uploadProgressOffset:a,calc:i}=e,l=`${t}-list`,o=`${l}-item`;return{[`${t}-wrapper`]:{[` + ${l}${l}-picture, + ${l}${l}-picture-card, + ${l}${l}-picture-circle + `]:{[o]:{position:"relative",height:i(r).add(i(e.lineWidth).mul(2)).add(i(e.paddingXS).mul(2)).equal(),padding:e.paddingXS,border:`${(0,q.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadiusLG,"&:hover":{background:"transparent"},[`${o}-thumbnail`]:Object.assign(Object.assign({},M.textEllipsis),{width:r,height:r,lineHeight:(0,q.unit)(i(r).add(e.paddingSM).equal()),textAlign:"center",flex:"none",[n]:{fontSize:e.fontSizeHeading2,color:e.colorPrimary},img:{display:"block",width:"100%",height:"100%",overflow:"hidden"}}),[`${o}-progress`]:{bottom:a,width:`calc(100% - ${(0,q.unit)(i(e.paddingSM).mul(2).equal())})`,marginTop:0,paddingInlineStart:i(r).add(e.paddingXS).equal()}},[`${o}-error`]:{borderColor:e.colorError,[`${o}-thumbnail ${n}`]:{[`svg path[fill='${X.blue[0]}']`]:{fill:e.colorErrorBg},[`svg path[fill='${X.blue.primary}']`]:{fill:e.colorError}}},[`${o}-uploading`]:{borderStyle:"dashed",[`${o}-name`]:{marginBottom:a}}},[`${l}${l}-picture-circle ${o}`]:{[`&, &::before, ${o}-thumbnail`]:{borderRadius:"50%"}}}}})(l),(e=>{let{componentCls:t,iconCls:n,fontSizeLG:r,colorTextLightSolid:a,calc:i}=e,l=`${t}-list`,o=`${l}-item`,s=e.uploadPicCardSize;return{[` + ${t}-wrapper${t}-picture-card-wrapper, + ${t}-wrapper${t}-picture-circle-wrapper + `]:Object.assign(Object.assign({},(0,M.clearFix)()),{display:"block",[`${t}${t}-select`]:{width:s,height:s,textAlign:"center",verticalAlign:"top",backgroundColor:e.colorFillAlter,border:`${(0,q.unit)(e.lineWidth)} dashed ${e.colorBorder}`,borderRadius:e.borderRadiusLG,cursor:"pointer",transition:`border-color ${e.motionDurationSlow}`,[`> ${t}`]:{display:"flex",alignItems:"center",justifyContent:"center",height:"100%",textAlign:"center"},[`&:not(${t}-disabled):hover`]:{borderColor:e.colorPrimary}},[`${l}${l}-picture-card, ${l}${l}-picture-circle`]:{display:"flex",flexWrap:"wrap","@supports not (gap: 1px)":{"& > *":{marginBlockEnd:e.marginXS,marginInlineEnd:e.marginXS}},"@supports (gap: 1px)":{gap:e.marginXS},[`${l}-item-container`]:{display:"inline-block",width:s,height:s,verticalAlign:"top"},"&::after":{display:"none"},"&::before":{display:"none"},[o]:{height:"100%",margin:0,"&::before":{position:"absolute",zIndex:1,width:`calc(100% - ${(0,q.unit)(i(e.paddingXS).mul(2).equal())})`,height:`calc(100% - ${(0,q.unit)(i(e.paddingXS).mul(2).equal())})`,backgroundColor:e.colorBgMask,opacity:0,transition:`all ${e.motionDurationSlow}`,content:'" "'}},[`${o}:hover`]:{[`&::before, ${o}-actions`]:{opacity:1}},[`${o}-actions`]:{position:"absolute",insetInlineStart:0,zIndex:10,width:"100%",whiteSpace:"nowrap",textAlign:"center",opacity:0,transition:`all ${e.motionDurationSlow}`,[` + ${n}-eye, + ${n}-download, + ${n}-delete + `]:{zIndex:10,width:r,margin:`0 ${(0,q.unit)(e.marginXXS)}`,fontSize:r,cursor:"pointer",transition:`all ${e.motionDurationSlow}`,color:a,"&:hover":{color:a},svg:{verticalAlign:"baseline"}}},[`${o}-thumbnail, ${o}-thumbnail img`]:{position:"static",display:"block",width:"100%",height:"100%",objectFit:"contain"},[`${o}-name`]:{display:"none",textAlign:"center"},[`${o}-file + ${o}-name`]:{position:"absolute",bottom:e.margin,display:"block",width:`calc(100% - ${(0,q.unit)(i(e.paddingXS).mul(2).equal())})`},[`${o}-uploading`]:{[`&${o}`]:{backgroundColor:e.colorFillAlter},[`&::before, ${n}-eye, ${n}-download, ${n}-delete`]:{display:"none"}},[`${o}-progress`]:{bottom:e.marginXL,width:`calc(100% - ${(0,q.unit)(i(e.paddingXS).mul(2).equal())})`,paddingInlineStart:0}}}),[`${t}-wrapper${t}-picture-circle-wrapper`]:{[`${t}${t}-select`]:{borderRadius:"50%"}}}})(l),(e=>{let{componentCls:t,iconCls:n,fontSize:r,lineHeight:a,calc:i}=e,l=`${t}-list-item`,o=`${l}-actions`,s=`${l}-action`;return{[`${t}-wrapper`]:{[`${t}-list`]:Object.assign(Object.assign({},(0,M.clearFix)()),{lineHeight:e.lineHeight,[l]:{position:"relative",height:i(e.lineHeight).mul(r).equal(),marginTop:e.marginXS,fontSize:r,display:"flex",alignItems:"center",transition:`background-color ${e.motionDurationSlow}`,borderRadius:e.borderRadiusSM,"&:hover":{backgroundColor:e.controlItemBgHover},[`${l}-name`]:Object.assign(Object.assign({},M.textEllipsis),{padding:`0 ${(0,q.unit)(e.paddingXS)}`,lineHeight:a,flex:"auto",transition:`all ${e.motionDurationSlow}`}),[o]:{whiteSpace:"nowrap",[s]:{opacity:0},[n]:{color:e.actionsColor,transition:`all ${e.motionDurationSlow}`},[` + ${s}:focus-visible, + &.picture ${s} + `]:{opacity:1}},[`${t}-icon ${n}`]:{color:e.colorIcon,fontSize:r},[`${l}-progress`]:{position:"absolute",bottom:e.calc(e.uploadProgressOffset).mul(-1).equal(),width:"100%",paddingInlineStart:i(r).add(e.paddingXS).equal(),fontSize:r,lineHeight:0,pointerEvents:"none","> div":{margin:0}}},[`${l}:hover ${s}`]:{opacity:1},[`${l}-error`]:{color:e.colorError,[`${l}-name, ${t}-icon ${n}`]:{color:e.colorError},[o]:{[`${n}, ${n}:hover`]:{color:e.colorError},[s]:{opacity:1}}},[`${t}-list-item-container`]:{transition:`opacity ${e.motionDurationSlow}, height ${e.motionDurationSlow}`,"&::before":{display:"table",width:0,height:0,content:'""'}}})}}})(l),(e=>{let{componentCls:t}=e,n=new T.Keyframes("uploadAnimateInlineIn",{from:{width:0,height:0,padding:0,opacity:0,margin:e.calc(e.marginXS).div(-2).equal()}}),r=new T.Keyframes("uploadAnimateInlineOut",{to:{width:0,height:0,padding:0,opacity:0,margin:e.calc(e.marginXS).div(-2).equal()}}),a=`${t}-animate-inline`;return[{[`${t}-wrapper`]:{[`${a}-appear, ${a}-enter, ${a}-leave`]:{animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseInOutCirc,animationFillMode:"forwards"},[`${a}-appear, ${a}-enter`]:{animationName:n},[`${a}-leave`]:{animationName:r}}},{[`${t}-wrapper`]:(0,H.initFadeMotion)(e)},n,r]})(l),(e=>{let{componentCls:t}=e;return{[`${t}-rtl`]:{direction:"rtl"}}})(l),(0,z.genCollapseMotion)(l)]},e=>({actionsColor:e.colorIcon,pictureCardSize:2.55*e.controlHeightLG})),B={icon:function(e,t){return{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M534 352V136H232v752h560V394H576a42 42 0 01-42-42z",fill:t}},{tag:"path",attrs:{d:"M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM602 137.8L790.2 326H602V137.8zM792 888H232V136h302v216a42 42 0 0042 42h216v494z",fill:e}}]}},name:"file",theme:"twotone"};var W=e.i(9583),V=n.forwardRef(function(e,t){return n.createElement(W.default,(0,l.default)({},e,{ref:t,icon:B}))}),G=e.i(739295);let K={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M779.3 196.6c-94.2-94.2-247.6-94.2-341.7 0l-261 260.8c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l261-260.8c32.4-32.4 75.5-50.2 121.3-50.2s88.9 17.8 121.2 50.2c32.4 32.4 50.2 75.5 50.2 121.2 0 45.8-17.8 88.8-50.2 121.2l-266 265.9-43.1 43.1c-40.3 40.3-105.8 40.3-146.1 0-19.5-19.5-30.2-45.4-30.2-73s10.7-53.5 30.2-73l263.9-263.8c6.7-6.6 15.5-10.3 24.9-10.3h.1c9.4 0 18.1 3.7 24.7 10.3 6.7 6.7 10.3 15.5 10.3 24.9 0 9.3-3.7 18.1-10.3 24.7L372.4 653c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l215.6-215.6c19.9-19.9 30.8-46.3 30.8-74.4s-11-54.6-30.8-74.4c-41.1-41.1-107.9-41-149 0L463 364 224.8 602.1A172.22 172.22 0 00174 724.8c0 46.3 18.1 89.8 50.8 122.5 33.9 33.8 78.3 50.7 122.7 50.7 44.4 0 88.8-16.9 122.6-50.7l309.2-309C824.8 492.7 850 432 850 367.5c.1-64.6-25.1-125.3-70.7-170.9z"}}]},name:"paper-clip",theme:"outlined"};var J=n.forwardRef(function(e,t){return n.createElement(W.default,(0,l.default)({},e,{ref:t,icon:K}))});e.s(["default",0,J],955719);let Q={icon:function(e,t){return{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32zm-40 632H136v-39.9l138.5-164.3 150.1 178L658.1 489 888 761.6V792zm0-129.8L664.2 396.8c-3.2-3.8-9-3.8-12.2 0L424.6 666.4l-144-170.7c-3.2-3.8-9-3.8-12.2 0L136 652.7V232h752v430.2z",fill:e}},{tag:"path",attrs:{d:"M424.6 765.8l-150.1-178L136 752.1V792h752v-30.4L658.1 489z",fill:t}},{tag:"path",attrs:{d:"M136 652.7l132.4-157c3.2-3.8 9-3.8 12.2 0l144 170.7L652 396.8c3.2-3.8 9-3.8 12.2 0L888 662.2V232H136v420.7zM304 280a88 88 0 110 176 88 88 0 010-176z",fill:t}},{tag:"path",attrs:{d:"M276 368a28 28 0 1056 0 28 28 0 10-56 0z",fill:t}},{tag:"path",attrs:{d:"M304 456a88 88 0 100-176 88 88 0 000 176zm0-116c15.5 0 28 12.5 28 28s-12.5 28-28 28-28-12.5-28-28 12.5-28 28-28z",fill:e}}]}},name:"picture",theme:"twotone"};var Y=n.forwardRef(function(e,t){return n.createElement(W.default,(0,l.default)({},e,{ref:t,icon:Q}))}),Z=e.i(361275),ee=e.i(629587),et=e.i(529681),en=e.i(149809),er=e.i(613541),ea=e.i(763731),ei=e.i(920228);function el(e){return Object.assign(Object.assign({},e),{lastModified:e.lastModified,lastModifiedDate:e.lastModifiedDate,name:e.name,size:e.size,type:e.type,uid:e.uid,percent:0,originFileObj:e})}function eo(e,t){let n=(0,r.default)(t),a=n.findIndex(({uid:t})=>t===e.uid);return -1===a?n.push(e):n[a]=e,n}function es(e,t){let n=void 0!==e.uid?"uid":"name";return t.filter(t=>t[n]===e[n])[0]}let eu=e=>0===e.indexOf("image/"),ec=e=>{if(e.type&&!e.thumbUrl)return eu(e.type);let t=e.thumbUrl||e.url||"",n=((e="")=>{let t=e.split("/"),n=t[t.length-1].split(/#|\?/)[0];return(/\.[^./\\]*$/.exec(n)||[""])[0]})(t);return!!(/^data:image\//.test(t)||/(webp|svg|png|gif|jpg|jpeg|jfif|bmp|dpg|ico|heic|heif)$/i.test(n))||!/^data:/.test(t)&&!n};function ed(e){return new Promise(t=>{if(!e.type||!eu(e.type))return void t("");let n=document.createElement("canvas");n.width=200,n.height=200,n.style.cssText="position: fixed; left: 0; top: 0; width: 200px; height: 200px; z-index: 9999; display: none;",document.body.appendChild(n);let r=n.getContext("2d"),a=new Image;if(a.onload=()=>{let{width:e,height:i}=a,l=200,o=200,s=0,u=0;e>i?u=-((o=200/e*i)-l)/2:s=-((l=200/i*e)-o)/2,r.drawImage(a,s,u,l,o);let c=n.toDataURL();document.body.removeChild(n),window.URL.revokeObjectURL(a.src),t(c)},a.crossOrigin="anonymous",e.type.startsWith("image/svg+xml")){let t=new FileReader;t.onload=()=>{t.result&&"string"==typeof t.result&&(a.src=t.result)},t.readAsDataURL(e)}else if(e.type.startsWith("image/gif")){let n=new FileReader;n.onload=()=>{n.result&&t(n.result)},n.readAsDataURL(e)}else a.src=window.URL.createObjectURL(e)})}var ep=e.i(597440),ef=e.i(184163),em=e.i(984125),eh=e.i(309821),eg=e.i(491816);let eb=n.forwardRef(({prefixCls:e,className:t,style:r,locale:a,listType:l,file:o,items:s,progress:u,iconRender:c,actionIconRender:d,itemRender:p,isImgUrl:f,showPreviewIcon:m,showRemoveIcon:h,showDownloadIcon:g,previewIcon:b,removeIcon:v,downloadIcon:y,extra:$,onPreview:w,onDownload:E,onClose:x},k)=>{var O,C;let{status:S}=o,[j,D]=n.useState(S);n.useEffect(()=>{"removed"!==S&&D(S)},[S]);let[F,R]=n.useState(!1);n.useEffect(()=>{let e=setTimeout(()=>{R(!0)},300);return()=>{clearTimeout(e)}},[]);let P=c(o),N=n.createElement("div",{className:`${e}-icon`},P);if("picture"===l||"picture-card"===l||"picture-circle"===l)if("uploading"!==j&&(o.thumbUrl||o.url)){let t=(null==f?void 0:f(o))?n.createElement("img",{src:o.thumbUrl||o.url,alt:o.name,className:`${e}-list-item-image`,crossOrigin:o.crossOrigin}):P,r=(0,i.default)(`${e}-list-item-thumbnail`,{[`${e}-list-item-file`]:f&&!f(o)});N=n.createElement("a",{className:r,onClick:e=>w(o,e),href:o.url||o.thumbUrl,target:"_blank",rel:"noopener noreferrer"},t)}else{let t=(0,i.default)(`${e}-list-item-thumbnail`,{[`${e}-list-item-file`]:"uploading"!==j});N=n.createElement("div",{className:t},P)}let L=(0,i.default)(`${e}-list-item`,`${e}-list-item-${j}`),M="string"==typeof o.linkProps?JSON.parse(o.linkProps):o.linkProps,z=("function"==typeof h?h(o):h)?d(("function"==typeof v?v(o):v)||n.createElement(ep.default,null),()=>x(o),e,a.removeFile,!0):null,U=("function"==typeof g?g(o):g)&&"done"===j?d(("function"==typeof y?y(o):y)||n.createElement(ef.default,null),()=>E(o),e,a.downloadFile):null,A="picture-card"!==l&&"picture-circle"!==l&&n.createElement("span",{key:"download-delete",className:(0,i.default)(`${e}-list-item-actions`,{picture:"picture"===l})},U,z),q="function"==typeof $?$(o):$,T=q&&n.createElement("span",{className:`${e}-list-item-extra`},q),H=(0,i.default)(`${e}-list-item-name`),X=o.url?n.createElement("a",Object.assign({key:"view",target:"_blank",rel:"noopener noreferrer",className:H,title:o.name},M,{href:o.url,onClick:e=>w(o,e)}),o.name,T):n.createElement("span",{key:"view",className:H,onClick:e=>w(o,e),title:o.name},o.name,T),_=("function"==typeof m?m(o):m)&&(o.url||o.thumbUrl)?n.createElement("a",{href:o.url||o.thumbUrl,target:"_blank",rel:"noopener noreferrer",onClick:e=>w(o,e),title:a.previewFile},"function"==typeof b?b(o):b||n.createElement(em.default,null)):null,B=("picture-card"===l||"picture-circle"===l)&&"uploading"!==j&&n.createElement("span",{className:`${e}-list-item-actions`},_,"done"===j&&U,z),{getPrefixCls:W}=n.useContext(I.ConfigContext),V=W(),G=n.createElement("div",{className:L},N,X,A,B,F&&n.createElement(Z.default,{motionName:`${V}-fade`,visible:"uploading"===j,motionDeadline:2e3},({className:t})=>{let r="percent"in o?n.createElement(eh.default,Object.assign({type:"line",percent:o.percent,"aria-label":o["aria-label"],"aria-labelledby":o["aria-labelledby"]},u)):null;return n.createElement("div",{className:(0,i.default)(`${e}-list-item-progress`,t)},r)})),K=o.response&&"string"==typeof o.response?o.response:(null==(O=o.error)?void 0:O.statusText)||(null==(C=o.error)?void 0:C.message)||a.uploadError,J="error"===j?n.createElement(eg.default,{title:K,getPopupContainer:e=>e.parentNode},G):G;return n.createElement("div",{className:(0,i.default)(`${e}-list-item-container`,t),style:r,ref:k},p?p(J,o,s,{download:E.bind(null,o),preview:w.bind(null,o),remove:x.bind(null,o)}):J)}),ev=n.forwardRef((e,t)=>{let{listType:a="text",previewFile:l=ed,onPreview:o,onDownload:s,onRemove:u,locale:c,iconRender:d,isImageUrl:p=ec,prefixCls:f,items:m=[],showPreviewIcon:h=!0,showRemoveIcon:g=!0,showDownloadIcon:b=!1,removeIcon:v,previewIcon:y,downloadIcon:$,extra:w,progress:E={size:[-1,2],showInfo:!1},appendAction:x,appendActionVisible:k=!0,itemRender:O,disabled:C}=e,[,S]=(0,en.useForceUpdate)(),[j,D]=n.useState(!1),F=["picture-card","picture-circle"].includes(a);n.useEffect(()=>{a.startsWith("picture")&&(m||[]).forEach(e=>{(e.originFileObj instanceof File||e.originFileObj instanceof Blob)&&void 0===e.thumbUrl&&(e.thumbUrl="",null==l||l(e.originFileObj).then(t=>{e.thumbUrl=t||"",S()}))})},[a,m,l]),n.useEffect(()=>{D(!0)},[]);let R=(e,t)=>{if(o)return null==t||t.preventDefault(),o(e)},P=e=>{"function"==typeof s?s(e):e.url&&window.open(e.url)},N=e=>{null==u||u(e)},L=e=>{if(d)return d(e,a);let t="uploading"===e.status;if(a.startsWith("picture")){let r="picture"===a?n.createElement(G.default,null):c.uploading,i=(null==p?void 0:p(e))?n.createElement(Y,null):n.createElement(V,null);return t?r:i}return t?n.createElement(G.default,null):n.createElement(J,null)},M=(e,t,r,a,i)=>{let l={type:"text",size:"small",title:a,onClick:r=>{var a,i;t(),n.isValidElement(e)&&(null==(i=(a=e.props).onClick)||i.call(a,r))},className:`${r}-list-item-action`,disabled:!!i&&C};return n.isValidElement(e)?n.createElement(ei.default,Object.assign({},l,{icon:(0,ea.cloneElement)(e,Object.assign(Object.assign({},e.props),{onClick:()=>{}}))})):n.createElement(ei.default,Object.assign({},l),n.createElement("span",null,e))};n.useImperativeHandle(t,()=>({handlePreview:R,handleDownload:P}));let{getPrefixCls:z}=n.useContext(I.ConfigContext),U=z("upload",f),A=z(),q=(0,i.default)(`${U}-list`,`${U}-list-${a}`),T=n.useMemo(()=>(0,et.default)((0,er.default)(A),["onAppearEnd","onEnterEnd","onLeaveEnd"]),[A]),H=Object.assign(Object.assign({},F?{}:T),{motionDeadline:2e3,motionName:`${U}-${F?"animate-inline":"animate"}`,keys:(0,r.default)(m.map(e=>({key:e.uid,file:e}))),motionAppear:j});return n.createElement("div",{className:q},n.createElement(ee.CSSMotionList,Object.assign({},H,{component:!1}),({key:e,file:t,className:r,style:i})=>n.createElement(eb,{key:e,locale:c,prefixCls:U,className:r,style:i,file:t,items:m,progress:E,listType:a,isImgUrl:p,showPreviewIcon:h,showRemoveIcon:g,showDownloadIcon:b,removeIcon:v,previewIcon:y,downloadIcon:$,extra:w,iconRender:L,actionIconRender:M,itemRender:O,onPreview:R,onDownload:P,onClose:N})),x&&n.createElement(Z.default,Object.assign({},H,{visible:k,forceRender:!0}),({className:e,style:t})=>(0,ea.cloneElement)(x,n=>({className:(0,i.default)(n.className,e),style:Object.assign(Object.assign(Object.assign({},t),{pointerEvents:e?"none":void 0}),n.style)}))))}),ey=`__LIST_IGNORE_${Date.now()}__`,e$=n.forwardRef((e,t)=>{let l=(0,I.useComponentConfig)("upload"),{fileList:o,defaultFileList:s,onRemove:u,showUploadList:c=!0,listType:d="text",onPreview:p,onDownload:f,onChange:m,onDrop:h,previewFile:g,disabled:b,locale:v,iconRender:y,isImageUrl:$,progress:w,prefixCls:E,className:x,type:k="select",children:O,style:C,itemRender:S,maxCount:j,data:D={},multiple:M=!1,hasControlInside:z=!0,action:U="",accept:A="",supportServerRender:q=!0,rootClassName:T}=e,H=n.useContext(P.default),X=null!=b?b:H,B=e.customRequest||l.customRequest,[W,V]=(0,R.default)(s||[],{value:o,postState:e=>null!=e?e:[]}),[G,K]=n.useState("drop"),J=n.useRef(null),Q=n.useRef(null);n.useMemo(()=>{let e=Date.now();(o||[]).forEach((t,n)=>{t.uid||Object.isFrozen(t)||(t.uid=`__AUTO__${e}_${n}__`)})},[o]);let Y=(e,t,n)=>{let i=(0,r.default)(t),l=!1;1===j?i=i.slice(-1):j&&(l=i.length>j,i=i.slice(0,j)),(0,a.flushSync)(()=>{V(i)});let o={file:e,fileList:i};n&&(o.event=n),(!l||"removed"===e.status||i.some(t=>t.uid===e.uid))&&(0,a.flushSync)(()=>{null==m||m(o)})},Z=e=>{let t=e.filter(e=>!e.file[ey]);if(!t.length)return;let n=t.map(e=>el(e.file)),a=(0,r.default)(W);n.forEach(e=>{a=eo(e,a)}),n.forEach((e,n)=>{let r=e;if(t[n].parsedFile)e.status="uploading";else{let t,{originFileObj:n}=e;try{t=new File([n],n.name,{type:n.type})}catch(e){(t=new Blob([n],{type:n.type})).name=n.name,t.lastModifiedDate=new Date,t.lastModified=new Date().getTime()}t.uid=e.uid,r=t}Y(r,a)})},ee=(e,t,n)=>{try{"string"==typeof e&&(e=JSON.parse(e))}catch(e){}if(!es(t,W))return;let r=el(t);r.status="done",r.percent=100,r.response=e,r.xhr=n;let a=eo(r,W);Y(r,a)},et=(e,t)=>{if(!es(t,W))return;let n=el(t);n.status="uploading",n.percent=e.percent;let r=eo(n,W);Y(n,r,e)},en=(e,t,n)=>{if(!es(n,W))return;let r=el(n);r.error=e,r.response=t,r.status="error";let a=eo(r,W);Y(r,a)},er=e=>{let t;Promise.resolve("function"==typeof u?u(e):u).then(n=>{var r;let a,i;if(!1===n)return;let l=(a=void 0!==e.uid?"uid":"name",(i=W.filter(t=>t[a]!==e[a])).length===W.length?null:i);l&&(t=Object.assign(Object.assign({},e),{status:"removed"}),null==W||W.forEach(e=>{let n=void 0!==t.uid?"uid":"name";e[n]!==t[n]||Object.isFrozen(e)||(e.status="removed")}),null==(r=J.current)||r.abort(t),Y(t,l))})},ea=e=>{K(e.type),"drop"===e.type&&(null==h||h(e))};n.useImperativeHandle(t,()=>({onBatchStart:Z,onSuccess:ee,onProgress:et,onError:en,fileList:W,upload:J.current,nativeElement:Q.current}));let{getPrefixCls:ei,direction:eu,upload:ec}=n.useContext(I.ConfigContext),ed=ei("upload",E),ep=Object.assign(Object.assign({onBatchStart:Z,onError:en,onProgress:et,onSuccess:ee},e),{customRequest:B,data:D,multiple:M,action:U,accept:A,supportServerRender:q,prefixCls:ed,disabled:X,beforeUpload:(t,n)=>{var r,a,i,l;return r=void 0,a=void 0,i=void 0,l=function*(){let{beforeUpload:r,transformFile:a}=e,i=t;if(r){let e=yield r(t,n);if(!1===e)return!1;if(delete t[ey],e===ey)return Object.defineProperty(t,ey,{value:!0,configurable:!0}),!1;"object"==typeof e&&e&&(i=e)}return a&&(i=yield a(i)),i},new(i||(i=Promise))(function(e,t){function n(e){try{s(l.next(e))}catch(e){t(e)}}function o(e){try{s(l.throw(e))}catch(e){t(e)}}function s(t){var r;t.done?e(t.value):((r=t.value)instanceof i?r:new i(function(e){e(r)})).then(n,o)}s((l=l.apply(r,a||[])).next())})},onChange:void 0,hasControlInside:z});delete ep.className,delete ep.style,(!O||X)&&delete ep.id;let ef=`${ed}-wrapper`,[em,eh,eg]=_(ed,ef),[eb]=(0,N.useLocale)("Upload",L.default.Upload),{showRemoveIcon:e$,showPreviewIcon:ew,showDownloadIcon:eE,removeIcon:ex,previewIcon:ek,downloadIcon:eO,extra:eC}="boolean"==typeof c?{}:c,eS=void 0===e$?!X:e$,ej=(e,t)=>c?n.createElement(ev,{prefixCls:ed,listType:d,items:W,previewFile:g,onPreview:p,onDownload:f,onRemove:er,showRemoveIcon:eS,showPreviewIcon:ew,showDownloadIcon:eE,removeIcon:ex,previewIcon:ek,downloadIcon:eO,iconRender:y,extra:eC,locale:Object.assign(Object.assign({},eb),v),isImageUrl:$,progress:w,appendAction:e,appendActionVisible:t,itemRender:S,disabled:X}):e,eD=(0,i.default)(ef,x,T,eh,eg,null==ec?void 0:ec.className,{[`${ed}-rtl`]:"rtl"===eu,[`${ed}-picture-card-wrapper`]:"picture-card"===d,[`${ed}-picture-circle-wrapper`]:"picture-circle"===d}),eF=Object.assign(Object.assign({},null==ec?void 0:ec.style),C);if("drag"===k){let e=(0,i.default)(eh,ed,`${ed}-drag`,{[`${ed}-drag-uploading`]:W.some(e=>"uploading"===e.status),[`${ed}-drag-hover`]:"dragover"===G,[`${ed}-disabled`]:X,[`${ed}-rtl`]:"rtl"===eu});return em(n.createElement("span",{className:eD,ref:Q},n.createElement("div",{className:e,style:eF,onDrop:ea,onDragOver:ea,onDragLeave:ea},n.createElement(F,Object.assign({},ep,{ref:J,className:`${ed}-btn`}),n.createElement("div",{className:`${ed}-drag-container`},O))),ej()))}let eR=(0,i.default)(ed,`${ed}-select`,{[`${ed}-disabled`]:X,[`${ed}-hidden`]:!O}),eI=n.createElement("div",{className:eR,style:eF},n.createElement(F,Object.assign({},ep,{ref:J})));return em("picture-card"===d||"picture-circle"===d?n.createElement("span",{className:eD,ref:Q},ej(eI,!!O)):n.createElement("span",{className:eD,ref:Q},eI,ej()))});var ew=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,r=Object.getOwnPropertySymbols(e);at.indexOf(r[a])&&Object.prototype.propertyIsEnumerable.call(e,r[a])&&(n[r[a]]=e[r[a]]);return n};let eE=n.forwardRef((e,t)=>{let{style:r,height:a,hasControlInside:i=!1,children:l}=e,o=ew(e,["style","height","hasControlInside","children"]),s=Object.assign(Object.assign({},r),{height:a});return n.createElement(e$,Object.assign({ref:t,hasControlInside:i},o,{style:s,type:"drag"}),l)});e$.Dragger=eE,e$.LIST_IGNORE=ey,e.s(["Upload",0,e$],515831)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0t50t_0rum~ur.js b/litellm/proxy/_experimental/out/_next/static/chunks/0t50t_0rum~ur.js new file mode 100644 index 00000000000..66753a87984 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0t50t_0rum~ur.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,207082,e=>{"use strict";var t=e.i(619273),l=e.i(266027),s=e.i(243652),a=e.i(602869),r=e.i(431703),i=e.i(135214);let n=(0,s.createQueryKeys)("keys"),o=async(e,t,l,s={})=>{try{let i=(0,a.getProxyBaseUrl)(),n=new URLSearchParams(Object.entries({team_id:s.teamID,project_id:s.projectID,agent_id:s.agentID,organization_id:s.organizationID,key_alias:s.selectedKeyAlias,key_hash:s.keyHash,user_id:s.userID,page:t,size:l,sort_by:s.sortBy,sort_order:s.sortOrder,expand:s.expand,status:s.status,return_full_object:"true",include_team_keys:"true",include_created_by_keys:"true",substring_matching:"true"}).filter(([,e])=>null!=e).map(([e,t])=>[e,String(t)])),o=`${i?`${i}/key/list`:"/key/list"}?${n}`,d=await fetch(o,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!d.ok){let e=await d.json(),t=(0,r.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return await d.json()}catch(e){throw console.error("Failed to list keys:",e),e}},d=(0,s.createQueryKeys)("deletedKeys");e.s(["keyKeys",0,n,"useDeletedKeys",0,(e,s,a={})=>{let{accessToken:r}=(0,i.default)();return(0,l.useQuery)({queryKey:d.list({page:e,limit:s,...a}),queryFn:async()=>await o(r,e,s,{...a,status:"deleted"}),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})},"useKeys",0,(e,s,a={})=>{let{accessToken:r}=(0,i.default)();return(0,l.useQuery)({queryKey:n.list({page:e,limit:s,...a}),queryFn:async()=>await o(r,e,s,a),enabled:!!r,staleTime:3e4,placeholderData:t.keepPreviousData})}])},510674,e=>{"use strict";var t=e.i(266027),l=e.i(243652),s=e.i(602869),a=e.i(431703),r=e.i(135214),i=e.i(708347);let n=(0,l.createQueryKeys)("projects"),o=[...i.all_admin_roles,...i.internalUserRoles],d=async e=>{let t=(0,s.getProxyBaseUrl)(),l=`${t}/project/list`,r=await fetch(l,{method:"GET",headers:{[(0,s.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!r.ok){let e=await r.json(),t=(0,a.deriveErrorMessage)(e);throw(0,s.handleError)(t),Error(t)}return r.json()};e.s(["projectKeys",0,n,"useProjects",0,()=>{let{accessToken:e,userRole:l}=(0,r.default)();return(0,t.useQuery)({queryKey:n.list({}),queryFn:async()=>d(e),enabled:!!e&&o.includes(l)})}])},557662,e=>{"use strict";let t="/ui/assets/logos/",l=[{id:"arize",displayName:"Arize",logo:`${t}arize.png`,supports_key_team_logging:!0,dynamic_params:{arize_api_key:"password",arize_space_id:"password"},description:"Arize Logging Integration"},{id:"braintrust",displayName:"Braintrust",logo:`${t}braintrust.png`,supports_key_team_logging:!1,dynamic_params:{braintrust_api_key:"password",braintrust_project_name:"text"},description:"Braintrust Logging Integration"},{id:"custom_callback_api",displayName:"Custom Callback API",logo:`${t}custom.svg`,supports_key_team_logging:!0,dynamic_params:{custom_callback_api_url:"text",custom_callback_api_headers:"text"},description:"Custom Callback API Logging Integration"},{id:"galileo",displayName:"Galileo",logo:`${t}galileo.ico`,supports_key_team_logging:!1,dynamic_params:{GALILEO_API_KEY:"password",GALILEO_PROJECT_ID:"text",GALILEO_LOG_STREAM_ID:"text",GALILEO_BASE_URL:"text",GALILEO_USERNAME:"text",GALILEO_PASSWORD:"password"},description:"Galileo AI Observability Integration"},{id:"datadog",displayName:"Datadog",logo:`${t}datadog.png`,supports_key_team_logging:!1,dynamic_params:{dd_api_key:"password",dd_site:"text"},description:"Datadog Logging Integration"},{id:"lago",displayName:"Lago",logo:`${t}lago.svg`,supports_key_team_logging:!1,dynamic_params:{lago_api_url:"text",lago_api_key:"password"},description:"Lago Billing Logging Integration"},{id:"langfuse",displayName:"Langfuse",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v2 Logging Integration"},{id:"langfuse_otel",displayName:"Langfuse OTEL",logo:`${t}langfuse.png`,supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v3 OTEL Logging Integration"},{id:"langsmith",displayName:"LangSmith",logo:`${t}langsmith.png`,supports_key_team_logging:!0,dynamic_params:{langsmith_api_key:"password",langsmith_project:"text",langsmith_base_url:"text",langsmith_sampling_rate:"number"},description:"Langsmith Logging Integration"},{id:"openmeter",displayName:"OpenMeter",logo:`${t}openmeter.png`,supports_key_team_logging:!1,dynamic_params:{openmeter_api_key:"password",openmeter_base_url:"text"},description:"OpenMeter Logging Integration"},{id:"otel",displayName:"Open Telemetry",logo:`${t}otel.png`,supports_key_team_logging:!1,dynamic_params:{otel_endpoint:"text",otel_headers:"text"},description:"OpenTelemetry Logging Integration"},{id:"s3",displayName:"S3",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{s3_bucket_name:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"S3 Bucket (AWS) Logging Integration"},{id:"SQS",displayName:"SQS",logo:`${t}aws.svg`,supports_key_team_logging:!1,dynamic_params:{sqs_queue_url:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"SQS Queue (AWS) Logging Integration"}],s=l.reduce((e,t)=>(e[t.displayName]=t,e),{}),a=l.reduce((e,t)=>(e[t.displayName]=t.id,e),{}),r=l.reduce((e,t)=>(e[t.id]=t.displayName,e),{});e.s(["callbackInfo",0,s,"callback_map",0,a,"mapDisplayToInternalNames",0,e=>e.map(e=>a[e]||e),"mapInternalToDisplayNames",0,e=>e.map(e=>r[e]||e),"reverse_callback_map",0,r])},810757,477386,e=>{"use strict";var t=e.i(271645);let l=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"}),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"}))});e.s(["CogIcon",0,l],810757);let s=t.forwardRef(function(e,l){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:l},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"}))});e.s(["BanIcon",0,s],477386)},552130,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select agents",disabled:d=!1})=>{let[c,u]=(0,l.useState)([]),[m,p]=(0,l.useState)([]),[g,h]=(0,l.useState)(!1);(0,l.useEffect)(()=>{(async()=>{if(n){h(!0);try{let e=await (0,a.getAgentsList)(n),t=e?.agents||[];u(t);let l=new Set;t.forEach(e=>{let t=e.agent_access_groups;t&&Array.isArray(t)&&t.forEach(e=>l.add(e))}),p(Array.from(l))}catch(e){console.error("Error fetching agents:",e)}finally{h(!1)}}})()},[n]);let x=[...m.map(e=>({label:e,value:`group:${e}`,isAccessGroup:!0,searchText:`${e} Access Group`})),...c.map(e=>({label:`${e.agent_name||e.agent_id}`,value:e.agent_id,isAccessGroup:!1,searchText:`${e.agent_name||e.agent_id} ${e.agent_id} Agent`}))],y=[...r?.agents||[],...(r?.accessGroups||[]).map(e=>`group:${e}`)];return(0,t.jsx)("div",{children:(0,t.jsx)(s.Select,{mode:"multiple",placeholder:o,onChange:t=>{e({agents:t.filter(e=>!e.startsWith("group:")),accessGroups:t.filter(e=>e.startsWith("group:")).map(e=>e.replace("group:",""))})},value:y,loading:g,className:i,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:d,filterOption:(e,t)=>(x.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:x.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,label:e.label,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:e.isAccessGroup?"#52c41a":"#722ed1",flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:e.isAccessGroup?"#52c41a":"#722ed1",fontSize:"12px",fontWeight:500,opacity:.8},children:e.isAccessGroup?"Access Group":"Agent"})]})},e.value))})})}])},9314,645526,e=>{"use strict";var t=e.i(843476),l=e.i(199133),s=e.i(981339);e.i(247167);var a=e.i(931067),r=e.i(271645);let i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M824.2 699.9a301.55 301.55 0 00-86.4-60.4C783.1 602.8 812 546.8 812 484c0-110.8-92.4-201.7-203.2-200-109.1 1.7-197 90.6-197 200 0 62.8 29 118.8 74.2 155.5a300.95 300.95 0 00-86.4 60.4C345 754.6 314 826.8 312 903.8a8 8 0 008 8.2h56c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5A226.62 226.62 0 01612 684c60.9 0 118.2 23.7 161.3 66.8C814.5 792 838 846.3 840 904.3c.1 4.3 3.7 7.7 8 7.7h56a8 8 0 008-8.2c-2-77-33-149.2-87.8-203.9zM612 612c-34.2 0-66.4-13.3-90.5-37.5a126.86 126.86 0 01-37.5-91.8c.3-32.8 13.4-64.5 36.3-88 24-24.6 56.1-38.3 90.4-38.7 33.9-.3 66.8 12.9 91 36.6 24.8 24.3 38.4 56.8 38.4 91.4 0 34.2-13.3 66.3-37.5 90.5A127.3 127.3 0 01612 612zM361.5 510.4c-.9-8.7-1.4-17.5-1.4-26.4 0-15.9 1.5-31.4 4.3-46.5.7-3.6-1.2-7.3-4.5-8.8-13.6-6.1-26.1-14.5-36.9-25.1a127.54 127.54 0 01-38.7-95.4c.9-32.1 13.8-62.6 36.3-85.6 24.7-25.3 57.9-39.1 93.2-38.7 31.9.3 62.7 12.6 86 34.4 7.9 7.4 14.7 15.6 20.4 24.4 2 3.1 5.9 4.4 9.3 3.2 17.6-6.1 36.2-10.4 55.3-12.4 5.6-.6 8.8-6.6 6.3-11.6-32.5-64.3-98.9-108.7-175.7-109.9-110.9-1.7-203.3 89.2-203.3 199.9 0 62.8 28.9 118.8 74.2 155.5-31.8 14.7-61.1 35-86.5 60.4-54.8 54.7-85.8 126.9-87.8 204a8 8 0 008 8.2h56.1c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5 29.4-29.4 65.4-49.8 104.7-59.7 3.9-1 6.5-4.7 6-8.7z"}}]},name:"team",theme:"outlined"};var n=e.i(9583),o=r.forwardRef(function(e,t){return r.createElement(n.default,(0,a.default)({},e,{ref:t,icon:i}))});e.s(["TeamOutlined",0,o],645526);var d=e.i(599724),c=e.i(263147);e.s(["default",0,({value:e,onChange:a,placeholder:r="Select access groups",disabled:i=!1,style:n,className:u,showLabel:m=!1,labelText:p="Access Group",allowClear:g=!0})=>{let{data:h,isLoading:x,isError:y}=(0,c.useAccessGroups)();if(x)return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(d.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(o,{className:"mr-2"})," ",p]}),(0,t.jsx)(s.Skeleton.Input,{active:!0,block:!0,style:{height:32,...n}})]});let f=(h??[]).map(e=>({label:(0,t.jsxs)("span",{children:[(0,t.jsx)("span",{className:"font-medium",children:e.access_group_name})," ",(0,t.jsxs)("span",{className:"text-gray-400 text-xs",children:["(",e.access_group_id,")"]})]}),value:e.access_group_id,selectedLabel:e.access_group_name,searchText:`${e.access_group_name} ${e.access_group_id}`}));return(0,t.jsxs)("div",{children:[m&&(0,t.jsxs)(d.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(o,{className:"mr-2"})," ",p]}),(0,t.jsx)(l.Select,{mode:"multiple",value:e,placeholder:r,onChange:a,disabled:i,allowClear:g,showSearch:!0,style:{width:"100%",...n},className:`rounded-md ${u??""}`,notFoundContent:y?(0,t.jsx)("span",{className:"text-red-500",children:"Failed to load access groups"}):"No access groups found",filterOption:(e,t)=>(f.find(e=>e.value===t?.value)?.searchText??"").toLowerCase().includes(e.toLowerCase()),optionLabelProp:"selectedLabel",options:f.map(e=>({label:e.label,value:e.value,selectedLabel:e.selectedLabel}))})]})}],9314)},392110,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(199133),a=e.i(592968),r=e.i(312361),i=e.i(790848),n=e.i(536916),o=e.i(827252),d=e.i(779241);let{Option:c}=s.Select;e.s(["default",0,({form:e,autoRotationEnabled:u,onAutoRotationChange:m,rotationInterval:p,onRotationIntervalChange:g,isCreateMode:h=!1,neverExpire:x=!1,onNeverExpireChange:y})=>{let f=p&&!["7d","30d","90d","180d","365d"].includes(p),[b,_]=(0,l.useState)(f),[j,v]=(0,l.useState)(f?p:""),[w,k]=(0,l.useState)(e?.getFieldValue?.("duration")||"");return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Key Expiry Settings"}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Expire Key"}),(0,t.jsx)(a.Tooltip,{title:"Set when this key should expire. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days). Leave empty to keep the current expiry unchanged.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),!h&&y&&(0,t.jsx)(n.Checkbox,{checked:x,onChange:t=>{let l=t.target.checked;y(l),l&&(k(""),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",""):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:""}))},className:"ml-2 text-sm font-normal text-gray-600",children:"Never Expire"})]}),(0,t.jsx)(d.TextInput,{name:"duration",placeholder:h?"e.g., 30d or leave empty to never expire":"e.g., 30d",className:"w-full",value:w,onValueChange:t=>{k(t),e&&"function"==typeof e.setFieldValue?e.setFieldValue("duration",t):e&&"function"==typeof e.setFieldsValue&&e.setFieldsValue({duration:t})},disabled:!h&&x})]})]}),(0,t.jsx)(r.Divider,{}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Auto-Rotation Settings"}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Enable Auto-Rotation"}),(0,t.jsx)(a.Tooltip,{title:"Key will automatically regenerate at the specified interval for enhanced security.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsx)(i.Switch,{checked:u,onChange:m,size:"default",className:u?"":"bg-gray-400"})]}),u&&(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 flex items-center space-x-1",children:[(0,t.jsx)("span",{children:"Rotation Interval"}),(0,t.jsx)(a.Tooltip,{title:"How often the key should be automatically rotated. Choose the interval that best fits your security requirements.",children:(0,t.jsx)(o.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)(s.Select,{value:b?"custom":p,onChange:e=>{"custom"===e?_(!0):(_(!1),v(""),g(e))},className:"w-full",placeholder:"Select interval",children:[(0,t.jsx)(c,{value:"7d",children:"7 days"}),(0,t.jsx)(c,{value:"30d",children:"30 days"}),(0,t.jsx)(c,{value:"90d",children:"90 days"}),(0,t.jsx)(c,{value:"180d",children:"180 days"}),(0,t.jsx)(c,{value:"365d",children:"365 days"}),(0,t.jsx)(c,{value:"custom",children:"Custom interval"})]}),b&&(0,t.jsxs)("div",{className:"space-y-1",children:[(0,t.jsx)(d.TextInput,{value:j,onChange:e=>{let t=e.target.value;v(t),g(t)},placeholder:"e.g., 1s, 5m, 2h, 14d"}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Supported formats: seconds (s), minutes (m), hours (h), days (d)"})]})]})]})]}),u&&(0,t.jsx)("div",{className:"bg-blue-50 p-3 rounded-md text-sm text-blue-700",children:"When rotation occurs, you'll receive a notification with the new key. The old key will be deactivated after a brief grace period."})]})]})}])},844565,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:r,className:i,accessToken:n,placeholder:o="Select pass through routes",disabled:d=!1,teamId:c})=>{let[u,m]=(0,l.useState)([]),[p,g]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,a.getPassThroughEndpointsCall)(n,c);if(e.endpoints){let t=e.endpoints.flatMap(e=>{let t=e.path,l=e.methods;return l&&l.length>0?l.map(e=>({label:`${e} ${t}`,value:t})):[{label:t,value:t}]});m(t)}}catch(e){console.error("Error fetching pass through routes:",e)}finally{g(!1)}}})()},[n,c]),(0,t.jsx)(s.Select,{mode:"tags",placeholder:o,onChange:e,value:r,loading:p,className:i,allowClear:!0,options:u,optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:d})}])},939510,e=>{"use strict";var t=e.i(843476),l=e.i(808613),s=e.i(199133),a=e.i(592968),r=e.i(827252);let{Option:i}=s.Select;e.s(["default",0,({type:e,name:n,showDetailedDescriptions:o=!0,className:d="",initialValue:c=null,form:u,onChange:m})=>{let p=e.toUpperCase(),g=e.toLowerCase(),h=`Select 'guaranteed_throughput' to prevent overallocating ${p} limit when the key belongs to a Team with specific ${p} limits.`;return(0,t.jsx)(l.Form.Item,{label:(0,t.jsxs)("span",{children:[p," Rate Limit Type"," ",(0,t.jsx)(a.Tooltip,{title:h,children:(0,t.jsx)(r.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:n,initialValue:c,className:d,children:(0,t.jsx)(s.Select,{defaultValue:o?"default":void 0,placeholder:"Select rate limit type",style:{width:"100%"},optionLabelProp:o?"label":void 0,onChange:e=>{u&&u.setFieldValue(n,e),m&&m(e)},children:o?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",label:"Default",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Default"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Best effort throughput - no error if we're overallocating ",g," (Team/Key Limits checked at runtime)."]})]})}),(0,t.jsx)(i,{value:"guaranteed_throughput",label:"Guaranteed throughput",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Guaranteed throughput"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["Guaranteed throughput - raise an error if we're overallocating ",g," (also checks model-specific limits)"]})]})}),(0,t.jsx)(i,{value:"dynamic",label:"Dynamic",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Dynamic"}),(0,t.jsxs)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:["If the key has a set ",p," (e.g. 2 ",p,") and there are no 429 errors, it can dynamically exceed the limit when the model being called is not erroring."]})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i,{value:"best_effort_throughput",children:"Best effort throughput"}),(0,t.jsx)(i,{value:"guaranteed_throughput",children:"Guaranteed throughput"}),(0,t.jsx)(i,{value:"dynamic",children:"Dynamic"})]})})})}])},363256,e=>{"use strict";var t=e.i(843476),l=e.i(199133);let{Text:s}=e.i(898586).Typography;e.s(["default",0,({organizations:e,value:a,onChange:r,disabled:i,loading:n,style:o})=>(0,t.jsx)(l.Select,{showSearch:!0,placeholder:"All Organizations",value:a,onChange:r,disabled:i,loading:n,allowClear:!0,style:{minWidth:280,...o},filterOption:(t,l)=>{if(!l)return!1;let s=e?.find(e=>e.organization_id===l.key);if(!s)return!1;let a=t.toLowerCase().trim(),r=(s.organization_alias||"").toLowerCase(),i=(s.organization_id||"").toLowerCase();return r.includes(a)||i.includes(a)},children:e?.map(e=>(0,t.jsxs)(l.Select.Option,{value:e.organization_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.organization_alias})," ",(0,t.jsxs)(s,{type:"secondary",children:["(",e.organization_id,")"]})]},e.organization_id))})])},128233,319312,833400,e=>{"use strict";var t=e.i(843476),l=e.i(464571),s=e.i(199133),a=e.i(592968),r=e.i(425063),i=e.i(107233),n=e.i(37727),o=e.i(271645);e.s(["BudgetFallbacksEditor",0,function({value:e,onChange:d,availableModels:c}){let[u,m]=(0,o.useState)(()=>{let t;return 0===(t=Object.keys(e)).length?[]:t.map((t,l)=>({id:String(l+1),primaryModel:t,fallbackModels:e[t]}))}),p=e=>{m(e),d(Object.fromEntries(e.filter(e=>null!==e.primaryModel&&e.fallbackModels.length>0).map(e=>[e.primaryModel,e.fallbackModels])))},g=()=>{p([...u,{id:Date.now().toString(),primaryModel:null,fallbackModels:[]}])},h=(e,t)=>{p(u.map(l=>l.id===e?{...l,...t}:l))},x=new Set(u.map(e=>e.primaryModel).filter(Boolean));return 0===u.length?(0,t.jsxs)("div",{children:[(0,t.jsx)("div",{className:"text-xs text-gray-500 mb-2",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),(0,t.jsx)(l.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"When a model exceeds its per-model budget, requests automatically reroute to fallback models"}),u.map(e=>{let l=c.filter(t=>t===e.primaryModel||!x.has(t)),i=c.filter(t=>t!==e.primaryModel);return(0,t.jsxs)("div",{className:"relative rounded-lg border border-gray-200 bg-gray-50 p-4",children:[(0,t.jsx)("button",{type:"button",onClick:()=>{var t;return t=e.id,void p(u.filter(e=>e.id!==t))},className:"absolute top-2 right-2 text-gray-400 hover:text-red-500 transition-colors p-1",children:(0,t.jsx)(n.X,{className:"w-4 h-4"})}),(0,t.jsxs)("div",{className:"mb-3",children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Primary Model"}),(0,t.jsx)(s.Select,{className:"w-full",placeholder:"Select model",value:e.primaryModel,onChange:t=>{let l=e.fallbackModels.filter(e=>e!==t);h(e.id,{primaryModel:t,fallbackModels:l})},showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:l.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-1 mb-2",children:(0,t.jsxs)("div",{className:"bg-amber-50 text-amber-600 px-3 py-0.5 rounded-full text-[10px] font-bold border border-amber-100 flex items-center gap-1",children:[(0,t.jsx)(r.ArrowDown,{className:"w-3 h-3"}),"IF BUDGET EXCEEDED, TRY"]})}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs font-medium text-gray-600 mb-1",children:"Fallback Models"}),(0,t.jsx)(s.Select,{mode:"multiple",className:"w-full",placeholder:e.primaryModel?"Select fallback models":"Select a primary model first",value:e.fallbackModels,onChange:t=>h(e.id,{fallbackModels:t}),disabled:!e.primaryModel,showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:i.map(e=>({label:e,value:e})),getPopupContainer:e=>e.parentElement||document.body,maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(a.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})})}),e.fallbackModels.length>1&&(0,t.jsx)("div",{className:"text-[10px] text-gray-400 mt-1 ml-1",children:"Tried in order; first model still within its own budget is used"})]})]},e.id)}),(0,t.jsx)(l.Button,{size:"small",onClick:g,icon:(0,t.jsx)(i.Plus,{className:"w-3 h-3"}),children:"Add Budget Fallback"})]})}],128233);var d=e.i(28651);let c=[{value:"1h",label:"Hourly",resetHint:"Resets every hour"},{value:"24h",label:"Daily",resetHint:"Resets daily at midnight UTC"},{value:"7d",label:"Weekly",resetHint:"Resets every Sunday at midnight UTC"},{value:"30d",label:"Monthly",resetHint:"Resets on the 1st of every month at midnight UTC"}];e.s(["BudgetWindowsEditor",0,function({value:e,onChange:a}){let r=(t,l,s)=>{a(e.map((e,a)=>a===t?{...e,[l]:s}:e))};return(0,t.jsxs)("div",{children:[e.map((i,n)=>{let o=c.find(e=>e.value===i.budget_duration)?.resetHint;return(0,t.jsxs)("div",{style:{marginBottom:12},children:[(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[(0,t.jsx)(s.Select,{value:i.budget_duration,onChange:e=>r(n,"budget_duration",e),style:{width:130},options:c.map(e=>({value:e.value,label:e.label}))}),(0,t.jsx)(d.InputNumber,{step:.01,min:0,precision:2,value:i.max_budget??void 0,onChange:e=>r(n,"max_budget",e??null),placeholder:"Max spend ($)",style:{width:160},prefix:"$"}),(0,t.jsx)(l.Button,{type:"text",danger:!0,size:"small",onClick:()=>{a(e.filter((e,t)=>t!==n))},style:{padding:"0 4px"},children:"✕"})]}),o&&(0,t.jsxs)("div",{style:{fontSize:11,color:"#888",marginTop:3,marginLeft:2},children:["↻ ",o]})]},n)}),(0,t.jsx)(l.Button,{size:"small",onClick:t=>{t.preventDefault(),a([...e,{budget_duration:"24h",max_budget:null}])},children:"+ Add Budget Window"})]})}],319312);var u=e.i(311451);let m=0,p=()=>`tag-row-${m++}`;e.s(["TagRateLimitEditor",0,function({value:e,onChange:s}){let a=(t,l,a)=>{s(e.map((e,s)=>s===t?{...e,[l]:a}:e))};return(0,t.jsxs)("div",{children:[e.map((r,i)=>(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center",marginBottom:12},children:[(0,t.jsx)(u.Input,{value:r.tag,onChange:e=>a(i,"tag",e.target.value),placeholder:"Tag (e.g. cell-1)",style:{width:180}}),(0,t.jsx)(d.InputNumber,{min:0,value:r.rpm_limit??void 0,onChange:e=>a(i,"rpm_limit",e??null),placeholder:"RPM",style:{width:120}}),(0,t.jsx)(l.Button,{type:"text",danger:!0,size:"small",onClick:()=>{s(e.filter((e,t)=>t!==i))},style:{padding:"0 4px"},children:"✕"})]},r.id)),(0,t.jsx)(l.Button,{size:"small",onClick:t=>{t.preventDefault(),s([...e,{id:p(),tag:"",rpm_limit:null}])},children:"+ Add Tag Limit"})]})},"tagLimitsToRows",0,e=>{let t=(e=>{if(!e||"object"!=typeof e)return{};let t={};return Object.entries(e).forEach(([e,l])=>{"number"==typeof l&&(t[e]=l)}),t})(e);return Object.keys(t).map(e=>({id:p(),tag:e,rpm_limit:t[e]}))},"tagRowsToLimits",0,e=>{let t={};return e.forEach(({tag:e,rpm_limit:l})=>{let s=e.trim();s&&"number"==typeof l&&(t[s]=l)}),{tag_rpm_limit:t}}],833400)},390605,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(602869),a=e.i(599724),r=e.i(482725),i=e.i(91739),n=e.i(500727),o=e.i(531516),d=e.i(696609);e.s(["default",0,({accessToken:e,selectedServers:c,toolPermissions:u,onChange:m,disabled:p=!1})=>{let{data:g=[]}=(0,n.useMCPServers)(),[h,x]=(0,l.useState)({}),[y,f]=(0,l.useState)({}),[b,_]=(0,l.useState)({}),[j,v]=(0,l.useState)({}),w=(0,l.useRef)(u);(0,l.useEffect)(()=>{w.current=u},[u]);let k=(0,l.useMemo)(()=>0===c.length?[]:g.filter(e=>c.includes(e.server_id)),[g,c]),N=async(e,t)=>{f(t=>({...t,[e]:!0})),_(t=>({...t,[e]:""}));try{let l=await (0,s.listMCPTools)(t,e);if(l.error)_(t=>({...t,[e]:l.message||"Failed to fetch tools"})),x(t=>({...t,[e]:[]}));else{let t=l.tools||[];x(l=>({...l,[e]:t}));let s=w.current;if(!s[e]&&t.length>0){let l=t.filter(e=>"delete"!==(0,d.classifyToolOp)(e.name,e.description||"")).map(e=>e.name);m({...s,[e]:l})}}}catch(t){console.error(`Error fetching tools for server ${e}:`,t),_(t=>({...t,[e]:"Failed to fetch tools"})),x(t=>({...t,[e]:[]}))}finally{f(t=>({...t,[e]:!1}))}};(0,l.useEffect)(()=>{k.forEach(t=>{h[t.server_id]||y[t.server_id]||N(t.server_id,e)})},[k,e]);let S=(e,t)=>{m({...u,[e]:t})};return 0===c.length?null:(0,t.jsx)("div",{className:"space-y-4",children:k.map(e=>{let l=e.server_name||e.alias||e.server_id,s=h[e.server_id]||[],n=u[e.server_id]||[],d=y[e.server_id],c=b[e.server_id],g=j[e.server_id]??"crud";return(0,t.jsxs)("div",{className:"border rounded-lg bg-gray-50",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between p-4 border-b bg-white rounded-t-lg",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(a.Text,{className:"font-semibold text-gray-900",children:l}),e.description&&(0,t.jsx)(a.Text,{className:"text-sm text-gray-500",children:e.description})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[!p&&s.length>0&&(0,t.jsx)(i.Radio.Group,{value:g,onChange:t=>v(l=>({...l,[e.server_id]:t.target.value})),size:"small",optionType:"button",buttonStyle:"solid",options:[{label:"Risk Groups",value:"crud"},{label:"Flat List",value:"flat"}]}),!p&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;let l;return l=h[t=e.server_id]||[],void m({...u,[t]:l.map(e=>e.name)})},disabled:d,children:"Select All"}),(0,t.jsx)("button",{type:"button",className:"text-sm text-blue-600 hover:text-blue-700 font-medium",onClick:()=>{var t;return t=e.server_id,void m({...u,[t]:[]})},disabled:d,children:"Deselect All"})]})]})]}),(0,t.jsxs)("div",{className:"p-4",children:[d&&(0,t.jsxs)("div",{className:"flex items-center justify-center py-8",children:[(0,t.jsx)(r.Spin,{size:"large"}),(0,t.jsx)(a.Text,{className:"ml-3 text-gray-500",children:"Loading tools..."})]}),c&&!d&&(0,t.jsxs)("div",{className:"p-4 bg-red-50 border border-red-200 rounded-lg text-center",children:[(0,t.jsx)(a.Text,{className:"text-red-600 font-medium",children:"Unable to load tools"}),(0,t.jsx)(a.Text,{className:"text-sm text-red-500 mt-1",children:c})]}),!d&&!c&&s.length>0&&"crud"===g&&(0,t.jsx)(o.default,{tools:s,value:u[e.server_id]?n:void 0,onChange:t=>S(e.server_id,t),readOnly:p}),!d&&!c&&s.length>0&&"flat"===g&&(0,t.jsx)("div",{className:"space-y-2",children:s.map(l=>{let s=n.includes(l.name);return(0,t.jsxs)("div",{className:"flex items-start gap-2",children:[(0,t.jsx)("input",{type:"checkbox",checked:s,onChange:()=>{if(p)return;let t=s?n.filter(e=>e!==l.name):[...n,l.name];S(e.server_id,t)},disabled:p,className:"mt-0.5"}),(0,t.jsx)("div",{className:"flex-1 min-w-0",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900",children:l.name}),(0,t.jsxs)(a.Text,{className:"text-sm text-gray-500",children:["- ",l.description||"No description"]})]})})]},l.name)})}),!d&&!c&&0===s.length&&(0,t.jsx)("div",{className:"text-center py-6",children:(0,t.jsx)(a.Text,{className:"text-gray-500",children:"No tools available"})})]})]},e.server_id)})})}])},109034,e=>{"use strict";var t=e.i(266027),l=e.i(243652),s=e.i(602869),a=e.i(135214);let r=(0,l.createQueryKeys)("tags");e.s(["useTags",0,()=>{let{accessToken:e,userId:l,userRole:i}=(0,a.default)();return(0,t.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,s.tagListCall)(e),enabled:!!(e&&l&&i)})}])},533882,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(250980),a=e.i(797672),r=e.i(68155),i=e.i(304967),n=e.i(629569),o=e.i(599724),d=e.i(269200),c=e.i(427612),u=e.i(64848),m=e.i(942232),p=e.i(496020),g=e.i(977572),h=e.i(992619),x=e.i(727749);e.s(["default",0,({accessToken:e,initialModelAliases:y={},onAliasUpdate:f,showExampleConfig:b=!0})=>{let[_,j]=(0,l.useState)([]),[v,w]=(0,l.useState)({aliasName:"",targetModel:""}),[k,N]=(0,l.useState)(null);(0,l.useEffect)(()=>{j(Object.entries(y).map(([e,t],l)=>({id:`${l}-${e}`,aliasName:e,targetModel:t})))},[y]);let S=()=>{if(!k)return;if(!k.aliasName||!k.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.id!==k.id&&e.aliasName===k.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=_.map(e=>e.id===k.id?k:e);j(e),N(null);let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias updated successfully")},C=()=>{N(null)},T=_.reduce((e,t)=>(e[t.aliasName]=t.targetModel,e),{});return(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Add New Alias"}),(0,t.jsxs)("div",{className:"grid grid-cols-3 gap-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Alias Name"}),(0,t.jsx)("input",{type:"text",value:v.aliasName,onChange:e=>w({...v,aliasName:e.target.value}),placeholder:"e.g., gpt-4o",className:"w-full px-3 py-2 border border-gray-300 rounded-md text-sm"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"block text-xs text-gray-500 mb-1",children:"Target Model"}),(0,t.jsx)(h.default,{accessToken:e,value:v.targetModel,placeholder:"Select target model",onChange:e=>w({...v,targetModel:e}),showLabel:!1})]}),(0,t.jsx)("div",{className:"flex items-end",children:(0,t.jsxs)("button",{onClick:()=>{if(!v.aliasName||!v.targetModel)return void x.default.fromBackend("Please provide both alias name and target model");if(_.some(e=>e.aliasName===v.aliasName))return void x.default.fromBackend("An alias with this name already exists");let e=[..._,{id:`${Date.now()}-${v.aliasName}`,aliasName:v.aliasName,targetModel:v.targetModel}];j(e),w({aliasName:"",targetModel:""});let t={};e.forEach(e=>{t[e.aliasName]=e.targetModel}),f&&f(t),x.default.success("Alias added successfully")},disabled:!v.aliasName||!v.targetModel,className:`flex items-center px-4 py-2 rounded-md text-sm ${!v.aliasName||!v.targetModel?"bg-gray-300 text-gray-500 cursor-not-allowed":"bg-green-600 text-white hover:bg-green-700"}`,children:[(0,t.jsx)(s.PlusCircleIcon,{className:"w-4 h-4 mr-1"}),"Add Alias"]})})]})]}),(0,t.jsx)(o.Text,{className:"text-sm font-medium text-gray-700 mb-2",children:"Manage Existing Aliases"}),(0,t.jsx)("div",{className:"rounded-lg custom-border relative mb-6",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(d.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",children:[(0,t.jsx)(c.TableHead,{children:(0,t.jsxs)(p.TableRow,{children:[(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Alias Name"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Target Model"}),(0,t.jsx)(u.TableHeaderCell,{className:"py-1 h-8",children:"Actions"})]})}),(0,t.jsxs)(m.TableBody,{children:[_.map(l=>(0,t.jsx)(p.TableRow,{className:"h-8",children:k&&k.id===l.id?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)("input",{type:"text",value:k.aliasName,onChange:e=>N({...k,aliasName:e.target.value}),className:"w-full px-2 py-1 border border-gray-300 rounded-md text-sm"})}),(0,t.jsx)(g.TableCell,{className:"py-0.5",children:(0,t.jsx)(h.default,{accessToken:e,value:k.targetModel,onChange:e=>N({...k,targetModel:e}),showLabel:!1,style:{height:"32px"}})}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:S,className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:"Save"}),(0,t.jsx)("button",{onClick:C,className:"text-xs bg-gray-50 text-gray-600 px-2 py-1 rounded-sm hover:bg-gray-100",children:"Cancel"})]})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-900",children:l.aliasName}),(0,t.jsx)(g.TableCell,{className:"py-0.5 text-sm text-gray-500",children:l.targetModel}),(0,t.jsx)(g.TableCell,{className:"py-0.5 whitespace-nowrap",children:(0,t.jsxs)("div",{className:"flex space-x-2",children:[(0,t.jsx)("button",{onClick:()=>{N({...l})},className:"text-xs bg-blue-50 text-blue-600 px-2 py-1 rounded-sm hover:bg-blue-100",children:(0,t.jsx)(a.PencilIcon,{className:"w-3 h-3"})}),(0,t.jsx)("button",{onClick:()=>{var e;let t,s;return e=l.id,j(t=_.filter(t=>t.id!==e)),s={},void(t.forEach(e=>{s[e.aliasName]=e.targetModel}),f&&f(s),x.default.success("Alias deleted successfully"))},className:"text-xs bg-red-50 text-red-600 px-2 py-1 rounded-sm hover:bg-red-100",children:(0,t.jsx)(r.TrashIcon,{className:"w-3 h-3"})})]})})]})},l.id)),0===_.length&&(0,t.jsx)(p.TableRow,{children:(0,t.jsx)(g.TableCell,{colSpan:3,className:"py-0.5 text-sm text-gray-500 text-center",children:"No aliases added yet. Add a new alias above."})})]})]})})}),b&&(0,t.jsxs)(i.Card,{children:[(0,t.jsx)(n.Title,{className:"mb-4",children:"Configuration Example"}),(0,t.jsx)(o.Text,{className:"text-gray-600 mb-4",children:"Here's how your current aliases would look in the config:"}),(0,t.jsx)("div",{className:"bg-gray-100 rounded-lg p-4 font-mono text-sm",children:(0,t.jsxs)("div",{className:"text-gray-700",children:["model_aliases:",0===Object.keys(T).length?(0,t.jsxs)("span",{className:"text-gray-500",children:[(0,t.jsx)("br",{}),"  # No aliases configured yet"]}):Object.entries(T).map(([e,l])=>(0,t.jsxs)("span",{children:[(0,t.jsx)("br",{}),'  "',e,'": "',l,'"']},e))]})})]})]})}])},266484,e=>{"use strict";var t=e.i(843476),l=e.i(199133),s=e.i(592968),a=e.i(312361),r=e.i(827252),i=e.i(994388),n=e.i(304967),o=e.i(779241),d=e.i(988297),c=e.i(68155),u=e.i(810757),m=e.i(477386),p=e.i(557662),g=e.i(555987),h=e.i(435451);let{Option:x}=l.Select;e.s(["default",0,({value:e=[],onChange:y,disabledCallbacks:f=[],onDisabledCallbacksChange:b})=>{let _=Object.entries(p.callbackInfo).filter(([e,t])=>t.supports_key_team_logging).map(([e,t])=>e),j=Object.keys(p.callbackInfo),v=e=>{y?.(e)},w=(t,l,s)=>{let a=[...e];if("callback_name"===l){let e=p.callback_map[s]||s;a[t]={...a[t],[l]:e,callback_vars:{}}}else a[t]={...a[t],[l]:s};v(a)},k=(t,l,s)=>{let a=[...e];a[t]={...a[t],callback_vars:{...a[t].callback_vars,[l]:s}},v(a)};return(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(m.BanIcon,{className:"w-5 h-5 text-red-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Disabled Callbacks"}),(0,t.jsx)(s.Tooltip,{title:"Select callbacks to disable for this key. Disabled callbacks will not receive any logging data.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Disabled Callbacks"}),(0,t.jsx)(l.Select,{mode:"multiple",placeholder:"Select callbacks to disable",value:f,onChange:e=>{let t=(0,p.mapDisplayToInternalNames)(e);b?.(t)},style:{width:"100%"},optionLabelProp:"label",children:j.map(e=>{let l=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(s.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[l&&(0,t.jsx)("img",{src:l,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let l=t.target,s=l.parentElement;if(s){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),s.replaceChild(t,l)}}}),(0,t.jsx)("span",{children:e})]})})},e)})}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Select callbacks that should be disabled for this key. These callbacks will not receive any logging data."})]})]}),(0,t.jsx)(a.Divider,{}),(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(u.CogIcon,{className:"w-5 h-5 text-blue-500"}),(0,t.jsx)("span",{className:"text-base font-semibold text-gray-800",children:"Logging Integrations"}),(0,t.jsx)(s.Tooltip,{title:"Configure callback logging integrations for this team.",children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help"})})]}),(0,t.jsx)(i.Button,{variant:"secondary",onClick:()=>{v([...e,{callback_name:"",callback_type:"success",callback_vars:{}}])},icon:d.PlusIcon,size:"sm",className:"hover:border-blue-400 hover:text-blue-500",type:"button",children:"Add Integration"})]}),(0,t.jsx)("div",{className:"space-y-4",children:e.map((a,d)=>{let u=a.callback_name?Object.entries(p.callback_map).find(([e,t])=>t===a.callback_name)?.[0]:void 0,m=u?(0,g.resolveLogoSrc)(p.callbackInfo[u]?.logo):null;return(0,t.jsxs)(n.Card,{className:"border border-gray-200 shadow-xs hover:shadow-md transition-shadow duration-200",decoration:"top",decorationColor:"blue",children:[(0,t.jsxs)("div",{className:"flex justify-between items-start mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[m&&(0,t.jsx)("img",{src:m,alt:u,className:"w-5 h-5 object-contain"}),(0,t.jsxs)("span",{className:"text-sm font-medium",children:[u||"New Integration"," Configuration"]})]}),(0,t.jsx)(i.Button,{variant:"light",onClick:()=>{v(e.filter((e,t)=>t!==d))},icon:c.TrashIcon,size:"xs",color:"red",className:"hover:bg-red-50",type:"button",children:"Remove"})]}),(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Integration Type"}),(0,t.jsx)(l.Select,{value:u,placeholder:"Select integration",onChange:e=>w(d,"callback_name",e),className:"w-full",optionLabelProp:"label",children:_.map(e=>{let l=(0,g.resolveLogoSrc)(p.callbackInfo[e]?.logo),a=p.callbackInfo[e]?.description;return(0,t.jsx)(x,{value:e,label:e,children:(0,t.jsx)(s.Tooltip,{title:a,placement:"right",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[l&&(0,t.jsx)("img",{src:l,alt:e,className:"w-4 h-4 object-contain",onError:t=>{let l=t.target,s=l.parentElement;if(s){let t=document.createElement("div");t.className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs",t.textContent=e.charAt(0),s.replaceChild(t,l)}}}),(0,t.jsx)("span",{children:e})]})})},e)})})]}),(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsx)("label",{className:"text-sm font-medium text-gray-700",children:"Event Type"}),(0,t.jsxs)(l.Select,{value:a.callback_type,onChange:e=>w(d,"callback_type",e),className:"w-full",children:[(0,t.jsx)(x,{value:"success",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-green-500 rounded-full"}),(0,t.jsx)("span",{children:"Success Only"})]})}),(0,t.jsx)(x,{value:"failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-red-500 rounded-full"}),(0,t.jsx)("span",{children:"Failure Only"})]})}),(0,t.jsx)(x,{value:"success_and_failure",children:(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)("div",{className:"w-2 h-2 bg-blue-500 rounded-full"}),(0,t.jsx)("span",{children:"Success & Failure"})]})})]})]})]}),((e,l)=>{if(!e.callback_name)return null;let a=Object.entries(p.callback_map).find(([t,l])=>l===e.callback_name)?.[0];if(!a)return null;let i=p.callbackInfo[a]?.dynamic_params||{};return 0===Object.keys(i).length?null:(0,t.jsxs)("div",{className:"mt-6 pt-4 border-t border-gray-100",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2 mb-4",children:[(0,t.jsx)("div",{className:"w-3 h-3 bg-blue-100 rounded-full flex items-center justify-center",children:(0,t.jsx)("div",{className:"w-1.5 h-1.5 bg-blue-500 rounded-full"})}),(0,t.jsx)("span",{className:"text-sm font-medium text-gray-700",children:"Integration Parameters"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-4",children:Object.entries(i).map(([a,i])=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-gray-700 capitalize flex items-center space-x-1",children:[(0,t.jsx)("span",{children:a.replace(/_/g," ")}),(0,t.jsx)(s.Tooltip,{title:`Environment variable reference recommended: os.environ/${a.toUpperCase()}`,children:(0,t.jsx)(r.InfoCircleOutlined,{className:"text-gray-400 cursor-help text-xs"})}),"password"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Sensitive"}),"number"===i&&(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800",children:"Number"})]}),"number"===i&&(0,t.jsx)("span",{className:"text-xs text-gray-500",children:"Value must be between 0 and 1"}),"number"===i?(0,t.jsx)(h.default,{step:.01,width:400,placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>k(l,a,e.target.value)}):(0,t.jsx)(o.TextInput,{type:"password"===i?"password":"text",placeholder:`os.environ/${a.toUpperCase()}`,value:e.callback_vars[a]||"",onChange:e=>k(l,a,e.target.value)})]},a))})]})})(a,d)]})]},d)})}),0===e.length&&(0,t.jsxs)("div",{className:"text-center py-12 text-gray-500 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50",children:[(0,t.jsx)(u.CogIcon,{className:"w-12 h-12 text-gray-300 mb-3 mx-auto"}),(0,t.jsx)("div",{className:"text-base font-medium mb-1",children:"No logging integrations configured"}),(0,t.jsx)("div",{className:"text-sm text-gray-400",children:'Click "Add Integration" to configure logging for this team'})]})]})}])},651904,e=>{"use strict";var t=e.i(843476),l=e.i(599724),s=e.i(266484);e.s(["default",0,function({value:e,onChange:a,premiumUser:r=!1,disabledCallbacks:i=[],onDisabledCallbacksChange:n}){return r?(0,t.jsx)(s.default,{value:e,onChange:a,disabledCallbacks:i,onDisabledCallbacksChange:n}):(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex flex-wrap gap-2 mb-3",children:[(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ langfuse-logging"}),(0,t.jsx)("div",{className:"inline-flex items-center px-3 py-1.5 rounded-lg bg-green-50 border border-green-200 text-green-800 text-sm font-medium opacity-50",children:"✨ datadog-logging"})]}),(0,t.jsx)("div",{className:"p-3 bg-yellow-50 border border-yellow-200 rounded-lg",children:(0,t.jsxs)(l.Text,{className:"text-sm text-yellow-800",children:["Setting Key/Team logging settings is a LiteLLM Enterprise feature. Global Logging Settings are available for all free users. Get a trial key"," ",(0,t.jsx)("a",{href:"https://www.litellm.ai/#pricing",target:"_blank",rel:"noopener noreferrer",className:"underline",children:"here"}),"."]})})]})}])},460285,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(404206),a=e.i(723731),r=e.i(653824),i=e.i(881073),n=e.i(197647),o=e.i(602869),d=e.i(158392),c=e.i(419470),u=e.i(695411);let m=(0,l.forwardRef)(({accessToken:e,value:m,onChange:p,modelData:g},h)=>{let[x,y]=(0,l.useState)({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),[f,b]=(0,l.useState)([]),[_,j]=(0,l.useState)([]),[v,w]=(0,l.useState)([]),[k,N]=(0,l.useState)([]),[S,C]=(0,l.useState)({}),[T,I]=(0,l.useState)({}),A=(0,l.useRef)(!1),L=(0,l.useRef)(null);(0,l.useEffect)(()=>{let e=m?.router_settings?JSON.stringify({routing_strategy:m.router_settings.routing_strategy,fallbacks:m.router_settings.fallbacks,enable_tag_filtering:m.router_settings.enable_tag_filtering}):null;if(A.current&&e===L.current){A.current=!1;return}if(A.current&&e!==L.current&&(A.current=!1),e!==L.current)if(L.current=e,m?.router_settings){let e=m.router_settings,{fallbacks:t,...l}=e;y({routerSettings:l,selectedStrategy:e.routing_strategy||null,enableTagFiltering:e.enable_tag_filtering??!1});let s=e.fallbacks||[];b(s),j(s&&0!==s.length?s.map((e,t)=>{let[l,s]=Object.entries(e)[0];return{id:(t+1).toString(),primaryModel:l||null,fallbackModels:s||[]}}):[{id:"1",primaryModel:null,fallbackModels:[]}])}else y({routerSettings:{},selectedStrategy:null,enableTagFiltering:!1}),b([]),j([{id:"1",primaryModel:null,fallbackModels:[]}])},[m]),(0,l.useEffect)(()=>{e&&(0,o.getRouterSettingsCall)(e).then(e=>{if(e.fields){let t={};e.fields.forEach(e=>{t[e.field_name]={ui_field_name:e.ui_field_name,field_description:e.field_description,options:e.options,link:e.link}}),C(t);let l=e.fields.find(e=>"routing_strategy"===e.field_name);l?.options&&N(l.options),e.routing_strategy_descriptions&&I(e.routing_strategy_descriptions)}})},[e]),(0,l.useEffect)(()=>{e&&(async()=>{try{let t=await (0,u.fetchAvailableModels)(e);w(t)}catch(e){console.error("Error fetching model info for fallbacks:",e)}})()},[e]);let F=()=>{let e=new Set(["allowed_fails","cooldown_time","num_retries","timeout","retry_after"]),t=new Set(["model_group_alias","retry_policy"]),l=Object.fromEntries(Object.entries({...x.routerSettings,enable_tag_filtering:x.enableTagFiltering,routing_strategy:x.selectedStrategy,fallbacks:f.length>0?f:null}).map(([l,s])=>{if("routing_strategy_args"!==l&&"routing_strategy"!==l&&"enable_tag_filtering"!==l&&"fallbacks"!==l){let a=document.querySelector(`input[name="${l}"]`);if(a){if(void 0!==a.value&&""!==a.value){let r=((l,s,a)=>{if(null==s)return a;let r=String(s).trim();if(""===r||"null"===r.toLowerCase())return null;if(e.has(l)){let e=Number(r);return Number.isNaN(e)?a:e}if(t.has(l)){if(""===r)return null;try{return JSON.parse(r)}catch{return a}}return"true"===r.toLowerCase()||"false"!==r.toLowerCase()&&r})(l,a.value,s);return[l,r]}return[l,null]}}else if("routing_strategy"===l)return[l,x.selectedStrategy];else if("enable_tag_filtering"===l)return[l,x.enableTagFiltering];else if("fallbacks"===l)return[l,f.length>0?f:null];else if("routing_strategy_args"===l&&"latency-based-routing"===x.selectedStrategy){let e=document.querySelector('input[name="lowest_latency_buffer"]'),t=document.querySelector('input[name="ttl"]'),l={};return e?.value&&(l.lowest_latency_buffer=Number(e.value)),t?.value&&(l.ttl=Number(t.value)),["routing_strategy_args",Object.keys(l).length>0?l:null]}return[l,s]}).filter(e=>null!=e)),s=(e,t=!1)=>null==e||"object"==typeof e&&!Array.isArray(e)&&0===Object.keys(e).length||t&&("number"!=typeof e||Number.isNaN(e))?null:e;return{routing_strategy:s(l.routing_strategy),allowed_fails:s(l.allowed_fails,!0),cooldown_time:s(l.cooldown_time,!0),num_retries:s(l.num_retries,!0),timeout:s(l.timeout,!0),retry_after:s(l.retry_after,!0),fallbacks:f.length>0?f:null,context_window_fallbacks:s(l.context_window_fallbacks),retry_policy:s(l.retry_policy),model_group_alias:s(l.model_group_alias),enable_tag_filtering:x.enableTagFiltering,routing_strategy_args:s(l.routing_strategy_args)}};(0,l.useEffect)(()=>{if(!p)return;let e=setTimeout(()=>{A.current=!0,p({router_settings:F()})},100);return()=>clearTimeout(e)},[x,f]);let M=Array.from(new Set(v.map(e=>e.model_group))).sort();return((0,l.useImperativeHandle)(h,()=>({getValue:()=>({router_settings:F()})})),e)?(0,t.jsx)("div",{className:"w-full",children:(0,t.jsxs)(r.TabGroup,{className:"w-full",children:[(0,t.jsxs)(i.TabList,{variant:"line",defaultValue:"1",className:"px-8 pt-4",children:[(0,t.jsx)(n.Tab,{value:"1",children:"Loadbalancing"}),(0,t.jsx)(n.Tab,{value:"2",children:"Fallbacks"})]}),(0,t.jsxs)(a.TabPanels,{className:"px-8 py-6",children:[(0,t.jsx)(s.TabPanel,{children:(0,t.jsx)(d.default,{value:x,onChange:y,routerFieldsMetadata:S,availableRoutingStrategies:k,routingStrategyDescriptions:T})}),(0,t.jsx)(s.TabPanel,{children:(0,t.jsx)(c.FallbackSelectionForm,{groups:_,onGroupsChange:e=>{j(e),b(e.filter(e=>e.primaryModel&&e.fallbackModels.length>0).map(e=>({[e.primaryModel]:e.fallbackModels})))},availableModels:M,maxGroups:5})})]})]})}):null});m.displayName="RouterSettingsAccordion",e.s(["default",0,m])},575260,e=>{"use strict";var t=e.i(843476),l=e.i(199133),s=e.i(482725),a=e.i(56456);e.s(["default",0,({projects:e,value:r,onChange:i,disabled:n,loading:o,teamId:d})=>{let c=d?e?.filter(e=>e.team_id===d):e;return(0,t.jsx)(l.Select,{showSearch:!0,placeholder:"Search or select a project",value:r,onChange:i,disabled:n,loading:o,allowClear:!0,notFoundContent:o?(0,t.jsx)(s.Spin,{indicator:(0,t.jsx)(a.LoadingOutlined,{spin:!0}),size:"small"}):void 0,filterOption:(e,t)=>{if(!t)return!1;let l=c?.find(e=>e.project_id===t.key);if(!l)return!1;let s=e.toLowerCase().trim(),a=(l.project_alias||"").toLowerCase(),r=(l.project_id||"").toLowerCase();return a.includes(s)||r.includes(s)},optionFilterProp:"children",children:!o&&c?.map(e=>(0,t.jsxs)(l.Select.Option,{value:e.project_id,children:[(0,t.jsx)("span",{className:"font-medium",children:e.project_alias||e.project_id})," ",(0,t.jsxs)("span",{className:"text-gray-500",children:["(",e.project_id,")"]})]},e.project_id))})}])},364769,e=>{"use strict";var t=e.i(843476),l=e.i(271645),s=e.i(237016),a=e.i(464571),r=e.i(888259);e.s(["default",0,({apiKey:e})=>{let[i,n]=(0,l.useState)(!1);return(0,t.jsxs)("div",{children:[(0,t.jsxs)("p",{className:"mb-2",children:["Please save this secret key somewhere safe and accessible. For security reasons,"," ",(0,t.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]}),(0,t.jsx)("p",{className:"text-sm text-gray-600 mt-3 mb-1",children:"Virtual Key:"}),(0,t.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,t.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal",margin:0},children:e})}),(0,t.jsx)(s.CopyToClipboard,{text:e,onCopy:()=>{n(!0),r.default.success("Key copied to clipboard"),setTimeout(()=>n(!1),2e3)},children:(0,t.jsx)(a.Button,{type:"primary",style:{marginTop:12},children:i?"Copied!":"Copy Virtual Key"})})]})}])},702597,e=>{"use strict";var t=e.i(843476),l=e.i(207082),s=e.i(109799),a=e.i(510674),r=e.i(109034),i=e.i(292639),n=e.i(135214),o=e.i(500330),d=e.i(827252),c=e.i(912598),u=e.i(677667),m=e.i(130643),p=e.i(898667),g=e.i(994388),h=e.i(309426),x=e.i(350967),y=e.i(599724),f=e.i(779241),b=e.i(629569),_=e.i(464571),j=e.i(808613),v=e.i(311451),w=e.i(212931),k=e.i(91739),N=e.i(199133),S=e.i(790848),C=e.i(262218),T=e.i(592968),I=e.i(898586),A=e.i(374009),L=e.i(271645),F=e.i(708347),M=e.i(552130),O=e.i(557662),E=e.i(9314),P=e.i(860585),R=e.i(82946),B=e.i(392110),$=e.i(533882),D=e.i(844565),V=e.i(651904),z=e.i(939510),U=e.i(460285),G=e.i(663435),K=e.i(363256),q=e.i(575260),W=e.i(371455),H=e.i(128233),Q=e.i(319312),J=e.i(833400),Y=e.i(355619),X=e.i(75921),Z=e.i(234713),ee=e.i(390605),et=e.i(727749),el=e.i(602869),es=e.i(364769),ea=e.i(435451),er=e.i(916940);let{Option:ei}=N.Select,en=async(e,t,l,s)=>{try{if(null===e||null===t)return[];if(null!==l)return(await (0,el.modelAvailableCall)(l,e,t,!0,s,!0)).data.map(e=>e.id);return[]}catch(e){return console.error("Error fetching user models:",e),[]}},eo=async(e,t,l,s)=>{try{if(null===e||null===t)return;if(null!==l){let a=(await (0,el.modelAvailableCall)(l,e,t)).data.map(e=>e.id);s(a)}}catch(e){console.error("Error fetching user models:",e)}};e.s(["default",0,({team:e,teams:ed,data:ec,addKey:eu,autoOpenCreate:em,prefillData:ep})=>{let{accessToken:eg,userId:eh,userRole:ex,premiumUser:ey}=(0,n.default)(),ef=ey||null!=ex&&F.rolesWithWriteAccess.includes(ex),{data:eb,isLoading:e_}=(0,s.useOrganizations)(),{data:ej,isLoading:ev}=(0,a.useProjects)(),{data:ew}=(0,i.useUISettings)(),{data:ek}=(0,r.useTags)(),eN=!!ew?.values?.enable_projects_ui,eS=!!ew?.values?.disable_custom_api_keys,eC=ek?Object.values(ek).map(e=>({value:e.name,label:e.name})):[],eT=(0,c.useQueryClient)(),[eI]=j.Form.useForm(),[eA,eL]=(0,L.useState)(!1),[eF,eM]=(0,L.useState)(null),[eO,eE]=(0,L.useState)(null),[eP,eR]=(0,L.useState)([]),[eB,e$]=(0,L.useState)([]),[eD,eV]=(0,L.useState)("you"),[ez,eU]=(0,L.useState)(!1),[eG,eK]=(0,L.useState)(null),[eq,eW]=(0,L.useState)([]),[eH,eQ]=(0,L.useState)([]),[eJ,eY]=(0,L.useState)([]),[eX,eZ]=(0,L.useState)([]),[e0,e1]=(0,L.useState)(e),[e4,e2]=(0,L.useState)(null),[e3,e6]=(0,L.useState)(null),[e5,e7]=(0,L.useState)(!1),[e8,e9]=(0,L.useState)(null),[te,tt]=(0,L.useState)({}),[tl,ts]=(0,L.useState)([]),[ta,tr]=(0,L.useState)(!1),[ti,tn]=(0,L.useState)([]),[to,td]=(0,L.useState)([]),[tc,tu]=(0,L.useState)("llm_api"),[tm,tp]=(0,L.useState)({}),[tg,th]=(0,L.useState)(!1),[tx,ty]=(0,L.useState)("30d"),[tf,tb]=(0,L.useState)(null),[t_,tj]=(0,L.useState)([]),[tv,tw]=(0,L.useState)([]),[tk,tN]=(0,L.useState)({}),[tS,tC]=(0,L.useState)(0),[tT,tI]=(0,L.useState)(0),[tA,tL]=(0,L.useState)([]),[tF,tM]=(0,L.useState)(null),tO=j.Form.useWatch("models",eI)??[],tE=()=>{eL(!1),eI.resetFields(),eZ([]),td([]),tu("llm_api"),tp({}),th(!1),ty("30d"),tb(null),tI(e=>e+1),tM(null),e2(null),e6(null),tj([]),tw([]),tN({}),tC(e=>e+1)},tP=()=>{eL(!1),eM(null),e1(null),eI.resetFields(),eZ([]),td([]),tu("llm_api"),tp({}),th(!1),ty("30d"),tb(null),tI(e=>e+1),tM(null),e2(null),e6(null),tj([]),tw([]),tN({}),tC(e=>e+1)};(0,L.useEffect)(()=>{eh&&ex&&eg&&eo(eh,ex,eg,eR)},[eg,eh,ex]),(0,L.useEffect)(()=>{eg&&(0,el.getAgentsList)(eg).then(e=>tL(e?.agents||[])).catch(()=>tL([]))},[eg]),(0,L.useEffect)(()=>{let e=async()=>{try{let e=(await (0,el.getPoliciesList)(eg)).policies.map(e=>e.policy_name);eQ(e)}catch(e){console.error("Failed to fetch policies:",e)}},t=async()=>{try{let e=await (0,el.getPromptsList)(eg);eY(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}};(async()=>{try{let e=(await (0,el.getGuardrailsList)(eg)).guardrails.map(e=>e.guardrail_name);eW(e)}catch(e){console.error("Failed to fetch guardrails:",e)}})(),e(),t()},[eg]),(0,L.useEffect)(()=>{(async()=>{try{if(eg){let e=sessionStorage.getItem("possibleUserRoles");if(e)tt(JSON.parse(e));else{let e=await (0,el.getPossibleUserRoles)(eg);sessionStorage.setItem("possibleUserRoles",JSON.stringify(e)),tt(e)}}}catch(e){console.error("Error fetching possible user roles:",e)}})()},[eg]),(0,L.useEffect)(()=>{if(em&&!ez&&ed&&ex&&F.rolesWithWriteAccess.includes(ex)&&(eL(!0),eU(!0),ep)){if(ep.owned_by&&("another_user"===ep.owned_by&&"Admin"!==ex?eV("you"):eV(ep.owned_by)),ep.team_id){let e=ed?.find(e=>e.team_id===ep.team_id)||null;e&&(e1(e),eI.setFieldsValue({team_id:ep.team_id}))}ep.key_alias&&eI.setFieldsValue({key_alias:ep.key_alias}),ep.models&&ep.models.length>0&&eK(ep.models),ep.key_type&&(tu(ep.key_type),eI.setFieldsValue({key_type:ep.key_type}))}},[em,ep,ed,ez,eI,ex]);let tR=eB.includes("no-default-models")&&!e0,tB=async e=>{try{let t,s=e?.key_alias??"",a=e?.team_id??null;if((ec?.filter(e=>e.team_id===a).map(e=>e.key_alias)??[]).includes(s))throw Error(`Key alias ${s} already exists for team with ID ${a}, please provide another key alias`);if(et.default.info("Making API Call"),eL(!0),"you"===eD)e.user_id=eh;else if("agent"===eD){if(!tF)return void et.default.fromBackend("Please select an agent");e.agent_id=tF}let r={};try{r=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}if("service_account"===eD&&(r.service_account_id=e.key_alias),eX.length>0&&(r={...r,logging:eX.filter(e=>e.callback_name)}),to.length>0){let e=(0,O.mapDisplayToInternalNames)(to);r={...r,litellm_disabled_callbacks:e}}if(tg&&(e.auto_rotate=!0,e.rotation_interval=tx),e.duration&&""!==e.duration.trim()||(e.duration=null),e.metadata=JSON.stringify(r),e.disable_global_guardrails||delete e.disable_global_guardrails,e.allowed_vector_store_ids&&e.allowed_vector_store_ids.length>0&&(e.object_permission={vector_stores:e.allowed_vector_store_ids},delete e.allowed_vector_store_ids),e.allowed_mcp_servers_and_groups&&(e.allowed_mcp_servers_and_groups.servers?.length>0||e.allowed_mcp_servers_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{servers:t,accessGroups:l}=e.allowed_mcp_servers_and_groups;t&&t.length>0&&(e.object_permission.mcp_servers=t),l&&l.length>0&&(e.object_permission.mcp_access_groups=l),delete e.allowed_mcp_servers_and_groups}let i=e.mcp_tool_permissions||{};if(Object.keys(i).length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_tool_permissions=i),delete e.mcp_tool_permissions,e.allowed_mcp_access_groups&&e.allowed_mcp_access_groups.length>0&&(e.object_permission||(e.object_permission={}),e.object_permission.mcp_access_groups=e.allowed_mcp_access_groups,delete e.allowed_mcp_access_groups),e.allowed_agents_and_groups&&(e.allowed_agents_and_groups.agents?.length>0||e.allowed_agents_and_groups.accessGroups?.length>0)){e.object_permission||(e.object_permission={});let{agents:t,accessGroups:l}=e.allowed_agents_and_groups;t&&t.length>0&&(e.object_permission.agents=t),l&&l.length>0&&(e.object_permission.agent_access_groups=l),delete e.allowed_agents_and_groups}Object.keys(tm).length>0&&(e.aliases=JSON.stringify(tm)),tf?.router_settings&&Object.values(tf.router_settings).some(e=>null!=e&&""!==e)&&(e.router_settings=tf.router_settings);let n=t_.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);n.length>0&&(e.budget_limits=n);let{tag_rpm_limit:o}=(0,J.tagRowsToLimits)(tv);Object.keys(o).length>0&&(e.tag_rpm_limit=o),Object.keys(tk).length>0&&(e.budget_fallbacks=tk),t="service_account"===eD?await (0,el.keyCreateServiceAccountCall)(eg,e):await (0,el.keyCreateCall)(eg,eh,e),eu(t),eT.invalidateQueries({queryKey:l.keyKeys.lists()}),eM(t.key),eE(t.soft_budget),et.default.success("Virtual Key Created"),eI.resetFields(),tj([]),tw([]),tN({}),tC(e=>e+1),localStorage.removeItem("userData"+eh)}catch(t){let e=(e=>{let t;if(!(t=!e||"object"!=typeof e||e instanceof Error?String(e):JSON.stringify(e)).includes("/key/generate")&&!t.includes("KeyManagementRoutes.KEY_GENERATE"))return`Error creating the key: ${e}`;let l=t;try{if(!e||"object"!=typeof e||e instanceof Error){let e=t.match(/\{[\s\S]*\}/);if(e){let t=JSON.parse(e[0]),s=t?.error||t;s?.message&&(l=s.message)}}else{let t=e?.error||e;t?.message&&(l=t.message)}}catch(e){}return t.includes("team_member_permission_error")||l.includes("Team member does not have permissions")?"Team member does not have permission to generate key for this team. Ask your proxy admin to configure the team member permission settings.":`Error creating the key: ${e}`})(t);et.default.fromBackend(e)}};(0,L.useEffect)(()=>{if(e3){let e=ej?.find(e=>e.project_id===e3);e$(e?.models??[]),eI.setFieldValue("models",[]);return}eh&&ex&&eg&&en(eh,ex,eg,e0?.team_id??null).then(e=>{e$((0,Y.excludeProxyWideSentinel)(Array.from(new Set([...e0?.models??[],...e]))))}),eG||eI.setFieldValue("models",[]),eI.setFieldValue("allowed_mcp_servers_and_groups",{servers:[],accessGroups:[]})},[e0,e3,eg,eh,ex,eI]),(0,L.useEffect)(()=>{if(!eG||0===eG.length||!eB||0===eB.length)return;let e=eG.filter(e=>eB.includes(e));e.length>0&&eI.setFieldsValue({models:e}),eK(null)},[eG,eB,eI]),(0,L.useEffect)(()=>{if(!e3||!ed)return;let e=ej?.find(e=>e.project_id===e3);if(!e?.team_id||e0?.team_id===e.team_id)return;let t=ed.find(t=>t.team_id===e.team_id)||null;t&&(e1(t),eI.setFieldValue("team_id",t.team_id))},[ed,e3,ej]);let t$=async e=>{if(!e)return void ts([]);tr(!0);try{let t=new URLSearchParams;if(t.append("user_email",e),null==eg)return;let l=(await (0,el.userFilterUICall)(eg,t)).map(e=>({label:`${e.user_email} (${e.user_id})`,value:e.user_id,user:e}));ts(l)}catch(e){console.error("Error fetching users:",e),et.default.fromBackend("Failed to search for users")}finally{tr(!1)}},tD=(0,L.useCallback)((0,A.default)(e=>t$(e),300),[eg]);return(0,t.jsxs)("div",{children:[ex&&F.rolesWithWriteAccess.includes(ex)&&(0,t.jsx)(g.Button,{className:"mx-auto",onClick:()=>eL(!0),"data-testid":"create-key-button",children:"+ Create New Key"}),(0,t.jsx)(w.Modal,{open:eA,width:1e3,footer:null,onOk:tE,onCancel:tP,children:(0,t.jsxs)(j.Form,{form:eI,onFinish:tB,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Ownership"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Owned By"," ",(0,t.jsx)(T.Tooltip,{title:"Select who will own this Virtual Key",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),className:"mb-4",children:(0,t.jsxs)(k.Radio.Group,{onChange:e=>eV(e.target.value),value:eD,children:[(0,t.jsx)(k.Radio,{value:"you",children:"You"}),(0,t.jsx)(k.Radio,{value:"service_account",children:"Service Account"}),"Admin"===ex&&(0,t.jsx)(k.Radio,{value:"another_user",children:"Another User"}),(0,t.jsxs)(k.Radio,{value:"agent",children:["Agent ",(0,t.jsx)(C.Tag,{color:"purple",children:"New"})]})]})}),"another_user"===eD&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["User ID"," ",(0,t.jsx)(T.Tooltip,{title:"The user who will own this key and be responsible for its usage",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"user_id",className:"mt-4",rules:[{required:"another_user"===eD,message:"Please input the user ID of the user you are assigning the key to"}],children:(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{style:{display:"flex",marginBottom:"8px"},children:[(0,t.jsx)(N.Select,{showSearch:!0,placeholder:"Type email to search for users",filterOption:!1,onSearch:e=>{tD(e)},onSelect:(e,t)=>{let l;return l=t.user,void eI.setFieldsValue({user_id:l.user_id})},options:tl,loading:ta,allowClear:!0,style:{width:"100%"},notFoundContent:ta?"Searching...":"No users found"}),(0,t.jsx)(_.Button,{onClick:()=>e7(!0),style:{marginLeft:"8px"},children:"Create User"})]}),(0,t.jsx)("div",{className:"text-xs text-gray-500",children:"Search by email to find users"})]})}),"agent"===eD&&(0,t.jsxs)("div",{className:"mt-4 p-4 bg-purple-50 border border-purple-200 rounded-md",children:[(0,t.jsx)("div",{className:"mb-3",children:(0,t.jsxs)("span",{className:"text-sm font-medium text-gray-700",children:["Select Agent ",(0,t.jsx)("span",{className:"text-red-500",children:"*"})]})}),(0,t.jsx)(N.Select,{showSearch:!0,placeholder:"Select an agent",style:{width:"100%"},value:tF,onChange:e=>tM(e),filterOption:(e,t)=>t?.label?.toLowerCase().includes(e.toLowerCase()),options:tA.map(e=>({label:e.agent_name||e.agent_id,value:e.agent_id}))}),(0,t.jsx)("div",{className:"text-xs text-gray-500 mt-2",children:"This key will be used by the selected agent to make requests to LiteLLM"})]}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(T.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",className:"mt-4",children:(0,t.jsx)(K.default,{organizations:eb,loading:e_,disabled:"Admin"!==ex,onChange:e=>{e2(e||null),e1(null),e6(null),eI.setFieldValue("team_id",void 0),eI.setFieldValue("project_id",void 0)}})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Team"," ",(0,t.jsx)(T.Tooltip,{title:"The team this key belongs to, which determines available models and budget limits",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"team_id",initialValue:e?e.team_id:null,className:"mt-4",rules:[{required:"service_account"===eD,message:"Please select a team for the service account"}],help:"service_account"===eD?"required":"",children:(0,t.jsx)(G.default,{disabled:null!==e3,organizationId:e4,onTeamSelect:e=>{e1(e),e6(null),eI.setFieldValue("project_id",void 0),e?.organization_id?(e2(e.organization_id),eI.setFieldValue("organization_id",e.organization_id)):e||(e2(null),eI.setFieldValue("organization_id",void 0))}})}),eN&&(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Project"," ",(0,t.jsx)(T.Tooltip,{title:"Assign this key to a project. Selecting a project will lock the team to the project's team.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"project_id",className:"mt-4",children:(0,t.jsx)(q.default,{projects:ej,teamId:e0?.team_id,loading:ev||!ed,onChange:e=>{if(!e){e6(null),e1(null),eI.setFieldValue("team_id",void 0);return}e6(e)}})})]}),tR&&(0,t.jsx)("div",{className:"mb-8 p-4 bg-blue-50 border border-blue-200 rounded-md",children:(0,t.jsx)(y.Text,{className:"text-blue-800 text-sm",children:"Please select a team to continue configuring your Virtual Key. If you do not see any teams, please contact your Proxy Admin to either provide you with access to models or to add you to a team."})}),!tR&&(0,t.jsxs)("div",{className:"mb-8",children:[(0,t.jsx)(b.Title,{className:"mb-4",children:"Key Details"}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["you"===eD||"another_user"===eD?"Key Name":"Service Account ID"," ",(0,t.jsx)(T.Tooltip,{title:"you"===eD||"another_user"===eD?"A descriptive name to identify this key":"Unique identifier for this service account",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_alias",rules:[{required:!0,message:`Please input a ${"you"===eD?"key name":"service account ID"}`}],help:"required",children:(0,t.jsx)(f.TextInput,{placeholder:""})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Models"," ",(0,t.jsx)(T.Tooltip,{title:"Select which models this key can access. Choose 'All Team Models' to grant access to all models available to the team. Leave empty to allow access to all models.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"models",rules:[],help:"management"===tc||"read_only"===tc?"Models field is disabled for this key type":"optional - leave empty to allow access to all models",className:"mt-4",children:(0,t.jsxs)(N.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:"management"===tc||"read_only"===tc,onChange:e=>{e.includes("all-team-models")?eI.setFieldsValue({models:["all-team-models"]}):e.includes("all-proxy-models")&&eI.setFieldsValue({models:["all-proxy-models"]})},children:[!e3&&e0&&(0,t.jsx)(ei,{value:"all-team-models",children:"All Team Models"},"all-team-models"),!e3&&!e0&&(0,t.jsx)(ei,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),eB.map(e=>(0,t.jsx)(ei,{value:e,disabled:(0,Y.hasAllModelsSentinel)(tO),children:(0,Y.getModelDisplayName)(e)},e))]})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Key Type"," ",(0,t.jsx)(T.Tooltip,{title:"Select the type of key to determine what routes and operations this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"key_type",initialValue:"llm_api",className:"mt-4",children:(0,t.jsxs)(N.Select,{defaultValue:"llm_api",placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",onChange:e=>{tu(e),("management"===e||"read_only"===e)&&eI.setFieldsValue({models:[]})},children:[(0,t.jsx)(ei,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"AI APIs"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(ei,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Management"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call only management routes (user/team/key management)"})]})}),(0,t.jsx)(ei,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)(I.Typography.Text,{strong:!0,children:"Full Access"}),(0,t.jsx)(I.Typography.Paragraph,{type:"secondary",style:{fontSize:11,margin:"2px 0 0"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})})]})})]}),!tR&&(0,t.jsx)("div",{className:"mb-8",children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)(b.Title,{className:"m-0",children:"Optional Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Max Budget (USD)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum amount in USD this key can spend. When reached, the key will be blocked from making further requests",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"max_budget",help:`Budget cannot exceed team max budget: $${e?.max_budget!==null&&e?.max_budget!==void 0?e?.max_budget:"unlimited"}`,rules:[{validator:async(t,l)=>{if(l&&e&&null!==e.max_budget&&l>e.max_budget)throw Error(`Budget cannot exceed team max budget: $${(0,o.formatNumberWithCommas)(e.max_budget,4)}`)}}],children:(0,t.jsx)(ea.default,{step:.01,precision:2,width:200})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Reset Budget"," ",(0,t.jsx)(T.Tooltip,{title:"How often the budget should reset. For example, setting 'daily' will reset the budget every 24 hours",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"budget_duration",help:`Team Reset Budget: ${e?.budget_duration!==null&&e?.budget_duration!==void 0?e?.budget_duration:"None"}`,children:(0,t.jsx)(P.default,{onChange:e=>eI.setFieldValue("budget_duration",e)})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(T.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(Q.BudgetWindowsEditor,{value:t_,onChange:tj})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(T.Tooltip,{title:"When a model exceeds its per-model budget (model_max_budget), requests automatically reroute to fallback models instead of failing. Configure per-model budgets in Advanced Settings.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(H.BudgetFallbacksEditor,{value:tk,onChange:tN,availableModels:eB},tS)}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Tokens per minute Limit (TPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of tokens this key can process per minute. Helps control usage and costs",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tpm_limit",help:`TPM cannot exceed team TPM limit: ${e?.tpm_limit!==null&&e?.tpm_limit!==void 0?e?.tpm_limit:"unlimited"}`,rules:[{validator:async(t,l)=>{if(l&&e&&null!==e.tpm_limit&&l>e.tpm_limit)throw Error(`TPM limit cannot exceed team TPM limit: ${e.tpm_limit}`)}}],children:(0,t.jsx)(ea.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"tpm",name:"tpm_limit_type",className:"mt-4",initialValue:null,form:eI,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Requests per minute Limit (RPM)"," ",(0,t.jsx)(T.Tooltip,{title:"Maximum number of API requests this key can make per minute. Helps prevent abuse and manage load",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"rpm_limit",help:`RPM cannot exceed team RPM limit: ${e?.rpm_limit!==null&&e?.rpm_limit!==void 0?e?.rpm_limit:"unlimited"}`,rules:[{validator:async(t,l)=>{if(l&&e&&null!==e.rpm_limit&&l>e.rpm_limit)throw Error(`RPM limit cannot exceed team RPM limit: ${e.rpm_limit}`)}}],children:(0,t.jsx)(ea.default,{step:1,width:400})}),(0,t.jsx)(z.default,{type:"rpm",name:"rpm_limit_type",className:"mt-4",initialValue:null,form:eI,showDetailedDescriptions:!0}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Per-Tag Rate Limits"," ",(0,t.jsx)(T.Tooltip,{title:"Scope rate limits to a request tag so each tag (e.g. a cell or group) gets its own RPM counter. Requests without a matching tag fall back to the key-level limit.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(J.TagRateLimitEditor,{value:tv,onChange:tw})}),(0,t.jsx)(j.Form.Item,{className:"mt-4",label:(0,t.jsxs)("span",{children:["Throttle on budget exceeded"," ",(0,t.jsx)(T.Tooltip,{title:"When this key exceeds its max budget, throttle its TPM/RPM to the globally configured percentage instead of blocking access entirely. Requires budget_exceeded_throttle_percentage in litellm_settings and a TPM/RPM limit on the key.",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"throttle_on_budget_exceeded",valuePropName:"checked",children:(0,t.jsx)(S.Switch,{checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"Apply safety guardrails to this key to filter content or enforce policies",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"guardrails",className:"mt-4",help:ef?"Select existing guardrails or enter new ones":"Premium feature - Upgrade to set guardrails by key",children:(0,t.jsx)(N.Select,{mode:"tags",style:{width:"100%"},disabled:!ef,placeholder:ef?"Select or enter guardrails":"Premium feature - Upgrade to set guardrails by key",options:eq.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(T.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/quick_start",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"disable_global_guardrails",className:"mt-4",valuePropName:"checked",help:ef?"Bypass global guardrails for this key":"Premium feature - Upgrade to disable global guardrails by key",children:(0,t.jsx)(S.Switch,{disabled:!ef,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(T.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"policies",className:"mt-4",help:ey?"Select existing policies or enter new ones":"Premium feature - Upgrade to set policies by key",children:(0,t.jsx)(N.Select,{mode:"tags",style:{width:"100%"},disabled:!ey,placeholder:ey?"Select or enter policies":"Premium feature - Upgrade to set policies by key",options:eH.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Prompts"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific prompt templates",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/prompt_management",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"prompts",className:"mt-4",help:ey?"Select existing prompts or enter new ones":"Premium feature - Upgrade to set prompts by key",children:(0,t.jsx)(N.Select,{mode:"tags",style:{width:"100%"},disabled:!ey,placeholder:ey?"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:eJ.map(e=>({value:e,label:e}))})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(T.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",className:"mt-4",help:"Select access groups to assign to this key",children:(0,t.jsx)(E.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Pass Through Routes"," ",(0,t.jsx)(T.Tooltip,{title:"Allow this key to use specific pass through routes",children:(0,t.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/pass_through",target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})})]}),name:"allowed_passthrough_routes",className:"mt-4",help:ey?"Select existing pass through routes or enter new ones":"Premium feature - Upgrade to set pass through routes by key",children:(0,t.jsx)(D.default,{onChange:e=>eI.setFieldValue("allowed_passthrough_routes",e),value:eI.getFieldValue("allowed_passthrough_routes"),accessToken:eg,placeholder:ey?"Select or enter pass through routes":"Premium feature - Upgrade to set pass through routes by key",disabled:!ey,teamId:e0?e0.team_id:null})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Vector Stores"," ",(0,t.jsx)(T.Tooltip,{title:"Select which vector stores this key can access. If none selected, the key will have access to all available vector stores",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_vector_store_ids",className:"mt-4",help:"Select vector stores this key can access. Leave empty for access to all vector stores",children:(0,t.jsx)(er.default,{onChange:e=>eI.setFieldValue("allowed_vector_store_ids",e),value:eI.getFieldValue("allowed_vector_store_ids"),accessToken:eg,placeholder:"Select vector stores (optional)"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Metadata"," ",(0,t.jsx)(T.Tooltip,{title:"JSON object with additional information about this key. Used for tracking or custom logic",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"metadata",className:"mt-4",children:(0,t.jsx)(v.Input.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Tags"," ",(0,t.jsx)(T.Tooltip,{title:"Tags for tracking spend and/or doing tag-based routing. Used for analytics and filtering",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"tags",className:"mt-4",help:"Tags for tracking spend and/or doing tag-based routing.",children:(0,t.jsx)(N.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",tokenSeparators:[","],options:eC})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"MCP Settings"})}),(0,t.jsxs)(m.AccordionBody,{children:[(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed MCP Servers"," ",(0,t.jsx)(T.Tooltip,{title:"Select which MCP servers or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_mcp_servers_and_groups",help:"Select MCP servers or access groups this key can access",children:(0,t.jsx)(X.default,{onChange:e=>eI.setFieldValue("allowed_mcp_servers_and_groups",e),value:eI.getFieldValue("allowed_mcp_servers_and_groups"),accessToken:eg,teamId:e0?.team_id??null,placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(j.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(v.Input,{type:"hidden"})}),(0,t.jsx)(j.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_mcp_servers_and_groups!==t.allowed_mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mt-6",children:(0,t.jsx)(ee.default,{accessToken:eg,selectedServers:(eI.getFieldValue("allowed_mcp_servers_and_groups")?.servers||[]).filter(e=>e!==Z.NO_MCP_SERVERS_SENTINEL),toolPermissions:eI.getFieldValue("mcp_tool_permissions")||{},onChange:e=>eI.setFieldsValue({mcp_tool_permissions:e})})})})]})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Agent Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(j.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Agents"," ",(0,t.jsx)(T.Tooltip,{title:"Select which agents or access groups this key can access",children:(0,t.jsx)(d.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_agents_and_groups",help:"Select agents or access groups this key can access",children:(0,t.jsx)(M.default,{onChange:e=>eI.setFieldValue("allowed_agents_and_groups",e),value:eI.getFieldValue("allowed_agents_and_groups"),accessToken:eg,placeholder:"Select agents or access groups (optional)"})})})]}),ey?(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eX,onChange:eZ,premiumUser:!0,disabledCallbacks:to,onDisabledCallbacksChange:td})})})]}):(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Key-level logging settings is an enterprise feature, get in touch -",(0,t.jsx)("a",{href:"https://www.litellm.ai/enterprise",target:"_blank",children:"https://www.litellm.ai/enterprise"})]}),placement:"top",children:(0,t.jsxs)("div",{style:{position:"relative"},children:[(0,t.jsx)("div",{style:{opacity:.5},children:(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Logging Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(V.default,{value:eX,onChange:eZ,premiumUser:!1,disabledCallbacks:to,onDisabledCallbacksChange:td})})})]})}),(0,t.jsx)("div",{style:{position:"absolute",inset:0,cursor:"not-allowed"}})]})}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Router Settings"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4 w-full",children:(0,t.jsx)(U.default,{accessToken:eg||"",value:tf||void 0,onChange:tb,modelData:eP.length>0?{data:eP.map(e=>({model_name:e}))}:void 0},tT)})})]},`router-settings-accordion-${tT}`),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Model Aliases"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsx)(y.Text,{className:"text-sm text-gray-600 mb-4",children:"Create custom aliases for models that can be used in API calls. This allows you to create shortcuts for specific models."}),(0,t.jsx)($.default,{accessToken:eg,initialModelAliases:tm,onAliasUpdate:tp,showExampleConfig:!1})]})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsx)("b",{children:"Key Lifecycle"})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(B.default,{form:eI,autoRotationEnabled:tg,onAutoRotationChange:th,rotationInterval:tx,onRotationIntervalChange:ty,isCreateMode:!0})})}),(0,t.jsx)(j.Form.Item,{name:"duration",hidden:!0,initialValue:null,children:(0,t.jsx)(v.Input,{})})]}),(0,t.jsxs)(u.Accordion,{className:"mt-4 mb-4",children:[(0,t.jsx)(p.AccordionHeader,{children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("b",{children:"Advanced Settings"}),(0,t.jsx)(T.Tooltip,{title:(0,t.jsxs)("span",{children:["Learn more about advanced settings in our"," ",(0,t.jsx)("a",{href:el.proxyBaseUrl?`${el.proxyBaseUrl}/#/key%20management/generate_key_fn_key_generate_post`:"/#/key%20management/generate_key_fn_key_generate_post",target:"_blank",rel:"noopener noreferrer",className:"text-blue-400 hover:text-blue-300",children:"documentation"})]}),children:(0,t.jsx)(d.InfoCircleOutlined,{className:"text-gray-400 hover:text-gray-300 cursor-help"})})]})}),(0,t.jsx)(m.AccordionBody,{children:(0,t.jsx)(R.default,{schemaComponent:"GenerateKeyRequest",form:eI,excludedFields:["key_alias","team_id","organization_id","models","duration","metadata","tags","guardrails","max_budget","budget_duration","tpm_limit","rpm_limit",...eS?["key"]:[]]})})]})]})]})}),(0,t.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,t.jsx)(_.Button,{htmlType:"submit",disabled:tR,style:{opacity:tR?.5:1},children:"Create Key"})})]})}),e5&&(0,t.jsx)(w.Modal,{title:"Create New User",open:e5,onCancel:()=>e7(!1),footer:null,width:800,children:(0,t.jsx)(W.CreateUserButton,{userID:eh,accessToken:eg,teams:ed,possibleUIRoles:te,onUserCreated:e=>{e9(e),eI.setFieldsValue({user_id:e}),e7(!1)},isEmbedded:!0})}),eF&&(0,t.jsx)(w.Modal,{open:eA,onOk:tE,onCancel:tP,footer:null,children:(0,t.jsxs)(x.Grid,{numItems:1,className:"gap-2 w-full",children:[(0,t.jsx)(b.Title,{children:"Save your Key"}),(0,t.jsx)(h.Col,{numColSpan:1,children:null!=eF?(0,t.jsx)(es.default,{apiKey:eF}):(0,t.jsx)(y.Text,{children:"Key being created, this might take 30s"})})]})})]})},"fetchTeamModels",0,en,"fetchUserModels",0,eo],702597)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0tbzoqict3-mi.js b/litellm/proxy/_experimental/out/_next/static/chunks/0tbzoqict3-mi.js new file mode 100644 index 00000000000..a0dca3b36ea --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0tbzoqict3-mi.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,867384,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"}}]},name:"ellipsis",theme:"outlined"};var r=e.i(9583),o=n.forwardRef(function(e,o){return n.createElement(r.default,(0,t.default)({},e,{ref:o,icon:l}))});e.s(["default",0,o],867384)},878081,465394,452741,905054,259792,983409,375565,e=>{"use strict";var t=e.i(931067),n=e.i(211577),l=e.i(392221),r=e.i(703923),o=e.i(707067),a=e.i(343794),u=e.i(611935),i=e.i(271645),c=e.i(404948),f=e.i(963188),s=c.default.ESC,d=c.default.TAB,p=(0,i.forwardRef)(function(e,t){var n=e.overlay,l=e.arrow,r=e.prefixCls,o=(0,i.useMemo)(function(){return"function"==typeof n?n():n},[n]),a=(0,u.composeRef)(t,(0,u.getNodeRef)(o));return i.default.createElement(i.default.Fragment,null,l&&i.default.createElement("div",{className:"".concat(r,"-arrow")}),i.default.cloneElement(o,{ref:(0,u.supportRef)(o)?a:void 0}))}),v={adjustX:1,adjustY:1},m=[0,0];let b={topLeft:{points:["bl","tl"],overflow:v,offset:[0,-4],targetOffset:m},top:{points:["bc","tc"],overflow:v,offset:[0,-4],targetOffset:m},topRight:{points:["br","tr"],overflow:v,offset:[0,-4],targetOffset:m},bottomLeft:{points:["tl","bl"],overflow:v,offset:[0,4],targetOffset:m},bottom:{points:["tc","bc"],overflow:v,offset:[0,4],targetOffset:m},bottomRight:{points:["tr","br"],overflow:v,offset:[0,4],targetOffset:m}};var y=["arrow","prefixCls","transitionName","animation","align","placement","placements","getPopupContainer","showAction","hideAction","overlayClassName","overlayStyle","visible","trigger","autoFocus","overlay","children","onVisibleChange"];let h=i.default.forwardRef(function(e,c){var v,m,h,g,C,E,w,R,M,x,k,N,P,S,I=e.arrow,K=void 0!==I&&I,O=e.prefixCls,A=void 0===O?"rc-dropdown":O,T=e.transitionName,L=e.animation,D=e.align,_=e.placement,V=e.placements,F=e.getPopupContainer,z=e.showAction,j=e.hideAction,B=e.overlayClassName,W=e.overlayStyle,H=e.visible,U=e.trigger,q=void 0===U?["hover"]:U,G=e.autoFocus,X=e.overlay,Y=e.children,J=e.onVisibleChange,Q=(0,r.default)(e,y),Z=i.default.useState(),$=(0,l.default)(Z,2),ee=$[0],et=$[1],en="visible"in e?H:ee,el=i.default.useRef(null),er=i.default.useRef(null),eo=i.default.useRef(null);i.default.useImperativeHandle(c,function(){return el.current});var ea=function(e){et(e),null==J||J(e)};m=(v={visible:en,triggerRef:eo,onVisibleChange:ea,autoFocus:G,overlayRef:er}).visible,h=v.triggerRef,g=v.onVisibleChange,C=v.autoFocus,E=v.overlayRef,w=i.useRef(!1),R=function(){if(m){var e,t;null==(e=h.current)||null==(t=e.focus)||t.call(e),null==g||g(!1)}},M=function(){var e;return null!=(e=E.current)&&!!e.focus&&(E.current.focus(),w.current=!0,!0)},x=function(e){switch(e.keyCode){case s:R();break;case d:var t=!1;w.current||(t=M()),t?e.preventDefault():R()}},i.useEffect(function(){return m?(window.addEventListener("keydown",x),C&&(0,f.default)(M,3),function(){window.removeEventListener("keydown",x),w.current=!1}):function(){w.current=!1}},[m]);var eu=function(){return i.default.createElement(p,{ref:er,overlay:X,prefixCls:A,arrow:K})},ei=i.default.cloneElement(Y,{className:(0,a.default)(null==(S=Y.props)?void 0:S.className,en&&(void 0!==(k=e.openClassName)?k:"".concat(A,"-open"))),ref:(0,u.supportRef)(Y)?(0,u.composeRef)(eo,(0,u.getNodeRef)(Y)):void 0}),ec=j;return ec||-1===q.indexOf("contextMenu")||(ec=["click"]),i.default.createElement(o.default,(0,t.default)({builtinPlacements:void 0===V?b:V},Q,{prefixCls:A,ref:el,popupClassName:(0,a.default)(B,(0,n.default)({},"".concat(A,"-show-arrow"),K)),popupStyle:W,action:q,showAction:z,hideAction:ec,popupPlacement:void 0===_?"bottomLeft":_,popupAlign:D,popupTransitionName:T,popupAnimation:L,popupVisible:en,stretch:(N=e.minOverlayWidthMatchTrigger,P=e.alignPoint,"minOverlayWidthMatchTrigger"in e?N:!P)?"minWidth":"",popup:"function"==typeof X?eu:eu(),onPopupVisibleChange:ea,onPopupClick:function(t){var n=e.onOverlayClick;et(!1),n&&n(t)},getPopupContainer:F}),ei)});e.s(["default",0,h],878081),e.i(247167);var g=e.i(209428),C=e.i(8211),E=e.i(658315),w=e.i(914949),R=e.i(929123),M=e.i(883110),x=e.i(174080),k=i.createContext(null);function N(e,t){return void 0===e?null:"".concat(e,"-").concat(t)}function P(e){return N(i.useContext(k),e)}var S=e.i(182585),I=["children","locked"],K=i.createContext(null);function O(e){var t=e.children,n=e.locked,l=(0,r.default)(e,I),o=i.useContext(K),a=(0,S.default)(function(){var e;return e=(0,g.default)({},o),Object.keys(l).forEach(function(t){var n=l[t];void 0!==n&&(e[t]=n)}),e},[o,l],function(e,t){return!n&&(e[0]!==t[0]||!(0,R.default)(e[1],t[1],!0))});return i.createElement(K.Provider,{value:a},t)}var A=i.createContext(null);function T(){return i.useContext(A)}var L=i.createContext([]);function D(e){var t=i.useContext(L);return i.useMemo(function(){return void 0!==e?[].concat((0,C.default)(t),[e]):t},[t,e])}var _=i.createContext(null);e.s(["PathRegisterContext",0,A,"PathTrackerContext",0,L,"PathUserContext",0,_,"useFullPath",0,D,"useMeasure",0,T],465394);var V=i.createContext({}),F=e.i(606262);function z(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if((0,F.default)(e)){var n=e.nodeName.toLowerCase(),l=["input","select","textarea","button"].includes(n)||e.isContentEditable||"a"===n&&!!e.getAttribute("href"),r=e.getAttribute("tabindex"),o=Number(r),a=null;return r&&!Number.isNaN(o)?a=o:l&&null===a&&(a=0),l&&e.disabled&&(a=null),null!==a&&(a>=0||t&&a<0)}return!1}var j=c.default.LEFT,B=c.default.RIGHT,W=c.default.UP,H=c.default.DOWN,U=c.default.ENTER,q=c.default.ESC,G=c.default.HOME,X=c.default.END,Y=[W,H,j,B];function J(e,t){return(function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=(0,C.default)(e.querySelectorAll("*")).filter(function(e){return z(e,t)});return z(e,t)&&n.unshift(e),n})(e,!0).filter(function(e){return t.has(e)})}function Q(e,t,n){var l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1;if(!e)return null;var r=J(e,t),o=r.length,a=r.findIndex(function(e){return n===e});return l<0?-1===a?a=o-1:a-=1:l>0&&(a+=1),r[a=(a+o)%o]}var Z=function(e,t){var n=new Set,l=new Map,r=new Map;return e.forEach(function(e){var o=document.querySelector("[data-menu-id='".concat(N(t,e),"']"));o&&(n.add(o),r.set(o,e),l.set(e,o))}),{elements:n,key2element:l,element2key:r}},$="__RC_UTIL_PATH_SPLIT__",ee=function(e){return e.join($)},et="rc-menu-more";function en(e){var t=i.useRef(e);t.current=e;var n=i.useCallback(function(){for(var e,n=arguments.length,l=Array(n),r=0;r1&&(w.motionAppear=!1);var R=w.onVisibleChanged;return(w.onVisibleChanged=function(e){return m.current||e||C(!0),null==R?void 0:R(e)},h)?null:i.createElement(O,{mode:u,locked:!m.current},i.createElement(eK.default,(0,t.default)({visible:E},w,{forceRender:s,removeOnLeave:!1,leavedClassName:"".concat(f,"-hidden")}),function(e){var t=e.className,l=e.style;return i.createElement(ew,{id:n,className:t,style:l},a)}))}var eA=["style","className","title","eventKey","warnKey","disabled","internalPopupClose","children","itemIcon","expandIcon","popupClassName","popupOffset","popupStyle","onClick","onMouseEnter","onMouseLeave","onTitleClick","onTitleMouseEnter","onTitleMouseLeave"],eT=["active"],eL=i.forwardRef(function(e,o){var u=e.style,c=e.className,f=e.title,s=e.eventKey,d=(e.warnKey,e.disabled),p=e.internalPopupClose,v=e.children,m=e.itemIcon,b=e.expandIcon,y=e.popupClassName,h=e.popupOffset,C=e.popupStyle,w=e.onClick,R=e.onMouseEnter,M=e.onMouseLeave,x=e.onTitleClick,k=e.onTitleMouseEnter,N=e.onTitleMouseLeave,S=(0,r.default)(e,eA),I=P(s),A=i.useContext(K),T=A.prefixCls,L=A.mode,F=A.openKeys,z=A.disabled,j=A.overflowDisabled,B=A.activeKey,W=A.selectedKeys,H=A.itemIcon,U=A.expandIcon,q=A.onItemClick,G=A.onOpenChange,X=A.onActive,Y=i.useContext(V)._internalRenderSubMenuItem,J=i.useContext(_).isSubPathKey,Q=D(),Z="".concat(T,"-submenu"),$=z||d,ee=i.useRef(),et=i.useRef(),el=null!=b?b:U,er=F.includes(s),eo=!j&&er,ea=J(W,s),eu=ef(s,$,k,N),ei=eu.active,ec=(0,r.default)(eu,eT),ep=i.useState(!1),em=(0,l.default)(ep,2),eb=em[0],ey=em[1],eh=function(e){$||ey(e)},eg=i.useMemo(function(){return ei||"inline"!==L&&(eb||J([B],s))},[L,ei,B,eb,s,J]),eC=es(Q.length),eE=en(function(e){null==w||w(ev(e)),q(e)}),eR=I&&"".concat(I,"-popup"),eM=i.useMemo(function(){return i.createElement(ed,{icon:"horizontal"!==L?el:void 0,props:(0,g.default)((0,g.default)({},e),{},{isOpen:eo,isSubMenu:!0})},i.createElement("i",{className:"".concat(Z,"-arrow")}))},[L,el,e,eo,Z]),ex=i.createElement("div",(0,t.default)({role:"menuitem",style:eC,className:"".concat(Z,"-title"),tabIndex:$?null:-1,ref:ee,title:"string"==typeof f?f:null,"data-menu-id":j&&I?null:I,"aria-expanded":eo,"aria-haspopup":!0,"aria-controls":eR,"aria-disabled":$,onClick:function(e){$||(null==x||x({key:s,domEvent:e}),"inline"===L&&G(s,!er))},onFocus:function(){X(s)}},ec),f,eM),ek=i.useRef(L);if("inline"!==L&&Q.length>1?ek.current="vertical":ek.current=L,!j){var eN=ek.current;ex=i.createElement(eI,{mode:eN,prefixCls:Z,visible:!p&&eo&&"inline"!==L,popupClassName:y,popupOffset:h,popupStyle:C,popup:i.createElement(O,{mode:"horizontal"===eN?"vertical":eN},i.createElement(ew,{id:eR,ref:et},v)),disabled:$,onVisibleChange:function(e){"inline"!==L&&G(s,e)}},ex)}var eP=i.createElement(E.default.Item,(0,t.default)({ref:o,role:"none"},S,{component:"li",style:u,className:(0,a.default)(Z,"".concat(Z,"-").concat(L),c,(0,n.default)((0,n.default)((0,n.default)((0,n.default)({},"".concat(Z,"-open"),eo),"".concat(Z,"-active"),eg),"".concat(Z,"-selected"),ea),"".concat(Z,"-disabled"),$)),onMouseEnter:function(e){eh(!0),null==R||R({key:s,domEvent:e})},onMouseLeave:function(e){eh(!1),null==M||M({key:s,domEvent:e})}}),ex,!j&&i.createElement(eO,{id:eR,open:eo,keyPath:Q},v));return Y&&(eP=Y(eP,e,{selected:ea,active:eg,open:eo,disabled:$})),i.createElement(O,{onItemClick:eE,mode:"horizontal"===L?"vertical":L,itemIcon:null!=m?m:H,expandIcon:el},eP)}),eD=i.forwardRef(function(e,n){var l,r=e.eventKey,o=e.children,a=D(r),u=eM(o,a),c=T();return i.useEffect(function(){if(c)return c.registerPath(r,a),function(){c.unregisterPath(r,a)}},[a]),l=c?u:i.createElement(eL,(0,t.default)({ref:n},e),u),i.createElement(L.Provider,{value:a},l)});e.s(["default",0,eD],905054);var e_=e.i(410160);function eV(e){var t=e.className,n=e.style,l=i.useContext(K).prefixCls;return T()?null:i.createElement("li",{role:"separator",className:(0,a.default)("".concat(l,"-item-divider"),t),style:n})}e.s(["default",0,eV],259792);var eF=["className","title","eventKey","children"],ez=i.forwardRef(function(e,n){var l=e.className,o=e.title,u=(e.eventKey,e.children),c=(0,r.default)(e,eF),f=i.useContext(K).prefixCls,s="".concat(f,"-item-group");return i.createElement("li",(0,t.default)({ref:n,role:"presentation"},c,{onClick:function(e){return e.stopPropagation()},className:(0,a.default)(s,l)}),i.createElement("div",{role:"presentation",className:"".concat(s,"-title"),title:"string"==typeof o?o:void 0},o),i.createElement("ul",{role:"group",className:"".concat(s,"-list")},u))}),ej=i.forwardRef(function(e,n){var l=e.eventKey,r=eM(e.children,D(l));return T()?r:i.createElement(ez,(0,t.default)({ref:n},(0,ec.default)(e,["warnKey"])),r)});e.s(["default",0,ej],983409);var eB=["label","children","key","type","extra"];function eW(e,n,l,o,a){var u=e,c=(0,g.default)({divider:eV,item:eC,group:ej,submenu:eD},o);return n&&(u=function e(n,l,o){var a=l.item,u=l.group,c=l.submenu,f=l.divider;return(n||[]).map(function(n,s){if(n&&"object"===(0,e_.default)(n)){var d=n.label,p=n.children,v=n.key,m=n.type,b=n.extra,y=(0,r.default)(n,eB),h=null!=v?v:"tmp-".concat(s);return p||"group"===m?"group"===m?i.createElement(u,(0,t.default)({key:h},y,{title:d}),e(p,l,o)):i.createElement(c,(0,t.default)({key:h},y,{title:d}),e(p,l,o)):"divider"===m?i.createElement(f,(0,t.default)({key:h},y)):i.createElement(a,(0,t.default)({key:h},y,{extra:b}),d,(!!b||0===b)&&i.createElement("span",{className:"".concat(o,"-item-extra")},b))}return null}).filter(function(e){return e})}(n,c,a)),eM(u,l)}var eH=["prefixCls","rootClassName","style","className","tabIndex","items","children","direction","id","mode","inlineCollapsed","disabled","disabledOverflow","subMenuOpenDelay","subMenuCloseDelay","forceSubMenuRender","defaultOpenKeys","openKeys","activeKey","defaultActiveFirst","selectable","multiple","defaultSelectedKeys","selectedKeys","onSelect","onDeselect","inlineIndent","motion","defaultMotions","triggerSubMenuAction","builtinPlacements","itemIcon","expandIcon","overflowedIndicator","overflowedIndicatorPopupClassName","getPopupContainer","onClick","onOpenChange","onKeyDown","openAnimation","openTransitionName","_internalRenderMenuItem","_internalRenderSubMenuItem","_internalComponents"],eU=[],eq=i.forwardRef(function(e,o){var u,c,s,d,p,v,m,b,y,h,M,N,P,S,I,K,T,L,D,F,z,eo,ea,eu,ei,ec,ef=e.prefixCls,es=void 0===ef?"rc-menu":ef,ed=e.rootClassName,ep=e.style,em=e.className,eb=e.tabIndex,ey=e.items,eh=e.children,eg=e.direction,eE=e.id,ew=e.mode,eR=void 0===ew?"vertical":ew,eM=e.inlineCollapsed,ex=e.disabled,ek=e.disabledOverflow,eN=e.subMenuOpenDelay,eP=e.subMenuCloseDelay,eS=e.forceSubMenuRender,eI=e.defaultOpenKeys,eK=e.openKeys,eO=e.activeKey,eA=e.defaultActiveFirst,eT=e.selectable,eL=void 0===eT||eT,e_=e.multiple,eV=void 0!==e_&&e_,eF=e.defaultSelectedKeys,ez=e.selectedKeys,ej=e.onSelect,eB=e.onDeselect,eq=e.inlineIndent,eG=e.motion,eX=e.defaultMotions,eY=e.triggerSubMenuAction,eJ=e.builtinPlacements,eQ=e.itemIcon,eZ=e.expandIcon,e$=e.overflowedIndicator,e0=void 0===e$?"...":e$,e1=e.overflowedIndicatorPopupClassName,e2=e.getPopupContainer,e5=e.onClick,e6=e.onOpenChange,e4=e.onKeyDown,e8=(e.openAnimation,e.openTransitionName,e._internalRenderMenuItem),e7=e._internalRenderSubMenuItem,e9=e._internalComponents,e3=(0,r.default)(e,eH),te=i.useMemo(function(){return[eW(eh,ey,eU,e9,es),eW(eh,ey,eU,{},es)]},[eh,ey,e9]),tt=(0,l.default)(te,2),tn=tt[0],tl=tt[1],tr=i.useState(!1),to=(0,l.default)(tr,2),ta=to[0],tu=to[1],ti=i.useRef(),tc=(u=(0,w.default)(eE,{value:eE}),s=(c=(0,l.default)(u,2))[0],d=c[1],i.useEffect(function(){er+=1;var e="".concat(el,"-").concat(er);d("rc-menu-uuid-".concat(e))},[]),s),tf="rtl"===eg,ts=(0,w.default)(eI,{value:eK,postState:function(e){return e||eU}}),td=(0,l.default)(ts,2),tp=td[0],tv=td[1],tm=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];function n(){tv(e),null==e6||e6(e)}t?(0,x.flushSync)(n):n()},tb=i.useState(tp),ty=(0,l.default)(tb,2),th=ty[0],tg=ty[1],tC=i.useRef(!1),tE=i.useMemo(function(){return("inline"===eR||"vertical"===eR)&&eM?["vertical",eM]:[eR,!1]},[eR,eM]),tw=(0,l.default)(tE,2),tR=tw[0],tM=tw[1],tx="inline"===tR,tk=i.useState(tR),tN=(0,l.default)(tk,2),tP=tN[0],tS=tN[1],tI=i.useState(tM),tK=(0,l.default)(tI,2),tO=tK[0],tA=tK[1];i.useEffect(function(){tS(tR),tA(tM),tC.current&&(tx?tv(th):tm(eU))},[tR,tM]);var tT=i.useState(0),tL=(0,l.default)(tT,2),tD=tL[0],t_=tL[1],tV=tD>=tn.length-1||"horizontal"!==tP||ek;i.useEffect(function(){tx&&tg(tp)},[tp]),i.useEffect(function(){return tC.current=!0,function(){tC.current=!1}},[]);var tF=(p=i.useState({}),v=(0,l.default)(p,2)[1],m=(0,i.useRef)(new Map),b=(0,i.useRef)(new Map),y=i.useState([]),M=(h=(0,l.default)(y,2))[0],N=h[1],P=(0,i.useRef)(0),S=(0,i.useRef)(!1),I=function(){S.current||v({})},K=(0,i.useCallback)(function(e,t){var n=ee(t);b.current.set(n,e),m.current.set(e,n),P.current+=1;var l=P.current;Promise.resolve().then(function(){l===P.current&&I()})},[]),T=(0,i.useCallback)(function(e,t){var n=ee(t);b.current.delete(n),m.current.delete(e)},[]),L=(0,i.useCallback)(function(e){N(e)},[]),D=(0,i.useCallback)(function(e,t){var n=(m.current.get(e)||"").split($);return t&&M.includes(n[0])&&n.unshift(et),n},[M]),F=(0,i.useCallback)(function(e,t){return e.filter(function(e){return void 0!==e}).some(function(e){return D(e,!0).includes(t)})},[D]),z=(0,i.useCallback)(function(e){var t="".concat(m.current.get(e)).concat($),n=new Set;return(0,C.default)(b.current.keys()).forEach(function(e){e.startsWith(t)&&n.add(b.current.get(e))}),n},[]),i.useEffect(function(){return function(){S.current=!0}},[]),{registerPath:K,unregisterPath:T,refreshOverflowKeys:L,isSubPathKey:F,getKeyPath:D,getKeys:function(){var e=(0,C.default)(m.current.keys());return M.length&&e.push(et),e},getSubPathKeys:z}),tz=tF.registerPath,tj=tF.unregisterPath,tB=tF.refreshOverflowKeys,tW=tF.isSubPathKey,tH=tF.getKeyPath,tU=tF.getKeys,tq=tF.getSubPathKeys,tG=i.useMemo(function(){return{registerPath:tz,unregisterPath:tj}},[tz,tj]),tX=i.useMemo(function(){return{isSubPathKey:tW}},[tW]);i.useEffect(function(){tB(tV?eU:tn.slice(tD+1).map(function(e){return e.key}))},[tD,tV]);var tY=(0,w.default)(eO||eA&&(null==(ec=tn[0])?void 0:ec.key),{value:eO}),tJ=(0,l.default)(tY,2),tQ=tJ[0],tZ=tJ[1],t$=en(function(e){tZ(e)}),t0=en(function(){tZ(void 0)});(0,i.useImperativeHandle)(o,function(){return{list:ti.current,focus:function(e){var t,n,l=Z(tU(),tc),r=l.elements,o=l.key2element,a=l.element2key,u=J(ti.current,r),i=null!=tQ?tQ:u[0]?a.get(u[0]):null==(t=tn.find(function(e){return!e.props.disabled}))?void 0:t.key,c=o.get(i);i&&c&&(null==c||null==(n=c.focus)||n.call(c,e))}}});var t1=(0,w.default)(eF||[],{value:ez,postState:function(e){return Array.isArray(e)?e:null==e?eU:[e]}}),t2=(0,l.default)(t1,2),t5=t2[0],t6=t2[1],t4=function(e){if(eL){var t,n=e.key,l=t5.includes(n);t6(t=eV?l?t5.filter(function(e){return e!==n}):[].concat((0,C.default)(t5),[n]):[n]);var r=(0,g.default)((0,g.default)({},e),{},{selectedKeys:t});l?null==eB||eB(r):null==ej||ej(r)}!eV&&tp.length&&"inline"!==tP&&tm(eU)},t8=en(function(e){null==e5||e5(ev(e)),t4(e)}),t7=en(function(e,t){var n=tp.filter(function(t){return t!==e});if(t)n.push(e);else if("inline"!==tP){var l=tq(e);n=n.filter(function(e){return!l.has(e)})}(0,R.default)(tp,n,!0)||tm(n,!0)}),t9=(eo=function(e,t){var n=null!=t?t:!tp.includes(e);t7(e,n)},ea=i.useRef(),(eu=i.useRef()).current=tQ,ei=function(){f.default.cancel(ea.current)},i.useEffect(function(){return function(){ei()}},[]),function(e){var t=e.which;if([].concat(Y,[U,q,G,X]).includes(t)){var l=tU(),r=Z(l,tc),o=r,a=o.elements,u=o.key2element,i=o.element2key,c=function(e,t){for(var n=e||document.activeElement;n;){if(t.has(n))return n;n=n.parentElement}return null}(u.get(tQ),a),s=i.get(c),d=function(e,t,l,r){var o,a="prev",u="next",i="children",c="parent";if("inline"===e&&r===U)return{inlineTrigger:!0};var f=(0,n.default)((0,n.default)({},W,a),H,u),s=(0,n.default)((0,n.default)((0,n.default)((0,n.default)({},j,l?u:a),B,l?a:u),H,i),U,i),d=(0,n.default)((0,n.default)((0,n.default)((0,n.default)((0,n.default)((0,n.default)({},W,a),H,u),U,i),q,c),j,l?i:c),B,l?c:i);switch(null==(o=({inline:f,horizontal:s,vertical:d,inlineSub:f,horizontalSub:d,verticalSub:d})["".concat(e).concat(t?"":"Sub")])?void 0:o[r]){case a:return{offset:-1,sibling:!0};case u:return{offset:1,sibling:!0};case c:return{offset:-1,sibling:!1};case i:return{offset:1,sibling:!1};default:return null}}(tP,1===tH(s,!0).length,tf,t);if(!d&&t!==G&&t!==X)return;(Y.includes(t)||[G,X].includes(t))&&e.preventDefault();var p=function(e){if(e){var t=e,n=e.querySelector("a");null!=n&&n.getAttribute("href")&&(t=n);var l=i.get(e);tZ(l),ei(),ea.current=(0,f.default)(function(){eu.current===l&&t.focus()})}};if([G,X].includes(t)||d.sibling||!c){var v=c&&"inline"!==tP?function(e){for(var t=e;t;){if(t.getAttribute("data-menu-list"))return t;t=t.parentElement}return null}(c):ti.current,m=J(v,a);p(t===G?m[0]:t===X?m[m.length-1]:Q(v,a,c,d.offset))}else if(d.inlineTrigger)eo(s);else if(d.offset>0)eo(s,!0),ei(),ea.current=(0,f.default)(function(){r=Z(l,tc);var e=c.getAttribute("aria-controls");p(Q(document.getElementById(e),r.elements))},5);else if(d.offset<0){var b=tH(s,!0),y=b[b.length-2],h=u.get(y);eo(y,!1),p(h)}}null==e4||e4(e)});i.useEffect(function(){tu(!0)},[]);var t3=i.useMemo(function(){return{_internalRenderMenuItem:e8,_internalRenderSubMenuItem:e7}},[e8,e7]),ne="horizontal"!==tP||ek?tn:tn.map(function(e,t){return i.createElement(O,{key:e.key,overflowDisabled:t>tD},e)}),nt=i.createElement(E.default,(0,t.default)({id:eE,ref:ti,prefixCls:"".concat(es,"-overflow"),component:"ul",itemComponent:eC,className:(0,a.default)(es,"".concat(es,"-root"),"".concat(es,"-").concat(tP),em,(0,n.default)((0,n.default)({},"".concat(es,"-inline-collapsed"),tO),"".concat(es,"-rtl"),tf),ed),dir:eg,style:ep,role:"menu",tabIndex:void 0===eb?0:eb,data:ne,renderRawItem:function(e){return e},renderRawRest:function(e){var t=e.length,n=t?tn.slice(-t):null;return i.createElement(eD,{eventKey:et,title:e0,disabled:tV,internalPopupClose:0===t,popupClassName:e1},n)},maxCount:"horizontal"!==tP||ek?E.default.INVALIDATE:E.default.RESPONSIVE,ssr:"full","data-menu-list":!0,onVisibleChange:function(e){t_(e)},onKeyDown:t9},e3));return i.createElement(V.Provider,{value:t3},i.createElement(k.Provider,{value:tc},i.createElement(O,{prefixCls:es,rootClassName:ed,mode:tP,openKeys:tp,rtl:tf,disabled:ex,motion:ta?eG:null,defaultMotions:ta?eX:null,activeKey:tQ,onActive:t$,onInactive:t0,selectedKeys:t5,inlineIndent:void 0===eq?24:eq,subMenuOpenDelay:void 0===eN?.1:eN,subMenuCloseDelay:void 0===eP?.1:eP,forceSubMenuRender:eS,builtinPlacements:eJ,triggerSubMenuAction:void 0===eY?"hover":eY,getPopupContainer:e2,itemIcon:eQ,expandIcon:eZ,onItemClick:t8,onOpenChange:t7},i.createElement(_.Provider,{value:tX},nt),i.createElement("div",{style:{display:"none"},"aria-hidden":!0},i.createElement(A.Provider,{value:tG},tl)))))});eq.Item=eC,eq.SubMenu=eD,eq.ItemGroup=ej,eq.Divider=eV,e.s(["default",0,eq],375565)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0tmaomqtwbi33.js b/litellm/proxy/_experimental/out/_next/static/chunks/0tmaomqtwbi33.js new file mode 100644 index 00000000000..db8c2b3c771 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0tmaomqtwbi33.js @@ -0,0 +1,41 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,464571,e=>{"use strict";var t=e.i(920228);e.s(["Button",()=>t.default])},735049,e=>{"use strict";var t=e.i(654310),n=function(e){if((0,t.default)()&&window.document.documentElement){var n=Array.isArray(e)?e:[e],l=window.document.documentElement;return n.some(function(e){return e in l.style})}return!1},l=function(e,t){if(!n(e))return!1;var l=document.createElement("div"),r=l.style[e];return l.style[e]=t,l.style[e]!==r};e.s(["isStyleSupport",0,function(e,t){return Array.isArray(e)||void 0===t?n(e):l(e,t)}])},190144,e=>{"use strict";e.i(247167);var t=e.i(931067),n=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"}}]},name:"copy",theme:"outlined"};var r=e.i(9583),o=n.forwardRef(function(e,o){return n.createElement(r.default,(0,t.default)({},e,{ref:o,icon:l}))});e.s(["default",0,o],190144)},486794,(e,t,n)=>{t.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],l=0;l{"use strict";var l=e.r(486794),r={"text/plain":"Text","text/html":"Url",default:"Text"};t.exports=function(e,t){var n,o,a,i,c,s,u,d,p=!1;t||(t={}),a=t.debug||!1;try{if(c=l(),s=document.createRange(),u=document.getSelection(),(d=document.createElement("span")).textContent=e,d.ariaHidden="true",d.style.all="unset",d.style.position="fixed",d.style.top=0,d.style.clip="rect(0, 0, 0, 0)",d.style.whiteSpace="pre",d.style.webkitUserSelect="text",d.style.MozUserSelect="text",d.style.msUserSelect="text",d.style.userSelect="text",d.addEventListener("copy",function(n){if(n.stopPropagation(),t.format)if(n.preventDefault(),void 0===n.clipboardData){a&&console.warn("unable to use e.clipboardData"),a&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var l=r[t.format]||r.default;window.clipboardData.setData(l,e)}else n.clipboardData.clearData(),n.clipboardData.setData(t.format,e);t.onCopy&&(n.preventDefault(),t.onCopy(n.clipboardData))}),document.body.appendChild(d),s.selectNodeContents(d),u.addRange(s),!document.execCommand("copy"))throw Error("copy command was unsuccessful");p=!0}catch(l){a&&console.error("unable to copy using execCommand: ",l),a&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(t.format||"text",e),t.onCopy&&t.onCopy(window.clipboardData),p=!0}catch(l){a&&console.error("unable to copy using clipboardData: ",l),a&&console.error("falling back to prompt"),n="message"in t?t.message:"Copy to clipboard: #{key}, Enter",o=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C",i=n.replace(/#{\s*key\s*}/g,o),window.prompt(i,e)}}finally{u&&("function"==typeof u.removeRange?u.removeRange(s):u.removeAllRanges()),d&&document.body.removeChild(d),c()}return p}},898586,401361,335771,e=>{"use strict";e.i(247167);var t=e.i(271645),n=e.i(8211),l=e.i(931067);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"}}]},name:"edit",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,n){return t.createElement(o.default,(0,l.default)({},e,{ref:n,icon:r}))});e.s(["default",0,a],401361);var i=e.i(343794),c=e.i(430073),s=e.i(876556),u=e.i(174428),d=e.i(914949),p=e.i(529681),f=e.i(611935),m=e.i(735049),g=e.i(242064),b=e.i(929447),y=e.i(491816);let v={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M864 170h-60c-4.4 0-8 3.6-8 8v518H310v-73c0-6.7-7.8-10.5-13-6.3l-141.9 112a8 8 0 000 12.6l141.9 112c5.3 4.2 13 .4 13-6.3v-75h498c35.3 0 64-28.7 64-64V178c0-4.4-3.6-8-8-8z"}}]},name:"enter",theme:"outlined"};var h=t.forwardRef(function(e,n){return t.createElement(o.default,(0,l.default)({},e,{ref:n,icon:v}))}),x=e.i(404948),O=e.i(763731),E=e.i(635432),w=e.i(183293),S=e.i(246422);e.i(765846);var j=e.i(896091);let C=(0,S.genStyleHooks)("Typography",e=>{let t,{componentCls:n,titleMarginTop:l}=e;return{[n]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({color:e.colorText,wordBreak:"break-word",lineHeight:e.lineHeight,[`&${n}-secondary`]:{color:e.colorTextDescription},[`&${n}-success`]:{color:e.colorSuccessText},[`&${n}-warning`]:{color:e.colorWarningText},[`&${n}-danger`]:{color:e.colorErrorText,"a&:active, a&:focus":{color:e.colorErrorTextActive},"a&:hover":{color:e.colorErrorTextHover}},[`&${n}-disabled`]:{color:e.colorTextDisabled,cursor:"not-allowed",userSelect:"none"},[` + div&, + p + `]:{marginBottom:"1em"}},(t={},[1,2,3,4,5].forEach(n=>{t[` + h${n}&, + div&-h${n}, + div&-h${n} > textarea, + h${n} + `]=((e,t,n,l)=>{let{titleMarginBottom:r,fontWeightStrong:o}=l;return{marginBottom:r,color:n,fontWeight:o,fontSize:e,lineHeight:t}})(e[`fontSizeHeading${n}`],e[`lineHeightHeading${n}`],e.colorTextHeading,e)}),t)),{[` + & + h1${n}, + & + h2${n}, + & + h3${n}, + & + h4${n}, + & + h5${n} + `]:{marginTop:l},[` + div, + ul, + li, + p, + h1, + h2, + h3, + h4, + h5`]:{[` + + h1, + + h2, + + h3, + + h4, + + h5 + `]:{marginTop:l}}}),{code:{margin:"0 0.2em",paddingInline:"0.4em",paddingBlock:"0.2em 0.1em",fontSize:"85%",fontFamily:e.fontFamilyCode,background:"rgba(150, 150, 150, 0.1)",border:"1px solid rgba(100, 100, 100, 0.2)",borderRadius:3},kbd:{margin:"0 0.2em",paddingInline:"0.4em",paddingBlock:"0.15em 0.1em",fontSize:"90%",fontFamily:e.fontFamilyCode,background:"rgba(150, 150, 150, 0.06)",border:"1px solid rgba(100, 100, 100, 0.2)",borderBottomWidth:2,borderRadius:3},mark:{padding:0,backgroundColor:j.gold[2]},"u, ins":{textDecoration:"underline",textDecorationSkipInk:"auto"},"s, del":{textDecoration:"line-through"},strong:{fontWeight:e.fontWeightStrong},"ul, ol":{marginInline:0,marginBlock:"0 1em",padding:0,li:{marginInline:"20px 0",marginBlock:0,paddingInline:"4px 0",paddingBlock:0}},ul:{listStyleType:"circle",ul:{listStyleType:"disc"}},ol:{listStyleType:"decimal"},"pre, blockquote":{margin:"1em 0"},pre:{padding:"0.4em 0.6em",whiteSpace:"pre-wrap",wordWrap:"break-word",background:"rgba(150, 150, 150, 0.1)",border:"1px solid rgba(100, 100, 100, 0.2)",borderRadius:3,fontFamily:e.fontFamilyCode,code:{display:"inline",margin:0,padding:0,fontSize:"inherit",fontFamily:"inherit",background:"transparent",border:0}},blockquote:{paddingInline:"0.6em 0",paddingBlock:0,borderInlineStart:"4px solid rgba(100, 100, 100, 0.2)",opacity:.85}}),(e=>{let{componentCls:t}=e;return{"a&, a":Object.assign(Object.assign({},(0,w.operationUnit)(e)),{userSelect:"text",[`&[disabled], &${t}-disabled`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:active, &:hover":{color:e.colorTextDisabled},"&:active":{pointerEvents:"none"}}})}})(e)),{[` + ${n}-expand, + ${n}-collapse, + ${n}-edit, + ${n}-copy + `]:Object.assign(Object.assign({},(0,w.operationUnit)(e)),{marginInlineStart:e.marginXXS})}),(e=>{let{componentCls:t,paddingSM:n}=e;return{"&-edit-content":{position:"relative","div&":{insetInlineStart:e.calc(e.paddingSM).mul(-1).equal(),insetBlockStart:e.calc(n).div(-2).add(1).equal(),marginBottom:e.calc(n).div(2).sub(2).equal()},[`${t}-edit-content-confirm`]:{position:"absolute",insetInlineEnd:e.calc(e.marginXS).add(2).equal(),insetBlockEnd:e.marginXS,color:e.colorIcon,fontWeight:"normal",fontSize:e.fontSize,fontStyle:"normal",pointerEvents:"none"},textarea:{margin:"0!important",MozTransition:"none",height:"1em"}}}})(e)),{[`${e.componentCls}-copy-success`]:{[` + &, + &:hover, + &:focus`]:{color:e.colorSuccess}},[`${e.componentCls}-copy-icon-only`]:{marginInlineStart:0}}),{[` + a&-ellipsis, + span&-ellipsis + `]:{display:"inline-block",maxWidth:"100%"},"&-ellipsis-single-line":{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis","a&, span&":{verticalAlign:"bottom"},"> code":{paddingBlock:0,maxWidth:"calc(100% - 1.2em)",display:"inline-block",overflow:"hidden",textOverflow:"ellipsis",verticalAlign:"bottom",boxSizing:"content-box"}},"&-ellipsis-multiple-line":{display:"-webkit-box",overflow:"hidden",WebkitLineClamp:3,WebkitBoxOrient:"vertical"}}),{"&-rtl":{direction:"rtl"}})}},()=>({titleMarginTop:"1.2em",titleMarginBottom:"0.5em"})),k=e=>{let{prefixCls:n,"aria-label":l,className:r,style:o,direction:a,maxLength:c,autoSize:s=!0,value:u,onSave:d,onCancel:p,onEnd:f,component:m,enterIcon:g=t.createElement(h,null)}=e,b=t.useRef(null),y=t.useRef(!1),v=t.useRef(null),[w,S]=t.useState(u);t.useEffect(()=>{S(u)},[u]),t.useEffect(()=>{var e;if(null==(e=b.current)?void 0:e.resizableTextArea){let{textArea:e}=b.current.resizableTextArea;e.focus();let{length:t}=e.value;e.setSelectionRange(t,t)}},[]);let j=()=>{d(w.trim())},[k,R,$]=C(n),T=(0,i.default)(n,`${n}-edit-content`,{[`${n}-rtl`]:"rtl"===a,[`${n}-${m}`]:!!m},r,R,$);return k(t.createElement("div",{className:T,style:o},t.createElement(E.default,{ref:b,maxLength:c,value:w,onChange:({target:e})=>{S(e.value.replace(/[\n\r]/g,""))},onKeyDown:({keyCode:e})=>{y.current||(v.current=e)},onKeyUp:({keyCode:e,ctrlKey:t,altKey:n,metaKey:l,shiftKey:r})=>{v.current!==e||y.current||t||n||l||r||(e===x.default.ENTER?(j(),null==f||f()):e===x.default.ESC&&p())},onCompositionStart:()=>{y.current=!0},onCompositionEnd:()=>{y.current=!1},onBlur:()=>{j()},"aria-label":l,rows:1,autoSize:s}),null!==g?(0,O.cloneElement)(g,{className:`${n}-edit-content-confirm`}):null))};var R=e.i(844343),$=e.i(175066);function T(e,n){return t.useMemo(()=>{let t=!!e;return[t,Object.assign(Object.assign({},n),t&&"object"==typeof e?e:null)]},[e])}var I=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let D=t.forwardRef((e,n)=>{let{prefixCls:l,component:r="article",className:o,rootClassName:a,setContentRef:c,children:s,direction:u,style:d}=e,p=I(e,["prefixCls","component","className","rootClassName","setContentRef","children","direction","style"]),{getPrefixCls:m,direction:b,className:y,style:v}=(0,g.useComponentConfig)("typography"),h=c?(0,f.composeRef)(n,c):n,x=m("typography",l),[O,E,w]=C(x),S=(0,i.default)(x,y,{[`${x}-rtl`]:"rtl"===(null!=u?u:b)},o,a,E,w),j=Object.assign(Object.assign({},v),d);return O(t.createElement(r,Object.assign({className:S,style:j,ref:h},p),s))});var P=e.i(121229),B=e.i(190144),H=e.i(739295);function M(e){return!1===e?[!1,!1]:Array.isArray(e)?e:[e]}function z(e,t,n){return!0===e||void 0===e?t:e||n&&t}let A=e=>["string","number"].includes(typeof e),L=({prefixCls:e,copied:n,locale:l,iconOnly:r,tooltips:o,icon:a,tabIndex:c,onCopy:s,loading:u})=>{let d=M(o),p=M(a),{copied:f,copy:m}=null!=l?l:{},g=n?f:m,b=z(d[+!!n],g),v="string"==typeof b?b:g;return t.createElement(y.default,{title:b},t.createElement("button",{type:"button",className:(0,i.default)(`${e}-copy`,{[`${e}-copy-success`]:n,[`${e}-copy-icon-only`]:r}),onClick:s,"aria-label":v,tabIndex:c},n?z(p[1],t.createElement(P.default,null),!0):z(p[0],u?t.createElement(H.default,null):t.createElement(B.default,null),!0)))},W=t.forwardRef(({style:e,children:n},l)=>{let r=t.useRef(null);return t.useImperativeHandle(l,()=>({isExceed:()=>{let e=r.current;return e.scrollHeight>e.clientHeight},getHeight:()=>r.current.clientHeight})),t.createElement("span",{"aria-hidden":!0,ref:r,style:Object.assign({position:"fixed",display:"block",left:0,top:0,pointerEvents:"none",backgroundColor:"rgba(255, 0, 0, 0.65)"},e)},n)});function N(e,t){let n=0,l=[];for(let r=0;rt){let e=t-n;return l.push(String(o).slice(0,e)),l}l.push(o),n=a}return e}let U={display:"-webkit-box",overflow:"hidden",WebkitBoxOrient:"vertical"};function F(e){let{enableMeasure:l,width:r,text:o,children:a,rows:i,expanded:c,miscDeps:d,onEllipsis:p}=e,f=t.useMemo(()=>(0,s.default)(o),[o]),m=t.useMemo(()=>f.reduce((e,t)=>e+(A(t)?String(t).length:1),0),[o]),g=t.useMemo(()=>a(f,!1),[o]),[b,y]=t.useState(null),v=t.useRef(null),h=t.useRef(null),x=t.useRef(null),O=t.useRef(null),E=t.useRef(null),[w,S]=t.useState(!1),[j,C]=t.useState(0),[k,R]=t.useState(0),[$,T]=t.useState(null);(0,u.default)(()=>{l&&r&&m?C(1):C(0)},[r,o,i,l,f]),(0,u.default)(()=>{var e,t,n,l;if(1===j)C(2),T(h.current&&getComputedStyle(h.current).whiteSpace);else if(2===j){let r=!!(null==(e=x.current)?void 0:e.isExceed());C(r?3:4),y(r?[0,m]:null),S(r),R(Math.max((null==(t=x.current)?void 0:t.getHeight())||0,(1===i?0:(null==(n=O.current)?void 0:n.getHeight())||0)+((null==(l=E.current)?void 0:l.getHeight())||0))+1),p(r)}},[j]);let I=b?Math.ceil((b[0]+b[1])/2):0;(0,u.default)(()=>{var e;let[t,n]=b||[0,0];if(t!==n){let l=((null==(e=v.current)?void 0:e.getHeight())||0)>k,r=I;n-t==1&&(r=l?t:n),y(l?[t,r]:[r,n])}},[b,I]);let D=t.useMemo(()=>{if(!l)return a(f,!1);if(3!==j||!b||b[0]!==b[1]){let e=a(f,!1);return[4,0].includes(j)?e:t.createElement("span",{style:Object.assign(Object.assign({},U),{WebkitLineClamp:i})},e)}return a(c?f:N(f,b[0]),w)},[c,j,b,f].concat((0,n.default)(d))),P={width:r,margin:0,padding:0,whiteSpace:"nowrap"===$?"normal":"inherit"};return t.createElement(t.Fragment,null,D,2===j&&t.createElement(t.Fragment,null,t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:i}),ref:x},g),t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:i-1}),ref:O},g),t.createElement(W,{style:Object.assign(Object.assign(Object.assign({},P),U),{WebkitLineClamp:1}),ref:E},a([],!0))),3===j&&b&&b[0]!==b[1]&&t.createElement(W,{style:Object.assign(Object.assign({},P),{top:400}),ref:v},a(N(f,I),!0)),1===j&&t.createElement("span",{style:{whiteSpace:"inherit"},ref:h}))}let q=({enableEllipsis:e,isEllipsis:n,children:l,tooltipProps:r})=>(null==r?void 0:r.title)&&e?t.createElement(y.default,Object.assign({open:!!n&&void 0},r),l):l;var V=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let X=["delete","mark","code","underline","strong","keyboard","italic"],K=t.forwardRef((e,l)=>{var r;let o,v,h,{prefixCls:x,className:O,style:E,type:w,disabled:S,children:j,ellipsis:C,editable:I,copyable:P,component:B,title:H}=e,M=V(e,["prefixCls","className","style","type","disabled","children","ellipsis","editable","copyable","component","title"]),{getPrefixCls:z,direction:W}=t.useContext(g.ConfigContext),[N]=(0,b.default)("Text"),U=t.useRef(null),K=t.useRef(null),_=z("typography",x),G=(0,p.default)(M,X),[J,Q]=T(I),[Y,Z]=(0,d.default)(!1,{value:Q.editing}),{triggerType:ee=["icon"]}=Q,et=e=>{var t;e&&(null==(t=Q.onStart)||t.call(Q)),Z(e)},en=(o=(0,t.useRef)(void 0),(0,t.useEffect)(()=>{o.current=Y}),o.current);(0,u.default)(()=>{var e;!Y&&en&&(null==(e=K.current)||e.focus())},[Y]);let el=e=>{null==e||e.preventDefault(),et(!0)},[er,eo]=T(P),{copied:ea,copyLoading:ei,onClick:ec}=(({copyConfig:e,children:n})=>{let[l,r]=t.useState(!1),[o,a]=t.useState(!1),i=t.useRef(null),c=()=>{i.current&&clearTimeout(i.current)},s={};e.format&&(s.format=e.format),t.useEffect(()=>c,[]);let u=(0,$.default)(t=>{var l,o,u,d;return l=void 0,o=void 0,u=void 0,d=function*(){var l;null==t||t.preventDefault(),null==t||t.stopPropagation(),a(!0);try{let o="function"==typeof e.text?yield e.text():e.text;(0,R.default)(o||((e,t=!1)=>t&&null==e?[]:Array.isArray(e)?e:[e])(n,!0).join("")||"",s),a(!1),r(!0),c(),i.current=setTimeout(()=>{r(!1)},3e3),null==(l=e.onCopy)||l.call(e,t)}catch(e){throw a(!1),e}},new(u||(u=Promise))(function(e,t){function n(e){try{a(d.next(e))}catch(e){t(e)}}function r(e){try{a(d.throw(e))}catch(e){t(e)}}function a(t){var l;t.done?e(t.value):((l=t.value)instanceof u?l:new u(function(e){e(l)})).then(n,r)}a((d=d.apply(l,o||[])).next())})});return{copied:l,copyLoading:o,onClick:u}})({copyConfig:eo,children:j}),[es,eu]=t.useState(!1),[ed,ep]=t.useState(!1),[ef,em]=t.useState(!1),[eg,eb]=t.useState(!1),[ey,ev]=t.useState(!0),[eh,ex]=T(C,{expandable:!1,symbol:e=>e?null==N?void 0:N.collapse:null==N?void 0:N.expand}),[eO,eE]=(0,d.default)(ex.defaultExpanded||!1,{value:ex.expanded}),ew=eh&&(!eO||"collapsible"===ex.expandable),{rows:eS=1}=ex,ej=t.useMemo(()=>ew&&(void 0!==ex.suffix||ex.onEllipsis||ex.expandable||J||er),[ew,ex,J,er]);(0,u.default)(()=>{eh&&!ej&&(eu((0,m.isStyleSupport)("webkitLineClamp")),ep((0,m.isStyleSupport)("textOverflow")))},[ej,eh]);let[eC,ek]=t.useState(ew),eR=t.useMemo(()=>!ej&&(1===eS?ed:es),[ej,ed,es]);(0,u.default)(()=>{ek(eR&&ew)},[eR,ew]);let e$=ew&&(eC?eg:ef),eT=ew&&1===eS&&eC,eI=ew&&eS>1&&eC,[eD,eP]=t.useState(0),eB=e=>{var t;em(e),ef!==e&&(null==(t=ex.onEllipsis)||t.call(ex,e))};t.useEffect(()=>{let e=U.current;if(eh&&eC&&e){let t,n,l,r=(t=document.createElement("em"),e.appendChild(t),n=e.getBoundingClientRect(),l=t.getBoundingClientRect(),e.removeChild(t),n.left>l.left||l.right>n.right||n.top>l.top||l.bottom>n.bottom);eg!==r&&eb(r)}},[eh,eC,j,eI,ey,eD]),t.useEffect(()=>{let e=U.current;if("u"{ev(!!e.offsetParent)});return t.observe(e),()=>{t.disconnect()}},[eC,ew]);let eH=(v=ex.tooltip,h=Q.text,(0,t.useMemo)(()=>!0===v?{title:null!=h?h:j}:(0,t.isValidElement)(v)?{title:v}:"object"==typeof v?Object.assign({title:null!=h?h:j},v):{title:v},[v,h,j])),eM=t.useMemo(()=>{if(eh&&!eC)return[Q.text,j,H,eH.title].find(A)},[eh,eC,H,eH.title,e$]);return Y?t.createElement(k,{value:null!=(r=Q.text)?r:"string"==typeof j?j:"",onSave:e=>{var t;null==(t=Q.onChange)||t.call(Q,e),et(!1)},onCancel:()=>{var e;null==(e=Q.onCancel)||e.call(Q),et(!1)},onEnd:Q.onEnd,prefixCls:_,className:O,style:E,direction:W,component:B,maxLength:Q.maxLength,autoSize:Q.autoSize,enterIcon:Q.enterIcon}):t.createElement(c.default,{onResize:({offsetWidth:e})=>{eP(e)},disabled:!ew},r=>t.createElement(q,{tooltipProps:eH,enableEllipsis:ew,isEllipsis:e$},t.createElement(D,Object.assign({className:(0,i.default)({[`${_}-${w}`]:w,[`${_}-disabled`]:S,[`${_}-ellipsis`]:eh,[`${_}-ellipsis-single-line`]:eT,[`${_}-ellipsis-multiple-line`]:eI},O),prefixCls:x,style:Object.assign(Object.assign({},E),{WebkitLineClamp:eI?eS:void 0}),component:B,ref:(0,f.composeRef)(r,U,l),direction:W,onClick:ee.includes("text")?el:void 0,"aria-label":null==eM?void 0:eM.toString(),title:H},G),t.createElement(F,{enableMeasure:ew&&!eC,text:j,rows:eS,width:eD,onEllipsis:eB,expanded:eO,miscDeps:[ea,eO,ei,J,er,N].concat((0,n.default)(X.map(t=>e[t])))},(n,l)=>{let r;return function({mark:e,code:n,underline:l,delete:r,strong:o,keyboard:a,italic:i},c){let s=c;function u(e,n){n&&(s=t.createElement(e,{},s))}return u("strong",o),u("u",l),u("del",r),u("code",n),u("mark",e),u("kbd",a),u("i",i),s}(e,t.createElement(t.Fragment,null,n.length>0&&l&&!eO&&eM?t.createElement("span",{key:"show-content","aria-hidden":!0},n):n,[(r=l)&&!eO&&t.createElement("span",{"aria-hidden":!0,key:"ellipsis"},"..."),ex.suffix,[r&&(()=>{let{expandable:e,symbol:n}=ex;return e?t.createElement("button",{type:"button",key:"expand",className:`${_}-${eO?"collapse":"expand"}`,onClick:e=>{var t,n;eE((t={expanded:!eO}).expanded),null==(n=ex.onExpand)||n.call(ex,e,t)},"aria-label":eO?N.collapse:null==N?void 0:N.expand},"function"==typeof n?n(eO):n):null})(),(()=>{if(!J)return;let{icon:e,tooltip:n,tabIndex:l}=Q,r=(0,s.default)(n)[0]||(null==N?void 0:N.edit),o="string"==typeof r?r:"";return ee.includes("icon")?t.createElement(y.default,{key:"edit",title:!1===n?"":r},t.createElement("button",{type:"button",ref:K,className:`${_}-edit`,onClick:el,"aria-label":o,tabIndex:l},e||t.createElement(a,{role:"button"}))):null})(),er?t.createElement(L,Object.assign({key:"copy"},eo,{prefixCls:_,copied:ea,locale:N,onCopy:ec,loading:ei,iconOnly:null==j})):null]]))}))))});var _=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let G=t.forwardRef((e,n)=>{let{ellipsis:l,rel:r,children:o,navigate:a}=e,i=_(e,["ellipsis","rel","children","navigate"]),c=Object.assign(Object.assign({},i),{rel:void 0===r&&"_blank"===i.target?"noopener noreferrer":r});return t.createElement(K,Object.assign({},c,{ref:n,ellipsis:!!l,component:"a"}),o)});var J=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let Q=t.forwardRef((e,n)=>{let{children:l}=e,r=J(e,["children"]);return t.createElement(K,Object.assign({ref:n},r,{component:"div"}),l)});var Y=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let Z=t.forwardRef((e,n)=>{let{ellipsis:l,children:r}=e,o=Y(e,["ellipsis","children"]),a=t.useMemo(()=>l&&"object"==typeof l?(0,p.default)(l,["expandable","rows"]):l,[l]);return t.createElement(K,Object.assign({ref:n},o,{ellipsis:a,component:"span"}),r)});var ee=function(e,t){var n={};for(var l in e)Object.prototype.hasOwnProperty.call(e,l)&&0>t.indexOf(l)&&(n[l]=e[l]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,l=Object.getOwnPropertySymbols(e);rt.indexOf(l[r])&&Object.prototype.propertyIsEnumerable.call(e,l[r])&&(n[l[r]]=e[l[r]]);return n};let et=[1,2,3,4,5],en=t.forwardRef((e,n)=>{let{level:l=1,children:r}=e,o=ee(e,["level","children"]),a=et.includes(l)?`h${l}`:"h1";return t.createElement(K,Object.assign({ref:n},o,{component:a}),r)});e.s(["default",0,en],335771),D.Text=Z,D.Link=G,D.Title=en,D.Paragraph=Q,e.s(["Typography",0,D],898586)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0tvwf-7q.gldz.js b/litellm/proxy/_experimental/out/_next/static/chunks/0tvwf-7q.gldz.js new file mode 100644 index 00000000000..1ff7cdbbe5b --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0tvwf-7q.gldz.js @@ -0,0 +1,10 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var a=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(a.default,(0,t.default)({},e,{ref:n,icon:o}))});e.s(["default",0,n],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},954616,e=>{"use strict";var t=e.i(271645),r=e.i(114272),o=e.i(540143),a=e.i(915823),n=e.i(619273),l=class extends a.Subscribable{#e;#t=void 0;#r;#o;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#a()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),(0,n.shallowEqualObjects)(this.options,t)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#r,observer:this}),t?.mutationKey&&this.options.mutationKey&&(0,n.hashKey)(t.mutationKey)!==(0,n.hashKey)(this.options.mutationKey)?this.reset():this.#r?.state.status==="pending"&&this.#r.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#r?.removeObserver(this)}onMutationUpdate(e){this.#a(),this.#n(e)}getCurrentResult(){return this.#t}reset(){this.#r?.removeObserver(this),this.#r=void 0,this.#a(),this.#n()}mutate(e,t){return this.#o=t,this.#r?.removeObserver(this),this.#r=this.#e.getMutationCache().build(this.#e,this.options),this.#r.addObserver(this),this.#r.execute(e)}#a(){let e=this.#r?.state??(0,r.getDefaultState)();this.#t={...e,isPending:"pending"===e.status,isSuccess:"success"===e.status,isError:"error"===e.status,isIdle:"idle"===e.status,mutate:this.mutate,reset:this.reset}}#n(e){o.notifyManager.batch(()=>{if(this.#o&&this.hasListeners()){let t=this.#t.variables,r=this.#t.context,o={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type==="success"){try{this.#o.onSuccess?.(e.data,t,r,o)}catch(e){Promise.reject(e)}try{this.#o.onSettled?.(e.data,null,t,r,o)}catch(e){Promise.reject(e)}}else if(e?.type==="error"){try{this.#o.onError?.(e.error,t,r,o)}catch(e){Promise.reject(e)}try{this.#o.onSettled?.(void 0,e.error,t,r,o)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},i=e.i(912598);e.s(["useMutation",0,function(e,r){let a=(0,i.useQueryClient)(r),[s]=t.useState(()=>new l(a,e));t.useEffect(()=>{s.setOptions(e)},[s,e]);let d=t.useSyncExternalStore(t.useCallback(e=>s.subscribe(o.notifyManager.batchCalls(e)),[s]),()=>s.getCurrentResult(),()=>s.getCurrentResult()),c=t.useCallback((e,t)=>{s.mutate(e,t).catch(n.noop)},[s]);if(d.error&&(0,n.shouldThrowError)(s.options.throwOnError,[d.error]))throw d.error;return{...d,mutate:c,mutateAsync:d.mutate}}],954616)},175712,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),o=e.i(529681),a=e.i(242064),n=e.i(517455),l=e.i(185793),i=e.i(721369),s=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let d=e=>{var{prefixCls:o,className:n,hoverable:l=!0}=e,i=s(e,["prefixCls","className","hoverable"]);let{getPrefixCls:d}=t.useContext(a.ConfigContext),c=d("card",o),u=(0,r.default)(`${c}-grid`,n,{[`${c}-grid-hoverable`]:l});return t.createElement("div",Object.assign({},i,{className:u}))};e.i(296059);var c=e.i(915654),u=e.i(183293),m=e.i(246422),g=e.i(838378);let p=(0,m.genStyleHooks)("Card",e=>{let t=(0,g.mergeToken)(e,{cardShadow:e.boxShadowCard,cardHeadPadding:e.padding,cardPaddingBase:e.paddingLG,cardActionsIconSize:e.fontSize});return[(e=>{let{componentCls:t,cardShadow:r,cardHeadPadding:o,colorBorderSecondary:a,boxShadowTertiary:n,bodyPadding:l,extraColor:i}=e;return{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",background:e.colorBgContainer,borderRadius:e.borderRadiusLG,[`&:not(${t}-bordered)`]:{boxShadow:n},[`${t}-head`]:(e=>{let{antCls:t,componentCls:r,headerHeight:o,headerPadding:a,tabsMarginBottom:n}=e;return Object.assign(Object.assign({display:"flex",justifyContent:"center",flexDirection:"column",minHeight:o,marginBottom:-1,padding:`0 ${(0,c.unit)(a)}`,color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.headerFontSize,background:e.headerBg,borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`,borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`},(0,u.clearFix)()),{"&-wrapper":{width:"100%",display:"flex",alignItems:"center"},"&-title":Object.assign(Object.assign({display:"inline-block",flex:1},u.textEllipsis),{[` + > ${r}-typography, + > ${r}-typography-edit-content + `]:{insetInlineStart:0,marginTop:0,marginBottom:0}}),[`${t}-tabs-top`]:{clear:"both",marginBottom:n,color:e.colorText,fontWeight:"normal",fontSize:e.fontSize,"&-bar":{borderBottom:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorderSecondary}`}}})})(e),[`${t}-extra`]:{marginInlineStart:"auto",color:i,fontWeight:"normal",fontSize:e.fontSize},[`${t}-body`]:{padding:l,borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},[`${t}-grid`]:(e=>{let{cardPaddingBase:t,colorBorderSecondary:r,cardShadow:o,lineWidth:a}=e;return{width:"33.33%",padding:t,border:0,borderRadius:0,boxShadow:` + ${(0,c.unit)(a)} 0 0 0 ${r}, + 0 ${(0,c.unit)(a)} 0 0 ${r}, + ${(0,c.unit)(a)} ${(0,c.unit)(a)} 0 0 ${r}, + ${(0,c.unit)(a)} 0 0 0 ${r} inset, + 0 ${(0,c.unit)(a)} 0 0 ${r} inset; + `,transition:`all ${e.motionDurationMid}`,"&-hoverable:hover":{position:"relative",zIndex:1,boxShadow:o}}})(e),[`${t}-cover`]:{"> *":{display:"block",width:"100%",borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0`}},[`${t}-actions`]:(e=>{let{componentCls:t,iconCls:r,actionsLiMargin:o,cardActionsIconSize:a,colorBorderSecondary:n,actionsBg:l}=e;return Object.assign(Object.assign({margin:0,padding:0,listStyle:"none",background:l,borderTop:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${n}`,display:"flex",borderRadius:`0 0 ${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)}`},(0,u.clearFix)()),{"& > li":{margin:o,color:e.colorTextDescription,textAlign:"center","> span":{position:"relative",display:"block",minWidth:e.calc(e.cardActionsIconSize).mul(2).equal(),fontSize:e.fontSize,lineHeight:e.lineHeight,cursor:"pointer","&:hover":{color:e.colorPrimary,transition:`color ${e.motionDurationMid}`},[`a:not(${t}-btn), > ${r}`]:{display:"inline-block",width:"100%",color:e.colorIcon,lineHeight:(0,c.unit)(e.fontHeight),transition:`color ${e.motionDurationMid}`,"&:hover":{color:e.colorPrimary}},[`> ${r}`]:{fontSize:a,lineHeight:(0,c.unit)(e.calc(a).mul(e.lineHeight).equal())}},"&:not(:last-child)":{borderInlineEnd:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${n}`}}})})(e),[`${t}-meta`]:Object.assign(Object.assign({margin:`${(0,c.unit)(e.calc(e.marginXXS).mul(-1).equal())} 0`,display:"flex"},(0,u.clearFix)()),{"&-avatar":{paddingInlineEnd:e.padding},"&-detail":{overflow:"hidden",flex:1,"> div:not(:last-child)":{marginBottom:e.marginXS}},"&-title":Object.assign({color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG},u.textEllipsis),"&-description":{color:e.colorTextDescription}})}),[`${t}-bordered`]:{border:`${(0,c.unit)(e.lineWidth)} ${e.lineType} ${a}`,[`${t}-cover`]:{marginTop:-1,marginInlineStart:-1,marginInlineEnd:-1}},[`${t}-hoverable`]:{cursor:"pointer",transition:`box-shadow ${e.motionDurationMid}, border-color ${e.motionDurationMid}`,"&:hover":{borderColor:"transparent",boxShadow:r}},[`${t}-contain-grid`]:{borderRadius:`${(0,c.unit)(e.borderRadiusLG)} ${(0,c.unit)(e.borderRadiusLG)} 0 0 `,[`${t}-body`]:{display:"flex",flexWrap:"wrap"},[`&:not(${t}-loading) ${t}-body`]:{marginBlockStart:e.calc(e.lineWidth).mul(-1).equal(),marginInlineStart:e.calc(e.lineWidth).mul(-1).equal(),padding:0}},[`${t}-contain-tabs`]:{[`> div${t}-head`]:{minHeight:0,[`${t}-head-title, ${t}-extra`]:{paddingTop:o}}},[`${t}-type-inner`]:(e=>{let{componentCls:t,colorFillAlter:r,headerPadding:o,bodyPadding:a}=e;return{[`${t}-head`]:{padding:`0 ${(0,c.unit)(o)}`,background:r,"&-title":{fontSize:e.fontSize}},[`${t}-body`]:{padding:`${(0,c.unit)(e.padding)} ${(0,c.unit)(a)}`}}})(e),[`${t}-loading`]:(e=>{let{componentCls:t}=e;return{overflow:"hidden",[`${t}-body`]:{userSelect:"none"}}})(e),[`${t}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t,bodyPaddingSM:r,headerPaddingSM:o,headerHeightSM:a,headerFontSizeSM:n}=e;return{[`${t}-small`]:{[`> ${t}-head`]:{minHeight:a,padding:`0 ${(0,c.unit)(o)}`,fontSize:n,[`> ${t}-head-wrapper`]:{[`> ${t}-extra`]:{fontSize:e.fontSize}}},[`> ${t}-body`]:{padding:r}},[`${t}-small${t}-contain-tabs`]:{[`> ${t}-head`]:{[`${t}-head-title, ${t}-extra`]:{paddingTop:0,display:"flex",alignItems:"center"}}}}})(t)]},e=>{var t,r;return{headerBg:"transparent",headerFontSize:e.fontSizeLG,headerFontSizeSM:e.fontSize,headerHeight:e.fontSizeLG*e.lineHeightLG+2*e.padding,headerHeightSM:e.fontSize*e.lineHeight+2*e.paddingXS,actionsBg:e.colorBgContainer,actionsLiMargin:`${e.paddingSM}px 0`,tabsMarginBottom:-e.padding-e.lineWidth,extraColor:e.colorText,bodyPaddingSM:12,headerPaddingSM:12,bodyPadding:null!=(t=e.bodyPadding)?t:e.paddingLG,headerPadding:null!=(r=e.headerPadding)?r:e.paddingLG}});var b=e.i(792812),h=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let f=e=>{let{actionClasses:r,actions:o=[],actionStyle:a}=e;return t.createElement("ul",{className:r,style:a},o.map((e,r)=>{let a=`action-${r}`;return t.createElement("li",{style:{width:`${100/o.length}%`},key:a},t.createElement("span",null,e))}))},v=t.forwardRef((e,s)=>{let c,{prefixCls:u,className:m,rootClassName:g,style:v,extra:y,headStyle:x={},bodyStyle:$={},title:C,loading:O,bordered:S,variant:w,size:j,type:k,cover:E,actions:N,tabList:B,children:M,activeTabKey:T,defaultActiveTabKey:P,tabBarExtraContent:z,hoverable:R,tabProps:L={},classNames:I,styles:H}=e,G=h(e,["prefixCls","className","rootClassName","style","extra","headStyle","bodyStyle","title","loading","bordered","variant","size","type","cover","actions","tabList","children","activeTabKey","defaultActiveTabKey","tabBarExtraContent","hoverable","tabProps","classNames","styles"]),{getPrefixCls:W,direction:X,card:A}=t.useContext(a.ConfigContext),[K]=(0,b.default)("card",w,S),D=e=>{var t;return(0,r.default)(null==(t=null==A?void 0:A.classNames)?void 0:t[e],null==I?void 0:I[e])},Y=e=>{var t;return Object.assign(Object.assign({},null==(t=null==A?void 0:A.styles)?void 0:t[e]),null==H?void 0:H[e])},_=t.useMemo(()=>{let e=!1;return t.Children.forEach(M,t=>{(null==t?void 0:t.type)===d&&(e=!0)}),e},[M]),F=W("card",u),[q,U,V]=p(F),Q=t.createElement(l.default,{loading:!0,active:!0,paragraph:{rows:4},title:!1},M),Z=void 0!==T,J=Object.assign(Object.assign({},L),{[Z?"activeKey":"defaultActiveKey"]:Z?T:P,tabBarExtraContent:z}),ee=(0,n.default)(j),et=ee&&"default"!==ee?ee:"large",er=B?t.createElement(i.default,Object.assign({size:et},J,{className:`${F}-head-tabs`,onChange:t=>{var r;null==(r=e.onTabChange)||r.call(e,t)},items:B.map(e=>{var{tab:t}=e;return Object.assign({label:t},h(e,["tab"]))})})):null;if(C||y||er){let e=(0,r.default)(`${F}-head`,D("header")),o=(0,r.default)(`${F}-head-title`,D("title")),a=(0,r.default)(`${F}-extra`,D("extra")),n=Object.assign(Object.assign({},x),Y("header"));c=t.createElement("div",{className:e,style:n},t.createElement("div",{className:`${F}-head-wrapper`},C&&t.createElement("div",{className:o,style:Y("title")},C),y&&t.createElement("div",{className:a,style:Y("extra")},y)),er)}let eo=(0,r.default)(`${F}-cover`,D("cover")),ea=E?t.createElement("div",{className:eo,style:Y("cover")},E):null,en=(0,r.default)(`${F}-body`,D("body")),el=Object.assign(Object.assign({},$),Y("body")),ei=t.createElement("div",{className:en,style:el},O?Q:M),es=(0,r.default)(`${F}-actions`,D("actions")),ed=(null==N?void 0:N.length)?t.createElement(f,{actionClasses:es,actionStyle:Y("actions"),actions:N}):null,ec=(0,o.default)(G,["onTabChange"]),eu=(0,r.default)(F,null==A?void 0:A.className,{[`${F}-loading`]:O,[`${F}-bordered`]:"borderless"!==K,[`${F}-hoverable`]:R,[`${F}-contain-grid`]:_,[`${F}-contain-tabs`]:null==B?void 0:B.length,[`${F}-${ee}`]:ee,[`${F}-type-${k}`]:!!k,[`${F}-rtl`]:"rtl"===X},m,g,U,V),em=Object.assign(Object.assign({},null==A?void 0:A.style),v);return q(t.createElement("div",Object.assign({ref:s},ec,{className:eu,style:em}),c,ea,ei,ed))});var y=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};v.Grid=d,v.Meta=e=>{let{prefixCls:o,className:n,avatar:l,title:i,description:s}=e,d=y(e,["prefixCls","className","avatar","title","description"]),{getPrefixCls:c}=t.useContext(a.ConfigContext),u=c("card",o),m=(0,r.default)(`${u}-meta`,n),g=l?t.createElement("div",{className:`${u}-meta-avatar`},l):null,p=i?t.createElement("div",{className:`${u}-meta-title`},i):null,b=s?t.createElement("div",{className:`${u}-meta-description`},s):null,h=p||b?t.createElement("div",{className:`${u}-meta-detail`},p,b):null;return t.createElement("div",Object.assign({},d,{className:m}),g,h)},e.s(["Card",0,v],175712)},869216,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),o=e.i(908206),a=e.i(242064),n=e.i(517455),l=e.i(150073);let i={xxl:3,xl:3,lg:3,md:3,sm:2,xs:1},s=t.default.createContext({});var d=e.i(876556),c=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r},u=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let m=e=>{let{itemPrefixCls:o,component:a,span:n,className:l,style:i,labelStyle:d,contentStyle:c,bordered:u,label:m,content:g,colon:p,type:b,styles:h}=e,{classNames:f}=t.useContext(s),v=Object.assign(Object.assign({},d),null==h?void 0:h.label),y=Object.assign(Object.assign({},c),null==h?void 0:h.content);if(u)return t.createElement(a,{colSpan:n,style:i,className:(0,r.default)(l,{[`${o}-item-${b}`]:"label"===b||"content"===b,[null==f?void 0:f.label]:(null==f?void 0:f.label)&&"label"===b,[null==f?void 0:f.content]:(null==f?void 0:f.content)&&"content"===b})},null!=m&&t.createElement("span",{style:v},m),null!=g&&t.createElement("span",{style:y},g));return t.createElement(a,{colSpan:n,style:i,className:(0,r.default)(`${o}-item`,l)},t.createElement("div",{className:`${o}-item-container`},null!=m&&t.createElement("span",{style:v,className:(0,r.default)(`${o}-item-label`,null==f?void 0:f.label,{[`${o}-item-no-colon`]:!p})},m),null!=g&&t.createElement("span",{style:y,className:(0,r.default)(`${o}-item-content`,null==f?void 0:f.content)},g)))};function g(e,{colon:r,prefixCls:o,bordered:a},{component:n,type:l,showLabel:i,showContent:s,labelStyle:d,contentStyle:c,styles:u}){return e.map(({label:e,children:g,prefixCls:p=o,className:b,style:h,labelStyle:f,contentStyle:v,span:y=1,key:x,styles:$},C)=>"string"==typeof n?t.createElement(m,{key:`${l}-${x||C}`,className:b,style:h,styles:{label:Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),f),null==$?void 0:$.label),content:Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),v),null==$?void 0:$.content)},span:y,colon:r,component:n,itemPrefixCls:p,bordered:a,label:i?e:null,content:s?g:null,type:l}):[t.createElement(m,{key:`label-${x||C}`,className:b,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},d),null==u?void 0:u.label),h),f),null==$?void 0:$.label),span:1,colon:r,component:n[0],itemPrefixCls:p,bordered:a,label:e,type:"label"}),t.createElement(m,{key:`content-${x||C}`,className:b,style:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},c),null==u?void 0:u.content),h),v),null==$?void 0:$.content),span:2*y-1,component:n[1],itemPrefixCls:p,bordered:a,content:g,type:"content"})])}let p=e=>{let r=t.useContext(s),{prefixCls:o,vertical:a,row:n,index:l,bordered:i}=e;return a?t.createElement(t.Fragment,null,t.createElement("tr",{key:`label-${l}`,className:`${o}-row`},g(n,e,Object.assign({component:"th",type:"label",showLabel:!0},r))),t.createElement("tr",{key:`content-${l}`,className:`${o}-row`},g(n,e,Object.assign({component:"td",type:"content",showContent:!0},r)))):t.createElement("tr",{key:l,className:`${o}-row`},g(n,e,Object.assign({component:i?["th","td"]:"td",type:"item",showLabel:!0,showContent:!0},r)))};e.i(296059);var b=e.i(915654),h=e.i(183293),f=e.i(246422),v=e.i(838378);let y=(0,f.genStyleHooks)("Descriptions",e=>(e=>{let{componentCls:t,extraColor:r,itemPaddingBottom:o,itemPaddingEnd:a,colonMarginRight:n,colonMarginLeft:l,titleMarginBottom:i}=e;return{[t]:Object.assign(Object.assign(Object.assign({},(0,h.resetComponent)(e)),(e=>{let{componentCls:t,labelBg:r}=e;return{[`&${t}-bordered`]:{[`> ${t}-view`]:{border:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"> table":{tableLayout:"auto"},[`${t}-row`]:{borderBottom:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:first-child":{"> th:first-child, > td:first-child":{borderStartStartRadius:e.borderRadiusLG}},"&:last-child":{borderBottom:"none","> th:first-child, > td:first-child":{borderEndStartRadius:e.borderRadiusLG}},[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.padding)} ${(0,b.unit)(e.paddingLG)}`,borderInlineEnd:`${(0,b.unit)(e.lineWidth)} ${e.lineType} ${e.colorSplit}`,"&:last-child":{borderInlineEnd:"none"}},[`> ${t}-item-label`]:{color:e.colorTextSecondary,backgroundColor:r,"&::after":{display:"none"}}}},[`&${t}-middle`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.paddingSM)} ${(0,b.unit)(e.paddingLG)}`}}},[`&${t}-small`]:{[`${t}-row`]:{[`> ${t}-item-label, > ${t}-item-content`]:{padding:`${(0,b.unit)(e.paddingXS)} ${(0,b.unit)(e.padding)}`}}}}}})(e)),{"&-rtl":{direction:"rtl"},[`${t}-header`]:{display:"flex",alignItems:"center",marginBottom:i},[`${t}-title`]:Object.assign(Object.assign({},h.textEllipsis),{flex:"auto",color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.fontSizeLG,lineHeight:e.lineHeightLG}),[`${t}-extra`]:{marginInlineStart:"auto",color:r,fontSize:e.fontSize},[`${t}-view`]:{width:"100%",borderRadius:e.borderRadiusLG,table:{width:"100%",tableLayout:"fixed",borderCollapse:"collapse"}},[`${t}-row`]:{"> th, > td":{paddingBottom:o,paddingInlineEnd:a},"> th:last-child, > td:last-child":{paddingInlineEnd:0},"&:last-child":{borderBottom:"none","> th, > td":{paddingBottom:0}}},[`${t}-item-label`]:{color:e.labelColor,fontWeight:"normal",fontSize:e.fontSize,lineHeight:e.lineHeight,textAlign:"start","&::after":{content:'":"',position:"relative",top:-.5,marginInline:`${(0,b.unit)(l)} ${(0,b.unit)(n)}`},[`&${t}-item-no-colon::after`]:{content:'""'}},[`${t}-item-no-label`]:{"&::after":{margin:0,content:'""'}},[`${t}-item-content`]:{display:"table-cell",flex:1,color:e.contentColor,fontSize:e.fontSize,lineHeight:e.lineHeight,wordBreak:"break-word",overflowWrap:"break-word"},[`${t}-item`]:{paddingBottom:0,verticalAlign:"top","&-container":{display:"flex",[`${t}-item-label`]:{display:"inline-flex",alignItems:"baseline"},[`${t}-item-content`]:{display:"inline-flex",alignItems:"baseline",minWidth:"1em"}}},"&-middle":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingSM}}},"&-small":{[`${t}-row`]:{"> th, > td":{paddingBottom:e.paddingXS}}}})}})((0,v.mergeToken)(e,{})),e=>({labelBg:e.colorFillAlter,labelColor:e.colorTextTertiary,titleColor:e.colorText,titleMarginBottom:e.fontSizeSM*e.lineHeightSM,itemPaddingBottom:e.padding,itemPaddingEnd:e.padding,colonMarginRight:e.marginXS,colonMarginLeft:e.marginXXS/2,contentColor:e.colorText,extraColor:e.colorText}));var x=function(e,t){var r={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,o=Object.getOwnPropertySymbols(e);at.indexOf(o[a])&&Object.prototype.propertyIsEnumerable.call(e,o[a])&&(r[o[a]]=e[o[a]]);return r};let $=e=>{let m,{prefixCls:g,title:b,extra:h,column:f,colon:v=!0,bordered:$,layout:C,children:O,className:S,rootClassName:w,style:j,size:k,labelStyle:E,contentStyle:N,styles:B,items:M,classNames:T}=e,P=x(e,["prefixCls","title","extra","column","colon","bordered","layout","children","className","rootClassName","style","size","labelStyle","contentStyle","styles","items","classNames"]),{getPrefixCls:z,direction:R,className:L,style:I,classNames:H,styles:G}=(0,a.useComponentConfig)("descriptions"),W=z("descriptions",g),X=(0,l.default)(),A=t.useMemo(()=>{var e;return"number"==typeof f?f:null!=(e=(0,o.matchScreen)(X,Object.assign(Object.assign({},i),f)))?e:3},[X,f]),K=(m=t.useMemo(()=>M||(0,d.default)(O).map(e=>Object.assign(Object.assign({},null==e?void 0:e.props),{key:e.key})),[M,O]),t.useMemo(()=>m.map(e=>{var{span:t}=e,r=c(e,["span"]);return"filled"===t?Object.assign(Object.assign({},r),{filled:!0}):Object.assign(Object.assign({},r),{span:"number"==typeof t?t:(0,o.matchScreen)(X,t)})}),[m,X])),D=(0,n.default)(k),Y=((e,r)=>{let[o,a]=(0,t.useMemo)(()=>{let t,o,a,n;return t=[],o=[],a=!1,n=0,r.filter(e=>e).forEach(r=>{let{filled:l}=r,i=u(r,["filled"]);if(l){o.push(i),t.push(o),o=[],n=0;return}let s=e-n;(n+=r.span||1)>=e?(n>e?(a=!0,o.push(Object.assign(Object.assign({},i),{span:s}))):o.push(i),t.push(o),o=[],n=0):o.push(i)}),o.length>0&&t.push(o),[t=t.map(t=>{let r=t.reduce((e,t)=>e+(t.span||1),0);if(r({labelStyle:E,contentStyle:N,styles:{content:Object.assign(Object.assign({},G.content),null==B?void 0:B.content),label:Object.assign(Object.assign({},G.label),null==B?void 0:B.label)},classNames:{label:(0,r.default)(H.label,null==T?void 0:T.label),content:(0,r.default)(H.content,null==T?void 0:T.content)}}),[E,N,B,T,H,G]);return _(t.createElement(s.Provider,{value:U},t.createElement("div",Object.assign({className:(0,r.default)(W,L,H.root,null==T?void 0:T.root,{[`${W}-${D}`]:D&&"default"!==D,[`${W}-bordered`]:!!$,[`${W}-rtl`]:"rtl"===R},S,w,F,q),style:Object.assign(Object.assign(Object.assign(Object.assign({},I),G.root),null==B?void 0:B.root),j)},P),(b||h)&&t.createElement("div",{className:(0,r.default)(`${W}-header`,H.header,null==T?void 0:T.header),style:Object.assign(Object.assign({},G.header),null==B?void 0:B.header)},b&&t.createElement("div",{className:(0,r.default)(`${W}-title`,H.title,null==T?void 0:T.title),style:Object.assign(Object.assign({},G.title),null==B?void 0:B.title)},b),h&&t.createElement("div",{className:(0,r.default)(`${W}-extra`,H.extra,null==T?void 0:T.extra),style:Object.assign(Object.assign({},G.extra),null==B?void 0:B.extra)},h)),t.createElement("div",{className:`${W}-view`},t.createElement("table",null,t.createElement("tbody",null,Y.map((e,r)=>t.createElement(p,{key:r,index:r,colon:v,prefixCls:W,vertical:"vertical"===C,bordered:$,row:e}))))))))};$.Item=({children:e})=>e,e.s(["Descriptions",0,$],869216)},270377,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"}}]},name:"exclamation-circle",theme:"outlined"};var a=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(a.default,(0,t.default)({},e,{ref:n,icon:o}))});e.s(["ExclamationCircleOutlined",0,n],270377)},530212,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"}))});e.s(["ArrowLeftIcon",0,r],530212)},350967,46757,e=>{"use strict";var t=e.i(290571),r=e.i(444755),o=e.i(673706),a=e.i(271645);let n={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},l={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},i={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},s={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,n,"gridColsLg",0,s,"gridColsMd",0,i,"gridColsSm",0,l],46757);let d=(0,o.makeClassName)("Grid"),c=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=a.default.forwardRef((e,o)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:g,numItemsLg:p,children:b,className:h}=e,f=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),v=c(u,n),y=c(m,l),x=c(g,i),$=c(p,s),C=(0,r.tremorTwMerge)(v,y,x,$);return a.default.createElement("div",Object.assign({ref:o,className:(0,r.tremorTwMerge)(d("root"),"grid",C,h)},f),b)});u.displayName="Grid",e.s(["Grid",0,u],350967)},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},555987,e=>{"use strict";var t=e.i(221688),r=e.i(950643);let o=/^(https?:|data:|blob:|\/\/)/i;e.s(["resolveLogoSrc",0,(e,a=t.serverRootPath)=>{if(e){let t;return o.test(e)?e:(t=(0,r.normalizeRootPath)(a),`${t}${e.startsWith("/")?e:`/${e}`}`)}}])},447566,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let o={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var a=e.i(9583),n=r.forwardRef(function(e,n){return r.createElement(a.default,(0,t.default)({},e,{ref:n,icon:o}))});e.s(["ArrowLeftOutlined",0,n],447566)},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),o=e.i(271645);let a=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],n=e=>({_s:e,status:a[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),l=e=>e?6:5,i=(e,t,r,o,a)=>{clearTimeout(o.current);let l=n(e);t(l),r.current=l,a&&a({current:l})};var s=e.i(480731),d=e.i(444755),c=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return o.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),o.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},p=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},b=(0,c.makeClassName)("Button"),h=({loading:e,iconSize:t,iconPosition:r,Icon:a,needMargin:n,transitionStatus:l})=>{let i=n?r===s.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?o.default.createElement(u,{className:(0,d.tremorTwMerge)(b("icon"),"animate-spin shrink-0",i,m.default,m[l]),style:{transition:"width 150ms"}}):o.default.createElement(a,{className:(0,d.tremorTwMerge)(b("icon"),"shrink-0",t,i)})},f=o.default.forwardRef((e,a)=>{let{icon:u,iconPosition:m=s.HorizontalPositions.Left,size:f=s.Sizes.SM,color:v,variant:y="primary",disabled:x,loading:$=!1,loadingText:C,children:O,tooltip:S,className:w}=e,j=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),k=$||x,E=void 0!==u||$,N=$&&C,B=!(!O&&!N),M=(0,d.tremorTwMerge)(g[f].height,g[f].width),T="light"!==y?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",P=p(y,v),z=("light"!==y?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[f],{tooltipProps:R,getReferenceProps:L}=(0,r.useTooltip)(300),[I,H]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:a,timeout:s,initialEntered:d,mountOnEnter:c,unmountOnExit:u,onStateChange:m}={})=>{let[g,p]=(0,o.useState)(()=>n(d?2:l(c))),b=(0,o.useRef)(g),h=(0,o.useRef)(0),[f,v]="object"==typeof s?[s.enter,s.exit]:[s,s],y=(0,o.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return l(t)}})(b.current._s,u);e&&i(e,p,b,h,m)},[m,u]);return[g,(0,o.useCallback)(o=>{let n=e=>{switch(i(e,p,b,h,m),e){case 1:f>=0&&(h.current=((...e)=>setTimeout(...e))(y,f));break;case 4:v>=0&&(h.current=((...e)=>setTimeout(...e))(y,v));break;case 0:case 3:h.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||n(e+1)},0)}},s=b.current.isEnter;"boolean"!=typeof o&&(o=!s),o?s||n(e?+!r:2):s&&n(t?a?3:4:l(u))},[y,m,e,t,r,a,f,v,u]),y]})({timeout:50});return(0,o.useEffect)(()=>{H($)},[$]),o.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([a,R.refs.setReference]),className:(0,d.tremorTwMerge)(b("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",T,z.paddingX,z.paddingY,z.fontSize,P.textColor,P.bgColor,P.borderColor,P.hoverBorderColor,k?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(p(y,v).hoverTextColor,p(y,v).hoverBgColor,p(y,v).hoverBorderColor),w),disabled:k},L,j),o.default.createElement(r.default,Object.assign({text:S},R)),E&&m!==s.HorizontalPositions.Right?o.default.createElement(h,{loading:$,iconSize:M,iconPosition:m,Icon:u,transitionStatus:I.status,needMargin:B}):null,N||O?o.default.createElement("span",{className:(0,d.tremorTwMerge)(b("text"),"text-tremor-default whitespace-nowrap")},N?C:O):null,E&&m===s.HorizontalPositions.Right?o.default.createElement(h,{loading:$,iconSize:M,iconPosition:m,Icon:u,transitionStatus:I.status,needMargin:B}):null)});f.displayName="Button",e.s(["Button",0,f],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),o=e.i(673706),a=e.i(271645);let n=a.default.forwardRef((e,n)=>{let{color:l,className:i,children:s}=e;return a.default.createElement("p",{ref:n,className:(0,r.tremorTwMerge)("text-tremor-default",l?(0,o.getColorClassNames)(l,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},s)});n.displayName="Text",e.s(["default",0,n],936325),e.s(["Text",0,n],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),o=e.i(480731),a=e.i(95779),n=e.i(444755),l=e.i(673706);let i=(0,l.makeClassName)("Card"),s=r.default.forwardRef((e,s)=>{let{decoration:d="",decorationColor:c,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:s,className:(0,n.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,l.getColorClassNames)(c,a.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case o.HorizontalPositions.Left:return"border-l-4";case o.VerticalPositions.Top:return"border-t-4";case o.HorizontalPositions.Right:return"border-r-4";case o.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},g),u)});s.displayName="Card",e.s(["Card",0,s],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),o=e.i(444755),a=e.i(673706),n=e.i(271645);let l=n.default.forwardRef((e,l)=>{let{color:i,children:s,className:d}=e,c=(0,t.__rest)(e,["color","children","className"]);return n.default.createElement("p",Object.assign({ref:l,className:(0,o.tremorTwMerge)("font-medium text-tremor-title",i?(0,a.getColorClassNames)(i,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),s)});l.displayName="Title",e.s(["Title",0,l],629569)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0u0zny6.djks2.js b/litellm/proxy/_experimental/out/_next/static/chunks/0u0zny6.djks2.js new file mode 100644 index 00000000000..bb72707cb43 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0u0zny6.djks2.js @@ -0,0 +1,3 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,144267,e=>{"use strict";let t,r,n;var a,o,l,i=e.i(843476),s=e.i(271645),u=e.i(290571);let d=e=>{var t=(0,u.__rest)(e,[]);return s.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor"}),s.default.createElement("path",{fillRule:"evenodd",d:"M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z",clipRule:"evenodd"}))};var c=e.i(446428),m=e.i(281092);function f(e,t){let r=(0,m.toDate)(e,t?.in);return r.setHours(0,0,0,0),r}function h(e){return f(Date.now(),e)}function p(e,t){let r=(0,m.toDate)(e,t?.in);return r.setDate(1),r.setHours(0,0,0,0),r}var b=e.i(444755),v=e.i(103471),g=e.i(677241),w=e.i(595727);function y(e,t,r){return(0,w.addDays)(e,-t,r)}var x=e.i(688594);function k(e,t,r){var n;let{years:a=0,months:o=0,weeks:l=0,days:i=0,hours:s=0,minutes:u=0,seconds:d=0}=t,c=y((n=o+12*a,(0,x.addMonths)(e,-n,r)),i+7*l,r);return(0,g.constructFrom)(r?.in||e,c-1e3*(d+60*(u+60*s)))}function M(e,t){let r=(0,m.toDate)(e,t?.in);return r.setFullYear(r.getFullYear(),0,1),r.setHours(0,0,0,0),r}function D(e,t){let r,n=t?.in;return e.forEach(e=>{n||"object"!=typeof e||(n=g.constructFrom.bind(null,e));let t=(0,m.toDate)(e,n);(!r||r{n||"object"!=typeof e||(n=g.constructFrom.bind(null,e));let t=(0,m.toDate)(e,n);(!r||r>t||isNaN(+t))&&(r=t)}),(0,g.constructFrom)(n,r||NaN)}let E={lessThanXSeconds:{one:"less than a second",other:"less than {{count}} seconds"},xSeconds:{one:"1 second",other:"{{count}} seconds"},halfAMinute:"half a minute",lessThanXMinutes:{one:"less than a minute",other:"less than {{count}} minutes"},xMinutes:{one:"1 minute",other:"{{count}} minutes"},aboutXHours:{one:"about 1 hour",other:"about {{count}} hours"},xHours:{one:"1 hour",other:"{{count}} hours"},xDays:{one:"1 day",other:"{{count}} days"},aboutXWeeks:{one:"about 1 week",other:"about {{count}} weeks"},xWeeks:{one:"1 week",other:"{{count}} weeks"},aboutXMonths:{one:"about 1 month",other:"about {{count}} months"},xMonths:{one:"1 month",other:"{{count}} months"},aboutXYears:{one:"about 1 year",other:"about {{count}} years"},xYears:{one:"1 year",other:"{{count}} years"},overXYears:{one:"over 1 year",other:"over {{count}} years"},almostXYears:{one:"almost 1 year",other:"almost {{count}} years"}};function S(e){return (t={})=>{let r=t.width?String(t.width):e.defaultWidth;return e.formats[r]||e.formats[e.defaultWidth]}}let P={date:S({formats:{full:"EEEE, MMMM do, y",long:"MMMM do, y",medium:"MMM d, y",short:"MM/dd/yyyy"},defaultWidth:"full"}),time:S({formats:{full:"h:mm:ss a zzzz",long:"h:mm:ss a z",medium:"h:mm:ss a",short:"h:mm a"},defaultWidth:"full"}),dateTime:S({formats:{full:"{{date}} 'at' {{time}}",long:"{{date}} 'at' {{time}}",medium:"{{date}}, {{time}}",short:"{{date}}, {{time}}"},defaultWidth:"full"})},T={lastWeek:"'last' eeee 'at' p",yesterday:"'yesterday at' p",today:"'today at' p",tomorrow:"'tomorrow at' p",nextWeek:"eeee 'at' p",other:"P"};function C(e){return(t,r)=>{let n;if("formatting"===(r?.context?String(r.context):"standalone")&&e.formattingValues){let t=e.defaultFormattingWidth||e.defaultWidth,a=r?.width?String(r.width):t;n=e.formattingValues[a]||e.formattingValues[t]}else{let t=e.defaultWidth,a=r?.width?String(r.width):e.defaultWidth;n=e.values[a]||e.values[t]}return n[e.argumentCallback?e.argumentCallback(t):t]}}function _(e){return(t,r={})=>{let n,a=r.width,o=a&&e.matchPatterns[a]||e.matchPatterns[e.defaultMatchWidth],l=t.match(o);if(!l)return null;let i=l[0],s=a&&e.parsePatterns[a]||e.parsePatterns[e.defaultParseWidth],u=Array.isArray(s)?function(e,t){for(let r=0;re.test(i)):function(e,t){for(let r in e)if(Object.prototype.hasOwnProperty.call(e,r)&&t(e[r]))return r}(s,e=>e.test(i));return n=e.valueCallback?e.valueCallback(u):u,{value:n=r.valueCallback?r.valueCallback(n):n,rest:t.slice(i.length)}}}let j={code:"en-US",formatDistance:(e,t,r)=>{let n,a=E[e];if(n="string"==typeof a?a:1===t?a.one:a.other.replace("{{count}}",t.toString()),r?.addSuffix)if(r.comparison&&r.comparison>0)return"in "+n;else return n+" ago";return n},formatLong:P,formatRelative:(e,t,r,n)=>T[e],localize:{ordinalNumber:(e,t)=>{let r=Number(e),n=r%100;if(n>20||n<10)switch(n%10){case 1:return r+"st";case 2:return r+"nd";case 3:return r+"rd"}return r+"th"},era:C({values:{narrow:["B","A"],abbreviated:["BC","AD"],wide:["Before Christ","Anno Domini"]},defaultWidth:"wide"}),quarter:C({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["1st quarter","2nd quarter","3rd quarter","4th quarter"]},defaultWidth:"wide",argumentCallback:e=>e-1}),month:C({values:{narrow:["J","F","M","A","M","J","J","A","S","O","N","D"],abbreviated:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],wide:["January","February","March","April","May","June","July","August","September","October","November","December"]},defaultWidth:"wide"}),day:C({values:{narrow:["S","M","T","W","T","F","S"],short:["Su","Mo","Tu","We","Th","Fr","Sa"],abbreviated:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],wide:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},defaultWidth:"wide"}),dayPeriod:C({values:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"}},defaultWidth:"wide",formattingValues:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:(a={matchPattern:/^(\d+)(th|st|nd|rd)?/i,parsePattern:/\d+/i,valueCallback:e=>parseInt(e,10)},(e,t={})=>{let r=e.match(a.matchPattern);if(!r)return null;let n=r[0],o=e.match(a.parsePattern);if(!o)return null;let l=a.valueCallback?a.valueCallback(o[0]):o[0];return{value:l=t.valueCallback?t.valueCallback(l):l,rest:e.slice(n.length)}}),era:_({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:"any"}),quarter:_({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:"wide",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:"any",valueCallback:e=>e+1}),month:_({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:"any"}),day:_({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:"any"}),dayPeriod:_({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:"any"})},options:{weekStartsOn:0,firstWeekContainsDate:1}},F={};function L(e){let t=(0,m.toDate)(e),r=new Date(Date.UTC(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours(),t.getMinutes(),t.getSeconds(),t.getMilliseconds()));return r.setUTCFullYear(t.getFullYear()),e-r}function O(e,...t){let r=g.constructFrom.bind(null,e||t.find(e=>"object"==typeof e));return t.map(r)}var I=e.i(234662);function Y(e,t,r){let[n,a]=O(r?.in,e,t),o=f(n),l=f(a);return Math.round((o-L(o)-(l-L(l)))/I.millisecondsInDay)}function W(e,t){let r=t?.weekStartsOn??t?.locale?.options?.weekStartsOn??F.weekStartsOn??F.locale?.options?.weekStartsOn??0,n=(0,m.toDate)(e,t?.in),a=n.getDay();return n.setDate(n.getDate()-(7*(a=o.getTime()?n+1:r.getTime()>=i.getTime()?n:n-1}function B(e,t){let r,n,a=(0,m.toDate)(e,t?.in);return Math.round((H(a)-(r=R(a,void 0),(n=(0,g.constructFrom)(a,0)).setFullYear(r,0,4),n.setHours(0,0,0,0),H(n)))/I.millisecondsInWeek)+1}function q(e,t){let r=(0,m.toDate)(e,t?.in),n=r.getFullYear(),a=t?.firstWeekContainsDate??t?.locale?.options?.firstWeekContainsDate??F.firstWeekContainsDate??F.locale?.options?.firstWeekContainsDate??1,o=(0,g.constructFrom)(t?.in||e,0);o.setFullYear(n+1,0,a),o.setHours(0,0,0,0);let l=W(o,t),i=(0,g.constructFrom)(t?.in||e,0);i.setFullYear(n,0,a),i.setHours(0,0,0,0);let s=W(i,t);return+r>=+l?n+1:+r>=+s?n:n-1}function A(e,t){let r,n,a,o=(0,m.toDate)(e,t?.in);return Math.round((W(o,t)-(r=t?.firstWeekContainsDate??t?.locale?.options?.firstWeekContainsDate??F.firstWeekContainsDate??F.locale?.options?.firstWeekContainsDate??1,n=q(o,t),(a=(0,g.constructFrom)(t?.in||o,0)).setFullYear(n,0,r),a.setHours(0,0,0,0),W(a,t)))/I.millisecondsInWeek)+1}function Q(e,t){let r=Math.abs(e).toString().padStart(t,"0");return(e<0?"-":"")+r}let G={y(e,t){let r=e.getFullYear(),n=r>0?r:1-r;return Q("yy"===t?n%100:n,t.length)},M(e,t){let r=e.getMonth();return"M"===t?String(r+1):Q(r+1,2)},d:(e,t)=>Q(e.getDate(),t.length),a(e,t){let r=e.getHours()/12>=1?"pm":"am";switch(t){case"a":case"aa":return r.toUpperCase();case"aaa":return r;case"aaaaa":return r[0];default:return"am"===r?"a.m.":"p.m."}},h:(e,t)=>Q(e.getHours()%12||12,t.length),H:(e,t)=>Q(e.getHours(),t.length),m:(e,t)=>Q(e.getMinutes(),t.length),s:(e,t)=>Q(e.getSeconds(),t.length),S(e,t){let r=t.length;return Q(Math.trunc(e.getMilliseconds()*Math.pow(10,r-3)),t.length)}},z={G:function(e,t,r){let n=+(e.getFullYear()>0);switch(t){case"G":case"GG":case"GGG":return r.era(n,{width:"abbreviated"});case"GGGGG":return r.era(n,{width:"narrow"});default:return r.era(n,{width:"wide"})}},y:function(e,t,r){if("yo"===t){let t=e.getFullYear();return r.ordinalNumber(t>0?t:1-t,{unit:"year"})}return G.y(e,t)},Y:function(e,t,r,n){let a=q(e,n),o=a>0?a:1-a;return"YY"===t?Q(o%100,2):"Yo"===t?r.ordinalNumber(o,{unit:"year"}):Q(o,t.length)},R:function(e,t){return Q(R(e),t.length)},u:function(e,t){return Q(e.getFullYear(),t.length)},Q:function(e,t,r){let n=Math.ceil((e.getMonth()+1)/3);switch(t){case"Q":return String(n);case"QQ":return Q(n,2);case"Qo":return r.ordinalNumber(n,{unit:"quarter"});case"QQQ":return r.quarter(n,{width:"abbreviated",context:"formatting"});case"QQQQQ":return r.quarter(n,{width:"narrow",context:"formatting"});default:return r.quarter(n,{width:"wide",context:"formatting"})}},q:function(e,t,r){let n=Math.ceil((e.getMonth()+1)/3);switch(t){case"q":return String(n);case"qq":return Q(n,2);case"qo":return r.ordinalNumber(n,{unit:"quarter"});case"qqq":return r.quarter(n,{width:"abbreviated",context:"standalone"});case"qqqqq":return r.quarter(n,{width:"narrow",context:"standalone"});default:return r.quarter(n,{width:"wide",context:"standalone"})}},M:function(e,t,r){let n=e.getMonth();switch(t){case"M":case"MM":return G.M(e,t);case"Mo":return r.ordinalNumber(n+1,{unit:"month"});case"MMM":return r.month(n,{width:"abbreviated",context:"formatting"});case"MMMMM":return r.month(n,{width:"narrow",context:"formatting"});default:return r.month(n,{width:"wide",context:"formatting"})}},L:function(e,t,r){let n=e.getMonth();switch(t){case"L":return String(n+1);case"LL":return Q(n+1,2);case"Lo":return r.ordinalNumber(n+1,{unit:"month"});case"LLL":return r.month(n,{width:"abbreviated",context:"standalone"});case"LLLLL":return r.month(n,{width:"narrow",context:"standalone"});default:return r.month(n,{width:"wide",context:"standalone"})}},w:function(e,t,r,n){let a=A(e,n);return"wo"===t?r.ordinalNumber(a,{unit:"week"}):Q(a,t.length)},I:function(e,t,r){let n=B(e);return"Io"===t?r.ordinalNumber(n,{unit:"week"}):Q(n,t.length)},d:function(e,t,r){return"do"===t?r.ordinalNumber(e.getDate(),{unit:"date"}):G.d(e,t)},D:function(e,t,r){let n,a=Y(n=(0,m.toDate)(e,void 0),M(n))+1;return"Do"===t?r.ordinalNumber(a,{unit:"dayOfYear"}):Q(a,t.length)},E:function(e,t,r){let n=e.getDay();switch(t){case"E":case"EE":case"EEE":return r.day(n,{width:"abbreviated",context:"formatting"});case"EEEEE":return r.day(n,{width:"narrow",context:"formatting"});case"EEEEEE":return r.day(n,{width:"short",context:"formatting"});default:return r.day(n,{width:"wide",context:"formatting"})}},e:function(e,t,r,n){let a=e.getDay(),o=(a-n.weekStartsOn+8)%7||7;switch(t){case"e":return String(o);case"ee":return Q(o,2);case"eo":return r.ordinalNumber(o,{unit:"day"});case"eee":return r.day(a,{width:"abbreviated",context:"formatting"});case"eeeee":return r.day(a,{width:"narrow",context:"formatting"});case"eeeeee":return r.day(a,{width:"short",context:"formatting"});default:return r.day(a,{width:"wide",context:"formatting"})}},c:function(e,t,r,n){let a=e.getDay(),o=(a-n.weekStartsOn+8)%7||7;switch(t){case"c":return String(o);case"cc":return Q(o,t.length);case"co":return r.ordinalNumber(o,{unit:"day"});case"ccc":return r.day(a,{width:"abbreviated",context:"standalone"});case"ccccc":return r.day(a,{width:"narrow",context:"standalone"});case"cccccc":return r.day(a,{width:"short",context:"standalone"});default:return r.day(a,{width:"wide",context:"standalone"})}},i:function(e,t,r){let n=e.getDay(),a=0===n?7:n;switch(t){case"i":return String(a);case"ii":return Q(a,t.length);case"io":return r.ordinalNumber(a,{unit:"day"});case"iii":return r.day(n,{width:"abbreviated",context:"formatting"});case"iiiii":return r.day(n,{width:"narrow",context:"formatting"});case"iiiiii":return r.day(n,{width:"short",context:"formatting"});default:return r.day(n,{width:"wide",context:"formatting"})}},a:function(e,t,r){let n=e.getHours()/12>=1?"pm":"am";switch(t){case"a":case"aa":return r.dayPeriod(n,{width:"abbreviated",context:"formatting"});case"aaa":return r.dayPeriod(n,{width:"abbreviated",context:"formatting"}).toLowerCase();case"aaaaa":return r.dayPeriod(n,{width:"narrow",context:"formatting"});default:return r.dayPeriod(n,{width:"wide",context:"formatting"})}},b:function(e,t,r){let n,a=e.getHours();switch(n=12===a?"noon":0===a?"midnight":a/12>=1?"pm":"am",t){case"b":case"bb":return r.dayPeriod(n,{width:"abbreviated",context:"formatting"});case"bbb":return r.dayPeriod(n,{width:"abbreviated",context:"formatting"}).toLowerCase();case"bbbbb":return r.dayPeriod(n,{width:"narrow",context:"formatting"});default:return r.dayPeriod(n,{width:"wide",context:"formatting"})}},B:function(e,t,r){let n,a=e.getHours();switch(n=a>=17?"evening":a>=12?"afternoon":a>=4?"morning":"night",t){case"B":case"BB":case"BBB":return r.dayPeriod(n,{width:"abbreviated",context:"formatting"});case"BBBBB":return r.dayPeriod(n,{width:"narrow",context:"formatting"});default:return r.dayPeriod(n,{width:"wide",context:"formatting"})}},h:function(e,t,r){if("ho"===t){let t=e.getHours()%12;return 0===t&&(t=12),r.ordinalNumber(t,{unit:"hour"})}return G.h(e,t)},H:function(e,t,r){return"Ho"===t?r.ordinalNumber(e.getHours(),{unit:"hour"}):G.H(e,t)},K:function(e,t,r){let n=e.getHours()%12;return"Ko"===t?r.ordinalNumber(n,{unit:"hour"}):Q(n,t.length)},k:function(e,t,r){let n=e.getHours();return(0===n&&(n=24),"ko"===t)?r.ordinalNumber(n,{unit:"hour"}):Q(n,t.length)},m:function(e,t,r){return"mo"===t?r.ordinalNumber(e.getMinutes(),{unit:"minute"}):G.m(e,t)},s:function(e,t,r){return"so"===t?r.ordinalNumber(e.getSeconds(),{unit:"second"}):G.s(e,t)},S:function(e,t){return G.S(e,t)},X:function(e,t,r){let n=e.getTimezoneOffset();if(0===n)return"Z";switch(t){case"X":return $(n);case"XXXX":case"XX":return K(n);default:return K(n,":")}},x:function(e,t,r){let n=e.getTimezoneOffset();switch(t){case"x":return $(n);case"xxxx":case"xx":return K(n);default:return K(n,":")}},O:function(e,t,r){let n=e.getTimezoneOffset();switch(t){case"O":case"OO":case"OOO":return"GMT"+V(n,":");default:return"GMT"+K(n,":")}},z:function(e,t,r){let n=e.getTimezoneOffset();switch(t){case"z":case"zz":case"zzz":return"GMT"+V(n,":");default:return"GMT"+K(n,":")}},t:function(e,t,r){return Q(Math.trunc(e/1e3),t.length)},T:function(e,t,r){return Q(+e,t.length)}};function V(e,t=""){let r=e>0?"-":"+",n=Math.abs(e),a=Math.trunc(n/60),o=n%60;return 0===o?r+String(a):r+String(a)+t+Q(o,2)}function $(e,t){return e%60==0?(e>0?"-":"+")+Q(Math.abs(e)/60,2):K(e,t)}function K(e,t=""){let r=Math.abs(e);return(e>0?"-":"+")+Q(Math.trunc(r/60),2)+t+Q(r%60,2)}let X=(e,t)=>{switch(e){case"P":return t.date({width:"short"});case"PP":return t.date({width:"medium"});case"PPP":return t.date({width:"long"});default:return t.date({width:"full"})}},Z=(e,t)=>{switch(e){case"p":return t.time({width:"short"});case"pp":return t.time({width:"medium"});case"ppp":return t.time({width:"long"});default:return t.time({width:"full"})}},U={p:Z,P:(e,t)=>{let r,n=e.match(/(P+)(p+)?/)||[],a=n[1],o=n[2];if(!o)return X(e,t);switch(a){case"P":r=t.dateTime({width:"short"});break;case"PP":r=t.dateTime({width:"medium"});break;case"PPP":r=t.dateTime({width:"long"});break;default:r=t.dateTime({width:"full"})}return r.replace("{{date}}",X(a,t)).replace("{{time}}",Z(o,t))}},J=/^D+$/,ee=/^Y+$/,et=["D","DD","YY","YYYY"];function er(e){return e instanceof Date||"object"==typeof e&&"[object Date]"===Object.prototype.toString.call(e)}let en=/[yYQqMLwIdDecihHKkms]o|(\w)\1*|''|'(''|[^'])+('|$)|./g,ea=/P+p+|P+|p+|''|'(''|[^'])+('|$)|./g,eo=/^'([^]*?)'?$/,el=/''/g,ei=/[a-zA-Z]/;function es(e,t,r){let n=r?.locale??F.locale??j,a=r?.firstWeekContainsDate??r?.locale?.options?.firstWeekContainsDate??F.firstWeekContainsDate??F.locale?.options?.firstWeekContainsDate??1,o=r?.weekStartsOn??r?.locale?.options?.weekStartsOn??F.weekStartsOn??F.locale?.options?.weekStartsOn??0,l=(0,m.toDate)(e,r?.in);if(!er(l)&&"number"!=typeof l||isNaN(+(0,m.toDate)(l)))throw RangeError("Invalid time value");let i=t.match(ea).map(e=>{let t=e[0];return"p"===t||"P"===t?(0,U[t])(e,n.formatLong):e}).join("").match(en).map(e=>{if("''"===e)return{isToken:!1,value:"'"};let t=e[0];if("'"===t){var r;let t;return{isToken:!1,value:(t=(r=e).match(eo))?t[1].replace(el,"'"):r}}if(z[t])return{isToken:!0,value:e};if(t.match(ei))throw RangeError("Format string contains an unescaped latin alphabet character `"+t+"`");return{isToken:!1,value:e}});n.localize.preprocessor&&(i=n.localize.preprocessor(l,i));let s={firstWeekContainsDate:a,weekStartsOn:o,locale:n};return i.map(a=>{if(!a.isToken)return a.value;let o=a.value;return(!r?.useAdditionalWeekYearTokens&&ee.test(o)||!r?.useAdditionalDayOfYearTokens&&J.test(o))&&function(e,t,r){var n,a,o;let l,i=(n=e,a=t,o=r,l="Y"===n[0]?"years":"days of the month",`Use \`${n.toLowerCase()}\` instead of \`${n}\` (in \`${a}\`) for formatting ${l} to the input \`${o}\`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md`);if(console.warn(i),et.includes(e))throw RangeError(i)}(o,t,String(e)),(0,z[o[0]])(l,o,n.localize,s)}).join("")}let eu=(0,e.i(673706).makeClassName)("DateRangePicker"),ed=[{value:"tdy",text:"Today",from:h()},{value:"w",text:"Last 7 days",from:k(h(),{days:7})},{value:"t",text:"Last 30 days",from:k(h(),{days:30})},{value:"m",text:"Month to Date",from:p(h())},{value:"y",text:"Year to Date",from:M(h())}];function ec(e,t){let r=(0,m.toDate)(e,t?.in),n=r.getMonth();return r.setFullYear(r.getFullYear(),n+1,0),r.setHours(23,59,59,999),r}function em(e,t,r){let n,a,o,l,i=(0,m.toDate)(e,r?.in),s=i.getFullYear(),u=i.getDate(),d=(0,g.constructFrom)(r?.in||e,0);d.setFullYear(s,t,15),d.setHours(0,0,0,0);let c=(a=(n=(0,m.toDate)(d,void 0)).getFullYear(),o=n.getMonth(),(l=(0,g.constructFrom)(n,0)).setFullYear(a,o+1,0),l.setHours(0,0,0,0),l.getDate());return i.setMonth(t,Math.min(u,c)),i}function ef(e,t,r){let n=(0,m.toDate)(e,r?.in);return isNaN(+n)?(0,g.constructFrom)(r?.in||e,NaN):(n.setFullYear(t),n)}function eh(e,t,r){let[n,a]=O(r?.in,e,t);return 12*(n.getFullYear()-a.getFullYear())+(n.getMonth()-a.getMonth())}function ep(e,t,r){let[n,a]=O(r?.in,e,t);return n.getFullYear()===a.getFullYear()&&n.getMonth()===a.getMonth()}function eb(e,t){return+(0,m.toDate)(e)<+(0,m.toDate)(t)}function ev(e,t,r){let[n,a]=O(r?.in,e,t);return+f(n)==+f(a)}function eg(e,t){return+(0,m.toDate)(e)>+(0,m.toDate)(t)}function ew(e,t,r){return(0,w.addDays)(e,7*t,r)}function ey(e,t,r){return(0,x.addMonths)(e,12*t,r)}function ex(e,t){let r=t?.weekStartsOn??t?.locale?.options?.weekStartsOn??F.weekStartsOn??F.locale?.options?.weekStartsOn??0,n=(0,m.toDate)(e,t?.in),a=n.getDay();return n.setDate(n.getDate()+((a0,a=n?t:1-t;if(a<=50)r=e||100;else{let t=a+50;r=e+100*Math.trunc(t/100)-100*(e>=t%100)}return n?r:1-r}function e4(e){return e%400==0||e%4==0&&e%100!=0}let e3=[31,28,31,30,31,30,31,31,30,31,30,31],e5=[31,29,31,30,31,30,31,31,30,31,30,31];function e7(e,t,r){let n=r?.weekStartsOn??r?.locale?.options?.weekStartsOn??F.weekStartsOn??F.locale?.options?.weekStartsOn??0,a=(0,m.toDate)(e,r?.in),o=a.getDay(),l=7-n,i=t<0||t>6?t-(o+l)%7:((t%7+7)%7+l)%7-(o+l)%7;return(0,w.addDays)(a,i,r)}new class extends eN{priority=140;parse(e,t,r){switch(t){case"G":case"GG":case"GGG":return r.era(e,{width:"abbreviated"})||r.era(e,{width:"narrow"});case"GGGGG":return r.era(e,{width:"narrow"});default:return r.era(e,{width:"wide"})||r.era(e,{width:"abbreviated"})||r.era(e,{width:"narrow"})}}set(e,t,r){return t.era=r,e.setFullYear(r,0,1),e.setHours(0,0,0,0),e}incompatibleTokens=["R","u","t","T"]},new class extends eN{priority=130;incompatibleTokens=["Y","R","u","w","I","i","e","c","t","T"];parse(e,t,r){let n=e=>({year:e,isTwoDigitYear:"yy"===t});switch(t){case"y":return eX(eJ(4,e),n);case"yo":return eX(r.ordinalNumber(e,{unit:"year"}),n);default:return eX(eJ(t.length,e),n)}}validate(e,t){return t.isTwoDigitYear||t.year>0}set(e,t,r){let n=e.getFullYear();if(r.isTwoDigitYear){let t=e2(r.year,n);return e.setFullYear(t,0,1),e.setHours(0,0,0,0),e}let a="era"in t&&1!==t.era?1-r.year:r.year;return e.setFullYear(a,0,1),e.setHours(0,0,0,0),e}},new class extends eN{priority=130;parse(e,t,r){let n=e=>({year:e,isTwoDigitYear:"YY"===t});switch(t){case"Y":return eX(eJ(4,e),n);case"Yo":return eX(r.ordinalNumber(e,{unit:"year"}),n);default:return eX(eJ(t.length,e),n)}}validate(e,t){return t.isTwoDigitYear||t.year>0}set(e,t,r,n){let a=q(e,n);if(r.isTwoDigitYear){let t=e2(r.year,a);return e.setFullYear(t,0,n.firstWeekContainsDate),e.setHours(0,0,0,0),W(e,n)}let o="era"in t&&1!==t.era?1-r.year:r.year;return e.setFullYear(o,0,n.firstWeekContainsDate),e.setHours(0,0,0,0),W(e,n)}incompatibleTokens=["y","R","u","Q","q","M","L","I","d","D","i","t","T"]},new class extends eN{priority=130;parse(e,t){return"R"===t?e0(4,e):e0(t.length,e)}set(e,t,r){let n=(0,g.constructFrom)(e,0);return n.setFullYear(r,0,4),n.setHours(0,0,0,0),H(n)}incompatibleTokens=["G","y","Y","u","Q","q","M","L","w","d","D","e","c","t","T"]},new class extends eN{priority=130;parse(e,t){return"u"===t?e0(4,e):e0(t.length,e)}set(e,t,r){return e.setFullYear(r,0,1),e.setHours(0,0,0,0),e}incompatibleTokens=["G","y","Y","R","w","I","i","e","c","t","T"]},new class extends eN{priority=120;parse(e,t,r){switch(t){case"Q":case"QQ":return eJ(t.length,e);case"Qo":return r.ordinalNumber(e,{unit:"quarter"});case"QQQ":return r.quarter(e,{width:"abbreviated",context:"formatting"})||r.quarter(e,{width:"narrow",context:"formatting"});case"QQQQQ":return r.quarter(e,{width:"narrow",context:"formatting"});default:return r.quarter(e,{width:"wide",context:"formatting"})||r.quarter(e,{width:"abbreviated",context:"formatting"})||r.quarter(e,{width:"narrow",context:"formatting"})}}validate(e,t){return t>=1&&t<=4}set(e,t,r){return e.setMonth((r-1)*3,1),e.setHours(0,0,0,0),e}incompatibleTokens=["Y","R","q","M","L","w","I","d","D","i","e","c","t","T"]},new class extends eN{priority=120;parse(e,t,r){switch(t){case"q":case"qq":return eJ(t.length,e);case"qo":return r.ordinalNumber(e,{unit:"quarter"});case"qqq":return r.quarter(e,{width:"abbreviated",context:"standalone"})||r.quarter(e,{width:"narrow",context:"standalone"});case"qqqqq":return r.quarter(e,{width:"narrow",context:"standalone"});default:return r.quarter(e,{width:"wide",context:"standalone"})||r.quarter(e,{width:"abbreviated",context:"standalone"})||r.quarter(e,{width:"narrow",context:"standalone"})}}validate(e,t){return t>=1&&t<=4}set(e,t,r){return e.setMonth((r-1)*3,1),e.setHours(0,0,0,0),e}incompatibleTokens=["Y","R","Q","M","L","w","I","d","D","i","e","c","t","T"]},new class extends eN{incompatibleTokens=["Y","R","q","Q","L","w","I","D","i","e","c","t","T"];priority=110;parse(e,t,r){let n=e=>e-1;switch(t){case"M":return eX(eZ(eE,e),n);case"MM":return eX(eJ(2,e),n);case"Mo":return eX(r.ordinalNumber(e,{unit:"month"}),n);case"MMM":return r.month(e,{width:"abbreviated",context:"formatting"})||r.month(e,{width:"narrow",context:"formatting"});case"MMMMM":return r.month(e,{width:"narrow",context:"formatting"});default:return r.month(e,{width:"wide",context:"formatting"})||r.month(e,{width:"abbreviated",context:"formatting"})||r.month(e,{width:"narrow",context:"formatting"})}}validate(e,t){return t>=0&&t<=11}set(e,t,r){return e.setMonth(r,1),e.setHours(0,0,0,0),e}},new class extends eN{priority=110;parse(e,t,r){let n=e=>e-1;switch(t){case"L":return eX(eZ(eE,e),n);case"LL":return eX(eJ(2,e),n);case"Lo":return eX(r.ordinalNumber(e,{unit:"month"}),n);case"LLL":return r.month(e,{width:"abbreviated",context:"standalone"})||r.month(e,{width:"narrow",context:"standalone"});case"LLLLL":return r.month(e,{width:"narrow",context:"standalone"});default:return r.month(e,{width:"wide",context:"standalone"})||r.month(e,{width:"abbreviated",context:"standalone"})||r.month(e,{width:"narrow",context:"standalone"})}}validate(e,t){return t>=0&&t<=11}set(e,t,r){return e.setMonth(r,1),e.setHours(0,0,0,0),e}incompatibleTokens=["Y","R","q","Q","M","w","I","D","i","e","c","t","T"]},new class extends eN{priority=100;parse(e,t,r){switch(t){case"w":return eZ(eT,e);case"wo":return r.ordinalNumber(e,{unit:"week"});default:return eJ(t.length,e)}}validate(e,t){return t>=1&&t<=53}set(e,t,r,n){let a,o;return W((o=A(a=(0,m.toDate)(e,n?.in),n)-r,a.setDate(a.getDate()-7*o),(0,m.toDate)(a,n?.in)),n)}incompatibleTokens=["y","R","u","q","Q","M","L","I","d","D","i","t","T"]},new class extends eN{priority=100;parse(e,t,r){switch(t){case"I":return eZ(eT,e);case"Io":return r.ordinalNumber(e,{unit:"week"});default:return eJ(t.length,e)}}validate(e,t){return t>=1&&t<=53}set(e,t,r){let n,a;return H((a=B(n=(0,m.toDate)(e,void 0),void 0)-r,n.setDate(n.getDate()-7*a),n))}incompatibleTokens=["y","Y","u","q","Q","M","L","w","d","D","e","c","t","T"]},new class extends eN{priority=90;subPriority=1;parse(e,t,r){switch(t){case"d":return eZ(eS,e);case"do":return r.ordinalNumber(e,{unit:"date"});default:return eJ(t.length,e)}}validate(e,t){let r=e4(e.getFullYear()),n=e.getMonth();return r?t>=1&&t<=e5[n]:t>=1&&t<=e3[n]}set(e,t,r){return e.setDate(r),e.setHours(0,0,0,0),e}incompatibleTokens=["Y","R","q","Q","w","I","D","i","e","c","t","T"]},new class extends eN{priority=90;subpriority=1;parse(e,t,r){switch(t){case"D":case"DD":return eZ(eP,e);case"Do":return r.ordinalNumber(e,{unit:"date"});default:return eJ(t.length,e)}}validate(e,t){return e4(e.getFullYear())?t>=1&&t<=366:t>=1&&t<=365}set(e,t,r){return e.setMonth(0,r),e.setHours(0,0,0,0),e}incompatibleTokens=["Y","R","q","Q","M","L","w","I","d","E","i","e","c","t","T"]},new class extends eN{priority=90;parse(e,t,r){switch(t){case"E":case"EE":case"EEE":return r.day(e,{width:"abbreviated",context:"formatting"})||r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"});case"EEEEE":return r.day(e,{width:"narrow",context:"formatting"});case"EEEEEE":return r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"});default:return r.day(e,{width:"wide",context:"formatting"})||r.day(e,{width:"abbreviated",context:"formatting"})||r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"})}}validate(e,t){return t>=0&&t<=6}set(e,t,r,n){return(e=e7(e,r,n)).setHours(0,0,0,0),e}incompatibleTokens=["D","i","e","c","t","T"]},new class extends eN{priority=90;parse(e,t,r,n){let a=e=>{let t=7*Math.floor((e-1)/7);return(e+n.weekStartsOn+6)%7+t};switch(t){case"e":case"ee":return eX(eJ(t.length,e),a);case"eo":return eX(r.ordinalNumber(e,{unit:"day"}),a);case"eee":return r.day(e,{width:"abbreviated",context:"formatting"})||r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"});case"eeeee":return r.day(e,{width:"narrow",context:"formatting"});case"eeeeee":return r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"});default:return r.day(e,{width:"wide",context:"formatting"})||r.day(e,{width:"abbreviated",context:"formatting"})||r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"})}}validate(e,t){return t>=0&&t<=6}set(e,t,r,n){return(e=e7(e,r,n)).setHours(0,0,0,0),e}incompatibleTokens=["y","R","u","q","Q","M","L","I","d","D","E","i","c","t","T"]},new class extends eN{priority=90;parse(e,t,r,n){let a=e=>{let t=7*Math.floor((e-1)/7);return(e+n.weekStartsOn+6)%7+t};switch(t){case"c":case"cc":return eX(eJ(t.length,e),a);case"co":return eX(r.ordinalNumber(e,{unit:"day"}),a);case"ccc":return r.day(e,{width:"abbreviated",context:"standalone"})||r.day(e,{width:"short",context:"standalone"})||r.day(e,{width:"narrow",context:"standalone"});case"ccccc":return r.day(e,{width:"narrow",context:"standalone"});case"cccccc":return r.day(e,{width:"short",context:"standalone"})||r.day(e,{width:"narrow",context:"standalone"});default:return r.day(e,{width:"wide",context:"standalone"})||r.day(e,{width:"abbreviated",context:"standalone"})||r.day(e,{width:"short",context:"standalone"})||r.day(e,{width:"narrow",context:"standalone"})}}validate(e,t){return t>=0&&t<=6}set(e,t,r,n){return(e=e7(e,r,n)).setHours(0,0,0,0),e}incompatibleTokens=["y","R","u","q","Q","M","L","I","d","D","E","i","e","t","T"]},new class extends eN{priority=90;parse(e,t,r){let n=e=>0===e?7:e;switch(t){case"i":case"ii":return eJ(t.length,e);case"io":return r.ordinalNumber(e,{unit:"day"});case"iii":return eX(r.day(e,{width:"abbreviated",context:"formatting"})||r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"}),n);case"iiiii":return eX(r.day(e,{width:"narrow",context:"formatting"}),n);case"iiiiii":return eX(r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"}),n);default:return eX(r.day(e,{width:"wide",context:"formatting"})||r.day(e,{width:"abbreviated",context:"formatting"})||r.day(e,{width:"short",context:"formatting"})||r.day(e,{width:"narrow",context:"formatting"}),n)}}validate(e,t){return t>=1&&t<=7}set(e,t,r){var n,a;let o,l,i;return n=e,o=(0,m.toDate)(n,void 0),a=void 0,i=0===(l=(0,m.toDate)(o,a?.in).getDay())?7:l,(e=(0,w.addDays)(o,r-i,void 0)).setHours(0,0,0,0),e}incompatibleTokens=["y","Y","u","q","Q","M","L","w","d","D","E","e","c","t","T"]},new class extends eN{priority=80;parse(e,t,r){switch(t){case"a":case"aa":case"aaa":return r.dayPeriod(e,{width:"abbreviated",context:"formatting"})||r.dayPeriod(e,{width:"narrow",context:"formatting"});case"aaaaa":return r.dayPeriod(e,{width:"narrow",context:"formatting"});default:return r.dayPeriod(e,{width:"wide",context:"formatting"})||r.dayPeriod(e,{width:"abbreviated",context:"formatting"})||r.dayPeriod(e,{width:"narrow",context:"formatting"})}}set(e,t,r){return e.setHours(e1(r),0,0,0),e}incompatibleTokens=["b","B","H","k","t","T"]},new class extends eN{priority=80;parse(e,t,r){switch(t){case"b":case"bb":case"bbb":return r.dayPeriod(e,{width:"abbreviated",context:"formatting"})||r.dayPeriod(e,{width:"narrow",context:"formatting"});case"bbbbb":return r.dayPeriod(e,{width:"narrow",context:"formatting"});default:return r.dayPeriod(e,{width:"wide",context:"formatting"})||r.dayPeriod(e,{width:"abbreviated",context:"formatting"})||r.dayPeriod(e,{width:"narrow",context:"formatting"})}}set(e,t,r){return e.setHours(e1(r),0,0,0),e}incompatibleTokens=["a","B","H","k","t","T"]},new class extends eN{priority=80;parse(e,t,r){switch(t){case"B":case"BB":case"BBB":return r.dayPeriod(e,{width:"abbreviated",context:"formatting"})||r.dayPeriod(e,{width:"narrow",context:"formatting"});case"BBBBB":return r.dayPeriod(e,{width:"narrow",context:"formatting"});default:return r.dayPeriod(e,{width:"wide",context:"formatting"})||r.dayPeriod(e,{width:"abbreviated",context:"formatting"})||r.dayPeriod(e,{width:"narrow",context:"formatting"})}}set(e,t,r){return e.setHours(e1(r),0,0,0),e}incompatibleTokens=["a","b","t","T"]},new class extends eN{priority=70;parse(e,t,r){switch(t){case"h":return eZ(eF,e);case"ho":return r.ordinalNumber(e,{unit:"hour"});default:return eJ(t.length,e)}}validate(e,t){return t>=1&&t<=12}set(e,t,r){let n=e.getHours()>=12;return n&&r<12?e.setHours(r+12,0,0,0):n||12!==r?e.setHours(r,0,0,0):e.setHours(0,0,0,0),e}incompatibleTokens=["H","K","k","t","T"]},new class extends eN{priority=70;parse(e,t,r){switch(t){case"H":return eZ(eC,e);case"Ho":return r.ordinalNumber(e,{unit:"hour"});default:return eJ(t.length,e)}}validate(e,t){return t>=0&&t<=23}set(e,t,r){return e.setHours(r,0,0,0),e}incompatibleTokens=["a","b","h","K","k","t","T"]},new class extends eN{priority=70;parse(e,t,r){switch(t){case"K":return eZ(ej,e);case"Ko":return r.ordinalNumber(e,{unit:"hour"});default:return eJ(t.length,e)}}validate(e,t){return t>=0&&t<=11}set(e,t,r){return e.getHours()>=12&&r<12?e.setHours(r+12,0,0,0):e.setHours(r,0,0,0),e}incompatibleTokens=["h","H","k","t","T"]},new class extends eN{priority=70;parse(e,t,r){switch(t){case"k":return eZ(e_,e);case"ko":return r.ordinalNumber(e,{unit:"hour"});default:return eJ(t.length,e)}}validate(e,t){return t>=1&&t<=24}set(e,t,r){return e.setHours(r<=24?r%24:r,0,0,0),e}incompatibleTokens=["a","b","h","H","K","t","T"]},new class extends eN{priority=60;parse(e,t,r){switch(t){case"m":return eZ(eL,e);case"mo":return r.ordinalNumber(e,{unit:"minute"});default:return eJ(t.length,e)}}validate(e,t){return t>=0&&t<=59}set(e,t,r){return e.setMinutes(r,0,0),e}incompatibleTokens=["t","T"]},new class extends eN{priority=50;parse(e,t,r){switch(t){case"s":return eZ(eO,e);case"so":return r.ordinalNumber(e,{unit:"second"});default:return eJ(t.length,e)}}validate(e,t){return t>=0&&t<=59}set(e,t,r){return e.setSeconds(r,0),e}incompatibleTokens=["t","T"]},new class extends eN{priority=30;parse(e,t){return eX(eJ(t.length,e),e=>Math.trunc(e*Math.pow(10,-t.length+3)))}set(e,t,r){return e.setMilliseconds(r),e}incompatibleTokens=["t","T"]},new class extends eN{priority=10;parse(e,t){switch(t){case"X":return eU(eG,e);case"XX":return eU(ez,e);case"XXXX":return eU(eV,e);case"XXXXX":return eU(eK,e);default:return eU(e$,e)}}set(e,t,r){return t.timestampIsSet?e:(0,g.constructFrom)(e,e.getTime()-L(e)-r)}incompatibleTokens=["t","T","x"]},new class extends eN{priority=10;parse(e,t){switch(t){case"x":return eU(eG,e);case"xx":return eU(ez,e);case"xxxx":return eU(eV,e);case"xxxxx":return eU(eK,e);default:return eU(e$,e)}}set(e,t,r){return t.timestampIsSet?e:(0,g.constructFrom)(e,e.getTime()-L(e)-r)}incompatibleTokens=["t","T","X"]},new class extends eN{priority=40;parse(e){return eZ(eR,e)}set(e,t,r){return[(0,g.constructFrom)(e,1e3*r),{timestampIsSet:!0}]}incompatibleTokens="*"},new class extends eN{priority=20;parse(e){return eZ(eR,e)}set(e,t,r){return[(0,g.constructFrom)(e,r),{timestampIsSet:!0}]}incompatibleTokens="*"};var e6=function(){return(e6=Object.assign||function(e){for(var t,r=1,n=arguments.length;reh(u,l)&&(l=(0,x.addMonths)(u,-1*((void 0===c?1:c)-1))),d&&0>eh(l,d)&&(l=d),m=p(l),f=t.month,b=(h=(0,s.useState)(m))[0],v=[void 0===f?b:f,h[1]])[0],w=v[1],[g,function(e){if(!t.disableNavigation){var r,n=p(e);w(n),null==(r=t.onMonthChange)||r.call(t,n)}}]),M=k[0],D=k[1],N=function(e,t){for(var r=t.reverseMonths,n=t.numberOfMonths,a=p(e),o=eh(p((0,x.addMonths)(a,n)),a),l=[],i=0;i=eh(o,r)))return(0,x.addMonths)(o,-(n?void 0===a?1:a:1))}}(M,y),P=function(e){return N.some(function(t){return ep(e,t)})};return(0,i.jsx)(tf.Provider,{value:{currentMonth:M,displayMonths:N,goToMonth:D,goToDate:function(e,t){P(e)||(t&&eb(e,t)?D((0,x.addMonths)(e,1+-1*y.numberOfMonths)):D(e))},previousMonth:S,nextMonth:E,isDateDisplayed:P},children:e.children})}function tp(){var e=(0,s.useContext)(tf);if(!e)throw Error("useNavigation must be used within a NavigationProvider");return e}function tb(e){var t,r=ti(),n=r.classNames,a=r.styles,o=r.components,l=tp().goToMonth,s=function(t){l((0,x.addMonths)(t,e.displayIndex?-e.displayIndex:0))},u=null!=(t=null==o?void 0:o.CaptionLabel)?t:ts,d=(0,i.jsx)(u,{id:e.id,displayMonth:e.displayMonth});return(0,i.jsxs)("div",{className:n.caption_dropdowns,style:a.caption_dropdowns,children:[(0,i.jsx)("div",{className:n.vhidden,children:d}),(0,i.jsx)(tc,{onChange:s,displayMonth:e.displayMonth}),(0,i.jsx)(tm,{onChange:s,displayMonth:e.displayMonth})]})}function tv(e){return(0,i.jsx)("svg",e6({width:"16px",height:"16px",viewBox:"0 0 120 120"},e,{children:(0,i.jsx)("path",{d:"M69.490332,3.34314575 C72.6145263,0.218951416 77.6798462,0.218951416 80.8040405,3.34314575 C83.8617626,6.40086786 83.9268205,11.3179931 80.9992143,14.4548388 L80.8040405,14.6568542 L35.461,60 L80.8040405,105.343146 C83.8617626,108.400868 83.9268205,113.317993 80.9992143,116.454839 L80.8040405,116.656854 C77.7463184,119.714576 72.8291931,119.779634 69.6923475,116.852028 L69.490332,116.656854 L18.490332,65.6568542 C15.4326099,62.5991321 15.367552,57.6820069 18.2951583,54.5451612 L18.490332,54.3431458 L69.490332,3.34314575 Z",fill:"currentColor",fillRule:"nonzero"})}))}function tg(e){return(0,i.jsx)("svg",e6({width:"16px",height:"16px",viewBox:"0 0 120 120"},e,{children:(0,i.jsx)("path",{d:"M49.8040405,3.34314575 C46.6798462,0.218951416 41.6145263,0.218951416 38.490332,3.34314575 C35.4326099,6.40086786 35.367552,11.3179931 38.2951583,14.4548388 L38.490332,14.6568542 L83.8333725,60 L38.490332,105.343146 C35.4326099,108.400868 35.367552,113.317993 38.2951583,116.454839 L38.490332,116.656854 C41.5480541,119.714576 46.4651794,119.779634 49.602025,116.852028 L49.8040405,116.656854 L100.804041,65.6568542 C103.861763,62.5991321 103.926821,57.6820069 100.999214,54.5451612 L100.804041,54.3431458 L49.8040405,3.34314575 Z",fill:"currentColor"})}))}var tw=(0,s.forwardRef)(function(e,t){var r=ti(),n=r.classNames,a=r.styles,o=[n.button_reset,n.button];e.className&&o.push(e.className);var l=o.join(" "),s=e6(e6({},a.button_reset),a.button);return e.style&&Object.assign(s,e.style),(0,i.jsx)("button",e6({},e,{ref:t,type:"button",className:l,style:s}))});function ty(e){var t,r,n=ti(),a=n.dir,o=n.locale,l=n.classNames,s=n.styles,u=n.labels,d=u.labelPrevious,c=u.labelNext,m=n.components;if(!e.nextMonth&&!e.previousMonth)return(0,i.jsx)(i.Fragment,{});var f=d(e.previousMonth,{locale:o}),h=[l.nav_button,l.nav_button_previous].join(" "),p=c(e.nextMonth,{locale:o}),b=[l.nav_button,l.nav_button_next].join(" "),v=null!=(t=null==m?void 0:m.IconRight)?t:tg,g=null!=(r=null==m?void 0:m.IconLeft)?r:tv;return(0,i.jsxs)("div",{className:l.nav,style:s.nav,children:[!e.hidePrevious&&(0,i.jsx)(tw,{name:"previous-month","aria-label":f,className:h,style:s.nav_button_previous,disabled:!e.previousMonth,onClick:e.onPreviousClick,children:"rtl"===a?(0,i.jsx)(v,{className:l.nav_icon,style:s.nav_icon}):(0,i.jsx)(g,{className:l.nav_icon,style:s.nav_icon})}),!e.hideNext&&(0,i.jsx)(tw,{name:"next-month","aria-label":p,className:b,style:s.nav_button_next,disabled:!e.nextMonth,onClick:e.onNextClick,children:"rtl"===a?(0,i.jsx)(g,{className:l.nav_icon,style:s.nav_icon}):(0,i.jsx)(v,{className:l.nav_icon,style:s.nav_icon})})]})}function tx(e){var t=ti().numberOfMonths,r=tp(),n=r.previousMonth,a=r.nextMonth,o=r.goToMonth,l=r.displayMonths,s=l.findIndex(function(t){return ep(e.displayMonth,t)}),u=0===s,d=s===l.length-1;return(0,i.jsx)(ty,{displayMonth:e.displayMonth,hideNext:t>1&&(u||!d),hidePrevious:t>1&&(d||!u),nextMonth:a,previousMonth:n,onPreviousClick:function(){n&&o(n)},onNextClick:function(){a&&o(a)}})}function tk(e){var t,r,n=ti(),a=n.classNames,o=n.disableNavigation,l=n.styles,s=n.captionLayout,u=n.components,d=null!=(t=null==u?void 0:u.CaptionLabel)?t:ts;return r=o?(0,i.jsx)(d,{id:e.id,displayMonth:e.displayMonth}):"dropdown"===s?(0,i.jsx)(tb,{displayMonth:e.displayMonth,id:e.id}):"dropdown-buttons"===s?(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(tb,{displayMonth:e.displayMonth,displayIndex:e.displayIndex,id:e.id}),(0,i.jsx)(tx,{displayMonth:e.displayMonth,displayIndex:e.displayIndex,id:e.id})]}):(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(d,{id:e.id,displayMonth:e.displayMonth,displayIndex:e.displayIndex}),(0,i.jsx)(tx,{displayMonth:e.displayMonth,id:e.id})]}),(0,i.jsx)("div",{className:a.caption,style:l.caption,children:r})}function tM(e){var t=ti(),r=t.footer,n=t.styles,a=t.classNames.tfoot;return r?(0,i.jsx)("tfoot",{className:a,style:n.tfoot,children:(0,i.jsx)("tr",{children:(0,i.jsx)("td",{colSpan:8,children:r})})}):(0,i.jsx)(i.Fragment,{})}function tD(){var e=ti(),t=e.classNames,r=e.styles,n=e.showWeekNumber,a=e.locale,o=e.weekStartsOn,l=e.ISOWeek,s=e.formatters.formatWeekdayName,u=e.labels.labelWeekday,d=function(e,t,r){for(var n=r?H(new Date):W(new Date,{locale:e,weekStartsOn:t}),a=[],o=0;o<7;o++){var l=(0,w.addDays)(n,o);a.push(l)}return a}(a,o,l);return(0,i.jsxs)("tr",{style:r.head_row,className:t.head_row,children:[n&&(0,i.jsx)("td",{style:r.head_cell,className:t.head_cell}),d.map(function(e,n){return(0,i.jsx)("th",{scope:"col",className:t.head_cell,style:r.head_cell,"aria-label":u(e,{locale:a}),children:s(e,{locale:a})},n)})]})}function tN(){var e,t=ti(),r=t.classNames,n=t.styles,a=t.components,o=null!=(e=null==a?void 0:a.HeadRow)?e:tD;return(0,i.jsx)("thead",{style:n.head,className:r.head,children:(0,i.jsx)(o,{})})}function tE(e){var t=ti(),r=t.locale,n=t.formatters.formatDay;return(0,i.jsx)(i.Fragment,{children:n(e.date,{locale:r})})}var tS=(0,s.createContext)(void 0);function tP(e){return e9(e.initialProps)?(0,i.jsx)(tT,{initialProps:e.initialProps,children:e.children}):(0,i.jsx)(tS.Provider,{value:{selected:void 0,modifiers:{disabled:[]}},children:e.children})}function tT(e){var t=e.initialProps,r=e.children,n=t.selected,a=t.min,o=t.max,l={disabled:[]};return n&&l.disabled.push(function(e){var t=o&&n.length>o-1,r=n.some(function(t){return ev(t,e)});return!!(t&&!r)}),(0,i.jsx)(tS.Provider,{value:{selected:n,onDayClick:function(e,r,l){var i,s;if((null==(i=t.onDayClick)||i.call(t,e,r,l),!r.selected||!a||(null==n?void 0:n.length)!==a)&&!(!r.selected&&o&&(null==n?void 0:n.length)===o)){var u=n?e8([],n,!0):[];if(r.selected){var d=u.findIndex(function(t){return ev(e,t)});u.splice(d,1)}else u.push(e);null==(s=t.onSelect)||s.call(t,u,e,r,l)}},modifiers:l},children:r})}function tC(){var e=(0,s.useContext)(tS);if(!e)throw Error("useSelectMultiple must be used within a SelectMultipleProvider");return e}var t_=(0,s.createContext)(void 0);function tj(e){return te(e.initialProps)?(0,i.jsx)(tF,{initialProps:e.initialProps,children:e.children}):(0,i.jsx)(t_.Provider,{value:{selected:void 0,modifiers:{range_start:[],range_end:[],range_middle:[],disabled:[]}},children:e.children})}function tF(e){var t=e.initialProps,r=e.children,n=t.selected,a=n||{},o=a.from,l=a.to,s=t.min,u=t.max,d={range_start:[],range_end:[],range_middle:[],disabled:[]};if(o?(d.range_start=[o],l?(d.range_end=[l],ev(o,l)||(d.range_middle=[{after:o,before:l}])):d.range_end=[o]):l&&(d.range_start=[l],d.range_end=[l]),s&&(o&&!l&&d.disabled.push({after:y(o,s-1),before:(0,w.addDays)(o,s-1)}),o&&l&&d.disabled.push({after:o,before:(0,w.addDays)(o,s-1)}),!o&&l&&d.disabled.push({after:y(l,s-1),before:(0,w.addDays)(l,s-1)})),u){if(o&&!l&&(d.disabled.push({before:(0,w.addDays)(o,-u+1)}),d.disabled.push({after:(0,w.addDays)(o,u-1)})),o&&l){var c=u-(Y(l,o)+1);d.disabled.push({before:y(o,c)}),d.disabled.push({after:(0,w.addDays)(l,c)})}!o&&l&&(d.disabled.push({before:(0,w.addDays)(l,-u+1)}),d.disabled.push({after:(0,w.addDays)(l,u-1)}))}return(0,i.jsx)(t_.Provider,{value:{selected:n,onDayClick:function(e,r,a){null==(u=t.onDayClick)||u.call(t,e,r,a);var o,l,i,s,u,d,c=(o=e,i=(l=n||{}).from,s=l.to,i&&s?ev(s,o)&&ev(i,o)?void 0:ev(s,o)?{from:s,to:void 0}:ev(i,o)?void 0:eg(i,o)?{from:o,to:s}:{from:i,to:o}:s?eg(o,s)?{from:s,to:o}:{from:o,to:s}:i?eb(o,i)?{from:o,to:i}:{from:i,to:o}:{from:o,to:void 0});null==(d=t.onSelect)||d.call(t,c,e,r,a)},modifiers:d},children:r})}function tL(){var e=(0,s.useContext)(t_);if(!e)throw Error("useSelectRange must be used within a SelectRangeProvider");return e}function tO(e){return Array.isArray(e)?e8([],e,!0):void 0!==e?[e]:[]}(o=l||(l={})).Outside="outside",o.Disabled="disabled",o.Selected="selected",o.Hidden="hidden",o.Today="today",o.RangeStart="range_start",o.RangeEnd="range_end",o.RangeMiddle="range_middle";var tI=l.Selected,tY=l.Disabled,tW=l.Hidden,tH=l.Today,tR=l.RangeEnd,tB=l.RangeMiddle,tq=l.RangeStart,tA=l.Outside,tQ=(0,s.createContext)(void 0);function tG(e){var t,r,n,a,o=ti(),l=tC(),s=tL(),u=((t={})[tI]=tO(o.selected),t[tY]=tO(o.disabled),t[tW]=tO(o.hidden),t[tH]=[o.today],t[tR]=[],t[tB]=[],t[tq]=[],t[tA]=[],r=t,o.fromDate&&r[tY].push({before:o.fromDate}),o.toDate&&r[tY].push({after:o.toDate}),e9(o)?r[tY]=r[tY].concat(l.modifiers[tY]):te(o)&&(r[tY]=r[tY].concat(s.modifiers[tY]),r[tq]=s.modifiers[tq],r[tB]=s.modifiers[tB],r[tR]=s.modifiers[tR]),r),d=(n=o.modifiers,a={},Object.entries(n).forEach(function(e){var t=e[0],r=e[1];a[t]=tO(r)}),a),c=e6(e6({},u),d);return(0,i.jsx)(tQ.Provider,{value:c,children:e.children})}function tz(){var e=(0,s.useContext)(tQ);if(!e)throw Error("useModifiers must be used within a ModifiersProvider");return e}function tV(e,t,r){var n=Object.keys(t).reduce(function(r,n){return t[n].some(function(t){if("boolean"==typeof t)return t;if(er(t))return ev(e,t);if(Array.isArray(t)&&t.every(er))return t.includes(e);if(t&&"object"==typeof t&&"from"in t)return n=t.from,a=t.to,n&&a?(0>Y(a,n)&&(n=(r=[a,n])[0],a=r[1]),Y(e,n)>=0&&Y(a,e)>=0):a?ev(a,e):!!n&&ev(n,e);if(t&&"object"==typeof t&&"dayOfWeek"in t)return t.dayOfWeek.includes(e.getDay());if(t&&"object"==typeof t&&"before"in t&&"after"in t){var r,n,a,o=Y(t.before,e),l=Y(t.after,e),i=o>0,s=l<0;return eg(t.before,t.after)?s&&i:i||s}return t&&"object"==typeof t&&"after"in t?Y(e,t.after)>0:t&&"object"==typeof t&&"before"in t?Y(t.before,e)>0:"function"==typeof t&&t(e)})&&r.push(n),r},[]),a={};return n.forEach(function(e){return a[e]=!0}),r&&!ep(e,r)&&(a.outside=!0),a}var t$=(0,s.createContext)(void 0);function tK(e){var t=tp(),r=tz(),n=(0,s.useState)(),a=n[0],o=n[1],l=(0,s.useState)(),u=l[0],d=l[1],c=function(e,t){for(var r,n,a=p(e[0]),o=ec(e[e.length-1]),l=a;l<=o;){var i=tV(l,t);if(!(!i.disabled&&!i.hidden)){l=(0,w.addDays)(l,1);continue}if(i.selected)return l;i.today&&!n&&(n=l),r||(r=l),l=(0,w.addDays)(l,1)}return n||r}(t.displayMonths,r),m=(null!=a?a:u&&t.isDateDisplayed(u))?u:c,f=function(e){o(e)},h=ti(),b=function(e,n){if(a){var o=function e(t,r){var n=r.moveBy,a=r.direction,o=r.context,l=r.modifiers,i=r.retry,s=void 0===i?{count:0,lastFocused:t}:i,u=o.weekStartsOn,d=o.fromDate,c=o.toDate,m=o.locale,f=({day:w.addDays,week:ew,month:x.addMonths,year:ey,startOfWeek:function(e){return o.ISOWeek?H(e):W(e,{locale:m,weekStartsOn:u})},endOfWeek:function(e){return o.ISOWeek?ek(e):ex(e,{locale:m,weekStartsOn:u})}})[n](t,"after"===a?1:-1);"before"===a&&d?f=D([d,f]):"after"===a&&c&&(f=N([c,f]));var h=!0;if(l){var p=tV(f,l);h=!p.disabled&&!p.hidden}return h?f:s.count>365?s.lastFocused:e(f,{moveBy:n,direction:a,context:o,modifiers:l,retry:e6(e6({},s),{count:s.count+1})})}(a,{moveBy:e,direction:n,context:h,modifiers:r});ev(a,o)||(t.goToDate(o,a),f(o))}};return(0,i.jsx)(t$.Provider,{value:{focusedDay:a,focusTarget:m,blur:function(){d(a),o(void 0)},focus:f,focusDayAfter:function(){return b("day","after")},focusDayBefore:function(){return b("day","before")},focusWeekAfter:function(){return b("week","after")},focusWeekBefore:function(){return b("week","before")},focusMonthBefore:function(){return b("month","before")},focusMonthAfter:function(){return b("month","after")},focusYearBefore:function(){return b("year","before")},focusYearAfter:function(){return b("year","after")},focusStartOfWeek:function(){return b("startOfWeek","before")},focusEndOfWeek:function(){return b("endOfWeek","after")}},children:e.children})}function tX(){var e=(0,s.useContext)(t$);if(!e)throw Error("useFocusContext must be used within a FocusProvider");return e}var tZ=(0,s.createContext)(void 0);function tU(e){return tt(e.initialProps)?(0,i.jsx)(tJ,{initialProps:e.initialProps,children:e.children}):(0,i.jsx)(tZ.Provider,{value:{selected:void 0},children:e.children})}function tJ(e){var t=e.initialProps,r=e.children,n={selected:t.selected,onDayClick:function(e,r,n){var a,o,l;if(null==(a=t.onDayClick)||a.call(t,e,r,n),r.selected&&!t.required){null==(o=t.onSelect)||o.call(t,void 0,e,r,n);return}null==(l=t.onSelect)||l.call(t,e,e,r,n)}};return(0,i.jsx)(tZ.Provider,{value:n,children:r})}function t0(){var e=(0,s.useContext)(tZ);if(!e)throw Error("useSelectSingle must be used within a SelectSingleProvider");return e}function t1(e){var t,r,n,a,o,u,d,c,m,f,h,p,b,v,g,w,y,x,k,M,D,N,E,S,P,T,C,_,j,F,L,O,I,Y,W,H,R,B,q,A,Q,G,z=(0,s.useRef)(null),V=(t=e.date,r=e.displayMonth,u=ti(),d=tX(),c=tV(t,tz(),r),m=ti(),f=t0(),h=tC(),p=tL(),v=(b=tX()).focusDayAfter,g=b.focusDayBefore,w=b.focusWeekAfter,y=b.focusWeekBefore,x=b.blur,k=b.focus,M=b.focusMonthBefore,D=b.focusMonthAfter,N=b.focusYearBefore,E=b.focusYearAfter,S=b.focusStartOfWeek,P=b.focusEndOfWeek,T={onClick:function(e){var r,n,a,o;tt(m)?null==(r=f.onDayClick)||r.call(f,t,c,e):e9(m)?null==(n=h.onDayClick)||n.call(h,t,c,e):te(m)?null==(a=p.onDayClick)||a.call(p,t,c,e):null==(o=m.onDayClick)||o.call(m,t,c,e)},onFocus:function(e){var r;k(t),null==(r=m.onDayFocus)||r.call(m,t,c,e)},onBlur:function(e){var r;x(),null==(r=m.onDayBlur)||r.call(m,t,c,e)},onKeyDown:function(e){var r;switch(e.key){case"ArrowLeft":e.preventDefault(),e.stopPropagation(),"rtl"===m.dir?v():g();break;case"ArrowRight":e.preventDefault(),e.stopPropagation(),"rtl"===m.dir?g():v();break;case"ArrowDown":e.preventDefault(),e.stopPropagation(),w();break;case"ArrowUp":e.preventDefault(),e.stopPropagation(),y();break;case"PageUp":e.preventDefault(),e.stopPropagation(),e.shiftKey?N():M();break;case"PageDown":e.preventDefault(),e.stopPropagation(),e.shiftKey?E():D();break;case"Home":e.preventDefault(),e.stopPropagation(),S();break;case"End":e.preventDefault(),e.stopPropagation(),P()}null==(r=m.onDayKeyDown)||r.call(m,t,c,e)},onKeyUp:function(e){var r;null==(r=m.onDayKeyUp)||r.call(m,t,c,e)},onMouseEnter:function(e){var r;null==(r=m.onDayMouseEnter)||r.call(m,t,c,e)},onMouseLeave:function(e){var r;null==(r=m.onDayMouseLeave)||r.call(m,t,c,e)},onPointerEnter:function(e){var r;null==(r=m.onDayPointerEnter)||r.call(m,t,c,e)},onPointerLeave:function(e){var r;null==(r=m.onDayPointerLeave)||r.call(m,t,c,e)},onTouchCancel:function(e){var r;null==(r=m.onDayTouchCancel)||r.call(m,t,c,e)},onTouchEnd:function(e){var r;null==(r=m.onDayTouchEnd)||r.call(m,t,c,e)},onTouchMove:function(e){var r;null==(r=m.onDayTouchMove)||r.call(m,t,c,e)},onTouchStart:function(e){var r;null==(r=m.onDayTouchStart)||r.call(m,t,c,e)}},C=ti(),_=t0(),j=tC(),F=tL(),L=tt(C)?_.selected:e9(C)?j.selected:te(C)?F.selected:void 0,O=!!(u.onDayClick||"default"!==u.mode),(0,s.useEffect)(function(){var e;c.outside||!d.focusedDay||O&&ev(d.focusedDay,t)&&(null==(e=z.current)||e.focus())},[d.focusedDay,t,z,O,c.outside]),Y=(I=[u.classNames.day],Object.keys(c).forEach(function(e){var t=u.modifiersClassNames[e];if(t)I.push(t);else if(Object.values(l).includes(e)){var r=u.classNames["day_".concat(e)];r&&I.push(r)}}),I).join(" "),W=e6({},u.styles.day),Object.keys(c).forEach(function(e){var t;W=e6(e6({},W),null==(t=u.modifiersStyles)?void 0:t[e])}),H=W,R=!!(c.outside&&!u.showOutsideDays||c.hidden),B=null!=(o=null==(a=u.components)?void 0:a.DayContent)?o:tE,q={style:H,className:Y,children:(0,i.jsx)(B,{date:t,displayMonth:r,activeModifiers:c}),role:"gridcell"},A=d.focusTarget&&ev(d.focusTarget,t)&&!c.outside,Q=d.focusedDay&&ev(d.focusedDay,t),G=e6(e6(e6({},q),((n={disabled:c.disabled,role:"gridcell"})["aria-selected"]=c.selected,n.tabIndex=Q||A?0:-1,n)),T),{isButton:O,isHidden:R,activeModifiers:c,selectedDays:L,buttonProps:G,divProps:q});return V.isHidden?(0,i.jsx)("div",{role:"gridcell"}):V.isButton?(0,i.jsx)(tw,e6({name:"day",ref:z},V.buttonProps)):(0,i.jsx)("div",e6({},V.divProps))}function t2(e){var t=e.number,r=e.dates,n=ti(),a=n.onWeekNumberClick,o=n.styles,l=n.classNames,s=n.locale,u=n.labels.labelWeekNumber,d=(0,n.formatters.formatWeekNumber)(Number(t),{locale:s});if(!a)return(0,i.jsx)("span",{className:l.weeknumber,style:o.weeknumber,children:d});var c=u(Number(t),{locale:s});return(0,i.jsx)(tw,{name:"week-number","aria-label":c,className:l.weeknumber,style:o.weeknumber,onClick:function(e){a(t,r,e)},children:d})}function t4(e){var t,r,n,a=ti(),o=a.styles,l=a.classNames,s=a.showWeekNumber,u=a.components,d=null!=(t=null==u?void 0:u.Day)?t:t1,c=null!=(r=null==u?void 0:u.WeekNumber)?r:t2;return s&&(n=(0,i.jsx)("td",{className:l.cell,style:o.cell,children:(0,i.jsx)(c,{number:e.weekNumber,dates:e.dates})})),(0,i.jsxs)("tr",{className:l.row,style:o.row,children:[n,e.dates.map(function(t){return(0,i.jsx)("td",{className:l.cell,style:o.cell,role:"presentation",children:(0,i.jsx)(d,{displayMonth:e.displayMonth,date:t})},Math.trunc((0,m.toDate)(t)/1e3))})]})}function t3(e,t,r){for(var n=(null==r?void 0:r.ISOWeek)?ek(t):ex(t,r),a=(null==r?void 0:r.ISOWeek)?H(e):W(e,r),o=Y(n,a),l=[],i=0;i<=o;i++)l.push((0,w.addDays)(a,i));return l.reduce(function(e,t){var n=(null==r?void 0:r.ISOWeek)?B(t):A(t,r),a=e.find(function(e){return e.weekNumber===n});return a?a.dates.push(t):e.push({weekNumber:n,dates:[t]}),e},[])}function t5(e){var t,r,n,a=ti(),o=a.locale,l=a.classNames,s=a.styles,u=a.hideHead,d=a.fixedWeeks,c=a.components,f=a.weekStartsOn,h=a.firstWeekContainsDate,b=a.ISOWeek,v=function(e,t){var r=t3(p(e),ec(e),t);if(null==t?void 0:t.useFixedWeeks){let s,u,d;var n=function(e,t,r){let[n,a]=O(r?.in,e,t),o=W(n,r),l=W(a,r);return Math.round((o-L(o)-(l-L(l)))/I.millisecondsInWeek)}((s=(0,m.toDate)(e,t?.in),d=(u=(0,m.toDate)(s,t?.in)).getMonth(),u.setFullYear(u.getFullYear(),d+1,0),u.setHours(0,0,0,0),(0,m.toDate)(u,t?.in)),p(s,t),t)+1;if(n<6){var a=r[r.length-1],o=a.dates[a.dates.length-1],l=ew(o,6-n),i=t3(ew(o,1),l,t);r.push.apply(r,i)}}return r}(e.displayMonth,{useFixedWeeks:!!d,ISOWeek:b,locale:o,weekStartsOn:f,firstWeekContainsDate:h}),g=null!=(t=null==c?void 0:c.Head)?t:tN,w=null!=(r=null==c?void 0:c.Row)?r:t4,y=null!=(n=null==c?void 0:c.Footer)?n:tM;return(0,i.jsxs)("table",{id:e.id,className:l.table,style:s.table,role:"grid","aria-labelledby":e["aria-labelledby"],children:[!u&&(0,i.jsx)(g,{}),(0,i.jsx)("tbody",{className:l.tbody,style:s.tbody,children:v.map(function(t){return(0,i.jsx)(w,{displayMonth:e.displayMonth,dates:t.dates,weekNumber:t.weekNumber},t.weekNumber)})}),(0,i.jsx)(y,{displayMonth:e.displayMonth})]})}var t7="u">typeof window&&window.document&&window.document.createElement?s.useLayoutEffect:s.useEffect,t6=!1,t8=0;function t9(){return"react-day-picker-".concat(++t8)}function re(e){var t,r,n,a,o,l,u,d,c=ti(),m=c.dir,f=c.classNames,h=c.styles,p=c.components,b=tp().displayMonths,v=(n=null!=(t=c.id?"".concat(c.id,"-").concat(e.displayIndex):void 0)?t:t6?t9():null,o=(a=(0,s.useState)(n))[0],l=a[1],t7(function(){null===o&&l(t9())},[]),(0,s.useEffect)(function(){!1===t6&&(t6=!0)},[]),null!=(r=null!=t?t:o)?r:void 0),g=c.id?"".concat(c.id,"-grid-").concat(e.displayIndex):void 0,w=[f.month],y=h.month,x=0===e.displayIndex,k=e.displayIndex===b.length-1,M=!x&&!k;"rtl"===m&&(k=(u=[x,k])[0],x=u[1]),x&&(w.push(f.caption_start),y=e6(e6({},y),h.caption_start)),k&&(w.push(f.caption_end),y=e6(e6({},y),h.caption_end)),M&&(w.push(f.caption_between),y=e6(e6({},y),h.caption_between));var D=null!=(d=null==p?void 0:p.Caption)?d:tk;return(0,i.jsxs)("div",{className:w.join(" "),style:y,children:[(0,i.jsx)(D,{id:v,displayMonth:e.displayMonth,displayIndex:e.displayIndex}),(0,i.jsx)(t5,{id:g,"aria-labelledby":v,displayMonth:e.displayMonth})]},e.displayIndex)}function rt(e){var t=ti(),r=t.classNames,n=t.styles;return(0,i.jsx)("div",{className:r.months,style:n.months,children:e.children})}function rr(e){var t,r,n=e.initialProps,a=ti(),o=tX(),l=tp(),u=(0,s.useState)(!1),d=u[0],c=u[1];(0,s.useEffect)(function(){a.initialFocus&&o.focusTarget&&(d||(o.focus(o.focusTarget),c(!0)))},[a.initialFocus,d,o.focus,o.focusTarget,o]);var m=[a.classNames.root,a.className];a.numberOfMonths>1&&m.push(a.classNames.multiple_months),a.showWeekNumber&&m.push(a.classNames.with_weeknumber);var f=e6(e6({},a.styles.root),a.style),h=Object.keys(n).filter(function(e){return e.startsWith("data-")}).reduce(function(e,t){var r;return e6(e6({},e),((r={})[t]=n[t],r))},{}),p=null!=(r=null==(t=n.components)?void 0:t.Months)?r:rt;return(0,i.jsx)("div",e6({className:m.join(" "),style:f,dir:a.dir,id:a.id,nonce:n.nonce,title:n.title,lang:n.lang},h,{children:(0,i.jsx)(p,{children:l.displayMonths.map(function(e,t){return(0,i.jsx)(re,{displayIndex:t,displayMonth:e},t)})})}))}function rn(e){var t=e.children,r=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var a=0,n=Object.getOwnPropertySymbols(e);at.indexOf(n[a])&&Object.prototype.propertyIsEnumerable.call(e,n[a])&&(r[n[a]]=e[n[a]]);return r}(e,["children"]);return(0,i.jsx)(tl,{initialProps:r,children:(0,i.jsx)(th,{children:(0,i.jsx)(tU,{initialProps:r,children:(0,i.jsx)(tP,{initialProps:r,children:(0,i.jsx)(tj,{initialProps:r,children:(0,i.jsx)(tG,{children:(0,i.jsx)(tK,{children:t})})})})})})})}function ra(e){return(0,i.jsx)(rn,e6({},e,{children:(0,i.jsx)(rr,{initialProps:e})}))}let ro=e=>{var t=(0,u.__rest)(e,[]);return s.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),s.default.createElement("path",{d:"M10.8284 12.0007L15.7782 16.9504L14.364 18.3646L8 12.0007L14.364 5.63672L15.7782 7.05093L10.8284 12.0007Z"}))},rl=e=>{var t=(0,u.__rest)(e,[]);return s.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),s.default.createElement("path",{d:"M13.1717 12.0007L8.22192 7.05093L9.63614 5.63672L16.0001 12.0007L9.63614 18.3646L8.22192 16.9504L13.1717 12.0007Z"}))},ri=e=>{var t=(0,u.__rest)(e,[]);return s.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),s.default.createElement("path",{d:"M4.83582 12L11.0429 18.2071L12.4571 16.7929L7.66424 12L12.4571 7.20712L11.0429 5.79291L4.83582 12ZM10.4857 12L16.6928 18.2071L18.107 16.7929L13.3141 12L18.107 7.20712L16.6928 5.79291L10.4857 12Z"}))},rs=e=>{var t=(0,u.__rest)(e,[]);return s.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),s.default.createElement("path",{d:"M19.1642 12L12.9571 5.79291L11.5429 7.20712L16.3358 12L11.5429 16.7929L12.9571 18.2071L19.1642 12ZM13.5143 12L7.30722 5.79291L5.89301 7.20712L10.6859 12L5.89301 16.7929L7.30722 18.2071L13.5143 12Z"}))};var ru=e.i(936325),rd=e.i(728889);let rc=e=>{var{onClick:t,icon:r}=e,n=(0,u.__rest)(e,["onClick","icon"]);return s.default.createElement("button",Object.assign({type:"button",className:(0,b.tremorTwMerge)("flex items-center justify-center p-1 h-7 w-7 outline-none focus:ring-2 transition duration-100 border border-tremor-border dark:border-dark-tremor-border hover:bg-tremor-background-muted dark:hover:bg-dark-tremor-background-muted rounded-tremor-small focus:border-tremor-brand-subtle select-none dark:focus:border-dark-tremor-brand-subtle focus:ring-tremor-brand-muted dark:focus:ring-dark-tremor-brand-muted text-tremor-content-subtle dark:text-dark-tremor-content-subtle hover:text-tremor-content dark:hover:text-dark-tremor-content")},n),s.default.createElement(rd.default,{onClick:t,icon:r,variant:"simple",color:"slate",size:"sm"}))};function rm(e){var{mode:t,defaultMonth:r,selected:n,onSelect:a,locale:o,disabled:l,enableYearNavigation:i,classNames:d,weekStartsOn:c=0}=e,m=(0,u.__rest)(e,["mode","defaultMonth","selected","onSelect","locale","disabled","enableYearNavigation","classNames","weekStartsOn"]);return s.default.createElement(ra,Object.assign({showOutsideDays:!0,mode:t,defaultMonth:r,selected:n,onSelect:a,locale:o,disabled:l,weekStartsOn:c,classNames:Object.assign({months:"flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",month:"space-y-4",caption:"flex justify-center pt-2 relative items-center",caption_label:"text-tremor-default text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis font-medium",nav:"space-x-1 flex items-center",nav_button:"flex items-center justify-center p-1 h-7 w-7 outline-none focus:ring-2 transition duration-100 border border-tremor-border dark:border-dark-tremor-border hover:bg-tremor-background-muted dark:hover:bg-dark-tremor-background-muted rounded-tremor-small focus:border-tremor-brand-subtle dark:focus:border-dark-tremor-brand-subtle focus:ring-tremor-brand-muted dark:focus:ring-dark-tremor-brand-muted text-tremor-content-subtle dark:text-dark-tremor-content-subtle hover:text-tremor-content dark:hover:text-dark-tremor-content",nav_button_previous:"absolute left-1",nav_button_next:"absolute right-1",table:"w-full border-collapse space-y-1",head_row:"flex",head_cell:"w-9 font-normal text-center text-tremor-content-subtle dark:text-dark-tremor-content-subtle",row:"flex w-full mt-0.5",cell:"text-center p-0 relative focus-within:relative text-tremor-default text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis",day:"h-9 w-9 p-0 hover:bg-tremor-background-subtle dark:hover:bg-dark-tremor-background-subtle outline-tremor-brand dark:outline-dark-tremor-brand rounded-tremor-default",day_today:"font-bold",day_selected:"aria-selected:bg-tremor-background-emphasis aria-selected:text-tremor-content-inverted dark:aria-selected:bg-dark-tremor-background-emphasis dark:aria-selected:text-dark-tremor-content-inverted ",day_disabled:"text-tremor-content-subtle dark:text-dark-tremor-content-subtle disabled:hover:bg-transparent",day_outside:"text-tremor-content-subtle dark:text-dark-tremor-content-subtle"},d),components:{IconLeft:e=>{var t=(0,u.__rest)(e,[]);return s.default.createElement(ro,Object.assign({className:"h-4 w-4"},t))},IconRight:e=>{var t=(0,u.__rest)(e,[]);return s.default.createElement(rl,Object.assign({className:"h-4 w-4"},t))},Caption:e=>{var t=(0,u.__rest)(e,[]);let{goToMonth:r,nextMonth:n,previousMonth:a,currentMonth:l}=tp();return s.default.createElement("div",{className:"flex justify-between items-center"},s.default.createElement("div",{className:"flex items-center space-x-1"},i&&s.default.createElement(rc,{onClick:()=>l&&r(ey(l,-1)),icon:ri}),s.default.createElement(rc,{onClick:()=>a&&r(a),icon:ro})),s.default.createElement(ru.default,{className:"text-tremor-default tabular-nums capitalize text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis font-medium"},es(t.displayMonth,"LLLL yyy",{locale:o})),s.default.createElement("div",{className:"flex items-center space-x-1"},s.default.createElement(rc,{onClick:()=>n&&r(n),icon:rl}),i&&s.default.createElement(rc,{onClick:()=>l&&r(ey(l,1)),icon:rs})))}}},m))}rm.displayName="DateRangePicker";var rf=e.i(333771),rh=e.i(888288),rp=e.i(783222),rb=e.i(433336),rv=e.i(394487),rg=e.i(992704),rw=e.i(914189),ry=e.i(941444),rx=e.i(835696),rk=e.i(877891),rM=e.i(952744),rD=e.i(605083),rN=e.i(144279),rE=e.i(2788),rS=e.i(402155);let rP=(0,s.createContext)(null);function rT({children:e,node:t}){let[r,n]=(0,s.useState)(null),a=rC(null!=t?t:r);return s.default.createElement(rP.Provider,{value:a},e,null===a&&s.default.createElement(rE.Hidden,{features:rE.HiddenFeatures.Hidden,ref:e=>{var t,r;if(e){for(let a of null!=(r=null==(t=(0,rS.getOwnerDocument)(e))?void 0:t.querySelectorAll("html > *, body > *"))?r:[])if(a!==document.body&&a!==document.head&&a instanceof HTMLElement&&null!=a&&a.contains(e)){n(a);break}}}}))}function rC(e=null){var t;return null!=(t=(0,s.useContext)(rP))?t:e}var r_=e.i(101852),rj=e.i(294316),rF=e.i(401141),rL=((t=rL||{})[t.Forwards=0]="Forwards",t[t.Backwards=1]="Backwards",t);function rO(){let e=(0,s.useRef)(0);return(0,rF.useWindowEvent)(!0,"keydown",t=>{"Tab"===t.key&&(e.current=+!!t.shiftKey)},!0),e}var rI=e.i(83733),rY=e.i(674175),rW=e.i(919751),rH=e.i(233137),rR=e.i(233538),rB=e.i(652265),rq=e.i(397701),rA=e.i(700020),rQ=e.i(998348),rG=e.i(635307),rz=((r=rz||{})[r.Open=0]="Open",r[r.Closed=1]="Closed",r),rV=((n=rV||{})[n.TogglePopover=0]="TogglePopover",n[n.ClosePopover=1]="ClosePopover",n[n.SetButton=2]="SetButton",n[n.SetButtonId=3]="SetButtonId",n[n.SetPanel=4]="SetPanel",n[n.SetPanelId=5]="SetPanelId",n);let r$={0:e=>({...e,popoverState:(0,rq.match)(e.popoverState,{0:1,1:0}),__demoMode:!1}),1:e=>1===e.popoverState?e:{...e,popoverState:1,__demoMode:!1},2:(e,t)=>e.button===t.button?e:{...e,button:t.button},3:(e,t)=>e.buttonId===t.buttonId?e:{...e,buttonId:t.buttonId},4:(e,t)=>e.panel===t.panel?e:{...e,panel:t.panel},5:(e,t)=>e.panelId===t.panelId?e:{...e,panelId:t.panelId}},rK=(0,s.createContext)(null);function rX(e){let t=(0,s.useContext)(rK);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,rX),t}return t}rK.displayName="PopoverContext";let rZ=(0,s.createContext)(null);function rU(e){let t=(0,s.useContext)(rZ);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,rU),t}return t}rZ.displayName="PopoverAPIContext";let rJ=(0,s.createContext)(null);function r0(){return(0,s.useContext)(rJ)}rJ.displayName="PopoverGroupContext";let r1=(0,s.createContext)(null);function r2(e,t){return(0,rq.match)(t.type,r$,e,t)}r1.displayName="PopoverPanelContext";let r4=rA.RenderFeatures.RenderStrategy|rA.RenderFeatures.Static;function r3(e,t){let r=(0,s.useId)(),{id:n=`headlessui-popover-backdrop-${r}`,transition:a=!1,...o}=e,[{popoverState:l},i]=rX("Popover.Backdrop"),[u,d]=(0,s.useState)(null),c=(0,rj.useSyncRefs)(t,d),m=(0,rH.useOpenClosed)(),[f,h]=(0,rI.useTransition)(a,u,null!==m?(m&rH.State.Open)===rH.State.Open:0===l),p=(0,rw.useEvent)(e=>{if((0,rR.isDisabledReactIssue7711)(e.currentTarget))return e.preventDefault();i({type:1})}),b=(0,s.useMemo)(()=>({open:0===l}),[l]),v={ref:c,id:n,"aria-hidden":!0,onClick:p,...(0,rI.transitionDataAttributes)(h)};return(0,rA.useRender)()({ourProps:v,theirProps:o,slot:b,defaultTag:"div",features:r4,visible:f,name:"Popover.Backdrop"})}let r5=rA.RenderFeatures.RenderStrategy|rA.RenderFeatures.Static,r7=(0,rA.forwardRefWithAs)(function(e,t){var r,n,a;let o,{__demoMode:l=!1,...i}=e,u=(0,s.useRef)(null),d=(0,rj.useSyncRefs)(t,(0,rj.optionalRef)(e=>{u.current=e})),c=(0,s.useRef)([]),m=(0,s.useReducer)(r2,{__demoMode:l,popoverState:+!l,buttons:c,button:null,buttonId:null,panel:null,panelId:null,beforePanelSentinel:(0,s.createRef)(),afterPanelSentinel:(0,s.createRef)(),afterButtonSentinel:(0,s.createRef)()}),[{popoverState:f,button:h,buttonId:p,panel:b,panelId:v,beforePanelSentinel:g,afterPanelSentinel:w,afterButtonSentinel:y},x]=m,k=(0,rD.useOwnerDocument)(null!=(r=u.current)?r:h),M=(0,s.useMemo)(()=>{if(!h||!b)return!1;for(let e of document.querySelectorAll("body > *"))if(Number(null==e?void 0:e.contains(h))^Number(null==e?void 0:e.contains(b)))return!0;let e=(0,rB.getFocusableElements)(),t=e.indexOf(h),r=(t+e.length-1)%e.length,n=(t+1)%e.length,a=e[r],o=e[n];return!b.contains(a)&&!b.contains(o)},[h,b]),D=(0,ry.useLatestValue)(p),N=(0,ry.useLatestValue)(v),E=(0,s.useMemo)(()=>({buttonId:D,panelId:N,close:()=>x({type:1})}),[D,N,x]),S=r0(),P=null==S?void 0:S.registerPopover,T=(0,rw.useEvent)(()=>{var e;return null!=(e=null==S?void 0:S.isFocusWithinPopoverGroup())?e:(null==k?void 0:k.activeElement)&&((null==h?void 0:h.contains(k.activeElement))||(null==b?void 0:b.contains(k.activeElement)))});(0,s.useEffect)(()=>null==P?void 0:P(E),[P,E]);let[C,_]=(0,rG.useNestedPortals)(),j=rC(h),F=function({defaultContainers:e=[],portals:t,mainTreeNode:r}={}){let n=(0,rD.useOwnerDocument)(r),a=(0,rw.useEvent)(()=>{var a,o;let l=[];for(let t of e)null!==t&&(t instanceof HTMLElement?l.push(t):"current"in t&&t.current instanceof HTMLElement&&l.push(t.current));if(null!=t&&t.current)for(let e of t.current)l.push(e);for(let e of null!=(a=null==n?void 0:n.querySelectorAll("html > *, body > *"))?a:[])e!==document.body&&e!==document.head&&e instanceof HTMLElement&&"headlessui-portal-root"!==e.id&&(r&&(e.contains(r)||e.contains(null==(o=null==r?void 0:r.getRootNode())?void 0:o.host))||l.some(t=>e.contains(t))||l.push(e));return l});return{resolveContainers:a,contains:(0,rw.useEvent)(e=>a().some(t=>t.contains(e)))}}({mainTreeNode:j,portals:C,defaultContainers:[h,b]});n=null==k?void 0:k.defaultView,a="focus",o=(0,ry.useLatestValue)(e=>{var t,r,n,a,o,l;e.target!==window&&e.target instanceof HTMLElement&&0===f&&(T()||h&&b&&(F.contains(e.target)||null!=(r=null==(t=g.current)?void 0:t.contains)&&r.call(t,e.target)||null!=(a=null==(n=w.current)?void 0:n.contains)&&a.call(n,e.target)||null!=(l=null==(o=y.current)?void 0:o.contains)&&l.call(o,e.target)||x({type:1})))}),(0,s.useEffect)(()=>{function e(e){o.current(e)}return(n=null!=n?n:window).addEventListener(a,e,!0),()=>n.removeEventListener(a,e,!0)},[n,a,!0]),(0,rM.useOutsideClick)(0===f,F.resolveContainers,(e,t)=>{x({type:1}),(0,rB.isFocusableElement)(t,rB.FocusableMode.Loose)||(e.preventDefault(),null==h||h.focus())});let L=(0,rw.useEvent)(e=>{x({type:1});let t=e?e instanceof HTMLElement?e:"current"in e&&e.current instanceof HTMLElement?e.current:h:h;null==t||t.focus()}),O=(0,s.useMemo)(()=>({close:L,isPortalled:M}),[L,M]),I=(0,s.useMemo)(()=>({open:0===f,close:L}),[f,L]),Y=(0,rA.useRender)();return s.default.createElement(rT,{node:j},s.default.createElement(rW.FloatingProvider,null,s.default.createElement(r1.Provider,{value:null},s.default.createElement(rK.Provider,{value:m},s.default.createElement(rZ.Provider,{value:O},s.default.createElement(rY.CloseProvider,{value:L},s.default.createElement(rH.OpenClosedProvider,{value:(0,rq.match)(f,{0:rH.State.Open,1:rH.State.Closed})},s.default.createElement(_,null,Y({ourProps:{ref:d},theirProps:i,slot:I,defaultTag:"div",name:"Popover"})))))))))}),r6=(0,rA.forwardRefWithAs)(function(e,t){let r=(0,s.useId)(),{id:n=`headlessui-popover-button-${r}`,disabled:a=!1,autoFocus:o=!1,...l}=e,[i,u]=rX("Popover.Button"),{isPortalled:d}=rU("Popover.Button"),c=(0,s.useRef)(null),m=`headlessui-focus-sentinel-${(0,s.useId)()}`,f=r0(),h=null==f?void 0:f.closeOthers,p=null!==(0,s.useContext)(r1);(0,s.useEffect)(()=>{if(!p)return u({type:3,buttonId:n}),()=>{u({type:3,buttonId:null})}},[p,n,u]);let[b]=(0,s.useState)(()=>Symbol()),v=(0,rj.useSyncRefs)(c,t,(0,rW.useFloatingReference)(),(0,rw.useEvent)(e=>{if(!p){if(e)i.buttons.current.push(b);else{let e=i.buttons.current.indexOf(b);-1!==e&&i.buttons.current.splice(e,1)}i.buttons.current.length>1&&console.warn("You are already using a but only 1 is supported."),e&&u({type:2,button:e})}})),g=(0,rj.useSyncRefs)(c,t),w=(0,rD.useOwnerDocument)(c),y=(0,rw.useEvent)(e=>{var t,r,n;if(p){if(1===i.popoverState)return;switch(e.key){case rQ.Keys.Space:case rQ.Keys.Enter:e.preventDefault(),null==(r=(t=e.target).click)||r.call(t),u({type:1}),null==(n=i.button)||n.focus()}}else switch(e.key){case rQ.Keys.Space:case rQ.Keys.Enter:e.preventDefault(),e.stopPropagation(),1===i.popoverState&&(null==h||h(i.buttonId)),u({type:0});break;case rQ.Keys.Escape:if(0!==i.popoverState)return null==h?void 0:h(i.buttonId);if(!c.current||null!=w&&w.activeElement&&!c.current.contains(w.activeElement))return;e.preventDefault(),e.stopPropagation(),u({type:1})}}),x=(0,rw.useEvent)(e=>{p||e.key===rQ.Keys.Space&&e.preventDefault()}),k=(0,rw.useEvent)(e=>{var t,r;(0,rR.isDisabledReactIssue7711)(e.currentTarget)||a||(p?(u({type:1}),null==(t=i.button)||t.focus()):(e.preventDefault(),e.stopPropagation(),1===i.popoverState&&(null==h||h(i.buttonId)),u({type:0}),null==(r=i.button)||r.focus()))}),M=(0,rw.useEvent)(e=>{e.preventDefault(),e.stopPropagation()}),{isFocusVisible:D,focusProps:N}=(0,rp.useFocusRing)({autoFocus:o}),{isHovered:E,hoverProps:S}=(0,rb.useHover)({isDisabled:a}),{pressed:P,pressProps:T}=(0,rv.useActivePress)({disabled:a}),C=0===i.popoverState,_=(0,s.useMemo)(()=>({open:C,active:P||C,disabled:a,hover:E,focus:D,autofocus:o}),[C,E,D,P,a,o]),j=(0,rN.useResolveButtonType)(e,i.button),F=p?(0,rA.mergeProps)({ref:g,type:j,onKeyDown:y,onClick:k,disabled:a||void 0,autoFocus:o},N,S,T):(0,rA.mergeProps)({ref:v,id:i.buttonId,type:j,"aria-expanded":0===i.popoverState,"aria-controls":i.panel?i.panelId:void 0,disabled:a||void 0,autoFocus:o,onKeyDown:y,onKeyUp:x,onClick:k,onMouseDown:M},N,S,T),L=rO(),O=(0,rw.useEvent)(()=>{let e=i.panel;e&&(0,rq.match)(L.current,{[rL.Forwards]:()=>(0,rB.focusIn)(e,rB.Focus.First),[rL.Backwards]:()=>(0,rB.focusIn)(e,rB.Focus.Last)})===rB.FocusResult.Error&&(0,rB.focusIn)((0,rB.getFocusableElements)().filter(e=>"true"!==e.dataset.headlessuiFocusGuard),(0,rq.match)(L.current,{[rL.Forwards]:rB.Focus.Next,[rL.Backwards]:rB.Focus.Previous}),{relativeTo:i.button})}),I=(0,rA.useRender)();return s.default.createElement(s.default.Fragment,null,I({ourProps:F,theirProps:l,slot:_,defaultTag:"button",name:"Popover.Button"}),C&&!p&&d&&s.default.createElement(rE.Hidden,{id:m,ref:i.afterButtonSentinel,features:rE.HiddenFeatures.Focusable,"data-headlessui-focus-guard":!0,as:"button",type:"button",onFocus:O}))}),r8=(0,rA.forwardRefWithAs)(r3),r9=(0,rA.forwardRefWithAs)(r3),ne=(0,rA.forwardRefWithAs)(function(e,t){let r=(0,s.useId)(),{id:n=`headlessui-popover-panel-${r}`,focus:a=!1,anchor:o,portal:l=!1,modal:i=!1,transition:u=!1,...d}=e,[c,m]=rX("Popover.Panel"),{close:f,isPortalled:h}=rU("Popover.Panel"),p=`headlessui-focus-sentinel-before-${r}`,b=`headlessui-focus-sentinel-after-${r}`,v=(0,s.useRef)(null),g=(0,rW.useResolvedAnchor)(o),[w,y]=(0,rW.useFloatingPanel)(g),x=(0,rW.useFloatingPanelProps)();g&&(l=!0);let[k,M]=(0,s.useState)(null),D=(0,rj.useSyncRefs)(v,t,g?w:null,(0,rw.useEvent)(e=>m({type:4,panel:e})),M),N=(0,rD.useOwnerDocument)(v);(0,rx.useIsoMorphicEffect)(()=>(m({type:5,panelId:n}),()=>{m({type:5,panelId:null})}),[n,m]);let E=(0,rH.useOpenClosed)(),[S,P]=(0,rI.useTransition)(u,k,null!==E?(E&rH.State.Open)===rH.State.Open:0===c.popoverState);(0,rk.useOnDisappear)(S,c.button,()=>{m({type:1})});let T=!c.__demoMode&&i&&S;(0,r_.useScrollLock)(T,N);let C=(0,rw.useEvent)(e=>{var t;if(e.key===rQ.Keys.Escape){if(0!==c.popoverState||!v.current||null!=N&&N.activeElement&&!v.current.contains(N.activeElement))return;e.preventDefault(),e.stopPropagation(),m({type:1}),null==(t=c.button)||t.focus()}});(0,s.useEffect)(()=>{var t;e.static||1===c.popoverState&&(null==(t=e.unmount)||t)&&m({type:4,panel:null})},[c.popoverState,e.unmount,e.static,m]),(0,s.useEffect)(()=>{if(c.__demoMode||!a||0!==c.popoverState||!v.current)return;let e=null==N?void 0:N.activeElement;v.current.contains(e)||(0,rB.focusIn)(v.current,rB.Focus.First)},[c.__demoMode,a,v.current,c.popoverState]);let _=(0,s.useMemo)(()=>({open:0===c.popoverState,close:f}),[c.popoverState,f]),j=(0,rA.mergeProps)(g?x():{},{ref:D,id:n,onKeyDown:C,onBlur:a&&0===c.popoverState?e=>{var t,r,n,a,o;let l=e.relatedTarget;l&&v.current&&(null!=(t=v.current)&&t.contains(l)||(m({type:1}),(null!=(n=null==(r=c.beforePanelSentinel.current)?void 0:r.contains)&&n.call(r,l)||null!=(o=null==(a=c.afterPanelSentinel.current)?void 0:a.contains)&&o.call(a,l))&&l.focus({preventScroll:!0})))}:void 0,tabIndex:-1,style:{...d.style,...y,"--button-width":(0,rg.useElementSize)(c.button,!0).width},...(0,rI.transitionDataAttributes)(P)}),F=rO(),L=(0,rw.useEvent)(()=>{let e=v.current;e&&(0,rq.match)(F.current,{[rL.Forwards]:()=>{var t;(0,rB.focusIn)(e,rB.Focus.First)===rB.FocusResult.Error&&(null==(t=c.afterPanelSentinel.current)||t.focus())},[rL.Backwards]:()=>{var e;null==(e=c.button)||e.focus({preventScroll:!0})}})}),O=(0,rw.useEvent)(()=>{let e=v.current;e&&(0,rq.match)(F.current,{[rL.Forwards]:()=>{if(!c.button)return;let e=(0,rB.getFocusableElements)(),t=e.indexOf(c.button),r=e.slice(0,t+1),n=[...e.slice(t+1),...r];for(let e of n.slice())if("true"===e.dataset.headlessuiFocusGuard||null!=k&&k.contains(e)){let t=n.indexOf(e);-1!==t&&n.splice(t,1)}(0,rB.focusIn)(n,rB.Focus.First,{sorted:!1})},[rL.Backwards]:()=>{var t;(0,rB.focusIn)(e,rB.Focus.Previous)===rB.FocusResult.Error&&(null==(t=c.button)||t.focus())}})}),I=(0,rA.useRender)();return s.default.createElement(rH.ResetOpenClosedProvider,null,s.default.createElement(r1.Provider,{value:n},s.default.createElement(rZ.Provider,{value:{close:f,isPortalled:h}},s.default.createElement(rG.Portal,{enabled:!!l&&(e.static||S)},S&&h&&s.default.createElement(rE.Hidden,{id:p,ref:c.beforePanelSentinel,features:rE.HiddenFeatures.Focusable,"data-headlessui-focus-guard":!0,as:"button",type:"button",onFocus:L}),I({ourProps:j,theirProps:d,slot:_,defaultTag:"div",features:r5,visible:S,name:"Popover.Panel"}),S&&h&&s.default.createElement(rE.Hidden,{id:b,ref:c.afterPanelSentinel,features:rE.HiddenFeatures.Focusable,"data-headlessui-focus-guard":!0,as:"button",type:"button",onFocus:O})))))}),nt=Object.assign(r7,{Button:r6,Backdrop:r9,Overlay:r8,Panel:ne,Group:(0,rA.forwardRefWithAs)(function(e,t){let r=(0,s.useRef)(null),n=(0,rj.useSyncRefs)(r,t),[a,o]=(0,s.useState)([]),l=(0,rw.useEvent)(e=>{o(t=>{let r=t.indexOf(e);if(-1!==r){let e=t.slice();return e.splice(r,1),e}return t})}),i=(0,rw.useEvent)(e=>(o(t=>[...t,e]),()=>l(e))),u=(0,rw.useEvent)(()=>{var e;let t=(0,rS.getOwnerDocument)(r);if(!t)return!1;let n=t.activeElement;return!!(null!=(e=r.current)&&e.contains(n))||a.some(e=>{var r,a;return(null==(r=t.getElementById(e.buttonId.current))?void 0:r.contains(n))||(null==(a=t.getElementById(e.panelId.current))?void 0:a.contains(n))})}),d=(0,rw.useEvent)(e=>{for(let t of a)t.buttonId.current!==e&&t.close()}),c=(0,s.useMemo)(()=>({registerPopover:i,unregisterPopover:l,isFocusWithinPopoverGroup:u,closeOthers:d}),[i,l,u,d]),m=(0,s.useMemo)(()=>({}),[]),f=(0,rA.useRender)();return s.default.createElement(rT,null,s.default.createElement(rJ.Provider,{value:c},f({ourProps:{ref:n},theirProps:e,slot:m,defaultTag:"div",name:"Popover.Group"})))})});var nr=e.i(854056),nn=e.i(495470);let na=h(),no=s.default.forwardRef((e,t)=>{var r,n;let{value:a,defaultValue:o,onValueChange:l,enableSelect:i=!0,minDate:g,maxDate:w,placeholder:y="Select range",selectPlaceholder:x="Select range",disabled:k=!1,locale:M=j,enableClear:E=!0,displayFormat:S,children:P,className:T,enableYearNavigation:C=!1,weekStartsOn:_=0,disabledDates:F}=e,L=(0,u.__rest)(e,["value","defaultValue","onValueChange","enableSelect","minDate","maxDate","placeholder","selectPlaceholder","disabled","locale","enableClear","displayFormat","children","className","enableYearNavigation","weekStartsOn","disabledDates"]),[O,I]=(0,rh.default)(o,a),[Y,W]=(0,s.useState)(!1),[H,R]=(0,s.useState)(!1),B=(0,s.useMemo)(()=>{let e=[];return g&&e.push({before:g}),w&&e.push({after:w}),[...e,...null!=F?F:[]]},[g,w,F]),q=(0,s.useMemo)(()=>{let e=new Map;return P?s.default.Children.forEach(P,t=>{var r;e.set(t.props.value,{text:null!=(r=(0,v.getNodeText)(t))?r:t.props.value,from:t.props.from,to:t.props.to})}):ed.forEach(t=>{e.set(t.value,{text:t.text,from:t.from,to:na})}),e},[P]),A=(0,s.useMemo)(()=>{if(P)return(0,v.constructValueToNameMapping)(P);let e=new Map;return ed.forEach(t=>e.set(t.value,t.text)),e},[P]),Q=(null==O?void 0:O.selectValue)||"",G=((e,t,r,n)=>{var a;if(r&&(e=null==(a=n.get(r))?void 0:a.from),e)return f(e&&!t?e:D([e,t]))})(null==O?void 0:O.from,g,Q,q),z=((e,t,r,n)=>{var a,o;if(r&&(e=f(null!=(o=null==(a=n.get(r))?void 0:a.to)?o:h())),e)return f(e&&!t?e:N([e,t]))})(null==O?void 0:O.to,w,Q,q),V=G||z?((e,t,r,n)=>{let a=(null==r?void 0:r.code)||"en-US";if(!e&&!t)return"";if(e&&!t)return n?es(e,n):e.toLocaleDateString(a,{year:"numeric",month:"short",day:"numeric"});if(e&&t){if(+(0,m.toDate)(e)==+(0,m.toDate)(t))return n?es(e,n):e.toLocaleDateString(a,{year:"numeric",month:"short",day:"numeric"});if(e.getMonth()===t.getMonth()&&e.getFullYear()===t.getFullYear())return n?`${es(e,n)} - ${es(t,n)}`:`${e.toLocaleDateString(a,{month:"short",day:"numeric"})} - + ${t.getDate()}, ${t.getFullYear()}`;{if(n)return`${es(e,n)} - ${es(t,n)}`;let r={year:"numeric",month:"short",day:"numeric"};return`${e.toLocaleDateString(a,r)} - + ${t.toLocaleDateString(a,r)}`}}return""})(G,z,M,S):y,$=p(null!=(n=null!=(r=null!=z?z:G)?r:w)?n:na),K=E&&!k;return s.default.createElement("div",Object.assign({ref:t,className:(0,b.tremorTwMerge)("w-full min-w-[10rem] relative flex justify-between text-tremor-default max-w-sm shadow-tremor-input dark:shadow-dark-tremor-input rounded-tremor-default",T)},L),s.default.createElement(nt,{as:"div",className:(0,b.tremorTwMerge)("w-full",i?"rounded-l-tremor-default":"rounded-tremor-default",Y&&"ring-2 ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted z-10")},s.default.createElement("div",{className:"relative w-full"},s.default.createElement(r6,{onFocus:()=>W(!0),onBlur:()=>W(!1),disabled:k,className:(0,b.tremorTwMerge)("w-full outline-none text-left whitespace-nowrap truncate focus:ring-2 transition duration-100 rounded-l-tremor-default flex flex-nowrap border pl-3 py-2","rounded-l-tremor-default border-tremor-border text-tremor-content-emphasis focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted","dark:border-dark-tremor-border dark:text-dark-tremor-content-emphasis dark:focus:border-dark-tremor-brand-subtle dark:focus:ring-dark-tremor-brand-muted",i?"rounded-l-tremor-default":"rounded-tremor-default",K?"pr-8":"pr-4",(0,v.getSelectButtonColors)((0,v.hasValue)(G||z),k))},s.default.createElement(d,{className:(0,b.tremorTwMerge)(eu("calendarIcon"),"flex-none shrink-0 h-5 w-5 -ml-0.5 mr-2","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle"),"aria-hidden":"true"}),s.default.createElement("p",{className:"truncate"},V)),K&&G?s.default.createElement("button",{type:"button",className:(0,b.tremorTwMerge)("absolute outline-none inset-y-0 right-0 flex items-center transition duration-100 mr-4"),onClick:e=>{e.preventDefault(),null==l||l({}),I({})}},s.default.createElement(c.default,{className:(0,b.tremorTwMerge)(eu("clearIcon"),"flex-none h-4 w-4","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})):null),s.default.createElement(nr.Transition,{enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},s.default.createElement(ne,{anchor:"bottom start",focus:!0,className:(0,b.tremorTwMerge)("min-w-min divide-y overflow-y-auto outline-none rounded-tremor-default p-3 border [--anchor-gap:4px]","bg-tremor-background border-tremor-border divide-tremor-border shadow-tremor-dropdown","dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border dark:shadow-dark-tremor-dropdown")},s.default.createElement(rm,Object.assign({mode:"range",showOutsideDays:!0,defaultMonth:$,selected:{from:G,to:z},onSelect:e=>{null==l||l({from:null==e?void 0:e.from,to:null==e?void 0:e.to}),I({from:null==e?void 0:e.from,to:null==e?void 0:e.to})},locale:M,disabled:B,enableYearNavigation:C,classNames:{day_range_middle:(0,b.tremorTwMerge)("!rounded-none aria-selected:!bg-tremor-background-subtle aria-selected:dark:!bg-dark-tremor-background-subtle aria-selected:!text-tremor-content aria-selected:dark:!bg-dark-tremor-background-subtle"),day_range_start:"rounded-r-none rounded-l-tremor-small aria-selected:text-tremor-brand-inverted dark:aria-selected:text-dark-tremor-brand-inverted",day_range_end:"rounded-l-none rounded-r-tremor-small aria-selected:text-tremor-brand-inverted dark:aria-selected:text-dark-tremor-brand-inverted"},weekStartsOn:_},e))))),i&&s.default.createElement(nn.Listbox,{as:"div",className:(0,b.tremorTwMerge)("w-48 -ml-px rounded-r-tremor-default",H&&"ring-2 ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted z-10"),value:Q,onChange:e=>{let{from:t,to:r}=q.get(e),n=null!=r?r:na;null==l||l({from:t,to:n,selectValue:e}),I({from:t,to:n,selectValue:e})},disabled:k},({value:e})=>{var t;return s.default.createElement(s.default.Fragment,null,s.default.createElement(nn.ListboxButton,{onFocus:()=>R(!0),onBlur:()=>R(!1),className:(0,b.tremorTwMerge)("w-full outline-none text-left whitespace-nowrap truncate rounded-r-tremor-default transition duration-100 border px-4 py-2","border-tremor-border text-tremor-content-emphasis focus:border-tremor-brand-subtle","dark:border-dark-tremor-border dark:text-dark-tremor-content-emphasis dark:focus:border-dark-tremor-brand-subtle",(0,v.getSelectButtonColors)((0,v.hasValue)(e),k))},e&&null!=(t=A.get(e))?t:x),s.default.createElement(nr.Transition,{enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},s.default.createElement(nn.ListboxOptions,{anchor:"bottom end",className:(0,b.tremorTwMerge)("[--anchor-gap:4px] divide-y overflow-y-auto outline-none border min-w-44","shadow-tremor-dropdown bg-tremor-background border-tremor-border divide-tremor-border rounded-tremor-default","dark:shadow-dark-tremor-dropdown dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border")},null!=P?P:ed.map(e=>s.default.createElement(rf.default,{key:e.value,value:e.value},e.text)))))}))});no.displayName="DateRangePicker";var nl=e.i(599724);e.s(["default",0,({value:e,onValueChange:t,label:r="Select Time Range",className:n="",showTimeRange:a=!0})=>{let[o,l]=(0,s.useState)(!1),u=(0,s.useRef)(null),d=(0,s.useCallback)(e=>{l(!0),setTimeout(()=>l(!1),1500),t(e),requestIdleCallback(()=>{if(e.from){let r,n={...e},a=new Date(e.from);r=new Date(e.to?e.to:e.from),a.toDateString(),r.toDateString(),a.setHours(0,0,0,0),r.setHours(23,59,59,999),n.from=a,n.to=r,t(n)}},{timeout:100})},[t]),c=(0,s.useCallback)((e,t)=>{if(!e||!t)return"";let r=e=>e.toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!0,timeZoneName:"short"});if(e.toDateString()!==t.toDateString())return`${r(e)} - ${r(t)}`;{let r=e.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}),n=e.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!0}),a=t.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!0,timeZoneName:"short"});return`${r}: ${n} - ${a}`}},[]);return(0,i.jsxs)("div",{className:n,children:[r&&(0,i.jsx)(nl.Text,{className:"mb-2",children:r}),(0,i.jsxs)("div",{className:"relative w-fit",children:[(0,i.jsx)("div",{ref:u,children:(0,i.jsx)(no,{enableSelect:!0,value:e,onValueChange:d,placeholder:"Select date range",enableClear:!1,style:{zIndex:100}})}),o&&(0,i.jsx)("div",{className:"absolute top-1/2 animate-pulse",style:{left:"calc(100% + 8px)",transform:"translateY(-50%)",zIndex:110},children:(0,i.jsxs)("div",{className:"flex items-center gap-1 text-green-600 text-sm font-medium bg-white px-2 py-1 rounded-full border border-green-200 shadow-xs whitespace-nowrap",children:[(0,i.jsx)("div",{className:"w-3 h-3 bg-green-500 text-white rounded-full flex items-center justify-center text-xs",children:"✓"}),(0,i.jsx)("span",{className:"text-xs",children:"Selected"})]})})]}),a&&e.from&&e.to&&(0,i.jsx)(nl.Text,{className:"mt-2 text-xs text-gray-500",children:c(e.from,e.to)})]})}],144267)},621642,25080,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(144582),a=e.i(888288),o=e.i(757440);let l=e=>{var n=(0,t.__rest)(e,[]);return r.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},n),r.default.createElement("path",{d:"M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"}))};var i=e.i(446428);let s=e=>{var n=(0,t.__rest)(e,[]);return r.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",width:"100%",height:"100%",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},n),r.default.createElement("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),r.default.createElement("line",{x1:"6",y1:"6",x2:"18",y2:"18"}))};var u=e.i(444755),d=e.i(673706),c=e.i(103471),m=e.i(495470),f=e.i(854056);let h=(0,d.makeClassName)("MultiSelect"),p=r.default.forwardRef((e,d)=>{let{defaultValue:p=[],value:b,onValueChange:v,placeholder:g="Select...",placeholderSearch:w="Search",disabled:y=!1,icon:x,children:k,className:M,required:D,name:N,error:E=!1,errorMessage:S,id:P}=e,T=(0,t.__rest)(e,["defaultValue","value","onValueChange","placeholder","placeholderSearch","disabled","icon","children","className","required","name","error","errorMessage","id"]),C=(0,r.useRef)(null),[_,j]=(0,a.default)(p,b),{reactElementChildren:F,optionsAvailable:L}=(0,r.useMemo)(()=>{let e=r.default.Children.toArray(k).filter(r.isValidElement);return{reactElementChildren:e,optionsAvailable:(0,c.getFilteredOptions)("",e)}},[k]),[O,I]=(0,r.useState)(""),Y=(null!=_?_:[]).length>0,W=(0,r.useMemo)(()=>O?(0,c.getFilteredOptions)(O,F):L,[O,F,L]),H=()=>{I("")};return r.default.createElement("div",{className:(0,u.tremorTwMerge)("w-full min-w-[10rem] text-tremor-default",M)},r.default.createElement("div",{className:"relative"},r.default.createElement("select",{title:"multi-select-hidden",required:D,className:(0,u.tremorTwMerge)("h-full w-full absolute left-0 top-0 -z-10 opacity-0"),value:_,onChange:e=>{e.preventDefault()},name:N,disabled:y,multiple:!0,id:P,onFocus:()=>{let e=C.current;e&&e.focus()}},r.default.createElement("option",{className:"hidden",value:"",disabled:!0,hidden:!0},g),W.map(e=>{let t=e.props.value,n=e.props.children;return r.default.createElement("option",{className:"hidden",key:t,value:t},n)})),r.default.createElement(m.Listbox,Object.assign({as:"div",ref:d,defaultValue:_,value:_,onChange:e=>{null==v||v(e),j(e)},disabled:y,id:P,multiple:!0},T),({value:e})=>r.default.createElement(r.default.Fragment,null,r.default.createElement(m.ListboxButton,{className:(0,u.tremorTwMerge)("w-full outline-none text-left whitespace-nowrap truncate rounded-tremor-default focus:ring-2 transition duration-100 border pr-8 py-1.5","border-tremor-border shadow-tremor-input focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted","dark:border-dark-tremor-border dark:shadow-dark-tremor-input dark:focus:border-dark-tremor-brand-subtle dark:focus:ring-dark-tremor-brand-muted",x?"pl-11 -ml-0.5":"pl-3",(0,c.getSelectButtonColors)(e.length>0,y,E)),ref:C},x&&r.default.createElement("span",{className:(0,u.tremorTwMerge)("absolute inset-y-0 left-0 flex items-center ml-px pl-2.5")},r.default.createElement(x,{className:(0,u.tremorTwMerge)(h("Icon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})),r.default.createElement("div",{className:"h-6 flex items-center"},e.length>0?r.default.createElement("div",{className:"flex flex-nowrap overflow-x-scroll [&::-webkit-scrollbar]:hidden [scrollbar-width:none] gap-x-1 mr-5 -ml-1.5 relative"},L.filter(t=>e.includes(t.props.value)).map((t,n)=>{var a;return r.default.createElement("div",{key:n,className:(0,u.tremorTwMerge)("max-w-[100px] lg:max-w-[200px] flex justify-center items-center pl-2 pr-1.5 py-1 font-medium","rounded-tremor-small","bg-tremor-background-muted dark:bg-dark-tremor-background-muted","bg-tremor-background-subtle dark:bg-dark-tremor-background-subtle","text-tremor-content-default dark:text-dark-tremor-content-default","text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis")},r.default.createElement("div",{className:"text-xs truncate "},null!=(a=t.props.children)?a:t.props.value),r.default.createElement("div",{onClick:r=>{r.preventDefault();let n=e.filter(e=>e!==t.props.value);null==v||v(n),j(n)}},r.default.createElement(s,{className:(0,u.tremorTwMerge)(h("clearIconItem"),"cursor-pointer rounded-tremor-full w-3.5 h-3.5 ml-2","text-tremor-content-subtle hover:text-tremor-content","dark:text-dark-tremor-content-subtle dark:hover:text-tremor-content")})))})):r.default.createElement("span",null,g)),r.default.createElement("span",{className:(0,u.tremorTwMerge)("absolute inset-y-0 right-0 flex items-center mr-2.5")},r.default.createElement(o.default,{className:(0,u.tremorTwMerge)(h("arrowDownIcon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")}))),Y&&!y?r.default.createElement("button",{type:"button",className:(0,u.tremorTwMerge)("absolute inset-y-0 right-0 flex items-center mr-8"),onClick:e=>{e.preventDefault(),j([]),null==v||v([])}},r.default.createElement(i.default,{className:(0,u.tremorTwMerge)(h("clearIconAllItems"),"flex-none h-4 w-4","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})):null,r.default.createElement(f.Transition,{enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},r.default.createElement(m.ListboxOptions,{anchor:"bottom start",className:(0,u.tremorTwMerge)("z-10 divide-y w-[var(--button-width)] overflow-y-auto outline-none rounded-tremor-default max-h-[228px] border [--anchor-gap:4px]","bg-tremor-background border-tremor-border divide-tremor-border shadow-tremor-dropdown","dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border dark:shadow-dark-tremor-dropdown")},r.default.createElement("div",{className:(0,u.tremorTwMerge)("flex items-center w-full px-2.5","bg-tremor-background-muted","dark:bg-dark-tremor-background-muted")},r.default.createElement("span",null,r.default.createElement(l,{className:(0,u.tremorTwMerge)("flex-none w-4 h-4 mr-2","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})),r.default.createElement("input",{name:"search",type:"input",autoComplete:"off",placeholder:w,className:(0,u.tremorTwMerge)("w-full focus:outline-none focus:ring-none bg-transparent text-tremor-default py-2","text-tremor-content-emphasis","dark:text-dark-tremor-content-subtle"),onKeyDown:e=>{"Space"===e.code&&""!==e.target.value&&e.stopPropagation()},onChange:e=>I(e.target.value),value:O})),r.default.createElement(n.default.Provider,Object.assign({},{onBlur:{handleResetSearch:H}},{value:{selectedValue:e}}),W)))))),E&&S?r.default.createElement("p",{className:(0,u.tremorTwMerge)("errorMessage","text-sm text-rose-500 mt-1")},S):null)});p.displayName="MultiSelect",e.s(["MultiSelect",0,p],621642);let b=(0,d.makeClassName)("MultiSelectItem"),v=r.default.forwardRef((e,a)=>{let{value:o,className:l,children:i}=e,s=(0,t.__rest)(e,["value","className","children"]),{selectedValue:c}=(0,r.useContext)(n.default),f=(0,d.isValueInArray)(o,c);return r.default.createElement(m.ListboxOption,Object.assign({className:(0,u.tremorTwMerge)(b("root"),"flex justify-start items-center cursor-default text-tremor-default p-2.5","data-[focus]:bg-tremor-background-muted data-[focus]:text-tremor-content-strong data-[select]ed:text-tremor-content-strong text-tremor-content-emphasis","dark:data-[focus]:bg-dark-tremor-background-muted dark:data-[focus]:text-dark-tremor-content-strong dark:data-[select]ed:text-dark-tremor-content-strong dark:data-[select]ed:bg-dark-tremor-background-muted dark:text-dark-tremor-content-emphasis",l),ref:a,key:o,value:o},s),r.default.createElement("input",{type:"checkbox",className:(0,u.tremorTwMerge)(b("checkbox"),"flex-none focus:ring-none focus:outline-none cursor-pointer mr-2.5","accent-tremor-brand","dark:accent-dark-tremor-brand"),checked:f,readOnly:!0}),r.default.createElement("span",{className:"whitespace-nowrap truncate"},null!=i?i:o))});v.displayName="MultiSelectItem",e.s(["MultiSelectItem",0,v],25080)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0u55zmkgol9ci.js b/litellm/proxy/_experimental/out/_next/static/chunks/0u55zmkgol9ci.js new file mode 100644 index 00000000000..14304d140bc --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0u55zmkgol9ci.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,94629,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,r],94629)},728889,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(829087),l=e.i(480731),o=e.i(444755),s=e.i(673706),i=e.i(95779);let n={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},c={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},u=(0,s.makeClassName)("Icon"),m=r.default.forwardRef((e,m)=>{let{icon:g,variant:h="simple",tooltip:x,size:p=l.Sizes.SM,color:f,className:b}=e,v=(0,t.__rest)(e,["icon","variant","tooltip","size","color","className"]),C=((e,t)=>{switch(e){case"simple":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,s.getColorClassNames)(t,i.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,s.getColorClassNames)(t,i.colorPalette.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,o.tremorTwMerge)((0,s.getColorClassNames)(t,i.colorPalette.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}})(h,f),{tooltipProps:w,getReferenceProps:y}=(0,a.useTooltip)();return r.default.createElement("span",Object.assign({ref:(0,s.mergeRefs)([m,w.refs.setReference]),className:(0,o.tremorTwMerge)(u("root"),"inline-flex shrink-0 items-center justify-center",C.bgColor,C.textColor,C.borderColor,C.ringColor,c[h].rounded,c[h].border,c[h].shadow,c[h].ring,n[p].paddingX,n[p].paddingY,b)},y,v),r.default.createElement(a.default,Object.assign({text:x},w)),r.default.createElement(g,{className:(0,o.tremorTwMerge)(u("icon"),"shrink-0",d[p].height,d[p].width)}))});m.displayName="Icon",e.s(["default",0,m],728889)},752978,e=>{"use strict";var t=e.i(728889);e.s(["Icon",()=>t.default])},278587,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))});e.s(["RefreshIcon",0,r],278587)},166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},447566,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var l=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(l.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["ArrowLeftOutlined",0,o],447566)},502547,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"}))});e.s(["ChevronRightIcon",0,r],502547)},211576,e=>{"use strict";var t=e.i(131757);e.s(["Col",()=>t.default])},178654,e=>{"use strict";let t=e.i(211576).Col;e.s(["Col",0,t],178654)},621192,e=>{"use strict";let t=e.i(281256).Row;e.s(["Row",0,t],621192)},263147,e=>{"use strict";var t=e.i(266027),r=e.i(243652),a=e.i(602869),l=e.i(431703),o=e.i(708347),s=e.i(135214);let i=(0,r.createQueryKeys)("accessGroups"),n=async e=>{let t=(0,a.getProxyBaseUrl)(),r=`${t}/v1/access_group`,o=await fetch(r,{method:"GET",headers:{[(0,a.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!o.ok){let e=await o.json(),t=(0,l.deriveErrorMessage)(e);throw(0,a.handleError)(t),Error(t)}return o.json()};e.s(["accessGroupKeys",0,i,"useAccessGroups",0,()=>{let{accessToken:e,userRole:r}=(0,s.default)();return(0,t.useQuery)({queryKey:i.list({}),queryFn:async()=>n(e),enabled:!!e&&o.all_admin_roles.includes(r||"")})}])},304911,e=>{"use strict";var t=e.i(843476),r=e.i(262218);let{Text:a}=e.i(898586).Typography;e.s(["default",0,function({userId:e}){return"default_user_id"===e?(0,t.jsx)(r.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,t.jsx)(a,{children:e})}])},637235,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"}}]},name:"clock-circle",theme:"outlined"};var l=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(l.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["ClockCircleOutlined",0,o],637235)},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var l=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(l.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["default",0,o],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let l=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],o=e=>({_s:e,status:l[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),s=e=>e?6:5,i=(e,t,r,a,l)=>{clearTimeout(a.current);let s=o(e);t(s),r.current=s,l&&l({current:s})};var n=e.i(480731),d=e.i(444755),c=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},h=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},x=(0,c.makeClassName)("Button"),p=({loading:e,iconSize:t,iconPosition:r,Icon:l,needMargin:o,transitionStatus:s})=>{let i=o?r===n.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?a.default.createElement(u,{className:(0,d.tremorTwMerge)(x("icon"),"animate-spin shrink-0",i,m.default,m[s]),style:{transition:"width 150ms"}}):a.default.createElement(l,{className:(0,d.tremorTwMerge)(x("icon"),"shrink-0",t,i)})},f=a.default.forwardRef((e,l)=>{let{icon:u,iconPosition:m=n.HorizontalPositions.Left,size:f=n.Sizes.SM,color:b,variant:v="primary",disabled:C,loading:w=!1,loadingText:y,children:k,tooltip:j,className:S}=e,N=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),_=w||C,z=void 0!==u||w,T=w&&y,I=!(!k&&!T),P=(0,d.tremorTwMerge)(g[f].height,g[f].width),B="light"!==v?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",D=h(v,b),M=("light"!==v?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[f],{tooltipProps:E,getReferenceProps:R}=(0,r.useTooltip)(300),[L,A]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:l,timeout:n,initialEntered:d,mountOnEnter:c,unmountOnExit:u,onStateChange:m}={})=>{let[g,h]=(0,a.useState)(()=>o(d?2:s(c))),x=(0,a.useRef)(g),p=(0,a.useRef)(0),[f,b]="object"==typeof n?[n.enter,n.exit]:[n,n],v=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return s(t)}})(x.current._s,u);e&&i(e,h,x,p,m)},[m,u]);return[g,(0,a.useCallback)(a=>{let o=e=>{switch(i(e,h,x,p,m),e){case 1:f>=0&&(p.current=((...e)=>setTimeout(...e))(v,f));break;case 4:b>=0&&(p.current=((...e)=>setTimeout(...e))(v,b));break;case 0:case 3:p.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||o(e+1)},0)}},n=x.current.isEnter;"boolean"!=typeof a&&(a=!n),a?n||o(e?+!r:2):n&&o(t?l?3:4:s(u))},[v,m,e,t,r,l,f,b,u]),v]})({timeout:50});return(0,a.useEffect)(()=>{A(w)},[w]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([l,E.refs.setReference]),className:(0,d.tremorTwMerge)(x("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",B,M.paddingX,M.paddingY,M.fontSize,D.textColor,D.bgColor,D.borderColor,D.hoverBorderColor,_?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(h(v,b).hoverTextColor,h(v,b).hoverBgColor,h(v,b).hoverBorderColor),S),disabled:_},R,N),a.default.createElement(r.default,Object.assign({text:j},E)),z&&m!==n.HorizontalPositions.Right?a.default.createElement(p,{loading:w,iconSize:P,iconPosition:m,Icon:u,transitionStatus:L.status,needMargin:I}):null,T||k?a.default.createElement("span",{className:(0,d.tremorTwMerge)(x("text"),"text-tremor-default whitespace-nowrap")},T?y:k):null,z&&m===n.HorizontalPositions.Right?a.default.createElement(p,{loading:w,iconSize:P,iconPosition:m,Icon:u,transitionStatus:L.status,needMargin:I}):null)});f.displayName="Button",e.s(["Button",0,f],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),l=e.i(271645);let o=l.default.forwardRef((e,o)=>{let{color:s,className:i,children:n}=e;return l.default.createElement("p",{ref:o,className:(0,r.tremorTwMerge)("text-tremor-default",s?(0,a.getColorClassNames)(s,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},n)});o.displayName="Text",e.s(["default",0,o],936325),e.s(["Text",0,o],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),l=e.i(95779),o=e.i(444755),s=e.i(673706);let i=(0,s.makeClassName)("Card"),n=r.default.forwardRef((e,n)=>{let{decoration:d="",decorationColor:c,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:n,className:(0,o.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,s.getColorClassNames)(c,l.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},g),u)});n.displayName="Card",e.s(["Card",0,n],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),a=e.i(444755),l=e.i(673706),o=e.i(271645);let s=o.default.forwardRef((e,s)=>{let{color:i,children:n,className:d}=e,c=(0,t.__rest)(e,["color","children","className"]);return o.default.createElement("p",Object.assign({ref:s,className:(0,a.tremorTwMerge)("font-medium text-tremor-title",i?(0,l.getColorClassNames)(i,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),n)});s.displayName="Title",e.s(["Title",0,s],629569)},891547,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(199133),l=e.i(602869);e.s(["default",0,({onChange:e,value:o,className:s,accessToken:i,disabled:n})=>{let[d,c]=(0,r.useState)([]),[u,m]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(i){m(!0);try{let e=await (0,l.getGuardrailsList)(i);e.guardrails&&c(e.guardrails)}catch(e){console.error("Error fetching guardrails:",e)}finally{m(!1)}}})()},[i]),(0,t.jsx)("div",{children:(0,t.jsx)(a.Select,{mode:"multiple",disabled:n,placeholder:n?"Setting guardrails is a premium feature.":"Select guardrails",onChange:t=>{e(t)},value:o,loading:u,className:s,allowClear:!0,options:d.map(e=>({label:`${e.guardrail_name}`,value:e.guardrail_name})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"}})})}])},921511,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(199133),l=e.i(602869);function o(e){return e.filter(e=>(e.version_status??"draft")!=="draft").map(e=>{var t;let r=e.version_number??1,a=e.version_status??"draft";return{label:`${e.policy_name} — v${r} (${a})${e.description?` — ${e.description}`:""}`,value:"production"===a?e.policy_name:e.policy_id?(t=e.policy_id,`policy_${t}`):e.policy_name}})}e.s(["default",0,({onChange:e,value:s,className:i,accessToken:n,disabled:d,onPoliciesLoaded:c})=>{let[u,m]=(0,r.useState)([]),[g,h]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(n){h(!0);try{let e=await (0,l.getPoliciesList)(n);e.policies&&(m(e.policies),c?.(e.policies))}catch(e){console.error("Error fetching policies:",e)}finally{h(!1)}}})()},[n,c]),(0,t.jsx)("div",{children:(0,t.jsx)(a.Select,{mode:"multiple",disabled:d,placeholder:d?"Setting policies is a premium feature.":"Select policies (production or published versions)",onChange:t=>{e(t)},value:s,loading:g,className:i,allowClear:!0,options:o(u),optionFilterProp:"label",showSearch:!0,style:{width:"100%"}})})},"getPolicyOptionEntries",0,o])},595727,234662,677241,281092,688594,e=>{"use strict";let t=Symbol.for("constructDateFrom");function r(e,r){return"function"==typeof e?e(r):e&&"object"==typeof e&&t in e?e[t](r):e instanceof Date?new e.constructor(r):new Date(r)}function a(e,t){return r(t||e,e)}e.s(["constructFromSymbol",0,t,"millisecondsInDay",0,864e5,"millisecondsInHour",0,36e5,"millisecondsInMinute",0,6e4,"millisecondsInSecond",0,1e3,"millisecondsInWeek",0,6048e5],234662),e.s(["constructFrom",0,r],677241),e.s(["toDate",0,a],281092),e.s(["addDays",0,function(e,t,l){let o=a(e,l?.in);return isNaN(t)?r(l?.in||e,NaN):(t&&o.setDate(o.getDate()+t),o)}],595727),e.s(["addMonths",0,function(e,t,l){let o=a(e,l?.in);if(isNaN(t))return r(l?.in||e,NaN);if(!t)return o;let s=o.getDate(),i=r(l?.in||e,o.getTime());return(i.setMonth(o.getMonth()+t+1,0),s>=i.getDate())?i:(o.setFullYear(i.getFullYear(),i.getMonth(),s),o)}],688594)},24529,e=>{"use strict";var t=e.i(595727),r=e.i(688594),a=e.i(677241),l=e.i(281092);function o(e,o,s){let{years:i=0,months:n=0,weeks:d=0,days:c=0,hours:u=0,minutes:m=0,seconds:g=0}=o,h=(0,l.toDate)(e,s?.in),x=n||i?(0,r.addMonths)(h,n+12*i):h,p=c||d?(0,t.addDays)(x,c+7*d):x;return(0,a.constructFrom)(s?.in||e,+p+1e3*(g+60*(m+60*u)))}let s=/[zZ]$|[+-]\d{2}:?\d{2}$/;function i(e){return Date.parse(s.test(e)?e:`${e}Z`)}e.s(["calculateExpiryPreviewFromDuration",0,function(e){if(!e)return null;try{let t,r=parseInt(e);if(Number.isNaN(r))throw Error("Invalid duration format");let a=new Date;if(e.endsWith("mo"))t=o(a,{months:r});else if(e.endsWith("s"))t=o(a,{seconds:r});else if(e.endsWith("m"))t=o(a,{minutes:r});else if(e.endsWith("h"))t=o(a,{hours:r});else if(e.endsWith("d"))t=o(a,{days:r});else if(e.endsWith("w"))t=o(a,{weeks:r});else throw Error("Invalid duration format");return t.toLocaleString()}catch{return null}},"formatExpiresUtc",0,function(e){let t=i(e);return Number.isNaN(t)?e:new Date(t).toLocaleString()},"isKeyExpired",0,function(e){if(!e)return!1;let t=i(e);return!Number.isNaN(t)&&t{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M848 359.3H627.7L825.8 109c4.1-5.3.4-13-6.3-13H436c-2.8 0-5.5 1.5-6.9 4L170 547.5c-3.1 5.3.7 12 6.9 12h174.4l-89.4 357.6c-1.9 7.8 7.5 13.3 13.3 7.7L853.5 373c5.2-4.9 1.7-13.7-5.5-13.7zM378.2 732.5l60.3-241H281.1l189.6-327.4h224.6L487 427.4h211L378.2 732.5z"}}]},name:"thunderbolt",theme:"outlined"};var l=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(l.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["ThunderboltOutlined",0,o],962944)},72713,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"}}]},name:"calendar",theme:"outlined"};var l=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(l.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["CalendarOutlined",0,o],72713)},140928,e=>{"use strict";var t=e.i(271645),r=e.i(152473);e.s(["useDebouncedValue",0,function(e,a){let[l,o,s]=(0,r.useDebouncedState)(e,a);return(0,t.useEffect)(()=>(o(e),()=>{s.cancel()}),[e,o,s]),[l,s]}])},969550,e=>{"use strict";var t=e.i(843476),r=e.i(271645);let a=r.forwardRef(function(e,t){return r.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),r.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"}))});var l=e.i(464571),o=e.i(311451),s=e.i(199133),i=e.i(374009);e.s(["default",0,({options:e,onApplyFilters:n,onResetFilters:d,initialValues:c={},buttonLabel:u="Filters"})=>{let[m,g]=(0,r.useState)(!1),[h,x]=(0,r.useState)(c),[p,f]=(0,r.useState)({}),[b,v]=(0,r.useState)({}),[C,w]=(0,r.useState)({}),[y,k]=(0,r.useState)({}),j=(0,r.useCallback)((0,i.default)(async(e,t)=>{if(t.isSearchable&&t.searchFn){v(e=>({...e,[t.name]:!0}));try{let r=await t.searchFn(e);f(e=>({...e,[t.name]:r}))}catch(e){console.error("Error searching:",e),f(e=>({...e,[t.name]:[]}))}finally{v(e=>({...e,[t.name]:!1}))}}},300),[]),S=(0,r.useCallback)(async e=>{if(e.isSearchable&&e.searchFn&&!e.loading&&!y[e.name]){v(t=>({...t,[e.name]:!0})),k(t=>({...t,[e.name]:!0}));try{let t=await e.searchFn("");f(r=>({...r,[e.name]:t}))}catch(t){console.error("Error loading initial options:",t),f(t=>({...t,[e.name]:[]}))}finally{v(t=>({...t,[e.name]:!1}))}}},[y]);(0,r.useEffect)(()=>{m&&e.forEach(e=>{e.isSearchable&&!y[e.name]&&S(e)})},[m,e,S,y]);let N=(e,t)=>{let r={...h,[e]:t};x(r),n(r)};return(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-6",children:[(0,t.jsx)(l.Button,{icon:(0,t.jsx)(a,{className:"h-4 w-4"}),onClick:()=>g(!m),className:"flex items-center gap-2",children:u}),(0,t.jsx)(l.Button,{onClick:()=>{let t={};e.forEach(e=>{t[e.name]=""}),x(t),d()},children:"Reset Filters"})]}),m&&(0,t.jsx)("div",{className:"grid grid-cols-3 gap-x-6 gap-y-4 mb-6",children:e.map(e=>{let r,a=b[e.name]||e.loading;return(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsx)("label",{className:"text-sm text-gray-600",children:e.label||e.name}),e.isSearchable?(0,t.jsx)(s.Select,{showSearch:!0,className:"w-full",placeholder:`Search ${e.label||e.name}...`,value:h[e.name]||void 0,onChange:t=>N(e.name,t),onOpenChange:t=>{t&&e.isSearchable&&!y[e.name]&&S(e)},onSearch:t=>{w(r=>({...r,[e.name]:t})),e.searchFn&&j(t,e)},filterOption:!1,loading:a,options:p[e.name]||[],allowClear:!0,notFoundContent:a?"Loading...":"No results found"}):e.options?(0,t.jsx)(s.Select,{className:"w-full",placeholder:`Select ${e.label||e.name}...`,value:h[e.name]||void 0,onChange:t=>N(e.name,t),allowClear:!0,children:e.options.map(e=>(0,t.jsx)(s.Select.Option,{value:e.value,children:e.label},e.value))}):e.customComponent?(r=e.customComponent,(0,t.jsx)(r,{value:h[e.name]||void 0,onChange:t=>N(e.name,t??""),placeholder:`Select ${e.label||e.name}...`,allFilters:h})):(0,t.jsx)(o.Input,{className:"w-full",placeholder:`Enter ${e.label||e.name}...`,value:h[e.name]||"",onChange:t=>N(e.name,t.target.value),allowClear:!0})]},e.name)})})]})}],969550)},50882,e=>{"use strict";var t=e.i(843476),r=e.i(621482),a=e.i(243652),l=e.i(602869),o=e.i(135214);let s=(0,a.createQueryKeys)("infiniteKeyAliases");var i=e.i(56456),n=e.i(152473),d=e.i(199133),c=e.i(271645);e.s(["PaginatedKeyAliasSelect",0,({value:e,onChange:a,placeholder:u="Select a key alias",style:m,pageSize:g=50,allowClear:h=!0,disabled:x=!1,allFilters:p})=>{let[f,b]=(0,c.useState)(""),[v,C]=(0,n.useDebouncedState)("",{wait:300}),{data:w,fetchNextPage:y,hasNextPage:k,isFetchingNextPage:j,isLoading:S}=((e=50,t,a)=>{let{accessToken:i}=(0,o.default)();return(0,r.useInfiniteQuery)({queryKey:s.list({filters:{size:e,...t&&{search:t},...a&&{team_id:a}}}),queryFn:async({pageParam:r})=>await (0,l.keyAliasesCall)(i,r,e,t,a),initialPageParam:1,getNextPageParam:e=>{if(e.current_page{if(!w?.pages)return[];let e=new Set,t=[];for(let r of w.pages)for(let a of r.aliases)!a||e.has(a)||(e.add(a),t.push({label:a,value:a}));return t},[w]);return(0,t.jsx)(d.Select,{value:e||void 0,onChange:e=>{a?.(e??"")},placeholder:u,style:{width:"100%",...m},allowClear:h,disabled:x,showSearch:!0,filterOption:!1,onSearch:e=>{b(e),C(e)},searchValue:f,onPopupScroll:e=>{let t=e.currentTarget;(t.scrollTop+t.clientHeight)/t.scrollHeight>=.8&&k&&!j&&y()},loading:S,notFoundContent:S?(0,t.jsx)(i.LoadingOutlined,{spin:!0}):"No key aliases found",options:N,popupRender:e=>(0,t.jsxs)(t.Fragment,{children:[e,j&&(0,t.jsx)("div",{style:{textAlign:"center",padding:8},children:(0,t.jsx)(i.LoadingOutlined,{spin:!0})})]})})}],50882)},502501,e=>{"use strict";var t=e.i(843476),r=e.i(785242),a=e.i(135214),l=e.i(268004),o=e.i(309426),s=e.i(350967),i=e.i(947293),n=e.i(271645),d=e.i(602869);let c=async(e,t,r,a,l)=>{l("Admin"!=r&&"Admin Viewer"!=r?await (0,d.teamListCall)(e,a?.organization_id||null,t):await (0,d.teamListCall)(e,a?.organization_id||null))};var u=e.i(702597),m=e.i(207082),g=e.i(109799),h=e.i(140928),x=e.i(500330),p=e.i(871943),f=e.i(502547),b=e.i(360820),v=e.i(94629),C=e.i(152990),w=e.i(682830),y=e.i(389083),k=e.i(752978),j=e.i(269200),S=e.i(942232),N=e.i(977572),_=e.i(427612),z=e.i(64848),T=e.i(496020),I=e.i(599724),P=e.i(827252),B=e.i(772345),D=e.i(464571),M=e.i(282786),E=e.i(981339),R=e.i(898586);e.i(622826);var L=e.i(200208),A=e.i(399536),O=e.i(964471),K=e.i(112179),H=e.i(355619),V=e.i(50882),U=e.i(969550),$=e.i(304911),F=e.i(20147);let Y={"Team ID":"","Organization ID":"","Key Alias":"","User ID":"","Key Hash":""};function W(){let{data:e,isLoading:a}=(0,g.useOrganizations)(),l=(0,n.useMemo)(()=>e??[],[e]),[o,s]=(0,n.useState)(null),[i,d]=n.default.useState([{id:"created_at",desc:!0}]),[c,u]=n.default.useState({pageIndex:0,pageSize:50}),[W,X]=(0,n.useState)(Y),[G]=(0,h.useDebouncedValue)(W,{wait:300}),q=i.length>0?i[0].id:null,Q=i.length>0?i[0].desc?"desc":"asc":null,{data:J,isPending:Z,isFetching:ee,isError:et,refetch:er}=(0,m.useKeys)(c.pageIndex+1,c.pageSize,{...{teamID:G["Team ID"].trim()||void 0,organizationID:G["Organization ID"].trim()||void 0,selectedKeyAlias:G["Key Alias"].trim()||void 0,userID:G["User ID"].trim()||void 0,keyHash:G["Key Hash"].trim()||void 0},sortBy:q||void 0,sortOrder:Q||void 0,expand:"user"}),[ea,el]=(0,n.useState)({}),eo=(0,n.useMemo)(()=>J?.keys??[],[J]),{data:es,isLoading:ei}=(0,r.useAllTeams)(),en=(0,n.useMemo)(()=>es??[],[es]),ed=(0,n.useDeferredValue)(ee),ec=(ee||ed)&&!et,eu=J?.total_count??0,em=(0,n.useMemo)(()=>[{id:"expander",header:()=>null,size:40,enableSorting:!1,cell:({row:e})=>e.getCanExpand()?(0,t.jsx)("button",{onClick:e.getToggleExpandedHandler(),style:{cursor:"pointer"},children:e.getIsExpanded()?"▼":"▶"}):null},{id:"token",accessorKey:"token",header:"Key ID",size:100,enableSorting:!0,cell:e=>(0,t.jsx)(A.IdCell,{value:e.getValue(),onClick:()=>s(e.row.original)})},{id:"key_alias",accessorKey:"key_alias",header:"Key Alias",size:150,enableSorting:!0,cell:e=>{let r=e.getValue(),a=e.cell.column.getSize();return(0,t.jsx)("span",{className:"font-mono text-xs truncate block",style:{maxWidth:a,overflow:"hidden"},children:r??"-"})}},{id:"status",header:"Status",size:100,enableSorting:!1,cell:({row:e})=>{let r=e.original;if(!0!==r.blocked)return(0,t.jsx)(K.StatusBadge,{tone:"success",label:"Active",dataTestId:`key-status-${r.token_id}`});let a=r.metadata?.scim_blocked===!0;return(0,t.jsx)(K.StatusBadge,{tone:"error",label:"Blocked",tooltip:a?"Blocked by SCIM (external identity provider deactivated or deleted the owning user).":"Blocked. Requests using this key will be rejected with 401.",dataTestId:`key-status-${r.token_id}`})}},{id:"key_name",accessorKey:"key_name",header:"Secret Key",size:120,enableSorting:!1,cell:e=>(0,t.jsx)("span",{className:"font-mono text-xs",children:e.getValue()})},{id:"team_alias",accessorKey:"team_id",header:"Team",size:120,enableSorting:!1,cell:e=>{let r=e.getValue();if(!r)return"-";let a=en.find(e=>e.team_id===r),l=a?.team_alias||r,o=e.cell.column.getSize();return(0,t.jsx)("span",{className:"font-mono text-xs truncate block",style:{maxWidth:o,overflow:"hidden"},children:l})}},{id:"organization_alias",accessorKey:"org_id",header:"Organization",size:140,enableSorting:!1,cell:e=>{let r=e.getValue();if(!r)return"-";let a=l.find(e=>e.organization_id===r),o=a?.organization_alias||r,s=e.cell.column.getSize();return(0,t.jsx)("span",{className:"font-mono text-xs truncate block",style:{maxWidth:s,overflow:"hidden"},children:o})}},{id:"user",accessorKey:"user",header:()=>(0,t.jsxs)("span",{className:"flex items-center gap-1",children:["User",(0,t.jsx)(M.Popover,{content:"Displays the first available value: User Alias, User Email, or User ID.",trigger:"hover",children:(0,t.jsx)(P.InfoCircleOutlined,{className:"text-gray-400 text-xs cursor-help"})})]}),size:160,enableSorting:!1,cell:({row:e})=>{let r=e.original,a=r.user?.user_alias??null,l=r.user?.user_email??r.user_email??null,o=r.user_id??null,s="default_user_id"===o,i=a||l||o,n=(0,t.jsx)("div",{className:"flex flex-col gap-2 text-xs min-w-[200px] max-w-[300px]",children:[{label:"User Alias",value:a},{label:"User Email",value:l},{label:"User ID",value:o}].map(({label:e,value:r})=>(0,t.jsxs)("div",{className:"flex flex-col min-w-0",children:[(0,t.jsx)("span",{className:"text-gray-400",children:e}),r?(0,t.jsx)(R.Typography.Text,{className:"font-mono text-xs",ellipsis:{tooltip:r},copyable:!0,children:r}):(0,t.jsx)("span",{className:"font-mono",children:"-"})]},e))});return!s||a||l?(0,t.jsx)(M.Popover,{content:n,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"font-mono text-xs truncate block cursor-default",style:{maxWidth:160,overflow:"hidden"},children:i||"-"})}):(0,t.jsx)(M.Popover,{content:n,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"cursor-default",children:(0,t.jsx)($.default,{userId:o})})})}},{id:"created_at",accessorKey:"created_at",header:"Created At",size:120,enableSorting:!0,cell:e=>(0,t.jsx)(L.DateCell,{value:e.getValue(),precision:"date"})},{id:"created_by",accessorKey:"created_by",header:"Created By",size:160,enableSorting:!1,cell:e=>{let r=e.getValue();if(!r)return"-";let a=e.row.original.created_by_user,l=a?.user_alias??null,o=a?.user_email??null,s="default_user_id"===r,i=l||o||r,n=(0,t.jsx)("div",{className:"flex flex-col gap-2 text-xs min-w-[200px] max-w-[300px]",children:[{label:"User Alias",value:l},{label:"User Email",value:o},{label:"User ID",value:r}].map(({label:e,value:r})=>(0,t.jsxs)("div",{className:"flex flex-col min-w-0",children:[(0,t.jsx)("span",{className:"text-gray-400",children:e}),r?(0,t.jsx)(R.Typography.Text,{className:"font-mono text-xs",ellipsis:{tooltip:r},copyable:!0,children:r}):(0,t.jsx)("span",{className:"font-mono",children:"-"})]},e))});return!s||l||o?(0,t.jsx)(M.Popover,{content:n,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"font-mono text-xs truncate block cursor-default",style:{maxWidth:160,overflow:"hidden"},children:i})}):(0,t.jsx)(M.Popover,{content:n,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"cursor-default",children:(0,t.jsx)($.default,{userId:r})})})}},{id:"updated_at",accessorKey:"updated_at",header:"Updated At",size:120,enableSorting:!0,cell:e=>(0,t.jsx)(L.DateCell,{value:e.getValue(),precision:"date",fallback:"Never"})},{id:"last_active",accessorKey:"last_active",header:()=>(0,t.jsxs)("span",{className:"flex items-center gap-1",children:["Last Active",(0,t.jsx)(M.Popover,{content:"This is a new field and is not backfilled. Only new key usage will update this value.",trigger:"hover",children:(0,t.jsx)(P.InfoCircleOutlined,{className:"text-gray-400 text-xs cursor-help"})})]}),size:130,enableSorting:!1,cell:e=>(0,t.jsx)(L.DateCell,{value:e.getValue(),precision:"date",fallback:"Unknown"})},{id:"expires",accessorKey:"expires",header:"Expires",size:120,enableSorting:!1,cell:e=>(0,t.jsx)(L.DateCell,{value:e.getValue(),precision:"date",fallback:"Never"})},{id:"spend",accessorKey:"spend",header:"Spend (USD)",size:100,enableSorting:!0,cell:e=>(0,t.jsx)(O.MoneyCell,{value:e.getValue(),decimals:4})},{id:"max_budget",accessorKey:"max_budget",header:"Budget (USD)",size:110,enableSorting:!0,cell:e=>{let t=e.getValue();if(null!==t)return`$${(0,x.formatNumberWithCommas)(t)}`;let r=e.row.original.team_id,a=en.find(e=>e.team_id===r);return a?.max_budget!=null?`$${(0,x.formatNumberWithCommas)(a.max_budget)} (Team)`:"Unlimited"}},{id:"budget_reset_at",accessorKey:"budget_reset_at",header:"Budget Reset",size:130,enableSorting:!1,cell:e=>(0,t.jsx)(L.DateCell,{value:e.getValue(),fallback:"Never"})},{id:"models",accessorKey:"models",header:"Models",size:200,enableSorting:!1,cell:e=>{let r=e.getValue();return(0,t.jsx)("div",{className:"flex flex-col py-2",children:Array.isArray(r)?(0,t.jsx)("div",{className:"flex flex-col",children:0===r.length?(0,t.jsx)(y.Badge,{size:"xs",className:"mb-1",color:"red",children:(0,t.jsx)(I.Text,{children:"All Proxy Models"})}):(0,t.jsx)(t.Fragment,{children:(0,t.jsxs)("div",{className:"flex items-start",children:[r.length>3&&(0,t.jsx)("div",{children:(0,t.jsx)(k.Icon,{icon:ea[e.row.id]?p.ChevronDownIcon:f.ChevronRightIcon,className:"cursor-pointer",size:"xs",onClick:()=>{el(t=>({...t,[e.row.id]:!t[e.row.id]}))}})}),(0,t.jsxs)("div",{className:"flex flex-wrap gap-1",children:[r.slice(0,3).map((e,r)=>"all-proxy-models"===e?(0,t.jsx)(y.Badge,{size:"xs",color:"red",children:(0,t.jsx)(I.Text,{children:"All Proxy Models"})},r):(0,t.jsx)(y.Badge,{size:"xs",color:"blue",children:(0,t.jsx)(I.Text,{children:e.length>30?`${(0,H.getModelDisplayName)(e).slice(0,30)}...`:(0,H.getModelDisplayName)(e)})},r)),r.length>3&&!ea[e.row.id]&&(0,t.jsx)(y.Badge,{size:"xs",color:"gray",className:"cursor-pointer",children:(0,t.jsxs)(I.Text,{children:["+",r.length-3," ",r.length-3==1?"more model":"more models"]})}),ea[e.row.id]&&(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:r.slice(3).map((e,r)=>"all-proxy-models"===e?(0,t.jsx)(y.Badge,{size:"xs",color:"red",children:(0,t.jsx)(I.Text,{children:"All Proxy Models"})},r+3):(0,t.jsx)(y.Badge,{size:"xs",color:"blue",children:(0,t.jsx)(I.Text,{children:e.length>30?`${(0,H.getModelDisplayName)(e).slice(0,30)}...`:(0,H.getModelDisplayName)(e)})},r+3))})]})]})})}):null})}},{id:"rate_limits",header:"Rate Limits",size:140,enableSorting:!1,cell:({row:e})=>{let r=e.original;return(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{children:["TPM: ",null!==r.tpm_limit?r.tpm_limit:"Unlimited"]}),(0,t.jsxs)("div",{children:["RPM: ",null!==r.rpm_limit?r.rpm_limit:"Unlimited"]})]})}}],[en,l]),eg=[{name:"Team ID",label:"Team ID",isSearchable:!0,loading:ei,searchFn:async e=>en&&0!==en.length?en.filter(t=>t.team_id.toLowerCase().includes(e.toLowerCase())||t.team_alias&&t.team_alias.toLowerCase().includes(e.toLowerCase())).map(e=>({label:`${e.team_alias||e.team_id} (${e.team_id})`,value:e.team_id})):[]},{name:"Organization ID",label:"Organization ID",isSearchable:!0,loading:a,searchFn:async e=>l&&0!==l.length?l.filter(t=>t.organization_id?.toLowerCase().includes(e.toLowerCase())??!1).filter(e=>null!==e.organization_id&&void 0!==e.organization_id).map(e=>({label:`${e.organization_id||"Unknown"} (${e.organization_id})`,value:e.organization_id})):[]},{name:"Key Alias",label:"Key Alias",customComponent:V.PaginatedKeyAliasSelect},{name:"User ID",label:"User ID",isSearchable:!1},{name:"Key Hash",label:"Key ID",isSearchable:!1}],eh=(0,C.useReactTable)({data:eo,columns:em.filter(e=>"expander"!==e.id),columnResizeMode:"onChange",columnResizeDirection:"ltr",state:{sorting:i,pagination:c},onSortingChange:e=>{d("function"==typeof e?e(i):e),u(e=>({...e,pageIndex:0}))},onPaginationChange:u,getCoreRowModel:(0,w.getCoreRowModel)(),enableSorting:!0,manualSorting:!0,manualPagination:!0,pageCount:Math.ceil(eu/c.pageSize)}),{pageIndex:ex,pageSize:ep}=eh.getState().pagination,ef=Math.min((ex+1)*ep,eu),eb=`${ex*ep+1} - ${ef}`;return(0,t.jsx)("div",{className:"w-full h-full overflow-hidden",children:o?(0,t.jsx)(F.default,{keyId:o.token,onClose:()=>s(null),keyData:o,teams:en}):(0,t.jsxs)("div",{className:"border-b py-4 flex-1 overflow-hidden",children:[(0,t.jsx)("div",{className:"w-full mb-6",children:(0,t.jsx)(U.default,{options:eg,onApplyFilters:e=>{X({"Team ID":e["Team ID"]||"","Organization ID":e["Organization ID"]||"","Key Alias":e["Key Alias"]||"","User ID":e["User ID"]||"","Key Hash":e["Key Hash"]||""}),u(e=>({...e,pageIndex:0}))},initialValues:W,onResetFilters:()=>{X(Y),u(e=>({...e,pageIndex:0}))}})}),(0,t.jsxs)("div",{className:"flex items-center justify-between w-full mb-4",children:[(0,t.jsxs)("div",{className:"inline-flex items-center gap-2",children:[Z?(0,t.jsx)(E.Skeleton.Node,{active:!0,style:{width:200,height:20}}):(0,t.jsxs)("span",{className:"inline-flex text-sm text-gray-700",children:["Showing ",eb," of ",eu," results"]}),(0,t.jsx)(D.Button,{type:"default",icon:(0,t.jsx)(B.SyncOutlined,{spin:ec}),onClick:()=>{er()},disabled:ec,title:"Fetch data",children:ec?"Fetching":"Fetch"})]}),(0,t.jsxs)("div",{className:"inline-flex items-center gap-2",children:[Z?(0,t.jsx)(E.Skeleton.Node,{active:!0,style:{width:74,height:20}}):(0,t.jsxs)("span",{className:"text-sm text-gray-700",children:["Page ",ex+1," of ",eh.getPageCount()]}),Z?(0,t.jsx)(E.Skeleton.Button,{active:!0,size:"small",style:{width:84,height:30}}):(0,t.jsx)("button",{onClick:()=>eh.previousPage(),disabled:Z||!eh.getCanPreviousPage(),className:"px-3 py-1 text-sm border rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed",children:"Previous"}),Z?(0,t.jsx)(E.Skeleton.Button,{active:!0,size:"small",style:{width:58,height:30}}):(0,t.jsx)("button",{onClick:()=>eh.nextPage(),disabled:Z||!eh.getCanNextPage(),className:"px-3 py-1 text-sm border rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed",children:"Next"})]})]}),(0,t.jsx)("div",{className:"h-[75vh] overflow-auto",children:(0,t.jsx)("div",{className:"rounded-lg custom-border relative",children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(j.Table,{className:"[&_td]:py-0.5 [&_th]:py-1",style:{width:eh.getCenterTotalSize()},children:[(0,t.jsx)(_.TableHead,{children:eh.getHeaderGroups().map(e=>(0,t.jsx)(T.TableRow,{children:e.headers.map(e=>(0,t.jsx)(z.TableHeaderCell,{"data-header-id":e.id,className:`py-1 h-8 relative hover:bg-gray-50 ${"actions"===e.id?"sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]":""}`,style:{width:e.getSize(),position:"relative",cursor:e.column.getCanSort()?"pointer":"default"},onMouseEnter:()=>{let t=document.querySelector(`[data-header-id="${e.id}"] .resizer`);t&&(t.style.opacity="0.5")},onMouseLeave:()=>{let t=document.querySelector(`[data-header-id="${e.id}"] .resizer`);t&&!e.column.getIsResizing()&&(t.style.opacity="0")},onClick:e.column.getCanSort()?e.column.getToggleSortingHandler():void 0,children:(0,t.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,t.jsx)("div",{className:"flex items-center",children:e.isPlaceholder?null:(0,C.flexRender)(e.column.columnDef.header,e.getContext())}),"actions"!==e.id&&e.column.getCanSort()&&(0,t.jsx)("div",{className:"w-4",children:e.column.getIsSorted()?({asc:(0,t.jsx)(b.ChevronUpIcon,{className:"h-4 w-4 text-blue-500"}),desc:(0,t.jsx)(p.ChevronDownIcon,{className:"h-4 w-4 text-blue-500"})})[e.column.getIsSorted()]:(0,t.jsx)(v.SwitchVerticalIcon,{className:"h-4 w-4 text-gray-400"})}),(0,t.jsx)("div",{onDoubleClick:()=>e.column.resetSize(),onMouseDown:e.getResizeHandler(),onTouchStart:e.getResizeHandler(),className:`resizer ${eh.options.columnResizeDirection} ${e.column.getIsResizing()?"isResizing":""}`,style:{position:"absolute",right:0,top:0,height:"100%",width:"5px",background:e.column.getIsResizing()?"#3b82f6":"transparent",cursor:"col-resize",userSelect:"none",touchAction:"none",opacity:+!!e.column.getIsResizing()}})]})},e.id))},e.id))}),(0,t.jsx)(S.TableBody,{children:Z?(0,t.jsx)(T.TableRow,{children:(0,t.jsx)(N.TableCell,{colSpan:em.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"🚅 Loading keys..."})})})}):eo.length>0?eh.getRowModel().rows.map(e=>(0,t.jsx)(T.TableRow,{className:"h-8",children:e.getVisibleCells().map(e=>(0,t.jsx)(N.TableCell,{style:{width:e.column.getSize(),maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},className:`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap ${"models"===e.column.id&&Array.isArray(e.getValue())&&e.getValue().length>3?"px-0":""}`,children:(0,C.flexRender)(e.column.columnDef.cell,e.getContext())},e.id))},e.id)):(0,t.jsx)(T.TableRow,{children:(0,t.jsx)(N.TableCell,{colSpan:em.length,className:"h-8 text-center",children:(0,t.jsx)("div",{className:"text-center text-gray-500",children:(0,t.jsx)("p",{children:"No keys found"})})})})})]})})})})]})})}let X=({userID:e,userRole:r,teams:a,keys:m,setUserRole:g,userEmail:h,setUserEmail:x,setTeams:p,setKeys:f,premiumUser:b,addKey:v,createClicked:C,autoOpenCreate:w,prefillData:y})=>{let[k,j]=(0,n.useState)(null),[S,N]=(0,n.useState)(null),_=(0,l.getCookie)("token"),[z,T]=(0,n.useState)(null),[I,P]=(0,n.useState)(null),[B,D]=(0,n.useState)([]),[M,E]=(0,n.useState)(null),[R,L]=(0,n.useState)(null);function A(){(0,l.clearTokenCookies)();let e=(0,d.getProxyBaseUrl)(),t=e?`${e}/sso/key/generate`:"/sso/key/generate";return window.location.href=t,null}if((0,n.useEffect)(()=>{let e=()=>{let e=sessionStorage.getItem("token");sessionStorage.clear(),e&&sessionStorage.setItem("token",e)};return window.addEventListener("beforeunload",e),()=>window.removeEventListener("beforeunload",e)},[]),(0,n.useEffect)(()=>{if(_){let e=(0,i.jwtDecode)(_);e&&(T(e.key),e.user_role&&g(function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"app_user":return"App User";case"internal_user":return"Internal User";case"internal_user_viewer":return"Internal Viewer";default:return"Unknown Role"}}(e.user_role)),e.user_email&&x(e.user_email))}if(e&&z&&r&&!k){let t=sessionStorage.getItem("userModels"+e);t?D(JSON.parse(t)):((async()=>{try{let t=await (0,d.getProxyUISettings)(z);E(t);let a=await (0,d.userGetInfoV2)(z,e);j(a),sessionStorage.setItem("userSpendData"+e,JSON.stringify(a));let l=(await (0,d.modelAvailableCall)(z,e,r)).data.map(e=>e.id);D(l),sessionStorage.setItem("userModels"+e,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e),e.message.includes("Invalid proxy server token passed")&&A()}})(),c(z,e,r,S,p))}},[e,_,z,r]),(0,n.useEffect)(()=>{z&&(async()=>{try{await (0,d.keyInfoCall)(z,[z])}catch(e){e.message.includes("Invalid proxy server token passed")&&A()}})()},[z]),(0,n.useEffect)(()=>{z&&c(z,e,r,S,p)},[S]),(0,n.useEffect)(()=>{if(null!==m&&null!=R&&null!==R.team_id){let e=0;for(let t of m)R.hasOwnProperty("team_id")&&null!==t.team_id&&t.team_id===R.team_id&&(e+=t.spend);P(e)}else if(null!==m){let e=0;for(let t of m)e+=t.spend;P(e)}},[R]),null==_)return A(),null;try{let e=(0,i.jwtDecode)(_).exp,t=Math.floor(Date.now()/1e3);if(e&&t>=e)return A(),null}catch(e){return console.error("Error decoding token:",e),(0,l.clearTokenCookies)(),A(),null}if(null==z)return null;if(null==e)return(0,t.jsx)("h1",{children:"User ID is not set"});null==r&&g("App Owner");let O="Admin Viewer"!==r&&"proxy_admin_viewer"!==r;return(0,t.jsx)("div",{className:"w-full mx-4 h-[75vh]",children:(0,t.jsx)(s.Grid,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,t.jsxs)(o.Col,{numColSpan:1,className:"flex flex-col gap-2",children:[O&&(0,t.jsx)(u.default,{team:R,teams:a,data:m,addKey:v,autoOpenCreate:w,prefillData:y},R?R.team_id:null),(0,t.jsx)(W,{})]})})})};var G=e.i(557951),q=e.i(618566);e.s(["default",0,function(){let{userId:e,userRole:l,userEmail:o,accessToken:s,premiumUser:i}=(0,a.default)(),{setUserRole:d,setUserEmail:c}=(0,G.useAuth)(),u=(0,q.useSearchParams)(),[m,g]=(0,n.useState)(null),[h,x]=(0,n.useState)([]),[p,f]=(0,n.useState)(!1),b="true"===u.get("create"),v=(0,n.useMemo)(()=>{if(!b)return;let e=u.get("owned_by"),t=u.get("team_id"),r=u.get("key_alias"),a=u.get("models"),l=u.get("key_type");if(!e&&!t&&!r&&!a&&!l)return;let o=e&&["you","service_account","another_user"].includes(e)?e:void 0,s=l&&["default","llm_api","management"].includes(l)?l:void 0,i=r?r.trim().slice(0,256):void 0,n=a?a.split(",").slice(0,100).map(e=>e.trim().slice(0,256)).filter(e=>e.length>0):void 0;return{owned_by:o,team_id:t?.trim()||void 0,key_alias:i,models:n&&n.length>0?n:void 0,key_type:s}},[u,b]);return(0,n.useEffect)(()=>{s&&e&&l&&(0,r.teamListCall)(s,1,100,{userID:"Admin"!==l&&"Admin Viewer"!==l?e:null}).then(e=>g(e.teams??[])).catch(console.error)},[s,e,l]),(0,t.jsx)(X,{userID:e,userRole:l,premiumUser:i??!1,teams:m,keys:h,setUserRole:d,userEmail:o,setUserEmail:c,setTeams:g,setKeys:x,addKey:e=>{x(t=>t?[...t,e]:[e]),f(e=>!e)},createClicked:p,autoOpenCreate:b,prefillData:v})}],502501)},973095,e=>{"use strict";var t=e.i(843476),r=e.i(502501),a=e.i(135214),l=e.i(936578),o=e.i(271645);function s(){let{isLoading:e,isAuthorized:o}=(0,a.default)();return e||!o?(0,t.jsx)(l.default,{}):(0,t.jsx)(r.default,{})}e.s(["default",0,function(){return(0,t.jsx)(o.Suspense,{fallback:(0,t.jsx)(l.default,{}),children:(0,t.jsx)(s,{})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0u9~32cojjvj6.js b/litellm/proxy/_experimental/out/_next/static/chunks/0u9~32cojjvj6.js new file mode 100644 index 00000000000..e8c234f69a5 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0u9~32cojjvj6.js @@ -0,0 +1,179 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,93826,952571,e=>{"use strict";var s=e.i(271645);let t=s.forwardRef(function(e,t){return s.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),s.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"}))});e.s(["SearchIcon",0,t],93826);var a=e.i(879664);e.s(["Info",()=>a.default],952571)},737033,e=>{"use strict";var s=e.i(843476),t=e.i(271645),a=e.i(599724),l=e.i(928685),r=e.i(311451),i=e.i(199133),n=e.i(798496),c=e.i(389083),o=e.i(592968),d=e.i(166406),x=e.i(596239);e.i(622826);var m=e.i(112179),h=e.i(652272);e.s(["default",0,({skills:e,isLoading:u,isAdmin:p,accessToken:g,publicPage:j=!1,onPublishSuccess:b})=>{let[f,v]=(0,t.useState)(""),[y,N]=(0,t.useState)(void 0),[_,T]=(0,t.useState)(null),w=e.length,S=(0,t.useMemo)(()=>[...new Set(e.map(e=>e.domain).filter(Boolean))],[e]),C=(0,t.useMemo)(()=>[...new Set(e.map(e=>e.namespace).filter(Boolean))],[e]),k=(0,t.useMemo)(()=>{let s=e;if(y&&(s=s.filter(e=>(e.domain||"General")===y)),f.trim()){let e=f.toLowerCase();s=s.filter(s=>s.name.toLowerCase().includes(e)||s.description?.toLowerCase().includes(e)||s.domain?.toLowerCase().includes(e)||s.namespace?.toLowerCase().includes(e)||s.keywords?.some(s=>s.toLowerCase().includes(e)))}return s},[e,f,y]);return _?(0,s.jsx)(h.default,{skill:_,onBack:()=>T(null),isAdmin:p,accessToken:g,onPublishClick:b}):u?(0,s.jsx)("div",{className:"text-center py-16 text-gray-400",children:"Loading skills..."}):(0,s.jsxs)("div",{className:"space-y-6",children:[(0,s.jsxs)("div",{className:"grid grid-cols-3 gap-4",children:[(0,s.jsxs)("div",{className:"border border-gray-200 rounded-lg p-4",children:[(0,s.jsx)("div",{className:"text-xs text-gray-500 mb-1",children:"Total Skills"}),(0,s.jsx)("div",{className:"text-2xl font-semibold text-gray-900",children:w})]}),(0,s.jsxs)("div",{className:"border border-gray-200 rounded-lg p-4",children:[(0,s.jsx)("div",{className:"text-xs text-gray-500 mb-1",children:"Namespaces"}),(0,s.jsx)("div",{className:"text-2xl font-semibold text-gray-900",children:C.length})]}),(0,s.jsxs)("div",{className:"border border-gray-200 rounded-lg p-4",children:[(0,s.jsx)("div",{className:"text-xs text-gray-500 mb-1",children:"Domains"}),(0,s.jsx)("div",{className:"text-2xl font-semibold text-gray-900",children:S.length})]})]}),(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,s.jsxs)("h3",{className:"text-sm font-semibold text-gray-700",children:["All ",j?"Public ":"","Skills"]}),(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)(i.Select,{placeholder:"All Domains",allowClear:!0,value:y,onChange:e=>N(e),style:{width:160},options:S.map(e=>({label:e,value:e}))}),(0,s.jsx)(r.Input,{prefix:(0,s.jsx)(l.SearchOutlined,{className:"text-gray-400"}),placeholder:"Search by name, namespace, or tag…",value:f,onChange:e=>v(e.target.value),style:{width:280},allowClear:!0})]})]}),(0,s.jsx)(n.ModelDataTable,{columns:((e,t,l=!1)=>[{header:"Skill Name",accessorKey:"name",enableSorting:!0,sortingFn:"alphanumeric",cell:({row:l})=>{let r=l.original;return(0,s.jsxs)("div",{className:"space-y-1",children:[(0,s.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,s.jsx)("button",{type:"button",className:"font-medium text-sm cursor-pointer text-blue-600 hover:underline bg-transparent border-none p-0",onClick:()=>e(r),children:r.name}),(0,s.jsx)(o.Tooltip,{title:"Copy skill name",children:(0,s.jsx)(d.CopyOutlined,{onClick:()=>t(r.name),className:"cursor-pointer text-gray-500 hover:text-blue-500 text-xs"})})]}),r.description&&(0,s.jsx)(a.Text,{className:"text-xs text-gray-500 line-clamp-1 md:hidden",children:r.description})]})}},{header:"Description",accessorKey:"description",enableSorting:!1,cell:({row:e})=>(0,s.jsx)(a.Text,{className:"text-xs line-clamp-2",children:e.original.description||"-"})},{header:"Category",accessorKey:"category",enableSorting:!0,cell:({row:e})=>{let t=e.original.category;return t?(0,s.jsx)(c.Badge,{color:"blue",size:"xs",children:t}):(0,s.jsx)(a.Text,{className:"text-xs text-gray-400",children:"-"})}},{header:"Domain",accessorKey:"domain",enableSorting:!0,cell:({row:e})=>(0,s.jsx)(a.Text,{className:"text-xs",children:e.original.domain||"-"})},{header:"Source",accessorKey:"source",enableSorting:!1,cell:({row:e})=>{let t=e.original.source,l=null,r="-";return(t?.source==="github"&&t.repo?(l=`https://github.com/${t.repo}`,r=t.repo):t?.source==="git-subdir"&&t.url?r=(l=t.path?`${t.url}/tree/main/${t.path}`:t.url).replace("https://github.com/",""):t?.source==="url"&&t.url&&(l=t.url,r=t.url.replace(/^https?:\/\//,"")),l)?(0,s.jsxs)("a",{href:l,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs text-blue-600 hover:underline truncate max-w-[180px]",title:r,children:[(0,s.jsx)("span",{className:"truncate",children:r}),(0,s.jsx)(x.LinkOutlined,{className:"shrink-0",style:{fontSize:10}})]}):(0,s.jsx)(a.Text,{className:"text-xs text-gray-400",children:"-"})}},{header:"Status",accessorKey:"enabled",enableSorting:!0,cell:({row:e})=>(0,s.jsx)(m.StatusBadge,{tone:e.original.enabled?"success":"neutral",label:e.original.enabled?"Public":"Draft"})}])(e=>T(e),e=>{navigator.clipboard.writeText(e)},j),data:k,isLoading:!1,defaultSorting:[{id:"name",desc:!1}]}),(0,s.jsx)("div",{className:"mt-3 text-center",children:(0,s.jsxs)(a.Text,{className:"text-sm text-gray-500",children:["Showing ",k.length," of ",w," skill",1!==w?"s":""]})})]})]})}],737033)},976883,e=>{"use strict";var s=e.i(843476),t=e.i(275144),a=e.i(434626),l=e.i(93826),r=e.i(994388),i=e.i(304967),n=e.i(599724),c=e.i(629569),o=e.i(212931),d=e.i(199133),x=e.i(653496),m=e.i(262218),h=e.i(592968),u=e.i(174886),p=e.i(952571),g=e.i(271645),j=e.i(798496),b=e.i(727749),f=e.i(402874),v=e.i(602869),y=e.i(737033),N=e.i(339019),_=e.i(865361),T=e.i(916925);let{TabPane:w}=x.Tabs;e.s(["default",0,({accessToken:e,isEmbedded:S=!1})=>{let C,k,A,M,P,L,z,E,[O,K]=(0,g.useState)(null),[I,D]=(0,g.useState)(null),[R,U]=(0,g.useState)(null),[H,F]=(0,g.useState)("LiteLLM Gateway"),[$,B]=(0,g.useState)(null),[W,q]=(0,g.useState)(""),[G,V]=(0,g.useState)({}),[X,J]=(0,g.useState)(!0),[Y,Q]=(0,g.useState)(!0),[Z,ee]=(0,g.useState)(!0),[es,et]=(0,g.useState)(""),[ea,el]=(0,g.useState)(""),[er,ei]=(0,g.useState)(""),[en,ec]=(0,g.useState)([]),[eo,ed]=(0,g.useState)([]),[ex,em]=(0,g.useState)([]),[eh,eu]=(0,g.useState)([]),[ep,eg]=(0,g.useState)([]),[ej,eb]=(0,g.useState)("I'm alive! ✓"),[ef,ev]=(0,g.useState)(!1),[ey,eN]=(0,g.useState)(!1),[e_,eT]=(0,g.useState)(!1),[ew,eS]=(0,g.useState)(null),[eC,ek]=(0,g.useState)(null),[eA,eM]=(0,g.useState)(null),[eP,eL]=(0,g.useState)("models"),[ez,eE]=(0,g.useState)([]),[eO,eK]=(0,g.useState)(!1);(0,g.useEffect)(()=>{(async()=>{try{await (0,v.getUiConfig)()}catch(e){console.error("Failed to get UI config:",e)}let e=async()=>{try{J(!0);let e=await (0,v.modelHubPublicModelsCall)();K(Array.isArray(e)?e:[])}catch(e){console.error("There was an error fetching the public model data",e),eb("Service unavailable")}finally{J(!1)}},s=async()=>{try{Q(!0);let e=await (0,v.agentHubPublicModelsCall)();D(Array.isArray(e)?e:[])}catch(e){console.error("There was an error fetching the public agent data",e)}finally{Q(!1)}},t=async()=>{try{ee(!0);let e=await (0,v.mcpHubPublicServersCall)();U(Array.isArray(e)?e:[])}catch(e){console.error("There was an error fetching the public MCP server data",e)}finally{ee(!1)}},a=async()=>{try{eK(!0);let e=await (0,v.skillHubPublicCall)();eE(e.plugins??[])}catch(e){console.error("There was an error fetching the public skill data",e)}finally{eK(!1)}};(async()=>{let e=await (0,v.getPublicModelHubInfo)();F(e.docs_title),B(e.custom_docs_description),q(e.litellm_version),V(e.useful_links||{})})(),e(),s(),t(),a()})()},[]),(0,g.useEffect)(()=>{},[es,en,eo,ex]);let eI=(0,g.useMemo)(()=>{if(!O||!Array.isArray(O))return[];let e=O;if(es.trim()){let s=es.toLowerCase(),t=s.split(/\s+/),a=O.filter(e=>{let a=e.model_group.toLowerCase();return!!a.includes(s)||t.every(e=>a.includes(e))});a.length>0&&(e=a.sort((e,t)=>{let a=e.model_group.toLowerCase(),l=t.model_group.toLowerCase(),r=1e3*(a===s),i=1e3*(l===s),n=100*!!a.startsWith(s),c=100*!!l.startsWith(s),o=50*!!s.split(/\s+/).every(e=>a.includes(e)),d=50*!!s.split(/\s+/).every(e=>l.includes(e)),x=a.length;return i+c+d+(1e3-l.length)-(r+n+o+(1e3-x))}))}return e.filter(e=>{let s=0===en.length||en.some(s=>e.providers.includes(s)),t=0===eo.length||eo.includes(e.mode||""),a=0===ex.length||Object.entries(e).filter(([e,s])=>e.startsWith("supports_")&&!0===s).some(([e])=>{let s=e.replace(/^supports_/,"").split("_").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ");return ex.includes(s)});return s&&t&&a})},[O,es,en,eo,ex]),eD=(0,g.useMemo)(()=>{if(!I||!Array.isArray(I))return[];let e=I;if(ea.trim()){let s=ea.toLowerCase(),t=s.split(/\s+/);e=(e=I.filter(e=>{let a=e.name.toLowerCase(),l=e.description.toLowerCase();return!!(a.includes(s)||l.includes(s))||t.every(e=>a.includes(e)||l.includes(e))})).sort((e,t)=>{let a=e.name.toLowerCase(),l=t.name.toLowerCase(),r=1e3*(a===s),i=1e3*(l===s),n=100*!!a.startsWith(s),c=100*!!l.startsWith(s),o=r+n+(1e3-a.length);return i+c+(1e3-l.length)-o})}return e.filter(e=>0===eh.length||e.skills?.some(e=>e.tags?.some(e=>eh.includes(e))))},[I,ea,eh]),eR=(0,g.useMemo)(()=>{if(!R||!Array.isArray(R))return[];let e=R;if(er.trim()){let s=er.toLowerCase(),t=s.split(/\s+/);e=(e=R.filter(e=>{let a=e.server_name.toLowerCase(),l=(e.mcp_info?.description||"").toLowerCase();return!!(a.includes(s)||l.includes(s))||t.every(e=>a.includes(e)||l.includes(e))})).sort((e,t)=>{let a=e.server_name.toLowerCase(),l=t.server_name.toLowerCase(),r=1e3*(a===s),i=1e3*(l===s),n=100*!!a.startsWith(s),c=100*!!l.startsWith(s),o=r+n+(1e3-a.length);return i+c+(1e3-l.length)-o})}return e.filter(e=>0===ep.length||ep.includes(e.transport))},[R,er,ep]),eU=e=>{navigator.clipboard.writeText(e),b.default.success("Copied to clipboard!")},eH=e=>e.replace(/^supports_/,"").split("_").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" "),eF=e=>`$${(1e6*e).toFixed(4)}`,e$=e=>e?e>=1e3?`${(e/1e3).toFixed(0)}K`:e.toString():"N/A";return(0,s.jsx)(t.ThemeProvider,{accessToken:e,children:(0,s.jsxs)("div",{className:S?"w-full":"min-h-screen bg-white",children:[!S&&(0,s.jsx)(f.default,{accessToken:e||null,isPublicPage:!0}),(0,s.jsxs)("div",{className:S?"w-full p-6":"w-full px-8 py-12",children:[S&&(0,s.jsx)("div",{className:"mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg",children:(0,s.jsx)("p",{className:"text-sm text-gray-700",children:"These are models, agents, and MCP servers your proxy admin has indicated are available in your company."})}),!S&&(0,s.jsxs)(i.Card,{className:"mb-10 p-8 bg-white border border-gray-200 rounded-lg shadow-xs",children:[(0,s.jsx)(c.Title,{className:"text-2xl font-semibold mb-6 text-gray-900",children:"About"}),(0,s.jsx)("p",{className:"text-gray-700 mb-6 text-base leading-relaxed",children:$||"Proxy Server to call 100+ LLMs in the OpenAI format."}),(0,s.jsx)("div",{className:"flex items-center space-x-3 text-sm text-gray-600",children:(0,s.jsxs)("span",{className:"flex items-center",children:[(0,s.jsx)("span",{className:"w-4 h-4 mr-2",children:"🔧"}),"Built with litellm: v",W]})})]}),G&&Object.keys(G).length>0&&(0,s.jsxs)(i.Card,{className:"mb-10 p-8 bg-white border border-gray-200 rounded-lg shadow-xs",children:[(0,s.jsx)(c.Title,{className:"text-2xl font-semibold mb-6 text-gray-900",children:"Useful Links"}),(0,s.jsx)("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6",children:Object.entries(G||{}).map(([e,s])=>({title:e,url:"string"==typeof s?s:s.url,index:"string"==typeof s?0:s.index??0})).sort((e,s)=>e.index-s.index).map(({title:e,url:t})=>(0,s.jsxs)("button",{onClick:()=>window.open(t,"_blank"),className:"flex items-center space-x-3 text-blue-600 hover:text-blue-800 transition-colors p-3 rounded-lg hover:bg-blue-50 border border-gray-200",children:[(0,s.jsx)(a.ExternalLinkIcon,{className:"w-4 h-4"}),(0,s.jsx)(n.Text,{className:"text-sm font-medium",children:e})]},e))})]}),!S&&(0,s.jsxs)(i.Card,{className:"mb-10 p-8 bg-white border border-gray-200 rounded-lg shadow-xs",children:[(0,s.jsx)(c.Title,{className:"text-2xl font-semibold mb-6 text-gray-900",children:"Health and Endpoint Status"}),(0,s.jsx)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:(0,s.jsxs)(n.Text,{className:"text-green-600 font-medium text-sm",children:["Service status: ",ej]})})]}),(0,s.jsx)(i.Card,{className:"p-8 bg-white border border-gray-200 rounded-lg shadow-xs",children:(0,s.jsxs)(x.Tabs,{activeKey:eP,onChange:eL,size:"large",className:"public-hub-tabs",children:[(0,s.jsxs)(w,{tab:"Model Hub",children:[(0,s.jsx)("div",{className:"flex justify-between items-center mb-8",children:(0,s.jsx)(c.Title,{className:"text-2xl font-semibold text-gray-900",children:"Available Models"})}),(0,s.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200",children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-center space-x-2 mb-3",children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium text-gray-700",children:"Search Models:"}),(0,s.jsx)(h.Tooltip,{title:"Smart search with relevance ranking - finds models containing your search terms, ranked by relevance. Try searching 'xai grok-4', 'claude-4', 'gpt-4', or 'sonnet'",placement:"top",children:(0,s.jsx)(p.Info,{className:"w-4 h-4 text-gray-400 cursor-help"})})]}),(0,s.jsxs)("div",{className:"relative",children:[(0,s.jsx)(l.SearchIcon,{className:"w-4 h-4 text-gray-400 absolute left-3 top-1/2 transform -translate-y-1/2"}),(0,s.jsx)("input",{type:"text",placeholder:"Search model names... (smart search enabled)",value:es,onChange:e=>et(e.target.value),className:"border border-gray-300 rounded-lg pl-10 pr-4 py-2 w-full text-sm focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white"})]})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium mb-3 text-gray-700",children:"Provider:"}),(0,s.jsx)(d.Select,{mode:"multiple",value:en,onChange:e=>ec(e),placeholder:"Select providers",className:"w-full",size:"large",allowClear:!0,optionRender:e=>{let{logo:t}=(0,T.getProviderLogoAndName)(e.value);return(0,s.jsxs)("div",{className:"flex items-center space-x-2",children:[t&&(0,s.jsx)("img",{src:t,alt:e.label,className:"w-5 h-5 shrink-0 object-contain",onError:e=>{e.target.style.display="none"}}),(0,s.jsx)("span",{className:"capitalize",children:e.label})]})},children:O&&Array.isArray(O)&&(C=new Set,O.forEach(e=>{(e.providers??[]).forEach(e=>C.add(e))}),Array.from(C)).map(e=>(0,s.jsx)(d.Select.Option,{value:e,children:e},e))})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium mb-3 text-gray-700",children:"Mode:"}),(0,s.jsx)(d.Select,{mode:"multiple",value:eo,onChange:e=>ed(e),placeholder:"Select modes",className:"w-full",size:"large",allowClear:!0,children:O&&Array.isArray(O)&&(k=new Set,O.forEach(e=>{e.mode&&k.add(e.mode)}),Array.from(k)).map(e=>(0,s.jsx)(d.Select.Option,{value:e,children:e},e))})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium mb-3 text-gray-700",children:"Features:"}),(0,s.jsx)(d.Select,{mode:"multiple",value:ex,onChange:e=>em(e),placeholder:"Select features",className:"w-full",size:"large",allowClear:!0,children:O&&Array.isArray(O)&&(A=new Set,O.forEach(e=>{Object.entries(e).filter(([e,s])=>e.startsWith("supports_")&&!0===s).forEach(([e])=>{let s=e.replace(/^supports_/,"").split("_").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ");A.add(s)})}),Array.from(A).sort()).map(e=>(0,s.jsx)(d.Select.Option,{value:e,children:e},e))})]})]}),(0,s.jsx)(j.ModelDataTable,{columns:[{header:"Model Name",accessorKey:"model_group",enableSorting:!0,cell:({row:e})=>(0,s.jsx)("div",{className:"overflow-hidden",children:(0,s.jsx)(h.Tooltip,{title:e.original.model_group,children:(0,s.jsx)(r.Button,{size:"xs",variant:"light",className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left",onClick:()=>{eS(e.original),ev(!0)},children:e.original.model_group})})}),size:150},{header:"Providers",accessorKey:"providers",enableSorting:!0,cell:({row:e})=>{let t=e.original.providers??[];return(0,s.jsx)("div",{className:"flex flex-wrap gap-1",children:t.map(e=>{let{logo:t}=(0,T.getProviderLogoAndName)(e);return(0,s.jsxs)("div",{className:"flex items-center space-x-1 px-2 py-1 bg-gray-100 rounded-sm text-xs",children:[t&&(0,s.jsx)("img",{src:t,alt:e,className:"w-3 h-3 shrink-0 object-contain",onError:e=>{e.target.style.display="none"}}),(0,s.jsx)("span",{className:"capitalize",children:e})]},e)})})},size:120},{header:"Mode",accessorKey:"mode",enableSorting:!0,cell:({row:e})=>{let t=e.original.mode;return(0,s.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,s.jsx)("span",{children:(e=>{switch(e?.toLowerCase()){case"chat":return"💬";case"rerank":return"🔄";case"embedding":return"📄";default:return"🤖"}})(t||"")}),(0,s.jsx)(n.Text,{children:t||"Chat"})]})},size:100},{header:"Max Input",accessorKey:"max_input_tokens",enableSorting:!0,cell:({row:e})=>(0,s.jsx)(n.Text,{className:"text-center",children:e$(e.original.max_input_tokens)}),size:100,meta:{className:"text-center"}},{header:"Max Output",accessorKey:"max_output_tokens",enableSorting:!0,cell:({row:e})=>(0,s.jsx)(n.Text,{className:"text-center",children:e$(e.original.max_output_tokens)}),size:100,meta:{className:"text-center"}},{header:"Input $/1M",accessorKey:"input_cost_per_token",enableSorting:!0,cell:({row:e})=>{let t=e.original.input_cost_per_token;return(0,s.jsx)(n.Text,{className:"text-center",children:t?eF(t):"Free"})},size:100,meta:{className:"text-center"}},{header:"Output $/1M",accessorKey:"output_cost_per_token",enableSorting:!0,cell:({row:e})=>{let t=e.original.output_cost_per_token;return(0,s.jsx)(n.Text,{className:"text-center",children:t?eF(t):"Free"})},size:100,meta:{className:"text-center"}},{header:"Features",accessorKey:"supports_vision",enableSorting:!1,cell:({row:e})=>{let t=Object.entries(e.original).filter(([e,s])=>e.startsWith("supports_")&&!0===s).map(([e])=>eH(e));return 0===t.length?(0,s.jsx)(n.Text,{className:"text-gray-400",children:"-"}):1===t.length?(0,s.jsx)("div",{className:"h-6 flex items-center",children:(0,s.jsx)(m.Tag,{color:"blue",className:"text-xs",children:t[0]})}):(0,s.jsxs)("div",{className:"h-6 flex items-center space-x-1",children:[(0,s.jsx)(m.Tag,{color:"blue",className:"text-xs",children:t[0]}),(0,s.jsx)(h.Tooltip,{title:(0,s.jsxs)("div",{className:"space-y-1",children:[(0,s.jsx)("div",{className:"font-medium",children:"All Features:"}),t.map((e,t)=>(0,s.jsxs)("div",{className:"text-xs",children:["• ",e]},t))]}),trigger:"click",placement:"topLeft",children:(0,s.jsxs)("span",{className:"text-xs text-blue-600 cursor-pointer hover:text-blue-800 hover:underline",onClick:e=>e.stopPropagation(),children:["+",t.length-1]})})]})},size:120},{header:"Health Status",accessorKey:"health_status",enableSorting:!0,cell:({row:e})=>{let t=e.original,a="healthy"===t.health_status?"green":"unhealthy"===t.health_status?"red":"default",l=t.health_response_time?`Response Time: ${Number(t.health_response_time).toFixed(2)}ms`:"N/A",r=t.health_checked_at?`Last Checked: ${new Date(t.health_checked_at).toLocaleString()}`:"N/A";return(0,s.jsx)(h.Tooltip,{title:(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("div",{children:l}),(0,s.jsx)("div",{children:r})]}),children:(0,s.jsx)(m.Tag,{color:a,children:(0,s.jsx)("span",{className:"capitalize",children:t.health_status??"Unknown"})},t.model_group)})},size:100},{header:"Limits",accessorKey:"rpm",enableSorting:!0,cell:({row:e})=>{var t,a;let l,r=e.original;return(0,s.jsx)(n.Text,{className:"text-xs text-gray-600",children:(t=r.rpm,a=r.tpm,l=[],t&&l.push(`RPM: ${t.toLocaleString()}`),a&&l.push(`TPM: ${a.toLocaleString()}`),l.length>0?l.join(", "):"N/A")})},size:150}],data:eI,isLoading:X,defaultSorting:[{id:"model_group",desc:!1}]}),(0,s.jsx)("div",{className:"mt-8 text-center",children:(0,s.jsxs)(n.Text,{className:"text-sm text-gray-600",children:["Showing ",eI.length," of ",O?.length||0," models"]})})]},"models"),I&&Array.isArray(I)&&I.length>0&&(0,s.jsxs)(w,{tab:"Agent Hub",children:[(0,s.jsx)("div",{className:"flex justify-between items-center mb-8",children:(0,s.jsx)(c.Title,{className:"text-2xl font-semibold text-gray-900",children:"Available Agents"})}),(0,s.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6 mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200",children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-center space-x-2 mb-3",children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium text-gray-700",children:"Search Agents:"}),(0,s.jsx)(h.Tooltip,{title:"Search agents by name or description",placement:"top",children:(0,s.jsx)(p.Info,{className:"w-4 h-4 text-gray-400 cursor-help"})})]}),(0,s.jsxs)("div",{className:"relative",children:[(0,s.jsx)(l.SearchIcon,{className:"w-4 h-4 text-gray-400 absolute left-3 top-1/2 transform -translate-y-1/2"}),(0,s.jsx)("input",{type:"text",placeholder:"Search agent names or descriptions...",value:ea,onChange:e=>el(e.target.value),className:"border border-gray-300 rounded-lg pl-10 pr-4 py-2 w-full text-sm focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white"})]})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium mb-3 text-gray-700",children:"Skills:"}),(0,s.jsx)(d.Select,{mode:"multiple",value:eh,onChange:e=>eu(e),placeholder:"Select skills",className:"w-full",size:"large",allowClear:!0,children:I&&Array.isArray(I)&&(M=new Set,I.forEach(e=>{e.skills?.forEach(e=>{e.tags?.forEach(e=>M.add(e))})}),Array.from(M).sort()).map(e=>(0,s.jsx)(d.Select.Option,{value:e,children:e},e))})]})]}),(0,s.jsx)(j.ModelDataTable,{columns:[{header:"Agent Name",accessorKey:"name",enableSorting:!0,cell:({row:e})=>(0,s.jsx)("div",{className:"overflow-hidden",children:(0,s.jsx)(h.Tooltip,{title:e.original.name,children:(0,s.jsx)(r.Button,{size:"xs",variant:"light",className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left",onClick:()=>{ek(e.original),eN(!0)},children:e.original.name})})}),size:150},{header:"Description",accessorKey:"description",enableSorting:!1,cell:({row:e})=>{let t=e.original.description??"",a=t.length>80?t.substring(0,80)+"...":t;return(0,s.jsx)(h.Tooltip,{title:t,children:(0,s.jsx)(n.Text,{className:"text-sm text-gray-700",children:a})})},size:250},{header:"Version",accessorKey:"version",enableSorting:!0,cell:({row:e})=>(0,s.jsx)(n.Text,{className:"text-sm",children:e.original.version}),size:80},{header:"Provider",accessorKey:"provider",enableSorting:!1,cell:({row:e})=>{let t=e.original.provider;return t?(0,s.jsx)("div",{className:"text-sm",children:(0,s.jsx)(n.Text,{className:"font-medium",children:t.organization})}):(0,s.jsx)(n.Text,{className:"text-gray-400",children:"-"})},size:120},{header:"Skills",accessorKey:"skills",enableSorting:!1,cell:({row:e})=>{let t=e.original.skills||[];return 0===t.length?(0,s.jsx)(n.Text,{className:"text-gray-400",children:"-"}):1===t.length?(0,s.jsx)("div",{className:"h-6 flex items-center",children:(0,s.jsx)(m.Tag,{color:"purple",className:"text-xs",children:t[0].name})}):(0,s.jsxs)("div",{className:"h-6 flex items-center space-x-1",children:[(0,s.jsx)(m.Tag,{color:"purple",className:"text-xs",children:t[0].name}),(0,s.jsx)(h.Tooltip,{title:(0,s.jsxs)("div",{className:"space-y-1",children:[(0,s.jsx)("div",{className:"font-medium",children:"All Skills:"}),t.map((e,t)=>(0,s.jsxs)("div",{className:"text-xs",children:["• ",e.name]},t))]}),trigger:"click",placement:"topLeft",children:(0,s.jsxs)("span",{className:"text-xs text-purple-600 cursor-pointer hover:text-purple-800 hover:underline",onClick:e=>e.stopPropagation(),children:["+",t.length-1]})})]})},size:150},{header:"Capabilities",accessorKey:"capabilities",enableSorting:!1,cell:({row:e})=>{let t=Object.entries(e.original.capabilities||{}).filter(([e,s])=>!0===s).map(([e])=>e);return 0===t.length?(0,s.jsx)(n.Text,{className:"text-gray-400",children:"-"}):(0,s.jsx)("div",{className:"flex flex-wrap gap-1",children:t.map(e=>(0,s.jsx)(m.Tag,{color:"green",className:"text-xs capitalize",children:e},e))})},size:150}],data:eD,isLoading:Y,defaultSorting:[{id:"name",desc:!1}]}),(0,s.jsx)("div",{className:"mt-8 text-center",children:(0,s.jsxs)(n.Text,{className:"text-sm text-gray-600",children:["Showing ",eD.length," of ",I?.length||0," agents"]})})]},"agents"),R&&Array.isArray(R)&&R.length>0&&(0,s.jsxs)(w,{tab:"MCP Hub",children:[(0,s.jsx)("div",{className:"flex justify-between items-center mb-8",children:(0,s.jsx)(c.Title,{className:"text-2xl font-semibold text-gray-900",children:"Available MCP Servers"})}),(0,s.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6 mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200",children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-center space-x-2 mb-3",children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium text-gray-700",children:"Search MCP Servers:"}),(0,s.jsx)(h.Tooltip,{title:"Search MCP servers by name or description",placement:"top",children:(0,s.jsx)(p.Info,{className:"w-4 h-4 text-gray-400 cursor-help"})})]}),(0,s.jsxs)("div",{className:"relative",children:[(0,s.jsx)(l.SearchIcon,{className:"w-4 h-4 text-gray-400 absolute left-3 top-1/2 transform -translate-y-1/2"}),(0,s.jsx)("input",{type:"text",placeholder:"Search MCP server names or descriptions...",value:er,onChange:e=>ei(e.target.value),className:"border border-gray-300 rounded-lg pl-10 pr-4 py-2 w-full text-sm focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white"})]})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium mb-3 text-gray-700",children:"Transport:"}),(0,s.jsx)(d.Select,{mode:"multiple",value:ep,onChange:e=>eg(e),placeholder:"Select transport types",className:"w-full",size:"large",allowClear:!0,children:R&&Array.isArray(R)&&(P=new Set,R.forEach(e=>{e.transport&&P.add(e.transport)}),Array.from(P).sort()).map(e=>(0,s.jsx)(d.Select.Option,{value:e,children:e},e))})]})]}),(0,s.jsx)(j.ModelDataTable,{columns:(L=e=>{eM(e),eT(!0)},[{header:"Server Name",accessorKey:"server_name",enableSorting:!0,cell:({row:e})=>(0,s.jsx)("div",{className:"overflow-hidden",children:(0,s.jsx)(h.Tooltip,{title:e.original.server_name,children:(0,s.jsx)(r.Button,{size:"xs",variant:"light",className:"font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left",onClick:()=>L(e.original),children:e.original.server_name})})}),size:150},{header:"Description",accessorKey:"mcp_info.description",enableSorting:!1,cell:({row:e})=>{let t=String(e.original.mcp_info?.description??"-"),a=t.length>80?t.substring(0,80)+"...":t;return(0,s.jsx)(h.Tooltip,{title:t,children:(0,s.jsx)(n.Text,{className:"text-sm text-gray-700",children:a})})},size:250},{header:"Transport",accessorKey:"transport",enableSorting:!0,cell:({row:e})=>{let t=e.original.transport;return(0,s.jsx)(m.Tag,{color:"blue",className:"text-xs uppercase",children:t})},size:100},{header:"Auth Type",accessorKey:"auth_type",enableSorting:!0,cell:({row:e})=>{let t=e.original.auth_type;return(0,s.jsx)(m.Tag,{color:"none"===t?"gray":"green",className:"text-xs capitalize",children:t})},size:100}]),data:eR,isLoading:Z,defaultSorting:[{id:"server_name",desc:!1}]}),(0,s.jsx)("div",{className:"mt-8 text-center",children:(0,s.jsxs)(n.Text,{className:"text-sm text-gray-600",children:["Showing ",eR.length," of ",R?.length||0," MCP servers"]})})]},"mcp"),(0,s.jsx)(w,{tab:"Skill Hub",children:(0,s.jsx)(y.default,{skills:ez,isLoading:eO,publicPage:!0})},"skills")]})})]}),(0,s.jsx)(o.Modal,{title:(0,s.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,s.jsx)("span",{children:ew?.model_group||"Model Details"}),ew&&(0,s.jsx)(h.Tooltip,{title:"Copy model name",children:(0,s.jsx)(u.Copy,{onClick:()=>eU(ew.model_group),className:"cursor-pointer text-gray-500 hover:text-blue-500 w-4 h-4"})})]}),width:1e3,open:ef,footer:null,onOk:()=>{ev(!1),eS(null)},onCancel:()=>{ev(!1),eS(null)},children:ew&&(0,s.jsxs)("div",{className:"space-y-6",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Model Overview"}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4 mb-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Model Name:"}),(0,s.jsx)(n.Text,{children:ew.model_group})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Mode:"}),(0,s.jsx)(n.Text,{children:ew.mode||"Not specified"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Providers:"}),(0,s.jsx)("div",{className:"flex flex-wrap gap-1 mt-1",children:(ew.providers??[]).map(e=>{let{logo:t}=(0,T.getProviderLogoAndName)(e);return(0,s.jsx)(m.Tag,{color:"blue",children:(0,s.jsxs)("div",{className:"flex items-center space-x-1",children:[t&&(0,s.jsx)("img",{src:t,alt:e,className:"w-3 h-3 shrink-0 object-contain",onError:e=>{e.target.style.display="none"}}),(0,s.jsx)("span",{className:"capitalize",children:e})]})},e)})})]})]}),ew.model_group.includes("*")&&(0,s.jsx)("div",{className:"bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4",children:(0,s.jsxs)("div",{className:"flex items-start space-x-2",children:[(0,s.jsx)(p.Info,{className:"w-4 h-4 text-blue-600 mt-0.5 shrink-0"}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium text-blue-900 mb-2",children:"Wildcard Routing"}),(0,s.jsxs)(n.Text,{className:"text-sm text-blue-800 mb-2",children:["This model uses wildcard routing. You can pass any value where you see the"," ",(0,s.jsx)("code",{className:"bg-blue-100 px-1 py-0.5 rounded-sm text-xs",children:"*"})," symbol."]}),(0,s.jsxs)(n.Text,{className:"text-sm text-blue-800",children:["For example, with"," ",(0,s.jsx)("code",{className:"bg-blue-100 px-1 py-0.5 rounded-sm text-xs",children:ew.model_group}),", you can use any string (",(0,s.jsx)("code",{className:"bg-blue-100 px-1 py-0.5 rounded-sm text-xs",children:ew.model_group.replaceAll("*","my-custom-value")}),") that matches this pattern."]})]})]})})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Token & Cost Information"}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Max Input Tokens:"}),(0,s.jsx)(n.Text,{children:ew.max_input_tokens?.toLocaleString()||"Not specified"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Max Output Tokens:"}),(0,s.jsx)(n.Text,{children:ew.max_output_tokens?.toLocaleString()||"Not specified"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Input Cost per 1M Tokens:"}),(0,s.jsx)(n.Text,{children:ew.input_cost_per_token?eF(ew.input_cost_per_token):"Not specified"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Output Cost per 1M Tokens:"}),(0,s.jsx)(n.Text,{children:ew.output_cost_per_token?eF(ew.output_cost_per_token):"Not specified"})]})]})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Capabilities"}),(0,s.jsx)("div",{className:"flex flex-wrap gap-2",children:(z=Object.entries(ew).filter(([e,s])=>e.startsWith("supports_")&&!0===s).map(([e])=>e),E=["green","blue","purple","orange","red","yellow"],0===z.length?(0,s.jsx)(n.Text,{className:"text-gray-500",children:"No special capabilities listed"}):z.map((e,t)=>(0,s.jsx)(m.Tag,{color:E[t%E.length],children:eH(e)},e)))})]}),(ew.tpm||ew.rpm)&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Rate Limits"}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[ew.tpm&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Tokens per Minute:"}),(0,s.jsx)(n.Text,{children:ew.tpm.toLocaleString()})]}),ew.rpm&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Requests per Minute:"}),(0,s.jsx)(n.Text,{children:ew.rpm.toLocaleString()})]})]})]}),ew.supported_openai_params&&ew.supported_openai_params.length>0&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Supported OpenAI Parameters"}),(0,s.jsx)("div",{className:"flex flex-wrap gap-2",children:ew.supported_openai_params.map(e=>(0,s.jsx)(m.Tag,{color:"green",children:e},e))})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Usage Example"}),(0,s.jsx)("div",{className:"bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto",children:(0,s.jsx)("pre",{className:"text-sm",children:(0,N.generateCodeSnippet)({apiKeySource:"custom",accessToken:null,apiKey:"your_api_key",inputMessage:"Hello, how are you?",chatHistory:[{role:"user",content:"Hello, how are you?",isImage:!1}],selectedTags:[],selectedVectorStores:[],selectedGuardrails:[],selectedPolicies:[],selectedMCPServers:[],endpointType:(0,_.getEndpointType)(ew.mode||"chat"),selectedModel:ew.model_group,selectedSdk:"openai"})})}),(0,s.jsx)("div",{className:"mt-2 text-right",children:(0,s.jsx)("button",{onClick:()=>{eU((0,N.generateCodeSnippet)({apiKeySource:"custom",accessToken:null,apiKey:"your_api_key",inputMessage:"Hello, how are you?",chatHistory:[{role:"user",content:"Hello, how are you?",isImage:!1}],selectedTags:[],selectedVectorStores:[],selectedGuardrails:[],selectedPolicies:[],selectedMCPServers:[],endpointType:(0,_.getEndpointType)(ew.mode||"chat"),selectedModel:ew.model_group,selectedSdk:"openai"}))},className:"text-sm text-blue-600 hover:text-blue-800 cursor-pointer",children:"Copy to clipboard"})})]})]})}),(0,s.jsx)(o.Modal,{title:(0,s.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,s.jsx)("span",{children:eC?.name||"Agent Details"}),eC&&(0,s.jsx)(h.Tooltip,{title:"Copy agent name",children:(0,s.jsx)(u.Copy,{onClick:()=>eU(eC.name),className:"cursor-pointer text-gray-500 hover:text-blue-500 w-4 h-4"})})]}),width:1e3,open:ey,footer:null,onOk:()=>{eN(!1),ek(null)},onCancel:()=>{eN(!1),ek(null)},children:eC&&(0,s.jsxs)("div",{className:"space-y-6",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Agent Overview"}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4 mb-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Name:"}),(0,s.jsx)(n.Text,{children:eC.name})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Version:"}),(0,s.jsx)(n.Text,{children:eC.version})]}),(0,s.jsxs)("div",{className:"col-span-2",children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Description:"}),(0,s.jsx)(n.Text,{children:eC.description})]}),eC.url&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"URL:"}),(0,s.jsx)("a",{href:eC.url,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 text-sm break-all",children:eC.url})]})]})]}),eC.capabilities&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Capabilities"}),(0,s.jsx)("div",{className:"flex flex-wrap gap-2",children:Object.entries(eC.capabilities).filter(([e,s])=>!0===s).map(([e])=>(0,s.jsx)(m.Tag,{color:"green",className:"capitalize",children:e},e))})]}),eC.skills&&eC.skills.length>0&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Skills"}),(0,s.jsx)("div",{className:"space-y-4",children:eC.skills.map((e,t)=>(0,s.jsxs)("div",{className:"border border-gray-200 rounded-lg p-4",children:[(0,s.jsx)("div",{className:"flex items-start justify-between mb-2",children:(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium text-base",children:e.name}),(0,s.jsx)(n.Text,{className:"text-sm text-gray-600",children:e.description})]})}),e.tags&&e.tags.length>0&&(0,s.jsx)("div",{className:"flex flex-wrap gap-1 mt-2",children:e.tags.map(e=>(0,s.jsx)(m.Tag,{color:"purple",className:"text-xs",children:e},e))})]},t))})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Input/Output Modes"}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Input Modes:"}),(0,s.jsx)("div",{className:"flex flex-wrap gap-1 mt-1",children:(eC.defaultInputModes??[]).map(e=>(0,s.jsx)(m.Tag,{color:"blue",children:e},e))})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Output Modes:"}),(0,s.jsx)("div",{className:"flex flex-wrap gap-1 mt-1",children:(eC.defaultOutputModes??[]).map(e=>(0,s.jsx)(m.Tag,{color:"blue",children:e},e))})]})]})]}),eC.documentationUrl&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Documentation"}),(0,s.jsxs)("a",{href:eC.documentationUrl,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 flex items-center space-x-2",children:[(0,s.jsx)(a.ExternalLinkIcon,{className:"w-4 h-4"}),(0,s.jsx)("span",{children:"View Documentation"})]})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Usage Example (A2A Protocol)"}),(0,s.jsxs)("div",{className:"mb-4",children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium mb-2 text-gray-700",children:"Step 1: Retrieve Agent Card"}),(0,s.jsx)("div",{className:"bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto",children:(0,s.jsx)("pre",{className:"text-xs",children:`base_url = '${eC.url}' + +resolver = A2ACardResolver( + httpx_client=httpx_client, + base_url=base_url, + # agent_card_path uses default, extended_agent_card_path also uses default +) + +# Fetch Public Agent Card and Initialize Client +final_agent_card_to_use: AgentCard | None = None +_public_card = ( + await resolver.get_agent_card() +) # Fetches from default public path - \`/agents/{agent_id}/\` +final_agent_card_to_use = _public_card + +if _public_card.supports_authenticated_extended_card: + try: + auth_headers_dict = { + 'Authorization': 'Bearer dummy-token-for-extended-card' + } + _extended_card = await resolver.get_agent_card( + relative_card_path=EXTENDED_AGENT_CARD_PATH, + http_kwargs={'headers': auth_headers_dict}, + ) + final_agent_card_to_use = ( + _extended_card # Update to use the extended card + ) + except Exception as e_extended: + logger.warning( + f'Failed to fetch extended agent card: {e_extended}. Will proceed with public card.', + exc_info=True, + )`})}),(0,s.jsx)("div",{className:"mt-2 text-right",children:(0,s.jsx)("button",{onClick:()=>{eU(`from a2a.client import A2ACardResolver, A2AClient +from a2a.types import ( + AgentCard, + MessageSendParams, + SendMessageRequest, + SendStreamingMessageRequest, +) +from a2a.utils.constants import ( + AGENT_CARD_WELL_KNOWN_PATH, + EXTENDED_AGENT_CARD_PATH, +) + +base_url = '${eC.url}' + +resolver = A2ACardResolver( + httpx_client=httpx_client, + base_url=base_url, + # agent_card_path uses default, extended_agent_card_path also uses default +) + +# Fetch Public Agent Card and Initialize Client +final_agent_card_to_use: AgentCard | None = None +_public_card = ( + await resolver.get_agent_card() +) # Fetches from default public path - \`/agents/{agent_id}/\` +final_agent_card_to_use = _public_card + +if _public_card.supports_authenticated_extended_card: + try: + auth_headers_dict = { + 'Authorization': 'Bearer dummy-token-for-extended-card' + } + _extended_card = await resolver.get_agent_card( + relative_card_path=EXTENDED_AGENT_CARD_PATH, + http_kwargs={'headers': auth_headers_dict}, + ) + final_agent_card_to_use = ( + _extended_card # Update to use the extended card + ) + except Exception as e_extended: + logger.warning( + f'Failed to fetch extended agent card: {e_extended}. Will proceed with public card.', + exc_info=True, + )`)},className:"text-sm text-blue-600 hover:text-blue-800 cursor-pointer",children:"Copy to clipboard"})})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-sm font-medium mb-2 text-gray-700",children:"Step 2: Call the Agent"}),(0,s.jsx)("div",{className:"bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto",children:(0,s.jsx)("pre",{className:"text-xs",children:`client = A2AClient( + httpx_client=httpx_client, agent_card=final_agent_card_to_use +) + +send_message_payload: dict[str, Any] = { + 'message': { + 'role': 'user', + 'parts': [ + {'kind': 'text', 'text': 'how much is 10 USD in INR?'} + ], + 'messageId': uuid4().hex, + }, +} +request = SendMessageRequest( + id=str(uuid4()), params=MessageSendParams(**send_message_payload) +) + +response = await client.send_message(request) +print(response.model_dump(mode='json', exclude_none=True))`})}),(0,s.jsx)("div",{className:"mt-2 text-right",children:(0,s.jsx)("button",{onClick:()=>{eU(`client = A2AClient( + httpx_client=httpx_client, agent_card=final_agent_card_to_use +) + +send_message_payload: dict[str, Any] = { + 'message': { + 'role': 'user', + 'parts': [ + {'kind': 'text', 'text': 'how much is 10 USD in INR?'} + ], + 'messageId': uuid4().hex, + }, +} +request = SendMessageRequest( + id=str(uuid4()), params=MessageSendParams(**send_message_payload) +) + +response = await client.send_message(request) +print(response.model_dump(mode='json', exclude_none=True))`)},className:"text-sm text-blue-600 hover:text-blue-800 cursor-pointer",children:"Copy to clipboard"})})]})]})]})}),(0,s.jsx)(o.Modal,{title:(0,s.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,s.jsx)("span",{children:eA?.server_name||"MCP Server Details"}),eA&&(0,s.jsx)(h.Tooltip,{title:"Copy server name",children:(0,s.jsx)(u.Copy,{onClick:()=>eU(eA.server_name),className:"cursor-pointer text-gray-500 hover:text-blue-500 w-4 h-4"})})]}),width:1e3,open:e_,footer:null,onOk:()=>{eT(!1),eM(null)},onCancel:()=>{eT(!1),eM(null)},children:eA&&(0,s.jsxs)("div",{className:"space-y-6",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Server Overview"}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4 mb-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Server Name:"}),(0,s.jsx)(n.Text,{children:eA.server_name})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Transport:"}),(0,s.jsx)(m.Tag,{color:"blue",children:eA.transport})]}),eA.alias&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Alias:"}),(0,s.jsx)(n.Text,{children:eA.alias})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Auth Type:"}),(0,s.jsx)(m.Tag,{color:"none"===eA.auth_type?"gray":"green",children:eA.auth_type})]}),(0,s.jsxs)("div",{className:"col-span-2",children:[(0,s.jsx)(n.Text,{className:"font-medium",children:"Description:"}),(0,s.jsx)(n.Text,{children:eA.mcp_info?.description||"-"})]})]})]}),eA.mcp_info&&Object.keys(eA.mcp_info).length>0&&(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Additional Information"}),(0,s.jsx)("div",{className:"bg-gray-50 p-4 rounded-lg",children:(0,s.jsx)("pre",{className:"text-xs overflow-x-auto",children:JSON.stringify(eA.mcp_info,null,2)})})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(n.Text,{className:"text-lg font-semibold mb-4",children:"Usage Example"}),(0,s.jsx)("div",{className:"bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto",children:(0,s.jsx)("pre",{className:"text-sm",children:`# Using MCP Server with Python FastMCP + +from fastmcp import Client +import asyncio + +# Standard MCP configuration +config = { + "mcpServers": { + "${eA.server_name}": { + "url": "${(0,v.getProxyBaseUrl)()}/${eA.server_name}/mcp", + "headers": { + "x-litellm-api-key": "Bearer sk-1234" + } + } + } +} + +# Create a client that connects to the server +client = Client(config) + +async def main(): + async with client: + # List available tools + tools = await client.list_tools() + print(f"Available tools: {[tool.name for tool in tools]}") + + # Call a tool + response = await client.call_tool( + name="tool_name", + arguments={"arg": "value"} + ) + print(f"Response: {response}") + +if __name__ == "__main__": + asyncio.run(main())`})}),(0,s.jsx)("div",{className:"mt-2 text-right",children:(0,s.jsx)("button",{onClick:()=>{eU(`# Using MCP Server with Python FastMCP + +from fastmcp import Client +import asyncio + +# Standard MCP configuration +config = { + "mcpServers": { + "${eA.server_name}": { + "url": "${(0,v.getProxyBaseUrl)()}/${eA.server_name}/mcp", + "headers": { + "x-litellm-api-key": "Bearer sk-1234" + } + } + } +} + +# Create a client that connects to the server +client = Client(config) + +async def main(): + async with client: + # List available tools + tools = await client.list_tools() + print(f"Available tools: {[tool.name for tool in tools]}") + + # Call a tool + response = await client.call_tool( + name="tool_name", + arguments={"arg": "value"} + ) + print(f"Response: {response}") + +if __name__ == "__main__": + asyncio.run(main())`)},className:"text-sm text-blue-600 hover:text-blue-800 cursor-pointer",children:"Copy to clipboard"})})]})]})})]})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0ubbv4xlta87q.js b/litellm/proxy/_experimental/out/_next/static/chunks/0ubbv4xlta87q.js new file mode 100644 index 00000000000..30952344ac3 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0ubbv4xlta87q.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,250980,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["PlusCircleIcon",0,r],250980)},309426,e=>{"use strict";var t=e.i(290571),r=e.i(444755),l=e.i(673706),a=e.i(271645),s=e.i(46757);let o=(0,l.makeClassName)("Col"),n=a.default.forwardRef((e,l)=>{let n,i,d,c,{numColSpan:u=1,numColSpanSm:m,numColSpanMd:g,numColSpanLg:p,children:h,className:f}=e,x=(0,t.__rest)(e,["numColSpan","numColSpanSm","numColSpanMd","numColSpanLg","children","className"]),b=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"";return a.default.createElement("div",Object.assign({ref:l,className:(0,r.tremorTwMerge)(o("root"),(n=b(u,s.colSpan),i=b(m,s.colSpanSm),d=b(g,s.colSpanMd),c=b(p,s.colSpanLg),(0,r.tremorTwMerge)(n,i,d,c)),f)},x),h)});n.displayName="Col",e.s(["Col",0,n],309426)},950724,(e,t,r)=>{t.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},100236,(e,t,r)=>{t.exports=e.g&&e.g.Object===Object&&e.g},139088,(e,t,r)=>{var l=e.r(100236),a="object"==typeof self&&self&&self.Object===Object&&self;t.exports=l||a||Function("return this")()},631926,(e,t,r)=>{var l=e.r(139088);t.exports=function(){return l.Date.now()}},748891,(e,t,r)=>{var l=/\s/;t.exports=function(e){for(var t=e.length;t--&&l.test(e.charAt(t)););return t}},830364,(e,t,r)=>{var l=e.r(748891),a=/^\s+/;t.exports=function(e){return e?e.slice(0,l(e)+1).replace(a,""):e}},630353,(e,t,r)=>{t.exports=e.r(139088).Symbol},243436,(e,t,r)=>{var l=e.r(630353),a=Object.prototype,s=a.hasOwnProperty,o=a.toString,n=l?l.toStringTag:void 0;t.exports=function(e){var t=s.call(e,n),r=e[n];try{e[n]=void 0;var l=!0}catch(e){}var a=o.call(e);return l&&(t?e[n]=r:delete e[n]),a}},223243,(e,t,r)=>{var l=Object.prototype.toString;t.exports=function(e){return l.call(e)}},377684,(e,t,r)=>{var l=e.r(630353),a=e.r(243436),s=e.r(223243),o=l?l.toStringTag:void 0;t.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":o&&o in Object(e)?a(e):s(e)}},877289,(e,t,r)=>{t.exports=function(e){return null!=e&&"object"==typeof e}},361884,(e,t,r)=>{var l=e.r(377684),a=e.r(877289);t.exports=function(e){return"symbol"==typeof e||a(e)&&"[object Symbol]"==l(e)}},773759,(e,t,r)=>{var l=e.r(830364),a=e.r(950724),s=e.r(361884),o=0/0,n=/^[-+]0x[0-9a-f]+$/i,i=/^0b[01]+$/i,d=/^0o[0-7]+$/i,c=parseInt;t.exports=function(e){if("number"==typeof e)return e;if(s(e))return o;if(a(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=a(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=l(e);var r=i.test(e);return r||d.test(e)?c(e.slice(2),r?2:8):n.test(e)?o:+e}},374009,(e,t,r)=>{var l=e.r(950724),a=e.r(631926),s=e.r(773759),o=Math.max,n=Math.min;t.exports=function(e,t,r){var i,d,c,u,m,g,p=0,h=!1,f=!1,x=!0;if("function"!=typeof e)throw TypeError("Expected a function");function b(t){var r=i,l=d;return i=d=void 0,p=t,u=e.apply(l,r)}function v(e){var r=e-g,l=e-p;return void 0===g||r>=t||r<0||f&&l>=c}function y(){var e,r,l,s=a();if(v(s))return w(s);m=setTimeout(y,(e=s-g,r=s-p,l=t-e,f?n(l,c-r):l))}function w(e){return(m=void 0,x&&i)?b(e):(i=d=void 0,u)}function C(){var e,r=a(),l=v(r);if(i=arguments,d=this,g=r,l){if(void 0===m)return p=e=g,m=setTimeout(y,t),h?b(e):u;if(f)return clearTimeout(m),m=setTimeout(y,t),b(g)}return void 0===m&&(m=setTimeout(y,t)),u}return t=s(t)||0,l(r)&&(h=!!r.leading,c=(f="maxWait"in r)?o(s(r.maxWait)||0,t):c,x="trailing"in r?!!r.trailing:x),C.cancel=function(){void 0!==m&&clearTimeout(m),p=0,i=g=d=m=void 0},C.flush=function(){return void 0===m?u:w(a())},C}},435451,e=>{"use strict";var t=e.i(843476),r=e.i(290571),l=e.i(271645);let a=e=>{var t=(0,r.__rest)(e,[]);return l.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),l.default.createElement("path",{d:"M12 4v16m8-8H4"}))},s=e=>{var t=(0,r.__rest)(e,[]);return l.default.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),l.default.createElement("path",{d:"M20 12H4"}))};var o=e.i(444755),n=e.i(673706),i=e.i(677955);let d="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",c="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",u=l.default.forwardRef((e,t)=>{let{onSubmit:u,enableStepper:m=!0,disabled:g,onValueChange:p,onChange:h}=e,f=(0,r.__rest)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),x=(0,l.useRef)(null),[b,v]=l.default.useState(!1),y=l.default.useCallback(()=>{v(!0)},[]),w=l.default.useCallback(()=>{v(!1)},[]),[C,k]=l.default.useState(!1),N=l.default.useCallback(()=>{k(!0)},[]),j=l.default.useCallback(()=>{k(!1)},[]);return l.default.createElement(i.default,Object.assign({type:"number",ref:(0,n.mergeRefs)([x,t]),disabled:g,makeInputClassName:(0,n.makeClassName)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null==(t=x.current)?void 0:t.value;null==u||u(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&y(),"ArrowUp"===e.key&&N()},onKeyUp:e=>{"ArrowDown"===e.key&&w(),"ArrowUp"===e.key&&j()},onChange:e=>{g||(null==p||p(parseFloat(e.target.value)),null==h||h(e))},stepper:m?l.default.createElement("div",{className:(0,o.tremorTwMerge)("flex justify-center align-middle")},l.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;g||(null==(e=x.current)||e.stepDown(),null==(t=x.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,o.tremorTwMerge)(!g&&c,d,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},l.default.createElement(s,{"data-testid":"step-down",className:(b?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),l.default.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;g||(null==(e=x.current)||e.stepUp(),null==(t=x.current)||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,o.tremorTwMerge)(!g&&c,d,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},l.default.createElement(a,{"data-testid":"step-up",className:(C?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},f))});u.displayName="NumberInput",e.s(["default",0,({step:e=.01,style:r={width:"100%"},placeholder:l="Enter a numerical value",min:a,max:s,onChange:o,...n})=>(0,t.jsx)(u,{onWheel:e=>e.currentTarget.blur(),step:e,style:r,placeholder:l,min:a,max:s,onChange:o,...n})],435451)},860585,e=>{"use strict";var t=e.i(843476),r=e.i(199133);let{Option:l}=r.Select;e.s(["default",0,({value:e,onChange:a,className:s="",style:o={}})=>(0,t.jsxs)(r.Select,{style:{width:"100%",...o},value:e||void 0,onChange:a,className:s,placeholder:"n/a",allowClear:!0,children:[(0,t.jsx)(l,{value:"1h",children:"hourly"}),(0,t.jsx)(l,{value:"24h",children:"daily"}),(0,t.jsx)(l,{value:"7d",children:"weekly"}),(0,t.jsx)(l,{value:"30d",children:"monthly"})]}),"getBudgetDurationLabel",0,e=>e?({"1h":"hourly","24h":"daily","7d":"weekly","30d":"monthly"})[e]||e:"Not set"])},213205,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M678.3 642.4c24.2-13 51.9-20.4 81.4-20.4h.1c3 0 4.4-3.6 2.2-5.6a371.67 371.67 0 00-103.7-65.8c-.4-.2-.8-.3-1.2-.5C719.2 505 759.6 431.7 759.6 349c0-137-110.8-248-247.5-248S264.7 212 264.7 349c0 82.7 40.4 156 102.6 201.1-.4.2-.8.3-1.2.5-44.7 18.9-84.8 46-119.3 80.6a373.42 373.42 0 00-80.4 119.5A373.6 373.6 0 00137 888.8a8 8 0 008 8.2h59.9c4.3 0 7.9-3.5 8-7.8 2-77.2 32.9-149.5 87.6-204.3C357 628.2 432.2 597 512.2 597c56.7 0 111.1 15.7 158 45.1a8.1 8.1 0 008.1.3zM512.2 521c-45.8 0-88.9-17.9-121.4-50.4A171.2 171.2 0 01340.5 349c0-45.9 17.9-89.1 50.3-121.6S466.3 177 512.2 177s88.9 17.9 121.4 50.4A171.2 171.2 0 01683.9 349c0 45.9-17.9 89.1-50.3 121.6C601.1 503.1 558 521 512.2 521zM880 759h-84v-84c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v84h-84c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h84v84c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-84h84c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"}}]},name:"user-add",theme:"outlined"};var a=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(a.default,(0,t.default)({},e,{ref:s,icon:l}))});e.s(["UserAddOutlined",0,s],213205)},355619,e=>{"use strict";var t=e.i(602869);let r=async(e,r,l)=>{try{if(null===e||null===r)return;if(null!==l){let a=(await (0,t.modelAvailableCall)(l,e,r,!0,null,!0)).data.map(e=>e.id),s=[],o=[];return a.forEach(e=>{e.endsWith("/*")?s.push(e):o.push(e)}),[...s,...o]}}catch(e){console.error("Error fetching user models:",e)}};e.s(["excludeProxyWideSentinel",0,e=>e.filter(e=>"all-proxy-models"!==e),"fetchAvailableModelsForTeamOrKey",0,r,"getModelDisplayName",0,e=>{if("all-proxy-models"===e)return"All Proxy Models";if(e.endsWith("/*")){let t=e.replace("/*","");return`All ${t} models`}return e},"hasAllModelsSentinel",0,e=>e.includes("all-proxy-models")||e.includes("all-team-models"),"unfurlWildcardModelsInList",0,(e,t)=>{let r=[],l=[];return e.forEach(e=>{if(e.endsWith("/*")){let a=e.replace("/*",""),s=t.filter(e=>e.startsWith(a+"/"));l.push(...s),r.push(e)}else l.push(e)}),[...r,...l].filter((e,t,r)=>r.indexOf(e)===t)}])},350967,46757,e=>{"use strict";var t=e.i(290571),r=e.i(444755),l=e.i(673706),a=e.i(271645);let s={0:"grid-cols-none",1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",5:"grid-cols-5",6:"grid-cols-6",7:"grid-cols-7",8:"grid-cols-8",9:"grid-cols-9",10:"grid-cols-10",11:"grid-cols-11",12:"grid-cols-12"},o={0:"sm:grid-cols-none",1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",5:"sm:grid-cols-5",6:"sm:grid-cols-6",7:"sm:grid-cols-7",8:"sm:grid-cols-8",9:"sm:grid-cols-9",10:"sm:grid-cols-10",11:"sm:grid-cols-11",12:"sm:grid-cols-12"},n={0:"md:grid-cols-none",1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",5:"md:grid-cols-5",6:"md:grid-cols-6",7:"md:grid-cols-7",8:"md:grid-cols-8",9:"md:grid-cols-9",10:"md:grid-cols-10",11:"md:grid-cols-11",12:"md:grid-cols-12"},i={0:"lg:grid-cols-none",1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6",7:"lg:grid-cols-7",8:"lg:grid-cols-8",9:"lg:grid-cols-9",10:"lg:grid-cols-10",11:"lg:grid-cols-11",12:"lg:grid-cols-12"};e.s(["colSpan",0,{1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12",13:"col-span-13"},"colSpanLg",0,{1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12",13:"lg:col-span-13"},"colSpanMd",0,{1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12",13:"md:col-span-13"},"colSpanSm",0,{1:"sm:col-span-1",2:"sm:col-span-2",3:"sm:col-span-3",4:"sm:col-span-4",5:"sm:col-span-5",6:"sm:col-span-6",7:"sm:col-span-7",8:"sm:col-span-8",9:"sm:col-span-9",10:"sm:col-span-10",11:"sm:col-span-11",12:"sm:col-span-12",13:"sm:col-span-13"},"gridCols",0,s,"gridColsLg",0,i,"gridColsMd",0,n,"gridColsSm",0,o],46757);let d=(0,l.makeClassName)("Grid"),c=(e,t)=>e&&Object.keys(t).includes(String(e))?t[e]:"",u=a.default.forwardRef((e,l)=>{let{numItems:u=1,numItemsSm:m,numItemsMd:g,numItemsLg:p,children:h,className:f}=e,x=(0,t.__rest)(e,["numItems","numItemsSm","numItemsMd","numItemsLg","children","className"]),b=c(u,s),v=c(m,o),y=c(g,n),w=c(p,i),C=(0,r.tremorTwMerge)(b,v,y,w);return a.default.createElement("div",Object.assign({ref:l,className:(0,r.tremorTwMerge)(d("root"),"grid",C,f)},x),h)});u.displayName="Grid",e.s(["Grid",0,u],350967)},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},500727,e=>{"use strict";var t=e.i(266027),r=e.i(243652),l=e.i(602869),a=e.i(135214);let s=(0,r.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:r}=(0,a.default)();return(0,t.useQuery)({queryKey:s.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,l.fetchMCPServers)(r,e),enabled:!!r})}])},699857,e=>{"use strict";var t=e.i(266027),r=e.i(243652),l=e.i(602869),a=e.i(135214);let s=(0,r.createQueryKeys)("mcpToolsets");e.s(["useMCPToolsets",0,()=>{let{accessToken:e}=(0,a.default)();return(0,t.useQuery)({queryKey:s.list(),queryFn:async()=>await (0,l.fetchMCPToolsets)(e),enabled:!!e})}])},916940,e=>{"use strict";var t=e.i(843476),r=e.i(271645),l=e.i(199133),a=e.i(602869);e.s(["default",0,({onChange:e,value:s,className:o,accessToken:n,placeholder:i="Select vector stores",disabled:d=!1})=>{let[c,u]=(0,r.useState)([]),[m,g]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{(async()=>{if(n){g(!0);try{let e=await (0,a.vectorStoreListCall)(n);e.data&&u(e.data)}catch(e){console.error("Error fetching vector stores:",e)}finally{g(!1)}}})()},[n]),(0,t.jsx)("div",{children:(0,t.jsx)(l.Select,{mode:"multiple",placeholder:i,onChange:e,value:s,loading:m,className:o,allowClear:!0,options:c.map(e=>({label:`${e.vector_store_name||e.vector_store_id} (${e.vector_store_id})`,value:e.vector_store_id,title:e.vector_store_description||e.vector_store_id})),optionFilterProp:"label",showSearch:!0,style:{width:"100%"},disabled:d})})}])},75921,e=>{"use strict";var t=e.i(843476),r=e.i(266027),l=e.i(243652),a=e.i(602869),s=e.i(135214);let o=(0,l.createQueryKeys)("mcpAccessGroups");var n=e.i(500727),i=e.i(699857),d=e.i(199133),c=e.i(234713);let u="toolset:";e.s(["default",0,({onChange:e,value:l,className:m,accessToken:g,placeholder:p="Select MCP servers",disabled:h=!1,teamId:f,allowNoMcpServers:x=!1,allowAllProxyMcpServers:b=!1})=>{let{data:v=[],isLoading:y}=(0,n.useMCPServers)(f),{data:w=[],isLoading:C}=(()=>{let{accessToken:e}=(0,s.default)();return(0,r.useQuery)({queryKey:o.list({}),queryFn:async()=>await (0,a.fetchMCPAccessGroups)(e),enabled:!!e})})(),{data:k=[],isLoading:N}=(0,i.useMCPToolsets)(),j=new Set(w),S=[...w.map(e=>({label:e,value:e,type:"accessGroup",searchText:`${e} Access Group`})),...v.map(e=>({label:`${e.server_name||e.server_id} (${e.server_id})`,value:e.server_id,type:"server",searchText:`${e.server_name||e.server_id} ${e.server_id} MCP Server`})),...k.map(e=>({label:e.toolset_name,value:`${u}${e.toolset_id}`,type:"toolset",searchText:`${e.toolset_name} ${e.toolset_id} Toolset`}))],_={accessGroup:"#52c41a",server:"#1890ff",toolset:"#722ed1"},M={accessGroup:"Access Group",server:"MCP Server",toolset:"Toolset"},E=[...l?.servers||[],...l?.accessGroups||[],...(l?.toolsets||[]).map(e=>`${u}${e}`)],T=x&&E.includes(c.NO_MCP_SERVERS_SENTINEL),P=E.includes(c.ALL_PROXY_MCP_SERVERS_SENTINEL);return(0,t.jsx)("div",{children:(0,t.jsxs)(d.Select,{mode:"multiple",placeholder:p,onChange:t=>{if(b&&t.includes(c.ALL_PROXY_MCP_SERVERS_SENTINEL))return void e({servers:[c.ALL_PROXY_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});if(x&&t.includes(c.NO_MCP_SERVERS_SENTINEL))return void e({servers:[c.NO_MCP_SERVERS_SENTINEL],accessGroups:[],toolsets:[]});let r=t.filter(e=>e.startsWith(u)).map(e=>e.slice(u.length)),l=t.filter(e=>!e.startsWith(u));e({servers:l.filter(e=>!j.has(e)),accessGroups:l.filter(e=>j.has(e)),toolsets:r})},value:E,loading:y||C||N,className:m,allowClear:!0,showSearch:!0,style:{width:"100%"},disabled:h,filterOption:(e,t)=>t?.value===c.NO_MCP_SERVERS_SENTINEL||t?.value===c.ALL_PROXY_MCP_SERVERS_SENTINEL||(S.find(e=>e.value===t?.value)?.searchText||"").toLowerCase().includes(e.toLowerCase()),children:[(b||P)&&(0,t.jsx)(d.Select.Option,{value:c.ALL_PROXY_MCP_SERVERS_SENTINEL,label:"All Proxy MCP Servers",children:(0,t.jsx)("span",{style:{color:"#1890ff",fontWeight:500},children:"All Proxy MCP Servers"})},c.ALL_PROXY_MCP_SERVERS_SENTINEL),x&&(0,t.jsx)(d.Select.Option,{value:c.NO_MCP_SERVERS_SENTINEL,label:"No MCP Servers",children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{flex:1},children:"No MCP Servers"}),(0,t.jsx)("span",{style:{color:"#8c8c8c",fontSize:"12px",fontWeight:500,opacity:.8},children:"Block all"})]})},c.NO_MCP_SERVERS_SENTINEL),S.map(e=>(0,t.jsx)(d.Select.Option,{value:e.value,label:e.label,disabled:T||P,children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[(0,t.jsx)("span",{style:{display:"inline-block",width:8,height:8,borderRadius:"50%",background:_[e.type],flexShrink:0}}),(0,t.jsx)("span",{style:{flex:1},children:e.label}),(0,t.jsx)("span",{style:{color:_[e.type],fontSize:"12px",fontWeight:500,opacity:.8},children:M[e.type]})]})},e.value))]})})}],75921)},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),l=e.i(271645);let a=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],s=e=>({_s:e,status:a[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),o=e=>e?6:5,n=(e,t,r,l,a)=>{clearTimeout(l.current);let o=s(e);t(o),r.current=o,a&&a({current:o})};var i=e.i(480731),d=e.i(444755),c=e.i(673706);let u=e=>{var r=(0,t.__rest)(e,[]);return l.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),l.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),l.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var m=e.i(95779);let g={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},p=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,m.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,m.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,m.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,m.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},h=(0,c.makeClassName)("Button"),f=({loading:e,iconSize:t,iconPosition:r,Icon:a,needMargin:s,transitionStatus:o})=>{let n=s?r===i.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),m={default:c,entering:c,entered:t,exiting:t,exited:c};return e?l.default.createElement(u,{className:(0,d.tremorTwMerge)(h("icon"),"animate-spin shrink-0",n,m.default,m[o]),style:{transition:"width 150ms"}}):l.default.createElement(a,{className:(0,d.tremorTwMerge)(h("icon"),"shrink-0",t,n)})},x=l.default.forwardRef((e,a)=>{let{icon:u,iconPosition:m=i.HorizontalPositions.Left,size:x=i.Sizes.SM,color:b,variant:v="primary",disabled:y,loading:w=!1,loadingText:C,children:k,tooltip:N,className:j}=e,S=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),_=w||y,M=void 0!==u||w,E=w&&C,T=!(!k&&!E),P=(0,d.tremorTwMerge)(g[x].height,g[x].width),R="light"!==v?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",L=p(v,b),B=("light"!==v?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[x],{tooltipProps:O,getReferenceProps:A}=(0,r.useTooltip)(300),[z,I]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:a,timeout:i,initialEntered:d,mountOnEnter:c,unmountOnExit:u,onStateChange:m}={})=>{let[g,p]=(0,l.useState)(()=>s(d?2:o(c))),h=(0,l.useRef)(g),f=(0,l.useRef)(0),[x,b]="object"==typeof i?[i.enter,i.exit]:[i,i],v=(0,l.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return o(t)}})(h.current._s,u);e&&n(e,p,h,f,m)},[m,u]);return[g,(0,l.useCallback)(l=>{let s=e=>{switch(n(e,p,h,f,m),e){case 1:x>=0&&(f.current=((...e)=>setTimeout(...e))(v,x));break;case 4:b>=0&&(f.current=((...e)=>setTimeout(...e))(v,b));break;case 0:case 3:f.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||s(e+1)},0)}},i=h.current.isEnter;"boolean"!=typeof l&&(l=!i),l?i||s(e?+!r:2):i&&s(t?a?3:4:o(u))},[v,m,e,t,r,a,x,b,u]),v]})({timeout:50});return(0,l.useEffect)(()=>{I(w)},[w]),l.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([a,O.refs.setReference]),className:(0,d.tremorTwMerge)(h("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",R,B.paddingX,B.paddingY,B.fontSize,L.textColor,L.bgColor,L.borderColor,L.hoverBorderColor,_?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(p(v,b).hoverTextColor,p(v,b).hoverBgColor,p(v,b).hoverBorderColor),j),disabled:_},A,S),l.default.createElement(r.default,Object.assign({text:N},O)),M&&m!==i.HorizontalPositions.Right?l.default.createElement(f,{loading:w,iconSize:P,iconPosition:m,Icon:u,transitionStatus:z.status,needMargin:T}):null,E||k?l.default.createElement("span",{className:(0,d.tremorTwMerge)(h("text"),"text-tremor-default whitespace-nowrap")},E?C:k):null,M&&m===i.HorizontalPositions.Right?l.default.createElement(f,{loading:w,iconSize:P,iconPosition:m,Icon:u,transitionStatus:z.status,needMargin:T}):null)});x.displayName="Button",e.s(["Button",0,x],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),l=e.i(673706),a=e.i(271645);let s=a.default.forwardRef((e,s)=>{let{color:o,className:n,children:i}=e;return a.default.createElement("p",{ref:s,className:(0,r.tremorTwMerge)("text-tremor-default",o?(0,l.getColorClassNames)(o,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),n)},i)});s.displayName="Text",e.s(["default",0,s],936325),e.s(["Text",0,s],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),l=e.i(480731),a=e.i(95779),s=e.i(444755),o=e.i(673706);let n=(0,o.makeClassName)("Card"),i=r.default.forwardRef((e,i)=>{let{decoration:d="",decorationColor:c,children:u,className:m}=e,g=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:i,className:(0,s.tremorTwMerge)(n("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,o.getColorClassNames)(c,a.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case l.HorizontalPositions.Left:return"border-l-4";case l.VerticalPositions.Top:return"border-t-4";case l.HorizontalPositions.Right:return"border-r-4";case l.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),m)},g),u)});i.displayName="Card",e.s(["Card",0,i],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),l=e.i(444755),a=e.i(673706),s=e.i(271645);let o=s.default.forwardRef((e,o)=>{let{color:n,children:i,className:d}=e,c=(0,t.__rest)(e,["color","children","className"]);return s.default.createElement("p",Object.assign({ref:o,className:(0,l.tremorTwMerge)("font-medium text-tremor-title",n?(0,a.getColorClassNames)(n,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),i)});o.displayName="Title",e.s(["Title",0,o],629569)},409797,e=>{"use strict";var t=e.i(631171);e.s(["ChevronDownIcon",()=>t.default])},361653,e=>{"use strict";let t=(0,e.i(475254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["default",0,t])},983561,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M300 328a60 60 0 10120 0 60 60 0 10-120 0zM852 64H172c-17.7 0-32 14.3-32 32v660c0 17.7 14.3 32 32 32h680c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-32 660H204V128h616v596zM604 328a60 60 0 10120 0 60 60 0 10-120 0zm250.2 556H169.8c-16.5 0-29.8 14.3-29.8 32v36c0 4.4 3.3 8 7.4 8h729.1c4.1 0 7.4-3.6 7.4-8v-36c.1-17.7-13.2-32-29.7-32zM664 508H360c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"robot",theme:"outlined"};var a=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(a.default,(0,t.default)({},e,{ref:s,icon:l}))});e.s(["RobotOutlined",0,s],983561)},797672,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"}))});e.s(["PencilIcon",0,r],797672)},992619,e=>{"use strict";var t=e.i(843476),r=e.i(271645),l=e.i(779241),a=e.i(599724),s=e.i(199133),o=e.i(983561),n=e.i(695411);e.s(["default",0,({accessToken:e,value:i,placeholder:d="Select a Model",onChange:c,disabled:u=!1,style:m,className:g,showLabel:p=!0,labelText:h="Select Model"})=>{let[f,x]=(0,r.useState)(i),[b,v]=(0,r.useState)(!1),[y,w]=(0,r.useState)([]),C=(0,r.useRef)(null);return(0,r.useEffect)(()=>{x(i)},[i]),(0,r.useEffect)(()=>{e&&(async()=>{try{let t=await (0,n.fetchAvailableModels)(e);t.length>0&&w(t)}catch(e){console.error("Error fetching model info:",e)}})()},[e]),(0,t.jsxs)("div",{children:[p&&(0,t.jsxs)(a.Text,{className:"font-medium block mb-2 text-gray-700 flex items-center",children:[(0,t.jsx)(o.RobotOutlined,{className:"mr-2"})," ",h]}),(0,t.jsx)(s.Select,{value:f,placeholder:d,onChange:e=>{"custom"===e?(v(!0),x(void 0)):(v(!1),x(e),c&&c(e))},options:[...Array.from(new Set(y.map(e=>e.model_group))).map((e,t)=>({value:e,label:e,key:t})),{value:"custom",label:"Enter custom model",key:"custom"}],style:{width:"100%",...m},showSearch:!0,className:`rounded-md ${g||""}`,disabled:u}),b&&(0,t.jsx)(l.TextInput,{className:"mt-2",placeholder:"Enter custom model name",onValueChange:e=>{C.current&&clearTimeout(C.current),C.current=setTimeout(()=>{x(e),c&&c(e)},500)},disabled:u})]})}])},91739,e=>{"use strict";var t=e.i(544195);e.s(["Radio",()=>t.default])},988297,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 4v16m8-8H4"}))});e.s(["PlusIcon",0,r],988297)},531516,696609,e=>{"use strict";var t=e.i(843476),r=e.i(271645),l=e.i(536916),a=e.i(599724),s=e.i(409797),o=e.i(246349),o=o;let n=/\b(delete|remove|destroy|purge|drop|erase|unlink)\b/i,i=/\b(create|add|insert|new|post|submit|register|make|generate|write|upload)\b/i,d=/\b(update|edit|modify|change|patch|put|set|rename|move|transform)\b/i,c=/\b(get|read|list|fetch|search|find|query|retrieve|show|view|check|describe|info)\b/i;function u(e,t=""){let r=e.toLowerCase();if(c.test(r))return"read";if(n.test(r))return"delete";if(d.test(r))return"update";if(i.test(r))return"create";if(t){let e=t.toLowerCase();if(c.test(e))return"read";if(n.test(e))return"delete";if(d.test(e))return"update";if(i.test(e))return"create"}return"unknown"}function m(e){let t={read:[],create:[],update:[],delete:[],unknown:[]};for(let r of e)t[u(r.name,r.description)].push(r);return t}let g={read:{label:"Read",description:"Safe operations — fetch, list, search. No side effects.",risk:"low"},create:{label:"Create",description:"Add new resources — insert, upload, register.",risk:"medium"},update:{label:"Update",description:"Modify existing resources — edit, patch, rename.",risk:"medium"},delete:{label:"Delete",description:"Destructive operations — remove, purge, destroy.",risk:"high"},unknown:{label:"Other",description:"Operations that could not be automatically classified.",risk:"unknown"}};e.s(["CRUD_GROUP_META",0,g,"classifyToolOp",0,u,"groupToolsByCrud",0,m],696609);let p=["read","create","update","delete","unknown"],h={low:"bg-green-100 text-green-800",medium:"bg-yellow-100 text-yellow-800",high:"bg-red-100 text-red-800 font-semibold",unknown:"bg-gray-100 text-gray-700"},f={read:"border-green-200",create:"border-blue-200",update:"border-yellow-200",delete:"border-red-300",unknown:"border-gray-200"},x={read:"bg-green-50",create:"bg-blue-50",update:"bg-yellow-50",delete:"bg-red-50",unknown:"bg-gray-50"};e.s(["default",0,({tools:e,value:n,onChange:i,readOnly:d=!1,searchFilter:c=""})=>{let[u,b]=(0,r.useState)({read:!1,create:!1,update:!1,delete:!1,unknown:!0}),v=(0,r.useMemo)(()=>m(e),[e]),y=(0,r.useMemo)(()=>new Set(void 0===n?e.map(e=>e.name):n),[n,e]),w=e=>{if(d)return;let t=new Set(y);t.has(e)?t.delete(e):t.add(e),i(Array.from(t))};return 0===e.length?null:(0,t.jsx)("div",{className:"space-y-3",children:p.map(e=>{let r,n=v[e];if(0===n.length)return null;if(c){let e=c.toLowerCase();if(!n.some(t=>t.name.toLowerCase().includes(e)||(t.description??"").toLowerCase().includes(e)))return null}let m=g[e],p=(r=v[e]).length>0&&r.every(e=>y.has(e.name)),C=(e=>{let t=v[e];if(0===t.length)return!1;let r=t.filter(e=>y.has(e.name)).length;return r>0&&r{b(t=>({...t,[e]:!t[e]}))},children:[k?(0,t.jsx)(o.default,{className:"w-4 h-4 text-gray-500 shrink-0"}):(0,t.jsx)(s.ChevronDownIcon,{className:"w-4 h-4 text-gray-500 shrink-0"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900 text-sm",children:m.label}),(0,t.jsx)("span",{className:`text-xs px-2 py-0.5 rounded-full ${h[m.risk]}`,children:"high"===m.risk?"High Risk":"medium"===m.risk?"Medium Risk":"low"===m.risk?"Safe":"Unclassified"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 ml-1",children:[n.filter(e=>y.has(e.name)).length,"/",n.length," allowed"]})]}),!d&&(0,t.jsxs)("div",{className:"flex items-center gap-2 ml-4",children:[(0,t.jsx)(a.Text,{className:"text-xs text-gray-500",children:p?"All on":C?"Partial":"All off"}),(0,t.jsx)(l.Checkbox,{checked:p,indeterminate:C,onChange:t=>((e,t)=>{if(d)return;let r=new Set(y);for(let l of v[e])t?r.add(l.name):r.delete(l.name);i(Array.from(r))})(e,t.target.checked),onClick:e=>e.stopPropagation()})]})]}),!k&&(0,t.jsx)("div",{className:"px-4 pt-2 pb-1 text-xs text-gray-500 bg-white border-b border-gray-100",children:m.description}),!k&&(0,t.jsx)("div",{className:"bg-white divide-y divide-gray-50",children:n.filter(e=>!c||e.name.toLowerCase().includes(c.toLowerCase())||(e.description??"").toLowerCase().includes(c.toLowerCase())).map(e=>{let r,s=(r=e.name,y.has(r));return(0,t.jsxs)("div",{className:`flex items-start gap-3 px-4 py-2.5 transition-colors hover:bg-gray-50 ${!d?"cursor-pointer":""} ${s?"":"opacity-60"}`,onClick:()=>w(e.name),children:[(0,t.jsx)(l.Checkbox,{checked:s,onChange:()=>w(e.name),disabled:d,onClick:e=>e.stopPropagation()}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)(a.Text,{className:"font-medium text-gray-900 text-sm",children:e.name}),e.description&&(0,t.jsx)(a.Text,{className:"text-xs text-gray-500 mt-0.5 leading-snug",children:e.description})]}),(0,t.jsx)("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${s?"bg-green-100 text-green-700":"bg-gray-100 text-gray-500"}`,children:s?"on":"off"})]},e.name)})})]},e)})})}],531516)},263147,e=>{"use strict";var t=e.i(266027),r=e.i(243652),l=e.i(602869),a=e.i(431703),s=e.i(708347),o=e.i(135214);let n=(0,r.createQueryKeys)("accessGroups"),i=async e=>{let t=(0,l.getProxyBaseUrl)(),r=`${t}/v1/access_group`,s=await fetch(r,{method:"GET",headers:{[(0,l.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!s.ok){let e=await s.json(),t=(0,a.deriveErrorMessage)(e);throw(0,l.handleError)(t),Error(t)}return s.json()};e.s(["accessGroupKeys",0,n,"useAccessGroups",0,()=>{let{accessToken:e,userRole:r}=(0,o.default)();return(0,t.useQuery)({queryKey:n.list({}),queryFn:async()=>i(e),enabled:!!e&&s.all_admin_roles.includes(r||"")})}])},695411,e=>{"use strict";var t=e.i(602869);let r=async e=>{try{let r=await (0,t.modelHubCall)(e);if(r?.data.length>0){let e=r.data.map(e=>({model_group:e.model_group,mode:e?.mode}));return e.sort((e,t)=>e.model_group.localeCompare(t.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}};e.s(["fetchAvailableModels",0,r])},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var a=e.i(9583),s=r.forwardRef(function(e,s){return r.createElement(a.default,(0,t.default)({},e,{ref:s,icon:l}))});e.s(["default",0,s],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},37727,e=>{"use strict";var t=e.i(841947);e.s(["X",()=>t.default])},841947,e=>{"use strict";let t=(0,e.i(475254).default)("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);e.s(["default",0,t])},603908,e=>{"use strict";let t=(0,e.i(475254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);e.s(["default",0,t])},107233,e=>{"use strict";var t=e.i(603908);e.s(["Plus",()=>t.default])},158392,63209,e=>{"use strict";var t=e.i(843476),r=e.i(311451);let l={ttl:3600,lowest_latency_buffer:0},a=({routingStrategyArgs:e})=>{let a={ttl:"Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Latency-Based Configuration"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Fine-tune latency-based routing behavior"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e||l).map(([e,l])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:e.replace(/_/g," ")}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:a[e]||""}),(0,t.jsx)(r.Input,{name:e,defaultValue:"object"==typeof l?JSON.stringify(l,null,2):l?.toString(),className:"font-mono text-sm w-full"})]})},e))})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"})]})},s=({routerSettings:e,routerFieldsMetadata:l})=>(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Reliability & Retries"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure retry logic and failure handling"})]}),(0,t.jsx)("div",{className:"grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3",children:Object.entries(e).filter(([e])=>"fallbacks"!=e&&"context_window_fallbacks"!=e&&"routing_strategy_args"!=e&&"routing_strategy"!=e&&"enable_tag_filtering"!=e&&"retry_policy"!=e&&"model_group_retry_policy"!=e&&"routing_groups"!=e).map(([e,a])=>(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("label",{className:"block",children:[(0,t.jsx)("span",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:l[e]?.ui_field_name||e}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:l[e]?.field_description||""}),(0,t.jsx)(r.Input,{name:e,defaultValue:null==a||"null"===a?"":"object"==typeof a?JSON.stringify(a,null,2):a?.toString()||"",placeholder:"—",className:"font-mono text-sm w-full"})]})},e))})]});var o=e.i(199133);let n=({selectedStrategy:e,availableStrategies:r,routingStrategyDescriptions:l,routerFieldsMetadata:a,onStrategyChange:s})=>(0,t.jsxs)("div",{className:"space-y-2 max-w-3xl",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:a.routing_strategy?.ui_field_name||"Routing Strategy"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-0.5 mb-2",children:a.routing_strategy?.field_description||""})]}),(0,t.jsx)("div",{className:"routing-strategy-select max-w-3xl",children:(0,t.jsx)(o.Select,{value:e,onChange:s,style:{width:"100%"},size:"large",children:r.map(e=>(0,t.jsx)(o.Select.Option,{value:e,label:e,children:(0,t.jsxs)("div",{className:"flex flex-col gap-0.5 py-1",children:[(0,t.jsx)("span",{className:"font-mono text-sm font-medium",children:e}),l[e]&&(0,t.jsx)("span",{className:"text-xs text-gray-500 font-normal",children:l[e]})]})},e))})})]});var i=e.i(790848);let d=({enabled:e,routerFieldsMetadata:r,onToggle:l})=>(0,t.jsx)("div",{className:"space-y-3 max-w-3xl",children:(0,t.jsxs)("div",{className:"flex items-start justify-between",children:[(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("label",{className:"text-xs font-medium text-gray-700 uppercase tracking-wide",children:r.enable_tag_filtering?.ui_field_name||"Enable Tag Filtering"}),(0,t.jsxs)("p",{className:"text-xs text-gray-500 mt-0.5",children:[r.enable_tag_filtering?.field_description||"",r.enable_tag_filtering?.link&&(0,t.jsxs)(t.Fragment,{children:[" ",(0,t.jsx)("a",{href:r.enable_tag_filtering.link,target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:text-blue-800 underline",children:"Learn more"})]})]})]}),(0,t.jsx)(i.Switch,{checked:e,onChange:l,className:"ml-4"})]})});e.s(["default",0,({value:e,onChange:r,routerFieldsMetadata:l,availableRoutingStrategies:o,routingStrategyDescriptions:i})=>(0,t.jsxs)("div",{className:"w-full space-y-8 py-2",children:[(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"max-w-3xl",children:[(0,t.jsx)("h3",{className:"text-sm font-medium text-gray-900",children:"Routing Settings"}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1",children:"Configure how requests are routed to deployments"})]}),o.length>0&&(0,t.jsx)(n,{selectedStrategy:e.selectedStrategy||e.routerSettings.routing_strategy||null,availableStrategies:o,routingStrategyDescriptions:i,routerFieldsMetadata:l,onStrategyChange:t=>{r({...e,selectedStrategy:t})}}),(0,t.jsx)(d,{enabled:e.enableTagFiltering,routerFieldsMetadata:l,onToggle:t=>{r({...e,enableTagFiltering:t})}})]}),(0,t.jsx)("div",{className:"border-t border-gray-200"}),"latency-based-routing"===e.selectedStrategy&&(0,t.jsx)(a,{routingStrategyArgs:e.routerSettings.routing_strategy_args}),(0,t.jsx)(s,{routerSettings:e.routerSettings,routerFieldsMetadata:l})]})],158392);var c=e.i(361653);e.s(["AlertCircle",()=>c.default],63209)},425063,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]);e.s(["ArrowDown",0,t],425063)},419470,e=>{"use strict";var t=e.i(843476),r=e.i(994388),l=e.i(653496),a=e.i(107233),s=e.i(271645),o=e.i(888259),n=e.i(199133),i=e.i(592968),d=e.i(63209),c=e.i(425063),u=e.i(37727);function m({group:e,onChange:r,availableModels:l,maxFallbacks:a}){let s=l.filter(t=>t!==e.primaryModel),o=e.fallbackModels.length{let l=[...e.fallbackModels];l.includes(t)&&(l=l.filter(e=>e!==t)),r({...e,primaryModel:t,fallbackModels:l})},showSearch:!0,getPopupContainer:e=>e.parentElement||document.body,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase()),options:l.map(e=>({label:e,value:e}))}),!e.primaryModel&&(0,t.jsxs)("div",{className:"mt-2 flex items-center gap-2 text-amber-600 text-xs bg-amber-50 p-2 rounded-sm",children:[(0,t.jsx)(d.AlertCircle,{className:"w-4 h-4"}),(0,t.jsx)("span",{children:"Select a model to begin configuring fallbacks"})]})]}),(0,t.jsx)("div",{className:"flex items-center justify-center -my-4 z-10",children:(0,t.jsxs)("div",{className:"bg-indigo-50 text-indigo-500 px-4 py-1 rounded-full text-xs font-bold border border-indigo-100 flex items-center gap-2 shadow-xs",children:[(0,t.jsx)(c.ArrowDown,{className:"w-4 h-4"}),"IF FAILS, TRY..."]})}),(0,t.jsxs)("div",{className:`transition-opacity duration-300 ${!e.primaryModel?"opacity-50 pointer-events-none":"opacity-100"}`,children:[(0,t.jsxs)("label",{className:"block text-sm font-semibold text-gray-700 mb-2",children:["Fallback Chain ",(0,t.jsx)("span",{className:"text-red-500",children:"*"}),(0,t.jsxs)("span",{className:"text-xs text-gray-500 font-normal ml-2",children:["(Max ",a," fallbacks at a time)"]})]}),(0,t.jsxs)("div",{className:"bg-gray-50 rounded-xl p-4 border border-gray-200",children:[(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(n.Select,{mode:"multiple",className:"w-full",size:"large",placeholder:o?"Select fallback models to add...":`Maximum ${a} fallbacks reached`,value:e.fallbackModels,onChange:t=>{let l=t.slice(0,a);r({...e,fallbackModels:l})},disabled:!e.primaryModel,getPopupContainer:e=>e.parentElement||document.body,options:s.map(e=>({label:e,value:e})),optionRender:(r,l)=>{let a=e.fallbackModels.includes(r.value),s=a?e.fallbackModels.indexOf(r.value)+1:null;return(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[a&&null!==s&&(0,t.jsx)("span",{className:"flex items-center justify-center w-5 h-5 rounded-sm bg-indigo-100 text-indigo-600 text-xs font-bold",children:s}),(0,t.jsx)("span",{children:r.label})]})},maxTagCount:"responsive",maxTagPlaceholder:e=>(0,t.jsx)(i.Tooltip,{styles:{root:{pointerEvents:"none"}},title:e.map(({value:e})=>e).join(", "),children:(0,t.jsxs)("span",{children:["+",e.length," more"]})}),showSearch:!0,filterOption:(e,t)=>(t?.label??"").toLowerCase().includes(e.toLowerCase())}),(0,t.jsx)("p",{className:"text-xs text-gray-500 mt-1 ml-1",children:o?`Search and select multiple models. Selected models will appear below in order. (${e.fallbackModels.length}/${a} used)`:`Maximum ${a} fallbacks reached. Remove some to add more.`})]}),(0,t.jsx)("div",{className:"space-y-2 min-h-[100px]",children:0===e.fallbackModels.length?(0,t.jsxs)("div",{className:"h-32 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center text-gray-400",children:[(0,t.jsx)("span",{className:"text-sm",children:"No fallback models selected"}),(0,t.jsx)("span",{className:"text-xs mt-1",children:"Add models from the dropdown above"})]}):e.fallbackModels.map((l,a)=>(0,t.jsxs)("div",{className:"group flex items-center justify-between p-3 bg-white rounded-lg border border-gray-200 hover:border-indigo-300 hover:shadow-xs transition-all",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)("div",{className:"flex items-center justify-center w-6 h-6 rounded-sm bg-gray-100 text-gray-400 group-hover:text-indigo-500 group-hover:bg-indigo-50",children:(0,t.jsx)("span",{className:"text-xs font-bold",children:a+1})}),(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"font-medium text-gray-800",children:l})})]}),(0,t.jsx)("button",{type:"button",onClick:()=>{let t;return t=e.fallbackModels.filter((e,t)=>t!==a),void r({...e,fallbackModels:t})},className:"opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 hover:text-red-500 p-1",children:(0,t.jsx)(u.X,{className:"w-4 h-4"})})]},`${l}-${a}`))})]})]})]})}e.s(["FallbackSelectionForm",0,function({groups:e,onGroupsChange:n,availableModels:i,maxFallbacks:d=10,maxGroups:c=5}){let[u,g]=(0,s.useState)(e.length>0?e[0].id:"1");(0,s.useEffect)(()=>{e.length>0?e.some(e=>e.id===u)||g(e[0].id):g("1")},[e]);let p=()=>{if(e.length>=c)return;let t=Date.now().toString();n([...e,{id:t,primaryModel:null,fallbackModels:[]}]),g(t)},h=t=>{n(e.map(e=>e.id===t.id?t:e))},f=e.map((r,l)=>{let a=r.primaryModel?r.primaryModel:`Group ${l+1}`;return{key:r.id,label:a,closable:e.length>1,children:(0,t.jsx)(m,{group:r,onChange:h,availableModels:i,maxFallbacks:d})}});return 0===e.length?(0,t.jsxs)("div",{className:"text-center py-12 bg-gray-50 rounded-lg border border-dashed border-gray-300",children:[(0,t.jsx)("p",{className:"text-gray-500 mb-4",children:"No fallback groups configured"}),(0,t.jsx)(r.Button,{variant:"primary",onClick:p,icon:()=>(0,t.jsx)(a.Plus,{className:"w-4 h-4"}),children:"Create First Group"})]}):(0,t.jsx)(l.Tabs,{type:"editable-card",activeKey:u,onChange:g,onEdit:(t,r)=>{"add"===r?p():"remove"===r&&e.length>1&&(t=>{if(1===e.length)return o.default.warning("At least one group is required");let r=e.filter(e=>e.id!==t);n(r),u===t&&r.length>0&&g(r[r.length-1].id)})(t)},items:f,className:"fallback-tabs",tabBarStyle:{marginBottom:0},hideAdd:e.length>=c})}],419470)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0ubynsv~w-kqx.js b/litellm/proxy/_experimental/out/_next/static/chunks/0ubynsv~w-kqx.js new file mode 100644 index 00000000000..6f887582769 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0ubynsv~w-kqx.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,564897,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M696 480H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"minus-circle",theme:"outlined"};var l=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(l.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["MinusCircleOutlined",0,o],564897)},362024,e=>{"use strict";var t=e.i(988122);e.s(["Collapse",()=>t.default])},597440,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"}}]},name:"delete",theme:"outlined"};var l=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(l.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["default",0,o],597440)},955135,e=>{"use strict";var t=e.i(597440);e.s(["DeleteOutlined",()=>t.default])},646563,e=>{"use strict";var t=e.i(959013);e.s(["PlusOutlined",()=>t.default])},536916,e=>{"use strict";var t=e.i(374276);e.s(["Checkbox",()=>t.default])},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},95779,e=>{"use strict";var t=e.i(480731);let r=[t.BaseColors.Blue,t.BaseColors.Cyan,t.BaseColors.Sky,t.BaseColors.Indigo,t.BaseColors.Violet,t.BaseColors.Purple,t.BaseColors.Fuchsia,t.BaseColors.Slate,t.BaseColors.Gray,t.BaseColors.Zinc,t.BaseColors.Neutral,t.BaseColors.Stone,t.BaseColors.Red,t.BaseColors.Orange,t.BaseColors.Amber,t.BaseColors.Yellow,t.BaseColors.Lime,t.BaseColors.Green,t.BaseColors.Emerald,t.BaseColors.Teal,t.BaseColors.Pink,t.BaseColors.Rose];e.s(["colorPalette",0,{canvasBackground:50,lightBackground:100,background:500,darkBackground:600,darkestBackground:800,lightBorder:200,border:500,darkBorder:700,lightRing:200,ring:300,iconRing:500,lightText:400,text:500,iconText:600,darkText:700,darkestText:900,icon:500},"themeColorRange",0,r])},994388,e=>{"use strict";var t=e.i(290571),r=e.i(829087),a=e.i(271645);let l=["preEnter","entering","entered","preExit","exiting","exited","unmounted"],o=e=>({_s:e,status:l[e],isEnter:e<3,isMounted:6!==e,isResolved:2===e||e>4}),s=e=>e?6:5,i=(e,t,r,a,l)=>{clearTimeout(a.current);let s=o(e);t(s),r.current=s,l&&l({current:s})};var n=e.i(480731),d=e.i(444755),c=e.i(673706);let m=e=>{var r=(0,t.__rest)(e,[]);return a.default.createElement("svg",Object.assign({},r,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),a.default.createElement("path",{fill:"none",d:"M0 0h24v24H0z"}),a.default.createElement("path",{d:"M18.364 5.636L16.95 7.05A7 7 0 1 0 19 12h2a9 9 0 1 1-2.636-6.364z"}))};var u=e.i(95779);let p={xs:{height:"h-4",width:"w-4"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-6",width:"w-6"},xl:{height:"h-6",width:"w-6"}},g=(e,t)=>{switch(e){case"primary":return{textColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",hoverTextColor:t?(0,c.getColorClassNames)("white").textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,c.getColorClassNames)(t,u.colorPalette.background).bgColor:"bg-tremor-brand dark:bg-dark-tremor-brand",hoverBgColor:t?(0,c.getColorClassNames)(t,u.colorPalette.darkBackground).hoverBgColor:"hover:bg-tremor-brand-emphasis dark:hover:bg-dark-tremor-brand-emphasis",borderColor:t?(0,c.getColorClassNames)(t,u.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",hoverBorderColor:t?(0,c.getColorClassNames)(t,u.colorPalette.darkBorder).hoverBorderColor:"hover:border-tremor-brand-emphasis dark:hover:border-dark-tremor-brand-emphasis"};case"secondary":return{textColor:t?(0,c.getColorClassNames)(t,u.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,u.colorPalette.text).textColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,hoverBgColor:t?(0,d.tremorTwMerge)((0,c.getColorClassNames)(t,u.colorPalette.background).hoverBgColor,"hover:bg-opacity-20 dark:hover:bg-opacity-20"):"hover:bg-tremor-brand-faint dark:hover:bg-dark-tremor-brand-faint",borderColor:t?(0,c.getColorClassNames)(t,u.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand"};case"light":return{textColor:t?(0,c.getColorClassNames)(t,u.colorPalette.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",hoverTextColor:t?(0,c.getColorClassNames)(t,u.colorPalette.darkText).hoverTextColor:"hover:text-tremor-brand-emphasis dark:hover:text-dark-tremor-brand-emphasis",bgColor:(0,c.getColorClassNames)("transparent").bgColor,borderColor:"",hoverBorderColor:""}}},h=(0,c.makeClassName)("Button"),x=({loading:e,iconSize:t,iconPosition:r,Icon:l,needMargin:o,transitionStatus:s})=>{let i=o?r===n.HorizontalPositions.Left?(0,d.tremorTwMerge)("-ml-1","mr-1.5"):(0,d.tremorTwMerge)("-mr-1","ml-1.5"):"",c=(0,d.tremorTwMerge)("w-0 h-0"),u={default:c,entering:c,entered:t,exiting:t,exited:c};return e?a.default.createElement(m,{className:(0,d.tremorTwMerge)(h("icon"),"animate-spin shrink-0",i,u.default,u[s]),style:{transition:"width 150ms"}}):a.default.createElement(l,{className:(0,d.tremorTwMerge)(h("icon"),"shrink-0",t,i)})},f=a.default.forwardRef((e,l)=>{let{icon:m,iconPosition:u=n.HorizontalPositions.Left,size:f=n.Sizes.SM,color:y,variant:j="primary",disabled:C,loading:b=!1,loadingText:v,children:k,tooltip:w,className:_}=e,S=(0,t.__rest)(e,["icon","iconPosition","size","color","variant","disabled","loading","loadingText","children","tooltip","className"]),T=b||C,B=void 0!==m||b,M=b&&v,P=!(!k&&!M),N=(0,d.tremorTwMerge)(p[f].height,p[f].width),I="light"!==j?(0,d.tremorTwMerge)("rounded-tremor-default border","shadow-tremor-input","dark:shadow-dark-tremor-input"):"",z=g(j,y),E=("light"!==j?{xs:{paddingX:"px-2.5",paddingY:"py-1.5",fontSize:"text-xs"},sm:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-sm"},md:{paddingX:"px-4",paddingY:"py-2",fontSize:"text-md"},lg:{paddingX:"px-4",paddingY:"py-2.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-3",fontSize:"text-xl"}}:{xs:{paddingX:"",paddingY:"",fontSize:"text-xs"},sm:{paddingX:"",paddingY:"",fontSize:"text-sm"},md:{paddingX:"",paddingY:"",fontSize:"text-md"},lg:{paddingX:"",paddingY:"",fontSize:"text-lg"},xl:{paddingX:"",paddingY:"",fontSize:"text-xl"}})[f],{tooltipProps:F,getReferenceProps:L}=(0,r.useTooltip)(300),[O,R]=(({enter:e=!0,exit:t=!0,preEnter:r,preExit:l,timeout:n,initialEntered:d,mountOnEnter:c,unmountOnExit:m,onStateChange:u}={})=>{let[p,g]=(0,a.useState)(()=>o(d?2:s(c))),h=(0,a.useRef)(p),x=(0,a.useRef)(0),[f,y]="object"==typeof n?[n.enter,n.exit]:[n,n],j=(0,a.useCallback)(()=>{let e=((e,t)=>{switch(e){case 1:case 0:return 2;case 4:case 3:return s(t)}})(h.current._s,m);e&&i(e,g,h,x,u)},[u,m]);return[p,(0,a.useCallback)(a=>{let o=e=>{switch(i(e,g,h,x,u),e){case 1:f>=0&&(x.current=((...e)=>setTimeout(...e))(j,f));break;case 4:y>=0&&(x.current=((...e)=>setTimeout(...e))(j,y));break;case 0:case 3:x.current=((...e)=>setTimeout(...e))(()=>{isNaN(document.body.offsetTop)||o(e+1)},0)}},n=h.current.isEnter;"boolean"!=typeof a&&(a=!n),a?n||o(e?+!r:2):n&&o(t?l?3:4:s(m))},[j,u,e,t,r,l,f,y,m]),j]})({timeout:50});return(0,a.useEffect)(()=>{R(b)},[b]),a.default.createElement("button",Object.assign({ref:(0,c.mergeRefs)([l,F.refs.setReference]),className:(0,d.tremorTwMerge)(h("root"),"shrink-0 inline-flex justify-center items-center group font-medium outline-none",I,E.paddingX,E.paddingY,E.fontSize,z.textColor,z.bgColor,z.borderColor,z.hoverBorderColor,T?"opacity-50 cursor-not-allowed":(0,d.tremorTwMerge)(g(j,y).hoverTextColor,g(j,y).hoverBgColor,g(j,y).hoverBorderColor),_),disabled:T},L,S),a.default.createElement(r.default,Object.assign({text:w},F)),B&&u!==n.HorizontalPositions.Right?a.default.createElement(x,{loading:b,iconSize:N,iconPosition:u,Icon:m,transitionStatus:O.status,needMargin:P}):null,M||k?a.default.createElement("span",{className:(0,d.tremorTwMerge)(h("text"),"text-tremor-default whitespace-nowrap")},M?v:k):null,B&&u===n.HorizontalPositions.Right?a.default.createElement(x,{loading:b,iconSize:N,iconPosition:u,Icon:m,transitionStatus:O.status,needMargin:P}):null)});f.displayName="Button",e.s(["Button",0,f],994388)},599724,936325,e=>{"use strict";var t=e.i(95779),r=e.i(444755),a=e.i(673706),l=e.i(271645);let o=l.default.forwardRef((e,o)=>{let{color:s,className:i,children:n}=e;return l.default.createElement("p",{ref:o,className:(0,r.tremorTwMerge)("text-tremor-default",s?(0,a.getColorClassNames)(s,t.colorPalette.text).textColor:(0,r.tremorTwMerge)("text-tremor-content","dark:text-dark-tremor-content"),i)},n)});o.displayName="Text",e.s(["default",0,o],936325),e.s(["Text",0,o],599724)},304967,e=>{"use strict";var t=e.i(290571),r=e.i(271645),a=e.i(480731),l=e.i(95779),o=e.i(444755),s=e.i(673706);let i=(0,s.makeClassName)("Card"),n=r.default.forwardRef((e,n)=>{let{decoration:d="",decorationColor:c,children:m,className:u}=e,p=(0,t.__rest)(e,["decoration","decorationColor","children","className"]);return r.default.createElement("div",Object.assign({ref:n,className:(0,o.tremorTwMerge)(i("root"),"relative w-full text-left ring-1 rounded-tremor-default p-6","bg-tremor-background ring-tremor-ring shadow-tremor-card","dark:bg-dark-tremor-background dark:ring-dark-tremor-ring dark:shadow-dark-tremor-card",c?(0,s.getColorClassNames)(c,l.colorPalette.border).borderColor:"border-tremor-brand dark:border-dark-tremor-brand",(e=>{if(!e)return"";switch(e){case a.HorizontalPositions.Left:return"border-l-4";case a.VerticalPositions.Top:return"border-t-4";case a.HorizontalPositions.Right:return"border-r-4";case a.VerticalPositions.Bottom:return"border-b-4";default:return""}})(d),u)},p),m)});n.displayName="Card",e.s(["Card",0,n],304967)},629569,e=>{"use strict";var t=e.i(290571),r=e.i(95779),a=e.i(444755),l=e.i(673706),o=e.i(271645);let s=o.default.forwardRef((e,s)=>{let{color:i,children:n,className:d}=e,c=(0,t.__rest)(e,["color","children","className"]);return o.default.createElement("p",Object.assign({ref:s,className:(0,a.tremorTwMerge)("font-medium text-tremor-title",i?(0,l.getColorClassNames)(i,r.colorPalette.darkText).textColor:"text-tremor-content-strong dark:text-dark-tremor-content-strong",d)},c),n)});s.displayName="Title",e.s(["Title",0,s],629569)},987432,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M893.3 293.3L730.7 130.7c-7.5-7.5-16.7-13-26.7-16V112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V338.5c0-17-6.7-33.2-18.7-45.2zM384 184h256v104H384V184zm456 656H184V184h136v136c0 17.7 14.3 32 32 32h320c17.7 0 32-14.3 32-32V205.8l136 136V840zM512 442c-79.5 0-144 64.5-144 144s64.5 144 144 144 144-64.5 144-144-64.5-144-144-144zm0 224c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z"}}]},name:"save",theme:"outlined"};var l=e.i(9583),o=r.forwardRef(function(e,o){return r.createElement(l.default,(0,t.default)({},e,{ref:o,icon:a}))});e.s(["SaveOutlined",0,o],987432)},823429,e=>{"use strict";let t=(0,e.i(475254).default)("square-pen",[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",key:"1m0v6g"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",key:"ohrbg2"}]]);e.s(["default",0,t])},497650,e=>{"use strict";var t=e.i(309821);e.s(["Progress",()=>t.default])},21548,e=>{"use strict";var t=e.i(616303);e.s(["Empty",()=>t.default])},988846,e=>{"use strict";var t=e.i(54943);e.s(["SearchIcon",()=>t.default])},438100,e=>{"use strict";let t=(0,e.i(475254).default)("key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]]);e.s(["KeyIcon",0,t],438100)},95684,e=>{"use strict";var t=e.i(165370);e.s(["Pagination",()=>t.default])},372943,897565,166452,e=>{"use strict";e.i(247167);var t=e.i(8211),r=e.i(271645),a=e.i(343794),l=e.i(529681),o=e.i(242064),s=e.i(704914),i=e.i(876556),n=e.i(290224),d=e.i(251224),c=function(e,t){var r={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(r[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var l=0,a=Object.getOwnPropertySymbols(e);lt.indexOf(a[l])&&Object.prototype.propertyIsEnumerable.call(e,a[l])&&(r[a[l]]=e[a[l]]);return r};function m({suffixCls:e,tagName:t,displayName:a}){return a=>r.forwardRef((l,o)=>r.createElement(a,Object.assign({ref:o,suffixCls:e,tagName:t},l)))}let u=r.forwardRef((e,t)=>{let{prefixCls:l,suffixCls:s,className:i,tagName:n}=e,m=c(e,["prefixCls","suffixCls","className","tagName"]),{getPrefixCls:u}=r.useContext(o.ConfigContext),p=u("layout",l),[g,h,x]=(0,d.default)(p),f=s?`${p}-${s}`:p;return g(r.createElement(n,Object.assign({className:(0,a.default)(l||f,i,h,x),ref:t},m)))}),p=r.forwardRef((e,m)=>{let{direction:u}=r.useContext(o.ConfigContext),[p,g]=r.useState([]),{prefixCls:h,className:x,rootClassName:f,children:y,hasSider:j,tagName:C,style:b}=e,v=c(e,["prefixCls","className","rootClassName","children","hasSider","tagName","style"]),k=(0,l.default)(v,["suffixCls"]),{getPrefixCls:w,className:_,style:S}=(0,o.useComponentConfig)("layout"),T=w("layout",h),B="boolean"==typeof j?j:!!p.length||(0,i.default)(y).some(e=>e.type===n.default),[M,P,N]=(0,d.default)(T),I=(0,a.default)(T,{[`${T}-has-sider`]:B,[`${T}-rtl`]:"rtl"===u},_,x,f,P,N),z=r.useMemo(()=>({siderHook:{addSider:e=>{g(r=>[].concat((0,t.default)(r),[e]))},removeSider:e=>{g(t=>t.filter(t=>t!==e))}}}),[]);return M(r.createElement(s.LayoutContext.Provider,{value:z},r.createElement(C,Object.assign({ref:m,className:I,style:Object.assign(Object.assign({},S),b)},k),y)))}),g=m({tagName:"div",displayName:"Layout"})(p),h=m({suffixCls:"header",tagName:"header",displayName:"Header"})(u),x=m({suffixCls:"footer",tagName:"footer",displayName:"Footer"})(u),f=m({suffixCls:"content",tagName:"main",displayName:"Content"})(u);g.Header=h,g.Footer=x,g.Content=f,g.Sider=n.default,g._InternalSiderContext=n.SiderContext,e.s(["Layout",0,g],372943);let y=(0,e.i(475254).default)("layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);e.s(["LayersIcon",0,y],897565);var j=e.i(98740);e.s(["UsersIcon",()=>j.default],166452)},180127,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-left",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);e.s(["default",0,t])},516430,e=>{"use strict";var t=e.i(180127);e.s(["ArrowLeftIcon",()=>t.default])},44068,e=>{"use strict";var t=e.i(823429);e.s(["EditIcon",()=>t.default])},454587,e=>{"use strict";var t=e.i(843476),r=e.i(510674),a=e.i(785242);e.i(622826);var l=e.i(200208),o=e.i(399536),s=e.i(56456),i=e.i(646563),n=e.i(464571),d=e.i(175712),c=e.i(525720),m=e.i(311451),u=e.i(372943),p=e.i(95684),g=e.i(770914),h=e.i(482725),x=e.i(291542),f=e.i(262218),y=e.i(368869),j=e.i(592968),C=e.i(898586),b=e.i(897565),v=e.i(988846),k=e.i(271645),w=e.i(212931),_=e.i(808613);e.i(247167);var S=e.i(931067);let T={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M484 443.1V528h-84.5c-4.1 0-7.5 3.1-7.5 7v42c0 3.8 3.4 7 7.5 7H484v84.9c0 3.9 3.2 7.1 7 7.1h42c3.9 0 7-3.2 7-7.1V584h84.5c4.1 0 7.5-3.2 7.5-7v-42c0-3.9-3.4-7-7.5-7H540v-84.9c0-3.9-3.1-7.1-7-7.1h-42c-3.8 0-7 3.2-7 7.1zm396-144.7H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"}}]},name:"folder-add",theme:"outlined"};var B=e.i(9583),M=k.forwardRef(function(e,t){return k.createElement(B.default,(0,S.default)({},e,{ref:t,icon:T}))}),P=e.i(888259),N=e.i(954616),I=e.i(912598),z=e.i(602869),E=e.i(431703),F=e.i(135214);let L=async(e,t)=>{let r=(0,z.getProxyBaseUrl)(),a=`${r}/project/new`,l=await fetch(a,{method:"POST",headers:{[(0,z.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(!l.ok){let e=await l.json(),t=(0,E.deriveErrorMessage)(e);throw(0,z.handleError)(t),Error(t)}return l.json()};var O=e.i(560445),R=e.i(178654),H=e.i(362024),D=e.i(312361),A=e.i(28651),V=e.i(621192),$=e.i(199133),G=e.i(790848),q=e.i(564897),K=e.i(702597),U=e.i(355619);function Y({form:e}){let{accessToken:r,userId:l,userRole:o}=(0,F.default)(),{data:s}=(0,a.useTeams)(),[d,u]=(0,k.useState)(null),[p,h]=(0,k.useState)([]),[x,f]=(0,k.useState)([]);(0,k.useEffect)(()=>{(async()=>{if(r)try{let e=(await (0,z.getGuardrailsList)(r)).guardrails.map(e=>e.guardrail_name);f(e)}catch(e){console.error("Failed to fetch guardrails:",e)}})()},[r]);let y=_.Form.useWatch("team_id",e);return(0,k.useEffect)(()=>{if(y&&s){let e=s.find(e=>e.team_id===y)??null;e&&e.team_id!==d?.team_id&&u(e)}},[y,s,d?.team_id]),(0,k.useEffect)(()=>{l&&o&&r&&d?(0,K.fetchTeamModels)(l,o,r,d.team_id).then(e=>{h(Array.from(new Set([...d.models??[],...e])))}):h([])},[d,r,l,o]),(0,t.jsxs)(_.Form,{form:e,layout:"vertical",name:"project_form",initialValues:{isBlocked:!1},style:{marginTop:24},children:[(0,t.jsx)(C.Typography.Text,{strong:!0,style:{fontSize:13,color:"#374151",textTransform:"uppercase",letterSpacing:"0.05em"},children:"Basic Information"}),(0,t.jsx)(D.Divider,{style:{marginTop:8,marginBottom:16}}),(0,t.jsxs)(V.Row,{gutter:24,children:[(0,t.jsx)(R.Col,{span:12,children:(0,t.jsx)(_.Form.Item,{name:"project_alias",label:"Project Name",rules:[{required:!0,message:"Please enter a project name"}],children:(0,t.jsx)(m.Input,{placeholder:"e.g. Customer Support Bot"})})}),(0,t.jsx)(R.Col,{span:12,children:(0,t.jsx)(_.Form.Item,{name:"team_id",label:"Team",rules:[{required:!0,message:"Please select a team"}],children:(0,t.jsx)($.Select,{showSearch:!0,placeholder:"Search or select a team",onChange:t=>{u(s?.find(e=>e.team_id===t)??null),e.setFieldValue("models",[])},allowClear:!0,optionLabelProp:"label",filterOption:(e,t)=>{let r=s?.find(e=>e.team_id===t?.value);if(!r)return!1;let a=e.toLowerCase().trim();return(r.team_alias||"").toLowerCase().includes(a)||r.team_id.toLowerCase().includes(a)},children:s?.map(e=>(0,t.jsxs)($.Select.Option,{value:e.team_id,label:e.team_alias||e.team_id,children:[(0,t.jsx)("span",{style:{fontWeight:500},children:e.team_alias})," ",(0,t.jsxs)("span",{style:{color:"#9ca3af"},children:["(",e.team_id,")"]})]},e.team_id))})})})]}),(0,t.jsx)(V.Row,{children:(0,t.jsx)(R.Col,{span:24,children:(0,t.jsx)(_.Form.Item,{name:"description",label:"Description",children:(0,t.jsx)(m.Input.TextArea,{placeholder:"Describe the purpose of this project",rows:3})})})}),(0,t.jsx)(V.Row,{children:(0,t.jsx)(R.Col,{span:24,children:(0,t.jsx)(_.Form.Item,{name:"models",label:"Allowed Models (scoped to selected team's models)",help:d?void 0:"Select a team first to see available models",children:(0,t.jsxs)($.Select,{mode:"multiple",placeholder:d?"Select models":"Select a team first",disabled:!d,allowClear:!0,maxTagCount:"responsive",onChange:t=>{t.includes("all-team-models")&&e.setFieldsValue({models:["all-team-models"]})},children:[(0,t.jsx)($.Select.Option,{value:"all-team-models",children:"All Team Models"},"all-team-models"),p.map(e=>(0,t.jsx)($.Select.Option,{value:e,children:(0,U.getModelDisplayName)(e)},e))]})})})}),(0,t.jsx)(V.Row,{gutter:24,children:(0,t.jsx)(R.Col,{span:12,children:(0,t.jsx)(_.Form.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,t.jsx)(A.InputNumber,{prefix:"$",style:{width:"100%"},placeholder:"0.00",min:0,precision:2})})})}),(0,t.jsx)(V.Row,{children:(0,t.jsx)(R.Col,{span:24,children:(0,t.jsx)(H.Collapse,{ghost:!0,style:{background:"#f9fafb",borderRadius:8,border:"1px solid #e5e7eb"},items:[{key:"1",label:(0,t.jsx)(C.Typography.Text,{strong:!0,style:{color:"#374151"},children:"Advanced Settings"}),children:(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(c.Flex,{align:"center",gap:12,children:[(0,t.jsx)(C.Typography.Text,{strong:!0,children:"Block Project"}),(0,t.jsx)(_.Form.Item,{name:"isBlocked",valuePropName:"checked",noStyle:!0,children:(0,t.jsx)(G.Switch,{})})]}),(0,t.jsx)(_.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.isBlocked!==t.isBlocked,children:({getFieldValue:e})=>e("isBlocked")?(0,t.jsx)(O.Alert,{banner:!0,type:"warning",showIcon:!0,message:"All API requests using keys under this project will be rejected.",style:{marginTop:12}}):null}),(0,t.jsx)(D.Divider,{}),(0,t.jsx)(_.Form.Item,{label:"Guardrails",name:"guardrails",help:"Select existing guardrails or enter new ones",children:(0,t.jsx)($.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter guardrails",options:x.map(e=>({value:e,label:e}))})}),(0,t.jsx)(D.Divider,{}),(0,t.jsx)(C.Typography.Text,{strong:!0,style:{display:"block",marginBottom:12},children:"Model-Specific Limits"}),(0,t.jsx)(_.Form.List,{name:"modelLimits",children:(r,{add:a,remove:l})=>(0,t.jsxs)(t.Fragment,{children:[r.map(({key:r,name:a,...o})=>(0,t.jsxs)(g.Space,{style:{display:"flex",marginBottom:8},align:"baseline",children:[(0,t.jsx)(_.Form.Item,{...o,name:[a,"model"],rules:[{required:!0,message:"Missing model"},{validator:(t,r)=>r&&(e.getFieldValue("modelLimits")??[]).filter(e=>e?.model===r).length>1?Promise.reject(Error("Duplicate model")):Promise.resolve()}],children:(0,t.jsx)(m.Input,{placeholder:"Model name (e.g. gpt-4)"})}),(0,t.jsx)(_.Form.Item,{...o,name:[a,"tpm"],children:(0,t.jsx)(A.InputNumber,{placeholder:"TPM Limit",min:0})}),(0,t.jsx)(_.Form.Item,{...o,name:[a,"rpm"],children:(0,t.jsx)(A.InputNumber,{placeholder:"RPM Limit",min:0})}),(0,t.jsx)(q.MinusCircleOutlined,{onClick:()=>l(a),style:{color:"#ef4444"}})]},r)),(0,t.jsx)(_.Form.Item,{children:(0,t.jsx)(n.Button,{type:"dashed",onClick:()=>a(),block:!0,icon:(0,t.jsx)(i.PlusOutlined,{}),children:"Add Model Limit"})})]})}),(0,t.jsx)(D.Divider,{}),(0,t.jsx)(C.Typography.Text,{strong:!0,style:{display:"block",marginBottom:12},children:"Metadata"}),(0,t.jsx)(_.Form.List,{name:"metadata",children:(r,{add:a,remove:l})=>(0,t.jsxs)(t.Fragment,{children:[r.map(({key:r,name:a,...o})=>(0,t.jsxs)(g.Space,{style:{display:"flex",marginBottom:8},align:"baseline",children:[(0,t.jsx)(_.Form.Item,{...o,name:[a,"key"],rules:[{required:!0,message:"Missing key"},{validator:(t,r)=>r&&(e.getFieldValue("metadata")??[]).filter(e=>e?.key===r).length>1?Promise.reject(Error("Duplicate key")):Promise.resolve()}],children:(0,t.jsx)(m.Input,{placeholder:"Key"})}),(0,t.jsx)(_.Form.Item,{...o,name:[a,"value"],rules:[{required:!0,message:"Missing value"}],children:(0,t.jsx)(m.Input,{placeholder:"Value"})}),(0,t.jsx)(q.MinusCircleOutlined,{onClick:()=>l(a),style:{color:"#ef4444"}})]},r)),(0,t.jsx)(_.Form.Item,{children:(0,t.jsx)(n.Button,{type:"dashed",onClick:()=>a(),block:!0,icon:(0,t.jsx)(i.PlusOutlined,{}),children:"Add Key-Value Pair"})})]})})]})}]})})})]})}function X(e){let t={},r={};for(let a of e.modelLimits??[])a.model&&(null!=a.rpm&&(t[a.model]=a.rpm),null!=a.tpm&&(r[a.model]=a.tpm));let a={};for(let t of e.metadata??[])t.key&&(a[t.key]=t.value);return{project_alias:e.project_alias,description:e.description,models:e.models??[],max_budget:e.max_budget,blocked:e.isBlocked??!1,...e.guardrails&&e.guardrails.length>0&&{guardrails:e.guardrails},...Object.keys(t).length>0&&{model_rpm_limit:t},...Object.keys(r).length>0&&{model_tpm_limit:r},...Object.keys(a).length>0&&{metadata:a}}}function Q({isOpen:e,onClose:a}){let[l]=_.Form.useForm(),o=(()=>{let{accessToken:e}=(0,F.default)(),t=(0,I.useQueryClient)();return(0,N.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return L(e,t)},onSuccess:()=>{t.invalidateQueries({queryKey:r.projectKeys.all})}})})(),s=async()=>{try{let e=await l.validateFields(),t={...X(e),team_id:e.team_id};o.mutate(t,{onSuccess:()=>{P.default.success("Project created successfully"),l.resetFields(),a()},onError:e=>{P.default.error(e.message||"Failed to create project")}})}catch(e){console.error("Validation failed:",e)}},i=()=>{l.resetFields(),a()};return(0,t.jsx)(w.Modal,{title:(0,t.jsx)(C.Typography.Text,{strong:!0,style:{fontSize:18},children:"Create New Project"}),open:e,onCancel:i,width:720,destroyOnHidden:!0,footer:[(0,t.jsx)(n.Button,{onClick:i,children:"Cancel"},"cancel"),(0,t.jsx)(n.Button,{type:"primary",icon:(0,t.jsx)(M,{}),loading:o.isPending,onClick:s,children:"Create Project"},"submit")],children:(0,t.jsx)(Y,{form:l})})}var W=e.i(266027),J=e.i(708347);let Z=async(e,t)=>{let r=(0,z.getProxyBaseUrl)(),a=`${r}/project/info?project_id=${encodeURIComponent(t)}`,l=await fetch(a,{method:"GET",headers:{[(0,z.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!l.ok){let e=await l.json(),t=(0,E.deriveErrorMessage)(e);throw(0,z.handleError)(t),Error(t)}return l.json()};var ee=e.i(869216),et=e.i(21548),er=e.i(497650),ea=e.i(584935),el=e.i(516430);let eo=(0,e.i(475254).default)("dollar-sign",[["line",{x1:"12",x2:"12",y1:"2",y2:"22",key:"7eqyqh"}],["path",{d:"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6",key:"1b0p4s"}]]);var es=e.i(44068),ei=e.i(438100),en=e.i(166452),ed=e.i(304911),ec=e.i(987432);let em=async(e,t,r)=>{let a=(0,z.getProxyBaseUrl)(),l=`${a}/project/update`,o=await fetch(l,{method:"POST",headers:{[(0,z.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({project_id:t,...r})});if(!o.ok){let e=await o.json(),t=(0,E.deriveErrorMessage)(e);throw(0,z.handleError)(t),Error(t)}return o.json()};function eu({isOpen:e,project:a,onClose:l,onSuccess:o}){let[s]=_.Form.useForm(),i=(()=>{let{accessToken:e}=(0,F.default)(),t=(0,I.useQueryClient)();return(0,N.useMutation)({mutationFn:async({projectId:t,params:r})=>{if(!e)throw Error("Access token is required");return em(e,t,r)},onSuccess:()=>{t.invalidateQueries({queryKey:r.projectKeys.all})}})})();(0,k.useEffect)(()=>{if(e&&a){let e=a.metadata??{},t=e.model_rpm_limit??{},r=e.model_tpm_limit??{},l=Array.isArray(e.guardrails)?e.guardrails:[],o=[];for(let e of new Set([...Object.keys(t),...Object.keys(r)]))o.push({model:e,rpm:t[e],tpm:r[e]});let i=new Set(["model_rpm_limit","model_tpm_limit","guardrails"]),n=[];for(let[t,r]of Object.entries(e))i.has(t)||n.push({key:t,value:String(r)});s.setFieldsValue({project_alias:a.project_alias??"",team_id:a.team_id??"",description:a.description??"",models:a.models??[],max_budget:a.litellm_budget_table?.max_budget??void 0,isBlocked:a.blocked,guardrails:l.length>0?l:void 0,modelLimits:o.length>0?o:void 0,metadata:n.length>0?n:void 0})}},[e,a,s]);let d=async()=>{try{let e=await s.validateFields(),t={...X(e),team_id:e.team_id};i.mutate({projectId:a.project_id,params:t},{onSuccess:()=>{P.default.success("Project updated successfully"),o?.(),l()},onError:e=>{P.default.error(e.message||"Failed to update project")}})}catch(e){console.error("Validation failed:",e)}};return(0,t.jsx)(w.Modal,{title:(0,t.jsx)(C.Typography.Text,{strong:!0,style:{fontSize:18},children:"Edit Project"}),open:e,onCancel:l,width:720,destroyOnHidden:!0,footer:[(0,t.jsx)(n.Button,{onClick:l,children:"Cancel"},"cancel"),(0,t.jsx)(n.Button,{type:"primary",icon:(0,t.jsx)(ec.SaveOutlined,{}),loading:i.isPending,onClick:d,children:"Save Changes"},"submit")],children:(0,t.jsx)(Y,{form:s})})}let{Title:ep,Text:eg}=C.Typography,{Content:eh}=u.Layout;function ex({projectId:e,onBack:l}){let o,i,m,u,{data:p,isLoading:g}=(e=>{let{accessToken:t,userRole:a}=(0,F.default)(),l=(0,I.useQueryClient)();return(0,W.useQuery)({queryKey:r.projectKeys.detail(e),queryFn:async()=>Z(t,e),enabled:!!(t&&e)&&J.all_admin_roles.includes(a||""),initialData:()=>{if(!e)return;let t=l.getQueryData(r.projectKeys.list({}));return t?.find(t=>t.project_id===e)}})})(e),{data:x}=(0,a.useTeam)(p?.team_id??void 0),j=x?.team_info??x,{token:C}=y.theme.useToken(),[b,v]=(0,k.useState)(!1),w=p?.spend??0,_=p?.litellm_budget_table?.max_budget??null,S=null!=_&&_>0,T=S?Math.min(w/_*100,100):0,B=(0,k.useMemo)(()=>Object.entries(p?.model_spend??{}).map(([e,t])=>({model:e,spend:t})).sort((e,t)=>t.spend-e.spend),[p?.model_spend]);return g?(0,t.jsx)(eh,{style:{padding:C.paddingLG,paddingInline:2*C.paddingLG},children:(0,t.jsx)(c.Flex,{justify:"center",align:"center",style:{minHeight:300},children:(0,t.jsx)(h.Spin,{indicator:(0,t.jsx)(s.LoadingOutlined,{spin:!0}),size:"large"})})}):p?(0,t.jsxs)(eh,{style:{padding:C.paddingLG,paddingInline:2*C.paddingLG},children:[(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:24},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:16},children:[(0,t.jsx)(n.Button,{icon:(0,t.jsx)(el.ArrowLeftIcon,{size:16}),onClick:l,type:"text"}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(c.Flex,{align:"center",gap:8,children:[(0,t.jsx)(ep,{level:2,style:{margin:0},children:p.project_alias??p.project_id}),(0,t.jsx)(f.Tag,{color:p.blocked?"red":"green",children:p.blocked?"Blocked":"Active"})]}),(0,t.jsxs)(eg,{type:"secondary",children:["ID: ",(0,t.jsx)(eg,{copyable:!0,children:p.project_id})]})]})]}),(0,t.jsx)(n.Button,{type:"primary",icon:(0,t.jsx)(es.EditIcon,{size:16}),onClick:()=>v(!0),children:"Edit Project"})]}),(0,t.jsx)(V.Row,{style:{marginBottom:24},children:(0,t.jsx)(d.Card,{children:(0,t.jsxs)(ee.Descriptions,{title:"Project Details",column:1,children:[(0,t.jsx)(ee.Descriptions.Item,{label:"Description",children:p.description||"—"}),(0,t.jsxs)(ee.Descriptions.Item,{label:"Created",children:[new Date(p.created_at).toLocaleString(),p.created_by&&(0,t.jsxs)(eg,{children:[" ","by"," ",(0,t.jsx)(ed.default,{userId:p.created_by})]})]}),(0,t.jsxs)(ee.Descriptions.Item,{label:"Last Updated",children:[new Date(p.updated_at).toLocaleString(),p.updated_by&&(0,t.jsxs)(eg,{children:[" ","by"," ",(0,t.jsx)(ed.default,{userId:p.updated_by})]})]})]})})}),(0,t.jsxs)(V.Row,{gutter:[16,16],style:{marginBottom:24},children:[(0,t.jsx)(R.Col,{xs:24,lg:8,children:(0,t.jsx)(d.Card,{title:(0,t.jsxs)(c.Flex,{align:"center",gap:8,children:[(0,t.jsx)(eo,{size:16}),"Budget"]}),style:{height:"100%"},children:(0,t.jsxs)(c.Flex,{vertical:!0,gap:16,children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(eg,{strong:!0,style:{fontSize:28,lineHeight:1},children:["$",w.toFixed(2)]}),(0,t.jsx)("br",{}),(0,t.jsx)(eg,{type:"secondary",children:S?`of $${_.toFixed(2)} budget`:"No budget limit"})]}),S&&(0,t.jsxs)("div",{children:[(0,t.jsx)(er.Progress,{percent:Math.round(10*T)/10,strokeColor:T>=90?"#f5222d":T>=70?"#faad14":"#52c41a",showInfo:!1}),(0,t.jsxs)(eg,{type:"secondary",style:{fontSize:12},children:[(Math.round(10*T)/10).toFixed(1),"% utilized"]})]})]})})}),(0,t.jsx)(R.Col,{xs:24,lg:16,children:(0,t.jsx)(d.Card,{title:"Spend by Model",style:{height:"100%"},children:B.length>0?(0,t.jsx)(ea.BarChart,{data:B,index:"model",categories:["spend"],colors:["cyan"],layout:"vertical",valueFormatter:e=>`$${e.toFixed(4)}`,yAxisWidth:140,showLegend:!1,style:{height:Math.max(40*B.length,120)}}):(0,t.jsx)(et.Empty,{description:"No model spend recorded yet",image:et.Empty.PRESENTED_IMAGE_SIMPLE})})})]}),(0,t.jsxs)(V.Row,{gutter:[16,16],style:{marginBottom:24},children:[(0,t.jsx)(R.Col,{xs:24,lg:12,children:(0,t.jsx)(d.Card,{title:(0,t.jsxs)(c.Flex,{align:"center",gap:8,children:[(0,t.jsx)(ei.KeyIcon,{size:16}),"Keys"]}),style:{height:"100%"},children:(0,t.jsx)(et.Empty,{description:"No keys to display",image:et.Empty.PRESENTED_IMAGE_SIMPLE})})}),(0,t.jsx)(R.Col,{xs:24,lg:12,children:(0,t.jsx)(d.Card,{title:(0,t.jsxs)(c.Flex,{align:"center",gap:8,children:[(0,t.jsx)(en.UsersIcon,{size:16}),"Team"]}),style:{height:"100%"},children:j?(o=j.max_budget??null,i=j.spend??0,u=(m=null!=o&&o>0)?Math.min(i/o*100,100):0,(0,t.jsxs)(c.Flex,{vertical:!0,gap:12,children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(eg,{strong:!0,style:{fontSize:16},children:j.team_alias||j.team_id}),(0,t.jsx)("br",{}),(0,t.jsxs)(eg,{type:"secondary",style:{fontSize:12},children:["ID:"," ",(0,t.jsx)(eg,{copyable:!0,style:{fontSize:12},children:j.team_id})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(eg,{type:"secondary",style:{fontSize:12,display:"block",marginBottom:4},children:"Models"}),(j.models?.length??0)>0?(0,t.jsx)(c.Flex,{wrap:"wrap",gap:4,style:{maxHeight:60,overflow:"hidden"},children:j.models?.map(e=>(0,t.jsx)(f.Tag,{style:{margin:0},children:e},e))}):(0,t.jsx)(eg,{type:"secondary",children:"All models"})]}),(0,t.jsxs)("div",{children:[(0,t.jsxs)(c.Flex,{justify:"space-between",align:"center",style:{marginBottom:2},children:[(0,t.jsx)(eg,{type:"secondary",style:{fontSize:12},children:"Spend"}),(0,t.jsxs)(eg,{style:{fontSize:12},children:["$",i.toFixed(2),m?(0,t.jsxs)(eg,{type:"secondary",style:{fontSize:12},children:[" ","/ $",o.toFixed(2)]}):(0,t.jsxs)(eg,{type:"secondary",style:{fontSize:12},children:[" ","(Unlimited)"]})]})]}),m&&(0,t.jsx)(er.Progress,{percent:Math.round(10*u)/10,strokeColor:u>=90?"#f5222d":u>=70?"#faad14":"#52c41a",size:"small",showInfo:!1})]}),(0,t.jsxs)(c.Flex,{justify:"space-between",children:[(0,t.jsx)(eg,{type:"secondary",style:{fontSize:12},children:"Members"}),(0,t.jsx)(eg,{style:{fontSize:12},children:j.members_with_roles?.length??0})]})]})):p.team_id?(0,t.jsx)(c.Flex,{justify:"center",align:"center",style:{padding:16},children:(0,t.jsx)(h.Spin,{indicator:(0,t.jsx)(s.LoadingOutlined,{spin:!0}),size:"small"})}):(0,t.jsx)(et.Empty,{description:"No team assigned",image:et.Empty.PRESENTED_IMAGE_SIMPLE})})})]}),(0,t.jsx)(eu,{isOpen:b,project:p,onClose:()=>v(!1)})]}):(0,t.jsxs)(eh,{style:{padding:C.paddingLG,paddingInline:2*C.paddingLG},children:[(0,t.jsx)(n.Button,{icon:(0,t.jsx)(el.ArrowLeftIcon,{size:16}),onClick:l,type:"text",style:{marginBottom:16}}),(0,t.jsx)(et.Empty,{description:"Project not found"})]})}let{Title:ef,Text:ey}=C.Typography,{Content:ej}=u.Layout;function eC(){let{token:e}=y.theme.useToken(),{data:u,isLoading:C}=(0,r.useProjects)(),{data:w,isLoading:_}=(0,a.useTeams)(),[S,T]=(0,k.useState)(null),[B,M]=(0,k.useState)(!1),[P,N]=(0,k.useState)(""),[I,z]=(0,k.useState)(1);(0,k.useEffect)(()=>{z(1)},[P]);let E=(0,k.useMemo)(()=>{let e=new Map;for(let t of w??[])e.set(t.team_id,t.team_alias??t.team_id);return e},[w]),F=(0,k.useMemo)(()=>{let e=u??[];if(!P)return e;let t=P.toLowerCase();return e.filter(e=>{let r=E.get(e.team_id??"")??"";return(e.project_alias??"").toLowerCase().includes(t)||e.project_id.toLowerCase().includes(t)||(e.description??"").toLowerCase().includes(t)||r.toLowerCase().includes(t)})},[u,P,E]),L=[{title:"ID",dataIndex:"project_id",key:"project_id",width:170,render:e=>(0,t.jsx)(o.IdCell,{value:e,onClick:T})},{title:"Name",dataIndex:"project_alias",key:"project_alias",sorter:(e,t)=>(e.project_alias??"").localeCompare(t.project_alias??""),render:e=>e??"—"},{title:"Team",key:"team",sorter:(e,t)=>{let r=E.get(e.team_id??"")??"",a=E.get(t.team_id??"")??"";return r.localeCompare(a)},render:(e,r)=>{if(!r.team_id)return"—";let a=E.get(r.team_id);return a||(_?(0,t.jsx)(h.Spin,{indicator:(0,t.jsx)(s.LoadingOutlined,{spin:!0}),size:"small"}):r.team_id)}},{title:"Models",key:"models",render:(e,r)=>{let a=r.models??[];return(0,t.jsx)(j.Tooltip,{title:a.length>0?a.join(", "):"No models",children:(0,t.jsx)(f.Tag,{color:"blue",style:{fontSize:14,padding:"2px 8px",margin:0},children:(0,t.jsxs)(c.Flex,{align:"center",gap:6,children:[(0,t.jsx)(b.LayersIcon,{size:14}),a.length]})})})}},{title:"Status",dataIndex:"blocked",key:"status",render:e=>(0,t.jsx)(f.Tag,{color:e?"red":"green",children:e?"Blocked":"Active"})},{title:"Created",dataIndex:"created_at",key:"created_at",sorter:(e,t)=>new Date(e.created_at).getTime()-new Date(t.created_at).getTime(),responsive:["lg"],render:e=>(0,t.jsx)(l.DateCell,{value:e,precision:"date"})},{title:"Updated",dataIndex:"updated_at",key:"updated_at",responsive:["xl"],render:e=>(0,t.jsx)(l.DateCell,{value:e,precision:"date"})}];return S?(0,t.jsx)(ex,{projectId:S,onBack:()=>T(null)}):(0,t.jsxs)(ej,{style:{padding:e.paddingLG,paddingInline:2*e.paddingLG},children:[(0,t.jsxs)(c.Flex,{justify:"space-between",align:"center",style:{marginBottom:16},children:[(0,t.jsxs)(g.Space,{direction:"vertical",size:0,children:[(0,t.jsx)(ef,{level:2,style:{margin:0},children:"Projects"}),(0,t.jsx)(ey,{type:"secondary",children:"Manage projects within your teams"})]}),(0,t.jsx)(n.Button,{type:"primary",icon:(0,t.jsx)(i.PlusOutlined,{}),onClick:()=>M(!0),children:"Create Project"})]}),(0,t.jsxs)(d.Card,{styles:{body:{padding:0}},children:[(0,t.jsxs)(c.Flex,{justify:"space-between",align:"center",style:{padding:"12px 16px"},children:[(0,t.jsx)(m.Input,{prefix:(0,t.jsx)(v.SearchIcon,{size:16}),placeholder:"Search projects by name, ID, description, or team...",style:{maxWidth:400},value:P,onChange:e=>N(e.target.value),allowClear:!0}),(0,t.jsx)(p.Pagination,{current:I,total:F.length,pageSize:10,onChange:e=>z(e),size:"small",showTotal:e=>`${e} projects`,showSizeChanger:!1})]}),(0,t.jsx)(x.Table,{columns:L,dataSource:F.slice((I-1)*10,10*I),rowKey:"project_id",loading:C,pagination:!1})]}),(0,t.jsx)(Q,{isOpen:B,onClose:()=>M(!1)})]})}e.s(["default",0,function(){return(0,F.default)(),(0,t.jsx)(eC,{})}],454587)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0v1rxqc1hqmrl.js b/litellm/proxy/_experimental/out/_next/static/chunks/0v1rxqc1hqmrl.js new file mode 100644 index 00000000000..cde8446c0d1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0v1rxqc1hqmrl.js @@ -0,0 +1,4 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,783222,433336,80758,402155,368578,544508,746725,835696,941444,914189,394487,e=>{"use strict";let t;e.i(247167);let r=e=>e?.ownerDocument??document,n=e=>e&&"window"in e&&e.window===e?e:r(e).defaultView||window;function o(e,t){return!!t&&!!e&&e.contains(t)}function s(e){return e.target}let a=null;"u">typeof Element&&Element.prototype;let i=["input:not([disabled]):not([type=hidden])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","a[href]","area[href]","summary","iframe","object","embed","audio[controls]","video[controls]",'[contenteditable]:not([contenteditable^="false"])',"permission"];i.join(":not([hidden]),"),i.push('[tabindex]:not([tabindex="-1"]):not([disabled])'),i.join(':not([hidden]):not([tabindex="-1"]),');var l=e.i(271645);let u="u">typeof document?l.default.useLayoutEffect:()=>{};function c(e){return e.nativeEvent=e,e.isDefaultPrevented=()=>e.defaultPrevented,e.isPropagationStopped=()=>e.cancelBubble,e.persist=()=>{},e}function d(e){let t=(0,l.useRef)({isFocused:!1,observer:null});return u(()=>{let e=t.current;return()=>{e.observer&&(e.observer.disconnect(),e.observer=null)}},[]),(0,l.useCallback)(r=>{let n=s(r);(n instanceof HTMLButtonElement||n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement)&&(t.current.isFocused=!0,n.addEventListener("focusout",r=>{if(t.current.isFocused=!1,n.disabled){let t=c(r);e?.(t)}t.current.observer&&(t.current.observer.disconnect(),t.current.observer=null)},{once:!0}),t.current.observer=new MutationObserver(()=>{if(t.current.isFocused&&n.disabled){t.current.observer?.disconnect();let e=n===((e=document)=>e.activeElement)()?null:((e=document)=>e.activeElement)();n.dispatchEvent(new FocusEvent("blur",{relatedTarget:e})),n.dispatchEvent(new FocusEvent("focusout",{bubbles:!0,relatedTarget:e}))}}),t.current.observer.observe(n,{attributes:!0,attributeFilter:["disabled"]}))},[e])}function f(e){if("u"e.test(t.brand))||e.test(window.navigator.userAgent)}function p(e){return"u">typeof window&&null!=window.navigator&&e.test(window.navigator.userAgentData?.platform||window.navigator.platform)}function m(e){let t=null;return()=>(null==t&&(t=e()),t)}let b=m(function(){return p(/^Mac/i)}),v=m(function(){return p(/^iPhone/i)}),h=m(function(){return p(/^iPad/i)||b()&&navigator.maxTouchPoints>1}),g=m(function(){return v()||h()});m(function(){return b()||g()});let y=m(function(){return f(/AppleWebKit/i)&&!E()}),E=m(function(){return f(/Chrome/i)}),T=m(function(){return f(/Android/i)}),w=m(function(){return f(/Firefox/i)});function x(e,t,r=!0){let{metaKey:n,ctrlKey:o,altKey:s,shiftKey:i}=t;w()&&window.event?.type?.startsWith("key")&&"_blank"===e.target&&(b()?n=!0:o=!0);let l=y()&&b()&&!h()&&1?new KeyboardEvent("keydown",{keyIdentifier:"Enter",metaKey:n,ctrlKey:o,altKey:s,shiftKey:i}):new MouseEvent("click",{metaKey:n,ctrlKey:o,altKey:s,shiftKey:i,detail:1,bubbles:!0,cancelable:!0});x.isOpening=r;if(function(){if(null==a){a=!1;try{document.createElement("div").focus({get preventScroll(){return a=!0,!0}})}catch{}}return a}())e.focus({preventScroll:!0});else{let t=function(e){let t=e.parentNode,r=[],n=document.scrollingElement||document.documentElement;for(;t instanceof HTMLElement&&t!==n;)(t.offsetHeighttypeof window&&window.document&&window.document.createElement,new WeakMap;l.default.useId;let P=null,k=new Set,L=new Map,N=!1,C=!1,I={Tab:!0,Escape:!0};function S(e,t){for(let r of k)r(e,t)}function A(e){N=!0,x.isOpening||e.metaKey||!b()&&e.altKey||e.ctrlKey||"Control"===e.key||"Shift"===e.key||"Meta"===e.key||(P="keyboard",S("keyboard",e))}function M(e){P="pointer","pointerType"in e&&e.pointerType,("mousedown"===e.type||"pointerdown"===e.type)&&(N=!0,S("pointer",e))}function R(e){x.isOpening||(""!==e.pointerType||!e.isTrusted)&&(T()&&e.pointerType?"click"!==e.type||1!==e.buttons:0!==e.detail||e.pointerType)||(N=!0,P="virtual")}function O(e){let t=n(s(e)),o=r(s(e));s(e)!==t&&s(e)!==o&&e.isTrusted&&(N||C||(P="virtual",S("virtual",e)),N=!1,C=!1)}function D(){N=!1,C=!0}function H(e){if("u"typeof PointerEvent&&(o.addEventListener("pointerdown",M,!0),o.addEventListener("pointermove",M,!0),o.addEventListener("pointerup",M,!0)),t.addEventListener("beforeunload",()=>{j(e)},{once:!0}),L.set(t,{focus:s})}let j=(e,t)=>{let o=n(e),s=r(e);t&&s.removeEventListener("DOMContentLoaded",t),L.has(o)&&(o.HTMLElement.prototype.focus=L.get(o).focus,s.removeEventListener("keydown",A,!0),s.removeEventListener("keyup",A,!0),s.removeEventListener("click",R,!0),o.removeEventListener("focus",O,!0),o.removeEventListener("blur",D,!1),"u">typeof PointerEvent&&(s.removeEventListener("pointerdown",M,!0),s.removeEventListener("pointermove",M,!0),s.removeEventListener("pointerup",M,!0)),L.delete(o))};function K(){return"pointer"!==P}"u">typeof document&&("loading"!==(t=r(void 0)).readyState?H(void 0):t.addEventListener("DOMContentLoaded",()=>{H(void 0)}));let W=new Set(["checkbox","radio","range","color","file","image","button","submit","reset"]);function B(){let e=(0,l.useRef)(new Map),t=(0,l.useCallback)((t,r,n,o)=>{let s=o?.once?(...t)=>{e.current.delete(n),n(...t)}:n;e.current.set(n,{type:r,eventTarget:t,fn:s,options:o}),t.addEventListener(r,s,o)},[]),r=(0,l.useCallback)((t,r,n,o)=>{let s=e.current.get(n)?.fn||n;t.removeEventListener(r,s,o),e.current.delete(n)},[]),n=(0,l.useCallback)(()=>{e.current.forEach((e,t)=>{r(e.eventTarget,e.type,t,e.options)})},[r]);return(0,l.useEffect)(()=>n,[n]),{addGlobalListener:t,removeGlobalListener:r,removeAllGlobalListeners:n}}e.s(["useFocusRing",0,function(e={}){var t;let{autoFocus:a=!1,isTextInput:i,within:u}=e,f=(0,l.useRef)({isFocused:!1,isFocusVisible:a||K()}),[p,m]=(0,l.useState)(!1),[b,v]=(0,l.useState)(()=>f.current.isFocused&&f.current.isFocusVisible),h=(0,l.useCallback)(()=>v(f.current.isFocused&&f.current.isFocusVisible),[]),g=(0,l.useCallback)(e=>{f.current.isFocused=e,f.current.isFocusVisible=K(),m(e),h()},[h]);t={enabled:p,isTextInput:i},H(),(0,l.useEffect)(()=>{if(t?.enabled===!1)return;let e=(e,o)=>{var a;let i,l,u,c,d,p,m,b;a=!!t?.isTextInput,l=r(i=o?s(o):void 0),c=void 0!==(u=n(i))?u.HTMLInputElement:HTMLInputElement,d=void 0!==u?u.HTMLTextAreaElement:HTMLTextAreaElement,p=void 0!==u?u.HTMLElement:HTMLElement,m=void 0!==u?u.KeyboardEvent:KeyboardEvent,b=((e=document)=>e.activeElement)(l),(a=a||b instanceof c&&!W.has(b.type)||b instanceof d||b instanceof p&&b.isContentEditable)&&"keyboard"===e&&o instanceof m&&!I[o.key]||(e=>{f.current.isFocusVisible=e,h()})(K())};return k.add(e),()=>{k.delete(e)}},[i,p]);let{focusProps:y}=function(e){let{isDisabled:t,onFocus:n,onBlur:o,onFocusChange:a}=e,i=(0,l.useCallback)(e=>{if(s(e)===e.currentTarget)return o&&o(e),a&&a(!1),!0},[o,a]),u=d(i),c=(0,l.useCallback)(e=>{let t=s(e),o=r(t),i=o?((e=document)=>e.activeElement)(o):((e=document)=>e.activeElement)();t===e.currentTarget&&t===i&&(n&&n(e),a&&a(!0),u(e))},[a,n,u]);return{focusProps:{onFocus:!t&&(n||a||o)?c:void 0,onBlur:!t&&(o||a)?i:void 0}}}({isDisabled:u,onFocusChange:g}),{focusWithinProps:E}=function(e){let{isDisabled:t,onBlurWithin:n,onFocusWithin:a,onFocusWithinChange:i}=e,u=(0,l.useRef)({isFocusWithin:!1}),{addGlobalListener:f,removeAllGlobalListeners:p}=B(),m=(0,l.useCallback)(e=>{o(e.currentTarget,s(e))&&u.current.isFocusWithin&&!o(e.currentTarget,e.relatedTarget)&&(u.current.isFocusWithin=!1,p(),n&&n(e),i&&i(!1))},[n,i,u,p]),b=d(m),v=(0,l.useCallback)(e=>{if(!o(e.currentTarget,s(e)))return;let t=s(e),n=r(t),l=((e=document)=>e.activeElement)(n);if(!u.current.isFocusWithin&&l===t){a&&a(e),i&&i(!0),u.current.isFocusWithin=!0,b(e);let t=e.currentTarget;f(n,"focus",e=>{let r=s(e);if(u.current.isFocusWithin&&!o(t,r)){let e=new n.defaultView.FocusEvent("blur",{relatedTarget:r});Object.defineProperty(e,"target",{value:t}),Object.defineProperty(e,"currentTarget",{value:t}),m(c(e))}},{capture:!0})}},[a,i,b,f,m]);return t?{focusWithinProps:{onFocus:void 0,onBlur:void 0}}:{focusWithinProps:{onFocus:v,onBlur:m}}}({isDisabled:!u,onFocusWithinChange:g});return{isFocused:p,isFocusVisible:b,focusProps:u?E:y}}],783222);let V=!1,_=0;function G(e){"touch"===e.pointerType&&(V=!0,setTimeout(()=>{V=!1},500))}function U(){let e=r(null);if(void 0!==e)return 0===_&&"u">typeof PointerEvent&&e.addEventListener("pointerup",G),_++,()=>{!(--_>0)&&"u">typeof PointerEvent&&e.removeEventListener("pointerup",G)}}e.s(["useHover",0,function(e){let{onHoverStart:t,onHoverChange:n,onHoverEnd:a,isDisabled:i}=e,[u,c]=(0,l.useState)(!1),d=(0,l.useRef)({isHovered:!1,ignoreEmulatedMouseEvents:!1,pointerType:"",target:null}).current;(0,l.useEffect)(U,[]);let{addGlobalListener:f,removeAllGlobalListeners:p}=B(),{hoverProps:m,triggerHoverEnd:b}=(0,l.useMemo)(()=>{let e=(e,t)=>{let r=d.target;d.pointerType="",d.target=null,"touch"!==t&&d.isHovered&&r&&(d.isHovered=!1,p(),a&&a({type:"hoverend",target:r,pointerType:t}),n&&n(!1),c(!1))},l={};return"u">typeof PointerEvent&&(l.onPointerEnter=a=>{V&&"mouse"===a.pointerType||((a,l)=>{if(d.pointerType=l,i||"touch"===l||d.isHovered||!o(a.currentTarget,s(a)))return;d.isHovered=!0;let u=a.currentTarget;d.target=u,f(r(s(a)),"pointerover",t=>{d.isHovered&&d.target&&!o(d.target,s(t))&&e(t,t.pointerType)},{capture:!0}),t&&t({type:"hoverstart",target:u,pointerType:l}),n&&n(!0),c(!0)})(a,a.pointerType)},l.onPointerLeave=t=>{!i&&o(t.currentTarget,s(t))&&e(t,t.pointerType)}),{hoverProps:l,triggerHoverEnd:e}},[t,n,a,i,d,f,p]);return(0,l.useEffect)(()=>{i&&b({currentTarget:d.target},d.pointerType)},[i]),{hoverProps:m,isHovered:u}}],433336);var $=Object.defineProperty,q=(e,t,r)=>{let n;return(n="symbol"!=typeof t?t+"":t)in e?$(e,n,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[n]=r,r};let X=new class{constructor(){q(this,"current",this.detect()),q(this,"handoffState","pending"),q(this,"currentId",0)}set(e){this.current!==e&&(this.handoffState="pending",this.currentId=0,this.current=e)}reset(){this.set(this.detect())}nextId(){return++this.currentId}get isServer(){return"server"===this.current}get isClient(){return"client"===this.current}detect(){return"u"setTimeout(()=>{throw e}))}function Z(){let e=[],t={addEventListener:(e,r,n,o)=>(e.addEventListener(r,n,o),t.add(()=>e.removeEventListener(r,n,o))),requestAnimationFrame(...e){let r=requestAnimationFrame(...e);return t.add(()=>cancelAnimationFrame(r))},nextFrame:(...e)=>t.requestAnimationFrame(()=>t.requestAnimationFrame(...e)),setTimeout(...e){let r=setTimeout(...e);return t.add(()=>clearTimeout(r))},microTask(...e){let r={current:!0};return z(()=>{r.current&&e[0]()}),t.add(()=>{r.current=!1})},style(e,t,r){let n=e.style.getPropertyValue(t);return Object.assign(e.style,{[t]:r}),this.add(()=>{Object.assign(e.style,{[t]:n})})},group(e){let t=Z();return e(t),this.add(()=>t.dispose())},add:t=>(e.includes(t)||e.push(t),()=>{let r=e.indexOf(t);if(r>=0)for(let t of e.splice(r,1))t()}),dispose(){for(let t of e.splice(0))t()}};return t}function J(){let[e]=(0,l.useState)(Z);return(0,l.useEffect)(()=>()=>e.dispose(),[e]),e}e.s(["env",0,X],80758),e.s(["getOwnerDocument",0,Y],402155),e.s(["microTask",0,z],368578),e.s(["disposables",0,Z],544508),e.s(["useDisposables",0,J],746725);let Q=(e,t)=>{X.isServer?(0,l.useEffect)(e,t):(0,l.useLayoutEffect)(e,t)};function ee(e){let t=(0,l.useRef)(e);return Q(()=>{t.current=e},[e]),t}e.s(["useIsoMorphicEffect",0,Q],835696),e.s(["useLatestValue",0,ee],941444);let et=function(e){let t=ee(e);return l.default.useCallback((...e)=>t.current(...e),[t])};e.s(["useEvent",0,et],914189),e.s(["useActivePress",0,function({disabled:e=!1}={}){let t=(0,l.useRef)(null),[r,n]=(0,l.useState)(!1),o=J(),s=et(()=>{t.current=null,n(!1),o.dispose()}),a=et(e=>{if(o.dispose(),null===t.current){t.current=e.currentTarget,n(!0);{let r=Y(e.currentTarget);o.addEventListener(r,"pointerup",s,!1),o.addEventListener(r,"pointermove",e=>{if(t.current){var r,o;let s,a;n((s=e.width/2,a=e.height/2,r={top:e.clientY-a,right:e.clientX+s,bottom:e.clientY+a,left:e.clientX-s},o=t.current.getBoundingClientRect(),!(!r||!o||r.righto.right||r.bottomo.bottom)))}},!1),o.addEventListener(r,"pointercancel",s,!1)}}});return{pressed:r,pressProps:e?{}:{onPointerDown:a,onPointerUp:s,onClick:s}}}],394487)},144279,294316,e=>{"use strict";var t=e.i(271645);e.s(["useResolveButtonType",0,function(e,r){return(0,t.useMemo)(()=>{var t;if(e.type)return e.type;let n=null!=(t=e.as)?t:"button";if("string"==typeof n&&"button"===n.toLowerCase()||(null==r?void 0:r.tagName)==="BUTTON"&&!r.hasAttribute("type"))return"button"},[e.type,e.as,r])}],144279);var r=e.i(914189);let n=Symbol();e.s(["optionalRef",0,function(e,t=!0){return Object.assign(e,{[n]:t})},"useSyncRefs",0,function(...e){let o=(0,t.useRef)(e);(0,t.useEffect)(()=>{o.current=e},[e]);let s=(0,r.useEvent)(e=>{for(let t of o.current)null!=t&&("function"==typeof t?t(e):t.current=e)});return e.every(e=>null==e||(null==e?void 0:e[n]))?void 0:s}],294316)},553521,e=>{"use strict";var t=e.i(271645),r=e.i(835696);e.s(["useIsMounted",0,function(){let e=(0,t.useRef)(!1);return(0,r.useIsoMorphicEffect)(()=>(e.current=!0,()=>{e.current=!1}),[]),e}])},732607,e=>{"use strict";e.s(["classNames",0,function(...e){return Array.from(new Set(e.flatMap(e=>"string"==typeof e?e.split(" "):[]))).filter(Boolean).join(" ")}])},397701,e=>{"use strict";e.s(["match",0,function e(t,r,...n){if(t in r){let e=r[t];return"function"==typeof e?e(...n):e}let o=Error(`Tried to handle "${t}" but there is no handler defined. Only defined handlers are: ${Object.keys(r).map(e=>`"${e}"`).join(", ")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(o,e),o}])},700020,e=>{"use strict";let t,r;var n=e.i(271645),o=e.i(732607),s=e.i(397701),a=((t=a||{})[t.None=0]="None",t[t.RenderStrategy=1]="RenderStrategy",t[t.Static=2]="Static",t),i=((r=i||{})[r.Unmount=0]="Unmount",r[r.Hidden=1]="Hidden",r);function l(e,t={},r,s,a){let{as:i=r,children:u,refName:p="ref",...m}=f(e,["unmount","static"]),b=void 0!==e.ref?{[p]:e.ref}:{},v="function"==typeof u?u(t):u;"className"in m&&m.className&&"function"==typeof m.className&&(m.className=m.className(t)),m["aria-labelledby"]&&m["aria-labelledby"]===m.id&&(m["aria-labelledby"]=void 0);let h={};if(t){let e=!1,r=[];for(let[n,o]of Object.entries(t))"boolean"==typeof o&&(e=!0),!0===o&&r.push(n.replace(/([A-Z])/g,e=>`-${e.toLowerCase()}`));if(e)for(let e of(h["data-headlessui-state"]=r.join(" "),r))h[`data-${e}`]=""}if(i===n.Fragment&&(Object.keys(d(m)).length>0||Object.keys(d(h)).length>0))if(!(0,n.isValidElement)(v)||Array.isArray(v)&&v.length>1){if(Object.keys(d(m)).length>0)throw Error(['Passing props on "Fragment"!',"",`The current component <${s} /> is rendering a "Fragment".`,"However we need to passthrough the following props:",Object.keys(d(m)).concat(Object.keys(d(h))).map(e=>` - ${e}`).join(` +`),"","You can apply a few solutions:",['Add an `as="..."` prop, to ensure that we render an actual element instead of a "Fragment".',"Render a single element as the child so that we can forward the props onto that element."].map(e=>` - ${e}`).join(` +`)].join(` +`))}else{var g;let e=v.props,t=null==e?void 0:e.className,r="function"==typeof t?(...e)=>(0,o.classNames)(t(...e),m.className):(0,o.classNames)(t,m.className),s=c(v.props,d(f(m,["ref"])));for(let e in h)e in s&&delete h[e];return(0,n.cloneElement)(v,Object.assign({},s,h,b,{ref:a((g=v,n.default.version.split(".")[0]>="19"?g.props.ref:g.ref),b.ref)},r?{className:r}:{}))}return(0,n.createElement)(i,Object.assign({},f(m,["ref"]),i!==n.Fragment&&b,i!==n.Fragment&&h),v)}function u(...e){return e.every(e=>null==e)?void 0:t=>{for(let r of e)null!=r&&("function"==typeof r?r(t):r.current=t)}}function c(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},r={};for(let n of e)for(let e in n)e.startsWith("on")&&"function"==typeof n[e]?(null!=r[e]||(r[e]=[]),r[e].push(n[e])):t[e]=n[e];if(t.disabled||t["aria-disabled"])for(let e in r)/^(on(?:Click|Pointer|Mouse|Key)(?:Down|Up|Press)?)$/.test(e)&&(r[e]=[e=>{var t;return null==(t=null==e?void 0:e.preventDefault)?void 0:t.call(e)}]);for(let e in r)Object.assign(t,{[e](t,...n){for(let o of r[e]){if((t instanceof Event||(null==t?void 0:t.nativeEvent)instanceof Event)&&t.defaultPrevented)return;o(t,...n)}}});return t}function d(e){let t=Object.assign({},e);for(let e in t)void 0===t[e]&&delete t[e];return t}function f(e,t=[]){let r=Object.assign({},e);for(let e of t)e in r&&delete r[e];return r}e.s(["RenderFeatures",0,a,"RenderStrategy",0,i,"compact",0,d,"forwardRefWithAs",0,function(e){var t;return Object.assign((0,n.forwardRef)(e),{displayName:null!=(t=e.displayName)?t:e.name})},"mergeProps",0,function(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},r={};for(let n of e)for(let e in n)e.startsWith("on")&&"function"==typeof n[e]?(null!=r[e]||(r[e]=[]),r[e].push(n[e])):t[e]=n[e];for(let e in r)Object.assign(t,{[e](...t){for(let n of r[e])null==n||n(...t)}});return t},"useRender",0,function(){let e,t,r=(e=(0,n.useRef)([]),t=(0,n.useCallback)(t=>{for(let r of e.current)null!=r&&("function"==typeof r?r(t):r.current=t)},[]),(...r)=>{if(!r.every(e=>null==e))return e.current=r,t});return(0,n.useCallback)(e=>(function({ourProps:e,theirProps:t,slot:r,defaultTag:n,features:o,visible:a=!0,name:i,mergeRefs:d}){d=null!=d?d:u;let f=c(t,e);if(a)return l(f,r,n,i,d);let p=null!=o?o:0;if(2&p){let{static:e=!1,...t}=f;if(e)return l(t,r,n,i,d)}if(1&p){let{unmount:e=!0,...t}=f;return(0,s.match)(+!e,{0:()=>null,1:()=>l({...t,hidden:!0,style:{display:"none"}},r,n,i,d)})}return l(f,r,n,i,d)})({mergeRefs:r,...e}),[r])}])},2788,e=>{"use strict";let t;var r=e.i(700020),n=((t=n||{})[t.None=1]="None",t[t.Focusable=2]="Focusable",t[t.Hidden=4]="Hidden",t);let o=(0,r.forwardRefWithAs)(function(e,t){var n;let{features:o=1,...s}=e,a={ref:t,"aria-hidden":(2&o)==2||(null!=(n=s["aria-hidden"])?n:void 0),hidden:(4&o)==4||void 0,style:{position:"fixed",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",...(4&o)==4&&(2&o)!=2&&{display:"none"}}};return(0,r.useRender)()({ourProps:a,theirProps:s,slot:{},defaultTag:"span",name:"Hidden"})});e.s(["Hidden",0,o,"HiddenFeatures",0,n])},640497,e=>{"use strict";var t=e.i(271645),r=e.i(553521),n=e.i(2788);e.s(["FocusSentinel",0,function({onFocus:e}){let[o,s]=(0,t.useState)(!0),a=(0,r.useIsMounted)();return o?t.default.createElement(n.Hidden,{as:"button",type:"button",features:n.HiddenFeatures.Focusable,onFocus:t=>{t.preventDefault();let r,n=50;r=requestAnimationFrame(function t(){if(n--<=0){r&&cancelAnimationFrame(r);return}if(e()){if(cancelAnimationFrame(r),!a.current)return;s(!1);return}r=requestAnimationFrame(t)})}}):null}])},652265,e=>{"use strict";let t,r,n,o,s;e.i(544508);var a=e.i(397701),i=e.i(402155);let l=["[contentEditable=true]","[tabindex]","a[href]","area[href]","button:not([disabled])","iframe","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].map(e=>`${e}:not([tabindex='-1'])`).join(","),u=["[data-autofocus]"].map(e=>`${e}:not([tabindex='-1'])`).join(",");var c=((t=c||{})[t.First=1]="First",t[t.Previous=2]="Previous",t[t.Next=4]="Next",t[t.Last=8]="Last",t[t.WrapAround=16]="WrapAround",t[t.NoScroll=32]="NoScroll",t[t.AutoFocus=64]="AutoFocus",t),d=((r=d||{})[r.Error=0]="Error",r[r.Overflow=1]="Overflow",r[r.Success=2]="Success",r[r.Underflow=3]="Underflow",r),f=((n=f||{})[n.Previous=-1]="Previous",n[n.Next=1]="Next",n);function p(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(l)).sort((e,t)=>Math.sign((e.tabIndex||Number.MAX_SAFE_INTEGER)-(t.tabIndex||Number.MAX_SAFE_INTEGER)))}var m=((o=m||{})[o.Strict=0]="Strict",o[o.Loose=1]="Loose",o),b=((s=b||{})[s.Keyboard=0]="Keyboard",s[s.Mouse=1]="Mouse",s);function v(e,t=e=>e){return e.slice().sort((e,r)=>{let n=t(e),o=t(r);if(null===n||null===o)return 0;let s=n.compareDocumentPosition(o);return s&Node.DOCUMENT_POSITION_FOLLOWING?-1:s&Node.DOCUMENT_POSITION_PRECEDING?1:0})}function h(e,t,{sorted:r=!0,relativeTo:n=null,skipElements:o=[]}={}){var s,a,i;let l=Array.isArray(e)?e.length>0?e[0].ownerDocument:document:e.ownerDocument,c=Array.isArray(e)?r?v(e):e:64&t?function(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(u)).sort((e,t)=>Math.sign((e.tabIndex||Number.MAX_SAFE_INTEGER)-(t.tabIndex||Number.MAX_SAFE_INTEGER)))}(e):p(e);o.length>0&&c.length>1&&(c=c.filter(e=>!o.some(t=>null!=t&&"current"in t?(null==t?void 0:t.current)===e:t===e))),n=null!=n?n:l.activeElement;let d=(()=>{if(5&t)return 1;if(10&t)return -1;throw Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),f=(()=>{if(1&t)return 0;if(2&t)return Math.max(0,c.indexOf(n))-1;if(4&t)return Math.max(0,c.indexOf(n))+1;if(8&t)return c.length-1;throw Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),m=32&t?{preventScroll:!0}:{},b=0,g=c.length,y;do{if(b>=g||b+g<=0)return 0;let e=f+b;if(16&t)e=(e+g)%g;else{if(e<0)return 3;if(e>=g)return 1}null==(y=c[e])||y.focus(m),b+=d}while(y!==l.activeElement)return 6&t&&null!=(i=null==(a=null==(s=y)?void 0:s.matches)?void 0:a.call(s,"textarea,input"))&&i&&y.select(),2}"u">typeof window&&"u">typeof document&&(document.addEventListener("keydown",e=>{e.metaKey||e.altKey||e.ctrlKey||(document.documentElement.dataset.headlessuiFocusVisible="")},!0),document.addEventListener("click",e=>{1===e.detail?delete document.documentElement.dataset.headlessuiFocusVisible:0===e.detail&&(document.documentElement.dataset.headlessuiFocusVisible="")},!0)),e.s(["Focus",0,c,"FocusResult",0,d,"FocusableMode",0,m,"focusFrom",0,function(e,t){return h(p(),t,{relativeTo:e})},"focusIn",0,h,"getFocusableElements",0,p,"isFocusableElement",0,function(e,t=0){var r;return e!==(null==(r=(0,i.getOwnerDocument)(e))?void 0:r.body)&&(0,a.match)(t,{0:()=>e.matches(l),1(){let t=e;for(;null!==t;){if(t.matches(l))return!0;t=t.parentElement}return!1}})},"sortByDomNode",0,v])},963703,e=>{"use strict";var t=e.i(271645);let r=t.createContext(null);e.s(["StableCollection",0,function({children:e}){let n=t.useRef({groups:new Map,get(e,t){var r;let n=this.groups.get(e);n||(n=new Map,this.groups.set(e,n));let o=null!=(r=n.get(t))?r:0;return n.set(t,o+1),[Array.from(n.keys()).indexOf(t),function(){let e=n.get(t);e>1?n.set(t,e-1):n.delete(t)}]}});return t.createElement(r.Provider,{value:n},e)},"useStableCollectionIndex",0,function(e){let n=t.useContext(r);if(!n)throw Error("You must wrap your component in a ");let o=t.useId(),[s,a]=n.current.get(e,o);return t.useEffect(()=>a,[]),s}])},998348,e=>{"use strict";let t;var r=((t=r||{}).Space=" ",t.Enter="Enter",t.Escape="Escape",t.Backspace="Backspace",t.Delete="Delete",t.ArrowLeft="ArrowLeft",t.ArrowUp="ArrowUp",t.ArrowRight="ArrowRight",t.ArrowDown="ArrowDown",t.Home="Home",t.End="End",t.PageUp="PageUp",t.PageDown="PageDown",t.Tab="Tab",t);e.s(["Keys",0,r])},970554,e=>{"use strict";let t,r,n;var o=e.i(783222),s=e.i(433336),a=e.i(271645),i=e.i(394487),l=e.i(914189),u=e.i(835696),c=e.i(941444),d=e.i(144279),f=e.i(294316),p=e.i(640497),m=e.i(2788),b=e.i(652265),v=e.i(397701),h=e.i(368578),g=e.i(402155),y=e.i(700020),E=e.i(963703),T=e.i(998348),w=((t=w||{})[t.Forwards=0]="Forwards",t[t.Backwards=1]="Backwards",t),x=((r=x||{})[r.Less=-1]="Less",r[r.Equal=0]="Equal",r[r.Greater=1]="Greater",r),F=((n=F||{})[n.SetSelectedIndex=0]="SetSelectedIndex",n[n.RegisterTab=1]="RegisterTab",n[n.UnregisterTab=2]="UnregisterTab",n[n.RegisterPanel=3]="RegisterPanel",n[n.UnregisterPanel=4]="UnregisterPanel",n);let P={0(e,t){var r;let n=(0,b.sortByDomNode)(e.tabs,e=>e.current),o=(0,b.sortByDomNode)(e.panels,e=>e.current),s=n.filter(e=>{var t;return!(null!=(t=e.current)&&t.hasAttribute("disabled"))}),a={...e,tabs:n,panels:o};if(t.index<0||t.index>n.length-1){let r=(0,v.match)(Math.sign(t.index-e.selectedIndex),{[-1]:()=>1,0:()=>(0,v.match)(Math.sign(t.index),{[-1]:()=>0,0:()=>0,1:()=>1}),1:()=>0});if(0===s.length)return a;let o=(0,v.match)(r,{0:()=>n.indexOf(s[0]),1:()=>n.indexOf(s[s.length-1])});return{...a,selectedIndex:-1===o?e.selectedIndex:o}}let i=n.slice(0,t.index),l=[...n.slice(t.index),...i].find(e=>s.includes(e));if(!l)return a;let u=null!=(r=n.indexOf(l))?r:e.selectedIndex;return -1===u&&(u=e.selectedIndex),{...a,selectedIndex:u}},1(e,t){if(e.tabs.includes(t.tab))return e;let r=e.tabs[e.selectedIndex],n=(0,b.sortByDomNode)([...e.tabs,t.tab],e=>e.current),o=e.selectedIndex;return e.info.current.isControlled||-1===(o=n.indexOf(r))&&(o=e.selectedIndex),{...e,tabs:n,selectedIndex:o}},2:(e,t)=>({...e,tabs:e.tabs.filter(e=>e!==t.tab)}),3:(e,t)=>e.panels.includes(t.panel)?e:{...e,panels:(0,b.sortByDomNode)([...e.panels,t.panel],e=>e.current)},4:(e,t)=>({...e,panels:e.panels.filter(e=>e!==t.panel)})},k=(0,a.createContext)(null);function L(e){let t=(0,a.useContext)(k);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,L),t}return t}k.displayName="TabsDataContext";let N=(0,a.createContext)(null);function C(e){let t=(0,a.useContext)(N);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,C),t}return t}function I(e,t){return(0,v.match)(t.type,P,e,t)}N.displayName="TabsActionsContext";let S=y.RenderFeatures.RenderStrategy|y.RenderFeatures.Static,A=Object.assign((0,y.forwardRefWithAs)(function(e,t){var r,n;let c=(0,a.useId)(),{id:p=`headlessui-tabs-tab-${c}`,disabled:m=!1,autoFocus:w=!1,...x}=e,{orientation:F,activation:P,selectedIndex:k,tabs:N,panels:I}=L("Tab"),S=C("Tab"),A=L("Tab"),[M,R]=(0,a.useState)(null),O=(0,a.useRef)(null),D=(0,f.useSyncRefs)(O,t,R);(0,u.useIsoMorphicEffect)(()=>S.registerTab(O),[S,O]);let H=(0,E.useStableCollectionIndex)("tabs"),j=N.indexOf(O);-1===j&&(j=H);let K=j===k,W=(0,l.useEvent)(e=>{var t;let r=e();if(r===b.FocusResult.Success&&"auto"===P){let e=null==(t=(0,g.getOwnerDocument)(O))?void 0:t.activeElement,r=A.tabs.findIndex(t=>t.current===e);-1!==r&&S.change(r)}return r}),B=(0,l.useEvent)(e=>{let t=N.map(e=>e.current).filter(Boolean);if(e.key===T.Keys.Space||e.key===T.Keys.Enter){e.preventDefault(),e.stopPropagation(),S.change(j);return}switch(e.key){case T.Keys.Home:case T.Keys.PageUp:return e.preventDefault(),e.stopPropagation(),W(()=>(0,b.focusIn)(t,b.Focus.First));case T.Keys.End:case T.Keys.PageDown:return e.preventDefault(),e.stopPropagation(),W(()=>(0,b.focusIn)(t,b.Focus.Last))}if(W(()=>(0,v.match)(F,{vertical:()=>e.key===T.Keys.ArrowUp?(0,b.focusIn)(t,b.Focus.Previous|b.Focus.WrapAround):e.key===T.Keys.ArrowDown?(0,b.focusIn)(t,b.Focus.Next|b.Focus.WrapAround):b.FocusResult.Error,horizontal:()=>e.key===T.Keys.ArrowLeft?(0,b.focusIn)(t,b.Focus.Previous|b.Focus.WrapAround):e.key===T.Keys.ArrowRight?(0,b.focusIn)(t,b.Focus.Next|b.Focus.WrapAround):b.FocusResult.Error}))===b.FocusResult.Success)return e.preventDefault()}),V=(0,a.useRef)(!1),_=(0,l.useEvent)(()=>{var e;V.current||(V.current=!0,null==(e=O.current)||e.focus({preventScroll:!0}),S.change(j),(0,h.microTask)(()=>{V.current=!1}))}),G=(0,l.useEvent)(e=>{e.preventDefault()}),{isFocusVisible:U,focusProps:$}=(0,o.useFocusRing)({autoFocus:w}),{isHovered:q,hoverProps:X}=(0,s.useHover)({isDisabled:m}),{pressed:Y,pressProps:z}=(0,i.useActivePress)({disabled:m}),Z=(0,a.useMemo)(()=>({selected:K,hover:q,active:Y,focus:U,autofocus:w,disabled:m}),[K,q,U,Y,w,m]),J=(0,y.mergeProps)({ref:D,onKeyDown:B,onMouseDown:G,onClick:_,id:p,role:"tab",type:(0,d.useResolveButtonType)(e,M),"aria-controls":null==(n=null==(r=I[j])?void 0:r.current)?void 0:n.id,"aria-selected":K,tabIndex:K?0:-1,disabled:m||void 0,autoFocus:w},$,X,z);return(0,y.useRender)()({ourProps:J,theirProps:x,slot:Z,defaultTag:"button",name:"Tabs.Tab"})}),{Group:(0,y.forwardRefWithAs)(function(e,t){let{defaultIndex:r=0,vertical:n=!1,manual:o=!1,onChange:s,selectedIndex:i=null,...d}=e,m=n?"vertical":"horizontal",v=o?"manual":"auto",h=null!==i,g=(0,c.useLatestValue)({isControlled:h}),T=(0,f.useSyncRefs)(t),[w,x]=(0,a.useReducer)(I,{info:g,selectedIndex:null!=i?i:r,tabs:[],panels:[]}),F=(0,a.useMemo)(()=>({selectedIndex:w.selectedIndex}),[w.selectedIndex]),P=(0,c.useLatestValue)(s||(()=>{})),L=(0,c.useLatestValue)(w.tabs),C=(0,a.useMemo)(()=>({orientation:m,activation:v,...w}),[m,v,w]),S=(0,l.useEvent)(e=>(x({type:1,tab:e}),()=>x({type:2,tab:e}))),A=(0,l.useEvent)(e=>(x({type:3,panel:e}),()=>x({type:4,panel:e}))),M=(0,l.useEvent)(e=>{R.current!==e&&P.current(e),h||x({type:0,index:e})}),R=(0,c.useLatestValue)(h?e.selectedIndex:w.selectedIndex),O=(0,a.useMemo)(()=>({registerTab:S,registerPanel:A,change:M}),[]);(0,u.useIsoMorphicEffect)(()=>{x({type:0,index:null!=i?i:r})},[i]),(0,u.useIsoMorphicEffect)(()=>{if(void 0===R.current||w.tabs.length<=0)return;let e=(0,b.sortByDomNode)(w.tabs,e=>e.current);e.some((e,t)=>w.tabs[t]!==e)&&M(e.indexOf(w.tabs[R.current]))});let D=(0,y.useRender)();return a.default.createElement(E.StableCollection,null,a.default.createElement(N.Provider,{value:O},a.default.createElement(k.Provider,{value:C},C.tabs.length<=0&&a.default.createElement(p.FocusSentinel,{onFocus:()=>{var e,t;for(let r of L.current)if((null==(e=r.current)?void 0:e.tabIndex)===0)return null==(t=r.current)||t.focus(),!0;return!1}}),D({ourProps:{ref:T},theirProps:d,slot:F,defaultTag:"div",name:"Tabs"}))))}),List:(0,y.forwardRefWithAs)(function(e,t){let{orientation:r,selectedIndex:n}=L("Tab.List"),o=(0,f.useSyncRefs)(t),s=(0,a.useMemo)(()=>({selectedIndex:n}),[n]);return(0,y.useRender)()({ourProps:{ref:o,role:"tablist","aria-orientation":r},theirProps:e,slot:s,defaultTag:"div",name:"Tabs.List"})}),Panels:(0,y.forwardRefWithAs)(function(e,t){let{selectedIndex:r}=L("Tab.Panels"),n=(0,f.useSyncRefs)(t),o=(0,a.useMemo)(()=>({selectedIndex:r}),[r]);return(0,y.useRender)()({ourProps:{ref:n},theirProps:e,slot:o,defaultTag:"div",name:"Tabs.Panels"})}),Panel:(0,y.forwardRefWithAs)(function(e,t){var r,n,s,i;let l=(0,a.useId)(),{id:c=`headlessui-tabs-panel-${l}`,tabIndex:d=0,...p}=e,{selectedIndex:b,tabs:v,panels:h}=L("Tab.Panel"),g=C("Tab.Panel"),T=(0,a.useRef)(null),w=(0,f.useSyncRefs)(T,t);(0,u.useIsoMorphicEffect)(()=>g.registerPanel(T),[g,T]);let x=(0,E.useStableCollectionIndex)("panels"),F=h.indexOf(T);-1===F&&(F=x);let P=F===b,{isFocusVisible:k,focusProps:N}=(0,o.useFocusRing)(),I=(0,a.useMemo)(()=>({selected:P,focus:k}),[P,k]),A=(0,y.mergeProps)({ref:w,id:c,role:"tabpanel","aria-labelledby":null==(n=null==(r=v[F])?void 0:r.current)?void 0:n.id,tabIndex:P?d:-1},N),M=(0,y.useRender)();return P||null!=(s=p.unmount)&&!s||null!=(i=p.static)&&i?M({ourProps:A,theirProps:p,slot:I,defaultTag:"div",features:S,visible:P,name:"Tabs.Panel"}):a.default.createElement(m.Hidden,{"aria-hidden":"true",...A})})});e.s(["Tab",0,A])},405371,910342,e=>{"use strict";var t=e.i(290571),r=e.i(271645),n=e.i(480731);let o=(0,r.createContext)(n.BaseColors.Blue);e.s(["default",0,o],910342);var s=e.i(970554),a=e.i(444755);let i=(0,e.i(673706).makeClassName)("TabList"),l=(0,r.createContext)("line"),u={line:(0,a.tremorTwMerge)("flex border-b space-x-4","border-tremor-border","dark:border-dark-tremor-border"),solid:(0,a.tremorTwMerge)("inline-flex p-0.5 rounded-tremor-default space-x-1.5","bg-tremor-background-subtle","dark:bg-dark-tremor-background-subtle")},c=r.default.forwardRef((e,n)=>{let{color:c,variant:d="line",children:f,className:p}=e,m=(0,t.__rest)(e,["color","variant","children","className"]);return r.default.createElement(s.Tab.List,Object.assign({ref:n,className:(0,a.tremorTwMerge)(i("root"),"justify-start overflow-x-clip",u[d],p)},m),r.default.createElement(l.Provider,{value:d},r.default.createElement(o.Provider,{value:c},f)))});c.displayName="TabList",e.s(["TabVariantContext",0,l,"default",0,c],405371)},197647,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(95779),o=e.i(444755),s=e.i(673706),a=e.i(271645),i=e.i(405371),l=e.i(910342);let u=(0,s.makeClassName)("Tab"),c=a.default.forwardRef((e,c)=>{let{icon:d,className:f,children:p}=e,m=(0,t.__rest)(e,["icon","className","children"]),b=(0,a.useContext)(i.TabVariantContext),v=(0,a.useContext)(l.default);return a.default.createElement(r.Tab,Object.assign({ref:c,className:(0,o.tremorTwMerge)(u("root"),"flex whitespace-nowrap truncate max-w-xs outline-none data-focus-visible:ring text-tremor-default transition duration-100",function(e,t){switch(e){case"line":return(0,o.tremorTwMerge)("data-[selected]:border-b-2 hover:border-b-2 border-transparent transition duration-100 -mb-px px-2 py-2","hover:border-tremor-content hover:text-tremor-content-emphasis text-tremor-content","[&:not([data-selected])]:dark:hover:border-dark-tremor-content-emphasis [&:not([data-selected])]:dark:hover:text-dark-tremor-content-emphasis [&:not([data-selected])]:dark:text-dark-tremor-content",t?(0,s.getColorClassNames)(t,n.colorPalette.border).selectBorderColor:["data-[selected]:border-tremor-brand data-[selected]:text-tremor-brand","data-[selected]:dark:border-dark-tremor-brand data-[selected]:dark:text-dark-tremor-brand"]);case"solid":return(0,o.tremorTwMerge)("border-transparent border rounded-tremor-small px-2.5 py-1","data-[selected]:border-tremor-border data-[selected]:bg-tremor-background data-[selected]:shadow-tremor-input [&:not([data-selected])]:hover:text-tremor-content-emphasis data-[selected]:text-tremor-brand [&:not([data-selected])]:text-tremor-content","dark:data-[selected]:border-dark-tremor-border dark:data-[selected]:bg-dark-tremor-background dark:data-[selected]:shadow-dark-tremor-input dark:[&:not([data-selected])]:hover:text-dark-tremor-content-emphasis dark:data-[selected]:text-dark-tremor-brand dark:[&:not([data-selected])]:text-dark-tremor-content",t?(0,s.getColorClassNames)(t,n.colorPalette.text).selectTextColor:"text-tremor-content dark:text-dark-tremor-content")}}(b,v),f,v&&(0,s.getColorClassNames)(v,n.colorPalette.text).selectTextColor)},m),d?a.default.createElement(d,{className:(0,o.tremorTwMerge)(u("icon"),"flex-none h-5 w-5",p?"mr-2":"")}):null,p?a.default.createElement("span",null,p):null)});c.displayName="Tab",e.s(["Tab",0,c],197647)},653824,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(444755),o=e.i(673706),s=e.i(271645);let a=(0,o.makeClassName)("TabGroup"),i=s.default.forwardRef((e,o)=>{let{defaultIndex:i,index:l,onIndexChange:u,children:c,className:d}=e,f=(0,t.__rest)(e,["defaultIndex","index","onIndexChange","children","className"]);return s.default.createElement(r.Tab.Group,Object.assign({as:"div",ref:o,defaultIndex:i,selectedIndex:l,onChange:u,className:(0,n.tremorTwMerge)(a("root"),"w-full",d)},f),c)});i.displayName="TabGroup",e.s(["TabGroup",0,i],653824)},881073,e=>{"use strict";var t=e.i(405371);e.s(["TabList",()=>t.default])},751734,e=>{"use strict";let t=(0,e.i(271645).createContext)(0);e.s(["default",0,t])},144582,e=>{"use strict";let t=(0,e.i(271645).createContext)({selectedValue:void 0,handleValueChange:void 0});e.s(["default",0,t])},404206,e=>{"use strict";var t=e.i(290571),r=e.i(751734),n=e.i(144582),o=e.i(444755),s=e.i(673706),a=e.i(271645);let i=(0,s.makeClassName)("TabPanel"),l=a.default.forwardRef((e,s)=>{let{children:l,className:u}=e,c=(0,t.__rest)(e,["children","className"]),{selectedValue:d}=(0,a.useContext)(n.default),f=d===(0,a.useContext)(r.default);return a.default.createElement("div",Object.assign({ref:s,className:(0,o.tremorTwMerge)(i("root"),"w-full mt-2",f?"":"hidden",u),"aria-selected":f?"true":"false"},c),l)});l.displayName="TabPanel",e.s(["TabPanel",0,l],404206)},723731,e=>{"use strict";var t=e.i(290571),r=e.i(970554),n=e.i(751734),o=e.i(144582),s=e.i(444755),a=e.i(673706),i=e.i(271645);let l=(0,a.makeClassName)("TabPanels"),u=i.default.forwardRef((e,a)=>{let{children:u,className:c}=e,d=(0,t.__rest)(e,["children","className"]);return i.default.createElement(r.Tab.Panels,Object.assign({as:"div",ref:a,className:(0,s.tremorTwMerge)(l("root"),"w-full",c)},d),({selectedIndex:e})=>i.default.createElement(o.default.Provider,{value:{selectedValue:e}},i.default.Children.map(u,(e,t)=>i.default.createElement(n.default.Provider,{value:t},e))))});u.displayName="TabPanels",e.s(["TabPanels",0,u],723731)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0vffq7buvlg04.js b/litellm/proxy/_experimental/out/_next/static/chunks/0vffq7buvlg04.js new file mode 100644 index 00000000000..3e8f10799af --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0vffq7buvlg04.js @@ -0,0 +1,13 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,695411,e=>{"use strict";var t=e.i(602869);let i=async e=>{try{let i=await (0,t.modelHubCall)(e);if(i?.data.length>0){let e=i.data.map(e=>({model_group:e.model_group,mode:e?.mode}));return e.sort((e,t)=>e.model_group.localeCompare(t.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}};e.s(["fetchAvailableModels",0,i])},555987,e=>{"use strict";var t=e.i(221688),i=e.i(950643);let n=/^(https?:|data:|blob:|\/\/)/i;e.s(["resolveLogoSrc",0,(e,o=t.serverRootPath)=>{if(e){let t;return n.test(e)?e:(t=(0,i.normalizeRootPath)(o),`${t}${e.startsWith("/")?e:`/${e}`}`)}}])},482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},165370,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(931067);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M272.9 512l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512zm304 0l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"}}]},name:"double-left",theme:"outlined"};var o=e.i(9583),l=t.forwardRef(function(e,l){return t.createElement(o.default,(0,i.default)({},e,{ref:l,icon:n}))});let a={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"}}]},name:"double-right",theme:"outlined"};var r=t.forwardRef(function(e,n){return t.createElement(o.default,(0,i.default)({},e,{ref:n,icon:a}))}),c=e.i(801312),u=e.i(286612),s=e.i(343794),d=e.i(211577),m=e.i(410160),g=e.i(209428),p=e.i(392221),b=e.i(914949),f=e.i(404948),h=e.i(244009);e.i(883110);let v={items_per_page:"条/页",jump_to:"跳至",jump_to_confirm:"确定",page:"页",prev_page:"上一页",next_page:"下一页",prev_5:"向前 5 页",next_5:"向后 5 页",prev_3:"向前 3 页",next_3:"向后 3 页",page_size:"页码"};var $=[10,20,50,100];let S=function(e){var i=e.pageSizeOptions,n=void 0===i?$:i,o=e.locale,l=e.changeSize,a=e.pageSize,r=e.goButton,c=e.quickGo,u=e.rootPrefixCls,s=e.disabled,d=e.buildOptionText,m=e.showSizeChanger,g=e.sizeChangerRender,b=t.default.useState(""),h=(0,p.default)(b,2),v=h[0],S=h[1],C=function(){return!v||Number.isNaN(v)?void 0:Number(v)},k="function"==typeof d?d:function(e){return"".concat(e," ").concat(o.items_per_page)},y=function(e){""!==v&&(e.keyCode===f.default.ENTER||"click"===e.type)&&(S(""),null==c||c(C()))},x="".concat(u,"-options");if(!m&&!c)return null;var z=null,E=null,N=null;return m&&g&&(z=g({disabled:s,size:a,onSizeChange:function(e){null==l||l(Number(e))},"aria-label":o.page_size,className:"".concat(x,"-size-changer"),options:(n.some(function(e){return e.toString()===a.toString()})?n:n.concat([a]).sort(function(e,t){return(Number.isNaN(Number(e))?0:Number(e))-(Number.isNaN(Number(t))?0:Number(t))})).map(function(e){return{label:k(e),value:e}})})),c&&(r&&(N="boolean"==typeof r?t.default.createElement("button",{type:"button",onClick:y,onKeyUp:y,disabled:s,className:"".concat(x,"-quick-jumper-button")},o.jump_to_confirm):t.default.createElement("span",{onClick:y,onKeyUp:y},r)),E=t.default.createElement("div",{className:"".concat(x,"-quick-jumper")},o.jump_to,t.default.createElement("input",{disabled:s,type:"text",value:v,onChange:function(e){S(e.target.value)},onKeyUp:y,onBlur:function(e){r||""===v||(S(""),e.relatedTarget&&(e.relatedTarget.className.indexOf("".concat(u,"-item-link"))>=0||e.relatedTarget.className.indexOf("".concat(u,"-item"))>=0)||null==c||c(C()))},"aria-label":o.page}),o.page,N)),t.default.createElement("li",{className:x},z,E)},C=function(e){var i=e.rootPrefixCls,n=e.page,o=e.active,l=e.className,a=e.showTitle,r=e.onClick,c=e.onKeyPress,u=e.itemRender,m="".concat(i,"-item"),g=(0,s.default)(m,"".concat(m,"-").concat(n),(0,d.default)((0,d.default)({},"".concat(m,"-active"),o),"".concat(m,"-disabled"),!n),l),p=u(n,"page",t.default.createElement("a",{rel:"nofollow"},n));return p?t.default.createElement("li",{title:a?String(n):null,className:g,onClick:function(){r(n)},onKeyDown:function(e){c(e,r,n)},tabIndex:0},p):null};var k=function(e,t,i){return i};function y(){}function x(e){var t=Number(e);return"number"==typeof t&&!Number.isNaN(t)&&isFinite(t)&&Math.floor(t)===t}function z(e,t,i){return Math.floor((i-1)/(void 0===e?t:e))+1}let E=function(e){var n,o,l,a,r=e.prefixCls,c=void 0===r?"rc-pagination":r,u=e.selectPrefixCls,$=e.className,E=e.current,N=e.defaultCurrent,j=e.total,B=void 0===j?0:j,M=e.pageSize,I=e.defaultPageSize,O=e.onChange,w=void 0===O?y:O,T=e.hideOnSinglePage,P=e.align,D=e.showPrevNextJumpers,H=e.showQuickJumper,A=e.showLessItems,_=e.showTitle,R=void 0===_||_,W=e.onShowSizeChange,L=void 0===W?y:W,q=e.locale,K=void 0===q?v:q,X=e.style,U=e.totalBoundaryShowSizeChanger,F=e.disabled,J=e.simple,G=e.showTotal,Q=e.showSizeChanger,V=void 0===Q?B>(void 0===U?50:U):Q,Y=e.sizeChangerRender,Z=e.pageSizeOptions,ee=e.itemRender,et=void 0===ee?k:ee,ei=e.jumpPrevIcon,en=e.jumpNextIcon,eo=e.prevIcon,el=e.nextIcon,ea=t.default.useRef(null),er=(0,b.default)(10,{value:M,defaultValue:void 0===I?10:I}),ec=(0,p.default)(er,2),eu=ec[0],es=ec[1],ed=(0,b.default)(1,{value:E,defaultValue:void 0===N?1:N,postState:function(e){return Math.max(1,Math.min(e,z(void 0,eu,B)))}}),em=(0,p.default)(ed,2),eg=em[0],ep=em[1],eb=t.default.useState(eg),ef=(0,p.default)(eb,2),eh=ef[0],ev=ef[1];(0,t.useEffect)(function(){ev(eg)},[eg]);var e$=Math.max(1,eg-(A?3:5)),eS=Math.min(z(void 0,eu,B),eg+(A?3:5));function eC(i,n){var o=i||t.default.createElement("button",{type:"button","aria-label":n,className:"".concat(c,"-item-link")});return"function"==typeof i&&(o=t.default.createElement(i,(0,g.default)({},e))),o}function ek(e){var t=e.target.value,i=z(void 0,eu,B);return""===t?t:Number.isNaN(Number(t))?eh:t>=i?i:Number(t)}var ey=B>eu&&H;function ex(e){var t=ek(e);switch(t!==eh&&ev(t),e.keyCode){case f.default.ENTER:ez(t);break;case f.default.UP:ez(t-1);break;case f.default.DOWN:ez(t+1)}}function ez(e){if(x(e)&&e!==eg&&x(B)&&B>0&&!F){var t=z(void 0,eu,B),i=e;return e>t?i=t:e<1&&(i=1),i!==eh&&ev(i),ep(i),null==w||w(i,eu),i}return eg}var eE=eg>1,eN=eg2?i-2:0),o=2;oB?B:eg*eu])),eH=null,eA=z(void 0,eu,B);if(T&&B<=eu)return null;var e_=[],eR={rootPrefixCls:c,onClick:ez,onKeyPress:eO,showTitle:R,itemRender:et,page:-1},eW=eg-1>0?eg-1:0,eL=eg+1=2*eF&&3!==eg&&(e_[0]=t.default.cloneElement(e_[0],{className:(0,s.default)("".concat(c,"-item-after-jump-prev"),e_[0].props.className)}),e_.unshift(eT)),eA-eg>=2*eF&&eg!==eA-2){var e2=e_[e_.length-1];e_[e_.length-1]=t.default.cloneElement(e2,{className:(0,s.default)("".concat(c,"-item-before-jump-next"),e2.props.className)}),e_.push(eH)}1!==eZ&&e_.unshift(t.default.createElement(C,(0,i.default)({},eR,{key:1,page:1}))),e0!==eA&&e_.push(t.default.createElement(C,(0,i.default)({},eR,{key:eA,page:eA})))}var e9=(n=et(eW,"prev",eC(eo,"prev page")),t.default.isValidElement(n)?t.default.cloneElement(n,{disabled:!eE}):n);if(e9){var e3=!eE||!eA;e9=t.default.createElement("li",{title:R?K.prev_page:null,onClick:ej,tabIndex:e3?null:0,onKeyDown:function(e){eO(e,ej)},className:(0,s.default)("".concat(c,"-prev"),(0,d.default)({},"".concat(c,"-disabled"),e3)),"aria-disabled":e3},e9)}var e6=(o=et(eL,"next",eC(el,"next page")),t.default.isValidElement(o)?t.default.cloneElement(o,{disabled:!eN}):o);e6&&(J?(l=!eN,a=eE?0:null):a=(l=!eN||!eA)?null:0,e6=t.default.createElement("li",{title:R?K.next_page:null,onClick:eB,tabIndex:a,onKeyDown:function(e){eO(e,eB)},className:(0,s.default)("".concat(c,"-next"),(0,d.default)({},"".concat(c,"-disabled"),l)),"aria-disabled":l},e6));var e4=(0,s.default)(c,$,(0,d.default)((0,d.default)((0,d.default)((0,d.default)((0,d.default)({},"".concat(c,"-start"),"start"===P),"".concat(c,"-center"),"center"===P),"".concat(c,"-end"),"end"===P),"".concat(c,"-simple"),J),"".concat(c,"-disabled"),F));return t.default.createElement("ul",(0,i.default)({className:e4,style:X,ref:ea},eP),eD,e9,J?eU:e_,e6,t.default.createElement(S,{locale:K,rootPrefixCls:c,disabled:F,selectPrefixCls:void 0===u?"rc-select":u,changeSize:function(e){var t=z(e,eu,B),i=eg>t&&0!==t?t:eg;es(e),ev(i),null==L||L(eg,e),ep(i),null==w||w(i,e)},pageSize:eu,pageSizeOptions:Z,quickGo:ey?ez:null,goButton:eX,showSizeChanger:V,sizeChangerRender:Y}))};var N=e.i(727214),j=e.i(242064),B=e.i(517455),M=e.i(150073),I=e.i(408850),O=e.i(327494),w=e.i(104458);e.i(296059);var T=e.i(915654),P=e.i(349942),D=e.i(517458),H=e.i(889943),A=e.i(183293),_=e.i(246422),R=e.i(838378);let W=e=>Object.assign({itemBg:e.colorBgContainer,itemSize:e.controlHeight,itemSizeSM:e.controlHeightSM,itemActiveBg:e.colorBgContainer,itemActiveColor:e.colorPrimary,itemActiveColorHover:e.colorPrimaryHover,itemLinkBg:e.colorBgContainer,itemActiveColorDisabled:e.colorTextDisabled,itemActiveBgDisabled:e.controlItemBgActiveDisabled,itemInputBg:e.colorBgContainer,miniOptionsSizeChangerTop:0},(0,D.initComponentToken)(e)),L=e=>(0,R.mergeToken)(e,{inputOutlineOffset:0,quickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.25).equal(),paginationMiniOptionsMarginInlineStart:e.calc(e.marginXXS).div(2).equal(),paginationMiniQuickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.1).equal(),paginationItemPaddingInline:e.calc(e.marginXXS).mul(1.5).equal(),paginationEllipsisLetterSpacing:e.calc(e.marginXXS).div(2).equal(),paginationSlashMarginInlineStart:e.marginSM,paginationSlashMarginInlineEnd:e.marginSM,paginationEllipsisTextIndent:"0.13em"},(0,D.initInputToken)(e)),q=(0,_.genStyleHooks)("Pagination",e=>{let t=L(e);return[(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,A.resetComponent)(e)),{display:"flex",flexWrap:"wrap",rowGap:e.paddingXS,"&-start":{justifyContent:"start"},"&-center":{justifyContent:"center"},"&-end":{justifyContent:"end"},"ul, ol":{margin:0,padding:0,listStyle:"none"},"&::after":{display:"block",clear:"both",height:0,overflow:"hidden",visibility:"hidden",content:'""'},[`${t}-total-text`]:{display:"inline-block",height:e.itemSize,marginInlineEnd:e.marginXS,lineHeight:(0,T.unit)(e.calc(e.itemSize).sub(2).equal()),verticalAlign:"middle"}}),(e=>{let{componentCls:t}=e;return{[`${t}-item`]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,marginInlineEnd:e.marginXS,fontFamily:e.fontFamily,lineHeight:(0,T.unit)(e.calc(e.itemSize).sub(2).equal()),textAlign:"center",verticalAlign:"middle",listStyle:"none",backgroundColor:e.itemBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:0,cursor:"pointer",userSelect:"none",a:{display:"block",padding:`0 ${(0,T.unit)(e.paginationItemPaddingInline)}`,color:e.colorText,"&:hover":{textDecoration:"none"}},[`&:not(${t}-item-active)`]:{"&:hover":{transition:`all ${e.motionDurationMid}`,backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive}},"&-active":{fontWeight:e.fontWeightStrong,backgroundColor:e.itemActiveBg,borderColor:e.colorPrimary,a:{color:e.itemActiveColor},"&:hover":{borderColor:e.colorPrimaryHover},"&:hover a":{color:e.itemActiveColorHover}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-jump-prev, ${t}-jump-next`]:{outline:0,[`${t}-item-container`]:{position:"relative",[`${t}-item-link-icon`]:{color:e.colorPrimary,fontSize:e.fontSizeSM,opacity:0,transition:`all ${e.motionDurationMid}`,"&-svg":{top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,margin:"auto"}},[`${t}-item-ellipsis`]:{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,display:"block",margin:"auto",color:e.colorTextDisabled,letterSpacing:e.paginationEllipsisLetterSpacing,textAlign:"center",textIndent:e.paginationEllipsisTextIndent,opacity:1,transition:`all ${e.motionDurationMid}`}},"&:hover":{[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}}},[` + ${t}-prev, + ${t}-jump-prev, + ${t}-jump-next + `]:{marginInlineEnd:e.marginXS},[` + ${t}-prev, + ${t}-next, + ${t}-jump-prev, + ${t}-jump-next + `]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,color:e.colorText,fontFamily:e.fontFamily,lineHeight:(0,T.unit)(e.itemSize),textAlign:"center",verticalAlign:"middle",listStyle:"none",borderRadius:e.borderRadius,cursor:"pointer",transition:`all ${e.motionDurationMid}`},[`${t}-prev, ${t}-next`]:{outline:0,button:{color:e.colorText,cursor:"pointer",userSelect:"none"},[`${t}-item-link`]:{display:"block",width:"100%",height:"100%",padding:0,fontSize:e.fontSizeSM,textAlign:"center",backgroundColor:"transparent",border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:"none",transition:`all ${e.motionDurationMid}`},[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover`]:{[`${t}-item-link`]:{backgroundColor:"transparent"}}},[`${t}-slash`]:{marginInlineEnd:e.paginationSlashMarginInlineEnd,marginInlineStart:e.paginationSlashMarginInlineStart},[`${t}-options`]:{display:"inline-block",marginInlineStart:e.margin,verticalAlign:"middle","&-size-changer":{display:"inline-block",width:"auto"},"&-quick-jumper":{display:"inline-block",height:e.controlHeight,marginInlineStart:e.marginXS,lineHeight:(0,T.unit)(e.controlHeight),verticalAlign:"top",input:Object.assign(Object.assign(Object.assign({},(0,P.genBasicInputStyle)(e)),(0,H.genBaseOutlinedStyle)(e,{borderColor:e.colorBorder,hoverBorderColor:e.colorPrimaryHover,activeBorderColor:e.colorPrimary,activeShadow:e.activeShadow})),{"&[disabled]":Object.assign({},(0,H.genDisabledStyle)(e)),width:e.quickJumperInputWidth,height:e.controlHeight,boxSizing:"border-box",margin:0,marginInlineStart:e.marginXS,marginInlineEnd:e.marginXS})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-simple`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSize,lineHeight:(0,T.unit)(e.itemSize),verticalAlign:"top",[`${t}-item-link`]:{height:e.itemSize,backgroundColor:"transparent",border:0,"&:hover":{backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive},"&::after":{height:e.itemSize,lineHeight:(0,T.unit)(e.itemSize)}}},[`${t}-simple-pager`]:{display:"inline-flex",alignItems:"center",height:e.itemSize,marginInlineEnd:e.marginXS,input:{boxSizing:"border-box",height:"100%",width:e.quickJumperInputWidth,padding:`0 ${(0,T.unit)(e.paginationItemPaddingInline)}`,textAlign:"center",backgroundColor:e.itemInputBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadius,outline:"none",transition:`border-color ${e.motionDurationMid}`,color:"inherit","&:hover":{borderColor:e.colorPrimary},"&:focus":{borderColor:e.colorPrimaryHover,boxShadow:`${(0,T.unit)(e.inputOutlineOffset)} 0 ${(0,T.unit)(e.controlOutlineWidth)} ${e.controlOutline}`},"&[disabled]":{color:e.colorTextDisabled,backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,cursor:"not-allowed"}}},[`&${t}-disabled`]:{[`${t}-prev, ${t}-next`]:{[`${t}-item-link`]:{"&:hover, &:active":{backgroundColor:"transparent"}}}},[`&${t}-mini`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM),[`${t}-item-link`]:{height:e.itemSizeSM,"&::after":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)}}},[`${t}-simple-pager`]:{height:e.itemSizeSM,input:{width:e.paginationMiniQuickJumperInputWidth}}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-mini ${t}-total-text, &${t}-mini ${t}-simple-pager`]:{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-item`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,T.unit)(e.calc(e.itemSizeSM).sub(2).equal())},[`&${t}-mini ${t}-prev, &${t}-mini ${t}-next`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini:not(${t}-disabled)`]:{[`${t}-prev, ${t}-next`]:{[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover ${t}-item-link`]:{backgroundColor:"transparent"}}},[` + &${t}-mini ${t}-prev ${t}-item-link, + &${t}-mini ${t}-next ${t}-item-link + `]:{backgroundColor:"transparent",borderColor:"transparent","&::after":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)}},[`&${t}-mini ${t}-jump-prev, &${t}-mini ${t}-jump-next`]:{height:e.itemSizeSM,marginInlineEnd:0,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-options`]:{marginInlineStart:e.paginationMiniOptionsMarginInlineStart,"&-size-changer":{top:e.miniOptionsSizeChangerTop},"&-quick-jumper":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM),input:Object.assign(Object.assign({},(0,P.genInputSmallStyle)(e)),{width:e.paginationMiniQuickJumperInputWidth,height:e.controlHeightSM})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-disabled`]:{"&, &:hover":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}},"&:focus-visible":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}}},[`&${t}-disabled`]:{cursor:"not-allowed",[`${t}-item`]:{cursor:"not-allowed",backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"},a:{color:e.colorTextDisabled,backgroundColor:"transparent",border:"none",cursor:"not-allowed"},"&-active":{borderColor:e.colorBorder,backgroundColor:e.itemActiveBgDisabled,"&:hover, &:active":{backgroundColor:e.itemActiveBgDisabled},a:{color:e.itemActiveColorDisabled}}},[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:hover, &:active":{backgroundColor:"transparent"},[`${t}-simple&`]:{backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"}}},[`${t}-simple-pager`]:{color:e.colorTextDisabled},[`${t}-jump-prev, ${t}-jump-next`]:{[`${t}-item-link-icon`]:{opacity:0},[`${t}-item-ellipsis`]:{opacity:1}}}}})(e)),{[`@media only screen and (max-width: ${e.screenLG}px)`]:{[`${t}-item`]:{"&-after-jump-prev, &-before-jump-next":{display:"none"}}},[`@media only screen and (max-width: ${e.screenSM}px)`]:{[`${t}-options`]:{display:"none"}}}),[`&${e.componentCls}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t}=e;return{[`${t}:not(${t}-disabled)`]:{[`${t}-item`]:Object.assign({},(0,A.genFocusStyle)(e)),[`${t}-jump-prev, ${t}-jump-next`]:{"&:focus-visible":Object.assign({[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}},(0,A.genFocusOutline)(e))},[`${t}-prev, ${t}-next`]:{[`&:focus-visible ${t}-item-link`]:(0,A.genFocusOutline)(e)}}}})(t)]},W),K=(0,_.genSubStyleComponent)(["Pagination","bordered"],e=>(e=>{let{componentCls:t}=e;return{[`${t}${t}-bordered${t}-disabled:not(${t}-mini)`]:{"&, &:hover":{[`${t}-item-link`]:{borderColor:e.colorBorder}},"&:focus-visible":{[`${t}-item-link`]:{borderColor:e.colorBorder}},[`${t}-item, ${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,[`&:hover:not(${t}-item-active)`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,a:{color:e.colorTextDisabled}},[`&${t}-item-active`]:{backgroundColor:e.itemActiveBgDisabled}},[`${t}-prev, ${t}-next`]:{"&:hover button":{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,color:e.colorTextDisabled},[`${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder}}},[`${t}${t}-bordered:not(${t}-mini)`]:{[`${t}-prev, ${t}-next`]:{"&:hover button":{borderColor:e.colorPrimaryHover,backgroundColor:e.itemBg},[`${t}-item-link`]:{backgroundColor:e.itemLinkBg,borderColor:e.colorBorder},[`&:hover ${t}-item-link`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,color:e.colorPrimary},[`&${t}-disabled`]:{[`${t}-item-link`]:{borderColor:e.colorBorder,color:e.colorTextDisabled}}},[`${t}-item`]:{backgroundColor:e.itemBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,[`&:hover:not(${t}-item-active)`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,a:{color:e.colorPrimary}},"&-active":{borderColor:e.colorPrimary}}}}})(L(e)),W);function X(e){return(0,t.useMemo)(()=>"boolean"==typeof e?[e,{}]:e&&"object"==typeof e?[!0,e]:[void 0,void 0],[e])}var U=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};e.s(["default",0,e=>{let{align:i,prefixCls:n,selectPrefixCls:o,className:a,rootClassName:d,style:m,size:g,locale:p,responsive:b,showSizeChanger:f,selectComponentClass:h,pageSizeOptions:v}=e,$=U(e,["align","prefixCls","selectPrefixCls","className","rootClassName","style","size","locale","responsive","showSizeChanger","selectComponentClass","pageSizeOptions"]),{xs:S}=(0,M.default)(b),[,C]=(0,w.useToken)(),{getPrefixCls:k,direction:y,showSizeChanger:x,className:z,style:T}=(0,j.useComponentConfig)("pagination"),P=k("pagination",n),[D,H,A]=q(P),_=(0,B.default)(g),R="small"===_||!!(S&&!_&&b),[W]=(0,I.useLocale)("Pagination",N.default),L=Object.assign(Object.assign({},W),p),[F,J]=X(f),[G,Q]=X(x),V=null!=J?J:Q,Y=h||O.default,Z=t.useMemo(()=>v?v.map(e=>Number(e)):void 0,[v]),ee=t.useMemo(()=>{let e=t.createElement("span",{className:`${P}-item-ellipsis`},"•••"),i=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===y?t.createElement(u.default,null):t.createElement(c.default,null)),n=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===y?t.createElement(c.default,null):t.createElement(u.default,null));return{prevIcon:i,nextIcon:n,jumpPrevIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===y?t.createElement(r,{className:`${P}-item-link-icon`}):t.createElement(l,{className:`${P}-item-link-icon`}),e)),jumpNextIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===y?t.createElement(l,{className:`${P}-item-link-icon`}):t.createElement(r,{className:`${P}-item-link-icon`}),e))}},[y,P]),et=k("select",o),ei=(0,s.default)({[`${P}-${i}`]:!!i,[`${P}-mini`]:R,[`${P}-rtl`]:"rtl"===y,[`${P}-bordered`]:C.wireframe},z,a,d,H,A),en=Object.assign(Object.assign({},T),m);return D(t.createElement(t.Fragment,null,C.wireframe&&t.createElement(K,{prefixCls:P}),t.createElement(E,Object.assign({},ee,$,{style:en,prefixCls:P,selectPrefixCls:et,className:ei,locale:L,pageSizeOptions:Z,showSizeChanger:null!=F?F:G,sizeChangerRender:e=>{var i;let{disabled:n,size:o,onSizeChange:l,"aria-label":a,className:r,options:c}=e,{className:u,onChange:d}=V||{},m=null==(i=c.find(e=>String(e.value)===String(o)))?void 0:i.value;return t.createElement(Y,Object.assign({disabled:n,showSearch:!0,popupMatchSelectWidth:!1,getPopupContainer:e=>e.parentNode,"aria-label":a,options:c},V,{value:m,onChange:(e,t)=>{null==l||l(e),null==d||d(e,t)},size:R?"small":"middle",className:(0,s.default)(r,u)}))}}))))}],165370)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0vr7vyqn3e7s0.js b/litellm/proxy/_experimental/out/_next/static/chunks/0vr7vyqn3e7s0.js new file mode 100644 index 00000000000..3010732d000 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0vr7vyqn3e7s0.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,195116,e=>{"use strict";let t=(0,e.i(475254).default)("wrench",[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z",key:"cbrjhi"}]]);e.s(["Wrench",0,t],195116)},531278,e=>{"use strict";let t=(0,e.i(475254).default)("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);e.s(["Loader2",0,t],531278)},180127,e=>{"use strict";let t=(0,e.i(475254).default)("arrow-left",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);e.s(["default",0,t])},434166,e=>{"use strict";e.s(["getSecureItem",0,function(e){try{let t=window.sessionStorage.getItem(e);if(null===t)return null;return decodeURIComponent(atob(t).split("").map(e=>"%"+e.charCodeAt(0).toString(16).padStart(2,"0")).join(""))}catch{return null}},"setSecureItem",0,function(e,t){window.sessionStorage.setItem(e,btoa(encodeURIComponent(t).replace(/%([0-9A-F]{2})/g,(e,t)=>String.fromCharCode(parseInt(t,16)))))}])},302747,e=>{"use strict";var t=e.i(843476),r=e.i(271645),n=e.i(115504);let s=r.forwardRef(({className:e,...r},s)=>(0,t.jsx)("div",{ref:s,"data-slot":"skeleton",className:(0,n.cn)("animate-pulse rounded-md bg-accent",e),...r}));s.displayName="Skeleton",e.s(["Skeleton",0,s])},246349,e=>{"use strict";let t=(0,e.i(475254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["default",0,t])},463059,e=>{"use strict";var t=e.i(246349);e.s(["ChevronRight",()=>t.default])},951437,e=>{"use strict";var t=e.i(271645);e.s(["useControlled",0,function({controlled:e,default:r,name:n,state:s="value"}){let{current:o}=t.useRef(void 0!==e),[l,i]=t.useState(r),u=t.useCallback(e=>{o||i(e)},[]);return[o?e:l,u]}])},53687,673553,395530,e=>{"use strict";var t,r=e.i(271645),n=e.i(921374),s=e.i(667865),o=e.i(146376);e.i(247167);let l=r.createContext({register:()=>{},unregister:()=>{},subscribeMapChange:()=>()=>{},elementsRef:{current:[]},nextIndexRef:{current:0}});var i=e.i(843476);function u(){return new Map}function a(){return new Set}function c(e,t){let r=e.compareDocumentPosition(t);return r&Node.DOCUMENT_POSITION_FOLLOWING||r&Node.DOCUMENT_POSITION_CONTAINED_BY?-1:r&Node.DOCUMENT_POSITION_PRECEDING||r&Node.DOCUMENT_POSITION_CONTAINS?1:0}e.s(["CompositeList",0,function(e){let{children:t,elementsRef:d,labelsRef:f,onMapChange:h}=e,p=(0,s.useStableCallback)(h),_=r.useRef(0),m=(0,n.useRefWithInit)(a).current,O=(0,n.useRefWithInit)(u).current,[g,E]=r.useState(0),y=r.useRef(g),A=(0,s.useStableCallback)((e,t)=>{O.set(e,t??null),y.current+=1,E(y.current)}),I=(0,s.useStableCallback)(e=>{O.delete(e),y.current+=1,E(y.current)}),T=r.useMemo(()=>{let e=new Map;return Array.from(O.keys()).filter(e=>e.isConnected).sort(c).forEach((t,r)=>{let n=O.get(t)??{};e.set(t,{...n,index:r})}),e},[O,g]);(0,o.useIsoLayoutEffect)(()=>{if("function"!=typeof MutationObserver||0===T.size)return;let e=new MutationObserver(e=>{let t=new Set,r=e=>t.has(e)?t.delete(e):t.add(e);e.forEach(e=>{e.removedNodes.forEach(r),e.addedNodes.forEach(r)}),0===t.size&&(y.current+=1,E(y.current))});return T.forEach((t,r)=>{r.parentElement&&e.observe(r.parentElement,{childList:!0})}),()=>{e.disconnect()}},[T]),(0,o.useIsoLayoutEffect)(()=>{y.current===g&&(d.current.length!==T.size&&(d.current.length=T.size),f&&f.current.length!==T.size&&(f.current.length=T.size),_.current=T.size),p(T)},[p,T,d,f,g]),(0,o.useIsoLayoutEffect)(()=>()=>{d.current=[]},[d]),(0,o.useIsoLayoutEffect)(()=>()=>{f&&(f.current=[])},[f]);let S=(0,s.useStableCallback)(e=>(m.add(e),()=>{m.delete(e)}));(0,o.useIsoLayoutEffect)(()=>{m.forEach(e=>e(T))},[m,T]);let C=r.useMemo(()=>({register:A,unregister:I,subscribeMapChange:S,elementsRef:d,labelsRef:f,nextIndexRef:_}),[A,I,S,d,f,_]);return(0,i.jsx)(l.Provider,{value:C,children:t})}],53687);var d=e.i(828918),f=e.i(838452);let h=((t={})[t.None=0]="None",t[t.GuessFromOrder=1]="GuessFromOrder",t);function p(e={}){let{label:t,metadata:n,textRef:s,indexGuessBehavior:i,index:u}=e,{register:a,unregister:c,subscribeMapChange:d,elementsRef:f,labelsRef:_,nextIndexRef:m}=r.useContext(l),O=r.useRef(-1),[g,E]=r.useState(u??(i===h.GuessFromOrder?()=>{if(-1===O.current){let e=m.current;m.current+=1,O.current=e}return O.current}:-1)),y=r.useRef(null),A=r.useCallback(e=>{if(y.current=e,-1!==g&&null!==e&&(f.current[g]=e,_)){let r=void 0!==t;_.current[g]=r?t:s?.current?.textContent??e.textContent}},[g,f,_,t,s]);return(0,o.useIsoLayoutEffect)(()=>{if(null!=u)return;let e=y.current;if(e)return a(e,n),()=>{c(e)}},[u,a,c,n]),(0,o.useIsoLayoutEffect)(()=>{if(null==u)return d(e=>{let t=y.current?e.get(y.current)?.index:null;null!=t&&E(t)})},[u,d,E]),{ref:A,index:g}}e.s(["IndexGuessBehavior",0,h,"useCompositeListItem",0,p],673553),e.s(["useCompositeItem",0,function(e={}){let{highlightItemOnHover:t,highlightedIndex:n,onHighlightedIndexChange:s}=(0,f.useCompositeRootContext)(),{ref:o,index:l}=p(e),i=n===l,u=r.useRef(null),a=(0,d.useMergedRefs)(o,u);return{compositeProps:{tabIndex:i?0:-1,onFocus(){s(l)},onMouseMove(){let e=u.current;if(!t||!e)return;let r=e.hasAttribute("disabled")||"true"===e.ariaDisabled;i||r||e.focus()}},compositeRef:a,index:l}}],395530)},590803,e=>{"use strict";e.s(["isElementDisabled",0,function(e){return null==e||e.hasAttribute("disabled")||"true"===e.getAttribute("aria-disabled")}])},269638,e=>{"use strict";let t=(0,e.i(475254).default)("circle-check-big",[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]]);e.s(["CheckCircle",0,t],269638)},292335,122520,165615,779129,280024,e=>{"use strict";let t={NONE:"none",API_KEY:"api_key",BEARER_TOKEN:"bearer_token",TOKEN:"token",BASIC:"basic",OAUTH2:"oauth2",OAUTH2_TOKEN_EXCHANGE:"oauth2_token_exchange",AWS_SIGV4:"aws_sigv4",TRUE_PASSTHROUGH:"true_passthrough",OAUTH_DELEGATE:"oauth_delegate"},r={INTERACTIVE:"interactive",M2M:"m2m"},n=e=>{let t=e.credentials??{};return JSON.stringify({url:"string"==typeof e.url?e.url:null,spec_path:"string"==typeof e.spec_path?e.spec_path:null,auth_type:e.auth_type??null,oauth_flow_type:e.oauth_flow_type??null,client_id:t.client_id??null,client_secret:t.client_secret??null,scopes:t.scopes??null,authorization_url:e.authorization_url??null,token_url:e.token_url??null,registration_url:e.registration_url??null})},s=["client_id","client_secret"],o=["access_token","refresh_token","expires_in","scope"],l="client_credentials",i={SSE:"sse",HTTP:"http",STDIO:"stdio",OPENAPI:"openapi"};e.s(["AUTH_TYPE",0,t,"CLEARED_ON_INVALIDATION",0,["credentials"],"MCP_OAUTH2_FLOW_INTERACTIVE",0,"authorization_code","MCP_OAUTH2_FLOW_M2M",0,l,"OAUTH_FLOW",0,r,"TRANSPORT",0,i,"credentialAuthClass",0,e=>e===t.TRUE_PASSTHROUGH||e===t.OAUTH_DELEGATE?"client_forwarded":e??null,"getMcpOAuthMode",0,function(e){return e.auth_type===t.OAUTH2_TOKEN_EXCHANGE?"token_exchange":e.auth_type!==t.OAUTH2?null:e.oauth2_flow===l?"m2m":e.delegate_auth_to_upstream?"passthrough":"authorization_code"},"getOAuthAuthorizationIdentity",0,n,"handleAuth",0,e=>null==e?t.NONE:e,"handleTransport",0,(e,t)=>null==e?i.SSE:t&&e!==i.STDIO?i.OPENAPI:e,"isClientForwardedTokenMode",0,e=>e===t.TRUE_PASSTHROUGH||e===t.OAUTH_DELEGATE,"isHeldOAuthTokenStale",0,(e,t)=>void 0!==t&&n(e)!==t,"oauth2FlowToFormValue",0,function(e){return e===l?r.M2M:e?r.INTERACTIVE:void 0},"preservedDeclaredAppCredentials",0,e=>{if(!e)return;let t=Object.fromEntries(s.filter(t=>"string"==typeof e[t]&&""!==e[t]).map(t=>[t,e[t]]));return Object.keys(t).length>0?t:void 0},"withoutMintedTokenCredentials",0,e=>{if(!e)return;let t=Object.fromEntries(Object.entries(e).filter(([e])=>!o.includes(e)));return Object.keys(t).length>0?t:void 0}],292335);var u=e.i(271645),a=e.i(602869),c=e.i(727749);function d(e){if(e instanceof Error)return e.message;if(e&&"object"==typeof e){let t=e.detail;return"string"==typeof t?t:Array.isArray(t)?t.map(e=>e&&"object"==typeof e?"string"==typeof e.msg?e.msg:JSON.stringify(e):String(e)).join("; "):t&&"object"==typeof t&&"string"==typeof t.error?t.error:"string"==typeof e.message?e.message:JSON.stringify(e)}return String(e)}e.s(["extractErrorMessage",0,d],122520);let f=e=>{let t=new Uint8Array(e),r="";return t.forEach(e=>r+=String.fromCharCode(e)),btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")},h=()=>{let e=new Uint8Array(32);return window.crypto.getRandomValues(e),f(e.buffer)},p=async e=>{let t=new TextEncoder().encode(e);return f(await window.crypto.subtle.digest("SHA-256",t))};e.s(["generateCodeChallenge",0,p,"generateCodeVerifier",0,h],165615);var _=e.i(434166);let m=()=>{{let e=window.location.pathname||"",t=e.indexOf("/ui"),r=t>=0?e.slice(0,t+3).replace(/\/+$/,""):"";return`${window.location.origin}${r}/mcp/oauth/callback`}},O=(...e)=>{e.forEach(e=>{try{window.sessionStorage.removeItem(e)}catch(e){}})};e.s(["TOOLS_OAUTH_UI_STATE_KEY",0,"litellm-mcp-oauth-tools-state","buildCallbackUrl",0,m,"clearStorage",0,O],779129);let g="litellm-user-mcp-oauth-flow-state",E="litellm-user-mcp-oauth-result",y=(e,t)=>{(0,_.setSecureItem)(e,t)},A=e=>(0,_.getSecureItem)(e);e.s(["useUserMcpOAuthFlow",0,({accessToken:e,serverId:t,serverAlias:r,scopes:n,clientId:s,onSuccess:o})=>{let[l,i]=(0,u.useState)("idle"),[f,_]=(0,u.useState)(null),I=(0,u.useRef)(!1),T=(0,u.useCallback)(async()=>{try{let o;i("authorizing"),_(null);let l=s??void 0;if(!l)try{let n=await (0,a.registerMcpOAuthClient)(e,t,{client_name:r||t,grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"});l=n?.client_id,o=n?.client_secret}catch(e){}let u=h(),c=await p(u),d=crypto.randomUUID(),f=m(),O=n?.filter(e=>e.trim()).join(" "),E=(0,a.buildMcpOAuthAuthorizeUrl)({serverId:t,clientId:l,redirectUri:f,state:d,codeChallenge:c,scope:O}),A={state:d,codeVerifier:u,serverId:t,redirectUri:f,clientId:l,clientSecret:o,scopes:n};y(g,JSON.stringify(A));let I=new URL(window.location.href);I.searchParams.set("mcpOauthReturn","apps"),y("litellm-mcp-oauth-return-url",I.toString()),window.location.href=E}catch(t){let e=d(t);_(e),i("error"),c.default.error(e)}},[e,t,r,n,s]),S=(0,u.useCallback)(async()=>{if(I.current)return;let r=A(E);if(!r)return;let n=A(g);if(!n)return;try{let e=JSON.parse(n);if(e.serverId&&e.serverId!==t)return}catch(e){}I.current=!0,O(E);let s=null,l=null;try{s=JSON.parse(r);let e=A(g);l=e?JSON.parse(e):null}catch(e){_("Failed to resume OAuth flow. Please retry."),i("error"),I.current=!1,O(g);return}try{if(!l?.state||!l.codeVerifier||!l.serverId)throw Error("OAuth session state was lost. Please retry.");if(!s?.state||s.state!==l.state)throw Error("OAuth state mismatch. Please retry.");if(s.error)throw Error(s.error_description||s.error);if(!s.code)throw Error("Authorization code missing in callback.");i("exchanging");let t=await (0,a.exchangeMcpOAuthToken)({serverId:l.serverId,code:s.code,clientId:l.clientId,clientSecret:l.clientSecret,codeVerifier:l.codeVerifier,redirectUri:l.redirectUri,accessToken:e});await (0,a.storeMCPOAuthUserCredential)(e,l.serverId,{access_token:t.access_token,refresh_token:t.refresh_token,expires_in:t.expires_in,scopes:l.scopes}),i("success"),_(null),c.default.success("Connected successfully"),o()}catch(t){let e=d(t);_(e),i("error"),c.default.error(e)}finally{O(g),setTimeout(()=>{I.current=!1},1e3)}},[e,t,o]);return(0,u.useEffect)(()=>{S()},[S]),{startOAuthFlow:T,status:l,error:f}}],280024)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/0w98a8ubxago4.js b/litellm/proxy/_experimental/out/_next/static/chunks/0w98a8ubxago4.js new file mode 100644 index 00000000000..d9d9c02f39a --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/0w98a8ubxago4.js @@ -0,0 +1,2 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,285027,e=>{"use strict";e.i(247167);var t=e.i(931067),r=e.i(271645);let s={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 720a48 48 0 1096 0 48 48 0 10-96 0zm16-304v184c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V416c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8zm475.7 440l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zm-783.5-27.9L512 239.9l339.8 588.2H172.2z"}}]},name:"warning",theme:"outlined"};var n=e.i(9583),i=r.forwardRef(function(e,i){return r.createElement(n.default,(0,t.default)({},e,{ref:i,icon:s}))});e.s(["WarningOutlined",0,i],285027)},743151,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.CopyToClipboard=void 0;var s=a(e.r(844343)),n=a(e.r(271645)),i=["text","onCopy","options","children"];function a(e){return e&&e.__esModule?e:{default:e}}function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);t&&(s=s.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,s)}return r}function d(e){for(var t=1;t{"use strict";var s=e.r(743151).CopyToClipboard;s.CopyToClipboard=s,t.exports=s},233538,e=>{"use strict";e.s(["isDisabledReactIssue7711",0,function(e){let t=e.parentElement,r=null;for(;t&&!(t instanceof HTMLFieldSetElement);)t instanceof HTMLLegendElement&&(r=t),t=t.parentElement;let s=(null==t?void 0:t.getAttribute("disabled"))==="";return!(s&&function(e){if(!e)return!1;let t=e.previousElementSibling;for(;null!==t;){if(t instanceof HTMLLegendElement)return!1;t=t.previousElementSibling}return!0}(r))&&s}])},83733,233137,e=>{"use strict";let t,r;var s,n,i=e.i(247167),a=e.i(271645),l=e.i(544508),o=e.i(746725),d=e.i(835696);void 0!==i.default&&"u">typeof globalThis&&"u">typeof Element&&(null==(s=null==i.default?void 0:i.default.env)?void 0:s.NODE_ENV)==="test"&&void 0===(null==(n=null==Element?void 0:Element.prototype)?void 0:n.getAnimations)&&(Element.prototype.getAnimations=function(){return console.warn(["Headless UI has polyfilled `Element.prototype.getAnimations` for your tests.","Please install a proper polyfill e.g. `jsdom-testing-mocks`, to silence these warnings.","","Example usage:","```js","import { mockAnimationsApi } from 'jsdom-testing-mocks'","mockAnimationsApi()","```"].join(` +`)),[]});var u=((t=u||{})[t.None=0]="None",t[t.Closed=1]="Closed",t[t.Enter=2]="Enter",t[t.Leave=4]="Leave",t);e.s(["transitionDataAttributes",0,function(e){let t={};for(let r in e)!0===e[r]&&(t[`data-${r}`]="");return t},"useTransition",0,function(e,t,r,s){let[n,i]=(0,a.useState)(r),{hasFlag:u,addFlag:c,removeFlag:m}=function(e=0){let[t,r]=(0,a.useState)(e),s=(0,a.useCallback)(e=>r(e),[t]),n=(0,a.useCallback)(e=>r(t=>t|e),[t]),i=(0,a.useCallback)(e=>(t&e)===e,[t]);return{flags:t,setFlag:s,addFlag:n,hasFlag:i,removeFlag:(0,a.useCallback)(e=>r(t=>t&~e),[r]),toggleFlag:(0,a.useCallback)(e=>r(t=>t^e),[r])}}(e&&n?3:0),h=(0,a.useRef)(!1),f=(0,a.useRef)(!1),p=(0,o.useDisposables)();return(0,d.useIsoMorphicEffect)(()=>{var n;if(e){if(r&&i(!0),!t){r&&c(3);return}return null==(n=null==s?void 0:s.start)||n.call(s,r),function(e,{prepare:t,run:r,done:s,inFlight:n}){let i=(0,l.disposables)();return function(e,{inFlight:t,prepare:r}){if(null!=t&&t.current)return r();let s=e.style.transition;e.style.transition="none",r(),e.offsetHeight,e.style.transition=s}(e,{prepare:t,inFlight:n}),i.nextFrame(()=>{r(),i.requestAnimationFrame(()=>{i.add(function(e,t){var r,s;let n=(0,l.disposables)();if(!e)return n.dispose;let i=!1;n.add(()=>{i=!0});let a=null!=(s=null==(r=e.getAnimations)?void 0:r.call(e).filter(e=>e instanceof CSSTransition))?s:[];return 0===a.length?t():Promise.allSettled(a.map(e=>e.finished)).then(()=>{i||t()}),n.dispose}(e,s))})}),i.dispose}(t,{inFlight:h,prepare(){f.current?f.current=!1:f.current=h.current,h.current=!0,f.current||(r?(c(3),m(4)):(c(4),m(2)))},run(){f.current?r?(m(3),c(4)):(m(4),c(3)):r?m(1):c(1)},done(){var e;f.current&&"function"==typeof t.getAnimations&&t.getAnimations().length>0||(h.current=!1,m(7),r||i(!1),null==(e=null==s?void 0:s.end)||e.call(s,r))}})}},[e,r,t,p]),e?[n,{closed:u(1),enter:u(2),leave:u(4),transition:u(2)||u(4)}]:[r,{closed:void 0,enter:void 0,leave:void 0,transition:void 0}]}],83733);let c=(0,a.createContext)(null);c.displayName="OpenClosedContext";var m=((r=m||{})[r.Open=1]="Open",r[r.Closed=2]="Closed",r[r.Closing=4]="Closing",r[r.Opening=8]="Opening",r);e.s(["OpenClosedProvider",0,function({value:e,children:t}){return a.default.createElement(c.Provider,{value:e},t)},"ResetOpenClosedProvider",0,function({children:e}){return a.default.createElement(c.Provider,{value:null},e)},"State",0,m,"useOpenClosed",0,function(){return(0,a.useContext)(c)}],233137)},677667,674175,886148,543086,e=>{"use strict";let t,r;var s,n=e.i(290571),i=e.i(783222),a=e.i(433336),l=e.i(271645),o=e.i(394487),d=e.i(914189),u=e.i(144279),c=e.i(294316),m=e.i(83733);let h=(0,l.createContext)(()=>{});function f({value:e,children:t}){return l.default.createElement(h.Provider,{value:e},t)}e.s(["CloseProvider",0,f],674175);var p=e.i(233137),g=e.i(233538),x=e.i(397701),y=e.i(402155),v=e.i(700020);let b=null!=(s=l.default.startTransition)?s:function(e){e()};var _=e.i(998348),j=((t=j||{})[t.Open=0]="Open",t[t.Closed=1]="Closed",t),w=((r=w||{})[r.ToggleDisclosure=0]="ToggleDisclosure",r[r.CloseDisclosure=1]="CloseDisclosure",r[r.SetButtonId=2]="SetButtonId",r[r.SetPanelId=3]="SetPanelId",r[r.SetButtonElement=4]="SetButtonElement",r[r.SetPanelElement=5]="SetPanelElement",r);let C={0:e=>({...e,disclosureState:(0,x.match)(e.disclosureState,{0:1,1:0})}),1:e=>1===e.disclosureState?e:{...e,disclosureState:1},2:(e,t)=>e.buttonId===t.buttonId?e:{...e,buttonId:t.buttonId},3:(e,t)=>e.panelId===t.panelId?e:{...e,panelId:t.panelId},4:(e,t)=>e.buttonElement===t.element?e:{...e,buttonElement:t.element},5:(e,t)=>e.panelElement===t.element?e:{...e,panelElement:t.element}},k=(0,l.createContext)(null);function S(e){let t=(0,l.useContext)(k);if(null===t){let t=Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,S),t}return t}k.displayName="DisclosureContext";let E=(0,l.createContext)(null);E.displayName="DisclosureAPIContext";let N=(0,l.createContext)(null);function O(e,t){return(0,x.match)(t.type,C,e,t)}N.displayName="DisclosurePanelContext";let I=l.Fragment,T=v.RenderFeatures.RenderStrategy|v.RenderFeatures.Static,R=Object.assign((0,v.forwardRefWithAs)(function(e,t){let{defaultOpen:r=!1,...s}=e,n=(0,l.useRef)(null),i=(0,c.useSyncRefs)(t,(0,c.optionalRef)(e=>{n.current=e},void 0===e.as||e.as===l.Fragment)),a=(0,l.useReducer)(O,{disclosureState:+!r,buttonElement:null,panelElement:null,buttonId:null,panelId:null}),[{disclosureState:o,buttonId:u},m]=a,h=(0,d.useEvent)(e=>{m({type:1});let t=(0,y.getOwnerDocument)(n);if(!t||!u)return;let r=e?e instanceof HTMLElement?e:e.current instanceof HTMLElement?e.current:t.getElementById(u):t.getElementById(u);null==r||r.focus()}),g=(0,l.useMemo)(()=>({close:h}),[h]),b=(0,l.useMemo)(()=>({open:0===o,close:h}),[o,h]),_=(0,v.useRender)();return l.default.createElement(k.Provider,{value:a},l.default.createElement(E.Provider,{value:g},l.default.createElement(f,{value:h},l.default.createElement(p.OpenClosedProvider,{value:(0,x.match)(o,{0:p.State.Open,1:p.State.Closed})},_({ourProps:{ref:i},theirProps:s,slot:b,defaultTag:I,name:"Disclosure"})))))}),{Button:(0,v.forwardRefWithAs)(function(e,t){let r=(0,l.useId)(),{id:s=`headlessui-disclosure-button-${r}`,disabled:n=!1,autoFocus:m=!1,...h}=e,[f,p]=S("Disclosure.Button"),x=(0,l.useContext)(N),y=null!==x&&x===f.panelId,b=(0,l.useRef)(null),j=(0,c.useSyncRefs)(b,t,(0,d.useEvent)(e=>{if(!y)return p({type:4,element:e})}));(0,l.useEffect)(()=>{if(!y)return p({type:2,buttonId:s}),()=>{p({type:2,buttonId:null})}},[s,p,y]);let w=(0,d.useEvent)(e=>{var t;if(y){if(1===f.disclosureState)return;switch(e.key){case _.Keys.Space:case _.Keys.Enter:e.preventDefault(),e.stopPropagation(),p({type:0}),null==(t=f.buttonElement)||t.focus()}}else switch(e.key){case _.Keys.Space:case _.Keys.Enter:e.preventDefault(),e.stopPropagation(),p({type:0})}}),C=(0,d.useEvent)(e=>{e.key===_.Keys.Space&&e.preventDefault()}),k=(0,d.useEvent)(e=>{var t;(0,g.isDisabledReactIssue7711)(e.currentTarget)||n||(y?(p({type:0}),null==(t=f.buttonElement)||t.focus()):p({type:0}))}),{isFocusVisible:E,focusProps:O}=(0,i.useFocusRing)({autoFocus:m}),{isHovered:I,hoverProps:T}=(0,a.useHover)({isDisabled:n}),{pressed:R,pressProps:P}=(0,o.useActivePress)({disabled:n}),D=(0,l.useMemo)(()=>({open:0===f.disclosureState,hover:I,active:R,disabled:n,focus:E,autofocus:m}),[f,I,R,E,n,m]),F=(0,u.useResolveButtonType)(e,f.buttonElement),L=y?(0,v.mergeProps)({ref:j,type:F,disabled:n||void 0,autoFocus:m,onKeyDown:w,onClick:k},O,T,P):(0,v.mergeProps)({ref:j,id:s,type:F,"aria-expanded":0===f.disclosureState,"aria-controls":f.panelElement?f.panelId:void 0,disabled:n||void 0,autoFocus:m,onKeyDown:w,onKeyUp:C,onClick:k},O,T,P);return(0,v.useRender)()({ourProps:L,theirProps:h,slot:D,defaultTag:"button",name:"Disclosure.Button"})}),Panel:(0,v.forwardRefWithAs)(function(e,t){let r=(0,l.useId)(),{id:s=`headlessui-disclosure-panel-${r}`,transition:n=!1,...i}=e,[a,o]=S("Disclosure.Panel"),{close:u}=function e(t){let r=(0,l.useContext)(E);if(null===r){let r=Error(`<${t} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(r,e),r}return r}("Disclosure.Panel"),[h,f]=(0,l.useState)(null),g=(0,c.useSyncRefs)(t,(0,d.useEvent)(e=>{b(()=>o({type:5,element:e}))}),f);(0,l.useEffect)(()=>(o({type:3,panelId:s}),()=>{o({type:3,panelId:null})}),[s,o]);let x=(0,p.useOpenClosed)(),[y,_]=(0,m.useTransition)(n,h,null!==x?(x&p.State.Open)===p.State.Open:0===a.disclosureState),j=(0,l.useMemo)(()=>({open:0===a.disclosureState,close:u}),[a.disclosureState,u]),w={ref:g,id:s,...(0,m.transitionDataAttributes)(_)},C=(0,v.useRender)();return l.default.createElement(p.ResetOpenClosedProvider,null,l.default.createElement(N.Provider,{value:a.panelId},C({ourProps:w,theirProps:i,slot:j,defaultTag:"div",features:T,visible:y,name:"Disclosure.Panel"})))})});e.s(["Disclosure",0,R],886148);let P=(0,l.createContext)(void 0);var D=e.i(444755);let F=(0,e.i(673706).makeClassName)("Accordion"),L=(0,l.createContext)({isOpen:!1}),A=l.default.forwardRef((e,t)=>{var r;let{defaultOpen:s=!1,children:i,className:a}=e,o=(0,n.__rest)(e,["defaultOpen","children","className"]),d=null!=(r=(0,l.useContext)(P))?r:(0,D.tremorTwMerge)("rounded-tremor-default border");return l.default.createElement(R,Object.assign({as:"div",ref:t,className:(0,D.tremorTwMerge)(F("root"),"overflow-hidden","bg-tremor-background border-tremor-border","dark:bg-dark-tremor-background dark:border-dark-tremor-border",d,a),defaultOpen:s},o),({open:e})=>l.default.createElement(L.Provider,{value:{isOpen:e}},i))});A.displayName="Accordion",e.s(["OpenContext",0,L,"default",0,A],543086),e.s(["Accordion",0,A],677667)},898667,e=>{"use strict";var t=e.i(290571),r=e.i(271645),s=e.i(886148);let n=e=>{var s=(0,t.__rest)(e,[]);return r.default.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},s),r.default.createElement("path",{d:"M11.9999 10.8284L7.0502 15.7782L5.63599 14.364L11.9999 8L18.3639 14.364L16.9497 15.7782L11.9999 10.8284Z"}))};var i=e.i(543086),a=e.i(444755);let l=(0,e.i(673706).makeClassName)("AccordionHeader"),o=r.default.forwardRef((e,o)=>{let{children:d,className:u}=e,c=(0,t.__rest)(e,["children","className"]),{isOpen:m}=(0,r.useContext)(i.OpenContext);return r.default.createElement(s.Disclosure.Button,Object.assign({ref:o,className:(0,a.tremorTwMerge)(l("root"),"w-full flex items-center justify-between px-4 py-3","text-tremor-content-emphasis","dark:text-dark-tremor-content-emphasis",u)},c),r.default.createElement("div",{className:(0,a.tremorTwMerge)(l("children"),"flex flex-1 text-inherit mr-4")},d),r.default.createElement("div",null,r.default.createElement(n,{className:(0,a.tremorTwMerge)(l("arrowIcon"),"h-5 w-5 -mr-1","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle",m?"transition-all":"transition-all -rotate-180")})))});o.displayName="AccordionHeader",e.s(["AccordionHeader",0,o],898667)},130643,e=>{"use strict";var t=e.i(290571),r=e.i(271645),s=e.i(886148),n=e.i(444755);let i=(0,e.i(673706).makeClassName)("AccordionBody"),a=r.default.forwardRef((e,a)=>{let{children:l,className:o}=e,d=(0,t.__rest)(e,["children","className"]);return r.default.createElement(s.Disclosure.Panel,Object.assign({ref:a,className:(0,n.tremorTwMerge)(i("root"),"w-full text-tremor-default px-4 pb-3","text-tremor-content","dark:text-dark-tremor-content",o)},d),l)});a.displayName="AccordionBody",e.s(["AccordionBody",0,a],130643)},220508,e=>{"use strict";var t=e.i(271645);let r=t.forwardRef(function(e,r){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:r},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["CheckCircleIcon",0,r],220508)},503269,214520,601893,694421,140721,942803,35889,722678,e=>{"use strict";var t=e.i(271645),r=e.i(914189);e.s(["useControllable",0,function(e,s,n){let[i,a]=(0,t.useState)(n),l=void 0!==e,o=(0,t.useRef)(l),d=(0,t.useRef)(!1),u=(0,t.useRef)(!1);return!l||o.current||d.current?l||!o.current||u.current||(u.current=!0,o.current=l,console.error("A component is changing from controlled to uncontrolled. This may be caused by the value changing from a defined value to undefined, which should not happen.")):(d.current=!0,o.current=l,console.error("A component is changing from uncontrolled to controlled. This may be caused by the value changing from undefined to a defined value, which should not happen.")),[l?e:i,(0,r.useEvent)(e=>(l||a(e),null==s?void 0:s(e)))]}],503269),e.s(["useDefaultValue",0,function(e){let[r]=(0,t.useState)(e);return r}],214520);let s=(0,t.createContext)(void 0);function n(){return(0,t.useContext)(s)}e.s(["useDisabled",0,n],601893);var i=e.i(174080),a=e.i(746725);function l(e={},t=null,r=[]){for(let[s,n]of Object.entries(e))!function e(t,r,s){if(Array.isArray(s))for(let[n,i]of s.entries())e(t,o(r,n.toString()),i);else s instanceof Date?t.push([r,s.toISOString()]):"boolean"==typeof s?t.push([r,s?"1":"0"]):"string"==typeof s?t.push([r,s]):"number"==typeof s?t.push([r,`${s}`]):null==s?t.push([r,""]):l(s,r,t)}(r,o(t,s),n);return r}function o(e,t){return e?e+"["+t+"]":t}e.s(["attemptSubmit",0,function(e){var t,r;let s=null!=(t=null==e?void 0:e.form)?t:e.closest("form");if(s){for(let t of s.elements)if(t!==e&&("INPUT"===t.tagName&&"submit"===t.type||"BUTTON"===t.tagName&&"submit"===t.type||"INPUT"===t.nodeName&&"image"===t.type))return void t.click();null==(r=s.requestSubmit)||r.call(s)}},"objectToFormEntries",0,l],694421);var d=e.i(700020),u=e.i(2788);let c=(0,t.createContext)(null);function m({children:e}){let r=(0,t.useContext)(c);if(!r)return t.default.createElement(t.default.Fragment,null,e);let{target:s}=r;return s?(0,i.createPortal)(t.default.createElement(t.default.Fragment,null,e),s):null}function h({setForm:e,formId:r}){return(0,t.useEffect)(()=>{if(r){let t=document.getElementById(r);t&&e(t)}},[e,r]),r?null:t.default.createElement(u.Hidden,{features:u.HiddenFeatures.Hidden,as:"input",type:"hidden",hidden:!0,readOnly:!0,ref:t=>{if(!t)return;let r=t.closest("form");r&&e(r)}})}e.s(["FormFields",0,function({data:e,form:r,disabled:s,onReset:n,overrides:i}){let[o,c]=(0,t.useState)(null),f=(0,a.useDisposables)();return(0,t.useEffect)(()=>{if(n&&o)return f.addEventListener(o,"reset",n)},[o,r,n]),t.default.createElement(m,null,t.default.createElement(h,{setForm:c,formId:r}),l(e).map(([e,n])=>t.default.createElement(u.Hidden,{features:u.HiddenFeatures.Hidden,...(0,d.compact)({key:e,as:"input",type:"hidden",hidden:!0,readOnly:!0,form:r,disabled:s,name:e,value:n,...i})})))}],140721);let f=(0,t.createContext)(void 0);function p(){return(0,t.useContext)(f)}e.s(["useProvidedId",0,p],942803);var g=e.i(835696),x=e.i(294316);let y=(0,t.createContext)(null);y.displayName="DescriptionContext";let v=Object.assign((0,d.forwardRefWithAs)(function(e,r){let s=(0,t.useId)(),i=n(),{id:a=`headlessui-description-${s}`,...l}=e,o=function e(){let r=(0,t.useContext)(y);if(null===r){let t=Error("You used a component, but it is not inside a relevant parent.");throw Error.captureStackTrace&&Error.captureStackTrace(t,e),t}return r}(),u=(0,x.useSyncRefs)(r);(0,g.useIsoMorphicEffect)(()=>o.register(a),[a,o.register]);let c=i||!1,m=(0,t.useMemo)(()=>({...o.slot,disabled:c}),[o.slot,c]),h={ref:u,...o.props,id:a};return(0,d.useRender)()({ourProps:h,theirProps:l,slot:m,defaultTag:"p",name:o.name||"Description"})}),{});e.s(["Description",0,v,"useDescribedBy",0,function(){var e,r;return null!=(r=null==(e=(0,t.useContext)(y))?void 0:e.value)?r:void 0},"useDescriptions",0,function(){let[e,s]=(0,t.useState)([]);return[e.length>0?e.join(" "):void 0,(0,t.useMemo)(()=>function(e){let n=(0,r.useEvent)(e=>(s(t=>[...t,e]),()=>s(t=>{let r=t.slice(),s=r.indexOf(e);return -1!==s&&r.splice(s,1),r}))),i=(0,t.useMemo)(()=>({register:n,slot:e.slot,name:e.name,props:e.props,value:e.value}),[n,e.slot,e.name,e.props,e.value]);return t.default.createElement(y.Provider,{value:i},e.children)},[s])]}],35889);let b=(0,t.createContext)(null);function _(e){var r,s,n;let i=null!=(s=null==(r=(0,t.useContext)(b))?void 0:r.value)?s:void 0;return(null!=(n=null==e?void 0:e.length)?n:0)>0?[i,...e].filter(Boolean).join(" "):i}b.displayName="LabelContext";let j=Object.assign((0,d.forwardRefWithAs)(function(e,s){var i;let a=(0,t.useId)(),l=function e(){let r=(0,t.useContext)(b);if(null===r){let t=Error("You used a ` tag."),"__NEXT_ERROR_CODE",{value:"E863",enumerable:!1,configurable:!0});a=l.default.Children.only(o)}let K=P?a&&"object"==typeof a&&a.ref:A,W=l.default.useCallback(e=>(null!==M&&(w.current=(0,g.mountLinkInstance)(e,U,M,z,F,E)),()=>{w.current&&((0,g.unmountLinkForCurrentNavigation)(w.current),w.current=null),(0,g.unmountPrefetchableInstance)(e)}),[F,U,M,z,E]),H={ref:(0,c.useMergedRef)(W,K),onClick(t){P||"function"!=typeof N||N(t),P&&a.props&&"function"==typeof a.props.onClick&&a.props.onClick(t),!M||t.defaultPrevented||function(t,r,n,o,a,i,s){if("u">typeof window){let u,{nodeName:c}=t.currentTarget;if("A"===c.toUpperCase()&&((u=t.currentTarget.getAttribute("target"))&&"_self"!==u||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.nativeEvent&&2===t.nativeEvent.which)||t.currentTarget.hasAttribute("download"))return;if(!(0,p.isLocalURL)(r)){o&&(t.preventDefault(),location.replace(r));return}if(t.preventDefault(),i){let e=!1;if(i({preventDefault:()=>{e=!0}}),e)return}let{dispatchNavigateAction:d}=e.r(699781);l.default.startTransition(()=>{d(r,o?"replace":"push",!1===a?m.ScrollBehavior.NoScroll:m.ScrollBehavior.Default,n.current,s)})}}(t,U,w,x,T,_,I)},onMouseEnter(e){P||"function"!=typeof L||L(e),P&&a.props&&"function"==typeof a.props.onMouseEnter&&a.props.onMouseEnter(e),M&&F&&(0,g.onNavigationIntent)(e.currentTarget,!0===B)},onTouchStart:function(e){P||"function"!=typeof R||R(e),P&&a.props&&"function"==typeof a.props.onTouchStart&&a.props.onTouchStart(e),M&&F&&(0,g.onNavigationIntent)(e.currentTarget,!0===B)}};return(0,d.isAbsoluteUrl)(U)?H.href=U:P&&!j&&("a"!==a.type||"href"in a.props)||(H.href=(0,f.addBasePath)(U)),h=P?l.default.cloneElement(a,H):(0,i.jsx)("a",{...D,...H,children:o}),(0,i.jsx)(y.Provider,{value:v,children:h})}e.r(284508);let y=(0,l.createContext)(g.IDLE_LINK_STATUS),v=()=>(0,l.useContext)(y);("function"==typeof r.default||"object"==typeof r.default&&null!==r.default)&&void 0===r.default.__esModule&&(Object.defineProperty(r.default,"__esModule",{value:!0}),Object.assign(r.default,r),t.exports=r.default)},906579,e=>{"use strict";e.i(247167);var t=e.i(271645),r=e.i(343794),n=e.i(361275),o=e.i(702779),a=e.i(763731),i=e.i(242064);e.i(296059);var l=e.i(915654),s=e.i(694758),u=e.i(183293),c=e.i(403541),d=e.i(246422),f=e.i(838378);let m=new s.Keyframes("antStatusProcessing",{"0%":{transform:"scale(0.8)",opacity:.5},"100%":{transform:"scale(2.4)",opacity:0}}),g=new s.Keyframes("antZoomBadgeIn",{"0%":{transform:"scale(0) translate(50%, -50%)",opacity:0},"100%":{transform:"scale(1) translate(50%, -50%)"}}),p=new s.Keyframes("antZoomBadgeOut",{"0%":{transform:"scale(1) translate(50%, -50%)"},"100%":{transform:"scale(0) translate(50%, -50%)",opacity:0}}),b=new s.Keyframes("antNoWrapperZoomBadgeIn",{"0%":{transform:"scale(0)",opacity:0},"100%":{transform:"scale(1)"}}),h=new s.Keyframes("antNoWrapperZoomBadgeOut",{"0%":{transform:"scale(1)"},"100%":{transform:"scale(0)",opacity:0}}),y=new s.Keyframes("antBadgeLoadingCircle",{"0%":{transformOrigin:"50%"},"100%":{transform:"translate(50%, -50%) rotate(360deg)",transformOrigin:"50%"}}),v=e=>{let{fontHeight:t,lineWidth:r,marginXS:n,colorBorderBg:o}=e,a=e.colorTextLightSolid,i=e.colorError,l=e.colorErrorHover;return(0,f.mergeToken)(e,{badgeFontHeight:t,badgeShadowSize:r,badgeTextColor:a,badgeColor:i,badgeColorHover:l,badgeShadowColor:o,badgeProcessingDuration:"1.2s",badgeRibbonOffset:n,badgeRibbonCornerTransform:"scaleY(0.75)",badgeRibbonCornerFilter:"brightness(75%)"})},E=e=>{let{fontSize:t,lineHeight:r,fontSizeSM:n,lineWidth:o}=e;return{indicatorZIndex:"auto",indicatorHeight:Math.round(t*r)-2*o,indicatorHeightSM:t,dotSize:n/2,textFontSize:n,textFontSizeSM:n,textFontWeight:"normal",statusSize:n/2}},w=(0,d.genStyleHooks)("Badge",e=>(e=>{let{componentCls:t,iconCls:r,antCls:n,badgeShadowSize:o,textFontSize:a,textFontSizeSM:i,statusSize:s,dotSize:d,textFontWeight:f,indicatorHeight:v,indicatorHeightSM:E,marginXS:w,calc:S}=e,O=`${n}-scroll-number`,$=(0,c.genPresetColor)(e,(e,{darkColor:r})=>({[`&${t} ${t}-color-${e}`]:{background:r,[`&:not(${t}-count)`]:{color:r},"a:hover &":{background:r}}}));return{[t]:Object.assign(Object.assign(Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"relative",display:"inline-block",width:"fit-content",lineHeight:1,[`${t}-count`]:{display:"inline-flex",justifyContent:"center",zIndex:e.indicatorZIndex,minWidth:v,height:v,color:e.badgeTextColor,fontWeight:f,fontSize:a,lineHeight:(0,l.unit)(v),whiteSpace:"nowrap",textAlign:"center",background:e.badgeColor,borderRadius:S(v).div(2).equal(),boxShadow:`0 0 0 ${(0,l.unit)(o)} ${e.badgeShadowColor}`,transition:`background ${e.motionDurationMid}`,a:{color:e.badgeTextColor},"a:hover":{color:e.badgeTextColor},"a:hover &":{background:e.badgeColorHover}},[`${t}-count-sm`]:{minWidth:E,height:E,fontSize:i,lineHeight:(0,l.unit)(E),borderRadius:S(E).div(2).equal()},[`${t}-multiple-words`]:{padding:`0 ${(0,l.unit)(e.paddingXS)}`,bdi:{unicodeBidi:"plaintext"}},[`${t}-dot`]:{zIndex:e.indicatorZIndex,width:d,minWidth:d,height:d,background:e.badgeColor,borderRadius:"100%",boxShadow:`0 0 0 ${(0,l.unit)(o)} ${e.badgeShadowColor}`},[`${t}-count, ${t}-dot, ${O}-custom-component`]:{position:"absolute",top:0,insetInlineEnd:0,transform:"translate(50%, -50%)",transformOrigin:"100% 0%",[`&${r}-spin`]:{animationName:y,animationDuration:"1s",animationIterationCount:"infinite",animationTimingFunction:"linear"}},[`&${t}-status`]:{lineHeight:"inherit",verticalAlign:"baseline",[`${t}-status-dot`]:{position:"relative",top:-1,display:"inline-block",width:s,height:s,verticalAlign:"middle",borderRadius:"50%"},[`${t}-status-success`]:{backgroundColor:e.colorSuccess},[`${t}-status-processing`]:{overflow:"visible",color:e.colorInfo,backgroundColor:e.colorInfo,borderColor:"currentcolor","&::after":{position:"absolute",top:0,insetInlineStart:0,width:"100%",height:"100%",borderWidth:o,borderStyle:"solid",borderColor:"inherit",borderRadius:"50%",animationName:m,animationDuration:e.badgeProcessingDuration,animationIterationCount:"infinite",animationTimingFunction:"ease-in-out",content:'""'}},[`${t}-status-default`]:{backgroundColor:e.colorTextPlaceholder},[`${t}-status-error`]:{backgroundColor:e.colorError},[`${t}-status-warning`]:{backgroundColor:e.colorWarning},[`${t}-status-text`]:{marginInlineStart:w,color:e.colorText,fontSize:e.fontSize}}}),$),{[`${t}-zoom-appear, ${t}-zoom-enter`]:{animationName:g,animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseOutBack,animationFillMode:"both"},[`${t}-zoom-leave`]:{animationName:p,animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseOutBack,animationFillMode:"both"},[`&${t}-not-a-wrapper`]:{[`${t}-zoom-appear, ${t}-zoom-enter`]:{animationName:b,animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseOutBack},[`${t}-zoom-leave`]:{animationName:h,animationDuration:e.motionDurationSlow,animationTimingFunction:e.motionEaseOutBack},[`&:not(${t}-status)`]:{verticalAlign:"middle"},[`${O}-custom-component, ${t}-count`]:{transform:"none"},[`${O}-custom-component, ${O}`]:{position:"relative",top:"auto",display:"block",transformOrigin:"50% 50%"}},[O]:{overflow:"hidden",transition:`all ${e.motionDurationMid} ${e.motionEaseOutBack}`,[`${O}-only`]:{position:"relative",display:"inline-block",height:v,transition:`all ${e.motionDurationSlow} ${e.motionEaseOutBack}`,WebkitTransformStyle:"preserve-3d",WebkitBackfaceVisibility:"hidden",[`> p${O}-only-unit`]:{height:v,margin:0,WebkitTransformStyle:"preserve-3d",WebkitBackfaceVisibility:"hidden"}},[`${O}-symbol`]:{verticalAlign:"top"}},"&-rtl":{direction:"rtl",[`${t}-count, ${t}-dot, ${O}-custom-component`]:{transform:"translate(-50%, -50%)"}}})}})(v(e)),E),S=(0,d.genStyleHooks)(["Badge","Ribbon"],e=>(e=>{let{antCls:t,badgeFontHeight:r,marginXS:n,badgeRibbonOffset:o,calc:a}=e,i=`${t}-ribbon`,s=`${t}-ribbon-wrapper`,d=(0,c.genPresetColor)(e,(e,{darkColor:t})=>({[`&${i}-color-${e}`]:{background:t,color:t}}));return{[s]:{position:"relative"},[i]:Object.assign(Object.assign(Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"absolute",top:n,padding:`0 ${(0,l.unit)(e.paddingXS)}`,color:e.colorPrimary,lineHeight:(0,l.unit)(r),whiteSpace:"nowrap",backgroundColor:e.colorPrimary,borderRadius:e.borderRadiusSM,[`${i}-text`]:{color:e.badgeTextColor},[`${i}-corner`]:{position:"absolute",top:"100%",width:o,height:o,color:"currentcolor",border:`${(0,l.unit)(a(o).div(2).equal())} solid`,transform:e.badgeRibbonCornerTransform,transformOrigin:"top",filter:e.badgeRibbonCornerFilter}}),d),{[`&${i}-placement-end`]:{insetInlineEnd:a(o).mul(-1).equal(),borderEndEndRadius:0,[`${i}-corner`]:{insetInlineEnd:0,borderInlineEndColor:"transparent",borderBlockEndColor:"transparent"}},[`&${i}-placement-start`]:{insetInlineStart:a(o).mul(-1).equal(),borderEndStartRadius:0,[`${i}-corner`]:{insetInlineStart:0,borderBlockEndColor:"transparent",borderInlineStartColor:"transparent"}},"&-rtl":{direction:"rtl"}})}})(v(e)),E),O=e=>{let n,{prefixCls:o,value:a,current:i,offset:l=0}=e;return l&&(n={position:"absolute",top:`${l}00%`,left:0}),t.createElement("span",{style:n,className:(0,r.default)(`${o}-only-unit`,{current:i})},a)},$=e=>{let r,n,{prefixCls:o,count:a,value:i}=e,l=Number(i),s=Math.abs(a),[u,c]=t.useState(l),[d,f]=t.useState(s),m=()=>{c(l),f(s)};if(t.useEffect(()=>{let e=setTimeout(m,1e3);return()=>clearTimeout(e)},[l]),u===l||Number.isNaN(l)||Number.isNaN(u))r=[t.createElement(O,Object.assign({},e,{key:l,current:!0}))],n={transition:"none"};else{r=[];let o=l+10,a=[];for(let e=l;e<=o;e+=1)a.push(e);let i=de%10===u);r=(i<0?a.slice(0,c+1):a.slice(c)).map((r,n)=>t.createElement(O,Object.assign({},e,{key:r,value:r%10,offset:i<0?n-c:n,current:n===c}))),n={transform:`translateY(${-function(e,t,r){let n=e,o=0;for(;(n+10)%10!==t;)n+=r,o+=r;return o}(u,l,i)}00%)`}}return t.createElement("span",{className:`${o}-only`,style:n,onTransitionEnd:m},r)};var C=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let j=t.forwardRef((e,n)=>{let{prefixCls:o,count:l,className:s,motionClassName:u,style:c,title:d,show:f,component:m="sup",children:g}=e,p=C(e,["prefixCls","count","className","motionClassName","style","title","show","component","children"]),{getPrefixCls:b}=t.useContext(i.ConfigContext),h=b("scroll-number",o),y=Object.assign(Object.assign({},p),{"data-show":f,style:c,className:(0,r.default)(h,s,u),title:d}),v=l;if(l&&Number(l)%1==0){let e=String(l).split("");v=t.createElement("bdi",null,e.map((r,n)=>t.createElement($,{prefixCls:h,count:Number(l),value:r,key:e.length-n})))}return((null==c?void 0:c.borderColor)&&(y.style=Object.assign(Object.assign({},c),{boxShadow:`0 0 0 1px ${c.borderColor} inset`})),g)?(0,a.cloneElement)(g,e=>({className:(0,r.default)(`${h}-custom-component`,null==e?void 0:e.className,u)})):t.createElement(m,Object.assign({},y,{ref:n}),v)});var x=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r};let k=t.forwardRef((e,l)=>{var s,u,c,d,f;let{prefixCls:m,scrollNumberPrefixCls:g,children:p,status:b,text:h,color:y,count:v=null,overflowCount:E=99,dot:S=!1,size:O="default",title:$,offset:C,style:k,className:T,rootClassName:N,classNames:L,styles:R,showZero:P=!1}=e,_=x(e,["prefixCls","scrollNumberPrefixCls","children","status","text","color","count","overflowCount","dot","size","title","offset","style","className","rootClassName","classNames","styles","showZero"]),{getPrefixCls:I,direction:A,badge:B}=t.useContext(i.ConfigContext),D=I("badge",m),[M,F,z]=w(D),U=v>E?`${E}+`:v,K="0"===U||0===U||"0"===h||0===h,W=null===v||K&&!P,H=(null!=b||null!=y)&&W,G=null!=b||!K,q=S&&!K,V=q?"":U,Z=(0,t.useMemo)(()=>((null==V||""===V)&&(null==h||""===h)||K&&!P)&&!q,[V,K,P,q,h]),X=(0,t.useRef)(v);Z||(X.current=v);let Q=X.current,Y=(0,t.useRef)(V);Z||(Y.current=V);let J=Y.current,ee=(0,t.useRef)(q);Z||(ee.current=q);let et=(0,t.useMemo)(()=>{if(!C)return Object.assign(Object.assign({},null==B?void 0:B.style),k);let e={marginTop:C[1]};return"rtl"===A?e.left=Number.parseInt(C[0],10):e.right=-Number.parseInt(C[0],10),Object.assign(Object.assign(Object.assign({},e),null==B?void 0:B.style),k)},[A,C,k,null==B?void 0:B.style]),er=null!=$?$:"string"==typeof Q||"number"==typeof Q?Q:void 0,en=!Z&&(0===h?P:!!h&&!0!==h),eo=en?t.createElement("span",{className:`${D}-status-text`},h):null,ea=Q&&"object"==typeof Q?(0,a.cloneElement)(Q,e=>({style:Object.assign(Object.assign({},et),e.style)})):void 0,ei=(0,o.isPresetColor)(y,!1),el=(0,r.default)(null==L?void 0:L.indicator,null==(s=null==B?void 0:B.classNames)?void 0:s.indicator,{[`${D}-status-dot`]:H,[`${D}-status-${b}`]:!!b,[`${D}-color-${y}`]:ei}),es={};y&&!ei&&(es.color=y,es.background=y);let eu=(0,r.default)(D,{[`${D}-status`]:H,[`${D}-not-a-wrapper`]:!p,[`${D}-rtl`]:"rtl"===A},T,N,null==B?void 0:B.className,null==(u=null==B?void 0:B.classNames)?void 0:u.root,null==L?void 0:L.root,F,z);if(!p&&H&&(h||G||!W)){let e=et.color;return M(t.createElement("span",Object.assign({},_,{className:eu,style:Object.assign(Object.assign(Object.assign({},null==R?void 0:R.root),null==(c=null==B?void 0:B.styles)?void 0:c.root),et)}),t.createElement("span",{className:el,style:Object.assign(Object.assign(Object.assign({},null==R?void 0:R.indicator),null==(d=null==B?void 0:B.styles)?void 0:d.indicator),es)}),en&&t.createElement("span",{style:{color:e},className:`${D}-status-text`},h)))}return M(t.createElement("span",Object.assign({ref:l},_,{className:eu,style:Object.assign(Object.assign({},null==(f=null==B?void 0:B.styles)?void 0:f.root),null==R?void 0:R.root)}),p,t.createElement(n.default,{visible:!Z,motionName:`${D}-zoom`,motionAppear:!1,motionDeadline:1e3},({className:e})=>{var n,o;let a=I("scroll-number",g),i=ee.current,l=(0,r.default)(null==L?void 0:L.indicator,null==(n=null==B?void 0:B.classNames)?void 0:n.indicator,{[`${D}-dot`]:i,[`${D}-count`]:!i,[`${D}-count-sm`]:"small"===O,[`${D}-multiple-words`]:!i&&J&&J.toString().length>1,[`${D}-status-${b}`]:!!b,[`${D}-color-${y}`]:ei}),s=Object.assign(Object.assign(Object.assign({},null==R?void 0:R.indicator),null==(o=null==B?void 0:B.styles)?void 0:o.indicator),et);return y&&!ei&&((s=s||{}).background=y),t.createElement(j,{prefixCls:a,show:!Z,motionClassName:e,className:l,count:J,title:er,style:s,key:"scrollNumber"},ea)}),eo))});k.Ribbon=e=>{let{className:n,prefixCls:a,style:l,color:s,children:u,text:c,placement:d="end",rootClassName:f}=e,{getPrefixCls:m,direction:g}=t.useContext(i.ConfigContext),p=m("ribbon",a),b=`${p}-wrapper`,[h,y,v]=S(p,b),E=(0,o.isPresetColor)(s,!1),w=(0,r.default)(p,`${p}-placement-${d}`,{[`${p}-rtl`]:"rtl"===g,[`${p}-color-${s}`]:E},n),O={},$={};return s&&!E&&(O.background=s,$.color=s),h(t.createElement("div",{className:(0,r.default)(b,f,y,v)},u,t.createElement("div",{className:(0,r.default)(w,y),style:Object.assign(Object.assign({},O),l)},t.createElement("span",{className:`${p}-text`},c),t.createElement("div",{className:`${p}-corner`,style:$}))))},e.s(["Badge",0,k],906579)},115571,e=>{"use strict";let t="local-storage-change";e.s(["LOCAL_STORAGE_EVENT",0,t,"emitLocalStorageChange",0,function(e){window.dispatchEvent(new CustomEvent(t,{detail:{key:e}}))},"getLocalStorageItem",0,function(e){try{return window.localStorage.getItem(e)}catch(t){return console.warn(`Error reading localStorage key "${e}":`,t),null}},"removeLocalStorageItem",0,function(e){try{window.localStorage.removeItem(e)}catch(t){console.warn(`Error removing localStorage key "${e}":`,t)}},"setLocalStorageItem",0,function(e,t){try{window.localStorage.setItem(e,t)}catch(t){console.warn(`Error setting localStorage key "${e}":`,t)}}])},275144,e=>{"use strict";var t=e.i(843476),r=e.i(271645),n=e.i(602869);let o=(0,r.createContext)(void 0);e.s(["ThemeProvider",0,({children:e,accessToken:a})=>{let[i,l]=(0,r.useState)(null),[s,u]=(0,r.useState)(null);return(0,r.useEffect)(()=>{(async()=>{try{let e=(0,n.getProxyBaseUrl)(),t=e?`${e}/get/ui_theme_settings`:"/get/ui_theme_settings",r=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json"}});if(r.ok){let e=await r.json();e.values?.logo_url&&l(e.values.logo_url),e.values?.favicon_url&&u(e.values.favicon_url)}}catch(e){console.warn("Failed to load theme settings from backend:",e)}})()},[]),(0,r.useEffect)(()=>{if(s){let e=document.querySelectorAll("link[rel*='icon']");if(e.length>0)e.forEach(e=>{e.href=s});else{let e=document.createElement("link");e.rel="icon",e.href=s,document.head.appendChild(e)}}},[s]),(0,t.jsx)(o.Provider,{value:{logoUrl:i,setLogoUrl:l,faviconUrl:s,setFaviconUrl:u},children:e})},"useTheme",0,()=>{let e=(0,r.useContext)(o);if(!e)throw Error("useTheme must be used within a ThemeProvider");return e}])},143488,e=>{"use strict";var t=e.i(266027),r=e.i(602869);let n=(0,e.i(243652).createQueryKeys)("healthReadinessDetails"),o=async e=>{let t=(0,r.getProxyBaseUrl)(),n=await fetch(`${t}/health/readiness/details`,{method:"GET",headers:{[(0,r.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!n.ok)throw Error(`Failed to fetch health readiness details: ${n.statusText}`);return n.json()};e.s(["useHealthReadinessDetails",0,e=>(0,t.useQuery)({queryKey:n.detail("readiness"),queryFn:()=>o(e),enabled:!!e,staleTime:3e5,retry:!1})])},731565,e=>{"use strict";var t=e.i(115571),r=e.i(271645);function n(e){let r=t=>{"disableBlogPosts"===t.key&&e()},n=t=>{let{key:r}=t.detail;"disableBlogPosts"===r&&e()};return window.addEventListener("storage",r),window.addEventListener(t.LOCAL_STORAGE_EVENT,n),()=>{window.removeEventListener("storage",r),window.removeEventListener(t.LOCAL_STORAGE_EVENT,n)}}function o(){return"true"===(0,t.getLocalStorageItem)("disableBlogPosts")}e.s(["useDisableBlogPosts",0,function(){return(0,r.useSyncExternalStore)(n,o)}])},912089,636772,e=>{"use strict";var t=e.i(115571),r=e.i(271645);function n(e){let r=t=>{"disableBouncingIcon"===t.key&&e()},n=t=>{let{key:r}=t.detail;"disableBouncingIcon"===r&&e()};return window.addEventListener("storage",r),window.addEventListener(t.LOCAL_STORAGE_EVENT,n),()=>{window.removeEventListener("storage",r),window.removeEventListener(t.LOCAL_STORAGE_EVENT,n)}}function o(){return"true"===(0,t.getLocalStorageItem)("disableBouncingIcon")}function a(e){let r=t=>{"disableShowPrompts"===t.key&&e()},n=t=>{let{key:r}=t.detail;"disableShowPrompts"===r&&e()};return window.addEventListener("storage",r),window.addEventListener(t.LOCAL_STORAGE_EVENT,n),()=>{window.removeEventListener("storage",r),window.removeEventListener(t.LOCAL_STORAGE_EVENT,n)}}function i(){return"true"===(0,t.getLocalStorageItem)("disableShowPrompts")}e.s(["useDisableBouncingIcon",0,function(){return(0,r.useSyncExternalStore)(n,o)}],912089),e.s(["useDisableShowPrompts",0,function(){return(0,r.useSyncExternalStore)(a,i)}],636772)},371401,222038,799676,e=>{"use strict";var t=e.i(115571),r=e.i(271645);function n(e){let r=t=>{"disableUsageIndicator"===t.key&&e()},n=t=>{let{key:r}=t.detail;"disableUsageIndicator"===r&&e()};return window.addEventListener("storage",r),window.addEventListener(t.LOCAL_STORAGE_EVENT,n),()=>{window.removeEventListener("storage",r),window.removeEventListener(t.LOCAL_STORAGE_EVENT,n)}}function o(){return"true"===(0,t.getLocalStorageItem)("disableUsageIndicator")}e.s(["useDisableUsageIndicator",0,function(){return(0,r.useSyncExternalStore)(n,o)}],371401),e.s(["navAccountDisplayName",0,function(e,t){let r=e?.trim();if(r)return r;let n=t?.trim();return!n||/^default[_\s-]?user[_\s-]?id$/i.test(n)?"Account":n}],222038);var a=e.i(843476);e.s([],704824),e.i(704824),e.i(247167);var i=e.i(552245),l=e.i(733332);let s=r.createContext(void 0);function u(){let e=r.useContext(s);if(void 0===e)throw Error((0,l.default)(13));return e}let c={imageLoadingStatus:()=>null},d=r.forwardRef(function(e,t){let{className:n,render:o,style:l,...u}=e,[d,f]=r.useState("idle"),m=r.useMemo(()=>({imageLoadingStatus:d,setImageLoadingStatus:f}),[d,f]),g=(0,i.useRenderElement)("span",e,{state:{imageLoadingStatus:d},ref:t,props:u,stateAttributesMapping:c});return(0,a.jsx)(s.Provider,{value:m,children:g})});var f=e.i(667865),m=e.i(146376),g=e.i(137584),p=e.i(209407),b=e.i(223910),h=e.i(956789);let y={...c,...p.transitionStatusMapping},v=r.forwardRef(function(e,t){let{className:n,render:o,onLoadingStatusChange:a,style:l,...s}=e,{setImageLoadingStatus:c}=u(),d=function(e,{referrerPolicy:t,crossOrigin:n,sizes:o,srcSet:a}){let[i,l]=r.useState("idle");return(0,m.useIsoLayoutEffect)(()=>{if(!e&&!a)return l("error"),h.NOOP;let r=!0,i=new window.Image,s=e=>()=>{r&&l(e)};return l("loading"),i.onload=s("loaded"),i.onerror=s("error"),t&&(i.referrerPolicy=t),i.crossOrigin=n??null,o&&(i.sizes=o),a&&(i.srcset=a),e&&(i.src=e),i.complete&&l(i.naturalWidth>0?"loaded":"error"),()=>{r=!1}},[e,a,o,n,t]),i}(s.src,s),p="loaded"===d,{mounted:v,transitionStatus:E,setMounted:w}=(0,b.useTransitionStatus)(p),S=r.useRef(null),O=(0,f.useStableCallback)(e=>{a?.(e),c(e)});(0,m.useIsoLayoutEffect)(()=>{"idle"!==d&&O(d)},[d,O]),(0,m.useIsoLayoutEffect)(()=>()=>c("idle"),[c]),(0,g.useOpenChangeComplete)({open:p,ref:S,onComplete(){p||w(!1)}});let $=(0,i.useRenderElement)("img",e,{state:{imageLoadingStatus:d,transitionStatus:E},ref:[t,S],props:s,stateAttributesMapping:y,enabled:v});return v?$:null});var E=e.i(439957);let w=r.forwardRef(function(e,t){let{className:n,render:o,delay:a,style:l,...s}=e,{imageLoadingStatus:d}=u(),[f,m]=r.useState(void 0===a),g=(0,E.useTimeout)();return r.useEffect(()=>(void 0!==a?g.start(a,()=>m(!0)):m(!0),g.clear),[g,a]),(0,i.useRenderElement)("span",e,{state:{imageLoadingStatus:d},ref:t,props:s,stateAttributesMapping:c,enabled:"loaded"!==d&&(void 0===a||f)})});e.s(["Fallback",0,w,"Image",0,v,"Root",0,d],514751);var S=e.i(514751),S=S,O=e.i(115504);let $=r.forwardRef(({className:e,...t},r)=>(0,a.jsx)(S.Root,{ref:r,"data-slot":"avatar",className:(0,O.cn)("relative flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-full",e),...t}));$.displayName="Avatar",r.forwardRef(({className:e,...t},r)=>(0,a.jsx)(S.Image,{ref:r,"data-slot":"avatar-image",className:(0,O.cn)("size-full object-cover",e),...t})).displayName="AvatarImage";let C=r.forwardRef(({className:e,...t},r)=>(0,a.jsx)(S.Fallback,{ref:r,"data-slot":"avatar-fallback",className:(0,O.cn)("flex size-full items-center justify-center rounded-full text-xs font-medium",e),...t}));C.displayName="AvatarFallback",e.s(["Avatar",0,$,"AvatarFallback",0,C],799676)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/17oj3l80l727c.js b/litellm/proxy/_experimental/out/_next/static/chunks/17oj3l80l727c.js new file mode 100644 index 00000000000..b533b413201 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/17oj3l80l727c.js @@ -0,0 +1,8 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,244451,e=>{"use strict";let t;e.i(247167);var o=e.i(271645),n=e.i(343794),r=e.i(242064),i=e.i(763731),l=e.i(174428);let a=80*Math.PI,d=e=>{let{dotClassName:t,style:r,hasCircleCls:i}=e;return o.createElement("circle",{className:(0,n.default)(`${t}-circle`,{[`${t}-circle-bg`]:i}),r:40,cx:50,cy:50,strokeWidth:20,style:r})},s=({percent:e,prefixCls:t})=>{let r=`${t}-dot`,i=`${r}-holder`,s=`${i}-hidden`,[c,u]=o.useState(!1);(0,l.default)(()=>{0!==e&&u(!0)},[0!==e]);let p=Math.max(Math.min(e,100),0);if(!c)return null;let b={strokeDashoffset:`${a/4}`,strokeDasharray:`${a*p/100} ${a*(100-p)/100}`};return o.createElement("span",{className:(0,n.default)(i,`${r}-progress`,p<=0&&s)},o.createElement("svg",{viewBox:"0 0 100 100",role:"progressbar","aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":p},o.createElement(d,{dotClassName:r,hasCircleCls:!0}),o.createElement(d,{dotClassName:r,style:b})))};function c(e){let{prefixCls:t,percent:r=0}=e,i=`${t}-dot`,l=`${i}-holder`,a=`${l}-hidden`;return o.createElement(o.Fragment,null,o.createElement("span",{className:(0,n.default)(l,r>0&&a)},o.createElement("span",{className:(0,n.default)(i,`${t}-dot-spin`)},[1,2,3,4].map(e=>o.createElement("i",{className:`${t}-dot-item`,key:e})))),o.createElement(s,{prefixCls:t,percent:r}))}function u(e){var t;let{prefixCls:r,indicator:l,percent:a}=e,d=`${r}-dot`;return l&&o.isValidElement(l)?(0,i.cloneElement)(l,{className:(0,n.default)(null==(t=l.props)?void 0:t.className,d),percent:a}):o.createElement(c,{prefixCls:r,percent:a})}e.i(296059);var p=e.i(694758),b=e.i(183293),f=e.i(246422),m=e.i(838378);let g=new p.Keyframes("antSpinMove",{to:{opacity:1}}),h=new p.Keyframes("antRotate",{to:{transform:"rotate(405deg)"}}),v=(0,f.genStyleHooks)("Spin",e=>(e=>{let{componentCls:t,calc:o}=e;return{[t]:Object.assign(Object.assign({},(0,b.resetComponent)(e)),{position:"absolute",display:"none",color:e.colorPrimary,fontSize:0,textAlign:"center",verticalAlign:"middle",opacity:0,transition:`transform ${e.motionDurationSlow} ${e.motionEaseInOutCirc}`,"&-spinning":{position:"relative",display:"inline-block",opacity:1},[`${t}-text`]:{fontSize:e.fontSize,paddingTop:o(o(e.dotSize).sub(e.fontSize)).div(2).add(2).equal()},"&-fullscreen":{position:"fixed",width:"100vw",height:"100vh",backgroundColor:e.colorBgMask,zIndex:e.zIndexPopupBase,inset:0,display:"flex",alignItems:"center",flexDirection:"column",justifyContent:"center",opacity:0,visibility:"hidden",transition:`all ${e.motionDurationMid}`,"&-show":{opacity:1,visibility:"visible"},[t]:{[`${t}-dot-holder`]:{color:e.colorWhite},[`${t}-text`]:{color:e.colorTextLightSolid}}},"&-nested-loading":{position:"relative",[`> div > ${t}`]:{position:"absolute",top:0,insetInlineStart:0,zIndex:4,display:"block",width:"100%",height:"100%",maxHeight:e.contentHeight,[`${t}-dot`]:{position:"absolute",top:"50%",insetInlineStart:"50%",margin:o(e.dotSize).mul(-1).div(2).equal()},[`${t}-text`]:{position:"absolute",top:"50%",width:"100%",textShadow:`0 1px 2px ${e.colorBgContainer}`},[`&${t}-show-text ${t}-dot`]:{marginTop:o(e.dotSize).div(2).mul(-1).sub(10).equal()},"&-sm":{[`${t}-dot`]:{margin:o(e.dotSizeSM).mul(-1).div(2).equal()},[`${t}-text`]:{paddingTop:o(o(e.dotSizeSM).sub(e.fontSize)).div(2).add(2).equal()},[`&${t}-show-text ${t}-dot`]:{marginTop:o(e.dotSizeSM).div(2).mul(-1).sub(10).equal()}},"&-lg":{[`${t}-dot`]:{margin:o(e.dotSizeLG).mul(-1).div(2).equal()},[`${t}-text`]:{paddingTop:o(o(e.dotSizeLG).sub(e.fontSize)).div(2).add(2).equal()},[`&${t}-show-text ${t}-dot`]:{marginTop:o(e.dotSizeLG).div(2).mul(-1).sub(10).equal()}}},[`${t}-container`]:{position:"relative",transition:`opacity ${e.motionDurationSlow}`,"&::after":{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,zIndex:10,width:"100%",height:"100%",background:e.colorBgContainer,opacity:0,transition:`all ${e.motionDurationSlow}`,content:'""',pointerEvents:"none"}},[`${t}-blur`]:{clear:"both",opacity:.5,userSelect:"none",pointerEvents:"none","&::after":{opacity:.4,pointerEvents:"auto"}}},"&-tip":{color:e.spinDotDefault},[`${t}-dot-holder`]:{width:"1em",height:"1em",fontSize:e.dotSize,display:"inline-block",transition:`transform ${e.motionDurationSlow} ease, opacity ${e.motionDurationSlow} ease`,transformOrigin:"50% 50%",lineHeight:1,color:e.colorPrimary,"&-hidden":{transform:"scale(0.3)",opacity:0}},[`${t}-dot-progress`]:{position:"absolute",inset:0},[`${t}-dot`]:{position:"relative",display:"inline-block",fontSize:e.dotSize,width:"1em",height:"1em","&-item":{position:"absolute",display:"block",width:o(e.dotSize).sub(o(e.marginXXS).div(2)).div(2).equal(),height:o(e.dotSize).sub(o(e.marginXXS).div(2)).div(2).equal(),background:"currentColor",borderRadius:"100%",transform:"scale(0.75)",transformOrigin:"50% 50%",opacity:.3,animationName:g,animationDuration:"1s",animationIterationCount:"infinite",animationTimingFunction:"linear",animationDirection:"alternate","&:nth-child(1)":{top:0,insetInlineStart:0,animationDelay:"0s"},"&:nth-child(2)":{top:0,insetInlineEnd:0,animationDelay:"0.4s"},"&:nth-child(3)":{insetInlineEnd:0,bottom:0,animationDelay:"0.8s"},"&:nth-child(4)":{bottom:0,insetInlineStart:0,animationDelay:"1.2s"}},"&-spin":{transform:"rotate(45deg)",animationName:h,animationDuration:"1.2s",animationIterationCount:"infinite",animationTimingFunction:"linear"},"&-circle":{strokeLinecap:"round",transition:["stroke-dashoffset","stroke-dasharray","stroke","stroke-width","opacity"].map(t=>`${t} ${e.motionDurationSlow} ease`).join(","),fillOpacity:0,stroke:"currentcolor"},"&-circle-bg":{stroke:e.colorFillSecondary}},[`&-sm ${t}-dot`]:{"&, &-holder":{fontSize:e.dotSizeSM}},[`&-sm ${t}-dot-holder`]:{i:{width:o(o(e.dotSizeSM).sub(o(e.marginXXS).div(2))).div(2).equal(),height:o(o(e.dotSizeSM).sub(o(e.marginXXS).div(2))).div(2).equal()}},[`&-lg ${t}-dot`]:{"&, &-holder":{fontSize:e.dotSizeLG}},[`&-lg ${t}-dot-holder`]:{i:{width:o(o(e.dotSizeLG).sub(e.marginXXS)).div(2).equal(),height:o(o(e.dotSizeLG).sub(e.marginXXS)).div(2).equal()}},[`&${t}-show-text ${t}-text`]:{display:"block"}})}})((0,m.mergeToken)(e,{spinDotDefault:e.colorTextDescription})),e=>{let{controlHeightLG:t,controlHeight:o}=e;return{contentHeight:400,dotSize:t/2,dotSizeSM:.35*t,dotSizeLG:o}}),$=[[30,.05],[70,.03],[96,.01]];var y=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,n=Object.getOwnPropertySymbols(e);rt.indexOf(n[r])&&Object.prototype.propertyIsEnumerable.call(e,n[r])&&(o[n[r]]=e[n[r]]);return o};let C=e=>{var i;let{prefixCls:l,spinning:a=!0,delay:d=0,className:s,rootClassName:c,size:p="default",tip:b,wrapperClassName:f,style:m,children:g,fullscreen:h=!1,indicator:C,percent:S}=e,k=y(e,["prefixCls","spinning","delay","className","rootClassName","size","tip","wrapperClassName","style","children","fullscreen","indicator","percent"]),{getPrefixCls:x,direction:w,className:E,style:O,indicator:I}=(0,r.useComponentConfig)("spin"),z=x("spin",l),[j,N,R]=v(z),[D,B]=o.useState(()=>a&&(!a||!d||!!Number.isNaN(Number(d)))),P=function(e,t){let[n,r]=o.useState(0),i=o.useRef(null),l="auto"===t;return o.useEffect(()=>(l&&e&&(r(0),i.current=setInterval(()=>{r(e=>{let t=100-e;for(let o=0;o<$.length;o+=1){let[n,r]=$[o];if(e<=n)return e+t*r}return e})},200)),()=>{i.current&&(clearInterval(i.current),i.current=null)}),[l,e]),l?n:t}(D,S);o.useEffect(()=>{if(a){let e=function(e,t,o){var n,r=o||{},i=r.noTrailing,l=void 0!==i&&i,a=r.noLeading,d=void 0!==a&&a,s=r.debounceMode,c=void 0===s?void 0:s,u=!1,p=0;function b(){n&&clearTimeout(n)}function f(){for(var o=arguments.length,r=Array(o),i=0;ie?d?(p=Date.now(),l||(n=setTimeout(c?m:f,e))):f():!0!==l&&(n=setTimeout(c?m:f,void 0===c?e-s:e)))}return f.cancel=function(e){var t=(e||{}).upcomingOnly;b(),u=!(void 0!==t&&t)},f}(d,()=>{B(!0)},{debounceMode:false});return e(),()=>{var t;null==(t=null==e?void 0:e.cancel)||t.call(e)}}B(!1)},[d,a]);let q=o.useMemo(()=>void 0!==g&&!h,[g,h]),T=(0,n.default)(z,E,{[`${z}-sm`]:"small"===p,[`${z}-lg`]:"large"===p,[`${z}-spinning`]:D,[`${z}-show-text`]:!!b,[`${z}-rtl`]:"rtl"===w},s,!h&&c,N,R),M=(0,n.default)(`${z}-container`,{[`${z}-blur`]:D}),L=null!=(i=null!=C?C:I)?i:t,H=Object.assign(Object.assign({},O),m),X=o.createElement("div",Object.assign({},k,{style:H,className:T,"aria-live":"polite","aria-busy":D}),o.createElement(u,{prefixCls:z,indicator:L,percent:P}),b&&(q||h)?o.createElement("div",{className:`${z}-text`},b):null);return j(q?o.createElement("div",Object.assign({},k,{className:(0,n.default)(`${z}-nested-loading`,f,N,R)}),D&&o.createElement("div",{key:"loading"},X),o.createElement("div",{className:M,key:"container"},g)):h?o.createElement("div",{className:(0,n.default)(`${z}-fullscreen`,{[`${z}-fullscreen-show`]:D},c,N,R)},X):X)};C.setDefaultIndicator=e=>{t=e},e.s(["default",0,C],244451)},91874,e=>{"use strict";var t=e.i(931067),o=e.i(209428),n=e.i(211577),r=e.i(392221),i=e.i(703923),l=e.i(343794),a=e.i(914949),d=e.i(271645),s=["prefixCls","className","style","checked","disabled","defaultChecked","type","title","onChange"],c=(0,d.forwardRef)(function(e,c){var u=e.prefixCls,p=void 0===u?"rc-checkbox":u,b=e.className,f=e.style,m=e.checked,g=e.disabled,h=e.defaultChecked,v=e.type,$=void 0===v?"checkbox":v,y=e.title,C=e.onChange,S=(0,i.default)(e,s),k=(0,d.useRef)(null),x=(0,d.useRef)(null),w=(0,a.default)(void 0!==h&&h,{value:m}),E=(0,r.default)(w,2),O=E[0],I=E[1];(0,d.useImperativeHandle)(c,function(){return{focus:function(e){var t;null==(t=k.current)||t.focus(e)},blur:function(){var e;null==(e=k.current)||e.blur()},input:k.current,nativeElement:x.current}});var z=(0,l.default)(p,b,(0,n.default)((0,n.default)({},"".concat(p,"-checked"),O),"".concat(p,"-disabled"),g));return d.createElement("span",{className:z,title:y,style:f,ref:x},d.createElement("input",(0,t.default)({},S,{className:"".concat(p,"-input"),ref:k,onChange:function(t){g||("checked"in e||I(t.target.checked),null==C||C({target:(0,o.default)((0,o.default)({},e),{},{type:$,checked:t.target.checked}),stopPropagation:function(){t.stopPropagation()},preventDefault:function(){t.preventDefault()},nativeEvent:t.nativeEvent}))},disabled:g,checked:!!O,type:$})),d.createElement("span",{className:"".concat(p,"-inner")}))});e.s(["default",0,c])},421512,236836,e=>{"use strict";let t=e.i(271645).default.createContext(null);e.s(["default",0,t],421512),e.i(296059);var o=e.i(915654),n=e.i(183293),r=e.i(246422),i=e.i(838378);function l(e,t){return(e=>{let{checkboxCls:t}=e,r=`${t}-wrapper`;return[{[`${t}-group`]:Object.assign(Object.assign({},(0,n.resetComponent)(e)),{display:"inline-flex",flexWrap:"wrap",columnGap:e.marginXS,[`> ${e.antCls}-row`]:{flex:1}}),[r]:Object.assign(Object.assign({},(0,n.resetComponent)(e)),{display:"inline-flex",alignItems:"baseline",cursor:"pointer","&:after":{display:"inline-block",width:0,overflow:"hidden",content:"'\\a0'"},[`& + ${r}`]:{marginInlineStart:0},[`&${r}-in-form-item`]:{'input[type="checkbox"]':{width:14,height:14}}}),[t]:Object.assign(Object.assign({},(0,n.resetComponent)(e)),{position:"relative",whiteSpace:"nowrap",lineHeight:1,cursor:"pointer",borderRadius:e.borderRadiusSM,alignSelf:"center",[`${t}-input`]:{position:"absolute",inset:0,zIndex:1,cursor:"pointer",opacity:0,margin:0,[`&:focus-visible + ${t}-inner`]:(0,n.genFocusOutline)(e)},[`${t}-inner`]:{boxSizing:"border-box",display:"block",width:e.checkboxSize,height:e.checkboxSize,direction:"ltr",backgroundColor:e.colorBgContainer,border:`${(0,o.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadiusSM,borderCollapse:"separate",transition:`all ${e.motionDurationSlow}`,"&:after":{boxSizing:"border-box",position:"absolute",top:"50%",insetInlineStart:"25%",display:"table",width:e.calc(e.checkboxSize).div(14).mul(5).equal(),height:e.calc(e.checkboxSize).div(14).mul(8).equal(),border:`${(0,o.unit)(e.lineWidthBold)} solid ${e.colorWhite}`,borderTop:0,borderInlineStart:0,transform:"rotate(45deg) scale(0) translate(-50%,-50%)",opacity:0,content:'""',transition:`all ${e.motionDurationFast} ${e.motionEaseInBack}, opacity ${e.motionDurationFast}`}},"& + span":{paddingInlineStart:e.paddingXS,paddingInlineEnd:e.paddingXS}})},{[` + ${r}:not(${r}-disabled), + ${t}:not(${t}-disabled) + `]:{[`&:hover ${t}-inner`]:{borderColor:e.colorPrimary}},[`${r}:not(${r}-disabled)`]:{[`&:hover ${t}-checked:not(${t}-disabled) ${t}-inner`]:{backgroundColor:e.colorPrimaryHover,borderColor:"transparent"},[`&:hover ${t}-checked:not(${t}-disabled):after`]:{borderColor:e.colorPrimaryHover}}},{[`${t}-checked`]:{[`${t}-inner`]:{backgroundColor:e.colorPrimary,borderColor:e.colorPrimary,"&:after":{opacity:1,transform:"rotate(45deg) scale(1) translate(-50%,-50%)",transition:`all ${e.motionDurationMid} ${e.motionEaseOutBack} ${e.motionDurationFast}`}}},[` + ${r}-checked:not(${r}-disabled), + ${t}-checked:not(${t}-disabled) + `]:{[`&:hover ${t}-inner`]:{backgroundColor:e.colorPrimaryHover,borderColor:"transparent"}}},{[t]:{"&-indeterminate":{"&":{[`${t}-inner`]:{backgroundColor:`${e.colorBgContainer}`,borderColor:`${e.colorBorder}`,"&:after":{top:"50%",insetInlineStart:"50%",width:e.calc(e.fontSizeLG).div(2).equal(),height:e.calc(e.fontSizeLG).div(2).equal(),backgroundColor:e.colorPrimary,border:0,transform:"translate(-50%, -50%) scale(1)",opacity:1,content:'""'}},[`&:hover ${t}-inner`]:{backgroundColor:`${e.colorBgContainer}`,borderColor:`${e.colorPrimary}`}}}}},{[`${r}-disabled`]:{cursor:"not-allowed"},[`${t}-disabled`]:{[`&, ${t}-input`]:{cursor:"not-allowed",pointerEvents:"none"},[`${t}-inner`]:{background:e.colorBgContainerDisabled,borderColor:e.colorBorder,"&:after":{borderColor:e.colorTextDisabled}},"&:after":{display:"none"},"& + span":{color:e.colorTextDisabled},[`&${t}-indeterminate ${t}-inner::after`]:{background:e.colorTextDisabled}}}]})((0,i.mergeToken)(t,{checkboxCls:`.${e}`,checkboxSize:t.controlInteractiveSize}))}let a=(0,r.genStyleHooks)("Checkbox",(e,{prefixCls:t})=>[l(t,e)]);e.s(["default",0,a,"getStyle",0,l],236836)},681216,e=>{"use strict";var t=e.i(271645),o=e.i(963188);e.s(["default",0,function(e){let n=t.default.useRef(null),r=()=>{o.default.cancel(n.current),n.current=null};return[()=>{r(),n.current=(0,o.default)(()=>{n.current=null})},t=>{n.current&&(t.stopPropagation(),r()),null==e||e(t)}]}])},374276,e=>{"use strict";e.i(247167);var t=e.i(271645),o=e.i(343794),n=e.i(91874),r=e.i(611935),i=e.i(121872),l=e.i(26905),a=e.i(242064),d=e.i(937328),s=e.i(321883),c=e.i(62139),u=e.i(421512),p=e.i(236836),b=e.i(681216),f=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,n=Object.getOwnPropertySymbols(e);rt.indexOf(n[r])&&Object.prototype.propertyIsEnumerable.call(e,n[r])&&(o[n[r]]=e[n[r]]);return o};let m=t.forwardRef((e,m)=>{var g;let{prefixCls:h,className:v,rootClassName:$,children:y,indeterminate:C=!1,style:S,onMouseEnter:k,onMouseLeave:x,skipGroup:w=!1,disabled:E}=e,O=f(e,["prefixCls","className","rootClassName","children","indeterminate","style","onMouseEnter","onMouseLeave","skipGroup","disabled"]),{getPrefixCls:I,direction:z,checkbox:j}=t.useContext(a.ConfigContext),N=t.useContext(u.default),{isFormItemInput:R}=t.useContext(c.FormItemInputContext),D=t.useContext(d.default),B=null!=(g=(null==N?void 0:N.disabled)||E)?g:D,P=t.useRef(O.value),q=t.useRef(null),T=(0,r.composeRef)(m,q);t.useEffect(()=>{null==N||N.registerValue(O.value)},[]),t.useEffect(()=>{if(!w)return O.value!==P.current&&(null==N||N.cancelValue(P.current),null==N||N.registerValue(O.value),P.current=O.value),()=>null==N?void 0:N.cancelValue(O.value)},[O.value]),t.useEffect(()=>{var e;(null==(e=q.current)?void 0:e.input)&&(q.current.input.indeterminate=C)},[C]);let M=I("checkbox",h),L=(0,s.default)(M),[H,X,F]=(0,p.default)(M,L),G=Object.assign({},O);N&&!w&&(G.onChange=(...e)=>{O.onChange&&O.onChange.apply(O,e),N.toggleOption&&N.toggleOption({label:y,value:O.value})},G.name=N.name,G.checked=N.value.includes(O.value));let A=(0,o.default)(`${M}-wrapper`,{[`${M}-rtl`]:"rtl"===z,[`${M}-wrapper-checked`]:G.checked,[`${M}-wrapper-disabled`]:B,[`${M}-wrapper-in-form-item`]:R},null==j?void 0:j.className,v,$,F,L,X),W=(0,o.default)({[`${M}-indeterminate`]:C},l.TARGET_CLS,X),[_,V]=(0,b.default)(G.onClick);return H(t.createElement(i.default,{component:"Checkbox",disabled:B},t.createElement("label",{className:A,style:Object.assign(Object.assign({},null==j?void 0:j.style),S),onMouseEnter:k,onMouseLeave:x,onClick:_},t.createElement(n.default,Object.assign({},G,{onClick:V,prefixCls:M,className:W,disabled:B,ref:T})),null!=y&&t.createElement("span",{className:`${M}-label`},y))))});var g=e.i(8211),h=e.i(529681),v=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,n=Object.getOwnPropertySymbols(e);rt.indexOf(n[r])&&Object.prototype.propertyIsEnumerable.call(e,n[r])&&(o[n[r]]=e[n[r]]);return o};let $=t.forwardRef((e,n)=>{let{defaultValue:r,children:i,options:l=[],prefixCls:d,className:c,rootClassName:b,style:f,onChange:$}=e,y=v(e,["defaultValue","children","options","prefixCls","className","rootClassName","style","onChange"]),{getPrefixCls:C,direction:S}=t.useContext(a.ConfigContext),[k,x]=t.useState(y.value||r||[]),[w,E]=t.useState([]);t.useEffect(()=>{"value"in y&&x(y.value||[])},[y.value]);let O=t.useMemo(()=>l.map(e=>"string"==typeof e||"number"==typeof e?{label:e,value:e}:e),[l]),I=e=>{E(t=>t.filter(t=>t!==e))},z=e=>{E(t=>[].concat((0,g.default)(t),[e]))},j=e=>{let t=k.indexOf(e.value),o=(0,g.default)(k);-1===t?o.push(e.value):o.splice(t,1),"value"in y||x(o),null==$||$(o.filter(e=>w.includes(e)).sort((e,t)=>O.findIndex(t=>t.value===e)-O.findIndex(e=>e.value===t)))},N=C("checkbox",d),R=`${N}-group`,D=(0,s.default)(N),[B,P,q]=(0,p.default)(N,D),T=(0,h.default)(y,["value","disabled"]),M=l.length?O.map(e=>t.createElement(m,{prefixCls:N,key:e.value.toString(),disabled:"disabled"in e?e.disabled:y.disabled,value:e.value,checked:k.includes(e.value),onChange:e.onChange,className:(0,o.default)(`${R}-item`,e.className),style:e.style,title:e.title,id:e.id,required:e.required},e.label)):i,L=t.useMemo(()=>({toggleOption:j,value:k,disabled:y.disabled,name:y.name,registerValue:z,cancelValue:I}),[j,k,y.disabled,y.name,z,I]),H=(0,o.default)(R,{[`${R}-rtl`]:"rtl"===S},c,b,q,D,P);return B(t.createElement("div",Object.assign({className:H,style:f},T,{ref:n}),t.createElement(u.default.Provider,{value:L},M)))});m.Group=$,m.__ANT_CHECKBOX=!0,e.s(["default",0,m],374276)},544195,e=>{"use strict";var t=e.i(271645),o=e.i(343794),n=e.i(981444),r=e.i(914949),i=e.i(244009),l=e.i(242064),a=e.i(321883),d=e.i(517455);let s=t.createContext(null),c=s.Provider,u=t.createContext(null),p=u.Provider;e.i(247167);var b=e.i(91874),f=e.i(611935),m=e.i(121872),g=e.i(26905),h=e.i(681216),v=e.i(937328),$=e.i(62139);e.i(296059);var y=e.i(915654),C=e.i(183293),S=e.i(246422),k=e.i(838378);let x=(0,S.genStyleHooks)("Radio",e=>{let{controlOutline:t,controlOutlineWidth:o}=e,n=`0 0 0 ${(0,y.unit)(o)} ${t}`,r=(0,k.mergeToken)(e,{radioFocusShadow:n,radioButtonFocusShadow:n});return[(e=>{let{componentCls:t,antCls:o}=e,n=`${t}-group`;return{[n]:Object.assign(Object.assign({},(0,C.resetComponent)(e)),{display:"inline-block",fontSize:0,[`&${n}-rtl`]:{direction:"rtl"},[`&${n}-block`]:{display:"flex"},[`${o}-badge ${o}-badge-count`]:{zIndex:1},[`> ${o}-badge:not(:first-child) > ${o}-button-wrapper`]:{borderInlineStart:"none"}})}})(r),(e=>{let{componentCls:t,wrapperMarginInlineEnd:o,colorPrimary:n,radioSize:r,motionDurationSlow:i,motionDurationMid:l,motionEaseInOutCirc:a,colorBgContainer:d,colorBorder:s,lineWidth:c,colorBgContainerDisabled:u,colorTextDisabled:p,paddingXS:b,dotColorDisabled:f,lineType:m,radioColor:g,radioBgColor:h,calc:v}=e,$=`${t}-inner`,S=v(r).sub(v(4).mul(2)),k=v(1).mul(r).equal({unit:!0});return{[`${t}-wrapper`]:Object.assign(Object.assign({},(0,C.resetComponent)(e)),{display:"inline-flex",alignItems:"baseline",marginInlineStart:0,marginInlineEnd:o,cursor:"pointer","&:last-child":{marginInlineEnd:0},[`&${t}-wrapper-rtl`]:{direction:"rtl"},"&-disabled":{cursor:"not-allowed",color:e.colorTextDisabled},"&::after":{display:"inline-block",width:0,overflow:"hidden",content:'"\\a0"'},"&-block":{flex:1,justifyContent:"center"},[`${t}-checked::after`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,width:"100%",height:"100%",border:`${(0,y.unit)(c)} ${m} ${n}`,borderRadius:"50%",visibility:"hidden",opacity:0,content:'""'},[t]:Object.assign(Object.assign({},(0,C.resetComponent)(e)),{position:"relative",display:"inline-block",outline:"none",cursor:"pointer",alignSelf:"center",borderRadius:"50%"}),[`${t}-wrapper:hover &, + &:hover ${$}`]:{borderColor:n},[`${t}-input:focus-visible + ${$}`]:(0,C.genFocusOutline)(e),[`${t}:hover::after, ${t}-wrapper:hover &::after`]:{visibility:"visible"},[`${t}-inner`]:{"&::after":{boxSizing:"border-box",position:"absolute",insetBlockStart:"50%",insetInlineStart:"50%",display:"block",width:k,height:k,marginBlockStart:v(1).mul(r).div(-2).equal({unit:!0}),marginInlineStart:v(1).mul(r).div(-2).equal({unit:!0}),backgroundColor:g,borderBlockStart:0,borderInlineStart:0,borderRadius:k,transform:"scale(0)",opacity:0,transition:`all ${i} ${a}`,content:'""'},boxSizing:"border-box",position:"relative",insetBlockStart:0,insetInlineStart:0,display:"block",width:k,height:k,backgroundColor:d,borderColor:s,borderStyle:"solid",borderWidth:c,borderRadius:"50%",transition:`all ${l}`},[`${t}-input`]:{position:"absolute",inset:0,zIndex:1,cursor:"pointer",opacity:0},[`${t}-checked`]:{[$]:{borderColor:n,backgroundColor:h,"&::after":{transform:`scale(${e.calc(e.dotSize).div(r).equal()})`,opacity:1,transition:`all ${i} ${a}`}}},[`${t}-disabled`]:{cursor:"not-allowed",[$]:{backgroundColor:u,borderColor:s,cursor:"not-allowed","&::after":{backgroundColor:f}},[`${t}-input`]:{cursor:"not-allowed"},[`${t}-disabled + span`]:{color:p,cursor:"not-allowed"},[`&${t}-checked`]:{[$]:{"&::after":{transform:`scale(${v(S).div(r).equal()})`}}}},[`span${t} + *`]:{paddingInlineStart:b,paddingInlineEnd:b}})}})(r),(e=>{let{buttonColor:t,controlHeight:o,componentCls:n,lineWidth:r,lineType:i,colorBorder:l,motionDurationMid:a,buttonPaddingInline:d,fontSize:s,buttonBg:c,fontSizeLG:u,controlHeightLG:p,controlHeightSM:b,paddingXS:f,borderRadius:m,borderRadiusSM:g,borderRadiusLG:h,buttonCheckedBg:v,buttonSolidCheckedColor:$,colorTextDisabled:S,colorBgContainerDisabled:k,buttonCheckedBgDisabled:x,buttonCheckedColorDisabled:w,colorPrimary:E,colorPrimaryHover:O,colorPrimaryActive:I,buttonSolidCheckedBg:z,buttonSolidCheckedHoverBg:j,buttonSolidCheckedActiveBg:N,calc:R}=e;return{[`${n}-button-wrapper`]:{position:"relative",display:"inline-block",height:o,margin:0,paddingInline:d,paddingBlock:0,color:t,fontSize:s,lineHeight:(0,y.unit)(R(o).sub(R(r).mul(2)).equal()),background:c,border:`${(0,y.unit)(r)} ${i} ${l}`,borderBlockStartWidth:R(r).add(.02).equal(),borderInlineEndWidth:r,cursor:"pointer",transition:`color ${a},background ${a},box-shadow ${a}`,a:{color:t},[`> ${n}-button`]:{position:"absolute",insetBlockStart:0,insetInlineStart:0,zIndex:-1,width:"100%",height:"100%"},"&:not(:last-child)":{marginInlineEnd:R(r).mul(-1).equal()},"&:first-child":{borderInlineStart:`${(0,y.unit)(r)} ${i} ${l}`,borderStartStartRadius:m,borderEndStartRadius:m},"&:last-child":{borderStartEndRadius:m,borderEndEndRadius:m},"&:first-child:last-child":{borderRadius:m},[`${n}-group-large &`]:{height:p,fontSize:u,lineHeight:(0,y.unit)(R(p).sub(R(r).mul(2)).equal()),"&:first-child":{borderStartStartRadius:h,borderEndStartRadius:h},"&:last-child":{borderStartEndRadius:h,borderEndEndRadius:h}},[`${n}-group-small &`]:{height:b,paddingInline:R(f).sub(r).equal(),paddingBlock:0,lineHeight:(0,y.unit)(R(b).sub(R(r).mul(2)).equal()),"&:first-child":{borderStartStartRadius:g,borderEndStartRadius:g},"&:last-child":{borderStartEndRadius:g,borderEndEndRadius:g}},"&:hover":{position:"relative",color:E},"&:has(:focus-visible)":(0,C.genFocusOutline)(e),[`${n}-inner, input[type='checkbox'], input[type='radio']`]:{width:0,height:0,opacity:0,pointerEvents:"none"},[`&-checked:not(${n}-button-wrapper-disabled)`]:{zIndex:1,color:E,background:v,borderColor:E,"&::before":{backgroundColor:E},"&:first-child":{borderColor:E},"&:hover":{color:O,borderColor:O,"&::before":{backgroundColor:O}},"&:active":{color:I,borderColor:I,"&::before":{backgroundColor:I}}},[`${n}-group-solid &-checked:not(${n}-button-wrapper-disabled)`]:{color:$,background:z,borderColor:z,"&:hover":{color:$,background:j,borderColor:j},"&:active":{color:$,background:N,borderColor:N}},"&-disabled":{color:S,backgroundColor:k,borderColor:l,cursor:"not-allowed","&:first-child, &:hover":{color:S,backgroundColor:k,borderColor:l}},[`&-disabled${n}-button-wrapper-checked`]:{color:w,backgroundColor:x,borderColor:l,boxShadow:"none"},"&-block":{flex:1,textAlign:"center"}}}})(r)]},e=>{let{wireframe:t,padding:o,marginXS:n,lineWidth:r,fontSizeLG:i,colorText:l,colorBgContainer:a,colorTextDisabled:d,controlItemBgActiveDisabled:s,colorTextLightSolid:c,colorPrimary:u,colorPrimaryHover:p,colorPrimaryActive:b,colorWhite:f}=e;return{radioSize:i,dotSize:t?i-8:i-(4+r)*2,dotColorDisabled:d,buttonSolidCheckedColor:c,buttonSolidCheckedBg:u,buttonSolidCheckedHoverBg:p,buttonSolidCheckedActiveBg:b,buttonBg:a,buttonCheckedBg:a,buttonColor:l,buttonCheckedBgDisabled:s,buttonCheckedColorDisabled:d,buttonPaddingInline:o-r,wrapperMarginInlineEnd:n,radioColor:t?u:f,radioBgColor:t?a:u}},{unitless:{radioSize:!0,dotSize:!0}});var w=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,n=Object.getOwnPropertySymbols(e);rt.indexOf(n[r])&&Object.prototype.propertyIsEnumerable.call(e,n[r])&&(o[n[r]]=e[n[r]]);return o};let E=t.forwardRef((e,n)=>{var r,i;let d=t.useContext(s),c=t.useContext(u),{getPrefixCls:p,direction:y,radio:C}=t.useContext(l.ConfigContext),S=t.useRef(null),k=(0,f.composeRef)(n,S),{isFormItemInput:E}=t.useContext($.FormItemInputContext),{prefixCls:O,className:I,rootClassName:z,children:j,style:N,title:R}=e,D=w(e,["prefixCls","className","rootClassName","children","style","title"]),B=p("radio",O),P="button"===((null==d?void 0:d.optionType)||c),q=P?`${B}-button`:B,T=(0,a.default)(B),[M,L,H]=x(B,T),X=Object.assign({},D),F=t.useContext(v.default);d&&(X.name=d.name,X.onChange=t=>{var o,n;null==(o=e.onChange)||o.call(e,t),null==(n=null==d?void 0:d.onChange)||n.call(d,t)},X.checked=e.value===d.value,X.disabled=null!=(r=X.disabled)?r:d.disabled),X.disabled=null!=(i=X.disabled)?i:F;let G=(0,o.default)(`${q}-wrapper`,{[`${q}-wrapper-checked`]:X.checked,[`${q}-wrapper-disabled`]:X.disabled,[`${q}-wrapper-rtl`]:"rtl"===y,[`${q}-wrapper-in-form-item`]:E,[`${q}-wrapper-block`]:!!(null==d?void 0:d.block)},null==C?void 0:C.className,I,z,L,H,T),[A,W]=(0,h.default)(X.onClick);return M(t.createElement(m.default,{component:"Radio",disabled:X.disabled},t.createElement("label",{className:G,style:Object.assign(Object.assign({},null==C?void 0:C.style),N),onMouseEnter:e.onMouseEnter,onMouseLeave:e.onMouseLeave,title:R,onClick:A},t.createElement(b.default,Object.assign({},X,{className:(0,o.default)(X.className,{[g.TARGET_CLS]:!P}),type:"radio",prefixCls:q,ref:k,onClick:W})),void 0!==j?t.createElement("span",{className:`${q}-label`},j):null)))});var O=e.i(286039);let I=t.forwardRef((e,s)=>{let{getPrefixCls:u,direction:p}=t.useContext(l.ConfigContext),{name:b}=t.useContext($.FormItemInputContext),f=(0,n.default)((0,O.toNamePathStr)(b)),{prefixCls:m,className:g,rootClassName:h,options:v,buttonStyle:y="outline",disabled:C,children:S,size:k,style:w,id:I,optionType:z,name:j=f,defaultValue:N,value:R,block:D=!1,onChange:B,onMouseEnter:P,onMouseLeave:q,onFocus:T,onBlur:M}=e,[L,H]=(0,r.default)(N,{value:R}),X=t.useCallback(t=>{let o=t.target.value;"value"in e||H(o),o!==L&&(null==B||B(t))},[L,H,B]),F=u("radio",m),G=`${F}-group`,A=(0,a.default)(F),[W,_,V]=x(F,A),K=S;v&&v.length>0&&(K=v.map(e=>"string"==typeof e||"number"==typeof e?t.createElement(E,{key:e.toString(),prefixCls:F,disabled:C,value:e,checked:L===e},e):t.createElement(E,{key:`radio-group-value-options-${e.value}`,prefixCls:F,disabled:e.disabled||C,value:e.value,checked:L===e.value,title:e.title,style:e.style,className:e.className,id:e.id,required:e.required},e.label)));let U=(0,d.default)(k),J=(0,o.default)(G,`${G}-${y}`,{[`${G}-${U}`]:U,[`${G}-rtl`]:"rtl"===p,[`${G}-block`]:D},g,h,_,V,A),Q=t.useMemo(()=>({onChange:X,value:L,disabled:C,name:j,optionType:z,block:D}),[X,L,C,j,z,D]);return W(t.createElement("div",Object.assign({},(0,i.default)(e,{aria:!0,data:!0}),{className:J,style:w,onMouseEnter:P,onMouseLeave:q,onFocus:T,onBlur:M,id:I,ref:s}),t.createElement(c,{value:Q},K)))}),z=t.memo(I);var j=function(e,t){var o={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(o[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,n=Object.getOwnPropertySymbols(e);rt.indexOf(n[r])&&Object.prototype.propertyIsEnumerable.call(e,n[r])&&(o[n[r]]=e[n[r]]);return o};let N=t.forwardRef((e,o)=>{let{getPrefixCls:n}=t.useContext(l.ConfigContext),{prefixCls:r}=e,i=j(e,["prefixCls"]),a=n("radio",r);return t.createElement(p,{value:"button"},t.createElement(E,Object.assign({prefixCls:a},i,{type:"radio",ref:o})))});E.Button=N,E.Group=z,E.__ANT_RADIO=!0,e.s(["default",0,E],544195)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/17y3_yqikcnb1.js b/litellm/proxy/_experimental/out/_next/static/chunks/17y3_yqikcnb1.js new file mode 100644 index 00000000000..7a86351b8b1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/17y3_yqikcnb1.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,94629,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"}))});e.s(["SwitchVerticalIcon",0,a],94629)},434626,e=>{"use strict";var t=e.i(271645);let a=t.forwardRef(function(e,a){return t.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:a},e),t.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}))});e.s(["ExternalLinkIcon",0,a],434626)},166406,e=>{"use strict";var t=e.i(190144);e.s(["CopyOutlined",()=>t.default])},492030,e=>{"use strict";var t=e.i(121229);e.s(["CheckOutlined",()=>t.default])},447566,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 000 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"}}]},name:"arrow-left",theme:"outlined"};var o=e.i(9583),i=a.forwardRef(function(e,i){return a.createElement(o.default,(0,t.default)({},e,{ref:i,icon:r}))});e.s(["ArrowLeftOutlined",0,i],447566)},292639,e=>{"use strict";var t=e.i(602869),a=e.i(266027);let r=(0,e.i(243652).createQueryKeys)("uiSettings");e.s(["useUISettings",0,()=>(0,a.useQuery)({queryKey:r.list({}),queryFn:async()=>await (0,t.getUiSettings)(),staleTime:36e5,gcTime:36e5})])},829672,836938,310730,e=>{"use strict";e.i(247167);var t=e.i(271645),a=e.i(343794),r=e.i(914949),o=e.i(404948);let i=e=>e?"function"==typeof e?e():e:null;e.s(["getRenderPropValue",0,i],836938);var n=e.i(613541),l=e.i(763731),s=e.i(242064),c=e.i(491816);e.i(793154);var p=e.i(880476),u=e.i(183293),A=e.i(717356),g=e.i(320560),d=e.i(307358),m=e.i(246422),v=e.i(838378),f=e.i(617933);let I=(0,m.genStyleHooks)("Popover",e=>{let{colorBgElevated:t,colorText:a}=e,r=(0,v.mergeToken)(e,{popoverBg:t,popoverColor:a});return[(e=>{let{componentCls:t,popoverColor:a,titleMinWidth:r,fontWeightStrong:o,innerPadding:i,boxShadowSecondary:n,colorTextHeading:l,borderRadiusLG:s,zIndexPopup:c,titleMarginBottom:p,colorBgElevated:A,popoverBg:d,titleBorderBottom:m,innerContentPadding:v,titlePadding:f}=e;return[{[t]:Object.assign(Object.assign({},(0,u.resetComponent)(e)),{position:"absolute",top:0,left:{_skip_check_:!0,value:0},zIndex:c,fontWeight:"normal",whiteSpace:"normal",textAlign:"start",cursor:"auto",userSelect:"text","--valid-offset-x":"var(--arrow-offset-horizontal, var(--arrow-x))",transformOrigin:"var(--valid-offset-x, 50%) var(--arrow-y, 50%)","--antd-arrow-background-color":A,width:"max-content",maxWidth:"100vw","&-rtl":{direction:"rtl"},"&-hidden":{display:"none"},[`${t}-content`]:{position:"relative"},[`${t}-inner`]:{backgroundColor:d,backgroundClip:"padding-box",borderRadius:s,boxShadow:n,padding:i},[`${t}-title`]:{minWidth:r,marginBottom:p,color:l,fontWeight:o,borderBottom:m,padding:f},[`${t}-inner-content`]:{color:a,padding:v}})},(0,g.default)(e,"var(--antd-arrow-background-color)"),{[`${t}-pure`]:{position:"relative",maxWidth:"none",margin:e.sizePopupArrow,display:"inline-block",[`${t}-content`]:{display:"inline-block"}}}]})(r),(e=>{let{componentCls:t}=e;return{[t]:f.PresetColors.map(a=>{let r=e[`${a}6`];return{[`&${t}-${a}`]:{"--antd-arrow-background-color":r,[`${t}-inner`]:{backgroundColor:r},[`${t}-arrow`]:{background:"transparent"}}}})}})(r),(0,A.initZoomMotion)(r,"zoom-big")]},e=>{let{lineWidth:t,controlHeight:a,fontHeight:r,padding:o,wireframe:i,zIndexPopupBase:n,borderRadiusLG:l,marginXS:s,lineType:c,colorSplit:p,paddingSM:u}=e,A=a-r;return Object.assign(Object.assign(Object.assign({titleMinWidth:177,zIndexPopup:n+30},(0,d.getArrowToken)(e)),(0,g.getArrowOffsetToken)({contentRadius:l,limitVerticalRadius:!0})),{innerPadding:12*!i,titleMarginBottom:i?0:s,titlePadding:i?`${A/2}px ${o}px ${A/2-t}px`:0,titleBorderBottom:i?`${t}px ${c} ${p}`:"none",innerContentPadding:i?`${u}px ${o}px`:0})},{resetStyle:!1,deprecatedTokens:[["width","titleMinWidth"],["minWidth","titleMinWidth"]]});var O=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,r=Object.getOwnPropertySymbols(e);ot.indexOf(r[o])&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(a[r[o]]=e[r[o]]);return a};let b=({title:e,content:a,prefixCls:r})=>e||a?t.createElement(t.Fragment,null,e&&t.createElement("div",{className:`${r}-title`},e),a&&t.createElement("div",{className:`${r}-inner-content`},a)):null,C=e=>{let{hashId:r,prefixCls:o,className:n,style:l,placement:s="top",title:c,content:u,children:A}=e,g=i(c),d=i(u),m=(0,a.default)(r,o,`${o}-pure`,`${o}-placement-${s}`,n);return t.createElement("div",{className:m,style:l},t.createElement("div",{className:`${o}-arrow`}),t.createElement(p.Popup,Object.assign({},e,{className:r,prefixCls:o}),A||t.createElement(b,{prefixCls:o,title:g,content:d})))},E=e=>{let{prefixCls:r,className:o}=e,i=O(e,["prefixCls","className"]),{getPrefixCls:n}=t.useContext(s.ConfigContext),l=n("popover",r),[c,p,u]=I(l);return c(t.createElement(C,Object.assign({},i,{prefixCls:l,hashId:p,className:(0,a.default)(o,u)})))};e.s(["Overlay",0,b,"default",0,E],310730);var h=function(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(a[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,r=Object.getOwnPropertySymbols(e);ot.indexOf(r[o])&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(a[r[o]]=e[r[o]]);return a};let _=t.forwardRef((e,p)=>{var u,A;let{prefixCls:g,title:d,content:m,overlayClassName:v,placement:f="top",trigger:O="hover",children:C,mouseEnterDelay:E=.1,mouseLeaveDelay:_=.1,onOpenChange:T,overlayStyle:L={},styles:y,classNames:x}=e,$=h(e,["prefixCls","title","content","overlayClassName","placement","trigger","children","mouseEnterDelay","mouseLeaveDelay","onOpenChange","overlayStyle","styles","classNames"]),{getPrefixCls:S,className:k,style:M,classNames:w,styles:R}=(0,s.useComponentConfig)("popover"),P=S("popover",g),[N,D,B]=I(P),V=S(),j=(0,a.default)(v,D,B,k,w.root,null==x?void 0:x.root),G=(0,a.default)(w.body,null==x?void 0:x.body),[z,H]=(0,r.default)(!1,{value:null!=(u=e.open)?u:e.visible,defaultValue:null!=(A=e.defaultOpen)?A:e.defaultVisible}),F=(e,t)=>{H(e,!0),null==T||T(e,t)},W=i(d),U=i(m);return N(t.createElement(c.default,Object.assign({placement:f,trigger:O,mouseEnterDelay:E,mouseLeaveDelay:_},$,{prefixCls:P,classNames:{root:j,body:G},styles:{root:Object.assign(Object.assign(Object.assign(Object.assign({},R.root),M),L),null==y?void 0:y.root),body:Object.assign(Object.assign({},R.body),null==y?void 0:y.body)},ref:p,open:z,onOpenChange:e=>{F(e)},overlay:W||U?t.createElement(b,{prefixCls:P,title:W,content:U}):null,transitionName:(0,n.getTransitionName)(V,"zoom-big",$.transitionName),"data-popover-inject":!0}),(0,l.cloneElement)(C,{onKeyDown:e=>{var a,r;(0,t.isValidElement)(C)&&(null==(r=null==C?void 0:(a=C.props).onKeyDown)||r.call(a,e)),e.keyCode===o.default.ESC&&F(!1,e)}})))});_._InternalPanelDoNotUseOrYouWillBeFired=E,e.s(["default",0,_],829672)},282786,e=>{"use strict";var t=e.i(829672);e.s(["Popover",()=>t.default])},771674,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let r={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"}}]},name:"user",theme:"outlined"};var o=e.i(9583),i=a.forwardRef(function(e,i){return a.createElement(o.default,(0,t.default)({},e,{ref:i,icon:r}))});e.s(["UserOutlined",0,i],771674)},755151,e=>{"use strict";var t=e.i(247153);e.s(["DownOutlined",()=>t.default])},916925,e=>{"use strict";var t,a=e.i(555987),r=((t={}).A2A_Agent="A2A Agent",t.AI21="Ai21",t.AI21_CHAT="Ai21 Chat",t.AIML="AI/ML API",t.AIOHTTP_OPENAI="Aiohttp Openai",t.Anthropic="Anthropic",t.ANTHROPIC_TEXT="Anthropic Text",t.AssemblyAI="AssemblyAI",t.AUTO_ROUTER="Auto Router",t.Bedrock="Amazon Bedrock",t.BedrockMantle="Amazon Bedrock Mantle",t.SageMaker="AWS SageMaker",t.Azure="Azure",t.Azure_AI_Studio="Azure AI Foundry (Studio)",t.AZURE_TEXT="Azure Text",t.BASETEN="Baseten",t.BYTEZ="Bytez",t.Cerebras="Cerebras",t.CLARIFAI="Clarifai",t.CLOUDFLARE="Cloudflare",t.CODESTRAL="Codestral",t.Cohere="Cohere",t.COHERE_CHAT="Cohere Chat",t.COMETAPI="Cometapi",t.COMPACTIFAI="Compactifai",t.Cursor="Cursor",t.Dashscope="Dashscope",t.Databricks="Databricks (Qwen API)",t.DATAROBOT="Datarobot",t.DeepInfra="DeepInfra",t.Deepgram="Deepgram",t.Deepseek="Deepseek",t.DOCKER_MODEL_RUNNER="Docker Model Runner",t.DOTPROMPT="Dotprompt",t.ElevenLabs="ElevenLabs",t.EMPOWER="Empower",t.FalAI="Fal AI",t.FEATHERLESS_AI="Featherless Ai",t.FireworksAI="Fireworks AI",t.FRIENDLIAI="Friendliai",t.GALADRIEL="Galadriel",t.GITHUB_COPILOT="Github Copilot",t.Google_AI_Studio="Google AI Studio",t.GradientAI="GradientAI",t.Groq="Groq",t.HEROKU="Heroku",t.Hosted_Vllm="vllm",t.HUGGINGFACE="Huggingface",t.HYPERBOLIC="Hyperbolic",t.Infinity="Infinity",t.JinaAI="Jina AI",t.LAMBDA_AI="Lambda Ai",t.LEMONADE="Lemonade",t.LLAMAFILE="Llamafile",t.LM_STUDIO="Lm Studio",t.LLAMA="Meta Llama",t.MARITALK="Maritalk",t.MiniMax="MiniMax",t.MistralAI="Mistral AI",t.MOONSHOT="Moonshot",t.MORPH="Morph",t.NEBIUS="Nebius",t.NLP_CLOUD="Nlp Cloud",t.NOVITA="Novita",t.NSCALE="Nscale",t.NVIDIA_NIM="Nvidia Nim",t.Ollama="Ollama",t.OLLAMA_CHAT="Ollama Chat",t.OOBABOOGA="Oobabooga",t.OpenAI="OpenAI",t.OPENAI_LIKE="Openai Like",t.OpenAI_Compatible="OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)",t.OpenAI_Text="OpenAI Text Completion",t.OpenAI_Text_Compatible="OpenAI-Compatible Completions (legacy /v1/completions)",t.Openrouter="Openrouter",t.Oracle="Oracle Cloud Infrastructure (OCI)",t.OVHCLOUD="Ovhcloud",t.Perplexity="Perplexity",t.PETALS="Petals",t.PG_VECTOR="Pg Vector",t.PREDIBASE="Predibase",t.RECRAFT="Recraft",t.REPLICATE="Replicate",t.RunwayML="RunwayML",t.SAGEMAKER_LEGACY="Sagemaker",t.Sambanova="Sambanova",t.SAP="SAP Generative AI Hub",t.Snowflake="Snowflake",t.Soniox="Soniox",t.TEXT_COMPLETION_CODESTRAL="Text-Completion-Codestral",t.TogetherAI="TogetherAI",t.TOPAZ="Topaz",t.Triton="Triton",t.V0="V0",t.VERCEL_AI_GATEWAY="Vercel Ai Gateway",t.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",t.VERTEX_AI_BETA="Vertex Ai Beta",t.VLLM="Vllm",t.VolcEngine="VolcEngine",t.Voyage="Voyage AI",t.WANDB="Wandb",t.WATSONX="Watsonx",t.WATSONX_TEXT="Watsonx Text",t.xAI="xAI",t.XINFERENCE="Xinference",t.ZAI="Z.AI (Zhipu AI)",t);let o={A2A_Agent:"a2a_agent",AI21:"ai21",AI21_CHAT:"ai21_chat",AIML:"aiml",AIOHTTP_OPENAI:"aiohttp_openai",Anthropic:"anthropic",ANTHROPIC_TEXT:"anthropic_text",AssemblyAI:"assemblyai",AUTO_ROUTER:"auto_router",Azure:"azure",Azure_AI_Studio:"azure_ai",AZURE_TEXT:"azure_text",BASETEN:"baseten",Bedrock:"bedrock",BedrockMantle:"bedrock_mantle",BYTEZ:"bytez",Cerebras:"cerebras",CLARIFAI:"clarifai",CLOUDFLARE:"cloudflare",CODESTRAL:"codestral",Cohere:"cohere",COHERE_CHAT:"cohere_chat",COMETAPI:"cometapi",COMPACTIFAI:"compactifai",Cursor:"cursor",Dashscope:"dashscope",Databricks:"databricks",DATAROBOT:"datarobot",DeepInfra:"deepinfra",Deepgram:"deepgram",Deepseek:"deepseek",DOCKER_MODEL_RUNNER:"docker_model_runner",DOTPROMPT:"dotprompt",ElevenLabs:"elevenlabs",EMPOWER:"empower",FalAI:"fal_ai",FEATHERLESS_AI:"featherless_ai",FireworksAI:"fireworks_ai",FRIENDLIAI:"friendliai",GALADRIEL:"galadriel",GITHUB_COPILOT:"github_copilot",Google_AI_Studio:"gemini",GradientAI:"gradient_ai",Groq:"groq",HEROKU:"heroku",Hosted_Vllm:"hosted_vllm",HUGGINGFACE:"huggingface",HYPERBOLIC:"hyperbolic",Infinity:"infinity",JinaAI:"jina_ai",LAMBDA_AI:"lambda_ai",LEMONADE:"lemonade",LLAMAFILE:"llamafile",LLAMA:"meta_llama",LM_STUDIO:"lm_studio",MARITALK:"maritalk",MiniMax:"minimax",MistralAI:"mistral",MOONSHOT:"moonshot",MORPH:"morph",NEBIUS:"nebius",NLP_CLOUD:"nlp_cloud",NOVITA:"novita",NSCALE:"nscale",NVIDIA_NIM:"nvidia_nim",Ollama:"ollama",OLLAMA_CHAT:"ollama_chat",OOBABOOGA:"oobabooga",OpenAI:"openai",OPENAI_LIKE:"openai_like",OpenAI_Compatible:"openai",OpenAI_Text:"text-completion-openai",OpenAI_Text_Compatible:"text-completion-openai",Openrouter:"openrouter",Oracle:"oci",OVHCLOUD:"ovhcloud",Perplexity:"perplexity",PETALS:"petals",PG_VECTOR:"pg_vector",PREDIBASE:"predibase",RECRAFT:"recraft",REPLICATE:"replicate",RunwayML:"runwayml",SAGEMAKER_LEGACY:"sagemaker",SageMaker:"sagemaker_chat",Sambanova:"sambanova",SAP:"sap",Snowflake:"snowflake",Soniox:"soniox",TEXT_COMPLETION_CODESTRAL:"text-completion-codestral",TogetherAI:"together_ai",TOPAZ:"topaz",Triton:"triton",V0:"v0",VERCEL_AI_GATEWAY:"vercel_ai_gateway",Vertex_AI:"vertex_ai",VERTEX_AI_BETA:"vertex_ai_beta",VLLM:"vllm",VolcEngine:"volcengine",Voyage:"voyage",WANDB:"wandb",WATSONX:"watsonx",WATSONX_TEXT:"watsonx_text",xAI:"xai",XINFERENCE:"xinference",ZAI:"zai"},i=new Set(["bedrock_mantle"]),n="/ui/assets/logos/",l={"A2A Agent":`${n}a2a_agent.png`,Ai21:`${n}ai21.svg`,"Ai21 Chat":`${n}ai21.svg`,"AI/ML API":`${n}aiml_api.svg`,"Aiohttp Openai":`${n}openai_small.svg`,Anthropic:`${n}anthropic.svg`,"Anthropic Text":`${n}anthropic.svg`,AssemblyAI:`${n}assemblyai_small.png`,Azure:`${n}microsoft_azure.svg`,"Azure AI Foundry (Studio)":`${n}microsoft_azure.svg`,"Azure Text":`${n}microsoft_azure.svg`,Baseten:`${n}baseten.svg`,"Amazon Bedrock":`${n}bedrock.svg`,"Amazon Bedrock Mantle":`${n}bedrock.svg`,"AWS SageMaker":`${n}bedrock.svg`,Cerebras:`${n}cerebras.svg`,Cloudflare:`${n}cloudflare.svg`,Codestral:`${n}mistral.svg`,Cohere:`${n}cohere.svg`,"Cohere Chat":`${n}cohere.svg`,Cometapi:`${n}cometapi.svg`,Cursor:`${n}cursor.svg`,"Databricks (Qwen API)":`${n}databricks.svg`,Dashscope:`${n}dashscope.svg`,Deepseek:`${n}deepseek.svg`,Deepgram:`${n}deepgram.png`,DeepInfra:`${n}deepinfra.png`,ElevenLabs:`${n}elevenlabs.png`,"Fal AI":`${n}fal_ai.jpg`,"Featherless Ai":`${n}featherless.svg`,"Fireworks AI":`${n}fireworks.svg`,Friendliai:`${n}friendli.svg`,"Github Copilot":`${n}github_copilot.svg`,"Google AI Studio":`${n}google.svg`,GradientAI:`${n}gradientai.svg`,Groq:`${n}groq.svg`,vllm:`${n}vllm.png`,Huggingface:`${n}huggingface.svg`,Hyperbolic:`${n}hyperbolic.svg`,Infinity:`${n}infinity.png`,"Jina AI":`${n}jina.png`,"Lambda Ai":`${n}lambda.svg`,"Lm Studio":`${n}lmstudio.svg`,"Meta Llama":`${n}meta_llama.svg`,MiniMax:`${n}minimax.svg`,"Mistral AI":`${n}mistral.svg`,Moonshot:`${n}moonshot.svg`,Morph:`${n}morph.svg`,Nebius:`${n}nebius.svg`,Novita:`${n}novita.svg`,"Nvidia Nim":`${n}nvidia_nim.svg`,Ollama:`${n}ollama.svg`,"Ollama Chat":`${n}ollama.svg`,Oobabooga:`${n}openai_small.svg`,OpenAI:`${n}openai_small.svg`,"Openai Like":`${n}openai_small.svg`,"OpenAI Text Completion":`${n}openai_small.svg`,"OpenAI-Compatible Completions (legacy /v1/completions)":`${n}openai_small.svg`,"OpenAI-Compatible Chat Completions (Together AI, vLLM, etc.)":`${n}openai_small.svg`,Openrouter:`${n}openrouter.svg`,"Oracle Cloud Infrastructure (OCI)":`${n}oracle.svg`,Perplexity:`${n}perplexity-ai.svg`,Recraft:`${n}recraft.svg`,Replicate:`${n}replicate.svg`,RunwayML:`${n}runwayml.png`,Sagemaker:`${n}bedrock.svg`,Sambanova:`${n}sambanova.svg`,"SAP Generative AI Hub":`${n}sap.png`,Snowflake:`${n}snowflake.svg`,Soniox:`${n}soniox.svg`,"Text-Completion-Codestral":`${n}mistral.svg`,TogetherAI:`${n}togetherai.svg`,Topaz:`${n}topaz.svg`,Triton:`${n}nvidia_triton.png`,V0:`${n}v0.svg`,"Vercel Ai Gateway":`${n}vercel.svg`,"Vertex AI (Anthropic, Gemini, etc.)":`${n}google.svg`,"Vertex Ai Beta":`${n}google.svg`,Vllm:`${n}vllm.png`,VolcEngine:`${n}volcengine.png`,"Voyage AI":`${n}voyage.webp`,Watsonx:`${n}watsonx.svg`,"Watsonx Text":`${n}watsonx.svg`,xAI:`${n}xai.svg`,Xinference:`${n}xinference.svg`};e.s(["Providers",()=>r,"getPlaceholder",0,e=>{if("AI/ML API"===e)return"aiml/flux-pro/v1.1";if("Vertex AI (Anthropic, Gemini, etc.)"===e)return"gemini-pro";if("Anthropic"==e)return"claude-3-opus";if("Amazon Bedrock"==e)return"claude-3-opus";if("AWS SageMaker"==e)return"sagemaker/jumpstart-dft-meta-textgeneration-llama-2-7b";else if("Google AI Studio"==e)return"gemini-pro";else if("Azure AI Foundry (Studio)"==e)return"azure_ai/command-r-plus";else if("Azure"==e)return"my-deployment";else if("Oracle Cloud Infrastructure (OCI)"==e)return"oci/xai.grok-4";else if("Snowflake"==e)return"snowflake/mistral-7b";else if("Voyage AI"==e)return"voyage/";else if("Jina AI"==e)return"jina_ai/";else if("VolcEngine"==e)return"volcengine/";else if("DeepInfra"==e)return"deepinfra/";else if("Fal AI"==e)return"fal_ai/fal-ai/flux-pro/v1.1-ultra";else if("RunwayML"==e)return"runwayml/gen4_turbo";else if("Watsonx"===e)return"watsonx/ibm/granite-3-3-8b-instruct";else if("Cursor"===e)return"cursor/claude-4-sonnet";else if("Z.AI (Zhipu AI)"===e)return"zai/glm-4.5";else return"gpt-3.5-turbo"},"getProviderLogoAndName",0,e=>{if(!e)return{logo:"",displayName:"-"};if("gemini"===e.toLowerCase()){let e="Google AI Studio";return{logo:(0,a.resolveLogoSrc)(l[e])??"",displayName:e}}let t=Object.keys(o).find(t=>o[t].toLowerCase()===e.toLowerCase())??Object.keys(o).find(t=>t.toLowerCase()===e.toLowerCase());if(!t)return{logo:"",displayName:e};let i=r[t];return{logo:(0,a.resolveLogoSrc)(l[i])??"",displayName:i}},"getProviderModels",0,(e,t)=>{let a=o[e],r=[];return e&&"object"==typeof t&&(Object.entries(t).forEach(([e,t])=>{if(null!==t&&"object"==typeof t&&"litellm_provider"in t){let o=t.litellm_provider,n="string"==typeof o&&(o.startsWith(`${a}_`)||o.startsWith(`${a}-`));(o===a||n&&!i.has(o))&&r.push(e)}}),"Cohere"==e&&Object.entries(t).forEach(([e,t])=>{null!==t&&"object"==typeof t&&"litellm_provider"in t&&"cohere_chat"===t.litellm_provider&&r.push(e)}),"AWS SageMaker"==e&&Object.entries(t).forEach(([e,t])=>{null!==t&&"object"==typeof t&&"litellm_provider"in t&&"sagemaker_chat"===t.litellm_provider&&r.push(e)})),r},"providerLogoMap",0,l,"provider_map",0,o])},928685,e=>{"use strict";var t=e.i(38953);e.s(["SearchOutlined",()=>t.default])},560280,e=>{"use strict";var t=e.i(843476),a=e.i(271645),r=e.i(618566),o=e.i(976883);function i(){let e=(0,r.useSearchParams)().get("key"),[i,n]=(0,a.useState)(null);return(0,a.useEffect)(()=>{e&&n(e)},[e]),(0,t.jsx)(o.default,{accessToken:i})}e.s(["default",0,function(){return(0,t.jsx)(a.Suspense,{fallback:(0,t.jsx)("div",{className:"flex items-center justify-center min-h-screen",children:"Loading..."}),children:(0,t.jsx)(i,{})})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/17~sdyib4xxst.js b/litellm/proxy/_experimental/out/_next/static/chunks/17~sdyib4xxst.js new file mode 100644 index 00000000000..a0165c328ee --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/17~sdyib4xxst.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,519455,838452,540886,527930,e=>{"use strict";var t=e.i(843476),r=e.i(271645);e.i(247167);var n=e.i(229315),o=e.i(667865),i=e.i(146376),l=e.i(176782),s=e.i(733332);let a=r.createContext(void 0);function u(e=!1){let t=r.useContext(a);if(void 0===t&&!e)throw Error((0,s.default)(16));return t}function c(e={}){let{disabled:t=!1,focusableWhenDisabled:n,tabIndex:s=0,native:a=!0,composite:f}=e,g=r.useRef(null),p=u(!0),v=f??void 0!==p,{props:b}=function(e){let{focusableWhenDisabled:t,disabled:n,composite:o=!1,tabIndex:i=0,isNativeButton:l}=e,s=o&&!1!==t,a=o&&!1===t;return{props:r.useMemo(()=>{let e={onKeyDown(e){n&&t&&"Tab"!==e.key&&e.preventDefault()}};return o||(e.tabIndex=i,!l&&n&&(e.tabIndex=t?i:-1)),(l&&(t||s)||!l&&n)&&(e["aria-disabled"]=n),l&&(!t||a)&&(e.disabled=n),e},[o,n,t,s,a,l,i])}}({focusableWhenDisabled:n,disabled:t,composite:v,tabIndex:s,isNativeButton:a}),m=r.useCallback(()=>{let e=g.current;d(e)&&v&&t&&void 0===b.disabled&&e.disabled&&(e.disabled=!1)},[t,b.disabled,v]);return(0,i.useIsoLayoutEffect)(m,[m]),{getButtonProps:r.useCallback((e={})=>{let{onClick:r,onMouseDown:n,onKeyUp:o,onKeyDown:i,onPointerDown:s,...u}=e;return(0,l.mergeProps)({onClick(e){t?e.preventDefault():r?.(e)},onMouseDown(e){t||n?.(e)},onKeyDown(e){var n;if(t||((0,l.makeEventPreventable)(e),i?.(e),e.baseUIHandlerPrevented))return;let o=e.target===e.currentTarget,s=e.currentTarget,u=d(s),c=!a&&(n=s,!!(n?.tagName==="A"&&n?.href)),f=o&&(a?u:!c),g="Enter"===e.key,p=" "===e.key,b=s.getAttribute("role"),m=b?.startsWith("menuitem")||"option"===b||"gridcell"===b;if(o&&v&&p){if(e.defaultPrevented&&m)return;e.preventDefault(),c||a&&u?(s.click(),e.preventBaseUIHandler()):f&&(r?.(e),e.preventBaseUIHandler());return}f&&(!a&&(p||g)&&e.preventDefault(),!a&&g&&r?.(e))},onKeyUp(e){t||(((0,l.makeEventPreventable)(e),o?.(e),e.target===e.currentTarget&&a&&v&&d(e.currentTarget)&&" "===e.key)?e.preventDefault():!e.baseUIHandlerPrevented&&(e.target!==e.currentTarget||a||v||" "!==e.key||r?.(e)))},onPointerDown(e){t?e.preventDefault():s?.(e)}},a?{type:"button"}:{role:"button"},b,u)},[t,b,v,a]),buttonRef:(0,o.useStableCallback)(e=>{g.current=e,m()})}}function d(e){return(0,n.isHTMLElement)(e)&&"BUTTON"===e.tagName}e.s(["CompositeRootContext",0,a,"useCompositeRootContext",0,u],838452),e.s(["useButton",0,c],540886);var f=e.i(552245);let g=r.forwardRef(function(e,t){let{render:r,className:n,disabled:o=!1,focusableWhenDisabled:i=!1,nativeButton:l=!0,style:s,...a}=e,{getButtonProps:u,buttonRef:d}=c({disabled:o,focusableWhenDisabled:i,native:l});return(0,f.useRenderElement)("button",e,{state:{disabled:o},ref:[t,d],props:[a,u]})});e.s(["Button",0,g],527930);var p=e.i(115504);let v=(0,p.cva)({base:"group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/80",outline:"border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",secondary:"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",ghost:"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",destructive:"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",xs:"h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",sm:"h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",lg:"h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",icon:"size-9","icon-xs":"size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3","icon-sm":"size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md","icon-lg":"size-10"}},defaultVariants:{variant:"default",size:"default"}}),b=r.forwardRef(({className:e,variant:r="default",size:n="default",...o},i)=>(0,t.jsx)(g,{ref:i,"data-slot":"button",className:(0,p.cn)(v({variant:r,size:n,className:e})),...o}));b.displayName="Button",e.s(["Button",0,b],519455)},673327,e=>{"use strict";var t=e.i(229315);let r="ArrowUp",n="ArrowDown",o="ArrowLeft",i="ArrowRight",l="Home",s=new Set([o,i]),a=new Set([o,i,l,"End"]),u=new Set([r,n]),c=new Set([r,n,l,"End"]),d=new Set([...s,...u]),f=new Set([...d,l,"End"]),g=new Set(["Shift","Control","Alt","Meta"]);function p(e,t,r){let n="left"===r?"offsetLeft":"offsetTop",o=0;for(;t.offsetParent&&(o+=t[n],t.offsetParent!==e);)t=t.offsetParent;return o}function v(e){let t=getComputedStyle(e);return{scrollMarginTop:parseFloat(t.scrollMarginTop)||0,scrollMarginRight:parseFloat(t.scrollMarginRight)||0,scrollMarginBottom:parseFloat(t.scrollMarginBottom)||0,scrollMarginLeft:parseFloat(t.scrollMarginLeft)||0,scrollPaddingTop:parseFloat(t.scrollPaddingTop)||0,scrollPaddingRight:parseFloat(t.scrollPaddingRight)||0,scrollPaddingBottom:parseFloat(t.scrollPaddingBottom)||0,scrollPaddingLeft:parseFloat(t.scrollPaddingLeft)||0}}e.s(["ARROW_DOWN",0,n,"ARROW_KEYS",0,d,"ARROW_LEFT",0,o,"ARROW_RIGHT",0,i,"ARROW_UP",0,r,"COMPOSITE_KEYS",0,f,"END",0,"End","HOME",0,l,"HORIZONTAL_KEYS",0,s,"HORIZONTAL_KEYS_WITH_EXTRA_KEYS",0,a,"MODIFIER_KEYS",0,g,"VERTICAL_KEYS",0,u,"VERTICAL_KEYS_WITH_EXTRA_KEYS",0,c,"isNativeInput",0,function(e){return!!((0,t.isHTMLElement)(e)&&"INPUT"===e.tagName&&null!=e.selectionStart||(0,t.isHTMLElement)(e)&&"TEXTAREA"===e.tagName)},"scrollIntoViewIfNeeded",0,function(e,t,r,n){if(!e||!t||!t.scrollTo)return;let o=e.scrollLeft,i=e.scrollTop,l=e.clientWidthe.scrollLeft+e.clientWidth-i.scrollPaddingRight?o=n+t.offsetWidth+l.scrollMarginRight-e.clientWidth+i.scrollPaddingRight:n-l.scrollMarginLefte.scrollLeft+e.clientWidth-i.scrollPaddingRight&&(o=n+t.offsetWidth+l.scrollMarginRight-e.clientWidth+i.scrollPaddingRight))}if(s&&"horizontal"!==n){let r=p(e,t,"top"),n=v(e),o=v(t);r-o.scrollMarginTope.scrollTop+e.clientHeight-n.scrollPaddingBottom&&(i=r+t.offsetHeight+o.scrollMarginBottom-e.clientHeight+n.scrollPaddingBottom)}e.scrollTo({left:o,top:i,behavior:"auto"})}])},503596,e=>{"use strict";var t=e.i(956789);let r=0;e.s(["enqueueFocus",0,function(e,n={}){let{preventScroll:o=!1,sync:i=!1,shouldFocus:l}=n;function s(){(!l||l())&&e?.focus({preventScroll:o})}if(cancelAnimationFrame(r),i)return s(),t.NOOP;let a=requestAnimationFrame(s);return r=a,()=>{r===a&&(cancelAnimationFrame(a),r=0)}}])},61487,e=>{"use strict";var t=e.i(271645),r=e.i(229315),n=e.i(574735),o=e.i(365420),i=e.i(828918),l=e.i(446265),s=e.i(667865),a=e.i(146376),u=e.i(439957),c=e.i(328744),d=e.i(708445),f=e.i(108868),g=e.i(333848),p=e.i(152535),v=e.i(647554),b=e.i(596296),m=e.i(157940),h=e.i(383976),y=e.i(958408),E=e.i(621082),w=e.i(675606),R=e.i(56434),T=e.i(451321),k=e.i(503596);let x={inert:new WeakMap,"aria-hidden":new WeakMap},S="data-base-ui-inert",A={inert:new WeakSet,"aria-hidden":new WeakSet},C=new WeakMap,M=0,L=(e,t)=>t.map(t=>{if(e.contains(t))return t;let n=function e(t){return t?(0,r.isShadowRoot)(t)?t.host:e(t.parentNode):null}(t);return e.contains(n)?n:null}).filter(e=>null!=e),O=e=>{let t=new Set;return e.forEach(e=>{let r=e;for(;r&&!t.has(r);)t.add(r),r=r.parentNode}),t},P=(e,t,n)=>{let o=[],i=e=>{!e||n.has(e)||Array.from(e.children).forEach(e=>{"script"!==(0,r.getNodeName)(e)&&(t.has(e)?i(e):o.push(e))})};return i(e),o};function W(e,t={}){let{ariaHidden:r=!1,inert:n=!1,mark:o=!0}=t,i=(0,f.ownerDocument)(e[0]).body;return function(e,t,r,n,{mark:o=!0}){let i=null;n?i="inert":r&&(i="aria-hidden");let l=null,s=null,a=L(t,e),u=o?P(t,O(a),new Set(a)):[],c=[],d=[];if(i){let e=x[i],r=A[i];s=r,l=e;let n=L(t,Array.from(t.querySelectorAll("[aria-live]"))),o=a.concat(n);P(t,O(o),new Set(o)).forEach(t=>{let n=t.getAttribute(i),o=null!==n&&"false"!==n,l=(e.get(t)||0)+1;e.set(t,l),c.push(t),1===l&&o&&r.add(t),o||t.setAttribute(i,"inert"===i?"":"true")})}return o&&u.forEach(e=>{let t=(C.get(e)||0)+1;C.set(e,t),d.push(e),1===t&&e.setAttribute(S,"")}),M+=1,()=>{l&&c.forEach(e=>{let t=(l.get(e)||0)-1;l.set(e,t),t||(!s?.has(e)&&i&&e.removeAttribute(i),s?.delete(e))}),o&&d.forEach(e=>{let t=(C.get(e)||0)-1;C.set(e,t),t||e.removeAttribute(S)}),(M-=1)||(x.inert=new WeakMap,x["aria-hidden"]=new WeakMap,A.inert=new WeakSet,A["aria-hidden"]=new WeakSet,C=new WeakMap)}}(e,i,r,n,{mark:o})}var I=e.i(726674),F=e.i(46420),N=e.i(638396),D=e.i(594603),H=e.i(843476);let B=[];function Y(){B=B.filter(e=>e.deref()?.isConnected)}function _(e){Y(),e&&"body"!==(0,r.getNodeName)(e)&&(B.push(new WeakRef(e)),B.length>20&&(B=B.slice(-20)))}function K(){return Y(),B[B.length-1]?.deref()}function z(e){if(e.hasAttribute("tabindex")&&!e.hasAttribute("data-tabindex")||!e.getAttribute("role")?.includes("dialog"))return;let t=(0,h.focusable)(e).filter(e=>{let t=e.getAttribute("data-tabindex")||"";return(0,h.isTabbable)(e)||e.hasAttribute("data-tabindex")&&!t.startsWith("-")}),r=e.getAttribute("tabindex");0===t.length?"0"!==r&&(e.setAttribute("tabindex","0"),e.setAttribute("data-tabindex","0")):("-1"!==r||e.hasAttribute("data-tabindex")&&"-1"!==e.getAttribute("data-tabindex"))&&(e.setAttribute("tabindex","-1"),e.setAttribute("data-tabindex","-1"))}e.s(["FloatingFocusManager",0,function(e){let{context:x,children:S,disabled:A=!1,initialFocus:C=!0,returnFocus:M=!0,restoreFocus:L=!1,modal:O=!0,closeOnFocusOut:P=!0,openInteractionType:B="",nextFocusableElement:q,previousFocusableElement:U,beforeContentFocusGuardRef:X,externalTree:V,getInsideElements:$}=e,j="rootStore"in x?x.rootStore:x,G=j.useState("open"),Z=j.useState("domReferenceElement"),J=j.useState("floatingElement"),{events:Q,dataRef:ee}=j.context,et=(0,s.useStableCallback)(()=>ee.current.floatingContext?.nodeId),er=(0,b.isTypeableCombobox)(Z)&&!1===C,en=(0,l.useValueAsRef)(C),eo=(0,l.useValueAsRef)(M),ei=(0,l.useValueAsRef)(B),el=(0,l.useValueAsRef)(G),es=(0,F.useFloatingTree)(V),ea=(0,I.usePortalContext)(),eu=t.useRef(!1),ec=t.useRef(!1),ed=t.useRef(!1),ef=t.useRef(null),eg=t.useRef(""),ep=t.useRef(""),ev=t.useRef(null),eb=t.useRef(null),em=(0,i.useMergedRefs)(ev,X,ea?.beforeInsideRef),eh=(0,i.useMergedRefs)(eb,ea?.afterInsideRef),ey=(0,u.useTimeout)(),eE=(0,u.useTimeout)(),ew=(0,d.useAnimationFrame)(),eR=null!=ea,eT=(0,b.getFloatingFocusElement)(J),ek=(0,s.useStableCallback)((e=eT)=>e?(0,h.tabbable)(e):[]),ex=(0,s.useStableCallback)(()=>$?.().filter(e=>null!=e)??[]);t.useEffect(()=>{if(A||!O)return;let e=(0,f.ownerDocument)(eT);return(0,n.addEventListener)(e,"keydown",function(e){"Tab"===e.key&&(0,v.contains)(eT,(0,v.activeElement)((0,f.ownerDocument)(eT)))&&0===ek().length&&!er&&(0,m.stopEvent)(e)})},[A,eT,O,er,ek]),t.useEffect(()=>{if(A||!G)return;let e=(0,f.ownerDocument)(eT);function t(){ed.current=!1}return(0,o.mergeCleanups)((0,n.addEventListener)(e,"pointerdown",function(e){let t=(0,v.getTarget)(e),r=ex();ed.current=!((0,v.contains)(J,t)||(0,v.contains)(Z,t)||(0,v.contains)(ea?.portalNode,t)||r.some(e=>e===t||(0,v.contains)(e,t))),ep.current=e.pointerType||"keyboard",t?.closest(`[${N.CLICK_TRIGGER_IDENTIFIER}]`)&&(ec.current=!0,eE.start(0,()=>{ec.current=!1}))},!0),(0,n.addEventListener)(e,"pointerup",t,!0),(0,n.addEventListener)(e,"pointercancel",t,!0),(0,n.addEventListener)(e,"keydown",function(){ep.current="keyboard"},!0),t)},[A,J,Z,eT,G,ea,eE,ex]),t.useEffect(()=>{if(A||!P)return;let e=(0,f.ownerDocument)(eT);function t(t){let n=t.relatedTarget,o=t.currentTarget,i=(0,v.getTarget)(t);O&&null==n&&null!=i&&(0,v.contains)(J,i)&&_(i),queueMicrotask(()=>{let l=et(),s=j.context.triggerElements,a=ex(),u=n?.hasAttribute((0,T.createAttribute)("focus-guard"))&&[ev.current,eb.current,ea?.beforeInsideRef.current,ea?.afterInsideRef.current,ea?.beforeOutsideRef.current,ea?.afterOutsideRef.current,(0,D.resolveRef)(U),(0,D.resolveRef)(q)].includes(n),c=!((0,v.contains)(Z,n)||(0,v.contains)(J,n)||(0,v.contains)(n,J)||(0,v.contains)(ea?.portalNode,n)||a.some(e=>e===n||(0,v.contains)(e,n))||null!=n&&s.hasElement(n)||s.hasMatchingElement(e=>(0,v.contains)(e,n))||u||es&&((0,y.getNodeChildren)(es.nodesRef.current,l).find(e=>(0,v.contains)(e.context?.elements.floating,n)||(0,v.contains)(e.context?.elements.domReference,n))||(0,y.getNodeAncestors)(es.nodesRef.current,l).find(e=>[e.context?.elements.floating,(0,b.getFloatingFocusElement)(e.context?.elements.floating)].includes(n)||e.context?.elements.domReference===n)));if(o===Z&&eT&&z(eT),L&&o!==Z&&!(0,E.isElementVisible)(i)&&(0,v.activeElement)(e)===e.body){if((0,r.isHTMLElement)(eT)&&(eT.focus(),"popup"===L))return void ew.request(()=>{eT.focus()});let e=ek(),t=ef.current,n=(t&&e.includes(t)?t:null)||e[e.length-1]||eT;(0,r.isHTMLElement)(n)&&n.focus()}if(ee.current.insideReactTree){ee.current.insideReactTree=!1;return}(er||!O)&&n&&c&&!ec.current&&(er||n!==K())&&(eu.current=!0,j.setOpen(!1,(0,w.createChangeEventDetails)(R.REASONS.focusOut,t)))})}let i=(0,r.isHTMLElement)(Z)?Z:null;if(J||i)return(0,o.mergeCleanups)(i&&(0,n.addEventListener)(i,"focusout",t),i&&(0,n.addEventListener)(i,"pointerdown",function(){ec.current=!0,eE.start(0,()=>{ec.current=!1})}),J&&(0,n.addEventListener)(J,"focusin",function(e){let t=(0,v.getTarget)(e);(0,h.isTabbable)(t)&&(ef.current=t)}),J&&(0,n.addEventListener)(J,"focusout",t),J&&ea&&(0,n.addEventListener)(J,"focusout",function(){ed.current||(ee.current.insideReactTree=!0,ey.start(0,()=>{ee.current.insideReactTree=!1}))},!0))},[A,Z,J,eT,O,es,ea,j,P,L,ek,er,et,ee,ey,eE,ew,q,U,ex]),t.useEffect(()=>{if(A||!J||!G)return;let e=Array.from(ea?.portalNode?.querySelectorAll(`[${(0,T.createAttribute)("portal")}]`)||[]),t=es?(0,y.getNodeAncestors)(es.nodesRef.current,et()):[],r=t.find(e=>(0,b.isTypeableCombobox)(e.context?.elements.domReference||null))?.context?.elements.domReference,n=W([J,...e,ev.current,eb.current,ea?.beforeOutsideRef.current,ea?.afterOutsideRef.current,...ex(),r,(0,D.resolveRef)(U),(0,D.resolveRef)(q),er?Z:null].filter(e=>null!=e),{ariaHidden:O||er,mark:!1}),o=W([J,...e].filter(e=>null!=e));return()=>{o(),n()}},[G,A,Z,J,O,ea,er,es,et,q,U,ex]),(0,a.useIsoLayoutEffect)(()=>{if(!G||A||!(0,r.isHTMLElement)(eT))return;let e=(0,f.ownerDocument)(eT),t=(0,v.activeElement)(e);queueMicrotask(()=>{let r,n=en.current,o="function"==typeof n?n(ei.current||""):n;if(void 0===o||!1===o||(0,v.contains)(eT,t))return;let i=null,l=()=>(null==i&&(i=ek(eT)),i[0]||eT);r=(r=!0===o||null===o?l():(0,D.resolveRef)(o))||l();let s=(0,v.contains)(eT,(0,v.activeElement)(e));(0,k.enqueueFocus)(r,{preventScroll:r===eT,shouldFocus(){if(!el.current)return!1;if(s)return!0;let t=(0,v.activeElement)(e);return!(t!==r&&(0,v.contains)(eT,t))}})})},[A,G,eT,ek,en,ei,el]),(0,a.useIsoLayoutEffect)(()=>{if(A||!eT)return;let e=(0,f.ownerDocument)(eT),t=(0,v.activeElement)(e),n=null==ei.current;function o(e){var t,r;let n;if(e.open||(t=e.nativeEvent,r=ep.current,n=(0,g.ownerWindow)((0,v.getTarget)(t)),eg.current=t instanceof n.KeyboardEvent?"keyboard":t instanceof n.FocusEvent?r||"keyboard":"pointerType"in t?t.pointerType||"keyboard":"touches"in t?"touch":t instanceof n.MouseEvent?r||(0===t.detail?"keyboard":"mouse"):""),e.reason===R.REASONS.triggerHover&&"mouseleave"===e.nativeEvent.type&&(eu.current=!0),e.reason===R.REASONS.outsidePress)if(e.nested)eu.current=!1;else if((0,m.isVirtualClick)(e.nativeEvent)||(0,m.isVirtualPointerEvent)(e.nativeEvent))eu.current=!1;else{let e=!1;(0,f.ownerDocument)(eT).createElement("div").focus({get preventScroll(){return e=!0,!1}}),e?eu.current=!1:eu.current=!0}}return _(t),Q.on("openchange",o),()=>{Q.off("openchange",o);let i=(0,v.activeElement)(e),l=ex(),s=(0,v.contains)(J,i)||l.some(e=>e===i||(0,v.contains)(e,i))||es&&(0,y.getNodeChildren)(es.nodesRef.current,et(),!1).some(e=>(0,v.contains)(e.context?.elements.floating,i)),a=eo.current,u=function(){let e=eo.current,o="function"==typeof e?e(eg.current):e;if(void 0===o||!1===o)return null;null===o&&(o=!0);let i=Z?.isConnected?Z:null,l=t?.isConnected&&"body"!==(0,r.getNodeName)(t)?t:null,s=n?l||i:i||l;return(s||(s=K()||null),"boolean"==typeof o)?s:(0,D.resolveRef)(o)||s||null}();queueMicrotask(()=>{let t=u?(0,h.isTabbable)(u)?u:(0,h.tabbable)(u)[0]||u:null;a&&!eu.current&&(0,r.isHTMLElement)(t)&&("boolean"!=typeof a||t===i||i===e.body||s)&&t.focus({preventScroll:!0}),eu.current=!1})}},[A,J,eT,eo,ei,Q,es,Z,et,ex]),(0,a.useIsoLayoutEffect)(()=>{if(!c.platform.engine.webkit||G||!J)return;let e=(0,v.activeElement)((0,f.ownerDocument)(J));(0,r.isHTMLElement)(e)&&(0,b.isTypeableElement)(e)&&(0,v.contains)(J,e)&&e.blur()},[G,J]),(0,a.useIsoLayoutEffect)(()=>{if(!A&&ea)return ea.setFocusManagerState({modal:O,closeOnFocusOut:P,open:G,onOpenChange:j.setOpen,domReference:Z}),()=>{ea.setFocusManagerState(null)}},[A,ea,O,G,j,P,Z]),(0,a.useIsoLayoutEffect)(()=>{if(!A&&eT)return z(eT),()=>{queueMicrotask(Y)}},[A,eT]);let eS=!A&&(!O||!er)&&(eR||O);return(0,H.jsxs)(t.Fragment,{children:[eS&&(0,H.jsx)(p.FocusGuard,{"data-type":"inside",ref:em,onFocus:e=>{if(O){let e=ek();(0,k.enqueueFocus)(e[e.length-1])}else if(ea?.portalNode)if(eu.current=!1,(0,h.isOutsideEvent)(e,ea.portalNode)){let e=(0,h.getNextTabbable)(Z);e?.focus()}else(0,D.resolveRef)(U??ea.beforeOutsideRef)?.focus()}}),S,eS&&(0,H.jsx)(p.FocusGuard,{"data-type":"inside",ref:eh,onFocus:e=>{if(O)(0,k.enqueueFocus)(ek()[0]);else if(ea?.portalNode)if(P&&(eu.current=!0),(0,h.isOutsideEvent)(e,ea.portalNode)){let e=(0,h.getPreviousTabbable)(Z);e?.focus()}else(0,D.resolveRef)(q??ea.afterOutsideRef)?.focus()}})]})}],61487)},426,e=>{"use strict";var t=e.i(271645),r=e.i(843476);let n=t.forwardRef(function(e,t){let n,{cutout:o,...i}=e;if(o){let e=o.getBoundingClientRect();n=`polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${e.left}px ${e.top}px,${e.left}px ${e.bottom}px,${e.right}px ${e.bottom}px,${e.right}px ${e.top}px,${e.left}px ${e.top}px)`}return(0,r.jsx)("div",{ref:t,role:"presentation","data-base-ui-inert":"",...i,style:{position:"fixed",inset:0,userSelect:"none",WebkitUserSelect:"none",clipPath:n}})});e.s(["InternalBackdrop",0,n])},145484,e=>{"use strict";var t=e.i(229315),r=e.i(574735),n=e.i(328744),o=e.i(108868),i=e.i(333848),l=e.i(146376),s=e.i(439957),a=e.i(708445),u=e.i(956789);let c={},d={},f="";class g{lockCount=0;restore=null;timeoutLock=s.Timeout.create();timeoutUnlock=s.Timeout.create();acquire(e){return this.lockCount+=1,1===this.lockCount&&null===this.restore&&this.timeoutLock.start(0,()=>this.lock(e)),this.release}release=()=>{this.lockCount-=1,0===this.lockCount&&this.restore&&this.timeoutUnlock.start(0,this.unlock)};unlock=()=>{0===this.lockCount&&this.restore&&(this.restore?.(),this.restore=null)};lock(e){let l,s,g,p,v;if(0===this.lockCount||null!==this.restore)return;let b=(0,o.ownerDocument)(e).documentElement,m=(0,i.ownerWindow)(b).getComputedStyle(b).overflowY;if("hidden"===m||"clip"===m){this.restore=u.NOOP;return}let h=n.platform.os.ios||!function(e){if("u"0}(e);this.restore=h?(s=(l=(0,o.ownerDocument)(e)).documentElement,g=l.body,v={overflowY:(p=(0,t.isOverflowElement)(s)?s:g).style.overflowY,overflowX:p.style.overflowX},Object.assign(p.style,{overflowY:"hidden",overflowX:"hidden"}),()=>{Object.assign(p.style,v)}):function(e){let l=(0,o.ownerDocument)(e),s=l.documentElement,u=l.body,g=(0,i.ownerWindow)(s),p=0,v=0,b=!1,m=a.AnimationFrame.create();if(n.platform.engine.webkit&&(g.visualViewport?.scale??1)!==1)return()=>{};function h(){let r=g.getComputedStyle(s),n=g.getComputedStyle(u),i=(r.scrollbarGutter||"").includes("both-edges")?"stable both-edges":"stable";p=s.scrollTop,v=s.scrollLeft,c={scrollbarGutter:s.style.scrollbarGutter,overflowY:s.style.overflowY,overflowX:s.style.overflowX},f=s.style.scrollBehavior,d={position:u.style.position,height:u.style.height,width:u.style.width,boxSizing:u.style.boxSizing,overflowY:u.style.overflowY,overflowX:u.style.overflowX,scrollBehavior:u.style.scrollBehavior};let l=s.scrollHeight>s.clientHeight,a=s.scrollWidth>s.clientWidth,m="scroll"===r.overflowY||"scroll"===n.overflowY,h="scroll"===r.overflowX||"scroll"===n.overflowX,y=Math.max(0,g.innerWidth-u.clientWidth),E=Math.max(0,g.innerHeight-u.clientHeight),w=parseFloat(n.marginTop)+parseFloat(n.marginBottom),R=parseFloat(n.marginLeft)+parseFloat(n.marginRight),T=(0,t.isOverflowElement)(s)?s:u;if(b=function(e){if(!("u">typeof CSS&&CSS.supports&&CSS.supports("scrollbar-gutter","stable"))||"u"{m.cancel(),y(),"function"==typeof g.removeEventListener&&E()}}(e)}}let p=new g;e.s(["useScrollLock",0,function(e=!0,t=null){(0,l.useIsoLayoutEffect)(()=>{if(e)return p.acquire(t)},[e,t])}])},385689,e=>{"use strict";var t=e.i(271645),r=e.i(708445),n=e.i(439957),o=e.i(956789),i=e.i(647554),l=e.i(596296),s=e.i(157940),a=e.i(675606),u=e.i(56434);e.s(["useClick",0,function(e,c={}){let{enabled:d=!0,event:f="click",toggle:g=!0,ignoreMouse:p=!1,stickIfOpen:v=!0,touchOpenDelay:b=0,reason:m=u.REASONS.triggerPress}=c,h="rootStore"in e?e.rootStore:e,y=h.context.dataRef,E=t.useRef(void 0),w=(0,r.useAnimationFrame)(),R=(0,n.useTimeout)(),T=t.useMemo(()=>{function e(e,t,r,n){let o=(0,a.createChangeEventDetails)(m,t,r);e&&"touch"===n&&b>0?R.start(b,()=>{h.setOpen(!0,o)}):h.setOpen(e,o)}function t(e,t,r){let n=y.current.openEvent,o=h.select("domReferenceElement")!==t;return!!e&&!!o||!e||!g||!!n&&!!v&&!r(n.type)}return{onPointerDown(e){E.current=e.pointerType},onMouseDown(r){let n=E.current,o=r.nativeEvent,a=h.select("open");if(0!==r.button||"click"===f||(0,s.isMouseLikePointerType)(n,!0)&&p)return;let u=t(a,r.currentTarget,e=>"click"===e||"mousedown"===e),c=(0,i.getTarget)(o);if((0,l.isTypeableElement)(c))return void e(u,o,c,n);let d=r.currentTarget;w.request(()=>{e(u,o,d,n)})},onClick(r){if("mousedown-only"===f)return;let n=E.current;if("mousedown"===f&&n){E.current=void 0;return}(0,s.isMouseLikePointerType)(n,!0)&&p||e(t(h.select("open"),r.currentTarget,e=>"click"===e||"mousedown"===e||"keydown"===e||"keyup"===e),r.nativeEvent,r.currentTarget,n)},onKeyDown(){E.current=void 0}}},[y,f,p,m,h,v,g,w,R,b]);return t.useMemo(()=>d?{reference:T}:o.EMPTY_OBJECT,[d,T])}])},427803,e=>{"use strict";var t=e.i(271645);e.s(["useEnhancedClickHandler",0,function(e){let r=t.useRef(""),n=t.useCallback(t=>{t.defaultPrevented||(r.current=t.pointerType,e(t,t.pointerType))},[e]);return{onClick:t.useCallback(t=>{0===t.detail?e(t,"keyboard"):("pointerType"in t?e(t,t.pointerType):e(t,r.current),r.current="")},[e]),onPointerDown:n}}])},606039,e=>{"use strict";var t=e.i(271645),r=e.i(146376),n=e.i(667865);e.s(["useValueChanged",0,function(e,o){let i=t.useRef(e),l=(0,n.useStableCallback)(o);(0,r.useIsoLayoutEffect)(()=>{i.current!==e&&l(i.current)},[e,l]),(0,r.useIsoLayoutEffect)(()=>{i.current=e},[e])}])},32199,e=>{"use strict";var t=e.i(271645),r=e.i(667865),n=e.i(427803),o=e.i(328744),i=e.i(606039);function l(e,i){let l=(0,r.useStableCallback)((t,r)=>{("function"==typeof e?e():e)||i(r||(o.platform.os.ios?"touch":""))}),{onClick:s,onPointerDown:a}=(0,n.useEnhancedClickHandler)(l);return t.useMemo(()=>({onClick:s,onPointerDown:a}),[s,a])}e.s(["useOpenInteractionType",0,function(e){let[r,n]=t.useState(null),o=l(e,n);return(0,i.useValueChanged)(e,t=>{t&&!e&&n(null)}),t.useMemo(()=>({openMethod:r,triggerProps:o}),[r,o])},"useOpenMethodTriggerProps",0,l])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/18187o3gb9vc5.js b/litellm/proxy/_experimental/out/_next/static/chunks/18187o3gb9vc5.js new file mode 100644 index 00000000000..90877bb9587 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/18187o3gb9vc5.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,886400,530909,21218,245423,961888,810980,607486,828579,997625,658041,327025,61574,78094,647780,972518,799647,117697,234098,176516,618393,581418,340270,e=>{"use strict";var a=e.i(268004),t=e.i(321836),l=e.i(592392);e.s(["useLogout",0,function(e){let r=(0,l.default)(e);return()=>{(0,a.clearTokenCookies)(),(0,t.clearStoredReturnUrl)(),localStorage.removeItem("litellm_selected_worker_id"),localStorage.removeItem("litellm_worker_url"),window.location.href=r.PROXY_LOGOUT_URL||""}}],886400);var r=e.i(843476),s=e.i(271645),i=e.i(527930),n=e.i(115504);let o=s.createContext({collapsed:!1}),d=s.forwardRef(({className:e,collapsed:a=!1,children:t,...l},s)=>(0,r.jsx)(o.Provider,{value:{collapsed:a},children:(0,r.jsx)("aside",{ref:s,"data-slot":"sidebar","data-collapsed":a,className:(0,n.cn)("group/sidebar flex h-full flex-none flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-in-out",a?"w-[72px]":"w-[280px]",e),...l,children:t})}));d.displayName="Sidebar";let c=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("div",{ref:t,"data-slot":"sidebar-header",className:(0,n.cn)("flex flex-none flex-col gap-2 p-3",e),...a}));c.displayName="SidebarHeader";let u=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("nav",{ref:t,"data-slot":"sidebar-content",className:(0,n.cn)("flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-3 pb-3",e),...a}));u.displayName="SidebarContent";let p=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("div",{ref:t,"data-slot":"sidebar-footer",className:(0,n.cn)("flex flex-none flex-col gap-2.5 border-t border-sidebar-border p-3",e),...a}));p.displayName="SidebarFooter";let m=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("div",{ref:t,"data-slot":"sidebar-group",className:(0,n.cn)("flex flex-col gap-0.5 py-1",e),...a}));m.displayName="SidebarGroup";let x=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("div",{ref:t,"data-slot":"sidebar-group-label",className:(0,n.cn)("px-2 pt-3 pb-1.5 text-[11px] font-semibold tracking-wider text-muted-foreground uppercase group-data-[collapsed=true]/sidebar:hidden",e),...a}));x.displayName="SidebarGroupLabel";let h=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("ul",{ref:t,"data-slot":"sidebar-menu",className:(0,n.cn)("flex w-full flex-col gap-0.5",e),...a}));h.displayName="SidebarMenu";let g=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("li",{ref:t,"data-slot":"sidebar-menu-item",className:(0,n.cn)("relative",e),...a}));g.displayName="SidebarMenuItem";let f=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("ul",{ref:t,"data-slot":"sidebar-menu-sub",className:(0,n.cn)("mx-3.5 my-0.5 flex min-w-0 flex-col gap-0.5 border-l border-sidebar-border py-0.5 pl-3 group-data-[collapsed=true]/sidebar:hidden",e),...a}));f.displayName="SidebarMenuSub",s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("span",{ref:t,"data-slot":"sidebar-menu-badge",className:(0,n.cn)("ml-auto flex-none rounded-full bg-sidebar-primary/10 px-1.5 py-px text-[10px] font-semibold text-sidebar-primary tabular-nums group-data-[collapsed=true]/sidebar:hidden",e),...a})).displayName="SidebarMenuBadge";let b=(0,n.cva)({base:"group/menu-btn relative flex w-full items-center gap-2.5 overflow-hidden rounded-md px-2.5 text-left text-[13px] font-medium no-underline text-sidebar-foreground/70 outline-none transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring disabled:pointer-events-none disabled:opacity-50 [&>svg]:size-[18px] [&>svg]:shrink-0 group-data-[collapsed=true]/sidebar:mx-auto group-data-[collapsed=true]/sidebar:size-9 group-data-[collapsed=true]/sidebar:justify-center group-data-[collapsed=true]/sidebar:gap-0 group-data-[collapsed=true]/sidebar:px-0",variants:{isActive:{true:"bg-sidebar-accent text-sidebar-accent-foreground before:absolute before:inset-y-1.5 before:left-0 before:w-[3px] before:rounded-r-full before:bg-sidebar-primary group-data-[collapsed=true]/sidebar:before:hidden",false:""},size:{default:"h-[34px]",sub:"h-[34px]"}},defaultVariants:{isActive:!1,size:"default"}}),y=s.forwardRef(({className:e,isActive:a,size:t,...l},s)=>(0,r.jsx)(i.Button,{ref:s,"data-slot":"sidebar-menu-button","data-active":a||void 0,className:(0,n.cn)(b({isActive:a,size:t,className:e})),...l}));y.displayName="SidebarMenuButton";let k=s.forwardRef(({className:e,...a},t)=>(0,r.jsx)("div",{ref:t,"data-slot":"sidebar-separator",className:(0,n.cn)("mx-2 my-2 h-px bg-sidebar-border",e),...a}));k.displayName="SidebarSeparator",e.s(["Sidebar",0,d,"SidebarContent",0,u,"SidebarFooter",0,p,"SidebarGroup",0,m,"SidebarGroupLabel",0,x,"SidebarHeader",0,c,"SidebarMenu",0,h,"SidebarMenuButton",0,y,"SidebarMenuItem",0,g,"SidebarMenuSub",0,f,"SidebarSeparator",0,k,"sidebarMenuButtonVariants",0,b],530909);var j=e.i(475254);let v=(0,j.default)("activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);e.s(["Activity",0,v],21218);let w=(0,j.default)("bell",[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0",key:"vwvbt9"}],["path",{d:"M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326",key:"11g9vi"}]]);e.s(["Bell",0,w],245423);let N=(0,j.default)("blocks",[["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["path",{d:"M10 21V8a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H3",key:"1fpvtg"}]]);e.s(["Blocks",0,N],961888);let S=(0,j.default)("book-open",[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]]);e.s(["BookOpen",0,S],810980);let M=(0,j.default)("building-2",[["path",{d:"M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18Z",key:"1b4qmf"}],["path",{d:"M6 12H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2",key:"i71pzd"}],["path",{d:"M18 9h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-2",key:"10jefs"}],["path",{d:"M10 6h4",key:"1itunk"}],["path",{d:"M10 10h4",key:"tcdvrf"}],["path",{d:"M10 14h4",key:"kelpxr"}],["path",{d:"M10 18h4",key:"1ulq68"}]]);e.s(["Building2",0,M],607486);let C=(0,j.default)("boxes",[["path",{d:"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z",key:"lc1i9w"}],["path",{d:"m7 16.5-4.74-2.85",key:"1o9zyk"}],["path",{d:"m7 16.5 5-3",key:"va8pkn"}],["path",{d:"M7 16.5v5.17",key:"jnp8gn"}],["path",{d:"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z",key:"8zsnat"}],["path",{d:"m17 16.5-5-3",key:"8arw3v"}],["path",{d:"m17 16.5 4.74-2.85",key:"8rfmw"}],["path",{d:"M17 16.5v5.17",key:"k6z78m"}],["path",{d:"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z",key:"1xygjf"}],["path",{d:"M12 8 7.26 5.15",key:"1vbdud"}],["path",{d:"m12 8 4.74-2.85",key:"3rx089"}],["path",{d:"M12 13.5V8",key:"1io7kd"}]]);e.s(["Boxes",0,C],828579);let L=(0,j.default)("code-xml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]]);e.s(["Code2",0,L],997625);let _=(0,j.default)("database",[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]]);e.s(["Database",0,_],658041);let T=(0,j.default)("folder",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]]);e.s(["Folder",0,T],327025);let A=(0,j.default)("heart-pulse",[["path",{d:"M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z",key:"c3ymky"}],["path",{d:"M3.22 12H9.5l.5-1 2 4.5 2-7 1.5 3.5h5.27",key:"1uw2ng"}]]);e.s(["HeartPulse",0,A],61574);let R=(0,j.default)("network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);e.s(["Network",0,R],78094);let B=(0,j.default)("palette",[["path",{d:"M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z",key:"e79jfc"}],["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor",key:"1okk4w"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor",key:"f64h9f"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor",key:"qy21gx"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor",key:"fotxhn"}]]);e.s(["Palette",0,B],647780);let z=(0,j.default)("panel-left-close",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M9 3v18",key:"fh3hqa"}],["path",{d:"m16 15-3-3 3-3",key:"14y99z"}]]);e.s(["PanelLeftClose",0,z],972518);let I=(0,j.default)("panel-left-open",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M9 3v18",key:"fh3hqa"}],["path",{d:"m14 9 3 3-3 3",key:"8010ee"}]]);e.s(["PanelLeftOpen",0,I],799647);let P=(0,j.default)("circle-play",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polygon",{points:"10 8 16 12 10 16 10 8",key:"1cimsy"}]]);e.s(["PlayCircle",0,P],117697);let U=(0,j.default)("route",[["circle",{cx:"6",cy:"19",r:"3",key:"1kj8tv"}],["path",{d:"M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15",key:"1d8sl"}],["circle",{cx:"18",cy:"5",r:"3",key:"gq8acd"}]]);e.s(["Route",0,U],234098);let E=(0,j.default)("scroll-text",[["path",{d:"M15 12h-5",key:"r7krc0"}],["path",{d:"M15 8h-5",key:"1khuty"}],["path",{d:"M19 17V5a2 2 0 0 0-2-2H4",key:"zz82l3"}],["path",{d:"M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3",key:"1ph1d7"}]]);e.s(["ScrollText",0,E],176516);var D=e.i(953651);e.s(["Server",()=>D.default],618393);let V=(0,j.default)("shield-check",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);e.s(["ShieldCheck",0,V],581418);let H=(0,j.default)("tags",[["path",{d:"m15 5 6.3 6.3a2.4 2.4 0 0 1 0 3.4L17 19",key:"1cbfv1"}],["path",{d:"M9.586 5.586A2 2 0 0 0 8.172 5H3a1 1 0 0 0-1 1v5.172a2 2 0 0 0 .586 1.414L8.29 18.29a2.426 2.426 0 0 0 3.42 0l3.58-3.58a2.426 2.426 0 0 0 0-3.42z",key:"135mg7"}],["circle",{cx:"6.5",cy:"9.5",r:".5",fill:"currentColor",key:"5pm5xn"}]]);e.s(["Tags",0,H],340270)},761911,e=>{"use strict";var a=e.i(98740);e.s(["Users",()=>a.default])},111672,858488,625005,e=>{"use strict";var a=e.i(843476),t=e.i(109799),l=e.i(785242),r=e.i(135214),s=e.i(143488),i=e.i(886400),n=e.i(602869),o=e.i(275144),d=e.i(487486),c=e.i(519455),u=e.i(530909),p=e.i(21218),m=e.i(217923),x=e.i(245423),h=e.i(961888),g=e.i(531245),f=e.i(810980),b=e.i(607486),y=e.i(828579),k=e.i(463059),j=e.i(997625),v=e.i(658041),w=e.i(778917),N=e.i(178583),S=e.i(38982),M=e.i(327025),C=e.i(61574),L=e.i(465261),_=e.i(373264),T=e.i(686311),A=e.i(78094),R=e.i(647780),B=e.i(972518),z=e.i(799647),I=e.i(117697),P=e.i(234098),U=e.i(176516),E=e.i(555436),D=e.i(618393),V=e.i(239616),H=e.i(98919),O=e.i(581418),G=e.i(340270),q=e.i(868054),F=e.i(284614),$=e.i(761911),W=e.i(475254);let K=(0,W.default)("wallet",[["path",{d:"M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1",key:"18etb6"}],["path",{d:"M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4",key:"xoc0q4"}]]);var Z=e.i(195116);let Y=(0,W.default)("workflow",[["rect",{width:"8",height:"8",x:"3",y:"3",rx:"2",key:"by2w9f"}],["path",{d:"M7 11v4a2 2 0 0 0 2 2h4",key:"xkn7yn"}],["rect",{width:"8",height:"8",x:"13",y:"13",rx:"2",key:"1cgmvn"}]]);var Q=e.i(522016),J=e.i(271645),X=e.i(115504),ee=e.i(708347),ea=e.i(844444),et=e.i(731565),el=e.i(912089),er=e.i(814431),es=e.i(636772),ei=e.i(371401),en=e.i(115571),eo=e.i(222038),ed=e.i(643531),ec=e.i(174886);let eu=({value:e,label:t,className:l,iconClassName:r="size-[15px]"})=>{let[s,i]=(0,J.useState)(!1);if((0,J.useEffect)(()=>{if(!s)return;let e=setTimeout(()=>i(!1),1200);return()=>clearTimeout(e)},[s]),!e)return null;let n=async()=>{if(navigator.clipboard)try{await navigator.clipboard.writeText(e),i(!0)}catch{i(!1)}};return(0,a.jsx)(c.Button,{type:"button",variant:"ghost",size:"icon-xs",onClick:n,"aria-label":t,title:t,className:(0,X.cn)("text-muted-foreground hover:text-primary",l),children:s?(0,a.jsx)(ed.Check,{className:r}):(0,a.jsx)(ec.Copy,{className:r})})};var ep=e.i(799676),em=e.i(337822),ex=e.i(772436),eh=e.i(699375),eg=e.i(344523);let ef=(0,W.default)("crown",[["path",{d:"M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z",key:"1vdc57"}],["path",{d:"M5 21h14",key:"11awu3"}]]),eb=(0,W.default)("id-card",[["path",{d:"M16 10h2",key:"8sgtl7"}],["path",{d:"M16 14h2",key:"epxaof"}],["path",{d:"M6.17 15a3 3 0 0 1 5.66 0",key:"n6f512"}],["circle",{cx:"9",cy:"11",r:"2",key:"yxgjnd"}],["rect",{x:"2",y:"5",width:"20",height:"14",rx:"2",key:"qneu4z"}]]),ey=(0,W.default)("log-out",[["path",{d:"m16 17 5-5-5-5",key:"1bji2h"}],["path",{d:"M21 12H9",key:"dn1m92"}],["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}]]),ek=(0,W.default)("mail",[["path",{d:"m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7",key:"132q7q"}],["rect",{x:"2",y:"4",width:"20",height:"16",rx:"2",key:"izxlao"}]]),ej=({icon:e,label:t,children:l})=>(0,a.jsxs)("div",{className:"flex min-h-[34px] items-center justify-between gap-3",children:[(0,a.jsxs)("span",{className:"flex items-center gap-2 text-[13px] text-muted-foreground",children:[e,t]}),l]}),ev=({value:e,copyLabel:t})=>(0,a.jsxs)("span",{className:"flex min-w-0 items-center gap-1",children:[(0,a.jsx)("span",{className:"max-w-[150px] truncate font-mono text-[13px] font-medium text-foreground",title:e||"-",children:e||"-"}),(0,a.jsx)(eu,{value:e,label:t})]}),ew=({onLogout:e,collapsed:t=!1})=>{let{userId:l,userEmail:i,userRole:n,premiumUser:o,accessToken:u}=(0,r.default)(),{data:p}=(0,s.useHealthReadinessDetails)(u),m=p?.litellm_version,x=(0,es.useDisableShowPrompts)(),h=(0,ei.useDisableUsageIndicator)(),g=(0,et.useDisableBlogPosts)(),f=(0,el.useDisableBouncingIcon)(),b=(0,er.useDisableShowNewBadge)(),y=(e,a)=>{a?(0,en.setLocalStorageItem)(e,"true"):(0,en.removeLocalStorageItem)(e),(0,en.emitLocalStorageChange)(e)},k=[{key:"disableShowNewBadge",label:"Hide New Feature Indicators",ariaLabel:"Toggle hide new feature indicators",checked:b,onCheckedChange:e=>y("disableShowNewBadge",e)},{key:"disableShowPrompts",label:"Hide All Prompts",ariaLabel:"Toggle hide all prompts",checked:x,onCheckedChange:e=>y("disableShowPrompts",e)},{key:"disableUsageIndicator",label:"Hide Usage Indicator",ariaLabel:"Toggle hide usage indicator",checked:h,onCheckedChange:e=>y("disableUsageIndicator",e)},{key:"disableBlogPosts",label:"Hide Blog Posts",ariaLabel:"Toggle hide blog posts",checked:g,onCheckedChange:e=>y("disableBlogPosts",e)},{key:"disableBouncingIcon",label:"Hide Bouncing Icon",ariaLabel:"Toggle hide bouncing icon",checked:f,onCheckedChange:e=>y("disableBouncingIcon",e)}],j=i||l||"user",v=function(e,a){let t=e?.split("@")[0]?.trim();if(t){let e=t.replace(/[^a-zA-Z0-9]+/g," ").trim().split(/\s+/).filter(Boolean);if(e.length>=2)return`${e[0].charAt(0)}${e[1].charAt(0)}`.toUpperCase();if(1===e.length){let a=e[0];return a.length>=2?a.slice(0,2).toUpperCase():`${a.charAt(0)}`.toUpperCase()}}return a&&a.length>=2?a.slice(0,2).toUpperCase():a&&1===a.length?`${a.toUpperCase()}•`:"?"}(i,l),w=function(e){let a=0;for(let t=0;t(0,a.jsxs)("div",{className:"flex h-[38px] items-center justify-between gap-3 px-3",children:[(0,a.jsx)("span",{className:"text-[13px] text-foreground",children:e.label}),(0,a.jsx)(eh.Switch,{size:"sm",checked:e.checked,onCheckedChange:e.onCheckedChange,"aria-label":e.ariaLabel})]},e.key))}),(0,a.jsx)(ex.Separator,{}),(0,a.jsxs)(c.Button,{variant:"ghost",onClick:e,className:"h-[42px] w-full justify-start gap-2.5 rounded-none px-3 text-sm font-medium text-foreground",children:[(0,a.jsx)(ey,{className:"size-[19px] text-muted-foreground"}),"Logout"]})]})]})};var eN=e.i(266027);let eS=(0,e.i(243652).createQueryKeys)("licenseInfo"),eM=e=>{let a={queryKey:eS.detail("license"),queryFn:()=>(0,n.getLicenseInfo)(e),enabled:!!e,staleTime:3e5,retry:!1};return(0,eN.useQuery)(a)};e.s(["useLicenseInfo",0,eM],858488);let eC=(e,a=new Date)=>{if(!e)return null;let t=new Date(`${e}T00:00:00Z`);if(Number.isNaN(t.getTime()))return null;let l=Date.UTC(a.getUTCFullYear(),a.getUTCMonth(),a.getUTCDate());return Math.ceil((t.getTime()-l)/864e5)},eL={year:"numeric",month:"short",day:"numeric",timeZone:"UTC"};e.s(["formatExpiryDate",0,e=>{let a=new Date(`${e}T00:00:00Z`);return Number.isNaN(a.getTime())?e:a.toLocaleDateString("en-US",eL)},"getDaysUntilExpiration",0,eC,"getLicenseExpiryTier",0,(e,a=new Date)=>{let t=eC(e,a);return null===t?"none":t<0?"expired":t<=7?"critical":t<=30?"warning":"none"}],625005);var e_=e.i(204258);e.s([],876013),e.i(876013),e.i(247167);var eT=e.i(502077),eA=e.i(733332);let eR=J.createContext(void 0);function eB(){let e=J.useContext(eR);if(void 0===e)throw Error((0,eA.default)(38));return e}let ez=new Map;function eI(e,a,t){return null==e?"":(function(e,a){let t=JSON.stringify({locale:function e(a){return Array.isArray(a)?a.map(a=>e(a)).join(","):null==a?"":String(a)}(e),options:a}),l=ez.get(t);if(l)return l;let r=new Intl.NumberFormat(e,a);return ez.set(t,r),r})(a,t).format(e)}var eP=e.i(201675),eU=e.i(552245);let eE=J.forwardRef(function(e,t){let{format:l,getAriaValueText:r,locale:s,max:i=100,min:n=0,value:o,render:d,className:c,children:u,style:p,...m}=e,[x,h]=J.useState(),g=(o-n)*100/(i-n),f=(0,eP.clamp)(Number.isNaN(g)?0:g,0,100),b=(0,eP.clamp)(Number.isNaN(o)?n:o,n,i),y=l?eI(o,s,l):eI(f/100,s,{style:"percent"}),k=y;r&&(k=r(y,o));let j={"aria-labelledby":x,"aria-valuemax":i,"aria-valuemin":n,"aria-valuenow":b,"aria-valuetext":k,role:"meter",children:(0,a.jsxs)(J.Fragment,{children:[u,(0,a.jsx)("span",{role:"presentation",style:eT.visuallyHidden,children:"x"})]})},v=J.useMemo(()=>({formattedValue:y,max:i,min:n,percentageValue:f,setLabelId:h,value:o}),[y,i,n,f,h,o]),w=(0,eU.useRenderElement)("div",e,{ref:t,props:[j,m]});return(0,a.jsx)(eR.Provider,{value:v,children:w})}),eD=J.forwardRef(function(e,a){let{render:t,className:l,style:r,...s}=e;return(0,eU.useRenderElement)("div",e,{ref:a,props:s})}),eV=J.forwardRef(function(e,a){let{render:t,className:l,style:r,...s}=e,{percentageValue:i}=eB();return(0,eU.useRenderElement)("div",e,{ref:a,props:[{style:{insetInlineStart:0,height:"inherit",width:`${i}%`}},s]})}),eH=J.forwardRef(function(e,a){let{className:t,render:l,children:r,style:s,...i}=e,{value:n,formattedValue:o}=eB();return(0,eU.useRenderElement)("span",e,{ref:a,props:[{"aria-hidden":!0,children:"function"==typeof r?r(o,n):o},i]})});var eO=e.i(757337);let eG=J.forwardRef(function(e,a){let{render:t,className:l,style:r,id:s,...i}=e,{setLabelId:n}=eB(),o=(0,eO.useRegisteredLabelId)(s,n);return(0,eU.useRenderElement)("span",e,{ref:a,props:[{id:o,role:"presentation"},i]})});e.s(["Indicator",0,eV,"Label",0,eG,"Root",0,eE,"Track",0,eD,"Value",0,eH],6256);var eq=e.i(6256),eq=eq;let eF=(0,X.cva)({base:"h-full rounded-full transition-[width] duration-300",variants:{tone:{default:"bg-primary",warning:"bg-amber-500",over:"bg-destructive"}},defaultVariants:{tone:"default"}}),e$=J.forwardRef(({className:e,...t},l)=>(0,a.jsx)(eq.Root,{ref:l,"data-slot":"meter",className:(0,X.cn)("flex w-full flex-col gap-1.5",e),...t}));e$.displayName="Meter";let eW=J.forwardRef(({className:e,...t},l)=>(0,a.jsx)(eq.Label,{ref:l,"data-slot":"meter-label",className:(0,X.cn)("text-xs text-muted-foreground",e),...t}));eW.displayName="MeterLabel",J.forwardRef(({className:e,...t},l)=>(0,a.jsx)(eq.Value,{ref:l,"data-slot":"meter-value",className:(0,X.cn)("text-xs font-medium tabular-nums",e),...t})).displayName="MeterValue";let eK=J.forwardRef(({className:e,...t},l)=>(0,a.jsx)(eq.Track,{ref:l,"data-slot":"meter-track",className:(0,X.cn)("h-1.5 w-full overflow-hidden rounded-full bg-muted",e),...t}));eK.displayName="MeterTrack";let eZ=J.forwardRef(({className:e,tone:t,...l},r)=>(0,a.jsx)(eq.Indicator,{ref:r,"data-slot":"meter-indicator",className:(0,X.cn)(eF({tone:t,className:e})),...l}));eZ.displayName="MeterIndicator";let eY=(0,W.default)("award",[["path",{d:"m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526",key:"1yiouv"}],["circle",{cx:"12",cy:"8",r:"6",key:"1vp47v"}]]);var eQ=e.i(664659),eJ=e.i(531278);let eX=({label:e,used:t,total:l})=>{let r=l>0?t/l*100:0;return(0,a.jsxs)(e$,{value:t,max:l,"aria-valuetext":`${t.toLocaleString()} of ${l.toLocaleString()}`,children:[(0,a.jsxs)("div",{className:"flex items-baseline justify-between gap-2",children:[(0,a.jsx)(eW,{children:e}),(0,a.jsxs)("span",{className:"text-xs font-medium tabular-nums",children:[(0,a.jsx)("span",{className:"text-foreground",children:t.toLocaleString()}),(0,a.jsxs)("span",{className:"text-muted-foreground",children:[" / ",l.toLocaleString()]})]})]}),(0,a.jsx)(eK,{children:(0,a.jsx)(eZ,{tone:r>100?"over":r>=80?"warning":"default"})})]})};function e0({accessToken:e,collapsed:t,onExpandRail:l}){let r=(0,ei.useDisableUsageIndicator)(),s=eM(e).data??null,{data:i,isLoading:o}=(0,eN.useQuery)({queryKey:["sidebarRemainingUsers",e],queryFn:()=>(0,n.getRemainingUsers)(e),enabled:!!e,retry:!1,staleTime:3e5}),d=i??null,u=null!==d&&(null!==d.total_users||null!==d.total_teams),p=!s?.has_license||!o&&!u;if(r||!e||p)return null;if(t)return(0,a.jsx)(c.Button,{variant:"outline",onClick:l,title:"Enterprise usage",className:"h-9 w-full rounded-lg border-sidebar-border bg-sidebar text-sidebar-primary shadow-none hover:bg-sidebar-accent hover:text-sidebar-primary",children:(0,a.jsx)(eY,{className:"size-[18px]",strokeWidth:1.75})});let m=s?.expiration_date?eC(s.expiration_date):null,x=s?.expiration_date?null===m?"No expiration":m<0?"Expired":0===m?"Expires today":1===m?"1 day remaining":m<30?`${m} days remaining`:m<60?"1 month remaining":`${Math.floor(m/30)} months remaining`:"Active plan",h=d?[...null!=d.total_users?[{label:"Seats",used:d.total_users_used,total:d.total_users}]:[],...null!=d.total_teams?[{label:"Teams",used:d.total_teams_used,total:d.total_teams}]:[]]:[];return(0,a.jsxs)(e_.Collapsible,{defaultOpen:!0,className:"overflow-hidden rounded-xl border border-sidebar-border bg-sidebar",children:[(0,a.jsxs)(e_.CollapsibleTrigger,{className:"group/usage flex w-full items-center gap-2.5 px-3 py-2.5 text-left transition-colors hover:bg-sidebar-accent",children:[(0,a.jsx)("span",{className:"flex size-[26px] flex-none items-center justify-center rounded-md bg-sidebar-primary/10 text-sidebar-primary",children:(0,a.jsx)(eY,{className:"size-4",strokeWidth:1.75})}),(0,a.jsxs)("span",{className:"min-w-0 flex-1 leading-tight",children:[(0,a.jsx)("span",{className:"block text-[13px] font-semibold text-foreground",children:"Enterprise usage"}),(0,a.jsx)("span",{className:"block truncate text-[11px] text-muted-foreground",children:x})]}),(0,a.jsx)(eQ.ChevronDown,{className:"size-4 flex-none -rotate-90 text-muted-foreground transition-transform group-data-[panel-open]/usage:rotate-0"})]}),(0,a.jsx)(e_.CollapsibleContent,{className:"flex flex-col gap-3 px-3 pt-0.5 pb-3",children:o&&0===h.length?(0,a.jsxs)("div",{className:"flex items-center gap-2 py-1 text-xs text-muted-foreground",children:[(0,a.jsx)(eJ.Loader2,{className:"size-3.5 animate-spin"})," Loading…"]}):h.map(e=>(0,a.jsx)(eX,{...e},e.label))})]})}var e1=e.i(571353);let e2={strokeWidth:1.75},e5=[{groupLabel:"AI GATEWAY",items:[{key:"api-keys",page:"api-keys",label:"Virtual Keys",icon:(0,a.jsx)(L.KeyRound,{...e2})},{key:"llm-playground",page:"llm-playground",label:"Playground",icon:(0,a.jsx)(I.PlayCircle,{...e2}),roles:ee.rolesWithWriteAccess},{key:"chat",page:"chat",label:(0,a.jsxs)("span",{className:"flex items-center gap-2",children:["Chat ",(0,a.jsx)(ea.default,{})]}),icon:(0,a.jsx)(T.MessageSquare,{...e2})},{key:"models",page:"models",label:"Models + Endpoints",icon:(0,a.jsx)(A.Network,{...e2}),roles:ee.rolesAllowedToViewWriteScopedPages},{key:"agentic",page:"agentic",label:"Agentic",icon:(0,a.jsx)(g.Bot,{...e2}),children:[{key:"agents",page:"agents",label:"Agents",icon:(0,a.jsx)(g.Bot,{...e2}),roles:ee.rolesAllowedToViewWriteScopedPages},{key:"workflows",page:"workflows",label:"Workflow Runs",icon:(0,a.jsx)(Y,{...e2})},{key:"memory",page:"memory",label:"Memory",icon:(0,a.jsx)(v.Database,{...e2})}]},{key:"mcp-servers",page:"mcp-servers",label:"MCP Servers",icon:(0,a.jsx)(D.Server,{...e2})},{key:"skills",page:"skills",label:"Skills",icon:(0,a.jsx)(h.Blocks,{...e2}),roles:ee.all_admin_roles},{key:"guardrails",page:"guardrails",label:"Guardrails",icon:(0,a.jsx)(H.Shield,{...e2})},{key:"policies",page:"policies",label:"Policies",icon:(0,a.jsx)(U.ScrollText,{...e2}),roles:ee.all_admin_roles},{key:"tools",page:"tools",label:"Tools",icon:(0,a.jsx)(Z.Wrench,{...e2}),children:[{key:"search-tools",page:"search-tools",label:"Search Tools",icon:(0,a.jsx)(E.Search,{...e2})},{key:"vector-stores",page:"vector-stores",label:"Vector Stores",icon:(0,a.jsx)(v.Database,{...e2})},{key:"tool-policies",page:"tool-policies",label:"Tool Policies",icon:(0,a.jsx)(O.ShieldCheck,{...e2})}]}]},{groupLabel:"OBSERVABILITY",items:[{key:"new_usage",page:"new_usage",icon:(0,a.jsx)(m.BarChart3,{...e2}),roles:[...ee.all_admin_roles,...ee.internalUserRoles],label:"Usage"},{key:"logs",page:"logs",label:"Logs",icon:(0,a.jsx)(p.Activity,{...e2})},{key:"guardrails-monitor",page:"guardrails-monitor",label:"Guardrails Monitor",icon:(0,a.jsx)(C.HeartPulse,{...e2}),roles:[...ee.all_admin_roles,...ee.internalUserRoles]}]},{groupLabel:"ACCESS CONTROL",items:[{key:"teams",page:"teams",label:"Teams",icon:(0,a.jsx)($.Users,{...e2})},{key:"projects",page:"projects",label:(0,a.jsxs)("span",{className:"flex items-center gap-2",children:["Projects ",(0,a.jsx)(ea.default,{})]}),icon:(0,a.jsx)(M.Folder,{...e2}),roles:ee.all_admin_roles},{key:"users",page:"users",label:"Internal Users",icon:(0,a.jsx)(F.User,{...e2}),roles:ee.all_admin_roles},{key:"organizations",page:"organizations",label:"Organizations",icon:(0,a.jsx)(b.Building2,{...e2}),roles:ee.all_admin_roles},{key:"access-groups",page:"access-groups",label:"Access Groups",icon:(0,a.jsx)(y.Boxes,{...e2}),roles:ee.all_admin_roles},{key:"budgets",page:"budgets",label:"Budgets",icon:(0,a.jsx)(K,{...e2}),roles:ee.all_admin_roles}]},{groupLabel:"DEVELOPER TOOLS",items:[{key:"api_ref",page:"api_ref",label:"API Reference",icon:(0,a.jsx)(j.Code2,{...e2})},{key:"model-hub-table",page:"model-hub-table",label:"AI Hub",icon:(0,a.jsx)(_.LayoutGrid,{...e2})},{key:"learning-resources",page:"learning-resources",label:"Learning Resources",icon:(0,a.jsx)(f.BookOpen,{...e2}),external_url:"https://models.litellm.ai/cookbook"},{key:"experimental",page:"experimental",label:"Experimental",icon:(0,a.jsx)(S.FlaskConical,{...e2}),children:[{key:"caching",page:"caching",label:"Caching",icon:(0,a.jsx)(v.Database,{...e2}),roles:ee.all_admin_roles},{key:"prompts",page:"prompts",label:"Prompts",icon:(0,a.jsx)(N.FileText,{...e2}),roles:ee.all_admin_roles},{key:"transform-request",page:"transform-request",label:"API Playground",icon:(0,a.jsx)(q.Terminal,{...e2}),roles:[...ee.all_admin_roles,...ee.internalUserRoles]},{key:"tag-management",page:"tag-management",label:"Tag Management",icon:(0,a.jsx)(G.Tags,{...e2}),roles:ee.all_admin_roles},{key:"4",page:"usage",label:"Old Usage",icon:(0,a.jsx)(m.BarChart3,{...e2})}]}]},{groupLabel:"SETTINGS",roles:ee.all_admin_roles,items:[{key:"settings",page:"settings",label:(0,a.jsxs)("span",{className:"flex items-center gap-2",children:["Settings ",(0,a.jsx)(ea.default,{})]}),icon:(0,a.jsx)(V.Settings,{...e2}),roles:ee.all_admin_roles,children:[{key:"router-settings",page:"router-settings",label:"Router Settings",icon:(0,a.jsx)(P.Route,{...e2}),roles:ee.all_admin_roles},{key:"logging-and-alerts",page:"logging-and-alerts",label:"Logging & Alerts",icon:(0,a.jsx)(x.Bell,{...e2}),roles:ee.all_admin_roles},{key:"admin-panel",page:"admin-panel",label:(0,a.jsxs)("span",{className:"flex items-center gap-2",children:["Admin Settings"," ",(0,a.jsx)(ea.default,{dot:!0,children:(0,a.jsx)("span",{})})]}),icon:(0,a.jsx)(V.Settings,{...e2}),roles:ee.all_admin_roles},{key:"cost-tracking",page:"cost-tracking",label:"Cost Tracking",icon:(0,a.jsx)(m.BarChart3,{...e2}),roles:ee.all_admin_roles},{key:"ui-theme",page:"ui-theme",label:"UI Theme",icon:(0,a.jsx)(R.Palette,{...e2}),roles:ee.all_admin_roles}]}]}],e3=e=>{for(let a of e5)for(let t of a.items)if(t.children?.some(a=>a.page===e||a.key===e))return t.key;return null},e4=e=>"string"==typeof e.label?e.label:e.key,e7={"AI GATEWAY":"AI Gateway",OBSERVABILITY:"Observability","ACCESS CONTROL":"Access Control","DEVELOPER TOOLS":"Developer Tools",SETTINGS:"Settings"},e8=e=>e.split(/[-_]/).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ");e.s(["default",0,({setPage:e,defaultSelectedKey:p,collapsed:m=!1,onToggleCollapsed:x,enabledPagesInternalUsers:h,enableProjectsUI:g,enableChatUI:f,disableAgentsForInternalUsers:b,allowAgentsForTeamAdmins:y,disableVectorStoresForInternalUsers:j,allowVectorStoresForTeamAdmins:v})=>{let{userId:N,accessToken:S,userRole:M}=(0,r.default)(),{data:C}=(0,t.useOrganizations)(),{data:L}=(0,l.useTeams)(),{logoUrl:_}=(0,o.useTheme)(),{data:T}=(0,s.useHealthReadinessDetails)(S),A=(0,i.useLogout)(S),R=(0,n.getProxyBaseUrl)(),I=T?.litellm_version,P=(e=>{for(let a of e5)for(let t of a.items){if(t.page===e)return t.key;let a=t.children?.find(a=>a.page===e);if(a)return a.key}return"api-keys"})(p),[U,E]=(0,J.useState)(()=>{let e=e3(p);return new Set(e?[e]:[])}),[D,V]=(0,J.useState)(p);if(p!==D){V(p);let e=e3(p);e&&!U.has(e)&&E(a=>new Set(a).add(e))}let H=(0,J.useMemo)(()=>!!N&&!!C&&C.some(e=>e.members?.some(e=>e.user_id===N&&"org_admin"===e.user_role)),[N,C]),O=(0,J.useMemo)(()=>(0,ee.isUserTeamAdminForAnyTeam)(L??null,N??""),[L,N]),G=e=>{let a=(0,ee.isAdminRole)(M);return e.map(e=>({...e,children:e.children?G(e.children):void 0})).filter(e=>{if("organizations"===e.key||"users"===e.key)return!!(!e.roles||e.roles.includes(M)||H)&&(!!a||null==h||h.includes(e.page));if("projects"===e.key&&!g||"chat"===e.key&&!f||!a&&"agents"===e.key&&b&&!(y&&O)||!a&&"vector-stores"===e.key&&j&&!(v&&O)||e.roles&&!e.roles.includes(M))return!1;if(!a&&null!=h)return!!(e.children&&e.children.length>0&&e.children.some(e=>h.includes(e.page)))||h.includes(e.page);return!0})},q=e5.filter(e=>!e.roles||e.roles.includes(M)).map(e=>({groupLabel:e.groupLabel,items:G(e.items)})).filter(e=>e.items.length>0),F=(t,l)=>{let r=P===t.key,s=l?"sub":"default",i=(0,a.jsx)("span",{className:"flex-1 truncate group-data-[collapsed=true]/sidebar:hidden",children:t.label});if(t.external_url)return(0,a.jsxs)("a",{href:t.external_url,target:"_blank",rel:"noopener noreferrer",title:m?e4(t):void 0,"data-active":r||void 0,className:(0,X.cn)((0,u.sidebarMenuButtonVariants)({isActive:r,size:s})),children:[t.icon,i,(0,a.jsx)(w.ExternalLink,{className:"size-3.5 shrink-0 opacity-70 group-data-[collapsed=true]/sidebar:hidden"})]},t.key);let n=e1.MIGRATED_PAGES[t.page]?(0,e1.migratedHref)(e1.MIGRATED_PAGES[t.page]):(0,e1.legacyPageHref)(t.page);return(0,a.jsxs)("a",{href:n,onClick:a=>{t.external_url||!a.metaKey&&!a.ctrlKey&&!a.shiftKey&&1!==a.button&&(a.preventDefault(),e(t.page))},title:m?e4(t):void 0,"data-active":r||void 0,className:(0,X.cn)((0,u.sidebarMenuButtonVariants)({isActive:r,size:s})),children:[t.icon,i]},t.key)},$=_||`${R}/get_image`;return(0,a.jsxs)(u.Sidebar,{collapsed:m,children:[(0,a.jsx)(u.SidebarHeader,{className:"h-14 border-b border-border group-data-[collapsed=true]/sidebar:h-auto",children:(0,a.jsxs)("div",{className:"flex items-center justify-between gap-2 group-data-[collapsed=true]/sidebar:flex-col",children:[(0,a.jsxs)("div",{className:"flex min-w-0 items-center gap-2",children:[(0,a.jsx)(Q.default,{href:R||"/",className:"flex min-w-0 items-center","aria-label":"LiteLLM home",children:(0,a.jsx)("img",{src:$,alt:"LiteLLM",className:"h-7 w-auto max-w-[150px] object-contain group-data-[collapsed=true]/sidebar:w-7"})}),I&&(0,a.jsxs)(d.Badge,{variant:"outline",render:(0,a.jsx)("a",{href:"https://docs.litellm.ai/release_notes",target:"_blank",rel:"noopener noreferrer"}),className:"px-1.5 py-0 font-mono text-[10px] font-medium text-muted-foreground group-data-[collapsed=true]/sidebar:hidden",children:["v",I]})]}),x&&(0,a.jsx)(c.Button,{variant:"ghost",size:"icon-sm",onClick:x,"aria-label":m?"Expand sidebar":"Collapse sidebar",className:"flex-none text-muted-foreground",children:m?(0,a.jsx)(z.PanelLeftOpen,{}):(0,a.jsx)(B.PanelLeftClose,{})})]})}),(0,a.jsx)(u.SidebarContent,{children:q.map((e,t)=>(0,a.jsxs)(u.SidebarGroup,{children:[t>0&&(0,a.jsx)(u.SidebarSeparator,{className:"hidden group-data-[collapsed=true]/sidebar:block"}),(0,a.jsx)(u.SidebarGroupLabel,{children:e.groupLabel}),(0,a.jsx)(u.SidebarMenu,{children:e.items.map(e=>(e=>{if(!(e.children&&e.children.length>0))return(0,a.jsx)(u.SidebarMenuItem,{children:F(e,!1)},e.key);let t=P===e.key,l=U.has(e.key);return(0,a.jsxs)(u.SidebarMenuItem,{children:[(0,a.jsxs)(u.SidebarMenuButton,{isActive:t,onClick:()=>(e=>{if(m){x?.(),E(a=>new Set(a).add(e));return}E(a=>{let t=new Set(a);return t.has(e)?t.delete(e):t.add(e),t})})(e.key),title:m?e4(e):void 0,children:[e.icon,(0,a.jsx)("span",{className:"flex-1 truncate group-data-[collapsed=true]/sidebar:hidden",children:e.label}),(0,a.jsx)(k.ChevronRight,{className:(0,X.cn)("size-4 shrink-0 transition-transform group-data-[collapsed=true]/sidebar:hidden",l&&"rotate-90")})]}),l&&(0,a.jsx)(u.SidebarMenuSub,{children:e.children.map(e=>(0,a.jsx)(u.SidebarMenuItem,{children:F(e,!0)},e.key))})]},e.key)})(e))})]},e.groupLabel))}),(0,a.jsxs)(u.SidebarFooter,{children:[(0,ee.isAdminRole)(M)&&(0,a.jsx)(e0,{accessToken:S,collapsed:m,onExpandRail:()=>x?.()}),(0,a.jsx)(ew,{onLogout:A,collapsed:m})]})]})},"getBreadcrumb",0,e=>{for(let a of e5)for(let t of a.items){let l=e7[a.groupLabel]??a.groupLabel;if(t.page===e)return{section:l,title:"string"==typeof t.label?t.label:e8(t.key)};let r=t.children?.find(a=>a.page===e);if(r)return{section:l,title:"string"==typeof r.label?r.label:e8(r.key)}}return{section:null,title:e8(e)}},"menuGroups",0,e5],111672)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/189gx.py268lp.js b/litellm/proxy/_experimental/out/_next/static/chunks/189gx.py268lp.js new file mode 100644 index 00000000000..d93c9e6a16c --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/189gx.py268lp.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,11751,e=>{"use strict";e.s(["mapEmptyStringToNull",0,function(e){return""===e?null:e}])},643449,e=>{"use strict";var t=e.i(843476),a=e.i(262218),s=e.i(810757),l=e.i(477386),i=e.i(557662),r=e.i(555987);e.s(["default",0,function({loggingConfigs:e=[],disabledCallbacks:n=[],variant:o="card",className:d=""}){let c=(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(s.CogIcon,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900",children:"Logging Integrations"}),(0,t.jsx)(a.Tag,{color:"blue",children:e.length})]}),e.length>0?(0,t.jsx)("div",{className:"space-y-3",children:e.map((e,l)=>{var n;let o=(n=e.callback_name,Object.entries(i.callback_map).find(([e,t])=>t===n)?.[0]||n),d=(0,r.resolveLogoSrc)(i.callbackInfo[o]?.logo);return(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-blue-50 border border-blue-200",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[d?(0,t.jsx)("img",{src:d,alt:o,className:"w-5 h-5 object-contain"}):(0,t.jsx)(s.CogIcon,{className:"h-5 w-5 text-gray-400"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-medium text-blue-800",children:o}),(0,t.jsxs)("span",{className:"block text-xs text-blue-600",children:[Object.keys(e.callback_vars).length," parameters configured"]})]})]}),(0,t.jsx)(a.Tag,{color:(e=>{switch(e){case"success":return"green";case"failure":return"red";case"success_and_failure":return"blue";default:return}})(e.callback_type),children:(e=>{switch(e){case"success":return"Success Only";case"failure":return"Failure Only";case"success_and_failure":return"Success & Failure";default:return e}})(e.callback_type)})]},l)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(s.CogIcon,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)("span",{className:"text-gray-500 text-sm",children:"No logging integrations configured"})]})]}),(0,t.jsxs)("div",{className:"space-y-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(l.BanIcon,{className:"h-4 w-4 text-red-600"}),(0,t.jsx)("span",{className:"font-semibold text-gray-900",children:"Disabled Callbacks"}),(0,t.jsx)(a.Tag,{color:"red",children:n.length})]}),n.length>0?(0,t.jsx)("div",{className:"space-y-3",children:n.map((e,s)=>{let n=i.reverse_callback_map[e]||e,o=(0,r.resolveLogoSrc)(i.callbackInfo[n]?.logo);return(0,t.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-red-50 border border-red-200",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[o?(0,t.jsx)("img",{src:o,alt:n,className:"w-5 h-5 object-contain"}):(0,t.jsx)(l.BanIcon,{className:"h-5 w-5 text-gray-400"}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-medium text-red-800",children:n}),(0,t.jsx)("span",{className:"block text-xs text-red-600",children:"Disabled for this key"})]})]}),(0,t.jsx)(a.Tag,{color:"red",children:"Disabled"})]},s)})}):(0,t.jsxs)("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200",children:[(0,t.jsx)(l.BanIcon,{className:"h-4 w-4 text-gray-400"}),(0,t.jsx)("span",{className:"text-gray-500 text-sm",children:"No callbacks disabled"})]})]})]});return"card"===o?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${d}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"block font-semibold text-gray-900",children:"Logging Settings"}),(0,t.jsx)("span",{className:"block text-xs text-gray-500",children:"Active logging integrations and disabled callbacks for this key"})]})}),c]}):(0,t.jsxs)("div",{className:`${d}`,children:[(0,t.jsx)("span",{className:"block font-medium text-gray-900 mb-3",children:"Logging Settings"}),c]})}])},183588,e=>{"use strict";var t=e.i(843476),a=e.i(266484);e.s(["default",0,({value:e,onChange:s,disabledCallbacks:l=[],onDisabledCallbacksChange:i})=>(0,t.jsx)(a.default,{value:e,onChange:s,disabledCallbacks:l,onDisabledCallbacksChange:i})])},214541,e=>{"use strict";var t=e.i(271645),a=e.i(135214),s=e.i(270345);e.s(["default",0,()=>{let[e,l]=(0,t.useState)([]),{accessToken:i,userId:r,userRole:n}=(0,a.default)();return(0,t.useEffect)(()=>{(async()=>{l(await (0,s.fetchTeams)(i,r,n,null))})()},[i,r,n]),{teams:e,setTeams:l}}])},772345,e=>{"use strict";e.i(247167);var t=e.i(931067),a=e.i(271645);let s={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M168 504.2c1-43.7 10-86.1 26.9-126 17.3-41 42.1-77.7 73.7-109.4S337 212.3 378 195c42.4-17.9 87.4-27 133.9-27s91.5 9.1 133.8 27A341.5 341.5 0 01755 268.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.7 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c0-6.7-7.7-10.5-12.9-6.3l-56.4 44.1C765.8 155.1 646.2 92 511.8 92 282.7 92 96.3 275.6 92 503.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8zm756 7.8h-60c-4.4 0-7.9 3.5-8 7.8-1 43.7-10 86.1-26.9 126-17.3 41-42.1 77.8-73.7 109.4A342.45 342.45 0 01512.1 856a342.24 342.24 0 01-243.2-100.8c-9.9-9.9-19.2-20.4-27.8-31.4l60.2-47a8 8 0 00-3-14.1l-175.7-43c-5-1.2-9.9 2.6-9.9 7.7l-.7 181c0 6.7 7.7 10.5 12.9 6.3l56.4-44.1C258.2 868.9 377.8 932 512.2 932c229.2 0 415.5-183.7 419.8-411.8a8 8 0 00-8-8.2z"}}]},name:"sync",theme:"outlined"};var l=e.i(9583),i=a.forwardRef(function(e,i){return a.createElement(l.default,(0,t.default)({},e,{ref:i,icon:s}))});e.s(["SyncOutlined",0,i],772345)},784647,505022,721929,e=>{"use strict";var t=e.i(843476),a=e.i(464571),s=e.i(898586),l=e.i(592968),i=e.i(770914),r=e.i(312361),n=e.i(525720),o=e.i(282786),d=e.i(447566),c=e.i(772345),m=e.i(955135),u=e.i(646563),x=e.i(771674),p=e.i(72713),g=e.i(637235),h=e.i(962944);e.i(247167);var _=e.i(931067),j=e.i(271645);let y={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M866.9 169.9L527.1 54.1C523 52.7 517.5 52 512 52s-11 .7-15.1 2.1L157.1 169.9c-8.3 2.8-15.1 12.4-15.1 21.2v482.4c0 8.8 5.7 20.4 12.6 25.9L499.3 968c3.5 2.7 8 4.1 12.6 4.1s9.2-1.4 12.6-4.1l344.7-268.6c6.9-5.4 12.6-17 12.6-25.9V191.1c.2-8.8-6.6-18.3-14.9-21.2zM810 654.3L512 886.5 214 654.3V226.7l298-101.6 298 101.6v427.6zm-405.8-201c-3-4.1-7.8-6.6-13-6.6H336c-6.5 0-10.3 7.4-6.5 12.7l126.4 174a16.1 16.1 0 0026 0l212.6-292.7c3.8-5.3 0-12.7-6.5-12.7h-55.2c-5.1 0-10 2.5-13 6.6L468.9 542.4l-64.7-89.1z"}}]},name:"safety-certificate",theme:"outlined"};var b=e.i(9583),f=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:y}))});let v={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M668.6 320c0-4.4-3.6-8-8-8h-54.5c-3 0-5.8 1.7-7.1 4.4l-84.7 168.8H511l-84.7-168.8a8 8 0 00-7.1-4.4h-55.7c-1.3 0-2.6.3-3.8 1-3.9 2.1-5.3 7-3.2 10.8l103.9 191.6h-57c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76v39h-76c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76V704c0 4.4 3.6 8 8 8h49.9c4.4 0 8-3.6 8-8v-63.5h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8h-76.3v-39h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8H564l103.7-191.6c.5-1.1.9-2.4.9-3.7zM157.9 504.2a352.7 352.7 0 01103.5-242.4c32.5-32.5 70.3-58.1 112.4-75.9 43.6-18.4 89.9-27.8 137.6-27.8 47.8 0 94.1 9.3 137.6 27.8 42.1 17.8 79.9 43.4 112.4 75.9 10 10 19.3 20.5 27.9 31.4l-50 39.1a8 8 0 003 14.1l156.8 38.3c5 1.2 9.9-2.6 9.9-7.7l.8-161.5c0-6.7-7.7-10.5-12.9-6.3l-47.8 37.4C770.7 146.3 648.6 82 511.5 82 277 82 86.3 270.1 82 503.8a8 8 0 008 8.2h60c4.3 0 7.8-3.5 7.9-7.8zM934 512h-60c-4.3 0-7.9 3.5-8 7.8a352.7 352.7 0 01-103.5 242.4 352.57 352.57 0 01-112.4 75.9c-43.6 18.4-89.9 27.8-137.6 27.8s-94.1-9.3-137.6-27.8a352.57 352.57 0 01-112.4-75.9c-10-10-19.3-20.5-27.9-31.4l49.9-39.1a8 8 0 00-3-14.1l-156.8-38.3c-5-1.2-9.9 2.6-9.9 7.7l-.8 161.7c0 6.7 7.7 10.5 12.9 6.3l47.8-37.4C253.3 877.7 375.4 942 512.5 942 747 942 937.7 753.9 942 520.2a8 8 0 00-8-8.2z"}}]},name:"transaction",theme:"outlined"};var k=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:v}))}),N={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"defs",attrs:{},children:[{tag:"style",attrs:{}}]},{tag:"path",attrs:{d:"M945 412H689c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h256c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM811 548H689c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h122c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM477.3 322.5H434c-6.2 0-11.2 5-11.2 11.2v248c0 3.6 1.7 6.9 4.6 9l148.9 108.6c5 3.6 12 2.6 15.6-2.4l25.7-35.1v-.1c3.6-5 2.5-12-2.5-15.6l-126.7-91.6V333.7c.1-6.2-5-11.2-11.1-11.2z"}},{tag:"path",attrs:{d:"M804.8 673.9H747c-5.6 0-10.9 2.9-13.9 7.7a321 321 0 01-44.5 55.7 317.17 317.17 0 01-101.3 68.3c-39.3 16.6-81 25-124 25-43.1 0-84.8-8.4-124-25-37.9-16-72-39-101.3-68.3s-52.3-63.4-68.3-101.3c-16.6-39.2-25-80.9-25-124 0-43.1 8.4-84.7 25-124 16-37.9 39-72 68.3-101.3 29.3-29.3 63.4-52.3 101.3-68.3 39.2-16.6 81-25 124-25 43.1 0 84.8 8.4 124 25 37.9 16 72 39 101.3 68.3a321 321 0 0144.5 55.7c3 4.8 8.3 7.7 13.9 7.7h57.8c6.9 0 11.3-7.2 8.2-13.3-65.2-129.7-197.4-214-345-215.7-216.1-2.7-395.6 174.2-396 390.1C71.6 727.5 246.9 903 463.2 903c149.5 0 283.9-84.6 349.8-215.8a9.18 9.18 0 00-8.2-13.3z"}}]},name:"field-time",theme:"outlined"},T=j.forwardRef(function(e,t){return j.createElement(b.default,(0,_.default)({},e,{ref:t,icon:N}))}),w=e.i(304911);let{Text:S}=s.Typography;function C({label:e,value:a,icon:s,truncate:l=!1,copyable:r=!1,defaultUserIdCheck:n=!1}){let o=!a,d=n&&"default_user_id"===a,c=d?(0,t.jsx)(w.default,{userId:a}):(0,t.jsx)(S,{strong:!0,copyable:!!(r&&!o&&!d)&&{tooltips:[`Copy ${e}`,"Copied!"]},ellipsis:l,style:l?{maxWidth:160,display:"block"}:void 0,children:o?"-":a});return(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.Space,{size:4,children:[(0,t.jsx)(S,{type:"secondary",children:s}),(0,t.jsx)(S,{type:"secondary",style:{fontSize:12,textTransform:"uppercase",letterSpacing:"0.05em"},children:e})]}),(0,t.jsx)("div",{children:c})]})}let{Title:I,Text:A}=s.Typography;function F({userAlias:e,userEmail:a,userId:l}){let r=(0,t.jsxs)(i.Space,{size:4,children:[(0,t.jsx)(A,{type:"secondary",children:(0,t.jsx)(x.UserOutlined,{})}),(0,t.jsx)(A,{type:"secondary",style:{fontSize:12,textTransform:"uppercase",letterSpacing:"0.05em"},children:"User"})]});if(!e&&!a&&!l)return(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(A,{strong:!0,children:"-"})})]});let n="default_user_id"===l,d=e||a||l,c=(0,t.jsx)("div",{className:"flex flex-col gap-2 text-xs min-w-[200px] max-w-[300px]",children:[{label:"User Alias",value:e??null},{label:"User Email",value:a||null},{label:"User ID",value:l||null}].map(({label:e,value:a})=>(0,t.jsxs)("div",{className:"flex flex-col min-w-0",children:[(0,t.jsx)("span",{className:"text-gray-400",children:e}),a?(0,t.jsx)(s.Typography.Text,{className:"font-mono text-xs",style:{maxWidth:220},ellipsis:{tooltip:a},copyable:!0,children:a}):(0,t.jsx)("span",{className:"font-mono",children:"-"})]},e))});return!n||e||a?(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(o.Popover,{content:c,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)(A,{strong:!0,ellipsis:!0,style:{cursor:"default",maxWidth:200,display:"block"},children:d})})})]}):(0,t.jsxs)("div",{children:[r,(0,t.jsx)("div",{children:(0,t.jsx)(o.Popover,{content:c,trigger:"hover",placement:"bottomLeft",children:(0,t.jsx)("span",{className:"cursor-default",children:(0,t.jsx)(w.default,{userId:l})})})})]})}e.s(["KeyInfoHeader",0,function({data:e,onBack:s,onCreateNew:o,onRegenerate:x,onDelete:_,onResetSpend:j,canModifyKey:y=!0,backButtonText:b="Back to Keys",regenerateDisabled:v=!1,regenerateTooltip:N}){return(0,t.jsxs)("div",{children:[o&&(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(a.Button,{type:"primary",icon:(0,t.jsx)(u.PlusOutlined,{}),onClick:o,children:"Create New Key"})}),(0,t.jsx)("div",{style:{marginBottom:16},children:(0,t.jsx)(a.Button,{type:"text",icon:(0,t.jsx)(d.ArrowLeftOutlined,{}),onClick:s,children:b})}),(0,t.jsxs)(n.Flex,{justify:"space-between",align:"start",style:{marginBottom:20},children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(I,{level:3,copyable:{tooltips:["Copy Key Alias","Copied!"]},style:{margin:0},children:e.keyName}),(0,t.jsxs)(A,{type:"secondary",copyable:{text:e.keyId,tooltips:["Copy Key ID","Copied!"]},children:["Key ID: ",e.keyId]})]}),y&&(0,t.jsxs)(i.Space,{children:[(0,t.jsx)(l.Tooltip,{title:N||"",children:(0,t.jsx)("span",{children:(0,t.jsx)(a.Button,{icon:(0,t.jsx)(c.SyncOutlined,{}),onClick:x,disabled:v,children:"Regenerate Key"})})}),j&&(0,t.jsx)(a.Button,{danger:!0,icon:(0,t.jsx)(k,{}),onClick:j,children:"Reset Spend"}),(0,t.jsx)(a.Button,{danger:!0,icon:(0,t.jsx)(m.DeleteOutlined,{}),onClick:_,children:"Delete Key"})]})]}),(0,t.jsxs)(n.Flex,{align:"stretch",gap:40,style:{marginBottom:40},children:[(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(F,{userAlias:e.userAlias,userEmail:e.userEmail,userId:e.userId}),(0,t.jsx)(C,{label:"Expires",value:e.expires,icon:(0,t.jsx)(T,{})})]}),(0,t.jsx)(r.Divider,{type:"vertical",style:{height:"auto"}}),(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(C,{label:"Created At",value:e.createdAt,icon:(0,t.jsx)(p.CalendarOutlined,{})}),(0,t.jsx)(C,{label:"Created By",value:e.createdBy,icon:(0,t.jsx)(f,{}),truncate:!0,copyable:!0,defaultUserIdCheck:!0})]}),(0,t.jsx)(r.Divider,{type:"vertical",style:{height:"auto"}}),(0,t.jsxs)(i.Space,{direction:"vertical",size:16,children:[(0,t.jsx)(C,{label:"Last Updated",value:e.lastUpdated,icon:(0,t.jsx)(g.ClockCircleOutlined,{})}),(0,t.jsx)(C,{label:"Last Active",value:e.lastActive,icon:(0,t.jsx)(h.ThunderboltOutlined,{})})]})]})]})}],784647);var M=e.i(599724),L=e.i(389083),R=e.i(278587);let E=j.forwardRef(function(e,t){return j.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor","aria-hidden":"true",ref:t},e),j.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"}))});e.s(["default",0,({autoRotate:e=!1,rotationInterval:a,lastRotationAt:s,keyRotationAt:l,nextRotationAt:i,variant:r="card",className:n=""})=>{let o=e=>{let t=new Date(e),a=t.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),s=t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});return`${a} at ${s}`},d=(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsx)("div",{className:"space-y-3",children:(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(R.RefreshIcon,{className:"h-4 w-4 text-blue-600"}),(0,t.jsx)(M.Text,{className:"font-semibold text-gray-900",children:"Auto-Rotation"}),(0,t.jsx)(L.Badge,{color:e?"green":"gray",size:"xs",children:e?"Enabled":"Disabled"}),e&&a&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(M.Text,{className:"text-gray-400",children:"•"}),(0,t.jsxs)(M.Text,{className:"text-sm text-gray-600",children:["Every ",a]})]})]})}),(e||s||l||i)&&(0,t.jsxs)("div",{className:"space-y-3",children:[s&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-200 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-700",children:"Last Rotation"}),(0,t.jsx)(M.Text,{className:"text-sm text-gray-600",children:o(s)})]})]}),(l||i)&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-200 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-700",children:"Next Scheduled Rotation"}),(0,t.jsx)(M.Text,{className:"text-sm text-gray-600",children:o(i||l||"")})]})]}),e&&!s&&!l&&!i&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-100 rounded-md",children:[(0,t.jsx)(E,{className:"w-4 h-4 text-gray-500"}),(0,t.jsx)(M.Text,{className:"text-gray-600",children:"No rotation history available"})]})]}),!e&&!s&&!l&&!i&&(0,t.jsxs)("div",{className:"flex items-center gap-2 p-3 bg-gray-50 border border-gray-100 rounded-md",children:[(0,t.jsx)(R.RefreshIcon,{className:"w-4 h-4 text-gray-400"}),(0,t.jsx)(M.Text,{className:"text-gray-600",children:"Auto-rotation is not enabled for this key"})]})]});return"card"===r?(0,t.jsxs)("div",{className:`bg-white border border-gray-200 rounded-lg p-6 ${n}`,children:[(0,t.jsx)("div",{className:"flex items-center gap-2 mb-6",children:(0,t.jsxs)("div",{children:[(0,t.jsx)(M.Text,{className:"font-semibold text-gray-900",children:"Auto-Rotation"}),(0,t.jsx)(M.Text,{className:"text-xs text-gray-500",children:"Automatic key rotation settings and status for this key"})]})}),d]}):(0,t.jsxs)("div",{className:`${n}`,children:[(0,t.jsx)(M.Text,{className:"font-medium text-gray-900 mb-3",children:"Auto-Rotation"}),d]})}],505022);let P=["logging"];e.s(["extractLoggingSettings",0,e=>e&&"object"==typeof e&&Array.isArray(e.logging)?e.logging:[],"formatMetadataForDisplay",0,(e,t=2)=>JSON.stringify(e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([e])=>!P.includes(e))):{},null,t),"stripTagsFromMetadata",0,e=>{if(!e||"object"!=typeof e)return e;let{tags:t,...a}=e;return a}],721929)},65932,272753,e=>{"use strict";var t=e.i(954616),a=e.i(912598),s=e.i(602869),l=e.i(431703),i=e.i(135214),r=e.i(207082);let n=async(e,t)=>{let a=(0,s.getProxyBaseUrl)(),i=`${a?`${a}/key/${t}/reset_spend`:`/key/${t}/reset_spend`}`,r=await fetch(i,{method:"POST",headers:{[(0,s.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({reset_to:0})});if(!r.ok){let e=await r.json(),t=(0,l.deriveErrorMessage)(e);throw(0,s.handleError)(t),Error(t)}return r.json()};e.s(["useResetKeySpend",0,()=>{let{accessToken:e}=(0,i.default)(),s=(0,a.useQueryClient)();return(0,t.useMutation)({mutationFn:async t=>{if(!e)throw Error("Access token is required");return n(e,t)},onSuccess:()=>{s.invalidateQueries({queryKey:r.keyKeys.all})}})}],65932);var o=e.i(843476),d=e.i(492030),c=e.i(166406),m=e.i(772345),u=e.i(560445),x=e.i(464571),p=e.i(178654),g=e.i(525720),h=e.i(808613),_=e.i(311451),j=e.i(28651),y=e.i(212931),b=e.i(621192),f=e.i(770914),v=e.i(898586),k=e.i(271645),N=e.i(237016),T=e.i(727749),w=e.i(24529);let{Text:S}=v.Typography,C={pattern:/^(\d+(s|m|h|d|w|mo))?$/,message:"Must be a duration like 30s, 30m, 24h, 2d, 1w, or 1mo"};e.s(["RegenerateKeyModal",0,function({selectedToken:e,visible:t,onClose:a,onKeyUpdate:l}){let{accessToken:r}=(0,i.default)(),[n]=h.Form.useForm(),[v,I]=(0,k.useState)(null),[A,F]=(0,k.useState)(!1),[M,L]=(0,k.useState)(!1),R=(0,w.isKeyExpired)(e?.expires),E=h.Form.useWatch("duration",n),P=R?[{required:!0,message:"Expiration is required for expired keys"},C]:[C];(0,k.useEffect)(()=>{t&&e&&r&&n.setFieldsValue({key_alias:e.key_alias,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,duration:e.duration||"",grace_period:""})},[t,e,n,r]);let O=E?(0,w.calculateExpiryPreviewFromDuration)(E):null,B=async()=>{if(e&&r){F(!0);try{let t=await n.validateFields(),a=await (0,s.regenerateKeyCall)(r,e.token||e.token_id,t);I(a.key),T.default.success("Virtual Key regenerated successfully");let i={...a,token:a.token||a.key_id||e.token,key_name:a.key,max_budget:t.max_budget,tpm_limit:t.tpm_limit,rpm_limit:t.rpm_limit,expires:a.expires??e.expires};l&&l(i),F(!1)}catch(e){if(F(!1),e&&"object"==typeof e&&"errorFields"in e)return;console.error("Error regenerating key:",e),T.default.fromBackend(e)}}},D=()=>{I(null),F(!1),L(!1),n.resetFields(),a()};return(0,o.jsx)(y.Modal,{title:"Regenerate Virtual Key",open:t,onCancel:D,width:520,maskClosable:!1,footer:v?[(0,o.jsxs)(f.Space,{children:[(0,o.jsx)(x.Button,{onClick:D,children:"Close"}),(0,o.jsx)(N.CopyToClipboard,{text:v,onCopy:()=>{L(!0)},children:(0,o.jsx)(x.Button,{type:"primary",icon:M?(0,o.jsx)(d.CheckOutlined,{}):(0,o.jsx)(c.CopyOutlined,{}),children:M?"Copied":"Copy Key"})})]},"footer-actions")]:[(0,o.jsxs)(f.Space,{children:[(0,o.jsx)(x.Button,{onClick:D,children:"Cancel"}),(0,o.jsx)(x.Button,{type:"primary",icon:(0,o.jsx)(m.SyncOutlined,{}),onClick:B,loading:A,children:"Regenerate"})]},"footer-actions")],children:v?(0,o.jsxs)(g.Flex,{vertical:!0,gap:"middle",children:[(0,o.jsx)(u.Alert,{type:"warning",showIcon:!0,message:"Save it now, you will not see it again"}),(0,o.jsxs)(g.Flex,{vertical:!0,gap:2,children:[(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Key Alias"}),(0,o.jsx)(S,{children:e?.key_alias||"No alias set"})]}),(0,o.jsxs)(g.Flex,{vertical:!0,gap:6,children:[(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Virtual Key"}),(0,o.jsx)("div",{style:{background:"#f5f5f5",border:"1px solid #e8e8e8",borderRadius:6,padding:"14px 16px",fontFamily:"SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace",fontSize:16,wordBreak:"break-all",color:"#262626"},children:v})]})]}):(0,o.jsxs)(h.Form,{form:n,layout:"vertical",style:{marginTop:4},children:[(0,o.jsx)(h.Form.Item,{name:"key_alias",label:"Key Alias",children:(0,o.jsx)(_.Input,{disabled:!0})}),(0,o.jsxs)(b.Row,{gutter:12,children:[(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,o.jsx)(j.InputNumber,{step:.01,precision:2,style:{width:"100%"}})})}),(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"tpm_limit",label:"TPM Limit",children:(0,o.jsx)(j.InputNumber,{style:{width:"100%"}})})}),(0,o.jsx)(p.Col,{span:8,children:(0,o.jsx)(h.Form.Item,{name:"rpm_limit",label:"RPM Limit",children:(0,o.jsx)(j.InputNumber,{style:{width:"100%"}})})})]}),(0,o.jsxs)(b.Row,{gutter:12,children:[(0,o.jsx)(p.Col,{span:12,children:(0,o.jsx)(h.Form.Item,{name:"duration",label:"Expire Key",rules:P,extra:(0,o.jsxs)(g.Flex,{vertical:!0,gap:2,children:[(0,o.jsxs)(S,{type:R?"danger":"secondary",style:{fontSize:12},children:["Current expiry: ",e?.expires?(0,w.formatExpiresUtc)(e.expires):"Never",R&&" (expired)"]}),O&&(0,o.jsxs)(S,{type:"success",style:{fontSize:12},children:["New expiry: ",O]})]}),children:(0,o.jsx)(_.Input,{placeholder:"e.g. 30s, 30h, 30d"})})}),(0,o.jsx)(p.Col,{span:12,children:(0,o.jsx)(h.Form.Item,{name:"grace_period",label:"Grace Period",tooltip:"Keep the old key valid for this duration after rotation. Both keys work during this period for seamless cutover. Empty = immediate revoke.",extra:(0,o.jsx)(S,{type:"secondary",style:{fontSize:12},children:"Recommended: 24h to 72h for production keys"}),rules:[C],children:(0,o.jsx)(_.Input,{placeholder:"e.g. 24h, 2d"})})})]})]})})}],272753)},20147,e=>{"use strict";var t=e.i(843476),a=e.i(135214),s=e.i(510674),l=e.i(292639),i=e.i(214541),r=e.i(500330),n=e.i(11751),o=e.i(530212),d=e.i(389083),c=e.i(994388),m=e.i(304967),u=e.i(350967),x=e.i(197647),p=e.i(653824),g=e.i(881073),h=e.i(404206),_=e.i(723731),j=e.i(599724),y=e.i(629569),b=e.i(808613),f=e.i(212931),v=e.i(262218),k=e.i(784647),N=e.i(271645),T=e.i(708347),w=e.i(557662),S=e.i(505022),C=e.i(127952),I=e.i(721929),A=e.i(643449),F=e.i(727749),M=e.i(602869),L=e.i(65932),R=e.i(207082),E=e.i(912598),P=e.i(384767),O=e.i(272753),B=e.i(190702),D=e.i(891547),z=e.i(109799),K=e.i(921511),$=e.i(827252),U=e.i(779241),V=e.i(311451),W=e.i(199133),G=e.i(790848),q=e.i(592968),H=e.i(552130),J=e.i(9314),Q=e.i(392110),Y=e.i(844565),X=e.i(939510),Z=e.i(363256),ee=e.i(128233),et=e.i(319312),ea=e.i(833400),es=e.i(355619),el=e.i(75921),ei=e.i(234713),er=e.i(390605),en=e.i(702597),eo=e.i(435451),ed=e.i(183588),ec=e.i(916940);function em({keyData:e,onCancel:a,onSubmit:i,teams:r,accessToken:n,userID:o,userRole:d,premiumUser:m=!1}){let u=m||null!=d&&T.rolesWithWriteAccess.includes(d),[x]=b.Form.useForm(),[p,g]=(0,N.useState)([]),[h,_]=(0,N.useState)({}),j=r?.find(t=>t.team_id===e.team_id),[y,f]=(0,N.useState)([]),[v,k]=(0,N.useState)(Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[]),[S,C]=(0,N.useState)(e.organization_id||null),[A,L]=(0,N.useState)(e.auto_rotate||!1),[R,E]=(0,N.useState)(e.rotation_interval||""),[P,O]=(0,N.useState)(!e.expires),[B,eu]=(0,N.useState)(!1),[ex,ep]=(0,N.useState)(Array.isArray(e.budget_limits)?e.budget_limits:[]),[eg,eh]=(0,N.useState)((0,ea.tagLimitsToRows)(e.metadata?.tag_rpm_limit)),[e_,ej]=(0,N.useState)(e.budget_fallbacks&&"object"==typeof e.budget_fallbacks?e.budget_fallbacks:{}),{data:ey,isLoading:eb}=(0,z.useOrganizations)(),{data:ef}=(0,s.useProjects)(),{data:ev}=(0,l.useUISettings)(),ek=!!ev?.values?.enable_projects_ui,eN=!!e.project_id,eT=(()=>{if(!e.project_id)return null;let t=ef?.find(t=>t.project_id===e.project_id);return t?.project_alias?`${t.project_alias} (${e.project_id})`:e.project_id})();(0,N.useEffect)(()=>{let t=async()=>{if(o&&d&&n)try{if(null===e.team_id){let e=(await (0,M.modelAvailableCall)(n,o,d)).data.map(e=>e.id);f((0,es.excludeProxyWideSentinel)(e))}else if(j?.team_id){let e=await (0,en.fetchTeamModels)(o,d,n,j.team_id);f((0,es.excludeProxyWideSentinel)(Array.from(new Set([...j.models,...e]))))}}catch(e){console.error("Error fetching models:",e)}};(async()=>{if(n)try{let e=await (0,M.getPromptsList)(n);g(e.prompts.map(e=>e.prompt_id))}catch(e){console.error("Failed to fetch prompts:",e)}})(),t()},[o,d,n,j,e.team_id]),(0,N.useEffect)(()=>{x.setFieldValue("disabled_callbacks",v)},[x,v]);let ew=e=>e&&({"24h":"daily","7d":"weekly","30d":"monthly"})[e]||null,eS={...e,token:e.token||e.token_id,budget_duration:ew(e.budget_duration),metadata:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(e.metadata)),guardrails:e.metadata?.guardrails,disable_global_guardrails:e.metadata?.disable_global_guardrails||!1,throttle_on_budget_exceeded:e.metadata?.throttle_on_budget_exceeded||!1,prompts:e.metadata?.prompts,tags:e.metadata?.tags,vector_stores:e.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:e.object_permission?.mcp_servers||[],accessGroups:e.object_permission?.mcp_access_groups||[]},mcp_tool_permissions:e.object_permission?.mcp_tool_permissions||{},agents_and_groups:{agents:e.object_permission?.agents||[],accessGroups:e.object_permission?.agent_access_groups||[]},logging_settings:(0,I.extractLoggingSettings)(e.metadata),disabled_callbacks:Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[],access_group_ids:e.access_group_ids||[],auto_rotate:e.auto_rotate||!1,...e.rotation_interval&&{rotation_interval:e.rotation_interval},allowed_routes:Array.isArray(e.allowed_routes)&&e.allowed_routes.length>0?e.allowed_routes.join(", "):""};(0,N.useEffect)(()=>{x.setFieldsValue({...e,token:e.token||e.token_id,budget_duration:ew(e.budget_duration),metadata:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(e.metadata)),guardrails:e.metadata?.guardrails,disable_global_guardrails:e.metadata?.disable_global_guardrails||!1,prompts:e.metadata?.prompts,tags:e.metadata?.tags,vector_stores:e.object_permission?.vector_stores||[],mcp_servers_and_groups:{servers:e.object_permission?.mcp_servers||[],accessGroups:e.object_permission?.mcp_access_groups||[]},mcp_tool_permissions:e.object_permission?.mcp_tool_permissions||{},throttle_on_budget_exceeded:e.metadata?.throttle_on_budget_exceeded||!1,logging_settings:(0,I.extractLoggingSettings)(e.metadata),disabled_callbacks:Array.isArray(e.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(e.metadata.litellm_disabled_callbacks):[],access_group_ids:e.access_group_ids||[],auto_rotate:e.auto_rotate||!1,...e.rotation_interval&&{rotation_interval:e.rotation_interval},allowed_routes:Array.isArray(e.allowed_routes)&&e.allowed_routes.length>0?e.allowed_routes.join(", "):""})},[e,x]),(0,N.useEffect)(()=>{x.setFieldValue("auto_rotate",A)},[A,x]),(0,N.useEffect)(()=>{R&&x.setFieldValue("rotation_interval",R)},[R,x]),(0,N.useEffect)(()=>{(async()=>{if(n)try{let e=await (0,M.tagListCall)(n);_(e)}catch(e){F.default.fromBackend("Error fetching tags: "+e)}})()},[n]);let eC=async t=>{try{if(eu(!0),"string"==typeof t.allowed_routes){let e=t.allowed_routes.trim();""===e?t.allowed_routes=[]:t.allowed_routes=e.split(",").map(e=>e.trim()).filter(e=>e.length>0)}let a=new Set(Array.isArray(e.allowed_routes)?e.allowed_routes:[]),s=new Set(Array.isArray(t.allowed_routes)?t.allowed_routes:[]);a.size===s.size&&[...s].every(e=>a.has(e))&&delete t.allowed_routes,P&&(t.duration=null);let l=ex.filter(e=>e.budget_duration&&null!==e.max_budget&&void 0!==e.max_budget);l.length>0?t.budget_limits=l:0===ex.length&&(t.budget_limits=[]);let{tag_rpm_limit:r}=(0,ea.tagRowsToLimits)(eg);t.tag_rpm_limit=r;let n=null!=e.budget_fallbacks&&Object.keys(e.budget_fallbacks).length>0;Object.keys(e_).length>0?t.budget_fallbacks=e_:n&&(t.budget_fallbacks={}),await i(t)}finally{eu(!1)}};return(0,t.jsxs)(b.Form,{form:x,onFinish:eC,initialValues:eS,layout:"vertical",children:[(0,t.jsx)(b.Form.Item,{label:"Key Alias",name:"key_alias",children:(0,t.jsx)(U.TextInput,{})}),(0,t.jsx)(b.Form.Item,{label:"Models",name:"models",children:(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_routes!==t.allowed_routes||e.models!==t.models,children:({getFieldValue:a,setFieldValue:s})=>{let l=a("allowed_routes")||"",i="string"==typeof l&&""!==l.trim()?l.split(",").map(e=>e.trim()).filter(e=>e.length>0):[],r=i.includes("management_routes")||i.includes("info_routes"),n=a("models")||[];return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(W.Select,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},disabled:r,value:r?[]:n,onChange:e=>{e.includes("all-team-models")?s("models",["all-team-models"]):e.includes("all-proxy-models")?s("models",["all-proxy-models"]):s("models",e)},children:[null!=e.team_id?null!=j&&(0,t.jsx)(W.Select.Option,{value:"all-team-models",children:"All Team Models"}):(0,t.jsx)(W.Select.Option,{value:"all-proxy-models",children:"All Proxy Models"}),y.map(e=>(0,t.jsx)(W.Select.Option,{value:e,disabled:(0,es.hasAllModelsSentinel)(n),children:e},e))]}),r&&(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Models field is disabled for this key type"})]})}})}),(0,t.jsx)(b.Form.Item,{label:"Key Type",children:(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.allowed_routes!==t.allowed_routes,children:({getFieldValue:e,setFieldValue:a})=>{var s;let l=e("allowed_routes")||"",i=(s="string"==typeof l&&""!==l.trim()?l.split(",").map(e=>e.trim()).filter(e=>e.length>0):[])&&0!==s.length?s.includes("llm_api_routes")?"llm_api":s.includes("management_routes")?"management":s.includes("info_routes")?"read_only":"default":"default";return(0,t.jsxs)(W.Select,{placeholder:"Select key type",style:{width:"100%"},optionLabelProp:"label",value:i,onChange:e=>{switch(e){case"default":a("allowed_routes","");break;case"llm_api":a("allowed_routes","llm_api_routes");break;case"management":a("allowed_routes","management_routes"),a("models",[])}},children:[(0,t.jsx)(W.Select.Option,{value:"default",label:"Full Access",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Full Access"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call all routes (AI APIs, Management, and read-only)"})]})}),(0,t.jsx)(W.Select.Option,{value:"llm_api",label:"AI APIs",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"AI APIs"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call only AI API routes (chat/completions, embeddings, etc.)"})]})}),(0,t.jsx)(W.Select.Option,{value:"management",label:"Management",children:(0,t.jsxs)("div",{style:{padding:"4px 0"},children:[(0,t.jsx)("div",{style:{fontWeight:500},children:"Management"}),(0,t.jsx)("div",{style:{fontSize:"11px",color:"#6b7280",marginTop:"2px"},children:"Can call only management routes (user/team/key management)"})]})})]})}})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Allowed Routes"," ",(0,t.jsx)(q.Tooltip,{title:"List of allowed routes for the key (comma-separated). Can be specific routes (e.g., '/chat/completions') or route patterns (e.g., 'llm_api_routes', 'management_routes', '/keys/*'). Leave empty to allow all routes.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"allowed_routes",children:(0,t.jsx)(V.Input,{placeholder:"Enter allowed routes (comma-separated). Special values: llm_api_routes, management_routes. Examples: llm_api_routes, /chat/completions, /keys/*. Leave empty to allow all routes"})}),(0,t.jsx)(b.Form.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,t.jsx)(eo.default,{step:.01,style:{width:"100%"},placeholder:"Enter a numerical value"})}),(0,t.jsx)(b.Form.Item,{label:"Reset Budget",name:"budget_duration",children:(0,t.jsxs)(W.Select,{placeholder:"n/a",children:[(0,t.jsx)(W.Select.Option,{value:"daily",children:"Daily"}),(0,t.jsx)(W.Select.Option,{value:"weekly",children:"Weekly"}),(0,t.jsx)(W.Select.Option,{value:"monthly",children:"Monthly"})]})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Budget Windows"," ",(0,t.jsx)(q.Tooltip,{title:"Set multiple independent budget windows (e.g., hourly $10 AND monthly $200). Each window tracks spend separately and resets on its own schedule.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(et.BudgetWindowsEditor,{value:ex,onChange:ep})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Budget Fallbacks"," ",(0,t.jsx)(q.Tooltip,{title:"When a model exceeds its per-model budget, requests automatically reroute to fallback models instead of failing",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(ee.BudgetFallbacksEditor,{value:e_,onChange:ej,availableModels:y})}),(0,t.jsx)(b.Form.Item,{label:"TPM Limit",name:"tpm_limit",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(X.default,{type:"tpm",name:"tpm_limit_type",showDetailedDescriptions:!1}),(0,t.jsx)(b.Form.Item,{label:"RPM Limit",name:"rpm_limit",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(X.default,{type:"rpm",name:"rpm_limit_type",showDetailedDescriptions:!1}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Throttle on budget exceeded"," ",(0,t.jsx)(q.Tooltip,{title:"When this key exceeds its max budget, throttle its TPM/RPM to the globally configured percentage instead of blocking access entirely. Requires budget_exceeded_throttle_percentage in litellm_settings and a TPM/RPM limit on the key.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"throttle_on_budget_exceeded",valuePropName:"checked",children:(0,t.jsx)(G.Switch,{checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(b.Form.Item,{label:"Max Parallel Requests",name:"max_parallel_requests",children:(0,t.jsx)(eo.default,{min:0})}),(0,t.jsx)(b.Form.Item,{label:"Model TPM Limit",name:"model_tpm_limit",children:(0,t.jsx)(V.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}'})}),(0,t.jsx)(b.Form.Item,{label:"Model RPM Limit",name:"model_rpm_limit",children:(0,t.jsx)(V.Input.TextArea,{rows:4,placeholder:'{"gpt-4": 100, "claude-v1": 200}'})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Per-Tag Rate Limits"," ",(0,t.jsx)(q.Tooltip,{title:"Scope rate limits to a request tag so each tag (e.g. a cell or group) gets its own RPM counter. Requests without a matching tag fall back to the key-level limit.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),children:(0,t.jsx)(ea.TagRateLimitEditor,{value:eg,onChange:eh})}),(0,t.jsx)(b.Form.Item,{label:"Guardrails",name:"guardrails",children:n&&(0,t.jsx)(D.default,{onChange:e=>{x.setFieldValue("guardrails",e)},accessToken:n,disabled:!u})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Disable Global Guardrails"," ",(0,t.jsx)(q.Tooltip,{title:"When enabled, this key will bypass any guardrails configured to run on every request (global guardrails)",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"disable_global_guardrails",valuePropName:"checked",children:(0,t.jsx)(G.Switch,{disabled:!u,checkedChildren:"Yes",unCheckedChildren:"No"})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Policies"," ",(0,t.jsx)(q.Tooltip,{title:"Apply policies to this key to control guardrails and other settings",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"policies",children:n&&(0,t.jsx)(K.default,{onChange:e=>{x.setFieldValue("policies",e)},accessToken:n,disabled:!m})}),(0,t.jsx)(b.Form.Item,{label:"Tags",name:"tags",children:(0,t.jsx)(W.Select,{mode:"tags",style:{width:"100%"},placeholder:"Select or enter tags",options:Object.values(h).map(e=>({value:e.name,label:e.name,title:e.description||e.name}))})}),(0,t.jsx)(b.Form.Item,{label:"Prompts",name:"prompts",children:(0,t.jsx)(q.Tooltip,{title:m?"":"Setting prompts by key is a premium feature",placement:"top",children:(0,t.jsx)(W.Select,{mode:"tags",style:{width:"100%"},disabled:!m,placeholder:m?Array.isArray(e.metadata?.prompts)&&e.metadata.prompts.length>0?`Current: ${e.metadata.prompts.join(", ")}`:"Select or enter prompts":"Premium feature - Upgrade to set prompts by key",options:p.map(e=>({value:e,label:e}))})})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Access Groups"," ",(0,t.jsx)(q.Tooltip,{title:"Assign access groups to this key. Access groups control which models, MCP servers, and agents this key can use",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"access_group_ids",children:(0,t.jsx)(J.default,{placeholder:"Select access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{label:"Allowed Pass Through Routes",name:"allowed_passthrough_routes",children:(0,t.jsx)(q.Tooltip,{title:m?"":"Setting allowed pass through routes by key is a premium feature",placement:"top",children:(0,t.jsx)(Y.default,{onChange:e=>x.setFieldValue("allowed_passthrough_routes",e),value:x.getFieldValue("allowed_passthrough_routes"),accessToken:n||"",placeholder:m?Array.isArray(e.metadata?.allowed_passthrough_routes)&&e.metadata.allowed_passthrough_routes.length>0?`Current: ${e.metadata.allowed_passthrough_routes.join(", ")}`:"Select or enter allowed pass through routes":"Premium feature - Upgrade to set allowed pass through routes by key",disabled:!m})})}),(0,t.jsx)(b.Form.Item,{label:"Vector Stores",name:"vector_stores",children:(0,t.jsx)(ec.default,{onChange:e=>x.setFieldValue("vector_stores",e),value:x.getFieldValue("vector_stores"),accessToken:n||"",placeholder:"Select vector stores"})}),(0,t.jsx)(b.Form.Item,{label:"MCP Servers / Access Groups",name:"mcp_servers_and_groups",children:(0,t.jsx)(el.default,{onChange:e=>x.setFieldValue("mcp_servers_and_groups",e),value:x.getFieldValue("mcp_servers_and_groups"),accessToken:n||"",placeholder:"Select MCP servers or access groups (optional)",allowNoMcpServers:!0})}),(0,t.jsx)(b.Form.Item,{name:"mcp_tool_permissions",initialValue:{},hidden:!0,children:(0,t.jsx)(V.Input,{type:"hidden"})}),(0,t.jsx)(b.Form.Item,{noStyle:!0,shouldUpdate:(e,t)=>e.mcp_servers_and_groups!==t.mcp_servers_and_groups||e.mcp_tool_permissions!==t.mcp_tool_permissions,children:()=>(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsx)(er.default,{accessToken:n||"",selectedServers:(x.getFieldValue("mcp_servers_and_groups")?.servers||[]).filter(e=>e!==ei.NO_MCP_SERVERS_SENTINEL),toolPermissions:x.getFieldValue("mcp_tool_permissions")||{},onChange:e=>x.setFieldsValue({mcp_tool_permissions:e})})})}),(0,t.jsx)(b.Form.Item,{label:"Agents / Access Groups",name:"agents_and_groups",children:(0,t.jsx)(H.default,{onChange:e=>x.setFieldValue("agents_and_groups",e),value:x.getFieldValue("agents_and_groups"),accessToken:n||"",placeholder:"Select agents or access groups (optional)"})}),(0,t.jsx)(b.Form.Item,{label:(0,t.jsxs)("span",{children:["Organization"," ",(0,t.jsx)(q.Tooltip,{title:"The organization this key belongs to. Selecting an organization filters the available teams.",children:(0,t.jsx)($.InfoCircleOutlined,{style:{marginLeft:"4px"}})})]}),name:"organization_id",children:(0,t.jsx)(Z.default,{organizations:ey,loading:eb,disabled:"Admin"!==d,onChange:e=>{C(e||null),x.setFieldValue("team_id",void 0)}})}),(0,t.jsx)(b.Form.Item,{label:"Team ID",name:"team_id",help:ek&&eN?"Team is locked because this key belongs to a project":void 0,children:(0,t.jsx)(W.Select,{placeholder:"Select team",showSearch:!0,disabled:ek&&eN,style:{width:"100%"},onChange:e=>{let t=r?.find(t=>t.team_id===e)||null;t?.organization_id?(C(t.organization_id),x.setFieldValue("organization_id",t.organization_id)):e||(C(null),x.setFieldValue("organization_id",void 0))},filterOption:(e,t)=>{let a=S?r?.filter(e=>e.organization_id===S):r,s=a?.find(e=>e.team_id===t?.value);return!!s&&(s.team_alias?.toLowerCase().includes(e.toLowerCase())??!1)},children:(S?r?.filter(e=>e.organization_id===S):r)?.map(e=>(0,t.jsx)(W.Select.Option,{value:e.team_id,children:`${e.team_alias} (${e.team_id})`},e.team_id))})}),ek&&eN&&(0,t.jsx)(b.Form.Item,{label:"Project",children:(0,t.jsx)(V.Input,{value:eT??"",disabled:!0})}),(0,t.jsx)(b.Form.Item,{label:"Logging Settings",name:"logging_settings",children:(0,t.jsx)(ed.default,{value:x.getFieldValue("logging_settings"),onChange:e=>x.setFieldValue("logging_settings",e),disabledCallbacks:v,onDisabledCallbacksChange:e=>{k((0,w.mapInternalToDisplayNames)(e)),x.setFieldValue("disabled_callbacks",e)}})}),(0,t.jsx)(b.Form.Item,{label:"Metadata",name:"metadata",children:(0,t.jsx)(V.Input.TextArea,{rows:10})}),(0,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)(Q.default,{form:x,autoRotationEnabled:A,onAutoRotationChange:L,rotationInterval:R,onRotationIntervalChange:E,neverExpire:P,onNeverExpireChange:O}),(0,t.jsx)(b.Form.Item,{name:"duration",hidden:!0,initialValue:"",children:(0,t.jsx)(V.Input,{})})]}),(0,t.jsx)(b.Form.Item,{name:"token",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"disabled_callbacks",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"auto_rotate",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)(b.Form.Item,{name:"rotation_interval",hidden:!0,children:(0,t.jsx)(V.Input,{})}),(0,t.jsx)("div",{className:"sticky z-10 bg-white p-4 border-t border-gray-200 -bottom-6 -inset-x-6",children:(0,t.jsxs)("div",{className:"flex justify-end items-center gap-2",children:[(0,t.jsx)(c.Button,{variant:"secondary",onClick:a,disabled:B,children:"Cancel"}),(0,t.jsx)(c.Button,{type:"submit",loading:B,children:"Save Changes"})]})})]})}let eu=["policies","guardrails","prompts","tags","allowed_passthrough_routes"],ex=e=>null==e||Array.isArray(e)&&0===e.length||"string"==typeof e&&""===e.trim();e.s(["default",0,function({onClose:e,keyData:D,teams:z,onKeyDataUpdate:K,onDelete:$,backButtonText:U="Back to Keys"}){let V,{accessToken:W,userId:G,userRole:q,premiumUser:H}=(0,a.default)(),J=(0,E.useQueryClient)(),Q=H||null!=q&&T.rolesWithWriteAccess.includes(q),{teams:Y}=(0,i.default)(),{data:X}=(0,s.useProjects)(),{data:Z}=(0,l.useUISettings)(),ee=!!Z?.values?.enable_projects_ui,[et,ea]=(0,N.useState)(!1),[es]=b.Form.useForm(),[el,ei]=(0,N.useState)(!1),[er,en]=(0,N.useState)(!1),[eo,ed]=(0,N.useState)(""),[ec,ep]=(0,N.useState)(!1),[eg,eh]=(0,N.useState)(!1),{mutate:e_,isPending:ej}=(0,L.useResetKeySpend)(),[ey,eb]=(0,N.useState)(D),[ef,ev]=(0,N.useState)(null),[ek,eN]=(0,N.useState)(!1),[eT,ew]=(0,N.useState)({}),[eS,eC]=(0,N.useState)(!1);if((0,N.useEffect)(()=>{D&&eb(D)},[D]),(0,N.useEffect)(()=>{(async()=>{let e=ey?.metadata?.policies;if(!W||!e||!Array.isArray(e)||0===e.length)return;eC(!0);let t={};try{await Promise.all(e.map(async e=>{try{let a=await (0,M.getPolicyInfoWithGuardrails)(W,e);t[e]=a.resolved_guardrails||[]}catch(a){console.error(`Failed to fetch guardrails for policy ${e}:`,a),t[e]=[]}})),ew(t)}catch(e){console.error("Failed to fetch policy guardrails:",e)}finally{eC(!1)}})()},[W,ey?.metadata?.policies]),(0,N.useEffect)(()=>{if(ek){let e=setTimeout(()=>{eN(!1)},5e3);return()=>clearTimeout(e)}},[ek]),!ey)return(0,t.jsxs)("div",{className:"p-4",children:[(0,t.jsx)(c.Button,{icon:o.ArrowLeftIcon,variant:"light",onClick:e,className:"mb-4",children:U}),(0,t.jsx)(j.Text,{children:"Key not found"})]});let eI=async e=>{try{if(!W)return;let t=e.token;for(let a of(e.key=t,Q||(delete e.guardrails,delete e.prompts),eu)){let t=ey.metadata?.[a]??ey[a];ex(e[a])&&ex(t)&&delete e[a]}let a=!!ey.metadata?.disable_global_guardrails;if(!!e.disable_global_guardrails===a&&delete e.disable_global_guardrails,e.max_budget=(0,n.mapEmptyStringToNull)(e.max_budget),void 0!==e.vector_stores&&(e.object_permission={...ey.object_permission,vector_stores:e.vector_stores||[]},delete e.vector_stores),void 0!==e.mcp_servers_and_groups){let{servers:t,accessGroups:a,toolsets:s}=e.mcp_servers_and_groups||{servers:[],accessGroups:[],toolsets:[]};e.object_permission={...ey.object_permission,mcp_servers:t||[],mcp_access_groups:a||[],mcp_toolsets:s||[]},delete e.mcp_servers_and_groups}if(void 0!==e.mcp_tool_permissions){let t=e.mcp_tool_permissions||{};Object.keys(t).length>0&&(e.object_permission={...e.object_permission,mcp_tool_permissions:t}),delete e.mcp_tool_permissions}if(void 0!==e.agents_and_groups){let{agents:t,accessGroups:a}=e.agents_and_groups||{agents:[],accessGroups:[]};e.object_permission={...e.object_permission,agents:t||[],agent_access_groups:a||[]},delete e.agents_and_groups}if(e.max_budget=(0,n.mapEmptyStringToNull)(e.max_budget),e.tpm_limit=(0,n.mapEmptyStringToNull)(e.tpm_limit),e.rpm_limit=(0,n.mapEmptyStringToNull)(e.rpm_limit),e.max_parallel_requests=(0,n.mapEmptyStringToNull)(e.max_parallel_requests),e.metadata&&"string"==typeof e.metadata)try{let t=JSON.parse(e.metadata);"tags"in t&&delete t.tags,e.metadata={...t,...Array.isArray(e.tags)&&e.tags.length>0?{tags:e.tags}:{},...e.guardrails?.length>0?{guardrails:e.guardrails}:{},...Array.isArray(e.logging_settings)&&e.logging_settings.length>0?{logging:e.logging_settings}:{},...e.disabled_callbacks?.length>0?{litellm_disabled_callbacks:(0,w.mapDisplayToInternalNames)(e.disabled_callbacks)}:{}}}catch(e){console.error("Error parsing metadata JSON:",e),F.default.error("Invalid metadata JSON");return}else{let{tags:t,...a}=e.metadata||{};e.metadata={...a,...Array.isArray(e.tags)&&e.tags.length>0?{tags:e.tags}:{},...e.guardrails?.length>0?{guardrails:e.guardrails}:{},...Array.isArray(e.logging_settings)&&e.logging_settings.length>0?{logging:e.logging_settings}:{},...e.disabled_callbacks?.length>0?{litellm_disabled_callbacks:(0,w.mapDisplayToInternalNames)(e.disabled_callbacks)}:{}}}"tags"in e&&delete e.tags,delete e.logging_settings,e.budget_duration&&(e.budget_duration=({daily:"24h",weekly:"7d",monthly:"30d"})[e.budget_duration]);let s=await (0,M.keyUpdateCall)(W,e);eb(e=>e?{...e,...s}:void 0),K&&K(s),F.default.success("Key updated successfully"),ea(!1)}catch(e){F.default.fromBackend((0,B.parseErrorMessage)(e)),console.error("Error updating key:",e)}},eA=async()=>{try{if(en(!0),!W)return;await (0,M.keyDeleteCall)(W,ey.token||ey.token_id),F.default.success("Key deleted successfully"),await J.invalidateQueries({queryKey:R.keyKeys.lists()}),$&&$(),e()}catch(e){console.error("Error deleting the key:",e),F.default.fromBackend(e)}finally{en(!1),ei(!1),ed("")}},eF=e=>{let t=new Date(e),a=t.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),s=t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0});return`${a} at ${s}`},eM=(0,T.isProxyAdminRole)(q||"")||Y&&(0,T.isUserTeamAdminForSingleTeam)(Y?.filter(e=>e.team_id===ey.team_id)[0]?.members_with_roles,G||"")||G===ey.user_id&&"Internal Viewer"!==q,eL=(0,T.isProxyAdminRole)(q||"")||Y&&(0,T.isUserTeamAdminForSingleTeam)(Y?.filter(e=>e.team_id===ey.team_id)[0]?.members_with_roles,G||""),eR=ey.team_id?Y?.find(e=>e.team_id===ey.team_id):null,eE=null!==ey.max_budget?`$${(0,r.formatNumberWithCommas)(ey.max_budget,2)}`:eR?.max_budget!=null?`$${(0,r.formatNumberWithCommas)(eR.max_budget,2)} (Team: ${eR.team_alias||eR.team_id}${eR.budget_duration?` / ${eR.budget_duration}`:""})`:"Unlimited";return(0,t.jsxs)("div",{className:"w-full h-full overflow-y-auto p-4",children:[(0,t.jsx)(k.KeyInfoHeader,{data:{keyName:ey.key_alias||"Virtual Key",keyId:ey.token_id||ey.token,userId:ey.user_id||"",userEmail:ey.user_email||"",userAlias:ey.user?.user_alias??null,createdBy:ey.created_by_user?.user_alias||ey.created_by_user?.user_email||ey.created_by||"",createdAt:ey.created_at?eF(ey.created_at):"",lastUpdated:ey.updated_at?eF(ey.updated_at):"",lastActive:ey.last_active?eF(ey.last_active):"Never",expires:ey.expires?eF(ey.expires):"Never"},onBack:e,onRegenerate:()=>ep(!0),onDelete:()=>ei(!0),onResetSpend:eL?()=>eh(!0):void 0,canModifyKey:eM,backButtonText:U,regenerateDisabled:!H,regenerateTooltip:H?void 0:"This is a LiteLLM Enterprise feature, and requires a valid key to use."}),(0,t.jsx)(O.RegenerateKeyModal,{selectedToken:ey,visible:ec,onClose:()=>ep(!1),onKeyUpdate:e=>{eb(t=>{if(t)return{...t,...e,created_at:new Date().toLocaleString()}}),ev(new Date),eN(!0),K&&K({...e,created_at:new Date().toLocaleString()})}}),(0,t.jsx)(C.default,{isOpen:el,title:"Delete Key",alertMessage:"This action is irreversible and will immediately revoke access for any applications using this key.",message:"Are you sure you want to delete this Virtual Key?",resourceInformationTitle:"Key Information",resourceInformation:[{label:"Key Alias",value:ey?.key_alias||"-"},{label:"Key ID",value:ey?.token_id||ey?.token||"-",code:!0},{label:"Team ID",value:ey?.team_id||"-",code:!0},{label:"Spend",value:ey?.spend?`$${(0,r.formatNumberWithCommas)(ey.spend,4)}`:"$0.0000"}],onCancel:()=>{ei(!1),ed("")},onOk:eA,confirmLoading:er,requiredConfirmation:ey?.key_alias}),(0,t.jsxs)(f.Modal,{title:"Reset Key Spend",open:eg,onOk:()=>{e_(ey.token||ey.token_id,{onSuccess:()=>{eb(e=>e?{...e,spend:0}:void 0),K&&K({spend:0}),F.default.success("Key spend reset to $0"),eh(!1)},onError:e=>{F.default.fromBackend((0,B.parseErrorMessage)(e)),console.error("Error resetting key spend:",e)}})},onCancel:()=>eh(!1),okText:"Reset",okButtonProps:{danger:!0},confirmLoading:ej,children:[(0,t.jsxs)("p",{children:["Reset spend for ",(0,t.jsx)("strong",{children:ey?.key_alias||ey?.token_id||"this key"})," to"," ",(0,t.jsx)("strong",{children:"$0"}),"?"]}),(0,t.jsxs)("p",{style:{color:"#666",fontSize:"0.875rem",marginTop:8},children:["Current spend: ",(0,t.jsxs)("strong",{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)]}),". Spend history is preserved in logs. This resets the current period spend counter, the same as an automatic budget reset."]})]}),(0,t.jsxs)(p.TabGroup,{children:[(0,t.jsxs)(g.TabList,{className:"mb-4",children:[(0,t.jsx)(x.Tab,{children:"Overview"}),(0,t.jsx)(x.Tab,{children:"Settings"})]}),(0,t.jsxs)(_.TabPanels,{children:[(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(u.Grid,{numItems:1,numItemsSm:2,numItemsLg:3,className:"gap-6",children:[(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Spend"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(y.Title,{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)]}),(0,t.jsxs)(j.Text,{children:["of ",eE]})]})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Rate Limits"}),(0,t.jsxs)("div",{className:"mt-2",children:[(0,t.jsxs)(j.Text,{children:["TPM: ",null!==ey.tpm_limit?ey.tpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["RPM: ",null!==ey.rpm_limit?ey.rpm_limit:"Unlimited"]}),!!ey.metadata?.throttle_on_budget_exceeded&&(0,t.jsx)(j.Text,{children:"Throttle on budget exceeded: Yes"})]})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{children:"Models"}),(0,t.jsx)("div",{className:"mt-2 flex flex-wrap gap-2",children:ey.models&&ey.models.length>0?ey.models.map((e,a)=>(0,t.jsx)(d.Badge,{color:"red",children:e},a)):(0,t.jsx)(j.Text,{children:"No models specified"})})]}),(0,t.jsx)(m.Card,{children:(0,t.jsx)(P.default,{objectPermission:ey.object_permission,variant:"inline",accessToken:W})}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{className:"font-medium mb-3",children:"Guardrails"}),Array.isArray(ey.metadata?.guardrails)&&ey.metadata.guardrails.length>0?(0,t.jsx)("div",{className:"flex flex-wrap gap-2",children:ey.metadata.guardrails.map((e,a)=>(0,t.jsx)(d.Badge,{color:"blue",children:e},a))}):(0,t.jsx)(j.Text,{className:"text-gray-500",children:"No guardrails configured"}),"boolean"==typeof ey.metadata?.disable_global_guardrails&&!0===ey.metadata.disable_global_guardrails&&(0,t.jsx)("div",{className:"mt-3 pt-3 border-t border-gray-200",children:(0,t.jsx)(d.Badge,{color:"yellow",children:"Global Guardrails Disabled"})})]}),(0,t.jsxs)(m.Card,{children:[(0,t.jsx)(j.Text,{className:"font-medium mb-3",children:"Policies"}),Array.isArray(ey.metadata?.policies)&&ey.metadata.policies.length>0?(0,t.jsx)("div",{className:"space-y-4",children:ey.metadata.policies.map((e,a)=>(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(d.Badge,{color:"purple",children:e}),eS&&(0,t.jsx)(j.Text,{className:"text-xs text-gray-400",children:"Loading guardrails..."})]}),!eS&&eT[e]&&eT[e].length>0&&(0,t.jsxs)("div",{className:"ml-4 pl-3 border-l-2 border-gray-200",children:[(0,t.jsx)(j.Text,{className:"text-xs text-gray-500 mb-1",children:"Resolved Guardrails:"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-1",children:eT[e].map((e,a)=>(0,t.jsx)(d.Badge,{color:"blue",size:"xs",children:e},a))})]})]},a))}):(0,t.jsx)(j.Text,{className:"text-gray-500",children:"No policies configured"})]}),(0,t.jsx)(A.default,{loggingConfigs:(0,I.extractLoggingSettings)(ey.metadata),disabledCallbacks:Array.isArray(ey.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(ey.metadata.litellm_disabled_callbacks):[],variant:"card"}),(0,t.jsx)(S.default,{autoRotate:ey.auto_rotate,rotationInterval:ey.rotation_interval,lastRotationAt:ey.last_rotation_at,keyRotationAt:ey.key_rotation_at,nextRotationAt:ey.next_rotation_at,variant:"card"})]})}),(0,t.jsx)(h.TabPanel,{children:(0,t.jsxs)(m.Card,{children:[(0,t.jsxs)("div",{className:"flex justify-between items-center mb-4",children:[(0,t.jsx)(y.Title,{children:"Key Settings"}),!et&&eM&&(0,t.jsx)(c.Button,{onClick:()=>ea(!0),children:"Edit Settings"})]}),et?(0,t.jsx)(em,{keyData:ey,onCancel:()=>ea(!1),onSubmit:eI,teams:z,accessToken:W,userID:G,userRole:q,premiumUser:H}):(0,t.jsxs)("div",{className:"space-y-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Key ID"}),(0,t.jsx)(j.Text,{className:"font-mono",children:ey.token_id||ey.token})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Key Alias"}),(0,t.jsx)(j.Text,{children:ey.key_alias||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Secret Key"}),(0,t.jsx)(j.Text,{className:"font-mono",children:ey.key_name})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Team ID"}),(0,t.jsx)(j.Text,{children:ey.team_id||"Not Set"})]}),ee&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Project"}),(0,t.jsx)(j.Text,{children:ey.project_id?(V=X?.find(e=>e.project_id===ey.project_id),V?.project_alias?`${V.project_alias} (${ey.project_id})`:ey.project_id):"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Organization"}),(0,t.jsx)(j.Text,{children:(ey.organization_id??ey.org_id)||"Not Set"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Created"}),(0,t.jsx)(j.Text,{children:eF(ey.created_at)})]}),ef&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Last Regenerated"}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(j.Text,{children:eF(ef)}),(0,t.jsx)(d.Badge,{color:"green",size:"xs",children:"Recent"})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Expires"}),(0,t.jsx)(j.Text,{children:ey.expires?eF(ey.expires):"Never"})]}),(0,t.jsx)(S.default,{autoRotate:ey.auto_rotate,rotationInterval:ey.rotation_interval,lastRotationAt:ey.last_rotation_at,keyRotationAt:ey.key_rotation_at,nextRotationAt:ey.next_rotation_at,variant:"inline",className:"pt-4 border-t border-gray-200"}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Spend"}),(0,t.jsxs)(j.Text,{children:["$",(0,r.formatNumberWithCommas)(ey.spend,4)," USD"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Budget"}),(0,t.jsx)(j.Text,{children:null!==ey.max_budget?`$${(0,r.formatNumberWithCommas)(ey.max_budget,2)}`:"Unlimited"})]}),ey.budget_fallbacks&&Object.keys(ey.budget_fallbacks).length>0&&(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Budget Fallbacks"}),(0,t.jsx)("div",{className:"mt-1 space-y-1",children:Object.entries(ey.budget_fallbacks).map(([e,a])=>(0,t.jsxs)("div",{className:"text-xs text-gray-600",children:[(0,t.jsx)("span",{className:"font-medium",children:e}),(0,t.jsx)("span",{className:"mx-1 text-gray-400",children:"->"}),a.join(", ")]},e))})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Tags"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:Array.isArray(ey.metadata?.tags)&&ey.metadata.tags.length>0?ey.metadata.tags.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No tags specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Prompts"}),(0,t.jsx)(j.Text,{children:Array.isArray(ey.metadata?.prompts)&&ey.metadata.prompts.length>0?ey.metadata.prompts.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No prompts specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Allowed Routes"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:Array.isArray(ey.allowed_routes)&&ey.allowed_routes.length>0?ey.allowed_routes.map((e,a)=>(0,t.jsx)("span",{className:"px-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):(0,t.jsx)(v.Tag,{color:"green",children:"All routes allowed"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Allowed Pass Through Routes"}),(0,t.jsx)(j.Text,{children:Array.isArray(ey.metadata?.allowed_passthrough_routes)&&ey.metadata.allowed_passthrough_routes.length>0?ey.metadata.allowed_passthrough_routes.map((e,a)=>(0,t.jsx)("span",{className:"px-2 mr-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):"No pass through routes specified"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Disable Global Guardrails"}),(0,t.jsx)(j.Text,{children:ey.metadata?.disable_global_guardrails===!0?(0,t.jsx)(d.Badge,{color:"yellow",children:"Enabled - Global guardrails bypassed"}):(0,t.jsx)(d.Badge,{color:"green",children:"Disabled - Global guardrails active"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Models"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-2 mt-1",children:ey.models&&ey.models.length>0?ey.models.map((e,a)=>(0,t.jsx)("span",{className:"px-2 py-1 bg-blue-100 rounded-sm text-xs",children:e},a)):(0,t.jsx)(j.Text,{children:"No models specified"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Rate Limits"}),(0,t.jsxs)(j.Text,{children:["TPM: ",null!==ey.tpm_limit?ey.tpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["RPM: ",null!==ey.rpm_limit?ey.rpm_limit:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Max Parallel Requests:"," ",null!==ey.max_parallel_requests?ey.max_parallel_requests:"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Model TPM Limits:"," ",ey.metadata?.model_tpm_limit?JSON.stringify(ey.metadata.model_tpm_limit):"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Model RPM Limits:"," ",ey.metadata?.model_rpm_limit?JSON.stringify(ey.metadata.model_rpm_limit):"Unlimited"]}),(0,t.jsxs)(j.Text,{children:["Tag RPM Limits:"," ",ey.metadata?.tag_rpm_limit&&Object.keys(ey.metadata.tag_rpm_limit).length>0?JSON.stringify(ey.metadata.tag_rpm_limit):"Unlimited"]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(j.Text,{className:"font-medium",children:"Metadata"}),(0,t.jsx)("pre",{className:"bg-gray-100 p-2 rounded-sm text-xs overflow-auto mt-1",children:(0,I.formatMetadataForDisplay)((0,I.stripTagsFromMetadata)(ey.metadata))})]}),(0,t.jsx)(P.default,{objectPermission:ey.object_permission,variant:"inline",className:"pt-4 border-t border-gray-200",accessToken:W}),(0,t.jsx)(A.default,{loggingConfigs:(0,I.extractLoggingSettings)(ey.metadata),disabledCallbacks:Array.isArray(ey.metadata?.litellm_disabled_callbacks)?(0,w.mapInternalToDisplayNames)(ey.metadata.litellm_disabled_callbacks):[],variant:"inline",className:"pt-4 border-t border-gray-200"})]})]})})]})]})]})}],20147)}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/18aswm2wrvkis.js b/litellm/proxy/_experimental/out/_next/static/chunks/18aswm2wrvkis.js new file mode 100644 index 00000000000..f6fb02277f1 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/18aswm2wrvkis.js @@ -0,0 +1,13 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,482725,e=>{"use strict";var t=e.i(244451);e.s(["Spin",()=>t.default])},165370,e=>{"use strict";e.i(247167);var t=e.i(271645),i=e.i(931067);let n={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M272.9 512l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512zm304 0l265.4-339.1c4.1-5.2.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3a31.99 31.99 0 000 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"}}]},name:"double-left",theme:"outlined"};var o=e.i(9583),a=t.forwardRef(function(e,a){return t.createElement(o.default,(0,i.default)({},e,{ref:a,icon:n}))});let l={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"}}]},name:"double-right",theme:"outlined"};var r=t.forwardRef(function(e,n){return t.createElement(o.default,(0,i.default)({},e,{ref:n,icon:l}))}),c=e.i(801312),u=e.i(286612),s=e.i(343794),d=e.i(211577),m=e.i(410160),p=e.i(209428),g=e.i(392221),f=e.i(914949),b=e.i(404948),v=e.i(244009);e.i(883110);let h={items_per_page:"条/页",jump_to:"跳至",jump_to_confirm:"确定",page:"页",prev_page:"上一页",next_page:"下一页",prev_5:"向前 5 页",next_5:"向后 5 页",prev_3:"向前 3 页",next_3:"向后 3 页",page_size:"页码"};var S=[10,20,50,100];let $=function(e){var i=e.pageSizeOptions,n=void 0===i?S:i,o=e.locale,a=e.changeSize,l=e.pageSize,r=e.goButton,c=e.quickGo,u=e.rootPrefixCls,s=e.disabled,d=e.buildOptionText,m=e.showSizeChanger,p=e.sizeChangerRender,f=t.default.useState(""),v=(0,g.default)(f,2),h=v[0],$=v[1],C=function(){return!h||Number.isNaN(h)?void 0:Number(h)},y="function"==typeof d?d:function(e){return"".concat(e," ").concat(o.items_per_page)},k=function(e){""!==h&&(e.keyCode===b.default.ENTER||"click"===e.type)&&($(""),null==c||c(C()))},x="".concat(u,"-options");if(!m&&!c)return null;var E=null,z=null,N=null;return m&&p&&(E=p({disabled:s,size:l,onSizeChange:function(e){null==a||a(Number(e))},"aria-label":o.page_size,className:"".concat(x,"-size-changer"),options:(n.some(function(e){return e.toString()===l.toString()})?n:n.concat([l]).sort(function(e,t){return(Number.isNaN(Number(e))?0:Number(e))-(Number.isNaN(Number(t))?0:Number(t))})).map(function(e){return{label:y(e),value:e}})})),c&&(r&&(N="boolean"==typeof r?t.default.createElement("button",{type:"button",onClick:k,onKeyUp:k,disabled:s,className:"".concat(x,"-quick-jumper-button")},o.jump_to_confirm):t.default.createElement("span",{onClick:k,onKeyUp:k},r)),z=t.default.createElement("div",{className:"".concat(x,"-quick-jumper")},o.jump_to,t.default.createElement("input",{disabled:s,type:"text",value:h,onChange:function(e){$(e.target.value)},onKeyUp:k,onBlur:function(e){r||""===h||($(""),e.relatedTarget&&(e.relatedTarget.className.indexOf("".concat(u,"-item-link"))>=0||e.relatedTarget.className.indexOf("".concat(u,"-item"))>=0)||null==c||c(C()))},"aria-label":o.page}),o.page,N)),t.default.createElement("li",{className:x},E,z)},C=function(e){var i=e.rootPrefixCls,n=e.page,o=e.active,a=e.className,l=e.showTitle,r=e.onClick,c=e.onKeyPress,u=e.itemRender,m="".concat(i,"-item"),p=(0,s.default)(m,"".concat(m,"-").concat(n),(0,d.default)((0,d.default)({},"".concat(m,"-active"),o),"".concat(m,"-disabled"),!n),a),g=u(n,"page",t.default.createElement("a",{rel:"nofollow"},n));return g?t.default.createElement("li",{title:l?String(n):null,className:p,onClick:function(){r(n)},onKeyDown:function(e){c(e,r,n)},tabIndex:0},g):null};var y=function(e,t,i){return i};function k(){}function x(e){var t=Number(e);return"number"==typeof t&&!Number.isNaN(t)&&isFinite(t)&&Math.floor(t)===t}function E(e,t,i){return Math.floor((i-1)/(void 0===e?t:e))+1}let z=function(e){var n,o,a,l,r=e.prefixCls,c=void 0===r?"rc-pagination":r,u=e.selectPrefixCls,S=e.className,z=e.current,N=e.defaultCurrent,j=e.total,B=void 0===j?0:j,w=e.pageSize,M=e.defaultPageSize,I=e.onChange,O=void 0===I?k:I,T=e.hideOnSinglePage,P=e.align,D=e.showPrevNextJumpers,H=e.showQuickJumper,A=e.showLessItems,_=e.showTitle,R=void 0===_||_,q=e.onShowSizeChange,K=void 0===q?k:q,W=e.locale,L=void 0===W?h:W,X=e.style,F=e.totalBoundaryShowSizeChanger,G=e.disabled,U=e.simple,J=e.showTotal,Q=e.showSizeChanger,V=void 0===Q?B>(void 0===F?50:F):Q,Y=e.sizeChangerRender,Z=e.pageSizeOptions,ee=e.itemRender,et=void 0===ee?y:ee,ei=e.jumpPrevIcon,en=e.jumpNextIcon,eo=e.prevIcon,ea=e.nextIcon,el=t.default.useRef(null),er=(0,f.default)(10,{value:w,defaultValue:void 0===M?10:M}),ec=(0,g.default)(er,2),eu=ec[0],es=ec[1],ed=(0,f.default)(1,{value:z,defaultValue:void 0===N?1:N,postState:function(e){return Math.max(1,Math.min(e,E(void 0,eu,B)))}}),em=(0,g.default)(ed,2),ep=em[0],eg=em[1],ef=t.default.useState(ep),eb=(0,g.default)(ef,2),ev=eb[0],eh=eb[1];(0,t.useEffect)(function(){eh(ep)},[ep]);var eS=Math.max(1,ep-(A?3:5)),e$=Math.min(E(void 0,eu,B),ep+(A?3:5));function eC(i,n){var o=i||t.default.createElement("button",{type:"button","aria-label":n,className:"".concat(c,"-item-link")});return"function"==typeof i&&(o=t.default.createElement(i,(0,p.default)({},e))),o}function ey(e){var t=e.target.value,i=E(void 0,eu,B);return""===t?t:Number.isNaN(Number(t))?ev:t>=i?i:Number(t)}var ek=B>eu&&H;function ex(e){var t=ey(e);switch(t!==ev&&eh(t),e.keyCode){case b.default.ENTER:eE(t);break;case b.default.UP:eE(t-1);break;case b.default.DOWN:eE(t+1)}}function eE(e){if(x(e)&&e!==ep&&x(B)&&B>0&&!G){var t=E(void 0,eu,B),i=e;return e>t?i=t:e<1&&(i=1),i!==ev&&eh(i),eg(i),null==O||O(i,eu),i}return ep}var ez=ep>1,eN=ep2?i-2:0),o=2;oB?B:ep*eu])),eH=null,eA=E(void 0,eu,B);if(T&&B<=eu)return null;var e_=[],eR={rootPrefixCls:c,onClick:eE,onKeyPress:eI,showTitle:R,itemRender:et,page:-1},eq=ep-1>0?ep-1:0,eK=ep+1=2*eG&&3!==ep&&(e_[0]=t.default.cloneElement(e_[0],{className:(0,s.default)("".concat(c,"-item-after-jump-prev"),e_[0].props.className)}),e_.unshift(eT)),eA-ep>=2*eG&&ep!==eA-2){var e2=e_[e_.length-1];e_[e_.length-1]=t.default.cloneElement(e2,{className:(0,s.default)("".concat(c,"-item-before-jump-next"),e2.props.className)}),e_.push(eH)}1!==eZ&&e_.unshift(t.default.createElement(C,(0,i.default)({},eR,{key:1,page:1}))),e0!==eA&&e_.push(t.default.createElement(C,(0,i.default)({},eR,{key:eA,page:eA})))}var e3=(n=et(eq,"prev",eC(eo,"prev page")),t.default.isValidElement(n)?t.default.cloneElement(n,{disabled:!ez}):n);if(e3){var e6=!ez||!eA;e3=t.default.createElement("li",{title:R?L.prev_page:null,onClick:ej,tabIndex:e6?null:0,onKeyDown:function(e){eI(e,ej)},className:(0,s.default)("".concat(c,"-prev"),(0,d.default)({},"".concat(c,"-disabled"),e6)),"aria-disabled":e6},e3)}var e9=(o=et(eK,"next",eC(ea,"next page")),t.default.isValidElement(o)?t.default.cloneElement(o,{disabled:!eN}):o);e9&&(U?(a=!eN,l=ez?0:null):l=(a=!eN||!eA)?null:0,e9=t.default.createElement("li",{title:R?L.next_page:null,onClick:eB,tabIndex:l,onKeyDown:function(e){eI(e,eB)},className:(0,s.default)("".concat(c,"-next"),(0,d.default)({},"".concat(c,"-disabled"),a)),"aria-disabled":a},e9));var e7=(0,s.default)(c,S,(0,d.default)((0,d.default)((0,d.default)((0,d.default)((0,d.default)({},"".concat(c,"-start"),"start"===P),"".concat(c,"-center"),"center"===P),"".concat(c,"-end"),"end"===P),"".concat(c,"-simple"),U),"".concat(c,"-disabled"),G));return t.default.createElement("ul",(0,i.default)({className:e7,style:X,ref:el},eP),eD,e3,U?eF:e_,e9,t.default.createElement($,{locale:L,rootPrefixCls:c,disabled:G,selectPrefixCls:void 0===u?"rc-select":u,changeSize:function(e){var t=E(e,eu,B),i=ep>t&&0!==t?t:ep;es(e),eh(i),null==K||K(ep,e),eg(i),null==O||O(i,e)},pageSize:eu,pageSizeOptions:Z,quickGo:ek?eE:null,goButton:eX,showSizeChanger:V,sizeChangerRender:Y}))};var N=e.i(727214),j=e.i(242064),B=e.i(517455),w=e.i(150073),M=e.i(408850),I=e.i(327494),O=e.i(104458);e.i(296059);var T=e.i(915654),P=e.i(349942),D=e.i(517458),H=e.i(889943),A=e.i(183293),_=e.i(246422),R=e.i(838378);let q=e=>Object.assign({itemBg:e.colorBgContainer,itemSize:e.controlHeight,itemSizeSM:e.controlHeightSM,itemActiveBg:e.colorBgContainer,itemActiveColor:e.colorPrimary,itemActiveColorHover:e.colorPrimaryHover,itemLinkBg:e.colorBgContainer,itemActiveColorDisabled:e.colorTextDisabled,itemActiveBgDisabled:e.controlItemBgActiveDisabled,itemInputBg:e.colorBgContainer,miniOptionsSizeChangerTop:0},(0,D.initComponentToken)(e)),K=e=>(0,R.mergeToken)(e,{inputOutlineOffset:0,quickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.25).equal(),paginationMiniOptionsMarginInlineStart:e.calc(e.marginXXS).div(2).equal(),paginationMiniQuickJumperInputWidth:e.calc(e.controlHeightLG).mul(1.1).equal(),paginationItemPaddingInline:e.calc(e.marginXXS).mul(1.5).equal(),paginationEllipsisLetterSpacing:e.calc(e.marginXXS).div(2).equal(),paginationSlashMarginInlineStart:e.marginSM,paginationSlashMarginInlineEnd:e.marginSM,paginationEllipsisTextIndent:"0.13em"},(0,D.initInputToken)(e)),W=(0,_.genStyleHooks)("Pagination",e=>{let t=K(e);return[(e=>{let{componentCls:t}=e;return{[t]:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},(0,A.resetComponent)(e)),{display:"flex",flexWrap:"wrap",rowGap:e.paddingXS,"&-start":{justifyContent:"start"},"&-center":{justifyContent:"center"},"&-end":{justifyContent:"end"},"ul, ol":{margin:0,padding:0,listStyle:"none"},"&::after":{display:"block",clear:"both",height:0,overflow:"hidden",visibility:"hidden",content:'""'},[`${t}-total-text`]:{display:"inline-block",height:e.itemSize,marginInlineEnd:e.marginXS,lineHeight:(0,T.unit)(e.calc(e.itemSize).sub(2).equal()),verticalAlign:"middle"}}),(e=>{let{componentCls:t}=e;return{[`${t}-item`]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,marginInlineEnd:e.marginXS,fontFamily:e.fontFamily,lineHeight:(0,T.unit)(e.calc(e.itemSize).sub(2).equal()),textAlign:"center",verticalAlign:"middle",listStyle:"none",backgroundColor:e.itemBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:0,cursor:"pointer",userSelect:"none",a:{display:"block",padding:`0 ${(0,T.unit)(e.paginationItemPaddingInline)}`,color:e.colorText,"&:hover":{textDecoration:"none"}},[`&:not(${t}-item-active)`]:{"&:hover":{transition:`all ${e.motionDurationMid}`,backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive}},"&-active":{fontWeight:e.fontWeightStrong,backgroundColor:e.itemActiveBg,borderColor:e.colorPrimary,a:{color:e.itemActiveColor},"&:hover":{borderColor:e.colorPrimaryHover},"&:hover a":{color:e.itemActiveColorHover}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-jump-prev, ${t}-jump-next`]:{outline:0,[`${t}-item-container`]:{position:"relative",[`${t}-item-link-icon`]:{color:e.colorPrimary,fontSize:e.fontSizeSM,opacity:0,transition:`all ${e.motionDurationMid}`,"&-svg":{top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,margin:"auto"}},[`${t}-item-ellipsis`]:{position:"absolute",top:0,insetInlineEnd:0,bottom:0,insetInlineStart:0,display:"block",margin:"auto",color:e.colorTextDisabled,letterSpacing:e.paginationEllipsisLetterSpacing,textAlign:"center",textIndent:e.paginationEllipsisTextIndent,opacity:1,transition:`all ${e.motionDurationMid}`}},"&:hover":{[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}}},[` + ${t}-prev, + ${t}-jump-prev, + ${t}-jump-next + `]:{marginInlineEnd:e.marginXS},[` + ${t}-prev, + ${t}-next, + ${t}-jump-prev, + ${t}-jump-next + `]:{display:"inline-block",minWidth:e.itemSize,height:e.itemSize,color:e.colorText,fontFamily:e.fontFamily,lineHeight:(0,T.unit)(e.itemSize),textAlign:"center",verticalAlign:"middle",listStyle:"none",borderRadius:e.borderRadius,cursor:"pointer",transition:`all ${e.motionDurationMid}`},[`${t}-prev, ${t}-next`]:{outline:0,button:{color:e.colorText,cursor:"pointer",userSelect:"none"},[`${t}-item-link`]:{display:"block",width:"100%",height:"100%",padding:0,fontSize:e.fontSizeSM,textAlign:"center",backgroundColor:"transparent",border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} transparent`,borderRadius:e.borderRadius,outline:"none",transition:`all ${e.motionDurationMid}`},[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover`]:{[`${t}-item-link`]:{backgroundColor:"transparent"}}},[`${t}-slash`]:{marginInlineEnd:e.paginationSlashMarginInlineEnd,marginInlineStart:e.paginationSlashMarginInlineStart},[`${t}-options`]:{display:"inline-block",marginInlineStart:e.margin,verticalAlign:"middle","&-size-changer":{display:"inline-block",width:"auto"},"&-quick-jumper":{display:"inline-block",height:e.controlHeight,marginInlineStart:e.marginXS,lineHeight:(0,T.unit)(e.controlHeight),verticalAlign:"top",input:Object.assign(Object.assign(Object.assign({},(0,P.genBasicInputStyle)(e)),(0,H.genBaseOutlinedStyle)(e,{borderColor:e.colorBorder,hoverBorderColor:e.colorPrimaryHover,activeBorderColor:e.colorPrimary,activeShadow:e.activeShadow})),{"&[disabled]":Object.assign({},(0,H.genDisabledStyle)(e)),width:e.quickJumperInputWidth,height:e.controlHeight,boxSizing:"border-box",margin:0,marginInlineStart:e.marginXS,marginInlineEnd:e.marginXS})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-simple`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSize,lineHeight:(0,T.unit)(e.itemSize),verticalAlign:"top",[`${t}-item-link`]:{height:e.itemSize,backgroundColor:"transparent",border:0,"&:hover":{backgroundColor:e.colorBgTextHover},"&:active":{backgroundColor:e.colorBgTextActive},"&::after":{height:e.itemSize,lineHeight:(0,T.unit)(e.itemSize)}}},[`${t}-simple-pager`]:{display:"inline-flex",alignItems:"center",height:e.itemSize,marginInlineEnd:e.marginXS,input:{boxSizing:"border-box",height:"100%",width:e.quickJumperInputWidth,padding:`0 ${(0,T.unit)(e.paginationItemPaddingInline)}`,textAlign:"center",backgroundColor:e.itemInputBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadius,outline:"none",transition:`border-color ${e.motionDurationMid}`,color:"inherit","&:hover":{borderColor:e.colorPrimary},"&:focus":{borderColor:e.colorPrimaryHover,boxShadow:`${(0,T.unit)(e.inputOutlineOffset)} 0 ${(0,T.unit)(e.controlOutlineWidth)} ${e.controlOutline}`},"&[disabled]":{color:e.colorTextDisabled,backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,cursor:"not-allowed"}}},[`&${t}-disabled`]:{[`${t}-prev, ${t}-next`]:{[`${t}-item-link`]:{"&:hover, &:active":{backgroundColor:"transparent"}}}},[`&${t}-mini`]:{[`${t}-prev, ${t}-next`]:{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM),[`${t}-item-link`]:{height:e.itemSizeSM,"&::after":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)}}},[`${t}-simple-pager`]:{height:e.itemSizeSM,input:{width:e.paginationMiniQuickJumperInputWidth}}}}}})(e)),(e=>{let{componentCls:t}=e;return{[`&${t}-mini ${t}-total-text, &${t}-mini ${t}-simple-pager`]:{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-item`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,T.unit)(e.calc(e.itemSizeSM).sub(2).equal())},[`&${t}-mini ${t}-prev, &${t}-mini ${t}-next`]:{minWidth:e.itemSizeSM,height:e.itemSizeSM,margin:0,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini:not(${t}-disabled)`]:{[`${t}-prev, ${t}-next`]:{[`&:hover ${t}-item-link`]:{backgroundColor:e.colorBgTextHover},[`&:active ${t}-item-link`]:{backgroundColor:e.colorBgTextActive},[`&${t}-disabled:hover ${t}-item-link`]:{backgroundColor:"transparent"}}},[` + &${t}-mini ${t}-prev ${t}-item-link, + &${t}-mini ${t}-next ${t}-item-link + `]:{backgroundColor:"transparent",borderColor:"transparent","&::after":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM)}},[`&${t}-mini ${t}-jump-prev, &${t}-mini ${t}-jump-next`]:{height:e.itemSizeSM,marginInlineEnd:0,lineHeight:(0,T.unit)(e.itemSizeSM)},[`&${t}-mini ${t}-options`]:{marginInlineStart:e.paginationMiniOptionsMarginInlineStart,"&-size-changer":{top:e.miniOptionsSizeChangerTop},"&-quick-jumper":{height:e.itemSizeSM,lineHeight:(0,T.unit)(e.itemSizeSM),input:Object.assign(Object.assign({},(0,P.genInputSmallStyle)(e)),{width:e.paginationMiniQuickJumperInputWidth,height:e.controlHeightSM})}}}})(e)),(e=>{let{componentCls:t}=e;return{[`${t}-disabled`]:{"&, &:hover":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}},"&:focus-visible":{cursor:"not-allowed",[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed"}}},[`&${t}-disabled`]:{cursor:"not-allowed",[`${t}-item`]:{cursor:"not-allowed",backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"},a:{color:e.colorTextDisabled,backgroundColor:"transparent",border:"none",cursor:"not-allowed"},"&-active":{borderColor:e.colorBorder,backgroundColor:e.itemActiveBgDisabled,"&:hover, &:active":{backgroundColor:e.itemActiveBgDisabled},a:{color:e.itemActiveColorDisabled}}},[`${t}-item-link`]:{color:e.colorTextDisabled,cursor:"not-allowed","&:hover, &:active":{backgroundColor:"transparent"},[`${t}-simple&`]:{backgroundColor:"transparent","&:hover, &:active":{backgroundColor:"transparent"}}},[`${t}-simple-pager`]:{color:e.colorTextDisabled},[`${t}-jump-prev, ${t}-jump-next`]:{[`${t}-item-link-icon`]:{opacity:0},[`${t}-item-ellipsis`]:{opacity:1}}}}})(e)),{[`@media only screen and (max-width: ${e.screenLG}px)`]:{[`${t}-item`]:{"&-after-jump-prev, &-before-jump-next":{display:"none"}}},[`@media only screen and (max-width: ${e.screenSM}px)`]:{[`${t}-options`]:{display:"none"}}}),[`&${e.componentCls}-rtl`]:{direction:"rtl"}}})(t),(e=>{let{componentCls:t}=e;return{[`${t}:not(${t}-disabled)`]:{[`${t}-item`]:Object.assign({},(0,A.genFocusStyle)(e)),[`${t}-jump-prev, ${t}-jump-next`]:{"&:focus-visible":Object.assign({[`${t}-item-link-icon`]:{opacity:1},[`${t}-item-ellipsis`]:{opacity:0}},(0,A.genFocusOutline)(e))},[`${t}-prev, ${t}-next`]:{[`&:focus-visible ${t}-item-link`]:(0,A.genFocusOutline)(e)}}}})(t)]},q),L=(0,_.genSubStyleComponent)(["Pagination","bordered"],e=>(e=>{let{componentCls:t}=e;return{[`${t}${t}-bordered${t}-disabled:not(${t}-mini)`]:{"&, &:hover":{[`${t}-item-link`]:{borderColor:e.colorBorder}},"&:focus-visible":{[`${t}-item-link`]:{borderColor:e.colorBorder}},[`${t}-item, ${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,[`&:hover:not(${t}-item-active)`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,a:{color:e.colorTextDisabled}},[`&${t}-item-active`]:{backgroundColor:e.itemActiveBgDisabled}},[`${t}-prev, ${t}-next`]:{"&:hover button":{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder,color:e.colorTextDisabled},[`${t}-item-link`]:{backgroundColor:e.colorBgContainerDisabled,borderColor:e.colorBorder}}},[`${t}${t}-bordered:not(${t}-mini)`]:{[`${t}-prev, ${t}-next`]:{"&:hover button":{borderColor:e.colorPrimaryHover,backgroundColor:e.itemBg},[`${t}-item-link`]:{backgroundColor:e.itemLinkBg,borderColor:e.colorBorder},[`&:hover ${t}-item-link`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,color:e.colorPrimary},[`&${t}-disabled`]:{[`${t}-item-link`]:{borderColor:e.colorBorder,color:e.colorTextDisabled}}},[`${t}-item`]:{backgroundColor:e.itemBg,border:`${(0,T.unit)(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,[`&:hover:not(${t}-item-active)`]:{borderColor:e.colorPrimary,backgroundColor:e.itemBg,a:{color:e.colorPrimary}},"&-active":{borderColor:e.colorPrimary}}}}})(K(e)),q);function X(e){return(0,t.useMemo)(()=>"boolean"==typeof e?[e,{}]:e&&"object"==typeof e?[!0,e]:[void 0,void 0],[e])}var F=function(e,t){var i={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(i[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(i[n[o]]=e[n[o]]);return i};e.s(["default",0,e=>{let{align:i,prefixCls:n,selectPrefixCls:o,className:l,rootClassName:d,style:m,size:p,locale:g,responsive:f,showSizeChanger:b,selectComponentClass:v,pageSizeOptions:h}=e,S=F(e,["align","prefixCls","selectPrefixCls","className","rootClassName","style","size","locale","responsive","showSizeChanger","selectComponentClass","pageSizeOptions"]),{xs:$}=(0,w.default)(f),[,C]=(0,O.useToken)(),{getPrefixCls:y,direction:k,showSizeChanger:x,className:E,style:T}=(0,j.useComponentConfig)("pagination"),P=y("pagination",n),[D,H,A]=W(P),_=(0,B.default)(p),R="small"===_||!!($&&!_&&f),[q]=(0,M.useLocale)("Pagination",N.default),K=Object.assign(Object.assign({},q),g),[G,U]=X(b),[J,Q]=X(x),V=null!=U?U:Q,Y=v||I.default,Z=t.useMemo(()=>h?h.map(e=>Number(e)):void 0,[h]),ee=t.useMemo(()=>{let e=t.createElement("span",{className:`${P}-item-ellipsis`},"•••"),i=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===k?t.createElement(u.default,null):t.createElement(c.default,null)),n=t.createElement("button",{className:`${P}-item-link`,type:"button",tabIndex:-1},"rtl"===k?t.createElement(c.default,null):t.createElement(u.default,null));return{prevIcon:i,nextIcon:n,jumpPrevIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===k?t.createElement(r,{className:`${P}-item-link-icon`}):t.createElement(a,{className:`${P}-item-link-icon`}),e)),jumpNextIcon:t.createElement("a",{className:`${P}-item-link`},t.createElement("div",{className:`${P}-item-container`},"rtl"===k?t.createElement(a,{className:`${P}-item-link-icon`}):t.createElement(r,{className:`${P}-item-link-icon`}),e))}},[k,P]),et=y("select",o),ei=(0,s.default)({[`${P}-${i}`]:!!i,[`${P}-mini`]:R,[`${P}-rtl`]:"rtl"===k,[`${P}-bordered`]:C.wireframe},E,l,d,H,A),en=Object.assign(Object.assign({},T),m);return D(t.createElement(t.Fragment,null,C.wireframe&&t.createElement(L,{prefixCls:P}),t.createElement(z,Object.assign({},ee,S,{style:en,prefixCls:P,selectPrefixCls:et,className:ei,locale:K,pageSizeOptions:Z,showSizeChanger:null!=G?G:J,sizeChangerRender:e=>{var i;let{disabled:n,size:o,onSizeChange:a,"aria-label":l,className:r,options:c}=e,{className:u,onChange:d}=V||{},m=null==(i=c.find(e=>String(e.value)===String(o)))?void 0:i.value;return t.createElement(Y,Object.assign({disabled:n,showSearch:!0,popupMatchSelectWidth:!1,getPopupContainer:e=>e.parentNode,"aria-label":l,options:c},V,{value:m,onChange:(e,t)=>{null==a||a(e),null==d||d(e,t)},size:R?"small":"middle",className:(0,s.default)(r,u)}))}}))))}],165370)},211576,e=>{"use strict";var t=e.i(131757);e.s(["Col",()=>t.default])},178654,e=>{"use strict";let t=e.i(211576).Col;e.s(["Col",0,t],178654)},621192,e=>{"use strict";let t=e.i(281256).Row;e.s(["Row",0,t],621192)},653496,e=>{"use strict";var t=e.i(721369);e.s(["Tabs",()=>t.default])},981339,e=>{"use strict";var t=e.i(185793);e.s(["Skeleton",()=>t.default])},500727,e=>{"use strict";var t=e.i(266027),i=e.i(243652),n=e.i(602869),o=e.i(135214);let a=(0,i.createQueryKeys)("mcpServers");e.s(["useMCPServers",0,e=>{let{accessToken:i}=(0,o.default)();return(0,t.useQuery)({queryKey:a.list(e?{filters:{teamId:e}}:void 0),queryFn:async()=>await (0,n.fetchMCPServers)(i,e),enabled:!!i})}])},263147,e=>{"use strict";var t=e.i(266027),i=e.i(243652),n=e.i(602869),o=e.i(431703),a=e.i(708347),l=e.i(135214);let r=(0,i.createQueryKeys)("accessGroups"),c=async e=>{let t=(0,n.getProxyBaseUrl)(),i=`${t}/v1/access_group`,a=await fetch(i,{method:"GET",headers:{[(0,n.getGlobalLitellmHeaderName)()]:`Bearer ${e}`,"Content-Type":"application/json"}});if(!a.ok){let e=await a.json(),t=(0,o.deriveErrorMessage)(e);throw(0,n.handleError)(t),Error(t)}return a.json()};e.s(["accessGroupKeys",0,r,"useAccessGroups",0,()=>{let{accessToken:e,userRole:i}=(0,l.default)();return(0,t.useQuery)({queryKey:r.list({}),queryFn:async()=>c(e),enabled:!!e&&a.all_admin_roles.includes(i||"")})}])},304911,e=>{"use strict";var t=e.i(843476),i=e.i(262218);let{Text:n}=e.i(898586).Typography;e.s(["default",0,function({userId:e}){return"default_user_id"===e?(0,t.jsx)(i.Tag,{color:"blue",children:"Default Proxy Admin"}):(0,t.jsx)(n,{children:e})}])}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/turbopack-0c_gbv0_h~sru.js b/litellm/proxy/_experimental/out/_next/static/chunks/turbopack-0c_gbv0_h~sru.js new file mode 100644 index 00000000000..2bdf28b2a2a --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/turbopack-0c_gbv0_h~sru.js @@ -0,0 +1 @@ +(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,{otherChunks:["static/chunks/15_6vcg943diw.js","static/chunks/0yu_1~b4-6wf1.js","static/chunks/0d85pf5s8at.6.js","static/chunks/0aqpm5mabssqz.js"],runtimeModuleIds:[494553]}]),(()=>{let e;if(!Array.isArray(globalThis.TURBOPACK))return;let t="/litellm-asset-prefix/_next/",r=function(){if(null!=self.TURBOPACK_ASSET_SUFFIX)return self.TURBOPACK_ASSET_SUFFIX;let e=document?.currentScript?.getAttribute?.("src")??"",t=e.indexOf("?");return t>=0?e.slice(t):""}(),n=["NEXT_DEPLOYMENT_ID","NEXT_CLIENT_ASSET_SUFFIX"];var o,i=((o=i||{})[o.Runtime=0]="Runtime",o[o.Parent=1]="Parent",o[o.Update=2]="Update",o);let l=new WeakMap;function s(e,t){this.m=e,this.e=t}let u=s.prototype,a=Object.prototype.hasOwnProperty,c="u">typeof Symbol&&Symbol.toStringTag;function f(e,t,r){a.call(e,t)||Object.defineProperty(e,t,r)}function p(e,t){let r=e[t];return r||(r=h(t),e[t]=r),r}function h(e){return{exports:{},error:void 0,id:e,namespaceObject:void 0}}function d(e,t){f(e,"__esModule",{value:!0}),c&&f(e,c,{value:"Module"});let r=0;for(;rObject.getPrototypeOf(e):e=>e.__proto__,y=[null,b({}),b([]),b(b)];function g(e,t,r){let n=[],o=-1;for(let t=e;("object"==typeof t||"function"==typeof t)&&!y.includes(t);t=b(t))for(let r of Object.getOwnPropertyNames(t))n.push(r,function(e,t){return()=>e[t]}(e,r)),-1===o&&"default"===r&&(o=n.length-1);return r&&o>=0||(o>=0?n.splice(o,1,0,e):n.push("default",0,e)),d(t,n),t}function w(e){let t=B(e,this.m);if(t.namespaceObject)return t.namespaceObject;let r=t.exports;return t.namespaceObject=g(r,"function"==typeof r?function(...e){return r.apply(this,e)}:Object.create(null),r&&r.__esModule)}function O(e){let t=e.indexOf("#");-1!==t&&(e=e.substring(0,t));let r=e.indexOf("?");return -1!==r&&(e=e.substring(0,r)),e}function j(){let e,t;return{promise:new Promise((r,n)=>{t=n,e=r}),resolve:e,reject:t}}u.i=w,u.A=function(e){return this.r(e)(w.bind(this))},u.t="function"==typeof require?require:function(){throw Error("Unexpected use of runtime require")},u.r=function(e){return B(e,this.m).exports},u.f=function(e){function t(t){if(t=O(t),a.call(e,t))return e[t].module();let r=Error(`Cannot find module '${t}'`);throw r.code="MODULE_NOT_FOUND",r}return t.keys=()=>Object.keys(e),t.resolve=t=>{if(t=O(t),a.call(e,t))return e[t].id();let r=Error(`Cannot find module '${t}'`);throw r.code="MODULE_NOT_FOUND",r},t.import=async e=>await t(e),t};let k=Symbol("turbopack queues"),U=Symbol("turbopack exports"),v=Symbol("turbopack error");function C(e){e&&1!==e.status&&(e.status=1,e.forEach(e=>e.queueCount--),e.forEach(e=>e.queueCount--?e.queueCount++:e()))}u.a=function(e,t){let r=this.m,n=t?Object.assign([],{status:-1}):void 0,o=new Set,{resolve:i,reject:l,promise:s}=j(),u=Object.assign(s,{[U]:r.exports,[k]:e=>{n&&e(n),o.forEach(e),u.catch(()=>{})}}),a={get:()=>u,set(e){e!==u&&(u[U]=e)}};Object.defineProperty(r,"exports",a),Object.defineProperty(r,"namespaceObject",a),e(function(e){let t=e.map(e=>{if(null!==e&&"object"==typeof e){if(k in e)return e;if(null!=e&&"object"==typeof e&&"then"in e&&"function"==typeof e.then){let t=Object.assign([],{status:0}),r={[U]:{},[k]:e=>e(t)};return e.then(e=>{r[U]=e,C(t)},e=>{r[v]=e,C(t)}),r}}return{[U]:e,[k]:()=>{}}}),r=()=>t.map(e=>{if(e[v])throw e[v];return e[U]}),{promise:i,resolve:l}=j(),s=Object.assign(()=>l(r),{queueCount:0});function u(e){e!==n&&!o.has(e)&&(o.add(e),e&&0===e.status&&(s.queueCount++,e.push(s)))}return t.map(e=>e[k](u)),s.queueCount?i:r()},function(e){e?l(u[v]=e):i(u[U]),C(n)}),n&&-1===n.status&&(n.status=0)};let P=function(e){let t=new URL(e,"x:/"),r={};for(let e in t)r[e]=t[e];for(let t in r.href=e,r.pathname=e.replace(/[?#].*/,""),r.origin=r.protocol="",r.toString=r.toJSON=(...t)=>e,r)Object.defineProperty(this,t,{enumerable:!0,configurable:!0,value:r[t]})};function R(e,t){throw Error(`Invariant: ${t(e)}`)}P.prototype=URL.prototype,u.U=P,u.z=function(e){throw Error("dynamic usage of require is not supported")},u.g=globalThis;let S=s.prototype,$=new Map;u.M=$;let _=new Map,E=new Map;async function T(e,t,r){let n;if("string"==typeof r)return M(e,t,q(r));let o=r.included||[],i=o.map(e=>!!$.has(e)||_.get(e));if(i.length>0&&i.every(e=>e))return void await Promise.all(i);let l=r.moduleChunks||[],s=l.map(e=>E.get(e)).filter(e=>e);if(s.length>0){if(s.length===l.length)return void await Promise.all(s);let r=new Set;for(let e of l)E.has(e)||r.add(e);for(let n of r){let r=M(e,t,q(n));E.set(n,r),s.push(r)}n=Promise.all(s)}else{for(let o of(n=M(e,t,q(r.path)),l))E.has(o)||E.set(o,n)}for(let e of o)_.has(e)||_.set(e,n);await n}S.l=function(e){return T(i.Parent,this.m.id,e)};let A=Promise.resolve(void 0),x=new WeakMap;function M(t,r,n){let o=e.loadChunkCached(t,n),l=x.get(o);if(void 0===l){let e=x.set.bind(x,o,A);l=o.then(e).catch(e=>{let o;switch(t){case i.Runtime:o=`as a runtime dependency of chunk ${r}`;break;case i.Parent:o=`from module ${r}`;break;case i.Update:o="from an HMR update";break;default:R(t,e=>`Unknown source type: ${e}`)}let l=Error(`Failed to load chunk ${n} ${o}${e?`: ${e}`:""}`,e?{cause:e}:void 0);throw l.name="ChunkLoadError",l}),x.set(o,l)}return l}function q(e){return`${t}${e.split("/").map(e=>encodeURIComponent(e)).join("/")}${r}`}S.L=function(e){return M(i.Parent,this.m.id,e)},S.R=function(e){let t=this.r(e);return t?.default??t},S.P=function(e){return`/ROOT/${e??""}`},S.q=function(e,t){m.call(this,`${e}${r}`,t)},S.b=function(e,t,o,i){let l="SharedWorker"===e.name,s=[o.map(e=>q(e)).reverse(),r];for(let e of n)s.push(globalThis[e]);let u=new URL(q(t),location.origin),a=JSON.stringify(s);return l?u.searchParams.set("params",a):u.hash="#params="+encodeURIComponent(a),new e(u,i?{...i,type:void 0}:void 0)};let N=/\.js(?:\?[^#]*)?(?:#.*)?$/,K=/\.css(?:\?[^#]*)?(?:#.*)?$/;function L(e){return K.test(e)}u.w=function(t,r,n){return e.loadWebAssembly(i.Parent,this.m.id,t,r,n)},u.u=function(t,r){return e.loadWebAssemblyModule(i.Parent,this.m.id,t,r)};let I={};u.c=I;let B=(e,t)=>{let r=I[e];if(r){if(r.error)throw r.error;return r}return W(e,i.Parent,t.id)};function W(e,t,r){let n=$.get(e);if("function"!=typeof n)throw Error(function(e,t,r){let n;switch(t){case 0:n=`as a runtime entry of chunk ${r}`;break;case 1:n=`because it was required from module ${r}`;break;case 2:n="because of an HMR update";break;default:R(t,e=>`Unknown source type: ${e}`)}return`Module ${e} was instantiated ${n}, but the module factory is not available.`}(e,t,r));let o=h(e),i=o.exports;I[e]=o;let l=new s(o,i);try{n(l,o,i)}catch(e){throw o.error=e,e}return o.namespaceObject&&o.exports!==o.namespaceObject&&g(o.exports,o.namespaceObject),o}function F(t){let r,n=function(e){if("string"==typeof e)return e;if(e)return{src:e.getAttribute("src")};if("u">typeof TURBOPACK_NEXT_CHUNK_URLS)return{src:TURBOPACK_NEXT_CHUNK_URLS.pop()};throw Error("chunk path empty but not in a worker")}(t[0]);return 2===t.length?r=t[1]:(r=void 0,!function(e,t){let r=1;for(;r{r=e,n=t}),resolve:()=>{t.resolved=!0,r()},reject:n},X.set(e,t)}return t}e={async registerChunk(e,r){let n=function(e){if("string"==typeof e)return e;let r=decodeURIComponent(e.src.replace(/[?#].*$/,""));return r.startsWith(t)?r.slice(t.length):r}(e);if(D("string"==typeof e?q(e):e.src).resolve(),null!=r){for(let e of r.otherChunks)D(q("string"==typeof e?e:e.path));if(await Promise.all(r.otherChunks.map(e=>T(i.Runtime,n,e))),r.runtimeModuleIds.length>0)for(let e of r.runtimeModuleIds)!function(e,t){let r=I[t];if(r){if(r.error)throw r.error;return}W(t,i.Runtime,e)}(n,e)}},loadChunkCached:(e,t)=>(function(e,t){let r=D(t);if(r.loadingStarted)return r.promise;if(e===i.Runtime)return r.loadingStarted=!0,L(t)&&r.resolve(),r.promise;if("function"==typeof importScripts)if(L(t));else if(N.test(t))self.TURBOPACK_NEXT_CHUNK_URLS.push(t),importScripts(t);else throw Error(`can't infer type of chunk from URL ${t} in worker`);else{let e=decodeURI(t);if(L(t))if(document.querySelectorAll(`link[rel=stylesheet][href="${t}"],link[rel=stylesheet][href^="${t}?"],link[rel=stylesheet][href="${e}"],link[rel=stylesheet][href^="${e}?"]`).length>0)r.resolve();else{let e=document.createElement("link");e.rel="stylesheet",e.href=t,e.onerror=()=>{r.reject()},e.onload=()=>{r.resolve()},document.head.appendChild(e)}else if(N.test(t)){let n=document.querySelectorAll(`script[src="${t}"],script[src^="${t}?"],script[src="${e}"],script[src^="${e}?"]`);if(n.length>0)for(let e of Array.from(n))e.addEventListener("error",()=>{r.reject()});else{let e=document.createElement("script");e.src=t,e.onerror=()=>{r.reject()},document.head.appendChild(e)}}else throw Error(`can't infer type of chunk from URL ${t}`)}return r.loadingStarted=!0,r.promise})(e,t),async loadWebAssembly(e,t,r,n,o){let i=fetch(q(r)),{instance:l}=await WebAssembly.instantiateStreaming(i,o);return l.exports},async loadWebAssemblyModule(e,t,r,n){let o=fetch(q(r));return await WebAssembly.compileStreaming(o)}};let H=globalThis.TURBOPACK;globalThis.TURBOPACK={push:F},H.forEach(F)})(); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/media/1bffadaabf893a1e-s.16ipb6fqu393i.woff2 b/litellm/proxy/_experimental/out/_next/static/media/1bffadaabf893a1e-s.16ipb6fqu393i.woff2 new file mode 100644 index 00000000000..57da6f8d46e Binary files /dev/null and b/litellm/proxy/_experimental/out/_next/static/media/1bffadaabf893a1e-s.16ipb6fqu393i.woff2 differ diff --git a/litellm/proxy/_experimental/out/_next/static/media/2bbe8d2671613f1f-s.067x_6k0k23tk.woff2 b/litellm/proxy/_experimental/out/_next/static/media/2bbe8d2671613f1f-s.067x_6k0k23tk.woff2 new file mode 100644 index 00000000000..072229b8706 Binary files /dev/null and b/litellm/proxy/_experimental/out/_next/static/media/2bbe8d2671613f1f-s.067x_6k0k23tk.woff2 differ diff --git a/litellm/proxy/_experimental/out/_next/static/media/2c55a0e60120577a-s.0bjc5tiuqdqro.woff2 b/litellm/proxy/_experimental/out/_next/static/media/2c55a0e60120577a-s.0bjc5tiuqdqro.woff2 new file mode 100644 index 00000000000..2cd45edf43e Binary files /dev/null and b/litellm/proxy/_experimental/out/_next/static/media/2c55a0e60120577a-s.0bjc5tiuqdqro.woff2 differ diff --git a/litellm/proxy/_experimental/out/_next/static/media/5476f68d60460930-s.0wxq9webf.ew4.woff2 b/litellm/proxy/_experimental/out/_next/static/media/5476f68d60460930-s.0wxq9webf.ew4.woff2 new file mode 100644 index 00000000000..9c71603a561 Binary files /dev/null and b/litellm/proxy/_experimental/out/_next/static/media/5476f68d60460930-s.0wxq9webf.ew4.woff2 differ diff --git a/litellm/proxy/_experimental/out/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2 b/litellm/proxy/_experimental/out/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2 new file mode 100644 index 00000000000..91dc3e85299 Binary files /dev/null and b/litellm/proxy/_experimental/out/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2 differ diff --git a/litellm/proxy/_experimental/out/_next/static/media/9c72aa0f40e4eef8-s.0m6w47a4e5dy9.woff2 b/litellm/proxy/_experimental/out/_next/static/media/9c72aa0f40e4eef8-s.0m6w47a4e5dy9.woff2 new file mode 100644 index 00000000000..bc0e0ab261b Binary files /dev/null and b/litellm/proxy/_experimental/out/_next/static/media/9c72aa0f40e4eef8-s.0m6w47a4e5dy9.woff2 differ diff --git a/litellm/proxy/_experimental/out/_next/static/media/ad66f9afd8947f86-s.11u06r12fd6v_.woff2 b/litellm/proxy/_experimental/out/_next/static/media/ad66f9afd8947f86-s.11u06r12fd6v_.woff2 new file mode 100644 index 00000000000..b6dd1facb44 Binary files /dev/null and b/litellm/proxy/_experimental/out/_next/static/media/ad66f9afd8947f86-s.11u06r12fd6v_.woff2 differ diff --git a/litellm/proxy/_experimental/out/_next/static/media/favicon.0~dgapwhi~75y.ico b/litellm/proxy/_experimental/out/_next/static/media/favicon.0~dgapwhi~75y.ico new file mode 100644 index 00000000000..7c45601d5c3 Binary files /dev/null and b/litellm/proxy/_experimental/out/_next/static/media/favicon.0~dgapwhi~75y.ico differ diff --git a/litellm/proxy/_experimental/out/_not-found/__next._full.txt b/litellm/proxy/_experimental/out/_not-found/__next._full.txt new file mode 100644 index 00000000000..e618870cb8a --- /dev/null +++ b/litellm/proxy/_experimental/out/_not-found/__next._full.txt @@ -0,0 +1,20 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +8:"$Sreact.suspense" +b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +d:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"P":null,"c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L7",null,{"children":["$","$8",null,{"name":"Next.MetadataOutlet","children":"$@9"}]}]]}],{},null,false,null]},null,false,"$@a"]},null,false,null],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$8",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +10:[] +a:"$W10" +c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +11:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +9:null +e:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L11","4",{}]] diff --git a/litellm/proxy/_experimental/out/_not-found/__next._head.txt b/litellm/proxy/_experimental/out/_not-found/__next._head.txt new file mode 100644 index 00000000000..4fbe008912a --- /dev/null +++ b/litellm/proxy/_experimental/out/_not-found/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/_not-found/__next._index.txt b/litellm/proxy/_experimental/out/_not-found/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/_not-found/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/_not-found/__next._not-found.__PAGE__.txt b/litellm/proxy/_experimental/out/_not-found/__next._not-found.__PAGE__.txt new file mode 100644 index 00000000000..aa975d04dd3 --- /dev/null +++ b/litellm/proxy/_experimental/out/_not-found/__next._not-found.__PAGE__.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +3:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:null diff --git a/litellm/proxy/_experimental/out/_not-found/__next._not-found.txt b/litellm/proxy/_experimental/out/_not-found/__next._not-found.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/_not-found/__next._not-found.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/_not-found/__next._tree.txt b/litellm/proxy/_experimental/out/_not-found/__next._tree.txt new file mode 100644 index 00000000000..61939ed5470 --- /dev/null +++ b/litellm/proxy/_experimental/out/_not-found/__next._tree.txt @@ -0,0 +1,3 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"/_not-found","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/_not-found/index.html b/litellm/proxy/_experimental/out/_not-found/index.html new file mode 100644 index 00000000000..7d4cc0b67af --- /dev/null +++ b/litellm/proxy/_experimental/out/_not-found/index.html @@ -0,0 +1 @@ +404: This page could not be found.LiteLLM Dashboard

404

This page could not be found.

\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_not-found/index.txt b/litellm/proxy/_experimental/out/_not-found/index.txt new file mode 100644 index 00000000000..e618870cb8a --- /dev/null +++ b/litellm/proxy/_experimental/out/_not-found/index.txt @@ -0,0 +1,20 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +8:"$Sreact.suspense" +b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +d:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"P":null,"c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L7",null,{"children":["$","$8",null,{"name":"Next.MetadataOutlet","children":"$@9"}]}]]}],{},null,false,null]},null,false,"$@a"]},null,false,null],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$8",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +10:[] +a:"$W10" +c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +11:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +9:null +e:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L11","4",{}]] diff --git a/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.access-groups.__PAGE__.txt b/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.access-groups.__PAGE__.txt new file mode 100644 index 00000000000..056f51071a2 --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.access-groups.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[852119,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/18aswm2wrvkis.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0scfmfivwcppe.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sstlyp4g1tlt.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/18aswm2wrvkis.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0scfmfivwcppe.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0sstlyp4g1tlt.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.access-groups.txt b/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.access-groups.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.access-groups.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/access-groups/__next._full.txt b/litellm/proxy/_experimental/out/access-groups/__next._full.txt new file mode 100644 index 00000000000..a672942b890 --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","access-groups",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["access-groups",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[852119,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/18aswm2wrvkis.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0scfmfivwcppe.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sstlyp4g1tlt.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/18aswm2wrvkis.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0scfmfivwcppe.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0sstlyp4g1tlt.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/access-groups/__next._head.txt b/litellm/proxy/_experimental/out/access-groups/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/access-groups/__next._index.txt b/litellm/proxy/_experimental/out/access-groups/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/access-groups/__next._tree.txt b/litellm/proxy/_experimental/out/access-groups/__next._tree.txt new file mode 100644 index 00000000000..95f494c0c2e --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"access-groups","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/access-groups/index.html b/litellm/proxy/_experimental/out/access-groups/index.html new file mode 100644 index 00000000000..49097b3f178 --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/access-groups/index.txt b/litellm/proxy/_experimental/out/access-groups/index.txt new file mode 100644 index 00000000000..a672942b890 --- /dev/null +++ b/litellm/proxy/_experimental/out/access-groups/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","access-groups",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["access-groups",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[852119,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/18aswm2wrvkis.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0scfmfivwcppe.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sstlyp4g1tlt.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/18aswm2wrvkis.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0scfmfivwcppe.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0sstlyp4g1tlt.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.admin-panel.__PAGE__.txt b/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.admin-panel.__PAGE__.txt new file mode 100644 index 00000000000..623bb00d47e --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.admin-panel.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[648214,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0x~cndb57rdjx.js","/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0ror7df3rm9k-.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0lv5868t_e1qc.js","/litellm-asset-prefix/_next/static/chunks/086wcbw3gq.hj.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0x~cndb57rdjx.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0ror7df3rm9k-.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0lv5868t_e1qc.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/086wcbw3gq.hj.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.admin-panel.txt b/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.admin-panel.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.admin-panel.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/admin-panel/__next._full.txt b/litellm/proxy/_experimental/out/admin-panel/__next._full.txt new file mode 100644 index 00000000000..04d7410276c --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","admin-panel",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["admin-panel",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[648214,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0x~cndb57rdjx.js","/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0ror7df3rm9k-.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0lv5868t_e1qc.js","/litellm-asset-prefix/_next/static/chunks/086wcbw3gq.hj.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0x~cndb57rdjx.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0ror7df3rm9k-.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0lv5868t_e1qc.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/086wcbw3gq.hj.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/admin-panel/__next._head.txt b/litellm/proxy/_experimental/out/admin-panel/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/admin-panel/__next._index.txt b/litellm/proxy/_experimental/out/admin-panel/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/admin-panel/__next._tree.txt b/litellm/proxy/_experimental/out/admin-panel/__next._tree.txt new file mode 100644 index 00000000000..d348d3a6ffc --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"admin-panel","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/admin-panel/index.html b/litellm/proxy/_experimental/out/admin-panel/index.html new file mode 100644 index 00000000000..8eb729df615 --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/admin-panel/index.txt b/litellm/proxy/_experimental/out/admin-panel/index.txt new file mode 100644 index 00000000000..04d7410276c --- /dev/null +++ b/litellm/proxy/_experimental/out/admin-panel/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","admin-panel",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["admin-panel",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[648214,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0x~cndb57rdjx.js","/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0ror7df3rm9k-.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0lv5868t_e1qc.js","/litellm-asset-prefix/_next/static/chunks/086wcbw3gq.hj.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0x~cndb57rdjx.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0ror7df3rm9k-.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0lv5868t_e1qc.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/086wcbw3gq.hj.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.agents.__PAGE__.txt b/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.agents.__PAGE__.txt new file mode 100644 index 00000000000..37a33306e72 --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.agents.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[298805,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/03e_5nw.1urn4.js","/litellm-asset-prefix/_next/static/chunks/0y4fhi8l9yeht.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/03m16pvgn6tls.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0nvi66x2vqzm2.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/03e_5nw.1urn4.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0y4fhi8l9yeht.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/03m16pvgn6tls.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0nvi66x2vqzm2.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.agents.txt b/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.agents.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.agents.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/agents/__next._full.txt b/litellm/proxy/_experimental/out/agents/__next._full.txt new file mode 100644 index 00000000000..b9534400b06 --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","agents",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["agents",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[298805,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/03e_5nw.1urn4.js","/litellm-asset-prefix/_next/static/chunks/0y4fhi8l9yeht.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/03m16pvgn6tls.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0nvi66x2vqzm2.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/03e_5nw.1urn4.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0y4fhi8l9yeht.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/03m16pvgn6tls.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0nvi66x2vqzm2.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/agents/__next._head.txt b/litellm/proxy/_experimental/out/agents/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/agents/__next._index.txt b/litellm/proxy/_experimental/out/agents/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/agents/__next._tree.txt b/litellm/proxy/_experimental/out/agents/__next._tree.txt new file mode 100644 index 00000000000..d7c0590a10c --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"agents","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/agents/index.html b/litellm/proxy/_experimental/out/agents/index.html new file mode 100644 index 00000000000..ccf7a8d469d --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/agents/index.txt b/litellm/proxy/_experimental/out/agents/index.txt new file mode 100644 index 00000000000..b9534400b06 --- /dev/null +++ b/litellm/proxy/_experimental/out/agents/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","agents",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["agents",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[298805,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/03e_5nw.1urn4.js","/litellm-asset-prefix/_next/static/chunks/0y4fhi8l9yeht.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/03m16pvgn6tls.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0nvi66x2vqzm2.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/03e_5nw.1urn4.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0y4fhi8l9yeht.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/03m16pvgn6tls.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0nvi66x2vqzm2.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.api-keys.__PAGE__.txt b/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.api-keys.__PAGE__.txt new file mode 100644 index 00000000000..c7dffa8ea5b --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.api-keys.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[973095,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","/litellm-asset-prefix/_next/static/chunks/0u55zmkgol9ci.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0u55zmkgol9ci.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.api-keys.txt b/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.api-keys.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.api-keys.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/api-keys/__next._full.txt b/litellm/proxy/_experimental/out/api-keys/__next._full.txt new file mode 100644 index 00000000000..fdffd34ba73 --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","api-keys",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["api-keys",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[973095,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","/litellm-asset-prefix/_next/static/chunks/0u55zmkgol9ci.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0u55zmkgol9ci.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/api-keys/__next._head.txt b/litellm/proxy/_experimental/out/api-keys/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/api-keys/__next._index.txt b/litellm/proxy/_experimental/out/api-keys/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/api-keys/__next._tree.txt b/litellm/proxy/_experimental/out/api-keys/__next._tree.txt new file mode 100644 index 00000000000..303ad1a1117 --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"api-keys","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/api-keys/index.html b/litellm/proxy/_experimental/out/api-keys/index.html new file mode 100644 index 00000000000..9b09f43d2ca --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/api-keys/index.txt b/litellm/proxy/_experimental/out/api-keys/index.txt new file mode 100644 index 00000000000..fdffd34ba73 --- /dev/null +++ b/litellm/proxy/_experimental/out/api-keys/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","api-keys",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["api-keys",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[973095,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","/litellm-asset-prefix/_next/static/chunks/0u55zmkgol9ci.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0u55zmkgol9ci.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.api-reference.__PAGE__.txt b/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.api-reference.__PAGE__.txt new file mode 100644 index 00000000000..673640f784f --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.api-reference.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[191905,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/09z_~48rtyt6c.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09z_~48rtyt6c.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.api-reference.txt b/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.api-reference.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.api-reference.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/api-reference/__next._full.txt b/litellm/proxy/_experimental/out/api-reference/__next._full.txt new file mode 100644 index 00000000000..d76b9c7adfe --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","api-reference",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["api-reference",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[191905,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/09z_~48rtyt6c.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09z_~48rtyt6c.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/api-reference/__next._head.txt b/litellm/proxy/_experimental/out/api-reference/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/api-reference/__next._index.txt b/litellm/proxy/_experimental/out/api-reference/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/api-reference/__next._tree.txt b/litellm/proxy/_experimental/out/api-reference/__next._tree.txt new file mode 100644 index 00000000000..8b81ebab5b8 --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"api-reference","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/api-reference/index.html b/litellm/proxy/_experimental/out/api-reference/index.html new file mode 100644 index 00000000000..364658163f4 --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/api-reference/index.txt b/litellm/proxy/_experimental/out/api-reference/index.txt new file mode 100644 index 00000000000..d76b9c7adfe --- /dev/null +++ b/litellm/proxy/_experimental/out/api-reference/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","api-reference",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["api-reference",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[191905,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/09z_~48rtyt6c.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09z_~48rtyt6c.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/assets/audit-logs-preview.png b/litellm/proxy/_experimental/out/assets/audit-logs-preview.png new file mode 100644 index 00000000000..4e97c291d24 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/audit-logs-preview.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/a2a_agent.png b/litellm/proxy/_experimental/out/assets/logos/a2a_agent.png new file mode 100644 index 00000000000..305ae1acaf4 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/a2a_agent.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/ai21.svg b/litellm/proxy/_experimental/out/assets/logos/ai21.svg new file mode 100644 index 00000000000..7e62a9517af --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/ai21.svg @@ -0,0 +1 @@ +AI21 \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/aim_logo.jpeg b/litellm/proxy/_experimental/out/assets/logos/aim_logo.jpeg new file mode 100644 index 00000000000..60fc2a9295c Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/aim_logo.jpeg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/aim_security.jpeg b/litellm/proxy/_experimental/out/assets/logos/aim_security.jpeg new file mode 100644 index 00000000000..60fc2a9295c Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/aim_security.jpeg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/aiml_api.svg b/litellm/proxy/_experimental/out/assets/logos/aiml_api.svg new file mode 100644 index 00000000000..660ab920ec0 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/aiml_api.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/akto.svg b/litellm/proxy/_experimental/out/assets/logos/akto.svg new file mode 100644 index 00000000000..cdea32535f2 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/akto.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/anthropic.svg b/litellm/proxy/_experimental/out/assets/logos/anthropic.svg new file mode 100644 index 00000000000..a37f591fb76 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/anthropic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/aporia.png b/litellm/proxy/_experimental/out/assets/logos/aporia.png new file mode 100644 index 00000000000..34bc1767918 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/aporia.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/arize.png b/litellm/proxy/_experimental/out/assets/logos/arize.png new file mode 100644 index 00000000000..92a7741141c Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/arize.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/assemblyai_small.png b/litellm/proxy/_experimental/out/assets/logos/assemblyai_small.png new file mode 100644 index 00000000000..19eaf1a126d Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/assemblyai_small.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/aws.svg b/litellm/proxy/_experimental/out/assets/logos/aws.svg new file mode 100644 index 00000000000..53896fa05f4 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/aws.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/azure_ai_foundry.png b/litellm/proxy/_experimental/out/assets/logos/azure_ai_foundry.png new file mode 100644 index 00000000000..9f19b52e0bc Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/azure_ai_foundry.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/baseten.svg b/litellm/proxy/_experimental/out/assets/logos/baseten.svg new file mode 100644 index 00000000000..6e98ffbc315 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/baseten.svg @@ -0,0 +1 @@ +Baseten \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/bedrock.svg b/litellm/proxy/_experimental/out/assets/logos/bedrock.svg new file mode 100644 index 00000000000..e0f929a7a97 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/bedrock.svg @@ -0,0 +1 @@ +Bedrock \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/braintrust.png b/litellm/proxy/_experimental/out/assets/logos/braintrust.png new file mode 100644 index 00000000000..8da739b6971 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/braintrust.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/cato_networks.svg b/litellm/proxy/_experimental/out/assets/logos/cato_networks.svg new file mode 100644 index 00000000000..290ec5eb8a5 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/cato_networks.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/cerebras.svg b/litellm/proxy/_experimental/out/assets/logos/cerebras.svg new file mode 100644 index 00000000000..426f6430c23 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/cerebras.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/cisco.png b/litellm/proxy/_experimental/out/assets/logos/cisco.png new file mode 100644 index 00000000000..034e2fa72eb Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/cisco.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/cloudflare.svg b/litellm/proxy/_experimental/out/assets/logos/cloudflare.svg new file mode 100644 index 00000000000..d555b6f2c08 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/cloudflare.svg @@ -0,0 +1 @@ +Cloudflare \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/cohere.svg b/litellm/proxy/_experimental/out/assets/logos/cohere.svg new file mode 100644 index 00000000000..cb1b2a5919e --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/cohere.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/cometapi.svg b/litellm/proxy/_experimental/out/assets/logos/cometapi.svg new file mode 100644 index 00000000000..c7469e4f643 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/cometapi.svg @@ -0,0 +1 @@ +CometAPI \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/cursor.svg b/litellm/proxy/_experimental/out/assets/logos/cursor.svg new file mode 100644 index 00000000000..79b44c5e83b --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/cursor.svg @@ -0,0 +1 @@ +Cursor diff --git a/litellm/proxy/_experimental/out/assets/logos/databricks.svg b/litellm/proxy/_experimental/out/assets/logos/databricks.svg new file mode 100644 index 00000000000..cd079ceb224 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/databricks.svg @@ -0,0 +1 @@ +DBRX \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/datadog.png b/litellm/proxy/_experimental/out/assets/logos/datadog.png new file mode 100644 index 00000000000..0f66cbe2e6a Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/datadog.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/dataforseo.png b/litellm/proxy/_experimental/out/assets/logos/dataforseo.png new file mode 100644 index 00000000000..fced13674b3 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/dataforseo.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/deepgram.png b/litellm/proxy/_experimental/out/assets/logos/deepgram.png new file mode 100644 index 00000000000..591a8ae0a70 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/deepgram.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/deepinfra.png b/litellm/proxy/_experimental/out/assets/logos/deepinfra.png new file mode 100644 index 00000000000..541497f2cca Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/deepinfra.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/deepseek.svg b/litellm/proxy/_experimental/out/assets/logos/deepseek.svg new file mode 100644 index 00000000000..c4754047da2 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/deepseek.svg @@ -0,0 +1,25 @@ + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/elevenlabs.png b/litellm/proxy/_experimental/out/assets/logos/elevenlabs.png new file mode 100644 index 00000000000..634ddfa0542 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/elevenlabs.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/enkrypt_ai.avif b/litellm/proxy/_experimental/out/assets/logos/enkrypt_ai.avif new file mode 100644 index 00000000000..a6228afb5ca Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/enkrypt_ai.avif differ diff --git a/litellm/proxy/_experimental/out/assets/logos/exa_ai.png b/litellm/proxy/_experimental/out/assets/logos/exa_ai.png new file mode 100644 index 00000000000..d5512bbd9ed Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/exa_ai.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/fal_ai.jpg b/litellm/proxy/_experimental/out/assets/logos/fal_ai.jpg new file mode 100644 index 00000000000..5de52c9188b Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/fal_ai.jpg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/featherless.svg b/litellm/proxy/_experimental/out/assets/logos/featherless.svg new file mode 100644 index 00000000000..9d5690d8d4b --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/featherless.svg @@ -0,0 +1 @@ +featherless.ai \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/figma.svg b/litellm/proxy/_experimental/out/assets/logos/figma.svg new file mode 100644 index 00000000000..2d8b70457d9 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/figma.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/fireworks.svg b/litellm/proxy/_experimental/out/assets/logos/fireworks.svg new file mode 100644 index 00000000000..a23445cf94b --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/fireworks.svg @@ -0,0 +1 @@ +Fireworks \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/friendli.svg b/litellm/proxy/_experimental/out/assets/logos/friendli.svg new file mode 100644 index 00000000000..e854d2ab485 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/friendli.svg @@ -0,0 +1 @@ +Friendli \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/galileo.ico b/litellm/proxy/_experimental/out/assets/logos/galileo.ico new file mode 100644 index 00000000000..c50b9de4df5 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/galileo.ico differ diff --git a/litellm/proxy/_experimental/out/assets/logos/github.svg b/litellm/proxy/_experimental/out/assets/logos/github.svg new file mode 100644 index 00000000000..93262122815 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/github.svg @@ -0,0 +1 @@ +Github \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/github_copilot.svg b/litellm/proxy/_experimental/out/assets/logos/github_copilot.svg new file mode 100644 index 00000000000..fd0bc9ed7aa --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/github_copilot.svg @@ -0,0 +1 @@ +GithubCopilot \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/gitlab.svg b/litellm/proxy/_experimental/out/assets/logos/gitlab.svg new file mode 100644 index 00000000000..18a89fa328d --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/gitlab.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/gmail.svg b/litellm/proxy/_experimental/out/assets/logos/gmail.svg new file mode 100644 index 00000000000..d702890620d --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/gmail.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/google.svg b/litellm/proxy/_experimental/out/assets/logos/google.svg new file mode 100644 index 00000000000..7bc4a38ce7a --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/google.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/google_drive.svg b/litellm/proxy/_experimental/out/assets/logos/google_drive.svg new file mode 100644 index 00000000000..7048af9915e --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/google_drive.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/google_pse.png b/litellm/proxy/_experimental/out/assets/logos/google_pse.png new file mode 100644 index 00000000000..741997b36b3 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/google_pse.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/groq.svg b/litellm/proxy/_experimental/out/assets/logos/groq.svg new file mode 100644 index 00000000000..550316a7e0e --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/groq.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/guardrails_ai.jpeg b/litellm/proxy/_experimental/out/assets/logos/guardrails_ai.jpeg new file mode 100644 index 00000000000..b0935b74b98 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/guardrails_ai.jpeg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/hubspot.svg b/litellm/proxy/_experimental/out/assets/logos/hubspot.svg new file mode 100644 index 00000000000..b993945ac6b --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/hubspot.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/huggingface.svg b/litellm/proxy/_experimental/out/assets/logos/huggingface.svg new file mode 100644 index 00000000000..dc1cf3ffb77 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/huggingface.svg @@ -0,0 +1 @@ +HuggingFace \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/hyperbolic.svg b/litellm/proxy/_experimental/out/assets/logos/hyperbolic.svg new file mode 100644 index 00000000000..76536c29c53 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/hyperbolic.svg @@ -0,0 +1 @@ +Hyperbolic \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/infinity.png b/litellm/proxy/_experimental/out/assets/logos/infinity.png new file mode 100644 index 00000000000..d0f72579986 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/infinity.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/javelin.png b/litellm/proxy/_experimental/out/assets/logos/javelin.png new file mode 100644 index 00000000000..1a3fe31b585 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/javelin.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/jina.png b/litellm/proxy/_experimental/out/assets/logos/jina.png new file mode 100644 index 00000000000..5dff74a7ece Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/jina.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/jira.svg b/litellm/proxy/_experimental/out/assets/logos/jira.svg new file mode 100644 index 00000000000..fb10ca75173 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/jira.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/lago.svg b/litellm/proxy/_experimental/out/assets/logos/lago.svg new file mode 100644 index 00000000000..d5264f756dd --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/lago.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/lakeraai.jpeg b/litellm/proxy/_experimental/out/assets/logos/lakeraai.jpeg new file mode 100644 index 00000000000..b30d3ede6be Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/lakeraai.jpeg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/lambda.svg b/litellm/proxy/_experimental/out/assets/logos/lambda.svg new file mode 100644 index 00000000000..346414694d6 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/lambda.svg @@ -0,0 +1 @@ +Lambda \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/langflow.svg b/litellm/proxy/_experimental/out/assets/logos/langflow.svg new file mode 100644 index 00000000000..1c7b36c4dd6 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/langflow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/langfuse.png b/litellm/proxy/_experimental/out/assets/logos/langfuse.png new file mode 100644 index 00000000000..8b765fb041d Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/langfuse.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/langfuse.svg b/litellm/proxy/_experimental/out/assets/logos/langfuse.svg new file mode 100644 index 00000000000..ccf072e5dbb --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/langfuse.svg @@ -0,0 +1 @@ +Langfuse \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/langgraph.png b/litellm/proxy/_experimental/out/assets/logos/langgraph.png new file mode 100644 index 00000000000..3df93e5205b Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/langgraph.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/langsmith.png b/litellm/proxy/_experimental/out/assets/logos/langsmith.png new file mode 100644 index 00000000000..3df93e5205b Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/langsmith.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/lasso.png b/litellm/proxy/_experimental/out/assets/logos/lasso.png new file mode 100644 index 00000000000..f4ffcb5f284 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/lasso.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/linear.svg b/litellm/proxy/_experimental/out/assets/logos/linear.svg new file mode 100644 index 00000000000..83662a1f9ff --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/linear.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/litellm.jpg b/litellm/proxy/_experimental/out/assets/logos/litellm.jpg new file mode 100644 index 00000000000..a10a1d24969 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/litellm.jpg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/litellm_logo.jpg b/litellm/proxy/_experimental/out/assets/logos/litellm_logo.jpg new file mode 100644 index 00000000000..6fe96e2ed35 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/litellm_logo.jpg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/llm_guard.png b/litellm/proxy/_experimental/out/assets/logos/llm_guard.png new file mode 100644 index 00000000000..ed01c0f044e Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/llm_guard.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/lmstudio.svg b/litellm/proxy/_experimental/out/assets/logos/lmstudio.svg new file mode 100644 index 00000000000..d38a17ee43f --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/lmstudio.svg @@ -0,0 +1 @@ +LM Studio \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/mcp_logo.png b/litellm/proxy/_experimental/out/assets/logos/mcp_logo.png new file mode 100644 index 00000000000..d920e50e7f2 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/mcp_logo.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/meta_llama.svg b/litellm/proxy/_experimental/out/assets/logos/meta_llama.svg new file mode 100644 index 00000000000..a0b2a5e30a1 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/meta_llama.svg @@ -0,0 +1 @@ +MetaAI \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/microsoft_azure.svg b/litellm/proxy/_experimental/out/assets/logos/microsoft_azure.svg new file mode 100644 index 00000000000..cd96a7d3724 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/microsoft_azure.svg @@ -0,0 +1,72 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/milvus.svg b/litellm/proxy/_experimental/out/assets/logos/milvus.svg new file mode 100644 index 00000000000..76154467b4b --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/milvus.svg @@ -0,0 +1 @@ +milvus-horizontal-color \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/minimax.svg b/litellm/proxy/_experimental/out/assets/logos/minimax.svg new file mode 100644 index 00000000000..59b741bbcb7 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/minimax.svg @@ -0,0 +1 @@ +资源 2 \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/mistral.svg b/litellm/proxy/_experimental/out/assets/logos/mistral.svg new file mode 100644 index 00000000000..8e03e244bf1 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/mistral.svg @@ -0,0 +1 @@ +Mistral \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/moonshot.svg b/litellm/proxy/_experimental/out/assets/logos/moonshot.svg new file mode 100644 index 00000000000..15a0380628b --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/moonshot.svg @@ -0,0 +1 @@ +MoonshotAI \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/morph.svg b/litellm/proxy/_experimental/out/assets/logos/morph.svg new file mode 100644 index 00000000000..dbe7c4167c1 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/morph.svg @@ -0,0 +1 @@ +Morph \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/nebius.svg b/litellm/proxy/_experimental/out/assets/logos/nebius.svg new file mode 100644 index 00000000000..2662140b21a --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/nebius.svg @@ -0,0 +1 @@ +Nebius \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/newrelic.png b/litellm/proxy/_experimental/out/assets/logos/newrelic.png new file mode 100644 index 00000000000..c841e3e7136 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/newrelic.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/noma_security.png b/litellm/proxy/_experimental/out/assets/logos/noma_security.png new file mode 100644 index 00000000000..8a332586d43 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/noma_security.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/notion.svg b/litellm/proxy/_experimental/out/assets/logos/notion.svg new file mode 100644 index 00000000000..170b9bb4140 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/notion.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/novita.svg b/litellm/proxy/_experimental/out/assets/logos/novita.svg new file mode 100644 index 00000000000..0658ce0f092 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/novita.svg @@ -0,0 +1 @@ +Novita AI \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/nvidia_nim.svg b/litellm/proxy/_experimental/out/assets/logos/nvidia_nim.svg new file mode 100644 index 00000000000..a9683c2e00d --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/nvidia_nim.svg @@ -0,0 +1 @@ +Nvidia \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/nvidia_triton.png b/litellm/proxy/_experimental/out/assets/logos/nvidia_triton.png new file mode 100644 index 00000000000..b6d7a5b2bb6 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/nvidia_triton.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/ollama.svg b/litellm/proxy/_experimental/out/assets/logos/ollama.svg new file mode 100644 index 00000000000..d7780867b53 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/ollama.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/openai_small.svg b/litellm/proxy/_experimental/out/assets/logos/openai_small.svg new file mode 100644 index 00000000000..52dad8269ec --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/openai_small.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/openmeter.png b/litellm/proxy/_experimental/out/assets/logos/openmeter.png new file mode 100644 index 00000000000..fa9f880b76b Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/openmeter.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/openrouter.svg b/litellm/proxy/_experimental/out/assets/logos/openrouter.svg new file mode 100644 index 00000000000..c9952c11f6d --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/openrouter.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/oracle.svg b/litellm/proxy/_experimental/out/assets/logos/oracle.svg new file mode 100644 index 00000000000..0981dfcff28 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/oracle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/otel.png b/litellm/proxy/_experimental/out/assets/logos/otel.png new file mode 100644 index 00000000000..878b0781c2e Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/otel.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/palo_alto_networks.jpeg b/litellm/proxy/_experimental/out/assets/logos/palo_alto_networks.jpeg new file mode 100644 index 00000000000..dc3cb66c6e7 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/palo_alto_networks.jpeg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/pangea.png b/litellm/proxy/_experimental/out/assets/logos/pangea.png new file mode 100644 index 00000000000..fb815530fee Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/pangea.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/parallel_ai.png b/litellm/proxy/_experimental/out/assets/logos/parallel_ai.png new file mode 100644 index 00000000000..c877d869e8b Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/parallel_ai.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/perplexity-ai.svg b/litellm/proxy/_experimental/out/assets/logos/perplexity-ai.svg new file mode 100644 index 00000000000..e828b6dfbf1 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/perplexity-ai.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/perplexity.png b/litellm/proxy/_experimental/out/assets/logos/perplexity.png new file mode 100644 index 00000000000..57d55970452 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/perplexity.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/pillar.jpeg b/litellm/proxy/_experimental/out/assets/logos/pillar.jpeg new file mode 100644 index 00000000000..084e8599764 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/pillar.jpeg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/postgresql.svg b/litellm/proxy/_experimental/out/assets/logos/postgresql.svg new file mode 100644 index 00000000000..7fed68bcd33 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/postgresql.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/presidio.png b/litellm/proxy/_experimental/out/assets/logos/presidio.png new file mode 100644 index 00000000000..cce91390178 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/presidio.png differ diff --git a/ui/litellm-dashboard/src/components/guardrails/prompt_security.png b/litellm/proxy/_experimental/out/assets/logos/prompt_security.png similarity index 100% rename from ui/litellm-dashboard/src/components/guardrails/prompt_security.png rename to litellm/proxy/_experimental/out/assets/logos/prompt_security.png diff --git a/litellm/proxy/_experimental/out/assets/logos/promptguard.svg b/litellm/proxy/_experimental/out/assets/logos/promptguard.svg new file mode 100644 index 00000000000..44cdd52eae3 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/promptguard.svg @@ -0,0 +1,95 @@ + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/pydantic.svg b/litellm/proxy/_experimental/out/assets/logos/pydantic.svg new file mode 100644 index 00000000000..0ff8e5c44c7 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/pydantic.svg @@ -0,0 +1,5 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/qohash.jpg b/litellm/proxy/_experimental/out/assets/logos/qohash.jpg new file mode 100644 index 00000000000..50227ab3910 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/qohash.jpg differ diff --git a/litellm/proxy/_experimental/out/assets/logos/qwen.png b/litellm/proxy/_experimental/out/assets/logos/qwen.png new file mode 100644 index 00000000000..d9feba46a28 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/qwen.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/recraft.svg b/litellm/proxy/_experimental/out/assets/logos/recraft.svg new file mode 100644 index 00000000000..da5d951cac9 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/recraft.svg @@ -0,0 +1 @@ +Recraft \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/repelloai.png b/litellm/proxy/_experimental/out/assets/logos/repelloai.png new file mode 100644 index 00000000000..d93c0096f60 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/repelloai.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/replicate.svg b/litellm/proxy/_experimental/out/assets/logos/replicate.svg new file mode 100644 index 00000000000..35112ab3a7c --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/replicate.svg @@ -0,0 +1 @@ +Replicate \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/runway.png b/litellm/proxy/_experimental/out/assets/logos/runway.png new file mode 100644 index 00000000000..c909cb9e0f2 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/runway.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/s3_vector.png b/litellm/proxy/_experimental/out/assets/logos/s3_vector.png new file mode 100644 index 00000000000..15a1a456e12 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/s3_vector.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/salesforce.svg b/litellm/proxy/_experimental/out/assets/logos/salesforce.svg new file mode 100644 index 00000000000..1a541a004f1 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/salesforce.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/sambanova.svg b/litellm/proxy/_experimental/out/assets/logos/sambanova.svg new file mode 100644 index 00000000000..1c3ce8052e3 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/sambanova.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/sap.png b/litellm/proxy/_experimental/out/assets/logos/sap.png new file mode 100644 index 00000000000..7d3c4604c4c Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/sap.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/search1api.png b/litellm/proxy/_experimental/out/assets/logos/search1api.png new file mode 100644 index 00000000000..e9091d3668f Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/search1api.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/secret_detect.png b/litellm/proxy/_experimental/out/assets/logos/secret_detect.png new file mode 100644 index 00000000000..b7e09d33077 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/secret_detect.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/sentry.svg b/litellm/proxy/_experimental/out/assets/logos/sentry.svg new file mode 100644 index 00000000000..9c3733dc43e --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/sentry.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/shopify.svg b/litellm/proxy/_experimental/out/assets/logos/shopify.svg new file mode 100644 index 00000000000..fcc7547269d --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/shopify.svg @@ -0,0 +1,4 @@ + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/slack.svg b/litellm/proxy/_experimental/out/assets/logos/slack.svg new file mode 100644 index 00000000000..801de4f70c8 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/slack.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/snowflake.svg b/litellm/proxy/_experimental/out/assets/logos/snowflake.svg new file mode 100644 index 00000000000..e88dcad650b --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/snowflake.svg @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/soniox.svg b/litellm/proxy/_experimental/out/assets/logos/soniox.svg new file mode 100644 index 00000000000..7b7408401c4 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/soniox.svg @@ -0,0 +1 @@ +Soniox diff --git a/litellm/proxy/_experimental/out/assets/logos/stripe.svg b/litellm/proxy/_experimental/out/assets/logos/stripe.svg new file mode 100644 index 00000000000..ac16a6fb170 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/stripe.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/tavily.png b/litellm/proxy/_experimental/out/assets/logos/tavily.png new file mode 100644 index 00000000000..81dcda6b0d5 Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/tavily.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/togetherai.svg b/litellm/proxy/_experimental/out/assets/logos/togetherai.svg new file mode 100644 index 00000000000..18aa72fc2cf --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/togetherai.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/topaz.svg b/litellm/proxy/_experimental/out/assets/logos/topaz.svg new file mode 100644 index 00000000000..d8efae94340 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/topaz.svg @@ -0,0 +1 @@ +TopazLabs \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/twilio.svg b/litellm/proxy/_experimental/out/assets/logos/twilio.svg new file mode 100644 index 00000000000..3517a2824d9 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/twilio.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/v0.svg b/litellm/proxy/_experimental/out/assets/logos/v0.svg new file mode 100644 index 00000000000..aeada8b7ebe --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/v0.svg @@ -0,0 +1 @@ +V0 \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/vercel.svg b/litellm/proxy/_experimental/out/assets/logos/vercel.svg new file mode 100644 index 00000000000..97316223317 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/vercel.svg @@ -0,0 +1 @@ +Vercel \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/vllm.png b/litellm/proxy/_experimental/out/assets/logos/vllm.png new file mode 100644 index 00000000000..6026ec741dc Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/vllm.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/volcengine.png b/litellm/proxy/_experimental/out/assets/logos/volcengine.png new file mode 100644 index 00000000000..6dd6c0ebefc Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/volcengine.png differ diff --git a/litellm/proxy/_experimental/out/assets/logos/voyage.webp b/litellm/proxy/_experimental/out/assets/logos/voyage.webp new file mode 100644 index 00000000000..0225878d13c Binary files /dev/null and b/litellm/proxy/_experimental/out/assets/logos/voyage.webp differ diff --git a/litellm/proxy/_experimental/out/assets/logos/watsonx.svg b/litellm/proxy/_experimental/out/assets/logos/watsonx.svg new file mode 100644 index 00000000000..019b9c8096e --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/watsonx.svg @@ -0,0 +1 @@ +IBM \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/xai.svg b/litellm/proxy/_experimental/out/assets/logos/xai.svg new file mode 100644 index 00000000000..9491b192fd5 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/xai.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/xecguard.svg b/litellm/proxy/_experimental/out/assets/logos/xecguard.svg new file mode 100644 index 00000000000..060718dc363 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/xecguard.svg @@ -0,0 +1,4 @@ + + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/xinference.svg b/litellm/proxy/_experimental/out/assets/logos/xinference.svg new file mode 100644 index 00000000000..6520116fd15 --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/xinference.svg @@ -0,0 +1 @@ +Xinference \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/assets/logos/zapier.svg b/litellm/proxy/_experimental/out/assets/logos/zapier.svg new file mode 100644 index 00000000000..8428ba82a5b --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/zapier.svg @@ -0,0 +1,3 @@ + + + diff --git a/litellm/proxy/_experimental/out/assets/logos/zscaler.svg b/litellm/proxy/_experimental/out/assets/logos/zscaler.svg new file mode 100644 index 00000000000..2a95cb02aed --- /dev/null +++ b/litellm/proxy/_experimental/out/assets/logos/zscaler.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.budgets.__PAGE__.txt b/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.budgets.__PAGE__.txt new file mode 100644 index 00000000000..88346f97960 --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.budgets.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[359200,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/02zbkoezzcnn1.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0-px9-g~2oyp5.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/02zbkoezzcnn1.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0-px9-g~2oyp5.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.budgets.txt b/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.budgets.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.budgets.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/budgets/__next._full.txt b/litellm/proxy/_experimental/out/budgets/__next._full.txt new file mode 100644 index 00000000000..4fa28695692 --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","budgets",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["budgets",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[359200,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/02zbkoezzcnn1.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0-px9-g~2oyp5.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/02zbkoezzcnn1.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0-px9-g~2oyp5.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/budgets/__next._head.txt b/litellm/proxy/_experimental/out/budgets/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/budgets/__next._index.txt b/litellm/proxy/_experimental/out/budgets/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/budgets/__next._tree.txt b/litellm/proxy/_experimental/out/budgets/__next._tree.txt new file mode 100644 index 00000000000..f0f2b7114a0 --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"budgets","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/budgets/index.html b/litellm/proxy/_experimental/out/budgets/index.html new file mode 100644 index 00000000000..57389257ef5 --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/budgets/index.txt b/litellm/proxy/_experimental/out/budgets/index.txt new file mode 100644 index 00000000000..4fa28695692 --- /dev/null +++ b/litellm/proxy/_experimental/out/budgets/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","budgets",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["budgets",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[359200,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/02zbkoezzcnn1.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0-px9-g~2oyp5.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/02zbkoezzcnn1.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0-px9-g~2oyp5.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.caching.__PAGE__.txt b/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.caching.__PAGE__.txt new file mode 100644 index 00000000000..9f9cf2b624c --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.caching.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[254709,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0qulu-1pxxbt3.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/129bujhdmi9ce.js","/litellm-asset-prefix/_next/static/chunks/12zuecs-ycilm.js","/litellm-asset-prefix/_next/static/chunks/0kic0gmx.szai.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0ds~u4~7m29__.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0qulu-1pxxbt3.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/129bujhdmi9ce.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/12zuecs-ycilm.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0kic0gmx.szai.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0ds~u4~7m29__.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.caching.txt b/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.caching.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.caching.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/caching/__next._full.txt b/litellm/proxy/_experimental/out/caching/__next._full.txt new file mode 100644 index 00000000000..7d3baae20f9 --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","caching",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["caching",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[254709,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0qulu-1pxxbt3.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/129bujhdmi9ce.js","/litellm-asset-prefix/_next/static/chunks/12zuecs-ycilm.js","/litellm-asset-prefix/_next/static/chunks/0kic0gmx.szai.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0ds~u4~7m29__.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0qulu-1pxxbt3.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/129bujhdmi9ce.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/12zuecs-ycilm.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0kic0gmx.szai.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0ds~u4~7m29__.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/caching/__next._head.txt b/litellm/proxy/_experimental/out/caching/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/caching/__next._index.txt b/litellm/proxy/_experimental/out/caching/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/caching/__next._tree.txt b/litellm/proxy/_experimental/out/caching/__next._tree.txt new file mode 100644 index 00000000000..c3527522fb6 --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"caching","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/caching/index.html b/litellm/proxy/_experimental/out/caching/index.html new file mode 100644 index 00000000000..ca1d5b19bd2 --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/caching/index.txt b/litellm/proxy/_experimental/out/caching/index.txt new file mode 100644 index 00000000000..7d3baae20f9 --- /dev/null +++ b/litellm/proxy/_experimental/out/caching/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","caching",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["caching",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[254709,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0qulu-1pxxbt3.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/129bujhdmi9ce.js","/litellm-asset-prefix/_next/static/chunks/12zuecs-ycilm.js","/litellm-asset-prefix/_next/static/chunks/0kic0gmx.szai.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0ds~u4~7m29__.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0qulu-1pxxbt3.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/129bujhdmi9ce.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/12zuecs-ycilm.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0kic0gmx.szai.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0ds~u4~7m29__.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/__next._full.txt b/litellm/proxy/_experimental/out/chat/__next._full.txt new file mode 100644 index 00000000000..d698a71559e --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/__next._full.txt @@ -0,0 +1,30 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +c:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{},null,false,null]},null,false,null]},null,false,null],"$Lb",false]],"m":"$undefined","G":["$c",["$Ld","$Le"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +f:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +10:I[321443,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/0ggd6bmz46d--.js","/litellm-asset-prefix/_next/static/chunks/06_vzvq0hkdw-.js","/litellm-asset-prefix/_next/static/chunks/00tczcrtv5upo.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js"],"default"] +13:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +14:"$Sreact.suspense" +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +18:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[["$","$Lf",null,{"Component":"$10","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@11","$@12"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0ggd6bmz46d--.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/06_vzvq0hkdw-.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/00tczcrtv5upo.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}]],["$","$L13",null,{"children":["$","$14",null,{"name":"Next.MetadataOutlet","children":"$@15"}]}]]}] +b:["$","$1","h",{"children":[null,["$","$L16",null,{"children":"$L17"}],["$","div",null,{"hidden":true,"children":["$","$L18",null,{"children":["$","$14",null,{"name":"Next.Metadata","children":"$L19"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +d:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +e:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +11:{} +12:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +17:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1a:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +15:null +19:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1a","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/__next._head.txt b/litellm/proxy/_experimental/out/chat/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/__next._index.txt b/litellm/proxy/_experimental/out/chat/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/__next._tree.txt b/litellm/proxy/_experimental/out/chat/__next._tree.txt new file mode 100644 index 00000000000..646f0e41ecc --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"chat","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/__next.chat.__PAGE__.txt b/litellm/proxy/_experimental/out/chat/__next.chat.__PAGE__.txt new file mode 100644 index 00000000000..af9b8310d23 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/__next.chat.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[321443,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/0ggd6bmz46d--.js","/litellm-asset-prefix/_next/static/chunks/06_vzvq0hkdw-.js","/litellm-asset-prefix/_next/static/chunks/00tczcrtv5upo.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0ggd6bmz46d--.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/06_vzvq0hkdw-.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/00tczcrtv5upo.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/chat/__next.chat.txt b/litellm/proxy/_experimental/out/chat/__next.chat.txt new file mode 100644 index 00000000000..1f9903dfc3e --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/__next.chat.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/chat/api-keys/__next._full.txt b/litellm/proxy/_experimental/out/chat/api-keys/__next._full.txt new file mode 100644 index 00000000000..1bdf4ea15ae --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/__next._full.txt @@ -0,0 +1,33 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +e:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat","api-keys",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["api-keys",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{"children":["$Lb",{},null,false,null]},null,false,"$@c"]},null,false,null]},null,false,null],"$Ld",false]],"m":"$undefined","G":["$e",["$Lf","$L10"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +11:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +12:I[516448,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/16c4tr94o_76g.js"],"default"] +15:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +16:"$Sreact.suspense" +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +b:["$","$1","c",{"children":[["$","$L11",null,{"Component":"$12","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@13","$@14"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/16c4tr94o_76g.js","async":true,"nonce":"$undefined"}]],["$","$L15",null,{"children":["$","$16",null,{"name":"Next.MetadataOutlet","children":"$@17"}]}]]}] +18:[] +c:"$W18" +d:["$","$1","h",{"children":[null,["$","$L19",null,{"children":"$L1a"}],["$","div",null,{"hidden":true,"children":["$","$L1b",null,{"children":["$","$16",null,{"name":"Next.Metadata","children":"$L1c"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +f:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +10:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +13:{} +14:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1d:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +17:null +1c:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1d","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/api-keys/__next._head.txt b/litellm/proxy/_experimental/out/chat/api-keys/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/api-keys/__next._index.txt b/litellm/proxy/_experimental/out/chat/api-keys/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/api-keys/__next._tree.txt b/litellm/proxy/_experimental/out/chat/api-keys/__next._tree.txt new file mode 100644 index 00000000000..7ea6ad56b84 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"chat","param":null,"prefetchHints":0,"slots":{"children":{"name":"api-keys","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.api-keys.__PAGE__.txt b/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.api-keys.__PAGE__.txt new file mode 100644 index 00000000000..d71f71c58b3 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.api-keys.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[516448,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/16c4tr94o_76g.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/16c4tr94o_76g.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.api-keys.txt b/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.api-keys.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.api-keys.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.txt b/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.txt new file mode 100644 index 00000000000..1f9903dfc3e --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/__next.chat.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/chat/api-keys/index.html b/litellm/proxy/_experimental/out/chat/api-keys/index.html new file mode 100644 index 00000000000..f816e41f4e4 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/chat/api-keys/index.txt b/litellm/proxy/_experimental/out/chat/api-keys/index.txt new file mode 100644 index 00000000000..1bdf4ea15ae --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/api-keys/index.txt @@ -0,0 +1,33 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +e:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat","api-keys",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["api-keys",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{"children":["$Lb",{},null,false,null]},null,false,"$@c"]},null,false,null]},null,false,null],"$Ld",false]],"m":"$undefined","G":["$e",["$Lf","$L10"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +11:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +12:I[516448,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/16c4tr94o_76g.js"],"default"] +15:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +16:"$Sreact.suspense" +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +b:["$","$1","c",{"children":[["$","$L11",null,{"Component":"$12","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@13","$@14"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/16c4tr94o_76g.js","async":true,"nonce":"$undefined"}]],["$","$L15",null,{"children":["$","$16",null,{"name":"Next.MetadataOutlet","children":"$@17"}]}]]}] +18:[] +c:"$W18" +d:["$","$1","h",{"children":[null,["$","$L19",null,{"children":"$L1a"}],["$","div",null,{"hidden":true,"children":["$","$L1b",null,{"children":["$","$16",null,{"name":"Next.Metadata","children":"$L1c"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +f:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +10:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +13:{} +14:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1d:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +17:null +1c:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1d","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/credentials/__next._full.txt b/litellm/proxy/_experimental/out/chat/credentials/__next._full.txt new file mode 100644 index 00000000000..de729ac8ae3 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/__next._full.txt @@ -0,0 +1,33 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +e:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat","credentials",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["credentials",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{"children":["$Lb",{},null,false,null]},null,false,"$@c"]},null,false,null]},null,false,null],"$Ld",false]],"m":"$undefined","G":["$e",["$Lf","$L10"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +11:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +12:I[628851,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/069vv6t-agy4i.js"],"default"] +15:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +16:"$Sreact.suspense" +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +b:["$","$1","c",{"children":[["$","$L11",null,{"Component":"$12","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@13","$@14"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/069vv6t-agy4i.js","async":true,"nonce":"$undefined"}]],["$","$L15",null,{"children":["$","$16",null,{"name":"Next.MetadataOutlet","children":"$@17"}]}]]}] +18:[] +c:"$W18" +d:["$","$1","h",{"children":[null,["$","$L19",null,{"children":"$L1a"}],["$","div",null,{"hidden":true,"children":["$","$L1b",null,{"children":["$","$16",null,{"name":"Next.Metadata","children":"$L1c"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +f:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +10:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +13:{} +14:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1d:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +17:null +1c:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1d","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/credentials/__next._head.txt b/litellm/proxy/_experimental/out/chat/credentials/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/credentials/__next._index.txt b/litellm/proxy/_experimental/out/chat/credentials/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/credentials/__next._tree.txt b/litellm/proxy/_experimental/out/chat/credentials/__next._tree.txt new file mode 100644 index 00000000000..8cdbaedcf25 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"chat","param":null,"prefetchHints":0,"slots":{"children":{"name":"credentials","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/credentials/__next.chat.credentials.__PAGE__.txt b/litellm/proxy/_experimental/out/chat/credentials/__next.chat.credentials.__PAGE__.txt new file mode 100644 index 00000000000..31d34578270 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/__next.chat.credentials.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[628851,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/069vv6t-agy4i.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/069vv6t-agy4i.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/chat/credentials/__next.chat.credentials.txt b/litellm/proxy/_experimental/out/chat/credentials/__next.chat.credentials.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/__next.chat.credentials.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/credentials/__next.chat.txt b/litellm/proxy/_experimental/out/chat/credentials/__next.chat.txt new file mode 100644 index 00000000000..1f9903dfc3e --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/__next.chat.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/chat/credentials/index.html b/litellm/proxy/_experimental/out/chat/credentials/index.html new file mode 100644 index 00000000000..8d497de6be8 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/chat/credentials/index.txt b/litellm/proxy/_experimental/out/chat/credentials/index.txt new file mode 100644 index 00000000000..de729ac8ae3 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/credentials/index.txt @@ -0,0 +1,33 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +e:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat","credentials",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["credentials",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{"children":["$Lb",{},null,false,null]},null,false,"$@c"]},null,false,null]},null,false,null],"$Ld",false]],"m":"$undefined","G":["$e",["$Lf","$L10"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +11:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +12:I[628851,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/069vv6t-agy4i.js"],"default"] +15:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +16:"$Sreact.suspense" +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +b:["$","$1","c",{"children":[["$","$L11",null,{"Component":"$12","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@13","$@14"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/069vv6t-agy4i.js","async":true,"nonce":"$undefined"}]],["$","$L15",null,{"children":["$","$16",null,{"name":"Next.MetadataOutlet","children":"$@17"}]}]]}] +18:[] +c:"$W18" +d:["$","$1","h",{"children":[null,["$","$L19",null,{"children":"$L1a"}],["$","div",null,{"hidden":true,"children":["$","$L1b",null,{"children":["$","$16",null,{"name":"Next.Metadata","children":"$L1c"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +f:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +10:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +13:{} +14:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1d:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +17:null +1c:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1d","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/index.html b/litellm/proxy/_experimental/out/chat/index.html new file mode 100644 index 00000000000..d08ec18640f --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/chat/index.txt b/litellm/proxy/_experimental/out/chat/index.txt new file mode 100644 index 00000000000..d698a71559e --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/index.txt @@ -0,0 +1,30 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +c:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{},null,false,null]},null,false,null]},null,false,null],"$Lb",false]],"m":"$undefined","G":["$c",["$Ld","$Le"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +f:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +10:I[321443,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/0ggd6bmz46d--.js","/litellm-asset-prefix/_next/static/chunks/06_vzvq0hkdw-.js","/litellm-asset-prefix/_next/static/chunks/00tczcrtv5upo.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js"],"default"] +13:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +14:"$Sreact.suspense" +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +18:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[["$","$Lf",null,{"Component":"$10","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@11","$@12"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0ggd6bmz46d--.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/06_vzvq0hkdw-.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/00tczcrtv5upo.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}]],["$","$L13",null,{"children":["$","$14",null,{"name":"Next.MetadataOutlet","children":"$@15"}]}]]}] +b:["$","$1","h",{"children":[null,["$","$L16",null,{"children":"$L17"}],["$","div",null,{"hidden":true,"children":["$","$L18",null,{"children":["$","$14",null,{"name":"Next.Metadata","children":"$L19"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +d:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +e:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +11:{} +12:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +17:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1a:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +15:null +19:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1a","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/integrations/__next._full.txt b/litellm/proxy/_experimental/out/chat/integrations/__next._full.txt new file mode 100644 index 00000000000..04d8fbfcd1d --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/__next._full.txt @@ -0,0 +1,33 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +e:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat","integrations",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["integrations",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{"children":["$Lb",{},null,false,null]},null,false,"$@c"]},null,false,null]},null,false,null],"$Ld",false]],"m":"$undefined","G":["$e",["$Lf","$L10"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +11:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +12:I[248536,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/0vr7vyqn3e7s0.js","/litellm-asset-prefix/_next/static/chunks/0_6ht24.5ej1i.js"],"default"] +15:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +16:"$Sreact.suspense" +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +b:["$","$1","c",{"children":[["$","$L11",null,{"Component":"$12","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@13","$@14"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vr7vyqn3e7s0.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0_6ht24.5ej1i.js","async":true,"nonce":"$undefined"}]],["$","$L15",null,{"children":["$","$16",null,{"name":"Next.MetadataOutlet","children":"$@17"}]}]]}] +18:[] +c:"$W18" +d:["$","$1","h",{"children":[null,["$","$L19",null,{"children":"$L1a"}],["$","div",null,{"hidden":true,"children":["$","$L1b",null,{"children":["$","$16",null,{"name":"Next.Metadata","children":"$L1c"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +f:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +10:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +13:{} +14:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1d:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +17:null +1c:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1d","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/integrations/__next._head.txt b/litellm/proxy/_experimental/out/chat/integrations/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/integrations/__next._index.txt b/litellm/proxy/_experimental/out/chat/integrations/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/integrations/__next._tree.txt b/litellm/proxy/_experimental/out/chat/integrations/__next._tree.txt new file mode 100644 index 00000000000..a980755e369 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"chat","param":null,"prefetchHints":0,"slots":{"children":{"name":"integrations","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/integrations/__next.chat.integrations.__PAGE__.txt b/litellm/proxy/_experimental/out/chat/integrations/__next.chat.integrations.__PAGE__.txt new file mode 100644 index 00000000000..ca706c45228 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/__next.chat.integrations.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[248536,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/0vr7vyqn3e7s0.js","/litellm-asset-prefix/_next/static/chunks/0_6ht24.5ej1i.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vr7vyqn3e7s0.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0_6ht24.5ej1i.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/chat/integrations/__next.chat.integrations.txt b/litellm/proxy/_experimental/out/chat/integrations/__next.chat.integrations.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/__next.chat.integrations.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/integrations/__next.chat.txt b/litellm/proxy/_experimental/out/chat/integrations/__next.chat.txt new file mode 100644 index 00000000000..1f9903dfc3e --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/__next.chat.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/chat/integrations/index.html b/litellm/proxy/_experimental/out/chat/integrations/index.html new file mode 100644 index 00000000000..bc0e8928fcc --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/chat/integrations/index.txt b/litellm/proxy/_experimental/out/chat/integrations/index.txt new file mode 100644 index 00000000000..04d8fbfcd1d --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/integrations/index.txt @@ -0,0 +1,33 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +e:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat","integrations",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["integrations",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{"children":["$Lb",{},null,false,null]},null,false,"$@c"]},null,false,null]},null,false,null],"$Ld",false]],"m":"$undefined","G":["$e",["$Lf","$L10"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +11:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +12:I[248536,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/0vr7vyqn3e7s0.js","/litellm-asset-prefix/_next/static/chunks/0_6ht24.5ej1i.js"],"default"] +15:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +16:"$Sreact.suspense" +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +b:["$","$1","c",{"children":[["$","$L11",null,{"Component":"$12","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@13","$@14"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vr7vyqn3e7s0.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0_6ht24.5ej1i.js","async":true,"nonce":"$undefined"}]],["$","$L15",null,{"children":["$","$16",null,{"name":"Next.MetadataOutlet","children":"$@17"}]}]]}] +18:[] +c:"$W18" +d:["$","$1","h",{"children":[null,["$","$L19",null,{"children":"$L1a"}],["$","div",null,{"hidden":true,"children":["$","$L1b",null,{"children":["$","$16",null,{"name":"Next.Metadata","children":"$L1c"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +f:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +10:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +13:{} +14:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1d:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +17:null +1c:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1d","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/usage/__next._full.txt b/litellm/proxy/_experimental/out/chat/usage/__next._full.txt new file mode 100644 index 00000000000..8e457766cd2 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/__next._full.txt @@ -0,0 +1,33 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +e:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat","usage",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["usage",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{"children":["$Lb",{},null,false,null]},null,false,"$@c"]},null,false,null]},null,false,null],"$Ld",false]],"m":"$undefined","G":["$e",["$Lf","$L10"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +11:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +12:I[35440,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/14l3wd4cyws22.js"],"default"] +15:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +16:"$Sreact.suspense" +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +b:["$","$1","c",{"children":[["$","$L11",null,{"Component":"$12","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@13","$@14"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/14l3wd4cyws22.js","async":true,"nonce":"$undefined"}]],["$","$L15",null,{"children":["$","$16",null,{"name":"Next.MetadataOutlet","children":"$@17"}]}]]}] +18:[] +c:"$W18" +d:["$","$1","h",{"children":[null,["$","$L19",null,{"children":"$L1a"}],["$","div",null,{"hidden":true,"children":["$","$L1b",null,{"children":["$","$16",null,{"name":"Next.Metadata","children":"$L1c"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +f:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +10:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +13:{} +14:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1d:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +17:null +1c:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1d","4",{}]] diff --git a/litellm/proxy/_experimental/out/chat/usage/__next._head.txt b/litellm/proxy/_experimental/out/chat/usage/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/usage/__next._index.txt b/litellm/proxy/_experimental/out/chat/usage/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/usage/__next._tree.txt b/litellm/proxy/_experimental/out/chat/usage/__next._tree.txt new file mode 100644 index 00000000000..e0f783531b3 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"chat","param":null,"prefetchHints":0,"slots":{"children":{"name":"usage","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/usage/__next.chat.txt b/litellm/proxy/_experimental/out/chat/usage/__next.chat.txt new file mode 100644 index 00000000000..1f9903dfc3e --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/__next.chat.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/chat/usage/__next.chat.usage.__PAGE__.txt b/litellm/proxy/_experimental/out/chat/usage/__next.chat.usage.__PAGE__.txt new file mode 100644 index 00000000000..885ff132cb2 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/__next.chat.usage.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[35440,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/14l3wd4cyws22.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/14l3wd4cyws22.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/chat/usage/__next.chat.usage.txt b/litellm/proxy/_experimental/out/chat/usage/__next.chat.usage.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/__next.chat.usage.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/chat/usage/index.html b/litellm/proxy/_experimental/out/chat/usage/index.html new file mode 100644 index 00000000000..7dfb5d24b9c --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/chat/usage/index.txt b/litellm/proxy/_experimental/out/chat/usage/index.txt new file mode 100644 index 00000000000..8e457766cd2 --- /dev/null +++ b/litellm/proxy/_experimental/out/chat/usage/index.txt @@ -0,0 +1,33 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[444069,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js"],"default"] +e:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","chat","usage",""],"q":"","i":false,"f":[[["",{"children":["chat",{"children":["usage",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@9"]}}]]}],{"children":["$La",{"children":["$Lb",{},null,false,null]},null,false,"$@c"]},null,false,null]},null,false,null],"$Ld",false]],"m":"$undefined","G":["$e",["$Lf","$L10"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +11:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +12:I[35440,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0hpyic_._9giq.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/17~sdyib4xxst.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0s6wj75..ba9e.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/15jvuw910z3b2.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/0e9bsl~yo20nh.js","/litellm-asset-prefix/_next/static/chunks/0a6.utjw97odb.js","/litellm-asset-prefix/_next/static/chunks/0g00nxafc38-t.js","/litellm-asset-prefix/_next/static/chunks/14l3wd4cyws22.js"],"default"] +15:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +16:"$Sreact.suspense" +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +a:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +b:["$","$1","c",{"children":[["$","$L11",null,{"Component":"$12","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@13","$@14"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/14l3wd4cyws22.js","async":true,"nonce":"$undefined"}]],["$","$L15",null,{"children":["$","$16",null,{"name":"Next.MetadataOutlet","children":"$@17"}]}]]}] +18:[] +c:"$W18" +d:["$","$1","h",{"children":[null,["$","$L19",null,{"children":"$L1a"}],["$","div",null,{"hidden":true,"children":["$","$L1b",null,{"children":["$","$16",null,{"name":"Next.Metadata","children":"$L1c"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +f:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +10:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +13:{} +14:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1d:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +17:null +1c:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1d","4",{}]] diff --git a/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.cost-tracking.__PAGE__.txt b/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.cost-tracking.__PAGE__.txt new file mode 100644 index 00000000000..7aea911787c --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.cost-tracking.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[193317,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/05z02g9s~8km0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0b5ys20if-ovu.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/060kl3yana4g8.js","/litellm-asset-prefix/_next/static/chunks/0zlzm14kabqg_.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/05z02g9s~8km0.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0b5ys20if-ovu.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/060kl3yana4g8.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0zlzm14kabqg_.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.cost-tracking.txt b/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.cost-tracking.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.cost-tracking.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/cost-tracking/__next._full.txt b/litellm/proxy/_experimental/out/cost-tracking/__next._full.txt new file mode 100644 index 00000000000..e4da779e782 --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","cost-tracking",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["cost-tracking",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[193317,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/05z02g9s~8km0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0b5ys20if-ovu.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/060kl3yana4g8.js","/litellm-asset-prefix/_next/static/chunks/0zlzm14kabqg_.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/05z02g9s~8km0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0b5ys20if-ovu.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/060kl3yana4g8.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0zlzm14kabqg_.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/cost-tracking/__next._head.txt b/litellm/proxy/_experimental/out/cost-tracking/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/cost-tracking/__next._index.txt b/litellm/proxy/_experimental/out/cost-tracking/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/cost-tracking/__next._tree.txt b/litellm/proxy/_experimental/out/cost-tracking/__next._tree.txt new file mode 100644 index 00000000000..d24f0773511 --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"cost-tracking","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/cost-tracking/index.html b/litellm/proxy/_experimental/out/cost-tracking/index.html new file mode 100644 index 00000000000..6e8baa170b2 --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/cost-tracking/index.txt b/litellm/proxy/_experimental/out/cost-tracking/index.txt new file mode 100644 index 00000000000..e4da779e782 --- /dev/null +++ b/litellm/proxy/_experimental/out/cost-tracking/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","cost-tracking",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["cost-tracking",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[193317,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/05z02g9s~8km0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0b5ys20if-ovu.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/060kl3yana4g8.js","/litellm-asset-prefix/_next/static/chunks/0zlzm14kabqg_.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/05z02g9s~8km0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0b5ys20if-ovu.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/060kl3yana4g8.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0zlzm14kabqg_.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/favicon.ico b/litellm/proxy/_experimental/out/favicon.ico new file mode 100644 index 00000000000..7c45601d5c3 Binary files /dev/null and b/litellm/proxy/_experimental/out/favicon.ico differ diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.guardrails-monitor.__PAGE__.txt b/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.guardrails-monitor.__PAGE__.txt new file mode 100644 index 00000000000..aef20f2c201 --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.guardrails-monitor.__PAGE__.txt @@ -0,0 +1,10 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[55004,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/1456z~hc~xuel.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/1456z~hc~xuel.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.guardrails-monitor.txt b/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.guardrails-monitor.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.guardrails-monitor.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/__next._full.txt b/litellm/proxy/_experimental/out/guardrails-monitor/__next._full.txt new file mode 100644 index 00000000000..0494835bc7d --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/__next._full.txt @@ -0,0 +1,35 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"P":null,"c":["","guardrails-monitor",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["guardrails-monitor",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[55004,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/1456z~hc~xuel.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/1456z~hc~xuel.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/__next._head.txt b/litellm/proxy/_experimental/out/guardrails-monitor/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/__next._index.txt b/litellm/proxy/_experimental/out/guardrails-monitor/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/__next._tree.txt b/litellm/proxy/_experimental/out/guardrails-monitor/__next._tree.txt new file mode 100644 index 00000000000..e80df6aee68 --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/__next._tree.txt @@ -0,0 +1,5 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"guardrails-monitor","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/index.html b/litellm/proxy/_experimental/out/guardrails-monitor/index.html new file mode 100644 index 00000000000..58f46bc2cc0 --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/guardrails-monitor/index.txt b/litellm/proxy/_experimental/out/guardrails-monitor/index.txt new file mode 100644 index 00000000000..0494835bc7d --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails-monitor/index.txt @@ -0,0 +1,35 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"P":null,"c":["","guardrails-monitor",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["guardrails-monitor",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[55004,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/1456z~hc~xuel.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/1456z~hc~xuel.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.guardrails.__PAGE__.txt b/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.guardrails.__PAGE__.txt new file mode 100644 index 00000000000..213a556db51 --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.guardrails.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[509345,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0rle8dv-1hl2i.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15.qmi9pavyv_.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/08rmtqzoefj-i.js","/litellm-asset-prefix/_next/static/chunks/01dk-b-_masm~.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0lea3j.fjm625.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0rle8dv-1hl2i.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15.qmi9pavyv_.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/08rmtqzoefj-i.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/01dk-b-_masm~.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0lea3j.fjm625.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.guardrails.txt b/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.guardrails.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.guardrails.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/guardrails/__next._full.txt b/litellm/proxy/_experimental/out/guardrails/__next._full.txt new file mode 100644 index 00000000000..d74941e731c --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","guardrails",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["guardrails",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[509345,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0rle8dv-1hl2i.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15.qmi9pavyv_.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/08rmtqzoefj-i.js","/litellm-asset-prefix/_next/static/chunks/01dk-b-_masm~.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0lea3j.fjm625.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0rle8dv-1hl2i.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15.qmi9pavyv_.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/08rmtqzoefj-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/01dk-b-_masm~.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0lea3j.fjm625.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/guardrails/__next._head.txt b/litellm/proxy/_experimental/out/guardrails/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/guardrails/__next._index.txt b/litellm/proxy/_experimental/out/guardrails/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/guardrails/__next._tree.txt b/litellm/proxy/_experimental/out/guardrails/__next._tree.txt new file mode 100644 index 00000000000..4673152e76c --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"guardrails","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/guardrails/index.html b/litellm/proxy/_experimental/out/guardrails/index.html new file mode 100644 index 00000000000..e05c658c021 --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/guardrails/index.txt b/litellm/proxy/_experimental/out/guardrails/index.txt new file mode 100644 index 00000000000..d74941e731c --- /dev/null +++ b/litellm/proxy/_experimental/out/guardrails/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","guardrails",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["guardrails",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[509345,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0rle8dv-1hl2i.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15.qmi9pavyv_.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/08rmtqzoefj-i.js","/litellm-asset-prefix/_next/static/chunks/01dk-b-_masm~.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0lea3j.fjm625.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0rle8dv-1hl2i.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15.qmi9pavyv_.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/08rmtqzoefj-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/01dk-b-_masm~.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0lea3j.fjm625.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/index.html b/litellm/proxy/_experimental/out/index.html new file mode 100644 index 00000000000..022319a4f2e --- /dev/null +++ b/litellm/proxy/_experimental/out/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/index.txt b/litellm/proxy/_experimental/out/index.txt new file mode 100644 index 00000000000..8aebbcdc258 --- /dev/null +++ b/litellm/proxy/_experimental/out/index.txt @@ -0,0 +1,31 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +d:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{},null,false,null]},null,false,null]},null,false,null],"$Lc",false]],"m":"$undefined","G":["$d",["$Le","$Lf"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +10:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +11:I[871135,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js"],"default"] +14:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +15:"$Sreact.suspense" +17:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +19:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[["$","$L10",null,{"Component":"$11","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@12","$@13"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/122djf0bncn-8.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/14g~hmf3h_efw.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0rehsq9xe1kde.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0nk7-_~gcxbz0.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true,"nonce":"$undefined"}]],["$","$L14",null,{"children":["$","$15",null,{"name":"Next.MetadataOutlet","children":"$@16"}]}]]}] +c:["$","$1","h",{"children":[null,["$","$L17",null,{"children":"$L18"}],["$","div",null,{"hidden":true,"children":["$","$L19",null,{"children":["$","$15",null,{"name":"Next.Metadata","children":"$L1a"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +e:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +f:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +12:{} +13:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +18:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1b:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +16:null +1a:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1b","4",{}]] diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.logging-and-alerts.__PAGE__.txt b/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.logging-and-alerts.__PAGE__.txt new file mode 100644 index 00000000000..142722eb1b0 --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.logging-and-alerts.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[372024,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0pvvj8a2cte7e.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/035e9knuui_xh.js","/litellm-asset-prefix/_next/static/chunks/00-xblkiz3o8~.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0em0654rb513m.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/08lkxewxqko83.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0pvvj8a2cte7e.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/035e9knuui_xh.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/00-xblkiz3o8~.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0em0654rb513m.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/08lkxewxqko83.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.logging-and-alerts.txt b/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.logging-and-alerts.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.logging-and-alerts.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/__next._full.txt b/litellm/proxy/_experimental/out/logging-and-alerts/__next._full.txt new file mode 100644 index 00000000000..0de8e026e99 --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","logging-and-alerts",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["logging-and-alerts",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[372024,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0pvvj8a2cte7e.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/035e9knuui_xh.js","/litellm-asset-prefix/_next/static/chunks/00-xblkiz3o8~.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0em0654rb513m.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/08lkxewxqko83.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0pvvj8a2cte7e.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/035e9knuui_xh.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/00-xblkiz3o8~.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0em0654rb513m.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/08lkxewxqko83.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/__next._head.txt b/litellm/proxy/_experimental/out/logging-and-alerts/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/__next._index.txt b/litellm/proxy/_experimental/out/logging-and-alerts/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/__next._tree.txt b/litellm/proxy/_experimental/out/logging-and-alerts/__next._tree.txt new file mode 100644 index 00000000000..4d68484427e --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"logging-and-alerts","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/index.html b/litellm/proxy/_experimental/out/logging-and-alerts/index.html new file mode 100644 index 00000000000..f816396c27a --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/logging-and-alerts/index.txt b/litellm/proxy/_experimental/out/logging-and-alerts/index.txt new file mode 100644 index 00000000000..0de8e026e99 --- /dev/null +++ b/litellm/proxy/_experimental/out/logging-and-alerts/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","logging-and-alerts",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["logging-and-alerts",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[372024,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0pvvj8a2cte7e.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/035e9knuui_xh.js","/litellm-asset-prefix/_next/static/chunks/00-xblkiz3o8~.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0em0654rb513m.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/08lkxewxqko83.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0pvvj8a2cte7e.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/035e9knuui_xh.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/00-xblkiz3o8~.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0em0654rb513m.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/08lkxewxqko83.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/login/__next._full.txt b/litellm/proxy/_experimental/out/login/__next._full.txt new file mode 100644 index 00000000000..3e49317623c --- /dev/null +++ b/litellm/proxy/_experimental/out/login/__next._full.txt @@ -0,0 +1,25 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[594542,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0~yq6te8~3jfz.js","/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","/litellm-asset-prefix/_next/static/chunks/0dlc1_mls9g-0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js"],"default"] +b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +c:"$Sreact.suspense" +f:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +11:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +13:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","login",""],"q":"","i":false,"f":[[["",{"children":["login",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0~yq6te8~3jfz.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0dlc1_mls9g-0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true,"nonce":"$undefined"}]],["$","$Lb",null,{"children":["$","$c",null,{"name":"Next.MetadataOutlet","children":"$@d"}]}]]}],{},null,false,null]},null,false,"$@e"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lf",null,{"children":"$L10"}],["$","div",null,{"hidden":true,"children":["$","$L11",null,{"children":["$","$c",null,{"name":"Next.Metadata","children":"$L12"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$13",[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +14:[] +e:"$W14" +9:{} +a:"$0:f:0:1:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +10:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +15:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +d:null +12:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L15","4",{}]] diff --git a/litellm/proxy/_experimental/out/login/__next._head.txt b/litellm/proxy/_experimental/out/login/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/login/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/login/__next._index.txt b/litellm/proxy/_experimental/out/login/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/login/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/login/__next._tree.txt b/litellm/proxy/_experimental/out/login/__next._tree.txt new file mode 100644 index 00000000000..ecd5fd92d5c --- /dev/null +++ b/litellm/proxy/_experimental/out/login/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"login","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/login/__next.login.__PAGE__.txt b/litellm/proxy/_experimental/out/login/__next.login.__PAGE__.txt new file mode 100644 index 00000000000..2b76ee5c39a --- /dev/null +++ b/litellm/proxy/_experimental/out/login/__next.login.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[594542,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0~yq6te8~3jfz.js","/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","/litellm-asset-prefix/_next/static/chunks/0dlc1_mls9g-0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0~yq6te8~3jfz.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0dlc1_mls9g-0.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/login/__next.login.txt b/litellm/proxy/_experimental/out/login/__next.login.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/login/__next.login.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/login/index.html b/litellm/proxy/_experimental/out/login/index.html new file mode 100644 index 00000000000..08e9597d239 --- /dev/null +++ b/litellm/proxy/_experimental/out/login/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/login/index.txt b/litellm/proxy/_experimental/out/login/index.txt new file mode 100644 index 00000000000..3e49317623c --- /dev/null +++ b/litellm/proxy/_experimental/out/login/index.txt @@ -0,0 +1,25 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[594542,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0~yq6te8~3jfz.js","/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","/litellm-asset-prefix/_next/static/chunks/0dlc1_mls9g-0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js"],"default"] +b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +c:"$Sreact.suspense" +f:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +11:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +13:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","login",""],"q":"","i":false,"f":[[["",{"children":["login",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0~yq6te8~3jfz.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0dlc1_mls9g-0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true,"nonce":"$undefined"}]],["$","$Lb",null,{"children":["$","$c",null,{"name":"Next.MetadataOutlet","children":"$@d"}]}]]}],{},null,false,null]},null,false,"$@e"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lf",null,{"children":"$L10"}],["$","div",null,{"hidden":true,"children":["$","$L11",null,{"children":["$","$c",null,{"name":"Next.Metadata","children":"$L12"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$13",[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +14:[] +e:"$W14" +9:{} +a:"$0:f:0:1:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +10:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +15:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +d:null +12:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L15","4",{}]] diff --git a/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.logs.__PAGE__.txt b/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.logs.__PAGE__.txt new file mode 100644 index 00000000000..32ee0f75441 --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.logs.__PAGE__.txt @@ -0,0 +1,10 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[799062,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0p2cacg05iprd.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/14f4w-z4k4mtz.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","/litellm-asset-prefix/_next/static/chunks/0t50t_0rum~ur.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0jh7h3_26_oz9.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/10vzdencbb-2b.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0p2cacg05iprd.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/14f4w-z4k4mtz.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0t50t_0rum~ur.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0jh7h3_26_oz9.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/10vzdencbb-2b.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.logs.txt b/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.logs.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.logs.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/logs/__next._full.txt b/litellm/proxy/_experimental/out/logs/__next._full.txt new file mode 100644 index 00000000000..0e7a90a87c7 --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/__next._full.txt @@ -0,0 +1,35 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"P":null,"c":["","logs",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["logs",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[799062,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0p2cacg05iprd.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/14f4w-z4k4mtz.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","/litellm-asset-prefix/_next/static/chunks/0t50t_0rum~ur.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0jh7h3_26_oz9.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/10vzdencbb-2b.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0p2cacg05iprd.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/14f4w-z4k4mtz.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0t50t_0rum~ur.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0jh7h3_26_oz9.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/10vzdencbb-2b.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/logs/__next._head.txt b/litellm/proxy/_experimental/out/logs/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/logs/__next._index.txt b/litellm/proxy/_experimental/out/logs/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/logs/__next._tree.txt b/litellm/proxy/_experimental/out/logs/__next._tree.txt new file mode 100644 index 00000000000..38ee786bc0a --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/__next._tree.txt @@ -0,0 +1,5 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"logs","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/logs/index.html b/litellm/proxy/_experimental/out/logs/index.html new file mode 100644 index 00000000000..5ba1800a913 --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/logs/index.txt b/litellm/proxy/_experimental/out/logs/index.txt new file mode 100644 index 00000000000..0e7a90a87c7 --- /dev/null +++ b/litellm/proxy/_experimental/out/logs/index.txt @@ -0,0 +1,35 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"P":null,"c":["","logs",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["logs",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[799062,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0p2cacg05iprd.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/14f4w-z4k4mtz.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","/litellm-asset-prefix/_next/static/chunks/0t50t_0rum~ur.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0jh7h3_26_oz9.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/10vzdencbb-2b.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0p2cacg05iprd.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/14f4w-z4k4mtz.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0t50t_0rum~ur.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0jh7h3_26_oz9.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/10vzdencbb-2b.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.mcp-servers.__PAGE__.txt b/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.mcp-servers.__PAGE__.txt new file mode 100644 index 00000000000..f238db58c0e --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.mcp-servers.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[366321,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0tvwf-7q.gldz.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0eenr4v7sbd44.js","/litellm-asset-prefix/_next/static/chunks/0rvhrqi0s_~5q.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tvwf-7q.gldz.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0eenr4v7sbd44.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0rvhrqi0s_~5q.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.mcp-servers.txt b/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.mcp-servers.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.mcp-servers.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/mcp-servers/__next._full.txt b/litellm/proxy/_experimental/out/mcp-servers/__next._full.txt new file mode 100644 index 00000000000..eec02363ed5 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","mcp-servers",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["mcp-servers",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[366321,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0tvwf-7q.gldz.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0eenr4v7sbd44.js","/litellm-asset-prefix/_next/static/chunks/0rvhrqi0s_~5q.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tvwf-7q.gldz.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0eenr4v7sbd44.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0rvhrqi0s_~5q.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/mcp-servers/__next._head.txt b/litellm/proxy/_experimental/out/mcp-servers/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp-servers/__next._index.txt b/litellm/proxy/_experimental/out/mcp-servers/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp-servers/__next._tree.txt b/litellm/proxy/_experimental/out/mcp-servers/__next._tree.txt new file mode 100644 index 00000000000..a8bc660a510 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"mcp-servers","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp-servers/index.html b/litellm/proxy/_experimental/out/mcp-servers/index.html new file mode 100644 index 00000000000..89c77e2f173 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/mcp-servers/index.txt b/litellm/proxy/_experimental/out/mcp-servers/index.txt new file mode 100644 index 00000000000..eec02363ed5 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp-servers/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","mcp-servers",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["mcp-servers",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[366321,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0tvwf-7q.gldz.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0eenr4v7sbd44.js","/litellm-asset-prefix/_next/static/chunks/0rvhrqi0s_~5q.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15hm8gokjq2uu.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tvwf-7q.gldz.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0eenr4v7sbd44.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0rvhrqi0s_~5q.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._full.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._full.txt new file mode 100644 index 00000000000..925d532de44 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._full.txt @@ -0,0 +1,25 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[346328,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0b5g~_decuer~.js"],"default"] +b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +c:"$Sreact.suspense" +f:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +11:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +13:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","mcp","oauth","callback",""],"q":"","i":false,"f":[[["",{"children":["mcp",{"children":["oauth",{"children":["callback",{"children":["__PAGE__",{}]}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0b5g~_decuer~.js","async":true,"nonce":"$undefined"}]],["$","$Lb",null,{"children":["$","$c",null,{"name":"Next.MetadataOutlet","children":"$@d"}]}]]}],{},null,false,null]},null,false,"$@e"]},null,false,"$@e"]},null,false,"$@e"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lf",null,{"children":"$L10"}],["$","div",null,{"hidden":true,"children":["$","$L11",null,{"children":["$","$c",null,{"name":"Next.Metadata","children":"$L12"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$13",[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +14:[] +e:"$W14" +9:{} +a:"$0:f:0:1:1:children:1:children:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +10:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +15:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +d:null +12:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L15","4",{}]] diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._head.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._index.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._tree.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._tree.txt new file mode 100644 index 00000000000..19b67861696 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"mcp","param":null,"prefetchHints":0,"slots":{"children":{"name":"oauth","param":null,"prefetchHints":0,"slots":{"children":{"name":"callback","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.callback.__PAGE__.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.callback.__PAGE__.txt new file mode 100644 index 00000000000..cff669c8017 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.callback.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[346328,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0b5g~_decuer~.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0b5g~_decuer~.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.callback.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.callback.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.callback.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.oauth.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/__next.mcp.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/index.html b/litellm/proxy/_experimental/out/mcp/oauth/callback/index.html new file mode 100644 index 00000000000..3d6d5863d1a --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/mcp/oauth/callback/index.txt b/litellm/proxy/_experimental/out/mcp/oauth/callback/index.txt new file mode 100644 index 00000000000..925d532de44 --- /dev/null +++ b/litellm/proxy/_experimental/out/mcp/oauth/callback/index.txt @@ -0,0 +1,25 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[346328,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0b5g~_decuer~.js"],"default"] +b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +c:"$Sreact.suspense" +f:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +11:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +13:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","mcp","oauth","callback",""],"q":"","i":false,"f":[[["",{"children":["mcp",{"children":["oauth",{"children":["callback",{"children":["__PAGE__",{}]}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0b5g~_decuer~.js","async":true,"nonce":"$undefined"}]],["$","$Lb",null,{"children":["$","$c",null,{"name":"Next.MetadataOutlet","children":"$@d"}]}]]}],{},null,false,null]},null,false,"$@e"]},null,false,"$@e"]},null,false,"$@e"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lf",null,{"children":"$L10"}],["$","div",null,{"hidden":true,"children":["$","$L11",null,{"children":["$","$c",null,{"name":"Next.Metadata","children":"$L12"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$13",[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +14:[] +e:"$W14" +9:{} +a:"$0:f:0:1:1:children:1:children:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +10:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +15:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +d:null +12:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L15","4",{}]] diff --git a/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.memory.__PAGE__.txt b/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.memory.__PAGE__.txt new file mode 100644 index 00000000000..72e17709be2 --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.memory.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[956224,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/07k57gyd_7~yb.js","/litellm-asset-prefix/_next/static/chunks/08dlewb0bh-vz.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0noytyudtoxih.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/07k57gyd_7~yb.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/08dlewb0bh-vz.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0noytyudtoxih.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.memory.txt b/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.memory.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.memory.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/memory/__next._full.txt b/litellm/proxy/_experimental/out/memory/__next._full.txt new file mode 100644 index 00000000000..c5684975df1 --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","memory",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["memory",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[956224,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/07k57gyd_7~yb.js","/litellm-asset-prefix/_next/static/chunks/08dlewb0bh-vz.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0noytyudtoxih.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/07k57gyd_7~yb.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/08dlewb0bh-vz.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0noytyudtoxih.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/memory/__next._head.txt b/litellm/proxy/_experimental/out/memory/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/memory/__next._index.txt b/litellm/proxy/_experimental/out/memory/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/memory/__next._tree.txt b/litellm/proxy/_experimental/out/memory/__next._tree.txt new file mode 100644 index 00000000000..b855b5a8620 --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"memory","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/memory/index.html b/litellm/proxy/_experimental/out/memory/index.html new file mode 100644 index 00000000000..cf69254bc12 --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/memory/index.txt b/litellm/proxy/_experimental/out/memory/index.txt new file mode 100644 index 00000000000..c5684975df1 --- /dev/null +++ b/litellm/proxy/_experimental/out/memory/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","memory",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["memory",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[956224,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/07k57gyd_7~yb.js","/litellm-asset-prefix/_next/static/chunks/08dlewb0bh-vz.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0noytyudtoxih.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/07k57gyd_7~yb.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/08dlewb0bh-vz.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0noytyudtoxih.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.model-hub-table.__PAGE__.txt b/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.model-hub-table.__PAGE__.txt new file mode 100644 index 00000000000..b6946e2ca6e --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.model-hub-table.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[157058,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/07vi6evrqzvik.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0iztt_s1c7uqp.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0hi3v5j28eskv.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/07vi6evrqzvik.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0iztt_s1c7uqp.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0hi3v5j28eskv.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.model-hub-table.txt b/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.model-hub-table.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.model-hub-table.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/model-hub-table/__next._full.txt b/litellm/proxy/_experimental/out/model-hub-table/__next._full.txt new file mode 100644 index 00000000000..b0a1f3aa953 --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","model-hub-table",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["model-hub-table",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[157058,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/07vi6evrqzvik.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0iztt_s1c7uqp.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0hi3v5j28eskv.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/07vi6evrqzvik.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0iztt_s1c7uqp.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0hi3v5j28eskv.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/model-hub-table/__next._head.txt b/litellm/proxy/_experimental/out/model-hub-table/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model-hub-table/__next._index.txt b/litellm/proxy/_experimental/out/model-hub-table/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model-hub-table/__next._tree.txt b/litellm/proxy/_experimental/out/model-hub-table/__next._tree.txt new file mode 100644 index 00000000000..787e01503a8 --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"model-hub-table","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model-hub-table/index.html b/litellm/proxy/_experimental/out/model-hub-table/index.html new file mode 100644 index 00000000000..a9258c34df6 --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/model-hub-table/index.txt b/litellm/proxy/_experimental/out/model-hub-table/index.txt new file mode 100644 index 00000000000..b0a1f3aa953 --- /dev/null +++ b/litellm/proxy/_experimental/out/model-hub-table/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","model-hub-table",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["model-hub-table",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[157058,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/07vi6evrqzvik.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0iztt_s1c7uqp.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0hi3v5j28eskv.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/07vi6evrqzvik.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0iztt_s1c7uqp.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0hi3v5j28eskv.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/model_hub/__next._full.txt b/litellm/proxy/_experimental/out/model_hub/__next._full.txt new file mode 100644 index 00000000000..137110ddae4 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[560280,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/17y3_yqikcnb1.js","/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","/litellm-asset-prefix/_next/static/chunks/0zr5p_mss4q5v.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0u9~32cojjvj6.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0fch8lvubeqb-.js","/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0f~m6gi_k-res.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js"],"default"] +13:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","model_hub",""],"q":"","i":false,"f":[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17y3_yqikcnb1.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0zr5p_mss4q5v.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0u9~32cojjvj6.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0fch8lvubeqb-.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","async":true,"nonce":"$undefined"}],"$Lb","$Lc","$Ld","$Le","$Lf"],"$L10"]}],{},null,false,null]},null,false,"$@11"]},null,false,null],"$L12",false]],"m":"$undefined","G":["$13",["$L14","$L15"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +b:["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}] +c:["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0f~m6gi_k-res.js","async":true,"nonce":"$undefined"}] +d:["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}] +e:["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}] +f:["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}] +10:["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}] +19:[] +11:"$W19" +12:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +14:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +15:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:{} +a:"$0:f:0:1:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/model_hub/__next._head.txt b/litellm/proxy/_experimental/out/model_hub/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model_hub/__next._index.txt b/litellm/proxy/_experimental/out/model_hub/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model_hub/__next._tree.txt b/litellm/proxy/_experimental/out/model_hub/__next._tree.txt new file mode 100644 index 00000000000..8496ad61a80 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"model_hub","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model_hub/__next.model_hub.__PAGE__.txt b/litellm/proxy/_experimental/out/model_hub/__next.model_hub.__PAGE__.txt new file mode 100644 index 00000000000..882ada961cd --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub/__next.model_hub.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[560280,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/17y3_yqikcnb1.js","/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","/litellm-asset-prefix/_next/static/chunks/0zr5p_mss4q5v.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0u9~32cojjvj6.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0fch8lvubeqb-.js","/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0f~m6gi_k-res.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17y3_yqikcnb1.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0zr5p_mss4q5v.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0u9~32cojjvj6.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0fch8lvubeqb-.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0f~m6gi_k-res.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/model_hub/__next.model_hub.txt b/litellm/proxy/_experimental/out/model_hub/__next.model_hub.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub/__next.model_hub.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model_hub/index.html b/litellm/proxy/_experimental/out/model_hub/index.html new file mode 100644 index 00000000000..2d9ea9799e3 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/model_hub/index.txt b/litellm/proxy/_experimental/out/model_hub/index.txt new file mode 100644 index 00000000000..137110ddae4 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[560280,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/17y3_yqikcnb1.js","/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","/litellm-asset-prefix/_next/static/chunks/0zr5p_mss4q5v.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/0u9~32cojjvj6.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0fch8lvubeqb-.js","/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0f~m6gi_k-res.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js"],"default"] +13:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","model_hub",""],"q":"","i":false,"f":[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17y3_yqikcnb1.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0zr5p_mss4q5v.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0u9~32cojjvj6.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0fch8lvubeqb-.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","async":true,"nonce":"$undefined"}],"$Lb","$Lc","$Ld","$Le","$Lf"],"$L10"]}],{},null,false,null]},null,false,"$@11"]},null,false,null],"$L12",false]],"m":"$undefined","G":["$13",["$L14","$L15"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +b:["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}] +c:["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0f~m6gi_k-res.js","async":true,"nonce":"$undefined"}] +d:["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}] +e:["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}] +f:["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}] +10:["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}] +19:[] +11:"$W19" +12:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +14:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +15:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:{} +a:"$0:f:0:1:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/model_hub_table/__next._full.txt b/litellm/proxy/_experimental/out/model_hub_table/__next._full.txt new file mode 100644 index 00000000000..748ca5a7737 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub_table/__next._full.txt @@ -0,0 +1,38 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[86408,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0gw7v5z1-5x0y.js","/litellm-asset-prefix/_next/static/chunks/0l1wacob277d1.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0rcx~89hm.r_w.js","/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/02dxw4eubg_rq.js","/litellm-asset-prefix/_next/static/chunks/0ovrnw54dbivd.js"],"default"] +17:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","model_hub_table",""],"q":"","i":false,"f":[[["",{"children":["model_hub_table",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0gw7v5z1-5x0y.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0l1wacob277d1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],"$Lb","$Lc","$Ld","$Le","$Lf","$L10","$L11","$L12","$L13"],"$L14"]}],{},null,false,null]},null,false,"$@15"]},null,false,null],"$L16",false]],"m":"$undefined","G":["$17",["$L18","$L19"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +1b:"$Sreact.suspense" +1e:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +20:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +b:["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}] +c:["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0rcx~89hm.r_w.js","async":true,"nonce":"$undefined"}] +d:["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","async":true,"nonce":"$undefined"}] +e:["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","async":true,"nonce":"$undefined"}] +f:["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}] +10:["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}] +11:["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true,"nonce":"$undefined"}] +12:["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/02dxw4eubg_rq.js","async":true,"nonce":"$undefined"}] +13:["$","script","script-23",{"src":"/litellm-asset-prefix/_next/static/chunks/0ovrnw54dbivd.js","async":true,"nonce":"$undefined"}] +14:["$","$L1a",null,{"children":["$","$1b",null,{"name":"Next.MetadataOutlet","children":"$@1c"}]}] +1d:[] +15:"$W1d" +16:["$","$1","h",{"children":[null,["$","$L1e",null,{"children":"$L1f"}],["$","div",null,{"hidden":true,"children":["$","$L20",null,{"children":["$","$1b",null,{"name":"Next.Metadata","children":"$L21"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +18:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +19:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:{} +a:"$0:f:0:1:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +1f:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +22:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +1c:null +21:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L22","4",{}]] diff --git a/litellm/proxy/_experimental/out/model_hub_table/__next._head.txt b/litellm/proxy/_experimental/out/model_hub_table/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub_table/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model_hub_table/__next._index.txt b/litellm/proxy/_experimental/out/model_hub_table/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub_table/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model_hub_table/__next._tree.txt b/litellm/proxy/_experimental/out/model_hub_table/__next._tree.txt new file mode 100644 index 00000000000..b4c102c21de --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub_table/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"model_hub_table","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model_hub_table/__next.model_hub_table.__PAGE__.txt b/litellm/proxy/_experimental/out/model_hub_table/__next.model_hub_table.__PAGE__.txt new file mode 100644 index 00000000000..3904d1bfda7 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub_table/__next.model_hub_table.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[86408,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0gw7v5z1-5x0y.js","/litellm-asset-prefix/_next/static/chunks/0l1wacob277d1.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0rcx~89hm.r_w.js","/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/02dxw4eubg_rq.js","/litellm-asset-prefix/_next/static/chunks/0ovrnw54dbivd.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0gw7v5z1-5x0y.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0l1wacob277d1.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0rcx~89hm.r_w.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","async":true}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","async":true}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true}],["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/02dxw4eubg_rq.js","async":true}],["$","script","script-23",{"src":"/litellm-asset-prefix/_next/static/chunks/0ovrnw54dbivd.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/model_hub_table/__next.model_hub_table.txt b/litellm/proxy/_experimental/out/model_hub_table/__next.model_hub_table.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub_table/__next.model_hub_table.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/model_hub_table/index.html b/litellm/proxy/_experimental/out/model_hub_table/index.html new file mode 100644 index 00000000000..0f06abab1fe --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub_table/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/model_hub_table/index.txt b/litellm/proxy/_experimental/out/model_hub_table/index.txt new file mode 100644 index 00000000000..748ca5a7737 --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub_table/index.txt @@ -0,0 +1,38 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[86408,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0gw7v5z1-5x0y.js","/litellm-asset-prefix/_next/static/chunks/0l1wacob277d1.js","/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0rcx~89hm.r_w.js","/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/02dxw4eubg_rq.js","/litellm-asset-prefix/_next/static/chunks/0ovrnw54dbivd.js"],"default"] +17:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","model_hub_table",""],"q":"","i":false,"f":[[["",{"children":["model_hub_table",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0~y.5tdzi3t_z.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/01ut.srbq8~b9.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/01jbmgk~h02uq.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/15_tz5y4766-7.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0gw7v5z1-5x0y.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0l1wacob277d1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0n2w3jqk0bu61.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0bqnnjc1qf48g.js","async":true,"nonce":"$undefined"}],"$Lb","$Lc","$Ld","$Le","$Lf","$L10","$L11","$L12","$L13"],"$L14"]}],{},null,false,null]},null,false,"$@15"]},null,false,null],"$L16",false]],"m":"$undefined","G":["$17",["$L18","$L19"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +1b:"$Sreact.suspense" +1e:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +20:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +b:["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}] +c:["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0rcx~89hm.r_w.js","async":true,"nonce":"$undefined"}] +d:["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/13jobki5iqy.c.js","async":true,"nonce":"$undefined"}] +e:["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/1647r3v3s_66h.js","async":true,"nonce":"$undefined"}] +f:["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}] +10:["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}] +11:["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true,"nonce":"$undefined"}] +12:["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/02dxw4eubg_rq.js","async":true,"nonce":"$undefined"}] +13:["$","script","script-23",{"src":"/litellm-asset-prefix/_next/static/chunks/0ovrnw54dbivd.js","async":true,"nonce":"$undefined"}] +14:["$","$L1a",null,{"children":["$","$1b",null,{"name":"Next.MetadataOutlet","children":"$@1c"}]}] +1d:[] +15:"$W1d" +16:["$","$1","h",{"children":[null,["$","$L1e",null,{"children":"$L1f"}],["$","div",null,{"hidden":true,"children":["$","$L20",null,{"children":["$","$1b",null,{"name":"Next.Metadata","children":"$L21"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +18:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +19:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +9:{} +a:"$0:f:0:1:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +1f:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +22:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +1c:null +21:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L22","4",{}]] diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.models-and-endpoints.__PAGE__.txt b/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.models-and-endpoints.__PAGE__.txt new file mode 100644 index 00000000000..71bcf9b6935 --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.models-and-endpoints.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[664307,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/016u~n51r0h1k.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0giyrzfhu4lu5.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0zkzibztmigs0.js","/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","/litellm-asset-prefix/_next/static/chunks/0y5t2sslri-iq.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/022.sz94ycw4x.js","/litellm-asset-prefix/_next/static/chunks/0w98a8ubxago4.js","/litellm-asset-prefix/_next/static/chunks/11lf2owsm68y3.js","/litellm-asset-prefix/_next/static/chunks/027d2u2cl335o.js","/litellm-asset-prefix/_next/static/chunks/0elk4ibay4~zx.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/016u~n51r0h1k.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0giyrzfhu4lu5.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0zkzibztmigs0.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0y5t2sslri-iq.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/022.sz94ycw4x.js","async":true}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0w98a8ubxago4.js","async":true}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/11lf2owsm68y3.js","async":true}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/027d2u2cl335o.js","async":true}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0elk4ibay4~zx.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.models-and-endpoints.txt b/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.models-and-endpoints.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.models-and-endpoints.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/__next._full.txt b/litellm/proxy/_experimental/out/models-and-endpoints/__next._full.txt new file mode 100644 index 00000000000..3947edec08c --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","models-and-endpoints",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["models-and-endpoints",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$L9","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[664307,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/016u~n51r0h1k.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0giyrzfhu4lu5.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0zkzibztmigs0.js","/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","/litellm-asset-prefix/_next/static/chunks/0y5t2sslri-iq.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/022.sz94ycw4x.js","/litellm-asset-prefix/_next/static/chunks/0w98a8ubxago4.js","/litellm-asset-prefix/_next/static/chunks/11lf2owsm68y3.js","/litellm-asset-prefix/_next/static/chunks/027d2u2cl335o.js","/litellm-asset-prefix/_next/static/chunks/0elk4ibay4~zx.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],[]] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/016u~n51r0h1k.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0giyrzfhu4lu5.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0zkzibztmigs0.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0y5t2sslri-iq.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/022.sz94ycw4x.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0w98a8ubxago4.js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/11lf2owsm68y3.js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/027d2u2cl335o.js","async":true,"nonce":"$undefined"}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0elk4ibay4~zx.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/__next._head.txt b/litellm/proxy/_experimental/out/models-and-endpoints/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/__next._index.txt b/litellm/proxy/_experimental/out/models-and-endpoints/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/__next._tree.txt b/litellm/proxy/_experimental/out/models-and-endpoints/__next._tree.txt new file mode 100644 index 00000000000..fef93be8b9d --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"models-and-endpoints","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/index.html b/litellm/proxy/_experimental/out/models-and-endpoints/index.html new file mode 100644 index 00000000000..77a77c3db65 --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/models-and-endpoints/index.txt b/litellm/proxy/_experimental/out/models-and-endpoints/index.txt new file mode 100644 index 00000000000..3947edec08c --- /dev/null +++ b/litellm/proxy/_experimental/out/models-and-endpoints/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","models-and-endpoints",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["models-and-endpoints",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$L9","forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[664307,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/016u~n51r0h1k.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0giyrzfhu4lu5.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0zkzibztmigs0.js","/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","/litellm-asset-prefix/_next/static/chunks/0y5t2sslri-iq.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/022.sz94ycw4x.js","/litellm-asset-prefix/_next/static/chunks/0w98a8ubxago4.js","/litellm-asset-prefix/_next/static/chunks/11lf2owsm68y3.js","/litellm-asset-prefix/_next/static/chunks/027d2u2cl335o.js","/litellm-asset-prefix/_next/static/chunks/0elk4ibay4~zx.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],[]] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/016u~n51r0h1k.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0giyrzfhu4lu5.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0zkzibztmigs0.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0y5t2sslri-iq.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/022.sz94ycw4x.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0w98a8ubxago4.js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/11lf2owsm68y3.js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/027d2u2cl335o.js","async":true,"nonce":"$undefined"}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0elk4ibay4~zx.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/next.svg b/litellm/proxy/_experimental/out/next.svg new file mode 100644 index 00000000000..5174b28c565 --- /dev/null +++ b/litellm/proxy/_experimental/out/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.old-usage.__PAGE__.txt b/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.old-usage.__PAGE__.txt new file mode 100644 index 00000000000..681337985b4 --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.old-usage.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[183051,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15a9nl3e4nrsf.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0x0jl05-mloxm.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/01y._o853f7le.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","/litellm-asset-prefix/_next/static/chunks/0u0zny6.djks2.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","/litellm-asset-prefix/_next/static/chunks/167o-sada1242.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/15a9nl3e4nrsf.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0x0jl05-mloxm.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/01y._o853f7le.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0u0zny6.djks2.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","async":true}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","async":true}],["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/167o-sada1242.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.old-usage.txt b/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.old-usage.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.old-usage.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/old-usage/__next._full.txt b/litellm/proxy/_experimental/out/old-usage/__next._full.txt new file mode 100644 index 00000000000..d8904d3cb8f --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","old-usage",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["old-usage",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[183051,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15a9nl3e4nrsf.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0x0jl05-mloxm.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/01y._o853f7le.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","/litellm-asset-prefix/_next/static/chunks/0u0zny6.djks2.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","/litellm-asset-prefix/_next/static/chunks/167o-sada1242.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/15a9nl3e4nrsf.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0x0jl05-mloxm.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/01y._o853f7le.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0u0zny6.djks2.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","async":true,"nonce":"$undefined"}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","async":true,"nonce":"$undefined"}],["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/167o-sada1242.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/old-usage/__next._head.txt b/litellm/proxy/_experimental/out/old-usage/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/old-usage/__next._index.txt b/litellm/proxy/_experimental/out/old-usage/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/old-usage/__next._tree.txt b/litellm/proxy/_experimental/out/old-usage/__next._tree.txt new file mode 100644 index 00000000000..b685437d627 --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"old-usage","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/old-usage/index.html b/litellm/proxy/_experimental/out/old-usage/index.html new file mode 100644 index 00000000000..b9219c97964 --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/old-usage/index.txt b/litellm/proxy/_experimental/out/old-usage/index.txt new file mode 100644 index 00000000000..d8904d3cb8f --- /dev/null +++ b/litellm/proxy/_experimental/out/old-usage/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","old-usage",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["old-usage",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[183051,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/15a9nl3e4nrsf.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0x0jl05-mloxm.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/01y._o853f7le.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","/litellm-asset-prefix/_next/static/chunks/0u0zny6.djks2.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","/litellm-asset-prefix/_next/static/chunks/167o-sada1242.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/15a9nl3e4nrsf.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0x0jl05-mloxm.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/01y._o853f7le.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0u0zny6.djks2.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","async":true,"nonce":"$undefined"}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","async":true,"nonce":"$undefined"}],["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/167o-sada1242.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/onboarding/__next._full.txt b/litellm/proxy/_experimental/out/onboarding/__next._full.txt new file mode 100644 index 00000000000..bd92f123630 --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding/__next._full.txt @@ -0,0 +1,25 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[566606,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0skjxv866-8kr.js","/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/0csst_9x.d5wb.js"],"default"] +b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +c:"$Sreact.suspense" +f:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +11:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +13:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","onboarding",""],"q":"","i":false,"f":[[["",{"children":["onboarding",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0skjxv866-8kr.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0csst_9x.d5wb.js","async":true,"nonce":"$undefined"}]],["$","$Lb",null,{"children":["$","$c",null,{"name":"Next.MetadataOutlet","children":"$@d"}]}]]}],{},null,false,null]},null,false,"$@e"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lf",null,{"children":"$L10"}],["$","div",null,{"hidden":true,"children":["$","$L11",null,{"children":["$","$c",null,{"name":"Next.Metadata","children":"$L12"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$13",[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +14:[] +e:"$W14" +9:{} +a:"$0:f:0:1:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +10:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +15:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +d:null +12:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L15","4",{}]] diff --git a/litellm/proxy/_experimental/out/onboarding/__next._head.txt b/litellm/proxy/_experimental/out/onboarding/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/onboarding/__next._index.txt b/litellm/proxy/_experimental/out/onboarding/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/onboarding/__next._tree.txt b/litellm/proxy/_experimental/out/onboarding/__next._tree.txt new file mode 100644 index 00000000000..8fc6b27dd42 --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"onboarding","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/onboarding/__next.onboarding.__PAGE__.txt b/litellm/proxy/_experimental/out/onboarding/__next.onboarding.__PAGE__.txt new file mode 100644 index 00000000000..a78f06cd3f1 --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding/__next.onboarding.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[566606,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0skjxv866-8kr.js","/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/0csst_9x.d5wb.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0skjxv866-8kr.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0csst_9x.d5wb.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/onboarding/__next.onboarding.txt b/litellm/proxy/_experimental/out/onboarding/__next.onboarding.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding/__next.onboarding.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/onboarding/index.html b/litellm/proxy/_experimental/out/onboarding/index.html new file mode 100644 index 00000000000..8a377e37f45 --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/onboarding/index.txt b/litellm/proxy/_experimental/out/onboarding/index.txt new file mode 100644 index 00000000000..bd92f123630 --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding/index.txt @@ -0,0 +1,25 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +8:I[566606,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0skjxv866-8kr.js","/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","/litellm-asset-prefix/_next/static/chunks/0csst_9x.d5wb.js"],"default"] +b:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +c:"$Sreact.suspense" +f:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +11:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +13:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","onboarding",""],"q":"","i":false,"f":[[["",{"children":["onboarding",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L7",null,{"Component":"$8","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@9","$@a"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0skjxv866-8kr.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0.xp85h9ki~9t.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0tbzoqict3-mi.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0csst_9x.d5wb.js","async":true,"nonce":"$undefined"}]],["$","$Lb",null,{"children":["$","$c",null,{"name":"Next.MetadataOutlet","children":"$@d"}]}]]}],{},null,false,null]},null,false,"$@e"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lf",null,{"children":"$L10"}],["$","div",null,{"hidden":true,"children":["$","$L11",null,{"children":["$","$c",null,{"name":"Next.Metadata","children":"$L12"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$13",[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +14:[] +e:"$W14" +9:{} +a:"$0:f:0:1:1:children:1:children:0:props:children:0:props:serverProvidedParams:params" +10:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +15:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +d:null +12:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L15","4",{}]] diff --git a/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.organizations.__PAGE__.txt b/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.organizations.__PAGE__.txt new file mode 100644 index 00000000000..395e0aa6285 --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.organizations.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[526612,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/08.e6-0-i510z.js","/litellm-asset-prefix/_next/static/chunks/0hipu1px0oa-i.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0xhq8.xb2mggk.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/052zw1.u.as-x.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/08.e6-0-i510z.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0hipu1px0oa-i.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0xhq8.xb2mggk.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/052zw1.u.as-x.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.organizations.txt b/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.organizations.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.organizations.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/organizations/__next._full.txt b/litellm/proxy/_experimental/out/organizations/__next._full.txt new file mode 100644 index 00000000000..3d945b40e60 --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","organizations",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["organizations",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[526612,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/08.e6-0-i510z.js","/litellm-asset-prefix/_next/static/chunks/0hipu1px0oa-i.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0xhq8.xb2mggk.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/052zw1.u.as-x.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/08.e6-0-i510z.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0hipu1px0oa-i.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0xhq8.xb2mggk.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/052zw1.u.as-x.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/organizations/__next._head.txt b/litellm/proxy/_experimental/out/organizations/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/organizations/__next._index.txt b/litellm/proxy/_experimental/out/organizations/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/organizations/__next._tree.txt b/litellm/proxy/_experimental/out/organizations/__next._tree.txt new file mode 100644 index 00000000000..4c1b1c37391 --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"organizations","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/organizations/index.html b/litellm/proxy/_experimental/out/organizations/index.html new file mode 100644 index 00000000000..35d6fa59192 --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/organizations/index.txt b/litellm/proxy/_experimental/out/organizations/index.txt new file mode 100644 index 00000000000..3d945b40e60 --- /dev/null +++ b/litellm/proxy/_experimental/out/organizations/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","organizations",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["organizations",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[526612,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/08.e6-0-i510z.js","/litellm-asset-prefix/_next/static/chunks/0hipu1px0oa-i.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0xhq8.xb2mggk.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/052zw1.u.as-x.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/08.e6-0-i510z.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0hipu1px0oa-i.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0xhq8.xb2mggk.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/052zw1.u.as-x.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.playground.__PAGE__.txt b/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.playground.__PAGE__.txt new file mode 100644 index 00000000000..180c03d568f --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.playground.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[213970,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0afclx4envf0g.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0muex_g1s25-x.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/09n64dqzn.le~.js","/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0afclx4envf0g.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0muex_g1s25-x.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/09n64dqzn.le~.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.playground.txt b/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.playground.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.playground.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/playground/__next._full.txt b/litellm/proxy/_experimental/out/playground/__next._full.txt new file mode 100644 index 00000000000..9c5044607e1 --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","playground",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["playground",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[213970,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0afclx4envf0g.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0muex_g1s25-x.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/09n64dqzn.le~.js","/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0afclx4envf0g.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0muex_g1s25-x.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/09n64dqzn.le~.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/playground/__next._head.txt b/litellm/proxy/_experimental/out/playground/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/playground/__next._index.txt b/litellm/proxy/_experimental/out/playground/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/playground/__next._tree.txt b/litellm/proxy/_experimental/out/playground/__next._tree.txt new file mode 100644 index 00000000000..f273c05d840 --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"playground","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/playground/index.html b/litellm/proxy/_experimental/out/playground/index.html new file mode 100644 index 00000000000..571e2319580 --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/playground/index.txt b/litellm/proxy/_experimental/out/playground/index.txt new file mode 100644 index 00000000000..9c5044607e1 --- /dev/null +++ b/litellm/proxy/_experimental/out/playground/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","playground",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["playground",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[213970,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0afclx4envf0g.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0muex_g1s25-x.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/09n64dqzn.le~.js","/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0_y-b9_d9dsuv.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0afclx4envf0g.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0muex_g1s25-x.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/09n64dqzn.le~.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.policies.__PAGE__.txt b/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.policies.__PAGE__.txt new file mode 100644 index 00000000000..19b815a0b76 --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.policies.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[102616,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/14pn07nb9stc_.js","/litellm-asset-prefix/_next/static/chunks/04119inby~4wy.js","/litellm-asset-prefix/_next/static/chunks/09si~t2d7101x.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0~g42t_dvc1-o.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/14pn07nb9stc_.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04119inby~4wy.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/09si~t2d7101x.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0~g42t_dvc1-o.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.policies.txt b/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.policies.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.policies.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/policies/__next._full.txt b/litellm/proxy/_experimental/out/policies/__next._full.txt new file mode 100644 index 00000000000..7d003af10e0 --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","policies",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["policies",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[102616,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/14pn07nb9stc_.js","/litellm-asset-prefix/_next/static/chunks/04119inby~4wy.js","/litellm-asset-prefix/_next/static/chunks/09si~t2d7101x.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0~g42t_dvc1-o.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/14pn07nb9stc_.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04119inby~4wy.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/09si~t2d7101x.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0~g42t_dvc1-o.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/policies/__next._head.txt b/litellm/proxy/_experimental/out/policies/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/policies/__next._index.txt b/litellm/proxy/_experimental/out/policies/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/policies/__next._tree.txt b/litellm/proxy/_experimental/out/policies/__next._tree.txt new file mode 100644 index 00000000000..9484d63d9ed --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"policies","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/policies/index.html b/litellm/proxy/_experimental/out/policies/index.html new file mode 100644 index 00000000000..a12542710fd --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/policies/index.txt b/litellm/proxy/_experimental/out/policies/index.txt new file mode 100644 index 00000000000..7d003af10e0 --- /dev/null +++ b/litellm/proxy/_experimental/out/policies/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","policies",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["policies",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[102616,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/14pn07nb9stc_.js","/litellm-asset-prefix/_next/static/chunks/04119inby~4wy.js","/litellm-asset-prefix/_next/static/chunks/09si~t2d7101x.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0~g42t_dvc1-o.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/14pn07nb9stc_.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04119inby~4wy.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/09si~t2d7101x.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0n0x5.if~4h0v.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0~g42t_dvc1-o.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.projects.__PAGE__.txt b/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.projects.__PAGE__.txt new file mode 100644 index 00000000000..9b92a1dc70a --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.projects.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[454587,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/09qysx83l-.6u.js","/litellm-asset-prefix/_next/static/chunks/0ql16xan6en_0.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0d1mj4t4xlhja.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/09n4d0jmr93_4.js","/litellm-asset-prefix/_next/static/chunks/0ubynsv~w-kqx.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0kte7ybpz~r8x.js","/litellm-asset-prefix/_next/static/chunks/0b.lop-x27mvf.js","/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qysx83l-.6u.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0ql16xan6en_0.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0d1mj4t4xlhja.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/09n4d0jmr93_4.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0ubynsv~w-kqx.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0kte7ybpz~r8x.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0b.lop-x27mvf.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.projects.txt b/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.projects.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.projects.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/projects/__next._full.txt b/litellm/proxy/_experimental/out/projects/__next._full.txt new file mode 100644 index 00000000000..8537cdbe64e --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","projects",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["projects",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[454587,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/09qysx83l-.6u.js","/litellm-asset-prefix/_next/static/chunks/0ql16xan6en_0.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0d1mj4t4xlhja.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/09n4d0jmr93_4.js","/litellm-asset-prefix/_next/static/chunks/0ubynsv~w-kqx.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0kte7ybpz~r8x.js","/litellm-asset-prefix/_next/static/chunks/0b.lop-x27mvf.js","/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qysx83l-.6u.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0ql16xan6en_0.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0d1mj4t4xlhja.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/09n4d0jmr93_4.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0ubynsv~w-kqx.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0kte7ybpz~r8x.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0b.lop-x27mvf.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/projects/__next._head.txt b/litellm/proxy/_experimental/out/projects/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/projects/__next._index.txt b/litellm/proxy/_experimental/out/projects/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/projects/__next._tree.txt b/litellm/proxy/_experimental/out/projects/__next._tree.txt new file mode 100644 index 00000000000..5b79b0a99a4 --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"projects","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/projects/index.html b/litellm/proxy/_experimental/out/projects/index.html new file mode 100644 index 00000000000..e7dcc8c230a --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/projects/index.txt b/litellm/proxy/_experimental/out/projects/index.txt new file mode 100644 index 00000000000..8537cdbe64e --- /dev/null +++ b/litellm/proxy/_experimental/out/projects/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","projects",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["projects",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[454587,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/09qysx83l-.6u.js","/litellm-asset-prefix/_next/static/chunks/0ql16xan6en_0.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0d1mj4t4xlhja.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/09n4d0jmr93_4.js","/litellm-asset-prefix/_next/static/chunks/0ubynsv~w-kqx.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0kte7ybpz~r8x.js","/litellm-asset-prefix/_next/static/chunks/0b.lop-x27mvf.js","/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qysx83l-.6u.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0ql16xan6en_0.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0d1mj4t4xlhja.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/09n4d0jmr93_4.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0ubynsv~w-kqx.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0kte7ybpz~r8x.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0b.lop-x27mvf.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/032wf1_8kb1mb.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.prompts.__PAGE__.txt b/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.prompts.__PAGE__.txt new file mode 100644 index 00000000000..59c598ea183 --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.prompts.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[66899,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/02hq_0zk6htur.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0i_bg.46lh34y.js","/litellm-asset-prefix/_next/static/chunks/01m7lab3u92-v.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0iq7qt.dkwr7i.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02hq_0zk6htur.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0i_bg.46lh34y.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/01m7lab3u92-v.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0iq7qt.dkwr7i.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.prompts.txt b/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.prompts.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.prompts.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/prompts/__next._full.txt b/litellm/proxy/_experimental/out/prompts/__next._full.txt new file mode 100644 index 00000000000..0d966b32e39 --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","prompts",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["prompts",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[66899,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/02hq_0zk6htur.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0i_bg.46lh34y.js","/litellm-asset-prefix/_next/static/chunks/01m7lab3u92-v.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0iq7qt.dkwr7i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02hq_0zk6htur.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0i_bg.46lh34y.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/01m7lab3u92-v.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0iq7qt.dkwr7i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/prompts/__next._head.txt b/litellm/proxy/_experimental/out/prompts/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/prompts/__next._index.txt b/litellm/proxy/_experimental/out/prompts/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/prompts/__next._tree.txt b/litellm/proxy/_experimental/out/prompts/__next._tree.txt new file mode 100644 index 00000000000..95fdef20cc1 --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"prompts","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/prompts/index.html b/litellm/proxy/_experimental/out/prompts/index.html new file mode 100644 index 00000000000..bc19c6b7492 --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/prompts/index.txt b/litellm/proxy/_experimental/out/prompts/index.txt new file mode 100644 index 00000000000..0d966b32e39 --- /dev/null +++ b/litellm/proxy/_experimental/out/prompts/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","prompts",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["prompts",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[66899,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/02hq_0zk6htur.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0i_bg.46lh34y.js","/litellm-asset-prefix/_next/static/chunks/01m7lab3u92-v.js","/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","/litellm-asset-prefix/_next/static/chunks/0iq7qt.dkwr7i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02hq_0zk6htur.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0i_bg.46lh34y.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/01m7lab3u92-v.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0m6zdocif1gl4.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0iq7qt.dkwr7i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.router-settings.__PAGE__.txt b/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.router-settings.__PAGE__.txt new file mode 100644 index 00000000000..2dd32be6be0 --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.router-settings.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[389543,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0fbigtz~tewov.js","/litellm-asset-prefix/_next/static/chunks/011mgw.-67gs_.js","/litellm-asset-prefix/_next/static/chunks/0ff8~y~c6xxv-.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/06wsz_ii_ixc0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/03zxkn.2-qj65.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/15.9ylrtxojbj.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0fbigtz~tewov.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/011mgw.-67gs_.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0ff8~y~c6xxv-.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/06wsz_ii_ixc0.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/03zxkn.2-qj65.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/15.9ylrtxojbj.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.router-settings.txt b/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.router-settings.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.router-settings.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/router-settings/__next._full.txt b/litellm/proxy/_experimental/out/router-settings/__next._full.txt new file mode 100644 index 00000000000..ffe23fd4234 --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","router-settings",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["router-settings",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[389543,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0fbigtz~tewov.js","/litellm-asset-prefix/_next/static/chunks/011mgw.-67gs_.js","/litellm-asset-prefix/_next/static/chunks/0ff8~y~c6xxv-.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/06wsz_ii_ixc0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/03zxkn.2-qj65.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/15.9ylrtxojbj.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0fbigtz~tewov.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/011mgw.-67gs_.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0ff8~y~c6xxv-.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/06wsz_ii_ixc0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/03zxkn.2-qj65.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/15.9ylrtxojbj.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/router-settings/__next._head.txt b/litellm/proxy/_experimental/out/router-settings/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/router-settings/__next._index.txt b/litellm/proxy/_experimental/out/router-settings/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/router-settings/__next._tree.txt b/litellm/proxy/_experimental/out/router-settings/__next._tree.txt new file mode 100644 index 00000000000..1f8460dfb12 --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"router-settings","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/router-settings/index.html b/litellm/proxy/_experimental/out/router-settings/index.html new file mode 100644 index 00000000000..bc3d14661e7 --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/router-settings/index.txt b/litellm/proxy/_experimental/out/router-settings/index.txt new file mode 100644 index 00000000000..ffe23fd4234 --- /dev/null +++ b/litellm/proxy/_experimental/out/router-settings/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","router-settings",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["router-settings",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[389543,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0fbigtz~tewov.js","/litellm-asset-prefix/_next/static/chunks/011mgw.-67gs_.js","/litellm-asset-prefix/_next/static/chunks/0ff8~y~c6xxv-.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/06wsz_ii_ixc0.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/03zxkn.2-qj65.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/15.9ylrtxojbj.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0fbigtz~tewov.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/011mgw.-67gs_.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0ff8~y~c6xxv-.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/06wsz_ii_ixc0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/03zxkn.2-qj65.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0nnx~7-7e5t~1.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/15.9ylrtxojbj.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.search-tools.__PAGE__.txt b/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.search-tools.__PAGE__.txt new file mode 100644 index 00000000000..3fd101af72e --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.search-tools.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[962296,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/05efmcn18yevj.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0qofycjxzylqf.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/05efmcn18yevj.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0qofycjxzylqf.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.search-tools.txt b/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.search-tools.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.search-tools.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/search-tools/__next._full.txt b/litellm/proxy/_experimental/out/search-tools/__next._full.txt new file mode 100644 index 00000000000..53bc863280d --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","search-tools",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["search-tools",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[962296,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/05efmcn18yevj.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0qofycjxzylqf.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/05efmcn18yevj.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0qofycjxzylqf.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/search-tools/__next._head.txt b/litellm/proxy/_experimental/out/search-tools/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/search-tools/__next._index.txt b/litellm/proxy/_experimental/out/search-tools/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/search-tools/__next._tree.txt b/litellm/proxy/_experimental/out/search-tools/__next._tree.txt new file mode 100644 index 00000000000..1fab50c5f80 --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"search-tools","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/search-tools/index.html b/litellm/proxy/_experimental/out/search-tools/index.html new file mode 100644 index 00000000000..238f172e45b --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/search-tools/index.txt b/litellm/proxy/_experimental/out/search-tools/index.txt new file mode 100644 index 00000000000..53bc863280d --- /dev/null +++ b/litellm/proxy/_experimental/out/search-tools/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","search-tools",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["search-tools",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[962296,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/05efmcn18yevj.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0qofycjxzylqf.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/05efmcn18yevj.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0qofycjxzylqf.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.skills.__PAGE__.txt b/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.skills.__PAGE__.txt new file mode 100644 index 00000000000..ced99ea8295 --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.skills.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[974992,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0yqqp4mmyebbs.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/12lhnhzn7xr1r.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0yqqp4mmyebbs.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/12lhnhzn7xr1r.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.skills.txt b/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.skills.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.skills.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/skills/__next._full.txt b/litellm/proxy/_experimental/out/skills/__next._full.txt new file mode 100644 index 00000000000..ba3ed735a31 --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","skills",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["skills",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[974992,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0yqqp4mmyebbs.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/12lhnhzn7xr1r.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0yqqp4mmyebbs.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/12lhnhzn7xr1r.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/skills/__next._head.txt b/litellm/proxy/_experimental/out/skills/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/skills/__next._index.txt b/litellm/proxy/_experimental/out/skills/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/skills/__next._tree.txt b/litellm/proxy/_experimental/out/skills/__next._tree.txt new file mode 100644 index 00000000000..2191fbde299 --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"skills","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/skills/index.html b/litellm/proxy/_experimental/out/skills/index.html new file mode 100644 index 00000000000..dc9a67af4b6 --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/skills/index.txt b/litellm/proxy/_experimental/out/skills/index.txt new file mode 100644 index 00000000000..ba3ed735a31 --- /dev/null +++ b/litellm/proxy/_experimental/out/skills/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","skills",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["skills",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[974992,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0yqqp4mmyebbs.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/12lhnhzn7xr1r.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0yqqp4mmyebbs.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/12lhnhzn7xr1r.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.tag-management.__PAGE__.txt b/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.tag-management.__PAGE__.txt new file mode 100644 index 00000000000..69b27f7a4d7 --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.tag-management.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[601757,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0oeiq~0bevyfo.js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0wrsqsfdm2msz.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0ubbv4xlta87q.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0oeiq~0bevyfo.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0wrsqsfdm2msz.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0ubbv4xlta87q.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.tag-management.txt b/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.tag-management.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.tag-management.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/tag-management/__next._full.txt b/litellm/proxy/_experimental/out/tag-management/__next._full.txt new file mode 100644 index 00000000000..d3649b1acd1 --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","tag-management",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["tag-management",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[601757,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0oeiq~0bevyfo.js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0wrsqsfdm2msz.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0ubbv4xlta87q.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0oeiq~0bevyfo.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0wrsqsfdm2msz.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0ubbv4xlta87q.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/tag-management/__next._head.txt b/litellm/proxy/_experimental/out/tag-management/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/tag-management/__next._index.txt b/litellm/proxy/_experimental/out/tag-management/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/tag-management/__next._tree.txt b/litellm/proxy/_experimental/out/tag-management/__next._tree.txt new file mode 100644 index 00000000000..b710a7c1164 --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"tag-management","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/tag-management/index.html b/litellm/proxy/_experimental/out/tag-management/index.html new file mode 100644 index 00000000000..1405caa78d3 --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/tag-management/index.txt b/litellm/proxy/_experimental/out/tag-management/index.txt new file mode 100644 index 00000000000..d3649b1acd1 --- /dev/null +++ b/litellm/proxy/_experimental/out/tag-management/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","tag-management",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["tag-management",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[601757,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0oeiq~0bevyfo.js","/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0wrsqsfdm2msz.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0ubbv4xlta87q.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/03-4f3.602g1r.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0oeiq~0bevyfo.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0~r95y0t-0dlp.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0wrsqsfdm2msz.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0ubbv4xlta87q.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.teams.__PAGE__.txt b/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.teams.__PAGE__.txt new file mode 100644 index 00000000000..83d2fa9dcb2 --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.teams.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[596115,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","/litellm-asset-prefix/_next/static/chunks/189gx.py268lp.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0_pv6eckrl4ll.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0d6--m0s425_s.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","/litellm-asset-prefix/_next/static/chunks/0ig36cgw_.2w2.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/09t-7sfh4ovhu.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/02u6qkt2tomg4.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/189gx.py268lp.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0_pv6eckrl4ll.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0d6--m0s425_s.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0ig36cgw_.2w2.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/09t-7sfh4ovhu.js","async":true}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/02u6qkt2tomg4.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.teams.txt b/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.teams.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.teams.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/teams/__next._full.txt b/litellm/proxy/_experimental/out/teams/__next._full.txt new file mode 100644 index 00000000000..bb345862265 --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","teams",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["teams",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[596115,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","/litellm-asset-prefix/_next/static/chunks/189gx.py268lp.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0_pv6eckrl4ll.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0d6--m0s425_s.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","/litellm-asset-prefix/_next/static/chunks/0ig36cgw_.2w2.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/09t-7sfh4ovhu.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/02u6qkt2tomg4.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/189gx.py268lp.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0_pv6eckrl4ll.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0d6--m0s425_s.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0ig36cgw_.2w2.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/09t-7sfh4ovhu.js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/02u6qkt2tomg4.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/teams/__next._head.txt b/litellm/proxy/_experimental/out/teams/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/teams/__next._index.txt b/litellm/proxy/_experimental/out/teams/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/teams/__next._tree.txt b/litellm/proxy/_experimental/out/teams/__next._tree.txt new file mode 100644 index 00000000000..406eca464cd --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"teams","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/teams/index.html b/litellm/proxy/_experimental/out/teams/index.html new file mode 100644 index 00000000000..e2b4223bb6d --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/teams/index.txt b/litellm/proxy/_experimental/out/teams/index.txt new file mode 100644 index 00000000000..bb345862265 --- /dev/null +++ b/litellm/proxy/_experimental/out/teams/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","teams",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["teams",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[596115,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","/litellm-asset-prefix/_next/static/chunks/189gx.py268lp.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0_pv6eckrl4ll.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0d6--m0s425_s.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","/litellm-asset-prefix/_next/static/chunks/0ig36cgw_.2w2.js","/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","/litellm-asset-prefix/_next/static/chunks/09t-7sfh4ovhu.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/02u6qkt2tomg4.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/13r-xkk_i-8_r.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0.cm9osit06~i.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/189gx.py268lp.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0_pv6eckrl4ll.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0d6--m0s425_s.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0nb9hn_5vp72z.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/0ig36cgw_.2w2.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0mh1wnrvmv_y7.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/09t-7sfh4ovhu.js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/02u6qkt2tomg4.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.tool-policies.__PAGE__.txt b/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.tool-policies.__PAGE__.txt new file mode 100644 index 00000000000..c3bb7dad8a3 --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.tool-policies.__PAGE__.txt @@ -0,0 +1,10 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[752754,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0ieipexnz8d8h.js","/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0ieipexnz8d8h.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.tool-policies.txt b/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.tool-policies.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.tool-policies.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/tool-policies/__next._full.txt b/litellm/proxy/_experimental/out/tool-policies/__next._full.txt new file mode 100644 index 00000000000..dd76e7e2c28 --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/__next._full.txt @@ -0,0 +1,35 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"P":null,"c":["","tool-policies",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["tool-policies",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[752754,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0ieipexnz8d8h.js","/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0ieipexnz8d8h.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/tool-policies/__next._head.txt b/litellm/proxy/_experimental/out/tool-policies/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/tool-policies/__next._index.txt b/litellm/proxy/_experimental/out/tool-policies/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/tool-policies/__next._tree.txt b/litellm/proxy/_experimental/out/tool-policies/__next._tree.txt new file mode 100644 index 00000000000..a0fffd0300e --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/__next._tree.txt @@ -0,0 +1,5 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"tool-policies","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/tool-policies/index.html b/litellm/proxy/_experimental/out/tool-policies/index.html new file mode 100644 index 00000000000..82f7ab7b597 --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/tool-policies/index.txt b/litellm/proxy/_experimental/out/tool-policies/index.txt new file mode 100644 index 00000000000..dd76e7e2c28 --- /dev/null +++ b/litellm/proxy/_experimental/out/tool-policies/index.txt @@ -0,0 +1,35 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +:HL["/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","style"] +0:{"P":null,"c":["","tool-policies",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["tool-policies",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[752754,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0ieipexnz8d8h.js","/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/0jib1e4hgitwz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0axk76owb7jv..js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0ieipexnz8d8h.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0r9irx-7_i6hr.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0ft3qhkd2xm70.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.transform-request.__PAGE__.txt b/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.transform-request.__PAGE__.txt new file mode 100644 index 00000000000..c177e9bac72 --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.transform-request.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[411929,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/068p6o.s_qzmk.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/068p6o.s_qzmk.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.transform-request.txt b/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.transform-request.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.transform-request.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/transform-request/__next._full.txt b/litellm/proxy/_experimental/out/transform-request/__next._full.txt new file mode 100644 index 00000000000..f459a32e620 --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","transform-request",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["transform-request",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[411929,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/068p6o.s_qzmk.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/068p6o.s_qzmk.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/transform-request/__next._head.txt b/litellm/proxy/_experimental/out/transform-request/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/transform-request/__next._index.txt b/litellm/proxy/_experimental/out/transform-request/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/transform-request/__next._tree.txt b/litellm/proxy/_experimental/out/transform-request/__next._tree.txt new file mode 100644 index 00000000000..2d011e87e55 --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"transform-request","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/transform-request/index.html b/litellm/proxy/_experimental/out/transform-request/index.html new file mode 100644 index 00000000000..71839cbc66b --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/transform-request/index.txt b/litellm/proxy/_experimental/out/transform-request/index.txt new file mode 100644 index 00000000000..f459a32e620 --- /dev/null +++ b/litellm/proxy/_experimental/out/transform-request/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","transform-request",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["transform-request",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[411929,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/068p6o.s_qzmk.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/068p6o.s_qzmk.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.ui-theme.__PAGE__.txt b/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.ui-theme.__PAGE__.txt new file mode 100644 index 00000000000..79d9a4a465e --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.ui-theme.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[312130,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/11m6ge09i-sdl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/11m6ge09i-sdl.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.ui-theme.txt b/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.ui-theme.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/__next.!KGRhc2hib2FyZCk.ui-theme.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/ui-theme/__next._full.txt b/litellm/proxy/_experimental/out/ui-theme/__next._full.txt new file mode 100644 index 00000000000..133dd745a21 --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","ui-theme",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["ui-theme",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[312130,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/11m6ge09i-sdl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/11m6ge09i-sdl.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/ui-theme/__next._head.txt b/litellm/proxy/_experimental/out/ui-theme/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/ui-theme/__next._index.txt b/litellm/proxy/_experimental/out/ui-theme/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/ui-theme/__next._tree.txt b/litellm/proxy/_experimental/out/ui-theme/__next._tree.txt new file mode 100644 index 00000000000..9faeb622ab3 --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"ui-theme","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/ui-theme/index.html b/litellm/proxy/_experimental/out/ui-theme/index.html new file mode 100644 index 00000000000..ab2664408cb --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/ui-theme/index.txt b/litellm/proxy/_experimental/out/ui-theme/index.txt new file mode 100644 index 00000000000..133dd745a21 --- /dev/null +++ b/litellm/proxy/_experimental/out/ui-theme/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","ui-theme",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["ui-theme",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[312130,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/11m6ge09i-sdl.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/11m6ge09i-sdl.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.usage.__PAGE__.txt b/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.usage.__PAGE__.txt new file mode 100644 index 00000000000..16b01fadd6e --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.usage.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[986888,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0hwry-i7zdlyq.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/03sib2ibxxpji.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0de6le6gt7u2y.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0hwry-i7zdlyq.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/03sib2ibxxpji.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0de6le6gt7u2y.js","async":true}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","async":true}],["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true}],["$","script","script-23",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.usage.txt b/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.usage.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/__next.!KGRhc2hib2FyZCk.usage.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/usage/__next._full.txt b/litellm/proxy/_experimental/out/usage/__next._full.txt new file mode 100644 index 00000000000..72d0171ab42 --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","usage",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["usage",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[986888,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0hwry-i7zdlyq.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/03sib2ibxxpji.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0de6le6gt7u2y.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0hwry-i7zdlyq.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/03sib2ibxxpji.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0de6le6gt7u2y.js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","async":true,"nonce":"$undefined"}],["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true,"nonce":"$undefined"}],["$","script","script-23",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/usage/__next._head.txt b/litellm/proxy/_experimental/out/usage/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/usage/__next._index.txt b/litellm/proxy/_experimental/out/usage/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/usage/__next._tree.txt b/litellm/proxy/_experimental/out/usage/__next._tree.txt new file mode 100644 index 00000000000..b3153109529 --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"usage","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/usage/index.html b/litellm/proxy/_experimental/out/usage/index.html new file mode 100644 index 00000000000..6751e01a9e9 --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/usage/index.txt b/litellm/proxy/_experimental/out/usage/index.txt new file mode 100644 index 00000000000..72d0171ab42 --- /dev/null +++ b/litellm/proxy/_experimental/out/usage/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","usage",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["usage",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[986888,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","/litellm-asset-prefix/_next/static/chunks/0hwry-i7zdlyq.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/03sib2ibxxpji.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0de6le6gt7u2y.js","/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0vffq7buvlg04.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16jfov1k2wrj0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0.bx44y-6~tug.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0hwry-i7zdlyq.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/03sib2ibxxpji.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0ph0315t6aok1.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0f2wyvhnwd.zh.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0h93t~lbv3mn~.js","async":true,"nonce":"$undefined"}],["$","script","script-16",{"src":"/litellm-asset-prefix/_next/static/chunks/15t9dw3befzvy.js","async":true,"nonce":"$undefined"}],["$","script","script-17",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-18",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-19",{"src":"/litellm-asset-prefix/_next/static/chunks/0de6le6gt7u2y.js","async":true,"nonce":"$undefined"}],["$","script","script-20",{"src":"/litellm-asset-prefix/_next/static/chunks/05q6y.kb.q2s..js","async":true,"nonce":"$undefined"}],["$","script","script-21",{"src":"/litellm-asset-prefix/_next/static/chunks/0ejhw01~9ehf6.js","async":true,"nonce":"$undefined"}],["$","script","script-22",{"src":"/litellm-asset-prefix/_next/static/chunks/08n63gj8a5vdw.js","async":true,"nonce":"$undefined"}],["$","script","script-23",{"src":"/litellm-asset-prefix/_next/static/chunks/036wlkuzplhfz.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.users.__PAGE__.txt b/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.users.__PAGE__.txt new file mode 100644 index 00000000000..fc48213c2ff --- /dev/null +++ b/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.users.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[198134,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0zz6cagpnuur8.js","/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","/litellm-asset-prefix/_next/static/chunks/0-~nw1zmks9_4.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/03rw9i0cxdgdj.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0wdw7d1enxey-.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0xi5pylskqz4k.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0zz6cagpnuur8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0-~nw1zmks9_4.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/03rw9i0cxdgdj.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0wdw7d1enxey-.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0xi5pylskqz4k.js","async":true}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.users.txt b/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.users.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/users/__next.!KGRhc2hib2FyZCk.users.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/users/__next._full.txt b/litellm/proxy/_experimental/out/users/__next._full.txt new file mode 100644 index 00000000000..268f4a21146 --- /dev/null +++ b/litellm/proxy/_experimental/out/users/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","users",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["users",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[198134,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0zz6cagpnuur8.js","/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","/litellm-asset-prefix/_next/static/chunks/0-~nw1zmks9_4.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/03rw9i0cxdgdj.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0wdw7d1enxey-.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0xi5pylskqz4k.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0zz6cagpnuur8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0-~nw1zmks9_4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/03rw9i0cxdgdj.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0wdw7d1enxey-.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0xi5pylskqz4k.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/users/__next._head.txt b/litellm/proxy/_experimental/out/users/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/users/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/users/__next._index.txt b/litellm/proxy/_experimental/out/users/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/users/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/users/__next._tree.txt b/litellm/proxy/_experimental/out/users/__next._tree.txt new file mode 100644 index 00000000000..0506bdc7246 --- /dev/null +++ b/litellm/proxy/_experimental/out/users/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"users","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/users/index.html b/litellm/proxy/_experimental/out/users/index.html new file mode 100644 index 00000000000..5e02a77ab9f --- /dev/null +++ b/litellm/proxy/_experimental/out/users/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/users/index.txt b/litellm/proxy/_experimental/out/users/index.txt new file mode 100644 index 00000000000..268f4a21146 --- /dev/null +++ b/litellm/proxy/_experimental/out/users/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","users",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["users",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[198134,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0zz6cagpnuur8.js","/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","/litellm-asset-prefix/_next/static/chunks/0-~nw1zmks9_4.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/03rw9i0cxdgdj.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","/litellm-asset-prefix/_next/static/chunks/0wdw7d1enxey-.js","/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","/litellm-asset-prefix/_next/static/chunks/0xi5pylskqz4k.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0zz6cagpnuur8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0m9eq7z1d8z7f.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0-~nw1zmks9_4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/03rw9i0cxdgdj.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0v1rxqc1hqmrl.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0wdw7d1enxey-.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/0q6~n4y84cejn.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0xi5pylskqz4k.js","async":true,"nonce":"$undefined"}],["$","script","script-15",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.vector-stores.__PAGE__.txt b/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.vector-stores.__PAGE__.txt new file mode 100644 index 00000000000..f0870be72b3 --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.vector-stores.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[400157,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/04tc3ssviv_6d.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0efyfhhak4ccc.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0dvxcnqpg0_ef.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/0n~wn5hor8~tu.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/04tc3ssviv_6d.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0efyfhhak4ccc.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0dvxcnqpg0_ef.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0n~wn5hor8~tu.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.vector-stores.txt b/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.vector-stores.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/__next.!KGRhc2hib2FyZCk.vector-stores.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/vector-stores/__next._full.txt b/litellm/proxy/_experimental/out/vector-stores/__next._full.txt new file mode 100644 index 00000000000..954cb6b4ef7 --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","vector-stores",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["vector-stores",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[400157,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/04tc3ssviv_6d.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0efyfhhak4ccc.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0dvxcnqpg0_ef.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/0n~wn5hor8~tu.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/04tc3ssviv_6d.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0efyfhhak4ccc.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0dvxcnqpg0_ef.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0n~wn5hor8~tu.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/vector-stores/__next._head.txt b/litellm/proxy/_experimental/out/vector-stores/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/vector-stores/__next._index.txt b/litellm/proxy/_experimental/out/vector-stores/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/vector-stores/__next._tree.txt b/litellm/proxy/_experimental/out/vector-stores/__next._tree.txt new file mode 100644 index 00000000000..30f16ef0035 --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"vector-stores","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/vector-stores/index.html b/litellm/proxy/_experimental/out/vector-stores/index.html new file mode 100644 index 00000000000..6f6dba39b32 --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/vector-stores/index.txt b/litellm/proxy/_experimental/out/vector-stores/index.txt new file mode 100644 index 00000000000..954cb6b4ef7 --- /dev/null +++ b/litellm/proxy/_experimental/out/vector-stores/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","vector-stores",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["vector-stores",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[400157,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","/litellm-asset-prefix/_next/static/chunks/04tc3ssviv_6d.js","/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","/litellm-asset-prefix/_next/static/chunks/0efyfhhak4ccc.js","/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","/litellm-asset-prefix/_next/static/chunks/0dvxcnqpg0_ef.js","/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","/litellm-asset-prefix/_next/static/chunks/0n~wn5hor8~tu.js","/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/17oj3l80l727c.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0sqw622fcvsv4.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/04tc3ssviv_6d.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7em-5kjv49e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0el08tticy_20.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/0efyfhhak4ccc.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0onea0n77pqw1.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/0dvxcnqpg0_ef.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/0sx3mu2_l9g_y.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/0m._ijxus~ryi.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/0n~wn5hor8~tu.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0cmepm.jkel-i.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/vercel.svg b/litellm/proxy/_experimental/out/vercel.svg new file mode 100644 index 00000000000..d2f84222734 --- /dev/null +++ b/litellm/proxy/_experimental/out/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.txt b/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.txt new file mode 100644 index 00000000000..3413c4c285d --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.txt @@ -0,0 +1,7 @@ +1:"$Sreact.fragment" +2:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +3:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +4:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +5:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +0:{"rsc":["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true}]],["$","$L2",null,{"Component":"$3","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","template":["$","$L5",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]},"serverProvidedParams":{"params":{},"promises":["$@6"]}}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +6:"$0:rsc:props:children:1:props:serverProvidedParams:params" diff --git a/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.workflows.__PAGE__.txt b/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.workflows.__PAGE__.txt new file mode 100644 index 00000000000..457553e3f5b --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.workflows.__PAGE__.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +3:I[425656,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0l7~-onhsb.b4.js","/litellm-asset-prefix/_next/static/chunks/0dqmuvqc8719p.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js"],"default"] +6:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +7:"$Sreact.suspense" +0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7~-onhsb.b4.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0dqmuvqc8719p.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} +4:{} +5:"$0:rsc:props:children:0:props:serverProvidedParams:params" +8:null diff --git a/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.workflows.txt b/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.workflows.txt new file mode 100644 index 00000000000..9b3f7872692 --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/__next.!KGRhc2hib2FyZCk.workflows.txt @@ -0,0 +1,5 @@ +1:"$Sreact.fragment" +2:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:[] +0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/workflows/__next._full.txt b/litellm/proxy/_experimental/out/workflows/__next._full.txt new file mode 100644 index 00000000000..10dafead89a --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/__next._full.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","workflows",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["workflows",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[425656,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0l7~-onhsb.b4.js","/litellm-asset-prefix/_next/static/chunks/0dqmuvqc8719p.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7~-onhsb.b4.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0dqmuvqc8719p.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_experimental/out/workflows/__next._head.txt b/litellm/proxy/_experimental/out/workflows/__next._head.txt new file mode 100644 index 00000000000..51067b68caa --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/__next._head.txt @@ -0,0 +1,6 @@ +1:"$Sreact.fragment" +2:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +3:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +4:"$Sreact.suspense" +5:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L5","4",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/workflows/__next._index.txt b/litellm/proxy/_experimental/out/workflows/__next._index.txt new file mode 100644 index 00000000000..ac9a9fe0dca --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/__next._index.txt @@ -0,0 +1,9 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/workflows/__next._tree.txt b/litellm/proxy/_experimental/out/workflows/__next._tree.txt new file mode 100644 index 00000000000..9e1cbeb3395 --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/__next._tree.txt @@ -0,0 +1,4 @@ +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"(dashboard)","param":null,"prefetchHints":0,"slots":{"children":{"name":"workflows","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}}}},"staleTime":300,"buildId":"N7WCdfNd30Hp6HEF5tFIL"} diff --git a/litellm/proxy/_experimental/out/workflows/index.html b/litellm/proxy/_experimental/out/workflows/index.html new file mode 100644 index 00000000000..07710b75965 --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/index.html @@ -0,0 +1 @@ +LiteLLM Dashboard
🚅 LiteLLM
Loading...
\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/workflows/index.txt b/litellm/proxy/_experimental/out/workflows/index.txt new file mode 100644 index 00000000000..10dafead89a --- /dev/null +++ b/litellm/proxy/_experimental/out/workflows/index.txt @@ -0,0 +1,34 @@ +1:"$Sreact.fragment" +2:I[867271,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +3:I[71195,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +4:I[557951,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"AuthProvider"] +5:I[339756,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +6:I[837457,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default"] +7:I[92825,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientSegmentRoot"] +8:I[216370,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js"],"default"] +f:I[168027,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"default",1] +:HL["/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","style"] +:HL["/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","style"] +:HL["/litellm-asset-prefix/_next/static/media/83afe278b6a6bb3c-s.p.0q-301v4kxxnr.woff2","font",{"crossOrigin":"","type":"font/woff2"}] +0:{"P":null,"c":["","workflows",""],"q":"","i":false,"f":[[["",{"children":["(dashboard)",{"children":["workflows",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"inter_5972bc34-module__OU16Qa__className","children":["$","$L2",null,{"children":["$","$L3",null,{"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","async":true,"nonce":"$undefined"}],["$","script","script-12",{"src":"/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","async":true,"nonce":"$undefined"}],["$","script","script-13",{"src":"/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","async":true,"nonce":"$undefined"}],["$","script","script-14",{"src":"/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],"$L9"],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]},"serverProvidedParams":{"params":{},"promises":["$@a"]}}]]}],{"children":["$Lb",{"children":["$Lc",{},null,false,null]},null,false,"$@d"]},null,false,null]},null,false,null],"$Le",false]],"m":"$undefined","G":["$f",["$L10","$L11"]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"N7WCdfNd30Hp6HEF5tFIL"} +12:I[347257,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ClientPageRoot"] +13:I[425656,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js","/litellm-asset-prefix/_next/static/chunks/0kc37~1yrtr2p.js","/litellm-asset-prefix/_next/static/chunks/04s-iyzsr4cq~.js","/litellm-asset-prefix/_next/static/chunks/16zj68af4snfa.js","/litellm-asset-prefix/_next/static/chunks/003_1s9xbht43.js","/litellm-asset-prefix/_next/static/chunks/0tmaomqtwbi33.js","/litellm-asset-prefix/_next/static/chunks/02-2~p5k.ielz.js","/litellm-asset-prefix/_next/static/chunks/0op63kdo3uwng.js","/litellm-asset-prefix/_next/static/chunks/112_-0alpxot8.js","/litellm-asset-prefix/_next/static/chunks/043q3g5-5-aju.js","/litellm-asset-prefix/_next/static/chunks/12yfh0_n50ojz.js","/litellm-asset-prefix/_next/static/chunks/06w8_.601z7_i.js","/litellm-asset-prefix/_next/static/chunks/0nht59ws0elww.js","/litellm-asset-prefix/_next/static/chunks/0lg.6rbfsd-l9.js","/litellm-asset-prefix/_next/static/chunks/18187o3gb9vc5.js","/litellm-asset-prefix/_next/static/chunks/0._ir~nvcseg7.js","/litellm-asset-prefix/_next/static/chunks/0l7~-onhsb.b4.js","/litellm-asset-prefix/_next/static/chunks/0dqmuvqc8719p.js","/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js"],"default"] +16:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"OutletBoundary"] +17:"$Sreact.suspense" +1a:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"ViewportBoundary"] +1c:I[897367,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"MetadataBoundary"] +9:["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}] +b:["$","$1","c",{"children":[null,["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] +c:["$","$1","c",{"children":[["$","$L12",null,{"Component":"$13","serverProvidedParams":{"searchParams":{},"params":"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params","promises":["$@14","$@15"]}}],[["$","script","script-0",{"src":"/litellm-asset-prefix/_next/static/chunks/0l7~-onhsb.b4.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/litellm-asset-prefix/_next/static/chunks/0dqmuvqc8719p.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/litellm-asset-prefix/_next/static/chunks/02-u6qtmsnqn0.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/litellm-asset-prefix/_next/static/chunks/0cjjdx_ufdyva.js","async":true,"nonce":"$undefined"}]],["$","$L16",null,{"children":["$","$17",null,{"name":"Next.MetadataOutlet","children":"$@18"}]}]]}] +19:[] +d:"$W19" +e:["$","$1","h",{"children":[null,["$","$L1a",null,{"children":"$L1b"}],["$","div",null,{"hidden":true,"children":["$","$L1c",null,{"children":["$","$17",null,{"name":"Next.Metadata","children":"$L1d"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}] +10:["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/05qmwjqau64bz.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +11:["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/chunks/15j3hwz2dxrik.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}] +a:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +14:{} +15:"$0:f:0:1:1:children:0:props:children:1:props:serverProvidedParams:params" +1b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] +1e:I[27201,["/litellm-asset-prefix/_next/static/chunks/09qhs_ev_bxr8.js","/litellm-asset-prefix/_next/static/chunks/0a6iga_s7xld1.js","/litellm-asset-prefix/_next/static/chunks/0hpxif-db_y5-.js"],"IconMark"] +18:null +1d:[["$","title","0",{"children":"LiteLLM Dashboard"}],["$","meta","1",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0~dgapwhi~75y.ico","sizes":"48x48","type":"image/x-icon"}],["$","link","3",{"rel":"icon","href":"/get_favicon"}],["$","$L1e","4",{}]] diff --git a/litellm/proxy/_new_new_secret_config.yaml b/litellm/proxy/_new_new_secret_config.yaml new file mode 100644 index 00000000000..7932cc20fe9 --- /dev/null +++ b/litellm/proxy/_new_new_secret_config.yaml @@ -0,0 +1,14 @@ +model_list: + - model_name: bedrock-claude + litellm_params: + model: bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0 + aws_region_name: us-east-1 + aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID + aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY + +litellm_settings: + callbacks: ["datadog"] # logs llm success + failure logs on datadog + service_callback: ["datadog"] # logs redis, postgres failures on datadog + +general_settings: + store_prompts_in_spend_logs: true diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml new file mode 100644 index 00000000000..703fe6adc41 --- /dev/null +++ b/litellm/proxy/_new_secret_config.yaml @@ -0,0 +1,83 @@ +# model_list: +# - model_name: claude-sonnet-4-6 +# litellm_params: {model: anthropic/claude-sonnet-4-6} +# model_info: +# litellm_routing_preferences: +# quality_tier: 1 +# keywords: [tin] +# - model_name: gpt-4o-mini +# litellm_params: {model: openai/gpt-4o-mini} +# model_info: +# litellm_routing_preferences: +# quality_tier: 1 +# keywords: [] +# - model_name: gpt-4o +# litellm_params: {model: openai/gpt-4o} +# model_info: +# litellm_routing_preferences: +# quality_tier: 2 +# keywords: [vision, function_calling] +# - model_name: opus +# litellm_params: {model: anthropic/claude-opus-4-7} +# model_info: +# litellm_routing_preferences: +# quality_tier: 3 +# keywords: ["architecture", "design"] +# - model_name: my-quality-router +# litellm_params: +# model: auto_router/adaptive_router +# adaptive_router_default_model: gpt-4o-mini +# adaptive_router_config: +# available_models: [gpt-4o-mini, gpt-4o, opus, claude-sonnet-4-6] +# Example proxy config for the adaptive router (v0). +# +# Wires one logical router ("smart-cheap-router") that adaptively picks between +# two real deployments ("fast" and "smart") based on per-session feedback signals. +# +# How to use from a client: +# POST /v1/chat/completions { "model": "smart-cheap-router", ... } +# Add { "metadata": { "litellm_session_id": "" } } to enable +# sticky-session routing within a conversation. +# +# Required env vars: OPENAI_API_KEY, DATABASE_URL. + +model_list: + # ---- The adaptive router "control" deployment ------------------------- + # `model_name` is what clients call. `available_models` lists the underlying + # deployments the router is allowed to pick from (must match other model_name + # entries in this list). + - model_name: smart-cheap-router + litellm_params: + model: auto_router/adaptive_router + adaptive_router_config: + available_models: ["fast", "smart"] + weights: + quality: 0.7 + cost: 0.3 + + # ---- Underlying deployments the router picks from --------------------- + - model_name: fast + litellm_params: + model: anthropic/claude-sonnet-4-6 + api_key: os.environ/ANTHROPIC_API_KEY + input_cost_per_token: 0.00000015 + model_info: + adaptive_router_preferences: + quality_tier: 2 + strengths: [] + + - model_name: smart + litellm_params: + model: anthropic/claude-opus-4-7 + api_key: os.environ/ANTHROPIC_API_KEY + input_cost_per_token: 0.0000050 + model_info: + adaptive_router_preferences: + quality_tier: 3 + strengths: ["code_generation", "technical_design", "analytical_reasoning"] + +litellm_settings: + drop_params: True + +general_settings: + master_key: sk-1234 # REPLACE in production diff --git a/litellm/proxy/_super_secret_config.yaml b/litellm/proxy/_super_secret_config.yaml new file mode 100644 index 00000000000..b993b9cdfef --- /dev/null +++ b/litellm/proxy/_super_secret_config.yaml @@ -0,0 +1,110 @@ +model_list: +- model_name: claude-3-5-sonnet + litellm_params: + model: claude-3-haiku-20240307 +# - model_name: gemini-1.5-flash-gemini +# litellm_params: +# model: vertex_ai_beta/gemini-1.5-flash +# api_base: https://gateway.ai.cloudflare.com/v1/fa4cdcab1f32b95ca3b53fd36043d691/test/google-vertex-ai/v1/projects/adroit-crow-413218/locations/us-central1/publishers/google/models/gemini-1.5-flash +- litellm_params: + api_base: http://0.0.0.0:8080 + api_key: '' + model: gpt-4o + rpm: 800 + input_cost_per_token: 300 + model_name: gpt-4o +- model_name: llama3-70b-8192 + litellm_params: + model: groq/llama3-70b-8192 +- model_name: fake-openai-endpoint + litellm_params: + model: predibase/llama-3-8b-instruct + api_key: os.environ/PREDIBASE_API_KEY + tenant_id: os.environ/PREDIBASE_TENANT_ID + max_new_tokens: 256 +# - litellm_params: +# api_base: https://my-endpoint-europe-berri-992.openai.azure.com/ +# api_key: os.environ/AZURE_EUROPE_API_KEY +# model: azure/gpt-35-turbo +# rpm: 10 +# model_name: gpt-3.5-turbo-fake-model +- litellm_params: + api_base: https://openai-gpt-4-test-v-1.openai.azure.com + api_key: os.environ/AZURE_API_KEY + api_version: 2024-02-15-preview + model: azure/chatgpt-v-2 + tpm: 100 + model_name: gpt-3.5-turbo +- litellm_params: + model: anthropic.claude-3-sonnet-20240229-v1:0 + model_name: bedrock-anthropic-claude-3 +- litellm_params: + model: claude-3-haiku-20240307 + model_name: anthropic-claude-3 +- litellm_params: + api_base: https://openai-gpt-4-test-v-1.openai.azure.com/ + api_key: os.environ/AZURE_API_KEY + api_version: 2024-02-15-preview + model: azure/chatgpt-v-2 + drop_params: True + tpm: 100 + model_name: gpt-3.5-turbo +- model_name: tts + litellm_params: + model: openai/tts-1 +- model_name: gpt-4-turbo-preview + litellm_params: + api_base: https://openai-france-1234.openai.azure.com + api_key: os.environ/AZURE_FRANCE_API_KEY + api_version: 2024-02-15-preview + model: azure/gpt-turbo +- model_name: text-embedding + litellm_params: + model: textembedding-gecko-multilingual@001 + vertex_project: my-project-9d5c + vertex_location: us-central1 +- model_name: lbl/command-r-plus + litellm_params: + model: openai/lbl/command-r-plus + api_key: "os.environ/VLLM_API_KEY" + api_base: http://vllm-command:8000/v1 + rpm: 1000 + input_cost_per_token: 0 + output_cost_per_token: 0 + model_info: + max_input_tokens: 80920 + +# litellm_settings: +# callbacks: ["dynamic_rate_limiter"] +# # success_callback: ["langfuse"] +# # failure_callback: ["langfuse"] +# # default_team_settings: +# # - team_id: proj1 +# # success_callback: ["langfuse"] +# # langfuse_public_key: os.environ/LANGFUSE_PUBLIC_KEY +# # langfuse_secret: os.environ/LANGFUSE_SECRET +# # langfuse_host: https://us.cloud.langfuse.com +# # - team_id: proj2 +# # success_callback: ["langfuse"] +# # langfuse_public_key: os.environ/LANGFUSE_PUBLIC_KEY +# # langfuse_secret: os.environ/LANGFUSE_SECRET +# # langfuse_host: https://us.cloud.langfuse.com + +assistant_settings: + custom_llm_provider: openai + litellm_params: + api_key: os.environ/OPENAI_API_KEY + + +router_settings: + enable_pre_call_checks: true + + +litellm_settings: + callbacks: ["s3"] + +# general_settings: +# # alerting: ["slack"] +# enable_jwt_auth: True +# litellm_jwtauth: +# team_id_jwt_field: "client_id" \ No newline at end of file diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 5fe17d79ab5..23fe7730c17 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -26,6 +26,7 @@ from litellm.types.llms.openai import ( ResponsesAPIResponse, ) from litellm.types.mcp import ( + MCPAuth, MCPAuthType, MCPCredentials, MCPTransport, @@ -189,6 +190,9 @@ class LitellmTableNames(str, enum.Enum): TOOL_TABLE_NAME = "LiteLLM_ToolTable" CACHE_CONFIG_TABLE_NAME = "LiteLLM_CacheConfig" CONFIG_OVERRIDES_TABLE_NAME = "LiteLLM_ConfigOverrides" + CONFIG_TABLE_NAME = "LiteLLM_Config" + SSO_CONFIG_TABLE_NAME = "LiteLLM_SSOConfig" + UI_SETTINGS_TABLE_NAME = "LiteLLM_UISettings" class Litellm_EntityType(enum.Enum): @@ -585,6 +589,7 @@ class LiteLLMRoutes(enum.Enum): # team "/team/new", "/team/update", + "/team/{team_id}", "/team/delete", "/team/list", "/v2/team/list", @@ -1003,6 +1008,7 @@ class LiteLLM_ObjectPermissionBase(LiteLLMPydanticObjectBase): agent_access_groups: Optional[List[str]] = None models: Optional[List[str]] = None search_tools: Optional[List[str]] = None + mcp_tool_search_enabled: Optional[bool] = None from litellm.types.object_permission import ( # noqa: E402 @@ -1035,11 +1041,13 @@ class GenerateRequestBase(LiteLLMPydanticObjectBase): config: Optional[dict] = {} permissions: Optional[dict] = {} model_max_budget: Optional[dict] = {} # {"gpt-4": 5.0, "gpt-3.5-turbo": 5.0}, defaults to {} + budget_fallbacks: Optional[dict[str, list[str]]] = None model_config = ConfigDict(protected_namespaces=()) model_rpm_limit: Optional[dict] = None model_tpm_limit: Optional[dict] = None mcp_rpm_limit: Optional[Dict[str, int]] = None + tag_rpm_limit: Optional[dict[str, int]] = None guardrails: Optional[List[str]] = None policies: Optional[List[str]] = None prompts: Optional[List[str]] = None @@ -1065,6 +1073,7 @@ class KeyRequestBase(GenerateRequestBase): budget_id: Optional[str] = None tags: Optional[List[str]] = None disable_global_guardrails: Optional[bool] = None + throttle_on_budget_exceeded: Optional[bool] = None enforced_params: Optional[List[str]] = None allowed_routes: Optional[list] = [] allowed_passthrough_routes: Optional[list] = None @@ -1132,6 +1141,7 @@ class GenerateKeyResponse(KeyRequestBase): "config", "permissions", "model_max_budget", + "budget_fallbacks", "router_settings", "budget_limits", ] @@ -1221,6 +1231,14 @@ from litellm.models.mcp_server import ( # noqa: E402 # MCP Proxy Request Types +def _dcr_bridge_auth_type_error(auth_type: object) -> ValueError: + return ValueError( + f"dcr_bridge is only supported for auth_type true_passthrough or oauth_delegate (got {auth_type!r}). " + "The DCR bridge serves gateway-hosted OAuth discovery for the client-forwarded token modes; " + "interactive oauth2 servers already run the gateway authorization-code flow." + ) + + class NewMCPServerRequest(LiteLLMPydanticObjectBase): server_id: Optional[str] = None server_name: Optional[str] = None @@ -1248,15 +1266,25 @@ class NewMCPServerRequest(LiteLLMPydanticObjectBase): token_url: Optional[str] = None registration_url: Optional[str] = None oauth2_flow: Optional[Literal["client_credentials", "authorization_code"]] = None + # Token Exchange (OBO) fields — RFC 8693. These top-level fields are the + # canonical shape; the same keys inside ``credentials`` are the legacy + # pre-column REST shape and are lifted into these columns on write (an + # explicit top-level value wins) and stripped from the stored blob. + token_exchange_endpoint: Optional[str] = None + audience: Optional[str] = None + subject_token_type: Optional[str] = None + token_exchange_profile: Optional[str] = None allow_all_keys: bool = False available_on_public_internet: bool = True delegate_auth_to_upstream: bool = False oauth_passthrough: bool = False + dcr_bridge: Optional[bool] = None is_byok: bool = False byok_description: List[str] = Field(default_factory=list) byok_api_key_help_url: Optional[str] = None source_url: Optional[str] = None timeout: Optional[float] = None + max_concurrent_requests: Optional[int] = None # BYOM submission fields — set by the endpoint, not by the caller. # Any caller-provided values are silently overridden before persistence. approval_status: Optional[str] = Field( @@ -1305,6 +1333,16 @@ class NewMCPServerRequest(LiteLLMPydanticObjectBase): """ return values + @model_validator(mode="before") + @classmethod + def validate_dcr_bridge_auth_type(cls, values): + if not isinstance(values, dict) or not values.get("dcr_bridge"): + return values + auth_type = values.get("auth_type") + if auth_type in (MCPAuth.true_passthrough, MCPAuth.oauth_delegate): + return values + raise _dcr_bridge_auth_type_error(auth_type) + class UpdateMCPServerRequest(LiteLLMPydanticObjectBase): server_id: str @@ -1333,15 +1371,25 @@ class UpdateMCPServerRequest(LiteLLMPydanticObjectBase): token_url: Optional[str] = None registration_url: Optional[str] = None oauth2_flow: Optional[Literal["client_credentials", "authorization_code"]] = None + # Token Exchange (OBO) fields — RFC 8693. These top-level fields are the + # canonical shape; the same keys inside ``credentials`` are the legacy + # pre-column REST shape and are lifted into these columns on write (an + # explicit top-level value wins) and stripped from the stored blob. + token_exchange_endpoint: Optional[str] = None + audience: Optional[str] = None + subject_token_type: Optional[str] = None + token_exchange_profile: Optional[str] = None allow_all_keys: bool = False available_on_public_internet: bool = True delegate_auth_to_upstream: bool = False oauth_passthrough: bool = False + dcr_bridge: Optional[bool] = None is_byok: bool = False byok_description: List[str] = Field(default_factory=list) byok_api_key_help_url: Optional[str] = None source_url: Optional[str] = None timeout: Optional[float] = None + max_concurrent_requests: Optional[int] = None @model_validator(mode="before") @classmethod @@ -1365,6 +1413,21 @@ class UpdateMCPServerRequest(LiteLLMPydanticObjectBase): raise ValueError("url or spec_path is required for HTTP/SSE transport") return values + @model_validator(mode="before") + @classmethod + def validate_dcr_bridge_auth_type(cls, values): + """Partial updates omit auth_type; that case is validated against the stored row by the + update endpoint, which can read the database. This validator covers payloads that carry + both fields.""" + if not isinstance(values, dict) or not values.get("dcr_bridge"): + return values + if "auth_type" not in values: + return values + auth_type = values.get("auth_type") + if auth_type in (MCPAuth.true_passthrough, MCPAuth.oauth_delegate): + return values + raise _dcr_bridge_auth_type_error(auth_type) + from litellm.models.mcp_server import ( # noqa: E402 LiteLLM_MCPServerTable as LiteLLM_MCPServerTable, @@ -2089,6 +2152,41 @@ class PluginConfig(LiteLLMPydanticObjectBase): ) +class CoordinationRedisNode(LiteLLMPydanticObjectBase): + """A single startup node of a cluster-mode Redis used for proxy coordination.""" + + host: str = Field(description="hostname of the cluster node") + port: int = Field(description="port of the cluster node") + + +class CoordinationRedisParams(LiteLLMPydanticObjectBase): + """ + Connection params for the proxy's coordination Redis (cross-pod tpm/rpm rate + limits, spend tracking, pod lock manager, shared health checks), configured + independently of the response-cache backend in `litellm_settings.cache_params`. + """ + + model_config = ConfigDict(extra="allow", protected_namespaces=()) + + host: Optional[str] = Field(None, description="Redis hostname") + port: Optional[int] = Field(None, description="Redis port") + password: Optional[str] = Field(None, description="Redis password") + username: Optional[str] = Field(None, description="Redis username") + url: Optional[str] = Field(None, description="full Redis connection url, e.g. redis://:pass@host:6379") + ssl: Optional[bool] = Field(None, description="connect over TLS") + startup_nodes: Optional[List[CoordinationRedisNode]] = Field( + None, description="cluster-mode startup nodes; when set a cluster client is used" + ) + sentinel_nodes: Optional[List[List[Union[str, int]]]] = Field( + None, description="sentinel [host, port] pairs; when set a sentinel-managed client is used" + ) + sentinel_password: Optional[str] = Field(None, description="password for the sentinel nodes") + service_name: Optional[str] = Field(None, description="sentinel service name") + + def has_connection_target(self) -> bool: + return any(value is not None for value in (self.host, self.url, self.startup_nodes, self.sentinel_nodes)) + + class ConfigGeneralSettings(LiteLLMPydanticObjectBase): """ Documents all the fields supported by `general_settings` in config.yaml @@ -2104,6 +2202,15 @@ class ConfigGeneralSettings(LiteLLMPydanticObjectBase): use_google_kms: Optional[bool] = Field(None, description="decrypt keys with google kms") use_azure_key_vault: Optional[bool] = Field(None, description="load keys from azure key vault") master_key: Optional[str] = Field(None, description="require a key for all calls to proxy") + coordination_redis: Optional[CoordinationRedisParams] = Field( + None, + description=( + "standalone Redis for cross-pod coordination (tpm/rpm rate limits, " + "spend tracking, pod lock manager, shared health checks), configured " + "independently of the response-cache backend; takes precedence over " + "borrowing the `cache_params` Redis and over the REDIS_* env fallback" + ), + ) allow_cli_sso_verification_uri_complete: bool | None = Field( None, description="opt-in to RFC 8628 verification_uri_complete for the CLI SSO device flow, pre-filling the user_code in the browser. Off by default; intended for same-host clients where the device that starts the flow and the browser run on the same machine", @@ -2314,6 +2421,28 @@ class ConfigGeneralSettings(LiteLLMPydanticObjectBase): "is active as a reminder that hard enforcement is relaxed." ), ) + user_url_validation: Optional[bool] = Field( + None, + description=( + "Master switch for the SSRF guard applied to user-supplied URLs " + "(image_url, file_url, MCP/OpenAPI spec URLs, etc). Defaults to True. " + "Set to False to disable DNS/IP validation entirely (not recommended)." + ), + ) + user_url_allowed_hosts: Optional[list[str]] = Field( + None, + description=( + "SSRF allowlist for user-supplied URLs. Entries are `hostname` or " + "`hostname:port` (bracketed for IPv6, e.g. `[::1]:8080`). Allowlisted " + "hosts skip the blocked-network check in validate_url() but still " + "resolve DNS. Use this to permit legitimate internal targets, e.g. " + "an internal OpenAPI/MCP server." + ), + ) + provider_url_destination_allowed_hosts: Optional[list[str]] = Field( + None, + description="Allowlist of hosts a request may redirect a provider call's destination URL to.", + ) class ConfigYAML(LiteLLMPydanticObjectBase): @@ -2439,6 +2568,7 @@ class UserAPIKeyAuth(LiteLLM_VerificationTokenView): # the expected response ob request_route: Optional[str] = None is_session_token: bool = False budget_reservation: Optional[Dict[str, Any]] = Field(default=None, exclude=True) + budget_throttle_pct: Optional[float] = Field(default=None, exclude=True) user: Optional[Any] = None # Expanded user object when expand=user is used created_by_user: Optional[Any] = None # Expanded created_by user when expand=user is used end_user_object_permission: Optional[LiteLLM_ObjectPermissionTable] = None @@ -3821,6 +3951,7 @@ LiteLLM_ManagementEndpoint_MetadataFields = [ "model_rpm_limit", "model_tpm_limit", "mcp_rpm_limit", + "tag_rpm_limit", "rpm_limit_type", "tpm_limit_type", "enforced_params", @@ -3829,6 +3960,7 @@ LiteLLM_ManagementEndpoint_MetadataFields = [ "allowed_vector_store_indexes", "enforced_batch_output_expires_after", "enforced_file_expires_after", + "throttle_on_budget_exceeded", ] LiteLLM_ManagementEndpoint_MetadataFields_Premium = [ @@ -4176,6 +4308,17 @@ class LiteLLM_JWTAuth(LiteLLMPydanticObjectBase): "authorization." ), ) + fallback_to_db_teams: bool = Field( + default=False, + description=( + "When True, users whose JWT contains no team claims are authenticated " + "using their database team memberships instead of receiving HTTP 403. " + "Usage is attributed to the user's first resolvable DB team, or to the " + "team specified via the x-litellm-team-id request header (validated " + "against DB membership). Requires user_id_upsert=True so that user " + "records exist before the fallback runs." + ), + ) issuers: Optional[List[JWTIssuerConfig]] = Field( default=None, description="Optional issuer-bound JWT validation rules. When a token's `iss` matches a configured issuer, validation uses that issuer's JWKS, audience, and claim mappings. Tokens with an unlisted `iss` fall back to the global JWT_AUDIENCE/JWT_ISSUER validation path — this is additive routing, not an allow-list.", diff --git a/litellm/proxy/anthropic_endpoints/endpoints.py b/litellm/proxy/anthropic_endpoints/endpoints.py index 71acc1f3106..92279a9e685 100644 --- a/litellm/proxy/anthropic_endpoints/endpoints.py +++ b/litellm/proxy/anthropic_endpoints/endpoints.py @@ -12,6 +12,9 @@ from litellm.integrations.custom_guardrail import ModifyResponseException from litellm.llms.anthropic.experimental_pass_through.context_management import ( AnthropicContextManagementError, ) +from litellm.llms.base_llm.guardrail_translation.utils import ( + blocked_response_usage as _blocked_response_usage, +) from litellm.proxy._types import * from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_request_processing import ( @@ -134,6 +137,10 @@ async def anthropic_response( from litellm.types.utils import AnthropicMessagesResponse + # Report the blocked LLM response's real token usage (carried on the + # exception) instead of discarding it; zero for pre-call blocks. + _usage = _blocked_response_usage(e.original_response) + _anthropic_response = AnthropicMessagesResponse( id=f"msg_{str(uuid.uuid4())}", type="message", @@ -141,7 +148,7 @@ async def anthropic_response( content=[{"type": "text", "text": e.message}], model=e.model, stop_reason="end_turn", - usage={"input_tokens": 0, "output_tokens": 0}, + usage=_usage, ) if data.get("stream", None) is not None and data["stream"] is True: diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index ced2cf125ce..93811812901 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -60,6 +60,10 @@ from litellm.proxy._types import ( UserAPIKeyAuth, ) from litellm.proxy.auth.route_checks import RouteChecks +from litellm.proxy.auth.budget_throttle import ( + budget_throttle_percentage, + should_throttle_budget_exceeded, +) from litellm.proxy.spend_tracking.budget_reservation import get_budget_window_start from litellm.proxy.common_utils.cache_pydantic_utils import CacheCodec from litellm.proxy.common_utils.http_parsing_utils import ( @@ -94,8 +98,11 @@ from litellm.repositories.user_repository import UserRepository from litellm.router import Router from litellm.utils import get_utc_datetime -from .auth_checks_organization import organization_role_based_access_check -from .auth_utils import get_model_from_request +from .auth_checks_organization import ( + add_team_org_context_to_request_body, + organization_role_based_access_check, +) +from .auth_utils import get_model_from_request, get_request_route_template if TYPE_CHECKING: from opentelemetry.trace import Span as _Span @@ -703,10 +710,29 @@ async def common_checks( # 10 [OPTIONAL] Organization RBAC checks organization_role_based_access_check(user_object=user_object, route=route, request_body=request_body) + async def _fetch_team_org_id(team_id: str) -> Optional[str]: + try: + team = await get_team_object( + team_id=team_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + proxy_logging_obj=proxy_logging_obj, + ) + except HTTPException: + return None + return team.organization_id + + request_body_for_route_check = await add_team_org_context_to_request_body( + route=route, + request_body=request_body, + fetch_team_org_id=_fetch_team_org_id, + route_template=get_request_route_template(request), + ) + _is_route_allowed = _is_api_route_allowed( route=route, request=request, - request_data=request_body, + request_data=request_body_for_route_check, valid_token=valid_token, user_obj=user_object, ) @@ -1470,7 +1496,7 @@ def _should_check_db(key: str, last_db_access_time: LimitedSizeOrderedDict, db_c elif last_db_access_time[key][0] is not None: # check db for non-null values (for refresh operations) return True elif last_db_access_time[key][0] is None: - if current_time - last_db_access_time[key] >= db_cache_expiry: + if current_time - last_db_access_time[key][1] >= db_cache_expiry: return True return False @@ -1645,6 +1671,12 @@ async def get_user_object( include={"organization_memberships": True}, ) else: + if should_check_db: + _update_last_db_access_time( + key=db_access_time_key, + value=None, + last_db_access_time=last_db_access_time, + ) raise Exception if response.organization_memberships is not None and len(response.organization_memberships) > 0: @@ -2496,6 +2528,7 @@ def _copy_user_api_key_auth_for_cache( ) -> UserAPIKeyAuth: copied_key_obj = user_api_key_obj.model_copy() copied_key_obj.budget_reservation = None + copied_key_obj.budget_throttle_pct = None copied_key_obj.parent_otel_span = None copied_key_obj.request_route = None return copied_key_obj @@ -2941,18 +2974,16 @@ def _resolve_key_models_for_auth_check(valid_token: UserAPIKeyAuth) -> List[str] """ Expand key model sentinels before auth checks. - ``all-team-models`` means inherit the parent team's allowlist — same + ``all-team-models`` means inherit the parent team's allowlist -- same semantics as ``get_key_models`` in ``model_checks.py``. - If the key has no team_id the sentinel cannot be resolved, so the original - model list (still containing the sentinel string) is returned unchanged. - That string won't match any real model, so access is denied rather than - silently falling through to unrestricted access. + If the key has no team_id, it inherits the full proxy model list + (equivalent to an empty models field, i.e. unrestricted access). """ models = list(valid_token.models or []) if SpecialModelNames.all_team_models.value in models: if valid_token.team_id is None: - return models + return [] return list(valid_token.team_models or []) return models @@ -3430,6 +3461,24 @@ async def is_valid_fallback_model( return True +def _apply_budget_exceeded_throttle(valid_token: UserAPIKeyAuth) -> bool: + """ + Throttle an over-budget key instead of blocking it, when the key opted in + via `throttle_on_budget_exceeded` and a global percentage is configured. + + Records the percentage on the request-scoped `budget_throttle_pct` so the + rate limiter scales the key's TPM/RPM down to it; the persistent limits are + left untouched so the throttle never compounds across requests. Returns True + when the key was throttled (caller skips raising), False when it should still + be hard-blocked. + """ + pct = budget_throttle_percentage() + if pct is None or not should_throttle_budget_exceeded(valid_token): + return False + valid_token.budget_throttle_pct = pct + return True + + async def _virtual_key_max_budget_check( valid_token: UserAPIKeyAuth, proxy_logging_obj: ProxyLogging, @@ -3490,6 +3539,8 @@ async def _virtual_key_max_budget_check( # so a NaN max_budget would silently disable enforcement. Treat a # non-finite max_budget as "no configured limit" rather than as a bypass. if math.isfinite(valid_token.max_budget) and spend >= valid_token.max_budget: + if _apply_budget_exceeded_throttle(valid_token): + return # name the key in the error so operators don't have to reverse-map # spend back to a key; key_name is the masked form (last 4 chars) key_label = valid_token.key_alias or "key" @@ -4332,14 +4383,23 @@ def _model_custom_llm_provider_matches_wildcard_pattern(model: str, allowed_mode or - `model=claude-3-5-sonnet-20240620` - `allowed_model_pattern=anthropic/*` + + A model that already carries a namespace get_llm_provider did not consume + (e.g. `bedrockz/anthropic.claude-...`) is never granted here: its provider was + inferred from a fragment of the full string, so rebuilding + `{provider}/{model}` would produce `bedrock/bedrockz/...` and slip an + unrecognized namespace through a `bedrock/*` key. """ try: - model, custom_llm_provider, _, _ = get_llm_provider(model=model) + stripped_model, custom_llm_provider, _, _ = get_llm_provider(model=model) except Exception: return False + if stripped_model == model and "/" in model: + return False + return is_model_allowed_by_pattern( - model=f"{custom_llm_provider}/{model}", + model=f"{custom_llm_provider}/{stripped_model}", allowed_model_pattern=allowed_model_pattern, ) diff --git a/litellm/proxy/auth/auth_checks_organization.py b/litellm/proxy/auth/auth_checks_organization.py index 44c1d158cbe..9b9e0661084 100644 --- a/litellm/proxy/auth/auth_checks_organization.py +++ b/litellm/proxy/auth/auth_checks_organization.py @@ -2,7 +2,7 @@ Auth Checks for Organizations """ -from typing import Dict, List, Optional, Tuple +from typing import Awaitable, Callable, Dict, List, Optional, Tuple from fastapi import status @@ -170,3 +170,46 @@ def _user_is_org_admin( # User must be admin of ALL requested orgs, not just any one return all(org_id in admin_org_ids for org_id in candidate_org_ids) + + +TEAM_ORG_CONTEXT_ROUTES = frozenset({"/team/update"}) +# The RESTful update route carries the team id in the path. Match on the route +# template so the sibling /team/ routes (which share the single-segment +# shape) are not mistaken for it and don't trigger a team lookup. +PATCH_TEAM_ROUTE_TEMPLATE = "/team/{team_id}" + + +async def add_team_org_context_to_request_body( + route: str, + request_body: dict, + fetch_team_org_id: Callable[[str], Awaitable[Optional[str]]], + route_template: Optional[str] = None, +) -> dict: + """ + Return a copy of request_body with organization_id resolved from the target + team when the route identifies the team by team_id and the caller did not + pass organization_id. This lets an org admin of the team's own org reach the + org-scoped branch of the route gate (which keys off organization_id) without + the client having to send it. Returns request_body unchanged when it does + not apply, so callers that already pass organization_id and non-team routes + are untouched. + + The team_id is taken from the body for TEAM_ORG_CONTEXT_ROUTES, or from the + last path segment when ``route_template`` is the ``/team/{team_id}`` route. + """ + if request_body.get("organization_id"): + return request_body + + if route in TEAM_ORG_CONTEXT_ROUTES: + team_id: Optional[str] = request_body.get("team_id") + elif route_template == PATCH_TEAM_ROUTE_TEMPLATE: + team_id = route.rsplit("/", 1)[-1] + else: + return request_body + + if not isinstance(team_id, str) or not team_id: + return request_body + org_id = await fetch_team_org_id(team_id) + if not org_id: + return request_body + return {**request_body, "organization_id": org_id} diff --git a/litellm/proxy/auth/auth_utils.py b/litellm/proxy/auth/auth_utils.py index b1bce352784..893e09ece6e 100644 --- a/litellm/proxy/auth/auth_utils.py +++ b/litellm/proxy/auth/auth_utils.py @@ -278,6 +278,12 @@ _BANNED_REQUEST_BODY_PARAMS: Tuple[str, ...] = ( "s3_endpoint_url", "sagemaker_base_url", "deployment_url", + # NVIDIA Riva fields consumed by the audio-transcription handler + # via ``optional_params``. Banned for the same reason as the + # provider-specific entries above: a caller-supplied value retargets + # the request away from the admin's pinned configuration. + "nvcf_function_id", + "use_ssl", # SDK-only field; also rejected outright in is_request_body_safe. "model_list", # Observability credentials, hosts, and project identifiers: derived @@ -369,6 +375,16 @@ def is_request_body_safe(request_body: dict, general_settings: dict, llm_router: metadata = _coerce_metadata_to_dict(request_body.get(metadata_key)) if metadata is not None: _check_banned_params(metadata, general_settings, llm_router, model) + litellm_params = _coerce_metadata_to_dict(request_body.get("litellm_params")) + if litellm_params is not None: + litellm_params_metadata = _coerce_metadata_to_dict(litellm_params.get("metadata")) + if litellm_params_metadata is not None: + _check_banned_params( + litellm_params_metadata, + general_settings, + llm_router, + model, + ) return True @@ -959,6 +975,20 @@ def get_team_mcp_rpm_limit( return None +def get_key_tag_rpm_limit( + user_api_key_dict: UserAPIKeyAuth, +) -> Optional[dict[str, int]]: + """ + Get the per-request-tag rpm limit configured on a given api key. + + The returned dict is keyed by request tag, so each tag/group tracked on + the key gets its own independent RPM counter. + """ + if user_api_key_dict.metadata: + return user_api_key_dict.metadata.get("tag_rpm_limit") + return None + + def get_project_model_rpm_limit( user_api_key_dict: UserAPIKeyAuth, ) -> Optional[Dict[str, int]]: diff --git a/litellm/proxy/auth/budget_throttle.py b/litellm/proxy/auth/budget_throttle.py new file mode 100644 index 00000000000..19dffee462b --- /dev/null +++ b/litellm/proxy/auth/budget_throttle.py @@ -0,0 +1,56 @@ +""" +Throttle a key after it exceeds its own ``max_budget`` instead of blocking it. + +When a key opts in via ``throttle_on_budget_exceeded`` and a global +``budget_exceeded_throttle_percentage`` is configured, an over-budget key keeps +serving requests but at a reduced TPM/RPM (the configured percentage of its +configured limits). The decision (over budget + opted in) is made once during +auth; the scaling is recomputed from the key's original limits on every request +so it never compounds across requests. +""" + +import math +from typing import Optional + +import litellm +from litellm.proxy._types import UserAPIKeyAuth + + +def budget_throttle_percentage() -> Optional[float]: + """ + The global throttle percentage, or None when throttling is disabled / + misconfigured (in which case an over-budget key is hard-blocked, the safe + default). + """ + pct = litellm.budget_exceeded_throttle_percentage + if not isinstance(pct, (int, float)) or isinstance(pct, bool): + return None + if not 0 < pct <= 1: + return None + return float(pct) + + +def should_throttle_budget_exceeded(valid_token: UserAPIKeyAuth) -> bool: + """ + True when a key that exceeded its own ``max_budget`` should be throttled + rather than blocked: it opted in, a valid global percentage is set, and the + key has a TPM or RPM limit to scale down. A key with neither limit has + nothing to throttle, so it stays hard-blocked (the safe default) rather than + serving unlimited requests past its budget. + """ + if (valid_token.metadata or {}).get("throttle_on_budget_exceeded") is not True: + return False + if valid_token.tpm_limit is None and valid_token.rpm_limit is None: + return False + return budget_throttle_percentage() is not None + + +def throttled_limit(limit: Optional[int], pct: Optional[float]) -> Optional[int]: + """ + Scale a TPM/RPM limit to ``pct`` of its value, keeping a trickle of at least + 1 so a throttled key is slowed rather than fully locked out. An unset limit + or unset percentage leaves the limit unchanged. + """ + if limit is None or pct is None: + return limit + return max(1, math.floor(limit * pct)) diff --git a/litellm/proxy/auth/handle_jwt.py b/litellm/proxy/auth/handle_jwt.py index 9db9b970d88..a44318c072c 100644 --- a/litellm/proxy/auth/handle_jwt.py +++ b/litellm/proxy/auth/handle_jwt.py @@ -12,7 +12,7 @@ import fnmatch import hashlib import os import re -from typing import Any, List, Literal, Optional, Set, Tuple, Union, cast +from typing import Any, List, Literal, NoReturn, Optional, Set, Tuple, Union, cast import jwt from cryptography import x509 @@ -1196,10 +1196,22 @@ class JWTAuthManager: ) -> Tuple[Optional[str], Optional[LiteLLM_TeamTable]]: """Find and validate specific team ID from team_id_jwt_field or team_alias_jwt_field""" individual_team_id = jwt_handler.get_team_id(token=jwt_valid_token, default_value=None) + team_alias = jwt_handler.get_team_alias(token=jwt_valid_token, default_value=None) + + # `get_team_id` silently substitutes `team_id_default` for a missing + # JWT team_id claim. When the token actually carries an alias claim, + # that substitution would mask the alias-resolved team, so prefer + # alias resolution. `get_all_jwt_team_ids` ignores `team_id_default`; + # an empty result means no real JWT team_id claim is present. + if ( + team_alias + and individual_team_id is not None + and not jwt_handler.get_all_jwt_team_ids(token=jwt_valid_token) + ): + individual_team_id = None team_object: Optional[LiteLLM_TeamTable] = None - # First try to get team by team_id if individual_team_id: try: team_object = await get_team_object( @@ -1222,8 +1234,6 @@ class JWTAuthManager: ) return None, None - # If no team_id found, try to resolve via team_alias_jwt_field - team_alias = jwt_handler.get_team_alias(token=jwt_valid_token, default_value=None) if team_alias: verbose_proxy_logger.info(f"JWT Auth: Resolving team by alias: '{team_alias}'") team_object = await get_team_object_by_alias( @@ -1329,7 +1339,10 @@ class JWTAuthManager: denied_auth_enforced_pass_through_route = False if not team_ids: - if jwt_handler.litellm_jwtauth.enforce_team_based_model_access: + if ( + jwt_handler.litellm_jwtauth.enforce_team_based_model_access + and not jwt_handler.litellm_jwtauth.fallback_to_db_teams + ): raise HTTPException( status_code=403, detail="No teams found in token. `enforce_team_based_model_access` is set to True. Token must belong to a team.", @@ -1571,6 +1584,7 @@ class JWTAuthManager: def get_team_id_from_header( request_headers: Optional[dict], allowed_team_ids: Set[str], + fallback_to_db_teams: bool = False, ) -> Optional[str]: """ Extract team_id from x-litellm-team-id header if present. @@ -1579,6 +1593,10 @@ class JWTAuthManager: Args: request_headers: Dictionary of request headers allowed_team_ids: Set of team IDs the user is allowed to access (from JWT) + fallback_to_db_teams: When True and the JWT carries no team claims + (allowed_team_ids is empty), the header value is returned + provisionally and validated against DB memberships later in + auth_builder instead of being rejected here. Returns: The team_id from header if valid, None otherwise @@ -1596,8 +1614,8 @@ class JWTAuthManager: if not header_team_id: return None - # Validate that the team_id is in the allowed teams - if header_team_id not in allowed_team_ids: + defer_to_db_membership = fallback_to_db_teams and not allowed_team_ids + if not defer_to_db_membership and header_team_id not in allowed_team_ids: raise HTTPException( status_code=403, detail=f"Team '{header_team_id}' from x-litellm-team-id header is not in your JWT's allowed teams. Allowed teams: {list(allowed_team_ids)}", @@ -1694,11 +1712,20 @@ class JWTAuthManager: ttl=get_management_object_ttl(user_api_key_cache), ) - # Sync team memberships - jwt_team_ids = set(jwt_handler.get_team_ids_from_jwt(jwt_valid_token)) + # Sync team memberships. With fallback_to_db_teams on, read both plural and + # singular claim shapes so a singular-only IdP token (e.g. Okta/Auth0) is + # not mistaken for claimless and left with stale DB memberships the fallback + # could later attribute. With the flag off, keep the upstream plural-only + # reconciliation so existing deployments are unchanged. + jwt_team_ids = set( + jwt_handler.get_all_jwt_team_ids(jwt_valid_token) + if jwt_handler.litellm_jwtauth.fallback_to_db_teams + else jwt_handler.get_team_ids_from_jwt(jwt_valid_token) + ) existing_teams = set(user_object.teams or []) teams_to_add = jwt_team_ids - existing_teams - teams_to_remove = existing_teams - jwt_team_ids + preserve_db_teams_without_claims = jwt_handler.litellm_jwtauth.fallback_to_db_teams and not jwt_team_ids + teams_to_remove = set() if preserve_db_teams_without_claims else existing_teams - jwt_team_ids if teams_to_add or teams_to_remove: from litellm.proxy.management_endpoints.scim.scim_v2 import ( patch_team_membership, @@ -1818,6 +1845,155 @@ class JWTAuthManager: ) return None, None, None + @staticmethod + async def _resolve_db_team_fallback( + user_object: LiteLLM_UserTable | None, + user_id: str | None, + requested_model: str | None, + route: str, + jwt_handler: JWTHandler, + enforce_team_based_model_access: bool, + team_id_upsert: bool, + prisma_client: PrismaClient | None, + user_api_key_cache: UserApiKeyCache, + parent_otel_span: Span | None, + proxy_logging_obj: ProxyLogging, + request_method: str | None = None, + ) -> tuple[str | None, LiteLLM_TeamTable | None, LiteLLM_TeamMembership | None]: + """ + Resolve a team for a user whose JWT carries no team claims by selecting + the first DB team membership that loads successfully and, when a model is + requested, can access that model — mirroring the per-team model-access + check the claim-based path enforces, so a team's `models` restriction is + not bypassed by the fallback. + + The same `team_allowed_routes` gate the claim-based path applies is + enforced here too, so a DB-selected team cannot reach a route the JWT + config excludes for team-role callers. Auth-enforced passthrough routes + are exempt from that gate by design (they are governed by the team's + `allowed_passthrough_routes`, re-checked by the caller). + + The resolved team's membership row is loaded too (when user_id is set) so + per-team membership budget limits are enforced on the fallback path the + same as on the claim-based path. + + Raises HTTP 403 when the user has no usable DB team membership and + `enforce_team_based_model_access` is set; otherwise returns (None, None, None). + """ + from litellm.proxy.proxy_server import llm_router + + user_team_ids = user_object.teams if user_object else [] + team_route_allowed = JWTAuthManager._is_team_route_allowed( + route=route, request_method=request_method, jwt_handler=jwt_handler + ) + any_team_resolved = False + for candidate_team_id in user_team_ids: + try: + team_object = await get_team_object( + team_id=candidate_team_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + parent_otel_span=parent_otel_span, + proxy_logging_obj=proxy_logging_obj, + team_id_upsert=team_id_upsert, + ) + except HTTPException: + continue + any_team_resolved = True + if requested_model: + try: + await can_team_access_model( + model=requested_model, + team_object=team_object, + llm_router=llm_router, + team_model_aliases=None, + ) + except ProxyException: + continue + if not team_route_allowed: + continue + verbose_proxy_logger.debug( + "JWT DB team fallback: resolved team_id=%s from user DB membership", + candidate_team_id, + ) + if user_id: + return ( + candidate_team_id, + team_object, + await get_team_membership( + user_id=user_id, + team_id=candidate_team_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + parent_otel_span=parent_otel_span, + proxy_logging_obj=proxy_logging_obj, + ), + ) + return candidate_team_id, team_object, None + + if enforce_team_based_model_access: + if requested_model and any_team_resolved: + raise HTTPException( + status_code=403, + detail=( + f"No team you are a member of has access to the requested " + f"model: {requested_model}. Check `/models` to see the models " + f"available to you." + ), + ) + raise HTTPException( + status_code=403, + detail=("User is not a member of any team. Add the user to a team via the LiteLLM UI or API."), + ) + return None, None, None + + @staticmethod + def _is_team_route_allowed( + route: str, + request_method: str | None, + jwt_handler: JWTHandler, + ) -> bool: + """ + Whether a team-role caller may reach `route` per the JWT config's + `team_allowed_routes`. Auth-enforced passthrough routes are exempt + here; their team's `allowed_passthrough_routes` gate runs separately. + """ + normalized_method = request_method.upper() if isinstance(request_method, str) else None + return RouteChecks.is_auth_enforced_pass_through_route( + route=route, method=normalized_method + ) or allowed_routes_check( + user_role=LitellmUserRoles.TEAM, + user_route=route, + litellm_proxy_roles=jwt_handler.litellm_jwtauth, + ) + + @staticmethod + def _raise_header_team_membership_denial(team_id: str) -> NoReturn: + """ + The single denial shape for a provisional x-litellm-team-id header, + raised identically for nonexistent teams and for teams the user is not + a member of, so the response does not reveal whether a team id exists. + """ + raise HTTPException( + status_code=403, + detail=(f"Team '{team_id}' (from x-litellm-team-id header) is not in your team memberships."), + ) + + @staticmethod + def _validate_header_team_in_db_membership( + team_id: str, + user_object: LiteLLM_UserTable | None, + ) -> None: + """ + A provisional team_id from the x-litellm-team-id header (accepted without + JWT-team validation when the JWT carries no team claims) must exist in the + user's DB team memberships before it becomes request context. + """ + user_team_ids = user_object.teams if user_object else [] + if team_id in user_team_ids: + return + JWTAuthManager._raise_header_team_membership_denial(team_id) + @staticmethod async def auth_builder( api_key: str, @@ -1911,24 +2087,49 @@ class JWTAuthManager: ## Check if team_id is specified via x-litellm-team-id header all_team_ids = JWTAuthManager.get_all_team_ids(jwt_handler, jwt_valid_token) specific_team_id = jwt_handler.get_team_id(token=jwt_valid_token, default_value=None) - if specific_team_id: + + # The DB fallback only applies when the token carries no team identity at + # all. `get_all_jwt_team_ids` ignores `team_id_default` so a configured + # default does not hide a claimless token, `get_team_alias` covers + # alias-only tokens so the alias still resolves via + # `find_and_validate_specific_team_id`, and `team_id is None` excludes + # the RBAC team-role path (which already set `team_id`); otherwise a + # provisional x-litellm-team-id header could override an RBAC-asserted team. + db_team_fallback = ( + jwt_handler.litellm_jwtauth.fallback_to_db_teams + and not jwt_handler.get_all_jwt_team_ids(token=jwt_valid_token) + and not jwt_handler.get_team_alias(token=jwt_valid_token, default_value=None) + and team_id is None + ) + if specific_team_id and not db_team_fallback: all_team_ids.add(specific_team_id) header_team_id = JWTAuthManager.get_team_id_from_header( request_headers=request_headers, allowed_team_ids=all_team_ids, + fallback_to_db_teams=db_team_fallback, ) if header_team_id: team_id = header_team_id - team_object = await get_team_object( - team_id=team_id, - prisma_client=prisma_client, - user_api_key_cache=user_api_key_cache, - parent_otel_span=parent_otel_span, - proxy_logging_obj=proxy_logging_obj, - team_id_upsert=jwt_handler.litellm_jwtauth.team_id_upsert, - ) - elif not team_id: + # A provisional header team (accepted only because the JWT carries no + # team claims) is validated against DB membership further down; never + # upsert it here or an attacker-supplied x-litellm-team-id would create + # an orphaned team row before that check runs. A genuine membership team + # already exists, so suppressing the upsert in that case costs nothing. + try: + team_object = await get_team_object( + team_id=team_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + parent_otel_span=parent_otel_span, + proxy_logging_obj=proxy_logging_obj, + team_id_upsert=(jwt_handler.litellm_jwtauth.team_id_upsert and not db_team_fallback), + ) + except HTTPException: + if not db_team_fallback: + raise + JWTAuthManager._raise_header_team_membership_denial(team_id) + elif not team_id and not db_team_fallback: ## SPECIFIC TEAM ID ( team_id, @@ -2020,8 +2221,36 @@ class JWTAuthManager: user_api_key_cache=user_api_key_cache, ) - # If JWT did not resolve team_id, attempt single-team DB fallback. - if team_id is None: + # If JWT did not resolve team_id, attempt a team fallback. + if team_id is None and db_team_fallback: + ( + team_id, + team_object, + team_membership_object, + ) = await JWTAuthManager._resolve_db_team_fallback( + user_object=user_object, + user_id=user_id, + requested_model=request_data.get("model"), + route=route, + jwt_handler=jwt_handler, + enforce_team_based_model_access=jwt_handler.litellm_jwtauth.enforce_team_based_model_access, + team_id_upsert=jwt_handler.litellm_jwtauth.team_id_upsert, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + parent_otel_span=parent_otel_span, + proxy_logging_obj=proxy_logging_obj, + request_method=request_method, + ) + # The earlier passthrough gate ran when team_id was None; re-check + # against the DB-resolved team so a fallback-selected team must also + # pass the auth-enforced passthrough allowlist. + if team_id and not JWTAuthManager._team_has_passthrough_route_access( + team_object=team_object, + route=route, + request_method=request_method, + ): + JWTAuthManager._raise_team_passthrough_route_denial(route=route) + elif team_id is None: ( team_id, team_object, @@ -2035,6 +2264,22 @@ class JWTAuthManager: proxy_logging_obj=proxy_logging_obj, team_id_upsert=jwt_handler.litellm_jwtauth.team_id_upsert, ) + elif db_team_fallback and team_id == header_team_id: + JWTAuthManager._validate_header_team_in_db_membership( + team_id=team_id, + user_object=user_object, + ) + if not JWTAuthManager._is_team_route_allowed( + route=route, + request_method=request_method, + jwt_handler=jwt_handler, + ): + raise HTTPException( + status_code=403, + detail=( + f"Team '{team_id}' (from x-litellm-team-id header) is not allowed to access route '{route}'." + ), + ) ## MAP USER TO TEAMS await JWTAuthManager.map_user_to_teams( diff --git a/litellm/proxy/auth/model_checks.py b/litellm/proxy/auth/model_checks.py index 0f3b8b5a412..7d31ce1b909 100644 --- a/litellm/proxy/auth/model_checks.py +++ b/litellm/proxy/auth/model_checks.py @@ -111,7 +111,7 @@ def get_key_models( all_models: List[str] = [] if len(user_api_key_dict.models) > 0: all_models = list(user_api_key_dict.models) # copy to avoid mutating cached objects - if SpecialModelNames.all_team_models.value in all_models and user_api_key_dict.team_id is not None: + if SpecialModelNames.all_team_models.value in all_models: all_models = list(user_api_key_dict.team_models) if SpecialModelNames.all_team_models.value in all_models: all_models = [model for model in all_models if model != SpecialModelNames.all_team_models.value] diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index 73f9def822b..2613510bd0c 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -11,8 +11,10 @@ import asyncio import fnmatch import re import secrets + +import orjson from datetime import datetime, timezone -from typing import Any, Dict, Iterator, NamedTuple, List, Optional, Tuple, Union, cast +from typing import Any, Dict, Iterator, NamedTuple, List, Optional, Protocol, Tuple, Union, cast import fastapi from fastapi import HTTPException, Request, WebSocket, status @@ -30,6 +32,7 @@ from litellm.proxy._types import * from litellm.proxy.auth.auth_checks import ( ExperimentalUIJWTToken, _cache_key_object, + _can_object_call_model, _check_end_user_budget, _delete_cache_key_object, _get_user_role, @@ -73,6 +76,7 @@ from litellm.proxy.common_utils.http_parsing_utils import ( _read_request_body, _safe_get_request_headers, _safe_get_request_query_params, + _safe_set_request_parsed_body, populate_request_with_path_params, ) from litellm.proxy.common_utils.realtime_utils import _realtime_request_body @@ -171,6 +175,87 @@ def _get_model_names_for_budget_checks( return model +class _KeyModelBudgetLimiter(Protocol): + async def is_key_within_model_budget(self, user_api_key_dict: UserAPIKeyAuth, model: str) -> bool: ... + + async def get_fallback_model_within_budget( + self, user_api_key_dict: UserAPIKeyAuth, model: str + ) -> Optional[str]: ... + + +async def _check_key_model_budget_with_fallback( + valid_token: UserAPIKeyAuth, + model_max_budget_limiter: _KeyModelBudgetLimiter, + model_name: str, + request_data: dict, + request: Request, + llm_model_list: Optional[list] = None, + llm_router: Optional[litellm.Router] = None, +) -> None: + """ + Enforce the key's per-model budget for `model_name`. If exceeded and the + key has a `budget_fallbacks` chain configured for `model_name`, reroute + the request to the first fallback model still within its own budget + instead of rejecting the request. + + The selected fallback is validated against the key's model-access + allowlist and the team's model restrictions so that budget_fallbacks + cannot bypass model authorization. The rewrite is persisted to the + parsed-body cache, Starlette's JSON cache (``request._json``), and + path parameters so that downstream handlers see the final model + regardless of whether they consume ``_read_request_body()``, + ``request.json()``, or the path ``model`` parameter. + + Fallback is only attempted when ``model_name`` matches the top-level + ``request_data["model"]``; models extracted from nested fields + (``session.model``, ``completion.model``, etc.) are not rewritable + and raise immediately. + + Raises: + BudgetExceededError: if `model_name` is over budget and no configured + fallback is within budget either (or the fallback is not authorized). + """ + try: + await model_max_budget_limiter.is_key_within_model_budget( + user_api_key_dict=valid_token, + model=model_name, + ) + except litellm.BudgetExceededError as e: + if request_data.get("model") != model_name: + raise e + fallback_model = await model_max_budget_limiter.get_fallback_model_within_budget( + user_api_key_dict=valid_token, + model=model_name, + ) + if fallback_model is None: + raise e + try: + await can_key_call_model( + model=fallback_model, + llm_model_list=llm_model_list, + valid_token=valid_token, + llm_router=llm_router, + ) + if valid_token.team_models: + _can_object_call_model( + model=fallback_model, + llm_router=llm_router, + models=valid_token.team_models, + team_model_aliases=valid_token.team_model_aliases, + team_id=valid_token.team_id, + object_type="team", + ) + except ProxyException: + raise e + request_data["model"] = fallback_model + _safe_set_request_parsed_body(request=request, parsed_body=request_data) + request._json = request_data # type: ignore[attr-defined] + request._body = orjson.dumps(request_data) # type: ignore[attr-defined] + path_params = request.scope.get("path_params") + if isinstance(path_params, dict) and "model" in path_params: + path_params["model"] = fallback_model + + def _get_bearer_token_or_received_api_key(api_key: str) -> str: if api_key.startswith("Bearer "): # ensure Bearer token passed in api_key = api_key.replace("Bearer ", "") # extract the token @@ -1779,11 +1864,26 @@ async def _user_api_key_auth_builder( ): ## GET THE SPEND FOR THIS MODEL for model_name in current_models: - await model_max_budget_limiter.is_key_within_model_budget( - user_api_key_dict=valid_token, - model=model_name, + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=model_max_budget_limiter, + model_name=model_name, + request_data=request_data, + request=request, + llm_model_list=llm_model_list, + llm_router=llm_router, ) + # Recompute after a potential budget-fallback rewrite so + # the end-user check below validates the final model + current_model = _get_model_from_request_context( + request_data=request_data, + route=route, + request=request, + llm_router=llm_router, + ) + current_models = _get_model_names_for_budget_checks(model=current_model) + # Check 5b. End-user model max budget end_user_mmb = valid_token.end_user_model_max_budget if ( @@ -1904,6 +2004,11 @@ async def _user_api_key_auth_builder( valid_token_dict = valid_token.model_dump(exclude_none=True) valid_token_dict.pop("token", None) + # budget_throttle_pct is excluded from model_dump (it must not leak + # into serialized responses), so carry the request-scoped decision + # forward by hand to the auth object the rate limiter receives. + if valid_token.budget_throttle_pct is not None: + valid_token_dict["budget_throttle_pct"] = valid_token.budget_throttle_pct if _end_user_object is not None: valid_token_dict.update(end_user_params) @@ -2834,11 +2939,26 @@ async def _run_post_custom_auth_checks( and valid_token.token is not None ): for model_name in current_models: - await model_max_budget_limiter.is_key_within_model_budget( - user_api_key_dict=valid_token, - model=model_name, + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=model_max_budget_limiter, + model_name=model_name, + request_data=request_data, + request=request, + llm_model_list=llm_model_list, + llm_router=llm_router, ) + # Recompute after a potential budget-fallback rewrite so + # the end-user check below validates the final model + current_model = _get_model_from_request_context( + request_data=request_data, + route=route, + request=request, + llm_router=llm_router, + ) + current_models = _get_model_names_for_budget_checks(model=current_model) + # 4. Check end-user model_max_budget end_user_mmb = valid_token.end_user_model_max_budget if ( diff --git a/litellm/proxy/batches_endpoints/endpoints.py b/litellm/proxy/batches_endpoints/endpoints.py index 02e4d9c170e..fffa0bf86d2 100644 --- a/litellm/proxy/batches_endpoints/endpoints.py +++ b/litellm/proxy/batches_endpoints/endpoints.py @@ -625,7 +625,7 @@ async def list_batches( route_type="alist_batches", ) - # Try to use managed objects table for listing batches (returns encoded IDs) + # Try to use managed objects table for listing batches (returns encoded IDs). managed_files_obj = proxy_logging_obj.get_proxy_hook("managed_files") if managed_files_obj is not None and hasattr(managed_files_obj, "list_user_batches"): verbose_proxy_logger.debug("Using managed objects table for batch listing") diff --git a/litellm/proxy/client/README.md b/litellm/proxy/client/README.md index f33367a96c2..6b28f43ac73 100644 --- a/litellm/proxy/client/README.md +++ b/litellm/proxy/client/README.md @@ -336,11 +336,12 @@ sequenceDiagram ### Authentication Commands -The CLI provides three authentication commands: +The CLI provides these authentication commands: - **`lite login`** - Start SSO authentication flow - **`lite logout`** - Clear stored authentication token - **`lite whoami`** - Show current authentication status +- **`lite auth print-token`** - Print the cached token (used as Claude Code's `apiKeyHelper`); fails once the token has expired ### Authentication Flow Steps @@ -376,7 +377,7 @@ Authentication tokens are stored in `~/.litellm/token.json` with restricted file } ``` -The stored credential is a short-lived, per-session agent token, not a managed virtual key. It is scoped to the user and team you logged in as and inherits their models and budgets; spend is tracked against the shared team and user budgets rather than a separate per-session cap, so multiple logins or several concurrent agents all draw down the same allowance. It is short-lived by design (default 24h, configurable via `LITELLM_CLI_JWT_EXPIRATION_HOURS`); re-run `lite login` to refresh it and pick up your latest team and user settings. It is accepted on a default deployment without `EXPERIMENTAL_UI_LOGIN`, does not appear in the Keys UI, and cannot be rotated or revoked mid-session. For a long-lived, rotatable, Keys-UI-visible credential, create a dedicated virtual key in the dashboard and pass it via `--api-key` or `LITELLM_PROXY_API_KEY`. +The stored credential is a short-lived, per-session agent token, not a managed virtual key. It is scoped to the user and team you logged in as and inherits their models and budgets; spend is tracked against the shared team and user budgets rather than a separate per-session cap, so multiple logins or several concurrent agents all draw down the same allowance. It is short-lived by design (default 24h, configurable via `LITELLM_CLI_JWT_EXPIRATION_HOURS`); re-run `lite login` to refresh it and pick up your latest team and user settings. `lite auth print-token` (usable as Claude Code's `apiKeyHelper`) prints it while fresh and fails once it expires -- there is no silent renewal. It is accepted on a default deployment without `EXPERIMENTAL_UI_LOGIN`, does not appear in the Keys UI, and cannot be rotated or revoked mid-session. For a long-lived, rotatable, Keys-UI-visible credential, create a dedicated virtual key in the dashboard and pass it via `--api-key` or `LITELLM_PROXY_API_KEY`. ### Usage diff --git a/litellm/proxy/client/cli/README.md b/litellm/proxy/client/cli/README.md index f53e7db4e6b..ce13a906a36 100644 --- a/litellm/proxy/client/cli/README.md +++ b/litellm/proxy/client/cli/README.md @@ -469,7 +469,7 @@ To pin the model, pass the agent's own model flag (for example `lite claude --mo The token minted by `lite login` is a short-lived, per-session agent credential, not a managed virtual key. It is scoped to the user and team you authenticated as, inherits that user's and team's models and budgets, and is enforced on the proxy exactly like a virtual key on the same team (guardrails, routing, logging, spend). Spend is tracked against the shared team and user budgets, so running several agents (or logging in more than once) does not hand each session its own separate budget; they all draw down the same team/user allowance. There is no separate per-session cap, so sustained agent use is not capped at a small chat-session limit. -The credential is short-lived by design (default 24h, configurable via `LITELLM_CLI_JWT_EXPIRATION_HOURS`); run `lite login` again to refresh it, which also re-reads your latest team and user settings. It does not appear in the Keys UI and cannot be rotated or revoked mid-session. `lite claude`, `lite codex`, and `lite opencode` work with it on a default deployment; `EXPERIMENTAL_UI_LOGIN` is not required. If you need a long-lived, rotatable key that shows up in the Keys UI, create a dedicated virtual key in the dashboard and pass it via `--api-key` or `LITELLM_PROXY_API_KEY` instead. +The credential is short-lived by design (default 24h, configurable via `LITELLM_CLI_JWT_EXPIRATION_HOURS`); run `lite login` again to refresh it, which also re-reads your latest team and user settings. It does not appear in the Keys UI and cannot be rotated or revoked mid-session. `lite auth print-token` (usable as Claude Code's `apiKeyHelper`) prints it while it's still fresh and fails once it expires -- there is no silent renewal, so a long-running session needs a fresh `lite login` once a day. `lite claude`, `lite codex`, and `lite opencode` work with it on a default deployment; `EXPERIMENTAL_UI_LOGIN` is not required. If you need a long-lived, rotatable key that shows up in the Keys UI, create a dedicated virtual key in the dashboard and pass it via `--api-key` or `LITELLM_PROXY_API_KEY` instead. ## Environment Variables diff --git a/litellm/proxy/client/cli/commands/auth.py b/litellm/proxy/client/cli/commands/auth.py index b258664ee16..4eda6817252 100644 --- a/litellm/proxy/client/cli/commands/auth.py +++ b/litellm/proxy/client/cli/commands/auth.py @@ -13,6 +13,7 @@ from rich.console import Console from rich.table import Table from litellm.constants import CLI_JWT_EXPIRATION_HOURS +from litellm.litellm_core_utils.cli_token_utils import is_cli_token_fresh # Token storage utilities @@ -593,6 +594,44 @@ def logout(): click.echo("✅ Logged out successfully. Authentication token cleared.") +@click.command(name="print-token") +@click.pass_context +def print_token(ctx: click.Context): + """Print a valid API token for this proxy. + + Designed to be used as Claude Code's `apiKeyHelper` + (https://docs.claude.com/en/docs/claude-code/settings): stdout must + contain only the token, so all diagnostics go to stderr. The token + expires after `LITELLM_CLI_JWT_EXPIRATION_HOURS` (default 24h); once + expired, run `lite login` again. + """ + token_data = load_token() + if not token_data: + click.echo("Not authenticated. Run 'lite login'.", err=True) + sys.exit(1) + + # apiKeyHelper is invoked bare (no --base-url), so unless the caller + # explicitly pointed us at a server, trust whichever one `lite login` + # actually issued this token for -- that's the whole point of not + # needing a wrapper command. + if ctx.obj.get("base_url_explicit"): + base_url = ctx.obj["base_url"] + if token_data.get("base_url") != base_url.rstrip("/"): + click.echo("Not authenticated for this server. Run 'lite login'.", err=True) + sys.exit(1) + + if not is_cli_token_fresh(token_data): + click.echo("Token expired. Run 'lite login' again.", err=True) + sys.exit(1) + + api_key = token_data.get("key") + if not api_key: + click.echo("No token available. Run 'lite login'.", err=True) + sys.exit(1) + + click.echo(api_key) + + @click.command(name="whoami") def whoami(): """Show current authentication status""" @@ -616,8 +655,16 @@ def whoami(): click.echo(f"⚠️ Warning: Token is more than {CLI_JWT_EXPIRATION_HOURS} hours old and may have expired.") +@click.group(name="auth") +def auth_group(): + """Manage CLI authentication (apiKeyHelper support, etc.)""" + + +auth_group.add_command(print_token) + + # Export functions for use by other CLI commands -__all__ = ["login", "logout", "whoami", "prompt_team_selection"] +__all__ = ["login", "logout", "print_token", "auth_group", "whoami", "prompt_team_selection"] # Export individual commands instead of grouping them # login, logout, and whoami will be added as top-level commands diff --git a/litellm/proxy/client/cli/main.py b/litellm/proxy/client/cli/main.py index 512e6a44e48..c48026be688 100644 --- a/litellm/proxy/client/cli/main.py +++ b/litellm/proxy/client/cli/main.py @@ -8,7 +8,7 @@ from litellm._version import version as litellm_version from litellm.proxy.client.health import HealthManagementClient from .commands.agents import agent_commands -from .commands.auth import get_stored_api_key, login, logout, whoami +from .commands.auth import auth_group, get_stored_api_key, login, logout, whoami from .commands.chat import chat from .commands.credentials import credentials from .commands.db import db @@ -77,6 +77,10 @@ def cli(ctx: click.Context, base_url: str, api_key: Optional[str]) -> None: """LiteLLM Proxy CLI - Manage your LiteLLM proxy server""" ctx.ensure_object(dict) + # Normalize once here so every downstream command (login, agents, http, ...) can safely + # do f"{base_url}/some/path" without producing a double slash. + base_url = base_url.rstrip("/") + # If no API key provided via flag or environment variable, try to load from saved token. # Pass base_url so we only use the stored key when it was issued for this server. if api_key is None: @@ -84,6 +88,12 @@ def cli(ctx: click.Context, base_url: str, api_key: Optional[str]) -> None: ctx.obj["base_url"] = base_url ctx.obj["api_key"] = api_key + # `--base-url` defaults to localhost:4000 for local dev convenience, but + # apiKeyHelper is invoked bare (no flags) -- commands that must work + # unattended (print-token) need to tell "user didn't say" apart from + # "user said localhost:4000 on purpose" so they can fall back to + # whatever server the stored token was actually issued for. + ctx.obj["base_url_explicit"] = ctx.get_parameter_source("base_url") != click.core.ParameterSource.DEFAULT # If no subcommand was invoked, start interactive mode if ctx.invoked_subcommand is None: @@ -103,6 +113,8 @@ cli.add_command(logout) cli.add_command(whoami) # Add the db command group cli.add_command(db) +# Add the auth command group (e.g. `lite auth print-token`, used as Claude Code's apiKeyHelper) +cli.add_command(auth_group, name="auth") # Add the models command group cli.add_command(models) # Add the credentials command group diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index 97f7d51970c..02bb66388ca 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -52,6 +52,7 @@ from litellm.proxy.dd_span_tagger import DDSpanTagger from litellm.proxy.route_llm_request import route_request from litellm.proxy.utils import ProxyLogging from litellm.router import Router +from litellm.router_utils.add_retry_fallback_headers import get_hidden_params_dict from litellm.types.guardrails import GuardrailEventHooks from litellm.types.router import RouterRateLimitError from litellm.types.utils import ServerToolUse @@ -90,7 +91,9 @@ _CLIENT_DISCONNECTED_ERROR_INFORMATION: StandardLoggingPayloadErrorInformation = } -def _apply_client_disconnect_metadata(target_metadata: dict[str, object]) -> None: +def _apply_client_disconnect_metadata(target_metadata: Optional[dict[str, object]]) -> None: + if target_metadata is None: + return target_metadata["client_disconnected"] = True target_metadata["error_information"] = dict(_CLIENT_DISCONNECTED_ERROR_INFORMATION) @@ -113,12 +116,33 @@ async def _record_streaming_client_disconnect_if_needed( logging_obj = request_data.get("litellm_logging_obj") if logging_obj is not None: litellm_params = logging_obj.model_call_details.setdefault("litellm_params", {}) - _apply_client_disconnect_metadata(litellm_params.setdefault("metadata", {})) - _apply_client_disconnect_metadata(logging_obj.model_call_details.setdefault("metadata", {})) + _lp_metadata = litellm_params.get("metadata") + if _lp_metadata is None: + _lp_metadata = {} + litellm_params["metadata"] = _lp_metadata + _apply_client_disconnect_metadata(_lp_metadata) - _apply_client_disconnect_metadata(request_data.setdefault("metadata", {})) - litellm_params = request_data.setdefault("litellm_params", {}) - _apply_client_disconnect_metadata(litellm_params.setdefault("metadata", {})) + _mcd_metadata = logging_obj.model_call_details.get("metadata") + if _mcd_metadata is None: + _mcd_metadata = {} + logging_obj.model_call_details["metadata"] = _mcd_metadata + _apply_client_disconnect_metadata(_mcd_metadata) + + _rd_metadata = request_data.get("metadata") + if _rd_metadata is None: + _rd_metadata = {} + request_data["metadata"] = _rd_metadata + _apply_client_disconnect_metadata(_rd_metadata) + + _rd_litellm_params = request_data.get("litellm_params") + if _rd_litellm_params is None: + _rd_litellm_params = {} + request_data["litellm_params"] = _rd_litellm_params + _rd_lp_metadata = _rd_litellm_params.get("metadata") + if _rd_lp_metadata is None: + _rd_lp_metadata = {} + _rd_litellm_params["metadata"] = _rd_lp_metadata + _apply_client_disconnect_metadata(_rd_lp_metadata) verbose_proxy_logger.debug( "Recorded streaming client disconnect with error_code=499 for litellm_call_id=%s", @@ -600,7 +624,7 @@ def _override_openai_response_model( if not requested_model: return - hidden_params = getattr(response_obj, "_hidden_params", {}) or {} + hidden_params = get_hidden_params_dict(response_obj) if isinstance(hidden_params, dict): # Check if a fallback occurred - if so, preserve the actual model used fallback_headers = hidden_params.get("additional_headers", {}) or {} @@ -896,7 +920,7 @@ class ProxyBaseLLMRequestProcessing: (e.g. Google native :generateContent) instead of base_process_llm_request. """ if isinstance(response, dict): - hidden_params = response.get("_hidden_params") or {} + hidden_params = get_hidden_params_dict(response) else: hidden_params = getattr(response, "_hidden_params", None) or {} if not isinstance(hidden_params, dict): @@ -1174,6 +1198,120 @@ class ProxyBaseLLMRequestProcessing: return self.data, logging_obj + async def _pre_call_with_fallbacks( + self, + request: Request, + general_settings: dict, + proxy_logging_obj: ProxyLogging, + user_api_key_dict: UserAPIKeyAuth, + version: Optional[str], + proxy_config: ProxyConfig, + user_model: Optional[str], + user_temperature: Optional[float], + user_request_timeout: Optional[float], + user_max_tokens: Optional[int], + user_api_base: Optional[str], + model: Optional[str], + route_type: str, + llm_router: Optional[Router], + ) -> tuple[dict, LiteLLMLoggingObj]: + from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError + + try: + return await self.common_processing_pre_call_logic( + request=request, + general_settings=general_settings, + proxy_logging_obj=proxy_logging_obj, + user_api_key_dict=user_api_key_dict, + version=version, + proxy_config=proxy_config, + user_model=user_model, + user_temperature=user_temperature, + user_request_timeout=user_request_timeout, + user_max_tokens=user_max_tokens, + user_api_base=user_api_base, + model=model, + route_type=route_type, + llm_router=llm_router, + ) + except ProxyRateLimitError as original_exc: + original_model = self.data.get("model") + if not original_model or not llm_router or self.data.get("disable_fallbacks"): + raise + + fallback_models = self._resolve_fallback_models( + model=original_model, + llm_router=llm_router, + user_api_key_dict=user_api_key_dict, + ) + if not fallback_models: + raise + + verbose_proxy_logger.info( + "Local rate limit hit for model=%s, attempting fallbacks: %s", + original_model, + fallback_models, + ) + + try: + for fallback_model in fallback_models: + if fallback_model == original_model: + continue + self.data["model"] = fallback_model + try: + return await self.common_processing_pre_call_logic( + request=request, + general_settings=general_settings, + proxy_logging_obj=proxy_logging_obj, + user_api_key_dict=user_api_key_dict, + version=version, + proxy_config=proxy_config, + user_model=user_model, + user_temperature=user_temperature, + user_request_timeout=user_request_timeout, + user_max_tokens=user_max_tokens, + user_api_base=user_api_base, + model=fallback_model, + route_type=route_type, + llm_router=llm_router, + ) + except ProxyRateLimitError: + continue + except BaseException: + self.data["model"] = original_model + raise + + self.data["model"] = original_model + raise original_exc + + def _resolve_fallback_models( + self, + model: str, + llm_router: Router, + user_api_key_dict: UserAPIKeyAuth, + ) -> Optional[list]: + from litellm.router_utils.fallback_event_handlers import get_fallback_model_group + + fallbacks = None + + key_router_settings = user_api_key_dict.router_settings + if isinstance(key_router_settings, dict) and "fallbacks" in key_router_settings: + fallbacks = key_router_settings["fallbacks"] + + if fallbacks is None: + fallbacks = llm_router.fallbacks + + if not fallbacks: + return None + + fallback_model_group, generic_fallback_idx = get_fallback_model_group( + fallbacks=fallbacks, + model_group=model, + ) + if fallback_model_group is None and generic_fallback_idx is not None: + fallback_model_group = fallbacks[generic_fallback_idx]["*"] + return fallback_model_group + @staticmethod def _get_model_id_from_response(hidden_params: dict, data: dict) -> str: """Extract model_id from hidden_params with fallback to litellm_metadata.""" @@ -1349,7 +1487,7 @@ class ProxyBaseLLMRequestProcessing: "Ensure common_processing_pre_call_logic was called before using this parameter." ) else: - self.data, logging_obj = await self.common_processing_pre_call_logic( + self.data, logging_obj = await self._pre_call_with_fallbacks( request=request, general_settings=general_settings, proxy_logging_obj=proxy_logging_obj, @@ -1429,7 +1567,7 @@ class ProxyBaseLLMRequestProcessing: _exception_raised = False try: - hidden_params = getattr(response, "_hidden_params", {}) or {} + hidden_params = get_hidden_params_dict(response) model_id = self._get_model_id_from_response(hidden_params, self.data) cache_key, api_base, response_cost = ( @@ -1704,7 +1842,7 @@ class ProxyBaseLLMRequestProcessing: log_context=f"litellm_call_id={logging_obj.litellm_call_id}", ) - hidden_params = getattr(response, "_hidden_params", {}) or {} # get any updated response headers + hidden_params = get_hidden_params_dict(response) # get any updated response headers additional_headers = hidden_params.get("additional_headers", {}) or {} recover_response_cost = not response_cost and hidden_params.get("response_cost") is None @@ -1732,6 +1870,9 @@ class ProxyBaseLLMRequestProcessing: ) ) + if isinstance(response, dict): + response.pop("_hidden_params", None) + # Call response headers hook for non-streaming success callback_headers = await proxy_logging_obj.post_call_response_headers_hook( data=self.data, diff --git a/litellm/proxy/common_utils/cache_pydantic_utils.py b/litellm/proxy/common_utils/cache_pydantic_utils.py index 25d33a0aa52..f57f6a299ae 100644 --- a/litellm/proxy/common_utils/cache_pydantic_utils.py +++ b/litellm/proxy/common_utils/cache_pydantic_utils.py @@ -42,7 +42,7 @@ class CacheCodec: Encode a value for DualCache / Redis (``json.dumps``-safe). If ``model_type`` is set, the payload is validated with that model, then - ``model_dump(mode="json", exclude_none=True)`` — symmetric with ``deserialize``. + ``model_dump(mode="json")`` — symmetric with ``deserialize``. If the value is already an instance of ``model_type`` (or a subclass), ``model_validate`` is skipped to avoid an unnecessary Pydantic copy — the @@ -54,12 +54,12 @@ class CacheCodec: if model_type is not None: if isinstance(value, model_type): # Already the right type: dump directly, skip re-validation copy. - return value.model_dump(mode="json", exclude_none=True) + return value.model_dump(mode="json") if isinstance(value, (dict, BaseModel)): - return model_type.model_validate(value).model_dump(mode="json", exclude_none=True) + return model_type.model_validate(value).model_dump(mode="json") return value if isinstance(value, BaseModel): - return value.model_dump(mode="json", exclude_none=True) + return value.model_dump(mode="json") return value @staticmethod diff --git a/litellm/proxy/common_utils/callback_utils.py b/litellm/proxy/common_utils/callback_utils.py index 573763d6627..c644ecc3dae 100644 --- a/litellm/proxy/common_utils/callback_utils.py +++ b/litellm/proxy/common_utils/callback_utils.py @@ -611,10 +611,17 @@ def _transform_callback_vars(metadata: Any, transform: Callable[[str, Any], Any] return out -def _is_sensitive_callback_var(key: str) -> bool: - """Match codebase precedent: only credential-bearing fields get encrypted; - routing/identifier fields (host, base_url, project, region) stay plain.""" - if key in _EXTRA_SENSITIVE_CALLBACK_KEYS: +def is_sensitive_callback_key( + key: str, + extra: Optional[set[str]] = None, +) -> bool: + """Return ``True`` if ``key`` is present in ``extra`` (checked as-is), or + if its lowercase form is in ``_EXTRA_SENSITIVE_CALLBACK_KEYS``, or if + ``_CALLBACK_VAR_MASKER.is_sensitive_key`` matches it. + """ + if extra and key in extra: + return True + if key.lower() in _EXTRA_SENSITIVE_CALLBACK_KEYS: return True return _CALLBACK_VAR_MASKER.is_sensitive_key(key) @@ -622,7 +629,7 @@ def _is_sensitive_callback_var(key: str) -> bool: def _encrypt_if_plaintext(key: str, value: Any) -> Any: if not isinstance(value, str) or not value: return value - if not _is_sensitive_callback_var(key): + if not is_sensitive_callback_key(key): return value if value.startswith(_CALLBACK_VAR_ENCRYPTED_PREFIX): # Already encrypted — round-tripping ciphertext (e.g. UI Edit Settings diff --git a/litellm/proxy/common_utils/encrypt_decrypt_utils.py b/litellm/proxy/common_utils/encrypt_decrypt_utils.py index 8599b3ace7f..9a0b4f8b982 100644 --- a/litellm/proxy/common_utils/encrypt_decrypt_utils.py +++ b/litellm/proxy/common_utils/encrypt_decrypt_utils.py @@ -150,7 +150,7 @@ def decrypt_value_helper( verbose_proxy_logger.debug(error_message) return value if return_original_value else None - verbose_proxy_logger.debug(f"Unable to decrypt value={value} for key: {key}, returning None") + verbose_proxy_logger.debug(f"Unable to decrypt value for key: {key}, returning None") if return_original_value: return value else: diff --git a/litellm/proxy/common_utils/json_merge_patch.py b/litellm/proxy/common_utils/json_merge_patch.py new file mode 100644 index 00000000000..ec576a8ec70 --- /dev/null +++ b/litellm/proxy/common_utils/json_merge_patch.py @@ -0,0 +1,33 @@ +"""RFC 7386 JSON Merge Patch (https://www.rfc-editor.org/rfc/rfc7386).""" + +from pydantic import JsonValue + +# A merge patch recurses as deep as the client's JSON nests. Cap it far above any +# realistic team-metadata shape but well below Python's stack limit, so a +# pathologically deep patch is rejected instead of overflowing the stack. +_MAX_MERGE_DEPTH = 64 + + +def apply_json_merge_patch(target: JsonValue, patch: JsonValue, _depth: int = 0) -> JsonValue: + """Apply an RFC 7386 JSON Merge Patch to ``target`` and return the result. + + - a key absent from ``patch`` keeps its value in ``target`` + - a key mapped to ``null`` in ``patch`` is removed from the result + - any other value overwrites, recursing into nested objects + + ``target`` is never mutated; a new value is returned. Raises ``ValueError`` + if ``patch`` nests deeper than ``_MAX_MERGE_DEPTH``. + """ + if not isinstance(patch, dict): + return patch + if _depth >= _MAX_MERGE_DEPTH: + raise ValueError(f"JSON merge patch nesting exceeds the maximum depth of {_MAX_MERGE_DEPTH}") + + base = target if isinstance(target, dict) else {} + preserved = {key: value for key, value in base.items() if key not in patch} + applied = { + key: apply_json_merge_patch(base.get(key), value, _depth + 1) + for key, value in patch.items() + if value is not None + } + return {**preserved, **applied} diff --git a/litellm/proxy/compliance_checks.py b/litellm/proxy/compliance_checks.py index 8cde91e32b2..445257a1e01 100644 --- a/litellm/proxy/compliance_checks.py +++ b/litellm/proxy/compliance_checks.py @@ -35,15 +35,49 @@ class ComplianceChecker: If a guardrail doesn't have a mode specified, it's treated as pre-call (the most common case). """ - result = [] - for g in self.guardrails: - g_mode = g.get("guardrail_mode") - # If no mode specified, default to pre_call - if g_mode is None and mode == "pre_call": - result.append(g) - elif g_mode == mode: - result.append(g) - return result + return [g for g in self.guardrails if self._mode_matches(g.get("guardrail_mode"), mode)] + + @staticmethod + def _mode_matches(g_mode: object, mode: str) -> bool: + """ + Return True only when a guardrail with logged ``guardrail_mode`` of + ``g_mode`` is guaranteed to have run in ``mode`` for the audited request. + + ``guardrail_mode`` in a spend log can take several shapes because + ``LitellmParams.mode`` is typed ``Union[str, List[str], Mode]``, and + when the event type cannot be inferred at write time the raw config is + logged verbatim. The spend log records the configured mode(s), not the + concrete hook that fired for a given request; a match reports a mode + satisfied only when every configured branch runs in that mode, so True + never claims a hook the guardrail may not have actually executed. + + Fails safe: if the guarantee cannot be established (missing default, + divergent per-tag override, or a list that runs in more than one mode), + the guardrail counts for no mode. The precise fix is to log the + resolved event mode and match on it; this is the safe interim. + """ + if g_mode is None: + return mode == "pre_call" + if isinstance(g_mode, str): + return g_mode == mode + if isinstance(g_mode, (list, tuple)): + return bool(g_mode) and all(m == mode for m in g_mode) + if isinstance(g_mode, dict): + default = g_mode.get("default") + if default is None: + return False + tags = g_mode.get("tags") + tag_branches = list(tags.values()) if isinstance(tags, dict) else [] + + def _branch_runs_in_mode(branch: object) -> bool: + if isinstance(branch, str): + return branch == mode + if isinstance(branch, (list, tuple)): + return bool(branch) and all(m == mode for m in branch) + return False + + return all(_branch_runs_in_mode(branch) for branch in [default, *tag_branches]) + return False def _has_guardrail_intervention(self, guardrails: List[Dict]) -> bool: """Check if any guardrail intervened (blocked/masked content).""" diff --git a/litellm/proxy/db/db_spend_update_writer.py b/litellm/proxy/db/db_spend_update_writer.py index cf4c3e98f00..ca6875ca800 100644 --- a/litellm/proxy/db/db_spend_update_writer.py +++ b/litellm/proxy/db/db_spend_update_writer.py @@ -175,16 +175,9 @@ class DBSpendUpdateWriter: if team_id is not None and team_id != "": payload["team_id"] = team_id - # One deepcopy shared by all 6 daily spend helpers (was 5, fixes agent bug) - payload_copy = copy.deepcopy(payload) - - # Deepcopy request_tags for _update_tag_db - request_tags = copy.deepcopy(payload.get("request_tags")) - - # Keep _insert_spend_log_to_db awaited inline (not a task, preserve current behavior) if disable_spend_logs is False: await self._insert_spend_log_to_db( - payload=copy.deepcopy(payload), + payload=payload, prisma_client=prisma_client, ) else: @@ -204,8 +197,7 @@ class DBSpendUpdateWriter: prisma_client=prisma_client, user_api_key_cache=user_api_key_cache, litellm_proxy_budget_name=litellm_proxy_budget_name, - payload_copy=payload_copy, - request_tags=request_tags, + payload=payload, ) ) @@ -336,14 +328,18 @@ class DBSpendUpdateWriter: prisma_client: Optional[PrismaClient], user_api_key_cache: DualCache, litellm_proxy_budget_name: Optional[str], - payload_copy: SpendLogsPayload, - request_tags: Optional[Any], + payload: SpendLogsPayload, ): """ Runs all 11 spend-update helpers sequentially inside a single asyncio task. Each helper is wrapped in try/except so one failure doesn't prevent the others. + + The deepcopy runs here, off the awaited request path, so the daily spend + helpers get a payload isolated from the spend-log queue entry and the caller. """ + payload_copy = copy.deepcopy(payload) + request_tags = payload_copy.get("request_tags") try: await self._update_user_db( response_cost=response_cost, diff --git a/litellm/proxy/db/db_transaction_queue/redis_update_buffer.py b/litellm/proxy/db/db_transaction_queue/redis_update_buffer.py index 15d8c6c5d1e..c924448669d 100644 --- a/litellm/proxy/db/db_transaction_queue/redis_update_buffer.py +++ b/litellm/proxy/db/db_transaction_queue/redis_update_buffer.py @@ -525,10 +525,11 @@ class RedisUpdateBuffer: # Slots 1-5: daily spend categories daily_results: List[Optional[Dict[str, Any]]] = [] for slot in range(1, 6): - if raw_results[slot] is None: + slot_result = raw_results[slot] + if slot_result is None: daily_results.append(None) else: - list_of_daily = [json.loads(t) for t in raw_results[slot]] # type: ignore + list_of_daily = [json.loads(t) for t in slot_result] aggregated = DailySpendUpdateQueue.get_aggregated_daily_spend_update_transactions(list_of_daily) daily_results.append(aggregated) diff --git a/litellm/proxy/db/exception_handler.py b/litellm/proxy/db/exception_handler.py index 48066945131..3a93896a206 100644 --- a/litellm/proxy/db/exception_handler.py +++ b/litellm/proxy/db/exception_handler.py @@ -66,6 +66,29 @@ class PrismaDBExceptionHandler: return True return False + @staticmethod + def is_prisma_data_error(e: Exception) -> bool: + """True iff ``e`` is a base prisma ``DataError``: the database processed + the statement and refused the data itself (e.g. ``invalid byte sequence + for encoding "UTF8": 0x00``), as opposed to a connectivity failure. + + Matched by exact type, not ``isinstance``: the specific data-layer + subclasses (``UniqueViolationError``, ``TableNotFoundError``, + ``MissingRequiredValueError`` ...) all derive from ``DataError`` but + carry their own semantics, and a systemic one like a missing table must + not be mistaken for a single poison row and bisected away. A raw + Postgres execution error with no prisma P-code surfaces as the base + ``DataError``. + + prisma also wraps the P1001 "can't reach database server" outage as a + base ``DataError``, so a caller that must not treat an outage as a + per-row data rejection has to additionally consult + ``is_database_service_unavailable_error`` before acting on a True here. + """ + import prisma + + return type(e) is prisma.errors.DataError + @staticmethod def is_database_transport_error(e: Exception) -> bool: """ diff --git a/litellm/proxy/db/prisma_client.py b/litellm/proxy/db/prisma_client.py index 4042755f80d..fbccb8a726c 100644 --- a/litellm/proxy/db/prisma_client.py +++ b/litellm/proxy/db/prisma_client.py @@ -137,8 +137,17 @@ class PrismaWrapper: self.on_engine_replaced: Callable[[], None] | None = None def _get_engine_pid(self) -> int: - """Get the PID of the current Prisma engine subprocess, or 0 if unavailable.""" + """Get the PID of the current Prisma engine subprocess, or 0 if unavailable. + + Must never raise: it runs inside the reconnect path, where the client + may be in any broken state. Prisma's ``_engine`` is a property that + raises ``ClientNotConnectedError`` on a disconnected client; if that + escaped here, ``recreate_prisma_client`` would fail before it could + build a replacement client and the reconnect loop could never recover. + """ try: + if self._original_prisma.is_connected() is not True: + return 0 engine = self._original_prisma._engine process = getattr(engine, "process", None) if engine is not None else None if process is not None: diff --git a/litellm/proxy/db/routing_prisma_wrapper.py b/litellm/proxy/db/routing_prisma_wrapper.py index 7ae60121c6f..703abc64d8c 100644 --- a/litellm/proxy/db/routing_prisma_wrapper.py +++ b/litellm/proxy/db/routing_prisma_wrapper.py @@ -73,6 +73,17 @@ class RoutingPrismaWrapper: `connect()` or `recreate_prisma_client()` clears the flag. This keeps the proxy serving traffic during transient reader outages instead of failing startup or returning errors for read-heavy endpoints. + + Writer degradation: a writer-side `connect()` failure while the reader + connects is likewise non-fatal — the wrapper sets + `_writer_unavailable=True`, logs a warning, and keeps serving reads from + the reader (key lookups, DB-stored model loads) so a proxy that starts + during a primary outage still serves inference from the replica. Writes + fail at call time until the writer recovers; the PrismaClient DB health + watchdog polls `writer_unavailable` and drives the writer reconnect, + which clears the flag via `recreate_prisma_client`. Only when BOTH sides + fail to connect does `connect()` raise (full DB outage — the existing + `allow_requests_on_db_unavailable` startup handling applies). """ def __init__(self, writer: PrismaWrapper, reader: PrismaWrapper): @@ -81,6 +92,7 @@ class RoutingPrismaWrapper: # When True, reads fall back to the writer. Flipped on by reader # connect/recreate failures and flipped off on the next reader recovery. self._reader_unavailable: bool = False + self._writer_unavailable: bool = False @property def writer(self) -> PrismaWrapper: @@ -94,17 +106,46 @@ class RoutingPrismaWrapper: def reader_unavailable(self) -> bool: return self._reader_unavailable + @property + def writer_unavailable(self) -> bool: + return self._writer_unavailable + + def mark_writer_recovered(self) -> None: + """Clear the degraded-writer flag after an external health probe proved + the writer reachable. Needed when recovery happens without + `recreate_prisma_client` (e.g. an IAM token refresh already recreated + the writer engine), which is otherwise the only runtime path that + clears the flag — without this, the watchdog would keep firing + reconnect attempts against an already-healthy writer.""" + self._writer_unavailable = False + def _should_use_reader(self) -> bool: return not self._reader_unavailable - async def connect(self, *args: Any, **kwargs: Any) -> None: - await self._writer.connect(*args, **kwargs) - verbose_proxy_logger.info("[writer] DB connected") + @staticmethod + async def _try_connect(client: PrismaWrapper, *args: Any, **kwargs: Any) -> Exception | None: + if client.is_connected() is True: + return None try: - await self._reader.connect(*args, **kwargs) + await client.connect(*args, **kwargs) + return None + except Exception as e: + return e + + async def connect(self, *args: Any, **kwargs: Any) -> None: + writer_error = await self._try_connect(self._writer, *args, **kwargs) + if writer_error is None: + self._writer_unavailable = False + verbose_proxy_logger.info("[writer] DB connected") + reader_error = await self._try_connect(self._reader, *args, **kwargs) + if reader_error is None: self._reader_unavailable = False verbose_proxy_logger.info("[reader] DB connected") - except Exception as e: + if writer_error is None and reader_error is None: + return + if writer_error is not None and reader_error is not None: + raise writer_error + if reader_error is not None: # Degrade gracefully: the proxy keeps serving traffic with reads # routed to the writer until the reader endpoint is reachable. # Aborting startup here would tie proxy availability to an @@ -113,8 +154,15 @@ class RoutingPrismaWrapper: verbose_proxy_logger.warning( "Failed to connect to read replica DB: %s. " "Falling back to the writer for reads until the reader is reachable.", - e, + reader_error, ) + return + self._writer_unavailable = True + verbose_proxy_logger.warning( + "Failed to connect to primary (writer) DB: %s. " + "Serving reads from the read replica; writes will fail until the writer recovers.", + writer_error, + ) async def disconnect(self, *args: Any, **kwargs: Any) -> None: first_error: BaseException | None = None @@ -172,6 +220,7 @@ class RoutingPrismaWrapper: ) if not writer_recreated: return False + self._writer_unavailable = False try: await self._recreate_reader(http_client=http_client) self._reader_unavailable = False diff --git a/litellm/proxy/dev_config.yaml b/litellm/proxy/dev_config.yaml index e437ed7a118..4e9c710d446 100644 --- a/litellm/proxy/dev_config.yaml +++ b/litellm/proxy/dev_config.yaml @@ -28,6 +28,16 @@ model_list: litellm_params: model: anthropic/claude-opus-4-8 api_key: os.environ/ANTHROPIC_API_KEY + - model_name: anthropic-sonnet-5 + litellm_params: + model: anthropic/claude-sonnet-5 + api_key: os.environ/ANTHROPIC_API_KEY + + # ---------- Embeddings (for complexity router semantic keyword matching) ---------- + - model_name: voyage-4-large + litellm_params: + model: voyage/voyage-4-large + api_key: os.environ/VOYAGE_API_KEY # ---------- Bedrock Invoke ---------- - model_name: bedrock-invoke-haiku-4-5 @@ -182,10 +192,28 @@ model_list: litellm_params: model: openai/gpt-5.5 api_key: os.environ/OPENAI_API_KEY + - model_name: gpt-4o-mini + litellm_params: + model: openai/gpt-4o-mini + api_key: os.environ/OPENAI_API_KEY general_settings: master_key: sk-1234 + # Opt-in: let CheckBatchCost track cost for unmanaged Vertex batches created with a raw gs:// input_file_id. + # Requires a vertex_ai deployment configured for the batched model. Defaults to false. + # track_unmanaged_vertex_batch_cost: true + +sandbox_tools: + - sandbox_tool_name: e2b_sandbox + litellm_params: + sandbox_provider: e2b + api_key: os.environ/E2B_API_KEY litellm_settings: drop_params: True telemetry: False + code_interpreter_interception_params: + enabled: true + sandbox_tool_name: e2b_sandbox + callbacks: + - code_interpreter_interception diff --git a/litellm/proxy/example_config_yaml/multi_instance_simple_config.yaml b/litellm/proxy/example_config_yaml/multi_instance_simple_config.yaml index b353924f000..eb091cc72c5 100644 --- a/litellm/proxy/example_config_yaml/multi_instance_simple_config.yaml +++ b/litellm/proxy/example_config_yaml/multi_instance_simple_config.yaml @@ -5,8 +5,9 @@ model_list: api_key: my-fake-key api_base: os.environ/FAKE_OPENAI_API_BASE -litellm_settings: - cache: True - cache_params: - type: redis +general_settings: + coordination_redis: + host: os.environ/REDIS_HOST + port: os.environ/REDIS_PORT + password: os.environ/REDIS_PASSWORD diff --git a/litellm/proxy/example_config_yaml/spend_tracking_config.yaml b/litellm/proxy/example_config_yaml/spend_tracking_config.yaml index 60adadbd8d4..d66fd5fa601 100644 --- a/litellm/proxy/example_config_yaml/spend_tracking_config.yaml +++ b/litellm/proxy/example_config_yaml/spend_tracking_config.yaml @@ -7,9 +7,6 @@ model_list: general_settings: use_redis_transaction_buffer: true - -litellm_settings: - cache: True - cache_params: - type: redis - supported_call_types: [] \ No newline at end of file + coordination_redis: + host: os.environ/REDIS_HOST + port: os.environ/REDIS_PORT \ No newline at end of file diff --git a/litellm/proxy/guardrails/_content_utils.py b/litellm/proxy/guardrails/_content_utils.py index 1d31e33c77c..6cdf49818e6 100644 --- a/litellm/proxy/guardrails/_content_utils.py +++ b/litellm/proxy/guardrails/_content_utils.py @@ -32,6 +32,9 @@ def is_text_content_call_type(call_type: str) -> bool: return call_type in TEXT_CONTENT_CALL_TYPES +TEXT_PART_TYPES: FrozenSet[str] = frozenset({"text", "input_text", "output_text"}) + + def _iter_text_parts_in_content(content: Any) -> Iterator[str]: """Yield text fragments from a ``message.content`` value (string or multimodal list). Non-text parts (images, audio, …) are skipped.""" @@ -48,7 +51,7 @@ def _iter_text_parts_in_content(content: Any) -> Iterator[str]: continue if not isinstance(part, dict): continue - if part.get("type") == "text": + if part.get("type") in TEXT_PART_TYPES: text = part.get("text") if isinstance(text, str) and text: yield text @@ -58,14 +61,20 @@ def _coerce_input_to_messages(input_value: Any) -> List[Dict[str, Any]]: """Coerce a Responses-API ``data["input"]`` value into chat-style messages.""" if isinstance(input_value, str): return [{"role": "user", "content": input_value}] - if isinstance(input_value, list): - if input_value and all(isinstance(item, dict) and "role" in item for item in input_value): - return list(input_value) - # Mixed lists (content-part dicts + bare strings) and pure - # string/dict lists all become a single user message; the content - # iterator below handles each element type uniformly. - return [{"role": "user", "content": input_value}] - return [] + if not isinstance(input_value, list): + return [] + messages: List[Dict[str, Any]] = [] + for item in input_value: + if isinstance(item, str): + messages.append({"role": "user", "content": item}) + elif isinstance(item, dict): + if item.get("type") in TEXT_PART_TYPES: + messages.append({"role": item.get("role") or "user", "content": [item]}) + elif "content" in item: + messages.append({"role": item.get("role") or "user", "content": item["content"]}) + elif item.get("type") == "function_call_output" and "output" in item: + messages.append({"role": item.get("role") or "tool", "content": item["output"]}) + return messages def _iter_inspection_messages(data: Dict[str, Any]) -> Iterator[Dict[str, Any]]: @@ -112,7 +121,7 @@ def walk_user_text(data: Dict[str, Any], visit: Callable[[str], str]) -> int: new_parts.append(visit(part)) elif ( isinstance(part, dict) - and part.get("type") == "text" + and part.get("type") in TEXT_PART_TYPES and isinstance(part.get("text"), str) and part["text"] ): @@ -136,25 +145,20 @@ def walk_user_text(data: Dict[str, Any], visit: Callable[[str], str]) -> int: data["input"] = visit(input_value) return visited if isinstance(input_value, list): - # List of full messages: rewrite each message's content. - if input_value and all(isinstance(item, dict) and "role" in item for item in input_value): - for item in input_value: - if "content" in item: - item["content"] = _rewrite_content(item["content"]) - return visited - # List of content parts and/or bare strings: rewrite in place. for idx, item in enumerate(input_value): - if isinstance(item, str) and item: - visited += 1 - input_value[idx] = visit(item) - elif ( - isinstance(item, dict) - and item.get("type") == "text" - and isinstance(item.get("text"), str) - and item["text"] - ): - visited += 1 - input_value[idx] = {**item, "text": visit(item["text"])} + if isinstance(item, str): + if item: + visited += 1 + input_value[idx] = visit(item) + elif isinstance(item, dict): + if item.get("type") in TEXT_PART_TYPES: + if isinstance(item.get("text"), str) and item["text"]: + visited += 1 + input_value[idx] = {**item, "text": visit(item["text"])} + elif "content" in item: + item["content"] = _rewrite_content(item["content"]) + elif item.get("type") == "function_call_output" and "output" in item: + item["output"] = _rewrite_content(item["output"]) return visited return visited diff --git a/litellm/proxy/guardrails/guardrail_endpoints.py b/litellm/proxy/guardrails/guardrail_endpoints.py index d3a5d649f17..b6a2d8d9069 100644 --- a/litellm/proxy/guardrails/guardrail_endpoints.py +++ b/litellm/proxy/guardrails/guardrail_endpoints.py @@ -1296,21 +1296,27 @@ async def get_guardrail_ui_settings(): get_available_content_categories, get_pattern_metadata, ) + from litellm.proxy.guardrails.guardrail_registry import guardrail_class_registry - # Convert the PII_ENTITY_CATEGORIES_MAP to the format expected by the UI - category_maps = [] - for category, entities in PII_ENTITY_CATEGORIES_MAP.items(): - category_maps.append( - { - "category": category.value, - "entities": [entity.value for entity in entities], - } - ) + category_maps = [ + { + "category": category.value, + "entities": [entity.value for entity in entities], + } + for category, entities in PII_ENTITY_CATEGORIES_MAP.items() + ] + + supported_modes_by_provider = { + provider: [hook.value for hook in hooks] + for provider, guardrail_class in guardrail_class_registry.items() + if (hooks := guardrail_class.get_supported_event_hooks()) is not None + } return GuardrailUIAddGuardrailSettings( supported_entities=[entity.value for entity in PiiEntityType], supported_actions=[action.value for action in PiiAction], supported_modes=[mode.value for mode in GuardrailEventHooks], + supported_modes_by_provider=supported_modes_by_provider, pii_entity_categories=category_maps, content_filter_settings={ "prebuilt_patterns": get_pattern_metadata(), diff --git a/litellm/proxy/guardrails/guardrail_hooks/aim/aim.py b/litellm/proxy/guardrails/guardrail_hooks/aim/aim.py index e7d9406ae3b..01eb61dad08 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/aim/aim.py +++ b/litellm/proxy/guardrails/guardrail_hooks/aim/aim.py @@ -7,7 +7,7 @@ import asyncio import json import os -from typing import TYPE_CHECKING, Any, AsyncGenerator, Optional, Type, Union +from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional, Type, Union from pydantic import BaseModel from websockets.asyncio.client import ClientConnection, connect @@ -26,6 +26,7 @@ from litellm.proxy.guardrails._content_utils import ( build_inspection_messages, has_non_string_content, ) +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import ( CallTypesLiteral, Choices, @@ -44,7 +45,16 @@ class AimGuardrailMissingSecrets(Exception): class AimGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] + def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None, **kwargs): + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) ssl_verify = kwargs.pop("ssl_verify", None) self.async_handler = get_async_httpx_client( llm_provider=httpxSpecialProvider.GuardrailCallback, @@ -93,11 +103,10 @@ class AimGuardrail(CustomGuardrail): user_email=user_email, litellm_call_id=call_id, ) - # Covers multimodal list content + Responses-API input. response = await self.async_handler.post( f"{self.api_base}/fw/v1/analyze", headers=headers, - json={"messages": build_inspection_messages(data)}, + json={"messages": self._build_aim_inspection_messages(data)}, ) response.raise_for_status() res = response.json() @@ -116,6 +125,15 @@ class AimGuardrail(CustomGuardrail): verbose_proxy_logger.error(f"Aim: {action_type} action") return data + @staticmethod + def _build_aim_inspection_messages(data: dict) -> list[dict[str, str]]: + """AIM validates against the OpenAI chat schema. Bare ``role: "tool"`` + without ``tool_call_id`` and bare ``role: "function"`` without ``name`` + are rejected; the flatten drops those fields, so any role outside + ``{system, user, assistant}`` collapses to ``user`` for the AIM POST.""" + safe_roles = {"system", "user", "assistant"} + return [{**m, "role": "user"} if m["role"] not in safe_roles else m for m in build_inspection_messages(data)] + @staticmethod def _rejection(message: str, *, openai_code: str | None = None) -> ProxyException: return ProxyException( @@ -177,7 +195,10 @@ class AimGuardrail(CustomGuardrail): user_email=user_email, litellm_call_id=call_id, ), - json={"messages": build_inspection_messages(request_data) + [{"role": "assistant", "content": output}]}, + json={ + "messages": self._build_aim_inspection_messages(request_data) + + [{"role": "assistant", "content": output}] + }, ) response.raise_for_status() res = response.json() diff --git a/litellm/proxy/guardrails/guardrail_hooks/akto/akto.py b/litellm/proxy/guardrails/guardrail_hooks/akto/akto.py index be9c9cb1be7..daae74ae8e0 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/akto/akto.py +++ b/litellm/proxy/guardrails/guardrail_hooks/akto/akto.py @@ -11,7 +11,7 @@ import asyncio import json import os from datetime import datetime -from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Tuple, Type +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Type from fastapi import HTTPException @@ -52,6 +52,13 @@ class AktoGuardrail(CustomGuardrail): return AktoConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, akto_base_url: Optional[str] = None, @@ -90,10 +97,7 @@ class AktoGuardrail(CustomGuardrail): self.akto_account_id = akto_account_id or os.environ.get("AKTO_ACCOUNT_ID", "1000000") self.akto_vxlan_id = akto_vxlan_id or os.environ.get("AKTO_VXLAN_ID", "0") - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - ] + kwargs["supported_event_hooks"] = list(self.get_supported_event_hooks()) super().__init__(**kwargs) verbose_proxy_logger.debug( diff --git a/litellm/proxy/guardrails/guardrail_hooks/aporia_ai/aporia_ai.py b/litellm/proxy/guardrails/guardrail_hooks/aporia_ai/aporia_ai.py index ba9c8398152..dc3fc40625c 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/aporia_ai/aporia_ai.py +++ b/litellm/proxy/guardrails/guardrail_hooks/aporia_ai/aporia_ai.py @@ -37,7 +37,15 @@ if TYPE_CHECKING: class AporiaGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] + def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None, **kwargs): + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) self.aporia_api_key = api_key or os.environ["APORIO_API_KEY"] self.aporia_api_base = api_base or os.environ["APORIO_API_BASE"] diff --git a/litellm/proxy/guardrails/guardrail_hooks/azure/prompt_shield.py b/litellm/proxy/guardrails/guardrail_hooks/azure/prompt_shield.py index 788fe5b05c7..befb1b7ae56 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/azure/prompt_shield.py +++ b/litellm/proxy/guardrails/guardrail_hooks/azure/prompt_shield.py @@ -12,6 +12,7 @@ from litellm.integrations.custom_guardrail import ( CustomGuardrail, log_guardrail_information, ) +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import CallTypesLiteral from .base import AzureGuardrailBase @@ -47,19 +48,13 @@ class AzureContentSafetyPromptShieldGuardrail(AzureGuardrailBase, CustomGuardrai **kwargs, ): """Initialize Azure Prompt Shield guardrail handler.""" - from litellm.types.guardrails import GuardrailEventHooks - - supported_event_hooks = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.during_call, - ] # AzureGuardrailBase.__init__ stores api_key, api_base, api_version, # async_handler and forwards the rest to CustomGuardrail. super().__init__( api_key=api_key, api_base=api_base, guardrail_name=guardrail_name, - supported_event_hooks=supported_event_hooks, + supported_event_hooks=list(self.get_supported_event_hooks()), **kwargs, ) @@ -149,3 +144,10 @@ class AzureContentSafetyPromptShieldGuardrail(AzureGuardrailBase, CustomGuardrai ) return AzurePromptShieldGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/azure/text_moderation.py b/litellm/proxy/guardrails/guardrail_hooks/azure/text_moderation.py index c8553926559..91f5df0e9b8 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/azure/text_moderation.py +++ b/litellm/proxy/guardrails/guardrail_hooks/azure/text_moderation.py @@ -13,6 +13,7 @@ from litellm.integrations.custom_guardrail import ( log_guardrail_information, ) from litellm.proxy._types import UserAPIKeyAuth +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import CallTypesLiteral from .base import AzureGuardrailBase @@ -42,6 +43,13 @@ class AzureContentSafetyTextModerationGuardrail(AzureGuardrailBase, CustomGuardr default_severity_threshold: int = 2 + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, guardrail_name: str, @@ -56,6 +64,7 @@ class AzureContentSafetyTextModerationGuardrail(AzureGuardrailBase, CustomGuardr AzureTextModerationRequestBodyOptionalParams, ) + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) # AzureGuardrailBase.__init__ stores api_key, api_base, api_version, # async_handler and forwards the rest to CustomGuardrail. super().__init__( diff --git a/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py b/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py index 6e46f971dd8..44ae57f81db 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py +++ b/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py @@ -33,7 +33,7 @@ import litellm from litellm._logging import verbose_proxy_logger from litellm.litellm_core_utils.core_helpers import redact_nested_match_and_regex_keys from litellm.caching import DualCache -from litellm.exceptions import GuardrailInterventionNormalStringError +from litellm.exceptions import ModifyResponseException from litellm.integrations.custom_guardrail import CustomGuardrail from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM from litellm.llms.custom_httpx.http_handler import ( @@ -166,14 +166,7 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): """ # Set supported event hooks to include MCP hooks - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.pre_mcp_call, - GuardrailEventHooks.during_mcp_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) BaseAWSLLM.__init__(self) @@ -184,6 +177,16 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): self.guardrailVersion, ) + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.pre_mcp_call, + GuardrailEventHooks.during_mcp_call, + ] + def _create_bedrock_input_content_request(self, messages: Optional[List[AllMessageValues]]) -> BedrockRequest: """ Create a bedrock request for the input content - the LLM request. @@ -754,7 +757,9 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): ) bedrock_guardrail_response = BedrockGuardrailResponse(**_json_response) if self._should_raise_guardrail_blocked_exception(bedrock_guardrail_response): - raise self._get_http_exception_for_blocked_guardrail(bedrock_guardrail_response) + raise self._get_http_exception_for_blocked_guardrail( + bedrock_guardrail_response, request_data=request_data + ) else: status_code, detail_message = self._parse_bedrock_guardrail_error_response(httpx_response) verbose_proxy_logger.error( @@ -1027,8 +1032,8 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): return blocked def _get_http_exception_for_blocked_guardrail( - self, response: BedrockGuardrailResponse - ) -> Union[HTTPException, GuardrailInterventionNormalStringError]: + self, response: BedrockGuardrailResponse, request_data: Optional[dict] = None + ) -> Union[HTTPException, ModifyResponseException]: """ Get the HTTP exception for a blocked guardrail. """ @@ -1040,7 +1045,13 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): bedrock_guardrail_output_text += output.get("text") or "" if self.disable_exception_on_block is True: - return GuardrailInterventionNormalStringError(message=bedrock_guardrail_output_text) + _request_data = request_data or {} + return ModifyResponseException( + message=bedrock_guardrail_output_text, + model=_request_data.get("model", "bedrock-guardrail"), + request_data=_request_data, + guardrail_name=self.guardrail_name, + ) detail: Dict[str, Any] = { "error": "Violated guardrail policy", @@ -1134,18 +1145,6 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): # This means all actions were ANONYMIZED or NONE, so don't raise exception return False - def create_guardrail_blocked_response(self, response: str) -> ModelResponse: - from litellm.types.utils import Choices, Message, ModelResponse - - return ModelResponse( - choices=[ - Choices( - message=Message(content=response), - ) - ], - model="bedrock-guardrail", - ) - async def async_pre_call_hook( self, user_api_key_dict: UserAPIKeyAuth, @@ -1183,16 +1182,15 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): ######################################################### ########## 1. Make the Bedrock API request ########## ######################################################### - bedrock_guardrail_response: Optional[Union[BedrockGuardrailResponse, str]] = None - try: - bedrock_guardrail_response = await self.make_bedrock_api_request( - source="INPUT", - messages=filtered_messages, - request_data=data, - logging_event_type=GuardrailEventHooks.pre_call, - ) - except GuardrailInterventionNormalStringError as e: - bedrock_guardrail_response = e.message + # A block with disable_exception_on_block=True raises ModifyResponseException + # from make_bedrock_api_request; that propagates to the endpoint handler, + # which returns a 200 whose message is the guardrail's blockedInputMessaging. + bedrock_guardrail_response = await self.make_bedrock_api_request( + source="INPUT", + messages=filtered_messages, + request_data=data, + logging_event_type=GuardrailEventHooks.pre_call, + ) ######################################################### ######################################################### @@ -1207,8 +1205,6 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): updated_target_messages=updated_subset, target_indices=filter_result.target_indices, ) - if isinstance(bedrock_guardrail_response, str): - data["mock_response"] = self.create_guardrail_blocked_response(response=bedrock_guardrail_response) ######################################################### ########## 3. Add the guardrail to the applied guardrails header ########## @@ -1248,16 +1244,19 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): ######################################################### ########## 1. Make the Bedrock API request ########## ######################################################### - bedrock_guardrail_response: Optional[Union[BedrockGuardrailResponse, str]] = None - try: - bedrock_guardrail_response = await self.make_bedrock_api_request( - source="INPUT", - messages=filtered_messages, - request_data=data, - logging_event_type=GuardrailEventHooks.during_call, - ) - except GuardrailInterventionNormalStringError as e: - bedrock_guardrail_response = e.message + # A block with disable_exception_on_block=True raises ModifyResponseException + # from make_bedrock_api_request. Because during_call runs in an asyncio.gather + # alongside the LLM call (common_request_processing.py), swallowing the + # exception here to set data["mock_response"] was ineffective: route_request + # unpacked kwargs before this hook ran, and the LLM task's response was taken + # unconditionally. Letting the exception propagate cancels the LLM task and + # the endpoint handler returns the block response. + bedrock_guardrail_response = await self.make_bedrock_api_request( + source="INPUT", + messages=filtered_messages, + request_data=data, + logging_event_type=GuardrailEventHooks.during_call, + ) ######################################################### ######################################################### @@ -1272,8 +1271,6 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): updated_target_messages=updated_subset, target_indices=filter_result.target_indices, ) - if isinstance(bedrock_guardrail_response, str): - data["mock_response"] = self.create_guardrail_blocked_response(response=bedrock_guardrail_response) ######################################################### ########## 3. Add the guardrail to the applied guardrails header ########## @@ -1323,7 +1320,11 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): # users should configure if they want input validation. Running an # extra INPUT scan here produced a duplicate post-call entry in the # trace and made no semantic sense for a "post-call" event. - output_content_bedrock: Optional[Union[BedrockGuardrailResponse, str]] = None + # A block with disable_exception_on_block=True raises ModifyResponseException + # from make_bedrock_api_request; that propagates to the endpoint handler, + # which returns a 200 whose message is the guardrail's blockedInputMessaging. + # Attach the LLM response to original_response so the synthetic block reply + # reports the real token usage the upstream call consumed instead of zero. try: output_content_bedrock = await self.make_bedrock_api_request( source="OUTPUT", @@ -1332,15 +1333,15 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): request_data=data, logging_event_type=GuardrailEventHooks.post_call, ) - except GuardrailInterventionNormalStringError as e: - output_content_bedrock = e.message + except ModifyResponseException as e: + if e.original_response is None: + e.original_response = response + raise ######################################################### ########## 2. Apply masking to response with output guardrail response ########## ######################################################### - if isinstance(output_content_bedrock, str): - response = self.create_guardrail_blocked_response(response=output_content_bedrock) - elif output_content_bedrock is not None: + if output_content_bedrock is not None: self._apply_masking_to_response( response=response, bedrock_guardrail_response=output_content_bedrock, @@ -1357,7 +1358,7 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): def _update_messages_with_updated_bedrock_guardrail_response( self, messages: List[AllMessageValues], - bedrock_guardrail_response: Union[BedrockGuardrailResponse, str], + bedrock_guardrail_response: BedrockGuardrailResponse, ) -> List[AllMessageValues]: """ Use the output from the bedrock guardrail to mask sensitive content in messages. @@ -1369,8 +1370,6 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): Returns: List of messages with content masked according to guardrail response """ - if isinstance(bedrock_guardrail_response, str): - return messages # Get masked texts from guardrail response masked_texts = self._extract_masked_texts_from_response(bedrock_guardrail_response) @@ -1422,7 +1421,14 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): # pre_call / during_call. Bedrock will raise if the response # violates the guardrail policy. ################################################################### - output_guardrail_response: Optional[Union[BedrockGuardrailResponse, str]] = None + # A block with disable_exception_on_block=True raises ModifyResponseException + # from make_bedrock_api_request. Non-streaming paths let it propagate so + # the endpoint handler turns it into a 200. Streaming can't do that: the + # SSE response headers are already flushed, so a raise would be serialized + # as an error frame by async_streaming_data_generator. Instead, replace + # the assembled response with the synthetic block content in-place and + # yield it as a normal stream, matching the shape a non-streaming block + # produces. try: output_guardrail_response = await self.make_bedrock_api_request( source="OUTPUT", @@ -1431,15 +1437,31 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): request_data=request_data, logging_event_type=GuardrailEventHooks.post_call, ) - except GuardrailInterventionNormalStringError as e: - output_guardrail_response = e.message + except ModifyResponseException as e: + # Preserve upstream usage from the LLM call we already + # consumed. Non-streaming blocks carry it via + # ModifyResponseException.original_response + + # _blocked_response_usage; streaming has to do the copy + # itself since the exception can't escape this generator. + _original_usage = getattr(assembled_model_response, "usage", None) + assembled_model_response = ModelResponse( + choices=[ + Choices( + index=0, + message=Message(role="assistant", content=e.message), + finish_reason="content_filter", + ) + ], + model=e.model, + ) + if _original_usage is not None: + assembled_model_response.usage = _original_usage + output_guardrail_response = None ######################################################################### ########## 2. Apply masking to response with output guardrail response ########## ######################################################################### - if isinstance(output_guardrail_response, str): - assembled_model_response = self.create_guardrail_blocked_response(response=output_guardrail_response) - elif output_guardrail_response is not None: + if output_guardrail_response is not None: self._apply_masking_to_response( response=assembled_model_response, bedrock_guardrail_response=output_guardrail_response, @@ -1732,13 +1754,13 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): inputs["texts"] = masked_texts return inputs - except (HTTPException, GuardrailInterventionNormalStringError): - # Let guardrail blocking exceptions propagate as-is so the proxy - # can return the correct HTTP status (400) or handle the - # GuardrailInterventionNormalStringError for disable_exception_on_block mode. - # Without this, the generic except below wraps them into a plain - # Exception, losing the HTTP semantics and preventing the proxy - # from properly blocking the call. + except (HTTPException, ModifyResponseException): + # Let guardrail blocking exceptions propagate as-is so the proxy can + # return the correct HTTP status (400 for HTTPException, 200 with the + # block message for ModifyResponseException in disable_exception_on_block + # mode). Without this, the generic except below wraps them into a plain + # Exception, losing the semantics and preventing the proxy from + # properly blocking the call. raise except Exception as e: verbose_proxy_logger.error("Bedrock Guardrail: Failed to apply guardrail: %s", str(e)) diff --git a/litellm/proxy/guardrails/guardrail_hooks/block_code_execution/block_code_execution.py b/litellm/proxy/guardrails/guardrail_hooks/block_code_execution/block_code_execution.py index ea66f416e15..cfb4a78fa6e 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/block_code_execution/block_code_execution.py +++ b/litellm/proxy/guardrails/guardrail_hooks/block_code_execution/block_code_execution.py @@ -351,11 +351,7 @@ class BlockCodeExecutionGuardrail(CustomGuardrail): _event_hook = GuardrailEventHooks(event_hook) super().__init__( guardrail_name=guardrail_name or "block_code_execution", - supported_event_hooks=[ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.during_call, - ], + supported_event_hooks=list(self.get_supported_event_hooks()), event_hook=_event_hook or [ GuardrailEventHooks.pre_call, @@ -378,6 +374,14 @@ class BlockCodeExecutionGuardrail(CustomGuardrail): return BlockCodeExecutionGuardrailConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.during_call, + ] + def _find_blocks(self, text: str) -> List[Tuple[int, int, str, str, float, CodeBlockActionTaken]]: """ Find all fenced code blocks in text. Returns list of diff --git a/litellm/proxy/guardrails/guardrail_hooks/cato_networks/cato_networks.py b/litellm/proxy/guardrails/guardrail_hooks/cato_networks/cato_networks.py index bf5a0a5f262..440618a2ffa 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/cato_networks/cato_networks.py +++ b/litellm/proxy/guardrails/guardrail_hooks/cato_networks/cato_networks.py @@ -9,7 +9,7 @@ import contextlib import json import os import ssl -from typing import TYPE_CHECKING, Any, AsyncGenerator, Optional, Type, Union +from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional, Type, Union from fastapi import HTTPException from pydantic import BaseModel @@ -30,6 +30,7 @@ from litellm.proxy.guardrails._content_utils import ( apply_redacted_messages_back, build_inspection_messages, ) +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import ( CallTypesLiteral, Choices, @@ -49,7 +50,16 @@ class CatoNetworksGuardrailMissingSecrets(Exception): class CatoNetworksGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] + def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None, **kwargs): + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) ssl_verify = kwargs.pop("ssl_verify", None) self.async_handler = get_async_httpx_client( llm_provider=httpxSpecialProvider.GuardrailCallback, diff --git a/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py b/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py index 561e6ce5f2b..4c31b038172 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py +++ b/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py @@ -224,18 +224,9 @@ class CiscoAIDefenseGuardrail(_CiscoAIDefenseMcpMixin, CustomGuardrail): self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) # Register broadly; runtime filtering happens in ``_surface_matches``. - supported_event_hooks = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.logging_only, - GuardrailEventHooks.pre_mcp_call, - GuardrailEventHooks.during_mcp_call, - ] - super().__init__( guardrail_name=guardrail_name, - supported_event_hooks=supported_event_hooks, + supported_event_hooks=list(self.get_supported_event_hooks()), **kwargs, ) @@ -2133,3 +2124,14 @@ class CiscoAIDefenseGuardrail(_CiscoAIDefenseMcpMixin, CustomGuardrail): ) return CiscoAIDefenseGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.logging_only, + GuardrailEventHooks.pre_mcp_call, + GuardrailEventHooks.during_mcp_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/crowdstrike_aidr/crowdstrike_aidr.py b/litellm/proxy/guardrails/guardrail_hooks/crowdstrike_aidr/crowdstrike_aidr.py index 84b0a3b8eba..fa71e7fc301 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/crowdstrike_aidr/crowdstrike_aidr.py +++ b/litellm/proxy/guardrails/guardrail_hooks/crowdstrike_aidr/crowdstrike_aidr.py @@ -1,17 +1,30 @@ -from collections.abc import Mapping, Sequence import json import os -from typing import TYPE_CHECKING, Annotated, Literal, Optional, Type, Union, cast -from pydantic import BaseModel, ConfigDict, Field -from typing_extensions import Any, override +from collections.abc import Mapping, Sequence +from typing import ( + TYPE_CHECKING, + Annotated, + List, + Literal, + NamedTuple, + Optional, + Union, + cast, +) from fastapi import HTTPException +from pydantic import BaseModel, ConfigDict, Field +from typing_extensions import Any, override from litellm._logging import verbose_proxy_logger from litellm.integrations.custom_guardrail import ( CustomGuardrail, log_guardrail_information, ) +from litellm.llms.base_llm.guardrail_translation.utils import ( + effective_skip_system_message_for_guardrail, + effective_skip_tool_message_for_guardrail, +) from litellm.llms.custom_httpx.http_handler import ( get_async_httpx_client, httpxSpecialProvider, @@ -19,7 +32,8 @@ from litellm.llms.custom_httpx.http_handler import ( from litellm.proxy.common_utils.callback_utils import ( add_guardrail_to_applied_guardrails_header, ) -from litellm.types.llms.openai import OpenAIChatCompletionToolParam +from litellm.types.guardrails import GuardrailEventHooks +from litellm.types.llms.openai import AllMessageValues, OpenAIChatCompletionToolParam from litellm.types.utils import GenericGuardrailAPIInputs if TYPE_CHECKING: @@ -64,6 +78,37 @@ class _GuardInput(BaseModel): tools: Optional[Sequence[OpenAIChatCompletionToolParam]] = None +class _GuardChatCompletionsResult(BaseModel): + guard_output: Optional[_GuardInput] = None + """Updated structured prompt.""" + blocked: Optional[bool] = None + """Whether or not the prompt triggered a block detection.""" + transformed: Optional[bool] = None + """Whether or not the original input was transformed.""" + detectors: Optional[dict[str, Any]] = None + """Result of the policy analyzing and input prompt.""" + + +class _GuardChatCompletionsResponse(BaseModel): + result: Optional[_GuardChatCompletionsResult] = None + + +class _FilteredMessages(NamedTuple): + """Subset of a conversation selected for guardrail analysis.""" + + messages: list[AllMessageValues] + """Messages subset.""" + indices: tuple[int, ...] + """Positions of the subset's messages in the original list.""" + + +class _GuardInputWithIndices(NamedTuple): + guard_input: _GuardInput + """Guard API payload.""" + sent_indices: tuple[int, ...] + """Positions of the guard input's messages in the original list.""" + + def _normalize_content(raw: object) -> str | list[_ContentPart] | None: if raw is None: return None @@ -99,7 +144,16 @@ def _extract_text_from_content(content: object) -> str: return "" -def _merge_metadata_bags(request_data: Mapping[str, Any]) -> Optional[dict[str, Any]]: +def _extract_text_from_message(message: _Message) -> str: + content = message.content + if isinstance(content, str): + return content + if content is None: + return "" + return "\n".join(part.text for part in content if isinstance(part, _TextContentPart)) + + +def _merge_metadata_bags(request_data: Mapping[str, Any]) -> dict[str, Any] | None: merged: dict[str, Any] = {} present = False for bag in (request_data.get("metadata"), request_data.get("litellm_metadata")): @@ -109,26 +163,102 @@ def _merge_metadata_bags(request_data: Mapping[str, Any]) -> Optional[dict[str, return merged if present else None +def _messages_since_last_assistant( + messages: list[AllMessageValues], +) -> _FilteredMessages: + if not messages: + return _FilteredMessages([], ()) + + if messages[-1]["role"] == "assistant": + indices = tuple(i for i, m in enumerate(messages) if m["role"] == "system") + (len(messages) - 1,) + return _FilteredMessages([messages[i] for i in indices], indices) + + last_assistant_idx = -1 + for i in range(len(messages) - 1, -1, -1): + if messages[i]["role"] == "assistant": + last_assistant_idx = i + break + + system_indices = tuple(i for i in range(last_assistant_idx + 1) if messages[i]["role"] == "system") + tail_indices = tuple(range(last_assistant_idx + 1, len(messages))) + indices = system_indices + tail_indices + return _FilteredMessages([messages[i] for i in indices], indices) + + +def _merge_request_transforms( + guard_output: _GuardInput, + structured_messages: list[AllMessageValues] | None, + texts: list[str], + sent_indices: tuple[int, ...], +) -> list[str]: + returned_texts = [_extract_text_from_message(msg) for msg in guard_output.messages] + original_texts = ( + [_extract_text_from_content(m.get("content")) for m in structured_messages] if structured_messages else texts + ) + replacements = { + idx: returned_texts[pos] + for pos, idx in enumerate(sent_indices) + if pos < len(returned_texts) and idx < len(original_texts) + } + return [replacements.get(idx, original) for idx, original in enumerate(original_texts)] + + +def _apply_message_redaction(original: AllMessageValues, redacted: _Message) -> AllMessageValues: + content = original.get("content") + if isinstance(content, str): + return cast(AllMessageValues, {**original, "content": _extract_text_from_message(redacted)}) + if isinstance(content, list) and _extract_text_from_content(content): + redacted_content = redacted.content + new_content = ( + [part.model_dump() for part in redacted_content] if isinstance(redacted_content, list) else redacted_content + ) + return cast(AllMessageValues, {**original, "content": new_content}) + return original + + +def _redacted_messages( + processed_messages: list[AllMessageValues], + guard_output: _GuardInput, + sent_indices: tuple[int, ...], + full_messages: list[AllMessageValues], +) -> list[AllMessageValues] | None: + redactions = { + id(processed_messages[idx]): _apply_message_redaction(processed_messages[idx], guard_output.messages[pos]) + for pos, idx in enumerate(sent_indices) + if pos < len(guard_output.messages) and idx < len(processed_messages) + } + if not redactions.keys() <= {id(message) for message in full_messages}: + return None + return [redactions.get(id(message), message) for message in full_messages] + + class CrowdStrikeAIDRHandler(CustomGuardrail): """ CrowdStrike AIDR AI Guardrail handler to interact with the CrowdStrike AIDR AI Guard service. """ + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, guardrail_name: str, - api_key: Optional[str] = None, - api_base: Optional[str] = None, + api_key: str | None = None, + api_base: str | None = None, **kwargs, - ): + ) -> None: """ Initializes the CrowdStrikeAIDRHandler. Args: guardrail_name (str): The name of the guardrail instance. - api_key (Optional[str]): The CrowdStrike AIDR API key. Reads from CS_AIDR_TOKEN env var if None. - api_base (Optional[str]): The CrowdStrike AIDR API base URL. Reads from CS_AIDR_BASE_URL env var if None. + api_key (str | None): The CrowdStrike AIDR API key. Reads from CS_AIDR_TOKEN env var if None. + api_base (str | None): The CrowdStrike AIDR API base URL. Reads from CS_AIDR_BASE_URL env var if None. **kwargs: Additional arguments passed to the CustomGuardrail base class. """ self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) @@ -145,13 +275,16 @@ class CrowdStrikeAIDRHandler(CustomGuardrail): "CrowdStrike AIDR API base URL is required. Set CS_AIDR_BASE_URL environment variable or pass it in litellm_params." ) + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) # Pass relevant kwargs to the parent class super().__init__(guardrail_name=guardrail_name, **kwargs) verbose_proxy_logger.debug( f"Initialized CrowdStrike AIDR Guardrail: name={guardrail_name}, api_base={self.api_base}" ) - async def _call_crowdstrike_aidr_guard(self, payload: dict[str, Any], hook_name: str) -> dict[str, Any]: + async def _call_crowdstrike_aidr_guard( + self, payload: dict[str, Any], hook_name: str + ) -> _GuardChatCompletionsResult: """ Makes the API call to the CrowdStrike AIDR AI Guard endpoint. The function itself will raise an error if a response should be blocked, @@ -167,7 +300,7 @@ class CrowdStrikeAIDRHandler(CustomGuardrail): Exception: For other API call failures. Returns: - dict: The API response body + The parsed `result` body of the API response. """ endpoint = f"{self.api_base}/v1/guard_chat_completions" @@ -181,11 +314,12 @@ class CrowdStrikeAIDRHandler(CustomGuardrail): ) response = await self.async_handler.post(url=endpoint, json=payload, headers=headers) + assert response is not None response.raise_for_status() - result: dict[str, Any] = response.json() + result = _GuardChatCompletionsResponse.model_validate(response.json()).result or _GuardChatCompletionsResult() - if result.get("result", {}).get("blocked"): + if result.blocked: verbose_proxy_logger.warning( f"CrowdStrike AIDR Guardrail ({hook_name}): Request blocked. Response: {result}" ) @@ -197,25 +331,28 @@ class CrowdStrikeAIDRHandler(CustomGuardrail): }, ) verbose_proxy_logger.debug( - f"CrowdStrike AIDR Guardrail ({hook_name}): Request passed. Response: {result.get('result', {}).get('detectors')}" + f"CrowdStrike AIDR Guardrail ({hook_name}): Request passed. Response: {result.detectors}" ) return result - def _build_guard_input_for_request(self, inputs: GenericGuardrailAPIInputs) -> Optional[_GuardInput]: + def _build_guard_input_for_request(self, inputs: GenericGuardrailAPIInputs) -> _GuardInputWithIndices | None: guard_input = _GuardInput(messages=[], tools=[]) structured_messages = inputs.get("structured_messages") texts = inputs.get("texts", []) tools = inputs.get("tools") if structured_messages: - for message in structured_messages: + filtered = _messages_since_last_assistant(structured_messages) + for message in filtered.messages: content = _normalize_content(message.get("content")) if content is None or len(content) == 0: content = "" guard_input.messages.append(_Message(role=message["role"], content=content)) + indices = filtered.indices elif texts: guard_input.messages = [_Message(role="user", content=text) for text in texts] + indices = tuple(range(len(texts))) else: verbose_proxy_logger.warning("CrowdStrike AIDR Guardrail: No messages or texts provided for input request") return None @@ -223,37 +360,36 @@ class CrowdStrikeAIDRHandler(CustomGuardrail): if tools: guard_input.tools = tools - return guard_input + return _GuardInputWithIndices(guard_input, indices) - def _build_guard_input_for_response( - self, inputs: GenericGuardrailAPIInputs, request_data: Mapping[str, Any] - ) -> Optional[_GuardInput]: + def _build_guard_input_for_response(self, inputs: GenericGuardrailAPIInputs) -> _GuardInput: output_texts: list[str] = inputs.get("texts", []) - if len(output_texts) == 0: - verbose_proxy_logger.warning("CrowdStrike AIDR Guardrail: No text in output response.") - return None - - input_messages = request_data.get("messages", []) - return _GuardInput( - messages=[ - _Message(role=role, content=content) - for (role, content) in ( - (message["role"], _normalize_content(message.get("content"))) for message in input_messages - ) - if content is not None and len(content) > 0 - ] - + [_Message(role="assistant", content=text) for text in output_texts] + messages=[_Message(role="assistant", content=text) for text in output_texts], + tools=inputs.get("tools", []), ) - def _extract_transformed_texts( + def _extract_transformed_texts(self, guard_output: _GuardInput, num_assistant_messages: int) -> list[str]: + tail = guard_output.messages[-num_assistant_messages:] if num_assistant_messages > 0 else [] + return [_extract_text_from_message(msg) for msg in tail] + + def _writeback_messages( self, - guard_output: Mapping[str, Any], - num_assistant_messages: int, - ) -> list[str]: - transformed_messages = guard_output.get("messages", []) - tail = transformed_messages[-num_assistant_messages:] if num_assistant_messages > 0 else [] - return [(_extract_text_from_content(msg.get("content")) if isinstance(msg, dict) else "") for msg in tail] + structured_messages: list[AllMessageValues], + guard_output: _GuardInput, + sent_indices: tuple[int, ...], + request_data: dict, + ) -> list[AllMessageValues] | None: + if effective_skip_system_message_for_guardrail(self) or effective_skip_tool_message_for_guardrail(self): + request_messages = request_data.get("messages") + full_messages = ( + cast("list[AllMessageValues]", request_messages) + if isinstance(request_messages, list) + else structured_messages + ) + else: + full_messages = structured_messages + return _redacted_messages(structured_messages, guard_output, sent_indices, full_messages) @log_guardrail_information @override @@ -273,15 +409,18 @@ class CrowdStrikeAIDRHandler(CustomGuardrail): tool_calls = inputs.get("tool_calls") # Build guard_input based on input_type + sent_indices: tuple[int, ...] = () if input_type == "request": - guard_input = self._build_guard_input_for_request(inputs) - if guard_input is None: + request_result = self._build_guard_input_for_request(inputs) + if request_result is None: return inputs + guard_input = request_result.guard_input + sent_indices = request_result.sent_indices event_type = "input" hook_name = "apply_guardrail (request)" else: - guard_input = self._build_guard_input_for_response(inputs, request_data) - if guard_input is None: + guard_input = self._build_guard_input_for_response(inputs) + if len(guard_input.messages) == 0: return inputs event_type = "output" hook_name = "apply_guardrail (response)" @@ -307,29 +446,20 @@ class CrowdStrikeAIDRHandler(CustomGuardrail): extra_info["user_name"] = user_email ai_guard_payload["extra_info"] = extra_info - ai_guard_response = await self._call_crowdstrike_aidr_guard(ai_guard_payload, hook_name) + result = await self._call_crowdstrike_aidr_guard(ai_guard_payload, hook_name) if "body" in request_data or "messages" in request_data: add_guardrail_to_applied_guardrails_header(request_data=request_data, guardrail_name=self.guardrail_name) - result = ai_guard_response.get("result", {}) - if not result.get("transformed"): + if not result.transformed or result.guard_output is None: return inputs - guard_output = result.get("guard_output", {}) + guard_output = result.guard_output if input_type == "request": - # For requests, all messages were in the guard_input. Extract texts - # for every message in guard_output. - all_messages = guard_output.get("messages", []) - transformed_texts = [ - _extract_text_from_content(msg.get("content") if isinstance(msg, dict) else "") for msg in all_messages - ] + transformed_texts = _merge_request_transforms(guard_output, structured_messages, texts, sent_indices) else: - # For responses, guard_input contained history + assistant messages - # appended at the end. Extract only the assistant tail. - num_assistant = len(texts) - transformed_texts = self._extract_transformed_texts(guard_output, num_assistant) + transformed_texts = self._extract_transformed_texts(guard_output, len(texts)) result_inputs: GenericGuardrailAPIInputs = {"texts": transformed_texts} if tools: @@ -337,13 +467,18 @@ class CrowdStrikeAIDRHandler(CustomGuardrail): if tool_calls: result_inputs["tool_calls"] = tool_calls if structured_messages: - result_inputs["structured_messages"] = structured_messages + rebuilt = ( + self._writeback_messages(structured_messages, guard_output, sent_indices, request_data) + if input_type == "request" + else None + ) + result_inputs["structured_messages"] = rebuilt if rebuilt is not None else structured_messages return result_inputs @override @staticmethod - def get_config_model() -> Optional[Type["GuardrailConfigModel"]]: + def get_config_model() -> type["GuardrailConfigModel"] | None: from litellm.types.proxy.guardrails.guardrail_hooks.crowdstrike_aidr import ( CrowdStrikeAIDRGuardrailConfigModel, ) diff --git a/litellm/proxy/guardrails/guardrail_hooks/custom_code/custom_code_guardrail.py b/litellm/proxy/guardrails/guardrail_hooks/custom_code/custom_code_guardrail.py index 9021f023156..245b9806e71 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/custom_code/custom_code_guardrail.py +++ b/litellm/proxy/guardrails/guardrail_hooks/custom_code/custom_code_guardrail.py @@ -36,7 +36,7 @@ Example: block when response rejects the user (input_type response only): import asyncio import threading -from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Type, cast +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Type, cast from fastapi import HTTPException @@ -121,18 +121,9 @@ class CustomCodeGuardrail(CustomGuardrail): self._compile_lock = threading.Lock() self._compile_error: Optional[str] = None - supported_event_hooks = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.pre_mcp_call, - GuardrailEventHooks.during_mcp_call, - GuardrailEventHooks.logging_only, - ] - super().__init__( guardrail_name=guardrail_name, - supported_event_hooks=supported_event_hooks, + supported_event_hooks=list(self.get_supported_event_hooks()), **kwargs, ) @@ -144,6 +135,17 @@ class CustomCodeGuardrail(CustomGuardrail): """Returns the config model for the UI.""" return CustomCodeGuardrailConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.pre_mcp_call, + GuardrailEventHooks.during_mcp_call, + GuardrailEventHooks.logging_only, + ] + def _do_compile(self) -> None: """Internal compilation method without lock. Expected to run inside _compile_lock.""" exec_globals = build_sandbox_globals() diff --git a/litellm/proxy/guardrails/guardrail_hooks/dynamoai/dynamoai.py b/litellm/proxy/guardrails/guardrail_hooks/dynamoai/dynamoai.py index b02f1030592..2db3c35866f 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/dynamoai/dynamoai.py +++ b/litellm/proxy/guardrails/guardrail_hooks/dynamoai/dynamoai.py @@ -73,12 +73,7 @@ class DynamoAIGuardrails(CustomGuardrail): self.optional_params = kwargs # Set supported event hooks - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.during_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(guardrail_name=guardrail_name, **kwargs) @@ -470,3 +465,11 @@ class DynamoAIGuardrails(CustomGuardrail): ) return DynamoAIGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.during_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/enkryptai/enkryptai.py b/litellm/proxy/guardrails/guardrail_hooks/enkryptai/enkryptai.py index f9bb13ad64e..dad8e7dd973 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/enkryptai/enkryptai.py +++ b/litellm/proxy/guardrails/guardrail_hooks/enkryptai/enkryptai.py @@ -80,12 +80,7 @@ class EnkryptAIGuardrails(CustomGuardrail): self.optional_params = kwargs # Set supported event hooks - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.during_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(guardrail_name=guardrail_name, **kwargs) @@ -500,3 +495,11 @@ class EnkryptAIGuardrails(CustomGuardrail): ) return EnkryptAIGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.during_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/__init__.py b/litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/__init__.py index 2386f80e819..63ead52baa6 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/__init__.py +++ b/litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/__init__.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Optional from litellm.types.guardrails import SupportedGuardrailIntegrations @@ -8,9 +8,23 @@ if TYPE_CHECKING: from litellm.types.guardrails import Guardrail, LitellmParams +def _get_config_value(litellm_params: Any, optional_params: Any, attribute_name: str) -> Optional[Any]: + if optional_params is not None: + value = ( + optional_params.get(attribute_name) + if isinstance(optional_params, dict) + else getattr(optional_params, attribute_name, None) + ) + if value is not None: + return value + return getattr(litellm_params, attribute_name, None) + + def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail"): import litellm + optional_params = getattr(litellm_params, "optional_params", None) + _generic_guardrail_api_callback = GenericGuardrailAPI( api_base=litellm_params.api_base, api_key=litellm_params.api_key, @@ -22,6 +36,8 @@ def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail" guardrail_name=guardrail.get("guardrail_name", ""), event_hook=litellm_params.mode, default_on=litellm_params.default_on, + streaming_end_of_stream_only=_get_config_value(litellm_params, optional_params, "streaming_end_of_stream_only"), + streaming_sampling_rate=_get_config_value(litellm_params, optional_params, "streaming_sampling_rate"), ) litellm.logging_callback_manager.add_litellm_callback(_generic_guardrail_api_callback) diff --git a/litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/generic_guardrail_api.py b/litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/generic_guardrail_api.py index df80ea09de0..e29d6e56353 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/generic_guardrail_api.py +++ b/litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/generic_guardrail_api.py @@ -7,7 +7,7 @@ import fnmatch import os -from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Set +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Set import httpx @@ -33,6 +33,7 @@ from litellm.types.utils import GenericGuardrailAPIInputs if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj + from litellm.types.proxy.guardrails.guardrail_hooks.base import GuardrailConfigModel GUARDRAIL_NAME = "generic_guardrail_api" @@ -178,6 +179,8 @@ class GenericGuardrailAPI(CustomGuardrail): unreachable_fallback: Literal["fail_closed", "fail_open"] = "fail_closed", fail_on_error: Optional[bool] = True, extra_headers: Optional[list] = None, + streaming_end_of_stream_only: Optional[bool] = None, + streaming_sampling_rate: Optional[int] = None, **kwargs, ): self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) @@ -209,13 +212,17 @@ class GenericGuardrailAPI(CustomGuardrail): self.fail_on_error: bool = True if fail_on_error is None else fail_on_error + # Read by UnifiedLLMGuardrails.async_post_call_streaming_iterator_hook + # via getattr(guardrail_to_apply, "streaming_*", default). + self.streaming_end_of_stream_only: bool = ( + False if streaming_end_of_stream_only is None else streaming_end_of_stream_only + ) + if streaming_sampling_rate is not None and streaming_sampling_rate < 1: + raise ValueError(f"streaming_sampling_rate must be >= 1 (got {streaming_sampling_rate})") + self.streaming_sampling_rate: int = 5 if streaming_sampling_rate is None else streaming_sampling_rate + # Set supported event hooks - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.during_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) @@ -470,3 +477,19 @@ class GenericGuardrailAPI(CustomGuardrail): return self._handle_guardrail_request_error(e, inputs, input_type, logging_obj) except Exception as e: return self._handle_guardrail_request_error(e, inputs, input_type, logging_obj, is_unreachable=False) + + @staticmethod + def get_config_model() -> Optional[type["GuardrailConfigModel"]]: + from litellm.types.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + GenericGuardrailAPIConfigModel, + ) + + return GenericGuardrailAPIConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.during_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py b/litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py index a14d2fc8608..72409a61c30 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py +++ b/litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py @@ -119,18 +119,20 @@ class GraySwanGuardrail(CustomGuardrail): streaming_sampling_rate, ) - supported_event_hooks = [ + super().__init__( + guardrail_name=guardrail_name, + supported_event_hooks=list(self.get_supported_event_hooks()), + **kwargs, + ) + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ GuardrailEventHooks.pre_call, GuardrailEventHooks.during_call, GuardrailEventHooks.post_call, ] - super().__init__( - guardrail_name=guardrail_name, - supported_event_hooks=supported_event_hooks, - **kwargs, - ) - # ------------------------------------------------------------------ # Debug override to trace post_call issues # ------------------------------------------------------------------ @@ -213,7 +215,7 @@ class GraySwanGuardrail(CustomGuardrail): verbose_proxy_logger.debug("Gray Swan Guardrail: dynamic extra_body=%s", safe_dumps(dynamic_body)) # Prepare and send payload - payload = self._prepare_payload(messages, dynamic_body, request_data) + payload = self._prepare_payload(messages, dynamic_body, request_data, logging_obj) if payload is None: return inputs @@ -502,10 +504,38 @@ class GraySwanGuardrail(CustomGuardrail): "grayswan-api-key": self.api_key, } + def _extract_inbound_headers( + self, + request_data: dict, + logging_obj: Optional["LiteLLMLoggingObj"] = None, + ) -> Optional[dict[str, str]]: + headers = (request_data.get("proxy_server_request") or {}).get("headers") + if not headers: + headers = request_data.get("headers") + if not headers: + headers = (request_data.get("metadata") or {}).get("headers") + if not headers and logging_obj and getattr(logging_obj, "model_call_details", None): + headers = ( + (logging_obj.model_call_details or {}).get("litellm_params", {}).get("metadata", {}).get("headers") + ) + if not isinstance(headers, dict): + return None + + forwarded_header_names = ("shade_scan_id",) + forwarded_headers = {} + for key, value in headers.items(): + if str(key).lower() in forwarded_header_names: + forwarded_headers[str(key)] = str(value) + return forwarded_headers or None + def _prepare_payload( - self, messages: List[Dict[str, str]], dynamic_body: dict, request_data: dict - ) -> Optional[Dict[str, Any]]: - payload: Dict[str, Any] = {"messages": messages} + self, + messages: list[dict[str, str]], + dynamic_body: dict, + request_data: dict, + logging_obj: Optional["LiteLLMLoggingObj"] = None, + ) -> Optional[dict[str, Any]]: + payload: dict[str, Any] = {"messages": messages} categories = dynamic_body.get("categories") or self.categories if categories: @@ -523,10 +553,16 @@ class GraySwanGuardrail(CustomGuardrail): if "metadata" in dynamic_body: payload["metadata"] = dynamic_body["metadata"] + inbound_headers = self._extract_inbound_headers(request_data, logging_obj) + litellm_metadata = request_data.get("litellm_metadata") - if isinstance(litellm_metadata, dict) and litellm_metadata: - cleaned_litellm_metadata = dict(litellm_metadata) - # cleaned_litellm_metadata.pop("user_api_key_auth", None) + cleaned_litellm_metadata = dict(litellm_metadata) if isinstance(litellm_metadata, dict) else {} + if inbound_headers: + existing_headers = cleaned_litellm_metadata.get("headers") + cleaned_litellm_metadata["headers"] = ( + {**existing_headers, **inbound_headers} if isinstance(existing_headers, dict) else inbound_headers + ) + if cleaned_litellm_metadata: sanitized = safe_json_loads(safe_dumps(cleaned_litellm_metadata), default={}) if isinstance(sanitized, dict) and sanitized: payload["litellm_metadata"] = sanitized diff --git a/litellm/proxy/guardrails/guardrail_hooks/guardrails_ai/guardrails_ai.py b/litellm/proxy/guardrails/guardrail_hooks/guardrails_ai/guardrails_ai.py index 71c426a3367..a7c94a4742d 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/guardrails_ai/guardrails_ai.py +++ b/litellm/proxy/guardrails/guardrail_hooks/guardrails_ai/guardrails_ai.py @@ -74,12 +74,7 @@ class GuardrailsAI(CustomGuardrail): self.guardrails_ai_guard_name = guard_name self.optional_params = kwargs self.guardrails_ai_api_input_format = guardrails_ai_api_input_format - supported_event_hooks = [ - GuardrailEventHooks.post_call, - GuardrailEventHooks.pre_call, - GuardrailEventHooks.logging_only, - ] - super().__init__(supported_event_hooks=supported_event_hooks, **kwargs) + super().__init__(supported_event_hooks=list(self.get_supported_event_hooks()), **kwargs) async def make_guardrails_ai_api_request(self, llm_output: str, request_data: dict) -> GuardrailsAIResponse: from httpx import URL @@ -240,3 +235,11 @@ class GuardrailsAI(CustomGuardrail): ) return GuardrailsAIGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.post_call, + GuardrailEventHooks.pre_call, + GuardrailEventHooks.logging_only, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/headroom/__init__.py b/litellm/proxy/guardrails/guardrail_hooks/headroom/__init__.py index 9b7934b7705..d2b8a979261 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/headroom/__init__.py +++ b/litellm/proxy/guardrails/guardrail_hooks/headroom/__init__.py @@ -34,6 +34,7 @@ def initialize_guardrail(litellm_params: LitellmParams, guardrail: Guardrail) -> guardrail_name=guardrail["guardrail_name"], event_hook=_coerce_event_hook(litellm_params.mode), default_on=litellm_params.default_on or False, + unreachable_fallback=litellm_params.unreachable_fallback, ) litellm.logging_callback_manager.add_litellm_callback( # pyright: ignore[reportUnknownMemberType] _callback diff --git a/litellm/proxy/guardrails/guardrail_hooks/headroom/headroom.py b/litellm/proxy/guardrails/guardrail_hooks/headroom/headroom.py index 2228ccf3997..e6f76b67c3c 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/headroom/headroom.py +++ b/litellm/proxy/guardrails/guardrail_hooks/headroom/headroom.py @@ -1,9 +1,15 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Literal +import json +import re +import time +import uuid +from typing import TYPE_CHECKING, Any, List, Literal, Optional import httpx from fastapi import HTTPException + +import litellm from httpx import Response as HttpxResponse from typing_extensions import TypeGuard @@ -12,12 +18,18 @@ from litellm.integrations.custom_guardrail import ( CustomGuardrail, log_guardrail_information, ) +from litellm.litellm_core_utils.prompt_templates.factory import ( + get_attribute_or_key, + get_tool_calls_from_response, + has_tool_with_name, +) from litellm.llms.custom_httpx.http_handler import ( get_async_httpx_client, # pyright: ignore[reportUnknownVariableType] httpxSpecialProvider, ) from litellm.secret_managers.main import get_secret_str from litellm.types.guardrails import GuardrailEventHooks, Mode +from litellm.types.integrations.custom_logger import AgenticLoopPlan, AgenticLoopRequestPatch from litellm.types.utils import GenericGuardrailAPIInputs if TYPE_CHECKING: @@ -25,6 +37,9 @@ if TYPE_CHECKING: from litellm.types.proxy.guardrails.guardrail_hooks.base import GuardrailConfigModel BYPASS_HEADER = "x-headroom-bypass" +HEADROOM_RETRIEVE_TOOL_NAME = "headroom_retrieve" +_HASH_PATTERN = re.compile(r"hash=([a-f0-9]{24})") +_HASH_CACHE_TTL_SECONDS = 15 * 60 def _is_str_object_dict(value: object) -> TypeGuard[dict[str, object]]: # guard-ok: isinstance narrows correctly; predicate is trivially correct # fmt: skip @@ -35,7 +50,171 @@ def _is_object_list(value: object) -> TypeGuard[list[object]]: # guard-ok: isin return isinstance(value, list) +def extract_hashes_from_messages(messages: list[dict[str, object]]) -> list[str]: + hashes: list[str] = [] + for msg in messages: + content = msg.get("content") + if isinstance(content, str): + hashes.extend(_HASH_PATTERN.findall(content)) + elif isinstance(content, list): + for block in content: + if isinstance(block, dict): + text = block.get("text") + if isinstance(text, str): + hashes.extend(_HASH_PATTERN.findall(text)) + return hashes + + +def _build_headroom_retrieve_tool() -> dict[str, object]: + return { + "type": "function", + "function": { + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "description": ( + "Retrieve original content that was compressed by Headroom. " + "Call this when you encounter a compression marker containing a hash." + ), + "parameters": { + "type": "object", + "properties": { + "hash": { + "type": "string", + "description": "The 24-character hex hash from the compression marker.", + }, + "query": { + "type": "string", + "description": "Optional search query for BM25-ranked retrieval.", + }, + }, + "required": ["hash"], + }, + }, + } + + +def _resolve_call_id(logging_obj: object, request_state: dict[str, object]) -> Optional[str]: + """Resolve the litellm_call_id shared by a request's pre-call hook and its + agentic-loop hooks, so CCR hash validation can be scoped per call instead + of trusting any hash-shaped string that shows up in message text.""" + logging_call_id = getattr(logging_obj, "litellm_call_id", None) + if isinstance(logging_call_id, str) and logging_call_id: + return logging_call_id + kwargs_call_id = request_state.get("litellm_call_id") + return kwargs_call_id if isinstance(kwargs_call_id, str) else None + + +def has_headroom_retrieve_tool(tools: object) -> bool: + return has_tool_with_name(tools, HEADROOM_RETRIEVE_TOOL_NAME) + + +def _extract_headroom_tool_calls(response: object) -> list[dict[str, object]]: + return [ + {"id": tc["id"], "type": "function", "name": tc["name"], "arguments": tc["arguments"]} + for tc in get_tool_calls_from_response(response) + if tc["name"] == HEADROOM_RETRIEVE_TOOL_NAME + ] + + +def _build_assistant_message_from_response(response: object) -> dict[str, object]: + choices = getattr(response, "choices", None) + if not isinstance(choices, list) or not choices: + return {"role": "assistant", "content": None, "tool_calls": []} + message = getattr(choices[0], "message", None) + if message is None: + return {"role": "assistant", "content": None, "tool_calls": []} + content = getattr(message, "content", None) + tool_calls = getattr(message, "tool_calls", None) + raw_tool_calls: list[dict[str, object]] = [] + if isinstance(tool_calls, list): + for tc in tool_calls: + fn = getattr(tc, "function", None) + raw_tool_calls.append( + { + "id": getattr(tc, "id", None), + "type": "function", + "function": { + "name": getattr(fn, "name", None) if fn else None, + "arguments": getattr(fn, "arguments", "{}") if fn else "{}", + }, + } + ) + return {"role": "assistant", "content": content, "tool_calls": raw_tool_calls} + + +def _is_responses_api_response(response: object) -> bool: + # Real response objects can be plain dicts at runtime (e.g. TypedDict-based + # response types), so getattr alone would silently miss the key -- use the + # same dict-or-object accessor as the tool-call extractors. + return isinstance(get_attribute_or_key(response, "output", None), list) + + +def _is_anthropic_messages_response(response: object) -> bool: + return isinstance(get_attribute_or_key(response, "content", None), list) + + +def _build_anthropic_followup_messages( + retrieved: list[tuple[dict[str, object], str]], +) -> list[dict[str, object]]: + """Build Anthropic Messages API follow-up messages for a tool round-trip. + + Anthropic requires the tool_use block to be echoed back in an assistant + message, paired with a tool_result block in a user message keyed by the + same tool_use_id -- it does not accept chat-style tool-role messages. + """ + assistant_message: dict[str, object] = { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": tool_call.get("id"), + "name": tool_call.get("name"), + "input": tool_call.get("arguments", {}), + } + for tool_call, _ in retrieved + ], + } + user_message: dict[str, object] = { + "role": "user", + "content": [ + {"type": "tool_result", "tool_use_id": tool_call.get("id"), "content": content} + for tool_call, content in retrieved + ], + } + return [assistant_message, user_message] + + +def _build_responses_followup_items( + retrieved: list[tuple[dict[str, object], str]], +) -> list[dict[str, object]]: + """Build Responses API input items for a tool round-trip. + + The Responses API does not accept chat-style assistant/tool messages as + follow-up input; it requires the model's function_call to be echoed back + paired with a function_call_output keyed by the same call_id. + """ + items: list[dict[str, object]] = [] + for tool_call, content in retrieved: + call_id = tool_call.get("id") + items.append( + { + "type": "function_call", + "call_id": call_id, + "name": tool_call.get("name"), + "arguments": json.dumps(tool_call.get("arguments", {})), + } + ) + items.append({"type": "function_call_output", "call_id": call_id, "output": content}) + return items + + class HeadroomGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, api_base: str | None = None, @@ -44,6 +223,7 @@ class HeadroomGuardrail(CustomGuardrail): guardrail_name: str | None = None, event_hook: GuardrailEventHooks | list[GuardrailEventHooks] | Mode | None = None, default_on: bool = False, + unreachable_fallback: str | None = None, ): self.headroom_api_base = (api_base or get_secret_str("HEADROOM_API_BASE") or "").rstrip("/") if not self.headroom_api_base: @@ -53,13 +233,18 @@ class HeadroomGuardrail(CustomGuardrail): ) self.headroom_api_key = api_key or get_secret_str("HEADROOM_API_KEY") self.headroom_model = model + self.unreachable_fallback: Literal["fail_closed", "fail_open"] = ( + "fail_open" if unreachable_fallback == "fail_open" else "fail_closed" + ) self.async_handler = get_async_httpx_client( llm_provider=httpxSpecialProvider.GuardrailCallback, ) + self._issued_hashes_by_call_id: dict[str, tuple[frozenset[str], float]] = {} super().__init__( # pyright: ignore[reportUnknownMemberType] guardrail_name=guardrail_name, event_hook=event_hook, default_on=default_on, + supported_event_hooks=list(self.get_supported_event_hooks()), ) def _should_bypass(self, request_data: dict) -> bool: @@ -72,87 +257,138 @@ class HeadroomGuardrail(CustomGuardrail): value = headers.get(BYPASS_HEADER) return str(value).lower() == "true" + def _request_headers(self) -> dict[str, str]: + headers: dict[str, str] = {"Content-Type": "application/json"} + if self.headroom_api_key: + headers["Authorization"] = f"Bearer {self.headroom_api_key}" + return headers + + def _prune_expired_hashes(self) -> None: + now = time.monotonic() + self._issued_hashes_by_call_id = { + call_id: (hashes, expiry) + for call_id, (hashes, expiry) in self._issued_hashes_by_call_id.items() + if expiry > now + } + + def _handle_compress_failure( + self, + messages: list[dict[str, object]], + error: str, + detail: dict[str, object], + ) -> list[dict[str, object]]: + if self.unreachable_fallback == "fail_open": + verbose_proxy_logger.critical( + "Headroom: %s; fail_open configured, forwarding request uncompressed. detail=%s", + error, + detail, + ) + return messages + raise HTTPException(status_code=502, detail={"error": error, **detail}) + async def _call_compress( self, messages: list[dict[str, object]], model: str | None, - ) -> list[dict[str, object]]: + ) -> tuple[list[dict[str, object]], bool, dict[str, object]]: payload: dict[str, object] = {"messages": messages} if model: payload["model"] = model - request_headers: dict[str, str] = {"Content-Type": "application/json"} - if self.headroom_api_key: - request_headers["Authorization"] = f"Bearer {self.headroom_api_key}" - try: raw_response: HttpxResponse | None = await self.async_handler.post( # pyright: ignore[reportUnknownMemberType] url=f"{self.headroom_api_base}/v1/compress", json=payload, - headers=request_headers, + headers=self._request_headers(), + ) + except httpx.HTTPStatusError as e: + return ( + self._handle_compress_failure( + messages, + "Headroom compression service returned an error", + {"status_code": e.response.status_code, "body": e.response.text}, + ), + False, + {}, + ) + except (httpx.ConnectError, httpx.TimeoutException, httpx.TransportError, litellm.Timeout) as e: + return ( + self._handle_compress_failure( + messages, + "Headroom compression service unreachable", + {"detail": str(e)}, + ), + False, + {}, ) - except (httpx.ConnectError, httpx.TimeoutException, httpx.TransportError) as e: - raise HTTPException( - status_code=502, - detail={ - "error": "Headroom compression service unreachable", - "detail": str(e), - }, - ) from e if raw_response is None: - raise HTTPException( - status_code=502, - detail={"error": "Headroom compression service returned no response"}, + return ( + self._handle_compress_failure( + messages, + "Headroom compression service returned no response", + {}, + ), + False, + {}, ) response: HttpxResponse = raw_response if response.status_code != 200: - raise HTTPException( - status_code=502, - detail={ - "error": "Headroom compression service returned an error", - "status_code": response.status_code, - "body": response.text, - }, + return ( + self._handle_compress_failure( + messages, + "Headroom compression service returned an error", + {"status_code": response.status_code, "body": response.text}, + ), + False, + {}, ) try: body: object = response.json() - except Exception: - raise HTTPException( - status_code=502, - detail={ - "error": "Headroom compression service returned non-JSON response", - "body": response.text[:500], - }, + except ValueError: + return ( + self._handle_compress_failure( + messages, + "Headroom compression service returned non-JSON response", + {"body": response.text[:500]}, + ), + False, + {}, ) if not _is_str_object_dict(body): - raise HTTPException( - status_code=502, - detail={ - "error": "Headroom compression service returned unexpected response shape", - "body": response.text[:500], - }, + return ( + self._handle_compress_failure( + messages, + "Headroom compression service returned unexpected response shape", + {"body": response.text[:500]}, + ), + False, + {}, ) compressed_messages = body.get("messages") if not _is_object_list(compressed_messages): - raise HTTPException( - status_code=502, - detail={ - "error": "Headroom compression service response missing 'messages'", - "body": response.text, - }, + return ( + self._handle_compress_failure( + messages, + "Headroom compression service response missing 'messages'", + {"body": response.text}, + ), + False, + {}, ) filtered = [item for item in compressed_messages if _is_str_object_dict(item)] if not filtered: - raise HTTPException( - status_code=502, - detail={ - "error": "Headroom compression service returned empty message list", - "body": response.text, - }, + return ( + self._handle_compress_failure( + messages, + "Headroom compression service returned empty message list", + {"body": response.text}, + ), + False, + {}, ) verbose_proxy_logger.debug( @@ -161,7 +397,57 @@ class HeadroomGuardrail(CustomGuardrail): body.get("tokens_after", "?"), body.get("compression_ratio", 0), ) - return filtered + + stats = { + key: body[key] + for key in ( + "tokens_before", + "tokens_after", + "tokens_saved", + "compression_ratio", + "transforms_applied", + ) + if key in body + } + return filtered, True, stats + + async def _call_retrieve(self, hash_value: str, query: str | None = None) -> str: + params: dict[str, str] = {} + if query: + params["query"] = query + + try: + raw_response: HttpxResponse | None = await self.async_handler.get( # pyright: ignore[reportUnknownMemberType] + url=f"{self.headroom_api_base}/v1/retrieve/{hash_value}", + params=params, + headers=self._request_headers(), + ) + except (httpx.ConnectError, httpx.TimeoutException, httpx.TransportError, litellm.Timeout) as e: + verbose_proxy_logger.warning("Headroom: retrieve failed for hash=%s: %s", hash_value, e) + return f"[Headroom: retrieval failed for hash={hash_value}]" + + if raw_response is None or raw_response.status_code == 404: + return f"[Headroom: hash={hash_value} not found or expired]" + + if raw_response.status_code != 200: + verbose_proxy_logger.warning( + "Headroom: retrieve returned %s for hash=%s", + raw_response.status_code, + hash_value, + ) + return f"[Headroom: retrieval error {raw_response.status_code} for hash={hash_value}]" + + try: + body: object = raw_response.json() + except ValueError: + return raw_response.text + + if _is_str_object_dict(body): + original_content = body.get("original_content") + if isinstance(original_content, str): + return original_content + + return str(body) @log_guardrail_information async def apply_guardrail( @@ -187,12 +473,147 @@ class HeadroomGuardrail(CustomGuardrail): return inputs model = self.headroom_model or request_data.get("model") - compressed = await self._call_compress( + start_time = time.time() + compressed, compression_succeeded, stats = await self._call_compress( messages=messages, model=model if isinstance(model, str) else None, ) + end_time = time.time() - return {**inputs, "structured_messages": compressed} # pyright: ignore[reportReturnType] + if not compression_succeeded: + return {**inputs, "structured_messages": compressed} # pyright: ignore[reportReturnType] + + self.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response=stats, + request_data=request_data, + guardrail_status="success", + guardrail_provider="headroom", + start_time=start_time, + end_time=end_time, + duration=end_time - start_time, + ) + + hashes = extract_hashes_from_messages(compressed) + if not hashes: + return {**inputs, "structured_messages": compressed} # pyright: ignore[reportReturnType] + + self._prune_expired_hashes() + call_id = _resolve_call_id(logging_obj, request_data) + if not call_id: + call_id = str(uuid.uuid4()) + request_data["litellm_call_id"] = call_id + self._issued_hashes_by_call_id[call_id] = (frozenset(hashes), time.monotonic() + _HASH_CACHE_TTL_SECONDS) + + existing_tools = inputs.get("tools") + retrieve_tool = _build_headroom_retrieve_tool() + if isinstance(existing_tools, list) and not has_headroom_retrieve_tool(existing_tools): + merged_tools: list[object] = list(existing_tools) + [retrieve_tool] + elif existing_tools is None: + merged_tools = [retrieve_tool] + else: + merged_tools = list(existing_tools) if isinstance(existing_tools, list) else [retrieve_tool] + + return {**inputs, "structured_messages": compressed, "tools": merged_tools} # pyright: ignore[reportReturnType] + + async def async_should_run_agentic_loop( + self, + response: Any, + model: str, + messages: list[dict], + tools: Optional[list[dict]], + stream: bool, + custom_llm_provider: str, + kwargs: dict, + ) -> tuple[bool, dict]: + if not has_headroom_retrieve_tool(tools): + return False, {} + + tool_calls = _extract_headroom_tool_calls(response) + if not tool_calls: + return False, {} + + return True, {"tool_calls": tool_calls} + + async def async_build_agentic_loop_plan( + self, + tools: dict, + model: str, + messages: list[dict], + response: Any, + anthropic_messages_provider_config: Any, + anthropic_messages_optional_request_params: dict, + logging_obj: Any, + stream: bool, + kwargs: dict, + ) -> AgenticLoopPlan: + tool_calls: list[dict[str, object]] = tools.get("tool_calls", []) # type: ignore[assignment] + + self._prune_expired_hashes() + call_id = _resolve_call_id(logging_obj, kwargs) + valid_hashes = self._issued_hashes_by_call_id.get(call_id, (frozenset(), 0.0))[0] if call_id else frozenset() + + retrieved: list[tuple[dict[str, object], str]] = [] + for tc in tool_calls: + arguments = tc.get("arguments", {}) + hash_value = arguments.get("hash", "") if isinstance(arguments, dict) else "" + query = arguments.get("query") if isinstance(arguments, dict) else None + # A hash is only honored if it was issued by *this request's own* + # Headroom /v1/compress call, scoped by litellm_call_id. Scoping by + # message text alone is forgeable -- an attacker can plant a + # hash-shaped string in their own prompt, and a hash issued for one + # request would validate for any other request that echoes it back. + if str(hash_value) not in valid_hashes: + verbose_proxy_logger.warning( + "Headroom CCR: rejecting hash=%s not produced by current request compression", + hash_value, + ) + content = f"[Headroom: hash={hash_value} was not produced by the current request]" + else: + content = await self._call_retrieve( + hash_value=str(hash_value), + query=str(query) if query else None, + ) + verbose_proxy_logger.debug("Headroom CCR: retrieved hash=%s (%d chars)", hash_value, len(content)) + retrieved.append((tc, content)) + + if _is_responses_api_response(response): + follow_up_messages = list(messages) + _build_responses_followup_items(retrieved) + elif _is_anthropic_messages_response(response): + follow_up_messages = list(messages) + _build_anthropic_followup_messages(retrieved) + else: + assistant_message = _build_assistant_message_from_response(response) + tool_results = [ + {"role": "tool", "tool_call_id": tc.get("id"), "content": content} for tc, content in retrieved + ] + follow_up_messages = list(messages) + [assistant_message] + tool_results + + max_tokens: Optional[int] = anthropic_messages_optional_request_params.get("max_tokens") or kwargs.get( + "max_tokens" + ) + optional_params_without_max_tokens = { + k: v for k, v in anthropic_messages_optional_request_params.items() if k != "max_tokens" + } + + full_model_name = model + if logging_obj is not None: + agentic_params = getattr(logging_obj, "model_call_details", {}).get("agentic_loop_params", {}) + candidate = agentic_params.get("model", model) + if isinstance(candidate, str) and candidate: + full_model_name = candidate + + return AgenticLoopPlan( + run_agentic_loop=True, + request_patch=AgenticLoopRequestPatch( + model=full_model_name, + messages=follow_up_messages, + max_tokens=max_tokens, + optional_params=optional_params_without_max_tokens, + kwargs={ + k: v for k, v in kwargs.items() if not k.startswith("_headroom") and k != "litellm_logging_obj" + }, + ), + metadata={"tool_type": "headroom_ccr"}, + ) @staticmethod def get_config_model() -> type[GuardrailConfigModel[object]] | None: diff --git a/litellm/proxy/guardrails/guardrail_hooks/hiddenlayer/hiddenlayer.py b/litellm/proxy/guardrails/guardrail_hooks/hiddenlayer/hiddenlayer.py index 287f108b070..1566c90ac0c 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/hiddenlayer/hiddenlayer.py +++ b/litellm/proxy/guardrails/guardrail_hooks/hiddenlayer/hiddenlayer.py @@ -3,7 +3,7 @@ from uuid import uuid4 import httpx import os -from typing import TYPE_CHECKING, Any, Literal, Optional, Type +from typing import TYPE_CHECKING, Any, List, Literal, Optional, Type from urllib.parse import urlparse import requests @@ -21,6 +21,7 @@ from litellm.llms.custom_httpx.http_handler import ( get_async_httpx_client, httpxSpecialProvider, ) +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.proxy.guardrails.guardrail_hooks.hiddenlayer import ( HiddenlayerAction, HiddenlayerMessages, @@ -63,6 +64,13 @@ def _get_jwt(auth_url, api_id, api_key): class HiddenlayerGuardrail(CustomGuardrail): """Custom guardrail wrapper for HiddenLayer's safety checks.""" + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, api_id: Optional[str] = None, @@ -71,6 +79,7 @@ class HiddenlayerGuardrail(CustomGuardrail): auth_url: Optional[str] = None, **kwargs: Any, ) -> None: + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) self.hiddenlayer_client_id = api_id or os.getenv("HIDDENLAYER_CLIENT_ID") self.hiddenlayer_client_secret = api_key or os.getenv("HIDDENLAYER_CLIENT_SECRET") self.api_base = api_base or os.getenv("HIDDENLAYER_API_BASE") or "https://api.hiddenlayer.ai" diff --git a/litellm/proxy/guardrails/guardrail_hooks/ibm_guardrails/ibm_detector.py b/litellm/proxy/guardrails/guardrail_hooks/ibm_guardrails/ibm_detector.py index 27ba9f3467c..955ec0c0d35 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/ibm_guardrails/ibm_detector.py +++ b/litellm/proxy/guardrails/guardrail_hooks/ibm_guardrails/ibm_detector.py @@ -86,12 +86,7 @@ class IBMGuardrailDetector(CustomGuardrail): self.optional_params = kwargs # Set supported event hooks - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.during_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(guardrail_name=guardrail_name, **kwargs) @@ -669,3 +664,11 @@ class IBMGuardrailDetector(CustomGuardrail): ) return IBMDetectorGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.during_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/javelin/javelin.py b/litellm/proxy/guardrails/guardrail_hooks/javelin/javelin.py index 4575504feb6..4da3e75d03e 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/javelin/javelin.py +++ b/litellm/proxy/guardrails/guardrail_hooks/javelin/javelin.py @@ -25,6 +25,12 @@ if TYPE_CHECKING: class JavelinGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + ] + def __init__( self, api_key: Optional[str] = None, @@ -72,6 +78,7 @@ class JavelinGuardrail(CustomGuardrail): self.api_version, ) + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(guardrail_name=guardrail_name, default_on=default_on, **kwargs) async def call_javelin_guard( diff --git a/litellm/proxy/guardrails/guardrail_hooks/lakera_ai.py b/litellm/proxy/guardrails/guardrail_hooks/lakera_ai.py index 3804d1cb93f..d3360bbe641 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/lakera_ai.py +++ b/litellm/proxy/guardrails/guardrail_hooks/lakera_ai.py @@ -30,6 +30,7 @@ from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.guardrails.guardrail_helpers import should_proceed_based_on_metadata from litellm.secret_managers.main import get_secret from litellm.types.guardrails import ( + GuardrailEventHooks, GuardrailItem, LakeraCategoryThresholds, Role, @@ -46,6 +47,13 @@ INPUT_POSITIONING_MAP = { class lakeraAI_Moderation(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + ] + def __init__( self, moderation_check: Literal["pre_call", "in_parallel"] = "in_parallel", @@ -54,6 +62,7 @@ class lakeraAI_Moderation(CustomGuardrail): api_key: Optional[str] = None, **kwargs, ): + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) self.lakera_api_key = api_key or os.environ.get("LAKERA_API_KEY") or "" self.moderation_check = moderation_check diff --git a/litellm/proxy/guardrails/guardrail_hooks/lakera_ai_v2.py b/litellm/proxy/guardrails/guardrail_hooks/lakera_ai_v2.py index e79a3e7b3d8..76603579d6c 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/lakera_ai_v2.py +++ b/litellm/proxy/guardrails/guardrail_hooks/lakera_ai_v2.py @@ -29,6 +29,14 @@ from litellm.types.utils import CallTypesLiteral, GuardrailStatus, ModelResponse class LakeraAIGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, api_key: Optional[str] = None, @@ -68,6 +76,7 @@ class LakeraAIGuardrail(CustomGuardrail): self.metadata: Optional[Dict] = metadata self.dev_info: Optional[bool] = dev_info self.on_flagged = on_flagged or "block" + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) async def call_v2_guard( diff --git a/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py b/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py index 36fbd73c5bd..9c4cef2f06b 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py +++ b/litellm/proxy/guardrails/guardrail_hooks/lasso/lasso.py @@ -93,6 +93,14 @@ class LassoGuardrail(CustomGuardrail): through the Lasso Security API. """ + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, lasso_api_key: Optional[str] = None, @@ -103,6 +111,7 @@ class LassoGuardrail(CustomGuardrail): mask: Optional[bool] = False, **kwargs, ): + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) self.lasso_api_key = lasso_api_key or api_key or os.environ.get("LASSO_API_KEY") self.user_id = user_id or os.environ.get("LASSO_USER_ID") diff --git a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py index ede36b23216..dda4b0236eb 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py @@ -29,9 +29,11 @@ from fastapi import HTTPException from litellm import Router from litellm._logging import verbose_proxy_logger +from litellm.constants import DEFAULT_MAX_RECURSE_DEPTH from litellm.integrations.custom_guardrail import CustomGuardrail from litellm.proxy._types import UserAPIKeyAuth from litellm.types.utils import ( + CallTypes, GenericGuardrailAPIInputs, GuardrailStatus, GuardrailTracingDetail, @@ -179,12 +181,7 @@ class ContentFilterGuardrail(CustomGuardrail): super().__init__( guardrail_name=guardrail_name, - supported_event_hooks=[ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.realtime_input_transcription, - ], + supported_event_hooks=list(self.get_supported_event_hooks()), event_hook=event_hook or GuardrailEventHooks.pre_call, default_on=default_on, **kwargs, @@ -1688,6 +1685,66 @@ class ContentFilterGuardrail(CustomGuardrail): tracing_detail=GuardrailTracingDetail(**tracing_kw), # type: ignore[typeddict-item] ) + @staticmethod + def _get_mcp_tool_name(request_data: dict) -> str | None: + raw_name: object = request_data.get("mcp_tool_name") + if isinstance(raw_name, str) and raw_name: + return raw_name + return None + + def _assert_mcp_argument_label_clean(self, text: str, detections: list[ContentFilterDetection]) -> None: + if self._filter_single_text(text, detections=detections) != text: + raise HTTPException( + status_code=400, + detail={ + "error": "Content blocked: MCP tool call argument matched a masking rule on a non-rewritable field" + }, + ) + + def _filter_mcp_argument_value( + self, value: object, detections: list[ContentFilterDetection], depth: int = 0 + ) -> object: + if depth > DEFAULT_MAX_RECURSE_DEPTH: + raise HTTPException( + status_code=400, + detail={"error": "Content blocked: MCP tool call arguments exceed the maximum nesting depth"}, + ) + if isinstance(value, str): + return self._filter_single_text(value, detections=detections) + if isinstance(value, (int, float)) and not isinstance(value, bool): + self._assert_mcp_argument_label_clean(str(value), detections) + return value + if isinstance(value, dict): + for key in value: + if isinstance(key, str): + self._assert_mcp_argument_label_clean(key, detections) + return {key: self._filter_mcp_argument_value(item, detections, depth + 1) for key, item in value.items()} + if isinstance(value, list): + return [self._filter_mcp_argument_value(item, detections, depth + 1) for item in value] + return value + + def _scan_mcp_tool_call_arguments( + self, + request_data: dict, + detections: list[ContentFilterDetection], + logging_obj: Optional["LiteLLMLoggingObj"] = None, + ) -> None: + if not self._event_hook_is_event_type(GuardrailEventHooks.pre_mcp_call): + return + call_type: object = getattr(logging_obj, "call_type", None) + if logging_obj is not None and call_type != CallTypes.call_mcp_tool.value: + return + if self._get_mcp_tool_name(request_data) is None: + return + raw_arguments: object = request_data.get("mcp_arguments") + if not isinstance(raw_arguments, dict) or not raw_arguments: + return + filtered_arguments = self._filter_mcp_argument_value(raw_arguments, detections) + if filtered_arguments == raw_arguments: + return + request_data["mcp_arguments"] = filtered_arguments + request_data["modified_arguments"] = filtered_arguments + async def apply_guardrail( self, inputs: "GenericGuardrailAPIInputs", @@ -1742,6 +1799,11 @@ class ContentFilterGuardrail(CustomGuardrail): verbose_proxy_logger.debug("ContentFilterGuardrail: Guardrail applied successfully") inputs["texts"] = processed_texts + if input_type == "request": + self._scan_mcp_tool_call_arguments( + request_data=request_data, detections=detections, logging_obj=logging_obj + ) + # Count masked entities by type self._count_masked_entities(detections, masked_entity_count) @@ -1900,3 +1962,13 @@ class ContentFilterGuardrail(CustomGuardrail): ) return LitellmContentFilterGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.realtime_input_transcription, + GuardrailEventHooks.pre_mcp_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/llm_as_a_judge/__init__.py b/litellm/proxy/guardrails/guardrail_hooks/llm_as_a_judge/__init__.py index 5445425a1d1..a17ca07ae2e 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/llm_as_a_judge/__init__.py +++ b/litellm/proxy/guardrails/guardrail_hooks/llm_as_a_judge/__init__.py @@ -9,7 +9,7 @@ from fastapi import HTTPException from litellm._logging import verbose_logger from litellm.integrations.custom_guardrail import CustomGuardrail -from litellm.types.guardrails import GuardrailEventHooks +from litellm.types.guardrails import GuardrailEventHooks, SupportedGuardrailIntegrations from litellm.types.utils import GenericGuardrailAPIInputs, GuardrailStatus if TYPE_CHECKING: @@ -105,7 +105,7 @@ class LLMAsAJudgeGuardrail(CustomGuardrail): super().__init__( guardrail_name=guardrail_name, - supported_event_hooks=[GuardrailEventHooks.post_call], + supported_event_hooks=list(self.get_supported_event_hooks()), event_hook=_event_hook or GuardrailEventHooks.post_call, default_on=default_on, **kwargs, @@ -115,6 +115,10 @@ class LLMAsAJudgeGuardrail(CustomGuardrail): self.overall_threshold = overall_threshold self.on_failure = on_failure + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [GuardrailEventHooks.post_call] + async def _run_judge( self, messages: List[Dict[str, Any]], @@ -267,7 +271,13 @@ def initialize_guardrail( return instance +guardrail_class_registry = { + SupportedGuardrailIntegrations.LLM_AS_A_JUDGE.value: LLMAsAJudgeGuardrail, +} + + __all__ = [ "LLMAsAJudgeGuardrail", + "guardrail_class_registry", "initialize_guardrail", ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/mcp_end_user_permission/mcp_end_user_permission.py b/litellm/proxy/guardrails/guardrail_hooks/mcp_end_user_permission/mcp_end_user_permission.py index 524d087cfb7..d084a7e088b 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/mcp_end_user_permission/mcp_end_user_permission.py +++ b/litellm/proxy/guardrails/guardrail_hooks/mcp_end_user_permission/mcp_end_user_permission.py @@ -40,10 +40,7 @@ class MCPEndUserPermissionGuardrail(CustomGuardrail): """ def __init__(self, **kwargs): - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) verbose_proxy_logger.debug("MCP End User Permission Guardrail initialized") @@ -210,6 +207,12 @@ class MCPEndUserPermissionGuardrail(CustomGuardrail): return MCPEndUserPermissionGuardrailConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + ] + # ------------------------------------------------------------------ # Private — tool name extraction # ------------------------------------------------------------------ diff --git a/litellm/proxy/guardrails/guardrail_hooks/mcp_jwt_signer/mcp_jwt_signer.py b/litellm/proxy/guardrails/guardrail_hooks/mcp_jwt_signer/mcp_jwt_signer.py index 46b1afb5db7..e70a4e1d8e7 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/mcp_jwt_signer/mcp_jwt_signer.py +++ b/litellm/proxy/guardrails/guardrail_hooks/mcp_jwt_signer/mcp_jwt_signer.py @@ -87,6 +87,7 @@ from litellm.integrations.custom_guardrail import ( log_guardrail_information, ) from litellm.proxy._types import UserAPIKeyAuth +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import CallTypesLiteral # Module-level singleton for the JWKS discovery endpoint to access. @@ -211,6 +212,10 @@ class MCPJWTSigner(CustomGuardrail): DEFAULT_AUDIENCE = "mcp" SIGNING_KEY_ENV = "MCP_JWT_SIGNING_KEY" + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [GuardrailEventHooks.pre_mcp_call] + def __init__( self, # Core signing config @@ -240,6 +245,7 @@ class MCPJWTSigner(CustomGuardrail): allowed_scopes: Optional[List[str]] = None, **kwargs: Any, ) -> None: + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) # --- Signing key setup --- diff --git a/litellm/proxy/guardrails/guardrail_hooks/mcp_security/mcp_security_guardrail.py b/litellm/proxy/guardrails/guardrail_hooks/mcp_security/mcp_security_guardrail.py index 3aeed1a25bb..9b5c221b9c4 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/mcp_security/mcp_security_guardrail.py +++ b/litellm/proxy/guardrails/guardrail_hooks/mcp_security/mcp_security_guardrail.py @@ -27,11 +27,14 @@ class MCPSecurityGuardrail(CustomGuardrail): on_violation: Literal["block", "alert"] = "block", **kwargs, ): - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [GuardrailEventHooks.pre_call] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) self.on_violation = on_violation + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [GuardrailEventHooks.pre_call] + @log_guardrail_information async def async_pre_call_hook( self, diff --git a/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/purview_dlp.py b/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/purview_dlp.py index c6471cfcf51..f0fdaff9c29 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/purview_dlp.py +++ b/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/purview_dlp.py @@ -75,12 +75,6 @@ class MicrosoftPurviewDLPGuardrail(PurviewGuardrailBase, CustomGuardrail): user_id_field: str = "user_id", **kwargs: Any, ): - supported_event_hooks = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.logging_only, - ] - super().__init__( tenant_id=tenant_id, client_id=client_id, @@ -88,7 +82,7 @@ class MicrosoftPurviewDLPGuardrail(PurviewGuardrailBase, CustomGuardrail): purview_app_name=purview_app_name, user_id_field=user_id_field, guardrail_name=guardrail_name, - supported_event_hooks=supported_event_hooks, + supported_event_hooks=list(self.get_supported_event_hooks()), **kwargs, ) self.guardrail_provider = "microsoft_purview" @@ -101,6 +95,14 @@ class MicrosoftPurviewDLPGuardrail(PurviewGuardrailBase, CustomGuardrail): def get_config_model() -> Optional[Type["GuardrailConfigModel"]]: return None # Config model can be added later for UI support + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.logging_only, + ] + # ------------------------------------------------------------------ # Core DLP check # ------------------------------------------------------------------ diff --git a/litellm/proxy/guardrails/guardrail_hooks/model_armor/model_armor.py b/litellm/proxy/guardrails/guardrail_hooks/model_armor/model_armor.py index bebd9b28745..3ca63a1e287 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/model_armor/model_armor.py +++ b/litellm/proxy/guardrails/guardrail_hooks/model_armor/model_armor.py @@ -39,6 +39,7 @@ from litellm.proxy.guardrails.guardrail_hooks.model_armor.file_scanning import ( from litellm.types.guardrails import GuardrailEventHooks from litellm.types.llms.openai import AllMessageValues from litellm.types.utils import ( + CallTypes, CallTypesLiteral, Choices, GuardrailStatus, @@ -59,6 +60,16 @@ class ModelArmorGuardrail(CustomGuardrail, VertexBase): - Post-call sanitization (sanitizeModelResponse) """ + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.pre_mcp_call, + GuardrailEventHooks.during_mcp_call, + ] + def __init__( self, template_id: Optional[str] = None, @@ -75,6 +86,7 @@ class ModelArmorGuardrail(CustomGuardrail, VertexBase): GuardrailEventHooks.during_call, GuardrailEventHooks.post_call, ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) # Initialize parent classes first super().__init__(**kwargs) @@ -477,6 +489,8 @@ class ModelArmorGuardrail(CustomGuardrail, VertexBase): ) event_type = GuardrailEventHooks.pre_call + if call_type == CallTypes.call_mcp_tool.value: + event_type = GuardrailEventHooks.pre_mcp_call if self.should_run_guardrail(data=data, event_type=event_type) is not True: return data @@ -574,6 +588,8 @@ class ModelArmorGuardrail(CustomGuardrail, VertexBase): ) event_type = GuardrailEventHooks.during_call + if call_type == CallTypes.call_mcp_tool.value: + event_type = GuardrailEventHooks.during_mcp_call if self.should_run_guardrail(data=data, event_type=event_type) is not True: return data diff --git a/litellm/proxy/guardrails/guardrail_hooks/noma/noma.py b/litellm/proxy/guardrails/guardrail_hooks/noma/noma.py index 7e8a22a66a8..a467bb14eb6 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/noma/noma.py +++ b/litellm/proxy/guardrails/guardrail_hooks/noma/noma.py @@ -104,6 +104,15 @@ class NomaGuardrail(CustomGuardrail): _DEFAULT_API_BASE = "https://api.noma.security/" _AIDR_ENDPOINT = "/ai-dr/v2/prompt/scan" + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.pre_mcp_call, + ] + def __init__( self, api_key: Optional[str] = None, @@ -147,6 +156,7 @@ class NomaGuardrail(CustomGuardrail): else: self.anonymize_input = anonymize_input + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) def _create_background_noma_check( diff --git a/litellm/proxy/guardrails/guardrail_hooks/noma/noma_v2.py b/litellm/proxy/guardrails/guardrail_hooks/noma/noma_v2.py index 1cf3dcd9ac4..9cf0986c122 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/noma/noma_v2.py +++ b/litellm/proxy/guardrails/guardrail_hooks/noma/noma_v2.py @@ -8,7 +8,7 @@ import enum import json import os from datetime import datetime -from typing import TYPE_CHECKING, Any, Literal, Optional, Type, cast +from typing import TYPE_CHECKING, Any, List, Literal, Optional, Type, cast from urllib.parse import urlparse from litellm._logging import verbose_proxy_logger @@ -71,14 +71,7 @@ class NomaV2Guardrail(CustomGuardrail): if self._requires_api_key(api_base=self.api_base) and not self.api_key: raise ValueError("Noma v2 guardrail requires api_key when using Noma SaaS endpoint") - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.pre_mcp_call, - GuardrailEventHooks.during_mcp_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) @@ -90,6 +83,16 @@ class NomaV2Guardrail(CustomGuardrail): return NomaV2GuardrailConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.pre_mcp_call, + GuardrailEventHooks.during_mcp_call, + ] + def _get_authorization_header(self) -> str: if not self.api_key: return "" diff --git a/litellm/proxy/guardrails/guardrail_hooks/onyx/onyx.py b/litellm/proxy/guardrails/guardrail_hooks/onyx/onyx.py index b411d0fb9eb..606f0a4587d 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/onyx/onyx.py +++ b/litellm/proxy/guardrails/guardrail_hooks/onyx/onyx.py @@ -6,7 +6,7 @@ # +-------------------------------------------------------------+ import os import uuid -from typing import TYPE_CHECKING, Any, Literal, Optional, Type +from typing import TYPE_CHECKING, Any, List, Literal, Optional, Type import httpx from fastapi import HTTPException @@ -21,6 +21,7 @@ from litellm.llms.custom_httpx.http_handler import ( get_async_httpx_client, httpxSpecialProvider, ) +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import GenericGuardrailAPIInputs, ModelResponse if TYPE_CHECKING: @@ -28,6 +29,14 @@ if TYPE_CHECKING: class OnyxGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, api_base: Optional[str] = None, @@ -35,6 +44,7 @@ class OnyxGuardrail(CustomGuardrail): timeout: Optional[float] = 10.0, **kwargs, ): + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) timeout = timeout or int(os.getenv("ONYX_TIMEOUT", 10.0)) self.async_handler = get_async_httpx_client( llm_provider=httpxSpecialProvider.GuardrailCallback, diff --git a/litellm/proxy/guardrails/guardrail_hooks/openai/moderations.py b/litellm/proxy/guardrails/guardrail_hooks/openai/moderations.py index 093ac693d5e..44016bfd3dc 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/openai/moderations.py +++ b/litellm/proxy/guardrails/guardrail_hooks/openai/moderations.py @@ -6,6 +6,7 @@ OpenAI Moderation Guardrail Integration for LiteLLM from typing import ( TYPE_CHECKING, Dict, + List, Literal, Optional, Type, @@ -64,17 +65,9 @@ class OpenAIModerationGuardrail(OpenAIGuardrailBase, CustomGuardrail): **kwargs, ): """Initialize OpenAI Moderation guardrail handler.""" - from litellm.types.guardrails import GuardrailEventHooks - - # Initialize parent CustomGuardrail - supported_event_hooks = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.post_call, - ] super().__init__( guardrail_name=guardrail_name, - supported_event_hooks=supported_event_hooks, + supported_event_hooks=list(self.get_supported_event_hooks()), **kwargs, ) @@ -353,3 +346,11 @@ class OpenAIModerationGuardrail(OpenAIGuardrailBase, CustomGuardrail): ) return OpenAIModerationGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/ovalix/ovalix.py b/litellm/proxy/guardrails/guardrail_hooks/ovalix/ovalix.py index 7986d4294a4..e92ac37ca77 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/ovalix/ovalix.py +++ b/litellm/proxy/guardrails/guardrail_hooks/ovalix/ovalix.py @@ -66,6 +66,13 @@ class OvalixGuardrail(CustomGuardrail): Monolith backend. """ + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, tracker_api_base: Optional[str] = None, diff --git a/litellm/proxy/guardrails/guardrail_hooks/pangea/pangea.py b/litellm/proxy/guardrails/guardrail_hooks/pangea/pangea.py index 3d3c5403993..d02d4b448e8 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/pangea/pangea.py +++ b/litellm/proxy/guardrails/guardrail_hooks/pangea/pangea.py @@ -89,15 +89,10 @@ class PangeaHandler(CustomGuardrail): self.pangea_input_recipe = pangea_input_recipe self.pangea_output_recipe = pangea_output_recipe - supported_event_hooks = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - ] - # Pass relevant kwargs to the parent class super().__init__( guardrail_name=guardrail_name, - supported_event_hooks=supported_event_hooks, + supported_event_hooks=list(self.get_supported_event_hooks()), **kwargs, ) verbose_proxy_logger.debug( @@ -317,3 +312,10 @@ class PangeaHandler(CustomGuardrail): ) return PangeaGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/panw_prisma_airs/panw_prisma_airs.py b/litellm/proxy/guardrails/guardrail_hooks/panw_prisma_airs/panw_prisma_airs.py index c522ffad35d..192c8c9bc77 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/panw_prisma_airs/panw_prisma_airs.py +++ b/litellm/proxy/guardrails/guardrail_hooks/panw_prisma_airs/panw_prisma_airs.py @@ -94,14 +94,7 @@ class PanwPrismaAirsHandler(CustomGuardrail): super().__init__( guardrail_name=guardrail_name, default_on=default_on, - supported_event_hooks=[ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.logging_only, - GuardrailEventHooks.pre_mcp_call, - GuardrailEventHooks.during_mcp_call, - ], + supported_event_hooks=list(self.get_supported_event_hooks()), mask_request_content=_mask_request_content, mask_response_content=_mask_response_content, violation_message_template=violation_message_template, @@ -1854,3 +1847,14 @@ class PanwPrismaAirsHandler(CustomGuardrail): ) return PanwPrismaAirsGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.logging_only, + GuardrailEventHooks.pre_mcp_call, + GuardrailEventHooks.during_mcp_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/pillar/pillar.py b/litellm/proxy/guardrails/guardrail_hooks/pillar/pillar.py index f976839787b..1d884352eec 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/pillar/pillar.py +++ b/litellm/proxy/guardrails/guardrail_hooks/pillar/pillar.py @@ -269,18 +269,9 @@ class PillarGuardrail(CustomGuardrail): ) self.timeout = self.DEFAULT_TIMEOUT - # Define supported event hooks - supported_event_hooks = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.pre_mcp_call, - GuardrailEventHooks.during_mcp_call, - ] - super().__init__( guardrail_name=guardrail_name, - supported_event_hooks=supported_event_hooks, + supported_event_hooks=list(self.get_supported_event_hooks()), **kwargs, ) @@ -830,3 +821,13 @@ class PillarGuardrail(CustomGuardrail): ) return PillarGuardrailConfigModel + + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.pre_mcp_call, + GuardrailEventHooks.during_mcp_call, + ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/presidio.py b/litellm/proxy/guardrails/guardrail_hooks/presidio.py index 95876a55eab..a0c822964a0 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/presidio.py +++ b/litellm/proxy/guardrails/guardrail_hooks/presidio.py @@ -67,6 +67,16 @@ class _OPTIONAL_PresidioPIIMasking(CustomGuardrail): user_api_key_cache = None ad_hoc_recognizers = None + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.logging_only, + GuardrailEventHooks.pre_mcp_call, + ] + # Class variables or attributes def __init__( self, @@ -87,6 +97,7 @@ class _OPTIONAL_PresidioPIIMasking(CustomGuardrail): if logging_only is True: self.logging_only = True kwargs["event_hook"] = GuardrailEventHooks.logging_only + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) self.guardrail_provider = "presidio" self.pii_tokens: dict = {} # mapping of PII token to original text - only used with Presidio `replace` operation diff --git a/litellm/proxy/guardrails/guardrail_hooks/prompt_security/prompt_security.py b/litellm/proxy/guardrails/guardrail_hooks/prompt_security/prompt_security.py index 23f349e0b18..e60815dbcf7 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/prompt_security/prompt_security.py +++ b/litellm/proxy/guardrails/guardrail_hooks/prompt_security/prompt_security.py @@ -14,6 +14,7 @@ from litellm.llms.custom_httpx.http_handler import ( get_async_httpx_client, httpxSpecialProvider, ) +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import GenericGuardrailAPIInputs if TYPE_CHECKING: @@ -26,6 +27,14 @@ class PromptSecurityGuardrailMissingSecrets(Exception): class PromptSecurityGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, api_key: Optional[str] = None, @@ -35,6 +44,7 @@ class PromptSecurityGuardrail(CustomGuardrail): check_tool_results: Optional[bool] = None, **kwargs, ): + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) self.api_key = api_key or os.environ.get("PROMPT_SECURITY_API_KEY") self.api_base = api_base or os.environ.get("PROMPT_SECURITY_API_BASE") diff --git a/litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py b/litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py index 7012b0d8d5d..6603183efef 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py +++ b/litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py @@ -81,11 +81,7 @@ class PromptGuardGuardrail(CustomGuardrail): llm_provider=httpxSpecialProvider.GuardrailCallback, ) - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) @@ -97,6 +93,13 @@ class PromptGuardGuardrail(CustomGuardrail): return PromptGuardConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + @log_guardrail_information async def apply_guardrail( self, diff --git a/litellm/proxy/guardrails/guardrail_hooks/qualifire/qualifire.py b/litellm/proxy/guardrails/guardrail_hooks/qualifire/qualifire.py index 54f47cfbfa1..9c62c2915ff 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/qualifire/qualifire.py +++ b/litellm/proxy/guardrails/guardrail_hooks/qualifire/qualifire.py @@ -22,6 +22,7 @@ from litellm.llms.custom_httpx.http_handler import ( httpxSpecialProvider, ) from litellm.secret_managers.main import get_secret_str +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.llms.openai import AllMessageValues from litellm.types.proxy.guardrails.guardrail_hooks.base import GuardrailConfigModel from litellm.types.utils import GenericGuardrailAPIInputs @@ -31,6 +32,14 @@ DEFAULT_QUALIFIRE_API_BASE = "https://proxy.qualifire.ai" class QualifireGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, api_key: Optional[str] = None, @@ -86,6 +95,7 @@ class QualifireGuardrail(CustomGuardrail): # Initialize async HTTP client for direct API calls self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) def _has_any_check_enabled(self) -> bool: diff --git a/litellm/proxy/guardrails/guardrail_hooks/repelloai/repelloai.py b/litellm/proxy/guardrails/guardrail_hooks/repelloai/repelloai.py index f8971ceb405..da1c74b37b6 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/repelloai/repelloai.py +++ b/litellm/proxy/guardrails/guardrail_hooks/repelloai/repelloai.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime -from typing import AsyncGenerator, Literal +from typing import AsyncGenerator, List, Literal from pydantic import TypeAdapter, ValidationError from pydantic import BaseModel @@ -64,6 +64,13 @@ def _is_object_list(value: object) -> TypeGuard[list[object]]: # guard-ok: isin class RepelloAIGuardrail(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + @staticmethod def _get_field(obj: object, key: str) -> object: if _is_object_dict(obj): @@ -169,6 +176,7 @@ class RepelloAIGuardrail(CustomGuardrail): guardrail_name=guardrail_name, event_hook=event_hook, default_on=default_on, + supported_event_hooks=list(self.get_supported_event_hooks()), ) async def _call_analyze( diff --git a/litellm/proxy/guardrails/guardrail_hooks/semantic_guard/semantic_guard.py b/litellm/proxy/guardrails/guardrail_hooks/semantic_guard/semantic_guard.py index 5840480f0da..3865251a48a 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/semantic_guard/semantic_guard.py +++ b/litellm/proxy/guardrails/guardrail_hooks/semantic_guard/semantic_guard.py @@ -58,10 +58,7 @@ class SemanticGuardrail(CustomGuardrail): ): super().__init__( guardrail_name=guardrail_name, - supported_event_hooks=[ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - ], + supported_event_hooks=list(self.get_supported_event_hooks()), event_hook=event_hook or GuardrailEventHooks.pre_call, default_on=default_on, **kwargs, @@ -96,6 +93,13 @@ class SemanticGuardrail(CustomGuardrail): f"embedding_model={embedding_model}, threshold={similarity_threshold}" ) + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + @log_guardrail_information async def async_pre_call_hook( self, @@ -225,7 +229,7 @@ def _handle_match( detection_info=detection_info, ) else: - raise HTTPException( # type: ignore[reportOptionalCall] + raise HTTPException( # pyright: ignore[reportOptionalCall] # fastapi is installed wherever this proxy hook runs status_code=400, detail={ "error": violation_msg, diff --git a/litellm/proxy/guardrails/guardrail_hooks/tool_permission.py b/litellm/proxy/guardrails/guardrail_hooks/tool_permission.py index 2171be235e5..58c57dfdac1 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/tool_permission.py +++ b/litellm/proxy/guardrails/guardrail_hooks/tool_permission.py @@ -52,11 +52,7 @@ class ToolPermissionGuardrail(CustomGuardrail): **kwargs: Additional arguments passed to CustomGuardrail """ # Set supported event hooks - this guardrail only works on post_call - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) @@ -179,6 +175,13 @@ class ToolPermissionGuardrail(CustomGuardrail): return ToolPermissionGuardrailConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + def _matches_regex(self, pattern: Optional[re.Pattern], value: Optional[str]) -> bool: if pattern is None: return True diff --git a/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py b/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py index 79c29670d93..e0b387e92c0 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py +++ b/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py @@ -8,7 +8,7 @@ Unified Guardrail, leveraging LiteLLM's /applyGuardrail endpoint import copy import json -from typing import Any, AsyncGenerator, List, Optional, Union +from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional, Union from fastapi import HTTPException @@ -23,6 +23,11 @@ from litellm.proxy._types import UserAPIKeyAuth from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import CallTypes, CallTypesLiteral +if TYPE_CHECKING: + # Imported lazily at runtime (inside the streaming hook) to avoid a + # module-level cyclic import with litellm.integrations.custom_guardrail. + from litellm.integrations.custom_guardrail import ModifyResponseException + # Call types that use NDJSON streaming (A2A); guardrail HTTPException is emitted as in-stream error A2A_CALL_TYPES = (CallTypes.asend_message, CallTypes.send_message) @@ -197,6 +202,10 @@ class UnifiedLLMGuardrails(CustomLogger): ) from litellm.types.guardrails import GuardrailEventHooks + # Local import avoids a module-level cyclic import with + # litellm.integrations.custom_guardrail. + from litellm.integrations.custom_guardrail import ModifyResponseException + guardrail_to_apply: CustomGuardrail = data.pop("guardrail_to_apply", None) if guardrail_to_apply is None: @@ -238,18 +247,51 @@ class UnifiedLLMGuardrails(CustomLogger): endpoint_translation = endpoint_guardrail_translation_mappings[CallTypes(call_type)]() - response = await endpoint_translation.process_output_response( - response=response, # type: ignore - guardrail_to_apply=guardrail_to_apply, - litellm_logging_obj=data.get("litellm_logging_obj"), - user_api_key_dict=user_api_key_dict, - request_data=data, - ) + try: + response = await endpoint_translation.process_output_response( + response=response, # type: ignore + guardrail_to_apply=guardrail_to_apply, + litellm_logging_obj=data.get("litellm_logging_obj"), + user_api_key_dict=user_api_key_dict, + request_data=data, + ) + except ModifyResponseException as e: + # The guardrail blocked the response. Attach the original LLM + # response so the endpoint handler can report its real token usage + # instead of discarding it (the block replaces the content, but the + # upstream call already consumed those tokens). + if e.original_response is None: + e.original_response = response + raise # Add guardrail to applied guardrails header add_guardrail_to_applied_guardrails_header(request_data=data, guardrail_name=guardrail_to_apply.guardrail_name) return response + async def _handle_streaming_block( + self, + exc: "ModifyResponseException", + endpoint_translation: Any, + stream_started: bool, + responses_so_far: list[Any], + ) -> AsyncGenerator[Any, None]: + """ + Terminate a streamed response cleanly when a guardrail blocks it. + + Format-agnostic routing: delegates to the provider translation handler's + ``build_block_sse_chunks`` (see ``BaseTranslation.build_block_sse_chunks`` + for the ``stream_started`` / ``responses_so_far`` contract). When the + format has no safe terminator the handler returns None and we re-raise + ``exc`` so the proxy can surface a clean error. + """ + block_chunks = endpoint_translation.build_block_sse_chunks( + exc, stream_started=stream_started, responses_so_far=responses_so_far + ) + if block_chunks is None: + raise exc + for chunk in block_chunks: + yield chunk + async def async_post_call_streaming_iterator_hook( self, user_api_key_dict: UserAPIKeyAuth, @@ -271,26 +313,53 @@ class UnifiedLLMGuardrails(CustomLogger): global endpoint_guardrail_translation_mappings + # Local import avoids a module-level cyclic import with + # litellm.integrations.custom_guardrail. + from litellm.integrations.custom_guardrail import ModifyResponseException + guardrail_to_apply: CustomGuardrail = request_data.pop("guardrail_to_apply", None) - # Get streaming configuration from guardrail or optional_params - sampling_rate = 5 - end_of_stream_only = False # If True, only apply guardrail at end of stream + # Get streaming configuration. Resolution order (later wins): default + # < guardrail attribute < guardrail_config dict < this callback's + # optional_params. + def _streaming_flag(name: str, default: Any) -> Any: + value = default + if guardrail_to_apply is not None: + value = getattr(guardrail_to_apply, name, value) + config = getattr(guardrail_to_apply, "guardrail_config", {}) + if isinstance(config, dict): + value = config.get(name, value) + return self.optional_params.get(name, value) - if guardrail_to_apply is not None: - # Check direct attributes on guardrail first - sampling_rate = getattr(guardrail_to_apply, "streaming_sampling_rate", sampling_rate) - end_of_stream_only = getattr(guardrail_to_apply, "streaming_end_of_stream_only", end_of_stream_only) + sampling_rate = _streaming_flag("streaming_sampling_rate", 5) + # Only apply the guardrail at end of stream (not per chunk). + end_of_stream_only = _streaming_flag("streaming_end_of_stream_only", False) + # Withhold every chunk until end-of-stream moderation passes, then + # release the original chunks (clean) or only the block message + # (blocked) -- moderating the whole response *before* any content + # reaches the client. Only safe for allow/block guardrails: on + # release the original chunks are replayed as-is, so a + # content-rewriting guardrail (e.g. PII masking) would leak + # unredacted content. Guarded below via mask_response_content. + buffer_until_moderated = _streaming_flag("streaming_buffer_until_moderated", False) - # Also check guardrail_config dict if present - guardrail_config = getattr(guardrail_to_apply, "guardrail_config", {}) - if isinstance(guardrail_config, dict): - sampling_rate = guardrail_config.get("streaming_sampling_rate", sampling_rate) - end_of_stream_only = guardrail_config.get("streaming_end_of_stream_only", end_of_stream_only) + if ( + buffer_until_moderated + and guardrail_to_apply is not None + and getattr(guardrail_to_apply, "mask_response_content", False) + ): + verbose_proxy_logger.warning( + "UnifiedLLMGuardrails: streaming_buffer_until_moderated is disabled for %s " + "because mask_response_content=True -- buffered replay would release " + "unredacted original chunks instead of the moderated output.", + guardrail_to_apply.guardrail_name, + ) + buffer_until_moderated = False - # Also check optional_params as fallback - sampling_rate = self.optional_params.get("streaming_sampling_rate", sampling_rate) - end_of_stream_only = self.optional_params.get("streaming_end_of_stream_only", end_of_stream_only) + # Buffering can only moderate the assembled response, so it always + # defers to end-of-stream. + if buffer_until_moderated: + end_of_stream_only = True if guardrail_to_apply is None: async for item in response: @@ -315,6 +384,12 @@ class UnifiedLLMGuardrails(CustomLogger): call_type = None chunk_counter = 0 responses_so_far: List[Any] = [] + responses_yielded: list[Any] = [] + pending_end_of_stream_items: list[Any] = [] + # Whether any real response chunk has been forwarded to the client. + # Drives how a block terminates the stream: continue the in-progress + # message (True) vs emit a standalone block message (False, buffered). + chunks_yielded = False async for item in response: chunk_counter += 1 @@ -336,9 +411,22 @@ class UnifiedLLMGuardrails(CustomLogger): yield remaining_item return - # If end_of_stream_only mode, yield chunks without processing + # If end_of_stream_only mode, yield chunks without processing. + # When buffering, withhold them instead -- they are released (or + # replaced by the block message) only after end-of-stream + # moderation runs below. if end_of_stream_only: - yield item + if not buffer_until_moderated: + endpoint_translation = endpoint_guardrail_translation_mappings[CallTypes(call_type)]() + stream_has_ended = hasattr( + endpoint_translation, "_check_streaming_has_ended" + ) and endpoint_translation._check_streaming_has_ended(responses_so_far) + if pending_end_of_stream_items or stream_has_ended: + pending_end_of_stream_items.append(item) + else: + chunks_yielded = True + responses_yielded.append(item) + yield item continue # Process chunk based on sampling rate @@ -368,6 +456,26 @@ class UnifiedLLMGuardrails(CustomLogger): user_api_key_dict=user_api_key_dict, request_data=request_data, ) + except ModifyResponseException as e: + if e.original_response is None: + e.original_response = responses_so_far + # Guardrail blocked the response mid-stream. Emit a clean + # terminating SSE sequence delivering the block message + # instead of letting the exception propagate into a bare + # `data: {"error": ...}` blob (which truncates the stream). + # Chunks have already been forwarded here, so the block + # continues the in-progress message (stream_started=True). + # The current chunk was appended to responses_so_far but not + # yet yielded, so exclude it: the continuation must reflect + # only what the client has actually received. + async for block_chunk in self._handle_streaming_block( + e, + endpoint_translation, + stream_started=chunks_yielded, + responses_so_far=responses_yielded, + ): + yield block_chunk + return except HTTPException as e: # Response already started (we already yielded chunks); cannot send 400. # For A2A (NDJSON), yield an in-stream JSON-RPC error so the client sees it. @@ -394,8 +502,12 @@ class UnifiedLLMGuardrails(CustomLogger): yield error_chunk return raise + chunks_yielded = True + responses_yielded.append(original_item) yield original_item else: + chunks_yielded = True + responses_yielded.append(item) yield item # Stream has ended - do final processing with all collected chunks @@ -408,6 +520,15 @@ class UnifiedLLMGuardrails(CustomLogger): endpoint_translation = endpoint_guardrail_translation_mappings[CallTypes(call_type)]() + # When buffering, snapshot the original chunks before moderation. + # A shallow copy suffices: end-of-stream + # process_output_streaming_response builds a separate assembled + # response (it does not mutate the individual chunks in place), and + # the chunks themselves are replayed verbatim -- so we only need to + # preserve the list, not clone every chunk (deepcopy would double + # peak memory for large responses). + buffered_items = list(responses_so_far) if buffer_until_moderated else None + try: await endpoint_translation.process_output_streaming_response( responses_so_far=responses_so_far, @@ -416,6 +537,28 @@ class UnifiedLLMGuardrails(CustomLogger): user_api_key_dict=user_api_key_dict, request_data=request_data, ) + # Moderation passed: release the withheld original chunks. + if buffered_items is not None: + for buffered_item in buffered_items: + yield buffered_item + for pending_item in pending_end_of_stream_items: + responses_yielded.append(pending_item) + yield pending_item + except ModifyResponseException as e: + if e.original_response is None: + e.original_response = responses_so_far + # Block detected during end-of-stream processing. Emit a clean + # terminating SSE sequence with the block message rather than + # propagating into a bare error blob that truncates the stream. + # The withheld original chunks are never released. + async for block_chunk in self._handle_streaming_block( + e, + endpoint_translation, + stream_started=bool(responses_yielded), + responses_so_far=responses_yielded, + ): + yield block_chunk + return except HTTPException as e: if call_type is not None and CallTypes(call_type) in A2A_CALL_TYPES: request_id = _get_a2a_request_id(responses_so_far, request_data) diff --git a/litellm/proxy/guardrails/guardrail_hooks/vigil_guard/vigil_guard.py b/litellm/proxy/guardrails/guardrail_hooks/vigil_guard/vigil_guard.py index 9a8893734e1..9976a1c48b1 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/vigil_guard/vigil_guard.py +++ b/litellm/proxy/guardrails/guardrail_hooks/vigil_guard/vigil_guard.py @@ -116,11 +116,7 @@ class VigilGuardGuardrail(CustomGuardrail): llm_provider=httpxSpecialProvider.GuardrailCallback, ) - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.post_call, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) @@ -132,6 +128,13 @@ class VigilGuardGuardrail(CustomGuardrail): return VigilGuardGuardrailConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + @log_guardrail_information async def apply_guardrail( self, diff --git a/litellm/proxy/guardrails/guardrail_hooks/xecguard/xecguard.py b/litellm/proxy/guardrails/guardrail_hooks/xecguard/xecguard.py index 36c22753404..7fe942bcb38 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/xecguard/xecguard.py +++ b/litellm/proxy/guardrails/guardrail_hooks/xecguard/xecguard.py @@ -44,12 +44,18 @@ from litellm.integrations.custom_guardrail import ( CustomGuardrail, log_guardrail_information, ) +from litellm.litellm_core_utils.core_helpers import redact_nested_match_and_regex_keys +from litellm.litellm_core_utils.sensitive_data_masker import mask_credentials_in_payload from litellm.llms.custom_httpx.http_handler import ( get_async_httpx_client, httpxSpecialProvider, ) from litellm.types.guardrails import GuardrailEventHooks -from litellm.types.utils import GenericGuardrailAPIInputs, GuardrailStatus +from litellm.types.utils import ( + GenericGuardrailAPIInputs, + GuardrailStatus, + StandardLoggingGuardrailInformation, +) if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import ( @@ -60,6 +66,13 @@ if TYPE_CHECKING: ) +def _sanitize_scan_result_for_logging(scan_result: dict) -> dict: + without_secrets = {key: value for key, value in scan_result.items() if key != "secret_fields"} + redacted = redact_nested_match_and_regex_keys(without_secrets) + masked = mask_credentials_in_payload(redacted if isinstance(redacted, dict) else without_secrets) + return masked if isinstance(masked, dict) else without_secrets + + _DEFAULT_API_BASE = "https://api-xecguard.cycraft.ai" _SCAN_ENDPOINT = "/xecguard/v1/scan" _GROUNDING_ENDPOINT = "/xecguard/v1/grounding" @@ -119,13 +132,7 @@ class XecGuardGuardrail(CustomGuardrail): llm_provider=httpxSpecialProvider.GuardrailCallback, ) - if "supported_event_hooks" not in kwargs: - kwargs["supported_event_hooks"] = [ - GuardrailEventHooks.pre_call, - GuardrailEventHooks.during_call, - GuardrailEventHooks.post_call, - GuardrailEventHooks.logging_only, - ] + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) super().__init__(**kwargs) @@ -137,6 +144,15 @@ class XecGuardGuardrail(CustomGuardrail): return XecGuardConfigModel + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.during_call, + GuardrailEventHooks.post_call, + GuardrailEventHooks.logging_only, + ] + @log_guardrail_information async def apply_guardrail( self, @@ -243,16 +259,21 @@ class XecGuardGuardrail(CustomGuardrail): "guardrail_intervened" if scan_result.get("decision") == "UNSAFE" else "success" ) end_time = datetime.now() - kwargs["standard_logging_object"]["guardrail_information"] = { - "duration": (end_time - start_time).total_seconds(), - "end_time": end_time.timestamp(), - "guardrail_mode": "logging_only", - "guardrail_name": "xecguard", - "guardrail_response": scan_result, - "guardrail_status": guardrail_status, - "masked_entity_count": None, - "start_time": start_time.timestamp(), - } + slg = StandardLoggingGuardrailInformation( + guardrail_name=self.guardrail_name or "xecguard", + guardrail_mode=GuardrailEventHooks.logging_only, + guardrail_response=_sanitize_scan_result_for_logging(scan_result), + guardrail_status=guardrail_status, + start_time=start_time.timestamp(), + end_time=end_time.timestamp(), + duration=(end_time - start_time).total_seconds(), + masked_entity_count=None, + ) + existing = kwargs["standard_logging_object"].get("guardrail_information") + if isinstance(existing, list): + existing.append(slg) + else: + kwargs["standard_logging_object"]["guardrail_information"] = [slg] except Exception as exc: verbose_proxy_logger.debug( diff --git a/litellm/proxy/guardrails/guardrail_hooks/zscaler_ai_guard/zscaler_ai_guard.py b/litellm/proxy/guardrails/guardrail_hooks/zscaler_ai_guard/zscaler_ai_guard.py index 9c660620582..65338827e07 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/zscaler_ai_guard/zscaler_ai_guard.py +++ b/litellm/proxy/guardrails/guardrail_hooks/zscaler_ai_guard/zscaler_ai_guard.py @@ -4,7 +4,7 @@ # # +-------------------------------------------------------------+ import os -from typing import TYPE_CHECKING, Literal, Optional +from typing import TYPE_CHECKING, List, Literal, Optional from fastapi import HTTPException @@ -17,6 +17,7 @@ from litellm.llms.custom_httpx.http_handler import ( get_async_httpx_client, httpxSpecialProvider, ) +from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import GenericGuardrailAPIInputs if TYPE_CHECKING: @@ -27,6 +28,13 @@ GUARDRAIL_TIMEOUT = 5 class ZscalerAIGuard(CustomGuardrail): + @classmethod + def get_supported_event_hooks(cls) -> List[GuardrailEventHooks]: + return [ + GuardrailEventHooks.pre_call, + GuardrailEventHooks.post_call, + ] + def __init__( self, api_key: Optional[str] = None, @@ -37,6 +45,7 @@ class ZscalerAIGuard(CustomGuardrail): send_user_api_key_team_id: Optional[bool] = None, **kwargs, ): + kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks())) self.optional_params = kwargs self.zscaler_ai_guard_url = api_base or os.getenv( "ZSCALER_AI_GUARD_URL", diff --git a/litellm/proxy/guardrails/guardrail_registry.py b/litellm/proxy/guardrails/guardrail_registry.py index 8962073fe7a..e9eee3a1a8a 100644 --- a/litellm/proxy/guardrails/guardrail_registry.py +++ b/litellm/proxy/guardrails/guardrail_registry.py @@ -13,12 +13,23 @@ from litellm._logging import verbose_proxy_logger from litellm._uuid import uuid from litellm.integrations.custom_guardrail import CustomGuardrail from litellm.litellm_core_utils.safe_json_dumps import safe_dumps +from litellm.proxy.guardrails.guardrail_hooks.bedrock_guardrails import ( + BedrockGuardrail, +) from litellm.proxy.guardrails.guardrail_hooks.grayswan import ( GraySwanGuardrail, ) from litellm.proxy.guardrails.guardrail_hooks.grayswan import ( initialize_guardrail as initialize_grayswan, ) +from litellm.proxy.guardrails.guardrail_hooks.lakera_ai import lakeraAI_Moderation +from litellm.proxy.guardrails.guardrail_hooks.lakera_ai_v2 import LakeraAIGuardrail +from litellm.proxy.guardrails.guardrail_hooks.presidio import ( + _OPTIONAL_PresidioPIIMasking, +) +from litellm.proxy.guardrails.guardrail_hooks.tool_permission import ( + ToolPermissionGuardrail, +) from litellm.proxy.types_utils.utils import get_instance_fn from litellm.proxy.utils import PrismaClient from litellm.repositories.table_repositories import GuardrailsRepository @@ -55,7 +66,12 @@ guardrail_initializer_registry = { } guardrail_class_registry: Dict[str, Type[CustomGuardrail]] = { - SupportedGuardrailIntegrations.GRAYSWAN.value: GraySwanGuardrail + SupportedGuardrailIntegrations.BEDROCK.value: BedrockGuardrail, + SupportedGuardrailIntegrations.GRAYSWAN.value: GraySwanGuardrail, + SupportedGuardrailIntegrations.LAKERA.value: lakeraAI_Moderation, + SupportedGuardrailIntegrations.LAKERA_V2.value: LakeraAIGuardrail, + SupportedGuardrailIntegrations.PRESIDIO.value: _OPTIONAL_PresidioPIIMasking, + SupportedGuardrailIntegrations.TOOL_PERMISSION.value: ToolPermissionGuardrail, } diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index c20991b8d43..4250b9668ff 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -6,7 +6,7 @@ import secrets import time import traceback from datetime import datetime, timedelta -from typing import Any, Dict, Iterable, Literal, Optional, Union, cast +from typing import Any, Dict, Iterable, Literal, Optional, TypedDict, Union, cast import fastapi from fastapi import APIRouter, Depends, HTTPException, Request, Response, status @@ -1271,7 +1271,12 @@ async def health_license_endpoint( } -db_health_cache = {"status": "unknown", "last_updated": datetime.now()} +class DBHealthCache(TypedDict): + status: str + last_updated: datetime + + +db_health_cache: DBHealthCache = {"status": "unknown", "last_updated": datetime.now()} async def _db_health_readiness_check(): @@ -1807,6 +1812,7 @@ async def test_model_connection( # Look up model configuration from router if model name is provided # This gets the litellm_params from proxy config (with resolved env vars) config_litellm_params: dict = {} + loaded_model_info: Optional[dict] = None if llm_router is not None: # Prefer disambiguation by deployment id (`model_info.id`) when # the caller supplies it. This is required when multiple @@ -1825,6 +1831,7 @@ async def test_model_connection( if deployment_by_id is not None: config_litellm_params = deployment_by_id.litellm_params.model_dump(exclude_none=True) + loaded_model_info = deployment_by_id.model_info.model_dump(exclude_none=True) elif model_name: # Fall back to model_name lookup for callers (e.g. the # "Add Model" wizard, or curl) that don't supply an id. @@ -1846,6 +1853,7 @@ async def test_model_connection( # config. These already have resolved environment # variables from proxy config. config_litellm_params = dict(deployments[0].get("litellm_params", {})) + loaded_model_info = dict(deployments[0].get("model_info") or {}) except Exception as e: verbose_proxy_logger.debug( f"Could not find model {model_name} in router: {e}. Proceeding with request params only." @@ -1856,11 +1864,12 @@ async def test_model_connection( litellm_params = {**config_litellm_params, **request_litellm_params} ## Auth check + auth_model_info = loaded_model_info if loaded_model_info is not None else model_info await ModelManagementAuthChecks.can_user_make_model_call( model_params=Deployment( model_name="test_model", litellm_params=LiteLLM_Params(**litellm_params), - model_info=model_info, + model_info=auth_model_info, ), user_api_key_dict=user_api_key_dict, prisma_client=prisma_client, diff --git a/litellm/proxy/hooks/dynamic_rate_limiter.py b/litellm/proxy/hooks/dynamic_rate_limiter.py index f2e31b77761..83161fca5bb 100644 --- a/litellm/proxy/hooks/dynamic_rate_limiter.py +++ b/litellm/proxy/hooks/dynamic_rate_limiter.py @@ -4,7 +4,8 @@ import asyncio import os -from typing import List, Optional, Tuple, Union +from datetime import datetime +from typing import Callable, List, Optional, Tuple, Union import litellm from litellm import ModelResponse, Router @@ -30,12 +31,13 @@ class DynamicRateLimiterCache: Track number of active projects calling a model. """ - def __init__(self, cache: DualCache) -> None: + def __init__(self, cache: DualCache, time_fn: Callable[[], datetime] = get_utc_datetime) -> None: self.cache = cache self.ttl = 60 # 1 min ttl + self.time_fn = time_fn async def async_get_cache(self, model: str) -> Optional[int]: - dt = get_utc_datetime() + dt = self.time_fn() current_minute = dt.strftime("%H-%M") key_name = "{}:{}".format(current_minute, model) _response = await self.cache.async_get_cache(key=key_name) @@ -59,7 +61,7 @@ class DynamicRateLimiterCache: - Exception, if unable to connect to cache client (if redis caching enabled) """ try: - dt = get_utc_datetime() + dt = self.time_fn() current_minute = dt.strftime("%H-%M") key_name = "{}:{}".format(current_minute, model) @@ -75,8 +77,8 @@ class DynamicRateLimiterCache: class _PROXY_DynamicRateLimitHandler(CustomLogger): # Class variables or attributes - def __init__(self, internal_usage_cache: DualCache): - self.internal_usage_cache = DynamicRateLimiterCache(cache=internal_usage_cache) + def __init__(self, internal_usage_cache: DualCache, time_fn: Callable[[], datetime] = get_utc_datetime): + self.internal_usage_cache = DynamicRateLimiterCache(cache=internal_usage_cache, time_fn=time_fn) def update_variables(self, llm_router: Router): self.llm_router = llm_router diff --git a/litellm/proxy/hooks/mcp_semantic_filter/hook.py b/litellm/proxy/hooks/mcp_semantic_filter/hook.py index 92b74848188..bad6ef44ccd 100644 --- a/litellm/proxy/hooks/mcp_semantic_filter/hook.py +++ b/litellm/proxy/hooks/mcp_semantic_filter/hook.py @@ -7,6 +7,8 @@ Reduces context window size and improves tool selection accuracy. from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from fastapi import HTTPException + from litellm._logging import verbose_proxy_logger from litellm.constants import ( DEFAULT_MCP_SEMANTIC_FILTER_EMBEDDING_MODEL, @@ -14,6 +16,9 @@ from litellm.constants import ( DEFAULT_MCP_SEMANTIC_FILTER_TOP_K, ) from litellm.integrations.custom_logger import CustomLogger +from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + SemanticToolFilterContextWindowError, +) if TYPE_CHECKING: from litellm.caching.caching import DualCache @@ -24,6 +29,18 @@ if TYPE_CHECKING: from litellm.router import Router +def _truncate_csv_at_tool_name_boundary(tool_names_csv: str, max_length: int) -> str: + """Cap a CSV of tool names to max_length, dropping any name that does not fit whole.""" + if len(tool_names_csv) <= max_length: + return tool_names_csv + + head = tool_names_csv[: max_length + 1] + if "," not in head: + return "" + + return head.rsplit(",", 1)[0] + + class SemanticToolFilterHook(CustomLogger): """ Pre-call hook that filters MCP tools semantically. @@ -117,6 +134,27 @@ class SemanticToolFilterHook(CustomLogger): return openai_tools_as_dicts + async def _filter_expanded_tools( + self, + data: dict, + expanded_tools: list[dict[str, Any]], + ) -> list[dict[str, Any]]: + """ + Apply the semantic filter to expanded MCP tool definitions. + + Expanded tools are flat OpenAI function dicts with a top-level + "name" (see transform_mcp_tool_to_openai_responses_api_tool), so + filter_tools can name-match them against the semantic router. + """ + raw_messages = data.get("messages") or data.get("input") or [] + messages = [{"role": "user", "content": raw_messages}] if isinstance(raw_messages, str) else raw_messages + user_query = self.filter.extract_user_query(messages) + if not user_query: + verbose_proxy_logger.debug("No user query found, skipping semantic filter on expanded MCP tools") + return expanded_tools + + return await self.filter.filter_tools(query=user_query, available_tools=expanded_tools) + def _is_mcp_tool(self, tool: object) -> bool: """ Check whether *tool* is registered in the MCP semantic router. @@ -172,6 +210,32 @@ class SemanticToolFilterHook(CustomLogger): f"Semantic tool filter: all {len(native_tools)} tools are native, no MCP filtering applied" ) + def _emit_filter_metadata_safe( + self, + data: dict, + mcp_tools: list[object], + filtered_mcp_tools: list[object], + native_tools: list[object], + filtered_tools: list[object], + ) -> None: + """ + Emit filter metadata without letting an emission failure abort the + already-filtered request. + """ + try: + self._emit_filter_metadata( + data=data, + mcp_tools=mcp_tools, + filtered_mcp_tools=filtered_mcp_tools, + native_tools=native_tools, + filtered_tools=filtered_tools, + ) + except Exception as e: + verbose_proxy_logger.warning( + f"Failed to emit semantic filter metadata: {e}", + exc_info=True, + ) + async def async_pre_call_hook( self, user_api_key_dict: "UserAPIKeyAuth", @@ -194,9 +258,6 @@ class SemanticToolFilterHook(CustomLogger): verbose_proxy_logger.debug("No tools in request, skipping semantic filter") return None - # Expanded MCP tools are in OpenAI nested format which - # filter_tools/_extract_tool_info cannot name-match, so we skip - # semantic filtering and return early. if self._should_expand_mcp_tools(tools): verbose_proxy_logger.debug("Detected litellm_proxy MCP references, expanding before semantic filtering") @@ -215,14 +276,31 @@ class SemanticToolFilterHook(CustomLogger): verbose_proxy_logger.warning("No tools expanded from MCP references") return None - data["tools"] = native_tools_before_expand + expanded_tools + if not self.filter.enabled: + data["tools"] = native_tools_before_expand + expanded_tools + verbose_proxy_logger.debug("Semantic filter disabled, forwarding expanded MCP tools unfiltered") + return data + + filtered_expanded_tools = await self._filter_expanded_tools(data=data, expanded_tools=expanded_tools) + + combined_tools = native_tools_before_expand + filtered_expanded_tools + data["tools"] = combined_tools + self._emit_filter_metadata_safe( + data=data, + mcp_tools=expanded_tools, + filtered_mcp_tools=filtered_expanded_tools, + native_tools=native_tools_before_expand, + filtered_tools=combined_tools, + ) verbose_proxy_logger.info( f"Expanded MCP references to {len(expanded_tools)} tools " f"({len(native_tools_before_expand)} native preserved), " - f"skipping semantic filter (OpenAI nested format)" + f"semantic filter selected {len(filtered_expanded_tools)}" ) return data + except SemanticToolFilterContextWindowError as e: + raise HTTPException(status_code=400, detail={"error": str(e)}) from e except Exception as e: verbose_proxy_logger.error(f"Failed to expand MCP references: {e}", exc_info=True) return None @@ -285,22 +363,18 @@ class SemanticToolFilterHook(CustomLogger): data["tools"] = filtered_tools - try: - self._emit_filter_metadata( - data=data, - mcp_tools=mcp_tools, - filtered_mcp_tools=filtered_mcp_tools, - native_tools=native_tools, - filtered_tools=filtered_tools, - ) - except Exception as e: - verbose_proxy_logger.warning( - f"Failed to emit semantic filter metadata: {e}", - exc_info=True, - ) + self._emit_filter_metadata_safe( + data=data, + mcp_tools=mcp_tools, + filtered_mcp_tools=filtered_mcp_tools, + native_tools=native_tools, + filtered_tools=filtered_tools, + ) return data + except SemanticToolFilterContextWindowError as e: + raise HTTPException(status_code=400, detail={"error": str(e)}) from e except Exception as e: verbose_proxy_logger.warning(f"Semantic tool filter hook failed: {e}. Proceeding with all tools.") return None @@ -327,11 +401,12 @@ class SemanticToolFilterHook(CustomLogger): # Add CSV of filtered tool names (nginx-safe length) tool_names_csv = metadata.get("litellm_semantic_filter_tools", "") - if tool_names_csv: - if len(tool_names_csv) > MAX_MCP_SEMANTIC_FILTER_TOOLS_HEADER_LENGTH: - tool_names_csv = tool_names_csv[: MAX_MCP_SEMANTIC_FILTER_TOOLS_HEADER_LENGTH - 3] + "..." - - headers["x-litellm-semantic-filter-tools"] = tool_names_csv + header_safe_csv = _truncate_csv_at_tool_name_boundary( + tool_names_csv=tool_names_csv, + max_length=MAX_MCP_SEMANTIC_FILTER_TOOLS_HEADER_LENGTH, + ) + if header_safe_csv: + headers["x-litellm-semantic-filter-tools"] = header_safe_csv return headers diff --git a/litellm/proxy/hooks/model_max_budget_limiter.py b/litellm/proxy/hooks/model_max_budget_limiter.py index 5ebb5819c20..803fe64c193 100644 --- a/litellm/proxy/hooks/model_max_budget_limiter.py +++ b/litellm/proxy/hooks/model_max_budget_limiter.py @@ -80,6 +80,20 @@ class _PROXY_VirtualKeyModelMaxBudgetLimiter(RouterBudgetLimiting): return True + async def get_fallback_model_within_budget( + self, + user_api_key_dict: UserAPIKeyAuth, + model: str, + ) -> Optional[str]: + budget_fallbacks: dict[str, list[str]] = user_api_key_dict.budget_fallbacks or {} + for fallback_model in budget_fallbacks.get(model, []): + try: + await self.is_key_within_model_budget(user_api_key_dict=user_api_key_dict, model=fallback_model) + return fallback_model + except litellm.BudgetExceededError: + continue + return None + async def is_end_user_within_model_budget( self, end_user_id: str, diff --git a/litellm/proxy/hooks/parallel_request_limiter.py b/litellm/proxy/hooks/parallel_request_limiter.py index 1ed76d5b1e3..ee6abb13d6b 100644 --- a/litellm/proxy/hooks/parallel_request_limiter.py +++ b/litellm/proxy/hooks/parallel_request_limiter.py @@ -17,6 +17,7 @@ from litellm.proxy.auth.auth_utils import ( get_key_model_rpm_limit, get_key_model_tpm_limit, ) +from litellm.proxy.auth.budget_throttle import throttled_limit from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError from litellm.proxy.hooks.rate_limiter_utils import resolve_llm_provider_for_rate_limit @@ -248,10 +249,11 @@ class _PROXY_MaxParallelRequestsHandler(CustomLogger): if data is None: data = {} global_max_parallel_requests = data.get("metadata", {}).get("global_max_parallel_requests", None) - tpm_limit = getattr(user_api_key_dict, "tpm_limit", sys.maxsize) + throttle_pct = getattr(user_api_key_dict, "budget_throttle_pct", None) + tpm_limit = throttled_limit(getattr(user_api_key_dict, "tpm_limit", sys.maxsize), throttle_pct) if tpm_limit is None: tpm_limit = sys.maxsize - rpm_limit = getattr(user_api_key_dict, "rpm_limit", sys.maxsize) + rpm_limit = throttled_limit(getattr(user_api_key_dict, "rpm_limit", sys.maxsize), throttle_pct) if rpm_limit is None: rpm_limit = sys.maxsize diff --git a/litellm/proxy/hooks/parallel_request_limiter_v3.py b/litellm/proxy/hooks/parallel_request_limiter_v3.py index 8522294d120..d60c17c744f 100644 --- a/litellm/proxy/hooks/parallel_request_limiter_v3.py +++ b/litellm/proxy/hooks/parallel_request_limiter_v3.py @@ -31,7 +31,12 @@ from litellm.litellm_core_utils.prompt_templates.common_utils import ( get_str_from_messages, ) from litellm.proxy._types import UserAPIKeyAuth -from litellm.proxy.auth.auth_utils import get_model_rate_limit_from_metadata +from litellm.proxy.auth.auth_utils import ( + get_key_tag_rpm_limit, + get_model_rate_limit_from_metadata, +) +from litellm.proxy.auth.budget_throttle import throttled_limit +from litellm.proxy.common_utils.http_parsing_utils import get_tags_from_request_body from litellm.proxy.common_utils.proxy_rate_limit_error import ( ProxyRateLimitError, map_v3_rate_limit_type, @@ -239,6 +244,10 @@ TPM_RESERVED_SCOPES_KEY = "_litellm_tpm_reserved_scopes" # does not double-refund. TPM_RESERVATION_RELEASED_KEY = "_litellm_tpm_reservation_released" RATE_LIMIT_DESCRIPTORS_KEY = "_litellm_rate_limit_descriptors" +# Pre-call RateLimitResponse stashed here so streaming success logging can +# mirror ``x-ratelimit-*`` headers into the SLP. Streaming exits +# common_request_processing before ``async_post_call_success_hook`` runs. +RATE_LIMIT_RESPONSE_KEY = "_litellm_proxy_rate_limit_response" # Stash keys live ONLY in metadata channels — never at the top level of the # request body. Top-level keys are forwarded as body params to upstream # providers, which reject unknown fields with 400/429 errors. @@ -248,6 +257,7 @@ _LITELLM_STASH_KEYS: Tuple[str, ...] = ( TPM_RESERVED_SCOPES_KEY, TPM_RESERVATION_RELEASED_KEY, RATE_LIMIT_DESCRIPTORS_KEY, + RATE_LIMIT_RESPONSE_KEY, ) @@ -956,7 +966,7 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): for _idx, (keys, args, meta) in enumerate(descriptor_groups): try: - raw = await self.check_and_increment_by_n_script( + raw = await self.check_and_increment_by_n_script( # pyright: ignore[reportOptionalCall] # sole caller guards it is not None keys=keys, args=args, ) @@ -1299,6 +1309,43 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): ) ) + def _add_tag_per_key_rate_limit_descriptor( + self, + user_api_key_dict: UserAPIKeyAuth, + data: dict, + descriptors: list[RateLimitDescriptor], + ) -> None: + """ + Add per-request-tag rpm limit descriptors for the API key. + + Each tag carried on the request that has a configured limit gets its own + ``{api_key}:{tag}`` counter, so a burst on one tag/group never consumes + another's budget. Tags without a configured limit fall through to the + key-level descriptor. + """ + if not user_api_key_dict.api_key: + return + + tag_rpm_limit = get_key_tag_rpm_limit(user_api_key_dict) or {} + if not tag_rpm_limit: + return + + for tag in dict.fromkeys(get_tags_from_request_body(data)): + rpm_limit = tag_rpm_limit.get(tag) + if rpm_limit is None: + continue + descriptors.append( + RateLimitDescriptor( + key="tag_per_key", + value=f"{user_api_key_dict.api_key}:{tag}", + rate_limit={ + "requests_per_unit": rpm_limit, + "tokens_per_unit": None, + "window_size": self.window_size, + }, + ) + ) + def _add_mcp_per_key_rate_limit_descriptor( self, user_api_key_dict: UserAPIKeyAuth, @@ -1549,18 +1596,19 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): or user_api_key_dict.tpm_limit is not None or user_api_key_dict.max_parallel_requests is not None ): + throttle_pct = user_api_key_dict.budget_throttle_pct descriptors.append( RateLimitDescriptor( key="api_key", value=user_api_key_dict.api_key, rate_limit={ "requests_per_unit": self._get_enforced_limit( - limit_value=user_api_key_dict.rpm_limit, + limit_value=throttled_limit(user_api_key_dict.rpm_limit, throttle_pct), limit_type=rpm_limit_type, model_has_failures=model_has_failures, ), "tokens_per_unit": self._get_enforced_limit( - limit_value=user_api_key_dict.tpm_limit, + limit_value=throttled_limit(user_api_key_dict.tpm_limit, throttle_pct), limit_type=tpm_limit_type, model_has_failures=model_has_failures, ), @@ -1643,6 +1691,13 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): descriptors=descriptors, ) + # Per-request-tag rate limits scoped to this key + self._add_tag_per_key_rate_limit_descriptor( + user_api_key_dict=user_api_key_dict, + data=data, + descriptors=descriptors, + ) + # REST MCP calls pass the raw body through this hook before server # resolution; only the later synthetic hook payload may carry this key. if call_type == CallTypes.call_mcp_tool.value and "server_id" not in data: @@ -1959,6 +2014,7 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): # Org Level Rate Limits descriptors.extend(self.create_organization_rate_limit_descriptor(user_api_key_dict, requested_model)) + # Only check rate limits if we have descriptors with actual limits if descriptors: # First pass: RPM and max_parallel_requests sliding-window check. @@ -1986,6 +2042,13 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): else: # add descriptors to request headers data["litellm_proxy_rate_limit_response"] = response + # Mirror into metadata so streaming success logging can find + # it via ``kwargs["litellm_params"]["metadata"]``. + self._stash_value_in_metadata_channels( + data=data, + key=RATE_LIMIT_RESPONSE_KEY, + value=response, + ) # ---------------------------------------------------------------- # TPM token reservation @@ -2082,6 +2145,13 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): stored_response.setdefault("statuses", []).extend(tpm_response["statuses"]) elif tpm_response["statuses"]: data["litellm_proxy_rate_limit_response"] = tpm_response + # Keep the metadata stash in sync when this is the + # first snapshot written. + self._stash_value_in_metadata_channels( + data=data, + key=RATE_LIMIT_RESPONSE_KEY, + value=tpm_response, + ) verbose_proxy_logger.debug(f"TPM tokens reserved: {estimated_tokens} for model {requested_model}") @@ -2267,6 +2337,23 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): return "total" # default to total return specified_rate_limit_type + @staticmethod + def _merge_ratelimit_statuses_into_additional_headers( + additional_headers: Dict[str, Any], + statuses: List[RateLimitStatus], + ) -> Dict[str, Any]: + """ + Return ``additional_headers`` extended with + ``x-ratelimit-{descriptor_key}-{remaining|limit}-{rate_limit_type}`` + entries. Non-mutating so callers pick their own target dict. + """ + merged: Dict[str, Any] = dict(additional_headers) + for status in statuses: + prefix = f"x-ratelimit-{status['descriptor_key']}" + merged[f"{prefix}-remaining-{status['rate_limit_type']}"] = status["limit_remaining"] + merged[f"{prefix}-limit-{status['rate_limit_type']}"] = status["current_limit"] + return merged + @staticmethod def _stash_value_in_metadata_channels( data: Dict[str, Any], @@ -2647,6 +2734,112 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): except Exception as e: verbose_proxy_logger.exception(f"Error in rate limit success event: {str(e)}") + async def async_logging_hook( + self, + kwargs: dict, + result: Any, + call_type: str, + ) -> Tuple[dict, Any]: + """ + Mirror the pre-call rate-limit snapshot into the SLP so streaming + success callbacks see the same ``x-ratelimit-*`` headers the + non-streaming path writes via ``async_post_call_success_hook``. + Runs in the earlier of the two callback loops inside + ``async_success_handler`` so downstream callbacks see the values + regardless of registration order. Idempotent for non-streaming. + """ + self._mirror_ratelimit_response_into_logging_payload( + kwargs=kwargs, + response_obj=result, + ) + return kwargs, result + + def _mirror_ratelimit_response_into_logging_payload( + self, + kwargs: Any, + response_obj: Any, + ) -> None: + """ + Copy the stashed ``RateLimitResponse`` into the SLP's + ``hidden_params.additional_headers`` and the response object's + ``_hidden_params.additional_headers`` (when the latter is a dict). + """ + if not isinstance(kwargs, dict): + return + + standard_logging_object = kwargs.get("standard_logging_object") + standard_logging_metadata: Optional[Dict[str, Any]] = None + if isinstance(standard_logging_object, dict): + slp_metadata = standard_logging_object.get("metadata") + if isinstance(slp_metadata, dict): + standard_logging_metadata = slp_metadata + + statuses = self._narrow_ratelimit_statuses( + self._lookup_stashed_value( + kwargs=kwargs, + standard_logging_metadata=standard_logging_metadata, + key=RATE_LIMIT_RESPONSE_KEY, + ) + ) + if not statuses: + return + + if isinstance(standard_logging_object, dict): + hidden_params = standard_logging_object.get("hidden_params") + if not isinstance(hidden_params, dict): + hidden_params = {} + existing = hidden_params.get("additional_headers") + hidden_params["additional_headers"] = self._merge_ratelimit_statuses_into_additional_headers( + additional_headers=existing if isinstance(existing, dict) else {}, + statuses=statuses, + ) + standard_logging_object["hidden_params"] = hidden_params + + response_hidden = getattr(response_obj, "_hidden_params", None) + if isinstance(response_hidden, dict): + existing = response_hidden.get("additional_headers") + response_hidden["additional_headers"] = self._merge_ratelimit_statuses_into_additional_headers( + additional_headers=existing if isinstance(existing, dict) else {}, + statuses=statuses, + ) + + @staticmethod + def _narrow_ratelimit_statuses(stashed: Any) -> List[RateLimitStatus]: + """ + Narrow a stashed ``RateLimitResponse``-shaped dict to a typed + ``statuses`` list. Entries missing any header-write field are dropped; + an empty list means "nothing to mirror". + """ + if not isinstance(stashed, dict): + return [] + raw_statuses = stashed.get("statuses") + if not isinstance(raw_statuses, list): + return [] + narrowed: List[RateLimitStatus] = [] + for entry in raw_statuses: + if not isinstance(entry, dict): + continue + descriptor_key = entry.get("descriptor_key") + rate_limit_type = entry.get("rate_limit_type") + current_limit = entry.get("current_limit") + limit_remaining = entry.get("limit_remaining") + if ( + isinstance(descriptor_key, str) + and rate_limit_type in ("requests", "tokens", "max_parallel_requests") + and isinstance(current_limit, int) + and isinstance(limit_remaining, int) + ): + narrowed.append( + RateLimitStatus( + code=entry.get("code", "OK") if isinstance(entry.get("code"), str) else "OK", + current_limit=current_limit, + limit_remaining=limit_remaining, + rate_limit_type=rate_limit_type, + descriptor_key=descriptor_key, + ) + ) + return narrowed + async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time): """ On failure: decrement max_parallel_requests and refund the upfront @@ -2787,15 +2980,10 @@ class _PROXY_MaxParallelRequestsHandler_v3(CustomLogger): if isinstance(_hidden_params, BaseModel): _hidden_params = _hidden_params.model_dump() - _additional_headers = _hidden_params.get("additional_headers", {}) or {} - - # Add rate limit headers - for status in litellm_proxy_rate_limit_response["statuses"]: - prefix = f"x-ratelimit-{status['descriptor_key']}" - _additional_headers[f"{prefix}-remaining-{status['rate_limit_type']}"] = status[ - "limit_remaining" - ] - _additional_headers[f"{prefix}-limit-{status['rate_limit_type']}"] = status["current_limit"] + _additional_headers = self._merge_ratelimit_statuses_into_additional_headers( + additional_headers=_hidden_params.get("additional_headers", {}) or {}, + statuses=litellm_proxy_rate_limit_response["statuses"], + ) setattr( response, diff --git a/litellm/proxy/hooks/prompt_injection_detection.py b/litellm/proxy/hooks/prompt_injection_detection.py index 390074bf005..2d55a644bb2 100644 --- a/litellm/proxy/hooks/prompt_injection_detection.py +++ b/litellm/proxy/hooks/prompt_injection_detection.py @@ -250,7 +250,9 @@ class _OPTIONAL_PromptInjectionDetection(CustomLogger): self.print_verbose(f"Received LLM Moderation response: {response}") self.print_verbose(f"llm_api_fail_call_string: {self.prompt_injection_params.llm_api_fail_call_string}") if isinstance(response, litellm.ModelResponse) and isinstance(response.choices[0], litellm.Choices): - if self.prompt_injection_params.llm_api_fail_call_string in response.choices[0].message.content: # type: ignore + fail_call_string = self.prompt_injection_params.llm_api_fail_call_string + content = response.choices[0].message.content + if fail_call_string is not None and content is not None and fail_call_string in content: is_prompt_attack = True if is_prompt_attack is True: diff --git a/litellm/proxy/hooks/proxy_track_cost_callback.py b/litellm/proxy/hooks/proxy_track_cost_callback.py index 9c09231cd9f..b6342f4fa1a 100644 --- a/litellm/proxy/hooks/proxy_track_cost_callback.py +++ b/litellm/proxy/hooks/proxy_track_cost_callback.py @@ -190,6 +190,11 @@ class _ProxyDBLogger(CustomLogger): litellm_params = kwargs.get("litellm_params", {}) or {} end_user_id = get_end_user_id_for_cost_tracking(litellm_params) metadata = get_litellm_metadata_from_kwargs(kwargs=kwargs) + # Only fetch key details when user_id wasn't already populated (e.g. direct MCP REST calls). + # Avoids a cache/DB lookup on every normal LLM request. + if metadata.get("user_api_key") and not metadata.get("user_api_key_user_id"): + metadata = await _ProxyDBLogger._enrich_failure_metadata_with_key_info(metadata=metadata) + _write_spend_metadata_to_kwargs(kwargs=kwargs, metadata=metadata) budget_reservation = _get_budget_reservation_from_metadata(metadata=metadata) user_id = cast(Optional[str], metadata.get("user_api_key_user_id", None)) team_id = cast(Optional[str], metadata.get("user_api_key_team_id", None)) @@ -388,6 +393,20 @@ class _ProxyDBLogger(CustomLogger): return +def _write_spend_metadata_to_kwargs(kwargs: dict, metadata: dict) -> None: + patch = {k: v for k, v in metadata.items() if (k.startswith("user_api_key") or k == "tags") and v is not None} + if not patch: + return + + litellm_params = kwargs.setdefault("litellm_params", {}) + for bucket_name in ("litellm_metadata", "metadata"): + bucket = litellm_params.get(bucket_name) + if isinstance(bucket, dict): + for key, value in patch.items(): + if bucket.get(key) is None: + bucket[key] = value + + def _should_track_cost_callback( user_api_key: Optional[str], user_id: Optional[str], diff --git a/litellm/proxy/hooks/responses_id_security.py b/litellm/proxy/hooks/responses_id_security.py index a7b05c57dac..cba58506a00 100644 --- a/litellm/proxy/hooks/responses_id_security.py +++ b/litellm/proxy/hooks/responses_id_security.py @@ -44,6 +44,7 @@ class ResponsesIDSecurity(CustomLogger): "aget_responses", "adelete_responses", "acancel_responses", + "alist_input_items", } if call_type not in responses_api_call_types: return None @@ -54,7 +55,7 @@ class ResponsesIDSecurity(CustomLogger): original_response_id, user_id, team_id = self._decrypt_response_id(previous_response_id) self.check_user_access_to_response_id(user_id, team_id, user_api_key_dict) data["previous_response_id"] = original_response_id - elif call_type in {"aget_responses", "adelete_responses", "acancel_responses"}: + elif call_type in {"aget_responses", "adelete_responses", "acancel_responses", "alist_input_items"}: response_id = data.get("response_id") if response_id and self._is_encrypted_response_id(response_id): diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 0ffb0337545..afd8a437cc1 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -15,6 +15,9 @@ from litellm._logging import verbose_logger, verbose_proxy_logger from litellm._service_logger import ServiceLogging from litellm.constants import PRE_CALL_EXECUTED_GUARDRAILS_KEY from litellm.litellm_core_utils.credential_accessor import CredentialAccessor +from litellm.litellm_core_utils.initialize_dynamic_callback_params import ( + iter_client_callback_metadata_dicts, +) from litellm.litellm_core_utils.safe_json_loads import safe_json_loads from litellm.litellm_core_utils.url_utils import is_url_destination_allowed_by_host from litellm.proxy._types import ( @@ -153,6 +156,7 @@ _UNTRUSTED_ROOT_CONTROL_FIELDS = ( "_code_interpreter_interception_active", "_code_interpreter_interception_converted_stream", "_code_interpreter_interception_sandbox_key", + "_code_interpreter_interception_session_scoped", "max_agentic_loops", ) @@ -301,6 +305,24 @@ def _key_or_team_allows_client_pricing_override( ) +def _strip_client_message_redaction_opt_out(data: dict[str, Any]) -> None: + stripped: list[str] = [] + if "turn_off_message_logging" in data and _is_false_like(data["turn_off_message_logging"]): + stripped.append("turn_off_message_logging") + data.pop("turn_off_message_logging", None) + for slot_label, metadata in iter_client_callback_metadata_dicts(data): + if "turn_off_message_logging" in metadata and _is_false_like(metadata["turn_off_message_logging"]): + stripped.append(f"{slot_label}.turn_off_message_logging") + metadata.pop("turn_off_message_logging", None) + if stripped: + verbose_proxy_logger.debug( + "Stripped client-supplied message-redaction opt-out fields from request body: %s. " + "Set `allow_client_message_redaction_opt_out: true` on the key or team metadata " + "to keep these values.", + ", ".join(stripped), + ) + + def _strip_client_pricing_overrides(data: Dict[str, Any]) -> None: """Drop pricing overrides from the request body and any metadata variant. @@ -1307,13 +1329,6 @@ async def add_litellm_data_to_request( _headers, allow_client_message_redaction_opt_out=_allow_client_message_redaction_opt_out, ) - if ( - not _allow_client_message_redaction_opt_out - and litellm.turn_off_message_logging is True - and "turn_off_message_logging" in data - and _is_false_like(data["turn_off_message_logging"]) - ): - data.pop("turn_off_message_logging", None) verbose_proxy_logger.debug(f"Request Headers: {_headers}") verbose_proxy_logger.debug(f"Raw Headers: {_raw_headers}") @@ -1465,6 +1480,9 @@ async def add_litellm_data_to_request( if not _key_or_team_allows_client_pricing_override(user_api_key_dict): _strip_client_pricing_overrides(data) + if not _allow_client_message_redaction_opt_out and litellm.turn_off_message_logging is True: + _strip_client_message_redaction_opt_out(data) + # Fill in the proxy_server_request body snapshot now that metadata has # been parsed. Consumers (standard_logging_payload, lago, # spend_tracking_utils, streaming_iterator) read `body` to audit the @@ -1648,6 +1666,16 @@ async def add_litellm_data_to_request( tags_to_add=tags, ) + if _metadata_variable_name != "metadata": + _user_metadata = data.get("metadata") + if isinstance(_user_metadata, dict): + _user_tags = _user_metadata.get("tags") + if isinstance(_user_tags, list) and _user_tags: + data[_metadata_variable_name]["tags"] = LiteLLMProxyRequestSetup._merge_tags( + request_tags=data[_metadata_variable_name].get("tags"), + tags_to_add=_user_tags, + ) + # Team Callbacks controls callback_settings_obj = _get_dynamic_logging_metadata( user_api_key_dict=user_api_key_dict, proxy_config=proxy_config diff --git a/litellm/proxy/management_endpoints/cache_settings_endpoints.py b/litellm/proxy/management_endpoints/cache_settings_endpoints.py index 6aa3dbbf902..9f45cb619aa 100644 --- a/litellm/proxy/management_endpoints/cache_settings_endpoints.py +++ b/litellm/proxy/management_endpoints/cache_settings_endpoints.py @@ -38,13 +38,35 @@ from litellm.types.management_endpoints import ( router = APIRouter() # Cache fields holding credentials. Masked on read so plaintext Redis / -# Sentinel passwords never leave the server in a GET response. -_CACHE_SENSITIVE_FIELDS: set = {"password", "sentinel_password"} +# Sentinel passwords never leave the server in a GET response. `url` is here +# because a Redis/Valkey URL can embed a password inline +# (e.g. redis://:secret@host:6379/1). +_CACHE_SENSITIVE_FIELDS: set = {"password", "sentinel_password", "url"} _REDACTED_VALUE = "***REDACTED***" +_URL_OVERRIDDEN_CONNECTION_FIELDS: frozenset = frozenset({"host", "port", "db", "password", "username"}) + + +def _resolve_cache_url_precedence(settings: Mapping[str, Any]) -> dict[str, Any]: + """Return cache settings with the url-vs-discrete-fields ambiguity resolved. + + When a full ``url`` is supplied it wins: the discrete + host/port/db/password/username fields are dropped so the persisted config + is unambiguous and matches runtime resolution in ``litellm._redis`` + (``redis.Redis.from_url`` ignores them). Cluster mode + (``redis_startup_nodes``) is exempt because it authenticates via the + discrete fields rather than a url. + """ + url = settings.get("url") + has_url = isinstance(url, str) and url.strip() != "" + if not has_url or settings.get("redis_startup_nodes"): + return dict(settings) + return {k: v for k, v in settings.items() if k not in _URL_OVERRIDDEN_CONNECTION_FIELDS} + + def _redact_settings(settings: Optional[Mapping[str, Any]]) -> Dict[str, Any]: """Replace every value in a settings map with a fixed marker. @@ -311,7 +333,7 @@ async def test_cache_connection( from litellm import Cache try: - cache_settings = request.cache_settings.copy() + cache_settings = _resolve_cache_url_precedence(request.cache_settings) verbose_proxy_logger.debug("Testing cache connection with settings: %s", cache_settings) # Only support Redis for now @@ -378,7 +400,7 @@ async def update_cache_settings( ) try: - cache_settings = request.cache_settings.copy() + cache_settings = _resolve_cache_url_precedence(request.cache_settings) # Snapshot the prior settings (key set only — values get redacted in # the audit row) so the audit-log entry shows which fields changed. diff --git a/litellm/proxy/management_endpoints/coordination_redis_endpoints.py b/litellm/proxy/management_endpoints/coordination_redis_endpoints.py new file mode 100644 index 00000000000..7ab4e3019c3 --- /dev/null +++ b/litellm/proxy/management_endpoints/coordination_redis_endpoints.py @@ -0,0 +1,431 @@ +""" +COORDINATION REDIS SETTINGS MANAGEMENT + +Endpoints for managing `general_settings.coordination_redis` - the standalone +Redis the proxy uses for cross-pod coordination (tpm/rpm rate limits, spend +tracking, pod lock manager, shared health checks), configured independently of +the response-cache backend. + +GET /coordination_redis/settings - Get the coordination Redis settings, field metadata, and which source is active +POST /coordination_redis/settings - Save coordination Redis settings to the database +POST /coordination_redis/settings/test - Test a coordination Redis connection with the provided credentials +""" + +import asyncio +import json +from collections.abc import Mapping +from contextlib import suppress +from datetime import datetime, timezone +from typing import Optional + +from fastapi import APIRouter, Depends, Header, HTTPException +from pydantic import BaseModel, Field, TypeAdapter, ValidationError + +import litellm +from litellm._logging import verbose_proxy_logger +from litellm._uuid import uuid +from litellm.caching.caching import RedisCache +from litellm.caching.redis_cluster_cache import RedisClusterCache +from litellm.proxy._types import ( + AUDIT_ACTIONS, + CoordinationRedisParams, + LiteLLM_AuditLogs, + LitellmTableNames, + LitellmUserRoles, + UserAPIKeyAuth, +) +from litellm.proxy.auth.user_api_key_auth import user_api_key_auth +from litellm.proxy.utils import invalidate_config_param +from litellm.repositories.config_repository import ConfigRepository +from litellm.secret_managers.main import get_secret_str +from litellm.types.management_endpoints import ( + COORDINATION_REDIS_SETTINGS_FIELDS, + CoordinationRedisSettingsField, + CoordinationRedisSource, +) + +router = APIRouter() + +_GENERAL_SETTINGS_PARAM_NAME = "general_settings" +_COORDINATION_REDIS_KEY = "coordination_redis" + +# Fields that carry credentials. Redacted on read so a plaintext Redis / +# Sentinel password never leaves the server, and scrubbed out of connection-test +# error strings. `url` is here because a Redis url can embed a password inline +# (e.g. redis://:secret@host:6379/1). +_SENSITIVE_FIELDS: frozenset[str] = frozenset({"password", "sentinel_password", "url"}) + +_REDACTED_VALUE = "***REDACTED***" + +_ENV_REF_PREFIX = "os.environ/" + +_PING_TIMEOUT_SECONDS = 5.0 + +_SETTINGS_ADAPTER: TypeAdapter[dict[str, object]] = TypeAdapter(dict[str, object]) + + +def _enforce_proxy_admin(user_api_key_dict: UserAPIKeyAuth) -> None: + if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN: + raise HTTPException( + status_code=403, + detail={"error": "Only proxy admins can manage coordination Redis settings"}, + ) + + +def _resolve_env_ref(value: object) -> object: + """Resolve an `os.environ/VAR` reference to its value, passing anything else through.""" + if isinstance(value, str) and value.startswith(_ENV_REF_PREFIX): + return get_secret_str(value) + return value + + +def _resolve_env_refs(settings: Mapping[str, object]) -> dict[str, object]: + return {key: _resolve_env_ref(value) for key, value in settings.items()} + + +def _redact_credentials(settings: Mapping[str, object]) -> dict[str, object]: + """Replace credential-bearing values with a fixed marker, keeping the rest intact.""" + return { + key: (_REDACTED_VALUE if key in _SENSITIVE_FIELDS and value is not None else value) + for key, value in settings.items() + } + + +def _redact_all_values(settings: Optional[Mapping[str, object]]) -> dict[str, object]: + """Replace every value with a fixed marker, preserving the key set. + + The audit row shows *which* fields changed without the audit table becoming + a credential-harvest sink. + """ + if not settings: + return {} + return {key: _REDACTED_VALUE for key in settings} + + +def _credential_values(settings: Mapping[str, object]) -> tuple[str, ...]: + return tuple( + str(value) for key, value in settings.items() if key in _SENSITIVE_FIELDS and isinstance(value, (str, int)) + ) + + +def _scrub_credentials(message: str, settings: Mapping[str, object]) -> str: + """Strip any credential value the caller supplied out of an error string. + + Redis client errors routinely echo the connection url (password inline) or + the auth error back to the caller. + """ + scrubbed = message + for secret in _credential_values(settings): + if secret: + scrubbed = scrubbed.replace(secret, _REDACTED_VALUE) + return scrubbed + + +def _merge_over_saved( + incoming: Mapping[str, object], + saved: Mapping[str, object], +) -> dict[str, object]: + """Restore the real credential behind every value the caller echoed back redacted. + + GET returns credentials as ``***REDACTED***``; an admin who edits the + non-secret fields and re-submits would otherwise test (and save) the marker + as the password. + """ + return { + key: (saved[key] if value == _REDACTED_VALUE and key in saved else value) for key, value in incoming.items() + } + + +def _validated_params(settings: Mapping[str, object]) -> CoordinationRedisParams: + """Validate settings the way startup does: resolve env refs, then require a connection target.""" + try: + params = CoordinationRedisParams(**_resolve_env_refs(settings)) + except ValidationError as e: + invalid_fields = sorted({str(error["loc"][0]) for error in e.errors() if error["loc"]}) + raise HTTPException( + status_code=400, + detail={"error": f"Invalid coordination_redis settings for fields: {invalid_fields}"}, + ) + if not params.has_connection_target(): + raise HTTPException( + status_code=400, + detail={ + "error": ( + "coordination_redis needs a connection target: " + "set one of host, url, startup_nodes, or sentinel_nodes" + ) + }, + ) + return params + + +async def _read_general_settings() -> dict[str, object]: + """Read the persisted `general_settings` config row (empty when unset or no DB).""" + from litellm.proxy.proxy_server import prisma_client + + if prisma_client is None: + return {} + config_param = await ConfigRepository(prisma_client).get_param(_GENERAL_SETTINGS_PARAM_NAME) + if config_param is None or config_param.param_value is None: + return {} + return _SETTINGS_ADAPTER.validate_python(config_param.param_value) + + +async def get_persisted_coordination_redis_settings() -> Optional[dict[str, object]]: + """The coordination_redis block saved to the database, if any. + + Read at startup so settings saved from the admin UI take effect on the next + boot, and used here so a read reports what the proxy would boot with. + """ + persisted = (await _read_general_settings()).get(_COORDINATION_REDIS_KEY) + if isinstance(persisted, dict): + return _SETTINGS_ADAPTER.validate_python(persisted) + return None + + +async def _current_coordination_redis_settings() -> Optional[dict[str, object]]: + """The coordination_redis block the proxy would boot with. + + The persisted row wins over the yaml-loaded config state because startup + applies the DB `general_settings` row over the file config. + """ + from litellm.proxy.proxy_server import proxy_config + + persisted = await get_persisted_coordination_redis_settings() + if persisted is not None: + return persisted + + config_state = _SETTINGS_ADAPTER.validate_python(proxy_config.get_config_state()) + general_settings = config_state.get(_GENERAL_SETTINGS_PARAM_NAME) + if not isinstance(general_settings, dict): + return None + from_file = general_settings.get(_COORDINATION_REDIS_KEY) + if isinstance(from_file, dict): + return _SETTINGS_ADAPTER.validate_python(from_file) + return None + + +def _coordination_redis_source(settings: Optional[Mapping[str, object]]) -> Optional[CoordinationRedisSource]: + """Which source the proxy's coordination Redis comes from, in startup precedence order. + + Mirrors `ProxyConfig._init_coordination_redis` -> `ProxyConfig._init_cache`: + an explicit block wins, else a plain-Redis response-cache backend is + borrowed, else the REDIS_* environment fallback applies. + """ + from litellm.proxy.proxy_server import _environment_has_redis_connection_target + + if settings: + return "coordination_redis" + cache_backend = litellm.cache.cache if litellm.cache is not None else None + if isinstance(cache_backend, (RedisCache, RedisClusterCache)): + return "cache_backend" + if _environment_has_redis_connection_target(): + return "environment" + return None + + +def _log_audit_task_exception(task: "asyncio.Task[None]") -> None: + """Surface a fire-and-forget audit-log task failure as a warning.""" + if task.cancelled(): + return + exc = task.exception() + if exc is not None: + verbose_proxy_logger.warning("Failed to write coordination-redis-settings audit log: %s", exc) + + +async def _emit_coordination_redis_audit_log( + *, + action: AUDIT_ACTIONS, + before_settings: Optional[Mapping[str, object]], + after_settings: Optional[Mapping[str, object]], + user_api_key_dict: UserAPIKeyAuth, + litellm_changed_by: Optional[str], +) -> None: + """Emit an audit-log row for a /coordination_redis/settings mutation.""" + if litellm.store_audit_logs is not True: + return + + from litellm.proxy.management_helpers.audit_logs import create_audit_log_for_update + from litellm.proxy.proxy_server import litellm_proxy_admin_name + + task = asyncio.create_task( + create_audit_log_for_update( + request_data=LiteLLM_AuditLogs( + id=str(uuid.uuid4()), + updated_at=datetime.now(timezone.utc), + changed_by=litellm_changed_by or user_api_key_dict.user_id or litellm_proxy_admin_name, + changed_by_api_key=user_api_key_dict.api_key, + table_name=LitellmTableNames.CONFIG_TABLE_NAME, + object_id=_COORDINATION_REDIS_KEY, + action=action, + updated_values=json.dumps({"settings": _redact_all_values(after_settings)}, default=str), + before_value=json.dumps({"settings": _redact_all_values(before_settings)}, default=str), + ) + ) + ) + task.add_done_callback(_log_audit_task_exception) + + +class CoordinationRedisSettingsResponse(BaseModel): + values: dict[str, object] = Field(description="Current coordination Redis settings, with credentials redacted") + fields: list[CoordinationRedisSettingsField] = Field( + description="List of all configurable coordination Redis settings with metadata" + ) + source: Optional[CoordinationRedisSource] = Field( + description="Where the proxy's coordination Redis comes from; null when it has none" + ) + + +class CoordinationRedisSettingsRequest(BaseModel): + settings: dict[str, object] = Field(description="Coordination Redis connection params") + + +class CoordinationRedisTestResponse(BaseModel): + status: str = Field(description="Connection status: 'healthy' or 'unhealthy'") + error: Optional[str] = Field(default=None, description="Error message if the connection failed") + + +@router.get( + "/coordination_redis/settings", + tags=["Coordination Redis Settings"], + dependencies=[Depends(user_api_key_auth)], + response_model=CoordinationRedisSettingsResponse, +) +async def get_coordination_redis_settings( + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +) -> CoordinationRedisSettingsResponse: + """ + Get the coordination Redis configuration and available settings. + + Returns: + - values: current coordination Redis settings, with password/sentinel_password/url redacted + - fields: all configurable settings with their metadata (type, description, default, section) + - source: "coordination_redis" | "cache_backend" | "environment" | null + """ + _enforce_proxy_admin(user_api_key_dict) + + settings = await _current_coordination_redis_settings() + source = _coordination_redis_source(settings) + + values = _redact_credentials(settings or {}) + fields = [field.model_copy(deep=True) for field in COORDINATION_REDIS_SETTINGS_FIELDS] + for field in fields: + if field.field_name in values: + field.field_value = values[field.field_name] + + return CoordinationRedisSettingsResponse(values=values, fields=fields, source=source) + + +@router.post( + "/coordination_redis/settings", + tags=["Coordination Redis Settings"], + dependencies=[Depends(user_api_key_auth)], +) +async def update_coordination_redis_settings( + request: CoordinationRedisSettingsRequest, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), + litellm_changed_by: Optional[str] = Header( + None, + description="The litellm-changed-by header enables tracking of actions performed by authorized users on behalf of other users, providing an audit trail for accountability", + ), +) -> dict[str, object]: + """ + Save coordination Redis settings under `general_settings.coordination_redis`. + + Parameters: + - settings: dict - Redis connection params (host, port, username, password, url, ssl, startup_nodes, sentinel_nodes, sentinel_password, service_name). Values may be `os.environ/VAR` references, which are stored as written and resolved at startup + + The settings are written to the `general_settings` row of LiteLLM_Config, + which startup merges over the yaml config; the proxy picks them up on its + next restart. + """ + from litellm.proxy.proxy_server import prisma_client, store_model_in_db + + _enforce_proxy_admin(user_api_key_dict) + + if prisma_client is None: + raise HTTPException( + status_code=500, + detail={"error": "Database not connected. Please connect a database."}, + ) + + if store_model_in_db is not True: + raise HTTPException( + status_code=500, + detail={"error": "Set `'STORE_MODEL_IN_DB='True'` in your env to enable this feature."}, + ) + + saved_settings = await _current_coordination_redis_settings() + settings = _merge_over_saved(request.settings, saved_settings or {}) + _validated_params(settings) + + general_settings = await _read_general_settings() + before_settings = general_settings.get(_COORDINATION_REDIS_KEY) + action: AUDIT_ACTIONS = "updated" if isinstance(before_settings, dict) else "created" + + await ConfigRepository(prisma_client).set_param( + param_name=_GENERAL_SETTINGS_PARAM_NAME, + param_value={**general_settings, _COORDINATION_REDIS_KEY: settings}, + ) + await invalidate_config_param(_GENERAL_SETTINGS_PARAM_NAME) + + # coordination_redis carries Redis credentials and decides where cross-pod + # rate-limit and spend state lives; an admin repointing it is a + # data-routing pivot, so make the change traceable. + await _emit_coordination_redis_audit_log( + action=action, + before_settings=before_settings if isinstance(before_settings, dict) else None, + after_settings=settings, + user_api_key_dict=user_api_key_dict, + litellm_changed_by=litellm_changed_by, + ) + + return { + "message": "Coordination Redis settings updated successfully. Restart the proxy to apply them.", + "status": "success", + "settings": _redact_credentials(settings), + } + + +@router.post( + "/coordination_redis/settings/test", + tags=["Coordination Redis Settings"], + dependencies=[Depends(user_api_key_auth)], + response_model=CoordinationRedisTestResponse, +) +async def check_coordination_redis_connection( + request: CoordinationRedisSettingsRequest, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +) -> CoordinationRedisTestResponse: + """ + Test a coordination Redis connection with the provided credentials. + + Parameters: + - settings: dict - Redis connection params to test. Credential fields sent back as `***REDACTED***` fall back to the saved value + + Builds a throwaway client (never touching global state) and pings it. + """ + from litellm.proxy.proxy_server import _build_redis_usage_cache + + _enforce_proxy_admin(user_api_key_dict) + + saved_settings = await _current_coordination_redis_settings() + settings = _merge_over_saved(request.settings, saved_settings or {}) + params = _validated_params(settings) + + redis_cache: Optional[RedisCache] = None + try: + redis_cache = _build_redis_usage_cache(params.model_dump(exclude_none=True)) + await asyncio.wait_for(redis_cache.ping(), timeout=_PING_TIMEOUT_SECONDS) + return CoordinationRedisTestResponse(status="healthy") + except asyncio.TimeoutError: + return CoordinationRedisTestResponse( + status="unhealthy", + error=f"Connection timed out after {_PING_TIMEOUT_SECONDS}s", + ) + except Exception as e: # noqa: BLE001 # any client/connection failure is a health verdict, not a 500 + return CoordinationRedisTestResponse(status="unhealthy", error=_scrub_credentials(str(e), settings)) + finally: + if redis_cache is not None: + with suppress(Exception): + await redis_cache.disconnect() diff --git a/litellm/proxy/management_endpoints/customer_endpoints.py b/litellm/proxy/management_endpoints/customer_endpoints.py index 7c8a9b88191..84f67bdc3bc 100644 --- a/litellm/proxy/management_endpoints/customer_endpoints.py +++ b/litellm/proxy/management_endpoints/customer_endpoints.py @@ -15,6 +15,7 @@ from typing import List, Optional import fastapi from fastapi import APIRouter, Depends, HTTPException, Request +from pydantic import BaseModel import litellm from litellm._logging import verbose_proxy_logger @@ -32,10 +33,26 @@ from litellm.repositories.table_repositories import EndUserRepository from litellm.types.proxy.management_endpoints.common_daily_activity import ( SpendAnalyticsPaginatedResponse, ) +from litellm.types.proxy.management_endpoints.customer_endpoints import ( + BlockUsersResponse, + CustomerResponse, + DeleteCustomersResponse, + UnblockUsersResponse, +) router = APIRouter() +def _to_customer_response(record: BaseModel) -> CustomerResponse: + """Validate a raw end-user DB row into the typed customer response. + + object_permission reverse relations and the budget's audit fields are + dropped here by the response model's field set, so callers need no manual + cleanup. + """ + return CustomerResponse.model_validate(record.model_dump()) + + @router.post( "/end_user/block", tags=["Customer Management"], @@ -46,6 +63,7 @@ router = APIRouter() "/customer/block", tags=["Customer Management"], dependencies=[Depends(user_api_key_auth)], + response_model=BlockUsersResponse, ) async def block_user(data: BlockUsers): """ @@ -100,6 +118,7 @@ async def block_user(data: BlockUsers): "/customer/unblock", tags=["Customer Management"], dependencies=[Depends(user_api_key_auth)], + response_model=UnblockUsersResponse, ) async def unblock_user(data: BlockUsers): """ @@ -213,11 +232,12 @@ async def _handle_customer_object_permission_update( "/customer/new", tags=["Customer Management"], dependencies=[Depends(user_api_key_auth)], + response_model=CustomerResponse, ) async def new_end_user( data: NewCustomerRequest, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), -): +) -> CustomerResponse: """ Allow creating a new Customer @@ -370,20 +390,7 @@ async def new_end_user( include={"litellm_budget_table": True, "object_permission": True}, ) - # Convert to dict and clean up recursive fields - response_dict = end_user_record.model_dump() - if response_dict.get("object_permission"): - # Remove reverse relations from object_permission - for field in [ - "teams", - "verification_tokens", - "organizations", - "users", - "end_users", - ]: - response_dict["object_permission"].pop(field, None) - - return response_dict + return _to_customer_response(end_user_record) except Exception as e: verbose_proxy_logger.exception( "litellm.proxy.management_endpoints.customer_endpoints.new_end_user(): Exception occured - {}".format( @@ -404,7 +411,7 @@ async def new_end_user( "/customer/info", tags=["Customer Management"], dependencies=[Depends(user_api_key_auth)], - response_model=LiteLLM_EndUserTable, + response_model=CustomerResponse, ) @router.get( "/end_user/info", @@ -414,7 +421,7 @@ async def new_end_user( ) async def end_user_info( end_user_id: str = fastapi.Query(description="End User ID in the request parameters"), -): +) -> CustomerResponse: """ Get information about an end-user. An `end_user` is a customer (external user) of the proxy. @@ -449,20 +456,7 @@ async def end_user_info( param="end_user_id", ) - # Convert to dict and clean up recursive fields - response_dict = user_info.model_dump(exclude_none=True) - if response_dict.get("object_permission"): - # Remove reverse relations from object_permission - for field in [ - "teams", - "verification_tokens", - "organizations", - "users", - "end_users", - ]: - response_dict["object_permission"].pop(field, None) - - return response_dict + return _to_customer_response(user_info) except Exception as e: verbose_proxy_logger.exception( @@ -477,6 +471,7 @@ async def end_user_info( "/customer/update", tags=["Customer Management"], dependencies=[Depends(user_api_key_auth)], + response_model=CustomerResponse, ) @router.post( "/end_user/update", @@ -487,7 +482,7 @@ async def end_user_info( async def update_end_user( data: UpdateCustomerRequest, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), -): +) -> CustomerResponse: """ Example curl @@ -641,20 +636,7 @@ async def update_end_user( raise ValueError(f"Failed updating customer data. User ID does not exist passed user_id={data.user_id}") verbose_proxy_logger.debug(f"received response from updating prisma client. response={response}") - # Convert to dict and clean up recursive fields - response_dict = response.model_dump() - if response_dict.get("object_permission"): - # Remove reverse relations from object_permission - for field in [ - "teams", - "verification_tokens", - "organizations", - "users", - "end_users", - ]: - response_dict["object_permission"].pop(field, None) - - return response_dict + return _to_customer_response(response) else: raise ValueError(f"user_id is required, passed user_id = {data.user_id}") @@ -671,6 +653,7 @@ async def update_end_user( "/customer/delete", tags=["Customer Management"], dependencies=[Depends(user_api_key_auth)], + response_model=DeleteCustomersResponse, ) @router.post( "/end_user/delete", @@ -681,7 +664,7 @@ async def update_end_user( async def delete_end_user( data: DeleteCustomerRequest, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), -): +) -> DeleteCustomersResponse: """ Delete multiple end-users. @@ -728,10 +711,10 @@ async def delete_end_user( where={"user_id": {"in": data.user_ids}} ) verbose_proxy_logger.debug(f"received response from updating prisma client. response={response}") - return { - "deleted_customers": response, - "message": "Successfully deleted customers with ids: " + str(data.user_ids), - } + return DeleteCustomersResponse( + deleted_customers=response, + message="Successfully deleted customers with ids: " + str(data.user_ids), + ) else: raise ValueError(f"user_id is required, passed user_id = {data.user_ids}") @@ -747,7 +730,7 @@ async def delete_end_user( "/customer/list", tags=["Customer Management"], dependencies=[Depends(user_api_key_auth)], - response_model=List[LiteLLM_EndUserTable], + response_model=List[CustomerResponse], ) @router.get( "/end_user/list", @@ -758,7 +741,7 @@ async def delete_end_user( async def list_end_user( http_request: Request, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), -): +) -> List[CustomerResponse]: """ [Admin-only] List all available customers @@ -791,21 +774,7 @@ async def list_end_user( include={"litellm_budget_table": True, "object_permission": True} ) - returned_response: List[LiteLLM_EndUserTable] = [] - for item in response: - item_dict = item.model_dump() - # Remove reverse relations from object_permission - if item_dict.get("object_permission"): - for field in [ - "teams", - "verification_tokens", - "organizations", - "users", - "end_users", - ]: - item_dict["object_permission"].pop(field, None) - returned_response.append(LiteLLM_EndUserTable(**item_dict)) - return returned_response + return [_to_customer_response(item) for item in response] except Exception as e: verbose_proxy_logger.exception( diff --git a/litellm/proxy/management_endpoints/internal_user_endpoints.py b/litellm/proxy/management_endpoints/internal_user_endpoints.py index 9374aa3180b..ccd15a68437 100644 --- a/litellm/proxy/management_endpoints/internal_user_endpoints.py +++ b/litellm/proxy/management_endpoints/internal_user_endpoints.py @@ -39,6 +39,7 @@ from litellm.proxy.management_endpoints.common_utils import ( validate_finite_spend, ) from litellm.proxy.management_endpoints.key_management_endpoints import ( + _check_permissions_caller_permission, generate_key_helper_fn, prepare_metadata_fields, ) @@ -373,8 +374,10 @@ async def new_user( - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - soft_budget: Optional[float] - Get alerts when user crosses given budget, doesn't block requests. - model_max_budget: Optional[dict] - Model-specific max budget for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-budgets-to-keys) + - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}. - model_rpm_limit: Optional[float] - Model-specific rpm limit for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-limits-to-keys) - mcp_rpm_limit: Optional[dict] - Per-MCP-server rpm limit, keyed by MCP server name {"github": 100, "slack": 200}. Enforced for keys and teams only; values set on a user are stored but not enforced per user. + - tag_rpm_limit: Optional[dict] - Per-request-tag rpm limit, keyed by request tag {"cell-1": 1000, "cell-2": 500}. Enforced for keys only; values set on a user are stored but not enforced per user. - model_tpm_limit: Optional[float] - Model-specific tpm limit for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-limits-to-keys) - spend: Optional[float] - Amount spent by user. Default is 0. Will be updated by proxy whenever user is used. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"), months ("1mo"). - agent_id: Optional[str] - The agent id associated with the user. @@ -440,6 +443,11 @@ async def new_user( detail=f"Only proxy admins can create administrative users (proxy_admin, proxy_admin_viewer). Attempted to create user with role: {data.user_role}. Your role: {user_api_key_dict.user_role}", ) + _check_permissions_caller_permission( + data=data, + user_api_key_dict=user_api_key_dict, + ) + data_json = data.json() # type: ignore data_json = _update_internal_new_user_params(data_json, data) _hash_password_in_dict(data_json) @@ -1198,6 +1206,11 @@ async def _update_single_user_helper( if not user_request.user_id and not user_request.user_email: raise ValueError("Either user_id or user_email must be provided") + _check_permissions_caller_permission( + data=user_request, + user_api_key_dict=user_api_key_dict, + ) + data_json: dict = user_request.model_dump(exclude_unset=True) non_default_values = _update_internal_user_params(data_json=data_json, data=user_request) _hash_password_in_dict(non_default_values) @@ -1364,8 +1377,10 @@ async def user_update( - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - soft_budget: Optional[float] - Get alerts when user crosses given budget, doesn't block requests. - model_max_budget: Optional[dict] - Model-specific max budget for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-budgets-to-keys) + - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}. - model_rpm_limit: Optional[float] - Model-specific rpm limit for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-limits-to-keys) - mcp_rpm_limit: Optional[dict] - Per-MCP-server rpm limit, keyed by MCP server name {"github": 100, "slack": 200}. Enforced for keys and teams only; values set on a user are stored but not enforced per user. + - tag_rpm_limit: Optional[dict] - Per-request-tag rpm limit, keyed by request tag {"cell-1": 1000, "cell-2": 500}. Enforced for keys only; values set on a user are stored but not enforced per user. - model_tpm_limit: Optional[float] - Model-specific tpm limit for user. [Docs](https://docs.litellm.ai/docs/proxy/users#add-model-specific-limits-to-keys) - spend: Optional[float] - Amount spent by user. Default is 0. Will be updated by proxy whenever user is used. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"), months ("1mo"). - agent_id: Optional[str] - The agent id associated with the user. diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index 4106eae606c..b128b0ea57e 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -111,6 +111,7 @@ from litellm.repositories.verification_token_repository import ( VerificationTokenRepository, ) from litellm.router import Router +from litellm.secret_managers.base_secret_manager import raise_if_unsafe_secret_name from litellm.secret_managers.main import get_secret from litellm.types.proxy.management_endpoints.key_management_endpoints import ( BulkUpdateKeyRequest, @@ -343,7 +344,7 @@ def _personal_key_membership_check( if user_api_key_dict.user_role not in personal_key_generation["allowed_user_roles"]: raise HTTPException( status_code=400, - detail=f"Personal key creation has been restricted by admin. Allowed roles={litellm.key_generation_settings['personal_key_generation']['allowed_user_roles']}. Your role={user_api_key_dict.user_role}", # type: ignore + detail=f"Personal key creation has been restricted by admin. Allowed roles={personal_key_generation['allowed_user_roles']}. Your role={user_api_key_dict.user_role}", ) return True @@ -530,26 +531,33 @@ def _check_allowed_routes_caller_permission( allowed_routes: Optional[list], user_api_key_dict: UserAPIKeyAuth, *, + allowed_routes_was_provided: bool = False, allow_safe_presets: bool = False, ) -> None: """ - Only proxy admins may set `allowed_routes` on a key. + Require PROXY_ADMIN when `allowed_routes` is present in the request body, + unless the caller went through the `key_type` preset flow. - `allowed_routes` overrides the standard role-based route gate in - RouteChecks.non_proxy_admin_allowed_routes_check, so the field is - restricted to admins. Non-admins must instead use `key_type` to pick a - preset bucket — that path goes through `handle_key_type` and re-enters - this function with `allow_safe_presets=True`, which lets the derived - `llm_api_routes` / `info_routes` values through. Raw-body call sites - leave `allow_safe_presets=False` so non-admins can't write those values - directly. + Raw-body call sites pass + `allowed_routes_was_provided="allowed_routes" in data.model_fields_set` so a + caller that omits the field (model default flows through) is distinct from + one that sends any explicit value. + + Post-`handle_key_type` call sites pass `allow_safe_presets=True` with the + values derived by `handle_key_type`; those values are not from the request + body, so `allowed_routes_was_provided` stays False and the safe-preset + carve-out below accepts any list of tokens in + `_NON_ADMIN_SAFE_ALLOWED_ROUTES_PRESETS`. """ - # Empty list is the default on GenerateKeyRequest — treat as "not set". - if not allowed_routes: + if not allowed_routes_was_provided and not allowed_routes: return if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value: return - if allow_safe_presets and all(r in _NON_ADMIN_SAFE_ALLOWED_ROUTES_PRESETS for r in allowed_routes): + if ( + allow_safe_presets + and allowed_routes + and all(r in _NON_ADMIN_SAFE_ALLOWED_ROUTES_PRESETS for r in allowed_routes) + ): return raise HTTPException( status_code=403, @@ -563,24 +571,24 @@ def _check_allowed_routes_caller_permission( def _check_permissions_caller_permission( - permissions: Optional[dict], + data: GenerateRequestBase, user_api_key_dict: UserAPIKeyAuth, ) -> None: """ - Only proxy admins may set the `permissions` dict on a key. + Require PROXY_ADMIN when `permissions` is present in the request body. - The field grants ambient capabilities (e.g. `get_spend_routes` exposes - `/global/spend/*`), so it must follow the same admin gate as - `allowed_routes`. Without this gate a non-admin can self-grant capabilities - they do not hold, including read access to global spend. + Presence is detected via `data.model_fields_set` so a caller that + omits the field (default flows through) is distinct from one that + sends any explicit value. """ - if not permissions: + permissions_in_request = "permissions" in data.model_fields_set + if not permissions_in_request and not data.permissions: return if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value: return raise HTTPException( status_code=403, - detail={"error": "Only proxy admins can set `permissions` on a key."}, + detail={"error": "Only proxy admins can set `permissions`."}, ) @@ -751,6 +759,12 @@ async def _common_key_generation_helper( premium_user=premium_user, ) + if data.throttle_on_budget_exceeded is True and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN.value: + raise HTTPException( + status_code=403, + detail={"error": "Only proxy admins can enable throttle_on_budget_exceeded on a key."}, + ) + if data.metadata is not None and data.metadata.get("service_account_id") is not None and data.team_id is None: await validate_team_id_used_in_service_account_request( team_id=data.team_id, @@ -840,7 +854,7 @@ async def _common_key_generation_helper( team_table=team_table, ) _check_permissions_caller_permission( - permissions=data.permissions, + data=data, user_api_key_dict=user_api_key_dict, ) @@ -965,6 +979,23 @@ async def _common_key_generation_helper( is_proxy_admin=_is_proxy_admin_caller, ) + # Merge default_key_generate_params.object_permission in *after* the team-scope + # checks above, so an admin-configured default (e.g. vector_stores, search_tools) + # is never mistaken for a caller-requested permission and rejected by those + # non-admin/no-team checks. Only fields the caller left unset are filled in. + _default_object_permission = ( + litellm.default_key_generate_params.get("object_permission") + if litellm.default_key_generate_params is not None + else None + ) + if isinstance(_default_object_permission, dict): + _caller_object_permission = data_json.get("object_permission") + if _caller_object_permission is None: + data_json["object_permission"] = dict(_default_object_permission) + elif isinstance(_caller_object_permission, dict): + for _op_field, _op_default_value in _default_object_permission.items(): + _caller_object_permission.setdefault(_op_field, _op_default_value) + data_json = await _set_object_permission( data_json=data_json, prisma_client=prisma_client, @@ -1220,7 +1251,7 @@ async def _check_team_key_limits( ) # Exclude the key being updated to avoid double-counting its limits. # data.key may be a raw key (sk-...) or a pre-hashed token_id. - if isinstance(data, UpdateKeyRequest): + if isinstance(data, UpdateKeyRequest) and data.key is not None: hashed_key = _hash_token_if_needed(data.key) keys = [key for key in keys if key.token != hashed_key] check_team_key_model_specific_limits( @@ -1402,7 +1433,7 @@ async def _check_org_key_limits( ) # Exclude the key being updated to avoid double-counting its limits. # data.key may be a raw key (sk-...) or a pre-hashed token_id. - if isinstance(data, UpdateKeyRequest): + if isinstance(data, UpdateKeyRequest) and data.key is not None: hashed_key = _hash_token_if_needed(data.key) keys = [key for key in keys if key.token != hashed_key] check_org_key_model_specific_limits( @@ -1459,11 +1490,14 @@ async def generate_key_fn( - guardrails: Optional[List[str]] - List of active guardrails for the key - policies: Optional[List[str]] - List of policy names to apply to the key. Policies define guardrails, conditions, and inheritance rules. - disable_global_guardrails: Optional[bool] - Whether to disable global guardrails for the key. + - throttle_on_budget_exceeded: Optional[bool] - When the key exceeds its max_budget, throttle its tpm/rpm to the global budget_exceeded_throttle_percentage instead of blocking the key entirely. - permissions: Optional[dict] - key-specific permissions. Currently just used for turning off pii masking (if connected). Example - {"pii": false} - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}}}. IF null or {} then no model specific budget. + - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}. - model_rpm_limit: Optional[dict] - key-specific model rpm limit. Example - {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model specific rpm limit. - model_tpm_limit: Optional[dict] - key-specific model tpm limit. Example - {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model specific tpm limit. - mcp_rpm_limit: Optional[dict] - key-specific per-MCP-server rpm limit, keyed by MCP server name (alias if set, else the configured name). Example - {"github": 100, "slack": 200}. IF null or {} then no MCP-specific rpm limit. + - tag_rpm_limit: Optional[dict] - key-specific per-request-tag rpm limit, keyed by request tag. Example - {"cell-1": 1000, "cell-2": 500}. Each tag gets an independent counter; requests whose tag is absent fall back to the key-level rpm limit. - tpm_limit_type: Optional[str] - Type of tpm limit. Options: "best_effort_throughput" (no error if we're overallocating tpm), "guaranteed_throughput" (raise an error if we're overallocating tpm), "dynamic" (dynamically exceed limit when no 429 errors). Defaults to "best_effort_throughput". - rpm_limit_type: Optional[str] - Type of rpm limit. Options: "best_effort_throughput" (no error if we're overallocating rpm), "guaranteed_throughput" (raise an error if we're overallocating rpm), "dynamic" (dynamically exceed limit when no 429 errors). Defaults to "best_effort_throughput". - allowed_cache_controls: Optional[list] - List of allowed cache control values. Example - ["no-cache", "no-store"]. See all values - https://docs.litellm.ai/docs/proxy/caching#turn-on--off-caching-per-request @@ -1549,6 +1583,7 @@ async def generate_key_fn( _check_allowed_routes_caller_permission( allowed_routes=data.allowed_routes, user_api_key_dict=user_api_key_dict, + allowed_routes_was_provided="allowed_routes" in data.model_fields_set, ) _check_passthrough_routes_caller_permission( data=data, @@ -1666,6 +1701,7 @@ async def generate_service_account_key_fn( - guardrails: Optional[List[str]] - List of active guardrails for the key - permissions: Optional[dict] - key-specific permissions. Currently just used for turning off pii masking (if connected). Example - {"pii": false} - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}}}. IF null or {} then no model specific budget. + - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}. - model_rpm_limit: Optional[dict] - key-specific model rpm limit. Example - {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model specific rpm limit. - model_tpm_limit: Optional[dict] - key-specific model tpm limit. Example - {"text-davinci-002": 1000, "gpt-3.5-turbo": 1000}. IF null or {} then no model specific tpm limit. - mcp_rpm_limit: Optional[dict] - key-specific per-MCP-server rpm limit, keyed by MCP server name (alias if set, else the configured name). Example - {"github": 100, "slack": 200}. IF null or {} then no MCP-specific rpm limit. @@ -1719,6 +1755,7 @@ async def generate_service_account_key_fn( _check_allowed_routes_caller_permission( allowed_routes=data.allowed_routes, user_api_key_dict=user_api_key_dict, + allowed_routes_was_provided="allowed_routes" in data.model_fields_set, ) _check_passthrough_routes_caller_permission( data=data, @@ -2043,6 +2080,11 @@ async def _process_single_key_update( # Validate max_budget _validate_max_budget(update_key_request.max_budget) + _check_permissions_caller_permission( + data=update_key_request, + user_api_key_dict=user_api_key_dict, + ) + # Get and validate existing key if existing_key_row is None: existing_key_row = await _get_and_validate_existing_key( @@ -2214,11 +2256,16 @@ async def _validate_update_key_data( _check_allowed_routes_caller_permission( allowed_routes=data.allowed_routes, user_api_key_dict=user_api_key_dict, + allowed_routes_was_provided="allowed_routes" in data.model_fields_set, ) _check_passthrough_routes_caller_permission( data=data, user_api_key_dict=user_api_key_dict, ) + _check_permissions_caller_permission( + data=data, + user_api_key_dict=user_api_key_dict, + ) _validate_caller_can_change_key_ownership( data=data, @@ -2279,6 +2326,16 @@ async def _validate_update_key_data( or "budget_limits" in data.model_fields_set ) + _existing_metadata = getattr(existing_key_row, "metadata", None) + _existing_throttle = ( + _existing_metadata.get("throttle_on_budget_exceeded") if isinstance(_existing_metadata, dict) else None + ) + if data.throttle_on_budget_exceeded is True and _existing_throttle is not True and not _is_proxy_admin: + raise HTTPException( + status_code=403, + detail={"error": "Only proxy admins can enable throttle_on_budget_exceeded on a key."}, + ) + # Personal-key bypass: the caller both created the key AND still owns it # (user_id == caller). Checking only created_by would let a demoted admin # who originally created a key for another user continue editing it without @@ -2450,6 +2507,7 @@ async def update_key_fn( - spend: Optional[float] - Amount spent by key - max_budget: Optional[float] - Max budget for key - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} + - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}. - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) - soft_budget: Optional[float] - [TODO] Soft budget limit (warning vs. hard stop). Will trigger a slack alert when this soft budget is reached. - max_parallel_requests: Optional[int] - Rate limit for parallel requests @@ -2458,6 +2516,7 @@ async def update_key_fn( - rpm_limit: Optional[int] - Requests per minute limit - model_rpm_limit: Optional[dict] - Model-specific RPM limits {"gpt-4": 100, "claude-v1": 200} - mcp_rpm_limit: Optional[dict] - Per-MCP-server RPM limits, keyed by MCP server name {"github": 100, "slack": 200} + - tag_rpm_limit: Optional[dict] - Per-request-tag RPM limits, keyed by request tag {"cell-1": 1000, "cell-2": 500}. Each tag gets an independent counter; absent tags fall back to the key-level rpm limit. - model_tpm_limit: Optional[dict] - Model-specific TPM limits {"gpt-4": 100000, "claude-v1": 200000} - tpm_limit_type: Optional[str] - TPM rate limit type - "best_effort_throughput", "guaranteed_throughput", or "dynamic" - rpm_limit_type: Optional[str] - RPM rate limit type - "best_effort_throughput", "guaranteed_throughput", or "dynamic" @@ -2468,6 +2527,7 @@ async def update_key_fn( - guardrails: Optional[List[str]] - List of active guardrails for the key - policies: Optional[List[str]] - List of policy names to apply to the key. Policies define guardrails, conditions, and inheritance rules. - disable_global_guardrails: Optional[bool] - Whether to disable global guardrails for the key. + - throttle_on_budget_exceeded: Optional[bool] - When the key exceeds its max_budget, throttle its tpm/rpm to the global budget_exceeded_throttle_percentage instead of blocking the key entirely. - prompts: Optional[List[str]] - List of prompts that the key is allowed to use. - blocked: Optional[bool] - Whether the key is blocked - aliases: Optional[dict] - Model aliases for the key - [Docs](https://litellm.vercel.app/docs/proxy/virtual_keys#model-aliases) @@ -3490,9 +3550,11 @@ async def generate_key_helper_fn( allowed_cache_controls: Optional[list] = [], permissions: Optional[dict] = {}, model_max_budget: Optional[dict] = {}, + budget_fallbacks: Optional[dict] = None, model_rpm_limit: Optional[dict] = None, model_tpm_limit: Optional[dict] = None, mcp_rpm_limit: Optional[dict] = None, + tag_rpm_limit: Optional[dict] = None, guardrails: Optional[list] = None, policies: Optional[list] = None, prompts: Optional[list] = None, @@ -3566,6 +3628,9 @@ async def generate_key_helper_fn( if mcp_rpm_limit is not None: metadata = metadata or {} metadata["mcp_rpm_limit"] = mcp_rpm_limit + if tag_rpm_limit is not None: + metadata = metadata or {} + metadata["tag_rpm_limit"] = tag_rpm_limit if guardrails is not None: metadata = metadata or {} metadata["guardrails"] = guardrails @@ -3580,6 +3645,7 @@ async def generate_key_helper_fn( metadata_json = json.dumps(metadata) validate_model_max_budget(model_max_budget) model_max_budget_json = json.dumps(model_max_budget) + budget_fallbacks_json = json.dumps(budget_fallbacks or {}) user_role = user_role tpm_limit = tpm_limit rpm_limit = rpm_limit @@ -3632,6 +3698,7 @@ async def generate_key_helper_fn( "allowed_cache_controls": allowed_cache_controls, "permissions": permissions_json, "model_max_budget": model_max_budget_json, + "budget_fallbacks": budget_fallbacks_json, "organization_id": organization_id, "budget_id": budget_id, "blocked": blocked, @@ -3982,6 +4049,7 @@ def _transform_verification_tokens_to_deleted_records( "metadata", "model_spend", "model_max_budget", + "budget_fallbacks", "router_settings", ]: if json_field in record and record[json_field] is not None: @@ -4483,6 +4551,7 @@ async def regenerate_key_fn( - spend: Optional[float] - Amount spent by key - max_budget: Optional[float] - Max budget for key - model_max_budget: Optional[Dict[str, BudgetConfig]] - Model-specific budgets {"gpt-4": {"budget_limit": 0.0005, "time_period": "30d"}} + - budget_fallbacks: Optional[Dict[str, List[str]]] - Per-model fallback chain tried in order when that model's own `model_max_budget` is exceeded, e.g. {"gpt-4o": ["gpt-4o-mini"]}. - budget_duration: Optional[str] - Budget reset period ("30d", "1h", etc.) - soft_budget: Optional[float] - Soft budget limit (warning vs. hard stop). Will trigger a slack alert when this soft budget is reached. - max_parallel_requests: Optional[int] - Rate limit for parallel requests @@ -4530,11 +4599,16 @@ async def regenerate_key_fn( _check_allowed_routes_caller_permission( allowed_routes=data.allowed_routes, user_api_key_dict=user_api_key_dict, + allowed_routes_was_provided="allowed_routes" in data.model_fields_set, ) _check_passthrough_routes_caller_permission( data=data, user_api_key_dict=user_api_key_dict, ) + _check_permissions_caller_permission( + data=data, + user_api_key_dict=user_api_key_dict, + ) # Mirror /key/generate's post-handle_key_type recheck so a # non-admin can't elevate via a key_type preset that the # regenerate flow would otherwise carry through unchecked. @@ -5067,6 +5141,9 @@ async def get_member_team_ids( return _get_member_team_ids_from_objects(user_api_key_dict, team_objects) +VALID_EXPIRES_FILTER_VALUES = frozenset({"active", "expired"}) + + @router.get( "/key/list", tags=["key management"], @@ -5106,6 +5183,10 @@ async def list_keys( False, description="If true (proxy admins only), match user_id/key_alias as case-insensitive substrings instead of exact values. Defaults to false: /key/list matched these exactly before substring search was added, and an exact user_id/key_alias filter must never return another user's keys.", ), + expires: str | None = Query( + None, + description="Filter keys by expiration. 'expired' returns keys whose expires is in the past; 'active' returns keys that never expire or expire in the future. Omit to return keys regardless of expiration.", + ), ) -> KeyListResponseObject: """ List all keys for a given user / team / organization. @@ -5141,6 +5222,12 @@ async def list_keys( detail={"error": "Invalid status value. Currently only 'deleted' is supported."}, ) + if isinstance(expires, str) and expires not in VALID_EXPIRES_FILTER_VALUES: + raise HTTPException( + status_code=400, + detail={"error": "Invalid expires value. Supported: 'active', 'expired'."}, + ) + complete_user_info = await validate_key_list_check( user_api_key_dict=user_api_key_dict, user_id=user_id, @@ -5221,6 +5308,7 @@ async def list_keys( access_group_id=access_group_id, agent_id=agent_id, use_substring_matching=use_substring_matching, + expires_filter=expires if isinstance(expires, str) else None, ) verbose_proxy_logger.debug("Successfully prepared response") @@ -5428,6 +5516,12 @@ def _validate_sort_params(sort_by: Optional[str], sort_order: str) -> Optional[D return order_by +def _build_expires_where_clause(expires_filter: str, now: datetime) -> dict[str, Any]: + if expires_filter == "expired": + return {"AND": [{"expires": {"not": None}}, {"expires": {"lt": now}}]} + return {"OR": [{"expires": None}, {"expires": {"gte": now}}]} + + def _build_key_filter_conditions( user_id: Optional[str], team_id: Optional[str], @@ -5442,6 +5536,7 @@ def _build_key_filter_conditions( access_group_id: Optional[str] = None, agent_id: Optional[str] = None, use_substring_matching: bool = False, + expires_filter: str | None = None, ) -> Dict[str, Union[str, Dict[str, Any], List[Dict[str, Any]]]]: """Build filter conditions for key listing. @@ -5549,6 +5644,8 @@ def _build_key_filter_conditions( where = {"AND": [where, {"access_group_ids": {"hasSome": [access_group_id]}}]} if agent_id and isinstance(agent_id, str): where = {"AND": [where, {"agent_id": agent_id}]} + if expires_filter is not None and expires_filter in VALID_EXPIRES_FILTER_VALUES: + where = {"AND": [where, _build_expires_where_clause(expires_filter, datetime.now(timezone.utc))]} verbose_proxy_logger.debug(f"Filter conditions: {where}") return where @@ -5578,6 +5675,7 @@ async def _list_key_helper( access_group_id: Optional[str] = None, agent_id: Optional[str] = None, use_substring_matching: bool = False, + expires_filter: str | None = None, ) -> KeyListResponseObject: """ Helper function to list keys @@ -5615,6 +5713,7 @@ async def _list_key_helper( access_group_id=access_group_id, agent_id=agent_id, use_substring_matching=use_substring_matching, + expires_filter=expires_filter, ) # Calculate skip for pagination @@ -6218,8 +6317,13 @@ def _validate_key_alias_format(key_alias: Optional[str]) -> None: """ Validate the format of the key_alias. - Gated behind ``litellm.enable_key_alias_format_validation`` (default **False**). - When disabled, no validation is performed so existing workflows are not broken. + A baseline validation always runs, regardless of + ``litellm.enable_key_alias_format_validation``. + + The remaining charset/length rules are gated behind + ``litellm.enable_key_alias_format_validation`` (default **False**). When disabled, + only the baseline validation above is performed, so existing workflows are not + broken. Rules (when enabled): - None is OK (no alias). @@ -6227,10 +6331,20 @@ def _validate_key_alias_format(key_alias: Optional[str]) -> None: - start/end with alphanumeric - only allow a-zA-Z0-9_-/.@ """ - if not litellm.enable_key_alias_format_validation: + if key_alias is None: return - if key_alias is None: + try: + raise_if_unsafe_secret_name(key_alias) + except ValueError: + raise ProxyException( + message="Invalid key_alias", + type=ProxyErrorTypes.bad_request_error, + param="key_alias", + code=400, + ) + + if not litellm.enable_key_alias_format_validation: return if not _KEY_ALIAS_PATTERN.match(key_alias): diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index ab9d04a4eb4..cab2a51a8ca 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -125,13 +125,16 @@ if MCP_AVAILABLE: get_user_env_vars_bulk, get_user_oauth_credential, list_user_oauth_credentials, + mcp_oauth_token_identity, merge_user_env_vars, + purge_user_oauth_credentials_for_server, reject_mcp_server, store_user_credential, store_user_oauth_credential, update_mcp_server, ) from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _raise_if_not_oauth2, authorize_with_server, exchange_token_with_server, get_request_base_url, @@ -148,7 +151,6 @@ if MCP_AVAILABLE: LitellmUserRoles, MakeMCPServersPublicRequest, MCPApprovalStatus, - MCPEnvVarScope, MCPOAuthUserCredentialRequest, MCPOAuthUserCredentialStatus, MCPSubmissionsSummary, @@ -215,6 +217,34 @@ if MCP_AVAILABLE: _base_validate_and_normalize_mcp_server_payload(payload) _validate_mcp_server_name_fields(payload) + def stamp_omitted_oauth2_flow(payload: NewMCPServerRequest) -> None: + """Fallback only: fill in oauth2_flow when an oauth2 create omits it. + + An explicit oauth2_flow from the caller (the dashboard's flow selector, a REST + body, config.yaml) always wins and is never touched. The shape check below runs + solely for oauth2 creates that leave the field unset, so those rows still + persist a flow instead of relying on read-time inference. + + The create payload carries the plaintext credentials, so the M2M-vs-interactive + decision is reliable here in a way it is not at read time (credentials are + encrypted at rest and redacted in responses). The client_credentials shape + mirrors the legacy inference in MCPServerManager._resolve_oauth2_flow; every + other oauth2 configuration is the authorization_code grant, including + delegate_auth_to_upstream, where the client runs that grant upstream. + """ + if payload.auth_type != MCPAuth.oauth2: + return + if payload.oauth2_flow: + return + credentials = payload.credentials or {} + has_m2m_shape = bool( + payload.token_url + and credentials.get("client_id") + and credentials.get("client_secret") + and not payload.authorization_url + ) + payload.oauth2_flow = "client_credentials" if has_m2m_shape else "authorization_code" + _VALID_MCP_REQUIRED_FIELDS: frozenset = frozenset(NewMCPServerRequest.model_fields) def _validate_mcp_required_fields(payload: Any) -> None: @@ -460,18 +490,6 @@ if MCP_AVAILABLE: ) -> List[LiteLLM_MCPServerTable]: return [_redact_mcp_credentials(server) for server in mcp_servers] - def _redact_global_env_var_values(mcp_server: LiteLLM_MCPServerTable) -> None: - """Blank admin-supplied ``scope="global"`` env var secrets in place. - - Global entries hold the admin's plaintext credential (API key, - password, ...) and must never reach non-admin callers. Per-user - entries only carry a placeholder the user fills in themselves, so - their value is left intact. - """ - for env_var in mcp_server.env_vars or []: - if env_var.scope == MCPEnvVarScope.global_: - env_var.value = "" - def _user_is_full_admin(user_api_key_dict: UserAPIKeyAuth) -> bool: """True only for ``PROXY_ADMIN``; ``PROXY_ADMIN_VIEW_ONLY`` returns False. @@ -521,6 +539,10 @@ if MCP_AVAILABLE: sanitized.authorization_url = None sanitized.token_url = None sanitized.registration_url = None + sanitized.token_exchange_endpoint = None + sanitized.audience = None + sanitized.subject_token_type = None + sanitized.token_exchange_profile = None # Drop env vars entirely rather than only blanking global values: the # names alone (DB_PASSWORD, GITHUB_API_KEY, ...) leak what secrets the # admin configured. Non-admins get the per-user vars they must fill in @@ -562,6 +584,10 @@ if MCP_AVAILABLE: sanitized.authorization_url = None sanitized.token_url = None sanitized.registration_url = None + sanitized.token_exchange_endpoint = None + sanitized.audience = None + sanitized.subject_token_type = None + sanitized.token_exchange_profile = None sanitized.health_check_error = None sanitized.last_health_check = None @@ -666,6 +692,7 @@ if MCP_AVAILABLE: allow_all_keys=payload.allow_all_keys, available_on_public_internet=payload.available_on_public_internet, timeout=payload.timeout, + max_concurrent_requests=payload.max_concurrent_requests, ) def get_prisma_client_or_throw(message: str): @@ -1068,6 +1095,7 @@ if MCP_AVAILABLE: prisma_client = get_prisma_client_or_throw("Database not connected. Connect a database to your proxy") validate_and_normalize_mcp_server_payload(payload) + stamp_omitted_oauth2_flow(payload) _validate_mcp_required_fields(payload) payload.approval_status = MCPApprovalStatus.pending_review @@ -1114,9 +1142,9 @@ if MCP_AVAILABLE: prisma_client = get_prisma_client_or_throw("Database not connected. Connect a database to your proxy") submissions = await get_mcp_submissions(prisma_client) + submissions.items = _redact_mcp_credentials_list(submissions.items) if not _user_is_full_admin(user_api_key_dict): - for item in submissions.items: - _redact_global_env_var_values(item) + submissions.items = _sanitize_mcp_server_list_for_non_admin(submissions.items) return submissions @router.put( @@ -1158,6 +1186,7 @@ if MCP_AVAILABLE: server_id, touched_by=user_api_key_dict.user_id or LITELLM_PROXY_ADMIN_NAME, ) + await global_mcp_server_manager.invalidate_byom_submitted_servers_cache(approved.submitted_by) await global_mcp_server_manager.reload_servers_from_database() return _redact_mcp_credentials(approved) @@ -1332,6 +1361,7 @@ if MCP_AVAILABLE: # Validate and normalize payload fields validate_and_normalize_mcp_server_payload(payload) + stamp_omitted_oauth2_flow(payload) # AuthZ - restrict only proxy admins to create mcp servers if LitellmUserRoles.PROXY_ADMIN != user_api_key_dict.user_role: @@ -1423,6 +1453,7 @@ if MCP_AVAILABLE: # Validate and normalize payload fields (alias/server name rules) validate_and_normalize_mcp_server_payload(payload) + stamp_omitted_oauth2_flow(payload) # Restrict to proxy admins similar to the persistent create endpoint if LitellmUserRoles.PROXY_ADMIN != user_api_key_dict.user_role: @@ -1623,6 +1654,7 @@ if MCP_AVAILABLE: scope: Optional[str] = None, ): mcp_server = await _get_cached_temporary_mcp_server_or_404(server_id, user_api_key_dict, request=request) + _raise_if_not_oauth2(mcp_server) # Use the server's stored client_id when the caller doesn't supply one resolved_client_id = mcp_server.client_id or client_id or "" if not resolved_client_id: @@ -1667,6 +1699,7 @@ if MCP_AVAILABLE: scope: Optional[str] = Form(None), ): mcp_server = await _get_cached_temporary_mcp_server_or_404(server_id, user_api_key_dict, request=request) + _raise_if_not_oauth2(mcp_server) resolved_client_id = mcp_server.client_id or client_id or "" if not resolved_client_id: raise HTTPException( @@ -1714,6 +1747,7 @@ if MCP_AVAILABLE: response_types=data.get("response_types", []), token_endpoint_auth_method=data.get("token_endpoint_auth_method", ""), fallback_client_id=server_id, + persist_credentials=_user_is_full_admin(user_api_key_dict), ) @router.delete( @@ -1881,6 +1915,11 @@ if MCP_AVAILABLE: expires_in=payload.expires_in, scopes=payload.scopes, ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( # noqa: PLC0415 + global_mcp_server_manager, + ) + + await global_mcp_server_manager.invalidate_user_oauth_token_cache(user_id, server_id) # Read back the persisted record so the response reflects the stored # expires_at rather than recomputing it here (which could diverge by # milliseconds or if the storage logic ever adds a grace period). @@ -1921,6 +1960,11 @@ if MCP_AVAILABLE: await delete_user_credential(prisma_client, user_id, server_id) except RecordNotFoundError: pass # Already gone — treat as a successful delete + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( # noqa: PLC0415 + global_mcp_server_manager, + ) + + await global_mcp_server_manager.invalidate_user_oauth_token_cache(user_id, server_id) return MCPOAuthUserCredentialStatus( server_id=server_id, has_credential=False, @@ -2276,6 +2320,41 @@ if MCP_AVAILABLE: }, ) + # Snapshot the pre-update identity so we can detect a mint-relevant change below. The read is + # advisory (it only feeds the stale-token purge decision), so a failure skips the purge with a + # warning instead of failing the edit, whose primary job is the update itself. + try: + old_server_record = await get_mcp_server(prisma_client, payload.server_id) + old_server_record_read_failed = False + except Exception as exc: # noqa: BLE001 - advisory read; invalidation is best-effort end-to-end + verbose_logger.warning( + "MCP server %s: could not snapshot the pre-update record; skipping the stale-token check: %s", + payload.server_id, + exc, + ) + old_server_record = None + old_server_record_read_failed = True + + if ( + payload.dcr_bridge + and payload.auth_type is None + and (old_server_record is not None or old_server_record_read_failed) + ): + stored_auth_type = old_server_record.auth_type if old_server_record else None + stored_auth_type_name = getattr(stored_auth_type, "value", stored_auth_type) + if stored_auth_type not in (MCPAuth.true_passthrough, MCPAuth.oauth_delegate): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail={ + "error": ( + "dcr_bridge is only supported for auth_type true_passthrough or " + f"oauth_delegate (stored auth_type: {stored_auth_type_name!r}). Include " + "the server's auth_type in the update payload or configure one of the " + "client-forwarded token modes first." + ) + }, + ) + # try to update the mcp server mcp_server_record_updated = await update_mcp_server( prisma_client, @@ -2294,6 +2373,30 @@ if MCP_AVAILABLE: # Ensure registry is up to date by reloading from database await global_mcp_server_manager.reload_servers_from_database() + # If a field that determines which upstream OAuth token gets minted changed (url/audience, OAuth + # mode/grant, authorization-server endpoints, or the OAuth client + scopes), every stored per-user + # token was minted for the old configuration and is stale. Purge them (DB + cache) so the next + # tool call re-authorizes instead of forwarding a token for a resource/AS/client that no longer + # matches. Best-effort: a purge failure must not fail the update, whose primary job already + # succeeded. + if old_server_record is not None and mcp_oauth_token_identity(old_server_record) != mcp_oauth_token_identity( + mcp_server_record_updated + ): + try: + purged = await purge_user_oauth_credentials_for_server(prisma_client, payload.server_id) + if purged: + verbose_logger.info( + "MCP server %s: purged %d stale per-user OAuth token(s) after a mint-relevant config change", + payload.server_id, + purged, + ) + except Exception as exc: # noqa: BLE001 - purge is best-effort; the server update already succeeded + verbose_logger.warning( + "MCP server %s: failed to purge stale per-user OAuth tokens after config change: %s", + payload.server_id, + exc, + ) + # TODO: Enterprise: Finish audit log trail if litellm.store_audit_logs: pass diff --git a/litellm/proxy/management_endpoints/model_management_endpoints.py b/litellm/proxy/management_endpoints/model_management_endpoints.py index afe71084a45..d458d0f7c4a 100644 --- a/litellm/proxy/management_endpoints/model_management_endpoints.py +++ b/litellm/proxy/management_endpoints/model_management_endpoints.py @@ -1040,6 +1040,22 @@ class ModelManagementAuthChecks: return True +def _deployment_name_and_model(deployment: Optional[Union[Deployment, Dict[str, object]]]) -> Tuple[Optional[str], str]: + """Return (model_name, litellm_params.model) for a deployment. + + delete_deployment is annotated to return a Deployment but hands back the raw + model_list dict at runtime, so both shapes are handled; the model defaults to "". + """ + if deployment is None: + return None, "" + if isinstance(deployment, dict): + name = deployment.get("model_name") + params = deployment.get("litellm_params") + model = params.get("model") if isinstance(params, dict) else None + return (name if isinstance(name, str) else None), (model if isinstance(model, str) else "") + return deployment.model_name, str(getattr(deployment.litellm_params, "model", "") or "") + + #### [BETA] - This is a beta endpoint, format might change based on user feedback. - https://github.com/BerriAI/litellm/issues/964 @router.post( "/model/delete", @@ -1111,7 +1127,19 @@ async def delete_model( ## DELETE FROM ROUTER ## if llm_router is not None: - llm_router.delete_deployment(id=model_info.id) + deleted_deployment = llm_router.delete_deployment(id=model_info.id) + # delete_deployment only drops the deployment from model_list; the auto/ + # complexity router registries are keyed by model_name and would otherwise + # retain a stale (now unbacked) entry, so evict it here too. Guard on the + # auto_router/ prefix (as clear_cache does): a regular DB model that merely + # shares a model_name with a config-defined router must not evict that router, + # since add_deployment never restores config-defined routers. + deleted_name, deleted_model = _deployment_name_and_model(deleted_deployment) + if deleted_name is not None and deleted_model.startswith("auto_router/"): + llm_router.auto_routers.pop(deleted_name, None) + llm_router.complexity_routers.pop(deleted_name, None) + llm_router.adaptive_routers.pop(deleted_name, None) + llm_router.quality_routers.pop(deleted_name, None) # Runs after the row delete so the sibling check sees post-delete state. if model_params.model_info.team_id is not None: @@ -1715,8 +1743,27 @@ async def clear_cache(): for model_id in db_model_ids: llm_router.delete_deployment(id=model_id) - # Clear auto routers - llm_router.auto_routers.clear() + # Clear only DB-backed auto-router-family entries, keyed by model_name, so the + # reload below rebuilds them fresh. A blanket .clear() would also drop config-defined + # routers, which are never re-added below (add_deployment only reloads DB models), + # leaving them permanently unroutable until a full proxy restart for every tenant. + # Restrict to deployments whose model is actually an auto_router/* so a config + # router that merely shares a model_name with a regular DB model isn't evicted. The + # auto_router/ prefix also covers quality_router/ and adaptive_router/, so pop the + # name from every router registry (no-op where absent); missing quality/adaptive + # entries would otherwise make init raise "already exists" on reload and abort it. + db_router_names = { + model.get("model_name") + for model in current_models + if model.get("model_name") is not None + and model.get("model_info", {}).get("db_model", False) + and str(model.get("litellm_params", {}).get("model", "")).startswith("auto_router/") + } + for model_name in db_router_names: + llm_router.auto_routers.pop(model_name, None) + llm_router.complexity_routers.pop(model_name, None) + llm_router.adaptive_routers.pop(model_name, None) + llm_router.quality_routers.pop(model_name, None) # Reload only DB models await proxy_config.add_deployment(prisma_client=prisma_client, proxy_logging_obj=proxy_logging_obj) diff --git a/litellm/proxy/management_endpoints/team_endpoints.py b/litellm/proxy/management_endpoints/team_endpoints.py index 0ea2b9e05f9..f468f5ec30b 100644 --- a/litellm/proxy/management_endpoints/team_endpoints.py +++ b/litellm/proxy/management_endpoints/team_endpoints.py @@ -14,7 +14,7 @@ import json import math import traceback from datetime import datetime, timezone -from typing import Any, Dict, List, Optional, Tuple, Union, cast +from typing import Annotated, Any, Dict, List, Optional, Tuple, Union, cast import fastapi from fastapi import APIRouter, Depends, Header, HTTPException, Request, status @@ -26,6 +26,7 @@ from litellm._uuid import uuid from litellm.integrations.prometheus import PrometheusLogger from litellm.litellm_core_utils.safe_json_dumps import safe_dumps from litellm.proxy._types import ( + UI_TEAM_ID, BlockTeamRequest, CommonProxyErrors, DeleteTeamRequest, @@ -75,6 +76,7 @@ from litellm.proxy.auth.auth_checks import ( ) from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_utils.callback_utils import encrypt_callback_vars +from litellm.proxy.common_utils.json_merge_patch import apply_json_merge_patch from litellm.proxy.management_endpoints.common_utils import ( _check_passthrough_routes_caller_permission, _is_user_org_admin_for_team, @@ -93,6 +95,7 @@ from litellm.proxy.management_endpoints.tag_management_endpoints import ( ) from litellm.proxy.management_helpers.object_permission_utils import ( _set_object_permission, + enforce_all_proxy_mcp_servers_grant_is_admin_only, handle_update_object_permission_common, ) from litellm.proxy.management_helpers.team_member_permission_checks import ( @@ -1045,6 +1048,13 @@ async def new_team( if data.team_id is None: data.team_id = str(uuid.uuid4()) else: + if data.team_id == UI_TEAM_ID: + raise HTTPException( + status_code=400, + detail={ + "error": f"team_id '{UI_TEAM_ID}' is reserved for LiteLLM UI dashboard sessions and cannot be used for a real team. Please use a different team id." + }, + ) # Check if team_id exists already _existing_team_id = await prisma_client.get_data( team_id=data.team_id, table_name="team", query_type="find_unique" @@ -1144,6 +1154,12 @@ async def new_team( data_json = data.json() ## Handle Object Permission - MCP, Vector Stores etc. + await enforce_all_proxy_mcp_servers_grant_is_admin_only( + requested_mcp_servers=(data.object_permission.mcp_servers if data.object_permission is not None else None), + existing_object_permission_id=None, + is_proxy_admin=user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN, + prisma_client=prisma_client, + ) data_json = await _set_object_permission( data_json=data_json, prisma_client=prisma_client, @@ -1846,6 +1862,12 @@ async def update_team( # Check object permission if data.object_permission is not None: + await enforce_all_proxy_mcp_servers_grant_is_admin_only( + requested_mcp_servers=data.object_permission.mcp_servers, + existing_object_permission_id=existing_team_row.object_permission_id, + is_proxy_admin=user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN, + prisma_client=prisma_client, + ) updated_kv = await handle_update_object_permission( data_json=updated_kv, existing_team_row=existing_team_row, @@ -1915,6 +1937,87 @@ async def update_team( raise handle_exception_on_proxy(e) +@router.patch( + "/team/{team_id}", + tags=["team management"], + dependencies=[Depends(user_api_key_auth)], + response_model=LiteLLM_TeamTable, +) +async def patch_team( + team_id: str, + http_request: Request, + user_api_key_dict: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], + litellm_changed_by: Annotated[ + Optional[str], + Header( + description="The litellm-changed-by header enables tracking of actions performed by authorized users on behalf of other users, providing an audit trail for accountability", + ), + ] = None, +): + """ + Partially update a team using RFC 7386 JSON Merge Patch semantics. + + `team_id` is taken from the path. `metadata` is merged with the team's stored + metadata rather than replacing it: an omitted key is preserved, `key: null` + deletes it, and any other value overwrites (recursing into nested objects). + Every other field behaves exactly like `POST /team/update` (omitted preserves, + a value overwrites). Returns the full updated team. + + ``` + curl --location --request PATCH 'http://0.0.0.0:4000/team/8d916b1c-510d-4894-a334-1c16a93344f5' \ + --header 'Authorization: Bearer sk-1234' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "metadata": {"cost_center": "1234", "deprecated_key": null} + }' + ``` + """ + from litellm.proxy.proxy_server import prisma_client + + try: + if prisma_client is None: + raise HTTPException( + status_code=500, + detail={"error": CommonProxyErrors.db_not_connected_error.value}, + ) + + try: + body = await http_request.json() + except (json.JSONDecodeError, ValueError): + raise HTTPException(status_code=400, detail={"error": "Request body must be a JSON object"}) + if not isinstance(body, dict): + raise HTTPException(status_code=400, detail={"error": "Request body must be a JSON object"}) + + body_team_id = body.pop("team_id", None) + if body_team_id is not None and body_team_id != team_id: + raise HTTPException( + status_code=400, + detail={"error": f"team_id in body ({body_team_id}) does not match team_id in path ({team_id})"}, + ) + + if "metadata" in body: + existing_team_row = await TeamRepository(prisma_client).table.find_unique(where={"team_id": team_id}) + if existing_team_row is None: + raise HTTPException( + status_code=404, + detail={"error": f"Team not found, passed team_id={team_id}"}, + ) + existing_metadata = existing_team_row.metadata if isinstance(existing_team_row.metadata, dict) else {} + body["metadata"] = apply_json_merge_patch(existing_metadata, body["metadata"]) + + update_request = UpdateTeamRequest(team_id=team_id, **body) + + result = await update_team( + data=update_request, + http_request=http_request, + user_api_key_dict=user_api_key_dict, + litellm_changed_by=litellm_changed_by, + ) + return result["data"] + except Exception as e: # noqa: BLE001 # normalize every failure to the proxy exception contract + raise handle_exception_on_proxy(e) + + def _set_budget_reset_at(data: UpdateTeamRequest, updated_kv: dict) -> None: """Set budget_reset_at in updated_kv if budget_duration is provided.""" if data.budget_duration is not None: diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index 89c1a925eeb..065464aa565 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -113,7 +113,7 @@ from litellm.proxy.utils import ( from litellm.repositories.table_repositories import SSOConfigRepository from litellm.repositories.team_repository import TeamRepository from litellm.repositories.user_repository import UserRepository -from litellm.secret_managers.main import get_secret_bool, str_to_bool +from litellm.secret_managers.main import get_secret_bool, get_secret_str, str_to_bool from litellm.types.proxy.management_endpoints.ui_sso import * # noqa: F403 from litellm.types.proxy.management_endpoints.ui_sso import ( DefaultTeamSSOParams, @@ -1746,6 +1746,18 @@ async def auth_callback(request: Request, state: Optional[str] = None): """Verify login""" verbose_proxy_logger.info(f"Starting SSO callback with state: {state}") + oauth_error = request.query_params.get("error") + if oauth_error: + oauth_error_description = request.query_params.get("error_description") + verbose_proxy_logger.warning( + f"SSO callback received OAuth error: {oauth_error}, description: {oauth_error_description}" + ) + raise HTTPException( + status_code=401, + detail=f"OAuth error: {oauth_error}" + + (f", error_description: {oauth_error_description}" if oauth_error_description else ""), + ) + # Check if this is a CLI login (state starts with our CLI prefix) from litellm.constants import LITELLM_CLI_SESSION_TOKEN_PREFIX from litellm.proxy._types import LiteLLM_JWTAuth @@ -2134,12 +2146,16 @@ async def cli_poll_key( models=session_data.get("models", []), ) - user_db_obj = await get_user_object( - user_id=user_id, - prisma_client=prisma_client, - user_api_key_cache=user_api_key_cache, - user_id_upsert=False, - ) + try: + user_db_obj = await get_user_object( + user_id=user_id, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + user_id_upsert=False, + ) + except ValueError as e: + verbose_proxy_logger.debug(f"CLI poll: user lookup failed, proceeding without user budget: {e}") + user_db_obj = None user_budget = user_db_obj.max_budget if user_db_obj is not None else None team_budget: Optional[float] = None @@ -3733,8 +3749,7 @@ class MicrosoftSSOHandler: Handles Microsoft SSO callback response and returns a CustomOpenID object """ - graph_api_base_url = "https://graph.microsoft.com/v1.0" - graph_api_user_groups_endpoint = f"{graph_api_base_url}/me/memberOf" + DEFAULT_GRAPH_API_BASE_URL = "https://graph.microsoft.com/v1.0" """ Constants @@ -3744,6 +3759,19 @@ class MicrosoftSSOHandler: # used for debugging to show the user groups litellm found from Graph API GRAPH_API_RESPONSE_KEY = "graph_api_user_groups" + @staticmethod + def get_graph_api_base_url() -> str: + """ + Returns the Microsoft Graph API base URL, configurable via the + `MICROSOFT_GRAPH_ENDPOINT` env var so non-default clouds such as Azure + Government (GCC High) can point at `https://graph.microsoft.us/v1.0` + """ + return get_secret_str("MICROSOFT_GRAPH_ENDPOINT") or MicrosoftSSOHandler.DEFAULT_GRAPH_API_BASE_URL + + @staticmethod + def get_graph_api_user_groups_endpoint() -> str: + return f"{MicrosoftSSOHandler.get_graph_api_base_url()}/me/memberOf" + @staticmethod async def get_microsoft_callback_response( request: Request, @@ -3920,7 +3948,7 @@ class MicrosoftSSOHandler: # Fetch user membership from Microsoft Graph API all_group_ids = [] - next_link: Optional[str] = MicrosoftSSOHandler.graph_api_user_groups_endpoint + next_link: Optional[str] = MicrosoftSSOHandler.get_graph_api_user_groups_endpoint() auth_headers = {"Authorization": f"Bearer {access_token}"} page_count = 0 @@ -4003,7 +4031,7 @@ class MicrosoftSSOHandler: Users use Enterprise Applications to manage Groups and Users on Microsoft Entra ID """ - base_url = "https://graph.microsoft.com/v1.0" + base_url = MicrosoftSSOHandler.get_graph_api_base_url() # Endpoint to get app role assignments for the given service principal endpoint = f"/servicePrincipals/{service_principal_id}/appRoleAssignedTo" url = base_url + endpoint diff --git a/litellm/proxy/management_helpers/object_permission_utils.py b/litellm/proxy/management_helpers/object_permission_utils.py index fe96d9c260a..6bbd41b93ed 100644 --- a/litellm/proxy/management_helpers/object_permission_utils.py +++ b/litellm/proxy/management_helpers/object_permission_utils.py @@ -11,7 +11,7 @@ from fastapi import HTTPException, status from litellm._logging import verbose_proxy_logger from litellm._uuid import uuid from litellm.litellm_core_utils.safe_json_dumps import safe_dumps -from litellm.proxy._types import ObjectPermissionDict, SpecialMCPServerNames +from litellm.proxy._types import ObjectPermissionDict, SpecialMCPServerName, SpecialMCPServerNames from litellm.proxy.utils import PrismaClient from litellm.repositories.object_permission_repository import ObjectPermissionRepository from litellm.repositories.table_repositories import MCPServerRepository @@ -334,6 +334,8 @@ async def _resolve_team_allowed_mcp_servers( ) direct_servers: List[str] = team_object_permission.mcp_servers or [] + if SpecialMCPServerName.all_proxy_servers.value in direct_servers: + return _get_all_mcp_server_ids() access_group_servers: List[str] = await MCPRequestHandler._get_mcp_servers_from_access_groups( team_object_permission.mcp_access_groups or [] ) @@ -359,6 +361,62 @@ def _get_allow_all_keys_server_ids() -> Set[str]: return set(global_mcp_server_manager.get_allow_all_keys_server_ids()) +def _get_all_mcp_server_ids() -> set[str]: + """Return every MCP server id registered on the proxy (config + DB union).""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + return set(global_mcp_server_manager.get_registry().keys()) + + +async def _existing_object_permission_mcp_servers( + object_permission_id: Optional[str], + prisma_client: Optional[PrismaClient], +) -> list[str]: + if not object_permission_id or prisma_client is None: + return [] + existing = await ObjectPermissionRepository(prisma_client).table.find_unique( + where={"object_permission_id": object_permission_id}, + ) + if existing is None: + return [] + return existing.mcp_servers or [] + + +async def enforce_all_proxy_mcp_servers_grant_is_admin_only( + requested_mcp_servers: Optional[list[str]], + existing_object_permission_id: Optional[str], + is_proxy_admin: bool, + prisma_client: Optional[PrismaClient], +) -> None: + """ + Only a proxy admin may newly grant the all-proxy MCP sentinel. + + Scoping a team to every MCP server on the proxy is a proxy-wide authorization + decision, so a caller who is not a proxy admin (e.g. a team admin managing their + own team) cannot add ``all-proxy-mcpservers``. A sentinel a proxy admin already + granted is left untouched, so unrelated edits to such a team still succeed. + + Raises HTTPException(403) when a non-admin tries to add the sentinel. + """ + sentinel = SpecialMCPServerName.all_proxy_servers.value + if is_proxy_admin or sentinel not in (requested_mcp_servers or []): + return + existing_mcp_servers = await _existing_object_permission_mcp_servers( + object_permission_id=existing_object_permission_id, + prisma_client=prisma_client, + ) + if sentinel in existing_mcp_servers: + return + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail={ + "error": "Only a proxy admin can grant a team access to all proxy MCP servers ('all-proxy-mcpservers')." + }, + ) + + async def _get_team_allowed_mcp_servers( team_obj: Optional["LiteLLM_TeamTableCachedObj"], prisma_client: Optional[PrismaClient] = None, diff --git a/litellm/proxy/openai_files_endpoints/files_endpoints.py b/litellm/proxy/openai_files_endpoints/files_endpoints.py index 34ca43c603f..0c9aa667751 100644 --- a/litellm/proxy/openai_files_endpoints/files_endpoints.py +++ b/litellm/proxy/openai_files_endpoints/files_endpoints.py @@ -771,7 +771,7 @@ async def get_file_content( version=version, ) - if should_route: + if should_route and credentials is not None: # Use model-based routing with credentials from config prepare_data_with_credentials( data=data, @@ -1129,7 +1129,7 @@ async def delete_file( check_file_id_encoding=True, ) - if should_route: + if should_route and credentials is not None: # Use model-based routing with credentials from config prepare_data_with_credentials( data=data, @@ -1308,7 +1308,7 @@ async def list_files( check_file_id_encoding=False, ) - if should_route: + if should_route and credentials is not None: # Use model-based routing with credentials from config data.update(credentials) # type: ignore response = await litellm.afile_list( diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index 391540f385e..2aff663038b 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -7,7 +7,7 @@ import traceback from base64 import b64encode from datetime import datetime from itertools import groupby -from typing import Any, Dict, List, Mapping, Optional, Tuple, Union, cast +from typing import Any, AsyncGenerator, Dict, List, Mapping, Optional, Tuple, Union, cast from urllib.parse import urlencode, urlparse import httpx @@ -389,18 +389,24 @@ class HttpPassThroughEndpointHelpers(BasePassthroughUtils): forward_multipart: bool = False, ) -> httpx.Response: """ - Handle non-streaming HTTP requests + Handle non-SSE HTTP requests - Handles special cases when GET requests, multipart/form-data requests, and generic httpx requests + Handles special cases when GET requests, multipart/form-data requests, and generic httpx requests. + + GET and generic requests are sent with httpx stream semantics so the caller can + decide from the response headers whether to buffer the body (JSON, inspected for + logging/guardrails) or relay it to the client without materializing it in memory + (LIT-4009: large batch results files must not be buffered in proxy RSS). """ if request.method == "GET": - response = await async_client.request( - method=request.method, - url=url, + get_request = async_client.build_request( + request.method, + url, headers=headers, params=requested_query_params, ) - elif HttpPassThroughEndpointHelpers.is_multipart(request) is True and forward_multipart: + return await async_client.send(get_request, stream=True) + if HttpPassThroughEndpointHelpers.is_multipart(request) is True and forward_multipart: # Forward multipart via make_multipart_http_request even when _parsed_body is # non-empty (pass_through_request always injects litellm_logging_obj, etc.). # forward_multipart is False when custom_body was supplied (JSON body despite @@ -412,16 +418,14 @@ class HttpPassThroughEndpointHelpers(BasePassthroughUtils): headers=headers, requested_query_params=requested_query_params, ) - else: - # Generic httpx method - response = await async_client.request( - method=request.method, - url=url, - headers=headers, - params=requested_query_params, - json=_parsed_body, - ) - return response + generic_request = async_client.build_request( + request.method, + url, + headers=headers, + params=requested_query_params, + json=_parsed_body, + ) + return await async_client.send(generic_request, stream=True) @staticmethod def is_multipart(request: Request) -> bool: @@ -676,6 +680,75 @@ def _carry_guardrail_logging_info(request_data: dict, guardrail_data: Optional[d metadata.setdefault("standard_logging_guardrail_information", list(entries)) +def _build_passthrough_failure_request_payload( + parsed_body: Optional[dict], + kwargs: Optional[dict], + logging_obj: Optional[LiteLLMLoggingObj], + custom_llm_provider: Optional[str], +) -> dict: + """Build the ``request_data`` dict passed to ``post_call_failure_hook``. + + Shared by the outer exception handler (LiteLLM-internal failures) and + upstream HTTP error logging, so both failure paths report the same shape + of request data (model, custom_llm_provider, litellm_logging_obj, ...). + """ + request_payload: dict = dict(parsed_body or {}) + if kwargs: + request_payload.update(kwargs) + if logging_obj is not None: + request_payload["litellm_logging_obj"] = logging_obj + if "model" not in request_payload and parsed_body and isinstance(parsed_body, dict): + request_payload["model"] = parsed_body.get("model", "") + if "custom_llm_provider" not in request_payload and custom_llm_provider: + request_payload["custom_llm_provider"] = custom_llm_provider + return request_payload + + +async def _log_passthrough_upstream_failure( + response: httpx.Response, + user_api_key_dict: UserAPIKeyAuth, + request_payload: dict, +) -> None: + """Fire LiteLLM-side failure hooks (spend tracking, alerting callbacks) for + an upstream 4xx/5xx passthrough response. + + Passthrough must return the upstream status/body/headers to the client + unchanged, so this never raises or transforms the response - it only + mirrors the monitoring side effect that ``post_call_failure_hook`` would + have received had the error originated inside LiteLLM. + """ + if response.status_code < 400: + return + from litellm.proxy.proxy_server import proxy_logging_obj + + try: + response.raise_for_status() + except httpx.HTTPStatusError: + # Reported as an HTTPException, not the raw httpx error: ProxyLogging's + # alerting path only excludes HTTPException/ProxyException from its + # "High" severity llm_exceptions alert, treating everything else as an + # operational LLM-API failure. An upstream 4xx/5xx returned unchanged + # to the client is a user-facing error like any other, not something + # ops needs paged for, so it must be excluded the same way auth and + # rate-limit errors already are. + synthetic_exception = HTTPException( + status_code=response.status_code, + detail=f"Upstream passthrough request failed with status {response.status_code}", + ) + try: + await proxy_logging_obj.post_call_failure_hook( + user_api_key_dict=user_api_key_dict, + original_exception=synthetic_exception, + request_data=request_payload, + traceback_str=traceback.format_exc(limit=MAXIMUM_TRACEBACK_LINES_TO_LOG), + ) + except Exception: # noqa: BLE001 - a failing logging callback must never break the passthrough response + verbose_proxy_logger.warning( + "pass_through_endpoint: post_call_failure_hook raised for upstream error", + exc_info=True, + ) + + from litellm.passthrough.timeout_utils import ( DEFAULT_PASS_THROUGH_REQUEST_TIMEOUT_SECONDS, # noqa: F401 - re-exported for backward compat resolve_llm_passthrough_timeout, # noqa: F401 - re-exported for backward compat @@ -751,21 +824,7 @@ async def pass_through_request( forward_headers=forward_headers, ) - # Apply default query parameters if provided, regardless of merge_query_params setting - if default_query_params or merge_query_params: - # Determine what to merge based on settings - request_params = dict(request.query_params) if merge_query_params else {} - - # Create a new URL with the merged query params - url = url.copy_with( - query=urlencode( - HttpPassThroughEndpointHelpers.get_merged_query_parameters( - existing_url=url, - request_query_params=request_params, - default_query_params=default_query_params, - ) - ).encode("ascii") - ) + requested_query_params: Optional[dict] = query_params or dict(request.query_params) endpoint_type: EndpointType = HttpPassThroughEndpointHelpers.get_endpoint_type(str(url)) @@ -883,9 +942,6 @@ async def pass_through_request( ) logging_obj.model_call_details["litellm_call_id"] = litellm_call_id - # combine url with query params for logging - requested_query_params: Optional[dict] = query_params or dict(request.query_params) - ## PASSTHROUGH MANAGED ID RESOLUTION (INPUT) ## # Resolve managed IDs in path, query params, and body back to raw # provider IDs before forwarding upstream. Gated by feature flag and @@ -955,6 +1011,20 @@ async def pass_through_request( request.method, ) + # Apply default query parameters if provided, regardless of merge_query_params setting + if default_query_params or merge_query_params: + # Create a new URL with the merged query params + url = url.copy_with( + query=urlencode( + HttpPassThroughEndpointHelpers.get_merged_query_parameters( + existing_url=url, + request_query_params=requested_query_params, + default_query_params=default_query_params, + ) + ).encode("ascii") + ) + requested_query_params = None + ## PASSTHROUGH MANAGED LIST (DB-only response) ## # For GET /v1/files and GET /v1/batches passthrough routes, serve the # listing entirely from our DB so each caller only sees their own IDs. @@ -1053,10 +1123,16 @@ async def pass_through_request( response = await async_client.send(req, stream=stream) - try: - response.raise_for_status() - except httpx.HTTPStatusError as e: - raise HTTPException(status_code=e.response.status_code, detail=await e.response.aread()) + await _log_passthrough_upstream_failure( + response=response, + user_api_key_dict=user_api_key_dict, + request_payload=_build_passthrough_failure_request_payload( + parsed_body=_parsed_body, + kwargs=kwargs, + logging_obj=logging_obj, + custom_llm_provider=custom_llm_provider, + ), + ) # Call response headers hook for streaming pass-through _response_headers = HttpPassThroughEndpointHelpers.get_response_headers( @@ -1089,13 +1165,14 @@ async def pass_through_request( if state_raw_body is not None: # SigV4-signed callers (Bedrock) require the exact pre-signed bytes # to be forwarded so the signature/Content-Length stay valid. - response = await async_client.request( - method=request.method, - url=url, + raw_body_request = async_client.build_request( + request.method, + url, headers=headers, params=requested_query_params, content=state_raw_body, ) + response = await async_client.send(raw_body_request, stream=True) else: response = await HttpPassThroughEndpointHelpers.non_streaming_http_request_handler( request=request, @@ -1112,10 +1189,16 @@ async def pass_through_request( logging_obj.stream = True logging_obj.model_call_details["stream"] = True - try: - response.raise_for_status() - except httpx.HTTPStatusError as e: - raise HTTPException(status_code=e.response.status_code, detail=await e.response.aread()) + await _log_passthrough_upstream_failure( + response=response, + user_api_key_dict=user_api_key_dict, + request_payload=_build_passthrough_failure_request_payload( + parsed_body=_parsed_body, + kwargs=kwargs, + logging_obj=logging_obj, + custom_llm_provider=custom_llm_provider, + ), + ) # Call response headers hook for detected streaming pass-through _response_headers = HttpPassThroughEndpointHelpers.get_response_headers( @@ -1145,20 +1228,63 @@ async def pass_through_request( status_code=response.status_code, ) - try: - response.raise_for_status() - except httpx.HTTPStatusError as e: - raise HTTPException(status_code=e.response.status_code, detail=e.response.text) + if not _should_buffer_passthrough_response(response): + relay_custom_headers = ProxyBaseLLMRequestProcessing.get_custom_headers( + user_api_key_dict=user_api_key_dict, + call_id=litellm_call_id, + model_id=None, + cache_key=None, + api_base=str(url._uri_reference), + ) + relay_callback_headers = await proxy_logging_obj.post_call_response_headers_hook( + data=_parsed_body or {}, + user_api_key_dict=user_api_key_dict, + response=response, + request_headers=dict(request.headers), + ) + if relay_callback_headers: + relay_custom_headers.update(relay_callback_headers) - if response.status_code >= 300: - raise HTTPException(status_code=response.status_code, detail=response.text) + return StreamingResponse( + _relay_passthrough_response_bytes( + response=response, + request_body=_parsed_body or {}, + url_route=str(url), + start_time=start_time, + logging_obj=logging_obj, + custom_llm_provider=custom_llm_provider, + success_handler_kwargs=kwargs, + ), + status_code=response.status_code, + headers=HttpPassThroughEndpointHelpers.get_response_headers( + headers=response.headers, + custom_headers=relay_custom_headers, + ), + ) content = await response.aread() ## POST-CALL GUARDRAILS ## + # Guardrails and managed-id rewriting only apply to successful upstream + # responses; response_body itself is parsed unconditionally so the + # failure-hook log payload below still reflects upstream error bodies. _content_modified = False response_body: Optional[dict] = get_response_body(response) - if response_body is not None and guardrails_to_run: + + failure_request_payload = _build_passthrough_failure_request_payload( + parsed_body=_parsed_body, + kwargs=kwargs, + logging_obj=logging_obj, + custom_llm_provider=custom_llm_provider, + ) + failure_request_payload["response_body"] = response_body + await _log_passthrough_upstream_failure( + response=response, + user_api_key_dict=user_api_key_dict, + request_payload=failure_request_payload, + ) + + if response.status_code < 400 and response_body is not None and guardrails_to_run: # Build an enriched data dict: _parsed_body has been stripped of # `metadata` by both pre_call_hook and _init_kwargs_for_pass_through_endpoint, # so we re-attach the configured guardrails here so should_run_guardrail @@ -1249,23 +1375,28 @@ async def pass_through_request( ) ## LOG SUCCESS + # Upstream errors are already logged via _log_passthrough_upstream_failure + # above; the success handler has no status-code awareness of its own; so + # calling it here for a 4xx/5xx would double-log the same request as both + # a failure and a success (corrupting spend tracking). passthrough_logging_payload["response_body"] = response_body end_time = datetime.now() - GLOBAL_LOGGING_WORKER.ensure_initialized_and_enqueue( - async_coroutine=pass_through_endpoint_logging.pass_through_async_success_handler( - httpx_response=response, - response_body=response_body, - url_route=str(url), - result="", - start_time=start_time, - end_time=end_time, - logging_obj=logging_obj, - cache_hit=False, - request_body=_parsed_body or {}, - custom_llm_provider=custom_llm_provider, - **kwargs, + if response.status_code < 400: + GLOBAL_LOGGING_WORKER.ensure_initialized_and_enqueue( + async_coroutine=pass_through_endpoint_logging.pass_through_async_success_handler( + httpx_response=response, + response_body=response_body, + url_route=str(url), + result="", + start_time=start_time, + end_time=end_time, + logging_obj=logging_obj, + cache_hit=False, + request_body=_parsed_body or {}, + custom_llm_provider=custom_llm_provider, + **kwargs, + ) ) - ) ## CUSTOM HEADERS - `x-litellm-*` custom_headers = ProxyBaseLLMRequestProcessing.get_custom_headers( @@ -2119,6 +2250,70 @@ def _is_streaming_response(response: httpx.Response) -> bool: return False +def _should_buffer_passthrough_response(response: httpx.Response) -> bool: + """ + Decide from the response headers whether the body must be read into memory. + + JSON bodies (and upstream errors) stay buffered: spend logging, guardrails and + managed-id rewriting inspect them, and they are small in practice. Everything + else (jsonl batch results, octet-stream files, ...) is relayed to the client + chunk by chunk so a large body is never resident in full (LIT-4009). A missing + content-type is buffered because the body cannot be classified. + """ + if response.status_code >= 400: + return True + media_type = response.headers.get("content-type", "").split(";")[0].strip().lower() + return media_type in ("", "application/json") or media_type.endswith("+json") + + +async def _relay_passthrough_response_bytes( + response: httpx.Response, + request_body: dict, + url_route: str, + start_time: datetime, + logging_obj: LiteLLMLoggingObj, + custom_llm_provider: Optional[str], + success_handler_kwargs: dict, +) -> AsyncGenerator[bytes, None]: + """ + Yield upstream bytes to the client without accumulating them, then fire the + passthrough success handler with response_body=None (uninspected body). The + finally block also runs on client disconnect (GeneratorExit) so partial + downloads still produce a spend-log row, mirroring chunk_processor; a + disconnect additionally logs a warning with the number of bytes relayed so + partial deliveries are distinguishable from complete ones in proxy logs. + """ + bytes_relayed = 0 + upstream_fully_relayed = False + try: + async for chunk in response.aiter_bytes(): + bytes_relayed += len(chunk) + yield chunk + upstream_fully_relayed = True + finally: + if not upstream_fully_relayed: + verbose_proxy_logger.warning( + f"Passthrough stream for {url_route} ended before upstream body was fully relayed; " + f"{bytes_relayed} bytes were sent to the client" + ) + await response.aclose() + GLOBAL_LOGGING_WORKER.ensure_initialized_and_enqueue( + async_coroutine=pass_through_endpoint_logging.pass_through_async_success_handler( + httpx_response=response, + response_body=None, + url_route=url_route, + result="", + start_time=start_time, + end_time=datetime.now(), + logging_obj=logging_obj, + cache_hit=False, + request_body=request_body, + custom_llm_provider=custom_llm_provider, + **success_handler_kwargs, + ) + ) + + def _extract_model_from_vertex_ai_setup(setup_response: dict) -> Optional[str]: """ Extract the model name from Vertex AI Live setup response. diff --git a/litellm/proxy/pass_through_endpoints/streaming_handler.py b/litellm/proxy/pass_through_endpoints/streaming_handler.py index 1bdd57507e3..4dc1e0e70dd 100644 --- a/litellm/proxy/pass_through_endpoints/streaming_handler.py +++ b/litellm/proxy/pass_through_endpoints/streaming_handler.py @@ -25,6 +25,11 @@ from .success_handler import PassThroughEndpointLogging class PassThroughStreamingHandler: + @staticmethod + def _stamp_first_chunk_if_needed(litellm_logging_obj: LiteLLMLoggingObj) -> None: + if litellm_logging_obj.completion_start_time is None: + litellm_logging_obj._update_completion_start_time(completion_start_time=datetime.now()) + @staticmethod async def chunk_processor( response: httpx.Response, @@ -58,6 +63,7 @@ class PassThroughStreamingHandler: # Hot path: just buffer for end-of-stream logging and forward. async for chunk in response.aiter_bytes(): raw_bytes.append(chunk) + PassThroughStreamingHandler._stamp_first_chunk_if_needed(litellm_logging_obj) yield chunk else: # ``cost_injection_active`` already requires ``model_name`` to @@ -67,6 +73,7 @@ class PassThroughStreamingHandler: resolved_model_name: str = model_name async for chunk in response.aiter_bytes(): raw_bytes.append(chunk) + PassThroughStreamingHandler._stamp_first_chunk_if_needed(litellm_logging_obj) if endpoint_type == EndpointType.VERTEX_AI: if "streamRawPredict" in url_route or "rawPredict" in url_route: modified_chunk = ProxyBaseLLMRequestProcessing._process_chunk_with_cost_injection( @@ -89,7 +96,11 @@ class PassThroughStreamingHandler: # GeneratorExit (raised on client disconnect) is not caught by # `except Exception`; the finally block ensures partial usage # still gets logged for spend tracking. See LIT-2642. - if not logging_scheduled and raw_bytes: + # Upstream 4xx/5xx responses are already logged as a failure by + # the caller before this generator starts (see + # _log_passthrough_upstream_failure); logging them again here as + # a success would double-log the same request. + if not logging_scheduled and raw_bytes and response.status_code < 400: logging_scheduled = True try: GLOBAL_LOGGING_WORKER.ensure_initialized_and_enqueue( diff --git a/litellm/proxy/pass_through_endpoints/success_handler.py b/litellm/proxy/pass_through_endpoints/success_handler.py index ee651a15afe..6a673f6bebb 100644 --- a/litellm/proxy/pass_through_endpoints/success_handler.py +++ b/litellm/proxy/pass_through_endpoints/success_handler.py @@ -34,6 +34,18 @@ from .llm_provider_handlers.vertex_passthrough_logging_handler import ( cohere_passthrough_logging_handler = CoherePassthroughLoggingHandler() +def _safe_response_text(httpx_response: httpx.Response) -> str: + """ + Streamed passthrough responses are relayed to the client without being read + into memory, so accessing .text on them raises ResponseNotRead. Their body is + intentionally uninspected; log an empty string instead of failing the row. + """ + try: + return httpx_response.text + except httpx.ResponseNotRead: + return "" + + class PassThroughEndpointLogging: def __init__(self): self.TRACKED_VERTEX_ROUTES = [ @@ -306,7 +318,9 @@ class PassThroughEndpointLogging: ] kwargs = normalized_llm_passthrough_logging_payload["kwargs"] if standard_logging_response_object is None: - standard_logging_response_object = StandardPassThroughResponseObject(response=httpx_response.text) + standard_logging_response_object = StandardPassThroughResponseObject( + response=_safe_response_text(httpx_response) + ) kwargs = self._set_cost_per_request( logging_obj=logging_obj, diff --git a/litellm/proxy/policy_engine/policy_endpoints.py b/litellm/proxy/policy_engine/policy_endpoints.py index 4a94fc3d43c..a879f6b6f7e 100644 --- a/litellm/proxy/policy_engine/policy_endpoints.py +++ b/litellm/proxy/policy_engine/policy_endpoints.py @@ -695,7 +695,8 @@ async def create_policy_attachment( } ``` """ - from litellm.proxy.proxy_server import prisma_client + from litellm.proxy.policy_engine.policy_validator import PolicyValidator + from litellm.proxy.proxy_server import llm_router, prisma_client if prisma_client is None: raise HTTPException(status_code=500, detail="Database not connected") @@ -710,6 +711,19 @@ async def create_policy_attachment( detail=f"Policy '{request.policy_name}' not found. Create the policy first.", ) + # Reject concrete team/key/model scope entries that don't resolve to a real + # entity. Wildcard patterns are allowed through (they may match zero today). + scope_errors = await PolicyValidator( + prisma_client=prisma_client, llm_router=llm_router + ).find_invalid_scope_entries( + policy_name=request.policy_name, + teams=request.teams, + keys=request.keys, + models=request.models, + ) + if scope_errors: + raise HTTPException(status_code=400, detail=" | ".join(e.message for e in scope_errors)) + created_by = user_api_key_dict.user_id result = await get_attachment_registry().add_attachment_to_db( attachment_request=request, diff --git a/litellm/proxy/policy_engine/policy_validator.py b/litellm/proxy/policy_engine/policy_validator.py index 626bbbc1ce5..4db5bc0435e 100644 --- a/litellm/proxy/policy_engine/policy_validator.py +++ b/litellm/proxy/policy_engine/policy_validator.py @@ -9,9 +9,11 @@ Validates: - Inheritance chains are valid (no cycles, parents exist) """ +import asyncio from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set from litellm._logging import verbose_proxy_logger +from litellm.proxy.auth.route_checks import RouteChecks from litellm.repositories.team_repository import TeamRepository from litellm.repositories.verification_token_repository import ( VerificationTokenRepository, @@ -152,6 +154,71 @@ class PolicyValidator: verbose_proxy_logger.warning(f"Could not check model '{model}': {str(e)}") return True # Assume valid on error + @staticmethod + def _scope_error( + policy_name: str, + error_type: PolicyValidationErrorType, + field: str, + value: str, + label: str, + ) -> PolicyValidationError: + return PolicyValidationError( + policy_name=policy_name, + error_type=error_type, + message=( + f"{label.capitalize()} '{value}' does not exist. Reference an existing " + f"{label} or use a wildcard pattern (e.g. '{value}*') to match by prefix." + ), + field=field, + value=value, + ) + + async def find_invalid_scope_entries( + self, + policy_name: str, + teams: list[str] | None = None, + keys: list[str] | None = None, + models: list[str] | None = None, + ) -> list[PolicyValidationError]: + """ + Validate the concrete scope entries of a policy attachment. + + Returns an error for every non-wildcard entry that does not resolve to an + existing team alias, key alias, or model. Wildcard patterns are always + accepted: a pattern like "healthcare-*" may match zero entities today and + match ones created later, so it cannot be validated by existence. Tags are + intentionally not checked - they are free-form labels with no registry to + validate against. + """ + # A concrete entry is one the request-time matcher compares by exact equality; + # only a trailing "*" is a wildcard (RouteChecks._is_wildcard_pattern), and those + # are left unvalidated since they may match zero entities today and more later. + is_pattern = RouteChecks._is_wildcard_pattern + concrete_teams = [t for t in (teams or []) if not is_pattern(pattern=t)] + concrete_keys = [k for k in (keys or []) if not is_pattern(pattern=k)] + concrete_models = [m for m in (models or []) if not is_pattern(pattern=m)] + + team_exists = await asyncio.gather(*(self.check_team_alias_exists(t) for t in concrete_teams)) + key_exists = await asyncio.gather(*(self.check_key_alias_exists(k) for k in concrete_keys)) + + return [ + *( + self._scope_error(policy_name, PolicyValidationErrorType.INVALID_TEAM, "teams", team, "team") + for team, exists in zip(concrete_teams, team_exists) + if not exists + ), + *( + self._scope_error(policy_name, PolicyValidationErrorType.INVALID_KEY, "keys", key, "key") + for key, exists in zip(concrete_keys, key_exists) + if not exists + ), + *( + self._scope_error(policy_name, PolicyValidationErrorType.INVALID_MODEL, "models", model, "model") + for model in concrete_models + if not self.check_model_exists(model) + ), + ] + def _validate_inheritance_chain( self, policy_name: str, diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 6298a5a98b1..f0ca1f6396f 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -63,6 +63,7 @@ from litellm.litellm_core_utils.litellm_logging import ( _init_custom_logger_compatible_class, ) from litellm.litellm_core_utils.safe_json_dumps import safe_dumps +from litellm.litellm_core_utils.safe_json_loads import safe_json_loads from litellm.proxy._types import ( UI_TEAM_ID, CallbackDelete, @@ -74,6 +75,7 @@ from litellm.proxy._types import ( ConfigGeneralSettings, ConfigList, ConfigYAML, + CoordinationRedisParams, EnterpriseLicenseData, FieldDetail, InvitationClaim, @@ -102,6 +104,7 @@ from litellm.proxy._types import ( ) from litellm.proxy.common_utils.cache_pydantic_utils import CacheCodec from litellm.proxy.common_utils.callback_utils import ( + is_sensitive_callback_key, normalize_callback_names, process_callback, ) @@ -210,6 +213,7 @@ from contextlib import asynccontextmanager from functools import lru_cache import litellm +import litellm._redis from litellm import Router from litellm._logging import verbose_proxy_logger, verbose_router_logger from litellm.caching.caching import DualCache, RedisCache @@ -359,6 +363,10 @@ from litellm.proxy.management_endpoints.cache_settings_endpoints import ( from litellm.proxy.management_endpoints.callback_management_endpoints import ( router as callback_management_endpoints_router, ) +from litellm.proxy.management_endpoints.coordination_redis_endpoints import ( + get_persisted_coordination_redis_settings, + router as coordination_redis_settings_router, +) from litellm.proxy.management_endpoints.common_utils import ( _user_has_admin_privileges, _user_has_admin_view, @@ -425,7 +433,10 @@ from litellm.proxy.management_endpoints.user_agent_analytics_endpoints import ( from litellm.proxy.management_endpoints.workflow_management_endpoints import ( router as workflow_management_router, ) -from litellm.proxy.management_helpers.audit_logs import create_audit_log_for_update +from litellm.proxy.management_helpers.audit_logs import ( + create_audit_log_for_update, + create_object_audit_log, +) from litellm.proxy.memory.memory_endpoints import router as memory_router from litellm.proxy.plugin_routes import ( router as plugin_router, @@ -470,6 +481,7 @@ from litellm.proxy.response_api_endpoints.endpoints import router as response_ro from litellm.proxy.route_llm_request import route_request from litellm.proxy.search_endpoints.endpoints import router as search_router from litellm.proxy.shutdown.graceful_shutdown_manager import GracefulShutdownManager +from litellm.proxy.spend_tracking.budget_reservation import get_budget_window_start from litellm.proxy.spend_tracking.spend_management_endpoints import ( router as spend_management_router, ) @@ -632,6 +644,43 @@ premium_user_data: Optional["EnterpriseLicenseData"] = _license_check.airgapped_ global_max_parallel_request_retries_env: Optional[str] = os.getenv("LITELLM_GLOBAL_MAX_PARALLEL_REQUEST_RETRIES") proxy_state = ProxyState() SENSITIVE_DATA_MASKER = SensitiveDataMasker() + + +# Secret-bearing general_settings fields the segment masker does not match by +# name: database_url and database_extra_connection_params embed DB credentials, +# pass_through_endpoints carry upstream Authorization headers, and +# alert_to_webhook_url is itself a webhook secret +_EXTRA_SECRET_GENERAL_SETTINGS_FIELDS = frozenset( + { + "database_url", + "database_extra_connection_params", + "pass_through_endpoints", + "alert_to_webhook_url", + } +) + + +def _redact_worker_config_for_logging(worker_config: str | dict[str, JsonValue] | None) -> JsonValue: + """Mask sensitive fields in the worker config before it enters a log record. + + `worker_config` reaches `proxy_startup_event` as either the JSON blob + persisted by `save_worker_config` (a string) or the dict passed directly + to `initialize`. Both shapes can carry `master_key`, `database_url`, + provider API keys, etc.; passing the raw value to `verbose_proxy_logger` + leaks them whenever the last-line-of-defense regex filter is bypassed + (`LITELLM_DISABLE_REDACT_SECRETS=true`, an older log sink, a downstream + handler that captures records pre-filter). Redact at the source. + """ + if worker_config is None: + return None + if isinstance(worker_config, dict): + return _redact_secret_values_in_obj(worker_config) + parsed = safe_json_loads(worker_config, default=None) + if isinstance(parsed, dict): + return safe_dumps(_redact_secret_values_in_obj(parsed)) + return worker_config + + if global_max_parallel_request_retries_env is None: global_max_parallel_request_retries: int = 3 else: @@ -828,7 +877,7 @@ async def proxy_startup_event(app: FastAPI): ### LOAD CONFIG ### worker_config: Optional[Union[str, dict]] = get_secret("WORKER_CONFIG") # type: ignore env_config_yaml: Optional[str] = get_secret_str("CONFIG_FILE_PATH") - verbose_proxy_logger.debug("worker_config: %s", worker_config) + verbose_proxy_logger.debug("worker_config: %s", _redact_worker_config_for_logging(worker_config)) # check if it's a valid file path if env_config_yaml is not None: if os.path.isfile(env_config_yaml) and proxy_config.is_yaml(config_file_path=env_config_yaml): @@ -882,6 +931,16 @@ async def proxy_startup_event(app: FastAPI): asyncio.create_task(_run_pw_migration()) + ## A coordination_redis block saved from the admin UI lives in the database, + ## which is only reachable once the prisma client exists. Apply it here, before + ## the coordination Redis is published to its consumers below. + db_coordination_redis_cache = await ProxyStartupEvent._init_coordination_redis_from_db( + litellm_settings=proxy_config.get_config_state().get("litellm_settings") or {}, + llm_router=llm_router, + ) + if db_coordination_redis_cache is not None: + _set_redis_usage_cache(db_coordination_redis_cache) + ## use_redis_transaction_buffer: fall back to a standalone Redis (REDIS_* env) ## when the proxy cache backend is not Redis ## transaction_buffer_redis_cache = redis_usage_cache @@ -1074,33 +1133,6 @@ _OPENAPI_HTTP_METHODS = { # `_SSO_SENSITIVE_FIELDS` / `_CACHE_SENSITIVE_FIELDS` constants in the SSO # and cache endpoint files. _ALERTING_SENSITIVE_VARS: Set[str] = {"SLACK_WEBHOOK_URL", "SMTP_PASSWORD"} -_DB_LITELLM_PARAM_ENV_REF_KEYS = frozenset( - { - "api_key", - "client_secret", - "vertex_credentials", - "vertex_ai_credentials", - "aws_access_key_id", - "aws_secret_access_key", - } -) - - -def _db_model_is_team_scoped(model: object) -> bool: - model_info = getattr(model, "model_info", None) - if isinstance(model_info, BaseModel): - return getattr(model_info, "team_id", None) is not None - if isinstance(model_info, str): - try: - model_info = json.loads(model_info) - except (TypeError, ValueError): - model_info = None - if isinstance(model_info, dict) and model_info.get("team_id") is not None: - return True - if getattr(model_info, "team_id", None) is not None: - return True - model_name = getattr(model, "model_name", None) - return isinstance(model_name, str) and model_name.startswith("model_name_") def _strip_operation_id_method_suffix(operation_id: str) -> str: @@ -1434,31 +1466,6 @@ def _get_cors_config( origins, allow_cors_credentials = _get_cors_config() -def _restructure_ui_html_files(ui_root: str) -> None: - """Ensure each exported HTML route is available as /index.html.""" - - for current_root, _, files in os.walk(ui_root): - rel_root = os.path.relpath(current_root, ui_root) - first_segment = "" if rel_root == "." else rel_root.split(os.sep)[0] - - if first_segment in {"_next", "litellm-asset-prefix"}: - continue - - for filename in files: - if not filename.endswith(".html") or filename == "index.html": - continue - - file_path = os.path.join(current_root, filename) - target_dir = os.path.splitext(file_path)[0] - target_path = os.path.join(target_dir, "index.html") - - os.makedirs(target_dir, exist_ok=True) - try: - os.replace(file_path, target_path) - except FileNotFoundError: - continue - - # get current directory try: current_dir = os.path.dirname(os.path.abspath(__file__)) @@ -1693,17 +1700,43 @@ try: # # Mount the _next directory at the root level app.mount( "/_next", - StaticFiles(directory=os.path.join(ui_path, "_next"), check_dir=False), + StaticFiles(directory=os.path.join(ui_path, "_next")), name="next_static", ) app.mount( f"{litellm_asset_prefix}/_next", - StaticFiles(directory=os.path.join(ui_path, "_next"), check_dir=False), + StaticFiles(directory=os.path.join(ui_path, "_next")), name="next_static", ) # print(f"mounted _next at {server_root_path}/ui/_next") - app.mount("/ui", StaticFiles(directory=ui_path, html=True, check_dir=False), name="ui") + app.mount("/ui", StaticFiles(directory=ui_path, html=True), name="ui") + + def _restructure_ui_html_files(ui_root: str) -> None: + """Ensure each exported HTML route is available as /index.html.""" + + for current_root, _, files in os.walk(ui_root): + rel_root = os.path.relpath(current_root, ui_root) + first_segment = "" if rel_root == "." else rel_root.split(os.sep)[0] + + # Ignore Next.js asset directories + if first_segment in {"_next", "litellm-asset-prefix"}: + continue + + for filename in files: + if not filename.endswith(".html") or filename == "index.html": + continue + + file_path = os.path.join(current_root, filename) + target_dir = os.path.splitext(file_path)[0] + target_path = os.path.join(target_dir, "index.html") + + os.makedirs(target_dir, exist_ok=True) + try: + os.replace(file_path, target_path) + except FileNotFoundError: + # Another process may have already moved this file. + continue # Handle HTML file restructuring # Only restructure if: @@ -2263,111 +2296,133 @@ async def increment_spend_counters( budget_reservation["finalized"] = True return - if token is not None: - # token arrives pre-hashed from metadata["user_api_key"] (auth flow + cost: float = response_cost + + async def _key_scope(key_token: str) -> None: + # key_token arrives pre-hashed from metadata["user_api_key"] (auth flow # hashes raw "sk-..." keys before they reach the callback). The # startswith("sk-") check is a safety net matching update_cache — # if a raw key somehow arrives, hash it; otherwise use as-is to # avoid double-hashing (budget checks read valid_token.token which # is single-hashed). - hashed_token = hash_token(token=token) if isinstance(token, str) and token.startswith("sk-") else token + hashed_token = ( + hash_token(token=key_token) if isinstance(key_token, str) and key_token.startswith("sk-") else key_token + ) key_counter_key = f"spend:key:{hashed_token}" if key_counter_key not in reserved_counter_keys: await _init_and_increment_spend_counter( counter_key=key_counter_key, source_cache_key=hashed_token, - increment=response_cost, + increment=cost, ) - # Increment per-window budget counters for multi-budget keys key_obj = await user_api_key_cache.async_get_cache(key=hashed_token) - if key_obj is not None: - key_budget_limits = getattr(key_obj, "budget_limits", None) or ( - key_obj.get("budget_limits") if isinstance(key_obj, dict) else None - ) - if isinstance(key_budget_limits, str): - key_budget_limits = json.loads(key_budget_limits) - if isinstance(key_budget_limits, list): - for window in key_budget_limits: - duration = window["budget_duration"] if isinstance(window, dict) else window.budget_duration - key_window_counter = f"spend:key:{hashed_token}:window:{duration}" - if key_window_counter not in reserved_counter_keys: - from litellm.proxy.spend_tracking.budget_reservation import ( - get_budget_window_start, - ) + if key_obj is None: + return + key_budget_limits = getattr(key_obj, "budget_limits", None) or ( + key_obj.get("budget_limits") if isinstance(key_obj, dict) else None + ) + if isinstance(key_budget_limits, str): + key_budget_limits = json.loads(key_budget_limits) + if not isinstance(key_budget_limits, list): + return + for window in key_budget_limits: + duration = window["budget_duration"] if isinstance(window, dict) else window.budget_duration + key_window_counter = f"spend:key:{hashed_token}:window:{duration}" + if key_window_counter not in reserved_counter_keys: + await _init_and_increment_window_spend_counter( + counter_key=key_window_counter, + entity_type="Key", + entity_id=hashed_token, + window_start=get_budget_window_start(window), + increment=cost, + ) - await _init_and_increment_window_spend_counter( - counter_key=key_window_counter, - entity_type="Key", - entity_id=hashed_token, - window_start=get_budget_window_start(window), - increment=response_cost, - ) - - if team_id is not None: - team_counter_key = f"spend:team:{team_id}" + async def _team_scope(scope_team_id: str) -> None: + team_counter_key = f"spend:team:{scope_team_id}" if team_counter_key not in reserved_counter_keys: await _init_and_increment_spend_counter( counter_key=team_counter_key, - source_cache_key=f"team_id:{team_id}", - increment=response_cost, + source_cache_key=f"team_id:{scope_team_id}", + increment=cost, ) - # Increment per-window budget counters for multi-budget teams - team_obj = await user_api_key_cache.async_get_cache(key=f"team_id:{team_id}") - if team_obj is not None: - team_budget_limits = getattr(team_obj, "budget_limits", None) or ( - team_obj.get("budget_limits") if isinstance(team_obj, dict) else None + team_obj = await user_api_key_cache.async_get_cache(key=f"team_id:{scope_team_id}") + if team_obj is None: + return + team_budget_limits = getattr(team_obj, "budget_limits", None) or ( + team_obj.get("budget_limits") if isinstance(team_obj, dict) else None + ) + if isinstance(team_budget_limits, str): + team_budget_limits = json.loads(team_budget_limits) + if not isinstance(team_budget_limits, list): + return + for window in team_budget_limits: + duration = window["budget_duration"] if isinstance(window, dict) else window.budget_duration + team_window_counter = f"spend:team:{scope_team_id}:window:{duration}" + if team_window_counter not in reserved_counter_keys: + await _init_and_increment_window_spend_counter( + counter_key=team_window_counter, + entity_type="Team", + entity_id=scope_team_id, + window_start=get_budget_window_start(window), + increment=cost, + ) + + async def _team_member_scope(scope_user_id: str, scope_team_id: str) -> None: + team_member_counter_key = f"spend:team_member:{scope_user_id}:{scope_team_id}" + if team_member_counter_key in reserved_counter_keys: + return + await _init_and_increment_spend_counter( + counter_key=team_member_counter_key, + source_cache_key=f"team_membership:{scope_user_id}:{scope_team_id}", + increment=cost, + ) + + async def _user_scope(scope_user_id: str) -> None: + user_counter_key = f"spend:user:{scope_user_id}" + if user_counter_key in reserved_counter_keys: + return + await _init_and_increment_spend_counter( + counter_key=user_counter_key, + source_cache_key=scope_user_id, + increment=cost, + ) + + scope_coros = tuple( + coro + for coro in ( + _key_scope(token) if token is not None else None, + _team_scope(team_id) if team_id is not None else None, + _team_member_scope(user_id, team_id) if user_id is not None and team_id is not None else None, + _user_scope(user_id) if user_id is not None else None, + _increment_end_user_and_tag_spend_counters( + end_user_id=end_user_id, + tags=tags, + response_cost=cost, + reserved_counter_keys=reserved_counter_keys, ) - if isinstance(team_budget_limits, str): - team_budget_limits = json.loads(team_budget_limits) - if isinstance(team_budget_limits, list): - for window in team_budget_limits: - duration = window["budget_duration"] if isinstance(window, dict) else window.budget_duration - team_window_counter = f"spend:team:{team_id}:window:{duration}" - if team_window_counter not in reserved_counter_keys: - from litellm.proxy.spend_tracking.budget_reservation import ( - get_budget_window_start, - ) - - await _init_and_increment_window_spend_counter( - counter_key=team_window_counter, - entity_type="Team", - entity_id=team_id, - window_start=get_budget_window_start(window), - increment=response_cost, - ) - - if user_id is not None and team_id is not None: - team_member_counter_key = f"spend:team_member:{user_id}:{team_id}" - if team_member_counter_key not in reserved_counter_keys: - await _init_and_increment_spend_counter( - counter_key=team_member_counter_key, - source_cache_key=f"team_membership:{user_id}:{team_id}", - increment=response_cost, + if end_user_id is not None or tags is not None + else None, + _increment_org_spend_counter( + org_id=org_id, + response_cost=cost, + reserved_counter_keys=reserved_counter_keys, ) - - if user_id is not None: - user_counter_key = f"spend:user:{user_id}" - if user_counter_key not in reserved_counter_keys: - await _init_and_increment_spend_counter( - counter_key=user_counter_key, - source_cache_key=user_id, - increment=response_cost, - ) - - await _increment_end_user_and_tag_spend_counters( - end_user_id=end_user_id, - tags=tags, - response_cost=response_cost, - reserved_counter_keys=reserved_counter_keys, + if org_id is not None + else None, + ) + if coro is not None ) - await _increment_org_spend_counter( - org_id=org_id, - response_cost=response_cost, - reserved_counter_keys=reserved_counter_keys, - ) + # return_exceptions so a failing scope does not leave its siblings running + # as orphaned tasks that race the caller's reservation-counter invalidation; + # all scopes settle, then the first error propagates as before. + scope_results = await asyncio.gather(*scope_coros, return_exceptions=True) + scope_errors = [r for r in scope_results if isinstance(r, BaseException)] + if scope_errors: + raise scope_errors[0] + if budget_reservation is not None: budget_reservation["finalized"] = True @@ -3488,6 +3543,123 @@ def _scrub_db_overlay_remote_module_loads(section: str, db_value: Any) -> Any: return sanitized +def _normalize_user_url_validation(value: object) -> Optional[bool]: + if value is None: + return None + if isinstance(value, str): + return str_to_bool(value) + return bool(value) + + +def _apply_ssrf_general_settings(settings: Mapping[str, object]) -> None: + if "user_url_allowed_hosts" in settings: + litellm.user_url_allowed_hosts = cast(list[str], settings["user_url_allowed_hosts"]) + + user_url_validation = _normalize_user_url_validation(settings.get("user_url_validation")) + if user_url_validation is not None: + litellm.user_url_validation = user_url_validation + + if "provider_url_destination_allowed_hosts" in settings: + litellm.provider_url_destination_allowed_hosts = cast( + list[str], settings["provider_url_destination_allowed_hosts"] + ) + + +def _set_redis_usage_cache(coordination_redis_cache: RedisCache | None) -> None: + """Publish the resolved coordination Redis to the consumers that read it directly.""" + global redis_usage_cache + redis_usage_cache = coordination_redis_cache + + +def _resolve_coordination_redis_env_refs(raw_params: Mapping[str, object]) -> dict[str, object]: + """Resolve `os.environ/VAR` references in a coordination_redis block.""" + return { + key: (get_secret(value) if isinstance(value, str) and value.startswith("os.environ/") else value) + for key, value in raw_params.items() + } + + +def _build_redis_usage_cache(redis_params: Mapping[str, object]) -> RedisCache: + """ + Builds the proxy's coordination Redis client from resolved connection + params. Cluster-mode targets (explicit `startup_nodes` or the + REDIS_CLUSTER_NODES env var) get a `RedisClusterCache`, so consumers that + branch on cluster mode (e.g. the v3 rate limiter) take the cluster path; + everything else (host/url/sentinel) gets a plain `RedisCache`. + """ + startup_nodes = redis_params.get("startup_nodes") + if startup_nodes is None: + env_cluster_nodes = get_secret_str("REDIS_CLUSTER_NODES") + if env_cluster_nodes is not None: + startup_nodes = json.loads(env_cluster_nodes) + non_node_params = {key: value for key, value in redis_params.items() if key != "startup_nodes"} + if startup_nodes: + return RedisClusterCache(startup_nodes=startup_nodes, **non_node_params) + return RedisCache(**non_node_params) + + +def _environment_has_redis_connection_target() -> bool: + """ + Whether the REDIS_* environment variables name a Redis to connect to (host, + url, cluster nodes, or sentinel nodes). Read-only: callers that only need to + know whether the env fallback would apply use this instead of building a + client. + """ + redis_env_kwargs = litellm._redis._redis_kwargs_from_environment() + return ( + "host" in redis_env_kwargs + or "url" in redis_env_kwargs + or get_secret_str("REDIS_CLUSTER_NODES") is not None + or get_secret_str("REDIS_SENTINEL_NODES") is not None + ) + + +def _build_redis_usage_cache_from_environment() -> RedisCache | None: + """ + Builds a standalone coordination Redis from REDIS_* environment variables. + + Lets the proxy's coordination Redis (cross-pod tpm/rpm rate limits, spend + tracking, pod lock manager) run when the response-cache backend is not a + plain Redis KV cache (e.g. a semantic cache, disk, or s3). + + Returns None when the environment carries no connection target (host, url, + cluster nodes, or sentinel nodes). + """ + if not _environment_has_redis_connection_target(): + return None + return _build_redis_usage_cache(litellm._redis._redis_kwargs_from_environment()) + + +def _attach_redis_usage_cache(redis_cache: RedisCache, enable_redis_auth_cache: bool) -> None: + """ + Wires an established coordination Redis into the proxy-level caches that + consume it directly: the spend counter cache, the cluster-wide config + cache, and (only when opted in) the virtual-key auth cache. + """ + spend_counter_cache.attach_redis_cache( + redis_cache, + default_redis_ttl=litellm.default_redis_ttl, + ) + if enable_redis_auth_cache is True: + user_api_key_cache.attach_redis_cache( + redis_cache, + default_redis_ttl=litellm.default_redis_ttl, + ) + verbose_proxy_logger.info( + "enable_redis_auth_cache=True: attached Redis to " + "user_api_key_cache — virtual-key lookups are now " + "shared across all proxy workers." + ) + else: + verbose_proxy_logger.info( + "enable_redis_auth_cache is not set: user_api_key_cache " + "remains in-memory only (per-worker). Set " + "litellm_settings.enable_redis_auth_cache: true to share " + "the auth cache across workers and reduce DB load." + ) + litellm_config_cache.redis_cache = redis_cache + + class ProxyConfig: """ Abstraction class on top of config loading/updating logic. Gives us one place to control all config updating logic. @@ -3686,12 +3858,52 @@ class ProxyConfig: team_config = self._get_team_config(team_id=team_id, all_teams_config=all_teams_config) return team_config + def _init_coordination_redis(self, config: dict) -> RedisCache | None: + """ + Builds the coordination Redis from `general_settings.coordination_redis` + when present, attaching it to the proxy-level caches. Runs before cache + init, so an explicit block takes precedence over borrowing the + response-cache Redis and over the REDIS_* env fallback. Returns the + built client (None when the block is absent) for the caller to publish. + """ + settings = config.get("general_settings") or {} + litellm_settings = config.get("litellm_settings") or {} + raw_params = settings.get("coordination_redis") + if raw_params is None: + return None + if not isinstance(raw_params, dict): + raise ValueError("general_settings.coordination_redis must be a mapping of Redis connection params") + + coordination_params = CoordinationRedisParams(**_resolve_coordination_redis_env_refs(raw_params)) + if not coordination_params.has_connection_target(): + raise ValueError( + "general_settings.coordination_redis needs a connection target: " + "set one of host, url, startup_nodes, or sentinel_nodes" + ) + + coordination_redis_cache = _build_redis_usage_cache(coordination_params.model_dump(exclude_none=True)) + _attach_redis_usage_cache( + coordination_redis_cache, + enable_redis_auth_cache=litellm_settings.get("enable_redis_auth_cache", False) is True, + ) + verbose_proxy_logger.info( + "coordination_redis: using a standalone Redis from general_settings " + "for usage tracking, rate limiting, and cross-pod coordination." + ) + return coordination_redis_cache + def _init_cache( self, cache_params: dict, enable_redis_auth_cache: bool = False, - ): - global redis_usage_cache, llm_router, general_settings + ) -> RedisCache | None: + """ + Initializes the response cache and resolves the coordination Redis. + + Returns the coordination Redis for the caller to publish: an explicit + coordination_redis block already set wins, else a plain-Redis response + cache backend is borrowed, else the REDIS_* environment fallback applies. + """ from litellm import Cache if "default_in_memory_ttl" in cache_params: @@ -3702,37 +3914,29 @@ class ProxyConfig: litellm.cache = Cache(**cache_params) - if litellm.cache is not None and isinstance(litellm.cache.cache, (RedisCache, RedisClusterCache)): - ## INIT PROXY REDIS USAGE CLIENT ## - redis_usage_cache = litellm.cache.cache - spend_counter_cache.attach_redis_cache( - redis_usage_cache, - default_redis_ttl=litellm.default_redis_ttl, - ) - # Note: PKCE verifier storage uses redis_usage_cache directly (not - # user_api_key_cache) to avoid routing all API-key lookups through Redis. - if enable_redis_auth_cache is True: - user_api_key_cache.attach_redis_cache( - redis_usage_cache, - default_redis_ttl=litellm.default_redis_ttl, - ) - verbose_proxy_logger.info( - "enable_redis_auth_cache=True: attached Redis to " - "user_api_key_cache — virtual-key lookups are now " - "shared across all proxy workers." - ) + resolved_usage_cache = redis_usage_cache + cache_backend = litellm.cache.cache if litellm.cache is not None else None + if resolved_usage_cache is None: + if isinstance(cache_backend, (RedisCache, RedisClusterCache)): + ## INIT PROXY REDIS USAGE CLIENT ## + resolved_usage_cache = cache_backend else: - verbose_proxy_logger.info( - "enable_redis_auth_cache is not set: user_api_key_cache " - "remains in-memory only (per-worker). Set " - "litellm_settings.enable_redis_auth_cache: true to share " - "the auth cache across workers and reduce DB load." - ) - litellm_config_cache.redis_cache = redis_usage_cache + resolved_usage_cache = _build_redis_usage_cache_from_environment() + if resolved_usage_cache is not None: + verbose_proxy_logger.info( + "Cache backend %s is not a Redis KV cache; built a standalone " + "Redis from REDIS_* environment variables for usage tracking, " + "rate limiting, and cross-pod coordination.", + type(cache_backend).__name__, + ) + + if resolved_usage_cache is not None: # Note: PKCE verifier storage uses redis_usage_cache directly (not # user_api_key_cache) to avoid routing all API-key lookups through Redis. + _attach_redis_usage_cache(resolved_usage_cache, enable_redis_auth_cache) elif litellm_config_cache.redis_cache is None: verbose_proxy_logger.info("litellm_config_cache: no Redis configured; cluster-wide cache sharing disabled.") + return resolved_usage_cache def switch_on_llm_response_caching(self): """ @@ -3977,6 +4181,11 @@ class ProxyConfig: self._load_environment_variables(config=config) + ## Coordination Redis (before cache init, so the explicit block wins) + coordination_redis_cache = self._init_coordination_redis(config=config) + if coordination_redis_cache is not None: + _set_redis_usage_cache(coordination_redis_cache) + ## Callback settings callback_settings = config.get("callback_settings", {}) if callback_settings: @@ -4057,9 +4266,11 @@ class ProxyConfig: cache_params[key] = get_secret(value) ## to pass a complete url, or set ssl=True, etc. just set it as `os.environ[REDIS_URL] = `, _redis.py checks for REDIS specific environment variables - self._init_cache( - cache_params=cache_params, - enable_redis_auth_cache=litellm_settings.get("enable_redis_auth_cache", False) is True, + _set_redis_usage_cache( + self._init_cache( + cache_params=cache_params, + enable_redis_auth_cache=litellm_settings.get("enable_redis_auth_cache", False) is True, + ) ) if litellm.cache is not None: verbose_proxy_logger.debug(f"{blue_color_code}Set Cache on LiteLLM Proxy{reset_color_code}") @@ -4245,7 +4456,9 @@ class ProxyConfig: raise Exception( f"team_id missing from default_team_settings at index={idx}\npassed in value={type(team_setting)}" ) - verbose_proxy_logger.debug(f"{blue_color_code} setting litellm.{key}={value}{reset_color_code}") + verbose_proxy_logger.debug( + f"{blue_color_code} setting litellm.{key}={_redact_general_setting_value(key, value, is_full_admin=False)}{reset_color_code}" + ) setattr(litellm, key, value) elif key == "upperbound_key_generate_params": if value is not None and isinstance(value, dict): @@ -4260,7 +4473,9 @@ class ProxyConfig: litellm._turn_on_json() verbose_proxy_logger.debug(f"{blue_color_code} Enabled JSON logging via config{reset_color_code}") else: - verbose_proxy_logger.debug(f"{blue_color_code} setting litellm.{key}={value}{reset_color_code}") + verbose_proxy_logger.debug( + f"{blue_color_code} setting litellm.{key}={_redact_general_setting_value(key, value, is_full_admin=False)}{reset_color_code}" + ) setattr(litellm, key, value) if key == "request_timeout": litellm.request_timeout_explicitly_set = True @@ -4308,9 +4523,9 @@ class ProxyConfig: ### CONNECT TO DATABASE ### database_url = general_settings.get("database_url", None) if database_url and database_url.startswith("os.environ/"): - verbose_proxy_logger.debug("GOING INTO LITELLM.GET_SECRET!") + verbose_proxy_logger.debug("Resolving database_url via secret manager") database_url = get_secret(database_url) - verbose_proxy_logger.debug("RETRIEVED DB URL: %s", database_url) + verbose_proxy_logger.debug("Resolved database_url from secret manager") ### MASTER KEY ### master_key = general_settings.get("master_key", get_secret("LITELLM_MASTER_KEY", None)) @@ -4471,6 +4686,9 @@ class ProxyConfig: RoleBasedPermissions(**role_permission) for role_permission in rbac_role_permissions ] + ### SSRF URL VALIDATION SETTINGS ### + _apply_ssrf_general_settings(general_settings) + ## check if user has set a premium feature in general_settings if general_settings.get("enforced_params") is not None and premium_user is not True: raise ValueError("Trying to use `enforced_params`" + CommonProxyErrors.not_premium_user.value) @@ -4710,7 +4928,7 @@ class ProxyConfig: """ _alerting_callbacks = general_settings.get("alerting", None) - verbose_proxy_logger.debug(f"_alerting_callbacks: {general_settings}") + verbose_proxy_logger.debug("_alerting_callbacks: %s", _alerting_callbacks) if _alerting_callbacks is None: return @@ -4902,17 +5120,12 @@ class ProxyConfig: deleted_deployments += 1 return deleted_deployments - def _resolve_db_litellm_param(self, key: str, value: object, resolve_env_refs: bool = True) -> object: + def _resolve_db_litellm_param(self, key: str, value: object) -> object: if not isinstance(value, str): return value decrypted_value = decrypt_value_helper(value=value, key=key, return_original_value=True) - if ( - resolve_env_refs - and key in _DB_LITELLM_PARAM_ENV_REF_KEYS - and isinstance(decrypted_value, str) - and decrypted_value.startswith("os.environ/") - ): + if isinstance(decrypted_value, str) and decrypted_value.startswith("os.environ/"): return get_secret(decrypted_value) return decrypted_value @@ -4933,13 +5146,10 @@ class ProxyConfig: ## ADD MODEL LOGIC for m in db_models: _litellm_params = m.litellm_params - resolve_env_refs = not _db_model_is_team_scoped(m) if isinstance(_litellm_params, dict): # decrypt values for k, v in _litellm_params.items(): - _litellm_params[k] = self._resolve_db_litellm_param( - key=k, value=v, resolve_env_refs=resolve_env_refs - ) + _litellm_params[k] = self._resolve_db_litellm_param(key=k, value=v) _litellm_params = LiteLLM_Params(**_litellm_params) else: @@ -4965,15 +5175,12 @@ class ProxyConfig: _model_list: list = [] for m in new_models: _litellm_params = m.litellm_params - resolve_env_refs = not _db_model_is_team_scoped(m) if isinstance(_litellm_params, BaseModel): _litellm_params = _litellm_params.model_dump() if isinstance(_litellm_params, dict): # decrypt values for k, v in _litellm_params.items(): - _litellm_params[k] = self._resolve_db_litellm_param( - key=k, value=v, resolve_env_refs=resolve_env_refs - ) + _litellm_params[k] = self._resolve_db_litellm_param(key=k, value=v) _litellm_params = LiteLLM_Params(**_litellm_params) else: verbose_proxy_logger.error( @@ -5508,6 +5715,15 @@ class ProxyConfig: if old_value != new_value: await self._reschedule_spend_log_cleanup_job() + for key in ( + "user_url_allowed_hosts", + "user_url_validation", + "provider_url_destination_allowed_hosts", + ): + if key in _general_settings: + general_settings[key] = _general_settings[key] + _apply_ssrf_general_settings(_general_settings) + def _update_config_fields( self, current_config: dict, @@ -5618,7 +5834,11 @@ class ProxyConfig: param_name = getattr(response, "param_name", None) param_value = getattr(response, "param_value", None) - verbose_proxy_logger.debug(f"param_name={param_name}, param_value={param_value}") + verbose_proxy_logger.debug( + "param_name=%s, param_value=%s", + param_name, + _redact_config_param_value_for_logging(param_name, param_value), + ) if param_name is not None and param_value is not None: config = self._update_config_fields( @@ -6280,6 +6500,17 @@ class ProxyConfig: from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( global_mcp_server_manager, ) + from litellm.proxy._experimental.mcp_server.oauth2_flow_backfill import ( + backfill_null_oauth2_flows, + ) + + try: + if prisma_client is not None: + await backfill_null_oauth2_flows(prisma_client) + except Exception as e: # noqa: BLE001 + verbose_proxy_logger.exception( + "litellm.proxy.proxy_server.py::ProxyConfig:_init_mcp_servers_in_db backfill - {}".format(str(e)) + ) try: await global_mcp_server_manager.reload_servers_from_database() @@ -6288,6 +6519,10 @@ class ProxyConfig: "litellm.proxy.proxy_server.py::ProxyConfig:_init_mcp_servers_in_db - {}".format(str(e)) ) + async def init_mcp_servers_from_db(self) -> None: + if self._should_load_db_object(object_type="mcp"): + await self._init_mcp_servers_in_db() + async def _init_agents_in_db(self, prisma_client: PrismaClient): from litellm.proxy.agent_endpoints.agent_registry import ( global_agent_registry as AGENT_REGISTRY, @@ -6304,7 +6539,6 @@ class ProxyConfig: async def _init_search_tools_in_db(self, prisma_client: PrismaClient): """ Initialize search tools from database into the router on startup. - Only updates router if there are tools in the database, otherwise preserves config-loaded tools. """ global llm_router @@ -6314,26 +6548,29 @@ class ProxyConfig: from litellm.router_utils.search_api_router import SearchAPIRouter try: - search_tools = await SearchToolRegistry.get_all_search_tools_from_db(prisma_client=prisma_client) + db_search_tools = await SearchToolRegistry.get_all_search_tools_from_db(prisma_client=prisma_client) - verbose_proxy_logger.info(f"Loading {len(search_tools)} search tool(s) from database into router") + parsed_tools = self.parse_search_tools(self.get_config_state()) + config_search_tools = parsed_tools or [] - # Only update router if there are tools in the database - # This prevents overwriting config-loaded tools with an empty list - if len(search_tools) > 0: - if llm_router is not None: - # Add search tools to the router - await SearchAPIRouter.update_router_search_tools( - router_instance=llm_router, search_tools=search_tools - ) - verbose_proxy_logger.info(f"Successfully loaded {len(search_tools)} search tool(s) into router") - else: - verbose_proxy_logger.debug( - "Router not initialized yet, search tools will be added when router is created" - ) + search_tools = self._merge_config_and_db_search_tools( + config_search_tools=config_search_tools, + db_search_tools=[dict(tool) for tool in db_search_tools], + ) + + verbose_proxy_logger.info( + f"Loading {len(search_tools)} search tool(s) into router " + f"({len(config_search_tools)} from config, {len(db_search_tools)} from database)" + ) + + if llm_router is not None and search_tools: + await SearchAPIRouter.update_router_search_tools(router_instance=llm_router, search_tools=search_tools) + verbose_proxy_logger.info(f"Successfully loaded {len(search_tools)} search tool(s) into router") + elif llm_router is not None: + verbose_proxy_logger.debug("No search tools found in config or database, skipping router update") else: verbose_proxy_logger.debug( - "No search tools found in database, keeping config-loaded search tools (if any)" + "Router not initialized yet, search tools will be added when router is created" ) except Exception as e: @@ -6341,6 +6578,21 @@ class ProxyConfig: "litellm.proxy.proxy_server.py::ProxyConfig:_init_search_tools_in_db - {}".format(str(e)) ) + @staticmethod + def _merge_config_and_db_search_tools( + config_search_tools: list[SearchToolTypedDict], + db_search_tools: list[dict[str, Any]], + ) -> list[dict[str, Any]]: + db_tool_names = {tool.get("search_tool_name") for tool in db_search_tools} + return [ + *[ + dict(config_search_tool) + for config_search_tool in config_search_tools + if config_search_tool.get("search_tool_name") not in db_tool_names + ], + *db_search_tools, + ] + async def _init_pass_through_endpoints_in_db(self): from litellm.proxy.pass_through_endpoints.pass_through_endpoints import ( initialize_pass_through_endpoints_in_db, @@ -7163,6 +7415,46 @@ class ProxyStartupEvent: "Redis for the transaction buffer." ) + @staticmethod + async def _init_coordination_redis_from_db( + litellm_settings: Mapping[str, object], + llm_router: Optional[Router], + ) -> RedisCache | None: + """ + Applies a coordination_redis block saved to the database, which the admin + UI writes and the config file therefore never carries. + + Returns None when nothing is persisted or the persisted block names no + connection target, leaving the file/env resolution untouched. + """ + try: + persisted = await get_persisted_coordination_redis_settings() + except Exception as e: # noqa: BLE001 # a config-row read failure must not block proxy startup + verbose_proxy_logger.warning("Could not read coordination_redis from the database: %s", e) + return None + if persisted is None: + return None + + coordination_params = CoordinationRedisParams(**_resolve_coordination_redis_env_refs(persisted)) + if not coordination_params.has_connection_target(): + verbose_proxy_logger.warning( + "coordination_redis saved in the database names no connection target; ignoring it." + ) + return None + + coordination_redis_cache = _build_redis_usage_cache(coordination_params.model_dump(exclude_none=True)) + _attach_redis_usage_cache( + coordination_redis_cache, + enable_redis_auth_cache=litellm_settings.get("enable_redis_auth_cache", False) is True, + ) + if llm_router is not None and llm_router.cache.redis_cache is None: + llm_router._update_redis_cache(cache=coordination_redis_cache) + verbose_proxy_logger.info( + "coordination_redis: using the standalone Redis saved in the database " + "for usage tracking, rate limiting, and cross-pod coordination." + ) + return coordination_redis_cache + @staticmethod def _get_transaction_buffer_redis_cache( general_settings: dict, @@ -7175,7 +7467,6 @@ class ProxyStartupEvent: Returns None when the buffer is disabled, or when no Redis host or url is set in the environment. """ - from litellm._redis import _redis_kwargs_from_environment from litellm.secret_managers.main import str_to_bool _use_redis_transaction_buffer: bool | str | None = general_settings.get("use_redis_transaction_buffer", False) @@ -7185,11 +7476,7 @@ class ProxyStartupEvent: if not _use_redis_transaction_buffer: return None - redis_env_kwargs = _redis_kwargs_from_environment() - if "host" not in redis_env_kwargs and "url" not in redis_env_kwargs: - return None - - return RedisCache(**redis_env_kwargs) + return _build_redis_usage_cache_from_environment() @classmethod async def _initialize_semantic_tool_filter( @@ -7535,6 +7822,9 @@ class ProxyStartupEvent: ) await proxy_config.get_credentials(prisma_client=prisma_client) + if store_model_in_db is not True: + await proxy_config.init_mcp_servers_from_db() + await cls._initialize_slack_alerting_jobs( scheduler=scheduler, general_settings=general_settings, @@ -7592,6 +7882,7 @@ class ProxyStartupEvent: proxy_logging_obj=proxy_logging_obj, prisma_client=prisma_client, llm_router=llm_router, + track_unmanaged_vertex_batch_cost=general_settings.get("track_unmanaged_vertex_batch_cost", False), ) scheduler.add_job( check_batch_cost_job.check_batch_cost, @@ -8325,6 +8616,22 @@ async def model_info( ) +def _blocked_response_usage(original_response: Optional[Any]) -> "litellm.Usage": + """ + Token usage for a synthetic guardrail-blocked response. + + A post-call block replaces the LLM's response with the violation message, + but the upstream call already consumed tokens -- report that real usage + (carried on ``ModifyResponseException.original_response``) rather than + discarding it. Pre-call blocks never invoked the LLM (no original_response), + so usage is zero. + """ + usage = getattr(original_response, "usage", None) if original_response is not None else None + if isinstance(usage, litellm.Usage): + return usage + return litellm.Usage(prompt_tokens=0, completion_tokens=0, total_tokens=0) + + @router.post( "/v1/chat/completions", dependencies=[Depends(user_api_key_auth)], @@ -8422,6 +8729,8 @@ async def chat_completion( except ModifyResponseException as e: # Guardrail flagged content in passthrough mode - return 200 with violation message _data = e.request_data + # Capture logging_obj before post_call_failure_hook pops it from _data. + _logging_obj = _data.get("litellm_logging_obj") await proxy_logging_obj.post_call_failure_hook( user_api_key_dict=user_api_key_dict, original_exception=e, @@ -8431,6 +8740,9 @@ async def chat_completion( _chat_response.model = e.model # type: ignore _chat_response.choices[0].message.content = e.message # type: ignore _chat_response.choices[0].finish_reason = "content_filter" # type: ignore + # Report the blocked LLM response's real usage (set before the stream + # branch so both paths carry it); zero for pre-call blocks. + _chat_response.usage = _blocked_response_usage(e.original_response) # type: ignore if data.get("stream", None) is not None and data["stream"] is True: _iterator = litellm.utils.ModelResponseIterator(model_response=_chat_response, convert_to_delta=True) @@ -8438,7 +8750,7 @@ async def chat_completion( completion_stream=_iterator, model=e.model, custom_llm_provider="cached_response", - logging_obj=_data.get("litellm_logging_obj", None), + logging_obj=_logging_obj, ) selected_data_generator = select_data_generator( response=_streaming_response, @@ -8452,8 +8764,6 @@ async def chat_completion( media_type="text/event-stream", status_code=200, # Return 200 for passthrough mode ) - _usage = litellm.Usage(prompt_tokens=0, completion_tokens=0, total_tokens=0) - _chat_response.usage = _usage # type: ignore return _chat_response except RejectedRequestError as e: _data = e.request_data @@ -8582,11 +8892,7 @@ async def completion( # Set text attribute dynamically for text completion format setattr(_text_response.choices[0], "text", e.message) _text_response.model = e.model # type: ignore[assignment] - _usage = litellm.Usage( - prompt_tokens=0, - completion_tokens=0, - total_tokens=0, - ) + _usage = _blocked_response_usage(e.original_response) # Set usage attribute dynamically (ModelResponse accepts usage in __init__ but it's not in type definition) setattr(_text_response, "usage", _usage) _iterator = litellm.utils.ModelResponseIterator(model_response=_text_response, convert_to_delta=True) @@ -8611,11 +8917,7 @@ async def completion( _response = litellm.TextCompletionResponse() _response.choices[0].text = e.message _response.model = e.model # type: ignore - _usage = litellm.Usage( - prompt_tokens=0, - completion_tokens=0, - total_tokens=0, - ) + _usage = _blocked_response_usage(e.original_response) _response.usage = _usage # type: ignore return _response except RejectedRequestError as e: @@ -13603,9 +13905,7 @@ async def get_favicon(): ) current_dir = os.path.dirname(os.path.abspath(__file__)) - built_favicon = os.path.join(current_dir, "_experimental", "out", "favicon.ico") - bundled_favicon = os.path.join(current_dir, "swagger", "favicon.ico") - default_favicon = built_favicon if os.path.exists(built_favicon) else bundled_favicon + default_favicon = os.path.join(current_dir, "_experimental", "out", "favicon.ico") favicon_url = os.getenv("LITELLM_FAVICON_URL", "") @@ -13936,6 +14236,7 @@ async def update_config( # effect of auto-enabling slack alerting. if config_info.general_settings is not None: existing = await _read_section("general_settings") + before_general_settings = copy.deepcopy(existing) updates = config_info.general_settings.dict(exclude_none=True) for k, v in updates.items(): if k == "alert_to_webhook_url": @@ -13945,6 +14246,11 @@ async def update_config( existing["alerting"].append("slack") existing[k] = v await _upsert_section("general_settings", existing) + asyncio.create_task( + create_config_audit_log( + "general_settings", "updated", before_general_settings, existing, user_api_key_dict + ) + ) # environment_variables: idempotently encrypt the request values # (plaintext on first write, OR ciphertext the UI read back via @@ -13953,10 +14259,16 @@ async def update_config( # their stored ciphertext byte-for-byte. if config_info.environment_variables is not None: existing = await _read_section("environment_variables") + before_environment_variables = copy.deepcopy(existing) existing.update( proxy_config._encrypt_env_variables_for_db(environment_variables=config_info.environment_variables) ) await _upsert_section("environment_variables", existing) + asyncio.create_task( + create_config_audit_log( + "environment_variables", "updated", before_environment_variables, existing, user_api_key_dict + ) + ) # litellm_settings: merge existing + request, request wins (matching # router_settings semantics — the caller's value for any given key is @@ -13968,6 +14280,7 @@ async def update_config( # entries that delete_callback (lowercase lookup) cannot find. if config_info.litellm_settings is not None: existing = await _read_section("litellm_settings") + before_litellm_settings = copy.deepcopy(existing) updated_litellm_settings = dict(config_info.litellm_settings) incoming_cb = updated_litellm_settings.get("success_callback") @@ -13989,12 +14302,24 @@ async def update_config( merged["success_callback"] = list(set(incoming_cb)) await _upsert_section("litellm_settings", merged) + asyncio.create_task( + create_config_audit_log( + "litellm_settings", "updated", before_litellm_settings, merged, user_api_key_dict + ) + ) # router_settings: merge existing + request, request wins. if config_info.router_settings is not None: existing = await _read_section("router_settings") + before_router_settings = copy.deepcopy(existing) updates = config_info.router_settings.dict(exclude_none=True) - await _upsert_section("router_settings", {**existing, **updates}) + new_router_settings = {**existing, **updates} + await _upsert_section("router_settings", new_router_settings) + asyncio.create_task( + create_config_audit_log( + "router_settings", "updated", before_router_settings, new_router_settings, user_api_key_dict + ) + ) await proxy_config.add_deployment(prisma_client=prisma_client, proxy_logging_obj=proxy_logging_obj) @@ -14097,6 +14422,9 @@ async def update_config_general_settings( detail={"error": CommonProxyErrors.not_allowed_access.value}, ) + if data.field_name in _GENERAL_SETTINGS_UI_LITELLM_FIELDS: + return await _persist_general_settings_ui_litellm_field(data.field_name, data.field_value, user_api_key_dict) + if data.field_name not in ConfigGeneralSettings.model_fields: raise HTTPException( status_code=400, @@ -14126,6 +14454,8 @@ async def update_config_general_settings( else: general_settings = dict(db_general_settings.param_value) + before_general_settings = copy.deepcopy(general_settings) + ## update db field_value = data.field_value @@ -14145,27 +14475,19 @@ async def update_config_general_settings( }, ) await invalidate_config_param("general_settings") + asyncio.create_task( + create_config_audit_log( + "general_settings", "updated", before_general_settings, general_settings, user_api_key_dict + ) + ) if data.field_name == "plugins": register_plugins_from_config(general_settings) + _apply_ssrf_general_settings(general_settings) return response -# Secret-bearing general_settings fields the segment masker does not match by -# name: database_url and database_extra_connection_params embed DB credentials, -# pass_through_endpoints carry upstream Authorization headers, and -# alert_to_webhook_url is itself a webhook secret -_EXTRA_SECRET_GENERAL_SETTINGS_FIELDS = frozenset( - { - "database_url", - "database_extra_connection_params", - "pass_through_endpoints", - "alert_to_webhook_url", - } -) - - def _is_secret_general_setting_field(field_name: str) -> bool: return field_name in _EXTRA_SECRET_GENERAL_SETTINGS_FIELDS or SENSITIVE_DATA_MASKER.is_sensitive_key(field_name) @@ -14195,6 +14517,14 @@ def _redact_secret_values_in_obj(value: JsonValue, depth: int = 0) -> JsonValue: return value +def _redact_config_param_value_for_logging(param_name: Optional[str], param_value: JsonValue) -> JsonValue: + if param_name == "environment_variables" and isinstance(param_value, dict): + return {key: "REDACTED" for key in param_value} + if isinstance(param_value, (dict, list)): + return _redact_secret_values_in_obj(param_value) + return param_value + + def _redact_general_setting_value(field_name: str, value: JsonValue, is_full_admin: bool) -> JsonValue: if is_full_admin: return value @@ -14205,6 +14535,91 @@ def _redact_general_setting_value(field_name: str, value: JsonValue, is_full_adm return value +def _dump_redacted_config(value: Optional[JsonValue], *, redact_all_values: bool = False) -> Optional[str]: + # `default=str` matches the sibling audit-log serializers in + # team_endpoints.py and the LiteLLM_AuditLogs validator, so a YAML-loaded + # value with a non-JSON-native leaf (datetime, custom object) cannot turn + # an audit write into a 500. + if value is None: + return None + if redact_all_values and isinstance(value, dict): + return json.dumps({key: "REDACTED" for key in value}, default=str) + return json.dumps(_redact_secret_values_in_obj(value), default=str) + + +async def create_config_audit_log( + param_name: str, + action: AUDIT_ACTIONS, + before_value: Optional[JsonValue], + after_value: Optional[JsonValue], + user_api_key_dict: UserAPIKeyAuth, + table_name: LitellmTableNames = LitellmTableNames.CONFIG_TABLE_NAME, +) -> None: + """Record a system-wide settings change in LiteLLM_AuditLog. + + Secret leaves are redacted before the row is written. environment_variables + hold arbitrary credentials under non-secret-looking uppercase keys (e.g. + DATABASE_URL), so every value in that section is redacted rather than + relying on key-name matching; other sections reuse the same matcher + /config/field/info applies for non-admins. + """ + redact_all_values = param_name == "environment_variables" + await create_object_audit_log( + object_id=param_name, + action=action, + table_name=table_name, + before_value=_dump_redacted_config(before_value, redact_all_values=redact_all_values), + after_value=_dump_redacted_config(after_value, redact_all_values=redact_all_values), + user_api_key_dict=user_api_key_dict, + litellm_changed_by=None, + litellm_proxy_admin_name=LITELLM_PROXY_ADMIN_NAME, + ) + + +_EXTRA_SECRET_CALLBACK_ENV_VARS = frozenset( + { + "GALILEO_USERNAME", + "GENERIC_LOGGER_HEADERS", + "OTEL_HEADERS", + "SLACK_WEBHOOK_URL", + "SMTP_USERNAME", + } +) + + +def _redact_callback_env_vars(env_vars: dict[str, Optional[str]]) -> dict[str, Optional[str]]: + """Return a copy of ``env_vars`` with values for keys classified as + sensitive by ``is_sensitive_callback_key`` replaced with ``"REDACTED"``. + ``None`` values pass through unchanged. + """ + return { + key: ( + "REDACTED" + if value is not None and is_sensitive_callback_key(key, extra=_EXTRA_SECRET_CALLBACK_ENV_VARS) + else value + ) + for key, value in env_vars.items() + } + + +def _apply_callback_role_gate(entries: list, is_full_admin: bool) -> list: + if is_full_admin: + return entries + return [{**entry, "variables": _redact_callback_env_vars(entry.get("variables") or {})} for entry in entries] + + +def _apply_alerting_env_role_gate(env_vars: dict, is_full_admin: bool) -> dict: + if is_full_admin: + return mask_sensitive_keys(env_vars, _ALERTING_SENSITIVE_VARS) + return _redact_callback_env_vars(env_vars) + + +def _apply_webhook_role_gate(webhook_map, is_full_admin: bool): + if is_full_admin or not isinstance(webhook_map, dict): + return webhook_map + return {alert_type: "REDACTED" for alert_type in webhook_map} + + @router.get( "/config/field/info", tags=["config.yaml"], @@ -14275,6 +14690,55 @@ async def get_config_general_settings( ) +_GENERAL_SETTINGS_UI_LITELLM_FIELDS: dict[str, dict[str, str]] = { + "budget_exceeded_throttle_percentage": { + "type": "Float", + "description": ( + "Fraction (0, 1] of a key's configured TPM/RPM that an over-budget key with " + "'Throttle on budget exceeded' enabled keeps serving at. Leave empty to hard-block " + "over-budget keys." + ), + }, +} + + +def _validate_general_settings_ui_litellm_value(field_name: str, value: Any) -> Optional[float]: + if value is None or value == "": + return None + if isinstance(value, bool) or not isinstance(value, (int, float)) or not (0 < float(value) <= 1): + raise HTTPException( + status_code=400, + detail={"error": f"{field_name} must be a number in (0, 1] or empty"}, + ) + return float(value) + + +async def _persist_general_settings_ui_litellm_field( + field_name: str, value: Any, user_api_key_dict: UserAPIKeyAuth +) -> dict: + validated = _validate_general_settings_ui_litellm_value(field_name, value) + config = await proxy_config.get_config() + before_value = config.get("litellm_settings", {}).get(field_name) + setattr(litellm, field_name, validated) + if "litellm_settings" not in config: + config["litellm_settings"] = {} + config["litellm_settings"][field_name] = validated + await proxy_config.save_config(new_config=config) + asyncio.create_task(create_config_audit_log(field_name, "updated", before_value, validated, user_api_key_dict)) + return {"message": f"Field {field_name} updated", "status": "success"} + + +async def _reset_general_settings_ui_litellm_field(field_name: str, user_api_key_dict: UserAPIKeyAuth) -> dict: + config = await proxy_config.get_config() + before_value = config.get("litellm_settings", {}).get(field_name) + setattr(litellm, field_name, None) + if "litellm_settings" in config: + config["litellm_settings"].pop(field_name, None) + await proxy_config.save_config(new_config=config) + asyncio.create_task(create_config_audit_log(field_name, "deleted", before_value, None, user_api_key_dict)) + return {"message": f"Field {field_name} reset", "status": "success"} + + @router.get( "/config/list", tags=["config.yaml"], @@ -14428,6 +14892,35 @@ async def get_config_list( ) return_val.append(_response_obj) + db_litellm_settings_row = await ConfigRepository(prisma_client).table.find_first( + where={"param_name": "litellm_settings"} + ) + db_litellm_settings: dict = ( + dict(db_litellm_settings_row.param_value) + if db_litellm_settings_row is not None and db_litellm_settings_row.param_value is not None + else {} + ) + for litellm_field_name, spec in _GENERAL_SETTINGS_UI_LITELLM_FIELDS.items(): + current_value: Optional[float] = getattr(litellm, litellm_field_name, None) + stored_in_db_litellm: Optional[bool] + if litellm_field_name in db_litellm_settings: + stored_in_db_litellm = True + elif current_value is not None: + stored_in_db_litellm = False + else: + stored_in_db_litellm = None + return_val.append( + ConfigList( + field_name=litellm_field_name, + field_type=spec["type"], + field_description=spec["description"], + field_value=current_value, + stored_in_db=stored_in_db_litellm, + field_default_value=None, + nested_fields=None, + ) + ) + return return_val @@ -14468,6 +14961,9 @@ async def delete_config_general_settings( }, ) + if data.field_name in _GENERAL_SETTINGS_UI_LITELLM_FIELDS: + return await _reset_general_settings_ui_litellm_field(data.field_name, user_api_key_dict) + if data.field_name not in ConfigGeneralSettings.model_fields: raise HTTPException( status_code=400, @@ -14488,6 +14984,8 @@ async def delete_config_general_settings( else: general_settings = dict(db_general_settings.param_value) + before_general_settings = copy.deepcopy(general_settings) + ## update db general_settings.pop(data.field_name, None) @@ -14503,6 +15001,11 @@ async def delete_config_general_settings( }, ) await invalidate_config_param("general_settings") + asyncio.create_task( + create_config_audit_log( + "general_settings", "deleted", before_general_settings, general_settings, user_api_key_dict + ) + ) return response @@ -14560,6 +15063,8 @@ async def delete_callback( detail={"error": f"Callback '{callback_name}' not found in active configuration"}, ) + before_success_callbacks = list(success_callbacks) + # Remove callback from success_callback list success_callbacks.remove(callback_name) config.setdefault("litellm_settings", {})["success_callback"] = success_callbacks @@ -14567,6 +15072,16 @@ async def delete_callback( # Save the updated configuration await proxy_config.save_config(new_config=config) + asyncio.create_task( + create_config_audit_log( + "litellm_settings", + "deleted", + {"success_callback": before_success_callbacks}, + {"success_callback": success_callbacks}, + user_api_key_dict, + ) + ) + # Restart the proxy to apply changes await proxy_config.add_deployment(prisma_client=prisma_client, proxy_logging_obj=proxy_logging_obj) @@ -14596,7 +15111,9 @@ async def delete_callback( include_in_schema=False, dependencies=[Depends(user_api_key_auth)], ) -async def get_config(): +async def get_config( + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): """ For Admin UI - allows admin to view config via UI # return the callbacks and the env variables for the callback @@ -14611,6 +15128,8 @@ async def get_config(): _general_settings = config_data.get("general_settings", {}) environment_variables = config_data.get("environment_variables", {}) + is_full_admin = user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN + _success_callbacks = _litellm_settings.get("success_callback", []) _failure_callbacks = _litellm_settings.get("failure_callback", []) _success_and_failure_callbacks = _litellm_settings.get("callbacks", []) @@ -14652,6 +15171,8 @@ async def get_config(): for _callback in _success_and_failure_callbacks: _data_to_return.append(process_callback(_callback, "success_and_failure", environment_variables)) + _data_to_return = _apply_callback_role_gate(_data_to_return, is_full_admin) + # Check if slack alerting is on _alerting = _general_settings.get("alerting", []) alerting_data = [] @@ -14663,11 +15184,13 @@ async def get_config(): _var: (value if (value := environment_variables.get(_var)) is not None else os.getenv(_var)) for _var in _slack_vars } - _slack_env_vars = mask_sensitive_keys(_slack_env_vars, _ALERTING_SENSITIVE_VARS) + _slack_env_vars = _apply_alerting_env_role_gate(_slack_env_vars, is_full_admin) _alerting_types = proxy_logging_obj.slack_alerting_instance.alert_types _all_alert_types = proxy_logging_obj.slack_alerting_instance._all_possible_alert_types() - _alerts_to_webhook = proxy_logging_obj.slack_alerting_instance.alert_to_webhook_url + _alerts_to_webhook = _apply_webhook_role_gate( + proxy_logging_obj.slack_alerting_instance.alert_to_webhook_url, is_full_admin + ) alerting_data.append( { "name": "slack", @@ -14687,8 +15210,9 @@ async def get_config(): "EMAIL_LOGO_URL", "EMAIL_SUPPORT_CONTACT", ] - _email_env_vars = {_var: environment_variables.get(_var) for _var in _email_vars} - _email_env_vars = mask_sensitive_keys(_email_env_vars, _ALERTING_SENSITIVE_VARS) + _email_env_vars = _apply_alerting_env_role_gate( + {_var: environment_variables.get(_var) for _var in _email_vars}, is_full_admin + ) alerting_data.append( { @@ -15442,9 +15966,10 @@ app.include_router(search_router) app.include_router(image_router) app.include_router(fine_tuning_router) app.include_router(credential_router) +app.include_router(batches_router) +app.include_router(openai_files_router) app.include_router(llm_passthrough_router) app.include_router(pass_through_router) -app.include_router(batches_router) app.include_router(health_router) app.include_router(key_management_router) app.include_router(internal_user_router) @@ -15459,7 +15984,6 @@ app.include_router(callback_management_endpoints_router) app.include_router(debugging_endpoints_router) app.include_router(rust_control_plane_router) app.include_router(ui_crud_endpoints_router) -app.include_router(openai_files_router) app.include_router(team_callback_router) app.include_router(budget_management_router) app.include_router(model_management_router) @@ -15472,6 +15996,7 @@ app.include_router(cost_tracking_settings_router) app.include_router(router_settings_router) app.include_router(fallback_management_router) app.include_router(cache_settings_router) +app.include_router(coordination_redis_settings_router) app.include_router(user_agent_analytics_router) app.include_router(enterprise_router) app.include_router(ui_discovery_endpoints_router) diff --git a/litellm/proxy/route_llm_request.py b/litellm/proxy/route_llm_request.py index 63c6baf9ebc..8c980f33b01 100644 --- a/litellm/proxy/route_llm_request.py +++ b/litellm/proxy/route_llm_request.py @@ -435,9 +435,6 @@ async def route_request( "aget_run", "acancel_run", "adelete_run", - "acreate_realtime_client_secret", - "arealtime_calls", - "acreate_realtime_transcription_session", ]: # If a model is provided, get its credentials from the router model = data.get("model") diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index e21c0016491..fb4d8d0b5a3 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -279,6 +279,7 @@ model LiteLLM_ObjectPermissionTable { blocked_tools String[] @default([]) // Tool names blocked for any key/team/user with this permission mcp_toolsets String[] @default([]) // Toolset IDs granted to this key/team/user search_tools String[] @default([]) // search_tool_name values this key/team/user may call + mcp_tool_search_enabled Boolean? teams LiteLLM_TeamTable[] projects LiteLLM_ProjectTable[] verification_tokens LiteLLM_VerificationToken[] @@ -328,15 +329,23 @@ model LiteLLM_MCPServerTable { token_url String? registration_url String? oauth2_flow String? + token_exchange_endpoint String? + // Named for the RFC 8693 "audience" token-exchange request parameter (that flow only). + // RFC 8707 resource indicators are a separate concept, named "resource" in the v2 egress types. + audience String? + subject_token_type String? + token_exchange_profile String? allow_all_keys Boolean @default(false) available_on_public_internet Boolean @default(true) delegate_auth_to_upstream Boolean @default(false) oauth_passthrough Boolean @default(false) + dcr_bridge Boolean? is_byok Boolean @default(false) byok_description String[] @default([]) byok_api_key_help_url String? source_url String? timeout Float? + max_concurrent_requests Int? // BYOM submission lifecycle approval_status String? @default("active") submitted_by String? @@ -417,6 +426,7 @@ model LiteLLM_VerificationToken { access_group_ids String[] @default([]) model_spend Json @default("{}") model_max_budget Json @default("{}") + budget_fallbacks Json @default("{}") budget_id String? organization_id String? object_permission_id String? @@ -510,6 +520,7 @@ model LiteLLM_DeletedVerificationToken { access_group_ids String[] @default([]) model_spend Json @default("{}") model_max_budget Json @default("{}") + budget_fallbacks Json @default("{}") router_settings Json? @default("{}") budget_id String? organization_id String? diff --git a/litellm/proxy/spend_tracking/budget_reservation.py b/litellm/proxy/spend_tracking/budget_reservation.py index ca6c2e86789..38fd0d3f343 100644 --- a/litellm/proxy/spend_tracking/budget_reservation.py +++ b/litellm/proxy/spend_tracking/budget_reservation.py @@ -10,6 +10,7 @@ import litellm from litellm._logging import verbose_proxy_logger from litellm.caching import DualCache from litellm.litellm_core_utils.duration_parser import duration_in_seconds +from litellm.litellm_core_utils.llm_cost_calc.tiered_pricing import select_tier_for_input, tier_rate from litellm.proxy._types import ( LiteLLM_TeamMembership, LiteLLM_TeamTable, @@ -17,6 +18,7 @@ from litellm.proxy._types import ( UserAPIKeyAuth, ) from litellm.proxy.auth.auth_utils import get_model_from_request +from litellm.proxy.auth.budget_throttle import should_throttle_budget_exceeded from litellm.proxy.auth.route_checks import RouteChecks from litellm.proxy.utils import PrismaClient, ProxyLogging from litellm.router import Router @@ -54,6 +56,61 @@ def get_reserved_counter_keys(budget_reservation: Optional[dict]) -> set: } +def _key_reservation_should_release_for_throttle(counter_key: str, valid_token: Optional[UserAPIKeyAuth]) -> bool: + """ + Whether an over-budget key's own ``max_budget`` reservation should be + released rather than blocked, because the key opted into throttling: the + rate limiter slows it instead. Only the key's own ``max_budget`` counter is + exempt; team/user/window counters still enforce normally, and under-budget + requests never reach this branch so their concurrent-overspend protection is + untouched. + """ + if valid_token is None: + return False + return counter_key == f"spend:key:{valid_token.token}" and should_throttle_budget_exceeded(valid_token) + + +async def _apply_over_budget_reservation_policy( + counter: _BudgetCounter, + valid_token: Optional[UserAPIKeyAuth], + entry: dict[str, Any], + applied_entries: list[dict[str, Any]], + reservation_cost: float, + current_spend: float, +) -> float: + """ + Decide what to do when a counter is over budget, and return the reservation + cost to carry into the next counter. Three outcomes: an over-budget key that + opted into throttling releases its own reservation (the rate limiter slows + it) and keeps the cost; a partially-remaining budget resizes the reservation + down to what is left; anything else hard-blocks by raising. + """ + if _key_reservation_should_release_for_throttle(counter.counter_key, valid_token): + await _release_applied_entries_best_effort(entries=[entry], default_reserved_cost=reservation_cost) + applied_entries.remove(entry) + return reservation_cost + + remaining_before_reservation = counter.max_budget - (current_spend - reservation_cost) + if remaining_before_reservation > 1e-12: + await _resize_applied_reservation( + entries=applied_entries, + current_reserved_cost=reservation_cost, + new_reserved_cost=remaining_before_reservation, + ) + return remaining_before_reservation + + raise litellm.BudgetExceededError( + current_cost=current_spend, + max_budget=counter.max_budget, + message=( + "Budget has been exceeded! " + f"{counter.entity_type}={counter.entity_id} " + f"Current cost: {current_spend}, " + f"Max budget: {counter.max_budget}" + ), + ) + + async def reserve_budget_for_request( request_body: dict, route: str, @@ -130,25 +187,15 @@ async def reserve_budget_for_request( cached_spend = await _get_current_counter_value(counter=counter) current_spend = cached_spend + reservation_cost if current_spend > counter.max_budget: - remaining_before_reservation = counter.max_budget - (current_spend - reservation_cost) - if remaining_before_reservation > 1e-12: - await _resize_applied_reservation( - entries=applied_entries, - current_reserved_cost=reservation_cost, - new_reserved_cost=remaining_before_reservation, - ) - reservation_cost = remaining_before_reservation - continue - raise litellm.BudgetExceededError( - current_cost=current_spend, - max_budget=counter.max_budget, - message=( - "Budget has been exceeded! " - f"{counter.entity_type}={counter.entity_id} " - f"Current cost: {current_spend}, " - f"Max budget: {counter.max_budget}" - ), + reservation_cost = await _apply_over_budget_reservation_policy( + counter=counter, + valid_token=valid_token, + entry=entry, + applied_entries=applied_entries, + reservation_cost=reservation_cost, + current_spend=current_spend, ) + continue except Exception: await _release_applied_entries_best_effort( entries=applied_entries, @@ -878,12 +925,25 @@ def _estimate_request_input_cost_for_model( model: str, llm_router: Router | None, ) -> float | None: - model_info = _get_model_cost_info(model=model, llm_router=llm_router) - if model_info is None: - return None - input_cost_per_token = _to_float(model_info.get("input_cost_per_token")) - if input_cost_per_token is None: - return None + estimates = [ + _input_cost_for_cost_info( + request_body=request_body, + route=route, + model=model, + model_info=model_info, + ) + for model_info in _get_model_cost_infos(model=model, llm_router=llm_router) + ] + valid_estimates = [estimate for estimate in estimates if estimate is not None] + return max(valid_estimates) if valid_estimates else None + + +def _input_cost_for_cost_info( + request_body: dict, + route: str, + model: str, + model_info: Dict[str, Any], +) -> Optional[float]: input_tokens = _estimate_input_tokens( request_body=request_body, route=route, @@ -892,6 +952,14 @@ def _estimate_request_input_cost_for_model( ) if input_tokens is None: return None + tiered_pricing = model_info.get("tiered_pricing") + if isinstance(tiered_pricing, list) and tiered_pricing: + tier = select_tier_for_input(tiered_pricing=tiered_pricing, input_tokens=input_tokens) + if tier is not None: + return input_tokens * tier_rate(tier, "input_cost_per_token") + input_cost_per_token = _to_float(model_info.get("input_cost_per_token")) + if input_cost_per_token is None: + return None return input_tokens * input_cost_per_token @@ -901,10 +969,25 @@ def _estimate_request_max_cost_for_model( model: str, llm_router: Optional[Router], ) -> Optional[float]: - model_info = _get_model_cost_info(model=model, llm_router=llm_router) - if model_info is None: - return None + estimates = [ + _max_cost_for_cost_info( + request_body=request_body, + route=route, + model=model, + model_info=model_info, + ) + for model_info in _get_model_cost_infos(model=model, llm_router=llm_router) + ] + valid_estimates = [estimate for estimate in estimates if estimate is not None] + return max(valid_estimates) if valid_estimates else None + +def _max_cost_for_cost_info( + request_body: dict, + route: str, + model: str, + model_info: Dict[str, Any], +) -> Optional[float]: image_cost = _estimate_image_generation_cost( request_body=request_body, model_info=model_info, @@ -912,8 +995,6 @@ def _estimate_request_max_cost_for_model( if image_cost is not None: return image_cost - input_cost_per_token = _to_float(model_info.get("input_cost_per_token")) - output_cost_per_token = _to_float(model_info.get("output_cost_per_token")) input_tokens = _estimate_input_tokens( request_body=request_body, route=route, @@ -928,15 +1009,34 @@ def _estimate_request_max_cost_for_model( if input_tokens is None or output_tokens is None: return None + output_multiplier = _get_output_multiplier(request_body=request_body) + tiered_pricing = model_info.get("tiered_pricing") + if isinstance(tiered_pricing, list) and tiered_pricing: + tier = select_tier_for_input(tiered_pricing=tiered_pricing, input_tokens=input_tokens) + if tier is not None: + output_rate = max( + tier_rate(tier, "output_cost_per_token"), + tier_rate(tier, "output_cost_per_reasoning_token"), + ) + return (input_tokens * tier_rate(tier, "input_cost_per_token")) + ( + output_tokens * output_multiplier * output_rate + ) + + input_cost_per_token = _to_float(model_info.get("input_cost_per_token")) + output_cost_per_token = _to_float(model_info.get("output_cost_per_token")) + output_cost_per_reasoning_token = _to_float(model_info.get("output_cost_per_reasoning_token")) cost = 0.0 if input_cost_per_token is not None: cost += input_tokens * input_cost_per_token elif input_tokens > 0: return None - output_multiplier = _get_output_multiplier(request_body=request_body) - if output_cost_per_token is not None: - cost += output_tokens * output_multiplier * output_cost_per_token + # The reasoning-token share is unknown before the request runs, so reserve every + # output token at the higher of the standard and reasoning rates to avoid + # under-reserving reasoning-heavy requests. + output_rate = max(output_cost_per_token or 0.0, output_cost_per_reasoning_token or 0.0) + if output_cost_per_token is not None or output_cost_per_reasoning_token is not None: + cost += output_tokens * output_multiplier * output_rate elif output_tokens > 0: return None @@ -986,20 +1086,69 @@ def _get_model_cost_info( llm_router: Optional[Router], ) -> Optional[Dict[str, Any]]: if llm_router is not None: - try: - model_group_info = llm_router.get_model_group_info(model_group=model) - if model_group_info is not None: - return model_group_info.model_dump() - except Exception: - verbose_proxy_logger.debug( - "Unable to load router model group info for budget reservation", - exc_info=True, - ) + model_group_info = llm_router.get_model_group_info(model_group=model) + if model_group_info is not None: + return model_group_info.model_dump() + return dict(litellm.get_model_info(model=model)) + +def _get_model_cost_infos( + model: str, + llm_router: Optional[Router], +) -> List[Dict[str, Any]]: + """Cost-info candidates to estimate a request against for one model group. + + Reservation runs before routing, so the deployment that will serve the request + is unknown. Rather than guess, we estimate the cost against every eligible + pricing shape in the group (the group's flat rates plus each deployment's + tiered table) and let the caller reserve the maximum, so a cheaper sibling + deployment can never leave the request under-reserved. + """ try: - return dict(litellm.get_model_info(model=model)) + base = _get_model_cost_info(model=model, llm_router=llm_router) + if base is None: + return [] + tiered_tables = _get_deployment_tiered_pricing_tables(model=model, llm_router=llm_router) except Exception: + verbose_proxy_logger.debug( + "Unable to load model cost info for budget reservation", + exc_info=True, + ) + return [] + if not tiered_tables: + return [base] + return [base, *({**base, "tiered_pricing": table} for table in tiered_tables)] + + +def _deployment_tiered_pricing_table( + deployment: Dict[str, Any], + llm_router: Router, +) -> Optional[List[dict]]: + model_id = deployment.get("model_info", {}).get("id") + backend_model = deployment.get("litellm_params", {}).get("model") + if not isinstance(model_id, str) or not isinstance(backend_model, str): return None + deployment_model_info = llm_router.get_deployment_model_info(model_id=model_id, model_name=backend_model) + if deployment_model_info is None: + return None + tiered_pricing = deployment_model_info.get("tiered_pricing") + if isinstance(tiered_pricing, list) and tiered_pricing: + return tiered_pricing + return None + + +def _get_deployment_tiered_pricing_tables( + model: str, + llm_router: Optional[Router], +) -> List[List[dict]]: + if llm_router is None: + return [] + deployments = llm_router.get_model_list(model_name=model) or [] + return [ + table + for deployment in deployments + if (table := _deployment_tiered_pricing_table(deployment, llm_router)) is not None + ] def _estimate_input_tokens( diff --git a/litellm/proxy/spend_tracking/spend_management_endpoints.py b/litellm/proxy/spend_tracking/spend_management_endpoints.py index 5624afcfa5e..5a0b94d1524 100644 --- a/litellm/proxy/spend_tracking/spend_management_endpoints.py +++ b/litellm/proxy/spend_tracking/spend_management_endpoints.py @@ -27,6 +27,7 @@ from litellm.proxy.auth.user_api_key_auth import user_api_key_auth # module while common_utils may pull proxy_server during init, which can leave # those names undefined. Import the helpers locally where they are used. from litellm.proxy.spend_tracking.spend_tracking_utils import ( + get_spend_by_team, get_spend_by_team_and_customer, ) from litellm.proxy.utils import handle_exception_on_proxy @@ -44,6 +45,8 @@ else: router = APIRouter() +SPEND_LOGS_PAGINATION_COUNT_CAP = 10000 + @router.get( "/spend/keys", @@ -1131,68 +1134,7 @@ async def get_global_spend_report( start_date_obj, end_date_obj, team_id, customer_id, prisma_client ) if group_by == "team": - # first get data from spend logs -> SpendByModelApiKey - # then read data from "SpendByModelApiKey" to format the response obj - sql_query = """ - - WITH SpendByModelApiKey AS ( - SELECT - date_trunc('day', sl."startTime") AS group_by_day, - COALESCE(tt.team_alias, 'Unassigned Team') AS team_name, - sl.model, - sl.api_key, - SUM(sl.spend) AS model_api_spend, - SUM(sl.total_tokens) AS model_api_tokens - FROM - "LiteLLM_SpendLogs" sl - LEFT JOIN - "LiteLLM_TeamTable" tt - ON - sl.team_id = tt.team_id - WHERE - sl."startTime" >= ($1::timestamptz AT TIME ZONE 'UTC') - AND sl."startTime" < (($2::timestamptz + INTERVAL '1 day') AT TIME ZONE 'UTC') - GROUP BY - date_trunc('day', sl."startTime"), - tt.team_alias, - sl.model, - sl.api_key - ) - SELECT - group_by_day, - jsonb_agg(jsonb_build_object( - 'team_name', team_name, - 'total_spend', total_spend, - 'metadata', metadata - )) AS teams - FROM ( - SELECT - group_by_day, - team_name, - SUM(model_api_spend) AS total_spend, - jsonb_agg(jsonb_build_object( - 'model', model, - 'api_key', api_key, - 'spend', model_api_spend, - 'total_tokens', model_api_tokens - )) AS metadata - FROM - SpendByModelApiKey - GROUP BY - group_by_day, - team_name - ) AS aggregated - GROUP BY - group_by_day - ORDER BY - group_by_day; - """ - - db_response = await prisma_client.db.query_raw(sql_query, start_date_obj, end_date_obj) - if db_response is None: - return [] - - return db_response + return await get_spend_by_team(start_date_obj, end_date_obj, team_id, prisma_client) elif group_by == "customer": sql_query = """ @@ -1680,6 +1622,10 @@ async def ui_view_spend_logs( default=None, description="request_id to get spend logs for specific request_id", ), + session_id: str | None = fastapi.Query( + default=None, + description="Filter spend logs by session_id (partial string match)", + ), team_id: str | None = fastapi.Query( default=None, description="Filter spend logs by team_id", @@ -1964,6 +1910,12 @@ async def ui_view_spend_logs( p += 2 sql_conditions.append(or_clause) + if session_id is not None and isinstance(session_id, str): + like_escaped_session_id = session_id.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_") + sql_conditions.append(f"session_id LIKE ${p}") + sql_params.append(f"%{like_escaped_session_id}%") + p += 1 + # Status filter if status_filter is not None: if status_filter == "success": @@ -2018,6 +1970,20 @@ async def ui_view_spend_logs( else: _order_expr = order_column + count_query = f""" + SELECT COUNT(*) AS total_count + FROM ( + SELECT 1 + FROM "LiteLLM_SpendLogs" + WHERE {" AND ".join(sql_conditions)} + LIMIT ${p} + ) AS bounded_matches + """ + count_rows = await prisma_client.db.query_raw(count_query, *sql_params, SPEND_LOGS_PAGINATION_COUNT_CAP + 1) + raw_total = int(count_rows[0]["total_count"]) if count_rows else 0 + total_is_capped = raw_total > SPEND_LOGS_PAGINATION_COUNT_CAP + total_records = SPEND_LOGS_PAGINATION_COUNT_CAP if total_is_capped else raw_total + sql_query = f""" SELECT request_id, call_type, api_key, spend, total_tokens, @@ -2027,8 +1993,7 @@ async def ui_view_spend_logs( cache_hit, cache_key, request_tags, team_id, organization_id, end_user, requester_ip_address, session_id, status, mcp_namespaced_tool_name, agent_id, - COALESCE(request_duration_ms, (EXTRACT(EPOCH FROM ("endTime" - "startTime")) * 1000)::INTEGER) AS request_duration_ms, - COUNT(*) OVER () AS total_count + COALESCE(request_duration_ms, (EXTRACT(EPOCH FROM ("endTime" - "startTime")) * 1000)::INTEGER) AS request_duration_ms FROM "LiteLLM_SpendLogs" WHERE {" AND ".join(sql_conditions)} ORDER BY {_order_expr} {_sql_dir}{_nulls_clause} @@ -2038,34 +2003,13 @@ async def ui_view_spend_logs( data = await prisma_client.db.query_raw(sql_query, *sql_params) - # `COUNT(*) OVER ()` folds the total-match count into the same scan as the - # page data; a standalone `COUNT(*)` is a distributed RPC on sharded - # engines like YugabyteDB that contacts every tablet and times out - # regardless of row count (LIT-4027). The hot path (page 1 and in-range - # pages) always carries the count on its rows, so the count round trip is - # gone there. Only an out-of-range page overshoots the last row and comes - # back empty; fall back to a direct count there so total/total_pages stay - # accurate rather than collapsing to zero. - if data: - total_records = int(data[0]["total_count"]) - elif page > 1: - total_records = int( - await SpendLogsRepository(prisma_client).table.count( - where=where_conditions, - ) - ) - else: - total_records = 0 - # query_raw returns the JSONB `metadata` column as a string (the Prisma # serialiser bypasses the model-layer JSON hydration we get on the ORM # path). The UI reads `metadata.status` / `metadata.error_information` # as object fields, so failure rows looked like successes (#29674). - # Re-hydrate to dict here. Also drop the window-function `total_count` - # helper column so it does not leak into the serialised rows. + # Re-hydrate to dict here. for row in data: if isinstance(row, dict): - row.pop("total_count", None) md = row.get("metadata") if isinstance(md, str): try: @@ -2086,6 +2030,7 @@ async def ui_view_spend_logs( page_size, total_pages, enrich_session_counts=not is_v2, + total_is_capped=total_is_capped, ) except Exception as e: verbose_proxy_logger.exception(f"Error in ui_view_spend_logs: {e}") @@ -3394,6 +3339,7 @@ async def _build_ui_spend_logs_response( page_size: int, total_pages: int, enrich_session_counts: bool = True, + total_is_capped: bool = False, ) -> dict: """ Build the paginated response for the UI spend-logs endpoint. @@ -3418,10 +3364,12 @@ async def _build_ui_spend_logs_response( total_pages: Total number of pages. enrich_session_counts: Whether to add ``session_total_count`` to each row. Defaults to ``True``. + total_is_capped: Whether ``total_records`` was clamped to the + pagination count cap (there are more matching rows than the cap). Returns: A dict with ``data`` (enriched rows), ``total``, ``page``, - ``page_size``, and ``total_pages``. + ``page_size``, ``total_pages``, and ``total_is_capped``. """ count_map: dict[str, int] = {} if enrich_session_counts: @@ -3444,12 +3392,66 @@ async def _build_ui_spend_logs_response( ) count_map = {r["session_id"]: r["_count"]["session_id"] for r in counts if r.get("session_id")} + session_spend_map: dict[str, dict[str, Union[int, float]]] = {} + if enrich_session_counts and session_ids: + from prisma.errors import PrismaError + + try: + # Collect api_keys already present in the authorized page rows so the + # aggregate is scoped to the same ownership as the main query — prevents + # cross-tenant disclosure via a colliding session_id. + authorized_api_keys = list( + { + (row.get("api_key") if isinstance(row, dict) else getattr(row, "api_key", None)) + for row in data + if (row.get("api_key") if isinstance(row, dict) else getattr(row, "api_key", None)) + } + ) + rows = await prisma_client.db.query_raw( + """ + SELECT session_id, + COALESCE(SUM(spend), 0)::double precision AS session_total_spend, + COUNT(*) FILTER ( + WHERE call_type IN ('call_mcp_tool', 'list_mcp_tools') + )::int AS mcp_tool_call_count, + COALESCE(SUM(spend) FILTER ( + WHERE call_type IN ('call_mcp_tool', 'list_mcp_tools') + ), 0)::double precision AS mcp_tool_call_spend + FROM "LiteLLM_SpendLogs" + WHERE session_id = ANY($1::text[]) + AND api_key = ANY($2::text[]) + GROUP BY session_id + """, + session_ids, + authorized_api_keys, + ) + session_spend_map = { + row["session_id"]: { + "session_total_spend": float(row.get("session_total_spend") or 0.0), + "mcp_tool_call_count": int(row.get("mcp_tool_call_count") or 0), + "mcp_tool_call_spend": float(row.get("mcp_tool_call_spend") or 0.0), + } + for row in rows + if row.get("session_id") + } + except PrismaError: + verbose_proxy_logger.debug( + "Failed to enrich session spend aggregates for spend logs UI", + exc_info=True, + ) + if enrich_session_counts: enriched: List[dict] = [] for row in data: row_dict = dict(row) if isinstance(row, dict) else row.model_dump() sid = row_dict.get("session_id") row_dict["session_total_count"] = count_map.get(sid, 1) if sid else 1 + session_stats = session_spend_map.get(sid) if sid else None + if session_stats: + row_dict["session_total_spend"] = session_stats["session_total_spend"] + if session_stats["mcp_tool_call_count"]: + row_dict["mcp_tool_call_count"] = session_stats["mcp_tool_call_count"] + row_dict["mcp_tool_call_spend"] = session_stats["mcp_tool_call_spend"] enriched.append(row_dict) response_data: list = enriched else: @@ -3464,6 +3466,7 @@ async def _build_ui_spend_logs_response( "page": page, "page_size": page_size, "total_pages": total_pages, + "total_is_capped": total_is_capped, } diff --git a/litellm/proxy/spend_tracking/spend_tracking_utils.py b/litellm/proxy/spend_tracking/spend_tracking_utils.py index 22f97feecd9..b38d5e39800 100644 --- a/litellm/proxy/spend_tracking/spend_tracking_utils.py +++ b/litellm/proxy/spend_tracking/spend_tracking_utils.py @@ -55,6 +55,13 @@ def _get_max_string_length_prompt_in_db() -> int: return DEFAULT_MAX_STRING_LENGTH_PROMPT_IN_DB +def _hash_api_key_for_spend_log(api_key: str) -> str: + stripped = api_key[7:] if api_key[:7].lower() == "bearer " else api_key + if stripped.startswith("sk-"): + return hash_token(stripped) + return stripped + + def _is_master_key(api_key: Optional[str], _master_key: Optional[str]) -> bool: """ Raw-only constant-time master-key comparison. The hashed form is never @@ -120,13 +127,16 @@ def _get_spend_logs_metadata( key: metadata.get(key) for key in SpendLogsMetadata.__annotations__.keys() } ) + raw_user_api_key = clean_metadata.get("user_api_key") + if raw_user_api_key is not None and isinstance(raw_user_api_key, str): + clean_metadata["user_api_key"] = _hash_api_key_for_spend_log(raw_user_api_key) clean_metadata["applied_guardrails"] = applied_guardrails clean_metadata["batch_models"] = batch_models clean_metadata["mcp_tool_call_metadata"] = mcp_tool_call_metadata clean_metadata["vector_store_request_metadata"] = _get_vector_store_request_for_spend_logs_payload( vector_store_request_metadata ) - clean_metadata["guardrail_information"] = guardrail_information + clean_metadata["guardrail_information"] = _sanitize_guardrail_information_for_spend_logs(guardrail_information) clean_metadata["usage_object"] = usage_object clean_metadata["model_map_information"] = model_map_information clean_metadata["cold_storage_object_key"] = cold_storage_object_key @@ -281,9 +291,7 @@ def get_logging_payload(kwargs, response_obj, start_time, end_time) -> SpendLogs standard_logging_completion_tokens = standard_logging_payload.get("completion_tokens", 0) standard_logging_total_tokens = standard_logging_payload.get("total_tokens", 0) if api_key is not None and isinstance(api_key, str): - if api_key.startswith("sk-"): - # hash the api_key - api_key = hash_token(api_key) + api_key = _hash_api_key_for_spend_log(api_key) if ( standard_logging_payload is not None @@ -485,6 +493,74 @@ def _ensure_datetime_utc(timestamp: datetime) -> datetime: return timestamp +async def get_spend_by_team( + start_date: dt, + end_date: dt, + team_id: Optional[str], + prisma_client: PrismaClient, +): + sql_query = """ + WITH SpendByModelApiKey AS ( + SELECT + date_trunc('day', sl."startTime") AS group_by_day, + COALESCE(tt.team_alias, 'Unassigned Team') AS team_name, + sl.model, + sl.api_key, + SUM(sl.spend) AS model_api_spend, + SUM(sl.total_tokens) AS model_api_tokens + FROM + "LiteLLM_SpendLogs" sl + LEFT JOIN + "LiteLLM_TeamTable" tt + ON + sl.team_id = tt.team_id + WHERE + sl."startTime" >= ($1::timestamptz AT TIME ZONE 'UTC') + AND sl."startTime" < (($2::timestamptz + INTERVAL '1 day') AT TIME ZONE 'UTC') + AND ($3::text IS NULL OR sl.team_id = $3) + GROUP BY + date_trunc('day', sl."startTime"), + tt.team_alias, + sl.model, + sl.api_key + ) + SELECT + group_by_day, + jsonb_agg(jsonb_build_object( + 'team_name', team_name, + 'total_spend', total_spend, + 'metadata', metadata + )) AS teams + FROM ( + SELECT + group_by_day, + team_name, + SUM(model_api_spend) AS total_spend, + jsonb_agg(jsonb_build_object( + 'model', model, + 'api_key', api_key, + 'spend', model_api_spend, + 'total_tokens', model_api_tokens + )) AS metadata + FROM + SpendByModelApiKey + GROUP BY + group_by_day, + team_name + ) AS aggregated + GROUP BY + group_by_day + ORDER BY + group_by_day; + """ + + db_response = await prisma_client.db.query_raw(sql_query, start_date, end_date, team_id) + if db_response is None: + return [] + + return db_response + + async def get_spend_by_team_and_customer( start_date: dt, end_date: dt, @@ -792,6 +868,51 @@ def _redact_prompt_leaks_in_error_string(text: str) -> str: return "".join(out) +def _sanitize_guardrail_information_for_spend_logs( + guardrail_information: Optional[List[StandardLoggingGuardrailInformation]], +) -> Optional[List[StandardLoggingGuardrailInformation]]: + """ + When ``store_prompts_in_spend_logs`` is False, redact prompt-carrying fields + (``guardrail_request``, ``guardrail_response``, ``match_details``, + ``classification``) before they land in ``LiteLLM_SpendLogs.metadata``. + + Guardrail hooks may echo the LLM request payload back into + ``guardrail_response``, and two first-party hooks + (``block_code_execution``, ``litellm_content_filter``) inline user-prompt + substrings into ``match_details`` / ``classification`` too, so the flag + must cover all four fields. Every other typed field on the entry (name, + provider, mode, status, timings, action, violation_categories, risk_score, + masked_entity_count, ...) is preserved so guardrail dashboards keep + working. + + ``guardrail_information`` is typed ``Optional[List[...]]`` but at least + one writer (``xecguard``) assigns a bare dict, so normalize to a list + here to match OTEL's defensive read pattern; otherwise iteration would + yield the dict's keys and crash the whole spend-log write. + """ + if guardrail_information is None or _should_store_prompts_and_responses_in_spend_logs(): + return guardrail_information + entries = [guardrail_information] if isinstance(guardrail_information, dict) else guardrail_information + return [_redact_prompt_fields_in_guardrail_entry(entry) for entry in entries if isinstance(entry, dict)] + + +_PROMPT_CARRYING_GUARDRAIL_FIELDS = ( + "guardrail_request", + "guardrail_response", + "match_details", + "classification", +) + + +def _redact_prompt_fields_in_guardrail_entry( + entry: StandardLoggingGuardrailInformation, +) -> StandardLoggingGuardrailInformation: + return { + **entry, + **{key: REDACTED_BY_LITELM_STRING for key in _PROMPT_CARRYING_GUARDRAIL_FIELDS if key in entry}, + } + + def _sanitize_error_information_for_spend_logs( error_information: Optional[StandardLoggingPayloadErrorInformation], ) -> Optional[StandardLoggingPayloadErrorInformation]: diff --git a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py index e4f68a1e9db..a8926d26047 100644 --- a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py +++ b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py @@ -1,4 +1,5 @@ #### CRUD ENDPOINTS for UI Settings ##### +import asyncio import json from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union from urllib.parse import urlparse @@ -176,6 +177,11 @@ class UISettings(BaseModel): description="If true, org admins cannot generate API keys via /key/generate.", ) + enable_chat_ui: bool = Field( + default=False, + description="If true, shows the Chat page in the UI sidebar, letting users chat with an LLM and connect their own MCP server credentials via OAuth.", + ) + class UISettingsResponse(SettingsResponse): """Response model for UI settings""" @@ -199,6 +205,7 @@ ALLOWED_UI_SETTINGS_FIELDS = { "scope_user_search_to_org", "disable_custom_api_keys", "disable_key_generate_for_org_admin", + "enable_chat_ui", } # Flags that must be synced from the persisted UISettings into @@ -322,8 +329,12 @@ async def get_allowed_ips(): tags=["Budget & Spend Tracking"], dependencies=[Depends(user_api_key_auth)], ) -async def add_allowed_ip(ip_address: IPAddress): +async def add_allowed_ip( + ip_address: IPAddress, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): from litellm.proxy.proxy_server import ( + create_config_audit_log, general_settings, prisma_client, proxy_config, @@ -355,11 +366,22 @@ async def add_allowed_ip(ip_address: IPAddress): if "allowed_ips" not in config["general_settings"]: config["general_settings"]["allowed_ips"] = [] + before_allowed_ips = list(config["general_settings"]["allowed_ips"]) if ip_address.ip not in config["general_settings"]["allowed_ips"]: config["general_settings"]["allowed_ips"].append(ip_address.ip) await proxy_config.save_config(new_config=config) + asyncio.create_task( + create_config_audit_log( + param_name="general_settings", + action="updated", + before_value={"allowed_ips": before_allowed_ips}, + after_value={"allowed_ips": config["general_settings"]["allowed_ips"]}, + user_api_key_dict=user_api_key_dict, + ) + ) + return { "message": f"IP {ip_address.ip} address added successfully", "status": "success", @@ -371,8 +393,15 @@ async def add_allowed_ip(ip_address: IPAddress): tags=["Budget & Spend Tracking"], dependencies=[Depends(user_api_key_auth)], ) -async def delete_allowed_ip(ip_address: IPAddress): - from litellm.proxy.proxy_server import general_settings, proxy_config +async def delete_allowed_ip( + ip_address: IPAddress, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + from litellm.proxy.proxy_server import ( + create_config_audit_log, + general_settings, + proxy_config, + ) _allowed_ips: List = general_settings.get("allowed_ips", []) if ip_address.ip in _allowed_ips: @@ -390,11 +419,22 @@ async def delete_allowed_ip(ip_address: IPAddress): if "allowed_ips" not in config["general_settings"]: config["general_settings"]["allowed_ips"] = [] + before_allowed_ips = list(config["general_settings"]["allowed_ips"]) if ip_address.ip in config["general_settings"]["allowed_ips"]: config["general_settings"]["allowed_ips"].remove(ip_address.ip) await proxy_config.save_config(new_config=config) + asyncio.create_task( + create_config_audit_log( + param_name="general_settings", + action="deleted", + before_value={"allowed_ips": before_allowed_ips}, + after_value={"allowed_ips": config["general_settings"]["allowed_ips"]}, + user_api_key_dict=user_api_key_dict, + ) + ) + return {"message": f"IP {ip_address.ip} deleted successfully", "status": "success"} @@ -553,6 +593,7 @@ async def _update_litellm_setting( settings: Union[DefaultInternalUserParams, DefaultTeamSSOParams, MCPSemanticFilterSettings], settings_key: str, success_message: str, + user_api_key_dict: UserAPIKeyAuth, ): """ Common utility function to update `litellm_settings` in both memory and config. @@ -561,8 +602,13 @@ async def _update_litellm_setting( settings: The settings object to update settings_key: The key in litellm_settings to update success_message: Message to return on success + user_api_key_dict: The acting admin, recorded as the audit-log actor. """ - from litellm.proxy.proxy_server import proxy_config, store_model_in_db + from litellm.proxy.proxy_server import ( + create_config_audit_log, + proxy_config, + store_model_in_db, + ) if store_model_in_db is not True: raise HTTPException( @@ -576,6 +622,7 @@ async def _update_litellm_setting( # because get_config() may overwrite litellm. with stale DB values # via LITELLM_SETTINGS_SAFE_DB_OVERRIDES. config = await proxy_config.get_config() + before_value = config.get("litellm_settings", {}).get(settings_key) # Update the in-memory settings (after get_config to avoid stale override) setattr(litellm, settings_key, in_memory_var) @@ -589,6 +636,20 @@ async def _update_litellm_setting( # Save the updated config await proxy_config.save_config(new_config=config) + # Fire-and-forget so an audit-log failure (transient DB blip, etc.) + # never surfaces as a 500 after save_config has already committed, + # matching the create_object_audit_log pattern used elsewhere + # (e.g. model_management_endpoints). + asyncio.create_task( + create_config_audit_log( + param_name=settings_key, + action="updated", + before_value=before_value, + after_value=in_memory_var, + user_api_key_dict=user_api_key_dict, + ) + ) + return { "message": success_message, "status": "success", @@ -619,6 +680,7 @@ async def update_internal_user_settings( settings=settings, settings_key="default_internal_user_params", success_message="Internal user settings updated successfully", + user_api_key_dict=user_api_key_dict, ) @@ -627,7 +689,10 @@ async def update_internal_user_settings( tags=["SSO Settings"], dependencies=[Depends(user_api_key_auth)], ) -async def update_default_team_settings(settings: DefaultTeamSSOParams): +async def update_default_team_settings( + settings: DefaultTeamSSOParams, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): """ Update the default team parameters for SSO users. These settings will be applied to new teams created from SSO. @@ -636,6 +701,7 @@ async def update_default_team_settings(settings: DefaultTeamSSOParams): settings=settings, settings_key="default_team_params", success_message="Default team settings updated successfully", + user_api_key_dict=user_api_key_dict, ) @@ -746,7 +812,10 @@ async def get_sso_settings(): tags=["SSO Settings"], dependencies=[Depends(user_api_key_auth)], ) -async def update_sso_settings(sso_config: SSOConfig): +async def update_sso_settings( + sso_config: SSOConfig, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): """ Update SSO configuration by saving to the dedicated SSO table. """ @@ -754,6 +823,7 @@ async def update_sso_settings(sso_config: SSOConfig): import os from litellm.proxy.proxy_server import ( + create_config_audit_log, prisma_client, proxy_config, store_model_in_db, @@ -786,6 +856,20 @@ async def update_sso_settings(sso_config: SSOConfig): "proxy_base_url": "PROXY_BASE_URL", } + # Read the existing SSO row first so the audit log captures a real + # before/after diff. Stored values are encrypted; decrypt them so the + # before-snapshot has the same shape as after_value, and rely on + # create_config_audit_log's secret-name redaction to mask the + # *_client_secret fields before the audit row is written. + existing_sso_record = await SSOConfigRepository(prisma_client).table.find_unique(where={"id": "sso_config"}) + before_sso_data: Optional[Dict[str, Any]] = None + if existing_sso_record and existing_sso_record.sso_settings: + stored = existing_sso_record.sso_settings + if isinstance(stored, str): + stored = json.loads(stored) + if isinstance(stored, dict): + before_sso_data = proxy_config._decrypt_db_variables(stored) + # Load existing config config = await proxy_config.get_config() @@ -824,6 +908,17 @@ async def update_sso_settings(sso_config: SSOConfig): }, ) + asyncio.create_task( + create_config_audit_log( + param_name="sso_config", + action="updated", + before_value=before_sso_data, + after_value=sso_data, + user_api_key_dict=user_api_key_dict, + table_name=LitellmTableNames.SSO_CONFIG_TABLE_NAME, + ) + ) + # Remove SSO-related env vars from config.environment_variables try: env_var_entry = await ConfigRepository(prisma_client).table.find_unique( @@ -917,14 +1012,21 @@ def _validate_public_image_url(value: Optional[str], field_name: str) -> None: tags=["UI Theme Settings"], dependencies=[Depends(user_api_key_auth)], ) -async def update_ui_theme_settings(theme_config: UIThemeConfig): +async def update_ui_theme_settings( + theme_config: UIThemeConfig, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): """ Update UI theme configuration. Updates logo settings for the admin UI. """ import os - from litellm.proxy.proxy_server import proxy_config, store_model_in_db + from litellm.proxy.proxy_server import ( + create_config_audit_log, + proxy_config, + store_model_in_db, + ) _validate_public_image_url(theme_config.logo_url, "logo_url") _validate_public_image_url(theme_config.favicon_url, "favicon_url") @@ -937,6 +1039,7 @@ async def update_ui_theme_settings(theme_config: UIThemeConfig): # Load existing config config = await proxy_config.get_config() + before_theme = config.get("litellm_settings", {}).get("ui_theme_config") # Update config with UI theme settings if "general_settings" not in config: @@ -1003,6 +1106,16 @@ async def update_ui_theme_settings(theme_config: UIThemeConfig): # Save the updated config await proxy_config.save_config(new_config=stored_config) + asyncio.create_task( + create_config_audit_log( + param_name="ui_theme_config", + action="updated", + before_value=before_theme, + after_value=theme_data, + user_api_key_dict=user_api_key_dict, + ) + ) + return { "message": "UI theme settings updated successfully.", "status": "success", @@ -1053,10 +1166,17 @@ async def update_mcp_semantic_filter_settings( Update MCP semantic filter settings in database. Settings will be picked up by all pods within approximately 10 seconds via background polling. """ + if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN: + raise HTTPException( + status_code=403, + detail="Only proxy admins can update MCP semantic filter settings.", + ) + result = await _update_litellm_setting( settings=settings, settings_key="mcp_semantic_tool_filter", success_message="MCP Semantic Filter settings updated successfully. Changes will be applied across all pods within 10 seconds.", + user_api_key_dict=user_api_key_dict, ) try: from litellm.proxy.proxy_server import prisma_client, proxy_config @@ -1174,7 +1294,11 @@ async def update_ui_settings( Update UI-specific configuration flags. Only proxy admins are allowed to modify these settings. """ - from litellm.proxy.proxy_server import prisma_client, store_model_in_db + from litellm.proxy.proxy_server import ( + create_config_audit_log, + prisma_client, + store_model_in_db, + ) if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN: raise HTTPException(status_code=403, detail="Only proxy admins can update UI settings.") @@ -1256,6 +1380,17 @@ async def update_ui_settings( sanitized = {k: v for k, v in ui_settings.items() if k in ALLOWED_UI_SETTINGS_FIELDS} await user_api_key_cache.async_set_cache(key=UI_SETTINGS_CACHE_KEY, value=sanitized, ttl=UI_SETTINGS_CACHE_TTL) + asyncio.create_task( + create_config_audit_log( + param_name="ui_settings", + action="updated", + before_value=existing, + after_value=ui_settings, + user_api_key_dict=user_api_key_dict, + table_name=LitellmTableNames.UI_SETTINGS_TABLE_NAME, + ) + ) + return { "message": "UI settings updated successfully", "status": "success", diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 154a17bc4db..d7649b524aa 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -23,7 +23,9 @@ from typing import ( Dict, List, Literal, + Mapping, Optional, + Sequence, Tuple, Union, cast, @@ -4095,11 +4097,22 @@ class PrismaClient: raise e def _get_engine_pid(self) -> int: + """Get the PID of the writer's engine subprocess, or 0 if unavailable. + + Must never raise: prisma's ``_engine`` property raises + ``ClientNotConnectedError`` on a disconnected client, and an exception + escaping from the reconnect path would leave it unable to recover. + """ try: - engine = self.db._original_prisma._engine # type: ignore[attr-defined] + prisma_obj = self.writer_db._original_prisma + if prisma_obj.is_connected() is not True: + return 0 + engine = prisma_obj._engine process = getattr(engine, "process", None) if engine is not None else None if process is not None: - return process.pid + pid = process.pid + if isinstance(pid, int): + return pid except (AttributeError, TypeError): pass return 0 @@ -4525,6 +4538,8 @@ class PrismaClient: "Writer healthy on probe; skipping recreate (engine " "likely already replaced by a token refresh)." ) + if isinstance(self.db, RoutingPrismaWrapper): + self.db.mark_writer_recovered() await self._start_engine_watcher() return except Exception as probe_err: @@ -4717,6 +4732,11 @@ class PrismaClient: self.db.query_raw("SELECT 1"), timeout=self._db_health_watchdog_probe_timeout_seconds, ) + if isinstance(self.db, RoutingPrismaWrapper) and self.db.writer_unavailable: + await self.attempt_db_reconnect( + reason="db_health_watchdog_writer_unavailable", + timeout_seconds=self._db_watchdog_reconnect_timeout_seconds, + ) except asyncio.CancelledError: break except Exception as e: @@ -5194,8 +5214,10 @@ class ProxyUpdateSpend: for j in range(0, len(logs_to_process), BATCH_SIZE): batch = logs_to_process[j : j + BATCH_SIZE] batch_with_dates = [prisma_client.jsonify_object({**entry}) for entry in batch] - await SpendLogsRepository(prisma_client).table.create_many( - data=batch_with_dates, skip_duplicates=True + await _create_spend_logs_with_poison_isolation( + SpendLogsRepository(prisma_client), + batch_with_dates, + MAX_SPEND_LOG_ISOLATION_ATTEMPTS_PER_BATCH, ) verbose_proxy_logger.debug(f"Flushed {len(batch)} logs to the DB.") # Explicitly clear batch memory @@ -5462,6 +5484,65 @@ async def _monitor_spend_logs_queue( await asyncio.sleep(current_interval) +MAX_SPEND_LOG_ISOLATION_ATTEMPTS_PER_BATCH = 256 + + +async def _create_spend_logs_with_poison_isolation( + repo: SpendLogsRepository, + rows: Sequence[Mapping[str, object]], + attempts_left: int, +) -> int: + """Write spend-log rows, isolating any row Postgres rejects on its data. + + ``create_many`` writes the whole batch in a single statement, so one row + carrying bytes Postgres refuses (a residual NUL byte is the canonical case) + fails the entire insert and drops every good row alongside it. On a genuine + data-layer rejection the batch is bisected so the good rows still persist + and only the offending row is dropped and logged. Transport failures, + including the "can't reach database server" outage that prisma mislabels as + a ``DataError``, are re-raised unchanged so the caller's connection-retry + path still runs. + + ``attempts_left`` is a hard ceiling on the number of ``create_many`` calls + the isolation may issue for this batch, so an authenticated caller flooding + poisoned rows cannot amplify one failed bulk insert into unbounded failed + inserts and log lines. It is checked before any insert (so an exhausted + budget never even attempts a write), decremented once per ``create_many`` + call, and threaded through the recursion so the whole bisection shares one + allowance; total inserts are therefore bounded by the initial value + regardless of how many rows are poisoned. When it runs out the still-failing + remainder is dropped wholesale (the pre-existing drop-the-batch behavior) + under one log line. Returns the budget left after this subtree. + """ + if attempts_left <= 0: + spend_log_error( + "Spend tracking - dropping %d spend log rows without per-row isolation; " + "isolation attempt budget exhausted for this flush", + len(rows), + ) + return 0 + try: + await repo.table.create_many(data=rows, skip_duplicates=True) + return attempts_left - 1 + except Exception as e: + if not PrismaDBExceptionHandler.is_prisma_data_error(e): + raise + if PrismaDBExceptionHandler.is_database_service_unavailable_error(e): + raise + if len(rows) == 1: + request_id = rows[0].get("request_id") + spend_log_error( + "Spend tracking - dropping spend log row Postgres rejected. request_id=%s error=%s", + request_id, + str(e), + exc=e, + ) + return attempts_left - 1 + mid = len(rows) // 2 + remaining = await _create_spend_logs_with_poison_isolation(repo, rows[:mid], attempts_left - 1) + return await _create_spend_logs_with_poison_isolation(repo, rows[mid:], remaining) + + def _raise_failed_update_spend_exception(e: Exception, start_time: float, proxy_logging_obj: ProxyLogging): """ Raise an exception for failed update spend logs diff --git a/litellm/realtime_api/main.py b/litellm/realtime_api/main.py index a6fec4729ad..5ecf4d91ff6 100644 --- a/litellm/realtime_api/main.py +++ b/litellm/realtime_api/main.py @@ -40,6 +40,12 @@ vertex_llm_base = VertexBase() base_llm_http_handler = BaseLLMHTTPHandler() +def _with_resolved_session_model(session: dict[str, Any], model_name: str) -> dict[str, Any]: + if "model" not in session: + return session + return {**session, "model": model_name} + + def _build_litellm_metadata(kwargs: dict) -> dict: """Build the litellm_metadata dict for guardrail checking (internal only, not forwarded to provider).""" metadata: dict = {**(kwargs.get("litellm_metadata") or {})} @@ -134,6 +140,8 @@ async def acreate_realtime_client_secret( custom_llm_provider=custom_llm_provider, ) request_data = req.model_dump(exclude_none=True, exclude={"model"}) + if isinstance(request_data.get("session"), dict): + request_data["session"] = _with_resolved_session_model(request_data["session"], model_name) return await base_llm_http_handler.async_realtime_client_secret_handler( api_base=resolved_api_base, api_key=resolved_api_key, @@ -249,6 +257,8 @@ async def arealtime_calls( dynamic_api_key=dynamic_api_key, litellm_params=litellm_params, ) + if session is not None: + session = _with_resolved_session_model(session, model_name) litellm_logging_obj.update_from_kwargs( kwargs=kwargs, model=model_name, @@ -505,6 +515,7 @@ async def _realtime_health_check( api_base: Optional[str] = None, api_version: Optional[str] = None, realtime_protocol: Optional[str] = None, + model_params: Optional[dict] = None, ): """ Health check for realtime API - tries connection to the realtime API websocket @@ -540,14 +551,17 @@ async def _realtime_health_check( elif custom_llm_provider == "xai": url = xai_realtime._construct_url(api_base=api_base or "https://api.x.ai/v1", query_params={"model": model}) elif custom_llm_provider == "vertex_ai": - vertex_location = litellm.vertex_location or get_secret_str("VERTEXAI_LOCATION") - resolved_location = vertex_llm_base.get_vertex_region(vertex_region=vertex_location, model=model) + vertex_model_params = model_params or {} + resolved_location = vertex_llm_base.get_vertex_region( + vertex_region=VertexBase.safe_get_vertex_ai_location(vertex_model_params), + model=model, + ) ( access_token, resolved_project, ) = await vertex_llm_base._ensure_access_token_async( - credentials=None, - project_id=litellm.vertex_project or get_secret_str("VERTEXAI_PROJECT"), + credentials=VertexBase.safe_get_vertex_ai_credentials(vertex_model_params), + project_id=VertexBase.safe_get_vertex_ai_project(vertex_model_params), custom_llm_provider="vertex_ai", ) vertex_realtime_config = VertexAIRealtimeConfig( diff --git a/litellm/rerank_api/main.py b/litellm/rerank_api/main.py index 9320c7fae8a..b6ebf5589b7 100644 --- a/litellm/rerank_api/main.py +++ b/litellm/rerank_api/main.py @@ -158,7 +158,7 @@ def rerank( instruction=instruction, non_default_params=kwargs, ) - verbose_logger.info(f"optional_rerank_params: {optional_rerank_params}") + verbose_logger.debug(f"optional_rerank_params: {optional_rerank_params}") if isinstance(optional_params.timeout, str): optional_params.timeout = float(optional_params.timeout) diff --git a/litellm/responses/litellm_completion_transformation/custom_tools.py b/litellm/responses/litellm_completion_transformation/custom_tools.py new file mode 100644 index 00000000000..2417bf5cf2e --- /dev/null +++ b/litellm/responses/litellm_completion_transformation/custom_tools.py @@ -0,0 +1,163 @@ +""" +Utilities for handling OpenAI Responses API 'custom' tools (freeform/grammar tools) +when bridging to Chat Completions providers. + +Custom tools are defined with ``type: "custom"`` and a grammar/format specification. +Since most Chat Completions providers only support standard ``function`` tools, +the bridge converts them to ``function`` tools with a single ``content`` string +parameter. When the model responds with a ``function_call`` for such a tool, this +module converts it back to the ``custom_tool_call`` format expected by clients like +Codex CLI. + +The forward direction (custom -> function) and reverse direction (function_call -> +custom_tool_call) are both handled here so future custom tool types can be added by +extending this module without touching the streaming iterator or transformation +logic. +""" + +import json +from collections.abc import Mapping +from typing import Any + +from pydantic import BaseModel, TypeAdapter, ValidationError + +from litellm.types.llms.openai import ( + ChatCompletionToolParam, + ChatCompletionToolParamFunctionChunk, +) + +_MAX_ARGUMENTS_LEN = 1_000_000 + + +def extract_custom_tool_names(tools: list[Any] | None) -> set[str]: + """Extract names of tools originally defined as ``type: "custom"``.""" + if not tools: + return set() + names: set[str] = set() + for tool in tools: + if isinstance(tool, dict) and tool.get("type") == "custom" and "name" in tool: + names.add(tool["name"]) + return names + + +def is_custom_tool_call(tool_name: str, custom_tool_names: set[str]) -> bool: + """Check if a tool call name corresponds to a custom tool.""" + return tool_name in custom_tool_names + + +def unwrap_custom_tool_arguments(arguments: str) -> str: + """Extract the raw content string from JSON-wrapped arguments. + + The bridge converts custom tools to function tools with schema + ``{"properties": {"content": {"type": "string"}}}``, so the model returns + arguments like ``{"content": "*** Begin Patch\\n..."}``. This function + extracts just the content string. If the arguments are not valid JSON or do + not contain a ``content`` key, the original string is returned unchanged. + """ + if not arguments: + return "" + if len(arguments) > _MAX_ARGUMENTS_LEN: + return arguments + try: + parsed = json.loads(arguments) + if isinstance(parsed, dict) and "content" in parsed: + return str(parsed["content"]) + except (json.JSONDecodeError, TypeError, ValueError): + pass + return arguments + + +def build_tool_call_item_kwargs( + call_id: str, + name: str, + arguments_or_input: str, + status: str, + custom_tool_names: set[str], +) -> dict[str, Any]: + """Build kwargs for an output item dict that is either a ``function_call`` + or a ``custom_tool_call`` depending on whether *name* is in + *custom_tool_names*. + + For custom tools the ``arguments`` JSON is unwrapped into the ``input`` + field. For regular function tools the raw ``arguments`` string is kept. + + This centralises the branching logic so the streaming iterator and the + non-streaming transformation share a single code path. + """ + custom = is_custom_tool_call(name, custom_tool_names) + item_type = "custom_tool_call" if custom else "function_call" + kwargs: dict[str, Any] = { + "type": item_type, + "id": call_id, + "call_id": call_id, + "name": name, + "status": status, + } + if custom: + if status == "completed": + kwargs["input"] = unwrap_custom_tool_arguments(arguments_or_input) + else: + kwargs["input"] = "" + else: + kwargs["arguments"] = arguments_or_input + return kwargs + + +class _CustomToolFormat(BaseModel): + syntax: str = "" + definition: str = "" + + +_ALLOWED_CALLERS_ADAPTER = TypeAdapter(list[str] | None) + + +def _validated_allowed_callers(value: object) -> list[str] | None: + try: + return _ALLOWED_CALLERS_ADAPTER.validate_python(value, strict=True) + except ValidationError as exc: + raise ValueError("allowed_callers must be a list of strings") from exc + + +def _grammar_suffix(fmt: object) -> str: + try: + parsed = _CustomToolFormat.model_validate(fmt) + except ValidationError: + return "" + if not parsed.definition: + return "" + return f"\n\nFormat:\n```{parsed.syntax}\n{parsed.definition}\n```" + + +def convert_custom_tool_to_function_tool(tool: Mapping[str, object]) -> ChatCompletionToolParam | None: + """Convert a Responses API ``custom`` tool to a Chat Completions ``function`` + tool. + + The grammar definition is embedded in the description so the model can + produce correctly-formatted output. Returns ``None`` if the tool is not a + custom tool. Raises ``ValueError`` if ``allowed_callers`` is not a list of + strings. + """ + if tool.get("type") != "custom": + return None + raw_name = tool.get("name") + name = raw_name if isinstance(raw_name, str) else "" + raw_description = tool.get("description") + description = (raw_description if isinstance(raw_description, str) else "") + _grammar_suffix(tool.get("format")) + allowed_callers = _validated_allowed_callers(tool.get("allowed_callers")) + function_chunk = ChatCompletionToolParamFunctionChunk( + name=name, + description=description, + parameters={ + "type": "object", + "properties": { + "content": { + "type": "string", + "description": f"The {name} content following the specified format", + } + }, + "required": ["content"], + }, + ) + if allowed_callers is None: + return ChatCompletionToolParam(type="function", function=function_chunk) + return ChatCompletionToolParam(type="function", function=function_chunk, allowed_callers=allowed_callers) diff --git a/litellm/responses/litellm_completion_transformation/streaming_iterator.py b/litellm/responses/litellm_completion_transformation/streaming_iterator.py index b1198780bac..cf69654d15d 100644 --- a/litellm/responses/litellm_completion_transformation/streaming_iterator.py +++ b/litellm/responses/litellm_completion_transformation/streaming_iterator.py @@ -1,9 +1,13 @@ import time import uuid -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, cast import litellm from litellm.main import stream_chunk_builder +from litellm.responses.litellm_completion_transformation.custom_tools import ( + build_tool_call_item_kwargs, + extract_custom_tool_names, +) from litellm.responses.litellm_completion_transformation.transformation import ( LiteLLMCompletionResponsesConfig, ) @@ -53,20 +57,20 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): self, model: str, litellm_custom_stream_wrapper: litellm.CustomStreamWrapper, - request_input: Union[str, ResponseInputParam], + request_input: str | ResponseInputParam, responses_api_request: ResponsesAPIOptionalRequestParams, - custom_llm_provider: Optional[str] = None, - litellm_metadata: Optional[dict] = None, + custom_llm_provider: str | None = None, + litellm_metadata: dict | None = None, ): self.model: str = model self.litellm_custom_stream_wrapper: litellm.CustomStreamWrapper = litellm_custom_stream_wrapper - self.request_input: Union[str, ResponseInputParam] = request_input + self.request_input: str | ResponseInputParam = request_input self.responses_api_request: ResponsesAPIOptionalRequestParams = responses_api_request - self.custom_llm_provider: Optional[str] = custom_llm_provider - self.litellm_metadata: Optional[dict] = litellm_metadata or {} + self.custom_llm_provider: str | None = custom_llm_provider + self.litellm_metadata: dict | None = litellm_metadata or {} # Store lightweight dict snapshots for stream_chunk_builder to reduce # repeated Pydantic attribute access in end-of-stream assembly. - self.collected_chat_completion_chunks: List[Dict[str, Any]] = [] + self.collected_chat_completion_chunks: list[dict[str, Any]] = [] self.finished: bool = False self.litellm_logging_obj = litellm_custom_stream_wrapper.logging_obj self.sent_response_created_event: bool = False @@ -77,11 +81,11 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): self.sent_output_content_part_done_event: bool = False self.sent_output_item_done_event: bool = False self.sent_annotation_events: bool = False - self.litellm_model_response: Optional[Union[ModelResponse, TextCompletionResponse]] = None + self.litellm_model_response: ModelResponse | TextCompletionResponse | None = None self.final_text: str = "" - self._cached_item_id: Optional[str] = None - self._cached_response_id: Optional[str] = None - self._pending_tool_events: List[BaseLiteLLMOpenAIResponseObject] = [] + self._cached_item_id: str | None = None + self._cached_response_id: str | None = None + self._pending_tool_events: list[BaseLiteLLMOpenAIResponseObject] = [] self._tool_output_index_by_call_id: dict[str, int] = {} self._tool_args_by_call_id: dict[str, str] = {} self._tool_call_id_by_index: dict[int, str] = {} @@ -89,17 +93,18 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): self._next_tool_output_index: int = 1 # output_index=0 reserved for the message item self._final_tool_events_queued: bool = False self._sequence_number: int = 0 - self._cached_reasoning_item_id: Optional[str] = None + self._cached_reasoning_item_id: str | None = None self._sent_reasoning_summary_text_done_event: bool = False self._sent_reasoning_summary_part_done_event: bool = False self._reasoning_summary_text: str = "" # -- GENERIC RESPONSE-EVENTS PENDING QUEUE as required by fix -- - self._pending_response_events: List[BaseLiteLLMOpenAIResponseObject] = [] + self._pending_response_events: list[BaseLiteLLMOpenAIResponseObject] = [] self._reasoning_active = False self._reasoning_done_emitted = False - self._reasoning_item_id: Optional[str] = None - self._accumulated_reasoning_content_parts: List[str] = [] - self._accumulated_provider_specific_fields: Dict[str, Any] = {} + self._reasoning_item_id: str | None = None + self._accumulated_reasoning_content_parts: list[str] = [] + self._accumulated_provider_specific_fields: dict[str, Any] = {} + self._custom_tool_names: set[str] = extract_custom_tool_names(self.responses_api_request.get("tools")) def _get_or_assign_tool_output_index(self, call_id: str) -> int: existing = self._tool_output_index_by_call_id.get(call_id) @@ -110,7 +115,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): self._tool_output_index_by_call_id[call_id] = idx return idx - def _normalize_tool_call_index(self, tool_call: object) -> Optional[int]: + def _normalize_tool_call_index(self, tool_call: object) -> int | None: idx_raw = tool_call.get("index") if isinstance(tool_call, dict) else getattr(tool_call, "index", None) if idx_raw is None: return None @@ -183,19 +188,11 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): if call_id not in self._tool_args_by_call_id: self._tool_args_by_call_id[call_id] = "" self._sequence_number += 1 + item_kwargs = build_tool_call_item_kwargs(call_id, fn_name, "", "in_progress", self._custom_tool_names) event = OutputItemAddedEvent( type=ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED, output_index=output_index, - item=BaseLiteLLMOpenAIResponseObject( - **{ - "type": "function_call", - "id": call_id, - "call_id": call_id, - "name": fn_name, - "arguments": "", - "status": "in_progress", - } - ), + item=BaseLiteLLMOpenAIResponseObject(**item_kwargs), ) event.__dict__["sequence_number"] = self._sequence_number self._pending_tool_events.append(event) @@ -260,19 +257,11 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): if is_new_tool_call: self._tool_args_by_call_id[call_id] = "" self._sequence_number += 1 + item_kwargs = build_tool_call_item_kwargs(call_id, fn_name, "", "in_progress", self._custom_tool_names) event = OutputItemAddedEvent( type=ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED, output_index=output_index, - item=BaseLiteLLMOpenAIResponseObject( - **{ - "type": "function_call", - "id": call_id, - "call_id": call_id, - "name": fn_name, - "arguments": "", - "status": "in_progress", - } - ), + item=BaseLiteLLMOpenAIResponseObject(**item_kwargs), ) event.__dict__["sequence_number"] = self._sequence_number self._pending_tool_events.append(event) @@ -310,20 +299,14 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): self._pending_tool_events.append(done_event) self._sequence_number += 1 + item_kwargs = build_tool_call_item_kwargs( + call_id, fn_name, final_args, "completed", self._custom_tool_names + ) item_done_event = OutputItemDoneEvent( type=ResponsesAPIStreamEvents.OUTPUT_ITEM_DONE, output_index=output_index, sequence_number=self._sequence_number, - item=BaseLiteLLMOpenAIResponseObject( - **{ - "type": "function_call", - "id": call_id, - "call_id": call_id, - "name": fn_name, - "arguments": final_args, - "status": "completed", - } - ), + item=BaseLiteLLMOpenAIResponseObject(**item_kwargs), ) self._pending_tool_events.append(item_done_event) @@ -449,9 +432,9 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): for key, val in src.items(): self._accumulated_provider_specific_fields[key] = val - def create_litellm_model_response(self) -> Optional[ModelResponse]: + def create_litellm_model_response(self) -> ModelResponse | None: response = cast( - Optional[ModelResponse], + ModelResponse | None, stream_chunk_builder( chunks=self.collected_chat_completion_chunks, logging_obj=self.litellm_logging_obj, @@ -468,7 +451,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): @staticmethod def _snapshot_chunk_for_stream_chunk_builder( chunk: ModelResponseStream, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Convert a streaming chunk into a plain dict for end-of-stream assembly. Keep _hidden_params so downstream usage/header behavior is preserved. @@ -564,7 +547,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): reasoning_content = getattr(litellm_complete_object.choices[0].message, "reasoning_content", "") or "" # type: ignore annotations = getattr(litellm_complete_object.choices[0].message, "annotations", None) # type: ignore - part: Optional[PART_UNION_TYPES] = None + part: PART_UNION_TYPES | None = None if reasoning_content: part = ContentPartDonePartReasoningText( type="reasoning_text", @@ -671,7 +654,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): def return_default_done_events( self, litellm_complete_object: ModelResponse - ) -> Optional[BaseLiteLLMOpenAIResponseObject]: + ) -> BaseLiteLLMOpenAIResponseObject | None: if self.sent_output_text_done_event is False: self.sent_output_text_done_event = True return self.create_output_text_done_event(litellm_complete_object) @@ -685,7 +668,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): def return_default_initial_events( self, - ) -> Optional[BaseLiteLLMOpenAIResponseObject]: + ) -> BaseLiteLLMOpenAIResponseObject | None: if self.sent_response_created_event is False: self.sent_response_created_event = True return self.create_response_created_event() @@ -725,6 +708,9 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): self.finished = self.is_stream_finished() response_completed_event = self._emit_response_completed_event(self.litellm_model_response) if response_completed_event: + # Latch so wrappers (FallbackResponsesStreamWrapper) + proxy + # container-ownership hook can read completed_response. + self.completed_response = response_completed_event return response_completed_event else: if sync_mode: @@ -800,11 +786,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): async def __anext__( self, - ) -> Union[ - ResponsesAPIStreamingResponse, - ResponseCompletedEvent, - BaseLiteLLMOpenAIResponseObject, - ]: + ) -> ResponsesAPIStreamingResponse | ResponseCompletedEvent | BaseLiteLLMOpenAIResponseObject: try: while True: if self.finished is True: @@ -906,11 +888,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): def __next__( self, - ) -> Union[ - ResponsesAPIStreamingResponse, - ResponseCompletedEvent, - BaseLiteLLMOpenAIResponseObject, - ]: + ) -> ResponsesAPIStreamingResponse | ResponseCompletedEvent | BaseLiteLLMOpenAIResponseObject: try: while True: if self.finished is True: @@ -961,7 +939,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): def _transform_chat_completion_chunk_to_response_api_chunk( self, chunk: ModelResponseStream - ) -> Optional[ResponsesAPIStreamingResponse]: + ) -> ResponsesAPIStreamingResponse | None: """ Transform a chat completion chunk to a response API chunk. @@ -1047,7 +1025,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): return None - def _get_delta_string_from_streaming_choices(self, choices: List[StreamingChoices]) -> str: + def _get_delta_string_from_streaming_choices(self, choices: list[StreamingChoices]) -> str: """ Get the delta string from the streaming choices @@ -1059,7 +1037,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): chat_completion_delta: ChatCompletionDelta = choice.delta return chat_completion_delta.content or "" - def _emit_response_completed_event(self, litellm_model_response: ModelResponse) -> Optional[ResponseCompletedEvent]: + def _emit_response_completed_event(self, litellm_model_response: ModelResponse) -> ResponseCompletedEvent | None: if litellm_model_response: # Add cost to usage object if include_cost_in_streaming_usage is True if litellm.include_cost_in_streaming_usage and self.litellm_logging_obj is not None: diff --git a/litellm/responses/litellm_completion_transformation/transformation.py b/litellm/responses/litellm_completion_transformation/transformation.py index 866698b1f96..6b1ca3564e3 100644 --- a/litellm/responses/litellm_completion_transformation/transformation.py +++ b/litellm/responses/litellm_completion_transformation/transformation.py @@ -2,15 +2,17 @@ Handles transforming from Responses API -> LiteLLM completion (Chat Completion API) """ +import json import re from collections.abc import Sequence -from typing import Any, Dict, List, Literal, Optional, Set, Tuple, Union, cast +from typing import Any, Literal, cast from openai.types.responses import ResponseFunctionToolCall from openai.types.responses.response_create_params import ResponseInputParam from openai.types.responses.tool_param import FunctionToolParam from typing_extensions import TypedDict +from litellm._logging import verbose_logger from litellm.caching import InMemoryCache from litellm.litellm_core_utils.get_supported_openai_params import ( get_supported_openai_params, @@ -45,6 +47,7 @@ from litellm.types.llms.openai import ( ValidChatCompletionMessageContentTypesLiteral, ) from litellm.types.responses.main import ( + CustomToolCallOutputItem, GenericResponseOutputItem, GenericResponseOutputItemContentAnnotation, OutputCodeInterpreterCall, @@ -62,21 +65,26 @@ from litellm.types.utils import ( Usage, ) +from .custom_tools import ( + convert_custom_tool_to_function_tool, + extract_custom_tool_names, + is_custom_tool_call, + unwrap_custom_tool_arguments, +) + ########### Initialize Classes used for Responses API ########### TOOL_CALLS_CACHE = InMemoryCache() class ChatCompletionSession(TypedDict, total=False): - messages: List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ChatCompletionResponseMessage, - Message, - ] + messages: list[ + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionMessageToolCall + | ChatCompletionResponseMessage + | Message ] - litellm_session_id: Optional[str] + litellm_session_id: str | None ########### End of Initialize Classes used for Responses API ########### @@ -109,7 +117,7 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_tool_choice( tool_choice: Any, - ) -> Optional[Union[str, Dict[str, Any]]]: + ) -> str | dict[str, Any] | None: """ Transform tool_choice from various formats to OpenAI Chat Completion format. @@ -159,7 +167,7 @@ class LiteLLMCompletionResponsesConfig: return tool_choice @staticmethod - def _should_drop_derived_web_search_options(model: str, custom_llm_provider: Optional[str]) -> bool: + def _should_drop_derived_web_search_options(model: str, custom_llm_provider: str | None) -> bool: """ A Responses ``web_search`` built-in tool is derived into a ``web_search_options`` param. When the resolved provider/model does not support it (e.g. Bedrock Anthropic, where only @@ -169,7 +177,7 @@ class LiteLLMCompletionResponsesConfig: Support is read from each provider's own ``get_supported_openai_params`` so this bridge stays provider-agnostic; an unmapped provider (``None``) is treated as "keep". """ - supported_params: Optional[List[str]] = get_supported_openai_params( + supported_params: list[str] | None = get_supported_openai_params( model=model, custom_llm_provider=custom_llm_provider ) return supported_params is not None and "web_search_options" not in supported_params @@ -177,11 +185,11 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def transform_responses_api_request_to_chat_completion_request( model: str, - input: Union[str, ResponseInputParam], + input: str | ResponseInputParam, responses_api_request: ResponsesAPIOptionalRequestParams, - custom_llm_provider: Optional[str] = None, - stream: Optional[bool] = None, - extra_headers: Optional[Dict[str, Any]] = None, + custom_llm_provider: str | None = None, + stream: bool | None = None, + extra_headers: dict[str, Any] | None = None, **kwargs, ) -> dict: """ @@ -205,7 +213,7 @@ class LiteLLMCompletionResponsesConfig: response_format = LiteLLMCompletionResponsesConfig._transform_text_format_to_response_format(text_param) # Extract reasoning_effort from reasoning parameter - reasoning_effort: Optional[Union[Reasoning, str]] = None + reasoning_effort: Reasoning | str | None = None reasoning_param = responses_api_request.get("reasoning") if reasoning_param: if isinstance(reasoning_param, dict): @@ -255,7 +263,7 @@ class LiteLLMCompletionResponsesConfig: "include_usage": True, } litellm_completion_request["stream_options"] = stream_options - litellm_logging_obj: Optional[LiteLLMLoggingObj] = kwargs.get("litellm_logging_obj") + litellm_logging_obj: LiteLLMLoggingObj | None = kwargs.get("litellm_logging_obj") if litellm_logging_obj: litellm_logging_obj.stream_options = stream_options @@ -265,28 +273,24 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def transform_responses_api_input_to_messages( - input: Union[str, ResponseInputParam], - responses_api_request: Union[ResponsesAPIOptionalRequestParams, dict], - ) -> List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ChatCompletionResponseMessage, - Message, - ] + input: str | ResponseInputParam, + responses_api_request: ResponsesAPIOptionalRequestParams | dict, + ) -> list[ + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionMessageToolCall + | ChatCompletionResponseMessage + | Message ]: """ Transform a Responses API input into a list of messages """ - messages: List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ChatCompletionResponseMessage, - Message, - ] + messages: list[ + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionMessageToolCall + | ChatCompletionResponseMessage + | Message ] = [] if responses_api_request.get("instructions"): messages.append( @@ -373,31 +377,24 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_response_input_param_to_chat_completion_message( - input: Union[str, ResponseInputParam], - ) -> List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ChatCompletionResponseMessage, - ] + input: str | ResponseInputParam, + ) -> list[ + AllMessageValues | GenericChatCompletionMessage | ChatCompletionMessageToolCall | ChatCompletionResponseMessage ]: """ Transform a ResponseInputParam into a Chat Completion message """ - messages: List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ChatCompletionResponseMessage, - ] + messages: list[ + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionMessageToolCall + | ChatCompletionResponseMessage ] = [] if isinstance(input, str): messages.append(ChatCompletionUserMessage(role="user", content=input)) elif isinstance(input, list): - existing_tool_call_ids: Set[str] = set() + existing_tool_call_ids: set[str] = set() for _input in input: chat_completion_messages = ( LiteLLMCompletionResponsesConfig._transform_responses_api_input_item_to_chat_completion_message( @@ -449,7 +446,7 @@ class LiteLLMCompletionResponsesConfig: if not chat_completion_messages: continue - deduped_in_place: List[Any] = [] + deduped_in_place: list[Any] = [] for m in chat_completion_messages: role = "" if isinstance(m, dict): @@ -491,36 +488,27 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _deduplicate_tool_call_output_messages( - tool_call_output_messages: List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ChatCompletionResponseMessage, - ] + tool_call_output_messages: list[ + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionMessageToolCall + | ChatCompletionResponseMessage ], - existing_tool_call_ids: Set[str], - ) -> List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ChatCompletionResponseMessage, - ] + existing_tool_call_ids: set[str], + ) -> list[ + AllMessageValues | GenericChatCompletionMessage | ChatCompletionMessageToolCall | ChatCompletionResponseMessage ]: """Return tool call outputs after dropping assistant entries with duplicate call_ids.""" if not tool_call_output_messages: return [] - filtered_messages: List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ChatCompletionResponseMessage, - ] + filtered_messages: list[ + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionMessageToolCall + | ChatCompletionResponseMessage ] = [] - seen_tool_call_ids: Set[str] = set(existing_tool_call_ids) + seen_tool_call_ids: set[str] = set(existing_tool_call_ids) for tool_call_message in tool_call_output_messages: if isinstance(tool_call_message, dict): @@ -563,7 +551,7 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _ensure_tool_call_output_has_corresponding_tool_call( - messages: List[Union[AllMessageValues, GenericChatCompletionMessage]], + messages: list[AllMessageValues | GenericChatCompletionMessage], ) -> bool: """ If any tool call output is present, ensure there is a corresponding tool call/tool_use block @@ -574,7 +562,7 @@ class LiteLLMCompletionResponsesConfig: return False @staticmethod - def _find_previous_assistant_idx(messages: List[Any], current_idx: int) -> Optional[int]: + def _find_previous_assistant_idx(messages: list[Any], current_idx: int) -> int | None: """Find the index of the previous assistant message.""" for j in range(current_idx - 1, -1, -1): if messages[j].get("role") == "assistant": @@ -600,7 +588,7 @@ class LiteLLMCompletionResponsesConfig: return "" @staticmethod - def _get_tool_calls_list(assistant_message: Any) -> List[Any]: + def _get_tool_calls_list(assistant_message: Any) -> list[Any]: """Extract tool_calls as a list from assistant message.""" tool_calls_raw = ( assistant_message.get("tool_calls") @@ -616,10 +604,10 @@ class LiteLLMCompletionResponsesConfig: return [] @staticmethod - def _check_tool_call_exists(tool_calls: List[Any], tool_call_id: str) -> bool: + def _check_tool_call_exists(tool_calls: list[Any], tool_call_id: str) -> bool: """Check if a tool_call with the given ID exists in the list.""" for tool_call in tool_calls: - tool_call_id_to_check: Optional[str] = None + tool_call_id_to_check: str | None = None if isinstance(tool_call, dict): tool_call_id_to_check = tool_call.get("id") elif hasattr(tool_call, "id"): @@ -629,7 +617,7 @@ class LiteLLMCompletionResponsesConfig: return False @staticmethod - def _reconstruct_tool_call_from_tools(tool_call_id: str, tools: List[Any]) -> Optional[Dict[str, Any]]: + def _reconstruct_tool_call_from_tools(tool_call_id: str, tools: list[Any]) -> dict[str, Any] | None: """Reconstruct a minimal tool_call definition from tools list.""" for tool in tools: if isinstance(tool, dict): @@ -668,13 +656,13 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _create_tool_call_chunk( - tool_use_definition: Dict[str, Any], tool_call_id: str, index: int + tool_use_definition: dict[str, Any], tool_call_id: str, index: int ) -> ChatCompletionToolCallChunk: """Create a ChatCompletionToolCallChunk from tool_use_definition.""" function_raw = LiteLLMCompletionResponsesConfig._get_mapping_or_attr_value(tool_use_definition, "function") function_name_raw = LiteLLMCompletionResponsesConfig._get_mapping_or_attr_value(function_raw, "name") function_arguments_raw = LiteLLMCompletionResponsesConfig._get_mapping_or_attr_value(function_raw, "arguments") - function: Dict[str, Any] = { + function: dict[str, Any] = { "name": function_name_raw or "", "arguments": function_arguments_raw or "{}", } @@ -693,7 +681,7 @@ class LiteLLMCompletionResponsesConfig: ) @staticmethod - def _normalize_tool_use_definition(tool_use_definition: Any, tool_call_id: str) -> Optional[Dict[str, Any]]: + def _normalize_tool_use_definition(tool_use_definition: Any, tool_call_id: str) -> dict[str, Any] | None: """ Normalize cached tool_call definitions to a dict-like shape consumed by _create_tool_call_chunk. """ @@ -701,7 +689,7 @@ class LiteLLMCompletionResponsesConfig: return None if isinstance(tool_use_definition, dict): - normalized_definition: Dict[str, Any] = dict(tool_use_definition) + normalized_definition: dict[str, Any] = dict(tool_use_definition) else: tool_use_id_raw = LiteLLMCompletionResponsesConfig._get_mapping_or_attr_value(tool_use_definition, "id") tool_use_type_raw = LiteLLMCompletionResponsesConfig._get_mapping_or_attr_value(tool_use_definition, "type") @@ -737,7 +725,7 @@ class LiteLLMCompletionResponsesConfig: def _add_tool_call_to_assistant(assistant_message: Any, tool_call_chunk: ChatCompletionToolCallChunk) -> None: """Add a tool_call to an assistant message.""" if isinstance(assistant_message, dict): - prev_assistant_dict = cast(Dict[str, Any], assistant_message) + prev_assistant_dict = cast(dict[str, Any], assistant_message) if "tool_calls" not in prev_assistant_dict: prev_assistant_dict["tool_calls"] = [] tool_calls_list = prev_assistant_dict["tool_calls"] @@ -752,23 +740,19 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _ensure_tool_results_have_corresponding_tool_calls( messages: Sequence[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionResponseMessage, - ChatCompletionMessageToolCall, - Message, - ] + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionResponseMessage + | ChatCompletionMessageToolCall + | Message ], - tools: Optional[List[Any]] = None, - ) -> List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionResponseMessage, - ChatCompletionMessageToolCall, - Message, - ] + tools: list[Any] | None = None, + ) -> list[ + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionResponseMessage + | ChatCompletionMessageToolCall + | Message ]: """ Ensure that tool_result messages have corresponding tool_calls in the previous assistant message. @@ -789,14 +773,12 @@ class LiteLLMCompletionResponsesConfig: # Create a deep copy to avoid modifying the original (use list() so we can mutate and return List) import copy - fixed_messages: List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionResponseMessage, - ChatCompletionMessageToolCall, - Message, - ] + fixed_messages: list[ + AllMessageValues + | GenericChatCompletionMessage + | ChatCompletionResponseMessage + | ChatCompletionMessageToolCall + | Message ] = list(copy.deepcopy(messages)) messages_to_remove = [] @@ -828,7 +810,7 @@ class LiteLLMCompletionResponsesConfig: # Type-safe way to set tool_call_id on tool message if isinstance(message, dict): # Cast to dict to allow setting tool_call_id - message_dict = cast(Dict[str, Any], message) + message_dict = cast(dict[str, Any], message) message_dict["tool_call_id"] = tool_call_id elif hasattr(message, "tool_call_id"): setattr(message, "tool_call_id", tool_call_id) @@ -881,13 +863,7 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_responses_api_input_item_to_chat_completion_message( input_item: Any, - ) -> List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionResponseMessage, - ] - ]: + ) -> list[AllMessageValues | GenericChatCompletionMessage | ChatCompletionResponseMessage]: """ Transform a Responses API input item into a Chat Completion message @@ -937,6 +913,7 @@ class LiteLLMCompletionResponsesConfig: """ return input_item.get("type") in [ "function_call_output", + "custom_tool_call_output", "web_search_call", "computer_call_output", "tool_result", # Anthropic/MCP format @@ -945,20 +922,16 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _is_input_item_function_call(input_item: Any) -> bool: """ - Check if the input item is a function call + Check if the input item is a function call or custom tool call. + Both need to be reconstructed as assistant tool_calls for Chat + Completions providers. """ - return input_item.get("type") == "function_call" + return input_item.get("type") in ("function_call", "custom_tool_call") @staticmethod def _transform_responses_api_tool_call_output_to_chat_completion_message( - tool_call_output: Dict[str, Any], - ) -> List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionResponseMessage, - ] - ]: + tool_call_output: dict[str, Any], + ) -> list[AllMessageValues | GenericChatCompletionMessage | ChatCompletionResponseMessage]: """ ChatCompletionToolMessage is used to indicate the output from a tool call """ @@ -992,8 +965,8 @@ class LiteLLMCompletionResponsesConfig: # Some adapters represent tool output as a list of "input_*" parts if isinstance(output, list): - normalized_blocks: List[Dict[str, Any]] = [] - text_acc: List[str] = [] + normalized_blocks: list[dict[str, Any]] = [] + text_acc: list[str] = [] for part in output: if not isinstance(part, dict): continue @@ -1093,14 +1066,8 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_responses_api_function_call_to_chat_completion_message( - function_call: Dict[str, Any], - ) -> List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionResponseMessage, - ] - ]: + function_call: dict[str, Any], + ) -> list[AllMessageValues | GenericChatCompletionMessage | ChatCompletionResponseMessage]: """ Transform a Responses API function_call into a Chat Completion message with tool calls @@ -1117,13 +1084,19 @@ class LiteLLMCompletionResponsesConfig: } ``` """ - # Create a tool call for the function call + # Create a tool call for the function call. Custom tool calls + # store their payload in "input" (raw string) rather than + # "arguments" (JSON string), so normalize to arguments here. + raw_arguments = function_call.get("arguments") + if not raw_arguments and function_call.get("type") == "custom_tool_call": + raw_input = function_call.get("input") or "" + raw_arguments = json.dumps({"content": raw_input}) if raw_input else "" tool_call = ChatCompletionToolCallChunk( id=function_call.get("call_id") or function_call.get("id") or "", type="function", function=ChatCompletionToolCallFunctionChunk( name=function_call.get("name") or "", - arguments=str(function_call.get("arguments") or ""), + arguments=str(raw_arguments or ""), ), index=0, ) @@ -1138,7 +1111,7 @@ class LiteLLMCompletionResponsesConfig: return [chat_completion_response_message] @staticmethod - def _resolve_file_id(item: Dict[str, Any]) -> Optional[str]: + def _resolve_file_id(item: dict[str, Any]) -> str | None: """ Return the effective file_id for a Responses API input_file item. Explicit file_id takes precedence; file_url is used as fallback so @@ -1147,7 +1120,7 @@ class LiteLLMCompletionResponsesConfig: return item.get("file_id") or item.get("file_url") or None @staticmethod - def _transform_input_file_item_to_file_item(item: Dict[str, Any]) -> Dict[str, Any]: + def _transform_input_file_item_to_file_item(item: dict[str, Any]) -> dict[str, Any]: """ Transform a Responses API input_file item to a Chat Completion file item @@ -1157,21 +1130,21 @@ class LiteLLMCompletionResponsesConfig: Returns: Dictionary with transformed file structure for Chat Completion """ - file_dict: Dict[str, Any] = {} + file_dict: dict[str, Any] = {} file_id = LiteLLMCompletionResponsesConfig._resolve_file_id(item) if file_id: file_dict["file_id"] = file_id if item.get("file_data"): file_dict["file_data"] = item["file_data"] - new_item: Dict[str, Any] = {"type": "file", "file": file_dict} + new_item: dict[str, Any] = {"type": "file", "file": file_dict} if "cache_control" in item: new_item["cache_control"] = item["cache_control"] return new_item @staticmethod def _transform_input_image_item_to_image_item( - item: Dict[str, Any], + item: dict[str, Any], ) -> ChatCompletionImageObject: """ Transform a Responses API input_image item to a Chat Completion image item @@ -1185,7 +1158,7 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_responses_api_content_to_chat_completion_content( content: Any, - ) -> Union[str, List[Union[str, Dict[str, Any]]]]: + ) -> str | list[str | dict[str, Any]]: """ Transform a Responses API content into a Chat Completion content @@ -1199,7 +1172,7 @@ class LiteLLMCompletionResponsesConfig: elif isinstance(content, str): return content elif isinstance(content, list): - content_list: List[Union[str, Dict[str, Any]]] = [] + content_list: list[str | dict[str, Any]] = [] for item in content: if isinstance(item, str): content_list.append(item) @@ -1220,7 +1193,7 @@ class LiteLLMCompletionResponsesConfig: text_value = item.get("text") if text_value is None: continue - content_block: Dict[str, Any] = { + content_block: dict[str, Any] = { "type": LiteLLMCompletionResponsesConfig._get_chat_completion_request_content_type( item.get("type") or "text" ), @@ -1268,7 +1241,7 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def transform_instructions_to_system_message( - instructions: Optional[str], + instructions: str | None, ) -> ChatCompletionSystemMessage: """ Transform a Instructions into a system message @@ -1277,18 +1250,18 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def transform_responses_api_tools_to_chat_completion_tools( - tools: Optional[List[Union[FunctionToolParam, OpenAIMcpServerTool]]], - ) -> Tuple[ - List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]], - Optional[OpenAIWebSearchOptions], + tools: list[FunctionToolParam | OpenAIMcpServerTool] | None, + ) -> tuple[ + list[ChatCompletionToolParam | OpenAIMcpServerTool], + OpenAIWebSearchOptions | None, ]: """ Transform a Responses API tools into a Chat Completion tools """ if tools is None: return [], None - chat_completion_tools: List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]] = [] - web_search_options: Optional[OpenAIWebSearchOptions] = None + chat_completion_tools: list[ChatCompletionToolParam | OpenAIMcpServerTool] = [] + web_search_options: OpenAIWebSearchOptions | None = None for tool in tools: if tool.get("type") == "mcp": chat_completion_tools.append(cast(OpenAIMcpServerTool, tool)) @@ -1296,8 +1269,8 @@ class LiteLLMCompletionResponsesConfig: _search_context_size: Literal["low", "medium", "high"] = cast( Literal["low", "medium", "high"], tool.get("search_context_size") ) - _user_location: Optional[OpenAIWebSearchUserLocation] = cast( - Optional[OpenAIWebSearchUserLocation], + _user_location: OpenAIWebSearchUserLocation | None = cast( + OpenAIWebSearchUserLocation | None, tool.get("user_location") or None, ) web_search_options = OpenAIWebSearchOptions( @@ -1310,7 +1283,7 @@ class LiteLLMCompletionResponsesConfig: parameters = dict(typed_tool.get("parameters", {}) or {}) if not parameters or "type" not in parameters: parameters["type"] = "object" - chat_completion_tool: Dict[str, Any] = { + chat_completion_tool: dict[str, Any] = { "type": "function", "function": { "name": typed_tool.get("name") or "", @@ -1328,14 +1301,30 @@ class LiteLLMCompletionResponsesConfig: if tool.get("input_examples"): chat_completion_tool["input_examples"] = tool.get("input_examples") # type: ignore chat_completion_tools.append(cast(ChatCompletionToolParam, chat_completion_tool)) + elif tool.get("type") == "custom": + converted = convert_custom_tool_to_function_tool(tool) + if converted is not None: + chat_completion_tools.append(converted) else: - chat_completion_tools.append(cast(Union[ChatCompletionToolParam, OpenAIMcpServerTool], tool)) + _tool_type = tool.get("type") + if _tool_type in ("computer_use", "image_generation", "namespace", "shell"): + # Drop unsupported Responses-API-only tool types that have no + # Chat Completions equivalent. Passing them through verbatim + # causes providers to reject the request with "'function' is a + # required property". + verbose_logger.warning( + "Dropping Responses API tool of type '%s': it has no Chat Completions " + "equivalent and the target provider would reject the request.", + _tool_type, + ) + continue + chat_completion_tools.append(cast(ChatCompletionToolParam | OpenAIMcpServerTool, tool)) return chat_completion_tools, web_search_options @staticmethod def transform_chat_completion_tool_params_to_responses_api_tools( - chat_completion_tools: Optional[List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]]], - ) -> List[Dict[str, Any]]: + chat_completion_tools: list[ChatCompletionToolParam | OpenAIMcpServerTool] | None, + ) -> list[dict[str, Any]]: """ Transform Chat Completion tool params (e.g. from guardrail output) back to Responses API request tool format. Inverse of @@ -1343,17 +1332,17 @@ class LiteLLMCompletionResponsesConfig: """ if chat_completion_tools is None or not chat_completion_tools: return [] - result: List[Dict[str, Any]] = [] + result: list[dict[str, Any]] = [] for tool in chat_completion_tools: if not isinstance(tool, dict): result.append(tool) # type: ignore continue if tool.get("type") == "function": - fn = cast(Dict[str, Any], tool.get("function") or {}) + fn = cast(dict[str, Any], tool.get("function") or {}) parameters = dict(fn.get("parameters", {}) or {}) if not parameters or "type" not in parameters: parameters["type"] = "object" - responses_tool: Dict[str, Any] = { + responses_tool: dict[str, Any] = { "type": "function", "name": fn.get("name") or "", "description": fn.get("description") or "", @@ -1377,11 +1366,16 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def transform_chat_completion_tools_to_responses_tools( chat_completion_response: ModelResponse, - ) -> List[ResponseFunctionToolCall]: + responses_api_request: ResponsesAPIOptionalRequestParams | None = None, + ) -> list[ResponseFunctionToolCall | CustomToolCallOutputItem]: """ - Transform a Chat Completion tools into a Responses API tools + Transform a Chat Completion tools into a Responses API tools. + + For custom tools (e.g. apply_patch), returns CustomToolCallOutputItem + with ``type="custom_tool_call"``. For regular function tools, returns + ``ResponseFunctionToolCall`` with ``type="function_call"``. """ - all_chat_completion_tools: List[ChatCompletionMessageToolCall] = [] + all_chat_completion_tools: list[ChatCompletionMessageToolCall] = [] for choice in chat_completion_response.choices: if isinstance(choice, Choices): if choice.message.tool_calls: @@ -1392,53 +1386,77 @@ class LiteLLMCompletionResponsesConfig: value=tool_call, ) - responses_tools: List[ResponseFunctionToolCall] = [] + # Extract custom tool names from the original request + custom_tool_names: set[str] = set() + if responses_api_request and "tools" in responses_api_request: + custom_tool_names = extract_custom_tool_names(responses_api_request["tools"]) + + responses_tools: list[ResponseFunctionToolCall | CustomToolCallOutputItem] = [] for tool in all_chat_completion_tools: if tool.type == "function": function_definition = tool.function - provider_specific_fields: Optional[Dict] = None - if hasattr(tool, "provider_specific_fields") and getattr(tool, "provider_specific_fields", None): - provider_specific_fields = getattr(tool, "provider_specific_fields") - if not isinstance(provider_specific_fields, dict): - provider_specific_fields = ( - dict(provider_specific_fields) # type: ignore - if hasattr(provider_specific_fields, "__dict__") - else {} - ) - elif hasattr(function_definition, "provider_specific_fields") and getattr( - function_definition, "provider_specific_fields", None - ): - provider_specific_fields = getattr(function_definition, "provider_specific_fields") - if not isinstance(provider_specific_fields, dict): - provider_specific_fields = ( - dict(provider_specific_fields) # type: ignore - if hasattr(provider_specific_fields, "__dict__") - else {} - ) + tool_name = function_definition.name or "" + tool_id = tool.id or "" + tool_arguments = function_definition.get("arguments") or "" - output_tool_call: ResponseFunctionToolCall = ResponseFunctionToolCall( - name=function_definition.name or "", - arguments=function_definition.get("arguments") or "", - call_id=tool.id or "", - id=tool.id or "", - type="function_call", # critical this is "function_call" to work with tools like openai codex - status=function_definition.get("status") or "completed", - ) + # Check if this is a custom tool + if is_custom_tool_call(tool_name, custom_tool_names): + # Build custom_tool_call output item + input_str = unwrap_custom_tool_arguments(tool_arguments) + custom_item = CustomToolCallOutputItem( + type="custom_tool_call", + call_id=tool_id, + id=tool_id, + name=tool_name, + input=input_str, + status=function_definition.get("status") or "completed", + ) + responses_tools.append(custom_item) + else: + # Build regular function_call output item + provider_specific_fields: dict | None = None + if hasattr(tool, "provider_specific_fields") and getattr(tool, "provider_specific_fields", None): + provider_specific_fields = getattr(tool, "provider_specific_fields") + if not isinstance(provider_specific_fields, dict): + provider_specific_fields = ( + dict(provider_specific_fields) # type: ignore + if hasattr(provider_specific_fields, "__dict__") + else {} + ) + elif hasattr(function_definition, "provider_specific_fields") and getattr( + function_definition, "provider_specific_fields", None + ): + provider_specific_fields = getattr(function_definition, "provider_specific_fields") + if not isinstance(provider_specific_fields, dict): + provider_specific_fields = ( + dict(provider_specific_fields) # type: ignore + if hasattr(provider_specific_fields, "__dict__") + else {} + ) - # Pass through provider_specific_fields as-is if present - if provider_specific_fields: - setattr( - output_tool_call, - "provider_specific_fields", - provider_specific_fields, - ) # type: ignore + output_tool_call: ResponseFunctionToolCall = ResponseFunctionToolCall( + name=tool_name, + arguments=tool_arguments, + call_id=tool_id, + id=tool_id, + type="function_call", + status=function_definition.get("status") or "completed", + ) - responses_tools.append(output_tool_call) + # Pass through provider_specific_fields as-is if present + if provider_specific_fields: + setattr( + output_tool_call, + "provider_specific_fields", + provider_specific_fields, + ) # type: ignore + + responses_tools.append(output_tool_call) return responses_tools @staticmethod def _map_chat_completion_finish_reason_to_responses_status( - finish_reason: Optional[str], + finish_reason: str | None, ) -> ResponsesAPIStatus: """ Map chat completion finish_reason to responses API status. @@ -1465,7 +1483,7 @@ class LiteLLMCompletionResponsesConfig: return "completed" @staticmethod - def _tool_call_id_from_responses_item(item_id: Optional[str], call_id: Optional[str]) -> str: + def _tool_call_id_from_responses_item(item_id: str | None, call_id: str | None) -> str: """Bedrock Mantle returns a non-unique, index-based ``call_id`` (``call_0``, ``call_1``, ... that resets every response) alongside a unique ``id`` (``fc_...``). ``call_id`` is the canonical Responses API correlation key, so @@ -1480,7 +1498,7 @@ class LiteLLMCompletionResponsesConfig: def convert_response_function_tool_call_to_chat_completion_tool_call( tool_call_item: Any, index: int = 0, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Convert ResponseFunctionToolCall to ChatCompletionToolCallChunk format. @@ -1510,7 +1528,7 @@ class LiteLLMCompletionResponsesConfig: ) ) - function_dict: Dict[str, Any] = { + function_dict: dict[str, Any] = { "name": tool_call_item.name, "arguments": tool_call_item.arguments, } @@ -1518,7 +1536,7 @@ class LiteLLMCompletionResponsesConfig: if provider_specific_fields: function_dict["provider_specific_fields"] = provider_specific_fields - tool_call_dict: Dict[str, Any] = { + tool_call_dict: dict[str, Any] = { "id": LiteLLMCompletionResponsesConfig._tool_call_id_from_responses_item( getattr(tool_call_item, "id", None), getattr(tool_call_item, "call_id", None), @@ -1537,7 +1555,7 @@ class LiteLLMCompletionResponsesConfig: def convert_apply_patch_tool_call_to_chat_completion_tool_call( tool_call_item: Any, index: int = 0, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Convert ResponseApplyPatchToolCall to ChatCompletionToolCallChunk format. @@ -1555,7 +1573,7 @@ class LiteLLMCompletionResponsesConfig: import json operation_dict = tool_call_item.operation.model_dump() - tool_call_dict: Dict[str, Any] = { + tool_call_dict: dict[str, Any] = { "id": tool_call_item.call_id, "function": { "name": "apply_patch", @@ -1568,9 +1586,9 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def transform_chat_completion_response_to_responses_api_response( - request_input: Union[str, ResponseInputParam], + request_input: str | ResponseInputParam, responses_api_request: ResponsesAPIOptionalRequestParams, - chat_completion_response: Union[ModelResponse, dict], + chat_completion_response: ModelResponse | dict, ) -> ResponsesAPIResponse: """ Transform a Chat Completion response into a Responses API response @@ -1578,8 +1596,8 @@ class LiteLLMCompletionResponsesConfig: if isinstance(chat_completion_response, dict): chat_completion_response = ModelResponse(**chat_completion_response) # Get finish_reason from the first choice to determine overall status - finish_reason: Optional[str] = None - choices: List[Choices] = getattr(chat_completion_response, "choices", []) + finish_reason: str | None = None + choices: list[Choices] = getattr(chat_completion_response, "choices", []) if choices and len(choices) > 0: finish_reason = choices[0].finish_reason @@ -1595,6 +1613,7 @@ class LiteLLMCompletionResponsesConfig: output=LiteLLMCompletionResponsesConfig._transform_chat_completion_choices_to_responses_output( chat_completion_response=chat_completion_response, choices=getattr(chat_completion_response, "choices", []), + responses_api_request=responses_api_request, ), parallel_tool_calls=getattr(chat_completion_response, "parallel_tool_calls", False), temperature=getattr(chat_completion_response, "temperature", 0), @@ -1626,24 +1645,23 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_chat_completion_choices_to_responses_output( chat_completion_response: ModelResponse, - choices: List[Choices], - ) -> List[ - Union[ - GenericResponseOutputItem, - OutputCodeInterpreterCall, - OutputFunctionToolCall, - OutputImageGenerationCall, - ResponseFunctionToolCall, - ] + choices: list[Choices], + responses_api_request: ResponsesAPIOptionalRequestParams | None = None, + ) -> list[ + GenericResponseOutputItem + | OutputCodeInterpreterCall + | OutputFunctionToolCall + | OutputImageGenerationCall + | ResponseFunctionToolCall + | CustomToolCallOutputItem ]: - responses_output: List[ - Union[ - GenericResponseOutputItem, - OutputCodeInterpreterCall, - OutputFunctionToolCall, - OutputImageGenerationCall, - ResponseFunctionToolCall, - ] + responses_output: list[ + GenericResponseOutputItem + | OutputCodeInterpreterCall + | OutputFunctionToolCall + | OutputImageGenerationCall + | ResponseFunctionToolCall + | CustomToolCallOutputItem ] = [] responses_output.extend( @@ -1654,7 +1672,8 @@ class LiteLLMCompletionResponsesConfig: ) responses_output.extend( LiteLLMCompletionResponsesConfig.transform_chat_completion_tools_to_responses_tools( - chat_completion_response=chat_completion_response + chat_completion_response=chat_completion_response, + responses_api_request=responses_api_request, ) ) @@ -1713,8 +1732,8 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _extract_reasoning_output_items( chat_completion_response: ModelResponse, - choices: List[Choices], - ) -> List[GenericResponseOutputItem]: + choices: list[Choices], + ) -> list[GenericResponseOutputItem]: for choice in choices: if hasattr(choice, "message") and choice.message: message = choice.message @@ -1743,7 +1762,7 @@ class LiteLLMCompletionResponsesConfig: def _extract_image_generation_output_items( chat_completion_response: ModelResponse, choice: Choices, - ) -> List[OutputImageGenerationCall]: + ) -> list[OutputImageGenerationCall]: """ Extract image generation outputs from a choice that contains images. @@ -1762,7 +1781,7 @@ class LiteLLMCompletionResponsesConfig: 'result': 'iVBORw0...' # Pure base64 without data: prefix } """ - image_generation_items: List[OutputImageGenerationCall] = [] + image_generation_items: list[OutputImageGenerationCall] = [] images = getattr(choice.message, "images", []) if not images: @@ -1789,7 +1808,7 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _map_finish_reason_to_image_generation_status( - finish_reason: Optional[str], + finish_reason: str | None, ) -> Literal["in_progress", "completed", "incomplete", "failed"]: """ Map finish_reason to image generation status. @@ -1808,7 +1827,7 @@ class LiteLLMCompletionResponsesConfig: return "completed" @staticmethod - def _extract_base64_from_data_url(data_url: str) -> Optional[str]: + def _extract_base64_from_data_url(data_url: str) -> str | None: """ Extract pure base64 string from a data URL. @@ -1834,9 +1853,9 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _extract_message_output_items( chat_completion_response: ModelResponse, - choices: List[Choices], - ) -> List[Union[GenericResponseOutputItem, OutputImageGenerationCall]]: - message_output_items: List[Union[GenericResponseOutputItem, OutputImageGenerationCall]] = [] + choices: list[Choices], + ) -> list[GenericResponseOutputItem | OutputImageGenerationCall]: + message_output_items: list[GenericResponseOutputItem | OutputImageGenerationCall] = [] for choice in choices: # Check if message has images (image generation) if hasattr(choice.message, "images") and choice.message.images: @@ -1868,20 +1887,8 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_responses_api_outputs_to_chat_completion_messages( responses_api_output: ResponsesAPIResponse, - ) -> List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ] - ]: - messages: List[ - Union[ - AllMessageValues, - GenericChatCompletionMessage, - ChatCompletionMessageToolCall, - ] - ] = [] + ) -> list[AllMessageValues | GenericChatCompletionMessage | ChatCompletionMessageToolCall]: + messages: list[AllMessageValues | GenericChatCompletionMessage | ChatCompletionMessageToolCall] = [] output_items = responses_api_output.output for _output_item in output_items: output_item: dict = dict(_output_item) @@ -1939,9 +1946,9 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_chat_completion_annotations_to_response_output_annotations( - annotations: Optional[List[ChatCompletionAnnotation]], - ) -> List[GenericResponseOutputItemContentAnnotation]: - response_output_annotations: List[GenericResponseOutputItemContentAnnotation] = [] + annotations: list[ChatCompletionAnnotation] | None, + ) -> list[GenericResponseOutputItemContentAnnotation]: + response_output_annotations: list[GenericResponseOutputItemContentAnnotation] = [] if annotations is None: return response_output_annotations @@ -1965,10 +1972,10 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_chat_completion_usage_to_responses_usage( - chat_completion_response: Union[ModelResponse, Usage], + chat_completion_response: ModelResponse | Usage, ) -> ResponseAPIUsage: if isinstance(chat_completion_response, ModelResponse): - usage: Optional[Usage] = getattr(chat_completion_response, "usage", None) + usage: Usage | None = getattr(chat_completion_response, "usage", None) else: usage = chat_completion_response if usage is None: @@ -1991,7 +1998,7 @@ class LiteLLMCompletionResponsesConfig: # Translate prompt_tokens_details to input_tokens_details if hasattr(usage, "prompt_tokens_details") and usage.prompt_tokens_details is not None: prompt_details = usage.prompt_tokens_details - input_details_dict: Dict[str, int] = {} + input_details_dict: dict[str, int] = {} if hasattr(prompt_details, "cached_tokens") and prompt_details.cached_tokens is not None: input_details_dict["cached_tokens"] = prompt_details.cached_tokens @@ -2010,11 +2017,9 @@ class LiteLLMCompletionResponsesConfig: # Translate completion_tokens_details to output_tokens_details if hasattr(usage, "completion_tokens_details") and usage.completion_tokens_details is not None: completion_details = usage.completion_tokens_details - output_details_dict: Dict[str, int] = {} + output_details_dict: dict[str, int] = {} if hasattr(completion_details, "reasoning_tokens") and completion_details.reasoning_tokens is not None: output_details_dict["reasoning_tokens"] = completion_details.reasoning_tokens - else: - output_details_dict["reasoning_tokens"] = 0 if hasattr(completion_details, "text_tokens") and completion_details.text_tokens is not None: output_details_dict["text_tokens"] = completion_details.text_tokens @@ -2029,8 +2034,8 @@ class LiteLLMCompletionResponsesConfig: @staticmethod def _transform_text_format_to_response_format( - text_param: Union[Dict[str, Any], Any], - ) -> Optional[Dict[str, Any]]: + text_param: dict[str, Any] | Any, + ) -> dict[str, Any] | None: """ Transform Responses API text.format parameter to Chat Completion response_format parameter. diff --git a/litellm/responses/main.py b/litellm/responses/main.py index e8fe51ed484..92dd5a513b9 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -127,7 +127,7 @@ def mock_responses_api_response( "input_tokens": 36, "input_tokens_details": {"cached_tokens": 0}, "output_tokens": 87, - "output_tokens_details": {"reasoning_tokens": 0}, + "output_tokens_details": {}, "total_tokens": 123, }, "user": None, @@ -212,6 +212,7 @@ async def aresponses_api_with_mcp( litellm_trace_id=kwargs.get("litellm_trace_id"), mcp_auth_header=mcp_auth_header, mcp_server_auth_headers=mcp_server_auth_headers, + request_tags=LiteLLM_Proxy_MCP_Handler._get_parent_request_tags(kwargs), ) openai_tools = LiteLLM_Proxy_MCP_Handler._transform_mcp_tools_to_openai(original_mcp_tools) @@ -327,6 +328,7 @@ async def aresponses_api_with_mcp( raw_headers=raw_headers_from_request, litellm_call_id=kwargs.get("litellm_call_id"), litellm_trace_id=kwargs.get("litellm_trace_id"), + request_tags=LiteLLM_Proxy_MCP_Handler._get_parent_request_tags(kwargs), ) if tool_results: @@ -382,6 +384,7 @@ async def aresponses_api_with_mcp( mcp_tools_with_litellm_proxy=mcp_tools_with_litellm_proxy, mcp_auth_header=mcp_auth_header, mcp_server_auth_headers=mcp_server_auth_headers, + request_tags=LiteLLM_Proxy_MCP_Handler._get_parent_request_tags(kwargs), ) final_response = LiteLLM_Proxy_MCP_Handler._add_mcp_output_elements_to_response( response=final_response, diff --git a/litellm/responses/mcp/chat_completions_handler.py b/litellm/responses/mcp/chat_completions_handler.py index 10ff67f68d5..f2ccfd430ae 100644 --- a/litellm/responses/mcp/chat_completions_handler.py +++ b/litellm/responses/mcp/chat_completions_handler.py @@ -1,5 +1,6 @@ """Helpers for handling MCP-aware `/chat/completions` requests.""" +import logging from typing import ( Any, List, @@ -115,6 +116,7 @@ async def acompletion_with_mcp( # Extract user_api_key_auth from metadata or kwargs user_api_key_auth = kwargs.get("user_api_key_auth") or ((kwargs.get("metadata", {}) or {}).get("user_api_key_auth")) + request_tags = LiteLLM_Proxy_MCP_Handler._get_parent_request_tags(kwargs) # Extract MCP auth headers before fetching tools (needed for dynamic auth) ( @@ -137,6 +139,7 @@ async def acompletion_with_mcp( litellm_trace_id=kwargs.get("litellm_trace_id"), mcp_auth_header=mcp_auth_header, mcp_server_auth_headers=mcp_server_auth_headers, + request_tags=request_tags, ) openai_tools = LiteLLM_Proxy_MCP_Handler._transform_mcp_tools_to_openai( @@ -218,6 +221,7 @@ async def acompletion_with_mcp( litellm_trace_id, openai_tools, base_call_args, + request_tags, ): self.stream_wrapper = stream_wrapper self.messages = messages @@ -231,6 +235,7 @@ async def acompletion_with_mcp( self.litellm_trace_id = litellm_trace_id self.openai_tools = openai_tools self.base_call_args = base_call_args + self.request_tags = request_tags self.collected_chunks: List[ModelResponseStream] = [] self.tool_calls: Optional[List] = None self.tool_results: Optional[List] = None @@ -303,6 +308,17 @@ async def acompletion_with_mcp( return chunk + async def _drain_inner_stream(self): + try: + while True: + await self._stream_iterator.__anext__() + except StopAsyncIteration: + pass + except Exception: + logging.getLogger("LiteLLM").exception( + "Error draining inner MCP stream after final chunk; spend logging may be incomplete" + ) + async def __anext__(self): # Phase 1: Collect and yield initial stream chunks if not self.stream_exhausted: @@ -332,15 +348,16 @@ async def acompletion_with_mcp( ) if is_final: - # This is the final chunk, mark stream as exhausted self.stream_exhausted = True - # Process tool calls after we've collected all chunks await self._process_tool_calls() - # Apply MCP metadata (tool_calls and tool_results) to final chunk chunk = self._add_mcp_tool_metadata_to_final_chunk(chunk) - # If we have tool results, prepare follow-up call immediately if self.tool_results and self.complete_response: await self._prepare_follow_up_call() + # Drain inner stream so CustomStreamWrapper fires its + # end-of-stream handler (dispatch_success_handlers → + # _ProxyDBLogger → LiteLLM_SpendLogs). The CSW may + # yield one usage chunk before raising StopAsyncIteration. + await self._drain_inner_stream() return chunk except StopAsyncIteration: @@ -354,6 +371,7 @@ async def acompletion_with_mcp( # If we have tool results, prepare follow-up call if self.tool_results and self.complete_response: await self._prepare_follow_up_call() + await self._drain_inner_stream() return final_chunk # Phase 2: Yield follow-up stream chunks if available @@ -426,6 +444,7 @@ async def acompletion_with_mcp( raw_headers=self.raw_headers, litellm_call_id=self.litellm_call_id, litellm_trace_id=self.litellm_trace_id, + request_tags=self.request_tags, ) async def _prepare_follow_up_call(self): @@ -485,6 +504,7 @@ async def acompletion_with_mcp( litellm_trace_id=kwargs.get("litellm_trace_id"), openai_tools=openai_tools, base_call_args=base_call_args, + request_tags=request_tags, ) # Create a wrapper class that delegates to our custom iterator @@ -596,6 +616,7 @@ async def acompletion_with_mcp( raw_headers=raw_headers, litellm_call_id=kwargs.get("litellm_call_id"), litellm_trace_id=kwargs.get("litellm_trace_id"), + request_tags=request_tags, ) if not tool_results: diff --git a/litellm/responses/mcp/litellm_proxy_mcp_handler.py b/litellm/responses/mcp/litellm_proxy_mcp_handler.py index e969208d1d9..e03f0296109 100644 --- a/litellm/responses/mcp/litellm_proxy_mcp_handler.py +++ b/litellm/responses/mcp/litellm_proxy_mcp_handler.py @@ -16,7 +16,10 @@ from typing import ( from litellm._logging import verbose_logger from litellm.constants import MAXIMUM_TRACEBACK_LINES_TO_LOG from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj -from litellm.proxy._experimental.mcp_server.utils import split_server_prefix_from_name +from litellm.proxy._experimental.mcp_server.utils import ( + split_server_prefix_from_name, + strip_known_server_prefix, +) from litellm.responses.main import aresponses from litellm.responses.streaming_iterator import BaseResponsesAPIStreamingIterator from litellm.types.llms.openai import ResponsesAPIResponse @@ -59,6 +62,20 @@ class LiteLLM_Proxy_MCP_Handler: This handles when a user passes mcp server_url="litellm_proxy" in their tools. """ + @staticmethod + def _get_parent_request_tags(kwargs: Optional[dict[str, Any]]) -> list[str]: + """Tags from the parent LLM request, using the same extraction logic as standard logging (incl. User-Agent).""" + if not kwargs: + return [] + from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup + + litellm_params = kwargs.get("litellm_params") or kwargs + proxy_server_request = litellm_params.get("proxy_server_request") or kwargs.get("proxy_server_request") or {} + return StandardLoggingPayloadSetup._get_request_tags( + litellm_params=litellm_params, + proxy_server_request=proxy_server_request, + ) + @staticmethod def _should_use_litellm_mcp_gateway(tools: Optional[Iterable[ToolParam]]) -> bool: """ @@ -162,6 +179,7 @@ class LiteLLM_Proxy_MCP_Handler: litellm_trace_id: Optional[str] = None, mcp_auth_header: Optional[str] = None, mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]] = None, + request_tags: Optional[list[str]] = None, ) -> tuple[List[MCPTool], List[str]]: """ Get available tools from the MCP server manager. @@ -250,6 +268,7 @@ class LiteLLM_Proxy_MCP_Handler: log_list_tools_to_spendlogs=True, list_tools_log_source="responses", litellm_trace_id=litellm_trace_id, + request_tags=request_tags, ) allowed_mcp_server_ids = await global_mcp_server_manager.get_allowed_mcp_servers(user_api_key_auth) @@ -351,6 +370,7 @@ class LiteLLM_Proxy_MCP_Handler: user_api_key_auth: Any, mcp_tools_with_litellm_proxy: List[ToolParam], litellm_trace_id: Optional[str] = None, + request_tags: Optional[list[str]] = None, ) -> tuple[List[Any], dict[str, str]]: """ Centralized method to process MCP tools through the complete pipeline. @@ -371,6 +391,7 @@ class LiteLLM_Proxy_MCP_Handler: user_api_key_auth, mcp_tools_with_litellm_proxy, litellm_trace_id=litellm_trace_id, + request_tags=request_tags, ) openai_tools = LiteLLM_Proxy_MCP_Handler._transform_mcp_tools_to_openai(deduplicated_mcp_tools) @@ -384,6 +405,7 @@ class LiteLLM_Proxy_MCP_Handler: litellm_trace_id: Optional[str] = None, mcp_auth_header: Optional[str] = None, mcp_server_auth_headers: Optional[Dict[str, Dict[str, str]]] = None, + request_tags: Optional[list[str]] = None, ) -> tuple[List[Any], dict[str, str]]: """ Process MCP tools through filtering and deduplication pipeline without OpenAI transformation. @@ -411,6 +433,7 @@ class LiteLLM_Proxy_MCP_Handler: litellm_trace_id=litellm_trace_id, mcp_auth_header=mcp_auth_header, mcp_server_auth_headers=mcp_server_auth_headers, + request_tags=request_tags, ) # Step 2: Filter tools based on allowed_tools parameter @@ -597,6 +620,7 @@ class LiteLLM_Proxy_MCP_Handler: raw_headers: Optional[Dict[str, str]] = None, litellm_call_id: Optional[str] = None, litellm_trace_id: Optional[str] = None, + request_tags: Optional[list[str]] = None, ) -> List[Dict[str, Any]]: """Execute tool calls and return results.""" from fastapi import HTTPException @@ -606,6 +630,9 @@ class LiteLLM_Proxy_MCP_Handler: from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( global_mcp_server_manager, ) + from litellm.proxy._experimental.mcp_server.server import ( + _resolve_display_name_to_original, + ) from litellm.proxy.proxy_server import proxy_logging_obj tool_results = [] @@ -632,11 +659,13 @@ class LiteLLM_Proxy_MCP_Handler: server_name = tool_server_map[tool_name] - # Remove the server name prefix if the tool name includes it. - sanitized_tool_name = tool_name - unprefixed_name, prefixed_server_name = split_server_prefix_from_name(tool_name) - if prefixed_server_name and prefixed_server_name == server_name and unprefixed_name: - sanitized_tool_name = unprefixed_name + mcp_server = global_mcp_server_manager.get_mcp_server_by_name( + server_name + ) or global_mcp_server_manager._get_mcp_server_from_tool_name(tool_name) + resolved_tool_name = ( + _resolve_display_name_to_original(tool_name, [mcp_server]) if mcp_server else tool_name + ) + sanitized_tool_name = strip_known_server_prefix(resolved_tool_name, mcp_server) start_time = datetime.now() logging_input = [ @@ -672,17 +701,23 @@ class LiteLLM_Proxy_MCP_Handler: } if litellm_trace_id: logging_request_data["litellm_trace_id"] = litellm_trace_id - user_identifier = None + if request_tags: + logging_request_data["metadata"]["tags"] = request_tags if user_api_key_auth is not None: - user_api_key = getattr(user_api_key_auth, "api_key", None) - if user_api_key: - logging_request_data["metadata"]["user_api_key"] = user_api_key + from litellm.proxy.litellm_pre_call_utils import ( + LiteLLMProxyRequestSetup, + ) + LiteLLMProxyRequestSetup.add_user_api_key_auth_to_request_metadata( + data=logging_request_data, + user_api_key_dict=user_api_key_auth, + _metadata_variable_name="metadata", + ) user_identifier = getattr(user_api_key_auth, "end_user_id", None) or getattr( user_api_key_auth, "user_id", None ) - if user_identifier: - logging_request_data["user"] = user_identifier + if user_identifier: + logging_request_data["user"] = user_identifier litellm_logging_obj: Optional[LiteLLMLoggingObj] = None try: @@ -717,7 +752,6 @@ class LiteLLM_Proxy_MCP_Handler: "arguments": parsed_arguments, "namespaced_tool_name": tool_name, } - mcp_server = global_mcp_server_manager._get_mcp_server_from_tool_name(tool_name) if mcp_server: mcp_info = mcp_server.mcp_info or {} standard_logging_mcp_tool_call["mcp_server_name"] = ( diff --git a/litellm/responses/mcp/mcp_streaming_iterator.py b/litellm/responses/mcp/mcp_streaming_iterator.py index a961271e3f0..c705a04963c 100644 --- a/litellm/responses/mcp/mcp_streaming_iterator.py +++ b/litellm/responses/mcp/mcp_streaming_iterator.py @@ -24,6 +24,8 @@ if TYPE_CHECKING: else: MCPTool = Any +MAX_MCP_TOOL_CALL_ROUNDS = 5 + async def create_mcp_list_tools_events( mcp_tools_with_litellm_proxy: List[ToolParam], @@ -265,9 +267,7 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): self.should_auto_execute = self._should_auto_execute_tools() # Streaming state management - self.phase = ( - "initial_response" # initial_response -> mcp_discovery -> tool_execution -> follow_up_response -> finished - ) + self.phase = "initial_response" # initial_response -> mcp_discovery -> (continue_initial_response <-> tool_execution) -> finished self.finished = False # Event queues and generation flags @@ -281,11 +281,23 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): # Iterator references self.base_iterator: Optional[Union[Any, ResponsesAPIResponse]] = base_iterator # Will be created when needed - self.follow_up_iterator: Optional[Any] = None # Response collection for tool execution self.collected_response: Optional[ResponsesAPIResponse] = None + # Counts completed rounds of tool execution, so a model that keeps + # calling tools (e.g. retrying after an error) can't loop forever. + # Capped in _create_follow_up_iterator, which drops "tools" from the + # request once the cap is hit so the model must answer in text. + self.tool_call_round = 0 + # The collected_response that self.tool_results was computed from. + # _create_follow_up_iterator only builds a follow-up when this is + # still the current collected_response — otherwise a round whose + # response had no tool calls (e.g. the model finally answered in + # text) would incorrectly reuse tool_results left over from an + # earlier round and keep looping instead of finishing. + self._tool_results_for_response: Optional[ResponsesAPIResponse] = None + # Set up model metadata (will be updated when we get the real iterator) self.model = self.original_request_params.get("model", "unknown") self.litellm_metadata = {} @@ -376,10 +388,11 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): Phase-based streaming: 1. initial_response - Stream the first LLM response (includes response.created, response.in_progress, response.output_item.added) 2. mcp_discovery - Emit MCP discovery events (after response.output_item.added) - 3. continue_initial_response - Continue streaming the initial response content + 3. continue_initial_response - Stream the current round's response (initial or follow-up). + On completion, if auto-execute is on and the response contains tool calls, loops back + through tool_execution/follow-up instead of ending, up to MAX_MCP_TOOL_CALL_ROUNDS. 4. tool_execution - Emit tool execution events - 5. follow_up_response - Stream the follow-up response - 6. finished - End iteration + 5. finished - End iteration """ # Phase 1: Initial Response Stream (emit standard OpenAI events first) @@ -415,21 +428,17 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): if self.tool_execution_events: return self.tool_execution_events.pop(0) - # Move to follow-up response phase - self.phase = "follow_up_response" + # Route the follow-up call back through continue_initial_response so + # its completion is checked for further tool calls the same way the + # initial response is — otherwise a model that needs a second round + # of tool calls (e.g. retrying after an error) would have that round + # silently dropped and the stream would end with no final text. await self._create_follow_up_iterator() - - # Phase 5: Follow-up Response Stream - if self.phase == "follow_up_response": - if self.follow_up_iterator: - try: - return await cast(Any, self.follow_up_iterator).__anext__() # type: ignore[attr-defined] - except StopAsyncIteration: - self.phase = "finished" - raise - else: - self.phase = "finished" - raise StopAsyncIteration + if self.base_iterator is not None: + self.phase = "continue_initial_response" + return await self.__anext__() + self.phase = "finished" + raise StopAsyncIteration # Phase 6: Finished if self.phase == "finished": @@ -599,6 +608,7 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): tool_calls = [] if not tool_calls: return + self.tool_call_round += 1 for tool_call in tool_calls: ( @@ -630,6 +640,7 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): raw_headers=self.raw_headers, litellm_call_id=self.litellm_call_id, litellm_trace_id=self.litellm_trace_id, + request_tags=LiteLLM_Proxy_MCP_Handler._get_parent_request_tags(self.original_request_params), ) # Create completion events and output_item.done events for tool execution @@ -685,6 +696,7 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): # Store tool results for follow-up call self.tool_results = tool_results + self._tool_results_for_response = self.collected_response except Exception as e: verbose_logger.error(f"Error in tool execution: {e}") @@ -692,10 +704,16 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): traceback.print_exc() self.tool_results = [] + self._tool_results_for_response = self.collected_response async def _create_follow_up_iterator(self) -> None: """Create the follow-up response iterator with tool results""" - if not self.collected_response or not hasattr(self, "tool_results"): + if self.collected_response is None or self.collected_response is not self._tool_results_for_response: + # Either no response to follow up on, or the current round's + # response had no tool calls (self.tool_results is stale from an + # earlier round) — there is nothing to follow up with, so end + # the stream instead of reusing stale tool results. + self.base_iterator = None return from litellm.responses.main import aresponses @@ -725,18 +743,31 @@ class MCPEnhancedStreamingIterator(BaseResponsesAPIStreamingIterator): # Remove tool_choice to avoid forcing more tool calls follow_up_params.pop("tool_choice", None) + if self.tool_call_round >= MAX_MCP_TOOL_CALL_ROUNDS: + # Hit the round cap: drop tools entirely so the model must + # answer in text instead of emitting another (unexecuted) + # tool call that would otherwise end the stream in silence. + follow_up_params.pop("tools", None) + verbose_logger.warning( + "MCP auto-execute hit MAX_MCP_TOOL_CALL_ROUNDS=%s; forcing a text-only follow-up.", + MAX_MCP_TOOL_CALL_ROUNDS, + ) + follow_up_response = await aresponses(**follow_up_params) - # Set up the follow-up iterator + # Route the follow-up through the same base_iterator machinery as + # the initial call so its completion is checked for further tool + # calls (see phase 4 in __anext__). if hasattr(follow_up_response, "__aiter__"): - self.follow_up_iterator = follow_up_response + self.base_iterator = follow_up_response + self.collected_response = None except Exception as e: verbose_logger.error(f"Error creating follow-up iterator: {e}") import traceback traceback.print_exc() - self.follow_up_iterator = None + self.base_iterator = None def __iter__(self): return self diff --git a/litellm/responses/streaming_iterator.py b/litellm/responses/streaming_iterator.py index 6df544dee3e..eb78e6f9c8d 100644 --- a/litellm/responses/streaming_iterator.py +++ b/litellm/responses/streaming_iterator.py @@ -17,6 +17,7 @@ from litellm.constants import ( LITELLM_MAX_STREAMING_DURATION_SECONDS, STREAM_SSE_DONE_STRING, ) +from litellm.exceptions import MidStreamFallbackError from litellm.litellm_core_utils.asyncify import run_async_function from litellm.litellm_core_utils.core_helpers import process_response_headers from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj @@ -26,7 +27,7 @@ from litellm.litellm_core_utils.llm_response_utils.response_metadata import ( ) from litellm.litellm_core_utils.thread_pool_executor import executor from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig -from litellm.responses.utils import ResponsesAPIRequestUtils +from litellm.responses.utils import ResponseAPILoggingUtils, ResponsesAPIRequestUtils from litellm.types.llms.openai import ResponsesAPIStreamEvents from litellm.types.utils import CallTypes from litellm.utils import async_post_call_success_deployment_hook @@ -47,6 +48,44 @@ def _log_background_task_failure(task: "asyncio.Task[Any]", *, task_name: str) - verbose_logger.error("%s failed: %s", task_name, exception) +_CLIENT_ERROR_CODES: frozenset[str] = frozenset( + ( + "invalid_request_error", + "context_length_exceeded", + "content_policy_violation", + "model_not_found", + ) +) + + +def _error_event_fields(error_obj: object) -> tuple[str, Optional[str], Optional[str]]: + if isinstance(error_obj, dict): + raw_message = error_obj.get("message") + raw_type = error_obj.get("type") + raw_code = error_obj.get("code") + elif error_obj is not None: + raw_message = getattr(error_obj, "message", None) + raw_type = getattr(error_obj, "type", None) + raw_code = getattr(error_obj, "code", None) + else: + raw_message = None + raw_type = None + raw_code = None + message = str(raw_message) if raw_message is not None else "Response API in-stream error" + error_type = raw_type if isinstance(raw_type, str) else None + code = raw_code if isinstance(raw_code, str) else None + return message, error_type, code + + +def _status_code_for_error_fields(error_type: Optional[str], error_code: Optional[str]) -> int: + fields = tuple(field for field in (error_type, error_code) if field is not None) + if any(field.startswith("rate_limit") or field == "insufficient_quota" for field in fields): + return 429 + if any(field in _CLIENT_ERROR_CODES for field in fields): + return 400 + return 500 + + class BaseResponsesAPIStreamingIterator: """ Base class for streaming iterators that process responses from the Responses API. @@ -73,6 +112,8 @@ class BaseResponsesAPIStreamingIterator: self.completed_response: Optional[Any] = None self.start_time = getattr(logging_obj, "start_time", datetime.now()) self._failure_handled = False # Track if failure handler has been called + self._yielded_first_chunk = False + self._generated_content = "" self._completed_response_cached = False self._completed_response_logged = False self._completed_response_cache_hit: Optional[bool] = None @@ -128,6 +169,9 @@ class BaseResponsesAPIStreamingIterator: self.finished = True return None + if self.logging_obj.completion_start_time is None: + self.logging_obj._update_completion_start_time(completion_start_time=datetime.now()) + try: # Parse the JSON chunk parsed_chunk = json.loads(chunk) @@ -157,6 +201,10 @@ class BaseResponsesAPIStreamingIterator: # Encode container_id on streaming events so proxy/UI follow-ups route correctly _event_type = getattr(openai_responses_api_chunk, "type", None) + if _event_type == ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA: + _delta = getattr(openai_responses_api_chunk, "delta", None) + if isinstance(_delta, str): + self._generated_content += _delta _stream_model_id = ( self.litellm_metadata.get("model_info", {}).get("id") if self.litellm_metadata else None ) @@ -284,11 +332,12 @@ class BaseResponsesAPIStreamingIterator: end_time = datetime.now() if is_async: asyncio.create_task( - self.logging_obj.async_success_handler( - result=logging_response, + self.logging_obj.dispatch_success_handlers( + logging_response, start_time=self.start_time, end_time=end_time, cache_hit=self._completed_response_cache_hit, + prefer_async_handlers=True, ) ) else: @@ -299,14 +348,13 @@ class BaseResponsesAPIStreamingIterator: end_time=end_time, cache_hit=self._completed_response_cache_hit, ) - - executor.submit( - self.logging_obj.success_handler, - result=logging_response, - cache_hit=self._completed_response_cache_hit, - start_time=self.start_time, - end_time=end_time, - ) + executor.submit( + self.logging_obj.success_handler, + result=logging_response, + cache_hit=self._completed_response_cache_hit, + start_time=self.start_time, + end_time=end_time, + ) self._run_post_success_hooks(end_time=end_time) def _handle_logging_completed_response(self): @@ -324,17 +372,66 @@ class BaseResponsesAPIStreamingIterator: """ response_obj = getattr(self.completed_response, "response", None) if self.completed_response else None error_info = getattr(response_obj, "error", None) if response_obj else None - error_message = "Response failed" - if isinstance(error_info, dict): - error_message = error_info.get("message", str(error_info)) + error_message, error_type, error_code = _error_event_fields(error_info) + self._record_failed_response_usage(response_obj) exception = litellm.APIError( - status_code=500, + status_code=_status_code_for_error_fields(error_type, error_code), message=error_message, llm_provider=self.custom_llm_provider or "", model=self.model or "", ) self._handle_failure(exception) + def _record_failed_response_usage(self, response_obj: Optional[Any]) -> None: + if response_obj is None or self.logging_obj is None: + return + usage_obj = getattr(response_obj, "usage", None) + if usage_obj is None: + return + try: + self.logging_obj.model_call_details["combined_usage_object"] = ( + ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage(usage_obj) + ) + except (TypeError, ValueError) as usage_error: + verbose_logger.debug( + "could not record usage for failed responses stream: %s", + usage_error, + ) + return + self.logging_obj.model_call_details["response_cost"] = ( + self.logging_obj._response_cost_calculator(result=response_obj) or 0.0 + ) + + def _maybe_raise_for_error_event(self, result: object) -> None: + chunk_type = getattr(result, "type", None) + if chunk_type not in ("error", "response.failed"): + return + + error_obj: object = ( + getattr(getattr(result, "response", None), "error", None) + if chunk_type == "response.failed" + else getattr(result, "error", None) + ) + + error_message, error_type, error_code = _error_event_fields(error_obj) + status_code = _status_code_for_error_fields(error_type, error_code) + mapped_exception = litellm.APIError( + status_code=status_code, + message=error_message, + llm_provider=self.custom_llm_provider or "", + model=self.model or "", + ) + if 400 <= status_code < 500 and status_code != 429: + raise mapped_exception + raise MidStreamFallbackError( + message=str(mapped_exception), + model=self.model or "", + llm_provider=self.custom_llm_provider or "", + original_exception=mapped_exception, + generated_content=self._generated_content, + is_pre_first_chunk=not self._yielded_first_chunk, + ) + def _get_completed_response_object(self) -> Optional[Any]: openai_types = _get_openai_response_types() completed_response = self.completed_response @@ -608,11 +705,13 @@ class ResponsesAPIStreamingIterator(BaseResponsesAPIStreamingIterator): if self.finished: raise StopAsyncIteration elif result is not None: + self._maybe_raise_for_error_event(result) # Await hook directly instead of run_async_function # (which spawns a thread + event loop per call) result = await self._call_post_streaming_deployment_hook( chunk=result, ) + self._yielded_first_chunk = True return result # If result is None, continue the loop to get the next chunk @@ -682,11 +781,13 @@ class SyncResponsesAPIStreamingIterator(BaseResponsesAPIStreamingIterator): if self.finished: raise StopIteration elif result is not None: + self._maybe_raise_for_error_event(result) # Sync path: use run_async_function for the hook result = run_async_function( async_function=self._call_post_streaming_deployment_hook, chunk=result, ) + self._yielded_first_chunk = True return result # If result is None, continue the loop to get the next chunk @@ -1133,7 +1234,6 @@ def _build_synthetic_response_events( # --------------------------------------------------------------------------- from litellm._logging import verbose_logger -from litellm.litellm_core_utils.thread_pool_executor import executor as _ws_executor RESPONSES_WS_LOGGED_EVENT_TYPES = [ "response.created", @@ -1248,8 +1348,7 @@ class ResponsesWebSocketStreaming: if self.input_messages: self.logging_obj.model_call_details["messages"] = self.input_messages if self.messages: - asyncio.create_task(self.logging_obj.async_success_handler(self.messages)) - _ws_executor.submit(self.logging_obj.success_handler, self.messages) + asyncio.create_task(self.logging_obj.dispatch_success_handlers(self.messages, prefer_async_handlers=True)) async def backend_to_client(self) -> None: """Forward events from backend WebSocket to the client.""" diff --git a/litellm/responses/utils.py b/litellm/responses/utils.py index cff113dc3e5..234eb777aca 100644 --- a/litellm/responses/utils.py +++ b/litellm/responses/utils.py @@ -204,6 +204,9 @@ class ResponsesAPIRequestUtils: if response_id is None: return responses_api_response + if ResponsesAPIRequestUtils._is_litellm_encoded_response_id(response_id): + return responses_api_response + updated_id = ResponsesAPIRequestUtils._build_responses_api_response_id( model_id=model_id, custom_llm_provider=custom_llm_provider, @@ -470,6 +473,14 @@ class ResponsesAPIRequestUtils: response_id=response_id, ) + @staticmethod + def _is_litellm_encoded_response_id(response_id: str) -> bool: + decoded_response_id = ResponsesAPIRequestUtils._decode_responses_api_response_id(response_id) + return ( + decoded_response_id.get("model_id") is not None + or decoded_response_id.get("custom_llm_provider") is not None + ) + @staticmethod def get_model_id_from_response_id(response_id: Optional[str]) -> Optional[str]: """Get the model_id from the response_id""" diff --git a/litellm/router.py b/litellm/router.py index 2d66bc3158d..6539d3c0c43 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -72,7 +72,11 @@ from litellm.litellm_core_utils.coroutine_checker import coroutine_checker from litellm.litellm_core_utils.credential_accessor import CredentialAccessor from litellm.litellm_core_utils.dd_tracing import tracer from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging -from litellm.litellm_core_utils.sensitive_data_masker import SensitiveDataMasker +from litellm.litellm_core_utils.secret_redaction import redact_string +from litellm.litellm_core_utils.sensitive_data_masker import ( + SensitiveDataMasker, + mask_sensitive_structure, +) from litellm.llms.openai_like.json_loader import JSONProviderRegistry from litellm.router_strategy.budget_limiter import RouterBudgetLimiting from litellm.router_strategy.least_busy import LeastBusyLoggingHandler @@ -86,7 +90,12 @@ from litellm.router_utils.add_retry_fallback_headers import ( _HiddenParamsHost, add_fallback_headers_to_response, add_retry_headers_to_response, + apply_quality_router_decision_headers, + apply_remaining_usage_headers, + ensure_response_additional_headers, get_hidden_params_dict, + prepare_response_for_header_attachment, + response_in_flight_token_count, ) from litellm.router_utils.batch_utils import ( _get_router_metadata_variable_name, @@ -129,6 +138,12 @@ from litellm.router_utils.pre_call_checks.deployment_affinity_check import ( from litellm.router_utils.pre_call_checks.model_rate_limit_check import ( ModelRateLimitingCheck, ) +from litellm.router_utils.pre_call_checks.io_token_rate_limit_check import ( + build_io_token_rate_limit_headers, + deployment_has_io_token_limits, + refund_stale_reservation_before_retry, + set_io_token_rate_limit_request_kwargs, +) from litellm.router_utils.pre_call_checks.prompt_caching_deployment_check import ( PromptCachingDeploymentCheck, ) @@ -164,7 +179,9 @@ from litellm.types.router import ( RouterModelGroupAliasItem, RouterRateLimitError, RouterRateLimitErrorBasic, + RoutingContext, RoutingGroup, + RoutingPlugin, RoutingStrategy, SearchToolTypedDict, ) @@ -284,6 +301,7 @@ class Router: enable_pre_call_checks: bool = False, enable_tag_filtering: bool = False, tag_filtering_match_any: bool = True, + plugins: list[RoutingPlugin] | None = None, retry_after: int = 0, # min time to wait before retrying a failed request retry_policy: Optional[Union[RetryPolicy, dict]] = None, # set custom retries for different exceptions model_group_retry_policy: Dict[str, RetryPolicy] = {}, # set custom retry policies based on model group @@ -462,6 +480,7 @@ class Router: self.complexity_routers: Dict[str, "ComplexityRouter"] = {} self.adaptive_routers: Dict[str, "AdaptiveRouter"] = {} self.quality_routers: Dict[str, "QualityRouter"] = {} + self.routing_plugins: list[RoutingPlugin] = list(plugins) if plugins else [] # Initialize model_group_alias early since it's used in set_model_list self.model_group_alias: Dict[str, Union[str, RouterModelGroupAliasItem]] = ( @@ -841,6 +860,8 @@ class Router: router_cache=self.cache, routing_args={}, ) + case _: + pass if selector is not None and register_callbacks and isinstance(litellm.callbacks, list): litellm.logging_callback_manager.add_litellm_callback(selector) # type: ignore @@ -1114,6 +1135,13 @@ class Router: self.adelete_responses = self.factory_function(litellm.adelete_responses, call_type="adelete_responses") self.alist_input_items = self.factory_function(litellm.alist_input_items, call_type="alist_input_items") self._arealtime = self.factory_function(litellm._arealtime, call_type="_arealtime") + self.acreate_realtime_client_secret = self.factory_function( + litellm.acreate_realtime_client_secret, call_type="acreate_realtime_client_secret" + ) + self.arealtime_calls = self.factory_function(litellm.arealtime_calls, call_type="arealtime_calls") + self.acreate_realtime_transcription_session = self.factory_function( + litellm.acreate_realtime_transcription_session, call_type="acreate_realtime_transcription_session" + ) self._aresponses_websocket = self.factory_function( litellm._aresponses_websocket, call_type="_aresponses_websocket" ) @@ -1636,6 +1664,7 @@ class Router: ) thread.start() + kwargs.setdefault("messages", messages) self._update_kwargs_with_deployment(deployment=deployment, kwargs=kwargs) kwargs.pop("silent_model", None) # Ensure it's not in kwargs either model_name = litellm_params["model"] @@ -2319,6 +2348,8 @@ class Router: self.completed_response = None self.start_time = getattr(source_iterator, "start_time", datetime.now()) self._failure_handled = False + self._yielded_first_chunk = False + self._generated_content = "" self._completed_response_cached = False self._completed_response_logged = False self._completed_response_cache_hit = None @@ -2336,7 +2367,20 @@ class Router: return self async def __anext__(self): - chunk = await self._async_generator.__anext__() + try: + chunk = await self._async_generator.__anext__() + except StopAsyncIteration: + # The inner generator is exhausted. If we never sniffed a + # terminal event off a chunk (the bridge path emits the + # final response.completed via common_done_event_logic, + # which raises StopAsyncIteration after returning it), + # fall back to whatever the source iterator latched so + # the proxy's container-ownership hook still sees a + # completed_response instead of logging a spurious + # "no completed_response" warning. + if self.completed_response is None: + self.completed_response = getattr(source_iterator, "completed_response", None) + raise # Sniff the terminal stream event off each forwarded chunk # so ``self.completed_response`` is populated regardless of # which inner iterator produced it (source_iterator, @@ -2659,6 +2703,7 @@ class Router: ) ) + kwargs.setdefault("messages", messages) self._update_kwargs_with_deployment(deployment=deployment, kwargs=kwargs) kwargs.pop("silent_model", None) # Ensure it's not in kwargs either @@ -2933,6 +2978,13 @@ class Router: } ) + # A retry/fallback reuses this same kwargs dict for the next deployment. + # Refund and clear any reservation the previous deployment attempt left + # here before it's wiped below, instead of relying on that attempt's + # (possibly still-pending) failure event to do it. + refund_stale_reservation_before_retry(self.cache, kwargs) + set_io_token_rate_limit_request_kwargs(kwargs) + ## DEPLOYMENT-LEVEL TAGS deployment_tags = deployment.get("litellm_params", {}).get("tags") if deployment_tags: @@ -5346,6 +5398,9 @@ class Router: "afile_delete", "afile_content", "_arealtime", + "acreate_realtime_client_secret", + "arealtime_calls", + "acreate_realtime_transcription_session", "_aresponses_websocket", "acreate_fine_tuning_job", "acancel_fine_tuning_job", @@ -5585,6 +5640,16 @@ class Router: original_function=original_function, **kwargs, ) + elif call_type in ( + "acreate_realtime_client_secret", + "arealtime_calls", + "acreate_realtime_transcription_session", + ): + return await self._ageneric_api_call_with_fallbacks( + original_function=original_function, + client=client, + **kwargs, + ) elif call_type in ( "anthropic_messages", "_arealtime", @@ -6085,7 +6150,9 @@ class Router: else: error_message = "model={}. context_window_fallbacks={}. fallbacks={}.\n\nSet 'context_window_fallback' - https://docs.litellm.ai/docs/routing#fallbacks".format( - model_group, context_window_fallbacks, fallbacks + model_group, + mask_sensitive_structure(context_window_fallbacks), + mask_sensitive_structure(fallbacks), ) verbose_router_logger.info( msg="Got 'ContextWindowExceededError'. No context_window_fallback set. Defaulting \ @@ -6119,7 +6186,9 @@ class Router: return response else: error_message = "model={}. content_policy_fallback={}. fallbacks={}.\n\nSet 'content_policy_fallback' - https://docs.litellm.ai/docs/routing#fallbacks".format( - model_group, content_policy_fallbacks, fallbacks + model_group, + mask_sensitive_structure(content_policy_fallbacks), + mask_sensitive_structure(fallbacks), ) verbose_router_logger.info( msg="Got 'ContentPolicyViolationError'. No content_policy_fallback set. Defaulting \ @@ -6129,7 +6198,7 @@ class Router: if litellm.expose_router_debug_in_errors: e.message += "\n{}".format(error_message) if fallbacks is not None and model_group is not None: - verbose_router_logger.debug(f"inside model fallbacks: {fallbacks}") + verbose_router_logger.debug(f"inside model fallbacks: {mask_sensitive_structure(fallbacks)}") ( fallback_model_group, generic_fallback_idx, @@ -6142,11 +6211,12 @@ class Router: fallback_model_group = fallbacks[generic_fallback_idx]["*"] if fallback_model_group is None: + masked_fallbacks = mask_sensitive_structure(fallbacks) verbose_router_logger.info( - f"No fallback model group found for original model_group={model_group}. Fallbacks={fallbacks}" + f"No fallback model group found for original model_group={model_group}. Fallbacks={masked_fallbacks}" ) if hasattr(original_exception, "message") and litellm.expose_router_debug_in_errors: - original_exception.message += f"No fallback model group found for original model_group={model_group}. Fallbacks={fallbacks}" # type: ignore + original_exception.message += f"No fallback model group found for original model_group={model_group}. Fallbacks={masked_fallbacks}" # type: ignore raise original_exception input_kwargs.update( @@ -6164,23 +6234,23 @@ class Router: return response except Exception as new_exception: parent_otel_span = _get_parent_otel_span_from_kwargs(kwargs) + fallback_failure_exception_str = redact_string(str(new_exception)) verbose_router_logger.error( "litellm.router.py::async_function_with_fallbacks() - Error occurred while trying to do fallbacks - {}\n{}\n\nDebug Information:\nCooldown Deployments={}".format( - str(new_exception), - traceback.format_exc(), + fallback_failure_exception_str, + redact_string(traceback.format_exc()), await _async_get_cooldown_deployments_with_debug_info( litellm_router_instance=self, parent_otel_span=parent_otel_span, ), ) ) - fallback_failure_exception_str = str(new_exception) if hasattr(original_exception, "message") and litellm.expose_router_debug_in_errors: # add the available fallbacks to the exception original_exception.message += ". Received Model Group={}\nAvailable Model Group Fallbacks={}".format( # type: ignore model_group, - fallback_model_group, + mask_sensitive_structure(fallback_model_group), ) if len(fallback_failure_exception_str) > 0: original_exception.message += ( # type: ignore @@ -6722,7 +6792,13 @@ class Router: deployment_id=id, ) - ## if all are none, return - no need to track current tpm/rpm usage for models with no tpm/rpm set + deployment_dict = deployment_info if isinstance(deployment_info, dict) else deployment_info.model_dump() + has_io_token_limits = deployment_has_io_token_limits(deployment_dict) + + ## Nothing to track only when neither tpm/rpm nor itpm/otpm limits are + ## set. IO deployments still record TPM/RPM usage here so TPM-aware + ## routing strategies see their real load in mixed model groups; their + ## itpm/otpm enforcement runs separately in ModelRateLimitingCheck. if ( tpm is None and rpm is None @@ -6730,6 +6806,7 @@ class Router: and rpm_litellm_params is None and tpm_model_info is None and rpm_model_info is None + and not has_io_token_limits ): return @@ -7323,8 +7400,7 @@ class Router: # deployment sharing the same backend model name. # Each deployment's full pricing is already stored under its # unique model_id above. - _custom_pricing_fields = CustomPricingLiteLLMParams.model_fields.keys() - _shared_model_info = {k: v for k, v in _model_info.items() if k not in _custom_pricing_fields} + _shared_model_info = CustomPricingLiteLLMParams.strip_custom_pricing_fields(_model_info) _existing_shared_mode = (cast(Optional[dict], litellm.model_cost.get(_model_name, {})) or {}).get("mode") _deployment_mode = _shared_model_info.get("mode") # Keep the built-in bridge mode stable for shared backend keys. @@ -7480,7 +7556,11 @@ class Router: if default_model is None and complexity_router_config: tiers = complexity_router_config.get("tiers", {}) # Use MEDIUM tier as fallback default - default_model = tiers.get("MEDIUM") or tiers.get("SIMPLE") + medium = tiers.get("MEDIUM") or tiers.get("SIMPLE") + if isinstance(medium, list): + default_model = medium[0] if medium else None + else: + default_model = medium if default_model is None: raise ValueError( @@ -7517,15 +7597,6 @@ class Router: AdaptiveRouterPostCallHook, ) - for _cb_list in ( - litellm.callbacks, - litellm.success_callback, - litellm.failure_callback, - litellm._async_success_callback, - litellm._async_failure_callback, - ): - litellm.logging_callback_manager.remove_callbacks_by_type(_cb_list, AdaptiveRouterPostCallHook) - for entry in self.model_list or []: lp = entry.get("litellm_params") if isinstance(entry, dict) else entry.litellm_params lp_model = (lp.get("model") if isinstance(lp, dict) else lp.model) if lp else None @@ -7543,6 +7614,20 @@ class Router: ) self.init_adaptive_router_deployment(deployment=deployment) + for model_name, complexity_router in self.complexity_routers.items(): + if not complexity_router.config.adaptive or model_name in self.adaptive_routers: + continue + adaptive_router = complexity_router._ensure_adaptive_router() + if adaptive_router is not None: + self.adaptive_routers[model_name] = adaptive_router + + for callback in litellm.logging_callback_manager.get_custom_loggers_for_type(AdaptiveRouterPostCallHook): + litellm.logging_callback_manager.remove_callback_from_all_lists(callback) + for adaptive_router in self.adaptive_routers.values(): + litellm.logging_callback_manager.add_litellm_callback( + AdaptiveRouterPostCallHook(adaptive_router=adaptive_router) + ) + def init_adaptive_router_deployment(self, deployment: Deployment) -> None: """ Build an AdaptiveRouter instance for this deployment and register its @@ -7988,8 +8073,7 @@ class Router: # deployment sharing the same backend model name. # Each deployment's full pricing is already stored under its # unique model_id above (when present). - _custom_pricing_fields = CustomPricingLiteLLMParams.model_fields.keys() - _shared_model_info = {k: v for k, v in _model_info_dict.items() if k not in _custom_pricing_fields} + _shared_model_info = CustomPricingLiteLLMParams.strip_custom_pricing_fields(_model_info_dict) _backend_alias_cost = {_model_name: _shared_model_info} if "responses/" in _model_name: _stripped_model_name = _model_name.replace("responses/", "") @@ -8566,6 +8650,8 @@ class Router: total_tpm: Optional[int] = None total_rpm: Optional[int] = None + total_itpm: Optional[int] = None + total_otpm: Optional[int] = None configurable_clientside_auth_params: CONFIGURABLE_CLIENTSIDE_AUTH_PARAMS = None model_list = self.get_model_list(model_name=model_group) if model_list is None: @@ -8606,6 +8692,18 @@ class Router: if _deployment_rpm is None: _deployment_rpm = model_info_dict.get("rpm", None) # type: ignore + _deployment_itpm: Optional[int] = model.get("itpm") # type: ignore + if _deployment_itpm is None: + _deployment_itpm = model_litellm_params.get("itpm", None) # type: ignore + if _deployment_itpm is None: + _deployment_itpm = model_info_dict.get("itpm", None) # type: ignore + + _deployment_otpm: Optional[int] = model.get("otpm") # type: ignore + if _deployment_otpm is None: + _deployment_otpm = model_litellm_params.get("otpm", None) # type: ignore + if _deployment_otpm is None: + _deployment_otpm = model_info_dict.get("otpm", None) # type: ignore + # get model info try: model_id = model_info_dict.get("id", None) @@ -8746,6 +8844,16 @@ class Router: if total_rpm is None: total_rpm = 0 total_rpm += _deployment_rpm # type: ignore + + if _deployment_itpm is not None: + if total_itpm is None: + total_itpm = 0 + total_itpm += _deployment_itpm # type: ignore + + if _deployment_otpm is not None: + if total_otpm is None: + total_otpm = 0 + total_otpm += _deployment_otpm # type: ignore if model_group_info is not None: ## UPDATE WITH TOTAL TPM/RPM FOR MODEL GROUP if total_tpm is not None: @@ -8754,6 +8862,12 @@ class Router: if total_rpm is not None: model_group_info.rpm = total_rpm + if total_itpm is not None: + model_group_info.itpm = total_itpm + + if total_otpm is not None: + model_group_info.otpm = total_otpm + ## UPDATE WITH CONFIGURABLE CLIENTSIDE AUTH PARAMS FOR MODEL GROUP if configurable_clientside_auth_params is not None: model_group_info.configurable_clientside_auth_params = configurable_clientside_auth_params @@ -8856,6 +8970,58 @@ class Router: rpm_usage += t return tpm_usage, rpm_usage + async def get_model_group_io_token_usage(self, model_group: str) -> tuple[Optional[int], Optional[int]]: + """ + Returns current ITPM/OTPM usage for a model group (sum across deployments). + """ + dt = get_utc_datetime() + current_minute = dt.strftime("%H-%M") + itpm_keys: list[str] = [] + otpm_keys: list[str] = [] + + model_list = self.get_model_list(model_name=model_group) + if model_list is None: + return None, None + + for model in model_list: + model_id: Optional[str] = model.get("model_info", {}).get("id") # type: ignore + litellm_model: Optional[str] = model["litellm_params"].get("model") + if model_id is None or litellm_model is None: + continue + itpm_keys.append( + RouterCacheEnum.ITPM.value.format( + id=model_id, + model=litellm_model, + current_minute=current_minute, + ) + ) + otpm_keys.append( + RouterCacheEnum.OTPM.value.format( + id=model_id, + model=litellm_model, + current_minute=current_minute, + ) + ) + + combined_values = await self.cache.async_batch_get_cache(keys=itpm_keys + otpm_keys) + if combined_values is None: + return None, None + + itpm_values = combined_values[: len(itpm_keys)] + otpm_values = combined_values[len(itpm_keys) :] + + total_itpm: Optional[int] = None + for value in itpm_values: + if isinstance(value, int): + total_itpm = (total_itpm or 0) + value + + total_otpm: Optional[int] = None + for value in otpm_values: + if isinstance(value, int): + total_otpm = (total_otpm or 0) + value + + return total_itpm, total_otpm + @lru_cache(maxsize=DEFAULT_MAX_LRU_CACHE_SIZE) def _cached_get_model_group_info(self, model_group: str) -> Optional[ModelGroupInfo]: """ @@ -8865,25 +9031,33 @@ class Router: """ return self.get_model_group_info(model_group) - async def get_remaining_model_group_usage(self, model_group: str) -> Dict[str, int]: + async def get_remaining_model_group_usage(self, model_group: str) -> dict[str, int]: model_group_info = self._cached_get_model_group_info(model_group) - if model_group_info is not None and model_group_info.tpm is not None: - tpm_limit = model_group_info.tpm - else: - tpm_limit = None + returned_dict: dict[str, int] = {} - if model_group_info is not None and model_group_info.rpm is not None: - rpm_limit = model_group_info.rpm - else: - rpm_limit = None + # ITPM/OTPM groups emit input/output token headers, but they may also set + # tpm/rpm, so build both sets rather than returning early - clients and + # prometheus gauges that read the standard headers still get data. + if model_group_info is not None and (model_group_info.itpm is not None or model_group_info.otpm is not None): + current_itpm, current_otpm = await self.get_model_group_io_token_usage(model_group) + returned_dict.update( + build_io_token_rate_limit_headers( + itpm_limit=model_group_info.itpm, + otpm_limit=model_group_info.otpm, + current_itpm=current_itpm, + current_otpm=current_otpm, + ) + ) + + tpm_limit = model_group_info.tpm if model_group_info is not None else None + rpm_limit = model_group_info.rpm if model_group_info is not None else None if tpm_limit is None and rpm_limit is None: - return {} + return returned_dict current_tpm, current_rpm = await self.get_model_group_usage(model_group) - returned_dict = {} if tpm_limit is not None: returned_dict["x-ratelimit-remaining-tokens"] = tpm_limit - (current_tpm or 0) returned_dict["x-ratelimit-limit-tokens"] = tpm_limit @@ -8906,69 +9080,25 @@ class Router: # - if healthy_deployments > 1, return model group rate limit headers # - else return the model's rate limit headers """ - if response is not None and hasattr(response, "_hidden_params"): - hidden_params = getattr(response, "_hidden_params", {}) or {} - if hasattr(hidden_params, "model_dump"): - hidden_params = hidden_params.model_dump() - if not isinstance(hidden_params, dict): - return response - response._hidden_params = hidden_params + response = prepare_response_for_header_attachment(response) + if response is None: + return response - additional_headers = hidden_params.get("additional_headers") - if not isinstance(additional_headers, dict): - additional_headers = {} - hidden_params["additional_headers"] = additional_headers - additional_headers["x-litellm-model-group"] = model_group + additional_headers = ensure_response_additional_headers(response) + additional_headers["x-litellm-model-group"] = model_group + apply_quality_router_decision_headers(additional_headers, request_kwargs) - # Lift QualityRouter routing decision into response headers for - # transparency. The decision is stashed in request_kwargs.metadata - # by QualityRouter.async_pre_routing_hook. - metadata = (request_kwargs.get("metadata") or {}) if isinstance(request_kwargs, dict) else {} - decision = metadata.get("quality_router_decision") if isinstance(metadata, dict) else None - if isinstance(decision, dict): - # Only emit headers for fields that have a meaningful value. - # `complexity_tier` and `matched_keyword` are mutually exclusive - # (the keyword path short-circuits classification), so each - # request emits one or the other but not both. - if decision.get("routed_model") is not None: - additional_headers["x-litellm-quality-router-model"] = str(decision["routed_model"]) - if decision.get("quality_tier") is not None: - additional_headers["x-litellm-quality-router-tier"] = str(decision["quality_tier"]) - if decision.get("routed_via") is not None: - additional_headers["x-litellm-quality-router-via"] = str(decision["routed_via"]) - if decision.get("matched_keyword") is not None: - additional_headers["x-litellm-quality-router-keyword"] = str(decision["matched_keyword"]) - if decision.get("complexity_tier") is not None: - additional_headers["x-litellm-quality-router-complexity"] = str(decision["complexity_tier"]) - - if ( - "x-ratelimit-remaining-tokens" not in additional_headers - and "x-ratelimit-remaining-requests" not in additional_headers - and model_group is not None - ): - remaining_usage = await self.get_remaining_model_group_usage(model_group) - - # get_remaining_model_group_usage reads the router's TPM/RPM - # counter, which is incremented post-response by - # deployment_callback_on_success. So the values returned here - # are pre-decrement for the current request, while vendor - # headers (OpenAI/Anthropic/Azure) are post-decrement. Replay - # the in-flight increment so router-derived headers match - # vendor-derived semantics — for both the HTTP response sent - # to the client and the prometheus gauges that read these - # headers downstream (LIT-2719). - in_flight_tokens = 0 - usage = getattr(response, "usage", None) - if usage is not None: - in_flight_tokens = getattr(usage, "total_tokens", 0) or 0 - in_flight_delta = { - "x-ratelimit-remaining-tokens": in_flight_tokens, - "x-ratelimit-remaining-requests": 1, - } - - for header, value in remaining_usage.items(): - if value is not None: - additional_headers[header] = value - in_flight_delta.get(header, 0) + if model_group is not None: + remaining_usage = await self.get_remaining_model_group_usage(model_group) + # get_remaining_model_group_usage reads the router's TPM/RPM counter, + # which is incremented post-response by deployment_callback_on_success. + # Replay the in-flight increment for TPM/RPM only (LIT-2719); ITPM/OTPM + # counters are incremented at reservation time and must not be adjusted. + apply_remaining_usage_headers( + additional_headers, + remaining_usage, + response_in_flight_token_count(response), + ) return response def _build_model_name_index(self, model_list: list) -> None: @@ -10204,6 +10334,12 @@ class Router: metadata_variable_name=self._get_metadata_variable_name_from_kwargs(request_kwargs), ) + # narrow to whatever `self.routing_plugins` left in candidate_models + healthy_deployments = self._filter_by_routing_plugin_candidates( + healthy_deployments=healthy_deployments, + request_kwargs=request_kwargs, + ) + ## ORDER FILTERING ## -> if user set 'order' in deployments, return deployments with lowest order (e.g. order=1 > order=2) _target_order = (request_kwargs or {}).pop("_target_order", None) healthy_deployments = litellm.utils._get_order_filtered_deployments( @@ -10479,6 +10615,76 @@ class Router: ) raise e + async def _run_routing_plugins( + self, + model: str, + request_kwargs: dict, + messages: list[dict[str, Any]] | None, + ) -> RoutingContext: + """ + Build a RoutingContext for `model`, run it through `self.routing_plugins` + in order, then stash the narrowed candidate list and accumulated signals + onto `request_kwargs["metadata"]` so `_filter_by_routing_plugin_candidates` + (called later, during healthy-deployment filtering) can consume them. + """ + from litellm.litellm_core_utils.prompt_templates.factory import ( + resolve_structured_messages, + ) + + deployments = self.get_model_list(model_name=model) or [] + candidate_models = [ + d["litellm_params"]["model"] for d in deployments if d.get("litellm_params", {}).get("model") + ] + + metadata_key = self._get_metadata_variable_name_from_kwargs(request_kwargs) + metadata = request_kwargs.setdefault(metadata_key, {}) + + context = RoutingContext( + raw_messages=messages or [], + structured_messages=resolve_structured_messages(messages=messages, request_kwargs=request_kwargs) or [], + candidate_models=candidate_models, + metadata=metadata, + ) + + for plugin in self.routing_plugins: + context = await plugin.run(context) + + metadata["routing_plugin_signals"] = context.signals + if len(context.candidate_models) < len(candidate_models): + metadata["_routing_plugin_candidate_models"] = context.candidate_models + + return context + + def _filter_by_routing_plugin_candidates( + self, + healthy_deployments: Union[list[dict], dict], + request_kwargs: dict, + ) -> Union[list[dict], dict]: + """ + Narrow `healthy_deployments` to whatever `self.routing_plugins` left in + `context.candidate_models`. Raises rather than silently falling back to + the unfiltered pool -- a plugin narrowing to nothing is a policy decision + (e.g. no model this tenant's budget allows), not something to bypass. + """ + if not self.routing_plugins or not isinstance(healthy_deployments, list): + return healthy_deployments + + metadata_key = self._get_metadata_variable_name_from_kwargs(request_kwargs) + candidate_models = (request_kwargs.get(metadata_key) or {}).get("_routing_plugin_candidate_models") + # `is None` (not falsy-check): a plugin narrowing to an empty list must + # still hit the "no deployments left" raise below, not be treated the + # same as "no plugin ever set this key". + if candidate_models is None: + return healthy_deployments + + candidate_set = set(candidate_models) + filtered = [d for d in healthy_deployments if d.get("litellm_params", {}).get("model") in candidate_set] + + if not filtered: + raise ValueError(f"No deployments left after routing-plugin filtering. candidate_models={candidate_models}") + + return filtered + async def async_pre_routing_hook( self, model: str, @@ -10492,6 +10698,15 @@ class Router: Used for the litellm auto-router to modify the request before the routing decision is made. """ + ######################################################### + # Run the routing-plugin pipeline, if any plugins are configured. + # Plugins narrow the candidate deployment pool (consumed later by + # `_filter_by_routing_plugin_candidates`) and may attach signals for + # downstream strategies (auto-router, complexity-router, ...) to read. + ######################################################### + if self.routing_plugins: + await self._run_routing_plugins(model=model, request_kwargs=request_kwargs, messages=messages) + ######################################################### # Check if any auto-router should be used ######################################################### @@ -10554,6 +10769,18 @@ class Router: """ Returns the deployment based on routing strategy """ + if self.routing_plugins: + raise ValueError( + "Router(plugins=[...]) is configured, but this call resolved to the synchronous " + "deployment-selection path, which never runs the routing-plugin pipeline. This " + "happens for sync Router methods (e.g. Router.completion()) and for async calls " + "with a routing_strategy that has no async-native selector (e.g. legacy " + "'usage-based-routing', v1). Silently skipping " + "configured plugins would let a policy plugin (e.g. a deny-all rule) be bypassed. " + "Use an async Router method with a supported routing_strategy (simple-shuffle, " + "usage-based-routing-v2, cost-based-routing, latency-based-routing, least-busy), " + "or remove `plugins` from the Router config." + ) # users need to explicitly call a specific deployment, by setting `specific_deployment = True` as completion()/embedding() kwarg # When this was no explicit we had several issues with fallbacks timing out diff --git a/litellm/router_strategy/adaptive_router/README.md b/litellm/router_strategy/adaptive_router/README.md index 7f5d7aa21d0..09420a8dd9d 100644 --- a/litellm/router_strategy/adaptive_router/README.md +++ b/litellm/router_strategy/adaptive_router/README.md @@ -56,11 +56,10 @@ Callers may pass header `x-litellm-min-quality-tier: 3` (or metadata key - **Per-request decision.** Sample once per eligible model, score with `quality_weight·sample + cost_weight·normalized_cost`, pick the argmax. Routing is stateless per-turn — no sticky lookup. Each call resamples. -- **Owner-cache attribution.** Post-call, the conversation's first picked - model claims an "owner slot" for `OWNER_CACHE_TTL_SECONDS` (24h). Later - turns of the same conversation only fire bandit/state updates if the - same model handled them — mismatches are dropped (no attribution) and - counted in `skipped_updates_total`. Conversation identity is the +- **Previous-response attribution.** Post-call, feedback from the current user + message is attributed to the model that produced the previous response, while + response signals are attributed to the current model. Contexts expire after + 24 hours and the in-memory cache is capped at 1,024 sessions. Conversation identity is the client-supplied `litellm_session_id` if present, otherwise a sha256 over caller identity (api key hash, team, user, end-user) + the first message. - **Per-turn updates.** `satisfaction → +α`. `misalignment, stagnation, @@ -76,12 +75,6 @@ Callers may pass header `x-litellm-min-quality-tier: 3` (or metadata key model can still be picked. - **Hard sample cap at 200.** Once `α + β > 200`, deltas are silently dropped. No rescaling — drift is a v1 concern. -- **24h owner-cache TTL.** No explicit eviction below TTL. The in-memory map - can grow if traffic patterns produce many one-shot sessions. -- **Owner-recovery skew.** If model A "owns" a conversation but is then - dethroned in the bandit, later turns served by model B are dropped — so - bandit updates for that conversation flatline until A's TTL expires. - Tracked via `skipped_updates_total`. - **Signals are regex + tool-call only.** No LLM-judge, no embedding similarity, no exemplar storage. Signals are best-effort and biased toward English. - **One AdaptiveRouter per `Router`.** Multiple `adaptive_router/*` deployments diff --git a/litellm/router_strategy/adaptive_router/adaptive_router.py b/litellm/router_strategy/adaptive_router/adaptive_router.py index 69d6a019e68..ec84eb1decf 100644 --- a/litellm/router_strategy/adaptive_router/adaptive_router.py +++ b/litellm/router_strategy/adaptive_router/adaptive_router.py @@ -3,25 +3,21 @@ Main adaptive router strategy. See README.md for design overview. One AdaptiveRouter instance per router_name. Holds in-memory caches: - _cells: Beta(alpha, beta) bandit posteriors per (request_type, model) -- _owner_cache: session_key -> (owner_model, expires_at) — the first model - picked for a conversation owns its bandit-update slot - _session_states: (session_key, model) -> SessionState for incremental signal updates Owns the AdaptiveRouterUpdateQueue used by the proxy's flusher to persist state and session snapshots back to Postgres. -Routing is stateless per-turn (Thompson sample fresh on every call). The -owner cache is consulted only at post-call time to decide whether a turn's -signals should fire a bandit update — turns served by a different model than -the conversation's owner are skipped to avoid cross-model misattribution. +Routing is stateless per-turn (Thompson sample fresh on every call). """ from __future__ import annotations import asyncio import time -from dataclasses import asdict -from typing import Any, Dict, List, Optional, Tuple, Union, cast +from collections import OrderedDict +from dataclasses import asdict, dataclass +from typing import Any, Union, cast from litellm._logging import verbose_router_logger from litellm.litellm_core_utils.prompt_templates.common_utils import ( @@ -38,13 +34,18 @@ from litellm.router_strategy.adaptive_router.config import ( ADAPTIVE_ROUTER_CHOSEN_MODEL_KEY, MIN_QUALITY_TIER_HEADER, MIN_QUALITY_TIER_METADATA_KEY, + MIN_TURNS_FOR_CLEAN_CREDIT, OWNER_CACHE_TTL_SECONDS, ) from litellm.router_strategy.adaptive_router.signals import ( SessionState, SignalDelta, Turn, - apply_turn, + advance_session_state, + apply_signal_delta, + detect_response_signals, + detect_user_feedback, + merge_signal_deltas, ) from litellm.router_strategy.adaptive_router.update_queue import ( AdaptiveRouterUpdateQueue, @@ -53,8 +54,7 @@ from litellm.router_strategy.adaptive_router.update_queue import ( # Sweep session-state cache when it exceeds this many live entries. Expired # entries are dropped in bulk; amortizes to O(1) per insert. _SESSION_STATE_SWEEP_THRESHOLD: int = 1024 -# Same pattern for the owner cache. -_OWNER_CACHE_SWEEP_THRESHOLD: int = 1024 +_FEEDBACK_CONTEXT_MAX_ENTRIES: int = 1024 from litellm.repositories.table_repositories import AdaptiveRouterStateRepository from litellm.types.llms.openai import AllMessageValues from litellm.types.router import ( @@ -70,6 +70,17 @@ def _default_prefs() -> AdaptiveRouterPreferences: return AdaptiveRouterPreferences(quality_tier=2, strengths=[]) +@dataclass(frozen=True, slots=True) +class _FeedbackContext: + model_name: str + request_type: RequestType + user_content: str | None + assistant_content: str | None + turn_count: int + clean_credit_awarded: bool + expires_at: float + + class AdaptiveRouter: """One instance per router_name. Holds in-memory caches + the update queue.""" @@ -77,8 +88,8 @@ class AdaptiveRouter: self, router_name: str, config: AdaptiveRouterConfig, - model_to_prefs: Dict[str, AdaptiveRouterPreferences], - model_to_cost: Dict[str, float], + model_to_prefs: dict[str, AdaptiveRouterPreferences], + model_to_cost: dict[str, float], ) -> None: self.router_name = router_name self.config = config @@ -86,13 +97,14 @@ class AdaptiveRouter: self.model_to_cost = model_to_cost self.queue = AdaptiveRouterUpdateQueue() - self._cells: Dict[Tuple[RequestType, str], BanditCell] = {} - self._owner_cache: Dict[str, Tuple[str, float]] = {} - self._session_states: Dict[Tuple[str, str], SessionState] = {} - # Parallel expiry map for _session_states, same TTL as _owner_cache. - # Evicted opportunistically in `get_or_create_session_state`. - self._session_states_expiry: Dict[Tuple[str, str], float] = {} - self._skipped_updates_total: int = 0 + self._cells: dict[tuple[RequestType, str], BanditCell] = {} + self._session_states: dict[tuple[str, str], SessionState] = {} + self._feedback_contexts: OrderedDict[str, _FeedbackContext] = OrderedDict() + self._session_states_expiry: dict[tuple[str, str], float] = {} + self._feedback_attributed_total: int = 0 + self._feedback_without_context_total: int = 0 + self._cross_model_feedback_total: int = 0 + self._response_signal_updates_total: int = 0 # Set to True once the proxy flusher has loaded persisted priors from # Postgres. Checked to support lazy-load on hot-reloaded routers. self._state_loaded: bool = False @@ -145,11 +157,11 @@ class AdaptiveRouter: async def async_pre_routing_hook( self, model: str, - request_kwargs: Dict[str, Any], - messages: Optional[List[Dict[str, Any]]] = None, - input: Optional[Union[str, List]] = None, - specific_deployment: Optional[bool] = False, - ) -> Optional[PreRoutingHookResponse]: + request_kwargs: dict[str, Any], + messages: list[dict[str, Any]] | None = None, + input: Union[str, list] | None = None, + specific_deployment: bool | None = False, + ) -> PreRoutingHookResponse | None: """ Plugin entry point invoked by `Router.async_pre_routing_hook` when the inbound `model` matches this adaptive router's `router_name`. @@ -159,11 +171,9 @@ class AdaptiveRouter: post-call hook can surface it as a response header. Routing is stateless per-turn: every call Thompson-samples fresh, - regardless of any prior pick for the same session. Cross-turn - attribution is enforced post-call via the owner cache (see - `claim_or_check_owner`). + regardless of any prior pick for the same session. """ - user_text = get_last_user_message(cast(List[AllMessageValues], messages or [])) or "" + user_text = get_last_user_message(cast(list[AllMessageValues], messages or [])) or "" request_type = classify_prompt(user_text) min_quality_tier = self._extract_min_quality_tier(request_kwargs) @@ -190,7 +200,7 @@ class AdaptiveRouter: async def pick_model( self, request_type: RequestType, - min_quality_tier: Optional[int] = None, + min_quality_tier: int | None = None, ) -> str: """Thompson-sample across eligible models. Stateless per-turn.""" eligible = self._eligible_models(min_quality_tier) @@ -206,44 +216,7 @@ class AdaptiveRouter: cost_weight=self.config.weights.cost, ) - def claim_or_check_owner(self, session_key: str, current_model: str) -> bool: - """Resolve attribution for a turn under stateless routing. - - Returns True iff this turn should fire a bandit/state update. The - first call for a `session_key` claims ownership for `current_model` - and returns True. Subsequent calls return True only if the owner is - still live AND matches `current_model`. Mismatches (a different - model handled this turn) and expired owners both increment - `_skipped_updates_total` and return False — no attribution. - """ - now = time.time() - existing = self._owner_cache.get(session_key) - if existing is not None and existing[1] > now: - owner_model, _ = existing - if owner_model == current_model: - return True - self._skipped_updates_total += 1 - return False - - # Opportunistic bulk sweep — sessions that never come back would - # otherwise pile up here forever. Same threshold pattern as the - # session-state cache. - if len(self._owner_cache) >= _OWNER_CACHE_SWEEP_THRESHOLD: - self._evict_expired_owner_cache(now) - - # No live owner -> claim for current_model. - self._owner_cache[session_key] = ( - current_model, - now + OWNER_CACHE_TTL_SECONDS, - ) - return True - - def _evict_expired_owner_cache(self, now: float) -> None: - expired = [k for k, (_, exp) in self._owner_cache.items() if exp <= now] - for k in expired: - self._owner_cache.pop(k, None) - - async def get_state_snapshot(self) -> Dict[str, Any]: + async def get_state_snapshot(self) -> dict[str, Any]: """In-memory snapshot for the introspection endpoint. Cheap; no DB hit.""" cells = [] for (rt, model), cell in sorted(self._cells.items(), key=lambda kv: (kv[0][0].value, kv[0][1])): @@ -264,7 +237,7 @@ class AdaptiveRouter: ) queue = await self.queue.queue_size() now = time.time() - owner_cache_live = sum(1 for _, exp in self._owner_cache.values() if exp > now) + feedback_contexts_live = sum(1 for context in self._feedback_contexts.values() if context.expires_at > now) return { "router_name": self.router_name, "available_models": list(self.config.available_models), @@ -274,15 +247,18 @@ class AdaptiveRouter: }, "model_costs": dict(self.model_to_cost), "cells": cells, - "owner_cache_live": owner_cache_live, - "skipped_updates_total": self._skipped_updates_total, + "feedback_contexts_live": feedback_contexts_live, + "feedback_attributed_total": self._feedback_attributed_total, + "feedback_without_context_total": self._feedback_without_context_total, + "cross_model_feedback_total": self._cross_model_feedback_total, + "response_signal_updates_total": self._response_signal_updates_total, "queue": queue, } @staticmethod def _extract_min_quality_tier( - request_kwargs: Dict[str, Any], - ) -> Optional[int]: + request_kwargs: dict[str, Any], + ) -> int | None: """Pull `min_quality_tier` from request headers or metadata. Precedence: headers (`x-litellm-min-quality-tier`) over metadata @@ -310,7 +286,7 @@ class AdaptiveRouter: return None return None - def _eligible_models(self, min_quality_tier: Optional[int]) -> List[str]: + def _eligible_models(self, min_quality_tier: int | None) -> list[str]: if min_quality_tier is None: return list(self.config.available_models) return [ @@ -363,17 +339,131 @@ class AdaptiveRouter: request_type: RequestType, turn: Turn, ) -> SignalDelta: - """Apply one turn, push session snapshot + bandit deltas to the queue.""" - state = self.get_or_create_session_state(session_id, model_name, request_type) - delta = apply_turn(state, turn) - verbose_router_logger.debug("AdaptiveRouter[%s]: record_turn delta=%s", self.router_name, delta) + """Attribute feedback to the previous response and response signals to the current model.""" + async with self._lock: + now = time.time() + while self._feedback_contexts: + oldest_context = next(iter(self._feedback_contexts.values())) + if oldest_context.expires_at > now: + break + self._feedback_contexts.popitem(last=False) + previous = self._feedback_contexts.pop(session_id, None) - # Strip the raw conversation content before persisting. The - # last_user/assistant_content and tool_call_history fields are only - # needed in-memory for the next turn's incremental signal detection; - # writing user prompts and tool payloads to the DB would store PII - # for every adaptive-router conversation. Counts + bookkeeping is - # all the persisted row needs. + effective_request_type = ( + previous.request_type if previous is not None and request_type == RequestType.GENERAL else request_type + ) + current_state = self.get_or_create_session_state( + session_id, + model_name, + effective_request_type, + ) + feedback_delta = detect_user_feedback( + previous.user_content if previous else None, + turn.user_content, + turn.tool_results, + allow_satisfaction=( + previous is not None + and not previous.clean_credit_awarded + and previous.turn_count + 1 >= MIN_TURNS_FOR_CLEAN_CREDIT + ), + ) + previous_assistant = previous.assistant_content if previous else None + response_delta = detect_response_signals( + previous_assistant, + turn.assistant_content, + current_state.tool_call_history, + turn.tool_calls, + turn.tool_results, + turn.response_status, + ) + states_to_persist: dict[str, SessionState] = {model_name: current_state} + bandit_deltas: dict[tuple[RequestType, str], SignalDelta] = {} + + if previous is not None: + feedback_state = self.get_or_create_session_state( + session_id, + previous.model_name, + previous.request_type, + ) + apply_signal_delta(feedback_state, feedback_delta) + if feedback_delta.satisfaction: + feedback_state.clean_credit_awarded = True + states_to_persist[previous.model_name] = feedback_state + if feedback_delta.any_fired(): + self._feedback_attributed_total += 1 + if previous.model_name != model_name: + self._cross_model_feedback_total += 1 + bandit_deltas[(previous.request_type, previous.model_name)] = feedback_delta + else: + if feedback_delta.any_fired(): + self._feedback_without_context_total += 1 + initial_failure = SignalDelta(failure=feedback_delta.failure) + apply_signal_delta(current_state, initial_failure) + bandit_deltas[(effective_request_type, model_name)] = initial_failure + + apply_signal_delta(current_state, response_delta) + if self._compute_bandit_delta(response_delta) != (0.0, 0.0): + self._response_signal_updates_total += 1 + current_key = (effective_request_type, model_name) + bandit_deltas[current_key] = merge_signal_deltas( + bandit_deltas.get(current_key, SignalDelta()), + response_delta, + ) + advance_session_state(current_state, turn) + + next_turn_count = (previous.turn_count if previous else 0) + 1 + clean_credit_awarded = bool((previous and previous.clean_credit_awarded) or feedback_delta.satisfaction) + if len(self._feedback_contexts) >= _FEEDBACK_CONTEXT_MAX_ENTRIES: + self._feedback_contexts.popitem(last=False) + self._feedback_contexts[session_id] = _FeedbackContext( + model_name=model_name, + request_type=effective_request_type, + user_content=turn.user_content, + assistant_content=turn.assistant_content, + turn_count=next_turn_count, + clean_credit_awarded=clean_credit_awarded, + expires_at=now + OWNER_CACHE_TTL_SECONDS, + ) + + for state_model, state in states_to_persist.items(): + await self.queue.add_session_state( + session_id, + self.router_name, + state_model, + self._persistable_session_snapshot(state), + ) + + combined_delta = SignalDelta() + for (attribution_type, target_model), delta in bandit_deltas.items(): + combined_delta = merge_signal_deltas(combined_delta, delta) + d_alpha, d_beta = self._compute_bandit_delta(delta) + if d_alpha == 0 and d_beta == 0: + continue + cell_key = (attribution_type, target_model) + self._cells[cell_key] = apply_delta( + self._cells[cell_key], + d_alpha, + d_beta, + ) + await self.queue.add_state_delta( + self.router_name, + attribution_type.value, + target_model, + d_alpha, + d_beta, + ) + + verbose_router_logger.debug( + "AdaptiveRouter[%s]: feedback_target=%s current_model=%s delta=%s", + self.router_name, + previous.model_name if previous else None, + model_name, + combined_delta, + ) + return combined_delta + + @staticmethod + def _persistable_session_snapshot(state: SessionState) -> dict[str, Any]: snapshot = asdict(state) for sensitive in ( "last_user_content", @@ -382,38 +472,10 @@ class AdaptiveRouter: "pending_tool_calls", ): snapshot.pop(sensitive, None) - await self.queue.add_session_state(session_id, self.router_name, model_name, snapshot) - - d_alpha, d_beta = self._compute_bandit_delta(delta) - verbose_router_logger.debug( - "AdaptiveRouter[%s]: bandit delta alpha=%.2f beta=%.2f", - self.router_name, - d_alpha, - d_beta, - ) - if d_alpha != 0 or d_beta != 0: - # For non-GENERAL turns, attribute to the current-turn classification - # so genuine mid-session topic shifts (e.g. code → math) update the - # correct cell. For GENERAL turns ("thanks!", "ok", "sounds good"), fall - # back to the session's original type so closing pleasantries don't - # misattribute the reward. - attribution_type = ( - request_type if request_type != RequestType.GENERAL else RequestType(state.classified_type) - ) - cell_key = (attribution_type, model_name) - self._cells[cell_key] = apply_delta(self._cells[cell_key], d_alpha, d_beta) - await self.queue.add_state_delta( - self.router_name, - attribution_type.value, - model_name, - d_alpha, - d_beta, - ) - - return delta + return snapshot @staticmethod - def _compute_bandit_delta(delta: SignalDelta) -> Tuple[float, float]: + def _compute_bandit_delta(delta: SignalDelta) -> tuple[float, float]: """ Translate per-turn signal deltas into bandit-cell deltas. diff --git a/litellm/router_strategy/adaptive_router/hooks.py b/litellm/router_strategy/adaptive_router/hooks.py index c3e3f8ca74a..89ae28be227 100644 --- a/litellm/router_strategy/adaptive_router/hooks.py +++ b/litellm/router_strategy/adaptive_router/hooks.py @@ -214,10 +214,6 @@ class AdaptiveRouterPostCallHook(CustomLogger): ) -> None: try: messages = kwargs.get("messages") or [] - if len(messages) < SIGNAL_GATE_MIN_MESSAGES: - # Too few turns for any signal to be meaningful — skip. - return - session_key = _resolve_session_key(kwargs) if not session_key: return @@ -233,10 +229,6 @@ class AdaptiveRouterPostCallHook(CustomLogger): if not current_model: return - if not self.adaptive_router.claim_or_check_owner(session_key, current_model): - # A different model owns this conversation — skip attribution. - return - user_text = _last_user_content(messages) assistant_text, tool_calls = _assistant_content_and_tool_calls(response_obj) tool_results = _recent_tool_results(messages) diff --git a/litellm/router_strategy/adaptive_router/signals.py b/litellm/router_strategy/adaptive_router/signals.py index 2fd1d24fbbe..74fa8936098 100644 --- a/litellm/router_strategy/adaptive_router/signals.py +++ b/litellm/router_strategy/adaptive_router/signals.py @@ -14,7 +14,7 @@ from __future__ import annotations import re from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional, Set +from typing import Any from litellm.router_strategy.adaptive_router.config import ( LOOP_REPEAT_THRESHOLD, @@ -74,26 +74,26 @@ class SessionState: loop_count: int = 0 exhaustion_count: int = 0 - last_user_content: Optional[str] = None - last_assistant_content: Optional[str] = None - tool_call_history: List[str] = field(default_factory=list) - pending_tool_calls: Dict[str, str] = field(default_factory=dict) + last_user_content: str | None = None + last_assistant_content: str | None = None + tool_call_history: list[str] = field(default_factory=list) + pending_tool_calls: dict[str, str] = field(default_factory=dict) turn_count: int = 0 last_processed_turn: int = -1 clean_credit_awarded: bool = False - terminal_status: Optional[int] = None + terminal_status: int | None = None @dataclass class Turn: """One turn of input. Caller assembles this from the request/response.""" - user_content: Optional[str] = None - assistant_content: Optional[str] = None - tool_calls: List[Dict[str, Any]] = field(default_factory=list) - tool_results: List[Dict[str, Any]] = field(default_factory=list) - response_status: Optional[int] = None + user_content: str | None = None + assistant_content: str | None = None + tool_calls: list[dict[str, Any]] = field(default_factory=list) + tool_results: list[dict[str, Any]] = field(default_factory=list) + response_status: int | None = None # ---- Detection helpers ---------------------------------------------------- @@ -101,13 +101,13 @@ class Turn: _TOKEN_RE = re.compile(r"[A-Za-z0-9]+") -def _tokens(text: Optional[str]) -> Set[str]: +def _tokens(text: str | None) -> set[str]: if not text: return set() return {t.lower() for t in _TOKEN_RE.findall(text)} -def _jaccard(a: Set[str], b: Set[str]) -> float: +def _jaccard(a: set[str], b: set[str]) -> float: union = a | b if not union: return 0.0 @@ -130,7 +130,7 @@ _SATISFACTION_PATTERNS = [ ] -def _detect_misalignment(prev_user: Optional[str], curr_user: Optional[str]) -> bool: +def _detect_misalignment(prev_user: str | None, curr_user: str | None) -> bool: """Fires when consecutive user messages share *some* topic (jaccard > 0) but are sufficiently different (jaccard < threshold) — i.e. user is rephrasing, not changing topic, not repeating.""" @@ -140,7 +140,7 @@ def _detect_misalignment(prev_user: Optional[str], curr_user: Optional[str]) -> return 0.0 < j < MISALIGNMENT_JACCARD_THRESHOLD -def _detect_stagnation(prev_asst: Optional[str], curr_asst: Optional[str]) -> bool: +def _detect_stagnation(prev_asst: str | None, curr_asst: str | None) -> bool: """Fires when consecutive assistant messages are near-duplicates.""" if not prev_asst or not curr_asst: return False @@ -148,19 +148,19 @@ def _detect_stagnation(prev_asst: Optional[str], curr_asst: Optional[str]) -> bo return j >= STAGNATION_JACCARD_NEAR_DUP -def _detect_disengagement(curr_user: Optional[str]) -> bool: +def _detect_disengagement(curr_user: str | None) -> bool: if not curr_user: return False return any(p.search(curr_user) for p in _DISENGAGEMENT_PATTERNS) -def _detect_satisfaction(curr_user: Optional[str]) -> bool: +def _detect_satisfaction(curr_user: str | None) -> bool: if not curr_user: return False return any(p.search(curr_user) for p in _SATISFACTION_PATTERNS) -def _detect_failure(tool_results: List[Dict[str, Any]]) -> bool: +def _detect_failure(tool_results: list[dict[str, Any]]) -> bool: """Any tool result explicitly flagged as an error. We do NOT treat empty content as failure — many tools legitimately return @@ -173,7 +173,7 @@ def _detect_failure(tool_results: List[Dict[str, Any]]) -> bool: return False -def _signature(call: Dict[str, Any]) -> str: +def _signature(call: dict[str, Any]) -> str: """Stable signature for loop detection: name + sorted JSON-ish args.""" name = call.get("name") or call.get("function", {}).get("name", "") call_args = call.get("arguments") @@ -184,7 +184,7 @@ def _signature(call: Dict[str, Any]) -> str: return f"{name}({call_args})" -def _detect_loop(history: List[str], new_calls: List[Dict[str, Any]]) -> bool: +def _detect_loop(history: list[str], new_calls: list[dict[str, Any]]) -> bool: """Fires if any new call's signature appears >= LOOP_REPEAT_THRESHOLD-1 times in recent history (so this call would be the Nth).""" if not new_calls: @@ -209,7 +209,7 @@ _EXHAUSTION_KEYWORDS = ( ) -def _detect_exhaustion(status: Optional[int], tool_results: List[Dict[str, Any]]) -> bool: +def _detect_exhaustion(status: int | None, tool_results: list[dict[str, Any]]) -> bool: if status is not None and status in _EXHAUSTION_STATUSES: return True for r in tool_results: @@ -219,39 +219,53 @@ def _detect_exhaustion(status: Optional[int], tool_results: List[Dict[str, Any]] return False -# ---- Public entrypoint ---------------------------------------------------- +def detect_user_feedback( + previous_user_content: str | None, + current_user_content: str | None, + tool_results: list[dict[str, Any]], + allow_satisfaction: bool, +) -> SignalDelta: + return SignalDelta( + misalignment=int(_detect_misalignment(previous_user_content, current_user_content)), + disengagement=int(_detect_disengagement(current_user_content)), + satisfaction=int(allow_satisfaction and _detect_satisfaction(current_user_content)), + failure=int(_detect_failure(tool_results)), + ) -def apply_turn(state: SessionState, turn: Turn) -> SignalDelta: - """ - Detect signals on this turn, mutate state, return the delta. +def detect_response_signals( + previous_assistant_content: str | None, + current_assistant_content: str | None, + tool_call_history: list[str], + tool_calls: list[dict[str, Any]], + tool_results: list[dict[str, Any]], + response_status: int | None, +) -> SignalDelta: + return SignalDelta( + stagnation=int( + _detect_stagnation( + previous_assistant_content, + current_assistant_content, + ) + ), + loop=int(_detect_loop(tool_call_history, tool_calls)), + exhaustion=int(_detect_exhaustion(response_status, tool_results)), + ) - O(1) per turn (no full-history rescan). Only inspects last_*, recent tool history - (which is bounded at TOOL_CALL_HISTORY_MAX), and the new turn payload. - """ - delta = SignalDelta() - if _detect_misalignment(state.last_user_content, turn.user_content): - delta.misalignment = 1 - if _detect_stagnation(state.last_assistant_content, turn.assistant_content): - delta.stagnation = 1 - if _detect_disengagement(turn.user_content): - delta.disengagement = 1 - if _detect_satisfaction(turn.user_content): - # Gate: only award satisfaction credit once per session, and only - # after MIN_TURNS_FOR_CLEAN_CREDIT turns of context. Early "thanks" - # on turn 1-2 is noise, not a validated quality signal. - current_turn_index = state.turn_count + 1 - if not state.clean_credit_awarded and current_turn_index >= MIN_TURNS_FOR_CLEAN_CREDIT: - delta.satisfaction = 1 - state.clean_credit_awarded = True - if _detect_failure(turn.tool_results): - delta.failure = 1 - if _detect_loop(state.tool_call_history, turn.tool_calls): - delta.loop = 1 - if _detect_exhaustion(turn.response_status, turn.tool_results): - delta.exhaustion = 1 +def merge_signal_deltas(*deltas: SignalDelta) -> SignalDelta: + return SignalDelta( + misalignment=sum(delta.misalignment for delta in deltas), + stagnation=sum(delta.stagnation for delta in deltas), + disengagement=sum(delta.disengagement for delta in deltas), + satisfaction=sum(delta.satisfaction for delta in deltas), + failure=sum(delta.failure for delta in deltas), + loop=sum(delta.loop for delta in deltas), + exhaustion=sum(delta.exhaustion for delta in deltas), + ) + +def apply_signal_delta(state: SessionState, delta: SignalDelta) -> None: state.misalignment_count += delta.misalignment state.stagnation_count += delta.stagnation state.disengagement_count += delta.disengagement @@ -260,6 +274,8 @@ def apply_turn(state: SessionState, turn: Turn) -> SignalDelta: state.loop_count += delta.loop state.exhaustion_count += delta.exhaustion + +def advance_session_state(state: SessionState, turn: Turn) -> None: if turn.user_content: state.last_user_content = turn.user_content if turn.assistant_content: @@ -276,4 +292,38 @@ def apply_turn(state: SessionState, turn: Turn) -> SignalDelta: state.turn_count += 1 state.last_processed_turn = state.turn_count + +# ---- Public entrypoint ---------------------------------------------------- + + +def apply_turn(state: SessionState, turn: Turn) -> SignalDelta: + """ + Detect signals on this turn, mutate state, return the delta. + + O(1) per turn (no full-history rescan). Only inspects last_*, recent tool history + (which is bounded at TOOL_CALL_HISTORY_MAX), and the new turn payload. + """ + feedback_delta = detect_user_feedback( + state.last_user_content, + turn.user_content, + turn.tool_results, + allow_satisfaction=(not state.clean_credit_awarded and state.turn_count + 1 >= MIN_TURNS_FOR_CLEAN_CREDIT), + ) + response_delta = detect_response_signals( + state.last_assistant_content, + turn.assistant_content, + state.tool_call_history, + turn.tool_calls, + turn.tool_results, + turn.response_status, + ) + delta = merge_signal_deltas( + feedback_delta, + response_delta, + ) + apply_signal_delta(state, delta) + if delta.satisfaction: + state.clean_credit_awarded = True + advance_session_state(state, turn) + return delta diff --git a/litellm/router_strategy/auto_router/auto_router.py b/litellm/router_strategy/auto_router/auto_router.py index dc24623f505..c01e2f10d2c 100644 --- a/litellm/router_strategy/auto_router/auto_router.py +++ b/litellm/router_strategy/auto_router/auto_router.py @@ -130,11 +130,12 @@ class AutoRouter(CustomLogger): # do nothing, return same inputs return None - if self.routelayer is None: + routelayer = self.routelayer + if routelayer is None: ####################### # Create the route layer ####################### - self.routelayer = SemanticRouter( + routelayer = SemanticRouter( routes=self.loaded_routes, encoder=LiteLLMRouterEncoder( litellm_router_instance=self.litellm_router_instance, @@ -142,9 +143,10 @@ class AutoRouter(CustomLogger): ), auto_sync=self.auto_sync_value, ) + self.routelayer = routelayer message_content = self._extract_text_from_messages(messages) - route_choice: Optional[Union[RouteChoice, List[RouteChoice]]] = self.routelayer(text=message_content) + route_choice: Optional[Union[RouteChoice, List[RouteChoice]]] = routelayer(text=message_content) verbose_router_logger.debug(f"route_choice: {route_choice}") if isinstance(route_choice, RouteChoice): model = route_choice.name or self.default_model diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index 9a1d845dd65..bebdbba90ef 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -4,32 +4,108 @@ Complexity-based Auto Router A rule-based routing strategy that uses weighted scoring across multiple dimensions to classify requests by complexity and route them to appropriate models. -No external API calls - all scoring is local and <1ms. +By default, scoring is local (regex/keyword-based) with no external API calls and <1ms +latency. Optionally, classifier_type="llm" routes classification through a configured +model instead, trading that latency/cost guarantee for potentially better accuracy. +keyword_tier_rules (lexical or, with semantic_keyword_matching, embedding-based) are +evaluated before either classification strategy and force a tier outright when matched. Inspired by ClawRouter: https://github.com/BlockRunAI/ClawRouter """ +from __future__ import annotations + +import asyncio +import random import re -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast + +from pydantic import BaseModel from litellm._logging import verbose_router_logger from litellm.integrations.custom_logger import CustomLogger +from litellm.types.utils import ModelResponse from .config import ( DEFAULT_CODE_KEYWORDS, DEFAULT_REASONING_KEYWORDS, DEFAULT_SIMPLE_KEYWORDS, DEFAULT_TECHNICAL_KEYWORDS, + TIER_SEVERITY_ORDER, ComplexityRouterConfig, ComplexityTier, ) if TYPE_CHECKING: + from semantic_router.routers import SemanticRouter + from litellm.router import Router + from litellm.router_strategy.adaptive_router.adaptive_router import AdaptiveRouter from litellm.types.router import PreRoutingHookResponse else: Router = Any PreRoutingHookResponse = Any + SemanticRouter = Any + + +class TierClassification(BaseModel): + """Structured response schema for the LLM-based complexity classifier.""" + + tier: Literal["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"] + + +_CLASSIFICATION_PROMPT_TEMPLATE = """Classify the complexity of the following user request into exactly one tier. + +Tiers: +- SIMPLE: factual lookups, greetings, short direct questions with no reasoning or code involved. +- MEDIUM: everyday requests needing some explanation or minor code/technical content. +- COMPLEX: requests involving non-trivial code, architecture, or multi-step technical work. +- REASONING: requests explicitly requiring step-by-step reasoning, analysis, or weighing tradeoffs. + +{system_context}Request: +{prompt}""" + + +def _append_custom_keywords(base_keywords: list[str], custom_keywords: list[str] | None) -> list[str]: + if not custom_keywords: + return base_keywords + base_lowered = frozenset(keyword.lower() for keyword in base_keywords) + deduped_custom = {keyword.lower(): keyword for keyword in custom_keywords if keyword.lower() not in base_lowered} + return [*base_keywords, *deduped_custom.values()] + + +# Metadata keys that carry only the parent request's budget reservation state. These +# must not reach internal sub-calls (classifier, embedding): the reservation belongs to +# the routed completion being decided on, not to the sub-call itself, and forwarding it +# would let the sub-call's cost callback finalize the reservation, causing the routed +# completion's callback to skip incrementing key/team budget counters. +# +# Note: user_api_key_auth itself is intentionally kept; it is required by +# _filter_deployments_by_model_access_groups to scope embedding/classifier model +# selection to the caller's authorized access groups. It is forwarded as a sanitized +# copy with its budget_reservation sub-field removed, because the proxy cost callback +# (_get_budget_reservation_from_metadata) falls back to reading the reservation from +# inside the auth object when the top-level key is absent; forwarding it unsanitized +# would re-create the exact double-finalization this stripping exists to prevent. +_BUDGET_RESERVATION_METADATA_KEYS = frozenset({"user_api_key_budget_reservation"}) + + +def _sanitize_user_api_key_auth(auth: Any) -> Any: + if isinstance(auth, dict): + return {k: v for k, v in auth.items() if k != "budget_reservation"} + if getattr(auth, "budget_reservation", None) is not None and hasattr(auth, "model_copy"): + return auth.model_copy(update={"budget_reservation": None}) + return auth + + +def _classifier_call_metadata(metadata: dict[str, Any] | None) -> dict[str, Any] | None: + if not metadata: + return metadata + return { + k: _sanitize_user_api_key_auth(v) if k == "user_api_key_auth" else v + for k, v in metadata.items() + if k not in _BUDGET_RESERVATION_METADATA_KEYS + } class DimensionScore: @@ -37,7 +113,7 @@ class DimensionScore: __slots__ = ("name", "score", "signal") - def __init__(self, name: str, score: float, signal: Optional[str] = None): + def __init__(self, name: str, score: float, signal: str | None = None): self.name = name self.score = score self.signal = signal @@ -45,10 +121,10 @@ class DimensionScore: class ComplexityRouter(CustomLogger): """ - Rule-based complexity router that classifies requests and routes to appropriate models. + Complexity router that classifies requests and routes to appropriate models. - Handles requests in <1ms with zero external API calls by using weighted scoring - across multiple dimensions: + By default, handles requests in <1ms with zero external API calls, using weighted + scoring across multiple dimensions: - Token count (short=simple, long=complex) - Code presence (code keywords → complex) - Reasoning markers ("step by step", "think through" → reasoning tier) @@ -61,9 +137,9 @@ class ComplexityRouter(CustomLogger): def __init__( self, model_name: str, - litellm_router_instance: "Router", - complexity_router_config: Optional[Dict[str, Any]] = None, - default_model: Optional[str] = None, + litellm_router_instance: Router, + complexity_router_config: dict[str, Any] | None = None, + default_model: str | None = None, ): """ Initialize ComplexityRouter. @@ -90,9 +166,19 @@ class ComplexityRouter(CustomLogger): # Build effective keyword lists (use config overrides or defaults) self.code_keywords = self.config.code_keywords or DEFAULT_CODE_KEYWORDS self.reasoning_keywords = self.config.reasoning_keywords or DEFAULT_REASONING_KEYWORDS - self.technical_keywords = self.config.technical_keywords or DEFAULT_TECHNICAL_KEYWORDS + self.technical_keywords = _append_custom_keywords( + self.config.technical_keywords or DEFAULT_TECHNICAL_KEYWORDS, + self.config.custom_technical_keywords, + ) self.simple_keywords = self.config.simple_keywords or DEFAULT_SIMPLE_KEYWORDS + # Lazily built on first semantic request and cached for reuse (route + # embeddings are static, only the prompt is embedded per request). The lock + # serializes the one-time build so concurrent cold-start requests don't each + # construct the index and fire duplicate embedding calls. + self._semantic_routelayer: SemanticRouter | None = None + self._semantic_routelayer_lock = asyncio.Lock() + # Pre-compile regex patterns for efficiency # Use non-greedy .*? to prevent ReDoS on pathological inputs self._multi_step_patterns = [ @@ -102,6 +188,10 @@ class ComplexityRouter(CustomLogger): re.compile(r"[a-z]\)\s", re.IGNORECASE), ] + self.adaptive_router: AdaptiveRouter | None = None + self._model_tiers: dict[str, tuple[ComplexityTier, ...]] = {} + self._adaptive_init_attempted = False + verbose_router_logger.debug(f"ComplexityRouter initialized for {model_name} with tiers: {self.config.tiers}") def _estimate_tokens(self, text: str) -> int: @@ -145,12 +235,12 @@ class ComplexityRouter(CustomLogger): def _score_keyword_match( self, text: str, - keywords: List[str], + keywords: list[str], name: str, signal_label: str, - thresholds: Tuple[int, int], # (low, high) - scores: Tuple[float, float, float], # (none, low, high) - ) -> Tuple[DimensionScore, int]: + thresholds: tuple[int, int], # (low, high) + scores: tuple[float, float, float], # (none, low, high) + ) -> tuple[DimensionScore, int]: """Score based on keyword matches using word boundary matching. Returns: @@ -188,7 +278,7 @@ class ComplexityRouter(CustomLogger): return DimensionScore("questionComplexity", 0.5, f"{count} questions") return DimensionScore("questionComplexity", 0, None) - def classify(self, prompt: str, system_prompt: Optional[str] = None) -> Tuple[ComplexityTier, float, List[str]]: + def classify(self, prompt: str, system_prompt: str | None = None) -> tuple[ComplexityTier, float, list[str]]: """ Classify a prompt by complexity. @@ -247,7 +337,7 @@ class ComplexityRouter(CustomLogger): (0, -1.0, -1.0), ) - dimensions: List[DimensionScore] = [ + dimensions: list[DimensionScore] = [ self._score_token_count(estimated_tokens), code_score, reasoning_score, @@ -286,6 +376,63 @@ class ComplexityRouter(CustomLogger): return tier, weighted_score, signals + async def aclassify( + self, + prompt: str, + system_prompt: str | None = None, + request_kwargs: dict[str, Any] | None = None, + ) -> tuple[ComplexityTier, float, list[str]]: + """ + Classify a prompt by complexity, using the LLM classifier when configured. + + Falls back to the local heuristic scorer if classifier_type is "heuristic", + or if the LLM call fails, times out, or returns an unparseable response. + """ + if self.config.classifier_type != "llm" or self.config.classifier_llm_config is None: + return self.classify(prompt, system_prompt) + + try: + tier = await self._classify_with_llm(prompt, system_prompt, request_kwargs) + return tier, 1.0, [f"llm-classifier:{tier.value}"] + except Exception as e: # noqa: BLE001 -- external LLM call can fail in many distinct ways (timeout, provider error, validation, parse error); any failure must fall back to the heuristic scorer + verbose_router_logger.warning( + f"ComplexityRouter: LLM classifier failed ({e}), falling back to heuristic scoring" + ) + return self.classify(prompt, system_prompt) + + async def _classify_with_llm( + self, + prompt: str, + system_prompt: str | None = None, + request_kwargs: dict[str, Any] | None = None, + ) -> ComplexityTier: + """Call the configured classifier model and parse its structured tier response.""" + llm_config = self.config.classifier_llm_config + if llm_config is None: + raise ValueError("classifier_llm_config is not set") + + system_context = f"Context: {system_prompt}\n\n" if system_prompt else "" + classification_prompt = _CLASSIFICATION_PROMPT_TEMPLATE.format(system_context=system_context, prompt=prompt) + + # Forward the original request's metadata so the classifier call's spend is + # attributed to the calling key/team instead of being dropped. Excludes the + # parent request's budget reservation, which the routed completion (not this + # internal classifier call) is responsible for reconciling. + metadata = _classifier_call_metadata((request_kwargs or {}).get("litellm_metadata")) + + response: ModelResponse = await self.litellm_router_instance.acompletion( + model=llm_config.model, + messages=[{"role": "user", "content": classification_prompt}], + response_format=TierClassification, + timeout=llm_config.timeout_ms / 1000, + metadata=metadata, + ) + content = response.choices[0].message.content + if not content: + raise ValueError("LLM classifier returned empty content") + result = TierClassification.model_validate_json(content) + return ComplexityTier[result.tier] + def get_model_for_tier(self, tier: ComplexityTier) -> str: """ Get the model name for a given complexity tier. @@ -298,78 +445,351 @@ class ComplexityRouter(CustomLogger): """ tier_key = tier.value if isinstance(tier, ComplexityTier) else tier - # Check config tiers mapping - model = self.config.tiers.get(tier_key) - if model: - return model + if tier_key in self.config.tiers: + return self._pick_from_tier_value(self.config.tiers[tier_key], tier_key) - # Fallback to default model if configured if self.config.default_model: return self.config.default_model - # Last resort: return MEDIUM tier model or error - medium_model = self.config.tiers.get(ComplexityTier.MEDIUM.value) - if medium_model: - return medium_model + medium_key = ComplexityTier.MEDIUM.value + if medium_key in self.config.tiers: + return self._pick_from_tier_value(self.config.tiers[medium_key], medium_key) raise ValueError(f"No model configured for tier {tier_key} and no default_model set") + @staticmethod + def _pick_from_tier_value(model: str | list[str], tier_key: str) -> str: + if isinstance(model, str): + return model + if not model: + raise ValueError(f"Empty model pool for tier {tier_key}") + return random.choice(model) + + def _tier_pools(self) -> dict[str, list[str]]: + return {tier: (models if isinstance(models, list) else [models]) for tier, models in self.config.tiers.items()} + + def _ensure_adaptive_router(self) -> Any | None: + if not self.config.adaptive: + return None + if self.adaptive_router is not None: + return self.adaptive_router + if self._adaptive_init_attempted: + return self.adaptive_router + self._adaptive_init_attempted = True + + from litellm.router_strategy.adaptive_router.adaptive_router import ( + AdaptiveRouter, + ) + from litellm.router_strategy.adaptive_router.config import ( + ADAPTIVE_ROUTER_CHOSEN_MODEL_KEY, + ) + from litellm.types.router import ( + AdaptiveRouterConfig, + AdaptiveRouterPreferences, + ) + + pools = self._tier_pools() + available_models = list(dict.fromkeys(model for models in pools.values() for model in models)) + self._model_tiers = { + model: tuple(ComplexityTier(tier_name) for tier_name, models in pools.items() if model in models) + for model in available_models + } + + model_to_prefs: dict[str, AdaptiveRouterPreferences] = {} + model_to_cost: dict[str, float] = {} + model_list = getattr(self.litellm_router_instance, "model_list", None) or [] + name_to_indices = getattr(self.litellm_router_instance, "model_name_to_deployment_indices", {}) or {} + for name in available_models: + indices = name_to_indices.get(name, []) + if not indices: + model_to_prefs[name] = AdaptiveRouterPreferences(quality_tier=2, strengths=[]) + model_to_cost[name] = 0.0 + continue + deployment = model_list[indices[0]] + mi = deployment.get("model_info") if isinstance(deployment, dict) else deployment.model_info + mi_dict: dict[str, Any] = mi if isinstance(mi, dict) else (mi.model_dump() if mi else {}) + prefs_raw = mi_dict.get("adaptive_router_preferences") + if prefs_raw is not None: + model_to_prefs[name] = AdaptiveRouterPreferences(**prefs_raw) + else: + model_to_prefs[name] = AdaptiveRouterPreferences(quality_tier=2, strengths=[]) + + lp = deployment.get("litellm_params") if isinstance(deployment, dict) else deployment.litellm_params + lp_dict: dict[str, Any] = lp if isinstance(lp, dict) else (lp.model_dump() if lp else {}) + cost = lp_dict.get("input_cost_per_token") + model_to_cost[name] = float(cost) if cost is not None else 0.0 + + self.adaptive_router = AdaptiveRouter( + router_name=self.model_name, + config=AdaptiveRouterConfig( + available_models=available_models, + weights=self.config.adaptive_weights, + ), + model_to_prefs=model_to_prefs, + model_to_cost=model_to_cost, + ) + self._adaptive_chosen_model_key = ADAPTIVE_ROUTER_CHOSEN_MODEL_KEY + return self.adaptive_router + + def _soft_floor_pick( + self, + classified_tier: ComplexityTier, + user_message: str, + request_kwargs: dict[str, Any] | None = None, + ) -> str: + from litellm.router_strategy.adaptive_router.bandit import ( + normalized_cost, + thompson_sample, + ) + from litellm.router_strategy.adaptive_router.classifier import classify_prompt + + adaptive = self._ensure_adaptive_router() + if adaptive is None: + return self.get_model_for_tier(classified_tier) + + request_type = classify_prompt(user_message) + classified_idx = TIER_SEVERITY_ORDER.index(classified_tier) + pools = self._tier_pools() + classified_candidates = tuple(pools.get(classified_tier.value, ())) + cold_start_candidates = tuple( + model for model in classified_candidates if adaptive._cells[(request_type, model)].total_samples == 0 + ) + if cold_start_candidates: + chosen_model = random.choice(cold_start_candidates) + if request_kwargs is not None: + metadata = request_kwargs.setdefault("metadata", {}) + if isinstance(metadata, dict): + metadata["adaptive_router_decision"] = { + "phase": "cold_start", + "classified_tier": classified_tier.value, + "request_type": request_type.value, + "eligible_mode": "classified_tier", + "quality_weight": self.config.adaptive_weights.quality, + "cost_weight": self.config.adaptive_weights.cost, + "tier_distance_penalty": self.config.tier_distance_penalty, + "chosen_model": chosen_model, + "candidates": [ + { + "model": model, + "total_samples": adaptive._cells[(request_type, model)].total_samples, + } + for model in cold_start_candidates + ], + } + return chosen_model + if self.config.adaptive_eligible == "classified_tier": + candidates = list(classified_candidates) + if not candidates: + return self.get_model_for_tier(classified_tier) + else: + candidates = list(adaptive.config.available_models) + + all_costs = [adaptive.model_to_cost.get(m, 0.0) for m in candidates] + quality_weight = self.config.adaptive_weights.quality + cost_weight = self.config.adaptive_weights.cost + penalty_weight = self.config.tier_distance_penalty + + best_model: str | None = None + best_score = float("-inf") + candidate_scores: list[dict[str, Any]] = [] + for model in candidates: + cell = adaptive._cells[(request_type, model)] + quality_sample = thompson_sample(cell) + cost_score = normalized_cost(adaptive.model_to_cost.get(model, 0.0), all_costs) + if self.config.adaptive_eligible == "classified_tier": + distance = 0 + else: + model_tiers = self._model_tiers.get(model, (classified_tier,)) + distance = min( + abs(TIER_SEVERITY_ORDER.index(model_tier) - classified_idx) for model_tier in model_tiers + ) + score = quality_weight * quality_sample + cost_weight * cost_score - penalty_weight * distance + candidate_scores.append( + { + "model": model, + "quality_sample": quality_sample, + "cost_score": cost_score, + "tier_distance": distance, + "score": score, + } + ) + if score > best_score: + best_score = score + best_model = model + if best_model is None: + return self.get_model_for_tier(classified_tier) + if request_kwargs is not None: + metadata = request_kwargs.setdefault("metadata", {}) + if isinstance(metadata, dict): + metadata["adaptive_router_decision"] = { + "phase": "adaptive", + "classified_tier": classified_tier.value, + "request_type": request_type.value, + "eligible_mode": self.config.adaptive_eligible, + "quality_weight": quality_weight, + "cost_weight": cost_weight, + "tier_distance_penalty": penalty_weight, + "chosen_model": best_model, + "candidates": candidate_scores, + } + return best_model + + def _lexical_tier_override(self, user_message: str) -> ComplexityTier | None: + """When keyword_tier_rules match literally, the most-severe matched tier wins. + + Escalating to the highest tier (rather than the first rule in the list) keeps + routing independent of the order rules were authored in: a prompt hitting both a + SIMPLE and a REASONING keyword routes to REASONING. + """ + rules = self.config.keyword_tier_rules + if not rules: + return None + text = user_message.lower() + matched_tiers = [ + rule.tier for rule in rules if any(self._keyword_matches(text, keyword) for keyword in rule.keywords) + ] + if not matched_tiers: + return None + return max(matched_tiers, key=TIER_SEVERITY_ORDER.index) + + def _get_or_create_semantic_routelayer(self) -> SemanticRouter: + """Build (once) a SemanticRouter with one route per tier, utterances = that tier's keywords.""" + if self._semantic_routelayer is not None: + return self._semantic_routelayer + + from semantic_router.routers import SemanticRouter + from semantic_router.routers.base import Route + + from litellm.router_strategy.auto_router.litellm_encoder import ( + LiteLLMRouterEncoder, + ) + + embedding_model = self.config.embedding_model + if embedding_model is None: + raise ValueError("embedding_model is required for semantic keyword matching") + + rules = self.config.keyword_tier_rules or [] + ordered_tiers = tuple(dict.fromkeys(rule.tier.value for rule in rules)) + routes = [ + Route( + name=tier, + utterances=[keyword for rule in rules if rule.tier.value == tier for keyword in rule.keywords], + score_threshold=self.config.match_threshold, + ) + for tier in ordered_tiers + ] + routelayer = SemanticRouter( + routes=routes, + encoder=LiteLLMRouterEncoder( + litellm_router_instance=self.litellm_router_instance, + model_name=embedding_model, + score_threshold=self.config.match_threshold, + ), + auto_sync="local", + aggregation="max", + ) + self._semantic_routelayer = routelayer + return routelayer + + async def _ensure_semantic_routelayer(self) -> SemanticRouter: + """Return the cached route layer, building it once under a lock if needed. + + The build embeds the static route utterances via the encoder's synchronous path, + so it runs in a worker thread to avoid blocking the event loop. A double-checked + asyncio lock ensures concurrent cold-start requests build it exactly once rather + than each firing duplicate embedding calls. + """ + if self._semantic_routelayer is not None: + return self._semantic_routelayer + async with self._semantic_routelayer_lock: + routelayer = self._semantic_routelayer + if routelayer is None: + routelayer = await asyncio.to_thread(self._get_or_create_semantic_routelayer) + return routelayer + + async def _semantic_tier_override(self, user_message: str, request_kwargs: dict) -> ComplexityTier | None: + """Match the prompt against keyword_tier_rules by embedding similarity. + + Embeds the query ourselves (instead of letting SemanticRouter.acall embed it + internally) so the caller's metadata/litellm_metadata flows into aembedding() + and this spend is attributed and budget-checked against the originating key/team, + the same as any other litellm call. SemanticRouter.acall() has no parameter to + pass such kwargs through to the encoder, so it's bypassed for the query embedding; + the route index itself (static utterances, embedded once at build time with no + caller context) is unaffected and still reused via the precomputed `vector=` path. + """ + from semantic_router.schema import RouteChoice + + from litellm.router_strategy.auto_router.litellm_encoder import ( + LiteLLMRouterEncoder, + ) + + routelayer = await self._ensure_semantic_routelayer() + encoder = cast(LiteLLMRouterEncoder, routelayer.encoder) # cast-ok: always the encoder we built above + # Strip the parent request's budget reservation before forwarding: the reservation + # belongs to the routed completion this embedding is helping select, not to the + # embedding call. Forwarding it would let the embedding's cost callback finalize the + # reservation, so the routed completion's own callback then skips incrementing the + # key/team budget. Key/team attribution fields are preserved for spend logging. + metadata = _classifier_call_metadata(request_kwargs.get("metadata")) or {} + litellm_metadata = _classifier_call_metadata(request_kwargs.get("litellm_metadata")) or {} + query_vector = ( + await encoder.aencode_queries([user_message], metadata=metadata, litellm_metadata=litellm_metadata) + )[0] + route_choice = await routelayer.acall(vector=query_vector) + + if isinstance(route_choice, list): + route_choice = route_choice[0] if route_choice else None + if not isinstance(route_choice, RouteChoice) or not route_choice.name: + return None + try: + return ComplexityTier(route_choice.name) + except ValueError: + return None + + async def _resolve_keyword_tier_override(self, user_message: str, request_kwargs: dict) -> ComplexityTier | None: + """Resolve a keyword_tier_rule override, semantically or lexically per config. + + Returns None (no override -> fall through to the scorer) not only when no rule + matches, but also when the semantic path fails: the embedding call can error or + time out, and a routing helper must never turn that into a failed user request. + """ + if not self.config.keyword_tier_rules: + return None + if not self.config.semantic_keyword_matching: + return self._lexical_tier_override(user_message) + try: + return await self._semantic_tier_override(user_message, request_kwargs) + except Exception as e: # noqa: BLE001 -- embedding call can fail many ways (timeout, provider/network/parse error); any failure must fall back to scoring, never fail the request + verbose_router_logger.warning( + f"ComplexityRouter: semantic keyword matching failed ({e}), falling back to complexity scoring" + ) + return None + def _resolve_messages( self, - messages: Optional[List[Dict[str, Any]]], - request_kwargs: Dict, - ) -> Optional[List[Dict[str, Any]]]: + messages: list[dict[str, Any]] | None, + request_kwargs: dict, + ) -> list[dict[str, Any]] | None: """ Resolve messages from the request, converting from other formats if needed. Uses the guardrail translation handler dispatch to convert Responses API ``input`` (or other non-chat-completions formats) into OpenAI-spec messages. """ - if messages: - return messages - - from litellm.litellm_core_utils.api_route_to_call_types import ( - get_call_types_for_route, + from litellm.litellm_core_utils.prompt_templates.factory import ( + resolve_structured_messages, ) - from litellm.llms import load_guardrail_translation_mappings - from litellm.types.utils import CallTypes - mappings = load_guardrail_translation_mappings() - call_type: Optional[CallTypes] = None - - # 1. Try route-based inference from proxy metadata - route = request_kwargs.get("litellm_metadata", {}).get("user_api_key_request_route") - if route: - call_types_list = get_call_types_for_route(route) - if call_types_list: - for ct in call_types_list: - if ct in mappings: - call_type = ct - break - - # 2. Fallback: try each mapped handler until one produces messages - handlers_to_try: List[Any] = [] - if call_type is not None and call_type in mappings: - handlers_to_try.append(mappings[call_type]()) - else: - handlers_to_try.extend(handler_cls() for handler_cls in mappings.values()) - - for handler in handlers_to_try: - structured = handler.get_structured_messages(request_kwargs) - if structured: - return [ - msg if isinstance(msg, dict) else msg.model_dump() # type: ignore - for msg in structured - ] - return None + return resolve_structured_messages(messages=messages, request_kwargs=request_kwargs) @staticmethod def _extract_user_message_and_system_prompt( - messages: List[Dict[str, Any]], - ) -> Tuple[Optional[str], Optional[str]]: + messages: list[dict[str, Any]], + ) -> tuple[str | None, str | None]: """Extract the last user message text and last system prompt from messages.""" - user_message: Optional[str] = None - system_prompt: Optional[str] = None + user_message: str | None = None + system_prompt: str | None = None for msg in reversed(messages): role = msg.get("role", "") @@ -392,11 +812,11 @@ class ComplexityRouter(CustomLogger): async def async_pre_routing_hook( self, model: str, - request_kwargs: Dict, - messages: Optional[List[Dict[str, Any]]] = None, - input: Optional[Union[str, List]] = None, - specific_deployment: Optional[bool] = False, - ) -> Optional["PreRoutingHookResponse"]: + request_kwargs: dict, + messages: list[dict[str, Any]] | None = None, + input: Union[str, list] | None = None, + specific_deployment: bool | None = False, + ) -> Optional[PreRoutingHookResponse]: """ Pre-routing hook called before the routing decision. @@ -434,12 +854,39 @@ class ComplexityRouter(CustomLogger): messages=messages if has_original_messages else None, ) - tier, score, signals = self.classify(user_message, system_prompt) - routed_model = self.get_model_for_tier(tier) + override_tier = await self._resolve_keyword_tier_override(user_message, request_kwargs) + if override_tier is not None: + routed_model = self.get_model_for_tier(override_tier) + cause = "semantic_keyword_match" if self.config.semantic_keyword_matching else "literal_keyword_match" + verbose_router_logger.info( + f"ComplexityRouter: routing decision cause={cause}, " + f"tier={override_tier.value}, routed_model={routed_model}" + ) + return PreRoutingHookResponse( + model=routed_model, + messages=messages if has_original_messages else None, + ) - verbose_router_logger.info( - f"ComplexityRouter: tier={tier.value}, score={score:.3f}, signals={signals}, routed_model={routed_model}" - ) + tier, score, signals = await self.aclassify(user_message, system_prompt, request_kwargs) + if self.config.adaptive: + routed_model = self._soft_floor_pick(tier, user_message, request_kwargs) + adaptive = self._ensure_adaptive_router() + if adaptive is not None: + kwargs_metadata = request_kwargs.setdefault("metadata", {}) + if isinstance(kwargs_metadata, dict): + chosen_key = getattr(self, "_adaptive_chosen_model_key", "adaptive_router_chosen_model") + kwargs_metadata[chosen_key] = routed_model + verbose_router_logger.info( + f"ComplexityRouter[adaptive]: routing decision cause=complexity_scorer, " + f"tier={tier.value}, score={score:.3f}, " + f"signals={signals}, routed_model={routed_model}" + ) + else: + routed_model = self.get_model_for_tier(tier) + verbose_router_logger.info( + f"ComplexityRouter: routing decision cause=complexity_scorer, tier={tier.value}, " + f"score={score:.3f}, signals={signals}, routed_model={routed_model}" + ) return PreRoutingHookResponse( model=routed_model, diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index a8a21e3f30b..df699d1a059 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -6,9 +6,11 @@ All values are configurable via proxy config.yaml. """ from enum import Enum -from typing import Dict, List, Optional +from typing import Literal -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator + +from litellm.types.router import AdaptiveRouterWeights class ComplexityTier(str, Enum): @@ -20,11 +22,45 @@ class ComplexityTier(str, Enum): REASONING = "REASONING" +TIER_SEVERITY_ORDER: tuple[ComplexityTier, ...] = ( + ComplexityTier.SIMPLE, + ComplexityTier.MEDIUM, + ComplexityTier.COMPLEX, + ComplexityTier.REASONING, +) + +DEFAULT_TIER_DISTANCE_PENALTY: float = 0.5 + + +class KeywordTierRule(BaseModel): + """A deterministic override: if any keyword matches, route to this tier.""" + + keywords: list[str] = Field( + min_length=1, + description="Keywords/phrases that trigger this rule (lexical or semantic match)", + ) + tier: ComplexityTier = Field( + description="Tier to route to when this rule matches", + ) + + @model_validator(mode="after") + def _normalize_keywords(self) -> "KeywordTierRule": + # Strip and drop blank keywords. An empty/whitespace keyword is a routing foot-gun: + # _keyword_matches treats "" / " " as a substring that matches essentially every + # prompt, so a single stray blank would silently force this rule's tier for all + # traffic. Require at least one real keyword to remain. + cleaned = [stripped for keyword in self.keywords if (stripped := keyword.strip())] + if not cleaned: + raise ValueError("keyword_tier_rules entries must contain at least one non-empty keyword") + self.keywords = cleaned + return self + + # ─── Default Keyword Lists ─── # Note: Keywords should be full words/phrases to avoid substring false positives. # The matching logic uses word boundary detection for single-word keywords. -DEFAULT_CODE_KEYWORDS: List[str] = [ +DEFAULT_CODE_KEYWORDS: list[str] = [ "function", "class", "def", @@ -72,7 +108,7 @@ DEFAULT_CODE_KEYWORDS: List[str] = [ "pull request", ] -DEFAULT_REASONING_KEYWORDS: List[str] = [ +DEFAULT_REASONING_KEYWORDS: list[str] = [ "step by step", "think through", "let's think", @@ -94,7 +130,7 @@ DEFAULT_REASONING_KEYWORDS: List[str] = [ "conclude", ] -DEFAULT_TECHNICAL_KEYWORDS: List[str] = [ +DEFAULT_TECHNICAL_KEYWORDS: list[str] = [ "architecture", "distributed", "scalable", @@ -126,7 +162,7 @@ DEFAULT_TECHNICAL_KEYWORDS: List[str] = [ # Note: "async", "kubernetes", "docker" are in DEFAULT_CODE_KEYWORDS ] -DEFAULT_SIMPLE_KEYWORDS: List[str] = [ +DEFAULT_SIMPLE_KEYWORDS: list[str] = [ "what is", "what's", "define", @@ -159,7 +195,7 @@ DEFAULT_SIMPLE_KEYWORDS: List[str] = [ # ─── Default Dimension Weights ─── -DEFAULT_DIMENSION_WEIGHTS: Dict[str, float] = { +DEFAULT_DIMENSION_WEIGHTS: dict[str, float] = { "tokenCount": 0.10, # Reduced - length is less important than content "codePresence": 0.30, # High - code requests need capable models "reasoningMarkers": 0.25, # High - explicit reasoning requests @@ -172,7 +208,7 @@ DEFAULT_DIMENSION_WEIGHTS: Dict[str, float] = { # ─── Default Tier Boundaries ─── -DEFAULT_TIER_BOUNDARIES: Dict[str, float] = { +DEFAULT_TIER_BOUNDARIES: dict[str, float] = { "simple_medium": 0.15, # Lower threshold to catch more MEDIUM cases "medium_complex": 0.35, # Lower threshold to catch technical COMPLEX cases "complex_reasoning": 0.60, # Reasoning tier reserved for explicit reasoning markers @@ -181,7 +217,7 @@ DEFAULT_TIER_BOUNDARIES: Dict[str, float] = { # ─── Default Token Thresholds ─── -DEFAULT_TOKEN_THRESHOLDS: Dict[str, int] = { +DEFAULT_TOKEN_THRESHOLDS: dict[str, int] = { "simple": 15, # Only very short prompts (<15 tokens) are penalized "complex": 400, # Long prompts (>400 tokens) get complexity boost } @@ -189,7 +225,7 @@ DEFAULT_TOKEN_THRESHOLDS: Dict[str, int] = { # ─── Default Tier to Model Mapping ─── -DEFAULT_TIER_MODELS: Dict[str, str] = { +DEFAULT_TIER_MODELS: dict[str, str] = { "SIMPLE": "gpt-4o-mini", "MEDIUM": "gpt-4o", "COMPLEX": "claude-sonnet-4-20250514", @@ -197,59 +233,180 @@ DEFAULT_TIER_MODELS: Dict[str, str] = { } +class ClassifierLLMConfig(BaseModel): + """Configuration for the LLM-based complexity classifier.""" + + model: str = Field( + description="Model name (from the router's model_list) to call for classification", + ) + timeout_ms: int = Field( + default=3000, + description="Timeout budget for the classification call, in milliseconds", + ) + + class ComplexityRouterConfig(BaseModel): """Configuration for the ComplexityRouter.""" - # Tier to model mapping - tiers: Dict[str, str] = Field( + # string = pin; list = random pick when adaptive=False, soft-floor home pool when adaptive=True + tiers: dict[str, str | list[str]] = Field( default_factory=lambda: DEFAULT_TIER_MODELS.copy(), - description="Mapping of complexity tiers to model names", + description=( + "Mapping of complexity tiers to a model or model pool. " + "A list is randomly picked from when adaptive=False, and used as a soft-floor home pool when adaptive=True" + ), ) # Tier boundaries (normalized scores) - tier_boundaries: Dict[str, float] = Field( + tier_boundaries: dict[str, float] = Field( default_factory=lambda: DEFAULT_TIER_BOUNDARIES.copy(), description="Score boundaries between tiers", ) # Token count thresholds - token_thresholds: Dict[str, int] = Field( + token_thresholds: dict[str, int] = Field( default_factory=lambda: DEFAULT_TOKEN_THRESHOLDS.copy(), description="Token count thresholds for simple/complex classification", ) # Dimension weights - dimension_weights: Dict[str, float] = Field( + dimension_weights: dict[str, float] = Field( default_factory=lambda: DEFAULT_DIMENSION_WEIGHTS.copy(), description="Weights for each scoring dimension", ) # Keyword lists (overridable) - code_keywords: Optional[List[str]] = Field( + code_keywords: list[str] | None = Field( default=None, description="Keywords indicating code-related content", ) - reasoning_keywords: Optional[List[str]] = Field( + reasoning_keywords: list[str] | None = Field( default=None, description="Keywords indicating reasoning-required content", ) - technical_keywords: Optional[List[str]] = Field( + technical_keywords: list[str] | None = Field( default=None, description="Keywords indicating technical content", ) - simple_keywords: Optional[List[str]] = Field( + custom_technical_keywords: list[str] | None = Field( + default=None, + description=( + "Domain-specific technical keywords appended to the effective base list " + "(technical_keywords if set, otherwise DEFAULT_TECHNICAL_KEYWORDS). " + "Order is preserved; duplicates are removed case-insensitively against " + "the base list and within this list." + ), + ) + simple_keywords: list[str] | None = Field( default=None, description="Keywords indicating simple/basic queries", ) # Default model if scoring fails - default_model: Optional[str] = Field( + default_model: str | None = Field( default=None, description="Default model to use if tier cannot be determined", ) + # Classifier strategy + classifier_type: Literal["heuristic", "llm"] = Field( + default="heuristic", + description="Classification strategy: local regex/keyword scoring, or an LLM call", + ) + classifier_llm_config: ClassifierLLMConfig | None = Field( + default=None, + description="Configuration for the LLM classifier; required when classifier_type is 'llm'", + ) + + adaptive: bool = Field( + default=False, + description="Enable adaptive bandit selection with soft complexity floors", + ) + adaptive_weights: AdaptiveRouterWeights = Field( + default_factory=lambda: AdaptiveRouterWeights(quality=0.3, cost=0.7), + description="Quality vs cost weights for adaptive selection (used when adaptive=True)", + ) + tier_distance_penalty: float = Field( + default=DEFAULT_TIER_DISTANCE_PENALTY, + ge=0.0, + description="Score penalty per tier-step away from the classified tier when adaptive=True", + ) + adaptive_eligible: Literal["all", "classified_tier"] = Field( + default="all", + description=( + "When adaptive=True: 'all' scores every pool model with a tier-distance penalty (soft floors); " + "'classified_tier' Thompson-samples only inside the classified tier's pool" + ), + ) + + # Deterministic keyword -> tier overrides, evaluated before weighted scoring + keyword_tier_rules: list[KeywordTierRule] | None = Field( + default=None, + description="Rules that force a specific tier when their keywords match the prompt", + ) + + # Semantic (embedding) matching for keyword_tier_rules instead of literal text matching + semantic_keyword_matching: bool = Field( + default=False, + description="Match keyword_tier_rules by embedding similarity instead of literal text", + ) + embedding_model: str | None = Field( + default=None, + description="Embedding model (LiteLLM model name) used when semantic_keyword_matching is enabled", + ) + match_threshold: float = Field( + default=0.5, + ge=0.0, + le=1.0, + description="Minimum cosine similarity for a semantic keyword match", + ) + model_config = ConfigDict(extra="allow") # Allow additional fields + @field_validator("tiers", mode="before") + @classmethod + def _coerce_tier_values(cls, value: object) -> object: + if not isinstance(value, dict): + return value + coerced: dict[str, object] = {} + for key, item in value.items(): + if isinstance(item, str): + coerced[key] = item + elif isinstance(item, (list, tuple)): + coerced[key] = list(item) + else: + coerced[key] = item + return coerced + + @model_validator(mode="after") + def _validate_llm_classifier_config(self) -> "ComplexityRouterConfig": + if self.classifier_type == "llm" and self.classifier_llm_config is None: + raise ValueError("classifier_llm_config is required when classifier_type is 'llm'") + return self + + @model_validator(mode="after") + def _validate_adaptive_pools(self) -> "ComplexityRouterConfig": + if not self.adaptive: + return self + normalized = {tier: (models if isinstance(models, list) else [models]) for tier, models in self.tiers.items()} + if not any(normalized.values()): + raise ValueError("adaptive=True requires at least one non-empty tier pool") + empty = [tier for tier, models in normalized.items() if not models] + if empty: + raise ValueError(f"adaptive=True tier pools must be non-empty; empty tiers: {empty}") + self.tiers = normalized + return self + + @model_validator(mode="after") + def _validate_semantic_matching(self) -> "ComplexityRouterConfig": + if not self.semantic_keyword_matching: + return self + if not self.embedding_model: + raise ValueError("embedding_model is required when semantic_keyword_matching is enabled") + if not self.keyword_tier_rules: + raise ValueError("keyword_tier_rules must be non-empty when semantic_keyword_matching is enabled") + return self + # Combined default config DEFAULT_COMPLEXITY_CONFIG = ComplexityRouterConfig() diff --git a/litellm/router_strategy/tag_based_routing.py b/litellm/router_strategy/tag_based_routing.py index 65e76ba909b..6ca4e1de322 100644 --- a/litellm/router_strategy/tag_based_routing.py +++ b/litellm/router_strategy/tag_based_routing.py @@ -7,7 +7,7 @@ Use this to route requests between Teams """ import re -from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union +from typing import TYPE_CHECKING, Any, Literal, Optional, Union from litellm._logging import verbose_logger from litellm.types.router import RouterErrors @@ -21,8 +21,8 @@ else: def _is_valid_deployment_tag_regex( - tag_regexes: List[str], - header_strings: List[str], + tag_regexes: list[str], + header_strings: list[str], ) -> Optional[str]: """ Test compiled regex patterns against "Header-Name: value" strings. @@ -43,7 +43,7 @@ def _is_valid_deployment_tag_regex( return None -def is_valid_deployment_tag(deployment_tags: List[str], request_tags: List[str], match_any: bool = True) -> bool: +def is_valid_deployment_tag(deployment_tags: list[str], request_tags: list[str], match_any: bool = True) -> bool: """ Check if a tag is valid, the matching can be either any or all based on `match_any` flag """ @@ -71,10 +71,10 @@ def is_valid_deployment_tag(deployment_tags: List[str], request_tags: List[str], def _match_deployment( deployment: Any, - request_tags: Optional[List[str]], - header_strings: List[str], + request_tags: Optional[list[str]], + header_strings: list[str], match_any: bool, -) -> Optional[Dict[str, str]]: +) -> Optional[dict[str, str]]: """ Determine whether *deployment* matches the current request. @@ -87,8 +87,8 @@ def _match_deployment( ran and failed, so the regex cannot override strict-tag policy. """ litellm_params = deployment.get("litellm_params", {}) - deployment_tags: Optional[List[str]] = litellm_params.get("tags") - deployment_tag_regex: Optional[List[str]] = litellm_params.get("tag_regex") + deployment_tags: Optional[list[str]] = litellm_params.get("tags") + deployment_tag_regex: Optional[list[str]] = litellm_params.get("tag_regex") # 1. Exact tag match (existing behaviour). if deployment_tags and request_tags: @@ -114,11 +114,46 @@ def _match_deployment( return None +def _split_tags(tags: list[str]) -> tuple[list[str], list[str]]: + positive = [t for t in tags if not t.startswith("!")] + excluded = [tag[1:] for tag in tags if tag.startswith("!") and len(tag) > 1] + return positive, excluded + + +def _exclude_deployments( + deployments: Union[list[Any], dict[Any, Any]], + excluded_set: frozenset[str], +) -> list[Any]: + if not excluded_set: + return list(deployments) + return [d for d in deployments if not excluded_set.intersection(d.get("litellm_params", {}).get("tags") or [])] + + +def _require_candidates( + candidates: list[Any], + model: str, + request_tags: Any, +) -> list[Any]: + if not candidates: + raise ValueError( + f"{RouterErrors.no_deployments_with_tag_routing.value}. Passed model={model} and tags={request_tags}" + ) + return candidates + + +def _ban_only_base_pool( + deployments: Union[list[Any], dict[Any, Any]], +) -> list[Any]: + # Mirrors untagged-request semantics so callers can't use !tags to escape the default pool. + defaults = [d for d in deployments if "default" in (d.get("litellm_params", {}).get("tags") or [])] + return defaults if defaults else list(deployments) + + async def get_deployments_for_tag( llm_router_instance: LitellmRouter, model: str, # used to raise the correct error - healthy_deployments: Union[List[Any], Dict[Any, Any]], - request_kwargs: Optional[Dict[Any, Any]] = None, + healthy_deployments: Union[list[Any], dict[Any, Any]], + request_kwargs: Optional[dict[Any, Any]] = None, metadata_variable_name: Literal["metadata", "litellm_metadata"] = "metadata", ): """ @@ -136,13 +171,8 @@ async def get_deployments_for_tag( ) return healthy_deployments - if healthy_deployments is None: - verbose_logger.debug("get_deployments_for_tag: healthy_deployments is None returning healthy_deployments") - return healthy_deployments - - # Tag filtering applies only when there is at least one deployment to evaluate. - if isinstance(healthy_deployments, list) and len(healthy_deployments) == 0: - verbose_logger.debug("get_deployments_for_tag: empty candidate set; skipping tag filter") + if not healthy_deployments: + verbose_logger.debug("get_deployments_for_tag: empty or None healthy_deployments; skipping tag filter") return healthy_deployments verbose_logger.debug("request metadata: %s", request_kwargs.get(metadata_variable_name)) @@ -154,30 +184,36 @@ async def get_deployments_for_tag( # Build header strings for regex matching from what the proxy already stores. # Currently we match against User-Agent; format matches "^User-Agent: claude-code/..." user_agent = metadata.get("user_agent", "") - header_strings: List[str] = [f"User-Agent: {user_agent}"] if user_agent else [] + header_strings: list[str] = [f"User-Agent: {user_agent}"] if user_agent else [] - new_healthy_deployments: List[Any] = [] - default_deployments: List[Any] = [] + positive_tags, excluded_patterns = _split_tags(request_tags or []) + + excluded_set = frozenset(excluded_patterns) + candidates = _exclude_deployments(healthy_deployments, excluded_set) + + has_regex_deployments = any(d.get("litellm_params", {}).get("tag_regex") for d in candidates) + has_tag_filter = bool(positive_tags) or (bool(header_strings) and has_regex_deployments) + ban_only = bool(excluded_set) and not has_tag_filter + + if ban_only: + pool = _exclude_deployments(_ban_only_base_pool(healthy_deployments), excluded_set) + return _require_candidates(pool, model, request_tags) + + new_healthy_deployments: list[Any] = [] + default_deployments: list[Any] = [] - # Only activate header-based regex filtering when at least one deployment in - # the candidate set has tag_regex configured. This preserves existing - # behaviour for operators who use plain tags: a request that carries a - # User-Agent (all proxy requests do) but targets deployments with no - # tag_regex will continue to use the original tag-only code path. - has_regex_deployments = any(d.get("litellm_params", {}).get("tag_regex") for d in healthy_deployments) - has_tag_filter = bool(request_tags) or (bool(header_strings) and has_regex_deployments) if has_tag_filter: verbose_logger.debug( "get_deployments_for_tag routing: request_tags=%s user_agent=%s", request_tags, user_agent, ) - for deployment in healthy_deployments: + for deployment in candidates: deployment_tags = deployment.get("litellm_params", {}).get("tags") match_result = _match_deployment( deployment=deployment, - request_tags=request_tags, + request_tags=positive_tags, header_strings=header_strings, match_any=match_any, ) @@ -189,10 +225,6 @@ async def get_deployments_for_tag( match_result["matched_via"], match_result["matched_value"], ) - # Record provenance in metadata so it flows to SpendLogs. - # Written only for the first match — load balancer selects one - # deployment from new_healthy_deployments, so overwriting on - # subsequent matches would produce misleading observability data. if "tag_routing" not in metadata: metadata["tag_routing"] = { "matched_deployment": deployment.get("model_name"), @@ -208,7 +240,8 @@ async def get_deployments_for_tag( if len(new_healthy_deployments) == 0 and len(default_deployments) == 0: raise ValueError( - f"{RouterErrors.no_deployments_with_tag_routing.value}. Passed model={model} and tags={request_tags}" + f"{RouterErrors.no_deployments_with_tag_routing.value}." + f" Passed model={model} and tags={request_tags}" ) return new_healthy_deployments if len(new_healthy_deployments) > 0 else default_deployments @@ -231,9 +264,9 @@ async def get_deployments_for_tag( def _get_tags_from_request_kwargs( - request_kwargs: Optional[Dict[Any, Any]] = None, + request_kwargs: Optional[dict[Any, Any]] = None, metadata_variable_name: Literal["metadata", "litellm_metadata"] = "metadata", -) -> List[str]: +) -> list[str]: """ Helper to get tags from request kwargs diff --git a/litellm/router_utils/add_retry_fallback_headers.py b/litellm/router_utils/add_retry_fallback_headers.py index 0b927714ca9..e2204e18a3c 100644 --- a/litellm/router_utils/add_retry_fallback_headers.py +++ b/litellm/router_utils/add_retry_fallback_headers.py @@ -1,5 +1,5 @@ import json -from typing import Protocol, TypedDict, cast +from typing import Any, Protocol, TypedDict, cast from pydantic import BaseModel @@ -15,8 +15,98 @@ class _HiddenParamsHost(Protocol): _hidden_params: dict[str, object] -def get_hidden_params_dict(response: object) -> dict[str, object]: - hidden_params: object = cast(object, getattr(response, "_hidden_params", None)) +class HiddenParamsAsyncIteratorWrapper: + """ + Wraps a bare async generator/iterator (e.g. a provider's raw SSE + streaming response) that cannot itself hold a ``_hidden_params`` + attribute, so router-derived headers (ITPM/OTPM, model-group, retry, + fallback) can attach to a streaming response the same way they attach + to object-based responses (e.g. ``CustomStreamWrapper``). + """ + + def __init__(self, inner: object) -> None: + self._inner = inner + self._hidden_params: dict[str, object] = {} + + def __aiter__(self) -> "HiddenParamsAsyncIteratorWrapper": + return self + + async def __anext__(self) -> object: + return await cast(Any, self._inner).__anext__() + + async def aclose(self) -> None: + aclose = getattr(self._inner, "aclose", None) + if callable(aclose): + await aclose() + + +def prepare_response_for_header_attachment(response: object) -> object | None: + if response is None: + return None + if isinstance(response, dict) or hasattr(response, "_hidden_params"): + return response + if hasattr(response, "__anext__"): + return HiddenParamsAsyncIteratorWrapper(response) + return response + + +def ensure_response_additional_headers(response: object) -> dict[str, object]: + hidden_params = get_hidden_params_dict(response, create=isinstance(response, dict)) + _write_hidden_params(response, hidden_params) + additional_headers = hidden_params.get("additional_headers") + if not isinstance(additional_headers, dict): + additional_headers = {} + hidden_params["additional_headers"] = additional_headers + return additional_headers + + +def apply_quality_router_decision_headers( + additional_headers: dict[str, object], + request_kwargs: object, +) -> None: + metadata = (request_kwargs.get("metadata") or {}) if isinstance(request_kwargs, dict) else {} + decision = metadata.get("quality_router_decision") if isinstance(metadata, dict) else None + if not isinstance(decision, dict): + return + quality_header_fields = ( + ("routed_model", "x-litellm-quality-router-model"), + ("quality_tier", "x-litellm-quality-router-tier"), + ("routed_via", "x-litellm-quality-router-via"), + ("matched_keyword", "x-litellm-quality-router-keyword"), + ("complexity_tier", "x-litellm-quality-router-complexity"), + ) + for field, header in quality_header_fields: + if decision.get(field) is not None: + additional_headers[header] = str(decision[field]) + + +def response_in_flight_token_count(response: object) -> int: + usage = response.get("usage") if isinstance(response, dict) else getattr(response, "usage", None) + if usage is None: + return 0 + if isinstance(usage, dict): + total = int(usage.get("total_tokens") or 0) + if total: + return total + return int(usage.get("input_tokens") or 0) + int(usage.get("output_tokens") or 0) + return int(getattr(usage, "total_tokens", 0) or 0) + + +def apply_remaining_usage_headers( + additional_headers: dict[str, object], + remaining_usage: dict[str, int], + in_flight_tokens: int, +) -> None: + in_flight_delta = { + "x-ratelimit-remaining-tokens": in_flight_tokens, + "x-ratelimit-remaining-requests": 1, + } + for header, value in remaining_usage.items(): + if value is not None and header not in additional_headers: + additional_headers[header] = value - in_flight_delta.get(header, 0) + + +def _normalize_hidden_params(hidden_params: object) -> dict[str, object]: if isinstance(hidden_params, BaseModel): return cast("dict[str, object]", hidden_params.model_dump()) if isinstance(hidden_params, dict): @@ -24,6 +114,29 @@ def get_hidden_params_dict(response: object) -> dict[str, object]: return {} +def get_hidden_params_dict( + response: object, + *, + create: bool = False, +) -> dict[str, object]: + if isinstance(response, dict): + hidden_params = _normalize_hidden_params(response.get("_hidden_params")) + if not hidden_params and create: + hidden_params = {} + response["_hidden_params"] = hidden_params + return hidden_params + + hidden_params = _normalize_hidden_params(cast(object, getattr(response, "_hidden_params", None))) + return hidden_params + + +def _write_hidden_params(response: object, hidden_params: dict[str, object]) -> None: + if isinstance(response, dict): + response["_hidden_params"] = hidden_params + elif hasattr(response, "_hidden_params"): + cast(_HiddenParamsHost, response)._hidden_params = hidden_params + + def _ensure_additional_headers_dict( hidden_params: dict[str, object], ) -> dict[str, object]: @@ -73,15 +186,19 @@ def _add_headers_to_response(response: object, headers: dict[str, object]) -> ob if response is None: return response - if not isinstance(response, BaseModel) and not hasattr(response, "_hidden_params"): + if ( + not isinstance(response, BaseModel) + and not isinstance(response, dict) + and not hasattr(response, "_hidden_params") + ): return response - hidden_params = get_hidden_params_dict(response) + hidden_params = get_hidden_params_dict(response, create=isinstance(response, dict)) additional_headers = _ensure_additional_headers_dict(hidden_params) additional_headers.update(headers) hidden_params["additional_headers"] = additional_headers - cast(_HiddenParamsHost, response)._hidden_params = hidden_params + _write_hidden_params(response, hidden_params) return response @@ -127,12 +244,12 @@ def add_fallback_headers_to_response( if fallback_errors is None or response is None: return response - hidden_params = get_hidden_params_dict(response) + hidden_params = get_hidden_params_dict(response, create=isinstance(response, dict)) additional_headers = _ensure_additional_headers_dict(hidden_params) merged_errors = get_fallback_errors_from_headers(additional_headers) + [ cast("dict[str, object]", error) for error in fallback_errors ] additional_headers["x-litellm-fallback-errors"] = json.dumps(merged_errors) hidden_params["additional_headers"] = additional_headers - cast(_HiddenParamsHost, response)._hidden_params = hidden_params + _write_hidden_params(response, hidden_params) return response diff --git a/litellm/router_utils/clientside_credential_handler.py b/litellm/router_utils/clientside_credential_handler.py index e992ef63658..8234d89e248 100644 --- a/litellm/router_utils/clientside_credential_handler.py +++ b/litellm/router_utils/clientside_credential_handler.py @@ -52,6 +52,13 @@ def _admin_config_fields_to_clear_on_base_override() -> List[str]: "oci_tenancy", "oci_key", "oci_key_file", + # NVIDIA Riva fields — consumed by + # ``litellm/llms/nvidia_riva/audio_transcription/handler.py`` via + # optional_params and not declared on CredentialLiteLLMParams. + # Admin-pinned values must not flow through on a caller-redirected + # ``api_base`` for the same reason as the OCI entries above. + "nvcf_function_id", + "use_ssl", ] return typed_fields + kwargs_only_fields diff --git a/litellm/router_utils/fallback_event_handlers.py b/litellm/router_utils/fallback_event_handlers.py index 17d29ec0da8..eb92e9b1c29 100644 --- a/litellm/router_utils/fallback_event_handlers.py +++ b/litellm/router_utils/fallback_event_handlers.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union import litellm from litellm._logging import verbose_router_logger from litellm.integrations.custom_logger import CustomLogger +from litellm.litellm_core_utils.sensitive_data_masker import mask_sensitive_structure from litellm.router_utils.add_retry_fallback_headers import ( add_fallback_headers_to_response, get_fallback_error_info, @@ -126,7 +127,7 @@ async def run_async_fallback( try: # LOGGING kwargs = litellm_router.log_retry(kwargs=kwargs, e=original_exception) - verbose_router_logger.info(f"Falling back to model_group = {mg}") + verbose_router_logger.info(f"Falling back to model_group = {mask_sensitive_structure(mg)}") if isinstance(mg, str): kwargs["model"] = mg elif isinstance(mg, dict): diff --git a/litellm/router_utils/pre_call_checks/io_token_rate_limit_check.py b/litellm/router_utils/pre_call_checks/io_token_rate_limit_check.py new file mode 100644 index 00000000000..62c99a1c9f6 --- /dev/null +++ b/litellm/router_utils/pre_call_checks/io_token_rate_limit_check.py @@ -0,0 +1,711 @@ +""" +Separate ITPM/OTPM (input/output tokens per minute) deployment rate limits. + +- Pre-call: atomically reserve estimated_input against ITPM and max_tokens against OTPM +- Post-call: reconcile ITPM to actual input tokens and OTPM to actual output tokens +- Cached prompt-read tokens are excluded from ITPM post-call accounting + +Used by ModelRateLimitingCheck when a deployment sets itpm/otpm. +""" + +from __future__ import annotations + +import contextlib +import contextvars +from typing import TYPE_CHECKING, Any, Optional + +import httpx + +import litellm +from litellm import token_counter +from litellm._logging import verbose_router_logger +from litellm.caching.dual_cache import DualCache +from litellm.types.router import RouterCacheEnum, RouterErrors +from litellm.utils import get_utc_datetime + +if TYPE_CHECKING: + from opentelemetry.trace import Span as _Span + + Span = _Span | Any +else: + Span = Any + +RoutingArgsTTL = 60 + +_io_token_rate_limit_request_kwargs: contextvars.ContextVar[Optional[dict[str, Any]]] = contextvars.ContextVar( + "io_token_rate_limit_request_kwargs", + default=None, +) + +ITPM_RESERVED_KEY = "_litellm_itpm_reserved" +OTPM_RESERVED_KEY = "_litellm_otpm_reserved" +ITPM_CACHE_KEY = "_litellm_itpm_cache_key" +OTPM_CACHE_KEY = "_litellm_otpm_cache_key" + + +def set_io_token_rate_limit_request_kwargs(kwargs: Optional[dict[str, Any]]) -> None: + # The reservation sentinels are server-only, but `metadata` is caller + # controlled on proxy requests. Strip any client-supplied copies here (this + # runs before the router stashes its own reservation) so a forged + # reservation can't drive the post-call reconcile/refund against an + # arbitrary counter and bypass the configured limits. + _clear_reservation_from_kwargs(kwargs) + _io_token_rate_limit_request_kwargs.set(kwargs) + + +def get_io_token_rate_limit_request_kwargs() -> Optional[dict[str, Any]]: + return _io_token_rate_limit_request_kwargs.get() + + +def seconds_until_minute_reset() -> int: + dt = get_utc_datetime() + return max(1, 60 - dt.second) + + +def get_deployment_io_token_limits( + deployment: dict, +) -> tuple[Optional[int], Optional[int]]: + itpm = deployment.get("itpm") + otpm = deployment.get("otpm") + litellm_params = deployment.get("litellm_params") or {} + model_info = deployment.get("model_info") or {} + if itpm is None: + itpm = litellm_params.get("itpm") + if otpm is None: + otpm = litellm_params.get("otpm") + if itpm is None: + itpm = model_info.get("itpm") + if otpm is None: + otpm = model_info.get("otpm") + return itpm, otpm + + +def deployment_has_io_token_limits(deployment: dict) -> bool: + itpm, otpm = get_deployment_io_token_limits(deployment) + return itpm is not None or otpm is not None + + +def _get_cache_keys(deployment: dict, current_minute: str) -> Optional[tuple[str, str]]: + model_id = deployment.get("model_info", {}).get("id") + deployment_name = deployment.get("litellm_params", {}).get("model") + # Without both a deployment id and model name the key would collapse to a + # shared "None:None" bucket across misconfigured deployments, so bail out. + if model_id is None or deployment_name is None: + return None + itpm_key = RouterCacheEnum.ITPM.value.format(id=model_id, model=deployment_name, current_minute=current_minute) + otpm_key = RouterCacheEnum.OTPM.value.format(id=model_id, model=deployment_name, current_minute=current_minute) + return itpm_key, otpm_key + + +def _estimate_input_tokens(request_kwargs: Optional[dict[str, Any]], model: str = "") -> int: + if not request_kwargs: + return 0 + messages = request_kwargs.get("messages") + prompt = request_kwargs.get("prompt") + input_text = request_kwargs.get("input") + # token_counter can raise from any of its tokenizer backends; this is a + # best-effort estimate for the ITPM reservation and must never fail the + # underlying request. Passing the deployment model name uses a model-specific + # tokenizer when available, reducing the reservation over/under-estimate window + # between pre-call and post-call reconcile. + with contextlib.suppress(Exception): + return max(0, int(token_counter(model=model, messages=messages, text=prompt or input_text))) + return 0 + + +def _model_max_output_tokens(model_name: str) -> Optional[int]: + # litellm.get_model_info raises a bare Exception for an unrecognized model; + # this lookup is a fallback default and must never fail the request. + with contextlib.suppress(Exception): + info = litellm.get_model_info(model=model_name) + model_max = info.get("max_output_tokens") or info.get("max_tokens") + if model_max is not None: + return max(0, int(model_max)) + return None + + +def _resolve_max_tokens(request_kwargs: Optional[dict[str, Any]], deployment: dict) -> int: + if request_kwargs: + # An explicit max_tokens=0 must be honored, not treated as absent and + # replaced by the model default. + explicit = request_kwargs.get("max_tokens") + if explicit is None: + explicit = request_kwargs.get("max_completion_tokens") + if explicit is None: + explicit = request_kwargs.get("max_output_tokens") + if explicit is not None: + return max(0, int(explicit)) + + model_name = (deployment.get("litellm_params") or {}).get("model") + if model_name: + model_max = _model_max_output_tokens(model_name) + if model_max is not None: + return model_max + return 4096 + + +def _get_usage_tokens(usage: Any) -> tuple[int, int, int]: + if usage is None: + return 0, 0, 0 + if hasattr(usage, "prompt_tokens") or hasattr(usage, "input_tokens"): + prompt = int(getattr(usage, "prompt_tokens", None) or getattr(usage, "input_tokens", 0) or 0) + completion = int(getattr(usage, "completion_tokens", None) or getattr(usage, "output_tokens", 0) or 0) + cached = 0 + details = getattr(usage, "prompt_tokens_details", None) + if details is not None: + cached = int(getattr(details, "cached_tokens", 0) or 0) + if not cached: + cached = int(getattr(usage, "cache_read_input_tokens", 0) or 0) + return prompt, completion, cached + if isinstance(usage, dict): + prompt = int(usage.get("prompt_tokens") or usage.get("input_tokens") or 0) + completion = int(usage.get("completion_tokens") or usage.get("output_tokens") or 0) + details = usage.get("prompt_tokens_details") or {} + cached = int(details.get("cached_tokens", 0) or 0) if isinstance(details, dict) else 0 + if not cached: + cached = int(usage.get("cache_read_input_tokens") or 0) + return prompt, completion, cached + return 0, 0, 0 + + +def _extract_response_usage(response_obj: Any) -> Any: + if isinstance(response_obj, dict): + return response_obj.get("usage") + return getattr(response_obj, "usage", None) + + +def _usage_is_present(usage: Any) -> bool: + """ + True only if usage carries an actual input/output breakdown. + + ``total_tokens`` alone is deliberately excluded: ``_get_usage_tokens`` has + no way to split a bare total into input vs. output, so treating it as + "present" would resolve to (0, 0) and refund the full reservation as if + zero tokens were used. + """ + if usage is None: + return False + fields = ("prompt_tokens", "completion_tokens", "input_tokens", "output_tokens") + if isinstance(usage, dict): + return any(key in usage for key in fields) + return any(hasattr(usage, key) for key in fields) + + +def _resolve_reconcile_usage_tokens( + kwargs: Any, + response_obj: Any, +) -> tuple[int, int, bool]: + """ + Resolve billable input and output tokens for post-call reconcile. + + Prefer the response usage object; fall back to standard_logging_object token + fields. When usage cannot be resolved, return ``usage_resolved=False`` so + callers keep the pre-call reservation instead of refunding it as zero usage. + """ + usage = _extract_response_usage(response_obj) + if _usage_is_present(usage): + prompt_tokens, completion_tokens, cached_tokens = _get_usage_tokens(usage) + return max(0, prompt_tokens - cached_tokens), completion_tokens, True + + if isinstance(kwargs, dict): + standard_logging_object = kwargs.get("standard_logging_object") + if isinstance(standard_logging_object, dict): + prompt_tokens = int(standard_logging_object.get("prompt_tokens") or 0) + completion_tokens = int(standard_logging_object.get("completion_tokens") or 0) + cached_tokens = 0 + metadata = standard_logging_object.get("metadata") + if isinstance(metadata, dict): + cached_tokens = int(metadata.get("cache_read_input_tokens") or 0) + # Same rationale as _usage_is_present: a bare total_tokens with no + # prompt/completion breakdown can't be split, so it isn't treated + # as resolved usage - the reservation is kept instead of refunded. + if prompt_tokens or completion_tokens: + return max(0, prompt_tokens - cached_tokens), completion_tokens, True + + return 0, 0, False + + +def _stash_reservation_in_metadata( + request_kwargs: Optional[dict[str, Any]], + *, + itpm_reserved: int, + otpm_reserved: int, + itpm_cache_key: Optional[str], + otpm_cache_key: Optional[str], +) -> None: + if not request_kwargs: + return + reservation = { + ITPM_RESERVED_KEY: itpm_reserved, + OTPM_RESERVED_KEY: otpm_reserved, + ITPM_CACHE_KEY: itpm_cache_key, + OTPM_CACHE_KEY: otpm_cache_key, + } + for channel in ("metadata", "litellm_metadata"): + existing = request_kwargs.get(channel) + if isinstance(existing, dict): + existing.update(reservation) + elif channel == "metadata": + request_kwargs[channel] = dict(reservation) + + +def _extract_reservation(reservation: dict[str, Any]) -> tuple[int, int, Optional[str], Optional[str]]: + itpm_cache_key = reservation.get(ITPM_CACHE_KEY) + otpm_cache_key = reservation.get(OTPM_CACHE_KEY) + return ( + int(reservation.get(ITPM_RESERVED_KEY, 0) or 0), + int(reservation.get(OTPM_RESERVED_KEY, 0) or 0), + itpm_cache_key if isinstance(itpm_cache_key, str) else None, + otpm_cache_key if isinstance(otpm_cache_key, str) else None, + ) + + +def _reservation_channels(kwargs: Any) -> tuple[Any, ...]: + """ + Places a reservation may live, in priority order: the top-level metadata + channels win over litellm_params.metadata (so a top-level stash is never + shadowed), which win over the standard_logging_object copy. + """ + if not isinstance(kwargs, dict): + return () + channels = [kwargs.get("metadata"), kwargs.get("litellm_metadata")] + litellm_params = kwargs.get("litellm_params") + if isinstance(litellm_params, dict): + channels.append(litellm_params.get("metadata")) + standard_logging_object = kwargs.get("standard_logging_object") + if isinstance(standard_logging_object, dict): + channels.append(standard_logging_object.get("metadata")) + return tuple(channels) + + +def _read_reservation_from_kwargs(kwargs: Any) -> tuple[int, int, Optional[str], Optional[str]]: + for channel_dict in _reservation_channels(kwargs): + if isinstance(channel_dict, dict) and ITPM_RESERVED_KEY in channel_dict: + return _extract_reservation(channel_dict) + return 0, 0, None, None + + +def _clear_reservation_from_kwargs(kwargs: Any) -> None: + """ + Remove the stashed reservation so a retry on a different (e.g. non-IO) + deployment does not re-process the already-reconciled/refunded reservation. + """ + for channel_dict in _reservation_channels(kwargs): + if isinstance(channel_dict, dict): + for key in (ITPM_RESERVED_KEY, OTPM_RESERVED_KEY, ITPM_CACHE_KEY, OTPM_CACHE_KEY): + channel_dict.pop(key, None) + + +def _reservation_value(value: int, limit: Optional[int]) -> int: + if limit is None: + return 0 + if value > 0: + return value + # Estimation failed (empty messages, unsupported model, tokenizer error). + # Reserve a minimal 1-token slot rather than the full limit: the latter + # would let one request whose estimate failed fill the entire bucket, + # serializing every concurrent request to the deployment until it + # completes and reconciles against actual usage. + return 1 + + +def _rate_limit_error(limit_label: str, limit: int, current: float) -> litellm.RateLimitError: + return litellm.RateLimitError( + message=f"Model rate limit exceeded. {limit_label} limit={limit}, current usage={current}", + llm_provider="", + model="", + response=httpx.Response( + status_code=429, + content=( + f"{RouterErrors.user_defined_ratelimit_error.value} " + f"{limit_label} limit={limit}. current usage={current}." + ), + headers={"retry-after": str(RoutingArgsTTL)}, + request=httpx.Request( + method="io_token_rate_limit_check", + url="https://github.com/BerriAI/litellm", + ), + ), + num_retries=0, + ) + + +def _sync_increment_with_rollback( + dual_cache: DualCache, + key: str, + value: int, + limit: Optional[int], + *, + limit_label: str, +) -> None: + if value <= 0 or limit is None: + return + current = dual_cache.increment_cache( + key=key, + value=value, + ttl=RoutingArgsTTL, + ) + if current is not None and current > limit: + dual_cache.increment_cache( + key=key, + value=-value, + ttl=RoutingArgsTTL, + ) + raise _rate_limit_error(limit_label, limit, current) + + +async def _increment_with_rollback( + dual_cache: DualCache, + key: str, + value: int, + limit: Optional[int], + *, + parent_otel_span: Optional[Span] = None, + limit_label: str, +) -> None: + if value <= 0 or limit is None: + return + current = await dual_cache.async_increment_cache( + key=key, + value=value, + ttl=RoutingArgsTTL, + parent_otel_span=parent_otel_span, + ) + if current is not None and current > limit: + await dual_cache.async_increment_cache( + key=key, + value=-value, + ttl=RoutingArgsTTL, + parent_otel_span=parent_otel_span, + ) + raise _rate_limit_error(limit_label, limit, current) + + +def io_token_pre_call_check( + dual_cache: DualCache, + deployment: dict, +) -> Optional[dict]: + itpm_limit, otpm_limit = get_deployment_io_token_limits(deployment) + if itpm_limit is None and otpm_limit is None: + return deployment + + request_kwargs = get_io_token_rate_limit_request_kwargs() + _model = (deployment.get("litellm_params") or {}).get("model") or "" + estimated_input = _estimate_input_tokens(request_kwargs, model=_model) + max_tokens = _resolve_max_tokens(request_kwargs, deployment) + + dt = get_utc_datetime() + current_minute = dt.strftime("%H-%M") + cache_keys = _get_cache_keys(deployment, current_minute) + if cache_keys is None: + return deployment + itpm_key, otpm_key = cache_keys + + itpm_reserved = 0 + otpm_reserved = 0 + + if itpm_limit is not None: + itpm_reserved = _reservation_value(estimated_input, itpm_limit) + _sync_increment_with_rollback( + dual_cache, + itpm_key, + itpm_reserved, + itpm_limit, + limit_label="ITPM", + ) + + if otpm_limit is not None: + otpm_reserved = 0 if max_tokens == 0 else _reservation_value(max_tokens, otpm_limit) + try: + _sync_increment_with_rollback( + dual_cache, + otpm_key, + otpm_reserved, + otpm_limit, + limit_label="OTPM", + ) + except Exception: + if itpm_reserved > 0: + dual_cache.increment_cache( + key=itpm_key, + value=-itpm_reserved, + ttl=RoutingArgsTTL, + ) + raise + + _stash_reservation_in_metadata( + request_kwargs, + itpm_reserved=itpm_reserved, + otpm_reserved=otpm_reserved, + itpm_cache_key=itpm_key if itpm_limit is not None else None, + otpm_cache_key=otpm_key if otpm_limit is not None else None, + ) + return deployment + + +async def async_io_token_pre_call_check( + dual_cache: DualCache, + deployment: dict, + parent_otel_span: Optional[Span] = None, +) -> Optional[dict]: + itpm_limit, otpm_limit = get_deployment_io_token_limits(deployment) + if itpm_limit is None and otpm_limit is None: + return deployment + + request_kwargs = get_io_token_rate_limit_request_kwargs() + _model = (deployment.get("litellm_params") or {}).get("model") or "" + estimated_input = _estimate_input_tokens(request_kwargs, model=_model) + max_tokens = _resolve_max_tokens(request_kwargs, deployment) + + dt = get_utc_datetime() + current_minute = dt.strftime("%H-%M") + cache_keys = _get_cache_keys(deployment, current_minute) + if cache_keys is None: + return deployment + itpm_key, otpm_key = cache_keys + + itpm_reserved = 0 + otpm_reserved = 0 + + if itpm_limit is not None: + itpm_reserved = _reservation_value(estimated_input, itpm_limit) + await _increment_with_rollback( + dual_cache, + itpm_key, + itpm_reserved, + itpm_limit, + parent_otel_span=parent_otel_span, + limit_label="ITPM", + ) + + if otpm_limit is not None: + otpm_reserved = 0 if max_tokens == 0 else _reservation_value(max_tokens, otpm_limit) + try: + await _increment_with_rollback( + dual_cache, + otpm_key, + otpm_reserved, + otpm_limit, + parent_otel_span=parent_otel_span, + limit_label="OTPM", + ) + except Exception: + # Any failure reserving OTPM (a 429 or a transient cache error) must + # release the ITPM reservation already made, or it stays inflated + # until the TTL expires. + if itpm_reserved > 0: + await dual_cache.async_increment_cache( + key=itpm_key, + value=-itpm_reserved, + ttl=RoutingArgsTTL, + parent_otel_span=parent_otel_span, + ) + raise + + _stash_reservation_in_metadata( + request_kwargs, + itpm_reserved=itpm_reserved, + otpm_reserved=otpm_reserved, + itpm_cache_key=itpm_key if itpm_limit is not None else None, + otpm_cache_key=otpm_key if otpm_limit is not None else None, + ) + return deployment + + +def io_token_reconcile_success( + dual_cache: DualCache, + kwargs: Any, + response_obj: Any, +) -> None: + itpm_reserved, otpm_reserved, itpm_key, otpm_key = _read_reservation_from_kwargs(kwargs) + if itpm_key is None and otpm_key is None: + return + + billable_input, completion_tokens, usage_resolved = _resolve_reconcile_usage_tokens(kwargs, response_obj) + + try: + if usage_resolved: + if itpm_key is not None: + itpm_delta = billable_input - itpm_reserved + if itpm_delta != 0: + dual_cache.increment_cache( + key=itpm_key, + value=itpm_delta, + ttl=RoutingArgsTTL, + ) + + if otpm_key is not None: + otpm_delta = completion_tokens - otpm_reserved + if otpm_delta != 0: + dual_cache.increment_cache( + key=otpm_key, + value=otpm_delta, + ttl=RoutingArgsTTL, + ) + else: + verbose_router_logger.debug( + "[IO TOKEN LIMIT] usage missing; keeping reservation " + f"(itpm_reserved={itpm_reserved}, otpm_reserved={otpm_reserved})" + ) + finally: + _clear_reservation_from_kwargs(kwargs) + + verbose_router_logger.debug( + f"[IO TOKEN LIMIT] reconciled " + f"(usage_resolved={usage_resolved}, itpm_reserved={itpm_reserved}, " + f"billable_input={billable_input}, otpm_reserved={otpm_reserved}, output={completion_tokens})" + ) + + +async def async_io_token_reconcile_success( + dual_cache: DualCache, + kwargs: Any, + response_obj: Any, + *, + parent_otel_span: Optional[Span] = None, +) -> None: + itpm_reserved, otpm_reserved, itpm_key, otpm_key = _read_reservation_from_kwargs(kwargs) + if itpm_key is None and otpm_key is None: + return + + billable_input, completion_tokens, usage_resolved = _resolve_reconcile_usage_tokens(kwargs, response_obj) + + # Reconcile against the exact key that held the reservation (which encodes + # the reservation's minute), not a key recomputed at response time. This + # tracks actual usage even when the pre-call estimate was 0, and avoids a + # minute-boundary mismatch for calls that span into the next minute. Always + # clear the stash afterwards (even if an increment throws) so a retry or a + # duplicate success event can't re-process it. + try: + if usage_resolved: + if itpm_key is not None: + itpm_delta = billable_input - itpm_reserved + if itpm_delta != 0: + await dual_cache.async_increment_cache( + key=itpm_key, + value=itpm_delta, + ttl=RoutingArgsTTL, + parent_otel_span=parent_otel_span, + ) + + if otpm_key is not None: + otpm_delta = completion_tokens - otpm_reserved + if otpm_delta != 0: + await dual_cache.async_increment_cache( + key=otpm_key, + value=otpm_delta, + ttl=RoutingArgsTTL, + parent_otel_span=parent_otel_span, + ) + else: + verbose_router_logger.debug( + "[IO TOKEN LIMIT] usage missing; keeping reservation " + f"(itpm_reserved={itpm_reserved}, otpm_reserved={otpm_reserved})" + ) + finally: + _clear_reservation_from_kwargs(kwargs) + + verbose_router_logger.debug( + f"[IO TOKEN LIMIT] reconciled " + f"(usage_resolved={usage_resolved}, itpm_reserved={itpm_reserved}, " + f"billable_input={billable_input}, otpm_reserved={otpm_reserved}, output={completion_tokens})" + ) + + +def io_token_refund_failure( + dual_cache: DualCache, + kwargs: Any, +) -> None: + itpm_reserved, otpm_reserved, itpm_key, otpm_key = _read_reservation_from_kwargs(kwargs) + if itpm_key is None and otpm_key is None: + return + if itpm_key is not None and itpm_reserved > 0: + dual_cache.increment_cache( + key=itpm_key, + value=-itpm_reserved, + ttl=RoutingArgsTTL, + ) + if otpm_key is not None and otpm_reserved > 0: + dual_cache.increment_cache( + key=otpm_key, + value=-otpm_reserved, + ttl=RoutingArgsTTL, + ) + _clear_reservation_from_kwargs(kwargs) + verbose_router_logger.debug(f"[IO TOKEN LIMIT] refunded ITPM={itpm_reserved} OTPM={otpm_reserved}") + + +def refund_stale_reservation_before_retry(dual_cache: DualCache, kwargs: Optional[dict[str, Any]]) -> None: + """ + Synchronously refund and clear any reservation a previous deployment + attempt stashed in ``kwargs``, before it's overwritten for the next + attempt (retry/fallback). + + ``set_io_token_rate_limit_request_kwargs`` strips reservation sentinels + from ``kwargs`` on every deployment pick (a security measure so a + caller-forged reservation can't be replayed). Without this refund, a + retry after a non-RateLimitError failure (e.g. an upstream 500) would + wipe deployment A's still-unreconciled reservation before its failure + event - which may be scheduled as a background task - gets a chance to + refund it, permanently stranding the reservation until its TTL expires + and causing false rate-limit errors for subsequent requests. + + ponytail: uses sync ``DualCache.increment_cache`` which issues a blocking + Redis INCR when a Redis backend is configured. This only triggers on + streaming mid-stream retries (non-streaming failures await their failure + handler before retrying, so the sentinels are already cleared). Upgrade + path: make ``_update_kwargs_with_deployment`` async and switch to + ``async_io_token_refund_failure`` — requires touching all callers. + """ + if not kwargs: + return + io_token_refund_failure(dual_cache, kwargs) + + +async def async_io_token_refund_failure( + dual_cache: DualCache, + kwargs: Any, + *, + parent_otel_span: Optional[Span] = None, +) -> None: + itpm_reserved, otpm_reserved, itpm_key, otpm_key = _read_reservation_from_kwargs(kwargs) + if itpm_key is None and otpm_key is None: + return + if itpm_key is not None and itpm_reserved > 0: + await dual_cache.async_increment_cache( + key=itpm_key, + value=-itpm_reserved, + ttl=RoutingArgsTTL, + parent_otel_span=parent_otel_span, + ) + if otpm_key is not None and otpm_reserved > 0: + await dual_cache.async_increment_cache( + key=otpm_key, + value=-otpm_reserved, + ttl=RoutingArgsTTL, + parent_otel_span=parent_otel_span, + ) + _clear_reservation_from_kwargs(kwargs) + verbose_router_logger.debug(f"[IO TOKEN LIMIT] refunded ITPM={itpm_reserved} OTPM={otpm_reserved}") + + +def build_io_token_rate_limit_headers( + *, + itpm_limit: Optional[int], + otpm_limit: Optional[int], + current_itpm: Optional[int], + current_otpm: Optional[int], +) -> dict[str, int]: + headers: dict[str, int] = {} + reset = seconds_until_minute_reset() + if itpm_limit is not None: + usage = current_itpm or 0 + headers["x-ratelimit-limit-input-tokens"] = itpm_limit + headers["x-ratelimit-remaining-input-tokens"] = max(0, itpm_limit - usage) + headers["x-ratelimit-reset-input-tokens"] = reset + if otpm_limit is not None: + usage = current_otpm or 0 + headers["x-ratelimit-limit-output-tokens"] = otpm_limit + headers["x-ratelimit-remaining-output-tokens"] = max(0, otpm_limit - usage) + headers["x-ratelimit-reset-output-tokens"] = reset + return headers diff --git a/litellm/router_utils/pre_call_checks/model_rate_limit_check.py b/litellm/router_utils/pre_call_checks/model_rate_limit_check.py index 0c6450b191f..373563ca442 100644 --- a/litellm/router_utils/pre_call_checks/model_rate_limit_check.py +++ b/litellm/router_utils/pre_call_checks/model_rate_limit_check.py @@ -1,13 +1,16 @@ """ -Enforce TPM/RPM rate limits set on model deployments. +Enforce TPM/RPM or separate ITPM/OTPM rate limits set on model deployments. -This pre-call check ensures that model-level TPM/RPM limits are enforced -across all requests, regardless of routing strategy. +When enabled via router_settings.optional_pre_call_checks: ["enforce_model_rate_limits"] -When enabled via `enforce_model_rate_limits: true` in litellm_settings, -requests that exceed the configured TPM/RPM limits will receive a 429 error. +- tpm/rpm: combined TPM + optional RPM (legacy) +- itpm/otpm: separate input/output tokens per minute + +When a deployment sets both itpm/otpm and tpm/rpm, both are enforced. A warning +is logged the first time such a deployment is seen. """ +import contextlib from typing import TYPE_CHECKING, Any, Dict, Optional, Union import httpx @@ -16,6 +19,17 @@ import litellm from litellm._logging import verbose_router_logger from litellm.caching.dual_cache import DualCache from litellm.integrations.custom_logger import CustomLogger +from litellm.router_utils.pre_call_checks.io_token_rate_limit_check import ( + async_io_token_pre_call_check, + async_io_token_reconcile_success, + async_io_token_refund_failure, + deployment_has_io_token_limits, + get_io_token_rate_limit_request_kwargs, + io_token_pre_call_check, + io_token_reconcile_success, + io_token_refund_failure, + ITPM_RESERVED_KEY, +) from litellm.types.router import RouterErrors from litellm.types.utils import StandardLoggingPayload from litellm.utils import get_utc_datetime @@ -34,7 +48,7 @@ class RoutingArgs: class ModelRateLimitingCheck(CustomLogger): """ - Pre-call check that enforces TPM/RPM limits on model deployments. + Pre-call check that enforces TPM/RPM or ITPM/OTPM limits on model deployments. This check runs before each request and raises a RateLimitError if the deployment has exceeded its configured TPM or RPM limits. @@ -45,6 +59,42 @@ class ModelRateLimitingCheck(CustomLogger): def __init__(self, dual_cache: DualCache): self.dual_cache = dual_cache + # model_ids already warned about itpm/otpm + tpm/rpm on the same deployment, + # so the warning is logged once per deployment rather than per request. + self._io_token_conflict_warned_ids: set[str] = set() + + def _warn_io_token_and_tpm_rpm_coexist_once(self, deployment: dict) -> None: + tpm_limit, rpm_limit = self._get_deployment_limits(deployment) + if tpm_limit is None and rpm_limit is None: + return + model_id = deployment.get("model_info", {}).get("id") + # Dedup per deployment id; if there is no id (degenerate config) don't + # collapse every such deployment onto one key - warn each time instead. + if model_id is not None: + if model_id in self._io_token_conflict_warned_ids: + return + self._io_token_conflict_warned_ids.add(str(model_id)) + verbose_router_logger.warning( + f"Deployment '{model_id}' configures itpm/otpm alongside tpm/rpm; " + "both limit types are enforced on this deployment" + ) + + def _refund_io_token_reservation_if_any(self) -> None: + request_kwargs = get_io_token_rate_limit_request_kwargs() + if request_kwargs is not None: + io_token_refund_failure(self.dual_cache, request_kwargs) + + async def _async_refund_io_token_reservation_if_any( + self, + parent_otel_span: Optional[Span] = None, + ) -> None: + request_kwargs = get_io_token_rate_limit_request_kwargs() + if request_kwargs is not None: + await async_io_token_refund_failure( + self.dual_cache, + request_kwargs, + parent_otel_span=parent_otel_span, + ) def _get_deployment_limits(self, deployment: Dict) -> tuple[Optional[int], Optional[int]]: """ @@ -93,6 +143,15 @@ class ModelRateLimitingCheck(CustomLogger): Raises RateLimitError if deployment exceeds TPM/RPM limits. """ try: + io_reservation_made = False + if deployment_has_io_token_limits(deployment): + self._warn_io_token_and_tpm_rpm_coexist_once(deployment) + io_token_pre_call_check( + self.dual_cache, + deployment, + ) + io_reservation_made = True + tpm_limit, rpm_limit = self._get_deployment_limits(deployment) # If no limits are set, allow the request @@ -149,6 +208,8 @@ class ModelRateLimitingCheck(CustomLogger): return deployment except litellm.RateLimitError: + if io_reservation_made: + self._refund_io_token_reservation_if_any() raise except Exception as e: verbose_router_logger.debug(f"Error in ModelRateLimitingCheck.pre_call_check: {str(e)}") @@ -159,9 +220,19 @@ class ModelRateLimitingCheck(CustomLogger): """ Async pre-call check for model rate limits. - Raises RateLimitError if deployment exceeds TPM/RPM limits. + Raises RateLimitError if deployment exceeds TPM/RPM or ITPM/OTPM limits. """ try: + io_reservation_made = False + if deployment_has_io_token_limits(deployment): + self._warn_io_token_and_tpm_rpm_coexist_once(deployment) + await async_io_token_pre_call_check( + self.dual_cache, + deployment, + parent_otel_span=parent_otel_span, + ) + io_reservation_made = True + tpm_limit, rpm_limit = self._get_deployment_limits(deployment) # If no limits are set, allow the request @@ -225,6 +296,8 @@ class ModelRateLimitingCheck(CustomLogger): return deployment except litellm.RateLimitError: + if io_reservation_made: + await self._async_refund_io_token_reservation_if_any(parent_otel_span=parent_otel_span) raise except Exception as e: verbose_router_logger.debug(f"Error in ModelRateLimitingCheck.async_pre_call_check: {str(e)}") @@ -232,14 +305,31 @@ class ModelRateLimitingCheck(CustomLogger): return deployment async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): - """ - Track TPM usage after successful request. + from litellm.litellm_core_utils.core_helpers import ( + _get_parent_otel_span_from_kwargs, + ) - This updates the TPM counter with the actual tokens used. - Always tracks tokens - the pre-call check handles enforcement. - """ try: standard_logging_object: Optional[StandardLoggingPayload] = kwargs.get("standard_logging_object") + + # IO token reconciliation works purely from the cache keys stashed in + # kwargs/metadata, so it must run before the model_id guard below + # (which only the TPM-tracking path needs). Otherwise a request whose + # standard_logging_object lacks model_id would never return its + # reservation, leaving the counter elevated until the TTL expires. + slo_metadata = (standard_logging_object.get("metadata") or {}) if standard_logging_object else {} + kwargs_metadata = kwargs.get("metadata") or {} + if ITPM_RESERVED_KEY in slo_metadata or ITPM_RESERVED_KEY in kwargs_metadata: + await async_io_token_reconcile_success( + self.dual_cache, + kwargs, + response_obj, + parent_otel_span=_get_parent_otel_span_from_kwargs(kwargs), + ) + # Fall through: a deployment can also configure tpm/rpm alongside + # itpm/otpm, and that path's pre-call check reads the tpm_key + # counter tracked below, so it must still be incremented here. + if standard_logging_object is None: return @@ -272,6 +362,19 @@ class ModelRateLimitingCheck(CustomLogger): except Exception as e: verbose_router_logger.debug(f"Error in ModelRateLimitingCheck.async_log_success_event: {str(e)}") + async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time): + from litellm.litellm_core_utils.core_helpers import ( + _get_parent_otel_span_from_kwargs, + ) + + # Never fail the primary logging pipeline over an io-token refund error. + with contextlib.suppress(Exception): + await async_io_token_refund_failure( + self.dual_cache, + kwargs, + parent_otel_span=_get_parent_otel_span_from_kwargs(kwargs), + ) + def log_success_event(self, kwargs, response_obj, start_time, end_time): """ Sync version of tracking TPM usage after successful request. @@ -279,6 +382,18 @@ class ModelRateLimitingCheck(CustomLogger): """ try: standard_logging_object: Optional[StandardLoggingPayload] = kwargs.get("standard_logging_object") + slo_metadata = (standard_logging_object.get("metadata") or {}) if standard_logging_object else {} + kwargs_metadata = kwargs.get("metadata") or {} + if ITPM_RESERVED_KEY in slo_metadata or ITPM_RESERVED_KEY in kwargs_metadata: + io_token_reconcile_success( + self.dual_cache, + kwargs, + response_obj, + ) + # Fall through: a deployment can also configure tpm/rpm alongside + # itpm/otpm, and that path's pre-call check reads the tpm_key + # counter tracked below, so it must still be incremented here. + if standard_logging_object is None: return @@ -304,3 +419,10 @@ class ModelRateLimitingCheck(CustomLogger): except Exception as e: verbose_router_logger.debug(f"Error in ModelRateLimitingCheck.log_success_event: {str(e)}") + + def log_failure_event(self, kwargs, response_obj, start_time, end_time): + with contextlib.suppress(Exception): + io_token_refund_failure( + self.dual_cache, + kwargs, + ) diff --git a/litellm/secret_managers/base_secret_manager.py b/litellm/secret_managers/base_secret_manager.py index d33d76093c9..2bb8dc73138 100644 --- a/litellm/secret_managers/base_secret_manager.py +++ b/litellm/secret_managers/base_secret_manager.py @@ -1,3 +1,4 @@ +import re from abc import ABC, abstractmethod from typing import Any, Dict, Optional, Union @@ -5,6 +6,20 @@ import httpx from litellm import verbose_logger +_UNSAFE_SECRET_NAME_PATTERN = re.compile(r"(^|/)\.\.(/|$)|[\x00-\x1f\x7f-\x9f…

]") + + +def raise_if_unsafe_secret_name(secret_name: str) -> None: + """ + Validate a secret name before it is used by a secret manager integration. + + Rejects ".." only as a path segment (bounded by "/" or the start/end of the + string, e.g. "../x", "x/..", or exactly ".."), not as a plain substring, so + names like "release-1.0..2" are not rejected. + """ + if _UNSAFE_SECRET_NAME_PATTERN.search(secret_name): + raise ValueError(f"Invalid secret_name {secret_name!r}") + class BaseSecretManager(ABC): """ diff --git a/litellm/secret_managers/cyberark_secret_manager.py b/litellm/secret_managers/cyberark_secret_manager.py index faf6224757f..2b888cb85f6 100644 --- a/litellm/secret_managers/cyberark_secret_manager.py +++ b/litellm/secret_managers/cyberark_secret_manager.py @@ -4,6 +4,7 @@ from typing import Any, Dict, Optional, Union from urllib.parse import quote import httpx +import yaml import litellm from litellm._logging import verbose_logger @@ -15,7 +16,7 @@ from litellm.llms.custom_httpx.http_handler import ( ) from litellm.proxy._types import KeyManagementSystem -from .base_secret_manager import BaseSecretManager +from .base_secret_manager import BaseSecretManager, raise_if_unsafe_secret_name from .main import str_to_bool @@ -125,8 +126,11 @@ class CyberArkSecretManager(BaseSecretManager): """ # In production, we'd check if the variable exists first # For now, we'll attempt to create it and ignore if it already exists + raise_if_unsafe_secret_name(secret_name) policy_url = f"{self.conjur_addr}/policies/{self.conjur_account}/policy/root" - policy_yaml = f"- !variable {secret_name}\n" + # Use a real YAML serializer to build the scalar safely. + quoted_name = yaml.safe_dump(secret_name, default_style='"').strip() + policy_yaml = f"- !variable {quoted_name}\n" try: client = _get_httpx_client(params={"ssl_verify": self.ssl_verify}) diff --git a/litellm/secret_managers/hashicorp_secret_manager.py b/litellm/secret_managers/hashicorp_secret_manager.py index bd1b1097347..039aecb9e58 100644 --- a/litellm/secret_managers/hashicorp_secret_manager.py +++ b/litellm/secret_managers/hashicorp_secret_manager.py @@ -14,7 +14,7 @@ from litellm.llms.custom_httpx.http_handler import ( ) from litellm.proxy._types import KeyManagementSystem -from .base_secret_manager import BaseSecretManager +from .base_secret_manager import BaseSecretManager, raise_if_unsafe_secret_name class HashicorpSecretManager(BaseSecretManager): @@ -220,6 +220,7 @@ class HashicorpSecretManager(BaseSecretManager): - With custom mount: http://127.0.0.1:8200/v1/kv/data/mykey - With path prefix: http://127.0.0.1:8200/v1/secret/data/myapp/mykey """ + raise_if_unsafe_secret_name(secret_name) resolved_namespace = self._sanitize_path_component(namespace if namespace is not None else self.vault_namespace) resolved_mount = self._sanitize_path_component(mount_name if mount_name is not None else self.vault_mount_name) if resolved_mount is None: diff --git a/litellm/setup_wizard.py b/litellm/setup_wizard.py index b43590079fa..10b4fb30f22 100644 --- a/litellm/setup_wizard.py +++ b/litellm/setup_wizard.py @@ -58,6 +58,7 @@ PROVIDERS: List[Dict] = [ "test_model": "claude-haiku-4-5-20251001", "models": [ "claude-fable-5", + "claude-sonnet-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", diff --git a/litellm/types/guardrails.py b/litellm/types/guardrails.py index 889e029b902..c7e080b1363 100644 --- a/litellm/types/guardrails.py +++ b/litellm/types/guardrails.py @@ -697,7 +697,7 @@ class BaseLitellmParams(ContentFilterConfigModel): # works for new and patch up default="fail_closed", description=( "Behavior when a guardrail endpoint is unreachable due to network errors. " - "Implemented by guardrail='generic_guardrail_api', 'akto', 'vigil_guard', and 'repelloai'. " + "Implemented by guardrail='generic_guardrail_api', 'akto', 'vigil_guard', 'repelloai', and 'headroom'. " "'fail_closed' raises an error (default). 'fail_open' logs a critical error and allows the request to proceed." ), ) @@ -904,6 +904,7 @@ class GuardrailUIAddGuardrailSettings(BaseModel): supported_entities: List[str] supported_actions: List[str] supported_modes: List[str] + supported_modes_by_provider: Dict[str, List[str]] pii_entity_categories: List[PiiEntityCategoryMap] content_filter_settings: Optional[Dict[str, Any]] = None diff --git a/litellm/types/integrations/anthropic_cache_control_hook.py b/litellm/types/integrations/anthropic_cache_control_hook.py index 3e2d0d688ac..601978bb04f 100644 --- a/litellm/types/integrations/anthropic_cache_control_hook.py +++ b/litellm/types/integrations/anthropic_cache_control_hook.py @@ -18,6 +18,7 @@ class CacheControlToolConfigInjectionPoint(TypedDict): """Type for tool_config-level injection points (Bedrock).""" location: Literal["tool_config"] + control: Optional[ChatCompletionCachedContent] CacheControlInjectionPoint = Union[ diff --git a/litellm/types/integrations/datadog.py b/litellm/types/integrations/datadog.py index 89faac27830..e0f43519b3d 100644 --- a/litellm/types/integrations/datadog.py +++ b/litellm/types/integrations/datadog.py @@ -6,6 +6,7 @@ from typing_extensions import NotRequired, TypedDict from litellm.types.integrations.custom_logger import StandardCustomLoggerInitParams DD_MAX_BATCH_SIZE = 1000 +DD_MAX_PAYLOAD_SIZE_BYTES = 4_000_000 class DataDogStatus(str, Enum): diff --git a/litellm/types/integrations/prometheus.py b/litellm/types/integrations/prometheus.py index fca3319254c..69bccff701f 100644 --- a/litellm/types/integrations/prometheus.py +++ b/litellm/types/integrations/prometheus.py @@ -188,6 +188,8 @@ class UserAPIKeyLabelNames(Enum): STREAM = "stream" ORG_ID = "org_id" ORG_ALIAS = "org_alias" + MCP_TOOL_NAME = "mcp_tool_name" + MCP_SERVER_NAME = "mcp_server_name" DEFINED_PROMETHEUS_METRICS = Literal[ @@ -264,6 +266,9 @@ DEFINED_PROMETHEUS_METRICS = Literal[ "litellm_check_batch_cost_jobs_processed_total", "litellm_check_batch_cost_errors_total", "litellm_check_batch_cost_last_run_timestamp", + # MCP tool call metrics + "litellm_mcp_tool_calls_total", + "litellm_mcp_tool_call_spend_metric", ] @@ -278,6 +283,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.END_USER.value, UserAPIKeyLabelNames.USER.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] litellm_llm_api_time_to_first_token_metric = [ @@ -290,6 +296,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.END_USER.value, UserAPIKeyLabelNames.USER.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] litellm_request_total_latency_metric = [ @@ -302,6 +309,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.USER.value, UserAPIKeyLabelNames.v1_LITELLM_MODEL_NAME.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] litellm_request_queue_time_seconds = [ @@ -314,6 +322,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.USER.value, UserAPIKeyLabelNames.v1_LITELLM_MODEL_NAME.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] # Guardrail metrics - these use custom labels (guardrail_name, status, error_type, hook_type) @@ -336,6 +345,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.CLIENT_IP.value, UserAPIKeyLabelNames.USER_AGENT.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] litellm_proxy_failed_requests_metric = [ @@ -357,6 +367,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.CLIENT_IP.value, UserAPIKeyLabelNames.USER_AGENT.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] litellm_deployment_latency_per_output_token = [ @@ -453,6 +464,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.USER_EMAIL.value, UserAPIKeyLabelNames.REQUESTED_MODEL.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] litellm_total_tokens_metric = [ @@ -466,6 +478,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.USER_EMAIL.value, UserAPIKeyLabelNames.REQUESTED_MODEL.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] litellm_output_tokens_metric = [ @@ -479,6 +492,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.USER_EMAIL.value, UserAPIKeyLabelNames.REQUESTED_MODEL.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] # Token-type detail metrics — reuse the same label set as @@ -677,6 +691,7 @@ class PrometheusMetricLabels: UserAPIKeyLabelNames.END_USER.value, UserAPIKeyLabelNames.USER.value, UserAPIKeyLabelNames.MODEL_ID.value, + UserAPIKeyLabelNames.API_PROVIDER.value, ] litellm_cache_hits_metric = _cache_metric_labels @@ -737,6 +752,20 @@ class PrometheusMetricLabels: litellm_check_batch_cost_last_run_timestamp: List[str] = [] + # MCP tool call metrics + litellm_mcp_tool_calls_total: list[str] = [ + UserAPIKeyLabelNames.MCP_TOOL_NAME.value, + UserAPIKeyLabelNames.MCP_SERVER_NAME.value, + UserAPIKeyLabelNames.API_KEY_HASH.value, + UserAPIKeyLabelNames.API_KEY_ALIAS.value, + UserAPIKeyLabelNames.TEAM.value, + UserAPIKeyLabelNames.TEAM_ALIAS.value, + UserAPIKeyLabelNames.USER.value, + UserAPIKeyLabelNames.END_USER.value, + ] + + litellm_mcp_tool_call_spend_metric: list[str] = list(litellm_mcp_tool_calls_total) + @staticmethod def get_labels(label_name: DEFINED_PROMETHEUS_METRICS) -> List[str]: default_labels = getattr(PrometheusMetricLabels, label_name) @@ -840,6 +869,8 @@ class UserAPIKeyLabelValues: stream: Optional[str] = None org_id: Optional[str] = None org_alias: Optional[str] = None + mcp_tool_name: Optional[str] = None + mcp_server_name: Optional[str] = None # Added for test compatibility. def __init__(self, **kwargs: Any) -> None: diff --git a/litellm/types/llms/bedrock.py b/litellm/types/llms/bedrock.py index a1dc08e5f29..bdf6b8fefed 100644 --- a/litellm/types/llms/bedrock.py +++ b/litellm/types/llms/bedrock.py @@ -325,7 +325,7 @@ class ToolConfigBlock(TypedDict, total=False): class GuardrailConfigBlock(TypedDict, total=False): guardrailIdentifier: str guardrailVersion: str - trace: Literal["enabled", "disabled"] + trace: Literal["enabled", "disabled", "enabled_full"] class InferenceConfig(TypedDict, total=False): diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index 4c656b32081..daac1e4506f 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -90,6 +90,7 @@ from typing_extensions import ( from litellm.types.llms.base import BaseLiteLLMOpenAIResponseObject from litellm.types.responses.main import ( + CustomToolCallOutputItem, GenericResponseOutputItem, OutputCodeInterpreterCall, OutputFunctionToolCall, @@ -914,6 +915,7 @@ class OpenAIChatCompletionToolParam(TypedDict): class ChatCompletionToolParam(OpenAIChatCompletionToolParam, total=False): cache_control: ChatCompletionCachedContent + allowed_callers: List[str] class Function(TypedDict, total=False): @@ -1183,7 +1185,7 @@ class ResponsesAPIRequestParams(ResponsesAPIOptionalRequestParams, total=False): class OutputTokensDetails(BaseLiteLLMOpenAIResponseObject): - reasoning_tokens: int = 0 + reasoning_tokens: Optional[int] = None text_tokens: Optional[int] = None @@ -1253,6 +1255,7 @@ class ResponsesAPIResponse(BaseLiteLLMOpenAIResponseObject): OutputFunctionToolCall, OutputImageGenerationCall, ResponseFunctionToolCall, + CustomToolCallOutputItem, ] ], ] @@ -1717,7 +1720,7 @@ class ErrorEventError(BaseLiteLLMOpenAIResponseObject): type: str # e.g., 'invalid_request_error' code: str # e.g., 'context_length_exceeded' message: str - param: Optional[str] = None + param: Optional[Union[str, Dict[str, Any]]] = None class ErrorEvent(BaseLiteLLMOpenAIResponseObject): diff --git a/litellm/types/llms/vertex_ai_speech_to_text.py b/litellm/types/llms/vertex_ai_speech_to_text.py new file mode 100644 index 00000000000..8995d98385b --- /dev/null +++ b/litellm/types/llms/vertex_ai_speech_to_text.py @@ -0,0 +1,40 @@ +from pydantic import BaseModel +from typing_extensions import TypedDict + + +class VertexSpeechToTextAutoDecodingConfig(TypedDict): + pass + + +class VertexSpeechToTextRecognitionFeatures(TypedDict): + enableAutomaticPunctuation: bool + + +class VertexSpeechToTextRecognitionConfig(TypedDict): + model: str + languageCodes: list[str] + features: VertexSpeechToTextRecognitionFeatures + autoDecodingConfig: VertexSpeechToTextAutoDecodingConfig + + +class VertexSpeechToTextRecognizeRequest(TypedDict): + config: VertexSpeechToTextRecognitionConfig + content: str + + +class VertexSpeechToTextAlternative(BaseModel): + transcript: str | None = None + + +class VertexSpeechToTextResult(BaseModel): + alternatives: list[VertexSpeechToTextAlternative] = [] + languageCode: str | None = None + + +class VertexSpeechToTextResponseMetadata(BaseModel): + totalBilledDuration: str | None = None + + +class VertexSpeechToTextRecognizeResponse(BaseModel): + results: list[VertexSpeechToTextResult] = [] + metadata: VertexSpeechToTextResponseMetadata | None = None diff --git a/litellm/types/management_endpoints/__init__.py b/litellm/types/management_endpoints/__init__.py index 5c5bcb2e754..3b501443edd 100644 --- a/litellm/types/management_endpoints/__init__.py +++ b/litellm/types/management_endpoints/__init__.py @@ -7,6 +7,12 @@ from .cache_settings_endpoints import ( REDIS_TYPE_DESCRIPTIONS, CacheSettingsField, ) +from .coordination_redis_endpoints import ( + COORDINATION_REDIS_SETTINGS_FIELDS, + CoordinationRedisSection, + CoordinationRedisSettingsField, + CoordinationRedisSource, +) from .router_settings_endpoints import ( ROUTER_SETTINGS_FIELDS, ROUTING_STRATEGY_DESCRIPTIONS, @@ -20,4 +26,8 @@ __all__ = [ "CACHE_SETTINGS_FIELDS", "REDIS_TYPE_DESCRIPTIONS", "CacheSettingsField", + "COORDINATION_REDIS_SETTINGS_FIELDS", + "CoordinationRedisSection", + "CoordinationRedisSettingsField", + "CoordinationRedisSource", ] diff --git a/litellm/types/management_endpoints/cache_settings_endpoints.py b/litellm/types/management_endpoints/cache_settings_endpoints.py index 9bccfed7c14..32ae70ac92e 100644 --- a/litellm/types/management_endpoints/cache_settings_endpoints.py +++ b/litellm/types/management_endpoints/cache_settings_endpoints.py @@ -40,6 +40,15 @@ CACHE_SETTINGS_FIELDS: List[CacheSettingsField] = [ redis_type=None, ), # Common fields for all Redis types + CacheSettingsField( + field_name="url", + field_type="String", + field_value=None, + field_description="Full Redis/Valkey connection URL (e.g. redis://:password@host:6379/1). When set, it takes precedence over Host, Port, Username, Password, and Database Index.", + field_default=None, + ui_field_name="Redis URL", + redis_type=None, + ), CacheSettingsField( field_name="host", field_type="String", @@ -58,6 +67,15 @@ CACHE_SETTINGS_FIELDS: List[CacheSettingsField] = [ ui_field_name="Port", redis_type=None, ), + CacheSettingsField( + field_name="db", + field_type="Integer", + field_value=None, + field_description="Logical database index to isolate the cache (e.g. 1 for redis://host:6379/1)", + field_default=None, + ui_field_name="Database Index", + redis_type=None, + ), CacheSettingsField( field_name="password", field_type="String", diff --git a/litellm/types/management_endpoints/coordination_redis_endpoints.py b/litellm/types/management_endpoints/coordination_redis_endpoints.py new file mode 100644 index 00000000000..b6889d83323 --- /dev/null +++ b/litellm/types/management_endpoints/coordination_redis_endpoints.py @@ -0,0 +1,105 @@ +""" +Types and field definitions for coordination Redis settings management endpoints +""" + +from typing import Literal, Optional + +from pydantic import BaseModel + +CoordinationRedisSection = Literal["connection", "cluster", "sentinel"] + +CoordinationRedisSource = Literal["coordination_redis", "cache_backend", "environment"] + + +class CoordinationRedisSettingsField(BaseModel): + field_name: str + field_type: str + field_value: Optional[object] = None + field_description: str + field_default: Optional[object] = None + ui_field_name: str + section: CoordinationRedisSection + + +COORDINATION_REDIS_SETTINGS_FIELDS: list[CoordinationRedisSettingsField] = [ + CoordinationRedisSettingsField( + field_name="host", + field_type="String", + field_description="Redis server hostname or IP address", + ui_field_name="Host", + section="connection", + ), + CoordinationRedisSettingsField( + field_name="port", + field_type="Integer", + field_description="Redis server port number", + field_default=6379, + ui_field_name="Port", + section="connection", + ), + CoordinationRedisSettingsField( + field_name="username", + field_type="String", + field_description="Redis server username (if required)", + ui_field_name="Username", + section="connection", + ), + CoordinationRedisSettingsField( + field_name="password", + field_type="String", + field_description="Redis server password", + ui_field_name="Password", + section="connection", + ), + CoordinationRedisSettingsField( + field_name="url", + field_type="String", + field_description=( + "Full Redis connection URL (e.g. redis://:password@host:6379/1). " + "Set this instead of the discrete host/port/username/password fields." + ), + ui_field_name="Redis URL", + section="connection", + ), + CoordinationRedisSettingsField( + field_name="ssl", + field_type="Boolean", + field_description="Connect to Redis over TLS", + field_default=False, + ui_field_name="SSL", + section="connection", + ), + CoordinationRedisSettingsField( + field_name="startup_nodes", + field_type="List", + field_description=( + "Cluster-mode startup nodes (e.g. [{'host': '127.0.0.1', 'port': 7001}]). " + "When set, a Redis Cluster client is used." + ), + ui_field_name="Cluster Startup Nodes", + section="cluster", + ), + CoordinationRedisSettingsField( + field_name="sentinel_nodes", + field_type="List", + field_description=( + "Sentinel [host, port] pairs (e.g. [['localhost', 26379]]). When set, a Sentinel-managed client is used." + ), + ui_field_name="Sentinel Nodes", + section="sentinel", + ), + CoordinationRedisSettingsField( + field_name="sentinel_password", + field_type="String", + field_description="Password for the Redis Sentinel nodes", + ui_field_name="Sentinel Password", + section="sentinel", + ), + CoordinationRedisSettingsField( + field_name="service_name", + field_type="String", + field_description="Master service name for Redis Sentinel", + ui_field_name="Service Name", + section="sentinel", + ), +] diff --git a/litellm/types/mcp.py b/litellm/types/mcp.py index e9a6bfa602e..ac411ad9d9a 100644 --- a/litellm/types/mcp.py +++ b/litellm/types/mcp.py @@ -38,8 +38,16 @@ class MCPAuth(str, enum.Enum): aws_sigv4 = "aws_sigv4" token = "token" oauth2_token_exchange = "oauth2_token_exchange" + true_passthrough = "true_passthrough" + oauth_delegate = "oauth_delegate" +# RFC 8693 default subject_token_type. A NULL column / omitted config key means +# "use this default"; it is applied at every egress build site via this single +# constant rather than a DB-level DEFAULT (Prisma writes explicit values on +# insert, so a column default would rarely apply anyway). +DEFAULT_SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token" + # MCP Literals MCPTransportType = Literal[MCPTransport.sse, MCPTransport.http, MCPTransport.stdio] MCPSpecVersionType = Literal[MCPSpecVersion.nov_2024, MCPSpecVersion.mar_2025, MCPSpecVersion.jun_2025] @@ -54,6 +62,8 @@ MCPAuthType = Optional[ MCPAuth.aws_sigv4, MCPAuth.token, MCPAuth.oauth2_token_exchange, + MCPAuth.true_passthrough, + MCPAuth.oauth_delegate, ] ] @@ -122,18 +132,31 @@ class MCPCredentials(TypedDict, total=False): audience: Optional[str] """ - Target audience for OAuth 2.0 Token Exchange (RFC 8693) + Target audience for OAuth 2.0 Token Exchange (RFC 8693). + + Legacy input shape: this setting has a dedicated ``audience`` column, which is + authoritative. A value sent here is accepted for back-compat (the pre-column + REST shape, released since 2026-05), lifted into the column on write, and + stripped from the stored blob. Prefer the top-level request field. """ token_exchange_endpoint: Optional[str] """ - IDP token endpoint for OAuth 2.0 Token Exchange (RFC 8693) + IDP token endpoint for OAuth 2.0 Token Exchange (RFC 8693). + + Legacy input shape: lifted into the dedicated ``token_exchange_endpoint`` + column on write and stripped from the stored blob; the column is + authoritative. Prefer the top-level request field. """ subject_token_type: Optional[str] """ Subject token type for OAuth 2.0 Token Exchange (RFC 8693). - Default: urn:ietf:params:oauth:token-type:access_token + Default: DEFAULT_SUBJECT_TOKEN_TYPE (urn:ietf:params:oauth:token-type:access_token). + + Legacy input shape: lifted into the dedicated ``subject_token_type`` column on + write and stripped from the stored blob; the column is authoritative. Prefer + the top-level request field. """ token_endpoint_auth_method: Optional[MCPTokenEndpointAuthMethod] @@ -142,6 +165,26 @@ class MCPCredentials(TypedDict, total=False): sends HTTP Basic; defaults to "client_secret_post" when unset. """ + redirect_uris: Optional[List[str]] + """ + The redirect URIs a dynamically registered (RFC 7591) OAuth client was bound to at + registration time. Lets a later registration detect that the proxy's public origin no + longer matches the registered callback and re-register instead of reusing a client the + IdP will reject. Absent for admin-configured clients and for clients registered before + this field existed. Not a secret; stored unencrypted. + """ + + token_exchange_profile: Optional[str] + """ + Token exchange wire dialect: "rfc8693" (default, the standard token-exchange grant) or + "entra_obo" (Microsoft Entra On-Behalf-Of, the RFC 7523 jwt-bearer grant + requested_token_use + extension). Not a secret; stored unencrypted. + + Legacy input shape: lifted into the dedicated ``token_exchange_profile`` column on + write and stripped from the stored blob; the column is authoritative. Prefer the + top-level request field. + """ + class MCPServerCostInfo(TypedDict, total=False): default_cost_per_query: Optional[float] diff --git a/litellm/types/mcp_server/mcp_server_manager.py b/litellm/types/mcp_server/mcp_server_manager.py index d7c04c09585..bb0baba6cf4 100644 --- a/litellm/types/mcp_server/mcp_server_manager.py +++ b/litellm/types/mcp_server/mcp_server_manager.py @@ -4,6 +4,7 @@ from typing import Any, Dict, List, Literal, Optional from pydantic import BaseModel, ConfigDict from litellm.types.mcp import ( + DEFAULT_SUBJECT_TOKEN_TYPE, MCPAuth, MCPAuthType, MCPTokenEndpointAuthMethod, @@ -65,10 +66,13 @@ class MCPServer(BaseModel): aws_service_name: Optional[str] = None # defaults to "bedrock-agentcore" aws_role_name: Optional[str] = None # IAM role ARN for STS AssumeRole aws_session_name: Optional[str] = None # session name for CloudTrail auditing - # Token Exchange (OBO) fields — RFC 8693 + # Token Exchange (OBO) fields token_exchange_endpoint: Optional[str] = None audience: Optional[str] = None - subject_token_type: str = "urn:ietf:params:oauth:token-type:access_token" + subject_token_type: str = DEFAULT_SUBJECT_TOKEN_TYPE + # Wire dialect: "rfc8693" (standard token-exchange grant) or "entra_obo" (Microsoft Entra + # On-Behalf-Of, the RFC 7523 jwt-bearer grant + requested_token_use extension) + token_exchange_profile: str = "rfc8693" # Stdio-specific fields command: Optional[str] = None args: Optional[List[str]] = None @@ -99,6 +103,7 @@ class MCPServer(BaseModel): # ``Authorization`` for non-OAuth reasons (e.g. static bearer tokens). Must # be set explicitly to avoid regressing servers that did not opt in. oauth_passthrough: bool = False + dcr_bridge: Optional[bool] = None is_byok: bool = False byok_description: List[str] = [] byok_api_key_help_url: Optional[str] = None @@ -118,6 +123,9 @@ class MCPServer(BaseModel): # MCP_PER_USER_TOKEN_DEFAULT_TTL when expires_in is absent. token_storage_ttl_seconds: Optional[int] = None timeout: Optional[float] = None + # Max concurrent outbound tool calls to this server; excess calls queue. + # None or a value <= 0 means unlimited. + max_concurrent_requests: Optional[int] = None # Resolved short-ID tool prefix when LITELLM_USE_SHORT_MCP_TOOL_PREFIX is # enabled. Set by ``MCPServerManager._assign_unique_short_prefix`` at # registration time so that natural-hash collisions between two @@ -145,6 +153,27 @@ class MCPServer(BaseModel): """True if this is an OAuth2 server that relies on per-user tokens (no client_credentials).""" return self.auth_type == MCPAuth.oauth2 and not self.has_client_credentials + @property + def is_true_passthrough(self) -> bool: + """True for the transparent-proxy mode: LiteLLM performs no admission auth and forwards the + client's ``Authorization`` to the upstream unchanged.""" + return self.auth_type == MCPAuth.true_passthrough + + @property + def is_oauth_delegate(self) -> bool: + """True for the delegated-upstream-OAuth mode: LiteLLM still admits the caller (API key / SSO / + JWT) but forwards the caller's separate upstream ``Authorization`` unchanged, minting nothing.""" + return self.auth_type == MCPAuth.oauth_delegate + + @property + def is_dcr_bridge(self) -> bool: + """True when this client-forwarded-token server serves the gateway-hosted DCR front door + (gateway-self protected-resource and authorization-server metadata plus the register, + authorize, and token relays) instead of relaying the upstream's own OAuth discovery + verbatim. ``dcr_bridge`` is rejected on every other auth type at create, update, and + config load, so the mode gate here only defends rows edited outside those paths.""" + return bool(self.dcr_bridge) and (self.is_true_passthrough or self.is_oauth_delegate) + @property def requires_per_user_auth(self) -> bool: """ @@ -160,6 +189,9 @@ class MCPServer(BaseModel): if self.needs_user_oauth_token: return True + if self.is_true_passthrough or self.is_oauth_delegate: + return True + # PAT passthrough: auth_type is none but extra_headers includes auth headers if self.auth_type == MCPAuth.none and self.extra_headers: auth_header_names = {"authorization", "x-api-key", "api-key", "apikey"} diff --git a/litellm/types/object_permission.py b/litellm/types/object_permission.py index ff932dccd5d..d0458173fbf 100644 --- a/litellm/types/object_permission.py +++ b/litellm/types/object_permission.py @@ -24,3 +24,4 @@ class ObjectPermissionDict(TypedDict, total=False): agent_access_groups: Optional[list[str]] models: Optional[list[str]] search_tools: Optional[list[str]] + mcp_tool_search_enabled: Optional[bool] diff --git a/litellm/types/proxy/guardrails/guardrail_hooks/generic_guardrail_api.py b/litellm/types/proxy/guardrails/guardrail_hooks/generic_guardrail_api.py index 28fb482b3af..d0ac8bb8998 100644 --- a/litellm/types/proxy/guardrails/guardrail_hooks/generic_guardrail_api.py +++ b/litellm/types/proxy/guardrails/guardrail_hooks/generic_guardrail_api.py @@ -1,7 +1,7 @@ from typing import Any, Dict, List, Literal, Optional, Union from pydantic import BaseModel, ConfigDict, Field -from typing_extensions import TYPE_CHECKING, TypedDict +from typing_extensions import TypedDict from litellm.types.llms.openai import ( AllMessageValues, @@ -60,6 +60,30 @@ class GenericGuardrailAPIOptionalParams(BaseModel): ), ) + streaming_end_of_stream_only: Optional[bool] = Field( + default=None, + description=( + "If False (default when unset), the guardrail runs on sampled chunks during " + "the stream at the cadence set by streaming_sampling_rate, and an in-flight " + "BLOCKED stops further chunks from streaming. If True, the guardrail runs " + "once at end of stream over the assembled response; lower cost and latency, " + "but flagged content has already streamed to the client before the terminal " + "block. Defaults are applied in GenericGuardrailAPI.__init__ when None so " + "unset optional_params does not shadow top-level litellm_params." + ), + ) + + streaming_sampling_rate: Optional[int] = Field( + default=None, + ge=1, + description=( + "When streaming_end_of_stream_only is False, the guardrail runs every Nth " + "streamed chunk. Ignored when streaming_end_of_stream_only is True. " + "Must be >= 1 when set. Defaults to 5 in GenericGuardrailAPI.__init__ " + "when None so unset optional_params does not shadow top-level litellm_params." + ), + ) + class GenericGuardrailAPIConfigModel( GuardrailConfigModel[GenericGuardrailAPIOptionalParams], diff --git a/litellm/types/proxy/guardrails/guardrail_hooks/headroom.py b/litellm/types/proxy/guardrails/guardrail_hooks/headroom.py index 3186c9fc612..71aa243069a 100644 --- a/litellm/types/proxy/guardrails/guardrail_hooks/headroom.py +++ b/litellm/types/proxy/guardrails/guardrail_hooks/headroom.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Literal, Optional from pydantic import BaseModel, Field @@ -18,6 +18,14 @@ class HeadroomGuardrailConfigModel(GuardrailConfigModel[BaseModel]): default=None, description="Model name forwarded to the headroom /v1/compress endpoint.", ) + unreachable_fallback: Literal["fail_closed", "fail_open"] = Field( + default="fail_closed", + description=( + "Behavior when the headroom compression service is unreachable or errors. " + "'fail_closed' raises an error (default). 'fail_open' logs a critical error and " + "forwards the request uncompressed instead of blocking it." + ), + ) @staticmethod def ui_friendly_name() -> str: diff --git a/litellm/types/proxy/management_endpoints/customer_endpoints.py b/litellm/types/proxy/management_endpoints/customer_endpoints.py new file mode 100644 index 00000000000..e7653360d63 --- /dev/null +++ b/litellm/types/proxy/management_endpoints/customer_endpoints.py @@ -0,0 +1,30 @@ +from typing import List, Optional + +from pydantic import BaseModel, Field + +from litellm.models.budget import LiteLLM_BudgetTableFull +from litellm.models.end_user import LiteLLM_EndUserTable + + +class CustomerResponse(LiteLLM_EndUserTable): + """Customer object returned by the /customer read+write endpoints. + + Nests the full budget response model so server-managed budget fields + (budget_reset_at, created_at) survive response_model filtering, rather than + the narrow write-allowlist shape LiteLLM_EndUserTable carries for internal use. + """ + + litellm_budget_table: Optional[LiteLLM_BudgetTableFull] = None # pyright: ignore + + +class BlockUsersResponse(BaseModel): + blocked_users: List[LiteLLM_EndUserTable] + + +class UnblockUsersResponse(BaseModel): + blocked_users: List[str] = Field(description="User IDs that remain blocked after this unblock call") + + +class DeleteCustomersResponse(BaseModel): + deleted_customers: int + message: str diff --git a/litellm/types/responses/main.py b/litellm/types/responses/main.py index ebd2ad5b5a8..32e07f9e52f 100644 --- a/litellm/types/responses/main.py +++ b/litellm/types/responses/main.py @@ -85,6 +85,22 @@ def build_code_interpreter_log_outputs( return [OutputCodeInterpreterCallLog(type="logs", logs=logs)] if logs else None +class CustomToolCallOutputItem(BaseLiteLLMOpenAIResponseObject): + """A custom/freeform tool call output item (e.g. apply_patch). + + Mirrors the ``custom_tool_call`` variant of OpenAI's Responses API output. + Unlike ``OutputFunctionToolCall`` which uses ``arguments`` (JSON string), + this uses ``input`` (raw string) for the tool payload. + """ + + type: Literal["custom_tool_call"] + call_id: str + id: Optional[str] = None + name: str + input: str + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + + class GenericResponseOutputItem(BaseLiteLLMOpenAIResponseObject): """ Generic response API output item diff --git a/litellm/types/router.py b/litellm/types/router.py index 0c3485deae7..3bedd97c20c 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -9,7 +9,7 @@ from typing import Any, Dict, List, Literal, Optional, Tuple, Union, get_type_hi import httpx from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator -from typing_extensions import Required, TypedDict +from typing_extensions import Protocol, Required, TypedDict from litellm._uuid import uuid @@ -214,6 +214,8 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): custom_llm_provider: Optional[str] = None tpm: Optional[int] = None rpm: Optional[int] = None + itpm: Optional[int] = None + otpm: Optional[int] = None timeout: Optional[Union[float, str, httpx.Timeout]] = None # if str, pass in as os.environ/ stream_timeout: Optional[Union[float, str]] = ( None # timeout when making stream=True calls, if str, pass in as os.environ/ @@ -359,6 +361,8 @@ class LiteLLMParamsTypedDict(TypedDict, total=False): custom_llm_provider: Optional[str] tpm: Optional[int] rpm: Optional[int] + itpm: Optional[int] + otpm: Optional[int] order: Optional[int] weight: Optional[int] max_parallel_requests: Optional[int] @@ -552,6 +556,8 @@ class ModelGroupInfo(BaseModel): ] = Field(default="chat") tpm: Optional[int] = None rpm: Optional[int] = None + itpm: Optional[int] = None + otpm: Optional[int] = None supports_parallel_function_calling: bool = Field(default=False) supports_vision: bool = Field(default=False) supports_web_search: bool = Field(default=False) @@ -749,6 +755,8 @@ class RoutingStrategy(enum.Enum): class RouterCacheEnum(enum.Enum): TPM = "global_router:{id}:{model}:tpm:{current_minute}" RPM = "global_router:{id}:{model}:rpm:{current_minute}" + ITPM = "global_router:{id}:{model}:itpm:{current_minute}" + OTPM = "global_router:{id}:{model}:otpm:{current_minute}" class GenericBudgetWindowDetails(BaseModel): @@ -821,6 +829,35 @@ class PreRoutingHookResponse(BaseModel): messages: Optional[List[Dict[str, Any]]] +class RoutingContext(BaseModel): + """ + Passed through a Router's `plugins` pipeline before the routing decision is made. + + Each plugin reads and mutates this object; the next plugin sees the previous + plugin's changes. `candidate_models` narrows as the pipeline runs -- Router + only selects a deployment whose `litellm_params.model` survives the pipeline. + + `raw_messages` and `structured_messages` mirror the pattern + `CustomGuardrail.apply_guardrail` uses: the message shape differs by API + surface (chat completions, Anthropic /v1/messages, Responses API `input`, + ...), so plugins that need a stable, provider-agnostic shape should read + `structured_messages` (normalized to OpenAI chat-completions format); + plugins that need the exact original payload can read `raw_messages`. + """ + + raw_messages: list[dict[str, Any]] + structured_messages: list[dict[str, Any]] + candidate_models: list[str] + metadata: dict[str, Any] = Field(default_factory=dict) + signals: dict[str, Any] = Field(default_factory=dict) + + +class RoutingPlugin(Protocol): + """Interface a custom routing plugin must implement to run in `Router(plugins=[...])`.""" + + async def run(self, context: RoutingContext) -> RoutingContext: ... + + class RequestType(str, enum.Enum): """Fixed v0 taxonomy. User-extensible types come in v1.""" diff --git a/litellm/types/utils.py b/litellm/types/utils.py index dff4e4af89e..e33e2335525 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -143,6 +143,7 @@ class ProviderSpecificModelInfo(TypedDict, total=False): supports_web_search: Optional[bool] supports_reasoning: Optional[bool] supports_adaptive_thinking: Optional[bool] + supports_mid_conversation_system: Optional[bool] supports_url_context: Optional[bool] supports_none_reasoning_effort: Optional[bool] supports_minimal_reasoning_effort: Optional[bool] @@ -152,6 +153,7 @@ class ProviderSpecificModelInfo(TypedDict, total=False): supports_output_config: Optional[bool] supports_image_size: Optional[bool] bedrock_output_config_effort_ceiling: Optional[Literal["low", "medium", "high", "max", "xhigh"]] + bedrock_converse_supports_strict_tools: Optional[bool] class SearchContextCostPerQuery(TypedDict, total=False): @@ -2522,6 +2524,23 @@ class StandardLoggingMCPToolCall(TypedDict, total=False): the client is driving a stateful session. Absent for stateless calls. """ + mcp_auth_mode: Optional[str] + """ + The server's auth_type for this call (e.g. `true_passthrough`, `oauth_delegate`, + `oauth2`). For the client-forwarded token modes this records that the caller's own + upstream token was relayed, so an audit can attribute a relayed request to its mode + without logging any credential. + """ + + mcp_server_resource: Optional[str] + """ + The origin (scheme + host + port) of the upstream MCP server the tool call was forwarded + to. Redacted for logging: userinfo, the path, the query string, and the fragment are all + stripped, because hosted MCP servers routinely embed the credential in the URL path and + this value is readable by callers via request logs. + Records which upstream received a relayed request; never a credential. + """ + class StandardLoggingVectorStoreRequest(TypedDict, total=False): """ @@ -2680,7 +2699,7 @@ class StandardLoggingGuardrailInformation(TypedDict, total=False): guardrail_name: Optional[str] guardrail_provider: Optional[str] guardrail_mode: Optional[Union[GuardrailEventHooks, List[GuardrailEventHooks], GuardrailMode]] - guardrail_request: Optional[dict] + guardrail_request: Optional[Union[str, dict]] guardrail_response: Optional[Union[dict, str, List[dict]]] guardrail_status: GuardrailStatus start_time: Optional[float] @@ -2711,10 +2730,10 @@ class StandardLoggingGuardrailInformation(TypedDict, total=False): confidence_score: Optional[float] """For LLM-judge guardrails: confidence score 0.0-1.0""" - classification: Optional[dict] + classification: Optional[Union[str, dict]] """For LLM-judge guardrails: structured classification output""" - match_details: Optional[List[dict]] + match_details: Optional[Union[str, List[dict]]] """Detailed match information for each detected pattern""" patterns_checked: Optional[int] @@ -3047,6 +3066,17 @@ class CustomPricingLiteLLMParams(BaseModel): regional_processing_uplift_multiplier_eu: Optional[float] = None regional_processing_uplift_multiplier_us: Optional[float] = None + @classmethod + def strip_custom_pricing_fields(cls, model_info: Dict[str, Any]) -> Dict[str, Any]: + """Return a copy of ``model_info`` without per-deployment custom pricing fields. + + Used when registering a deployment's info under the shared + ``{provider}/{model}`` key in ``litellm.model_cost``, so one deployment's + pricing overrides don't pollute sibling deployments that share the same + backend model. Full pricing stays under the deployment's unique model id. + """ + return {k: v for k, v in model_info.items() if k not in cls.model_fields} + # Server-controlled fields that bound or drive an interceptor's agentic loop # (depth, cycle fingerprints, ceiling, code-interpreter sandbox state). Listed @@ -3062,6 +3092,7 @@ agentic_loop_internal_litellm_params = [ "max_agentic_loops", "_code_interpreter_interception_active", "_code_interpreter_interception_sandbox_key", + "_code_interpreter_interception_session_scoped", "_code_interpreter_interception_converted_stream", ] @@ -3118,6 +3149,8 @@ all_litellm_params = ( "client", "rpm", "tpm", + "itpm", + "otpm", "max_parallel_requests", "input_cost_per_token", "output_cost_per_token", @@ -3309,6 +3342,7 @@ class LlmProviders(str, Enum): CUSTOM = "custom" LITELLM_PROXY = "litellm_proxy" HOSTED_VLLM = "hosted_vllm" + TENCENT = "tencent" LLAMAFILE = "llamafile" LM_STUDIO = "lm_studio" GALADRIEL = "galadriel" @@ -3365,9 +3399,11 @@ class LlmProviders(str, Enum): LIBERTAI = "libertai" PINSTRIPES = "pinstripes" DARKBLOOM = "darkbloom" + META = "meta" LITELLM_AGENT = "litellm_agent" CURSOR = "cursor" BEDROCK_MANTLE = "bedrock_mantle" + GDC = "gdc" # Create a set of all provider values for quick lookup diff --git a/litellm/utils.py b/litellm/utils.py index 45ce5332f1d..18b89ee0d13 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -61,7 +61,7 @@ from litellm._lazy_imports import ( ) from litellm._uuid import uuid from litellm.litellm_core_utils.fallback_generalizations import ( - match_fallback_generalization, + match_capability_generalizations, ) from litellm.constants import ( DEFAULT_CHAT_COMPLETION_PARAM_VALUES, @@ -2619,8 +2619,9 @@ _CACHE_PRICING_FIELDS = ( def _resolve_builtin_model_cost_entry(key: str, provider: str) -> Optional[Dict[str, Any]]: """Best-effort lookup of a built-in ``model_cost`` entry for a custom key - whose shape ``get_model_info`` cannot resolve (double provider prefixes - like ``bedrock/bedrock/us.anthropic.claude-sonnet-4-6`` or region aliases). + whose shape ``get_model_info`` cannot resolve (repeated provider prefixes + like ``bedrock/bedrock/bedrock/us.anthropic.claude-sonnet-4-6`` or region + aliases). Returns a copy of the matching entry so the caller can inherit its defaults (most importantly cache pricing) without mutating the shared built-in. @@ -2650,6 +2651,26 @@ def _resolve_builtin_model_cost_entry(key: str, provider: str) -> Optional[Dict[ return None +def _get_builtin_model_info_for_registration(model: str) -> Optional[ModelInfo]: + """Resolve ``model`` to its built-in cost-map entry for registration merging. + + Returns ``None`` when the lookup raises or when it resolved via a + fallback-generalization capability rule, detected as the resolved key missing + ``litellm.model_cost`` while matching a capability rule. A rule-derived entry + carries no pricing, so treating it as a hit would skip the built-in + cache-pricing inheritance for prefix-mangled keys. + """ + try: + info = get_model_info(model=model) + except Exception: + return None + if info["key"] in litellm.model_cost: + return info + if match_capability_generalizations(info["key"]) is None: + return info + return None + + def register_model(model_cost: Union[str, dict]): """ Register new / Override existing models (and their pricing) to specific providers. @@ -2690,10 +2711,11 @@ def register_model(model_cost: Union[str, dict]): existing_model = litellm.model_cost.get(key, {}) model_cost_key = key else: - try: - existing_model = cast(dict, get_model_info(model=key)) + builtin_model_info = _get_builtin_model_info_for_registration(model=_key_str) + if builtin_model_info is not None: + existing_model = cast(dict, builtin_model_info) model_cost_key = existing_model["key"] - except Exception: + else: existing_model = {} model_cost_key = key builtin_entry = _resolve_builtin_model_cost_entry(key=_key_str, provider=provider) @@ -3497,6 +3519,17 @@ def filter_out_litellm_params(kwargs: dict) -> dict: return {key: value for key, value in kwargs.items() if key not in all_litellm_params} +def _provider_supports_vertex_params(custom_llm_provider: str) -> bool: + if custom_llm_provider in ("vertex_ai", "vertex_ai_beta"): + return True + try: + provider = LlmProviders(custom_llm_provider) + except ValueError: + return False + provider_config = ProviderConfigManager.get_provider_chat_config(model="", provider=provider) + return bool(getattr(provider_config, "supports_vertex_params", False)) + + class PreProcessNonDefaultParams: @staticmethod def base_pre_process_non_default_params( @@ -3518,11 +3551,7 @@ class PreProcessNonDefaultParams: continue elif k == "hf_model_name" and custom_llm_provider != "sagemaker": continue - elif ( - k.startswith("vertex_") - and custom_llm_provider != "vertex_ai" - and custom_llm_provider != "vertex_ai_beta" - ): # allow dynamically setting vertex ai init logic + elif k.startswith("vertex_") and not _provider_supports_vertex_params(custom_llm_provider): continue passed_params[k] = v @@ -4197,6 +4226,13 @@ def get_optional_params( model=model, drop_params=(drop_params if drop_params is not None and isinstance(drop_params, bool) else False), ) + elif custom_llm_provider == "tencent": + optional_params = litellm.TencentChatConfig().map_openai_params( + non_default_params=non_default_params, + optional_params=optional_params, + model=model, + drop_params=(drop_params if drop_params is not None and isinstance(drop_params, bool) else False), + ) elif custom_llm_provider == "openrouter": optional_params = litellm.OpenrouterConfig().map_openai_params( non_default_params=non_default_params, @@ -5028,26 +5064,35 @@ def _get_model_info_from_generalization( potential_model_names: PotentialModelNamesAndCustomLLMProvider, custom_llm_provider: Optional[str], ) -> Optional[tuple[str, dict]]: - """Resolve an unmapped model via a declarative fallback-generalization rule. + """Resolve an unmapped model via the declarative capability generalization rules. Tries the same name candidates as the exact lookups, in the same order, and - returns ``(matched_name, model_info)`` for the first candidate whose rule also - satisfies the provider constraint. O(number of rules); only call after the + returns ``(matched_name, model_info)`` for the first candidate matched by at + least one capability rule, with ``litellm_provider`` backfilled from the + provider the caller requested. Rules lose to exact entries: if ANY candidate is + an exact ``litellm.model_cost`` key (necessarily provider-mismatched, or the + exact lookups would have returned it), the model is known rather than unmapped, + and resolving it from rules would hand an unpriced rule-derived entry to + callers whose fallback ladder (e.g. the cost calculator's model-name variants) + still had a priced exact name to try. O(number of rules); only call after the exact lookups have missed. """ - candidates = [ + candidates = ( potential_model_names["combined_model_name"], model, + potential_model_names["split_model"], potential_model_names["combined_stripped_model_name"], potential_model_names["stripped_model_name"], - potential_model_names["split_model"], - ] + ) + if any(_get_model_cost_key(candidate) is not None for candidate in candidates): + return None for candidate in candidates: - generalized_info = match_fallback_generalization(candidate) - if generalized_info is not None and _check_provider_match( - model_info=generalized_info, custom_llm_provider=custom_llm_provider - ): + generalized_info = match_capability_generalizations(candidate) + if generalized_info is None: + continue + if custom_llm_provider is None: return candidate, generalized_info + return candidate, {**generalized_info, "litellm_provider": custom_llm_provider} return None @@ -5080,6 +5125,11 @@ def _get_potential_model_names( stripped_model_name, ) + if custom_llm_provider in ("bedrock", "bedrock_converse"): + from litellm.llms.bedrock.common_utils import strip_bedrock_routing_prefix + + split_model = strip_bedrock_routing_prefix(split_model) + return PotentialModelNamesAndCustomLLMProvider( split_model=split_model, combined_model_name=combined_model_name, @@ -5247,9 +5297,9 @@ def _get_model_info_helper( Check if: (in order of specificity) 1. 'custom_llm_provider/model' in litellm.model_cost. Checks "groq/llama3-8b-8192" if model="llama3-8b-8192" and custom_llm_provider="groq" 2. 'model' in litellm.model_cost. Checks "gemini-1.5-pro-002" in litellm.model_cost if model="gemini-1.5-pro-002" and custom_llm_provider=None - 3. 'combined_stripped_model_name' in litellm.model_cost. Checks if 'gemini/gemini-1.5-flash' in model map, if 'gemini/gemini-1.5-flash-001' given. - 4. 'stripped_model_name' in litellm.model_cost. Checks if 'ft:gpt-3.5-turbo' in model map, if 'ft:gpt-3.5-turbo:my-org:custom_suffix:id' given. - 5. 'split_model' in litellm.model_cost. Checks "llama3-8b-8192" in litellm.model_cost if model="groq/llama3-8b-8192" + 3. 'split_model' in litellm.model_cost. Checks "au.anthropic.claude-opus-4-8" in litellm.model_cost if model="bedrock/au.anthropic.claude-opus-4-8" + 4. 'combined_stripped_model_name' in litellm.model_cost. Checks if 'gemini/gemini-1.5-flash' in model map, if 'gemini/gemini-1.5-flash-001' given. + 5. 'stripped_model_name' in litellm.model_cost. Checks if 'ft:gpt-3.5-turbo' in model map, if 'ft:gpt-3.5-turbo:my-org:custom_suffix:id' given. """ _model_info: Optional[Dict[str, Any]] = None @@ -5275,6 +5325,16 @@ def _get_model_info_helper( custom_llm_provider=model_cost_custom_llm_provider, ): _model_info = None + if _model_info is None: + _matched_key = _get_model_cost_key(split_model) + if _matched_key is not None: + key = _matched_key + _model_info = _get_model_info_from_model_cost(key=cast(str, key)) + if not _check_provider_match( + model_info=_model_info, + custom_llm_provider=model_cost_custom_llm_provider, + ): + _model_info = None if _model_info is None: _matched_key = _get_model_cost_key(combined_stripped_model_name) if _matched_key is not None: @@ -5295,16 +5355,6 @@ def _get_model_info_helper( custom_llm_provider=model_cost_custom_llm_provider, ): _model_info = None - if _model_info is None: - _matched_key = _get_model_cost_key(split_model) - if _matched_key is not None: - key = _matched_key - _model_info = _get_model_info_from_model_cost(key=cast(str, key)) - if not _check_provider_match( - model_info=_model_info, - custom_llm_provider=model_cost_custom_llm_provider, - ): - _model_info = None if _model_info is None: generalization = _get_model_info_from_generalization( @@ -5452,12 +5502,14 @@ def _get_model_info_helper( supports_url_context=_model_info.get("supports_url_context", None), supports_reasoning=_model_info.get("supports_reasoning", None), supports_adaptive_thinking=_model_info.get("supports_adaptive_thinking", None), + supports_mid_conversation_system=_model_info.get("supports_mid_conversation_system", None), supports_none_reasoning_effort=_model_info.get("supports_none_reasoning_effort", None), supports_minimal_reasoning_effort=_model_info.get("supports_minimal_reasoning_effort", None), supports_low_reasoning_effort=_model_info.get("supports_low_reasoning_effort", None), supports_xhigh_reasoning_effort=_model_info.get("supports_xhigh_reasoning_effort", None), supports_max_reasoning_effort=_model_info.get("supports_max_reasoning_effort", None), bedrock_output_config_effort_ceiling=_model_info.get("bedrock_output_config_effort_ceiling", None), + bedrock_converse_supports_strict_tools=_model_info.get("bedrock_converse_supports_strict_tools", None), supports_computer_use=_model_info.get("supports_computer_use", None), search_context_cost_per_query=_model_info.get("search_context_cost_per_query", None), web_search_billing_unit=_model_info.get("web_search_billing_unit", None), @@ -6009,6 +6061,11 @@ def validate_environment( keys_in_environment = True else: missing_keys.append("DEEPSEEK_API_KEY") + elif custom_llm_provider == "tencent": + if "TENCENT_API_KEY" in os.environ: + keys_in_environment = True + else: + missing_keys.append("TENCENT_API_KEY") elif custom_llm_provider == "mistral": if "MISTRAL_API_KEY" in os.environ: keys_in_environment = True @@ -7550,6 +7607,7 @@ class ProviderConfigManager: ), # Simple provider mappings (no model parameter needed) LlmProviders.DEEPSEEK: (lambda: litellm.DeepSeekChatConfig(), False), + LlmProviders.TENCENT: (lambda: litellm.TencentChatConfig(), False), LlmProviders.GROQ: (lambda: litellm.GroqChatConfig(), False), LlmProviders.BEDROCK_MANTLE: ( lambda: litellm.BedrockMantleChatConfig(), @@ -7674,6 +7732,10 @@ class ProviderConfigManager: lambda: ProviderConfigManager._get_langflow_config(), False, ), + LlmProviders.GDC: ( + lambda: litellm.GDCGeminiConfig(), + False, + ), } @staticmethod @@ -7984,6 +8046,29 @@ class ProviderConfigManager: ) return DeepSeekAnthropicMessagesConfig() + elif litellm.LlmProviders.TENCENT == provider: + from litellm.llms.tencent.messages.transformation import ( + TencentAnthropicMessagesConfig, + ) + + return TencentAnthropicMessagesConfig() + elif litellm.LlmProviders.GITHUB_COPILOT == provider: + if "claude" in model_lower: + from litellm.llms.github_copilot.messages.transformation import ( + GithubCopilotAnthropicMessagesConfig, + ) + + return GithubCopilotAnthropicMessagesConfig() + + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + json_provider = JSONProviderRegistry.get(provider.value) + if json_provider is not None and "/v1/messages" in json_provider.supported_endpoints: + from litellm.llms.openai_like.messages.transformation import ( + JSONProviderAnthropicMessagesConfig, + ) + + return JSONProviderAnthropicMessagesConfig(json_provider) return None @staticmethod @@ -8059,6 +8144,12 @@ class ProviderConfigManager: ) return SonioxAudioTranscriptionConfig() + elif litellm.LlmProviders.VERTEX_AI == provider: + from litellm.llms.vertex_ai.audio_transcription.transformation import ( + VertexAIAudioTranscriptionConfig, + ) + + return VertexAIAudioTranscriptionConfig() return None @staticmethod diff --git a/migrations/Dockerfile b/migrations/Dockerfile index caca280cbfc..b20284df000 100644 --- a/migrations/Dockerfile +++ b/migrations/Dockerfile @@ -1,5 +1,5 @@ -ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 -ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:c61ac6919b811ea53c4782d69f1fe05218ba3c25d53f01b6ab7892e621bd4370 +ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f +ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a FROM $UV_IMAGE AS uvbin diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 73cefeb7c77..08a452be844 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -744,7 +744,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "anthropic.claude-haiku-4-5@20251001": { "cache_creation_input_token_cost": 1.25e-06, @@ -768,7 +769,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_streaming": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "anthropic.claude-3-5-sonnet-20240620-v1:0": { "input_cost_per_token": 3e-06, @@ -787,8 +789,6 @@ "output_cost_per_token_above_200k_tokens": 3e-05, "cache_creation_input_token_cost_above_200k_tokens": 7.5e-06, "cache_read_input_token_cost_above_200k_tokens": 6e-07, - "cache_creation_input_token_cost_above_1hr": 7.5e-06, - "cache_creation_input_token_cost_above_1hr_above_200k_tokens": 1.5e-05, "cache_creation_input_token_cost": 3.75e-06, "cache_read_input_token_cost": 3e-07 }, @@ -813,9 +813,7 @@ "input_cost_per_token_above_200k_tokens": 6e-06, "output_cost_per_token_above_200k_tokens": 3e-05, "cache_creation_input_token_cost_above_200k_tokens": 7.5e-06, - "cache_read_input_token_cost_above_200k_tokens": 6e-07, - "cache_creation_input_token_cost_above_1hr": 7.5e-06, - "cache_creation_input_token_cost_above_1hr_above_200k_tokens": 1.5e-05 + "cache_read_input_token_cost_above_200k_tokens": 6e-07 }, "anthropic.claude-3-7-sonnet-20240620-v1:0": { "cache_creation_input_token_cost": 4.5e-06, @@ -991,7 +989,8 @@ "supports_vision": true, "supports_native_structured_output": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "high" + "bedrock_output_config_effort_ceiling": "high", + "supports_parallel_tool_use_config": true }, "anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1010,7 +1009,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1023,7 +1021,8 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1042,7 +1041,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1055,7 +1053,8 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1074,7 +1073,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1087,7 +1085,8 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1106,7 +1105,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1119,7 +1117,8 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-opus-4-6-v1": { "supports_adaptive_thinking": true, @@ -1138,7 +1137,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1151,9 +1149,11 @@ "supports_native_structured_output": true, "supports_output_config": true, "supports_max_reasoning_effort": true, - "bedrock_output_config_effort_ceiling": "max" + "bedrock_output_config_effort_ceiling": "max", + "supports_parallel_tool_use_config": true }, "anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, @@ -1170,7 +1170,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1185,7 +1184,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "anthropic.claude-mythos-preview": { "input_cost_per_token": 0, @@ -1203,6 +1203,7 @@ "supports_output_config": true }, "global.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, @@ -1219,7 +1220,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1234,9 +1234,11 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, @@ -1253,7 +1255,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1268,9 +1269,11 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, @@ -1287,7 +1290,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1302,9 +1304,11 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, @@ -1321,7 +1325,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1336,7 +1339,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "anthropic.claude-fable-5": { "cache_creation_input_token_cost": 1.25e-05, @@ -1355,6 +1359,7 @@ "search_context_size_medium": 0.01 }, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1369,7 +1374,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-fable-5": { "cache_creation_input_token_cost": 1.25e-05, @@ -1388,6 +1394,7 @@ "search_context_size_medium": 0.01 }, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1402,7 +1409,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-fable-5": { "cache_creation_input_token_cost": 1.375e-05, @@ -1421,6 +1429,7 @@ "search_context_size_medium": 0.01 }, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1435,7 +1444,8 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-fable-5": { "cache_creation_input_token_cost": 1.375e-05, @@ -1454,6 +1464,7 @@ "search_context_size_medium": 0.01 }, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1468,10 +1479,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, @@ -1487,7 +1501,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1502,10 +1515,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, @@ -1521,7 +1537,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1536,10 +1551,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, "cache_read_input_token_cost": 5.5e-07, @@ -1555,7 +1573,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1570,10 +1587,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, "cache_read_input_token_cost": 5.5e-07, @@ -1589,7 +1609,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1604,10 +1623,13 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, "cache_creation_input_token_cost": 6.875e-06, "cache_creation_input_token_cost_above_1hr": 1.1e-05, "cache_read_input_token_cost": 5.5e-07, @@ -1623,7 +1645,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -1638,9 +1659,47 @@ "supports_native_structured_output": true, "supports_max_reasoning_effort": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "xhigh" + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "jp.anthropic.claude-opus-4-8": { + "bedrock_converse_supports_strict_tools": false, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "cache_creation_input_token_cost": 6.875e-06, + "cache_creation_input_token_cost_above_1hr": 1.1e-05, + "cache_read_input_token_cost": 5.5e-07, + "input_cost_per_token": 5.5e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 2.75e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "jp.anthropic.claude-opus-4-7": { + "bedrock_converse_supports_strict_tools": false, "cache_creation_input_token_cost": 6.875e-06, "cache_read_input_token_cost": 5.5e-07, "input_cost_per_token": 5.5e-06, @@ -1669,7 +1728,218 @@ "supports_xhigh_reasoning_effort": true, "supports_native_structured_output": true, "supports_max_reasoning_effort": true, - "supports_minimal_reasoning_effort": true + "supports_minimal_reasoning_effort": true, + "supports_parallel_tool_use_config": true + }, + "anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "global.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "us.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + "input_cost_per_token": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "eu.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + "input_cost_per_token": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "au.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + "input_cost_per_token": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true + }, + "jp.anthropic.claude-sonnet-5": { + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + "input_cost_per_token": 2.2e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_mid_conversation_system": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_output_config": true, + "bedrock_output_config_effort_ceiling": "xhigh", + "supports_parallel_tool_use_config": true }, "anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1688,7 +1958,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1700,7 +1969,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1719,7 +1989,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1731,7 +2000,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1750,7 +2020,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1762,7 +2031,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1781,7 +2051,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1793,7 +2062,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1812,7 +2082,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1824,7 +2093,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "jp.anthropic.claude-sonnet-4-6": { "supports_adaptive_thinking": true, @@ -1843,7 +2113,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -1855,7 +2124,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_output_config": true + "supports_output_config": true, + "supports_parallel_tool_use_config": true }, "anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 3.75e-06, @@ -1884,7 +2154,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "anthropic.claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 3.75e-06, @@ -1916,7 +2187,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "anthropic.claude-v1": { "input_cost_per_token": 8e-06, @@ -2166,7 +2438,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "apac.anthropic.claude-3-sonnet-20240229-v1:0": { "input_cost_per_token": 3e-06, @@ -2211,7 +2484,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "assemblyai/best": { "input_cost_per_second": 3.333e-05, @@ -2255,7 +2529,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "azure/ada": { "input_cost_per_token": 1e-07, @@ -2364,7 +2639,6 @@ "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -2394,7 +2668,6 @@ "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -2455,7 +2728,6 @@ "cache_creation_input_token_cost": 6.25e-06, "cache_creation_input_token_cost_above_1hr": 1e-05, "cache_read_input_token_cost": 5e-07, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -2511,6 +2783,36 @@ "supports_tool_choice": true, "supports_vision": true }, + "azure_ai/claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_max_reasoning_effort": true + }, "azure_ai/claude-sonnet-4-6": { "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 3.75e-06, @@ -2523,7 +2825,6 @@ "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -5511,6 +5812,76 @@ "supports_tool_choice": true, "supports_vision": true }, + "azure/us/gpt-5.4": { + "cache_read_input_token_cost": 2.8e-07, + "cache_read_input_token_cost_priority": 5.5e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_priority": 5.5e-06, + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_priority": 3.3e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "azure/eu/gpt-5.4": { + "cache_read_input_token_cost": 2.8e-07, + "cache_read_input_token_cost_priority": 5.5e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_priority": 5.5e-06, + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_priority": 3.3e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, "azure/gpt-5.4-2026-03-05": { "cache_read_input_token_cost": 2.5e-07, "cache_read_input_token_cost_above_272k_tokens": 5e-07, @@ -5552,6 +5923,76 @@ "supports_tool_choice": true, "supports_vision": true }, + "azure/us/gpt-5.4-2026-03-05": { + "cache_read_input_token_cost": 2.8e-07, + "cache_read_input_token_cost_priority": 5.5e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_priority": 5.5e-06, + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_priority": 3.3e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "azure/eu/gpt-5.4-2026-03-05": { + "cache_read_input_token_cost": 2.8e-07, + "cache_read_input_token_cost_priority": 5.5e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_priority": 5.5e-06, + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_priority": 3.3e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, "azure/gpt-5.4-pro": { "cache_read_input_token_cost": 3e-06, "cache_read_input_token_cost_above_272k_tokens": 6e-06, @@ -5622,6 +6063,522 @@ "supports_vision": true, "supports_web_search": true }, + "azure/gpt-5.6": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "cache_read_input_token_cost_priority": 1e-06, + "cache_read_input_token_cost_above_272k_tokens_priority": 2e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "input_cost_per_token_priority": 1e-05, + "input_cost_per_token_above_272k_tokens_priority": 2e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "output_cost_per_token_above_272k_tokens": 4.5e-05, + "output_cost_per_token_priority": 6e-05, + "output_cost_per_token_above_272k_tokens_priority": 9e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/gpt-5.6-sol": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "cache_read_input_token_cost_priority": 1e-06, + "cache_read_input_token_cost_above_272k_tokens_priority": 2e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "input_cost_per_token_priority": 1e-05, + "input_cost_per_token_above_272k_tokens_priority": 2e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "output_cost_per_token_above_272k_tokens": 4.5e-05, + "output_cost_per_token_priority": 6e-05, + "output_cost_per_token_above_272k_tokens_priority": 9e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/gpt-5.6-terra": { + "cache_read_input_token_cost": 2.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 5e-07, + "cache_read_input_token_cost_priority": 5e-07, + "cache_read_input_token_cost_above_272k_tokens_priority": 1e-06, + "input_cost_per_token": 2.5e-06, + "input_cost_per_token_above_272k_tokens": 5e-06, + "input_cost_per_token_priority": 5e-06, + "input_cost_per_token_above_272k_tokens_priority": 1e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "output_cost_per_token_above_272k_tokens": 2.25e-05, + "output_cost_per_token_priority": 3e-05, + "output_cost_per_token_above_272k_tokens_priority": 4.5e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/gpt-5.6-luna": { + "cache_read_input_token_cost": 1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2e-07, + "cache_read_input_token_cost_priority": 2e-07, + "cache_read_input_token_cost_above_272k_tokens_priority": 4e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "input_cost_per_token_priority": 2e-06, + "input_cost_per_token_above_272k_tokens_priority": 4e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_272k_tokens": 9e-06, + "output_cost_per_token_priority": 1.2e-05, + "output_cost_per_token_above_272k_tokens_priority": 1.8e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/us/gpt-5.6": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.375e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/us/gpt-5.6-sol": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.375e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/us/gpt-5.6-terra": { + "cache_read_input_token_cost": 2.75e-07, + "cache_read_input_token_cost_above_272k_tokens": 5.5e-07, + "cache_read_input_token_cost_priority": 6.875e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_above_272k_tokens": 5.5e-06, + "input_cost_per_token_priority": 6.875e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_above_272k_tokens": 2.475e-05, + "output_cost_per_token_priority": 4.125e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/us/gpt-5.6-luna": { + "cache_read_input_token_cost": 1.1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2.2e-07, + "cache_read_input_token_cost_priority": 2.75e-07, + "input_cost_per_token": 1.1e-06, + "input_cost_per_token_above_272k_tokens": 2.2e-06, + "input_cost_per_token_priority": 2.75e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 6.6e-06, + "output_cost_per_token_above_272k_tokens": 9.9e-06, + "output_cost_per_token_priority": 1.65e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.6": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.375e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.6-sol": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.375e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.6-terra": { + "cache_read_input_token_cost": 2.75e-07, + "cache_read_input_token_cost_above_272k_tokens": 5.5e-07, + "cache_read_input_token_cost_priority": 6.875e-07, + "input_cost_per_token": 2.75e-06, + "input_cost_per_token_above_272k_tokens": 5.5e-06, + "input_cost_per_token_priority": 6.875e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.65e-05, + "output_cost_per_token_above_272k_tokens": 2.475e-05, + "output_cost_per_token_priority": 4.125e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.6-luna": { + "cache_read_input_token_cost": 1.1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2.2e-07, + "cache_read_input_token_cost_priority": 2.75e-07, + "input_cost_per_token": 1.1e-06, + "input_cost_per_token_above_272k_tokens": 2.2e-06, + "input_cost_per_token_priority": 2.75e-06, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 6.6e-06, + "output_cost_per_token_above_272k_tokens": 9.9e-06, + "output_cost_per_token_priority": 1.65e-05, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, "azure/gpt-5.5": { "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, @@ -5667,6 +6624,90 @@ "supports_xhigh_reasoning_effort": true, "supports_minimal_reasoning_effort": false }, + "azure/us/gpt-5.5": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.38e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, + "azure/eu/gpt-5.5": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.38e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": false + }, "azure/gpt-5.5-2026-04-23": { "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, @@ -5709,6 +6750,84 @@ "supports_vision": true, "supports_web_search": true }, + "azure/us/gpt-5.5-2026-04-23": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.38e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, + "azure/eu/gpt-5.5-2026-04-23": { + "cache_read_input_token_cost": 5.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1.1e-06, + "cache_read_input_token_cost_priority": 1.38e-06, + "input_cost_per_token": 5.5e-06, + "input_cost_per_token_above_272k_tokens": 1.1e-05, + "input_cost_per_token_priority": 1.375e-05, + "output_cost_per_token": 3.3e-05, + "output_cost_per_token_above_272k_tokens": 4.95e-05, + "output_cost_per_token_priority": 8.25e-05, + "litellm_provider": "azure", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "azure/gpt-5.5-pro": { "cache_read_input_token_cost": 3e-06, "cache_read_input_token_cost_above_272k_tokens": 6e-06, @@ -9132,17 +10251,16 @@ }, "bedrock/us-east-1/minimax.minimax-m2.5": { "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 8192, "max_tokens": 8192, "mode": "chat", - "source": "https://aws.amazon.com/bedrock/pricing/", "supports_function_calling": true, - "supports_reasoning": true, "supports_system_messages": true, "supports_tool_choice": true, - "output_cost_per_token": 1.2e-06 + "source": "https://aws.amazon.com/bedrock/pricing/" }, "bedrock/us-east-1/moonshotai.kimi-k2-thinking": { "input_cost_per_token": 6e-07, @@ -9371,7 +10489,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-east-1/claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 4.5e-06, @@ -9393,7 +10512,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-east-1/meta.llama3-70b-instruct-v1:0": { "input_cost_per_token": 2.65e-06, @@ -9546,7 +10666,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-west-1/claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 4.5e-06, @@ -9568,7 +10689,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-west-1/meta.llama3-70b-instruct-v1:0": { "input_cost_per_token": 2.65e-06, @@ -9754,17 +10876,16 @@ }, "bedrock/us-west-2/minimax.minimax-m2.5": { "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, "litellm_provider": "bedrock", "max_input_tokens": 1000000, "max_output_tokens": 8192, "max_tokens": 8192, "mode": "chat", - "source": "https://aws.amazon.com/bedrock/pricing/", "supports_function_calling": true, - "supports_reasoning": true, "supports_system_messages": true, "supports_tool_choice": true, - "output_cost_per_token": 1.2e-06 + "source": "https://aws.amazon.com/bedrock/pricing/" }, "bedrock/us-west-2/moonshotai.kimi-k2-thinking": { "input_cost_per_token": 6e-07, @@ -10245,6 +11366,40 @@ "supports_vision": true, "supports_web_search": true }, + "claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "anthropic", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_max_reasoning_effort": true, + "provider_specific_entry": { + "us": 1.1 + }, + "supports_output_config": true + }, "claude-sonnet-4-6": { "cache_creation_input_token_cost": 3.75e-06, "cache_creation_input_token_cost_above_1hr": 6e-06, @@ -10298,7 +11453,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "supports_parallel_tool_use_config": true }, "claude-opus-4-1": { "cache_creation_input_token_cost": 1.875e-05, @@ -14370,7 +15526,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-3-5-sonnet-20240620-v1:0": { "input_cost_per_token": 3e-06, @@ -14551,7 +15708,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "eu.anthropic.claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 4.125e-06, @@ -14583,7 +15741,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "eu.meta.llama3-2-1b-instruct-v1:0": { "input_cost_per_token": 1.3e-07, @@ -17706,7 +18865,8 @@ "search_context_size_medium": 0.014, "search_context_size_high": 0.014 }, - "web_search_billing_unit": "per_query" + "web_search_billing_unit": "per_query", + "supports_reasoning": false }, "gemini/gemini-3-pro-image-preview": { "input_cost_per_image": 0.0011, @@ -17739,6 +18899,7 @@ ], "supports_function_calling": false, "supports_prompt_caching": true, + "supports_reasoning": false, "supports_response_schema": true, "supports_system_messages": true, "supports_vision": true, @@ -17780,6 +18941,7 @@ ], "supports_function_calling": false, "supports_prompt_caching": true, + "supports_reasoning": false, "supports_response_schema": true, "supports_system_messages": true, "supports_vision": true, @@ -17821,6 +18983,7 @@ ], "supports_function_calling": false, "supports_prompt_caching": true, + "supports_reasoning": false, "supports_response_schema": true, "supports_system_messages": true, "supports_vision": true, @@ -19105,7 +20268,6 @@ "supported_endpoints": [ "/v1/chat/completions" ], - "supports_adaptive_thinking": true, "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_vision": true @@ -19941,7 +21103,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 3.75e-06, @@ -19970,7 +21133,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "global.anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.25e-06, @@ -19993,7 +21157,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "global.amazon.nova-2-lite-v1:0": { "cache_read_input_token_cost": 7.5e-08, @@ -21837,6 +23002,218 @@ "supports_xhigh_reasoning_effort": true, "supports_minimal_reasoning_effort": true }, + "gpt-5.6": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05, + "cache_creation_input_token_cost_flex": 3.125e-06, + "cache_creation_input_token_cost_priority": 1.25e-05, + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "cache_read_input_token_cost_flex": 2.5e-07, + "cache_read_input_token_cost_priority": 1e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "input_cost_per_token_batches": 2.5e-06, + "input_cost_per_token_flex": 2.5e-06, + "input_cost_per_token_priority": 1e-05, + "litellm_provider": "openai", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "output_cost_per_token_above_272k_tokens": 4.5e-05, + "output_cost_per_token_batches": 1.5e-05, + "output_cost_per_token_flex": 1.5e-05, + "output_cost_per_token_priority": 6e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, + "gpt-5.6-sol": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05, + "cache_creation_input_token_cost_flex": 3.125e-06, + "cache_creation_input_token_cost_priority": 1.25e-05, + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "cache_read_input_token_cost_flex": 2.5e-07, + "cache_read_input_token_cost_priority": 1e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "input_cost_per_token_batches": 2.5e-06, + "input_cost_per_token_flex": 2.5e-06, + "input_cost_per_token_priority": 1e-05, + "litellm_provider": "openai", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "output_cost_per_token_above_272k_tokens": 4.5e-05, + "output_cost_per_token_batches": 1.5e-05, + "output_cost_per_token_flex": 1.5e-05, + "output_cost_per_token_priority": 6e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, + "gpt-5.6-terra": { + "cache_creation_input_token_cost": 3.125e-06, + "cache_creation_input_token_cost_above_272k_tokens": 6.25e-06, + "cache_creation_input_token_cost_flex": 1.5625e-06, + "cache_creation_input_token_cost_priority": 6.25e-06, + "cache_read_input_token_cost": 2.5e-07, + "cache_read_input_token_cost_above_272k_tokens": 5e-07, + "cache_read_input_token_cost_flex": 1.25e-07, + "cache_read_input_token_cost_priority": 5e-07, + "input_cost_per_token": 2.5e-06, + "input_cost_per_token_above_272k_tokens": 5e-06, + "input_cost_per_token_batches": 1.25e-06, + "input_cost_per_token_flex": 1.25e-06, + "input_cost_per_token_priority": 5e-06, + "litellm_provider": "openai", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "output_cost_per_token_above_272k_tokens": 2.25e-05, + "output_cost_per_token_batches": 7.5e-06, + "output_cost_per_token_flex": 7.5e-06, + "output_cost_per_token_priority": 3e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, + "gpt-5.6-luna": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_creation_input_token_cost_flex": 6.25e-07, + "cache_creation_input_token_cost_priority": 2.5e-06, + "cache_read_input_token_cost": 1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2e-07, + "cache_read_input_token_cost_flex": 5e-08, + "cache_read_input_token_cost_priority": 2e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, + "input_cost_per_token_batches": 5e-07, + "input_cost_per_token_flex": 5e-07, + "input_cost_per_token_priority": 2e-06, + "litellm_provider": "openai", + "max_input_tokens": 1050000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_272k_tokens": 9e-06, + "output_cost_per_token_batches": 3e-06, + "output_cost_per_token_flex": 3e-06, + "output_cost_per_token_priority": 1.2e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": false, + "supports_native_streaming": true, + "supports_none_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, "gpt-5.5": { "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, @@ -21857,8 +23234,8 @@ "output_cost_per_token_flex": 1.5e-05, "output_cost_per_token_batches": 1.5e-05, "output_cost_per_token_priority": 6e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -21906,8 +23283,8 @@ "output_cost_per_token_flex": 1.5e-05, "output_cost_per_token_batches": 1.5e-05, "output_cost_per_token_priority": 6e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -21951,8 +23328,8 @@ "output_cost_per_token_above_272k_tokens": 0.00027, "output_cost_per_token_flex": 9e-05, "output_cost_per_token_batches": 9e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/responses", "/v1/batch" @@ -21996,8 +23373,8 @@ "output_cost_per_token_above_272k_tokens": 0.00027, "output_cost_per_token_flex": 9e-05, "output_cost_per_token_batches": 9e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/responses", "/v1/batch" @@ -22045,8 +23422,8 @@ "output_cost_per_token_flex": 7.5e-06, "output_cost_per_token_batches": 7.5e-06, "output_cost_per_token_priority": 3e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22093,8 +23470,8 @@ "output_cost_per_token_flex": 7.5e-06, "output_cost_per_token_batches": 7.5e-06, "output_cost_per_token_priority": 3e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22134,8 +23511,8 @@ "output_cost_per_token_above_272k_tokens": 0.00027, "output_cost_per_token_flex": 9e-05, "output_cost_per_token_batches": 9e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/responses", "/v1/batch" @@ -22178,8 +23555,8 @@ "output_cost_per_token_above_272k_tokens": 0.00027, "output_cost_per_token_flex": 9e-05, "output_cost_per_token_batches": 9e-05, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/responses", "/v1/batch" @@ -22223,8 +23600,8 @@ "output_cost_per_token_flex": 2.25e-06, "output_cost_per_token_batches": 2.25e-06, "output_cost_per_token_priority": 9e-06, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22269,8 +23646,8 @@ "output_cost_per_token_flex": 2.25e-06, "output_cost_per_token_batches": 2.25e-06, "output_cost_per_token_priority": 9e-06, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22312,8 +23689,8 @@ "output_cost_per_token": 1.25e-06, "output_cost_per_token_flex": 6.25e-07, "output_cost_per_token_batches": 6.25e-07, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -22355,8 +23732,8 @@ "output_cost_per_token": 1.25e-06, "output_cost_per_token_flex": 6.25e-07, "output_cost_per_token_batches": 6.25e-07, - "regional_processing_uplift_multiplier_eu": 1.10, - "regional_processing_uplift_multiplier_us": 1.10, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, "supported_endpoints": [ "/v1/chat/completions", "/v1/batch", @@ -23067,6 +24444,76 @@ "supports_system_messages": true, "supports_tool_choice": true }, + "gpt-realtime-2.1": { + "cache_creation_input_audio_token_cost": 4e-07, + "cache_read_input_audio_token_cost": 4e-07, + "cache_read_input_token_cost": 4e-07, + "input_cost_per_audio_token": 3.2e-05, + "input_cost_per_image": 5e-06, + "input_cost_per_token": 4e-06, + "litellm_provider": "openai", + "max_input_tokens": 128000, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_audio_token": 6.4e-05, + "output_cost_per_token": 2.4e-05, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/realtime" + ], + "supported_modalities": [ + "text", + "image", + "audio" + ], + "supported_output_modalities": [ + "text", + "audio" + ], + "supports_audio_input": true, + "supports_audio_output": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_system_messages": true, + "supports_tool_choice": true + }, + "gpt-realtime-2.1-mini": { + "cache_creation_input_audio_token_cost": 3e-07, + "cache_read_input_audio_token_cost": 3e-07, + "cache_read_input_token_cost": 6e-08, + "input_cost_per_audio_token": 1e-05, + "input_cost_per_image": 8e-07, + "input_cost_per_token": 6e-07, + "litellm_provider": "openai", + "max_input_tokens": 128000, + "max_output_tokens": 4096, + "max_tokens": 4096, + "mode": "chat", + "output_cost_per_audio_token": 2e-05, + "output_cost_per_token": 2.4e-06, + "regional_processing_uplift_multiplier_eu": 1.1, + "regional_processing_uplift_multiplier_us": 1.1, + "supported_endpoints": [ + "/v1/realtime" + ], + "supported_modalities": [ + "text", + "image", + "audio" + ], + "supported_output_modalities": [ + "text", + "audio" + ], + "supports_audio_input": true, + "supports_audio_output": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_system_messages": true, + "supports_tool_choice": true + }, "gpt-realtime-mini": { "cache_creation_input_audio_token_cost": 3e-07, "cache_read_input_audio_token_cost": 3e-07, @@ -24062,7 +25509,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "jp.anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.375e-06, @@ -24085,7 +25533,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "crusoe/deepseek-ai/DeepSeek-R1-0528": { "input_cost_per_token": 3e-06, @@ -24781,6 +26230,42 @@ "supports_function_calling": true, "supports_tool_choice": false }, + "meta/muse-spark-1.1": { + "cache_read_input_token_cost": 1.5e-07, + "input_cost_per_token": 1.25e-06, + "litellm_provider": "meta", + "max_input_tokens": 1048576, + "max_output_tokens": 131072, + "max_tokens": 131072, + "mode": "chat", + "output_cost_per_token": 4.25e-06, + "source": "https://dev.meta.ai/docs/getting-started/pricing-rate-limits", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses", + "/v1/messages" + ], + "supported_modalities": [ + "text", + "image", + "video" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_minimal_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_xhigh_reasoning_effort": true + }, "meta_llama/Llama-3.3-70B-Instruct": { "litellm_provider": "meta_llama", "max_input_tokens": 128000, @@ -24873,14 +26358,13 @@ }, "minimax.minimax-m2.5": { "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 1000000, "max_output_tokens": 8192, "max_tokens": 8192, "mode": "chat", - "output_cost_per_token": 1.2e-06, "supports_function_calling": true, - "supports_reasoning": true, "supports_system_messages": true, "supports_tool_choice": true, "source": "https://aws.amazon.com/bedrock/pricing/" @@ -28336,7 +29820,6 @@ "output_cost_per_token": 1.5e-05, "output_cost_per_token_above_200k_tokens": 2.25e-05, "source": "https://openrouter.ai/anthropic/claude-sonnet-4.6", - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -28376,7 +29859,6 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -28438,7 +29920,6 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 2.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -30377,7 +31858,6 @@ "supports_adaptive_thinking": true, "litellm_provider": "perplexity", "mode": "responses", - "supports_adaptive_thinking": true, "supports_web_search": true, "supports_reasoning": false, "supports_function_calling": true, @@ -30387,7 +31867,6 @@ "supports_adaptive_thinking": true, "litellm_provider": "perplexity", "mode": "responses", - "supports_adaptive_thinking": true, "supports_web_search": true, "supports_reasoning": false, "supports_function_calling": true, @@ -31434,15 +32913,15 @@ "supports_vision": true, "source": "https://cloud.sambanova.ai/plans/pricing" }, - "snowflake/claude-3-5-sonnet": { + "snowflake/claude-3-5-sonnet": { "litellm_provider": "snowflake", "max_input_tokens": 200000, "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, "supports_computer_use": true, "supports_function_calling": true, "supports_vision": true, @@ -31450,14 +32929,14 @@ "supports_system_messages": true, "supports_response_schema": true }, - "snowflake/deepseek-r1": { + "snowflake/deepseek-r1": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.00000135, - "output_cost_per_token": 0.0000054, + "input_cost_per_token": 1.35e-06, + "output_cost_per_token": 5.4e-06, "supports_reasoning": true, "supports_system_messages": true }, @@ -31516,8 +32995,8 @@ "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.0000012, - "output_cost_per_token": 0.0000012, + "input_cost_per_token": 1.2e-06, + "output_cost_per_token": 1.2e-06, "supports_function_calling": true, "supports_system_messages": true }, @@ -31527,8 +33006,8 @@ "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.00000072, - "output_cost_per_token": 0.00000072, + "input_cost_per_token": 7.2e-07, + "output_cost_per_token": 7.2e-07, "supports_function_calling": true, "supports_system_messages": true }, @@ -31538,8 +33017,8 @@ "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.00000024, - "output_cost_per_token": 0.00000024, + "input_cost_per_token": 2.4e-07, + "output_cost_per_token": 2.4e-07, "supports_system_messages": true }, "snowflake/llama3.2-1b": { @@ -31556,17 +33035,17 @@ "max_tokens": 8192, "mode": "chat" }, - "snowflake/llama3.3-70b": { + "snowflake/llama3.3-70b": { "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, - "input_cost_per_token": 0.00000072, - "output_cost_per_token": 0.00000072, + "input_cost_per_token": 7.2e-07, + "output_cost_per_token": 7.2e-07, "litellm_provider": "snowflake", "mode": "chat", "supports_function_calling": true, "supports_system_messages": true - }, + }, "snowflake/mistral-7b": { "litellm_provider": "snowflake", "max_input_tokens": 32000, @@ -31581,14 +33060,14 @@ "max_tokens": 8192, "mode": "chat" }, - "snowflake/mistral-large2": { + "snowflake/mistral-large2": { "litellm_provider": "snowflake", "max_input_tokens": 128000, "max_output_tokens": 16384, "max_tokens": 16384, "mode": "chat", - "input_cost_per_token": 0.000002, - "output_cost_per_token": 0.000006, + "input_cost_per_token": 2e-06, + "output_cost_per_token": 6e-06, "supports_function_calling": true, "supports_system_messages": true, "supports_response_schema": true @@ -31628,17 +33107,17 @@ "max_tokens": 8192, "mode": "chat" }, - "snowflake/snowflake-llama-3.3-70b": { + "snowflake/snowflake-llama-3.3-70b": { "max_tokens": 16384, "max_input_tokens": 128000, "max_output_tokens": 16384, - "input_cost_per_token": 0.00000072, - "output_cost_per_token": 0.00000072, + "input_cost_per_token": 7.2e-07, + "output_cost_per_token": 7.2e-07, "litellm_provider": "snowflake", "mode": "chat", "supports_function_calling": true, "supports_system_messages": true - }, + }, "stability/sd3": { "litellm_provider": "stability", "mode": "image_generation", @@ -32722,7 +34201,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-3-5-sonnet-20240620-v1:0": { "input_cost_per_token": 3e-06, @@ -32881,7 +34361,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 4.5e-06, @@ -32890,7 +34371,7 @@ "input_cost_per_token": 3.6e-06, "input_cost_per_token_above_200k_tokens": 7.2e-06, "output_cost_per_token_above_200k_tokens": 2.7e-05, - "cache_creation_input_token_cost_above_200k_tokens": 9.0e-06, + "cache_creation_input_token_cost_above_200k_tokens": 9e-06, "cache_creation_input_token_cost_above_1hr_above_200k_tokens": 1.44e-05, "cache_read_input_token_cost_above_200k_tokens": 7.2e-07, "litellm_provider": "bedrock_converse", @@ -32908,7 +34389,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "au.anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.375e-06, @@ -32930,7 +34412,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-opus-4-20250514-v1:0": { "cache_creation_input_token_cost": 1.875e-05, @@ -32984,7 +34467,8 @@ "supports_vision": true, "supports_native_structured_output": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "high" + "bedrock_output_config_effort_ceiling": "high", + "supports_parallel_tool_use_config": true }, "global.anthropic.claude-opus-4-5-20251101-v1:0": { "cache_creation_input_token_cost": 6.25e-06, @@ -33013,7 +34497,8 @@ "supports_vision": true, "supports_native_structured_output": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "high" + "bedrock_output_config_effort_ceiling": "high", + "supports_parallel_tool_use_config": true }, "eu.anthropic.claude-opus-4-5-20251101-v1:0": { "cache_creation_input_token_cost": 6.25e-06, @@ -33041,7 +34526,8 @@ "supports_vision": true, "supports_native_structured_output": true, "supports_output_config": true, - "bedrock_output_config_effort_ceiling": "high" + "bedrock_output_config_effort_ceiling": "high", + "supports_parallel_tool_use_config": true }, "us.anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 3.75e-06, @@ -33070,7 +34556,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "bedrock_converse_supports_strict_tools": false }, "us.deepseek.r1-v1:0": { "input_cost_per_token": 1.35e-06, @@ -33632,7 +35119,6 @@ "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 2.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -34534,6 +36020,19 @@ "/v1/audio/speech" ] }, + "vertex_ai/chirp_3": { + "input_cost_per_second": 0.00026667, + "litellm_provider": "vertex_ai", + "metadata": { + "calculation": "$0.016/60 seconds = $0.00026667 per second", + "original_pricing_per_minute": 0.016 + }, + "mode": "audio_transcription", + "source": "https://cloud.google.com/speech-to-text/pricing", + "supported_endpoints": [ + "/v1/audio/transcriptions" + ] + }, "vertex_ai/claude-3-5-haiku": { "input_cost_per_token": 1e-06, "litellm_provider": "vertex_ai-anthropic_models", @@ -34867,7 +36366,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34897,7 +36395,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34927,7 +36424,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -34958,7 +36454,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -35049,7 +36544,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -35080,7 +36574,6 @@ "search_context_size_low": 0.01, "search_context_size_medium": 0.01 }, - "supports_adaptive_thinking": true, "supports_assistant_prefill": false, "supports_computer_use": true, "supports_function_calling": true, @@ -35121,6 +36614,36 @@ "supports_tool_choice": true, "supports_vision": true }, + "vertex_ai/claude-sonnet-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "vertex_ai-anthropic_models", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_max_reasoning_effort": true + }, "vertex_ai/claude-sonnet-4-6": { "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 3.75e-06, @@ -35133,7 +36656,6 @@ "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -35433,6 +36955,7 @@ "supports_parallel_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, + "supports_reasoning": false, "supports_response_schema": true, "supports_system_messages": true, "supports_tool_choice": true, @@ -35455,6 +36978,7 @@ "output_cost_per_image_token": 0.00012, "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, + "supports_reasoning": false, "source": "https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro-image" }, "vertex_ai/gemini-3-pro-image-preview": { @@ -35470,6 +36994,7 @@ "output_cost_per_image_token": 0.00012, "output_cost_per_token": 1.2e-05, "output_cost_per_token_batches": 6e-06, + "supports_reasoning": false, "source": "https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro-image" }, "vertex_ai/gemini-3.1-flash-image": { @@ -35483,6 +37008,7 @@ "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, "output_cost_per_token": 3e-06, + "supports_reasoning": false, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#gemini-models" }, "vertex_ai/gemini-3.1-flash-image-preview": { @@ -35496,6 +37022,7 @@ "output_cost_per_image": 0.0672, "output_cost_per_image_token": 6e-05, "output_cost_per_token": 3e-06, + "supports_reasoning": false, "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing#gemini-models" }, "vertex_ai/gemini-3.1-flash-lite-preview": { @@ -37600,6 +39127,48 @@ "supports_vision": true, "supports_web_search": true }, + "xai/grok-4.5": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_200k_tokens": 1e-06, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_200k_tokens": 4e-06, + "litellm_provider": "xai", + "max_input_tokens": 500000, + "max_output_tokens": 500000, + "max_tokens": 500000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_200k_tokens": 1.2e-05, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, + "xai/grok-4.5-latest": { + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_200k_tokens": 1e-06, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_200k_tokens": 4e-06, + "litellm_provider": "xai", + "max_input_tokens": 500000, + "max_output_tokens": 500000, + "max_tokens": 500000, + "mode": "chat", + "output_cost_per_token": 6e-06, + "output_cost_per_token_above_200k_tokens": 1.2e-05, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "xai/grok-beta": { "input_cost_per_token": 5e-06, "litellm_provider": "xai", @@ -37690,12 +39259,12 @@ }, "zai.glm-5": { "input_cost_per_token": 1e-06, + "output_cost_per_token": 3.2e-06, "litellm_provider": "bedrock_converse", "max_input_tokens": 200000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", - "output_cost_per_token": 3.2e-06, "supports_function_calling": true, "supports_reasoning": true, "supports_system_messages": true, @@ -37716,20 +39285,6 @@ "supports_tool_choice": true, "source": "https://aws.amazon.com/bedrock/pricing/" }, - "zai.glm-5": { - "input_cost_per_token": 1e-06, - "litellm_provider": "bedrock_converse", - "max_input_tokens": 200000, - "max_output_tokens": 128000, - "max_tokens": 128000, - "mode": "chat", - "output_cost_per_token": 3.2e-06, - "source": "https://aws.amazon.com/bedrock/pricing/", - "supports_function_calling": true, - "supports_reasoning": true, - "supports_system_messages": true, - "supports_tool_choice": true - }, "zai/glm-5": { "cache_creation_input_token_cost": 0, "cache_read_input_token_cost": 2e-07, @@ -42616,6 +44171,36 @@ "search_context_size_high": 0.035 } }, + "vertex_ai/claude-sonnet-5@default": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + "input_cost_per_token": 2e-06, + "litellm_provider": "vertex_ai-anthropic_models", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_adaptive_thinking": true, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_sampling_params": false, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "supports_max_reasoning_effort": true + }, "vertex_ai/claude-sonnet-4-6@default": { "supports_adaptive_thinking": true, "cache_creation_input_token_cost": 3.75e-06, @@ -42628,7 +44213,6 @@ "max_tokens": 64000, "mode": "chat", "output_cost_per_token": 1.5e-05, - "supports_adaptive_thinking": true, "supports_assistant_prefill": true, "supports_computer_use": true, "supports_function_calling": true, @@ -42662,7 +44246,10 @@ "max_output_tokens": 32768, "max_tokens": 32768, "mode": "chat", - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_reasoning": true, @@ -42677,7 +44264,10 @@ "max_output_tokens": 32768, "max_tokens": 32768, "mode": "chat", - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": true, "supports_reasoning": true, @@ -42692,7 +44282,9 @@ "max_output_tokens": 65536, "max_tokens": 65536, "mode": "chat", - "supported_endpoints": ["/v1/chat/completions"], + "supported_endpoints": [ + "/v1/chat/completions" + ], "supports_function_calling": true, "supports_reasoning": true, "supports_response_schema": true, @@ -42706,7 +44298,9 @@ "max_output_tokens": 65536, "max_tokens": 65536, "mode": "chat", - "supported_endpoints": ["/v1/chat/completions"], + "supported_endpoints": [ + "/v1/chat/completions" + ], "supports_function_calling": true, "supports_reasoning": true, "supports_response_schema": true, @@ -42722,9 +44316,16 @@ "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/responses"], - "supported_modalities": ["text", "image"], - "supported_output_modalities": ["text"], + "supported_endpoints": [ + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, @@ -42742,9 +44343,16 @@ "max_tokens": 128000, "mode": "responses", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/responses"], - "supported_modalities": ["text", "image"], - "supported_output_modalities": ["text"], + "supported_endpoints": [ + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], "supports_function_calling": true, "supports_prompt_caching": true, "supports_reasoning": true, @@ -42761,7 +44369,10 @@ "max_tokens": 256000, "mode": "chat", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": false, "supports_reasoning": true, @@ -42777,7 +44388,10 @@ "max_tokens": 256000, "mode": "chat", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": false, "supports_reasoning": true, @@ -42793,13 +44407,36 @@ "max_tokens": 128000, "mode": "chat", "use_openai_responses_path": true, - "supported_endpoints": ["/v1/chat/completions", "/v1/responses"], + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], "supports_function_calling": true, "supports_parallel_function_calling": false, "supports_reasoning": true, "supports_tool_choice": true, "supports_vision": true }, + "bedrock_mantle/xai.grok-4.3": { + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 2.5e-06, + "cache_read_input_token_cost": 2e-07, + "litellm_provider": "bedrock_mantle", + "max_input_tokens": 131072, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supports_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "source": "https://aws.amazon.com/bedrock/pricing/" + }, "volcengine/doubao-seed-2-0-pro-260215": { "litellm_provider": "volcengine", "max_input_tokens": 256000, @@ -42952,20 +44589,6 @@ } ] }, - "zai.glm-5": { - "input_cost_per_token": 1e-06, - "output_cost_per_token": 3.2e-06, - "litellm_provider": "bedrock_converse", - "max_input_tokens": 200000, - "max_output_tokens": 128000, - "max_tokens": 128000, - "mode": "chat", - "supports_function_calling": true, - "supports_reasoning": true, - "supports_system_messages": true, - "supports_tool_choice": true, - "source": "https://aws.amazon.com/bedrock/pricing/" - }, "bedrock/us-east-1/zai.glm-5": { "input_cost_per_token": 1e-06, "output_cost_per_token": 3.2e-06, @@ -42994,45 +44617,6 @@ "supports_tool_choice": true, "source": "https://aws.amazon.com/bedrock/pricing/" }, - "minimax.minimax-m2.5": { - "input_cost_per_token": 3e-07, - "output_cost_per_token": 1.2e-06, - "litellm_provider": "bedrock_converse", - "max_input_tokens": 1000000, - "max_output_tokens": 8192, - "max_tokens": 8192, - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_tool_choice": true, - "source": "https://aws.amazon.com/bedrock/pricing/" - }, - "bedrock/us-east-1/minimax.minimax-m2.5": { - "input_cost_per_token": 3e-07, - "output_cost_per_token": 1.2e-06, - "litellm_provider": "bedrock", - "max_input_tokens": 1000000, - "max_output_tokens": 8192, - "max_tokens": 8192, - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_tool_choice": true, - "source": "https://aws.amazon.com/bedrock/pricing/" - }, - "bedrock/us-west-2/minimax.minimax-m2.5": { - "input_cost_per_token": 3e-07, - "output_cost_per_token": 1.2e-06, - "litellm_provider": "bedrock", - "max_input_tokens": 1000000, - "max_output_tokens": 8192, - "max_tokens": 8192, - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_tool_choice": true, - "source": "https://aws.amazon.com/bedrock/pricing/" - }, "bedrock/us-gov-east-1/anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.5e-06, "cache_creation_input_token_cost_above_1hr": 2.4e-06, @@ -43054,7 +44638,8 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_pdf_input": true + "supports_pdf_input": true, + "supports_parallel_tool_use_config": true }, "bedrock/us-gov-west-1/anthropic.claude-haiku-4-5-20251001-v1:0": { "cache_creation_input_token_cost": 1.5e-06, @@ -43077,365 +44662,369 @@ "supports_tool_choice": true, "supports_vision": true, "supports_native_structured_output": true, - "supports_pdf_input": true + "supports_pdf_input": true, + "supports_parallel_tool_use_config": true + }, + "snowflake/claude-sonnet-4-5": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/claude-sonnet-4-6": { + "supports_adaptive_thinking": true, + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/claude-4-sonnet": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/claude-4-opus": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 5e-06, + "output_cost_per_token": 2.5e-05, + "cache_read_input_token_cost": 5e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "supports_response_schema": true + }, + "snowflake/claude-haiku-4-5": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 1e-06, + "output_cost_per_token": 5e-06, + "cache_read_input_token_cost": 1e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/claude-3-7-sonnet": { + "max_tokens": 16384, + "max_input_tokens": 200000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-06, + "output_cost_per_token": 1.5e-05, + "cache_read_input_token_cost": 3e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "supports_response_schema": true + }, + "snowflake/openai-gpt-4.1": { + "max_tokens": 16384, + "max_input_tokens": 300000, + "max_output_tokens": 16384, + "input_cost_per_token": 2e-06, + "output_cost_per_token": 8e-06, + "cache_read_input_token_cost": 5e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/openai-gpt-5": { + "max_tokens": 16384, + "max_input_tokens": 300000, + "max_output_tokens": 16384, + "input_cost_per_token": 1.25e-06, + "output_cost_per_token": 1e-05, + "cache_read_input_token_cost": 1.25e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "supports_response_schema": true + }, + "snowflake/openai-gpt-5-mini": { + "max_tokens": 16384, + "max_input_tokens": 1000000, + "max_output_tokens": 16384, + "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/openai-gpt-5-nano": { + "max_tokens": 16384, + "max_input_tokens": 5000000, + "max_output_tokens": 16384, + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_system_messages": true, + "supports_response_schema": true + }, + "snowflake/llama4-maverick": { + "max_tokens": 16384, + "max_input_tokens": 128000, + "max_output_tokens": 16384, + "input_cost_per_token": 2.4e-07, + "output_cost_per_token": 9.7e-07, + "litellm_provider": "snowflake", + "mode": "chat", + "supports_function_calling": true, + "supports_system_messages": true + }, + "snowflake/snowflake-arctic-embed-l-v2.0": { + "max_tokens": 8192, + "max_input_tokens": 8192, + "input_cost_per_token": 7e-08, + "output_cost_per_token": 0.0, + "litellm_provider": "snowflake", + "mode": "embedding" + }, + "snowflake/snowflake-arctic-embed-m-v2.0": { + "max_tokens": 8192, + "max_input_tokens": 8192, + "input_cost_per_token": 7e-08, + "output_cost_per_token": 0.0, + "litellm_provider": "snowflake", + "mode": "embedding" + }, + "soniox/stt-async-v4": { + "litellm_provider": "soniox", + "max_output_tokens": 8000, + "max_tokens": 8000, + "input_cost_per_second": 0.0, + "output_cost_per_second": 2.77778e-05, + "mode": "audio_transcription", + "source": "https://soniox.com/pricing", + "supported_endpoints": [ + "/v1/audio/transcriptions" + ], + "supports_audio_input": true + }, + "soniox/stt-async-v5": { + "litellm_provider": "soniox", + "max_output_tokens": 8000, + "max_tokens": 8000, + "input_cost_per_second": 0.0, + "output_cost_per_second": 2.77778e-05, + "mode": "audio_transcription", + "source": "https://soniox.com/pricing", + "supported_endpoints": [ + "/v1/audio/transcriptions" + ], + "supports_audio_input": true + }, + "tensormesh/Qwen/Qwen3.5-397B-A17B-FP8": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 6e-07, + "output_cost_per_token": 3.6e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 4.5e-07, + "output_cost_per_token": 1.8e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/Qwen/Qwen3.6-27B-FP8": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 3.2e-07, + "output_cost_per_token": 3.2e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/lukealonso/GLM-5.1-NVFP4-MTP": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 1.4e-06, + "output_cost_per_token": 4.4e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 202752, + "max_output_tokens": 202752, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/deepseek-ai/DeepSeek-V4-Flash": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 2.8e-07, + "cache_read_input_token_cost": 0, + "max_input_tokens": 32768, + "max_output_tokens": 32768, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/moonshotai/Kimi-K2.6": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 9.6e-07, + "output_cost_per_token": 4e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 32768, + "max_output_tokens": 32768, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/MiniMaxAI/MiniMax-M2.5": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, + "cache_read_input_token_cost": 0, + "max_input_tokens": 196608, + "max_output_tokens": 196608, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/google/gemma-4-31B-it": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 5.6e-07, + "cache_read_input_token_cost": 0, + "max_input_tokens": 32768, + "max_output_tokens": 32768, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/openai/gpt-oss-120b": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 1.5e-07, + "output_cost_per_token": 6e-07, + "cache_read_input_token_cost": 0, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" + }, + "tensormesh/openai/gpt-oss-20b": { + "litellm_provider": "tensormesh", + "mode": "chat", + "input_cost_per_token": 7e-08, + "output_cost_per_token": 2.8e-07, + "cache_read_input_token_cost": 0, + "max_input_tokens": 131072, + "max_output_tokens": 131072, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_response_schema": true, + "supports_prompt_caching": true, + "supports_system_messages": true, + "supports_reasoning": true, + "source": "https://serverless.tensormesh.ai/v1/models/openrouter" }, - "snowflake/claude-sonnet-4-5": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/claude-sonnet-4-6": { - "supports_adaptive_thinking": true, - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/claude-4-sonnet": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/claude-4-opus": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000005, - "output_cost_per_token": 0.000025, - "cache_read_input_token_cost": 0.0000005, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "supports_response_schema": true - }, - "snowflake/claude-haiku-4-5": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000001, - "output_cost_per_token": 0.000005, - "cache_read_input_token_cost": 0.0000001, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/claude-3-7-sonnet": { - "max_tokens": 16384, - "max_input_tokens": 200000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000015, - "cache_read_input_token_cost": 0.0000003, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "supports_response_schema": true - }, - "snowflake/openai-gpt-4.1": { - "max_tokens": 16384, - "max_input_tokens": 300000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.000002, - "output_cost_per_token": 0.000008, - "cache_read_input_token_cost": 0.0000005, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/openai-gpt-5": { - "max_tokens": 16384, - "max_input_tokens": 300000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.00000125, - "output_cost_per_token": 0.00001, - "cache_read_input_token_cost": 0.000000125, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_vision": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "supports_response_schema": true - }, - "snowflake/openai-gpt-5-mini": { - "max_tokens": 16384, - "max_input_tokens": 1000000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.0000003, - "output_cost_per_token": 0.0000012, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/openai-gpt-5-nano": { - "max_tokens": 16384, - "max_input_tokens": 5000000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.00000015, - "output_cost_per_token": 0.0000006, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true, - "supports_response_schema": true - }, - "snowflake/llama4-maverick": { - "max_tokens": 16384, - "max_input_tokens": 128000, - "max_output_tokens": 16384, - "input_cost_per_token": 0.00000024, - "output_cost_per_token": 0.00000097, - "litellm_provider": "snowflake", - "mode": "chat", - "supports_function_calling": true, - "supports_system_messages": true - }, - "snowflake/snowflake-arctic-embed-l-v2.0": { - "max_tokens": 8192, - "max_input_tokens": 8192, - "input_cost_per_token": 0.00000007, - "output_cost_per_token": 0.0, - "litellm_provider": "snowflake", - "mode": "embedding" - }, - "snowflake/snowflake-arctic-embed-m-v2.0": { - "max_tokens": 8192, - "max_input_tokens": 8192, - "input_cost_per_token": 0.00000007, - "output_cost_per_token": 0.0, - "litellm_provider": "snowflake", - "mode": "embedding" - }, - "soniox/stt-async-v4": { - "litellm_provider": "soniox", - "max_output_tokens": 8000, - "max_tokens": 8000, - "input_cost_per_second": 0.0, - "output_cost_per_second": 0.0000277778, - "mode": "audio_transcription", - "source": "https://soniox.com/pricing", - "supported_endpoints": ["/v1/audio/transcriptions"], - "supports_audio_input": true - }, - "soniox/stt-async-v5": { - "litellm_provider": "soniox", - "max_output_tokens": 8000, - "max_tokens": 8000, - "input_cost_per_second": 0.0, - "output_cost_per_second": 0.0000277778, - "mode": "audio_transcription", - "source": "https://soniox.com/pricing", - "supported_endpoints": ["/v1/audio/transcriptions"], - "supports_audio_input": true - }, - "tensormesh/Qwen/Qwen3.5-397B-A17B-FP8": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 6e-07, - "output_cost_per_token": 3.6e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 262144, - "max_output_tokens": 262144, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 4.5e-07, - "output_cost_per_token": 1.8e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 262144, - "max_output_tokens": 262144, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/Qwen/Qwen3.6-27B-FP8": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 3.2e-07, - "output_cost_per_token": 3.2e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 262144, - "max_output_tokens": 262144, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/lukealonso/GLM-5.1-NVFP4-MTP": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 1.4e-06, - "output_cost_per_token": 4.4e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 202752, - "max_output_tokens": 202752, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/deepseek-ai/DeepSeek-V4-Flash": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 1.4e-07, - "output_cost_per_token": 2.8e-07, - "cache_read_input_token_cost": 0, - "max_input_tokens": 32768, - "max_output_tokens": 32768, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/moonshotai/Kimi-K2.6": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 9.6e-07, - "output_cost_per_token": 4e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 32768, - "max_output_tokens": 32768, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/MiniMaxAI/MiniMax-M2.5": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 3e-07, - "output_cost_per_token": 1.2e-06, - "cache_read_input_token_cost": 0, - "max_input_tokens": 196608, - "max_output_tokens": 196608, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/google/gemma-4-31B-it": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 1.4e-07, - "output_cost_per_token": 5.6e-07, - "cache_read_input_token_cost": 0, - "max_input_tokens": 32768, - "max_output_tokens": 32768, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/openai/gpt-oss-120b": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 1.5e-07, - "output_cost_per_token": 6e-07, - "cache_read_input_token_cost": 0, - "max_input_tokens": 131072, - "max_output_tokens": 131072, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - }, - "tensormesh/openai/gpt-oss-20b": { - "litellm_provider": "tensormesh", - "mode": "chat", - "input_cost_per_token": 7e-08, - "output_cost_per_token": 2.8e-07, - "cache_read_input_token_cost": 0, - "max_input_tokens": 131072, - "max_output_tokens": 131072, - "supports_function_calling": true, - "supports_tool_choice": true, - "supports_response_schema": true, - "supports_prompt_caching": true, - "supports_system_messages": true, - "supports_reasoning": true, - "source": "https://serverless.tensormesh.ai/v1/models/openrouter" - } - , "deepseek-v4-flash": { "cache_creation_input_token_cost": 0.0, "cache_read_input_token_cost": 2.8e-09, @@ -43536,12 +45125,64 @@ "supports_tool_choice": true, "supports_vision": false }, + "tencent/deepseek-v4-pro": { + "cache_creation_input_token_cost": 0.0, + "cache_read_input_token_cost": 3.625e-09, + "input_cost_per_token": 4.35e-07, + "input_cost_per_token_cache_hit": 3.625e-09, + "litellm_provider": "tencent", + "max_input_tokens": 1000000, + "max_output_tokens": 384000, + "max_tokens": 384000, + "mode": "chat", + "output_cost_per_token": 8.7e-07, + "source": "https://www.tencentcloud.com/products/tokenhub", + "supported_endpoints": [ + "/v1/chat/completions" + ], + "supports_assistant_prefill": true, + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_reasoning": true, + "supports_vision": false + }, + "tencent/deepseek-v4-flash": { + "cache_creation_input_token_cost": 0.0, + "cache_read_input_token_cost": 2.8e-09, + "input_cost_per_token": 1.4e-07, + "input_cost_per_token_cache_hit": 2.8e-09, + "litellm_provider": "tencent", + "max_input_tokens": 1000000, + "max_output_tokens": 384000, + "max_tokens": 384000, + "mode": "chat", + "output_cost_per_token": 2.8e-07, + "source": "https://www.tencentcloud.com/products/tokenhub", + "supported_endpoints": [ + "/v1/chat/completions" + ], + "supports_assistant_prefill": true, + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_reasoning": true, + "supports_vision": false + }, "pinstripes/ps/glm-4.5-air": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, - "input_cost_per_token": 0.000000125, - "output_cost_per_token": 0.00000045, + "input_cost_per_token": 1.25e-07, + "output_cost_per_token": 4.5e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43553,8 +45194,8 @@ "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, - "input_cost_per_token": 0.00000014, - "output_cost_per_token": 0.00000045, + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 4.5e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43566,8 +45207,8 @@ "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, - "input_cost_per_token": 0.00000009, - "output_cost_per_token": 0.0000002, + "input_cost_per_token": 9e-08, + "output_cost_per_token": 2e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43579,8 +45220,8 @@ "max_tokens": 131072, "max_input_tokens": 131072, "max_output_tokens": 131072, - "input_cost_per_token": 0.0000003, - "output_cost_per_token": 0.0000006, + "input_cost_per_token": 3e-07, + "output_cost_per_token": 6e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43592,8 +45233,8 @@ "max_tokens": 163840, "max_input_tokens": 163840, "max_output_tokens": 163840, - "input_cost_per_token": 0.0000001, - "output_cost_per_token": 0.0000002, + "input_cost_per_token": 1e-07, + "output_cost_per_token": 2e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43605,8 +45246,8 @@ "max_tokens": 1000192, "max_input_tokens": 1000192, "max_output_tokens": 1000192, - "input_cost_per_token": 0.000000255, - "output_cost_per_token": 0.00000055, + "input_cost_per_token": 2.55e-07, + "output_cost_per_token": 5.5e-07, "litellm_provider": "pinstripes", "mode": "chat", "supports_function_calling": true, @@ -43648,39 +45289,61 @@ "supports_system_messages": true, "supports_tool_choice": true }, - "fallback_generalizations": { - "rules": [ - { - "name": "anthropic-claude-adaptive-thinking", - "pattern": "(?:opus|sonnet|haiku)[-._](?:4[-._](?:[6-9]|[1-9]\\d)(?!\\d)|(?:[5-9]|[1-9]\\d{1,})[-._]\\d{1,2}(?!\\d))", - "description": "Claude opus/sonnet/haiku at version 4.6 or higher: 4.6 through 4.99, then any 5.x, 6.x or later major. The minor is capped at two digits so an 8-digit date suffix such as claude-opus-4-20250514 is never read as a >= 4.6 minor. Turns on adaptive thinking for new families with no code change.", - "extends": "anthropic-claude", - "model_info": { - "supports_adaptive_thinking": true - } - }, - { - "name": "anthropic-claude", - "pattern": "^claude-[a-z]+-\\d+[-.]\\d+(?:-\\d{8})?$", - "description": "Any Claude family-major-minor id, optionally with an 8-digit date suffix, anchored to the whole name. Version-neutral fallback that gives an unmapped Claude provider routing and baseline capabilities; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number.", - "model_info": { - "litellm_provider": "anthropic", - "mode": "chat", - "max_input_tokens": 200000, - "max_output_tokens": 64000, - "max_tokens": 64000, - "supports_function_calling": true, - "supports_parallel_function_calling": true, - "supports_vision": true, - "supports_tool_choice": true, - "supports_assistant_prefill": true, - "supports_prompt_caching": true, - "supports_response_schema": true, - "supports_reasoning": true, - "supports_pdf_input": true, - "supports_system_messages": true - } - } - ] - } + "fallback_generalizations": { + "rules": [ + { + "name": "bedrock-claude-ids", + "pattern": "^(?:[a-z-]+\\.)?anthropic\\.claude-", + "description": "A Bedrock-syntax Claude id, for every version: anthropic.claude- at the start of the name, optionally behind a single dotted geo segment (us./eu./au./jp./apac./global./us-gov.). Anchored to the start because routing rules see the raw request string and provider inference feeds the proxy's provider/* wildcard access checks: an id under an unrecognized namespace such as bedrockz/anthropic.claude-... must stay unroutable rather than resolve to bedrock and slip through a bedrock/* key. Routes to bedrock before the bare-id Anthropic rule is consulted.", + "model_info": { + "litellm_provider": "bedrock" + } + }, + { + "name": "anthropic-claude-ids", + "pattern": "^claude-[a-z]+-\\d+(?:[-.]\\d+)?(?:-\\d{8})?$", + "description": "A bare Claude family-major id with an optional minor and an optional 8-digit date suffix, anchored to the whole name, so claude-newfamily-5 routes like claude-newfamily-5-1 does. Routes an unmapped Claude id that carries no provider namespace to the Anthropic API.", + "model_info": { + "litellm_provider": "anthropic" + } + }, + { + "name": "claude-family-baseline", + "pattern": "claude-[a-z]+-\\d+(?:[-.]\\d+)?(?:-\\d{8})?", + "description": "Any Claude family-major id with an optional minor and an optional 8-digit date suffix, under any provider namespace (bare, bedrock-dotted, vertex, databricks, ...), so bare majors like claude-newfamily-5 get the same baseline as claude-newfamily-5-1. Carries the model-family facts every Claude shares; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number.", + "model_info": { + "mode": "chat", + "max_input_tokens": 200000, + "max_output_tokens": 64000, + "max_tokens": 64000, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_vision": true, + "supports_tool_choice": true, + "supports_assistant_prefill": true, + "supports_prompt_caching": true, + "supports_response_schema": true, + "supports_reasoning": true, + "supports_pdf_input": true, + "supports_system_messages": true + } + }, + { + "name": "claude-adaptive-thinking", + "pattern": "claude-[a-z]+-(?:4[-._](?:[6-9]|[1-9]\\d)(?!\\d)|(?:[5-9]|[1-9]\\d)(?!\\d)(?:[-._]\\d{1,2}(?!\\d))?)", + "description": "Claude at version 4.6 or higher, in any id shape that contains claude--: minors 4.6 through 4.99, any later major-minor, and bare 5+ majors so a new family shaped like claude-fable-5 matches. Requiring the claude- prefix keeps non-Claude names such as team-sonnet-5-1 out. The minor is capped at two digits so an 8-digit date suffix such as claude-opus-4-20250514 is never read as a >= 4.6 minor. Turns on adaptive thinking for new versions and new families with no code change.", + "model_info": { + "supports_adaptive_thinking": true + } + }, + { + "name": "claude-mid-conversation-system", + "pattern": "claude-[a-z]+-(?:4[-._](?:[89]|[1-9]\\d)(?!\\d)|(?:[5-9]|[1-9]\\d)(?!\\d)(?:[-._]\\d{1,2}(?!\\d))?)", + "description": "Claude at version 4.8 or higher, in any id shape that contains claude--: minors 4.8 through 4.99, any later major-minor, and bare 5+ majors so a new family like claude-fable-5 matches. Anthropic introduced mid-conversation system messages with Opus 4.8 and every newer Claude keeps them; 4.7 and below reject the system role inside messages.", + "model_info": { + "supports_mid_conversation_system": true + } + } + ] + } } diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index b137ec59a1f..65db63dc045 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -1059,6 +1059,16 @@ "interactions": true } }, + "gdc": { + "display_name": "Google Distributed Cloud (GDC)", + "url": "https://docs.litellm.ai/docs/providers/gdc", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": false + } + }, "github_copilot": { "display_name": "GitHub Copilot (`github_copilot`)", "url": "https://docs.litellm.ai/docs/providers/github_copilot", @@ -1974,6 +1984,23 @@ "interactions": true } }, + "meta": { + "display_name": "Meta Model API (`meta`)", + "url": "https://docs.litellm.ai/docs/providers/meta", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, "pinstripes": { "display_name": "Pinstripes (`pinstripes`)", "url": "https://docs.litellm.ai/docs/providers/pinstripes", @@ -2285,6 +2312,24 @@ "text_completion": true } }, + "tencent": { + "display_name": "Tencent TokenHub (`tencent`)", + "url": "https://docs.litellm.ai/docs/providers/tencent", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false, + "text_completion": false + } + }, "text-completion-codestral": { "display_name": "Text Completion Codestral (`text-completion-codestral`)", "url": "https://docs.litellm.ai/docs/providers/codestral", diff --git a/proxy_server_config.yaml b/proxy_server_config.yaml index 6feffe036bd..1a51dd0d0a7 100644 --- a/proxy_server_config.yaml +++ b/proxy_server_config.yaml @@ -189,7 +189,7 @@ litellm_settings: langfuse_host: https://us.cloud.langfuse.com # cache: true # [OPTIONAL] use for caching responses # enable_caching_on_provider_specific_optional_params: True # Include provider-specific params in cache keys - # cache_params: # And for shared health check + # cache_params: # type: redis # host: localhost # port: 6379 @@ -228,8 +228,11 @@ general_settings: proxy_batch_write_at: 1 database_connection_pool_limit: 10 # background_health_checks: true - # use_shared_health_check: true + # use_shared_health_check: true # needs a coordination Redis (below) # health_check_interval: 30 + # coordination_redis: # standalone Redis for cross-pod coordination: rate limits, spend tracking, pod locks, shared health checks + # host: localhost + # port: 6379 # cancel_on_disconnect: true # cancel the in-flight upstream LLM request (non-streaming) when the client disconnects, freeing backend capacity (e.g. a vLLM GPU slot) # database_url: "postgresql://:@:/" # [OPTIONAL] use for token-based auth to proxy diff --git a/pyproject.toml b/pyproject.toml index e4e840e4303..dc8092a4c5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "litellm" -version = "1.91.0" +version = "1.93.0" description = "Library to easily interface with LLM API providers" readme = "README.md" requires-python = ">=3.10, <3.14" @@ -62,8 +62,8 @@ proxy = [ "azure-identity>=1.25.2,<2.0", "azure-storage-blob>=12.28.0,<13.0", "mcp>=1.26.0,<2.0", - "litellm-proxy-extras==0.4.74", - "litellm-enterprise==0.1.44", + "litellm-proxy-extras==0.4.76", + "litellm-enterprise==0.1.49", "RestrictedPython>=8.1,<9.0", "rich>=13.9.4,<14.0", "polars>=1.38.1,<2.0", @@ -184,6 +184,10 @@ dev = [ "vcrpy==8.2.1", "pytest-recording==0.13.4", ] +e2e-dev = [ + "playwright==1.61.0", + "websockets>=15.0.1,<16.0", +] proxy-dev = [ "prisma==0.11.0", "hypercorn==0.17.3", @@ -234,14 +238,32 @@ healthcheck = [ ] [build-system] -requires = ["uv_build==0.11.8"] -build-backend = "uv_build" +requires = ["maturin==1.9.4"] +build-backend = "maturin" + +[tool.maturin] +manifest-path = "litellm-rust/crates/python-bridge/Cargo.toml" +module-name = "litellm.rust_bridge._native" +python-source = "." +bindings = "pyo3" +include = ["litellm/proxy/_experimental/out/**"] +exclude = [ + "litellm/proxy/enterprise", + "litellm/proxy/enterprise/**", + "**/__pycache__", + "**/__pycache__/**", + "**/.pytest_cache", + "**/.pytest_cache/**", + "**/.ruff_cache", + "**/.ruff_cache/**", +] [tool.uv] constraint-dependencies = [ "tornado>=6.5.6", "aiohttp>=3.14.1,<4.0", "packaging>=24.0", + "soupsieve>=2.8.4", ] override-dependencies = [ # a2a-sdk 1.x requires packaging>=24.0; lunary 1.4.x still caps at <24.0. @@ -258,23 +280,11 @@ litellm-enterprise = { workspace = true } [tool.uv.workspace] members = ["enterprise", "litellm-proxy-extras"] -[tool.uv.build-backend] -module-root = "" -source-exclude = [ - "litellm/proxy/enterprise", - "**/__pycache__", - "**/__pycache__/**", - "**/.pytest_cache", - "**/.pytest_cache/**", - "**/.ruff_cache", - "**/.ruff_cache/**", -] - [tool.isort] profile = "black" [tool.commitizen] -version = "1.91.0" +version = "1.93.0" version_files = [ "pyproject.toml:^version", ] diff --git a/qa_sticky_session.sh b/qa_sticky_session.sh new file mode 100755 index 00000000000..326bb8117c7 --- /dev/null +++ b/qa_sticky_session.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# QA: code interpreter sandbox stickiness via metadata.session_id +# bash qa_sticky_session.sh +# LITELLM_BASE_URL=http://localhost:4000 LITELLM_KEY=sk-1234 bash qa_sticky_session.sh + +set -euo pipefail + +BASE="${LITELLM_BASE_URL:-http://localhost:4000}" +KEY="${LITELLM_KEY:-sk-1234}" +MODEL="${LITELLM_MODEL:-gpt-4o-mini}" +# proxy running at http://localhost:4000 (master key: sk-1234) +SESSION_A="qa-session-$(date +%s)-A" +SESSION_B="qa-session-$(date +%s)-B" + +content() { + echo "$1" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('choices',[{}])[0].get('message',{}).get('content',''))" +} + +call() { + local session="${1:-}" code="$2" meta="" + [[ -n "$session" ]] && meta=", \"metadata\": {\"session_id\": \"$session\"}" + curl -s -X POST "$BASE/chat/completions" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $KEY" \ + -d "{\"model\":\"$MODEL\"$meta,\"tools\":[{\"type\":\"code_interpreter\"}],\"messages\":[{\"role\":\"user\",\"content\":\"Run this Python code and tell me the result: $code\"}]}" +} + +assert_match() { + local label="$1" body="$2" pattern="$3" + if echo "$body" | grep -qiE "$pattern"; then + echo "PASS $label" + else + echo "FAIL $label (expected /$pattern/)" + echo " $(content "$body")" + exit 1 + fi +} + +echo "=== Sticky Session Sandbox QA ===" +echo "base: $BASE session A: $SESSION_A session B: $SESSION_B" +echo + +R=$(call "$SESSION_A" "x = 42; print(x)") +assert_match "same session_id reuses sandbox (set x=42)" "$R" "42" + +R=$(call "$SESSION_A" "print(x)") +assert_match "same session_id keeps state (x still 42)" "$R" "42" + +R=$(call "$SESSION_B" "print(x)") +assert_match "different session_id is isolated" "$R" "not defined|NameError|undefined|error" + +R=$(call "" "y = 99; print(y)") +assert_match "no session_id runs code" "$R" "99" + +R=$(call "" "print(y)") +assert_match "no session_id gets fresh sandbox each request" "$R" "not defined|NameError|undefined|error" + +echo +echo "All checks passed." diff --git a/ruff-strict-budget.json b/ruff-strict-budget.json index 10c820324ea..dcde6fd1641 100644 --- a/ruff-strict-budget.json +++ b/ruff-strict-budget.json @@ -1,490 +1,368 @@ { "ANN001": { - "baseline": 2865, - "slack": 287 + "limit": 3152 }, "ANN002": { - "baseline": 64, - "slack": 5 + "limit": 69 }, "ANN003": { - "baseline": 759, - "slack": 76 + "limit": 835 }, "ANN201": { - "baseline": 1944, - "slack": 194 + "limit": 2138 }, "ANN202": { - "baseline": 858, - "slack": 86 + "limit": 944 }, "ANN204": { - "baseline": 658, - "slack": 66 + "limit": 724 }, "ANN205": { - "baseline": 117, - "slack": 10 + "limit": 127 }, "ANN206": { - "baseline": 120, - "slack": 10 + "limit": 130 }, "ANN401": { - "baseline": 1886, - "slack": 189 + "limit": 2075 }, "ASYNC230": { - "baseline": 11, - "slack": 3 + "limit": 14 }, "B004": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "B006": { - "baseline": 180, - "slack": 10 + "limit": 190 }, "B008": { - "baseline": 490, - "slack": 15 + "limit": 505 }, "B009": { - "baseline": 79, - "slack": 5 + "limit": 84 }, "B010": { - "baseline": 187, - "slack": 10 + "limit": 197 }, "B018": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "B019": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "B021": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "B026": { - "baseline": 3, - "slack": 3 + "limit": 6 }, "B033": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "BLE001": { - "baseline": 2854, - "slack": 50 + "limit": 2903 }, "C401": { - "baseline": 8, - "slack": 3 + "limit": 11 }, "C404": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "C405": { - "baseline": 20, - "slack": 3 + "limit": 23 }, "C408": { - "baseline": 11, - "slack": 3 + "limit": 14 }, "C414": { - "baseline": 4, - "slack": 3 + "limit": 7 }, "C419": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "C901": { - "baseline": 301, - "slack": 15 + "limit": 316 }, "D419": { - "baseline": 6, - "slack": 3 + "limit": 9 }, "DTZ001": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "DTZ003": { - "baseline": 30, - "slack": 3 + "limit": 33 }, "DTZ005": { - "baseline": 229, - "slack": 15 + "limit": 244 }, "DTZ006": { - "baseline": 10, - "slack": 3 + "limit": 13 }, "DTZ007": { - "baseline": 20, - "slack": 3 + "limit": 23 }, "DTZ011": { - "baseline": 3, - "slack": 3 + "limit": 6 }, "EXE001": { - "baseline": 4, - "slack": 3 + "limit": 7 }, "EXE002": { - "baseline": 3, - "slack": 3 + "limit": 6 }, "F401": { - "baseline": 20, - "slack": 3 + "limit": 23 }, "FURB136": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "FURB168": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "FURB188": { - "baseline": 49, - "slack": 3 + "limit": 52 }, "I001": { - "baseline": 258, - "slack": 15 + "limit": 273 }, "LOG015": { - "baseline": 5, - "slack": 3 + "limit": 8 }, "N999": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "PERF102": { - "baseline": 27, - "slack": 3 + "limit": 30 }, "PERF401": { - "baseline": 136, - "slack": 10 + "limit": 146 }, "PERF402": { - "baseline": 6, - "slack": 3 + "limit": 9 }, "PERF403": { - "baseline": 69, - "slack": 5 + "limit": 74 }, "PIE790": { - "baseline": 263, - "slack": 15 + "limit": 278 }, "PIE800": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "PIE804": { - "baseline": 21, - "slack": 3 + "limit": 24 }, "PIE810": { - "baseline": 41, - "slack": 3 + "limit": 44 }, "PLC0206": { - "baseline": 28, - "slack": 3 + "limit": 31 }, "PLC0208": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "PLC0414": { - "baseline": 35, - "slack": 3 + "limit": 38 }, "PLR0124": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "PLR0206": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "PLR0402": { - "baseline": 6, - "slack": 3 + "limit": 9 }, "PLR1704": { - "baseline": 3, - "slack": 3 + "limit": 6 }, "PLR1711": { - "baseline": 31, - "slack": 3 + "limit": 34 }, "PLR1714": { - "baseline": 252, - "slack": 15 + "limit": 265 }, "PLR1730": { - "baseline": 7, - "slack": 3 + "limit": 10 }, "PLR2044": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "PLW0127": { - "baseline": 41, - "slack": 3 + "limit": 44 }, "PLW0133": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "PLW0602": { - "baseline": 215, - "slack": 15 + "limit": 230 }, "PLW0603": { - "baseline": 183, - "slack": 10 + "limit": 193 }, "PLW1508": { - "baseline": 188, - "slack": 10 + "limit": 198 }, "PLW1510": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "PYI030": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "PYI036": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "PYI041": { - "baseline": 9, - "slack": 3 + "limit": 12 }, "PYI064": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "RET501": { - "baseline": 35, - "slack": 3 + "limit": 38 }, "RET504": { - "baseline": 702, - "slack": 20 + "limit": 721 }, "RUF010": { - "baseline": 844, - "slack": 30 + "limit": 874 }, "RUF012": { - "baseline": 158, - "slack": 10 + "limit": 168 }, "RUF015": { - "baseline": 8, - "slack": 3 + "limit": 11 }, "RUF019": { - "baseline": 38, - "slack": 3 + "limit": 41 }, "RUF022": { - "baseline": 80, - "slack": 5 + "limit": 85 }, "RUF023": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "RUF046": { - "baseline": 5, - "slack": 3 + "limit": 8 }, "RUF051": { - "baseline": 3, - "slack": 3 + "limit": 6 }, "RUF059": { - "baseline": 69, - "slack": 5 + "limit": 73 }, "RUF100": { - "baseline": 465, - "slack": 15 + "limit": 480 }, "S110": { - "baseline": 222, - "slack": 15 + "limit": 236 }, "S112": { - "baseline": 21, - "slack": 3 + "limit": 24 }, "SIM101": { - "baseline": 58, - "slack": 5 + "limit": 63 }, "SIM102": { - "baseline": 311, - "slack": 15 + "limit": 324 }, "SIM103": { - "baseline": 119, - "slack": 10 + "limit": 129 }, "SIM113": { - "baseline": 3, - "slack": 3 + "limit": 6 }, "SIM114": { - "baseline": 103, - "slack": 10 + "limit": 113 }, "SIM115": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "SIM117": { - "baseline": 7, - "slack": 3 + "limit": 10 }, "SIM118": { - "baseline": 104, - "slack": 10 + "limit": 114 }, "SIM201": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "SIM210": { - "baseline": 9, - "slack": 3 + "limit": 12 }, "SIM211": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "SIM222": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "SIM401": { - "baseline": 9, - "slack": 3 + "limit": 12 }, "TC004": { - "baseline": 5, - "slack": 3 + "limit": 8 }, "TC005": { - "baseline": 6, - "slack": 3 + "limit": 9 }, "TID251": { - "baseline": 2664, - "slack": 50 + "limit": 2701 }, "TRY002": { - "baseline": 528, - "slack": 20 + "limit": 548 }, "TRY004": { - "baseline": 93, - "slack": 5 + "limit": 98 }, "TRY201": { - "baseline": 409, - "slack": 15 + "limit": 424 }, "TRY203": { - "baseline": 113, - "slack": 10 + "limit": 123 }, "TRY300": { - "baseline": 853, - "slack": 30 + "limit": 883 }, "UP006": { - "baseline": 12941, - "slack": 100 + "limit": 12792 }, "UP007": { - "baseline": 2520, - "slack": 50 + "limit": 2570 }, "UP008": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "UP012": { - "baseline": 4, - "slack": 3 + "limit": 7 }, "UP018": { - "baseline": 18, - "slack": 3 + "limit": 21 }, "UP024": { - "baseline": 12, - "slack": 3 + "limit": 15 }, "UP028": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "UP031": { - "baseline": 2, - "slack": 3 + "limit": 5 }, "UP032": { - "baseline": 609, - "slack": 20 + "limit": 629 }, "UP034": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "UP035": { - "baseline": 2250, - "slack": 50 + "limit": 2284 }, "UP036": { - "baseline": 1, - "slack": 3 + "limit": 4 }, "UP037": { - "baseline": 100, - "slack": 5 + "limit": 105 }, "UP045": { - "baseline": 18417, - "slack": 100 + "limit": 18462 } } diff --git a/ruff.toml b/ruff.toml index a09bc663ff1..2ea9d7260fb 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,5 +1,5 @@ -lint.ignore = ["F405", "E402", "E501", "F403"] -lint.extend-select = ["E501", "T20", "PGH004", "RUF008", "RUF009", "RUF100"] +lint.ignore = ["F405", "E402", "F403"] +lint.extend-select = ["T20", "PGH004", "RUF008", "RUF009", "RUF100"] # RUF100 (unused-noqa) only knows the rules enabled in THIS config, so it would strip # `# noqa` directives that protect rules enforced elsewhere. List those codes as external # so RUF100 leaves their directives alone: the strict gate (ruff-strict.toml) and upstream diff --git a/schema.prisma b/schema.prisma index e21c0016491..fb4d8d0b5a3 100644 --- a/schema.prisma +++ b/schema.prisma @@ -279,6 +279,7 @@ model LiteLLM_ObjectPermissionTable { blocked_tools String[] @default([]) // Tool names blocked for any key/team/user with this permission mcp_toolsets String[] @default([]) // Toolset IDs granted to this key/team/user search_tools String[] @default([]) // search_tool_name values this key/team/user may call + mcp_tool_search_enabled Boolean? teams LiteLLM_TeamTable[] projects LiteLLM_ProjectTable[] verification_tokens LiteLLM_VerificationToken[] @@ -328,15 +329,23 @@ model LiteLLM_MCPServerTable { token_url String? registration_url String? oauth2_flow String? + token_exchange_endpoint String? + // Named for the RFC 8693 "audience" token-exchange request parameter (that flow only). + // RFC 8707 resource indicators are a separate concept, named "resource" in the v2 egress types. + audience String? + subject_token_type String? + token_exchange_profile String? allow_all_keys Boolean @default(false) available_on_public_internet Boolean @default(true) delegate_auth_to_upstream Boolean @default(false) oauth_passthrough Boolean @default(false) + dcr_bridge Boolean? is_byok Boolean @default(false) byok_description String[] @default([]) byok_api_key_help_url String? source_url String? timeout Float? + max_concurrent_requests Int? // BYOM submission lifecycle approval_status String? @default("active") submitted_by String? @@ -417,6 +426,7 @@ model LiteLLM_VerificationToken { access_group_ids String[] @default([]) model_spend Json @default("{}") model_max_budget Json @default("{}") + budget_fallbacks Json @default("{}") budget_id String? organization_id String? object_permission_id String? @@ -510,6 +520,7 @@ model LiteLLM_DeletedVerificationToken { access_group_ids String[] @default([]) model_spend Json @default("{}") model_max_budget Json @default("{}") + budget_fallbacks Json @default("{}") router_settings Json? @default("{}") budget_id String? organization_id String? diff --git a/scripts/budget_ratchet_check.py b/scripts/budget_ratchet_check.py index df9815d6557..10a78483643 100644 --- a/scripts/budget_ratchet_check.py +++ b/scripts/budget_ratchet_check.py @@ -1,19 +1,16 @@ #!/usr/bin/env python3 -"""Non-gating ratchet guard: budget baselines and ceilings may only fall, never rise. +"""Non-gating ratchet guard: budget limits may only fall, never rise. Every `*-budget.json` file (ruff-strict, type-discipline, basedpyright-code) is a -one-way ratchet: each rule's ceiling is `baseline + slack`, and both the recorded -`baseline` (the live violation count) and that ceiling are meant to be driven DOWN -over time. This check compares every budget file against its own content at the -merge-base with the target branch and fails (exits 1, red) if: +one-way ratchet: each rule's ceiling is its `limit`, and that limit is meant to be +driven DOWN over time. This check compares every budget file against its own +content at the merge-base with the target branch and fails (exits 1, red) if: - * a rule's ceiling (`baseline + slack`) went up, - * a rule's `baseline` went up, even if `slack` was lowered to keep the ceiling - flat (a higher baseline bakes in more accepted debt and must be acknowledged), + * a rule's `limit` went up, * a rule was dropped from a budget (its ceiling effectively became infinite), or * an entire budget file was deleted. -New rules and lowered/equal baselines and ceilings are fine. +New rules and lowered/equal limits are fine. This is deliberately NOT a gating check. It should turn the run red so that a loosening is impossible to miss in review, but it must stay OUT of the @@ -89,19 +86,21 @@ def _load_base(rel: str, ref: str) -> dict | None: return json.loads(proc.stdout) -def _baselines(budget: dict) -> dict[str, int]: - """Map each rule to its recorded baseline; skip malformed specs.""" - return { - rule: int(spec.get("baseline", 0)) - for rule, spec in budget.items() - if isinstance(spec, dict) - } +def _ceiling(spec: dict) -> int: + """A rule's ceiling: its `limit`, or legacy `baseline + slack`. + + The base side of the diff can predate the `limit` migration, so a spec is read + under either schema and the two are compared on the same footing. + """ + if "limit" in spec: + return int(spec["limit"]) + return int(spec.get("baseline", 0)) + int(spec.get("slack", 0)) -def _caps(budget: dict) -> dict[str, int]: - """Map each rule to its ceiling (baseline + slack); skip malformed specs.""" +def _limits(budget: dict) -> dict[str, int]: + """Map each rule to its ceiling; skip malformed specs.""" return { - rule: int(spec.get("baseline", 0)) + int(spec.get("slack", 0)) + rule: _ceiling(spec) for rule, spec in budget.items() if isinstance(spec, dict) } @@ -109,54 +108,32 @@ def _caps(budget: dict) -> dict[str, int]: def _regression_detail( rule: str, - base_caps: dict[str, int], - head_caps: dict[str, int], - base_baselines: dict[str, int], - head_baselines: dict[str, int], + base_limits: dict[str, int], + head_limits: dict[str, int], ) -> str | None: """Why `rule` regressed vs base, or None when it held flat or fell. - A dropped rule is terminal; otherwise a raised ceiling and a raised baseline are - independent loosenings (the latter catches a baseline bump masked by a slack cut), - so both reasons are reported when both apply. + A dropped rule is terminal; otherwise the only loosening left is a raised limit. """ - base_cap = base_caps[rule] - if rule not in head_caps: - return f"rule dropped (ceiling {base_cap} -> removed)" - reasons = tuple( - message - for raised, message in ( - ( - head_caps[rule] > base_cap, - f"ceiling raised {base_cap} -> {head_caps[rule]}", - ), - ( - head_baselines[rule] > base_baselines[rule], - f"baseline raised {base_baselines[rule]} -> {head_baselines[rule]}", - ), - ) - if raised - ) - return "; ".join(reasons) or None + base_limit = base_limits[rule] + if rule not in head_limits: + return f"rule dropped (limit {base_limit} -> removed)" + if head_limits[rule] > base_limit: + return f"limit raised {base_limit} -> {head_limits[rule]}" + return None def regressions_for(rel: str, base: dict | None, head: dict | None) -> list[Regression]: if base is None: return [] # new budget file: nothing to ratchet against yet if head is None: - return [Regression(rel, "*", "budget file was deleted (every ceiling removed)")] + return [Regression(rel, "*", "budget file was deleted (every limit removed)")] - base_caps, head_caps = _caps(base), _caps(head) - base_baselines, head_baselines = _baselines(base), _baselines(head) + base_limits, head_limits = _limits(base), _limits(head) return [ Regression(rel, rule, detail) - for rule in sorted(base_caps) - if ( - detail := _regression_detail( - rule, base_caps, head_caps, base_baselines, head_baselines - ) - ) - is not None + for rule in sorted(base_limits) + if (detail := _regression_detail(rule, base_limits, head_limits)) is not None ] @@ -191,7 +168,7 @@ def main() -> int: if regressions: print( - f"FAIL: budget baseline(s)/ceiling(s) loosened vs base {args.base} (merge-base {ref[:12]}):" + f"FAIL: budget limit(s) loosened vs base {args.base} (merge-base {ref[:12]}):" ) for reg in regressions: print(f" {reg.budget} {reg.rule}: {reg.detail}") @@ -203,7 +180,7 @@ def main() -> int: return 1 suffix = f" ({', '.join(checked)})" if checked else "" - print(f"OK: no budget ceiling increased vs base {args.base}{suffix}") + print(f"OK: no budget limit increased vs base {args.base}{suffix}") return 0 diff --git a/scripts/check_type_discipline.py b/scripts/check_type_discipline.py index 6e152541863..809dc141eb8 100644 --- a/scripts/check_type_discipline.py +++ b/scripts/check_type_discipline.py @@ -23,7 +23,7 @@ LIT002 Mutable-collection *construction*: a list/dict/set literal or comprehens str]`) are exempt. Suppress with `# mutable-ok: `. LIT003 noqa suppression without rule codes or without a reason. Required shape: `# noqa: TID251 # ` -LIT004 type/pyright/mypy ignore without bracketed codes or without a reason. +LIT004 pyright/mypy ignore without bracketed codes or without a reason. Required shape: `# pyright: ignore[reportArgumentType] # ` LIT005 A `# mutable-ok` / `# cast-ok` / `# guard-ok` / `# kwargs-ok` suppression without a reason. @@ -38,6 +38,10 @@ LIT008 `**kwargs` parameter. The keyword contract is erased and everything it c is effectively Any. ruff can force it to be typed (ANN003) but can't ban the syntax. Declare explicit keyword params, or accept one frozen payload. `*args`, by contrast, is fine when typed (it's just a tuple). Suppress: `# kwargs-ok: `. +LIT009 `# type: ignore` in any shape (bare, with codes, with a reason). + pyrightconfig.json sets enableTypeIgnoreComments to false, so basedpyright + never honors it: the comment is inert dead syntax that suppresses nothing. + Use `# pyright: ignore[ruleName] # ` instead. LIT000 Setup failure: a target file could not be read, or contains a syntax error. Reported as a violation rather than crashing the run. @@ -95,8 +99,9 @@ NOQA_RE = re.compile( r"(?P.*)", re.IGNORECASE, ) +TYPE_IGNORE_RE = re.compile(r"#\s*type:\s*ignore\b") IGNORE_RE = re.compile( - r"#\s*(?:type|pyright|mypy):\s*ignore(?P\[[^\]]*\])?(?P.*)" + r"#\s*(?:pyright|mypy):\s*ignore(?P\[[^\]]*\])?(?P.*)" ) MUTABLE_OK_RE = re.compile(r"#\s*mutable-ok(?::\s*(?P.*))?") CAST_OK_RE = re.compile(r"#\s*cast-ok(?::\s*(?P.*))?") @@ -161,6 +166,11 @@ def _comment_violations(path: Path, line_no: int, text: str) -> Iterator[Violati elif len(_reason_of(m.group("rest"))) < MIN_REASON_LEN: yield Violation(path, line_no, "LIT003", "noqa requires a reason: `# noqa: XXX123 # `") + if TYPE_IGNORE_RE.search(text): + yield Violation(path, line_no, "LIT009", + "`# type: ignore` is inert (enableTypeIgnoreComments is false, so " + "basedpyright never honors it); use `# pyright: ignore[ruleName] # `") + m = IGNORE_RE.search(text) if m: codes = m.group("codes") diff --git a/scripts/install_git_hooks.sh b/scripts/install_git_hooks.sh index 1e4e3c6de19..7ea8c3ff2e9 100755 --- a/scripts/install_git_hooks.sh +++ b/scripts/install_git_hooks.sh @@ -34,5 +34,8 @@ cat < `make lint` (test-linting.yml's lint job) +# - tests/e2e Python staged -> `make lint-e2e-basedpyright` (test-linting.yml's e2e type-check step) +# - dashboard staged -> prettier + eslint + lint budgets (test-litellm-ui-build.yml's frontend-lint) +# - proxy/types staged -> regenerate dashboard API types and fail on drift (check-ui-api-types.yml) +# +# Each block is skipped when no matching files are staged, so unrelated commits stay +# fast. This is intentionally not auto-installed as a git hook (see scripts/install_git_hooks.sh): +# the dashboard and basedpyright passes can take minutes, so it's run on demand rather +# than firing on every human commit. It is hook-compatible if you want that anyway: +# `ln -s ../../scripts/pre_commit_lint.sh .git/hooks/pre-commit`. + +set -eu + +repo_root=$(git rev-parse --show-toplevel) +cd "$repo_root" + +staged=$(git diff --cached --name-only --diff-filter=ACMR) +staged_match() { printf '%s\n' "$staged" | grep -E "$1" || true; } + +# CI's lint job (test-linting.yml) only inspects litellm/, so a tests-only or +# scripts-only commit can't turn it red; scope the trigger there to skip the slow +# make lint when it couldn't catch anything. +litellm_py_files=$(staged_match '^litellm/.*\.py$') +e2e_py_files=$(staged_match '^tests/e2e/.*\.py$') +# ruff format (and CI's format step) skip enterprise; the rest of make lint covers it. +fmt_files=$(printf '%s\n' "$litellm_py_files" | grep -v '^litellm/enterprise/' || true) +# check-ui-api-types.yml triggers on any file under litellm/proxy or litellm/types +# (Prisma schema and configs included, not just Python) plus the generator and its +# lockfiles, so match that whole trigger set rather than a Python subset. +spec_files=$(staged_match '^(litellm/(proxy|types)/.*|ui/litellm-dashboard/(scripts/gen-api-types\.mjs|package\.json|package-lock\.json|src/lib/http/schema\.d\.ts))$') +# CI's frontend-lint runs prettier over a wider extension set than eslint; keep that +# split so this flags exactly what the job would. +ui_prettier_files=$(staged_match '^ui/litellm-dashboard/.*\.(js|jsx|ts|tsx|mjs|cjs|json|css|scss|md|mdx|yml|yaml|html)$') +ui_eslint_files=$(staged_match '^ui/litellm-dashboard/.*\.(js|jsx|ts|tsx|mjs|cjs)$') + +# CI lints the committed tree, so this script predicts CI for what you have STAGED +# (every trigger above reads `git diff --cached`). The tools it runs, though, read +# the working tree, so unstaged edits to tracked files and untracked files fold +# into the result and a green/red here won't match a commit of just the staged +# changes. There's no safe way to lint the index in place, so surface the gap +# instead of hiding it: stage everything you intend to commit before trusting a +# pass. This only warns; it never blocks or touches your changes. +unstaged=$(git diff --name-only) +untracked=$(git ls-files --others --exclude-standard) +if [ -n "$unstaged" ] || [ -n "$untracked" ]; then + echo "pre-commit: NOTE - unstaged/untracked changes are included in these checks but" >&2 + echo " won't be in a commit of only your staged changes, so this result may differ from" >&2 + echo " CI. Stage everything you intend to commit (git add) for an accurate prediction:" >&2 + printf '%s\n' "$unstaged" "$untracked" | sed '/^$/d' | sed 's/^/ /' >&2 +fi + +lint_dashboard() { + ( + rc=0 + prettier_rel=() + eslint_rel=() + while IFS= read -r f; do + [ -n "$f" ] && prettier_rel+=("${f#ui/litellm-dashboard/}") + done <&2 + echo " Fix: make bootstrap" >&2 +} + +if [ -n "$litellm_py_files" ]; then + echo "pre-commit: linting Python (make lint)" + make lint || { echo "✗ Python lint failed. Fix the reds above, then re-run make pre-commit." >&2; status=1; } + # `make lint` format-checks files in origin/base...HEAD, which at pre-commit time + # predates the staged change, so format-check the staged litellm files directly to + # cover a brand-new commit before it lands. + if [ -n "$fmt_files" ]; then + echo "pre-commit: ruff format --check (staged litellm files)" + printf '%s\n' "$fmt_files" | xargs uv run --no-sync ruff format --check --exclude '/enterprise/' \ + || { echo "✗ Unformatted staged files. Fix with: make format, then re-stage." >&2; status=1; } + fi +fi + +if [ -n "$e2e_py_files" ] && [ -z "$litellm_py_files" ]; then + echo "pre-commit: type-checking tests/e2e (make lint-e2e-basedpyright)" + make lint-e2e-basedpyright || { echo "✗ tests/e2e basedpyright failed. Fix the errors above, then re-run make pre-commit." >&2; status=1; } +fi + +if [ -n "$ui_prettier_files" ] || [ -n "$ui_eslint_files" ]; then + echo "pre-commit: linting dashboard (prettier + eslint + lint budgets)" + if [ ! -d ui/litellm-dashboard/node_modules ]; then + echo "✗ ui/litellm-dashboard/node_modules is missing; dashboard lint cannot run." >&2 + bootstrap_hint + status=1 + else + lint_dashboard || { echo "✗ Dashboard lint failed. See above; format with: (cd ui/litellm-dashboard && npm run format)." >&2; status=1; } + fi +fi + +if [ -n "$spec_files" ]; then + echo "pre-commit: checking dashboard API types are in sync (npm run gen:api)" + # gen-api-types.mjs imports litellm.proxy.proxy_server, which needs the proxy deps + # and an up-to-date Prisma client; check-ui-api-types.yml installs those and runs + # prisma generate before gen:api, so mirror that here or a stale client can mask + # drift that CI will still flag. + if [ ! -d ui/litellm-dashboard/node_modules ]; then + echo "✗ ui/litellm-dashboard/node_modules is missing; the gen:api sync check cannot run." >&2 + bootstrap_hint + status=1 + elif ! uv run --no-sync python -c "import orjson, prisma" 2>/dev/null; then + echo "✗ The Python env lacks the proxy deps (orjson/prisma) that gen:api needs." >&2 + bootstrap_hint + status=1 + elif ! uv run --no-sync python scripts/prisma_generate_if_needed.py; then + echo "✗ Could not regenerate Prisma client (prisma generate failed)." >&2 + status=1 + elif ( cd ui/litellm-dashboard && LITELLM_PYTHON="uv run --no-sync python" npm run gen:api ); then + if ! git diff --quiet -- ui/litellm-dashboard/src/lib/http/schema.d.ts; then + echo "✗ Dashboard API types are stale; regenerated src/lib/http/schema.d.ts. Stage it and re-run make pre-commit." >&2 + status=1 + fi + else + echo "✗ Could not regenerate API types (npm run gen:api failed)." >&2 + status=1 + fi +fi + +exit $status diff --git a/scripts/prisma_generate_if_needed.py b/scripts/prisma_generate_if_needed.py new file mode 100644 index 00000000000..d2c40adf820 --- /dev/null +++ b/scripts/prisma_generate_if_needed.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Run ``prisma generate`` only when its inputs changed since the last run. + +The generated client is a pure function of ``litellm/proxy/schema.prisma`` and +the installed prisma package version, so a stamp of those two written next to +the venv is enough to prove the client is current. The stamp lives under +``sys.prefix`` so recreating the venv discards it, and a missing generated +client (a fresh or reinstalled prisma package) forces a regenerate even when +the stamp matches. The prisma package itself is never imported here: once +generated it re-exports the whole client on import, which costs more than the +generate this script exists to skip. +""" + +import hashlib +import importlib.metadata +import importlib.util +import subprocess +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +SCHEMA = REPO_ROOT / "litellm" / "proxy" / "schema.prisma" +STAMP = Path(sys.prefix) / "litellm-prisma-schema.stamp" + + +def stamp_value(schema_bytes: bytes, prisma_version: str) -> str: + return f"{hashlib.sha256(schema_bytes).hexdigest()}:{prisma_version}" + + +def should_skip(stamp: Path, expected: str, client_generated: bool) -> bool: + if not client_generated: + return False + try: + return stamp.read_text() == expected + except OSError: + return False + + +def client_is_generated() -> bool: + spec = importlib.util.find_spec("prisma") + if spec is None or not spec.submodule_search_locations: + return False + return any( + (Path(location) / "client.py").exists() + for location in spec.submodule_search_locations + ) + + +def main() -> int: + version = importlib.metadata.version("prisma") + expected = stamp_value(SCHEMA.read_bytes(), version) + if should_skip(STAMP, expected, client_is_generated()): + print( + f"Prisma client already generated for {SCHEMA.relative_to(REPO_ROOT)} " + f"(prisma {version}); skipping prisma generate" + ) + return 0 + result = subprocess.run( + [sys.executable, "-m", "prisma", "generate", "--schema", str(SCHEMA)], + cwd=REPO_ROOT, + ) + if result.returncode != 0: + return result.returncode + STAMP.write_text(expected) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/ruff_strict_gate.py b/scripts/ruff_strict_gate.py index 5951a1215ed..25f6c4d29ba 100644 --- a/scripts/ruff_strict_gate.py +++ b/scripts/ruff_strict_gate.py @@ -1,10 +1,12 @@ #!/usr/bin/env python3 """Total-count gate for the strict ruff rules in ruff-strict.toml. -Each rule has a hard ceiling (baseline + slack) in ruff-strict-budget.json. The -gate counts each rule across the whole tree and fails when a rule is both over -its ceiling and higher than the base it merges into, so a change is blamed for -the violations it adds, never for drift that already exists in the base. +Each rule has a hard ``limit`` in ruff-strict-budget.json. The gate counts each +rule across the whole tree and fails when a rule is both over its limit and +higher than the base it merges into, so a change is blamed for the violations it +adds, never for drift that already exists in the base. ``--update`` ratchets each +rule's limit down by the number of violations this branch fixed relative to its +branch point (the merge-base). """ import argparse @@ -87,10 +89,22 @@ def base_counts(ref: str) -> dict: shutil.rmtree(parent, ignore_errors=True) +def over_ceiling(head: dict, budget: dict) -> frozenset: + """Rules whose head count already exceeds their limit. + + A rule can only breach when it is over its limit, so when none are the base + comparison cannot change the verdict and the base worktree scan can be skipped. + """ + return frozenset( + rule for rule, spec in budget.items() + if head.get(rule, 0) > spec["limit"] + ) + + def evaluate(head: dict, base: dict, budget: dict) -> list: breaches = [] for rule, spec in budget.items(): - cap = spec["baseline"] + spec["slack"] + cap = spec["limit"] total = head.get(rule, 0) if total > cap and total > base.get(rule, 0): breaches.append(Breach(rule, total, cap, total - base.get(rule, 0))) @@ -117,8 +131,12 @@ def introduced(violations: list, changed: dict) -> list: def cmd_check(base: str) -> None: budget = json.loads(BUDGET_PATH.read_text()) head = head_violations() + head_counts = count_by_rule(head) + if not over_ceiling(head_counts, budget): + print(f"OK: every strict rule is within its codebase ceiling (base {base})") + return base_point = _run(["git", "merge-base", base, "HEAD"]).strip() or base - breaches = evaluate(count_by_rule(head), base_counts(base_point), budget) + breaches = evaluate(head_counts, base_counts(base_point), budget) if not breaches: print(f"OK: every strict rule is within its codebase ceiling (base {base})") return @@ -128,26 +146,49 @@ def cmd_check(base: str) -> None: _run(["git", "diff", base_point, "--unified=0", "--no-color", "--", TARGET]) ), ) - print(f"FAIL: strict-rule totals exceed their ceiling (base {base}):") + print(f"FAIL: strict-rule totals exceed their limit (base {base}):") for breach in breaches: print( - f" {breach.rule}: total {breach.total} over cap {breach.cap} (this change added {breach.added})" + f" {breach.rule}: total {breach.total} over limit {breach.cap} (this change added {breach.added})" ) for violation in sorted(v for v in new if v.code == breach.rule): print(f" {violation.file}:{violation.line}") print( - "Reduce the new violations or remove an equal number elsewhere; the ceiling is baseline + slack in ruff-strict-budget.json." + "Reduce the new violations or remove an equal number elsewhere; the ceiling is the limit in ruff-strict-budget.json." ) raise SystemExit(1) -def cmd_update() -> None: +def ratcheted_budget(budget: dict, current: dict, base: dict) -> dict: + """Each rule's limit lowered by the violations `current` fixed vs `base`. + + `base` is the count at the branch point (the commit this branch diverged + from). The drop is clamped to what was actually cleared (a rule that grew + stays put), so the limit only ever falls. + """ + return { + rule: { + "limit": max(0, spec["limit"] - max(0, base.get(rule, 0) - current.get(rule, 0))) + } + for rule, spec in sorted(budget.items()) + } + + +def cmd_update(base_ref: str = DEFAULT_BASE) -> None: + """Ratchet each rule's limit down by the violations this branch fixed. + + The working-tree count is compared against a ruff pass over a detached + worktree at the branch point (the merge-base with `base_ref`), so a branch's + fixes tighten its own ceilings by exactly what they cleared since it diverged. + """ budget = json.loads(BUDGET_PATH.read_text()) - head = count_by_rule(head_violations()) - for rule in budget: - budget[rule]["baseline"] = head.get(rule, 0) - BUDGET_PATH.write_text(json.dumps(budget, indent=2, sort_keys=True) + "\n") - print("Re-captured per-rule baselines from the current tree") + base_point = _run(["git", "merge-base", base_ref, "HEAD"]).strip() or base_ref + updated = ratcheted_budget( + budget, count_by_rule(head_violations()), base_counts(base_point) + ) + BUDGET_PATH.write_text(json.dumps(updated, indent=2, sort_keys=True) + "\n") + cleared = sum(budget[rule]["limit"] - updated[rule]["limit"] for rule in updated) + print(f"Ratcheted strict-rule limits down by {cleared} violations this branch fixed") def main() -> None: @@ -155,7 +196,7 @@ def main() -> None: parser.add_argument("--base", default=DEFAULT_BASE) parser.add_argument("--update", action="store_true") args = parser.parse_args() - cmd_update() if args.update else cmd_check(args.base) + cmd_update(args.base) if args.update else cmd_check(args.base) if __name__ == "__main__": diff --git a/scripts/type_check_gate.py b/scripts/type_check_gate.py index 2ef332d91ea..2c5306cec7d 100644 --- a/scripts/type_check_gate.py +++ b/scripts/type_check_gate.py @@ -3,20 +3,26 @@ basedpyright's ``--outputjson`` is reduced to a count of errors per *rule* (``reportAny``, ``reportArgumentType``, ...) and checked against a committed -budget of the form ``{rule: {baseline, slack}}``, the same shape as +budget of the form ``{rule: {limit}}``, the same shape as ``ruff-strict-budget.json``. A rule fails only when its codebase-wide total is -both over its ceiling (``baseline + slack``) *and* higher than the count on the -base it merges into, so a change is blamed for the errors it adds, never for -drift that already sits in the base. That ``> base`` guard is what stops an -unrelated PR from inheriting a red once two PRs each land near the ceiling and -their sum crosses it: the bystander's count equals its base, so it is spared, -while any PR that actually grows the rule past the cap still fails. +both over its ``limit`` *and* higher than the count on the base it merges into, +so a change is blamed for the errors it adds, never for drift that already sits +in the base. That ``> base`` guard is what stops an unrelated PR from inheriting +a red once two PRs each land near the limit and their sum crosses it: the +bystander's count equals its base, so it is spared, while any PR that actually +grows the rule past its limit still fails. Head counts are read from stdin (the caller runs basedpyright once and pipes -``--outputjson`` in); the base count is a second basedpyright pass over a -detached worktree at the merge-base, run under the same environment so import -resolution matches. ``--update`` re-captures the absolute per-rule baselines for -the ratchet, preserving each rule's slack. +``--outputjson`` in). The base count only matters once some rule is over its +limit, so when none is the base pass is skipped outright. When it is needed, it +is a second basedpyright pass over a detached worktree at the merge-base, run +under the same environment so import resolution matches, and its per-rule +counts are cached under the repo's git common dir keyed by merge-base commit, +``pyrightconfig.json``, and ``uv.lock``, so re-runs against the same branch +point pay for it once. ``--update`` ratchets each rule's ``limit`` down by the +number of errors this branch fixed relative to its branch point (the merge-base), +so the headroom you were granted shrinks by exactly what you cleared and never +grows. ``--outputjson`` is used rather than text diagnostics because the latter wrap across lines, leaving the ``(reportRule)`` on a continuation line away from the @@ -26,28 +32,32 @@ carries an unambiguous ``rule`` field. import argparse import contextlib +import hashlib import json +import os import shutil import subprocess import sys import tempfile from collections import Counter -from collections.abc import Iterator, Mapping +from collections.abc import Callable, Iterator, Mapping from pathlib import Path from typing import NamedTuple REPO_ROOT = Path(__file__).resolve().parent.parent BUDGET_PATH = REPO_ROOT / "basedpyright-code-budget.json" PYRIGHT_CONFIG = REPO_ROOT / "pyrightconfig.json" +UV_LOCK = REPO_ROOT / "uv.lock" DEFAULT_BASE = "origin/litellm_internal_staging" +CACHE_FILE_PREFIX = "basedpyright-base-" # Bucket for a basedpyright diagnostic with no `rule`. Counted so it's gated. UNCODED = "" -# Ceiling for a rule that shows up at HEAD but isn't in the budget at all -- a -# brand-new error category (new construct, or a tool/version change). baseline -# is treated as 0, so the rule fails once it clears this much slack. -DEFAULT_SLACK = 10 +# Limit for a rule that shows up at HEAD but isn't in the budget at all -- a +# brand-new error category (new construct, or a tool/version change). The rule +# fails once it clears this many errors. +DEFAULT_LIMIT = 10 class Breach(NamedTuple): @@ -57,18 +67,11 @@ class Breach(NamedTuple): added: int -def _seed_slack(baseline: int) -> int: - """Slack written for a rule first captured into a budget; busy rules get - more headroom, mirroring the tiering in ruff-strict-budget.json. Existing - rules keep whatever slack their JSON already declares.""" - return 10 if baseline >= 50 else 3 - - def _to_relative(raw: str, root: Path) -> str | None: path = Path(raw) absolute = path if path.is_absolute() else root / path try: - return absolute.resolve().relative_to(root).as_posix() + return absolute.resolve().relative_to(root.resolve()).as_posix() except ValueError: return None @@ -134,6 +137,109 @@ def base_counts(ref: str) -> dict[str, int]: return count_basedpyright(proc.stdout, root=worktree) +def over_ceiling( + head: Mapping[str, int], budget: Mapping[str, Mapping[str, int]] +) -> frozenset[str]: + """Rules whose head count already exceeds their limit. + + A rule can only breach when it is over its limit, so when none are the base + comparison cannot change the verdict and the base worktree pass can be skipped. + """ + return frozenset( + code + for code, total in head.items() + if total > (budget[code]["limit"] if code in budget else DEFAULT_LIMIT) + ) + + +def environment_fingerprints() -> tuple[str, ...]: + return tuple( + hashlib.sha256(path.read_bytes()).hexdigest() + for path in (PYRIGHT_CONFIG, UV_LOCK) + if path.exists() + ) + + +def cache_key(base_point: str, fingerprints: tuple[str, ...]) -> str: + return hashlib.sha256("|".join((base_point, *fingerprints)).encode()).hexdigest()[ + :16 + ] + + +def cache_path( + directory: Path, base_point: str, fingerprints: tuple[str, ...] +) -> Path: + return directory / f"{CACHE_FILE_PREFIX}{cache_key(base_point, fingerprints)}.json" + + +def default_cache_dir() -> Path: + common = Path(_run(["git", "rev-parse", "--git-common-dir"]).strip()) + resolved = common if common.is_absolute() else REPO_ROOT / common + return resolved / "litellm-lint-cache" + + +def load_cached_counts(path: Path) -> dict[str, int] | None: + try: + data = json.loads(path.read_text()) + except (OSError, json.JSONDecodeError): + return None + counts = data.get("counts") if isinstance(data, dict) else None + if not isinstance(counts, dict): + return None + if not all( + isinstance(code, str) and isinstance(total, int) and not isinstance(total, bool) + for code, total in counts.items() + ): + return None + return counts + + +def scratch_path(path: Path) -> Path: + """In-flight scratch for the tmp+rename write. Dot-prefixed so the prune + glob in `store_counts` can never match it (a concurrent run would otherwise + unlink it between write and rename), and pid-suffixed so two concurrent + writers of the same entry never share a scratch.""" + return path.with_name(f".{path.name}.{os.getpid()}.tmp") + + +def store_counts( + directory: Path, path: Path, base_point: str, counts: Mapping[str, int] +) -> None: + directory.mkdir(parents=True, exist_ok=True) + for stale in directory.glob(f"{CACHE_FILE_PREFIX}*.json"): + if stale != path: + stale.unlink(missing_ok=True) + scratch = scratch_path(path) + scratch.write_text( + json.dumps( + {"base_point": base_point, "counts": dict(sorted(counts.items()))}, + indent=2, + ) + + "\n" + ) + scratch.replace(path) + + +def base_counts_cached( + base_point: str, + cache_dir: Path | None = None, + compute: Callable[[str], dict[str, int]] = base_counts, +) -> dict[str, int]: + """`base_counts` memoized on disk. The base tree at a given commit is + immutable, so its counts are a pure function of the merge-base plus the + environment fingerprints in the cache key; an empty result is never stored + because it is the signature of a crashed pass, not a clean tree.""" + directory = default_cache_dir() if cache_dir is None else cache_dir + path = cache_path(directory, base_point, environment_fingerprints()) + cached = load_cached_counts(path) + if cached is not None: + return cached + counts = compute(base_point) + if counts: + store_counts(directory, path, base_point, counts) + return counts + + def evaluate( head: Mapping[str, int], base: Mapping[str, int], @@ -142,7 +248,7 @@ def evaluate( breaches = [] for code, total in head.items(): spec = budget.get(code) - cap = spec["baseline"] + spec["slack"] if spec else DEFAULT_SLACK + cap = spec["limit"] if spec else DEFAULT_LIMIT prior = base.get(code, 0) if total > cap and total > prior: breaches.append(Breach(code, total, cap, total - prior)) @@ -155,24 +261,47 @@ def is_vacuous_run( """True when nothing was parsed but the budget expects errors -- the signature of a type checker that crashed or produced no output. The CI pipe swallows the tool's exit code (`tool || true`), so without this guard an - empty run would clear every ceiling and pass silently.""" - return not counts and any(spec["baseline"] for spec in budget.values()) + empty run would clear every limit and pass silently.""" + return not counts and any(spec["limit"] for spec in budget.values()) -def cmd_update(counts: Mapping[str, int]) -> None: - existing = json.loads(BUDGET_PATH.read_text()) if BUDGET_PATH.exists() else {} - budget = { +def ratcheted_budget( + budget: Mapping[str, Mapping[str, int]], + current: Mapping[str, int], + base: Mapping[str, int], +) -> dict[str, dict[str, int]]: + """Each rule's limit lowered by the errors `current` fixed vs `base`. + + `base` is the count at the branch point (the commit this branch diverged + from). The drop is clamped to what was actually cleared (a rule that grew + stays put), so the limit only ever falls. Rules absent from the budget are + dropped: a genuinely new error category is added to the JSON deliberately, + not on update. + """ + return { code: { - "baseline": count, - "slack": ( - existing[code]["slack"] if code in existing else _seed_slack(count) - ), + "limit": max(0, spec["limit"] - max(0, base.get(code, 0) - current.get(code, 0))) } - for code, count in sorted(counts.items()) + for code, spec in sorted(budget.items()) } - BUDGET_PATH.write_text(json.dumps(budget, indent=2, sort_keys=True) + "\n") + + +def cmd_update(current: Mapping[str, int], base_ref: str = DEFAULT_BASE) -> None: + """Ratchet each rule's limit down by the errors this branch fixed. + + `current` is the working-tree count (piped in); the reference count comes + from a second basedpyright pass over a detached worktree at the branch point + (the merge-base with `base_ref`), so a branch's fixes tighten its own ceilings + by exactly what they cleared since it diverged, and limits never rise. + """ + budget = json.loads(BUDGET_PATH.read_text()) if BUDGET_PATH.exists() else {} + base_point = _run(["git", "merge-base", base_ref, "HEAD"]).strip() or base_ref + updated = ratcheted_budget(budget, current, base_counts_cached(base_point)) + BUDGET_PATH.write_text(json.dumps(updated, indent=2, sort_keys=True) + "\n") + cleared = sum(budget[code]["limit"] - updated[code]["limit"] for code in updated) print( - f"Re-captured basedpyright per-rule budget: {len(budget)} rules, {sum(counts.values())} errors total" + f"Ratcheted basedpyright limits down by {cleared} errors this branch fixed " + f"across {len(updated)} rules" ) @@ -180,15 +309,20 @@ def cmd_check(base_ref: str) -> None: budget = json.loads(BUDGET_PATH.read_text()) head = count_basedpyright(sys.stdin.read()) if is_vacuous_run(head, budget): - expected = sum(spec["baseline"] for spec in budget.values()) + expected = sum(spec["limit"] for spec in budget.values()) print( - f"FAIL: basedpyright produced no errors, but {BUDGET_PATH.name} expects " - f"~{expected}. The type checker almost certainly crashed or emitted " + f"FAIL: basedpyright produced no errors, but {BUDGET_PATH.name} allows " + f"up to ~{expected}. The type checker almost certainly crashed or emitted " f"nothing; refusing to certify a vacuous run." ) raise SystemExit(1) + if not over_ceiling(head, budget): + print( + f"OK: every rule is within its basedpyright limit ({sum(head.values())} errors total)" + ) + return base_point = _run(["git", "merge-base", base_ref, "HEAD"]).strip() or base_ref - base = base_counts(base_point) + base = base_counts_cached(base_point) if is_vacuous_run(base, budget): print( f"FAIL: basedpyright produced no errors for the base tree at " @@ -199,17 +333,17 @@ def cmd_check(base_ref: str) -> None: breaches = evaluate(head, base, budget) if not breaches: print( - f"OK: every rule is within its basedpyright ceiling or no higher than base ({sum(head.values())} errors total)" + f"OK: every rule is within its basedpyright limit or no higher than base ({sum(head.values())} errors total)" ) return - print("FAIL: basedpyright errors exceed the per-rule ceiling:") + print("FAIL: basedpyright errors exceed the per-rule limit:") for breach in breaches: print( - f" {breach.code}: total {breach.total} over cap {breach.cap} (this change added {breach.added})" + f" {breach.code}: total {breach.total} over limit {breach.cap} (this change added {breach.added})" ) print( "Reduce the new errors or remove an equal number elsewhere; the ceiling is " - "baseline + slack in basedpyright-code-budget.json." + "the limit in basedpyright-code-budget.json." ) summary = "; ".join(f"{b.code} {b.total}/{b.cap} (+{b.added})" for b in breaches) print(f"BREACHED RULES: {summary}") @@ -222,7 +356,7 @@ def main() -> None: parser.add_argument("--update", action="store_true") args = parser.parse_args() if args.update: - cmd_update(count_basedpyright(sys.stdin.read())) + cmd_update(count_basedpyright(sys.stdin.read()), args.base) else: cmd_check(args.base) diff --git a/scripts/type_discipline_gate.py b/scripts/type_discipline_gate.py index c111486e56a..bd8d16553b1 100644 --- a/scripts/type_discipline_gate.py +++ b/scripts/type_discipline_gate.py @@ -2,18 +2,21 @@ """Total-count gate for the LIT* rules in scripts/check_type_discipline.py. Sibling of scripts/ruff_strict_gate.py. Each rule listed in -type-discipline-budget.json has a hard ceiling (baseline + slack). The gate counts -each rule across the whole `litellm` tree and fails when a rule is both over its -ceiling and higher than the base it merges into, so a change is blamed for the -violations it adds, never for drift that already exists in the base. +type-discipline-budget.json has a hard ``limit``. The gate counts each rule +across the whole `litellm` tree and fails when a rule is both over its limit and +higher than the base it merges into, so a change is blamed for the violations it +adds, never for drift that already exists in the base. Rules not present in the budget are ignored, but today every rule the checker emits is gated: LIT001 (mutable collection in any annotation), LIT002 -(mutable-collection construction), LIT003/LIT004 (noqa / ignore without codes or -reason), LIT006 (cast), and LIT008 (`**kwargs`) carry slack-buffered ceilings to -ratchet down; LIT005 (`*-ok` suppression without a reason) is frozen at slack 0 -so any net-new reasonless suppression trips the gate; and LIT007 (TypeGuard/TypeIs) -is a hard zero. Re-baseline with `--update` to ratchet a ceiling down. +(mutable-collection construction), LIT003/LIT004 (noqa / pyright-mypy ignore +without codes or reason), LIT006 (cast), LIT008 (`**kwargs`), and LIT009 (inert +`# type: ignore`, dead syntax while enableTypeIgnoreComments is false) carry +limits at or above their current count to ratchet down; LIT005 (`*-ok` +suppression without a reason) is frozen at limit 0 so any net-new reasonless +suppression trips the gate; and LIT007 (TypeGuard/TypeIs) is a hard zero. +``--update`` ratchets a limit down by the violations this branch fixed relative +to its branch point (the merge-base). """ import argparse @@ -104,21 +107,21 @@ def base_counts(ref: str) -> dict: def over_ceiling(head: dict, budget: dict) -> frozenset: - """Rules whose head count already exceeds baseline + slack. + """Rules whose head count already exceeds their limit. - A rule can only breach when it is over its ceiling, so when none are the base + A rule can only breach when it is over its limit, so when none are the base comparison cannot change the verdict and the base worktree scan can be skipped. """ return frozenset( rule for rule, spec in budget.items() - if head.get(rule, 0) > spec["baseline"] + spec["slack"] + if head.get(rule, 0) > spec["limit"] ) def evaluate(head: dict, base: dict, budget: dict) -> list: breaches = [] for rule, spec in budget.items(): - cap = spec["baseline"] + spec["slack"] + cap = spec["limit"] total = head.get(rule, 0) if total > cap and total > base.get(rule, 0): breaches.append(Breach(rule, total, cap, total - base.get(rule, 0))) @@ -160,10 +163,10 @@ def cmd_check(base: str) -> None: _run(["git", "diff", base_point, "--unified=0", "--no-color", "--", TARGET]) ), ) - print(f"FAIL: LIT-rule totals exceed their ceiling (base {base}):") + print(f"FAIL: LIT-rule totals exceed their limit (base {base}):") for breach in breaches: print( - f" {breach.rule}: total {breach.total} over cap {breach.cap} (this change added {breach.added})" + f" {breach.rule}: total {breach.total} over limit {breach.cap} (this change added {breach.added})" ) for violation in sorted(v for v in new if v.code == breach.rule): print(f" {violation.file}:{violation.line}") @@ -171,19 +174,42 @@ def cmd_check(base: str) -> None: "Remove the new violations, give each a reason (`# noqa: XXX # `, " "`# pyright: ignore[rule] # `, `# mutable-ok: `, " "`# cast-ok: `, `# guard-ok: `, `# kwargs-ok: `), or " - "remove an equal number elsewhere; the ceiling is baseline + slack in " + "remove an equal number elsewhere; the ceiling is the limit in " "type-discipline-budget.json." ) raise SystemExit(1) -def cmd_update() -> None: +def ratcheted_budget(budget: dict, current: dict, base: dict) -> dict: + """Each rule's limit lowered by the violations `current` fixed vs `base`. + + `base` is the count at the branch point (the commit this branch diverged + from). The drop is clamped to what was actually cleared (a rule that grew + stays put), so the limit only ever falls. + """ + return { + rule: { + "limit": max(0, spec["limit"] - max(0, base.get(rule, 0) - current.get(rule, 0))) + } + for rule, spec in sorted(budget.items()) + } + + +def cmd_update(base_ref: str = DEFAULT_BASE) -> None: + """Ratchet each rule's limit down by the violations this branch fixed. + + The working-tree count is compared against a checker pass over a detached + worktree at the branch point (the merge-base with `base_ref`), so a branch's + fixes tighten its own ceilings by exactly what they cleared since it diverged. + """ budget = json.loads(BUDGET_PATH.read_text()) - head = count_by_rule(head_violations()) - for rule in budget: - budget[rule]["baseline"] = head.get(rule, 0) - BUDGET_PATH.write_text(json.dumps(budget, indent=2, sort_keys=True) + "\n") - print("Re-captured per-rule baselines from the current tree") + base_point = _run(["git", "merge-base", base_ref, "HEAD"]).strip() or base_ref + updated = ratcheted_budget( + budget, count_by_rule(head_violations()), base_counts(base_point) + ) + BUDGET_PATH.write_text(json.dumps(updated, indent=2, sort_keys=True) + "\n") + cleared = sum(budget[rule]["limit"] - updated[rule]["limit"] for rule in updated) + print(f"Ratcheted LIT-rule limits down by {cleared} violations this branch fixed") def main() -> None: @@ -191,7 +217,7 @@ def main() -> None: parser.add_argument("--base", default=DEFAULT_BASE) parser.add_argument("--update", action="store_true") args = parser.parse_args() - cmd_update() if args.update else cmd_check(args.base) + cmd_update(args.base) if args.update else cmd_check(args.base) if __name__ == "__main__": diff --git a/terraform/litellm/README.md b/terraform/litellm/README.md index 8f09cb53407..d4b40741052 100644 --- a/terraform/litellm/README.md +++ b/terraform/litellm/README.md @@ -178,6 +178,7 @@ only where the underlying cloud forces it. | Force destroy of object store | `s3_force_destroy` | `gcs_force_destroy` | | Database deletion protection | `skip_final_snapshot` | `cloudsql_deletion_protection` | | `proxy_config` (typed YAML map) | `proxy_config` | `proxy_config` | +| Coordination Redis | `REDIS_*` from ElastiCache (automatic) | `REDIS_*` from Memorystore (automatic) | | Extra plain env per component | `gateway_extra_env`, `backend_extra_env` | `gateway_extra_env`, `backend_extra_env` | | Extra secret-backed env | `gateway_extra_secrets`, `backend_extra_secrets` (ARNs) | `gateway_extra_secrets`, `backend_extra_secrets` (resource IDs) | | Uvicorn `--workers` on gateway | `gateway_num_workers` | `gateway_num_workers` | @@ -189,6 +190,19 @@ Each module stamps its own stack-identity tag (`litellm:stack` on AWS, merges `var.tags` / `var.labels` on top. Provider `default_tags` on AWS merge on top of all of these. +Coordination Redis needs no input on either cloud. Each module provisions the +managed Redis (ElastiCache on AWS, Memorystore on GCP) and exports `REDIS_HOST`, +`REDIS_PORT` and `REDIS_SSL` (plus `REDIS_SSL_CA_CERTS` on GCP) into the gateway +and backend env. The proxy falls back to those variables to build its +coordination Redis, which backs cross-pod tpm/rpm rate limits, spend tracking +and the pod lock manager. This is independent of LLM response caching, which +stays off unless you enable `litellm_settings.cache` in `proxy_config`. + +To coordinate through a Redis the module does not manage, set +`general_settings.coordination_redis` in `var.proxy_config`. An explicit block +overrides the `REDIS_*` env fallback; see the commented example in each +stack's `examples/default/terraform.tfvars.example` + OTel is opt-in on both clouds: leave `otel_endpoint` empty and nothing OTel-related is added to the container env; set it and both gateway and backend get `LITELLM_OTEL_V2=true` plus the full `OTEL_*` block, with diff --git a/terraform/litellm/aws/examples/default/terraform.tfvars.example b/terraform/litellm/aws/examples/default/terraform.tfvars.example index 4fdfb47e678..061ca2a9b82 100644 --- a/terraform/litellm/aws/examples/default/terraform.tfvars.example +++ b/terraform/litellm/aws/examples/default/terraform.tfvars.example @@ -56,6 +56,19 @@ env = "stage" # general_settings = { # master_key = "os.environ/LITELLM_MASTER_KEY" # database_url = "os.environ/DATABASE_URL" +# +# # Optional. The module already exports REDIS_HOST / REDIS_PORT / REDIS_SSL +# # from the ElastiCache group it provisions, and the proxy falls back to +# # those for cross-pod rate limits, spend tracking and the pod lock manager. +# # Set this block only to coordinate through a Redis the module does not +# # manage; it overrides the REDIS_* env fallback. Cluster mode takes +# # `startup_nodes` and sentinel takes `sentinel_nodes` + `service_name` +# # coordination_redis = { +# # host = "os.environ/COORDINATION_REDIS_HOST" +# # port = "os.environ/COORDINATION_REDIS_PORT" +# # password = "os.environ/COORDINATION_REDIS_PASSWORD" +# # ssl = true +# # } # } # } diff --git a/terraform/litellm/gcp/examples/default/terraform.tfvars.example b/terraform/litellm/gcp/examples/default/terraform.tfvars.example index 6358ec96e6d..4416cf0ee5d 100644 --- a/terraform/litellm/gcp/examples/default/terraform.tfvars.example +++ b/terraform/litellm/gcp/examples/default/terraform.tfvars.example @@ -51,6 +51,20 @@ env = "stage" # general_settings = { # master_key = "os.environ/LITELLM_MASTER_KEY" # database_url = "os.environ/DATABASE_URL" +# +# # Optional. The module already exports REDIS_HOST / REDIS_PORT / REDIS_SSL +# # (plus REDIS_SSL_CA_CERTS) from the Memorystore instance it provisions, and +# # the proxy falls back to those for cross-pod rate limits, spend tracking +# # and the pod lock manager. Set this block only to coordinate through a +# # Redis the module does not manage; it overrides the REDIS_* env fallback. +# # Cluster mode takes `startup_nodes` and sentinel takes `sentinel_nodes` +# # plus `service_name` +# # coordination_redis = { +# # host = "os.environ/COORDINATION_REDIS_HOST" +# # port = "os.environ/COORDINATION_REDIS_PORT" +# # password = "os.environ/COORDINATION_REDIS_PASSWORD" +# # ssl = true +# # } # } # } diff --git a/terraform/provider/.gitignore b/terraform/provider/.gitignore new file mode 100644 index 00000000000..7606b250a4c --- /dev/null +++ b/terraform/provider/.gitignore @@ -0,0 +1,71 @@ +# Local .terraform directories +**/.terraform/* +test_litellm/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data +*.tfvars +!*.tfvars.example + +# Ignore override files as they are usually used to override resources locally +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +# Binary files +terraform-provider-litellm + +# IDE and editor files +.idea/ +*.swp +*.swo +.vscode/ +*.sublime-workspace +*.sublime-project + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Go specific +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out +go.work + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a + +# Log files +*.log + +# Environment files +.env diff --git a/terraform/provider/.goreleaser.yml b/terraform/provider/.goreleaser.yml new file mode 100644 index 00000000000..f41a29406b8 --- /dev/null +++ b/terraform/provider/.goreleaser.yml @@ -0,0 +1,81 @@ +# Visit https://goreleaser.com for documentation on how to customize this +# behavior. +version: 2 +before: + hooks: + # this is just an example and not a requirement for provider building/publishing + - go mod tidy +builds: +- env: + # goreleaser does not work with CGO, it could also complicate + # usage by users in CI/CD systems like HCP Terraform where + # they are unable to install libraries. + - CGO_ENABLED=0 + mod_timestamp: '{{ .CommitTimestamp }}' + flags: + - -trimpath + ldflags: + - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' + goos: + - freebsd + - windows + - linux + - darwin + goarch: + - amd64 + - '386' + - arm + - arm64 + ignore: + # macOS doesn't support 32-bit anymore + - goos: darwin + goarch: '386' + # Windows ARM is uncommon for Terraform usage + - goos: windows + goarch: arm + - goos: windows + goarch: arm64 + # FreeBSD ARM is rarely used + - goos: freebsd + goarch: arm + - goos: freebsd + goarch: arm64 + # This builds the following key targets for Terraform users: + # - linux/amd64 (most common CI/CD) + # - linux/arm64 (Graviton, ARM-based CI) + # - linux/386 (legacy 32-bit systems) + # - linux/arm (Raspberry Pi, etc.) + # - darwin/amd64 (Intel Macs) + # - darwin/arm64 (Apple Silicon Macs) + # - windows/amd64 (Windows desktops) + # - freebsd/amd64, freebsd/386 (FreeBSD servers) + binary: '{{ .ProjectName }}_v{{ .Version }}' +archives: +- format: zip + name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' +checksum: + extra_files: + - glob: 'terraform-registry-manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' + algorithm: sha256 +signs: + - artifacts: checksum + args: + # if you are using this in a GitHub action or some other automated pipeline, you + # need to pass the batch flag to indicate its not interactive. + - "--batch" + - "--local-user" + - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key + - "--output" + - "${signature}" + - "--detach-sign" + - "${artifact}" +release: + extra_files: + - glob: 'terraform-registry-manifest.json' + name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' + # If you want to manually examine the release before its live, uncomment this line: + # draft: true +changelog: + disable: true \ No newline at end of file diff --git a/terraform/provider/CHANGELOG.md b/terraform/provider/CHANGELOG.md new file mode 100644 index 00000000000..101519c0b08 --- /dev/null +++ b/terraform/provider/CHANGELOG.md @@ -0,0 +1,294 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Fixed + +- **organization**: Send `PATCH` instead of `POST` to `/organization/update` and `/organization/member_update`, matching the methods the LiteLLM proxy serves; organization and organization member updates previously failed with a 405 + +### Changed + +- The provider source of truth moved to `terraform/provider/` in [BerriAI/litellm](https://github.com/BerriAI/litellm); this repository is now a release mirror. CI in the monorepo statically audits every endpoint the provider calls against the proxy's OpenAPI schema on every change + +## [0.2.2] - 2026-05-13 + +### Fixed + +- **key**: Include `tags` in `UpdateKey` payload so tag changes on an existing `litellm_key` are applied on update instead of being silently dropped (#41) + +## [0.2.1] - 2026-04-13 + +### Fixed + +- **team, organization**: Use pointer types for `tpm_limit`, `rpm_limit`, and `max_budget` to prevent zero-value diffs on every `terraform plan` when these fields are not configured (#31) + +## [0.2.0] - 2026-04-03 + +### ⚠️ Breaking Changes + +#### `litellm_key`: API keys are no longer stored in Terraform state + +**Why this change?** Storing raw API keys in Terraform state is a security risk — state files are often stored in S3, Terraform Cloud, or other backends where the key could be exposed even with encryption at rest. This release eliminates that risk entirely. + +**What changed:** +- The `key` attribute is now **write-only** — available during `terraform apply` so you can pipe it to a secrets manager, but never persisted to state +- The resource ID has changed from the raw key value to its **SHA-256 hash (`token_id`)** — safe to store in state, cannot be used to authenticate +- **Requires Terraform 1.11+** + +**Migration steps for existing `litellm_key` resources:** + +1. Find the `token_id` for each key via the LiteLLM UI or `GET /key/info?key=` +2. Remove the old resource from state: + ``` + terraform state rm litellm_key.example + ``` +3. Re-import using the token_id: + ``` + terraform import litellm_key.example + ``` + +> ⚠️ After upgrading, you cannot retrieve the raw key from state. Make sure you have the key value stored somewhere safe before migrating, or plan to rotate the key after re-import. + +**Security best practice:** Since the key is only available during the initial `terraform apply`, pipe it directly to a secrets manager: + +```hcl +resource "aws_ssm_parameter" "litellm_key" { + name = "/myapp/litellm-key" + type = "SecureString" + value = litellm_key.example.key +} +``` + +### Fixed + +- **key**: API key is no longer stored in Terraform state. The `key` attribute is now write-only and `token_id` is used as the resource ID (#27) +- **model**: Handle eventual consistency in model reads post-create (#26) + +## [0.1.2] - 2026-02-17 + +### Added +- **Documentation**: Added RELEASING.md with comprehensive release process documentation + - GPG key setup instructions + - Step-by-step release workflow + - Troubleshooting guide + - Security best practices + +## [0.1.1] - 2026-02-11 + +### Added +- **New Model Modes**: Added support for `audio_speech` and `rerank` model modes + - `audio_speech`: For text-to-speech models (e.g., Gemini TTS, OpenAI TTS) + - `rerank`: For reranking/semantic ranking models (e.g., Cohere Rerank, Vertex AI Semantic Ranker) + +### Fixed +- Implemented exponential backoff for credential reads +- Only include cost fields when explicitly set in model resource +- Added litellm_credential_name support + +## [0.3.14] - 2025-08-24 + +### Added +- **Enhanced JSON Parsing**: Added support for JSON string parsing in `additional_litellm_params` + - JSON objects and arrays (starting with `{` or `[`) are now automatically parsed + - Maintains backward compatibility with existing string-to-type conversion + - Enables complex nested parameter configurations +- **Parameter Dropping Feature**: Added `additional_drop_params` special parameter + - Allows removal of unwanted parameters from final `litellm_params` before API submission + - Specified as JSON array string: `"additional_drop_params" = "[\"reasoningEffort\"]"` + - Useful for overriding or removing built-in parameters when needed +- **Enhanced Examples**: Updated `examples/model_additional_params.tf` with comprehensive JSON parsing examples + - Demonstrates all supported value types (boolean, integer, float, string, JSON objects/arrays) + - Includes real-world Azure model configuration with parameter dropping + - Shows both simple and complex use cases + +### Changed +- **Documentation Enhancement**: Updated `docs/resources/model.md` with detailed JSON parsing documentation + - Added comprehensive explanation of conversion rules and behavior + - Included special `additional_drop_params` parameter documentation + - Enhanced examples showing all supported parameter types and JSON parsing capabilities + +### Technical Details +- Enhanced parameter processing logic in `createOrUpdateModel()` function +- Added JSON detection and parsing for string values starting with `[` or `{` +- Implemented parameter filtering system for `additional_drop_params` +- Maintains full backward compatibility with existing configurations + +## [0.3.13] - 2025-08-24 + +### Changed +- Documentation: Performed a documentation audit and improvements across resources and data-sources. Added missing argument references, clarified types/defaults, documented implementation behaviors (e.g., additional_litellm_params parsing and state-preservation), and added an `examples/` directory with runnable HCL examples (starting with `examples/model_additional_params.tf`). +- Docs: Updated `docs/resources/model.md` with missing fields (`vertex_*`, pixel/second cost fields, and `additional_litellm_params`) and added conversion rules and an example. +- Docs Index: Added references to the new `examples/` directory in `docs/index.md`. + +## [0.3.12] - 2025-08-13 + +### Added +- **New AWS Parameters**: Added `aws_session_name` and `aws_role_name` to model resource for cross-account access scenarios + - Support for AWS session names in cross-account access configurations + - Support for AWS IAM role names for cross-account access + - Enhanced AWS Bedrock integration capabilities + +### Changed +- **Documentation Overhaul**: Comprehensive update to all provider documentation + - Updated provider source references from `bitop/litellm` to `registry.terraform.io/ncecere/litellm` + - Consolidated all scattered example files into organized documentation structure + - Enhanced all resource documentation with multiple real-world examples + - Added comprehensive cross-resource integration examples +- **Vector Store Documentation**: Updated to reflect only officially supported LiteLLM providers + - Removed unsupported providers (Pinecone, Weaviate, Chroma, Qdrant, Milvus, FAISS) + - Added accurate examples for supported providers: AWS Bedrock Knowledge Bases, OpenAI Vector Stores, Azure Vector Stores, Vertex AI RAG Engine, PG Vector + - Updated provider-specific parameters with correct configurations + - Added references to official LiteLLM documentation +- **Project Organization**: Cleaned up project structure + - Removed scattered example files from root directory + - Consolidated all examples into comprehensive documentation + - Updated README.md to reflect current capabilities and structure + +### Fixed +- Corrected vector store provider documentation to match LiteLLM's official capabilities +- Updated all documentation links and references for accuracy + +## [0.3.11] - 2025-08-10 + +### Added +- **New Resource**: `litellm_credential` - Manage credentials for secure authentication + - Support for storing sensitive credential values (API keys, tokens, etc.) + - Non-sensitive credential information storage + - Model ID association for credentials + - Secure handling of sensitive data with Terraform's sensitive attribute +- **New Resource**: `litellm_vector_store` - Manage vector stores for embeddings and RAG + - Support for multiple vector store providers (Pinecone, Weaviate, Chroma, Qdrant, etc.) + - Integration with credential management for secure authentication + - Configurable metadata and provider-specific parameters + - Full CRUD operations for vector store lifecycle management +- **New Data Source**: `litellm_credential` - Retrieve information about existing credentials + - Read-only access to credential metadata (sensitive values excluded for security) + - Support for model ID filtering + - Cross-stack and cross-configuration referencing capabilities +- **New Data Source**: `litellm_vector_store` - Retrieve information about existing vector stores + - Complete vector store information retrieval + - Support for monitoring, validation, and cross-referencing use cases + - Metadata-based conditional logic support +- Enhanced API response handling for credential and vector store operations +- Comprehensive documentation and examples for new resources and data sources +- Example Terraform configurations for common use cases + +### Changed +- Extended `utils.go` with specialized API response handlers for credentials and vector stores +- Updated provider configuration to include new resources and data sources +- Enhanced error handling for credential and vector store not found scenarios + +## [0.3.10] - 2025-08-10 + +### Added +- **New Resource**: `litellm_mcp_server` - Manage MCP (Model Context Protocol) servers + - Support for HTTP, SSE, and stdio transport types + - Configurable authentication types (none, bearer, basic) + - MCP access groups for permission management + - Cost tracking configuration for MCP tools + - Environment variables and command arguments for stdio transport + - Health check status monitoring + - Comprehensive documentation and examples + +### Changed +- Updated provider to support MCP server management functionality +- Enhanced API response handling for MCP-specific operations + +## [0.3.9] - 2025-08-10 + +### Fixed +- Fixed issue where omitting `budget_duration` in key resource caused API error "Invalid duration format" +- Added missing `omitempty` JSON tag to `BudgetDuration` field in Key struct to prevent sending empty strings to API + +## [0.3.8] - 2025-08-08 + +### Added +- Added `additional_litellm_params` field to model resource for custom parameters beyond standard ones +- Support for passing custom parameters like `drop_params`, `timeout`, `max_retries`, `organization`, etc. +- Automatic type conversion for string values to appropriate types (boolean, integer, float) +- Full backward compatibility with existing model configurations +- Comprehensive example demonstrating various use cases with different providers + +## [0.3.7] - 2025-08-08 + +### Fixed +- Fixed issue where changing max_budget_in_team didn't update existing team members with new budget +- Added budget change detection using d.HasChange to update ALL existing members when budget changes +- Implemented tracking to avoid duplicate API calls for members already updated +- Enhanced debug logging for budget update operations + +## [0.3.6] - 2025-08-08 + +### Fixed +- Fixed issue where models deleted from LiteLLM proxy caused terraform plan to fail instead of planning recreation +- Enhanced ErrorResponse struct to properly parse LiteLLM proxy error format with Detail field +- Improved isModelNotFoundError function to detect "not found on litellm proxy" messages in Detail.Error field + +## [0.3.5] - 2025-08-08 + +### Fixed +- Fixed team member update behavior to use member_update endpoint instead of delete/re-add +- Restored team_member_permissions functionality to litellm_team resource +- Enhanced team resource with proper permissions management endpoints + +## [0.3.0] - 2025-04-23 + +### Fixed +- Implemented retry mechanism with exponential backoff for model read operations +- Added detailed logging for retry attempts +- Improved error handling for "model not found" errors + +## [0.2.9] - 2025-04-23 + +### Fixed +- Increased delay after model creation from 2 to 5 seconds to fix "model not found" errors +- Added logging to confirm delay is working properly + +## [0.2.8] - 2025-04-23 + +### Fixed +- Added delay after model creation to fix "model not found" errors when the LiteLLM proxy hasn't fully registered the model yet + +## [0.2.7] - 2025-04-23 + +### Fixed +- Fixed issue where `thinking_enabled` and `merge_reasoning_content_in_choices` values were not being preserved in state, causing Terraform to want to modify them on every run + +## [0.2.6] - 2025-03-13 + +### Added +- Added new `merge_reasoning_content_in_choices` option to model resource + +## [0.2.5] - 2025-03-13 + +### Fixed +- Fixed issue where `thinking_budget_tokens` was being added to models that don't have `thinking_enabled = true` + +## [0.2.4] - 2025-03-13 + +### Added +- Added new `thinking` capability to model resource with configurable parameters: + - `thinking_enabled` - Boolean to enable/disable thinking capability (default: false) + - `thinking_budget_tokens` - Integer to set token budget for thinking (default: 1024) + +## [0.2.2] - 2025-02-06 + +### Added +- Added new `reasoning_effort` parameter to model resource with values: "low", "medium", "high" +- Added "chat" mode to model resource + +### Changed +- Updated model mode options to: "completion", "embedding", "image_generation", "chat", "moderation", "audio_transcription" + +## [1.0.0] - 2024-01-17 + +### Added +- Initial release of the LiteLLM Terraform Provider +- Support for managing LiteLLM models +- Support for managing teams and team members +- Comprehensive documentation for all resources diff --git a/terraform/provider/LICENSE b/terraform/provider/LICENSE new file mode 100644 index 00000000000..967d4ac9b42 --- /dev/null +++ b/terraform/provider/LICENSE @@ -0,0 +1,35 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source diff --git a/terraform/provider/Makefile b/terraform/provider/Makefile new file mode 100644 index 00000000000..ddca16e1636 --- /dev/null +++ b/terraform/provider/Makefile @@ -0,0 +1,32 @@ +HOSTNAME=registry.terraform.io +NAMESPACE=local +NAME=litellm +VERSION=1.0.0 +OS_ARCH=darwin_amd64 + +default: install + +build: + go build -o terraform-provider-${NAME} + +install: build + mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH} + mv terraform-provider-${NAME} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}/terraform-provider-${NAME}_v${VERSION} + +test: + go test ./... + +fmt: + go fmt ./... + +vet: + go vet ./... + +lint: + golangci-lint run + +clean: + rm -f terraform-provider-${NAME} + rm -rf ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION} + +.PHONY: build install test fmt vet lint clean diff --git a/terraform/provider/README.md b/terraform/provider/README.md new file mode 100644 index 00000000000..3b59edd97c6 --- /dev/null +++ b/terraform/provider/README.md @@ -0,0 +1,223 @@ +# LiteLLM Terraform Provider + +This Terraform provider allows you to manage LiteLLM resources through Infrastructure as Code. It provides support for managing models, teams, team members, and API keys via the LiteLLM REST API. + +## Source of truth + +This directory (`terraform/provider/` in [BerriAI/litellm](https://github.com/BerriAI/litellm)) is the source of truth for the provider. [BerriAI/terraform-provider-litellm](https://github.com/BerriAI/terraform-provider-litellm) is a thin release mirror that the public Terraform Registry ingests from; do not open PRs there. Changes land here, where CI builds the provider, runs its tests, and statically audits every endpoint the provider calls against the proxy's generated OpenAPI schema (`tools/endpointaudit/`), so the provider cannot drift from the LiteLLM API silently. Releases are published by mirroring this directory into the split repo and tagging it, which triggers the goreleaser workflow there (see `RELEASING.md`) + +## Features + +- Manage LiteLLM model configurations +- Associate models with specific teams +- Create and manage teams +- Configure team members and their permissions +- Set usage limits and budgets +- Control access to specific models +- Specify model modes (e.g., completion, embedding, image generation) +- Manage API keys with fine-grained controls +- Support for reasoning effort configuration in the model resource + +## Requirements + +- [Terraform](https://www.terraform.io/downloads.html) >= 0.13.x +- [Go](https://golang.org/doc/install) >= 1.16 (for development) + +## Using the Provider + +To use the LiteLLM provider in your Terraform configuration, you need to declare it in the terraform block: + +```hcl +terraform { + required_providers { + litellm = { + source = "BerriAI/litellm" + version = "~> 0.1.1" #HERE UPDATE VERSION ACCORDINGLY + } + } +} + +provider "litellm" { + api_base = var.litellm_api_base + api_key = var.litellm_api_key +} +``` + +Then, you can use the provider to manage LiteLLM resources. Here's an example of creating a model configuration: + +```hcl +resource "litellm_model" "gpt4" { + model_name = "gpt-4-proxy" + custom_llm_provider = "openai" + model_api_key = var.openai_api_key + model_api_base = "https://api.openai.com/v1" + base_model = "gpt-4" + tier = "paid" + mode = "chat" + reasoning_effort = "medium" # Optional: "low", "medium", or "high" + + input_cost_per_million_tokens = 30.0 + output_cost_per_million_tokens = 60.0 +} +``` + +For full details on the litellm_model resource, see the [model resource documentation](docs/resources/model.md). + +Here's an example of creating an API key with various options: + +```hcl +resource "litellm_key" "example_key" { + models = ["gpt-4", "claude-3.5-sonnet"] + max_budget = 100.0 + user_id = "user123" + team_id = "team456" + max_parallel_requests = 5 + tpm_limit = 1000 + rpm_limit = 60 + budget_duration = "monthly" + key_alias = "prod-key-1" + duration = "30d" + metadata = { + environment = "production" + } + allowed_cache_controls = ["no-cache", "max-age=3600"] + soft_budget = 80.0 + aliases = { + "gpt-4" = "gpt4" + } + config = { + default_model = "gpt-4" + } + permissions = { + can_create_keys = "true" + } + model_max_budget = { + "gpt-4" = 50.0 + } + model_rpm_limit = { + "claude-3.5-sonnet" = 30 + } + model_tpm_limit = { + "gpt-4" = 500 + } + guardrails = ["content_filter", "token_limit"] + blocked = false + tags = ["production", "api"] +} +``` + +The litellm_key resource supports the following options: + +- models: List of allowed models for this key +- max_budget: Maximum budget for the key +- user_id and team_id: Associate the key with a user and team +- max_parallel_requests: Limit concurrent requests +- tpm_limit and rpm_limit: Set tokens and requests per minute limits +- budget_duration: Specify budget duration (e.g., "monthly", "weekly") +- key_alias: Set a friendly name for the key +- duration: Set the key's validity period +- metadata: Add custom metadata to the key +- allowed_cache_controls: Specify allowed cache control directives +- soft_budget: Set a soft budget limit +- aliases: Define model aliases +- config: Set configuration options +- permissions: Specify key permissions +- model_max_budget, model_rpm_limit, model_tpm_limit: Set per-model limits +- guardrails: Apply specific guardrails to the key +- blocked: Flag to block/unblock the key +- tags: Add tags for organization and filtering + +For full details on the litellm_key resource, see the [key resource documentation](docs/resources/key.md). + +### Available Resources + +- litellm_model: Manage model configurations. [Documentation](docs/resources/model.md) +- litellm_team: Manage teams. [Documentation](docs/resources/team.md) +- litellm_team_member: Manage team members. [Documentation](docs/resources/team_member.md) +- litellm_team_member_add: Add multiple members to teams. [Documentation](docs/resources/team_member_add.md) +- litellm_key: Manage API keys. [Documentation](docs/resources/key.md) +- litellm_mcp_server: Manage MCP (Model Context Protocol) servers. [Documentation](docs/resources/mcp_server.md) +- litellm_credential: Manage credentials for secure authentication. [Documentation](docs/resources/credential.md) +- litellm_vector_store: Manage vector stores for embeddings and RAG. [Documentation](docs/resources/vector_store.md) + +### Available Data Sources + +- litellm_credential: Retrieve information about existing credentials. [Documentation](docs/data-sources/credential.md) +- litellm_vector_store: Retrieve information about existing vector stores. [Documentation](docs/data-sources/vector_store.md) + +## Development + +### Project Structure + +The project is organized as follows: + +``` +terraform-provider-litellm/ +├── litellm/ +│ ├── provider.go +│ ├── resource_model.go +│ ├── resource_model_crud.go +│ ├── resource_team.go +│ ├── resource_team_member.go +│ ├── resource_key.go +│ ├── resource_key_utils.go +│ ├── types.go +│ └── utils.go +├── main.go +├── go.mod +├── go.sum +├── Makefile +└── ... +``` + +### Building the Provider + +1. Clone the repository: +```sh +git clone https://github.com/your-username/terraform-provider-litellm.git +``` + +2. Enter the repository directory: +```sh +cd terraform-provider-litellm +``` + +3. Build and install the provider: +```sh +make install +``` + +### Development Commands + +The Makefile provides several useful commands for development: + +- `make build`: Builds the provider +- `make install`: Builds and installs the provider +- `make test`: Runs the test suite +- `make fmt`: Formats the code +- `make vet`: Runs go vet +- `make lint`: Runs golangci-lint +- `make clean`: Removes build artifacts and installed provider + +### Testing + +To run the tests: +```sh +make test +``` + +### Contributing + +Contributions are welcome! Please read our [contributing guidelines](CONTRIBUTING.md) first. + +## License + +This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. + +## Notes + +- Always use environment variables or secure secret management solutions to handle sensitive information like API keys and AWS credentials. +- Refer to the comprehensive documentation in the `docs/` directory for detailed usage examples and configuration options. +- Make sure to keep your provider version updated for the latest features and bug fixes. +- The provider now supports AWS cross-account access with `aws_session_name` and `aws_role_name` parameters in the model resource. +- All example configurations have been consolidated into the documentation for better organization and maintenance. diff --git a/terraform/provider/RELEASING.md b/terraform/provider/RELEASING.md new file mode 100644 index 00000000000..1dc296f29b8 --- /dev/null +++ b/terraform/provider/RELEASING.md @@ -0,0 +1,237 @@ +# Release Process + +This document describes the release process for the LiteLLM Terraform Provider. + +## Overview + +Releases are automated via GitHub Actions when a version tag is pushed. The workflow builds the provider for multiple platforms, signs the artifacts with GPG, and publishes them to GitHub Releases. + +## Prerequisites + +### GPG Key Setup (One-Time Setup for Repository Maintainers) + +The Terraform Registry requires all providers to be signed with a GPG key. This must be configured before the first release. + +#### 1. Generate a GPG Key + +If you don't already have a GPG key for provider signing: + +```bash +gpg --full-generate-key +``` + +Configuration: +- Key type: RSA and RSA (default) +- Key size: 4096 bits +- Expiration: No expiration (or set a long expiration period) +- Email: Use an email associated with your GitHub account +- Set a strong passphrase (or leave empty for CI/CD use) + +#### 2. Export the GPG Key + +```bash +# List your keys to get the key ID +gpg --list-secret-keys --keyid-format=long + +# Example output: +# sec rsa4096/ABCD1234EFGH5678 2024-01-01 [SC] +# 1234567890ABCDEF1234567890ABCDEF12345678 +# uid [ultimate] Your Name +# +# The key ID is: ABCD1234EFGH5678 +# The fingerprint is: 1234567890ABCDEF1234567890ABCDEF12345678 + +# Export the private key (ASCII-armored format) +gpg --armor --export-secret-keys ABCD1234EFGH5678 + +# Export the public key +gpg --armor --export ABCD1234EFGH5678 +``` + +#### 3. Configure GitHub Repository Secrets + +Add the following secrets to the repository at: **Settings → Secrets and variables → Actions → New repository secret** + +| Secret Name | Description | Value | +|-------------|-------------|-------| +| `GPG_PRIVATE_KEY` | The GPG private key for signing releases | Full output from `gpg --armor --export-secret-keys` (including `-----BEGIN PGP PRIVATE KEY BLOCK-----` and `-----END PGP PRIVATE KEY BLOCK-----`) | +| `PASSPHRASE` | The passphrase for the GPG key | Your GPG key passphrase (leave empty if no passphrase was set) | + +#### 4. Register Public Key with Terraform Registry + +Before publishing to the Terraform Registry: + +1. Go to https://registry.terraform.io/settings/gpg-keys +2. Click "Add a key" +3. Paste your public GPG key (output from `gpg --armor --export`) +4. Submit + +**Note**: The public key fingerprint must match the key used to sign the provider releases. + +## Release Steps + +### 1. Prepare the Release + +Before creating a release: + +1. **Update CHANGELOG.md** + - Move items from `[Unreleased]` section to a new version section + - Follow [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format + - Use [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for version numbers + - Include all notable changes since the last release + + Example: + ```markdown + ## [0.1.2] - 2026-02-20 + + ### Added + - New feature description + + ### Fixed + - Bug fix description + + ### Changed + - Changed behavior description + ``` + +2. **Verify tests pass** + ```bash + make test + ``` + +3. **Verify the build works locally** + ```bash + make build + ``` + +4. **Land the changes in BerriAI/litellm** + + Open a PR to `BerriAI/litellm` updating `terraform/provider/CHANGELOG.md` (and any source changes) and merge it. Note the merge commit SHA; the release workflow takes it as `git_ref` + +### 2. Mirror and Tag via project-releaser + +The provider source lives at `terraform/provider/` in `BerriAI/litellm`; `BerriAI/terraform-provider-litellm` is a thin release mirror. Do not commit or tag the mirror directly + +1. Go to `BerriAI/project-releaser` > **Actions** > `Publish Terraform provider` +2. Click **Run workflow**: + - `git_ref`: full 40-char commit SHA from `BerriAI/litellm` to release from + - `provider_version`: the new version without the `v` prefix (e.g. `0.3.0`) + - `dry_run`: optional; validates without pushing +3. The workflow rsyncs `terraform/provider/` into the mirror repo, commits, and pushes tag `v` +4. The tag push triggers the mirror's `Release` workflow (goreleaser), which is gated by the `production-release` environment approval + +**Important**: +- Tags must follow the format: `v..` (e.g., `v0.1.2`, `v1.0.0`) +- The workflow refuses to overwrite an existing tag; publish a new version instead + +### 3. Monitor the Release Workflow + +1. Go to: https://github.com/BerriAI/terraform-provider-litellm/actions +2. Find the "Release" workflow run for your tag +3. Monitor the progress and check for any errors + +The workflow will: +- Check out the code +- Set up Go +- Import the GPG key +- Run `go mod tidy` +- Build binaries for multiple platforms (Linux, macOS, Windows, FreeBSD) +- Create archives and checksums +- Sign the checksums with GPG +- Create a GitHub release +- Upload all artifacts + +### 4. Verify the Release + +After the workflow completes successfully: + +1. **Check the GitHub Release** + - Go to: https://github.com/BerriAI/terraform-provider-litellm/releases + - Verify the release was created with the correct version + - Confirm all artifacts are present: + - Binary archives for each platform + - SHA256SUMS file + - SHA256SUMS.sig (GPG signature) + - terraform-registry-manifest.json + +2. **Verify the signature** (optional) + ```bash + # Download the checksums and signature + wget https://github.com/BerriAI/terraform-provider-litellm/releases/download/v0.1.2/terraform-provider-litellm_0.1.2_SHA256SUMS + wget https://github.com/BerriAI/terraform-provider-litellm/releases/download/v0.1.2/terraform-provider-litellm_0.1.2_SHA256SUMS.sig + + # Verify the signature + gpg --verify terraform-provider-litellm_0.1.2_SHA256SUMS.sig terraform-provider-litellm_0.1.2_SHA256SUMS + ``` + +### 5. Publish to Terraform Registry (Optional) + +If this provider is published to the Terraform Registry: + +1. The registry should automatically detect the new release via the GitHub webhook +2. If not, you may need to manually trigger a sync on the Terraform Registry dashboard +3. Verify the new version appears at: https://registry.terraform.io/providers/BerriAI/litellm/latest + +## Troubleshooting + +### Release Workflow Fails with GPG Error + +**Error**: `Input required and not supplied: gpg_private_key` + +**Solution**: +- Verify that `GPG_PRIVATE_KEY` and `PASSPHRASE` secrets are configured in the repository +- Ensure the secrets are not expired +- Check that the secret names match exactly (case-sensitive) + +### GoReleaser Signing Fails + +**Error**: `gpg: signing failed: No secret key` + +**Solution**: +- Verify the `GPG_PRIVATE_KEY` secret contains the complete private key block +- Ensure the passphrase is correct +- Check that the key hasn't expired: `gpg --list-keys` + +### Build Fails + +**Error**: Build errors during compilation + +**Solution**: +- Run `make test` and `make build` locally first +- Ensure `go.mod` and `go.sum` are up to date +- Check that all dependencies are available + +### Tag Already Exists + +**Error**: The publish workflow refuses to push because the tag already exists on the mirror + +**Solution**: Tags are immutable by design. Re-run the workflow with a new patch version instead of deleting or moving an existing tag + +## Version Numbering + +This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html): + +- **MAJOR** version (1.0.0): Incompatible API changes +- **MINOR** version (0.1.0): New functionality in a backward-compatible manner +- **PATCH** version (0.0.1): Backward-compatible bug fixes + +For pre-1.0 releases: +- Breaking changes may occur in minor versions +- Patch versions should only contain bug fixes + +## Security Considerations + +1. **Never commit private keys**: The GPG private key should only be stored as a GitHub secret +2. **Protect repository secrets**: Limit who has access to manage repository secrets +3. **Use a dedicated key**: Consider using a separate GPG key specifically for provider signing +4. **Key rotation**: If the GPG key is compromised, generate a new key, update secrets, and register the new public key with the Terraform Registry +5. **Passphrase**: Use a strong passphrase for the GPG key, or use a passphrase-less key specifically for CI/CD + +## References + +- [GoReleaser Documentation](https://goreleaser.com/) +- [Terraform Provider Publishing](https://www.terraform.io/docs/registry/providers/publishing.html) +- [HashiCorp GPG Signing Requirements](https://www.terraform.io/docs/registry/providers/publishing.html#signing-releases) +- [GitHub Actions Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets) +- [Semantic Versioning](https://semver.org/) +- [Keep a Changelog](https://keepachangelog.com/) diff --git a/terraform/provider/docs/data-sources/credential.md b/terraform/provider/docs/data-sources/credential.md new file mode 100644 index 00000000000..de4b9a8d9e4 --- /dev/null +++ b/terraform/provider/docs/data-sources/credential.md @@ -0,0 +1,153 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "litellm_credential Data Source - terraform-provider-litellm" +subcategory: "" +description: |- + Retrieves information about an existing LiteLLM credential. +--- + +# litellm_credential (Data Source) + +Retrieves information about an existing LiteLLM credential. This data source allows you to reference credentials that were created outside of Terraform or in other Terraform configurations. + +## Example Usage + +```terraform +# Retrieve an existing credential by name +data "litellm_credential" "existing_openai" { + credential_name = "openai-production-key" +} + +# Use the credential in a model resource +resource "litellm_model" "gpt4_with_existing_cred" { + model_name = "gpt-4-with-existing-cred" + custom_llm_provider = "openai" + base_model = "gpt-4" + tier = "paid" + mode = "chat" + + # Reference the existing credential's info + additional_litellm_params = { + credential_name = data.litellm_credential.existing_openai.credential_name + } +} +``` + +## Example Usage with Model ID + +```terraform +# Retrieve a credential associated with a specific model +data "litellm_credential" "model_specific_cred" { + credential_name = "claude-api-key" + model_id = "claude-3-sonnet" +} + +# Use in a vector store +resource "litellm_vector_store" "knowledge_base" { + vector_store_name = "claude-knowledge-base" + custom_llm_provider = "anthropic" + litellm_credential_name = data.litellm_credential.model_specific_cred.credential_name + + vector_store_description = "Knowledge base using Claude credentials" +} +``` + +## Example Usage for Cross-Reference + +```terraform +# Get credential info to use in other resources +data "litellm_credential" "shared_cred" { + credential_name = "shared-api-key" +} + +# Create multiple resources using the same credential +resource "litellm_vector_store" "store_1" { + vector_store_name = "store-1" + custom_llm_provider = "pinecone" + litellm_credential_name = data.litellm_credential.shared_cred.credential_name + + vector_store_description = "First store using shared credential" +} + +resource "litellm_vector_store" "store_2" { + vector_store_name = "store-2" + custom_llm_provider = "pinecone" + litellm_credential_name = data.litellm_credential.shared_cred.credential_name + + vector_store_description = "Second store using shared credential" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `credential_name` - (Required) Name of the credential to retrieve. +* `model_id` - (Optional) Model ID associated with this credential. Use this when the same credential name is used for different models. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `credential_info` - Map of additional non-sensitive information about the credential. + +## Security Note + +For security reasons, the `credential_values` (sensitive data like API keys) are not exposed through data sources. This prevents accidental exposure of sensitive information in Terraform plans and logs. If you need to access credential values, you should manage them through the resource directly or use external secret management systems. + +## Common Use Cases + +### 1. Cross-Stack References +Use data sources to reference credentials created in other Terraform configurations or stacks: + +```terraform +data "litellm_credential" "shared_openai" { + credential_name = "openai-shared-key" +} + +resource "litellm_model" "gpt4" { + model_name = "gpt-4-cross-stack" + custom_llm_provider = "openai" + base_model = "gpt-4" + + additional_litellm_params = { + credential_reference = data.litellm_credential.shared_openai.credential_name + } +} +``` + +### 2. Conditional Logic +Use credential information for conditional resource creation: + +```terraform +data "litellm_credential" "optional_cred" { + credential_name = var.credential_name +} + +resource "litellm_vector_store" "conditional_store" { + count = length(data.litellm_credential.optional_cred.credential_info) > 0 ? 1 : 0 + + vector_store_name = "conditional-store" + custom_llm_provider = "weaviate" + litellm_credential_name = data.litellm_credential.optional_cred.credential_name +} +``` + +### 3. Validation and Verification +Verify that required credentials exist before creating dependent resources: + +```terraform +data "litellm_credential" "required_cred" { + credential_name = "production-api-key" +} + +# This will fail if the credential doesn't exist +resource "litellm_model" "production_model" { + model_name = "production-gpt-4" + custom_llm_provider = "openai" + base_model = "gpt-4" + + additional_litellm_params = { + credential_name = data.litellm_credential.required_cred.credential_name + } +} diff --git a/terraform/provider/docs/data-sources/vector_store.md b/terraform/provider/docs/data-sources/vector_store.md new file mode 100644 index 00000000000..30bc26c163e --- /dev/null +++ b/terraform/provider/docs/data-sources/vector_store.md @@ -0,0 +1,225 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "litellm_vector_store Data Source - terraform-provider-litellm" +subcategory: "" +description: |- + Retrieves information about an existing LiteLLM vector store. +--- + +# litellm_vector_store (Data Source) + +Retrieves information about an existing LiteLLM vector store. This data source allows you to reference vector stores that were created outside of Terraform or in other Terraform configurations. + +## Example Usage + +```terraform +# Retrieve an existing vector store by ID +data "litellm_vector_store" "existing_store" { + vector_store_id = "vs-12345" +} + +# Use the vector store information in outputs +output "vector_store_info" { + value = { + name = data.litellm_vector_store.existing_store.vector_store_name + provider = data.litellm_vector_store.existing_store.custom_llm_provider + created_at = data.litellm_vector_store.existing_store.created_at + } +} +``` + +## Example Usage for Cross-Reference + +```terraform +# Get vector store info to reference in other configurations +data "litellm_vector_store" "shared_store" { + vector_store_id = var.shared_vector_store_id +} + +# Create a model that might use the same credential as the vector store +data "litellm_credential" "store_credential" { + credential_name = data.litellm_vector_store.shared_store.litellm_credential_name +} + +resource "litellm_model" "embedding_model" { + model_name = "embedding-model" + custom_llm_provider = "openai" + base_model = "text-embedding-ada-002" + mode = "embedding" + + additional_litellm_params = { + credential_name = data.litellm_credential.store_credential.credential_name + } +} +``` + +## Example Usage for Validation + +```terraform +# Verify vector store exists and get its configuration +data "litellm_vector_store" "production_store" { + vector_store_id = "production-vector-store-id" +} + +# Create resources only if the vector store is properly configured +resource "litellm_model" "rag_model" { + count = data.litellm_vector_store.production_store.custom_llm_provider == "pinecone" ? 1 : 0 + + model_name = "rag-enabled-model" + custom_llm_provider = "openai" + base_model = "gpt-4" + mode = "chat" + + additional_litellm_params = { + vector_store_id = data.litellm_vector_store.production_store.vector_store_id + } +} +``` + +## Example Usage for Monitoring + +```terraform +# Get vector store details for monitoring and alerting +data "litellm_vector_store" "monitored_stores" { + for_each = toset(var.vector_store_ids) + + vector_store_id = each.value +} + +# Output store information for monitoring systems +output "vector_store_status" { + value = { + for k, v in data.litellm_vector_store.monitored_stores : k => { + name = v.vector_store_name + provider = v.custom_llm_provider + created_at = v.created_at + updated_at = v.updated_at + metadata = v.vector_store_metadata + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vector_store_id` - (Required) Unique identifier for the vector store to retrieve. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `vector_store_name` - Name of the vector store. +* `custom_llm_provider` - Custom LLM provider for the vector store. +* `vector_store_description` - Description of the vector store. +* `vector_store_metadata` - Map of metadata associated with the vector store. +* `litellm_credential_name` - Name of the LiteLLM credential used. +* `litellm_params` - Map of additional LiteLLM parameters. +* `created_at` - Timestamp when the vector store was created. +* `updated_at` - Timestamp when the vector store was last updated. + +## Common Use Cases + +### 1. Cross-Stack References +Reference vector stores created in other Terraform configurations: + +```terraform +data "litellm_vector_store" "shared_knowledge_base" { + vector_store_id = var.knowledge_base_id +} + +# Use the same credential for consistency +resource "litellm_model" "knowledge_model" { + model_name = "knowledge-retrieval-model" + custom_llm_provider = "openai" + base_model = "gpt-4" + + additional_litellm_params = { + vector_store_credential = data.litellm_vector_store.shared_knowledge_base.litellm_credential_name + } +} +``` + +### 2. Configuration Validation +Validate vector store configuration before creating dependent resources: + +```terraform +data "litellm_vector_store" "target_store" { + vector_store_id = var.target_vector_store_id +} + +# Ensure the vector store uses the expected provider +locals { + is_pinecone_store = data.litellm_vector_store.target_store.custom_llm_provider == "pinecone" +} + +resource "litellm_model" "pinecone_optimized_model" { + count = local.is_pinecone_store ? 1 : 0 + + model_name = "pinecone-optimized" + custom_llm_provider = "openai" + base_model = "text-embedding-ada-002" + mode = "embedding" +} +``` + +### 3. Metadata-Based Logic +Use vector store metadata for conditional resource creation: + +```terraform +data "litellm_vector_store" "environment_store" { + vector_store_id = var.vector_store_id +} + +# Create different resources based on environment metadata +resource "litellm_model" "production_model" { + count = lookup(data.litellm_vector_store.environment_store.vector_store_metadata, "environment", "") == "production" ? 1 : 0 + + model_name = "production-rag-model" + custom_llm_provider = "openai" + base_model = "gpt-4" + mode = "chat" +} + +resource "litellm_model" "development_model" { + count = lookup(data.litellm_vector_store.environment_store.vector_store_metadata, "environment", "") == "development" ? 1 : 0 + + model_name = "development-rag-model" + custom_llm_provider = "openai" + base_model = "gpt-3.5-turbo" + mode = "chat" +} +``` + +### 4. Audit and Compliance +Retrieve vector store information for audit and compliance reporting: + +```terraform +data "litellm_vector_store" "compliance_stores" { + for_each = toset(var.compliance_vector_store_ids) + + vector_store_id = each.value +} + +# Generate compliance report +output "compliance_report" { + value = { + for k, v in data.litellm_vector_store.compliance_stores : k => { + store_name = v.vector_store_name + provider = v.custom_llm_provider + credential = v.litellm_credential_name + created_date = v.created_at + last_updated = v.updated_at + metadata = v.vector_store_metadata + } + } +} +``` + +## Notes + +* Vector store IDs are unique identifiers assigned by the LiteLLM system. +* The data source will fail if the specified vector store ID does not exist. +* All computed attributes reflect the current state of the vector store in the LiteLLM system. +* Use this data source to integrate with existing vector stores or to reference stores created outside of Terraform. diff --git a/terraform/provider/docs/index.md b/terraform/provider/docs/index.md new file mode 100644 index 00000000000..c03071e7ed3 --- /dev/null +++ b/terraform/provider/docs/index.md @@ -0,0 +1,117 @@ +# LiteLLM Provider + +The LiteLLM provider allows Terraform to manage LiteLLM resources. LiteLLM is a proxy service that standardizes the input/output across different LLM APIs, providing a unified interface for various language model providers. + +## Example Usage + +```hcl +terraform { + required_providers { + litellm = { + source = "registry.terraform.io/BerriAI/litellm" + } + } +} + +provider "litellm" { + api_base = "https://your-litellm-proxy.com" + api_key = var.litellm_api_key +} + +# Basic model configuration +resource "litellm_model" "gpt4" { + model_name = "gpt-4-proxy" + custom_llm_provider = "openai" + model_api_key = var.openai_api_key + base_model = "gpt-4" + tier = "paid" + mode = "chat" + + input_cost_per_million_tokens = 30.0 + output_cost_per_million_tokens = 60.0 +} + +# Team configuration +resource "litellm_team" "dev_team" { + team_alias = "development-team" + models = [litellm_model.gpt4.model_name] + max_budget = 100.0 +} +``` + +## Available Resources + +The LiteLLM provider supports the following resources: + +* [`litellm_model`](./resources/model) - Manage LiteLLM model configurations +* [`litellm_team`](./resources/team) - Manage teams and their permissions +* [`litellm_team_member`](./resources/team_member) - Manage team member configurations +* [`litellm_team_member_add`](./resources/team_member_add) - Add members to teams +* [`litellm_key`](./resources/key) - Manage API keys +* [`litellm_mcp_server`](./resources/mcp_server) - Manage MCP (Model Context Protocol) servers +* [`litellm_credential`](./resources/credential) - Manage credentials for various providers +* [`litellm_vector_store`](./resources/vector_store) - Manage vector stores + +## Available Data Sources + +The LiteLLM provider supports the following data sources: + +* [`litellm_credential`](./data-sources/credential) - Retrieve credential information +* [`litellm_vector_store`](./data-sources/vector_store) - Retrieve vector store information + +## Authentication + +The LiteLLM provider requires an API key and base URL for authentication. These can be provided in the provider configuration block or via environment variables. + +### Environment Variables + +- `LITELLM_API_BASE` - The base URL of your LiteLLM instance +- `LITELLM_API_KEY` - Your LiteLLM API key + +### Example with Environment Variables + +```bash +export LITELLM_API_BASE="https://your-litellm-proxy.com" +export LITELLM_API_KEY="your-api-key" +``` + +```hcl +terraform { + required_providers { + litellm = { + source = "registry.terraform.io/BerriAI/litellm" + } + } +} + +# Provider will automatically use environment variables +provider "litellm" {} +``` + +## Provider Arguments + +The following arguments are supported in the provider block: + +* `api_base` - (Required) The base URL of your LiteLLM instance. This can also be provided via the `LITELLM_API_BASE` environment variable. +* `api_key` - (Required) The API key used to authenticate with LiteLLM. This can also be provided via the `LITELLM_API_KEY` environment variable. + +## Getting Started + +1. Install the provider by adding it to your Terraform configuration +2. Configure your LiteLLM instance URL and API key +3. Start creating resources like models, teams, and credentials +4. Use data sources to reference existing configurations + +For detailed examples and configuration options, see the individual resource and data source documentation pages. + +## Examples + +This repository includes an `examples/` directory with curated, ready-to-run HCL examples that demonstrate common and advanced usages of the provider. Examples are grouped by resource and illustrate provider-specific configuration, handling of sensitive values, and advanced options such as `additional_litellm_params`. + +See: +* `examples/model_additional_params.tf` — demonstrates how to use `additional_litellm_params` (booleans, integers, floats, and strings). +* Other example files will be added to `examples/` for credentials, vector stores, and MCP servers. + +You can reference these examples directly or copy snippets into your Terraform configurations for quick starts. + +For detailed examples and configuration options, see the individual resource and data source documentation pages. diff --git a/terraform/provider/docs/resources/credential.md b/terraform/provider/docs/resources/credential.md new file mode 100644 index 00000000000..554ac07c395 --- /dev/null +++ b/terraform/provider/docs/resources/credential.md @@ -0,0 +1,152 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "litellm_credential Resource - terraform-provider-litellm" +subcategory: "" +description: |- + Manages a LiteLLM credential for storing sensitive authentication information. +--- + +# litellm_credential (Resource) + +Manages a LiteLLM credential for storing sensitive authentication information. Credentials can be used to securely store API keys, tokens, and other sensitive data that can be referenced by models and vector stores. + +## Example Usage + +### Basic OpenAI Credential + +```terraform +resource "litellm_credential" "openai_cred" { + credential_name = "openai-api-key" + model_id = "gpt-4" + + credential_info = { + provider = "openai" + region = "us-east-1" + purpose = "chat-completions" + } + + credential_values = { + api_key = var.openai_api_key + org_id = var.openai_org_id + } +} +``` + +### Anthropic Credential + +```terraform +resource "litellm_credential" "anthropic_cred" { + credential_name = "anthropic-api-key" + + credential_info = { + provider = "anthropic" + purpose = "text-generation" + } + + credential_values = { + api_key = var.anthropic_api_key + } +} +``` + +### Pinecone Vector Store Credential + +```terraform +resource "litellm_credential" "pinecone_cred" { + credential_name = "pinecone-production" + + credential_info = { + provider = "pinecone" + environment = "production" + region = "us-east-1" + } + + credential_values = { + api_key = var.pinecone_api_key + index_name = "document-embeddings" + } +} +``` + +### Using Credentials with Vector Store + +```terraform +resource "litellm_vector_store" "example" { + vector_store_name = "my-vector-store" + custom_llm_provider = "pinecone" + litellm_credential_name = litellm_credential.pinecone_cred.credential_name + + vector_store_description = "Example vector store using Pinecone" + + vector_store_metadata = { + environment = "production" + team = "ai-team" + } +} +``` + +### Multiple Provider Credentials + +```terraform +# AWS Bedrock credential +resource "litellm_credential" "aws_bedrock" { + credential_name = "aws-bedrock-cred" + + credential_info = { + provider = "aws" + service = "bedrock" + region = "us-east-1" + } + + credential_values = { + aws_access_key_id = var.aws_access_key_id + aws_secret_access_key = var.aws_secret_access_key + aws_region = "us-east-1" + } +} + +# Azure OpenAI credential +resource "litellm_credential" "azure_openai" { + credential_name = "azure-openai-cred" + + credential_info = { + provider = "azure" + service = "openai" + } + + credential_values = { + api_key = var.azure_openai_key + api_base = var.azure_openai_endpoint + api_version = "2023-12-01-preview" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `credential_name` - (Required) Name of the credential. This will be used as the identifier for the credential. +* `credential_values` - (Required, Sensitive) Map of sensitive credential values such as API keys, tokens, etc. +* `model_id` - (Optional) Model ID associated with this credential. +* `credential_info` - (Optional) Map of additional non-sensitive information about the credential. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `credential_name` - The name of the credential. + +## Import + +Credentials can be imported using their name: + +```shell +terraform import litellm_credential.example "credential-name" +``` + +## Security Considerations + +* The `credential_values` field is marked as sensitive and will not be displayed in Terraform output or logs. +* Credential values are not read back from the API for security reasons, so they are preserved in the Terraform state. +* Like every Terraform attribute marked `Sensitive`, `credential_values` is still written in plaintext to the state file. Anyone with read access to the state (or state artifacts such as plan files) can recover the configured secrets. Use an encrypted remote backend with tight access controls, and prefer feeding secrets in via variables sourced from a secret manager rather than hardcoding them in configuration. diff --git a/terraform/provider/docs/resources/key.md b/terraform/provider/docs/resources/key.md new file mode 100644 index 00000000000..b48d3334c14 --- /dev/null +++ b/terraform/provider/docs/resources/key.md @@ -0,0 +1,116 @@ +# litellm_key Resource + +Manages a LiteLLM API key. + +## Example Usage + +```hcl +resource "litellm_key" "example" { + models = ["gpt-3.5-turbo", "gpt-4"] + max_budget = 100.0 + user_id = "user123" + team_id = "team456" + max_parallel_requests = 5 + metadata = { + "environment" = "production" + } + tpm_limit = 1000 + rpm_limit = 60 + budget_duration = "monthly" + allowed_cache_controls = ["no-cache", "max-age=3600"] + soft_budget = 80.0 + key_alias = "prod-key-1" + duration = "30d" + aliases = { + "gpt-3.5-turbo" = "chatgpt" + } + config = { + "default_model" = "gpt-3.5-turbo" + } + permissions = { + "can_create_keys" = "true" + } + model_max_budget = { + "gpt-4" = 50.0 + } + model_rpm_limit = { + "gpt-3.5-turbo" = 30 + } + model_tpm_limit = { + "gpt-4" = 500 + } + guardrails = ["content_filter", "token_limit"] + blocked = false + tags = ["production", "api"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `models` - (Optional) List of models that can be used with this key. This restricts the key to only use the specified models. + +* `max_budget` - (Optional) Maximum budget for this key. This sets an upper limit on the total spend allowed for this key. + +* `user_id` - (Optional) User ID associated with this key. This links the key to a specific user in the LiteLLM system. + +* `team_id` - (Optional) Team ID associated with this key. This links the key to a specific team in the LiteLLM system. + +* `max_parallel_requests` - (Optional) Maximum number of parallel requests allowed for this key. This helps in controlling concurrent usage. + +* `metadata` - (Optional) Metadata associated with this key. This can be used to store additional, custom information about the key. + +* `tpm_limit` - (Optional) Tokens per minute limit for this key. This sets a rate limit based on the number of tokens processed. + +* `rpm_limit` - (Optional) Requests per minute limit for this key. This sets a rate limit based on the number of API calls. + +* `budget_duration` - (Optional) Duration for the budget (e.g., "monthly", "weekly"). This defines the time period for which the `max_budget` applies. + +* `allowed_cache_controls` - (Optional) List of allowed cache control directives. This can be used to control caching behavior for requests made with this key. + +* `soft_budget` - (Optional) Soft budget limit for this key. This can be used to set a warning threshold before reaching the `max_budget`. + +* `key_alias` - (Optional) Alias for this key. This provides a human-readable identifier for the key. + +* `duration` - (Optional) Duration for which this key is valid. This sets an expiration time for the key. + +* `aliases` - (Optional) Map of model aliases. This allows you to create custom names for models when using this key. + +* `config` - (Optional) Configuration options for this key. This can be used to set key-specific settings. + +* `permissions` - (Optional) Permissions associated with this key. This defines what actions are allowed with this key. + +* `model_max_budget` - (Optional) Maximum budget per model. This allows setting different budget limits for each model. + +* `model_rpm_limit` - (Optional) Requests per minute limit per model. This allows setting different RPM limits for each model. + +* `model_tpm_limit` - (Optional) Tokens per minute limit per model. This allows setting different TPM limits for each model. + +* `guardrails` - (Optional) List of guardrails applied to this key. This can be used to enforce certain safety or quality checks. + +* `blocked` - (Optional) Whether this key is blocked. If set to true, the key will be unable to make any requests. + +* `tags` - (Optional) List of tags associated with this key. This can be used for organization and filtering of keys. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `key` - The generated API key. This is the actual key value that will be used for authentication. + +* `spend` - The current spend for this key. This reflects the total amount spent using this key so far. + +## State Management + +Recent updates have improved how the Key resource manages its state. The provider now ensures that all non-zero and non-empty values are correctly persisted in the Terraform state file. This means that any value you set will be accurately reflected in your state, preventing unnecessary updates and ensuring consistency between your configuration and the actual resource state. + +## Import + +LiteLLM keys can be imported using the `id`, e.g., + +``` +$ terraform import litellm_key.example 12345 +``` + +This allows you to import existing keys into your Terraform state, enabling management of keys that were created outside of Terraform. diff --git a/terraform/provider/docs/resources/mcp_server.md b/terraform/provider/docs/resources/mcp_server.md new file mode 100644 index 00000000000..77457a5ae55 --- /dev/null +++ b/terraform/provider/docs/resources/mcp_server.md @@ -0,0 +1,217 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "litellm_mcp_server Resource - terraform-provider-litellm" +subcategory: "" +description: |- + Manages an MCP (Model Context Protocol) server in LiteLLM. +--- + +# litellm_mcp_server (Resource) + +Manages an MCP (Model Context Protocol) server in LiteLLM. MCP servers provide tools and resources that can be used by LLMs through the LiteLLM proxy. + +## Example Usage + +### Basic HTTP MCP Server + +```terraform +resource "litellm_mcp_server" "github_server" { + server_name = "github-mcp-server" + alias = "github" + description = "GitHub MCP server for repository operations" + url = "https://api.github.com/mcp" + transport = "http" + auth_type = "bearer" + + mcp_access_groups = ["dev_team", "devops_team"] +} +``` + +### SSE MCP Server with Comprehensive Cost Tracking + +```terraform +resource "litellm_mcp_server" "zapier_server" { + server_name = "zapier-automation" + alias = "zapier" + description = "Zapier MCP server for workflow automation" + url = "https://actions.zapier.com/mcp/sk-xxxxx/sse" + transport = "sse" + auth_type = "bearer" + spec_version = "2024-11-05" + + mcp_access_groups = ["automation_team", "marketing_team"] + + mcp_info { + server_name = "Zapier Integration Server" + description = "Provides automation tools through Zapier's MCP interface" + logo_url = "https://zapier.com/assets/images/zapier-logo.png" + + mcp_server_cost_info { + default_cost_per_query = 0.01 + + tool_name_to_cost_per_query = { + "send_email" = 0.05 + "create_document" = 0.03 + "update_spreadsheet" = 0.02 + "post_to_slack" = 0.01 + "create_calendar_event" = 0.04 + } + } + } +} +``` + +### Stdio MCP Server for Local Development + +```terraform +resource "litellm_mcp_server" "local_dev_server" { + server_name = "local-development-tools" + alias = "local-dev" + description = "Local MCP server for development tools" + url = "stdio://local-dev" + transport = "stdio" + auth_type = "none" + + command = "python3" + args = ["/opt/mcp-servers/dev-tools/server.py", "--verbose"] + + env = { + "PYTHONPATH" = "/opt/mcp-servers/dev-tools" + "DEBUG" = "true" + "LOG_LEVEL" = "info" + "WORKSPACE_DIR" = "/workspace" + } + + mcp_access_groups = ["local_developers"] + + mcp_info { + server_name = "Development Tools" + description = "Local development utilities and tools" + + mcp_server_cost_info { + default_cost_per_query = 0.0 # Free for local development + } + } +} +``` + +### Enterprise MCP Server with Full Configuration + +```terraform +resource "litellm_mcp_server" "enterprise_api_server" { + server_name = "enterprise-api-gateway" + alias = "enterprise" + description = "Enterprise API gateway MCP server" + url = "https://api.enterprise.com/mcp/v1" + transport = "http" + auth_type = "bearer" + spec_version = "2024-11-05" + + mcp_access_groups = [ + "enterprise_users", + "api_consumers", + "integration_team" + ] + + mcp_info { + server_name = "Enterprise API Gateway" + description = "Provides access to enterprise APIs and services" + logo_url = "https://enterprise.com/logo.png" + + mcp_server_cost_info { + default_cost_per_query = 0.10 + + tool_name_to_cost_per_query = { + "query_database" = 0.25 + "generate_report" = 0.50 + "send_notification" = 0.05 + "create_user" = 0.15 + "update_permissions" = 0.20 + "audit_log_query" = 0.30 + } + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +### Required Arguments + +* `server_name` - (Required) Name of the MCP server. +* `url` - (Required) URL of the MCP server. For stdio transport, use `stdio://` prefix. +* `transport` - (Required) Transport type for the MCP server. Valid values: `http`, `sse`, `stdio`. + +### Optional Arguments + +* `alias` - (Optional) Alias for the MCP server. Used for easier reference. +* `description` - (Optional) Description of the MCP server. +* `spec_version` - (Optional) MCP specification version. Defaults to `2024-11-05`. +* `auth_type` - (Optional) Authentication type. Valid values: `none`, `bearer`, `basic`. Defaults to `none`. +* `mcp_access_groups` - (Optional) List of access groups that can use this MCP server. +* `command` - (Optional) Command to run for stdio transport. +* `args` - (Optional) List of arguments for the command (stdio transport only). Do not pass secrets as arguments; args are shown in plans, stored unencrypted in state, and visible in the server's process list. +* `env` - (Optional, Sensitive) Map of environment variables for the command (stdio transport only). Hidden from plan output but still stored unencrypted in state; secure your state backend when configuring tokens here. + +### MCP Info Block + +The `mcp_info` block supports: + +* `server_name` - (Optional) Server name in MCP info. +* `description` - (Optional) Description in MCP info. +* `logo_url` - (Optional) Logo URL for the MCP server. + +#### MCP Server Cost Info Block + +The `mcp_server_cost_info` block within `mcp_info` supports: + +* `default_cost_per_query` - (Optional) Default cost per query for all tools. +* `tool_name_to_cost_per_query` - (Optional) Map of specific tool names to their cost per query. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `server_id` - Unique identifier for the MCP server. +* `created_at` - Timestamp when the server was created. +* `created_by` - User who created the server. +* `updated_at` - Timestamp when the server was last updated. +* `updated_by` - User who last updated the server. +* `status` - Current status of the MCP server. +* `last_health_check` - Timestamp of the last health check. +* `health_check_error` - Error message from the last health check, if any. + +## Import + +MCP servers can be imported using their server ID: + +```shell +terraform import litellm_mcp_server.example server-id-here +``` + +## Transport Types + +### HTTP Transport +- Standard HTTP/HTTPS communication +- Suitable for REST API-based MCP servers +- Supports authentication via `auth_type` + +### SSE (Server-Sent Events) Transport +- Real-time streaming communication +- Ideal for servers that need to push updates +- Commonly used with services like Zapier + +### Stdio Transport +- Standard input/output communication +- Used for local MCP servers or command-line tools +- Requires `command` and optionally `args` and `env` + +## Access Control + +Use `mcp_access_groups` to control which teams or users can access the MCP server tools. This integrates with LiteLLM's permission management system. + +## Cost Tracking + +Configure cost tracking through the `mcp_info.mcp_server_cost_info` block to monitor and control spending on MCP tool usage. diff --git a/terraform/provider/docs/resources/model.md b/terraform/provider/docs/resources/model.md new file mode 100644 index 00000000000..5a46fe2f073 --- /dev/null +++ b/terraform/provider/docs/resources/model.md @@ -0,0 +1,238 @@ +# litellm_model Resource + +Manages a LiteLLM model configuration. This resource allows you to create, update, and delete model configurations in your LiteLLM instance. + +## Example Usage + +### Basic OpenAI Model + +```hcl +resource "litellm_model" "gpt4" { + model_name = "gpt-4-proxy" + custom_llm_provider = "openai" + model_api_key = var.openai_api_key + base_model = "gpt-4" + tier = "paid" + mode = "chat" + + input_cost_per_million_tokens = 30.0 + output_cost_per_million_tokens = 60.0 +} +``` + +### Advanced Model with All Features + +```hcl +resource "litellm_model" "advanced_gpt4" { + model_name = "gpt-4-advanced" + custom_llm_provider = "openai" + model_api_key = var.openai_api_key + model_api_base = "https://api.openai.com/v1" + api_version = "2023-05-15" + base_model = "gpt-4" + tier = "paid" + team_id = "team-123" + mode = "chat" + reasoning_effort = "medium" + thinking_enabled = true + thinking_budget_tokens = 1024 + merge_reasoning_content_in_choices = true + tpm = 100000 + rpm = 1000 + + # Cost configuration (per million tokens) + input_cost_per_million_tokens = 30.0 # $0.03 per 1k tokens = $30 per million + output_cost_per_million_tokens = 60.0 # $0.06 per 1k tokens = $60 per million +} +``` + +### AWS Bedrock Model with Cross-Account Access + +```hcl +resource "litellm_model" "bedrock_claude" { + model_name = "bedrock-claude-proxy" + custom_llm_provider = "bedrock" + base_model = "anthropic.claude-3-sonnet-20240229-v1:0" + tier = "paid" + mode = "chat" + + # AWS configuration with cross-account access + aws_access_key_id = var.aws_access_key_id + aws_secret_access_key = var.aws_secret_access_key + aws_region_name = "us-east-1" + aws_session_name = "litellm-cross-account-session" + aws_role_name = "arn:aws:iam::123456789012:role/LiteLLMCrossAccountRole" + + input_cost_per_million_tokens = 3.0 + output_cost_per_million_tokens = 15.0 +} +``` + +### Anthropic Model + +```hcl +resource "litellm_model" "claude" { + model_name = "claude-proxy" + custom_llm_provider = "anthropic" + model_api_key = var.anthropic_api_key + base_model = "claude-3-sonnet-20240229" + tier = "paid" + mode = "chat" + + input_cost_per_million_tokens = 3.0 + output_cost_per_million_tokens = 15.0 +} +``` + +### Azure OpenAI Model + +```hcl +resource "litellm_model" "azure_gpt4" { + model_name = "azure-gpt4-proxy" + custom_llm_provider = "azure" + model_api_key = var.azure_openai_key + model_api_base = var.azure_openai_endpoint + api_version = "2023-12-01-preview" + base_model = "gpt-4" + tier = "paid" + mode = "chat" + + input_cost_per_million_tokens = 30.0 + output_cost_per_million_tokens = 60.0 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `model_name` - (Required) string. The name of the model configuration used to identify the model in API calls. + +* `custom_llm_provider` - (Required) string. The LLM provider for this model (e.g., "openai", "anthropic", "azure", "bedrock"). + +* `model_api_key` - (Optional) string (Sensitive). The API key for the underlying model provider. Sensitive attributes are hidden from Terraform output but still stored in plaintext in the state file; prefer storing provider secrets in a `litellm_credential` and referencing it via `litellm_credential_name`, and secure your state backend. + +* `model_api_base` - (Optional) string. The base URL for the model provider's API. + +* `api_version` - (Optional) string. The API version to use for the model provider. + +* `base_model` - (Required) string. The actual model identifier from the provider (e.g., "gpt-4", "claude-2"). + +* `litellm_credential_name` - (Optional) string. Name of a LiteLLM credential to use for this model. + +* `tier` - (Optional) string. The usage tier for this model. Valid values are `"free"` or `"paid"`. Default: `"free"`. + +* `team_id` - (Optional) string. Associate the model with a specific team. + +* `mode` - (Optional) string. The intended use of the model. Valid values are: + * `completion` + * `embedding` + * `image_generation` + * `chat` + * `moderation` + * `audio_transcription` + * `audio_speech` + * `rerank` + +* `tpm` - (Optional) integer. Tokens per minute limit for this model. + +* `rpm` - (Optional) integer. Requests per minute limit for this model. + +* `reasoning_effort` - (Optional) string. Configures the model's reasoning effort level. Valid values are: + * `low` + * `medium` + * `high` + +* `thinking_enabled` - (Optional) boolean. Enables the model's thinking capability. Default: `false`. + +* `thinking_budget_tokens` - (Optional) integer. Sets the token budget for the model's thinking capability. Default: `1024`. Note: this field is only relevant when `thinking_enabled = true`. + +* `merge_reasoning_content_in_choices` - (Optional) boolean. When set to `true`, merges reasoning content into the model's choices. + +* `input_cost_per_million_tokens` - (Optional) float. Cost per million input tokens. The provider converts this to a per-token cost sent to the API. + +* `output_cost_per_million_tokens` - (Optional) float. Cost per million output tokens. The provider converts this to a per-token cost sent to the API. + +* `input_cost_per_pixel` - (Optional) float. Cost applied per input pixel for models that charge by image size. + +* `output_cost_per_pixel` - (Optional) float. Cost applied per output pixel for image-generation models. + +* `input_cost_per_second` - (Optional) float. Cost applied per input second for audio/transcription models. + +* `output_cost_per_second` - (Optional) float. Cost applied per output second for audio/transcription models. + +* `vertex_project` - (Optional) string. Vertex AI project id (for `custom_llm_provider = "vertex"`). + +* `vertex_location` - (Optional) string. Vertex AI location (e.g., `us-central1`). + +* `vertex_credentials` - (Optional) string. Vertex credentials (JSON string or path depending on your setup). + +* `additional_litellm_params` - (Optional) map(string). A map of arbitrary additional parameters that will be merged into the `litellm_params` object sent to the LiteLLM API. This is intended for provider-specific or experimental options not exposed as dedicated arguments. + + Conversion and behavior rules (how the provider handles values): + * When values in the map are strings the provider will attempt to coerce them: + * `"true"` / `"false"` (strings) -> boolean true / false + * Numeric strings are parsed first as integers; if integer parsing fails, parsed as floats (e.g., `"16384"` -> 16384, `"0.75"` -> 0.75) + * JSON strings (starting with `[` or `{`) are parsed as JSON objects/arrays + * Non-convertible strings remain strings + * Non-string map values (if supplied) are passed through unchanged. + * The provider merges these keys into the `litellm_params` payload sent to the API. + * Note: the remote API may not echo back all custom parameters; this provider preserves `additional_litellm_params` in state when present in configuration. + + **Special parameter: `additional_drop_params`** + * When `additional_drop_params` is provided as a JSON array string, it specifies parameters to remove from the final `litellm_params` before sending to the API + * This allows you to override or remove built-in parameters if needed + * The `additional_drop_params` key itself is not included in the final parameters + + Example showing booleans, integers, floats, strings, and parameter dropping: + + ```hcl + resource "litellm_model" "with_additional" { + model_name = "custom-model" + custom_llm_provider = "openai" + model_api_key = var.openai_api_key + base_model = "gpt-4" + mode = "chat" + + additional_litellm_params = { + "use_fine_tune" = "true" # becomes boolean true + "max_context" = "16384" # becomes integer 16384 + "scale" = "0.75" # becomes float 0.75 + "note" = "for testing" # stays string + "complex_config" = "{\"nested\": {\"value\": 42}}" # parsed as JSON object + "additional_drop_params" = "[\"reasoningEffort\"]" # removes reasoningEffort parameter + } + } + ``` + +### AWS-specific Configuration + +* `aws_access_key_id` - (Optional) string (Sensitive). AWS access key ID for AWS-based models. + +* `aws_secret_access_key` - (Optional) string (Sensitive). AWS secret access key for AWS-based models. As with `model_api_key`, the value is stored in plaintext in the state file; prefer a `litellm_credential` referenced via `litellm_credential_name` and secure your state backend. + +* `aws_region_name` - (Optional) string. AWS region name for AWS-based models. + +* `aws_session_name` - (Optional) string (Sensitive). AWS session name for cross-account access scenarios. + +* `aws_role_name` - (Optional) string (Sensitive). AWS IAM role name for cross-account access scenarios. + +## Attribute Reference + +In addition to the arguments above, the following attributes are exported: + +* `id` - The ID of the model configuration. + +## Import + +Model configurations can be imported using the model ID: + +```shell +terraform import litellm_model.gpt4 +``` + +Note: The model ID is generated when the model is created and is different from the `model_name`. + +## Security Note + +When using this resource, ensure that sensitive information such as API keys and AWS credentials are stored securely. It's recommended to use environment variables or a secure secret management solution rather than hardcoding these values in your Terraform configuration files. diff --git a/terraform/provider/docs/resources/team.md b/terraform/provider/docs/resources/team.md new file mode 100644 index 00000000000..68535309f10 --- /dev/null +++ b/terraform/provider/docs/resources/team.md @@ -0,0 +1,130 @@ +# litellm_team Resource + +Manages a team configuration in LiteLLM. Teams allow you to group users and manage their access to models and usage limits. + +## Example Usage + +### Basic Team Configuration + +```hcl +resource "litellm_team" "engineering" { + team_alias = "engineering-team" + models = ["gpt-4-proxy", "claude-2"] + max_budget = 1000.0 +} +``` + +### Team with Comprehensive Configuration + +```hcl +resource "litellm_team" "advanced_team" { + team_alias = "ai-research-team" + organization_id = "org_123456" + models = ["gpt-4-proxy", "claude-2", "gpt-3.5-turbo"] + + # Budget and rate limiting + max_budget = 1000.0 + budget_duration = "1mo" + tpm_limit = 500000 + rpm_limit = 5000 + blocked = false + + # Team member permissions + team_member_permissions = [ + "create_key", + "delete_key", + "view_spend", + "edit_team" + ] + + # Metadata for organization + metadata = { + department = "Engineering" + project = "AI Research" + cost_center = "R&D-001" + } +} +``` + +### Team with Model Dependencies + +```hcl +# First create models +resource "litellm_model" "gpt4" { + model_name = "gpt-4-proxy" + custom_llm_provider = "openai" + base_model = "gpt-4" + model_api_key = var.openai_api_key +} + +resource "litellm_model" "claude" { + model_name = "claude-proxy" + custom_llm_provider = "anthropic" + base_model = "claude-3-sonnet-20240229" + model_api_key = var.anthropic_api_key +} + +# Then create team with access to these models +resource "litellm_team" "model_dependent_team" { + team_alias = "model-users" + models = [ + litellm_model.gpt4.model_name, + litellm_model.claude.model_name + ] + + max_budget = 500.0 + budget_duration = "1mo" + + team_member_permissions = [ + "view_spend" + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `team_alias` - (Required) A human-readable identifier for the team. + +* `organization_id` - (Optional) The ID of the organization this team belongs to. + +* `models` - (Optional) List of model names that this team can access. + +* `metadata` - (Optional) A map of metadata key-value pairs associated with the team. + +* `blocked` - (Optional) Whether the team is blocked from making requests. Default is `false`. + +* `tpm_limit` - (Optional) Team-wide tokens per minute limit. + +* `rpm_limit` - (Optional) Team-wide requests per minute limit. + +* `max_budget` - (Optional) Maximum budget allocated to the team. + +* `budget_duration` - (Optional) Duration for the budget cycle. Valid values are: + * `daily` + * `weekly` + * `monthly` + * `yearly` + +* `team_member_permissions` - (Optional) List of permissions granted to team members. This controls what actions team members can perform within the team context. + +## Attribute Reference + +In addition to the arguments above, the following attributes are exported: + +* `id` - The unique identifier for the team. + +## Import + +Teams can be imported using the team ID: + +```shell +terraform import litellm_team.engineering +``` + +Note: The team ID is generated when the team is created and is different from the `team_alias`. + +## Note on Team Members + +Team members are managed through the separate `litellm_team_member` resource. This allows for more granular control over team membership and permissions. See the `litellm_team_member` resource documentation for details on managing team members. diff --git a/terraform/provider/docs/resources/team_member.md b/terraform/provider/docs/resources/team_member.md new file mode 100644 index 00000000000..426d8b8892f --- /dev/null +++ b/terraform/provider/docs/resources/team_member.md @@ -0,0 +1,54 @@ +# litellm_team_member Resource + +Manages individual team member configurations in LiteLLM. This resource allows you to add, update, and remove team members with specific permissions and budget limits. + +## Example Usage + +```hcl +resource "litellm_team_member" "engineer" { + team_id = litellm_team.engineering.id + user_id = "user_3" + user_email = "engineer@example.com" + role = "user" + max_budget_in_team = 200.0 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `team_id` - (Required) The ID of the team this member belongs to. + +* `user_id` - (Required) Unique identifier for the user. + +* `user_email` - (Required) Email address of the user. + +* `role` - (Required) The role of the team member. Valid values are: + * `org_admin` + * `internal_user` + * `internal_user_viewer` + * `admin` + * `user` + +* `max_budget_in_team` - (Optional) Maximum budget allocated to this team member within the team's budget. + +## Attribute Reference + +In addition to the arguments above, the following attributes are exported: + +* `id` - The unique identifier for the team member configuration. This is typically a composite of the team_id and user_id. + +## Import + +Team members can be imported using the format `team_id:user_id`: + +```shell +terraform import litellm_team_member.engineer : +``` + +Note: The team_id and user_id should match the values used in the resource configuration. + +## Security Note + +Ensure that sensitive information such as user emails and IDs are handled securely. It's recommended to use variables or a secure secret management solution rather than hardcoding these values in your Terraform configuration files. diff --git a/terraform/provider/docs/resources/team_member_add.md b/terraform/provider/docs/resources/team_member_add.md new file mode 100644 index 00000000000..f5398e49d9c --- /dev/null +++ b/terraform/provider/docs/resources/team_member_add.md @@ -0,0 +1,161 @@ +# Resource: litellm_team_member_add + +Add multiple members to a team with a single resource. This resource efficiently manages team members by using the appropriate API endpoints for each operation: + +- **Adding new members**: Uses `/team/member_add` endpoint +- **Updating existing members**: Uses `/team/member_update` endpoint (preserves member identity) +- **Removing members**: Uses `/team/member_delete` endpoint + +When you modify an existing team member's attributes (like role), the resource will update the member in-place rather than deleting and re-adding them. + +## Example Usage + +### Basic Usage + +```hcl +resource "litellm_team_member_add" "example" { + team_id = "team-123" + + member { + user_id = "user-456" + role = "admin" + } + + member { + user_email = "user@example.com" + role = "user" + } + + max_budget_in_team = 100.0 +} +``` + +### Complete Team Setup with Members + +```hcl +# First create a team +resource "litellm_team" "development" { + team_alias = "development-team" + max_budget = 500.0 + models = ["gpt-4", "gpt-3.5-turbo"] + + team_member_permissions = [ + "create_key", + "view_spend" + ] +} + +# Add members to the team +resource "litellm_team_member_add" "dev_team_members" { + team_id = litellm_team.development.id + + # Team lead with admin role + member { + user_email = "team-lead@company.com" + role = "admin" + } + + # Regular developers + member { + user_email = "developer1@company.com" + role = "user" + } + + member { + user_email = "developer2@company.com" + role = "user" + } + + member { + user_id = "existing-user-123" + role = "user" + } + + # Budget per member + max_budget_in_team = 100.0 +} +``` + +### Dynamic Members Using Locals + +```hcl +locals { + team_members = [ + { + user_id = "user-123" + role = "admin" + }, + { + user_email = "developer1@company.com" + role = "user" + }, + { + user_email = "developer2@company.com" + role = "user" + } + ] +} + +resource "litellm_team_member_add" "dynamic_example" { + team_id = "team-456" + + dynamic "member" { + for_each = local.team_members + content { + user_id = lookup(member.value, "user_id", null) + user_email = lookup(member.value, "user_email", null) + role = member.value.role + } + } + + max_budget_in_team = 200.0 +} +``` + +### Budget Update Example + +```hcl +# This example demonstrates how budget updates work correctly +resource "litellm_team_member_add" "budget_example" { + team_id = litellm_team.example.id + + # Initial budget of $100 per member + max_budget_in_team = 100.0 + + member { + user_email = "user1@example.com" + role = "admin" + } + + member { + user_email = "user2@example.com" + role = "user" + } + + member { + user_id = "user123" + role = "user" + } +} + +# To update the budget: +# 1. Change max_budget_in_team from 100.0 to 120.0 +# 2. Run terraform plan - it will show the budget change +# 3. Run terraform apply - all existing members will be updated with the new budget +``` + +## Argument Reference + +* `team_id` - (Required) The ID of the team to add members to. +* `member` - (Required) One or more member blocks defining team members. Each block supports: + * `user_id` - (Optional) The ID of the user to add to the team. + * `user_email` - (Optional) The email of the user to add to the team. + * `role` - (Required) The role of the user in the team. Must be one of: "admin" or "user". +* `max_budget_in_team` - (Optional) The maximum budget allocated for the team members. + +## Import + +Team members can be imported using a composite ID of the team ID and user ID: + +```shell +terraform import litellm_team_member_add.example team-123:user-456 diff --git a/terraform/provider/docs/resources/vector_store.md b/terraform/provider/docs/resources/vector_store.md new file mode 100644 index 00000000000..b839b327429 --- /dev/null +++ b/terraform/provider/docs/resources/vector_store.md @@ -0,0 +1,274 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "litellm_vector_store Resource - terraform-provider-litellm" +subcategory: "" +description: |- + Manages a LiteLLM vector store for storing and retrieving vector embeddings. +--- + +# litellm_vector_store (Resource) + +Manages a LiteLLM vector store for storing and retrieving vector embeddings. Vector stores enable semantic search and retrieval-augmented generation (RAG) capabilities using officially supported providers including AWS Bedrock Knowledge Bases, OpenAI Vector Stores, Azure Vector Stores, Vertex AI RAG Engine, and PG Vector. + +## Example Usage + +### AWS Bedrock Knowledge Base + +```terraform +resource "litellm_credential" "bedrock_cred" { + credential_name = "bedrock-knowledge-base" + + credential_info = { + provider = "bedrock" + region = "us-east-1" + } + + credential_values = { + aws_access_key_id = var.aws_access_key_id + aws_secret_access_key = var.aws_secret_access_key + aws_region = "us-east-1" + } +} + +resource "litellm_vector_store" "bedrock_kb" { + vector_store_name = "bedrock-litellm-website-knowledgebase" + custom_llm_provider = "bedrock" + litellm_credential_name = litellm_credential.bedrock_cred.credential_name + + vector_store_description = "Bedrock vector store for the LiteLLM website knowledgebase" + + vector_store_metadata = { + source = "https://www.litellm.com/docs" + } + + litellm_params = { + vector_store_id = "T37J8R4WTM" + } +} +``` + +### OpenAI Vector Store + +```terraform +resource "litellm_credential" "openai_cred" { + credential_name = "openai-vector-store" + + credential_info = { + provider = "openai" + } + + credential_values = { + api_key = var.openai_api_key + } +} + +resource "litellm_vector_store" "openai_store" { + vector_store_name = "openai-knowledge-base" + custom_llm_provider = "openai" + litellm_credential_name = litellm_credential.openai_cred.credential_name + + vector_store_description = "OpenAI vector store for document search" + + vector_store_metadata = { + environment = "production" + purpose = "file-search" + } + + litellm_params = { + vector_store_id = "vs_687ae3b2439881918b433cb99d10662e" + } +} +``` + +### Azure Vector Store + +```terraform +resource "litellm_credential" "azure_cred" { + credential_name = "azure-vector-store" + + credential_info = { + provider = "azure" + } + + credential_values = { + api_key = var.azure_openai_key + api_base = var.azure_openai_endpoint + api_version = "2023-12-01-preview" + } +} + +resource "litellm_vector_store" "azure_store" { + vector_store_name = "azure-knowledge-base" + custom_llm_provider = "azure" + litellm_credential_name = litellm_credential.azure_cred.credential_name + + vector_store_description = "Azure vector store for enterprise search" + + vector_store_metadata = { + environment = "production" + team = "enterprise" + } + + litellm_params = { + vector_store_id = "vs_azure_example_id" + } +} +``` + +### Vertex AI RAG Engine + +```terraform +resource "litellm_credential" "vertex_cred" { + credential_name = "vertex-rag-engine" + + credential_info = { + provider = "vertex_ai" + project = "your-gcp-project" + } + + credential_values = { + service_account_key = var.gcp_service_account_key + } +} + +resource "litellm_vector_store" "vertex_rag" { + vector_store_name = "vertex-rag-corpus" + custom_llm_provider = "vertex_ai" + litellm_credential_name = litellm_credential.vertex_cred.credential_name + + vector_store_description = "Vertex AI RAG Engine for enterprise knowledge" + + vector_store_metadata = { + project = "your-gcp-project" + environment = "production" + } + + litellm_params = { + vector_store_id = "6917529027641081856" + } +} +``` + +### PG Vector Store + +```terraform +resource "litellm_credential" "pgvector_cred" { + credential_name = "pgvector-store" + + credential_info = { + provider = "pgvector" + host = "your-pgvector-host.com" + } + + credential_values = { + api_key = var.pgvector_api_key + api_base = "https://your-pgvector-host.com" + } +} + +resource "litellm_vector_store" "pgvector_store" { + vector_store_name = "postgres-vector-store" + custom_llm_provider = "pgvector" + litellm_credential_name = litellm_credential.pgvector_cred.credential_name + + vector_store_description = "PostgreSQL vector store with pgvector extension" + + vector_store_metadata = { + database = "vector_db" + table = "embeddings" + environment = "production" + } + + litellm_params = { + api_base = "https://your-pgvector-host.com" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vector_store_name` - (Required) Name of the vector store. +* `custom_llm_provider` - (Required) The vector store provider. Supported values: "bedrock", "openai", "azure", "vertex_ai", "pgvector". +* `vector_store_description` - (Optional) Description of the vector store. +* `vector_store_metadata` - (Optional) Map of metadata associated with the vector store. +* `litellm_credential_name` - (Optional) Name of the LiteLLM credential to use for authentication. +* `litellm_params` - (Optional, Sensitive) Map of additional parameters specific to the vector store provider. Do not put API keys or other secrets here; this map is stored unencrypted in state. Store secrets in a `litellm_credential` and reference it via `litellm_credential_name`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `vector_store_id` - The unique identifier of the vector store. +* `created_at` - Timestamp when the vector store was created. +* `updated_at` - Timestamp when the vector store was last updated. + +## Supported Providers + +The following vector store providers are officially supported by LiteLLM: + +* **AWS Bedrock Knowledge Bases** - Managed knowledge bases on AWS Bedrock +* **OpenAI Vector Stores** - OpenAI's native vector store service +* **Azure Vector Stores** - Azure OpenAI vector store integration +* **Vertex AI RAG Engine** - Google Cloud's RAG API for vector search +* **PG Vector** - PostgreSQL with pgvector extension + +## Provider-Specific Parameters + +### AWS Bedrock Knowledge Base + +```terraform +litellm_params = { + vector_store_id = "T37J8R4WTM" # Your Bedrock Knowledge Base ID +} +``` + +### OpenAI Vector Store + +```terraform +litellm_params = { + vector_store_id = "vs_687ae3b2439881918b433cb99d10662e" # Your OpenAI Vector Store ID +} +``` + +### Azure Vector Store + +```terraform +litellm_params = { + vector_store_id = "vs_azure_example_id" # Your Azure Vector Store ID +} +``` + +### Vertex AI RAG Engine + +```terraform +litellm_params = { + vector_store_id = "6917529027641081856" # Your Vertex AI RAG Engine ID +} +``` + +### PG Vector + +```terraform +litellm_params = { + api_base = "https://your-pgvector-host.com" +} +``` + +## Import + +Vector stores can be imported using their ID: + +```shell +terraform import litellm_vector_store.example "vector-store-id" +``` + +## Notes + +* Vector stores require appropriate credentials for the chosen provider. +* The `litellm_params` field allows provider-specific configuration. +* Some providers may require additional setup outside of Terraform (e.g., creating Knowledge Bases in AWS Bedrock, Vector Stores in OpenAI). +* Ensure your vector store provider is properly configured and accessible from your LiteLLM instance. +* Only the officially supported providers listed above are guaranteed to work with LiteLLM's vector store integration. +* For the most up-to-date list of supported providers, refer to the [LiteLLM documentation](https://docs.litellm.ai/docs/completion/knowledgebase). diff --git a/terraform/provider/examples/model_additional_params.tf b/terraform/provider/examples/model_additional_params.tf new file mode 100644 index 00000000000..fb4981ec6fb --- /dev/null +++ b/terraform/provider/examples/model_additional_params.tf @@ -0,0 +1,57 @@ +provider "litellm" { + api_base = "https://your-litellm-proxy.com" + api_key = var.litellm_api_key +} + +# Example: using additional_litellm_params to pass provider-specific options. +# Notes: +# - String values "true"/"false" will be coerced to booleans. +# - Numeric strings will be parsed to integer (if possible) otherwise float. +# - JSON strings (starting with [ or {) will be parsed as JSON objects/arrays. +# - Non-convertible strings remain strings. +# - Non-string map values are passed through unchanged. +# - Use "additional_drop_params" as a JSON array to remove parameters from the final request. + +resource "litellm_model" "with_additional" { + model_name = "custom-model" + custom_llm_provider = "openai" + model_api_key = var.openai_api_key + base_model = "gpt-4" + mode = "chat" + + # Additional parameters not exposed as first-class arguments + additional_litellm_params = { + "use_fine_tune" = "true" # becomes boolean true + "max_context" = "16384" # becomes integer 16384 + "temperature_scale" = "0.75" # becomes float 0.75 + "experimental_feature" = "enabled" # stays string "enabled" + "complex_config" = "{\"nested\": {\"value\": 42}}" # parsed as JSON object + "additional_drop_params" = "[\"reasoningEffort\"]" # removes reasoningEffort parameter + # You may also pass non-string values (they will be passed through unchanged) + # "raw_flag" = true + } + + # Cost configuration (optional) + input_cost_per_million_tokens = 30.0 + output_cost_per_million_tokens = 60.0 +} + +# Example: Azure model with parameter dropping +resource "litellm_model" "azure_with_drop_params" { + model_name = "gpt-5-mini-coder" + custom_llm_provider = "azure" + model_api_key = "your-azure-api-key" + model_api_base = "https://your-azure-endpoint.openai.azure.com/" + api_version = "2025-03-01-preview" + base_model = "gpt-5-mini" + tier = "paid" + mode = "completion" + + # Drop the reasoningEffort parameter that might be automatically added + additional_litellm_params = { + "additional_drop_params" = "[\"reasoningEffort\"]" + } + + input_cost_per_million_tokens = 0.25 + output_cost_per_million_tokens = 2.00 +} diff --git a/terraform/provider/go.mod b/terraform/provider/go.mod new file mode 100644 index 00000000000..899af1a6fbe --- /dev/null +++ b/terraform/provider/go.mod @@ -0,0 +1,61 @@ +module github.com/BerriAI/terraform-provider-litellm + +go 1.25.0 + +require ( + github.com/google/uuid v1.6.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0 +) + +require ( + github.com/ProtonMail/go-crypto v1.3.0 // indirect + github.com/agext/levenshtein v1.2.2 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/cloudflare/circl v1.6.1 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-checkpoint v0.5.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-cty v1.5.0 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.7.0 // indirect + github.com/hashicorp/go-retryablehttp v0.7.8 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect + github.com/hashicorp/hc-install v0.9.3 // indirect + github.com/hashicorp/hcl/v2 v2.24.0 // indirect + github.com/hashicorp/logutils v1.0.0 // indirect + github.com/hashicorp/terraform-exec v0.25.0 // indirect + github.com/hashicorp/terraform-json v0.27.2 // indirect + github.com/hashicorp/terraform-plugin-go v0.31.0 // indirect + github.com/hashicorp/terraform-plugin-log v0.10.0 // indirect + github.com/hashicorp/terraform-registry-address v0.4.0 // indirect + github.com/hashicorp/terraform-svchost v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zclconf/go-cty v1.17.0 // indirect + golang.org/x/crypto v0.48.0 // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/text v0.34.0 // indirect + golang.org/x/tools v0.41.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/grpc v1.79.2 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/terraform/provider/go.sum b/terraform/provider/go.sum new file mode 100644 index 00000000000..890703d4f8a --- /dev/null +++ b/terraform/provider/go.sum @@ -0,0 +1,239 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= +github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= +github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= +github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= +github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0= +github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hc-install v0.9.3 h1:1H4dgmgzxEVwT6E/d/vIL5ORGVKz9twRwDw+qA5Hyho= +github.com/hashicorp/hc-install v0.9.3/go.mod h1:FQlQ5I3I/X409N/J1U4pPeQQz1R3BoV0IysB7aiaQE0= +github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= +github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/terraform-exec v0.25.0 h1:Bkt6m3VkJqYh+laFMrWIpy9KHYFITpOyzRMNI35rNaY= +github.com/hashicorp/terraform-exec v0.25.0/go.mod h1:dl9IwsCfklDU6I4wq9/StFDp7dNbH/h5AnfS1RmiUl8= +github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU= +github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE= +github.com/hashicorp/terraform-plugin-go v0.31.0 h1:0Fz2r9DQ+kNNl6bx8HRxFd1TfMKUvnrOtvJPmp3Z0q8= +github.com/hashicorp/terraform-plugin-go v0.31.0/go.mod h1:A88bDhd/cW7FnwqxQRz3slT+QY6yzbHKc6AOTtmdeS8= +github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= +github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0 h1:MKS/2URqeJRwJdbOfcbdsZCq/IRrNkqJNN0GtVIsuGs= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0/go.mod h1:PuG4P97Ju3QXW6c6vRkRadWJbvnEu2Xh+oOuqcYOqX4= +github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= +github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0= +github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/terraform/provider/litellm/client.go b/terraform/provider/litellm/client.go new file mode 100644 index 00000000000..e0aba61477d --- /dev/null +++ b/terraform/provider/litellm/client.go @@ -0,0 +1,386 @@ +package litellm + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "regexp" + "strings" +) + +type Client struct { + APIBase string + APIKey string + httpClient *http.Client + InsecureSkipVerify bool +} + +func NewClient(apiBase, apiKey string, insecureSkipVerify bool) *Client { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify}, + } + + return &Client{ + APIBase: apiBase, + APIKey: apiKey, + httpClient: &http.Client{Transport: tr}, + InsecureSkipVerify: insecureSkipVerify, + } +} + +// Organization member methods +func (c *Client) AddOrganizationMember(data map[string]interface{}) (map[string]interface{}, error) { + return c.sendRequest("POST", "/organization/member_add", data) +} + +func (c *Client) UpdateOrganizationMember(data map[string]interface{}) (map[string]interface{}, error) { + return c.sendRequest("PATCH", "/organization/member_update", data) +} + +func (c *Client) DeleteOrganizationMember(data map[string]interface{}) (map[string]interface{}, error) { + return c.sendRequest("DELETE", "/organization/member_delete", data) +} + +// Key-related methods +func (c *Client) CreateKey(key *Key) (*Key, error) { + resp, err := c.sendRequest("POST", "/key/generate", key) + if err != nil { + return nil, err + } + + return c.parseKeyResponse(resp) +} + +func (c *Client) GetKey(keyID string) (*Key, error) { + resp, err := c.sendRequest("GET", fmt.Sprintf("/key/info?key=%s", keyID), nil) + if err != nil { + return nil, err + } + + return c.parseKeyResponse(resp) +} + +func (c *Client) UpdateKey(key *Key) (*Key, error) { + // Create a new map with only the fields that can be updated + updateData := map[string]interface{}{ + "key": key.Key, + "team_id": key.TeamID, + "metadata": key.Metadata, + "budget_duration": key.BudgetDuration, + "key_alias": key.KeyAlias, + "aliases": key.Aliases, + "permissions": key.Permissions, + "model_max_budget": key.ModelMaxBudget, + "model_rpm_limit": key.ModelRPMLimit, + "model_tpm_limit": key.ModelTPMLimit, + "blocked": key.Blocked, + } + + // Only add pointer fields if they are explicitly set + if key.MaxBudget != nil { + updateData["max_budget"] = *key.MaxBudget + } + if key.SoftBudget != nil { + updateData["soft_budget"] = *key.SoftBudget + } + if key.MaxParallelRequests != nil { + updateData["max_parallel_requests"] = *key.MaxParallelRequests + } + if key.TPMLimit != nil { + updateData["tpm_limit"] = *key.TPMLimit + } + if key.RPMLimit != nil { + updateData["rpm_limit"] = *key.RPMLimit + } + + // Only add array fields if they are non-empty + if len(key.Models) > 0 { + updateData["models"] = key.Models + } + if len(key.Guardrails) > 0 { + updateData["guardrails"] = key.Guardrails + } + if len(key.Tags) > 0 { + updateData["tags"] = key.Tags + } + + resp, err := c.sendRequest("POST", "/key/update", updateData) + if err != nil { + return nil, err + } + + return c.parseKeyResponse(resp) +} + +func (c *Client) DeleteKey(keyID string) error { + payload := map[string]interface{}{ + "keys": []string{keyID}, + } + _, err := c.sendRequest("POST", "/key/delete", payload) + return err +} + +func (c *Client) parseKeyResponse(resp map[string]interface{}) (*Key, error) { + if resp == nil { + return nil, fmt.Errorf("received nil response") + } + + createdKey := &Key{} + + for k, v := range resp { + if v == nil { + continue + } + + switch k { + case "key": + if s, ok := v.(string); ok { + createdKey.Key = s + } + case "token_id": + if s, ok := v.(string); ok { + createdKey.TokenID = s + } + case "models": + if models, ok := v.([]interface{}); ok { + createdKey.Models = make([]string, len(models)) + for i, model := range models { + if s, ok := model.(string); ok { + createdKey.Models[i] = s + } + } + } + case "spend": + if f, ok := v.(float64); ok { + createdKey.Spend = f + } + case "max_budget": + if f, ok := v.(float64); ok { + createdKey.MaxBudget = &f + } + case "user_id": + if s, ok := v.(string); ok { + createdKey.UserID = s + } + case "team_id": + if s, ok := v.(string); ok { + createdKey.TeamID = s + } + case "max_parallel_requests": + if i, ok := v.(float64); ok { + val := int(i) + createdKey.MaxParallelRequests = &val + } + case "metadata": + if m, ok := v.(map[string]interface{}); ok { + createdKey.Metadata = m + } + case "tpm_limit": + if i, ok := v.(float64); ok { + val := int(i) + createdKey.TPMLimit = &val + } + case "rpm_limit": + if i, ok := v.(float64); ok { + val := int(i) + createdKey.RPMLimit = &val + } + case "budget_duration": + if s, ok := v.(string); ok { + createdKey.BudgetDuration = s + } + case "soft_budget": + if f, ok := v.(float64); ok { + createdKey.SoftBudget = &f + } + case "key_alias": + if s, ok := v.(string); ok { + createdKey.KeyAlias = s + } + case "duration": + if s, ok := v.(string); ok { + createdKey.Duration = s + } + case "aliases": + if m, ok := v.(map[string]interface{}); ok { + createdKey.Aliases = m + } + case "config": + if m, ok := v.(map[string]interface{}); ok { + createdKey.Config = m + } + case "permissions": + if m, ok := v.(map[string]interface{}); ok { + createdKey.Permissions = m + } + case "model_max_budget": + if m, ok := v.(map[string]interface{}); ok { + createdKey.ModelMaxBudget = m + } + case "model_rpm_limit": + if m, ok := v.(map[string]interface{}); ok { + createdKey.ModelRPMLimit = m + } + case "model_tpm_limit": + if m, ok := v.(map[string]interface{}); ok { + createdKey.ModelTPMLimit = m + } + case "guardrails": + if guardrails, ok := v.([]interface{}); ok { + createdKey.Guardrails = make([]string, len(guardrails)) + for i, guardrail := range guardrails { + if s, ok := guardrail.(string); ok { + createdKey.Guardrails[i] = s + } + } + } + case "blocked": + if b, ok := v.(bool); ok { + createdKey.Blocked = b + } + case "tags": + if tags, ok := v.([]interface{}); ok { + createdKey.Tags = make([]string, len(tags)) + for i, tag := range tags { + if s, ok := tag.(string); ok { + createdKey.Tags[i] = s + } + } + } + } + } + + return createdKey, nil +} + +func (c *Client) sendRequest(method, path string, body interface{}) (map[string]interface{}, error) { + url := c.APIBase + path + + var req *http.Request + var err error + + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error marshaling request body: %v", err) + } + log.Printf("Making %s request to %s with body:\n%s", method, url, c.redactSensitiveData(string(jsonBody))) + req, err = http.NewRequest(method, url, bytes.NewBuffer(jsonBody)) + } else { + log.Printf("Making %s request to %s", method, url) + req, err = http.NewRequest(method, url, nil) + } + + if err != nil { + return nil, fmt.Errorf("error creating request: %v", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("x-api-key", c.APIKey) + req.Header.Set("accept", "application/json") + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error making request: %v", err) + } + defer resp.Body.Close() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %v", err) + } + + log.Printf("Response status: %d", resp.StatusCode) + log.Printf("Response body: %s", c.redactSensitiveData(string(bodyBytes))) + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API request failed with status code %d: %s", resp.StatusCode, string(bodyBytes)) + } + + var result map[string]interface{} + if err := json.Unmarshal(bodyBytes, &result); err != nil { + if (method == "POST" || method == "PATCH" || method == "PUT" || method == "DELETE") && + (len(bodyBytes) == 0 || string(bodyBytes) == "null") { + return make(map[string]interface{}), nil + } + return nil, fmt.Errorf("error parsing response JSON: %v\nResponse body: %s", err, string(bodyBytes)) + } + + return result, nil +} + +var sensitiveLogFields = map[string]bool{ + "api_key": true, + "key": true, + "token": true, + "password": true, + "secret": true, + "credential": true, + "auth": true, + "model_api_key": true, + "aws_access_key_id": true, + "aws_secret_access_key": true, + "vertex_credentials": true, + "x-api-key": true, + "credential_values": true, +} + +func redactJSONValue(value interface{}) interface{} { + switch typed := value.(type) { + case map[string]interface{}: + redacted := make(map[string]interface{}, len(typed)) + for k, v := range typed { + if sensitiveLogFields[k] { + redacted[k] = "[REDACTED]" + } else { + redacted[k] = redactJSONValue(v) + } + } + return redacted + case []interface{}: + redacted := make([]interface{}, len(typed)) + for i, v := range typed { + redacted[i] = redactJSONValue(v) + } + return redacted + default: + return value + } +} + +var sensitiveLogPatterns = []*regexp.Regexp{ + regexp.MustCompile(`"(api_key|key|token|password|secret|credential|auth)":\s*"[^"]*"`), + regexp.MustCompile(`"(model_api_key|aws_access_key_id|aws_secret_access_key|vertex_credentials)":\s*"[^"]*"`), + regexp.MustCompile(`"(x-api-key)":\s*"[^"]*"`), +} + +func redactWithPatterns(data string) string { + result := data + for _, re := range sensitiveLogPatterns { + result = re.ReplaceAllStringFunc(result, func(match string) string { + parts := strings.SplitN(match, ":", 2) + if len(parts) == 2 { + return parts[0] + `: "[REDACTED]"` + } + return "[REDACTED]" + }) + } + return result +} + +// redactSensitiveData masks sensitive information in logs +func (c *Client) redactSensitiveData(data string) string { + var parsed interface{} + if err := json.Unmarshal([]byte(data), &parsed); err != nil { + return redactWithPatterns(data) + } + redactedBytes, err := json.Marshal(redactJSONValue(parsed)) + if err != nil { + return redactWithPatterns(data) + } + return string(redactedBytes) +} diff --git a/terraform/provider/litellm/client_test.go b/terraform/provider/litellm/client_test.go new file mode 100644 index 00000000000..56f76565616 --- /dev/null +++ b/terraform/provider/litellm/client_test.go @@ -0,0 +1,71 @@ +package litellm + +import ( + "strings" + "testing" +) + +func TestRedactSensitiveDataNestedCredentialValues(t *testing.T) { + c := NewClient("http://localhost:4000", "sk-test", false) + + input := `{"credential_name":"azure-cred","credential_values":{"api_key":"sk-secret-123","config":{"region":"us-east-1","client_secret":"nested-secret"}}}` + got := c.redactSensitiveData(input) + + for _, leaked := range []string{"sk-secret-123", "us-east-1", "nested-secret"} { + if strings.Contains(got, leaked) { + t.Errorf("redacted output leaked %q: %s", leaked, got) + } + } + if !strings.Contains(got, `"credential_values":"[REDACTED]"`) { + t.Errorf("credential_values not redacted: %s", got) + } + if !strings.Contains(got, `"credential_name":"azure-cred"`) { + t.Errorf("non-sensitive field mangled: %s", got) + } +} + +func TestRedactSensitiveDataDeeplyNestedSensitiveKeys(t *testing.T) { + c := NewClient("http://localhost:4000", "sk-test", false) + + input := `{"data":[{"litellm_params":{"model":"gpt-4","api_key":"sk-deep-456","aws_secret_access_key":"aws-secret"}}]}` + got := c.redactSensitiveData(input) + + for _, leaked := range []string{"sk-deep-456", "aws-secret"} { + if strings.Contains(got, leaked) { + t.Errorf("redacted output leaked %q: %s", leaked, got) + } + } + if !strings.Contains(got, `"model":"gpt-4"`) { + t.Errorf("non-sensitive field mangled: %s", got) + } +} + +func TestRedactSensitiveDataTopLevelStringFields(t *testing.T) { + c := NewClient("http://localhost:4000", "sk-test", false) + + input := `{"model_api_key":"sk-top-789","vertex_credentials":"{\"type\":\"service_account\"}","team_alias":"eng"}` + got := c.redactSensitiveData(input) + + for _, leaked := range []string{"sk-top-789", "service_account"} { + if strings.Contains(got, leaked) { + t.Errorf("redacted output leaked %q: %s", leaked, got) + } + } + if !strings.Contains(got, `"team_alias":"eng"`) { + t.Errorf("non-sensitive field mangled: %s", got) + } +} + +func TestRedactSensitiveDataNonJSONFallback(t *testing.T) { + c := NewClient("http://localhost:4000", "sk-test", false) + + input := `error before "api_key": "sk-fallback-000" after` + got := c.redactSensitiveData(input) + + if strings.Contains(got, "sk-fallback-000") { + t.Errorf("fallback redaction leaked secret: %s", got) + } + if !strings.Contains(got, "[REDACTED]") { + t.Errorf("fallback redaction did not redact: %s", got) + } +} diff --git a/terraform/provider/litellm/data_source_credential.go b/terraform/provider/litellm/data_source_credential.go new file mode 100644 index 00000000000..e4533546a67 --- /dev/null +++ b/terraform/provider/litellm/data_source_credential.go @@ -0,0 +1,73 @@ +package litellm + +import ( + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceLiteLLMCredential() *schema.Resource { + return &schema.Resource{ + Read: dataSourceLiteLLMCredentialRead, + + Schema: map[string]*schema.Schema{ + "credential_name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the credential to retrieve", + }, + "model_id": { + Type: schema.TypeString, + Optional: true, + Description: "Model ID associated with this credential", + }, + "credential_info": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Additional information about the credential", + }, + // Note: credential_values are not exposed in data sources for security reasons + }, + } +} + +func dataSourceLiteLLMCredentialRead(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + credentialName := d.Get("credential_name").(string) + modelID := d.Get("model_id").(string) + + // Use the same endpoint as the resource read operation + endpoint := fmt.Sprintf("/credentials/by_name/%s", credentialName) + if modelID != "" { + endpoint += fmt.Sprintf("?model_id=%s", modelID) + } + + resp, err := MakeRequest(client, "GET", endpoint, nil) + if err != nil { + return fmt.Errorf("failed to read credential: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("credential '%s' not found", credentialName) + } + + var credentialResp CredentialResponse + err = handleCredentialAPIResponse(resp, &credentialResp, client) + if err != nil { + if err.Error() == "credential_not_found" { + return fmt.Errorf("credential '%s' not found", credentialName) + } + return fmt.Errorf("failed to read credential: %w", err) + } + + // Set the data source ID to the credential name + d.SetId(credentialResp.CredentialName) + d.Set("credential_name", credentialResp.CredentialName) + d.Set("credential_info", credentialResp.CredentialInfo) + // Note: We don't expose credential_values in data sources for security reasons + + return nil +} diff --git a/terraform/provider/litellm/data_source_vector_store.go b/terraform/provider/litellm/data_source_vector_store.go new file mode 100644 index 00000000000..d39a2f92af4 --- /dev/null +++ b/terraform/provider/litellm/data_source_vector_store.go @@ -0,0 +1,107 @@ +package litellm + +import ( + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceLiteLLMVectorStore() *schema.Resource { + return &schema.Resource{ + Read: dataSourceLiteLLMVectorStoreRead, + + Schema: map[string]*schema.Schema{ + "vector_store_id": { + Type: schema.TypeString, + Required: true, + Description: "Unique identifier for the vector store to retrieve", + }, + "vector_store_name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the vector store", + }, + "custom_llm_provider": { + Type: schema.TypeString, + Computed: true, + Description: "Custom LLM provider for the vector store", + }, + "vector_store_description": { + Type: schema.TypeString, + Computed: true, + Description: "Description of the vector store", + }, + "vector_store_metadata": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Metadata associated with the vector store", + }, + "litellm_credential_name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the LiteLLM credential used", + }, + "litellm_params": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Additional LiteLLM parameters", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Timestamp when the vector store was created", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Timestamp when the vector store was last updated", + }, + }, + } +} + +func dataSourceLiteLLMVectorStoreRead(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + vectorStoreID := d.Get("vector_store_id").(string) + + // Use the info endpoint to get vector store details + infoRequest := VectorStoreInfoRequest{ + VectorStoreID: vectorStoreID, + } + + resp, err := MakeRequest(client, "POST", "/vector_store/info", infoRequest) + if err != nil { + return fmt.Errorf("failed to read vector store: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("vector store '%s' not found", vectorStoreID) + } + + var vectorStoreResp VectorStoreResponse + err = handleVectorStoreAPIResponse(resp, &vectorStoreResp, client) + if err != nil { + if err.Error() == "vector_store_not_found" { + return fmt.Errorf("vector store '%s' not found", vectorStoreID) + } + return fmt.Errorf("failed to read vector store: %w", err) + } + + // Set the data source ID to the vector store ID + d.SetId(vectorStoreResp.VectorStoreID) + d.Set("vector_store_id", vectorStoreResp.VectorStoreID) + d.Set("vector_store_name", vectorStoreResp.VectorStoreName) + d.Set("custom_llm_provider", vectorStoreResp.CustomLLMProvider) + d.Set("vector_store_description", vectorStoreResp.VectorStoreDescription) + d.Set("vector_store_metadata", vectorStoreResp.VectorStoreMetadata) + d.Set("litellm_credential_name", vectorStoreResp.LiteLLMCredentialName) + d.Set("litellm_params", vectorStoreResp.LiteLLMParams) + d.Set("created_at", vectorStoreResp.CreatedAt) + d.Set("updated_at", vectorStoreResp.UpdatedAt) + + return nil +} diff --git a/terraform/provider/litellm/provider.go b/terraform/provider/litellm/provider.go new file mode 100644 index 00000000000..57f9cc24183 --- /dev/null +++ b/terraform/provider/litellm/provider.go @@ -0,0 +1,63 @@ +package litellm + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// Provider returns a terraform.ResourceProvider. +func Provider() *schema.Provider { + return &schema.Provider{ + ResourcesMap: map[string]*schema.Resource{ + "litellm_model": resourceLiteLLMModel(), + "litellm_team": ResourceLiteLLMTeam(), + "litellm_organization": resourceLiteLLMOrganization(), + "litellm_organization_member": resourceLiteLLMOrganizationMember(), + "litellm_organization_member_add": resourceLiteLLMOrganizationMemberAdd(), + "litellm_team_member": resourceLiteLLMTeamMember(), + "litellm_team_member_add": resourceLiteLLMTeamMemberAdd(), + "litellm_key": resourceKey(), + "litellm_mcp_server": resourceLiteLLMMCPServer(), + "litellm_credential": resourceLiteLLMCredential(), + "litellm_vector_store": resourceLiteLLMVectorStore(), + }, + DataSourcesMap: map[string]*schema.Resource{ + "litellm_credential": dataSourceLiteLLMCredential(), + "litellm_vector_store": dataSourceLiteLLMVectorStore(), + }, + Schema: map[string]*schema.Schema{ + "api_base": { + Type: schema.TypeString, + Required: true, + Sensitive: false, + DefaultFunc: schema.EnvDefaultFunc("LITELLM_API_BASE", nil), + Description: "The base URL of the LiteLLM API", + }, + "api_key": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + DefaultFunc: schema.EnvDefaultFunc("LITELLM_API_KEY", nil), + Description: "The API key for authenticating with LiteLLM", + }, + "insecure_skip_verify": { + Type: schema.TypeBool, + Optional: true, + Default: false, + DefaultFunc: schema.EnvDefaultFunc("LITELLM_INSECURE_SKIP_VERIFY", false), + Description: "Skip TLS certificate verification. Only use for development or when using self-signed certificates", + }, + }, + ConfigureFunc: providerConfigure, + } +} + +// providerConfigure configures the provider with the given schema data. +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + config := ProviderConfig{ + APIBase: d.Get("api_base").(string), + APIKey: d.Get("api_key").(string), + InsecureSkipVerify: d.Get("insecure_skip_verify").(bool), + } + + return NewClient(config.APIBase, config.APIKey, config.InsecureSkipVerify), nil +} diff --git a/terraform/provider/litellm/provider_test.go b/terraform/provider/litellm/provider_test.go new file mode 100644 index 00000000000..00817e7c410 --- /dev/null +++ b/terraform/provider/litellm/provider_test.go @@ -0,0 +1,83 @@ +package litellm + +import ( + "os" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var testAccProviders map[string]*schema.Provider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider() + testAccProviders = map[string]*schema.Provider{ + "litellm": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ *schema.Provider = Provider() +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("LITELLM_API_BASE"); v == "" { + t.Fatal("LITELLM_API_BASE must be set for acceptance tests") + } + if v := os.Getenv("LITELLM_API_KEY"); v == "" { + t.Fatal("LITELLM_API_KEY must be set for acceptance tests") + } + + // Create test users needed for organization member tests + createTestUsers(t) +} + +func createTestUsers(t *testing.T) { + apiBase := os.Getenv("LITELLM_API_BASE") + apiKey := os.Getenv("LITELLM_API_KEY") + + if apiBase == "" || apiKey == "" { + return + } + + client := NewClient(apiBase, apiKey, false) + + // Create test users + users := []map[string]interface{}{ + { + "user_id": "test-user-1", + "user_email": "test-user-1@example.com", + "user_role": "internal_user", + }, + { + "user_id": "bulk-user-1", + "user_email": "bulk-user-1@example.com", + "user_role": "internal_user", + }, + { + "user_id": "bulk-user-2", + "user_email": "bulk-user-2@example.com", + "user_role": "internal_user", + }, + } + + for _, user := range users { + _, err := client.sendRequest("POST", "/user/new", user) + if err != nil { + // Silently ignore if user already exists (400 error) + // This is expected when running tests multiple times + errStr := err.Error() + if !strings.Contains(errStr, "400") && !strings.Contains(errStr, "already exists") { + t.Logf("Warning: Could not create user %s: %v", user["user_id"], err) + } + } + } +} diff --git a/terraform/provider/litellm/resource_credential.go b/terraform/provider/litellm/resource_credential.go new file mode 100644 index 00000000000..f668a46a324 --- /dev/null +++ b/terraform/provider/litellm/resource_credential.go @@ -0,0 +1,44 @@ +package litellm + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceLiteLLMCredential() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMCredentialCreate, + Read: resourceLiteLLMCredentialRead, + Update: resourceLiteLLMCredentialUpdate, + Delete: resourceLiteLLMCredentialDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "credential_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Name of the credential", + }, + "model_id": { + Type: schema.TypeString, + Optional: true, + Description: "Model ID associated with this credential", + }, + "credential_info": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Additional information about the credential", + }, + "credential_values": { + Type: schema.TypeMap, + Required: true, + Sensitive: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Sensitive credential values (API keys, tokens, etc.)", + }, + }, + } +} diff --git a/terraform/provider/litellm/resource_credential_crud.go b/terraform/provider/litellm/resource_credential_crud.go new file mode 100644 index 00000000000..dd9aef64f76 --- /dev/null +++ b/terraform/provider/litellm/resource_credential_crud.go @@ -0,0 +1,204 @@ +package litellm + +import ( + "fmt" + "log" + "net/http" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// retryCredentialRead attempts to read a credential with exponential backoff. +// If the read path clears the ID (e.g., transient 404 right after create), +// we treat it as retryable instead of accepting an empty state. +func retryCredentialRead(d *schema.ResourceData, m interface{}, maxRetries int) error { + var err error + delay := 1 * time.Second + maxDelay := 10 * time.Second + origID := d.Id() + + for i := 0; i < maxRetries; i++ { + log.Printf("[INFO] Attempting to read credential (attempt %d/%d)", i+1, maxRetries) + + err = resourceLiteLLMCredentialRead(d, m) + // If read succeeded but wiped the ID, treat as not found so we retry. + if err == nil && d.Id() == "" { + d.SetId(origID) + err = fmt.Errorf("credential_not_found") + } + + if err == nil { + log.Printf("[INFO] Successfully read credential after %d attempts", i+1) + return nil + } + + if !strings.Contains(err.Error(), "credential_not_found") { + return err + } + + if i < maxRetries-1 { + log.Printf("[INFO] Credential not found yet, retrying in %v...", delay) + time.Sleep(delay) + + delay *= 2 + if delay > maxDelay { + delay = maxDelay + } + } + } + + log.Printf("[WARN] Failed to read credential after %d attempts: %v", maxRetries, err) + return err +} + +func resourceLiteLLMCredentialCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + credentialName := d.Get("credential_name").(string) + modelID := d.Get("model_id").(string) + credentialInfo := d.Get("credential_info").(map[string]interface{}) + credentialValues := d.Get("credential_values").(map[string]interface{}) + + // Convert credential_info to map[string]interface{} for JSON + credInfoMap := make(map[string]interface{}) + for k, v := range credentialInfo { + credInfoMap[k] = v + } + + // Convert credential_values to map[string]interface{} for JSON + credValuesMap := make(map[string]interface{}) + for k, v := range credentialValues { + credValuesMap[k] = v + } + + credentialRequest := CredentialRequest{ + CredentialName: credentialName, + ModelID: modelID, + CredentialInfo: credInfoMap, + CredentialValues: credValuesMap, + } + + resp, err := MakeRequest(client, "POST", "/credentials", credentialRequest) + if err != nil { + return fmt.Errorf("failed to create credential: %w", err) + } + defer resp.Body.Close() + + err = handleCredentialAPIResponse(resp, nil, client) + if err != nil { + return fmt.Errorf("failed to create credential: %w", err) + } + + // Set the resource ID to the credential name + d.SetId(credentialName) + + log.Printf("[INFO] Credential created with name %s. Starting retry mechanism to read the credential...", credentialName) + return retryCredentialRead(d, m, 5) +} + +func resourceLiteLLMCredentialRead(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + credentialName := d.Id() + + // Try to get credential by name first + modelID := d.Get("model_id").(string) + endpoint := fmt.Sprintf("/credentials/by_name/%s", credentialName) + if modelID != "" { + endpoint += fmt.Sprintf("?model_id=%s", modelID) + } + + resp, err := MakeRequest(client, "GET", endpoint, nil) + if err != nil { + return fmt.Errorf("failed to read credential: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + + var credentialResp CredentialResponse + err = handleCredentialAPIResponse(resp, &credentialResp, client) + if err != nil { + if err.Error() == "credential_not_found" { + d.SetId("") + return nil + } + return fmt.Errorf("failed to read credential: %w", err) + } + + d.Set("credential_name", credentialResp.CredentialName) + d.Set("credential_info", credentialResp.CredentialInfo) + // Note: We don't set credential_values from the response for security reasons + // The API might not return sensitive values, and we want to preserve what's in state + + return nil +} + +func resourceLiteLLMCredentialUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + credentialName := d.Id() + + credentialInfo := d.Get("credential_info").(map[string]interface{}) + credentialValues := d.Get("credential_values").(map[string]interface{}) + + // Convert credential_info to map[string]interface{} for JSON + credInfoMap := make(map[string]interface{}) + for k, v := range credentialInfo { + credInfoMap[k] = v + } + + // Convert credential_values to map[string]interface{} for JSON + credValuesMap := make(map[string]interface{}) + for k, v := range credentialValues { + credValuesMap[k] = v + } + + credentialRequest := CredentialRequest{ + CredentialName: credentialName, + CredentialInfo: credInfoMap, + CredentialValues: credValuesMap, + } + + endpoint := fmt.Sprintf("/credentials/%s", credentialName) + resp, err := MakeRequest(client, "PATCH", endpoint, credentialRequest) + if err != nil { + return fmt.Errorf("failed to update credential: %w", err) + } + defer resp.Body.Close() + + err = handleCredentialAPIResponse(resp, nil, client) + if err != nil { + return fmt.Errorf("failed to update credential: %w", err) + } + + log.Printf("[INFO] Credential updated with name %s. Starting retry mechanism to read the credential...", credentialName) + return retryCredentialRead(d, m, 5) +} + +func resourceLiteLLMCredentialDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + credentialName := d.Id() + + endpoint := fmt.Sprintf("/credentials/%s", credentialName) + resp, err := MakeRequest(client, "DELETE", endpoint, nil) + if err != nil { + return fmt.Errorf("failed to delete credential: %w", err) + } + defer resp.Body.Close() + + err = handleCredentialAPIResponse(resp, nil, client) + if err != nil { + if err.Error() == "credential_not_found" { + d.SetId("") + return nil + } + return fmt.Errorf("failed to delete credential: %w", err) + } + + d.SetId("") + return nil +} diff --git a/terraform/provider/litellm/resource_credential_crud_test.go b/terraform/provider/litellm/resource_credential_crud_test.go new file mode 100644 index 00000000000..3398e58dd13 --- /dev/null +++ b/terraform/provider/litellm/resource_credential_crud_test.go @@ -0,0 +1,201 @@ +package litellm + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "sync/atomic" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// newTestResourceData creates a *schema.ResourceData with the credential schema, +// sets the ID and populates the required fields. +func newTestResourceData(t *testing.T, id string) *schema.ResourceData { + t.Helper() + d := schema.TestResourceDataRaw(t, resourceLiteLLMCredential().Schema, map[string]interface{}{ + "credential_name": id, + "model_id": "", + "credential_info": map[string]interface{}{}, + "credential_values": map[string]interface{}{"key": "val"}, + }) + d.SetId(id) + return d +} + +func TestRetryCredentialRead_SuccessOnFirstAttempt(t *testing.T) { + resp := CredentialResponse{ + CredentialName: "test-cred", + CredentialInfo: map[string]interface{}{"provider": "aws"}, + } + body, _ := json.Marshal(resp) + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(body) + })) + defer srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := newTestResourceData(t, "test-cred") + + err := retryCredentialRead(d, client, 3) + if err != nil { + t.Fatalf("expected nil error, got: %v", err) + } + if d.Id() != "test-cred" { + t.Fatalf("expected ID 'test-cred', got %q", d.Id()) + } +} + +func TestRetryCredentialRead_SuccessAfterRetries(t *testing.T) { + resp := CredentialResponse{ + CredentialName: "test-cred", + CredentialInfo: map[string]interface{}{"provider": "aws"}, + } + body, _ := json.Marshal(resp) + + var callCount int32 + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + n := atomic.AddInt32(&callCount, 1) + w.Header().Set("Content-Type", "application/json") + if n <= 2 { + // First two calls return 404, triggering retry + w.WriteHeader(http.StatusNotFound) + return + } + w.WriteHeader(http.StatusOK) + w.Write(body) + })) + defer srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := newTestResourceData(t, "test-cred") + + err := retryCredentialRead(d, client, 3) + if err != nil { + t.Fatalf("expected nil error, got: %v", err) + } + if d.Id() != "test-cred" { + t.Fatalf("expected ID 'test-cred', got %q", d.Id()) + } + if atomic.LoadInt32(&callCount) != 3 { + t.Fatalf("expected 3 HTTP calls, got %d", callCount) + } +} + +func TestRetryCredentialRead_ExhaustsRetries(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + defer srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := newTestResourceData(t, "test-cred") + + err := retryCredentialRead(d, client, 2) + if err == nil { + t.Fatal("expected error after exhausting retries, got nil") + } + if err.Error() != "credential_not_found" { + t.Fatalf("expected 'credential_not_found' error, got: %v", err) + } + // ID should still be restored (not wiped) + if d.Id() != "test-cred" { + t.Fatalf("expected ID to be restored to 'test-cred', got %q", d.Id()) + } +} + +func TestRetryCredentialRead_NonRetryableError(t *testing.T) { + var callCount int32 + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + atomic.AddInt32(&callCount, 1) + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(`{"error": "internal server error"}`)) + })) + defer srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := newTestResourceData(t, "test-cred") + + err := retryCredentialRead(d, client, 3) + if err == nil { + t.Fatal("expected error for 500 response, got nil") + } + // Should fail on first attempt without retrying + if atomic.LoadInt32(&callCount) != 1 { + t.Fatalf("expected 1 HTTP call (no retries for non-retryable error), got %d", callCount) + } +} + +func TestRetryCredentialRead_IDRestoredBetweenRetries(t *testing.T) { + // Verify the ID is restored after each failed attempt where the read clears it. + // resourceLiteLLMCredentialRead sets ID to "" on 404, and retryCredentialRead + // should restore it before the next attempt. + resp := CredentialResponse{ + CredentialName: "my-cred", + CredentialInfo: map[string]interface{}{}, + } + body, _ := json.Marshal(resp) + + var callCount int32 + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + n := atomic.AddInt32(&callCount, 1) + w.Header().Set("Content-Type", "application/json") + if n == 1 { + w.WriteHeader(http.StatusNotFound) + return + } + w.WriteHeader(http.StatusOK) + w.Write(body) + })) + defer srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := newTestResourceData(t, "my-cred") + + err := retryCredentialRead(d, client, 2) + if err != nil { + t.Fatalf("expected nil error, got: %v", err) + } + if d.Id() != "my-cred" { + t.Fatalf("expected ID 'my-cred', got %q", d.Id()) + } +} + +func TestRetryCredentialRead_MaxRetriesOne(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + defer srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := newTestResourceData(t, "test-cred") + + err := retryCredentialRead(d, client, 1) + if err == nil { + t.Fatal("expected error with maxRetries=1 and always-404, got nil") + } + if err.Error() != "credential_not_found" { + t.Fatalf("expected 'credential_not_found', got: %v", err) + } +} + +func TestRetryCredentialRead_ConnectionError(t *testing.T) { + // Point to a server that's already closed to simulate connection failure + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := newTestResourceData(t, "test-cred") + + err := retryCredentialRead(d, client, 1) + if err == nil { + t.Fatal("expected error for connection failure, got nil") + } + // Connection error should not be retried (not a "credential_not_found") + fmt.Printf("connection error (expected): %v\n", err) +} diff --git a/terraform/provider/litellm/resource_key.go b/terraform/provider/litellm/resource_key.go new file mode 100644 index 00000000000..5c80198cf6a --- /dev/null +++ b/terraform/provider/litellm/resource_key.go @@ -0,0 +1,319 @@ +package litellm + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceKey() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceKeyCreate, + ReadContext: resourceKeyRead, + UpdateContext: resourceKeyUpdate, + DeleteContext: resourceKeyDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Optional: true, + WriteOnly: true, + Sensitive: true, + }, + "token_id": { + Type: schema.TypeString, + Computed: true, + }, + "models": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "max_budget": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Optional: true, + }, + "team_id": { + Type: schema.TypeString, + Optional: true, + }, + "max_parallel_requests": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "metadata": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "tpm_limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "rpm_limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "budget_duration": { + Type: schema.TypeString, + Optional: true, + }, + "allowed_cache_controls": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "soft_budget": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + }, + "key_alias": { + Type: schema.TypeString, + Optional: true, + }, + "duration": { + Type: schema.TypeString, + Optional: true, + }, + "aliases": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "config": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "permissions": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "model_max_budget": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeFloat, Computed: true}, + }, + "model_rpm_limit": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeInt, Computed: true}, + }, + "model_tpm_limit": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeInt, Computed: true}, + }, + "guardrails": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "blocked": { + Type: schema.TypeBool, + Optional: true, + }, + "tags": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "spend": { + Type: schema.TypeFloat, + Computed: true, + }, + }, + } +} + +func resourceKeyCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*Client) + + key := &Key{} + mapResourceDataToKey(d, key) + + createdKey, err := c.CreateKey(key) + if err != nil { + return diag.FromErr(fmt.Errorf("error creating key: %s", err)) + } + + d.SetId(createdKey.TokenID) + // Set the write-only key value so it's available during this apply + // but will not be persisted to state. + d.Set("key", createdKey.Key) + return resourceKeyRead(ctx, d, m) +} + +func resourceKeyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*Client) + + key, err := c.GetKey(d.Id()) + if err != nil { + return diag.FromErr(fmt.Errorf("error reading key: %s", err)) + } + + if key == nil { + d.SetId("") + return nil + } + + mapKeyToResourceData(d, key) + return nil +} + +func resourceKeyUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*Client) + + key := &Key{Key: d.Id()} + mapResourceDataToKey(d, key) + + _, err := c.UpdateKey(key) + if err != nil { + return diag.FromErr(fmt.Errorf("error updating key: %s", err)) + } + + return resourceKeyRead(ctx, d, m) +} + +func resourceKeyDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*Client) + + err := c.DeleteKey(d.Id()) + if err != nil { + return diag.FromErr(fmt.Errorf("error deleting key: %s", err)) + } + + d.SetId("") + return nil +} + +func mapResourceDataToKey(d *schema.ResourceData, key *Key) { + key.Models = expandStringList(d.Get("models").([]interface{})) + if v, ok := d.GetOk("max_budget"); ok { + val := v.(float64) + key.MaxBudget = &val + } + key.UserID = d.Get("user_id").(string) + key.TeamID = d.Get("team_id").(string) + if v, ok := d.GetOk("max_parallel_requests"); ok { + val := v.(int) + key.MaxParallelRequests = &val + } + key.Metadata = d.Get("metadata").(map[string]interface{}) + if v, ok := d.GetOk("tpm_limit"); ok { + val := v.(int) + key.TPMLimit = &val + } + if v, ok := d.GetOk("rpm_limit"); ok { + val := v.(int) + key.RPMLimit = &val + } + key.BudgetDuration = d.Get("budget_duration").(string) + key.AllowedCacheControls = expandStringList(d.Get("allowed_cache_controls").([]interface{})) + if v, ok := d.GetOk("soft_budget"); ok { + val := v.(float64) + key.SoftBudget = &val + } + key.KeyAlias = d.Get("key_alias").(string) + key.Duration = d.Get("duration").(string) + key.Aliases = d.Get("aliases").(map[string]interface{}) + key.Config = d.Get("config").(map[string]interface{}) + key.Permissions = d.Get("permissions").(map[string]interface{}) + key.ModelMaxBudget = d.Get("model_max_budget").(map[string]interface{}) + key.ModelRPMLimit = d.Get("model_rpm_limit").(map[string]interface{}) + key.ModelTPMLimit = d.Get("model_tpm_limit").(map[string]interface{}) + key.Guardrails = expandStringList(d.Get("guardrails").([]interface{})) + key.Blocked = d.Get("blocked").(bool) + key.Tags = expandStringList(d.Get("tags").([]interface{})) +} + +func mapKeyToResourceData(d *schema.ResourceData, key *Key) { + // token_id is the SHA-256 hash of the key, used as the resource ID. + // It is safe to store in state since it cannot be used to authenticate. + d.Set("token_id", d.Id()) + + // Note: "key" is write-only and must not be set here (Read operations). + // It is only set during Create so it is available during apply. + + if len(key.Models) > 0 { + d.Set("models", key.Models) + } + if key.MaxBudget != nil { + d.Set("max_budget", *key.MaxBudget) + } + if key.UserID != "" { + d.Set("user_id", key.UserID) + } + if key.TeamID != "" { + d.Set("team_id", key.TeamID) + } + if key.MaxParallelRequests != nil { + d.Set("max_parallel_requests", *key.MaxParallelRequests) + } + if key.Metadata != nil { + d.Set("metadata", key.Metadata) + } + if key.TPMLimit != nil { + d.Set("tpm_limit", *key.TPMLimit) + } + if key.RPMLimit != nil { + d.Set("rpm_limit", *key.RPMLimit) + } + if key.BudgetDuration != "" { + d.Set("budget_duration", key.BudgetDuration) + } + if len(key.AllowedCacheControls) > 0 { + d.Set("allowed_cache_controls", key.AllowedCacheControls) + } + if key.SoftBudget != nil { + d.Set("soft_budget", *key.SoftBudget) + } + if key.KeyAlias != "" { + d.Set("key_alias", key.KeyAlias) + } + if key.Duration != "" { + d.Set("duration", key.Duration) + } + if key.Aliases != nil { + d.Set("aliases", key.Aliases) + } + if key.Config != nil { + d.Set("config", key.Config) + } + if key.Permissions != nil { + d.Set("permissions", key.Permissions) + } + if key.ModelMaxBudget != nil { + d.Set("model_max_budget", key.ModelMaxBudget) + } + if key.ModelRPMLimit != nil { + d.Set("model_rpm_limit", key.ModelRPMLimit) + } + if key.ModelTPMLimit != nil { + d.Set("model_tpm_limit", key.ModelTPMLimit) + } + if len(key.Guardrails) > 0 { + d.Set("guardrails", key.Guardrails) + } + d.Set("blocked", key.Blocked) + if len(key.Tags) > 0 { + d.Set("tags", key.Tags) + } + if key.Spend != 0 { + d.Set("spend", key.Spend) + } +} diff --git a/terraform/provider/litellm/resource_key_utils.go b/terraform/provider/litellm/resource_key_utils.go new file mode 100644 index 00000000000..d426fec05b2 --- /dev/null +++ b/terraform/provider/litellm/resource_key_utils.go @@ -0,0 +1,230 @@ +package litellm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func buildKeyData(d *schema.ResourceData) map[string]interface{} { + keyData := make(map[string]interface{}) + + if v, ok := d.GetOkExists("models"); ok { + models := expandStringList(v.([]interface{})) + if len(models) > 0 { + keyData["models"] = models + } + } + if v, ok := d.GetOkExists("max_budget"); ok { + keyData["max_budget"] = v.(float64) + } + if v, ok := d.GetOkExists("user_id"); ok { + keyData["user_id"] = v.(string) + } + if v, ok := d.GetOkExists("team_id"); ok { + keyData["team_id"] = v.(string) + } + if v, ok := d.GetOkExists("max_parallel_requests"); ok { + keyData["max_parallel_requests"] = v.(int) + } + if v, ok := d.GetOkExists("metadata"); ok { + keyData["metadata"] = v.(map[string]interface{}) + } + if v, ok := d.GetOkExists("tpm_limit"); ok { + keyData["tpm_limit"] = v.(int) + } + if v, ok := d.GetOkExists("rpm_limit"); ok { + keyData["rpm_limit"] = v.(int) + } + if v, ok := d.GetOkExists("budget_duration"); ok { + keyData["budget_duration"] = v.(string) + } + if v, ok := d.GetOkExists("allowed_cache_controls"); ok { + cacheControls := expandStringList(v.([]interface{})) + if len(cacheControls) > 0 { + keyData["allowed_cache_controls"] = cacheControls + } + } + if v, ok := d.GetOkExists("soft_budget"); ok { + keyData["soft_budget"] = v.(float64) + } + if v, ok := d.GetOkExists("key_alias"); ok { + keyData["key_alias"] = v.(string) + } + if v, ok := d.GetOkExists("duration"); ok { + keyData["duration"] = v.(string) + } + if v, ok := d.GetOkExists("aliases"); ok { + keyData["aliases"] = v.(map[string]interface{}) + } + if v, ok := d.GetOkExists("config"); ok { + keyData["config"] = v.(map[string]interface{}) + } + if v, ok := d.GetOkExists("permissions"); ok { + keyData["permissions"] = v.(map[string]interface{}) + } + if v, ok := d.GetOkExists("model_max_budget"); ok { + keyData["model_max_budget"] = v.(map[string]interface{}) + } + if v, ok := d.GetOkExists("model_rpm_limit"); ok { + keyData["model_rpm_limit"] = v.(map[string]interface{}) + } + if v, ok := d.GetOkExists("model_tpm_limit"); ok { + keyData["model_tpm_limit"] = v.(map[string]interface{}) + } + if v, ok := d.GetOkExists("guardrails"); ok { + guardrails := expandStringList(v.([]interface{})) + if len(guardrails) > 0 { + keyData["guardrails"] = guardrails + } + } + if v, ok := d.GetOkExists("blocked"); ok { + keyData["blocked"] = v.(bool) + } + if v, ok := d.GetOkExists("tags"); ok { + tags := expandStringList(v.([]interface{})) + if len(tags) > 0 { + keyData["tags"] = tags + } + } + + return keyData +} + +func setKeyResourceData(d *schema.ResourceData, key *Key) error { + fields := map[string]interface{}{ + "key": key.Key, + "models": key.Models, + "spend": key.Spend, + "user_id": key.UserID, + "team_id": key.TeamID, + "metadata": key.Metadata, + "budget_duration": key.BudgetDuration, + "allowed_cache_controls": key.AllowedCacheControls, + "key_alias": key.KeyAlias, + "duration": key.Duration, + "aliases": key.Aliases, + "config": key.Config, + "permissions": key.Permissions, + "model_max_budget": key.ModelMaxBudget, + "model_rpm_limit": key.ModelRPMLimit, + "model_tpm_limit": key.ModelTPMLimit, + "guardrails": key.Guardrails, + "blocked": key.Blocked, + "tags": key.Tags, + } + + for field, value := range fields { + if err := d.Set(field, value); err != nil { + log.Printf("[WARN] Error setting %s: %s", field, err) + return fmt.Errorf("error setting %s: %s", field, err) + } + } + + // Handle pointer fields separately - only set if not nil + if key.MaxBudget != nil { + if err := d.Set("max_budget", *key.MaxBudget); err != nil { + return fmt.Errorf("error setting max_budget: %s", err) + } + } + if key.SoftBudget != nil { + if err := d.Set("soft_budget", *key.SoftBudget); err != nil { + return fmt.Errorf("error setting soft_budget: %s", err) + } + } + if key.MaxParallelRequests != nil { + if err := d.Set("max_parallel_requests", *key.MaxParallelRequests); err != nil { + return fmt.Errorf("error setting max_parallel_requests: %s", err) + } + } + if key.TPMLimit != nil { + if err := d.Set("tpm_limit", *key.TPMLimit); err != nil { + return fmt.Errorf("error setting tpm_limit: %s", err) + } + } + if key.RPMLimit != nil { + if err := d.Set("rpm_limit", *key.RPMLimit); err != nil { + return fmt.Errorf("error setting rpm_limit: %s", err) + } + } + + return nil +} + +func expandStringList(list []interface{}) []string { + result := make([]string, len(list)) + for i, v := range list { + result[i] = v.(string) + } + return result +} + +func mapToKey(data map[string]interface{}) *Key { + key := &Key{} + for k, v := range data { + switch k { + case "key": + key.Key = v.(string) + case "models": + key.Models = v.([]string) + case "max_budget": + if v, ok := v.(float64); ok { + key.MaxBudget = &v + } + case "user_id": + key.UserID = v.(string) + case "team_id": + key.TeamID = v.(string) + case "max_parallel_requests": + if v, ok := v.(int); ok { + key.MaxParallelRequests = &v + } + case "metadata": + key.Metadata = v.(map[string]interface{}) + case "tpm_limit": + if v, ok := v.(int); ok { + key.TPMLimit = &v + } + case "rpm_limit": + if v, ok := v.(int); ok { + key.RPMLimit = &v + } + case "budget_duration": + key.BudgetDuration = v.(string) + case "allowed_cache_controls": + key.AllowedCacheControls = v.([]string) + case "soft_budget": + if v, ok := v.(float64); ok { + key.SoftBudget = &v + } + case "key_alias": + key.KeyAlias = v.(string) + case "duration": + key.Duration = v.(string) + case "aliases": + key.Aliases = v.(map[string]interface{}) + case "config": + key.Config = v.(map[string]interface{}) + case "permissions": + key.Permissions = v.(map[string]interface{}) + case "model_max_budget": + key.ModelMaxBudget = v.(map[string]interface{}) + case "model_rpm_limit": + key.ModelRPMLimit = v.(map[string]interface{}) + case "model_tpm_limit": + key.ModelTPMLimit = v.(map[string]interface{}) + case "guardrails": + key.Guardrails = v.([]string) + case "blocked": + key.Blocked = v.(bool) + case "tags": + key.Tags = v.([]string) + } + } + return key +} + +func buildKeyForCreation(data map[string]interface{}) *Key { + return mapToKey(data) +} diff --git a/terraform/provider/litellm/resource_mcp_server.go b/terraform/provider/litellm/resource_mcp_server.go new file mode 100644 index 00000000000..b3eaef4a468 --- /dev/null +++ b/terraform/provider/litellm/resource_mcp_server.go @@ -0,0 +1,176 @@ +package litellm + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceLiteLLMMCPServer() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMMCPServerCreate, + Read: resourceLiteLLMMCPServerRead, + Update: resourceLiteLLMMCPServerUpdate, + Delete: resourceLiteLLMMCPServerDelete, + + Schema: map[string]*schema.Schema{ + "server_name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the MCP server", + }, + "alias": { + Type: schema.TypeString, + Optional: true, + Description: "Alias for the MCP server", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "Description of the MCP server", + }, + "url": { + Type: schema.TypeString, + Required: true, + Description: "URL of the MCP server", + }, + "transport": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "http", + "sse", + "stdio", + }, false), + Description: "Transport type for the MCP server (http, sse, stdio)", + }, + "spec_version": { + Type: schema.TypeString, + Optional: true, + Default: "2024-11-05", + Description: "MCP specification version", + }, + "auth_type": { + Type: schema.TypeString, + Optional: true, + Default: "none", + ValidateFunc: validation.StringInSlice([]string{ + "none", + "bearer", + "basic", + }, false), + Description: "Authentication type (none, bearer, basic)", + }, + "mcp_access_groups": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "List of access groups for the MCP server", + }, + "command": { + Type: schema.TypeString, + Optional: true, + Description: "Command to run for stdio transport", + }, + "args": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Arguments for the command (stdio transport)", + }, + "env": { + Type: schema.TypeMap, + Optional: true, + Sensitive: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Environment variables for the command (stdio transport)", + }, + "mcp_info": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "MCP server information and configuration", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "server_name": { + Type: schema.TypeString, + Optional: true, + Description: "Server name in MCP info", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "Description in MCP info", + }, + "logo_url": { + Type: schema.TypeString, + Optional: true, + Description: "Logo URL for the MCP server", + }, + "mcp_server_cost_info": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Cost information for MCP server tools", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_cost_per_query": { + Type: schema.TypeFloat, + Optional: true, + Description: "Default cost per query", + }, + "tool_name_to_cost_per_query": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeFloat}, + Description: "Map of tool names to their cost per query", + }, + }, + }, + }, + }, + }, + }, + // Read-only computed fields + "server_id": { + Type: schema.TypeString, + Computed: true, + Description: "Unique identifier for the MCP server", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Timestamp when the server was created", + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + Description: "User who created the server", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Timestamp when the server was last updated", + }, + "updated_by": { + Type: schema.TypeString, + Computed: true, + Description: "User who last updated the server", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "Current status of the MCP server", + }, + "last_health_check": { + Type: schema.TypeString, + Computed: true, + Description: "Timestamp of the last health check", + }, + "health_check_error": { + Type: schema.TypeString, + Computed: true, + Description: "Error message from the last health check, if any", + }, + }, + } +} diff --git a/terraform/provider/litellm/resource_mcp_server_crud.go b/terraform/provider/litellm/resource_mcp_server_crud.go new file mode 100644 index 00000000000..2a8980960f1 --- /dev/null +++ b/terraform/provider/litellm/resource_mcp_server_crud.go @@ -0,0 +1,317 @@ +package litellm + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + endpointMCPServerCreate = "/v1/mcp/server" + endpointMCPServerUpdate = "/v1/mcp/server" + endpointMCPServerRead = "/v1/mcp/server" + endpointMCPServerDelete = "/v1/mcp/server" +) + +// Helper function to convert schema data to MCPServerRequest +func buildMCPServerRequest(d *schema.ResourceData) *MCPServerRequest { + req := &MCPServerRequest{ + ServerName: d.Get("server_name").(string), + URL: d.Get("url").(string), + Transport: d.Get("transport").(string), + SpecVersion: d.Get("spec_version").(string), + AuthType: d.Get("auth_type").(string), + } + + // Set optional fields + if alias, ok := d.GetOk("alias"); ok { + req.Alias = alias.(string) + } + if description, ok := d.GetOk("description"); ok { + req.Description = description.(string) + } + if command, ok := d.GetOk("command"); ok { + req.Command = command.(string) + } + + // Handle access groups + if accessGroups, ok := d.GetOk("mcp_access_groups"); ok { + accessGroupsList := accessGroups.([]interface{}) + req.MCPAccessGroups = make([]string, len(accessGroupsList)) + for i, group := range accessGroupsList { + req.MCPAccessGroups[i] = group.(string) + } + } + + // Handle args + if args, ok := d.GetOk("args"); ok { + argsList := args.([]interface{}) + req.Args = make([]string, len(argsList)) + for i, arg := range argsList { + req.Args[i] = arg.(string) + } + } + + // Handle env + if env, ok := d.GetOk("env"); ok { + envMap := env.(map[string]interface{}) + req.Env = make(map[string]string) + for k, v := range envMap { + req.Env[k] = v.(string) + } + } + + // Handle mcp_info + if mcpInfoList, ok := d.GetOk("mcp_info"); ok { + mcpInfos := mcpInfoList.([]interface{}) + if len(mcpInfos) > 0 { + mcpInfoMap := mcpInfos[0].(map[string]interface{}) + req.MCPInfo = &MCPInfo{} + + if serverName, ok := mcpInfoMap["server_name"]; ok { + req.MCPInfo.ServerName = serverName.(string) + } + if description, ok := mcpInfoMap["description"]; ok { + req.MCPInfo.Description = description.(string) + } + if logoURL, ok := mcpInfoMap["logo_url"]; ok { + req.MCPInfo.LogoURL = logoURL.(string) + } + + // Handle cost info + if costInfoList, ok := mcpInfoMap["mcp_server_cost_info"]; ok { + costInfos := costInfoList.([]interface{}) + if len(costInfos) > 0 { + costInfoMap := costInfos[0].(map[string]interface{}) + req.MCPInfo.MCPServerCostInfo = &MCPServerCostInfo{} + + if defaultCost, ok := costInfoMap["default_cost_per_query"]; ok { + req.MCPInfo.MCPServerCostInfo.DefaultCostPerQuery = defaultCost.(float64) + } + if toolCosts, ok := costInfoMap["tool_name_to_cost_per_query"]; ok { + toolCostMap := toolCosts.(map[string]interface{}) + req.MCPInfo.MCPServerCostInfo.ToolNameToCostPerQuery = make(map[string]float64) + for k, v := range toolCostMap { + req.MCPInfo.MCPServerCostInfo.ToolNameToCostPerQuery[k] = v.(float64) + } + } + } + } + } + } + + return req +} + +// Helper function to update schema data from MCPServerResponse +func updateSchemaFromResponse(d *schema.ResourceData, resp *MCPServerResponse) error { + d.Set("server_id", resp.ServerID) + d.Set("server_name", resp.ServerName) + d.Set("alias", resp.Alias) + d.Set("description", resp.Description) + d.Set("url", resp.URL) + d.Set("transport", resp.Transport) + d.Set("spec_version", resp.SpecVersion) + d.Set("auth_type", resp.AuthType) + d.Set("created_at", resp.CreatedAt) + d.Set("created_by", resp.CreatedBy) + d.Set("updated_at", resp.UpdatedAt) + d.Set("updated_by", resp.UpdatedBy) + d.Set("status", resp.Status) + d.Set("last_health_check", resp.LastHealthCheck) + d.Set("health_check_error", resp.HealthCheckError) + d.Set("command", resp.Command) + + // Set access groups + if resp.MCPAccessGroups != nil { + d.Set("mcp_access_groups", resp.MCPAccessGroups) + } + + // Set args + if resp.Args != nil { + d.Set("args", resp.Args) + } + + // Set mcp_info + if resp.MCPInfo != nil { + mcpInfoList := make([]map[string]interface{}, 1) + mcpInfoMap := make(map[string]interface{}) + + mcpInfoMap["server_name"] = resp.MCPInfo.ServerName + mcpInfoMap["description"] = resp.MCPInfo.Description + mcpInfoMap["logo_url"] = resp.MCPInfo.LogoURL + + if resp.MCPInfo.MCPServerCostInfo != nil { + costInfoList := make([]map[string]interface{}, 1) + costInfoMap := make(map[string]interface{}) + + costInfoMap["default_cost_per_query"] = resp.MCPInfo.MCPServerCostInfo.DefaultCostPerQuery + if resp.MCPInfo.MCPServerCostInfo.ToolNameToCostPerQuery != nil { + costInfoMap["tool_name_to_cost_per_query"] = resp.MCPInfo.MCPServerCostInfo.ToolNameToCostPerQuery + } + + costInfoList[0] = costInfoMap + mcpInfoMap["mcp_server_cost_info"] = costInfoList + } + + mcpInfoList[0] = mcpInfoMap + d.Set("mcp_info", mcpInfoList) + } + + return nil +} + +func resourceLiteLLMMCPServerCreate(d *schema.ResourceData, m interface{}) error { + client, ok := m.(*Client) + if !ok { + return fmt.Errorf("invalid type assertion for client") + } + + req := buildMCPServerRequest(d) + + resp, err := MakeRequest(client, "POST", endpointMCPServerCreate, req) + if err != nil { + return fmt.Errorf("failed to create MCP server: %w", err) + } + defer resp.Body.Close() + + var mcpResp MCPServerResponse + if err := handleMCPAPIResponse(resp, &mcpResp, client); err != nil { + return fmt.Errorf("failed to create MCP server: %w", err) + } + + d.SetId(mcpResp.ServerID) + + // Update the state with the response data + if err := updateSchemaFromResponse(d, &mcpResp); err != nil { + return fmt.Errorf("failed to update state after create: %w", err) + } + + log.Printf("[INFO] MCP server created with ID %s", mcpResp.ServerID) + return nil +} + +func resourceLiteLLMMCPServerRead(d *schema.ResourceData, m interface{}) error { + client, ok := m.(*Client) + if !ok { + return fmt.Errorf("invalid type assertion for client") + } + + serverID := d.Id() + endpoint := fmt.Sprintf("%s/%s", endpointMCPServerRead, serverID) + + resp, err := MakeRequest(client, "GET", endpoint, nil) + if err != nil { + return fmt.Errorf("failed to read MCP server: %w", err) + } + defer resp.Body.Close() + + var mcpResp MCPServerResponse + if err := handleMCPAPIResponse(resp, &mcpResp, client); err != nil { + if err.Error() == "mcp_server_not_found" { + d.SetId("") + return nil + } + return fmt.Errorf("failed to read MCP server: %w", err) + } + + // Update the state with the response data + if err := updateSchemaFromResponse(d, &mcpResp); err != nil { + return fmt.Errorf("failed to update state after read: %w", err) + } + + return nil +} + +func resourceLiteLLMMCPServerUpdate(d *schema.ResourceData, m interface{}) error { + client, ok := m.(*Client) + if !ok { + return fmt.Errorf("invalid type assertion for client") + } + + req := buildMCPServerRequest(d) + req.ServerID = d.Id() // Ensure we include the server ID for updates + + resp, err := MakeRequest(client, "PUT", endpointMCPServerUpdate, req) + if err != nil { + return fmt.Errorf("failed to update MCP server: %w", err) + } + defer resp.Body.Close() + + var mcpResp MCPServerResponse + if err := handleMCPAPIResponse(resp, &mcpResp, client); err != nil { + return fmt.Errorf("failed to update MCP server: %w", err) + } + + // Update the state with the response data + if err := updateSchemaFromResponse(d, &mcpResp); err != nil { + return fmt.Errorf("failed to update state after update: %w", err) + } + + log.Printf("[INFO] MCP server updated with ID %s", mcpResp.ServerID) + return nil +} + +func resourceLiteLLMMCPServerDelete(d *schema.ResourceData, m interface{}) error { + client, ok := m.(*Client) + if !ok { + return fmt.Errorf("invalid type assertion for client") + } + + serverID := d.Id() + endpoint := fmt.Sprintf("%s/%s", endpointMCPServerDelete, serverID) + + resp, err := MakeRequest(client, "DELETE", endpoint, nil) + if err != nil { + return fmt.Errorf("failed to delete MCP server: %w", err) + } + defer resp.Body.Close() + + // For delete operations, we expect a simple string response + if resp.StatusCode != 200 { + return fmt.Errorf("failed to delete MCP server: unexpected status code %d", resp.StatusCode) + } + + d.SetId("") + log.Printf("[INFO] MCP server deleted with ID %s", serverID) + return nil +} + +// retryMCPServerRead attempts to read an MCP server with exponential backoff +func retryMCPServerRead(d *schema.ResourceData, m interface{}, maxRetries int) error { + var err error + delay := 1 * time.Second + maxDelay := 10 * time.Second + + for i := 0; i < maxRetries; i++ { + log.Printf("[INFO] Attempting to read MCP server (attempt %d/%d)", i+1, maxRetries) + + err = resourceLiteLLMMCPServerRead(d, m) + if err == nil { + log.Printf("[INFO] Successfully read MCP server after %d attempts", i+1) + return nil + } + + // Check if this is a "server not found" error + if err.Error() != "failed to read MCP server: mcp_server_not_found" { + // If it's a different error, don't retry + return err + } + + if i < maxRetries-1 { + log.Printf("[INFO] MCP server not found yet, retrying in %v...", delay) + time.Sleep(delay) + + // Exponential backoff with a maximum delay + delay *= 2 + if delay > maxDelay { + delay = maxDelay + } + } + } + + log.Printf("[WARN] Failed to read MCP server after %d attempts: %v", maxRetries, err) + return err +} diff --git a/terraform/provider/litellm/resource_mcp_server_crud_test.go b/terraform/provider/litellm/resource_mcp_server_crud_test.go new file mode 100644 index 00000000000..17300701954 --- /dev/null +++ b/terraform/provider/litellm/resource_mcp_server_crud_test.go @@ -0,0 +1,44 @@ +package litellm + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestMCPServerReadDoesNotPersistServerEnv(t *testing.T) { + d := schema.TestResourceDataRaw(t, resourceLiteLLMMCPServer().Schema, map[string]interface{}{ + "server_name": "gh", + "transport": "stdio", + "command": "npx", + "env": map[string]interface{}{ + "GITHUB_TOKEN": "from-config", + }, + }) + d.SetId("srv-1") + + resp := &MCPServerResponse{ + ServerID: "srv-1", + ServerName: "gh", + Transport: "stdio", + Command: "npx", + Env: map[string]string{ + "GITHUB_TOKEN": "raw-from-server", + "DB_PASSWORD": "leaked-secret", + }, + } + if err := updateSchemaFromResponse(d, resp); err != nil { + t.Fatalf("updateSchemaFromResponse failed: %v", err) + } + + got := d.Get("env").(map[string]interface{}) + if got["GITHUB_TOKEN"] != "from-config" { + t.Fatalf("config env overwritten by server response: %v", got) + } + if _, leaked := got["DB_PASSWORD"]; leaked { + t.Fatalf("server-returned env var persisted into state: %v", got) + } + if d.Get("server_name").(string) != "gh" { + t.Fatalf("read did not populate non-sensitive fields") + } +} diff --git a/terraform/provider/litellm/resource_model.go b/terraform/provider/litellm/resource_model.go new file mode 100644 index 00000000000..2858b6e763d --- /dev/null +++ b/terraform/provider/litellm/resource_model.go @@ -0,0 +1,177 @@ +package litellm + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceLiteLLMModel() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMModelCreate, + Read: resourceLiteLLMModelRead, + Update: resourceLiteLLMModelUpdate, + Delete: resourceLiteLLMModelDelete, + + Schema: map[string]*schema.Schema{ + "model_name": { + Type: schema.TypeString, + Required: true, + }, + "custom_llm_provider": { + Type: schema.TypeString, + Required: true, + }, + "tpm": { + Type: schema.TypeInt, + Optional: true, + }, + "rpm": { + Type: schema.TypeInt, + Optional: true, + }, + "reasoning_effort": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "low", + "medium", + "high", + }, false), + }, + "thinking_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "thinking_budget_tokens": { + Type: schema.TypeInt, + Optional: true, + Default: 1024, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // Only include thinking_budget_tokens in the diff if thinking_enabled is true + return !d.Get("thinking_enabled").(bool) + }, + }, + "merge_reasoning_content_in_choices": { + Type: schema.TypeBool, + Optional: true, + }, + "model_api_key": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "model_api_base": { + Type: schema.TypeString, + Optional: true, + }, + "api_version": { + Type: schema.TypeString, + Optional: true, + }, + "base_model": { + Type: schema.TypeString, + Required: true, + }, + "tier": { + Type: schema.TypeString, + Optional: true, + Default: "free", + }, + "team_id": { + Type: schema.TypeString, + Optional: true, + }, + "mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "completion", + "embedding", + "image_generation", + "chat", + "moderation", + "audio_transcription", + "audio_speech", + "rerank", + }, false), + }, + "input_cost_per_million_tokens": { + Type: schema.TypeFloat, + Optional: true, + }, + "output_cost_per_million_tokens": { + Type: schema.TypeFloat, + Optional: true, + }, + "input_cost_per_pixel": { + Type: schema.TypeFloat, + Optional: true, + }, + "output_cost_per_pixel": { + Type: schema.TypeFloat, + Optional: true, + }, + "input_cost_per_second": { + Type: schema.TypeFloat, + Optional: true, + }, + "output_cost_per_second": { + Type: schema.TypeFloat, + Optional: true, + }, + "aws_access_key_id": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "aws_secret_access_key": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "aws_region_name": { + Type: schema.TypeString, + Optional: true, + }, + "aws_session_name": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "aws_role_name": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "vertex_project": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "vertex_location": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "vertex_credentials": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "litellm_credential_name": { + Type: schema.TypeString, + Optional: true, + Description: "Name of the LiteLLM credential to use", + }, + "additional_litellm_params": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "Additional parameters to pass to litellm_params beyond the standard ones", + }, + }, + } +} diff --git a/terraform/provider/litellm/resource_model_crud.go b/terraform/provider/litellm/resource_model_crud.go new file mode 100644 index 00000000000..40766c8e312 --- /dev/null +++ b/terraform/provider/litellm/resource_model_crud.go @@ -0,0 +1,407 @@ +package litellm + +import ( + "encoding/json" + "fmt" + "log" + "strconv" + "strings" + "time" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// retryModelRead attempts to read a model with exponential backoff. +// It handles the case where resourceLiteLLMModelRead returns nil but clears the ID +// (eventual consistency: model created but not yet visible on read-back). +func retryModelRead(d *schema.ResourceData, m interface{}, maxRetries int) error { + delay := 1 * time.Second + maxDelay := 10 * time.Second + modelID := d.Id() + + for i := 0; i < maxRetries; i++ { + log.Printf("[INFO] Attempting to read model (attempt %d/%d)", i+1, maxRetries) + + err := resourceLiteLLMModelRead(d, m) + if err == nil { + if d.Id() != "" { + log.Printf("[INFO] Successfully read model after %d attempts", i+1) + return nil + } + // Read returned nil but cleared the ID — model not yet visible (eventual consistency). + // Restore the ID so we can retry. + d.SetId(modelID) + log.Printf("[INFO] Model not found yet (eventual consistency), retrying in %v...", delay) + } else { + log.Printf("[INFO] Read error, retrying in %v: %v", delay, err) + } + + if i < maxRetries-1 { + time.Sleep(delay) + delay *= 2 + if delay > maxDelay { + delay = maxDelay + } + } + } + + log.Printf("[WARN] Failed to read model after %d attempts", maxRetries) + return fmt.Errorf("model %s not found after %d read attempts post-create; the model may have been created successfully — re-running apply should resolve this", modelID, maxRetries) +} + +const ( + endpointModelNew = "/model/new" + endpointModelUpdate = "/model/update" + endpointModelInfo = "/model/info" + endpointModelDelete = "/model/delete" +) + +func createOrUpdateModel(d *schema.ResourceData, m interface{}, isUpdate bool) error { + client, ok := m.(*Client) + if !ok { + return fmt.Errorf("invalid type assertion for client") + } + + // Construct the model name in the format "custom_llm_provider/base_model" + customLLMProvider := d.Get("custom_llm_provider").(string) + baseModel := d.Get("base_model").(string) + modelName := fmt.Sprintf("%s/%s", customLLMProvider, baseModel) + + // Generate a UUID for new models + modelID := d.Id() + if !isUpdate { + modelID = uuid.New().String() + } + + // Create thinking configuration if enabled + var thinking map[string]interface{} + if d.Get("thinking_enabled").(bool) { + thinking = map[string]interface{}{ + "type": "enabled", + "budget_tokens": d.Get("thinking_budget_tokens").(int), + } + } + + // Build the base litellm_params as a map to allow for additional parameters + litellmParams := map[string]interface{}{ + "custom_llm_provider": customLLMProvider, + "model": modelName, + "merge_reasoning_content_in_choices": d.Get("merge_reasoning_content_in_choices").(bool), + } + + // Add optional parameters only if they have values + if tpm := d.Get("tpm").(int); tpm > 0 { + litellmParams["tpm"] = tpm + } + if rpm := d.Get("rpm").(int); rpm > 0 { + litellmParams["rpm"] = rpm + } + // Only include cost fields if explicitly set (non-zero) + if inputCostPerMillion := d.Get("input_cost_per_million_tokens").(float64); inputCostPerMillion > 0 { + litellmParams["input_cost_per_token"] = inputCostPerMillion / 1000000.0 + } + if outputCostPerMillion := d.Get("output_cost_per_million_tokens").(float64); outputCostPerMillion > 0 { + litellmParams["output_cost_per_token"] = outputCostPerMillion / 1000000.0 + } + if apiKey := d.Get("model_api_key").(string); apiKey != "" { + litellmParams["api_key"] = apiKey + } + if apiBase := d.Get("model_api_base").(string); apiBase != "" { + litellmParams["api_base"] = apiBase + } + if apiVersion := d.Get("api_version").(string); apiVersion != "" { + litellmParams["api_version"] = apiVersion + } + if inputCostPerPixel := d.Get("input_cost_per_pixel").(float64); inputCostPerPixel > 0 { + litellmParams["input_cost_per_pixel"] = inputCostPerPixel + } + if outputCostPerPixel := d.Get("output_cost_per_pixel").(float64); outputCostPerPixel > 0 { + litellmParams["output_cost_per_pixel"] = outputCostPerPixel + } + if inputCostPerSecond := d.Get("input_cost_per_second").(float64); inputCostPerSecond > 0 { + litellmParams["input_cost_per_second"] = inputCostPerSecond + } + if outputCostPerSecond := d.Get("output_cost_per_second").(float64); outputCostPerSecond > 0 { + litellmParams["output_cost_per_second"] = outputCostPerSecond + } + if awsAccessKeyID := d.Get("aws_access_key_id").(string); awsAccessKeyID != "" { + litellmParams["aws_access_key_id"] = awsAccessKeyID + } + if awsSecretAccessKey := d.Get("aws_secret_access_key").(string); awsSecretAccessKey != "" { + litellmParams["aws_secret_access_key"] = awsSecretAccessKey + } + if awsRegionName := d.Get("aws_region_name").(string); awsRegionName != "" { + litellmParams["aws_region_name"] = awsRegionName + } + if awsSessionName := d.Get("aws_session_name").(string); awsSessionName != "" { + litellmParams["aws_session_name"] = awsSessionName + } + if awsRoleName := d.Get("aws_role_name").(string); awsRoleName != "" { + litellmParams["aws_role_name"] = awsRoleName + } + if vertexProject := d.Get("vertex_project").(string); vertexProject != "" { + litellmParams["vertex_project"] = vertexProject + } + if vertexLocation := d.Get("vertex_location").(string); vertexLocation != "" { + litellmParams["vertex_location"] = vertexLocation + } + if vertexCredentials := d.Get("vertex_credentials").(string); vertexCredentials != "" { + litellmParams["vertex_credentials"] = vertexCredentials + } + if reasoningEffort := d.Get("reasoning_effort").(string); reasoningEffort != "" { + litellmParams["reasoning_effort"] = reasoningEffort + } + if thinking != nil { + litellmParams["thinking"] = thinking + } + + // Add additional parameters if provided + if additionalParams, ok := d.GetOk("additional_litellm_params"); ok { + var dropParams []string + + for key, value := range additionalParams.(map[string]interface{}) { + // Convert string values to appropriate types where possible + if strValue, ok := value.(string); ok { + // Check if it's JSON (starts with [ or {) + trimmedValue := strings.TrimSpace(strValue) + if strings.HasPrefix(trimmedValue, "[") || strings.HasPrefix(trimmedValue, "{") { + var parsedValue interface{} + if err := json.Unmarshal([]byte(strValue), &parsedValue); err == nil { + // Successfully parsed JSON + if key == "additional_drop_params" { + // Handle drop params specially + if dropList, ok := parsedValue.([]interface{}); ok { + for _, item := range dropList { + if paramStr, ok := item.(string); ok { + dropParams = append(dropParams, paramStr) + } + } + } + continue // Don't add to litellmParams + } else { + litellmParams[key] = parsedValue + } + } else { + // Not valid JSON, apply existing conversion logic + if strValue == "true" { + litellmParams[key] = true + } else if strValue == "false" { + litellmParams[key] = false + } else { + // Try to convert numeric strings + if intValue, err := strconv.Atoi(strValue); err == nil { + litellmParams[key] = intValue + } else if floatValue, err := strconv.ParseFloat(strValue, 64); err == nil { + litellmParams[key] = floatValue + } else { + // Keep as string + litellmParams[key] = strValue + } + } + } + } else { + // Apply existing conversion logic for non-JSON strings + if strValue == "true" { + litellmParams[key] = true + } else if strValue == "false" { + litellmParams[key] = false + } else { + // Try to convert numeric strings + if intValue, err := strconv.Atoi(strValue); err == nil { + litellmParams[key] = intValue + } else if floatValue, err := strconv.ParseFloat(strValue, 64); err == nil { + litellmParams[key] = floatValue + } else { + // Keep as string + litellmParams[key] = strValue + } + } + } + } else { + litellmParams[key] = value + } + } + + // Apply drop params at the end + for _, paramToDrop := range dropParams { + delete(litellmParams, paramToDrop) + } + } + + // Add litellm_credential_name to litellmParams if provided + if credentialName := d.Get("litellm_credential_name").(string); credentialName != "" { + litellmParams["litellm_credential_name"] = credentialName + } + + modelReq := ModelRequest{ + ModelName: d.Get("model_name").(string), + LiteLLMParams: litellmParams, + ModelInfo: ModelInfo{ + ID: modelID, + DBModel: true, + BaseModel: baseModel, + Tier: d.Get("tier").(string), + Mode: d.Get("mode").(string), + TeamID: d.Get("team_id").(string), + }, + Additional: make(map[string]interface{}), + } + + endpoint := endpointModelNew + if isUpdate { + endpoint = endpointModelUpdate + } + + resp, err := MakeRequest(client, "POST", endpoint, modelReq) + if err != nil { + return fmt.Errorf("failed to %s model: %w", map[bool]string{true: "update", false: "create"}[isUpdate], err) + } + defer resp.Body.Close() + + _, err = handleAPIResponse(resp, modelReq, client) + if err != nil { + if isUpdate && err.Error() == "model_not_found" { + return createOrUpdateModel(d, m, false) + } + return fmt.Errorf("failed to %s model: %w", map[bool]string{true: "update", false: "create"}[isUpdate], err) + } + + d.SetId(modelID) + + log.Printf("[INFO] Model created with ID %s. Starting retry mechanism to read the model...", modelID) + // Read back the resource with retries to ensure the state is consistent + return retryModelRead(d, m, 5) +} + +func resourceLiteLLMModelCreate(d *schema.ResourceData, m interface{}) error { + return createOrUpdateModel(d, m, false) +} + +func resourceLiteLLMModelRead(d *schema.ResourceData, m interface{}) error { + client, ok := m.(*Client) + if !ok { + return fmt.Errorf("invalid type assertion for client") + } + + resp, err := MakeRequest(client, "GET", fmt.Sprintf("%s?litellm_model_id=%s", endpointModelInfo, d.Id()), nil) + if err != nil { + return fmt.Errorf("failed to read model: %w", err) + } + defer resp.Body.Close() + + modelResp, err := handleAPIResponse(resp, nil, client) + if err != nil { + if err.Error() == "model_not_found" { + d.SetId("") + return nil + } + return fmt.Errorf("failed to read model: %w", err) + } + + // Update the state with values from the response or fall back to the data passed in during creation + d.Set("model_name", GetStringValue(modelResp.ModelName, d.Get("model_name").(string))) + d.Set("custom_llm_provider", GetStringValue(modelResp.LiteLLMParams.CustomLLMProvider, d.Get("custom_llm_provider").(string))) + d.Set("tpm", GetIntValue(modelResp.LiteLLMParams.TPM, d.Get("tpm").(int))) + d.Set("rpm", GetIntValue(modelResp.LiteLLMParams.RPM, d.Get("rpm").(int))) + d.Set("model_api_base", GetStringValue(modelResp.LiteLLMParams.APIBase, d.Get("model_api_base").(string))) + d.Set("api_version", GetStringValue(modelResp.LiteLLMParams.APIVersion, d.Get("api_version").(string))) + d.Set("base_model", GetStringValue(modelResp.ModelInfo.BaseModel, d.Get("base_model").(string))) + d.Set("tier", GetStringValue(modelResp.ModelInfo.Tier, d.Get("tier").(string))) + d.Set("mode", GetStringValue(modelResp.ModelInfo.Mode, d.Get("mode").(string))) + d.Set("team_id", GetStringValue(modelResp.ModelInfo.TeamID, d.Get("team_id").(string))) + + // Preserve credential name from state since it might not be returned by API + d.Set("litellm_credential_name", d.Get("litellm_credential_name").(string)) + + // Store sensitive information + d.Set("model_api_key", d.Get("model_api_key")) + d.Set("aws_access_key_id", d.Get("aws_access_key_id")) + d.Set("aws_secret_access_key", d.Get("aws_secret_access_key")) + d.Set("aws_region_name", GetStringValue(modelResp.LiteLLMParams.AWSRegionName, d.Get("aws_region_name").(string))) + d.Set("aws_session_name", d.Get("aws_session_name")) + d.Set("aws_role_name", d.Get("aws_role_name")) + + // Store cost information + d.Set("input_cost_per_million_tokens", d.Get("input_cost_per_million_tokens")) + d.Set("output_cost_per_million_tokens", d.Get("output_cost_per_million_tokens")) + + // Handle thinking configuration + if _, ok := d.GetOk("thinking_enabled"); ok { + // Keep the existing value from state + thinkingEnabled := d.Get("thinking_enabled").(bool) + d.Set("thinking_enabled", thinkingEnabled) + + // Only set thinking_budget_tokens if thinking is enabled and we have a value in state + if thinkingEnabled { + if _, ok := d.GetOk("thinking_budget_tokens"); ok { + d.Set("thinking_budget_tokens", d.Get("thinking_budget_tokens").(int)) + } + } + } else { + // Fall back to API response if no state value exists + if modelResp.LiteLLMParams.Thinking != nil { + if thinkingType, ok := modelResp.LiteLLMParams.Thinking["type"].(string); ok && thinkingType == "enabled" { + d.Set("thinking_enabled", true) + if budgetTokens, ok := modelResp.LiteLLMParams.Thinking["budget_tokens"].(float64); ok { + d.Set("thinking_budget_tokens", int(budgetTokens)) + } + } else { + d.Set("thinking_enabled", false) + } + } else { + d.Set("thinking_enabled", false) + } + } + + // Handle merge_reasoning_content_in_choices - preserve state value if not returned by API + if _, ok := d.GetOk("merge_reasoning_content_in_choices"); ok { + // Keep the existing value from state + d.Set("merge_reasoning_content_in_choices", d.Get("merge_reasoning_content_in_choices").(bool)) + } else { + // Only set from API response if we don't have a value in state + d.Set("merge_reasoning_content_in_choices", modelResp.LiteLLMParams.MergeReasoningContentInChoices) + } + + // Preserve additional_litellm_params from state since API might not return all custom parameters + if _, ok := d.GetOk("additional_litellm_params"); ok { + d.Set("additional_litellm_params", d.Get("additional_litellm_params")) + } + + return nil +} + +func resourceLiteLLMModelUpdate(d *schema.ResourceData, m interface{}) error { + return createOrUpdateModel(d, m, true) +} + +func resourceLiteLLMModelDelete(d *schema.ResourceData, m interface{}) error { + client, ok := m.(*Client) + if !ok { + return fmt.Errorf("invalid type assertion for client") + } + + deleteReq := struct { + ID string `json:"id"` + }{ + ID: d.Id(), + } + + resp, err := MakeRequest(client, "POST", endpointModelDelete, deleteReq) + if err != nil { + return fmt.Errorf("failed to delete model: %w", err) + } + defer resp.Body.Close() + + _, err = handleAPIResponse(resp, deleteReq, client) + if err != nil { + if err.Error() == "model_not_found" { + d.SetId("") + return nil + } + return fmt.Errorf("failed to delete model: %w", err) + } + + d.SetId("") + return nil +} diff --git a/terraform/provider/litellm/resource_organization.go b/terraform/provider/litellm/resource_organization.go new file mode 100644 index 00000000000..30e7feba1ec --- /dev/null +++ b/terraform/provider/litellm/resource_organization.go @@ -0,0 +1,210 @@ +package litellm + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + endpointOrganizationNew = "/organization/new" + endpointOrganizationInfo = "/organization/info" + endpointOrganizationUpdate = "/organization/update" + endpointOrganizationDelete = "/organization/delete" +) + +func resourceLiteLLMOrganization() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMOrganizationCreate, + Read: resourceLiteLLMOrganizationRead, + Update: resourceLiteLLMOrganizationUpdate, + Delete: resourceLiteLLMOrganizationDelete, + + Schema: map[string]*schema.Schema{ + "organization_alias": { + Type: schema.TypeString, + Required: true, + }, + "metadata": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "models": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "max_budget": { + Type: schema.TypeFloat, + Optional: true, + }, + "budget_duration": { + Type: schema.TypeString, + Optional: true, + }, + "tpm_limit": { + Type: schema.TypeInt, + Optional: true, + }, + "rpm_limit": { + Type: schema.TypeInt, + Optional: true, + }, + "blocked": { + Type: schema.TypeBool, + Optional: true, + }, + }, + } +} + +func resourceLiteLLMOrganizationCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + orgID := uuid.New().String() + orgData := buildOrganizationData(d, orgID) + + log.Printf("[DEBUG] Create organization request payload: %+v", orgData) + + resp, err := MakeRequest(client, "POST", endpointOrganizationNew, orgData) + if err != nil { + return fmt.Errorf("error creating organization: %w", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "creating organization"); err != nil { + return err + } + + d.SetId(orgID) + log.Printf("[INFO] Organization created with ID: %s", orgID) + + return resourceLiteLLMOrganizationRead(d, m) +} + +func resourceLiteLLMOrganizationRead(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + log.Printf("[INFO] Reading organization with ID: %s", d.Id()) + + resp, err := MakeRequest(client, "POST", endpointOrganizationInfo, map[string]interface{}{ + "organizations": []string{d.Id()}, + }) + if err != nil { + return fmt.Errorf("error reading organization: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + log.Printf("[WARN] Organization with ID %s not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + var orgResps []OrganizationResponse + if err := json.NewDecoder(resp.Body).Decode(&orgResps); err != nil { + return fmt.Errorf("error decoding organization info response: %w", err) + } + + if len(orgResps) == 0 { + log.Printf("[WARN] Organization with ID %s not found in response, removing from state", d.Id()) + d.SetId("") + return nil + } + + orgResp := orgResps[0] + + d.Set("organization_alias", GetStringValue(orgResp.OrganizationAlias, d.Get("organization_alias").(string))) + + if orgResp.Metadata != nil { + d.Set("metadata", orgResp.Metadata) + } else { + d.Set("metadata", d.Get("metadata")) + } + + if orgResp.Models != nil { + d.Set("models", orgResp.Models) + } else { + d.Set("models", d.Get("models")) + } + + if orgResp.MaxBudget != nil { + d.Set("max_budget", *orgResp.MaxBudget) + } + d.Set("budget_duration", GetStringValue(orgResp.BudgetDuration, d.Get("budget_duration").(string))) + if orgResp.TPMLimit != nil { + d.Set("tpm_limit", *orgResp.TPMLimit) + } + if orgResp.RPMLimit != nil { + d.Set("rpm_limit", *orgResp.RPMLimit) + } + d.Set("blocked", GetBoolValue(orgResp.Blocked, d.Get("blocked").(bool))) + + log.Printf("[INFO] Successfully read organization with ID: %s", d.Id()) + return nil +} + +func resourceLiteLLMOrganizationUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + orgData := buildOrganizationData(d, d.Id()) + log.Printf("[DEBUG] Update organization request payload: %+v", orgData) + + resp, err := MakeRequest(client, "PATCH", endpointOrganizationUpdate, orgData) + if err != nil { + return fmt.Errorf("error updating organization: %w", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "updating organization"); err != nil { + return err + } + + log.Printf("[INFO] Successfully updated organization with ID: %s", d.Id()) + return resourceLiteLLMOrganizationRead(d, m) +} + +func resourceLiteLLMOrganizationDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + log.Printf("[INFO] Deleting organization with ID: %s", d.Id()) + + deleteData := map[string]interface{}{ + "organization_ids": []string{d.Id()}, + } + + resp, err := MakeRequest(client, "DELETE", endpointOrganizationDelete, deleteData) + + if err != nil { + return fmt.Errorf("error deleting organization: %w", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "deleting organization"); err != nil { + return err + } + + log.Printf("[INFO] Successfully deleted organization with ID: %s", d.Id()) + d.SetId("") + return nil +} + +func buildOrganizationData(d *schema.ResourceData, orgID string) map[string]interface{} { + orgData := map[string]interface{}{ + "organization_id": orgID, + "organization_alias": d.Get("organization_alias").(string), + } + + for _, key := range []string{"metadata", "models", "max_budget", "budget_duration", "tpm_limit", "rpm_limit", "blocked"} { + if v, ok := d.GetOk(key); ok { + orgData[key] = v + } + } + + return orgData +} diff --git a/terraform/provider/litellm/resource_organization_member.go b/terraform/provider/litellm/resource_organization_member.go new file mode 100644 index 00000000000..e9abd26b9ec --- /dev/null +++ b/terraform/provider/litellm/resource_organization_member.go @@ -0,0 +1,126 @@ +package litellm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceLiteLLMOrganizationMember() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMOrganizationMemberCreate, + Read: resourceLiteLLMOrganizationMemberRead, + Update: resourceLiteLLMOrganizationMemberUpdate, + Delete: resourceLiteLLMOrganizationMemberDelete, + + Schema: map[string]*schema.Schema{ + "organization_id": { + Type: schema.TypeString, + Required: true, + }, + "user_id": { + Type: schema.TypeString, + Required: true, + }, + "user_email": { + Type: schema.TypeString, + Optional: true, + }, + "role": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "org_admin", + "internal_user", + "internal_user_viewer", + }, false), + }, + }, + } +} + +func resourceLiteLLMOrganizationMemberCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + memberData := map[string]interface{}{ + "member": []map[string]interface{}{ + { + "role": d.Get("role").(string), + "user_id": d.Get("user_id").(string), + "user_email": d.Get("user_email").(string), + }, + }, + "organization_id": d.Get("organization_id").(string), + } + + log.Printf("[DEBUG] Create organization member request payload: %+v", memberData) + + resp, err := client.AddOrganizationMember(memberData) + if err != nil { + return fmt.Errorf("error creating organization member: %v", err) + } + + log.Printf("[DEBUG] Create organization member response: %+v", resp) + + // Set a composite ID since there's no specific member ID returned + d.SetId(fmt.Sprintf("%s:%s", d.Get("organization_id").(string), d.Get("user_id").(string))) + + log.Printf("[INFO] Organization member created with ID: %s", d.Id()) + + return resourceLiteLLMOrganizationMemberRead(d, m) +} + +func resourceLiteLLMOrganizationMemberRead(d *schema.ResourceData, m interface{}) error { + // There's no specific endpoint to read a single organization member + // We'll just return the data we have in the state + log.Printf("[INFO] Reading organization member with ID: %s", d.Id()) + return nil +} + +func resourceLiteLLMOrganizationMemberUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + updateData := map[string]interface{}{ + "user_id": d.Get("user_id").(string), + "user_email": d.Get("user_email").(string), + "organization_id": d.Get("organization_id").(string), + "role": d.Get("role").(string), + } + + log.Printf("[DEBUG] Update organization member request payload: %+v", updateData) + + resp, err := client.UpdateOrganizationMember(updateData) + if err != nil { + return fmt.Errorf("error updating organization member: %v", err) + } + + log.Printf("[DEBUG] Update organization member response: %+v", resp) + + log.Printf("[INFO] Successfully updated organization member with ID: %s", d.Id()) + + return resourceLiteLLMOrganizationMemberRead(d, m) +} + +func resourceLiteLLMOrganizationMemberDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + deleteData := map[string]interface{}{ + "user_id": d.Get("user_id").(string), + "user_email": d.Get("user_email").(string), + "organization_id": d.Get("organization_id").(string), + } + + log.Printf("[DEBUG] Delete organization member request payload: %+v", deleteData) + + _, err := client.DeleteOrganizationMember(deleteData) + if err != nil { + return fmt.Errorf("error deleting organization member: %v", err) + } + + log.Printf("[INFO] Successfully deleted organization member with ID: %s", d.Id()) + + d.SetId("") + return nil +} diff --git a/terraform/provider/litellm/resource_organization_member_add.go b/terraform/provider/litellm/resource_organization_member_add.go new file mode 100644 index 00000000000..9bb4de09861 --- /dev/null +++ b/terraform/provider/litellm/resource_organization_member_add.go @@ -0,0 +1,260 @@ +package litellm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceLiteLLMOrganizationMemberAdd() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMOrganizationMemberAddCreate, + Read: resourceLiteLLMOrganizationMemberAddRead, + Update: resourceLiteLLMOrganizationMemberAddUpdate, + Delete: resourceLiteLLMOrganizationMemberAddDelete, + + Schema: map[string]*schema.Schema{ + "organization_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "member": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_id": { + Type: schema.TypeString, + Optional: true, + }, + "user_email": { + Type: schema.TypeString, + Optional: true, + }, + "role": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "org_admin", + "internal_user", + "internal_user_viewer", + }, false), + }, + }, + }, + }, + }, + } +} + +func resourceLiteLLMOrganizationMemberAddCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + orgID := d.Get("organization_id").(string) + members := d.Get("member").(*schema.Set) + + // Convert members to the expected format + membersList := make([]map[string]interface{}, 0, members.Len()) + for _, member := range members.List() { + m := member.(map[string]interface{}) + memberData := map[string]interface{}{ + "role": m["role"].(string), + } + if userID, ok := m["user_id"].(string); ok && userID != "" { + memberData["user_id"] = userID + } + if userEmail, ok := m["user_email"].(string); ok && userEmail != "" { + memberData["user_email"] = userEmail + } + membersList = append(membersList, memberData) + } + + memberData := map[string]interface{}{ + "member": membersList, + "organization_id": orgID, + } + + log.Printf("[DEBUG] Create organization members request payload: %+v", memberData) + + resp, err := client.AddOrganizationMember(memberData) + if err != nil { + return fmt.Errorf("error adding organization members: %v", err) + } + + log.Printf("[DEBUG] Create organization members response: %+v", resp) + + // Set ID as organization_id since this resource manages all members for an organization + d.SetId(orgID) + + return resourceLiteLLMOrganizationMemberAddRead(d, m) +} + +func resourceLiteLLMOrganizationMemberAddRead(d *schema.ResourceData, m interface{}) error { + // The API doesn't provide a way to read specific organization members easily + // We'll maintain the state as is + return nil +} + +func resourceLiteLLMOrganizationMemberAddUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + orgID := d.Get("organization_id").(string) + + o, n := d.GetChange("member") + oldMembers := o.(*schema.Set) + newMembers := n.(*schema.Set) + + // Create maps for easier lookup by user identifier + oldMemberMap := make(map[string]map[string]interface{}) + newMemberMap := make(map[string]map[string]interface{}) + + // Build old member map using user_id or user_email as key + for _, member := range oldMembers.List() { + m := member.(map[string]interface{}) + key := getOrgMemberKey(m) + if key != "" { + oldMemberMap[key] = m + } + } + + // Build new member map using user_id or user_email as key + for _, member := range newMembers.List() { + m := member.(map[string]interface{}) + key := getOrgMemberKey(m) + if key != "" { + newMemberMap[key] = m + } + } + + // Find members to delete (in old but not in new) + for key, oldMember := range oldMemberMap { + if _, exists := newMemberMap[key]; !exists { + deleteData := map[string]interface{}{ + "organization_id": orgID, + } + if userID, ok := oldMember["user_id"].(string); ok && userID != "" { + deleteData["user_id"] = userID + } + if userEmail, ok := oldMember["user_email"].(string); ok && userEmail != "" { + deleteData["user_email"] = userEmail + } + + log.Printf("[DEBUG] Delete organization member request payload: %+v", deleteData) + + _, err := client.DeleteOrganizationMember(deleteData) + if err != nil { + return fmt.Errorf("error deleting organization member: %v", err) + } + } + } + + // Find members to update (exist in both but with different attributes) + for key, newMember := range newMemberMap { + if oldMember, exists := oldMemberMap[key]; exists { + // Check if member attributes have changed + if orgMemberAttributesChanged(oldMember, newMember) { + updateData := map[string]interface{}{ + "organization_id": orgID, + "role": newMember["role"].(string), + } + if userID, ok := newMember["user_id"].(string); ok && userID != "" { + updateData["user_id"] = userID + } + if userEmail, ok := newMember["user_email"].(string); ok && userEmail != "" { + updateData["user_email"] = userEmail + } + + log.Printf("[DEBUG] Update organization member request payload: %+v", updateData) + + _, err := client.UpdateOrganizationMember(updateData) + if err != nil { + return fmt.Errorf("error updating organization member: %v", err) + } + } + } + } + + // Find members to add (in new but not in old) + var membersToAdd []map[string]interface{} + for key, newMember := range newMemberMap { + if _, exists := oldMemberMap[key]; !exists { + memberData := map[string]interface{}{ + "role": newMember["role"].(string), + } + if userID, ok := newMember["user_id"].(string); ok && userID != "" { + memberData["user_id"] = userID + } + if userEmail, ok := newMember["user_email"].(string); ok && userEmail != "" { + memberData["user_email"] = userEmail + } + membersToAdd = append(membersToAdd, memberData) + } + } + + if len(membersToAdd) > 0 { + memberData := map[string]interface{}{ + "member": membersToAdd, + "organization_id": orgID, + } + + log.Printf("[DEBUG] Adding new organization members request payload: %+v", memberData) + + resp, err := client.AddOrganizationMember(memberData) + if err != nil { + return fmt.Errorf("error adding organization members: %v", err) + } + + log.Printf("[DEBUG] Add organization members response: %+v", resp) + } + + return resourceLiteLLMOrganizationMemberAddRead(d, m) +} + +// getOrgMemberKey returns a unique key for a member based on user_id or user_email +func getOrgMemberKey(member map[string]interface{}) string { + if userID, ok := member["user_id"].(string); ok && userID != "" { + return "id:" + userID + } + if userEmail, ok := member["user_email"].(string); ok && userEmail != "" { + return "email:" + userEmail + } + return "" +} + +// orgMemberAttributesChanged checks if member attributes have changed between old and new +func orgMemberAttributesChanged(oldMember, newMember map[string]interface{}) bool { + // Compare role + oldRole, _ := oldMember["role"].(string) + newRole, _ := newMember["role"].(string) + return oldRole != newRole +} + +func resourceLiteLLMOrganizationMemberAddDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + orgID := d.Get("organization_id").(string) + members := d.Get("member").(*schema.Set) + + // Delete each member + for _, member := range members.List() { + m := member.(map[string]interface{}) + deleteData := map[string]interface{}{ + "organization_id": orgID, + } + if userID, ok := m["user_id"].(string); ok && userID != "" { + deleteData["user_id"] = userID + } + if userEmail, ok := m["user_email"].(string); ok && userEmail != "" { + deleteData["user_email"] = userEmail + } + + _, err := client.DeleteOrganizationMember(deleteData) + if err != nil { + return fmt.Errorf("error deleting organization member: %v", err) + } + } + + d.SetId("") + return nil +} diff --git a/terraform/provider/litellm/resource_organization_member_add_test.go b/terraform/provider/litellm/resource_organization_member_add_test.go new file mode 100644 index 00000000000..a26c9ed5812 --- /dev/null +++ b/terraform/provider/litellm/resource_organization_member_add_test.go @@ -0,0 +1,74 @@ +package litellm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccLiteLLMOrganizationMemberAdd_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccLiteLLMOrganizationMemberAddConfig("test-org-bulk", "bulk-user-1", "bulk-user-2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckLiteLLMOrganizationMemberAddExists("litellm_organization_member_add.test_members"), + resource.TestCheckResourceAttr("litellm_organization_member_add.test_members", "member.#", "2"), + ), + }, + }, + }) +} + +func testAccCheckLiteLLMOrganizationMemberAddExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + return nil + } +} + +func testAccLiteLLMOrganizationMemberAddConfig(orgAlias, user1, user2 string) string { + return fmt.Sprintf(` +resource "litellm_model" "test_model" { + model_name = "gpt-3.5-turbo" + custom_llm_provider = "openai" + base_model = "gpt-3.5-turbo" +} + +resource "litellm_organization" "test_org_bulk" { + organization_alias = "%s" + max_budget = 100.0 + budget_duration = "30d" + + depends_on = [litellm_model.test_model] +} + +resource "litellm_organization_member_add" "test_members" { + organization_id = litellm_organization.test_org_bulk.id + + member { + user_id = "%s" + user_email = "%s@example.com" + role = "org_admin" + } + + member { + user_id = "%s" + user_email = "%s@example.com" + role = "internal_user" + } +} +`, orgAlias, user1, user1, user2, user2) +} diff --git a/terraform/provider/litellm/resource_organization_member_test.go b/terraform/provider/litellm/resource_organization_member_test.go new file mode 100644 index 00000000000..8818ed81052 --- /dev/null +++ b/terraform/provider/litellm/resource_organization_member_test.go @@ -0,0 +1,66 @@ +package litellm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccLiteLLMOrganizationMember_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccLiteLLMOrganizationMemberConfig("test-org-member", "test-user-1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckLiteLLMOrganizationMemberExists("litellm_organization_member.test_member"), + resource.TestCheckResourceAttr("litellm_organization_member.test_member", "role", "org_admin"), + resource.TestCheckResourceAttr("litellm_organization_member.test_member", "user_id", "test-user-1"), + ), + }, + }, + }) +} + +func testAccCheckLiteLLMOrganizationMemberExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + return nil + } +} + +func testAccLiteLLMOrganizationMemberConfig(orgAlias, userID string) string { + return fmt.Sprintf(` +resource "litellm_model" "test_model" { + model_name = "gpt-3.5-turbo" + custom_llm_provider = "openai" + base_model = "gpt-3.5-turbo" +} + +resource "litellm_organization" "test_org" { + organization_alias = "%s" + max_budget = 100.0 + budget_duration = "30d" + + depends_on = [litellm_model.test_model] +} + +resource "litellm_organization_member" "test_member" { + organization_id = litellm_organization.test_org.id + user_id = "%s" + user_email = "%s@example.com" + role = "org_admin" +} +`, orgAlias, userID, userID) +} diff --git a/terraform/provider/litellm/resource_organization_test.go b/terraform/provider/litellm/resource_organization_test.go new file mode 100644 index 00000000000..2a2c32438fe --- /dev/null +++ b/terraform/provider/litellm/resource_organization_test.go @@ -0,0 +1,59 @@ +package litellm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccLiteLLMOrganization_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccLiteLLMOrganizationConfig("test-org", "test-org-alias"), + Check: resource.ComposeTestCheckFunc( + testAccCheckLiteLLMOrganizationExists("litellm_organization.test"), + resource.TestCheckResourceAttr("litellm_organization.test", "organization_alias", "test-org-alias"), + resource.TestCheckResourceAttr("litellm_organization.test", "max_budget", "100"), + ), + }, + }, + }) +} + +func testAccCheckLiteLLMOrganizationExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + return nil + } +} + +func testAccLiteLLMOrganizationConfig(name, alias string) string { + return fmt.Sprintf(` +resource "litellm_model" "test_model" { + model_name = "gpt-3.5-turbo" + custom_llm_provider = "openai" + base_model = "gpt-3.5-turbo" +} + +resource "litellm_organization" "test" { + organization_alias = "%s" + max_budget = 100.0 + budget_duration = "30d" + + depends_on = [litellm_model.test_model] +} +`, alias) +} diff --git a/terraform/provider/litellm/resource_team.go b/terraform/provider/litellm/resource_team.go new file mode 100644 index 00000000000..88e0dcd4811 --- /dev/null +++ b/terraform/provider/litellm/resource_team.go @@ -0,0 +1,311 @@ +package litellm + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + endpointTeamNew = "/team/new" + endpointTeamInfo = "/team/info" + endpointTeamUpdate = "/team/update" + endpointTeamDelete = "/team/delete" + endpointTeamPermissionsList = "/team/permissions_list" + endpointTeamPermissionsUpdate = "/team/permissions_update" +) + +func ResourceLiteLLMTeam() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMTeamCreate, + Read: resourceLiteLLMTeamRead, + Update: resourceLiteLLMTeamUpdate, + Delete: resourceLiteLLMTeamDelete, + + Schema: map[string]*schema.Schema{ + "team_alias": { + Type: schema.TypeString, + Required: true, + }, + "organization_id": { + Type: schema.TypeString, + Optional: true, + }, + "metadata": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "tpm_limit": { + Type: schema.TypeInt, + Optional: true, + }, + "rpm_limit": { + Type: schema.TypeInt, + Optional: true, + }, + "max_budget": { + Type: schema.TypeFloat, + Optional: true, + }, + "budget_duration": { + Type: schema.TypeString, + Optional: true, + }, + "models": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "blocked": { + Type: schema.TypeBool, + Optional: true, + }, + "team_member_permissions": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "List of permissions granted to team members", + }, + }, + } +} + +func resourceLiteLLMTeamCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + teamID := uuid.New().String() + teamData := buildTeamData(d, teamID) + + log.Printf("[DEBUG] Create team request payload: %+v", teamData) + + resp, err := MakeRequest(client, "POST", endpointTeamNew, teamData) + if err != nil { + return fmt.Errorf("error creating team: %w", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "creating team"); err != nil { + return err + } + + d.SetId(teamID) + log.Printf("[INFO] Team created with ID: %s", teamID) + + return resourceLiteLLMTeamRead(d, m) +} + +func resourceLiteLLMTeamRead(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + log.Printf("[INFO] Reading team with ID: %s", d.Id()) + + resp, err := MakeRequest(client, "GET", fmt.Sprintf("%s?team_id=%s", endpointTeamInfo, d.Id()), nil) + if err != nil { + return fmt.Errorf("error reading team: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + log.Printf("[WARN] Team with ID %s not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + var teamResp TeamResponse + if err := json.NewDecoder(resp.Body).Decode(&teamResp); err != nil { + return fmt.Errorf("error decoding team info response: %w", err) + } + + // Update the state with values from the response or fall back to the data passed in during creation + d.Set("team_alias", GetStringValue(teamResp.TeamAlias, d.Get("team_alias").(string))) + d.Set("organization_id", GetStringValue(teamResp.OrganizationID, d.Get("organization_id").(string))) + + // Handle metadata separately as it's a map + if teamResp.Metadata != nil { + d.Set("metadata", teamResp.Metadata) + } else { + d.Set("metadata", d.Get("metadata")) + } + + if teamResp.TPMLimit != nil { + d.Set("tpm_limit", *teamResp.TPMLimit) + } + if teamResp.RPMLimit != nil { + d.Set("rpm_limit", *teamResp.RPMLimit) + } + if teamResp.MaxBudget != nil { + d.Set("max_budget", *teamResp.MaxBudget) + } + d.Set("budget_duration", GetStringValue(teamResp.BudgetDuration, d.Get("budget_duration").(string))) + + // Handle models separately as it's a list + if teamResp.Models != nil { + d.Set("models", teamResp.Models) + } else { + d.Set("models", d.Get("models")) + } + + d.Set("blocked", GetBoolValue(teamResp.Blocked, d.Get("blocked").(bool))) + + // Explicitly fetch the current permissions from the API + permResp, err := getTeamPermissions(client, d.Id()) + if err != nil { + log.Printf("[WARN] Error fetching team permissions: %s", err) + // Fall back to the permissions from the team info response + if teamResp.TeamMemberPermissions != nil { + d.Set("team_member_permissions", teamResp.TeamMemberPermissions) + } else { + d.Set("team_member_permissions", d.Get("team_member_permissions")) + } + } else { + // Use the permissions from the permissions_list endpoint + log.Printf("[DEBUG] Team permissions from API: %+v", permResp.TeamMemberPermissions) + d.Set("team_member_permissions", permResp.TeamMemberPermissions) + } + + log.Printf("[INFO] Successfully read team with ID: %s", d.Id()) + return nil +} + +func resourceLiteLLMTeamUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + teamData := buildTeamData(d, d.Id()) + log.Printf("[DEBUG] Update team request payload: %+v", teamData) + + resp, err := MakeRequest(client, "POST", endpointTeamUpdate, teamData) + if err != nil { + return fmt.Errorf("error updating team: %w", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "updating team"); err != nil { + return err + } + + // Check if team_member_permissions have changed and explicitly update them + if d.HasChange("team_member_permissions") { + _, newPerms := d.GetChange("team_member_permissions") + if newPerms != nil { + // Convert interface{} to []string + var permissions []string + for _, perm := range newPerms.([]interface{}) { + permissions = append(permissions, perm.(string)) + } + + log.Printf("[DEBUG] Explicitly updating team permissions: %+v", permissions) + if err := updateTeamPermissions(client, d.Id(), permissions); err != nil { + return fmt.Errorf("error updating team permissions: %w", err) + } + } + } + + log.Printf("[INFO] Successfully updated team with ID: %s", d.Id()) + return resourceLiteLLMTeamRead(d, m) +} + +func resourceLiteLLMTeamDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + log.Printf("[INFO] Deleting team with ID: %s", d.Id()) + + deleteData := map[string]interface{}{ + "team_ids": []string{d.Id()}, + } + + resp, err := MakeRequest(client, "POST", endpointTeamDelete, deleteData) + if err != nil { + return fmt.Errorf("error deleting team: %w", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "deleting team"); err != nil { + return err + } + + log.Printf("[INFO] Successfully deleted team with ID: %s", d.Id()) + d.SetId("") + return nil +} + +func buildTeamData(d *schema.ResourceData, teamID string) map[string]interface{} { + teamData := map[string]interface{}{ + "team_id": teamID, + "team_alias": d.Get("team_alias").(string), + } + + for _, key := range []string{"organization_id", "metadata", "tpm_limit", "rpm_limit", "max_budget", "budget_duration", "models", "blocked", "team_member_permissions"} { + if v, ok := d.GetOk(key); ok { + teamData[key] = v + } + } + + return teamData +} + +func handleResponse(resp *http.Response, action string) error { + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return fmt.Errorf("error %s: %s - %s", action, resp.Status, string(body)) + } + return nil +} + +// TeamPermissionsResponse represents a response from the API containing team permissions information. +type TeamPermissionsResponse struct { + TeamID string `json:"team_id"` + TeamMemberPermissions []string `json:"team_member_permissions"` + AllAvailablePermissions []string `json:"all_available_permissions"` +} + +// getTeamPermissions retrieves the current permissions and available permissions for a team. +func getTeamPermissions(client *Client, teamID string) (*TeamPermissionsResponse, error) { + log.Printf("[INFO] Getting permissions for team with ID: %s", teamID) + + resp, err := MakeRequest(client, "GET", fmt.Sprintf("%s?team_id=%s", endpointTeamPermissionsList, teamID), nil) + if err != nil { + return nil, fmt.Errorf("error getting team permissions: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("error getting team permissions: %s - %s", resp.Status, string(body)) + } + + var permResp TeamPermissionsResponse + if err := json.NewDecoder(resp.Body).Decode(&permResp); err != nil { + return nil, fmt.Errorf("error decoding team permissions response: %w", err) + } + + return &permResp, nil +} + +// updateTeamPermissions updates the permissions for a team. +func updateTeamPermissions(client *Client, teamID string, permissions []string) error { + log.Printf("[INFO] Updating permissions for team with ID: %s", teamID) + + permData := map[string]interface{}{ + "team_id": teamID, + "team_member_permissions": permissions, + } + + resp, err := MakeRequest(client, "POST", endpointTeamPermissionsUpdate, permData) + if err != nil { + return fmt.Errorf("error updating team permissions: %w", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "updating team permissions"); err != nil { + return err + } + + log.Printf("[INFO] Successfully updated permissions for team with ID: %s", teamID) + return nil +} diff --git a/terraform/provider/litellm/resource_team_member.go b/terraform/provider/litellm/resource_team_member.go new file mode 100644 index 00000000000..84db07239fd --- /dev/null +++ b/terraform/provider/litellm/resource_team_member.go @@ -0,0 +1,146 @@ +package litellm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceLiteLLMTeamMember() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMTeamMemberCreate, + Read: resourceLiteLLMTeamMemberRead, + Update: resourceLiteLLMTeamMemberUpdate, + Delete: resourceLiteLLMTeamMemberDelete, + + Schema: map[string]*schema.Schema{ + "team_id": { + Type: schema.TypeString, + Required: true, + }, + "user_id": { + Type: schema.TypeString, + Required: true, + }, + "user_email": { + Type: schema.TypeString, + Required: true, + }, + "role": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "org_admin", + "internal_user", + "internal_user_viewer", + "admin", + "user", + }, false), + }, + "max_budget_in_team": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + } +} + +func resourceLiteLLMTeamMemberCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + memberData := map[string]interface{}{ + "member": []map[string]interface{}{ + { + "role": d.Get("role").(string), + "user_id": d.Get("user_id").(string), + "user_email": d.Get("user_email").(string), + }, + }, + "team_id": d.Get("team_id").(string), + "max_budget_in_team": d.Get("max_budget_in_team").(float64), + } + + log.Printf("[DEBUG] Create team member request payload: %+v", memberData) + + resp, err := MakeRequest(client, "POST", "/team/member_add", memberData) + if err != nil { + return fmt.Errorf("error creating team member: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "creating team member"); err != nil { + return err + } + + // Set a composite ID since there's no specific member ID returned + d.SetId(fmt.Sprintf("%s:%s", d.Get("team_id").(string), d.Get("user_id").(string))) + + log.Printf("[INFO] Team member created with ID: %s", d.Id()) + + return resourceLiteLLMTeamMemberRead(d, m) +} + +func resourceLiteLLMTeamMemberRead(d *schema.ResourceData, m interface{}) error { + // There's no specific endpoint to read a single team member + // We might need to read the entire team and find the member + // For now, we'll just return the data we have in the state + log.Printf("[INFO] Reading team member with ID: %s", d.Id()) + return nil +} + +func resourceLiteLLMTeamMemberUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + updateData := map[string]interface{}{ + "user_id": d.Get("user_id").(string), + "user_email": d.Get("user_email").(string), + "team_id": d.Get("team_id").(string), + "role": d.Get("role").(string), + "max_budget_in_team": d.Get("max_budget_in_team").(float64), + } + + log.Printf("[DEBUG] Update team member request payload: %+v", updateData) + + resp, err := MakeRequest(client, "POST", "/team/member_update", updateData) + if err != nil { + return fmt.Errorf("error updating team member: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "updating team member"); err != nil { + return err + } + + log.Printf("[INFO] Successfully updated team member with ID: %s", d.Id()) + + return resourceLiteLLMTeamMemberRead(d, m) +} + +func resourceLiteLLMTeamMemberDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + deleteData := map[string]interface{}{ + "user_id": d.Get("user_id").(string), + "user_email": d.Get("user_email").(string), + "team_id": d.Get("team_id").(string), + } + + log.Printf("[DEBUG] Delete team member request payload: %+v", deleteData) + + resp, err := MakeRequest(client, "POST", "/team/member_delete", deleteData) + if err != nil { + return fmt.Errorf("error deleting team member: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "deleting team member"); err != nil { + return err + } + + log.Printf("[INFO] Successfully deleted team member with ID: %s", d.Id()) + + d.SetId("") + return nil +} diff --git a/terraform/provider/litellm/resource_team_member_add.go b/terraform/provider/litellm/resource_team_member_add.go new file mode 100644 index 00000000000..da5c7a6ebd7 --- /dev/null +++ b/terraform/provider/litellm/resource_team_member_add.go @@ -0,0 +1,342 @@ +package litellm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceLiteLLMTeamMemberAdd() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMTeamMemberAddCreate, + Read: resourceLiteLLMTeamMemberAddRead, + Update: resourceLiteLLMTeamMemberAddUpdate, + Delete: resourceLiteLLMTeamMemberAddDelete, + + Schema: map[string]*schema.Schema{ + "team_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "member": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_id": { + Type: schema.TypeString, + Optional: true, + }, + "user_email": { + Type: schema.TypeString, + Optional: true, + }, + "role": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "admin", + "user", + }, false), + }, + }, + }, + }, + "max_budget_in_team": { + Type: schema.TypeFloat, + Optional: true, + }, + }, + } +} + +func resourceLiteLLMTeamMemberAddCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + teamID := d.Get("team_id").(string) + members := d.Get("member").(*schema.Set) + maxBudget := d.Get("max_budget_in_team").(float64) + + // Convert members to the expected format + membersList := make([]map[string]interface{}, 0, members.Len()) + for _, member := range members.List() { + m := member.(map[string]interface{}) + memberData := map[string]interface{}{ + "role": m["role"].(string), + } + if userID, ok := m["user_id"].(string); ok && userID != "" { + memberData["user_id"] = userID + } + if userEmail, ok := m["user_email"].(string); ok && userEmail != "" { + memberData["user_email"] = userEmail + } + membersList = append(membersList, memberData) + } + + memberData := map[string]interface{}{ + "member": membersList, + "team_id": teamID, + "max_budget_in_team": maxBudget, + } + + log.Printf("[DEBUG] Create team members request payload: %+v", memberData) + + resp, err := MakeRequest(client, "POST", "/team/member_add", memberData) + if err != nil { + return fmt.Errorf("error adding team members: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "adding team members"); err != nil { + return err + } + + // Set ID as team_id since this resource manages all members for a team + d.SetId(teamID) + + return resourceLiteLLMTeamMemberAddRead(d, m) +} + +func resourceLiteLLMTeamMemberAddRead(d *schema.ResourceData, m interface{}) error { + // The API doesn't provide a way to read specific team members + // We'll maintain the state as is + return nil +} + +func resourceLiteLLMTeamMemberAddUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + teamID := d.Get("team_id").(string) + maxBudget := d.Get("max_budget_in_team").(float64) + + o, n := d.GetChange("member") + oldMembers := o.(*schema.Set) + newMembers := n.(*schema.Set) + + // Create maps for easier lookup by user identifier + oldMemberMap := make(map[string]map[string]interface{}) + newMemberMap := make(map[string]map[string]interface{}) + + // Build old member map using user_id or user_email as key + for _, member := range oldMembers.List() { + m := member.(map[string]interface{}) + key := getMemberKey(m) + if key != "" { + oldMemberMap[key] = m + } + } + + // Build new member map using user_id or user_email as key + for _, member := range newMembers.List() { + m := member.(map[string]interface{}) + key := getMemberKey(m) + if key != "" { + newMemberMap[key] = m + } + } + + // Track which members have been updated to avoid duplicates + updatedMembers := make(map[string]bool) + + // Check if max_budget_in_team has changed + if d.HasChange("max_budget_in_team") { + log.Printf("[DEBUG] max_budget_in_team changed, updating all existing members with new budget: %f", maxBudget) + + // Update ALL existing members with the new budget + for key, newMember := range newMemberMap { + if _, exists := oldMemberMap[key]; exists { + updateData := map[string]interface{}{ + "team_id": teamID, + "role": newMember["role"].(string), + "max_budget_in_team": maxBudget, + } + if userID, ok := newMember["user_id"].(string); ok && userID != "" { + updateData["user_id"] = userID + } + if userEmail, ok := newMember["user_email"].(string); ok && userEmail != "" { + updateData["user_email"] = userEmail + } + + log.Printf("[DEBUG] Update team member budget request payload: %+v", updateData) + + resp, err := MakeRequest(client, "POST", "/team/member_update", updateData) + if err != nil { + return fmt.Errorf("error updating team member budget: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "updating team member budget"); err != nil { + return err + } + + // Mark this member as updated + updatedMembers[key] = true + } + } + } + + // Find members to delete (in old but not in new) + for key, oldMember := range oldMemberMap { + if _, exists := newMemberMap[key]; !exists { + deleteData := map[string]interface{}{ + "team_id": teamID, + } + if userID, ok := oldMember["user_id"].(string); ok && userID != "" { + deleteData["user_id"] = userID + } + if userEmail, ok := oldMember["user_email"].(string); ok && userEmail != "" { + deleteData["user_email"] = userEmail + } + + log.Printf("[DEBUG] Delete team member request payload: %+v", deleteData) + + resp, err := MakeRequest(client, "POST", "/team/member_delete", deleteData) + if err != nil { + return fmt.Errorf("error deleting team member: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "deleting team member"); err != nil { + return err + } + } + } + + // Find members to update (exist in both but with different attributes) + // Skip members that were already updated due to budget change + for key, newMember := range newMemberMap { + if oldMember, exists := oldMemberMap[key]; exists { + // Skip if already updated due to budget change + if updatedMembers[key] { + continue + } + + // Check if member attributes have changed + if memberAttributesChanged(oldMember, newMember) { + updateData := map[string]interface{}{ + "team_id": teamID, + "role": newMember["role"].(string), + "max_budget_in_team": maxBudget, + } + if userID, ok := newMember["user_id"].(string); ok && userID != "" { + updateData["user_id"] = userID + } + if userEmail, ok := newMember["user_email"].(string); ok && userEmail != "" { + updateData["user_email"] = userEmail + } + + log.Printf("[DEBUG] Update team member request payload: %+v", updateData) + + resp, err := MakeRequest(client, "POST", "/team/member_update", updateData) + if err != nil { + return fmt.Errorf("error updating team member: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "updating team member"); err != nil { + return err + } + } + } + } + + // Find members to add (in new but not in old) + var membersToAdd []map[string]interface{} + for key, newMember := range newMemberMap { + if _, exists := oldMemberMap[key]; !exists { + memberData := map[string]interface{}{ + "role": newMember["role"].(string), + } + if userID, ok := newMember["user_id"].(string); ok && userID != "" { + memberData["user_id"] = userID + } + if userEmail, ok := newMember["user_email"].(string); ok && userEmail != "" { + memberData["user_email"] = userEmail + } + membersToAdd = append(membersToAdd, memberData) + } + } + + if len(membersToAdd) > 0 { + memberData := map[string]interface{}{ + "member": membersToAdd, + "team_id": teamID, + "max_budget_in_team": maxBudget, + } + + log.Printf("[DEBUG] Adding new team members request payload: %+v", memberData) + + resp, err := MakeRequest(client, "POST", "/team/member_add", memberData) + if err != nil { + return fmt.Errorf("error adding team members: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "adding team members"); err != nil { + return err + } + } + + return resourceLiteLLMTeamMemberAddRead(d, m) +} + +// getMemberKey returns a unique key for a member based on user_id or user_email +func getMemberKey(member map[string]interface{}) string { + if userID, ok := member["user_id"].(string); ok && userID != "" { + return "id:" + userID + } + if userEmail, ok := member["user_email"].(string); ok && userEmail != "" { + return "email:" + userEmail + } + return "" +} + +// memberAttributesChanged checks if member attributes have changed between old and new +func memberAttributesChanged(oldMember, newMember map[string]interface{}) bool { + // Compare role + oldRole, _ := oldMember["role"].(string) + newRole, _ := newMember["role"].(string) + if oldRole != newRole { + return true + } + + // Note: max_budget_in_team is handled at the resource level, not per member + // so we don't need to compare it here + + return false +} + +func resourceLiteLLMTeamMemberAddDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + teamID := d.Get("team_id").(string) + members := d.Get("member").(*schema.Set) + + // Delete each member + for _, member := range members.List() { + m := member.(map[string]interface{}) + deleteData := map[string]interface{}{ + "team_id": teamID, + } + if userID, ok := m["user_id"].(string); ok && userID != "" { + deleteData["user_id"] = userID + } + if userEmail, ok := m["user_email"].(string); ok && userEmail != "" { + deleteData["user_email"] = userEmail + } + + resp, err := MakeRequest(client, "POST", "/team/member_delete", deleteData) + if err != nil { + return fmt.Errorf("error deleting team member: %v", err) + } + defer resp.Body.Close() + + if err := handleResponse(resp, "deleting team member"); err != nil { + return err + } + } + + d.SetId("") + return nil +} diff --git a/terraform/provider/litellm/resource_team_member_test.go b/terraform/provider/litellm/resource_team_member_test.go new file mode 100644 index 00000000000..156c4b3abfa --- /dev/null +++ b/terraform/provider/litellm/resource_team_member_test.go @@ -0,0 +1,44 @@ +package litellm + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestTeamMemberUpdateSendsRole(t *testing.T) { + var captured map[string]interface{} + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + json.Unmarshal(body, &captured) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{}`)) + })) + defer srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := schema.TestResourceDataRaw(t, resourceLiteLLMTeamMember().Schema, map[string]interface{}{ + "team_id": "team-1", + "user_id": "user-1", + "user_email": "user@example.com", + "role": "user", + }) + d.SetId("team-1:user-1") + + if err := resourceLiteLLMTeamMemberUpdate(d, client); err != nil { + t.Fatalf("update failed: %v", err) + } + + role, ok := captured["role"] + if !ok { + t.Fatalf("update payload missing role field: %v", captured) + } + if role != "user" { + t.Fatalf("update payload sent role %v, want user", role) + } +} diff --git a/terraform/provider/litellm/resource_vector_store.go b/terraform/provider/litellm/resource_vector_store.go new file mode 100644 index 00000000000..f77ba18c6d4 --- /dev/null +++ b/terraform/provider/litellm/resource_vector_store.go @@ -0,0 +1,65 @@ +package litellm + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceLiteLLMVectorStore() *schema.Resource { + return &schema.Resource{ + Create: resourceLiteLLMVectorStoreCreate, + Read: resourceLiteLLMVectorStoreRead, + Update: resourceLiteLLMVectorStoreUpdate, + Delete: resourceLiteLLMVectorStoreDelete, + + Schema: map[string]*schema.Schema{ + "vector_store_id": { + Type: schema.TypeString, + Computed: true, + Description: "Unique identifier for the vector store", + }, + "vector_store_name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the vector store", + }, + "custom_llm_provider": { + Type: schema.TypeString, + Required: true, + Description: "Custom LLM provider for the vector store", + }, + "vector_store_description": { + Type: schema.TypeString, + Optional: true, + Description: "Description of the vector store", + }, + "vector_store_metadata": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Metadata associated with the vector store", + }, + "litellm_credential_name": { + Type: schema.TypeString, + Optional: true, + Description: "Name of the LiteLLM credential to use", + }, + "litellm_params": { + Type: schema.TypeMap, + Optional: true, + Sensitive: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Additional LiteLLM parameters", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Timestamp when the vector store was created", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Timestamp when the vector store was last updated", + }, + }, + } +} diff --git a/terraform/provider/litellm/resource_vector_store_crud.go b/terraform/provider/litellm/resource_vector_store_crud.go new file mode 100644 index 00000000000..b05017f7125 --- /dev/null +++ b/terraform/provider/litellm/resource_vector_store_crud.go @@ -0,0 +1,168 @@ +package litellm + +import ( + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceLiteLLMVectorStoreCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + + vectorStoreName := d.Get("vector_store_name").(string) + customLLMProvider := d.Get("custom_llm_provider").(string) + vectorStoreDescription := d.Get("vector_store_description").(string) + vectorStoreMetadata := d.Get("vector_store_metadata").(map[string]interface{}) + litellmCredentialName := d.Get("litellm_credential_name").(string) + litellmParams := d.Get("litellm_params").(map[string]interface{}) + + // Convert metadata to map[string]interface{} for JSON + metadataMap := make(map[string]interface{}) + for k, v := range vectorStoreMetadata { + metadataMap[k] = v + } + + // Convert litellm_params to map[string]interface{} for JSON + paramsMap := make(map[string]interface{}) + for k, v := range litellmParams { + paramsMap[k] = v + } + + vectorStoreRequest := VectorStoreRequest{ + CustomLLMProvider: customLLMProvider, + VectorStoreName: vectorStoreName, + VectorStoreDescription: vectorStoreDescription, + VectorStoreMetadata: metadataMap, + LiteLLMCredentialName: litellmCredentialName, + LiteLLMParams: paramsMap, + } + + resp, err := MakeRequest(client, "POST", "/vector_store/new", vectorStoreRequest) + if err != nil { + return fmt.Errorf("failed to create vector store: %w", err) + } + defer resp.Body.Close() + + err = handleVectorStoreAPIResponse(resp, nil, client) + if err != nil { + return fmt.Errorf("failed to create vector store: %w", err) + } + + // Set the resource ID to the vector store name for now + // We'll update this after reading the response to get the actual ID + d.SetId(vectorStoreName) + + return resourceLiteLLMVectorStoreRead(d, m) +} + +func resourceLiteLLMVectorStoreRead(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + vectorStoreID := d.Id() + + // Use the info endpoint to get vector store details + infoRequest := VectorStoreInfoRequest{ + VectorStoreID: vectorStoreID, + } + + resp, err := MakeRequest(client, "POST", "/vector_store/info", infoRequest) + if err != nil { + return fmt.Errorf("failed to read vector store: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + + var vectorStoreResp VectorStoreResponse + err = handleVectorStoreAPIResponse(resp, &vectorStoreResp, client) + if err != nil { + if err.Error() == "vector_store_not_found" { + d.SetId("") + return nil + } + return fmt.Errorf("failed to read vector store: %w", err) + } + + // Update the resource ID to the actual vector store ID from the response + if vectorStoreResp.VectorStoreID != "" { + d.SetId(vectorStoreResp.VectorStoreID) + } + + d.Set("vector_store_id", vectorStoreResp.VectorStoreID) + d.Set("vector_store_name", vectorStoreResp.VectorStoreName) + d.Set("custom_llm_provider", vectorStoreResp.CustomLLMProvider) + d.Set("vector_store_description", vectorStoreResp.VectorStoreDescription) + d.Set("vector_store_metadata", vectorStoreResp.VectorStoreMetadata) + d.Set("litellm_credential_name", vectorStoreResp.LiteLLMCredentialName) + d.Set("created_at", vectorStoreResp.CreatedAt) + d.Set("updated_at", vectorStoreResp.UpdatedAt) + + return nil +} + +func resourceLiteLLMVectorStoreUpdate(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + vectorStoreID := d.Id() + + vectorStoreName := d.Get("vector_store_name").(string) + customLLMProvider := d.Get("custom_llm_provider").(string) + vectorStoreDescription := d.Get("vector_store_description").(string) + vectorStoreMetadata := d.Get("vector_store_metadata").(map[string]interface{}) + + // Convert metadata to map[string]interface{} for JSON + metadataMap := make(map[string]interface{}) + for k, v := range vectorStoreMetadata { + metadataMap[k] = v + } + + vectorStoreRequest := VectorStoreRequest{ + VectorStoreID: vectorStoreID, + CustomLLMProvider: customLLMProvider, + VectorStoreName: vectorStoreName, + VectorStoreDescription: vectorStoreDescription, + VectorStoreMetadata: metadataMap, + } + + resp, err := MakeRequest(client, "POST", "/vector_store/update", vectorStoreRequest) + if err != nil { + return fmt.Errorf("failed to update vector store: %w", err) + } + defer resp.Body.Close() + + err = handleVectorStoreAPIResponse(resp, nil, client) + if err != nil { + return fmt.Errorf("failed to update vector store: %w", err) + } + + return resourceLiteLLMVectorStoreRead(d, m) +} + +func resourceLiteLLMVectorStoreDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*Client) + vectorStoreID := d.Id() + + deleteRequest := VectorStoreDeleteRequest{ + VectorStoreID: vectorStoreID, + } + + resp, err := MakeRequest(client, "POST", "/vector_store/delete", deleteRequest) + if err != nil { + return fmt.Errorf("failed to delete vector store: %w", err) + } + defer resp.Body.Close() + + err = handleVectorStoreAPIResponse(resp, nil, client) + if err != nil { + if err.Error() == "vector_store_not_found" { + d.SetId("") + return nil + } + return fmt.Errorf("failed to delete vector store: %w", err) + } + + d.SetId("") + return nil +} diff --git a/terraform/provider/litellm/resource_vector_store_crud_test.go b/terraform/provider/litellm/resource_vector_store_crud_test.go new file mode 100644 index 00000000000..485ec54346d --- /dev/null +++ b/terraform/provider/litellm/resource_vector_store_crud_test.go @@ -0,0 +1,55 @@ +package litellm + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestVectorStoreReadDoesNotPersistServerLitellmParams(t *testing.T) { + resp := VectorStoreResponse{ + VectorStoreID: "vs-123", + VectorStoreName: "kb", + CustomLLMProvider: "openai", + LiteLLMParams: map[string]interface{}{ + "api_key": "sk-from-server", + "api_base": "https://upstream.example.com", + }, + } + body, _ := json.Marshal(resp) + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(body) + })) + defer srv.Close() + + client := NewClient(srv.URL, "test-key", true) + d := schema.TestResourceDataRaw(t, resourceLiteLLMVectorStore().Schema, map[string]interface{}{ + "vector_store_name": "kb", + "custom_llm_provider": "openai", + "litellm_params": map[string]interface{}{ + "vector_store_id": "vs-123", + }, + }) + d.SetId("vs-123") + + if err := resourceLiteLLMVectorStoreRead(d, client); err != nil { + t.Fatalf("read failed: %v", err) + } + + got := d.Get("litellm_params").(map[string]interface{}) + if _, leaked := got["api_key"]; leaked { + t.Fatalf("server-returned api_key persisted into state: %v", got) + } + if got["vector_store_id"] != "vs-123" { + t.Fatalf("config litellm_params not preserved: %v", got) + } + if d.Get("vector_store_name").(string) != "kb" { + t.Fatalf("read did not populate non-sensitive fields") + } +} diff --git a/terraform/provider/litellm/types.go b/terraform/provider/litellm/types.go new file mode 100644 index 00000000000..069fe4b3e23 --- /dev/null +++ b/terraform/provider/litellm/types.go @@ -0,0 +1,248 @@ +package litellm + +// ProviderConfig holds the configuration for the LiteLLM provider. +type ProviderConfig struct { + APIBase string + APIKey string + InsecureSkipVerify bool +} + +// ErrorResponse represents an error response from the API. +type ErrorResponse struct { + Error struct { + Message interface{} `json:"message"` + } `json:"error"` + Detail struct { + Error string `json:"error"` + } `json:"detail"` +} + +// ModelResponse represents a response from the API containing model information. +type ModelResponse struct { + ModelName string `json:"model_name"` + LiteLLMParams LiteLLMParams `json:"litellm_params"` + ModelInfo ModelInfo `json:"model_info"` + Additional map[string]interface{} `json:"additional"` +} + +// ModelRequest represents a request to create or update a model. +type ModelRequest struct { + ModelName string `json:"model_name"` + LiteLLMParams map[string]interface{} `json:"litellm_params"` + ModelInfo ModelInfo `json:"model_info"` + Additional map[string]interface{} `json:"additional"` +} + +// TeamResponse represents a response from the API containing team information. +type TeamResponse struct { + TeamID string `json:"team_id,omitempty"` + TeamAlias string `json:"team_alias,omitempty"` + OrganizationID string `json:"organization_id,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + TPMLimit *int `json:"tpm_limit,omitempty"` + RPMLimit *int `json:"rpm_limit,omitempty"` + MaxBudget *float64 `json:"max_budget,omitempty"` + BudgetDuration string `json:"budget_duration,omitempty"` + Models []string `json:"models"` + Blocked bool `json:"blocked,omitempty"` + TeamMemberPermissions []string `json:"team_member_permissions,omitempty"` +} + +// OrganizationResponse represents a response from the API containing organization information. +type OrganizationResponse struct { + OrganizationID string `json:"organization_id,omitempty"` + OrganizationAlias string `json:"organization_alias,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + Models []string `json:"models,omitempty"` + MaxBudget *float64 `json:"max_budget,omitempty"` + BudgetDuration string `json:"budget_duration,omitempty"` + TPMLimit *int `json:"tpm_limit,omitempty"` + RPMLimit *int `json:"rpm_limit,omitempty"` + Blocked bool `json:"blocked,omitempty"` +} + +// LiteLLMParams represents the parameters for LiteLLM. +type LiteLLMParams struct { + CustomLLMProvider string `json:"custom_llm_provider"` + TPM int `json:"tpm,omitempty"` + RPM int `json:"rpm,omitempty"` + ReasoningEffort string `json:"reasoning_effort,omitempty"` + Thinking map[string]interface{} `json:"thinking,omitempty"` + MergeReasoningContentInChoices bool `json:"merge_reasoning_content_in_choices,omitempty"` + APIKey string `json:"api_key,omitempty"` + APIBase string `json:"api_base,omitempty"` + APIVersion string `json:"api_version,omitempty"` + Model string `json:"model"` + InputCostPerToken float64 `json:"input_cost_per_token,omitempty"` + OutputCostPerToken float64 `json:"output_cost_per_token,omitempty"` + InputCostPerPixel float64 `json:"input_cost_per_pixel,omitempty"` + OutputCostPerPixel float64 `json:"output_cost_per_pixel,omitempty"` + InputCostPerSecond float64 `json:"input_cost_per_second,omitempty"` + OutputCostPerSecond float64 `json:"output_cost_per_second,omitempty"` + AWSAccessKeyID string `json:"aws_access_key_id,omitempty"` + AWSSecretAccessKey string `json:"aws_secret_access_key,omitempty"` + AWSRegionName string `json:"aws_region_name,omitempty"` + AWSSessionName string `json:"aws_session_name,omitempty"` + AWSRoleName string `json:"aws_role_name,omitempty"` + VertexProject string `json:"vertex_project,omitempty"` + VertexLocation string `json:"vertex_location,omitempty"` + VertexCredentials string `json:"vertex_credentials,omitempty"` +} + +// ModelInfo represents information about a model. +type ModelInfo struct { + ID string `json:"id"` + DBModel bool `json:"db_model"` + BaseModel string `json:"base_model"` + Tier string `json:"tier"` + Mode string `json:"mode"` + TeamID string `json:"team_id,omitempty"` +} + +// Key represents a LiteLLM API key. +type Key struct { + Key string `json:"key,omitempty"` + TokenID string `json:"token_id,omitempty"` + Models []string `json:"models"` + Spend float64 `json:"spend,omitempty"` + MaxBudget *float64 `json:"max_budget,omitempty"` + UserID string `json:"user_id,omitempty"` + TeamID string `json:"team_id,omitempty"` + MaxParallelRequests *int `json:"max_parallel_requests,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + TPMLimit *int `json:"tpm_limit,omitempty"` + RPMLimit *int `json:"rpm_limit,omitempty"` + BudgetDuration string `json:"budget_duration,omitempty"` + AllowedCacheControls []string `json:"allowed_cache_controls,omitempty"` + SoftBudget *float64 `json:"soft_budget,omitempty"` + KeyAlias string `json:"key_alias,omitempty"` + Duration string `json:"duration,omitempty"` + Aliases map[string]interface{} `json:"aliases,omitempty"` + Config map[string]interface{} `json:"config,omitempty"` + Permissions map[string]interface{} `json:"permissions,omitempty"` + ModelMaxBudget map[string]interface{} `json:"model_max_budget,omitempty"` + ModelRPMLimit map[string]interface{} `json:"model_rpm_limit,omitempty"` + ModelTPMLimit map[string]interface{} `json:"model_tpm_limit,omitempty"` + Guardrails []string `json:"guardrails,omitempty"` + Blocked bool `json:"blocked"` + Tags []string `json:"tags,omitempty"` +} + +// KeyResponse represents a response from the API containing key information. +type KeyResponse struct { + Key string `json:"key"` +} + +// MCPServerCostInfo represents cost information for MCP server tools. +type MCPServerCostInfo struct { + DefaultCostPerQuery float64 `json:"default_cost_per_query,omitempty"` + ToolNameToCostPerQuery map[string]float64 `json:"tool_name_to_cost_per_query,omitempty"` +} + +// MCPInfo represents MCP server information and configuration. +type MCPInfo struct { + ServerName string `json:"server_name,omitempty"` + Description string `json:"description,omitempty"` + LogoURL string `json:"logo_url,omitempty"` + MCPServerCostInfo *MCPServerCostInfo `json:"mcp_server_cost_info,omitempty"` +} + +// MCPServerRequest represents a request to create or update an MCP server. +type MCPServerRequest struct { + ServerID string `json:"server_id,omitempty"` + ServerName string `json:"server_name"` + Alias string `json:"alias,omitempty"` + Description string `json:"description,omitempty"` + Transport string `json:"transport"` + SpecVersion string `json:"spec_version,omitempty"` + AuthType string `json:"auth_type,omitempty"` + URL string `json:"url"` + MCPInfo *MCPInfo `json:"mcp_info,omitempty"` + MCPAccessGroups []string `json:"mcp_access_groups,omitempty"` + Command string `json:"command,omitempty"` + Args []string `json:"args,omitempty"` + Env map[string]string `json:"env,omitempty"` +} + +// MCPServerResponse represents a response from the API containing MCP server information. +type MCPServerResponse struct { + ServerID string `json:"server_id"` + ServerName string `json:"server_name"` + Alias string `json:"alias,omitempty"` + Description string `json:"description,omitempty"` + URL string `json:"url"` + Transport string `json:"transport"` + SpecVersion string `json:"spec_version,omitempty"` + AuthType string `json:"auth_type,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + CreatedBy string `json:"created_by,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + UpdatedBy string `json:"updated_by,omitempty"` + Teams []map[string]string `json:"teams,omitempty"` + MCPAccessGroups []string `json:"mcp_access_groups,omitempty"` + MCPInfo *MCPInfo `json:"mcp_info,omitempty"` + Status string `json:"status,omitempty"` + LastHealthCheck string `json:"last_health_check,omitempty"` + HealthCheckError string `json:"health_check_error,omitempty"` + Command string `json:"command,omitempty"` + Args []string `json:"args,omitempty"` + Env map[string]string `json:"env,omitempty"` +} + +// CredentialRequest represents a request to create or update a credential. +type CredentialRequest struct { + CredentialName string `json:"credential_name"` + CredentialInfo map[string]interface{} `json:"credential_info,omitempty"` + CredentialValues map[string]interface{} `json:"credential_values,omitempty"` + ModelID string `json:"model_id,omitempty"` +} + +// CredentialResponse represents a response from the API containing credential information. +type CredentialResponse struct { + CredentialName string `json:"credential_name"` + CredentialInfo map[string]interface{} `json:"credential_info,omitempty"` + CredentialValues map[string]interface{} `json:"credential_values,omitempty"` +} + +// VectorStoreRequest represents a request to create or update a vector store. +type VectorStoreRequest struct { + VectorStoreID string `json:"vector_store_id,omitempty"` + CustomLLMProvider string `json:"custom_llm_provider"` + VectorStoreName string `json:"vector_store_name"` + VectorStoreDescription string `json:"vector_store_description,omitempty"` + VectorStoreMetadata map[string]interface{} `json:"vector_store_metadata,omitempty"` + LiteLLMCredentialName string `json:"litellm_credential_name,omitempty"` + LiteLLMParams map[string]interface{} `json:"litellm_params,omitempty"` +} + +// VectorStoreResponse represents a response from the API containing vector store information. +type VectorStoreResponse struct { + VectorStoreID string `json:"vector_store_id"` + CustomLLMProvider string `json:"custom_llm_provider"` + VectorStoreName string `json:"vector_store_name"` + VectorStoreDescription string `json:"vector_store_description,omitempty"` + VectorStoreMetadata map[string]interface{} `json:"vector_store_metadata,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + LiteLLMCredentialName string `json:"litellm_credential_name,omitempty"` + LiteLLMParams map[string]interface{} `json:"litellm_params,omitempty"` +} + +// VectorStoreListResponse represents a response from the API containing a list of vector stores. +type VectorStoreListResponse struct { + Object string `json:"object"` + Data []VectorStoreResponse `json:"data"` + TotalCount int `json:"total_count"` + CurrentPage int `json:"current_page"` + TotalPages int `json:"total_pages"` +} + +// VectorStoreDeleteRequest represents a request to delete a vector store. +type VectorStoreDeleteRequest struct { + VectorStoreID string `json:"vector_store_id"` +} + +// VectorStoreInfoRequest represents a request to get vector store information. +type VectorStoreInfoRequest struct { + VectorStoreID string `json:"vector_store_id"` +} diff --git a/terraform/provider/litellm/utils.go b/terraform/provider/litellm/utils.go new file mode 100644 index 00000000000..01d8045300c --- /dev/null +++ b/terraform/provider/litellm/utils.go @@ -0,0 +1,279 @@ +package litellm + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" +) + +func isModelNotFoundError(errResp ErrorResponse) bool { + if msg, ok := errResp.Error.Message.(string); ok { + if strings.Contains(msg, "model not found") { + return true + } + } + + if msgMap, ok := errResp.Error.Message.(map[string]interface{}); ok { + if errStr, ok := msgMap["error"].(string); ok { + if strings.Contains(errStr, "Model with id=") && strings.Contains(errStr, "not found in db") { + return true + } + } + } + + // Check Detail.Error field for LiteLLM proxy error format + if errResp.Detail.Error != "" { + if strings.Contains(errResp.Detail.Error, "not found on litellm proxy") { + return true + } + } + + return false +} + +func handleAPIResponse(resp *http.Response, reqBody interface{}, client *Client) (*ModelResponse, error) { + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %v", err) + } + + if resp.StatusCode != http.StatusOK { + var errResp ErrorResponse + if err := json.Unmarshal(bodyBytes, &errResp); err == nil { + if isModelNotFoundError(errResp) { + return nil, fmt.Errorf("model_not_found") + } + } + reqBodyBytes, _ := json.Marshal(reqBody) + return nil, fmt.Errorf("API request failed: Status: %s, Response: %s, Request: %s", + resp.Status, client.redactSensitiveData(string(bodyBytes)), client.redactSensitiveData(string(reqBodyBytes))) + } + + var modelResp ModelResponse + if err := json.Unmarshal(bodyBytes, &modelResp); err != nil { + return nil, fmt.Errorf("failed to parse response: %v", err) + } + + return &modelResp, nil +} + +// MakeRequest is a helper function to make HTTP requests +func MakeRequest(client *Client, method, endpoint string, body interface{}) (*http.Response, error) { + var req *http.Request + var err error + + if body != nil { + jsonData, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("failed to marshal request body: %w", err) + } + req, err = http.NewRequest(method, fmt.Sprintf("%s%s", client.APIBase, endpoint), bytes.NewBuffer(jsonData)) + } else { + req, err = http.NewRequest(method, fmt.Sprintf("%s%s", client.APIBase, endpoint), nil) + } + + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("x-api-key", client.APIKey) + + return client.httpClient.Do(req) +} + +// Helper functions to handle potential nil values from the API response +func GetStringValue(apiValue, defaultValue string) string { + if apiValue != "" { + return apiValue + } + return defaultValue +} + +func GetIntValue(apiValue, defaultValue int) int { + if apiValue != 0 { + return apiValue + } + return defaultValue +} + +func GetFloatValue(apiValue, defaultValue float64) float64 { + if apiValue != 0 { + return apiValue + } + return defaultValue +} + +func GetBoolValue(apiValue, defaultValue bool) bool { + return apiValue +} + +// handleMCPAPIResponse handles API responses specifically for MCP server operations +func handleMCPAPIResponse(resp *http.Response, result interface{}, client *Client) error { + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %v", err) + } + + if resp.StatusCode != http.StatusOK { + var errResp ErrorResponse + if err := json.Unmarshal(bodyBytes, &errResp); err == nil { + if isMCPServerNotFoundError(errResp) { + return fmt.Errorf("mcp_server_not_found") + } + } + return fmt.Errorf("API request failed: Status: %s, Response: %s", + resp.Status, client.redactSensitiveData(string(bodyBytes))) + } + + if err := json.Unmarshal(bodyBytes, result); err != nil { + return fmt.Errorf("failed to parse response: %v", err) + } + + return nil +} + +// isMCPServerNotFoundError checks if the error response indicates an MCP server not found +func isMCPServerNotFoundError(errResp ErrorResponse) bool { + if msg, ok := errResp.Error.Message.(string); ok { + if strings.Contains(msg, "mcp server not found") || strings.Contains(msg, "server not found") { + return true + } + } + + if msgMap, ok := errResp.Error.Message.(map[string]interface{}); ok { + if errStr, ok := msgMap["error"].(string); ok { + if strings.Contains(errStr, "MCP server with id=") && strings.Contains(errStr, "not found") { + return true + } + } + } + + // Check Detail.Error field for LiteLLM proxy error format + if errResp.Detail.Error != "" { + if strings.Contains(errResp.Detail.Error, "not found") { + return true + } + } + + return false +} + +// isCredentialNotFoundError checks if the error response indicates a credential not found +func isCredentialNotFoundError(errResp ErrorResponse) bool { + if msg, ok := errResp.Error.Message.(string); ok { + if strings.Contains(msg, "credential not found") { + return true + } + } + + if msgMap, ok := errResp.Error.Message.(map[string]interface{}); ok { + if errStr, ok := msgMap["error"].(string); ok { + if strings.Contains(errStr, "Credential with name=") && strings.Contains(errStr, "not found") { + return true + } + } + } + + // Check Detail.Error field for LiteLLM proxy error format + if errResp.Detail.Error != "" { + if strings.Contains(errResp.Detail.Error, "credential not found") { + return true + } + } + + return false +} + +// handleCredentialAPIResponse handles API responses specifically for credential operations +func handleCredentialAPIResponse(resp *http.Response, result interface{}, client *Client) error { + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("credential_not_found") + } + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { + var errResp ErrorResponse + if err := json.Unmarshal(bodyBytes, &errResp); err == nil { + if isCredentialNotFoundError(errResp) { + return fmt.Errorf("credential_not_found") + } + } + return fmt.Errorf("API request failed: Status: %s, Response: %s", + resp.Status, client.redactSensitiveData(string(bodyBytes))) + } + + // For credential operations, we might get a simple string response or a credential object + if result != nil { + if err := json.Unmarshal(bodyBytes, result); err != nil { + // If parsing fails, it might be a simple string response which is fine for create/update/delete + return nil + } + } + + return nil +} + +// isVectorStoreNotFoundError checks if the error response indicates a vector store not found +func isVectorStoreNotFoundError(errResp ErrorResponse) bool { + if msg, ok := errResp.Error.Message.(string); ok { + if strings.Contains(msg, "vector store not found") { + return true + } + } + + if msgMap, ok := errResp.Error.Message.(map[string]interface{}); ok { + if errStr, ok := msgMap["error"].(string); ok { + if strings.Contains(errStr, "Vector store with id=") && strings.Contains(errStr, "not found") { + return true + } + } + } + + // Check Detail.Error field for LiteLLM proxy error format + if errResp.Detail.Error != "" { + if strings.Contains(errResp.Detail.Error, "vector store not found") { + return true + } + } + + return false +} + +// handleVectorStoreAPIResponse handles API responses specifically for vector store operations +func handleVectorStoreAPIResponse(resp *http.Response, result interface{}, client *Client) error { + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("vector_store_not_found") + } + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { + var errResp ErrorResponse + if err := json.Unmarshal(bodyBytes, &errResp); err == nil { + if isVectorStoreNotFoundError(errResp) { + return fmt.Errorf("vector_store_not_found") + } + } + return fmt.Errorf("API request failed: Status: %s, Response: %s", + resp.Status, client.redactSensitiveData(string(bodyBytes))) + } + + if result != nil { + if err := json.Unmarshal(bodyBytes, result); err != nil { + return fmt.Errorf("failed to parse response: %v", err) + } + } + + return nil +} diff --git a/terraform/provider/main.go b/terraform/provider/main.go new file mode 100644 index 00000000000..abe83718899 --- /dev/null +++ b/terraform/provider/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/BerriAI/terraform-provider-litellm/litellm" + "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" +) + +// main is the entry point for the plugin. It serves the provider +// using the Terraform plugin SDK. +func main() { + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: litellm.Provider, + }) +} diff --git a/terraform/provider/terraform-registry-manifest.json b/terraform/provider/terraform-registry-manifest.json new file mode 100644 index 00000000000..295001a07f7 --- /dev/null +++ b/terraform/provider/terraform-registry-manifest.json @@ -0,0 +1,6 @@ +{ + "version": 1, + "metadata": { + "protocol_versions": ["6.0"] + } +} diff --git a/terraform/provider/tools/dump_openapi.py b/terraform/provider/tools/dump_openapi.py new file mode 100644 index 00000000000..b4f2dceeb09 --- /dev/null +++ b/terraform/provider/tools/dump_openapi.py @@ -0,0 +1,23 @@ +"""Dump the LiteLLM proxy's OpenAPI schema to the path given as the only argument. + +Run from the litellm repo root with the proxy dependencies installed: + + python terraform/provider/tools/dump_openapi.py openapi.json +""" + +import json +import sys + +from litellm.proxy.proxy_server import app + + +def main(out_path: str) -> None: + with open(out_path, "w") as f: + json.dump(app.openapi(), f) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("usage: python terraform/provider/tools/dump_openapi.py ", file=sys.stderr) + sys.exit(2) + main(sys.argv[1]) diff --git a/terraform/provider/tools/endpointaudit/main.go b/terraform/provider/tools/endpointaudit/main.go new file mode 100644 index 00000000000..ebc011ee910 --- /dev/null +++ b/terraform/provider/tools/endpointaudit/main.go @@ -0,0 +1,345 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "regexp" + "sort" + "strconv" + "strings" +) + +type endpointCall struct { + Method string + Path string + Pos string +} + +type extraction struct { + Calls []endpointCall + Unresolved []string +} + +var formatVerbPattern = regexp.MustCompile(`%[sdv]`) + +func normalizePath(raw string) string { + withoutQuery := strings.SplitN(raw, "?", 2)[0] + return formatVerbPattern.ReplaceAllString(withoutQuery, "{param}") +} + +func stringLit(expr ast.Expr) (string, bool) { + lit, ok := expr.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return "", false + } + value, err := strconv.Unquote(lit.Value) + if err != nil { + return "", false + } + return value, true +} + +func packageConsts(files []*ast.File) map[string]string { + consts := make(map[string]string) + for _, file := range files { + for _, decl := range file.Decls { + genDecl, ok := decl.(*ast.GenDecl) + if !ok || (genDecl.Tok != token.CONST && genDecl.Tok != token.VAR) { + continue + } + for _, spec := range genDecl.Specs { + valueSpec, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + for i, name := range valueSpec.Names { + if i >= len(valueSpec.Values) { + continue + } + if value, ok := stringLit(valueSpec.Values[i]); ok { + consts[name.Name] = value + } + } + } + } + } + return consts +} + +func isSprintf(call *ast.CallExpr) bool { + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok || sel.Sel.Name != "Sprintf" { + return false + } + pkg, ok := sel.X.(*ast.Ident) + return ok && pkg.Name == "fmt" +} + +func resolveExpr(expr ast.Expr, fn *ast.FuncDecl, consts map[string]string) []string { + switch node := expr.(type) { + case *ast.BasicLit: + if value, ok := stringLit(node); ok { + return []string{value} + } + case *ast.Ident: + if value, ok := consts[node.Name]; ok { + return []string{value} + } + return resolveLocalIdent(node, fn, consts) + case *ast.CallExpr: + if isSprintf(node) && len(node.Args) > 0 { + return resolveSprintf(node, fn, consts) + } + } + return nil +} + +func resolveSprintf(call *ast.CallExpr, fn *ast.FuncDecl, consts map[string]string) []string { + formats := resolveExpr(call.Args[0], fn, consts) + results := formats + for _, arg := range call.Args[1:] { + argValues := resolveExpr(arg, fn, consts) + substituted := make([]string, 0, len(results)) + for _, format := range results { + verb := formatVerbPattern.FindStringIndex(format) + if verb == nil { + substituted = append(substituted, format) + continue + } + if len(argValues) == 0 { + substituted = append(substituted, format[:verb[0]]+"\x00param\x00"+format[verb[1]:]) + continue + } + for _, argValue := range argValues { + substituted = append(substituted, format[:verb[0]]+argValue+format[verb[1]:]) + } + } + results = substituted + } + restored := make([]string, 0, len(results)) + for _, result := range results { + restored = append(restored, strings.ReplaceAll(result, "\x00param\x00", "%s")) + } + return restored +} + +func resolveLocalIdent(ident *ast.Ident, fn *ast.FuncDecl, consts map[string]string) []string { + if fn == nil { + return nil + } + var values []string + ast.Inspect(fn.Body, func(node ast.Node) bool { + assign, ok := node.(*ast.AssignStmt) + if !ok { + return true + } + for i, lhs := range assign.Lhs { + lhsIdent, ok := lhs.(*ast.Ident) + if !ok || lhsIdent.Name != ident.Name || i >= len(assign.Rhs) { + continue + } + values = append(values, resolveExpr(assign.Rhs[i], fn, consts)...) + } + return true + }) + return values +} + +func requestCallMethodAndPath(call *ast.CallExpr) (methodArg ast.Expr, pathArg ast.Expr, matched bool) { + switch fun := call.Fun.(type) { + case *ast.SelectorExpr: + if fun.Sel.Name == "sendRequest" && len(call.Args) >= 2 { + return call.Args[0], call.Args[1], true + } + case *ast.Ident: + if fun.Name == "MakeRequest" && len(call.Args) >= 3 { + return call.Args[1], call.Args[2], true + } + } + return nil, nil, false +} + +func isRawHTTPRequest(call *ast.CallExpr) bool { + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok || (sel.Sel.Name != "NewRequest" && sel.Sel.Name != "NewRequestWithContext") { + return false + } + pkg, ok := sel.X.(*ast.Ident) + return ok && pkg.Name == "http" +} + +func extractFromFiles(fset *token.FileSet, files []*ast.File, helperFiles map[string]bool) extraction { + consts := packageConsts(files) + var result extraction + for _, file := range files { + fileName := fset.Position(file.Pos()).Filename + for _, decl := range file.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok || fn.Body == nil { + continue + } + ast.Inspect(fn.Body, func(node ast.Node) bool { + call, ok := node.(*ast.CallExpr) + if !ok { + return true + } + pos := fset.Position(call.Pos()).String() + if isRawHTTPRequest(call) && !helperFiles[fileName] { + result.Unresolved = append(result.Unresolved, + fmt.Sprintf("%s: raw http.NewRequest outside the request helpers; route it through Client.sendRequest or MakeRequest", pos)) + return true + } + methodArg, pathArg, matched := requestCallMethodAndPath(call) + if !matched { + return true + } + methods := resolveExpr(methodArg, fn, consts) + paths := resolveExpr(pathArg, fn, consts) + if len(methods) == 0 || len(paths) == 0 { + result.Unresolved = append(result.Unresolved, + fmt.Sprintf("%s: cannot statically resolve method or path; use a string literal, package const, or fmt.Sprintf with a literal format", pos)) + return true + } + for _, method := range methods { + for _, path := range paths { + result.Calls = append(result.Calls, endpointCall{Method: method, Path: normalizePath(path), Pos: pos}) + } + } + return true + }) + } + } + return result +} + +func extractProviderCalls(providerDir string) (extraction, error) { + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, providerDir, func(info os.FileInfo) bool { + return !strings.HasSuffix(info.Name(), "_test.go") + }, 0) + if err != nil { + return extraction{}, err + } + var files []*ast.File + helperFiles := make(map[string]bool) + for _, pkg := range pkgs { + fileNames := make([]string, 0, len(pkg.Files)) + for name := range pkg.Files { + fileNames = append(fileNames, name) + } + sort.Strings(fileNames) + for _, name := range fileNames { + files = append(files, pkg.Files[name]) + base := name[strings.LastIndex(name, "/")+1:] + if base == "client.go" || base == "utils.go" { + helperFiles[name] = true + } + } + } + return extractFromFiles(fset, files, helperFiles), nil +} + +func loadSpecPaths(specPath string) (map[string]map[string]json.RawMessage, error) { + data, err := os.ReadFile(specPath) + if err != nil { + return nil, err + } + var spec struct { + Paths map[string]map[string]json.RawMessage `json:"paths"` + } + if err := json.Unmarshal(data, &spec); err != nil { + return nil, err + } + if len(spec.Paths) == 0 { + return nil, fmt.Errorf("spec %s contains no paths", specPath) + } + return spec.Paths, nil +} + +func segmentsMatch(providerSegment, specSegment string) bool { + if providerSegment == "{param}" { + return strings.HasPrefix(specSegment, "{") && strings.HasSuffix(specSegment, "}") + } + return providerSegment == specSegment +} + +func pathMatches(providerPath, specPath string) bool { + providerSegments := strings.Split(strings.Trim(providerPath, "/"), "/") + specSegments := strings.Split(strings.Trim(specPath, "/"), "/") + if len(providerSegments) != len(specSegments) { + return false + } + for i := range providerSegments { + if !segmentsMatch(providerSegments[i], specSegments[i]) { + return false + } + } + return true +} + +func auditCalls(calls []endpointCall, specPaths map[string]map[string]json.RawMessage) []string { + var violations []string + for _, call := range calls { + pathFound := false + methodFound := false + for specPath, operations := range specPaths { + if !pathMatches(call.Path, specPath) { + continue + } + pathFound = true + if _, ok := operations[strings.ToLower(call.Method)]; ok { + methodFound = true + break + } + } + if !pathFound { + violations = append(violations, fmt.Sprintf("%s: %s %s is not served by the proxy", call.Pos, call.Method, call.Path)) + } else if !methodFound { + violations = append(violations, fmt.Sprintf("%s: %s %s: path exists but method not allowed", call.Pos, call.Method, call.Path)) + } + } + return violations +} + +func run(providerDir, specPath string) error { + extracted, err := extractProviderCalls(providerDir) + if err != nil { + return err + } + if len(extracted.Unresolved) > 0 { + return fmt.Errorf("unresolved call sites:\n %s", strings.Join(extracted.Unresolved, "\n ")) + } + if len(extracted.Calls) == 0 { + return fmt.Errorf("extracted zero request call sites from %s; extractor or provider layout changed", providerDir) + } + specPaths, err := loadSpecPaths(specPath) + if err != nil { + return err + } + violations := auditCalls(extracted.Calls, specPaths) + if len(violations) > 0 { + sort.Strings(violations) + return fmt.Errorf("provider/proxy endpoint drift:\n %s", strings.Join(violations, "\n ")) + } + fmt.Printf("OK: %d request call sites verified against %d proxy OpenAPI paths\n", len(extracted.Calls), len(specPaths)) + return nil +} + +func main() { + providerDir := flag.String("provider-dir", "./litellm", "directory containing the provider Go source") + specPath := flag.String("spec", "", "path to the proxy OpenAPI schema JSON") + flag.Parse() + if *specPath == "" { + fmt.Fprintln(os.Stderr, "error: -spec is required") + os.Exit(2) + } + if err := run(*providerDir, *specPath); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} diff --git a/terraform/provider/tools/endpointaudit/main_test.go b/terraform/provider/tools/endpointaudit/main_test.go new file mode 100644 index 00000000000..d3d5e7dec9c --- /dev/null +++ b/terraform/provider/tools/endpointaudit/main_test.go @@ -0,0 +1,187 @@ +package main + +import ( + "encoding/json" + "os" + "path/filepath" + "sort" + "strings" + "testing" +) + +func writeFixture(t *testing.T, dir, name, body string) { + t.Helper() + if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil { + t.Fatal(err) + } +} + +func extractFixture(t *testing.T, files map[string]string) extraction { + t.Helper() + dir := t.TempDir() + for name, body := range files { + writeFixture(t, dir, name, body) + } + result, err := extractProviderCalls(dir) + if err != nil { + t.Fatal(err) + } + return result +} + +func callSet(calls []endpointCall) []string { + set := make(map[string]bool) + for _, call := range calls { + set[call.Method+" "+call.Path] = true + } + keys := make([]string, 0, len(set)) + for key := range set { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + +func TestExtractResolvesAllCallShapes(t *testing.T) { + result := extractFixture(t, map[string]string{ + "consts.go": `package p + +const ( + endpointModelNew = "/model/new" + endpointModelUpdate = "/model/update" + endpointMCPRead = "/v1/mcp/server" +) +`, + "calls.go": `package p + +import "fmt" + +func (c *Client) a() { + c.sendRequest("POST", "/team/new", nil) + c.sendRequest("GET", fmt.Sprintf("/team/info?team_id=%s", "x"), nil) +} + +func b(client *Client, isUpdate bool, serverID string) { + MakeRequest(client, "POST", "/credentials", nil) + endpoint := endpointModelNew + if isUpdate { + endpoint = endpointModelUpdate + } + MakeRequest(client, "POST", endpoint, nil) + readEndpoint := fmt.Sprintf("%s/%s", endpointMCPRead, serverID) + MakeRequest(client, "GET", readEndpoint, nil) +} +`, + }) + if len(result.Unresolved) != 0 { + t.Fatalf("unexpected unresolved: %v", result.Unresolved) + } + got := callSet(result.Calls) + want := []string{ + "GET /team/info", + "GET /v1/mcp/server/{param}", + "POST /credentials", + "POST /model/new", + "POST /model/update", + "POST /team/new", + } + if strings.Join(got, ",") != strings.Join(want, ",") { + t.Fatalf("got %v, want %v", got, want) + } +} + +func TestExtractFailsClosedOnDynamicPath(t *testing.T) { + result := extractFixture(t, map[string]string{ + "calls.go": `package p + +func a(c *Client, path string) { + c.sendRequest("GET", path, nil) +} +`, + }) + if len(result.Unresolved) != 1 { + t.Fatalf("want 1 unresolved call site, got %v", result.Unresolved) + } +} + +func TestExtractFlagsRawHTTPRequestOutsideHelpers(t *testing.T) { + result := extractFixture(t, map[string]string{ + "rogue.go": `package p + +import "net/http" + +func a() { + http.NewRequest("GET", "http://example.com/model/new", nil) +} +`, + }) + if len(result.Unresolved) != 1 || !strings.Contains(result.Unresolved[0], "raw http.NewRequest") { + t.Fatalf("want raw request violation, got %v", result.Unresolved) + } +} + +func TestExtractAllowsRawHTTPRequestInHelpers(t *testing.T) { + result := extractFixture(t, map[string]string{ + "utils.go": `package p + +import "net/http" + +func MakeRequest(client *Client, method, endpoint string, body interface{}) { + http.NewRequest(method, endpoint, nil) +} +`, + }) + if len(result.Unresolved) != 0 { + t.Fatalf("unexpected unresolved: %v", result.Unresolved) + } +} + +func specFixture(t *testing.T) map[string]map[string]json.RawMessage { + t.Helper() + raw := `{ + "paths": { + "/team/new": {"post": {}}, + "/organization/update": {"patch": {}}, + "/credentials/{credential_name}": {"get": {}, "delete": {}} + } + }` + dir := t.TempDir() + specPath := filepath.Join(dir, "spec.json") + if err := os.WriteFile(specPath, []byte(raw), 0o644); err != nil { + t.Fatal(err) + } + paths, err := loadSpecPaths(specPath) + if err != nil { + t.Fatal(err) + } + return paths +} + +func TestAuditDetectsMissingPathAndWrongMethod(t *testing.T) { + spec := specFixture(t) + violations := auditCalls([]endpointCall{ + {Method: "POST", Path: "/team/new", Pos: "a.go:1"}, + {Method: "GET", Path: "/credentials/{param}", Pos: "a.go:2"}, + {Method: "POST", Path: "/organization/update", Pos: "a.go:3"}, + {Method: "POST", Path: "/gone/away", Pos: "a.go:4"}, + }, spec) + if len(violations) != 2 { + t.Fatalf("want 2 violations, got %v", violations) + } + joined := strings.Join(violations, "\n") + if !strings.Contains(joined, "POST /organization/update: path exists but method not allowed") { + t.Fatalf("missing method violation: %v", violations) + } + if !strings.Contains(joined, "POST /gone/away is not served by the proxy") { + t.Fatalf("missing path violation: %v", violations) + } +} + +func TestNormalizePathStripsQueryAndVerbs(t *testing.T) { + if got := normalizePath("/key/info?key=%s"); got != "/key/info" { + t.Fatalf("got %q", got) + } + if got := normalizePath("/credentials/%s"); got != "/credentials/{param}" { + t.Fatalf("got %q", got) + } +} diff --git a/tests/_ws_vcr.py b/tests/_ws_vcr.py new file mode 100644 index 00000000000..1f8843a23bd --- /dev/null +++ b/tests/_ws_vcr.py @@ -0,0 +1,546 @@ +"""Record and replay realtime WebSocket traffic in the shared VCR Redis store. + +The HTTP VCR layer (``tests/_vcr_redis_persister.py`` / +``tests/_vcr_conftest_common.py``) only intercepts httpx/aiohttp, so the +realtime suite always reached the live provider. This module intercepts at the +``websockets.connect`` boundary instead and caches whole WebSocket sessions +under a distinct ``litellm:vcr:wscassette:`` key, reusing the same Redis client, +24h TTL, save-on-pass, and best-effort degradation semantics. + +Record mode logs every frame in order with its direction, a text/binary flag, +and, for each server frame, the number of client frames seen before it. That +count is the causal gate for replay: a recorded server frame is only released +once the client has sent at least that many frames, so the deterministic replay +reproduces the same interleaving without a live connection. Client frames are +matched against the recording with volatile fields (ids, timestamps) normalized +away; a structurally different client frame is contract drift and raises loudly +rather than hanging, and every replay wait is bounded by a timeout. +""" + +from __future__ import annotations + +import asyncio +import base64 +import json +import logging +import os +import re +import warnings +from typing import AsyncIterator, Callable, Literal, Optional, Protocol, Union + +from pydantic import BaseModel, ConfigDict, ValidationError +from websockets.exceptions import ConnectionClosedOK + +from tests._vcr_redis_persister import ( + CASSETTE_TTL_SECONDS, + VCRCassetteCacheWarning, + _build_default_client, + _record_cache_failure, +) + +WS_REDIS_KEY_PREFIX = "litellm:vcr:wscassette:" +WS_MAX_SESSIONS_PER_CASSETTE = 20 +WS_MAX_FRAMES_PER_SESSION = 2000 +WS_REPLAY_TIMEOUT_ENV = "LITELLM_WS_VCR_REPLAY_TIMEOUT" +WS_DEFAULT_REPLAY_TIMEOUT_SECONDS = 15.0 +WS_CASSETTE_SCHEMA_VERSION = 1 + +_log = logging.getLogger(__name__) + +Message = Union[str, bytes] +Direction = Literal["client_to_server", "server_to_client"] +Opcode = Literal["text", "binary"] + + +class WsConnectionLike(Protocol): + async def recv(self, decode: Optional[bool] = None) -> Message: ... + + async def send(self, message: Message, *args: object, **kwargs: object) -> None: ... + + async def close(self, *args: object, **kwargs: object) -> None: ... + + def __aiter__(self) -> AsyncIterator[Message]: ... + + +class WsConnectContextLike(Protocol): + async def __aenter__(self) -> WsConnectionLike: ... + + async def __aexit__(self, *exc_info: object) -> Optional[bool]: ... + + +class RedisLike(Protocol): + def get(self, key: str) -> Optional[bytes]: ... + + def set(self, key: str, value: bytes, ex: int) -> object: ... + + +class WsFrame(BaseModel): + model_config = ConfigDict(frozen=True) + + direction: Direction + opcode: Opcode + text: Optional[str] = None + binary_b64: Optional[str] = None + client_frames_before: Optional[int] = None + + +class WsSession(BaseModel): + model_config = ConfigDict(frozen=True) + + frames: tuple[WsFrame, ...] + + +class WsCassette(BaseModel): + model_config = ConfigDict(frozen=True) + + schema_version: int = WS_CASSETTE_SCHEMA_VERSION + sessions: tuple[WsSession, ...] + + +class WsVcrReplayError(Exception): ... + + +class WsVcrContractDrift(WsVcrReplayError): ... + + +class WsVcrReplayTimeout(WsVcrReplayError): ... + + +_UUID_RE = re.compile(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") +_OPENAI_ID_RE = re.compile( + r"\b(?:evt|event|item|msg|resp|response|sess|session|call|fc|rs|conv|ce|audio)_[A-Za-z0-9]{6,}" +) +_ISO_TS_RE = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?") +_EPOCH_RE = re.compile(r"(? str: + scrubbed = _BEARER_RE.sub("Bearer ", text) + scrubbed = _OPENAI_KEY_RE.sub("", scrubbed) + scrubbed = _XAI_KEY_RE.sub("", scrubbed) + return scrubbed + + +def _normalize_json_for_match(obj: object) -> object: + if isinstance(obj, dict): + return { + str(key): ("" if key in _VOLATILE_KEYS else _normalize_json_for_match(value)) + for key, value in sorted(obj.items(), key=lambda kv: str(kv[0])) + } + if isinstance(obj, list): + return [_normalize_json_for_match(item) for item in obj] + if isinstance(obj, str): + return _normalize_scalar_string(obj) + return obj + + +def _normalize_scalar_string(text: str) -> str: + normalized = _UUID_RE.sub("", text) + normalized = _OPENAI_ID_RE.sub("", normalized) + normalized = _ISO_TS_RE.sub("", normalized) + normalized = _EPOCH_RE.sub("", normalized) + return normalized + + +def normalize_text_for_match(text: str) -> str: + try: + parsed = json.loads(text) + except (ValueError, TypeError): + return _normalize_scalar_string(text) + return json.dumps(_normalize_json_for_match(parsed), sort_keys=True, separators=(",", ":")) + + +def text_frames_match(recorded: str, incoming: str) -> bool: + return normalize_text_for_match(recorded) == normalize_text_for_match(incoming) + + +def _frame_payload(message: Message) -> tuple[Opcode, Optional[str], Optional[str]]: + if isinstance(message, str): + return "text", scrub_secrets(message), None + try: + decoded = message.decode("utf-8") + except UnicodeDecodeError: + return "binary", None, base64.b64encode(message).decode("ascii") + return "text", scrub_secrets(decoded), None + + +def ws_redis_key_for(nodeid: str) -> str: + rel = nodeid.replace("::", "/").replace("\\", "/").lstrip("./") + return f"{WS_REDIS_KEY_PREFIX}{rel}" + + +def replay_timeout_seconds() -> float: + raw = os.environ.get(WS_REPLAY_TIMEOUT_ENV) + if not raw: + return WS_DEFAULT_REPLAY_TIMEOUT_SECONDS + try: + return float(raw) + except ValueError: + return WS_DEFAULT_REPLAY_TIMEOUT_SECONDS + + +class WsSessionRecorder: + def __init__(self) -> None: + self._frames: list[WsFrame] = [] + self._client_count = 0 + + def record_client_frame(self, message: Message) -> None: + opcode, text, binary_b64 = _frame_payload(message) + self._frames.append(WsFrame(direction="client_to_server", opcode=opcode, text=text, binary_b64=binary_b64)) + self._client_count += 1 + + def record_server_frame(self, message: Message) -> None: + opcode, text, binary_b64 = _frame_payload(message) + self._frames.append( + WsFrame( + direction="server_to_client", + opcode=opcode, + text=text, + binary_b64=binary_b64, + client_frames_before=self._client_count, + ) + ) + + def to_session(self) -> WsSession: + return WsSession(frames=tuple(self._frames)) + + +class RecordingConnection: + def __init__(self, real: WsConnectionLike, recorder: WsSessionRecorder) -> None: + self._real = real + self._recorder = recorder + + async def recv(self, decode: Optional[bool] = None) -> Message: + result = await self._real.recv(decode=decode) + self._recorder.record_server_frame(result) + return result + + async def send(self, message: Message, *args: object, **kwargs: object) -> None: + self._recorder.record_client_frame(message) + await self._real.send(message, *args, **kwargs) + + async def close(self, *args: object, **kwargs: object) -> None: + await self._real.close(*args, **kwargs) + + def __aiter__(self) -> AsyncIterator[Message]: + return self._iterate() + + async def _iterate(self) -> AsyncIterator[Message]: + async for message in self._real: + self._recorder.record_server_frame(message) + yield message + + +class ReplayConnection: + def __init__( + self, + session: WsSession, + timeout: float, + on_error: Callable[[WsVcrReplayError], None], + ) -> None: + self._server_frames = tuple(f for f in session.frames if f.direction == "server_to_client") + self._client_frames = tuple(f for f in session.frames if f.direction == "client_to_server") + self._timeout = timeout + self._on_error = on_error + self._server_cursor = 0 + self._client_cursor = 0 + self._client_sent = 0 + self._closed = False + self._progress = asyncio.Event() + + async def recv(self, decode: Optional[bool] = None) -> Message: + want_bytes = decode is False + while True: + if self._closed or self._server_cursor >= len(self._server_frames): + raise ConnectionClosedOK(None, None) + frame = self._server_frames[self._server_cursor] + needed = frame.client_frames_before or 0 + if self._client_sent >= needed: + self._server_cursor += 1 + return _materialize_frame(frame, want_bytes) + await self._await_client_progress(needed) + + async def _await_client_progress(self, needed: int) -> None: + waiter = self._progress + try: + await asyncio.wait_for(waiter.wait(), timeout=self._timeout) + except asyncio.TimeoutError: + error = WsVcrReplayTimeout( + f"WS-VCR replay stalled: server frame #{self._server_cursor} needs " + f"{needed} client frame(s) but only {self._client_sent} were sent within " + f"{self._timeout}s. The client stopped driving the recorded session." + ) + self._on_error(error) + raise error + + async def send(self, message: Message, *args: object, **kwargs: object) -> None: + if self._client_cursor >= len(self._client_frames): + error = WsVcrContractDrift( + "WS-VCR contract drift: client sent frame " + f"#{self._client_cursor + 1} but the recording has only " + f"{len(self._client_frames)} client frame(s). Extra frame: {_preview(message)}" + ) + self._on_error(error) + raise error + recorded = self._client_frames[self._client_cursor] + if not _client_frame_matches(recorded, message): + error = WsVcrContractDrift( + "WS-VCR contract drift on client frame " + f"#{self._client_cursor + 1}:\n recorded: {_preview_frame(recorded)}\n" + f" got: {_preview(message)}" + ) + self._on_error(error) + raise error + self._client_cursor += 1 + self._client_sent += 1 + self._signal_progress() + + async def close(self, *args: object, **kwargs: object) -> None: + self._closed = True + self._signal_progress() + + def _signal_progress(self) -> None: + previous = self._progress + self._progress = asyncio.Event() + previous.set() + + def __aiter__(self) -> AsyncIterator[Message]: + return self._iterate() + + async def _iterate(self) -> AsyncIterator[Message]: + while True: + try: + yield await self.recv() + except ConnectionClosedOK: + return + + +def _materialize_frame(frame: WsFrame, want_bytes: bool) -> Message: + if frame.opcode == "text": + text = frame.text or "" + return text.encode("utf-8") if want_bytes else text + return base64.b64decode(frame.binary_b64 or "") + + +def _client_frame_matches(recorded: WsFrame, message: Message) -> bool: + opcode, text, binary_b64 = _frame_payload(message) + if recorded.opcode != opcode: + return False + if opcode == "text": + return text_frames_match(recorded.text or "", text or "") + return recorded.binary_b64 == binary_b64 + + +def _preview(message: Message) -> str: + text = message if isinstance(message, str) else message.decode("utf-8", errors="replace") + return scrub_secrets(text)[:200] + + +def _preview_frame(frame: WsFrame) -> str: + if frame.opcode == "text": + return (frame.text or "")[:200] + return f"" + + +class _RecordingConnect: + def __init__( + self, + real_context: WsConnectContextLike, + recorder: WsSessionRecorder, + on_done: Callable[[WsSessionRecorder], None], + ) -> None: + self._real_context = real_context + self._recorder = recorder + self._on_done = on_done + + async def __aenter__(self) -> RecordingConnection: + real = await self._real_context.__aenter__() + return RecordingConnection(real, self._recorder) + + async def __aexit__(self, *exc_info: object) -> Optional[bool]: + try: + return await self._real_context.__aexit__(*exc_info) + finally: + self._on_done(self._recorder) + + +class _ReplayConnect: + def __init__( + self, + session: WsSession, + timeout: float, + on_error: Callable[[WsVcrReplayError], None], + ) -> None: + self._session = session + self._timeout = timeout + self._on_error = on_error + + async def __aenter__(self) -> ReplayConnection: + return ReplayConnection(self._session, self._timeout, self._on_error) + + async def __aexit__(self, *exc_info: object) -> bool: + return False + + +class WsVcrController: + def __init__( + self, + original_connect: Callable[..., WsConnectContextLike], + cassette: Optional[WsCassette], + timeout: float, + ) -> None: + self._original_connect = original_connect + self._cassette = cassette + self._timeout = timeout + self._replay_cursor = 0 + self._recorded_sessions: list[WsSession] = [] + self._errors: list[WsVcrReplayError] = [] + self._replayed = False + self._recorded = False + + def connect(self, *args: object, **kwargs: object) -> object: + if self._cassette is not None and self._replay_cursor < len(self._cassette.sessions): + session = self._cassette.sessions[self._replay_cursor] + self._replay_cursor += 1 + self._replayed = True + return _ReplayConnect(session, self._timeout, self._errors.append) + self._recorded = True + recorder = WsSessionRecorder() + return _RecordingConnect(self._original_connect(*args, **kwargs), recorder, self._finish_recorder) + + def _finish_recorder(self, recorder: WsSessionRecorder) -> None: + self._recorded_sessions.append(recorder.to_session()) + + @property + def errors(self) -> tuple[WsVcrReplayError, ...]: + return tuple(self._errors) + + @property + def replayed(self) -> bool: + return self._replayed + + @property + def recorded(self) -> bool: + return self._recorded + + def built_cassette(self) -> Optional[WsCassette]: + if not self._recorded_sessions: + return None + return WsCassette(sessions=tuple(self._recorded_sessions)) + + def verdict(self) -> str: + if self._replayed and not self._recorded: + return f"[WS-VCR HIT] sessions={self._replay_cursor} frames={self._played_frame_count()}" + if self._recorded: + cassette = self.built_cassette() + frames = _cassette_frame_count(cassette) if cassette is not None else 0 + return f"[WS-VCR MISS] recorded sessions={len(self._recorded_sessions)} frames={frames}" + return "[WS-VCR NOOP] (no websocket traffic)" + + def _played_frame_count(self) -> int: + if self._cassette is None: + return 0 + return sum(len(s.frames) for s in self._cassette.sessions[: self._replay_cursor]) + + +def _cassette_frame_count(cassette: WsCassette) -> int: + return sum(len(s.frames) for s in cassette.sessions) + + +def load_ws_cassette(client: RedisLike, key: str) -> Optional[WsCassette]: + from redis.exceptions import RedisError + + try: + data = client.get(key) + except RedisError as exc: + _record_cache_failure("load", exc) + message = f"WS-VCR redis load failed for {key}; treating as cache miss: {type(exc).__name__}: {exc}" + _log.warning(message) + warnings.warn(message, VCRCassetteCacheWarning, stacklevel=2) + return None + if data is None: + return None + try: + raw = data.decode("utf-8") if isinstance(data, (bytes, bytearray)) else data + return WsCassette.model_validate_json(raw) + except (ValidationError, ValueError, TypeError) as exc: + _record_cache_failure("load", exc) + message = ( + f"WS-VCR redis load failed for {key}; cached payload is corrupt, " + f"treating as cache miss: {type(exc).__name__}: {exc}" + ) + _log.warning(message) + warnings.warn(message, VCRCassetteCacheWarning, stacklevel=2) + return None + + +def save_ws_cassette( + client: RedisLike, + key: str, + cassette: WsCassette, + passed: bool, + ttl_seconds: int = CASSETTE_TTL_SECONDS, +) -> bool: + from redis.exceptions import RedisError + + if not passed: + _log.info("WS-VCR redis save skipped for %s; test did not pass - leaving any prior cassette intact", key) + return False + if len(cassette.sessions) > WS_MAX_SESSIONS_PER_CASSETTE: + _log.warning( + "WS-VCR redis save refused for %s; %d sessions (> WS_MAX_SESSIONS_PER_CASSETTE=%d)", + key, + len(cassette.sessions), + WS_MAX_SESSIONS_PER_CASSETTE, + ) + return False + if any(len(session.frames) > WS_MAX_FRAMES_PER_SESSION for session in cassette.sessions): + _log.warning( + "WS-VCR redis save refused for %s; a session exceeds WS_MAX_FRAMES_PER_SESSION=%d", + key, + WS_MAX_FRAMES_PER_SESSION, + ) + return False + payload = cassette.model_dump_json().encode("utf-8") + try: + client.set(key, payload, ex=ttl_seconds) + except RedisError as exc: + _record_cache_failure("save", exc) + message = f"WS-VCR redis save failed for {key}; cassette not persisted: {type(exc).__name__}: {exc}" + _log.warning(message) + warnings.warn(message, VCRCassetteCacheWarning, stacklevel=2) + return False + return True + + +def build_ws_cassette_client( + builder: Callable[[], RedisLike] = _build_default_client, +) -> Optional[RedisLike]: + try: + return builder() + except Exception as exc: + _record_cache_failure("load", exc) + message = ( + f"WS-VCR redis client unavailable; realtime tests fall back to live " + f"websocket traffic: {type(exc).__name__}: {exc}" + ) + _log.warning(message) + warnings.warn(message, VCRCassetteCacheWarning, stacklevel=2) + return None diff --git a/tests/batches_tests/test_batches_logging_unit_tests.py b/tests/batches_tests/test_batches_logging_unit_tests.py index a0c94693783..33a0a87dd92 100644 --- a/tests/batches_tests/test_batches_logging_unit_tests.py +++ b/tests/batches_tests/test_batches_logging_unit_tests.py @@ -255,6 +255,55 @@ async def test_batch_retrieve_cost_tracking_with_completed_batch_no_explicit_cos assert mock_batch.usage == expected_usage +@pytest.mark.asyncio +async def test_handle_completed_batch_computes_real_cost_from_output_file( + sample_file_content_dict, +): + """Integration: a completed batch's cost and usage are computed from its output + file via the real cost-calc chain (only the file download is stubbed). This is + the function the retrieve handler invokes on completion; a dropped output line, a + wrong token sum, or mispriced model fails this test. + """ + from litellm.batches.batch_utils import _handle_completed_batch + from litellm.types.utils import LiteLLMBatch + + batch = LiteLLMBatch( + id="batch-real-cost-123", + object="batch", + endpoint="/v1/chat/completions", + input_file_id="file-input-123", + completion_window="24h", + status="completed", + output_file_id="file-output-123", + created_at=1234567890, + ) + + with patch( + "litellm.batches.batch_utils._get_batch_output_file_content_as_dictionary", + new=AsyncMock(return_value=sample_file_content_dict), + ): + cost, usage, models = await _handle_completed_batch( + batch=batch, custom_llm_provider="openai" + ) + + pricing = litellm.model_cost["gpt-4o-mini-2024-07-18"] + expected_cost = ( + 42 * pricing["input_cost_per_token_batches"] + + 20 * pricing["output_cost_per_token_batches"] + ) + + assert cost == pytest.approx(expected_cost) + assert cost > 0 + assert ( + cost + < 42 * pricing["input_cost_per_token"] + 20 * pricing["output_cost_per_token"] + ) + assert usage.prompt_tokens == 42 + assert usage.completion_tokens == 20 + assert usage.total_tokens == 62 + assert models == ["gpt-4o-mini-2024-07-18", "gpt-4o-mini-2024-07-18"] + + @pytest.mark.asyncio async def test_batch_retrieve_cost_tracking_with_explicit_cost_data(): """ diff --git a/tests/batches_tests/test_fine_tuning_api.py b/tests/batches_tests/test_fine_tuning_api.py index c489685eaff..bd6672a52e9 100644 --- a/tests/batches_tests/test_fine_tuning_api.py +++ b/tests/batches_tests/test_fine_tuning_api.py @@ -13,13 +13,10 @@ import litellm litellm.num_retries = 0 import asyncio -import logging from typing import Optional -import openai from test_openai_batches_and_files import load_vertex_ai_credentials from litellm import create_fine_tuning_job -from litellm._logging import verbose_logger from litellm.llms.vertex_ai.fine_tuning.handler import ( FineTuningJobCreate, VertexFineTuningAPI, @@ -47,115 +44,6 @@ class TestCustomLogger(CustomLogger): self.standard_logging_object = kwargs["standard_logging_object"] -async def _acreate_fine_tuning_job_with_propagation_retry( - *, max_attempts: int = 12, initial_delay: float = 1.0, **kwargs -): - """ - Wrap litellm.acreate_fine_tuning_job and retry on the eventual-consistency - 400 OpenAI returns when a freshly-uploaded training file isn't yet visible - to the fine-tuning endpoint (`'file-... does not exist'`). - - Polling the files-retrieve endpoint or `FileObject.status` doesn't help — - OpenAI's `status` field is deprecated, and the retrieve and fine-tuning - endpoints don't share a consistency model. Retrying the operation itself - is the only reliable signal that propagation has finished. - - Total budget with defaults: ~70s across 12 attempts (exp backoff capped at - 8s). - """ - delay = initial_delay - last_error: Optional[openai.BadRequestError] = None - for _ in range(max_attempts): - try: - return await litellm.acreate_fine_tuning_job(**kwargs) - except openai.BadRequestError as e: - if "does not exist" not in str(e): - raise - last_error = e - await asyncio.sleep(delay) - delay = min(delay * 1.5, 8.0) - assert last_error is not None - raise last_error - - -@pytest.mark.asyncio -async def test_create_fine_tune_jobs_async(): - try: - custom_logger = TestCustomLogger() - litellm.callbacks = ["datadog", custom_logger] - verbose_logger.setLevel(logging.DEBUG) - file_name = "openai_batch_completions.jsonl" - _current_dir = os.path.dirname(os.path.abspath(__file__)) - file_path = os.path.join(_current_dir, file_name) - - file_obj = await litellm.acreate_file( - file=open(file_path, "rb"), - purpose="fine-tune", - custom_llm_provider="openai", - ) - print("Response from creating file=", file_obj) - - create_fine_tuning_response = ( - await _acreate_fine_tuning_job_with_propagation_retry( - model="gpt-4o-mini-2024-07-18", - training_file=file_obj.id, - ) - ) - - print( - "response from litellm.create_fine_tuning_job=", create_fine_tuning_response - ) - - assert create_fine_tuning_response.id is not None - assert create_fine_tuning_response.model == "gpt-4o-mini-2024-07-18" - - await asyncio.sleep(2) - _logged_standard_logging_object = custom_logger.standard_logging_object - assert _logged_standard_logging_object is not None - print( - "custom_logger.standard_logging_object=", - json.dumps(_logged_standard_logging_object, indent=4), - ) - assert _logged_standard_logging_object["model"] == "gpt-4o-mini-2024-07-18" - assert _logged_standard_logging_object["id"] == create_fine_tuning_response.id - - # list fine tuning jobs - print("listing ft jobs") - ft_jobs = await litellm.alist_fine_tuning_jobs(limit=2) - print("response from litellm.list_fine_tuning_jobs=", ft_jobs) - assert len(list(ft_jobs)) > 0 - - # retrieve fine tuning job - response = await litellm.aretrieve_fine_tuning_job( - fine_tuning_job_id=create_fine_tuning_response.id, - ) - print("response from litellm.retrieve_fine_tuning_job=", response) - - # delete file - - await litellm.afile_delete( - file_id=file_obj.id, - ) - - # cancel ft job - response = await litellm.acancel_fine_tuning_job( - fine_tuning_job_id=create_fine_tuning_response.id, - ) - - print("response from litellm.cancel_fine_tuning_job=", response) - - assert response.status == "cancelled" - assert response.id == create_fine_tuning_response.id - except openai.RateLimitError: - pass - except Exception as e: - if "Job has already completed" in str(e): - return - else: - pytest.fail(f"Error occurred: {e}") - pass - - @pytest.mark.asyncio() async def test_create_vertex_fine_tune_jobs_mocked(): # Define reusable variables for the test @@ -455,6 +343,9 @@ async def test_mock_openai_create_fine_tune_job(): from openai import AsyncOpenAI from openai.types.fine_tuning.fine_tuning_job import FineTuningJob, Hyperparameters + custom_logger = TestCustomLogger() + previous_callbacks = litellm.callbacks + litellm.callbacks = [custom_logger] client = AsyncOpenAI(api_key="fake-api-key") with patch.object(client.fine_tuning.jobs, "create") as mock_create: @@ -500,6 +391,19 @@ async def test_mock_openai_create_fine_tune_job(): == "ft:gpt-4o-mini-2024-07-18:org:custom_suffix:id" ) + try: + for _ in range(20): + if custom_logger.standard_logging_object is not None: + break + await asyncio.sleep(0.25) + logged = custom_logger.standard_logging_object + assert logged is not None + assert logged["model"] == "gpt-4o-mini-2024-07-18" + assert logged["id"] == response.id + assert logged["call_type"] == "acreate_fine_tuning_job" + finally: + litellm.callbacks = previous_callbacks + @pytest.mark.asyncio async def test_mock_openai_list_fine_tune_jobs(): diff --git a/tests/benchmarks/conftest.py b/tests/benchmarks/conftest.py new file mode 100644 index 00000000000..c9b31cfb7d7 --- /dev/null +++ b/tests/benchmarks/conftest.py @@ -0,0 +1,36 @@ +"""Shared setup keeping CodSpeed measurements hermetic. + +CodSpeed's callgrind instrumentation counts instructions from every thread while +a measurement window is open, and valgrind serializes all threads onto one +virtual CPU. Work deferred to litellm's shared logging executor would therefore +be attributed to whichever benchmark the valgrind scheduler resumes it under, +flipping results between runs. Running the executor inline keeps each +benchmark's cost self-contained and deterministic. +""" + +from collections.abc import Callable, Iterator +from concurrent.futures import Future +from typing import ParamSpec, TypeVar + +import pytest + +from litellm.litellm_core_utils.thread_pool_executor import executor + +P = ParamSpec("P") +R = TypeVar("R") + + +def _submit_inline(fn: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> Future[R]: + future: Future[R] = Future() + try: + future.set_result(fn(*args, **kwargs)) + except BaseException as exc: + future.set_exception(exc) + return future + + +@pytest.fixture(autouse=True, scope="session") +def inline_logging_executor() -> Iterator[None]: + executor.submit = _submit_inline + yield + del executor.submit diff --git a/tests/benchmarks/test_a2a_benchmarks.py b/tests/benchmarks/test_a2a_benchmarks.py new file mode 100644 index 00000000000..cf7726230b6 --- /dev/null +++ b/tests/benchmarks/test_a2a_benchmarks.py @@ -0,0 +1,76 @@ +""" +Performance benchmarks for the A2A (agent-to-agent) message-translation hot path. + +Both directions are covered: the client direction (litellm.completion talking to +an upstream A2A agent) converts OpenAI messages into a prompt and extracts text +from the A2A response, and the proxy server-ingress direction converts an inbound +A2A message into OpenAI messages before bridging to a completion. All are pure-CPU +per-request transforms. +""" + +import pytest + +from litellm.a2a_protocol.litellm_completion_bridge.transformation import ( + A2ACompletionBridgeTransformation, +) +from litellm.llms.a2a.common_utils import ( + convert_messages_to_prompt, + extract_text_from_a2a_response, +) + +MESSAGES = [ + {"role": "system", "content": "You are a helpful research assistant."}, + {"role": "user", "content": "What is the capital of France?"}, + {"role": "assistant", "content": "The capital of France is Paris."}, + {"role": "user", "content": "And what is its population?"}, +] + +MESSAGE_RESPONSE = { + "result": { + "kind": "message", + "parts": [ + {"kind": "text", "text": "The population of Paris is about 2.1 million."}, + {"kind": "text", "text": "The metro area has over 12 million people."}, + ], + } +} + +TASK_RESPONSE = { + "result": { + "kind": "task", + "artifacts": [{"parts": [{"kind": "text", "text": "Paris has a population of about 2.1 million."}]}], + } +} + +A2A_INBOUND_MESSAGE = { + "role": "user", + "parts": [ + {"kind": "text", "text": "Summarize the latest quarterly report."}, + {"kind": "text", "text": "Focus on revenue and margins."}, + ], + "messageId": "msg-1", +} + + +@pytest.mark.benchmark +def test_convert_messages_to_a2a_prompt(): + """Benchmark converting OpenAI messages into an A2A prompt string.""" + convert_messages_to_prompt(messages=MESSAGES) + + +@pytest.mark.benchmark +def test_extract_text_from_a2a_message_response(): + """Benchmark extracting text from a direct-message A2A response.""" + extract_text_from_a2a_response(response_dict=MESSAGE_RESPONSE) + + +@pytest.mark.benchmark +def test_extract_text_from_a2a_task_response(): + """Benchmark extracting text from a task-with-artifacts A2A response.""" + extract_text_from_a2a_response(response_dict=TASK_RESPONSE) + + +@pytest.mark.benchmark +def test_a2a_inbound_message_to_openai_messages(): + """Benchmark the proxy converting an inbound A2A message into OpenAI messages.""" + A2ACompletionBridgeTransformation.a2a_message_to_openai_messages(A2A_INBOUND_MESSAGE) diff --git a/tests/benchmarks/test_benchmarks.py b/tests/benchmarks/test_benchmarks.py index 123dad93e11..59b3e0b6d5c 100644 --- a/tests/benchmarks/test_benchmarks.py +++ b/tests/benchmarks/test_benchmarks.py @@ -6,10 +6,13 @@ in the litellm hot path: token counting, model info lookup, provider resolution, and cost calculation. """ +import threading + import pytest import litellm from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider +from litellm.litellm_core_utils.thread_pool_executor import executor from litellm.litellm_core_utils.token_counter import token_counter @@ -205,3 +208,21 @@ def test_get_model_cost_key_exact_match(): def test_get_model_cost_key_case_insensitive(): """Benchmark model cost key lookup with case-insensitive fallback.""" litellm.utils._get_model_cost_key("GPT-4o") + + +# --------------------------------------------------------------------------- +# Measurement hermeticity guard +# --------------------------------------------------------------------------- + + +@pytest.mark.benchmark +def test_logging_executor_runs_inline(): + """Guard that the shared logging executor runs submissions inline. + + Deferred submissions execute on worker threads, and callgrind attributes + their instructions to whichever benchmark's measurement window is open when + the valgrind scheduler resumes them, making results nondeterministic. + """ + future = executor.submit(threading.get_ident) + assert future.done() + assert future.result() == threading.get_ident() diff --git a/tests/benchmarks/test_inference_benchmarks.py b/tests/benchmarks/test_inference_benchmarks.py new file mode 100644 index 00000000000..0a95e34a32c --- /dev/null +++ b/tests/benchmarks/test_inference_benchmarks.py @@ -0,0 +1,113 @@ +""" +Performance benchmarks for the LLM inference (chat completion) hot path. + +The end-to-end cases use ``mock_response`` so the full SDK overhead is exercised +-- provider resolution, request/response transformation, ``ModelResponse`` +construction, token counting and cost calculation -- without any network I/O. The +``convert_to_model_response_object`` case isolates the provider-response to +``ModelResponse`` translation, the single deterministic core every non-streaming +completion runs. +""" + +import pytest + +import litellm +from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( + convert_to_model_response_object, +) +from litellm.types.utils import ModelResponse + +SIMPLE_MESSAGES = [{"role": "user", "content": "Hello, how are you?"}] + +MULTI_TURN_MESSAGES = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the capital of France?"}, + { + "role": "assistant", + "content": "The capital of France is Paris. It is known as the City of Light.", + }, + {"role": "user", "content": "Tell me more about Paris."}, +] + +TOOL_DEFINITIONS = [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, + }, + "required": ["location"], + }, + }, + } +] + +MOCK_RESPONSE = "The capital of France is Paris, the country's largest city and cultural centre." + +PROVIDER_RESPONSE = { + "id": "chatcmpl-abc123", + "object": "chat.completion", + "created": 1700000000, + "model": "gpt-4o", + "choices": [ + { + "index": 0, + "finish_reason": "stop", + "message": {"role": "assistant", "content": MOCK_RESPONSE}, + } + ], + "usage": {"prompt_tokens": 12, "completion_tokens": 16, "total_tokens": 28}, +} + + +@pytest.mark.benchmark +def test_completion_simple_message(): + """Benchmark a single-message completion through the full SDK path.""" + litellm.completion(model="gpt-4o", messages=SIMPLE_MESSAGES, mock_response=MOCK_RESPONSE) + + +@pytest.mark.benchmark +def test_completion_multi_turn(): + """Benchmark a multi-turn completion through the full SDK path.""" + litellm.completion(model="gpt-4o", messages=MULTI_TURN_MESSAGES, mock_response=MOCK_RESPONSE) + + +@pytest.mark.benchmark +def test_completion_with_tools(): + """Benchmark a completion that has to process tool schemas.""" + litellm.completion( + model="gpt-4o", + messages=SIMPLE_MESSAGES, + tools=TOOL_DEFINITIONS, + mock_response=MOCK_RESPONSE, + ) + + +@pytest.mark.benchmark +def test_completion_streaming(): + """Benchmark consuming a full streamed completion (CustomStreamWrapper).""" + stream = litellm.completion( + model="gpt-4o", + messages=SIMPLE_MESSAGES, + mock_response=MOCK_RESPONSE, + stream=True, + ) + for _ in stream: + pass + + +@pytest.mark.benchmark +def test_response_to_model_response_object(): + """Benchmark the provider-response to ModelResponse translation core.""" + convert_to_model_response_object( + response_object=PROVIDER_RESPONSE, + model_response_object=ModelResponse(), + ) diff --git a/tests/benchmarks/test_mcp_benchmarks.py b/tests/benchmarks/test_mcp_benchmarks.py new file mode 100644 index 00000000000..7e23ab1b4f5 --- /dev/null +++ b/tests/benchmarks/test_mcp_benchmarks.py @@ -0,0 +1,84 @@ +""" +Performance benchmarks for the MCP tool hot path. + +Two layers are covered: the client-side translation between MCP and OpenAI +function-calling formats, and the server-side tool-name prefixing that the proxy +runs on every list-tools (prefix each tool) and call-tool (strip prefix to route) +request. Both are pure-CPU and deterministic. +""" + +import pytest +from mcp.types import Tool as MCPTool + +from litellm.experimental_mcp_client.tools import ( + transform_mcp_tool_to_openai_tool, + transform_openai_tool_call_request_to_mcp_tool_call_request, +) +from litellm.proxy._experimental.mcp_server.utils import ( + add_server_prefix_to_name, + split_server_prefix_from_name, +) + + +def _make_tool(index: int) -> MCPTool: + return MCPTool( + name=f"tool_{index}", + description=f"Test tool number {index} that performs an operation", + inputSchema={ + "type": "object", + "properties": { + "query": {"type": "string", "description": "The search query"}, + "limit": {"type": "integer", "description": "Max results"}, + }, + "required": ["query"], + }, + ) + + +SINGLE_TOOL = _make_tool(0) +TOOL_LIST = tuple(_make_tool(i) for i in range(20)) +TOOL_NAMES = tuple(t.name for t in TOOL_LIST) + +SERVER_NAME = "github_mcp" +PREFIXED_TOOL_NAME = add_server_prefix_to_name("tool_0", SERVER_NAME) + +OPENAI_TOOL_CALL = { + "id": "call_abc123", + "type": "function", + "function": { + "name": "tool_0", + "arguments": '{"query": "weather in San Francisco", "limit": 5}', + }, +} + + +@pytest.mark.benchmark +def test_transform_single_mcp_tool_to_openai(): + """Benchmark translating one MCP tool into OpenAI tool format.""" + transform_mcp_tool_to_openai_tool(mcp_tool=SINGLE_TOOL) + + +@pytest.mark.benchmark +def test_transform_mcp_tool_list_to_openai(): + """Benchmark translating a full list-tools response into OpenAI format.""" + for tool in TOOL_LIST: + transform_mcp_tool_to_openai_tool(mcp_tool=tool) + + +@pytest.mark.benchmark +def test_transform_openai_tool_call_to_mcp(): + """Benchmark translating an OpenAI tool call into an MCP call request.""" + transform_openai_tool_call_request_to_mcp_tool_call_request(openai_tool=OPENAI_TOOL_CALL) + + +@pytest.mark.benchmark +def test_mcp_server_prefix_tool_list(): + """Benchmark the proxy prefixing every tool name on a list-tools response.""" + for name in TOOL_NAMES: + add_server_prefix_to_name(name, SERVER_NAME) + + +@pytest.mark.benchmark +def test_mcp_server_strip_prefix_on_call(): + """Benchmark the proxy stripping the server prefix to route a tool call.""" + split_server_prefix_from_name(PREFIXED_TOOL_NAME) diff --git a/tests/code_coverage_tests/liccheck.ini b/tests/code_coverage_tests/liccheck.ini index 5a09403c570..4d6e0528ed4 100644 --- a/tests/code_coverage_tests/liccheck.ini +++ b/tests/code_coverage_tests/liccheck.ini @@ -172,3 +172,4 @@ langgraph: >=1.0.10 # MIT License langgraph-prebuilt: >=1.0.8 # MIT License - https://github.com/langchain-ai/langgraph/blob/main/LICENSE pytest-rerunfailures: >=15.1 # MPL 2.0 license pytest-recording: >=0.13.4 # MIT license +expression: >=5.6.0 # MIT License - https://github.com/cognitedata/Expression/blob/main/LICENSE diff --git a/tests/code_coverage_tests/recursive_detector.py b/tests/code_coverage_tests/recursive_detector.py index 6d0e12314f0..e08d703d21f 100644 --- a/tests/code_coverage_tests/recursive_detector.py +++ b/tests/code_coverage_tests/recursive_detector.py @@ -36,6 +36,7 @@ IGNORE_FUNCTIONS = [ "_collect_argument_paths", # max depth set. "_split_text", # max depth set. "_mask_sequence", # max depth set. + "_walk_payload", # max depth set (DEFAULT_MAX_RECURSE_DEPTH_SENSITIVE_DATA_MASKER). "_delete_nested_value_custom", # max depth set (bounded by number of path segments). "filter_exceptions_from_params", # max depth set (default 20) to prevent infinite recursion. "__getattr__", # lazy loading pattern in litellm/__init__.py with proper caching to prevent infinite recursion. @@ -52,6 +53,8 @@ IGNORE_FUNCTIONS = [ "resolve_oci_schema_anyof", # OCI: bounded by JSON-schema tree depth (no cycles possible in well-formed input). "sanitize_oci_schema", # OCI: bounded by JSON-schema tree depth. "_freeze_for_dedupe", # OTEL: max depth set (default 16, _FREEZE_MAX_DEPTH); fails closed by returning repr(value) at the cap. + "apply_json_merge_patch", # max depth set (_MAX_MERGE_DEPTH=64); fails closed by raising ValueError at the cap. + "_filter_mcp_argument_value", # max depth set (DEFAULT_MAX_RECURSE_DEPTH); fails closed by blocking the MCP call at the cap. ] diff --git a/tests/e2e/CLAUDE.md b/tests/e2e/CLAUDE.md index b155a1a7024..88992038cb7 100644 --- a/tests/e2e/CLAUDE.md +++ b/tests/e2e/CLAUDE.md @@ -6,16 +6,16 @@ Code-style rules for writing tests under `tests/e2e/`. The harness already encod Each subdirectory under `tests/e2e/` is one suite, scoped to an endpoint family or behavior area. If you add a new folder, you must add a line here describing what kind of tests belong in it, so the layout stays self-describing. `gateway/` is the exception: it holds proxy configuration only and never tests -- `llm_translation/` - LLM endpoint and provider-translation behavior: passthrough, custom pricing, OCR +- `llm_translation/` - LLM endpoint and provider-translation behavior: passthrough, custom pricing, OCR, and the non-chat inference endpoints (`/v1/responses`, `/v1/messages`, `/embeddings`, `/v1/rerank`, `/v1/audio/speech`, `/v1/images/generations`), each against a deployment the test creates via `/model/new` and deletes on teardown +- `access_control/` - the gateway's authorization and error-shape contract: per-key model allow-lists, route-group permissions (`allowed_routes`), and unknown-model validation - `embeddings/` - the `/embeddings` endpoint across providers - `batches/` - the `/batches` endpoint (placeholder until the first test lands) - `realtime/` - realtime websocket sessions, including the pipecat audio path -- `budgets/` - budget definition, enforcement, and reset windows (key, team, tag, soft, multi-window) -- `spend_tracking/` - spend logging and cost attribution on `/spend/*` -- `models_mgmt/` - model-management routes (add/update, tpm persistence) +- `quota_management/` - quota enforcement and accounting, one subfolder per behavior: `budgets/` (budget definition, enforcement, and reset windows: key, team, tag, soft, multi-window) and `spend_tracking/` (spend logging and cost attribution on `/spend/*`) +- `management/` - key/team/user/organization management routes: create/update/delete persistence via the info routes, team membership, and llm-only-key route denials; also the dashboard UI behavior on top of them, driven through the proxy-served UI at /ui with playwright (optional dep behind importorskip) - `logging/` - logging-integration delivery (datadog and friends) - `security/` - secret handling and log-leak protection -- `router/` - routing and reliability behavior (rate limits, fallbacks, cooldowns) +- `router/` - routing and reliability behavior (fallbacks, cooldowns) - `gateway/` - proxy configuration only (`litellm-config.yml`); no tests ## Lay the pattern down in a class @@ -56,29 +56,32 @@ Mark live tests with `@pytest.mark.e2e` (on the class or the module). Pure cover ## Typing -The harness is fully typed and new code must not add `Any` or widen the basedpyright budgets. When a response field is untyped, model it in `models.py` (just the fields you read) and let pydantic validate it, rather than threading a `dict` or `Any` through the test +The harness is fully typed with no error budget: `make lint-e2e-basedpyright` must report zero basedpyright errors, and CI enforces that on any PR touching `tests/e2e/**/*.py`. When a response field is untyped, model it in `models.py` (just the fields you read) and let pydantic validate it, rather than threading a `dict` or `Any` through the test ## Coverage registry The set of tests we want is a registry checked into this repo, one row per behavior; that file is the definition of done and the denominator. Each e2e test declares what it covers with `@pytest.mark.covers("...")`, and a small collector diffs the registry against the tests and ships coverage to the existing Grafana. No Allure, no new dependencies -Coverage is organized as module > feature > test. There are six modules: LLMs, MCPs, Management/UI, Reliability & Performance, Logging & Guardrails, and Other. A feature is either an endpoint (`/chat/completions`) or a behavior (fallbacks, rate limits; config-driven, with no route of its own). A cell reads like `llm.chat_completions.bedrock_converse.tool_use.stream.works` +Coverage is organized as module > feature > test. Dashboard modules are `Core LLMs`, `Non-Core LLMs`, `MCPs`, `Management/UI`, `Reliability & Performance`, `Quota Management`, `Logging & Guardrails`, and `Other`. The Loki stdout formatter maps those display modules to log-safe labels (`core_llms`, `non_core_llms`, `mcp`, `management_ui`, `reliability_performance`, `quota_management`, `logging_guardrails`, and `other`) without changing JSON or Prometheus labels. A feature is either an endpoint (`/chat/completions`) or a behavior (fallbacks, rate limits; config-driven, with no route of its own). A cell reads like `llm.chat_completions.bedrock_converse.tool_use.stream.works` The metric is coverage: the share of registry rows that have a passing covering test, reported to Grafana per module so a gap surfaces as an uncovered row rather than a silent absence +Tests do not declare a dashboard module directly. They only declare the registry cell id with `@pytest.mark.covers("...")`; the registry row decides the module, tier, endpoint, and dashboard rollup. Run `python -m coverage_registry.collector --strict` when you want CI to reject unknown marker ids. Add `--fail-on-collection-errors` when the job should also fail on pytest collection errors. + ### Naming grammar per module -LLMs - endpoint features (subject = the route), seeded from the Claude Code compat matrix +LLMs - endpoint features (subject = the route), seeded from the Claude Code compat matrix. `chat_completions`, `messages`, and `responses` roll up to `Core LLMs`. Other LLM endpoints, including `batches` and `realtime`, roll up to `Non-Core LLMs`. ``` llm..... endpoint : chat_completions | messages | responses | embeddings | batches | files | rerank | images_generations | audio_speech | audio_transcriptions | moderations - route : openai | azure_openai | anthropic | bedrock_invoke | bedrock_converse | vertex | azure_foundry + | realtime + route : openai | azure_openai | anthropic | bedrock_converse | vertex | azure_foundry + | cohere | together_ai (vocab varies per endpoint; messages is anthropic-format only) - capability : basic | tool_use | prompt_cache_5m | prompt_cache_1h | vision | thinking - | thinking_tool_use | pdf_input | web_search | structured_output | count_tokens - | tool_search | long_context_1m + capability : basic | tool_use | prompt_cache_5m | vision | thinking | structured_output + | service_tier streaming : stream | nonstream (omit where n/a) assertion : works | cost_logged label (not in id): model = haiku-4.5 | sonnet-4.6 | opus-4.7 | gpt-* @@ -111,14 +114,35 @@ Reliability & Performance - behavior features (no route; endpoint is exercised_o ``` reliability... - behavior : fallback | retry | cooldown | timeout | ratelimit | routing | cache | circuit_breaker | perf + behavior : fallback | retry | cooldown | timeout | routing | cache | circuit_breaker | perf variant : 5xx | context_window | content_policy | 429 | timeout simple_shuffle | usage_based | latency_based | cost_based | least_busy latency | throughput (perf only; SLO/threshold assertion, not binary) assertion : routes_to_fallback | succeeds_within_retries | picks_under_tpm | returns_cached | trips_then_recovers | under_slo e.g. reliability.fallback.context_window.routes_to_fallback exercised_on=[chat_completions] - reliability.ratelimit.rpm.blocks_over_limit exercised_on=[chat_completions, messages] + reliability.cooldown.429.trips_then_recovers exercised_on=[chat_completions, messages] +``` + +Quota Management - behavior features (entity- or config-driven caps and their accounting; endpoint is exercised_on) + +``` +quota_management... + behavior : ratelimit | budget | spend_tracking + variant : rpm | tpm | priority_generous | priority_strict + key | internal_user | end_user | organization | team_member | tag + | model_max | soft | key_multi_window | team_multi_window + | fallback | spend_counter + chat_completions | stream | embeddings | cache_hit | key_rollup + | concurrent_burst | tags | end_user | per_model | failure + | spend_calculate | pagination + assertion : blocks_over_limit | resets_after_window | headers_report_remaining | picks_under_tpm + | blocks_then_resets | resets_windows_independently | alerts_without_blocking + | isolates_per_model | routes_to_fallback | reseed_matches_db | logs_cost | zero_cost + | matches_sum_of_logs | loses_no_spend | attributes_spend | writes_own_rows + | writes_failure_row | returns_cost | keeps_total + e.g. quota_management.ratelimit.rpm.blocks_over_limit exercised_on=[chat_completions, messages] + quota_management.budget.key.blocks_over_limit exercised_on=[chat_completions] ``` Logging & Guardrails - behavior features (config-driven; endpoint is exercised_on) diff --git a/tests/e2e/CONTRIBUTING.md b/tests/e2e/CONTRIBUTING.md index 12aea6bbc25..2082f2c9de4 100644 --- a/tests/e2e/CONTRIBUTING.md +++ b/tests/e2e/CONTRIBUTING.md @@ -9,15 +9,18 @@ When contributing to this directory, please first discuss the change you wish to ## Setup -The suites run against a live proxy, so bring one up first. `docker-compose.yml` here starts that proxy with its Postgres and Redis, serving `gateway/litellm-config.yml`; add any model, pricing override, or guardrail your test needs to that file and read it back in the test rather than hardcoding values. `gateway/` holds proxy configuration only, so never put tests there +The suites run against a live proxy, so bring one up first. `docker-compose.yml` here starts that proxy with a throwaway Postgres and Redis; `docker compose down -v` resets everything, so no state leaks between runs. The proxy config is inlined in the compose file under `configs`, prewired with example models (`gpt-5.5`, `claude-haiku-4-5`, `gemini-2.5-flash`, `openai-text-embedding-3-small`) whose keys come from your `.env`. If your test needs another model, a pricing override, or a guardrail declared up front, add it to that inline config and read it back in the test rather than hardcoding values ## Running the tests locally -1. Create a .env file and add provider keys: +1. Create a `.env` file in this directory with the provider keys the example models use: + ```bash OPENAI_API_KEY="sk-..." ANTHROPIC_API_KEY="sk-..." - + GEMINI_API_KEY="..." + ``` + 2. Bring the stack up from this directory: ```bash @@ -31,6 +34,20 @@ The suites run against a live proxy, so bring one up first. `docker-compose.yml` uv run pytest tests/e2e/llm_translation/ -v ``` + The browser tests in the `management/` suite drive the dashboard the proxy serves at `/ui` through playwright, an optional dependency behind `importorskip` (the suite's API tests run without it). It lives in the `e2e-dev` dependency group; install it along with its browser: + + ```bash + uv sync --inexact --group e2e-dev + uv run playwright install chromium + ``` + + They also need a proxy whose bundled UI contains the change under test. The published `main-latest` image ships the UI from the last release; to test local UI changes, build the image from your branch and point the compose stack at it: + + ```bash + docker build -t litellm-local . + LITELLM_E2E_IMAGE=litellm-local docker compose up -d + ``` + 4. Tear it down when you're done: ```bash @@ -123,7 +140,17 @@ Mark live tests with `@pytest.mark.e2e` (on the class or the module). Pure cover Before you push -- Run basedpyright over your changes; the harness is fully typed and new code must not add `Any` or widen the budgets -- Bring the stack up with docker-compose from this directory and run your suite locally against it, so you exercise the same skip-vs-fail path CI does -- Use the config at `tests/e2e/gateway/litellm-config.yml` if your feature needs a model, pricing override, guardrail, or other proxy setting declared up front; add the deployment there and read it back in the test rather than hardcoding values -- Capture screenshots of the tests passing and attach them to the PR as proof of fix +1. Run `make lint-e2e-basedpyright` (or `make pre-commit` with your changes staged); the harness is fully typed and the gate allows zero basedpyright errors, enforced in CI on any PR touching `tests/e2e/**/*.py` + +2. Add the models your test needs to the inline config in `docker-compose.yml` + +3. Bring the stack up and run your suite against it: + + ```bash + docker compose up -d + uv run pytest tests/e2e// -v + ``` + +4. Capture screenshots of the test run and attach them to the PR as proof + +5. If a test fails because it surfaced a real issue in the product, flag that explicitly in the PR rather than reworking the test until it passes diff --git a/tests/e2e/access_control/access_control_client.py b/tests/e2e/access_control/access_control_client.py new file mode 100644 index 00000000000..d7bc9c280aa --- /dev/null +++ b/tests/e2e/access_control/access_control_client.py @@ -0,0 +1,56 @@ +"""Client for the access-control e2e suite.""" + +from __future__ import annotations + +from dataclasses import dataclass + +from e2e_gateway import Gateway, build_gateway +from e2e_http import StreamingResponse +from models import ( + ChatBody, + ChatMessage, + KeyGenerateBody, + LiteLLMParamsBody, + ModelInfoBody, + ModelNewBody, +) + +MODEL_ACCESS_DENIED_MARKER = "key_model_access_denied" +ROUTE_NOT_ALLOWED_MARKER = "not allowed to call this route" + + +@dataclass(frozen=True, slots=True) +class AccessControlClient: + gateway: Gateway + + def llm_only_key(self) -> str: + return self.gateway.generate_key( + KeyGenerateBody(models=[], allowed_routes=["llm_api_routes"]) + ) + + def delete_key(self, key: str) -> None: + self.gateway.delete_key(key) + + def chat_status(self, key: str, model: str, content: str) -> StreamingResponse: + return self.gateway.transport.send( + "/chat/completions", + headers=self.gateway.transport.bearer(key), + json=ChatBody( + model=model, messages=[ChatMessage(role="user", content=content)] + ), + ) + + def create_model_status(self, key: str, model_name: str) -> StreamingResponse: + return self.gateway.transport.send( + "/model/new", + headers=self.gateway.transport.bearer(key), + json=ModelNewBody( + model_name=model_name, + litellm_params=LiteLLMParamsBody(model="openai/gpt-4o-mini"), + model_info=ModelInfoBody(id=model_name), + ), + ) + + +def build_client() -> AccessControlClient: + return AccessControlClient(gateway=build_gateway()) diff --git a/tests/e2e/access_control/conftest.py b/tests/e2e/access_control/conftest.py new file mode 100644 index 00000000000..9f4a00fe06f --- /dev/null +++ b/tests/e2e/access_control/conftest.py @@ -0,0 +1,10 @@ +"""Access-control suite client fixture; lifecycle/skip/marker live in the parent conftest.""" + +import pytest + +from access_control_client import AccessControlClient, build_client + + +@pytest.fixture(scope="session") +def client() -> AccessControlClient: + return build_client() diff --git a/tests/e2e/access_control/test_access_control_e2e.py b/tests/e2e/access_control/test_access_control_e2e.py new file mode 100644 index 00000000000..ce649fa2400 --- /dev/null +++ b/tests/e2e/access_control/test_access_control_e2e.py @@ -0,0 +1,83 @@ +"""Live e2e: the gateway's authorization and error-shape contract. + +A virtual key may only call models in its allow-list and route groups in its +allowed_routes; both denials are a 403 raised before any provider is touched. A +syntactically valid request naming a non-existent model is a 400 with a JSON body, +never forwarded and never a 5xx. Migrated from +litellm-regression-tests/tests/test_access_control.py: the source asserted 401 for +the disallowed-model case against an older proxy, but the current contract +(auth_checks.py) is a 403 key_model_access_denied, and the unknown-route check is +replaced by a stronger route-permission check (an llm-only key rejected from a +management route). +""" + +from __future__ import annotations + +import json + +import pytest + +from access_control_client import ( + AccessControlClient, + MODEL_ACCESS_DENIED_MARKER, + ROUTE_NOT_ALLOWED_MARKER, +) +from e2e_config import unique_marker +from lifecycle import ResourceManager + +pytestmark = pytest.mark.e2e + +ALLOWED_MODEL = "gemini-2.5-flash" +DISALLOWED_MODEL = "gpt-5.5" + + +def _is_json(body: str) -> bool: + try: + json.loads(body) + return True + except ValueError: + return False + + +class TestAccessControl: + def test_disallowed_model_is_denied_403( + self, client: AccessControlClient, resources: ResourceManager + ) -> None: + key = resources.key(models=[ALLOWED_MODEL]) + result = client.chat_status( + key, DISALLOWED_MODEL, f"capital of France? {unique_marker()}" + ) + assert result.status_code == 403, ( + f"key limited to {ALLOWED_MODEL!r} calling {DISALLOWED_MODEL!r} must be " + f"denied 403, got {result.status_code}: {result.body[:300]}" + ) + assert MODEL_ACCESS_DENIED_MARKER in result.body, ( + f"403 body must be a model-access denial, got: {result.body[:300]}" + ) + + def test_llm_only_key_forbidden_from_management_route_403( + self, client: AccessControlClient, resources: ResourceManager + ) -> None: + key = client.llm_only_key() + resources.defer(lambda: client.delete_key(key)) + result = client.create_model_status(key, f"e2e-forbidden-{unique_marker()}") + assert result.status_code == 403, ( + f"llm-only key calling a management route must be denied 403, got " + f"{result.status_code}: {result.body[:300]}" + ) + assert ROUTE_NOT_ALLOWED_MARKER in result.body, ( + f"403 body must be a route-permission denial, got: {result.body[:300]}" + ) + + def test_unknown_model_returns_400( + self, client: AccessControlClient, resources: ResourceManager + ) -> None: + key = resources.key() + result = client.chat_status( + key, f"nonexistent-model-{unique_marker()}", "hi this is a test" + ) + assert result.status_code == 400, ( + f"unknown model must be rejected 400 before forwarding, got " + f"{result.status_code}: {result.body[:300]}" + ) + assert _is_json(result.body), f"400 body must be valid JSON: {result.body[:300]}" diff --git a/tests/e2e/batches/COVERAGE.md b/tests/e2e/batches/COVERAGE.md new file mode 100644 index 00000000000..4debf50bd6c --- /dev/null +++ b/tests/e2e/batches/COVERAGE.md @@ -0,0 +1,81 @@ +# Batches Test Coverage Matrix + +Live e2e coverage of the Batches API over a real proxy, real provider keys, and +real cost. Synchronous tier only: a batch's completion window is 24h, so these +tests never wait for `completed`. They assert the proxy accepts, routes, retrieves, +cancels, and lists a batch; everything created is deleted on teardown. + +## Provider x operation + +Only supported cells are tested. The capability table in `capabilities.py` holds one +row per supported (provider, scenario) pair, so there are no skipped cells in the +parametrized run. The batches suite never skips: missing provider creds or upstream +failures are hard test failures (see `tests/e2e/CLAUDE.md`). + +| Provider | create | retrieve | cancel | list | file backing | +|-----------|--------|----------|--------|------|--------------| +| OpenAI | yes | yes | yes | yes | OpenAI Files | +| Azure | yes | yes | yes | yes | Azure Files | +| Vertex AI | yes | yes | yes | yes | GCS (`gcs_bucket_name` / `GCS_BUCKET_NAME` on model) | +| Bedrock | yes (unified only) | yes | no (limited upstream) | no | S3 (`s3_bucket_name` + `aws_*` + `AWS_BATCH_ROLE_ARN` on model) | + +Bedrock cancel is unreliable upstream and list is unsupported, so both are gated off +(`can_cancel=False`, `can_list=False`) when that provider is enabled in the matrix. +Bedrock file upload requires a model on the request (`encoded` / `unified` scenarios only); +`model_param` and `provider_fallback` are omitted because `POST /bedrock/v1/files` has no +model-less passthrough path. + +## Routing scenarios (per `litellm/proxy/batches_endpoints/endpoints.py`) + +Each create-capable provider runs all four. The test asserts the returned file id +and batch id carry the shape that scenario must produce (`matches_id_shape`): + +| Scenario | How the batch is routed | File id | Batch id | +|----------|-------------------------|---------|----------| +| `encoded` | upload with `?model=` -> model-encoded file id -> create with just that id | model-encoded | model-encoded | +| `unified` | upload with `target_model_names=` -> unified managed file id -> create with that id | managed | managed | +| `model_param` | raw file (provider-fallback upload) -> create with `model` in the body | raw | model-encoded | +| `provider_fallback` | raw file -> `POST /{provider}/v1/batches`, env creds, no model | raw | raw (native provider shape) | + +"managed" ids base64-decode to a `litellm_proxy` marker; "model-encoded" ids keep the +provider prefix and base64-encode `litellm:;model,`; "raw" ids are the +provider's native ids. Asserting these catches a proxy that returns a raw id where it +should manage it, or vice versa. On top of the id shape, a misroute to the wrong +provider also fails create (the file id / model do not belong there), and the +`provider_fallback` raw batch id is additionally checked against the provider's native +shape (`raw_id_matches_provider`). + +## Key model restriction + +`test_batch_key_model_access_denied` mints a key restricted to one model +(`resources.key(models=[...])`) and proves the proxy returns 403 +`key_model_access_denied` both when that key uploads a file for a disallowed model +(files endpoint) and when it creates a batch for a disallowed model (batches +endpoint). + +## Per-endpoint output assertions + +Each endpoint's full response is validated, not just the id. File upload asserts +`object=="file"`, `purpose=="batch"`, a positive `bytes`, a status, and a created-at. +Batch create / retrieve assert `object=="batch"`, `endpoint=="/v1/chat/completions"`, +`completion_window=="24h"`, a non-empty `input_file_id`, and a created-at; retrieve +additionally cross-checks that `id` and `input_file_id` match the created batch. +Cancel asserts the same id, `object=="batch"`, and a cancelling/cancelled status. List +asserts the `object=="list"` envelope and that the created batch is present as a batch. +File delete asserts `object=="file"` and `deleted==True`. + +## This suite's files + +| File | Covers | +|------|--------| +| `batch_client.py` | typed file upload/download + batch create/retrieve/cancel/list/delete over the shared Gateway; runtime batch model registration via /model/new; denial helpers | +| `capabilities.py` | the provider x scenario matrix + per-provider /model/new params + id-shape classifiers + per-provider raw-id assertion | +| `conftest.py` | session-scoped batch deployment registration and teardown | +| `test_batches_e2e.py` | parametrized lifecycle with per-endpoint output assertions, file upload/delete outputs, key-model-access denial | + +## Out of scope (intentionally) + +Driving a batch to `completed`, cost tracking on completion, and the DB write-back +are not covered here; the 24h window makes them unfit for a synchronous gate. That +logic belongs in a DI-stubbed proxy integration test under `tests/test_litellm/proxy/` +where the provider client is injected to return `completed` deterministically. diff --git a/tests/e2e/batches/batch_client.py b/tests/e2e/batches/batch_client.py new file mode 100644 index 00000000000..fda4e87e478 --- /dev/null +++ b/tests/e2e/batches/batch_client.py @@ -0,0 +1,174 @@ +"""Client for the batches e2e suite: file upload/download and the batch +operations (create / retrieve / cancel / list) over the shared Gateway. + +Batch deployments are registered at runtime via /model/new (see conftest.py), +not baked into the proxy config. `create_batch` returns the raw HTTP outcome +(StreamingResponse) so a 403 model access denial and a provider-native batch +body both surface; the test parses BatchObject from the body. A `provider` arg +routes a call to /{provider}/v1/..., which the provider-fallback scenario needs +(its ids are raw, not model-encoded). The request/response models are +co-located here because only this suite uses them. +""" + +from __future__ import annotations + +from dataclasses import dataclass + +from pydantic import BaseModel + +from e2e_gateway import Gateway, build_gateway +from e2e_http import ( + FileUploadForm, + NoBody, + Result, + StreamingResponse, + UnknownApiError, +) +from models import LiteLLMParamsBody + + +class FileObject(BaseModel): + id: str + object: str | None = None + purpose: str | None = None + bytes: int | None = None + status: str | None = None + created_at: int | None = None + + +class BatchObject(BaseModel): + id: str + object: str | None = None + status: str + endpoint: str | None = None + input_file_id: str | None = None + output_file_id: str | None = None + completion_window: str | None = None + created_at: int | None = None + model: str | None = None + + +class BatchList(BaseModel): + object: str | None = None + data: list[BatchObject] = [] + + +class FileDeleteResponse(BaseModel): + id: str + object: str | None = None + deleted: bool + + +class BatchCreateBody(BaseModel): + input_file_id: str + endpoint: str = "/v1/chat/completions" + completion_window: str = "24h" + model: str | None = None + + +class ModelQuery(BaseModel): + model: str | None = None + + +def is_model_access_denied(resp: StreamingResponse) -> bool: + """True if the proxy rejected the call because the key may not access the model.""" + return resp.status_code == 403 and "key_model_access_denied" in resp.body + + +def is_result_access_denied[R: BaseModel](result: Result[R]) -> bool: + match result: + case UnknownApiError(status_code=403, body=body): + return "key_model_access_denied" in body + case _: + return False + + +@dataclass(frozen=True, slots=True) +class BatchClient: + gateway: Gateway + + def create_model(self, model_name: str, litellm_params: LiteLLMParamsBody) -> str: + return self.gateway.create_model(model_name, litellm_params, mode="batch") + + def delete_model(self, model_id: str) -> None: + self.gateway.delete_model(model_id) + + def upload_file( + self, + *, + content: bytes, + form: FileUploadForm, + key: str, + model: str | None = None, + provider: str | None = None, + ) -> Result[FileObject]: + return self.gateway.transport.upload( + _files_path(provider), + headers=self.gateway.transport.bearer(key), + form=form, + filename="batch_input.jsonl", + content=content, + params=ModelQuery(model=model), + response_type=FileObject, + ) + + def create_batch( + self, *, body: BatchCreateBody, key: str, provider: str | None = None + ) -> StreamingResponse: + return self.gateway.transport.send( + _batches_path(provider), + headers=self.gateway.transport.bearer(key), + json=body, + ) + + def retrieve_batch( + self, batch_id: str, *, key: str, provider: str | None = None + ) -> Result[BatchObject]: + return self.gateway.transport.get( + f"{_batches_path(provider)}/{batch_id}", + headers=self.gateway.transport.bearer(key), + params=NoBody(), + response_type=BatchObject, + ) + + def cancel_batch( + self, batch_id: str, *, key: str, provider: str | None = None + ) -> Result[BatchObject]: + return self.gateway.transport.post( + f"{_batches_path(provider)}/{batch_id}/cancel", + headers=self.gateway.transport.bearer(key), + json=NoBody(), + response_type=BatchObject, + ) + + def list_batches( + self, *, key: str, provider: str | None = None + ) -> Result[BatchList]: + return self.gateway.transport.get( + _batches_path(provider), + headers=self.gateway.transport.bearer(key), + params=NoBody(), + response_type=BatchList, + ) + + def delete_file( + self, file_id: str, *, key: str, provider: str | None = None + ) -> Result[FileDeleteResponse]: + return self.gateway.transport.delete( + f"{_files_path(provider)}/{file_id}", + headers=self.gateway.transport.bearer(key), + json=NoBody(), + response_type=FileDeleteResponse, + ) + + +def _files_path(provider: str | None) -> str: + return f"/{provider}/v1/files" if provider else "/v1/files" + + +def _batches_path(provider: str | None) -> str: + return f"/{provider}/v1/batches" if provider else "/v1/batches" + + +def build_client() -> BatchClient: + return BatchClient(gateway=build_gateway()) diff --git a/tests/e2e/batches/capabilities.py b/tests/e2e/batches/capabilities.py new file mode 100644 index 00000000000..3eb0e0328be --- /dev/null +++ b/tests/e2e/batches/capabilities.py @@ -0,0 +1,182 @@ +"""Provider x routing-scenario matrix for the batches lifecycle e2e.""" + +from __future__ import annotations + +import base64 +import os +from dataclasses import dataclass +from typing import Literal + +from models import LiteLLMParamsBody + + +def _env_ref(*names: str) -> str: + for name in names: + value = os.environ.get(name) + if value is not None and value.strip() != "": + return f"os.environ/{name}" + return f"os.environ/{names[0]}" + +Scenario = Literal["encoded", "unified", "model_param", "provider_fallback"] + +IdShape = Literal["managed", "model_encoded", "raw"] + +SCENARIOS: tuple[Scenario, ...] = ( + "encoded", + "unified", + "model_param", + "provider_fallback", +) + + +@dataclass(frozen=True, slots=True) +class Provider: + name: str + model: str + raw_model: str + can_cancel: bool + can_list: bool + + def litellm_params(self) -> LiteLLMParamsBody: + match self.name: + case "openai": + return LiteLLMParamsBody( + model="openai/gpt-4o-mini", + api_key="os.environ/OPENAI_API_KEY", + ) + case "azure": + return LiteLLMParamsBody( + model="azure/gpt-5.4-mini-batch", + api_base="os.environ/AZURE_API_BASE", + api_key="os.environ/AZURE_API_KEY", + api_version="2025-04-01-preview", + ) + case "vertex_ai": + return LiteLLMParamsBody( + model="vertex_ai/gemini-2.5-flash", + vertex_project="os.environ/VERTEXAI_PROJECT", + vertex_location="us-central1", + vertex_credentials="os.environ/VERTEXAI_CREDENTIALS", + gcs_bucket_name="os.environ/GCS_BUCKET_NAME", + bucket_name="os.environ/GCS_BUCKET_NAME", + ) + case "bedrock": + return LiteLLMParamsBody( + model="bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0", + aws_access_key_id="os.environ/AWS_ACCESS_KEY_ID", + aws_secret_access_key="os.environ/AWS_SECRET_ACCESS_KEY", + aws_region_name="os.environ/AWS_REGION", + s3_region_name="os.environ/AWS_REGION", + s3_bucket_name=_env_ref("AWS_BATCH_S3_BUCKET", "AWS_S3_BUCKET_NAME"), + s3_access_key_id="os.environ/AWS_ACCESS_KEY_ID", + s3_secret_access_key="os.environ/AWS_SECRET_ACCESS_KEY", + aws_batch_role_arn="os.environ/AWS_BATCH_ROLE_ARN", + ) + case _: + raise ValueError(f"unknown batch provider: {self.name!r}") + + +@dataclass(frozen=True, slots=True) +class Capability: + provider: str + model: str + raw_model: str + scenario: Scenario + can_cancel: bool + can_list: bool + + @property + def id(self) -> str: + return f"{self.provider}-{self.scenario}" + + @property + def jsonl_model(self) -> str: + return self.model if self.scenario == "unified" else self.raw_model + + +PROVIDERS: tuple[Provider, ...] = ( + Provider("openai", "openai-batch", "gpt-4o-mini", can_cancel=True, can_list=True), + Provider("azure", "azure-batch", "gpt-5.4-mini-batch", can_cancel=True, can_list=True), + Provider( + "vertex_ai", "vertex-batch", "gemini-2.5-flash", can_cancel=True, can_list=True + ), + Provider( + "bedrock", + "bedrock-batch", + "bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0", + can_cancel=False, + can_list=False, + ), +) + +BEDROCK_SCENARIOS: tuple[Scenario, ...] = ("unified",) + + +def scenarios_for_provider(provider: Provider) -> tuple[Scenario, ...]: + if provider.name == "bedrock": + return BEDROCK_SCENARIOS + return SCENARIOS + + +CAPABILITIES: tuple[Capability, ...] = tuple( + Capability(p.name, p.model, p.raw_model, scenario, p.can_cancel, p.can_list) + for p in PROVIDERS + for scenario in scenarios_for_provider(p) +) + + +def raw_id_matches_provider(provider: str, batch_id: str) -> bool: + if provider in ("openai", "azure"): + return batch_id.startswith("batch") + if provider == "vertex_ai": + return ( + batch_id.startswith("projects/") + or "batchPredictionJobs" in batch_id + or batch_id.isdigit() + ) + if provider == "bedrock": + return batch_id.startswith("arn:aws:bedrock:") + return True + + +FILE_ID_SHAPE: dict[Scenario, IdShape] = { + "encoded": "model_encoded", + "unified": "managed", + "model_param": "raw", + "provider_fallback": "raw", +} + +BATCH_ID_SHAPE: dict[Scenario, IdShape] = { + "encoded": "model_encoded", + "unified": "managed", + "model_param": "model_encoded", + "provider_fallback": "raw", +} + + +def _b64_decode(value: str) -> str: + padded = value + "=" * (-len(value) % 4) + try: + return base64.urlsafe_b64decode(padded).decode() + except Exception: + return "" + + +def is_managed_id(id_str: str) -> bool: + return _b64_decode(id_str).startswith("litellm_proxy") + + +def is_model_encoded_id(id_str: str) -> bool: + for prefix in ("file-", "batch_"): + if id_str.startswith(prefix): + decoded = _b64_decode(id_str[len(prefix) :]) + return decoded.startswith("litellm:") and ";model," in decoded + return False + + +def matches_id_shape(shape: IdShape, id_str: str) -> bool: + if shape == "managed": + return is_managed_id(id_str) + if shape == "model_encoded": + return is_model_encoded_id(id_str) + return not is_managed_id(id_str) and not is_model_encoded_id(id_str) diff --git a/tests/e2e/batches/conftest.py b/tests/e2e/batches/conftest.py new file mode 100644 index 00000000000..2c6070c437a --- /dev/null +++ b/tests/e2e/batches/conftest.py @@ -0,0 +1,52 @@ +"""Batches suite's `client` fixture. + +The shared lifecycle (resources/scoped_key), proxy liveness skip, and e2e marker +live in the parent tests/e2e/conftest.py. BatchClient holds the shared Gateway, so +the `resources` fixture cleans up keys through it; tests register file deletes and +batch cancels via `resources.defer(...)`. + +Batch deployments (openai-batch, azure-batch, vertex-batch, ...) are registered +once per session via /model/new and deleted on teardown so they need not live in +the proxy config. +""" + +from __future__ import annotations + +from typing import Iterator + +import pytest + +from batch_client import BatchClient, build_client +from capabilities import PROVIDERS +from e2e_http import NoBody + + +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line( + "markers", + "covers: registry cell a test covers, e.g. llm.batches.openai.basic.nonstream.works", + ) + + +@pytest.fixture(scope="session") +def client() -> BatchClient: + return build_client() + + +@pytest.fixture(scope="session") +def batch_deployments(client: BatchClient) -> Iterator[None]: + probe = client.gateway.probe("/health/liveliness", params=NoBody()) + if not probe.healthy: + yield + return + + registered: list[str] = [] + try: + for provider in PROVIDERS: + registered.append( + client.create_model(provider.model, provider.litellm_params()) + ) + yield + finally: + for model_id in registered: + client.delete_model(model_id) diff --git a/tests/e2e/batches/test_batches_e2e.py b/tests/e2e/batches/test_batches_e2e.py new file mode 100644 index 00000000000..7d54f05656e --- /dev/null +++ b/tests/e2e/batches/test_batches_e2e.py @@ -0,0 +1,388 @@ +"""Live e2e for the Batches API across every provider LiteLLM supports. + +Synchronous tier only: a batch's completion window is 24h, so these never wait for +"completed". Each case uploads a tiny JSONL, creates the batch through one of the +four routing scenarios, asserts it was accepted (non-terminal status) and routed to +the right provider, then retrieves / cancels / lists where the provider supports it. +Everything created is deleted on teardown. Completion + cost tracking are out of +scope here (see COVERAGE.md). + +Routing signal: for provider_fallback the raw batch id discriminates the provider; +for the encoded/unified/model_param scenarios the proxy re-encodes the id, so the +load-bearing signal is that create SUCCEEDS against that provider's own model - a +misroute to the wrong provider fails the create. +""" + +from __future__ import annotations + +import json +import time +from typing import Callable + +import pytest + +from e2e_config import unique_marker + +from batch_client import ( + BatchClient, + BatchCreateBody, + BatchObject, + FileObject, + is_model_access_denied, + is_result_access_denied, +) +from capabilities import ( + BATCH_ID_SHAPE, + CAPABILITIES, + FILE_ID_SHAPE, + Capability, + matches_id_shape, + raw_id_matches_provider, +) +from e2e_http import ( + FileUploadForm, + Result, + StreamingResponse, + Success, + UnknownApiError, + require_successful_call, + unwrap, +) +from lifecycle import ResourceManager +from models import KeyGenerateBody, SpendLogRow, SpendLogsParams + +pytestmark = pytest.mark.e2e + +CREATED_BATCH_STATUSES = {"validating", "in_progress", "finalizing"} +BATCH_CANCEL_DELAY_SECONDS = 2 +BATCH_TERMINAL_BEFORE_CANCEL = {"failed", "cancelled", "expired"} +BATCH_CANCEL_RETRIES = 3 + + +def cancel_batch( + client: BatchClient, batch_id: str, *, key: str, provider: str | None +) -> BatchObject: + last = client.cancel_batch(batch_id, key=key, provider=provider) + for _ in range(BATCH_CANCEL_RETRIES - 1): + match last: + case Success(data=data): + return data + case UnknownApiError(status_code=500): + time.sleep(1) + last = client.cancel_batch(batch_id, key=key, provider=provider) + case _: + break + return unwrap(last) + + +def render_jsonl(model: str) -> bytes: + line = { + "custom_id": "req-1", + "method": "POST", + "url": "/v1/chat/completions", + "body": { + "model": model, + "messages": [{"role": "user", "content": "ping"}], + "max_tokens": 8, + }, + } + return (json.dumps(line) + "\n").encode() + + +def upload_for_scenario( + client: BatchClient, cap: Capability, content: bytes, key: str +) -> Result[FileObject]: + if cap.scenario == "encoded": + return client.upload_file( + content=content, + form=FileUploadForm(purpose="batch"), + model=cap.model, + key=key, + ) + if cap.scenario == "unified": + return client.upload_file( + content=content, + form=FileUploadForm(purpose="batch", target_model_names=cap.model), + key=key, + ) + return client.upload_file( + content=content, + form=FileUploadForm(purpose="batch"), + key=key, + provider=cap.provider, + ) + + +def create_for_scenario( + client: BatchClient, cap: Capability, file_id: str, key: str +) -> StreamingResponse: + if cap.scenario == "model_param": + return client.create_batch( + body=BatchCreateBody(input_file_id=file_id, model=cap.model), key=key + ) + if cap.scenario == "provider_fallback": + return client.create_batch( + body=BatchCreateBody(input_file_id=file_id), key=key, provider=cap.provider + ) + return client.create_batch(body=BatchCreateBody(input_file_id=file_id), key=key) + + +def op_provider(cap: Capability) -> str | None: + """provider_fallback ids are raw, so retrieve/cancel/list/delete need the provider + hint; the other scenarios encode it into the id and route automatically.""" + return cap.provider if cap.scenario == "provider_fallback" else None + + +def quietly(action: Callable[[], object]) -> Callable[[], None]: + """Adapt a value-returning call into a best-effort cleanup the teardown can run.""" + + def run() -> None: + action() + + return run + + +def assert_file_object(file: FileObject, *, provider: str) -> None: + assert file.object == "file", f"file.object={file.object!r}" + assert file.purpose == "batch", f"file.purpose={file.purpose!r}" + assert file.bytes is not None, f"file.bytes={file.bytes!r}" + if provider != "bedrock": + assert file.bytes > 0, f"file.bytes={file.bytes!r}" + assert file.status, "file.status missing" + assert ( + file.created_at is not None and file.created_at > 0 + ), "file.created_at missing" + + +def assert_batch_object(batch: BatchObject) -> None: + assert batch.object == "batch", f"batch.object={batch.object!r}" + if batch.endpoint: + assert ( + batch.endpoint == "/v1/chat/completions" + ), f"batch.endpoint={batch.endpoint!r}" + assert batch.completion_window == "24h", f"window={batch.completion_window!r}" + assert batch.input_file_id, "batch.input_file_id missing" + assert ( + batch.created_at is not None and batch.created_at > 0 + ), "batch.created_at missing" + + +@pytest.mark.parametrize("cap", CAPABILITIES, ids=[c.id for c in CAPABILITIES]) +def test_batch_lifecycle( + cap: Capability, + client: BatchClient, + resources: ResourceManager, + batch_deployments: None, +) -> None: + key = resources.key() + provider = op_provider(cap) + + file = unwrap(upload_for_scenario(client, cap, render_jsonl(cap.jsonl_model), key)) + resources.defer( + quietly(lambda: client.delete_file(file.id, key=key, provider=provider)) + ) + assert_file_object(file, provider=cap.provider) + assert matches_id_shape( + FILE_ID_SHAPE[cap.scenario], file.id + ), f"{cap.id}: file id {file.id!r} is not a {FILE_ID_SHAPE[cap.scenario]} id" + + created = create_for_scenario(client, cap, file.id, key) + require_successful_call(created) + batch = BatchObject.model_validate_json(created.body) + resources.defer( + quietly(lambda: client.cancel_batch(batch.id, key=key, provider=provider)) + ) + + assert batch.id, f"create returned no batch id (body={created.body[:200]})" + assert ( + batch.status in CREATED_BATCH_STATUSES + ), f"freshly created batch has non-transitional status {batch.status!r}" + assert_batch_object(batch) + assert matches_id_shape( + BATCH_ID_SHAPE[cap.scenario], batch.id + ), f"{cap.id}: batch id {batch.id!r} is not a {BATCH_ID_SHAPE[cap.scenario]} id" + if cap.scenario == "provider_fallback": + assert raw_id_matches_provider( + cap.provider, batch.id + ), f"{cap.provider} batch id {batch.id!r} not in that provider's native shape; misrouted?" + + fetched = unwrap(client.retrieve_batch(batch.id, key=key, provider=provider)) + assert_batch_object(fetched) + assert fetched.id == batch.id + assert ( + fetched.input_file_id == batch.input_file_id + ), "retrieve changed input_file_id" + assert fetched.status, "retrieved batch has no status" + + if cap.can_cancel: + time.sleep(BATCH_CANCEL_DELAY_SECONDS) + pre_cancel = unwrap(client.retrieve_batch(batch.id, key=key, provider=provider)) + assert ( + pre_cancel.status not in BATCH_TERMINAL_BEFORE_CANCEL + ), ( + f"batch reached {pre_cancel.status!r} before cancel; " + "provider likely rejected the input" + ) + if pre_cancel.status == "completed": + return + cancelled = cancel_batch(client, batch.id, key=key, provider=provider) + assert cancelled.id == batch.id + assert cancelled.object == "batch" + valid_post_cancel = {"cancelling", "cancelled"} + if cap.provider == "vertex_ai": + valid_post_cancel |= CREATED_BATCH_STATUSES + assert cancelled.status in valid_post_cancel, ( + f"unexpected post-cancel status {cancelled.status!r}" + ) + + if cap.can_list: + list_result = client.list_batches(key=key, provider=provider) + managed_filter_unsupported = False + match list_result: + case UnknownApiError(body=body) if ( + "Filtering by 'provider' is not supported when using managed batches" in body + ): + managed_filter_unsupported = True + listed = unwrap(client.list_batches(key=key, provider=None)) + case _: + listed = unwrap(list_result) + if listed.object is not None: + assert listed.object == "list", f"list envelope object={listed.object!r}" + match = next((b for b in listed.data if b.id == batch.id), None) + if ( + match is None + and managed_filter_unsupported + and cap.scenario == "provider_fallback" + ): + # provider_fallback keeps the provider's raw batch id (not re-encoded + # into a managed/proxy id). When the gateway rejects provider-scoped + # list, the only available list is the unfiltered managed view, which + # does not index raw provider ids. Membership cannot be asserted here; + # create + retrieve (and raw_id_matches_provider above) already pin + # routing for this scenario. + return + assert match is not None, "created batch absent from list" + assert match.object == "batch" + + +def test_batch_key_model_access_denied( + client: BatchClient, resources: ResourceManager, batch_deployments: None +) -> None: + key = resources.key(models=["openai-batch"]) + + denied_upload = client.upload_file( + content=render_jsonl("azure-batch"), + form=FileUploadForm(purpose="batch"), + model="azure-batch", + key=key, + ) + assert is_result_access_denied( + denied_upload + ), f"restricted key uploaded a file for a disallowed model: {denied_upload}" + + raw_file = unwrap( + client.upload_file( + content=render_jsonl("openai-batch"), + form=FileUploadForm(purpose="batch"), + key=key, + provider="openai", + ) + ).id + resources.defer( + quietly(lambda: client.delete_file(raw_file, key=key, provider="openai")) + ) + + denied_create = client.create_batch( + body=BatchCreateBody(input_file_id=raw_file, model="azure-batch"), key=key + ) + assert is_model_access_denied( + denied_create + ), f"restricted key created a batch for a disallowed model (status {denied_create.status_code})" + + +def test_file_upload_and_delete_outputs( + client: BatchClient, resources: ResourceManager, batch_deployments: None +) -> None: + key = resources.key() + file = unwrap( + client.upload_file( + content=render_jsonl("openai-batch"), + form=FileUploadForm(purpose="batch"), + model="openai-batch", + key=key, + ) + ) + assert_file_object(file, provider="openai") + + deleted = unwrap(client.delete_file(file.id, key=key)) + assert deleted.id, "delete response has no id" + assert deleted.object == "file", f"delete object={deleted.object!r}" + assert deleted.deleted is True, "file was not reported deleted" + + +def unattributed_rows(rows: list[SpendLogRow]) -> list[SpendLogRow]: + """Spend rows that carry no caller identity (empty api_key). + + Every request the proxy bills is stamped with the calling key. A row with no + api_key is one the proxy could not attribute; LIT-3266 is exactly this: the + batch rate limiter's internal input-file read ran without the batch's auth + metadata, landing a spend row with empty api_key/user. The symptom is not + tied to a single call_type, so this catches any unattributed row rather than + only a named file-content one. + """ + return [row for row in rows if not row.api_key] + + +def test_rate_limited_batch_create_leaves_no_unattributed_spend_row( + client: BatchClient, resources: ResourceManager, batch_deployments: None +) -> None: + """LIT-3266: creating a batch on a rate-limited key runs the batch rate + limiter, which reads the input file to count tokens (the limiter only reads + the file when the key has applicable rpm/tpm limits, so an unlimited key + hides the path). That internal read must carry the batch's auth metadata; + the reported gap was that it did not, spawning a spend-log row with empty + api_key/user. Create returning 200 is not a reliable signal (the read error + is swallowed), so this asserts the hygiene contract instead: the operation + introduces no new unattributed spend row. + + The key sets generous rpm/tpm limits (not a restrictive model allowlist) so + the file-read path fires while the batch itself is not blocked. + ``resources.key()`` cannot set limits, so the key is minted on the gateway + directly and its delete deferred. + """ + user_id = f"e2e-batch-rl-{unique_marker()}" + key = client.gateway.generate_key( + KeyGenerateBody(models=[], tpm_limit=1_000_000, rpm_limit=1_000, user_id=user_id) + ) + resources.defer(lambda: client.gateway.delete_key(key)) + + before = frozenset( + row.request_id for row in unattributed_rows(client.gateway.spend_logs(SpendLogsParams())) + ) + + file = unwrap( + client.upload_file( + content=render_jsonl("gpt-4o-mini"), + form=FileUploadForm(purpose="batch"), + model="openai-batch", + key=key, + ) + ) + resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + + created = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) + require_successful_call(created) + batch = BatchObject.model_validate_json(created.body) + resources.defer(quietly(lambda: client.cancel_batch(batch.id, key=key))) + + _ = client.gateway.poll_logs_for_key(key, min_rows=1) + + new_orphans = [ + row + for row in unattributed_rows(client.gateway.spend_logs(SpendLogsParams())) + if row.request_id not in before + ] + assert not new_orphans, ( + "batch create on a rate-limited key left an unattributed spend row " + f"(LIT-3266); rows={[(r.request_id, r.call_type, r.model) for r in new_orphans]}" + ) diff --git a/tests/e2e/bob_the_builder.py b/tests/e2e/bob_the_builder.py new file mode 100644 index 00000000000..18aff2edc98 --- /dev/null +++ b/tests/e2e/bob_the_builder.py @@ -0,0 +1,247 @@ +"""Bob the builder: on a red e2e run, ask Devin to fix the failing tests. + +Wired as a ``pytest_sessionfinish`` step (see ``conftest.py``). When the run went +red and remediation is enabled, it hands the failing tests plus their captured +tracebacks to Devin *through the LiteLLM proxy's own MCP gateway* -- the same +gateway + master key the suite already uses -- so Devin files a Linear ticket per +failure and opens fix PRs. Nothing new ships in the runner pod: the proxy already +registers the ``devin`` MCP server and holds ``DEVIN_API_KEY``, injecting it +upstream, so this process only needs the proxy key it always has. + +Opt-in via ``E2E_DEVIN_REMEDIATION=1`` so a normal local ``pytest tests/e2e`` run +never spawns a Devin session. ``DEVIN_DRY_RUN=1`` prints the prompt it would send +and makes no call. Everything is best-effort: any error here is logged and +swallowed so the run's exit status still reflects the tests, not remediation. +""" + +from __future__ import annotations + +import hashlib +import os +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import Protocol, cast + +import pytest +from pydantic import BaseModel, ConfigDict + +from e2e_config import MASTER_KEY, PROXY_BASE_URL +from e2e_http import Success +from transport import HttpTransport + +REMEDIATION_ENV = "E2E_DEVIN_REMEDIATION" +_LIST_PATH = "/mcp-rest/tools/list" +_CALL_PATH = "/mcp-rest/tools/call" + + +@dataclass(frozen=True, slots=True) +class Failure: + """One failed test: its pytest node id and the captured failure text.""" + + nodeid: str + detail: str + + +@dataclass(frozen=True, slots=True) +class Config: + server: str + create_tool: str + linear_team: str + target_repo: str + target_ref: str + max_failures: int + max_detail_chars: int + tags: tuple[str, ...] + dry_run: bool + + +class _NoParams(BaseModel): + pass + + +class _McpToolInfo(BaseModel): + model_config = ConfigDict(extra="allow") + server_name: str | None = None + alias: str | None = None + + +class _McpTool(BaseModel): + model_config = ConfigDict(extra="allow") + name: str + mcp_info: _McpToolInfo | None = None + + +class _McpToolsList(BaseModel): + model_config = ConfigDict(extra="allow") + tools: tuple[_McpTool, ...] = () + + +class _DevinSessionArgs(BaseModel): + prompt: str + title: str + tags: list[str] + + +class _ToolCallBody(BaseModel): + name: str + arguments: _DevinSessionArgs + + +class _ToolCallResult(BaseModel): + model_config = ConfigDict(extra="allow") + + +class _Report(Protocol): + @property + def nodeid(self) -> str: ... + + @property + def longreprtext(self) -> str: ... + + +class _TerminalReporter(Protocol): + stats: Mapping[str, Sequence[_Report]] + + +def _env(name: str, default: str) -> str: + value = os.environ.get(name, "").strip() + return value or default + + +def load_config() -> Config: + raw_tags = _env("DEVIN_TAGS", "e2e,stage") + return Config( + server=_env("DEVIN_MCP_SERVER", "devin"), + create_tool=_env("DEVIN_SESSION_TOOL", "devin_session_create"), + linear_team=_env("DEVIN_LINEAR_TEAM", "LIT"), + target_repo=_env("DEVIN_TARGET_REPO", "BerriAI/litellm"), + target_ref=_env("DEVIN_TARGET_REF", "litellm_internal_staging"), + max_failures=int(_env("DEVIN_MAX_FAILURES", "50")), + max_detail_chars=int(_env("DEVIN_MAX_DETAIL_CHARS", "3000")), + tags=tuple(t.strip() for t in raw_tags.split(",") if t.strip()), + dry_run=_env("DEVIN_DRY_RUN", "0") == "1", + ) + + +def collect_failures(session: pytest.Session, max_detail_chars: int) -> tuple[Failure, ...]: + """Pull the failed and errored tests (with their tracebacks) off the run's + terminal reporter. Returns empty when nothing failed or the reporter is + absent (e.g. a skipped, proxy-less session).""" + plugin: object = session.config.pluginmanager.getplugin("terminalreporter") + if plugin is None: + return () + reporter = cast(_TerminalReporter, plugin) + reports = (*reporter.stats.get("failed", ()), *reporter.stats.get("error", ())) + return tuple( + Failure(nodeid=r.nodeid, detail=r.longreprtext.strip()[-max_detail_chars:]) for r in reports + ) + + +def dedup_tag(failures: tuple[Failure, ...]) -> str: + """Stable short tag identifying this exact set of failing tests, so repeated + nightly runs on the same failures reference one body of work.""" + joined = "\n".join(sorted(f.nodeid for f in failures)) + return "e2e-fail-" + hashlib.sha256(joined.encode()).hexdigest()[:12] + + +def _revision() -> str: + for candidate in (Path(__file__).parent / ".litellm-revision", Path("/app/e2e/.litellm-revision")): + try: + return candidate.read_text(encoding="utf-8").strip() + except OSError: + continue + return _env("E2E_REVISION", "unknown") + + +def build_prompt(cfg: Config, failures: tuple[Failure, ...], tag: str) -> str: + shown = failures[: cfg.max_failures] + header = ( + f"The LiteLLM end-to-end suite failed on the " + f"{_env('E2E_ENVIRONMENT', 'stage')} proxy. Source repo {cfg.target_repo} " + f"at revision {_revision()} (branch {cfg.target_ref}). {len(failures)} " + f"test(s) failed" + + (f"; the first {len(shown)} are shown" if len(shown) < len(failures) else "") + + ".\n\n" + ) + task = ( + "For each failing test below:\n" + f"1. Open a Linear ticket under the {cfg.linear_team} team describing the " + "failure (test id, the assertion/error, likely cause), unless an open " + "ticket for that same test already exists -- do not create duplicates.\n" + f"2. Fix it in {cfg.target_repo}, branching off {cfg.target_ref} and " + "following the repo's CONTRIBUTING and CLAUDE.md conventions (meaningful " + "regression coverage, conventional commits, run the suite locally), then " + "open a PR that references the Linear ticket.\n" + "3. Prefer one focused PR per failing test; if several share a root cause, " + "group them and say so.\n" + f"Before starting, search existing sessions/PRs tagged '{tag}' or " + "referencing these test ids and continue that work instead of restarting.\n\n" + "Failing tests and their captured output:\n" + ) + blocks = [f"### {i}. {f.nodeid}\n```\n{f.detail}\n```\n" for i, f in enumerate(shown, start=1)] + return header + task + "\n".join(blocks) + + +def _resolve_tool_name(transport: HttpTransport, cfg: Config) -> str | None: + """Find Devin's create-session tool on the gateway. The proxy prefixes tools + with the server alias, so match by suffix and (when present) the owning + server.""" + result = transport.get( + _LIST_PATH, headers=transport.master, params=_NoParams(), response_type=_McpToolsList + ) + if not isinstance(result, Success): + print(f"bob_the_builder: could not list gateway MCP tools: {result}") + return None + for tool in result.data.tools: + owner = tool.mcp_info.server_name or tool.mcp_info.alias if tool.mcp_info else None + if (owner is None or owner == cfg.server) and ( + tool.name == cfg.create_tool or tool.name.endswith(cfg.create_tool) + ): + return tool.name + print( + f"bob_the_builder: no '{cfg.create_tool}' tool for server '{cfg.server}' on the gateway; " + f"saw {[t.name for t in result.data.tools]}" + ) + return None + + +def remediate(session: pytest.Session) -> None: + """Entry point called from ``pytest_sessionfinish``. No-op unless remediation + is enabled and the run actually had failures.""" + if os.environ.get(REMEDIATION_ENV) != "1": + return + cfg = load_config() + failures = collect_failures(session, cfg.max_detail_chars) + if not failures: + return + + tag = dedup_tag(failures) + title = f"Fix {len(failures)} failing LiteLLM e2e test(s) [{tag}]" + prompt = build_prompt(cfg, failures, tag) + args = _DevinSessionArgs(prompt=prompt, title=title, tags=[*cfg.tags, tag]) + + if cfg.dry_run: + print("bob_the_builder: DRY RUN -- would create a Devin session:") + print(f" server : {cfg.server}\n tool : {cfg.create_tool}\n title : {title}") + print(f" tags : {args.tags}\n---- prompt ----\n{prompt}") + return + + try: + transport = HttpTransport(base_url=PROXY_BASE_URL, master_key=MASTER_KEY) + tool_name = _resolve_tool_name(transport, cfg) + if tool_name is None: + return + result = transport.post( + _CALL_PATH, + headers=transport.master, + json=_ToolCallBody(name=tool_name, arguments=args), + response_type=_ToolCallResult, + ) + if isinstance(result, Success): + print(f"bob_the_builder: created Devin session for {len(failures)} failure(s) [{tag}]") + print(result.data.model_dump_json()) + else: + print(f"bob_the_builder: Devin session call failed: {result}") + except Exception as exc: # noqa: BLE001 - remediation must never fail the run + print(f"bob_the_builder: remediation error (ignored): {exc}") diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index cc95c7538dd..08df334d4b8 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -1,9 +1,9 @@ """Shared fixtures for all live e2e suites under tests/e2e/. -Design rule: skip on environment, fail on behavior. Live tests (marked `e2e`) -skip when no proxy answers; once a request reaches the proxy, behavior is -asserted. Pure unit coverage of the harness itself carries no `e2e` marker and -runs regardless of whether a proxy is up. +Design rule: hard failures only. Live tests (marked `e2e`) fail when no proxy +answers or when credentials/env are missing; they never skip. Pure unit coverage +of the harness itself carries no `e2e` marker and runs regardless of whether a +proxy is up. Lifecycle: the `resources` fixture maps the init -> run -> teardown contract (lifecycle.E2ECase) onto pytest - setup is init(), the test body is run(), and @@ -33,10 +33,14 @@ def pytest_configure(config: pytest.Config) -> None: "markers", "e2e: live test that requires a running proxy and real provider keys", ) + config.addinivalue_line( + "markers", + "covers(cell_id, *, exercised_on=()): coverage-registry cell(s) this test covers", + ) def _liveness_reason(label: str, base_url: str) -> str | None: - """None if `base_url` answers its liveness probe, else a skip reason.""" + """None if `base_url` answers its liveness probe, else a failure reason.""" try: resp = requests.get(f"{base_url}/health/liveliness", timeout=5) except requests.RequestException as exc: @@ -47,10 +51,10 @@ def _liveness_reason(label: str, base_url: str) -> str | None: @functools.lru_cache(maxsize=1) -def _proxy_skip_reason() -> str | None: - """Probe the proxy once per session. None if it answers, else a skip reason. In - a split deployment the management/admin control plane is a separate service, so - require it too (when it differs) - else its tests would fail rather than skip.""" +def _proxy_fail_reason() -> str | None: + """Probe the proxy once per session. None if it answers, else a failure reason. + In a split deployment the management/admin control plane is a separate service, + so require it too when it differs.""" reason = _liveness_reason("proxy", PROXY_BASE_URL) if reason is not None: return reason @@ -60,19 +64,19 @@ def _proxy_skip_reason() -> str | None: def pytest_runtest_setup(item: pytest.Item) -> None: - """Skip `e2e`-marked tests unless a proxy answers its liveness probe. Unmarked - tests (unit coverage of the harness) don't touch the proxy, so they run even - when none is up.""" + """Hard-fail `e2e`-marked tests unless a proxy answers its liveness probe. + Unmarked tests (unit coverage of the harness) don't touch the proxy, so they + run even when none is up. Never skip for a missing proxy.""" if item.get_closest_marker("e2e") is None: return - reason = _proxy_skip_reason() + reason = _proxy_fail_reason() if reason is not None: - pytest.skip(reason) + pytest.fail(reason) def pytest_runtest_call(item: pytest.Item) -> None: - """Mark that an e2e test body actually ran (not skipped at setup). Skipped - sessions never reach this hook, so the session-finish cleanup can use it as a + """Mark that an e2e test body actually ran (setup passed). Sessions that fail + setup never reach this hook, so the session-finish cleanup can use it as a guard before truncating the spend-log DB. Tests under `tests/e2e/` without the `e2e` marker (pure unit coverage for the harness itself) never hit the proxy, so they must not arm the destructive DB truncate.""" @@ -83,26 +87,33 @@ def pytest_runtest_call(item: pytest.Item) -> None: def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None: """Once the whole e2e session is done (all suites), truncate the spend logs so - the DB doesn't accumulate test rows. Skipped sessions (no live proxy, no test - actually executed) leave the DB alone so a `DATABASE_URL` pointing at a shared - instance is never wiped without an e2e run. Best-effort: a cleanup failure (no - DB reachable) must not fail the run. The spend_tracking dir goes on sys.path - only for this import and is removed after, so a broader `pytest tests/` run is - not left with a mutated path.""" + the DB doesn't accumulate test rows. Sessions where no e2e test body ran leave + the DB alone so a `DATABASE_URL` pointing at a shared instance is never wiped + without an e2e run. Best-effort: a cleanup failure (no DB reachable) must not + fail the run. The spend_tracking dir goes on sys.path only for this import and + is removed after, so a broader `pytest tests/` run is not left with a mutated + path.""" if not session.stash.get(_E2E_TEST_RAN, False): return - spend_dir = str(Path(__file__).parent / "spend_tracking") + spend_dir = str(Path(__file__).parent / "quota_management" / "spend_tracking") sys.path.insert(0, spend_dir) try: from spend_e2e_client import reset_spend_logs # pyright: ignore reset_spend_logs() except Exception as exc: # noqa: BLE001 - cleanup is best-effort - print(f"spend-log cleanup skipped: {exc}") + print(f"spend-log cleanup best-effort failed: {exc}") finally: if spend_dir in sys.path: sys.path.remove(spend_dir) + try: + from bob_the_builder import remediate + + remediate(session) + except Exception as exc: # noqa: BLE001 - remediation is best-effort + print(f"devin remediation best-effort failed: {exc}") + @pytest.fixture def resources(client: GatewayProvider) -> Iterator[ResourceManager]: diff --git a/tests/e2e/coverage_registry/README.md b/tests/e2e/coverage_registry/README.md new file mode 100644 index 00000000000..aef4c16c89a --- /dev/null +++ b/tests/e2e/coverage_registry/README.md @@ -0,0 +1,82 @@ +# e2e coverage registry + +This directory is the **denominator** for e2e test coverage: the set of behaviors we +want covered, one row per behavior, checked into the repo so coverage is a number we +can track instead of a guess. It implements the plan in the "E2E Coverage Tracking" +note; the naming grammar lives in `tests/e2e/CLAUDE.md`. + +## The model + +A **cell** is one customer-noticeable behavior a single e2e test can assert pass/fail +on, for example `llm.chat_completions.bedrock_converse.tool_use.stream.works`. Cells are +grouped `module > feature > test`, with LLM cells split into `Core LLMs` and +`Non-Core LLMs` for dashboarding. Each cell carries a tier (P0/P1/P2), a source, and a +`fail_before_fix` flag. + +The rows live in per-prefix YAML files (`llm_*.yaml`, `mgmt.yaml`, `mcp.yaml`, +`reliability.yaml`, `quota_management.yaml`, `logging.yaml`, `guardrail.yaml`, +`other.yaml`) and validate against +the discriminated union in `schema.py`, so an LLM row cannot carry a guardrail field and +vice versa. `llm` rows with `subject_endpoint` of `chat_completions`, `messages`, or +`responses` roll up to `Core LLMs`; all other LLM endpoints roll up to `Non-Core LLMs`. +LLM endpoint, route, and capability values are typed in `schema.py`, so new taxonomy +values require an explicit schema change. `logging` and `guardrail` are two id-prefixes +that roll up into the single `Logging & Guardrails` dashboard module. + +A test declares what it covers with a marker: + +```python +@pytest.mark.covers("llm.chat_completions.openai.tool_use.stream.works") +def test_openai_streaming_tool_calls(self) -> None: + ... +``` + +## The number + +`collector.py` diffs the registry against those markers and reports coverage per module. +It is static: a collect-only pass reads the markers, so it runs no test and needs no live +proxy. Whether a covered cell currently passes or fails is a separate, live concern. + +``` +cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector +``` + +Use `--format loki` after the e2e pytest run in the same Kubernetes job/pod to print +structured stdout lines for Loki: + +``` +cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector --format loki --strict +``` + +This emits exactly one `COVERAGE_TOTAL` line and one `COVERAGE_MODULE` line per module +in `MODULE_ORDER`, in that order. Loki uses log-safe `module=` labels from +`LOKI_MODULE_LABELS` (`core_llms`, `management_ui`, etc.) so existing JSON and +Prometheus consumers keep their human-readable module names unchanged. + +The headline is overall coverage. The collector also lists markers that point at ids +not in the registry, so a typo or an unenumerated behavior surfaces instead of being +silently dropped. + +Use strict mode in CI once existing draft markers are reconciled: + +``` +cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector --strict +``` + +Strict mode exits non-zero on `@pytest.mark.covers(...)` ids that are not checked into +the registry. Add `--fail-on-collection-errors` when the job should also fail on pytest +collection errors. + +## Status: this is a draft for review + +The cells were enumerated from the codebase and the tiers are a first proposal. Known +things to settle before treating the set as final: + +- tiers are proposed, not signed off; 125 P0 is a lot to prove fail-before-fix, so P0 may + want tightening +- a few cells need a support check or a prune (for example `llm.embeddings.anthropic.*` + and `reliability.perf.throughput.under_slo`) +- auth is covered in two places (`other.auth.*` and the mgmt authz assertions); the + boundary needs a decision, and the auth cluster may deserve promotion to its own module +- the P2 "niche" cells each stand in for a large tail of integrations/providers by design, + so the denominator is deliberately P0-weighted rather than a full inventory diff --git a/tests/e2e/coverage_registry/__init__.py b/tests/e2e/coverage_registry/__init__.py new file mode 100644 index 00000000000..959b3327194 --- /dev/null +++ b/tests/e2e/coverage_registry/__init__.py @@ -0,0 +1,8 @@ +"""The e2e coverage registry: the denominator for e2e test coverage. + +`schema.py` defines one validated row per customer-noticeable behavior (a "cell"). +The `*.yaml` files hold the rows, one file per id-prefix. `registry.py` loads and +validates them; `collector.py` diffs the registry against the `@pytest.mark.covers` +markers on the live tests and reports coverage per module. See tests/e2e/CLAUDE.md +for the naming grammar. +""" diff --git a/tests/e2e/coverage_registry/collector.py b/tests/e2e/coverage_registry/collector.py new file mode 100644 index 00000000000..50ef23bcb1a --- /dev/null +++ b/tests/e2e/coverage_registry/collector.py @@ -0,0 +1,306 @@ +"""Diff the registry (denominator) against the @pytest.mark.covers markers on the +live tests (numerator) and report coverage per module. + +Coverage here is static: it reads the markers via a collect-only pass, so it runs +no test and needs no live proxy. Whether a covered cell currently passes or fails +(covered_pass vs covered_fail) is a separate, live concern layered on top later. + + cd tests/e2e && PYTHONPATH=. python -m coverage_registry.collector +""" + +from __future__ import annotations + +import contextlib +import io +import json +import sys +from argparse import ArgumentParser +from dataclasses import dataclass +from pathlib import Path +from typing import Literal + +import pytest +from pydantic import BaseModel + +from .registry import load_registry +from .schema import MODULE_ORDER, Cell, Tier, dashboard_module, loki_module_label + +E2E_DIR = Path(__file__).resolve().parent.parent + + +class _CoversSink: + """Pytest plugin: after collection, capture every cell id declared via + @pytest.mark.covers(...), plus any nodes that failed to import.""" + + def __init__(self) -> None: + self.covered_ids: frozenset[str] = frozenset() + self.collection_errors: tuple[str, ...] = () + + def pytest_collection_finish(self, session: pytest.Session) -> None: + marker_args: tuple[tuple[object, ...], ...] = tuple( + marker.args + for item in session.items + for marker in item.iter_markers(name="covers") + ) + self.covered_ids = frozenset( + arg for args in marker_args for arg in args if isinstance(arg, str) + ) + + def pytest_collectreport(self, report: pytest.CollectReport) -> None: + if report.failed: + self.collection_errors = (*self.collection_errors, report.nodeid) + + +def collect_covered_ids( + e2e_dir: Path = E2E_DIR, +) -> tuple[frozenset[str], tuple[str, ...]]: + """Return (covered cell ids, nodeids that failed to import).""" + sink = _CoversSink() + with contextlib.redirect_stdout(io.StringIO()): + pytest.main( + [ + "--collect-only", + "-qq", + "--continue-on-collection-errors", + "-p", + "no:cacheprovider", + str(e2e_dir), + ], + plugins=[sink], + ) + return sink.covered_ids, sink.collection_errors + + +@dataclass(frozen=True, slots=True) +class ModuleCoverage: + module: str + total: int + covered: int + p0_total: int + p0_covered: int + + @property + def coverage_percent(self) -> float: + return _percent(self.covered, self.total) + + +@dataclass(frozen=True, slots=True) +class CoverageReport: + modules: tuple[ModuleCoverage, ...] + total: int + covered: int + p0_total: int + p0_covered: int + p0_gaps: tuple[str, ...] + orphan_markers: tuple[str, ...] + collection_errors: tuple[str, ...] + + @property + def coverage_percent(self) -> float: + return _percent(self.covered, self.total) + + +def _percent(covered: int, total: int) -> float: + return (100.0 * covered / total) if total else 0.0 + + +def _module_coverage( + module: str, cells: tuple[Cell, ...], covered: frozenset[str] +) -> ModuleCoverage: + in_module = tuple(c for c in cells if dashboard_module(c) == module) + p0 = tuple(c for c in in_module if c.tier is Tier.P0) + return ModuleCoverage( + module=module, + total=len(in_module), + covered=sum(1 for c in in_module if c.id in covered), + p0_total=len(p0), + p0_covered=sum(1 for c in p0 if c.id in covered), + ) + + +def compute_coverage( + cells: tuple[Cell, ...], + covered: frozenset[str], + collection_errors: tuple[str, ...] = (), +) -> CoverageReport: + p0_cells = tuple(c for c in cells if c.tier is Tier.P0) + registry_ids = frozenset(c.id for c in cells) + return CoverageReport( + modules=tuple(_module_coverage(m, cells, covered) for m in MODULE_ORDER), + total=len(cells), + covered=sum(1 for c in cells if c.id in covered), + p0_total=len(p0_cells), + p0_covered=sum(1 for c in p0_cells if c.id in covered), + p0_gaps=tuple(sorted(c.id for c in p0_cells if c.id not in covered)), + orphan_markers=tuple(sorted(covered - registry_ids)), + collection_errors=collection_errors, + ) + + +def _row(label: str, covered: int, total: int) -> str: + frac = f"{covered}/{total}" + return f"{label:30}{frac:>12}{_percent(covered, total):>11.1f}%" + + +def render(report: CoverageReport) -> str: + rows = tuple(_row(m.module, m.covered, m.total) for m in report.modules) + lines = ( + f"{'MODULE':30}{'COVERED':>12}{'COVERAGE':>12}", + *rows, + "-" * 54, + _row("ALL", report.covered, report.total), + "", + f"Headline coverage: {report.covered}/{report.total} ({report.coverage_percent:.1f}%)", + ) + orphans = ( + ( + f"\n{len(report.orphan_markers)} marker(s) point at ids not in the registry " + f"(reconcile: fix the marker or add the cell):\n " + + "\n ".join(report.orphan_markers), + ) + if report.orphan_markers + else () + ) + warning = ( + ( + f"\nWARNING: {len(report.collection_errors)} node(s) failed to import during " + f"collection, so coverage may undercount:\n " + + "\n ".join(report.collection_errors), + ) + if report.collection_errors + else () + ) + return "\n".join((*lines, *orphans, *warning)) + + +def _report_dict(report: CoverageReport) -> dict[str, object]: + return { + "covered": report.covered, + "total": report.total, + "coverage_percent": report.coverage_percent, + "modules": [ + { + "module": m.module, + "covered": m.covered, + "total": m.total, + "coverage_percent": m.coverage_percent, + "p0_covered": m.p0_covered, + "p0_total": m.p0_total, + } + for m in report.modules + ], + "orphan_markers": list(report.orphan_markers), + "collection_errors": list(report.collection_errors), + } + + +def render_json(report: CoverageReport) -> str: + return json.dumps(_report_dict(report), indent=2, sort_keys=True) + + +def _label_value(value: str) -> str: + return value.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") + + +def render_prometheus(report: CoverageReport) -> str: + lines = [ + "# HELP litellm_e2e_coverage_cells E2E coverage registry cells by module and state.", + "# TYPE litellm_e2e_coverage_cells gauge", + ] + for module in report.modules: + label = _label_value(module.module) + lines.append( + f'litellm_e2e_coverage_cells{{module="{label}",state="covered"}} {module.covered}' + ) + lines.append( + f'litellm_e2e_coverage_cells{{module="{label}",state="total"}} {module.total}' + ) + lines.extend( + [ + f'litellm_e2e_coverage_cells{{module="ALL",state="covered"}} {report.covered}', + f'litellm_e2e_coverage_cells{{module="ALL",state="total"}} {report.total}', + "# HELP litellm_e2e_coverage_percent E2E coverage percent by module.", + "# TYPE litellm_e2e_coverage_percent gauge", + ] + ) + for module in report.modules: + label = _label_value(module.module) + lines.append( + f'litellm_e2e_coverage_percent{{module="{label}"}} {module.coverage_percent:.6f}' + ) + lines.extend( + [ + f'litellm_e2e_coverage_percent{{module="ALL"}} {report.coverage_percent:.6f}', + "# HELP litellm_e2e_coverage_orphan_markers Coverage markers not found in the registry.", + "# TYPE litellm_e2e_coverage_orphan_markers gauge", + f"litellm_e2e_coverage_orphan_markers {len(report.orphan_markers)}", + "# HELP litellm_e2e_coverage_collection_errors Pytest nodes that failed during collection.", + "# TYPE litellm_e2e_coverage_collection_errors gauge", + f"litellm_e2e_coverage_collection_errors {len(report.collection_errors)}", + ] + ) + return "\n".join(lines) + + +def render_loki(report: CoverageReport) -> str: + lines = [ + ( + f"COVERAGE_TOTAL percent={report.coverage_percent:.1f} " + f"covered={report.covered} total={report.total}" + ) + ] + lines.extend( + ( + f"COVERAGE_MODULE module={loki_module_label(module.module)} " + f"percent={module.coverage_percent:.1f} " + f"covered={module.covered} total={module.total}" + ) + for module in report.modules + ) + return "\n".join(lines) + + +class _CliArgs(BaseModel): + format: Literal["text", "json", "prometheus", "loki"] + strict: bool + fail_on_collection_errors: bool + + +def main() -> int: + parser = ArgumentParser() + parser.add_argument( + "--format", + choices=("text", "json", "prometheus", "loki"), + default="text", + help="Output format. Use loki for structured stdout lines in the e2e job.", + ) + parser.add_argument( + "--strict", + action="store_true", + help="Exit non-zero if markers outside the registry are found.", + ) + parser.add_argument( + "--fail-on-collection-errors", + action="store_true", + help="Exit non-zero if pytest collection errors are found.", + ) + args = _CliArgs.model_validate(vars(parser.parse_args())) + cells = load_registry() + covered, errors = collect_covered_ids() + report = compute_coverage(cells, covered, errors) + output = { + "text": render, + "json": render_json, + "prometheus": render_prometheus, + "loki": render_loki, + }[args.format](report) + print(output) # noqa: T201 # CLI entrypoint output + if args.strict and report.orphan_markers: + return 1 + if args.fail_on_collection_errors and report.collection_errors: + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/e2e/coverage_registry/guardrail.yaml b/tests/e2e/coverage_registry/guardrail.yaml new file mode 100644 index 00000000000..792cbaaff7c --- /dev/null +++ b/tests/e2e/coverage_registry/guardrail.yaml @@ -0,0 +1,29 @@ +# Guardrail enforcement (behavior features). Grounded in litellm/proxy/guardrails/guardrail_hooks/. +# Rolls up into the "Logging & Guardrails" dashboard module together with logging.* +- {id: guardrail.presidio.pre_call.masks, module: guardrail, tier: P0, hook_point: pre_call, assertions: [masks], exercised_on: [chat_completions, messages], source: "guardrail_hooks/presidio.py", rationale: "PII masking pre-call; data-leak blast radius"} +- {id: guardrail.presidio.post_call.masks, module: guardrail, tier: P0, hook_point: post_call, assertions: [masks], exercised_on: [chat_completions, messages], source: "guardrail_hooks/presidio.py", rationale: "Mask PII in model output"} +- {id: guardrail.presidio.logging_only.masks, module: guardrail, tier: P0, hook_point: logging_only, assertions: [masks], exercised_on: [chat_completions, messages], source: "guardrail_hooks/presidio.py", rationale: "Redact in logs without blocking"} +- {id: guardrail.bedrock.pre_call.blocks, module: guardrail, tier: P0, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/bedrock_guardrails.py", rationale: "AWS content guardrail blocks harmful input"} +- {id: guardrail.bedrock.during.blocks, module: guardrail, tier: P0, hook_point: during, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/bedrock_guardrails.py", rationale: "During-call moderation for streaming"} +- {id: guardrail.bedrock.post_call.blocks, module: guardrail, tier: P0, hook_point: post_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/bedrock_guardrails.py", rationale: "Block harmful output"} +- {id: guardrail.lakera.pre_call.blocks, module: guardrail, tier: P0, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions, messages], source: "guardrail_hooks/lakera_ai_v2.py", rationale: "Prompt-injection block pre-execution"} +- {id: guardrail.lakera.post_call.blocks, module: guardrail, tier: P0, hook_point: post_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/lakera_ai_v2.py", rationale: "Post-call injection on multi-turn chains"} +- {id: guardrail.openai_moderations.pre_call.blocks, module: guardrail, tier: P0, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions, messages], source: "guardrail_hooks/openai/moderations.py", rationale: "Content policy for regulated industries"} +- {id: guardrail.aim.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions, messages], source: "guardrail_hooks/aim/aim.py", rationale: "Security guardrail malicious-input"} +- {id: guardrail.aim.post_call.blocks, module: guardrail, tier: P1, hook_point: post_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/aim/aim.py", rationale: "Output security check"} +- {id: guardrail.ibm_guardrails.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/ibm_guardrails/ibm_detector.py", rationale: "Enterprise multi-policy"} +- {id: guardrail.ibm_guardrails.post_call.blocks, module: guardrail, tier: P1, hook_point: post_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/ibm_guardrails/ibm_detector.py", rationale: "Output policy validation"} +- {id: guardrail.semantic_guard.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/semantic_guard", rationale: "Semantic policy compliance"} +- {id: guardrail.block_code_execution.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/block_code_execution", rationale: "Code-injection prevention"} +- {id: guardrail.tool_permission.pre_call.allows, module: guardrail, tier: P1, hook_point: pre_call, assertions: [allows], exercised_on: [chat_completions], source: "guardrail_hooks/tool_permission.py", rationale: "Grant allowed tools"} +- {id: guardrail.tool_permission.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/tool_permission.py", rationale: "Block unauthorized tools"} +- {id: guardrail.microsoft_purview.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/microsoft_purview/purview_dlp.py", rationale: "DLP sensitive-data disclosure"} +- {id: guardrail.headroom.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/headroom/headroom.py", rationale: "Anomaly detection threshold"} +- {id: guardrail.generic_guardrail_api.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/generic_guardrail_api/generic_guardrail_api.py", rationale: "Vendor-agnostic custom API"} +- {id: guardrail.pangea.pre_call.blocks, module: guardrail, tier: P1, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/pangea/pangea.py", rationale: "API security + DLP"} +- {id: guardrail.niche_providers.pre_call.blocks, module: guardrail, tier: P2, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: grammar, rationale: "SMOKE cohort: lasso/hiddenlayer/model_armor/qualifire/guardrails_ai/cato/cisco/akto/prompt_security/promptguard/zscaler/vigil/etc"} +- {id: guardrail.niche_providers.post_call.blocks, module: guardrail, tier: P2, hook_point: post_call, assertions: [blocks], exercised_on: [chat_completions], source: grammar, rationale: "SMOKE niche output filtering"} +- {id: guardrail.niche_providers.pre_call.allows, module: guardrail, tier: P2, hook_point: pre_call, assertions: [allows], exercised_on: [chat_completions], source: grammar, rationale: "SMOKE niche allow-path passthrough"} +- {id: guardrail.tool_policy.pre_call.blocks, module: guardrail, tier: P2, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/tool_policy/tool_policy_guardrail.py", rationale: "Tool-use policy enforcement"} +- {id: guardrail.mcp_security.pre_call.blocks, module: guardrail, tier: P2, hook_point: pre_call, assertions: [blocks], exercised_on: [mcp_operations], source: "guardrail_hooks/mcp_security", rationale: "MCP protocol security"} +- {id: guardrail.llm_as_a_judge.pre_call.blocks, module: guardrail, tier: P2, hook_point: pre_call, assertions: [blocks], exercised_on: [chat_completions], source: "guardrail_hooks/llm_as_a_judge", rationale: "LLM-based judgment guardrail"} diff --git a/tests/e2e/coverage_registry/llm_conversational.yaml b/tests/e2e/coverage_registry/llm_conversational.yaml new file mode 100644 index 00000000000..7360aacb916 --- /dev/null +++ b/tests/e2e/coverage_registry/llm_conversational.yaml @@ -0,0 +1,54 @@ +# LLM conversational endpoints (chat_completions, messages, responses). Grounded in proxy handlers + model_prices json. +- {id: llm.chat_completions.openai.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "proxy_server.py:8455", rationale: "Core endpoint/route/capability"} +- {id: llm.chat_completions.openai.basic.stream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: openai, capability: basic, streaming: stream, assertions: [works], source: "proxy_server.py:8455", rationale: "Core streaming"} +- {id: llm.chat_completions.openai.basic.nonstream.cost_logged, module: llm, tier: P0, subject_endpoint: chat_completions, route: openai, capability: basic, streaming: nonstream, assertions: [works, cost_logged], source: "proxy_server.py:8455", rationale: "Cost logging regression catch"} +- {id: llm.chat_completions.openai.tool_use.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: openai, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "OpenAI function_calling; high usage"} +- {id: llm.chat_completions.openai.tool_use.stream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: openai, capability: tool_use, streaming: stream, assertions: [works], source: "model_prices json", rationale: "Tool calls over streaming"} +- {id: llm.chat_completions.openai.vision.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: openai, capability: vision, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "gpt-4o vision; high usage"} +- {id: llm.chat_completions.openai.prompt_cache_5m.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: openai, capability: prompt_cache_5m, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Prompt caching cost optimization"} +- {id: llm.chat_completions.openai.service_tier.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: openai, capability: service_tier, streaming: nonstream, assertions: [works], source: "OpenAI service_tier param", rationale: "OpenAI scale-tier request option is forwarded and echoed"} +- {id: llm.chat_completions.openai.thinking.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: openai, capability: thinking, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "o-series reasoning; emerging"} +- {id: llm.chat_completions.openai.structured_output.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: openai, capability: structured_output, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "response_schema extraction"} +- {id: llm.chat_completions.anthropic.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: anthropic, capability: basic, streaming: nonstream, assertions: [works], source: "proxy_server.py:8455", rationale: "P0 route translated to Anthropic"} +- {id: llm.chat_completions.anthropic.basic.stream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: anthropic, capability: basic, streaming: stream, assertions: [works], source: "proxy_server.py:8455", rationale: "Streaming translation"} +- {id: llm.chat_completions.anthropic.tool_use.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: anthropic, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Claude tool_use; high usage"} +- {id: llm.chat_completions.anthropic.tool_use.stream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: anthropic, capability: tool_use, streaming: stream, assertions: [works], source: "model_prices json", rationale: "Streaming tool calls"} +- {id: llm.chat_completions.anthropic.vision.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: anthropic, capability: vision, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Claude vision; high usage"} +- {id: llm.chat_completions.anthropic.prompt_cache_5m.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: anthropic, capability: prompt_cache_5m, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Claude prompt caching"} +- {id: llm.chat_completions.anthropic.thinking.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: anthropic, capability: thinking, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Claude extended thinking"} +- {id: llm.chat_completions.anthropic.structured_output.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: anthropic, capability: structured_output, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Claude response_schema"} +- {id: llm.chat_completions.bedrock_converse.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: bedrock_converse, capability: basic, streaming: nonstream, assertions: [works], source: "proxy_server.py:8455", rationale: "P0 route; Bedrock Converse unified"} +- {id: llm.chat_completions.bedrock_converse.basic.stream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: bedrock_converse, capability: basic, streaming: stream, assertions: [works], source: "proxy_server.py:8455", rationale: "Streaming over Converse"} +- {id: llm.chat_completions.bedrock_converse.tool_use.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: bedrock_converse, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Converse function_calling; AWS adoption"} +- {id: llm.chat_completions.bedrock_converse.vision.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: bedrock_converse, capability: vision, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Bedrock vision (Anthropic/Nova)"} +- {id: llm.chat_completions.bedrock_converse.prompt_cache_5m.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: bedrock_converse, capability: prompt_cache_5m, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Anthropic-on-Bedrock caching"} +- {id: llm.chat_completions.bedrock_converse.thinking.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: bedrock_converse, capability: thinking, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Anthropic thinking on Bedrock"} +- {id: llm.chat_completions.vertex.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: vertex, capability: basic, streaming: nonstream, assertions: [works], source: "proxy_server.py:8455", rationale: "P0 route; Vertex AI"} +- {id: llm.chat_completions.vertex.basic.stream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: vertex, capability: basic, streaming: stream, assertions: [works], source: "proxy_server.py:8455", rationale: "Streaming over Vertex"} +- {id: llm.chat_completions.vertex.tool_use.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: vertex, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Vertex Gemini function_calling"} +- {id: llm.chat_completions.vertex.vision.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: vertex, capability: vision, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Gemini vision"} +- {id: llm.chat_completions.vertex.prompt_cache_5m.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: vertex, capability: prompt_cache_5m, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Vertex Gemini prompt caching"} +- {id: llm.chat_completions.azure_openai.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: azure_openai, capability: basic, streaming: nonstream, assertions: [works], source: "proxy_server.py:8455", rationale: "P0 route; Azure OpenAI deployments"} +- {id: llm.chat_completions.azure_openai.tool_use.nonstream.works, module: llm, tier: P0, subject_endpoint: chat_completions, route: azure_openai, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Azure OpenAI function_calling"} +- {id: llm.chat_completions.azure_foundry.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: chat_completions, route: azure_foundry, capability: basic, streaming: nonstream, assertions: [works], source: "proxy_server.py:8455", rationale: "Azure Foundry (azure_ai); newer, smoke"} +- {id: llm.messages.anthropic.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: basic, streaming: nonstream, assertions: [works], source: "anthropic_endpoints/endpoints.py:64", rationale: "Core endpoint; Anthropic Messages native"} +- {id: llm.messages.anthropic.basic.stream.works, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: basic, streaming: stream, assertions: [works], source: "anthropic_endpoints/endpoints.py:64", rationale: "Streaming Messages API"} +- {id: llm.messages.anthropic.basic.nonstream.cost_logged, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: basic, streaming: nonstream, assertions: [works, cost_logged], source: "anthropic_endpoints/endpoints.py:64", rationale: "Cost logged on passthrough"} +- {id: llm.messages.anthropic.tool_use.nonstream.works, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Tool calls via Messages API"} +- {id: llm.messages.anthropic.tool_use.stream.works, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: tool_use, streaming: stream, assertions: [works], source: "model_prices json", rationale: "Streaming tool calls"} +- {id: llm.messages.anthropic.vision.nonstream.works, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: vision, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Vision via Messages API"} +- {id: llm.messages.anthropic.prompt_cache_5m.nonstream.works, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: prompt_cache_5m, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Prompt caching via Messages API"} +- {id: llm.messages.anthropic.thinking.nonstream.works, module: llm, tier: P1, subject_endpoint: messages, route: anthropic, capability: thinking, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Extended thinking via Messages API"} +- {id: llm.responses.openai.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: responses, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "response_api_endpoints/endpoints.py:26", rationale: "Core endpoint; OpenAI Responses native"} +- {id: llm.responses.openai.basic.stream.works, module: llm, tier: P0, subject_endpoint: responses, route: openai, capability: basic, streaming: stream, assertions: [works], source: "response_api_endpoints/endpoints.py:26", rationale: "Streaming via /v1/responses"} +- {id: llm.responses.openai.basic.nonstream.cost_logged, module: llm, tier: P0, subject_endpoint: responses, route: openai, capability: basic, streaming: nonstream, assertions: [works, cost_logged], source: "response_api_endpoints/endpoints.py:26", rationale: "Cost logged on responses"} +- {id: llm.responses.openai.tool_use.nonstream.works, module: llm, tier: P0, subject_endpoint: responses, route: openai, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Tool calls via Responses API"} +- {id: llm.responses.openai.vision.nonstream.works, module: llm, tier: P0, subject_endpoint: responses, route: openai, capability: vision, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Vision via Responses API"} +- {id: llm.responses.anthropic.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: responses, route: anthropic, capability: basic, streaming: nonstream, assertions: [works], source: "response_api_endpoints/endpoints.py:26", rationale: "Responses w/ Anthropic translation (smoke)"} +- {id: llm.responses.anthropic.tool_use.nonstream.works, module: llm, tier: P1, subject_endpoint: responses, route: anthropic, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Responses tool calls w/ Anthropic"} +- {id: llm.responses.bedrock_converse.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: responses, route: bedrock_converse, capability: basic, streaming: nonstream, assertions: [works], source: "response_api_endpoints/endpoints.py:26", rationale: "Responses w/ Bedrock Converse (smoke)"} +- {id: llm.responses.bedrock_converse.tool_use.nonstream.works, module: llm, tier: P1, subject_endpoint: responses, route: bedrock_converse, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Responses tool calls w/ Converse"} +- {id: llm.responses.vertex.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: responses, route: vertex, capability: basic, streaming: nonstream, assertions: [works], source: "response_api_endpoints/endpoints.py:26", rationale: "Responses w/ Vertex (smoke)"} +- {id: llm.responses.vertex.tool_use.nonstream.works, module: llm, tier: P1, subject_endpoint: responses, route: vertex, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Responses tool calls w/ Vertex"} +- {id: llm.responses.azure_openai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: responses, route: azure_openai, capability: basic, streaming: nonstream, assertions: [works], source: "response_api_endpoints/endpoints.py:26", rationale: "Responses w/ Azure OpenAI (smoke)"} +- {id: llm.responses.azure_openai.tool_use.nonstream.works, module: llm, tier: P1, subject_endpoint: responses, route: azure_openai, capability: tool_use, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Responses tool calls w/ Azure OpenAI"} diff --git a/tests/e2e/coverage_registry/llm_nonconversational.yaml b/tests/e2e/coverage_registry/llm_nonconversational.yaml new file mode 100644 index 00000000000..b01b219476d --- /dev/null +++ b/tests/e2e/coverage_registry/llm_nonconversational.yaml @@ -0,0 +1,45 @@ +# LLM non-conversational endpoints. Grounded in litellm/proxy endpoints + llms/ handlers. +- {id: llm.embeddings.openai.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: embeddings, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_embeddings_endpoint_e2e.py:23", rationale: "Core endpoint, live vector response"} +- {id: llm.embeddings.openai.basic.nonstream.cost_logged, module: llm, tier: P0, subject_endpoint: embeddings, route: openai, capability: basic, streaming: nonstream, assertions: [cost_logged], source: "SPEND_TRACKING_COVERAGE_MATRIX.md:34", rationale: "Cost tracking on embeddings"} +- {id: llm.embeddings.azure_openai.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: embeddings, route: azure_openai, capability: basic, streaming: nonstream, assertions: [works], source: "llms/azure/azure.py", rationale: "Azure embeddings via translation"} +- {id: llm.embeddings.bedrock.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: embeddings, route: bedrock_converse, capability: basic, streaming: nonstream, assertions: [works], source: "llms/bedrock/embed/embedding.py", rationale: "Bedrock Titan embeddings"} +- {id: llm.embeddings.vertex.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: embeddings, route: vertex, capability: basic, streaming: nonstream, assertions: [works], source: "vertex_embeddings/embedding_handler.py", rationale: "Vertex embeddings"} +- {id: llm.embeddings.cohere.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: embeddings, route: cohere, capability: basic, streaming: nonstream, assertions: [works], source: "llms/cohere/embed/handler.py", rationale: "Cohere embeddings"} +- {id: llm.embeddings.anthropic.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: embeddings, route: anthropic, capability: basic, streaming: nonstream, assertions: [works], source: "llms/anthropic/chat/handler.py", rationale: "Anthropic vector API (verify support)"} +- {id: llm.batches.openai.create.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_batches_e2e.py", rationale: "Core batch create"} +- {id: llm.batches.openai.retrieve.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_batches_e2e.py", rationale: "Batch retrieve, id round-trip + status"} +- {id: llm.batches.openai.cancel.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_batches_e2e.py", rationale: "Batch cancel"} +- {id: llm.batches.openai.list.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_batches_e2e.py", rationale: "Batch list envelope"} +- {id: llm.batches.openai.file_lifecycle.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_batches_e2e.py", rationale: "File upload/retrieve/delete for batch flow"} +- {id: llm.batches.openai_encoded.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py", rationale: "Encoded scenario lifecycle"} +- {id: llm.batches.openai_unified.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py", rationale: "Unified/managed-id scenario"} +- {id: llm.batches.openai_model_param.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py", rationale: "Model-param scenario"} +- {id: llm.batches.openai_provider_fallback.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py", rationale: "Provider-fallback raw-id scenario"} +- {id: llm.batches.azure_openai.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: azure_openai, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py:98", rationale: "Azure batches all scenarios"} +- {id: llm.batches.vertex.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: vertex, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py:98", rationale: "Vertex batches"} +- {id: llm.batches.bedrock.basic.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: bedrock_converse, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py:98", rationale: "Bedrock batches (encoded/unified only)"} +- {id: llm.batches.openai.key_model_access_denied.nonstream.works, module: llm, tier: P0, subject_endpoint: batches, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_batches_e2e.py", rationale: "Key model restriction 403 on upload/create"} +- {id: llm.files.openai.upload.nonstream.works, module: llm, tier: P0, subject_endpoint: files, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "openai_files_endpoints/files_endpoints.py:46", rationale: "File upload returns OpenAIFileObject"} +- {id: llm.files.openai.retrieve.nonstream.works, module: llm, tier: P0, subject_endpoint: files, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "files_endpoints.py", rationale: "File retrieve by id"} +- {id: llm.files.openai.delete.nonstream.works, module: llm, tier: P0, subject_endpoint: files, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "files_endpoints.py", rationale: "File delete returns deleted=true"} +- {id: llm.files.openai.list.nonstream.works, module: llm, tier: P0, subject_endpoint: files, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "files_endpoints.py", rationale: "File list paginated"} +- {id: llm.files.azure_openai.upload.nonstream.works, module: llm, tier: P0, subject_endpoint: files, route: azure_openai, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py:45", rationale: "Azure file upload managed backend"} +- {id: llm.files.vertex.upload.nonstream.works, module: llm, tier: P0, subject_endpoint: files, route: vertex, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py:52", rationale: "Vertex file upload to GCS"} +- {id: llm.files.bedrock.upload.nonstream.works, module: llm, tier: P0, subject_endpoint: files, route: bedrock_converse, capability: basic, streaming: nonstream, assertions: [works], source: "batches/capabilities.py:59", rationale: "Bedrock file upload to S3"} +- {id: llm.rerank.cohere.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: rerank, route: cohere, capability: basic, streaming: nonstream, assertions: [works], source: "test_rerank_e2e.py:29", rationale: "Cohere rerank, top_n + relevance_score"} +- {id: llm.rerank.bedrock.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: rerank, route: bedrock_converse, capability: basic, streaming: nonstream, assertions: [works], source: "llms/bedrock/rerank/handler.py", rationale: "Bedrock rerank"} +- {id: llm.rerank.together_ai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: rerank, route: together_ai, capability: basic, streaming: nonstream, assertions: [works], source: "llms/together_ai/rerank/handler.py", rationale: "Together rerank"} +- {id: llm.images_generations.openai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: images_generations, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_image_generation_e2e.py:22", rationale: "OpenAI image gen, b64/url"} +- {id: llm.images_generations.azure_openai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: images_generations, route: azure_openai, capability: basic, streaming: nonstream, assertions: [works], source: "llms/azure/azure.py", rationale: "Azure DALL-E"} +- {id: llm.images_generations.vertex.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: images_generations, route: vertex, capability: basic, streaming: nonstream, assertions: [works], source: "vertex_ai/image_generation/image_generation_handler.py", rationale: "Vertex Imagen"} +- {id: llm.images_generations.bedrock.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: images_generations, route: bedrock_converse, capability: basic, streaming: nonstream, assertions: [works], source: "bedrock/image_generation/image_handler.py", rationale: "Bedrock Titan Image"} +- {id: llm.images_generations.black_forest_labs.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: images_generations, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "black_forest_labs/image_generation/handler.py", rationale: "BFL Flux via OpenAI-compat"} +- {id: llm.audio_speech.openai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: audio_speech, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "test_audio_speech_e2e.py:22", rationale: "OpenAI TTS binary audio"} +- {id: llm.audio_speech.openai.basic.stream.works, module: llm, tier: P1, subject_endpoint: audio_speech, route: openai, capability: basic, streaming: stream, assertions: [works], source: "proxy_server.py:9043", rationale: "TTS streaming chunk generator"} +- {id: llm.audio_speech.azure_openai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: audio_speech, route: azure_openai, capability: basic, streaming: nonstream, assertions: [works], source: "llms/azure/azure.py", rationale: "Azure TTS"} +- {id: llm.audio_speech.vertex.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: audio_speech, route: vertex, capability: basic, streaming: nonstream, assertions: [works], source: "vertex_ai/text_to_speech/text_to_speech_handler.py", rationale: "Vertex TTS"} +- {id: llm.audio_transcriptions.openai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: audio_transcriptions, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "openai/transcriptions/handler.py", rationale: "OpenAI Whisper"} +- {id: llm.audio_transcriptions.azure_openai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: audio_transcriptions, route: azure_openai, capability: basic, streaming: nonstream, assertions: [works], source: "azure/audio_transcriptions.py", rationale: "Azure STT"} +- {id: llm.audio_transcriptions.soniox.basic.nonstream.works, module: llm, tier: P2, subject_endpoint: audio_transcriptions, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "soniox/audio_transcription/handler.py", rationale: "Soniox via OpenAI-compat (smoke)"} +- {id: llm.audio_transcriptions.nvidia_riva.basic.nonstream.works, module: llm, tier: P2, subject_endpoint: audio_transcriptions, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "nvidia_riva/audio_transcription/handler.py", rationale: "NVIDIA Riva (smoke)"} +- {id: llm.moderations.openai.basic.nonstream.works, module: llm, tier: P1, subject_endpoint: moderations, route: openai, capability: basic, streaming: nonstream, assertions: [works], source: "proxy_server.py", rationale: "OpenAI moderations (only provider)"} diff --git a/tests/e2e/coverage_registry/logging.yaml b/tests/e2e/coverage_registry/logging.yaml new file mode 100644 index 00000000000..65ab8f0096f --- /dev/null +++ b/tests/e2e/coverage_registry/logging.yaml @@ -0,0 +1,25 @@ +# Logging integration delivery (behavior features). Grounded in litellm/integrations/. +- {id: logging.langfuse.success.logs_spend, module: logging, tier: P0, event: success, assertions: [logs_spend], exercised_on: [chat_completions, messages, embeddings], source: "integrations/langfuse/langfuse.py", rationale: "Primary tracing backend; cost accuracy"} +- {id: logging.langfuse.failure.logs_spend, module: logging, tier: P0, event: failure, assertions: [logs_spend], exercised_on: [chat_completions, messages], source: "integrations/langfuse/langfuse.py", rationale: "Failure path must still track spend"} +- {id: logging.langfuse.stream.logs_spend, module: logging, tier: P0, event: stream, assertions: [logs_spend], exercised_on: [chat_completions, messages], source: "integrations/langfuse/langfuse.py", rationale: "Streaming token counts aggregate"} +- {id: logging.s3.success.writes_object, module: logging, tier: P0, event: success, assertions: [writes_object], exercised_on: [chat_completions, messages, embeddings], source: "integrations/s3_v2.py", rationale: "Primary audit trail; batch flush no-drop"} +- {id: logging.s3.failure.writes_object, module: logging, tier: P0, event: failure, assertions: [writes_object], exercised_on: [chat_completions, messages], source: "integrations/s3_v2.py", rationale: "Failed calls persisted for compliance"} +- {id: logging.gcs_bucket.success.writes_object, module: logging, tier: P0, event: success, assertions: [writes_object], exercised_on: [chat_completions, messages, embeddings], source: "integrations/gcs_bucket/gcs_bucket.py", rationale: "GCS parallel to S3"} +- {id: logging.datadog.success.exports_metric, module: logging, tier: P0, event: success, assertions: [exports_metric], exercised_on: [chat_completions, messages, embeddings], source: "integrations/datadog/datadog.py", rationale: "Powers dashboards/alerts; cardinality regressions common"} +- {id: logging.datadog.failure.exports_metric, module: logging, tier: P0, event: failure, assertions: [exports_metric], exercised_on: [chat_completions], source: "integrations/datadog/datadog.py", rationale: "Failure metrics for alerting/SLO"} +- {id: logging.prometheus.success.exports_metric, module: logging, tier: P0, event: success, assertions: [exports_metric], exercised_on: [chat_completions, messages, embeddings], source: "integrations/prometheus.py", rationale: "Standard OSS metrics; per-key cardinality (existing e2e)"} +- {id: logging.otel.success.exports_metric, module: logging, tier: P0, event: success, assertions: [exports_metric], exercised_on: [chat_completions, messages, embeddings], source: "integrations/otel/logger.py", rationale: "OTEL spans on every call path"} +- {id: logging.otel.failure.exports_metric, module: logging, tier: P0, event: failure, assertions: [exports_metric], exercised_on: [chat_completions, messages], source: "integrations/otel/logger.py", rationale: "Error spans for observability continuity"} +- {id: logging.braintrust.success.logs_spend, module: logging, tier: P1, event: success, assertions: [logs_spend], exercised_on: [chat_completions, messages], source: "integrations/braintrust_logging.py", rationale: "Evals platform spend"} +- {id: logging.langsmith.success.logs_spend, module: logging, tier: P1, event: success, assertions: [logs_spend], exercised_on: [chat_completions, messages], source: "integrations/langsmith.py", rationale: "LangChain ecosystem"} +- {id: logging.arize.success.logs_spend, module: logging, tier: P1, event: success, assertions: [logs_spend], exercised_on: [chat_completions, embeddings], source: "integrations/arize/arize.py", rationale: "ML-ops observability"} +- {id: logging.mlflow.success.logs_spend, module: logging, tier: P1, event: success, assertions: [logs_spend], exercised_on: [chat_completions], source: "integrations/mlflow.py", rationale: "Experiment tracking cost/run"} +- {id: logging.opik.success.logs_spend, module: logging, tier: P1, event: success, assertions: [logs_spend], exercised_on: [chat_completions], source: "integrations/opik/opik.py", rationale: "Eval platform spend/case"} +- {id: logging.openmeter.success.exports_metric, module: logging, tier: P1, event: success, assertions: [exports_metric], exercised_on: [chat_completions, messages, embeddings], source: "integrations/openmeter.py", rationale: "Usage metering for billing"} +- {id: logging.literal_ai.success.logs_spend, module: logging, tier: P1, event: success, assertions: [logs_spend], exercised_on: [chat_completions, messages], source: "integrations/literal_ai.py", rationale: "Tracing platform spend"} +- {id: logging.posthog.success.exports_metric, module: logging, tier: P1, event: success, assertions: [exports_metric], exercised_on: [chat_completions, messages], source: "integrations/posthog.py", rationale: "Product analytics batching"} +- {id: logging.azure_storage.success.writes_object, module: logging, tier: P1, event: success, assertions: [writes_object], exercised_on: [chat_completions, messages], source: "integrations/azure_storage/azure_storage.py", rationale: "Azure blob for enterprise"} +- {id: logging.cloudzero.success.logs_spend, module: logging, tier: P1, event: success, assertions: [logs_spend], exercised_on: [chat_completions], source: "integrations/cloudzero/cloudzero.py", rationale: "Cost ops correlation"} +- {id: logging.focus.success.writes_object, module: logging, tier: P1, event: success, assertions: [writes_object], exercised_on: [chat_completions, messages], source: "integrations/focus/focus_logger.py", rationale: "Cost mgmt multi-destination export"} +- {id: logging.niche_integrations.success.logs_spend, module: logging, tier: P2, event: success, assertions: [logs_spend], exercised_on: [chat_completions], source: grammar, rationale: "SMOKE cohort: athina/galileo/deepeval/langtrace/weave/lunary/humanloop/traceloop/helicone/argilla/newrelic/sqs/supabase/dynamodb/agentops/lago/etc"} +- {id: logging.niche_integrations.failure.logs_spend, module: logging, tier: P2, event: failure, assertions: [logs_spend], exercised_on: [chat_completions], source: grammar, rationale: "SMOKE niche failure path"} diff --git a/tests/e2e/coverage_registry/mcp.yaml b/tests/e2e/coverage_registry/mcp.yaml new file mode 100644 index 00000000000..d477b257cb0 --- /dev/null +++ b/tests/e2e/coverage_registry/mcp.yaml @@ -0,0 +1,113 @@ +# MCP module. Grounded in litellm/proxy/_experimental/mcp_server/. See tests/e2e/CLAUDE.md for the grammar. +- id: mcp.list_tools.api_key.succeeds + module: mcp + tier: P0 + operation: list_tools + auth_family: api_key + assertions: [succeeds] + source: "server.py:637" + rationale: Core operation; most common auth path; high usage +- id: mcp.list_tools.api_key.denied_without_permission + module: mcp + tier: P0 + operation: list_tools + auth_family: api_key + assertions: [denied_without_permission] + source: "mcp_server_manager.py:1409" + rationale: Permission guard is high blast-radius; multi-tenant safety +- id: mcp.call_tool.api_key.succeeds + module: mcp + tier: P0 + operation: call_tool + auth_family: api_key + assertions: [succeeds] + source: "server.py:849" + rationale: Primary operation; customer-critical; high usage +- id: mcp.call_tool.api_key.denied_without_permission + module: mcp + tier: P0 + operation: call_tool + auth_family: api_key + assertions: [denied_without_permission] + source: "rest_endpoints.py:305-386" + rationale: Tool-level permission guard; multi-tenant safety +- id: mcp.list_tools.bearer.succeeds + module: mcp + tier: P1 + operation: list_tools + auth_family: bearer + assertions: [succeeds] + source: "server.py:662" + rationale: OAuth/bearer token flow; upstream delegation +- id: mcp.call_tool.bearer.succeeds + module: mcp + tier: P1 + operation: call_tool + auth_family: bearer + assertions: [succeeds] + source: "server.py:886" + rationale: Bearer token forwarding for tool invocation +- id: mcp.list_tools.oauth.succeeds + module: mcp + tier: P1 + operation: list_tools + auth_family: oauth + assertions: [succeeds] + source: "rest_endpoints.py:138-188" + rationale: Interactive OAuth2 flow; live token management +- id: mcp.call_tool.oauth.succeeds + module: mcp + tier: P1 + operation: call_tool + auth_family: oauth + assertions: [succeeds] + source: "db.py user_oauth_credential lookup" + rationale: OAuth2 token passthrough; per-user credential storage +- id: mcp.list_tools.none.succeeds + module: mcp + tier: P1 + operation: list_tools + auth_family: none + assertions: [succeeds] + source: "mcp_server_manager.py:1485-1492" + rationale: Public/anonymous servers; delegate_auth_to_upstream +- id: mcp.call_tool.none.succeeds + module: mcp + tier: P1 + operation: call_tool + auth_family: none + assertions: [succeeds] + source: "rest_endpoints.py:305-334" + rationale: No upstream auth required; demo servers +- id: mcp.get_prompt.api_key.succeeds + module: mcp + tier: P1 + operation: get_prompt + auth_family: api_key + assertions: [succeeds] + source: "server.py:1042" + rationale: Prompt op; same auth stack as tools +- id: mcp.read_resource.api_key.succeeds + module: mcp + tier: P1 + operation: read_resource + auth_family: api_key + assertions: [succeeds] + source: "server.py:1177" + rationale: Resource op; same permission model as tools +- id: mcp.list_prompts.api_key.succeeds + module: mcp + tier: P2 + operation: list_prompts + auth_family: api_key + assertions: [succeeds] + source: "server.py:993" + rationale: Smoke-level; same auth stack as list_tools +- id: mcp.list_resources.api_key.succeeds + module: mcp + tier: P2 + operation: list_resources + auth_family: api_key + assertions: [succeeds] + source: "server.py:1089" + rationale: Smoke; rarely used; same auth model as tools diff --git a/tests/e2e/coverage_registry/mgmt.yaml b/tests/e2e/coverage_registry/mgmt.yaml new file mode 100644 index 00000000000..4fbaac0a205 --- /dev/null +++ b/tests/e2e/coverage_registry/mgmt.yaml @@ -0,0 +1,68 @@ +# Management/UI endpoint features. Grounded in litellm/proxy/management_endpoints/. +- {id: mgmt.key.generate.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "key_management_endpoints.py:1444", rationale: "API key survives DB roundtrip"} +- {id: mgmt.key.generate.admin_only, module: mgmt, tier: P0, surface: api, assertions: [admin_only], source: "key_management_endpoints.py:1444", rationale: "Only master/team-admin creates keys"} +- {id: mgmt.key.generate.happy_path, module: mgmt, tier: P0, surface: ui, assertions: [happy_path], source: "ui_sso.py:420", rationale: "SSO-driven key gen (UI path)"} +- {id: mgmt.key.update.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "key_management_endpoints.py:2462", rationale: "Budget/model changes persist"} +- {id: mgmt.key.update.admin_only, module: mgmt, tier: P0, surface: api, assertions: [admin_only], source: "key_management_endpoints.py:2462", rationale: "Non-admin cannot escalate perms"} +- {id: mgmt.key.update.happy_path, module: mgmt, tier: P1, surface: ui, assertions: [happy_path], source: "key_management_endpoints.py:2462", rationale: "Key edit through the dashboard"} +- {id: mgmt.key.delete.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "key_management_endpoints.py:3122", rationale: "Deletion revokes future calls"} +- {id: mgmt.key.delete.admin_only, module: mgmt, tier: P0, surface: api, assertions: [admin_only], source: "key_management_endpoints.py:3122", rationale: "Non-owner cannot delete"} +- {id: mgmt.key.info.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "key_management_endpoints.py:3380", rationale: "Info reflects all writes"} +- {id: mgmt.team.new.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "team_endpoints.py:897", rationale: "team_id/alias/budgets stored"} +- {id: mgmt.team.new.admin_only, module: mgmt, tier: P0, surface: api, assertions: [admin_only], source: "team_endpoints.py:897", rationale: "Only org-admin/master creates teams"} +- {id: mgmt.team.member_add.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "team_endpoints.py:2424", rationale: "Membership + per-member budget persist"} +- {id: mgmt.team.member_add.member_forbidden, module: mgmt, tier: P0, surface: api, assertions: [member_forbidden], source: "team_endpoints.py:2424", rationale: "Non-admin forbidden to add"} +- {id: mgmt.team.member_delete.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "team_endpoints.py:2800", rationale: "Removal revokes team key access"} +- {id: mgmt.team.member_delete.member_forbidden, module: mgmt, tier: P0, surface: api, assertions: [member_forbidden], source: "team_endpoints.py:2800", rationale: "Non-admin forbidden to remove"} +- {id: mgmt.budget.new.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "budget_management_endpoints.py:40", rationale: "max/soft/reset windows persist"} +- {id: mgmt.budget.new.admin_only, module: mgmt, tier: P0, surface: api, assertions: [admin_only], source: "budget_management_endpoints.py:40", rationale: "Requires master/admin"} +- {id: mgmt.model.add.persists, module: mgmt, tier: P0, surface: api, assertions: [persists], source: "model_management_endpoints.py:1201", rationale: "Registration persists for routing"} +- {id: mgmt.model.add.admin_only, module: mgmt, tier: P0, surface: api, assertions: [admin_only], source: "model_management_endpoints.py:1201", rationale: "Non-admin cannot inject model config"} +- {id: mgmt.user.new.happy_path, module: mgmt, tier: P0, surface: api, assertions: [happy_path], source: "internal_user_endpoints.py:360", rationale: "User creation full cycle"} +- {id: mgmt.key.list.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "key_management_endpoints.py:5119", rationale: "Key inventory pagination"} +- {id: mgmt.key.block.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "key_management_endpoints.py:5849", rationale: "Blocked stays blocked on restart"} +- {id: mgmt.key.unblock.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "key_management_endpoints.py:5960", rationale: "Unblock restores access"} +- {id: mgmt.key.regenerate.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "key_management_endpoints.py:6071", rationale: "Rotation: new works, old invalid"} +- {id: mgmt.key.health.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "key_management_endpoints.py:4292", rationale: "Key health endpoint"} +- {id: mgmt.key.bulk_update.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "key_management_endpoints.py:2677", rationale: "Batch key updates"} +- {id: mgmt.team.update.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "team_endpoints.py:1582", rationale: "Metadata/budget updates persist"} +- {id: mgmt.team.delete.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "team_endpoints.py:1750", rationale: "Deletion prevents key access"} +- {id: mgmt.team.block.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "team_endpoints.py", rationale: "Block suspends all members"} +- {id: mgmt.team.info.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "team_endpoints.py:2244", rationale: "Metadata+members+budgets"} +- {id: mgmt.team.list.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "team_endpoints.py:3645", rationale: "Pagination/filtering"} +- {id: mgmt.team.member_update.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "team_endpoints.py:2768", rationale: "Member budget/role updates persist"} +- {id: mgmt.user.update.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "internal_user_endpoints.py:555", rationale: "Metadata/perm updates persist"} +- {id: mgmt.user.delete.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "internal_user_endpoints.py:640", rationale: "Deletion revokes keys+teams"} +- {id: mgmt.user.list.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "internal_user_endpoints.py:475", rationale: "Admin view all users"} +- {id: mgmt.user.info.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "internal_user_endpoints.py:440", rationale: "Roles/perms/team membership"} +- {id: mgmt.organization.new.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "organization_endpoints.py:403", rationale: "Org for multi-tenant isolation"} +- {id: mgmt.organization.update.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "organization_endpoints.py:545", rationale: "Org metadata updates persist"} +- {id: mgmt.organization.delete.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "organization_endpoints.py:710", rationale: "Cascades to teams/keys"} +- {id: mgmt.organization.member_add.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "organization_endpoints.py:835", rationale: "Org member onboarding"} +- {id: mgmt.customer.new.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "customer_endpoints.py:372", rationale: "End-user for spend tracking"} +- {id: mgmt.customer.delete.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "customer_endpoints.py:480", rationale: "Removes from spend tracking"} +- {id: mgmt.end_user.new.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "customer_endpoints.py:730", rationale: "End-user create (synonym)"} +- {id: mgmt.tag.new.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "tag_management_endpoints.py:160", rationale: "Tag for spend categorization"} +- {id: mgmt.tag.list.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "tag_management_endpoints.py:315", rationale: "Tag enumeration"} +- {id: mgmt.tag.delete.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "tag_management_endpoints.py:390", rationale: "Stops future tagging"} +- {id: mgmt.model.update.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "model_management_endpoints.py:1358", rationale: "Pricing/concurrency persist"} +- {id: mgmt.model.delete.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "model_management_endpoints.py:1045", rationale: "Removes from registry"} +- {id: mgmt.model.block.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "model_management_endpoints.py", rationale: "Blocked model stays blocked"} +- {id: mgmt.access_group.new.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "model_access_group_management_endpoints.py:450", rationale: "Model permissioning group"} +- {id: mgmt.access_group.info.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "model_access_group_management_endpoints.py:600", rationale: "Access group membership query"} +- {id: mgmt.mcp_server.register.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "mcp_management_endpoints.py:880", rationale: "MCP server registration"} +- {id: mgmt.mcp_server.approve.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "mcp_management_endpoints.py:1200", rationale: "Admin approval persists"} +- {id: mgmt.budget.update.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "budget_management_endpoints.py:155", rationale: "Limit changes apply"} +- {id: mgmt.budget.delete.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "budget_management_endpoints.py:280", rationale: "Clears limits"} +- {id: mgmt.budget.list.happy_path, module: mgmt, tier: P1, surface: api, assertions: [happy_path], source: "budget_management_endpoints.py:215", rationale: "Budget enumeration"} +- {id: mgmt.callback.list.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "callback_management_endpoints.py", rationale: "Callback config (smoke)"} +- {id: mgmt.cache_settings.update.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "cache_settings_endpoints.py", rationale: "Cache config (smoke)"} +- {id: mgmt.cost_tracking.estimate.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "cost_tracking_settings.py", rationale: "Cost estimate (smoke)"} +- {id: mgmt.router_settings.update.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "router_settings_endpoints.py", rationale: "Router config (smoke)"} +- {id: mgmt.jwt_key_mapping.new.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "jwt_key_mapping_endpoints.py", rationale: "JWT->key mapping (smoke)"} +- {id: mgmt.compliance.gdpr.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "compliance_endpoints.py", rationale: "GDPR ops (smoke)"} +- {id: mgmt.tool_management.list.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "tool_management_endpoints.py", rationale: "Tool inventory (smoke)"} +- {id: mgmt.fallback_management.update.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "fallback_management_endpoints.py", rationale: "Fallback config (smoke)"} +- {id: mgmt.config_override.hashicorp_vault.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "config_override_endpoints.py", rationale: "Vault integration (smoke)"} +- {id: mgmt.workflow.list.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "workflow_management_endpoints.py", rationale: "Workflow tracking (smoke)"} +- {id: mgmt.credential_migration.check.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "key_management_endpoints.py:4252", rationale: "Encryption migration (smoke)"} diff --git a/tests/e2e/coverage_registry/other.yaml b/tests/e2e/coverage_registry/other.yaml new file mode 100644 index 00000000000..c2efecec677 --- /dev/null +++ b/tests/e2e/coverage_registry/other.yaml @@ -0,0 +1,28 @@ +# Other (holding pen). Grounded in litellm/proxy/auth/ + health_endpoints/ + proxy_server.py. +# PROMOTION NOTE: the auth cluster (~14 cells) is a candidate to promote to its own module once stable. +- {id: other.auth.master_key.valid_allows, module: other, tier: P0, area: auth, assertions: [valid_allows], source: "user_api_key_auth.py:1569-1588", rationale: "Master key authenticates; timing-safe compare"} +- {id: other.auth.master_key.invalid_denied, module: other, tier: P0, area: auth, assertions: [invalid_denied], source: "user_api_key_auth.py:1580", rationale: "Invalid master key rejected"} +- {id: other.auth.jwt.valid_token_allows, module: other, tier: P0, area: auth, assertions: [valid_token_allows], source: "handle_jwt.py:77-150", rationale: "Valid JWT with correct issuer + claims grants access"} +- {id: other.auth.jwt.expired_denied, module: other, tier: P0, area: auth, assertions: [expired_denied], source: "handle_jwt.py:125-135", rationale: "Expired JWT rejected even with valid signature"} +- {id: other.auth.jwt.invalid_signature_denied, module: other, tier: P0, area: auth, assertions: [invalid_signature_denied], source: "handle_jwt.py:145-150", rationale: "Bad/missing signature fails verification"} +- {id: other.auth.virtual_key.route_permission_enforced, module: other, tier: P0, area: auth, assertions: [route_permission_enforced], source: "route_checks.py:89-151", rationale: "allowed_routes whitelist denies disallowed routes"} +- {id: other.auth.virtual_key.route_group_allowed, module: other, tier: P1, area: auth, assertions: [route_group_allowed], source: "route_checks.py:106-128", rationale: "allowed_routes=[llm_api_routes] grants all LLM endpoints"} +- {id: other.auth.passthrough.model_allowlist_enforced, module: other, tier: P1, area: auth, assertions: [model_allowlist_enforced], source: "route_checks.py:135-151", rationale: "Passthrough enforces per-key model allow-lists"} +- {id: other.auth.oauth2.token_valid_allows, module: other, tier: P1, area: auth, assertions: [token_valid_allows], source: "oauth2_check.py:15-73", rationale: "OAuth2 introspection grants active token"} +- {id: other.auth.oauth2.token_invalid_denied, module: other, tier: P1, area: auth, assertions: [token_invalid_denied], source: "oauth2_check.py:37-73", rationale: "Expired/inactive OAuth2 token denied"} +- {id: other.auth.ip_allowlist.internal_ip_allows, module: other, tier: P1, area: auth, assertions: [internal_ip_allows], source: "ip_address_utils.py:54-76", rationale: "Internal CIDR bypasses public-API restriction"} +- {id: other.auth.ip_allowlist.external_ip_denied_to_private, module: other, tier: P1, area: auth, assertions: [external_ip_denied_to_private], source: "ip_address_utils.py:54-76", rationale: "External IP cannot reach internal-only resources"} +- {id: other.lifecycle.readiness.public_probe, module: other, tier: P0, area: lifecycle, assertions: [public_probe], source: "_health_endpoints.py:1551-1570", rationale: "Unauthenticated /health/readiness safe for LBs"} +- {id: other.lifecycle.readiness.reports_db_status, module: other, tier: P0, area: lifecycle, assertions: [reports_db_status], source: "_health_endpoints.py:1551-1570", rationale: "readiness distinguishes healthy vs DB-unreachable"} +- {id: other.lifecycle.readiness.shutting_down_returns_503, module: other, tier: P0, area: lifecycle, assertions: [shutting_down_returns_503], source: "_health_endpoints.py:1554-1556", rationale: "Graceful shutdown drains LB via 503"} +- {id: other.lifecycle.readiness_details.authenticated_diagnostics, module: other, tier: P1, area: lifecycle, assertions: [authenticated_diagnostics], source: "_health_endpoints.py:1574-1584", rationale: "Auth'd details expose cache/callback status"} +- {id: other.lifecycle.liveness.ping, module: other, tier: P1, area: lifecycle, assertions: [ping], source: "_health_endpoints.py:134-155", rationale: "Liveness confirms server responding"} +- {id: other.lifecycle.startup.config_loads, module: other, tier: P0, area: lifecycle, assertions: [config_loads], source: "proxy_server.py:4020-4100", rationale: "Startup loads YAML, resolves env, persists to DB"} +- {id: other.lifecycle.startup.env_vars_resolved, module: other, tier: P1, area: lifecycle, assertions: [env_vars_resolved], source: "proxy_server.py:3984-4010", rationale: "os.environ/ refs resolved at startup"} +- {id: other.lifecycle.background_health_check.interval_configurable, module: other, tier: P1, area: lifecycle, assertions: [interval_configurable], source: "proxy_server.py:3245-3310", rationale: "Background checks run at configurable interval"} +- {id: other.config.runtime_update.applies_at_runtime, module: other, tier: P0, area: config, assertions: [applies_at_runtime], source: "proxy_server.py:14014-14060", rationale: "/config/update persists to DB + invalidates cache"} +- {id: other.config.general_settings.alert_webhook_side_effect, module: other, tier: P1, area: config, assertions: [alert_webhook_side_effect], source: "proxy_server.py:14215", rationale: "alert_to_webhook_url auto-enables slack alerting"} +- {id: other.config.secret_resolution.kms_integration, module: other, tier: P1, area: config, assertions: [kms_integration], source: "proxy_server.py:3984-4010", rationale: "Resolves secrets from Vault/KMS at startup"} +- {id: other.config.overrides.audit_logged, module: other, tier: P1, area: config, assertions: [audit_logged], source: "config_override_endpoints.py:67-100", rationale: "Config override mutations audit-logged, values redacted"} +- {id: other.key_mgmt.regenerate.grace_period_honored, module: other, tier: P1, area: auth, assertions: [grace_period_honored], source: "key_management_endpoints.py:4503-4560", rationale: "Old key valid during grace_period then revoked"} +- {id: other.key_mgmt.spend_reset.resets_to_value, module: other, tier: P1, area: auth, assertions: [resets_to_value], source: "key_management_endpoints.py:4841", rationale: "reset_spend resets accumulated spend"} diff --git a/tests/e2e/coverage_registry/quota_management.yaml b/tests/e2e/coverage_registry/quota_management.yaml new file mode 100644 index 00000000000..fac266149f4 --- /dev/null +++ b/tests/e2e/coverage_registry/quota_management.yaml @@ -0,0 +1,35 @@ +# Quota Management (behavior features): rate limits, budgets, spend tracking. Grounded in +# litellm/proxy/hooks/ + litellm/proxy/auth/auth_checks.py + litellm/proxy/spend_tracking/. +- {id: quota_management.ratelimit.rpm.blocks_over_limit, module: quota_management, tier: P0, behavior: ratelimit, variant: rpm, assertions: [blocks_over_limit], exercised_on: [chat_completions, messages], source: "parallel_request_limiter_v3.py", rationale: "v3 limiter enforces RPM per key/team/model; 429 on breach"} +- {id: quota_management.ratelimit.tpm.blocks_over_limit, module: quota_management, tier: P0, behavior: ratelimit, variant: tpm, assertions: [blocks_over_limit], exercised_on: [chat_completions, messages], source: "parallel_request_limiter_v3.py", rationale: "v3 limiter enforces TPM per key/team/model; 429 on breach"} +- {id: quota_management.ratelimit.rpm.resets_after_window, module: quota_management, tier: P1, behavior: ratelimit, variant: rpm, assertions: [resets_after_window], exercised_on: [chat_completions], source: "parallel_request_limiter_v3.py", rationale: "Rate-limit window (LITELLM_RATE_LIMIT_WINDOW_SIZE, 60s default) expires; a blocked key serves again in the next window"} +- {id: quota_management.ratelimit.rpm.headers_report_remaining, module: quota_management, tier: P1, behavior: ratelimit, variant: rpm, assertions: [headers_report_remaining], exercised_on: [chat_completions], source: "parallel_request_limiter_v3.py async_post_call_success_hook", rationale: "Successful responses carry x-ratelimit-api_key-{limit,remaining}-{requests,tokens} so clients can pace"} +- {id: quota_management.ratelimit.priority_generous.picks_under_tpm, module: quota_management, tier: P1, behavior: ratelimit, variant: priority_generous, assertions: [picks_under_tpm], exercised_on: [chat_completions, messages], source: "dynamic_rate_limiter_v3.py:36-52", rationale: "Generous mode (<80% sat) allows priority borrowing"} +- {id: quota_management.ratelimit.priority_strict.picks_under_tpm, module: quota_management, tier: P1, behavior: ratelimit, variant: priority_strict, assertions: [picks_under_tpm], exercised_on: [chat_completions, messages], source: "dynamic_rate_limiter_v3.py:53-71", rationale: "Strict mode (>=80% sat) enforces priority fairness"} +- {id: quota_management.budget.key.blocks_over_limit, module: quota_management, tier: P0, behavior: budget, variant: key, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "A key's max_budget blocks further paid calls once spend crosses it"} +- {id: quota_management.budget.internal_user.blocks_over_limit, module: quota_management, tier: P1, behavior: budget, variant: internal_user, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "An internal user's max_budget governs personal keys"} +- {id: quota_management.budget.end_user.blocks_over_limit, module: quota_management, tier: P1, behavior: budget, variant: end_user, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "A customer (end-user) max_budget blocks calls attributed via user="} +- {id: quota_management.budget.organization.blocks_over_limit, module: quota_management, tier: P1, behavior: budget, variant: organization, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "An organization's max_budget blocks keys under its teams"} +- {id: quota_management.budget.team_member.blocks_over_limit, module: quota_management, tier: P1, behavior: budget, variant: team_member, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "A member's per-team budget blocks independently of the team budget"} +- {id: quota_management.budget.tag.blocks_over_limit, module: quota_management, tier: P1, behavior: budget, variant: tag, assertions: [blocks_over_limit], exercised_on: [chat_completions], source: "router_strategy/budget_limiter.py", rationale: "Proxy-level tag budgets block tagged requests at the cap"} +- {id: quota_management.budget.model_max.isolates_per_model, module: quota_management, tier: P1, behavior: budget, variant: model_max, assertions: [isolates_per_model], exercised_on: [chat_completions], source: "proxy/hooks/model_max_budget_limiter.py", rationale: "model_max_budget caps one model without touching a sibling's budget"} +- {id: quota_management.budget.soft.alerts_without_blocking, module: quota_management, tier: P1, behavior: budget, variant: soft, assertions: [alerts_without_blocking], exercised_on: [chat_completions], source: "proxy/auth/auth_checks.py", rationale: "soft_budget alerts but never blocks traffic"} +- {id: quota_management.budget.key.resets_after_window, module: quota_management, tier: P1, behavior: budget, variant: key, assertions: [resets_after_window], exercised_on: [chat_completions], source: "proxy/common_utils/reset_budget_job.py", rationale: "budget_duration zeroes key spend after the window; a blocked key serves again"} +- {id: quota_management.budget.team_member.resets_after_window, module: quota_management, tier: P1, behavior: budget, variant: team_member, assertions: [resets_after_window], exercised_on: [chat_completions], source: "proxy/common_utils/reset_budget_job.py", rationale: "Member per-team budget reset keeps advancing window after window"} +- {id: quota_management.budget.key_multi_window.blocks_then_resets, module: quota_management, tier: P1, behavior: budget, variant: key_multi_window, assertions: [blocks_then_resets], exercised_on: [chat_completions], source: "proxy/common_utils/reset_budget_job.py", rationale: "budget_limits enforce within a short window and serve again in the next"} +- {id: quota_management.budget.key_multi_window.resets_windows_independently, module: quota_management, tier: P2, behavior: budget, variant: key_multi_window, assertions: [resets_windows_independently], exercised_on: [chat_completions], source: "proxy/common_utils/reset_budget_job.py", rationale: "Each window of a multi-window budget resets on its own schedule"} +- {id: quota_management.budget.team_multi_window.blocks_then_resets, module: quota_management, tier: P1, behavior: budget, variant: team_multi_window, assertions: [blocks_then_resets], exercised_on: [chat_completions], source: "proxy/common_utils/reset_budget_job.py", rationale: "Team budget_limits enforce and reset per window"} +- {id: quota_management.budget.fallback.routes_to_fallback, module: quota_management, tier: P1, behavior: budget, variant: fallback, assertions: [routes_to_fallback], exercised_on: [messages], source: "proxy/hooks/model_max_budget_limiter.py", rationale: "budget_fallbacks reroute to the fallback model once the primary's budget is exhausted"} +- {id: quota_management.budget.spend_counter.reseed_matches_db, module: quota_management, tier: P2, behavior: budget, variant: spend_counter, assertions: [reseed_matches_db], exercised_on: [chat_completions], source: "proxy/spend_tracking/budget_reservation.py", rationale: "Concurrent cold-counter reseeds keep the enforcement counter equal to DB spend (#26829)"} +- {id: quota_management.spend_tracking.chat_completions.logs_cost, module: quota_management, tier: P0, behavior: spend_tracking, variant: chat_completions, assertions: [logs_cost], exercised_on: [chat_completions], source: "proxy/spend_tracking/spend_tracking_utils.py", rationale: "A paid chat call writes a nonzero spend row"} +- {id: quota_management.spend_tracking.stream.logs_cost, module: quota_management, tier: P1, behavior: spend_tracking, variant: stream, assertions: [logs_cost], exercised_on: [chat_completions], source: "proxy/spend_tracking/spend_tracking_utils.py", rationale: "Streaming responses aggregate token counts into a spend row"} +- {id: quota_management.spend_tracking.embeddings.logs_cost, module: quota_management, tier: P1, behavior: spend_tracking, variant: embeddings, assertions: [logs_cost], exercised_on: [embeddings], source: "proxy/spend_tracking/spend_tracking_utils.py", rationale: "Embedding calls write nonzero spend rows"} +- {id: quota_management.spend_tracking.cache_hit.zero_cost, module: quota_management, tier: P1, behavior: spend_tracking, variant: cache_hit, assertions: [zero_cost], exercised_on: [chat_completions], source: "proxy/spend_tracking/spend_tracking_utils.py", rationale: "A response-cache hit logs at zero cost with the cache-hit marker"} +- {id: quota_management.spend_tracking.key_rollup.matches_sum_of_logs, module: quota_management, tier: P1, behavior: spend_tracking, variant: key_rollup, assertions: [matches_sum_of_logs], exercised_on: [chat_completions], source: "proxy/db/db_spend_update_writer.py", rationale: "A key's rolled-up spend equals the sum of its log rows"} +- {id: quota_management.spend_tracking.concurrent_burst.loses_no_spend, module: quota_management, tier: P1, behavior: spend_tracking, variant: concurrent_burst, assertions: [loses_no_spend], exercised_on: [chat_completions], source: "proxy/db/db_spend_update_writer.py", rationale: "Concurrent calls all land as spend; no row lost to write contention"} +- {id: quota_management.spend_tracking.tags.attributes_spend, module: quota_management, tier: P1, behavior: spend_tracking, variant: tags, assertions: [attributes_spend], exercised_on: [chat_completions], source: "proxy/spend_tracking/spend_tracking_utils.py", rationale: "Request tags round-trip to spend rows and tag rollups match tagged logs"} +- {id: quota_management.spend_tracking.end_user.attributes_spend, module: quota_management, tier: P1, behavior: spend_tracking, variant: end_user, assertions: [attributes_spend], exercised_on: [chat_completions], source: "proxy/spend_tracking/spend_tracking_utils.py", rationale: "user= attribution lands the end-user id on the spend row"} +- {id: quota_management.spend_tracking.per_model.writes_own_rows, module: quota_management, tier: P2, behavior: spend_tracking, variant: per_model, assertions: [writes_own_rows], exercised_on: [chat_completions], source: "proxy/spend_tracking/spend_tracking_utils.py", rationale: "Each model on a shared key gets its own spend row"} +- {id: quota_management.spend_tracking.failure.writes_failure_row, module: quota_management, tier: P1, behavior: spend_tracking, variant: failure, assertions: [writes_failure_row], exercised_on: [chat_completions], source: "proxy/spend_tracking/spend_log_error_logger.py", rationale: "A failed call writes a failure-status spend row"} +- {id: quota_management.spend_tracking.spend_calculate.returns_cost, module: quota_management, tier: P2, behavior: spend_tracking, variant: spend_calculate, assertions: [returns_cost], exercised_on: [spend_calculate], source: "proxy/spend_tracking/spend_management_endpoints.py", rationale: "/spend/calculate prices a hypothetical request at nonzero cost"} +- {id: quota_management.spend_tracking.pagination.keeps_total, module: quota_management, tier: P2, behavior: spend_tracking, variant: pagination, assertions: [keeps_total], exercised_on: [chat_completions], source: "proxy/spend_tracking/spend_management_endpoints.py", rationale: "Spend-logs v2 pagination caps page size without losing the total"} diff --git a/tests/e2e/coverage_registry/registry.py b/tests/e2e/coverage_registry/registry.py new file mode 100644 index 00000000000..baeea080a9d --- /dev/null +++ b/tests/e2e/coverage_registry/registry.py @@ -0,0 +1,29 @@ +"""Load and validate the registry: the denominator, built in one shot from the YAMLs.""" + +from __future__ import annotations + +from collections import Counter +from pathlib import Path + +import yaml +from pydantic import TypeAdapter + +from .schema import Cell + +REGISTRY_DIR = Path(__file__).resolve().parent + +_CELLS_ADAPTER: TypeAdapter[tuple[Cell, ...]] = TypeAdapter(tuple[Cell, ...]) + + +def _load_cells(path: Path) -> tuple[Cell, ...]: + return _CELLS_ADAPTER.validate_python(yaml.safe_load(path.read_text()) or ()) + + +def load_registry(registry_dir: Path = REGISTRY_DIR) -> tuple[Cell, ...]: + """Every cell across every `*.yaml`, validated. Raises on a schema violation or + a duplicate id, since either would corrupt the coverage denominator.""" + cells = tuple(cell for path in sorted(registry_dir.glob("*.yaml")) for cell in _load_cells(path)) + duplicates = sorted(cid for cid, n in Counter(c.id for c in cells).items() if n > 1) + if duplicates: + raise ValueError(f"duplicate cell ids in registry: {duplicates}") + return cells diff --git a/tests/e2e/coverage_registry/reliability.yaml b/tests/e2e/coverage_registry/reliability.yaml new file mode 100644 index 00000000000..3746b029331 --- /dev/null +++ b/tests/e2e/coverage_registry/reliability.yaml @@ -0,0 +1,26 @@ +# Reliability & Performance (behavior features). Grounded in litellm/router.py + router_strategy/ + router_utils/. +- {id: reliability.fallback.5xx.routes_to_fallback, module: reliability, tier: P0, behavior: fallback, variant: "5xx", assertions: [routes_to_fallback], exercised_on: [chat_completions, messages], source: "litellm/router.py:2024", rationale: "Reroute on provider 5xx to alternate deployment"} +- {id: reliability.fallback.context_window.routes_to_fallback, module: reliability, tier: P0, behavior: fallback, variant: context_window, assertions: [routes_to_fallback], exercised_on: [chat_completions, messages], source: "litellm/router.py:6108", rationale: "Fallback when model exceeds context limit"} +- {id: reliability.fallback.content_policy.routes_to_fallback, module: reliability, tier: P0, behavior: fallback, variant: content_policy, assertions: [routes_to_fallback], exercised_on: [chat_completions, messages], source: "litellm/router.py:6023", rationale: "Reroute on content-policy violation"} +- {id: reliability.fallback.timeout.routes_to_fallback, module: reliability, tier: P0, behavior: fallback, variant: "timeout", assertions: [routes_to_fallback], exercised_on: [chat_completions, messages], source: "litellm/router.py:2766", rationale: "Fallback on request timeout"} +- {id: reliability.retry.5xx.succeeds_within_retries, module: reliability, tier: P0, behavior: retry, variant: "5xx", assertions: [succeeds_within_retries], exercised_on: [chat_completions, messages], source: "litellm/router.py:6414", rationale: "Transient 5xx often succeeds on retry"} +- {id: reliability.retry.timeout.succeeds_within_retries, module: reliability, tier: P0, behavior: retry, variant: timeout, assertions: [succeeds_within_retries], exercised_on: [chat_completions, messages], source: "get_retry_from_policy.py:44", rationale: "Timeout retried per policy"} +- {id: reliability.retry.429.succeeds_within_retries, module: reliability, tier: P0, behavior: retry, variant: "429", assertions: [succeeds_within_retries], exercised_on: [chat_completions, messages], source: "get_retry_from_policy.py:46", rationale: "429 retried per RateLimitErrorRetries policy"} +- {id: reliability.retry.auth.succeeds_within_retries, module: reliability, tier: P1, behavior: retry, variant: auth, assertions: [succeeds_within_retries], exercised_on: [chat_completions, messages], source: "get_retry_from_policy.py:42", rationale: "Transient auth glitch retry"} +- {id: reliability.retry.context_window.succeeds_within_retries, module: reliability, tier: P1, behavior: retry, variant: context_window, assertions: [succeeds_within_retries], exercised_on: [chat_completions, messages], source: "get_retry_from_policy.py:51", rationale: "Multi-attempt on context error"} +- {id: reliability.cooldown.5xx.trips_then_recovers, module: reliability, tier: P0, behavior: cooldown, variant: "5xx", assertions: [trips_then_recovers], exercised_on: [chat_completions, messages], source: "cooldown_handlers.py:40", rationale: "Deployment cools after repeated 5xx, recovers after cooldown_time"} +- {id: reliability.cooldown.429.trips_then_recovers, module: reliability, tier: P0, behavior: cooldown, variant: "429", assertions: [trips_then_recovers], exercised_on: [chat_completions, messages], source: "cooldown_handlers.py:69", rationale: "Cools on 429, avoids hammering exhausted provider"} +- {id: reliability.cooldown.auth.trips_then_recovers, module: reliability, tier: P1, behavior: cooldown, variant: auth, assertions: [trips_then_recovers], exercised_on: [chat_completions, messages], source: "cooldown_handlers.py:74", rationale: "Cools on 401 auth error"} +- {id: reliability.cooldown.timeout.trips_then_recovers, module: reliability, tier: P1, behavior: cooldown, variant: timeout, assertions: [trips_then_recovers], exercised_on: [chat_completions, messages], source: "cooldown_handlers.py:77", rationale: "Cools on 408 timeout"} +- {id: reliability.routing.simple_shuffle.picks_healthy_deployment, module: reliability, tier: P1, behavior: routing, variant: simple_shuffle, assertions: [picks_healthy_deployment], exercised_on: [chat_completions, messages], source: "router_strategy/simple_shuffle.py", rationale: "Baseline weighted/uniform pick"} +- {id: reliability.routing.latency_based.picks_lowest_latency, module: reliability, tier: P1, behavior: routing, variant: latency_based, assertions: [picks_lowest_latency], exercised_on: [chat_completions, messages], source: "router_strategy/lowest_latency.py", rationale: "Routes to lowest-latency deployment"} +- {id: reliability.routing.cost_based.picks_lowest_cost, module: reliability, tier: P1, behavior: routing, variant: cost_based, assertions: [picks_lowest_cost], exercised_on: [chat_completions, messages], source: "router_strategy/lowest_cost.py", rationale: "Spend-aware routing"} +- {id: reliability.routing.usage_based.picks_under_tpm, module: reliability, tier: P0, behavior: routing, variant: usage_based, assertions: [picks_under_tpm], exercised_on: [chat_completions, messages], source: "router_strategy/lowest_tpm_rpm_v2.py", rationale: "Routes to lowest-TPM deployment; prevents over-allocation"} +- {id: reliability.routing.least_busy.picks_lowest_traffic, module: reliability, tier: P1, behavior: routing, variant: least_busy, assertions: [picks_lowest_traffic], exercised_on: [chat_completions, messages], source: "router_strategy/least_busy.py", rationale: "Fewest in-flight requests"} +- {id: reliability.cache.exact.returns_cached, module: reliability, tier: P1, behavior: cache, variant: exact, assertions: [returns_cached], exercised_on: [chat_completions, messages, embeddings], source: "litellm/caching/caching.py", rationale: "Response cache returns cached on exact match"} +- {id: reliability.cache.prompt_caching_model_select.returns_cached, module: reliability, tier: P1, behavior: cache, variant: prompt_caching_model_select, assertions: [returns_cached], exercised_on: [chat_completions], source: "router_utils/prompt_caching_cache.py", rationale: "Selects model supporting prompt caching for cacheable prefix"} +- {id: reliability.circuit_breaker.redis.trips_then_recovers, module: reliability, tier: P0, behavior: circuit_breaker, variant: redis, assertions: [trips_then_recovers], exercised_on: [chat_completions, messages, embeddings], source: "litellm/caching/redis_cache.py:99", rationale: "Redis breaker CLOSED->OPEN->HALF_OPEN; guards all cache/rate-limit ops"} +- {id: reliability.timeout.request_timeout.exceeds_deadline, module: reliability, tier: P1, behavior: timeout, variant: request_timeout, assertions: [exceeds_deadline], exercised_on: [chat_completions, messages], source: "litellm/router.py:545-551", rationale: "Per-request timeout raises Timeout"} +- {id: reliability.timeout.stream_timeout.exceeds_deadline, module: reliability, tier: P1, behavior: timeout, variant: stream_timeout, assertions: [exceeds_deadline], exercised_on: [chat_completions], source: "litellm/router.py:551", rationale: "Streaming chunk-delivery timeout"} +- {id: reliability.perf.latency.under_slo, module: reliability, tier: P1, behavior: perf, variant: latency, assertions: [under_slo], exercised_on: [chat_completions, messages], source: "router_strategy/lowest_latency.py", rationale: "Latency SLO (p50/p99) compliance"} +- {id: reliability.perf.throughput.under_slo, module: reliability, tier: P1, behavior: perf, variant: throughput, assertions: [under_slo], exercised_on: [chat_completions, messages], source: grammar, rationale: "Throughput SLO under load"} diff --git a/tests/e2e/coverage_registry/schema.py b/tests/e2e/coverage_registry/schema.py new file mode 100644 index 00000000000..21be0acb18f --- /dev/null +++ b/tests/e2e/coverage_registry/schema.py @@ -0,0 +1,193 @@ +"""Registry row schema: the contract every denominator cell validates against. + +A cell is one customer-noticeable behavior a single e2e test can assert pass/fail +on. `module` is the id's segment-1 prefix (eight of them); dashboard rollups can +split or merge those prefixes. The union is discriminated on `module`, so an LLM +row cannot carry a guardrail field and vice versa. +""" + +from __future__ import annotations + +from enum import Enum +from typing import Annotated, Literal + +from pydantic import BaseModel, ConfigDict, Field, TypeAdapter + + +class Tier(str, Enum): + P0 = "P0" + P1 = "P1" + P2 = "P2" + + +class FailBeforeFix(str, Enum): + proven = "proven" + unproven = "unproven" + + +LlmEndpoint = Literal[ + "chat_completions", + "messages", + "responses", + "embeddings", + "batches", + "files", + "rerank", + "images_generations", + "audio_speech", + "audio_transcriptions", + "moderations", + "realtime", +] + +LlmRoute = Literal[ + "anthropic", + "azure_foundry", + "azure_openai", + "bedrock_converse", + "cohere", + "openai", + "together_ai", + "vertex", +] + +LlmCapability = Literal[ + "basic", + "prompt_cache_5m", + "service_tier", + "structured_output", + "thinking", + "tool_use", + "vision", +] + + +class _Base(BaseModel): + model_config = ConfigDict(frozen=True, extra="forbid") + + id: str + tier: Tier + assertions: tuple[str, ...] + source: str + rationale: str = "" + fail_before_fix: FailBeforeFix = FailBeforeFix.unproven + supported: bool = True + + +class LlmCell(_Base): + module: Literal["llm"] + subject_endpoint: LlmEndpoint + route: LlmRoute + capability: LlmCapability + streaming: Literal["stream", "nonstream", "na"] + + +class MgmtCell(_Base): + module: Literal["mgmt"] + surface: Literal["api", "ui"] + + +class McpCell(_Base): + module: Literal["mcp"] + operation: str + auth_family: Literal["none", "api_key", "bearer", "oauth"] + + +class ReliabilityCell(_Base): + module: Literal["reliability"] + behavior: str + variant: str + exercised_on: tuple[str, ...] + + +class QuotaCell(_Base): + module: Literal["quota_management"] + behavior: Literal["ratelimit", "budget", "spend_tracking"] + variant: str + exercised_on: tuple[str, ...] + + +class LoggingCell(_Base): + module: Literal["logging"] + event: str + exercised_on: tuple[str, ...] + + +class GuardrailCell(_Base): + module: Literal["guardrail"] + hook_point: str + exercised_on: tuple[str, ...] + + +class OtherCell(_Base): + module: Literal["other"] + area: str + + +Cell = Annotated[ + LlmCell + | MgmtCell + | McpCell + | ReliabilityCell + | QuotaCell + | LoggingCell + | GuardrailCell + | OtherCell, + Field(discriminator="module"), +] + +CELL_ADAPTER: TypeAdapter[Cell] = TypeAdapter(Cell) + +CORE_LLM_ENDPOINTS: frozenset[str] = frozenset( + { + "chat_completions", + "messages", + "responses", + } +) + +PREFIX_ROLLUP: dict[str, str] = { + "mcp": "MCPs", + "mgmt": "Management/UI", + "reliability": "Reliability & Performance", + "quota_management": "Quota Management", + "logging": "Logging & Guardrails", + "guardrail": "Logging & Guardrails", + "other": "Other", +} + +MODULE_ORDER: tuple[str, ...] = ( + "Core LLMs", + "Non-Core LLMs", + "MCPs", + "Management/UI", + "Reliability & Performance", + "Quota Management", + "Logging & Guardrails", + "Other", +) + +LOKI_MODULE_LABELS: dict[str, str] = { + "Core LLMs": "core_llms", + "Non-Core LLMs": "non_core_llms", + "MCPs": "mcp", + "Management/UI": "management_ui", + "Reliability & Performance": "reliability_performance", + "Quota Management": "quota_management", + "Logging & Guardrails": "logging_guardrails", + "Other": "other", +} + + +def dashboard_module(cell: Cell) -> str: + """Return the Grafana/reporting module for a registry cell.""" + if isinstance(cell, LlmCell): + if cell.subject_endpoint in CORE_LLM_ENDPOINTS: + return "Core LLMs" + return "Non-Core LLMs" + return PREFIX_ROLLUP[cell.module] + + +def loki_module_label(module: str) -> str: + """Return the log-safe Loki label for a dashboard module.""" + return LOKI_MODULE_LABELS[module] diff --git a/tests/e2e/coverage_registry/test_collector.py b/tests/e2e/coverage_registry/test_collector.py new file mode 100644 index 00000000000..079ee215866 --- /dev/null +++ b/tests/e2e/coverage_registry/test_collector.py @@ -0,0 +1,194 @@ +"""Tests for the coverage-registry tooling: pure logic plus a registry canary. + +No `e2e` marker, so these run without a proxy. They exercise the coverage math and +the registry loader, and guard the checked-in registry against schema drift and +duplicate ids. +""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from coverage_registry.collector import ( + compute_coverage, + render, + render_json, + render_loki, + render_prometheus, +) +from coverage_registry.registry import load_registry +from coverage_registry.schema import ( + GuardrailCell, + LlmCell, + LlmEndpoint, + LoggingCell, + Tier, + loki_module_label, +) + + +def _llm( + cell_id: str, tier: Tier, subject_endpoint: LlmEndpoint = "chat_completions" +) -> LlmCell: + return LlmCell( + id=cell_id, + module="llm", + tier=tier, + assertions=("works",), + source="test", + subject_endpoint=subject_endpoint, + route="openai", + capability="basic", + streaming="nonstream", + ) + + +def test_compute_coverage_counts_covered_p0_and_gaps() -> None: + cells = (_llm("llm.a", Tier.P0), _llm("llm.b", Tier.P0), _llm("llm.c", Tier.P1)) + report = compute_coverage(cells, frozenset({"llm.a"})) + assert (report.total, report.covered) == (3, 1) + assert (report.p0_total, report.p0_covered) == (2, 1) + assert report.p0_gaps == ("llm.b",) + assert report.orphan_markers == () + + +def test_orphan_marker_is_reported_not_counted() -> None: + cells = (_llm("llm.a", Tier.P0),) + report = compute_coverage(cells, frozenset({"llm.a", "llm.ghost"})) + assert report.covered == 1 + assert report.orphan_markers == ("llm.ghost",) + + +def test_logging_and_guardrail_roll_up_into_one_module() -> None: + cells = ( + LoggingCell( + id="logging.x", + module="logging", + tier=Tier.P0, + assertions=("logs_spend",), + source="t", + event="success", + exercised_on=("chat_completions",), + ), + GuardrailCell( + id="guardrail.y", + module="guardrail", + tier=Tier.P1, + assertions=("blocks",), + source="t", + hook_point="pre_call", + exercised_on=("chat_completions",), + ), + ) + report = compute_coverage(cells, frozenset()) + logging_and_guardrails = next( + m for m in report.modules if m.module == "Logging & Guardrails" + ) + assert logging_and_guardrails.total == 2 + + +def test_llm_cells_roll_up_by_core_endpoint() -> None: + cells = ( + _llm("llm.chat", Tier.P0, "chat_completions"), + _llm("llm.messages", Tier.P0, "messages"), + _llm("llm.responses", Tier.P1, "responses"), + _llm("llm.batches", Tier.P0, "batches"), + _llm("llm.realtime", Tier.P1, "realtime"), + ) + report = compute_coverage(cells, frozenset({"llm.chat", "llm.batches"})) + + core = next(m for m in report.modules if m.module == "Core LLMs") + non_core = next(m for m in report.modules if m.module == "Non-Core LLMs") + + assert (core.total, core.covered, core.p0_total, core.p0_covered) == (3, 1, 2, 1) + assert ( + non_core.total, + non_core.covered, + non_core.p0_total, + non_core.p0_covered, + ) == (2, 1, 1, 1) + + +def test_text_render_uses_plain_coverage_language() -> None: + report = compute_coverage( + (_llm("llm.chat", Tier.P0), _llm("llm.batches", Tier.P0, "batches")), + frozenset({"llm.chat"}), + ) + + text = render(report) + + assert "COVERAGE" in text + assert "Headline coverage: 1/2 (50.0%)" in text + assert "P0 COVERED" not in text + + +def test_json_render_exposes_module_coverage_for_grafana_jobs() -> None: + report = compute_coverage( + (_llm("llm.chat", Tier.P0), _llm("llm.batches", Tier.P0, "batches")), + frozenset({"llm.chat"}), + ) + + payload = render_json(report) + + assert '"coverage_percent": 50.0' in payload + assert '"module": "Core LLMs"' in payload + assert '"module": "Non-Core LLMs"' in payload + + +def test_prometheus_render_exposes_module_coverage_timeseries() -> None: + report = compute_coverage( + (_llm("llm.chat", Tier.P0), _llm("llm.batches", Tier.P0, "batches")), + frozenset({"llm.chat"}), + ) + + metrics = render_prometheus(report) + + assert 'litellm_e2e_coverage_cells{module="Core LLMs",state="covered"} 1' in metrics + assert 'litellm_e2e_coverage_percent{module="Core LLMs"} 100.000000' in metrics + assert 'litellm_e2e_coverage_percent{module="Non-Core LLMs"} 0.000000' in metrics + assert "litellm_e2e_coverage_orphan_markers 0" in metrics + + +def test_loki_render_exposes_exact_stdout_lines_for_loki() -> None: + report = compute_coverage( + (_llm("llm.chat", Tier.P0), _llm("llm.batches", Tier.P0, "batches")), + frozenset({"llm.chat"}), + ) + + lines = render_loki(report).splitlines() + + assert len(lines) == 1 + len(report.modules) + assert lines[0] == "COVERAGE_TOTAL percent=50.0 covered=1 total=2" + assert ( + lines[1] == "COVERAGE_MODULE module=core_llms percent=100.0 covered=1 total=1" + ) + assert ( + lines[2] == "COVERAGE_MODULE module=non_core_llms percent=0.0 covered=0 total=1" + ) + assert [line.split("module=", 1)[1].split(" ", 1)[0] for line in lines[1:]] == [ + loki_module_label(module.module) for module in report.modules + ] + assert all( + " " not in line.split("module=", 1)[1].split(" ", 1)[0] for line in lines[1:] + ) + + +def test_real_registry_loads_and_ids_are_unique() -> None: + cells = load_registry() + ids = [c.id for c in cells] + assert len(cells) > 250 + assert len(ids) == len(set(ids)) + assert any(c.id == "logging.prometheus.success.exports_metric" for c in cells) + + +def test_load_registry_rejects_duplicate_ids(tmp_path: Path) -> None: + row = ( + "- {id: llm.dup, module: llm, tier: P0, assertions: [works], source: t, " + "subject_endpoint: chat_completions, route: openai, capability: basic, streaming: nonstream}\n" + ) + (tmp_path / "a.yaml").write_text(row) + (tmp_path / "b.yaml").write_text(row) + with pytest.raises(ValueError, match="duplicate cell ids"): + load_registry(tmp_path) diff --git a/tests/e2e/docker-compose.yml b/tests/e2e/docker-compose.yml new file mode 100644 index 00000000000..0cfbb5b0b66 --- /dev/null +++ b/tests/e2e/docker-compose.yml @@ -0,0 +1,116 @@ +# local setup to run e2e tests +configs: + litellm_config: + content: | + general_settings: + master_key: os.environ/LITELLM_MASTER_KEY + database_url: os.environ/DATABASE_URL + store_prompts_in_spend_logs: true + proxy_budget_rescheduler_min_time: 5 + proxy_budget_rescheduler_max_time: 10 + + litellm_settings: + drop_params: true + num_retries: 3 + request_timeout: 600 + cache: true + cache_params: + type: redis + host: redis + port: 6379 + + router_settings: + routing_strategy: simple-shuffle + num_retries: 3 + allowed_fails: 5 + cooldown_time: 30 + fallbacks: + - gemini-2.5-flash: ["gpt-5.5", "claude-haiku-4-5"] + + finetune_settings: + - custom_llm_provider: openai + api_key: os.environ/OPENAI_API_KEY + + files_settings: + - custom_llm_provider: openai + api_key: os.environ/OPENAI_API_KEY + - custom_llm_provider: azure + api_base: os.environ/AZURE_API_BASE + api_key: os.environ/AZURE_API_KEY + api_version: "2024-05-01-preview" + + model_list: + - model_name: gpt-5.5 + litellm_params: + model: openai/gpt-5.5 + api_key: os.environ/OPENAI_API_KEY + + - model_name: claude-haiku-4-5 + litellm_params: + model: anthropic/claude-haiku-4-5 + api_key: os.environ/ANTHROPIC_API_KEY + + - model_name: gemini-2.5-flash + litellm_params: + model: gemini/gemini-2.5-flash + api_key: os.environ/GEMINI_API_KEY + + - model_name: openai-text-embedding-3-small + litellm_params: + model: openai/text-embedding-3-small + api_key: os.environ/OPENAI_API_KEY + +services: + litellm: + image: ghcr.io/berriai/litellm:main-latest + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + env_file: .env + environment: + LITELLM_MASTER_KEY: sk-1234 + DATABASE_URL: postgresql://litellm:litellm@db:5432/litellm + UI_USERNAME: admin + UI_PASSWORD: sk-1234 + AWS_S3_BUCKET_NAME: ${AWS_S3_BUCKET_NAME:-${AWS_BATCH_S3_BUCKET:-}} + AWS_BATCH_S3_BUCKET: ${AWS_BATCH_S3_BUCKET:-${AWS_S3_BUCKET_NAME:-}} + AWS_BATCH_ROLE_ARN: ${AWS_BATCH_ROLE_ARN:-} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-} + AWS_REGION: ${AWS_REGION:-us-east-1} + GCS_BUCKET_NAME: ${GCS_BUCKET_NAME:-} + VERTEXAI_PROJECT: ${VERTEXAI_PROJECT:-} + VERTEXAI_CREDENTIALS: ${VERTEXAI_CREDENTIALS:-} + GOOGLE_APPLICATION_CREDENTIALS: ${GOOGLE_APPLICATION_CREDENTIALS:-} + MISTRAL_API_KEY: ${MISTRAL_API_KEY:-} + AZURE_API_BASE: ${AZURE_API_BASE:-} + AZURE_API_KEY: ${AZURE_API_KEY:-} + ports: + - "4000:4000" + configs: + - source: litellm_config + target: /app/config.yaml + command: ["--config", "/app/config.yaml", "--port", "4000"] + +# throwaway db + db: + image: postgres:16 + environment: + POSTGRES_USER: litellm + POSTGRES_PASSWORD: litellm + POSTGRES_DB: litellm + healthcheck: + test: ["CMD-SHELL", "pg_isready -U litellm"] + interval: 3s + timeout: 3s + retries: 20 + + redis: + image: redis:7 + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 3s + timeout: 3s + retries: 20 diff --git a/tests/e2e/e2e_config.py b/tests/e2e/e2e_config.py index 3865804b08f..75bd715a23a 100644 --- a/tests/e2e/e2e_config.py +++ b/tests/e2e/e2e_config.py @@ -21,6 +21,9 @@ CONTROL_PLANE_BASE_URL = os.environ.get( "LITELLM_CONTROL_PLANE_URL", PROXY_BASE_URL ).rstrip("/") +UI_USERNAME = os.environ.get("E2E_UI_USERNAME", "admin") +UI_PASSWORD = os.environ.get("E2E_UI_PASSWORD", MASTER_KEY) + # Writes on the proxy are eventually consistent (e.g. spend rows flush on # proxy_batch_write_at, ~60s). Read-backs poll to this deadline, never sleep-once. POLL_TIMEOUT = float(os.environ.get("E2E_POLL_TIMEOUT", "120")) diff --git a/tests/e2e/e2e_gateway.py b/tests/e2e/e2e_gateway.py index a67ea594a71..05f83ecc085 100644 --- a/tests/e2e/e2e_gateway.py +++ b/tests/e2e/e2e_gateway.py @@ -9,6 +9,7 @@ Gateway's key/customer methods for cleanup. Read-backs are eventually consistent from __future__ import annotations import time +import warnings from collections.abc import Callable from dataclasses import dataclass @@ -18,6 +19,7 @@ from e2e_http import ( Result, StreamingResponse, Success, + is_ok, unwrap, ) from models import ( @@ -26,14 +28,24 @@ from models import ( CustomerDeleteBody, EmbedBody, EmbedResponse, + FileListResponse, + FineTuningJobsParams, + FineTuningJobsResponse, KeyDeleteBody, KeyGenerateBody, KeyGenerateResponse, KeyInfo, KeyInfoParams, KeyInfoResponse, + LiteLLMParamsBody, + ModelDeleteBody, + ModelInfoBody, ModelInfoEntry, ModelInfoResponse, + ModelMode, + ModelNewBody, + ModelNewResponse, + ModelsListResponse, OcrBody, OcrResponse, SpendLogRow, @@ -111,6 +123,94 @@ class Gateway: ) ).data + def list_files(self, key: str) -> Result[FileListResponse]: + return self.transport.get( + "/v1/files", + headers=self.transport.bearer(key), + params=NoBody(), + response_type=FileListResponse, + ) + + def list_fine_tuning_jobs( + self, key: str, params: FineTuningJobsParams + ) -> Result[FineTuningJobsResponse]: + return self.transport.get( + "/v1/fine_tuning/jobs", + headers=self.transport.bearer(key), + params=params, + response_type=FineTuningJobsResponse, + ) + + def create_model( + self, + model_name: str, + litellm_params: LiteLLMParamsBody, + mode: ModelMode | None = None, + ) -> str: + """Register a deployment under `model_name` and return its proxy-assigned + model_id, once the model is actually servable on the data plane. + + /model/new is a control-plane route; in a split control/data-plane + deployment the gateway (data plane, which serves /chat, /ocr, ...) only + picks the new model up on its next DB reload, so a call issued the instant + this returns can race the reload and 400 with "Invalid model name passed". + We therefore poll the data-plane /v1/models until the model appears before + handing back, so callers can invoke it immediately. In the monolithic case + it is already present on the first poll, so this adds one request.""" + model_id = unwrap( + self.transport.post( + "/model/new", + headers=self.transport.master, + json=ModelNewBody( + model_name=model_name, + litellm_params=litellm_params, + model_info=ModelInfoBody(mode=mode), + ), + response_type=ModelNewResponse, + ) + ).model_id + self._await_model_servable(model_name) + return model_id + + def _await_model_servable(self, model_name: str) -> None: + """Block until the data plane lists `model_name`, or fail loudly if it does + not within poll_timeout (a real propagation/config problem, surfaced here + instead of as a downstream "Invalid model name passed").""" + deadline = time.monotonic() + self.poll_timeout + last_result: Result[ModelsListResponse] | None = None + while time.monotonic() < deadline: + last_result = self.transport.get( + "/v1/models", + headers=self.transport.master, + params=NoBody(), + response_type=ModelsListResponse, + ) + if isinstance(last_result, Success) and any( + entry.id == model_name for entry in last_result.data.data + ): + return + time.sleep(self.poll_interval) + last_error = ( + f"; last /v1/models poll did not succeed: {last_result}" + if last_result is not None and not isinstance(last_result, Success) + else "" + ) + raise AssertionError( + f"model {model_name!r} was created but never became servable on the data " + f"plane within {self.poll_timeout}s of /model/new (control/data-plane " + f"propagation or STORE_MODEL_IN_DB reload issue){last_error}" + ) + + def delete_model(self, model_id: str) -> None: + result = self.transport.post( + "/model/delete", + headers=self.transport.master, + json=ModelDeleteBody(id=model_id), + response_type=NoBody, + ) + if not is_ok(result): + warnings.warn(f"delete_model({model_id!r}) failed: {result}", stacklevel=2) + # ---- LLM calls ------------------------------------------------------ def chat(self, key: str, body: ChatBody) -> Result[ChatResponse]: diff --git a/tests/e2e/e2e_http.py b/tests/e2e/e2e_http.py index 7458f316852..32005faed4b 100644 --- a/tests/e2e/e2e_http.py +++ b/tests/e2e/e2e_http.py @@ -36,6 +36,16 @@ class NoBody(BaseModel): """Empty body/query for routes that take none.""" +class FileUploadForm(BaseModel): + """Multipart form fields for POST /v1/files. The file bytes are passed + separately; `model` is not here because the proxy reads it from the query + (?model=) not the form.""" + + purpose: str = "batch" + target_model_names: str | None = None + custom_llm_provider: str | None = None + + # ---------- Result types ---------- R = TypeVar("R", bound=BaseModel) @@ -97,13 +107,15 @@ class ProbeResult(BaseModel): class StreamingResponse(BaseModel): """Raw outcome for calls whose body is provider-native or streamed: status, the - x-litellm-call-id header (== SpendLogs.request_id), the content-type (which - tells streaming `text/event-stream` from non-streaming `application/json`), and - the body. Used by passthrough and streaming, where one validated JSON model - does not fit.""" + x-litellm-call-id header, the x-litellm-response-cost header (StandardLogging + response_cost), the content-type (which tells streaming `text/event-stream` from + non-streaming `application/json`), and the body. SpendLogs.request_id is the + completion body id, not call_id. Used by passthrough and streaming, where one + validated JSON model does not fit.""" status_code: int call_id: str | None = None # x-litellm-call-id header + response_cost: float | None = None # x-litellm-response-cost header content_type: str | None = None body: str chunks: int = 0 # streamed events (0 for non-streaming) @@ -250,13 +262,25 @@ def probe( return ProbeResult(status_code=resp.status_code, body=resp.text) +def _parse_response_cost(resp: requests.Response) -> float | None: + raw = _hdr(resp, "x-litellm-response-cost") + if raw is None or raw == "": + return None + try: + return float(raw) + except ValueError: + return None + + def _streaming_outcome(resp: requests.Response, stream: bool) -> StreamingResponse: call_id = _hdr(resp, "x-litellm-call-id") + response_cost = _parse_response_cost(resp) content_type = _hdr(resp, "content-type") if not stream or not (200 <= resp.status_code < 300): return StreamingResponse( status_code=resp.status_code, call_id=call_id, + response_cost=response_cost, content_type=content_type, body=resp.text, ) @@ -265,6 +289,7 @@ def _streaming_outcome(resp: requests.Response, stream: bool) -> StreamingRespon return StreamingResponse( status_code=resp.status_code, call_id=call_id, + response_cost=response_cost, content_type=content_type, body="", chunks=chunks, @@ -304,3 +329,50 @@ def stream( """Streaming (SSE) call: consumes the stream counting events, and captures the x-litellm-call-id + content-type headers. Body is elided.""" return send(url, headers=headers, json=json, stream=True, timeout=timeout) + + +def upload[R: BaseModel]( + url: URL, + *, + headers: BaseModel, + form: FileUploadForm, + filename: str, + content: bytes, + params: BaseModel | None = None, + response_type: type[R], + timeout: float = 60.0, +) -> Result[R]: + """Multipart POST for file uploads (/v1/files). Form fields come from `form`, + the file bytes are sent as the `file` part, and `params` carries any query + routing (e.g. ?model=). requests sets the multipart Content-Type itself.""" + dumped: dict[str, object] = form.model_dump(by_alias=True, exclude_none=True) + data = {key: str(value) for key, value in dumped.items()} + try: + resp = requests.post( + str(url), + headers=_headers(headers), + params=_params(params), + data=data, + files={"file": (filename, content, "application/jsonl")}, + timeout=timeout, + ) + except requests.RequestException as exc: + return NetworkError(message=str(exc)) + return _classify(resp, response_type) + + +def download( + url: URL, *, headers: BaseModel, timeout: float = 60.0 +) -> StreamingResponse: + """Raw GET for file content (/v1/files/{id}/content): provider-native bytes, no + schema. Returns the decoded body and the x-litellm-call-id header.""" + try: + resp = requests.get(str(url), headers=_headers(headers), timeout=timeout) + except requests.RequestException as exc: + return StreamingResponse(status_code=-1, body=str(exc)) + return StreamingResponse( + status_code=resp.status_code, + call_id=_hdr(resp, "x-litellm-call-id"), + content_type=_hdr(resp, "content-type"), + body=resp.text, + ) diff --git a/tests/e2e/gateway/litellm-config.yml b/tests/e2e/gateway/litellm-config.yml deleted file mode 100644 index ecc2c044039..00000000000 --- a/tests/e2e/gateway/litellm-config.yml +++ /dev/null @@ -1,237 +0,0 @@ -# This default config file aims to support most popular model providers out of the box - -#In general, the model name used by the client will be the same as the ones from the provider (For example, you will use "anthropic.claude-3-5-sonnet-20240620-v1:0" when you're calling LiteLLM just like you would when calling Amazon Bedrock directly) -#In the case where there are model name conflicts, a prefix will be used (For example, the Azure and the openAI model names conflict, so when you are using Azure, you will use "azure/gpt-4o-realtime-preview-2024-10-01") - -#Some model providers require additional user-specific configuration (such as Azure which requires you to specify your own api_base with your resource name, and your api_version). -#In this case, the provider is commented out, and you should uncomment it and provide your specific info - -#For more detailed information about each provider, refer to the docs: https://docs.litellm.ai/docs/providers - -#If you are not interested in a particular provider, just remove it from your config.yaml, and redeploy, and it will no longer show up in your LiteLLM deployment - -#If a particular provider is not working, double check your .env file, and make sure you have provided a valid api key for that provider, and then redeploy - -#Full details on guardrails here: https://docs.litellm.ai/docs/proxy/guardrails/bedrock -general_settings: - store_prompts_in_spend_logs: true - master_key: os.environ/LITELLM_MASTER_KEY - proxy_batch_write_at: 60 - database_connection_pool_limit: 10 - # disable_error_logs: True - forward_client_headers_to_llm_api: false - maximum_spend_logs_retention_period: "60d" # GSE-13389: Cleanup logs older than 60 days - maximum_spend_logs_cleanup_cron: "0 1 * * *" # 01:00 UTC daily = 18:00 PDT - database_url: os.environ/DATABASE_URL - control_plane_url: os.environ/CONTROL_PLANE_URL - alerts: ["email"] - - proxy_budget_rescheduler_min_time: 15 - proxy_budget_rescheduler_max_time: 20 - -# fallbacks: [{"gpt-4": ["anthropic.claude-3-5-sonnet-20240620-v1:0"]}] #Configure fallbacks for context window exeeded errors (In this example, we will fall back to Claude Sonnet if over 8000 tokens, which is gpt-4's limit) - # default_fallbacks: ["anthropic.claude-3-haiku-20240307-v1:0"] #Configure fallbacks for any error for every model (the above fallback configurations override this one) -# environment_variables: -# STORE_MODEL_IN_DB: 'True' -# LITELLM_LOG: "DEBUG" -litellm_settings: - drop_params: True - # Spend counters inherit this as their Redis TTL, so an idle counter goes cold and - # the next request reseeds it from the DB; kept short to exercise the cross-pod - # reseed path in test_spend_counter_reseed_e2e. Response-cache writes pass their own - # ttl and are unaffected. - default_redis_ttl: 20 - request_timeout: 600 - num_retries: 3 - json_logs: true - store_audit_logs: True - cache: true - cache_params: - type: redis - host: redis - port: 6379 - password: os.environ/REDIS_PASSWORD - namespace: litellm.caching - ttl: 16600 - # max_budget: 1000000000.0 # (float) sets max budget in dollars across the entire proxy across all API keys. Note, the budget does not apply to the master key. That is the only exception. - # budget_duration: 1mo # (str) frequency of budget reset - You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"), months ("1mo"). - # max_internal_user_budget: 1000000000.0 # (float) sets default budget in dollars for each internal user. (Doesn't apply to Admins. Doesn't apply to Teams. Doesn't apply to master key) - # internal_user_budget_duration: "1mo" # (str) frequency of budget reset - You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"), months ("1mo"). - # success_callback: ["s3_v2"] - # failure_callback: ["s3_v2"] - # service_callback: ["datadog"] - callbacks: ["arize_phoenix", "datadog", "smtp_email", "prometheus", "otel"] - require_auth_for_metrics_endpoint: false - #type: redis-semantic - #similarity_threshold: 0.8 # similarity threshold for semantic cache - #redis_semantic_cache_embedding_model: text-embedding-ada-002 # only works with text-embedding-ada-002 for now... https://github.com/BerriAI/litellm/issues/4001 - -router_settings: - routing_strategy: simple-shuffle - num_retries: 3 - allowed_fails: 5 - cooldown_time: 30 - # When gemini deployments are exhausted (provider 429 / auth), cross over to - # working models. Exercised by tests/e2e/router/test_rate_limiter.py. - fallbacks: - - gemini-2.5-flash: ["gpt-5.5", "claude-haiku-4-5"] - -#ttl: Optional[float] -#default_in_memory_ttl: Optional[float] -#default_in_redis_ttl: Optional[float] - -model_list: - - model_name: gpt-5.5 - litellm_params: - model: openai/gpt-5.5 - api_key: os.environ/OPENAI_API_KEY - - - model_name: claude-haiku-4-5 - litellm_params: - model: anthropic/claude-haiku-4-5 - api_key: os.environ/ANTHROPIC_API_KEY - - # Same underlying model via Vertex AI — distinct routing/auth path - # # (service-account JSON), so it gets its own model_name. - - model_name: gemini-2.5-flash-vertex - litellm_params: - model: vertex_ai/gemini-2.5-flash - vertex_project: os.environ/VERTEXAI_PROJECT - vertex_location: us-central1 - vertex_credentials: os.environ/VERTEXAI_CREDENTIALS - - - model_name: gemini-2.5-flash - litellm_params: - model: gemini/gemini-2.5-flash - api_key: os.environ/GEMINI_API_KEY - - # load balancing to a different deployment, if gemini gets rate limited. - - model_name: gemini-2.5-flash - litellm_params: - model: gemini/gemini-2.5-flash - api_key: os.environ/GEMINI_API_KEY - - # Custom per-token pricing exercised by llm_translation/test_custom_pricing_e2e.py. - # Rates deliberately exceed canonical gemini-2.5-flash (input 3e-7 / output 2.5e-6) - # so an override that is ignored or under-applied reports spend at the base rate - # and fails that test. The test reads these same rates back from this file. - - model_name: custom-priced-flash - litellm_params: - model: gemini/gemini-2.5-flash - api_key: os.environ/GEMINI_API_KEY - input_cost_per_token: 0.00005 - output_cost_per_token: 0.0001 - - # embedding models - - model_name: openai-text-embedding-3-small - litellm_params: - model: openai/text-embedding-3-small - api_key: os.environ/OPENAI_API_KEY - - - model_name: gemini-2-embedding - litellm_params: - model: gemini/gemini-2-embedding - api_key: os.environ/GEMINI_API_KEY - - - model_name: openai-realtime - litellm_params: - model: openai/gpt-realtime - api_key: os.environ/OPENAI_API_KEY - model_info: - mode: realtime - - - model_name: azure-realtime - litellm_params: - model: azure/gpt-realtime-2 - api_key: os.environ/AZURE_API_KEY - api_base: os.environ/AZURE_API_BASE - api_version: "2025-08-28" - realtime_protocol: GA # Possible values: "GA"/ "v1", "beta" - model_info: - mode: realtime - - - model_name: gemini-realtime - litellm_params: - model: gemini/gemini-3.1-flash-live-preview - api_key: os.environ/GEMINI_API_KEY - model_info: - mode: realtime - - - model_name: vertex-realtime - litellm_params: - model: vertex_ai/gemini-live-2.5-flash-preview-native-audio-09-2025 - vertex_project: os.environ/VERTEXAI_PROJECT - vertex_location: us-central1 - vertex_credentials: os.environ/VERTEXAI_CREDENTIALS - model_info: - mode: realtime - - - model_name: bedrock-realtime - litellm_params: - model: bedrock/amazon.nova-sonic-v1:0 - aws_region_name: us-east-1 - model_info: - mode: realtime - - - model_name: xai-realtime - litellm_params: - model: xai/grok-voice-latest - api_key: os.environ/XAI_API_KEY - model_info: - mode: realtime - - model_name: rust-ocr-mistral - litellm_params: - model: mistral/mistral-ocr-latest - api_key: os.environ/MISTRAL_API_KEY - - - model_name: rust-ocr-azure-ai - litellm_params: - model: azure_ai/mistral-document-ai-2505 - api_base: os.environ/AZURE_API_BASE - api_key: os.environ/AZURE_API_KEY - - - model_name: rust-ocr-azure-document-intelligence - litellm_params: - model: azure_ai/doc-intelligence/prebuilt-layout - api_base: os.environ/AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT - api_key: os.environ/AZURE_DOCUMENT_INTELLIGENCE_API_KEY - - - model_name: rust-ocr-vertex-mistral - litellm_params: - model: vertex_ai/mistral-ocr-2505 - vertex_project: os.environ/VERTEXAI_PROJECT - vertex_location: us-central1 - - - model_name: rust-ocr-vertex-deepseek - litellm_params: - model: vertex_ai/deepseek-ocr-maas - vertex_project: os.environ/VERTEXAI_PROJECT - vertex_location: us-central1 - - -mcp_servers: - deepwiki_mcp: - url: "https://mcp.deepwiki.com/mcp" - auth_type: none - description: "just a test" - - atlassian: - url: "https://mcp.atlassian.com/v1/mcp" - auth_type: oauth2 - authorization_url: https://auth.atlassian.com/authorize - - -guardrails: - - guardrail_name: "presidio-pii" - litellm_params: - guardrail: presidio - mode: pre_call - presidio_analyzer_api_base: os.environ/PRESIDIO_ANALYZER_API_BASE - presidio_anonymizer_api_base: os.environ/PRESIDIO_ANONYMIZER_API_BASE - default_on: false - pii_entities_config: - EMAIL_ADDRESS: BLOCK - CREDIT_CARD: BLOCK - US_SSN: BLOCK - PHONE_NUMBER: BLOCK - diff --git a/tests/e2e/lifecycle.py b/tests/e2e/lifecycle.py index fdf2137584e..b15986987a7 100644 --- a/tests/e2e/lifecycle.py +++ b/tests/e2e/lifecycle.py @@ -98,9 +98,12 @@ class ResourceManager: """Register a teardown action for any resource the test just created.""" self._cleanups.append(cleanup) - def key(self) -> str: - """Create an all-models virtual key; delete it on teardown.""" - key = self.client.generate_key(KeyGenerateBody(models=[])) + def key(self, models: list[str] | None = None, user_id: str | None = "e2e-test-user") -> str: + """Create a virtual key; delete it on teardown. `models` restricts which + models the key may call (None/[] means all). `user_id` is required for + managed-batch ACL: the proxy stores created_by=user_id and checks it on + retrieve/cancel; None here means the 403 guard fires.""" + key = self.client.generate_key(KeyGenerateBody(models=models or [], user_id=user_id)) self.defer(lambda: self.client.delete_key(key)) return key diff --git a/tests/e2e/llm_translation/LLM_TRANSLATION_COVERAGE_MATRIX.md b/tests/e2e/llm_translation/LLM_TRANSLATION_COVERAGE_MATRIX.md index 5e4a448857f..44d6e79122e 100644 --- a/tests/e2e/llm_translation/LLM_TRANSLATION_COVERAGE_MATRIX.md +++ b/tests/e2e/llm_translation/LLM_TRANSLATION_COVERAGE_MATRIX.md @@ -28,7 +28,7 @@ Status: `covered` / `partial` / `gap`. |----------|---------------|-----------|------------|-------------|--------| | Gemini (`/gemini/v1beta/models/{m}:generateContent` / `:streamGenerateContent`) | live | live | live | live | **covered** | | Anthropic (`/anthropic/v1/messages`) | live | live | live | live | **covered** | -| Vertex AI (`/vertex_ai/...`) | - | - | - | - | gap (gcloud auth) | +| Vertex AI (`/vertex_ai/v1/projects/{p}/locations/{loc}/.../models/{m}:generateContent`) | live | - | - | live | **partial** | | OpenAI / Bedrock / Cohere / Mistral / VLLM | - | - | - | - | gap | Each covered cell asserts: `call_type == "pass_through_endpoint"`, `spend > 0`, @@ -60,11 +60,21 @@ most likely to silently break and the one a mock can't prove works. | `test_anthropic_passthrough_nonstreaming_logs_cost` | anthropic native, non-stream, cost | | `test_anthropic_passthrough_streaming_logs_cost` | anthropic native, stream, cost | | `test_anthropic_passthrough_tool_call_logs_cost` | anthropic native, tool call, cost | +| `test_vertex_passthrough_via_managed_model_logs_cost` | vertex_ai native, non-stream, cost | + +Vertex keeps the credential on the proxy like gemini/anthropic, but the deployment is +added at runtime instead of declared in the gateway config: the test POSTs `/model/new` +with `use_in_pass_through`, so the proxy registers that deployment's service account for +the `/vertex_ai` route, then deletes it on teardown. The passthrough call sends only its +litellm virtual key (`x-litellm-api-key`), no upstream bearer, and the proxy mints the +Vertex token itself. Credentials (`VERTEXAI_PROJECT` / `VERTEXAI_CREDENTIALS`) are read +from the same env the proxy uses, so the test never mints a token. ## Gaps -- Vertex / OpenAI / Bedrock / Cohere passthrough (same shape; add once the - provider credential is configured; Vertex is closest - route exists, auth stale). +- Vertex streaming / tool-call passthrough (non-streaming + cost now covered). +- OpenAI / Bedrock / Cohere passthrough (same shape; add once the provider + credential is configured). - Non-passthrough tool calls over `/chat/completions` end to end with cost. - Image / audio / rerank / responses / realtime translation + cost. - Streaming cost-injection (`include_cost_in_streaming_usage`); passthrough on diff --git a/tests/e2e/llm_translation/conftest.py b/tests/e2e/llm_translation/conftest.py index fbf008cf085..2a87ef7259d 100644 --- a/tests/e2e/llm_translation/conftest.py +++ b/tests/e2e/llm_translation/conftest.py @@ -7,9 +7,22 @@ Gateway, so the `resources` fixture cleans up keys this suite creates. import pytest +from endpoints_client import EndpointsClient, build_endpoints_client from passthrough_client import PassthroughClient, build_client +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line( + "markers", + "covers: registry cell a test covers, e.g. llm.chat_completions.provider.basic.nonstream.works", + ) + + @pytest.fixture(scope="session") def client() -> PassthroughClient: return build_client() + + +@pytest.fixture(scope="session") +def endpoints_client() -> EndpointsClient: + return build_endpoints_client() diff --git a/tests/e2e/llm_translation/endpoints_client.py b/tests/e2e/llm_translation/endpoints_client.py new file mode 100644 index 00000000000..0ab87472748 --- /dev/null +++ b/tests/e2e/llm_translation/endpoints_client.py @@ -0,0 +1,190 @@ +"""Client for the non-chat inference endpoints (responses, messages, rerank, +embeddings, audio speech, image generation). + +Each test registers the deployment it needs through /model/new (deleted on +teardown), so nothing is hardcoded into the gateway config, then drives the +endpoint with `send` and parses the provider-native body with a suite-local model +so the assertion is on real content, not just a 200. +""" + +from __future__ import annotations + +from dataclasses import dataclass + +from pydantic import BaseModel + +from e2e_gateway import Gateway, build_gateway +from e2e_http import StreamingResponse +from models import ChatMessage, LiteLLMParamsBody + + +class ResponsesRequest(BaseModel): + model: str + input: str + instructions: str | None = None + + +class MessagesRequest(BaseModel): + model: str + max_tokens: int + messages: list[ChatMessage] + + +class EmbeddingsRequest(BaseModel): + model: str + input: str + + +class RerankRequest(BaseModel): + model: str + query: str + documents: list[str] + top_n: int + + +class SpeechRequest(BaseModel): + model: str + input: str + voice: str + + +class ImageRequest(BaseModel): + model: str + prompt: str + n: int = 1 + size: str = "1024x1024" + + +class ResponsesOutputContent(BaseModel): + type: str | None = None + text: str | None = None + + +class ResponsesOutputItem(BaseModel): + type: str | None = None + content: list[ResponsesOutputContent] = [] + + +class ResponsesResult(BaseModel): + id: str | None = None + status: str | None = None + model: str | None = None + output: list[ResponsesOutputItem] = [] + + @property + def text(self) -> str: + return "".join( + content.text or "" for item in self.output for content in item.content + ) + + +class AnthropicContentBlock(BaseModel): + type: str | None = None + text: str | None = None + + +class MessagesResult(BaseModel): + id: str | None = None + role: str | None = None + model: str | None = None + content: list[AnthropicContentBlock] = [] + + @property + def text(self) -> str: + return "".join(block.text or "" for block in self.content) + + +class EmbeddingItem(BaseModel): + embedding: list[float] = [] + + +class EmbeddingsResult(BaseModel): + data: list[EmbeddingItem] = [] + + @property + def first_vector(self) -> tuple[float, ...]: + return tuple(self.data[0].embedding) if self.data else () + + +class RerankItem(BaseModel): + index: int | None = None + relevance_score: float | None = None + + +class RerankResult(BaseModel): + results: list[RerankItem] = [] + + +class ImageItem(BaseModel): + url: str | None = None + b64_json: str | None = None + + +class ImagesResult(BaseModel): + data: list[ImageItem] = [] + + +@dataclass(frozen=True, slots=True) +class EndpointsClient: + gateway: Gateway + + def create_model(self, model_name: str, litellm_params: LiteLLMParamsBody) -> str: + return self.gateway.create_model(model_name, litellm_params) + + def delete_model(self, model_id: str) -> None: + self.gateway.delete_model(model_id) + + def _send(self, path: str, key: str, body: BaseModel) -> StreamingResponse: + return self.gateway.transport.send( + path, headers=self.gateway.transport.bearer(key), json=body + ) + + def responses(self, key: str, model: str, text: str) -> StreamingResponse: + return self._send( + "/v1/responses", + key, + ResponsesRequest( + model=model, input=text, instructions="You are a helpful assistant" + ), + ) + + def messages( + self, key: str, model: str, text: str, *, max_tokens: int = 64 + ) -> StreamingResponse: + return self._send( + "/v1/messages", + key, + MessagesRequest( + model=model, + max_tokens=max_tokens, + messages=[ChatMessage(role="user", content=text)], + ), + ) + + def embeddings(self, key: str, model: str, text: str) -> StreamingResponse: + return self._send("/embeddings", key, EmbeddingsRequest(model=model, input=text)) + + def rerank( + self, key: str, model: str, query: str, documents: list[str], top_n: int + ) -> StreamingResponse: + return self._send( + "/v1/rerank", + key, + RerankRequest(model=model, query=query, documents=documents, top_n=top_n), + ) + + def audio_speech( + self, key: str, model: str, text: str, *, voice: str = "alloy" + ) -> StreamingResponse: + return self._send( + "/v1/audio/speech", key, SpeechRequest(model=model, input=text, voice=voice) + ) + + def images(self, key: str, model: str, prompt: str) -> StreamingResponse: + return self._send( + "/v1/images/generations", key, ImageRequest(model=model, prompt=prompt) + ) + + +def build_endpoints_client() -> EndpointsClient: + return EndpointsClient(gateway=build_gateway()) diff --git a/tests/e2e/llm_translation/passthrough_client.py b/tests/e2e/llm_translation/passthrough_client.py index fff4064a328..77dcf68a1e3 100644 --- a/tests/e2e/llm_translation/passthrough_client.py +++ b/tests/e2e/llm_translation/passthrough_client.py @@ -48,6 +48,16 @@ class AnthropicHeaders(Headers): tags: str | None = None +class VertexHeaders(Headers): + # Only the litellm virtual key; the /vertex_ai passthrough mints the Vertex token + # from the proxy's own service account (the deployment marked use_in_pass_through), + # so no upstream Authorization bearer is sent from the client. + x_litellm_api_key: str = Field(serialization_alias="x-litellm-api-key") + content_type: str = Field( + default="application/json", serialization_alias="Content-Type" + ) + + class AltSseParams(BaseModel): alt: str = "sse" @@ -132,6 +142,23 @@ class PassthroughClient: stream=True, ) + # ---- Vertex AI native passthrough (/vertex_ai/v1/projects/...) ------- + + def vertex_generate( + self, key: str, project: str, location: str, model: str, text: str + ) -> StreamingResponse: + path = ( + f"/vertex_ai/v1/projects/{project}/locations/{location}" + f"/publishers/google/models/{model}:generateContent" + ) + return self.gateway.transport.send( + path, + headers=VertexHeaders(x_litellm_api_key=key), + json=GeminiGenerateBody( + contents=[GeminiContent(parts=[GeminiPart(text=text)])] + ), + ) + # ---- Anthropic native passthrough (/anthropic/v1/messages) ---------- def anthropic_message( diff --git a/tests/e2e/llm_translation/realtime/REALTIME_COVERAGE_MATRIX.md b/tests/e2e/llm_translation/realtime/REALTIME_COVERAGE_MATRIX.md new file mode 100644 index 00000000000..4795c3b9f54 --- /dev/null +++ b/tests/e2e/llm_translation/realtime/REALTIME_COVERAGE_MATRIX.md @@ -0,0 +1,67 @@ +# Realtime e2e coverage + +Live tests for the proxy realtime websocket endpoint (`/v1/realtime`). One +GA-speaking websocket client drives every provider; the proxy normalizes each +provider's stream into the OpenAI GA event schema, so the same assertions hold +across providers and only the model alias changes. + +## What is asserted + +For each configured provider, `test_text_conversation` checks the session +lifecycle (`session.created`, then `session.update` echoed by `session.updated`), +the canonical response sequence (`response.created`, `response.output_item.added`, +through `response.done`), that the streamed deltas reconstruct a non-empty +transcript, and that `response.done` carries normalized usage. + +`test_tool_call_round_trip` checks the full tool path: the model emits a +normalized `response.function_call_arguments.done` with valid JSON arguments and +a matching `function_call` output item, the test sends a `function_call_output` +back, and the follow-up response incorporates the result (the temperature 72 +appears). That raw-websocket tool path is the source of truth for tool calling. + +`test_pipecat_tool_smoke` is a realism layer through pipecat for openai, azure, +and gemini only (not vertex_ai: native-audio live is flaky under pipecat tool +calling while raw-ws tools pass; see pipecat-ai/pipecat#2544). Assertions are +coarse; raw-ws remains authoritative. Requires `pipecat-ai`. + +Pipecat audio coverage lives in `test_realtime_pipecat_audio_e2e.py` (VAD / audio +I/O). + +## Provisioning + +The suite registers every provider's realtime deployment through `/model/new` at +session start (the `realtime_models` fixture) and deletes them on teardown, so it +never depends on a static or misconfigured gateway `model_list`. Each deployment +is created with `model_info.mode: realtime` and marker-unique names, and its +`litellm_params` point the credentials at `os.environ/*` refs the gateway resolves +at call time. The provider table below is the source of truth; edit `PROVIDERS` in +`realtime_client.py` to change a model or add one. + +| provider | model alias | upstream model | +|----------|-------------|----------------| +| openai | `openai-realtime` | `openai/gpt-realtime-2` | +| azure | `azure-realtime` | `azure/gpt-realtime-2` (GA protocol) | +| gemini | `gemini-realtime` | `gemini/gemini-3.1-flash-live-preview` | +| vertex_ai | `vertex-realtime` | `vertex_ai/gemini-live-2.5-flash-preview-native-audio-09-2025` | + +Bedrock and xai (`xai/grok-4-1-fast-non-reasoning`) are supported by the proxy but +kept commented out in `PROVIDERS` until they pass end-to-end here; re-enable them by +uncommenting their entry. + +Every provider is provisioned and asserted; the suite never skips a provider. Per +`tests/e2e/CLAUDE.md` the only sanctioned skip is the whole-suite proxy-liveness +skip, so a provider whose credentials or upstream realtime model are missing on the +gateway is a hard failure, not a skip. Give the gateway each provider's credentials +to turn its tests green. + +## Running + +Start a proxy with the provider keys set in its environment (the suite registers +the deployments itself), then + +``` +uv run pytest tests/e2e/llm_translation/realtime/ -v +``` + +The whole suite skips only when no proxy answers `GET /health/liveliness` at +`LITELLM_PROXY_URL` (default `http://localhost:4000`). diff --git a/tests/e2e/llm_translation/realtime/conftest.py b/tests/e2e/llm_translation/realtime/conftest.py new file mode 100644 index 00000000000..15cd789664e --- /dev/null +++ b/tests/e2e/llm_translation/realtime/conftest.py @@ -0,0 +1,37 @@ +"""Realtime suite's `client` and `realtime_models` fixtures. + +The shared lifecycle (resources/scoped_key), proxy liveness skip, and e2e marker +live in the parent tests/e2e/conftest.py. RealtimeClient holds the shared Gateway, +so the `resources` fixture cleans up keys this suite creates. + +`realtime_models` registers every provider's realtime deployment through /model/new +at session start and deletes them at teardown, so the suite provisions the models it +uses through the management endpoints instead of depending on a static (or +misconfigured) gateway model_list. +""" + +from collections.abc import Iterator + +import pytest + +from realtime_client import PROVIDERS, RealtimeClient, build_client + + +@pytest.fixture(scope="session") +def client() -> RealtimeClient: + return build_client() + + +@pytest.fixture(scope="session") +def realtime_models(client: RealtimeClient) -> Iterator[dict[str, str]]: + """Provision each provider's realtime deployment via /model/new and yield a + provider-id -> model-name map the tests connect with; delete them on teardown. + Every provider is provisioned (never skipped): a provider whose credentials or + upstream model are missing on the gateway hard-fails its test, per the suite's + fail-on-behavior contract in tests/e2e/CLAUDE.md.""" + records = tuple((provider.id, *client.provision(provider)) for provider in PROVIDERS) + try: + yield {provider_id: model_name for provider_id, model_name, _ in records} + finally: + for _, _, model_id in records: + client.gateway.delete_model(model_id) diff --git a/tests/e2e/realtime/fixtures/weather_question_24k.wav b/tests/e2e/llm_translation/realtime/fixtures/weather_question_24k.wav similarity index 100% rename from tests/e2e/realtime/fixtures/weather_question_24k.wav rename to tests/e2e/llm_translation/realtime/fixtures/weather_question_24k.wav diff --git a/tests/e2e/realtime/pipecat_service.py b/tests/e2e/llm_translation/realtime/pipecat_service.py similarity index 100% rename from tests/e2e/realtime/pipecat_service.py rename to tests/e2e/llm_translation/realtime/pipecat_service.py diff --git a/tests/e2e/realtime/realtime_client.py b/tests/e2e/llm_translation/realtime/realtime_client.py similarity index 67% rename from tests/e2e/realtime/realtime_client.py rename to tests/e2e/llm_translation/realtime/realtime_client.py index 07dfd76108b..b50160e3538 100644 --- a/tests/e2e/realtime/realtime_client.py +++ b/tests/e2e/llm_translation/realtime/realtime_client.py @@ -11,24 +11,24 @@ models, matching the suite's no-raw-dicts rule. from __future__ import annotations import time -from collections.abc import Generator +from collections.abc import Generator, Mapping from contextlib import contextmanager from dataclasses import dataclass -from typing import Any, TypeVar +from typing import TypeVar from urllib.parse import urlencode -import pytest from pydantic import BaseModel, ConfigDict from websockets.sync.client import connect from websockets.sync.connection import Connection -from e2e_config import PROXY_BASE_URL +from e2e_config import PROXY_BASE_URL, unique_marker from e2e_gateway import Gateway, build_gateway +from models import LiteLLMParamsBody _M = TypeVar("_M", bound=BaseModel) -def _ws_base_url() -> str: +def ws_base_url() -> str: for scheme, ws_scheme in (("https://", "wss://"), ("http://", "ws://")): if PROXY_BASE_URL.startswith(scheme): return ws_scheme + PROXY_BASE_URL[len(scheme) :] @@ -36,30 +36,81 @@ def _ws_base_url() -> str: def realtime_ws_url(model: str) -> str: - return f"{_ws_base_url()}/v1/realtime?{urlencode({'model': model})}" + return f"{ws_base_url()}/v1/realtime?{urlencode({'model': model})}" @dataclass(frozen=True, slots=True) class RealtimeProvider: + """A realtime provider the suite exercises. `litellm_params` is the deployment + the suite registers through /model/new (the gateway resolves the os.environ/* + credential refs), so the suite is self-contained and never depends on a static + gateway model_list. Every provider here is provisioned and asserted: per + tests/e2e/CLAUDE.md the suite never skips a provider, so a provider whose + credentials or upstream realtime model are missing on the gateway is a hard + failure, not a skip.""" + id: str - model: str + alias: str + litellm_params: LiteLLMParamsBody PROVIDERS = ( - RealtimeProvider("openai", "openai-realtime"), - RealtimeProvider("azure", "azure-realtime"), - RealtimeProvider("gemini", "gemini-realtime"), - RealtimeProvider("vertex_ai", "vertex-realtime"), - # RealtimeProvider("bedrock", "bedrock-realtime"), # TODO: Enable this when Bedrock is passing - RealtimeProvider("xai", "xai-realtime"), + RealtimeProvider( + "openai", + "openai-realtime", + LiteLLMParamsBody( + model="openai/gpt-realtime-2", + api_key="os.environ/OPENAI_API_KEY", + ), + ), + RealtimeProvider( + "azure", + "azure-realtime", + LiteLLMParamsBody( + model="azure/gpt-realtime", + api_key="os.environ/AZURE_API_KEY", + api_version="2025-08-28", + realtime_protocol="GA", + ), + ), + RealtimeProvider( + "gemini", + "gemini-realtime", + LiteLLMParamsBody( + model="gemini/gemini-3.1-flash-live-preview", + api_key="os.environ/GEMINI_API_KEY", + ), + ), + RealtimeProvider( + "vertex_ai", + "vertex-realtime", + LiteLLMParamsBody( + model="vertex_ai/gemini-live-2.5-flash-preview-native-audio-09-2025", + vertex_location="us-central1", + vertex_credentials="os.environ/VERTEXAI_CREDENTIALS", + ), + ), + # RealtimeProvider("bedrock", "bedrock-realtime", ...) # TODO: Enable when Bedrock is passing + # RealtimeProvider( + # "xai", + # "xai-realtime", + # LiteLLMParamsBody( + # model="xai/grok-4-1-fast", + # api_key="os.environ/XAI_API_KEY", + # ), + # ), # TODO: Enable once xai Grok Voice realtime is passing end-to-end here ) -def skip_if_unconfigured( - provider: RealtimeProvider, configured: frozenset[str] -) -> None: - if provider.model not in configured: - pytest.skip(f"{provider.model} not configured on proxy") +def realtime_model(provider: RealtimeProvider, provisioned: Mapping[str, str]) -> str: + """Return the provisioned deployment name for this provider. Every provider in + PROVIDERS is provisioned at session start, so a missing entry is a harness bug, + never an environment skip - the suite hard-fails instead (see tests/e2e/CLAUDE.md).""" + model = provisioned.get(provider.id) + assert model is not None, ( + f"{provider.id} was not provisioned; the realtime_models fixture is broken" + ) + return model # ---- sent events ------------------------------------------------------- @@ -166,7 +217,7 @@ class OutputItemDone(BaseModel): class ResponsePayload(BaseModel): model_config = ConfigDict(extra="allow") - usage: dict[str, Any] | None = None + usage: dict[str, object] | None = None output: list[OutputItem] | None = None @@ -280,12 +331,17 @@ class RealtimeSession: class RealtimeClient: gateway: Gateway - def configured_models(self) -> frozenset[str]: - return frozenset( - entry.model_name - for entry in self.gateway.model_info() - if entry.model_info.mode == "realtime" + def provision(self, provider: RealtimeProvider) -> tuple[str, str]: + """Register this provider's realtime deployment through /model/new and return + (model_name, model_id). The name is marker-unique so it never collides with a + same-named deployment already on the shared proxy, and mode=realtime makes it + show up as a realtime model on /model/info. add_deployment runs synchronously, + so the deployment is connectable as soon as this returns.""" + model_name = f"{provider.alias}-{unique_marker()}" + model_id = self.gateway.create_model( + model_name, provider.litellm_params, mode="realtime" ) + return model_name, model_id @contextmanager def connect( diff --git a/tests/e2e/realtime/test_realtime_e2e.py b/tests/e2e/llm_translation/realtime/test_realtime_e2e.py similarity index 92% rename from tests/e2e/realtime/test_realtime_e2e.py rename to tests/e2e/llm_translation/realtime/test_realtime_e2e.py index 01356900141..6aaffdd208e 100644 --- a/tests/e2e/realtime/test_realtime_e2e.py +++ b/tests/e2e/llm_translation/realtime/test_realtime_e2e.py @@ -31,7 +31,7 @@ from realtime_client import ( SessionUpdate, function_call_item, parse_last, - skip_if_unconfigured, + realtime_model, transcript, user_message, ) @@ -62,12 +62,12 @@ class WeatherResult(BaseModel): def test_text_conversation( client: RealtimeClient, scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) - with client.connect(key=scoped_key, model=provider.model) as session: + with client.connect(key=scoped_key, model=model) as session: created = session.collect_until("session.created", timeout=20) assert created[-1].type == "session.created" @@ -99,12 +99,12 @@ def test_text_conversation( def test_tool_call_round_trip( client: RealtimeClient, scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) - with client.connect(key=scoped_key, model=provider.model) as session: + with client.connect(key=scoped_key, model=model) as session: session.collect_until("session.created", timeout=20) session.send( SessionUpdate( diff --git a/tests/e2e/realtime/test_realtime_pipecat_audio_e2e.py b/tests/e2e/llm_translation/realtime/test_realtime_pipecat_audio_e2e.py similarity index 93% rename from tests/e2e/realtime/test_realtime_pipecat_audio_e2e.py rename to tests/e2e/llm_translation/realtime/test_realtime_pipecat_audio_e2e.py index d9d7c744f66..78955974cd5 100644 --- a/tests/e2e/realtime/test_realtime_pipecat_audio_e2e.py +++ b/tests/e2e/llm_translation/realtime/test_realtime_pipecat_audio_e2e.py @@ -30,14 +30,21 @@ import pytest from realtime_client import ( PROVIDERS, RealtimeProvider, - _ws_base_url, - skip_if_unconfigured, + ws_base_url, + realtime_model, ) pytestmark = pytest.mark.e2e pytest.importorskip("pipecat", reason="pipecat-ai not installed") +try: + import nltk + + nltk.data.find("tokenizers/punkt_tab") +except LookupError: + pytest.skip("NLTK punkt_tab data is not installed", allow_module_level=True) + from pipecat.adapters.schemas.function_schema import FunctionSchema # noqa: E402 from pipecat.adapters.schemas.tools_schema import ToolsSchema # noqa: E402 from pipecat.frames.frames import ( # noqa: E402 @@ -96,7 +103,7 @@ SERVER_VAD_SETTINGS = rt_events.SessionProperties( noise_reduction=rt_events.InputAudioNoiseReduction(type="near_field"), turn_detection=rt_events.TurnDetection( type="server_vad", - threshold=0.8, + threshold=0.5, prefix_padding_ms=300, silence_duration_ms=700, ), @@ -147,7 +154,7 @@ async def _run_pipeline( llm = LiteLLMRealtimeLLMService( api_key=key, - base_url=f"{_ws_base_url()}/v1/realtime", + base_url=f"{ws_base_url()}/v1/realtime", settings=OpenAIRealtimeLLMService.Settings( model=model, system_instruction=( @@ -189,13 +196,13 @@ async def _run_pipeline( @pytest.mark.parametrize("provider", PROVIDER_PARAMS) def test_pipecat_server_vad( scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: """Session is configured with server-VAD; bot must respond to a text prompt.""" - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) - tool_called, got_text, _ = asyncio.run(_run_pipeline(scoped_key, provider.model)) + tool_called, got_text, _ = asyncio.run(_run_pipeline(scoped_key, model)) assert tool_called, "get_weather tool was not invoked" assert got_text, "no assistant text frames produced" @@ -204,16 +211,16 @@ def test_pipecat_server_vad( @pytest.mark.parametrize("provider", PROVIDER_PARAMS) def test_pipecat_audio_output( scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: """Bot must produce at least one non-empty TTS audio frame.""" - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) _, got_text, audio_bytes = asyncio.run( _run_pipeline( scoped_key, - provider.model, + model, prompt="Say hello in one short sentence.", timeout=30.0, ) @@ -269,7 +276,7 @@ async def _run_audio_input_pipeline( llm = LiteLLMRealtimeLLMService( api_key=key, - base_url=f"{_ws_base_url()}/v1/realtime", + base_url=f"{ws_base_url()}/v1/realtime", settings=OpenAIRealtimeLLMService.Settings( model=model, system_instruction=( @@ -328,7 +335,7 @@ async def _run_audio_input_pipeline( @pytest.mark.parametrize("provider", PROVIDER_PARAMS) def test_pipecat_server_vad_audio_input( scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: """Stream a real PCM16 WAV fixture; server VAD must detect speech end and respond. @@ -337,12 +344,11 @@ def test_pipecat_server_vad_audio_input( → server-VAD turn detection → response.create (auto) → assistant reply. No LLMRunFrame is sent — the response must be triggered entirely by VAD. """ - if not WEATHER_WAV.exists(): - pytest.skip(f"audio fixture not found: {WEATHER_WAV}") - skip_if_unconfigured(provider, configured_models) + assert WEATHER_WAV.exists(), f"audio fixture not found: {WEATHER_WAV}" + model = realtime_model(provider, realtime_models) got_text, audio_bytes = asyncio.run( - _run_audio_input_pipeline(scoped_key, provider.model) + _run_audio_input_pipeline(scoped_key, model) ) assert got_text, "server VAD did not trigger a response (no assistant text)" diff --git a/tests/e2e/realtime/test_realtime_pipecat_e2e.py b/tests/e2e/llm_translation/realtime/test_realtime_pipecat_e2e.py similarity index 89% rename from tests/e2e/realtime/test_realtime_pipecat_e2e.py rename to tests/e2e/llm_translation/realtime/test_realtime_pipecat_e2e.py index 1068c54fdec..16628fd257a 100644 --- a/tests/e2e/realtime/test_realtime_pipecat_e2e.py +++ b/tests/e2e/llm_translation/realtime/test_realtime_pipecat_e2e.py @@ -28,8 +28,8 @@ import pytest from realtime_client import ( PROVIDERS, RealtimeProvider, - _ws_base_url, - skip_if_unconfigured, + ws_base_url, + realtime_model, ) pytestmark = pytest.mark.e2e @@ -60,7 +60,12 @@ from pipecat.services.llm_service import FunctionCallParams # noqa: E402 from pipecat_service import LiteLLMRealtimeLLMService # noqa: E402 -PROVIDER_PARAMS = [pytest.param(p, id=p.id) for p in PROVIDERS] +# Vertex native-audio live is flaky through pipecat tool calling (upstream +# pipecat-ai/pipecat#2544); raw-ws tool_call_round_trip[vertex_ai] is the +# source of truth for that provider. Keep openai/azure/gemini here. +PROVIDER_PARAMS = [ + pytest.param(p, id=p.id) for p in PROVIDERS if p.id != "vertex_ai" +] WEATHER_TOOL = ToolsSchema( standard_tools=[ @@ -94,7 +99,7 @@ async def _run_pipeline(key: str, model: str) -> tuple[bool, bool]: await params.result_callback({"city": "Paris", "temperature_f": 72}) llm = LiteLLMRealtimeLLMService( - api_key=key, base_url=f"{_ws_base_url()}/v1/realtime", model=model + api_key=key, base_url=f"{ws_base_url()}/v1/realtime", model=model ) llm.register_function("get_weather", get_weather) @@ -123,12 +128,12 @@ async def _run_pipeline(key: str, model: str) -> tuple[bool, bool]: @pytest.mark.parametrize("provider", PROVIDER_PARAMS) def test_pipecat_tool_smoke( scoped_key: str, - configured_models: frozenset[str], + realtime_models: dict[str, str], provider: RealtimeProvider, ) -> None: - skip_if_unconfigured(provider, configured_models) + model = realtime_model(provider, realtime_models) - tool_called, produced_text = asyncio.run(_run_pipeline(scoped_key, provider.model)) + tool_called, produced_text = asyncio.run(_run_pipeline(scoped_key, model)) assert tool_called, "pipecat did not invoke the get_weather callback" assert produced_text, "pipecat produced no assistant text frames" diff --git a/tests/e2e/llm_translation/test_audio_speech_e2e.py b/tests/e2e/llm_translation/test_audio_speech_e2e.py new file mode 100644 index 00000000000..f7a04d94cb3 --- /dev/null +++ b/tests/e2e/llm_translation/test_audio_speech_e2e.py @@ -0,0 +1,40 @@ +"""Live e2e: POST /v1/audio/speech returns audio. + +Registers an OpenAI text-to-speech deployment at runtime and asserts the response +is an audio body (binary, not JSON). Migrated from +litellm-regression-tests/tests/test_inference_endpoints.py. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import require_successful_call +from endpoints_client import EndpointsClient +from lifecycle import ResourceManager +from models import LiteLLMParamsBody + +pytestmark = pytest.mark.e2e + + +class TestAudioSpeech: + def test_audio_speech_returns_audio( + self, endpoints_client: EndpointsClient, resources: ResourceManager + ) -> None: + model = f"e2e-speech-{unique_marker()}" + model_id = endpoints_client.create_model( + model, + LiteLLMParamsBody( + model="openai/gpt-4o-mini-tts", api_key="os.environ/OPENAI_API_KEY" + ), + ) + resources.defer(lambda: endpoints_client.delete_model(model_id)) + key = resources.key() + + result = endpoints_client.audio_speech(key, model, "Hello!") + require_successful_call(result) + assert "audio" in (result.content_type or ""), ( + f"/audio/speech content-type is not audio: {result.content_type!r}" + ) + assert result.body, "/audio/speech returned an empty body" diff --git a/tests/e2e/llm_translation/test_cache_control.py b/tests/e2e/llm_translation/test_cache_control.py new file mode 100644 index 00000000000..51ae6ccbc1d --- /dev/null +++ b/tests/e2e/llm_translation/test_cache_control.py @@ -0,0 +1,162 @@ +"""Live e2e: provider-specific /chat/completions features take real effect. + +Each case asserts the feature actually happened, not just a 200. Coverage matrix +(register-on-demand deployments, deleted on teardown): + +- Bedrock (anthropic claude-haiku-4-5): prompt caching. A large cacheable prefix + marked with ``cache_control`` is sent twice; the second call must report + cache-read usage tokens > 0. service_tier is out of scope for Bedrock; AWS + Bedrock does not expose an OpenAI-style request service tier, so that cell is + intentionally not covered here. +- Vertex (gemini-2.5-flash): prompt caching via ``cache_control`` context + caching; the second identical call must report cached prompt tokens > 0. + +service_tier lives in test_provider_features_e2e.py. + +The provider-native cache_control request shape is not expressible with the +shared ``ChatBody`` (whose content is a plain string), so the cacheable body is +modelled locally with typed content blocks. +""" + +from __future__ import annotations + +import time + +import pytest +from pydantic import BaseModel + +from e2e_config import unique_marker +from e2e_http import Result, unwrap +from lifecycle import ResourceManager +from models import ChatResponse, LiteLLMParamsBody, Usage +from passthrough_client import PassthroughClient +import os + +pytestmark = pytest.mark.e2e + +BEDROCK_MODEL = "bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0" +VERTEX_MODEL = "vertex_ai/gemini-2.5-flash" + + +class CacheControl(BaseModel): + type: str = "ephemeral" + + +class TextBlock(BaseModel): + type: str = "text" + text: str + cache_control: CacheControl | None = None + + +class RichMessage(BaseModel): + role: str + content: list[TextBlock] + + +class CacheChatBody(BaseModel): + model: str + messages: list[RichMessage] + max_tokens: int = 64 + cache: dict[str, bool] = {"no-cache": True} + + +def _cacheable_prefix() -> str: + """A prefix long enough to clear provider minimum cacheable sizes (Haiku is + 2048 tokens), unique per run so the first call writes and the second reads.""" + marker = unique_marker() + body = " ".join( + f"Cacheable reference paragraph {index} for run {marker}." for index in range(600) + ) + return f"{body}\nEnd of reference material {marker}." + + +def _cached_read_tokens(usage: Usage | None) -> int: + """Cache-read tokens however the provider reports them: Anthropic-style + ``cache_read_input_tokens`` or OpenAI-style ``prompt_tokens_details.cached_tokens``.""" + if usage is None: + return 0 + if usage.cache_read_input_tokens: + return usage.cache_read_input_tokens + if usage.prompt_tokens_details and usage.prompt_tokens_details.cached_tokens: + return usage.prompt_tokens_details.cached_tokens + return 0 + + +def _cache_chat( + client: PassthroughClient, key: str, model: str, prefix: str +) -> Result[ChatResponse]: + body = CacheChatBody( + model=model, + messages=[ + RichMessage( + role="system", + content=[TextBlock(text=prefix, cache_control=CacheControl())], + ), + RichMessage(role="user", content=[TextBlock(text="Reply with one word.")]), + ], + ) + return client.gateway.transport.post( + "/chat/completions", + headers=client.gateway.transport.bearer(key), + json=body, + response_type=ChatResponse, + ) + + +def _assert_cache_read_on_second_call( + client: PassthroughClient, key: str, model: str +) -> None: + prefix = _cacheable_prefix() + + first = unwrap(_cache_chat(client, key, model, prefix)) + assert first.choices, f"{model}: first cache-priming call returned no choices: {first}" + + deadline = time.monotonic() + 30.0 + while True: + second = unwrap(_cache_chat(client, key, model, prefix)) + read_tokens = _cached_read_tokens(second.usage) + if read_tokens > 0 or time.monotonic() >= deadline: + break + time.sleep(3.0) + + assert read_tokens > 0, ( + f"{model}: second identical call reported no cache-read tokens " + f"({second.usage}); prompt caching did not take effect" + ) + + +class TestCacheControl: + @pytest.mark.covers( + "llm.chat_completions.bedrock_converse.prompt_cache_5m.nonstream.works", + exercised_on=[], + ) + def test_bedrock_prompt_caching_reads_cache( + self, client: PassthroughClient, resources: ResourceManager + ) -> None: + model = f"e2e-bedrock-cache-{unique_marker()}" + model_id = client.gateway.create_model( + model, + LiteLLMParamsBody(model=BEDROCK_MODEL, aws_region_name="us-east-1"), + ) + resources.defer(lambda: client.gateway.delete_model(model_id)) + _assert_cache_read_on_second_call(client, resources.key(), model) + + @pytest.mark.covers( + "llm.chat_completions.vertex.prompt_cache_5m.nonstream.works", + exercised_on=[], + ) + def test_vertex_prompt_caching_reads_cache( + self, client: PassthroughClient, resources: ResourceManager + ) -> None: + model = f"e2e-vertex-cache-{unique_marker()}" + model_id = client.gateway.create_model( + model, + LiteLLMParamsBody( + model=VERTEX_MODEL, + vertex_project=os.environ.get("VERTEXAI_PROJECT"), + vertex_location="us-central1", + vertex_credentials=os.environ.get("VERTEXAI_CREDENTIALS"), + ), + ) + resources.defer(lambda: client.gateway.delete_model(model_id)) + _assert_cache_read_on_second_call(client, resources.key(), model) diff --git a/tests/e2e/llm_translation/test_chat_completions_regression_e2e.py b/tests/e2e/llm_translation/test_chat_completions_regression_e2e.py new file mode 100644 index 00000000000..5cc4ff308fa --- /dev/null +++ b/tests/e2e/llm_translation/test_chat_completions_regression_e2e.py @@ -0,0 +1,70 @@ +"""Live regression net for /chat/completions across the configured providers. + +GH #28991 broke /chat/completions (and /responses) for most models on some +releases: a clean 200 came back but with no real completion. A status check +alone would not have caught it, so each case here asserts the product promise - +a non-empty assistant message and a real model name in the body - across the +three providers wired into the gateway config (OpenAI, Anthropic, Gemini). A +regression that empties the completion for any provider fails that provider's +row here. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import unwrap +from models import ChatBody, ChatMessage +from passthrough_client import PassthroughClient + +pytestmark = pytest.mark.e2e + +CHAT_MODELS: tuple[tuple[str, str], ...] = ( + ("gpt-5.5", "openai"), + ("claude-haiku-4-5", "anthropic"), + ("gemini-2.5-flash", "gemini"), +) + + +class TestChatCompletionsRegression: + @pytest.mark.parametrize( + ("model", "route"), + CHAT_MODELS, + ids=[f"{model}-{route}" for model, route in CHAT_MODELS], + ) + @pytest.mark.covers( + "llm.chat_completions.openai.basic.nonstream.works", + "llm.chat_completions.anthropic.basic.nonstream.works", + "llm.chat_completions.vertex.basic.nonstream.works", + exercised_on=[], + ) + def test_chat_returns_real_completion( + self, client: PassthroughClient, scoped_key: str, model: str, route: str + ) -> None: + response = unwrap( + client.gateway.chat( + scoped_key, + ChatBody( + model=model, + messages=[ + ChatMessage( + role="user", + content=f"reply with one word {unique_marker()}", + ) + ], + max_tokens=512, + ), + ) + ) + + assert ( + response.model + ), f"{model} ({route}): response carried no model name: {response}" + assert ( + response.choices + ), f"{model} ({route}): response had no choices: {response}" + message = response.choices[0].message + assert ( + message is not None and message.content and message.content.strip() + ), f"{model} ({route}): 200 with an empty completion (#28991): {response}" diff --git a/tests/e2e/llm_translation/test_custom_pricing_e2e.py b/tests/e2e/llm_translation/test_custom_pricing_e2e.py index 58faab61aac..7894b447be9 100644 --- a/tests/e2e/llm_translation/test_custom_pricing_e2e.py +++ b/tests/e2e/llm_translation/test_custom_pricing_e2e.py @@ -1,52 +1,48 @@ -"""Live e2e: a model's custom per-token pricing is loaded, billed, and isolated. +"""Live e2e: a deployment's custom per-token pricing is loaded, billed, and isolated. -The gateway config declares ``custom-priced-flash`` (gemini-2.5-flash underneath) -with input/output rates deliberately far above the canonical gemini price, read -back here from the same config file. Three behaviors are checked independently: +Each test registers the deployment(s) it needs through /model/new (deleted on +teardown) instead of relying on a statically configured model, so the check is +self-contained and never inherits pricing another suite or a stale config left on +the shared proxy. custom-priced-flash sets input/output rates deliberately far +above the canonical gemini price; the isolation sibling shares the same +gemini/gemini-2.5-flash backend but sets no override. Three behaviors are checked +independently: - billing: a real call's logged cost breakdown charges input and output tokens at the custom rates, each component checked separately (a base-rate bill lands ~100x lower; a swapped input/output rate passes a total-only check but not this) -- reporting: /model/info surfaces those rates for the model -- isolation: gemini-2.5-flash shares the same underlying gemini/gemini-2.5-flash - but sets no override, so it must keep its own price; an override that leaks into - the shared cost map misprices it. A regression that reintroduces that leak makes - the sibling's rate match the custom one and fails the isolation check. +- reporting: /model/info surfaces those rates for the deployment +- isolation: the sibling keeps its own price; an override that leaks into the + shared backend cost map (LIT-3897) misprices it, making the sibling's rate match + the custom one and failing the isolation check """ import time -from dataclasses import dataclass -from pathlib import Path import pytest -import yaml from pydantic import BaseModel, RootModel from e2e_config import unique_marker +from e2e_gateway import Gateway from e2e_http import Success, unwrap -from models import ChatBody, ChatMessage, CustomPricing, ModelInfoEntry, SpendLogsParams -from passthrough_client import PassthroughClient +from endpoints_client import EndpointsClient +from lifecycle import ResourceManager +from models import ( + ChatBody, + ChatMessage, + LiteLLMParamsBody, + ModelInfoEntry, + SpendLogsParams, +) pytestmark = pytest.mark.e2e -CUSTOM_MODEL = "custom-priced-flash" -BASE_MODEL = "gemini-2.5-flash" -CONFIG_PATH = Path(__file__).resolve().parents[1] / "gateway" / "litellm-config.yml" - - -@dataclass(frozen=True, slots=True) -class _Rates: - input_per_token: float - output_per_token: float - - -class _ConfiguredModel(BaseModel): - model_name: str - litellm_params: CustomPricing - - -class _GatewayConfig(BaseModel): - model_list: list[_ConfiguredModel] +BACKEND_MODEL = "gemini/gemini-2.5-flash" +GEMINI_API_KEY = "os.environ/GEMINI_API_KEY" +# Deliberately ~100x above canonical gemini-2.5-flash (input 3e-7 / output 2.5e-6) +# so an override that is ignored or under-applied bills at the base rate and fails. +CUSTOM_INPUT_RATE = 5e-05 +CUSTOM_OUTPUT_RATE = 1e-04 class _CostBreakdown(BaseModel): @@ -74,40 +70,60 @@ def _approx_equal(actual: float, expected: float) -> bool: return abs(actual - expected) <= max(1e-9, abs(expected) * 1e-2) -def _configured_pricing(model_name: str) -> _Rates: - """The custom rates declared for `model_name` in the gateway config the proxy - runs with - the source of truth the billed and reported prices are checked - against.""" - config = _GatewayConfig.model_validate(yaml.safe_load(CONFIG_PATH.read_text())) - for entry in config.model_list: - if entry.model_name == model_name: - pricing = entry.litellm_params - assert pricing.input_cost_per_token and pricing.output_cost_per_token, ( - f"{model_name} declares no custom per-token rates in {CONFIG_PATH.name}" - ) - return _Rates(pricing.input_cost_per_token, pricing.output_cost_per_token) - pytest.fail(f"{model_name} not found in {CONFIG_PATH.name}") +def _provision( + endpoints_client: EndpointsClient, + resources: ResourceManager, + prefix: str, + *, + input_cost_per_token: float | None, + output_cost_per_token: float | None, +) -> str: + """Register a fresh gemini/gemini-2.5-flash deployment (deleted on teardown) and + return its model name. With the cost fields set the deployment carries a custom + pricing override; with them None it is a plain sibling on the same backend. The + marker keeps the name unique so concurrent runs on the shared proxy never + collide.""" + model_name = f"{prefix}-{unique_marker()}" + model_id = endpoints_client.create_model( + model_name, + LiteLLMParamsBody( + model=BACKEND_MODEL, + api_key=GEMINI_API_KEY, + input_cost_per_token=input_cost_per_token, + output_cost_per_token=output_cost_per_token, + ), + ) + resources.defer(lambda: endpoints_client.delete_model(model_id)) + return model_name -def _model_info_entry( - entries: list[ModelInfoEntry], model_name: str -) -> ModelInfoEntry: +def _provision_custom_priced( + endpoints_client: EndpointsClient, resources: ResourceManager +) -> str: + return _provision( + endpoints_client, + resources, + "custom-priced-flash", + input_cost_per_token=CUSTOM_INPUT_RATE, + output_cost_per_token=CUSTOM_OUTPUT_RATE, + ) + + +def _model_info_entry(entries: list[ModelInfoEntry], model_name: str) -> ModelInfoEntry: for entry in entries: if entry.model_name == model_name: return entry pytest.fail(f"{model_name} absent from /model/info; the override did not load") -def _poll_breakdown_row( - client: PassthroughClient, key: str, response_id: str | None -) -> _SpendRow: +def _poll_breakdown_row(gateway: Gateway, key: str, response_id: str | None) -> _SpendRow: """Poll /spend/logs until the call's row lands with a cost breakdown (rows flush ~60s behind the call via proxy_batch_write_at).""" - deadline = time.monotonic() + client.gateway.poll_timeout + deadline = time.monotonic() + gateway.poll_timeout while time.monotonic() < deadline: - result = client.gateway.transport.get( + result = gateway.transport.get( "/spend/logs", - headers=client.gateway.transport.master, + headers=gateway.transport.master, params=SpendLogsParams(api_key=key), response_type=_SpendRows, ) @@ -128,85 +144,99 @@ def _poll_breakdown_row( return row if priced and response_id is None: return priced[0] - time.sleep(client.gateway.poll_interval) + time.sleep(gateway.poll_interval) pytest.fail("no spend row with a cost breakdown landed before the deadline") -def test_custom_pricing_is_billed_at_configured_rate( - client: PassthroughClient, scoped_key: str -) -> None: - rates = _configured_pricing(CUSTOM_MODEL) +class TestCustomPricing: + def test_custom_pricing_is_billed_at_configured_rate( + self, + endpoints_client: EndpointsClient, + resources: ResourceManager, + scoped_key: str, + ) -> None: + model = _provision_custom_priced(endpoints_client, resources) - chat = unwrap( - client.gateway.chat( - scoped_key, - ChatBody( - model=CUSTOM_MODEL, - messages=[ - ChatMessage( - role="user", content=f"reply with one word {unique_marker()}" - ) - ], - max_tokens=16, - ), + chat = unwrap( + endpoints_client.gateway.chat( + scoped_key, + ChatBody( + model=model, + messages=[ + ChatMessage( + role="user", content=f"reply with one word {unique_marker()}" + ) + ], + max_tokens=16, + ), + ) ) - ) - row = _poll_breakdown_row(client, scoped_key, chat.id) - assert row.metadata and row.metadata.cost_breakdown # guaranteed by the poll - breakdown = row.metadata.cost_breakdown + row = _poll_breakdown_row(endpoints_client.gateway, scoped_key, chat.id) + assert row.metadata and row.metadata.cost_breakdown # guaranteed by the poll + breakdown = row.metadata.cost_breakdown - prompt = row.prompt_tokens or 0 - completion = row.completion_tokens or 0 - assert prompt > 0 and completion > 0, f"call tokens not logged on the row: {row}" + prompt = row.prompt_tokens or 0 + completion = row.completion_tokens or 0 + assert prompt > 0 and completion > 0, f"call tokens not logged on the row: {row}" - input_cost = breakdown.input_cost - output_cost = breakdown.output_cost - assert input_cost is not None and output_cost is not None, ( - f"row cost breakdown missing input/output cost: {breakdown}" - ) - assert _approx_equal(input_cost, prompt * rates.input_per_token), ( - f"input_cost {input_cost} != {prompt} tokens * {rates.input_per_token} " - f"= {prompt * rates.input_per_token}" - ) - assert _approx_equal(output_cost, completion * rates.output_per_token), ( - f"output_cost {output_cost} != {completion} tokens * {rates.output_per_token} " - f"= {completion * rates.output_per_token}" - ) + input_cost = breakdown.input_cost + output_cost = breakdown.output_cost + assert input_cost is not None and output_cost is not None, ( + f"row cost breakdown missing input/output cost: {breakdown}" + ) + assert _approx_equal(input_cost, prompt * CUSTOM_INPUT_RATE), ( + f"input_cost {input_cost} != {prompt} tokens * {CUSTOM_INPUT_RATE} " + f"= {prompt * CUSTOM_INPUT_RATE}" + ) + assert _approx_equal(output_cost, completion * CUSTOM_OUTPUT_RATE), ( + f"output_cost {output_cost} != {completion} tokens * {CUSTOM_OUTPUT_RATE} " + f"= {completion * CUSTOM_OUTPUT_RATE}" + ) + def test_model_info_reports_custom_pricing( + self, endpoints_client: EndpointsClient, resources: ResourceManager + ) -> None: + model = _provision_custom_priced(endpoints_client, resources) + entry = _model_info_entry(endpoints_client.gateway.model_info(), model) -def test_model_info_reports_custom_pricing(client: PassthroughClient) -> None: - rates = _configured_pricing(CUSTOM_MODEL) - entry = _model_info_entry(client.gateway.model_info(), CUSTOM_MODEL) + assert entry.litellm_params.input_cost_per_token == CUSTOM_INPUT_RATE, ( + f"/model/info litellm_params input rate " + f"{entry.litellm_params.input_cost_per_token} != configured {CUSTOM_INPUT_RATE}" + ) + assert entry.litellm_params.output_cost_per_token == CUSTOM_OUTPUT_RATE, ( + f"/model/info litellm_params output rate " + f"{entry.litellm_params.output_cost_per_token} != configured {CUSTOM_OUTPUT_RATE}" + ) - assert entry.litellm_params.input_cost_per_token == rates.input_per_token, ( - f"/model/info litellm_params input rate " - f"{entry.litellm_params.input_cost_per_token} != configured " - f"{rates.input_per_token}" - ) - assert entry.litellm_params.output_cost_per_token == rates.output_per_token, ( - f"/model/info litellm_params output rate " - f"{entry.litellm_params.output_cost_per_token} != configured " - f"{rates.output_per_token}" - ) + def test_custom_pricing_is_isolated_from_sibling_deployment( + self, endpoints_client: EndpointsClient, resources: ResourceManager + ) -> None: + # Register the override first so its rate is in the backend cost map before + # the sibling resolves; a leak (LIT-3897) would then poison the sibling. + custom = _provision_custom_priced(endpoints_client, resources) + sibling = _provision( + endpoints_client, + resources, + "base-flash", + input_cost_per_token=None, + output_cost_per_token=None, + ) + entries = {entry.model_name: entry for entry in endpoints_client.gateway.model_info()} + custom_entry = entries.get(custom) + sibling_entry = entries.get(sibling) + assert custom_entry is not None, f"{custom} absent from /model/info" + assert sibling_entry is not None, f"{sibling} absent from /model/info" -def test_custom_pricing_is_isolated_from_sibling_deployment( - client: PassthroughClient, -) -> None: - entries = {entry.model_name: entry for entry in client.gateway.model_info()} - custom = entries.get(CUSTOM_MODEL) - base = entries.get(BASE_MODEL) - assert custom is not None, f"{CUSTOM_MODEL} absent from /model/info" - assert base is not None, f"{BASE_MODEL} absent from /model/info" - - # custom-priced-flash overrides pricing; gemini-2.5-flash shares the same - # underlying gemini/gemini-2.5-flash but sets no override, so it must keep its - # own price. Equal rates mean the override leaked into the shared cost map. - assert ( - base.model_info.input_cost_per_token != custom.model_info.input_cost_per_token - ), ( - f"{BASE_MODEL} input rate {base.model_info.input_cost_per_token} matches " - f"{CUSTOM_MODEL}'s override {custom.model_info.input_cost_per_token}; " - f"per-deployment custom pricing is not isolated" - ) + # custom-priced-flash overrides pricing; the sibling shares the same + # gemini/gemini-2.5-flash backend but sets no override, so it must keep its + # own price. Equal rates mean the override leaked into the shared cost map. + assert ( + sibling_entry.model_info.input_cost_per_token + != custom_entry.model_info.input_cost_per_token + ), ( + f"{sibling} input rate {sibling_entry.model_info.input_cost_per_token} matches " + f"{custom}'s override {custom_entry.model_info.input_cost_per_token}; " + f"per-deployment custom pricing is not isolated" + ) diff --git a/tests/e2e/llm_translation/test_deepseek_reasoning_e2e.py b/tests/e2e/llm_translation/test_deepseek_reasoning_e2e.py new file mode 100644 index 00000000000..5adb8c24f9f --- /dev/null +++ b/tests/e2e/llm_translation/test_deepseek_reasoning_e2e.py @@ -0,0 +1,116 @@ +"""Live e2e: DeepSeek reasoner honors a request to turn reasoning OFF. + +DeepSeek's reasoner defaults thinking ON and surfaces the chain as +``message.reasoning_content``. Two documented ways to disable it are +``reasoning_effort="none"`` and ``thinking={"type": "disabled"}``. The DeepSeek +param mapper (``litellm/llms/deepseek/chat/transformation.py`` +``map_openai_params``) forwards both as ``thinking={"type": "disabled"}`` so the +outbound body carries a real disable signal and ``deepseek-reasoner`` returns no +``reasoning_content``. This is the behavior tracked by LIT-3686 / GH #27453. + +The control case proves the model and path work (reasoning is returned when +nothing asks to disable it), so the two disable assertions are meaningful. + +Requires DEEPSEEK_API_KEY on the proxy (tests/e2e/.env). No skip gate: once the +proxy is up, a failure here is real, per the suite's hard-fail contract. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import unwrap +from lifecycle import ResourceManager +from models import ChatBody, ChatMessage, ChatResponse, LiteLLMParamsBody, ThinkingParam +from passthrough_client import PassthroughClient + +pytestmark = pytest.mark.e2e + +REASONER = "deepseek/deepseek-reasoner" +PROMPT = "What is 17 + 26? Answer with just the number." + + +def _register_reasoner(client: PassthroughClient, resources: ResourceManager) -> str: + model = f"e2e-deepseek-reasoner-{unique_marker()}" + model_id = client.gateway.create_model( + model, + LiteLLMParamsBody(model=REASONER, api_key="os.environ/DEEPSEEK_API_KEY"), + ) + resources.defer(lambda: client.gateway.delete_model(model_id)) + return model + + +def _reasoning_content(response: ChatResponse) -> str | None: + assert response.choices, f"reasoner returned no choices: {response}" + message = response.choices[0].message + assert message is not None, f"reasoner choice has no message: {response}" + return message.reasoning_content + + +class TestDeepSeekReasoningDisable: + def test_reasoner_returns_reasoning_by_default( + self, client: PassthroughClient, resources: ResourceManager + ) -> None: + model = _register_reasoner(client, resources) + key = resources.key() + + response = unwrap( + client.gateway.chat( + key, + ChatBody( + model=model, + messages=[ChatMessage(role="user", content=PROMPT)], + max_tokens=64, + ), + ) + ) + reasoning = _reasoning_content(response) + assert reasoning, ( + "control case: deepseek-reasoner returned no reasoning_content with no " + f"disable param, so the disable assertions below can't be trusted: {response}" + ) + + def test_reasoning_effort_none_disables_reasoning( + self, client: PassthroughClient, resources: ResourceManager + ) -> None: + model = _register_reasoner(client, resources) + key = resources.key() + + response = unwrap( + client.gateway.chat( + key, + ChatBody( + model=model, + messages=[ChatMessage(role="user", content=PROMPT)], + max_tokens=64, + reasoning_effort="none", + ), + ) + ) + assert not _reasoning_content(response), ( + "reasoning_effort='none' must disable reasoning, but reasoning_content " + f"is still present: {response}" + ) + + def test_thinking_disabled_disables_reasoning( + self, client: PassthroughClient, resources: ResourceManager + ) -> None: + model = _register_reasoner(client, resources) + key = resources.key() + + response = unwrap( + client.gateway.chat( + key, + ChatBody( + model=model, + messages=[ChatMessage(role="user", content=PROMPT)], + max_tokens=64, + thinking=ThinkingParam(type="disabled"), + ), + ) + ) + assert not _reasoning_content(response), ( + "thinking={'type': 'disabled'} must disable reasoning, but " + f"reasoning_content is still present: {response}" + ) diff --git a/tests/e2e/llm_translation/test_embeddings_endpoint_e2e.py b/tests/e2e/llm_translation/test_embeddings_endpoint_e2e.py new file mode 100644 index 00000000000..56f2de8bd4f --- /dev/null +++ b/tests/e2e/llm_translation/test_embeddings_endpoint_e2e.py @@ -0,0 +1,42 @@ +"""Live e2e: POST /embeddings returns a real vector. + +Registers an OpenAI embedding deployment at runtime and asserts a non-empty, +non-zero vector came back. Migrated from +litellm-regression-tests/tests/test_inference_endpoints.py; the LIT-3167 guard in +tests/e2e/embeddings/ covers the Gemini embedding path. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import require_successful_call +from endpoints_client import EmbeddingsResult, EndpointsClient +from lifecycle import ResourceManager +from models import LiteLLMParamsBody + +pytestmark = pytest.mark.e2e + + +class TestEmbeddingsEndpoint: + def test_embeddings_returns_vector( + self, endpoints_client: EndpointsClient, resources: ResourceManager + ) -> None: + model = f"e2e-embeddings-{unique_marker()}" + model_id = endpoints_client.create_model( + model, + LiteLLMParamsBody( + model="openai/text-embedding-3-small", api_key="os.environ/OPENAI_API_KEY" + ), + ) + resources.defer(lambda: endpoints_client.delete_model(model_id)) + key = resources.key() + + result = endpoints_client.embeddings(key, model, "Say this is a test!") + require_successful_call(result) + parsed = EmbeddingsResult.model_validate_json(result.body) + assert parsed.first_vector, f"/embeddings returned no vector: {result.body[:300]}" + assert any(component != 0.0 for component in parsed.first_vector), ( + f"embedding vector is all zeros: {result.body[:300]}" + ) diff --git a/tests/e2e/llm_translation/test_image_generation_e2e.py b/tests/e2e/llm_translation/test_image_generation_e2e.py new file mode 100644 index 00000000000..4d2211f3be4 --- /dev/null +++ b/tests/e2e/llm_translation/test_image_generation_e2e.py @@ -0,0 +1,42 @@ +"""Live e2e: POST /v1/images/generations returns an image. + +Registers an OpenAI image deployment at runtime and asserts the response carries a +generated image (url or base64). Migrated from +litellm-regression-tests/tests/test_inference_endpoints.py. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import require_successful_call +from endpoints_client import EndpointsClient, ImagesResult +from lifecycle import ResourceManager +from models import LiteLLMParamsBody + +pytestmark = pytest.mark.e2e + + +class TestImageGeneration: + def test_image_generation_returns_image( + self, endpoints_client: EndpointsClient, resources: ResourceManager + ) -> None: + model = f"e2e-image-{unique_marker()}" + model_id = endpoints_client.create_model( + model, + LiteLLMParamsBody( + model="openai/gpt-image-1-mini", api_key="os.environ/OPENAI_API_KEY" + ), + ) + resources.defer(lambda: endpoints_client.delete_model(model_id)) + key = resources.key() + + result = endpoints_client.images(key, model, "Draw a cute cat") + require_successful_call(result) + parsed = ImagesResult.model_validate_json(result.body) + assert parsed.data, f"/images/generations returned no data: {result.body[:300]}" + first = parsed.data[0] + assert first.b64_json or first.url, ( + f"generated image has neither b64_json nor url: {result.body[:300]}" + ) diff --git a/tests/e2e/llm_translation/test_messages_e2e.py b/tests/e2e/llm_translation/test_messages_e2e.py new file mode 100644 index 00000000000..b0a48f22118 --- /dev/null +++ b/tests/e2e/llm_translation/test_messages_e2e.py @@ -0,0 +1,39 @@ +"""Live e2e: POST /v1/messages (Anthropic Messages API) returns a real completion. + +Registers an Anthropic deployment at runtime, drives the Messages endpoint through +the gateway, and asserts an assistant message with text came back. Migrated from +litellm-regression-tests/tests/test_inference_endpoints.py. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import require_successful_call +from endpoints_client import EndpointsClient, MessagesResult +from lifecycle import ResourceManager +from models import LiteLLMParamsBody + +pytestmark = pytest.mark.e2e + + +class TestAnthropicMessages: + def test_messages_returns_completion( + self, endpoints_client: EndpointsClient, resources: ResourceManager + ) -> None: + model = f"e2e-messages-{unique_marker()}" + model_id = endpoints_client.create_model( + model, + LiteLLMParamsBody( + model="anthropic/claude-haiku-4-5", api_key="os.environ/ANTHROPIC_API_KEY" + ), + ) + resources.defer(lambda: endpoints_client.delete_model(model_id)) + key = resources.key() + + result = endpoints_client.messages(key, model, "reply with one word") + require_successful_call(result) + parsed = MessagesResult.model_validate_json(result.body) + assert parsed.role == "assistant", f"unexpected role: {result.body[:300]}" + assert parsed.text.strip(), f"/v1/messages returned no text: {result.body[:300]}" diff --git a/tests/e2e/llm_translation/test_ocr_rust_e2e.py b/tests/e2e/llm_translation/test_ocr_rust_e2e.py index 44908da2729..921010e5eae 100644 --- a/tests/e2e/llm_translation/test_ocr_rust_e2e.py +++ b/tests/e2e/llm_translation/test_ocr_rust_e2e.py @@ -1,31 +1,32 @@ """Live e2e: Rust-backed OCR is reachable through the gateway across providers. -The gateway config declares one rust-ocr deployment per provider (mistral, -azure_ai, azure document intelligence, vertex mistral, vertex deepseek). Start the -proxy with the Rust OCR path enabled: +Each provider's OCR deployment is registered at runtime via /model/new and deleted +on teardown, so nothing is hardcoded into the gateway config. Every provider is its +own typed OcrProvider below: it owns the model id and the os.environ/* credential +references the proxy resolves at call time, so adding a provider is a new type +rather than another inline body. Start the proxy with the Rust OCR path enabled: - LITELLM_USE_RUST_OCR=1 litellm --config tests/e2e/gateway/litellm-config.yml - -Three behaviors are checked: the config declares every provider's deployment (a -pure config read, no proxy needed); the running proxy loaded them onto /model/info; -and each one returns a well-formed OCR document over /v1/ocr. Per the e2e -"skip on environment, fail on behavior" rule, the proxy-backed cases skip when no -proxy answers but fail (never skip) once a request reaches it, so a provider whose -credentials are missing surfaces as a hard failure rather than silent green. +Each case creates its deployment, drives a real /v1/ocr call, and asserts a +well-formed OCR document comes back. Per the e2e "skip on environment, fail on +behavior" rule, a case skips when no proxy answers but fails (never skips) once a +request reaches it: the proxy fetches each provider's referenced secrets, so a +missing credential surfaces as a live provider error rather than silent green. """ from __future__ import annotations from dataclasses import dataclass -from pathlib import Path +from typing import Protocol import pytest -import yaml -from pydantic import BaseModel +from e2e_config import unique_marker from e2e_http import unwrap -from models import OcrBody, OcrDocument, OcrResponse -from passthrough_client import PassthroughClient +from endpoints_client import EndpointsClient +from lifecycle import ResourceManager +from models import LiteLLMParamsBody, OcrBody, OcrDocument, OcrResponse + +pytestmark = pytest.mark.e2e # Tiny in-repo fixtures served via jsdelivr (sha-pinned, immutable) so the request # bodies stay stable across runs. @@ -40,53 +41,96 @@ TEST_IMAGE_URL = ( "/tests/image_gen_tests/test_image.png" ) -CONFIG_PATH = Path(__file__).resolve().parents[1] / "gateway" / "litellm-config.yml" + +class OcrProvider(Protocol): + """One OCR provider's deployment config: its model id plus the os.environ/* + credential references the proxy resolves at call time. Each provider owns which + env vars it reads, so a new provider is a new type, not another inline body.""" + + def litellm_params(self) -> LiteLLMParamsBody: ... + + +@dataclass(frozen=True, slots=True) +class MistralOcr: + model: str = "mistral/mistral-ocr-latest" + + def litellm_params(self) -> LiteLLMParamsBody: + return LiteLLMParamsBody(model=self.model, api_key="os.environ/MISTRAL_API_KEY") + + +@dataclass(frozen=True, slots=True) +class AzureAiOcr: + """azure_ai (mistral) OCR. The rust OCR path resolves credentials itself from + AZURE_AI_API_BASE / AZURE_AI_API_KEY when the deployment leaves them unset; it + does NOT unwrap an `os.environ/*` reference passed as api_base (it would be sent + to Azure verbatim), so we omit them and let litellm read the env vars by name.""" + + model: str + + def litellm_params(self) -> LiteLLMParamsBody: + return LiteLLMParamsBody(model=self.model) + + +@dataclass(frozen=True, slots=True) +class AzureDocIntelligenceOcr: + """azure_ai Document Intelligence OCR. A separate Azure resource from the + mistral one, so it has its own env vars: AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT / + AZURE_DOCUMENT_INTELLIGENCE_API_KEY, which the OCR config resolves from the + doc-intelligence model name when api_base/api_key are left unset.""" + + model: str = "azure_ai/doc-intelligence/prebuilt-layout" + + def litellm_params(self) -> LiteLLMParamsBody: + return LiteLLMParamsBody(model=self.model) + + +@dataclass(frozen=True, slots=True) +class VertexOcr: + """Vertex AI OCR (Mistral publisher). Only the location (not a secret) is set; + the project and credentials are left unset so the gateway resolves VERTEXAI_PROJECT + and VERTEXAI_CREDENTIALS from its own environment by name, keeping every secret on + the gateway like the azure_ai cases above. This is deliberate: the OCR path reads + vertex_project verbatim from litellm_params and never unwraps an `os.environ/*` + ref, so passing one would put the literal string in the request URL.""" + + model: str + location: str + + def litellm_params(self) -> LiteLLMParamsBody: + return LiteLLMParamsBody(model=self.model, vertex_location=self.location) @dataclass(frozen=True, slots=True) class _OcrCase: - model: str + suffix: str + provider: OcrProvider document: OcrDocument RUST_OCR_CASES: tuple[_OcrCase, ...] = ( _OcrCase( - "rust-ocr-mistral", + "mistral", + MistralOcr(), OcrDocument(type="document_url", document_url=TEST_PDF_URL), ), _OcrCase( - "rust-ocr-azure-ai", + "azure-ai", + AzureAiOcr("azure_ai/mistral-document-ai-2512"), OcrDocument(type="document_url", document_url=TEST_PDF_URL), ), _OcrCase( - "rust-ocr-azure-document-intelligence", + "azure-document-intelligence", + AzureDocIntelligenceOcr(), OcrDocument(type="document_url", document_url=TEST_PDF_URL), ), _OcrCase( - "rust-ocr-vertex-mistral", + "vertex-mistral", + VertexOcr("vertex_ai/mistral-ocr-2505", "us-central1"), OcrDocument(type="document_url", document_url=TEST_PDF_URL), ), - _OcrCase( - "rust-ocr-vertex-deepseek", - OcrDocument(type="image_url", image_url=TEST_IMAGE_URL), - ), ) -_EXPECTED_MODELS = frozenset(case.model for case in RUST_OCR_CASES) -_CASE_IDS = tuple(case.model.removeprefix("rust-ocr-") for case in RUST_OCR_CASES) - - -class _ConfiguredModel(BaseModel): - model_name: str - - -class _GatewayConfig(BaseModel): - model_list: list[_ConfiguredModel] - - -def _configured_model_names() -> frozenset[str]: - config = _GatewayConfig.model_validate(yaml.safe_load(CONFIG_PATH.read_text())) - return frozenset(entry.model_name for entry in config.model_list) +_CASE_IDS = tuple(case.suffix for case in RUST_OCR_CASES) def _assert_ocr_document(response: OcrResponse) -> None: @@ -96,22 +140,17 @@ def _assert_ocr_document(response: OcrResponse) -> None: assert response.pages[0].markdown is not None, "first page has no markdown" -def test_rust_ocr_models_declared_in_gateway_config() -> None: - """Pure config read (no proxy): every provider's rust-ocr deployment the suite - exercises is declared in the gateway config the proxy runs with. A case added - here without a matching deployment fails before any live call is attempted.""" - missing = _EXPECTED_MODELS - _configured_model_names() - assert not missing, f"rust-ocr models absent from {CONFIG_PATH.name}: {missing}" - - -@pytest.mark.e2e class TestRustOcrGateway: - def test_gateway_loaded_rust_ocr_models(self, client: PassthroughClient) -> None: - loaded = frozenset(entry.model_name for entry in client.gateway.model_info()) - missing = _EXPECTED_MODELS - loaded - assert not missing, f"proxy did not load rust-ocr models: {missing}" - @pytest.mark.parametrize("case", RUST_OCR_CASES, ids=_CASE_IDS) - def test_rust_ocr_response(self, client: PassthroughClient, scoped_key: str, case: _OcrCase) -> None: - response = unwrap(client.gateway.ocr(scoped_key, OcrBody(model=case.model, document=case.document))) + def test_rust_ocr_response( + self, endpoints_client: EndpointsClient, resources: ResourceManager, case: _OcrCase + ) -> None: + model = f"rust-ocr-{case.suffix}-{unique_marker()}" + model_id = endpoints_client.create_model(model, case.provider.litellm_params()) + resources.defer(lambda: endpoints_client.delete_model(model_id)) + key = resources.key() + + response = unwrap(endpoints_client.gateway.ocr(key, OcrBody(model=model, document=case.document))) _assert_ocr_document(response) + + diff --git a/tests/e2e/llm_translation/test_provider_features_e2e.py b/tests/e2e/llm_translation/test_provider_features_e2e.py new file mode 100644 index 00000000000..822a1d8d4d5 --- /dev/null +++ b/tests/e2e/llm_translation/test_provider_features_e2e.py @@ -0,0 +1,63 @@ +"""Live e2e for model-specific request features: service_tier. + +Each case asserts the feature took effect, not just a 200. + +service_tier is an OpenAI concept. The proxy forwards it and the provider echoes +the tier back on the response, so sending a non-default tier ("priority") and +reading it back off ``service_tier`` proves the param was honored end to end; +litellm's own default injection (and service_tier="auto") both report "default", +so a "priority" echo can only come from the request being forwarded. "flex" is +avoided here because it is capacity-constrained and returns a transient 429 when +flex resources are unavailable. Bedrock and Vertex do not accept service_tier, so +that cell is OpenAI-only by design. + +Prompt caching lives in test_cache_control.py. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import unwrap +from lifecycle import ResourceManager +from models import ChatBody, ChatMessage, LiteLLMParamsBody +from passthrough_client import PassthroughClient + +pytestmark = pytest.mark.e2e + +SERVICE_TIER = "priority" + + +class TestServiceTier: + @pytest.mark.covers( + "llm.chat_completions.openai.service_tier.nonstream.works", exercised_on=[] + ) + def test_openai_service_tier_is_echoed( + self, client: PassthroughClient, resources: ResourceManager + ) -> None: + model = f"e2e-service-tier-{unique_marker()}" + model_id = client.gateway.create_model( + model, + LiteLLMParamsBody( + model="openai/gpt-5.5", api_key="os.environ/OPENAI_API_KEY" + ), + ) + resources.defer(lambda: client.gateway.delete_model(model_id)) + key = resources.key() + + response = unwrap( + client.gateway.chat( + key, + ChatBody( + model=model, + messages=[ChatMessage(role="user", content="reply with one word")], + max_tokens=64, + service_tier=SERVICE_TIER, + ), + ) + ) + assert response.service_tier == SERVICE_TIER, ( + f"service_tier not honored: sent {SERVICE_TIER!r}, response reported " + f"{response.service_tier!r} ({response})" + ) diff --git a/tests/e2e/llm_translation/test_rerank_e2e.py b/tests/e2e/llm_translation/test_rerank_e2e.py new file mode 100644 index 00000000000..4b30ac1ea5c --- /dev/null +++ b/tests/e2e/llm_translation/test_rerank_e2e.py @@ -0,0 +1,49 @@ +"""Live e2e: POST /v1/rerank ranks documents by relevance. + +Registers a Cohere rerank deployment at runtime and asserts the endpoint returns +scored results within the requested top_n. Migrated from +litellm-regression-tests/tests/test_inference_endpoints.py. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import require_successful_call +from endpoints_client import EndpointsClient, RerankResult +from lifecycle import ResourceManager +from models import LiteLLMParamsBody + +pytestmark = pytest.mark.e2e + +DOCUMENTS = [ + "Carson City is the capital city of the American state of Nevada.", + "The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean.", + "Washington, D.C. is the capital of the United States.", + "Capital punishment has existed in the United States since before it was a country.", +] + + +class TestRerank: + def test_rerank_scores_top_n( + self, endpoints_client: EndpointsClient, resources: ResourceManager + ) -> None: + model = f"e2e-rerank-{unique_marker()}" + model_id = endpoints_client.create_model( + model, + LiteLLMParamsBody(model="cohere/rerank-v3.5", api_key="os.environ/COHERE_API_KEY"), + ) + resources.defer(lambda: endpoints_client.delete_model(model_id)) + key = resources.key() + + result = endpoints_client.rerank( + key, model, "What is the capital of the United States?", DOCUMENTS, top_n=3 + ) + require_successful_call(result) + parsed = RerankResult.model_validate_json(result.body) + assert parsed.results, f"/rerank returned no results: {result.body[:300]}" + assert len(parsed.results) <= 3, f"top_n=3 not honored: {result.body[:300]}" + assert parsed.results[0].relevance_score is not None, ( + f"top rerank result has no relevance_score: {result.body[:300]}" + ) diff --git a/tests/e2e/llm_translation/test_responses_e2e.py b/tests/e2e/llm_translation/test_responses_e2e.py new file mode 100644 index 00000000000..743de79880f --- /dev/null +++ b/tests/e2e/llm_translation/test_responses_e2e.py @@ -0,0 +1,36 @@ +"""Live e2e: POST /v1/responses returns a real completion. + +Registers an OpenAI deployment at runtime, drives the Responses API through the +gateway, and asserts output text came back. Migrated from +litellm-regression-tests/tests/test_inference_endpoints.py. +""" + +from __future__ import annotations + +import pytest + +from e2e_config import unique_marker +from e2e_http import require_successful_call +from endpoints_client import EndpointsClient, ResponsesResult +from lifecycle import ResourceManager +from models import LiteLLMParamsBody + +pytestmark = pytest.mark.e2e + + +class TestResponses: + def test_responses_returns_completion( + self, endpoints_client: EndpointsClient, resources: ResourceManager + ) -> None: + model = f"e2e-responses-{unique_marker()}" + model_id = endpoints_client.create_model( + model, + LiteLLMParamsBody(model="openai/gpt-4o-mini", api_key="os.environ/OPENAI_API_KEY"), + ) + resources.defer(lambda: endpoints_client.delete_model(model_id)) + key = resources.key() + + result = endpoints_client.responses(key, model, "reply with one word") + require_successful_call(result) + parsed = ResponsesResult.model_validate_json(result.body) + assert parsed.text.strip(), f"/responses returned no output text: {result.body[:300]}" diff --git a/tests/e2e/llm_translation/test_vertex_passthrough_e2e.py b/tests/e2e/llm_translation/test_vertex_passthrough_e2e.py new file mode 100644 index 00000000000..78d2bb358d2 --- /dev/null +++ b/tests/e2e/llm_translation/test_vertex_passthrough_e2e.py @@ -0,0 +1,166 @@ +"""Live e2e: a native Vertex AI generateContent call over the proxy's /vertex_ai +passthrough is forwarded to Vertex and still logged as a costed SpendLogs row. + +Ports the de-flake of the SDK-based spend test (#31689). That test configured the +vertexai SDK with an api_endpoint override pointing at the proxy, but the SDK +intermittently ignored the override and billed the public Vertex endpoint directly, +so the request never reached LiteLLM and no spend was recorded; the bypass, not +logging lag, was the flake. Driving raw HTTP through the shared transport always +reaches the proxy, which the harness already guarantees, so the only residual +nondeterminism is the ~60s async spend flush the poll absorbs. + +The vertex deployment is added at runtime through the management endpoint rather than +declared in the gateway config: the test POSTs /model/new with use_in_pass_through so +the proxy registers that deployment's service account for the /vertex_ai route, then +deletes it on teardown. The credential is the one the proxy already holds (read from +the same VERTEXAI_CREDENTIALS the deployment uses), so the passthrough call sends only +its litellm virtual key in x-litellm-api-key and no upstream bearer, and the proxy +mints the Vertex token itself. The test never mints a token. + +Asserts both sides of the promise: the forward succeeds (2xx with a candidate) and +the costed row lands (call_type pass_through_endpoint, vertex_ai provider, a gemini +model, spend > 0), correlated by the x-litellm-call-id header. +""" + +import os + +import pytest +from pydantic import BaseModel + +from e2e_config import unique_marker +from e2e_http import NoBody, require_successful_call, unwrap +from lifecycle import ResourceManager +from models import SpendLogRow +from passthrough_client import PassthroughClient + +pytestmark = pytest.mark.e2e + +VERTEX_MODEL = "gemini-2.5-flash" +# The added deployment's region and the passthrough URL's region are the same constant, +# so they always agree; the proxy registers passthrough credentials per project+region. +VERTEX_LOCATION = os.environ.get("VERTEXAI_LOCATION", "us-central1") + + +@pytest.fixture(scope="session") +def vertex_project() -> str: + """The Vertex project to bill, read from the same VERTEXAI_PROJECT the proxy uses. + Skip when unset, since that is an environment gap rather than a behavior failure.""" + project = os.environ.get("VERTEXAI_PROJECT") + if not project: + pytest.skip("set VERTEXAI_PROJECT (the project the vertex deployment bills)") + return project + + +@pytest.fixture(scope="session") +def vertex_credentials() -> str: + """The service-account JSON the added deployment authenticates with, the same + VERTEXAI_CREDENTIALS the proxy holds. Skip when unset.""" + credentials = os.environ.get("VERTEXAI_CREDENTIALS") + if not credentials: + pytest.skip("set VERTEXAI_CREDENTIALS (the vertex service-account JSON)") + return credentials + + +class _VertexDeploymentParams(BaseModel): + model: str + vertex_project: str + vertex_location: str + vertex_credentials: str + use_in_pass_through: bool + + +class _ModelInfoId(BaseModel): + id: str + + +class _ModelNewBody(BaseModel): + model_name: str + litellm_params: _VertexDeploymentParams + model_info: _ModelInfoId + + +class _ModelNewResponse(BaseModel): + model_id: str + + +class _ModelDeleteBody(BaseModel): + id: str + + +def _add_vertex_passthrough_model( + client: PassthroughClient, model_name: str, project: str, credentials: str +) -> str: + return unwrap( + client.gateway.transport.post( + "/model/new", + headers=client.gateway.transport.master, + json=_ModelNewBody( + model_name=model_name, + litellm_params=_VertexDeploymentParams( + model=f"vertex_ai/{VERTEX_MODEL}", + vertex_project=project, + vertex_location=VERTEX_LOCATION, + vertex_credentials=credentials, + use_in_pass_through=True, + ), + model_info=_ModelInfoId(id=model_name), + ), + response_type=_ModelNewResponse, + ) + ).model_id + + +def _delete_model(client: PassthroughClient, model_id: str) -> None: + _ = client.gateway.transport.post( + "/model/delete", + headers=client.gateway.transport.master, + json=_ModelDeleteBody(id=model_id), + response_type=NoBody, + ) + + +def _costed_row(client: PassthroughClient, call_id: str | None) -> SpendLogRow: + """The passthrough call's SpendLogs row, polled until it carries a cost. + + A 2xx passthrough call that produced no costed row is a hard failure, not a skip: + a billed Vertex call that LiteLLM did not track is the exact regression #31689 + guards against.""" + assert call_id, "vertex passthrough response had no x-litellm-call-id header" + rows = client.gateway.poll_logs_for_request_id( + call_id, + predicate=lambda rs: (rs[0].spend or 0) > 0, + ) + assert rows, f"no SpendLogs row for vertex passthrough call_id {call_id}" + row = rows[0] + assert row.call_type == "pass_through_endpoint", f"unexpected call_type: {row}" + assert (row.spend or 0) > 0, f"vertex passthrough call was not costed: {row}" + assert row.status == "success", f"unexpected status: {row}" + return row + + +class TestVertexPassthroughSpendTracking: + def test_vertex_passthrough_via_managed_model_logs_cost( + self, + client: PassthroughClient, + scoped_key: str, + resources: ResourceManager, + vertex_project: str, + vertex_credentials: str, + ) -> None: + model_name = f"e2e-vertex-pt-{unique_marker()}" + model_id = _add_vertex_passthrough_model(client, model_name, vertex_project, vertex_credentials) + resources.defer(lambda: _delete_model(client, model_id)) + + result = client.vertex_generate( + key=scoped_key, + project=vertex_project, + location=VERTEX_LOCATION, + model=VERTEX_MODEL, + text=f"reply with one word {unique_marker()}", + ) + require_successful_call(result) + assert '"candidates"' in result.body, f"vertex passthrough returned no candidates: {result.body[:300]}" + + row = _costed_row(client, result.call_id) + assert row.custom_llm_provider == "vertex_ai", f"passthrough spend logged under the wrong provider: {row}" + assert "gemini" in (row.model or ""), f"unexpected model in spend log: {row}" diff --git a/tests/e2e/logging/conftest.py b/tests/e2e/logging/conftest.py new file mode 100644 index 00000000000..567355f23ac --- /dev/null +++ b/tests/e2e/logging/conftest.py @@ -0,0 +1,43 @@ +"""Fixtures for the logging e2e suite. + +Missing proxy, provider keys, or integration credentials are hard failures. +Never pytest.skip from this suite for environment gaps. +""" + +from __future__ import annotations + +import os + +import pytest + +from logging_client import LangfuseCreds, LoggingClient, build_logging_client, load_langfuse_creds + + +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line( + "markers", + "covers: registry cell a test covers, e.g. logging.langfuse.success.logs_spend", + ) + + +@pytest.fixture(scope="session") +def client() -> LoggingClient: + """The logging suite's client: holds the shared Gateway so `resources` / + `scoped_key` clean up keys and teams, and adds `/metrics` scraping plus + Langfuse read-back.""" + return build_logging_client() + + +@pytest.fixture +def datadog_creds() -> None: + """Require Datadog shipping credentials. Hard-fail when absent; never skip.""" + if not (os.getenv("DD_API_KEY") and os.getenv("DD_SITE")): + pytest.fail( + "Datadog e2e requires DD_API_KEY and DD_SITE; missing credentials is a hard failure, not a skip" + ) + + +@pytest.fixture(scope="session") +def langfuse_creds() -> LangfuseCreds: + """Require real Langfuse cloud credentials for team callback + trace poll.""" + return load_langfuse_creds() diff --git a/tests/e2e/logging/logging_client.py b/tests/e2e/logging/logging_client.py new file mode 100644 index 00000000000..6071e657bd3 --- /dev/null +++ b/tests/e2e/logging/logging_client.py @@ -0,0 +1,572 @@ +"""Client for the logging e2e suite: team/key/org-scoped Langfuse OTEL callbacks, +chat (including tools), Prometheus scrape, and Langfuse observation read-back. + +Holds the shared Gateway so the ``resources`` fixture cleans up keys, teams, +users, orgs, and models it creates. External Langfuse reads go through +``e2e_http`` (the only module allowed to call ``requests.*``). + +Uses the ``langfuse_otel`` callback (OTLP to ``{host}/api/public/otel``), not +the classic ``langfuse`` SDK callback. OTEL generations land as name +``litellm_request``; correlate by unique prompt marker and ``user_api_key_alias`` +in metadata. Spend is on ``calculatedTotalCost`` (StandardLogging response_cost). +""" + +from __future__ import annotations + +import base64 +import json +import os +import time +from dataclasses import dataclass +from typing import Literal + +import pytest +from pydantic import BaseModel, ConfigDict, Field, JsonValue, TypeAdapter, ValidationError + +from e2e_config import POLL_INTERVAL, POLL_TIMEOUT +from e2e_gateway import Gateway, build_gateway +from e2e_http import ( + URL, + AuthHeaders, + NoBody, + StreamingResponse, + Success, + get, + unwrap, +) +from models import ( + ChatBody, + ChatMessage, + ChatResponse, + ChatTool, + ChatToolFunction, + KeyGenerateBody, + KeyLoggingCallback, + KeyLoggingCallbackVars, + KeyMetadata, + LiteLLMParamsBody, + OrgDeleteBody, + OrgNewBody, + OrgNewResponse, + SpendLogRow, + TeamDeleteBody, + TeamNewBody, + TeamNewResponse, + UserDeleteBody, + UserNewBody, + UserNewResponse, +) + +# Deliberately invalid *upstream provider* key for failure-path tests. +# Not a LiteLLM virtual key; OpenAI must reject it after the proxy accepts the call. +INVALID_UPSTREAM_API_KEY = "sk-upstream-invalid-for-langfuse-e2e-only" + +WEATHER_TOOL = ChatTool( + type="function", + function=ChatToolFunction( + name="get_weather", + description="Get the current weather for a city", + parameters={ + "type": "object", + "properties": {"city": {"type": "string"}}, + "required": ["city"], + }, + ), +) + + +class TeamCallbackBody(BaseModel): + callback_name: Literal["langfuse_otel", "langfuse", "langsmith", "gcs"] + callback_type: Literal["success", "failure", "success_and_failure"] + callback_vars: dict[str, str] + + +class TeamCallbackResponse(BaseModel): + model_config = ConfigDict(extra="ignore") + + status: str + + +class GuardrailLitellmParams(BaseModel): + guardrail: str + mode: str + default_on: bool = False + rules: list[dict[str, object]] | None = None + default_action: str | None = None + on_disallowed_action: str | None = None + + +class GuardrailSpec(BaseModel): + guardrail_name: str + litellm_params: GuardrailLitellmParams + + +class CreateGuardrailBody(BaseModel): + guardrail: GuardrailSpec + + +class CreateGuardrailResponse(BaseModel): + model_config = ConfigDict(extra="ignore") + + guardrail_id: str | None = None + guardrail_name: str | None = None + + +class LangfuseObservation(BaseModel): + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + id: str + trace_id: str | None = Field(default=None, alias="traceId") + name: str | None = None + type: str | None = None + calculated_total_cost: float | None = Field(default=None, alias="calculatedTotalCost") + level: str | None = None + input: object | None = None + output: object | None = None + metadata: object | None = None + usage: object | None = None + usage_details: object | None = Field(default=None, alias="usageDetails") + model: str | None = None + + +class LangfuseObservationList(BaseModel): + model_config = ConfigDict(extra="ignore") + + data: list[LangfuseObservation] = [] + + +class LangfuseListParams(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + limit: int = 100 + trace_id: str | None = Field(default=None, alias="traceId") + name: str | None = None + from_start_time: str | None = Field(default=None, alias="fromStartTime") + + +@dataclass(frozen=True, slots=True) +class LangfuseCreds: + public_key: str + secret_key: str + host: str + + @property + def auth_headers(self) -> AuthHeaders: + token = base64.b64encode(f"{self.public_key}:{self.secret_key}".encode()).decode() + return AuthHeaders(authorization=f"Basic {token}") + + def callback_vars(self) -> dict[str, str]: + return { + "langfuse_public_key": self.public_key, + "langfuse_secret_key": self.secret_key, + "langfuse_host": self.host, + } + + def key_logging_metadata(self) -> KeyMetadata: + return KeyMetadata( + logging=[ + KeyLoggingCallback( + callback_name="langfuse_otel", + callback_type="success_and_failure", + callback_vars=KeyLoggingCallbackVars( + langfuse_public_key=self.public_key, + langfuse_secret_key=self.secret_key, + langfuse_host=self.host, + ), + ) + ] + ) + + +def load_langfuse_creds() -> LangfuseCreds: + public_key = os.getenv("LANGFUSE_PUBLIC_KEY") + secret_key = os.getenv("LANGFUSE_SECRET_KEY") + host = (os.getenv("LANGFUSE_BASE_URL") or os.getenv("LANGFUSE_HOST") or "").rstrip("/") + if not (public_key and secret_key and host): + pytest.fail( + "Langfuse e2e requires LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, and " + "LANGFUSE_BASE_URL (or LANGFUSE_HOST); missing credentials is a hard failure, not a skip" + ) + return LangfuseCreds(public_key=public_key, secret_key=secret_key, host=host) + + +def observation_spend(obs: LangfuseObservation) -> float | None: + """Langfuse calculatedTotalCost is populated from StandardLogging response_cost.""" + return obs.calculated_total_cost + + +def costs_agree(expected: float, actual: float, *, rel_tol: float = 0.05) -> bool: + """Costs agree within 5% relative (or 1e-9 absolute for near-zero).""" + return abs(expected - actual) <= max(1e-9, abs(expected) * rel_tol) + + +_COMPLETION_BODY_ADAPTER: TypeAdapter[dict[str, JsonValue]] = TypeAdapter(dict[str, JsonValue]) + + +def completion_response_id(body: str) -> str | None: + """SpendLogs.request_id is the chat completion body id, not x-litellm-call-id.""" + if not body or body == "": + return None + try: + parsed = _COMPLETION_BODY_ADAPTER.validate_json(body) + except ValidationError: + return None + raw = parsed.get("id") + return raw if isinstance(raw, str) and raw else None + + +def _matches_run(obs: LangfuseObservation, *, key_alias: str, prompt_marker: str) -> bool: + """Match a Langfuse generation for this run. + + langfuse_otel names generations ``litellm_request`` (not ``litellm:{alias}``). + Prefer the unique prompt marker in input; fall back to key alias in metadata + (user_api_key_alias) or the classic SDK generation name. + """ + if prompt_marker and prompt_marker in json.dumps(obs.input, default=str): + return True + meta_blob = json.dumps(obs.metadata, default=str) if obs.metadata is not None else "" + if key_alias and key_alias in meta_blob: + return True + if obs.name == f"litellm:{key_alias}": + return True + return False + + +def observation_mentions_tool(obs: LangfuseObservation, tool_name: str) -> bool: + blob = json.dumps( + {"input": obs.input, "output": obs.output, "metadata": obs.metadata}, + default=str, + ) + return tool_name in blob + + +def observation_has_guardrail(obs: LangfuseObservation, *, guardrail_name: str) -> bool: + blob = json.dumps(obs.metadata, default=str) if obs.metadata is not None else "" + if guardrail_name in blob or "guardrail" in blob.lower(): + return True + if obs.name is not None and "guardrail" in obs.name.lower(): + return True + return False + + +@dataclass(frozen=True, slots=True) +class LoggingClient: + gateway: Gateway + + def key_with_alias( + self, + alias: str, + *, + models: list[str], + team_id: str | None = None, + user_id: str | None = None, + organization_id: str | None = None, + metadata: KeyMetadata | None = None, + ) -> str: + return self.gateway.generate_key( + KeyGenerateBody( + key_alias=alias, + models=models, + user_id=user_id or f"e2e-{alias}", + team_id=team_id, + organization_id=organization_id, + metadata=metadata, + ) + ) + + def delete_key(self, key: str) -> None: + self.gateway.delete_key(key) + + def create_team( + self, + alias: str, + *, + models: list[str], + organization_id: str | None = None, + ) -> str: + return unwrap( + self.gateway.transport.post( + "/team/new", + headers=self.gateway.transport.master, + json=TeamNewBody( + team_alias=alias, + models=models, + organization_id=organization_id, + ), + response_type=TeamNewResponse, + ) + ).team_id + + def delete_team(self, team_id: str) -> None: + _ = self.gateway.transport.post( + "/team/delete", + headers=self.gateway.transport.master, + json=TeamDeleteBody(team_ids=[team_id]), + response_type=NoBody, + ) + + def create_user(self, *, user_email: str, user_id: str | None = None) -> str: + return unwrap( + self.gateway.transport.post( + "/user/new", + headers=self.gateway.transport.master, + json=UserNewBody( + user_email=user_email, + user_role="internal_user", + user_id=user_id, + ), + response_type=UserNewResponse, + ) + ).user_id + + def delete_user(self, user_id: str) -> None: + _ = self.gateway.transport.post( + "/user/delete", + headers=self.gateway.transport.master, + json=UserDeleteBody(user_ids=[user_id]), + response_type=NoBody, + ) + + def create_org(self, alias: str, *, models: list[str]) -> str: + return unwrap( + self.gateway.transport.post( + "/organization/new", + headers=self.gateway.transport.master, + json=OrgNewBody(organization_alias=alias, models=models), + response_type=OrgNewResponse, + ) + ).organization_id + + def delete_org(self, organization_id: str) -> None: + _ = self.gateway.transport.delete( + "/organization/delete", + headers=self.gateway.transport.master, + json=OrgDeleteBody(organization_ids=[organization_id]), + response_type=NoBody, + ) + + def add_team_langfuse_callback( + self, + team_id: str, + creds: LangfuseCreds, + *, + callback_type: Literal["success", "failure", "success_and_failure"] = "success_and_failure", + ) -> None: + response = unwrap( + self.gateway.transport.post( + f"/team/{team_id}/callback", + headers=self.gateway.transport.master, + json=TeamCallbackBody( + callback_name="langfuse_otel", + callback_type=callback_type, + callback_vars=creds.callback_vars(), + ), + response_type=TeamCallbackResponse, + ) + ) + assert response.status == "success", ( + f"POST /team/{team_id}/callback must return status=success; got {response.status!r}" + ) + + def create_tool_permission_guardrail(self, name: str, *, allowed_tool: str) -> str: + """Register a tool_permission guardrail that allows one tool and denies the rest.""" + response = unwrap( + self.gateway.transport.post( + "/guardrails", + headers=self.gateway.transport.master, + json=CreateGuardrailBody( + guardrail=GuardrailSpec( + guardrail_name=name, + litellm_params=GuardrailLitellmParams( + guardrail="tool_permission", + mode="post_call", + default_on=False, + default_action="deny", + on_disallowed_action="block", + rules=[ + { + "id": "allow-named-tool", + "tool_name": allowed_tool, + "decision": "allow", + } + ], + ), + ) + ), + response_type=CreateGuardrailResponse, + ) + ) + guardrail_id = response.guardrail_id + assert guardrail_id, f"create guardrail returned no id: {response!r}" + return guardrail_id + + def delete_guardrail(self, guardrail_id: str) -> None: + _ = self.gateway.transport.delete( + f"/guardrails/{guardrail_id}", + headers=self.gateway.transport.master, + json=NoBody(), + response_type=NoBody, + ) + + def create_model(self, model_name: str, litellm_params: LiteLLMParamsBody) -> str: + return self.gateway.create_model(model_name, litellm_params) + + def delete_model(self, model_id: str) -> None: + self.gateway.delete_model(model_id) + + def chat(self, key: str, model: str, text: str) -> ChatResponse: + return unwrap( + self.gateway.chat( + key, + ChatBody( + model=model, + messages=[ChatMessage(role="user", content=text)], + max_tokens=64, + ), + ) + ) + + def chat_raw( + self, + key: str, + model: str, + text: str, + *, + stream: bool = False, + tools: list[ChatTool] | None = None, + tool_choice: str | None = None, + guardrails: list[str] | None = None, + max_tokens: int = 64, + ) -> StreamingResponse: + body = ChatBody( + model=model, + messages=[ChatMessage(role="user", content=text)], + max_tokens=max_tokens, + stream=stream, + tools=tools, + tool_choice=tool_choice, + guardrails=guardrails, + ) + if stream: + return self.gateway.chat_stream(key, body) + return self.gateway.transport.send( + "/chat/completions", + headers=self.gateway.transport.bearer(key), + json=body, + ) + + def scrape_metrics(self) -> str: + return self.gateway.probe("/metrics", params=NoBody()).body + + def poll_proxy_spend_for_key( + self, + key: str, + *, + response_id: str | None = None, + require_positive_spend: bool = True, + ) -> SpendLogRow | None: + """Poll /spend/logs by virtual key. + + When ``response_id`` is set, only that SpendLogs.request_id may match. + When unset, any positive-spend row for the key is accepted. Never falls + back to an unmatched row; missing match returns None. + """ + + def _matches(row: SpendLogRow) -> bool: + if response_id is not None and row.request_id != response_id: + return False + if require_positive_spend and not (row.spend is not None and row.spend > 0): + return False + return True + + rows = self.gateway.poll_logs_for_key( + key, min_rows=1, predicate=lambda rs: any(_matches(r) for r in rs) + ) + for row in rows: + if _matches(row): + return row + return None + + def list_langfuse_observations( + self, + creds: LangfuseCreds, + *, + trace_id: str | None = None, + name: str | None = None, + from_start_time: str | None = None, + ) -> list[LangfuseObservation]: + result = get( + URL(f"{creds.host}/api/public/observations"), + headers=creds.auth_headers, + params=LangfuseListParams( + limit=100, + traceId=trace_id, + name=name, + fromStartTime=from_start_time, + ), + response_type=LangfuseObservationList, + timeout=30.0, + ) + match result: + case Success(data=page): + return page.data + case _: + return [] + + def find_langfuse_observation( + self, + creds: LangfuseCreds, + *, + key_alias: str, + prompt_marker: str, + ) -> LangfuseObservation | None: + # langfuse_otel generations are named litellm_request; classic SDK used + # litellm:{key_alias}. Search both, then a recent unfiltered page. + for name in ("litellm_request", f"litellm:{key_alias}"): + for obs in self.list_langfuse_observations(creds, name=name): + if _matches_run(obs, key_alias=key_alias, prompt_marker=prompt_marker): + return obs + for obs in self.list_langfuse_observations(creds): + if _matches_run(obs, key_alias=key_alias, prompt_marker=prompt_marker): + return obs + return None + + def poll_langfuse_observation( + self, + creds: LangfuseCreds, + *, + key_alias: str, + prompt_marker: str, + require_positive_cost: bool = False, + ) -> LangfuseObservation | None: + deadline = time.monotonic() + POLL_TIMEOUT + last: LangfuseObservation | None = None + while time.monotonic() < deadline: + last = self.find_langfuse_observation( + creds, key_alias=key_alias, prompt_marker=prompt_marker + ) + if last is not None: + cost = observation_spend(last) + if not require_positive_cost or (cost is not None and cost > 0): + return last + time.sleep(POLL_INTERVAL) + return last + + def poll_langfuse_trace_observations( + self, + creds: LangfuseCreds, + *, + key_alias: str, + prompt_marker: str, + ) -> list[LangfuseObservation]: + """Generation plus any sibling/child observations (guardrail spans, etc.).""" + gen = self.poll_langfuse_observation( + creds, key_alias=key_alias, prompt_marker=prompt_marker + ) + if gen is None or not gen.trace_id: + return [] if gen is None else [gen] + return self.list_langfuse_observations(creds, trace_id=gen.trace_id) or [gen] + + +def build_logging_client() -> LoggingClient: + return LoggingClient(gateway=build_gateway()) diff --git a/tests/e2e/logging/test_langfuse_e2e.py b/tests/e2e/logging/test_langfuse_e2e.py new file mode 100644 index 00000000000..d014b5d8291 --- /dev/null +++ b/tests/e2e/logging/test_langfuse_e2e.py @@ -0,0 +1,534 @@ +"""Live e2e: Langfuse OTEL logs_spend for registry cells in logging.yaml P0. + +Registry cells: +- logging.langfuse.success.logs_spend (exercised_on chat_completions, messages, embeddings) +- logging.langfuse.failure.logs_spend (exercised_on chat_completions, messages) +- logging.langfuse.stream.logs_spend (exercised_on chat_completions, messages) + +Integration under test is ``langfuse_otel`` (OTLP to Langfuse), not the classic +``langfuse`` SDK callback. StandardLoggingPayload.response_cost is the spend +source of truth. Generations are named ``litellm_request``; correlate by unique +prompt marker and user_api_key_alias in metadata. + +Dynamic credentials by product surface: +- team: POST /team/{id}/callback with callback_name=langfuse_otel +- user/key: key metadata.logging with callback_name=langfuse_otel +- org: organization + team under it + team callback (no org-level callback API) + +Extra success paths assert tool calls and applied guardrails land on the trace. +""" + +from __future__ import annotations + +import json + +import pytest + +from e2e_config import unique_marker +from e2e_http import StreamingResponse, require_successful_call +from lifecycle import ResourceManager +from logging_client import ( + INVALID_UPSTREAM_API_KEY, + WEATHER_TOOL, + LangfuseCreds, + LoggingClient, + completion_response_id, + costs_agree, + observation_has_guardrail, + observation_mentions_tool, + observation_spend, +) +from models import LiteLLMParamsBody + +pytestmark = pytest.mark.e2e + +DRIVER_MODEL = "gemini-2.5-flash" +FAIL_BACKEND = "openai/gpt-4o-mini" + + +def _json_blob(value: object) -> str: + return json.dumps(value, default=str) + + +def _assert_logs_spend( + client: LoggingClient, + *, + key: str, + outcome: StreamingResponse, + obs_cost: float | None, + scope: str, + require_positive: bool = True, +) -> None: + """logs_spend: Langfuse cost matches StandardLogging response_cost and proxy spend. + + Non-stream responses expose response_cost on x-litellm-response-cost. Streaming + sends headers before final cost is known, so stream paths rely on /spend/logs. + """ + if not require_positive: + assert obs_cost is not None, ( + f"{scope}: failure path must still track spend (0 is fine); cost={obs_cost!r}" + ) + return + + assert obs_cost is not None and obs_cost > 0, ( + f"{scope}: Langfuse must log positive spend; calculatedTotalCost={obs_cost!r}" + ) + # Stream responses send headers before final cost is known, so the cost header + # is often absent; non-stream must always expose x-litellm-response-cost. + if not outcome.is_streaming: + assert outcome.response_cost is not None and outcome.response_cost > 0, ( + f"{scope}: proxy must return positive x-litellm-response-cost; " + f"got {outcome.response_cost!r}" + ) + assert costs_agree(outcome.response_cost, obs_cost), ( + f"{scope}: Langfuse cost {obs_cost!r} disagrees with " + f"x-litellm-response-cost {outcome.response_cost!r}" + ) + elif outcome.response_cost is not None and outcome.response_cost > 0: + assert costs_agree(outcome.response_cost, obs_cost), ( + f"{scope}: Langfuse cost {obs_cost!r} disagrees with " + f"x-litellm-response-cost {outcome.response_cost!r}" + ) + spend_row = client.poll_proxy_spend_for_key( + key, + response_id=completion_response_id(outcome.body), + require_positive_spend=True, + ) + assert spend_row is not None and spend_row.spend is not None and spend_row.spend > 0, ( + f"{scope}: proxy /spend/logs never produced a positive spend row for key" + ) + assert costs_agree(spend_row.spend, obs_cost), ( + f"{scope}: Langfuse cost {obs_cost!r} disagrees with proxy spend " + f"{spend_row.spend!r} (request_id={spend_row.request_id!r})" + ) + + +class TestLangfuseTeamLogging: + """Team-scoped callback via POST /team/{id}/callback.""" + + def _team_key( + self, + client: LoggingClient, + resources: ResourceManager, + creds: LangfuseCreds, + *, + models: list[str], + organization_id: str | None = None, + ) -> tuple[str, str, str]: + marker = unique_marker() + key_alias = f"e2e-lf-team-key-{marker}" + team_id = client.create_team( + f"e2e-lf-team-{marker}", + models=models, + organization_id=organization_id, + ) + resources.defer(lambda: client.delete_team(team_id)) + client.add_team_langfuse_callback(team_id, creds) + key = client.key_with_alias(key_alias, models=models, team_id=team_id) + resources.defer(lambda: client.delete_key(key)) + return team_id, key, key_alias + + @pytest.mark.covers("logging.langfuse.success.logs_spend", exercised_on=["chat_completions"]) + def test_success_logs_spend( + self, + client: LoggingClient, + resources: ResourceManager, + langfuse_creds: LangfuseCreds, + ) -> None: + _, key, key_alias = self._team_key( + client, resources, langfuse_creds, models=[DRIVER_MODEL] + ) + prompt_marker = unique_marker() + outcome = client.chat_raw( + key, DRIVER_MODEL, f"reply with one word only {prompt_marker}" + ) + require_successful_call(outcome) + + obs = client.poll_langfuse_observation( + langfuse_creds, + key_alias=key_alias, + prompt_marker=prompt_marker, + require_positive_cost=True, + ) + assert obs is not None, ( + f"team scope: Langfuse never received generation for key_alias={key_alias!r}" + ) + _assert_logs_spend( + client, + key=key, + outcome=outcome, + obs_cost=observation_spend(obs), + scope="team-success", + ) + + @pytest.mark.covers("logging.langfuse.failure.logs_spend", exercised_on=["chat_completions"]) + def test_failure_logs_spend( + self, + client: LoggingClient, + resources: ResourceManager, + langfuse_creds: LangfuseCreds, + ) -> None: + """Provider-auth failure still ships a Langfuse observation with spend tracked. + + Uses a throwaway deployment whose upstream OpenAI key is + INVALID_UPSTREAM_API_KEY (not a LiteLLM virtual key). + """ + prompt_marker = unique_marker() + model_name = f"e2e-lf-fail-{prompt_marker}" + model_id = client.create_model( + model_name, + LiteLLMParamsBody(model=FAIL_BACKEND, api_key=INVALID_UPSTREAM_API_KEY), + ) + resources.defer(lambda: client.delete_model(model_id)) + + _, key, key_alias = self._team_key( + client, resources, langfuse_creds, models=[model_name] + ) + outcome = client.chat_raw(key, model_name, f"this must fail {prompt_marker}") + assert not outcome.ok, ( + f"expected upstream provider failure for {INVALID_UPSTREAM_API_KEY!r}, " + f"got {outcome.status_code}: {outcome.body[:200]}" + ) + + obs = client.poll_langfuse_observation( + langfuse_creds, + key_alias=key_alias, + prompt_marker=prompt_marker, + require_positive_cost=False, + ) + assert obs is not None, ( + f"team failure path: Langfuse never received generation for key_alias={key_alias!r}" + ) + _assert_logs_spend( + client, + key=key, + outcome=outcome, + obs_cost=observation_spend(obs), + scope="team-failure", + require_positive=False, + ) + + @pytest.mark.covers("logging.langfuse.stream.logs_spend", exercised_on=["chat_completions"]) + def test_stream_logs_spend( + self, + client: LoggingClient, + resources: ResourceManager, + langfuse_creds: LangfuseCreds, + ) -> None: + _, key, key_alias = self._team_key( + client, resources, langfuse_creds, models=[DRIVER_MODEL] + ) + prompt_marker = unique_marker() + outcome = client.chat_raw( + key, DRIVER_MODEL, f"reply with one word only {prompt_marker}", stream=True + ) + require_successful_call(outcome) + assert outcome.is_streaming + assert outcome.chunks > 0 + + obs = client.poll_langfuse_observation( + langfuse_creds, + key_alias=key_alias, + prompt_marker=prompt_marker, + require_positive_cost=True, + ) + assert obs is not None + # Streamed body is elided; correlate cost via header + key spend row. + _assert_logs_spend( + client, + key=key, + outcome=outcome, + obs_cost=observation_spend(obs), + scope="team-stream", + ) + + @pytest.mark.covers("logging.langfuse.success.logs_spend", exercised_on=["chat_completions"]) + def test_tool_calls_logged_with_cost( + self, + client: LoggingClient, + resources: ResourceManager, + langfuse_creds: LangfuseCreds, + ) -> None: + _, key, key_alias = self._team_key( + client, resources, langfuse_creds, models=[DRIVER_MODEL] + ) + prompt_marker = unique_marker() + outcome = client.chat_raw( + key, + DRIVER_MODEL, + f"Use get_weather for Paris. marker={prompt_marker}", + tools=[WEATHER_TOOL], + tool_choice="required", + max_tokens=128, + ) + require_successful_call(outcome) + assert "get_weather" in outcome.body or "tool_calls" in outcome.body, ( + f"gateway response must include a tool call; body={outcome.body[:300]}" + ) + + obs = client.poll_langfuse_observation( + langfuse_creds, + key_alias=key_alias, + prompt_marker=prompt_marker, + require_positive_cost=True, + ) + assert obs is not None + assert observation_mentions_tool(obs, "get_weather"), ( + f"Langfuse generation must record the tool; name={obs.name!r} " + f"input={str(obs.input)[:200]} output={str(obs.output)[:200]}" + ) + _assert_logs_spend( + client, + key=key, + outcome=outcome, + obs_cost=observation_spend(obs), + scope="team-tools", + ) + + @pytest.mark.covers("logging.langfuse.success.logs_spend", exercised_on=["chat_completions"]) + def test_tool_permission_guardrail_logged( + self, + client: LoggingClient, + resources: ResourceManager, + langfuse_creds: LangfuseCreds, + ) -> None: + """tool_permission post_call guardrail must appear on the Langfuse trace + (StandardLogging guardrail_information -> Langfuse guardrail span).""" + marker = unique_marker() + guardrail_name = f"e2e-lf-tool-perm-{marker}" + guardrail_id = client.create_tool_permission_guardrail( + guardrail_name, allowed_tool="get_weather" + ) + resources.defer(lambda: client.delete_guardrail(guardrail_id)) + + _, key, key_alias = self._team_key( + client, resources, langfuse_creds, models=[DRIVER_MODEL] + ) + prompt_marker = unique_marker() + outcome = client.chat_raw( + key, + DRIVER_MODEL, + f"Use get_weather for Berlin. marker={prompt_marker}", + tools=[WEATHER_TOOL], + tool_choice="required", + guardrails=[guardrail_name], + max_tokens=128, + ) + require_successful_call(outcome) + + observations = client.poll_langfuse_trace_observations( + langfuse_creds, key_alias=key_alias, prompt_marker=prompt_marker + ) + assert observations, ( + f"team+guardrail: no Langfuse observations for key_alias={key_alias!r}" + ) + gen = next( + ( + o + for o in observations + if prompt_marker in _json_blob(o.input) + or key_alias in _json_blob(o.metadata) + or o.name in (f"litellm:{key_alias}", "litellm_request") + ), + observations[0], + ) + _assert_logs_spend( + client, + key=key, + outcome=outcome, + obs_cost=observation_spend(gen), + scope="team-guardrail", + ) + assert any( + observation_has_guardrail(o, guardrail_name=guardrail_name) + or (o.name is not None and "guardrail" in o.name.lower()) + for o in observations + ), ( + f"Langfuse trace must include applied guardrail {guardrail_name!r}; " + f"observation names={[o.name for o in observations]}" + ) + + +class TestLangfuseUserKeyLogging: + """User-owned key with metadata.logging (key-level dynamic Langfuse credentials). + + Product surface: key metadata.logging on /key/generate, not a separate + /user/.../callback route. The key is bound to a real /user/new user_id. + """ + + def _user_key( + self, + client: LoggingClient, + resources: ResourceManager, + creds: LangfuseCreds, + *, + models: list[str], + ) -> tuple[str, str, str]: + marker = unique_marker() + key_alias = f"e2e-lf-user-key-{marker}" + user_id = client.create_user( + user_email=f"e2e-lf-user-{marker}@example.com", + user_id=f"e2e-lf-user-{marker}", + ) + resources.defer(lambda: client.delete_user(user_id)) + key = client.key_with_alias( + key_alias, + models=models, + user_id=user_id, + metadata=creds.key_logging_metadata(), + ) + resources.defer(lambda: client.delete_key(key)) + return user_id, key, key_alias + + @pytest.mark.covers("logging.langfuse.success.logs_spend", exercised_on=["chat_completions"]) + def test_success_logs_spend( + self, + client: LoggingClient, + resources: ResourceManager, + langfuse_creds: LangfuseCreds, + ) -> None: + user_id, key, key_alias = self._user_key( + client, resources, langfuse_creds, models=[DRIVER_MODEL] + ) + prompt_marker = unique_marker() + outcome = client.chat_raw( + key, DRIVER_MODEL, f"reply with one word only {prompt_marker}" + ) + require_successful_call(outcome) + + obs = client.poll_langfuse_observation( + langfuse_creds, + key_alias=key_alias, + prompt_marker=prompt_marker, + require_positive_cost=True, + ) + assert obs is not None, ( + f"user/key scope: Langfuse never received generation for key_alias={key_alias!r}" + ) + meta_blob = _json_blob(obs.metadata) + assert user_id in meta_blob or key_alias in (obs.name or ""), ( + f"user/key scope should attribute the user or key; metadata={meta_blob[:300]}" + ) + _assert_logs_spend( + client, + key=key, + outcome=outcome, + obs_cost=observation_spend(obs), + scope="user-key", + ) + + @pytest.mark.covers("logging.langfuse.success.logs_spend", exercised_on=["chat_completions"]) + def test_tool_calls_logged_with_cost( + self, + client: LoggingClient, + resources: ResourceManager, + langfuse_creds: LangfuseCreds, + ) -> None: + _, key, key_alias = self._user_key( + client, resources, langfuse_creds, models=[DRIVER_MODEL] + ) + prompt_marker = unique_marker() + outcome = client.chat_raw( + key, + DRIVER_MODEL, + f"Use get_weather for Tokyo. marker={prompt_marker}", + tools=[WEATHER_TOOL], + tool_choice="required", + max_tokens=128, + ) + require_successful_call(outcome) + + obs = client.poll_langfuse_observation( + langfuse_creds, + key_alias=key_alias, + prompt_marker=prompt_marker, + require_positive_cost=True, + ) + assert obs is not None + assert observation_mentions_tool(obs, "get_weather"), ( + f"user/key tool path: tool missing from Langfuse; output={str(obs.output)[:200]}" + ) + _assert_logs_spend( + client, + key=key, + outcome=outcome, + obs_cost=observation_spend(obs), + scope="user-key-tools", + ) + + +class TestLangfuseOrgScopedLogging: + """Org-scoped run: organization + team under it + team Langfuse callback. + + There is no /organization/.../callback today; logging attaches at the team + (or key) under the org. This class proves org-linked team keys still deliver + accurate Langfuse spend and team attribution (StandardLogging metadata + user_api_key_team_id / user_api_key_org_id). + """ + + def _org_team_key( + self, + client: LoggingClient, + resources: ResourceManager, + creds: LangfuseCreds, + *, + models: list[str], + ) -> tuple[str, str, str, str]: + marker = unique_marker() + key_alias = f"e2e-lf-org-key-{marker}" + org_id = client.create_org(f"e2e-lf-org-{marker}", models=models) + resources.defer(lambda: client.delete_org(org_id)) + team_id = client.create_team( + f"e2e-lf-org-team-{marker}", + models=models, + organization_id=org_id, + ) + resources.defer(lambda: client.delete_team(team_id)) + client.add_team_langfuse_callback(team_id, creds) + key = client.key_with_alias( + key_alias, + models=models, + team_id=team_id, + organization_id=org_id, + ) + resources.defer(lambda: client.delete_key(key)) + return org_id, team_id, key, key_alias + + @pytest.mark.covers("logging.langfuse.success.logs_spend", exercised_on=["chat_completions"]) + def test_success_logs_spend_with_team_attribution( + self, + client: LoggingClient, + resources: ResourceManager, + langfuse_creds: LangfuseCreds, + ) -> None: + org_id, team_id, key, key_alias = self._org_team_key( + client, resources, langfuse_creds, models=[DRIVER_MODEL] + ) + prompt_marker = unique_marker() + outcome = client.chat_raw( + key, DRIVER_MODEL, f"reply with one word only {prompt_marker}" + ) + require_successful_call(outcome) + + obs = client.poll_langfuse_observation( + langfuse_creds, + key_alias=key_alias, + prompt_marker=prompt_marker, + require_positive_cost=True, + ) + assert obs is not None, ( + f"org scope: Langfuse never received generation for key_alias={key_alias!r}" + ) + meta_blob = _json_blob(obs.metadata) + assert team_id in meta_blob, ( + f"org-scoped team key must stamp team_id on Langfuse metadata; " + f"team_id={team_id!r} metadata={meta_blob[:400]}" + ) + _ = org_id + _assert_logs_spend( + client, + key=key, + outcome=outcome, + obs_cost=observation_spend(obs), + scope="org-team", + ) diff --git a/tests/e2e/logging/test_prometheus_cardinality_e2e.py b/tests/e2e/logging/test_prometheus_cardinality_e2e.py new file mode 100644 index 00000000000..163293a3009 --- /dev/null +++ b/tests/e2e/logging/test_prometheus_cardinality_e2e.py @@ -0,0 +1,70 @@ +"""Live e2e: Prometheus request metrics grow one series per virtual key. + +The proxy exposes ``/metrics`` (prometheus is in the callbacks and +``require_auth_for_metrics_endpoint`` is off in the e2e config). The counter +``litellm_requests_metric_total`` carries an ``api_key_alias`` label, so driving +traffic through keys with distinct aliases must produce a distinct labeled series +per alias. This is the per-key cardinality contract: a regression that stops +stamping ``api_key_alias`` (or collapses every key onto one series) would drop +the aliases and fail here. + +Scraping goes through ``transport.probe`` (raw text) and is parsed with +prometheus_client; the metric is eventually consistent (it increments on the +success-logging callback), so the scrape polls to a deadline. +""" + +from __future__ import annotations + +import time + +import pytest +from prometheus_client.parser import text_string_to_metric_families + +from e2e_config import unique_marker +from lifecycle import ResourceManager +from logging_client import LoggingClient + +pytestmark = pytest.mark.e2e + +DRIVER_MODEL = "gemini-2.5-flash" +REQUESTS_METRIC = "litellm_requests_metric_total" +ALIAS_LABEL = "api_key_alias" +DISTINCT_KEYS = 3 + + +def _aliases_in_metric(exposition: str, metric: str, label: str) -> frozenset[str]: + """The set of ``label`` values present on ``metric`` samples in a scrape.""" + return frozenset( + sample.labels[label] + for family in text_string_to_metric_families(exposition) + for sample in family.samples + if sample.name == metric and label in sample.labels + ) + + +class TestPrometheusPerKeyCardinality: + @pytest.mark.covers("logging.prometheus.success.exports_metric", exercised_on=[]) + def test_distinct_key_aliases_produce_distinct_series( + self, client: LoggingClient, resources: ResourceManager + ) -> None: + aliases = tuple(f"e2e-prom-{unique_marker()}" for _ in range(DISTINCT_KEYS)) + for alias in aliases: + key = client.key_with_alias(alias, models=[DRIVER_MODEL]) + resources.defer(lambda k=key: client.delete_key(k)) + response = client.chat(key, DRIVER_MODEL, f"reply with one word {alias}") + assert response.model, f"driver call for {alias} returned no model: {response}" + + wanted = frozenset(aliases) + deadline = time.monotonic() + client.gateway.poll_timeout + seen: frozenset[str] = frozenset() + while time.monotonic() < deadline: + seen = _aliases_in_metric(client.scrape_metrics(), REQUESTS_METRIC, ALIAS_LABEL) + if wanted <= seen: + break + time.sleep(client.gateway.poll_interval) + + missing = wanted - seen + assert not missing, ( + f"{REQUESTS_METRIC} is missing a per-key series for aliases {sorted(missing)}; " + f"each distinct {ALIAS_LABEL} must grow its own series" + ) diff --git a/tests/e2e/management/conftest.py b/tests/e2e/management/conftest.py new file mode 100644 index 00000000000..47c1d34baae --- /dev/null +++ b/tests/e2e/management/conftest.py @@ -0,0 +1,57 @@ +"""Management suite fixtures: the client plus a logged-in dashboard page. + +Lifecycle/skip/marker live in the parent conftest. The browser fixtures drive +the dashboard the proxy serves at /ui, so browser tests exercise exactly what an +end user sees. playwright is an optional dependency loaded behind importorskip +inside the fixture, so the API tests in this suite collect and run without it: + + uv pip install playwright && uv run playwright install chromium +""" + +from typing import TYPE_CHECKING, Iterator + +import pytest + +from e2e_config import PROXY_BASE_URL, UI_PASSWORD, UI_USERNAME +from management_client import ManagementClient, build_client + +if TYPE_CHECKING: + from playwright.sync_api import Browser, Page + + +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line( + "markers", + "covers: registry cell a test covers, e.g. mgmt.key.generate.persists", + ) + + +@pytest.fixture(scope="session") +def client() -> ManagementClient: + return build_client() + + +@pytest.fixture(scope="session") +def browser() -> "Iterator[Browser]": + pytest.importorskip("playwright.sync_api", reason="playwright not installed") + from playwright.sync_api import sync_playwright + + with sync_playwright() as playwright: + launched = playwright.chromium.launch() + yield launched + launched.close() + + +@pytest.fixture +def ui_page(browser: "Browser") -> "Iterator[Page]": + context = browser.new_context() + try: + page = context.new_page() + page.goto(f"{PROXY_BASE_URL}/ui/") + page.fill("#username", UI_USERNAME) + page.fill("#password", UI_PASSWORD) + page.click('input[type="submit"]') + page.wait_for_url("**/ui/**") + yield page + finally: + context.close() diff --git a/tests/e2e/management/management_client.py b/tests/e2e/management/management_client.py new file mode 100644 index 00000000000..144a3c0c435 --- /dev/null +++ b/tests/e2e/management/management_client.py @@ -0,0 +1,268 @@ +"""Client for the management-routes e2e suite: the shared Gateway plus the +key/team/user/organization writes, the info/list read-backs the tests assert, +and the raw-status calls judged by HTTP outcome (chat under a scoped key, an +llm-only key hitting a management route). +""" + +from __future__ import annotations + +import time +from dataclasses import dataclass + +from e2e_gateway import Gateway, build_gateway +from e2e_http import NoBody, ProbeResult, Result, StreamingResponse, Success, UnknownApiError, unwrap +from models import ( + ChatBody, + ChatMessage, + KeyDeleteBody, + KeyGenerateBody, + KeyListParams, + KeyListResponse, + KeyUpdateBody, + OrgDeleteBody, + OrgInfoParams, + OrgInfoResponse, + OrgNewBody, + OrgNewResponse, + TeamData, + TeamDeleteBody, + TeamInfoParams, + TeamInfoResponse, + TeamMemberAddBody, + TeamMemberDeleteBody, + TeamMemberEntry, + TeamNewBody, + TeamNewResponse, + UserDeleteBody, + UserInfoParams, + UserInfoResponse, + UserListParams, + UserListResponse, + UserNewBody, + UserNewResponse, +) + +MODEL_ACCESS_DENIED_MARKER = "key_model_access_denied" +ROUTE_NOT_ALLOWED_MARKER = "not allowed to call this route" +_TEAM_READY_ATTEMPTS = 15 +_TEAM_READY_SLEEP_SECONDS = 0.4 + + +@dataclass(frozen=True, slots=True) +class ManagementClient: + gateway: Gateway + + def llm_only_key(self) -> str: + return self.gateway.generate_key(KeyGenerateBody(models=[], allowed_routes=["llm_api_routes"])) + + def update_key_models(self, key: str, models: list[str]) -> None: + last: Result[NoBody] | None = None + for attempt in range(5): + last = self.gateway.transport.post( + "/key/update", + headers=self.gateway.transport.master, + json=KeyUpdateBody(key=key, models=models), + response_type=NoBody, + ) + match last: + case Success(): + return + case UnknownApiError(body=body) if ( + "connecting to redis" in body.lower() or "name resolution" in body.lower() + ): + time.sleep(0.5 * (attempt + 1)) + continue + case _: + break + assert last is not None + raise AssertionError(last) + + def delete_key_strict(self, key: str) -> None: + """Strict delete for the act phase of a test: a failed delete is a hard + failure, unlike the warn-only Gateway.delete_key used at teardown.""" + _ = unwrap( + self.gateway.transport.post( + "/key/delete", + headers=self.gateway.transport.master, + json=KeyDeleteBody(keys=[key]), + response_type=NoBody, + ) + ) + + def key_alias_count(self, key_alias: str) -> int: + return unwrap( + self.gateway.transport.get( + "/key/list", + headers=self.gateway.transport.master, + params=KeyListParams(key_alias=key_alias), + response_type=KeyListResponse, + ) + ).total_count + + def create_team(self, body: TeamNewBody) -> str: + team_id = unwrap( + self.gateway.transport.post( + "/team/new", + headers=self.gateway.transport.master, + json=body, + response_type=TeamNewResponse, + ) + ).team_id + self._wait_for_team(team_id) + return team_id + + def delete_team(self, team_id: str) -> None: + _ = self.gateway.transport.post( + "/team/delete", + headers=self.gateway.transport.master, + json=TeamDeleteBody(team_ids=[team_id]), + response_type=NoBody, + ) + + def team_info(self, team_id: str) -> TeamData: + return unwrap( + self.gateway.transport.get( + "/team/info", + headers=self.gateway.transport.master, + params=TeamInfoParams(team_id=team_id), + response_type=TeamInfoResponse, + ) + ).team_info + + def team_info_status(self, team_id: str) -> ProbeResult: + return self.gateway.transport.probe("/team/info", params=TeamInfoParams(team_id=team_id)) + + def _wait_for_team(self, team_id: str) -> None: + last: Result[TeamInfoResponse] | None = None + for _ in range(_TEAM_READY_ATTEMPTS): + last = self.gateway.transport.get( + "/team/info", + headers=self.gateway.transport.master, + params=TeamInfoParams(team_id=team_id), + response_type=TeamInfoResponse, + ) + match last: + case Success(): + return + case _: + time.sleep(_TEAM_READY_SLEEP_SECONDS) + assert last is not None + raise AssertionError(last) + + def add_team_member(self, team_id: str, user_id: str) -> None: + last: Result[NoBody] | None = None + for attempt in range(_TEAM_READY_ATTEMPTS): + last = self.gateway.transport.post( + "/team/member_add", + headers=self.gateway.transport.master, + json=TeamMemberAddBody(team_id=team_id, member=TeamMemberEntry(role="user", user_id=user_id)), + response_type=NoBody, + ) + match last: + case Success(): + return + case UnknownApiError(body=body) if ( + "doesn't exist" in body and attempt + 1 < _TEAM_READY_ATTEMPTS + ): + time.sleep(_TEAM_READY_SLEEP_SECONDS) + continue + case _: + break + assert last is not None + raise AssertionError(last) + + def delete_team_member(self, team_id: str, user_id: str) -> None: + _ = unwrap( + self.gateway.transport.post( + "/team/member_delete", + headers=self.gateway.transport.master, + json=TeamMemberDeleteBody(team_id=team_id, user_id=user_id), + response_type=NoBody, + ) + ) + + def create_user(self, body: UserNewBody) -> str: + return unwrap( + self.gateway.transport.post( + "/user/new", + headers=self.gateway.transport.master, + json=body, + response_type=UserNewResponse, + ) + ).user_id + + def delete_user(self, user_id: str) -> None: + _ = self.gateway.transport.post( + "/user/delete", + headers=self.gateway.transport.master, + json=UserDeleteBody(user_ids=[user_id]), + response_type=NoBody, + ) + + def user_info(self, user_id: str) -> UserInfoResponse: + return unwrap( + self.gateway.transport.get( + "/user/info", + headers=self.gateway.transport.master, + params=UserInfoParams(user_id=user_id), + response_type=UserInfoResponse, + ) + ) + + def user_count(self, user_id: str) -> int: + return unwrap( + self.gateway.transport.get( + "/user/list", + headers=self.gateway.transport.master, + params=UserListParams(user_ids=user_id), + response_type=UserListResponse, + ) + ).total + + def create_org(self, body: OrgNewBody) -> str: + return unwrap( + self.gateway.transport.post( + "/organization/new", + headers=self.gateway.transport.master, + json=body, + response_type=OrgNewResponse, + ) + ).organization_id + + def delete_org(self, organization_id: str) -> None: + _ = self.gateway.transport.delete( + "/organization/delete", + headers=self.gateway.transport.master, + json=OrgDeleteBody(organization_ids=[organization_id]), + response_type=NoBody, + ) + + def org_info(self, organization_id: str) -> OrgInfoResponse: + return unwrap( + self.gateway.transport.get( + "/organization/info", + headers=self.gateway.transport.master, + params=OrgInfoParams(organization_id=organization_id), + response_type=OrgInfoResponse, + ) + ) + + def chat_status(self, key: str, model: str, content: str) -> StreamingResponse: + return self.gateway.transport.send( + "/chat/completions", + headers=self.gateway.transport.bearer(key), + json=ChatBody(model=model, messages=[ChatMessage(role="user", content=content)], max_tokens=16), + ) + + def key_generate_status(self, key: str, body: KeyGenerateBody) -> StreamingResponse: + return self.gateway.transport.send("/key/generate", headers=self.gateway.transport.bearer(key), json=body) + + def team_new_status(self, key: str, body: TeamNewBody) -> StreamingResponse: + return self.gateway.transport.send("/team/new", headers=self.gateway.transport.bearer(key), json=body) + + def user_new_status(self, key: str, body: UserNewBody) -> StreamingResponse: + return self.gateway.transport.send("/user/new", headers=self.gateway.transport.bearer(key), json=body) + + +def build_client() -> ManagementClient: + return ManagementClient(gateway=build_gateway()) diff --git a/tests/e2e/management/test_key_models_dropdown_e2e.py b/tests/e2e/management/test_key_models_dropdown_e2e.py new file mode 100644 index 00000000000..f0ba21699e0 --- /dev/null +++ b/tests/e2e/management/test_key_models_dropdown_e2e.py @@ -0,0 +1,161 @@ +"""The dashboard's key create/edit Models dropdown scopes its options to the key's team. + +A teamless key offers All Proxy Models but not the all-team-models sentinel (the +backend expands the latter to the full proxy model list when no team is attached), +and a team key offers all-team-models plus the team's own models but never the +all-proxy-models sentinel, even when the team's model list carries it. The create +cases also walk the full product path: submit the modal with the offered sentinel +and read the persisted key back through /key/info. + +The tests drive gpt-5.5, one of the example models prewired in the proxy config in +tests/e2e/docker-compose.yml; the dropdown wait fails with a pointer there when the +proxy under test does not serve it. +""" + +import pytest + +from e2e_config import PROXY_BASE_URL, unique_marker +from lifecycle import ResourceManager +from management_client import ManagementClient +from models import KeyGenerateBody, TeamNewBody + +pytest.importorskip("playwright.sync_api", reason="playwright not installed") + +from playwright.sync_api import Locator, Page, expect # noqa: E402 # import must follow the importorskip guard above + + +def _form_item(page: Page, label: str) -> Locator: + return page.locator(".ant-form-item").filter(has=page.get_by_text(label, exact=True)).first + + +def _open_dropdown(page: Page, label: str) -> Locator: + _form_item(page, label).locator(".ant-select-selector").first.click() + dropdown = page.locator(".ant-select-dropdown:not(.ant-select-dropdown-hidden)").last + expect(dropdown).to_be_visible() + return dropdown + + +def _models_dropdown_texts(page: Page, must_contain: str) -> list[str]: + dropdown = _open_dropdown(page, "Models") + expect( + dropdown.locator(".ant-select-item-option-content", has_text=must_contain).first, + f"{must_contain!r} never appeared in the Models dropdown; the proxy must serve it " + f"(see the model_list in tests/e2e/docker-compose.yml)", + ).to_be_visible() + return dropdown.locator(".ant-select-item-option-content").all_inner_texts() + + +def _open_create_key_modal(page: Page) -> None: + page.goto(f"{PROXY_BASE_URL}/ui/api-keys/?create=true") + expect(page.locator(".ant-modal").first).to_be_visible() + + +def _select_team(page: Page, alias: str) -> None: + dropdown = _open_dropdown(page, "Team") + dropdown.get_by_text(alias).first.click() + + +def _submit_create_modal(page: Page, sentinel_label: str) -> str: + dropdown = page.locator(".ant-select-dropdown:not(.ant-select-dropdown-hidden)").last + dropdown.locator(".ant-select-item-option-content", has_text=sentinel_label).first.click() + page.keyboard.press("Escape") + _form_item(page, "Key Name").locator("input").first.fill(f"e2e-ui-key-{unique_marker()}") + page.get_by_role("button", name="Create Key", exact=True).click() + + expect(page.get_by_text("Save your Key")).to_be_visible() + key = page.locator(".ant-modal pre").last.inner_text().strip() + assert key.startswith("sk-"), f"expected the created key in the success modal, got {key!r}" + return key + + +def _open_key_edit_form(page: Page, key_alias: str) -> None: + page.goto(f"{PROXY_BASE_URL}/ui/api-keys/") + page.get_by_text(key_alias).first.click() + page.get_by_role("tab", name="Settings").click() + page.get_by_role("button", name="Edit Settings").click() + expect(_form_item(page, "Models")).to_be_visible() + + +def _provision_team(client: ManagementClient, resources: ResourceManager, alias: str) -> str: + team_id = client.create_team(TeamNewBody(team_alias=alias, models=["all-proxy-models", "gpt-5.5"])) + resources.defer(lambda: client.delete_team(team_id)) + return team_id + + +def _provision_key( + client: ManagementClient, resources: ResourceManager, alias: str, team_id: str | None = None +) -> str: + key = client.gateway.generate_key(KeyGenerateBody(key_alias=alias, models=["gpt-5.5"], team_id=team_id)) + resources.defer(lambda: client.gateway.delete_key(key)) + return key + + +@pytest.mark.e2e +class TestKeyModelsDropdownUI: + @pytest.mark.covers("mgmt.key.generate.happy_path", exercised_on=[]) + def test_create_teamless_key_offers_proxy_scope_and_persists( + self, ui_page: Page, client: ManagementClient, resources: ResourceManager + ) -> None: + _open_create_key_modal(ui_page) + + options = _models_dropdown_texts(ui_page, must_contain="gpt-5.5") + assert "All Proxy Models" in options, f"teamless create lost 'All Proxy Models': {options}" + assert "All Team Models" not in options, f"teamless create offered 'All Team Models': {options}" + + key = _submit_create_modal(ui_page, sentinel_label="All Proxy Models") + resources.defer(lambda: client.gateway.delete_key(key)) + + info = client.gateway.key_info(key) + assert info.models == ["all-proxy-models"], f"persisted models {info.models}" + assert info.team_id is None, f"teamless key persisted with team {info.team_id}" + + @pytest.mark.covers("mgmt.key.generate.happy_path", exercised_on=[]) + def test_create_team_key_offers_team_scope_and_persists( + self, ui_page: Page, client: ManagementClient, resources: ResourceManager + ) -> None: + team_alias = f"e2e-ui-team-{unique_marker()}" + team_id = _provision_team(client, resources, team_alias) + + _open_create_key_modal(ui_page) + _select_team(ui_page, team_alias) + + options = _models_dropdown_texts(ui_page, must_contain="All Team Models") + assert "gpt-5.5" in options, f"team key create lost the team's own model: {options}" + assert "All Proxy Models" not in options, f"team key create offered 'All Proxy Models': {options}" + assert "all-proxy-models" not in options, f"team key create offered the raw sentinel: {options}" + + key = _submit_create_modal(ui_page, sentinel_label="All Team Models") + resources.defer(lambda: client.gateway.delete_key(key)) + + info = client.gateway.key_info(key) + assert info.models == ["all-team-models"], f"persisted models {info.models}" + assert info.team_id == team_id, f"persisted team {info.team_id}, expected {team_id}" + + @pytest.mark.covers("mgmt.key.update.happy_path", exercised_on=[]) + def test_edit_teamless_key_offers_proxy_scope( + self, ui_page: Page, client: ManagementClient, resources: ResourceManager + ) -> None: + key_alias = f"e2e-ui-teamless-{unique_marker()}" + _provision_key(client, resources, key_alias) + + _open_key_edit_form(ui_page, key_alias) + + options = _models_dropdown_texts(ui_page, must_contain="gpt-5.5") + assert "All Proxy Models" in options, f"teamless edit lost 'All Proxy Models': {options}" + assert "All Team Models" not in options, f"teamless edit offered 'All Team Models': {options}" + + @pytest.mark.covers("mgmt.key.update.happy_path", exercised_on=[]) + def test_edit_team_key_offers_team_scope_only( + self, ui_page: Page, client: ManagementClient, resources: ResourceManager + ) -> None: + team_alias = f"e2e-ui-team-{unique_marker()}" + team_id = _provision_team(client, resources, team_alias) + key_alias = f"e2e-ui-teamkey-{unique_marker()}" + _provision_key(client, resources, key_alias, team_id=team_id) + + _open_key_edit_form(ui_page, key_alias) + + options = _models_dropdown_texts(ui_page, must_contain="All Team Models") + assert "gpt-5.5" in options, f"team key edit lost the team's own model: {options}" + assert "All Proxy Models" not in options, f"team key edit offered 'All Proxy Models': {options}" + assert "all-proxy-models" not in options, f"team key edit offered the raw sentinel: {options}" diff --git a/tests/e2e/management/test_management_e2e.py b/tests/e2e/management/test_management_e2e.py new file mode 100644 index 00000000000..cbd5db0d59f --- /dev/null +++ b/tests/e2e/management/test_management_e2e.py @@ -0,0 +1,286 @@ +"""Live e2e: the key/team/user/organization management routes' lifecycle contract. + +Each test creates its resources under unique names (deleted on teardown) and +asserts both halves of the contract: the recorded state (the info route reflects +the write) and the enforced behavior (the data plane serves or refuses traffic +accordingly). Key writes reach the data plane when its auth cache entry expires, +so the traffic-facing read-backs poll to a deadline instead of asserting once. +""" + +from __future__ import annotations + +import time +from collections.abc import Callable + +import pytest + +from e2e_config import unique_marker +from e2e_http import StreamingResponse +from lifecycle import ResourceManager +from management_client import ( + MODEL_ACCESS_DENIED_MARKER, + ROUTE_NOT_ALLOWED_MARKER, + ManagementClient, +) +from models import KeyGenerateBody, OrgNewBody, TeamNewBody, UserNewBody + +pytestmark = pytest.mark.e2e + +def _poll[T](client: ManagementClient, attempt: Callable[[], T | None], failure: str) -> T: + deadline = time.monotonic() + client.gateway.poll_timeout + while time.monotonic() < deadline: + found = attempt() + if found is not None: + return found + time.sleep(client.gateway.poll_interval) + pytest.fail(failure) + + +def _generate_key(client: ManagementClient, resources: ResourceManager, body: KeyGenerateBody) -> str: + key = client.gateway.generate_key(body) + resources.defer(lambda: client.gateway.delete_key(key)) + return key + + +def _create_team(client: ManagementClient, resources: ResourceManager, alias: str, models: list[str]) -> str: + team_id = client.create_team(TeamNewBody(team_alias=alias, models=models)) + resources.defer(lambda: client.delete_team(team_id)) + return team_id + + +def _create_user(client: ManagementClient, resources: ResourceManager, body: UserNewBody) -> str: + user_id = client.create_user(body) + resources.defer(lambda: client.delete_user(user_id)) + return user_id + + +def _is_model_denial(outcome: StreamingResponse) -> bool: + return outcome.status_code == 403 and MODEL_ACCESS_DENIED_MARKER in outcome.body + + +def _assert_model_denied(outcome: StreamingResponse, model: str) -> None: + assert outcome.status_code == 403, ( + f"chat on {model!r} outside the key's model list must be denied 403, got " + f"{outcome.status_code}: {outcome.body[:300]}" + ) + assert MODEL_ACCESS_DENIED_MARKER in outcome.body, ( + f"403 body must be a model-access denial, got: {outcome.body[:300]}" + ) + + +def _poll_chat_ok(client: ManagementClient, key: str, model: str) -> None: + def attempt() -> bool | None: + outcome = client.chat_status(key, model, f"reply with one word {unique_marker()}") + return True if outcome.ok else None + + _ = _poll(client, attempt, f"chat on {model} never succeeded for the key before the deadline") + + +def _poll_chat_denied(client: ManagementClient, key: str, model: str) -> None: + def attempt() -> bool | None: + return True if _is_model_denial(client.chat_status(key, model, f"say hi {unique_marker()}")) else None + + _ = _poll( + client, + attempt, + f"chat on {model} was never denied with {MODEL_ACCESS_DENIED_MARKER} before the deadline", + ) + + +def _poll_model_access_granted(client: ManagementClient, key: str, model: str) -> None: + """The key's model-access check stopped denying `model`: any outcome other than + the key_model_access_denied 403 (a 200, or an upstream error) proves the flip. + Requiring a 200 would couple the assertion to `model` being a healthy routable + upstream, which is not the enforcement contract under test.""" + + def attempt() -> bool | None: + outcome = client.chat_status(key, model, f"say hi {unique_marker()}") + if _is_model_denial(outcome) or outcome.status_code == 401: + return None + return True + + _ = _poll(client, attempt, f"model-access denial on {model} never lifted before the deadline") + + +class TestKeyRoutes: + @pytest.mark.covers("mgmt.key.generate.persists") + def test_generate_persists_to_key_info_and_scopes_chat( + self, client: ManagementClient, resources: ResourceManager + ) -> None: + alias = f"e2e-mgmt-key-{unique_marker()}" + key = _generate_key( + client, + resources, + KeyGenerateBody(models=["gemini-2.5-flash"], key_alias=alias, tpm_limit=424242), + ) + + info = client.gateway.key_info(key) + assert info.key_alias == alias, f"/key/info reports key_alias {info.key_alias!r}, configured {alias!r}" + assert info.models == ["gemini-2.5-flash"], ( + f"/key/info reports models {info.models}, configured ['gemini-2.5-flash']" + ) + assert info.tpm_limit == 424242, ( + f"/key/info reports tpm_limit {info.tpm_limit}, configured 424242" + ) + + _poll_chat_ok(client, key, "gemini-2.5-flash") + _assert_model_denied( + client.chat_status(key, "gpt-5.5", f"say hi {unique_marker()}"), "gpt-5.5" + ) + + @pytest.mark.covers("mgmt.key.update.persists") + def test_update_models_persists_and_flips_enforcement( + self, client: ManagementClient, resources: ResourceManager + ) -> None: + key = _generate_key(client, resources, KeyGenerateBody(models=["gemini-2.5-flash"])) + _poll_chat_ok(client, key, "gemini-2.5-flash") + _assert_model_denied( + client.chat_status(key, "gpt-5.5", f"say hi {unique_marker()}"), "gpt-5.5" + ) + + client.update_key_models(key, ["gpt-5.5"]) + + info = client.gateway.key_info(key) + assert info.models == ["gpt-5.5"], ( + f"/key/info reports models {info.models} after /key/update to ['gpt-5.5']" + ) + + _poll_model_access_granted(client, key, "gpt-5.5") + _poll_chat_denied(client, key, "gemini-2.5-flash") + + @pytest.mark.covers("mgmt.key.delete.persists") + def test_delete_revokes_the_key_on_chat(self, client: ManagementClient, resources: ResourceManager) -> None: + """The teardown's deferred delete fires again on the already-deleted key by + design: the deferred cleanup must survive this test failing before the + in-body delete, and a repeat /key/delete is a cheap no-op the warn-only + teardown absorbs.""" + key = _generate_key(client, resources, KeyGenerateBody(models=["gemini-2.5-flash"])) + _poll_chat_ok(client, key, "gemini-2.5-flash") + + client.delete_key_strict(key) + + def rejected() -> bool | None: + outcome = client.chat_status(key, "gemini-2.5-flash", f"say hi {unique_marker()}") + return True if outcome.status_code == 401 else None + + _ = _poll(client, rejected, "deleted key was still accepted on chat (never rejected 401) at the deadline") + + +class TestTeamRoutes: + @pytest.mark.covers("mgmt.team.new.persists") + def test_new_persists_to_team_info_and_binds_keys( + self, client: ManagementClient, resources: ResourceManager + ) -> None: + alias = f"e2e-mgmt-team-{unique_marker()}" + team_id = _create_team(client, resources, alias, ["gemini-2.5-flash"]) + + info = client.team_info(team_id) + assert info.team_alias == alias, f"/team/info reports team_alias {info.team_alias!r}, configured {alias!r}" + assert info.models == ["gemini-2.5-flash"], ( + f"/team/info reports models {info.models}, configured ['gemini-2.5-flash']" + ) + + key = _generate_key(client, resources, KeyGenerateBody(team_id=team_id)) + key_info = client.gateway.key_info(key) + assert key_info.team_id == team_id, ( + f"key generated under team {team_id} carries team_id {key_info.team_id!r} in /key/info" + ) + + @pytest.mark.covers("mgmt.team.member_add.persists") + def test_member_add_and_delete_persist_to_team_info( + self, client: ManagementClient, resources: ResourceManager + ) -> None: + user_id = _create_user( + client, + resources, + UserNewBody(user_email=f"e2e-mgmt-{unique_marker()}@example.com", user_role="internal_user"), + ) + team_id = _create_team(client, resources, f"e2e-mgmt-team-{unique_marker()}", ["gemini-2.5-flash"]) + + client.add_team_member(team_id, user_id) + member = next( + (entry for entry in client.team_info(team_id).members_with_roles if entry.user_id == user_id), None + ) + assert member is not None, f"/team/info does not list {user_id} after /team/member_add" + assert member.role == "user", f"member {user_id} added with role 'user' but /team/info reports {member.role!r}" + + client.delete_team_member(team_id, user_id) + remaining = client.team_info(team_id).members_with_roles + assert all(entry.user_id != user_id for entry in remaining), ( + f"/team/info still lists {user_id} after /team/member_delete" + ) + + +class TestUserRoutes: + @pytest.mark.covers("mgmt.user.new.happy_path") + def test_new_persists_to_user_info(self, client: ManagementClient, resources: ResourceManager) -> None: + email = f"e2e-mgmt-{unique_marker()}@example.com" + user_id = _create_user(client, resources, UserNewBody(user_email=email, user_role="internal_user")) + + info = client.user_info(user_id).user_info + assert info.user_email == email, f"/user/info reports user_email {info.user_email!r}, configured {email!r}" + assert info.user_role == "internal_user", ( + f"/user/info reports user_role {info.user_role!r}, configured 'internal_user'" + ) + + +class TestOrganizationRoutes: + @pytest.mark.covers("mgmt.organization.new.happy_path") + def test_new_persists_to_organization_info( + self, client: ManagementClient, resources: ResourceManager + ) -> None: + alias = f"e2e-mgmt-org-{unique_marker()}" + org_id = client.create_org(OrgNewBody(organization_alias=alias, models=["gemini-2.5-flash"])) + resources.defer(lambda: client.delete_org(org_id)) + + info = client.org_info(org_id) + assert info.organization_alias == alias, ( + f"/organization/info reports alias {info.organization_alias!r}, configured {alias!r}" + ) + assert info.models == ["gemini-2.5-flash"], ( + f"/organization/info reports models {info.models}, configured ['gemini-2.5-flash']" + ) + + +def _assert_route_forbidden(route: str, outcome: StreamingResponse) -> None: + assert outcome.status_code == 403, ( + f"llm-only key POSTing {route} must be denied exactly 403, got {outcome.status_code}: {outcome.body[:300]}" + ) + assert ROUTE_NOT_ALLOWED_MARKER in outcome.body, ( + f"{route} denial body must be a route-permission denial, got: {outcome.body[:300]}" + ) + + +class TestManagementRoutePermissions: + @pytest.mark.covers("other.auth.virtual_key.route_permission_enforced") + def test_llm_only_key_forbidden_from_management_writes( + self, client: ManagementClient, resources: ResourceManager + ) -> None: + key = client.llm_only_key() + resources.defer(lambda: client.gateway.delete_key(key)) + marker = unique_marker() + alias = f"e2e-mgmt-forbidden-key-{marker}" + team_id = f"e2e-mgmt-forbidden-team-{marker}" + user_id = f"e2e-mgmt-forbidden-user-{marker}" + + _assert_route_forbidden( + "/key/generate", client.key_generate_status(key, KeyGenerateBody(models=[], key_alias=alias)) + ) + _assert_route_forbidden( + "/team/new", client.team_new_status(key, TeamNewBody(team_alias=team_id, team_id=team_id)) + ) + _assert_route_forbidden( + "/user/new", + client.user_new_status( + key, + UserNewBody(user_email=f"{user_id}@example.com", user_role="internal_user", user_id=user_id), + ), + ) + + assert client.key_alias_count(alias) == 0, f"key {alias} was created despite the 403 route denial" + team_probe = client.team_info_status(team_id) + assert team_probe.status_code == 404, ( + f"team {team_id} was created despite the 403 route denial: " + f"/team/info returned {team_probe.status_code}: {team_probe.body[:300]}" + ) + assert client.user_count(user_id) == 0, f"user {user_id} was created despite the 403 route denial" diff --git a/tests/e2e/models.py b/tests/e2e/models.py index 0d0352df9af..e32f2709181 100644 --- a/tests/e2e/models.py +++ b/tests/e2e/models.py @@ -6,6 +6,8 @@ response validates without mirroring every proxy field. No untyped dicts. from __future__ import annotations +from typing import Literal + from pydantic import BaseModel, ConfigDict, RootModel # ---------- keys ---------- @@ -21,6 +23,22 @@ class BudgetWindow(BaseModel): max_budget: float +class KeyLoggingCallbackVars(BaseModel): + langfuse_public_key: str | None = None + langfuse_secret_key: str | None = None + langfuse_host: str | None = None + + +class KeyLoggingCallback(BaseModel): + callback_name: str + callback_type: str = "success_and_failure" + callback_vars: KeyLoggingCallbackVars + + +class KeyMetadata(BaseModel): + logging: list[KeyLoggingCallback] | None = None + + class KeyGenerateBody(BaseModel): models: list[str] = [] duration: str | None = None @@ -29,11 +47,16 @@ class KeyGenerateBody(BaseModel): budget_duration: str | None = None user_id: str | None = None team_id: str | None = None + organization_id: str | None = None budget_id: str | None = None + key_alias: str | None = None model_max_budget: dict[str, ModelBudgetEntry] | None = None + budget_fallbacks: dict[str, list[str]] | None = None budget_limits: list[BudgetWindow] | None = None tpm_limit: int | None = None rpm_limit: int | None = None + allowed_routes: list[str] | None = None + metadata: KeyMetadata | None = None class KeyGenerateResponse(BaseModel): @@ -56,6 +79,10 @@ class LiteLLMBudgetTable(BaseModel): class KeyInfo(BaseModel): + key_alias: str | None = None + models: list[str] = [] + tpm_limit: int | None = None + team_id: str | None = None spend: float | None = None max_budget: float | None = None budget_reset_at: str | None = None @@ -86,6 +113,27 @@ class ChatMessage(BaseModel): content: str +class ThinkingParam(BaseModel): + """Extended-thinking control shared by Anthropic and DeepSeek reasoner models. + DeepSeek accepts only ``type`` (enabled/disabled) and ignores budget_tokens; + Anthropic also honors budget_tokens. Sending ``type="disabled"`` is the + product-facing way a caller turns reasoning off (LIT-3686 / GH #27453).""" + + type: Literal["enabled", "disabled"] + budget_tokens: int | None = None + + +class ChatToolFunction(BaseModel): + name: str + description: str | None = None + parameters: dict[str, object] | None = None + + +class ChatTool(BaseModel): + type: str = "function" + function: ChatToolFunction + + class ChatBody(BaseModel): model: str messages: list[ChatMessage] @@ -93,20 +141,44 @@ class ChatBody(BaseModel): max_tokens: int | None = None user: str | None = None metadata: ChatMetadata | None = None + reasoning_effort: str | None = None + thinking: ThinkingParam | None = None + service_tier: str | None = None + tools: list[ChatTool] | None = None + tool_choice: str | None = None + guardrails: list[str] | None = None + + +class AnthropicMessagesBody(BaseModel): + model: str + messages: list[ChatMessage] + max_tokens: int + + +class AnthropicMessagesResponse(BaseModel): + model: str | None = None class OutMessage(BaseModel): content: str | None = None + reasoning_content: str | None = None class ChatChoice(BaseModel): message: OutMessage | None = None +class PromptTokensDetails(BaseModel): + cached_tokens: int | None = None + + class Usage(BaseModel): prompt_tokens: int | None = None completion_tokens: int | None = None total_tokens: int | None = None + cache_read_input_tokens: int | None = None + cache_creation_input_tokens: int | None = None + prompt_tokens_details: PromptTokensDetails | None = None class ChatResponse(BaseModel): @@ -114,6 +186,7 @@ class ChatResponse(BaseModel): model: str | None = None choices: list[ChatChoice] = [] usage: Usage | None = None + service_tier: str | None = None class EmbedBody(BaseModel): @@ -158,6 +231,7 @@ class OcrResponse(BaseModel): class SpendLogRow(BaseModel): request_id: str | None = None + api_key: str | None = None model: str | None = None spend: float | None = None status: str | None = None @@ -182,6 +256,25 @@ class SpendLogsParams(BaseModel): api_key: str | None = None +class SpendLogsPageParams(BaseModel): + """Query for /spend/logs/v2, which requires an explicit date window and + serves pages of at most 100 rows.""" + + start_date: str + end_date: str + page: int + page_size: int + api_key: str | None = None + + +class SpendLogsPage(BaseModel): + data: list[SpendLogRow] = [] + total: int + page: int + page_size: int + total_pages: int + + # ---------- spend calculate ---------- @@ -194,6 +287,20 @@ class SpendCalculateResponse(BaseModel): cost: float +# ---------- spend tags ---------- + + +class TagSpend(BaseModel): + individual_request_tag: str | None = None + log_count: int | None = None + total_spend: float | None = None + + +class SpendTagsResponse(RootModel[list[TagSpend]]): + """GET /spend/tags answers with a bare array of per-tag aggregates, not an + object wrapping them (that's /global/spend/tags). Read the rows off .root.""" + + # ---------- route probing ---------- @@ -263,3 +370,225 @@ class ModelInfoEntry(BaseModel): class ModelInfoResponse(BaseModel): data: list[ModelInfoEntry] = [] + + +class FileEntry(BaseModel): + id: str + + +class FileListResponse(BaseModel): + """GET /files answer. `data` is required on purpose: a 200 whose body lacks + the OpenAI-format file list must fail validation, not pass vacuously.""" + + data: list[FileEntry] + + +class FineTuningJobsParams(BaseModel): + custom_llm_provider: Literal["openai", "azure"] + + +class FineTuningJobEntry(BaseModel): + id: str + + +class FineTuningJobsResponse(BaseModel): + """GET /fine_tuning/jobs answer; `data` required for the same reason as + FileListResponse.""" + + data: list[FineTuningJobEntry] + + +# ---------- model management ---------- + + +class LiteLLMParamsBody(BaseModel): + """POST /model/new litellm_params: `model` is the only required field; `api_key` + et al may be an `os.environ/FOO` reference the proxy resolves at call time. + `input_cost_per_token`/`output_cost_per_token` register a per-deployment custom + pricing override; left None (and dropped from the body) the deployment keeps the + backend's canonical rate.""" + + model: str + api_key: str | None = None + api_base: str | None = None + api_version: str | None = None + realtime_protocol: str | None = None + aws_access_key_id: str | None = None + aws_secret_access_key: str | None = None + aws_region_name: str | None = None + vertex_project: str | None = None + vertex_location: str | None = None + vertex_credentials: str | None = None + gcs_bucket_name: str | None = None + bucket_name: str | None = None + s3_bucket_name: str | None = None + s3_region_name: str | None = None + s3_access_key_id: str | None = None + s3_secret_access_key: str | None = None + aws_batch_role_arn: str | None = None + input_cost_per_token: float | None = None + output_cost_per_token: float | None = None + + +ModelMode = Literal["batch", "realtime", "image_generation"] + + +class ModelInfoBody(BaseModel): + # id is left unset so the proxy assigns a unique model_id per deployment. + # Pinning it to the model_name made re-registrations of a fixed-name model + # (e.g. the batch suite's openai-batch) collide on the model_id unique + # constraint when a prior run's teardown had not removed the row. + id: str | None = None + mode: ModelMode | None = None + + +class ModelNewBody(BaseModel): + model_config = ConfigDict(protected_namespaces=()) + model_name: str + litellm_params: LiteLLMParamsBody + model_info: ModelInfoBody + + +class ModelNewResponse(BaseModel): + model_config = ConfigDict(protected_namespaces=()) + model_id: str + + +class ModelListEntry(BaseModel): + id: str + + +class ModelsListResponse(BaseModel): + """GET /v1/models on the data plane: the deployments the gateway can actually + serve right now. Used to confirm a freshly created model has propagated from + the control plane before a test calls it.""" + + data: tuple[ModelListEntry, ...] = () + + +class ModelDeleteBody(BaseModel): + id: str + + +# ---------- key / team / user / organization management ---------- + + +class KeyUpdateBody(BaseModel): + key: str + models: list[str] + + +class KeyListParams(BaseModel): + key_alias: str + + +class KeyListResponse(BaseModel): + total_count: int + + +class TeamMemberEntry(BaseModel): + role: Literal["admin", "user"] + user_id: str + + +class TeamNewBody(BaseModel): + team_alias: str + models: list[str] = [] + team_id: str | None = None + organization_id: str | None = None + + +class TeamNewResponse(BaseModel): + team_id: str + + +class TeamInfoParams(BaseModel): + team_id: str + + +class TeamData(BaseModel): + team_alias: str | None = None + models: list[str] = [] + members_with_roles: list[TeamMemberEntry] = [] + + +class TeamInfoResponse(BaseModel): + team_id: str + team_info: TeamData + + +class TeamMemberAddBody(BaseModel): + team_id: str + member: TeamMemberEntry + + +class TeamMemberDeleteBody(BaseModel): + team_id: str + user_id: str + + +class TeamDeleteBody(BaseModel): + team_ids: list[str] + + +UserRole = Literal["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"] + + +class UserNewBody(BaseModel): + user_email: str + user_role: UserRole + user_id: str | None = None + + +class UserNewResponse(BaseModel): + user_id: str + + +class UserInfoParams(BaseModel): + user_id: str + + +class UserData(BaseModel): + user_id: str | None = None + user_email: str | None = None + user_role: str | None = None + + +class UserInfoResponse(BaseModel): + user_id: str + user_info: UserData + + +class UserDeleteBody(BaseModel): + user_ids: list[str] + + +class UserListParams(BaseModel): + user_ids: str + + +class UserListResponse(BaseModel): + total: int + + +class OrgNewBody(BaseModel): + organization_alias: str + models: list[str] = [] + + +class OrgNewResponse(BaseModel): + organization_id: str + + +class OrgInfoParams(BaseModel): + organization_id: str + + +class OrgInfoResponse(BaseModel): + organization_id: str + organization_alias: str | None = None + models: list[str] = [] + + +class OrgDeleteBody(BaseModel): + organization_ids: list[str] diff --git a/tests/e2e/pytest.ini b/tests/e2e/pytest.ini index 7799f6b16a2..d3b2193226b 100644 --- a/tests/e2e/pytest.ini +++ b/tests/e2e/pytest.ini @@ -1,6 +1,6 @@ [pytest] # Config when any e2e suite under tests/e2e/ is run directly, e.g. -# uv run pytest tests/e2e/spend_tracking/ -v +# uv run pytest tests/e2e/quota_management/spend_tracking/ -v # The e2e marker is also registered in conftest.py for runs rooted elsewhere. addopts = --strict-markers --strict-config markers = diff --git a/tests/e2e/budgets/BUDGET_CODE_MATRIX.md b/tests/e2e/quota_management/budgets/BUDGET_CODE_MATRIX.md similarity index 100% rename from tests/e2e/budgets/BUDGET_CODE_MATRIX.md rename to tests/e2e/quota_management/budgets/BUDGET_CODE_MATRIX.md diff --git a/tests/e2e/budgets/BUDGET_TEST_COVERAGE_MATRIX.md b/tests/e2e/quota_management/budgets/BUDGET_TEST_COVERAGE_MATRIX.md similarity index 98% rename from tests/e2e/budgets/BUDGET_TEST_COVERAGE_MATRIX.md rename to tests/e2e/quota_management/budgets/BUDGET_TEST_COVERAGE_MATRIX.md index 62bfc1fdd41..7ff920a8d6d 100644 --- a/tests/e2e/budgets/BUDGET_TEST_COVERAGE_MATRIX.md +++ b/tests/e2e/quota_management/budgets/BUDGET_TEST_COVERAGE_MATRIX.md @@ -12,7 +12,7 @@ Pre-existing live coverage outside this suite: - `tests/local_testing/test_router_budget_limiter.py` - provider / tag / deployment budgets at the router. -This suite (`tests/e2e/budgets/`) adds the missing live coverage and runs +This suite (`tests/e2e/quota_management/budgets/`) adds the missing live coverage and runs on the shared lifecycle (every entity it creates is deleted on teardown). --- diff --git a/tests/e2e/budgets/budget_client.py b/tests/e2e/quota_management/budgets/budget_client.py similarity index 84% rename from tests/e2e/budgets/budget_client.py rename to tests/e2e/quota_management/budgets/budget_client.py index af8021f9b93..7b37c3af98e 100644 --- a/tests/e2e/budgets/budget_client.py +++ b/tests/e2e/quota_management/budgets/budget_client.py @@ -10,13 +10,15 @@ and response models are co-located here because only this suite uses them. from __future__ import annotations +import time from dataclasses import dataclass from pydantic import AliasPath, BaseModel, Field, RootModel from e2e_gateway import Gateway, build_gateway -from e2e_http import NoBody, StreamingResponse, Success, unwrap +from e2e_http import NoBody, Result, StreamingResponse, Success, unwrap from models import ( + AnthropicMessagesBody, BudgetWindow, ChatBody, ChatMessage, @@ -25,6 +27,9 @@ from models import ( ModelBudgetEntry, ) +_TEAM_READY_ATTEMPTS = 15 +_TEAM_READY_SLEEP_SECONDS = 0.4 + class UserNewBody(BaseModel): max_budget: float @@ -171,6 +176,7 @@ class BudgetClient: user_id: str | None = None, team_id: str | None = None, model_max_budget: dict[str, ModelBudgetEntry] | None = None, + budget_fallbacks: dict[str, list[str]] | None = None, budget_limits: list[BudgetWindow] | None = None, ) -> str: return self.gateway.generate_key( @@ -183,6 +189,7 @@ class BudgetClient: user_id=user_id, team_id=team_id, model_max_budget=model_max_budget, + budget_fallbacks=budget_fallbacks, budget_limits=budget_limits, ) ) @@ -217,6 +224,24 @@ class BudgetClient: ), ) + def messages( + self, + key: str, + model: str, + content: str, + *, + max_tokens: int = 16, + ) -> StreamingResponse: + return self.gateway.transport.send( + "/v1/messages", + headers=self.gateway.transport.bearer(key), + json=AnthropicMessagesBody( + model=model, + messages=[ChatMessage(role="user", content=content)], + max_tokens=max_tokens, + ), + ) + # ---- internal user -------------------------------------------------- def create_user(self, *, max_budget: float) -> str: @@ -278,7 +303,7 @@ class BudgetClient: organization_id: str | None = None, budget_limits: list[BudgetWindow] | None = None, ) -> str: - return unwrap( + team_id = unwrap( self.gateway.transport.post( "/team/new", headers=self.gateway.transport.master, @@ -291,6 +316,8 @@ class BudgetClient: response_type=TeamNewResponse, ) ).team_id + self._wait_for_team(team_id) + return team_id def delete_team(self, team_id: str) -> None: _ = self.gateway.transport.post( @@ -300,17 +327,43 @@ class BudgetClient: response_type=NoBody, ) + def _wait_for_team(self, team_id: str) -> None: + last: Result[TeamInfoResponse] | None = None + for _ in range(_TEAM_READY_ATTEMPTS): + last = self.gateway.transport.get( + "/team/info", + headers=self.gateway.transport.master, + params=TeamInfoParams(team_id=team_id), + response_type=TeamInfoResponse, + ) + match last: + case Success(): + return + case _: + time.sleep(_TEAM_READY_SLEEP_SECONDS) + assert last is not None + raise AssertionError(last) + def add_team_member(self, team_id: str, user_id: str, *, max_budget_in_team: float | None = None) -> None: - resp = self.gateway.transport.send( - "/team/member_add", - headers=self.gateway.transport.master, - json=TeamMemberAddBody( - team_id=team_id, - member=TeamMember(role="user", user_id=user_id), - max_budget_in_team=max_budget_in_team, - ), - ) - assert resp.ok, resp.body + last_body = "" + for attempt in range(_TEAM_READY_ATTEMPTS): + resp = self.gateway.transport.send( + "/team/member_add", + headers=self.gateway.transport.master, + json=TeamMemberAddBody( + team_id=team_id, + member=TeamMember(role="user", user_id=user_id), + max_budget_in_team=max_budget_in_team, + ), + ) + if resp.ok: + return + last_body = resp.body + if "doesn't exist" in resp.body and attempt + 1 < _TEAM_READY_ATTEMPTS: + time.sleep(_TEAM_READY_SLEEP_SECONDS) + continue + break + assert False, last_body def update_team_member( self, diff --git a/tests/e2e/budgets/conftest.py b/tests/e2e/quota_management/budgets/conftest.py similarity index 100% rename from tests/e2e/budgets/conftest.py rename to tests/e2e/quota_management/budgets/conftest.py diff --git a/tests/e2e/budgets/test_budget_crud_e2e.py b/tests/e2e/quota_management/budgets/test_budget_crud_e2e.py similarity index 96% rename from tests/e2e/budgets/test_budget_crud_e2e.py rename to tests/e2e/quota_management/budgets/test_budget_crud_e2e.py index e697eca0051..47473326f32 100644 --- a/tests/e2e/budgets/test_budget_crud_e2e.py +++ b/tests/e2e/quota_management/budgets/test_budget_crud_e2e.py @@ -15,6 +15,7 @@ from lifecycle import ResourceManager pytestmark = pytest.mark.e2e +@pytest.mark.covers("mgmt.budget.new.persists") def test_budget_crud_roundtrip(client: BudgetClient, resources: ResourceManager) -> None: budget_id = client.create_budget(max_budget=12.5, soft_budget=10.0, budget_duration="30d") resources.defer(lambda: client.delete_budget(budget_id)) @@ -36,6 +37,7 @@ def test_budget_crud_roundtrip(client: BudgetClient, resources: ResourceManager) ) +@pytest.mark.covers("mgmt.budget.delete.persists") def test_budget_delete_removes_it(client: BudgetClient, resources: ResourceManager) -> None: budget_id = client.create_budget(max_budget=1.0) resources.defer(lambda: client.delete_budget(budget_id)) diff --git a/tests/e2e/budgets/test_budget_enforcement_e2e.py b/tests/e2e/quota_management/budgets/test_budget_enforcement_e2e.py similarity index 86% rename from tests/e2e/budgets/test_budget_enforcement_e2e.py rename to tests/e2e/quota_management/budgets/test_budget_enforcement_e2e.py index d03288b637a..dbe1cfa4ea8 100644 --- a/tests/e2e/budgets/test_budget_enforcement_e2e.py +++ b/tests/e2e/quota_management/budgets/test_budget_enforcement_e2e.py @@ -134,11 +134,26 @@ def _case_id(case_cls: Type[_BudgetCase]) -> str: @pytest.mark.parametrize( "case_cls", [ - KeyBudgetCase, - InternalUserBudgetCase, - EndUserBudgetCase, - OrganizationBudgetCase, - TeamMemberBudgetCase, + pytest.param( + KeyBudgetCase, + marks=pytest.mark.covers("quota_management.budget.key.blocks_over_limit"), + ), + pytest.param( + InternalUserBudgetCase, + marks=pytest.mark.covers("quota_management.budget.internal_user.blocks_over_limit"), + ), + pytest.param( + EndUserBudgetCase, + marks=pytest.mark.covers("quota_management.budget.end_user.blocks_over_limit"), + ), + pytest.param( + OrganizationBudgetCase, + marks=pytest.mark.covers("quota_management.budget.organization.blocks_over_limit"), + ), + pytest.param( + TeamMemberBudgetCase, + marks=pytest.mark.covers("quota_management.budget.team_member.blocks_over_limit"), + ), ], ids=_case_id, ) diff --git a/tests/e2e/quota_management/budgets/test_budget_fallback_e2e.py b/tests/e2e/quota_management/budgets/test_budget_fallback_e2e.py new file mode 100644 index 00000000000..56197dcfee2 --- /dev/null +++ b/tests/e2e/quota_management/budgets/test_budget_fallback_e2e.py @@ -0,0 +1,61 @@ +"""Live e2e: a virtual key's per-model `budget_fallbacks` reroutes `/v1/messages` +transparently from an exhausted Anthropic model to an OpenAI model, instead of +blocking the caller with a `budget_exceeded` error. Coverage for the +budget_fallbacks feature in litellm/proxy/hooks/model_max_budget_limiter.py. +""" + +import time + +import pytest + +from budget_client import BudgetClient, model_budget +from e2e_config import unique_marker +from lifecycle import ResourceManager +from models import AnthropicMessagesResponse + +pytestmark = pytest.mark.e2e + +PRIMARY_MODEL = "claude-haiku-4-5" +FALLBACK_MODEL = "gpt-5.5" + + +@pytest.mark.covers("quota_management.budget.fallback.routes_to_fallback") +def test_budget_fallback_reroutes_anthropic_messages_to_openai( + client: BudgetClient, resources: ResourceManager +) -> None: + key = client.generate_key( + model_max_budget=model_budget(PRIMARY_MODEL, 1e-6), + budget_fallbacks={PRIMARY_MODEL: [FALLBACK_MODEL]}, + ) + resources.defer(lambda: client.delete_key(key)) + + # Exhaust the primary model's near-zero budget. Once exceeded, every + # subsequent /v1/messages call for this key must reroute to the fallback + # instead of surfacing a budget_exceeded block. + served_by = None + deadline = time.monotonic() + 60 + while time.monotonic() < deadline: + result = client.messages( + key, PRIMARY_MODEL, f"hi {unique_marker()}", max_tokens=16 + ) + if not result.ok: + pytest.fail( + "budget_fallbacks must reroute transparently, never surface a " + f"block; status={result.status_code} body={result.body[:300]}" + ) + served_by = AnthropicMessagesResponse.model_validate_json(result.body).model + if served_by is not None and FALLBACK_MODEL in served_by: + break + time.sleep(1) + assert served_by is not None and FALLBACK_MODEL in served_by, ( + f"{PRIMARY_MODEL}'s budget_fallbacks never rerouted to {FALLBACK_MODEL}" + ) + + # The rerouted call must be recorded under the fallback model, not the + # exhausted primary - proving spend tracking followed the reroute. + rows = client.gateway.poll_logs_for_key( + key, predicate=lambda rows: any(FALLBACK_MODEL in (r.model or "") for r in rows) + ) + assert any(FALLBACK_MODEL in (r.model or "") for r in rows), ( + f"no spend log recorded against {FALLBACK_MODEL} after the reroute" + ) diff --git a/tests/e2e/quota_management/budgets/test_budget_reset_advances_e2e.py b/tests/e2e/quota_management/budgets/test_budget_reset_advances_e2e.py new file mode 100644 index 00000000000..bc634f70b13 --- /dev/null +++ b/tests/e2e/quota_management/budgets/test_budget_reset_advances_e2e.py @@ -0,0 +1,232 @@ +"""Live e2e: regression guards for #25109 (budget resets stopped working). + +The existing test_budget_reset_e2e.py / test_multi_window_budget_e2e.py prove a +blocked key flows again after its window. #25109 stored multi-budget-window data +in nullable JSON columns and filtered eligible rows with a `not: None`-style Prisma +filter that misbehaves on a nullable JSON column, so due rows were either skipped +(budget_reset_at stayed pinned, spend never cleared) or the reset path errored +(a non-budget 5xx leaked to callers). These tests assert the precise invariants +that bug broke, built up START-SLOW from scheduling -> enforcement -> the reset +strictly advancing -> the JSON-backed multi-window / team-member edges -> the +error path. They EXTEND the happy-path modules rather than duplicate them: each +asserts a delta (before datetime: + return datetime.fromisoformat(value.replace("Z", "+00:00")) + + +def _drive_to_block(client: BudgetClient, key: str) -> None: + """Spend until the cap blocks; fails loudly if enforcement never trips.""" + for _ in range(20): + result = _call(client, key) + if is_budget_block(result): + return + require_successful_call(result) + time.sleep(2) + pytest.fail("budget never enforced before block") + + +# ---- Rung 1: scheduling exists at creation ----------------------------------- + + +def test_key_with_budget_duration_schedules_reset_at_creation( + client: BudgetClient, resources: ResourceManager +) -> None: + """Baseline: a key created with a budget_duration has budget_reset_at populated + immediately. The reset job can only advance a timestamp that was scheduled in + the first place; everything below depends on this.""" + key = client.generate_key(max_budget=TINY_CAP, budget_duration=f"{WINDOW_SECONDS}s") + resources.defer(lambda: client.delete_key(key)) + + info = client.gateway.key_info(key) + assert info.budget_reset_at is not None, "budget_duration set no budget_reset_at" + assert _as_datetime(info.budget_reset_at) > _as_datetime("1970-01-01T00:00:00Z") + + +# ---- Rung 2: enforcement trips at the cap ------------------------------------ + + +@pytest.mark.covers("quota_management.budget.key.blocks_over_limit") +def test_key_spend_blocks_at_cap(client: BudgetClient, resources: ResourceManager) -> None: + """Sanity that the tiny cap is enforced before we test that it resets: spend + accrues across calls and eventually returns budget_exceeded, never a 5xx.""" + key = client.generate_key(max_budget=TINY_CAP, budget_duration=f"{WINDOW_SECONDS}s") + resources.defer(lambda: client.delete_key(key)) + + # _drive_to_block is the enforcement proof: it fails unless a budget_exceeded + # block follows successful (non-5xx) calls. key_info.spend is deliberately not + # asserted - it is the DB-persisted field that flushes ~60s later + # (proxy_batch_write_at), so reading it right after the block races to 0.0. + _drive_to_block(client, key) + + +# ---- Rung 3: the core regression - reset_at strictly advances + spend zeroes -- + + +@pytest.mark.covers("quota_management.budget.key.resets_after_window") +def test_key_budget_reset_at_advances_after_window( + client: BudgetClient, resources: ResourceManager +) -> None: + """The core #25109 guard: after the window elapses the reset job must move + budget_reset_at strictly forward AND zero key.spend. The broken nullable-JSON + filter left eligible rows untouched, so the timestamp stayed pinned and spend + never cleared. Asserting before before, ( + "budget_reset_at did not advance past the pre-reset value" + ) + assert (info.spend or 0.0) < TINY_CAP, f"spend not cleared after reset: {info.spend}" + return + pytest.fail(f"key budget never reset within {RESET_DEADLINE_SECONDS}s") + + +# ---- Rung 4: multi-window - tight window resets, roomy window keeps spend ----- + + +@pytest.mark.covers("quota_management.budget.key_multi_window.resets_windows_independently") +def test_multi_window_key_resets_each_window_independently( + client: BudgetClient, resources: ResourceManager +) -> None: + """The JSON-backed path #25109 specifically touched. A tight 30s window and a + roomy 1m window: the tight window must reset on its own boundary while the roomy + window keeps its accumulated spend (independent per-window reset). The + nullable-JSON filter bug skipped these JSON-backed rows entirely, so the tight + window never came back; a job that ERRORS on the JSON column would surface here + as a non-budget 5xx, which we reject throughout the wait.""" + key = client.generate_key( + budget_limits=[ + BudgetWindow(budget_duration=f"{WINDOW_SECONDS}s", max_budget=TINY_CAP), + BudgetWindow(budget_duration="1m", max_budget=1.0), + ] + ) + resources.defer(lambda: client.delete_key(key)) + + start = time.monotonic() + _drive_to_block(client, key) + spend_at_block = client.gateway.key_info(key).spend or 0.0 + + deadline = time.monotonic() + RESET_DEADLINE_SECONDS + while time.monotonic() < deadline: + time.sleep(5) + result = _call(client, key) + if result.ok: + elapsed = time.monotonic() - start + assert elapsed < WINDOW_SECONDS + 90, ( + f"tight window reset took {elapsed:.0f}s - too long for {WINDOW_SECONDS}s" + ) + assert (client.gateway.key_info(key).spend or 0.0) >= spend_at_block, ( + "roomy window spend was wiped when only the tight window should reset" + ) + return + assert is_budget_block(result), f"non-budget error during reset wait: {result.body[:200]}" + pytest.fail(f"tight window never reset within {RESET_DEADLINE_SECONDS}s") + + +# ---- Rung 5: team-member window advances (JSON-backed per-team budget) -------- + + +@pytest.mark.covers("quota_management.budget.team_member.resets_after_window") +def test_team_member_budget_reset_at_advances( + client: BudgetClient, resources: ResourceManager +) -> None: + """Per-team member windows are also JSON-backed. member_budget_reset_at must + advance after the window; the explicit before before: + return + pytest.fail(f"member budget_reset_at never advanced past {before.isoformat()} in {RESET_DEADLINE_SECONDS}s") + + +# ---- Rung 6: error-path edge - resets surface as blocks, never 5xx ----------- + + +def test_reset_wait_never_yields_non_budget_error( + client: BudgetClient, resources: ResourceManager +) -> None: + """The other #25109 failure mode: a reset job that ERRORS on the nullable-JSON + column surfaces to the caller as a non-budget 5xx. Across the whole reset wait + every non-ok response must be a budget block (is_budget_block) and never a + server error; this guards the error path independently of whether the reset + eventually fires.""" + key = client.generate_key(max_budget=TINY_CAP, budget_duration=f"{WINDOW_SECONDS}s") + resources.defer(lambda: client.delete_key(key)) + + _drive_to_block(client, key) + + saw_reset = False + deadline = time.monotonic() + RESET_DEADLINE_SECONDS + while time.monotonic() < deadline: + time.sleep(5) + result = _call(client, key) + if result.ok: + saw_reset = True + break + assert is_budget_block(result), ( + f"reset wait yielded a non-budget error (likely a JSON-column reset crash): {result.body[:200]}" + ) + assert saw_reset, f"key budget never reset within {RESET_DEADLINE_SECONDS}s" diff --git a/tests/e2e/budgets/test_budget_reset_e2e.py b/tests/e2e/quota_management/budgets/test_budget_reset_e2e.py similarity index 96% rename from tests/e2e/budgets/test_budget_reset_e2e.py rename to tests/e2e/quota_management/budgets/test_budget_reset_e2e.py index dcf776db9a2..bdbee027f28 100644 --- a/tests/e2e/budgets/test_budget_reset_e2e.py +++ b/tests/e2e/quota_management/budgets/test_budget_reset_e2e.py @@ -26,6 +26,7 @@ def _call(client: BudgetClient, key: str): ) +@pytest.mark.covers("quota_management.budget.key.resets_after_window") def test_key_budget_resets_after_duration( client: BudgetClient, resources: ResourceManager ) -> None: diff --git a/tests/e2e/budgets/test_model_max_budget_e2e.py b/tests/e2e/quota_management/budgets/test_model_max_budget_e2e.py similarity index 96% rename from tests/e2e/budgets/test_model_max_budget_e2e.py rename to tests/e2e/quota_management/budgets/test_model_max_budget_e2e.py index 44e6a333ef0..4d0df2c35ea 100644 --- a/tests/e2e/budgets/test_model_max_budget_e2e.py +++ b/tests/e2e/quota_management/budgets/test_model_max_budget_e2e.py @@ -28,6 +28,7 @@ def _call(client: BudgetClient, key: str, model: str): return result +@pytest.mark.covers("quota_management.budget.model_max.isolates_per_model") def test_model_max_budget_isolates_per_model( client: BudgetClient, resources: ResourceManager ) -> None: diff --git a/tests/e2e/budgets/test_multi_window_budget_e2e.py b/tests/e2e/quota_management/budgets/test_multi_window_budget_e2e.py similarity index 97% rename from tests/e2e/budgets/test_multi_window_budget_e2e.py rename to tests/e2e/quota_management/budgets/test_multi_window_budget_e2e.py index 553ad1ce701..5981160ccc8 100644 --- a/tests/e2e/budgets/test_multi_window_budget_e2e.py +++ b/tests/e2e/quota_management/budgets/test_multi_window_budget_e2e.py @@ -29,6 +29,7 @@ def _call(client: BudgetClient, key: str): ) +@pytest.mark.covers("quota_management.budget.key_multi_window.blocks_then_resets") def test_short_window_blocks_then_resets( client: BudgetClient, resources: ResourceManager ) -> None: diff --git a/tests/e2e/budgets/test_soft_budget_e2e.py b/tests/e2e/quota_management/budgets/test_soft_budget_e2e.py similarity index 94% rename from tests/e2e/budgets/test_soft_budget_e2e.py rename to tests/e2e/quota_management/budgets/test_soft_budget_e2e.py index 407de7ae467..2006efb5a57 100644 --- a/tests/e2e/budgets/test_soft_budget_e2e.py +++ b/tests/e2e/quota_management/budgets/test_soft_budget_e2e.py @@ -17,6 +17,7 @@ from lifecycle import ResourceManager pytestmark = pytest.mark.e2e +@pytest.mark.covers("quota_management.budget.soft.alerts_without_blocking") def test_soft_budget_does_not_block( client: BudgetClient, resources: ResourceManager ) -> None: diff --git a/tests/e2e/budgets/test_spend_counter_reseed_e2e.py b/tests/e2e/quota_management/budgets/test_spend_counter_reseed_e2e.py similarity index 73% rename from tests/e2e/budgets/test_spend_counter_reseed_e2e.py rename to tests/e2e/quota_management/budgets/test_spend_counter_reseed_e2e.py index a6860aeef43..52fe112ed29 100644 --- a/tests/e2e/budgets/test_spend_counter_reseed_e2e.py +++ b/tests/e2e/quota_management/budgets/test_spend_counter_reseed_e2e.py @@ -23,6 +23,7 @@ from threading import Barrier from typing import TYPE_CHECKING import pytest +from pydantic import TypeAdapter, ValidationError from budget_client import BudgetClient from e2e_config import unique_marker @@ -42,6 +43,8 @@ BURST = 6 # expires the counter; this waits out both. COLD_WAIT_SECONDS = 80 +_JSON_FLOAT: TypeAdapter[float] = TypeAdapter(float) + def _redis() -> "redis.Redis[str] | RedisCluster[str]": """The proxy's Redis. The deployed runner sets REDIS_HOST to the serverless @@ -64,25 +67,46 @@ def _redis() -> "redis.Redis[str] | RedisCluster[str]": ) +def _parse_counter(raw: object) -> float | None: + if raw is None: + return None + if isinstance(raw, (int, float)): + return float(raw) + text = str(raw).strip() + if not text: + return None + try: + return float(text) + except ValueError: + pass + try: + return _JSON_FLOAT.validate_json(text) + except ValidationError: + return None + + def _spend_counter(rds: "redis.Redis[str] | RedisCluster[str]", key: str) -> float | None: - """The shared spend counter for `key`, or None if it is cold. A cluster client - can't run a keyspace SCAN that spans shards, so read the key directly - the stage - gateway sets no cache namespace, so the key is the bare ``spend:key:{sha256(key)}``. - A standalone client matches by suffix, so the local cache namespace (litellm.caching) - need not be hard-coded here.""" + """The shared spend counter for `key`, or None if it is cold. + + The gateway keys counters as ``spend:key:{sha256(raw_sk)}``, optionally under a + redis namespace prefix. Cluster mode cannot SCAN all shards, so try the bare key + and a few common namespaces; standalone redis uses a suffix SCAN. + """ from redis.cluster import RedisCluster digest = hashlib.sha256(key.encode()).hexdigest() suffix = f"spend:key:{digest}" if isinstance(rds, RedisCluster): - raw = rds.get(suffix) - return float(raw) if raw is not None else None + for candidate in (suffix, f"litellm:{suffix}", f"litellm.caching:{suffix}"): + parsed = _parse_counter(rds.get(candidate)) + if parsed is not None: + return parsed + return None matches = list(rds.scan_iter(match=f"*{suffix}")) if not matches: return None - raw = rds.get(matches[0]) - return float(raw) if raw is not None else None + return _parse_counter(rds.get(matches[0])) def _chat(client: BudgetClient, key: str) -> StreamingResponse: @@ -97,19 +121,7 @@ def _accumulate(client: BudgetClient, key: str, count: int) -> None: list(pool.map(one, range(count))) -def _burst(client: BudgetClient, key: str, count: int) -> None: - """Fire `count` requests that start together, so multiple workers reseed the cold - counter concurrently rather than one warming it before the others arrive.""" - barrier = Barrier(count) - - def one(_: int) -> StreamingResponse: - barrier.wait() - return _chat(client, key) - - with ThreadPoolExecutor(max_workers=count) as pool: - list(pool.map(one, range(count))) - - +@pytest.mark.covers("quota_management.budget.spend_counter.reseed_matches_db") def test_cold_counter_reseed_keeps_counter_equal_to_db_spend( client: BudgetClient, resources: ResourceManager ) -> None: @@ -132,10 +144,28 @@ def test_cold_counter_reseed_keeps_counter_equal_to_db_spend( db_spend = client.gateway.key_info(key).spend or 0.0 assert db_spend > 0, f"no DB spend accumulated from real calls: {db_spend}" - _burst(client, key, BURST) - time.sleep(3) + burst_results = [] + barrier = Barrier(BURST) + + def one(_: int) -> StreamingResponse: + barrier.wait() + return _chat(client, key) + + with ThreadPoolExecutor(max_workers=BURST) as pool: + burst_results = list(pool.map(one, range(BURST))) + assert all(r.ok for r in burst_results), ( + "some burst calls failed; cannot exercise concurrent reseed. " + f"statuses={[r.status_code for r in burst_results]}" + ) + + counter: float | None = None + deadline = time.monotonic() + 15 + while time.monotonic() < deadline: + counter = _spend_counter(rds, key) + if counter is not None: + break + time.sleep(0.5) - counter = _spend_counter(rds, key) assert counter is not None, "the burst did not reseed the cold counter" assert db_spend * 0.95 <= counter < db_spend * 1.7, ( f"redis spend counter {counter} does not equal DB spend {db_spend} (expected ~equal " diff --git a/tests/e2e/budgets/test_tag_budget_e2e.py b/tests/e2e/quota_management/budgets/test_tag_budget_e2e.py similarity index 74% rename from tests/e2e/budgets/test_tag_budget_e2e.py rename to tests/e2e/quota_management/budgets/test_tag_budget_e2e.py index 7cec5bc96c1..b0068c66630 100644 --- a/tests/e2e/budgets/test_tag_budget_e2e.py +++ b/tests/e2e/quota_management/budgets/test_tag_budget_e2e.py @@ -26,13 +26,14 @@ def _tagged_call(client: BudgetClient, key: str, tag: str): "claude-haiku-4-5", f"hi {unique_marker()}", tags=[tag], - max_tokens=16, + max_tokens=64, ) if not result.ok and not is_budget_block(result): require_successful_call(result) return result +@pytest.mark.covers("quota_management.budget.tag.blocks_over_limit") def test_tag_budget_blocks_tagged_requests( client: BudgetClient, scoped_key: str, resources: ResourceManager ) -> None: @@ -40,17 +41,20 @@ def test_tag_budget_blocks_tagged_requests( client.create_tag(budgeted_tag, max_budget=TINY_BUDGET) resources.defer(lambda: client.delete_tag(budgeted_tag)) - # Requests under the budgeted tag get blocked once its spend is exceeded. - blocked = False - deadline = time.monotonic() + 60 - while time.monotonic() < deadline: - if is_budget_block(_tagged_call(client, scoped_key, budgeted_tag)): - blocked = True - break - time.sleep(1) + first = _tagged_call(client, scoped_key, budgeted_tag) + if is_budget_block(first): + blocked = True + else: + require_successful_call(first) + blocked = False + deadline = time.monotonic() + 120 + while time.monotonic() < deadline: + if is_budget_block(_tagged_call(client, scoped_key, budgeted_tag)): + blocked = True + break + time.sleep(1) assert blocked, f"tag budget for {budgeted_tag!r} never enforced" - # A request with an unbudgeted tag on the same key is unaffected. free_tag = f"e2e-free-tag-{unique_marker()}" other = _tagged_call(client, scoped_key, free_tag) assert not is_budget_block(other), ( diff --git a/tests/e2e/budgets/test_team_member_budget_e2e.py b/tests/e2e/quota_management/budgets/test_team_member_budget_e2e.py similarity index 98% rename from tests/e2e/budgets/test_team_member_budget_e2e.py rename to tests/e2e/quota_management/budgets/test_team_member_budget_e2e.py index 301617bfdca..2717b23d9dc 100644 --- a/tests/e2e/budgets/test_team_member_budget_e2e.py +++ b/tests/e2e/quota_management/budgets/test_team_member_budget_e2e.py @@ -97,6 +97,7 @@ class TestTeamMemberBudget: f"call {row.request_id} logged under user {row.user}, not member {member.user_id}" ) + @pytest.mark.covers("quota_management.budget.team_member.blocks_over_limit") def test_member_spend_over_budget_is_blocked(self, client: BudgetClient, member: _Member) -> None: for _ in range(40): result = client.chat(member.key, MODEL, f"spend {unique_marker()}", max_tokens=16) diff --git a/tests/e2e/budgets/test_team_member_budget_reset_e2e.py b/tests/e2e/quota_management/budgets/test_team_member_budget_reset_e2e.py similarity index 96% rename from tests/e2e/budgets/test_team_member_budget_reset_e2e.py rename to tests/e2e/quota_management/budgets/test_team_member_budget_reset_e2e.py index 2749f16a26e..5d097a81f92 100644 --- a/tests/e2e/budgets/test_team_member_budget_reset_e2e.py +++ b/tests/e2e/quota_management/budgets/test_team_member_budget_reset_e2e.py @@ -16,6 +16,7 @@ def _as_datetime(value: str) -> datetime: return datetime.fromisoformat(value.replace("Z", "+00:00")) +@pytest.mark.covers("quota_management.budget.team_member.resets_after_window") def test_team_member_budget_reset_keeps_advancing(client: BudgetClient, resources: ResourceManager) -> None: team_id = client.create_team(alias=f"e2e-member-reset-{unique_marker()}", max_budget=100.0) resources.defer(lambda: client.delete_team(team_id)) diff --git a/tests/e2e/budgets/test_team_multi_window_budget_e2e.py b/tests/e2e/quota_management/budgets/test_team_multi_window_budget_e2e.py similarity index 93% rename from tests/e2e/budgets/test_team_multi_window_budget_e2e.py rename to tests/e2e/quota_management/budgets/test_team_multi_window_budget_e2e.py index c58e74db965..c89807fd59d 100644 --- a/tests/e2e/budgets/test_team_multi_window_budget_e2e.py +++ b/tests/e2e/quota_management/budgets/test_team_multi_window_budget_e2e.py @@ -32,6 +32,7 @@ def _call(client: BudgetClient, key: str): return client.chat(key, "claude-haiku-4-5", f"team-window {unique_marker()}", max_tokens=16) +@pytest.mark.covers("quota_management.budget.team_multi_window.blocks_then_resets") def test_team_short_window_blocks_then_resets(client: BudgetClient, resources: ResourceManager) -> None: team_id = client.create_team( alias=f"e2e-team-window-{unique_marker()}", @@ -41,19 +42,19 @@ def test_team_short_window_blocks_then_resets(client: BudgetClient, resources: R ], ) resources.defer(lambda: client.delete_team(team_id)) - key = client.generate_key(team_id=team_id) + key = client.generate_key(team_id=team_id, models=["claude-haiku-4-5"]) resources.defer(lambda: client.delete_key(key)) # 1. exhaust the tight window -> litellm returns budget_exceeded start = time.monotonic() blocked = False - for _ in range(20): + for _ in range(30): result = _call(client, key) if is_budget_block(result): blocked = True break require_successful_call(result) - time.sleep(2) + time.sleep(1) assert blocked, f"team {WINDOW_SECONDS}s window never enforced" # 2. the window resets at the next wall-clock-aligned boundary (up to a window diff --git a/tests/e2e/spend_tracking/SPEND_TRACKING_COVERAGE_MATRIX.md b/tests/e2e/quota_management/spend_tracking/SPEND_TRACKING_COVERAGE_MATRIX.md similarity index 81% rename from tests/e2e/spend_tracking/SPEND_TRACKING_COVERAGE_MATRIX.md rename to tests/e2e/quota_management/spend_tracking/SPEND_TRACKING_COVERAGE_MATRIX.md index 53c4d4ace83..062ef8d73da 100644 --- a/tests/e2e/spend_tracking/SPEND_TRACKING_COVERAGE_MATRIX.md +++ b/tests/e2e/quota_management/spend_tracking/SPEND_TRACKING_COVERAGE_MATRIX.md @@ -19,7 +19,7 @@ proxy + SpendLogs rows. Status: `covered` / `partial` / `gap`. |------|----------|-------|--------|----------| | `_get_status_for_spend_log` | `test_spend_tracking_utils.py` | unit | covered | yes (status read off the row) | | cache-hit `request_id` suffix | `test_spend_tracking_utils.py` | unit | covered | yes (`test_cache_hit_is_zero_cost_and_suffixed`) | -| failure status + zero spend | `test_spend_tracking_utils.py` | unit | covered | no (live failure logging is non-deterministic across providers) | +| failure status + zero spend | `test_spend_tracking_utils.py` | unit | partial | yes (`test_failure_call_writes_failure_status_row`) | | per-model / per-provider attribution | `test_spend_tracking_utils.py` | unit | covered | yes (`test_each_model_on_a_shared_key_gets_its_own_row`) | | field population (model/tokens/api_key/team/org) | `test_spend_tracking_utils.py` | unit | partial | yes (asserts real values) | | `request_tags` propagation | `test_db_spend_update_writer.py` | unit | partial | yes (`test_request_tags_round_trip`) | @@ -40,9 +40,10 @@ proxy + SpendLogs rows. Status: `covered` / `partial` / `gap`. | Entity | Existing | Status | Live e2e | |--------|----------|--------|----------| | API key | `test_db_spend_update_writer.py`, `test_spend_counters.py` | covered | yes (`test_key_spend_equals_sum_of_logs`) | -| Tag | `test_update_daily_tag_spend.py` | partial | yes (`test_request_tags_round_trip`, propagation only) | +| Tag | `test_update_daily_tag_spend.py` | partial | yes (`test_tag_spend_matches_sum_of_tagged_logs`) | | End-user | `test_proxy_update_spend.py` | covered | yes | -| Spend == sum(logs) consistency | none | gap | yes (key aggregate == sum of rows) | +| Spend == sum(logs) consistency | none | gap | yes (key + tag aggregate == sum of rows) | +| Concurrent increments (one key, parallel writers) | `tests/spend_tracking_tests/test_spend_accuracy_tests.py` (burst) | partial | yes (`test_burst_of_concurrent_calls_loses_no_spend`) | ## Spend read endpoints (verification surface) @@ -50,7 +51,8 @@ proxy + SpendLogs rows. Status: `covered` / `partial` / `gap`. |----------|----------|--------|----------| | `/spend/logs` (request_id / api_key) | `test_spend_management_endpoints.py` | covered | yes (primary read path; `test_spend_logs_endpoint_returns_spend` asserts 200 + spend, never 5xx) | | `/spend/calculate` | `local_testing/test_spend_calculate_endpoint.py` | covered | yes (`test_spend_calculate_returns_nonzero_cost`) | -| `/spend/tags` | `test_spend_management_endpoints.py` | partial | yes (`test_spend_routes.py` route probe) | +| `/spend/tags` | `test_spend_management_endpoints.py` | partial | yes (tag accuracy test) | +| `/spend/logs/v2` pagination (total/total_pages/out-of-range) | `test_spend_query_optimization.py` | covered | yes (`test_spend_logs_v2_pagination_caps_pages_and_keeps_total`; filter takes the hashed token, not the raw key) | | whole spend GET surface (22 routes) | unit per-handler | partial | yes (`test_spend_routes.py` probes each for 404/5xx) | ## What this suite pins @@ -63,10 +65,14 @@ proxy + SpendLogs rows. Status: `covered` / `partial` / `gap`. | `test_cache_hit_is_zero_cost_and_suffixed` | cache hits not double-charged; `_cache_hit` suffix | | `test_key_spend_equals_sum_of_logs` | key aggregate == sum of rows | | `test_request_tags_round_trip` | tags persist onto the row | +| `test_tag_spend_matches_sum_of_tagged_logs` | `/spend/tags` SUM/COUNT == tagged rows | | `test_end_user_spend_attributed_on_row` | `end_user` attributed + costed | | `test_each_model_on_a_shared_key_gets_its_own_row` | per-model/provider rows, correct model + cost, distinct request_ids matching response id | +| `test_failure_call_writes_failure_status_row` | failed call -> `status=failure`, `spend=0` | | `test_spend_calculate_returns_nonzero_cost` | cost-map smoke (no batch wait) | | `test_spend_logs_endpoint_returns_spend` | `/spend/logs` returns 200 + the key's spend, never a 5xx (intermittent-500 regression) | +| `test_burst_of_concurrent_calls_loses_no_spend` | N parallel calls on one key: N distinct costed rows, key aggregate == sum (no lost increments) | +| `test_spend_logs_v2_pagination_caps_pages_and_keeps_total` | `/spend/logs/v2` page cap, stable total on out-of-range page, zero total on no-match filter | | `test_spend_routes.py` (23) | no spend route 404s or 5xxs | ## Design + timing diff --git a/tests/e2e/quota_management/spend_tracking/conftest.py b/tests/e2e/quota_management/spend_tracking/conftest.py new file mode 100644 index 00000000000..0e80764236b --- /dev/null +++ b/tests/e2e/quota_management/spend_tracking/conftest.py @@ -0,0 +1,55 @@ +"""Spend-tracking suite's `client` fixture and driver-model registration. + +The shared lifecycle (resources/scoped_key), proxy liveness skip, and e2e marker +live in the parent tests/e2e/conftest.py. SpendClient exposes the shared Gateway +(GatewayProvider), so the `resources` fixture cleans up keys and customers this +suite creates. + +The suite drives real calls through three deployments. On the stage gateway they +are baked into the proxy config; on a local dev proxy they usually are not, so +`driver_models` registers whichever are missing via /model/new and deletes only +the ones it created, never a config-baked deployment. Each registration carries +the provider key from the test runner's env when set (so a local proxy whose +container env lacks the key still works); otherwise it falls back to an +os.environ reference resolved from the proxy's own env, the stage convention. +""" + +import os +from typing import Iterator + +import pytest + +from models import LiteLLMParamsBody +from spend_e2e_client import SpendClient, build_client + + +def _driver_params(provider_model: str, env_var: str) -> LiteLLMParamsBody: + return LiteLLMParamsBody( + model=provider_model, + api_key=os.environ.get(env_var) or f"os.environ/{env_var}", + ) + + +DRIVER_MODELS: tuple[tuple[str, str, str], ...] = ( + ("gemini-2.5-flash", "gemini/gemini-2.5-flash", "GEMINI_API_KEY"), + ("claude-haiku-4-5", "anthropic/claude-haiku-4-5", "ANTHROPIC_API_KEY"), + ("openai-text-embedding-3-small", "openai/text-embedding-3-small", "OPENAI_API_KEY"), +) + + +@pytest.fixture(scope="session") +def client() -> SpendClient: + return build_client() + + +@pytest.fixture(scope="session", autouse=True) +def driver_models(client: SpendClient) -> Iterator[None]: + existing = frozenset(entry.model_name for entry in client.gateway.model_info()) + created = tuple( + client.gateway.create_model(name, _driver_params(provider_model, env_var)) + for name, provider_model, env_var in DRIVER_MODELS + if name not in existing + ) + yield + for model_id in created: + client.gateway.delete_model(model_id) diff --git a/tests/e2e/spend_tracking/spend_e2e_client.py b/tests/e2e/quota_management/spend_tracking/spend_e2e_client.py similarity index 67% rename from tests/e2e/spend_tracking/spend_e2e_client.py rename to tests/e2e/quota_management/spend_tracking/spend_e2e_client.py index d749d69f1a4..c4991199187 100644 --- a/tests/e2e/spend_tracking/spend_e2e_client.py +++ b/tests/e2e/quota_management/spend_tracking/spend_e2e_client.py @@ -2,8 +2,8 @@ Generic proxy operations (keys, customers, chat/embed, route probing, SpendLogs polling) come from the shared Gateway, DI'd in (composition, not inheritance). -This client adds only the spend surface: /spend/calculate, key-spend -polling, and the route probes the breadth test uses. +This client adds only the spend surface: /spend/calculate, /spend/tags, +key-spend polling, and the route probes the breadth test uses. Re-exports unwrap / is_ok / unique_marker / SpendLogRow so the tests import their helpers from one place. @@ -15,6 +15,7 @@ import os import time from collections.abc import Callable from dataclasses import dataclass +from datetime import datetime, timedelta, timezone from e2e_config import unique_marker from e2e_http import ( @@ -22,6 +23,7 @@ from e2e_http import ( ProbeResult, Result, StreamingResponse, + Success, is_ok, unwrap, ) @@ -38,6 +40,10 @@ from models import ( SpendCalculateBody, SpendCalculateResponse, SpendLogRow, + SpendLogsPage, + SpendLogsPageParams, + SpendTagsResponse, + TagSpend, ) __all__ = [ @@ -139,6 +145,34 @@ class SpendClient: ) ).cost + def spend_by_tags(self) -> list[TagSpend]: + result = self.gateway.transport.get( + "/spend/tags", + headers=self.gateway.transport.master, + params=NoBody(), + response_type=SpendTagsResponse, + ) + match result: + case Success(data=data): + return data.root + case _: + return [] + + def poll_tag_spend(self, tag: str, *, minimum: float = 0.0) -> TagSpend | None: + """Poll /spend/tags until the tag's aggregate reaches `minimum`; last seen.""" + deadline = time.monotonic() + self.gateway.poll_timeout + entry: TagSpend | None = None + while time.monotonic() < deadline: + matches = [ + t for t in self.spend_by_tags() if t.individual_request_tag == tag + ] + if matches: + entry = matches[0] + if (entry.total_spend or 0.0) >= minimum: + return entry + time.sleep(self.gateway.poll_interval) + return entry + def poll_key_spend(self, key: str, *, minimum: float = 0.0) -> float: deadline = time.monotonic() + self.gateway.poll_timeout spend = 0.0 @@ -149,6 +183,28 @@ class SpendClient: time.sleep(self.gateway.poll_interval) return spend + def spend_logs_page( + self, *, api_key: str | None, page: int, page_size: int + ) -> SpendLogsPage: + """One page of /spend/logs/v2 over a window wide enough to contain every + row this test run wrote (the endpoint requires explicit dates).""" + now = datetime.now(timezone.utc) + fmt = "%Y-%m-%d %H:%M:%S" + return unwrap( + self.gateway.transport.get( + "/spend/logs/v2", + headers=self.gateway.transport.master, + params=SpendLogsPageParams( + start_date=(now - timedelta(days=1)).strftime(fmt), + end_date=(now + timedelta(days=1)).strftime(fmt), + page=page, + page_size=page_size, + api_key=api_key, + ), + response_type=SpendLogsPage, + ) + ) + def probe(self, path: str, *, params: DateRangeParams) -> ProbeResult: return self.gateway.transport.probe(path, params=params) diff --git a/tests/e2e/spend_tracking/test_spend_routes.py b/tests/e2e/quota_management/spend_tracking/test_spend_routes.py similarity index 79% rename from tests/e2e/spend_tracking/test_spend_routes.py rename to tests/e2e/quota_management/spend_tracking/test_spend_routes.py index e3c96a4d578..9b4eaefae34 100644 --- a/tests/e2e/spend_tracking/test_spend_routes.py +++ b/tests/e2e/quota_management/spend_tracking/test_spend_routes.py @@ -27,13 +27,21 @@ pytestmark = pytest.mark.e2e # Verified present and responsive on a live proxy. One per row of the spend # surface: key / user / team / org / customer aggregation, model-cost, tags, -# activity. +# activity. Most are include_in_schema=False, so keep this list exhaustive by +# hand; the schema test below only auto-catches the visible minority. Excluded +# by design: path-param routes (/spend/logs/ui/{request_id}), POST readers +# (/spend/calculate has its own test, /global/spend/end_users), mutating +# POSTs (/global/spend/reset, /global/spend/refresh), and /provider/budgets, +# which 500s whenever router_settings.provider_budget_config is absent, so it +# is only probeable on a proxy configured with provider budget routing. SPEND_ROUTES = ( "/spend/keys", "/spend/users", "/spend/tags", "/spend/logs", "/spend/logs/ui", + "/spend/logs/v2", + "/spend/logs/session/ui", "/global/spend", "/global/spend/keys", "/global/spend/teams", @@ -43,9 +51,18 @@ SPEND_ROUTES = ( "/global/spend/tags", "/global/spend/logs", "/global/spend/all_tag_names", + "/global/all_end_users", "/global/activity", "/global/activity/model", "/global/activity/exceptions", + "/global/activity/exceptions/deployment", + "/user/daily/activity", + "/user/daily/activity/aggregated", + "/team/daily/activity", + "/organization/daily/activity", + "/customer/daily/activity", + "/end_user/daily/activity", + "/tag/daily/activity", "/key/list", "/user/list", "/team/list", diff --git a/tests/e2e/spend_tracking/test_spend_tracking_e2e.py b/tests/e2e/quota_management/spend_tracking/test_spend_tracking_e2e.py similarity index 61% rename from tests/e2e/spend_tracking/test_spend_tracking_e2e.py rename to tests/e2e/quota_management/spend_tracking/test_spend_tracking_e2e.py index 8c9e913b10f..2f0ffae44e3 100644 --- a/tests/e2e/spend_tracking/test_spend_tracking_e2e.py +++ b/tests/e2e/quota_management/spend_tracking/test_spend_tracking_e2e.py @@ -17,13 +17,14 @@ fails the test; a pricing or token-count drift does not. import time from collections.abc import Callable +from concurrent.futures import ThreadPoolExecutor import pytest -from e2e_http import Success +from e2e_http import Result, Success from lifecycle import ResourceManager -from models import SpendLogs, SpendLogsParams -from spend_e2e_client import SpendClient, SpendLogRow, unique_marker, unwrap +from models import ChatResponse, SpendLogs, SpendLogsParams +from spend_e2e_client import SpendClient, SpendLogRow, is_ok, unique_marker, unwrap pytestmark = pytest.mark.e2e @@ -58,6 +59,7 @@ def _require_row( return matches[0] +@pytest.mark.covers("quota_management.spend_tracking.chat_completions.logs_cost") def test_chat_completion_writes_nonzero_spend_row( client: SpendClient, scoped_key: str ) -> None: @@ -92,6 +94,7 @@ def test_chat_completion_writes_nonzero_spend_row( ), f"row request_id != client response.id ({chat.id})" +@pytest.mark.covers("quota_management.spend_tracking.stream.logs_cost") def test_streaming_chat_completion_tracks_spend( client: SpendClient, scoped_key: str ) -> None: @@ -119,6 +122,7 @@ def test_streaming_chat_completion_tracks_spend( assert (row.total_tokens or 0) == prompt + completion +@pytest.mark.covers("quota_management.spend_tracking.embeddings.logs_cost") def test_embedding_writes_nonzero_spend_row( client: SpendClient, scoped_key: str ) -> None: @@ -141,6 +145,7 @@ def test_embedding_writes_nonzero_spend_row( assert "text-embedding-3-small" in (row.model or "") +@pytest.mark.covers("quota_management.spend_tracking.cache_hit.zero_cost") def test_cache_hit_is_zero_cost_and_suffixed( client: SpendClient, scoped_key: str ) -> None: @@ -176,6 +181,7 @@ def test_cache_hit_is_zero_cost_and_suffixed( ), f"the non-cached call should still be charged: {_summarize(rows)}" +@pytest.mark.covers("quota_management.spend_tracking.key_rollup.matches_sum_of_logs") def test_key_spend_equals_sum_of_logs(client: SpendClient, scoped_key: str) -> None: for _ in range(2): _ = unwrap( @@ -202,6 +208,107 @@ def test_key_spend_equals_sum_of_logs(client: SpendClient, scoped_key: str) -> N ), f"key aggregate {key_spend} != sum of logs {logs_total}; rows: {_summarize(rows)}" +@pytest.mark.covers("quota_management.spend_tracking.concurrent_burst.loses_no_spend") +def test_burst_of_concurrent_calls_loses_no_spend( + client: SpendClient, scoped_key: str +) -> None: + """Six concurrent calls on one key: every call lands its own spend row under a + distinct request_id and the key aggregate equals the sum of the rows. + Sequential accuracy is covered by test_key_spend_equals_sum_of_logs; this pins + the concurrent increment path (parallel writers racing on one key's counter), + where a lost update can never be reproduced by sequential calls.""" + burst = 6 + + def call(idx: int) -> Result[ChatResponse]: + return client.chat( + scoped_key, + "gemini-2.5-flash", + f"burst call {idx} {unique_marker()}", + max_tokens=16, + ) + + with ThreadPoolExecutor(max_workers=burst) as pool: + results = tuple(pool.map(call, range(burst))) + failed = [r for r in results if not is_ok(r)] + assert not failed, f"{len(failed)}/{burst} burst calls failed; first: {failed[0]}" + + rows = client.poll_logs_for_key( + scoped_key, + min_rows=burst, + predicate=lambda rs: len([r for r in rs if (r.spend or 0) > 0]) >= burst, + ) + costed = [r for r in rows if (r.spend or 0) > 0] + assert len(costed) >= burst, ( + f"only {len(costed)}/{burst} burst calls produced a costed row - " + f"rows lost under concurrency: {_summarize(rows)}" + ) + request_ids = [r.request_id for r in costed] + assert len(set(request_ids)) == len(request_ids), ( + f"concurrent rows collapsed onto shared request_ids: {_summarize(rows)}" + ) + + logs_total = sum((r.spend or 0) for r in rows) + key_spend = client.poll_key_spend(scoped_key, minimum=logs_total * 0.999) + assert _approx_equal(key_spend, logs_total), ( + f"key aggregate {key_spend} != sum of {len(rows)} rows {logs_total} - " + f"spend increments lost under concurrency: {_summarize(rows)}" + ) + + +@pytest.mark.covers("quota_management.spend_tracking.pagination.keeps_total") +def test_spend_logs_v2_pagination_caps_pages_and_keeps_total( + client: SpendClient, scoped_key: str +) -> None: + """/spend/logs/v2 pagination contract for the key filter: page_size caps the + rows returned, total counts every row for the filter (so with page_size=1, + total_pages == total), a page past the end returns no rows while reporting + the same total (an out-of-range page must not reset the count the UI + paginates by), and a filter matching nothing reports zero without erroring. + + Unlike /spend/logs, the v2 filter matches the hashed token exactly as stored + on the row (the form the UI passes), not the raw sk- key, so the filter value + is read off the rows the poll returned.""" + for _ in range(2): + _ = unwrap( + client.chat( + scoped_key, + "gemini-2.5-flash", + f"page fodder {unique_marker()}", + max_tokens=16, + ) + ) + rows = client.poll_logs_for_key( + scoped_key, min_rows=2, predicate=lambda rs: sum((r.spend or 0) for r in rs) > 0 + ) + hashed_key = rows[0].api_key + assert hashed_key, f"polled rows carry no api_key: {_summarize(rows)}" + + first = client.spend_logs_page(api_key=hashed_key, page=1, page_size=1) + assert first.total >= 2, f"expected >=2 rows for the key, got total={first.total}" + assert len(first.data) == 1, f"page_size=1 returned {len(first.data)} rows" + assert first.total_pages == first.total, ( + f"page_size=1 must give one page per row: " + f"total={first.total} total_pages={first.total_pages}" + ) + + beyond = client.spend_logs_page( + api_key=hashed_key, page=first.total_pages + 7, page_size=1 + ) + assert beyond.data == [], f"out-of-range page returned rows: {beyond.data}" + assert beyond.total == first.total, ( + f"out-of-range page changed the total: {beyond.total} != {first.total}" + ) + + nomatch = client.spend_logs_page( + api_key=f"sk-no-such-key-{unique_marker()}", page=1, page_size=1 + ) + assert nomatch.total == 0 and nomatch.data == [], ( + f"filter matching nothing must report zero: " + f"total={nomatch.total} rows={len(nomatch.data)}" + ) + + +@pytest.mark.covers("quota_management.spend_tracking.tags.attributes_spend") def test_request_tags_round_trip(client: SpendClient, scoped_key: str) -> None: tag = f"e2e-spend-{unique_marker()}" _ = unwrap( @@ -218,6 +325,45 @@ def test_request_tags_round_trip(client: SpendClient, scoped_key: str) -> None: ) +@pytest.mark.covers("quota_management.spend_tracking.tags.attributes_spend") +def test_tag_spend_matches_sum_of_tagged_logs( + client: SpendClient, scoped_key: str +) -> None: + # Unique tag so /spend/tags can't be polluted by other rows; unique content + # per call so both are fresh misses (paid), not cache hits. + tag = f"e2e-tagspend-{unique_marker()}" + for _ in range(2): + _ = unwrap( + client.chat( + scoped_key, + "gemini-2.5-flash", + f"hi {unique_marker()}", + tags=[tag], + max_tokens=16, + ) + ) + + rows = client.poll_logs_for_key( + scoped_key, + min_rows=2, + predicate=lambda rs: sum((r.spend or 0) for r in rs) > 0, + ) + tagged = [r for r in rows if tag in (r.request_tags or [])] + assert len(tagged) >= 2, f"expected 2 tagged rows, saw {_summarize(rows)}" + logs_total = sum((r.spend or 0) for r in tagged) + assert logs_total > 0 + + entry = client.poll_tag_spend(tag, minimum=logs_total * 0.999) + assert entry is not None, f"tag {tag!r} never appeared in /spend/tags" + assert _approx_equal(entry.total_spend or 0, logs_total), ( + f"/spend/tags total_spend {entry} != sum of tagged rows {logs_total}" + ) + assert (entry.log_count or 0) == len(tagged), ( + f"/spend/tags log_count {entry.log_count} != tagged rows {len(tagged)}" + ) + + +@pytest.mark.covers("quota_management.spend_tracking.end_user.attributes_spend") def test_end_user_spend_attributed_on_row( client: SpendClient, scoped_key: str, resources: ResourceManager ) -> None: @@ -235,6 +381,7 @@ def test_end_user_spend_attributed_on_row( assert (row.spend or 0) > 0, f"end-user row should cost > 0: {_summarize(rows)}" +@pytest.mark.covers("quota_management.spend_tracking.per_model.writes_own_rows") def test_each_model_on_a_shared_key_gets_its_own_row( client: SpendClient, scoped_key: str ) -> None: @@ -283,6 +430,27 @@ def test_each_model_on_a_shared_key_gets_its_own_row( ), f"claude row request_id {claude_row.request_id} != response id {claude.id}" +@pytest.mark.covers("quota_management.spend_tracking.failure.writes_failure_row") +def test_failure_call_writes_failure_status_row( + client: SpendClient, scoped_key: str +) -> None: + result = client.chat(scoped_key, "gemini-2.5-flash", "", max_tokens=1) + if is_ok(result): + pytest.skip("call unexpectedly succeeded; could not induce a failure row") + + rows = client.poll_logs_for_key( + scoped_key, predicate=lambda rs: any(r.status == "failure" for r in rs) + ) + failure_rows = [r for r in rows if r.status == "failure"] + if not failure_rows: + pytest.skip( + "no failure-status row was logged for the rejected call; " + "failure logging is environment-specific" + ) + assert (failure_rows[0].spend or 0) == 0.0, "failed call must not be charged" + + +@pytest.mark.covers("quota_management.spend_tracking.spend_calculate.returns_cost") def test_spend_calculate_returns_nonzero_cost(client: SpendClient) -> None: cost = client.calculate_spend( "gemini-2.5-flash", "estimate the cost of this request" diff --git a/tests/e2e/realtime/REALTIME_COVERAGE_MATRIX.md b/tests/e2e/realtime/REALTIME_COVERAGE_MATRIX.md deleted file mode 100644 index 8624475d0de..00000000000 --- a/tests/e2e/realtime/REALTIME_COVERAGE_MATRIX.md +++ /dev/null @@ -1,55 +0,0 @@ -# Realtime e2e coverage - -Live tests for the proxy realtime websocket endpoint (`/v1/realtime`). One -GA-speaking websocket client drives every provider; the proxy normalizes each -provider's stream into the OpenAI GA event schema, so the same assertions hold -across providers and only the model alias changes. - -## What is asserted - -For each configured provider, `test_text_conversation` checks the session -lifecycle (`session.created`, then `session.update` echoed by `session.updated`), -the canonical response sequence (`response.created`, `response.output_item.added`, -through `response.done`), that the streamed deltas reconstruct a non-empty -transcript, and that `response.done` carries normalized usage. - -`test_tool_call_round_trip` checks the full tool path: the model emits a -normalized `response.function_call_arguments.done` with valid JSON arguments and -a matching `function_call` output item, the test sends a `function_call_output` -back, and the follow-up response incorporates the result (the temperature 72 -appears). - -`test_realtime_pipecat_e2e` is a realism layer that drives the same providers -through pipecat's GA `OpenAIRealtimeLLMService` (base_url pointed at the proxy) -rather than speaking the protocol by hand. Its assertions are coarse (the tool -callback fired, assistant text was produced); the raw-websocket suite is the -source of truth. It skips unless `pipecat-ai` is installed -(`uv pip install "pipecat-ai[openai]"`). - -## Provider status - -| provider | model alias | status | -|----------|-------------|--------| -| openai | `openai-realtime` | covered (in gateway config) | -| gemini | `gemini-realtime` | covered (in gateway config; needs Gemini Live API access) | -| azure | `azure-realtime` | gap: add to gateway config + AZURE creds | -| vertex_ai | `vertex-realtime` | gap: add to gateway config + Vertex creds | -| bedrock | `bedrock-realtime` | gap: add to gateway config + AWS creds | -| xai | `xai-realtime` | gap: add to gateway config + XAI_API_KEY | - -A provider whose alias is not present in the proxy's `/model/info` skips (skip on -environment). To enable one, add a `model_info.mode: realtime` entry under that -alias to `tests/e2e/gateway/litellm-config.yml` and give the proxy the -provider's credentials; the test then runs with no code change. - -## Running - -Start a proxy with the gateway config and the provider keys set in its -environment, then - -``` -uv run pytest tests/e2e/realtime/ -v -``` - -Tests skip when no proxy answers `GET /health/liveliness` at `LITELLM_PROXY_URL` -(default `http://localhost:4000`). diff --git a/tests/e2e/realtime/conftest.py b/tests/e2e/realtime/conftest.py deleted file mode 100644 index 4a5c4837a1a..00000000000 --- a/tests/e2e/realtime/conftest.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Realtime suite's `client` fixture. - -The shared lifecycle (resources/scoped_key), proxy liveness skip, and e2e marker -live in the parent tests/e2e/conftest.py. RealtimeClient holds the shared -Gateway, so the `resources` fixture cleans up keys this suite creates. -""" - -import pytest - -from realtime_client import RealtimeClient, build_client - - -@pytest.fixture(scope="session") -def client() -> RealtimeClient: - return build_client() - - -@pytest.fixture(scope="session") -def configured_models(client: RealtimeClient) -> frozenset[str]: - return client.configured_models() diff --git a/tests/e2e/spend_tracking/conftest.py b/tests/e2e/spend_tracking/conftest.py deleted file mode 100644 index 1d01ab3d17a..00000000000 --- a/tests/e2e/spend_tracking/conftest.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Spend-tracking suite's `client` fixture. - -The shared lifecycle (resources/scoped_key), proxy liveness skip, and e2e marker -live in the parent tests/e2e/conftest.py. SpendClient exposes the shared Gateway -(GatewayProvider), so the `resources` fixture cleans up keys and customers this -suite creates. -""" - -import pytest - -from spend_e2e_client import SpendClient, build_client - - -@pytest.fixture(scope="session") -def client() -> SpendClient: - return build_client() diff --git a/tests/e2e/test_e2e_gateway.py b/tests/e2e/test_e2e_gateway.py new file mode 100644 index 00000000000..9a9aa2fd2cc --- /dev/null +++ b/tests/e2e/test_e2e_gateway.py @@ -0,0 +1,204 @@ +"""Unit coverage for the Gateway model-management surface (create_model / +delete_model). + +The batches conftest and several llm_translation tests register deployments at +runtime through gateway.create_model; when that method went missing, every batch +test errored at fixture setup (AttributeError) before a single request reached +the proxy. This pins the surface with a typed fake Transport so a rename or +signature drift fails here instead of in a live stage run. +""" + +from dataclasses import dataclass, field + +import pytest +from pydantic import BaseModel + +from batches.batch_client import BatchClient +from e2e_gateway import Gateway +from e2e_http import ( + AuthHeaders, + FileUploadForm, + ProbeResult, + Result, + StreamingResponse, + Success, + UnknownApiError, +) +from models import ( + LiteLLMParamsBody, + ModelDeleteBody, + ModelNewBody, + ModelNewResponse, + ModelsListResponse, +) + + +@dataclass +class _RecordingTransport: + """Typed fake fulfilling the Transport protocol; records every post and + answers with a canned success so the test asserts on what was sent. + + `get("/v1/models")` reports a created model as servable only after + `servable_after_gets` polls, so a test can drive the data-plane wait in + create_model.""" + + posts: list[tuple[str, BaseModel]] = field(default_factory=list) + servable_after_gets: int = 0 + models_error: UnknownApiError | None = None + model_gets: int = 0 + _created: list[str] = field(default_factory=list) + + def post[R: BaseModel]( + self, path: str, *, headers: BaseModel, json: BaseModel, response_type: type[R] + ) -> Result[R]: + self.posts.append((path, json)) + if path == "/model/new" and isinstance(json, ModelNewBody): + self._created.append(json.model_name) + payload = ( + {"model_id": "registered-id"} if response_type is ModelNewResponse else {} + ) + return Success(data=response_type.model_validate(payload)) + + def stream( + self, path: str, *, headers: BaseModel, json: BaseModel + ) -> StreamingResponse: + raise AssertionError("stream is not part of model management") + + def send( + self, + path: str, + *, + headers: BaseModel, + json: BaseModel, + params: BaseModel | None = None, + stream: bool = False, + ) -> StreamingResponse: + raise AssertionError("send is not part of model management") + + def get[R: BaseModel]( + self, + path: str, + *, + headers: BaseModel, + params: BaseModel, + response_type: type[R], + ) -> Result[R]: + if path == "/v1/models" and response_type is ModelsListResponse: + self.model_gets += 1 + if self.models_error is not None: + return self.models_error + visible = self._created if self.model_gets > self.servable_after_gets else [] + return Success( + data=response_type.model_validate({"data": [{"id": name} for name in visible]}) + ) + raise AssertionError(f"unexpected get: {path}") + + def delete[R: BaseModel]( + self, path: str, *, headers: BaseModel, json: BaseModel, response_type: type[R] + ) -> Result[R]: + raise AssertionError("delete is not part of model management") + + def probe(self, path: str, *, params: BaseModel) -> ProbeResult: + raise AssertionError("probe is not part of model management") + + def upload[R: BaseModel]( + self, + path: str, + *, + headers: BaseModel, + form: FileUploadForm, + filename: str, + content: bytes, + params: BaseModel | None = None, + response_type: type[R], + ) -> Result[R]: + raise AssertionError("upload is not part of model management") + + def download(self, path: str, *, headers: BaseModel) -> StreamingResponse: + raise AssertionError("download is not part of model management") + + def bearer(self, key: str) -> AuthHeaders: + return AuthHeaders(authorization=f"Bearer {key}") + + @property + def master(self) -> AuthHeaders: + return self.bearer("sk-test-master") + + +def test_gateway_create_model_registers_deployment_and_returns_model_id() -> None: + transport = _RecordingTransport() + gateway = Gateway(transport=transport, poll_interval=0.0) + + model_id = gateway.create_model( + "e2e-test-model", LiteLLMParamsBody(model="openai/gpt-4o-mini") + ) + + assert model_id == "registered-id" + path, body = transport.posts[0] + assert path == "/model/new" + assert isinstance(body, ModelNewBody) + assert body.model_name == "e2e-test-model" + # No pinned model_id: the proxy assigns a unique one, so a fixed-name model + # re-registered after a failed teardown can't collide on the id constraint. + assert body.model_info.id is None + assert body.model_info.mode is None + # It confirmed data-plane visibility before returning. + assert transport.model_gets >= 1 + + +def test_gateway_create_model_waits_until_servable_on_the_data_plane() -> None: + # The model shows up on /v1/models only on the third poll (simulating the + # gateway's delayed DB reload in a split deployment); create_model must keep + # polling instead of returning after /model/new. + transport = _RecordingTransport(servable_after_gets=2) + gateway = Gateway(transport=transport, poll_interval=0.0) + + gateway.create_model("e2e-late-model", LiteLLMParamsBody(model="openai/gpt-4o-mini")) + + assert transport.model_gets == 3 + + +def test_gateway_create_model_fails_loudly_when_never_servable() -> None: + transport = _RecordingTransport(servable_after_gets=10**9) + gateway = Gateway(transport=transport, poll_timeout=0.05, poll_interval=0.0) + + with pytest.raises(AssertionError, match="never became servable"): + gateway.create_model("e2e-ghost-model", LiteLLMParamsBody(model="openai/gpt-4o-mini")) + + +def test_gateway_create_model_surfaces_the_last_data_plane_error() -> None: + transport = _RecordingTransport( + models_error=UnknownApiError(status_code=503, body="data plane down") + ) + gateway = Gateway(transport=transport, poll_timeout=0.05, poll_interval=0.0) + + with pytest.raises(AssertionError, match="data plane down") as excinfo: + gateway.create_model("e2e-flaky-model", LiteLLMParamsBody(model="openai/gpt-4o-mini")) + assert "503" in str(excinfo.value) + + +def test_batch_client_create_model_registers_a_batch_mode_deployment() -> None: + transport = _RecordingTransport() + client = BatchClient(gateway=Gateway(transport=transport, poll_interval=0.0)) + + model_id = client.create_model( + "e2e-batch-model", LiteLLMParamsBody(model="openai/gpt-4o-mini") + ) + + assert model_id == "registered-id" + path, body = transport.posts[0] + assert path == "/model/new" + assert isinstance(body, ModelNewBody) + assert body.model_info.mode == "batch" + + +def test_gateway_delete_model_posts_the_model_id() -> None: + transport = _RecordingTransport() + gateway = Gateway(transport=transport) + + gateway.delete_model("registered-id") + + path, body = transport.posts[0] + assert path == "/model/delete" + assert isinstance(body, ModelDeleteBody) + assert body.id == "registered-id" diff --git a/tests/e2e/test_transport.py b/tests/e2e/test_transport.py new file mode 100644 index 00000000000..c7ce61b90c1 --- /dev/null +++ b/tests/e2e/test_transport.py @@ -0,0 +1,52 @@ +"""Unit coverage for SplitTransport path routing (is_control_plane_path). + +Model-management calls (/model/new, /model/delete, /model/info) must go to the +control plane: the data-plane gateway does not serve management routes, so a +misrouted /model/new 404s and takes down every suite that registers deployments +at runtime (llm_translation, batches, access_control). /models must stay on the +data plane; it is the OpenAI-compatible list-models route, not a management +route. +""" + +import pytest + +from transport import is_control_plane_path + + +@pytest.mark.parametrize( + "path", + [ + "/model/new", + "/model/delete", + "/model/update", + "/model/info", + "/key/generate", + "/budget/new", + "/spend/logs", + "/end_user/daily/activity", + "/user/daily/activity", + "/team/daily/activity", + "/tag/daily/activity", + ], +) +def test_management_routes_go_to_the_control_plane(path: str) -> None: + assert is_control_plane_path(path), ( + f"{path} is a management route; sending it to the data plane 404s" + ) + + +@pytest.mark.parametrize( + "path", + [ + "/models", + "/v1/models", + "/chat/completions", + "/v1/messages", + "/embeddings", + "/anthropic/v1/messages", + ], +) +def test_llm_routes_stay_on_the_data_plane(path: str) -> None: + assert not is_control_plane_path(path), ( + f"{path} is an LLM route; it must go to the data plane" + ) diff --git a/tests/e2e/transport.py b/tests/e2e/transport.py index 37412fc0cf5..10e090f07a9 100644 --- a/tests/e2e/transport.py +++ b/tests/e2e/transport.py @@ -13,7 +13,14 @@ from typing import Protocol from pydantic import BaseModel import e2e_http -from e2e_http import URL, AuthHeaders, ProbeResult, Result, StreamingResponse +from e2e_http import ( + URL, + AuthHeaders, + FileUploadForm, + ProbeResult, + Result, + StreamingResponse, +) class Transport(Protocol): @@ -50,6 +57,20 @@ class Transport(Protocol): def probe(self, path: str, *, params: BaseModel) -> ProbeResult: ... + def upload[R: BaseModel]( + self, + path: str, + *, + headers: BaseModel, + form: FileUploadForm, + filename: str, + content: bytes, + params: BaseModel | None = None, + response_type: type[R], + ) -> Result[R]: ... + + def download(self, path: str, *, headers: BaseModel) -> StreamingResponse: ... + def bearer(self, key: str) -> AuthHeaders: ... @property @@ -143,6 +164,33 @@ class HttpTransport: timeout=self.request_timeout, ) + def upload[R: BaseModel]( + self, + path: str, + *, + headers: BaseModel, + form: FileUploadForm, + filename: str, + content: bytes, + params: BaseModel | None = None, + response_type: type[R], + ) -> Result[R]: + return e2e_http.upload( + self._url(path), + headers=headers, + form=form, + filename=filename, + content=content, + params=params, + response_type=response_type, + timeout=self.request_timeout, + ) + + def download(self, path: str, *, headers: BaseModel) -> StreamingResponse: + return e2e_http.download( + self._url(path), headers=headers, timeout=self.request_timeout + ) + # Top-level management/admin route groups. In a split deployment these are served # by the control plane (a different service from the LLM data plane). LLM routes @@ -154,9 +202,10 @@ CONTROL_PLANE_PREFIXES: tuple[str, ...] = ( "/team", "/organization", "/customer", + "/end_user", "/tag", "/budget", - "/model/info", + "/model/", "/spend", "/global", "/openapi.json", @@ -242,3 +291,27 @@ class SplitTransport: def probe(self, path: str, *, params: BaseModel) -> ProbeResult: return self._route(path).probe(path, params=params) + + def upload[R: BaseModel]( + self, + path: str, + *, + headers: BaseModel, + form: FileUploadForm, + filename: str, + content: bytes, + params: BaseModel | None = None, + response_type: type[R], + ) -> Result[R]: + return self._route(path).upload( + path, + headers=headers, + form=form, + filename=filename, + content=content, + params=params, + response_type=response_type, + ) + + def download(self, path: str, *, headers: BaseModel) -> StreamingResponse: + return self._route(path).download(path, headers=headers) diff --git a/tests/enterprise/litellm_enterprise/enterprise_callbacks/test_prometheus_logging_callbacks.py b/tests/enterprise/litellm_enterprise/enterprise_callbacks/test_prometheus_logging_callbacks.py index be199c19149..1b1ce3e0f2d 100644 --- a/tests/enterprise/litellm_enterprise/enterprise_callbacks/test_prometheus_logging_callbacks.py +++ b/tests/enterprise/litellm_enterprise/enterprise_callbacks/test_prometheus_logging_callbacks.py @@ -228,6 +228,7 @@ def test_increment_token_metrics(prometheus_logger): requested_model=None, model="gpt-5-mini", model_id="model-123", + api_provider="openai", ) prometheus_logger.litellm_tokens_metric.labels().inc.assert_called_once_with(100) @@ -244,6 +245,7 @@ def test_increment_token_metrics(prometheus_logger): requested_model=None, model="gpt-5-mini", model_id="model-123", + api_provider="openai", ) prometheus_logger.litellm_input_tokens_metric.labels().inc.assert_called_once_with( 50 @@ -262,6 +264,7 @@ def test_increment_token_metrics(prometheus_logger): requested_model=None, model="gpt-5-mini", model_id="model-123", + api_provider="openai", ) prometheus_logger.litellm_output_tokens_metric.labels().inc.assert_called_once_with( 50 @@ -424,6 +427,7 @@ def test_set_latency_metrics(prometheus_logger): requested_model="openai-gpt", model="gpt-5-mini", model_id="model-123", + api_provider="openai", ) prometheus_logger.litellm_llm_api_time_to_first_token_metric.labels().observe.assert_called_once_with( 0.5 @@ -442,6 +446,7 @@ def test_set_latency_metrics(prometheus_logger): requested_model="openai-gpt", model="gpt-5-mini", model_id="model-123", + api_provider="openai", ) prometheus_logger.litellm_llm_api_latency_metric.labels().observe.assert_called_once_with( 1.5 @@ -460,6 +465,7 @@ def test_set_latency_metrics(prometheus_logger): requested_model="openai-gpt", model="gpt-5-mini", model_id="model-123", + api_provider="openai", ) prometheus_logger.litellm_request_total_latency_metric.labels().observe.assert_called_once_with( 2.0 @@ -844,6 +850,7 @@ async def test_async_post_call_failure_hook(prometheus_logger): model_id=None, client_ip=None, user_agent=None, + api_provider="openai", ) finally: litellm.prometheus_emit_rate_limit_labels = original_emit @@ -867,6 +874,7 @@ async def test_async_post_call_failure_hook(prometheus_logger): model_id=None, client_ip=None, user_agent=None, + api_provider="openai", ) prometheus_logger.litellm_proxy_total_requests_metric.labels().inc.assert_called_once() diff --git a/tests/enterprise/litellm_enterprise/proxy/guardrails/test_bedrock_apply_guardrail.py b/tests/enterprise/litellm_enterprise/proxy/guardrails/test_bedrock_apply_guardrail.py index 87c5e3bf2a9..f257b47404e 100644 --- a/tests/enterprise/litellm_enterprise/proxy/guardrails/test_bedrock_apply_guardrail.py +++ b/tests/enterprise/litellm_enterprise/proxy/guardrails/test_bedrock_apply_guardrail.py @@ -329,12 +329,13 @@ def test_bedrock_guardrail_filters_latest_user_message_when_enabled(): @pytest.mark.asyncio async def test_bedrock_apply_guardrail_blocked_with_disable_exception_on_block(): """ - Regression test for issue #20045: when disable_exception_on_block=True, - make_bedrock_api_request raises GuardrailInterventionNormalStringError. - apply_guardrail must let it propagate as-is so the proxy can handle it - properly instead of wrapping it in a generic Exception. + Regression test for LIT-4186: when disable_exception_on_block=True, a + Bedrock block raises ModifyResponseException. apply_guardrail must let it + propagate as-is so the endpoint handler (proxy_server.py) can turn it into + a 200 response with the block message as content, instead of the exception + surfacing as a bare 500. """ - from litellm.exceptions import GuardrailInterventionNormalStringError + from litellm.exceptions import ModifyResponseException guardrail = BedrockGuardrail( guardrail_name="test-bedrock-guard", @@ -346,18 +347,21 @@ async def test_bedrock_apply_guardrail_blocked_with_disable_exception_on_block() with patch.object( guardrail, "make_bedrock_api_request", new_callable=AsyncMock ) as mock_api: - mock_api.side_effect = GuardrailInterventionNormalStringError( - message="Sorry, your question in its current format is unable to be answered." + mock_api.side_effect = ModifyResponseException( + message="Sorry, your question in its current format is unable to be answered.", + model="bedrock-guardrail", + request_data={}, + guardrail_name="test-bedrock-guard", ) - with pytest.raises(GuardrailInterventionNormalStringError) as exc_info: + with pytest.raises(ModifyResponseException) as exc_info: await guardrail.apply_guardrail( inputs={"texts": ["harmful prompt content"]}, request_data={}, input_type="request", ) - assert "unable to be answered" in str(exc_info.value.message) + assert "unable to be answered" in exc_info.value.message @pytest.mark.asyncio diff --git a/tests/enterprise/litellm_enterprise/proxy/hooks/test_managed_files.py b/tests/enterprise/litellm_enterprise/proxy/hooks/test_managed_files.py index 7b82d1eabd9..68dc3269f34 100644 --- a/tests/enterprise/litellm_enterprise/proxy/hooks/test_managed_files.py +++ b/tests/enterprise/litellm_enterprise/proxy/hooks/test_managed_files.py @@ -1404,7 +1404,7 @@ async def test_store_unified_file_id_with_none_file_object(): from litellm.proxy._types import UserAPIKeyAuth prisma_client = AsyncMock() - prisma_client.db.litellm_managedfiletable.create = AsyncMock( + prisma_client.db.litellm_managedfiletable.upsert = AsyncMock( return_value=MagicMock() ) internal_usage_cache = MagicMock() @@ -1424,11 +1424,73 @@ async def test_store_unified_file_id_with_none_file_object(): user_api_key_dict=UserAPIKeyAuth(user_id="test-user"), ) - # Verify DB create was called with expected data (without file_object) - prisma_client.db.litellm_managedfiletable.create.assert_called_once() - call_args = prisma_client.db.litellm_managedfiletable.create.call_args - assert call_args.kwargs["data"]["unified_file_id"] == "test-unified-file-id" - assert "file_object" not in call_args.kwargs["data"] + # Verify DB upsert was called idempotently with expected create data (without file_object) + prisma_client.db.litellm_managedfiletable.upsert.assert_called_once() + call_args = prisma_client.db.litellm_managedfiletable.upsert.call_args + assert call_args.kwargs["where"] == {"unified_file_id": "test-unified-file-id"} + create_data = call_args.kwargs["data"]["create"] + assert create_data["unified_file_id"] == "test-unified-file-id" + assert "file_object" not in create_data + + +@pytest.mark.asyncio +async def test_store_unified_file_id_updates_file_metadata_on_existing_row(): + from litellm.proxy._types import UserAPIKeyAuth + from litellm.types.llms.openai import OpenAIFileObject + + prisma_client = AsyncMock() + prisma_client.db.litellm_managedfiletable.upsert = AsyncMock( + return_value=MagicMock() + ) + internal_usage_cache = MagicMock() + internal_usage_cache.async_set_cache = AsyncMock() + + proxy_managed_files = _PROXY_LiteLLMManagedFiles( + internal_usage_cache=internal_usage_cache, + prisma_client=prisma_client, + ) + user_api_key_dict = UserAPIKeyAuth(user_id="test-user") + + await proxy_managed_files.store_unified_file_id( + file_id="test-unified-file-id", + file_object=None, + litellm_parent_otel_span=None, + model_mappings={"model-123": "file-provider-xyz"}, + user_api_key_dict=user_api_key_dict, + ) + + file_object = OpenAIFileObject( + id="file-provider-xyz", + object="file", + bytes=1234, + created_at=1234567890, + filename="output.jsonl", + purpose="batch_output", + status="processed", + ) + file_object._hidden_params = { + "storage_backend": "s3", + "storage_url": "s3://bucket/output.jsonl", + } + + await proxy_managed_files.store_unified_file_id( + file_id="test-unified-file-id", + file_object=file_object, + litellm_parent_otel_span=None, + model_mappings={"model-123": "file-provider-xyz"}, + user_api_key_dict=user_api_key_dict, + ) + + first_update = prisma_client.db.litellm_managedfiletable.upsert.await_args_list[ + 0 + ].kwargs["data"]["update"] + second_update = prisma_client.db.litellm_managedfiletable.upsert.await_args_list[ + 1 + ].kwargs["data"]["update"] + assert "file_object" not in first_update + assert second_update["file_object"] == file_object.model_dump_json() + assert second_update["storage_backend"] == "s3" + assert second_update["storage_url"] == "s3://bucket/output.jsonl" @pytest.mark.asyncio diff --git a/tests/guardrails_tests/test_bedrock_guardrails.py b/tests/guardrails_tests/test_bedrock_guardrails.py index a23e89e576c..823ee05839f 100644 --- a/tests/guardrails_tests/test_bedrock_guardrails.py +++ b/tests/guardrails_tests/test_bedrock_guardrails.py @@ -1390,7 +1390,14 @@ async def test_bedrock_guardrail_disable_exception_on_block_non_streaming(): assert exception.status_code == 400 assert "Violated guardrail policy" in str(exception.detail) - # Test 2: disable_exception_on_block=True - should NOT raise exception + # Test 2: disable_exception_on_block=True - raises ModifyResponseException. + # LIT-4186: pre-fix, the native hook swallowed the block and set + # data["mock_response"], which was dead code (route_request already + # unpacked kwargs) so during_call let the model call proceed anyway. + # The correct contract is to raise ModifyResponseException so the endpoint + # handler returns a 200 with the block message as content. + from litellm.exceptions import ModifyResponseException + guardrail_disabled = BedrockGuardrail( guardrailIdentifier="test-guardrail", guardrailVersion="DRAFT", @@ -1402,20 +1409,13 @@ async def test_bedrock_guardrail_disable_exception_on_block_non_streaming(): ) as mock_post: mock_post.return_value = mock_bedrock_response - # Should NOT raise exception when disable_exception_on_block=True - try: - response = await guardrail_disabled.async_moderation_hook( + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail_disabled.async_moderation_hook( data=request_data, user_api_key_dict=mock_user_api_key_dict, call_type="completion", ) - # Should succeed and return data (even though content was blocked) - assert response is not None - print("✅ No exception raised when disable_exception_on_block=True") - except Exception as e: - pytest.fail( - f"Should not raise exception when disable_exception_on_block=True, but got: {e}" - ) + assert exc_info.value.message == "I can't provide that information." @pytest.mark.asyncio @@ -1514,7 +1514,10 @@ async def test_bedrock_guardrail_disable_exception_on_block_streaming(): async for chunk in result_generator: pass - # Test 2: disable_exception_on_block=True - should NOT raise exception + # Test 2: disable_exception_on_block=True. Streaming can't raise up to the + # endpoint handler (SSE headers already flushed), so the block is delivered + # as a synthetic stream with finish_reason=content_filter and the block + # message as content -- same shape a non-streaming block produces. guardrail_disabled = BedrockGuardrail( guardrailIdentifier="test-guardrail", guardrailVersion="DRAFT", @@ -1526,31 +1529,20 @@ async def test_bedrock_guardrail_disable_exception_on_block_streaming(): ) as mock_post: mock_post.return_value = mock_bedrock_response - # Should NOT raise exception when disable_exception_on_block=True - try: - result_generator = ( - guardrail_disabled.async_post_call_streaming_iterator_hook( - user_api_key_dict=mock_user_api_key_dict, - response=mock_streaming_response(), - request_data=request_data, - ) - ) - - # Consume the generator - should succeed without exceptions - result_chunks = [] - async for chunk in result_generator: - result_chunks.append(chunk) - - # Should have received chunks back even though content was blocked - assert len(result_chunks) > 0 - print( - "✅ Streaming completed without exception when disable_exception_on_block=True" - ) - - except Exception as e: - pytest.fail( - f"Should not raise exception when disable_exception_on_block=True in streaming, but got: {e}" - ) + result_generator = guardrail_disabled.async_post_call_streaming_iterator_hook( + user_api_key_dict=mock_user_api_key_dict, + response=mock_streaming_response(), + request_data=request_data, + ) + chunks = [c async for c in result_generator] + assert chunks, "streaming block should yield synthetic chunks, not empty" + assembled_content = "".join( + (c.choices[0].delta.content or "") + for c in chunks + if getattr(c, "choices", None) and getattr(c.choices[0], "delta", None) + ) + assert assembled_content == "I can't provide that information." + assert chunks[-1].choices[0].finish_reason == "content_filter" @pytest.mark.asyncio diff --git a/tests/image_gen_tests/test_bedrock_image_gen_unit_tests.py b/tests/image_gen_tests/test_bedrock_image_gen_unit_tests.py index 36ae9e1df67..6925bb2abc5 100644 --- a/tests/image_gen_tests/test_bedrock_image_gen_unit_tests.py +++ b/tests/image_gen_tests/test_bedrock_image_gen_unit_tests.py @@ -527,12 +527,11 @@ def test_backward_compatibility_regular_nova_model(): assert result["imageGenerationConfig"]["cfg_scale"] == 7 -def test_amazon_titan_image_gen(): - """Test Amazon Titan image generation with cost tracking.""" +def test_amazon_nova_canvas_image_gen(): + """Test Amazon Nova Canvas image generation with cost tracking.""" from litellm import image_generation - # Use v2 as v1 has reached end of life - model_id = "bedrock/amazon.titan-image-generator-v2:0" + model_id = "bedrock/amazon.nova-canvas-v1:0" response = litellm.image_generation( model=model_id, diff --git a/tests/litellm/llms/anthropic/test_anthropic_reasoning_effort.py b/tests/litellm/llms/anthropic/test_anthropic_reasoning_effort.py index 98ae7148c77..ef74249ca8e 100644 --- a/tests/litellm/llms/anthropic/test_anthropic_reasoning_effort.py +++ b/tests/litellm/llms/anthropic/test_anthropic_reasoning_effort.py @@ -12,39 +12,39 @@ class TestMapReasoningEffort: def test_none_returns_none_for_opus_4_6(self): """reasoning_effort=None should return None for Opus 4.6, not adaptive.""" result = AnthropicConfig._map_reasoning_effort( - reasoning_effort=None, model="claude-opus-4-6" + reasoning_effort=None, model="claude-opus-4-6", custom_llm_provider="anthropic" ) assert result is None def test_none_returns_none_for_other_models(self): """reasoning_effort=None should return None for non-Opus models.""" result = AnthropicConfig._map_reasoning_effort( - reasoning_effort=None, model="claude-4-sonnet-20250514" + reasoning_effort=None, model="claude-4-sonnet-20250514", custom_llm_provider="anthropic" ) assert result is None def test_opus_4_6_returns_adaptive_for_low(self): result = AnthropicConfig._map_reasoning_effort( - reasoning_effort="low", model="claude-opus-4-6" + reasoning_effort="low", model="claude-opus-4-6", custom_llm_provider="anthropic" ) assert result["type"] == "adaptive" def test_opus_4_6_returns_adaptive_for_high(self): result = AnthropicConfig._map_reasoning_effort( - reasoning_effort="high", model="claude-opus-4-6" + reasoning_effort="high", model="claude-opus-4-6", custom_llm_provider="anthropic" ) assert result["type"] == "adaptive" def test_other_model_low_returns_enabled_with_budget(self): result = AnthropicConfig._map_reasoning_effort( - reasoning_effort="low", model="claude-4-sonnet-20250514" + reasoning_effort="low", model="claude-4-sonnet-20250514", custom_llm_provider="anthropic" ) assert result["type"] == "enabled" assert "budget_tokens" in result def test_other_model_high_returns_enabled_with_budget(self): result = AnthropicConfig._map_reasoning_effort( - reasoning_effort="high", model="claude-4-sonnet-20250514" + reasoning_effort="high", model="claude-4-sonnet-20250514", custom_llm_provider="anthropic" ) assert result["type"] == "enabled" assert "budget_tokens" in result @@ -52,13 +52,13 @@ class TestMapReasoningEffort: def test_none_string_returns_none_for_opus_4_6(self): """reasoning_effort='none' should return None for Opus 4.6.""" result = AnthropicConfig._map_reasoning_effort( - reasoning_effort="none", model="claude-opus-4-6" + reasoning_effort="none", model="claude-opus-4-6", custom_llm_provider="anthropic" ) assert result is None def test_none_string_returns_none_for_other_models(self): """reasoning_effort='none' should return None for non-Opus models.""" result = AnthropicConfig._map_reasoning_effort( - reasoning_effort="none", model="claude-4-sonnet-20250514" + reasoning_effort="none", model="claude-4-sonnet-20250514", custom_llm_provider="anthropic" ) assert result is None diff --git a/tests/litellm_utils_tests/test_cyberark.py b/tests/litellm_utils_tests/test_cyberark.py index 67575d3e781..71daf35a265 100644 --- a/tests/litellm_utils_tests/test_cyberark.py +++ b/tests/litellm_utils_tests/test_cyberark.py @@ -5,6 +5,7 @@ Integration test for CyberArk Conjur Secret Manager. import os import sys import pytest +import yaml from dotenv import load_dotenv load_dotenv() @@ -42,6 +43,82 @@ def create_mock_response(status_code: int, text: str = ""): return mock_response +@pytest.mark.asyncio +async def test_cyberark_write_secret_rejects_yaml_injection(): + """ + Regression test: async_write_secret must reject a secret_name that is not + safe to embed in the Conjur policy body, before any HTTP call is made. + """ + with patch("litellm.proxy.proxy_server.premium_user", True): + malicious_secret_name = "foo\n- !grant\n role: !!admin\n member: attacker" + + mock_sync_client = MagicMock() + mock_async_client = AsyncMock() + + with ( + patch( + "litellm.secret_managers.cyberark_secret_manager._get_httpx_client", + return_value=mock_sync_client, + ), + patch( + "litellm.secret_managers.cyberark_secret_manager.get_async_httpx_client", + return_value=mock_async_client, + ), + ): + cyberark_manager = CyberArkSecretManager() + + response = await cyberark_manager.async_write_secret( + secret_name=malicious_secret_name, + secret_value="sk-1234", + ) + + assert response["status"] == "error" + assert "Invalid secret_name" in response["message"] + # The malicious policy YAML must never reach the wire. + mock_sync_client.client.post.assert_not_called() + mock_async_client.post.assert_not_called() + + +@pytest.mark.parametrize( + "secret_name", + [ + "foo: bar", + "foo # bar", + "plain-alias", + "team/user@example.com", + ], +) +def test_cyberark_ensure_variable_exists_escapes_yaml_metacharacters(secret_name): + """ + Regression test: _ensure_variable_exists must escape secret_name (not just + denylist-check it) so the policy body always parses back to exactly one + '!variable' scalar node holding the untouched secret_name. + """ + with patch("litellm.proxy.proxy_server.premium_user", True): + captured = {} + + def _capture_post(url, headers=None, content=None): + captured["content"] = content + return create_mock_response(status_code=201, text="") + + mock_sync_client = MagicMock() + mock_sync_client.client.post.side_effect = _capture_post + + with patch( + "litellm.secret_managers.cyberark_secret_manager._get_httpx_client", + return_value=mock_sync_client, + ): + cyberark_manager = CyberArkSecretManager() + cyberark_manager._ensure_variable_exists(secret_name) + + policy_yaml = captured["content"] + parsed = yaml.compose(policy_yaml) + assert len(parsed.value) == 1 + node = parsed.value[0] + assert node.tag == "!variable" + assert node.value == secret_name + + @pytest.mark.asyncio async def test_cyberark_write_and_read_secret(): """ diff --git a/tests/litellm_utils_tests/test_hashicorp.py b/tests/litellm_utils_tests/test_hashicorp.py index 3bdf11ea565..9aff7ddc10e 100644 --- a/tests/litellm_utils_tests/test_hashicorp.py +++ b/tests/litellm_utils_tests/test_hashicorp.py @@ -409,6 +409,33 @@ def test_hashicorp_custom_mount_and_prefix(hashicorp_secret_manager): hashicorp_secret_manager.vault_namespace = original_namespace +@pytest.mark.parametrize( + "malicious_secret_name", + [ + "../../../other-app/creds", + "litellm/../../secret", + "foo\nbar", + "foo
bar", + "foo
bar", + "foo\x85bar", + ], +) +def test_hashicorp_get_url_rejects_path_traversal(monkeypatch, malicious_secret_name): + """ + Regression test: get_url must reject an invalid secret_name instead of + building a URL from it. + + Uses monkeypatch + a directly-constructed manager (not the shared + hashicorp_secret_manager fixture) so this runs in CI without real Vault + credentials configured; get_url performs no I/O. + """ + monkeypatch.setenv("HCP_VAULT_TOKEN", "test-token-for-get-url-only") + manager = HashicorpSecretManager() + + with pytest.raises(ValueError): + manager.get_url(malicious_secret_name) + + mock_old_vault_response = { "request_id": "80fafb6a-e96a-4c5b-29fa-ff505ac72201", "lease_id": "", diff --git a/tests/llm_responses_api_testing/base_responses_api.py b/tests/llm_responses_api_testing/base_responses_api.py index 30f444b9acc..407091a65b3 100644 --- a/tests/llm_responses_api_testing/base_responses_api.py +++ b/tests/llm_responses_api_testing/base_responses_api.py @@ -746,7 +746,8 @@ class BaseResponsesAPITest(ABC): E2E test for Shell tool on OpenAI Responses API. Passes tools=[{"type": "shell", "environment": {"type": "container_auto"}}]; validates that the request is accepted and returns a valid response. - Only runs for OpenAI/Azure (Responses API with shell support). + Only runs for OpenAI; offline coverage for the Azure route lives in + tests/test_litellm/responses/test_responses_api_request_body.py. """ base_completion_call_args = self.get_base_completion_call_args() model = ( @@ -754,8 +755,10 @@ class BaseResponsesAPITest(ABC): or base_completion_call_args.get("model") or "" ) - if "openai/" not in str(model) and "azure/" not in str(model): - pytest.skip("Shell tool e2e is only run for OpenAI/Azure Responses API") + if "openai/" not in str(model): + pytest.skip( + "Shell tool e2e is OpenAI-only; no Azure deployment supports the shell tool yet, re-enable once one exists" + ) tools = [{"type": "shell", "environment": {"type": "container_auto"}}] input_msg = "List files in /mnt/data and show python --version." try: @@ -765,7 +768,10 @@ class BaseResponsesAPITest(ABC): max_output_tokens=256, tools=tools, tool_choice="auto", + timeout=90, ) + except litellm.Timeout: + pytest.skip("Provider did not answer the shell tool request within 90s") except litellm.InternalServerError: pytest.skip("Skipping test due to litellm.InternalServerError") except litellm.BadRequestError as e: diff --git a/tests/llm_responses_api_testing/test_azure_responses_api.py b/tests/llm_responses_api_testing/test_azure_responses_api.py index fed9e9e11f0..ccef8cbf1e7 100644 --- a/tests/llm_responses_api_testing/test_azure_responses_api.py +++ b/tests/llm_responses_api_testing/test_azure_responses_api.py @@ -2,7 +2,6 @@ import os import sys import pytest import asyncio -from typing import Optional from unittest.mock import patch, AsyncMock sys.path.insert(0, os.path.abspath("../..")) @@ -30,10 +29,6 @@ class TestAzureResponsesAPITest(BaseResponsesAPITest): "api_version": "2025-03-01-preview", } - def get_advanced_model_for_shell_tool(self) -> Optional[str]: - """If specified, overrides the model used by test_responses_api_shell_tool_streaming_sees_shell_output (e.g. openai/gpt-5.2 for shell support).""" - return "azure/gpt-5-mini" - @pytest.mark.asyncio async def test_azure_responses_api_preview_api_version(): diff --git a/tests/llm_responses_api_testing/test_base_responses_api_streaming_iterator.py b/tests/llm_responses_api_testing/test_base_responses_api_streaming_iterator.py index 37fcc602d37..5388c5aef83 100644 --- a/tests/llm_responses_api_testing/test_base_responses_api_streaming_iterator.py +++ b/tests/llm_responses_api_testing/test_base_responses_api_streaming_iterator.py @@ -67,6 +67,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_config = Mock(spec=BaseResponsesAPIConfig) mock_responses_api_response = Mock(spec=ResponsesAPIResponse) @@ -107,6 +108,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_response.headers = {} mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_config = Mock(spec=BaseResponsesAPIConfig) # Create a mock ResponsesAPIResponse for the completed event @@ -179,6 +181,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_response.headers = {} mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_config = Mock(spec=BaseResponsesAPIConfig) # Create a mock OutputTextDeltaEvent (not a completed event) @@ -239,6 +242,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_response.headers = {} mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_config = Mock(spec=BaseResponsesAPIConfig) # Create the iterator instance @@ -265,6 +269,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_response.headers = {} mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_config = Mock(spec=BaseResponsesAPIConfig) # Create the iterator instance @@ -291,6 +296,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_response.headers = {} mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_config = Mock(spec=BaseResponsesAPIConfig) # Create the iterator instance @@ -329,6 +335,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_response.aiter_bytes = Mock() mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_logging_obj.async_success_handler = Mock() mock_logging_obj.success_handler = Mock() mock_config = Mock(spec=BaseResponsesAPIConfig) @@ -397,6 +404,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_logging_obj.async_failure_handler = Mock() mock_logging_obj.failure_handler = Mock() @@ -457,6 +465,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_logging_obj.async_failure_handler = Mock() mock_logging_obj.failure_handler = Mock() @@ -505,6 +514,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_response.aiter_bytes = Mock() mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_logging_obj.async_failure_handler = Mock() mock_logging_obj.failure_handler = Mock() mock_logging_obj.async_success_handler = Mock() @@ -587,6 +597,7 @@ class TestBaseResponsesAPIStreamingIterator: mock_response.aiter_bytes = Mock() mock_logging_obj = Mock(spec=LiteLLMLoggingObj) mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None mock_logging_obj.async_failure_handler = Mock() mock_logging_obj.failure_handler = Mock() mock_logging_obj.async_success_handler = Mock() @@ -636,9 +647,10 @@ class TestBaseResponsesAPIStreamingIterator: assert result.type == ResponsesAPIStreamEvents.RESPONSE_INCOMPLETE assert iterator.completed_response == result - # Success handler should have been called (via _handle_logging_completed_response) + # Success handlers are dispatched as one async task (via _handle_logging_completed_response); + # the sync handler must never be submitted to the executor concurrently (LIT-4210) mock_create_task.assert_called_once() - mock_executor.submit.assert_called_once() + mock_executor.submit.assert_not_called() # Failure handlers should NOT have been called mock_logging_obj.async_failure_handler.assert_not_called() diff --git a/tests/llm_responses_api_testing/test_openai_responses_api.py b/tests/llm_responses_api_testing/test_openai_responses_api.py index ea8b8fa886c..bd1517dbffb 100644 --- a/tests/llm_responses_api_testing/test_openai_responses_api.py +++ b/tests/llm_responses_api_testing/test_openai_responses_api.py @@ -1628,23 +1628,27 @@ async def test_openai_responses_api_token_limit_error(): """ Relevant issue: https://github.com/BerriAI/litellm/issues/15785 - - When this fails you'll see: - "pydantic_core._pydantic_core.ValidationError: 3 validation errors for ErrorEvent" - in the console. + Parsing the in-stream ErrorEvent must not raise + "pydantic_core._pydantic_core.ValidationError: 3 validation errors for ErrorEvent". + The iterator now surfaces the event as litellm.APIError with status 400 + (invalid_request_error is a non-retriable client error, so no + MidStreamFallbackError wrapping) carrying the provider's message. """ litellm._turn_on_debug() # Generate text with >400k tokens to trigger token limit error oversized_text = "This is a test sentence. " * 50000 # ~400k tokens - # This will raise ValidationError instead of showing the real error response = await litellm.aresponses( model="gpt-5-mini", input=oversized_text, stream=True ) - async for event in response: - print(event) # Never reaches here - ValidationError is raised + with pytest.raises(litellm.APIError) as exc_info: + async for event in response: + print(event) + + assert exc_info.value.status_code == 400 + assert "exceeds the context window" in str(exc_info.value) async def test_openai_streaming_logging(): diff --git a/tests/llm_responses_api_testing/test_responses_hooks.py b/tests/llm_responses_api_testing/test_responses_hooks.py index 3799a0b9121..2344a62de4d 100644 --- a/tests/llm_responses_api_testing/test_responses_hooks.py +++ b/tests/llm_responses_api_testing/test_responses_hooks.py @@ -34,9 +34,15 @@ class _FakeLoggingObj: self.last_success_kwargs = None self.last_async_success_kwargs = None self.start_time = datetime.now() + self.completion_start_time = None self.model_call_details = {"litellm_params": {}} # Signature alignment with Logging handlers + async def dispatch_success_handlers(self, *args, **kwargs): + kwargs.pop("prefer_async_handlers", None) + await self.async_success_handler(*args, **kwargs) + self.success_handler(*args, **kwargs) + def success_handler(self, *args, **kwargs): self.success_calls += 1 self.last_success_kwargs = kwargs @@ -51,6 +57,10 @@ class _FakeLoggingObj: async def async_failure_handler(self, *args, **kwargs): self.async_failure_calls += 1 + def _update_completion_start_time(self, completion_start_time): + self.completion_start_time = completion_start_time + self.model_call_details["completion_start_time"] = completion_start_time + def _make_completed_response(response_id: str = "resp_test") -> ResponseCompletedEvent: return ResponseCompletedEvent( diff --git a/tests/llm_translation/conftest.py b/tests/llm_translation/conftest.py index 77fcb46a2b1..f5b71236e92 100644 --- a/tests/llm_translation/conftest.py +++ b/tests/llm_translation/conftest.py @@ -41,11 +41,13 @@ def fake_openai_endpoint(): # Per-item respx detection (``apply_vcr_auto_marker_to_items``) handles -# the vast majority of respx-vs-vcrpy conflicts automatically. The only -# entry below is the persister's own unit-test file, which exercises -# ``save_cassette`` / ``load_cassette`` against fakeredis and must not -# itself run under a live cassette context. -_VCR_AUTO_MARKER_SKIP_FILES = frozenset({"test_vcr_redis_persister.py"}) +# the vast majority of respx-vs-vcrpy conflicts automatically. The entries +# below are the persister's and the WebSocket VCR's own unit-test files, which +# exercise ``save_cassette`` / ``load_cassette`` against fakeredis and must not +# themselves run under a live cassette context. +_VCR_AUTO_MARKER_SKIP_FILES = frozenset( + {"test_vcr_redis_persister.py", "test_ws_vcr.py"} +) _VCR_INCOMPATIBLE_NODEID_SUFFIXES: tuple[str, ...] = () diff --git a/tests/llm_translation/realtime/conftest.py b/tests/llm_translation/realtime/conftest.py new file mode 100644 index 00000000000..e305131593e --- /dev/null +++ b/tests/llm_translation/realtime/conftest.py @@ -0,0 +1,89 @@ +"""WebSocket VCR wiring for the realtime suite. + +This directory inherits the HTTP VCR machinery from +``tests/llm_translation/conftest.py`` (which only intercepts httpx/aiohttp and +is therefore a no-op for realtime WebSocket traffic). The autouse fixture below +adds the WebSocket layer: it patches ``websockets.connect`` for the duration of +each test so realtime frames are recorded to, or replayed from, the same +cassette Redis under a ``litellm:vcr:wscassette:`` prefix. +""" + +from __future__ import annotations + +import os +import sys +from typing import Optional + +import pytest + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))) + +from tests._vcr_conftest_common import ( # noqa: E402 + vcr_disabled, + vcr_outcome_logging_enabled, +) +from tests._ws_vcr import ( # noqa: E402 + WsVcrController, + build_ws_cassette_client, + load_ws_cassette, + replay_timeout_seconds, + save_ws_cassette, + ws_redis_key_for, +) + +_ws_cassette_client: Optional[object] = None + + +def _get_ws_cassette_client() -> Optional[object]: + global _ws_cassette_client + if _ws_cassette_client is None: + _ws_cassette_client = build_ws_cassette_client() + return _ws_cassette_client + + +def _emit_verdict(request: pytest.FixtureRequest, verdict: str) -> None: + if os.environ.get("PYTEST_XDIST_WORKER"): + return + reporter = request.config.pluginmanager.getplugin("terminalreporter") + if reporter is None: + return + reporter.write_line(f"{verdict} :: {request.node.nodeid}") + + +@pytest.fixture(autouse=True) +def _ws_vcr(request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch): + if vcr_disabled(): + yield + return + + import websockets + + client = _get_ws_cassette_client() + if client is None: + yield + return + + key = ws_redis_key_for(request.node.nodeid) + cassette = load_ws_cassette(client, key) + controller = WsVcrController( + original_connect=websockets.connect, + cassette=cassette, + timeout=replay_timeout_seconds(), + ) + monkeypatch.setattr(websockets, "connect", controller.connect) + + yield + + rep_call = getattr(request.node, "rep_call", None) + passed = bool(rep_call and rep_call.passed) + + if controller.recorded: + built = controller.built_cassette() + if built is not None: + save_ws_cassette(client, key, built, passed=passed) + + if vcr_outcome_logging_enabled(): + _emit_verdict(request, controller.verdict()) + + if controller.errors and passed: + raise controller.errors[0] diff --git a/tests/llm_translation/realtime/test_realtime_guardrails_openai.py b/tests/llm_translation/realtime/test_realtime_guardrails_openai.py index 413f5d1ff8b..cf596aa597e 100644 --- a/tests/llm_translation/realtime/test_realtime_guardrails_openai.py +++ b/tests/llm_translation/realtime/test_realtime_guardrails_openai.py @@ -4,7 +4,8 @@ Integration tests for RealTimeStreaming guardrails against a live OpenAI backend These tests require OPENAI_API_KEY and are skipped if not set. They verify end-to-end that: - 1. A text message blocked by a guardrail -> error event sent to client, NO AI response. + 1. A text message blocked by a guardrail -> error event sent to client, the blocked + message never reaches OpenAI, and the client's response.create is not forwarded. 2. A voice transcript blocked by a guardrail -> error event sent, response.create NOT sent. 3. A clean text message passes through and triggers a real OpenAI response. @@ -55,9 +56,25 @@ def _make_guardrail(event_hook=GuardrailEventHooks.pre_call): ) -async def _wait_for_event( - client_events: List[dict], event_type: str, timeout: float = 15.0 -) -> dict: +class RecordingBackendWebSocket: + """Wraps a real backend WebSocket and records every frame sent to it.""" + + def __init__(self, backend_ws): + self._backend_ws = backend_ws + self.sent_messages: List[str] = [] + + async def send(self, message): + self.sent_messages.append(message) + await self._backend_ws.send(message) + + async def recv(self, *args, **kwargs): + return await self._backend_ws.recv(*args, **kwargs) + + async def close(self): + await self._backend_ws.close() + + +async def _wait_for_event(client_events: List[dict], event_type: str, timeout: float = 15.0) -> dict: """Poll client_events list until an event with matching type appears.""" deadline = asyncio.get_event_loop().time() + timeout while asyncio.get_event_loop().time() < deadline: @@ -65,9 +82,7 @@ async def _wait_for_event( if matching: return matching[0] await asyncio.sleep(0.05) - raise TimeoutError( - f"Timed out waiting for '{event_type}'. Got so far: {[e.get('type') for e in client_events]}" - ) + raise TimeoutError(f"Timed out waiting for '{event_type}'. Got so far: {[e.get('type') for e in client_events]}") async def _build_streaming(client_events: List[dict], backend_ws, request_data=None): @@ -99,12 +114,21 @@ async def _build_streaming(client_events: List[dict], backend_ws, request_data=N @pytest.mark.asyncio async def test_text_message_blocked_by_guardrail_no_ai_response(): """ - Send a text message containing the blocked phrase. + Send a text message containing the blocked phrase, immediately followed by + response.create (the reflexive client pattern). Guardrail must: - Send error event (guardrail_violation) to client. - Send response.output_audio_transcript.delta (or beta-protocol - response.audio_transcript.delta) with the block message to client. - - NOT forward response.create to OpenAI (no AI response). + response.audio_transcript.delta) to client. + - NEVER forward the blocked message to OpenAI. + - Drop the client's response.create; the only response.create OpenAI sees + is the guardrail's own (which voices the block message), so the model + can never answer the blocked content. + + Assertions are on the recorded backend wire traffic, not on the model's + reply wording: gpt-realtime phrases its voicing/refusal of the guardrail + prompt nondeterministically, which made wording-based assertions flaky + (see PRs #28191, #28200, #29477). """ import websockets @@ -119,21 +143,16 @@ async def test_text_message_blocked_by_guardrail_no_ai_response(): additional_headers={ "Authorization": f"Bearer {OPENAI_API_KEY}", }, - ) as backend_ws: + ) as raw_backend_ws: + backend_ws = RecordingBackendWebSocket(raw_backend_ws) streaming, input_queue = await _build_streaming(client_events, backend_ws) - # Start backend -> client forwarding - backend_task = asyncio.create_task( - streaming.backend_to_client_send_messages() - ) - # Start client -> backend forwarding (reads from input_queue) + backend_task = asyncio.create_task(streaming.backend_to_client_send_messages()) client_task = asyncio.create_task(streaming.client_ack_messages()) try: - # Wait until session is ready await _wait_for_event(client_events, "session.created", timeout=15) - # Send the blocked message + response.create blocked_item = json.dumps( { "type": "conversation.item.create", @@ -149,34 +168,23 @@ async def test_text_message_blocked_by_guardrail_no_ai_response(): } ) await input_queue.put(blocked_item) - # Give guardrail time to process before the follow-up response.create - await asyncio.sleep(0.3) await input_queue.put(json.dumps({"type": "response.create"})) - # Allow time for guardrail round-trip - await asyncio.sleep(3.0) + await _wait_for_event(client_events, "response.done", timeout=30) finally: backend_task.cancel() client_task.cancel() await asyncio.gather(backend_task, client_task, return_exceptions=True) - # --- Assertions --- event_types = [e.get("type") for e in client_events] - # 1. Must have received guardrail error (may not be the first error event - # if the OpenAI session emits other errors, e.g. missing parameters) error_events = [e for e in client_events if e.get("type") == "error"] - guardrail_errors = [ - e - for e in error_events - if e.get("error", {}).get("type") == "guardrail_violation" - ] - assert ( - len(guardrail_errors) >= 1 - ), f"Expected at least one guardrail_violation error but got: {[e.get('error', {}).get('type') for e in error_events]}" + guardrail_errors = [e for e in error_events if e.get("error", {}).get("type") == "guardrail_violation"] + assert len(guardrail_errors) >= 1, ( + f"Expected at least one guardrail_violation error but got: {[e.get('error', {}).get('type') for e in error_events]}" + ) - # 2. Must have the guardrail message surfaced as an AI transcript delta transcript_deltas = [ e for e in client_events @@ -186,64 +194,30 @@ async def test_text_message_blocked_by_guardrail_no_ai_response(): "response.audio_transcript.delta", ) ] - assert ( - len(transcript_deltas) >= 1 - ), f"Expected guardrail message in transcript delta, got: {event_types}" + assert len(transcript_deltas) >= 1, f"Expected guardrail message in transcript delta, got: {event_types}" - # 3. No *real* AI response to the blocked content should have been - # generated. The original user message is blocked BEFORE it is - # forwarded to OpenAI, so the only thing the model ever sees is the - # guardrail's "say exactly: " prompt - # (see realtime_streaming.py). Two safe outcomes are possible: - # - the model voices the block message verbatim (older realtime - # snapshots did this -> text contains "blocked"), or - # - the model declines to repeat it (gpt-realtime tends to refuse - # verbatim-repeat instructions, e.g. "I'm sorry, but I can't - # repeat that message."). - # Both mean the blocked prompt itself was never answered, so we - # accept either. The hard invariant is that the blocked phrase must - # never leak into AI output, and the model must not have produced a - # normal answer to the user (which would have neither a block nor a - # refusal marker). - safe_markers = ( - "block", - "guardrail", - "content filter", - "policy", - "can't repeat", - "cannot repeat", - "can't say", - "cannot say", - "won't repeat", - "can't assist", - "can't help", - "unable to", - "i'm sorry", - "i am sorry", + sent_frames = backend_ws.sent_messages + assert all(BLOCKED_PHRASE not in frame for frame in sent_frames), ( + f"Blocked message was forwarded to OpenAI: {sent_frames}" ) + + sent_types = [json.loads(frame).get("type") for frame in sent_frames] + assert sent_types.count("response.create") == 1, ( + f"Expected only the guardrail's response.create to reach OpenAI, got backend frames: {sent_types}" + ) + assert sent_types.count("conversation.item.create") == 1, ( + f"Expected only the guardrail's conversation.item.create to reach OpenAI, got backend frames: {sent_types}" + ) + done_events = [e for e in client_events if e.get("type") == "response.done"] + assert len(done_events) >= 1, f"Expected response.done, got: {event_types}" for done in done_events: output = done.get("response", {}).get("output", []) ai_texts = [ - c.get("text", "") or c.get("transcript", "") - for item in output - for c in item.get("content", []) + c.get("text", "") or c.get("transcript", "") for item in output for c in item.get("content", []) ] real_ai_text = " ".join(ai_texts).strip() - if real_ai_text: - assert ( - BLOCKED_PHRASE not in real_ai_text - ), f"Blocked phrase leaked into AI response: {real_ai_text!r}" - normalized_ai_text = ( - real_ai_text.lower() - .replace("\u2019", "'") - .replace("\u2018", "'") - .replace("\u201c", '"') - .replace("\u201d", '"') - ) - assert any( - marker in normalized_ai_text for marker in safe_markers - ), f"AI responded with non-guardrail content even though message was blocked: {real_ai_text!r}" + assert BLOCKED_PHRASE not in real_ai_text, f"Blocked phrase leaked into AI response: {real_ai_text!r}" finally: litellm.callbacks = [] @@ -289,9 +263,7 @@ async def test_voice_transcript_blocked_by_guardrail(): # 1. Error event must be sent to client error_events = [e for e in client_events if e.get("type") == "error"] - assert ( - len(error_events) >= 1 - ), f"Expected guardrail error event, got: {event_types}" + assert len(error_events) >= 1, f"Expected guardrail error event, got: {event_types}" assert error_events[0]["error"]["type"] == "guardrail_violation" # 2. Check what was sent to backend. @@ -299,16 +271,12 @@ async def test_voice_transcript_blocked_by_guardrail(): # + response.create (to speak the block message). That's acceptable. # What we assert is that a response.cancel was sent (blocking the original). sent_to_backend = [ - json.loads(c.args[0]) - for c in backend_ws.send.call_args_list - if c.args and isinstance(c.args[0], str) + json.loads(c.args[0]) for c in backend_ws.send.call_args_list if c.args and isinstance(c.args[0], str) ] - response_cancels = [ - e for e in sent_to_backend if e.get("type") == "response.cancel" - ] - assert ( - len(response_cancels) >= 1 or len(sent_to_backend) == 0 - ), f"Guardrail should have sent response.cancel or nothing, got: {sent_to_backend}" + response_cancels = [e for e in sent_to_backend if e.get("type") == "response.cancel"] + assert len(response_cancels) >= 1 or len(sent_to_backend) == 0, ( + f"Guardrail should have sent response.cancel or nothing, got: {sent_to_backend}" + ) # Note: The guardrail may or may not send transcript deltas; the error event # (assertion #1) is the primary signal that the blocked content was handled. @@ -339,9 +307,7 @@ async def test_clean_text_message_passes_through_to_openai(): ) as backend_ws: streaming, input_queue = await _build_streaming(client_events, backend_ws) - backend_task = asyncio.create_task( - streaming.backend_to_client_send_messages() - ) + backend_task = asyncio.create_task(streaming.backend_to_client_send_messages()) client_task = asyncio.create_task(streaming.client_ack_messages()) try: @@ -353,9 +319,7 @@ async def test_clean_text_message_passes_through_to_openai(): "type": "conversation.item.create", "item": { "role": "user", - "content": [ - {"type": "input_text", "text": "Reply with just: OK"} - ], + "content": [{"type": "input_text", "text": "Reply with just: OK"}], }, } ) @@ -373,20 +337,14 @@ async def test_clean_text_message_passes_through_to_openai(): # No guardrail error should have been sent error_events = [e for e in client_events if e.get("type") == "error"] - guardrail_errors = [ - e - for e in error_events - if e.get("error", {}).get("type") == "guardrail_violation" - ] - assert ( - len(guardrail_errors) == 0 - ), f"Clean message should not trigger guardrail, got: {guardrail_errors}" + guardrail_errors = [e for e in error_events if e.get("error", {}).get("type") == "guardrail_violation"] + assert len(guardrail_errors) == 0, f"Clean message should not trigger guardrail, got: {guardrail_errors}" # AI response must be present done_events = [e for e in client_events if e.get("type") == "response.done"] - assert ( - len(done_events) >= 1 - ), f"Expected response.done from OpenAI, got: {[e.get('type') for e in client_events]}" + assert len(done_events) >= 1, ( + f"Expected response.done from OpenAI, got: {[e.get('type') for e in client_events]}" + ) finally: litellm.callbacks = [] diff --git a/tests/llm_translation/reasoning_effort_grid/grid_spec.py b/tests/llm_translation/reasoning_effort_grid/grid_spec.py index 1bb468d15ad..762606bb3a0 100644 --- a/tests/llm_translation/reasoning_effort_grid/grid_spec.py +++ b/tests/llm_translation/reasoning_effort_grid/grid_spec.py @@ -181,6 +181,13 @@ ANTHROPIC_DIRECT_MODELS: Tuple[ModelEntry, ...] = ( required_env=_ANTHROPIC_REQ, caps=_CAPS_XHIGH_MAX, ), + ModelEntry( + alias="claude-sonnet-5", + model="anthropic/claude-sonnet-5", + mode="adaptive", + required_env=_ANTHROPIC_REQ, + caps=_CAPS_XHIGH_MAX, + ), ModelEntry( alias="claude-sonnet-4-6", model="anthropic/claude-sonnet-4-6", diff --git a/tests/llm_translation/reasoning_effort_grid/test_reasoning_effort_grid.py b/tests/llm_translation/reasoning_effort_grid/test_reasoning_effort_grid.py index 304743b1f3c..2409067ebbe 100644 --- a/tests/llm_translation/reasoning_effort_grid/test_reasoning_effort_grid.py +++ b/tests/llm_translation/reasoning_effort_grid/test_reasoning_effort_grid.py @@ -201,8 +201,8 @@ async def test_reasoning_effort_grid( def test_grid_cell_count() -> None: - assert len(_PARAMS) == 29 * 11, ( - f"expected 319 cells (29 provider x model combos x 11 efforts), " + assert len(_PARAMS) == 30 * 11, ( + f"expected 330 cells (30 provider x model combos x 11 efforts), " f"got {len(_PARAMS)}" ) diff --git a/tests/llm_translation/test_prompt_factory.py b/tests/llm_translation/test_prompt_factory.py index 05a58a135d2..ae215602e31 100644 --- a/tests/llm_translation/test_prompt_factory.py +++ b/tests/llm_translation/test_prompt_factory.py @@ -20,6 +20,8 @@ from litellm.litellm_core_utils.prompt_templates.factory import ( convert_to_anthropic_tool_invoke, convert_url_to_base64, create_anthropic_image_param, + get_tool_calls_from_response, + has_tool_with_name, llama_2_chat_pt, prompt_factory, ) @@ -2385,3 +2387,100 @@ def test_anthropic_messages_pt_list_content_with_thinking_preserves_order(): # Verify signatures preserved in correct positions assert content[0]["signature"] == "sig_1" assert content[3]["signature"] == "sig_2" + + +def test_get_tool_calls_from_response_chat_completions(): + response = MagicMock() + response.output = None + response.content = None + tool_call = MagicMock() + tool_call.id = "call_abc" + tool_call.function.name = "my_tool" + tool_call.function.arguments = '{"x": 1}' + response.choices = [MagicMock(message=MagicMock(tool_calls=[tool_call]))] + + result = get_tool_calls_from_response(response) + + assert result == [{"id": "call_abc", "name": "my_tool", "arguments": {"x": 1}}] + + +def test_get_tool_calls_from_response_responses_api(): + response = MagicMock() + response.choices = None + response.content = None + response.output = [ + { + "type": "function_call", + "id": "fc_1", + "call_id": "call_1", + "name": "my_tool", + "arguments": '{"x": 2}', + } + ] + + result = get_tool_calls_from_response(response) + + assert result == [{"id": "call_1", "name": "my_tool", "arguments": {"x": 2}}] + + +def test_get_tool_calls_from_response_anthropic_messages(): + response = MagicMock() + response.choices = None + response.output = None + response.content = [ + {"type": "tool_use", "id": "toolu_1", "name": "my_tool", "input": {"x": 3}}, + ] + + result = get_tool_calls_from_response(response) + + assert result == [{"id": "toolu_1", "name": "my_tool", "arguments": {"x": 3}}] + + +def test_get_tool_calls_from_response_anthropic_messages_plain_dict(): + # AnthropicMessagesResponse is a TypedDict -- real responses are plain + # dicts at runtime, not objects with attribute access. A MagicMock-only + # test would pass even if the extractor used bare getattr() and silently + # returned nothing for a real response. + response = { + "content": [ + {"type": "tool_use", "id": "toolu_1", "name": "my_tool", "input": {"x": 3}}, + ] + } + + result = get_tool_calls_from_response(response) + + assert result == [{"id": "toolu_1", "name": "my_tool", "arguments": {"x": 3}}] + + +def test_get_tool_calls_from_response_no_tool_calls(): + response = MagicMock() + response.choices = None + response.output = None + response.content = None + + assert get_tool_calls_from_response(response) == [] + + +def test_has_tool_with_name_openai_function_shape(): + tools = [{"type": "function", "function": {"name": "my_tool"}}] + assert has_tool_with_name(tools, "my_tool") + assert not has_tool_with_name(tools, "other_tool") + + +def test_has_tool_with_name_anthropic_custom_shape(): + tools = [{"type": "custom", "name": "my_tool", "input_schema": {}}] + assert has_tool_with_name(tools, "my_tool") + assert not has_tool_with_name(tools, "other_tool") + + +def test_has_tool_with_name_anthropic_shape_without_type_field(): + # Anthropic's documented client tool format is just name + input_schema; + # "type" isn't required at all (type: "custom" is only one possible value). + tools = [{"name": "my_tool", "input_schema": {}}] + assert has_tool_with_name(tools, "my_tool") + assert not has_tool_with_name(tools, "other_tool") + + +def test_has_tool_with_name_not_a_list(): + assert not has_tool_with_name(None, "my_tool") + assert not has_tool_with_name("not a list", "my_tool") diff --git a/tests/llm_translation/test_ws_vcr.py b/tests/llm_translation/test_ws_vcr.py new file mode 100644 index 00000000000..1a72d62d80f --- /dev/null +++ b/tests/llm_translation/test_ws_vcr.py @@ -0,0 +1,275 @@ +from __future__ import annotations + +import asyncio +import os +import sys +import warnings + +import fakeredis +import pytest +from websockets.exceptions import ConnectionClosedOK + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) + +from tests._vcr_redis_persister import ( # noqa: E402 + VCRCassetteCacheWarning, + cassette_cache_health, +) +from tests._ws_vcr import ( # noqa: E402 + CASSETTE_TTL_SECONDS, + RedisLike, + ReplayConnection, + WsCassette, + WsFrame, + WsSession, + WsSessionRecorder, + WsVcrContractDrift, + WsVcrReplayError, + WsVcrReplayTimeout, + build_ws_cassette_client, + load_ws_cassette, + save_ws_cassette, + scrub_secrets, + text_frames_match, + ws_redis_key_for, +) + + +def _server(text: str, client_frames_before: int) -> WsFrame: + return WsFrame( + direction="server_to_client", + opcode="text", + text=text, + client_frames_before=client_frames_before, + ) + + +def _client(text: str) -> WsFrame: + return WsFrame(direction="client_to_server", opcode="text", text=text) + + +def _collect_errors(): + errors: list[WsVcrReplayError] = [] + return errors, errors.append + + +def test_cassette_json_roundtrip_preserves_frames_and_gate(): + cassette = WsCassette( + sessions=( + WsSession( + frames=( + _server('{"type":"session.created"}', 0), + _client('{"type":"response.create"}'), + _server('{"type":"response.done"}', 1), + WsFrame( + direction="server_to_client", opcode="binary", binary_b64="dGVzdA==", client_frames_before=1 + ), + ) + ), + ) + ) + + restored = WsCassette.model_validate_json(cassette.model_dump_json()) + + assert restored == cassette + assert restored.sessions[0].frames[2].client_frames_before == 1 + assert restored.sessions[0].frames[3].opcode == "binary" + assert restored.sessions[0].frames[3].binary_b64 == "dGVzdA==" + + +def test_recorder_tracks_client_frame_count_as_causal_gate(): + recorder = WsSessionRecorder() + recorder.record_server_frame('{"type":"session.created"}') + recorder.record_client_frame('{"type":"conversation.item.create"}') + recorder.record_client_frame('{"type":"response.create"}') + recorder.record_server_frame('{"type":"response.done"}') + + session = recorder.to_session() + server_frames = [f for f in session.frames if f.direction == "server_to_client"] + + assert server_frames[0].client_frames_before == 0 + assert server_frames[1].client_frames_before == 2 + + +async def test_replay_recv_returns_bytes_when_decode_false_and_str_otherwise(): + session = WsSession(frames=(_server("hello", 0), _server("world", 0))) + _, on_error = _collect_errors() + conn = ReplayConnection(session, timeout=1.0, on_error=on_error) + + as_bytes = await conn.recv(decode=False) + as_str = await conn.recv() + + assert as_bytes == b"hello" + assert as_str == "world" + + +async def test_replay_serves_server_frame_only_after_causal_client_count_met(): + session = WsSession( + frames=( + _server('{"type":"session.created"}', 0), + _client('{"type":"response.create"}'), + _server('{"type":"response.done"}', 1), + ) + ) + _, on_error = _collect_errors() + conn = ReplayConnection(session, timeout=2.0, on_error=on_error) + + first = await conn.recv(decode=False) + assert first == b'{"type":"session.created"}' + + gated = asyncio.ensure_future(conn.recv(decode=False)) + await asyncio.sleep(0.1) + assert not gated.done(), "gated server frame was released before the recorded client frame was sent" + + await conn.send('{"type":"response.create"}') + released = await asyncio.wait_for(gated, timeout=1.0) + assert released == b'{"type":"response.done"}' + + +async def test_replay_exhausted_server_frames_raise_connection_closed(): + session = WsSession(frames=(_server("only", 0),)) + _, on_error = _collect_errors() + conn = ReplayConnection(session, timeout=1.0, on_error=on_error) + + await conn.recv(decode=False) + with pytest.raises(ConnectionClosedOK): + await conn.recv(decode=False) + + +async def test_replay_timeout_raises_instead_of_hanging(): + session = WsSession( + frames=( + _server('{"type":"session.created"}', 0), + _server('{"type":"response.done"}', 5), + ) + ) + errors, on_error = _collect_errors() + conn = ReplayConnection(session, timeout=0.15, on_error=on_error) + + await conn.recv(decode=False) + with pytest.raises(WsVcrReplayTimeout): + await asyncio.wait_for(conn.recv(decode=False), timeout=2.0) + assert errors and isinstance(errors[0], WsVcrReplayTimeout) + + +async def test_replay_accepts_client_frame_with_volatile_id_drift(): + recorded_client = _client('{"type":"conversation.item.create","item":{"id":"item_ABC12345","role":"user"}}') + session = WsSession(frames=(_server("s", 0), recorded_client, _server("done", 1))) + errors, on_error = _collect_errors() + conn = ReplayConnection(session, timeout=1.0, on_error=on_error) + + await conn.recv(decode=False) + await conn.send('{"type":"conversation.item.create","item":{"role":"user","id":"item_ZZ99887766"}}') + + assert errors == [] + assert await conn.recv(decode=False) == b"done" + + +async def test_replay_rejects_structurally_different_client_frame(): + session = WsSession(frames=(_server("s", 0), _client('{"type":"response.create"}'), _server("done", 1))) + errors, on_error = _collect_errors() + conn = ReplayConnection(session, timeout=1.0, on_error=on_error) + + await conn.recv(decode=False) + with pytest.raises(WsVcrContractDrift): + await conn.send('{"type":"session.update","session":{"voice":"alloy"}}') + assert errors and isinstance(errors[0], WsVcrContractDrift) + + +async def test_replay_rejects_extra_client_frame_beyond_recording(): + session = WsSession(frames=(_server("s", 0), _client('{"type":"response.create"}'))) + errors, on_error = _collect_errors() + conn = ReplayConnection(session, timeout=1.0, on_error=on_error) + + await conn.send('{"type":"response.create"}') + with pytest.raises(WsVcrContractDrift): + await conn.send('{"type":"response.create"}') + assert errors + + +def test_text_frames_match_normalizes_ids_and_timestamps_but_not_structure(): + assert text_frames_match( + '{"type":"x","event_id":"evt_111","ts":"2026-05-25T03:40:37.262045Z"}', + '{"type":"x","event_id":"evt_999","ts":"2026-06-01T10:00:00Z"}', + ) + assert not text_frames_match('{"type":"x","text":"hi"}', '{"type":"x","text":"bye"}') + assert not text_frames_match('{"type":"x"}', '{"type":"x","extra":1}') + + +def test_scrub_secrets_removes_auth_material(): + scrubbed = scrub_secrets("Authorization: Bearer sk-abcdef123456 and key xai-zzz99988877 raw sk-plainkey123") + assert "sk-abcdef123456" not in scrubbed + assert "xai-zzz99988877" not in scrubbed + assert "sk-plainkey123" not in scrubbed + assert "Bearer " in scrubbed + + +def test_recorder_scrubs_secrets_in_stored_frames(): + recorder = WsSessionRecorder() + recorder.record_client_frame('{"authorization":"Bearer sk-supersecretvalue"}') + stored = recorder.to_session().frames[0].text + assert stored is not None + assert "sk-supersecretvalue" not in stored + + +def _sample_cassette() -> WsCassette: + return WsCassette(sessions=(WsSession(frames=(_server('{"type":"session.created"}', 0),)),)) + + +def test_save_sets_24h_ttl_and_load_roundtrips(): + fake = fakeredis.FakeStrictRedis() + key = ws_redis_key_for("tests/llm_translation/realtime/test_x.py::test_y") + + assert save_ws_cassette(fake, key, _sample_cassette(), passed=True) is True + + ttl = fake.ttl(key) + assert CASSETTE_TTL_SECONDS - 5 <= ttl <= CASSETTE_TTL_SECONDS + loaded = load_ws_cassette(fake, key) + assert loaded == _sample_cassette() + + +def test_save_skipped_when_test_failed_leaves_no_key(): + fake = fakeredis.FakeStrictRedis() + key = ws_redis_key_for("tests/llm_translation/realtime/test_x.py::test_fail") + + assert save_ws_cassette(fake, key, _sample_cassette(), passed=False) is False + assert fake.get(key) is None + + +def test_save_skipped_when_test_failed_preserves_prior_cassette(): + fake = fakeredis.FakeStrictRedis() + key = ws_redis_key_for("tests/llm_translation/realtime/test_x.py::test_keep") + + save_ws_cassette(fake, key, _sample_cassette(), passed=True) + newer = WsCassette(sessions=(WsSession(frames=(_server('{"type":"other"}', 0),)),)) + + assert save_ws_cassette(fake, key, newer, passed=False) is False + assert load_ws_cassette(fake, key) == _sample_cassette() + + +def test_load_missing_key_returns_none(): + fake = fakeredis.FakeStrictRedis() + assert load_ws_cassette(fake, ws_redis_key_for("never/recorded")) is None + + +def test_ws_redis_key_uses_distinct_prefix(): + key = ws_redis_key_for("tests/llm_translation/realtime/test_x.py::TestY::test_z") + assert key.startswith("litellm:vcr:wscassette:") + assert "::" not in key + + +def test_build_ws_cassette_client_warns_and_counts_failure_instead_of_silently_disabling(): + def _broken_builder() -> RedisLike: + raise ValueError("invalid CASSETTE_REDIS_URL") + + failures_before = cassette_cache_health()["load_failures"] + with pytest.warns(VCRCassetteCacheWarning, match="fall back to live websocket traffic"): + assert build_ws_cassette_client(builder=_broken_builder) is None + assert cassette_cache_health()["load_failures"] == failures_before + 1 + + +def test_build_ws_cassette_client_returns_built_client_without_warning(): + fake = fakeredis.FakeStrictRedis() + with warnings.catch_warnings(): + warnings.simplefilter("error", VCRCassetteCacheWarning) + assert build_ws_cassette_client(builder=lambda: fake) is fake diff --git a/tests/local_testing/test_amazing_vertex_completion.py b/tests/local_testing/test_amazing_vertex_completion.py index 2382b8a5197..6e31166ad99 100644 --- a/tests/local_testing/test_amazing_vertex_completion.py +++ b/tests/local_testing/test_amazing_vertex_completion.py @@ -355,7 +355,11 @@ async def test_async_vertexai_response_basic(): user_message = "Hello, how are you?" messages = [{"content": user_message, "role": "user"}] response = await acompletion( - model="gemini-2.5-flash", messages=messages, temperature=0.7, timeout=5 + model="gemini-3.5-flash", + messages=messages, + temperature=0.7, + timeout=5, + vertex_location="global", ) print(f"response: {response}") except litellm.NotFoundError as e: @@ -388,7 +392,7 @@ async def test_async_vertexai_streaming_response(): ) test_models = random.sample(list(test_models), 1) test_models += list(litellm.vertex_language_models) # always test gemini-pro - test_models = ["gemini-2.5-flash"] + test_models = ["gemini-3.5-flash"] for model in test_models: if model in VERTEX_MODELS_TO_NOT_TEST or ( "gecko" in model @@ -412,6 +416,7 @@ async def test_async_vertexai_streaming_response(): temperature=0.7, timeout=5, stream=True, + vertex_location="global", ) print(f"response: {response}") complete_response: str = "" @@ -3840,10 +3845,11 @@ def test_vertex_schema_test(): } response = litellm.completion( - model="vertex_ai/gemini-2.5-flash", + model="vertex_ai/gemini-3.5-flash", messages=[{"role": "user", "content": "call the tool"}], tools=[tool], tool_choice="required", + vertex_location="global", ) print(response) @@ -3895,10 +3901,11 @@ def test_gemini_nullable_object_tool_schema_httpx(): ] response = litellm.completion( - model="vertex_ai/gemini-2.5-flash", + model="vertex_ai/gemini-3.5-flash", messages=[{"role": "user", "content": "call the tool"}], tools=tools, tool_choice="required", + vertex_location="global", ) print(response) diff --git a/tests/local_testing/test_cost_calc.py b/tests/local_testing/test_cost_calc.py index ab4d44d2240..3623af59848 100644 --- a/tests/local_testing/test_cost_calc.py +++ b/tests/local_testing/test_cost_calc.py @@ -101,7 +101,16 @@ def test_run(model: str): pytest.skip( "LLM API returning inconsistent usage" ) # handles transient openai errors - streaming_cost_calc = completion_cost(response) * 100 + streaming_cost_calc = ( + completion_cost( + response, + custom_cost_per_token={ + "input_cost_per_token": kwargs["input_cost_per_token"], + "output_cost_per_token": kwargs["output_cost_per_token"], + }, + ) + * 100 + ) print(f"Stream output : {output}") print(f"Stream usage : {response.usage}") # type: ignore diff --git a/tests/local_testing/test_dynamic_rate_limit_handler.py b/tests/local_testing/test_dynamic_rate_limit_handler.py index ff540e22e7a..d288d622cfa 100644 --- a/tests/local_testing/test_dynamic_rate_limit_handler.py +++ b/tests/local_testing/test_dynamic_rate_limit_handler.py @@ -7,7 +7,7 @@ import sys import time import traceback from litellm._uuid import uuid -from datetime import datetime +from datetime import datetime, timezone from typing import Optional, Tuple from dotenv import load_dotenv @@ -38,7 +38,8 @@ Basic test cases: @pytest.fixture def dynamic_rate_limit_handler() -> DynamicRateLimitHandler: internal_cache = DualCache() - return DynamicRateLimitHandler(internal_usage_cache=internal_cache) + frozen_now = datetime(2024, 1, 1, 10, 30, 0, tzinfo=timezone.utc) + return DynamicRateLimitHandler(internal_usage_cache=internal_cache, time_fn=lambda: frozen_now) @pytest.fixture diff --git a/tests/local_testing/test_embedding.py b/tests/local_testing/test_embedding.py index 14626aa8e45..f4c61e99547 100644 --- a/tests/local_testing/test_embedding.py +++ b/tests/local_testing/test_embedding.py @@ -765,6 +765,10 @@ def test_fireworks_embeddings(): pass except litellm.InternalServerError as e: pass + except litellm.APIError as e: + if "suspended" in str(e): + pytest.skip(f"Fireworks account suspended: {e}") + pytest.fail(f"Error occurred: {e}") except Exception as e: pytest.fail(f"Error occurred: {e}") diff --git a/tests/local_testing/test_get_llm_provider.py b/tests/local_testing/test_get_llm_provider.py index 9ecb639fc3a..4c3e13da17a 100644 --- a/tests/local_testing/test_get_llm_provider.py +++ b/tests/local_testing/test_get_llm_provider.py @@ -509,10 +509,10 @@ def shipped_generalizations(): class TestClaudeModelPatternMatching: """ - The ``anthropic-claude`` fallback generalization rule routes future Claude - models to the Anthropic provider without requiring a + The ``anthropic-claude-ids`` fallback generalization routing rule routes future + Claude models to the Anthropic provider without requiring a model_prices_and_context_window.json entry. These tests exercise the rule - end-to-end through ``get_llm_provider`` and ``match_fallback_generalization``. + end-to-end through ``get_llm_provider`` and ``match_routing_generalization``. """ @pytest.mark.parametrize( @@ -556,10 +556,10 @@ class TestClaudeModelPatternMatching: self, model, shipped_generalizations ): from litellm.litellm_core_utils.fallback_generalizations import ( - match_fallback_generalization, + match_routing_generalization, ) - assert match_fallback_generalization(model) is None + assert match_routing_generalization(model) is None def test_routing_comes_from_the_rule_not_python(self, shipped_generalizations): """With the rule cleared, an unknown claude must no longer route to diff --git a/tests/local_testing/test_pass_through_endpoints.py b/tests/local_testing/test_pass_through_endpoints.py index eeb29dea531..793a60efc3f 100644 --- a/tests/local_testing/test_pass_through_endpoints.py +++ b/tests/local_testing/test_pass_through_endpoints.py @@ -22,10 +22,8 @@ from litellm.proxy.proxy_server import initialize_pass_through_endpoints # Mock the async_client used in the pass_through_request function -async def mock_request(*args, **kwargs): - mock_response = httpx.Response(200, json={"message": "Mocked response"}) - mock_response.request = Mock(spec=httpx.Request) - return mock_response +async def mock_request(self, request, **kwargs): + return httpx.Response(200, json={"message": "Mocked response"}, request=request) def remove_rerank_route(app): @@ -49,8 +47,8 @@ def client(): @pytest.mark.asyncio async def test_pass_through_endpoint_no_headers(client, monkeypatch): - # Mock the httpx.AsyncClient.request method - monkeypatch.setattr("httpx.AsyncClient.request", mock_request) + # Mock the httpx.AsyncClient.send method + monkeypatch.setattr("httpx.AsyncClient.send", mock_request) import litellm # Define a pass-through endpoint @@ -79,8 +77,8 @@ async def test_pass_through_endpoint_no_headers(client, monkeypatch): @pytest.mark.asyncio async def test_pass_through_endpoint(client, monkeypatch): - # Mock the httpx.AsyncClient.request method - monkeypatch.setattr("httpx.AsyncClient.request", mock_request) + # Mock the httpx.AsyncClient.send method + monkeypatch.setattr("httpx.AsyncClient.send", mock_request) import litellm # Define a pass-through endpoint @@ -181,7 +179,7 @@ async def test_pass_through_endpoint_rpm_limit( expected_status_codes, num_users, ): - monkeypatch.setattr("httpx.AsyncClient.request", mock_request) + monkeypatch.setattr("httpx.AsyncClient.send", mock_request) import litellm from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.proxy_server import ProxyLogging, hash_token, user_api_key_cache @@ -285,7 +283,7 @@ async def test_pass_through_endpoint_rpm_limit( async def test_pass_through_endpoint_sequential_rpm_limit( client, monkeypatch, auth, rpm_limit, requests_to_make, expected_status_codes ): - monkeypatch.setattr("httpx.AsyncClient.request", mock_request) + monkeypatch.setattr("httpx.AsyncClient.send", mock_request) import litellm from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.proxy_server import ProxyLogging, hash_token, user_api_key_cache @@ -504,10 +502,10 @@ async def test_pass_through_endpoint_bing(client, monkeypatch): captured_requests = [] - async def mock_bing_request(*args, **kwargs): + async def mock_bing_request(self, request, **kwargs): - captured_requests.append((args, kwargs)) - mock_response = httpx.Response( + captured_requests.append(request) + return httpx.Response( 200, json={ "_type": "SearchResponse", @@ -518,11 +516,10 @@ async def test_pass_through_endpoint_bing(client, monkeypatch): "value": [], }, }, + request=request, ) - mock_response.request = Mock(spec=httpx.Request) - return mock_response - monkeypatch.setattr("httpx.AsyncClient.request", mock_bing_request) + monkeypatch.setattr("httpx.AsyncClient.send", mock_bing_request) # Define a pass-through endpoint pass_through_endpoints = [ @@ -555,8 +552,8 @@ async def test_pass_through_endpoint_bing(client, monkeypatch): client.get("/bing/search?q=bob+barker") client.get("/bing/search-no-merge-params?q=bob+barker") - first_transformed_url = captured_requests[0][1]["url"] - second_transformed_url = captured_requests[1][1]["url"] + first_transformed_url = captured_requests[0].url + second_transformed_url = captured_requests[1].url # Parse URLs to compare query params order-independently # Parse first URL @@ -573,7 +570,7 @@ async def test_pass_through_endpoint_bing(client, monkeypatch): "setLang": ["en-US"], "mkt": ["en-US"], } - expected_second_params = {"setLang": ["en-US"], "mkt": ["en-US"]} + expected_second_params = {"q": ["bob barker"]} # Assert the response - compare base URL and params separately assert ( diff --git a/tests/local_testing/test_router_fallbacks.py b/tests/local_testing/test_router_fallbacks.py index 6547a3eb663..7c09c978029 100644 --- a/tests/local_testing/test_router_fallbacks.py +++ b/tests/local_testing/test_router_fallbacks.py @@ -1330,6 +1330,8 @@ def test_router_fallbacks_with_custom_model_costs(): Goal: make sure custom model doesn't override default model costs. """ + default_model_info = litellm.get_model_info(model="claude-sonnet-4-5-20250929") + model_list = [ { "model_name": "claude-sonnet-4-5-20250929", @@ -1383,8 +1385,8 @@ def test_router_fallbacks_with_custom_model_costs(): print(f"key: {model_info['key']}") - assert model_info["input_cost_per_token"] == 30 - assert model_info["output_cost_per_token"] == 60 + assert model_info["input_cost_per_token"] == default_model_info["input_cost_per_token"] + assert model_info["output_cost_per_token"] == default_model_info["output_cost_per_token"] @pytest.mark.parametrize("sync_mode", [True, False]) diff --git a/tests/logging_callback_tests/test_logging_redaction_e2e_test.py b/tests/logging_callback_tests/test_logging_redaction_e2e_test.py index 3f4b446bea5..891e5020f37 100644 --- a/tests/logging_callback_tests/test_logging_redaction_e2e_test.py +++ b/tests/logging_callback_tests/test_logging_redaction_e2e_test.py @@ -56,69 +56,56 @@ async def test_global_redaction_on(): ) -@pytest.mark.parametrize("turn_off_message_logging", [True, False]) +@pytest.mark.parametrize( + "dynamic_turn_off, expect_redacted", + [(True, True), (False, False)], +) @pytest.mark.asyncio -async def test_global_redaction_ignores_dynamic_param(turn_off_message_logging): - """ - Request-body `turn_off_message_logging` is no longer honored as a dynamic - callback param — global setting (or admin-configured key/team config) wins. - With global redaction ON, the caller cannot disable redaction via the - request body. - """ +async def test_dynamic_turn_off_message_logging_overrides_global_on(dynamic_turn_off, expect_redacted): litellm.turn_off_message_logging = True test_custom_logger = TestCustomLogger() litellm.callbacks = [test_custom_logger] - response = await litellm.acompletion( + await litellm.acompletion( model="gpt-5-mini", messages=[{"role": "user", "content": "hi"}], - turn_off_message_logging=turn_off_message_logging, + turn_off_message_logging=dynamic_turn_off, mock_response="hello", ) await asyncio.sleep(1) standard_logging_payload = test_custom_logger.logged_standard_logging_payload assert standard_logging_payload is not None - print( - "logged standard logging payload", - json.dumps(standard_logging_payload, indent=2), - ) - response = standard_logging_payload["response"] - assert response["choices"][0]["message"]["content"] == "redacted-by-litellm" - assert standard_logging_payload["messages"][0]["content"] == "redacted-by-litellm" + expected_response_content = "redacted-by-litellm" if expect_redacted else "hello" + expected_message_content = "redacted-by-litellm" if expect_redacted else "hi" + assert standard_logging_payload["response"]["choices"][0]["message"]["content"] == expected_response_content + assert standard_logging_payload["messages"][0]["content"] == expected_message_content -@pytest.mark.parametrize("turn_off_message_logging", [True, False]) +@pytest.mark.parametrize( + "dynamic_turn_off, expect_redacted", + [(True, True), (False, False)], +) @pytest.mark.asyncio -async def test_global_redaction_off_ignores_dynamic_param(turn_off_message_logging): - """ - Request-body `turn_off_message_logging` is no longer honored as a dynamic - callback param — global setting (or admin-configured key/team config) wins. - With global redaction OFF, the caller cannot enable redaction via the - request body. - """ +async def test_dynamic_turn_off_message_logging_overrides_global_off(dynamic_turn_off, expect_redacted): litellm.turn_off_message_logging = False test_custom_logger = TestCustomLogger() litellm.callbacks = [test_custom_logger] - response = await litellm.acompletion( + await litellm.acompletion( model="gpt-5-mini", messages=[{"role": "user", "content": "hi"}], - turn_off_message_logging=turn_off_message_logging, + turn_off_message_logging=dynamic_turn_off, mock_response="hello", ) await asyncio.sleep(1) standard_logging_payload = test_custom_logger.logged_standard_logging_payload assert standard_logging_payload is not None - print( - "logged standard logging payload", - json.dumps(standard_logging_payload, indent=2), - ) - assert ( - standard_logging_payload["response"]["choices"][0]["message"]["content"] - == "hello" - ) - assert standard_logging_payload["messages"][0]["content"] == "hi" + + expected_response_content = "redacted-by-litellm" if expect_redacted else "hello" + expected_message_content = "redacted-by-litellm" if expect_redacted else "hi" + assert standard_logging_payload["response"]["choices"][0]["message"]["content"] == expected_response_content + assert standard_logging_payload["messages"][0]["content"] == expected_message_content @pytest.mark.asyncio diff --git a/tests/logging_callback_tests/test_standard_logging_payload.py b/tests/logging_callback_tests/test_standard_logging_payload.py index 36215ca9c6b..f29b245b3be 100644 --- a/tests/logging_callback_tests/test_standard_logging_payload.py +++ b/tests/logging_callback_tests/test_standard_logging_payload.py @@ -335,6 +335,74 @@ def test_get_model_cost_information(): ) +def test_get_model_cost_information_custom_pricing_uses_base_model(): + result = StandardLoggingPayloadSetup.get_model_cost_information( + base_model="bedrock/invoke/global.anthropic.claude-opus-4-6-v1", + custom_pricing=True, + custom_llm_provider="bedrock", + init_response_obj={"model": "invoke_test_claude"}, + ) + assert result["model_map_value"] is not None + assert result["model_map_key"] != "invoke_test_claude" + + +def test_standard_logging_payload_uses_deployment_when_no_base_model(): + """metadata["deployment"] is used for cost-map lookup when base_model is not set.""" + from datetime import datetime + + from litellm.litellm_core_utils.litellm_logging import ( + Logging, + get_standard_logging_object_payload, + ) + + logging_obj = Logging( + model="invoke_test_claude", + messages=[{"role": "user", "content": "hi"}], + stream=False, + call_type="completion", + start_time=datetime.now(), + litellm_call_id="test-deploy-fallback", + function_id="test-fn", + ) + + kwargs = { + "model": "invoke_test_claude", + "messages": [{"role": "user", "content": "hi"}], + "custom_llm_provider": "bedrock", + "litellm_params": { + "metadata": { + "deployment": "bedrock/invoke/global.anthropic.claude-opus-4-6-v1", + }, + }, + } + mock_response = { + "id": "chatcmpl-deploy-test", + "object": "chat.completion", + "model": "invoke_test_claude", + "usage": {"prompt_tokens": 5, "completion_tokens": 10, "total_tokens": 15}, + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "hello"}, + "finish_reason": "stop", + } + ], + } + + payload = get_standard_logging_object_payload( + kwargs=kwargs, + init_response_obj=mock_response, + start_time=datetime.now(), + end_time=datetime.now(), + logging_obj=logging_obj, + status="success", + ) + + assert payload is not None + assert payload["model_map_information"]["model_map_value"] is not None + assert payload["model_map_information"]["model_map_key"] != "invoke_test_claude" + + def test_get_hidden_params(): """Test get_hidden_params with different inputs""" # Test with None diff --git a/tests/mcp_tests/test_mcp_server.py b/tests/mcp_tests/test_mcp_server.py index bb13a7ce8cc..515bf1233aa 100644 --- a/tests/mcp_tests/test_mcp_server.py +++ b/tests/mcp_tests/test_mcp_server.py @@ -955,6 +955,7 @@ async def test_get_tools_from_mcp_servers(): add_prefix=False, raw_headers=None, user_api_key_auth=None, + oauth2_headers=None, ): if server.server_id == "server1_id": return [mock_tool_1] @@ -1555,6 +1556,10 @@ async def test_add_update_server_with_alias(): mock_mcp_server.registration_url = None mock_mcp_server.token_url = None mock_mcp_server.oauth2_flow = None + mock_mcp_server.token_exchange_endpoint = None + mock_mcp_server.audience = None + mock_mcp_server.subject_token_type = None + mock_mcp_server.token_exchange_profile = None # Additional fields used by build_mcp_server_from_table mock_mcp_server.extra_headers = None mock_mcp_server.allow_all_keys = False @@ -1614,6 +1619,10 @@ async def test_add_update_server_without_alias(): mock_mcp_server.registration_url = None mock_mcp_server.token_url = None mock_mcp_server.oauth2_flow = None + mock_mcp_server.token_exchange_endpoint = None + mock_mcp_server.audience = None + mock_mcp_server.subject_token_type = None + mock_mcp_server.token_exchange_profile = None # Additional fields used by build_mcp_server_from_table mock_mcp_server.extra_headers = None mock_mcp_server.allow_all_keys = False @@ -1673,6 +1682,10 @@ async def test_add_update_server_fallback_to_server_id(): mock_mcp_server.registration_url = None mock_mcp_server.token_url = None mock_mcp_server.oauth2_flow = None + mock_mcp_server.token_exchange_endpoint = None + mock_mcp_server.audience = None + mock_mcp_server.subject_token_type = None + mock_mcp_server.token_exchange_profile = None # Additional fields used by build_mcp_server_from_table - set explicitly # to avoid MagicMock objects being passed to Pydantic MCPServer constructor mock_mcp_server.extra_headers = None diff --git a/tests/ocr_tests/test_ocr_azure_document_intelligence.py b/tests/ocr_tests/test_ocr_azure_document_intelligence.py index 7269890b7b6..09c21842ad7 100644 --- a/tests/ocr_tests/test_ocr_azure_document_intelligence.py +++ b/tests/ocr_tests/test_ocr_azure_document_intelligence.py @@ -61,8 +61,8 @@ class TestAzureDocumentIntelligencePagesParam: def cfg(self) -> AzureDocumentIntelligenceOCRConfig: return AzureDocumentIntelligenceOCRConfig() - def test_get_supported_ocr_params_includes_pages(self, cfg): - assert cfg.get_supported_ocr_params("prebuilt-layout") == ["pages"] + def test_get_supported_ocr_params_includes_pages_and_features(self, cfg): + assert cfg.get_supported_ocr_params("prebuilt-layout") == ["pages", "features"] def test_map_ocr_params_mistral_zero_based_int_list(self, cfg): mapped = cfg.map_ocr_params({"pages": [0, 1, 2]}, {}, "prebuilt-layout") diff --git a/tests/pass_through_tests/test_vertex_ai.py b/tests/pass_through_tests/test_vertex_ai.py index e8223f2219c..35cb5f49c56 100644 --- a/tests/pass_through_tests/test_vertex_ai.py +++ b/tests/pass_through_tests/test_vertex_ai.py @@ -11,6 +11,7 @@ import json import os import pytest import asyncio +import requests # Path to your service account JSON file SERVICE_ACCOUNT_FILE = "path/to/your/service-account.json" @@ -57,98 +58,114 @@ def load_vertex_ai_credentials(): os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = os.path.abspath(temp_file.name) -async def call_spend_logs_endpoint(): - """ - Call this - curl -X GET "http://0.0.0.0:4000/spend/logs" -H "Authorization: Bearer sk-1234" - """ - import datetime - import requests - - todays_date = datetime.datetime.now().strftime("%Y-%m-%d") - url = f"http://0.0.0.0:4000/global/spend/logs?api_key=best-api-key-ever" - headers = {"Authorization": f"Bearer sk-1234"} - response = requests.get(url, headers=headers) - print("response from call_spend_logs_endpoint", response) - - if response.status_code != 200: - print(f"spend logs endpoint returned {response.status_code}: {response.text}") - return None - - json_response = response.json() - - # get spend for today - """ - json response looks like this - - [{'date': '2024-08-30', 'spend': 0.00016600000000000002, 'api_key': 'best-api-key-ever'}] - """ - print("json_response", json_response) - - todays_date = datetime.datetime.now().strftime("%Y-%m-%d") - for spend_log in json_response: - if spend_log["date"] == todays_date: - return spend_log["spend"] - - LITE_LLM_ENDPOINT = "http://localhost:4000" +SPEND_LOG_API_KEY = "best-api-key-ever" -def _is_vertex_quota_error(exc: Exception) -> bool: - message = str(exc) - return ( - "429" in message - or "Too Many Requests" in message - or "RESOURCE_EXHAUSTED" in message + +def get_tracked_spend() -> float: + """ + Total spend recorded under the pass-through key in the global spend view. + + Sums every day the endpoint returns instead of matching the runner's local + "today" so a UTC date rollover mid-test can't hide a freshly billed call, and + treats an unreachable endpoint as "nothing recorded yet" (0.0). + """ + url = f"{LITE_LLM_ENDPOINT}/global/spend/logs?api_key={SPEND_LOG_API_KEY}" + response = requests.get(url, headers={"Authorization": "Bearer sk-1234"}) + if response.status_code != 200: + print(f"global spend logs endpoint returned {response.status_code}: {response.text}") + return 0.0 + + rows = response.json() + print("global spend logs rows", rows) + return sum(float(row.get("spend") or 0.0) for row in rows) + + +VERTEX_PROJECT = "litellm-ci-cd" +VERTEX_MODEL = "gemini-3.1-flash-lite" +VERTEX_GENERATE_CONTENT_URL = ( + f"{LITE_LLM_ENDPOINT}/vertex_ai/v1/projects/{VERTEX_PROJECT}" + f"/locations/global/publishers/google/models/{VERTEX_MODEL}:generateContent" +) + + +def _vertex_access_token() -> str: + import google.auth + import google.auth.transport.requests + + credentials, _ = google.auth.default( + scopes=["https://www.googleapis.com/auth/cloud-platform"] ) + credentials.refresh(google.auth.transport.requests.Request()) + return credentials.token + + +def _spend_log_for_request(call_id: str) -> dict | None: + response = requests.get( + f"{LITE_LLM_ENDPOINT}/spend/logs?request_id={call_id}", + headers={"Authorization": "Bearer sk-1234"}, + timeout=30, + ) + if response.status_code != 200: + return None + rows = response.json() + return rows[0] if rows else None + + +def _is_vertex_quota_error(response: requests.Response) -> bool: + return response.status_code == 429 or "RESOURCE_EXHAUSTED" in response.text @pytest.mark.asyncio() async def test_basic_vertex_ai_pass_through_with_spendlog(): - - spend_before = await call_spend_logs_endpoint() or 0.0 load_vertex_ai_credentials() + access_token = _vertex_access_token() - vertexai.init( - project="litellm-ci-cd", - location="global", - api_endpoint=f"{LITE_LLM_ENDPOINT}/vertex_ai", - api_transport="rest", - ) + # Drive the pass-through over HTTP instead of the vertexai SDK: the SDK intermittently + # routes generateContent to the public Vertex endpoint rather than the proxy override, + # so the call never reaches LiteLLM and no spend is logged. A direct request always + # hits the proxy. Spend logging then runs on a best-effort background worker that can + # drop a single event, so retry a few billed calls and assert that one specific call's + # spend log lands. Failing every attempt still fails hard, which is the signal we want + # if cost tracking is broken. + max_attempts = 3 + poll_seconds = 60 + poll_interval = 5 - model = GenerativeModel(model_name="gemini-3.1-flash-lite") - try: - response = model.generate_content("hi") - except Exception as exc: - if _is_vertex_quota_error(exc): + for attempt in range(1, max_attempts + 1): + response = requests.post( + VERTEX_GENERATE_CONTENT_URL, + headers={ + "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json", + }, + json={"contents": [{"role": "user", "parts": [{"text": "hi"}]}]}, + timeout=60, + ) + if _is_vertex_quota_error(response): pytest.skip("Vertex AI quota exhausted") - raise + assert ( + response.status_code == 200 + ), f"vertex pass-through call failed: {response.status_code} {response.text}" - print("response", response) + call_id = response.headers.get("x-litellm-call-id") + assert call_id, "proxy response missing x-litellm-call-id header" - # Spend logging is async/batched and can lag under CI load, so poll instead of - # sleeping a fixed amount. A transient empty read is skipped, not counted as 0.0 - # spend, which would spuriously fail the assertion on an otherwise-billed call. - max_wait = 240 # total seconds to wait - poll_interval = 10 # seconds between checks - elapsed = 0 - spend_after = spend_before - while elapsed < max_wait: - await asyncio.sleep(poll_interval) - elapsed += poll_interval - latest_spend = await call_spend_logs_endpoint() - if latest_spend is None: - print(f"spend logs unavailable (elapsed={elapsed}s), retrying") - continue - spend_after = latest_spend - print(f"spend_after (elapsed={elapsed}s)", spend_after) - if spend_after > spend_before: - break + for _ in range(poll_seconds // poll_interval): + await asyncio.sleep(poll_interval) + row = _spend_log_for_request(call_id) + if row is not None and float(row.get("spend") or 0) > 0: + assert "gemini" in row["model"], f"unexpected model in spend log: {row}" + assert ( + row["custom_llm_provider"] == "vertex_ai" + ), f"unexpected provider in spend log: {row}" + return - assert ( - spend_after > spend_before - ), "Spend should be greater than before after {}s. spend_before: {}, spend_after: {}".format( - elapsed, spend_before, spend_after + print(f"attempt {attempt}: spend log for call {call_id} not found yet, re-billing") + + pytest.fail( + f"Vertex pass-through spend never recorded after {max_attempts} billed calls" ) @@ -156,7 +173,7 @@ async def test_basic_vertex_ai_pass_through_with_spendlog(): @pytest.mark.skip(reason="skip flaky test - vertex pass through streaming is flaky") async def test_basic_vertex_ai_pass_through_streaming_with_spendlog(): - spend_before = await call_spend_logs_endpoint() or 0.0 + spend_before = get_tracked_spend() print("spend_before", spend_before) load_vertex_ai_credentials() @@ -176,7 +193,7 @@ async def test_basic_vertex_ai_pass_through_streaming_with_spendlog(): print("response", response) await asyncio.sleep(20) - spend_after = await call_spend_logs_endpoint() + spend_after = get_tracked_spend() print("spend_after", spend_after) assert ( spend_after > spend_before diff --git a/tests/pass_through_unit_tests/test_unit_test_streaming.py b/tests/pass_through_unit_tests/test_unit_test_streaming.py index 63965320f2b..ed98b720b37 100644 --- a/tests/pass_through_unit_tests/test_unit_test_streaming.py +++ b/tests/pass_through_unit_tests/test_unit_test_streaming.py @@ -50,6 +50,7 @@ async def test_chunk_processor_yields_raw_bytes(endpoint_type, url_route): """ # Mock inputs response = AsyncMock(spec=httpx.Response) + response.status_code = 200 raw_chunks = [ b'{"id": "1", "content": "Hello"}', b'{"id": "2", "content": "World"}', diff --git a/tests/pass_through_unit_tests/test_vertex_ai_anthropic_streaming_cost_injection.py b/tests/pass_through_unit_tests/test_vertex_ai_anthropic_streaming_cost_injection.py index 3e94eb3a4f5..ac754aefaea 100644 --- a/tests/pass_through_unit_tests/test_vertex_ai_anthropic_streaming_cost_injection.py +++ b/tests/pass_through_unit_tests/test_vertex_ai_anthropic_streaming_cost_injection.py @@ -39,6 +39,7 @@ async def test_vertex_ai_anthropic_streaming_cost_injection_enabled(): try: # Mock response with Anthropic SSE format chunks response = AsyncMock(spec=httpx.Response) + response.status_code = 200 # Create chunks with message_delta event containing usage chunks_with_usage = [ @@ -56,6 +57,7 @@ async def test_vertex_ai_anthropic_streaming_cost_injection_enabled(): # Setup logging object with model info litellm_logging_obj = MagicMock(spec=LiteLLMLoggingObj) litellm_logging_obj.model_call_details = {"model": "claude-sonnet-4@20250514"} + litellm_logging_obj.completion_start_time = None litellm_logging_obj.async_success_handler = AsyncMock() request_body = {"model": "claude-sonnet-4@20250514"} @@ -120,6 +122,7 @@ async def test_vertex_ai_anthropic_streaming_cost_injection_disabled(): try: # Mock response with Anthropic SSE format chunks response = AsyncMock(spec=httpx.Response) + response.status_code = 200 chunks_with_usage = [ b'data: {"type": "message_delta", "usage": {"input_tokens": 10, "output_tokens": 5}}\n\n', @@ -133,6 +136,7 @@ async def test_vertex_ai_anthropic_streaming_cost_injection_disabled(): litellm_logging_obj = MagicMock(spec=LiteLLMLoggingObj) litellm_logging_obj.model_call_details = {"model": "claude-sonnet-4@20250514"} + litellm_logging_obj.completion_start_time = None litellm_logging_obj.async_success_handler = AsyncMock() request_body = {"model": "claude-sonnet-4@20250514"} @@ -178,6 +182,7 @@ async def test_vertex_ai_anthropic_streaming_cost_injection_no_usage_chunk(): try: response = AsyncMock(spec=httpx.Response) + response.status_code = 200 # Chunks without usage (should not be modified) chunks_without_usage = [ @@ -193,6 +198,7 @@ async def test_vertex_ai_anthropic_streaming_cost_injection_no_usage_chunk(): litellm_logging_obj = MagicMock(spec=LiteLLMLoggingObj) litellm_logging_obj.model_call_details = {"model": "claude-sonnet-4@20250514"} + litellm_logging_obj.completion_start_time = None litellm_logging_obj.async_success_handler = AsyncMock() request_body = {"model": "claude-sonnet-4@20250514"} @@ -233,6 +239,7 @@ async def test_vertex_ai_anthropic_streaming_model_extraction(): try: response = AsyncMock(spec=httpx.Response) + response.status_code = 200 chunks = [ b'data: {"type": "message_delta", "usage": {"input_tokens": 10, "output_tokens": 5}}\n\n', @@ -246,6 +253,7 @@ async def test_vertex_ai_anthropic_streaming_model_extraction(): litellm_logging_obj = MagicMock(spec=LiteLLMLoggingObj) litellm_logging_obj.model_call_details = {} + litellm_logging_obj.completion_start_time = None litellm_logging_obj.async_success_handler = AsyncMock() # Test model extraction from request body diff --git a/tests/proxy_admin_ui_tests/test-results/.last-run.json b/tests/proxy_admin_ui_tests/test-results/.last-run.json new file mode 100644 index 00000000000..cbcc1fbac11 --- /dev/null +++ b/tests/proxy_admin_ui_tests/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "passed", + "failedTests": [] +} \ No newline at end of file diff --git a/tests/proxy_behavior/management/test_team_update.py b/tests/proxy_behavior/management/test_team_update.py index 9cb2b0fecda..23ea89fa74d 100644 --- a/tests/proxy_behavior/management/test_team_update.py +++ b/tests/proxy_behavior/management/test_team_update.py @@ -105,27 +105,35 @@ async def test_team_update_authz_matrix( assert row.team_alias != MARKER_ALIAS, "denied but team mutated" -async def test_team_update_requires_proxy_admin_without_org_context( +async def test_team_update_org_admin_resolved_from_team_without_org_context( proxy_client, prisma, scratch, world ): - """With no organization_id in the body the route gate has no org context - and falls back to proxy-admin-only: an org admin of the team's own org - is 401, PROXY_ADMIN is 200.""" + """With no organization_id in the body the route gate resolves the target + team's org from team_id, so an org admin of the team's own org is allowed + (200), same as PROXY_ADMIN. A team admin of that same team stays denied + (401): the resolution grants org admins access, not team admins.""" await _seed_target(prisma, world, "alpha", scratch.prefix) - denied = await proxy_client.post( + allowed_org_admin = await proxy_client.post( "/team/update", headers={"Authorization": f"Bearer {world.keys[Actor.ORG_ADMIN].cleartext}"}, json={"team_id": scratch.prefix, "team_alias": MARKER_ALIAS}, ) - assert denied.status_code == 401, denied.text + assert allowed_org_admin.status_code == 200, allowed_org_admin.text - allowed = await proxy_client.post( + allowed_proxy_admin = await proxy_client.post( "/team/update", headers={"Authorization": f"Bearer {world.keys[Actor.PROXY_ADMIN].cleartext}"}, json={"team_id": scratch.prefix, "team_alias": MARKER_ALIAS}, ) - assert allowed.status_code == 200, allowed.text + assert allowed_proxy_admin.status_code == 200, allowed_proxy_admin.text + + denied_team_admin = await proxy_client.post( + "/team/update", + headers={"Authorization": f"Bearer {world.keys[Actor.TEAM_ADMIN].cleartext}"}, + json={"team_id": scratch.prefix, "team_alias": MARKER_ALIAS}, + ) + assert denied_team_admin.status_code == 401, denied_team_admin.text # Relocation gate — moving a team to a different org. The scratch team starts diff --git a/tests/proxy_unit_tests/test_auth_checks.py b/tests/proxy_unit_tests/test_auth_checks.py index e7136ecb195..e58e6c9694b 100644 --- a/tests/proxy_unit_tests/test_auth_checks.py +++ b/tests/proxy_unit_tests/test_auth_checks.py @@ -236,6 +236,8 @@ async def test_can_team_call_model(model, expect_to_work): (["bedrock/*"], "bedrock/anthropic.claude-3-5-sonnet-20240620", True), (["bedrock/*"], "bedrockz/anthropic.claude-3-5-sonnet-20240620", False), (["bedrock/us.*"], "bedrock/us.amazon.nova-micro-v1:0", True), + (["openai/*"], "ft:gpt-4-0613", True), + (["openai/*"], "bedrockz/ft:gpt-4-0613", False), ], ) @pytest.mark.asyncio diff --git a/tests/proxy_unit_tests/test_check_batch_cost.py b/tests/proxy_unit_tests/test_check_batch_cost.py index e8acaf6fea6..f8f15f2e008 100644 --- a/tests/proxy_unit_tests/test_check_batch_cost.py +++ b/tests/proxy_unit_tests/test_check_batch_cost.py @@ -1,13 +1,35 @@ """ Unit tests for CheckBatchCost class. Covers: stale-row cleanup (file_purpose scoping), paginated find_many, -and the batch_processed-column fallback query. +the batch_processed-column fallback query, and routing of unmanaged +Vertex batches (raw gs:// input_file_id, no managed unified id). """ from unittest.mock import AsyncMock, MagicMock, patch import pytest +_IS_B64 = "litellm.proxy.openai_files_endpoints.common_utils._is_base64_encoded_unified_file_id" + + +def _unmanaged_vertex_file_object( + input_file_id="gs://bucket/litellm-vertex-files/publishers/google/models/gemini-2.5-flash/abc.jsonl", + status="validating", +): + """A LiteLLMBatch JSON blob shaped like what the managed-files hook stores for an + unmanaged Vertex batch (raw gs:// input_file_id).""" + from litellm.types.utils import LiteLLMBatch + + return LiteLLMBatch( + id="8823717160934178816", + completion_window="24h", + created_at=1, + endpoint="/v1/chat/completions", + input_file_id=input_file_id, + object="batch", + status=status, + ).model_dump_json() + class TestCheckBatchCost: """Test suite for CheckBatchCost class""" @@ -375,6 +397,142 @@ class TestCheckBatchCost: ), "update() must include batch_processed=True when column is present" assert update_data["status"] == "complete" + @pytest.mark.asyncio + async def test_cost_tracking_failure_leaves_job_unprocessed( + self, check_batch_cost_instance, mock_prisma_client, mock_llm_router + ): + """LIT-4008 regression: when fetching a completed batch's results fails + (e.g. Anthropic rejecting a msgbatch_ id on the Files API), the job must + NOT be marked complete/batch_processed. Pre-fix the $0 spend row was + written and batch_processed=True made it permanent; the failure must + instead leave the row untouched so the next poll retries, without + aborting the poll cycle. + """ + from unittest.mock import patch + + mock_prisma_client.db.litellm_managedobjecttable.update_many = AsyncMock( + return_value=0 + ) + mock_prisma_client.db.litellm_managedobjecttable.update = AsyncMock() + mock_prisma_client.db.litellm_usertable.find_unique = AsyncMock( + return_value=None + ) + + mock_job = MagicMock() + mock_job.id = "job-anthropic-1" + mock_job.unified_object_id = "dW5pZmllZF9iYXRjaF9pZA==" + mock_job.created_by = "user-1" + + mock_prisma_client.db.litellm_managedobjecttable.find_many = AsyncMock( + return_value=[mock_job] + ) + + mock_response = MagicMock() + mock_response.status = "completed" + mock_response.output_file_id = "msgbatch_01WA5hdsa2Xx8w4zyPjV1frs" + + mock_llm_router.aretrieve_batch = AsyncMock(return_value=mock_response) + mock_llm_router.get_deployment_credentials_with_provider = MagicMock( + return_value={"api_key": "sk-test", "custom_llm_provider": "anthropic"} + ) + + decoded_id = "llm_model_id,model-123;llm_batch_id,msgbatch_01WA5hdsa2Xx8w4zyPjV1frs;" + + with ( + patch( + "litellm.proxy.openai_files_endpoints.common_utils._is_base64_encoded_unified_file_id", + side_effect=[decoded_id, None], + ), + patch( + "litellm.proxy.openai_files_endpoints.common_utils.get_model_id_from_unified_batch_id", + return_value="model-123", + ), + patch( + "litellm.proxy.openai_files_endpoints.common_utils.get_batch_id_from_unified_batch_id", + return_value="msgbatch_01WA5hdsa2Xx8w4zyPjV1frs", + ), + patch( + "litellm.files.main.afile_content", + new_callable=AsyncMock, + side_effect=Exception("File id must have `file_` prefix."), + ), + ): + await check_batch_cost_instance.check_batch_cost() + + assert ( + mock_prisma_client.db.litellm_managedobjecttable.update.call_count == 0 + ), "a failed cost tracking attempt must not mark the job processed" + + @pytest.mark.asyncio + @pytest.mark.parametrize("terminal_status", ["failed", "expired", "cancelled"]) + async def test_terminal_status_marks_job_processed( + self, + check_batch_cost_instance, + mock_prisma_client, + mock_llm_router, + terminal_status, + ): + """When the provider reports a terminal status (failed/expired/cancelled), the row + must be written back with that status and batch_processed=True so it stops being + polled forever. + """ + from unittest.mock import patch + + mock_prisma_client.db.litellm_managedobjecttable.update_many = AsyncMock( + return_value=0 + ) + mock_prisma_client.db.litellm_managedobjecttable.update = AsyncMock() + mock_prisma_client.db.litellm_usertable.find_unique = AsyncMock( + return_value=None + ) + + mock_job = MagicMock() + mock_job.id = "job-terminal-1" + mock_job.unified_object_id = "dW5pZmllZF9iYXRjaF9pZA==" + mock_job.created_by = "user-1" + + assert check_batch_cost_instance._has_batch_processed_column is True + mock_prisma_client.db.litellm_managedobjecttable.find_many = AsyncMock( + return_value=[mock_job] + ) + + mock_response = MagicMock() + mock_response.status = terminal_status + mock_response.model_dump_json.return_value = ( + f'{{"id":"batch-1","status":"{terminal_status}"}}' + ) + + mock_llm_router.aretrieve_batch = AsyncMock(return_value=mock_response) + + decoded_id = "llm_model_id,model-123;llm_batch_id,batch-456;" + + with ( + patch( + "litellm.proxy.openai_files_endpoints.common_utils._is_base64_encoded_unified_file_id", + side_effect=[decoded_id, None], + ), + patch( + "litellm.proxy.openai_files_endpoints.common_utils.get_model_id_from_unified_batch_id", + return_value="model-123", + ), + patch( + "litellm.proxy.openai_files_endpoints.common_utils.get_batch_id_from_unified_batch_id", + return_value="batch-456", + ), + ): + await check_batch_cost_instance.check_batch_cost() + + assert ( + mock_prisma_client.db.litellm_managedobjecttable.update.call_count == 1 + ), f"Expected update() to be called exactly once for a {terminal_status} job" + update_data = mock_prisma_client.db.litellm_managedobjecttable.update.call_args[ + 1 + ]["data"] + assert update_data["status"] == terminal_status + assert ( + update_data["batch_processed"] is True + ), "terminal-status update() must set batch_processed=True so polling stops" + @pytest.mark.asyncio async def test_raw_output_file_id_converted_to_managed_id( self, check_batch_cost_instance, mock_prisma_client, mock_llm_router @@ -512,3 +670,249 @@ class TestCheckBatchCost: } assert mock_response.output_file_id == fake_managed_output_id assert mock_response.error_file_id == fake_managed_error_id + + +class TestUnmanagedVertexRouting: + """Routing of unmanaged Vertex batches whose unified_object_id is a raw provider job id.""" + + def _instance(self, track_unmanaged, router): + from litellm_enterprise.proxy.common_utils.check_batch_cost import ( + CheckBatchCost, + ) + + return CheckBatchCost( + proxy_logging_obj=MagicMock(), + prisma_client=MagicMock(), + llm_router=router, + track_unmanaged_vertex_batch_cost=track_unmanaged, + ) + + def _job(self, file_object=None): + job = MagicMock() + job.unified_object_id = "8823717160934178816" + job.file_object = ( + file_object if file_object is not None else _unmanaged_vertex_file_object() + ) + return job + + def test_flag_off_skips_unmanaged_id_unchanged(self): + """Default (flag off): a raw numeric unified_object_id is skipped exactly as before; + no model derivation or router lookup happens.""" + router = MagicMock() + instance = self._instance(track_unmanaged=False, router=router) + prom = MagicMock() + + with patch(_IS_B64, return_value=False): + result = instance._resolve_job_routing(self._job(), prom) + + assert result is None + prom.record_check_batch_cost_error.assert_called_once_with("invalid_unified_id") + router.resolve_model_name_from_model_id.assert_not_called() + router.get_model_ids.assert_not_called() + + def _vertex_deployment(self): + deployment = MagicMock() + deployment.litellm_params.custom_llm_provider = "vertex_ai" + deployment.litellm_params.model = "vertex_ai/gemini-2.5-flash" + return deployment + + def test_flag_on_routes_to_vertex_deployment(self): + """Flag on: derive the bare model from the gs:// path, resolve it to a deployment id, + and use the raw unified_object_id as the provider batch id.""" + router = MagicMock() + router.resolve_model_name_from_model_id.return_value = "gemini-2.5-flash" + router.get_model_ids.return_value = ["deploy-1"] + router.get_deployment = MagicMock(return_value=self._vertex_deployment()) + instance = self._instance(track_unmanaged=True, router=router) + + with patch(_IS_B64, return_value=False): + result = instance._resolve_job_routing(self._job(), MagicMock()) + + assert result == ("deploy-1", "8823717160934178816") + # bare model name (trailing GCS segment), not the full publishers/.. path + router.resolve_model_name_from_model_id.assert_called_once_with( + "gemini-2.5-flash" + ) + router.get_model_ids.assert_called_once_with(model_name="gemini-2.5-flash") + + def test_flag_on_skips_non_vertex_deployment_sharing_model_group(self): + """Flag on, but the only deployment for the model group is a non-vertex_ai + provider: must not be selected, even though the model group name matches.""" + router = MagicMock() + router.resolve_model_name_from_model_id.return_value = "gemini-2.5-flash" + router.get_model_ids.return_value = ["deploy-openai"] + non_vertex_deployment = MagicMock() + non_vertex_deployment.litellm_params.custom_llm_provider = "openai" + non_vertex_deployment.litellm_params.model = "gpt-4o" + router.get_deployment = MagicMock(return_value=non_vertex_deployment) + instance = self._instance(track_unmanaged=True, router=router) + prom = MagicMock() + + with patch(_IS_B64, return_value=False): + result = instance._resolve_job_routing(self._job(), prom) + + assert result is None + prom.record_check_batch_cost_error.assert_called_once_with( + "unmanaged_no_matching_deployment" + ) + + def test_flag_on_uses_later_vertex_deployment_with_matching_suffix(self): + router = MagicMock() + router.resolve_model_name_from_model_id.return_value = "azure-gemini" + router.get_model_ids.return_value = ["deploy-azure"] + non_vertex_deployment = MagicMock() + non_vertex_deployment.litellm_params.custom_llm_provider = "azure" + non_vertex_deployment.litellm_params.model = "azure/gemini-2.5-flash" + router.get_deployment = MagicMock(return_value=non_vertex_deployment) + router.get_model_list.return_value = [ + { + "model_name": "azure-gemini", + "litellm_params": { + "model": "azure/gemini-2.5-flash", + "custom_llm_provider": "azure", + }, + "model_info": {"id": "deploy-azure"}, + }, + { + "model_name": "vertex-gemini", + "litellm_params": { + "model": "vertex_ai/gemini-2.5-flash", + "custom_llm_provider": "vertex_ai", + }, + "model_info": {"id": "deploy-vertex"}, + }, + ] + instance = self._instance(track_unmanaged=True, router=router) + + with patch(_IS_B64, return_value=False): + result = instance._resolve_job_routing(self._job(), MagicMock()) + + assert result == ("deploy-vertex", "8823717160934178816") + router.get_model_ids.assert_called_once_with(model_name="azure-gemini") + + def test_flag_on_no_matching_deployment_records_metric(self): + """Flag on but no vertex_ai deployment for the model: skip with a distinct metric.""" + router = MagicMock() + router.resolve_model_name_from_model_id.return_value = None + router.get_model_ids.return_value = [] + instance = self._instance(track_unmanaged=True, router=router) + prom = MagicMock() + + with patch(_IS_B64, return_value=False): + result = instance._resolve_job_routing(self._job(), prom) + + assert result is None + prom.record_check_batch_cost_error.assert_called_once_with( + "unmanaged_no_matching_deployment" + ) + + def test_flag_on_non_gcs_input_is_not_unmanaged_vertex(self): + """Flag on, but input_file_id is not a gs:// publishers path: treat as unroutable, + do not attempt model derivation.""" + router = MagicMock() + instance = self._instance(track_unmanaged=True, router=router) + prom = MagicMock() + job = self._job( + file_object=_unmanaged_vertex_file_object(input_file_id="file-abc-123") + ) + + with patch(_IS_B64, return_value=False): + result = instance._resolve_job_routing(job, prom) + + assert result is None + prom.record_check_batch_cost_error.assert_called_once_with("invalid_unified_id") + router.resolve_model_name_from_model_id.assert_not_called() + + @pytest.mark.asyncio + async def test_end_to_end_costs_unmanaged_batch(self): + """Flag on, completed unmanaged batch: the poller polls Vertex with the raw job id, + computes cost, and marks batch_processed=True. Fails before this change (the row is + skipped at the unified-id gate).""" + router = MagicMock() + router.resolve_model_name_from_model_id.return_value = "gemini-2.5-flash" + router.get_model_ids.return_value = ["deploy-1"] + + mock_response = MagicMock() + mock_response.status = "completed" + mock_response.output_file_id = "gs://bucket/out/predictions.jsonl" + mock_response.error_file_id = None + mock_response.completed_at = None + mock_response.created_at = None + mock_response.model_dump_json.return_value = ( + '{"id":"8823717160934178816","status":"completed"}' + ) + router.aretrieve_batch = AsyncMock(return_value=mock_response) + router.get_deployment_credentials_with_provider = MagicMock( + return_value={"vertex_project": "p", "vertex_location": "us-central1"} + ) + + deployment = MagicMock() + deployment.litellm_params.custom_llm_provider = "vertex_ai" + deployment.litellm_params.model = "vertex_ai/gemini-2.5-flash" + deployment.model_name = "gemini-2.5-flash" + deployment.model_info.model_dump.return_value = {} + router.get_deployment = MagicMock(return_value=deployment) + + instance = self._instance(track_unmanaged=True, router=router) + instance.proxy_logging_obj.get_proxy_hook.return_value = None + instance._has_batch_processed_column = True + + prisma = instance.prisma_client + prisma.db = MagicMock() + prisma.db.litellm_managedobjecttable = MagicMock() + prisma.db.litellm_managedobjecttable.update_many = AsyncMock(return_value=0) + prisma.db.litellm_managedobjecttable.update = AsyncMock() + prisma.db.litellm_managedobjecttable.find_many = AsyncMock( + return_value=[self._job()] + ) + prisma.db.litellm_usertable = MagicMock() + prisma.db.litellm_usertable.find_unique = AsyncMock(return_value=None) + + mock_file_content = MagicMock() + mock_file_content.content = b'{"id":"req-1"}' + + with ( + patch(_IS_B64, side_effect=[False, None]), + patch( + "litellm.files.main.afile_content", + new_callable=AsyncMock, + return_value=mock_file_content, + ), + patch( + "litellm.batches.batch_utils._get_file_content_as_dictionary", + return_value=[{"id": "req-1"}], + ), + patch( + "litellm.batches.batch_utils.calculate_batch_cost_and_usage", + new_callable=AsyncMock, + return_value=( + 0.01, + {"prompt_tokens": 10, "completion_tokens": 5}, + ["gemini-2.5-flash"], + ), + ), + patch( + "litellm.litellm_core_utils.get_llm_provider_logic.get_llm_provider", + return_value=("gemini-2.5-flash", "vertex_ai", None, None), + ), + patch( + "litellm.litellm_core_utils.litellm_logging.Logging" + ) as mock_logging_cls, + ): + mock_logging_obj = MagicMock() + mock_logging_obj.async_success_handler = AsyncMock() + mock_logging_cls.return_value = mock_logging_obj + + await instance.check_batch_cost() + + router.aretrieve_batch.assert_awaited_once() + assert router.aretrieve_batch.call_args[1]["model"] == "deploy-1" + assert router.aretrieve_batch.call_args[1]["batch_id"] == "8823717160934178816" + + mock_logging_obj.async_success_handler.assert_awaited_once() + assert mock_logging_obj.async_success_handler.call_args[1]["batch_cost"] == 0.01 + + assert prisma.db.litellm_managedobjecttable.update.call_count == 1 + update_data = prisma.db.litellm_managedobjecttable.update.call_args[1]["data"] + assert update_data["batch_processed"] is True + assert update_data["status"] == "complete" diff --git a/tests/proxy_unit_tests/test_proxy_server.py b/tests/proxy_unit_tests/test_proxy_server.py index 921fbfa320f..212f7772cad 100644 --- a/tests/proxy_unit_tests/test_proxy_server.py +++ b/tests/proxy_unit_tests/test_proxy_server.py @@ -2844,7 +2844,9 @@ async def test_get_config_callbacks_with_all_types(client_no_auth): async def test_get_config_callbacks_environment_variables(client_no_auth): """ Test that /get/config/callbacks correctly includes environment variables - for each callback type. Values are returned as-is from the config (no decryption). + for each callback type. Under ``client_no_auth`` the resolved role is + not ``PROXY_ADMIN``, so values matched by the redaction helper come back + as ``"REDACTED"`` and other values pass through verbatim. """ from litellm.proxy.proxy_server import ProxyConfig @@ -2886,12 +2888,11 @@ async def test_get_config_callbacks_environment_variables(client_no_auth): assert langfuse_callback["type"] == "success" assert "variables" in langfuse_callback - # Verify langfuse env vars are present (values returned as-is, no decryption) langfuse_vars = langfuse_callback["variables"] assert "LANGFUSE_PUBLIC_KEY" in langfuse_vars - assert langfuse_vars["LANGFUSE_PUBLIC_KEY"] == "test-public-key" + assert langfuse_vars["LANGFUSE_PUBLIC_KEY"] == "REDACTED" assert "LANGFUSE_SECRET_KEY" in langfuse_vars - assert langfuse_vars["LANGFUSE_SECRET_KEY"] == "test-secret-key" + assert langfuse_vars["LANGFUSE_SECRET_KEY"] == "REDACTED" assert "LANGFUSE_HOST" in langfuse_vars assert langfuse_vars["LANGFUSE_HOST"] == "https://cloud.langfuse.com" @@ -2901,14 +2902,13 @@ async def test_get_config_callbacks_environment_variables(client_no_auth): assert otel_callback["type"] == "success_and_failure" assert "variables" in otel_callback - # Verify otel env vars are present otel_vars = otel_callback["variables"] assert "OTEL_EXPORTER" in otel_vars assert otel_vars["OTEL_EXPORTER"] == "otlp" assert "OTEL_ENDPOINT" in otel_vars assert otel_vars["OTEL_ENDPOINT"] == "http://localhost:4317" assert "OTEL_HEADERS" in otel_vars - assert otel_vars["OTEL_HEADERS"] == "key=value" + assert otel_vars["OTEL_HEADERS"] == "REDACTED" @pytest.mark.asyncio diff --git a/tests/proxy_unit_tests/test_unit_test_max_model_budget_limiter.py b/tests/proxy_unit_tests/test_unit_test_max_model_budget_limiter.py index 0daa5b17ffa..55459721906 100644 --- a/tests/proxy_unit_tests/test_unit_test_max_model_budget_limiter.py +++ b/tests/proxy_unit_tests/test_unit_test_max_model_budget_limiter.py @@ -452,6 +452,83 @@ async def test_async_log_success_event_pushes_redis_increments_when_redis_config mock_push.assert_awaited_once() +@pytest.mark.asyncio +async def test_get_fallback_model_within_budget_returns_none_without_fallbacks( + budget_limiter, +): + user_api_key = UserAPIKeyAuth(token="test-key", budget_fallbacks={}) + assert ( + await budget_limiter.get_fallback_model_within_budget(user_api_key, "gpt-4") + is None + ) + + +@pytest.mark.asyncio +async def test_get_fallback_model_within_budget_returns_first_within_budget( + budget_limiter, +): + user_api_key = UserAPIKeyAuth( + token="test-key", + model_max_budget={"gpt-4o-mini": {"budget_limit": 100.0, "time_period": "1d"}}, + budget_fallbacks={"gpt-4": ["gpt-4o-mini", "claude-haiku"]}, + ) + with patch.object( + budget_limiter, "_get_virtual_key_spend_for_model", return_value=1.0 + ): + result = await budget_limiter.get_fallback_model_within_budget( + user_api_key, "gpt-4" + ) + assert result == "gpt-4o-mini" + + +@pytest.mark.asyncio +async def test_get_fallback_model_within_budget_skips_exhausted_fallback( + budget_limiter, +): + user_api_key = UserAPIKeyAuth( + token="test-key", + model_max_budget={ + "gpt-4o-mini": {"budget_limit": 100.0, "time_period": "1d"}, + "claude-haiku": {"budget_limit": 100.0, "time_period": "1d"}, + }, + budget_fallbacks={"gpt-4": ["gpt-4o-mini", "claude-haiku"]}, + ) + + async def _spend_for_model(user_api_key_hash, model, key_budget_config): + return 150.0 if model == "gpt-4o-mini" else 1.0 + + with patch.object( + budget_limiter, + "_get_virtual_key_spend_for_model", + side_effect=_spend_for_model, + ): + result = await budget_limiter.get_fallback_model_within_budget( + user_api_key, "gpt-4" + ) + assert result == "claude-haiku" + + +@pytest.mark.asyncio +async def test_get_fallback_model_within_budget_returns_none_when_chain_exhausted( + budget_limiter, +): + user_api_key = UserAPIKeyAuth( + token="test-key", + model_max_budget={ + "gpt-4o-mini": {"budget_limit": 100.0, "time_period": "1d"}, + "claude-haiku": {"budget_limit": 100.0, "time_period": "1d"}, + }, + budget_fallbacks={"gpt-4": ["gpt-4o-mini", "claude-haiku"]}, + ) + with patch.object( + budget_limiter, "_get_virtual_key_spend_for_model", return_value=150.0 + ): + result = await budget_limiter.get_fallback_model_within_budget( + user_api_key, "gpt-4" + ) + assert result is None + + @pytest.mark.asyncio async def test_async_log_success_event_skips_redis_push_without_redis(budget_limiter): """When dual_cache has no Redis backend, do not await _push_in_memory_increments_to_redis.""" diff --git a/tests/router_unit_tests/test_router_aresponses_streaming_fallback.py b/tests/router_unit_tests/test_router_aresponses_streaming_fallback.py index 25bf79cd575..2fb7bdfceb5 100644 --- a/tests/router_unit_tests/test_router_aresponses_streaming_fallback.py +++ b/tests/router_unit_tests/test_router_aresponses_streaming_fallback.py @@ -266,3 +266,220 @@ async def test_aresponses_with_streaming_fallbacks_wraps_streaming_iterator(): ) assert out is wrapped mock_wrap.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_aresponses_fallback_on_in_stream_error_event(): + """A retriable in-stream error event (429) must trigger the router's mid-stream + fallback path: the wrapper catches MidStreamFallbackError raised by the source + iterator and yields the fallback stream instead of surfacing the error.""" + import json + from unittest.mock import Mock + + import litellm + from litellm.exceptions import MidStreamFallbackError + from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj + from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig + from litellm.responses.streaming_iterator import ResponsesAPIStreamingIterator + from litellm.types.llms.openai import ErrorEvent, ErrorEventError + + router = _make_router() + + error_payload = { + "type": "error", + "error": {"type": "tokens", "code": "rate_limit_exceeded", "message": "rate limited"}, + } + sse_bytes = f"data: {json.dumps(error_payload)}\n\n".encode() + + async def mock_aiter_bytes(): + yield sse_bytes + + mock_response = Mock() + mock_response.headers = {} + mock_response.aiter_bytes = mock_aiter_bytes + mock_logging_obj = MagicMock(spec=LiteLLMLoggingObj) + mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None + mock_config = Mock(spec=BaseResponsesAPIConfig) + mock_config.transform_streaming_response.return_value = ErrorEvent( + type=ResponsesAPIStreamEvents.ERROR, + sequence_number=0, + error=ErrorEventError(type="tokens", code="rate_limit_exceeded", message="rate limited"), + ) + + source = ResponsesAPIStreamingIterator( + response=mock_response, + model="gpt-5", + responses_api_provider_config=mock_config, + logging_obj=mock_logging_obj, + custom_llm_provider="openai", + ) + + fallback_event = _make_completed_event(1, 1, 2) + + class _FallbackStream: + def __init__(self) -> None: + self._done = False + + def __aiter__(self): + return self + + async def __anext__(self): + if self._done: + raise StopAsyncIteration + self._done = True + return fallback_event + + with patch.object( + router, + "async_function_with_fallbacks_common_utils", + new=AsyncMock(return_value=_FallbackStream()), + ) as mock_fallback: + wrapped = await router._aresponses_streaming_iterator( + response=source, + initial_kwargs={"model": "primary", "input": "original question"}, + ) + collected = [ev async for ev in wrapped] + + assert collected == [fallback_event] + mock_fallback.assert_awaited_once() + raised = mock_fallback.await_args.kwargs["e"] + assert isinstance(raised, MidStreamFallbackError) + assert raised.status_code == 429 + assert isinstance(raised.original_exception, litellm.APIError) + assert raised.original_exception.status_code == 429 + assert mock_fallback.await_args.kwargs["kwargs"]["input"] == "original question" + + +@pytest.mark.asyncio +async def test_aresponses_fallback_uses_continuation_input_after_partial_content(): + """When output text was already streamed before the error, the fallback re-entry + must carry a continuation input with the partial assistant text instead of + retrying the original input from scratch (which would duplicate streamed content).""" + import json + from unittest.mock import Mock + + from litellm.exceptions import MidStreamFallbackError + from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj + from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig + from litellm.responses.streaming_iterator import ResponsesAPIStreamingIterator + from litellm.types.llms.openai import ErrorEvent, ErrorEventError + + router = _make_router() + + events = [ + {"type": "response.output_text.delta", "delta": "partial answer"}, + {"type": "error", "error": {"type": "server_error", "code": "internal_error", "message": "boom"}}, + ] + sse_payload = b"".join(f"data: {json.dumps(event)}\n\n".encode() for event in events) + + async def mock_aiter_bytes(): + yield sse_payload + + mock_response = Mock() + mock_response.headers = {} + mock_response.aiter_bytes = mock_aiter_bytes + mock_logging_obj = MagicMock(spec=LiteLLMLoggingObj) + mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None + mock_config = Mock(spec=BaseResponsesAPIConfig) + + def transform(model, parsed_chunk, logging_obj): + if parsed_chunk.get("type") == "error": + return ErrorEvent( + type=ResponsesAPIStreamEvents.ERROR, + sequence_number=0, + error=ErrorEventError(**parsed_chunk["error"]), + ) + delta_event = Mock() + delta_event.type = ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA + delta_event.delta = parsed_chunk["delta"] + return delta_event + + mock_config.transform_streaming_response.side_effect = transform + + source = ResponsesAPIStreamingIterator( + response=mock_response, + model="gpt-5", + responses_api_provider_config=mock_config, + logging_obj=mock_logging_obj, + custom_llm_provider="openai", + ) + + fallback_event = _make_completed_event(1, 1, 2) + + class _FallbackStream: + def __init__(self) -> None: + self._done = False + + def __aiter__(self): + return self + + async def __anext__(self): + if self._done: + raise StopAsyncIteration + self._done = True + return fallback_event + + with patch.object( + router, + "async_function_with_fallbacks_common_utils", + new=AsyncMock(return_value=_FallbackStream()), + ) as mock_fallback: + wrapped = await router._aresponses_streaming_iterator( + response=source, + initial_kwargs={"model": "primary", "input": "original question"}, + ) + collected = [ev async for ev in wrapped] + + assert collected[-1] == fallback_event + raised = mock_fallback.await_args.kwargs["e"] + assert isinstance(raised, MidStreamFallbackError) + assert raised.is_pre_first_chunk is False + assert raised.generated_content == "partial answer" + continuation = mock_fallback.await_args.kwargs["kwargs"]["input"] + assert isinstance(continuation, list) + assert continuation[0]["content"][0]["text"] == "original question" + assert continuation[-2]["role"] == "developer" + assert continuation[-1]["role"] == "assistant" + assert continuation[-1]["content"][0]["text"] == "partial answer" + + +@pytest.mark.asyncio +async def test_aresponses_client_error_event_skips_fallback(): + """A 400-mapped in-stream error (raised as APIError, not MidStreamFallbackError) + must surface to the caller without invoking the router's fallback path.""" + import litellm + + router = _make_router() + + class _ClientErrorSource: + completed_response = None + + def __aiter__(self): + return self + + async def __anext__(self): + raise litellm.APIError( + status_code=400, + message="bad request", + llm_provider="openai", + model="gpt-5", + ) + + wrapped = await router._aresponses_streaming_iterator( + response=_ClientErrorSource(), + initial_kwargs={"model": "primary"}, + ) + + with patch.object( + router, + "async_function_with_fallbacks_common_utils", + new=AsyncMock(), + ) as mock_fallback: + with pytest.raises(litellm.APIError) as exc_info: + async for _ in wrapped: + pass + + assert exc_info.value.status_code == 400 + mock_fallback.assert_not_awaited() diff --git a/tests/router_unit_tests/test_router_helper_utils.py b/tests/router_unit_tests/test_router_helper_utils.py index 83d6d56df4f..848a6c28a57 100644 --- a/tests/router_unit_tests/test_router_helper_utils.py +++ b/tests/router_unit_tests/test_router_helper_utils.py @@ -523,6 +523,55 @@ async def test_deployment_callback_on_success(sync_mode): assert tpm_key is not None +@pytest.mark.asyncio +async def test_deployment_callback_on_success_tracks_tpm_for_io_deployment(): + """ + An IO-limited deployment (itpm/otpm, no tpm/rpm) must still record TPM usage + in the router's routing counter so TPM-aware routing strategies see its real + load in mixed model groups; its itpm/otpm enforcement runs separately. + """ + import time + + model_list = [ + { + "model_name": "opus", + "litellm_params": { + "model": "openai/gpt-4o-mini", + "api_key": "sk-fake", + "itpm": 1000, + }, + "model_info": {"id": "io-100"}, + } + ] + router = Router(model_list=model_list) + + standard_logging_payload = create_standard_logging_payload() + standard_logging_payload["total_tokens"] = 100 + standard_logging_payload["model_id"] = "io-100" + kwargs = { + "litellm_params": { + "metadata": { + "deployment": "openai/gpt-4o-mini", + "model_group": "opus", + }, + "model_info": {"id": "io-100"}, + }, + "standard_logging_object": standard_logging_payload, + } + response = litellm.ModelResponse(model="openai/gpt-4o-mini", usage={"total_tokens": 100}) + + tpm_key = await router.deployment_callback_on_success( + kwargs=kwargs, + completion_response=response, + start_time=time.time(), + end_time=time.time(), + ) + + # The IO deployment is no longer skipped: its TPM routing counter is tracked. + assert tpm_key is not None + assert await router.cache.async_get_cache(key=tpm_key) == 100 + + @pytest.mark.asyncio async def test_deployment_callback_on_failure(model_list): """Test if the '_deployment_callback_on_failure' function is working correctly""" @@ -923,6 +972,227 @@ async def test_set_response_headers_subtracts_in_flight_delta(model_list): assert headers["x-ratelimit-limit-requests"] == 100 +@pytest.mark.asyncio +async def test_set_response_headers_in_flight_delta_only_adjusts_tpm_rpm(model_list): + """ + The in-flight replay applies only to the post-incremented TPM/RPM counters + (`x-ratelimit-remaining-tokens` / `-requests`). The ITPM/OTPM counters are + incremented at reservation time (pre-call), so the input/output token + headers already reflect this request and must pass through untouched. + """ + from pydantic import BaseModel + + class _Usage(BaseModel): + total_tokens: int = 30 + prompt_tokens: int = 20 + completion_tokens: int = 10 + + class _Resp(BaseModel): + usage: _Usage = _Usage() + _hidden_params: dict = {} + + router = Router(model_list=model_list) + router.get_remaining_model_group_usage = AsyncMock( + return_value={ + "x-ratelimit-remaining-tokens": 1000, + "x-ratelimit-remaining-requests": 100, + "x-ratelimit-remaining-input-tokens": 1000, + "x-ratelimit-remaining-output-tokens": 500, + } + ) + + resp = _Resp() + resp._hidden_params = {} + await router.set_response_headers(response=resp, model_group="gpt-3.5-turbo") + + headers = resp._hidden_params["additional_headers"] + # TPM/RPM headers replay the in-flight increment... + assert headers["x-ratelimit-remaining-tokens"] == 970 + assert headers["x-ratelimit-remaining-requests"] == 99 + # ...but the reservation-based input/output headers pass through unchanged. + assert headers["x-ratelimit-remaining-input-tokens"] == 1000 + assert headers["x-ratelimit-remaining-output-tokens"] == 500 + + +@pytest.mark.asyncio +async def test_get_model_group_io_token_usage_sums_across_deployments(): + """ + get_model_group_io_token_usage must sum ITPM/OTPM across every deployment + in the model group (not just the first), reading the same per-deployment + cache keys the pre-call reservation writes to. + """ + from litellm.types.router import RouterCacheEnum + from litellm.utils import get_utc_datetime + + router = Router( + model_list=[ + { + "model_name": "opus", + "litellm_params": { + "model": "openai/gpt-4o-mini", + "itpm": 1000, + "otpm": 500, + }, + "model_info": {"id": "io-usage-dep-1"}, + }, + { + "model_name": "opus", + "litellm_params": { + "model": "openai/gpt-4o", + "itpm": 1000, + "otpm": 500, + }, + "model_info": {"id": "io-usage-dep-2"}, + }, + ] + ) + + minute = get_utc_datetime().strftime("%H-%M") + keys_and_values = [ + ( + RouterCacheEnum.ITPM.value.format( + id="io-usage-dep-1", model="openai/gpt-4o-mini", current_minute=minute + ), + 30, + ), + ( + RouterCacheEnum.OTPM.value.format( + id="io-usage-dep-1", model="openai/gpt-4o-mini", current_minute=minute + ), + 10, + ), + ( + RouterCacheEnum.ITPM.value.format( + id="io-usage-dep-2", model="openai/gpt-4o", current_minute=minute + ), + 70, + ), + ( + RouterCacheEnum.OTPM.value.format( + id="io-usage-dep-2", model="openai/gpt-4o", current_minute=minute + ), + 20, + ), + ] + for key, value in keys_and_values: + await router.cache.async_increment_cache(key=key, value=value, ttl=60) + + current_itpm, current_otpm = await router.get_model_group_io_token_usage("opus") + + assert current_itpm == 100 + assert current_otpm == 30 + + +@pytest.mark.asyncio +async def test_get_model_group_io_token_usage_no_deployments_returns_none(): + router = Router(model_list=[]) + current_itpm, current_otpm = await router.get_model_group_io_token_usage( + "nonexistent-group" + ) + assert current_itpm is None + assert current_otpm is None + + +@pytest.mark.asyncio +async def test_get_remaining_model_group_usage_merges_io_and_tpm_headers(model_list): + """ + A model group with both itpm/otpm and tpm/rpm limits must expose the + standard remaining-tokens/requests headers alongside the input/output token + headers, so clients and prometheus gauges relying on either still get data. + """ + from unittest.mock import Mock + + from litellm.types.router import ModelGroupInfo + + router = Router(model_list=model_list) + router._cached_get_model_group_info = Mock( + return_value=ModelGroupInfo( + model_group="gpt-3.5-turbo", + providers=["openai"], + itpm=2000, + otpm=1000, + tpm=5000, + rpm=50, + ) + ) + router.get_model_group_io_token_usage = AsyncMock(return_value=(100, 40)) + router.get_model_group_usage = AsyncMock(return_value=(500, 5)) + + headers = await router.get_remaining_model_group_usage("gpt-3.5-turbo") + + assert headers["x-ratelimit-remaining-input-tokens"] == 1900 + assert headers["x-ratelimit-remaining-output-tokens"] == 960 + assert headers["x-ratelimit-remaining-tokens"] == 4500 + assert headers["x-ratelimit-remaining-requests"] == 45 + + +@pytest.mark.asyncio +async def test_set_response_headers_native_input_token_header_does_not_suppress_router_headers(model_list): + """ + A provider that natively returns `x-ratelimit-remaining-input-tokens` must + not suppress the router's own remaining-tokens/requests headers for a + non-IO model group. + """ + from pydantic import BaseModel + + class _Usage(BaseModel): + total_tokens: int = 42 + + class _Resp(BaseModel): + usage: _Usage = _Usage() + _hidden_params: dict = {} + + router = Router(model_list=model_list) + router.get_remaining_model_group_usage = AsyncMock( + return_value={ + "x-ratelimit-remaining-tokens": 1000, + "x-ratelimit-remaining-requests": 100, + } + ) + + resp = _Resp() + resp._hidden_params = {"additional_headers": {"x-ratelimit-remaining-input-tokens": 5}} + await router.set_response_headers(response=resp, model_group="gpt-3.5-turbo") + + headers = resp._hidden_params["additional_headers"] + assert headers["x-ratelimit-remaining-tokens"] == 958 + assert headers["x-ratelimit-remaining-requests"] == 99 + # the provider's native header is left untouched + assert headers["x-ratelimit-remaining-input-tokens"] == 5 + + +@pytest.mark.asyncio +async def test_set_response_headers_native_token_header_does_not_suppress_io_headers(model_list): + from pydantic import BaseModel + + class _Usage(BaseModel): + total_tokens: int = 42 + + class _Resp(BaseModel): + usage: _Usage = _Usage() + _hidden_params: dict = {} + + router = Router(model_list=model_list) + router.get_remaining_model_group_usage = AsyncMock( + return_value={ + "x-ratelimit-remaining-tokens": 1000, + "x-ratelimit-remaining-requests": 100, + "x-ratelimit-remaining-input-tokens": 900, + "x-ratelimit-remaining-output-tokens": 450, + } + ) + + resp = _Resp() + resp._hidden_params = {"additional_headers": {"x-ratelimit-remaining-tokens": 5}} + await router.set_response_headers(response=resp, model_group="gpt-3.5-turbo") + + headers = resp._hidden_params["additional_headers"] + assert headers["x-ratelimit-remaining-tokens"] == 5 + assert headers["x-ratelimit-remaining-requests"] == 99 + assert headers["x-ratelimit-remaining-input-tokens"] == 900 + assert headers["x-ratelimit-remaining-output-tokens"] == 450 + + @pytest.mark.asyncio async def test_set_response_headers_handles_missing_usage(model_list): """ @@ -952,6 +1222,72 @@ async def test_set_response_headers_handles_missing_usage(model_list): assert headers["x-ratelimit-remaining-requests"] == 99 +@pytest.mark.asyncio +async def test_set_response_headers_dict_anthropic_messages_response(model_list): + """Anthropic /v1/messages returns a dict; IO rate-limit headers must attach.""" + router = Router(model_list=model_list) + router.get_remaining_model_group_usage = AsyncMock( + return_value={ + "x-ratelimit-limit-input-tokens": 25, + "x-ratelimit-remaining-input-tokens": 20, + "x-ratelimit-limit-output-tokens": 100, + "x-ratelimit-remaining-output-tokens": 95, + } + ) + + resp = { + "id": "msg_123", + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "hi"}], + "usage": {"input_tokens": 5, "output_tokens": 1}, + } + await router.set_response_headers(response=resp, model_group="io-itpm-strict") + + assert "_hidden_params" in resp + headers = resp["_hidden_params"]["additional_headers"] + assert headers["x-litellm-model-group"] == "io-itpm-strict" + assert headers["x-ratelimit-limit-input-tokens"] == 25 + assert headers["x-ratelimit-remaining-input-tokens"] == 20 + assert headers["x-ratelimit-remaining-output-tokens"] == 95 + + +@pytest.mark.asyncio +async def test_set_response_headers_wraps_bare_async_generator(model_list): + """ + Streaming responses that never go through Router.make_call's usual + object-based wrappers (e.g. the Anthropic /v1/messages -> Responses API + bridge, which yields a raw async generator with no `_hidden_params` slot) + must still get IO rate-limit headers attached via a thin wrapper. + """ + + async def _raw_generator(): + yield {"type": "message_start"} + yield {"type": "message_stop"} + + router = Router(model_list=model_list) + router.get_remaining_model_group_usage = AsyncMock( + return_value={ + "x-ratelimit-limit-input-tokens": 25, + "x-ratelimit-remaining-input-tokens": 20, + } + ) + + wrapped = await router.set_response_headers(response=_raw_generator(), model_group="io-itpm-strict") + + assert hasattr(wrapped, "_hidden_params") + headers = wrapped._hidden_params["additional_headers"] + assert headers["x-litellm-model-group"] == "io-itpm-strict" + assert headers["x-ratelimit-limit-input-tokens"] == 25 + assert headers["x-ratelimit-remaining-input-tokens"] == 20 + + from collections.abc import AsyncIterator + + assert isinstance(wrapped, AsyncIterator) + chunks = [chunk async for chunk in wrapped] + assert chunks == [{"type": "message_start"}, {"type": "message_stop"}] + + def test_get_all_deployments(model_list): """Test if the 'get_all_deployments' function is working correctly""" router = Router(model_list=model_list) diff --git a/tests/search_tests/test_tinyfish_search.py b/tests/search_tests/test_tinyfish_search.py index 337a7d5b115..aca28544513 100644 --- a/tests/search_tests/test_tinyfish_search.py +++ b/tests/search_tests/test_tinyfish_search.py @@ -161,10 +161,60 @@ class TestTinyfishSearch: query_params = parse_qs(parsed_url.query) assert query_params["language"] == ["en"] + @pytest.mark.asyncio + async def test_fetch_param_round_trip(self): + # End-to-end check: caller passes `fetch=...` (JSON-encoded tf-fetch + # config); param reaches TinyFish on the request side and the nested + # `fetch` object on each result surfaces back to the SearchResult on the + # response side. No LiteLLM-side support code is required. + os.environ["TINYFISH_API_KEY"] = "sk-tinyfish-test" + + fetched_response = { + "results": [ + { + "title": "TinyFish", + "url": "https://tinyfish.ai", + "snippet": "Web automation.", + "fetch": { + "url": "https://tinyfish.ai", + "title": "TinyFish", + "text": "Page body text.", + "cached": False, + }, + } + ] + } + mock_response = _make_mock_response(fetched_response) + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.get", + new_callable=AsyncMock, + ) as mock_get: + mock_get.return_value = mock_response + + response = await litellm.asearch( + query="tinyfish", + search_provider="tinyfish", + fetch="{}", + ) + + call_args = mock_get.call_args + parsed_url = urlparse(call_args.kwargs["url"]) + query_params = parse_qs(parsed_url.query) + assert query_params["fetch"] == ["{}"] + + first = response.results[0] + fetch_field = getattr(first, "fetch", None) + assert isinstance(fetch_field, dict) + assert fetch_field["text"] == "Page body text." + def test_max_results_truncates_response(self): from litellm.llms.tinyfish.search.transformation import TinyfishSearchConfig config = TinyfishSearchConfig() + # max_results is threaded through self by transform_search_request; + # simulate that for this direct response-side test. + config._caller_max_results = 3 many_results = { "results": [ { @@ -175,10 +225,7 @@ class TestTinyfishSearch: for i in range(10) ] } - mock_response = _make_mock_response( - many_results, - request_url="https://api.search.tinyfish.ai?query=test&max_results=3", - ) + mock_response = _make_mock_response(many_results) result = config.transform_search_response( raw_response=mock_response, diff --git a/tests/store_model_in_db_tests/test_callbacks_in_db.py b/tests/store_model_in_db_tests/test_callbacks_in_db.py index 4a851251a3e..e92aeb6ebc4 100644 --- a/tests/store_model_in_db_tests/test_callbacks_in_db.py +++ b/tests/store_model_in_db_tests/test_callbacks_in_db.py @@ -15,17 +15,30 @@ import os import dotenv from dotenv import load_dotenv import pytest -from openai import AsyncOpenAI +from openai import AsyncOpenAI, APIConnectionError from openai.types.chat import ChatCompletion load_dotenv() # used for testing LANGFUSE_BASE_URL = "https://exampleopenaiendpoint-production-c715.up.railway.app" +PROXY_BASE_URL = "http://127.0.0.1:4000" + + +async def wait_for_proxy_ready(session, timeout: int = 60): + for _ in range(timeout): + try: + async with session.get(f"{PROXY_BASE_URL}/health/liveliness") as response: + if response.status == 200: + return + except aiohttp.ClientError: + pass + await asyncio.sleep(1) + raise RuntimeError(f"Proxy at {PROXY_BASE_URL} not ready after {timeout}s") async def config_update(session, routing_strategy=None): - url = "http://0.0.0.0:4000/config/update" + url = f"{PROXY_BASE_URL}/config/update" headers = {"Authorization": "Bearer sk-1234", "Content-Type": "application/json"} print("routing_strategy: ", routing_strategy) data = { @@ -62,32 +75,41 @@ async def check_langfuse_request(response_id: str): async def make_chat_completions_request() -> ChatCompletion: - client = AsyncOpenAI(api_key="sk-1234", base_url="http://0.0.0.0:4000") - response = await client.chat.completions.create( - model="fake-openai-endpoint", - messages=[{"role": "user", "content": "Hello, world!"}], + client = AsyncOpenAI(api_key="sk-1234", base_url=PROXY_BASE_URL) + last_error = None + for _ in range(10): + try: + response = await client.chat.completions.create( + model="fake-openai-endpoint", + messages=[{"role": "user", "content": "Hello, world!"}], + ) + print(response) + return response + except APIConnectionError as e: + last_error = e + await asyncio.sleep(2) + raise AssertionError( + f"Proxy at {PROXY_BASE_URL} unreachable after retries: {last_error!r}" ) - print(response) - return response @pytest.mark.asyncio async def test_e2e_langfuse_callbacks_in_db(): - session = aiohttp.ClientSession() + async with aiohttp.ClientSession() as session: + # add langfuse callback to DB + await config_update(session) - # add langfuse callback to DB - await config_update(session) + # wait 20 seconds for the callback to be loaded into the instance + await asyncio.sleep(20) + await wait_for_proxy_ready(session) + + # make a /chat/completions request to the proxy + response = await make_chat_completions_request() + print(response) + response_id = response.id + print("response_id: ", response_id) - # wait 20 seconds for the callback to be loaded into the instance await asyncio.sleep(20) - - # make a /chat/completions request to the proxy - response = await make_chat_completions_request() - print(response) - response_id = response.id - print("response_id: ", response_id) - - await asyncio.sleep(11) # check if the request is logged in Langfuse await check_langfuse_request(response_id) diff --git a/tests/test_litellm/a2a_protocol/__init__.py b/tests/test_litellm/a2a_protocol/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/a2a_protocol/test_a2a_streaming_iterator.py b/tests/test_litellm/a2a_protocol/test_a2a_streaming_iterator.py new file mode 100644 index 00000000000..d86cbb94a91 --- /dev/null +++ b/tests/test_litellm/a2a_protocol/test_a2a_streaming_iterator.py @@ -0,0 +1,102 @@ +""" +Regression test for LIT-4210: completing an A2A stream must not run the sync +success_handler on the thread-pool executor concurrently with +async_success_handler (cross-thread pydantic mutation segfaults pydantic-core). +""" + +import asyncio +import time +from types import SimpleNamespace + +import pytest + +import litellm +from litellm.a2a_protocol import streaming_iterator as a2a_streaming_iterator_module +from litellm.a2a_protocol.streaming_iterator import A2AStreamingIterator +from litellm.integrations.custom_logger import CustomLogger +from litellm.litellm_core_utils import thread_pool_executor as thread_pool_executor_module +from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging + + +class RecordingCustomLogger(CustomLogger): + def __init__(self): + super().__init__() + self.async_hook_fired = False + + async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): + self.async_hook_fired = True + + async def async_log_stream_event(self, kwargs, response_obj, start_time, end_time): + self.async_hook_fired = True + + +class RecordingExecutor: + def __init__(self, inner): + self._inner = inner + self.submits: list = [] + + def submit(self, fn, *args, **kwargs): + self.submits.append(fn) + return self._inner.submit(fn, *args, **kwargs) + + def submitted_for(self, logging_obj) -> list: + return [fn for fn in self.submits if getattr(fn, "__self__", None) is logging_obj] + + +@pytest.fixture(autouse=True) +def _isolate_callbacks(): + saved = ( + litellm.callbacks, + litellm.success_callback, + litellm._async_success_callback, + litellm.failure_callback, + litellm._async_failure_callback, + ) + yield + ( + litellm.callbacks, + litellm.success_callback, + litellm._async_success_callback, + litellm.failure_callback, + litellm._async_failure_callback, + ) = saved + + +@pytest.mark.asyncio +async def test_custom_logger_only_never_submits_sync_success_handler(monkeypatch): + recording_executor = RecordingExecutor(thread_pool_executor_module.executor) + monkeypatch.setattr(thread_pool_executor_module, "executor", recording_executor) + monkeypatch.setattr(a2a_streaming_iterator_module, "executor", recording_executor, raising=False) + + recorder = RecordingCustomLogger() + litellm.success_callback = [recorder] + litellm._async_success_callback = [recorder] + + logging_obj = LitellmLogging( + model="a2a/test-agent", + messages=[{"role": "user", "content": "hi"}], + stream=True, + call_type="a2a_send_message_streaming", + start_time=time.time(), + litellm_call_id="lit-4210-test", + function_id="lit-4210-test", + ) + + async def _empty_stream(): + return + yield + + iterator = A2AStreamingIterator( + stream=_empty_stream(), + request=SimpleNamespace( + params=SimpleNamespace(message={"role": "user", "parts": [{"kind": "text", "text": "hi"}]}) + ), + logging_obj=logging_obj, + agent_name="test-agent", + ) + + await iterator._handle_stream_complete() + await asyncio.sleep(0.5) + + assert recorder.async_hook_fired is True + assert recording_executor.submitted_for(logging_obj) == [] diff --git a/tests/test_litellm/a2a_protocol/test_cost_calculator.py b/tests/test_litellm/a2a_protocol/test_cost_calculator.py index bf03562f8ae..a29f012170f 100644 --- a/tests/test_litellm/a2a_protocol/test_cost_calculator.py +++ b/tests/test_litellm/a2a_protocol/test_cost_calculator.py @@ -103,9 +103,7 @@ async def _mock_stream_messages(a2a_client: Any, request: Any) -> AsyncIterator[ kind="message", ) for _ in range(2): - yield SendStreamingMessageResponse( - root=SendStreamingMessageSuccessResponse(id=request.id, result=msg) - ) + yield SendStreamingMessageResponse(root=SendStreamingMessageSuccessResponse(id=request.id, result=msg)) class CostLogger(CustomLogger): @@ -119,9 +117,7 @@ class CostLogger(CustomLogger): slp = kwargs.get("standard_logging_object") if slp: self.response_cost = ( - slp.get("response_cost") - if isinstance(slp, dict) - else getattr(slp, "response_cost", None) + slp.get("response_cost") if isinstance(slp, dict) else getattr(slp, "response_cost", None) ) @@ -160,6 +156,43 @@ async def test_asend_message_uses_cost_per_query(): assert cost_logger.response_cost == 0.05 +@pytest.mark.asyncio +async def test_asend_message_uses_cost_per_query_from_litellm_params_dict(): + """ + Proxy passes agent pricing as the litellm_params dict param (not top-level + kwargs). Regression for cost_per_query landing at $0 on the native path. + """ + from litellm.a2a_protocol import asend_message + + litellm.logging_callback_manager._reset_all_callbacks() + cost_logger = CostLogger() + litellm.callbacks = [cost_logger] + + mock_client = MagicMock() + mock_client._litellm_agent_card = MagicMock() + mock_client._litellm_agent_card.name = "test-agent" + + mock_request = _make_send_message_request("test-123") + + with patch( + "litellm.a2a_protocol.main._execute_a2a_send_with_retry", + new=_mock_execute_a2a_send, + ): + await asend_message( + a2a_client=mock_client, + request=mock_request, + litellm_params={ + "cost_per_query": 0.5, + "input_cost_per_token": 0.099999, + "output_cost_per_token": 0.1, + }, + ) + + await asyncio.sleep(0.1) + + assert cost_logger.response_cost == 0.5 + + class TokenAndCostLogger(CustomLogger): """Custom logger to capture both token counts and cost.""" @@ -173,19 +206,13 @@ class TokenAndCostLogger(CustomLogger): slp = kwargs.get("standard_logging_object") if slp: self.response_cost = ( - slp.get("response_cost") - if isinstance(slp, dict) - else getattr(slp, "response_cost", None) + slp.get("response_cost") if isinstance(slp, dict) else getattr(slp, "response_cost", None) ) self.prompt_tokens = ( - slp.get("prompt_tokens") - if isinstance(slp, dict) - else getattr(slp, "prompt_tokens", None) + slp.get("prompt_tokens") if isinstance(slp, dict) else getattr(slp, "prompt_tokens", None) ) self.completion_tokens = ( - slp.get("completion_tokens") - if isinstance(slp, dict) - else getattr(slp, "completion_tokens", None) + slp.get("completion_tokens") if isinstance(slp, dict) else getattr(slp, "completion_tokens", None) ) @@ -207,9 +234,7 @@ async def test_asend_message_uses_input_output_cost_per_token(): mock_client._litellm_agent_card = MagicMock() mock_client._litellm_agent_card.name = "test-agent" - mock_request = _make_send_message_request( - "test-123", user_text="Hello, what can you do?" - ) + mock_request = _make_send_message_request("test-123", user_text="Hello, what can you do?") # Define specific cost per token values input_cost_per_token = 0.00001 # $0.01 per 1000 tokens @@ -246,15 +271,11 @@ async def test_asend_message_uses_input_output_cost_per_token(): assert response_cost is not None, "response_cost should be captured" # Calculate expected cost - expected_cost = (prompt_tokens * input_cost_per_token) + ( - completion_tokens * output_cost_per_token - ) + expected_cost = (prompt_tokens * input_cost_per_token) + (completion_tokens * output_cost_per_token) print(f"expected_cost: {expected_cost}") # Verify exact cost calculation - assert ( - response_cost == expected_cost - ), f"response_cost {response_cost} should equal expected {expected_cost}" + assert response_cost == expected_cost, f"response_cost {response_cost} should equal expected {expected_cost}" class AgentIdLogger(CustomLogger): @@ -305,9 +326,9 @@ async def test_asend_message_passes_agent_id_to_callback(): await asyncio.sleep(0.1) # Verify agent_id was passed to callback - assert ( - agent_id_logger.agent_id == test_agent_id - ), f"Expected agent_id '{test_agent_id}', got '{agent_id_logger.agent_id}'" + assert agent_id_logger.agent_id == test_agent_id, ( + f"Expected agent_id '{test_agent_id}', got '{agent_id_logger.agent_id}'" + ) class MetadataLogger(CustomLogger): @@ -418,9 +439,7 @@ async def test_asend_message_streaming_triggers_callbacks(): assert len(chunks) == 2 # Verify callbacks WERE triggered after stream completed - assert ( - callback_logger.kwargs is not None - ), "Streaming should trigger callbacks after completion" - assert ( - callback_logger.agent_id == test_agent_id - ), f"Expected agent_id '{test_agent_id}', got '{callback_logger.agent_id}'" + assert callback_logger.kwargs is not None, "Streaming should trigger callbacks after completion" + assert callback_logger.agent_id == test_agent_id, ( + f"Expected agent_id '{test_agent_id}', got '{callback_logger.agent_id}'" + ) diff --git a/tests/test_litellm/a2a_protocol/test_utils.py b/tests/test_litellm/a2a_protocol/test_utils.py new file mode 100644 index 00000000000..8a219211c62 --- /dev/null +++ b/tests/test_litellm/a2a_protocol/test_utils.py @@ -0,0 +1,41 @@ +"""Tests for litellm/a2a_protocol/utils.py token/usage extraction.""" + +import pytest + +pytest.importorskip("a2a.compat.v0_3.types") + +from a2a.compat.v0_3.types import MessageSendParams, SendMessageRequest + +from litellm.a2a_protocol.utils import A2ARequestUtils + + +def _request(user_text: str) -> SendMessageRequest: + return SendMessageRequest( + id="r1", + params=MessageSendParams( + message={ + "messageId": "m1", + "role": "user", + "parts": [{"kind": "text", "text": user_text}], + } + ), + ) + + +def test_calculate_usage_counts_input_tokens_from_request_object(): + """Regression: request-side Part is a RootModel; input tokens must be counted.""" + request = _request("count these input tokens please") + response_dict = { + "result": { + "kind": "message", + "parts": [{"kind": "text", "text": "ok"}], + } + } + + prompt_tokens, completion_tokens, total_tokens = A2ARequestUtils.calculate_usage_from_request_response( + request=request, response_dict=response_dict + ) + + assert prompt_tokens > 0 + assert completion_tokens > 0 + assert total_tokens == prompt_tokens + completion_tokens diff --git a/tests/test_litellm/batches/test_batch_utils.py b/tests/test_litellm/batches/test_batch_utils.py index 3aebfcb911e..9de5cd69b1e 100644 --- a/tests/test_litellm/batches/test_batch_utils.py +++ b/tests/test_litellm/batches/test_batch_utils.py @@ -733,3 +733,192 @@ def test_total_usage_vertex_disable_transform_path(monkeypatch): usage = bu._get_batch_job_total_usage_from_file_content([], custom_llm_provider="vertex_ai", model_name="gemini-x") assert usage.total_tokens == 3 + + +def _anthropic_usage(input_tokens, output_tokens, cache_creation=0, cache_read=0): + return { + "input_tokens": input_tokens, + "output_tokens": output_tokens, + "cache_creation_input_tokens": cache_creation, + "cache_read_input_tokens": cache_read, + } + + +def _anthropic_succeeded_row(model="claude-sonnet-4-5-20250929", usage=None): + return { + "custom_id": "req-1", + "result": { + "type": "succeeded", + "message": { + "id": "msg_1", + "type": "message", + "role": "assistant", + "model": model, + "content": [{"type": "text", "text": "ok"}], + "stop_reason": "end_turn", + "usage": usage or _anthropic_usage(10, 5), + }, + }, + } + + +def _anthropic_errored_row(): + return { + "custom_id": "req-2", + "result": { + "type": "errored", + "error": {"type": "invalid_request_error", "message": "bad request"}, + }, + } + + +_ANTHROPIC_MODEL_INFO = { + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "cache_read_input_token_cost": 3e-7, + "cache_creation_input_token_cost": 3.75e-6, +} + + +@pytest.mark.parametrize( + "row,expected", + [ + (_anthropic_succeeded_row(), True), + (_anthropic_errored_row(), False), + ({"custom_id": "x", "result": {"type": "canceled"}}, False), + ({"custom_id": "x", "result": {"type": "expired"}}, False), + ({"custom_id": "x"}, False), + ({"custom_id": "x", "result": None}, False), + ], +) +def test_anthropic_result_line_success_check(row, expected): + """ + LIT-4008 regression: anthropic batch results JSONL lines are not + OpenAI-shaped; success is result.type == "succeeded", not + response.status_code == 200. Pre-fix every anthropic line parsed as + unsuccessful, so completed batches were billed $0 forever. + """ + assert bu._batch_response_was_successful(row, custom_llm_provider="anthropic") is expected + + +def test_anthropic_response_body_is_result_message(): + row = _anthropic_succeeded_row(model="claude-sonnet-4-5-20250929") + body = bu._get_response_from_batch_job_output_file(row, custom_llm_provider="anthropic") + assert body["model"] == "claude-sonnet-4-5-20250929" + assert body["usage"] == _anthropic_usage(10, 5) + + +def test_anthropic_usage_conversion_includes_cache_tokens(): + body = {"model": "claude-sonnet-4-5-20250929", "usage": _anthropic_usage(1000, 200, cache_creation=2000, cache_read=8000)} + usage = bu._get_batch_job_usage_from_response_body(body, custom_llm_provider="anthropic") + assert usage.prompt_tokens == 11000 + assert usage.completion_tokens == 200 + assert usage.total_tokens == 11200 + assert usage.prompt_tokens_details.cached_tokens == 8000 + assert usage.prompt_tokens_details.cache_creation_tokens == 2000 + + +def test_anthropic_total_usage_sums_succeeded_only(): + rows = [ + _anthropic_succeeded_row(usage=_anthropic_usage(10, 5)), + _anthropic_errored_row(), + _anthropic_succeeded_row(usage=_anthropic_usage(20, 10, cache_read=100)), + ] + usage = bu._get_batch_job_total_usage_from_file_content(rows, custom_llm_provider="anthropic") + assert (usage.prompt_tokens, usage.completion_tokens, usage.total_tokens) == (130, 15, 145) + + +def test_anthropic_total_usage_aggregates_cache_token_details(): + rows = [ + _anthropic_succeeded_row(usage=_anthropic_usage(1000, 200, cache_creation=2000, cache_read=8000)), + _anthropic_errored_row(), + _anthropic_succeeded_row(usage=_anthropic_usage(50, 20, cache_creation=300, cache_read=700)), + ] + usage = bu._get_batch_job_total_usage_from_file_content(rows, custom_llm_provider="anthropic") + assert usage.prompt_tokens_details.cached_tokens == 8700 + assert usage.prompt_tokens_details.cache_creation_tokens == 2300 + assert usage.cache_read_input_tokens == 8700 + assert usage.cache_creation_input_tokens == 2300 + + +def test_total_usage_without_cache_tokens_has_no_prompt_details(): + rows = [ + { + "custom_id": "req-1", + "response": {"status_code": 200, "body": {"model": "gpt-5.2", "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}}}, + } + ] + usage = bu._get_batch_job_total_usage_from_file_content(rows, custom_llm_provider="openai") + assert (usage.prompt_tokens, usage.completion_tokens, usage.total_tokens) == (10, 5, 15) + assert usage.prompt_tokens_details is None + + +def test_anthropic_cost_applies_batch_discount_and_cache_pricing(): + """Anthropic batches bill at 50% of the regular rate for base input, + cache reads, cache writes, and output tokens alike.""" + rows = [ + _anthropic_succeeded_row(usage=_anthropic_usage(1000, 200, cache_creation=2000, cache_read=8000)), + _anthropic_errored_row(), + ] + + total = bu._get_batch_job_cost_from_file_content( + rows, + custom_llm_provider="anthropic", + model_info=_ANTHROPIC_MODEL_INFO, # type: ignore[arg-type] + ) + + expected_half_price = (1000 * 3e-6 + 8000 * 3e-7 + 2000 * 3.75e-6 + 200 * 15e-6) / 2 + assert total == pytest.approx(expected_half_price) + + +def test_anthropic_cost_without_model_info_uses_batch_cost_calculator(monkeypatch): + import litellm.cost_calculator as cc + + seen = [] + + def _fake_batch_cost_calculator(**kw): + seen.append(kw) + return (0.1, 0.2) + + monkeypatch.setattr(cc, "batch_cost_calculator", _fake_batch_cost_calculator) + monkeypatch.setattr( + litellm, + "completion_cost", + lambda **kw: pytest.fail("anthropic rows must not go through completion_cost"), + ) + + total = bu._get_batch_job_cost_from_file_content( + [_anthropic_succeeded_row()], custom_llm_provider="anthropic" + ) + + assert total == pytest.approx(0.3) + assert seen[0]["model"] == "claude-sonnet-4-5-20250929" + assert seen[0]["custom_llm_provider"] == "anthropic" + assert seen[0]["usage"].prompt_tokens == 10 + + +def test_anthropic_batch_models_collected_from_succeeded_rows(): + rows = [ + _anthropic_succeeded_row(model="claude-sonnet-4-5-20250929"), + _anthropic_errored_row(), + ] + assert bu._get_batch_models_from_file_content(rows, None, "anthropic") == ["claude-sonnet-4-5-20250929"] + + +@pytest.mark.asyncio +async def test_calculate_batch_cost_and_usage_anthropic_end_to_end(): + rows = [ + _anthropic_succeeded_row(usage=_anthropic_usage(1000, 200, cache_creation=2000, cache_read=8000)), + _anthropic_errored_row(), + ] + + cost, usage, models = await bu.calculate_batch_cost_and_usage( + file_content_dictionary=rows, + custom_llm_provider="anthropic", + model_name="claude-sonnet-4-5", + model_info=_ANTHROPIC_MODEL_INFO, # type: ignore[arg-type] + ) + + assert cost == pytest.approx(1000 * 3e-6 / 2 + 8000 * 3e-7 / 2 + 2000 * 3.75e-6 / 2 + 200 * 15e-6 / 2) + assert (usage.prompt_tokens, usage.completion_tokens, usage.total_tokens) == (11000, 200, 11200) + assert models == ["claude-sonnet-4-5"] diff --git a/tests/test_litellm/caching/test_disk_cache.py b/tests/test_litellm/caching/test_disk_cache.py new file mode 100644 index 00000000000..b8d3b7b8d36 --- /dev/null +++ b/tests/test_litellm/caching/test_disk_cache.py @@ -0,0 +1,41 @@ +import pytest + +pytest.importorskip("diskcache") + +from litellm.caching.disk_cache import DiskCache + + +@pytest.fixture +def cache(tmp_path): + return DiskCache(disk_cache_dir=str(tmp_path)) + + +def test_increment_cache_starts_from_zero_when_key_missing(cache): + assert cache.increment_cache("counter", 3) == 3 + assert cache.get_cache("counter") == 3 + + +def test_increment_cache_adds_to_existing_int(cache): + cache.set_cache("counter", 7) + assert cache.increment_cache("counter", 5) == 12 + assert cache.get_cache("counter") == 12 + + +def test_increment_cache_treats_non_int_cached_value_as_zero(cache): + cache.set_cache("counter", "not-a-number") + assert cache.increment_cache("counter", 4) == 4 + assert cache.get_cache("counter") == 4 + + +async def test_async_increment_starts_from_zero_when_key_missing(cache): + assert await cache.async_increment("counter", 2) == 2 + + +async def test_async_increment_adds_to_existing_int(cache): + await cache.async_set_cache("counter", 10) + assert await cache.async_increment("counter", 5) == 15 + + +async def test_async_increment_treats_non_int_cached_value_as_zero(cache): + await cache.async_set_cache("counter", "corrupt") + assert await cache.async_increment("counter", 9) == 9 diff --git a/tests/test_litellm/caching/test_valkey_semantic_cache.py b/tests/test_litellm/caching/test_valkey_semantic_cache.py index 44b9f061998..d2df0a98e12 100644 --- a/tests/test_litellm/caching/test_valkey_semantic_cache.py +++ b/tests/test_litellm/caching/test_valkey_semantic_cache.py @@ -300,6 +300,59 @@ async def test_async_set_and_get_roundtrip(): assert metadata["semantic-similarity"] == pytest.approx(0.95) +@pytest.mark.asyncio +async def test_async_set_cache_passes_only_metadata_to_get_async_embedding(): + async_client = AsyncMock() + async_client.ft = _async_ft(0.05) + cache = _make_cache(async_client=async_client) + captured: dict[str, object] = {} + + async def spy_embedding(prompt: str, metadata: dict | None = None) -> list[float]: + captured["prompt"] = prompt + captured["metadata"] = metadata + return [0.1, 0.2, 0.3] + + cache._get_async_embedding = spy_embedding + + await cache.async_set_cache( + key="cache-key", + value={"content": "Paris"}, + messages=[{"role": "user", "content": "What is the capital of France?"}], + metadata={"user_api_key": "sk-test"}, + cache_key="abc123", + custom_llm_provider="openai", + ) + + assert captured["metadata"] == {"user_api_key": "sk-test"} + async_client.hset.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_async_get_cache_passes_only_metadata_to_get_async_embedding(): + async_client = AsyncMock() + async_client.ft = _async_ft(0.05) + cache = _make_cache(async_client=async_client) + captured: dict[str, object] = {} + + async def spy_embedding(prompt: str, metadata: dict | None = None) -> list[float]: + captured["prompt"] = prompt + captured["metadata"] = dict(metadata) if metadata is not None else None + return [0.1, 0.2, 0.3] + + cache._get_async_embedding = spy_embedding + + result = await cache.async_get_cache( + key="cache-key", + messages=[{"role": "user", "content": "What is the capital of France?"}], + metadata={"user_api_key": "sk-test"}, + cache_key="abc123", + custom_llm_provider="openai", + ) + + assert result == {"content": "Paris"} + assert captured["metadata"] == {"user_api_key": "sk-test"} + + @pytest.mark.asyncio async def test_async_get_cache_misses_below_threshold(): async_client = AsyncMock() diff --git a/tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py b/tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py index 5cabfe5fb7f..61303340570 100644 --- a/tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py +++ b/tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py @@ -1,3 +1,4 @@ +import asyncio import json import os import sys @@ -7,6 +8,7 @@ from unittest.mock import patch import pytest from fastapi.testclient import TestClient +from litellm.caching.caching import DualCache from litellm_enterprise.enterprise_callbacks.send_emails.base_email import ( BaseEmailLogger, ) @@ -346,7 +348,9 @@ async def test_get_invitation_link(base_email_logger): result = await base_email_logger._get_invitation_link( user_id="test-user", base_url="http://test.com" ) - assert result == "http://test.com/ui?invitation_id=test-invitation-id" + assert ( + result == "http://test.com/ui/onboarding?invitation_id=test-invitation-id" + ) # Test with None user_id result = await base_email_logger._get_invitation_link( @@ -370,7 +374,7 @@ def test_construct_invitation_link(base_email_logger): result = base_email_logger._construct_invitation_link( invitation_id="test-id-123", base_url="http://test.com" ) - assert result == "http://test.com/ui?invitation_id=test-id-123" + assert result == "http://test.com/ui/onboarding?invitation_id=test-id-123" @pytest.mark.asyncio @@ -406,7 +410,10 @@ async def test_get_invitation_link_creates_new_when_none_exist(base_email_logger assert call_args["user_api_key_dict"].user_id == "test-user" # Verify the returned link uses the new invitation ID - assert result == "http://test.com/ui?invitation_id=new-invitation-id" + assert ( + result + == "http://test.com/ui/onboarding?invitation_id=new-invitation-id" + ) @pytest.mark.asyncio @@ -437,7 +444,10 @@ async def test_get_invitation_link_uses_existing_when_available(base_email_logge mock_create_invitation.assert_not_called() # Verify the returned link uses the existing invitation ID - assert result == "http://test.com/ui?invitation_id=existing-invitation-id" + assert ( + result + == "http://test.com/ui/onboarding?invitation_id=existing-invitation-id" + ) @pytest.mark.asyncio @@ -473,7 +483,10 @@ async def test_get_invitation_link_creates_new_when_list_is_none(base_email_logg assert call_args["user_api_key_dict"].user_id == "test-user" # Verify the returned link uses the new invitation ID - assert result == "http://test.com/ui?invitation_id=new-invitation-from-none" + assert ( + result + == "http://test.com/ui/onboarding?invitation_id=new-invitation-from-none" + ) @pytest.mark.asyncio @@ -493,7 +506,7 @@ async def test_get_email_params_user_invitation( with mock.patch.object( base_email_logger, "_get_invitation_link", - return_value="http://test.com/ui?invitation_id=test-id", + return_value="http://test.com/ui/onboarding?invitation_id=test-id", ): # Test with user invitation event result = await base_email_logger._get_email_params( @@ -507,7 +520,9 @@ async def test_get_email_params_user_invitation( == "https://litellm-listing.s3.amazonaws.com/litellm_logo.png" ) assert result.support_contact == "support@berri.ai" - assert result.base_url == "http://test.com/ui?invitation_id=test-id" + assert ( + result.base_url == "http://test.com/ui/onboarding?invitation_id=test-id" + ) assert result.recipient_email == "test@example.com" @@ -707,10 +722,9 @@ async def test_budget_alerts_soft_budget_crossed(base_email_logger, mock_send_em event_group=Litellm_EntityType.USER, ) - # Mock the cache to return None (no previous alert sent) + # Mock the cache so the claim is won (increment returns 1) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value=None) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict( @@ -726,14 +740,14 @@ async def test_budget_alerts_soft_budget_crossed(base_email_logger, mock_send_em call_args = mock_send_email.call_args[1] assert call_args["to_email"] == ["test@example.com"] - # Verify cache was set to prevent duplicate alerts - mock_cache.async_set_cache.assert_called_once() - cache_call_args = mock_cache.async_set_cache.call_args[1] + # Verify the send slot was claimed to prevent duplicate alerts + mock_cache.async_increment_cache.assert_called_once() + cache_call_args = mock_cache.async_increment_cache.call_args[1] assert ( cache_call_args["key"] == "email_budget_alerts:soft_budget_crossed:test_user" ) - assert cache_call_args["value"] == "SENT" + assert cache_call_args["value"] == 1 assert cache_call_args["ttl"] == EMAIL_BUDGET_ALERT_TTL @@ -774,9 +788,9 @@ async def test_budget_alerts_soft_budget_duplicate_prevention( event_group=Litellm_EntityType.USER, ) - # Mock the cache to return "SENT" (previous alert already sent) + # Mock the cache so the slot is already claimed (increment returns > 1) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value="SENT") + mock_cache.async_increment_cache = mock.AsyncMock(return_value=2) base_email_logger.internal_usage_cache = mock_cache await base_email_logger.budget_alerts(type="soft_budget", user_info=user_info) @@ -818,10 +832,9 @@ async def test_budget_alerts_uses_token_for_cache_key( event_group=Litellm_EntityType.KEY, ) - # Mock the cache to return None (no previous alert sent) + # Mock the cache so the claim is won (increment returns 1) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value=None) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict( @@ -833,8 +846,8 @@ async def test_budget_alerts_uses_token_for_cache_key( await base_email_logger.budget_alerts(type="soft_budget", user_info=user_info) # Verify cache key uses token instead of user_id - mock_cache.async_set_cache.assert_called_once() - cache_call_args = mock_cache.async_set_cache.call_args[1] + mock_cache.async_increment_cache.assert_called_once() + cache_call_args = mock_cache.async_increment_cache.call_args[1] assert ( cache_call_args["key"] == "email_budget_alerts:soft_budget_crossed:hashed_token_123" @@ -880,8 +893,7 @@ async def test_budget_alerts_max_budget_alert_crossed( ) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value=None) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict( @@ -899,12 +911,12 @@ async def test_budget_alerts_max_budget_alert_crossed( assert call_args["to_email"] == ["test@example.com"] assert "Max Budget Alert" in call_args["subject"] - mock_cache.async_set_cache.assert_called_once() - cache_call_args = mock_cache.async_set_cache.call_args[1] + mock_cache.async_increment_cache.assert_called_once() + cache_call_args = mock_cache.async_increment_cache.call_args[1] assert ( cache_call_args["key"] == "email_budget_alerts:max_budget_alert:test_user" ) - assert cache_call_args["value"] == "SENT" + assert cache_call_args["value"] == 1 assert cache_call_args["ttl"] == EMAIL_BUDGET_ALERT_TTL @@ -928,8 +940,7 @@ async def test_multi_threshold_sends_crossed_thresholds( ) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value=None) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): @@ -941,7 +952,9 @@ async def test_multi_threshold_sends_crossed_thresholds( assert mock_send_email.call_count == 2 # Check cache keys include threshold percentage - cache_keys = [c[1]["key"] for c in mock_cache.async_set_cache.call_args_list] + cache_keys = [ + c[1]["key"] for c in mock_cache.async_increment_cache.call_args_list + ] assert "email_budget_alerts:max_budget_alert:50:hashed_key_1" in cache_keys assert "email_budget_alerts:max_budget_alert:75:hashed_key_1" in cache_keys @@ -964,15 +977,14 @@ async def test_multi_threshold_dedup_cache_prevents_resend( }, ) - # Simulate 50% already sent (cached), 75% not yet sent - async def cache_get(key): + # Simulate 50% already claimed (increment returns >1), 75% first send (returns 1) + async def cache_increment(key, value, ttl=None): if "50:" in key: - return "SENT" - return None + return 2 + return 1 mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(side_effect=cache_get) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(side_effect=cache_increment) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): @@ -982,7 +994,7 @@ async def test_multi_threshold_dedup_cache_prevents_resend( # Only 75% should fire assert mock_send_email.call_count == 1 - cache_key = mock_cache.async_set_cache.call_args[1]["key"] + cache_key = mock_cache.async_increment_cache.call_args[1]["key"] assert "75:" in cache_key @@ -1004,8 +1016,7 @@ async def test_multi_threshold_owner_email_auto_included( ) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value=None) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): @@ -1038,8 +1049,7 @@ async def test_multi_threshold_malformed_keys_skipped( ) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value=None) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): @@ -1069,8 +1079,7 @@ async def test_multi_threshold_empty_emails_only_owner( ) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value=None) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): @@ -1097,8 +1106,7 @@ async def test_no_map_preserves_old_single_threshold( ) mock_cache = mock.AsyncMock() - mock_cache.async_get_cache = mock.AsyncMock(return_value=None) - mock_cache.async_set_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) base_email_logger.internal_usage_cache = mock_cache with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): @@ -1110,5 +1118,263 @@ async def test_no_map_preserves_old_single_threshold( call_args = mock_send_email.call_args[1] assert call_args["to_email"] == ["test@example.com"] # Old path cache key has no threshold percentage - cache_key = mock_cache.async_set_cache.call_args[1]["key"] + cache_key = mock_cache.async_increment_cache.call_args[1]["key"] assert cache_key == "email_budget_alerts:max_budget_alert:test_user" + + +CUSTOM_SIGNATURE = "
Best,
The Acme Platform Team
" + + +@pytest.mark.asyncio +async def test_send_soft_budget_alert_email_uses_custom_signature( + base_email_logger, mock_send_email, mock_lookup_user_email +): + """Soft budget alert honors EMAIL_SIGNATURE for premium users.""" + event = WebhookEvent( + user_id="test_user", + user_email="test@example.com", + event_group=Litellm_EntityType.USER, + event="soft_budget_crossed", + event_message="Soft Budget Crossed", + spend=105.0, + max_budget=200.0, + soft_budget=100.0, + ) + with mock.patch.dict( + os.environ, + {"PROXY_BASE_URL": "http://test.com", "EMAIL_SIGNATURE": CUSTOM_SIGNATURE}, + ), patch("litellm.proxy.proxy_server.premium_user", True): + await base_email_logger.send_soft_budget_alert_email(event) + + html_body = mock_send_email.call_args[1]["html_body"] + assert CUSTOM_SIGNATURE in html_body + assert "The LiteLLM team" not in html_body + + +@pytest.mark.asyncio +async def test_send_team_soft_budget_alert_email_uses_custom_signature( + base_email_logger, mock_send_email, mock_lookup_user_email +): + """Team soft budget alert honors EMAIL_SIGNATURE for premium users.""" + event = WebhookEvent( + user_id="test_user", + event_group=Litellm_EntityType.TEAM, + event="soft_budget_crossed", + event_message="Team Soft Budget Crossed", + spend=105.0, + max_budget=200.0, + soft_budget=100.0, + team_alias="Acme", + alert_emails=["teamlead@example.com"], + ) + with mock.patch.dict( + os.environ, + {"PROXY_BASE_URL": "http://test.com", "EMAIL_SIGNATURE": CUSTOM_SIGNATURE}, + ), patch("litellm.proxy.proxy_server.premium_user", True): + await base_email_logger.send_team_soft_budget_alert_email(event) + + html_body = mock_send_email.call_args[1]["html_body"] + assert CUSTOM_SIGNATURE in html_body + assert "The LiteLLM team" not in html_body + + +@pytest.mark.asyncio +async def test_send_max_budget_alert_email_single_recipient_uses_custom_signature( + base_email_logger, mock_send_email, mock_lookup_user_email +): + """Max budget alert (single-recipient path) honors EMAIL_SIGNATURE.""" + event = WebhookEvent( + user_id="test_user", + user_email="test@example.com", + event_group=Litellm_EntityType.USER, + event="max_budget_alert", + event_message="Max Budget Alert", + spend=165.0, + max_budget=200.0, + ) + with mock.patch.dict( + os.environ, + {"PROXY_BASE_URL": "http://test.com", "EMAIL_SIGNATURE": CUSTOM_SIGNATURE}, + ), patch("litellm.proxy.proxy_server.premium_user", True): + await base_email_logger.send_max_budget_alert_email(event) + + html_body = mock_send_email.call_args[1]["html_body"] + assert CUSTOM_SIGNATURE in html_body + assert "The LiteLLM team" not in html_body + + +@pytest.mark.asyncio +async def test_send_max_budget_alert_email_multi_recipient_uses_custom_signature( + base_email_logger, mock_send_email, mock_lookup_user_email +): + """Max budget alert (multi-threshold/recipient path) honors EMAIL_SIGNATURE.""" + event = WebhookEvent( + user_id="test_user", + user_email="owner@example.com", + event_group=Litellm_EntityType.USER, + event="max_budget_alert", + event_message="Max Budget Alert", + spend=165.0, + max_budget=200.0, + ) + with mock.patch.dict( + os.environ, + {"PROXY_BASE_URL": "http://test.com", "EMAIL_SIGNATURE": CUSTOM_SIGNATURE}, + ), patch("litellm.proxy.proxy_server.premium_user", True): + await base_email_logger.send_max_budget_alert_email( + event, threshold_pct=75, recipient_emails=["a@example.com", "b@example.com"] + ) + + html_body = mock_send_email.call_args[1]["html_body"] + assert CUSTOM_SIGNATURE in html_body + assert "The LiteLLM team" not in html_body + + +@pytest.mark.asyncio +async def test_send_soft_budget_alert_email_default_footer_when_no_signature( + base_email_logger, mock_send_email, mock_lookup_user_email +): + """Without EMAIL_SIGNATURE, budget alert falls back to the default EMAIL_FOOTER.""" + event = WebhookEvent( + user_id="test_user", + user_email="test@example.com", + event_group=Litellm_EntityType.USER, + event="soft_budget_crossed", + event_message="Soft Budget Crossed", + spend=105.0, + max_budget=200.0, + soft_budget=100.0, + ) + with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): + await base_email_logger.send_soft_budget_alert_email(event) + + html_body = mock_send_email.call_args[1]["html_body"] + assert EMAIL_FOOTER in html_body + + +_BUDGET_ALERT_BRANCHES = [ + ( + "multi_threshold", + "max_budget_alert", + "send_max_budget_alert_email", + dict(max_budget=100.0, spend=80.0, max_budget_alert_emails={"50": ["finance@co.com"]}), + ), + ( + "single_threshold", + "max_budget_alert", + "send_max_budget_alert_email", + dict(max_budget=100.0, spend=85.0), + ), + ( + "soft_budget", + "soft_budget", + "send_soft_budget_alert_email", + dict(soft_budget=50.0, spend=60.0), + ), +] + + +def _budget_alert_user_info(extra: dict) -> CallInfo: + return CallInfo( + token="hashed_key_1", + user_id="test_user", + user_email="owner@co.com", + event_group=Litellm_EntityType.KEY, + **extra, + ) + + +@pytest.mark.parametrize( + "branch, alert_type, send_method, ci_kwargs", + _BUDGET_ALERT_BRANCHES, + ids=[b[0] for b in _BUDGET_ALERT_BRANCHES], +) +@pytest.mark.asyncio +async def test_budget_alert_no_duplicate_on_concurrent_crossing( + base_email_logger, branch, alert_type, send_method, ci_kwargs +): + """Regression for LIT-4172: two requests crossing the same threshold at the + same time must send exactly one email. The old code wrote the dedup marker + only after the send finished awaiting, so both concurrent tasks passed the + 'already sent' check and both sent. Covers all three send branches.""" + base_email_logger.internal_usage_cache = DualCache() + + sends = [] + + async def slow_send(*args, **kwargs): + sends.append(1) + await asyncio.sleep(0.05) + + with mock.patch.object(base_email_logger, send_method, side_effect=slow_send): + with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): + await asyncio.gather( + base_email_logger.budget_alerts( + type=alert_type, user_info=_budget_alert_user_info(ci_kwargs) + ), + base_email_logger.budget_alerts( + type=alert_type, user_info=_budget_alert_user_info(ci_kwargs) + ), + ) + + assert len(sends) == 1 + + +@pytest.mark.parametrize( + "branch, alert_type, send_method, ci_kwargs", + _BUDGET_ALERT_BRANCHES, + ids=[b[0] for b in _BUDGET_ALERT_BRANCHES], +) +@pytest.mark.asyncio +async def test_budget_alert_failed_send_releases_claim_for_retry( + base_email_logger, branch, alert_type, send_method, ci_kwargs +): + """Claiming the send slot before sending must not swallow the alert forever + if the send fails; the claim is released so a later request retries. Covers + all three send branches.""" + base_email_logger.internal_usage_cache = DualCache() + + attempts = [] + + async def flaky_send(*args, **kwargs): + attempts.append(1) + if len(attempts) == 1: + raise ValueError("transient email backend failure") + + with mock.patch.object(base_email_logger, send_method, side_effect=flaky_send): + with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): + await base_email_logger.budget_alerts( + type=alert_type, user_info=_budget_alert_user_info(ci_kwargs) + ) + await base_email_logger.budget_alerts( + type=alert_type, user_info=_budget_alert_user_info(ci_kwargs) + ) + + assert len(attempts) == 2 + + +@pytest.mark.asyncio +async def test_budget_alert_release_failure_does_not_propagate(base_email_logger): + """If the send fails and releasing the claim also fails (transient cache + error), budget_alerts must swallow it and still log the send failure rather + than letting the exception escape the fire-and-forget task.""" + mock_cache = mock.AsyncMock() + mock_cache.async_increment_cache = mock.AsyncMock(return_value=1) + mock_cache.async_delete_cache = mock.AsyncMock( + side_effect=RuntimeError("cache backend unavailable") + ) + base_email_logger.internal_usage_cache = mock_cache + + async def failing_send(*args, **kwargs): + raise ValueError("smtp backend down") + + with mock.patch.object( + base_email_logger, "send_max_budget_alert_email", side_effect=failing_send + ): + with mock.patch.dict(os.environ, {"PROXY_BASE_URL": "http://test.com"}): + # Must not raise even though both the send and the release fail. + await base_email_logger.budget_alerts( + type="max_budget_alert", + user_info=_budget_alert_user_info(dict(max_budget=100.0, spend=85.0)), + ) + + mock_cache.async_delete_cache.assert_awaited_once() diff --git a/tests/test_litellm/expected_responses_api_request/azure_shell_tool.json b/tests/test_litellm/expected_responses_api_request/azure_shell_tool.json new file mode 100644 index 00000000000..b716c518106 --- /dev/null +++ b/tests/test_litellm/expected_responses_api_request/azure_shell_tool.json @@ -0,0 +1,14 @@ +{ + "model": "gpt-5-mini", + "input": "List files in /mnt/data and run python --version.", + "tools": [ + { + "type": "shell", + "environment": { + "type": "container_auto" + } + } + ], + "tool_choice": "auto", + "max_output_tokens": 256 +} diff --git a/tests/test_litellm/experimental_mcp_client/test_mcp_client.py b/tests/test_litellm/experimental_mcp_client/test_mcp_client.py index 499d98e5cfe..c97856598f1 100644 --- a/tests/test_litellm/experimental_mcp_client/test_mcp_client.py +++ b/tests/test_litellm/experimental_mcp_client/test_mcp_client.py @@ -35,9 +35,7 @@ class TestMCPClient: def test_mcp_client_stdio_init(self): """Test MCPClient initialization with stdio config""" - stdio_config = MCPStdioConfig( - command="python", args=["-m", "my_mcp_server"], env={"DEBUG": "1"} - ) + stdio_config = MCPStdioConfig(command="python", args=["-m", "my_mcp_server"], env={"DEBUG": "1"}) client = MCPClient(transport_type=MCPTransport.stdio, stdio_config=stdio_config) @@ -53,9 +51,7 @@ class TestMCPClient: # Test missing stdio_config client = MCPClient(transport_type=MCPTransport.stdio) - with pytest.raises( - ValueError, match="stdio_config is required for stdio transport" - ): + with pytest.raises(ValueError, match="stdio_config is required for stdio transport"): async def _noop(session): return None @@ -65,9 +61,7 @@ class TestMCPClient: @pytest.mark.asyncio @patch("litellm.experimental_mcp_client.client.stdio_client") @patch("litellm.experimental_mcp_client.client.ClientSession") - async def test_mcp_client_stdio_connect_success( - self, mock_session, mock_stdio_client - ): + async def test_mcp_client_stdio_connect_success(self, mock_session, mock_stdio_client): """Test successful stdio connection""" # Setup mocks - create proper async context manager mock_transport = (MagicMock(), MagicMock()) @@ -83,9 +77,7 @@ class TestMCPClient: mock_session_ctx.__aexit__.return_value = None mock_session.return_value = mock_session_ctx - stdio_config = MCPStdioConfig( - command="python", args=["-m", "my_mcp_server"], env={"DEBUG": "1"} - ) + stdio_config = MCPStdioConfig(command="python", args=["-m", "my_mcp_server"], env={"DEBUG": "1"}) client = MCPClient(transport_type=MCPTransport.stdio, stdio_config=stdio_config) @@ -110,9 +102,7 @@ class TestMCPClient: "SSL_CERTIFICATE": "/path/to/client-cert.pem", }, ) - async def test_mcp_client_ssl_configuration_from_env( - self, mock_streamable_http_client - ): + async def test_mcp_client_ssl_configuration_from_env(self, mock_streamable_http_client): """Test that MCP client uses SSL configuration from environment variables""" # Setup mocks - create proper async context manager mock_transport = (MagicMock(), MagicMock()) @@ -122,9 +112,7 @@ class TestMCPClient: mock_streamable_http_client.return_value = mock_http_ctx # Mock the session - with patch( - "litellm.experimental_mcp_client.client.ClientSession" - ) as mock_session: + with patch("litellm.experimental_mcp_client.client.ClientSession") as mock_session: mock_session_instance = AsyncMock() mock_session_instance.initialize = AsyncMock() mock_session_ctx = AsyncMock() @@ -170,9 +158,7 @@ class TestMCPClient: mock_sse_client.return_value = mock_sse_ctx # Mock the session - with patch( - "litellm.experimental_mcp_client.client.ClientSession" - ) as mock_session: + with patch("litellm.experimental_mcp_client.client.ClientSession") as mock_session: mock_session_instance = AsyncMock() mock_session_instance.initialize = AsyncMock() mock_session_ctx = AsyncMock() @@ -224,9 +210,7 @@ class TestMCPClient: mock_streamable_http_client.return_value = mock_http_ctx # Mock the session - with patch( - "litellm.experimental_mcp_client.client.ClientSession" - ) as mock_session: + with patch("litellm.experimental_mcp_client.client.ClientSession") as mock_session: mock_session_instance = AsyncMock() mock_session_instance.initialize = AsyncMock() mock_session_ctx = AsyncMock() @@ -451,14 +435,10 @@ class TestFirstNonCancelledCause: assert _first_non_cancelled_cause(outer) is target def test_all_cancelled_returns_none(self): - group = _FakeExceptionGroup( - "g", [asyncio.CancelledError(), asyncio.CancelledError()] - ) + group = _FakeExceptionGroup("g", [asyncio.CancelledError(), asyncio.CancelledError()]) assert _first_non_cancelled_cause(group) is None - @pytest.mark.skipif( - sys.version_info < (3, 11), reason="builtin ExceptionGroup requires 3.11+" - ) + @pytest.mark.skipif(sys.version_info < (3, 11), reason="builtin ExceptionGroup requires 3.11+") def test_unwraps_builtin_exception_group(self): target = httpx.ConnectError("refused") group = ExceptionGroup("transport failed", [target]) # noqa: F821 @@ -497,9 +477,7 @@ class TestExecuteSessionOperationSurfacesTransportError: AsyncMock(side_effect=asyncio.CancelledError("cancelled by group")), ) connect_error = httpx.ConnectError("All connection attempts failed") - transport_ctx = self._make_transport( - _FakeExceptionGroup("transport", [connect_error]) - ) + transport_ctx = self._make_transport(_FakeExceptionGroup("transport", [connect_error])) async def _op(session): return "done" @@ -511,12 +489,8 @@ class TestExecuteSessionOperationSurfacesTransportError: @patch("litellm.experimental_mcp_client.client.ClientSession") async def test_genuine_cancellation_is_not_replaced(self, mock_session_cls): client = MCPClient(server_url="http://example.com/mcp", transport_type="http") - self._make_session( - mock_session_cls, AsyncMock(side_effect=asyncio.CancelledError()) - ) - transport_ctx = self._make_transport( - _FakeExceptionGroup("teardown", [asyncio.CancelledError()]) - ) + self._make_session(mock_session_cls, AsyncMock(side_effect=asyncio.CancelledError())) + transport_ctx = self._make_transport(_FakeExceptionGroup("teardown", [asyncio.CancelledError()])) async def _op(session): return "done" @@ -531,9 +505,7 @@ class TestExecuteSessionOperationSurfacesTransportError: init_result = MagicMock() init_result.instructions = None self._make_session(mock_session_cls, AsyncMock(return_value=init_result)) - transport_ctx = self._make_transport( - _FakeExceptionGroup("late", [httpx.ConnectError("late cleanup error")]) - ) + transport_ctx = self._make_transport(_FakeExceptionGroup("late", [httpx.ConnectError("late cleanup error")])) async def _op(session): return "done" @@ -548,9 +520,7 @@ class TestMCPClientResolvedAuth: @pytest.mark.asyncio async def test_resolved_auth_feeds_the_auth_slot(self): resolved = httpx.Auth() - client = MCPClient( - server_url="https://upstream.example.com", resolved_auth=resolved - ) + client = MCPClient(server_url="https://upstream.example.com", resolved_auth=resolved) http_client = client._create_httpx_client_factory()() try: assert http_client.auth is resolved @@ -598,9 +568,7 @@ async def test_call_tool_does_not_log_arguments(): secret = "ssn-123-45-6789" client = MCPClient(server_url="http://test-server") client.run_with_session = AsyncMock(return_value=MagicMock()) - params = CallToolRequestParams( - name="search_tool", arguments={"input": secret, "model": "gpt-5-mini"} - ) + params = CallToolRequestParams(name="search_tool", arguments={"input": secret, "model": "gpt-5-mini"}) with patch.object(mcp_client_module, "verbose_logger") as mock_logger: await client.call_tool(params) @@ -630,3 +598,100 @@ async def test_get_prompt_does_not_log_arguments(): if __name__ == "__main__": pytest.main([__file__]) + + +@pytest.mark.asyncio +async def test_call_tool_raise_on_error_logs_at_debug_not_error(): + """When the caller opts into raise_on_error it owns the exception and logs it at the fitting + level (an expected pass-through re-auth 401 is info, not error). call_tool must therefore not emit + its own error-level line in that mode, so error-rate alerts do not trip on the expected signal; + the swallow path (raise_on_error=False) still logs at error since nothing downstream will.""" + from mcp.types import CallToolRequestParams + + client = MCPClient(transport_type=MCPTransport.stdio) + boom = RuntimeError("upstream boom") + + async def _raise(_operation, **_kwargs): + raise boom + + params = CallToolRequestParams(name="t", arguments={}) + + with patch.object(client, "run_with_session", side_effect=_raise) as mock_rws: + with patch.object(mcp_client_module, "verbose_logger") as mock_log: + with pytest.raises(RuntimeError): + await client.call_tool(params, raise_on_error=True) + assert not mock_log.error.called, "raise_on_error path must not log at error" + debug_msgs = [str(c.args[0]) for c in mock_log.debug.call_args_list if c.args] + assert any("call_tool failed" in m for m in debug_msgs), "the demoted failure line must go to debug" + assert mock_rws.call_args.kwargs.get("quiet_on_error") is True, ( + "call_tool must forward quiet_on_error so run_with_session also demotes its own failure line" + ) + + with patch.object(client, "run_with_session", side_effect=_raise): + with patch.object(mcp_client_module, "verbose_logger") as mock_log: + result = await client.call_tool(params, raise_on_error=False) + assert result.isError is True + assert mock_log.error.called, "swallow path must keep error-level visibility" + + +@pytest.mark.asyncio +async def test_list_tools_raise_on_error_logs_at_debug_not_error(): + """list_tools must mirror call_tool: when the caller opts into raise_on_error it owns the + exception, so an expected pass-through re-auth 401 does not emit an error/exception line that + would trip error-rate alerts. The swallow path still logs the full exception.""" + client = MCPClient(transport_type=MCPTransport.stdio) + boom = RuntimeError("upstream boom") + + async def _raise(_operation, **_kwargs): + raise boom + + with patch.object(client, "run_with_session", side_effect=_raise) as mock_rws: + with patch.object(mcp_client_module, "verbose_logger") as mock_log: + with pytest.raises(RuntimeError): + await client.list_tools(raise_on_error=True) + assert not mock_log.error.called, "raise_on_error path must not log at error" + assert not mock_log.exception.called, "raise_on_error path must not log a traceback" + debug_msgs = [str(c.args[0]) for c in mock_log.debug.call_args_list if c.args] + assert any("list_tools failed" in m for m in debug_msgs), "the demoted failure line must go to debug" + assert mock_rws.call_args.kwargs.get("quiet_on_error") is True, ( + "list_tools must forward quiet_on_error so run_with_session also demotes its own failure line" + ) + + with patch.object(client, "run_with_session", side_effect=_raise): + with patch.object(mcp_client_module, "verbose_logger") as mock_log: + result = await client.list_tools(raise_on_error=False) + assert result == [] + assert mock_log.exception.called, "swallow path must keep full exception visibility" + + +@pytest.mark.asyncio +async def test_run_with_session_quiet_on_error_demotes_warning_to_debug(): + """run_with_session logs its failure at warning by default (an operator signal for an unexpected + outage), but when the caller owns the exception (quiet_on_error=True, set by call_tool / list_tools + under raise_on_error) it must demote that line to debug so an expected pass-through re-auth does not + emit a warning per call.""" + client = MCPClient(transport_type=MCPTransport.stdio) + boom = RuntimeError("session boom") + + async def _op(_session): + raise boom + + async def _fake_exec(_transport_ctx, _operation): + raise boom + + with patch.object(client, "_create_transport_context", return_value=(object(), None)): + with patch.object(client, "_execute_session_operation", side_effect=_fake_exec): + with patch.object(mcp_client_module, "verbose_logger") as mock_log: + with pytest.raises(RuntimeError): + await client.run_with_session(_op, quiet_on_error=True) + assert not mock_log.warning.called, "quiet_on_error must not emit a warning" + debug_msgs = [str(c.args[0]) for c in mock_log.debug.call_args_list if c.args] + assert any("run_with_session failed" in m for m in debug_msgs), "the failure line must go to debug" + + with patch.object(mcp_client_module, "verbose_logger") as mock_log: + with pytest.raises(RuntimeError): + await client.run_with_session(_op) + warning_msgs = [str(c.args[0]) for c in mock_log.warning.call_args_list if c.args] + assert any("run_with_session failed" in m for m in warning_msgs), ( + "the default path must keep the operator-visible warning" + ) diff --git a/tests/test_litellm/integrations/code_interpreter_interception/test_handler.py b/tests/test_litellm/integrations/code_interpreter_interception/test_handler.py index 7ff58ba6324..9d308ac1989 100644 --- a/tests/test_litellm/integrations/code_interpreter_interception/test_handler.py +++ b/tests/test_litellm/integrations/code_interpreter_interception/test_handler.py @@ -14,6 +14,7 @@ from litellm.integrations.code_interpreter_interception.handler import ( LITELLM_CODE_EXECUTION_TOOL_NAME, _INTERCEPTION_ACTIVE_KEY as _ACTIVE_KEY, _SANDBOX_KEY, + _SESSION_SCOPED_KEY, ) from litellm.types.integrations.custom_logger import ( CHAT_COMPLETION_AGENTIC_SURFACE, @@ -138,11 +139,7 @@ async def test_build_plan_runs_code_and_feeds_output_back(): assert sandbox.run_calls[0]["code"] == "print(40 + 2)" messages = _iter_messages(plan) - outputs = [ - m - for m in messages - if isinstance(m, dict) and m.get("type") == "function_call_output" - ] + outputs = [m for m in messages if isinstance(m, dict) and m.get("type") == "function_call_output"] assert outputs, "expected a function_call_output item appended" output_item = next(m for m in outputs if m.get("call_id") == "c1") assert "42" in str(output_item["output"]) @@ -160,9 +157,7 @@ async def test_pre_call_converts_code_interpreter_tool(): assert result is not None tools = result["tools"] - assert not any( - t.get("type") == "code_interpreter" for t in tools - ), "code_interpreter tool must be removed" + assert not any(t.get("type") == "code_interpreter" for t in tools), "code_interpreter tool must be removed" names = [t.get("name") or (t.get("function") or {}).get("name") for t in tools] assert LITELLM_CODE_EXECUTION_TOOL_NAME in names @@ -267,9 +262,7 @@ async def test_should_run_detects_only_matching_function_call(): logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox()) active_kwargs = {"_code_interpreter_interception_active": True} - match = FakeResponse( - output=[_function_call_item(name=LITELLM_CODE_EXECUTION_TOOL_NAME)] - ) + match = FakeResponse(output=[_function_call_item(name=LITELLM_CODE_EXECUTION_TOOL_NAME)]) should_run, payload = await logger.async_should_run_agentic_loop( response=match, model="gpt-5", @@ -331,9 +324,7 @@ async def test_container_reused_within_request_via_server_sandbox_key(): **common, ) - assert ( - len(sandbox.create_calls) == 1 - ), "the sandbox is reused across loop iterations sharing one server sandbox key" + assert len(sandbox.create_calls) == 1, "the sandbox is reused across loop iterations sharing one server sandbox key" @pytest.mark.asyncio @@ -372,9 +363,9 @@ async def test_colliding_caller_call_id_does_not_share_sandbox(): **common, ) - assert ( - len(sandbox.create_calls) == 2 - ), "distinct server sandbox keys must isolate sandboxes despite a colliding call id" + assert len(sandbox.create_calls) == 2, ( + "distinct server sandbox keys must isolate sandboxes despite a colliding call id" + ) @pytest.mark.asyncio @@ -479,14 +470,11 @@ async def test_post_hook_injects_code_interpreter_call_matching_openai_shape(): ) response = FakeResponse(output=[{"type": "message", "content": []}]) - out = await logger.async_post_agentic_loop_response_hook( - response=response, plan=plan, kwargs={} - ) + out = await logger.async_post_agentic_loop_response_hook(response=response, plan=plan, kwargs={}) types = [item.get("type") for item in out.output] assert types == ["code_interpreter_call", "message"], ( - "code_interpreter_call must be re-injected before the message, matching " - "OpenAI's native output ordering" + "code_interpreter_call must be re-injected before the message, matching OpenAI's native output ordering" ) assert set(out.output[0].keys()) == { "id", @@ -524,8 +512,7 @@ async def test_pre_call_forces_non_stream_for_loop(): assert out is not None assert out["stream"] is False, "loop requires a non-streaming upstream call" assert out["_code_interpreter_interception_converted_stream"] is True, ( - "the converted-stream flag must be set so the final response is wrapped " - "back into a stream for the caller" + "the converted-stream flag must be set so the final response is wrapped back into a stream for the caller" ) @@ -556,9 +543,7 @@ async def test_gate_refuses_without_server_active_marker(): """A forged litellm_code_execution call must not trigger the loop unless the pre-call hook actually converted a native code_interpreter tool.""" logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox()) - forged = FakeResponse( - output=[_function_call_item(name=LITELLM_CODE_EXECUTION_TOOL_NAME)] - ) + forged = FakeResponse(output=[_function_call_item(name=LITELLM_CODE_EXECUTION_TOOL_NAME)]) should_run, payload = await logger.async_should_run_agentic_loop( response=forged, @@ -577,12 +562,8 @@ async def test_gate_refuses_without_server_active_marker(): @pytest.mark.asyncio async def test_gate_rechecks_provider_scope(): """enabled_providers must be re-enforced at the gate, not only in pre-call.""" - logger = CodeInterpreterInterceptionLogger( - sandbox_config=FakeSandbox(), enabled_providers=["openai"] - ) - response = FakeResponse( - output=[_function_call_item(name=LITELLM_CODE_EXECUTION_TOOL_NAME)] - ) + logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox(), enabled_providers=["openai"]) + response = FakeResponse(output=[_function_call_item(name=LITELLM_CODE_EXECUTION_TOOL_NAME)]) should_run, _ = await logger.async_should_run_agentic_loop( response=response, @@ -600,11 +581,7 @@ async def test_gate_rechecks_provider_scope(): @pytest.mark.asyncio async def test_chat_completion_gate_detects_code_execution_tool_call(): logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox()) - response = { - "choices": [ - {"message": {"tool_calls": [_chat_function_call_item(call_id="call_123")]}} - ] - } + response = {"choices": [{"message": {"tool_calls": [_chat_function_call_item(call_id="call_123")]}}]} should_run, payload = await logger.async_should_run_agentic_loop( response=response, @@ -661,9 +638,7 @@ async def test_chat_completion_build_plan_runs_code_and_appends_tool_message(): }, model="gpt-5", messages=[{"role": "user", "content": "x"}], - response={ - "choices": [{"message": {"tool_calls": [_chat_function_call_item()]}}] - }, + response={"choices": [{"message": {"tool_calls": [_chat_function_call_item()]}}]}, anthropic_messages_provider_config=None, anthropic_messages_optional_request_params={ "tools": [native_chat_tool], @@ -738,8 +713,7 @@ async def test_pre_call_strips_client_forged_marker_on_initial_request(): await logger.async_pre_call_deployment_hook(kwargs, CallTypes.aresponses) assert _ACTIVE_KEY not in kwargs, ( - "no native code_interpreter tool was present, so a client-supplied " - "active marker must be cleared" + "no native code_interpreter tool was present, so a client-supplied active marker must be cleared" ) assert kwargs["litellm_metadata"] == {"safe_user_value": "kept"} @@ -774,8 +748,7 @@ async def test_pre_call_strips_forged_loop_controls_then_mints_own_markers(): assert metadata[_ACTIVE_KEY] is True assert metadata[_SANDBOX_KEY] == result[_SANDBOX_KEY] assert metadata[_SANDBOX_KEY] != "client-forged", ( - "the surviving sandbox key must be the server-minted one, not the forged " - "value the client supplied" + "the surviving sandbox key must be the server-minted one, not the forged value the client supplied" ) @@ -793,8 +766,7 @@ async def test_pre_call_preserves_marker_on_server_followup(): await logger.async_pre_call_deployment_hook(kwargs, CallTypes.aresponses) assert kwargs.get(_ACTIVE_KEY) is True, ( - "the server-set marker must survive followup requests so multi-round " - "code execution keeps working" + "the server-set marker must survive followup requests so multi-round code execution keeps working" ) @@ -805,9 +777,7 @@ async def test_sandbox_deleted_after_loop_completes(): plan = await _build_plan(logger, sandbox, call_id="k1") assert sandbox.create_calls, "sandbox must be created during the loop" - assert ( - not sandbox.delete_calls - ), "sandbox must outlive the loop until the final hook" + assert not sandbox.delete_calls, "sandbox must outlive the loop until the final hook" await logger.async_post_agentic_loop_response_hook( response=FakeResponse(output=[{"type": "message", "content": []}]), @@ -816,8 +786,7 @@ async def test_sandbox_deleted_after_loop_completes(): ) assert len(sandbox.delete_calls) == 1, ( - "the sandbox must be deleted once the final response is assembled, " - "otherwise it keeps running and billing" + "the sandbox must be deleted once the final response is assembled, otherwise it keeps running and billing" ) assert "sbxkey1" not in logger._container_cache @@ -829,16 +798,11 @@ async def test_post_hook_delete_is_idempotent_across_loop_levels(): plan = await _build_plan(logger, sandbox, call_id="k1") response = FakeResponse(output=[{"type": "message", "content": []}]) - await logger.async_post_agentic_loop_response_hook( - response=response, plan=plan, kwargs={} - ) - await logger.async_post_agentic_loop_response_hook( - response=response, plan=plan, kwargs={} - ) + await logger.async_post_agentic_loop_response_hook(response=response, plan=plan, kwargs={}) + await logger.async_post_agentic_loop_response_hook(response=response, plan=plan, kwargs={}) assert len(sandbox.delete_calls) == 1, ( - "deleting an already-removed container must be a no-op so unwinding " - "loop levels do not double-delete" + "deleting an already-removed container must be a no-op so unwinding loop levels do not double-delete" ) @@ -860,8 +824,7 @@ async def test_build_plan_deletes_sandbox_when_execution_raises(): assert len(sandbox.create_calls) == 1, "the sandbox must have been created" assert len(sandbox.delete_calls) == 1, ( - "a build failure must delete the cached sandbox so it does not keep " - "running and billing" + "a build failure must delete the cached sandbox so it does not keep running and billing" ) assert "sbxkey1" not in logger._container_cache @@ -875,8 +838,7 @@ async def test_cleanup_hook_deletes_sandbox(): await logger.async_agentic_loop_cleanup_hook(plan=plan, kwargs={}) assert len(sandbox.delete_calls) == 1, ( - "the cleanup hook must delete the sandbox so a rerun failure cannot " - "leak a running container" + "the cleanup hook must delete the sandbox so a rerun failure cannot leak a running container" ) assert "sbxkey1" not in logger._container_cache @@ -895,8 +857,7 @@ async def test_cleanup_hook_is_idempotent_with_post_hook(): await logger.async_agentic_loop_cleanup_hook(plan=plan, kwargs={}) assert len(sandbox.delete_calls) == 1, ( - "cleanup running in finally after the success-path post hook already " - "deleted the sandbox must not double-delete" + "cleanup running in finally after the success-path post hook already deleted the sandbox must not double-delete" ) @@ -923,9 +884,7 @@ async def test_responses_plan_cleans_up_sandbox_when_followup_raises(): plan = AgenticLoopPlan( run_agentic_loop=True, - request_patch=AgenticLoopRequestPatch( - model="gpt-5", messages=[{"role": "user", "content": "x"}] - ), + request_patch=AgenticLoopRequestPatch(model="gpt-5", messages=[{"role": "user", "content": "x"}]), metadata={"sandbox_key": "sbxkey1"}, ) @@ -995,9 +954,7 @@ async def test_run_code_does_not_re_resolve_registry(monkeypatch): sandbox_tools.clear_sandbox_tools() - stdout = await logger._run_tool_call( - container=container, params=params, arguments='{"code":"print(1)"}' - ) + stdout = await logger._run_tool_call(container=container, params=params, arguments='{"code":"print(1)"}') finally: sandbox_tools.clear_sandbox_tools() @@ -1013,9 +970,7 @@ async def test_run_tool_call_surfaces_execution_error(): class ErroringSandbox(FakeSandbox): async def arun_code(self, *, container, code, **kwargs): self.run_calls.append({"container": container, "code": code}) - return CodeExecutionResult( - stdout="", error={"name": "ValueError", "value": "boom"} - ) + return CodeExecutionResult(stdout="", error={"name": "ValueError", "value": "boom"}) sandbox = ErroringSandbox() logger = CodeInterpreterInterceptionLogger(sandbox_config=sandbox) @@ -1036,9 +991,7 @@ async def test_run_tool_call_reports_unparseable_arguments(): logger = CodeInterpreterInterceptionLogger(sandbox_config=sandbox) container = await logger._create_container() - stdout = await logger._run_tool_call( - container=container[0], params=None, arguments="not-json" - ) + stdout = await logger._run_tool_call(container=container[0], params=None, arguments="not-json") assert stdout == "[invalid tool arguments: could not parse code]" assert not sandbox.run_calls, "code must not run when arguments cannot be parsed" @@ -1048,9 +1001,7 @@ async def test_run_tool_call_reports_unparseable_arguments(): async def test_pre_call_skips_provider_outside_scope(): """enabled_providers must filter the pre-call conversion so a request to an out-of-scope provider is left untouched.""" - logger = CodeInterpreterInterceptionLogger( - sandbox_config=FakeSandbox(), enabled_providers=["openai"] - ) + logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox(), enabled_providers=["openai"]) kwargs = { "tools": [{"type": "code_interpreter", "container": {"type": "auto"}}], "custom_llm_provider": "anthropic", @@ -1119,6 +1070,7 @@ async def test_prune_expired_cache_deletes_underlying_container(): container, params, time.time() - handler_mod._CACHE_TTL_SECONDS - 1, + None, ) await logger._prune_expired_cache() @@ -1217,3 +1169,258 @@ async def test_extract_tool_calls_reads_object_attributes(): assert len(calls) == 1 assert calls[0]["call_id"] == "c9" assert calls[0]["arguments"] == '{"code":"print(1)"}' + + +# --------------------------------------------------------------------------- +# Sticky session tests +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_pre_call_uses_session_id_from_metadata_as_sandbox_key(): + """When session_id is in request metadata, it becomes the sandbox key so the + container is shared across requests in the same session.""" + logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox()) + session_id = "conv-abc-123" + kwargs = { + "tools": [{"type": "code_interpreter", "container": {"type": "auto"}}], + "custom_llm_provider": "openai", + "metadata": {"session_id": session_id}, + } + + result = await logger.async_pre_call_deployment_hook(kwargs, CallTypes.acompletion) + + assert result is not None + assert result[_SANDBOX_KEY] == session_id + assert result[_SESSION_SCOPED_KEY] is True + assert result["litellm_metadata"][_SANDBOX_KEY] == session_id + assert result["litellm_metadata"][_SESSION_SCOPED_KEY] is True + + +@pytest.mark.asyncio +async def test_pre_call_uses_session_id_from_litellm_metadata(): + """session_id in litellm_metadata also works as the sticky key.""" + logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox()) + session_id = "sess-xyz-789" + kwargs = { + "tools": [{"type": "code_interpreter", "container": {"type": "auto"}}], + "custom_llm_provider": "openai", + "litellm_metadata": {"session_id": session_id}, + } + + result = await logger.async_pre_call_deployment_hook(kwargs, CallTypes.acompletion) + + assert result is not None + assert result[_SANDBOX_KEY] == session_id + assert result[_SESSION_SCOPED_KEY] is True + + +@pytest.mark.asyncio +async def test_pre_call_without_session_id_still_mints_random_key(): + """Requests without a session_id still get a server-minted random sandbox key.""" + logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox()) + kwargs = { + "tools": [{"type": "code_interpreter", "container": {"type": "auto"}}], + "custom_llm_provider": "openai", + } + + result = await logger.async_pre_call_deployment_hook(kwargs, CallTypes.acompletion) + + assert result is not None + assert _SESSION_SCOPED_KEY not in result or result[_SESSION_SCOPED_KEY] is False + assert len(result[_SANDBOX_KEY]) >= 16 + + +@pytest.mark.asyncio +async def test_session_scoped_sandbox_survives_agentic_loop_cleanup(): + """A session-scoped sandbox must NOT be deleted by the cleanup or post hooks; + it needs to persist across requests within the same session.""" + sandbox = FakeSandbox(stdout="42") + logger = CodeInterpreterInterceptionLogger(sandbox_config=sandbox) + session_id = "conv-persist-me" + + plan = await logger.async_build_agentic_loop_plan( + tools={ + "tool_calls": [ + { + "call_id": "c1", + "name": LITELLM_CODE_EXECUTION_TOOL_NAME, + "arguments": '{"code":"x = 10"}', + } + ] + }, + model="gpt-4o-mini", + messages=[{"role": "user", "content": "set x"}], + response=FakeResponse(output=[_function_call_item()]), + anthropic_messages_provider_config=None, + anthropic_messages_optional_request_params={"tools": []}, + logging_obj=FakeLogging(litellm_call_id="k1"), + stream=False, + kwargs={ + "litellm_call_id": "k1", + _SANDBOX_KEY: session_id, + _SESSION_SCOPED_KEY: True, + }, + ) + + assert plan.metadata["is_session_scoped"] is True + + await logger.async_post_agentic_loop_response_hook( + response=FakeResponse(output=[{"type": "message", "content": []}]), + plan=plan, + kwargs={}, + ) + await logger.async_agentic_loop_cleanup_hook(plan=plan, kwargs={}) + + assert not sandbox.delete_calls, ( + "session-scoped sandbox must not be deleted after a single agentic loop; " + "it must persist for the next request in the session" + ) + assert session_id in logger._container_cache, "session-scoped container must remain in cache after loop ends" + + +@pytest.mark.asyncio +async def test_session_scoped_sandbox_reused_across_sequential_requests(): + """Two sequential requests with the same session_id must share one container, + confirming state (e.g. assigned variables) can persist across HTTP requests.""" + sandbox = FakeSandbox(stdout="42") + logger = CodeInterpreterInterceptionLogger(sandbox_config=sandbox) + session_id = "conv-reuse-me" + + common_plan_args = dict( + tools={ + "tool_calls": [ + { + "call_id": "c1", + "name": LITELLM_CODE_EXECUTION_TOOL_NAME, + "arguments": '{"code":"print(1)"}', + } + ] + }, + model="gpt-4o-mini", + messages=[{"role": "user", "content": "x"}], + response=FakeResponse(output=[_function_call_item()]), + anthropic_messages_provider_config=None, + anthropic_messages_optional_request_params={"tools": []}, + stream=False, + ) + session_kwargs = {_SANDBOX_KEY: session_id, _SESSION_SCOPED_KEY: True} + + plan1 = await logger.async_build_agentic_loop_plan( + logging_obj=FakeLogging(litellm_call_id="req1"), + kwargs={"litellm_call_id": "req1", **session_kwargs}, + **common_plan_args, + ) + await logger.async_post_agentic_loop_response_hook( + response=FakeResponse(output=[{"type": "message", "content": []}]), + plan=plan1, + kwargs={}, + ) + + plan2 = await logger.async_build_agentic_loop_plan( + logging_obj=FakeLogging(litellm_call_id="req2"), + kwargs={"litellm_call_id": "req2", **session_kwargs}, + **common_plan_args, + ) + await logger.async_post_agentic_loop_response_hook( + response=FakeResponse(output=[{"type": "message", "content": []}]), + plan=plan2, + kwargs={}, + ) + + assert len(sandbox.create_calls) == 1, ( + "a single container must serve both requests in the same session; " + "two creates means state cannot persist between requests" + ) + assert len(sandbox.delete_calls) == 0, "the session container must still be alive after both requests complete" + + +@pytest.mark.asyncio +async def test_non_session_sandbox_still_deleted_after_loop(): + """Without a session_id, the existing per-request ephemeral behavior is unchanged.""" + sandbox = FakeSandbox(stdout="42") + logger = CodeInterpreterInterceptionLogger(sandbox_config=sandbox) + + plan = await _build_plan(logger, sandbox, call_id="k1") + await logger.async_post_agentic_loop_response_hook( + response=FakeResponse(output=[{"type": "message", "content": []}]), + plan=plan, + kwargs={}, + ) + + assert len(sandbox.delete_calls) == 1, "non-session sandbox must still be cleaned up after each request" + + +@pytest.mark.asyncio +async def test_sandbox_key_scoped_to_api_key_hash_isolates_users(): + """Two callers supplying the same session_id but different API key hashes must + each get their own sandbox; sharing across tenants would let one read or mutate + the other's interpreter state.""" + logger = CodeInterpreterInterceptionLogger(sandbox_config=FakeSandbox()) + session_id = "same-session-id" + + result_a = await logger.async_pre_call_deployment_hook( + { + "tools": [{"type": "code_interpreter"}], + "custom_llm_provider": "openai", + "metadata": {"session_id": session_id}, + "user_api_key_hash": "hash-for-tenant-a", + }, + CallTypes.acompletion, + ) + result_b = await logger.async_pre_call_deployment_hook( + { + "tools": [{"type": "code_interpreter"}], + "custom_llm_provider": "openai", + "metadata": {"session_id": session_id}, + "user_api_key_hash": "hash-for-tenant-b", + }, + CallTypes.acompletion, + ) + + assert result_a is not None and result_b is not None + assert result_a[_SANDBOX_KEY] != result_b[_SANDBOX_KEY], ( + "same session_id from different API keys must yield different sandbox keys; " + "otherwise tenant A can read tenant B's sandbox state" + ) + assert "hash-for-tenant-a" in result_a[_SANDBOX_KEY] + assert "hash-for-tenant-b" in result_b[_SANDBOX_KEY] + + +@pytest.mark.asyncio +async def test_per_identity_cap_evicts_lru_session(): + """When a single identity holds the cap limit of session sandboxes and opens a + new one, the least-recently-used session is evicted so the allocation stays + bounded. Without this, rotating session IDs is an unbounded sandbox leak.""" + from litellm.integrations.code_interpreter_interception.handler import _SESSION_SCOPED_PER_IDENTITY_CAP + + sandbox = FakeSandbox(stdout="ok") + logger = CodeInterpreterInterceptionLogger(sandbox_config=sandbox) + identity = "hash-for-identity-x" + + for i in range(_SESSION_SCOPED_PER_IDENTITY_CAP): + await logger._get_or_create_container( + cache_key=f"{identity}:session-{i}", + identity=identity, + ) + logger._container_cache[f"{identity}:session-{i}"] = ( + logger._container_cache[f"{identity}:session-{i}"][0], + logger._container_cache[f"{identity}:session-{i}"][1], + float(i), + identity, + ) + + assert len(logger._container_cache) == _SESSION_SCOPED_PER_IDENTITY_CAP + + await logger._get_or_create_container( + cache_key=f"{identity}:session-new", + identity=identity, + ) + + assert len(logger._container_cache) == _SESSION_SCOPED_PER_IDENTITY_CAP, ( + "adding a new session beyond the cap must evict one entry so total stays bounded" + ) + assert f"{identity}:session-0" not in logger._container_cache, ( + "the entry with the oldest last_accessed timestamp must be evicted first (LRU)" + ) + assert len(sandbox.delete_calls) == 1, "evicted sandbox must be deleted, not just removed from cache" diff --git a/tests/test_litellm/integrations/datadog/test_datadog_logger_batching.py b/tests/test_litellm/integrations/datadog/test_datadog_logger_batching.py index d1c7a4032fb..f645379a4f4 100644 --- a/tests/test_litellm/integrations/datadog/test_datadog_logger_batching.py +++ b/tests/test_litellm/integrations/datadog/test_datadog_logger_batching.py @@ -1,3 +1,4 @@ +import asyncio from unittest.mock import AsyncMock, Mock, patch import httpx @@ -6,16 +7,20 @@ from httpx import Request, Response from litellm.integrations.datadog.datadog import DataDogLogger from litellm.llms.custom_httpx.http_handler import MaskedHTTPStatusError -from litellm.types.integrations.datadog import DD_MAX_BATCH_SIZE, DatadogPayload +from litellm.types.integrations.datadog import ( + DD_MAX_BATCH_SIZE, + DD_MAX_PAYLOAD_SIZE_BYTES, + DatadogPayload, +) -def _payloads(n): +def _payloads(n, message=None): return [ DatadogPayload( ddsource="litellm", ddtags="env:test", hostname="host", - message=f'{{"event": {i}}}', + message=f"{message}{i}" if message else f'{{"event": {i}}}', service="svc", status="info", ) @@ -177,6 +182,87 @@ async def test_413_returned_response_also_splits(datadog_env): assert logger.log_queue == [] +def _make_recording_send(sent_batches, delivered): + async def _send(data): + sent_batches.append(list(data)) + delivered.extend(data) + return Response( + 202, request=Request("POST", "https://example.com"), text="Accepted" + ) + + return _send + + +@pytest.mark.asyncio +async def test_oversized_payload_splits_before_any_send(datadog_env): + """Regression for LIT-4325: a batch above Datadog's uncompressed payload limit is + split proactively, so the intake never has to reject it with a 413.""" + from litellm.litellm_core_utils.safe_json_dumps import safe_dumps + + with patch("asyncio.create_task"): + logger = DataDogLogger() + + events = _payloads(3, message="x" * 3_000_000) + logger.log_queue = list(events) + sent_batches: list = [] + delivered: list = [] + logger.async_send_compressed_data = AsyncMock( + side_effect=_make_recording_send(sent_batches, delivered) + ) + + await logger.async_send_batch() + + assert delivered == events + assert len(sent_batches) == 3 + assert all( + len(safe_dumps(batch).encode("utf-8")) <= DD_MAX_PAYLOAD_SIZE_BYTES + for batch in sent_batches + ) + assert logger.log_queue == [] + + +@pytest.mark.asyncio +async def test_batch_over_max_event_count_splits_before_any_send(datadog_env): + """Datadog caps a payload at 1000 events; a queue that grew past that (e.g. after + re-queues) must be sent in count-compliant chunks.""" + with patch("asyncio.create_task"): + logger = DataDogLogger() + + events = _payloads(DD_MAX_BATCH_SIZE + 1) + logger.log_queue = list(events) + sent_batches: list = [] + delivered: list = [] + logger.async_send_compressed_data = AsyncMock( + side_effect=_make_recording_send(sent_batches, delivered) + ) + + await logger.async_send_batch() + + assert delivered == events + assert all(len(batch) <= DD_MAX_BATCH_SIZE for batch in sent_batches) + assert logger.log_queue == [] + + +@pytest.mark.asyncio +async def test_single_event_above_payload_cap_is_still_sent(datadog_env): + """A lone event over the byte cap cannot be split further; it must be sent once + (Datadog decides), never looped on.""" + with patch("asyncio.create_task"): + logger = DataDogLogger() + + logger.log_queue = _payloads(1, message="x" * (DD_MAX_PAYLOAD_SIZE_BYTES + 1)) + sent_batches: list = [] + delivered: list = [] + send = AsyncMock(side_effect=_make_recording_send(sent_batches, delivered)) + logger.async_send_compressed_data = send + + await asyncio.wait_for(logger.async_send_batch(), timeout=10) + + assert send.await_count == 1 + assert len(delivered) == 1 + assert logger.log_queue == [] + + @pytest.mark.asyncio async def test_partial_delivery_then_transient_error_requeues_only_undelivered( datadog_env, diff --git a/tests/test_litellm/integrations/otel/test_otel_v2_components.py b/tests/test_litellm/integrations/otel/test_otel_v2_components.py index 19eef284b91..5191414edeb 100644 --- a/tests/test_litellm/integrations/otel/test_otel_v2_components.py +++ b/tests/test_litellm/integrations/otel/test_otel_v2_components.py @@ -485,6 +485,74 @@ def test_build_span_exporter_variants(): OpenTelemetryV2Config(exporter="otlp_http", endpoint="http://h:4318") ) assert "OTLPSpanExporter" in type(http_exporter).__name__ + + +def test_otlp_logs_endpoint_normalization(): + norm = providers._otlp_logs_endpoint + # A base endpoint gets the signal path appended (the common OTLP env shape). + assert norm("http://collector:4318") == "http://collector:4318/v1/logs" + assert norm("http://collector:4318/") == "http://collector:4318/v1/logs" + # An already-correct path is left intact. + assert norm("http://collector:4318/v1/logs") == "http://collector:4318/v1/logs" + # A sibling signal's path is rewritten to logs, so one OTEL_ENDPOINT works + # for every signal rather than POSTing events at the traces path. + assert norm("http://collector:4318/v1/traces") == "http://collector:4318/v1/logs" + assert norm("http://collector:4318/v1/metrics") == "http://collector:4318/v1/logs" + assert norm(None) is None + + +def test_build_log_exporter_variants(): + from opentelemetry.sdk._logs.export import ConsoleLogExporter, InMemoryLogExporter + + assert isinstance( + providers.build_log_exporter(OpenTelemetryV2Config(exporter="console")), + ConsoleLogExporter, + ) + assert isinstance( + providers.build_log_exporter(OpenTelemetryV2Config(exporter="in_memory")), + InMemoryLogExporter, + ) + # An unrecognized kind falls back to console rather than dropping events. + assert isinstance( + providers.build_log_exporter(OpenTelemetryV2Config(exporter="unknown")), + ConsoleLogExporter, + ) + http_exporter = providers.build_log_exporter( + OpenTelemetryV2Config(exporter="otlp_http", endpoint="http://h:4318") + ) + assert "OTLPLogExporter" in type(http_exporter).__name__ + + +def test_build_logger_provider_picks_processor_by_exporter_kind(): + """Console and in-memory exporters export synchronously (tests depend on it); + every other destination gets the batch processor.""" + from opentelemetry.sdk._logs.export import ( + BatchLogRecordProcessor, + ConsoleLogExporter, + InMemoryLogExporter, + SimpleLogRecordProcessor, + ) + + cfg = OpenTelemetryV2Config(exporter="in_memory") + + def processor_of(provider): + return provider._multi_log_record_processor._log_record_processors[0] + + assert isinstance( + processor_of(providers.build_logger_provider(cfg, log_exporter=InMemoryLogExporter())), + SimpleLogRecordProcessor, + ) + assert isinstance( + processor_of(providers.build_logger_provider(cfg, log_exporter=ConsoleLogExporter())), + SimpleLogRecordProcessor, + ) + http_exporter = providers.build_log_exporter( + OpenTelemetryV2Config(exporter="otlp_http", endpoint="http://h:4318") + ) + assert isinstance( + processor_of(providers.build_logger_provider(cfg, log_exporter=http_exporter)), + BatchLogRecordProcessor, + ) grpc_exporter = providers.build_span_exporter( OpenTelemetryV2Config(exporter="otlp_grpc", endpoint="http://h:4317") ) @@ -579,13 +647,10 @@ def _exception_event(span): def test_error_message_recorded_as_full_exception_event_untruncated(): - """Regression for the Elasticsearch keyword/ignore_above:1024 truncation. - - A long error message must survive intact on the standard ``exception`` - event under ``exception.message`` — not get dropped onto a bare string - attribute that backends dynamic-map to a 1024-char ``keyword``. The SDK - must not truncate it either, so a 5000-char message stays 5000 chars. - """ + """The ``exception`` event carries the full untruncated message under + ``exception.message`` so backends that dynamic-map unknown string span + attrs to ``keyword`` (e.g. Elasticsearch with a 1024-char ``ignore_above``) + still see it in full via the semconv-recognized event field.""" from litellm.integrations.otel.model.semconv import Error, ExceptionEvent long_message = "boom: " + "x" * 5000 @@ -596,13 +661,106 @@ def test_error_message_recorded_as_full_exception_event_untruncated(): assert len(event.attributes[ExceptionEvent.MESSAGE]) == len(long_message) > 1024 assert event.attributes[ExceptionEvent.TYPE] == "litellm.APIError" - # error.type stays a low-cardinality attribute; the message does NOT become a - # bare string attribute (which is what got truncated). + # error.type stays a low-cardinality attribute; the exception EVENT field + # ``exception.message`` never becomes a bare string attribute. assert span.attributes[Error.TYPE] == "litellm.APIError" assert ExceptionEvent.MESSAGE not in span.attributes assert span.status.description == long_message +def test_error_details_stamped_as_span_attributes_for_labels_ingest(): + """OTel-defined keys and litellm-specific detail keys both ride span + attributes so backends that flatten attrs into label indexes (Elastic APM + ``labels.*``, Datadog span tags) render them. The exception event with the + full untruncated message stays alongside.""" + from litellm.integrations.otel.model.semconv import Error, ExceptionEvent, LiteLLMError + from litellm.integrations.otel.emitter import SpanEmitter + + cfg = OpenTelemetryV2Config(exporter="in_memory") + provider, exporter = providers.in_memory_provider(cfg) + engine = SpanEmitter(providers.get_tracer(provider, "t"), cfg) + data = LLMCallSpanData( + operation=GenAIOperation.CHAT, + provider="openai", + request_model="gpt-4o", + response_model=None, + response_id=None, + request_params=LLMRequestParams(), + usage=LLMUsage(), + finish_reasons=(), + error=SpanError( + error_type="litellm.BadRequestError", + message="400: violated moderation policy", + code="400", + stack_trace="File proxy_server.py line 8570 ...", + llm_provider="openai", + ), + response_cost=None, + server=None, + identity=RequestIdentity(call_id=None), + ) + engine.emit(SpanRole.LLM_CALL, data) + (span,) = exporter.get_finished_spans() + + # OTel-defined keys (from the ``error.*`` semconv registry). + assert span.attributes[Error.TYPE] == "litellm.BadRequestError" + assert span.attributes[Error.MESSAGE] == "400: violated moderation policy" + # LiteLLM-specific detail keys, under the ``litellm.provider.error.*`` + # vendor namespace, not defined by OTel semconv. + assert span.attributes[LiteLLMError.CODE] == "400" + assert span.attributes[LiteLLMError.STACK_TRACE] == "File proxy_server.py line 8570 ..." + assert span.attributes[LiteLLMError.LLM_PROVIDER] == "openai" + + # The exception event carries the same message on the span too. + event = _exception_event(span) + assert event.attributes[ExceptionEvent.MESSAGE] == "400: violated moderation policy" + + +def test_error_details_omitted_when_span_error_carries_only_message(): + """A guardrail-shape error (message only, no code/traceback/provider) must + not pollute the span with empty-string detail attributes. Only the keys + that carry real data land.""" + from litellm.integrations.otel.model.semconv import Error, LiteLLMError + + span = _emit_error_span("guardrail rejected", error_type="ContentFilter") + + assert span.attributes[Error.TYPE] == "ContentFilter" + assert span.attributes[Error.MESSAGE] == "guardrail rejected" + # LiteLLM-specific detail keys aren't stamped when the SpanError doesn't + # carry them. + assert LiteLLMError.CODE not in span.attributes + assert LiteLLMError.STACK_TRACE not in span.attributes + assert LiteLLMError.LLM_PROVIDER not in span.attributes + + +def test_error_attribute_keys_are_pinned(): + """``error.type`` and ``error.message`` come from the semconv ``error.*`` + registry; the litellm-specific detail keys are vendor keys under + ``litellm.provider.error.*``. Pins the exact strings so the emitted + vocabulary can't drift silently.""" + from litellm.integrations.otel.model.semconv import Error, LiteLLMError + + assert Error.TYPE == "error.type" + assert Error.MESSAGE == "error.message" + assert LiteLLMError.CODE == "litellm.provider.error.code" + assert LiteLLMError.STACK_TRACE == "litellm.provider.error.stack_trace" + assert LiteLLMError.LLM_PROVIDER == "litellm.provider.error.llm_provider" + + +def test_error_message_falls_back_to_error_type_when_message_absent(): + """A ``SpanError(error_type=..., message=None)`` still renders on the span: + the resolved message is the error_type, and it lands on ``error.message``, + the exception event, and the span-status description in lockstep so a + single-source-of-truth view isn't inconsistent.""" + from litellm.integrations.otel.model.semconv import Error, ExceptionEvent + + span = _emit_error_span(message=None, error_type="RateLimitError") + + assert span.attributes[Error.MESSAGE] == "RateLimitError" + assert _exception_event(span).attributes[ExceptionEvent.MESSAGE] == "RateLimitError" + assert span.status.description == "RateLimitError" + + def test_success_span_records_no_exception_event(): from litellm.integrations.otel.emitter import SpanEmitter from litellm.integrations.otel.model.semconv import ExceptionEvent @@ -629,6 +787,177 @@ def test_success_span_records_no_exception_event(): assert all(e.name != ExceptionEvent.NAME for e in span.events) +def _engine_with_event_recorder(): + from opentelemetry.sdk._logs.export import InMemoryLogExporter + + from litellm.integrations.otel.emitter import SpanEmitter + from litellm.integrations.otel.plumbing.events import GenAIEventRecorder + + cfg = OpenTelemetryV2Config(exporter="in_memory", enable_events=True) + provider, span_exporter = providers.in_memory_provider(cfg) + log_exporter = InMemoryLogExporter() + logger_provider = providers.build_logger_provider(cfg, log_exporter=log_exporter) + recorder = GenAIEventRecorder(providers.get_event_logger(logger_provider)) + engine = SpanEmitter(providers.get_tracer(provider, "t"), cfg, event_recorder=recorder) + return engine, span_exporter, log_exporter + + +def _llm_call_data(error): + return LLMCallSpanData( + operation=GenAIOperation.CHAT, + provider="openai", + request_model="gpt-4o", + response_model=None, + response_id=None, + request_params=LLMRequestParams(), + usage=LLMUsage(), + finish_reasons=(), + error=error, + response_cost=None, + server=None, + identity=RequestIdentity(call_id=None), + ) + + +def test_operation_exception_log_event_emitted_on_failed_llm_call(): + """A failed LLM call records the GenAI semconv ``gen_ai.client.operation.exception`` + event on the logs signal: severity WARN, the full ``exception.*`` trio (including + the stacktrace, which span-side only exists under a vendor key), correlated to + the failed span via trace/span ids. The span-side error surface stays intact.""" + from opentelemetry._logs.severity import SeverityNumber + + from litellm.integrations.otel.model.semconv import ExceptionEvent, GenAIEvent + + engine, span_exporter, log_exporter = _engine_with_event_recorder() + engine.emit( + SpanRole.LLM_CALL, + _llm_call_data( + SpanError( + error_type="RateLimitError", + message="rate limited", + code="429", + stack_trace="Traceback (most recent call last) ...", + llm_provider="openai", + ) + ), + ) + (span,) = span_exporter.get_finished_spans() + (log,) = log_exporter.get_finished_logs() + record = log.log_record + + assert record.attributes["event.name"] == GenAIEvent.OPERATION_EXCEPTION + assert record.severity_number == SeverityNumber.WARN + assert record.attributes[ExceptionEvent.TYPE] == "RateLimitError" + assert record.attributes[ExceptionEvent.MESSAGE] == "rate limited" + assert record.attributes[ExceptionEvent.STACKTRACE] == "Traceback (most recent call last) ..." + assert record.trace_id == span.context.trace_id + assert record.span_id == span.context.span_id + + assert [e.name for e in span.events] == [ExceptionEvent.NAME] + assert span.attributes["error.type"] == "RateLimitError" + + +def test_operation_exception_log_event_omits_absent_stacktrace(): + from litellm.integrations.otel.model.semconv import ExceptionEvent + + engine, _, log_exporter = _engine_with_event_recorder() + engine.emit(SpanRole.LLM_CALL, _llm_call_data(SpanError(error_type="APIError", message="boom"))) + (log,) = log_exporter.get_finished_logs() + + assert ExceptionEvent.STACKTRACE not in log.log_record.attributes + assert log.log_record.attributes[ExceptionEvent.MESSAGE] == "boom" + + +def test_operation_exception_log_event_always_carries_required_pair(): + """``exception.type`` and ``exception.message`` are the semconv-required pair: + they ride the event even when the recorder is handed empty strings, so an + event is never emitted with no required field. Only the stacktrace is + conditional.""" + from opentelemetry.sdk._logs.export import InMemoryLogExporter + from opentelemetry.trace import INVALID_SPAN_CONTEXT + + from litellm.integrations.otel.model.semconv import ExceptionEvent + from litellm.integrations.otel.plumbing.events import GenAIEventRecorder + + cfg = OpenTelemetryV2Config(exporter="in_memory", enable_events=True) + log_exporter = InMemoryLogExporter() + logger_provider = providers.build_logger_provider(cfg, log_exporter=log_exporter) + recorder = GenAIEventRecorder(providers.get_event_logger(logger_provider)) + + recorder.record_operation_exception( + span_context=INVALID_SPAN_CONTEXT, + error_type="", + message="", + stack_trace="", + timestamp_ns=None, + ) + (log,) = log_exporter.get_finished_logs() + attributes = log.log_record.attributes + assert attributes[ExceptionEvent.TYPE] == "" + assert attributes[ExceptionEvent.MESSAGE] == "" + assert ExceptionEvent.STACKTRACE not in attributes + + +def test_operation_exception_log_event_not_emitted_on_success(): + engine, span_exporter, log_exporter = _engine_with_event_recorder() + engine.emit(SpanRole.LLM_CALL, _llm_call_data(None)) + + assert len(span_exporter.get_finished_spans()) == 1 + assert log_exporter.get_finished_logs() == () + + +def test_operation_exception_log_event_only_for_llm_call_role(): + """The event is scoped to GenAI client operations; a failed guardrail span + keeps its span-side error surface but records no GenAI exception event.""" + engine, span_exporter, log_exporter = _engine_with_event_recorder() + engine.emit( + SpanRole.GUARDRAIL, + GuardrailSpanData("presidio", status="failure", error=SpanError(error_type="X", message="denied")), + ) + (span,) = span_exporter.get_finished_spans() + + assert span.attributes["error.type"] == "X" + assert log_exporter.get_finished_logs() == () + + +def test_resolve_logger_provider_honors_explicit_noop_optout(monkeypatch): + """A ``NoOpLoggerProvider`` global is an explicit operator opt-out from the logs + signal: resolve to ``None`` so no recorder (and so no event) is ever built, + rather than emitting into a provider that drops everything.""" + from opentelemetry import _logs + from opentelemetry._logs import NoOpLoggerProvider + + from litellm.integrations.otel.logger import OpenTelemetryV2 + + cfg = OpenTelemetryV2Config(exporter="in_memory", enable_events=True) + tracer_provider, _ = providers.in_memory_provider(cfg) + monkeypatch.setattr(_logs, "get_logger_provider", lambda: NoOpLoggerProvider()) + + assert providers.resolve_logger_provider(cfg) is None + logger = OpenTelemetryV2(config=cfg, tracer_provider=tracer_provider) + assert logger._emitter._event_recorder is None + + +def test_resolve_logger_provider_reuses_operator_sdk_global(monkeypatch): + """Events ride an operator-configured logs pipeline rather than a second one + built by litellm, so they land wherever the operator's other logs land.""" + from opentelemetry import _logs + from opentelemetry.sdk._logs.export import InMemoryLogExporter + + cfg = OpenTelemetryV2Config(exporter="in_memory", enable_events=True) + operator_provider = providers.build_logger_provider(cfg, log_exporter=InMemoryLogExporter()) + monkeypatch.setattr(_logs, "get_logger_provider", lambda: operator_provider) + + assert providers.resolve_logger_provider(cfg) is operator_provider + + +def test_operation_exception_event_keys_are_pinned(): + from litellm.integrations.otel.model.semconv import ExceptionEvent, GenAIEvent + + assert GenAIEvent.OPERATION_EXCEPTION == "gen_ai.client.operation.exception" + assert ExceptionEvent.STACKTRACE == "exception.stacktrace" + + # --- service taxonomy: which calls become spans, and of what kind ----------- # diff --git a/tests/test_litellm/integrations/otel/test_otel_v2_logger.py b/tests/test_litellm/integrations/otel/test_otel_v2_logger.py index 0ceb7efbe0b..b5e077e3561 100644 --- a/tests/test_litellm/integrations/otel/test_otel_v2_logger.py +++ b/tests/test_litellm/integrations/otel/test_otel_v2_logger.py @@ -27,9 +27,11 @@ from litellm.integrations.otel import ( # noqa: E402 OpenTelemetryV2Config, ) from litellm.integrations.otel.plumbing import providers # noqa: E402 -from litellm.integrations.otel.plumbing.context import ( +from litellm.integrations.otel.plumbing.context import ( # noqa: E402 + reset_mcp_message_trace_carrier, + set_mcp_message_trace_carrier, set_request_root_span, -) # noqa: E402 +) from litellm.integrations.otel.logger import OpenTelemetryV2 # noqa: E402 from litellm.integrations.otel.model.spans import ( # noqa: E402 LITELLM_PROXY_REQUEST_SPAN_NAME, @@ -53,8 +55,10 @@ def _reset_request_root_span(): from litellm.integrations.otel.plumbing import context as _otel_context _otel_context._request_root_span.set(None) + _otel_context._mcp_message_trace_carrier.set(None) yield _otel_context._request_root_span.set(None) + _otel_context._mcp_message_trace_carrier.set(None) def _payload(**overrides): @@ -164,6 +168,43 @@ def test_async_log_success_event_emits_llm_call_span(): assert span.status.status_code is StatusCode.UNSET +def test_streaming_span_carries_time_to_first_chunk(): + logger, exporter = _logger() + kwargs = { + **_kwargs(payload=_payload(stream=True)), + "optional_params": {"stream": True}, + "api_call_start_time": datetime(2026, 5, 26, 12, 0, 0, tzinfo=timezone.utc), + "completion_start_time": datetime(2026, 5, 26, 12, 0, 0, 750000, tzinfo=timezone.utc), + } + _emit_llm(logger, kwargs) + (span,) = exporter.get_finished_spans() + assert span.attributes[GenAI.RESPONSE_TIME_TO_FIRST_CHUNK] == pytest.approx(0.75) + + +def test_non_streaming_span_has_no_time_to_first_chunk(): + logger, exporter = _logger() + kwargs = { + **_kwargs(), + "optional_params": {}, + "api_call_start_time": datetime(2026, 5, 26, 12, 0, 0, tzinfo=timezone.utc), + "completion_start_time": datetime(2026, 5, 26, 12, 0, 5, tzinfo=timezone.utc), + } + _emit_llm(logger, kwargs) + (span,) = exporter.get_finished_spans() + assert GenAI.RESPONSE_TIME_TO_FIRST_CHUNK not in span.attributes + + +def test_streaming_span_without_timing_omits_time_to_first_chunk(): + logger, exporter = _logger() + kwargs = { + **_kwargs(payload=_payload(stream=True)), + "optional_params": {"stream": True}, + } + _emit_llm(logger, kwargs) + (span,) = exporter.get_finished_spans() + assert GenAI.RESPONSE_TIME_TO_FIRST_CHUNK not in span.attributes + + def test_async_log_failure_event_marks_error_status(): logger, exporter = _logger() payload = _payload( @@ -176,6 +217,61 @@ def test_async_log_failure_event_marks_error_status(): assert span.attributes["error.type"] == "RateLimitError" +def _logger_with_events(enable_events): + from opentelemetry.sdk._logs.export import InMemoryLogExporter + + cfg = OpenTelemetryV2Config(exporter="in_memory", enable_events=enable_events) + span_exporter = InMemorySpanExporter() + tracer_provider = providers.build_tracer_provider(cfg, exporter=span_exporter) + log_exporter = InMemoryLogExporter() + logger_provider = providers.build_logger_provider(cfg, log_exporter=log_exporter) + logger = OpenTelemetryV2(config=cfg, tracer_provider=tracer_provider, logger_provider=logger_provider) + return logger, span_exporter, log_exporter + + +def test_enable_events_records_operation_exception_through_failure_callback(): + """With ``enable_events`` on, a real failure callback records the GenAI + ``gen_ai.client.operation.exception`` log event, carrying the traceback from + the standard logging payload and correlated to the LLM-call span.""" + from litellm.integrations.otel.model.semconv import ExceptionEvent, GenAIEvent + + logger, span_exporter, log_exporter = _logger_with_events(enable_events=True) + payload = _payload( + status="failure", + error_information={ + "error_class": "RateLimitError", + "error_message": "429 rate limited", + "traceback": "Traceback (most recent call last) ...", + }, + ) + _emit_llm(logger, _kwargs(payload=payload), fail=True) + + (span,) = span_exporter.get_finished_spans() + (log,) = log_exporter.get_finished_logs() + record = log.log_record + assert record.attributes["event.name"] == GenAIEvent.OPERATION_EXCEPTION + assert record.attributes[ExceptionEvent.TYPE] == "RateLimitError" + assert record.attributes[ExceptionEvent.MESSAGE] == "429 rate limited" + assert record.attributes[ExceptionEvent.STACKTRACE] == "Traceback (most recent call last) ..." + assert record.trace_id == span.context.trace_id + assert record.span_id == span.context.span_id + + +def test_events_off_by_default_records_no_log_event_on_failure(): + """``enable_events`` defaults to off: even with a logs pipeline injected, a + failure records only the span-side error surface, no log event.""" + logger, span_exporter, log_exporter = _logger_with_events(enable_events=False) + payload = _payload( + status="failure", + error_information={"error_class": "RateLimitError", "error_message": "429"}, + ) + _emit_llm(logger, _kwargs(payload=payload), fail=True) + + assert len(span_exporter.get_finished_spans()) == 1 + assert log_exporter.get_finished_logs() == () + assert OpenTelemetryV2Config(exporter="in_memory").enable_events is False + + def test_sync_log_event_is_noop(): """V2 closes the span async-only; the sync callback runs out-of-context, so it no-ops (the span stays open on the carrier until the async callback).""" @@ -387,6 +483,190 @@ def test_mcp_tool_call_metadata_read_from_nested_metadata_not_top_level(): assert LiteLLM.MCP_SERVER_NAME not in span.attributes +def _mcp_list_payload(**overrides): + payload = { + "call_type": "list_mcp_tools", + "status": "success", + "litellm_call_id": "mcp_list_1", + "metadata": { + "user_api_key_team_id": "t1", + "spend_logs_metadata": {"mcp_operation": "list_tools"}, + }, + "hidden_params": {}, + } + payload.update(overrides) + return payload + + +def test_mcp_list_tools_emits_client_span(): + """An MCP ``tools/list`` discovery call becomes a CLIENT span named ``tools/list``, + carrying only the MCP method and the call id. Per the GenAI MCP semconv the list + span omits ``gen_ai.operation.name`` and ``gen_ai.tool.name`` (tool-call-only) and + ``mcp.session.id`` (the list path threads no session id), so a naive reuse of the + tool-call mapper would wrongly stamp them, and the pre-fix code emitted no span at + all for a ``list_mcp_tools`` payload.""" + logger, exporter = _logger() + kwargs = {"standard_logging_object": _mcp_list_payload()} + asyncio.run(logger.async_log_success_event(kwargs, None, None, None)) + (span,) = exporter.get_finished_spans() + assert span.name == "tools/list" + assert span.kind is SpanKind.CLIENT + assert span.attributes["mcp.method.name"] == "tools/list" + assert span.attributes[LiteLLM.CALL_ID] == "mcp_list_1" + assert span.status.status_code is StatusCode.UNSET + # Bug-killers: no span pre-fix (empty exporter -> the unpack above raises), and a + # tool-call-shaped fix would leak execute_tool / tool name / session id here. + assert GenAI.OPERATION_NAME not in span.attributes + assert "gen_ai.tool.name" not in span.attributes + assert "mcp.session.id" not in span.attributes + + +_MCP_SPAN_CASES = [ + (_mcp_payload, "tools/call get_weather"), + (_mcp_list_payload, "tools/list"), +] + + +@pytest.mark.parametrize("make_payload, span_name", _MCP_SPAN_CASES) +def test_mcp_span_roots_and_links_transport_without_propagated_context( + make_payload, span_name +): + """MCP and the HTTP transport are independent lifecycles (one streamable-HTTP + session multiplexes many messages), so per the MCP semconv the message span + must NOT nest under the session/transport span — that is what made it render + skewed at the session's start. With no propagated ``params._meta`` context it + starts its own root trace and records the transport span as a *link*, never + the parent.""" + logger, exporter = _logger() + transport = logger._emitter.start_span( + SpanRole.PROXY_REQUEST, LITELLM_PROXY_REQUEST_SPAN_NAME + ) + set_request_root_span(transport) + asyncio.run( + logger.async_log_success_event( + {"standard_logging_object": make_payload()}, None, None, None + ) + ) + transport.end() + span = next(s for s in exporter.get_finished_spans() if s.name == span_name) + assert span.parent is None + assert span.context.trace_id != transport.get_span_context().trace_id + assert [link.context.span_id for link in span.links] == [ + transport.get_span_context().span_id + ] + + +@pytest.mark.parametrize("make_payload, span_name", _MCP_SPAN_CASES) +def test_mcp_span_parents_to_propagated_meta_trace_context(make_payload, span_name): + """When the client propagates W3C trace context in the request's + ``params._meta`` (SEP-414), the MCP span parents to it (one distributed trace) + and still links the transport span — never falling through to the + ambient/session span.""" + logger, exporter = _logger() + transport = logger._emitter.start_span( + SpanRole.PROXY_REQUEST, LITELLM_PROXY_REQUEST_SPAN_NAME + ) + set_request_root_span(transport) + token = set_mcp_message_trace_carrier( + {"traceparent": "00-11111111111111111111111111111111-2222222222222222-01"} + ) + try: + asyncio.run( + logger.async_log_success_event( + {"standard_logging_object": make_payload()}, None, None, None + ) + ) + finally: + reset_mcp_message_trace_carrier(token) + transport.end() + span = next(s for s in exporter.get_finished_spans() if s.name == span_name) + assert span.context.trace_id == 0x11111111111111111111111111111111 + assert span.parent is not None + assert span.parent.span_id == 0x2222222222222222 + assert [link.context.span_id for link in span.links] == [ + transport.get_span_context().span_id + ] + + +@pytest.mark.parametrize("make_payload, span_name", _MCP_SPAN_CASES) +def test_mcp_span_ignores_client_supplied_baggage(make_payload, span_name): + """The MCP span must NOT honor W3C Baggage from the client's ``params._meta``. + + ``params._meta`` is caller-controlled and the baggage processor stamps + allowlisted baggage keys onto every span, so extracting remote baggage would + let a client spoof a span's identity (e.g. ``litellm.team.id``). The propagator + extracts trace context only, so the spoofed keys never reach the span while the + legitimate traceparent parenting still works.""" + logger, exporter = _logger() + transport = logger._emitter.start_span( + SpanRole.PROXY_REQUEST, LITELLM_PROXY_REQUEST_SPAN_NAME + ) + set_request_root_span(transport) + token = set_mcp_message_trace_carrier( + { + "traceparent": "00-11111111111111111111111111111111-2222222222222222-01", + "baggage": "litellm.team.id=spoofed-team,litellm.metadata.user_api_key_user_id=attacker", + } + ) + try: + asyncio.run( + logger.async_log_success_event( + {"standard_logging_object": make_payload()}, None, None, None + ) + ) + finally: + reset_mcp_message_trace_carrier(token) + transport.end() + span = next(s for s in exporter.get_finished_spans() if s.name == span_name) + # Trace context still honored: proves the carrier was processed, not dropped wholesale. + assert span.parent is not None and span.parent.span_id == 0x2222222222222222 + # Identity is the authenticated payload's team, never the client's spoofed value. + assert span.attributes[LiteLLM.TEAM_ID] == "t1" + assert "litellm.metadata.user_api_key_user_id" not in span.attributes + + +@pytest.mark.parametrize("make_payload, span_name", _MCP_SPAN_CASES) +def test_mcp_span_carries_authenticated_identity(make_payload, span_name): + """An MCP span is labeled with the authenticated request's identity (team/key), + seeded from the parsed payload like the LLM-call span. Without this seeding the + span — parented to an empty remote context — would carry no team/key attribute at + all, so it couldn't be attributed or filtered by team in the traces backend.""" + logger, exporter = _logger() + asyncio.run( + logger.async_log_success_event( + {"standard_logging_object": make_payload()}, None, None, None + ) + ) + span = next(s for s in exporter.get_finished_spans() if s.name == span_name) + assert span.attributes[LiteLLM.TEAM_ID] == "t1" + + +def test_mcp_span_malformed_traceparent_starts_root(): + """A malformed traceparent in ``params._meta`` must not crash or parent to a + bogus span: the propagator ignores it, so the span starts its own root trace and + still links the transport span.""" + logger, exporter = _logger() + transport = logger._emitter.start_span( + SpanRole.PROXY_REQUEST, LITELLM_PROXY_REQUEST_SPAN_NAME + ) + set_request_root_span(transport) + token = set_mcp_message_trace_carrier({"traceparent": "not-a-valid-traceparent"}) + try: + asyncio.run( + logger.async_log_success_event( + {"standard_logging_object": _mcp_list_payload()}, None, None, None + ) + ) + finally: + reset_mcp_message_trace_carrier(token) + transport.end() + span = next(s for s in exporter.get_finished_spans() if s.name == "tools/list") + assert span.parent is None + assert [link.context.span_id for link in span.links] == [ + transport.get_span_context().span_id + ] + + def test_pre_call_idempotent_keeps_first_span(): """A retried call may re-enter ``pre_call`` with the same call id; the first span (with the true start time) is kept, not replaced.""" diff --git a/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py b/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py index 4bb26a70b02..71be28ea485 100644 --- a/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py +++ b/tests/test_litellm/integrations/otel/test_otel_v2_sources_of_truth.py @@ -94,19 +94,32 @@ def test_registry_parent_integrity_no_orphans(): def test_registry_hierarchy_shape(): - assert set(root_roles()) == {SpanRole.PROXY_REQUEST} + # MCP roles have no in-process parent: per the MCP semconv they root (or adopt + # the client's propagated _meta context), so they sit alongside PROXY_REQUEST. + assert set(root_roles()) == { + SpanRole.PROXY_REQUEST, + SpanRole.MCP_TOOL_CALL, + SpanRole.MCP_LIST_TOOLS, + } # Guardrails parent to the request span, not the LLM call: a pre-call # guardrail runs before the LLM call exists, so it's a sibling of it. assert set(child_roles(SpanRole.PROXY_REQUEST)) == { SpanRole.LLM_CALL, - SpanRole.MCP_TOOL_CALL, SpanRole.GUARDRAIL, SpanRole.DB_CALL, SpanRole.SERVICE, } assert SPAN_REGISTRY[SpanRole.LLM_CALL].kind is LiteLLMSpanKind.CLIENT - # The proxy is an MCP client to the upstream tool server: CLIENT span. + # The proxy is an MCP client to the upstream tool server: CLIENT span. Listing + # tools is the same client relationship, so it's a CLIENT span too. assert SPAN_REGISTRY[SpanRole.MCP_TOOL_CALL].kind is LiteLLMSpanKind.CLIENT + assert SPAN_REGISTRY[SpanRole.MCP_LIST_TOOLS].kind is LiteLLMSpanKind.CLIENT + # MCP spans don't nest under the transport: they link the PROXY_REQUEST span + # instead of parenting to it (OTel GenAI MCP semconv). + assert SPAN_REGISTRY[SpanRole.MCP_TOOL_CALL].parent is None + assert SPAN_REGISTRY[SpanRole.MCP_LIST_TOOLS].parent is None + assert SPAN_REGISTRY[SpanRole.MCP_TOOL_CALL].links is SpanRole.PROXY_REQUEST + assert SPAN_REGISTRY[SpanRole.MCP_LIST_TOOLS].links is SpanRole.PROXY_REQUEST assert SPAN_REGISTRY[SpanRole.PROXY_REQUEST].kind is LiteLLMSpanKind.SERVER assert SPAN_REGISTRY[SpanRole.GUARDRAIL].parent is SpanRole.PROXY_REQUEST # An outbound datastore call is a CLIENT span; an internal service is INTERNAL. @@ -131,11 +144,11 @@ def _all_constants(cls): def test_attribute_keys_are_unique_across_namespaces(): - from litellm.integrations.otel import MCP, Client, JsonRpc, Network + from litellm.integrations.otel import MCP, Client, JsonRpc, LiteLLMError, Network # prefixes are allowed to be substrings; exact keys must not collide. exact = set() - for cls in (GenAI, Error, Server, HTTP, DB, MCP, JsonRpc, Network, Client): + for cls in (GenAI, Error, LiteLLMError, Server, HTTP, DB, MCP, JsonRpc, Network, Client): for key in _all_constants(cls): assert key not in exact, f"duplicate attribute key {key}" exact.add(key) @@ -329,6 +342,47 @@ def test_llm_call_adapter_failure_path(): assert data.error.message == "429 slow down" +def test_llm_call_adapter_carries_error_detail_fields(): + """``_parse_error`` threads the full detail set from ``error_information`` + (``error_code``, ``traceback``, ``llm_provider``) onto ``SpanError`` so the + emitter can stamp them as span attributes.""" + payload = _sample_payload( + status="failure", + error_information={ + "error_class": "BadRequestError", + "error_message": "400 violated moderation policy", + "error_code": "400", + "traceback": "File proxy_server.py line 8570 ...", + "llm_provider": "openai", + }, + ) + data = LLMCallSpanData.from_standard_logging_payload(payload) + assert data.error is not None + assert data.error.error_type == "BadRequestError" + assert data.error.message == "400 violated moderation policy" + assert data.error.code == "400" + assert data.error.stack_trace == "File proxy_server.py line 8570 ..." + assert data.error.llm_provider == "openai" + + +def test_llm_call_adapter_error_details_default_to_none_when_absent(): + """Guardrail-shape payloads carry only ``error_class`` + ``error_message``. + The detail fields must stay ``None`` so the emitter's ``if error.code:`` + guards skip stamping empty attributes.""" + payload = _sample_payload( + status="failure", + error_information={ + "error_class": "ContentFilter", + "error_message": "guardrail rejected", + }, + ) + data = LLMCallSpanData.from_standard_logging_payload(payload) + assert data.error is not None + assert data.error.code is None + assert data.error.stack_trace is None + assert data.error.llm_provider is None + + def test_adapter_is_resilient_to_minimal_payload(): data = LLMCallSpanData.from_standard_logging_payload({}) assert data.request_model == "" diff --git a/tests/test_litellm/integrations/otel/test_runtime.py b/tests/test_litellm/integrations/otel/test_runtime.py new file mode 100644 index 00000000000..d11f31b2523 --- /dev/null +++ b/tests/test_litellm/integrations/otel/test_runtime.py @@ -0,0 +1,64 @@ +"""Regression tests for the SDK-free OTel runtime shim. + +The proxy auth hot path calls ``phase_span`` and ``seed_request_identity`` on +every request. These wrappers resolve the SDK-backed implementations with a +lazy import. CPython never caches a failed import, so before memoization an +absent OTel SDK made every request re-scan ``sys.path`` and contend on the +import lock. These tests pin the import to a single resolution. +""" + +import builtins + +import litellm.integrations.otel.runtime as runtime + + +def test_logger_not_reimported_after_first_resolution(monkeypatch): + runtime._otel_runtime.cache_clear() + + counts = {"n": 0} + real_import = builtins.__import__ + + def counting_import(name, globals=None, locals=None, fromlist=(), level=0): + if name == "litellm.integrations.otel" and fromlist and "logger" in fromlist: + counts["n"] += 1 + return real_import(name, globals, locals, fromlist, level) + + monkeypatch.setattr(builtins, "__import__", counting_import) + + with runtime.phase_span("auth /v1/chat/completions"): + pass + after_first = counts["n"] + + for _ in range(49): + with runtime.phase_span("auth /v1/chat/completions"): + pass + + assert counts["n"] == after_first, ( + f"otel.logger re-imported {counts['n'] - after_first} times after the first " + "resolution; it must be memoized so it does not re-scan sys.path per request" + ) + + runtime._otel_runtime.cache_clear() + + +def test_resolution_is_memoized(): + runtime._otel_runtime.cache_clear() + + for _ in range(25): + with runtime.phase_span("p"): + pass + + info = runtime._otel_runtime.cache_info() + assert info.misses == 1 + assert info.hits >= 24 + + runtime._otel_runtime.cache_clear() + + +def test_wrappers_no_op_when_runtime_absent(monkeypatch): + monkeypatch.setattr(runtime, "_otel_runtime", lambda: None) + + with runtime.phase_span("auth") as span: + assert span is None + + assert runtime.seed_request_identity({"token": "sk-x"}, model="gpt-4o") is None diff --git a/tests/test_litellm/integrations/test_anthropic_cache_control_hook.py b/tests/test_litellm/integrations/test_anthropic_cache_control_hook.py index 6afe5efc54d..4664cc86303 100644 --- a/tests/test_litellm/integrations/test_anthropic_cache_control_hook.py +++ b/tests/test_litellm/integrations/test_anthropic_cache_control_hook.py @@ -1,3 +1,4 @@ +import copy import datetime import json import os @@ -9,9 +10,7 @@ from unittest.mock import ANY, MagicMock, Mock, patch import httpx import pytest -sys.path.insert( - 0, os.path.abspath("../..") -) # Adds the parent directory to the system-path +sys.path.insert(0, os.path.abspath("../..")) # Adds the parent directory to the system-path import litellm from litellm.integrations.anthropic_cache_control_hook import AnthropicCacheControlHook from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler @@ -93,13 +92,9 @@ async def test_anthropic_cache_control_hook_system_message(): # Verify that cache control was applied (Bedrock transforms it to a separate item) cache_control_count = sum( - 1 - for item in request_body["system"] - if isinstance(item, dict) and "cachePoint" in item + 1 for item in request_body["system"] if isinstance(item, dict) and "cachePoint" in item ) - assert ( - cache_control_count == 1 - ), f"Expected exactly 1 cache control point, found {cache_control_count}" + assert cache_control_count == 1, f"Expected exactly 1 cache control point, found {cache_control_count}" @pytest.mark.asyncio @@ -171,9 +166,7 @@ async def test_anthropic_cache_control_hook_user_message(): print("request_body: ", json.dumps(request_body, indent=4)) # Verify the request body - assert request_body["messages"][1]["content"][1]["cachePoint"] == { - "type": "default" - } + assert request_body["messages"][1]["content"][1]["cachePoint"] == {"type": "default"} @pytest.mark.asyncio @@ -262,14 +255,10 @@ async def test_anthropic_cache_control_hook_negative_indices(): # Verify the last message (input index -1 -> request index 2) has cache control last_message_content = request_body["messages"][2]["content"] - assert isinstance( - last_message_content, list - ), "Last message content should be a list" - assert any( - "cachePoint" in item - for item in last_message_content - if isinstance(item, dict) - ), "CachePoint missing in last message" + assert isinstance(last_message_content, list), "Last message content should be a list" + assert any("cachePoint" in item for item in last_message_content if isinstance(item, dict)), ( + "CachePoint missing in last message" + ) # Note: Based on debug output, the hook correctly applies cache control to both messages, # but the Bedrock API transformation appears to only preserve cache control for user messages, @@ -278,30 +267,20 @@ async def test_anthropic_cache_control_hook_negative_indices(): # The second-to-last message (assistant) gets cache_control from the hook but loses it # during API transformation. This test documents this behavior. second_last_message_content = request_body["messages"][1]["content"] - assert isinstance( - second_last_message_content, list - ), "Second-to-last message content should be a list" + assert isinstance(second_last_message_content, list), "Second-to-last message content should be a list" # Check if assistant message cache control is preserved (currently it's not) assistant_has_cache_control = any( - "cachePoint" in item - for item in second_last_message_content - if isinstance(item, dict) - ) - print( - f"Assistant message has cache control in final request: {assistant_has_cache_control}" + "cachePoint" in item for item in second_last_message_content if isinstance(item, dict) ) + print(f"Assistant message has cache control in final request: {assistant_has_cache_control}") # Verify the first user message (request index 0) was NOT modified first_user_message_content = request_body["messages"][0]["content"] - assert isinstance( - first_user_message_content, list - ), "First user message content should be a list" - assert not any( - "cachePoint" in item - for item in first_user_message_content - if isinstance(item, dict) - ), "CachePoint unexpectedly found in first user message" + assert isinstance(first_user_message_content, list), "First user message content should be a list" + assert not any("cachePoint" in item for item in first_user_message_content if isinstance(item, dict)), ( + "CachePoint unexpectedly found in first user message" + ) @pytest.mark.asyncio @@ -342,9 +321,7 @@ async def test_anthropic_cache_control_hook_out_of_bounds_logging(): client = AsyncHTTPHandler() # Mock the verbose_logger to capture warning calls - with patch( - "litellm.integrations.anthropic_cache_control_hook.verbose_logger" - ) as mock_logger: + with patch("litellm.integrations.anthropic_cache_control_hook.verbose_logger") as mock_logger: with patch.object(client, "post", return_value=mock_response) as mock_post: messages = [ {"role": "user", "content": "Message 1"}, @@ -354,9 +331,7 @@ async def test_anthropic_cache_control_hook_out_of_bounds_logging(): await litellm.acompletion( model="bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0", messages=messages, - cache_control_injection_points=[ - {"location": "message", "index": 10} - ], # Out of bounds index + cache_control_injection_points=[{"location": "message", "index": 10}], # Out of bounds index client=client, ) @@ -365,10 +340,7 @@ async def test_anthropic_cache_control_hook_out_of_bounds_logging(): warning_call = mock_logger.warning.call_args[0][0] # Check that the warning message contains the expected information - assert ( - "AnthropicCacheControlHook: Provided index 10 is out of bounds" - in warning_call - ) + assert "AnthropicCacheControlHook: Provided index 10 is out of bounds" in warning_call assert "message list of length 2" in warning_call assert "Targeted index was 10" in warning_call assert "Skipping cache control injection for this point" in warning_call @@ -411,9 +383,7 @@ async def test_anthropic_cache_control_hook_negative_out_of_bounds_logging(): client = AsyncHTTPHandler() # Mock the verbose_logger to capture warning calls - with patch( - "litellm.integrations.anthropic_cache_control_hook.verbose_logger" - ) as mock_logger: + with patch("litellm.integrations.anthropic_cache_control_hook.verbose_logger") as mock_logger: with patch.object(client, "post", return_value=mock_response) as mock_post: messages = [ {"role": "user", "content": "Single message"}, @@ -436,14 +406,9 @@ async def test_anthropic_cache_control_hook_negative_out_of_bounds_logging(): warning_call = mock_logger.warning.call_args[0][0] # Check that the warning message contains the original negative index - assert ( - "AnthropicCacheControlHook: Provided index -5 is out of bounds" - in warning_call - ) + assert "AnthropicCacheControlHook: Provided index -5 is out of bounds" in warning_call assert "message list of length 1" in warning_call - assert ( - "Targeted index was -4" in warning_call - ) # -5 + 1 = -4 (converted index) + assert "Targeted index was -4" in warning_call # -5 + 1 = -4 (converted index) assert "Skipping cache control injection for this point" in warning_call @@ -531,15 +496,11 @@ async def test_anthropic_cache_control_hook_multiple_user_messages(): # Count cache control points - should have 2 since both injection points were applied cache_control_count = sum( - 1 - for item in combined_message_content - if isinstance(item, dict) and "cachePoint" in item + 1 for item in combined_message_content if isinstance(item, dict) and "cachePoint" in item ) assert cache_control_count == 2 - print( - f"Found {cache_control_count} cache control points in the combined message" - ) + print(f"Found {cache_control_count} cache control points in the combined message") @pytest.mark.asyncio @@ -588,9 +549,7 @@ async def test_anthropic_cache_control_hook_out_of_bounds(bad_index): await litellm.acompletion( model="bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0", messages=messages, - cache_control_injection_points=[ - {"location": "message", "index": bad_index} - ], + cache_control_injection_points=[{"location": "message", "index": bad_index}], client=client, ) @@ -601,19 +560,13 @@ async def test_anthropic_cache_control_hook_out_of_bounds(bad_index): for msg in request_body["messages"]: content = msg.get("content", []) if isinstance(content, list): - assert not any( - "cachePoint" in item - for item in content - if isinstance(item, dict) - ) + assert not any("cachePoint" in item for item in content if isinstance(item, dict)) @pytest.mark.asyncio @pytest.mark.parametrize( "message_list", - [ - [{"role": "user", "content": "Single message"}] - ], # Single message only - empty list will fail at API level + [[{"role": "user", "content": "Single message"}]], # Single message only - empty list will fail at API level ) async def test_anthropic_cache_control_hook_single_message(message_list): """ @@ -662,9 +615,7 @@ async def test_anthropic_cache_control_hook_single_message(message_list): # For the single message, verify cache control was applied content = request_body["messages"][0]["content"] assert isinstance(content, list) - assert any( - "cachePoint" in item for item in content if isinstance(item, dict) - ) + assert any("cachePoint" in item for item in content if isinstance(item, dict)) @pytest.mark.asyncio @@ -693,9 +644,7 @@ async def test_anthropic_cache_control_hook_empty_message_list(): await litellm.acompletion( model="bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0", messages=[], - cache_control_injection_points=[ - {"location": "message", "index": -1} - ], + cache_control_injection_points=[{"location": "message", "index": -1}], client=client, ) @@ -755,11 +704,7 @@ async def test_anthropic_cache_control_hook_no_op(): for msg in request_body["messages"]: content = msg.get("content", []) if isinstance(content, list): - assert not any( - "cachePoint" in item - for item in content - if isinstance(item, dict) - ) + assert not any("cachePoint" in item for item in content if isinstance(item, dict)) @pytest.mark.asyncio @@ -827,14 +772,10 @@ async def test_anthropic_cache_control_hook_multiple_content_items_last_only(): message_content = request_body["messages"][0]["content"] assert isinstance(message_content, list) - cache_control_count = sum( - 1 - for item in message_content - if isinstance(item, dict) and "cachePoint" in item + cache_control_count = sum(1 for item in message_content if isinstance(item, dict) and "cachePoint" in item) + assert cache_control_count == 1, ( + f"Expected exactly 1 cache control point, found {cache_control_count}. This test verifies the fix for issue 15696 where cache_control was incorrectly applied to ALL content items." ) - assert ( - cache_control_count == 1 - ), f"Expected exactly 1 cache control point, found {cache_control_count}. This test verifies the fix for issue 15696 where cache_control was incorrectly applied to ALL content items." @pytest.mark.asyncio @@ -891,30 +832,22 @@ async def test_anthropic_cache_control_hook_document_analysis_multiple_pages(): ], } ], - cache_control_injection_points=[ - {"location": "message", "role": "user"} - ], + cache_control_injection_points=[{"location": "message", "role": "user"}], client=client, ) mock_post.assert_called_once() request_body = json.loads(mock_post.call_args.kwargs["data"]) - print( - "Document analysis request_body: ", json.dumps(request_body, indent=4) - ) + print("Document analysis request_body: ", json.dumps(request_body, indent=4)) message_content = request_body["messages"][0]["content"] assert isinstance(message_content, list) - cache_control_count = sum( - 1 - for item in message_content - if isinstance(item, dict) and "cachePoint" in item + cache_control_count = sum(1 for item in message_content if isinstance(item, dict) and "cachePoint" in item) + assert cache_control_count == 1, ( + f"Expected exactly 1 cache control point (last item only), found {cache_control_count}. Before fix, this would be 6 (one for each content item)." ) - assert ( - cache_control_count == 1 - ), f"Expected exactly 1 cache control point (last item only), found {cache_control_count}. Before fix, this would be 6 (one for each content item)." def test_gemini_cache_control_injection_points_detected(): @@ -1076,13 +1009,8 @@ async def test_anthropic_cache_control_hook_string_negative_index(): # The last user message should have cache control applied last_message = request_body["messages"][-1] last_message_content = last_message["content"] - assert isinstance( - last_message_content, list - ), f"Expected list content, got {type(last_message_content)}" - has_cache_point = any( - isinstance(item, dict) and "cachePoint" in item - for item in last_message_content - ) + assert isinstance(last_message_content, list), f"Expected list content, got {type(last_message_content)}" + has_cache_point = any(isinstance(item, dict) and "cachePoint" in item for item in last_message_content) assert has_cache_point, ( f"Expected cachePoint in last message content, got: {last_message_content}. " "String index '-1' was not parsed correctly (str.isdigit() returns False for negative strings)." @@ -1146,17 +1074,13 @@ def test_cache_control_hook_caps_at_four_blocks_with_client_cache_control(): _, processed, _ = hook.get_chat_completion_prompt( model="bedrock/us.anthropic.claude-opus-4-6-v1:0", messages=messages, - non_default_params={ - "cache_control_injection_points": _build_injection_points() - }, + non_default_params={"cache_control_injection_points": _build_injection_points()}, prompt_id=None, prompt_variables=None, dynamic_callback_params={}, ) - assert ( - _count_cache_control(processed) == 4 - ), "Hook must cap cache_control at Anthropic's limit of 4 blocks" + assert _count_cache_control(processed) == 4, "Hook must cap cache_control at Anthropic's limit of 4 blocks" # Client TTL on system blocks must be preserved (not overwritten by config). for i in range(4): @@ -1170,11 +1094,7 @@ def test_cache_control_hook_caps_at_four_blocks_with_client_cache_control(): assert user_message.get("cache_control") is None user_content = user_message.get("content") if isinstance(user_content, list): - assert all( - block.get("cache_control") is None - for block in user_content - if isinstance(block, dict) - ) + assert all(block.get("cache_control") is None for block in user_content if isinstance(block, dict)) def test_cache_control_hook_caps_at_four_blocks_without_client_cache_control(): @@ -1184,17 +1104,13 @@ def test_cache_control_hook_caps_at_four_blocks_without_client_cache_control(): """ hook = AnthropicCacheControlHook() - messages: List[AllMessageValues] = [ - {"role": "system", "content": f"System {i}"} for i in range(4) - ] + messages: List[AllMessageValues] = [{"role": "system", "content": f"System {i}"} for i in range(4)] messages.append({"role": "user", "content": "hello"}) _, processed, _ = hook.get_chat_completion_prompt( model="bedrock/us.anthropic.claude-opus-4-6-v1:0", messages=messages, - non_default_params={ - "cache_control_injection_points": _build_injection_points() - }, + non_default_params={"cache_control_injection_points": _build_injection_points()}, prompt_id=None, prompt_variables=None, dynamic_callback_params={}, @@ -1303,18 +1219,12 @@ async def test_cache_control_hook_bedrock_payload_caps_cachepoints_at_four(): request_body = json.loads(mock_post.call_args.kwargs["data"]) cache_points = sum( - 1 - for block in request_body.get("system", []) - if isinstance(block, dict) and "cachePoint" in block + 1 for block in request_body.get("system", []) if isinstance(block, dict) and "cachePoint" in block ) for msg in request_body.get("messages", []): content = msg.get("content", []) if isinstance(content, list): - cache_points += sum( - 1 - for block in content - if isinstance(block, dict) and "cachePoint" in block - ) + cache_points += sum(1 for block in content if isinstance(block, dict) and "cachePoint" in block) assert cache_points <= 4, ( f"Bedrock payload exceeded Anthropic's 4 cache_control block limit: " @@ -1331,9 +1241,7 @@ def test_cache_control_hook_reserves_slot_for_tool_config_point(): """ hook = AnthropicCacheControlHook() - messages: List[AllMessageValues] = [ - {"role": "system", "content": f"System {i}"} for i in range(4) - ] + messages: List[AllMessageValues] = [{"role": "system", "content": f"System {i}"} for i in range(4)] messages.append({"role": "user", "content": "hello"}) _, processed, non_default_params = hook.get_chat_completion_prompt( @@ -1356,9 +1264,7 @@ def test_cache_control_hook_reserves_slot_for_tool_config_point(): assert _count_cache_control(processed) == 3 # The tool_config point is passed through for the provider transform. - assert non_default_params["cache_control_injection_points"] == [ - {"location": "tool_config"} - ] + assert non_default_params["cache_control_injection_points"] == [{"location": "tool_config"}] @pytest.mark.asyncio @@ -1384,9 +1290,7 @@ async def test_cache_control_hook_bedrock_payload_caps_with_tool_config_point(): client = AsyncHTTPHandler() with patch.object(client, "post", return_value=mock_response) as mock_post: - messages = [ - {"role": "system", "content": f"System block {i}"} for i in range(4) - ] + messages = [{"role": "system", "content": f"System block {i}"} for i in range(4)] messages.append({"role": "user", "content": "What is the weather?"}) await litellm.acompletion( @@ -1421,18 +1325,12 @@ async def test_cache_control_hook_bedrock_payload_caps_with_tool_config_point(): request_body = json.loads(mock_post.call_args.kwargs["data"]) cache_points = sum( - 1 - for block in request_body.get("system", []) - if isinstance(block, dict) and "cachePoint" in block + 1 for block in request_body.get("system", []) if isinstance(block, dict) and "cachePoint" in block ) for msg in request_body.get("messages", []): content = msg.get("content", []) if isinstance(content, list): - cache_points += sum( - 1 - for block in content - if isinstance(block, dict) and "cachePoint" in block - ) + cache_points += sum(1 for block in content if isinstance(block, dict) and "cachePoint" in block) for tool in request_body.get("toolConfig", {}).get("tools", []): if isinstance(tool, dict) and "cachePoint" in tool: cache_points += 1 @@ -1441,3 +1339,197 @@ async def test_cache_control_hook_bedrock_payload_caps_with_tool_config_point(): f"Bedrock payload exceeded Anthropic's 4 cache_control block limit " f"when mixing message and tool_config injection: found {cache_points}" ) + + +class TestApplyToAnthropicMessagesRequest: + """Tests for apply_to_anthropic_messages_request (v1/messages cache control).""" + + def test_system_string_injection(self): + messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}] + system = "You are helpful" + injection_points = [{"location": "message", "role": "system"}] + + result_msgs, result_sys, remaining = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=system, + injection_points=injection_points, + ) + + assert result_sys == [{"type": "text", "text": "You are helpful", "cache_control": {"type": "ephemeral"}}] + assert result_msgs == messages + assert remaining == [] + + def test_system_list_injection(self): + messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}] + system = [ + {"type": "text", "text": "Part 1"}, + {"type": "text", "text": "Part 2"}, + ] + injection_points = [{"location": "message", "role": "system"}] + + _, result_sys, _ = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=system, + injection_points=injection_points, + ) + + assert result_sys[0] == {"type": "text", "text": "Part 1"} + assert result_sys[1] == {"type": "text", "text": "Part 2", "cache_control": {"type": "ephemeral"}} + + def test_user_message_injection_by_role(self): + messages = [ + {"role": "user", "content": [{"type": "text", "text": "First"}]}, + {"role": "assistant", "content": [{"type": "text", "text": "Response"}]}, + {"role": "user", "content": [{"type": "text", "text": "Second"}]}, + ] + injection_points = [{"location": "message", "role": "user"}] + + result_msgs, _, _ = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=None, + injection_points=injection_points, + ) + + assert result_msgs[0]["content"][-1].get("cache_control") == {"type": "ephemeral"} + assert result_msgs[2]["content"][-1].get("cache_control") == {"type": "ephemeral"} + assert result_msgs[1]["content"][-1].get("cache_control") is None + + def test_message_injection_by_index(self): + messages = [ + {"role": "user", "content": [{"type": "text", "text": "First"}]}, + {"role": "assistant", "content": [{"type": "text", "text": "Response"}]}, + {"role": "user", "content": [{"type": "text", "text": "Second"}]}, + ] + injection_points = [{"location": "message", "index": -1}] + + result_msgs, _, _ = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=None, + injection_points=injection_points, + ) + + assert result_msgs[2]["content"][-1].get("cache_control") == {"type": "ephemeral"} + assert result_msgs[0]["content"][-1].get("cache_control") is None + assert result_msgs[1]["content"][-1].get("cache_control") is None + + def test_mixed_system_and_message_injection(self): + messages = [ + {"role": "user", "content": [{"type": "text", "text": "Hello"}]}, + {"role": "assistant", "content": [{"type": "text", "text": "Hi"}]}, + {"role": "user", "content": [{"type": "text", "text": "Question"}]}, + ] + system = "System prompt" + injection_points = [ + {"location": "message", "role": "system"}, + {"location": "message", "index": -1}, + ] + + result_msgs, result_sys, _ = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=system, + injection_points=injection_points, + ) + + assert result_sys[0]["cache_control"] == {"type": "ephemeral"} + assert result_msgs[2]["content"][-1].get("cache_control") == {"type": "ephemeral"} + + def test_respects_max_4_blocks(self): + messages = [{"role": "user", "content": [{"type": "text", "text": f"Msg {i}"}]} for i in range(6)] + system = "System" + injection_points = [ + {"location": "message", "role": "system"}, + {"location": "message", "role": "user"}, + ] + + result_msgs, result_sys, _ = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=system, + injection_points=injection_points, + ) + + sys_blocks = sum(1 for b in (result_sys or []) if isinstance(b, dict) and b.get("cache_control") is not None) + total_blocks = sys_blocks + sum(AnthropicCacheControlHook._count_cache_control_blocks(m) for m in result_msgs) + assert total_blocks <= 4 + + def test_tool_config_points_forwarded_as_remaining(self): + messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}] + injection_points = [ + {"location": "message", "role": "user"}, + {"location": "tool_config"}, + ] + + _, _, remaining = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=None, + injection_points=injection_points, + ) + + assert remaining == [{"location": "tool_config"}] + + def test_no_injection_points_returns_unchanged(self): + messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}] + system = "System" + + result_msgs, result_sys, remaining = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=system, + injection_points=[], + ) + + assert result_msgs == messages + assert result_sys == system + assert remaining == [] + + def test_does_not_mutate_input(self): + messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}] + system = [{"type": "text", "text": "System"}] + injection_points = [{"location": "message", "role": "system"}] + + original_system = copy.deepcopy(system) + original_messages = copy.deepcopy(messages) + + AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=system, + injection_points=injection_points, + ) + + assert messages == original_messages + assert system == original_system + + def test_system_none_with_system_point_skipped(self): + messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}] + injection_points = [{"location": "message", "role": "system"}] + + result_msgs, result_sys, _ = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=None, + injection_points=injection_points, + ) + + assert result_sys is None + + def test_existing_cache_control_counted_toward_limit(self): + messages = [ + {"role": "user", "content": [{"type": "text", "text": "A", "cache_control": {"type": "ephemeral"}}]}, + {"role": "assistant", "content": [{"type": "text", "text": "B", "cache_control": {"type": "ephemeral"}}]}, + {"role": "user", "content": [{"type": "text", "text": "C", "cache_control": {"type": "ephemeral"}}]}, + {"role": "user", "content": [{"type": "text", "text": "D"}]}, + {"role": "user", "content": [{"type": "text", "text": "E"}]}, + ] + system = "System" + injection_points = [ + {"location": "message", "role": "system"}, + {"location": "message", "index": 3}, + {"location": "message", "index": 4}, + ] + + result_msgs, result_sys, _ = AnthropicCacheControlHook.apply_to_anthropic_messages_request( + messages=messages, + system=system, + injection_points=injection_points, + ) + + sys_blocks = sum(1 for b in (result_sys or []) if isinstance(b, dict) and b.get("cache_control") is not None) + total_blocks = sys_blocks + sum(AnthropicCacheControlHook._count_cache_control_blocks(m) for m in result_msgs) + assert total_blocks <= 4 diff --git a/tests/test_litellm/integrations/test_azure_sentinel.py b/tests/test_litellm/integrations/test_azure_sentinel.py index 30b246202fc..55e462c82b8 100644 --- a/tests/test_litellm/integrations/test_azure_sentinel.py +++ b/tests/test_litellm/integrations/test_azure_sentinel.py @@ -263,3 +263,36 @@ async def test_azure_sentinel_flushes_standard_and_audit_logs_separately(): ] assert "Custom-LiteLLM-Audit" in audit_call.kwargs["url"] assert json.loads(audit_call.kwargs["data"].decode("utf-8")) == [audit_log] + + +@pytest.mark.asyncio +async def test_azure_sentinel_audit_stream_name_from_env_var(monkeypatch): + """Audit stream resolves from AZURE_SENTINEL_AUDIT_STREAM_NAME when the string + callback constructs the logger with no audit_stream_name argument.""" + monkeypatch.setenv("AZURE_SENTINEL_STREAM_NAME", "Custom-LiteLLM-Standard") + monkeypatch.setenv("AZURE_SENTINEL_AUDIT_STREAM_NAME", "Custom-LiteLLM-Audit") + + with patch("asyncio.create_task", side_effect=_close_periodic_flush_task): + logger = AzureSentinelLogger( + dcr_immutable_id="dcr-test123456789", + endpoint="https://test-dce.eastus-1.ingest.monitor.azure.com", + tenant_id="test-tenant-id", + client_id="test-client-id", + client_secret="test-client-secret", + ) + + assert logger.audit_stream_name == "Custom-LiteLLM-Audit" + assert "streams/Custom-LiteLLM-Audit" in logger.audit_api_endpoint + assert "streams/Custom-LiteLLM-Standard" in logger.api_endpoint + + with patch("asyncio.create_task", side_effect=_close_periodic_flush_task): + explicit_logger = AzureSentinelLogger( + dcr_immutable_id="dcr-test123456789", + endpoint="https://test-dce.eastus-1.ingest.monitor.azure.com", + tenant_id="test-tenant-id", + client_id="test-client-id", + client_secret="test-client-secret", + audit_stream_name="Custom-LiteLLM-Explicit", + ) + + assert explicit_logger.audit_stream_name == "Custom-LiteLLM-Explicit" diff --git a/tests/test_litellm/integrations/test_custom_guardrail.py b/tests/test_litellm/integrations/test_custom_guardrail.py index 29e9f4529fc..d300f326b9e 100644 --- a/tests/test_litellm/integrations/test_custom_guardrail.py +++ b/tests/test_litellm/integrations/test_custom_guardrail.py @@ -833,6 +833,173 @@ class TestGuardrailSensitiveFieldStripping: assert "sk-secret" not in serialized +class TestGuardrailResponseCredentialMasking: + """LIT-4314 issue B regression: credentials embedded in guardrail_response + (via team callback_vars flowing through data["metadata"]) must be masked at + the construction seam so every downstream sink (SpendLogs, OTel, Langfuse, + custom loggers) sees masked values rather than plaintext. + """ + + def _make_guardrail(self): + from litellm.types.guardrails import GuardrailEventHooks + + return CustomGuardrail( + guardrail_name="test_guardrail", + event_hook=GuardrailEventHooks.pre_call, + ) + + def test_callback_vars_api_key_is_masked(self): + import json + + guardrail = self._make_guardrail() + request_data: dict = {"metadata": {}} + plaintext_key = "lsv2_pt_abcdef1234567890" + + guardrail.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response={ + "model": "gpt-4o-mini", + "messages": [{"role": "user", "content": "hi"}], + "metadata_snapshot": { + "callback_vars": { + "langsmith_api_key": plaintext_key, + "langsmith_project": "proj-name", + } + }, + }, + request_data=request_data, + guardrail_status="success", + duration=1.0, + ) + + logged = request_data["metadata"]["standard_logging_guardrail_information"][0][ + "guardrail_response" + ] + + masked_key = logged["metadata_snapshot"]["callback_vars"]["langsmith_api_key"] + assert masked_key != plaintext_key + assert "*" in masked_key + assert plaintext_key not in json.dumps(request_data) + + assert logged["model"] == "gpt-4o-mini" + assert logged["messages"] == [{"role": "user", "content": "hi"}] + assert ( + logged["metadata_snapshot"]["callback_vars"]["langsmith_project"] + == "proj-name" + ) + + def test_nested_user_api_key_auth_metadata_is_masked(self): + import json + + guardrail = self._make_guardrail() + request_data: dict = {"metadata": {}} + token_value = "1b01552f6e52e0d41963dd6a185bd6b074624e330999534ca7ff5adfdf622dfc" + + guardrail.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response={ + "evaluated_metadata": { + "user_api_key_auth": { + "token": token_value, + "api_key": token_value, + "metadata": { + "callback_vars": { + "langsmith_api_key": "lsv2_pt_super_secret_value_1234", + } + }, + } + } + }, + request_data=request_data, + guardrail_status="success", + ) + + serialized = json.dumps(request_data) + assert token_value not in serialized + assert "lsv2_pt_super_secret_value_1234" not in serialized + + def test_secret_fields_pop_still_runs(self): + import json + + guardrail = self._make_guardrail() + request_data: dict = {"metadata": {}} + + guardrail.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response={ + "model": "gpt-4", + "secret_fields": { + "raw_headers": { + "authorization": "Bearer sk-live-should-not-appear", + } + }, + }, + request_data=request_data, + guardrail_status="success", + ) + + serialized = json.dumps(request_data) + assert "secret_fields" not in serialized + assert "sk-live-should-not-appear" not in serialized + + def test_match_and_regex_redaction_still_runs(self): + guardrail = self._make_guardrail() + request_data: dict = {"metadata": {}} + + guardrail.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response={ + "filters": [{"regex": r"\d{3}-\d{2}-\d{4}", "action": "BLOCKED"}] + }, + request_data=request_data, + guardrail_status="success", + ) + + slg = request_data["metadata"]["standard_logging_guardrail_information"][0] + assert slg["guardrail_response"]["filters"][0]["regex"] == "[REDACTED]" + + def test_scalar_types_pass_through_unchanged(self): + guardrail = self._make_guardrail() + request_data: dict = {"metadata": {}} + + guardrail.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response={ + "flagged": True, + "score": 0.94, + "tokens_used": 42, + "categories": ["pii", "toxicity"], + }, + request_data=request_data, + guardrail_status="success", + ) + + logged = request_data["metadata"]["standard_logging_guardrail_information"][0][ + "guardrail_response" + ] + assert logged["flagged"] is True + assert logged["score"] == 0.94 + assert logged["tokens_used"] == 42 + assert logged["categories"] == ["pii", "toxicity"] + + def test_masking_reveals_prefix_and_suffix(self): + guardrail = self._make_guardrail() + request_data: dict = {"metadata": {}} + plaintext = "lsv2_pt_abcdef1234567890" + + guardrail.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response={ + "metadata_snapshot": { + "callback_vars": {"langsmith_api_key": plaintext} + } + }, + request_data=request_data, + guardrail_status="success", + ) + + masked = request_data["metadata"]["standard_logging_guardrail_information"][0][ + "guardrail_response" + ]["metadata_snapshot"]["callback_vars"]["langsmith_api_key"] + assert masked != plaintext + assert masked.startswith(plaintext[:4]) + assert masked.endswith(plaintext[-4:]) + + class TestCustomGuardrailPassthroughSupport: """Tests for passthrough endpoint guardrail support - Issue fixes.""" diff --git a/tests/test_litellm/integrations/test_prometheus_budget_metric_guard.py b/tests/test_litellm/integrations/test_prometheus_budget_metric_guard.py new file mode 100644 index 00000000000..ef844c80d4d --- /dev/null +++ b/tests/test_litellm/integrations/test_prometheus_budget_metric_guard.py @@ -0,0 +1,359 @@ +""" +Unit tests for the NoOpMetric guard in _increment_remaining_budget_metrics +and the per-entity guards in _set_*_budget_metrics_after_api_request. + +Regression tests that the specific bug can never happen again: +when budget gauges are excluded from prometheus_metrics_config (and therefore +created as NoOpMetric instances), the DB/cache lookup helpers must not be called. +""" + +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from prometheus_client import REGISTRY + +import litellm +from litellm.integrations.prometheus import PrometheusLogger +from litellm.types.integrations.prometheus import NoOpMetric + +_BUDGET_EXCLUDED_CONFIG = [ + { + "group": "core-only", + "metrics": [ + "litellm_requests_metric", + "litellm_total_tokens_metric", + ], + } +] + + +@pytest.fixture(autouse=True) +def cleanup_prometheus_registry(): + old_config = litellm.prometheus_metrics_config + for collector in list(REGISTRY._collector_to_names.keys()): + try: + REGISTRY.unregister(collector) + except Exception: + pass + yield + litellm.prometheus_metrics_config = old_config + for collector in list(REGISTRY._collector_to_names.keys()): + try: + REGISTRY.unregister(collector) + except Exception: + pass + + +def make_logger_with_budget_metrics_disabled() -> PrometheusLogger: + litellm.prometheus_metrics_config = _BUDGET_EXCLUDED_CONFIG + return PrometheusLogger() + + +def make_logger_with_all_metrics_enabled() -> PrometheusLogger: + litellm.prometheus_metrics_config = None + return PrometheusLogger() + + +COMMON_KWARGS = dict( + user_api_team="team-123", + user_api_team_alias="my-team", + user_api_key="hashed-key", + user_api_key_alias="my-key", + litellm_params={"metadata": {}}, + response_cost=0.001, + user_id="user-1", + user_api_key_org_id="org-1", +) + + +class TestBudgetGaugesAreNoopWhenExcluded: + def test_team_gauge_is_noop(self): + logger = make_logger_with_budget_metrics_disabled() + assert isinstance(logger.litellm_remaining_team_budget_metric, NoOpMetric) + + def test_api_key_gauge_is_noop(self): + logger = make_logger_with_budget_metrics_disabled() + assert isinstance(logger.litellm_remaining_api_key_budget_metric, NoOpMetric) + + def test_user_gauge_is_noop(self): + logger = make_logger_with_budget_metrics_disabled() + assert isinstance(logger.litellm_remaining_user_budget_metric, NoOpMetric) + + def test_org_gauge_is_noop(self): + logger = make_logger_with_budget_metrics_disabled() + assert isinstance(logger.litellm_remaining_org_budget_metric, NoOpMetric) + + def test_gauges_are_real_when_all_metrics_enabled(self): + logger = make_logger_with_all_metrics_enabled() + assert not isinstance(logger.litellm_remaining_team_budget_metric, NoOpMetric) + assert not isinstance(logger.litellm_remaining_api_key_budget_metric, NoOpMetric) + assert not isinstance(logger.litellm_remaining_user_budget_metric, NoOpMetric) + assert not isinstance(logger.litellm_remaining_org_budget_metric, NoOpMetric) + + +class TestTopLevelGuard: + @pytest.mark.asyncio + async def test_no_db_lookups_when_all_budget_gauges_are_noop(self): + """Regression: _increment_remaining_budget_metrics must return early + without any I/O when all four budget gauges are NoOpMetric.""" + logger = make_logger_with_budget_metrics_disabled() + + assemble_team = AsyncMock(return_value=MagicMock()) + assemble_key = AsyncMock(return_value=MagicMock()) + assemble_user = AsyncMock(return_value=MagicMock()) + + with ( + patch.object(logger, "_assemble_team_object", assemble_team), + patch.object(logger, "_assemble_key_object", assemble_key), + patch.object(logger, "_assemble_user_object", assemble_user), + ): + await logger._increment_remaining_budget_metrics(**COMMON_KWARGS) + + assemble_team.assert_not_called() + assemble_key.assert_not_called() + assemble_user.assert_not_called() + + @pytest.mark.asyncio + async def test_db_lookups_run_when_budget_gauges_are_real(self): + """When budget gauges are real Prometheus metrics, the assemble helpers + must be called so I/O proceeds normally.""" + logger = make_logger_with_all_metrics_enabled() + + assemble_team = AsyncMock( + return_value=MagicMock( + team_id="team-123", + team_alias="my-team", + spend=0.001, + max_budget=None, + budget_reset_at=None, + ) + ) + assemble_key = AsyncMock( + return_value=MagicMock( + token="hashed-key", + key_alias="my-key", + spend=0.001, + max_budget=None, + budget_reset_at=None, + ) + ) + assemble_user = AsyncMock( + return_value=MagicMock( + user_id="user-1", + spend=0.001, + max_budget=None, + budget_reset_at=None, + user_email=None, + user_alias=None, + ) + ) + + with ( + patch.object(logger, "_assemble_team_object", assemble_team), + patch.object(logger, "_assemble_key_object", assemble_key), + patch.object(logger, "_assemble_user_object", assemble_user), + patch.object(logger, "_set_team_budget_metrics", MagicMock()), + patch.object(logger, "_set_key_budget_metrics", MagicMock()), + patch.object(logger, "_set_user_budget_metrics", MagicMock()), + patch.object(logger, "_set_org_budget_metrics_after_api_request", AsyncMock()), + ): + await logger._increment_remaining_budget_metrics(**COMMON_KWARGS) + + assemble_team.assert_called_once() + assemble_key.assert_called_once() + assemble_user.assert_called_once() + + +class TestPerEntityGuards: + @pytest.mark.asyncio + async def test_team_guard_skips_lookup_when_team_gauge_is_noop(self): + """Per-entity guard: team assemble helper is not called when team gauge is NoOp, + even when key and user gauges are real.""" + logger = make_logger_with_all_metrics_enabled() + logger.litellm_remaining_team_budget_metric = NoOpMetric() + + assemble_team = AsyncMock(return_value=MagicMock()) + assemble_key = AsyncMock( + return_value=MagicMock( + token="hashed-key", + key_alias="my-key", + spend=0.001, + max_budget=None, + budget_reset_at=None, + ) + ) + assemble_user = AsyncMock( + return_value=MagicMock( + user_id="user-1", + spend=0.001, + max_budget=None, + budget_reset_at=None, + user_email=None, + user_alias=None, + ) + ) + + with ( + patch.object(logger, "_assemble_team_object", assemble_team), + patch.object(logger, "_assemble_key_object", assemble_key), + patch.object(logger, "_assemble_user_object", assemble_user), + patch.object(logger, "_set_team_budget_metrics", MagicMock()), + patch.object(logger, "_set_key_budget_metrics", MagicMock()), + patch.object(logger, "_set_user_budget_metrics", MagicMock()), + patch.object(logger, "_set_org_budget_metrics_after_api_request", AsyncMock()), + ): + await logger._increment_remaining_budget_metrics(**COMMON_KWARGS) + + assemble_team.assert_not_called() + assemble_key.assert_called_once() + assemble_user.assert_called_once() + + @pytest.mark.asyncio + async def test_key_guard_skips_lookup_when_key_gauge_is_noop(self): + """Per-entity guard: key assemble helper is not called when key gauge is NoOp, + even when team and user gauges are real.""" + logger = make_logger_with_all_metrics_enabled() + logger.litellm_remaining_api_key_budget_metric = NoOpMetric() + + assemble_team = AsyncMock( + return_value=MagicMock( + team_id="team-123", + team_alias="my-team", + spend=0.001, + max_budget=None, + budget_reset_at=None, + ) + ) + assemble_key = AsyncMock(return_value=MagicMock()) + assemble_user = AsyncMock( + return_value=MagicMock( + user_id="user-1", + spend=0.001, + max_budget=None, + budget_reset_at=None, + user_email=None, + user_alias=None, + ) + ) + + with ( + patch.object(logger, "_assemble_team_object", assemble_team), + patch.object(logger, "_assemble_key_object", assemble_key), + patch.object(logger, "_assemble_user_object", assemble_user), + patch.object(logger, "_set_team_budget_metrics", MagicMock()), + patch.object(logger, "_set_key_budget_metrics", MagicMock()), + patch.object(logger, "_set_user_budget_metrics", MagicMock()), + patch.object(logger, "_set_org_budget_metrics_after_api_request", AsyncMock()), + ): + await logger._increment_remaining_budget_metrics(**COMMON_KWARGS) + + assemble_key.assert_not_called() + assemble_team.assert_called_once() + assemble_user.assert_called_once() + + @pytest.mark.asyncio + async def test_user_guard_skips_lookup_when_user_gauge_is_noop(self): + """Per-entity guard: user assemble helper is not called when user gauge is NoOp, + even when team and key gauges are real.""" + logger = make_logger_with_all_metrics_enabled() + logger.litellm_remaining_user_budget_metric = NoOpMetric() + + assemble_team = AsyncMock( + return_value=MagicMock( + team_id="team-123", + team_alias="my-team", + spend=0.001, + max_budget=None, + budget_reset_at=None, + ) + ) + assemble_key = AsyncMock( + return_value=MagicMock( + token="hashed-key", + key_alias="my-key", + spend=0.001, + max_budget=None, + budget_reset_at=None, + ) + ) + assemble_user = AsyncMock(return_value=MagicMock()) + + with ( + patch.object(logger, "_assemble_team_object", assemble_team), + patch.object(logger, "_assemble_key_object", assemble_key), + patch.object(logger, "_assemble_user_object", assemble_user), + patch.object(logger, "_set_team_budget_metrics", MagicMock()), + patch.object(logger, "_set_key_budget_metrics", MagicMock()), + patch.object(logger, "_set_user_budget_metrics", MagicMock()), + patch.object(logger, "_set_org_budget_metrics_after_api_request", AsyncMock()), + ): + await logger._increment_remaining_budget_metrics(**COMMON_KWARGS) + + assemble_user.assert_not_called() + assemble_team.assert_called_once() + assemble_key.assert_called_once() + + @pytest.mark.asyncio + async def test_set_team_budget_metrics_directly_skips_when_gauge_is_noop(self): + """_set_team_budget_metrics_after_api_request returns early when team gauge is NoOp.""" + logger = make_logger_with_budget_metrics_disabled() + assemble_team = AsyncMock(return_value=MagicMock()) + + with patch.object(logger, "_assemble_team_object", assemble_team): + await logger._set_team_budget_metrics_after_api_request( + user_api_team="team-123", + user_api_team_alias="my-team", + team_spend=0.5, + team_max_budget=10.0, + response_cost=0.001, + ) + + assemble_team.assert_not_called() + + @pytest.mark.asyncio + async def test_set_api_key_budget_metrics_directly_skips_when_gauge_is_noop(self): + """_set_api_key_budget_metrics_after_api_request returns early when key gauge is NoOp.""" + logger = make_logger_with_budget_metrics_disabled() + assemble_key = AsyncMock(return_value=MagicMock()) + + with patch.object(logger, "_assemble_key_object", assemble_key): + await logger._set_api_key_budget_metrics_after_api_request( + user_api_key="hashed-key", + user_api_key_alias="my-key", + response_cost=0.001, + key_max_budget=10.0, + key_spend=0.5, + ) + + assemble_key.assert_not_called() + + @pytest.mark.asyncio + async def test_set_user_budget_metrics_directly_skips_when_gauge_is_noop(self): + """_set_user_budget_metrics_after_api_request returns early when user gauge is NoOp.""" + logger = make_logger_with_budget_metrics_disabled() + assemble_user = AsyncMock(return_value=MagicMock()) + + with patch.object(logger, "_assemble_user_object", assemble_user): + await logger._set_user_budget_metrics_after_api_request( + user_id="user-1", + user_spend=0.5, + user_max_budget=10.0, + response_cost=0.001, + ) + + assemble_user.assert_not_called() + + @pytest.mark.asyncio + async def test_set_org_budget_metrics_directly_skips_when_gauge_is_noop(self): + """_set_org_budget_metrics_after_api_request returns early when org gauge is NoOp. + The guard fires before any import of auth_checks, so prisma_client is never touched.""" + logger = make_logger_with_budget_metrics_disabled() + + set_org_metrics = MagicMock() + with patch.object(logger, "_set_org_budget_metrics", set_org_metrics): + await logger._set_org_budget_metrics_after_api_request( + org_id="org-1", + response_cost=0.001, + ) + + set_org_metrics.assert_not_called() diff --git a/tests/test_litellm/integrations/test_prometheus_budget_metrics_db_lookups.py b/tests/test_litellm/integrations/test_prometheus_budget_metrics_db_lookups.py new file mode 100644 index 00000000000..ce446ae3a19 --- /dev/null +++ b/tests/test_litellm/integrations/test_prometheus_budget_metrics_db_lookups.py @@ -0,0 +1,93 @@ +""" +Unit tests for PrometheusLogger._assemble_key_object DB access. + +The post-request budget metrics run for every LLM API request. Auth has +already cached the key object for any real key in the same request, so the +metrics path must read the cache only. Falling through to the DB turns every +request whose token has no DB row (e.g. master-key requests, whose token is +an alias hash that never matches a stored key) into per-request +LiteLLM_VerificationToken and LiteLLM_DeprecatedVerificationToken queries. +""" + +import datetime +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from prometheus_client import REGISTRY + +from litellm.caching.dual_cache import DualCache +from litellm.caching.in_memory_cache import InMemoryCache +from litellm.integrations.prometheus import PrometheusLogger +from litellm.proxy._types import UserAPIKeyAuth + + +@pytest.fixture(autouse=True) +def cleanup_prometheus_registry(): + collectors = list(REGISTRY._collector_to_names.keys()) + for collector in collectors: + try: + REGISTRY.unregister(collector) + except Exception: + pass + yield + collectors = list(REGISTRY._collector_to_names.keys()) + for collector in collectors: + try: + REGISTRY.unregister(collector) + except Exception: + pass + + +@pytest.fixture +def prometheus_logger(): + return PrometheusLogger() + + +@pytest.mark.asyncio +async def test_assemble_key_object_does_not_query_db_on_cache_miss(prometheus_logger): + mock_prisma = MagicMock() + mock_prisma.get_data = AsyncMock() + cache = DualCache(in_memory_cache=InMemoryCache()) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.user_api_key_cache", cache), + ): + result = await prometheus_logger._assemble_key_object( + user_api_key="hashed-token-not-in-cache", + user_api_key_alias="", + key_max_budget=None, + key_spend=1.0, + response_cost=0.5, + ) + + mock_prisma.get_data.assert_not_called() + assert result.spend == 1.5 + assert result.budget_reset_at is None + + +@pytest.mark.asyncio +async def test_assemble_key_object_reads_budget_reset_at_from_cache(prometheus_logger): + hashed_token = "hashed-token-in-cache" + reset_at = datetime.datetime(2026, 8, 1, tzinfo=datetime.timezone.utc) + cached_key = UserAPIKeyAuth(token=hashed_token, budget_reset_at=reset_at) + + mock_prisma = MagicMock() + mock_prisma.get_data = AsyncMock() + cache = DualCache(in_memory_cache=InMemoryCache()) + await cache.async_set_cache(key=hashed_token, value=cached_key) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.user_api_key_cache", cache), + ): + result = await prometheus_logger._assemble_key_object( + user_api_key=hashed_token, + user_api_key_alias="alias", + key_max_budget=10.0, + key_spend=1.0, + response_cost=0.5, + ) + + mock_prisma.get_data.assert_not_called() + assert result.budget_reset_at == reset_at diff --git a/tests/test_litellm/integrations/test_prometheus_budget_metrics_timeout.py b/tests/test_litellm/integrations/test_prometheus_budget_metrics_timeout.py new file mode 100644 index 00000000000..a4d245e9dc0 --- /dev/null +++ b/tests/test_litellm/integrations/test_prometheus_budget_metrics_timeout.py @@ -0,0 +1,168 @@ +""" +Unit tests for the per-request budget-metric emission timeout in +PrometheusLogger._increment_remaining_budget_metrics. + +A slow Redis/DB lookup in one of the budget branches must not let the gather run +unbounded; it is wrapped in asyncio.wait_for so the success-logging coroutine +cannot exceed the LoggingWorker watchdog and get the whole event cancelled. +""" + +import asyncio +from unittest.mock import AsyncMock, patch + +import pytest +from prometheus_client import REGISTRY + +from litellm.integrations.prometheus import ( + PrometheusLogger, + _DEFAULT_BUDGET_METRICS_PER_REQUEST_TIMEOUT, + _get_budget_metrics_per_request_timeout, +) + +TIMEOUT_ENV = "PROMETHEUS_BUDGET_METRICS_PER_REQUEST_TIMEOUT" + + +@pytest.fixture(autouse=True) +def cleanup_prometheus_registry(): + collectors = list(REGISTRY._collector_to_names.keys()) + for collector in collectors: + try: + REGISTRY.unregister(collector) + except Exception: + pass + yield + collectors = list(REGISTRY._collector_to_names.keys()) + for collector in collectors: + try: + REGISTRY.unregister(collector) + except Exception: + pass + + +@pytest.fixture +def prometheus_logger(): + return PrometheusLogger() + + +def _call_increment(logger: PrometheusLogger): + return logger._increment_remaining_budget_metrics( + user_api_team="team-1", + user_api_team_alias="team-alias", + user_api_key="key-1", + user_api_key_alias="key-alias", + litellm_params={"metadata": {}}, + response_cost=0.01, + user_id="user-1", + user_api_key_org_id="org-1", + ) + + +def _skip_logged(debug_mock) -> bool: + return any("skipping" in str(call.args[0]) for call in debug_mock.call_args_list if call.args) + + +@pytest.mark.asyncio +async def test_budget_metric_emission_skips_on_timeout(prometheus_logger, monkeypatch): + """A branch slower than the timeout is skipped without propagating, and the + skip is logged instead of cancelling the success-logging event.""" + monkeypatch.setenv(TIMEOUT_ENV, "0.05") + + async def _slow_branch(**kwargs): + await asyncio.sleep(30) + + prometheus_logger._set_api_key_budget_metrics_after_api_request = _slow_branch + prometheus_logger._set_team_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_user_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_org_budget_metrics_after_api_request = AsyncMock() + + with patch("litellm.integrations.prometheus.verbose_logger") as mock_logger: + await _call_increment(prometheus_logger) + + assert _skip_logged(mock_logger.debug) + + +@pytest.mark.asyncio +async def test_budget_metric_emission_completes_within_timeout(prometheus_logger, monkeypatch): + """With a generous timeout every branch is awaited and no skip is logged.""" + monkeypatch.setenv(TIMEOUT_ENV, "5.0") + + prometheus_logger._set_api_key_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_team_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_user_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_org_budget_metrics_after_api_request = AsyncMock() + + with patch("litellm.integrations.prometheus.verbose_logger") as mock_logger: + await _call_increment(prometheus_logger) + + assert prometheus_logger._set_api_key_budget_metrics_after_api_request.await_count == 1 + assert prometheus_logger._set_team_budget_metrics_after_api_request.await_count == 1 + assert prometheus_logger._set_user_budget_metrics_after_api_request.await_count == 1 + assert prometheus_logger._set_org_budget_metrics_after_api_request.await_count == 1 + assert not _skip_logged(mock_logger.debug) + + +@pytest.mark.asyncio +async def test_invalid_timeout_env_falls_back_to_default(prometheus_logger, monkeypatch): + """A malformed timeout env value must not raise (which would recreate the + failure mode); it falls back to the default and every branch still runs.""" + monkeypatch.setenv(TIMEOUT_ENV, "not-a-number") + + prometheus_logger._set_api_key_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_team_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_user_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_org_budget_metrics_after_api_request = AsyncMock() + + await _call_increment(prometheus_logger) + + assert prometheus_logger._set_api_key_budget_metrics_after_api_request.await_count == 1 + assert prometheus_logger._set_org_budget_metrics_after_api_request.await_count == 1 + + +@pytest.mark.parametrize("value", ["not-a-number", "0", "-1", "nan", "inf", "-inf"]) +def test_unusable_timeout_env_falls_back_to_default(value, monkeypatch): + """Values that parse but disable or unbound the timeout (0, negative, nan, + inf) must fall back to the default instead of being used; otherwise they + either skip every emission or recreate the unbounded-wait failure mode.""" + monkeypatch.setenv(TIMEOUT_ENV, value) + + assert _get_budget_metrics_per_request_timeout() == _DEFAULT_BUDGET_METRICS_PER_REQUEST_TIMEOUT + + +@pytest.mark.parametrize("value,expected", [("0.05", 0.05), ("5.0", 5.0), ("30", 30.0)]) +def test_valid_timeout_env_is_used(value, expected, monkeypatch): + """A finite positive value is parsed and returned unchanged.""" + monkeypatch.setenv(TIMEOUT_ENV, value) + + assert _get_budget_metrics_per_request_timeout() == expected + + +def test_missing_timeout_env_uses_default(monkeypatch): + """With the env unset the default is returned.""" + monkeypatch.delenv(TIMEOUT_ENV, raising=False) + + assert _get_budget_metrics_per_request_timeout() == _DEFAULT_BUDGET_METRICS_PER_REQUEST_TIMEOUT + + +@pytest.mark.asyncio +async def test_outer_cancellation_still_propagates(prometheus_logger, monkeypatch): + """Only asyncio.TimeoutError is swallowed; an outer cancellation (cooperative + shutdown / watchdog) injected while awaiting must still propagate.""" + monkeypatch.setenv(TIMEOUT_ENV, "30") + + started = asyncio.Event() + + async def _slow_branch(**kwargs): + started.set() + await asyncio.sleep(30) + + prometheus_logger._set_api_key_budget_metrics_after_api_request = _slow_branch + prometheus_logger._set_team_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_user_budget_metrics_after_api_request = AsyncMock() + prometheus_logger._set_org_budget_metrics_after_api_request = AsyncMock() + + task = asyncio.create_task(_call_increment(prometheus_logger)) + await started.wait() + task.cancel() + + with pytest.raises(asyncio.CancelledError): + await task diff --git a/tests/test_litellm/integrations/test_prometheus_labels.py b/tests/test_litellm/integrations/test_prometheus_labels.py index 70cc9ab33d6..a7d6e163eaf 100644 --- a/tests/test_litellm/integrations/test_prometheus_labels.py +++ b/tests/test_litellm/integrations/test_prometheus_labels.py @@ -2,12 +2,35 @@ Unit tests for prometheus metric labels configuration """ +import pytest + from litellm.types.integrations.prometheus import ( PrometheusMetricLabels, UserAPIKeyLabelNames, ) +def _clear_prometheus_registry() -> None: + from prometheus_client import REGISTRY + + for collector in list(REGISTRY._collector_to_names.keys()): + try: + REGISTRY.unregister(collector) + except Exception: + pass + + +def _collected_samples(metric_name: str): + from prometheus_client import REGISTRY + + return [ + sample + for metric in REGISTRY.collect() + for sample in metric.samples + if sample.name == metric_name + ] + + def test_user_email_in_required_metrics(): """ Test that user_email label is present in all the metrics that should have it: @@ -85,6 +108,166 @@ def test_api_provider_in_spend_and_requests_metrics(): print(f"✅ {metric_name} contains api_provider label") +def test_api_provider_in_token_latency_and_request_metrics(): + """ + Regression test for LIT-4178. + + These metrics are all emitted from the same call site (async_log_success_event + / async_post_call_failure_hook) as litellm_spend_metric and + litellm_requests_metric, which already carry api_provider. They were the odd + ones out with no way to break down tokens, latency, request counts or cache + hits by upstream provider, even though the provider is available on the + payload as custom_llm_provider. + """ + api_provider_label = UserAPIKeyLabelNames.API_PROVIDER.value + + metrics_with_api_provider = [ + "litellm_llm_api_latency_metric", + "litellm_llm_api_time_to_first_token_metric", + "litellm_request_total_latency_metric", + "litellm_request_queue_time_seconds", + "litellm_proxy_total_requests_metric", + "litellm_proxy_failed_requests_metric", + "litellm_input_tokens_metric", + "litellm_total_tokens_metric", + "litellm_output_tokens_metric", + "litellm_cache_hits_metric", + "litellm_cache_misses_metric", + # The remaining cache metrics share _cache_metric_labels, so they pick up + # api_provider from the same change. Assert them explicitly so the shared + # list can't silently drop the label from them. + "litellm_cached_tokens_metric", + "litellm_provider_cache_read_input_tokens_metric", + "litellm_provider_cache_creation_input_tokens_metric", + ] + + for metric_name in metrics_with_api_provider: + labels = PrometheusMetricLabels.get_labels(metric_name) + assert ( + api_provider_label in labels + ), f"Metric {metric_name} should contain api_provider label" + + +def test_api_provider_value_flows_through_label_factory(): + """ + The label being in the allow-list is necessary but not sufficient: the + factory must also carry the value from the enum through to the emitted + label. This would fail if the label were dropped from the metric's list or + if the value plumbing regressed, which the allow-list assertion above cannot + catch on its own. + """ + from unittest.mock import MagicMock + + from litellm.integrations.prometheus import ( + PrometheusLogger, + UserAPIKeyLabelValues, + prometheus_label_factory, + ) + + prometheus_logger = MagicMock() + prometheus_logger._cached_metric_labels = {} + prometheus_logger.label_filters = {} + prometheus_logger.get_labels_for_metric = ( + PrometheusLogger.get_labels_for_metric.__get__(prometheus_logger) + ) + + enum_values = UserAPIKeyLabelValues( + api_provider="anthropic", + litellm_model_name="claude-sonnet-4", + requested_model="claude", + status_code="200", + ) + + for metric_name in [ + "litellm_input_tokens_metric", + "litellm_total_tokens_metric", + "litellm_output_tokens_metric", + "litellm_llm_api_latency_metric", + "litellm_request_total_latency_metric", + "litellm_proxy_total_requests_metric", + "litellm_cache_hits_metric", + ]: + labels = prometheus_label_factory( + supported_enum_labels=prometheus_logger.get_labels_for_metric( + metric_name=metric_name + ), + enum_values=enum_values, + ) + assert ( + labels.get("api_provider") == "anthropic" + ), f"{metric_name} should emit api_provider=anthropic, got {labels.get('api_provider')!r}" + + +def test_extract_api_provider_from_request_data_failure_path(): + """ + On the client-side failure path the provider is not always known. Prefer the + resolved custom_llm_provider on litellm_params, fall back to a partial + standard_logging_object (e.g. a stream that broke mid-flight), then infer it + from the requested model name, and return None only when nothing maps so the + label emits empty rather than a guess. + """ + from litellm.integrations.prometheus import PrometheusLogger + + extract = PrometheusLogger._extract_api_provider_from_request_data + + assert extract({"litellm_params": {"custom_llm_provider": "bedrock"}}) == "bedrock" + assert ( + extract({"standard_logging_object": {"custom_llm_provider": "vertex_ai"}}) + == "vertex_ai" + ) + # litellm_params wins over standard_logging_object when both are present + assert ( + extract( + { + "litellm_params": {"custom_llm_provider": "openai"}, + "standard_logging_object": {"custom_llm_provider": "azure"}, + } + ) + == "openai" + ) + # Fallback: infer provider from the requested model name when the proxy's + # failure request_data carries only the client-supplied model. This is the + # common client-side failure case (e.g. an invalid param rejected with 400). + assert extract({"model": "gpt-4o-mini"}) == "openai" + assert extract({"litellm_params": {"model": "anthropic/claude-haiku-4-5"}}) == "anthropic" + assert extract({}) is None + # Unmappable model name -> None, not a guess + assert extract({"model": "some-unknown-model-xyz"}) is None + + +def test_extract_api_provider_swallows_unknown_model_but_logs_unexpected_errors(): + """ + get_llm_provider raises BadRequestError for a model that maps to no + provider; that is the expected miss and must resolve to None quietly. Any + other exception is unexpected (e.g. a real bug) and must not vanish + silently, nor break metric emission: it is logged and still returns None. + """ + from unittest.mock import patch + + import litellm + from litellm.integrations.prometheus import PrometheusLogger + + extract = PrometheusLogger._extract_api_provider_from_request_data + + # Expected miss: BadRequestError -> None, no log noise + with patch.object( + litellm, + "get_llm_provider", + side_effect=litellm.exceptions.BadRequestError( + message="no provider", model="x", llm_provider="y" + ), + ): + with patch("litellm.integrations.prometheus.verbose_logger") as mock_logger: + assert extract({"model": "x"}) is None + mock_logger.debug.assert_not_called() + + # Unexpected error: must be logged and still return None (never raised) + with patch.object(litellm, "get_llm_provider", side_effect=RuntimeError("boom")): + with patch("litellm.integrations.prometheus.verbose_logger") as mock_logger: + assert extract({"model": "x"}) is None + mock_logger.debug.assert_called_once() + + def test_user_email_label_exists(): """Test that the USER_EMAIL label is properly defined""" assert UserAPIKeyLabelNames.USER_EMAIL.value == "user_email" @@ -427,6 +610,112 @@ def test_prometheus_label_value_sanitization_non_string_types(): print("✅ Non-string values are coerced to str") +@pytest.mark.asyncio +async def test_success_hook_emits_api_provider_value_on_token_metric(): + """ + End-to-end emit wiring for the success path. + + The label-list and factory tests prove the label exists and that the factory + carries a value handed to it, but neither drives the real logger, so deleting + the production api_provider=standard_logging_payload["custom_llm_provider"] + assignment in async_log_success_event would still pass them. This drives + async_log_success_event with a payload whose provider is openai and asserts + the collected litellm_total_tokens_metric sample actually carries + api_provider="openai"; it fails if that assignment is removed. + """ + import datetime + + from litellm.integrations.prometheus import PrometheusLogger + + payload = { + "id": "t", + "call_type": "completion", + "response_cost": 0.001, + "status": "success", + "total_tokens": 30, + "prompt_tokens": 20, + "completion_tokens": 10, + "startTime": 1.0, + "endTime": 2.0, + "completionStartTime": 1.5, + "model": "gpt-4o-mini", + "model_id": "model-123", + "model_group": "gpt-4o-mini", + "api_base": "https://api.openai.com", + "custom_llm_provider": "openai", + "request_tags": [], + "end_user": None, + "cache_hit": False, + "metadata": { + "user_api_key_hash": "h", + "user_api_key_alias": "a", + "user_api_key_team_id": "t", + "user_api_key_team_alias": "ta", + "user_api_key_user_id": "u", + "user_api_key_user_email": "e@x.com", + "user_api_key_org_id": None, + "user_api_key_org_alias": None, + "requester_metadata": None, + "user_api_key_end_user_id": None, + }, + "hidden_params": {"litellm_overhead_time_ms": None, "additional_headers": None}, + } + + _clear_prometheus_registry() + try: + logger = PrometheusLogger() + now = datetime.datetime.now() + await logger.async_log_success_event( + { + "model": "gpt-4o-mini", + "litellm_params": {"metadata": {}}, + "standard_logging_object": payload, + }, + None, + now, + now, + ) + samples = _collected_samples("litellm_total_tokens_metric_total") + assert samples, "expected litellm_total_tokens_metric to be emitted" + assert all(s.labels.get("api_provider") == "openai" for s in samples), ( + "collected token metric must carry api_provider=openai, got " + f"{[s.labels.get('api_provider') for s in samples]}" + ) + finally: + _clear_prometheus_registry() + + +@pytest.mark.asyncio +async def test_failure_hook_emits_api_provider_value_on_failed_requests_metric(): + """ + End-to-end emit wiring for the failure path. + + async_post_call_failure_hook on a request whose model resolves to openai must + emit litellm_proxy_failed_requests_metric with api_provider="openai". This + fails if the api_provider assignment in the failure hook is removed, which the + helper-only test cannot catch. + """ + from litellm.integrations.prometheus import PrometheusLogger + from litellm.proxy._types import UserAPIKeyAuth + + _clear_prometheus_registry() + try: + logger = PrometheusLogger() + await logger.async_post_call_failure_hook( + request_data={"model": "gpt-4o-mini", "metadata": {}}, + original_exception=Exception("boom"), + user_api_key_dict=UserAPIKeyAuth(token="tok"), + ) + samples = _collected_samples("litellm_proxy_failed_requests_metric_total") + assert samples, "expected litellm_proxy_failed_requests_metric to be emitted" + assert any(s.labels.get("api_provider") == "openai" for s in samples), ( + "collected failed-requests metric must carry api_provider=openai, got " + f"{[s.labels.get('api_provider') for s in samples]}" + ) + finally: + _clear_prometheus_registry() + + if __name__ == "__main__": test_user_email_in_required_metrics() test_user_email_label_exists() diff --git a/tests/test_litellm/integrations/test_prometheus_mcp_tool_metrics.py b/tests/test_litellm/integrations/test_prometheus_mcp_tool_metrics.py new file mode 100644 index 00000000000..22c36f00ca9 --- /dev/null +++ b/tests/test_litellm/integrations/test_prometheus_mcp_tool_metrics.py @@ -0,0 +1,276 @@ +""" +Unit tests for MCP tool call Prometheus metrics (LIT-3765). + +These metrics expose ``mcp_tool_call_metadata`` in Prometheus so Grafana +dashboards can break down MCP usage by server and tool name. + +Run with: + uv run pytest tests/test_litellm/integrations/test_prometheus_mcp_tool_metrics.py -v +""" + +from typing import get_args +from unittest.mock import MagicMock + +import pytest + +from litellm.integrations.prometheus import PrometheusLogger +from litellm.types.integrations.prometheus import ( + DEFINED_PROMETHEUS_METRICS, + PrometheusMetricLabels, + UserAPIKeyLabelNames, + UserAPIKeyLabelValues, +) + + +MCP_METRICS = ( + "litellm_mcp_tool_calls_total", + "litellm_mcp_tool_call_spend_metric", +) + + +def _make_mock_logger(): + logger = MagicMock() + for name in MCP_METRICS: + setattr(logger, name, MagicMock()) + logger.get_labels_for_metric = MagicMock( + return_value=PrometheusMetricLabels.litellm_mcp_tool_calls_total, + ) + return logger + + +def _make_enum_values( + *, + mcp_tool_name: str = "get_weather", + mcp_server_name: str = "weather-server", +) -> UserAPIKeyLabelValues: + return UserAPIKeyLabelValues( + mcp_tool_name=mcp_tool_name, + mcp_server_name=mcp_server_name, + hashed_api_key="sk-hash-123", + api_key_alias="test-key", + team="team-1", + team_alias="Test Team", + user="user-1", + end_user="end-user-1", + ) + + +def _make_payload( + *, + mcp_tool_name: str = "get_weather", + mcp_server_name: str = "weather-server", + response_cost: float = 0.005, +) -> dict: + return { + "model": "gpt-4o", + "model_group": "gpt-4o", + "model_id": "model-123", + "api_base": "https://api.openai.com", + "custom_llm_provider": "openai", + "response_cost": response_cost, + "completion_tokens": 50, + "prompt_tokens": 100, + "total_tokens": 150, + "request_tags": [], + "stream": False, + "metadata": { + "user_api_key_hash": "sk-hash-123", + "user_api_key_alias": "test-key", + "user_api_key_team_id": "team-1", + "user_api_key_team_alias": "Test Team", + "user_api_key_user_id": "user-1", + "user_api_key_user_email": None, + "user_api_key_org_id": None, + "user_api_key_org_alias": None, + "mcp_tool_call_metadata": { + "name": mcp_tool_name, + "mcp_server_name": mcp_server_name, + "namespaced_tool_name": f"{mcp_server_name}/{mcp_tool_name}", + "arguments": {"city": "SF"}, + "result": {"temp": 72}, + }, + }, + } + + +class TestMCPMetricRegistration: + def test_metrics_in_defined_prometheus_metrics(self): + defined = get_args(DEFINED_PROMETHEUS_METRICS) + for name in MCP_METRICS: + assert name in defined, f"{name} missing from DEFINED_PROMETHEUS_METRICS" + + def test_metric_labels_defined(self): + for name in MCP_METRICS: + assert hasattr(PrometheusMetricLabels, name), f"{name} missing from PrometheusMetricLabels" + + def test_mcp_labels_include_tool_and_server_name(self): + labels = PrometheusMetricLabels.litellm_mcp_tool_calls_total + assert UserAPIKeyLabelNames.MCP_TOOL_NAME.value in labels + assert UserAPIKeyLabelNames.MCP_SERVER_NAME.value in labels + + def test_spend_metric_shares_label_set_with_calls_metric(self): + assert ( + PrometheusMetricLabels.litellm_mcp_tool_call_spend_metric + == PrometheusMetricLabels.litellm_mcp_tool_calls_total + ) + assert ( + PrometheusMetricLabels.litellm_mcp_tool_call_spend_metric + is not PrometheusMetricLabels.litellm_mcp_tool_calls_total + ) + + def test_enum_values_accept_mcp_fields(self): + vals = _make_enum_values() + assert vals.mcp_tool_name == "get_weather" + assert vals.mcp_server_name == "weather-server" + + def test_enum_values_default_mcp_fields_to_none(self): + vals = UserAPIKeyLabelValues(user="u1") + assert vals.mcp_tool_name is None + assert vals.mcp_server_name is None + + +class TestIncrementMCPToolCallMetrics: + def test_increments_calls_counter_when_mcp_metadata_present(self): + logger = _make_mock_logger() + payload = _make_payload() + enum_values = _make_enum_values() + + PrometheusLogger._increment_mcp_tool_call_metrics( + logger, + standard_logging_payload=payload, + enum_values=enum_values, + response_cost=0.005, + ) + + logger.litellm_mcp_tool_calls_total.labels.assert_called_once() + logger.litellm_mcp_tool_calls_total.labels().inc.assert_called_once_with(1.0) + + def test_increments_spend_counter_when_cost_positive(self): + logger = _make_mock_logger() + payload = _make_payload(response_cost=0.01) + enum_values = _make_enum_values() + + PrometheusLogger._increment_mcp_tool_call_metrics( + logger, + standard_logging_payload=payload, + enum_values=enum_values, + response_cost=0.01, + ) + + logger.litellm_mcp_tool_call_spend_metric.labels.assert_called_once() + logger.litellm_mcp_tool_call_spend_metric.labels().inc.assert_called_once_with(0.01) + + def test_skips_spend_counter_when_cost_zero(self): + logger = _make_mock_logger() + payload = _make_payload(response_cost=0.0) + enum_values = _make_enum_values() + + PrometheusLogger._increment_mcp_tool_call_metrics( + logger, + standard_logging_payload=payload, + enum_values=enum_values, + response_cost=0.0, + ) + + logger.litellm_mcp_tool_calls_total.labels.assert_called_once() + logger.litellm_mcp_tool_call_spend_metric.labels.assert_not_called() + + def test_noop_when_no_mcp_metadata(self): + logger = _make_mock_logger() + payload = _make_payload() + payload["metadata"]["mcp_tool_call_metadata"] = None + enum_values = _make_enum_values() + + PrometheusLogger._increment_mcp_tool_call_metrics( + logger, + standard_logging_payload=payload, + enum_values=enum_values, + response_cost=0.005, + ) + + for name in MCP_METRICS: + getattr(logger, name).labels.assert_not_called() + + def test_noop_when_metadata_missing(self): + logger = _make_mock_logger() + payload = {"metadata": None} + enum_values = _make_enum_values() + + PrometheusLogger._increment_mcp_tool_call_metrics( + logger, + standard_logging_payload=payload, + enum_values=enum_values, + response_cost=0.005, + ) + + for name in MCP_METRICS: + getattr(logger, name).labels.assert_not_called() + + def test_label_values_carry_tool_and_server_name(self): + logger = _make_mock_logger() + payload = _make_payload( + mcp_tool_name="search_docs", + mcp_server_name="docs-mcp", + ) + enum_values = _make_enum_values() + + PrometheusLogger._increment_mcp_tool_call_metrics( + logger, + standard_logging_payload=payload, + enum_values=enum_values, + response_cost=0.005, + ) + + labels_passed = logger.litellm_mcp_tool_calls_total.labels.call_args + assert labels_passed.kwargs["mcp_tool_name"] == "search_docs" + assert labels_passed.kwargs["mcp_server_name"] == "docs-mcp" + + def test_label_values_carry_team_and_key_from_parent(self): + logger = _make_mock_logger() + payload = _make_payload() + enum_values = UserAPIKeyLabelValues( + hashed_api_key="sk-parent-key", + api_key_alias="parent-alias", + team="parent-team", + team_alias="Parent Team", + user="parent-user", + end_user="parent-end-user", + ) + + PrometheusLogger._increment_mcp_tool_call_metrics( + logger, + standard_logging_payload=payload, + enum_values=enum_values, + response_cost=0.005, + ) + + labels_passed = logger.litellm_mcp_tool_calls_total.labels.call_args + assert labels_passed.kwargs["hashed_api_key"] == "sk-parent-key" + assert labels_passed.kwargs["team"] == "parent-team" + assert labels_passed.kwargs["team_alias"] == "Parent Team" + assert labels_passed.kwargs["user"] == "parent-user" + + def test_handles_missing_server_name_gracefully(self): + logger = _make_mock_logger() + payload = _make_payload() + payload["metadata"]["mcp_tool_call_metadata"] = { + "name": "standalone_tool", + "arguments": {}, + "result": {}, + } + enum_values = _make_enum_values() + + PrometheusLogger._increment_mcp_tool_call_metrics( + logger, + standard_logging_payload=payload, + enum_values=enum_values, + response_cost=0.0, + ) + + labels_passed = logger.litellm_mcp_tool_calls_total.labels.call_args + assert labels_passed.kwargs["mcp_tool_name"] == "standalone_tool" + assert labels_passed.kwargs["mcp_server_name"] is None + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/test_litellm/integrations/test_prometheus_spend_logs_metadata.py b/tests/test_litellm/integrations/test_prometheus_spend_logs_metadata.py index 31934e5fd8e..e2af6fd2daf 100644 --- a/tests/test_litellm/integrations/test_prometheus_spend_logs_metadata.py +++ b/tests/test_litellm/integrations/test_prometheus_spend_logs_metadata.py @@ -5,7 +5,10 @@ Verifies that metadata from x-litellm-spend-logs-metadata header is available in Prometheus custom labels via combined_metadata. """ -from litellm.integrations.prometheus import get_custom_labels_from_metadata +from litellm.integrations.prometheus import ( + _get_combined_custom_metadata_from_standard_logging_payload, + get_custom_labels_from_metadata, +) def test_get_custom_labels_includes_spend_logs_metadata(monkeypatch): @@ -109,3 +112,96 @@ def test_combined_metadata_with_none_spend_logs(monkeypatch): result = get_custom_labels_from_metadata(combined_metadata) assert result == {"metadata_foo": "bar"} + + +def test_combined_metadata_includes_top_level_fields(): + """ + Regression test for LIT-3741: user_api_key_project_alias (and other + top-level metadata fields) must be included in the combined metadata + so they can be referenced via custom_prometheus_metadata_labels. + """ + standard_logging_payload = { + "metadata": { + "user_api_key_hash": "sk-abc123", + "user_api_key_alias": "hotel-key", + "user_api_key_team_id": "team-1", + "user_api_key_team_alias": "hotel-team", + "user_api_key_project_id": "proj-1", + "user_api_key_project_alias": "hotel-recommendations", + "user_api_key_user_id": "user-1", + "user_api_key_user_email": "user@example.com", + "user_api_key_end_user_id": None, + "user_api_key_org_id": None, + "user_api_key_org_alias": None, + "user_api_key_request_route": "/v1/chat/completions", + "requester_metadata": {"custom_field": "custom_value"}, + "user_api_key_auth_metadata": {"auth_field": "auth_value"}, + "spend_logs_metadata": None, + } + } + + combined = _get_combined_custom_metadata_from_standard_logging_payload( + standard_logging_payload + ) + + assert combined["user_api_key_project_alias"] == "hotel-recommendations" + assert combined["user_api_key_project_id"] == "proj-1" + assert combined["user_api_key_team_alias"] == "hotel-team" + assert combined["user_api_key_request_route"] == "/v1/chat/completions" + assert combined["custom_field"] == "custom_value" + assert combined["auth_field"] == "auth_value" + + +def test_project_alias_accessible_via_custom_prometheus_labels(monkeypatch): + """ + Regression test for LIT-3741: configuring + custom_prometheus_metadata_labels with "metadata.user_api_key_project_alias" + should produce a label with the project's alias value. + """ + monkeypatch.setattr( + "litellm.custom_prometheus_metadata_labels", + ["metadata.user_api_key_project_alias"], + ) + + standard_logging_payload = { + "metadata": { + "user_api_key_project_alias": "hotel-recommendations", + "requester_metadata": None, + "user_api_key_auth_metadata": None, + "spend_logs_metadata": None, + } + } + + combined = _get_combined_custom_metadata_from_standard_logging_payload( + standard_logging_payload + ) + result = get_custom_labels_from_metadata(combined) + + assert result == {"metadata_user_api_key_project_alias": "hotel-recommendations"} + + +def test_project_alias_accessible_without_prefix(monkeypatch): + """ + user_api_key_project_alias should also be accessible without + the "metadata." prefix in custom_prometheus_metadata_labels config. + """ + monkeypatch.setattr( + "litellm.custom_prometheus_metadata_labels", + ["user_api_key_project_alias"], + ) + + standard_logging_payload = { + "metadata": { + "user_api_key_project_alias": "hotel-recommendations", + "requester_metadata": None, + "user_api_key_auth_metadata": None, + "spend_logs_metadata": None, + } + } + + combined = _get_combined_custom_metadata_from_standard_logging_payload( + standard_logging_payload + ) + result = get_custom_labels_from_metadata(combined) + + assert result == {"user_api_key_project_alias": "hotel-recommendations"} diff --git a/tests/test_litellm/integrations/test_s3_v2.py b/tests/test_litellm/integrations/test_s3_v2.py index 3f21de41c53..246b378c982 100644 --- a/tests/test_litellm/integrations/test_s3_v2.py +++ b/tests/test_litellm/integrations/test_s3_v2.py @@ -1194,3 +1194,156 @@ def test_s3_callback_params_override_empty_dict_is_opt_in(): assert logger.s3_bucket_name is None finally: litellm.s3_callback_params = original + + +def _expected_content_md5(payload: dict) -> str: + import base64 + import hashlib + + from litellm.litellm_core_utils.safe_json_dumps import safe_dumps + + json_string = safe_dumps(payload) + return base64.b64encode( + hashlib.md5(json_string.encode("utf-8"), usedforsecurity=False).digest() + ).decode() + + +def _require_non_security_md5(monkeypatch): + import hashlib + + original_md5 = hashlib.md5 + + def fips_md5(data=b"", *, usedforsecurity=True): + if usedforsecurity: + raise ValueError("MD5 blocked for security use") + return original_md5(data, usedforsecurity=usedforsecurity) + + monkeypatch.setattr(hashlib, "md5", fips_md5) + + +@pytest.mark.asyncio +async def test_async_upload_sets_content_md5_header(monkeypatch): + """ + Object Lock buckets reject PUTs without a Content-MD5 header (AWS spec). + The async upload must send a base64 md5 of the exact signed body. + """ + from unittest.mock import AsyncMock, MagicMock + + from litellm.types.integrations.s3_v2 import s3BatchLoggingElement + + logger = S3Logger( + s3_bucket_name="test-bucket", + s3_aws_access_key_id="test-key", + s3_aws_secret_access_key="test-secret", + s3_region_name="us-east-1", + ) + + payload = {"test": "content-md5"} + test_element = s3BatchLoggingElement( + s3_object_key="2025-09-14/test-md5.json", + payload=payload, + s3_object_download_filename="test-md5.json", + ) + _require_non_security_md5(monkeypatch) + + response = MagicMock() + response.status_code = 200 + response.raise_for_status = MagicMock() + logger.async_httpx_client = AsyncMock() + logger.async_httpx_client.put.return_value = response + + await logger.async_upload_data_to_s3(test_element) + + headers = logger.async_httpx_client.put.call_args.kwargs["headers"] + assert headers["Content-MD5"] == _expected_content_md5(payload) + assert "x-amz-server-side-encryption" not in headers + + +def test_sync_upload_sets_content_md5_header(monkeypatch): + """The sync upload path must also send Content-MD5 for Object Lock buckets.""" + from unittest.mock import MagicMock + + from litellm.types.integrations.s3_v2 import s3BatchLoggingElement + + logger = S3Logger( + s3_bucket_name="test-bucket", + s3_aws_access_key_id="test-key", + s3_aws_secret_access_key="test-secret", + s3_region_name="us-east-1", + ) + + payload = {"test": "sync-content-md5"} + test_element = s3BatchLoggingElement( + s3_object_key="2025-09-14/test-sync-md5.json", + payload=payload, + s3_object_download_filename="test-sync-md5.json", + ) + _require_non_security_md5(monkeypatch) + + response = MagicMock() + response.status_code = 200 + response.raise_for_status = MagicMock() + mock_sync_client = MagicMock() + mock_sync_client.put.return_value = response + + with patch( + "litellm.integrations.s3_v2._get_httpx_client", + return_value=mock_sync_client, + ): + logger.upload_data_to_s3(test_element) + + headers = mock_sync_client.put.call_args.kwargs["headers"] + assert headers["Content-MD5"] == _expected_content_md5(payload) + assert "x-amz-server-side-encryption" not in headers + + +@pytest.mark.asyncio +async def test_async_upload_sets_server_side_encryption_header_when_configured(): + """ + When s3_server_side_encryption is set (e.g. buckets with a KMS default + encryption policy), the PUT must carry x-amz-server-side-encryption. + """ + from unittest.mock import AsyncMock, MagicMock + + from litellm.types.integrations.s3_v2 import s3BatchLoggingElement + + logger = S3Logger( + s3_bucket_name="test-bucket", + s3_aws_access_key_id="test-key", + s3_aws_secret_access_key="test-secret", + s3_region_name="us-east-1", + s3_server_side_encryption="aws:kms", + ) + + test_element = s3BatchLoggingElement( + s3_object_key="2025-09-14/test-sse.json", + payload={"test": "sse"}, + s3_object_download_filename="test-sse.json", + ) + + response = MagicMock() + response.status_code = 200 + response.raise_for_status = MagicMock() + logger.async_httpx_client = AsyncMock() + logger.async_httpx_client.put.return_value = response + + await logger.async_upload_data_to_s3(test_element) + + headers = logger.async_httpx_client.put.call_args.kwargs["headers"] + assert headers["x-amz-server-side-encryption"] == "aws:kms" + + +def test_s3_server_side_encryption_read_from_callback_params(): + """s3_server_side_encryption can be configured via s3_callback_params.""" + import litellm + + original = litellm.s3_callback_params + litellm.s3_callback_params = { + "s3_bucket_name": "from-global", + "s3_server_side_encryption": "aws:kms", + } + try: + logger = S3Logger() + assert logger.s3_server_side_encryption == "aws:kms" + finally: + litellm.s3_callback_params = original diff --git a/tests/test_litellm/integrations/websearch_interception/test_websearch_chat_completion.py b/tests/test_litellm/integrations/websearch_interception/test_websearch_chat_completion.py index 34555d76554..7ef43e2eadf 100644 --- a/tests/test_litellm/integrations/websearch_interception/test_websearch_chat_completion.py +++ b/tests/test_litellm/integrations/websearch_interception/test_websearch_chat_completion.py @@ -6,7 +6,7 @@ litellm.acompletion() for transparent server-side web search execution. """ import os -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import MagicMock import pytest @@ -34,9 +34,7 @@ def mock_search_response(): @pytest.fixture def websearch_logger(): """Create a WebSearchInterceptionLogger instance""" - return WebSearchInterceptionLogger( - enabled_providers=[LlmProviders.OPENAI, LlmProviders.MINIMAX] - ) + return WebSearchInterceptionLogger(enabled_providers=[LlmProviders.OPENAI, LlmProviders.MINIMAX]) @pytest.mark.asyncio @@ -55,9 +53,7 @@ async def test_websearch_chat_completion_with_openai(): """ # Configure WebSearch interception original_callbacks = litellm.callbacks.copy() if litellm.callbacks else [] - websearch_logger = WebSearchInterceptionLogger( - enabled_providers=[LlmProviders.OPENAI] - ) + websearch_logger = WebSearchInterceptionLogger(enabled_providers=[LlmProviders.OPENAI]) litellm.callbacks = [websearch_logger] try: @@ -100,9 +96,7 @@ async def test_websearch_chat_completion_with_openai(): if hasattr(response.choices[0].message, "tool_calls"): # If tool_calls exist, it means agentic loop didn't run # This could happen if search tool is not configured - pytest.skip( - "Agentic loop did not execute - search tool may not be configured" - ) + pytest.skip("Agentic loop did not execute - search tool may not be configured") # Verify we got a meaningful response assert response.choices[0].finish_reason in ["stop", "end_turn"] @@ -122,9 +116,7 @@ async def test_websearch_chat_completion_hook_detection(): Message, ) - websearch_logger = WebSearchInterceptionLogger( - enabled_providers=[LlmProviders.OPENAI] - ) + websearch_logger = WebSearchInterceptionLogger(enabled_providers=[LlmProviders.OPENAI]) # Mock response with litellm_web_search tool call mock_response = ModelResponse( @@ -155,21 +147,19 @@ async def test_websearch_chat_completion_hook_detection(): ) # Test should_run_chat_completion_agentic_loop - should_run, tools_dict = ( - await websearch_logger.async_should_run_chat_completion_agentic_loop( - response=mock_response, - model="gpt-4o", - messages=[{"role": "user", "content": "What's the weather?"}], - tools=[ - { - "type": "function", - "function": {"name": "litellm_web_search"}, - } - ], - stream=False, - custom_llm_provider="openai", - kwargs={}, - ) + should_run, tools_dict = await websearch_logger.async_should_run_chat_completion_agentic_loop( + response=mock_response, + model="gpt-4o", + messages=[{"role": "user", "content": "What's the weather?"}], + tools=[ + { + "type": "function", + "function": {"name": "litellm_web_search"}, + } + ], + stream=False, + custom_llm_provider="openai", + kwargs={}, ) # Verify hook detected the tool call @@ -185,9 +175,7 @@ async def test_websearch_not_triggered_without_tool(): """Test that websearch hook is NOT triggered when no web search tool in request.""" from litellm.types.utils import Choices, Message - websearch_logger = WebSearchInterceptionLogger( - enabled_providers=[LlmProviders.OPENAI] - ) + websearch_logger = WebSearchInterceptionLogger(enabled_providers=[LlmProviders.OPENAI]) mock_response = ModelResponse( id="test-123", @@ -208,21 +196,19 @@ async def test_websearch_not_triggered_without_tool(): ) # Test without web search tool - should_run, tools_dict = ( - await websearch_logger.async_should_run_chat_completion_agentic_loop( - response=mock_response, - model="gpt-4o", - messages=[{"role": "user", "content": "Hello"}], - tools=[ - { - "type": "function", - "function": {"name": "some_other_tool"}, - } - ], - stream=False, - custom_llm_provider="openai", - kwargs={}, - ) + should_run, tools_dict = await websearch_logger.async_should_run_chat_completion_agentic_loop( + response=mock_response, + model="gpt-4o", + messages=[{"role": "user", "content": "Hello"}], + tools=[ + { + "type": "function", + "function": {"name": "some_other_tool"}, + } + ], + stream=False, + custom_llm_provider="openai", + kwargs={}, ) # Verify hook did NOT trigger @@ -241,9 +227,7 @@ async def test_websearch_not_triggered_for_disabled_provider(): ) # Only enable bedrock - websearch_logger = WebSearchInterceptionLogger( - enabled_providers=[LlmProviders.BEDROCK] - ) + websearch_logger = WebSearchInterceptionLogger(enabled_providers=[LlmProviders.BEDROCK]) mock_response = ModelResponse( id="test-123", @@ -273,21 +257,19 @@ async def test_websearch_not_triggered_for_disabled_provider(): ) # Test with OpenAI provider (not enabled) - should_run, tools_dict = ( - await websearch_logger.async_should_run_chat_completion_agentic_loop( - response=mock_response, - model="gpt-4o", - messages=[{"role": "user", "content": "test"}], - tools=[ - { - "type": "function", - "function": {"name": "litellm_web_search"}, - } - ], - stream=False, - custom_llm_provider="openai", # Not in enabled_providers - kwargs={}, - ) + should_run, tools_dict = await websearch_logger.async_should_run_chat_completion_agentic_loop( + response=mock_response, + model="gpt-4o", + messages=[{"role": "user", "content": "test"}], + tools=[ + { + "type": "function", + "function": {"name": "litellm_web_search"}, + } + ], + stream=False, + custom_llm_provider="openai", # Not in enabled_providers + kwargs={}, ) # Verify hook did NOT trigger @@ -341,8 +323,7 @@ async def test_websearch_json_serialization_fix(): @pytest.mark.asyncio @pytest.mark.skipif( - os.environ.get("OPENAI_API_KEY") is None - or os.environ.get("PERPLEXITY_API_KEY") is None, + os.environ.get("OPENAI_API_KEY") is None or os.environ.get("PERPLEXITY_API_KEY") is None, reason="OPENAI_API_KEY or PERPLEXITY_API_KEY not set", ) async def test_websearch_streaming_conversion(): @@ -395,6 +376,174 @@ async def test_websearch_streaming_conversion(): litellm.callbacks = [] +@pytest.mark.asyncio +async def test_maybe_run_chat_completion_agentic_loop_calls_chat_completion_hook(): + """Regression test: maybe_run_chat_completion_agentic_loop must call + async_should_run_chat_completion_agentic_loop, not async_should_run_agentic_loop. + + Before the fix, the function used the wrong gate check and wrong hook, + causing WebSearchInterceptionLogger to never intercept chat completion requests + even when the LLM returned a litellm_web_search tool call. + """ + from litellm.litellm_core_utils.chat_completion_agentic_loop import ( + maybe_run_chat_completion_agentic_loop, + ) + from litellm.types.utils import ( + ChatCompletionMessageToolCall, + Choices, + Function, + Message, + ) + + mock_response = ModelResponse( + id="test-regression-123", + choices=[ + Choices( + finish_reason="tool_calls", + index=0, + message=Message( + role="assistant", + content=None, + tool_calls=[ + ChatCompletionMessageToolCall( + id="call_abc", + type="function", + function=Function( + name="litellm_web_search", + arguments='{"query": "latest news"}', + ), + ) + ], + ), + ) + ], + model="gpt-4o", + object="chat.completion", + created=1234567890, + ) + + sentinel = ModelResponse( + id="sentinel-final", + choices=[ + Choices( + finish_reason="stop", + index=0, + message=Message(role="assistant", content="Here is the news."), + ) + ], + model="gpt-4o", + object="chat.completion", + created=1234567890, + ) + + websearch_logger = WebSearchInterceptionLogger(enabled_providers=[LlmProviders.OPENAI]) + + chat_completion_hook_called = False + + async def fake_should_run_chat_completion(response, model, messages, tools, stream, custom_llm_provider, kwargs): + nonlocal chat_completion_hook_called + chat_completion_hook_called = True + return True, { + "tool_calls": [{"id": "call_abc", "name": "litellm_web_search", "input": {"query": "latest news"}}], + "tool_type": "websearch", + "provider": "openai", + "response_format": "openai", + } + + async def fake_build_plan(tools, model, messages, response, optional_params, logging_obj, stream, kwargs): + from litellm.types.integrations.custom_logger import AgenticLoopPlan + + return AgenticLoopPlan(run_agentic_loop=False, response_override=sentinel) + + websearch_logger.async_should_run_chat_completion_agentic_loop = fake_should_run_chat_completion + websearch_logger.async_build_chat_completion_agentic_loop_plan = fake_build_plan + + import litellm as _litellm + + original_callbacks = _litellm.callbacks[:] + _litellm.callbacks = [websearch_logger] + + mock_logging_obj = MagicMock() + mock_logging_obj.dynamic_success_callbacks = None + + try: + result = await maybe_run_chat_completion_agentic_loop( + response=mock_response, + model="gpt-4o", + messages=[{"role": "user", "content": "Latest news?"}], + optional_params={ + "tools": [ + { + "type": "function", + "function": {"name": "litellm_web_search"}, + } + ] + }, + kwargs={}, + logging_obj=mock_logging_obj, + custom_llm_provider="openai", + stream=False, + ) + finally: + _litellm.callbacks = original_callbacks + + assert chat_completion_hook_called, ( + "async_should_run_chat_completion_agentic_loop was never called; " + "maybe_run_chat_completion_agentic_loop used the wrong hook" + ) + assert result is sentinel, "Expected agentic loop to return sentinel final response" + + +@pytest.mark.asyncio +async def test_execute_chat_completion_agentic_loop_strips_tool_choice(): + """Regression: _execute_chat_completion_agentic_loop must not forward tool_choice + from the original request into the follow-up synthesis call. + + When the original request forces tool_choice to litellm_web_search, merging + optional_params into the follow-up params without explicit removal causes the + model to call the search tool again instead of synthesizing an answer. + """ + from unittest.mock import patch + + websearch_logger = WebSearchInterceptionLogger(enabled_providers=[LlmProviders.OPENAI]) + + captured_kwargs: dict = {} + + async def fake_acompletion(**kwargs): + captured_kwargs.update(kwargs) + return ModelResponse(id="followup", model="gpt-4o", object="chat.completion") + + async def fake_search(query): + return ("Bitcoin price is $60,000", None) + + with patch.object(websearch_logger, "_execute_search", side_effect=fake_search): + with patch("litellm.acompletion", side_effect=fake_acompletion): + await websearch_logger._execute_chat_completion_agentic_loop( + model="gpt-4o", + messages=[{"role": "user", "content": "What is Bitcoin price?"}], + tool_calls=[ + { + "id": "call_1", + "name": "litellm_web_search", + "input": {"query": "bitcoin price"}, + } + ], + optional_params={ + "tools": [{"type": "function", "function": {"name": "litellm_web_search"}}], + "tool_choice": {"type": "function", "function": {"name": "litellm_web_search"}}, + "max_tokens": 512, + }, + logging_obj=MagicMock(), + stream=False, + kwargs={}, + ) + + assert "tool_choice" not in captured_kwargs, ( + "tool_choice must not appear in follow-up acompletion kwargs; " + "it would force the model to call the search tool again instead of synthesizing" + ) + + if __name__ == "__main__": # Run with: pytest test_websearch_chat_completion.py -v -s pytest.main([__file__, "-v", "-s"]) diff --git a/tests/test_litellm/integrations/websearch_interception/test_websearch_interception_handler.py b/tests/test_litellm/integrations/websearch_interception/test_websearch_interception_handler.py index ea117380edf..b6ff3b70a4d 100644 --- a/tests/test_litellm/integrations/websearch_interception/test_websearch_interception_handler.py +++ b/tests/test_litellm/integrations/websearch_interception/test_websearch_interception_handler.py @@ -12,6 +12,8 @@ from litellm.constants import LITELLM_WEB_SEARCH_TOOL_NAME from litellm.integrations.websearch_interception.handler import ( WebSearchInterceptionLogger, ) +from litellm.llms.base_llm.search.transformation import SearchResponse +from litellm.proxy._types import LiteLLM_ObjectPermissionTable, LiteLLM_TeamTable, ProxyException, UserAPIKeyAuth from litellm.types.utils import LlmProviders @@ -56,9 +58,7 @@ def test_initialize_from_proxy_config_honors_dict_callback_specific_params(): """A valid dict under callback_settings.websearch_interception is applied.""" logger = WebSearchInterceptionLogger.initialize_from_proxy_config( litellm_settings={}, - callback_specific_params={ - "websearch_interception": {"search_tool_name": "ws-tool"} - }, + callback_specific_params={"websearch_interception": {"search_tool_name": "ws-tool"}}, ) assert logger.search_tool_name == "ws-tool" @@ -119,9 +119,7 @@ async def test_async_build_agentic_loop_plan_returns_request_patch(): "response_format": "anthropic", } logging_obj = MagicMock() - logging_obj.model_call_details = { - "agentic_loop_params": {"model": "bedrock/invoke/claude-3-5-sonnet"} - } + logging_obj.model_call_details = {"agentic_loop_params": {"model": "bedrock/invoke/claude-3-5-sonnet"}} kwargs = { "temperature": 0.2, "_websearch_interception_converted_stream": True, @@ -162,8 +160,6 @@ async def test_internal_flags_filtered_from_followup_kwargs(): to the follow-up LLM request, causing "Extra inputs are not permitted" errors from providers like Bedrock that use strict parameter validation. """ - logger = WebSearchInterceptionLogger(enabled_providers=["bedrock"]) - # Simulate kwargs that would be passed during agentic loop execution kwargs_with_internal_flags = { "_websearch_interception_converted_stream": True, @@ -174,9 +170,7 @@ async def test_internal_flags_filtered_from_followup_kwargs(): # Apply the same filtering logic used in _execute_agentic_loop kwargs_for_followup = { - k: v - for k, v in kwargs_with_internal_flags.items() - if not k.startswith("_websearch_interception") + k: v for k, v in kwargs_with_internal_flags.items() if not k.startswith("_websearch_interception") } # Verify internal flags are filtered out @@ -188,6 +182,138 @@ async def test_internal_flags_filtered_from_followup_kwargs(): assert kwargs_for_followup["max_tokens"] == 1024 +@pytest.mark.asyncio +async def test_execute_search_passes_selected_search_tool_litellm_params(monkeypatch): + import litellm + from litellm.proxy import proxy_server + + logger = WebSearchInterceptionLogger( + enabled_providers=["bedrock"], + search_tool_name="ui-tavily", + ) + router = MagicMock() + router.search_tools = [ + { + "search_tool_name": "ui-tavily", + "litellm_params": { + "search_provider": "tavily", + "api_key": "fake-ui-key", + "api_base": "https://api.tavily.com", + "timeout": 10.0, + "max_retries": 2, + "country": None, + }, + } + ] + mock_asearch = AsyncMock(return_value=SearchResponse(object="search", results=[])) + user_api_key_auth = UserAPIKeyAuth( + object_permission=LiteLLM_ObjectPermissionTable( + object_permission_id="op-allowed-search", + search_tools=["ui-tavily"], + ) + ) + + monkeypatch.setattr(proxy_server, "llm_router", router) + monkeypatch.setattr(litellm, "asearch", mock_asearch) + + await logger._execute_search( + "what is litellm", + kwargs={"litellm_params": {"metadata": {"user_api_key_auth": user_api_key_auth}}}, + ) + + mock_asearch.assert_awaited_once_with( + query="what is litellm", + search_provider="tavily", + api_key="fake-ui-key", + api_base="https://api.tavily.com", + timeout=10.0, + max_retries=2, + ) + + +@pytest.mark.asyncio +async def test_execute_search_enforces_key_search_tool_permission(monkeypatch): + import litellm + from litellm.proxy import proxy_server + + logger = WebSearchInterceptionLogger( + enabled_providers=["bedrock"], + search_tool_name="blocked-search", + ) + router = MagicMock() + router.search_tools = [ + { + "search_tool_name": "blocked-search", + "litellm_params": { + "search_provider": "tavily", + "api_key": "fake-ui-key", + }, + } + ] + mock_asearch = AsyncMock(return_value=SearchResponse(object="search", results=[])) + user_api_key_auth = UserAPIKeyAuth( + object_permission=LiteLLM_ObjectPermissionTable( + object_permission_id="op-key-search", + search_tools=["allowed-search"], + ) + ) + + monkeypatch.setattr(proxy_server, "llm_router", router) + monkeypatch.setattr(litellm, "asearch", mock_asearch) + + with pytest.raises(ProxyException): + await logger._execute_search( + "what is litellm", + kwargs={"metadata": {"user_api_key_auth": user_api_key_auth}}, + ) + + mock_asearch.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_execute_search_enforces_team_search_tool_permission(monkeypatch): + import litellm + from litellm.proxy import proxy_server + + logger = WebSearchInterceptionLogger( + enabled_providers=["bedrock"], + search_tool_name="blocked-search", + ) + router = MagicMock() + router.search_tools = [ + { + "search_tool_name": "blocked-search", + "litellm_params": { + "search_provider": "tavily", + "api_key": "fake-ui-key", + }, + } + ] + mock_asearch = AsyncMock(return_value=SearchResponse(object="search", results=[])) + team_key_auth = UserAPIKeyAuth(team_id="team-1") + team_object = LiteLLM_TeamTable( + team_id="team-1", + object_permission=LiteLLM_ObjectPermissionTable( + object_permission_id="op-team-search", + search_tools=["allowed-search"], + ), + ) + mock_get_team_object = AsyncMock(return_value=team_object) + + monkeypatch.setattr(proxy_server, "llm_router", router) + monkeypatch.setattr(litellm, "asearch", mock_asearch) + monkeypatch.setattr("litellm.proxy.auth.auth_checks.get_team_object", mock_get_team_object) + + with pytest.raises(ProxyException): + await logger._execute_search( + "what is litellm", + kwargs={"metadata": {"user_api_key_auth": team_key_auth}}, + ) + + mock_get_team_object.assert_awaited_once() + mock_asearch.assert_not_awaited() + + @pytest.mark.asyncio async def test_async_pre_call_deployment_hook_provider_from_top_level_kwargs(): """Test that async_pre_call_deployment_hook finds custom_llm_provider at top-level kwargs. @@ -216,15 +342,12 @@ async def test_async_pre_call_deployment_hook_provider_from_top_level_kwargs(): assert result is not None # The web_search tool should be converted to litellm_web_search (OpenAI format) assert any( - t.get("type") == "function" - and t.get("function", {}).get("name") == "litellm_web_search" + t.get("type") == "function" and t.get("function", {}).get("name") == "litellm_web_search" for t in result["tools"] ) # The non-web-search tool should be preserved assert any( - t.get("type") == "function" - and t.get("function", {}).get("name") == "other_tool" - for t in result["tools"] + t.get("type") == "function" and t.get("function", {}).get("name") == "other_tool" for t in result["tools"] ) @@ -261,8 +384,7 @@ async def test_async_pre_call_deployment_hook_returns_full_kwargs(): assert result["custom_llm_provider"] == "openai" # Tools should be converted assert any( - t.get("type") == "function" - and t.get("function", {}).get("name") == "litellm_web_search" + t.get("type") == "function" and t.get("function", {}).get("name") == "litellm_web_search" for t in result["tools"] ) @@ -323,8 +445,7 @@ async def test_async_pre_call_deployment_hook_nested_litellm_params_fallback(): assert result is not None assert any( - t.get("type") == "function" - and t.get("function", {}).get("name") == "litellm_web_search" + t.get("type") == "function" and t.get("function", {}).get("name") == "litellm_web_search" for t in result["tools"] ) # Full kwargs preserved @@ -357,8 +478,7 @@ async def test_async_pre_call_deployment_hook_provider_derived_from_model_name() # Should NOT be None — the hook should derive "openai" from "openai/gpt-4o-mini" assert result is not None assert any( - t.get("type") == "function" - and t.get("function", {}).get("name") == "litellm_web_search" + t.get("type") == "function" and t.get("function", {}).get("name") == "litellm_web_search" for t in result["tools"] ) # Full kwargs preserved @@ -478,9 +598,7 @@ def test_sync_forced_tool_choice_leaves_non_forced_untouched(tool_choice): and None pass through unchanged.""" converted_tools = [{"name": LITELLM_WEB_SEARCH_TOOL_NAME}] - result = WebSearchInterceptionLogger._sync_forced_tool_choice( - tool_choice, converted_tools - ) + result = WebSearchInterceptionLogger._sync_forced_tool_choice(tool_choice, converted_tools) assert result == tool_choice diff --git a/tests/test_litellm/interactions/test_interactions_streaming_iterator.py b/tests/test_litellm/interactions/test_interactions_streaming_iterator.py new file mode 100644 index 00000000000..9f88b2c9611 --- /dev/null +++ b/tests/test_litellm/interactions/test_interactions_streaming_iterator.py @@ -0,0 +1,97 @@ +""" +Regression test for LIT-4210: completing an async Interactions API stream must +not run the sync success_handler on the thread-pool executor concurrently with +async_success_handler (cross-thread pydantic mutation segfaults pydantic-core). +""" + +import asyncio +import time + +import httpx +import pytest + +import litellm +from litellm.integrations.custom_logger import CustomLogger +from litellm.interactions import streaming_iterator as interactions_streaming_iterator_module +from litellm.interactions.streaming_iterator import InteractionsAPIStreamingIterator +from litellm.litellm_core_utils import thread_pool_executor as thread_pool_executor_module +from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging +from litellm.types.interactions import InteractionsAPIStreamingResponse + + +class RecordingCustomLogger(CustomLogger): + def __init__(self): + super().__init__() + self.async_hook_fired = False + + async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): + self.async_hook_fired = True + + async def async_log_stream_event(self, kwargs, response_obj, start_time, end_time): + self.async_hook_fired = True + + +class RecordingExecutor: + def __init__(self, inner): + self._inner = inner + self.submits: list = [] + + def submit(self, fn, *args, **kwargs): + self.submits.append(fn) + return self._inner.submit(fn, *args, **kwargs) + + def submitted_for(self, logging_obj) -> list: + return [fn for fn in self.submits if getattr(fn, "__self__", None) is logging_obj] + + +@pytest.fixture(autouse=True) +def _isolate_callbacks(): + saved = ( + litellm.callbacks, + litellm.success_callback, + litellm._async_success_callback, + litellm.failure_callback, + litellm._async_failure_callback, + ) + yield + ( + litellm.callbacks, + litellm.success_callback, + litellm._async_success_callback, + litellm.failure_callback, + litellm._async_failure_callback, + ) = saved + + +@pytest.mark.asyncio +async def test_custom_logger_only_never_submits_sync_success_handler(monkeypatch): + recording_executor = RecordingExecutor(thread_pool_executor_module.executor) + monkeypatch.setattr(thread_pool_executor_module, "executor", recording_executor) + monkeypatch.setattr(interactions_streaming_iterator_module, "executor", recording_executor) + + recorder = RecordingCustomLogger() + litellm.success_callback = [recorder] + litellm._async_success_callback = [recorder] + + logging_obj = LitellmLogging( + model="gemini/gemini-3-pro-preview", + messages=[{"role": "user", "content": "hi"}], + stream=True, + call_type="ainteraction", + start_time=time.time(), + litellm_call_id="lit-4210-test", + function_id="lit-4210-test", + ) + iterator = InteractionsAPIStreamingIterator( + response=httpx.Response(200), + model="gemini/gemini-3-pro-preview", + interactions_api_config=None, + logging_obj=logging_obj, + ) + iterator.completed_response = InteractionsAPIStreamingResponse() + + iterator._handle_logging_completed_response() + await asyncio.sleep(0.5) + + assert recorder.async_hook_fired is True + assert recording_executor.submitted_for(logging_obj) == [] diff --git a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py index e9977efe47d..ca23e61352e 100644 --- a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py +++ b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py @@ -507,6 +507,104 @@ def test_generic_cost_per_token_gpt55_pro(): ) +@pytest.mark.parametrize( + "model,input_cost,output_cost,cache_read_cost,cache_write_cost", + [ + ("gpt-5.6", 5e-6, 3e-5, 5e-7, 6.25e-6), + ("gpt-5.6-sol", 5e-6, 3e-5, 5e-7, 6.25e-6), + ("gpt-5.6-terra", 2.5e-6, 1.5e-5, 2.5e-7, 3.125e-6), + ("gpt-5.6-luna", 1e-6, 6e-6, 1e-7, 1.25e-6), + ], +) +def test_generic_cost_per_token_gpt56( + model, input_cost, output_cost, cache_read_cost, cache_write_cost +): + """gpt-5.6 (sol/terra/luna): base pricing + new cache-write cost. + + Cache writes are billed at 1.25x the uncached input rate for this family. + """ + custom_llm_provider = "openai" + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + + model_cost_map = litellm.model_cost[model] + + assert model_cost_map["input_cost_per_token"] == input_cost + assert model_cost_map["output_cost_per_token"] == output_cost + assert model_cost_map["cache_read_input_token_cost"] == cache_read_cost + assert model_cost_map["cache_creation_input_token_cost"] == cache_write_cost + assert model_cost_map["litellm_provider"] == "openai" + assert model_cost_map["mode"] == "chat" + assert model_cost_map["cache_creation_input_token_cost"] == pytest.approx( + input_cost * 1.25 + ) + assert model_cost_map["max_input_tokens"] == 1050000 + assert model_cost_map["input_cost_per_token_above_272k_tokens"] == pytest.approx( + input_cost * 2 + ) + assert model_cost_map["output_cost_per_token_above_272k_tokens"] == pytest.approx( + output_cost * 1.5 + ) + + prompt_tokens = 1000 + completion_tokens = 500 + usage = Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ) + prompt_cost, completion_cost = generic_cost_per_token( + model=model, + usage=usage, + custom_llm_provider=custom_llm_provider, + ) + assert round(prompt_cost, 10) == round(input_cost * prompt_tokens, 10) + assert round(completion_cost, 10) == round(output_cost * completion_tokens, 10) + + +@pytest.mark.parametrize( + "model,input_cost,output_cost,cache_read_cost", + [ + ("azure/gpt-5.6", 5e-6, 3e-5, 5e-7), + ("azure/gpt-5.6-sol", 5e-6, 3e-5, 5e-7), + ("azure/gpt-5.6-terra", 2.5e-6, 1.5e-5, 2.5e-7), + ("azure/gpt-5.6-luna", 1e-6, 6e-6, 1e-7), + ("azure/us/gpt-5.6", 5.5e-6, 3.3e-5, 5.5e-7), + ("azure/eu/gpt-5.6-terra", 2.75e-6, 1.65e-5, 2.75e-7), + ("azure/eu/gpt-5.6-luna", 1.1e-6, 6.6e-6, 1.1e-7), + ], +) +def test_generic_cost_per_token_azure_gpt56( + model, input_cost, output_cost, cache_read_cost +): + """Azure gpt-5.6 (global + us/eu regional): pricing mirrors the openai + family for global deployments and carries the standard 10% regional uplift. + """ + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + + model_cost_map = litellm.model_cost[model] + assert model_cost_map["litellm_provider"] == "azure" + assert model_cost_map["input_cost_per_token"] == input_cost + assert model_cost_map["output_cost_per_token"] == output_cost + assert model_cost_map["cache_read_input_token_cost"] == cache_read_cost + + prompt_tokens = 1000 + completion_tokens = 500 + usage = Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ) + prompt_cost, completion_cost = generic_cost_per_token( + model=model, + usage=usage, + custom_llm_provider="azure", + ) + assert round(prompt_cost, 10) == round(input_cost * prompt_tokens, 10) + assert round(completion_cost, 10) == round(output_cost * completion_tokens, 10) + + @pytest.mark.parametrize( "model,expected_none,expected_xhigh,expected_minimal", [ @@ -1538,22 +1636,23 @@ def _local_model_cost_map(): os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = prev_env +@pytest.mark.parametrize("model", ["gpt-5.4", "gpt-realtime-2.1", "gpt-realtime-2.1-mini"]) @pytest.mark.parametrize("data_residency", ["eu", "us"]) -def test_data_residency_applies_uplift(data_residency, _local_model_cost_map): - """gpt-5.4 should apply the regional processing uplift multiplier when - data_residency is set. gpt-5.4+ (released 2026-03-05) carry the 10% uplift; - gpt-5 and older models do not.""" +def test_data_residency_applies_uplift(data_residency, model, _local_model_cost_map): + """Models released on/after 2026-03-05 (gpt-5.4/5.5 and gpt-realtime-2.1 + series) apply the 10% regional processing uplift multiplier when + data_residency is set; gpt-5 and older models do not.""" from litellm.types.utils import Usage usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500) base = generic_cost_per_token( - model="gpt-5.4", + model=model, usage=usage, custom_llm_provider="openai", ) regional = generic_cost_per_token( - model="gpt-5.4", + model=model, usage=usage, custom_llm_provider="openai", data_residency=data_residency, diff --git a/tests/test_litellm/litellm_core_utils/prompt_templates/test_bedrock_converse_strict_tools_opus_47_48.py b/tests/test_litellm/litellm_core_utils/prompt_templates/test_bedrock_converse_strict_tools_opus_47_48.py new file mode 100644 index 00000000000..791982fc3dc --- /dev/null +++ b/tests/test_litellm/litellm_core_utils/prompt_templates/test_bedrock_converse_strict_tools_opus_47_48.py @@ -0,0 +1,154 @@ +"""Regression tests for Bedrock Converse ``toolSpec.strict`` forwarding. + +Bedrock Converse routes Claude Opus 4.7/4.8 and Claude Sonnet 4 through an +Anthropic-compatible validator that rejects ``toolSpec.strict`` even though +Anthropic's native API accepts ``strict`` as a top-level tool field. See +BerriAI/litellm#31582. +""" + +import pytest + +from litellm.litellm_core_utils.prompt_templates.factory import _bedrock_tools_pt +from litellm.llms.bedrock.common_utils import bedrock_converse_supports_strict_tools + +_STRICT_TOOL = [ + { + "type": "function", + "function": { + "name": "get_weather", + "strict": True, + "description": "Get the weather for a city", + "parameters": { + "type": "object", + "properties": { + "city": {"type": "string"}, + "unit": {"type": "string", "enum": ["celsius"]}, + }, + "required": ["city", "unit"], + "additionalProperties": False, + }, + }, + } +] + + +@pytest.mark.parametrize( + "model_id", + [ + "bedrock/us.anthropic.claude-opus-4-7", + "bedrock/us.anthropic.claude-opus-4-8", + "anthropic.claude-opus-4-7", + "anthropic.claude-opus-4-8", + "anthropic.claude-opus-4-7-v1:0", + "bedrock/eu.anthropic.claude-opus-4-8-v1:0", + "bedrock/global.anthropic.claude-opus-4-7", + # Sonnet 4 also rejects toolSpec.strict on Bedrock Converse + "anthropic.claude-sonnet-4-20250514-v1:0", + "bedrock/global.anthropic.claude-sonnet-4-20250514-v1:0", + "bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0", + "bedrock/eu.anthropic.claude-sonnet-4-20250514-v1:0", + "bedrock/apac.anthropic.claude-sonnet-4-20250514-v1:0", + ], +) +def test_bedrock_tools_pt_strict_dropped_for_strict_unsupported_models( + model_id: str, +) -> None: + """Opus 4.7/4.8 and Sonnet 4 reject toolSpec.strict and additionalProperties.""" + result = _bedrock_tools_pt(_STRICT_TOOL, model=model_id) + tool_spec = result[0]["toolSpec"] + assert ( + "strict" not in tool_spec + ), f"strict leaked into toolSpec for {model_id}: {tool_spec}" + assert ( + "additionalProperties" not in tool_spec["inputSchema"]["json"] + ), f"additionalProperties leaked into toolSpec for {model_id}: {tool_spec}" + + +@pytest.mark.parametrize( + "model_id", + [ + "anthropic.claude-sonnet-4-5-20250929-v1:0", + "bedrock/us.anthropic.claude-sonnet-4-6", + "bedrock/us.anthropic.claude-opus-4-6", + "bedrock/us.anthropic.claude-opus-4-5", + ], +) +def test_bedrock_tools_pt_strict_kept_for_other_anthropic(model_id: str) -> None: + """Sonnet 4.5/4.6 and Opus <=4.6 accept toolSpec.strict — keep forwarding it.""" + result = _bedrock_tools_pt(_STRICT_TOOL, model=model_id) + assert ( + result[0]["toolSpec"]["strict"] is True + ), f"strict missing for {model_id}: {result[0]['toolSpec']}" + + +@pytest.mark.parametrize( + "model_id", + [ + "us.amazon.nova-micro-v1:0", + "meta.llama3-2-11b-instruct-v1:0", + ], +) +def test_bedrock_tools_pt_strict_dropped_for_non_anthropic(model_id: str) -> None: + """Non-Anthropic Bedrock families reject toolSpec.strict — must be dropped.""" + result = _bedrock_tools_pt(_STRICT_TOOL, model=model_id) + assert "strict" not in result[0]["toolSpec"] + + +def test_bedrock_converse_supports_strict_tools_helper() -> None: + """Direct check for the gate helper used by factory.py.""" + assert ( + bedrock_converse_supports_strict_tools("bedrock/us.anthropic.claude-opus-4-7") + is False + ) + assert ( + bedrock_converse_supports_strict_tools("bedrock/us.anthropic.claude-opus-4-8") + is False + ) + assert ( + bedrock_converse_supports_strict_tools( + "anthropic.claude-sonnet-4-5-20250929-v1:0" + ) + is True + ) + assert ( + bedrock_converse_supports_strict_tools("bedrock/us.anthropic.claude-opus-4-6") + is True + ) + assert bedrock_converse_supports_strict_tools("us.amazon.nova-micro-v1:0") is False + assert bedrock_converse_supports_strict_tools("") is False + # Sonnet 4 also rejects strict on Bedrock Converse + assert ( + bedrock_converse_supports_strict_tools( + "anthropic.claude-sonnet-4-20250514-v1:0" + ) + is False + ) + assert ( + bedrock_converse_supports_strict_tools( + "bedrock/global.anthropic.claude-sonnet-4-20250514-v1:0" + ) + is False + ) + + +@pytest.mark.parametrize( + "cost_map_key", + [ + "anthropic.claude-opus-4-7", + "us.anthropic.claude-opus-4-7", + "anthropic.claude-opus-4-8", + "us.anthropic.claude-opus-4-8", + "anthropic.claude-sonnet-4-20250514-v1:0", + "global.anthropic.claude-sonnet-4-20250514-v1:0", + "us.anthropic.claude-sonnet-4-20250514-v1:0", + "eu.anthropic.claude-sonnet-4-20250514-v1:0", + "apac.anthropic.claude-sonnet-4-20250514-v1:0", + ], +) +def test_strict_tools_flag_set_in_model_cost_map(cost_map_key: str) -> None: + """The gate is driven by ``bedrock_converse_supports_strict_tools: false`` in + ``model_prices_and_context_window.json``, not hardcoded model patterns.""" + from litellm.litellm_core_utils.get_model_cost_map import GetModelCostMap + + cost_map = GetModelCostMap.load_local_model_cost_map() + assert cost_map[cost_map_key]["bedrock_converse_supports_strict_tools"] is False diff --git a/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py b/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py index 864f685e7c9..bcda88ea609 100644 --- a/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py +++ b/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py @@ -1,5 +1,6 @@ import base64 import json +import os from unittest.mock import MagicMock, patch import pytest @@ -2744,102 +2745,132 @@ def test_add_cache_point_tool_block_passes_ttl_for_claude_4_5(): TTL ordering constraint (tools -> system -> messages). Ref: https://github.com/BerriAI/litellm/issues/XXXXX + + Forces the bundled local cost map so ttl eligibility (driven by + `cache_creation_input_token_cost_above_1hr` in litellm.model_cost) reads + this branch's pricing data rather than the network-fetched `main` copy, + which lacks the fix until merge. """ from litellm.litellm_core_utils.prompt_templates.factory import ( add_cache_point_tool_block, ) - tool_with_1h = { - "type": "function", - "function": {"name": "get_weather", "parameters": {"type": "object"}}, - "cache_control": {"type": "ephemeral", "ttl": "1h"}, - } + old_env = os.environ.get("LITELLM_LOCAL_MODEL_COST_MAP") + old_cost = litellm.model_cost + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + try: + tool_with_1h = { + "type": "function", + "function": {"name": "get_weather", "parameters": {"type": "object"}}, + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + } - # Claude 4.5 model: ttl should be preserved - result = add_cache_point_tool_block( - tool_with_1h, model="us.anthropic.claude-sonnet-4-5-20250514-v1:0" - ) - assert result is not None - assert result["cachePoint"]["type"] == "default" - assert result["cachePoint"]["ttl"] == "1h" + # Claude 4.5 model: ttl should be preserved + result = add_cache_point_tool_block( + tool_with_1h, model="jp.anthropic.claude-opus-4-7" + ) + assert result is not None + assert result["cachePoint"]["type"] == "default" + assert result["cachePoint"]["ttl"] == "1h" - # Claude 4.5 model with 5m ttl: also preserved - tool_with_5m = { - "cache_control": {"type": "ephemeral", "ttl": "5m"}, - } - result_5m = add_cache_point_tool_block( - tool_with_5m, model="us.anthropic.claude-sonnet-4-5-20250514-v1:0" - ) - assert result_5m is not None - assert result_5m["cachePoint"]["ttl"] == "5m" + # Claude 4.5 model with 5m ttl: also preserved + tool_with_5m = { + "cache_control": {"type": "ephemeral", "ttl": "5m"}, + } + result_5m = add_cache_point_tool_block( + tool_with_5m, model="jp.anthropic.claude-opus-4-7" + ) + assert result_5m is not None + assert result_5m["cachePoint"]["ttl"] == "5m" - # Older model: ttl should be stripped - result_old = add_cache_point_tool_block( - tool_with_1h, model="anthropic.claude-3-5-sonnet-20241022-v2:0" - ) - assert result_old is not None - assert result_old["cachePoint"]["type"] == "default" - assert "ttl" not in result_old["cachePoint"] + # Older model: ttl should be stripped + result_old = add_cache_point_tool_block( + tool_with_1h, model="anthropic.claude-3-5-sonnet-20241022-v2:0" + ) + assert result_old is not None + assert result_old["cachePoint"]["type"] == "default" + assert "ttl" not in result_old["cachePoint"] - # No model provided: ttl should be stripped (safe default) - result_no_model = add_cache_point_tool_block(tool_with_1h, model=None) - assert result_no_model is not None - assert "ttl" not in result_no_model["cachePoint"] + # No model provided: ttl should be stripped (safe default) + result_no_model = add_cache_point_tool_block(tool_with_1h, model=None) + assert result_no_model is not None + assert "ttl" not in result_no_model["cachePoint"] - # No cache_control: returns None (unchanged behavior) - tool_no_cache = { - "type": "function", - "function": {"name": "get_weather", "parameters": {"type": "object"}}, - } - assert add_cache_point_tool_block(tool_no_cache) is None + # No cache_control: returns None (unchanged behavior) + tool_no_cache = { + "type": "function", + "function": {"name": "get_weather", "parameters": {"type": "object"}}, + } + assert add_cache_point_tool_block(tool_no_cache) is None - # cache_control without ttl: returns default cachePoint (unchanged behavior) - tool_no_ttl = {"cache_control": {"type": "ephemeral"}} - result_no_ttl = add_cache_point_tool_block( - tool_no_ttl, model="us.anthropic.claude-sonnet-4-5-20250514-v1:0" - ) - assert result_no_ttl is not None - assert result_no_ttl["cachePoint"]["type"] == "default" - assert "ttl" not in result_no_ttl["cachePoint"] + # cache_control without ttl: returns default cachePoint (unchanged behavior) + tool_no_ttl = {"cache_control": {"type": "ephemeral"}} + result_no_ttl = add_cache_point_tool_block( + tool_no_ttl, model="us.anthropic.claude-sonnet-4-5-20250929-v1:0" + ) + assert result_no_ttl is not None + assert result_no_ttl["cachePoint"]["type"] == "default" + assert "ttl" not in result_no_ttl["cachePoint"] + finally: + litellm.model_cost = old_cost + if old_env is None: + os.environ.pop("LITELLM_LOCAL_MODEL_COST_MAP", None) + else: + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = old_env def test_bedrock_tools_pt_passes_ttl_for_claude_4_5(): """ End-to-end: _bedrock_tools_pt should produce cachePoint blocks with ttl for Claude 4.5+ models when tools have cache_control with ttl. + + Forces the bundled local cost map so ttl eligibility (driven by + `cache_creation_input_token_cost_above_1hr` in litellm.model_cost) reads + this branch's pricing data rather than the network-fetched `main` copy, + which lacks the fix until merge. """ from litellm.litellm_core_utils.prompt_templates.factory import _bedrock_tools_pt - tools = [ - { - "type": "function", - "function": { - "name": "get_weather", - "description": "Get weather", - "parameters": { - "type": "object", - "properties": {"city": {"type": "string"}}, + old_env = os.environ.get("LITELLM_LOCAL_MODEL_COST_MAP") + old_cost = litellm.model_cost + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + try: + tools = [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather", + "parameters": { + "type": "object", + "properties": {"city": {"type": "string"}}, + }, }, - }, - "cache_control": {"type": "ephemeral", "ttl": "1h"}, - } - ] + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + } + ] - # Claude 4.5: cachePoint should have ttl - result = _bedrock_tools_pt( - tools, model="us.anthropic.claude-sonnet-4-5-20250514-v1:0" - ) - cache_blocks = [b for b in result if "cachePoint" in b] - assert len(cache_blocks) == 1 - assert cache_blocks[0]["cachePoint"]["ttl"] == "1h" + # Claude 4.5: cachePoint should have ttl + result = _bedrock_tools_pt(tools, model="jp.anthropic.claude-opus-4-7") + cache_blocks = [b for b in result if "cachePoint" in b] + assert len(cache_blocks) == 1 + assert cache_blocks[0]["cachePoint"]["ttl"] == "1h" - # Older model: cachePoint should not have ttl - result_old = _bedrock_tools_pt( - tools, model="anthropic.claude-3-5-sonnet-20241022-v2:0" - ) - cache_blocks_old = [b for b in result_old if "cachePoint" in b] - assert len(cache_blocks_old) == 1 - assert "ttl" not in cache_blocks_old[0]["cachePoint"] + # Older model: cachePoint should not have ttl + result_old = _bedrock_tools_pt( + tools, model="anthropic.claude-3-5-sonnet-20241022-v2:0" + ) + cache_blocks_old = [b for b in result_old if "cachePoint" in b] + assert len(cache_blocks_old) == 1 + assert "ttl" not in cache_blocks_old[0]["cachePoint"] + finally: + litellm.model_cost = old_cost + if old_env is None: + os.environ.pop("LITELLM_LOCAL_MODEL_COST_MAP", None) + else: + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = old_env def test_convert_to_anthropic_tool_result_openai_file_pdf_becomes_document(): @@ -3054,3 +3085,84 @@ def test_bedrock_converse_messages_pt_document_rejects_url_source(): _bedrock_converse_messages_pt( messages, "anthropic.claude-sonnet-4-6", "bedrock" ) + + +def _collect_cache_points(blocks): + return [ + block["cachePoint"] + for message in blocks + for block in message["content"] + if "cachePoint" in block + ] + + +@pytest.mark.parametrize( + "messages", + [ + [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "conversation history", + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + } + ], + }, + ], + [ + {"role": "user", "content": "hello"}, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "assistant reply", + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + } + ], + }, + ], + ], +) +def test_bedrock_converse_message_level_cache_point_preserves_ttl(messages): + """ + Regression for https://github.com/BerriAI/litellm/issues/32154: message-level + cache_control ttl was silently dropped because the message-level + _get_cache_point_block call sites never passed model=, so multi-turn prefixes + fell back to the 5m default while the system prompt kept 1h, churning the + cache every turn on models like Opus 4.8. + """ + result = _bedrock_converse_messages_pt( + messages=messages, + model="eu.anthropic.claude-opus-4-8", + llm_provider="bedrock", + ) + + cache_points = _collect_cache_points(result) + assert cache_points == [{"type": "default", "ttl": "1h"}] + + +@pytest.mark.asyncio +async def test_bedrock_converse_message_level_cache_point_preserves_ttl_async(): + messages = [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "conversation history", + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + } + ], + }, + ] + + result = await BedrockConverseMessagesProcessor._bedrock_converse_messages_pt_async( + messages=messages, + model="eu.anthropic.claude-opus-4-8", + llm_provider="bedrock", + ) + + assert _collect_cache_points(result) == [{"type": "default", "ttl": "1h"}] diff --git a/tests/test_litellm/litellm_core_utils/test_audio_utils.py b/tests/test_litellm/litellm_core_utils/test_audio_utils.py index b2645c8f2ce..0e8176fffce 100644 --- a/tests/test_litellm/litellm_core_utils/test_audio_utils.py +++ b/tests/test_litellm/litellm_core_utils/test_audio_utils.py @@ -326,3 +326,24 @@ class TestGetAudioFileContentHash: assert isinstance(hash_result, str) assert len(hash_result) == 64, "Should return valid hash even on fallback" + + +class TestNormalizeTranscriptionLanguageToBcp47: + @pytest.mark.parametrize( + "language,expected", + [ + ("en", "en-US"), + ("EN", "en-US"), + ("ja", "ja-JP"), + ("en-US", "en-US"), + ("en-GB", "en-GB"), + ("auto", "auto"), + ("xx", "xx"), + ], + ) + def test_normalization(self, language, expected): + from litellm.litellm_core_utils.audio_utils.utils import ( + normalize_transcription_language_to_bcp47, + ) + + assert normalize_transcription_language_to_bcp47(language) == expected diff --git a/tests/test_litellm/litellm_core_utils/test_chat_completion_agentic_loop.py b/tests/test_litellm/litellm_core_utils/test_chat_completion_agentic_loop.py index f1196ab4692..cc16ad558e4 100644 --- a/tests/test_litellm/litellm_core_utils/test_chat_completion_agentic_loop.py +++ b/tests/test_litellm/litellm_core_utils/test_chat_completion_agentic_loop.py @@ -181,8 +181,7 @@ async def test_internal_control_fields_never_leak_into_provider_body(restore_cal # The loop must have actually fired (sanity: two provider calls). assert create.await_count == 2, ( - "expected the agentic loop to issue a follow-up provider call; " - f"got {create.await_count} call(s)" + f"expected the agentic loop to issue a follow-up provider call; got {create.await_count} call(s)" ) for idx, call in enumerate(create.await_args_list): @@ -194,8 +193,7 @@ async def test_internal_control_fields_never_leak_into_provider_body(restore_cal f"top-level request body: {sorted(body.keys())}" ) assert field not in extra_body, ( - f"provider call #{idx}: internal field {field!r} leaked into " - f"extra_body: {sorted(extra_body.keys())}" + f"provider call #{idx}: internal field {field!r} leaked into extra_body: {sorted(extra_body.keys())}" ) # The native code_interpreter tool must have been swapped for the # function tool, never sent raw to OpenAI as a chat-completions request. @@ -254,9 +252,7 @@ class _GateOnlyLogger(CustomLogger): ) -> AgenticLoopPlan: return self._plan - async def async_agentic_loop_cleanup_hook( - self, plan: AgenticLoopPlan, kwargs: Dict[str, Any] - ) -> None: + async def async_agentic_loop_cleanup_hook(self, plan: AgenticLoopPlan, kwargs: Dict[str, Any]) -> None: self.cleanup_calls += 1 @@ -343,9 +339,7 @@ async def test_dispatcher_runs_followup_with_incremented_depth_and_patched_messa assert call_kwargs["max_agentic_loops"] >= 1 assert "_agentic_loop_fingerprints" in call_kwargs # Interception markers are mirrored into litellm_metadata for the follow-up. - assert ( - call_kwargs["litellm_metadata"]["_code_interpreter_interception_active"] is True - ) + assert call_kwargs["litellm_metadata"]["_code_interpreter_interception_active"] is True # The transient surface marker is NOT forwarded to the follow-up call. assert "_agentic_loop_api_surface" not in call_kwargs # Cleanup hook always runs. @@ -390,9 +384,7 @@ async def test_dispatcher_raises_on_repeated_tool_call_fingerprint(restore_callb # The dispatcher fingerprints the whole value the gate returns as its second # tuple element, so the seeded fingerprint must mirror that dict exactly. - gate_tool_calls = { - "tool_calls": [{"id": "call_abc", "name": "litellm_code_execution"}] - } + gate_tool_calls = {"tool_calls": [{"id": "call_abc", "name": "litellm_code_execution"}]} fingerprint = json.dumps(gate_tool_calls, sort_keys=True, default=str) logger = _GateOnlyLogger( diff --git a/tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py b/tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py index 53960847fdc..1fcee1b1c42 100644 --- a/tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py +++ b/tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py @@ -78,6 +78,18 @@ context_window_test_cases = [ "CerebrasException - Please reduce the length of the messages or completion. Current length is 50000 while limit is 40000", True, ), + ( + "Invalid 'input[0]': maximum input length is 8192 tokens.", + True, + ), + ( + "OpenAIException - Error code: 400 - {'error': {'message': \"Invalid 'input[0]': maximum input length is 8192 tokens.\", 'type': 'invalid_request_error'}}", + True, + ), + ( + "Invalid 'metadata': maximum input length is 512 characters.", + False, + ), # Negative cases (should return False) ("A generic API error occurred.", False), ("Invalid API Key provided.", False), @@ -626,3 +638,45 @@ def test_replicate_422_maps_to_unprocessable_entity(): ) assert excinfo.value.llm_provider == "replicate" + + +def test_upstream_4xx_without_model_maps_to_bad_request(): + """Responses API follow-ups (cancel/get/delete) call ``exception_type`` with + ``model=None``; the provider mapping used to be gated on ``if model:``, so an + upstream 400 like Azure's "Cannot cancel a synchronous response." fell through to + the generic 500 APIConnectionError instead of surfacing as a 400.""" + from litellm.llms.base_llm.chat.transformation import BaseLLMException + + original_exception = BaseLLMException( + status_code=400, + message='{"error": {"message": "Cannot cancel a synchronous response.", "type": "invalid_request_error"}}', + ) + + with pytest.raises(litellm.BadRequestError) as excinfo: + exception_type( + model=None, + original_exception=original_exception, + custom_llm_provider="azure", + ) + + assert excinfo.value.status_code == 400 + assert "Cannot cancel a synchronous response." in excinfo.value.message + + +def test_azure_404_with_invalid_request_error_type_maps_to_not_found(): + from litellm.llms.base_llm.chat.transformation import BaseLLMException + + original_exception = BaseLLMException( + status_code=404, + message='{"error": {"message": "Response with id \'resp_abc\' not found.", "type": "invalid_request_error"}}', + ) + + with pytest.raises(litellm.NotFoundError) as excinfo: + exception_type( + model=None, + original_exception=original_exception, + custom_llm_provider="azure", + ) + + assert excinfo.value.status_code == 404 + assert "Response with id 'resp_abc' not found." in excinfo.value.message diff --git a/tests/test_litellm/litellm_core_utils/test_fallback_generalizations.py b/tests/test_litellm/litellm_core_utils/test_fallback_generalizations.py index 410014958b6..0414836fa79 100644 --- a/tests/test_litellm/litellm_core_utils/test_fallback_generalizations.py +++ b/tests/test_litellm/litellm_core_utils/test_fallback_generalizations.py @@ -1,11 +1,13 @@ """ Tests for the declarative fallback-generalizations mechanism. -Covers both the pure module (litellm.litellm_core_utils.fallback_generalizations) -and its end-to-end wiring into provider routing (get_llm_provider) and model-info -resolution (get_model_info / supports_*). +Covers the pure module (litellm.litellm_core_utils.fallback_generalizations): the +routing/capability rule split, install-time validation, capability unioning; and +its end-to-end wiring into provider routing (get_llm_provider) and model-info +resolution (get_model_info) including the shipped rules in the bundled cost map. """ +import logging import os import sys @@ -14,9 +16,11 @@ import pytest sys.path.insert(0, os.path.abspath("../../..")) import litellm +from litellm._logging import verbose_logger from litellm.litellm_core_utils.fallback_generalizations import ( get_fallback_generalization_rules, - match_fallback_generalization, + match_capability_generalizations, + match_routing_generalization, set_fallback_generalizations, ) @@ -31,50 +35,205 @@ def restore_generalizations(): set_fallback_generalizations(previous) +class _RecordingHandler(logging.Handler): + def __init__(self): + super().__init__(level=logging.WARNING) + self.messages = [] + + def emit(self, record): + self.messages.append(record.getMessage()) + + +@pytest.fixture +def warning_messages(): + handler = _RecordingHandler() + previous_level = verbose_logger.level + verbose_logger.setLevel(logging.WARNING) + verbose_logger.addHandler(handler) + try: + yield handler.messages + finally: + verbose_logger.removeHandler(handler) + verbose_logger.setLevel(previous_level) + + # --------------------------------------------------------------------------- # -# Pure module behaviour +# Engine: routing rules # --------------------------------------------------------------------------- # -def test_match_returns_model_info_of_first_matching_rule(restore_generalizations): +def test_routing_inference_first_match_wins(restore_generalizations): + restore_generalizations( + [ + {"name": "first", "pattern": r"^acme-", "model_info": {"litellm_provider": "openai"}}, + {"name": "second", "pattern": r"^acme-pro-", "model_info": {"litellm_provider": "anthropic"}}, + ] + ) + assert match_routing_generalization("acme-pro-1") == "openai" + assert match_routing_generalization("gpt-4o") is None + assert match_routing_generalization("") is None + + +def test_capability_rules_do_not_route(restore_generalizations): + restore_generalizations([{"name": "caps", "pattern": r"^acme-", "model_info": {"supports_vision": True}}]) + assert match_routing_generalization("acme-pro-1") is None + + +def test_routing_match_is_case_insensitive(restore_generalizations): + restore_generalizations( + [{"name": "r", "pattern": r"^claude-opus", "model_info": {"litellm_provider": "anthropic"}}] + ) + assert match_routing_generalization("CLAUDE-OPUS-9-9") == "anthropic" + + +# --------------------------------------------------------------------------- # +# Engine: capability rules +# --------------------------------------------------------------------------- # + + +def test_capability_union_is_last_wins_in_file_order(restore_generalizations): restore_generalizations( [ { - "name": "first", + "name": "broad", "pattern": r"^acme-", - "model_info": {"litellm_provider": "openai", "tag": "first"}, + "model_info": {"mode": "chat", "supports_vision": True, "max_input_tokens": 1000}, }, { - "name": "second", + "name": "narrow", "pattern": r"^acme-pro-", - "model_info": {"litellm_provider": "anthropic", "tag": "second"}, + "model_info": {"supports_vision": False, "supports_reasoning": True}, }, ] ) - # Both rules match "acme-pro-1"; first-in-list wins (documented precedence). - matched = match_fallback_generalization("acme-pro-1") - assert matched is not None - assert matched["tag"] == "first" + assert match_capability_generalizations("acme-pro-1") == { + "mode": "chat", + "supports_vision": False, + "max_input_tokens": 1000, + "supports_reasoning": True, + } + assert match_capability_generalizations("acme-basic-1") == { + "mode": "chat", + "supports_vision": True, + "max_input_tokens": 1000, + } -def test_match_is_case_insensitive(restore_generalizations): +def test_routing_rules_are_excluded_from_capability_results(restore_generalizations): restore_generalizations( - [{"name": "r", "pattern": r"^claude-opus", "model_info": {"ok": True}}] + [ + {"name": "route", "pattern": r"^acme-", "model_info": {"litellm_provider": "openai"}}, + {"name": "caps", "pattern": r"^acme-pro-", "model_info": {"supports_vision": True}}, + ] ) - assert match_fallback_generalization("CLAUDE-OPUS-9-9") == {"ok": True} + assert match_capability_generalizations("acme-pro-1") == {"supports_vision": True} + assert match_capability_generalizations("acme-basic-1") is None -def test_no_match_returns_none(restore_generalizations): - restore_generalizations( - [{"name": "r", "pattern": r"^claude-", "model_info": {"ok": True}}] - ) - assert match_fallback_generalization("gpt-4o") is None - assert match_fallback_generalization("") is None - - -def test_empty_rules_match_nothing(restore_generalizations): +def test_no_capability_match_returns_none(restore_generalizations): + restore_generalizations([{"name": "r", "pattern": r"^claude-", "model_info": {"ok": True}}]) + assert match_capability_generalizations("gpt-4o") is None + assert match_capability_generalizations("") is None restore_generalizations([]) - assert match_fallback_generalization("claude-opus-9-9") is None + assert match_capability_generalizations("claude-opus-9-9") is None + + +def test_reinstalling_rules_replaces_compiled_rules(restore_generalizations): + restore_generalizations([{"name": "r", "pattern": r"^aaa", "model_info": {"v": 1}}]) + assert match_capability_generalizations("aaa-1") == {"v": 1} + set_fallback_generalizations([{"name": "r", "pattern": r"^bbb", "model_info": {"v": 2}}]) + assert match_capability_generalizations("aaa-1") is None + assert match_capability_generalizations("bbb-1") == {"v": 2} + + +# --------------------------------------------------------------------------- # +# Engine: install-time validation and legacy-schema shim +# --------------------------------------------------------------------------- # + + +def test_legacy_mixed_rule_acts_as_both_kinds(restore_generalizations): + """A legacy rule mixing ``litellm_provider`` with capability keys routes AND + contributes its full model_info (provider included) to the capability union.""" + restore_generalizations( + [ + { + "name": "legacy-mixed", + "pattern": r"^acme-", + "model_info": {"litellm_provider": "anthropic", "supports_vision": True}, + }, + {"name": "new-caps", "pattern": r"^acme-pro-", "model_info": {"supports_reasoning": True}}, + ] + ) + assert match_routing_generalization("acme-pro-1") == "anthropic" + assert match_capability_generalizations("acme-pro-1") == { + "litellm_provider": "anthropic", + "supports_vision": True, + "supports_reasoning": True, + } + + +LEGACY_MAIN_RULES = [ + { + "name": "anthropic-claude-adaptive-thinking", + "pattern": "(?:opus|sonnet|haiku)[-._](?:4[-._](?:[6-9]|[1-9]\\d)(?!\\d)|(?:[5-9]|[1-9]\\d{1,})[-._]\\d{1,2}(?!\\d))", + "description": "Claude opus/sonnet/haiku at version 4.6 or higher: 4.6 through 4.99, then any 5.x, 6.x or later major. The minor is capped at two digits so an 8-digit date suffix such as claude-opus-4-20250514 is never read as a >= 4.6 minor. Turns on adaptive thinking for new families with no code change.", + "extends": "anthropic-claude", + "model_info": {"supports_adaptive_thinking": True}, + }, + { + "name": "anthropic-claude", + "pattern": "^claude-[a-z]+-\\d+[-.]\\d+(?:-\\d{8})?$", + "description": "Any Claude family-major-minor id, optionally with an 8-digit date suffix, anchored to the whole name. Version-neutral fallback that gives an unmapped Claude provider routing and baseline capabilities; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number.", + "model_info": { + "litellm_provider": "anthropic", + "mode": "chat", + "max_input_tokens": 200000, + "max_output_tokens": 64000, + "max_tokens": 64000, + "supports_function_calling": True, + "supports_parallel_function_calling": True, + "supports_vision": True, + "supports_tool_choice": True, + "supports_assistant_prefill": True, + "supports_prompt_caching": True, + "supports_response_schema": True, + "supports_reasoning": True, + "supports_pdf_input": True, + "supports_system_messages": True, + }, + }, +] + + +def test_legacy_main_schema_keeps_unmapped_claude_working(restore_generalizations): + """Pins the remote-map transition window: a released proxy running this engine + against main's old-schema block (mixed provider+capability rule plus ``extends``, + copied verbatim above) must keep unmapped-Claude inference and info resolution + working until the new-schema JSON reaches main.""" + restore_generalizations([dict(rule) for rule in LEGACY_MAIN_RULES]) + litellm.get_model_info.cache_clear() + + _, provider, _, _ = litellm.get_llm_provider(model="claude-opus-9-9") + assert provider == "anthropic" + + info = litellm.get_model_info("claude-opus-9-9") + assert info["litellm_provider"] == "anthropic" + assert info["supports_adaptive_thinking"] is True + assert info["supports_function_calling"] is True + assert info["max_input_tokens"] == 200000 + assert not info.get("input_cost_per_token") + + low = litellm.get_model_info("claude-opus-4-0") + assert low["litellm_provider"] == "anthropic" + assert low["supports_function_calling"] is True + assert low.get("supports_adaptive_thinking") is None + + +def test_non_string_provider_rule_warns_and_is_skipped(restore_generalizations, warning_messages): + restore_generalizations([{"name": "bad-provider", "pattern": r"^acme-", "model_info": {"litellm_provider": 42}}]) + assert any("bad-provider" in message for message in warning_messages) + assert match_routing_generalization("acme-1") is None + assert match_capability_generalizations("acme-1") is None def test_malformed_rules_are_skipped_not_fatal(restore_generalizations): @@ -88,69 +247,7 @@ def test_malformed_rules_are_skipped_not_fatal(restore_generalizations): {"name": "good", "pattern": r"^claude-", "model_info": {"good": True}}, ] ) - # Non-dict entries and dicts with bad fields are all skipped; the one - # valid rule still matches. - assert match_fallback_generalization("claude-opus-9-9") == {"good": True} - - -def test_setting_rules_invalidates_compiled_cache(restore_generalizations): - restore_generalizations([{"name": "r", "pattern": r"^aaa", "model_info": {"v": 1}}]) - assert match_fallback_generalization("aaa-1") == {"v": 1} - # Re-install different rules; the compiled cache must be rebuilt. - set_fallback_generalizations( - [{"name": "r", "pattern": r"^bbb", "model_info": {"v": 2}}] - ) - assert match_fallback_generalization("aaa-1") is None - assert match_fallback_generalization("bbb-1") == {"v": 2} - - -def test_extends_inherits_parent_and_own_overrides(restore_generalizations): - """A rule's ``extends`` pulls in the parent's model_info; its own keys win on conflict, - so a narrow rule carries only its delta instead of duplicating the parent.""" - restore_generalizations( - [ - { - "name": "base", - "pattern": r"^base-only$", - "model_info": { - "litellm_provider": "anthropic", - "input_cost_per_token": 5e-06, - "supports_vision": True, - }, - }, - { - "name": "child", - "pattern": r"^kid-", - "extends": "base", - "model_info": { - "supports_adaptive_thinking": True, - "supports_vision": False, - }, - }, - ] - ) - matched = match_fallback_generalization("kid-1") - assert matched == { - "litellm_provider": "anthropic", - "input_cost_per_token": 5e-06, - "supports_vision": False, - "supports_adaptive_thinking": True, - } - - -def test_extends_with_unknown_parent_keeps_own_model_info(restore_generalizations): - """A dangling ``extends`` is non-fatal: the rule resolves to its own model_info.""" - restore_generalizations( - [ - { - "name": "orphan", - "pattern": r"^orphan-", - "extends": "does-not-exist", - "model_info": {"litellm_provider": "openai"}, - } - ] - ) - assert match_fallback_generalization("orphan-1") == {"litellm_provider": "openai"} + assert match_capability_generalizations("claude-opus-9-9") == {"good": True} # --------------------------------------------------------------------------- # @@ -158,91 +255,61 @@ def test_extends_with_unknown_parent_keeps_own_model_info(restore_generalization # --------------------------------------------------------------------------- # -@pytest.fixture -def myco_rule(restore_generalizations): - """A self-contained rule carrying provider, pricing, context and capabilities.""" +def test_unknown_model_routes_via_routing_rule(restore_generalizations): + restore_generalizations([{"name": "myco", "pattern": r"^myco-", "model_info": {"litellm_provider": "openai"}}]) + _, provider, _, _ = litellm.get_llm_provider(model="myco-fast-1") + assert provider == "openai" + + +def test_capability_info_backfills_requested_provider(restore_generalizations): restore_generalizations( [ { - "name": "myco", - "pattern": r"^myco-[a-z]+-\d+$", + "name": "beeco-caps", + "pattern": r"^beeco-[a-z]+-\d+$", "model_info": { - "litellm_provider": "openai", "mode": "chat", - "input_cost_per_token": 1e-06, - "output_cost_per_token": 2e-06, "max_input_tokens": 12345, - "max_output_tokens": 678, "supports_vision": True, "supports_function_calling": True, }, } ] ) - return "myco-fast-1" - - -def test_unknown_model_routes_via_rule(myco_rule): - _, provider, _, _ = litellm.get_llm_provider(model=myco_rule) - assert provider == "openai" - - -def test_unknown_model_gets_pricing_context_and_capabilities(myco_rule): - info = litellm.get_model_info(myco_rule) - assert info["litellm_provider"] == "openai" - assert info["input_cost_per_token"] == 1e-06 - assert info["output_cost_per_token"] == 2e-06 + litellm.get_model_info.cache_clear() + info = litellm.get_model_info("beeco-fast-1", custom_llm_provider="groq") + assert info["litellm_provider"] == "groq" assert info["max_input_tokens"] == 12345 assert info["supports_vision"] is True + other = litellm.get_model_info("beeco-fast-1", custom_llm_provider="openai") + assert other["litellm_provider"] == "openai" -def test_supports_helper_reads_through_generalization(myco_rule): - assert litellm.supports_vision(myco_rule) is True - assert litellm.supports_function_calling(myco_rule) is True +def test_routing_only_match_does_not_resolve_model_info(restore_generalizations): + restore_generalizations([{"name": "route", "pattern": r"^ceeco-", "model_info": {"litellm_provider": "openai"}}]) + litellm.get_model_info.cache_clear() + with pytest.raises(Exception): + litellm.get_model_info("ceeco-fast-1", custom_llm_provider="openai") def test_exact_entry_takes_precedence_over_rule(restore_generalizations): - """An exact cost-map entry must win over a rule that also matches it.""" restore_generalizations( - [ - { - "name": "shadow-gpt4o", - "pattern": r"^gpt-4o$", - "model_info": { - "litellm_provider": "anthropic", - "input_cost_per_token": 999.0, - }, - } - ] + [{"name": "shadow-gpt4o", "pattern": r"^gpt-4o$", "model_info": {"input_cost_per_token": 999.0}}] ) + litellm.get_model_info.cache_clear() info = litellm.get_model_info("gpt-4o") - # Resolved from the real exact entry, not the shadowing rule. assert info["litellm_provider"] == "openai" assert info["input_cost_per_token"] != 999.0 -def test_unknown_model_without_matching_rule_still_unmapped(restore_generalizations): - restore_generalizations( - [ - { - "name": "claude", - "pattern": r"^claude-", - "model_info": {"litellm_provider": "anthropic"}, - } - ] - ) - with pytest.raises(Exception): - litellm.get_model_info("totally-unknown-model-xyz") - - # --------------------------------------------------------------------------- # -# Shipped anthropic-claude rule +# Shipped rules (bundled cost map) # --------------------------------------------------------------------------- # @pytest.fixture def shipped_cost_map(monkeypatch): - """Activate the bundled cost map so the shipped anthropic-claude rule is installed.""" + """Activate the bundled cost map so the shipped rules are installed.""" original_cost = litellm.model_cost previous_rules = list(get_fallback_generalization_rules()) monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") @@ -256,45 +323,241 @@ def shipped_cost_map(monkeypatch): set_fallback_generalizations(previous_rules) -def test_shipped_rule_marks_unmapped_high_version_claude_adaptive_without_pricing( - shipped_cost_map, -): - """An unmapped Claude >= 4.6 resolves via the version-gated adaptive-thinking rule, which - inherits routing and capabilities from the base rule and adds ``supports_adaptive_thinking``. - The rule carries no pricing, so cost stays unpriced (zero, not a fabricated number) rather - than reporting a confidently-wrong price.""" - model = "claude-opus-9-9" +def test_shipped_bare_claude_id_routes_to_anthropic(shipped_cost_map): + _, provider, _, _ = litellm.get_llm_provider(model="claude-haiku-4-6") + assert provider == "anthropic" + + +def test_shipped_bedrock_syntax_claude_id_routes_to_bedrock(shipped_cost_map): + """Regression: a bedrock-syntax id must infer bedrock even when its version also + matches an unanchored Anthropic capability pattern. The old first-match-wins engine + routed global.anthropic.claude-haiku-4-6 to anthropic via the adaptive rule.""" + for model in [ + "global.anthropic.claude-haiku-4-6", + "us.anthropic.claude-haiku-4-6", + "anthropic.claude-haiku-4-6", + "eu.anthropic.claude-opus-5-0", + ]: + assert model not in litellm.model_cost + _, provider, _, _ = litellm.get_llm_provider(model=model) + assert provider == "bedrock", model + + +def test_shipped_rules_resolve_unmapped_bedrock_claude_with_bedrock_provider(shipped_cost_map): + model = "us.anthropic.claude-haiku-4-6" assert model not in litellm.model_cost - info = litellm.get_model_info(model) - assert info["litellm_provider"] == "anthropic" + info = litellm.get_model_info(model, custom_llm_provider="bedrock") + assert info["litellm_provider"] == "bedrock" assert info["supports_adaptive_thinking"] is True assert info["supports_function_calling"] is True + assert info["max_input_tokens"] == 200000 + assert info.get("supports_mid_conversation_system") is None assert not info.get("input_cost_per_token") assert not info.get("output_cost_per_token") -def test_shipped_rule_resolves_unmapped_low_version_claude_without_adaptive(shipped_cost_map): - """An unmapped Claude < 4.6 falls through to the version-neutral anthropic-claude rule: it - gets provider routing and baseline capabilities but no ``supports_adaptive_thinking`` flag, - so a sub-4.6 alias such as ``claude-opus-4-0`` resolves yet is never marked adaptive.""" - model = "claude-opus-4-0" +def test_shipped_rules_stack_adaptive_and_mid_conversation_flags(shipped_cost_map): + model = "claude-opus-4-9" + assert model not in litellm.model_cost + info = litellm.get_model_info(model, custom_llm_provider="anthropic") + assert info["litellm_provider"] == "anthropic" + assert info["supports_adaptive_thinking"] is True + assert info["supports_mid_conversation_system"] is True + assert info["supports_function_calling"] is True + + +@pytest.mark.parametrize( + "model,provider", + [ + ("claude-opus-4-9@20260101", "vertex_ai"), + ("databricks-claude-opus-5-1", "databricks"), + ], +) +def test_shipped_rules_are_provider_neutral_for_unmapped_ids(shipped_cost_map, model, provider): + assert model not in litellm.model_cost + info = litellm.get_model_info(model, custom_llm_provider=provider) + assert info["litellm_provider"] == provider + assert info["supports_adaptive_thinking"] is True + assert info["supports_mid_conversation_system"] is True + assert info["supports_function_calling"] is True + + +@pytest.mark.parametrize( + "model,provider,adaptive,mid_conversation", + [ + ("us.anthropic.claude-opus-4-5", "bedrock", None, None), + ("claude-haiku-4-6", "anthropic", True, None), + ("claude-haiku-4-7", "anthropic", True, None), + ("claude-haiku-4-8", "anthropic", True, True), + ("claude-haiku-4-9", "anthropic", True, True), + ("claude-haiku-4-10", "anthropic", True, True), + ("claude-haiku-5-0", "anthropic", True, True), + ("claude-sonnet-5-1", "anthropic", True, True), + ], +) +def test_shipped_version_boundaries(shipped_cost_map, model, provider, adaptive, mid_conversation): + assert model not in litellm.model_cost + info = litellm.get_model_info(model, custom_llm_provider=provider) + assert info["litellm_provider"] == provider + assert info["supports_function_calling"] is True + assert not info.get("input_cost_per_token") + assert info.get("supports_adaptive_thinking") is adaptive, model + assert info.get("supports_mid_conversation_system") is mid_conversation, model + + +def test_shipped_rules_cover_new_families_like_fable_at_5_plus(shipped_cost_map): + """Both version gates accept any claude-- id at major 5 or higher, bare + major or major-minor, so a new family shaped like claude-fable-5 gets adaptive + thinking and mid-conversation system support without a cost-map entry.""" + model = "claude-fable-5-1" + assert model not in litellm.model_cost + info = litellm.get_model_info(model, custom_llm_provider="anthropic") + assert info["supports_mid_conversation_system"] is True + assert info["supports_adaptive_thinking"] is True + assert info["supports_function_calling"] is True + + +def test_shipped_rules_flag_bare_5_plus_majors_of_any_family(shipped_cost_map): + """A bare 5+ major with no minor gets both flags at the rule level; the mapped + claude-fable-5 entry itself still resolves from the cost map, so this pins the + pattern via the capability union rather than get_model_info.""" + matched = match_capability_generalizations("claude-fable-5") + assert matched is not None + assert matched["supports_adaptive_thinking"] is True + assert matched["supports_mid_conversation_system"] is True + + +def test_shipped_version_gates_are_family_agnostic_at_4x(shipped_cost_map): + """Both version gates apply to any claude-- id, 4.x included: a non-core + family at 4.9 gets adaptive and mid-conversation, while the same family at 4.5 + gets baseline only. Only opus/sonnet/haiku ever shipped 4.x ids, so the + family-agnostic 4.6+ gate changes nothing for real models.""" + high = litellm.get_model_info("claude-newfam-4-9", custom_llm_provider="anthropic") + assert high["supports_adaptive_thinking"] is True + assert high["supports_mid_conversation_system"] is True + assert high["supports_function_calling"] is True + + low = litellm.get_model_info("claude-newfam-4-5", custom_llm_provider="anthropic") + assert low.get("supports_adaptive_thinking") is None + assert low.get("supports_mid_conversation_system") is None + assert low["supports_function_calling"] is True + + +def test_shipped_rules_give_bare_majors_the_full_baseline_union(shipped_cost_map): + """A bare-major unmapped id (no minor) resolves the same baseline union as its + major-minor sibling: the baseline pattern's minor is optional, so claude-newt-5 + is not left with version flags but no mode, token limits, or capability facts.""" + model = "anthropic/claude-newt-5" assert model not in litellm.model_cost info = litellm.get_model_info(model) assert info["litellm_provider"] == "anthropic" + assert info["mode"] == "chat" + assert info["max_tokens"] == 64000 assert info["supports_function_calling"] is True - assert info.get("supports_adaptive_thinking") is None - assert not info.get("input_cost_per_token") + assert info["supports_adaptive_thinking"] is True + assert info["supports_mid_conversation_system"] is True + + +def test_shipped_routing_rule_covers_bare_majors(shipped_cost_map): + _, provider, _, _ = litellm.get_llm_provider(model="claude-newt-5") + assert provider == "anthropic" + + +def test_shipped_adaptive_rule_requires_claude_prefix(shipped_cost_map): + """A non-Claude name embedding a core-family 4.6+/5.x version substring must not + resolve from the rules; serving it a zero-priced rule entry would silently + swallow cost tracking for arbitrary custom deployment names.""" + model = "openai/team-sonnet-5-1-alias" + assert model not in litellm.model_cost + assert match_capability_generalizations("team-sonnet-5-1-alias") is None + with pytest.raises(Exception): + litellm.get_model_info(model) + + +def test_shipped_exact_entry_beats_rules(shipped_cost_map): + model = "us.anthropic.claude-sonnet-4-6" + assert model in litellm.model_cost + info = litellm.get_model_info(model, custom_llm_provider="bedrock") + assert info["litellm_provider"] == "bedrock_converse" + assert info["input_cost_per_token"] == 3.3e-06 + assert info["max_input_tokens"] == 1000000 + assert info["supports_adaptive_thinking"] is True + assert info.get("supports_mid_conversation_system") is None + + +def test_shipped_rules_lose_to_exact_entries_across_cost_ladder_variants(shipped_cost_map): + """A route-mangled variant of an exactly-mapped model must never resolve from + rules. The cost calculator tries model-name variants in order; a rule-derived + unpriced entry served for an early variant (here bedrock/claude-haiku-4-5-20251001, + whose bare form is exactly mapped under anthropic) would zero out the bill even + though the exact priced bedrock entry is one variant later. An exactly-mapped id + under a mismatched provider raises instead of resolving from rules.""" + from litellm import completion_cost + from litellm.types.utils import ModelResponse, Usage + + assert "claude-haiku-4-5-20251001" in litellm.model_cost + with pytest.raises(Exception): + litellm.get_model_info("claude-haiku-4-5-20251001", custom_llm_provider="bedrock") + + entry = litellm.model_cost["us.anthropic.claude-haiku-4-5-20251001-v1:0"] + response = ModelResponse(model="claude-haiku-4-5-20251001", usage=Usage(prompt_tokens=100, completion_tokens=50)) + cost = completion_cost( + completion_response=response, + model="bedrock/invoke/us.anthropic.claude-haiku-4-5-20251001-v1:0", + custom_llm_provider="bedrock", + ) + assert cost == 100 * entry["input_cost_per_token"] + 50 * entry["output_cost_per_token"] + assert cost > 0 def test_shipped_adaptive_rule_gates_on_version_not_pricing(shipped_cost_map): - """The version-gated ``anthropic-claude-adaptive-thinking`` rule marks an unmapped - Claude adaptive only from >= 4.6, including provider-prefixed ids the anchored pricing - rule cannot match, while leaving < 4.6 (and the dated Opus 4.0 form) non-adaptive.""" + """The version-gated adaptive-thinking capability rule marks an unmapped Claude + adaptive only from >= 4.6, including provider-prefixed ids the anchored routing + rule cannot match, while leaving the dated Opus 4.0 form non-adaptive.""" from litellm.llms.anthropic.common_utils import AnthropicModelInfo adaptive = "us.anthropic.claude-opus-4-9" non_adaptive = "us.anthropic.claude-opus-4-20250514" assert adaptive not in litellm.model_cost assert non_adaptive not in litellm.model_cost - assert AnthropicModelInfo._is_adaptive_thinking_model(adaptive) is True - assert AnthropicModelInfo._is_adaptive_thinking_model(non_adaptive) is False + assert AnthropicModelInfo._is_adaptive_thinking_model(adaptive, "anthropic") is True + assert AnthropicModelInfo._is_adaptive_thinking_model(non_adaptive, "anthropic") is False + + +def test_shipped_rules_resolve_unmapped_future_bedrock_claude_with_both_flags(shipped_cost_map): + """An unmapped Bedrock Claude >= 4.8 resolves for custom_llm_provider="bedrock" with + baseline capabilities, both version-gated flags, the bedrock provider backfilled, and + no fabricated pricing.""" + model = "us.anthropic.claude-opus-4-9" + assert model not in litellm.model_cost + info = litellm.get_model_info(model, custom_llm_provider="bedrock") + assert info["litellm_provider"] == "bedrock" + assert info["supports_mid_conversation_system"] is True + assert info["supports_adaptive_thinking"] is True + assert info["supports_function_calling"] is True + assert not info.get("input_cost_per_token") + + +def test_shipped_mid_conversation_gate_on_bedrock_ids(shipped_cost_map): + """Bedrock-syntax ids gain ``supports_mid_conversation_system`` only from 4.8 upward, + bare 5+ majors and new families included; 4.7-and-below Bedrock ids never gain it. + The flag comes from the provider-neutral capability rule rather than a bedrock-scoped + one, so the same gate covers native and vertex-shaped ids too.""" + for flagged in ( + "us.anthropic.claude-opus-4-8", + "jp.anthropic.claude-opus-4-8", + "anthropic.claude-sonnet-5", + "us.anthropic.claude-fable-5", + "anthropic.claude-sonnet-5-20260101-v1:0", + ): + matched = match_capability_generalizations(flagged) + assert matched is not None, flagged + assert matched["supports_mid_conversation_system"] is True, flagged + for unflagged in ( + "us.anthropic.claude-opus-4-7", + "us.anthropic.claude-sonnet-4-6", + "us.anthropic.claude-haiku-4-5-20251001-v1:0", + "anthropic.claude-3-5-sonnet-20240620-v1:0", + ): + matched = match_capability_generalizations(unflagged) + assert matched is None or not matched.get("supports_mid_conversation_system"), unflagged diff --git a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py index 924aaa7775d..1a38b5dc769 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py +++ b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py @@ -13,7 +13,8 @@ sys.path.insert(0, os.path.abspath("../../..")) from litellm.litellm_core_utils.fallback_generalizations import ( get_fallback_generalization_rules, - match_fallback_generalization, + match_capability_generalizations, + match_routing_generalization, set_fallback_generalizations, ) from litellm.litellm_core_utils.get_model_cost_map import ( @@ -102,9 +103,7 @@ def test_finalize_pops_key_and_installs_rules(): # The reserved key is removed from the returned model map ... assert FALLBACK_GENERALIZATIONS_KEY not in finalized # ... and its rules are installed into the generalizations module. - assert match_fallback_generalization("widget-9") == { - "litellm_provider": "openai" - } + assert match_routing_generalization("widget-9") == "openai" finally: set_fallback_generalizations(previous) @@ -116,27 +115,61 @@ def test_finalize_with_no_block_clears_rules(): [{"name": "stale", "pattern": r"^x", "model_info": {"a": 1}}] ) _finalize_model_cost_map(_make_models(2)) - assert match_fallback_generalization("x-1") is None + assert match_capability_generalizations("x-1") is None finally: set_fallback_generalizations(previous) -def test_shipped_backup_carries_the_anthropic_claude_rule(): - """The bundled backup must ship the anthropic-claude rule so a fresh install - (or an offline fallback) routes unknown Claude models without code changes.""" +def test_shipped_backup_carries_the_claude_routing_rules(): + """The bundled backup must ship the Claude routing rules so a fresh install + (or an offline fallback) routes unknown Claude models without code changes. + Bedrock-syntax ids must hit the bedrock rule before the bare-id Anthropic rule.""" backup = GetModelCostMap.load_local_model_cost_map() rules = backup.get(FALLBACK_GENERALIZATIONS_KEY, {}).get("rules", []) - names = {r.get("name") for r in rules} - assert "anthropic-claude" in names - - rule = next(r for r in rules if r.get("name") == "anthropic-claude") - assert rule["model_info"]["litellm_provider"] == "anthropic" + names = [r.get("name") for r in rules] + assert names.index("bedrock-claude-ids") < names.index("anthropic-claude-ids") previous = list(get_fallback_generalization_rules()) try: set_fallback_generalizations(rules) - matched = match_fallback_generalization("claude-opus-4-9") - assert matched is not None and matched["litellm_provider"] == "anthropic" + assert match_routing_generalization("claude-opus-4-9") == "anthropic" + assert match_routing_generalization("global.anthropic.claude-opus-4-9") == "bedrock" + finally: + set_fallback_generalizations(previous) + + +def test_shipped_routing_rules_never_match_through_an_unrecognized_namespace(): + """Routing rules decide ``litellm_provider`` for otherwise-unknown ids, and the + proxy's wildcard access check (``can_key_call_model`` with a ``bedrock/*`` key) + trusts that inference: it rebuilds ``{provider}/{model}`` and matches it against + the key's patterns. A routing pattern that matches as a substring lets + ``bedrockz/anthropic.claude-...`` resolve to bedrock and slip through a + ``bedrock/*`` key, so every shipped routing rule must anchor to the start of + the name and never match an id carrying an unrecognized namespace prefix.""" + backup = GetModelCostMap.load_local_model_cost_map() + rules = backup[FALLBACK_GENERALIZATIONS_KEY]["rules"] + + routing_rules = [r for r in rules if "litellm_provider" in r["model_info"]] + assert routing_rules + assert all(r["pattern"].startswith("^") for r in routing_rules) + + previous = list(get_fallback_generalization_rules()) + try: + set_fallback_generalizations(rules) + for bedrock_id in [ + "anthropic.claude-3-5-sonnet-20240620-v1:0", + "anthropic.claude-v2:1", + "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "us-gov.anthropic.claude-3-5-sonnet-20240620-v1:0", + "global.anthropic.claude-fable-5-20260120-v1:0", + ]: + assert match_routing_generalization(bedrock_id) == "bedrock", bedrock_id + for namespaced in [ + "bedrockz/anthropic.claude-3-5-sonnet-20240620", + "bedrockz/us.anthropic.claude-3-5-sonnet-20240620-v1:0", + "bedrockz/claude-3-5-sonnet-20240620", + ]: + assert match_routing_generalization(namespaced) is None, namespaced finally: set_fallback_generalizations(previous) @@ -147,23 +180,19 @@ def test_shipped_backup_marks_claude_4_6_plus_adaptive_not_4_0(): route) and on the version-gated anthropic-claude-adaptive-thinking rule for unmapped future Claudes, while leaving the dated Claude 4.0 names ("...-4-20250514") unflagged so a date can never be mistaken for a 4.6+ minor - version. The version-neutral anthropic-claude pricing rule must not flag it, so - an unmapped sub-4.6 name is priced but stays non-adaptive. The adaptive rule must - inherit pricing from the pricing rule via ``extends`` and carry only its delta, so - the Opus-tier price block is never duplicated across rules.""" + version. The version-neutral claude-family-baseline capability rule must not flag + it, so an unmapped sub-4.6 name resolves but stays non-adaptive. The adaptive rule + carries only its delta; capability unioning stacks it onto the baseline, so the + baseline block is never duplicated across rules and no rule needs ``extends``.""" backup = GetModelCostMap.load_local_model_cost_map() rules = backup[FALLBACK_GENERALIZATIONS_KEY]["rules"] - pricing_rule = next(r for r in rules if r.get("name") == "anthropic-claude") - adaptive_rule = next( - r for r in rules if r.get("name") == "anthropic-claude-adaptive-thinking" - ) - assert "supports_adaptive_thinking" not in pricing_rule["model_info"] - assert adaptive_rule["model_info"]["supports_adaptive_thinking"] is True - - assert "extends" not in pricing_rule - assert adaptive_rule.get("extends") == "anthropic-claude" + baseline_rule = next(r for r in rules if r.get("name") == "claude-family-baseline") + adaptive_rule = next(r for r in rules if r.get("name") == "claude-adaptive-thinking") + assert "supports_adaptive_thinking" not in baseline_rule["model_info"] + assert "litellm_provider" not in baseline_rule["model_info"] assert adaptive_rule["model_info"] == {"supports_adaptive_thinking": True} + assert all("extends" not in r for r in rules) for adaptive in [ "anthropic.claude-opus-4-8", diff --git a/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py b/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py index 02d72c89e80..f0d91224614 100644 --- a/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py @@ -14,6 +14,7 @@ from litellm.constants import LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME from litellm.litellm_core_utils.health_check_helpers import HealthCheckHelpers from litellm.main import ahealth_check from litellm.proxy._types import UserAPIKeyAuth +from litellm.types.utils import LIST_BATCHES_SUPPORTED_PROVIDERS def test_update_model_params_with_health_check_tracking_information(): @@ -140,3 +141,206 @@ async def test_ahealth_check_failure_masks_raw_request_headers(): assert headers["Content-Type"] == "application/json" print(f"Masked Authorization header: {headers.get('Authorization', 'NOT FOUND')}") + + +@pytest.mark.asyncio +async def test_batch_health_check_bridges_metadata_into_logging_obj(): + """_batch_health_check must call update_from_kwargs on the pre-injected + logging object so callbacks receive identity/tracking fields in + model_call_details["litellm_params"]["metadata"].""" + mock_logging_obj = MagicMock() + mock_logging_obj.update_from_kwargs = MagicMock() + + litellm_metadata = { + "tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME], + "user_api_key_alias": "health-check-key", + } + + filtered_model_params = { + "model": "openai/gpt-4", + "api_base": "https://api.openai.com", + "litellm_logging_obj": mock_logging_obj, + "litellm_metadata": litellm_metadata, + } + + with patch("litellm.alist_batches", new_callable=AsyncMock, return_value={}): + await HealthCheckHelpers._batch_health_check( + custom_llm_provider="openai", + model_params={"model": "openai/gpt-4"}, + filtered_model_params=filtered_model_params, + ) + + mock_logging_obj.update_from_kwargs.assert_called_once() + call_kwargs = mock_logging_obj.update_from_kwargs.call_args[1] + assert call_kwargs["model"] == "openai/gpt-4" + assert call_kwargs["kwargs"] is filtered_model_params + assert call_kwargs["litellm_params"] == {"api_base": "https://api.openai.com"} + + +@pytest.mark.asyncio +async def test_batch_health_check_omits_api_base_when_absent(): + """api_base must not appear in litellm_params when the provider resolves + it implicitly (bedrock, vertex, gemini).""" + mock_logging_obj = MagicMock() + mock_logging_obj.update_from_kwargs = MagicMock() + + litellm_metadata = {"tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME]} + + filtered_model_params = { + "model": "bedrock/anthropic.claude-v2", + "litellm_logging_obj": mock_logging_obj, + "litellm_metadata": litellm_metadata, + } + + with patch("litellm.acompletion", new_callable=AsyncMock, return_value={}): + await HealthCheckHelpers._batch_health_check( + custom_llm_provider="bedrock", + model_params={"model": "bedrock/anthropic.claude-v2"}, + filtered_model_params=filtered_model_params, + ) + + call_kwargs = mock_logging_obj.update_from_kwargs.call_args[1] + assert call_kwargs["litellm_params"] is None + + +@pytest.mark.asyncio +async def test_batch_health_check_skips_bridge_when_no_logging_obj(): + """When litellm_logging_obj is absent, dispatch still proceeds.""" + litellm_metadata = {"tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME]} + + filtered_model_params = { + "model": "openai/gpt-4", + "litellm_metadata": litellm_metadata, + } + + with patch( + "litellm.alist_batches", new_callable=AsyncMock, return_value={} + ) as mock_alist: + await HealthCheckHelpers._batch_health_check( + custom_llm_provider="openai", + model_params={"model": "openai/gpt-4"}, + filtered_model_params=filtered_model_params, + ) + mock_alist.assert_called_once() + + +@pytest.mark.asyncio +async def test_batch_health_check_uses_alist_batches_for_supported_providers(): + """Providers in LIST_BATCHES_SUPPORTED_PROVIDERS dispatch to alist_batches.""" + mock_logging_obj = MagicMock() + mock_logging_obj.update_from_kwargs = MagicMock() + + litellm_metadata = {"tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME]} + + for provider in LIST_BATCHES_SUPPORTED_PROVIDERS: + filtered_model_params = { + "model": f"{provider}/some-model", + "litellm_logging_obj": mock_logging_obj, + "litellm_metadata": litellm_metadata, + } + + with patch( + "litellm.alist_batches", new_callable=AsyncMock, return_value={} + ) as mock_alist: + await HealthCheckHelpers._batch_health_check( + custom_llm_provider=provider, + model_params={"model": f"{provider}/some-model"}, + filtered_model_params=filtered_model_params, + ) + mock_alist.assert_called_once() + + +@pytest.mark.asyncio +async def test_batch_health_check_falls_back_to_acompletion_for_unsupported(): + """Providers not in LIST_BATCHES_SUPPORTED_PROVIDERS fall back to acompletion.""" + mock_logging_obj = MagicMock() + mock_logging_obj.update_from_kwargs = MagicMock() + + litellm_metadata = {"tags": [LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME]} + + filtered_model_params = { + "model": "bedrock/anthropic.claude-v2", + "litellm_logging_obj": mock_logging_obj, + "litellm_metadata": litellm_metadata, + } + + model_params = {"model": "bedrock/anthropic.claude-v2", "messages": []} + + with ( + patch("litellm.alist_batches", new_callable=AsyncMock) as mock_alist, + patch("litellm.acompletion", new_callable=AsyncMock, return_value={}) as mock_acompletion, + ): + await HealthCheckHelpers._batch_health_check( + custom_llm_provider="bedrock", + model_params=model_params, + filtered_model_params=filtered_model_params, + ) + mock_alist.assert_not_called() + mock_acompletion.assert_called_once_with(**model_params) + + +class _FakeWebsocketConnect: + def __init__(self, calls, url, **kwargs): + calls.append({"url": url, **kwargs}) + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc, tb): + return False + + +@pytest.mark.asyncio +async def test_realtime_health_check_uses_model_level_vertex_params(): + """Regression test: realtime health checks must resolve vertex_credentials, + vertex_project, and vertex_location from the model row's params instead of + falling back to process-global VERTEXAI_* settings.""" + import litellm + from litellm.realtime_api import main as realtime_main + + fake_vertex_base = MagicMock() + fake_vertex_base.get_vertex_region = MagicMock(return_value="us-central1") + fake_vertex_base._ensure_access_token_async = AsyncMock( + return_value=("model-level-token", "model-level-project") + ) + connect_calls = [] + + with ( + patch.object(realtime_main, "vertex_llm_base", fake_vertex_base), + patch( + "websockets.connect", + lambda url, **kwargs: _FakeWebsocketConnect(connect_calls, url, **kwargs), + ), + patch.object( + HealthCheckHelpers, + "_update_model_params_with_health_check_tracking_information", + staticmethod(lambda model_params: model_params), + ), + ): + result = await litellm.ahealth_check( + model_params={ + "model": "vertex_ai/gemini-live-2.5-flash-native-audio", + "vertex_credentials": '{"type":"service_account"}', + "vertex_project": "model-level-project", + "vertex_location": "us-central1", + }, + mode="realtime", + ) + + assert result == {} + fake_vertex_base.get_vertex_region.assert_called_once_with( + vertex_region="us-central1", model="gemini-live-2.5-flash-native-audio" + ) + fake_vertex_base._ensure_access_token_async.assert_called_once_with( + credentials='{"type":"service_account"}', + project_id="model-level-project", + custom_llm_provider="vertex_ai", + ) + assert connect_calls[0]["url"] == ( + "wss://us-central1-aiplatform.googleapis.com/ws/" + "google.cloud.aiplatform.v1.LlmBidiService/BidiGenerateContent" + ) + assert connect_calls[0]["additional_headers"] == { + "Authorization": "Bearer model-level-token", + "x-goog-user-project": "model-level-project", + } diff --git a/tests/test_litellm/litellm_core_utils/test_image_handling.py b/tests/test_litellm/litellm_core_utils/test_image_handling.py index cc13e816dde..893472d63ae 100644 --- a/tests/test_litellm/litellm_core_utils/test_image_handling.py +++ b/tests/test_litellm/litellm_core_utils/test_image_handling.py @@ -7,6 +7,7 @@ import litellm from litellm import constants from litellm.litellm_core_utils.prompt_templates import image_handling from litellm.litellm_core_utils.prompt_templates.image_handling import ( + async_convert_url_to_base64, convert_url_to_base64, ) @@ -218,6 +219,41 @@ def test_streaming_download_handles_petabyte_file(monkeypatch): assert "exceeds maximum allowed size" in str(excinfo.value) +def test_data_url_is_returned_unchanged_without_fetch(monkeypatch): + """ + A data URL is already inline base64 image data, so convert_url_to_base64 + must return it as-is instead of attempting an HTTP fetch. + """ + + class ExplodingClient: + def get(self, url, follow_redirects=True): + raise AssertionError("data URLs must not trigger an HTTP fetch") + + monkeypatch.setattr(litellm, "module_level_client", ExplodingClient()) + + data_url = "data:image/png;base64,iVBORw0KGgo=" + + assert convert_url_to_base64(data_url) == data_url + + +@pytest.mark.asyncio +async def test_async_data_url_is_returned_unchanged_without_fetch(monkeypatch): + """ + The async path must short-circuit data URLs identically to the sync path, + otherwise async OCR flows would attempt an impossible HTTP fetch. + """ + + class ExplodingAsyncClient: + async def get(self, url, follow_redirects=True): + raise AssertionError("data URLs must not trigger an HTTP fetch") + + monkeypatch.setattr(litellm, "module_level_aclient", ExplodingAsyncClient()) + + data_url = "data:image/png;base64,iVBORw0KGgo=" + + assert await async_convert_url_to_base64(data_url) == data_url + + def test_image_size_limit_disabled(monkeypatch): """ Test that setting MAX_IMAGE_URL_DOWNLOAD_SIZE_MB to 0 disables all image URL downloads. diff --git a/tests/test_litellm/litellm_core_utils/test_initialize_dynamic_callback_params.py b/tests/test_litellm/litellm_core_utils/test_initialize_dynamic_callback_params.py index f63216b96d4..0dca4f3a1b1 100644 --- a/tests/test_litellm/litellm_core_utils/test_initialize_dynamic_callback_params.py +++ b/tests/test_litellm/litellm_core_utils/test_initialize_dynamic_callback_params.py @@ -7,9 +7,53 @@ sys.path.insert(0, os.path.abspath("../../..")) from litellm.litellm_core_utils.initialize_dynamic_callback_params import ( initialize_standard_callback_dynamic_params, + iter_client_callback_metadata_dicts, ) +def test_iter_client_callback_metadata_dicts_covers_all_read_paths(): + md = {"m": 1} + lm = {"lm": 1} + lp_md = {"lp": 1} + slots = dict( + iter_client_callback_metadata_dicts( + { + "metadata": md, + "litellm_metadata": lm, + "litellm_params": {"metadata": lp_md}, + } + ) + ) + assert slots == { + "metadata": md, + "litellm_metadata": lm, + "litellm_params.metadata": lp_md, + } + + +def test_iter_client_callback_metadata_dicts_skips_non_dict_slots(): + slots = list( + iter_client_callback_metadata_dicts( + { + "metadata": "not-a-dict", + "litellm_metadata": None, + "litellm_params": {"metadata": []}, + } + ) + ) + assert slots == [] + + +def test_extractor_reads_turn_off_message_logging_from_every_slot(): + for kwargs in ( + {"metadata": {"turn_off_message_logging": True}}, + {"litellm_metadata": {"turn_off_message_logging": True}}, + {"litellm_params": {"metadata": {"turn_off_message_logging": True}}}, + ): + params = initialize_standard_callback_dynamic_params(kwargs) + assert params.get("turn_off_message_logging") is True, kwargs + + def test_resolves_plain_values_at_top_level(): kwargs = { "langfuse_public_key": "pk-test", @@ -36,6 +80,33 @@ def test_resolves_plain_values_from_metadata(): assert params.get("langfuse_host") == "https://test.langfuse.com" +def test_litellm_params_metadata_overrides_metadata(): + kwargs = { + "metadata": { + "langfuse_public_key": "pk-meta", + }, + "litellm_params": { + "metadata": { + "langfuse_public_key": "pk-litellm-params", + } + }, + } + + params = initialize_standard_callback_dynamic_params(kwargs) + + assert params.get("langfuse_public_key") == "pk-litellm-params" + + +def test_top_level_kwargs_overrides_metadata_slots(): + kwargs = { + "langfuse_public_key": "from-top-level", + "metadata": {"langfuse_public_key": "from-metadata"}, + "litellm_params": {"metadata": {"langfuse_public_key": "from-litellm-params"}}, + } + params = initialize_standard_callback_dynamic_params(kwargs) + assert params.get("langfuse_public_key") == "from-top-level" + + def test_env_reference_at_top_level_raises_with_guidance(): kwargs = {"langfuse_public_key": "os.environ/LANGFUSE_PUBLIC_KEY"} @@ -100,11 +171,17 @@ def test_non_string_values_are_not_flagged(): assert params.get("langsmith_sampling_rate") == 0.5 -def test_turn_off_message_logging_not_extracted_from_request(): - """turn_off_message_logging is admin-only — must not be settable via request.""" - kwargs = {"turn_off_message_logging": True} +@pytest.mark.parametrize( + "kwargs,expected", + [ + ({"turn_off_message_logging": False}, False), + ({"turn_off_message_logging": "False"}, "False"), + ({"metadata": {"turn_off_message_logging": True}}, True), + ], +) +def test_turn_off_message_logging_extracted_from_kwargs(kwargs, expected): params = initialize_standard_callback_dynamic_params(kwargs) - assert params.get("turn_off_message_logging") is None + assert params.get("turn_off_message_logging") == expected def test_empty_kwargs_returns_empty_params(): diff --git a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py index 41560c18d15..0523ed7ecb1 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -704,7 +704,9 @@ async def test_logging_non_streaming_request(): litellm.callbacks = original_callbacks -@pytest.mark.parametrize("async_flag", ["acompletion", "aresponses"]) +@pytest.mark.parametrize( + "async_flag", ["acompletion", "aresponses", "allm_passthrough_route"] +) def test_success_handler_skips_sync_callbacks_for_async_requests( logging_obj, async_flag ): @@ -792,6 +794,21 @@ def test_success_handler_runs_sync_callbacks_for_sync_requests(logging_obj, call def test_is_sync_litellm_request(): assert LitellmLogging._is_sync_litellm_request({}) is True assert LitellmLogging._is_sync_litellm_request({"acompletion": True}) is False + assert ( + LitellmLogging._is_sync_litellm_request({"allm_passthrough_route": True}) + is False + ) + + +def test_get_litellm_params_propagates_allm_passthrough_route(): + """`allm_passthrough_route=True` set on kwargs by the async passthrough entrypoint + must land in `litellm_params` so `_is_sync_litellm_request` sees it and the + request is classified as async. Regression guard for LIT-4192.""" + from litellm.litellm_core_utils.get_litellm_params import get_litellm_params + + params = get_litellm_params(allm_passthrough_route=True) + assert params.get("allm_passthrough_route") is True + assert LitellmLogging._is_sync_litellm_request(params) is False @pytest.mark.asyncio diff --git a/tests/test_litellm/litellm_core_utils/test_realtime_streaming.py b/tests/test_litellm/litellm_core_utils/test_realtime_streaming.py index 2ad9b919a1f..dff54515098 100644 --- a/tests/test_litellm/litellm_core_utils/test_realtime_streaming.py +++ b/tests/test_litellm/litellm_core_utils/test_realtime_streaming.py @@ -2945,3 +2945,29 @@ def test_non_bidi_setup_left_untouched_for_followup_capable_providers(): assert streaming._maybe_inject_guardrail_auto_response_disable(msg) == msg finally: litellm.callbacks = [] + + +@pytest.mark.asyncio +async def test_log_messages_routes_async_logging_through_bounded_worker(): + """Realtime success logging must go through GLOBAL_LOGGING_WORKER (bounded + queue + per-coroutine timeout), not a bare asyncio.create_task. A bare task + has no timeout/concurrency cap, so when a logging callback is slow every + realtime turn leaves a suspended task pinning its response in memory -> an + unbounded leak. Regression for that fix.""" + logging_obj = MagicMock() + streaming = RealTimeStreaming(MagicMock(), MagicMock(), logging_obj) + streaming.messages = [{"type": "session.created"}] + + with ( + patch("litellm.litellm_core_utils.realtime_streaming.GLOBAL_LOGGING_WORKER") as mock_worker, + patch("litellm.litellm_core_utils.realtime_streaming.asyncio.create_task") as mock_create_task, + ): + await streaming.log_messages() + + mock_worker.ensure_initialized_and_enqueue.assert_called_once() + enqueued = mock_worker.ensure_initialized_and_enqueue.call_args + assert (enqueued.args or tuple(enqueued.kwargs.values()))[0] is logging_obj.dispatch_success_handlers.return_value + logging_obj.dispatch_success_handlers.assert_called_once_with(streaming.messages, prefer_async_handlers=True) + logging_obj.success_handler.assert_not_called() + # the bare create_task path must no longer be used for success logging + mock_create_task.assert_not_called() diff --git a/tests/test_litellm/litellm_core_utils/test_sensitive_data_masker.py b/tests/test_litellm/litellm_core_utils/test_sensitive_data_masker.py index 7239636fd48..ba8540f81e3 100644 --- a/tests/test_litellm/litellm_core_utils/test_sensitive_data_masker.py +++ b/tests/test_litellm/litellm_core_utils/test_sensitive_data_masker.py @@ -197,3 +197,121 @@ def test_cost_per_token_fields_not_masked(): # Actual secrets must still be masked assert "*" in masked["api_key"] assert "*" in masked["access_token"] + + +def test_mask_sensitive_structure_passes_through_plain_topology_names(): + """Fallback groups are usually lists of model-group name strings; those + carry no secrets and must survive verbatim so opt-in debug output stays useful.""" + from litellm.litellm_core_utils.sensitive_data_masker import mask_sensitive_structure + + assert mask_sensitive_structure(["gpt-4", "claude-3-haiku"]) == ["gpt-4", "claude-3-haiku"] + assert mask_sensitive_structure([{"gpt-3.5-turbo": ["claude-3-haiku"]}]) == [ + {"gpt-3.5-turbo": ["claude-3-haiku"]} + ] + assert mask_sensitive_structure(None) is None + + +def test_mask_sensitive_structure_masks_credentials_in_inline_fallback_dicts(): + """An inline-dict fallback can carry provider credentials; those values must be + masked before the structure is embedded in a client-facing error message.""" + from litellm.litellm_core_utils.sensitive_data_masker import mask_sensitive_structure + + secret = "sk-INLINEFALLBACKSECRET1234567890" + aws_secret = "wJalrXUtnFEMIK7MDENGbPxRfiCYSECRETKEY" + masked = mask_sensitive_structure( + [{"model": "openai/gpt-4", "api_key": secret, "aws_secret_access_key": aws_secret}] + ) + + rendered = str(masked) + assert secret not in rendered + assert aws_secret not in rendered + # Non-secret keys stay visible so the fallback wiring remains debuggable + assert masked[0]["model"] == "openai/gpt-4" + assert "*" in masked[0]["api_key"] + assert "*" in masked[0]["aws_secret_access_key"] + + +def test_mask_sensitive_structure_masks_credentials_nested_in_config_shape(): + """Credentials nested inside the {group: [fallbacks]} config shape must also be masked.""" + from litellm.litellm_core_utils.sensitive_data_masker import mask_sensitive_structure + + secret = "sk-NESTEDINLINESECRET0987654321" + masked = mask_sensitive_structure( + [{"primary-group": [{"model": "gpt-4o", "api_key": secret}]}] + ) + assert secret not in str(masked) + + +def test_mask_credentials_in_payload_preserves_none_and_scalars(): + """The payload variant does not distort JSON-shaped values: None stays None, + ints/floats/bools stay themselves, lists stay lists. This is what makes it + safe for logging pipelines that persist the record verbatim.""" + from litellm.litellm_core_utils.sensitive_data_masker import mask_credentials_in_payload + + result = mask_credentials_in_payload( + { + "reason": None, + "confidence": 0.42, + "flagged": True, + "tokens_used": 17, + "categories": ["pii", "toxicity"], + "nested": {"end_user_id": None}, + } + ) + assert result == { + "reason": None, + "confidence": 0.42, + "flagged": True, + "tokens_used": 17, + "categories": ["pii", "toxicity"], + "nested": {"end_user_id": None}, + } + + +def test_mask_credentials_in_payload_masks_inside_pydantic_models(): + """A Pydantic model reached during the walk gets dumped to a dict so its + sensitive-named string fields are masked. Without this the credentials + inside a nested ``UserAPIKeyAuth`` in a guardrail_response reach the + logging pipeline unmasked once JSON serialization flattens it.""" + from pydantic import BaseModel + + from litellm.litellm_core_utils.sensitive_data_masker import mask_credentials_in_payload + + class Auth(BaseModel): + token: str = "1b01552f6e52e0d41963dd6a185bd6b074624e330999534ca7ff5adfdf622dfc" + team_alias: str = "acme" + + result = mask_credentials_in_payload({"user_api_key_auth": Auth()}) + auth_dict = result["user_api_key_auth"] + assert isinstance(auth_dict, dict) + assert auth_dict["team_alias"] == "acme" + assert ( + auth_dict["token"] + != "1b01552f6e52e0d41963dd6a185bd6b074624e330999534ca7ff5adfdf622dfc" + ) + assert "*" in auth_dict["token"] + + +def test_mask_credentials_in_payload_masks_only_sensitive_string_leaves(): + """Sensitive-named string leaves get masked; sibling non-string values + (including None) under the same key stay verbatim.""" + from litellm.litellm_core_utils.sensitive_data_masker import mask_credentials_in_payload + + plaintext = "lsv2_pt_abcdef1234567890" + result = mask_credentials_in_payload( + { + "model": "gpt-4o-mini", + "callback_vars": { + "langsmith_api_key": plaintext, + "langsmith_project": "proj", + "extra_token_count": 5, + }, + } + ) + assert result["model"] == "gpt-4o-mini" + assert result["callback_vars"]["langsmith_project"] == "proj" + assert result["callback_vars"]["extra_token_count"] == 5 + masked = result["callback_vars"]["langsmith_api_key"] + assert masked != plaintext + assert masked.startswith(plaintext[:4]) + assert masked.endswith(plaintext[-4:]) diff --git a/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py b/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py index b5eb7af88b3..ba95c90798e 100644 --- a/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py +++ b/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py @@ -325,6 +325,159 @@ def test_cache_read_input_tokens_retained(): assert usage.cache_read_input_tokens == 11775 assert usage.prompt_tokens_details.cached_tokens == 11775 + +def test_streaming_preserves_anthropic_1hr_cache_creation_breakdown(): + """ + Anthropic emits the cache-creation TTL breakdown (ephemeral 5m/1h split) only + on the `message_start` SSE event; the later `message_delta` carries the flat + cache-creation count but drops the nested `cache_creation` object. Because + prompt_tokens_details is aggregated last-wins, the breakdown used to be + clobbered by message_delta, leaving cost calc with no TTL split. It then fell + back to the 5-minute write rate and undercounted 1-hour cache writes by ~37.5%. + + Reproduces the trace: input=3, cache_creation=50 (all 1h), cache_read=8728. + Correct cache-write cost is 50 * 6e-06 (1h) = 0.0003, not 50 * 3.75e-06 = 0.0001875. + """ + from litellm.llms.anthropic.chat.transformation import AnthropicConfig + from litellm.llms.anthropic.cost_calculation import cost_per_token + + config = AnthropicConfig() + message_start_usage = config.calculate_usage( + usage_object={ + "input_tokens": 3, + "cache_creation_input_tokens": 50, + "cache_read_input_tokens": 8728, + "output_tokens": 1, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 50, + }, + }, + reasoning_content=None, + ) + message_delta_usage = config.calculate_usage( + usage_object={ + "input_tokens": 3, + "cache_creation_input_tokens": 50, + "cache_read_input_tokens": 8728, + "output_tokens": 31, + }, + reasoning_content=None, + ) + # Sanity: the delta event genuinely lacks the breakdown - this is the input + # condition that used to defeat cost calc. + assert ( + getattr(message_delta_usage.prompt_tokens_details, "cache_creation_token_details", None) + is None + ) + + def _usage_chunk(usage, finish_reason): + return ModelResponseStream( + id="chatcmpl-1hr-cache", + created=1745513206, + model="claude-sonnet-4-6", + object="chat.completion.chunk", + choices=[ + StreamingChoices( + finish_reason=finish_reason, + index=0, + delta=Delta(content="" if finish_reason is None else None), + ) + ], + stream_options={"include_usage": True}, + usage=usage, + ) + + chunks = [ + _usage_chunk(message_start_usage, None), + _usage_chunk(message_delta_usage, "stop"), + ] + usage = ChunkProcessor(chunks=chunks).calculate_usage( + chunks=chunks, model="claude-sonnet-4-6", completion_output="hi" + ) + + breakdown = getattr(usage.prompt_tokens_details, "cache_creation_token_details", None) + assert breakdown is not None, "1h/5m cache-creation breakdown lost during aggregation" + assert breakdown.ephemeral_1h_input_tokens == 50 + assert breakdown.ephemeral_5m_input_tokens == 0 + assert usage.cache_creation_input_tokens == 50 + assert usage.cache_read_input_tokens == 8728 + + prompt_cost, _ = cost_per_token(model="claude-sonnet-4-6", usage=usage) + # text 3*3e-06 + cache_read 8728*3e-07 + cache_write 50*6e-06 (1h rate) + expected = 3 * 3e-06 + 8728 * 3e-07 + 50 * 6e-06 + assert prompt_cost == pytest.approx(expected) + # Guard against the regression: 5m-rate fallback would shave the write cost. + buggy = 3 * 3e-06 + 8728 * 3e-07 + 50 * 3.75e-06 + assert prompt_cost != pytest.approx(buggy) + + +def test_streaming_keeps_cache_creation_breakdown_from_final_chunk(): + """When the final usage chunk itself carries the cache-creation breakdown, + aggregation must keep that breakdown instead of re-attaching a stale one + captured from an earlier chunk.""" + from litellm.llms.anthropic.chat.transformation import AnthropicConfig + + config = AnthropicConfig() + message_start_usage = config.calculate_usage( + usage_object={ + "input_tokens": 3, + "cache_creation_input_tokens": 7, + "cache_read_input_tokens": 0, + "output_tokens": 1, + "cache_creation": { + "ephemeral_5m_input_tokens": 7, + "ephemeral_1h_input_tokens": 0, + }, + }, + reasoning_content=None, + ) + message_delta_usage = config.calculate_usage( + usage_object={ + "input_tokens": 3, + "cache_creation_input_tokens": 50, + "cache_read_input_tokens": 0, + "output_tokens": 31, + "cache_creation": { + "ephemeral_5m_input_tokens": 0, + "ephemeral_1h_input_tokens": 50, + }, + }, + reasoning_content=None, + ) + + def _usage_chunk(usage, finish_reason): + return ModelResponseStream( + id="chatcmpl-final-breakdown", + created=1745513206, + model="claude-sonnet-4-6", + object="chat.completion.chunk", + choices=[ + StreamingChoices( + finish_reason=finish_reason, + index=0, + delta=Delta(content="" if finish_reason is None else None), + ) + ], + stream_options={"include_usage": True}, + usage=usage, + ) + + chunks = [ + _usage_chunk(message_start_usage, None), + _usage_chunk(message_delta_usage, "stop"), + ] + usage = ChunkProcessor(chunks=chunks).calculate_usage( + chunks=chunks, model="claude-sonnet-4-6", completion_output="hi" + ) + + breakdown = getattr(usage.prompt_tokens_details, "cache_creation_token_details", None) + assert breakdown is not None + assert breakdown.ephemeral_1h_input_tokens == 50 + assert breakdown.ephemeral_5m_input_tokens == 0 + assert usage.cache_creation_input_tokens == 50 + + def test_cache_read_input_tokens_retained_genericstreamingchunk(): chunk1 = GenericStreamingChunk( text="Test1", diff --git a/tests/test_litellm/litellm_core_utils/test_streaming_handler.py b/tests/test_litellm/litellm_core_utils/test_streaming_handler.py index 81af0ad3e6f..e430ce3b084 100644 --- a/tests/test_litellm/litellm_core_utils/test_streaming_handler.py +++ b/tests/test_litellm/litellm_core_utils/test_streaming_handler.py @@ -986,6 +986,86 @@ async def test_bedrock_validation_error_raises_directly(logging_obj: Logging): assert getattr(excinfo.value, "status_code", None) == 400 +def _hosted_vllm_stream_wrapper(logging_obj: Logging, error_payload: dict) -> CustomStreamWrapper: + """A CustomStreamWrapper over the real OpenAI-compatible line iterator, + fed an HTTP 200 SSE body that carries an in-body error payload the way + vLLM/sglang emit it.""" + from litellm.llms.openai.chat.gpt_transformation import ( + OpenAIChatCompletionStreamingHandler, + ) + + async def _stream(): + yield f"data: {json.dumps(error_payload)}" + yield "data: [DONE]" + + completion_stream = OpenAIChatCompletionStreamingHandler( + streaming_response=_stream(), sync_stream=False + ) + return CustomStreamWrapper( + completion_stream=completion_stream, + model="qwen-vl", + logging_obj=logging_obj, + custom_llm_provider="hosted_vllm", + ) + + +@pytest.mark.asyncio +async def test_in_body_stream_error_400_raises_bad_request(logging_obj: Logging): + """Regression for https://github.com/BerriAI/litellm/issues/25492: a 400 + error returned inside a 200 SSE body must surface as BadRequestError with + the provider's message, not be parsed as an empty chunk that silently + ends the stream (and never as an internal MidStreamFallbackError).""" + from litellm.exceptions import MidStreamFallbackError + + response = _hosted_vllm_stream_wrapper( + logging_obj, + { + "error": { + "object": "error", + "message": "The model is not multimodal. Please remove image inputs.", + "type": "BadRequestError", + "param": None, + "code": 400, + } + }, + ) + + with pytest.raises(litellm.BadRequestError) as excinfo: + await response.__anext__() + + assert not isinstance(excinfo.value, MidStreamFallbackError) + assert excinfo.value.status_code == 400 + assert "not multimodal" in str(excinfo.value) + + +@pytest.mark.asyncio +async def test_in_body_stream_error_500_wraps_for_midstream_fallback( + logging_obj: Logging, +): + """An in-body 5xx error wraps into MidStreamFallbackError so the Router's + FallbackStreamWrapper can switch to a configured fallback deployment.""" + from litellm.exceptions import MidStreamFallbackError + + response = _hosted_vllm_stream_wrapper( + logging_obj, + { + "error": { + "object": "error", + "message": "internal engine crash", + "type": "InternalServerError", + "param": None, + "code": 500, + } + }, + ) + + with pytest.raises(MidStreamFallbackError) as excinfo: + await response.__anext__() + + assert excinfo.value.is_pre_first_chunk is True + assert "internal engine crash" in str(excinfo.value) + + @pytest.mark.asyncio async def test_async_streaming_read_timeout_triggers_midstream_fallback( logging_obj: Logging, diff --git a/tests/test_litellm/litellm_core_utils/test_token_counter.py b/tests/test_litellm/litellm_core_utils/test_token_counter.py index 60e5a797627..71e686563a5 100644 --- a/tests/test_litellm/litellm_core_utils/test_token_counter.py +++ b/tests/test_litellm/litellm_core_utils/test_token_counter.py @@ -97,6 +97,50 @@ def test_token_counter_normal_plus_function_calling(): # test_token_counter_normal_plus_function_calling() +def test_token_counter_legacy_function_call_counts_arguments(): + """ + Regression for VERIA-492 (Token-counter function_call bypass). + + The legacy OpenAI assistant `function_call` field carries arbitrary text in + `arguments`. Before the fix, `_count_messages` had no branch for + `function_call` and fell through to the unsupported-key `continue`, so an + assistant turn could smuggle unlimited text past `token_counter` and the + proxy `/utils/token_counter` endpoint (and downstream pre-call budget / + `get_modified_max_tokens` math). After the fix it must be counted the + same as the equivalent `tool_calls` payload. + """ + long_arg = "A" * 4000 + fc_messages = [ + {"role": "user", "content": "hi"}, + { + "role": "assistant", + "content": None, + "function_call": {"name": "search", "arguments": long_arg}, + }, + ] + tc_messages = [ + {"role": "user", "content": "hi"}, + { + "role": "assistant", + "content": None, + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": {"name": "search", "arguments": long_arg}, + } + ], + }, + ] + fc_tokens = token_counter(model="gpt-3.5-turbo", messages=fc_messages) + tc_tokens = token_counter(model="gpt-3.5-turbo", messages=tc_messages) + assert fc_tokens == tc_tokens, ( + f"function_call arguments must count like tool_calls arguments; " + f"got function_call={fc_tokens}, tool_calls={tc_tokens}" + ) + assert fc_tokens > 500, f"4000-char arguments payload must contribute real tokens, got {fc_tokens}" + + @pytest.mark.parametrize( "message_count_pair", MESSAGES_TEXT, diff --git a/tests/test_litellm/llms/a2a/chat/test_a2a_chat_transformation.py b/tests/test_litellm/llms/a2a/chat/test_a2a_chat_transformation.py new file mode 100644 index 00000000000..2e11c68244c --- /dev/null +++ b/tests/test_litellm/llms/a2a/chat/test_a2a_chat_transformation.py @@ -0,0 +1,42 @@ +"""Tests for litellm/llms/a2a/chat/transformation.py response transform.""" + +from unittest.mock import MagicMock + +from litellm.llms.a2a.chat.transformation import A2AConfig +from litellm.types.utils import ModelResponse + + +def _raw_response(text: str) -> MagicMock: + raw = MagicMock() + raw.status_code = 200 + raw.headers = {} + raw.json.return_value = { + "jsonrpc": "2.0", + "id": "resp-1", + "result": { + "kind": "message", + "parts": [{"kind": "text", "text": text}], + }, + } + return raw + + +def test_transform_response_sets_usage(): + """Regression: A2AConfig.transform_response must populate usage so per-token + pricing computes real cost and callers don't get usage 0/0/0.""" + result = A2AConfig().transform_response( + model="a2a/test-agent", + raw_response=_raw_response("hello from the agent"), + model_response=ModelResponse(), + logging_obj=MagicMock(), + request_data={}, + messages=[{"role": "user", "content": "hi there agent"}], + optional_params={}, + litellm_params={}, + encoding=None, + ) + + assert result.usage is not None + assert result.usage.prompt_tokens > 0 + assert result.usage.completion_tokens > 0 + assert result.usage.total_tokens == (result.usage.prompt_tokens + result.usage.completion_tokens) 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 852479e81e4..43ef7fcd971 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 @@ -10,6 +10,7 @@ from unittest.mock import MagicMock, patch import litellm from litellm.constants import ( + ANTHROPIC_MIN_THINKING_BUDGET_TOKENS, DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, DEFAULT_REASONING_EFFORT_MAX_THINKING_BUDGET, @@ -1661,7 +1662,7 @@ def test_effort_beta_header_injection(): # Test with effort parameter optional_params = {"output_config": {"effort": "low"}} - effort_used = model_info.is_effort_used(optional_params=optional_params) + effort_used = model_info.is_effort_used(optional_params=optional_params, custom_llm_provider="anthropic") assert effort_used is True headers = model_info.get_anthropic_headers( @@ -1877,7 +1878,7 @@ def test_anthropic_drop_params_false_forwards_to_unsupported_model(): ], ) def test_anthropic_model_supports_effort_param_recognizes_supporting_models(model): - assert AnthropicConfig._model_supports_effort_param(model) is True + assert AnthropicConfig._model_supports_effort_param(model, "anthropic") is True @pytest.mark.parametrize( @@ -1890,7 +1891,7 @@ def test_anthropic_model_supports_effort_param_recognizes_supporting_models(mode ], ) def test_anthropic_model_supports_effort_param_rejects_non_supporting_models(model): - assert AnthropicConfig._model_supports_effort_param(model) is False + assert AnthropicConfig._model_supports_effort_param(model, "anthropic") is False @pytest.mark.parametrize( @@ -2217,7 +2218,7 @@ def test_get_config_does_not_leak_module_constants(): ) def test_supports_effort_level_handles_provider_prefixes(model, level, expected): """``_supports_effort_level`` resolves bedrock/vertex/azure-prefixed model ids.""" - assert AnthropicConfig._supports_effort_level(model, level) is expected + assert AnthropicConfig._supports_effort_level(model, level, "anthropic") is expected @pytest.mark.parametrize( @@ -2239,7 +2240,7 @@ def test_supports_effort_level_handles_provider_prefixes(model, level, expected) def test_validate_effort_for_model_centralises_per_model_gating( model, effort, expect_error ): - err = AnthropicConfig._validate_effort_for_model(model, effort) + err = AnthropicConfig._validate_effort_for_model(model, effort, "anthropic") if expect_error: assert err is not None assert effort in err @@ -2443,6 +2444,78 @@ def test_reasoning_effort_maps_to_adaptive_thinking_for_claude_4_6_models(): assert result["output_config"]["effort"] == effort_map[effort] +def test_raw_adaptive_thinking_translates_to_legacy_for_pre_46_model(): + """Clients like Claude Code send ``thinking={"type": "adaptive"}`` directly + (not via ``reasoning_effort``) on every request, regardless of which model + the request routes to. For a pre-4.6 model that doesn't understand + adaptive thinking, this must be translated to the legacy + ``thinking={type: enabled, budget_tokens}`` interface instead of being + forwarded raw, which Anthropic would reject.""" + config = AnthropicConfig() + + result = config.map_openai_params( + non_default_params={"thinking": {"type": "adaptive"}, "max_tokens": 8192}, + optional_params={}, + model="claude-haiku-4-5-20251001", + drop_params=False, + ) + + assert result["thinking"]["type"] == "enabled" + assert result["thinking"]["budget_tokens"] == DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET + + +def test_raw_adaptive_thinking_budget_capped_below_max_tokens(): + """Anthropic requires ``max_tokens > thinking.budget_tokens``. When the + default medium budget wouldn't fit, it must be capped below max_tokens + rather than forwarded as an invalid combination.""" + config = AnthropicConfig() + + max_tokens = DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET - 100 + result = config.map_openai_params( + non_default_params={"thinking": {"type": "adaptive"}, "max_tokens": max_tokens}, + optional_params={}, + model="claude-haiku-4-5-20251001", + drop_params=False, + ) + + assert result["thinking"]["type"] == "enabled" + assert result["thinking"]["budget_tokens"] == max_tokens - 1 + + +def test_raw_adaptive_thinking_dropped_when_max_tokens_too_small(): + """When max_tokens can't fit even the minimum thinking budget, thinking + must be dropped entirely so the request still succeeds, matching how the + native /v1/messages passthrough already handles this.""" + config = AnthropicConfig() + + result = config.map_openai_params( + non_default_params={ + "thinking": {"type": "adaptive"}, + "max_tokens": ANTHROPIC_MIN_THINKING_BUDGET_TOKENS, + }, + optional_params={}, + model="claude-haiku-4-5-20251001", + drop_params=False, + ) + + assert "thinking" not in result + + +def test_raw_adaptive_thinking_untouched_for_46_plus_model(): + """Adaptive-thinking models understand ``thinking={"type": "adaptive"}`` + natively, so it must pass through unmodified.""" + config = AnthropicConfig() + + result = config.map_openai_params( + non_default_params={"thinking": {"type": "adaptive"}, "max_tokens": 8192}, + optional_params={}, + model="claude-sonnet-4-6-20260219", + drop_params=False, + ) + + assert result["thinking"] == {"type": "adaptive"} + + @pytest.fixture def local_model_cost_map(monkeypatch): original_model_cost = litellm.model_cost @@ -2490,7 +2563,7 @@ def test_is_adaptive_thinking_model_is_sourced_from_cost_map( fallback for ids the cost map cannot resolve. The dated Claude 4.0 names stay non-adaptive because the date suffix is not read as a minor version, while 4.8/4.9/5.x are covered without a code change.""" - assert AnthropicConfig._is_adaptive_thinking_model(model) is expected + assert AnthropicConfig._is_adaptive_thinking_model(model, "anthropic") is expected def test_get_supported_params_includes_reasoning_for_sonnet_4_6_alias( @@ -2836,6 +2909,7 @@ def test_effort_beta_header_not_injected_for_46_models(): result = model_info.is_effort_used( optional_params={"output_config": {"effort": "high"}}, model=model, + custom_llm_provider="anthropic", ) assert result is False, f"is_effort_used should return False for {model}" @@ -2947,6 +3021,7 @@ def test_effort_beta_header_still_injected_for_older_models(): result = model_info.is_effort_used( optional_params={"output_config": {"effort": "low"}}, model="claude-opus-4-5-20251101", + custom_llm_provider="anthropic", ) assert result is True diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/context_management/test_compact.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/context_management/test_compact.py index be430db9eed..9c8df1c79f9 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/context_management/test_compact.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/context_management/test_compact.py @@ -12,11 +12,13 @@ Coverage: - custom instructions → default prompt is not used even when tools present """ +import json from typing import Any, Dict, List from unittest.mock import AsyncMock, MagicMock, patch import pytest +import litellm from litellm.llms.anthropic.experimental_pass_through.context_management import ( AnthropicContextManagementError, apply_context_management, @@ -2042,12 +2044,12 @@ async def test_dispatcher_trigger_below_minimum_raises_through(): # --------------------------------------------------------------------------- -# _run_polyfill_if_enabled: drop_params gate +# _run_polyfill_if_enabled: additional_drop_params gate (drop_params must NOT gate) # --------------------------------------------------------------------------- -async def test_run_polyfill_skipped_when_drop_params_true(): - """When drop_params=True the polyfill must be skipped (returns None).""" +async def test_run_polyfill_skipped_when_context_management_in_additional_drop_params(): + """additional_drop_params=["context_management"] is the explicit opt-out.""" from litellm.llms.anthropic.experimental_pass_through.adapters.handler import ( _run_polyfill_if_enabled, ) @@ -2059,12 +2061,39 @@ async def test_run_polyfill_skipped_when_drop_params_true(): system=None, context_management_spec={"edits": [{"type": "compact_20260112"}]}, litellm_metadata={}, - drop_params=True, + additional_drop_params=["context_management"], llm_router=None, ) assert result is None +async def test_run_polyfill_runs_when_litellm_drop_params_true(monkeypatch): + """drop_params must not disable the polyfill: context_management is a + LiteLLM-supported param (polyfilled where not native), and drop_params only + exists to strip genuinely unsupported params.""" + from litellm.llms.anthropic.experimental_pass_through.adapters.handler import ( + _run_polyfill_if_enabled, + ) + + monkeypatch.setattr(litellm, "drop_params", True) + with patch( + "litellm.llms.anthropic.experimental_pass_through.context_management.editors.compact._read_summary_model_setting", + return_value=None, + ): + result = await _run_polyfill_if_enabled( + model=MODEL, + messages=_simple_messages(), + tools=None, + system=None, + context_management_spec={"edits": [{"type": "compact_20260112"}]}, + litellm_metadata={}, + additional_drop_params=None, + llm_router=None, + ) + assert result is not None + assert result.applied_edits[0]["type"] == "compact_20260112" + + async def test_run_polyfill_skipped_when_spec_empty(): """Empty context_management_spec must also return None (no polyfill work).""" from litellm.llms.anthropic.experimental_pass_through.adapters.handler import ( @@ -2078,12 +2107,169 @@ async def test_run_polyfill_skipped_when_spec_empty(): system=None, context_management_spec=None, litellm_metadata={}, - drop_params=False, + additional_drop_params=None, llm_router=None, ) assert result is None +# --------------------------------------------------------------------------- +# Adapter handler entry points: polyfill vs drop_params / additional_drop_params +# --------------------------------------------------------------------------- + +_CLEAR_TOOL_USES_SPEC: Dict[str, Any] = { + "edits": [ + { + "type": "clear_tool_uses_20250919", + "trigger": {"type": "tool_uses", "value": 1}, + "keep": {"type": "tool_uses", "value": 0}, + } + ] +} + +_CLEARED_PLACEHOLDER = "[Cleared by context management]" + + +def _tool_use_messages() -> List[Dict[str, Any]]: + return [ + {"role": "user", "content": "check the weather in two cities"}, + { + "role": "assistant", + "content": [{"type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": {"city": "SF"}}], + }, + { + "role": "user", + "content": [{"type": "tool_result", "tool_use_id": "toolu_01", "content": "sunny in SF"}], + }, + { + "role": "assistant", + "content": [{"type": "tool_use", "id": "toolu_02", "name": "get_weather", "input": {"city": "NY"}}], + }, + { + "role": "user", + "content": [{"type": "tool_result", "tool_use_id": "toolu_02", "content": "rainy in NY"}], + }, + {"role": "user", "content": "now summarize both"}, + ] + + +def _openai_chat_response(): + from litellm.types.utils import ModelResponse + + return ModelResponse( + id="chatcmpl-test", + model="gpt-4o", + choices=[{"finish_reason": "stop", "index": 0, "message": {"role": "assistant", "content": "done"}}], + usage={"prompt_tokens": 10, "completion_tokens": 2, "total_tokens": 12}, + ) + + +async def _call_async_adapter_handler(**handler_kwargs: Any): + from litellm.llms.anthropic.experimental_pass_through.adapters.handler import ( + LiteLLMMessagesToCompletionTransformationHandler, + ) + + captured: Dict[str, Any] = {} + + async def _capture_acompletion(**kwargs): + captured.update(kwargs) + return _openai_chat_response() + + with patch("litellm.acompletion", side_effect=_capture_acompletion): + response = await LiteLLMMessagesToCompletionTransformationHandler.async_anthropic_messages_handler( + max_tokens=128, + messages=_tool_use_messages(), + model=MODEL, + context_management=_CLEAR_TOOL_USES_SPEC, + litellm_router=MagicMock(), + **handler_kwargs, + ) + return response, captured + + +def _assert_polyfill_applied(response: Any, captured: Dict[str, Any]) -> None: + applied_edits = (response.get("context_management") or {}).get("applied_edits") + assert applied_edits, "polyfill must run and report applied_edits" + assert applied_edits[0]["type"] == "clear_tool_uses_20250919" + forwarded = json.dumps(captured["messages"], default=str) + assert _CLEARED_PLACEHOLDER in forwarded + assert "sunny in SF" not in forwarded + assert "rainy in NY" in forwarded + + +async def test_async_handler_runs_polyfill_when_request_drop_params_true(): + """Regression (LIT-3768): per-request drop_params=True silently skipped the + polyfill, so Claude Code requests (where the proxy defaults drop_params on) + lost context editing on non-Anthropic models.""" + response, captured = await _call_async_adapter_handler(drop_params=True) + _assert_polyfill_applied(response, captured) + + +async def test_async_handler_runs_polyfill_when_litellm_drop_params_true(monkeypatch): + """Regression (LIT-3768): proxy-wide litellm.drop_params=True silently + skipped the polyfill too.""" + monkeypatch.setattr(litellm, "drop_params", True) + response, captured = await _call_async_adapter_handler() + _assert_polyfill_applied(response, captured) + + +async def test_async_handler_additional_drop_params_strips_context_management(): + """additional_drop_params=["context_management"] stays the escape hatch: + the polyfill must not run and the request is forwarded untouched.""" + response, captured = await _call_async_adapter_handler(additional_drop_params=["context_management"]) + assert response.get("context_management") is None + forwarded = json.dumps(captured["messages"], default=str) + assert _CLEARED_PLACEHOLDER not in forwarded + assert "sunny in SF" in forwarded + + +def _call_sync_adapter_handler(**handler_kwargs: Any): + from litellm.llms.anthropic.experimental_pass_through.adapters.handler import ( + LiteLLMMessagesToCompletionTransformationHandler, + ) + + captured: Dict[str, Any] = {} + + def _capture_completion(**kwargs): + captured.update(kwargs) + return _openai_chat_response() + + with patch("litellm.completion", side_effect=_capture_completion): + response = LiteLLMMessagesToCompletionTransformationHandler.anthropic_messages_handler( + max_tokens=128, + messages=_tool_use_messages(), + model=MODEL, + context_management=_CLEAR_TOOL_USES_SPEC, + litellm_router=None, + **handler_kwargs, + ) + return response, captured + + +def test_sync_handler_runs_polyfill_when_request_drop_params_true(): + """The sync entry point reads its own kwargs; cover its gate separately.""" + response, captured = _call_sync_adapter_handler(drop_params=True) + _assert_polyfill_applied(response, captured) + + +def test_sync_handler_runs_polyfill_when_litellm_drop_params_true(monkeypatch): + """Proxy-wide litellm.drop_params=True must not skip the polyfill on the + sync entry point either.""" + monkeypatch.setattr(litellm, "drop_params", True) + response, captured = _call_sync_adapter_handler() + _assert_polyfill_applied(response, captured) + + +def test_sync_handler_additional_drop_params_strips_context_management(): + """The additional_drop_params=["context_management"] escape hatch is honored + on the sync entry point too: no polyfill, request forwarded untouched.""" + response, captured = _call_sync_adapter_handler(additional_drop_params=["context_management"]) + assert response.get("context_management") is None + forwarded = json.dumps(captured["messages"], default=str) + assert _CLEARED_PLACEHOLDER not in forwarded + assert "sunny in SF" in forwarded + + async def test_prepare_context_managed_request_forwards_proxy_litellm_metadata(): """The handler must hand the polyfill the proxy ``litellm_metadata`` (which carries ``user_api_key`` / ``user_api_key_team_id`` / ...), not the @@ -2120,7 +2306,7 @@ async def test_prepare_context_managed_request_forwards_proxy_litellm_metadata() "user_api_key_user_id": "user-xyz", "litellm_call_id": "call-1", }, - drop_params=False, + additional_drop_params=None, llm_router=_RouterStub(), ) diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py index 7bcaf07c5bb..3327fc39f73 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py @@ -715,3 +715,109 @@ async def test_async_wrapper_sets_presanitized_and_sanitizes_once(): assert spy.call_count == 1 assert captured["presanitized"] is True assert [b["type"] for b in captured["messages"][0]["content"]] == ["tool_use"] + + +def _gate_stubs(monkeypatch): + """Patch the gate's downstream dispatch targets so config selection can be + observed without making a network call. + + Returns ``(captured, translation_calls)`` where ``captured["config"]`` is the + provider config handed to the native passthrough path and ``translation_calls`` + counts hits on the Anthropic->OpenAI translation handlers. + """ + from litellm.llms.anthropic.experimental_pass_through.messages import handler + + captured = {} + translation_calls = {"count": 0} + + def fake_native(**kwargs): + captured["config"] = kwargs.get("anthropic_messages_provider_config") + return "native-passthrough" + + def fake_translation(**kwargs): + translation_calls["count"] += 1 + return "translated" + + monkeypatch.setattr(handler.base_llm_http_handler, "anthropic_messages_handler", fake_native) + monkeypatch.setattr( + handler.LiteLLMMessagesToResponsesAPIHandler, + "anthropic_messages_handler", + staticmethod(fake_translation), + ) + monkeypatch.setattr( + handler.LiteLLMMessagesToCompletionTransformationHandler, + "anthropic_messages_handler", + staticmethod(fake_translation), + ) + return captured, translation_calls + + +def test_gate_passthrough_when_supported_endpoints_opts_in(monkeypatch): + """provider=openai + model_info.supported_endpoints containing /v1/messages + must route to the native passthrough config, NOT the translation handlers.""" + from litellm.llms.anthropic.experimental_pass_through.messages.handler import ( + anthropic_messages_handler, + ) + from litellm.llms.openai_like.messages.transformation import ( + OpenAILikeAnthropicMessagesConfig, + ) + + captured, translation_calls = _gate_stubs(monkeypatch) + + result = anthropic_messages_handler( + max_tokens=100, + messages=[{"role": "user", "content": "Hello"}], + model="openai/some-model", + api_key="sk-test", + api_base="https://host/v1", + model_info={"supported_endpoints": ["/v1/chat/completions", "/v1/messages"]}, + ) + + assert result == "native-passthrough" + assert isinstance(captured["config"], OpenAILikeAnthropicMessagesConfig) + assert translation_calls["count"] == 0 + + +def test_gate_translates_when_supported_endpoints_absent(monkeypatch): + """Default behavior is unchanged: without the /v1/messages opt-in, an openai + deployment is translated (Responses API), never passed through natively.""" + from litellm.llms.anthropic.experimental_pass_through.messages.handler import ( + anthropic_messages_handler, + ) + + captured, translation_calls = _gate_stubs(monkeypatch) + + result = anthropic_messages_handler( + max_tokens=100, + messages=[{"role": "user", "content": "Hello"}], + model="openai/some-model", + api_key="sk-test", + api_base="https://host/v1", + ) + + assert result == "translated" + assert translation_calls["count"] == 1 + assert "config" not in captured + + +def test_gate_passthrough_skipped_when_only_chat_completions_supported(monkeypatch): + """A deployment that lists only /v1/chat/completions is still translated; + the opt-in is specifically the /v1/messages entry.""" + from litellm.llms.anthropic.experimental_pass_through.messages.handler import ( + anthropic_messages_handler, + ) + + captured, translation_calls = _gate_stubs(monkeypatch) + + result = anthropic_messages_handler( + max_tokens=100, + messages=[{"role": "user", "content": "Hello"}], + model="openai/some-model", + api_key="sk-test", + api_base="https://host/v1", + model_info={"supported_endpoints": ["/v1/chat/completions"]}, + ) + + assert result == "translated" + assert translation_calls["count"] == 1 + assert "config" not in captured diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_messages_effort.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_messages_effort.py new file mode 100644 index 00000000000..06d3effcfbb --- /dev/null +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_messages_effort.py @@ -0,0 +1,189 @@ +import pytest + +from litellm.constants import ( + DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, + DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET, + DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET, +) +from litellm.llms.anthropic.common_utils import AnthropicError +from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, +) + + +def _claude_code_payload(effort="medium", max_tokens=8192, **output_config_extra): + """The exact adaptive-thinking shape Claude Code (claude-cli) sends.""" + output_config = {"effort": effort, **output_config_extra} + return { + "max_tokens": max_tokens, + "thinking": {"type": "adaptive"}, + "output_config": output_config, + } + + +def _transform(model, params, litellm_params=None): + return AnthropicMessagesConfig().transform_anthropic_messages_request( + model=model, + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_optional_request_params=dict(params), + litellm_params=litellm_params or {}, + headers={}, + ) + + +def test_effort_translated_to_legacy_thinking_for_haiku_4_5(): + """Core regression: Claude Code sends adaptive thinking + effort to Haiku 4.5 + (thinking-capable, pre-4.6). Effort must be translated to legacy extended + thinking rather than forwarded raw (which Anthropic rejects with "This model + does not support the effort parameter").""" + result = _transform("claude-haiku-4-5", _claude_code_payload(effort="medium")) + + assert result["thinking"] == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET, + } + assert "output_config" not in result + + +def test_effort_high_maps_to_high_budget_for_sonnet_4_5(): + result = _transform("claude-sonnet-4-5", _claude_code_payload(effort="high")) + + assert result["thinking"] == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, + } + assert "output_config" not in result + + +def test_adaptive_effort_passes_through_untouched_for_4_6(): + """4.6+ natively supports the adaptive interface, so it must not be rewritten.""" + result = _transform("claude-sonnet-4-6", _claude_code_payload(effort="high")) + + assert result["thinking"] == {"type": "adaptive"} + assert result["output_config"] == {"effort": "high"} + + +def test_thinking_and_effort_dropped_for_non_reasoning_model(): + """A model with no reasoning support cannot take thinking or effort, so both are + silently dropped (no drop_params required) so the request still succeeds.""" + result = _transform("claude-3-5-haiku-latest", _claude_code_payload(effort="medium")) + + assert "thinking" not in result + assert "output_config" not in result + + +def test_residual_output_config_preserved_after_effort_translation(): + """output_config may carry `format` (structured outputs) alongside effort. Only + the consumed effort key is removed; the residual is left for provider subclasses + (bedrock/vertex) to handle, and effort is translated to legacy thinking.""" + result = _transform( + "claude-haiku-4-5", + _claude_code_payload(effort="medium", format={"type": "json_schema"}), + ) + + assert result["thinking"] == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_MEDIUM_THINKING_BUDGET, + } + assert result["output_config"] == {"format": {"type": "json_schema"}} + + +def test_opus_4_5_keeps_effort_but_drops_adaptive_thinking(): + """Regression: Opus 4.5 advertises supports_output_config (accepts + output_config.effort) but is NOT adaptive, so thinking:{type:adaptive} is + rejected by Anthropic. The effort must be kept and only the adaptive thinking + block dropped, rather than early-returning and forwarding adaptive thinking raw.""" + result = _transform("claude-opus-4-5", _claude_code_payload(effort="medium")) + + assert result["output_config"] == {"effort": "medium"} + assert "thinking" not in result + + +def test_opus_4_5_preserves_native_effort_without_adaptive_thinking(): + """A caller sending output_config.effort alone (no adaptive thinking) to Opus 4.5 + must pass through untouched, since the model supports it natively.""" + result = AnthropicMessagesConfig().transform_anthropic_messages_request( + model="claude-opus-4-5", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_optional_request_params={ + "max_tokens": 8192, + "output_config": {"effort": "high"}, + }, + litellm_params={}, + headers={}, + ) + + assert result["output_config"] == {"effort": "high"} + assert "thinking" not in result + + +def test_opus_4_5_unsupported_effort_level_translated_to_legacy_thinking(): + """Opus 4.5 accepts output_config.effort but only levels low/medium/high; + Claude Code defaults to xhigh on newer models, and forwarding that level raw + would be rejected with "effort='xhigh' is not supported by this model". An + unsupported level must fall through to the legacy translation (budget-based + thinking, effort stripped) instead of being preserved.""" + result = _transform("claude-opus-4-5", _claude_code_payload(effort="xhigh", max_tokens=64000)) + + assert result["thinking"] == { + "type": "enabled", + "budget_tokens": DEFAULT_REASONING_EFFORT_XHIGH_THINKING_BUDGET, + } + assert "output_config" not in result + + +def test_opus_4_5_effort_only_unsupported_level_left_for_provider_normalization(): + """An effort-only request (no adaptive thinking) must pass through untouched even + when the level exceeds what the model supports: provider subclasses own their + level normalization (bedrock clamps xhigh to the model's ceiling after this base + transform runs), so consuming the effort here breaks that contract.""" + result = _transform( + "claude-opus-4-5", + {"max_tokens": 4096, "output_config": {"effort": "xhigh"}}, + ) + + assert result["output_config"] == {"effort": "xhigh"} + assert "thinking" not in result + + +def test_budget_capped_below_max_tokens(): + """Adaptive thinking carries no budget, so the translated legacy budget must be + capped below max_tokens (Anthropic requires max_tokens > budget_tokens). A + high-effort budget (4096) with max_tokens=3000 must be capped to 2999.""" + result = _transform("claude-haiku-4-5", _claude_code_payload(effort="high", max_tokens=3000)) + + assert result["thinking"] == {"type": "enabled", "budget_tokens": 2999} + + +def test_thinking_dropped_when_max_tokens_too_small_for_min_budget(): + """When max_tokens can't fit even the minimum thinking budget, thinking is + silently dropped so the request still succeeds rather than being rejected.""" + result = _transform("claude-haiku-4-5", _claude_code_payload(effort="medium", max_tokens=512)) + + assert "thinking" not in result + assert "output_config" not in result + + +def test_unrecognized_effort_raises_clean_400(): + """An unrecognized effort value (e.g. a future Anthropic tier) must surface as a + clean AnthropicError 400, matching _translate_reasoning_effort_to_anthropic, + rather than leaking litellm's internal BadRequestError.""" + with pytest.raises(AnthropicError) as exc_info: + _transform("claude-haiku-4-5", _claude_code_payload(effort="turbo")) + + assert exc_info.value.status_code == 400 + + +def test_non_adaptive_request_without_effort_is_untouched(): + """A non-adaptive model receiving a request with no adaptive interface (no + effort, no adaptive thinking) must pass through untouched.""" + result = AnthropicMessagesConfig().transform_anthropic_messages_request( + model="claude-haiku-4-5", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_optional_request_params={"max_tokens": 1024}, + litellm_params={}, + headers={}, + ) + + assert "thinking" not in result + assert "output_config" not in result diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_streaming_iterator.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_streaming_iterator.py new file mode 100644 index 00000000000..6ea9098c228 --- /dev/null +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_streaming_iterator.py @@ -0,0 +1,245 @@ +import json +import os +import sys +from datetime import datetime + +import pytest + +sys.path.insert(0, os.path.abspath("../../../../..")) + +from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.llms.anthropic.experimental_pass_through.messages.streaming_iterator import ( + INCOMPLETE_STREAM_ERROR_MESSAGE, + BaseAnthropicMessagesStreamingIterator, + _incomplete_stream_error_sse_event, + _is_message_stop_chunk, +) + + +class _RecordingLoggingIterator(BaseAnthropicMessagesStreamingIterator): + def __init__(self, litellm_logging_obj: LiteLLMLoggingObj, request_body: dict): + super().__init__(litellm_logging_obj=litellm_logging_obj, request_body=request_body) + self.logged_chunks: list = [] + + async def _handle_streaming_logging(self, collected_chunks): + self.logged_chunks = list(collected_chunks) + + +def _make_logging_obj(test_name: str) -> LiteLLMLoggingObj: + return LiteLLMLoggingObj( + model="bedrock/invoke/anthropic.claude-3-sonnet-20240229-v1:0", + messages=[{"role": "user", "content": "hi"}], + stream=True, + call_type="chat", + start_time=datetime.now(), + litellm_call_id=test_name, + function_id=test_name, + ) + + +def _make_iterator(test_name: str) -> BaseAnthropicMessagesStreamingIterator: + return BaseAnthropicMessagesStreamingIterator( + litellm_logging_obj=_make_logging_obj(test_name), + request_body={}, + ) + + +async def _collect(iterator, stream): + return [chunk async for chunk in iterator.async_sse_wrapper(stream)] + + +TRUNCATED_TOOL_USE_EVENTS = ( + {"type": "message_start", "message": {"id": "msg_1", "usage": {"input_tokens": 10, "output_tokens": 1}}}, + { + "type": "content_block_start", + "index": 0, + "content_block": {"type": "tool_use", "id": "tooluse_1", "name": "write", "input": {}}, + }, + { + "type": "content_block_delta", + "index": 0, + "delta": {"type": "input_json_delta", "partial_json": '{"path": "/builder/docs/QUAL'}, + }, +) + + +@pytest.mark.asyncio +async def test_async_sse_wrapper_emits_error_event_when_stream_ends_without_message_stop(): + """ + Regression test for LIT-3724: a Bedrock stream that goes silent + mid tool_use must not be passed through as a successful, complete + SSE stream. An `error` SSE event must be appended so strict clients + (Anthropic SDK, Claude Code) surface the truncation instead of + crashing on unterminated tool-call JSON. + """ + + async def _truncated_stream(): + for event in TRUNCATED_TOOL_USE_EVENTS: + yield event + + iterator = _make_iterator("test_truncated_stream_emits_error") + chunks = await _collect(iterator, _truncated_stream()) + + assert len(chunks) == len(TRUNCATED_TOOL_USE_EVENTS) + 1 + error_chunk = chunks[-1].decode() + assert error_chunk.startswith("event: error\n") + assert error_chunk.endswith("\n\n") + + error_payload = json.loads(error_chunk.split("data: ", 1)[1]) + assert error_payload["type"] == "error" + assert error_payload["error"]["type"] == "api_error" + assert error_payload["error"]["message"] == INCOMPLETE_STREAM_ERROR_MESSAGE + + +@pytest.mark.asyncio +async def test_async_sse_wrapper_no_error_event_on_complete_stream(): + async def _complete_stream(): + for event in TRUNCATED_TOOL_USE_EVENTS: + yield event + yield {"type": "content_block_stop", "index": 0} + yield {"type": "message_delta", "delta": {"stop_reason": "tool_use"}, "usage": {"output_tokens": 5}} + yield {"type": "message_stop"} + + iterator = _make_iterator("test_complete_stream_no_error") + chunks = await _collect(iterator, _complete_stream()) + + assert len(chunks) == len(TRUNCATED_TOOL_USE_EVENTS) + 3 + decoded = [chunk.decode() for chunk in chunks] + assert decoded[-1].startswith("event: message_stop\n") + assert not any(chunk.startswith("event: error\n") for chunk in decoded) + + +@pytest.mark.asyncio +async def test_async_sse_wrapper_emits_error_event_on_empty_stream(): + async def _empty_stream(): + return + yield + + iterator = _make_iterator("test_empty_stream_emits_error") + chunks = await _collect(iterator, _empty_stream()) + + assert len(chunks) == 1 + assert chunks[0].decode().startswith("event: error\n") + + +@pytest.mark.asyncio +async def test_async_sse_wrapper_treats_message_stop_bytes_as_complete(): + async def _byte_stream(): + yield b'event: message_start\ndata: {"type": "message_start"}\n\n' + yield b'event: message_stop\ndata: {"type": "message_stop"}\n\n' + + iterator = _make_iterator("test_byte_stream_message_stop") + chunks = await _collect(iterator, _byte_stream()) + + assert len(chunks) == 2 + assert not any(chunk.startswith(b"event: error\n") for chunk in chunks) + + +def test_is_message_stop_chunk(): + assert _is_message_stop_chunk({"type": "message_stop"}) is True + assert _is_message_stop_chunk({"type": "message_delta"}) is False + assert _is_message_stop_chunk(b'event: message_stop\ndata: {}\n\n') is True + assert _is_message_stop_chunk(b"raw-bytes") is False + assert _is_message_stop_chunk("message_stop") is False + + +def test_is_message_stop_chunk_ignores_substring_in_payload(): + """ + Regression: a `content_block_delta` frame whose payload happens to contain + the literal string `message_stop` (e.g. inside a tool's partial_json) must + not be treated as a terminal stop event. + """ + delta_frame_with_substring = ( + b'event: content_block_delta\n' + b'data: {"type": "content_block_delta", "delta": ' + b'{"type": "input_json_delta", "partial_json": "\\"message_stop\\""}}\n\n' + ) + assert _is_message_stop_chunk(delta_frame_with_substring) is False + + +@pytest.mark.asyncio +async def test_async_sse_wrapper_emits_error_when_bytes_stream_only_mentions_message_stop_in_payload(): + """ + Regression for the bytes-branch substring false positive: a stream whose + payload text contains `message_stop` (but never emits the actual + `event: message_stop` frame) must still be flagged as incomplete. + """ + async def _byte_stream(): + yield b'event: message_start\ndata: {"type": "message_start"}\n\n' + yield ( + b'event: content_block_delta\n' + b'data: {"type": "content_block_delta", "delta": ' + b'{"type": "input_json_delta", "partial_json": "\\"message_stop\\""}}\n\n' + ) + + iterator = _make_iterator("test_bytes_substring_does_not_mark_complete") + chunks = await _collect(iterator, _byte_stream()) + + assert len(chunks) == 3 + assert chunks[-1].decode().startswith("event: error\n") + + +@pytest.mark.asyncio +async def test_async_sse_wrapper_does_not_double_error_on_provider_error_dict(): + """ + Regression: when the provider itself terminates the stream with an + `error` event (without a `message_stop`), the wrapper must forward that + error and not append a second synthetic incomplete-stream error. + """ + provider_error = {"type": "error", "error": {"type": "overloaded_error", "message": "boom"}} + + async def _error_terminated_stream(): + yield {"type": "message_start", "message": {"id": "msg_1"}} + yield provider_error + + iterator = _make_iterator("test_provider_error_terminal_dict") + chunks = await _collect(iterator, _error_terminated_stream()) + + assert len(chunks) == 2 + error_frames = [c for c in chunks if c.startswith(b"event: error\n")] + assert len(error_frames) == 1 + payload = json.loads(error_frames[0].decode().split("data: ", 1)[1]) + assert payload == provider_error + + +@pytest.mark.asyncio +async def test_async_sse_wrapper_does_not_double_error_on_provider_error_bytes(): + async def _byte_stream(): + yield b'event: message_start\ndata: {"type": "message_start"}\n\n' + yield b'event: error\ndata: {"type": "error", "error": {"type": "overloaded_error"}}\n\n' + + iterator = _make_iterator("test_provider_error_terminal_bytes") + chunks = await _collect(iterator, _byte_stream()) + + assert len(chunks) == 2 + error_frames = [c for c in chunks if c.startswith(b"event: error\n")] + assert len(error_frames) == 1 + + +@pytest.mark.asyncio +async def test_async_sse_wrapper_excludes_synthetic_error_event_from_logged_chunks(): + async def _truncated_stream(): + for event in TRUNCATED_TOOL_USE_EVENTS: + yield event + + iterator = _RecordingLoggingIterator( + litellm_logging_obj=_make_logging_obj("test_synthetic_error_not_logged"), + request_body={}, + ) + chunks = await _collect(iterator, _truncated_stream()) + + assert chunks[-1].startswith(b"event: error\n") + assert iterator.logged_chunks == chunks[:-1] + assert not any(chunk.startswith(b"event: error\n") for chunk in iterator.logged_chunks) + + +def test_incomplete_stream_error_sse_event_is_valid_anthropic_error(): + event = _incomplete_stream_error_sse_event().decode() + lines = event.split("\n") + assert lines[0] == "event: error" + payload = json.loads(lines[1].removeprefix("data: ")) + assert payload == { + "type": "error", + "error": {"type": "api_error", "message": INCOMPLETE_STREAM_ERROR_MESSAGE}, + } + assert event.endswith("\n\n") diff --git a/tests/test_litellm/llms/anthropic/files/test_anthropic_files_transformation.py b/tests/test_litellm/llms/anthropic/files/test_anthropic_files_transformation.py index 9fc4981510f..d9763f173a7 100644 --- a/tests/test_litellm/llms/anthropic/files/test_anthropic_files_transformation.py +++ b/tests/test_litellm/llms/anthropic/files/test_anthropic_files_transformation.py @@ -309,6 +309,31 @@ class TestAnthropicFilesConfig: assert url == f"{ANTHROPIC_FILES_API_BASE}/v1/files/file-abc123/content" assert params == {} + def test_transform_file_content_request_routes_message_batch_id_to_batch_results(self): + """ + Regression test for anthropic passthrough batch cost tracking (LIT-4008). + + Anthropic batch results are exposed via output_file_id=. + The Files API rejects those ids ("File id must have `file_` prefix"), + so file content for a msgbatch_ id must be fetched from the message + batches results endpoint instead. + """ + url, params = self.config.transform_file_content_request( + file_content_request={"file_id": "msgbatch_01WA5hdsa2Xx8w4zyPjV1frs"}, + optional_params={}, + litellm_params={}, + ) + assert url == f"{ANTHROPIC_FILES_API_BASE}/v1/messages/batches/msgbatch_01WA5hdsa2Xx8w4zyPjV1frs/results" + assert params == {} + + def test_transform_file_content_request_message_batch_id_custom_api_base(self): + url, _ = self.config.transform_file_content_request( + file_content_request={"file_id": "msgbatch_abc"}, + optional_params={}, + litellm_params={"api_base": "https://custom.example.com/"}, + ) + assert url == "https://custom.example.com/v1/messages/batches/msgbatch_abc/results" + def test_transform_file_content_request_rejects_dot_segment(self): with pytest.raises(ValueError, match="file_id cannot be a dot path segment"): self.config.transform_file_content_request( diff --git a/tests/test_litellm/llms/anthropic/messages/test_advisor_orchestration.py b/tests/test_litellm/llms/anthropic/messages/test_advisor_orchestration.py index 31047d30970..a2f5e00c8aa 100644 --- a/tests/test_litellm/llms/anthropic/messages/test_advisor_orchestration.py +++ b/tests/test_litellm/llms/anthropic/messages/test_advisor_orchestration.py @@ -558,9 +558,14 @@ async def _run_advisor_and_capture_subcall_kwargs(): return advisor_advice_resp return final_resp - with patch( - "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._call_messages_handler", - side_effect=mock_call, + with ( + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._call_messages_handler", + side_effect=mock_call, + ), + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor.validate_url", + ), ): h = AdvisorOrchestrationHandler() await h.handle( @@ -730,3 +735,187 @@ async def test_advisor_uses_tool_credentials_when_clientside_enabled(): captured = await _run_advisor_and_capture_subcall_kwargs() assert captured["api_key"] == "sk-other" assert captured["api_base"] == "https://other.example" + + +# --------------------------------------------------------------------------- +# 14. _resolve_advisor_credentials: api_base is only honored alongside a +# caller-supplied api_key, and is SSRF-validated before use. +# --------------------------------------------------------------------------- + + +def test_resolve_advisor_credentials_returns_none_when_gate_closed(): + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + with patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=False, + ): + result = _resolve_advisor_credentials(ADVISOR_TOOL_WITH_CREDS) + assert result == (None, None) + + +def test_resolve_advisor_credentials_allows_api_key_without_api_base(): + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + tool = {**ADVISOR_TOOL, "api_key": "sk-other"} + with ( + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ), + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor.validate_url", + side_effect=AssertionError("validate_url must not run without an api_base"), + ), + ): + result = _resolve_advisor_credentials(tool) + assert result == ("sk-other", None) + + +def test_resolve_advisor_credentials_rejects_api_base_without_api_key(): + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + tool = {**ADVISOR_TOOL, "api_base": "https://other.example"} + with patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ): + with pytest.raises(ValueError, match="api_base"): + _resolve_advisor_credentials(tool) + + +def test_resolve_advisor_credentials_validates_api_base_before_use(): + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + with ( + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ), + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor.validate_url" + ) as mock_validate, + ): + result = _resolve_advisor_credentials(ADVISOR_TOOL_WITH_CREDS) + mock_validate.assert_called_once_with("https://other.example") + assert result == ("sk-other", "https://other.example") + + +def test_resolve_advisor_credentials_propagates_ssrf_error(): + from litellm.litellm_core_utils.url_utils import SSRFError + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + with ( + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ), + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor.validate_url", + side_effect=SSRFError("URL targets a blocked address"), + ), + ): + with pytest.raises(SSRFError): + _resolve_advisor_credentials(ADVISOR_TOOL_WITH_CREDS) + + +def test_resolve_advisor_credentials_skips_validation_when_url_validation_disabled(): + import litellm + + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + with ( + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ), + patch.object(litellm, "user_url_validation", False), + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor.validate_url", + side_effect=AssertionError("validate_url must not run when user_url_validation is disabled"), + ), + ): + result = _resolve_advisor_credentials(ADVISOR_TOOL_WITH_CREDS) + assert result == ("sk-other", "https://other.example") + + +def test_resolve_advisor_credentials_blocks_real_cloud_metadata_address(): + """End-to-end (no mocked validate_url): a caller can't redirect the + advisor sub-call to the cloud-metadata address even with an api_key.""" + from litellm.litellm_core_utils.url_utils import SSRFError + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + tool = { + **ADVISOR_TOOL, + "api_key": "sk-other", + "api_base": "https://169.254.169.254/latest/meta-data/", + } + with patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ): + with pytest.raises(SSRFError): + _resolve_advisor_credentials(tool) + + +def test_resolve_advisor_credentials_rejects_non_https_api_base(): + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + tool = {**ADVISOR_TOOL, "api_key": "sk-other", "api_base": "http://8.8.8.8"} + with patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ): + with pytest.raises(ValueError, match="https"): + _resolve_advisor_credentials(tool) + + +def test_resolve_advisor_credentials_rejects_api_base_when_ssl_verify_disabled(): + import litellm + + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + tool = {**ADVISOR_TOOL, "api_key": "sk-other", "api_base": "https://8.8.8.8"} + with ( + patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ), + patch.object(litellm, "ssl_verify", False), + ): + with pytest.raises(ValueError, match="ssl_verify"): + _resolve_advisor_credentials(tool) + + +def test_resolve_advisor_credentials_allows_real_public_ip_address(): + """End-to-end (no mocked validate_url): a globally-routable literal IP + api_base is honored when paired with an api_key.""" + from litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor import ( + _resolve_advisor_credentials, + ) + + tool = {**ADVISOR_TOOL, "api_key": "sk-other", "api_base": "https://8.8.8.8"} + with patch( + "litellm.llms.anthropic.experimental_pass_through.messages.interceptors.advisor._allow_client_side_advisor_credentials", + return_value=True, + ): + result = _resolve_advisor_credentials(tool) + assert result == ("sk-other", "https://8.8.8.8") diff --git a/tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py b/tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py index a2fbb68bbb9..3c410cf84df 100644 --- a/tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py +++ b/tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py @@ -772,7 +772,6 @@ class TestProxyOAuthHeaderForwarding: self, ): """OAuth Authorization header IS forwarded when x-litellm-api-key was used for proxy auth.""" - from unittest.mock import patch from starlette.datastructures import Headers @@ -1579,7 +1578,7 @@ class TestClaudeOpus48AdaptiveThinking: def test_adaptive_thinking_detected_for_opus_4_8(self, local_model_cost_map, model): from litellm.llms.anthropic.common_utils import AnthropicModelInfo - assert AnthropicModelInfo._is_adaptive_thinking_model(model) is True + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is True def test_resolver_reads_flag_through_bedrock_invoke_prefix( self, local_model_cost_map @@ -1593,6 +1592,7 @@ class TestClaudeOpus48AdaptiveThinking: AnthropicModelInfo._supports_model_capability( "bedrock/invoke/us.anthropic.claude-opus-4-8", "supports_adaptive_thinking", + "anthropic", ) is True ) @@ -1610,7 +1610,7 @@ class TestClaudeOpus48AdaptiveThinking: def test_adaptive_thinking_detected_for_fable_5(self, local_model_cost_map, model): from litellm.llms.anthropic.common_utils import AnthropicModelInfo - assert AnthropicModelInfo._is_adaptive_thinking_model(model) is True + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is True @pytest.mark.parametrize( "model", @@ -1645,27 +1645,28 @@ class TestClaudeOpus48AdaptiveThinking: version (``4.6`` -> ``4-6``).""" from litellm.llms.anthropic.common_utils import AnthropicModelInfo - assert AnthropicModelInfo._is_adaptive_thinking_model(model) is True + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is True @pytest.mark.parametrize( "model", [ - "us.anthropic.claude-fable-5-preview", - "claude-fable-5-preview", + "us.anthropic.claude-fable-preview", + "claude-fable-preview", ], ) def test_unmapped_aliases_without_parseable_version_stay_non_adaptive( self, local_model_cost_map, model ): """An alias absent from the map, not matched by any ``fallback_generalizations`` - rule, and without a parseable opus/sonnet/haiku >= 4.6 family version stays - non-adaptive. ``fable`` is outside the version-rule family set, so neither the - cost map nor the declarative rule marks it adaptive.""" + rule, and without any parseable family version stays non-adaptive. ``fable`` + without a major version matches neither the core-family 4.6+ gate nor the + family-agnostic 5+ gate, so neither the cost map nor the declarative rule marks + it adaptive.""" import litellm from litellm.llms.anthropic.common_utils import AnthropicModelInfo assert model not in litellm.model_cost - assert AnthropicModelInfo._is_adaptive_thinking_model(model) is False + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is False @pytest.mark.parametrize( "model", @@ -1677,21 +1678,23 @@ class TestClaudeOpus48AdaptiveThinking: "claude-opus-5-0", "claude-opus-4-10", "claude-opus-4-8-some-future-suffix", + "claude-fable-5-preview", + "us.anthropic.claude-fable-5-preview", ], ) def test_adaptive_thinking_version_fallback_for_unmapped_high_versions( self, local_model_cost_map, model ): - """Provider-prefixed or suffixed Claude names that resolve to no mapped entry and - are not matched by the anchored ``anthropic-claude`` pricing rule still resolve to - adaptive when their opus/sonnet/haiku family version is >= 4.6. The version gate is - the declarative ``anthropic-claude-adaptive-thinking`` rule, so 5.x, 6.x and any - later family are covered with no code change.""" + """Provider-prefixed or suffixed Claude names that resolve to no mapped entry + still resolve to adaptive when the id carries claude-- at version 4.6 + or higher, bare 5+ majors included. The version gate is the declarative + ``claude-adaptive-thinking`` rule, so 5.x, 6.x and any later family are covered + with no code change.""" import litellm from litellm.llms.anthropic.common_utils import AnthropicModelInfo assert model not in litellm.model_cost - assert AnthropicModelInfo._is_adaptive_thinking_model(model) is True + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is True @pytest.mark.parametrize( "model", @@ -1714,7 +1717,7 @@ class TestClaudeOpus48AdaptiveThinking: from litellm.llms.anthropic.common_utils import AnthropicModelInfo assert model not in litellm.model_cost - assert AnthropicModelInfo._is_adaptive_thinking_model(model) is False + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is False @pytest.mark.parametrize( "model", @@ -1723,4 +1726,97 @@ class TestClaudeOpus48AdaptiveThinking: def test_non_adaptive_models_not_detected(self, local_model_cost_map, model): from litellm.llms.anthropic.common_utils import AnthropicModelInfo - assert AnthropicModelInfo._is_adaptive_thinking_model(model) is False + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is False + + +class TestDefaultSuffixAdaptiveThinking: + """@default-suffixed Vertex AI model names (e.g. vertex_ai/claude-opus-4-8@default) + must resolve as adaptive thinking. Before the fix, _model_map_lookup_candidates + never stripped the @default suffix, so the lookup fell through to the bare + model name without @default, which may or may not have the flag, and for + provider-prefixed forms the lookup always missed (issue #31760).""" + + @pytest.mark.parametrize( + "model", + [ + "vertex_ai/claude-opus-4-8@default", + "vertex_ai/claude-sonnet-4-6@default", + "vertex_ai/claude-opus-4-7@default", + "vertex_ai/claude-opus-4-6@default", + "vertex_ai/claude-fable-5@default", + ], + ) + def test_default_suffix_models_are_adaptive_thinking( + self, local_model_cost_map, model: str + ) -> None: + from litellm.llms.anthropic.common_utils import AnthropicModelInfo + + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is True, ( + f"{model} not classified as adaptive thinking. " + "Check _model_map_lookup_candidates strips @default suffix." + ) + + @pytest.mark.parametrize( + "model,expected_bare", + [ + ("vertex_ai/claude-opus-4-8@default", "claude-opus-4-8"), + ("vertex_ai/claude-sonnet-4-6@default", "claude-sonnet-4-6"), + ], + ) + def test_lookup_candidates_include_bare_name( + self, model: str, expected_bare: str + ) -> None: + from litellm.llms.anthropic.common_utils import AnthropicModelInfo + + candidates = AnthropicModelInfo._model_map_lookup_candidates(model) + assert expected_bare in candidates, ( + f"Expected '{expected_bare}' in candidates for '{model}', got: {candidates}" + ) + + +class TestCapabilityProbeUsesCallerProvider: + """``_supports_model_capability`` must probe under the caller's real provider + namespace instead of a pinned ``"anthropic"``. With the pin, the exact Bedrock + cost-map entry for ``global.anthropic.claude-opus-4-8`` was rejected by the + provider match and the anthropic-scoped fallback rule answered instead, so + flipping ``supports_adaptive_thinking`` on the exact entry changed nothing and + the documented "exact entry beats rule" precedence was silently violated.""" + + BEDROCK_MODEL = "global.anthropic.claude-opus-4-8" + + def test_exact_bedrock_entry_flag_is_authoritative_for_bedrock_caller( + self, local_model_cost_map, monkeypatch + ): + import litellm + from litellm.llms.anthropic.common_utils import AnthropicModelInfo + + assert ( + AnthropicModelInfo._is_adaptive_thinking_model(self.BEDROCK_MODEL, "bedrock") + is True + ) + + monkeypatch.setitem( + litellm.model_cost[self.BEDROCK_MODEL], "supports_adaptive_thinking", False + ) + litellm.get_model_info.cache_clear() + + assert ( + AnthropicModelInfo._is_adaptive_thinking_model(self.BEDROCK_MODEL, "bedrock") + is False + ) + + def test_native_anthropic_probe_still_reads_anthropic_entry( + self, local_model_cost_map, monkeypatch + ): + import litellm + from litellm.llms.anthropic.common_utils import AnthropicModelInfo + + monkeypatch.setitem( + litellm.model_cost[self.BEDROCK_MODEL], "supports_adaptive_thinking", False + ) + litellm.get_model_info.cache_clear() + + assert ( + AnthropicModelInfo._is_adaptive_thinking_model("claude-opus-4-8", "anthropic") + is True + ) diff --git a/tests/test_litellm/llms/azure/response/test_azure_transformation.py b/tests/test_litellm/llms/azure/response/test_azure_transformation.py index a4bd14d69ff..24ae563fb76 100644 --- a/tests/test_litellm/llms/azure/response/test_azure_transformation.py +++ b/tests/test_litellm/llms/azure/response/test_azure_transformation.py @@ -337,6 +337,24 @@ class TestAzureResponsesAPIConfig: assert url == expected_url assert data == {} + def test_azure_list_input_items_request_url_path_before_query(self): + from litellm.types.router import GenericLiteLLMParams + + api_base = "https://test.openai.azure.com/openai/responses?api-version=2025-03-01-preview" + + url, params = self.config.transform_list_input_items_request( + response_id="resp_test123", + api_base=api_base, + litellm_params=GenericLiteLLMParams(api_version="2025-03-01-preview"), + headers={}, + ) + + assert ( + url + == "https://test.openai.azure.com/openai/responses/resp_test123/input_items?api-version=2025-03-01-preview" + ) + assert params == {"limit": 20, "order": "desc"} + def test_azure_cancel_response_api_response(self): """Test Azure cancel response API response transformation""" from unittest.mock import Mock diff --git a/tests/test_litellm/llms/azure_ai/claude/test_azure_anthropic_messages_transformation.py b/tests/test_litellm/llms/azure_ai/claude/test_azure_anthropic_messages_transformation.py index 5983597196a..5e9af6bd34d 100644 --- a/tests/test_litellm/llms/azure_ai/claude/test_azure_anthropic_messages_transformation.py +++ b/tests/test_litellm/llms/azure_ai/claude/test_azure_anthropic_messages_transformation.py @@ -331,3 +331,59 @@ class TestProviderConfigManagerAzureAnthropicMessages: ) assert config is None + + +@pytest.fixture +def local_model_cost_map(monkeypatch): + """Force the bundled backup cost map so capability flags match this branch.""" + import litellm + + original = litellm.model_cost + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + litellm.model_cost = litellm.get_model_cost_map(url="") + litellm.get_model_info.cache_clear() + try: + yield + finally: + litellm.model_cost = original + litellm.get_model_info.cache_clear() + + +def test_messages_thinking_shape_follows_exact_azure_entry_flag(local_model_cost_map, monkeypatch): + """The Azure messages config must probe capabilities under ``azure_ai`` so an + operator setting ``supports_adaptive_thinking: false`` on the exact + ``azure_ai/claude-opus-4-8`` entry beats the unmodified ``anthropic`` entry. + With the inherited ``"anthropic"`` provider default the flip was ignored and + the transform kept emitting ``thinking.type='adaptive'``.""" + import litellm + + config = AzureAnthropicMessagesConfig() + + def transform(): + return config.transform_anthropic_messages_request( + model="claude-opus-4-8", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_optional_request_params={ + "max_tokens": 4096, + "reasoning_effort": "medium", + }, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + result = transform() + assert result.get("thinking") == {"type": "adaptive"} + assert result.get("output_config") == {"effort": "medium"} + + monkeypatch.setitem( + litellm.model_cost["azure_ai/claude-opus-4-8"], "supports_adaptive_thinking", False + ) + litellm.get_model_info.cache_clear() + assert litellm.model_cost["claude-opus-4-8"]["supports_adaptive_thinking"] is True + + flipped = transform() + thinking = flipped.get("thinking") + assert isinstance(thinking, dict) + assert thinking.get("type") == "enabled" + assert isinstance(thinking.get("budget_tokens"), int) + assert "output_config" not in flipped diff --git a/tests/test_litellm/llms/azure_ai/test_azure_document_intelligence_ocr_transformation.py b/tests/test_litellm/llms/azure_ai/test_azure_document_intelligence_ocr_transformation.py index e638be68ec0..39d6f1dc355 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_document_intelligence_ocr_transformation.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_document_intelligence_ocr_transformation.py @@ -1,3 +1,6 @@ +from unittest.mock import MagicMock + +import httpx import pytest from litellm.llms.azure_ai.ocr.document_intelligence.transformation import ( @@ -31,3 +34,217 @@ def test_should_reject_dot_segment_azure_document_intelligence_model_id(): optional_params={}, litellm_params={}, ) + + +AZURE_TABLES = [ + { + "rowCount": 2, + "columnCount": 2, + "cells": [ + {"kind": "columnHeader", "rowIndex": 0, "columnIndex": 0, "content": "Item"}, + {"kind": "columnHeader", "rowIndex": 0, "columnIndex": 1, "content": "Price"}, + {"rowIndex": 1, "columnIndex": 0, "content": "Widget"}, + {"rowIndex": 1, "columnIndex": 1, "content": "$100.00"}, + ], + }, + { + "rowCount": 1, + "columnCount": 1, + "cells": [{"rowIndex": 0, "columnIndex": 0, "content": "Totals"}], + }, +] + +AZURE_KEY_VALUE_PAIRS = [ + {"key": {"content": "Invoice No"}, "value": {"content": "INV-12345"}, "confidence": 0.98}, + {"key": {"content": "Total"}, "value": {"content": "$100.00"}, "confidence": 0.95}, +] + +AZURE_ANALYZE_SUCCEEDED = { + "status": "succeeded", + "createdDateTime": "2026-07-02T00:00:00Z", + "lastUpdatedDateTime": "2026-07-02T00:00:05Z", + "analyzeResult": { + "apiVersion": "2024-11-30", + "modelId": "prebuilt-layout", + "content": "Invoice\nInvoice No: INV-12345\nTotal: $100.00", + "pages": [ + { + "pageNumber": 1, + "width": 8.5, + "height": 11, + "unit": "inch", + "lines": [ + {"content": "Invoice"}, + {"content": "Invoice No: INV-12345"}, + {"content": "Total: $100.00"}, + ], + } + ], + "tables": AZURE_TABLES, + "keyValuePairs": AZURE_KEY_VALUE_PAIRS, + }, +} + + +def _completed_response(payload: dict) -> httpx.Response: + return httpx.Response( + status_code=200, + json=payload, + request=httpx.Request("GET", "https://example.cognitiveservices.azure.com/analyzeResults/xyz"), + ) + + +def _assert_native_fields_preserved(serialized: dict) -> None: + assert serialized["content"] == "Invoice\nInvoice No: INV-12345\nTotal: $100.00" + assert serialized["tables"] == AZURE_TABLES + assert serialized["keyValuePairs"] == AZURE_KEY_VALUE_PAIRS + assert serialized["object"] == "ocr" + assert serialized["usage_info"]["pages_processed"] == 1 + assert serialized["pages"][0]["index"] == 0 + assert serialized["pages"][0]["markdown"] == "Invoice\nInvoice No: INV-12345\nTotal: $100.00" + assert serialized["pages"][0]["dimensions"] == {"width": 816, "height": 1056, "dpi": 96} + + +def test_transform_ocr_response_preserves_azure_native_fields(): + config = AzureDocumentIntelligenceOCRConfig() + + result = config.transform_ocr_response( + model="azure_ai/doc-intelligence/prebuilt-layout", + raw_response=_completed_response(AZURE_ANALYZE_SUCCEEDED), + logging_obj=MagicMock(), + ) + + _assert_native_fields_preserved(result.model_dump()) + + +@pytest.mark.asyncio +async def test_async_transform_ocr_response_preserves_azure_native_fields(): + config = AzureDocumentIntelligenceOCRConfig() + + result = await config.async_transform_ocr_response( + model="azure_ai/doc-intelligence/prebuilt-layout", + raw_response=_completed_response(AZURE_ANALYZE_SUCCEEDED), + logging_obj=MagicMock(), + ) + + _assert_native_fields_preserved(result.model_dump()) + + +def test_transform_ocr_response_tolerates_missing_native_fields(): + config = AzureDocumentIntelligenceOCRConfig() + payload = { + "status": "succeeded", + "analyzeResult": { + "pages": [ + { + "pageNumber": 1, + "width": 8.5, + "height": 11, + "unit": "inch", + "lines": [{"content": "hello"}], + } + ], + }, + } + + result = config.transform_ocr_response( + model="azure_ai/doc-intelligence/prebuilt-read", + raw_response=_completed_response(payload), + logging_obj=MagicMock(), + ) + + serialized = result.model_dump() + assert serialized["pages"][0]["markdown"] == "hello" + assert serialized["content"] is None + assert serialized["tables"] is None + assert serialized["keyValuePairs"] is None + + +def test_transform_ocr_response_non_succeeded_status_raises(): + config = AzureDocumentIntelligenceOCRConfig() + + with pytest.raises(ValueError, match="failed with status: failed"): + config.transform_ocr_response( + model="azure_ai/doc-intelligence/prebuilt-layout", + raw_response=_completed_response({"status": "failed"}), + logging_obj=MagicMock(), + ) + + +def test_get_supported_ocr_params_includes_features(): + config = AzureDocumentIntelligenceOCRConfig() + + assert config.get_supported_ocr_params("prebuilt-layout") == ["pages", "features"] + + +@pytest.mark.parametrize( + "features,expected", + [ + (["keyValuePairs"], "keyValuePairs"), + (["keyValuePairs", "languages"], "keyValuePairs,languages"), + ("keyValuePairs", "keyValuePairs"), + ("keyValuePairs,languages", "keyValuePairs,languages"), + ("keyValuePairs, languages", "keyValuePairs,languages"), + ], +) +def test_map_ocr_params_features(features, expected): + config = AzureDocumentIntelligenceOCRConfig() + + mapped = config.map_ocr_params({"features": features}, {}, "prebuilt-layout") + + assert mapped == {"features": expected} + + +def test_map_ocr_params_empty_features_list_omitted(): + config = AzureDocumentIntelligenceOCRConfig() + + assert config.map_ocr_params({"features": []}, {}, "prebuilt-layout") == {} + + +@pytest.mark.parametrize( + "features", + [ + "keyValuePairs&pages=9", + "key value pairs", + "", + [1, 2], + [["keyValuePairs"]], + {"feature": "keyValuePairs"}, + 5, + ], +) +def test_map_ocr_params_invalid_features_raises(features): + config = AzureDocumentIntelligenceOCRConfig() + + with pytest.raises(ValueError, match="Invalid `features`"): + config.map_ocr_params({"features": features}, {}, "prebuilt-layout") + + +def test_get_complete_url_appends_features_query(): + config = AzureDocumentIntelligenceOCRConfig() + + url = config.get_complete_url( + api_base="https://example.cognitiveservices.azure.com", + model="azure_ai/doc-intelligence/prebuilt-layout", + optional_params={"features": "keyValuePairs"}, + ) + + assert "&features=keyValuePairs" in url + + +def test_get_complete_url_combines_pages_and_features(): + config = AzureDocumentIntelligenceOCRConfig() + + optional_params = config.map_ocr_params( + {"pages": [0, 1, 2], "features": ["keyValuePairs", "languages"]}, + {}, + "prebuilt-layout", + ) + url = config.get_complete_url( + api_base="https://example.cognitiveservices.azure.com", + model="prebuilt-layout", + optional_params=optional_params, + ) + + assert "&pages=1,2,3" in url + assert "&features=keyValuePairs,languages" in url diff --git a/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_base_invoke_transformation.py b/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_base_invoke_transformation.py index aff89f02ff2..5fefae7e411 100644 --- a/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_base_invoke_transformation.py +++ b/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_base_invoke_transformation.py @@ -14,6 +14,7 @@ from litellm.llms.bedrock.chat.invoke_transformations.anthropic_claude3_transfor from litellm.llms.bedrock.chat.invoke_transformations.base_invoke_transformation import ( AmazonInvokeConfig, ) +from litellm.llms.bedrock.common_utils import BedrockError @pytest.mark.parametrize( @@ -39,3 +40,145 @@ def test_transform_request_drops_stream_chunk_size(config, model): ) assert "stream_chunk_size" not in json.dumps(request_body) + + +def test_validate_environment_maps_guardrail_config_to_invoke_headers(): + """The InvokeModel API takes the guardrail identifier/version/trace as + X-Amzn-Bedrock-* request headers, unlike Converse which takes them in the + body. https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModel.html""" + optional_params = { + "guardrailConfig": { + "guardrailIdentifier": "ff6ujrregl1q", + "guardrailVersion": "DRAFT", + "trace": "enabled", + }, + "max_tokens": 10, + } + + headers = AmazonInvokeConfig().validate_environment( + headers={}, + model="anthropic.claude-3-sonnet-20240229-v1:0", + messages=[{"role": "user", "content": "hi"}], + optional_params=optional_params, + litellm_params={}, + ) + + assert headers["X-Amzn-Bedrock-GuardrailIdentifier"] == "ff6ujrregl1q" + assert headers["X-Amzn-Bedrock-GuardrailVersion"] == "DRAFT" + assert headers["X-Amzn-Bedrock-Trace"] == "ENABLED" + assert "guardrailConfig" not in optional_params + + +def test_validate_environment_without_guardrail_config_leaves_headers_untouched(): + headers = AmazonInvokeConfig().validate_environment( + headers={"foo": "bar"}, + model="anthropic.claude-3-sonnet-20240229-v1:0", + messages=[{"role": "user", "content": "hi"}], + optional_params={"max_tokens": 10}, + litellm_params={}, + ) + + assert headers == {"foo": "bar"} + + +def test_validate_environment_skips_absent_guardrail_fields(): + headers = AmazonInvokeConfig().validate_environment( + headers={}, + model="amazon.titan-text-express-v1", + messages=[{"role": "user", "content": "hi"}], + optional_params={"guardrailConfig": {"guardrailIdentifier": "gr-id", "guardrailVersion": "1"}}, + litellm_params={}, + ) + + assert headers == { + "X-Amzn-Bedrock-GuardrailIdentifier": "gr-id", + "X-Amzn-Bedrock-GuardrailVersion": "1", + } + + +def test_validate_environment_does_not_clobber_explicit_guardrail_headers(): + """Users worked around the missing guardrailConfig support by passing the + AWS headers directly; an explicit header must keep winning over + guardrailConfig regardless of casing.""" + headers = AmazonInvokeConfig().validate_environment( + headers={"x-amzn-bedrock-guardrailidentifier": "explicit-id"}, + model="anthropic.claude-3-sonnet-20240229-v1:0", + messages=[{"role": "user", "content": "hi"}], + optional_params={ + "guardrailConfig": {"guardrailIdentifier": "config-id", "guardrailVersion": "2"}, + }, + litellm_params={}, + ) + + assert headers["x-amzn-bedrock-guardrailidentifier"] == "explicit-id" + assert "X-Amzn-Bedrock-GuardrailIdentifier" not in headers + assert headers["X-Amzn-Bedrock-GuardrailVersion"] == "2" + + +@pytest.mark.parametrize( + "bad_guardrail_config", + [ + {"guardrailIdentifier": "gr-id", "trace": "verbose"}, + {"guardrailIdentifier": ["gr-id"]}, + "gr-id", + {}, + {"trace": "enabled"}, + ], +) +def test_validate_environment_rejects_malformed_guardrail_config(bad_guardrail_config): + with pytest.raises(BedrockError) as excinfo: + AmazonInvokeConfig().validate_environment( + headers={}, + model="anthropic.claude-3-sonnet-20240229-v1:0", + messages=[{"role": "user", "content": "hi"}], + optional_params={"guardrailConfig": bad_guardrail_config}, + litellm_params={}, + ) + + assert excinfo.value.status_code == 400 + assert "guardrailConfig" in str(excinfo.value) + + +@pytest.mark.parametrize( + "model", + [ + "anthropic.claude-3-sonnet-20240229-v1:0", + "amazon.titan-text-express-v1", + "mistral.mistral-7b-instruct-v0:2", + "meta.llama3-8b-instruct-v1:0", + ], +) +def test_guardrail_config_flows_to_headers_not_request_body(model): + """Mirrors the handler flow (validate_environment then transform_request): + guardrailConfig must end up in the signed headers and never leak into the + request body, where Bedrock rejects it as an extra input.""" + config = AmazonInvokeConfig() + optional_params = { + "guardrailConfig": { + "guardrailIdentifier": "ff6ujrregl1q", + "guardrailVersion": "DRAFT", + "trace": "disabled", + }, + "max_tokens": 10, + } + messages = [{"role": "user", "content": "hi"}] + + headers = config.validate_environment( + headers={}, + model=model, + messages=messages, + optional_params=optional_params, + litellm_params={}, + ) + request_body = config.transform_request( + model=model, + messages=messages, + optional_params=optional_params, + litellm_params={}, + headers=headers, + ) + + assert "guardrailConfig" not in json.dumps(request_body) + assert headers["X-Amzn-Bedrock-GuardrailIdentifier"] == "ff6ujrregl1q" + assert headers["X-Amzn-Bedrock-GuardrailVersion"] == "DRAFT" + assert headers["X-Amzn-Bedrock-Trace"] == "DISABLED" diff --git a/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py b/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py index d940f9f47a6..fc12ead36a1 100644 --- a/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py +++ b/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py @@ -611,6 +611,65 @@ def test_transform_request_helper_includes_anthropic_beta_and_tools(): assert fields["tools"][0]["type"] == "computer_20250124" +def test_parallel_tool_calls_config_kept_for_sonnet_5(): + old_env = os.environ.get("LITELLM_LOCAL_MODEL_COST_MAP") + old_cost = litellm.model_cost + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + try: + config = AmazonConverseConfig() + optional_params = config.map_openai_params( + model="anthropic.claude-sonnet-5", + non_default_params={"parallel_tool_calls": False}, + optional_params={}, + drop_params=False, + ) + + data = config._transform_request_helper( + model="anthropic.claude-sonnet-5", + system_content_blocks=[], + optional_params=optional_params, + messages=None, + ) + + assert data["additionalModelRequestFields"]["tool_choice"] == { + "disable_parallel_tool_use": True + } + finally: + litellm.model_cost = old_cost + if old_env is None: + os.environ.pop("LITELLM_LOCAL_MODEL_COST_MAP", None) + else: + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = old_env + + +def test_parallel_tool_calls_config_dropped_for_ttl_only_model( + monkeypatch: pytest.MonkeyPatch, +): + model = "anthropic.claude-fable-5" + monkeypatch.setitem( + litellm.model_cost, + model, + {"cache_creation_input_token_cost_above_1hr": 2e-05}, + ) + config = AmazonConverseConfig() + optional_params = config.map_openai_params( + model=model, + non_default_params={"parallel_tool_calls": False}, + optional_params={}, + drop_params=False, + ) + + data = config._transform_request_helper( + model=model, + system_content_blocks=[], + optional_params=optional_params, + messages=None, + ) + + assert "tool_choice" not in data.get("additionalModelRequestFields", {}) + + def test_transform_response_with_computer_use_tool(): """Test response transformation with computer use tool call.""" import httpx @@ -4130,6 +4189,42 @@ def test_parallel_tool_calls_newer_model_adds_disable_flag(): assert "parallel_tool_calls" not in request_data["additionalModelRequestFields"] +def test_parallel_tool_calls_flag_decoupled_from_ttl_pricing(monkeypatch): + """ + The disable_parallel_tool_use gate must read supports_parallel_tool_use_config, + not the 1h-TTL pricing field: a model carrying only the former still gets the flag. + """ + from litellm.llms.bedrock.common_utils import is_claude_4_5_on_bedrock + + config = AmazonConverseConfig() + model = "anthropic.claude-parallel-tool-use-only" + monkeypatch.setitem(litellm.model_cost, model, {"supports_parallel_tool_use_config": True}) + assert is_claude_4_5_on_bedrock(model) is False + messages = [{"role": "user", "content": "What's the weather in SF and NYC?"}] + + optional_params = config.map_openai_params( + non_default_params={"parallel_tool_calls": False, "tools": _TOOL_PARAM}, + optional_params={}, + model=model, + drop_params=False, + ) + + request_data = config.transform_request( + model=model, + messages=messages, + optional_params=optional_params, + litellm_params={}, + headers={}, + ) + + assert ( + request_data["additionalModelRequestFields"]["tool_choice"][ + "disable_parallel_tool_use" + ] + is True + ) + + def test_parallel_tool_calls_older_model_drops_disable_flag(): """Older Claude models (pre-4.5) must NOT receive disable_parallel_tool_use — Bedrock rejects it.""" config = AmazonConverseConfig() @@ -4554,6 +4649,154 @@ def test_cache_control_injection_tool_config_not_added_without_injection_point() assert all("cachePoint" not in tool for tool in tools) +def test_cache_control_injection_tool_config_honors_ttl_for_supported_model(): + """ + Regression test: cache_control_injection_points with location=tool_config + must honor the requested `control.ttl`, mirroring the message/system + cache_control behavior, instead of always emitting a bare + {"type": "default"} cachePoint with no ttl. + + Forces the bundled local cost map so `is_claude_4_5_on_bedrock` (which + reads `cache_creation_input_token_cost_above_1hr` from litellm.model_cost) + sees this branch's pricing data rather than the network-fetched `main` + copy, which lacks it until merge. + """ + old_env = os.environ.get("LITELLM_LOCAL_MODEL_COST_MAP") + old_cost = litellm.model_cost + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + try: + config = AmazonConverseConfig() + messages = [ + {"role": "user", "content": "What is the weather?"}, + ] + optional_params = { + "tools": [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather for a location", + "parameters": { + "type": "object", + "properties": {"location": {"type": "string"}}, + "required": ["location"], + }, + }, + } + ], + "cache_control_injection_points": [ + {"location": "tool_config", "control": {"type": "ephemeral", "ttl": "1h"}}, + ], + } + result = config._transform_request( + model="us.anthropic.claude-sonnet-4-5-20250929-v1:0", + messages=messages, + optional_params=optional_params, + litellm_params={}, + ) + tools = result["toolConfig"]["tools"] + assert tools[-1] == {"cachePoint": {"type": "default", "ttl": "1h"}} + finally: + litellm.model_cost = old_cost + if old_env is None: + os.environ.pop("LITELLM_LOCAL_MODEL_COST_MAP", None) + else: + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = old_env + + +def test_cache_control_injection_tool_config_honors_ttl_for_regional_model_lacking_own_pricing(): + """ + Regression test: a regional pricing entry that omits + `cache_creation_input_token_cost_above_1hr` (e.g. `jp.anthropic.claude-opus-4-7`) + must not shadow the base model entry that carries it; the requested ttl + survives through the base-model fallback. + """ + old_env = os.environ.get("LITELLM_LOCAL_MODEL_COST_MAP") + old_cost = litellm.model_cost + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + try: + assert "cache_creation_input_token_cost_above_1hr" not in litellm.model_cost["jp.anthropic.claude-opus-4-7"] + assert "cache_creation_input_token_cost_above_1hr" in litellm.model_cost["anthropic.claude-opus-4-7"] + config = AmazonConverseConfig() + messages = [ + {"role": "user", "content": "What is the weather?"}, + ] + optional_params = { + "tools": [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather for a location", + "parameters": { + "type": "object", + "properties": {"location": {"type": "string"}}, + "required": ["location"], + }, + }, + } + ], + "cache_control_injection_points": [ + {"location": "tool_config", "control": {"type": "ephemeral", "ttl": "1h"}}, + ], + } + result = config._transform_request( + model="jp.anthropic.claude-opus-4-7", + messages=messages, + optional_params=optional_params, + litellm_params={}, + ) + tools = result["toolConfig"]["tools"] + assert tools[-1] == {"cachePoint": {"type": "default", "ttl": "1h"}} + finally: + litellm.model_cost = old_cost + if old_env is None: + os.environ.pop("LITELLM_LOCAL_MODEL_COST_MAP", None) + else: + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = old_env + + +def test_cache_control_injection_tool_config_drops_ttl_for_unsupported_model(): + """ + Models that don't support extended TTL caching (only Claude 4.5+ on + Bedrock does) must fall back to the default cachePoint with no ttl, + even if the caller requested one, matching message/system behavior. + """ + config = AmazonConverseConfig() + messages = [ + {"role": "user", "content": "What is the weather?"}, + ] + optional_params = { + "tools": [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather for a location", + "parameters": { + "type": "object", + "properties": {"location": {"type": "string"}}, + "required": ["location"], + }, + }, + } + ], + "cache_control_injection_points": [ + {"location": "tool_config", "control": {"type": "ephemeral", "ttl": "1h"}}, + ], + } + result = config._transform_request( + model="anthropic.claude-3-5-haiku-20241022-v1:0", + messages=messages, + optional_params=optional_params, + litellm_params={}, + ) + tools = result["toolConfig"]["tools"] + assert tools[-1] == {"cachePoint": {"type": "default"}} + + def test_translate_response_format_json_schema_still_injects_tool(): """ response_format with an explicit json_schema should still use the @@ -5428,3 +5671,169 @@ async def test_grounding_source_and_query_rendered_as_text(): user_content = result[0]["content"] assert {"text": "Tokyo is the capital of Japan."} in user_content assert {"text": "What is the capital of Japan?"} in user_content + + +def _agentic_messages_with_ttl(ttl_target: str): + """A tool-loop conversation with `ttl: 1h` cache_control at `ttl_target`: + 'user', 'tool_call' (per-tool-call, on the assistant's tool call), or + 'tool' (message-level, on the tool result - where + `cache_control_injection_points` with `index: -1` lands mid-loop). + + Message-level cache_control on a content-less assistant message emits no + cachePoint at all today (a separate gap, orthogonal to ttl); per-tool-call + placement covers that message, so it's excluded from the params below.""" + user: dict = {"role": "user", "content": "optimize this kernel " * 60} + assistant: dict = { + "role": "assistant", + "content": None, + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": {"name": "evaluate", "arguments": "{}"}, + } + ], + } + tool: dict = {"role": "tool", "tool_call_id": "call_1", "content": "score: 42"} + ttl_cc = {"type": "ephemeral", "ttl": "1h"} + if ttl_target == "user": + user["cache_control"] = ttl_cc + elif ttl_target == "tool_call": + assistant["tool_calls"][0]["cache_control"] = ttl_cc + elif ttl_target == "tool": + tool["cache_control"] = ttl_cc + return [user, assistant, tool] + + +def _collect_cache_points(result): + return [ + block["cachePoint"] + for message in result + for block in message.get("content") or [] + if "cachePoint" in block + ] + + +@pytest.mark.parametrize("ttl_target", ["user", "tool_call", "tool"]) +@pytest.mark.asyncio +async def test_message_level_cache_control_honors_ttl_for_supported_model( + ttl_target, +): + """Message- and tool-call-level cache_control must carry `ttl` onto the + emitted cachePoint for models that support extended caching, mirroring the + system-message path. Regression test for the gap left by the system-only + fix: the message paths called `_get_cache_point_block` without `model` (or + hardcoded `{"type": "default"}`), silently downgrading 1h to 5m.""" + from litellm.litellm_core_utils.prompt_templates.factory import ( + BedrockConverseMessagesProcessor, + _bedrock_converse_messages_pt, + ) + + messages = _agentic_messages_with_ttl(ttl_target) + + result = _bedrock_converse_messages_pt( + messages=messages, + model="global.anthropic.claude-opus-4-7", + llm_provider="bedrock_converse", + ) + async_result = ( + await BedrockConverseMessagesProcessor._bedrock_converse_messages_pt_async( + messages=messages, + model="global.anthropic.claude-opus-4-7", + llm_provider="bedrock_converse", + ) + ) + assert result == async_result + + cache_points = _collect_cache_points(result) + assert len(cache_points) == 1 + assert cache_points[0].get("ttl") == "1h" + + +@pytest.mark.parametrize("ttl_target", ["user", "tool_call", "tool"]) +def test_message_level_cache_control_drops_ttl_for_unsupported_model(ttl_target): + """Models outside the extended-caching allow-list must keep emitting the + plain `{"type": "default"}` cachePoint (Bedrock rejects `ttl` for them).""" + from litellm.litellm_core_utils.prompt_templates.factory import ( + _bedrock_converse_messages_pt, + ) + + result = _bedrock_converse_messages_pt( + messages=_agentic_messages_with_ttl(ttl_target), + model="anthropic.claude-3-5-sonnet-20240620-v1:0", + llm_provider="bedrock_converse", + ) + + cache_points = _collect_cache_points(result) + assert len(cache_points) == 1 + assert "ttl" not in cache_points[0] + + +@pytest.mark.parametrize( + "model", + [ + "bedrock/converse/us.anthropic.claude-haiku-4-5", + "bedrock/converse/us.anthropic.claude-sonnet-4-5", + ], +) +def test_adaptive_thinking_translated_to_legacy_on_pre_46_converse(model): + """Raw thinking={type: adaptive} from callers like Claude Code must be + translated to legacy thinking={type: enabled, budget_tokens} for pre-4.6 + models on Bedrock Converse rather than forwarded as-is and rejected.""" + config = AmazonConverseConfig() + + optional_params = config.map_openai_params( + non_default_params={"thinking": {"type": "adaptive"}, "max_tokens": 8192}, + optional_params={}, + model=model, + drop_params=False, + ) + + thinking = optional_params.get("thinking") + assert thinking is not None + assert thinking["type"] == "enabled" + assert isinstance(thinking.get("budget_tokens"), int) + assert thinking["budget_tokens"] < 8192 + + +@pytest.mark.parametrize( + "model", + [ + "bedrock/converse/us.anthropic.claude-opus-4-7", + "bedrock/converse/us.anthropic.claude-sonnet-4-6", + ], +) +def test_adaptive_thinking_passes_through_on_46_plus_converse(model): + """thinking={type: adaptive} must be forwarded unchanged for 4.6+ models + that natively support adaptive thinking.""" + config = AmazonConverseConfig() + + optional_params = config.map_openai_params( + non_default_params={"thinking": {"type": "adaptive"}, "max_tokens": 8192}, + optional_params={}, + model=model, + drop_params=False, + ) + + assert optional_params.get("thinking") == {"type": "adaptive"} + + +def test_adaptive_thinking_dropped_when_max_tokens_too_small_converse(): + """When max_tokens can't fit even the minimum thinking budget, the raw + adaptive block must be dropped entirely rather than translated, so the + Bedrock Converse request still succeeds.""" + from litellm.constants import ANTHROPIC_MIN_THINKING_BUDGET_TOKENS + + config = AmazonConverseConfig() + + optional_params = config.map_openai_params( + non_default_params={ + "thinking": {"type": "adaptive"}, + "max_tokens": ANTHROPIC_MIN_THINKING_BUDGET_TOKENS, + }, + optional_params={}, + model="bedrock/converse/us.anthropic.claude-sonnet-4-5", + drop_params=False, + ) + + assert "thinking" not in optional_params diff --git a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py index c3191f5b5cb..3b8b4af78d9 100644 --- a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py +++ b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py @@ -90,6 +90,87 @@ async def test_bedrock_sse_wrapper_encodes_dict_chunks(): assert collected[1] == b"raw-bytes" +@pytest.mark.asyncio +async def test_bedrock_sse_wrapper_appends_error_event_when_stream_truncates_mid_tool_use(): + """ + Regression test for LIT-3724: Bedrock invoke streams that go silent + mid tool_use (no content_block_stop / message_delta / message_stop) + used to be closed as a successful SSE stream, handing clients + unterminated tool-call JSON with HTTP 200. The stream must now end + with an Anthropic-protocol `error` SSE event. + """ + cfg = AmazonAnthropicClaudeMessagesConfig() + + async def _truncated_stream(): + yield {"type": "message_start", "message": {"id": "msg_1", "usage": {"input_tokens": 3, "output_tokens": 1}}} + yield { + "type": "content_block_start", + "index": 0, + "content_block": {"type": "tool_use", "id": "tooluse_1", "name": "write", "input": {}}, + } + yield { + "type": "content_block_delta", + "index": 0, + "delta": {"type": "input_json_delta", "partial_json": '{"path": "/builder/docs/QUAL'}, + } + + collected: list[bytes] = [] + async for chunk in cfg.bedrock_sse_wrapper( + _truncated_stream(), + litellm_logging_obj=LiteLLMLoggingObj( + model="bedrock/invoke/anthropic.claude-3-sonnet-20240229-v1:0", + messages=[{"role": "user", "content": "write the file"}], + stream=True, + call_type="chat", + start_time=datetime.now(), + litellm_call_id="test_bedrock_sse_wrapper_truncated_tool_use", + function_id="test_bedrock_sse_wrapper_truncated_tool_use", + ), + request_body={}, + ): + collected.append(chunk) + + assert len(collected) == 4 + error_event = collected[-1].decode() + assert error_event.startswith("event: error\n") + error_payload = json.loads(error_event.split("data: ", 1)[1]) + assert error_payload["type"] == "error" + assert error_payload["error"]["type"] == "api_error" + + +@pytest.mark.asyncio +async def test_bedrock_sse_wrapper_no_error_event_when_stream_ends_with_message_stop(): + cfg = AmazonAnthropicClaudeMessagesConfig() + + async def _complete_stream(): + yield {"type": "message_start", "message": {"id": "msg_1", "usage": {"input_tokens": 3, "output_tokens": 1}}} + yield {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}} + yield {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "hi"}} + yield {"type": "content_block_stop", "index": 0} + yield {"type": "message_delta", "delta": {"stop_reason": "end_turn"}, "usage": {"output_tokens": 2}} + yield {"type": "message_stop"} + + collected: list[bytes] = [] + async for chunk in cfg.bedrock_sse_wrapper( + _complete_stream(), + litellm_logging_obj=LiteLLMLoggingObj( + model="bedrock/invoke/anthropic.claude-3-sonnet-20240229-v1:0", + messages=[{"role": "user", "content": "hi"}], + stream=True, + call_type="chat", + start_time=datetime.now(), + litellm_call_id="test_bedrock_sse_wrapper_complete_stream", + function_id="test_bedrock_sse_wrapper_complete_stream", + ), + request_body={}, + ): + collected.append(chunk) + + assert len(collected) == 6 + assert collected[-1].startswith(b"event: message_stop\n") + assert not any(chunk.startswith(b"event: error\n") for chunk in collected) + + @pytest.mark.asyncio async def test_bedrock_sse_wrapper_keeps_usage_in_message_start_and_message_delta(): """Regression test: usage should be available on both message_start and message_delta SSE events.""" @@ -492,7 +573,7 @@ def test_bedrock_invoke_messages_transform_converts_custom_tool_schema_type_to_o assert result["tools"][0]["type"] == "custom" -def test_remove_ttl_from_cache_control_processes_tools(): +def test_remove_ttl_from_cache_control_processes_tools(local_model_cost_map): """ Ensure _remove_ttl_from_cache_control also sanitizes cache_control on tools. @@ -538,7 +619,7 @@ def test_remove_ttl_from_cache_control_processes_tools(): assert "ttl" not in request["system"][0]["cache_control"] -def test_remove_ttl_from_cache_control_preserves_tools_ttl_for_claude_4_5(): +def test_remove_ttl_from_cache_control_preserves_tools_ttl_for_claude_4_5(local_model_cost_map): """ For Claude 4.5+ models, ttl in ["5m", "1h"] should be preserved on tools, just like it is for system and messages. @@ -564,7 +645,7 @@ def test_remove_ttl_from_cache_control_preserves_tools_ttl_for_claude_4_5(): } cfg._remove_ttl_from_cache_control( - request, model="us.anthropic.claude-sonnet-4-5-20250514-v1:0" + request, model="us.anthropic.claude-sonnet-4-5-20250929-v1:0" ) # Both tools and system should preserve ttl for Claude 4.5 @@ -1893,6 +1974,217 @@ def test_bedrock_invoke_transform_merges_list_content_system_role_into_system(): ] +@pytest.mark.parametrize( + "model", + [ + "anthropic.claude-opus-4-8", + "jp.anthropic.claude-opus-4-8", + "us.anthropic.claude-sonnet-5", + "us.anthropic.claude-fable-5", + ], +) +def test_bedrock_invoke_transform_keeps_mid_conversation_system_role_in_place(local_model_cost_map, model): + """Regression test for the Bedrock prompt-cache collapse: hoisting a + mid-conversation ``role: "system"`` message (e.g. Claude Code's + ``mid-conversation-system-2026-04-07`` reminders) into the top-level + ``system`` field mutates the cache prefix and invalidates the cached message + history, so on models flagged ``supports_mid_conversation_system`` (Claude + 4.8+, which Invoke accepts the role on) such entries must be forwarded + in place. Billing-header blocks must still be stripped from the top-level + ``system`` field even when nothing is hoisted.""" + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [ + {"role": "user", "content": "read the file"}, + {"role": "system", "content": "[Truncated: PARTIAL view of big1.txt]"}, + {"role": "assistant", "content": "reading"}, + {"role": "user", "content": "continue"}, + ] + + result = cfg.transform_anthropic_messages_request( + model=model, + messages=copy.deepcopy(messages), + anthropic_messages_optional_request_params={ + "max_tokens": 256, + "stream": False, + "system": [ + {"type": "text", "text": "x-anthropic-billing-header: cc_version=2.1.205;"}, + {"type": "text", "text": "Base.", "cache_control": {"type": "ephemeral"}}, + ], + }, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert result["messages"] == messages + assert result["system"] == [ + {"type": "text", "text": "Base.", "cache_control": {"type": "ephemeral"}} + ] + + +def test_bedrock_invoke_transform_hoists_only_leading_system_run(local_model_cost_map): + """On models flagged ``supports_mid_conversation_system``, only the leading + run of ``role: "system"`` messages is hoisted into the top-level ``system`` + field; a later system entry keeps its position in ``messages`` so the + serialized prefix stays stable across turns.""" + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [ + {"role": "system", "content": "You are terse."}, + {"role": "system", "content": "Cite sources."}, + {"role": "user", "content": "hi"}, + {"role": "system", "content": "mid-conversation reminder"}, + {"role": "user", "content": "continue"}, + ] + + result = cfg.transform_anthropic_messages_request( + model="anthropic.claude-opus-4-8", + messages=copy.deepcopy(messages), + anthropic_messages_optional_request_params={"max_tokens": 256, "stream": False}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert result["messages"] == [ + {"role": "user", "content": "hi"}, + {"role": "system", "content": "mid-conversation reminder"}, + {"role": "user", "content": "continue"}, + ] + assert result["system"] == [ + {"type": "text", "text": "You are terse."}, + {"type": "text", "text": "Cite sources."}, + ] + + +def test_bedrock_invoke_transform_hoists_mid_conversation_system_for_older_claude(local_model_cost_map): + """Regression test for Claude Code 400s on pre-Opus-4.8 Bedrock models: + Invoke rejects ``role: "system"`` in every position on Opus 4.7, Sonnet 4.6, + Haiku 4.5, etc. ("role 'system' is not supported on this model"), so on + models without ``supports_mid_conversation_system`` every system entry must + be hoisted into the top-level ``system`` field, mid-conversation ones + included.""" + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [ + {"role": "user", "content": "read the file"}, + {"role": "system", "content": "[Truncated: PARTIAL view of big1.txt]"}, + {"role": "assistant", "content": "reading"}, + {"role": "user", "content": "continue"}, + ] + + result = cfg.transform_anthropic_messages_request( + model="us.anthropic.claude-opus-4-7", + messages=copy.deepcopy(messages), + anthropic_messages_optional_request_params={ + "max_tokens": 256, + "stream": False, + "system": [{"type": "text", "text": "Base."}], + }, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert result["messages"] == [ + {"role": "user", "content": "read the file"}, + {"role": "assistant", "content": "reading"}, + {"role": "user", "content": "continue"}, + ] + assert result["system"] == [ + {"type": "text", "text": "Base."}, + {"type": "text", "text": "[Truncated: PARTIAL view of big1.txt]"}, + ] + + +def test_bedrock_invoke_transform_hoists_all_system_for_unmapped_model(local_model_cost_map): + """A model with no cost-map entry and no fallback-generalization rule gets + the hoist-everything behavior: the safe default is a mutated cache prefix, + never a provider 400 from forwarding a role the model may not accept.""" + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [ + {"role": "user", "content": "hi"}, + {"role": "system", "content": "mid-conversation reminder"}, + {"role": "assistant", "content": "hello"}, + {"role": "user", "content": "continue"}, + ] + + result = cfg.transform_anthropic_messages_request( + model="us.anthropic.claude-opus-3-9", + messages=copy.deepcopy(messages), + anthropic_messages_optional_request_params={"max_tokens": 256, "stream": False}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert result["messages"] == [ + {"role": "user", "content": "hi"}, + {"role": "assistant", "content": "hello"}, + {"role": "user", "content": "continue"}, + ] + assert result["system"] == [{"type": "text", "text": "mid-conversation reminder"}] + + +def test_bedrock_invoke_transform_keeps_system_in_place_for_unmapped_future_claude(local_model_cost_map): + """An unmapped Bedrock Claude at 4.8 or higher resolves through the + ``claude-mid-conversation-system`` capability rule, so a future model that + has not landed in the cost map yet keeps the cache-preserving in-place + behavior instead of falling back to hoist-all.""" + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [ + {"role": "user", "content": "hi"}, + {"role": "system", "content": "mid-conversation reminder"}, + {"role": "assistant", "content": "hello"}, + {"role": "user", "content": "continue"}, + ] + + result = cfg.transform_anthropic_messages_request( + model="us.anthropic.claude-opus-4-9", + messages=copy.deepcopy(messages), + anthropic_messages_optional_request_params={"max_tokens": 256, "stream": False}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert result["messages"] == messages + assert "system" not in result + + +def test_bedrock_claude_4_8_plus_cost_map_entries_carry_mid_conversation_system_flag(): + """Exact cost-map hits resolve before fallback-generalization rules, so a + mapped Bedrock Claude 4.8+ entry without ``supports_mid_conversation_system`` + silently loses the cache-preserving in-place handling that the + ``claude-mid-conversation-system`` capability rule grants unmapped ids. + Every mapped bedrock entry the rule's own pattern matches must carry the + flag explicitly.""" + import re + + import litellm + + cost_map_path = os.path.join(os.path.dirname(litellm.__file__), "model_prices_and_context_window_backup.json") + with open(cost_map_path) as f: + cost_map = json.load(f) + rules = cost_map["fallback_generalizations"]["rules"] + pattern = re.compile( + next(r["pattern"] for r in rules if r["name"] == "claude-mid-conversation-system"), + re.IGNORECASE, + ) + missing = [ + key + for key, info in cost_map.items() + if isinstance(info, dict) + and str(info.get("litellm_provider", "")).startswith("bedrock") + and pattern.search(key) + and info.get("supports_mid_conversation_system") is not True + ] + assert missing == [] + + def test_as_system_content_blocks_handles_each_shape(): """``_as_system_content_blocks`` normalizes every system shape: ``None`` -> empty, a string -> a single text block, a list -> a shallow copy, and any other value @@ -2009,3 +2301,217 @@ def test_bedrock_clear_thinking_leaves_enabled_thinking_on_non_adaptive_model(): assert changed is False assert request["thinking"] == {"type": "enabled", "budget_tokens": 8000} assert "output_config" not in request + + +@pytest.fixture +def local_beta_headers_config(monkeypatch): + from litellm.anthropic_beta_headers_manager import reload_beta_headers_config + + monkeypatch.setenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", "True") + reload_beta_headers_config() + yield + monkeypatch.delenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", raising=False) + reload_beta_headers_config() + + +def test_bedrock_messages_preserves_clear_tool_uses_context_management_and_adds_beta( + local_beta_headers_config, +): + """ + LIT-3393: Bedrock InvokeModel supports automatic tool-call clearing via + ``clear_tool_uses_20250919`` under the ``context-management-2025-06-27`` + beta. Before the LIT-3393 fix, the transformation stripped this edit (only + ``compact_20260112`` survived) AND the beta was filtered out by + ``filter_and_transform_beta_headers`` for ``bedrock``, producing a Bedrock + 400 ``"context_management: Extra inputs are not permitted"``. + + Post-fix, the edit must reach the body and the beta must reach + ``anthropic_beta``. + + AWS docs ("Automatic tool call clearing (Beta)"): + https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md + """ + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}] + optional_params = { + "max_tokens": 4096, + "context_management": { + "edits": [{"type": "clear_tool_uses_20250919"}] + }, + } + + result = cfg.transform_anthropic_messages_request( + model="anthropic.claude-haiku-4-5-20251001-v1:0", + messages=messages, + anthropic_messages_optional_request_params=optional_params, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert result.get("context_management") == { + "edits": [{"type": "clear_tool_uses_20250919"}] + }, "clear_tool_uses_20250919 edit must reach Bedrock InvokeModel body" + assert "context-management-2025-06-27" in result.get("anthropic_beta", []), ( + "context-management-2025-06-27 beta must reach the InvokeModel body so " + "the tool-call-clearing edit is accepted" + ) + + +def test_bedrock_messages_preserves_mixed_compact_and_clear_tool_uses_edits( + local_beta_headers_config, +): + """ + LIT-3393: a request mixing ``compact_20260112`` and + ``clear_tool_uses_20250919`` must keep BOTH edits and emit BOTH + anthropic-beta values (``compact-2026-01-12`` + ``context-management-2025-06-27``). + """ + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}] + optional_params = { + "max_tokens": 4096, + "context_management": { + "edits": [ + {"type": "compact_20260112"}, + {"type": "clear_tool_uses_20250919"}, + ] + }, + } + + result = cfg.transform_anthropic_messages_request( + model="anthropic.claude-sonnet-4-6-20250929-v1:0", + messages=messages, + anthropic_messages_optional_request_params=optional_params, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + cm = result.get("context_management") + assert cm is not None + edit_types = sorted(e.get("type") for e in cm["edits"]) + assert edit_types == ["clear_tool_uses_20250919", "compact_20260112"] + + betas = result.get("anthropic_beta", []) + assert "compact-2026-01-12" in betas + assert "context-management-2025-06-27" in betas + + +def test_bedrock_messages_filters_clear_thinking_keeps_clear_tool_uses( + local_beta_headers_config, +): + """ + LIT-3393: ``clear_thinking_20251015`` remains LiteLLM-internal (consumed via + thinking-injection) and MUST be stripped from the body, while + ``clear_tool_uses_20250919`` (officially supported on Bedrock InvokeModel) + survives in the same request. + """ + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}] + optional_params = { + "max_tokens": 4096, + "context_management": { + "edits": [ + {"type": "clear_thinking_20251015", "keep": "all"}, + {"type": "clear_tool_uses_20250919"}, + ] + }, + } + + result = cfg.transform_anthropic_messages_request( + model="anthropic.claude-opus-4-7", + messages=messages, + anthropic_messages_optional_request_params=optional_params, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + cm = result.get("context_management") + assert cm is not None + assert [e.get("type") for e in cm["edits"]] == [ + "clear_tool_uses_20250919" + ], "clear_thinking_20251015 must still be stripped (LiteLLM-internal)" + + betas = result.get("anthropic_beta", []) + assert "context-management-2025-06-27" in betas + # ``compact-2026-01-12`` was not requested. + assert "compact-2026-01-12" not in betas + + +def test_filter_and_transform_beta_headers_passes_context_management_for_bedrock( + local_beta_headers_config, +): + """ + LIT-3393: ``anthropic_beta_headers_config.json`` previously mapped + ``bedrock.context-management-2025-06-27`` to ``null``, so + ``filter_and_transform_beta_headers`` dropped the header even when the + transformation tried to set it. This regression guard locks the bundled + mapping in place. + + Pinned to the bundled local config via ``LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS`` + so the assertion is not subject to whatever the upstream remote currently + serves or what previous tests left in the module cache. + """ + from litellm.anthropic_beta_headers_manager import filter_and_transform_beta_headers + + out = filter_and_transform_beta_headers( + ["context-management-2025-06-27"], + provider="bedrock", + ) + assert out == ["context-management-2025-06-27"] + + # Bedrock_converse genuinely lacks it per AWS docs; this guard prevents + # an accidental flip there. + out_converse = filter_and_transform_beta_headers( + ["context-management-2025-06-27"], + provider="bedrock_converse", + ) + assert out_converse == [] + + + +def test_bedrock_messages_thinking_shape_follows_exact_bedrock_entry_flag( + local_model_cost_map, monkeypatch +): + """The outbound thinking payload must follow the exact Bedrock cost-map entry. + Before threading the caller's provider through the capability probes, the probe + was pinned to ``"anthropic"``: the exact ``global.anthropic.claude-opus-4-8`` + entry was rejected by the provider match and the anthropic-scoped fallback rule + forced ``thinking.type='adaptive'`` even with ``supports_adaptive_thinking`` + explicitly set to ``false`` on the entry.""" + import litellm + + from litellm.types.router import GenericLiteLLMParams + + model = "global.anthropic.claude-opus-4-8" + cfg = AmazonAnthropicClaudeMessagesConfig() + + def transform(): + return cfg.transform_anthropic_messages_request( + model=model, + messages=[{"role": "user", "content": [{"type": "text", "text": "Hello"}]}], + anthropic_messages_optional_request_params={ + "max_tokens": 4096, + "reasoning_effort": "medium", + }, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + result = transform() + assert result.get("thinking") == {"type": "adaptive"} + assert result.get("output_config") == {"effort": "medium"} + + monkeypatch.setitem(litellm.model_cost[model], "supports_adaptive_thinking", False) + litellm.get_model_info.cache_clear() + + flipped = transform() + thinking = flipped.get("thinking") + assert isinstance(thinking, dict) + assert thinking.get("type") == "enabled" + assert isinstance(thinking.get("budget_tokens"), int) + assert "output_config" not in flipped diff --git a/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_handler.py b/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_handler.py new file mode 100644 index 00000000000..ddc2e026e83 --- /dev/null +++ b/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_handler.py @@ -0,0 +1,362 @@ +import json +import os +import sys +import types +from types import SimpleNamespace +from unittest.mock import MagicMock + +import pytest + +sys.path.insert(0, os.path.abspath("../../../../..")) # Adds the parent directory to the system path + +from litellm.llms.bedrock.common_utils import BedrockError +from litellm.llms.bedrock.realtime.handler import BedrockRealtime +from litellm.llms.bedrock.realtime.transformation import BedrockRealtimeConfig + + +class FakePayloadPart: + def __init__(self, bytes_): + self.bytes_ = bytes_ + + +class FakeInputChunk: + def __init__(self, value): + self.value = value + + +class FakeInputStream: + def __init__(self): + self.sent = [] + self.closed = False + + async def send(self, event): + self.sent.append(event) + + async def close(self): + self.closed = True + + +class SendFailingInputStream(FakeInputStream): + async def send(self, event): + raise RuntimeError("bedrock send failed") + + +class FailOnPromptEndStream(FakeInputStream): + async def send(self, event): + payload = json.loads(event.value.bytes_.decode("utf-8")) + if "promptEnd" in payload.get("event", {}): + raise RuntimeError("bedrock rejected promptEnd") + self.sent.append(event) + + +class FakeBedrockStream: + def __init__(self, input_stream=None): + self.input_stream = input_stream if input_stream is not None else FakeInputStream() + + +class DisconnectingClientWS: + def __init__(self, messages): + self._messages = list(messages) + + async def receive_text(self): + if self._messages: + return self._messages.pop(0) + raise RuntimeError("client disconnected") + + +class ClosableClientWS: + def __init__(self): + self.closed = False + + async def close(self): + self.closed = True + + +class EndedBedrockReceiver: + async def receive(self): + return None + + +class EndedBedrockStream: + async def await_output(self): + return (None, EndedBedrockReceiver()) + + +class RealtimeClientWS: + def __init__(self): + self.closed = False + + async def receive_text(self): + raise RuntimeError("client disconnected") + + async def close(self, code=None, reason=None): + self.closed = True + + +class ImmediatelyEndingBedrockStream: + def __init__(self): + self.input_stream = FakeInputStream() + + async def await_output(self): + return (None, EndedBedrockReceiver()) + + +class FakeStaticCredentialsResolver: + pass + + +class NoCredentialsBedrockRealtime(BedrockRealtime): + def get_credentials(self, **kwargs): + return None + + +class StubCredentialsBedrockRealtime(BedrockRealtime): + def __init__(self, frozen_credentials): + super().__init__() + self.frozen_credentials = frozen_credentials + self.get_credentials_kwargs = None + + def get_credentials(self, **kwargs): + self.get_credentials_kwargs = kwargs + return SimpleNamespace(get_frozen_credentials=lambda: self.frozen_credentials) + + +@pytest.fixture +def stub_aws_sdk_client(monkeypatch): + captured = {} + + class CapturingConfig: + def __init__(self, **kwargs): + captured["config_kwargs"] = kwargs + self.kwargs = kwargs + + class FakeOperationInput: + def __init__(self, model_id): + self.model_id = model_id + + class FakeBedrockRuntimeClient: + def __init__(self, config): + captured["client_config"] = config + + async def invoke_model_with_bidirectional_stream(self, operation_input): + captured["operation_input"] = operation_input + return ImmediatelyEndingBedrockStream() + + package = types.ModuleType("aws_sdk_bedrock_runtime") + client_module = types.ModuleType("aws_sdk_bedrock_runtime.client") + client_module.BedrockRuntimeClient = FakeBedrockRuntimeClient + client_module.InvokeModelWithBidirectionalStreamOperationInput = FakeOperationInput + config_module = types.ModuleType("aws_sdk_bedrock_runtime.config") + config_module.Config = CapturingConfig + models_module = types.ModuleType("aws_sdk_bedrock_runtime.models") + models_module.BidirectionalInputPayloadPart = FakePayloadPart + models_module.InvokeModelWithBidirectionalStreamInputChunk = FakeInputChunk + package.client = client_module + package.config = config_module + package.models = models_module + smithy_package = types.ModuleType("smithy_aws_core") + identity_module = types.ModuleType("smithy_aws_core.identity") + identity_module.StaticCredentialsResolver = FakeStaticCredentialsResolver + smithy_package.identity = identity_module + + stubbed_modules = { + "aws_sdk_bedrock_runtime": package, + "aws_sdk_bedrock_runtime.client": client_module, + "aws_sdk_bedrock_runtime.config": config_module, + "aws_sdk_bedrock_runtime.models": models_module, + "smithy_aws_core": smithy_package, + "smithy_aws_core.identity": identity_module, + } + for module_name, module in stubbed_modules.items(): + monkeypatch.setitem(sys.modules, module_name, module) + + for env_var in ( + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", + "AWS_SESSION_TOKEN", + "AWS_REGION_NAME", + "AWS_SESSION_NAME", + "AWS_PROFILE_NAME", + "AWS_ROLE_NAME", + "AWS_WEB_IDENTITY_TOKEN", + "AWS_STS_ENDPOINT", + "AWS_EXTERNAL_ID", + ): + monkeypatch.delenv(env_var, raising=False) + + return captured + + +@pytest.fixture +def stub_aws_models(monkeypatch): + package = types.ModuleType("aws_sdk_bedrock_runtime") + models = types.ModuleType("aws_sdk_bedrock_runtime.models") + models.BidirectionalInputPayloadPart = FakePayloadPart + models.InvokeModelWithBidirectionalStreamInputChunk = FakeInputChunk + package.models = models + monkeypatch.setitem(sys.modules, "aws_sdk_bedrock_runtime", package) + monkeypatch.setitem(sys.modules, "aws_sdk_bedrock_runtime.models", models) + + +class TestBedrockRealtimeHandler: + """Client disconnect must close the Bedrock session gracefully (LIT-2239 regression)""" + + @pytest.mark.asyncio + async def test_client_disconnect_flushes_session_close_messages(self, stub_aws_models): + handler = BedrockRealtime() + config = BedrockRealtimeConfig() + stream = FakeBedrockStream() + client_ws = DisconnectingClientWS( + [json.dumps({"type": "session.update", "session": {"instructions": "You are helpful."}})] + ) + + await handler._forward_client_to_bedrock(client_ws, stream, config, "amazon.nova-sonic-v1:0", {}) + + sent_events = [json.loads(chunk.value.bytes_.decode("utf-8")) for chunk in stream.input_stream.sent] + event_names = [next(iter(event["event"])) for event in sent_events] + assert event_names[0] == "sessionStart" + assert event_names[-2:] == ["promptEnd", "sessionEnd"] + assert stream.input_stream.closed + + @pytest.mark.asyncio + async def test_client_disconnect_before_session_update_sends_nothing(self, stub_aws_models): + handler = BedrockRealtime() + config = BedrockRealtimeConfig() + stream = FakeBedrockStream() + + await handler._forward_client_to_bedrock( + DisconnectingClientWS([]), stream, config, "amazon.nova-sonic-v1:0", {} + ) + + assert stream.input_stream.sent == [] + assert stream.input_stream.closed + + @pytest.mark.asyncio + async def test_input_stream_closed_even_when_close_flush_fails(self, stub_aws_models): + handler = BedrockRealtime() + config = BedrockRealtimeConfig() + stream = FakeBedrockStream(input_stream=SendFailingInputStream()) + client_ws = DisconnectingClientWS( + [json.dumps({"type": "session.update", "session": {"instructions": "You are helpful."}})] + ) + + await handler._forward_client_to_bedrock(client_ws, stream, config, "amazon.nova-sonic-v1:0", {}) + + assert stream.input_stream.closed + + @pytest.mark.asyncio + async def test_close_flush_continues_after_partial_send_failure(self, stub_aws_models): + handler = BedrockRealtime() + config = BedrockRealtimeConfig() + stream = FakeBedrockStream(input_stream=FailOnPromptEndStream()) + client_ws = DisconnectingClientWS( + [json.dumps({"type": "session.update", "session": {"instructions": "You are helpful."}})] + ) + + await handler._forward_client_to_bedrock(client_ws, stream, config, "amazon.nova-sonic-v1:0", {}) + + sent_events = [json.loads(chunk.value.bytes_.decode("utf-8")) for chunk in stream.input_stream.sent] + event_names = [next(iter(event["event"])) for event in sent_events] + assert "sessionEnd" in event_names + assert stream.input_stream.closed + + @pytest.mark.asyncio + async def test_bedrock_stream_end_closes_client_websocket(self): + handler = BedrockRealtime() + client_ws = ClosableClientWS() + + await handler._forward_bedrock_to_client( + EndedBedrockStream(), + client_ws, + BedrockRealtimeConfig(), + "amazon.nova-sonic-v1:0", + MagicMock(), + {}, + ) + + assert client_ws.closed + + +class TestBedrockRealtimeAwsAuth: + """AWS auth params passed via litellm_params must reach the Smithy client config (LIT-3923 regression)""" + + @pytest.mark.asyncio + async def test_static_credentials_from_litellm_params_reach_smithy_config(self, stub_aws_sdk_client): + handler = BedrockRealtime() + websocket = RealtimeClientWS() + + await handler.async_realtime( + model="amazon.nova-sonic-v1:0", + websocket=websocket, + logging_obj=MagicMock(), + aws_region_name="us-east-1", + aws_access_key_id="litellm-params-access-key", + aws_secret_access_key="litellm-params-secret-key", + aws_session_token="litellm-params-session-token", + ) + + config_kwargs = stub_aws_sdk_client["config_kwargs"] + assert config_kwargs["aws_access_key_id"] == "litellm-params-access-key" + assert config_kwargs["aws_secret_access_key"] == "litellm-params-secret-key" + assert config_kwargs["aws_session_token"] == "litellm-params-session-token" + assert isinstance(config_kwargs["aws_credentials_identity_resolver"], FakeStaticCredentialsResolver) + assert config_kwargs["region"] == "us-east-1" + assert stub_aws_sdk_client["client_config"].kwargs is config_kwargs + assert stub_aws_sdk_client["operation_input"].model_id == "amazon.nova-sonic-v1:0" + assert websocket.closed + + @pytest.mark.asyncio + async def test_role_assumption_params_forwarded_to_get_credentials(self, stub_aws_sdk_client): + handler = StubCredentialsBedrockRealtime( + SimpleNamespace( + access_key="assumed-access-key", + secret_key="assumed-secret-key", + token="assumed-session-token", + ) + ) + + await handler.async_realtime( + model="amazon.nova-sonic-v1:0", + websocket=RealtimeClientWS(), + logging_obj=MagicMock(), + aws_region_name="eu-west-1", + aws_role_name="arn:aws:iam::123456789012:role/nova-sonic", + aws_session_name="realtime-session", + aws_external_id="realtime-external-id", + ) + + assert handler.get_credentials_kwargs == { + "aws_access_key_id": None, + "aws_secret_access_key": None, + "aws_session_token": None, + "aws_region_name": "eu-west-1", + "aws_session_name": "realtime-session", + "aws_profile_name": None, + "aws_role_name": "arn:aws:iam::123456789012:role/nova-sonic", + "aws_web_identity_token": None, + "aws_sts_endpoint": None, + "aws_external_id": "realtime-external-id", + } + config_kwargs = stub_aws_sdk_client["config_kwargs"] + assert config_kwargs["aws_access_key_id"] == "assumed-access-key" + assert config_kwargs["aws_secret_access_key"] == "assumed-secret-key" + assert config_kwargs["aws_session_token"] == "assumed-session-token" + assert isinstance(config_kwargs["aws_credentials_identity_resolver"], FakeStaticCredentialsResolver) + + @pytest.mark.asyncio + async def test_unresolvable_credentials_raise_clear_auth_error(self, stub_aws_sdk_client): + handler = NoCredentialsBedrockRealtime() + + with pytest.raises(BedrockError, match="No AWS credentials found for Bedrock realtime"): + await handler.async_realtime( + model="amazon.nova-sonic-v1:0", + websocket=RealtimeClientWS(), + logging_obj=MagicMock(), + aws_region_name="us-east-1", + ) + + assert "config_kwargs" not in stub_aws_sdk_client + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_transformation.py b/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_transformation.py index bf15727f4b4..a68aa603b26 100644 --- a/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_transformation.py +++ b/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_transformation.py @@ -5,11 +5,16 @@ from unittest.mock import MagicMock import pytest -sys.path.insert( - 0, os.path.abspath("../../../../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../../../..")) # Adds the parent directory to the system path -from litellm.llms.bedrock.realtime.transformation import BedrockRealtimeConfig +import base64 + +from litellm.llms.bedrock.realtime.transformation import ( + TRIGGER_LEADING_SILENCE, + TRIGGER_TRAILING_SILENCE, + BedrockRealtimeConfig, +) +from litellm.llms.bedrock.realtime.trigger_audio import ready_trigger_pcm from litellm.types.llms.openai import OpenAIRealtimeEventTypes @@ -67,19 +72,14 @@ class TestBedrockRealtimeConfig: } ] - session_config = config.session_configuration_request( - "amazon.nova-sonic-v1:0", tools=tools - ) + session_config = config.session_configuration_request("amazon.nova-sonic-v1:0", tools=tools) session_dict = json.loads(session_config) prompt_start = session_dict["prompt_start"]["event"]["promptStart"] assert "toolConfiguration" in prompt_start assert "tools" in prompt_start["toolConfiguration"] assert len(prompt_start["toolConfiguration"]["tools"]) == 1 - assert ( - prompt_start["toolConfiguration"]["tools"][0]["toolSpec"]["name"] - == "get_weather" - ) + assert prompt_start["toolConfiguration"]["tools"][0]["toolSpec"]["name"] == "get_weather" def test_transform_tools_to_bedrock_format(self): """Test OpenAI tool format to Bedrock format transformation""" @@ -93,9 +93,7 @@ class TestBedrockRealtimeConfig: "description": "Get current weather", "parameters": { "type": "object", - "properties": { - "location": {"type": "string", "description": "City name"} - }, + "properties": {"location": {"type": "string", "description": "City name"}}, "required": ["location"], }, }, @@ -120,18 +118,11 @@ class TestBedrockRealtimeConfig: # Test PCM16 format assert config._map_audio_format_to_sample_rate("pcm16", is_output=True) == 24000 - assert ( - config._map_audio_format_to_sample_rate("pcm16", is_output=False) == 16000 - ) + assert config._map_audio_format_to_sample_rate("pcm16", is_output=False) == 16000 # Test G.711 formats - assert ( - config._map_audio_format_to_sample_rate("g711_ulaw", is_output=True) == 8000 - ) - assert ( - config._map_audio_format_to_sample_rate("g711_alaw", is_output=False) - == 8000 - ) + assert config._map_audio_format_to_sample_rate("g711_ulaw", is_output=True) == 8000 + assert config._map_audio_format_to_sample_rate("g711_alaw", is_output=False) == 8000 def test_transform_session_update_event(self): """Test session.update event transformation""" @@ -158,12 +149,7 @@ class TestBedrockRealtimeConfig: # Verify session start message session_start = json.loads(messages[0]) - assert ( - session_start["event"]["sessionStart"]["inferenceConfiguration"][ - "temperature" - ] - == 0.9 - ) + assert session_start["event"]["sessionStart"]["inferenceConfiguration"]["temperature"] == 0.9 def test_transform_session_update_with_tools(self): """Test session.update with tools""" @@ -237,12 +223,7 @@ class TestBedrockRealtimeConfig: content_start = json.loads(messages[0]) assert content_start["event"]["contentStart"]["type"] == "TOOL" assert content_start["event"]["contentStart"]["role"] == "TOOL" - assert ( - content_start["event"]["contentStart"]["toolResultInputConfiguration"][ - "toolUseId" - ] - == "call_123" - ) + assert content_start["event"]["contentStart"]["toolResultInputConfiguration"]["toolUseId"] == "call_123" def test_transform_input_audio_buffer_append(self): """Test input_audio_buffer.append transformation""" @@ -260,12 +241,7 @@ class TestBedrockRealtimeConfig: content_start = json.loads(messages[0]) assert content_start["event"]["contentStart"]["type"] == "AUDIO" - assert ( - content_start["event"]["contentStart"]["audioInputConfiguration"][ - "sampleRateHertz" - ] - == 16000 - ) + assert content_start["event"]["contentStart"]["audioInputConfiguration"]["sampleRateHertz"] == 16000 audio_input = json.loads(messages[1]) assert audio_input["event"]["audioInput"]["content"] == "base64_audio_data_here" @@ -286,6 +262,144 @@ class TestBedrockRealtimeConfig: assert "contentEnd" in content_end["event"] +class TestBedrockRealtimeResponseCreate: + """response.create must trigger Nova Sonic generation (LIT-2239 regression)""" + + def _start_session(self, config): + config.transform_realtime_request( + json.dumps( + { + "type": "session.update", + "session": {"instructions": "You are a helpful assistant."}, + } + ), + "amazon.nova-sonic-v1:0", + ) + + def test_response_create_before_session_update_is_noop(self): + config = BedrockRealtimeConfig() + + messages = config.transform_realtime_request(json.dumps({"type": "response.create"}), "amazon.nova-sonic-v1:0") + + assert messages == [] + + def test_response_create_emits_spoken_trigger_audio(self): + config = BedrockRealtimeConfig() + self._start_session(config) + + messages = config.transform_realtime_request(json.dumps({"type": "response.create"}), "amazon.nova-sonic-v1:0") + + assert len(messages) > 1 + + content_start = json.loads(messages[0])["event"]["contentStart"] + assert content_start["promptName"] == config.prompt_name + assert content_start["contentName"] == config.audio_content_name + assert content_start["type"] == "AUDIO" + assert content_start["interactive"] is True + assert content_start["role"] == "USER" + assert content_start["audioInputConfiguration"]["sampleRateHertz"] == 16000 + + audio_events = [json.loads(message)["event"]["audioInput"] for message in messages[1:]] + assert all(event["promptName"] == config.prompt_name for event in audio_events) + assert all(event["contentName"] == config.audio_content_name for event in audio_events) + + sent_pcm = b"".join(base64.b64decode(event["content"]) for event in audio_events) + assert sent_pcm == TRIGGER_LEADING_SILENCE + ready_trigger_pcm() + TRIGGER_TRAILING_SILENCE + + def test_second_response_create_reuses_open_audio_content(self): + config = BedrockRealtimeConfig() + self._start_session(config) + + first = config.transform_realtime_request(json.dumps({"type": "response.create"}), "amazon.nova-sonic-v1:0") + second = config.transform_realtime_request(json.dumps({"type": "response.create"}), "amazon.nova-sonic-v1:0") + + assert len(second) == len(first) - 1 + assert all("audioInput" in json.loads(message)["event"] for message in second) + + def test_response_create_is_noop_when_client_streams_audio(self): + config = BedrockRealtimeConfig() + self._start_session(config) + config.transform_realtime_request( + json.dumps({"type": "input_audio_buffer.append", "audio": "c2lsZW5jZQ=="}), + "amazon.nova-sonic-v1:0", + ) + + messages = config.transform_realtime_request(json.dumps({"type": "response.create"}), "amazon.nova-sonic-v1:0") + + assert messages == [] + + def test_client_audio_after_trigger_reopens_block_at_client_sample_rate(self): + config = BedrockRealtimeConfig() + config.transform_realtime_request( + json.dumps( + { + "type": "session.update", + "session": { + "instructions": "You are a helpful assistant.", + "input_audio_format": "g711_ulaw", + }, + } + ), + "amazon.nova-sonic-v1:0", + ) + config.transform_realtime_request(json.dumps({"type": "response.create"}), "amazon.nova-sonic-v1:0") + trigger_content_name = config.audio_content_name + + messages = config.transform_realtime_request( + json.dumps({"type": "input_audio_buffer.append", "audio": "c2lsZW5jZQ=="}), + "amazon.nova-sonic-v1:0", + ) + + events = [json.loads(message)["event"] for message in messages] + assert [next(iter(event)) for event in events] == [ + "contentEnd", + "contentStart", + "audioInput", + ] + assert events[0]["contentEnd"]["contentName"] == trigger_content_name + new_content_start = events[1]["contentStart"] + assert new_content_start["contentName"] == config.audio_content_name + assert new_content_start["contentName"] != trigger_content_name + assert new_content_start["audioInputConfiguration"]["sampleRateHertz"] == 8000 + assert events[2]["audioInput"]["contentName"] == config.audio_content_name + + def test_client_audio_after_trigger_reuses_block_at_matching_sample_rate(self): + config = BedrockRealtimeConfig() + self._start_session(config) + config.transform_realtime_request(json.dumps({"type": "response.create"}), "amazon.nova-sonic-v1:0") + trigger_content_name = config.audio_content_name + + messages = config.transform_realtime_request( + json.dumps({"type": "input_audio_buffer.append", "audio": "c2lsZW5jZQ=="}), + "amazon.nova-sonic-v1:0", + ) + + assert len(messages) == 1 + audio_input = json.loads(messages[0])["event"]["audioInput"] + assert audio_input["contentName"] == trigger_content_name + + def test_session_close_messages_close_audio_prompt_and_session(self): + config = BedrockRealtimeConfig() + self._start_session(config) + config.transform_realtime_request(json.dumps({"type": "response.create"}), "amazon.nova-sonic-v1:0") + + close_messages = [json.loads(message)["event"] for message in config.session_close_messages()] + + assert [next(iter(event)) for event in close_messages] == [ + "contentEnd", + "promptEnd", + "sessionEnd", + ] + assert close_messages[0]["contentEnd"]["contentName"] == config.audio_content_name + assert close_messages[1]["promptEnd"]["promptName"] == config.prompt_name + assert config.session_close_messages() == [] + + def test_session_close_messages_before_session_update_is_empty(self): + config = BedrockRealtimeConfig() + + assert config.session_close_messages() == [] + + class TestBedrockRealtimeResponseTransformation: """Test suite for response transformation""" @@ -296,11 +410,7 @@ class TestBedrockRealtimeResponseTransformation: logging_obj.litellm_trace_id = "trace_123" bedrock_message = { - "event": { - "sessionStart": { - "inferenceConfiguration": {"maxTokens": 1024, "temperature": 0.7} - } - } + "event": {"sessionStart": {"inferenceConfiguration": {"maxTokens": 1024, "temperature": 0.7}}} } result = config.transform_realtime_response( @@ -330,9 +440,7 @@ class TestBedrockRealtimeResponseTransformation: logging_obj.litellm_trace_id = "trace_123" # First create a content start to initialize IDs - content_start_message = { - "event": {"contentStart": {"role": "ASSISTANT", "type": "TEXT"}} - } + content_start_message = {"event": {"contentStart": {"role": "ASSISTANT", "type": "TEXT"}}} result1 = config.transform_realtime_response( json.dumps(content_start_message), @@ -368,9 +476,7 @@ class TestBedrockRealtimeResponseTransformation: ) # Check for text delta - text_deltas = [ - msg for msg in result2["response"] if msg["type"] == "response.text.delta" - ] + text_deltas = [msg for msg in result2["response"] if msg["type"] == "response.text.delta"] assert len(text_deltas) == 1 assert text_deltas[0]["delta"] == "Hello, world!" @@ -384,9 +490,7 @@ class TestBedrockRealtimeResponseTransformation: logging_obj.litellm_trace_id = "trace_123" # First create a content start for audio - content_start_message = { - "event": {"contentStart": {"role": "ASSISTANT", "type": "AUDIO"}} - } + content_start_message = {"event": {"contentStart": {"role": "ASSISTANT", "type": "AUDIO"}}} result1 = config.transform_realtime_response( json.dumps(content_start_message), @@ -404,9 +508,7 @@ class TestBedrockRealtimeResponseTransformation: ) # Now send audio output - audio_output_message = { - "event": {"audioOutput": {"content": "base64_audio_content"}} - } + audio_output_message = {"event": {"audioOutput": {"content": "base64_audio_content"}}} result2 = config.transform_realtime_response( json.dumps(audio_output_message), @@ -424,9 +526,7 @@ class TestBedrockRealtimeResponseTransformation: ) # Check for audio delta - audio_deltas = [ - msg for msg in result2["response"] if msg["type"] == "response.audio.delta" - ] + audio_deltas = [msg for msg in result2["response"] if msg["type"] == "response.audio.delta"] assert len(audio_deltas) == 1 assert audio_deltas[0]["delta"] == "base64_audio_content" @@ -504,14 +604,67 @@ class TestBedrockRealtimeResponseTransformation: # Should have text.done, content_part.done, and output_item.done assert len(result["response"]) == 3 - text_done = [ - msg for msg in result["response"] if msg["type"] == "response.text.done" - ][0] + text_done = [msg for msg in result["response"] if msg["type"] == "response.text.done"][0] assert text_done["text"] == "Hello, world!" # Delta chunks should be reset assert result["current_delta_chunks"] is None + def test_content_end_end_turn_emits_response_done(self): + """END_TURN contentEnd must produce response.done (LIT-2239 regression)""" + config = BedrockRealtimeConfig() + logging_obj = MagicMock() + logging_obj.litellm_trace_id = "trace_123" + + content_end_message = {"event": {"contentEnd": {"stopReason": "END_TURN", "type": "AUDIO"}}} + + result = config.transform_realtime_response( + json.dumps(content_end_message), + "amazon.nova-sonic-v1:0", + logging_obj, + realtime_response_transform_input={ + "session_configuration_request": json.dumps({"configured": True}), + "current_output_item_id": "item_123", + "current_response_id": "resp_123", + "current_conversation_id": "conv_123", + "current_delta_chunks": [], + "current_item_chunks": [], + "current_delta_type": "audio", + }, + ) + + response_done_events = [msg for msg in result["response"] if msg["type"] == "response.done"] + assert len(response_done_events) == 1 + assert response_done_events[0]["response"]["status"] == "completed" + assert result["current_output_item_id"] is None + assert result["current_response_id"] is None + assert result["current_delta_type"] is None + + def test_content_end_partial_turn_does_not_emit_response_done(self): + config = BedrockRealtimeConfig() + logging_obj = MagicMock() + logging_obj.litellm_trace_id = "trace_123" + + content_end_message = {"event": {"contentEnd": {"stopReason": "PARTIAL_TURN", "type": "TEXT"}}} + + result = config.transform_realtime_response( + json.dumps(content_end_message), + "amazon.nova-sonic-v1:0", + logging_obj, + realtime_response_transform_input={ + "session_configuration_request": json.dumps({"configured": True}), + "current_output_item_id": "item_123", + "current_response_id": "resp_123", + "current_conversation_id": "conv_123", + "current_delta_chunks": [], + "current_item_chunks": [], + "current_delta_type": "text", + }, + ) + + assert all(msg["type"] != "response.done" for msg in result["response"]) + assert result["current_response_id"] == "resp_123" + def test_transform_prompt_end_response(self): """Test promptEnd response transformation""" config = BedrockRealtimeConfig() @@ -552,9 +705,7 @@ class TestBedrockRealtimeResponseTransformation: logging_obj.litellm_trace_id = "trace_123" # Create a sequence of messages - content_start = { - "event": {"contentStart": {"role": "ASSISTANT", "type": "TEXT"}} - } + content_start = {"event": {"contentStart": {"role": "ASSISTANT", "type": "TEXT"}}} text_output1 = {"event": {"textOutput": {"content": "Hello"}}} text_output2 = {"event": {"textOutput": {"content": " world"}}} @@ -600,9 +751,7 @@ class TestBedrockRealtimeResponseTransformation: logging_obj.litellm_trace_id = "trace_123" # Create a sequence of messages - content_start = { - "event": {"contentStart": {"role": "ASSISTANT", "type": "TEXT"}} - } + content_start = {"event": {"contentStart": {"role": "ASSISTANT", "type": "TEXT"}}} text_output = {"event": {"textOutput": {"content": "Hello"}}} all_events = [] @@ -636,9 +785,7 @@ class TestBedrockRealtimeResponseTransformation: ) # Check all response_ids are the same - response_ids = [ - event["response_id"] for event in all_events if "response_id" in event - ] + response_ids = [event["response_id"] for event in all_events if "response_id" in event] assert len(set(response_ids)) == 1, "Response IDs should be consistent" diff --git a/tests/test_litellm/llms/bedrock/test_base_aws_llm.py b/tests/test_litellm/llms/bedrock/test_base_aws_llm.py index 2d5242d510f..470448251c9 100644 --- a/tests/test_litellm/llms/bedrock/test_base_aws_llm.py +++ b/tests/test_litellm/llms/bedrock/test_base_aws_llm.py @@ -11,7 +11,7 @@ sys.path.insert( from datetime import datetime, timedelta, timezone -from typing import Any, Dict +from typing import Any, Dict, Optional from unittest.mock import MagicMock, patch from botocore.awsrequest import AWSPreparedRequest, AWSRequest @@ -2653,3 +2653,184 @@ class TestGetBedrockModelIdArnHandling: """invoke/ prefix stripping still works after the fix.""" model_id = self._call("invoke/anthropic.claude-3-sonnet-20240229-v1:0") assert model_id == "anthropic.claude-3-sonnet-20240229-v1:0" + + +def _recomputed_sigv4_signature(url: str, secret_key: str, authorization: str, headers: Dict[str, Any], body) -> str: + import hashlib + import hmac + from urllib.parse import urlparse + + parsed = urlparse(url) + credential_scope = authorization.split("Credential=")[1].split(",")[0].split("/", 1)[1] + signed_header_names = authorization.split("SignedHeaders=")[1].split(",")[0].split(";") + header_lookup = {name.lower(): str(value) for name, value in headers.items()} + header_lookup["host"] = parsed.netloc + body_bytes = body if isinstance(body, bytes) else str(body).encode() + canonical_request = "\n".join( + [ + "POST", + parsed.path or "/", + "", + "".join(f"{name}:{header_lookup[name]}\n" for name in signed_header_names), + ";".join(signed_header_names), + hashlib.sha256(body_bytes).hexdigest(), + ] + ) + string_to_sign = "\n".join( + [ + "AWS4-HMAC-SHA256", + header_lookup["x-amz-date"], + credential_scope, + hashlib.sha256(canonical_request.encode()).hexdigest(), + ] + ) + key = f"AWS4{secret_key}".encode() + for scope_part in credential_scope.split("/"): + key = hmac.new(key, scope_part.encode(), hashlib.sha256).digest() + return hmac.new(key, string_to_sign.encode(), hashlib.sha256).hexdigest() + + +class TestSignRequestResign: + """Regression: retrying a Bedrock request with headers from a previous SigV4 sign + (e.g. the /v1/messages strip-thinking-and-retry path) must produce a fresh + Authorization / X-Amz-Date for the new body, not inherit the stale ones and 403.""" + + URL = "https://bedrock-runtime.us-east-1.amazonaws.com/model/test-model/invoke" + ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE" + SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + + @pytest.fixture(autouse=True) + def _clean_aws_env(self, monkeypatch): + for env_var in ("AWS_BEARER_TOKEN_BEDROCK", "AWS_SESSION_TOKEN", "AWS_PROFILE"): + monkeypatch.delenv(env_var, raising=False) + + def _optional_params(self) -> Dict[str, Any]: + return { + "aws_access_key_id": self.ACCESS_KEY, + "aws_secret_access_key": self.SECRET_KEY, + "aws_region_name": "us-east-1", + } + + def _sign(self, headers: Dict[str, Any], request_data: Dict[str, Any]): + return BaseAWSLLM()._sign_request( + service_name="bedrock", + headers=headers, + optional_params=self._optional_params(), + request_data=request_data, + api_base=self.URL, + ) + + def test_resign_with_previously_signed_headers_replaces_stale_sigv4_headers(self): + original_body = { + "messages": [ + { + "role": "assistant", + "content": [{"type": "thinking", "thinking": "x", "signature": ""}], + } + ] + } + first_headers, _ = self._sign(headers={"Content-Type": "application/json"}, request_data=original_body) + assert first_headers["Authorization"].startswith("AWS4-HMAC-SHA256") + + stale_headers = {**first_headers, "X-Amz-Date": "20200101T000000Z"} + stripped_body = {"messages": [{"role": "user", "content": "hi"}]} + second_headers, second_signed_body = self._sign(headers=stale_headers, request_data=stripped_body) + + assert second_headers["X-Amz-Date"] != "20200101T000000Z" + assert second_headers["Authorization"] != stale_headers["Authorization"] + assert second_headers["Authorization"].split("Signature=")[1] == _recomputed_sigv4_signature( + url=self.URL, + secret_key=self.SECRET_KEY, + authorization=second_headers["Authorization"], + headers=second_headers, + body=second_signed_body, + ) + + def test_forwarded_headers_still_added_back_after_signing(self): + signed_headers, _ = self._sign( + headers={"Content-Type": "application/json", "anthropic-version": "bedrock-2023-05-31"}, + request_data={"messages": []}, + ) + assert signed_headers["anthropic-version"] == "bedrock-2023-05-31" + assert signed_headers["Content-Type"] == "application/json" + + def test_caller_supplied_bearer_authorization_survives_signing(self): + signed_headers, _ = self._sign( + headers={"Content-Type": "application/json", "Authorization": "Bearer caller-token"}, + request_data={"messages": []}, + ) + assert signed_headers["Authorization"] == "Bearer caller-token" + + +class TestGetRequestHeadersResign: + """Regression: get_request_headers (invoke/converse/embed/image paths) must not let + stale SigV4 values present in the input headers clobber the freshly computed signature.""" + + URL = "https://bedrock-runtime.us-east-1.amazonaws.com/model/test-model/converse" + ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE" + SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + SESSION_TOKEN = "fresh-session-token" + + @pytest.fixture(autouse=True) + def _clean_aws_env(self, monkeypatch): + for env_var in ("AWS_BEARER_TOKEN_BEDROCK", "AWS_SESSION_TOKEN", "AWS_PROFILE"): + monkeypatch.delenv(env_var, raising=False) + + def _prepare(self, headers: Dict[str, Any], data: str, extra_headers: Optional[Dict[str, str]] = None): + return BaseAWSLLM().get_request_headers( + credentials=Credentials(self.ACCESS_KEY, self.SECRET_KEY, self.SESSION_TOKEN), + aws_region_name="us-east-1", + extra_headers=extra_headers, + endpoint_url=self.URL, + data=data, + headers=headers, + ) + + def test_stale_sigv4_headers_in_input_replaced_by_fresh_signature(self): + first_prepped = self._prepare( + headers={"Content-Type": "application/json"}, + data=json.dumps({"messages": [{"role": "user", "content": "original"}]}), + ) + stale_authorization = first_prepped.headers["Authorization"] + assert stale_authorization.startswith("AWS4-HMAC-SHA256") + + stale_headers = { + "Content-Type": "application/json", + "Authorization": stale_authorization, + "X-Amz-Date": "20200101T000000Z", + "X-Amz-Security-Token": "stale-session-token", + } + retry_data = json.dumps({"messages": [{"role": "user", "content": "retry"}]}) + second_prepped = self._prepare(headers=stale_headers, data=retry_data) + + assert second_prepped.headers["X-Amz-Date"] != "20200101T000000Z" + assert second_prepped.headers["X-Amz-Security-Token"] == self.SESSION_TOKEN + assert second_prepped.headers["Authorization"] != stale_authorization + assert second_prepped.headers["Authorization"].split("Signature=")[1] == _recomputed_sigv4_signature( + url=self.URL, + secret_key=self.SECRET_KEY, + authorization=second_prepped.headers["Authorization"], + headers=dict(second_prepped.headers), + body=retry_data, + ) + + def test_forwarded_headers_still_added_back_after_signing(self): + prepped = self._prepare( + headers={ + "Content-Type": "application/json", + "anthropic-version": "bedrock-2023-05-31", + "user-agent": "litellm-test-client", + }, + data=json.dumps({"messages": []}), + ) + assert prepped.headers["anthropic-version"] == "bedrock-2023-05-31" + assert prepped.headers["user-agent"] == "litellm-test-client" + assert prepped.headers["Content-Type"] == "application/json" + + def test_extra_headers_bearer_authorization_still_overrides_signature(self): + prepped = self._prepare( + headers={"Content-Type": "application/json"}, + data=json.dumps({"messages": []}), + extra_headers={"Authorization": "Bearer foo"}, + ) + assert prepped.headers["Authorization"] == "Bearer foo" diff --git a/tests/test_litellm/llms/bedrock/test_bedrock_common_utils.py b/tests/test_litellm/llms/bedrock/test_bedrock_common_utils.py index 301bcba99f4..8cc6e4ff25d 100644 --- a/tests/test_litellm/llms/bedrock/test_bedrock_common_utils.py +++ b/tests/test_litellm/llms/bedrock/test_bedrock_common_utils.py @@ -445,3 +445,31 @@ def test_explicit_invoke_route_does_not_match_async_invoke(): BedrockModelInfo._explicit_async_invoke_route(f"bedrock/{async_invoke_model}") is True ) + + +def test_capability_lookups_fall_back_to_base_model_when_regional_entry_lacks_field(monkeypatch): + """ + Regression test: a regional model_cost entry without the capability field + must not shadow a base entry that has it (`get(model) or get(base)` used to + short-circuit on the truthy regional dict and drop the capability). + """ + import litellm + from litellm.llms.bedrock.common_utils import ( + bedrock_converse_supports_parallel_tool_use_config, + is_claude_4_5_on_bedrock, + ) + + base = "anthropic.claude-fallback-test" + regional = f"eu.{base}" + monkeypatch.setitem(litellm.model_cost, regional, {"input_cost_per_token": 1e-06}) + monkeypatch.setitem( + litellm.model_cost, + base, + { + "cache_creation_input_token_cost_above_1hr": 1e-05, + "supports_parallel_tool_use_config": True, + }, + ) + + assert is_claude_4_5_on_bedrock(regional) is True + assert bedrock_converse_supports_parallel_tool_use_config(regional) is True diff --git a/tests/test_litellm/llms/bedrock/test_mantle.py b/tests/test_litellm/llms/bedrock/test_mantle.py index f7f8f582abc..c5ce0d5aba7 100644 --- a/tests/test_litellm/llms/bedrock/test_mantle.py +++ b/tests/test_litellm/llms/bedrock/test_mantle.py @@ -204,6 +204,88 @@ def test_mantle_transform_request_strips_prefix_and_adds_model(): ) assert request["model"] == "anthropic.claude-mythos-preview" assert "mantle/" not in request["model"] + assert "stream" not in request + + +def test_mantle_transform_request_keeps_stream_in_body(): + config = AmazonMantleConfig() + request = config.transform_request( + model="mantle/anthropic.claude-mythos-preview", + messages=[{"role": "user", "content": "Hello"}], + optional_params={"max_tokens": 100, "stream": True}, + litellm_params={}, + headers={}, + ) + assert request["stream"] is True + assert request["model"] == "anthropic.claude-mythos-preview" + + +@pytest.mark.asyncio +async def test_mantle_async_transform_request_keeps_stream_in_body(): + config = AmazonMantleConfig() + request = await config.async_transform_request( + model="mantle/anthropic.claude-mythos-preview", + messages=[{"role": "user", "content": "Hello"}], + optional_params={"max_tokens": 100, "stream": True}, + litellm_params={}, + headers={}, + ) + assert request["stream"] is True + assert request["model"] == "anthropic.claude-mythos-preview" + + +@pytest.mark.asyncio +async def test_mantle_async_transform_request_omits_stream_when_not_streaming(): + config = AmazonMantleConfig() + request = await config.async_transform_request( + model="mantle/anthropic.claude-mythos-preview", + messages=[{"role": "user", "content": "Hello"}], + optional_params={"max_tokens": 100}, + litellm_params={}, + headers={}, + ) + assert "stream" not in request + + +def test_mantle_messages_transform_request_keeps_stream_in_body(): + from litellm.types.router import GenericLiteLLMParams + + config = AmazonMantleMessagesConfig() + request = config.transform_anthropic_messages_request( + model="mantle/anthropic.claude-mythos-preview", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_optional_request_params={"max_tokens": 100, "stream": True}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + assert request["stream"] is True + assert request["model"] == "anthropic.claude-mythos-preview" + + +def test_mantle_messages_transform_request_omits_stream_when_not_streaming(): + from litellm.types.router import GenericLiteLLMParams + + config = AmazonMantleMessagesConfig() + request = config.transform_anthropic_messages_request( + model="mantle/anthropic.claude-mythos-preview", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_optional_request_params={"max_tokens": 100}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + assert "stream" not in request + + +def test_mantle_chat_streaming_uses_anthropic_sse_iterator(): + from litellm.llms.anthropic.chat.handler import ModelResponseIterator + + config = AmazonMantleConfig() + assert config.has_custom_stream_wrapper is False + iterator = config.get_model_response_iterator( + streaming_response=iter([]), + sync_stream=True, + ) + assert isinstance(iterator, ModelResponseIterator) def test_mantle_validate_environment_sets_workspace_header(): @@ -347,3 +429,164 @@ async def test_mantle_anthropic_messages_routes_to_vpc_api_base(): assert len(urls) == 1 assert urls[0] == f"{_VPC_ENDPOINT}/anthropic/v1/messages" assert "api.aws" not in urls[0] + + +_ANTHROPIC_SSE_EVENTS = ( + ( + "message_start", + { + "type": "message_start", + "message": { + "id": "msg_stream_test", + "type": "message", + "role": "assistant", + "model": "anthropic.claude-mythos-preview", + "content": [], + "stop_reason": None, + "stop_sequence": None, + "usage": {"input_tokens": 10, "output_tokens": 1}, + }, + }, + ), + ( + "content_block_start", + { + "type": "content_block_start", + "index": 0, + "content_block": {"type": "text", "text": ""}, + }, + ), + ( + "content_block_delta", + { + "type": "content_block_delta", + "index": 0, + "delta": {"type": "text_delta", "text": "pong"}, + }, + ), + ("content_block_stop", {"type": "content_block_stop", "index": 0}), + ( + "message_delta", + { + "type": "message_delta", + "delta": {"stop_reason": "end_turn", "stop_sequence": None}, + "usage": {"output_tokens": 2}, + }, + ), + ("message_stop", {"type": "message_stop"}), +) + + +def _anthropic_sse_bytes() -> bytes: + return "".join( + f"event: {event}\ndata: {json.dumps(payload)}\n\n" + for event, payload in _ANTHROPIC_SSE_EVENTS + ).encode() + + +def _anthropic_sse_response(url: str) -> httpx.Response: + return httpx.Response( + status_code=200, + content=_anthropic_sse_bytes(), + headers={"content-type": "text/event-stream"}, + request=httpx.Request("POST", url), + ) + + +def test_mantle_completion_streaming_sends_stream_and_decodes_sse(): + import litellm + + requests = [] + + def mock_post(self, url, data=None, headers=None, **kwargs): + requests.append(_capture_request(url=url, headers=headers or {}, data=data)) + return _anthropic_sse_response(url) + + with patch("litellm.llms.custom_httpx.http_handler.HTTPHandler.post", mock_post): + response = litellm.completion( + model="bedrock/mantle/anthropic.claude-mythos-preview", + messages=[{"role": "user", "content": "ping"}], + max_tokens=10, + stream=True, + aws_access_key_id="fake-key", + aws_secret_access_key="fake-secret", + aws_region_name="us-east-1", + ) + chunks = list(response) + + assert len(requests) == 1 + assert requests[0]["body"]["stream"] is True + content = "".join(chunk.choices[0].delta.content or "" for chunk in chunks) + assert content == "pong" + assert chunks[-1].choices[0].finish_reason == "stop" + + +@pytest.mark.asyncio +async def test_mantle_acompletion_streaming_sends_stream_and_decodes_sse(): + import litellm + + requests = [] + + async def mock_post(self, url, data=None, headers=None, **kwargs): + requests.append(_capture_request(url=url, headers=headers or {}, data=data)) + return _anthropic_sse_response(url) + + try: + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + new=mock_post, + ): + response = await litellm.acompletion( + model="bedrock/mantle/anthropic.claude-mythos-preview", + messages=[{"role": "user", "content": "ping"}], + max_tokens=10, + stream=True, + aws_access_key_id="fake-key", + aws_secret_access_key="fake-secret", + aws_region_name="us-east-1", + ) + chunks = [chunk async for chunk in response] + finally: + await litellm.close_litellm_async_clients() + + assert len(requests) == 1 + assert requests[0]["body"]["stream"] is True + content = "".join(chunk.choices[0].delta.content or "" for chunk in chunks) + assert content == "pong" + assert chunks[-1].choices[0].finish_reason == "stop" + + +@pytest.mark.asyncio +async def test_mantle_anthropic_messages_streaming_sends_stream_and_passes_through_sse(): + import litellm + + requests = [] + + async def mock_post(self, url, data=None, headers=None, **kwargs): + requests.append(_capture_request(url=url, headers=headers or {}, data=data)) + return _anthropic_sse_response(str(url)) + + try: + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + new=mock_post, + ): + response = await litellm.anthropic_messages( + model="bedrock/mantle/anthropic.claude-mythos-preview", + messages=[{"role": "user", "content": "ping"}], + max_tokens=10, + stream=True, + aws_access_key_id="fake-key", + aws_secret_access_key="fake-secret", + aws_region_name="us-east-1", + ) + raw = b"".join([chunk async for chunk in response]) + finally: + await litellm.close_litellm_async_clients() + + assert len(requests) == 1 + assert requests[0]["body"]["stream"] is True + text = raw.decode() + assert "event: message_start" in text + assert '"text": "pong"' in text + assert "event: message_stop" in text diff --git a/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py b/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py index 94efc7c51ef..aafaf401700 100644 --- a/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py +++ b/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py @@ -452,6 +452,16 @@ class TestBedrockMantleResponsesRegistry: assert isinstance(cfg, BedrockMantleResponsesAPIConfig) assert cfg.use_openai_path is True + def test_registry_returns_native_config_for_xai_grok(self, local_cost_map): + from litellm.utils import ProviderConfigManager + + cfg = ProviderConfigManager.get_provider_responses_api_config( + provider="bedrock_mantle", + model="xai.grok-4.3", + ) + assert isinstance(cfg, BedrockMantleResponsesAPIConfig) + assert cfg.use_openai_path is False + def test_unmapped_frontier_model_falls_through_to_none(self, restore_model_cost): # The gate is data-driven, not name-based: an unseen model not yet in the # price map (e.g. a future gpt-6) has no capability signal, so it falls diff --git a/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py b/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py index 8c934f9c21e..926f40a6c67 100644 --- a/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py +++ b/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py @@ -1,4 +1,5 @@ import asyncio +import json import os import sys from unittest.mock import AsyncMock, Mock, patch @@ -12,6 +13,11 @@ from litellm.integrations.code_interpreter_interception.handler import ( CodeInterpreterInterceptionLogger, LITELLM_CODE_EXECUTION_TOOL_NAME, ) +from litellm.llms.base_llm.audio_transcription.transformation import ( + AudioTranscriptionRequestData, + BaseAudioTranscriptionConfig, +) +from litellm.llms.base_llm.chat.transformation import BaseLLMException from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from litellm.llms.custom_httpx.llm_http_handler import ( BaseLLMHTTPHandler, @@ -19,6 +25,7 @@ from litellm.llms.custom_httpx.llm_http_handler import ( ) from litellm.types.llms.openai import ResponsesAPIResponse from litellm.types.router import GenericLiteLLMParams +from litellm.types.utils import TranscriptionResponse _ACTIVE_KEY = "_code_interpreter_interception_active" _SANDBOX_KEY = "_code_interpreter_interception_sandbox_key" @@ -435,6 +442,227 @@ async def test_async_anthropic_messages_handler_extra_headers(): assert captured_headers["X-Auth-Token"] == "token123" +@pytest.mark.asyncio +async def test_async_anthropic_messages_handler_streaming_forwards_provider_response_headers(): + """ + Regression test for LIT-3724 (issue 2): streaming /v1/messages responses + dropped the upstream provider's HTTP response headers, so Bedrock's + x-amzn-requestid / x-amzn-trace-id never reached clients even with + `return_response_headers: true`. The returned stream object must carry + them in `_hidden_params["additional_headers"]` (llm_provider-* prefixed), + which the proxy merges into the client-facing response headers. + """ + from collections.abc import AsyncIterator as ABCAsyncIterator + + from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, + ) + + handler = BaseLLMHTTPHandler() + + sse_body = ( + b'event: message_start\ndata: {"type": "message_start"}\n\n' + b'event: message_stop\ndata: {"type": "message_stop"}\n\n' + ) + upstream_response = httpx.Response( + 200, + headers={ + "x-amzn-requestid": "amzn-req-123", + "x-amzn-trace-id": "Root=1-abc-def", + }, + content=sse_body, + request=httpx.Request("POST", "https://api.anthropic.com/v1/messages"), + ) + mock_client = AsyncMock(spec=AsyncHTTPHandler) + mock_client.post = AsyncMock(return_value=upstream_response) + + mock_logging_obj = Mock() + mock_logging_obj.model_call_details = {} + + result = await handler.async_anthropic_messages_handler( + model="claude-sonnet-4-20250514", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_provider_config=AnthropicMessagesConfig(), + anthropic_messages_optional_request_params={"max_tokens": 32}, + custom_llm_provider="anthropic", + litellm_params=GenericLiteLLMParams(), + logging_obj=mock_logging_obj, + client=mock_client, + api_key="sk-test", + stream=True, + kwargs={}, + ) + + assert isinstance(result, ABCAsyncIterator) + + additional_headers = result._hidden_params["additional_headers"] + assert additional_headers["llm_provider-x-amzn-requestid"] == "amzn-req-123" + assert additional_headers["llm_provider-x-amzn-trace-id"] == "Root=1-abc-def" + + collected = b"".join([chunk async for chunk in result]) + assert b"message_start" in collected + assert b"message_stop" in collected + + +@pytest.mark.asyncio +async def test_async_anthropic_messages_handler_agentic_streaming_forwards_provider_response_headers(): + """ + Companion to the test above for the agentic branch: when a callback + overrides async_should_run_agentic_loop, the handler wraps + AgenticAnthropicStreamingIterator in AnthropicMessagesStreamingResponse. + That wrapping must still expose the provider headers and delegate + iteration through the two-phase agentic iterator unchanged. + """ + from collections.abc import AsyncIterator as ABCAsyncIterator + + from litellm.integrations.custom_logger import CustomLogger + from litellm.llms.anthropic.experimental_pass_through.messages.agentic_streaming_iterator import ( + AgenticAnthropicStreamingIterator, + ) + from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, + ) + + class NoOpAgenticCallback(CustomLogger): + async def async_should_run_agentic_loop( + self, + response, + model, + messages, + tools, + stream, + custom_llm_provider, + kwargs, + ): + return False, {} + + handler = BaseLLMHTTPHandler() + + sse_body = ( + b'event: message_start\ndata: {"type": "message_start"}\n\n' + b'event: message_stop\ndata: {"type": "message_stop"}\n\n' + ) + upstream_response = httpx.Response( + 200, + headers={"x-amzn-requestid": "amzn-req-456"}, + content=sse_body, + request=httpx.Request("POST", "https://api.anthropic.com/v1/messages"), + ) + mock_client = AsyncMock(spec=AsyncHTTPHandler) + mock_client.post = AsyncMock(return_value=upstream_response) + + mock_logging_obj = Mock() + mock_logging_obj.model_call_details = {} + mock_logging_obj.dynamic_success_callbacks = [NoOpAgenticCallback()] + + result = await handler.async_anthropic_messages_handler( + model="claude-sonnet-4-20250514", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_provider_config=AnthropicMessagesConfig(), + anthropic_messages_optional_request_params={"max_tokens": 32}, + custom_llm_provider="anthropic", + litellm_params=GenericLiteLLMParams(), + logging_obj=mock_logging_obj, + client=mock_client, + api_key="sk-test", + stream=True, + kwargs={}, + ) + + assert isinstance(result, ABCAsyncIterator) + assert isinstance(result.completion_stream, AgenticAnthropicStreamingIterator) + assert result._hidden_params["additional_headers"]["llm_provider-x-amzn-requestid"] == "amzn-req-456" + + collected = b"".join([chunk async for chunk in result]) + assert b"message_start" in collected + assert b"message_stop" in collected + + +@pytest.mark.asyncio +async def test_anthropic_messages_streaming_response_aclose_closes_upstream_stream(): + """ + Regression test: the proxy's streaming cleanup calls aclose on the + handler's return value (see _finalize_streaming_generator_cleanup's + hasattr(response, "aclose") check). The wrapper must forward aclose to + the upstream stream so provider connections are released on client + disconnect instead of lingering until garbage collection. + """ + from litellm.llms.anthropic.experimental_pass_through.messages.streaming_iterator import ( + AnthropicMessagesStreamingResponse, + ) + + class UpstreamTracker: + def __init__(self): + self.closed = False + + tracker = UpstreamTracker() + + async def upstream(): + try: + yield b'data: {"type": "message_start"}\n\n' + yield b'data: {"type": "message_stop"}\n\n' + finally: + tracker.closed = True + + stream = AnthropicMessagesStreamingResponse( + completion_stream=upstream(), + hidden_params={"additional_headers": {}}, + ) + + first_chunk = await stream.__anext__() + assert b"message_start" in first_chunk + assert tracker.closed is False + + await stream.aclose() + assert tracker.closed is True + + +@pytest.mark.asyncio +async def test_anthropic_messages_streaming_response_aclose_closes_agentic_upstream_stream(): + from litellm.llms.anthropic.experimental_pass_through.messages.agentic_streaming_iterator import ( + AgenticAnthropicStreamingIterator, + ) + from litellm.llms.anthropic.experimental_pass_through.messages.streaming_iterator import ( + AnthropicMessagesStreamingResponse, + ) + + class UpstreamTracker: + def __init__(self): + self.closed = False + + tracker = UpstreamTracker() + + async def upstream(): + try: + yield b'data: {"type": "message_start"}\n\n' + yield b'data: {"type": "message_stop"}\n\n' + finally: + tracker.closed = True + + agentic_iterator = AgenticAnthropicStreamingIterator( + completion_stream=upstream(), + http_handler=Mock(), + model="claude-sonnet-4-20250514", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_provider_config=Mock(), + anthropic_messages_optional_request_params={}, + logging_obj=Mock(), + custom_llm_provider="anthropic", + kwargs={}, + ) + stream = AnthropicMessagesStreamingResponse( + completion_stream=agentic_iterator, + hidden_params={"additional_headers": {}}, + ) + + first_chunk = await stream.__anext__() + assert b"message_start" in first_chunk + assert tracker.closed is False + + await stream.aclose() + assert tracker.closed is True + + @pytest.mark.asyncio async def test_async_anthropic_messages_handler_passes_litellm_metadata(): """Ensure litellm_metadata from kwargs is forwarded via update_from_kwargs. @@ -1212,6 +1440,73 @@ def test_async_compact_handler_sends_json_when_not_signed(): assert "data" not in kwargs +@pytest.mark.asyncio +async def test_async_anthropic_messages_handler_passes_api_key_to_agentic_hooks(): + """ + Regression: async_anthropic_messages_handler must inject api_key into the + kwargs dict forwarded to _call_agentic_completion_hooks. + + Without this, follow-up calls made by agentic hooks (e.g. websearch + interception's second LLM call after executing searches) have no api_key + and fail with "x-api-key header is required". + """ + handler = BaseLLMHTTPHandler() + + mock_config = Mock() + mock_config.validate_anthropic_messages_environment = Mock( + return_value=({"x-api-key": "sk-test"}, "https://api.anthropic.com") + ) + mock_config.transform_anthropic_messages_request = Mock( + return_value={"model": "claude-haiku", "messages": [], "max_tokens": 16} + ) + mock_config.sign_request = Mock(return_value=({}, None)) + + fake_raw_response = {"id": "msg_1", "type": "message", "role": "assistant", "content": [], "stop_reason": "end_turn"} + mock_config.transform_anthropic_messages_response = Mock(return_value=fake_raw_response) + + mock_logging_obj = Mock() + mock_logging_obj.update_environment_variables = Mock() + mock_logging_obj.model_call_details = {} + mock_logging_obj.stream = False + mock_logging_obj.dynamic_success_callbacks = None + + captured_kwargs: dict = {} + sentinel_response = object() + + async def fake_agentic_hooks(**call_kwargs): + captured_kwargs.update(call_kwargs) + return sentinel_response + + mock_httpx_response = Mock() + mock_httpx_response.status_code = 200 + + with ( + patch.object(handler, "_async_post_anthropic_messages_with_http_error_retry", new=AsyncMock(return_value=mock_httpx_response)), + patch.object(handler, "_call_agentic_completion_hooks", side_effect=fake_agentic_hooks), + patch("litellm.llms.custom_httpx.llm_http_handler.get_async_httpx_client"), + patch("litellm.litellm_core_utils.get_provider_specific_headers.ProviderSpecificHeaderUtils.get_provider_specific_headers", return_value=None), + ): + result = await handler.async_anthropic_messages_handler( + model="claude-haiku", + messages=[{"role": "user", "content": "hi"}], + anthropic_messages_provider_config=mock_config, + anthropic_messages_optional_request_params={"stream": False}, + custom_llm_provider="anthropic", + litellm_params=GenericLiteLLMParams(api_key="sk-real-anthropic-key"), + logging_obj=mock_logging_obj, + api_key="sk-real-anthropic-key", + stream=False, + ) + + assert result is sentinel_response + assert "kwargs" in captured_kwargs, "_call_agentic_completion_hooks not called" + forwarded = captured_kwargs["kwargs"] + assert forwarded.get("api_key") == "sk-real-anthropic-key", ( + "api_key must be injected into kwargs passed to _call_agentic_completion_hooks " + "so follow-up calls in agentic hooks (e.g. websearch) can authenticate" + ) + + class _FakeWSExceptions: class WebSocketException(Exception): pass @@ -1297,3 +1592,339 @@ async def test_realtime_backend_open_does_not_retry_auth_failure(rejection): await BaseLLMHTTPHandler._open_realtime_backend_ws(fake, "wss://backend.example/live", {}, None) assert fake.attempts == 1 + + +class _JSONBodyAudioTranscriptionConfig(BaseAudioTranscriptionConfig): + def get_supported_openai_params(self, model): + return [] + + def map_openai_params(self, non_default_params, optional_params, model, drop_params): + return optional_params + + def validate_environment( + self, + headers, + model, + messages, + optional_params, + litellm_params, + api_key=None, + api_base=None, + ): + return {**headers, "Authorization": "Bearer test-token"} + + def get_complete_url(self, api_base, api_key, model, optional_params, litellm_params, stream=None): + return "https://transcription.example/recognize" + + def transform_audio_transcription_request(self, model, audio_file, optional_params, litellm_params): + return AudioTranscriptionRequestData(data={"config": {"model": model}, "content": "YXVkaW8="}) + + def transform_audio_transcription_response(self, raw_response): + return TranscriptionResponse(text=raw_response.json()["text"]) + + def get_error_class(self, error_message, status_code, headers): + return BaseLLMException(message=error_message, status_code=status_code, headers=headers) + + +def _json_transcription_call_kwargs(provider_config): + return { + "model": "test-model", + "audio_file": b"raw-audio", + "optional_params": {}, + "litellm_params": {}, + "model_response": TranscriptionResponse(), + "timeout": 10.0, + "max_retries": 0, + "logging_obj": Mock(), + "api_key": None, + "api_base": None, + "custom_llm_provider": "custom", + "headers": {}, + "provider_config": provider_config, + } + + +def _capture_json_transcription_request(captured): + def respond(request): + captured["content_type"] = request.headers.get("content-type") + captured["body"] = json.loads(request.content) + return httpx.Response(200, json={"text": "transcribed"}) + + return respond + + +def test_audio_transcriptions_sends_dict_data_as_json_body(): + """Regression: dict request data was passed to httpx's data= param, which + form-encodes it and silently ignores json=; JSON-body providers (e.g. + Google Speech-to-Text) need an application/json body.""" + captured = {} + client = HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(_capture_json_transcription_request(captured)))) + + response = BaseLLMHTTPHandler().audio_transcriptions( + client=client, + atranscription=False, + **_json_transcription_call_kwargs(_JSONBodyAudioTranscriptionConfig()), + ) + + assert captured["content_type"] == "application/json" + assert captured["body"] == {"config": {"model": "test-model"}, "content": "YXVkaW8="} + assert response.text == "transcribed" + + +@pytest.mark.asyncio +async def test_async_audio_transcriptions_sends_dict_data_as_json_body(): + captured = {} + client = AsyncHTTPHandler() + client.client = httpx.AsyncClient(transport=httpx.MockTransport(_capture_json_transcription_request(captured))) + + response = await BaseLLMHTTPHandler().async_audio_transcriptions( + client=client, + **_json_transcription_call_kwargs(_JSONBodyAudioTranscriptionConfig()), + ) + + assert captured["content_type"] == "application/json" + assert captured["body"] == {"config": {"model": "test-model"}, "content": "YXVkaW8="} + assert response.text == "transcribed" + + +@pytest.mark.asyncio +async def test_async_retrieve_file_content_raises_on_http_error(): + """ + LIT-4008 regression: a provider error response (e.g. Anthropic's 400 + "File id must have `file_` prefix") must raise instead of being wrapped + as file content, which downstream batch cost tracking would parse as an + empty results file and bill $0. + """ + from litellm.llms.anthropic.common_utils import AnthropicError + from litellm.llms.anthropic.files.transformation import AnthropicFilesConfig + + handler = BaseLLMHTTPHandler() + client = Mock(spec=AsyncHTTPHandler) + client.get = AsyncMock( + return_value=httpx.Response( + status_code=400, + content=b'{"type":"error","error":{"type":"invalid_request_error","message":"File id must have `file_` prefix."}}', + ) + ) + + with pytest.raises(AnthropicError) as exc_info: + await handler.async_retrieve_file_content( + file_content_request={"file_id": "msgbatch_123"}, + provider_config=AnthropicFilesConfig(), + litellm_params={"api_key": "sk-test"}, + headers={}, + logging_obj=Mock(), + client=client, + ) + + assert exc_info.value.status_code == 400 + assert "file_" in str(exc_info.value) + + +def test_sync_retrieve_file_content_raises_on_http_error(): + from litellm.llms.anthropic.common_utils import AnthropicError + from litellm.llms.anthropic.files.transformation import AnthropicFilesConfig + + handler = BaseLLMHTTPHandler() + client = Mock(spec=HTTPHandler) + client.get = Mock( + return_value=httpx.Response( + status_code=404, + content=b'{"type":"error","error":{"type":"not_found_error","message":"not found"}}', + ) + ) + + with pytest.raises(AnthropicError) as exc_info: + handler.retrieve_file_content( + file_content_request={"file_id": "file-abc"}, + provider_config=AnthropicFilesConfig(), + litellm_params={"api_key": "sk-test"}, + headers={}, + logging_obj=Mock(), + client=client, + ) + + assert exc_info.value.status_code == 404 + + +_UPSTREAM_NOT_FOUND_BODY = { + "error": { + "message": "Response with id 'resp_abc' not found.", + "type": "invalid_request_error", + "param": None, + "code": None, + } +} + + +def _async_handler_returning(status_code: int, body: dict) -> AsyncHTTPHandler: + handler = AsyncHTTPHandler() + handler.client = httpx.AsyncClient( + transport=httpx.MockTransport(lambda request: httpx.Response(status_code, json=body)) + ) + return handler + + +def _sync_handler_returning(status_code: int, body: dict) -> HTTPHandler: + handler = HTTPHandler() + handler.client = httpx.Client(transport=httpx.MockTransport(lambda request: httpx.Response(status_code, json=body))) + return handler + + +@pytest.mark.asyncio +async def test_aget_responses_surfaces_upstream_error_status_instead_of_500(): + client = _async_handler_returning(404, _UPSTREAM_NOT_FOUND_BODY) + + with pytest.raises(litellm.NotFoundError) as excinfo: + await litellm.aget_responses( + response_id="resp_abc", + custom_llm_provider="azure", + api_base="https://test.openai.azure.com", + api_key="test-key", + api_version="2025-03-01-preview", + client=client, + ) + + assert excinfo.value.status_code == 404 + assert "Response with id 'resp_abc' not found." in excinfo.value.message + + +def test_get_responses_surfaces_upstream_error_status_instead_of_500(): + client = _sync_handler_returning(404, _UPSTREAM_NOT_FOUND_BODY) + + with pytest.raises(litellm.NotFoundError) as excinfo: + litellm.get_responses( + response_id="resp_abc", + custom_llm_provider="azure", + api_base="https://test.openai.azure.com", + api_key="test-key", + api_version="2025-03-01-preview", + client=client, + ) + + assert excinfo.value.status_code == 404 + assert "Response with id 'resp_abc' not found." in excinfo.value.message + + +def test_list_input_items_surfaces_upstream_error_status(): + client = _sync_handler_returning(404, _UPSTREAM_NOT_FOUND_BODY) + + with pytest.raises(litellm.NotFoundError) as excinfo: + litellm.list_input_items( + response_id="resp_abc", + custom_llm_provider="azure", + api_base="https://test.openai.azure.com", + api_key="test-key", + api_version="2025-03-01-preview", + client=client, + ) + + assert excinfo.value.status_code == 404 + + +@pytest.mark.asyncio +async def test_alist_input_items_surfaces_upstream_error_status(): + client = _async_handler_returning(404, _UPSTREAM_NOT_FOUND_BODY) + + with pytest.raises(litellm.NotFoundError) as excinfo: + await litellm.alist_input_items( + response_id="resp_abc", + custom_llm_provider="azure", + api_base="https://test.openai.azure.com", + api_key="test-key", + api_version="2025-03-01-preview", + client=client, + ) + + assert excinfo.value.status_code == 404 + + +@pytest.mark.asyncio +async def test_anthropic_invalid_thinking_signature_retry_resigns_bedrock_request(monkeypatch): + """Regression: after Bedrock rejects a replayed thinking block (400 invalid signature), + the strip-and-retry re-sign must not inherit attempt 1's SigV4 Authorization/X-Amz-Date; + reusing them over the new stripped body makes AWS return 403 SignatureDoesNotMatch.""" + from litellm.llms.bedrock.messages.invoke_transformations.anthropic_claude3_transformation import ( + AmazonAnthropicClaudeMessagesConfig, + ) + + for env_var in ("AWS_BEARER_TOKEN_BEDROCK", "AWS_SESSION_TOKEN", "AWS_PROFILE"): + monkeypatch.delenv(env_var, raising=False) + + handler = BaseLLMHTTPHandler() + provider_config = AmazonAnthropicClaudeMessagesConfig() + litellm_params = GenericLiteLLMParams( + aws_access_key_id="AKIAIOSFODNN7EXAMPLE", + aws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + aws_region_name="us-east-1", + ) + request_url = "https://bedrock-runtime.us-east-1.amazonaws.com/model/test-model/invoke" + request_body = { + "anthropic_version": "bedrock-2023-05-31", + "max_tokens": 100, + "messages": [ + {"role": "user", "content": "hi"}, + { + "role": "assistant", + "content": [ + {"type": "thinking", "thinking": "x", "signature": ""}, + {"type": "text", "text": "ok"}, + ], + }, + {"role": "user", "content": "continue"}, + ], + } + first_attempt_headers, signed_json_body = provider_config.sign_request( + headers={"Content-Type": "application/json"}, + optional_params=dict(litellm_params), + request_data=request_body, + api_base=request_url, + api_key=None, + stream=False, + fake_stream=False, + model="test-model", + ) + + posts: list = [] + invalid_signature_response = httpx.Response( + 400, + text='{"message": "messages.1.content.0: Invalid `signature` in `thinking` block"}', + request=httpx.Request("POST", request_url), + ) + ok_response = httpx.Response(200, json={"id": "msg_1"}, request=httpx.Request("POST", request_url)) + + class FakeAsyncClient: + async def post(self, url, headers, data, stream=False, logging_obj=None): + posts.append({"headers": dict(headers), "data": data}) + return invalid_signature_response if len(posts) == 1 else ok_response + + logging_obj = Mock() + logging_obj.model_call_details = {} + + response = await handler._async_post_anthropic_messages_with_http_error_retry( + async_httpx_client=FakeAsyncClient(), + request_url=request_url, + headers=dict(first_attempt_headers), + signed_json_body=signed_json_body, + request_body=request_body, + stream=False, + logging_obj=logging_obj, + provider_config=provider_config, + litellm_params=litellm_params, + api_key=None, + model="test-model", + ) + + assert response.status_code == 200 + assert len(posts) == 2 + retry_payload = json.loads(posts[1]["data"]) + retry_blocks = [ + block + for message in retry_payload["messages"] + if isinstance(message.get("content"), list) + for block in message["content"] + ] + assert retry_blocks and all(block["type"] != "thinking" for block in retry_blocks) + retry_authorization = posts[1]["headers"]["Authorization"] + assert retry_authorization.startswith("AWS4-HMAC-SHA256") + assert retry_authorization != first_attempt_headers["Authorization"] diff --git a/tests/test_litellm/llms/dashscope/test_dashscope_cost_calculator.py b/tests/test_litellm/llms/dashscope/test_dashscope_cost_calculator.py index 05279202e8e..6041a8c8377 100644 --- a/tests/test_litellm/llms/dashscope/test_dashscope_cost_calculator.py +++ b/tests/test_litellm/llms/dashscope/test_dashscope_cost_calculator.py @@ -131,6 +131,71 @@ class TestDashscopeCostCalculator: assert math.isclose(prompt_cost, expected_total_prompt_cost, rel_tol=1e-10) + def _register_string_valued_tiered_model(self, model_key: str) -> None: + """Register a model whose tier costs are strings, mimicking YAML config parsing.""" + litellm.model_cost[model_key] = { + "litellm_provider": "dashscope", + "mode": "chat", + "tiered_pricing": [ + { + "range": [0, 1000], + "input_cost_per_token": "4e-07", + "output_cost_per_token": "1.6e-06", + }, + { + "range": [1000, 2000], + "input_cost_per_token": "8e-07", + "output_cost_per_token": "3.2e-06", + }, + ], + } + + def test_dashscope_tiered_pricing_string_costs_within_tier(self): + """ + Regression: YAML-parsed tier costs can be strings (e.g. "4e-07"). Costs that + fall entirely within a single tier must still be computed as floats. + """ + self._register_string_valued_tiered_model("dashscope/qwen-str-tier-test") + + usage = Usage(prompt_tokens=500, completion_tokens=200) + prompt_cost, completion_cost = dashscope_cost_per_token( + model="qwen-str-tier-test", usage=usage + ) + + expected_prompt_cost = 500 * float("4e-07") + expected_completion_cost = 200 * float("1.6e-06") + + assert prompt_cost > 0 + assert completion_cost > 0 + assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-10) + assert math.isclose(completion_cost, expected_completion_cost, rel_tol=1e-10) + + def test_dashscope_tiered_pricing_string_costs_exceeding_highest_tier(self): + """ + Regression: string-valued tier costs must also be coerced in the + remaining-tokens path that charges tokens above the highest tier. + """ + self._register_string_valued_tiered_model("dashscope/qwen-str-tier-test") + + usage = Usage(prompt_tokens=2500, completion_tokens=3000) + prompt_cost, completion_cost = dashscope_cost_per_token( + model="qwen-str-tier-test", usage=usage + ) + + # prompt: 1000 @ tier1 + 1000 @ tier2 + 500 remaining @ tier2 rate + expected_prompt_cost = ( + (1000 * float("4e-07")) + (1000 * float("8e-07")) + (500 * float("8e-07")) + ) + # completion: 1000 @ tier1 + 1000 @ tier2 + 1000 remaining @ tier2 rate + expected_completion_cost = ( + (1000 * float("1.6e-06")) + (1000 * float("3.2e-06")) + (1000 * float("3.2e-06")) + ) + + assert prompt_cost > 0 + assert completion_cost > 0 + assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-10) + assert math.isclose(completion_cost, expected_completion_cost, rel_tol=1e-10) + def test_dashscope_tiered_pricing_exceeding_highest_tier(self): """ Tests tiered pricing when token count exceeds the highest defined tier range. diff --git a/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py b/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py index cfdb76a97f4..00f3e7a6faf 100644 --- a/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py +++ b/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py @@ -416,3 +416,10 @@ def test_transform_request_keeps_parallel_tool_calls_for_claude(): )["messages"] assert len([m for m in result if m.get("role") == "assistant"]) == 1 + + +def test_databricks_config_probes_capabilities_under_databricks_namespace(): + """Inherited AnthropicConfig capability probes read ``self.custom_llm_provider``; + without this override they probed the ``anthropic`` cost-map namespace and + ignored the exact ``databricks/databricks-claude-*`` entries.""" + assert DatabricksConfig().custom_llm_provider == "databricks" diff --git a/tests/test_litellm/llms/gdc/chat/test_gdc_chat_transformation.py b/tests/test_litellm/llms/gdc/chat/test_gdc_chat_transformation.py new file mode 100644 index 00000000000..d106cf7ea21 --- /dev/null +++ b/tests/test_litellm/llms/gdc/chat/test_gdc_chat_transformation.py @@ -0,0 +1,717 @@ +import os +import sys +from unittest.mock import MagicMock, patch + +import pytest + +# Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../../../..")) + +import litellm +from litellm.llms.gdc.chat.transformation import GDCGeminiConfig + +TEST_API_KEY = '{"type": "gdch_service_account", "project_id": "test-project"}' +TEST_MODEL = "gdc/gemini-2.5-flash" +TEST_API_BASE = "https://gdc-endpoint.com" +TEST_PROJECT = "test-project" +TEST_LOCATION = "test-location" + + +class TestGDCGeminiConfig: + def test_get_complete_url(self): + config = GDCGeminiConfig() + url = config.get_complete_url( + api_base=TEST_API_BASE, + api_key=None, + model=TEST_MODEL, + optional_params={ + "vertex_project": TEST_PROJECT, + "vertex_location": TEST_LOCATION, + }, + litellm_params={}, + ) + assert ( + url + == f"{TEST_API_BASE}/v1/projects/{TEST_PROJECT}/locations/{TEST_LOCATION}/chat/completions" + ) + + def test_get_complete_url_adds_https_scheme(self): + config = GDCGeminiConfig() + url = config.get_complete_url( + api_base="gdc-endpoint.com", + api_key=None, + model=TEST_MODEL, + optional_params={}, + litellm_params={ + "vertex_project": TEST_PROJECT, + "vertex_location": TEST_LOCATION, + }, + ) + assert url.startswith("https://gdc-endpoint.com/v1/projects/") + + def test_get_complete_url_preformed_base_returned_as_is(self): + config = GDCGeminiConfig() + preformed = f"{TEST_API_BASE}/v1/projects/{TEST_PROJECT}/locations/{TEST_LOCATION}/chat/completions" + url = config.get_complete_url( + api_base=preformed, + api_key=None, + model=TEST_MODEL, + optional_params={"vertex_project": TEST_PROJECT}, + litellm_params={}, + ) + assert url == preformed + + def test_get_complete_url_missing_api_base(self): + config = GDCGeminiConfig() + with pytest.raises(Exception, match="api_base/host is required for GDC Gemini"): + config.get_complete_url( + api_base=None, + api_key=None, + model=TEST_MODEL, + optional_params={ + "vertex_project": TEST_PROJECT, + "vertex_location": TEST_LOCATION, + }, + litellm_params={}, + ) + + def test_get_complete_url_missing_project(self): + config = GDCGeminiConfig() + with pytest.raises(Exception, match="project is required for GDC Gemini"): + config.get_complete_url( + api_base=TEST_API_BASE, + api_key=None, + model=TEST_MODEL, + optional_params={}, + litellm_params={}, + ) + + def test_get_complete_url_missing_location(self): + config = GDCGeminiConfig() + with pytest.raises(Exception, match="location is required for GDC Gemini"): + config.get_complete_url( + api_base=TEST_API_BASE, + api_key=None, + model=TEST_MODEL, + optional_params={"vertex_project": TEST_PROJECT}, + litellm_params={}, + ) + + def test_get_complete_url_accepts_vertex_ai_aliases(self): + config = GDCGeminiConfig() + url = config.get_complete_url( + api_base=TEST_API_BASE, + api_key=None, + model=TEST_MODEL, + optional_params={}, + litellm_params={ + "vertex_ai_project": TEST_PROJECT, + "vertex_ai_location": TEST_LOCATION, + }, + ) + assert ( + url + == f"{TEST_API_BASE}/v1/projects/{TEST_PROJECT}/locations/{TEST_LOCATION}/chat/completions" + ) + + def test_get_complete_url_preformed_base_is_authoritative_over_litellm_params(self): + config = GDCGeminiConfig() + preformed = f"{TEST_API_BASE}/v1/projects/pinned-project/locations/pinned-loc/chat/completions" + url = config.get_complete_url( + api_base=preformed, + api_key=None, + model=TEST_MODEL, + optional_params={"vertex_project": "attacker-optional", "vertex_location": "attacker-loc"}, + litellm_params={ + "vertex_project": "attacker-project", + "vertex_location": "attacker-loc", + }, + ) + assert url == preformed + + def test_get_complete_url_preformed_base_needs_no_project_param(self): + config = GDCGeminiConfig() + preformed = f"{TEST_API_BASE}/v1/projects/pinned-project/locations/pinned-loc/chat/completions" + url = config.get_complete_url( + api_base=preformed, + api_key=None, + model=TEST_MODEL, + optional_params={}, + litellm_params={}, + ) + assert url == preformed + + def test_deployment_project_takes_precedence_over_request(self): + config = GDCGeminiConfig() + url = config.get_complete_url( + api_base=TEST_API_BASE, + api_key=None, + model=TEST_MODEL, + optional_params={ + "vertex_project": "caller-project", + "vertex_location": "caller-location", + }, + litellm_params={ + "vertex_project": "deployment-project", + "vertex_location": "deployment-location", + }, + ) + assert url == ( + f"{TEST_API_BASE}/v1/projects/deployment-project" + "/locations/deployment-location/chat/completions" + ) + + @patch("google.auth.load_credentials_from_dict") + @patch("requests.Session") + def test_validate_environment(self, mock_session, mock_load_creds): + mock_creds = MagicMock() + mock_creds.token = "mock-token" + mock_creds.with_gdch_audience.return_value = mock_creds + mock_load_creds.return_value = (mock_creds, None) + + mock_session_instance = MagicMock() + mock_session.return_value = mock_session_instance + + config = GDCGeminiConfig() + result = config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={ + "vertex_project": TEST_PROJECT, + "vertex_location": TEST_LOCATION, + }, + litellm_params={}, + api_key=TEST_API_KEY, + api_base=TEST_API_BASE, + ) + + assert result["Authorization"] == "Bearer mock-token" + assert result["Content-Type"] == "application/json" + assert result["x-goog-user-project"] == f"projects/{TEST_PROJECT}" + + mock_creds.with_gdch_audience.assert_called_once_with(TEST_API_BASE) + mock_creds.refresh.assert_called_once() + assert mock_session_instance.verify is True + + def test_validate_environment_strips_audience_trailing_slash(self): + config = GDCGeminiConfig() + mock_creds = MagicMock() + mock_creds.token = "mock-token" + mock_creds.with_gdch_audience.return_value = mock_creds + + with patch( + "google.auth.load_credentials_from_dict", return_value=(mock_creds, None) + ), patch("requests.Session"): + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={"vertex_project": TEST_PROJECT}, + api_key=TEST_API_KEY, + api_base="https://gdc-endpoint.com/", + ) + + mock_creds.with_gdch_audience.assert_called_once_with("https://gdc-endpoint.com") + + def test_validate_environment_audience_is_host_for_preformed_base(self): + config = GDCGeminiConfig() + mock_creds = MagicMock() + mock_creds.token = "mock-token" + mock_creds.with_gdch_audience.return_value = mock_creds + + with patch( + "google.auth.load_credentials_from_dict", return_value=(mock_creds, None) + ), patch("requests.Session"): + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={ + "vertex_project": "deployment-project", + "vertex_location": "deployment-loc", + }, + api_key=TEST_API_KEY, + api_base=f"{TEST_API_BASE}/v1/projects/embedded/locations/embedded/chat/completions", + ) + + mock_creds.with_gdch_audience.assert_called_once_with(TEST_API_BASE) + + def test_validate_environment_missing_api_base(self, monkeypatch): + monkeypatch.setattr(litellm, "api_base", None, raising=False) + monkeypatch.setattr(litellm, "gdc_api_base", None, raising=False) + config = GDCGeminiConfig() + with pytest.raises(Exception, match="api_base/host is required for GDC Gemini"): + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={"vertex_project": TEST_PROJECT}, + api_key=TEST_API_KEY, + api_base=None, + ) + + def test_validate_environment_missing_api_key(self): + config = GDCGeminiConfig() + with pytest.raises(Exception, match="api_key is required for GDC Gemini"): + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={"vertex_project": TEST_PROJECT}, + api_key=None, + api_base=TEST_API_BASE, + ) + + def test_validate_environment_missing_project(self): + config = GDCGeminiConfig() + with pytest.raises(Exception, match="project is required for GDC Gemini"): + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={}, + api_key=TEST_API_KEY, + api_base=TEST_API_BASE, + ) + + def test_validate_environment_raw_token_used_as_bearer(self): + config = GDCGeminiConfig() + headers = config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={"vertex_project": TEST_PROJECT}, + api_key="ya29.raw-access-token", + api_base=TEST_API_BASE, + ) + assert headers["Authorization"] == "Bearer ya29.raw-access-token" + assert headers["x-goog-user-project"] == f"projects/{TEST_PROJECT}" + + def test_validate_environment_bad_credentials_raise_auth_error(self): + config = GDCGeminiConfig() + with patch( + "google.auth.load_credentials_from_dict", + side_effect=ValueError("bad creds"), + ): + with pytest.raises( + Exception, match="Failed to load service account credentials" + ): + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={"vertex_project": TEST_PROJECT}, + api_key=TEST_API_KEY, + api_base=TEST_API_BASE, + ) + + def test_validate_environment_string_false_disables_token_caching(self): + config = GDCGeminiConfig() + mock_creds = MagicMock() + mock_creds.token = "mock-token" + mock_creds.with_gdch_audience.return_value = mock_creds + + with patch( + "google.auth.load_credentials_from_dict", return_value=(mock_creds, None) + ), patch("requests.Session"), patch.object( + config, "_cached_fetch_token" + ) as mock_cached: + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={ + "vertex_project": TEST_PROJECT, + "gdc_token_caching": "false", + }, + api_key=TEST_API_KEY, + api_base=TEST_API_BASE, + ) + + mock_cached.assert_not_called() + + def test_validate_environment_token_caching_path(self): + config = GDCGeminiConfig() + mock_creds = MagicMock() + mock_creds.token = "cached-token" + mock_creds.valid = True + mock_creds.with_gdch_audience.return_value = mock_creds + + with patch( + "google.auth.load_credentials_from_dict", return_value=(mock_creds, None) + ): + headers = config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={ + "vertex_project": TEST_PROJECT, + "gdc_token_caching": True, + }, + api_key=TEST_API_KEY, + api_base=TEST_API_BASE, + ) + + assert headers["Authorization"] == "Bearer cached-token" + mock_creds.refresh.assert_not_called() + + def test_validate_environment_preserves_content_type_but_rebinds_quota_project(self): + config = GDCGeminiConfig() + headers = config.validate_environment( + headers={ + "Content-Type": "text/plain", + "x-goog-user-project": "projects/attacker", + }, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={"vertex_project": TEST_PROJECT}, + api_key="raw-token", + api_base=TEST_API_BASE, + ) + assert headers["Content-Type"] == "text/plain" + assert headers["x-goog-user-project"] == f"projects/{TEST_PROJECT}" + + @pytest.mark.parametrize( + "header_name", ["x-goog-user-project", "X-Goog-User-Project", "X-GOOG-USER-PROJECT"] + ) + def test_validate_environment_strips_caller_forwarded_quota_header(self, header_name): + config = GDCGeminiConfig() + mock_creds = MagicMock() + mock_creds.token = "tok" + mock_creds.with_gdch_audience.return_value = mock_creds + preformed = f"{TEST_API_BASE}/v1/projects/deployment-proj/locations/us-central1/chat/completions" + with patch( + "google.auth.load_credentials_from_dict", return_value=(mock_creds, None) + ): + headers = config.validate_environment( + headers={header_name: "projects/attacker"}, + model=TEST_MODEL, + messages=[], + optional_params={"vertex_project": "attacker-proj"}, + litellm_params={}, + api_key=TEST_API_KEY, + api_base=preformed, + ) + quota_values = [v for k, v in headers.items() if k.lower() == "x-goog-user-project"] + assert quota_values == ["projects/deployment-proj"] + + @pytest.mark.parametrize( + "bad", ["p/locations/l/chat/completions?", "a/b", "a?b", "a#b", "..", "a b", "a:b", "a%2Fb"] + ) + def test_get_complete_url_rejects_project_path_injection(self, bad): + config = GDCGeminiConfig() + with pytest.raises(Exception, match="vertex_project must be a plain identifier"): + config.get_complete_url( + api_base=TEST_API_BASE, + api_key=None, + model=TEST_MODEL, + optional_params={"vertex_project": bad, "vertex_location": TEST_LOCATION}, + litellm_params={}, + ) + + @pytest.mark.parametrize("bad", ["../../evil", "l/chat/completions", "l?x", ".."]) + def test_get_complete_url_rejects_location_path_injection(self, bad): + config = GDCGeminiConfig() + with pytest.raises(Exception, match="vertex_location must be a plain identifier"): + config.get_complete_url( + api_base=TEST_API_BASE, + api_key=None, + model=TEST_MODEL, + optional_params={"vertex_project": TEST_PROJECT, "vertex_location": bad}, + litellm_params={}, + ) + + @pytest.mark.parametrize("good", ["test-project", "us-central1", "123456", "proj_1", "MyProj-2"]) + def test_get_complete_url_accepts_valid_ids(self, good): + config = GDCGeminiConfig() + url = config.get_complete_url( + api_base=TEST_API_BASE, + api_key=None, + model=TEST_MODEL, + optional_params={"vertex_project": good, "vertex_location": good}, + litellm_params={}, + ) + assert url == f"{TEST_API_BASE}/v1/projects/{good}/locations/{good}/chat/completions" + + def test_validate_environment_rejects_project_path_injection(self): + config = GDCGeminiConfig() + with pytest.raises(Exception, match="vertex_project must be a plain identifier"): + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={"vertex_project": "p/../admin"}, + litellm_params={}, + api_key="raw-token", + api_base=TEST_API_BASE, + ) + + def test_validate_environment_quota_header_bound_to_deployment_url(self): + config = GDCGeminiConfig() + mock_creds = MagicMock() + mock_creds.token = "tok" + mock_creds.with_gdch_audience.return_value = mock_creds + preformed = f"{TEST_API_BASE}/v1/projects/deployment-proj/locations/us-central1/chat/completions" + with patch( + "google.auth.load_credentials_from_dict", return_value=(mock_creds, None) + ): + headers = config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={"vertex_project": "attacker-proj"}, + litellm_params={}, + api_key=TEST_API_KEY, + api_base=preformed, + ) + assert headers["x-goog-user-project"] == "projects/deployment-proj" + + def test_validate_environment_quota_header_pinned_to_preformed_url(self): + config = GDCGeminiConfig() + mock_creds = MagicMock() + mock_creds.token = "tok" + mock_creds.with_gdch_audience.return_value = mock_creds + preformed = f"{TEST_API_BASE}/v1/projects/url-proj/locations/us-central1/chat/completions" + with patch( + "google.auth.load_credentials_from_dict", return_value=(mock_creds, None) + ): + headers = config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={"vertex_project": "attacker-proj"}, + litellm_params={"vertex_project": "override-proj"}, + api_key=TEST_API_KEY, + api_base=preformed, + ) + assert headers["x-goog-user-project"] == "projects/url-proj" + + def test_transform_request(self): + config = GDCGeminiConfig() + data = config.transform_request( + model=TEST_MODEL, + messages=[{"role": "user", "content": "Hello"}], + optional_params={ + "vertex_project": TEST_PROJECT, + "vertex_location": TEST_LOCATION, + }, + litellm_params={"ssl_verify": True}, + headers={}, + ) + assert data["model"] == "gemini-2.5-flash" + assert "vertex_project" not in data + assert "vertex_location" not in data + assert "ssl_verify" not in data + + def test_load_creds_from_key_ignores_file_paths(self, tmp_path): + config = GDCGeminiConfig() + creds_file = tmp_path / "service_account.json" + creds_file.write_text( + '{"type": "gdch_service_account", "project_id": "host-only-project"}' + ) + + creds, is_service_account = config._load_creds_from_key(str(creds_file)) + + assert creds is None + assert is_service_account is False + + def test_load_creds_from_key_rejects_non_gdch_credential_types(self): + config = GDCGeminiConfig() + external_account = ( + '{"type": "external_account", ' + '"token_url": "http://169.254.169.254/latest/api/token", ' + '"credential_source": {"url": "http://169.254.169.254/"}}' + ) + with patch( + "google.auth.load_credentials_from_dict", + return_value=(MagicMock(), None), + ) as mock_load: + with pytest.raises(ValueError, match="GDCH service account"): + config._load_creds_from_key(external_account) + mock_load.assert_not_called() + + def test_validate_environment_rejects_non_gdch_credential_without_refresh(self): + config = GDCGeminiConfig() + mock_creds = MagicMock() + mock_creds.token = "leaked-token" + mock_creds.with_gdch_audience.return_value = mock_creds + malicious = ( + '{"type": "external_account", ' + '"token_url": "http://169.254.169.254/latest/api/token"}' + ) + + with patch( + "google.auth.load_credentials_from_dict", + return_value=(mock_creds, None), + ) as mock_load, patch("requests.Session") as mock_session: + with pytest.raises( + Exception, match="Failed to load service account credentials" + ): + config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={"vertex_project": TEST_PROJECT}, + api_key=malicious, + api_base=TEST_API_BASE, + ) + + mock_load.assert_not_called() + mock_session.assert_not_called() + mock_creds.refresh.assert_not_called() + + def test_validate_environment_does_not_read_api_key_file_path(self, tmp_path): + config = GDCGeminiConfig() + creds_file = tmp_path / "service_account.json" + creds_file.write_text( + '{"type": "service_account", "project_id": "host-only-project"}' + ) + + headers = config.validate_environment( + headers={}, + model=TEST_MODEL, + messages=[], + optional_params={}, + litellm_params={ + "vertex_project": TEST_PROJECT, + "vertex_location": TEST_LOCATION, + }, + api_key=str(creds_file), + api_base=TEST_API_BASE, + ) + + assert headers["Authorization"] == f"Bearer {creds_file}" + assert headers["x-goog-user-project"] == f"projects/{TEST_PROJECT}" + + @pytest.mark.parametrize( + "val, env_value, default, expected", + [ + (True, None, True, True), + (False, "true", True, False), + ("literal", None, True, "literal"), + (None, None, True, True), + (None, None, False, False), + (None, "true", False, True), + (None, "1", False, True), + (None, "on", False, True), + (None, "false", True, False), + (None, "0", True, False), + (None, "off", True, False), + (None, "verbose", True, "verbose"), + ], + ) + def test_read_env_bool(self, monkeypatch, val, env_value, default, expected): + config = GDCGeminiConfig() + env_var = "GDC_TEST_FLAG" + if env_value is None: + monkeypatch.delenv(env_var, raising=False) + else: + monkeypatch.setenv(env_var, env_value) + assert config._read_env_bool(val, env_var, default=default) == expected + + def test_cached_fetch_token_keys_by_credential(self): + config = GDCGeminiConfig() + + def make_creds(token): + creds = MagicMock() + creds.with_gdch_audience.return_value = creds + creds.valid = True + creds.token = token + return creds + + creds_a = make_creds("token-a") + creds_b = make_creds("token-b") + + assert ( + config._cached_fetch_token(creds_a, TEST_API_BASE, True, api_key="key-a") + == "token-a" + ) + assert ( + config._cached_fetch_token(creds_b, TEST_API_BASE, True, api_key="key-b") + == "token-b" + ) + # same credential identity reuses the cached entry + config._cached_fetch_token(creds_a, TEST_API_BASE, True, api_key="key-a") + creds_a.with_gdch_audience.assert_called_once() + + def test_cached_fetch_token_refreshes_when_invalid(self): + config = GDCGeminiConfig() + creds = MagicMock() + creds.with_gdch_audience.return_value = creds + creds.valid = False + creds.token = "refreshed" + + with patch.object(config, "_fetch_auth") as mock_fetch: + token = config._cached_fetch_token( + creds, TEST_API_BASE, True, api_key="key" + ) + + assert token == "refreshed" + mock_fetch.assert_called_once() + + def test_init_sets_up_lock_and_cache(self): + config = GDCGeminiConfig() + assert config._gdch_creds_cache == {} + assert config._creds_lock is not None + + +class TestCompleteGDC: + @patch("litellm.main.base_llm_http_handler.completion") + def test_complete_gdc_resolves_key_and_base(self, mock_completion, monkeypatch): + from litellm.main import gdc_transformation + + mock_completion.return_value = MagicMock() + monkeypatch.setattr(litellm, "gdc_key", "resolved-key", raising=False) + monkeypatch.setattr( + litellm, "gdc_api_base", "https://resolved-base.com", raising=False + ) + monkeypatch.setattr(litellm, "api_base", None, raising=False) + + litellm.completion( + model="gdc/gemini-2.5-flash", + messages=[{"role": "user", "content": "hi"}], + vertex_project=TEST_PROJECT, + vertex_location=TEST_LOCATION, + ) + + assert mock_completion.called + _, kwargs = mock_completion.call_args + assert kwargs["custom_llm_provider"] == "gdc" + assert kwargs["api_key"] == "resolved-key" + assert kwargs["api_base"] == "https://resolved-base.com" + assert kwargs["provider_config"] is gdc_transformation + + @patch("litellm.main.base_llm_http_handler.completion") + def test_complete_gdc_prefers_gdc_api_base_over_global( + self, mock_completion, monkeypatch + ): + mock_completion.return_value = MagicMock() + monkeypatch.setattr(litellm, "gdc_key", "resolved-key", raising=False) + monkeypatch.setattr( + litellm, "gdc_api_base", "https://gdc-specific.com", raising=False + ) + monkeypatch.setattr( + litellm, "api_base", "https://other-provider.com", raising=False + ) + + litellm.completion( + model="gdc/gemini-2.5-flash", + messages=[{"role": "user", "content": "hi"}], + vertex_project=TEST_PROJECT, + vertex_location=TEST_LOCATION, + ) + + _, kwargs = mock_completion.call_args + assert kwargs["api_base"] == "https://gdc-specific.com" diff --git a/tests/test_litellm/llms/github_copilot/messages/__init__.py b/tests/test_litellm/llms/github_copilot/messages/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/llms/github_copilot/messages/test_github_copilot_messages_transformation.py b/tests/test_litellm/llms/github_copilot/messages/test_github_copilot_messages_transformation.py new file mode 100644 index 00000000000..8ed84b3ed8d --- /dev/null +++ b/tests/test_litellm/llms/github_copilot/messages/test_github_copilot_messages_transformation.py @@ -0,0 +1,336 @@ +import os +import sys +from unittest.mock import MagicMock + +import pytest + +sys.path.insert(0, os.path.abspath("../..")) + +from litellm.exceptions import AuthenticationError +from litellm.llms.github_copilot.common_utils import GetAPIKeyError +from litellm.llms.github_copilot.messages.transformation import ( + GithubCopilotAnthropicMessagesConfig, +) + + +def test_github_copilot_anthropic_messages_config_init(): + """Test GithubCopilotAnthropicMessagesConfig initialization.""" + config = GithubCopilotAnthropicMessagesConfig() + assert config is not None + assert hasattr(config, "authenticator") + + +def test_github_copilot_anthropic_messages_get_complete_url(): + """get_complete_url builds the /v1/messages URL from the base it is handed. + + In the request flow that ``api_base`` is the value already resolved by + validate_anthropic_messages_environment (the authenticated Copilot host); the + caller-supplied base is discarded there, not here (see the validate tests). + """ + config = GithubCopilotAnthropicMessagesConfig() + config.authenticator = MagicMock() + config.authenticator.get_api_base.return_value = None + + # No api_base supplied and no authenticator base -> default Copilot endpoint. + url = config.get_complete_url( + api_base=None, + api_key=None, + model="github_copilot/claude-haiku-4.5", + optional_params={}, + litellm_params={}, + ) + assert url == "https://api.githubcopilot.com/v1/messages" + # Falls back to a single authenticator read, not a hard-coded second one. + config.authenticator.get_api_base.assert_called() + + # The resolved (validated) base passed in is reused verbatim; no extra read. + config.authenticator.get_api_base.reset_mock() + url = config.get_complete_url( + api_base="https://api.business.githubcopilot.com", + api_key=None, + model="github_copilot/claude-haiku-4.5", + optional_params={}, + litellm_params={}, + ) + assert url == "https://api.business.githubcopilot.com/v1/messages" + config.authenticator.get_api_base.assert_not_called() + + # A trailing slash on the base must not produce a double-slash URL. + url = config.get_complete_url( + api_base="https://api.business.githubcopilot.com/", + api_key=None, + model="github_copilot/claude-haiku-4.5", + optional_params={}, + litellm_params={}, + ) + assert url == "https://api.business.githubcopilot.com/v1/messages" + + # An already-complete /v1/messages base is left untouched. + url = config.get_complete_url( + api_base="https://api.githubcopilot.com/v1/messages", + api_key=None, + model="github_copilot/claude-haiku-4.5", + optional_params={}, + litellm_params={}, + ) + assert url == "https://api.githubcopilot.com/v1/messages" + + +def test_github_copilot_anthropic_messages_get_complete_url_normalizes_authenticator_trailing_slash(): + """A tenant base with a trailing slash from the authenticator fallback must + not yield a double-slash URL.""" + config = GithubCopilotAnthropicMessagesConfig() + config.authenticator = MagicMock() + config.authenticator.get_api_base.return_value = "https://api.business.githubcopilot.com/" + + url = config.get_complete_url( + api_base=None, + api_key=None, + model="github_copilot/claude-haiku-4.5", + optional_params={}, + litellm_params={}, + ) + assert url == "https://api.business.githubcopilot.com/v1/messages" + + +def test_github_copilot_anthropic_messages_validate_environment(): + """Test environment validation and header injection.""" + config = GithubCopilotAnthropicMessagesConfig() + + # Mock the authenticator + config.authenticator = MagicMock() + config.authenticator.get_api_key.return_value = "gh.test-key-123" + config.authenticator.get_api_base.return_value = None + + headers = {} + # Pass a hostile api_base to confirm it is ignored. + validated_headers, api_base = config.validate_anthropic_messages_environment( + headers=headers, + model="github_copilot/claude-haiku-4.5", + messages=[{"role": "user", "content": "Hello"}], + optional_params={}, + litellm_params={}, + api_key=None, + api_base="https://attacker.example.com", + ) + + assert "copilot-integration-id" in validated_headers + assert validated_headers["copilot-integration-id"] == "vscode-chat" + assert "Authorization" in validated_headers + assert "anthropic-version" in validated_headers + assert validated_headers["anthropic-version"] == "2023-06-01" + # /v1/messages must use the messages-proxy intent so the Copilot backend + # enables Anthropic-native features (context_management, thinking, etc.). + assert validated_headers["openai-intent"] == "messages-proxy" + assert validated_headers["x-interaction-type"] == "messages-proxy" + assert validated_headers["x-github-api-version"] == "2026-06-01" + assert api_base == "https://api.githubcopilot.com" + + +def test_github_copilot_anthropic_messages_validate_environment_injects_beta_headers(): + """Anthropic-beta headers must be auto-injected for advanced features + (context_management, output_format, etc.) — matches the parent + AnthropicMessagesConfig contract.""" + config = GithubCopilotAnthropicMessagesConfig() + config.authenticator = MagicMock() + config.authenticator.get_api_key.return_value = "gh.test-key" + config.authenticator.get_api_base.return_value = None + + validated_headers, _ = config.validate_anthropic_messages_environment( + headers={}, + model="github_copilot/claude-haiku-4.5", + messages=[{"role": "user", "content": "Hello"}], + optional_params={"output_format": {"type": "json_object"}}, + litellm_params={}, + api_key=None, + api_base=None, + ) + + assert "anthropic-beta" in validated_headers + assert "structured-outputs-2025-11-13" in validated_headers["anthropic-beta"] + + +def test_github_copilot_anthropic_messages_validate_environment_preserves_caller_anthropic_version(): + """Caller-supplied anthropic-version must be forwarded verbatim.""" + config = GithubCopilotAnthropicMessagesConfig() + config.authenticator = MagicMock() + config.authenticator.get_api_key.return_value = "gh.test-key" + config.authenticator.get_api_base.return_value = None + + validated_headers, _ = config.validate_anthropic_messages_environment( + headers={"anthropic-version": "2024-10-22"}, + model="github_copilot/claude-haiku-4.5", + messages=[{"role": "user", "content": "Hello"}], + optional_params={}, + litellm_params={}, + api_key=None, + api_base=None, + ) + + assert validated_headers["anthropic-version"] == "2024-10-22" + + +def test_github_copilot_anthropic_messages_validate_environment_injects_context_management_beta(): + """context_management in optional_params must trigger the corresponding + anthropic-beta header so the Copilot backend accepts the field.""" + config = GithubCopilotAnthropicMessagesConfig() + config.authenticator = MagicMock() + config.authenticator.get_api_key.return_value = "gh.test-key" + config.authenticator.get_api_base.return_value = None + + validated_headers, _ = config.validate_anthropic_messages_environment( + headers={}, + model="github_copilot/claude-haiku-4.5", + messages=[{"role": "user", "content": "Hello"}], + optional_params={"context_management": {"edits": [{"type": "clear_tool_uses_20250919"}]}}, + litellm_params={}, + api_key=None, + api_base=None, + ) + + assert validated_headers["openai-intent"] == "messages-proxy" + assert validated_headers["x-interaction-type"] == "messages-proxy" + assert "anthropic-beta" in validated_headers + assert "context-management-2025-06-27" in validated_headers["anthropic-beta"] + + +def test_github_copilot_anthropic_messages_validate_environment_auth_error(): + """Test error handling when authentication fails.""" + config = GithubCopilotAnthropicMessagesConfig() + + # Mock the authenticator to raise an error + config.authenticator = MagicMock() + config.authenticator.get_api_key.side_effect = GetAPIKeyError(status_code=401, message="No valid API key found") + + with pytest.raises(AuthenticationError): + config.validate_anthropic_messages_environment( + headers={}, + model="github_copilot/claude-haiku-4.5", + messages=[{"role": "user", "content": "Hello"}], + optional_params={}, + litellm_params={}, + api_key=None, + api_base=None, + ) + + +def test_github_copilot_anthropic_messages_supported_params(): + """Test supported parameters list.""" + config = GithubCopilotAnthropicMessagesConfig() + params = config.get_supported_anthropic_messages_params("github_copilot/claude-haiku-4.5") + + # Should inherit from AnthropicMessagesConfig + assert "messages" in params + assert "model" in params + assert "max_tokens" in params + assert "thinking" in params + + +def test_provider_config_manager_dispatches_claude_to_copilot_messages_config(): + """ProviderConfigManager must return the Copilot Anthropic Messages config + for Claude models served via github_copilot.""" + from litellm.types.utils import LlmProviders + from litellm.utils import ProviderConfigManager + + config = ProviderConfigManager.get_provider_anthropic_messages_config( + model="github_copilot/claude-haiku-4.5", + provider=LlmProviders.GITHUB_COPILOT, + ) + + assert isinstance(config, GithubCopilotAnthropicMessagesConfig) + + +def test_provider_config_manager_skips_non_claude_copilot_models(): + """Non-Claude github_copilot models (e.g. gpt-*) must not be routed through + the Anthropic Messages dispatch.""" + from litellm.types.utils import LlmProviders + from litellm.utils import ProviderConfigManager + + config = ProviderConfigManager.get_provider_anthropic_messages_config( + model="github_copilot/gpt-5-mini", + provider=LlmProviders.GITHUB_COPILOT, + ) + + assert config is None + + +def test_github_copilot_anthropic_messages_validate_environment_normalizes_trailing_slash(): + """A tenant base with a trailing slash from the authenticator must be + normalized so the URL built downstream has no double slash.""" + config = GithubCopilotAnthropicMessagesConfig() + config.authenticator = MagicMock() + config.authenticator.get_api_key.return_value = "gh.test-key" + config.authenticator.get_api_base.return_value = "https://api.business.githubcopilot.com/" + + _, api_base = config.validate_anthropic_messages_environment( + headers={}, + model="github_copilot/claude-haiku-4.5", + messages=[{"role": "user", "content": "Hello"}], + optional_params={}, + litellm_params={}, + api_key=None, + api_base="https://attacker.example.com", + ) + + assert api_base == "https://api.business.githubcopilot.com" + + +def test_github_copilot_config_disables_anthropic_beta_filtering(): + """Copilot's /v1/messages is a native Anthropic passthrough, so injected + anthropic-beta values (context_management, structured outputs, ...) must be + forwarded verbatim. The default provider-scoped filter would drop them + because github_copilot has no entry in the beta headers config; a regression + here would silently disable header-gated Anthropic features for Copilot.""" + from litellm.anthropic_beta_headers_manager import update_headers_with_filtered_beta + from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, + ) + + config = GithubCopilotAnthropicMessagesConfig() + assert config.should_filter_anthropic_beta_headers() is False + assert AnthropicMessagesConfig().should_filter_anthropic_beta_headers() is True + + config.authenticator = MagicMock() + config.authenticator.get_api_key.return_value = "gh.test-key" + config.authenticator.get_api_base.return_value = None + + headers, _ = config.validate_anthropic_messages_environment( + headers={}, + model="github_copilot/claude-haiku-4.5", + messages=[{"role": "user", "content": "Hello"}], + optional_params={"context_management": {"edits": [{"type": "clear_tool_uses_20250919"}]}}, + litellm_params={}, + api_key=None, + api_base=None, + ) + + assert "context-management-2025-06-27" in headers["anthropic-beta"] + + # The override is load-bearing: had the config opted into the provider-scoped + # filter, the handler would have run it and dropped every value, since + # github_copilot has no mapping. Prove that here so a regression that flips + # should_filter back on is caught as the silent feature breakage it causes. + stripped = update_headers_with_filtered_beta(headers=dict(headers), provider="github_copilot") + assert "anthropic-beta" not in stripped + + +def test_github_copilot_config_does_not_handle_web_search_natively(): + """Copilot's /v1/messages does not run web_search, so its config must report + handles_web_search_natively() == False. This is what keeps the web-search + interception handler short-circuiting Copilot instead of routing to it, even + though Copilot now has a BaseAnthropicMessagesConfig. The base Anthropic + config (bedrock/vertex/anthropic path) must report True.""" + from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, + ) + + assert GithubCopilotAnthropicMessagesConfig().handles_web_search_natively() is False + assert AnthropicMessagesConfig().handles_web_search_natively() is True + + +def test_github_copilot_messages_config_probes_capabilities_under_copilot_namespace(): + """Capability probes in the shared pass-through helpers read + ``self.custom_llm_provider``; without this override they probed the + ``anthropic`` namespace and ignored the exact ``github_copilot/claude-*`` + cost-map entries.""" + assert GithubCopilotAnthropicMessagesConfig().custom_llm_provider == "github_copilot" diff --git a/tests/test_litellm/llms/openai/chat/test_openai_gpt_transformation.py b/tests/test_litellm/llms/openai/chat/test_openai_gpt_transformation.py index 20a1bf85751..1894294ea55 100644 --- a/tests/test_litellm/llms/openai/chat/test_openai_gpt_transformation.py +++ b/tests/test_litellm/llms/openai/chat/test_openai_gpt_transformation.py @@ -187,6 +187,84 @@ class TestOpenAIChatCompletionStreamingHandler: assert result.usage.completion_tokens == 350 assert result.usage.total_tokens == 14147 + def test_chunk_parser_raises_on_in_body_error_payload(self): + """vLLM/sglang return HTTP 200 streams whose body carries the error, + e.g. data: {"error": {..., "code": 400}}. chunk_parser must surface it + as a provider error instead of parsing an empty chunk that silently + ends the stream (https://github.com/BerriAI/litellm/issues/25492).""" + from litellm.llms.openai.common_utils import OpenAIError + + handler = OpenAIChatCompletionStreamingHandler( + streaming_response=None, sync_stream=True + ) + + error_chunk = { + "error": { + "object": "error", + "message": "The model is not multimodal. Please remove image inputs.", + "type": "BadRequestError", + "param": None, + "code": 400, + } + } + + with pytest.raises(OpenAIError) as excinfo: + handler.chunk_parser(error_chunk) + + assert excinfo.value.status_code == 400 + assert "not multimodal" in excinfo.value.message + + def test_chunk_parser_error_payload_without_usable_code_maps_to_500(self): + """OpenAI-style error payloads may carry a string code (e.g. + "invalid_api_key") or none at all; those must map to 500, not crash.""" + from litellm.llms.openai.common_utils import OpenAIError + + handler = OpenAIChatCompletionStreamingHandler( + streaming_response=None, sync_stream=True + ) + + with pytest.raises(OpenAIError) as excinfo: + handler.chunk_parser( + {"error": {"message": "engine crashed", "code": "server_error"}} + ) + assert excinfo.value.status_code == 500 + assert "engine crashed" in excinfo.value.message + + with pytest.raises(OpenAIError) as excinfo: + handler.chunk_parser({"error": "plain string error"}) + assert excinfo.value.status_code == 500 + assert "plain string error" in excinfo.value.message + + with pytest.raises(OpenAIError) as excinfo: + handler.chunk_parser({"error": {"type": "overloaded", "code": 503}}) + assert excinfo.value.status_code == 503 + assert excinfo.value.message == '{"type": "overloaded", "code": 503}' + + def test_chunk_parser_tolerates_null_error_field(self): + """A chunk that carries "error": null alongside real data must parse + normally, not raise.""" + handler = OpenAIChatCompletionStreamingHandler( + streaming_response=None, sync_stream=True + ) + + chunk = { + "id": "gen-123", + "created": 1234567890, + "model": "openai/gpt-4o-mini", + "object": "chat.completion.chunk", + "error": None, + "choices": [ + { + "index": 0, + "delta": {"role": "assistant", "content": "Hello"}, + "finish_reason": None, + } + ], + } + + result = handler.chunk_parser(chunk) + assert result.choices[0].delta.content == "Hello" + def test_chunk_parser_without_usage(self): """Test that chunk_parser works normally for chunks without usage.""" handler = OpenAIChatCompletionStreamingHandler( diff --git a/tests/test_litellm/llms/openai/test_is_model_gpt_5_model.py b/tests/test_litellm/llms/openai/test_is_model_gpt_5_model.py index 02dd9dade0a..1095819c98c 100644 --- a/tests/test_litellm/llms/openai/test_is_model_gpt_5_model.py +++ b/tests/test_litellm/llms/openai/test_is_model_gpt_5_model.py @@ -50,6 +50,10 @@ GPT5_MODELS = [ "gpt-5.5-pro", "gpt-5.5-2026-04-23", # dated variant "gpt-5.5-pro-2026-04-23", # dated variant + "gpt-5.6", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", "gpt-5.1-chat", # versioned chat — THE KEY REGRESSION CASE "gpt-5.2-chat", # versioned chat — also a regression case "gpt-5.3-chat", # versioned chat — THE KEY REGRESSION CASE @@ -112,6 +116,45 @@ class TestOpenAIGPT5ConfigIsModelGpt5Model: ), f"Expected '{model}' (gpt-5-chat family) NOT to be on the GPT-5 path" +# Models that are gpt-5.4 or newer. main.py gates the automatic switch to the +# /v1/responses bridge (when reasoning_effort is set and tools are passed) on +# is_model_gpt_5_4_plus_model, so the gpt-5.6 family must land on the True side. +GPT5_4_PLUS_MODELS = [ + "gpt-5.4", + "gpt-5.5", + "gpt-5.5-pro", + "gpt-5.6", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "openai/gpt-5.6-sol", +] + +GPT5_PRE_5_4_MODELS = [ + "gpt-5", + "gpt-5.1", + "gpt-5.2", + "gpt-5.3", + "gpt-5.3-chat", + "gpt-4o", +] + + +class TestOpenAIGPT5ConfigIsModelGpt54PlusModel: + + @pytest.mark.parametrize("model", GPT5_4_PLUS_MODELS) + def test_gpt5_4_plus_models_are_classified_as_5_4_plus(self, model: str): + assert OpenAIGPT5Config.is_model_gpt_5_4_plus_model( + model + ), f"Expected '{model}' to be classified as gpt-5.4-or-newer" + + @pytest.mark.parametrize("model", GPT5_PRE_5_4_MODELS) + def test_pre_5_4_models_are_not_classified_as_5_4_plus(self, model: str): + assert not OpenAIGPT5Config.is_model_gpt_5_4_plus_model( + model + ), f"Expected '{model}' NOT to be classified as gpt-5.4-or-newer" + + # --------------------------------------------------------------------------- # AzureOpenAIGPT5Config # --------------------------------------------------------------------------- diff --git a/tests/test_litellm/llms/openai_like/messages/__init__.py b/tests/test_litellm/llms/openai_like/messages/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/llms/openai_like/messages/test_openai_like_anthropic_messages_transformation.py b/tests/test_litellm/llms/openai_like/messages/test_openai_like_anthropic_messages_transformation.py new file mode 100644 index 00000000000..33e677b000e --- /dev/null +++ b/tests/test_litellm/llms/openai_like/messages/test_openai_like_anthropic_messages_transformation.py @@ -0,0 +1,319 @@ +import pytest + +from litellm.llms.anthropic.common_utils import AnthropicError +from litellm.llms.openai_like.messages.transformation import ( + OpenAILikeAnthropicMessagesConfig, +) +from litellm.types.router import GenericLiteLLMParams + + +@pytest.fixture +def config() -> OpenAILikeAnthropicMessagesConfig: + return OpenAILikeAnthropicMessagesConfig() + + +@pytest.mark.parametrize( + "api_base, expected", + [ + ("https://host/v1", "https://host/v1/messages"), + ("https://host/v1/", "https://host/v1/messages"), + ("https://host", "https://host/v1/messages"), + ("https://host/v1/messages", "https://host/v1/messages"), + ("https://api.deepseek.com/anthropic", "https://api.deepseek.com/anthropic/v1/messages"), + ("https://api.deepseek.com/anthropic/v1", "https://api.deepseek.com/anthropic/v1/messages"), + ], +) +def test_get_complete_url_handles_api_base_variants(config, api_base, expected): + url = config.get_complete_url( + api_base=api_base, + api_key="sk-test", + model="some-model", + optional_params={}, + litellm_params={}, + ) + assert url == expected + + +def test_get_complete_url_requires_api_base(config): + with pytest.raises(ValueError, match="api_base is required"): + config.get_complete_url( + api_base=None, + api_key="sk-test", + model="some-model", + optional_params={}, + litellm_params={}, + ) + + +def test_request_stays_in_anthropic_shape(config): + messages = [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Summarize this", + "cache_control": {"type": "ephemeral"}, + } + ], + } + ] + optional_params = { + "max_tokens": 256, + "system": "You are a careful assistant", + "thinking": {"type": "enabled", "budget_tokens": 1024}, + "temperature": 0.3, + "tools": [{"name": "lookup", "input_schema": {"type": "object"}}], + "stream": False, + } + + payload = config.transform_anthropic_messages_request( + model="some-model", + messages=messages, + anthropic_messages_optional_request_params=optional_params, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert payload["model"] == "some-model" + assert payload["messages"] == messages + assert payload["messages"][0]["content"][0]["cache_control"] == {"type": "ephemeral"} + assert payload["system"] == "You are a careful assistant" + assert payload["thinking"] == {"type": "enabled", "budget_tokens": 1024} + assert payload["max_tokens"] == 256 + assert payload["tools"] == optional_params["tools"] + + openai_only_keys = { + "max_completion_tokens", + "stop", + "n", + "logprobs", + "response_format", + "frequency_penalty", + } + assert openai_only_keys.isdisjoint(payload.keys()) + + +def test_request_requires_max_tokens(config): + with pytest.raises(AnthropicError, match="max_tokens is required"): + config.transform_anthropic_messages_request( + model="some-model", + messages=[{"role": "user", "content": "hi"}], + anthropic_messages_optional_request_params={"system": "s"}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + +def test_validate_environment_sets_bearer_and_anthropic_defaults(config): + headers, api_base = config.validate_anthropic_messages_environment( + headers={}, + model="some-model", + messages=[], + optional_params={}, + litellm_params={}, + api_key="sk-test", + api_base="https://host/v1", + ) + assert headers["authorization"] == "Bearer sk-test" + assert headers["anthropic-version"] == "2023-06-01" + assert headers["content-type"] == "application/json" + assert api_base == "https://host/v1" + + +def test_validate_environment_does_not_overwrite_caller_headers(config): + headers, _ = config.validate_anthropic_messages_environment( + headers={ + "authorization": "Bearer caller-token", + "anthropic-version": "2024-10-22", + "content-type": "application/json", + }, + model="some-model", + messages=[], + optional_params={}, + litellm_params={}, + api_key="sk-test", + api_base="https://host/v1", + ) + assert headers["authorization"] == "Bearer caller-token" + assert headers["anthropic-version"] == "2024-10-22" + + +def test_validate_environment_preserves_standard_cased_caller_headers(config): + headers, _ = config.validate_anthropic_messages_environment( + headers={ + "Authorization": "Bearer caller-token", + "Anthropic-Version": "2024-10-22", + "Content-Type": "application/json", + }, + model="some-model", + messages=[], + optional_params={}, + litellm_params={}, + api_key="sk-test", + api_base="https://host/v1", + ) + lowercased = {key.lower() for key in headers} + assert len(lowercased) == len(headers) + assert headers["Authorization"] == "Bearer caller-token" + assert headers["Anthropic-Version"] == "2024-10-22" + assert headers["Content-Type"] == "application/json" + + +def test_validate_environment_honors_x_api_key_when_present(config): + headers, _ = config.validate_anthropic_messages_environment( + headers={"X-Api-Key": "caller-key"}, + model="some-model", + messages=[], + optional_params={}, + litellm_params={}, + api_key="sk-test", + api_base="https://host/v1", + ) + assert "authorization" not in {key.lower() for key in headers} + assert headers["X-Api-Key"] == "caller-key" + + +def test_validate_environment_injects_anthropic_beta_for_context_management(config): + headers, _ = config.validate_anthropic_messages_environment( + headers={}, + model="some-model", + messages=[], + optional_params={ + "context_management": {"edits": [{"type": "clear_tool_uses_20250919"}]}, + }, + litellm_params={}, + api_key="sk-test", + api_base="https://host/v1", + ) + assert "context-management-2025-06-27" in headers["anthropic-beta"].split(",") + + +def test_validate_environment_injects_anthropic_beta_for_fast_mode(config): + headers, _ = config.validate_anthropic_messages_environment( + headers={}, + model="some-model", + messages=[], + optional_params={"speed": "fast"}, + litellm_params={}, + api_key="sk-test", + api_base="https://host/v1", + ) + assert "fast-mode-2026-02-01" in headers["anthropic-beta"].split(",") + + +def test_validate_environment_merges_existing_anthropic_beta(config): + headers, _ = config.validate_anthropic_messages_environment( + headers={"anthropic-beta": "caller-flag"}, + model="some-model", + messages=[], + optional_params={"speed": "fast"}, + litellm_params={}, + api_key="sk-test", + api_base="https://host/v1", + ) + beta_values = set(headers["anthropic-beta"].split(",")) + assert "caller-flag" in beta_values + assert "fast-mode-2026-02-01" in beta_values + + +def test_request_strips_advisor_blocks_when_advisor_tool_absent(config): + messages = [ + {"role": "user", "content": "hello"}, + { + "role": "assistant", + "content": [ + {"type": "text", "text": "thinking out loud"}, + {"type": "server_tool_use", "id": "advisor_1", "name": "advisor", "input": {}}, + {"type": "advisor_tool_result", "tool_use_id": "advisor_1", "content": "stale"}, + ], + }, + ] + + payload = config.transform_anthropic_messages_request( + model="some-model", + messages=messages, + anthropic_messages_optional_request_params={"max_tokens": 64}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + flattened_types = [ + block.get("type") + for message in payload["messages"] + if isinstance(message.get("content"), list) + for block in message["content"] + if isinstance(block, dict) + ] + assert "advisor_tool_result" not in flattened_types + assert "server_tool_use" not in flattened_types + + +def test_request_maps_reasoning_effort_to_thinking(config): + payload = config.transform_anthropic_messages_request( + model="claude-sonnet-4-20250514", + messages=[{"role": "user", "content": "hi"}], + anthropic_messages_optional_request_params={ + "max_tokens": 1024, + "reasoning_effort": "medium", + }, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert "reasoning_effort" not in payload + assert isinstance(payload.get("thinking"), dict) + assert payload["thinking"].get("type") == "enabled" + + +def test_passthrough_disables_anthropic_beta_filtering(config): + from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, + ) + + assert config.should_filter_anthropic_beta_headers() is False + assert AnthropicMessagesConfig().should_filter_anthropic_beta_headers() is True + + +def test_anthropic_beta_survives_provider_filter_on_passthrough_path(config): + from litellm.anthropic_beta_headers_manager import update_headers_with_filtered_beta + + headers, _ = config.validate_anthropic_messages_environment( + headers={"Anthropic-Beta": "caller-flag"}, + model="some-model", + messages=[], + optional_params={"speed": "fast"}, + litellm_params={}, + api_key="sk-test", + api_base="https://host/v1", + ) + + # The deployment routes as provider "openai", which has no beta mapping, so an + # unconditional filter would drop every anthropic-beta value. The handler must + # skip filtering for this config so the native upstream still receives them. + if config.should_filter_anthropic_beta_headers(): + headers = update_headers_with_filtered_beta(headers=dict(headers), provider="openai") + + survived = set(headers.get("anthropic-beta", "").split(",")) + assert {"caller-flag", "fast-mode-2026-02-01"} <= survived + + stripped = update_headers_with_filtered_beta(headers=dict(headers), provider="openai") + assert "anthropic-beta" not in stripped + + +def test_json_provider_messages_config_probes_capabilities_under_provider_slug(): + """Capability probes in the shared pass-through helpers read + ``self.custom_llm_provider``. The JSON-provider config knows its slug, so it + must expose it; the generic OpenAI-like config has no class-level namespace + and keeps the inherited ``anthropic`` default.""" + from litellm.llms.openai_like.json_loader import SimpleProviderConfig + from litellm.llms.openai_like.messages.transformation import ( + JSONProviderAnthropicMessagesConfig, + ) + + provider = SimpleProviderConfig( + slug="exampleprovider", + data={"base_url": "https://api.example.com/v1", "api_key_env": "EXAMPLE_API_KEY"}, + ) + assert JSONProviderAnthropicMessagesConfig(provider).custom_llm_provider == "exampleprovider" + assert OpenAILikeAnthropicMessagesConfig().custom_llm_provider == "anthropic" diff --git a/tests/test_litellm/llms/openai_like/test_meta_provider.py b/tests/test_litellm/llms/openai_like/test_meta_provider.py new file mode 100644 index 00000000000..11b78828da6 --- /dev/null +++ b/tests/test_litellm/llms/openai_like/test_meta_provider.py @@ -0,0 +1,224 @@ +""" +Tests for the Meta Model API (Muse Spark) provider configuration and integration. +""" + +import litellm + + +class TestMetaProviderConfig: + def test_meta_in_provider_list(self): + from litellm import LlmProviders + + assert hasattr(LlmProviders, "META") + assert LlmProviders.META.value == "meta" + assert "meta" in litellm.provider_list + + def test_meta_json_config_exists(self): + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + assert JSONProviderRegistry.exists("meta") + + meta = JSONProviderRegistry.get("meta") + assert meta is not None + assert meta.base_url == "https://api.meta.ai/v1" + assert meta.api_key_env == "META_API_KEY" + assert meta.api_base_env == "META_API_BASE" + + def test_meta_supports_responses_api(self): + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + assert JSONProviderRegistry.supports_responses_api("meta") + + def test_meta_in_openai_compatible_providers(self): + from litellm.constants import openai_compatible_providers + + assert "meta" in openai_compatible_providers + + def test_meta_provider_resolution(self): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + model, provider, api_key, api_base = get_llm_provider( + model="meta/muse-spark-1.1", + custom_llm_provider=None, + api_base=None, + api_key="sk-test", + ) + + assert model == "muse-spark-1.1" + assert provider == "meta" + assert api_base == "https://api.meta.ai/v1" + + def test_meta_api_base_override(self): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + model, provider, api_key, api_base = get_llm_provider( + model="meta/muse-spark-1.1", + custom_llm_provider=None, + api_base="https://custom.meta.ai/v1", + api_key="sk-test", + ) + + assert provider == "meta" + assert api_base == "https://custom.meta.ai/v1" + assert api_key == "sk-test" + + def test_meta_url_autodetection(self): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + model, provider, api_key, api_base = get_llm_provider( + model="muse-spark-1.1", + custom_llm_provider=None, + api_base="https://api.meta.ai/v1", + api_key=None, + ) + assert provider == "meta" + assert api_base == "https://api.meta.ai/v1" + + def test_meta_router_config(self): + from litellm import Router + + router = Router( + model_list=[ + { + "model_name": "muse-spark", + "litellm_params": { + "model": "meta/muse-spark-1.1", + "api_key": "test-key", + }, + } + ] + ) + + assert len(router.model_list) == 1 + assert router.model_list[0]["model_name"] == "muse-spark" + + +class TestMetaReasoningParams: + def test_muse_spark_supports_reasoning_effort(self): + params = litellm.get_supported_openai_params( + model="muse-spark-1.1", custom_llm_provider="meta" + ) + assert params is not None + assert "reasoning_effort" in params + + def test_reasoning_effort_mapped_through(self): + cfg = litellm.ProviderConfigManager.get_provider_chat_config( + model="muse-spark-1.1", provider=litellm.LlmProviders.META + ) + assert cfg is not None + mapped = cfg.map_openai_params( + non_default_params={"reasoning_effort": "xhigh"}, + optional_params={}, + model="muse-spark-1.1", + drop_params=False, + ) + assert mapped["reasoning_effort"] == "xhigh" + + def test_reasoning_effort_gated_on_capability(self): + """A meta model without reasoning metadata must not advertise reasoning_effort.""" + params = litellm.get_supported_openai_params( + model="some-non-reasoning-model", custom_llm_provider="meta" + ) + assert params is not None + assert "reasoning_effort" not in params + + +class TestMetaAnthropicMessages: + def test_meta_resolves_native_messages_config(self): + from litellm.llms.openai_like.messages.transformation import ( + JSONProviderAnthropicMessagesConfig, + ) + + cfg = litellm.ProviderConfigManager.get_provider_anthropic_messages_config( + model="muse-spark-1.1", provider=litellm.LlmProviders.META + ) + assert isinstance(cfg, JSONProviderAnthropicMessagesConfig) + + def test_json_provider_without_messages_endpoint_resolves_none(self): + cfg = litellm.ProviderConfigManager.get_provider_anthropic_messages_config( + model="some-model", provider=litellm.LlmProviders.PINSTRIPES + ) + assert cfg is None + + def test_complete_url_defaults_to_meta_base(self): + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + from litellm.llms.openai_like.messages.transformation import ( + JSONProviderAnthropicMessagesConfig, + ) + + provider = JSONProviderRegistry.get("meta") + assert provider is not None + cfg = JSONProviderAnthropicMessagesConfig(provider) + + url = cfg.get_complete_url( + api_base=None, + api_key="sk-test", + model="muse-spark-1.1", + optional_params={}, + litellm_params={}, + ) + assert url == "https://api.meta.ai/v1/messages" + + override_url = cfg.get_complete_url( + api_base="https://custom.meta.ai/v1", + api_key="sk-test", + model="muse-spark-1.1", + optional_params={}, + litellm_params={}, + ) + assert override_url == "https://custom.meta.ai/v1/messages" + + def test_api_key_resolved_from_env(self, monkeypatch): + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + from litellm.llms.openai_like.messages.transformation import ( + JSONProviderAnthropicMessagesConfig, + ) + + monkeypatch.setenv("META_API_KEY", "sk-env-key") + provider = JSONProviderRegistry.get("meta") + assert provider is not None + cfg = JSONProviderAnthropicMessagesConfig(provider) + + headers, _ = cfg.validate_anthropic_messages_environment( + headers={}, + model="muse-spark-1.1", + messages=[{"role": "user", "content": "hi"}], + optional_params={}, + litellm_params={}, + api_key=None, + api_base=None, + ) + assert headers["authorization"] == "Bearer sk-env-key" + assert headers["anthropic-version"] == "2023-06-01" + + +class TestMuseSparkModelInfo: + def test_muse_spark_pricing_and_capabilities(self): + info = litellm.get_model_info("meta/muse-spark-1.1") + + assert info["litellm_provider"] == "meta" + assert info["input_cost_per_token"] == 1.25e-06 + assert info["output_cost_per_token"] == 4.25e-06 + assert info["cache_read_input_token_cost"] == 1.5e-07 + assert info["max_input_tokens"] == 1048576 + assert info["supports_reasoning"] is True + assert info["supports_web_search"] is True + assert info["supports_vision"] is True + assert info["supports_function_calling"] is True + assert info["supports_prompt_caching"] is True + + def test_muse_spark_cost_calculation(self): + from litellm import completion_cost + from litellm.types.utils import ModelResponse, Usage + + response = ModelResponse( + model="muse-spark-1.1", + usage=Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500), + ) + cost = completion_cost( + completion_response=response, + model="meta/muse-spark-1.1", + custom_llm_provider="meta", + ) + expected = 1000 * 1.25e-06 + 500 * 4.25e-06 + assert abs(cost - expected) < 1e-12 diff --git a/tests/test_litellm/llms/tencent/__init__.py b/tests/test_litellm/llms/tencent/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/llms/tencent/chat/__init__.py b/tests/test_litellm/llms/tencent/chat/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/llms/tencent/chat/test_tencent_chat_transformation.py b/tests/test_litellm/llms/tencent/chat/test_tencent_chat_transformation.py new file mode 100644 index 00000000000..00a82041c20 --- /dev/null +++ b/tests/test_litellm/llms/tencent/chat/test_tencent_chat_transformation.py @@ -0,0 +1,207 @@ +from unittest.mock import patch + +from litellm.llms.tencent.chat.transformation import TencentChatConfig + + +def test_supported_openai_params_includes_thinking_and_reasoning_effort(): + config = TencentChatConfig() + + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=True, + ): + params = config.get_supported_openai_params(model="tencent/deepseek-v4-pro") + + assert "thinking" in params + assert "reasoning_effort" in params + assert "stream" in params + assert "temperature" in params + + +def test_supported_openai_params_excludes_thinking_without_reasoning_support(): + config = TencentChatConfig() + + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=False, + ): + params = config.get_supported_openai_params(model="tencent/non-reasoning-model") + + assert "thinking" not in params + assert "reasoning_effort" not in params + assert "stream" in params + + +def test_map_openai_params_passes_thinking_dict_through(): + config = TencentChatConfig() + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=True, + ): + result = config.map_openai_params( + non_default_params={"thinking": {"type": "enabled", "budget_tokens": 1024}}, + optional_params={}, + model="tencent/deepseek-v4-pro", + drop_params=False, + ) + + assert result["thinking"] == {"type": "enabled", "budget_tokens": 1024} + + +def test_map_openai_params_converts_reasoning_effort_to_thinking(): + config = TencentChatConfig() + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=True, + ): + result = config.map_openai_params( + non_default_params={"reasoning_effort": "medium"}, + optional_params={}, + model="tencent/deepseek-v4-pro", + drop_params=False, + ) + + assert result["thinking"] == {"type": "enabled"} + + +def test_map_openai_params_drops_none_reasoning_effort(): + config = TencentChatConfig() + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=True, + ): + result = config.map_openai_params( + non_default_params={"reasoning_effort": "none"}, + optional_params={}, + model="tencent/deepseek-v4-pro", + drop_params=False, + ) + + assert "thinking" not in result + assert "reasoning_effort" not in result + + +def test_map_openai_params_thinking_priority_over_reasoning_effort(): + config = TencentChatConfig() + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=True, + ): + result = config.map_openai_params( + non_default_params={ + "thinking": {"type": "enabled", "budget_tokens": 2048}, + "reasoning_effort": "high", + }, + optional_params={}, + model="tencent/deepseek-v4-pro", + drop_params=False, + ) + + assert result["thinking"] == {"type": "enabled", "budget_tokens": 2048} + + +def test_map_openai_params_extracts_thinking_and_effort_from_optional_params(): + config = TencentChatConfig() + result = config.map_openai_params( + non_default_params={}, + optional_params={"thinking": {"type": "enabled"}, "reasoning_effort": "medium"}, + model="tencent/deepseek-v4-pro", + drop_params=False, + ) + + assert "thinking" in result + assert "reasoning_effort" not in result + + +def test_get_complete_url_default(): + config = TencentChatConfig() + + url = config.get_complete_url( + api_base=None, + api_key=None, + model="tencent/deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + + assert url == "https://tokenhub-intl.tencentcloudmaas.com/v1/chat/completions" + + +def test_get_complete_url_strips_trailing_slash(): + config = TencentChatConfig() + + url = config.get_complete_url( + api_base="https://tokenhub-intl.tencentcloudmaas.com/v1/", + api_key=None, + model="tencent/deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + + assert url == "https://tokenhub-intl.tencentcloudmaas.com/v1/chat/completions" + + +def test_get_complete_url_custom_base_preserves_v1(): + config = TencentChatConfig() + + url = config.get_complete_url( + api_base="https://tokenhub.tencentcloudmaas.com/v1", + api_key=None, + model="tencent/deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + + assert url == "https://tokenhub.tencentcloudmaas.com/v1/chat/completions" + + +def test_get_complete_url_adds_v1_to_custom_base(): + config = TencentChatConfig() + + url = config.get_complete_url( + api_base="https://tokenhub.tencentcloudmaas.com", + api_key=None, + model="tencent/deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + + assert url == "https://tokenhub.tencentcloudmaas.com/v1/chat/completions" + + +def test_get_complete_url_does_not_append_to_full_url(): + config = TencentChatConfig() + + url = config.get_complete_url( + api_base="https://tokenhub.tencentcloudmaas.com/v1/chat/completions", + api_key=None, + model="tencent/deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + + assert url == "https://tokenhub.tencentcloudmaas.com/v1/chat/completions" + + +def test_provider_info_falls_back_to_default_base(): + config = TencentChatConfig() + + with patch("litellm.llms.tencent.chat.transformation.get_secret_str", return_value=None): + api_base, api_key = config._get_openai_compatible_provider_info(api_base=None, api_key="sk-arg") + + assert api_base == "https://tokenhub-intl.tencentcloudmaas.com/v1" + assert api_key == "sk-arg" + + +def test_provider_info_reads_env_secrets(): + config = TencentChatConfig() + + secrets = {"TENCENT_API_BASE": "https://env.tencent/v1", "TENCENT_API_KEY": "sk-env"} + with patch( + "litellm.llms.tencent.chat.transformation.get_secret_str", + side_effect=lambda key: secrets.get(key), + ): + api_base, api_key = config._get_openai_compatible_provider_info(api_base=None, api_key=None) + + assert api_base == "https://env.tencent/v1" + assert api_key == "sk-env" diff --git a/tests/test_litellm/llms/tencent/messages/__init__.py b/tests/test_litellm/llms/tencent/messages/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/llms/tencent/messages/test_tencent_anthropic_messages_transformation.py b/tests/test_litellm/llms/tencent/messages/test_tencent_anthropic_messages_transformation.py new file mode 100644 index 00000000000..70c965a6190 --- /dev/null +++ b/tests/test_litellm/llms/tencent/messages/test_tencent_anthropic_messages_transformation.py @@ -0,0 +1,173 @@ +import litellm +from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig, +) +from litellm.llms.tencent.messages.transformation import ( + TencentAnthropicMessagesConfig, +) +from litellm.utils import ProviderConfigManager + + +def test_tencent_provider_uses_anthropic_messages_config(): + config = ProviderConfigManager.get_provider_anthropic_messages_config( + model="deepseek-v4-pro", + provider=litellm.LlmProviders.TENCENT, + ) + + assert isinstance(config, TencentAnthropicMessagesConfig) + assert config.custom_llm_provider == "tencent" + + +def test_anthropic_provider_keeps_default_config_for_tencent_named_model(): + config = ProviderConfigManager.get_provider_anthropic_messages_config( + model="deepseek-v4-pro", + provider=litellm.LlmProviders.ANTHROPIC, + ) + + assert isinstance(config, AnthropicMessagesConfig) + assert not isinstance(config, TencentAnthropicMessagesConfig) + + +def test_strips_billing_metadata(): + config = TencentAnthropicMessagesConfig() + + assert config.should_strip_billing_metadata() is True + + +def test_get_api_base_default(): + config = TencentAnthropicMessagesConfig() + + assert config.get_api_base() == "https://tokenhub-intl.tencentcloudmaas.com" + + +def test_get_api_base_from_arg(): + config = TencentAnthropicMessagesConfig() + + assert config.get_api_base(api_base="https://custom.example.com") == "https://custom.example.com" + + +def test_messages_url_default(): + config = TencentAnthropicMessagesConfig() + + assert ( + config.get_complete_url( + api_base=None, + api_key=None, + model="deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + == "https://tokenhub-intl.tencentcloudmaas.com/v1/messages" + ) + + +def test_messages_url_with_base_ending_in_v1(): + config = TencentAnthropicMessagesConfig() + + assert ( + config.get_complete_url( + api_base="https://tokenhub-intl.tencentcloudmaas.com/v1", + api_key=None, + model="deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + == "https://tokenhub-intl.tencentcloudmaas.com/v1/messages" + ) + + +def test_messages_url_with_base_ending_in_v1_messages(): + config = TencentAnthropicMessagesConfig() + + url = config.get_complete_url( + api_base="https://tokenhub-intl.tencentcloudmaas.com/v1/messages", + api_key=None, + model="deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + + assert url == "https://tokenhub-intl.tencentcloudmaas.com/v1/messages" + + +def test_messages_url_with_base_ending_in_v1_chat_completions(): + config = TencentAnthropicMessagesConfig() + + assert ( + config.get_complete_url( + api_base="https://tokenhub-intl.tencentcloudmaas.com/v1/chat/completions", + api_key=None, + model="deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + == "https://tokenhub-intl.tencentcloudmaas.com/v1/messages" + ) + + +def test_messages_url_with_custom_base_no_v1(): + config = TencentAnthropicMessagesConfig() + + assert ( + config.get_complete_url( + api_base="https://tokenhub.tencentcloudmaas.com", + api_key=None, + model="deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + == "https://tokenhub.tencentcloudmaas.com/v1/messages" + ) + + +def test_validate_environment_sets_headers(): + config = TencentAnthropicMessagesConfig() + + headers, api_base = config.validate_anthropic_messages_environment( + headers={}, + model="deepseek-v4-pro", + messages=[], + optional_params={}, + litellm_params={}, + api_key="sk-tencent-key", + api_base="https://custom.test", + ) + + assert headers["x-api-key"] == "sk-tencent-key" + assert headers["anthropic-version"] == "2023-06-01" + assert headers["content-type"] == "application/json" + assert api_base == "https://custom.test" + + +def test_validate_environment_injects_anthropic_beta_headers(): + config = TencentAnthropicMessagesConfig() + + headers, _ = config.validate_anthropic_messages_environment( + headers={}, + model="deepseek-v4-pro", + messages=[], + optional_params={"speed": "fast"}, + litellm_params={}, + api_key="sk-tencent-key", + api_base=None, + ) + + assert "anthropic-beta" in headers + + +def test_validate_environment_preserves_existing_headers(): + config = TencentAnthropicMessagesConfig() + + headers, _ = config.validate_anthropic_messages_environment( + headers={"authorization": "Bearer existing", "anthropic-version": "2024-01-01"}, + model="deepseek-v4-pro", + messages=[], + optional_params={}, + litellm_params={}, + api_key="sk-tencent-key", + api_base=None, + ) + + assert headers["authorization"] == "Bearer existing" + assert headers["anthropic-version"] == "2024-01-01" + assert "x-api-key" not in headers diff --git a/tests/test_litellm/llms/tencent/test_cost_calculator.py b/tests/test_litellm/llms/tencent/test_cost_calculator.py new file mode 100644 index 00000000000..c2e905fab85 --- /dev/null +++ b/tests/test_litellm/llms/tencent/test_cost_calculator.py @@ -0,0 +1,41 @@ +import pytest + +import litellm +from litellm.llms.tencent.cost_calculator import cost_per_token +from litellm.types.utils import Usage + + +@pytest.fixture +def local_model_cost_map(monkeypatch): + original_model_cost = litellm.model_cost + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + litellm.model_cost = litellm.get_model_cost_map(url="") + litellm.get_model_info.cache_clear() + try: + yield + finally: + litellm.model_cost = original_model_cost + litellm.get_model_info.cache_clear() + + +def test_cost_per_token_uses_tencent_model_pricing(local_model_cost_map): + usage = Usage(prompt_tokens=1000, completion_tokens=2000, total_tokens=3000) + + prompt_cost, completion_cost = cost_per_token(model="tencent/deepseek-v4-pro", usage=usage) + + assert prompt_cost == pytest.approx(1000 * 4.35e-07) + assert completion_cost == pytest.approx(2000 * 8.7e-07) + + +def test_top_level_dispatcher_routes_tencent_to_wrapper(local_model_cost_map): + from litellm.cost_calculator import cost_per_token as dispatch_cost_per_token + + prompt_cost, completion_cost = dispatch_cost_per_token( + model="tencent/deepseek-v4-pro", + prompt_tokens=1000, + completion_tokens=1000, + custom_llm_provider="tencent", + ) + + assert prompt_cost == pytest.approx(1000 * 4.35e-07) + assert completion_cost == pytest.approx(1000 * 8.7e-07) diff --git a/tests/test_litellm/llms/tinyfish/test_tinyfish_search.py b/tests/test_litellm/llms/tinyfish/test_tinyfish_search.py index 9870d30d488..58363e3baea 100644 --- a/tests/test_litellm/llms/tinyfish/test_tinyfish_search.py +++ b/tests/test_litellm/llms/tinyfish/test_tinyfish_search.py @@ -2,7 +2,6 @@ Tests for TinyFish Search API integration. """ -import os from unittest.mock import MagicMock, patch import httpx @@ -11,6 +10,7 @@ import pytest from litellm.llms.tinyfish.search.transformation import ( TinyfishSearchConfig, _append_domain_filters, + _default_missing_result_fields, ) MOCK_TINYFISH_RESPONSE = { @@ -37,11 +37,24 @@ MOCK_TINYFISH_RESPONSE = { def _make_mock_response( - json_data: dict, status_code: int = 200, request_url: str | None = None + json_data: dict | None = None, + status_code: int = 200, + request_url: str | None = None, + text: str | None = None, + headers: dict | None = None, ) -> MagicMock: + import json as _json + mock = MagicMock() mock.status_code = status_code - mock.json.return_value = json_data + mock.headers = headers or {} + if json_data is not None: + mock.json.return_value = json_data + mock.text = text if text is not None else _json.dumps(json_data) + else: + # Force .json() to raise as httpx.Response does for non-JSON bodies. + mock.json.side_effect = _json.JSONDecodeError("Expecting value", text or "", 0) + mock.text = text or "" if request_url: mock.request = MagicMock() mock.request.url = httpx.URL(request_url) @@ -107,26 +120,62 @@ class TestTransformSearchRequest: ) assert result["_tinyfish_params"]["location"] == "US" - def test_max_results_clamped_upper(self): - config = TinyfishSearchConfig() - result = config.transform_search_request( - query="test", optional_params={"max_results": 100} - ) - assert result["_tinyfish_params"]["max_results"] == 20 - - def test_max_results_clamped_lower(self): - config = TinyfishSearchConfig() - result = config.transform_search_request( - query="test", optional_params={"max_results": 0} - ) - assert result["_tinyfish_params"]["max_results"] == 1 - - def test_max_results_normal(self): + def test_max_results_not_sent_on_wire(self): + # TinyFish doesn't honor max_results server-side; we apply it client-side + # in transform_search_response. The querystring should be free of it. config = TinyfishSearchConfig() result = config.transform_search_request( query="test", optional_params={"max_results": 5} ) - assert result["_tinyfish_params"]["max_results"] == 5 + assert "max_results" not in result["_tinyfish_params"] + + def test_max_results_clamped_upper_stored_on_self(self): + config = TinyfishSearchConfig() + config.transform_search_request( + query="test", optional_params={"max_results": 100} + ) + assert config._caller_max_results == 10 # TinyFish's natural cap + + def test_max_results_clamped_lower_stored_on_self(self): + config = TinyfishSearchConfig() + config.transform_search_request( + query="test", optional_params={"max_results": 0} + ) + assert config._caller_max_results == 1 + + def test_max_results_normal_stored_on_self(self): + config = TinyfishSearchConfig() + config.transform_search_request( + query="test", optional_params={"max_results": 5} + ) + assert config._caller_max_results == 5 + + def test_max_results_non_numeric_string_warns_and_skips(self, caplog): + # `int("abc")` would raise ValueError; guard makes the failure visible + # via warning and treats the value as if max_results wasn't set. + config = TinyfishSearchConfig() + with caplog.at_level("WARNING"): + result = config.transform_search_request( + query="test", optional_params={"max_results": "abc"} + ) + assert config._caller_max_results is None + assert "max_results" not in result["_tinyfish_params"] + messages = [r.getMessage() for r in caplog.records] + assert any("max_results" in m and "abc" in m for m in messages) + + def test_max_results_infinity_float_warns_and_skips(self, caplog): + # `int(float('inf'))` raises OverflowError, not ValueError/TypeError. + # Guard must catch it so a caller passing math.inf gets the same + # warn-and-ignore behavior as other malformed values. + config = TinyfishSearchConfig() + with caplog.at_level("WARNING"): + result = config.transform_search_request( + query="test", optional_params={"max_results": float("inf")} + ) + assert config._caller_max_results is None + assert "max_results" not in result["_tinyfish_params"] + messages = [r.getMessage() for r in caplog.records] + assert any("max_results" in m for m in messages) def test_domain_filter_appends_site_operators(self): config = TinyfishSearchConfig() @@ -172,6 +221,52 @@ class TestTransformSearchRequest: ) assert param not in result["_tinyfish_params"] + def test_arbitrary_param_passed_through(self): + # `fetch` is a TinyFish-specific param (JSON-encoded tf-fetch config). + # The passthrough loop should forward it verbatim without LiteLLM needing + # to know about it. + config = TinyfishSearchConfig() + result = config.transform_search_request( + query="test", optional_params={"fetch": "{}"} + ) + assert result["_tinyfish_params"]["fetch"] == "{}" + + def test_dict_param_auto_json_encoded(self): + # Callers naturally pass dict-shaped params; we serialize so the + # downstream urlencode step (which only accepts str|int|bool) doesn't reject. + config = TinyfishSearchConfig() + result = config.transform_search_request( + query="test", + optional_params={"fetch": {"format": "html", "fetch_path": "fast"}}, + ) + assert ( + result["_tinyfish_params"]["fetch"] + == '{"format":"html","fetch_path":"fast"}' + ) + + def test_bool_param_serialized_as_lowercase(self): + # urlencode renders Python bool as capitalized "True"/"False"; ux-labs + # rejects those (e.g. include_thumbnail must be literal "true"/"false"). + # Normalize before passing through. + config = TinyfishSearchConfig() + true_result = config.transform_search_request( + query="test", optional_params={"include_thumbnail": True} + ) + false_result = config.transform_search_request( + query="test", optional_params={"include_thumbnail": False} + ) + assert true_result["_tinyfish_params"]["include_thumbnail"] == "true" + assert false_result["_tinyfish_params"]["include_thumbnail"] == "false" + + def test_pre_stringified_param_passed_unchanged(self): + # If the caller already JSON-encoded, don't re-encode. + config = TinyfishSearchConfig() + already = '{"format":"html"}' + result = config.transform_search_request( + query="test", optional_params={"fetch": already} + ) + assert result["_tinyfish_params"]["fetch"] == already + class TestGetCompleteUrl: def test_default_api_base(self): @@ -259,8 +354,10 @@ class TestTransformSearchResponse: assert result.object == "search" assert len(result.results) == 0 - def test_max_results_truncates(self): + def test_max_results_truncates_from_self_state(self): config = TinyfishSearchConfig() + # Simulate transform_search_request having set the threaded value. + config._caller_max_results = 3 many_results = { "results": [ { @@ -271,10 +368,7 @@ class TestTransformSearchResponse: for i in range(10) ] } - mock_response = _make_mock_response( - many_results, - request_url="https://api.search.tinyfish.ai?query=test&max_results=3", - ) + mock_response = _make_mock_response(many_results) result = config.transform_search_response( raw_response=mock_response, logging_obj=None ) @@ -282,7 +376,8 @@ class TestTransformSearchResponse: assert result.results[0].title == "Result 0" assert result.results[2].title == "Result 2" - def test_max_results_default_is_20(self): + def test_max_results_default_is_tinyfish_cap(self): + # No caller value → fall back to TinyFish's natural ceiling (10). config = TinyfishSearchConfig() many_results = { "results": [ @@ -291,28 +386,70 @@ class TestTransformSearchResponse: "url": f"https://example.com/{i}", "snippet": f"Snippet {i}", } - for i in range(25) + for i in range(15) ] } - mock_response = _make_mock_response( - many_results, - request_url="https://api.search.tinyfish.ai?query=test", - ) + mock_response = _make_mock_response(many_results) result = config.transform_search_response( raw_response=mock_response, logging_obj=None ) - assert len(result.results) == 20 + assert len(result.results) == 10 - def test_missing_fields_default_to_empty_string(self): + def test_missing_required_fields_default_to_empty_string(self): + # title/url/snippet are required by LiteLLM's SearchResult schema. + # We default missing/null values to "" so a degraded TinyFish result + # flows through instead of failing the whole call. config = TinyfishSearchConfig() - mock_response = _make_mock_response({"results": [{}]}) + mock_response = _make_mock_response( + {"results": [{}, {"title": None, "url": None, "snippet": None}]} + ) result = config.transform_search_response( raw_response=mock_response, logging_obj=None ) - assert len(result.results) == 1 - assert result.results[0].title == "" - assert result.results[0].url == "" - assert result.results[0].snippet == "" + assert len(result.results) == 2 + for r in result.results: + assert r.title == "" + assert r.url == "" + assert r.snippet == "" + + def test_extra_per_result_fields_surface_as_attributes(self): + config = TinyfishSearchConfig() + mock_response = _make_mock_response(MOCK_TINYFISH_RESPONSE) + result = config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + first = result.results[0] + assert getattr(first, "position", None) == 1 + assert getattr(first, "site_name", None) == "tinyfish.ai" + + def test_fetch_field_rides_through_to_search_result(self): + # Mirrors browser-search's per-result `fetch` nested object (see + # api/src/parser.rs SearchResult.fetch). Confirms `fetch=...` requests + # surface their content to LiteLLM callers without provider changes. + config = TinyfishSearchConfig() + fetched = { + "results": [ + { + "title": "TinyFish", + "url": "https://tinyfish.ai", + "snippet": "Web automation.", + "fetch": { + "url": "https://tinyfish.ai", + "title": "TinyFish", + "text": "Body text", + "cached": False, + }, + } + ] + } + mock_response = _make_mock_response(fetched) + result = config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + first = result.results[0] + fetch_field = getattr(first, "fetch", None) + assert isinstance(fetch_field, dict) + assert fetch_field["text"] == "Body text" def test_no_request_uses_default_max_results(self): config = TinyfishSearchConfig() @@ -322,6 +459,220 @@ class TestTransformSearchResponse: ) assert len(result.results) == 2 + def test_parameter_warnings_reader_emits_log_lines(self, caplog): + # When TinyFish responds with a top-level `parameter_warnings` array + # (post-rollout of that contract), each entry is re-fired as a + # verbose_logger.warning so callers see what was ignored. + config = TinyfishSearchConfig() + body = { + "results": [ + {"title": "x", "url": "https://x", "snippet": "x"}, + ], + "parameter_warnings": [ + { + "type": "unsupported", + "parameter": "max_tokens_per_page", + "message": "Parameter not supported by TinyFish Search.", + "docs_url": "https://docs.tinyfish.ai/search-api", + }, + ], + } + mock_response = _make_mock_response(body) + with caplog.at_level("WARNING"): + config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + messages = [r.getMessage() for r in caplog.records] + assert any("max_tokens_per_page" in m for m in messages) + # The type is included in the message so agents can branch on it. + assert any("unsupported" in m for m in messages) + + def test_parameter_warnings_absent_no_log(self, caplog): + # Absence of the field is silent — most responses won't carry it. + config = TinyfishSearchConfig() + mock_response = _make_mock_response(MOCK_TINYFISH_RESPONSE) + with caplog.at_level("WARNING"): + config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + assert not any( + "TinyFish Search ignored" in r.getMessage() for r in caplog.records + ) + + def test_parameter_warnings_malformed_shapes_never_throw(self): + # Every shape that doesn't match {parameter: str, message: str} should + # silently no-op. None of these should raise an exception. + config = TinyfishSearchConfig() + + good_results = [{"title": "x", "url": "https://x", "snippet": "x"}] + + malformed_field_values = [ + "not a list", # string + 42, # int + {"parameter": "x", "message": "y"}, # dict instead of list + True, # bool + ] + for bad_value in malformed_field_values: + body = {"results": good_results, "parameter_warnings": bad_value} + config.transform_search_response( + raw_response=_make_mock_response(body), logging_obj=None + ) # must not raise + + malformed_entries = [ + "string in list", # non-dict + 42, # int + {}, # missing all + {"type": "unsupported", "parameter": "x"}, # missing message + {"type": "unsupported", "message": "y"}, # missing parameter + {"parameter": "x", "message": "y"}, # missing type + { + "type": "unsupported", + "parameter": None, + "message": "y", + }, # null parameter + {"type": "unsupported", "parameter": "x", "message": ""}, # empty message + { + "type": "unsupported", + "parameter": 42, + "message": "y", + }, # non-string parameter + {"type": 1, "parameter": "x", "message": "y"}, # non-string type + ] + body = {"results": good_results, "parameter_warnings": malformed_entries} + config.transform_search_response( + raw_response=_make_mock_response(body), logging_obj=None + ) # must not raise + + def test_parameter_warnings_malformed_entries_emit_nothing(self, caplog): + config = TinyfishSearchConfig() + body = { + "results": [{"title": "x", "url": "https://x", "snippet": "x"}], + "parameter_warnings": [ + {"type": "unsupported", "parameter": "x"}, # missing message — skipped + { + "type": "unsupported", + "parameter": "valid_one", + "message": "actual msg", + }, # ok — emitted + {"parameter": "x", "message": "y"}, # missing type — skipped + ], + } + with caplog.at_level("WARNING"): + config.transform_search_response( + raw_response=_make_mock_response(body), logging_obj=None + ) + messages = [r.getMessage() for r in caplog.records] + assert sum("parameter_warning" in m for m in messages) == 1 + assert any("valid_one" in m for m in messages) + + +class TestErrorHandling: + def test_4xx_response_raises_with_attribution_and_unwrapped_message(self): + # Reproduces ux-labs' error envelope shape for an INVALID_INPUT response. + config = TinyfishSearchConfig() + body = { + "error": { + "code": "INVALID_INPUT", + "message": "query is required", + "details": [{"field": "query"}], + } + } + mock_response = _make_mock_response(body, status_code=400) + with pytest.raises(Exception) as exc_info: + config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + msg = str(exc_info.value) + assert "TinyFish Search:" in msg + assert "query is required" in msg + assert "docs.tinyfish.ai/search-api" in msg + assert getattr(exc_info.value, "status_code", None) == 400 + + def test_429_preserves_status_code_and_headers(self): + config = TinyfishSearchConfig() + body = {"error": {"code": "RATE_LIMIT_EXCEEDED", "message": "60 rpm"}} + mock_response = _make_mock_response( + body, status_code=429, headers={"Retry-After": "60"} + ) + with pytest.raises(Exception) as exc_info: + config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + assert getattr(exc_info.value, "status_code", None) == 429 + headers = getattr(exc_info.value, "headers", {}) or {} + assert headers.get("Retry-After") == "60" + + def test_5xx_with_non_ux_labs_body_falls_back_to_raw_text(self): + # Cloudflare-style JSON or any other envelope: unwrap fails, fall back to raw. + config = TinyfishSearchConfig() + body = {"errors": [{"code": "10000", "message": "Internal"}]} + mock_response = _make_mock_response(body, status_code=502) + with pytest.raises(Exception) as exc_info: + config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + msg = str(exc_info.value) + assert "TinyFish Search:" in msg + # The raw JSON body string should appear in the message verbatim. + assert "10000" in msg + + def test_non_json_4xx_body_uses_raw_text(self): + config = TinyfishSearchConfig() + mock_response = _make_mock_response( + json_data=None, status_code=502, text="Bad Gateway" + ) + with pytest.raises(Exception) as exc_info: + config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + msg = str(exc_info.value) + assert "TinyFish Search:" in msg + assert "Bad Gateway" in msg + + def test_non_json_200_body_routes_through_get_error_class(self): + # 200 but the body isn't JSON (degraded backend, CDN-injected page, etc.) + config = TinyfishSearchConfig() + mock_response = _make_mock_response( + json_data=None, status_code=200, text="not json" + ) + with pytest.raises(Exception) as exc_info: + config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + msg = str(exc_info.value) + assert "TinyFish Search:" in msg + assert "Expected JSON response" in msg + + def test_wrap_error_returns_attributed_baselm_exception_directly(self): + # Direct unit test of the private _wrap_error helper used by + # transform_search_response. Network failures don't go through this; + # they hit BaseSearchConfig.get_error_class via LiteLLM core. + config = TinyfishSearchConfig() + body = '{"error": {"code": "UNAUTHORIZED", "message": "bad key"}}' + exc = config._wrap_error( + error_message=body, status_code=401, headers={"x": "y"} + ) + msg = str(exc) + assert "TinyFish Search:" in msg + assert "bad key" in msg + assert exc.status_code == 401 + + def test_schema_mismatch_wraps_with_attribution(self): + # When TinyFish returns a 200 with a body shape that doesn't match + # LiteLLM's SearchResponse contract (e.g. missing top-level `results`), + # raise with TinyFish attribution + docs link so the caller knows to + # check TinyFish's schema, not their own input. + config = TinyfishSearchConfig() + mock_response = _make_mock_response({"query": "x"}) # no `results` key + with pytest.raises(Exception) as exc_info: + config.transform_search_response( + raw_response=mock_response, logging_obj=None + ) + msg = str(exc_info.value) + assert "TinyFish Search:" in msg + assert "schema" in msg.lower() + assert "docs.tinyfish.ai/search-api" in msg + class TestAppendDomainFilters: def test_single_domain(self): @@ -331,3 +682,21 @@ class TestAppendDomainFilters: def test_multiple_domains(self): result = _append_domain_filters("query", ["a.com", "b.com", "c.com"]) assert result == "(query) (site:a.com OR site:b.com OR site:c.com)" + + +class TestDefaultMissingResultFields: + def test_non_dict_raw_json_is_noop(self): + # raw_json could be a string/list/None if TinyFish ever returns a + # non-envelope shape; the helper just returns without mutating. + for payload in ("not a dict", ["list"], None, 42): + _default_missing_result_fields(payload) # must not raise + + def test_non_dict_results_item_skipped(self): + # If `results` contains a non-dict entry (string, int, etc.), the helper + # skips it; SearchResponse.model_validate will reject it later. + raw_json = {"results": ["string item", 42, {"title": "ok"}]} + _default_missing_result_fields(raw_json) + # Only the dict item gets defaulted; the others are unchanged. + assert raw_json["results"][0] == "string item" + assert raw_json["results"][1] == 42 + assert raw_json["results"][2] == {"title": "ok", "url": "", "snippet": ""} diff --git a/tests/test_litellm/llms/vertex_ai/audio_transcription/__init__.py b/tests/test_litellm/llms/vertex_ai/audio_transcription/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/llms/vertex_ai/audio_transcription/test_vertex_ai_audio_transcription_transformation.py b/tests/test_litellm/llms/vertex_ai/audio_transcription/test_vertex_ai_audio_transcription_transformation.py new file mode 100644 index 00000000000..3fa28699f73 --- /dev/null +++ b/tests/test_litellm/llms/vertex_ai/audio_transcription/test_vertex_ai_audio_transcription_transformation.py @@ -0,0 +1,336 @@ +import base64 +import json +import os +import sys +from urllib.parse import urlparse + +import httpx +import pytest + +sys.path.insert(0, os.path.abspath("../../../../..")) + +import litellm +from litellm.llms.vertex_ai.audio_transcription.transformation import ( + VertexAIAudioTranscriptionConfig, +) +from litellm.llms.vertex_ai.common_utils import VertexAIError +from litellm.types.utils import LlmProviders +from litellm.utils import ProviderConfigManager, get_optional_params_transcription + + +@pytest.fixture +def config(): + return VertexAIAudioTranscriptionConfig() + + +class TestGetCompleteUrl: + def test_defaults_to_us_regional_host(self, config): + url = config.get_complete_url( + api_base=None, + api_key=None, + model="chirp_3", + optional_params={}, + litellm_params={"vertex_project": "test-project"}, + ) + assert url == "https://us-speech.googleapis.com/v2/projects/test-project/locations/us/recognizers/_:recognize" + + def test_uses_vertex_location_for_regional_host(self, config): + url = config.get_complete_url( + api_base=None, + api_key=None, + model="chirp_3", + optional_params={}, + litellm_params={"vertex_project": "test-project", "vertex_location": "eu"}, + ) + assert url == "https://eu-speech.googleapis.com/v2/projects/test-project/locations/eu/recognizers/_:recognize" + + def test_global_location_uses_unprefixed_host(self, config): + url = config.get_complete_url( + api_base=None, + api_key=None, + model="chirp_3", + optional_params={}, + litellm_params={"vertex_project": "test-project", "vertex_location": "global"}, + ) + assert url == "https://speech.googleapis.com/v2/projects/test-project/locations/global/recognizers/_:recognize" + + def test_api_base_override(self, config): + url = config.get_complete_url( + api_base="http://localhost:8080/", + api_key=None, + model="chirp_3", + optional_params={}, + litellm_params={"vertex_project": "test-project"}, + ) + assert url == "http://localhost:8080/v2/projects/test-project/locations/us/recognizers/_:recognize" + + @pytest.mark.parametrize( + "location,expected_netloc", + [ + ("us", "us-speech.googleapis.com"), + ("us-central1", "us-central1-speech.googleapis.com"), + ("eu", "eu-speech.googleapis.com"), + ("global", "speech.googleapis.com"), + ], + ) + def test_valid_location_netloc_always_google(self, config, location, expected_netloc): + url = config.get_complete_url( + api_base=None, + api_key=None, + model="chirp_3", + optional_params={}, + litellm_params={"vertex_project": "test-project", "vertex_location": location}, + ) + netloc = urlparse(url).netloc + assert netloc == expected_netloc + assert netloc.endswith("speech.googleapis.com") + + @pytest.mark.parametrize( + "malicious_location", + [ + "attacker.example/", + "evil.com#", + "us.attacker.example", + "us/../..", + "US", + "us_central1", + "us central1", + "attacker.example:443", + "-us", + ], + ) + def test_malicious_location_is_rejected(self, config, malicious_location): + """SSRF/credential-exfil guard: vertex_location is client-controllable on + the proxy, so a host-injecting value must raise rather than steer the + request (and its admin-minted Google bearer token) at another host.""" + with pytest.raises(VertexAIError): + config.get_complete_url( + api_base=None, + api_key=None, + model="chirp_3", + optional_params={}, + litellm_params={"vertex_project": "test-project", "vertex_location": malicious_location}, + ) + + @pytest.mark.parametrize( + "malicious_project", + [ + "proj/../../locations", + "proj/evil", + "proj#frag", + "proj?a=b", + "proj:evil", + "proj space", + ], + ) + def test_malicious_project_is_rejected(self, config, malicious_project): + with pytest.raises(VertexAIError): + config.get_complete_url( + api_base=None, + api_key=None, + model="chirp_3", + optional_params={}, + litellm_params={"vertex_project": malicious_project, "vertex_location": "us"}, + ) + + +class TestTransformRequest: + def test_request_body_shape(self, config): + audio_bytes = b"fake-audio-bytes" + request_data = config.transform_audio_transcription_request( + model="chirp_3", + audio_file=audio_bytes, + optional_params={}, + litellm_params={}, + ) + assert request_data.files is None + assert request_data.data == { + "config": { + "model": "chirp_3", + "languageCodes": ["auto"], + "features": {"enableAutomaticPunctuation": True}, + "autoDecodingConfig": {}, + }, + "content": base64.b64encode(audio_bytes).decode("utf-8"), + } + + @pytest.mark.parametrize( + "language,expected_language_codes", + [ + ("en", ["en-US"]), + ("en-US", ["en-US"]), + ("es-ES", ["es-ES"]), + ("fr", ["fr-FR"]), + (None, ["auto"]), + ], + ) + def test_language_param_maps_to_language_codes(self, config, language, expected_language_codes): + request_data = config.transform_audio_transcription_request( + model="chirp_3", + audio_file=b"fake-audio-bytes", + optional_params={"language": language} if language is not None else {}, + litellm_params={}, + ) + assert request_data.data["config"]["languageCodes"] == expected_language_codes + + def test_model_prefix_is_stripped(self, config): + request_data = config.transform_audio_transcription_request( + model="vertex_ai/chirp_3", + audio_file=b"fake-audio-bytes", + optional_params={}, + litellm_params={}, + ) + assert request_data.data["config"]["model"] == "chirp_3" + + def test_body_is_json_serializable(self, config): + request_data = config.transform_audio_transcription_request( + model="chirp_3", + audio_file=b"fake-audio-bytes", + optional_params={}, + litellm_params={}, + ) + json.dumps(request_data.data) + + +class TestTransformResponse: + def test_multi_result_transcripts_are_joined(self, config): + raw_response = httpx.Response( + status_code=200, + json={ + "results": [ + {"alternatives": [{"transcript": "Hello world.", "confidence": 0.98}], "languageCode": "en-US"}, + {"alternatives": [{"transcript": "How are you?", "confidence": 0.97}], "languageCode": "en-US"}, + ], + "metadata": {"totalBilledDuration": "15s"}, + }, + ) + response = config.transform_audio_transcription_response(raw_response) + assert response.text == "Hello world. How are you?" + assert response["task"] == "transcribe" + assert response["language"] == "en-US" + assert response["duration"] == 15.0 + + def test_results_without_alternatives_are_skipped(self, config): + raw_response = httpx.Response( + status_code=200, + json={ + "results": [ + {"alternatives": [{"transcript": "First."}]}, + {"alternatives": []}, + {}, + {"alternatives": [{"transcript": "Last."}]}, + ] + }, + ) + response = config.transform_audio_transcription_response(raw_response) + assert response.text == "First. Last." + + def test_empty_results_returns_empty_text(self, config): + raw_response = httpx.Response(status_code=200, json={}) + response = config.transform_audio_transcription_response(raw_response) + assert response.text == "" + + def test_fractional_billed_duration(self, config): + raw_response = httpx.Response( + status_code=200, + json={ + "results": [{"alternatives": [{"transcript": "Hi."}]}], + "metadata": {"totalBilledDuration": "3.5s"}, + }, + ) + response = config.transform_audio_transcription_response(raw_response) + assert response["duration"] == 3.5 + + +class TestValidateEnvironment: + def test_sets_oauth_headers(self): + class StubbedConfig(VertexAIAudioTranscriptionConfig): + def _ensure_access_token(self, credentials, project_id, custom_llm_provider): + return "fake-token", "resolved-project" + + headers = StubbedConfig().validate_environment( + headers={}, + model="chirp_3", + messages=[], + optional_params={}, + litellm_params={"vertex_project": "resolved-project"}, + ) + assert headers["Authorization"] == "Bearer fake-token" + assert headers["x-goog-user-project"] == "resolved-project" + assert headers["Content-Type"] == "application/json" + + +class TestProviderRouting: + def test_provider_config_manager_returns_vertex_config(self): + provider_config = ProviderConfigManager.get_provider_audio_transcription_config( + model="chirp_3", + provider=LlmProviders.VERTEX_AI, + ) + assert isinstance(provider_config, VertexAIAudioTranscriptionConfig) + + def test_get_optional_params_transcription_maps_language(self): + optional_params = get_optional_params_transcription( + model="chirp_3", + custom_llm_provider="vertex_ai", + language="fr-FR", + response_format="json", + ) + assert optional_params["language"] == "fr-FR" + assert optional_params["response_format"] == "json" + + def test_get_optional_params_transcription_rejects_unsupported_param(self): + with pytest.raises(litellm.utils.UnsupportedParamsError): + get_optional_params_transcription( + model="chirp_3", + custom_llm_provider="vertex_ai", + temperature=1, + ) + + @pytest.mark.parametrize("response_format", ["json", "text"]) + def test_supported_response_formats_pass_through(self, response_format): + optional_params = get_optional_params_transcription( + model="chirp_3", + custom_llm_provider="vertex_ai", + response_format=response_format, + ) + assert optional_params["response_format"] == response_format + + @pytest.mark.parametrize("response_format", ["verbose_json", "srt", "vtt"]) + def test_unsupported_response_format_raises(self, response_format): + with pytest.raises(litellm.utils.UnsupportedParamsError, match="response_format"): + get_optional_params_transcription( + model="chirp_3", + custom_llm_provider="vertex_ai", + response_format=response_format, + ) + + @pytest.mark.parametrize("response_format", ["verbose_json", "srt", "vtt"]) + def test_unsupported_response_format_dropped_with_drop_params(self, response_format): + optional_params = get_optional_params_transcription( + model="chirp_3", + custom_llm_provider="vertex_ai", + language="fr-FR", + response_format=response_format, + drop_params=True, + ) + assert "response_format" not in optional_params + assert optional_params["language"] == "fr-FR" + + +class TestModelCostEntry: + REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../..")) + + @pytest.mark.parametrize( + "cost_map_path", + [ + "model_prices_and_context_window.json", + "litellm/model_prices_and_context_window_backup.json", + ], + ) + def test_chirp_3_registered_as_audio_transcription(self, cost_map_path): + with open(os.path.join(self.REPO_ROOT, cost_map_path)) as f: + entry = json.load(f)["vertex_ai/chirp_3"] + assert entry["mode"] == "audio_transcription" + assert entry["litellm_provider"] == "vertex_ai" + assert entry["input_cost_per_second"] == pytest.approx(0.016 / 60, rel=1e-3) + assert entry["supported_endpoints"] == ["/v1/audio/transcriptions"] diff --git a/tests/test_litellm/llms/vertex_ai/batches/test_transformation.py b/tests/test_litellm/llms/vertex_ai/batches/test_transformation.py index 37084d43441..71da1d39876 100644 --- a/tests/test_litellm/llms/vertex_ai/batches/test_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/batches/test_transformation.py @@ -47,9 +47,7 @@ def test_transform_openai_request_builds_full_vertex_job(): "litellm.llms.vertex_ai.batches.transformation.uuid.uuid4", return_value="fixed-uuid", ): - job = T.transform_openai_batch_request_to_vertex_ai_batch_request( - {"input_file_id": INPUT_FILE} - ) + job = T.transform_openai_batch_request_to_vertex_ai_batch_request({"input_file_id": INPUT_FILE}) assert job["displayName"] == "litellm-vertex-batch-fixed-uuid" assert job["model"] == "publishers/google/models/gemini-1.5-flash-001" @@ -91,13 +89,11 @@ def test_transform_vertex_response_full_mapping(): assert isinstance(batch, LiteLLMBatch) assert batch.id == "3814889423749775360" - assert batch.completion_window == "24hrs" + assert batch.completion_window == "24h" # created_at is parsed via the shared helper (uses local tz); assert the # transform forwards createTime through that helper rather than a hardcoded # epoch that would be tz-dependent - assert batch.created_at == _convert_vertex_datetime_to_openai_datetime( - "2024-12-04T21:53:12.120184Z" - ) + assert batch.created_at == _convert_vertex_datetime_to_openai_datetime("2024-12-04T21:53:12.120184Z") assert batch.endpoint == "" assert batch.object == "batch" assert batch.input_file_id == "gs://bucket/in.jsonl" @@ -140,10 +136,7 @@ def test_transform_vertex_response_error_file_id_always_none(): ], ) def test_status_mapping_every_entry(vertex_state, expected): - assert ( - T._get_batch_job_status_from_vertex_ai_batch_response({"state": vertex_state}) - == expected - ) + assert T._get_batch_job_status_from_vertex_ai_batch_response({"state": vertex_state}) == expected def test_status_mapping_defaults_to_unspecified_when_missing(): @@ -163,9 +156,7 @@ def test_status_mapping_unknown_state_raises_keyerror(): def test_get_batch_id_splits_path(): assert ( - T._get_batch_id_from_vertex_ai_batch_response( - {"name": "projects/p/locations/l/batchPredictionJobs/999"} - ) + T._get_batch_id_from_vertex_ai_batch_response({"name": "projects/p/locations/l/batchPredictionJobs/999"}) == "999" ) @@ -198,18 +189,11 @@ def test_get_input_file_id_missing_input_config(): def test_get_input_file_id_missing_gcs_source(): - assert ( - T._get_input_file_id_from_vertex_ai_batch_response({"inputConfig": {}}) == "" - ) + assert T._get_input_file_id_from_vertex_ai_batch_response({"inputConfig": {}}) == "" def test_get_input_file_id_empty_uris(): - assert ( - T._get_input_file_id_from_vertex_ai_batch_response( - {"inputConfig": {"gcsSource": {"uris": []}}} - ) - == "" - ) + assert T._get_input_file_id_from_vertex_ai_batch_response({"inputConfig": {"gcsSource": {"uris": []}}}) == "" # =========================================================================== # @@ -220,18 +204,14 @@ def test_get_input_file_id_empty_uris(): def test_get_output_file_id_from_output_info(): # outputInfo branch: rstrip trailing slash, append predictions.jsonl assert ( - T._get_output_file_id_from_vertex_ai_batch_response( - {"outputInfo": {"gcsOutputDirectory": "gs://bucket/out/"}} - ) + T._get_output_file_id_from_vertex_ai_batch_response({"outputInfo": {"gcsOutputDirectory": "gs://bucket/out/"}}) == "gs://bucket/out/predictions.jsonl" ) def test_get_output_file_id_output_info_no_trailing_slash(): assert ( - T._get_output_file_id_from_vertex_ai_batch_response( - {"outputInfo": {"gcsOutputDirectory": "gs://bucket/out"}} - ) + T._get_output_file_id_from_vertex_ai_batch_response({"outputInfo": {"gcsOutputDirectory": "gs://bucket/out"}}) == "gs://bucket/out/predictions.jsonl" ) @@ -243,10 +223,7 @@ def test_get_output_file_id_empty_output_info_falls_through_to_output_config(): "outputInfo": {}, "outputConfig": {"gcsDestination": {"outputUriPrefix": "gs://b/cfg"}}, } - assert ( - T._get_output_file_id_from_vertex_ai_batch_response(resp) - == "gs://b/cfg/predictions.jsonl" - ) + assert T._get_output_file_id_from_vertex_ai_batch_response(resp) == "gs://b/cfg/predictions.jsonl" def test_get_output_file_id_no_output_info_and_no_output_config(): @@ -255,32 +232,18 @@ def test_get_output_file_id_no_output_info_and_no_output_config(): def test_get_output_file_id_output_config_missing_gcs_destination(): # outputConfig present but no gcsDestination -> returns the running "" value - assert ( - T._get_output_file_id_from_vertex_ai_batch_response({"outputConfig": {}}) == "" - ) + assert T._get_output_file_id_from_vertex_ai_batch_response({"outputConfig": {}}) == "" def test_get_output_file_id_output_config_already_has_suffix(): # outputUriPrefix already ends in /predictions.jsonl -> returned as-is (no double append) - resp = { - "outputConfig": { - "gcsDestination": {"outputUriPrefix": "gs://b/cfg/predictions.jsonl"} - } - } - assert ( - T._get_output_file_id_from_vertex_ai_batch_response(resp) - == "gs://b/cfg/predictions.jsonl" - ) + resp = {"outputConfig": {"gcsDestination": {"outputUriPrefix": "gs://b/cfg/predictions.jsonl"}}} + assert T._get_output_file_id_from_vertex_ai_batch_response(resp) == "gs://b/cfg/predictions.jsonl" def test_get_output_file_id_output_config_strips_trailing_slash(): - resp = { - "outputConfig": {"gcsDestination": {"outputUriPrefix": "gs://b/cfg/"}} - } - assert ( - T._get_output_file_id_from_vertex_ai_batch_response(resp) - == "gs://b/cfg/predictions.jsonl" - ) + resp = {"outputConfig": {"gcsDestination": {"outputUriPrefix": "gs://b/cfg/"}}} + assert T._get_output_file_id_from_vertex_ai_batch_response(resp) == "gs://b/cfg/predictions.jsonl" def test_get_output_file_id_output_info_takes_precedence_over_output_config(): @@ -288,10 +251,7 @@ def test_get_output_file_id_output_info_takes_precedence_over_output_config(): "outputInfo": {"gcsOutputDirectory": "gs://from-info"}, "outputConfig": {"gcsDestination": {"outputUriPrefix": "gs://from-config"}}, } - assert ( - T._get_output_file_id_from_vertex_ai_batch_response(resp) - == "gs://from-info/predictions.jsonl" - ) + assert T._get_output_file_id_from_vertex_ai_batch_response(resp) == "gs://from-info/predictions.jsonl" # =========================================================================== # @@ -301,16 +261,13 @@ def test_get_output_file_id_output_info_takes_precedence_over_output_config(): def test_get_gcs_uri_prefix_root(): assert ( - T._get_gcs_uri_prefix_from_file("gs://litellm-testing-bucket/vtx_batch.jsonl") - == "gs://litellm-testing-bucket" + T._get_gcs_uri_prefix_from_file("gs://litellm-testing-bucket/vtx_batch.jsonl") == "gs://litellm-testing-bucket" ) def test_get_gcs_uri_prefix_nested(): assert ( - T._get_gcs_uri_prefix_from_file( - "gs://litellm-testing-bucket/batches/vtx_batch.jsonl" - ) + T._get_gcs_uri_prefix_from_file("gs://litellm-testing-bucket/batches/vtx_batch.jsonl") == "gs://litellm-testing-bucket/batches" ) @@ -321,21 +278,13 @@ def test_get_gcs_uri_prefix_nested(): def test_get_model_from_gcs_file_plain(): - assert ( - T._get_model_from_gcs_file(INPUT_FILE) - == "publishers/google/models/gemini-1.5-flash-001" - ) + assert T._get_model_from_gcs_file(INPUT_FILE) == "publishers/google/models/gemini-1.5-flash-001" def test_get_model_from_gcs_file_url_encoded(): # %2F decodes to "/" via urllib.unquote before splitting - encoded = ( - "gs://bucket/publishers%2Fgoogle%2Fmodels%2Fgemini-1.5-flash-001%2Fuuid" - ) - assert ( - T._get_model_from_gcs_file(encoded) - == "publishers/google/models/gemini-1.5-flash-001" - ) + encoded = "gs://bucket/publishers%2Fgoogle%2Fmodels%2Fgemini-1.5-flash-001%2Fuuid" + assert T._get_model_from_gcs_file(encoded) == "publishers/google/models/gemini-1.5-flash-001" def test_get_model_from_gcs_file_no_publishers_raises(): @@ -389,8 +338,6 @@ def test_list_response_empty(): def test_list_response_none_jobs_treated_as_empty(): - out = T.transform_vertex_ai_batch_list_response_to_openai_list_response( - {"batchPredictionJobs": None} - ) + out = T.transform_vertex_ai_batch_list_response_to_openai_list_response({"batchPredictionJobs": None}) assert out["data"] == [] assert out["first_id"] is None diff --git a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py index 20e7aec377b..51f13affa3f 100644 --- a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py +++ b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py @@ -5210,3 +5210,41 @@ class TestModelResponseIteratorCleanup: mock_iterator.aclose.assert_awaited_once() mock_response.aclose.assert_awaited_once() + + +def test_process_candidates_merges_thought_signatures_and_server_side_tools(): + """ + thought_signatures and server_side_tool_invocations must both survive in + provider_specific_fields when a candidate carries the two at once; the second + merge must extend the dict created by the first, not replace it. + """ + candidates = [ + { + "content": { + "role": "model", + "parts": [ + {"text": "the weather is sunny", "thoughtSignature": "sig-text"}, + { + "toolCall": { + "toolType": "google_search", + "id": "tool-1", + "args": {"query": "weather"}, + } + }, + ], + }, + "finishReason": "STOP", + } + ] + model_response = ModelResponse() + + VertexGeminiConfig._process_candidates( + _candidates=candidates, + model_response=model_response, + standard_optional_params={}, + cumulative_tool_call_index=0, + ) + + fields = model_response.choices[-1].message.provider_specific_fields + assert fields["thought_signatures"] == ["sig-text"] + assert fields["server_side_tool_invocations"][0]["id"] == "tool-1" diff --git a/tests/test_litellm/llms/vertex_ai/image_generation/test_vertex_ai_image_generation_transformation.py b/tests/test_litellm/llms/vertex_ai/image_generation/test_vertex_ai_image_generation_transformation.py index dc2d945c33b..8c72bdee525 100644 --- a/tests/test_litellm/llms/vertex_ai/image_generation/test_vertex_ai_image_generation_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/image_generation/test_vertex_ai_image_generation_transformation.py @@ -33,27 +33,21 @@ class TestVertexAIGeminiImageGenerationConfig: """Test mapping n parameter to candidate_count""" non_default_params = {"n": 3} optional_params = {} - result = self.config.map_openai_params( - non_default_params, optional_params, "gemini-2.5-flash-image", False - ) + result = self.config.map_openai_params(non_default_params, optional_params, "gemini-2.5-flash-image", False) assert result.get("candidate_count") == 3 def test_map_openai_params_size(self): """Test mapping size parameter to aspectRatio""" non_default_params = {"size": "1024x1024"} optional_params = {} - result = self.config.map_openai_params( - non_default_params, optional_params, "gemini-2.5-flash-image", False - ) + result = self.config.map_openai_params(non_default_params, optional_params, "gemini-2.5-flash-image", False) assert result.get("aspectRatio") == "1:1" def test_map_openai_params_size_16_9(self): """Test mapping 16:9 size""" non_default_params = {"size": "1792x1024"} optional_params = {} - result = self.config.map_openai_params( - non_default_params, optional_params, "gemini-2.5-flash-image", False - ) + result = self.config.map_openai_params(non_default_params, optional_params, "gemini-2.5-flash-image", False) assert result.get("aspectRatio") == "16:9" def test_map_size_to_aspect_ratio(self): @@ -67,42 +61,106 @@ class TestVertexAIGeminiImageGenerationConfig: def test_get_supported_openai_params_includes_native_gemini_params(self): """Test that native Gemini imageConfig params are supported""" - supported = self.config.get_supported_openai_params( - "gemini-3-pro-image-preview" - ) + supported = self.config.get_supported_openai_params("gemini-3-pro-image-preview") assert "aspectRatio" in supported assert "aspect_ratio" in supported assert "imageSize" in supported assert "image_size" in supported + assert "imageConfig" in supported def test_map_openai_params_aspect_ratio_camel_case(self): """Test mapping native aspectRatio parameter""" - result = self.config.map_openai_params( - {"aspectRatio": "9:16"}, {}, "gemini-3-pro-image-preview", False - ) + result = self.config.map_openai_params({"aspectRatio": "9:16"}, {}, "gemini-3-pro-image-preview", False) assert result["aspectRatio"] == "9:16" def test_map_openai_params_aspect_ratio_snake_case(self): """Test mapping native aspect_ratio parameter""" - result = self.config.map_openai_params( - {"aspect_ratio": "16:9"}, {}, "gemini-3-pro-image-preview", False - ) + result = self.config.map_openai_params({"aspect_ratio": "16:9"}, {}, "gemini-3-pro-image-preview", False) assert result["aspectRatio"] == "16:9" def test_map_openai_params_image_size_camel_case(self): """Test mapping native imageSize parameter""" - result = self.config.map_openai_params( - {"imageSize": "4K"}, {}, "gemini-3-pro-image-preview", False - ) + result = self.config.map_openai_params({"imageSize": "4K"}, {}, "gemini-3-pro-image-preview", False) assert result["imageSize"] == "4K" def test_map_openai_params_image_size_snake_case(self): """Test mapping native image_size parameter""" - result = self.config.map_openai_params( - {"image_size": "2K"}, {}, "gemini-3-pro-image-preview", False - ) + result = self.config.map_openai_params({"image_size": "2K"}, {}, "gemini-3-pro-image-preview", False) assert result["imageSize"] == "2K" + def test_map_openai_params_image_config_dict_stored_whole(self): + """imageConfig dict is stored as-is so all fields survive""" + result = self.config.map_openai_params( + {"imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"}}, + {}, + "gemini-3.1-flash-image", + False, + ) + assert result["imageConfig"] == {"aspectRatio": "16:9", "imageSize": "2K"} + + def test_map_openai_params_image_config_all_fields(self): + """All ImageConfig fields (personGeneration, imageOutputOptions) pass through""" + payload = { + "imageConfig": { + "aspectRatio": "9:16", + "imageSize": "4K", + "personGeneration": "DONT_ALLOW", + "imageOutputOptions": { + "mimeType": "image/jpeg", + "compressionQuality": 80, + }, + } + } + result = self.config.map_openai_params(payload, {}, "gemini-3.1-flash-image", False) + assert result["imageConfig"] == payload["imageConfig"] + + def test_map_openai_params_image_config_non_dict_warns_and_drops(self): + """Non-dict imageConfig is dropped with a warning, not silently discarded""" + with patch("litellm.llms.vertex_ai.image_generation.vertex_gemini_transformation.verbose_logger") as mock_log: + result = self.config.map_openai_params( + {"imageConfig": "bad-string-value"}, {}, "gemini-3.1-flash-image", False + ) + assert "imageConfig" not in result + mock_log.warning.assert_called_once() + + def test_transform_image_generation_request_from_image_config(self): + """Full imageConfig dict is forwarded verbatim into generationConfig""" + full_config = { + "aspectRatio": "16:9", + "imageSize": "2K", + "personGeneration": "DONT_ALLOW", + "imageOutputOptions": {"mimeType": "image/jpeg", "compressionQuality": 85}, + } + mapped = self.config.map_openai_params( + {"imageConfig": full_config}, + {}, + "gemini-3.1-flash-image", + False, + ) + request = self.config.transform_image_generation_request( + model="gemini-3.1-flash-image", + prompt="A nano banana on a desk", + optional_params=mapped, + litellm_params={}, + headers={}, + ) + assert request["generationConfig"]["imageConfig"] == full_config + + def test_transform_image_generation_flat_params_override_image_config(self): + """Explicit flat params win over the same key inside imageConfig""" + request = self.config.transform_image_generation_request( + model="gemini-3.1-flash-image", + prompt="A nano banana", + optional_params={ + "imageConfig": {"aspectRatio": "1:1", "personGeneration": "DONT_ALLOW"}, + "aspectRatio": "16:9", # should win + }, + litellm_params={}, + headers={}, + ) + assert request["generationConfig"]["imageConfig"]["aspectRatio"] == "16:9" + assert request["generationConfig"]["imageConfig"]["personGeneration"] == "DONT_ALLOW" + def test_transform_image_generation_request_basic(self): """Test basic request transformation""" request = self.config.transform_image_generation_request( @@ -141,9 +199,7 @@ class TestVertexAIGeminiImageGenerationConfig: def test_map_openai_params_web_search_options(self): """Test web_search_options maps to googleSearch tool""" - result = self.config.map_openai_params( - {"web_search_options": {}}, {}, "gemini-3.1-flash-image-preview", False - ) + result = self.config.map_openai_params({"web_search_options": {}}, {}, "gemini-3.1-flash-image-preview", False) assert result["tools"] == [{"googleSearch": {}}] def test_transform_image_generation_request_with_web_search_tools(self): @@ -173,9 +229,7 @@ class TestVertexAIGeminiImageGenerationConfig: headers={}, ) assert request["tools"] == [{"googleMaps": {}}] - assert request["toolConfig"] == { - "retrievalConfig": {"latLng": {"latitude": 37.7, "longitude": -122.4}} - } + assert request["toolConfig"] == {"retrievalConfig": {"latLng": {"latitude": 37.7, "longitude": -122.4}}} def test_transform_image_generation_request_with_candidate_count(self): """Test request transformation with candidate_count""" @@ -344,10 +398,7 @@ class TestVertexAIGeminiImageGenerationConfig: assert len(result.data) == 1 assert result.data[0].b64_json == "base64_encoded_image_data" - assert ( - result.data[0].provider_specific_fields["thought_signature"] - == "test_signature_abc123" - ) + assert result.data[0].provider_specific_fields["thought_signature"] == "test_signature_abc123" def test_transform_image_generation_response_tracks_web_search_requests(self): """Grounding queries are carried onto usage so search spend can be billed""" @@ -366,9 +417,7 @@ class TestVertexAIGeminiImageGenerationConfig: } ] }, - "groundingMetadata": { - "webSearchQueries": ["eiffel tower", "paris skyline"] - }, + "groundingMetadata": {"webSearchQueries": ["eiffel tower", "paris skyline"]}, } ], "usageMetadata": { @@ -410,18 +459,14 @@ class TestVertexAIImagenImageGenerationConfig: """Test mapping n parameter to sampleCount""" non_default_params = {"n": 3} optional_params = {} - result = self.config.map_openai_params( - non_default_params, optional_params, "imagegeneration@006", False - ) + result = self.config.map_openai_params(non_default_params, optional_params, "imagegeneration@006", False) assert result.get("sampleCount") == 3 def test_map_openai_params_size(self): """Test mapping size parameter to aspectRatio""" non_default_params = {"size": "1024x1024"} optional_params = {} - result = self.config.map_openai_params( - non_default_params, optional_params, "imagegeneration@006", False - ) + result = self.config.map_openai_params(non_default_params, optional_params, "imagegeneration@006", False) assert result.get("aspectRatio") == "1:1" def test_map_size_to_aspect_ratio(self): @@ -462,9 +507,7 @@ class TestVertexAIImagenImageGenerationConfig: model="imagegeneration@006", prompt="A cat", optional_params={}, - litellm_params={ - "metadata": {"requester_metadata": {"team": "platform", "env": "prod"}} - }, + litellm_params={"metadata": {"requester_metadata": {"team": "platform", "env": "prod"}}}, headers={}, ) assert request["labels"] == {"team": "platform", "env": "prod"} @@ -474,9 +517,7 @@ class TestVertexAIImagenImageGenerationConfig: """Test response transformation""" mock_response = MagicMock(spec=httpx.Response) mock_response.status_code = 200 - mock_response.json.return_value = { - "predictions": [{"bytesBase64Encoded": "base64_encoded_image_data"}] - } + mock_response.json.return_value = {"predictions": [{"bytesBase64Encoded": "base64_encoded_image_data"}]} mock_response.headers = {} from litellm.types.utils import ImageResponse @@ -539,9 +580,7 @@ class TestGetVertexAIImageGenerationConfig: config = get_vertex_ai_image_generation_config("gemini-3-pro-image-preview") assert isinstance(config, VertexAIGeminiImageGenerationConfig) - config = get_vertex_ai_image_generation_config( - "vertex_ai/gemini-2.5-flash-image" - ) + config = get_vertex_ai_image_generation_config("vertex_ai/gemini-2.5-flash-image") assert isinstance(config, VertexAIGeminiImageGenerationConfig) def test_get_imagen_model_config(self): @@ -572,12 +611,8 @@ class TestVertexAIImageGenerationIntegration: """Test that Gemini config can validate environment""" config = VertexAIGeminiImageGenerationConfig() with ( - patch.object( - config, "_resolve_vertex_project", return_value="test-project" - ), - patch.object( - config, "_resolve_vertex_location", return_value="us-central1" - ), + patch.object(config, "_resolve_vertex_project", return_value="test-project"), + patch.object(config, "_resolve_vertex_location", return_value="us-central1"), patch.object(config, "_ensure_access_token", return_value=("token", None)), ): headers = config.validate_environment( @@ -597,12 +632,8 @@ class TestVertexAIImageGenerationIntegration: """Test that Imagen config can validate environment""" config = VertexAIImagenImageGenerationConfig() with ( - patch.object( - config, "_resolve_vertex_project", return_value="test-project" - ), - patch.object( - config, "_resolve_vertex_location", return_value="us-central1" - ), + patch.object(config, "_resolve_vertex_project", return_value="test-project"), + patch.object(config, "_resolve_vertex_location", return_value="us-central1"), patch.object(config, "_ensure_access_token", return_value=("token", None)), ): headers = config.validate_environment( diff --git a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py index bebf856ee6e..b83d4742b64 100644 --- a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py +++ b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py @@ -18,10 +18,25 @@ from litellm.llms.vertex_ai.common_utils import ( pop_vertex_request_labels, set_schema_property_ordering, supports_response_json_schema, + validate_vertex_location, vertex_request_labels_from_litellm_params, ) +@pytest.mark.parametrize("location", ["us", "eu", "us-central1", "europe-west1", "global"]) +def test_validate_vertex_location_accepts_valid(location): + assert validate_vertex_location(location) == location + + +@pytest.mark.parametrize( + "location", + ["attacker.example/", "evil.com#", "us.attacker.example", "us/../..", "US", "us_central1", "-us", "", None], +) +def test_validate_vertex_location_rejects_invalid(location): + with pytest.raises(ValueError): + validate_vertex_location(location) + + @pytest.mark.asyncio async def test_get_vertex_project_id_from_url(): """Test _get_vertex_project_id_from_url with various URLs""" diff --git a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_psc_endpoint_support.py b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_psc_endpoint_support.py index 5a6bc871a03..499bbf6ccd4 100644 --- a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_psc_endpoint_support.py +++ b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_psc_endpoint_support.py @@ -152,9 +152,9 @@ class TestVertexAIPSCEndpointSupport: assert url == expected_url, f"Expected {expected_url}, but got {url}" def test_standard_proxy_with_googleapis(self): - """Test that standard proxies with googleapis.com in URL use simple format""" + """Test that standard proxies with a path in the URL use simple format""" vertex_base = VertexBase() - proxy_api_base = "https://my-proxy.googleapis.com" + proxy_api_base = "https://my-proxy.googleapis.com/vertex-proxy" endpoint_id = "gemini-pro" # Not numeric project_id = "test-project" location = "us-central1" diff --git a/tests/test_litellm/llms/vertex_ai/test_vertex_llm_base.py b/tests/test_litellm/llms/vertex_ai/test_vertex_llm_base.py index 2cf97081806..18fc239b7c6 100644 --- a/tests/test_litellm/llms/vertex_ai/test_vertex_llm_base.py +++ b/tests/test_litellm/llms/vertex_ai/test_vertex_llm_base.py @@ -15,6 +15,7 @@ sys.path.insert( import litellm from litellm.llms.vertex_ai.vertex_ai_aws_wif import VertexAIAwsWifAuth from litellm.llms.vertex_ai.vertex_llm_base import VertexBase +from litellm.types.llms.vertex_ai import VertexPartnerProvider def run_sync(coro): @@ -774,7 +775,7 @@ class TestVertexBase: "https://aiplatform.googleapis.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-pro:generateContent", "gemini-pro", "Bearer token123", - "https://custom-vertex-api.com:generateContent", + "https://custom-vertex-api.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-pro:generateContent", ), # Test case 4: No API base provided (should return original values) ( @@ -930,6 +931,173 @@ class TestVertexBase: result_url_no_streaming == expected_no_streaming_url ), f"Expected {expected_no_streaming_url}, got {result_url_no_streaming}" + def test_check_custom_proxy_vertex_bare_host_api_base_grafts_default_path(self): + vertex_base = VertexBase() + + result_auth_header, result_url = vertex_base._check_custom_proxy( + api_base="https://aiplatform.googleapis.com", + custom_llm_provider="vertex_ai", + gemini_api_key=None, + endpoint="embedContent", + stream=None, + auth_header="Bearer token123", + url="https://us-central1-aiplatform.googleapis.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-embedding-2:embedContent", + model="gemini-embedding-2", + ) + + assert result_auth_header == "Bearer token123" + assert ( + result_url + == "https://aiplatform.googleapis.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-embedding-2:embedContent" + ) + + def test_check_custom_proxy_vertex_bare_host_api_base_with_trailing_slash(self): + vertex_base = VertexBase() + + _, result_url = vertex_base._check_custom_proxy( + api_base="https://internal-gateway.example.com/", + custom_llm_provider="vertex_ai", + gemini_api_key=None, + endpoint="embedContent", + stream=None, + auth_header="Bearer token123", + url="https://us-central1-aiplatform.googleapis.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-embedding-2:embedContent", + model="gemini-embedding-2", + ) + + assert ( + result_url + == "https://internal-gateway.example.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-embedding-2:embedContent" + ) + + def test_check_custom_proxy_vertex_api_base_with_path_keeps_endpoint_append(self): + vertex_base = VertexBase() + gateway_api_base = "https://gateway.ai.cloudflare.com/v1/account-id/my-gateway/google-vertex-ai/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-embedding-2" + + _, result_url = vertex_base._check_custom_proxy( + api_base=gateway_api_base, + custom_llm_provider="vertex_ai", + gemini_api_key=None, + endpoint="embedContent", + stream=None, + auth_header="Bearer token123", + url="https://us-central1-aiplatform.googleapis.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-embedding-2:embedContent", + model="gemini-embedding-2", + ) + + assert result_url == f"{gateway_api_base}:embedContent" + + def test_check_custom_proxy_vertex_bare_host_streaming_keeps_single_alt_sse(self): + vertex_base = VertexBase() + + _, result_url = vertex_base._check_custom_proxy( + api_base="https://internal-gateway.example.com", + custom_llm_provider="vertex_ai", + gemini_api_key=None, + endpoint="streamGenerateContent", + stream=True, + auth_header="Bearer token123", + url="https://us-central1-aiplatform.googleapis.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-2.5-pro:streamGenerateContent?alt=sse", + model="gemini-2.5-pro", + ) + + assert ( + result_url + == "https://internal-gateway.example.com/v1/projects/test-project/locations/us-central1/publishers/google/models/gemini-2.5-pro:streamGenerateContent?alt=sse" + ) + + def test_check_custom_proxy_psc_endpoint_format_unaffected_by_bare_host(self): + vertex_base = VertexBase() + + _, result_url = vertex_base._check_custom_proxy( + api_base="https://10.96.32.8", + custom_llm_provider="vertex_ai", + gemini_api_key=None, + endpoint="predict", + stream=None, + auth_header="Bearer token123", + url="", + model="1234567890", + vertex_project="test-project", + vertex_location="us-central1", + vertex_api_version="v1", + use_psc_endpoint_format=True, + ) + + assert result_url == "https://10.96.32.8/v1/projects/test-project/locations/us-central1/endpoints/1234567890:predict" + + @pytest.mark.parametrize( + "custom_api_base, stream, expected_url", + [ + ( + "https://aiplatform-myendpoint.p.googleapis.com", + False, + "https://aiplatform-myendpoint.p.googleapis.com/v1/projects/test-project/locations/global/endpoints/openapi/chat/completions", + ), + ( + "https://aiplatform-myendpoint.p.googleapis.com", + True, + "https://aiplatform-myendpoint.p.googleapis.com/v1/projects/test-project/locations/global/endpoints/openapi/chat/completions", + ), + ( + "https://gateway.example.com/vertex-proxy", + False, + "https://gateway.example.com/vertex-proxy/v1/projects/test-project/locations/global/endpoints/openapi/chat/completions", + ), + ], + ids=["psc-host", "psc-host-streaming", "api-base-with-path"], + ) + def test_get_complete_vertex_url_openai_path_partner_custom_api_base( + self, custom_api_base, stream, expected_url + ): + vertex_base = VertexBase() + + result = vertex_base.get_complete_vertex_url( + custom_api_base=custom_api_base, + vertex_location="global", + vertex_project="test-project", + project_id="test-project", + partner=VertexPartnerProvider.llama, + stream=stream, + model="minimaxai/minimax-m2-maas", + ) + + assert result == expected_url + assert result.count("://") == 1 + + def test_get_complete_vertex_url_openai_path_partner_default_api_base(self): + vertex_base = VertexBase() + + result = vertex_base.get_complete_vertex_url( + custom_api_base=None, + vertex_location="us-central1", + vertex_project="test-project", + project_id="test-project", + partner=VertexPartnerProvider.llama, + stream=True, + model="meta/llama-3.1-405b-instruct-maas", + ) + + assert ( + result + == "https://us-central1-aiplatform.googleapis.com/v1/projects/test-project/locations/us-central1/endpoints/openapi/chat/completions" + ) + + def test_get_complete_vertex_url_rawpredict_partner_custom_api_base_keeps_endpoint_format(self): + vertex_base = VertexBase() + + result = vertex_base.get_complete_vertex_url( + custom_api_base="https://gateway.example.com/vertex-proxy", + vertex_location="us-central1", + vertex_project="test-project", + project_id="test-project", + partner=VertexPartnerProvider.mistralai, + stream=False, + model="mistral-large-2411", + ) + + assert result == "https://gateway.example.com/vertex-proxy:rawPredict" + @pytest.mark.parametrize( "api_base, custom_llm_provider, gemini_api_key, endpoint, stream, auth_header, url, model, expected_auth_header, expected_url", [ diff --git a/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_messages_config.py b/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_messages_config.py index bfd37f73b2d..ce770221ceb 100644 --- a/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_messages_config.py +++ b/tests/test_litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_messages_config.py @@ -509,3 +509,59 @@ def test_vertex_claude_completion_does_not_mutate_shared_extra_headers(): assert ( shared_extra_headers == {} ), "extra_headers must not be mutated by completion()" + + +@pytest.fixture +def local_model_cost_map(monkeypatch): + """Force the bundled backup cost map so capability flags match this branch.""" + import litellm + + original = litellm.model_cost + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + litellm.model_cost = litellm.get_model_cost_map(url="") + litellm.get_model_info.cache_clear() + try: + yield + finally: + litellm.model_cost = original + litellm.get_model_info.cache_clear() + + +def test_messages_thinking_shape_follows_exact_vertex_entry_flag(local_model_cost_map, monkeypatch): + """The Vertex messages config must probe capabilities under ``vertex_ai`` so an + operator setting ``supports_adaptive_thinking: false`` on the exact + ``vertex_ai/claude-opus-4-8`` entry beats the unmodified ``anthropic`` entry. + With the inherited ``"anthropic"`` provider default the flip was ignored and + the transform kept emitting ``thinking.type='adaptive'``.""" + import litellm + + config = VertexAIPartnerModelsAnthropicMessagesConfig() + + def transform(): + return config.transform_anthropic_messages_request( + model="claude-opus-4-8", + messages=[{"role": "user", "content": "Hello"}], + anthropic_messages_optional_request_params={ + "max_tokens": 4096, + "reasoning_effort": "medium", + }, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + result = transform() + assert result.get("thinking") == {"type": "adaptive"} + assert result.get("output_config") == {"effort": "medium"} + + monkeypatch.setitem( + litellm.model_cost["vertex_ai/claude-opus-4-8"], "supports_adaptive_thinking", False + ) + litellm.get_model_info.cache_clear() + assert litellm.model_cost["claude-opus-4-8"]["supports_adaptive_thinking"] is True + + flipped = transform() + thinking = flipped.get("thinking") + assert isinstance(thinking, dict) + assert thinking.get("type") == "enabled" + assert isinstance(thinking.get("budget_tokens"), int) + assert "output_config" not in flipped diff --git a/tests/test_litellm/ocr/test_ocr_azure_document_intelligence_api_base.py b/tests/test_litellm/ocr/test_ocr_azure_document_intelligence_api_base.py new file mode 100644 index 00000000000..0c8b1cc2836 --- /dev/null +++ b/tests/test_litellm/ocr/test_ocr_azure_document_intelligence_api_base.py @@ -0,0 +1,85 @@ +""" +Regression tests for Azure Document Intelligence api_base resolution in OCR. + +`azure_ai` exposes two OCR services on one provider; the `doc-intelligence` +sub-route must resolve to `AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT`, not to the +generic `AZURE_AI_API_BASE` fallback that `get_llm_provider` injects. These tests +pin that routing and guard the backwards-compatibility contract that an explicitly +supplied api_base is always honoured. +""" + +from litellm.llms.azure_ai.ocr.common_utils import ( + is_azure_document_intelligence_model, +) +from litellm.ocr.main import _prepare_ocr_request, _rust_bridge_api_base + +_DOC = {"type": "document_url", "document_url": "https://example.com/doc.pdf"} +_DOC_INTELLIGENCE_ENDPOINT = "https://di.cognitiveservices.azure.com" +_AZURE_AI_API_BASE = "https://generic-azure-ai.example.com" + + +class _FakeLogging: + def update_from_kwargs(self, **kwargs: object) -> None: + return None + + +def _resolve_secret(name: str) -> str | None: + return { + "AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT": _DOC_INTELLIGENCE_ENDPOINT, + "AZURE_AI_API_BASE": _AZURE_AI_API_BASE, + }.get(name) + + +def _prepare(model: str, api_base: str | None): + return _prepare_ocr_request( + model=model, + document=dict(_DOC), + api_key="test-key", + api_base=api_base, + timeout=None, + custom_llm_provider=None, + extra_headers=None, + kwargs={"litellm_logging_obj": _FakeLogging()}, + ) + + +class TestIsAzureDocumentIntelligenceModel: + def test_matches_doc_intelligence_route(self): + assert is_azure_document_intelligence_model("doc-intelligence/prebuilt-layout") + + def test_matches_documentintelligence_and_is_case_insensitive(self): + assert is_azure_document_intelligence_model("azure_ai/DocumentIntelligence/x") + + def test_does_not_match_mistral_route(self): + assert not is_azure_document_intelligence_model("mistral-document-ai-2505") + + +class TestDocIntelligenceApiBaseResolution: + def test_generic_azure_ai_base_does_not_hijack_doc_intelligence(self, monkeypatch): + """Without an explicit api_base, the AZURE_AI_API_BASE fallback must not + overwrite the endpoint, so it resolves to the Document Intelligence one.""" + monkeypatch.setenv("AZURE_AI_API_BASE", _AZURE_AI_API_BASE) + monkeypatch.delenv("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT", raising=False) + + prepared = _prepare("azure_ai/doc-intelligence/prebuilt-layout", None) + + assert prepared.api_base is None + assert _rust_bridge_api_base(prepared, _resolve_secret) == _DOC_INTELLIGENCE_ENDPOINT + + def test_explicit_api_base_is_honoured_for_doc_intelligence(self, monkeypatch): + """A caller-supplied api_base must always win, even for doc-intelligence.""" + monkeypatch.setenv("AZURE_AI_API_BASE", _AZURE_AI_API_BASE) + + custom = "https://my-di.cognitiveservices.azure.com" + prepared = _prepare("azure_ai/doc-intelligence/prebuilt-layout", custom) + + assert prepared.api_base == custom + assert _rust_bridge_api_base(prepared, _resolve_secret) == custom + + def test_generic_azure_ai_base_still_applies_to_mistral_ocr(self, monkeypatch): + """Non doc-intelligence azure_ai models keep using AZURE_AI_API_BASE.""" + monkeypatch.setenv("AZURE_AI_API_BASE", _AZURE_AI_API_BASE) + + prepared = _prepare("azure_ai/mistral-document-ai-2505", None) + + assert prepared.api_base == _AZURE_AI_API_BASE diff --git a/tests/test_litellm/passthrough/test_passthrough_main.py b/tests/test_litellm/passthrough/test_passthrough_main.py index dc2b7cc3682..0b5bfac87bb 100644 --- a/tests/test_litellm/passthrough/test_passthrough_main.py +++ b/tests/test_litellm/passthrough/test_passthrough_main.py @@ -387,8 +387,9 @@ async def test_pass_through_request_stream_param_no_override( # Create mocks for the async client mock_async_client = AsyncMock() - # Mock request to return the non-streaming response - mock_async_client.request.return_value = mock_response + # Mock build_request/send to return the non-streaming response + mock_async_client.build_request = Mock(return_value=Mock()) + mock_async_client.send.return_value = mock_response # Mock get_async_httpx_client to return our mock client mock_client_obj = Mock() @@ -420,20 +421,19 @@ async def test_pass_through_request_stream_param_no_override( stream=False, # Should be used since no stream in request body ) - # Verify that build_request was NOT called (no streaming path) - mock_async_client.build_request.assert_not_called() - - # Verify that send was NOT called (no streaming path) - mock_async_client.send.assert_not_called() - - # Verify that the non-streaming request method WAS called - mock_async_client.request.assert_called_once_with( - method="POST", - url=httpx.URL("https://api.anthropic.com/v1/messages"), + # Non-SSE requests are sent with stream semantics so large bodies can + # be relayed without buffering; the JSON response below is still + # buffered into a plain Response. + mock_async_client.request.assert_not_called() + mock_async_client.build_request.assert_called_once_with( + "POST", + httpx.URL("https://api.anthropic.com/v1/messages"), headers={"Authorization": "Bearer test-key"}, params={}, json=request_body, ) + mock_async_client.send.assert_called_once() + assert mock_async_client.send.call_args.kwargs.get("stream") is True # Verify response is a regular Response (not StreamingResponse) from fastapi.responses import Response, StreamingResponse @@ -726,3 +726,78 @@ async def test_allm_passthrough_route_429_streaming_raises(): assert exc_info.value.response.status_code == 429 assert len(chunks) == 0, "No chunks should be yielded before the 429 raises" + + +def test_llm_passthrough_route_propagates_allm_passthrough_route_to_logging_obj(): + """ + Regression guard for LIT-4192: `allm_passthrough_route` sets + `kwargs["allm_passthrough_route"] = True` on the async entrypoint, and the + inner `llm_passthrough_route` must let that flag flow through + `get_litellm_params(**kwargs)` and land in the logging object's + `litellm_params`. Without that, `_is_sync_litellm_request` misclassifies + the request as sync and fires duplicate success callbacks. + """ + import asyncio + + from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging + + client = HTTPHandler() + + mock_provider_config = MagicMock() + mock_provider_config.get_complete_url.return_value = ( + httpx.URL("https://bedrock-runtime.us-east-1.amazonaws.com/model/foo/converse"), + "https://bedrock-runtime.us-east-1.amazonaws.com", + ) + mock_provider_config.get_api_key.return_value = "fake-key" + mock_provider_config.validate_environment.return_value = {} + mock_provider_config.sign_request.return_value = ({}, None) + mock_provider_config.is_streaming_request.return_value = False + + captured_litellm_params: dict = {} + + def _capture_update_env(*args, **kwargs): + captured_litellm_params.clear() + captured_litellm_params.update(kwargs.get("litellm_params") or {}) + + mock_logging_obj = MagicMock() + mock_logging_obj.update_environment_variables.side_effect = _capture_update_env + + with ( + patch( + "litellm.utils.ProviderConfigManager.get_provider_passthrough_config", + return_value=mock_provider_config, + ), + patch( + "litellm.litellm_core_utils.get_llm_provider_logic.get_llm_provider", + return_value=( + "bedrock/foo", + "bedrock", + "fake-key", + "https://bedrock-runtime.us-east-1.amazonaws.com", + ), + ), + patch.object( + client.client, + "send", + return_value=MagicMock(status_code=200, json=lambda: {}), + ), + patch.object(client.client, "build_request"), + ): + result = llm_passthrough_route( + model="bedrock/foo", + endpoint="model/foo/converse", + method="POST", + custom_llm_provider="bedrock", + api_base="https://bedrock-runtime.us-east-1.amazonaws.com", + api_key="fake-key", + json={"messages": []}, + client=client, + litellm_logging_obj=mock_logging_obj, + allm_passthrough_route=True, + ) + + if asyncio.iscoroutine(result): + result.close() + + assert captured_litellm_params.get("allm_passthrough_route") is True + assert LitellmLogging._is_sync_litellm_request(captured_litellm_params) is False diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py index b3b0e8adcf6..c785ac577f7 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py @@ -1,14 +1,15 @@ +import contextlib import json import os import sys -from unittest.mock import AsyncMock, MagicMock, call as mock_call, patch +from datetime import datetime, timedelta, timezone +from unittest.mock import AsyncMock, MagicMock, patch import pytest +from fastapi import HTTPException from fastapi.testclient import TestClient -sys.path.insert( - 0, os.path.abspath("../../../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../../..")) # Adds the parent directory to the system path from starlette.datastructures import Headers @@ -16,6 +17,8 @@ from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( MCPRequestHandler, ) from litellm.proxy._types import ( + LiteLLM_ObjectPermissionTable, + ProxyException, SpecialHeaders, SpecialMCPServerNames, UserAPIKeyAuth, @@ -78,20 +81,14 @@ class TestMCPRequestHandler: ) # Mock the helper methods instead of database calls - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key_servers: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team_servers: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key_servers: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team_servers: # Set up return values mock_key_servers.return_value = key_servers mock_team_servers.return_value = team_servers # Call the method - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth=mock_user_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth=mock_user_auth) # Assert the result (order-independent comparison) assert sorted(result) == sorted(expected_result) @@ -148,20 +145,14 @@ class TestMCPRequestHandler: ) # Mock the helper functions - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key_servers: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team_servers: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key_servers: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team_servers: # Configure mocks to return the test data mock_key_servers.return_value = key_servers mock_team_servers.return_value = team_servers # Call the method - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth) # Assert the result (order-independent comparison) assert sorted(result) == sorted(expected_servers) @@ -186,9 +177,7 @@ class TestMCPRequestHandler: ): """The require_key_mcp_access_defined general setting flips an empty key from inheriting its team's MCP servers (default) to inheriting none.""" - auth = UserAPIKeyAuth( - api_key="test-key", user_id="test-user", team_id="test-team" - ) + auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user", team_id="test-team") with ( patch.object( MCPRequestHandler, @@ -272,23 +261,22 @@ class TestMCPRequestHandler: async def test_no_mcp_servers_sentinel_returns_empty(self, team_servers): """A key scoped to the no-mcp-servers sentinel resolves to zero servers, overriding team inheritance and never leaking the sentinel marker.""" - user_api_key_auth = UserAPIKeyAuth( - api_key="test-key", user_id="test-user", team_id="test-team" - ) + user_api_key_auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user", team_id="test-team") key_object_permission = MagicMock() - key_object_permission.mcp_servers = [ - SpecialMCPServerNames.no_mcp_servers.value - ] + key_object_permission.mcp_servers = [SpecialMCPServerNames.no_mcp_servers.value] - with patch.object( - MCPRequestHandler, - "_get_key_object_permission", - return_value=key_object_permission, - ), patch.object( - MCPRequestHandler, - "_get_allowed_mcp_servers_for_team", - new_callable=AsyncMock, - return_value=team_servers, + with ( + patch.object( + MCPRequestHandler, + "_get_key_object_permission", + return_value=key_object_permission, + ), + patch.object( + MCPRequestHandler, + "_get_allowed_mcp_servers_for_team", + new_callable=AsyncMock, + return_value=team_servers, + ), ): result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth) @@ -309,9 +297,7 @@ class TestMCPRequestHandler: "_get_key_object_permission", return_value=key_object_permission, ): - result = await MCPRequestHandler._get_allowed_mcp_servers_for_key( - user_api_key_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth) assert result == [SpecialMCPServerNames.no_mcp_servers.value] @@ -320,9 +306,7 @@ class TestMCPRequestHandler: # Test case: None values in database mock_prisma_client = MagicMock() - mock_prisma_client.db.litellm_objectpermissiontable.find_unique.return_value = ( - None - ) + mock_prisma_client.db.litellm_objectpermissiontable.find_unique.return_value = None mock_prisma_client.db.litellm_teamtable.find_unique.return_value = None user_api_key_auth = UserAPIKeyAuth( @@ -337,9 +321,7 @@ class TestMCPRequestHandler: assert result == [] # Test case: Exception handling - mock_prisma_client.db.litellm_objectpermissiontable.find_unique.side_effect = ( - Exception("DB Error") - ) + mock_prisma_client.db.litellm_objectpermissiontable.find_unique.side_effect = Exception("DB Error") with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client): result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth) @@ -384,15 +366,9 @@ class TestMCPRequestHandler: access_group_ids=["grp-mcp"], ) with ( - patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key, - patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team, - patch.object( - MCPRequestHandler, "_get_key_access_group_mcp_server_extras" - ) as mock_grants, + patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key, + patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team, + patch.object(MCPRequestHandler, "_get_key_access_group_mcp_server_extras") as mock_grants, ): mock_key.return_value = key_servers mock_team.return_value = team_servers @@ -414,13 +390,9 @@ class TestMCPRequestHandler: "litellm.proxy.auth.auth_checks._get_mcp_server_ids_from_access_groups", new=AsyncMock(return_value=[]), ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - auth - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(auth) assert result == [] # expand_permission_list must not be reached when there are no raw ids. mock_mgr.expand_permission_list.assert_not_called() @@ -433,14 +405,10 @@ class TestMCPRequestHandler: "litellm.proxy.auth.auth_checks._get_mcp_server_ids_from_access_groups", new=AsyncMock(return_value=["alias-a", "srv-b"]), ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.expand_permission_list.return_value = ["srv-a", "srv-b"] - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - auth - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(auth) assert sorted(result) == ["srv-a", "srv-b"] mock_mgr.expand_permission_list.assert_called_once_with(["alias-a", "srv-b"]) @@ -451,9 +419,7 @@ class TestMCPRequestHandler: "litellm.proxy.auth.auth_checks._get_mcp_server_ids_from_access_groups", new=AsyncMock(side_effect=Exception("db down")), ): - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - auth - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(auth) assert result == [] @pytest.mark.parametrize( @@ -743,9 +709,7 @@ class TestMCPRequestHandler: # Verify MCP servers mcp_servers_header = extracted_headers.get(SpecialHeaders.mcp_servers.value) mcp_servers = None - if ( - mcp_servers_header is not None - ): # Changed from 'if mcp_servers_header:' to handle empty strings + if mcp_servers_header is not None: # Changed from 'if mcp_servers_header:' to handle empty strings try: # First try to parse as JSON array for backward compatibility try: @@ -754,16 +718,12 @@ class TestMCPRequestHandler: mcp_servers = None except (json.JSONDecodeError, TypeError, ValueError): # If JSON parsing fails, treat as comma-separated list - mcp_servers = [ - s.strip() for s in mcp_servers_header.split(",") if s.strip() - ] + mcp_servers = [s.strip() for s in mcp_servers_header.split(",") if s.strip()] except Exception: mcp_servers = None # If we got an empty string or parsing resulted in no servers, return empty list - if mcp_servers_header == "" or ( - mcp_servers is not None and len(mcp_servers) == 0 - ): + if mcp_servers_header == "" or (mcp_servers is not None and len(mcp_servers) == 0): mcp_servers = [] assert mcp_servers == expected_result["mcp_servers"] @@ -795,6 +755,40 @@ class TestMCPRequestHandler: # For these tests, mcp_server_auth_headers should be empty assert mcp_server_auth_headers == {} + def test_duplicate_authorization_header_is_rejected(self): + """A request carrying more than one Authorization header is malformed for bearer auth and, + for the client-forwarded token modes, would make which upstream token is forwarded ambiguous. + The ingress header converter must reject it with a 400 rather than silently keeping one.""" + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/tp_server", + "headers": [ + (b"authorization", b"Bearer upstream-token-a"), + (b"authorization", b"Bearer upstream-token-b"), + (b"content-type", b"application/json"), + ], + } + with pytest.raises(HTTPException) as exc_info: + MCPRequestHandler._safe_get_headers_from_scope(scope) + assert exc_info.value.status_code == 400 + assert "Authorization" in str(exc_info.value.detail) + + def test_single_authorization_header_is_forwarded_verbatim(self): + """The rejection must not disturb the normal single-Authorization case: the value passes + through unchanged (guards against the duplicate check over-matching).""" + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/tp_server", + "headers": [ + (b"authorization", b"Bearer upstream-token"), + (b"content-type", b"application/json"), + ], + } + headers = MCPRequestHandler._safe_get_headers_from_scope(scope) + assert headers.get("authorization") == "Bearer upstream-token" + @pytest.mark.asyncio class TestMCPOAuth2AuthFlow: @@ -833,9 +827,7 @@ class TestMCPOAuth2AuthFlow: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", new_callable=AsyncMock, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = oauth2_server ( @@ -851,10 +843,7 @@ class TestMCPOAuth2AuthFlow: # The upstream token is never validated as a LiteLLM key ... mock_auth.assert_not_called() # ... and is preserved for upstream forwarding. - assert ( - oauth2_headers.get("Authorization") - == "Bearer atlassian-oauth2-access-token-xyz" - ) + assert oauth2_headers.get("Authorization") == "Bearer atlassian-oauth2-access-token-xyz" async def test_explicit_litellm_key_with_oauth2_authorization(self): """ @@ -893,9 +882,7 @@ class TestMCPOAuth2AuthFlow: assert call_args.kwargs["api_key"] == "sk-litellm-valid-key" # OAuth2 headers should still contain the Authorization token - assert ( - oauth2_headers.get("Authorization") == "Bearer atlassian-oauth2-token" - ) + assert oauth2_headers.get("Authorization") == "Bearer atlassian-oauth2-token" async def test_litellm_key_in_authorization_backward_compat(self): """ @@ -997,9 +984,7 @@ class TestMCPOAuth2AuthFlow: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_proxy_exception, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = oauth2_server with pytest.raises(ProxyException) as exc_info: @@ -1068,9 +1053,7 @@ class TestMCPPublicRouteGuard: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): # Explicit unresolvable target — proves auth still fails even # when the registry has no info to fall back to. @@ -1101,9 +1084,7 @@ class TestMCPPublicRouteGuard: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: @@ -1157,9 +1138,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, patch( "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp._is_mcp_passthrough_cold_start" ) as mock_cold_start, @@ -1199,9 +1178,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1229,9 +1206,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1263,18 +1238,14 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.IPAddressUtils.get_mcp_client_ip", return_value="203.0.113.10", ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) assert exc_info.value.status_code == 401 - mock_mgr.get_mcp_server_by_name.assert_any_call( - "passthrough_server", client_ip="203.0.113.10" - ) + mock_mgr.get_mcp_server_by_name.assert_any_call("passthrough_server", client_ip="203.0.113.10") async def test_cold_start_propagates_non_401_http_error(self): from fastapi import HTTPException @@ -1294,9 +1265,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_forbidden, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1329,9 +1298,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_server_error, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1357,9 +1324,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1367,9 +1332,7 @@ class TestMCPPassthroughColdStartAdmission: auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) - mock_mgr.get_mcp_server_by_name.assert_any_call( - "passthrough_server", client_ip="" - ) + mock_mgr.get_mcp_server_by_name.assert_any_call("passthrough_server", client_ip="") async def test_cold_start_allows_proxy_exception_401_for_path_target(self): from litellm.proxy._types import ProxyException @@ -1394,9 +1357,7 @@ class TestMCPPassthroughColdStartAdmission: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = ( TestMCPPassthroughColdStartAdmission._make_passthrough_server() @@ -1404,9 +1365,7 @@ class TestMCPPassthroughColdStartAdmission: auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) - mock_mgr.get_mcp_server_by_name.assert_any_call( - "passthrough_server", client_ip="" - ) + mock_mgr.get_mcp_server_by_name.assert_any_call("passthrough_server", client_ip="") @pytest.mark.asyncio @@ -1450,12 +1409,10 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPOAuth2FallbackTargetGating._make_server(MCPAuth.api_key) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPOAuth2FallbackTargetGating._make_server( + MCPAuth.api_key ) with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) @@ -1483,9 +1440,7 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: @@ -1523,12 +1478,10 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPOAuth2FallbackTargetGating._make_server(MCPAuth.oauth2) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPOAuth2FallbackTargetGating._make_server( + MCPAuth.oauth2 ) with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) @@ -1562,15 +1515,11 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPOAuth2FallbackTargetGating._make_server( - auth_type=MCPAuth.none, - is_oauth_passthrough=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPOAuth2FallbackTargetGating._make_server( + auth_type=MCPAuth.none, + is_oauth_passthrough=True, ) auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) @@ -1598,9 +1547,7 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.IPAddressUtils.get_mcp_client_ip", return_value="203.0.113.10", ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: @@ -1612,9 +1559,7 @@ class TestMCPOAuth2FallbackTargetGating: # resolve to ``None`` (hidden by client IP) so neither bypass # opens. Use ``assert_any_call`` to assert the IP-scoped lookup # happened without locking the count. - mock_mgr.get_mcp_server_by_name.assert_any_call( - "hidden_oauth2_server", client_ip="203.0.113.10" - ) + mock_mgr.get_mcp_server_by_name.assert_any_call("hidden_oauth2_server", client_ip="203.0.113.10") async def test_fallback_blocked_when_any_target_in_header_is_not_oauth2(self): """ @@ -1649,9 +1594,7 @@ class TestMCPOAuth2FallbackTargetGating: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.side_effect = mock_lookup with pytest.raises(HTTPException) as exc_info: @@ -1732,17 +1675,10 @@ class TestMCPDelegateAuthToUpstream: delegate_auth_to_upstream=True, available_on_public_internet=True, ) - assert ( - manager._build_mcp_server_table(delegated).delegate_auth_to_upstream is True - ) + assert manager._build_mcp_server_table(delegated).delegate_auth_to_upstream is True - not_delegated = delegated.model_copy( - update={"delegate_auth_to_upstream": False} - ) - assert ( - manager._build_mcp_server_table(not_delegated).delegate_auth_to_upstream - is False - ) + not_delegated = delegated.model_copy(update={"delegate_auth_to_upstream": False}) + assert manager._build_mcp_server_table(not_delegated).delegate_auth_to_upstream is False def test_build_mcp_server_table_preserves_oauth_passthrough(self): """Registry → API list rows must expose oauth_passthrough for the UI. @@ -1773,9 +1709,7 @@ class TestMCPDelegateAuthToUpstream: assert row.delegate_auth_to_upstream is False not_passthrough = passthrough.model_copy(update={"oauth_passthrough": False}) - assert ( - manager._build_mcp_server_table(not_passthrough).oauth_passthrough is False - ) + assert manager._build_mcp_server_table(not_passthrough).oauth_passthrough is False async def test_delegate_skips_litellm_auth_with_no_authorization(self): """ @@ -1796,15 +1730,11 @@ class TestMCPDelegateAuthToUpstream: patch( "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, ) auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) @@ -1834,15 +1764,11 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", new_callable=AsyncMock, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, ) ( auth_result, @@ -1880,15 +1806,11 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=False, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=False, ) with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) @@ -1919,15 +1841,11 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.api_key, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.api_key, + delegate_auth_to_upstream=True, ) with pytest.raises(HTTPException) as exc_info: await MCPRequestHandler.process_mcp_request(scope) @@ -1971,9 +1889,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.side_effect = mock_lookup with pytest.raises(HTTPException) as exc_info: @@ -2003,9 +1919,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_user_api_key_auth_fails, ), - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = None with pytest.raises(HTTPException) as exc_info: @@ -2034,15 +1948,11 @@ class TestMCPDelegateAuthToUpstream: new_callable=AsyncMock, return_value=UserAPIKeyAuth(user_id="real-user"), ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, ) auth_result, *_rest = await MCPRequestHandler.process_mcp_request(scope) assert isinstance(auth_result, UserAPIKeyAuth) @@ -2074,15 +1984,11 @@ class TestMCPDelegateAuthToUpstream: new_callable=AsyncMock, return_value=UserAPIKeyAuth(user_id="real-user"), ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): - mock_mgr.get_mcp_server_by_name.return_value = ( - TestMCPDelegateAuthToUpstream._make_server( - auth_type=MCPAuth.oauth2, - delegate_auth_to_upstream=True, - ) + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, ) ( auth_result, @@ -2135,9 +2041,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = m2m_server # No delegate bypass → normal auth is attempted → 401 raised @@ -2146,6 +2050,100 @@ class TestMCPDelegateAuthToUpstream: assert exc_info.value.status_code == 401 mock_auth.assert_called_once() + async def test_delegate_ignored_for_unstamped_m2m_shaped_server(self): + """ + oauth2 + delegate + oauth2_flow=None but the M2M credential shape + (client_id/secret + token_url, no authorization_url) → bypass must NOT + fire. A legacy row that was never stamped still resolves to + client_credentials by shape, and reading the bare column here would + reopen the anonymous bypass to a server that runs upstream as LiteLLM's + service account. Fails closed like the client_credentials case above. + """ + from fastapi import HTTPException + + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/legacy_m2m_server", + "headers": [], + } + + legacy_m2m_server = MCPServer( + server_id="legacy-m2m-id", + name="legacy_m2m_server", + transport="http", + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, + oauth2_flow=None, + client_id="cid", + client_secret="csecret", + token_url="https://idp.example.com/token", + ) + assert legacy_m2m_server.has_client_credentials is False + + async def mock_auth_raises(*_args, **_kwargs): + raise HTTPException(status_code=401, detail="No key provided") + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + side_effect=mock_auth_raises, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + ): + mock_mgr.get_mcp_server_by_name.return_value = legacy_m2m_server + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + assert exc_info.value.status_code == 401 + mock_auth.assert_called_once() + + async def test_delegate_bypass_for_pure_pkce_server(self): + """ + oauth2 + delegate + oauth2_flow=None and NO stored client credentials + (pure PKCE, the common delegate case) → bypass must still fire. The + shape resolves to a non-M2M flow, so the security gate leaves it alone; + the fail-closed rule targets the M2M shape specifically, not every + unstamped row. + """ + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/pkce_server", + "headers": [], + } + + pkce_server = MCPServer( + server_id="pkce-server-id", + name="pkce_server", + transport="http", + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, + oauth2_flow=None, + ) + + async def mock_auth_raises(*_args, **_kwargs): + from fastapi import HTTPException + + raise HTTPException(status_code=401, detail="No key provided") + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + side_effect=mock_auth_raises, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + ): + mock_mgr.get_mcp_server_by_name.return_value = pkce_server + auth, *_rest = await MCPRequestHandler.process_mcp_request(scope) + mock_auth.assert_not_called() + assert auth.api_key is None + async def test_delegate_bypass_for_internal_server(self): """ Delegate + oauth2 interactive servers bypass LiteLLM auth even when @@ -2180,9 +2178,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.return_value = internal_server auth, *_rest = await MCPRequestHandler.process_mcp_request(scope) @@ -2234,6 +2230,56 @@ class TestMCPDelegateAuthToUpstream: assert "pkce-server" in result assert "m2m-server" not in result + async def test_get_allowed_servers_excludes_unstamped_m2m_shape_delegate(self): + """ + The anonymous allow-list must also exclude an M2M-shape delegate server whose + oauth2_flow was never stamped (null column, verbatim-read as non-M2M). Reading + the bare has_client_credentials here would surface it to anonymous callers; the + resolved-flow check fails closed on the shape, matching the auth gate. + """ + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + MCPServerManager, + ) + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + manager = MCPServerManager() + pkce_server = MCPServer( + server_id="pkce-server", + name="pkce_server", + transport="http", + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, + available_on_public_internet=True, + ) + unstamped_m2m = MCPServer( + server_id="unstamped-m2m", + name="unstamped_m2m", + transport="http", + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, + oauth2_flow=None, + client_id="cid", + client_secret="csecret", + token_url="https://idp.example.com/token", + ) + assert unstamped_m2m.has_client_credentials is False + manager.registry = { + pkce_server.server_id: pkce_server, + unstamped_m2m.server_id: unstamped_m2m, + } + + with patch.object( + MCPRequestHandler, + "get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=[], + ): + result = await manager.get_allowed_mcp_servers(None) + + assert "pkce-server" in result + assert "unstamped-m2m" not in result + async def test_get_allowed_servers_includes_internal_delegate(self): """ Internal-only (available_on_public_internet=False) delegate servers @@ -2278,6 +2324,104 @@ class TestMCPDelegateAuthToUpstream: assert "public-server" in result assert "internal-server" in result + async def test_true_passthrough_skips_litellm_auth_anonymously(self): + """auth_type=true_passthrough performs no admission auth: the caller's Authorization is an + upstream token forwarded unchanged and user_api_key_auth is never called.""" + from litellm.types.mcp import MCPAuth + + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/true_passthrough_server", + "headers": [(b"authorization", b"Bearer upstream-token")], + } + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + ): + mock_mgr.get_mcp_server_by_name.return_value = TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.true_passthrough, + ) + ( + auth_result, + _, + _, + _, + oauth2_headers, + _, + ) = await MCPRequestHandler.process_mcp_request(scope) + assert isinstance(auth_result, UserAPIKeyAuth) + assert auth_result.api_key is None + assert oauth2_headers.get("Authorization") == "Bearer upstream-token" + mock_auth.assert_not_called() + + async def test_true_passthrough_mixed_targets_fail_closed(self): + """One true_passthrough target mixed with a non-passthrough target must NOT skip admission.""" + from fastapi import HTTPException + + from litellm.types.mcp import MCPAuth + + scope = { + "type": "http", + "method": "POST", + "path": "/mcp", + "headers": [(b"x-mcp-servers", b"tp_server,plain_server")], + } + + async def mock_user_api_key_auth_fails(api_key, request): + raise HTTPException(status_code=401, detail="Invalid API key") + + def mock_lookup(name, client_ip=None): + if name == "tp_server": + return TestMCPDelegateAuthToUpstream._make_server( + auth_type=MCPAuth.true_passthrough, + ) + return TestMCPDelegateAuthToUpstream._make_server(auth_type=MCPAuth.api_key) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + side_effect=mock_user_api_key_auth_fails, + ), + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + ): + mock_mgr.get_mcp_server_by_name.side_effect = mock_lookup + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + assert exc_info.value.status_code == 401 + + async def test_get_allowed_servers_includes_true_passthrough(self): + """Anonymous callers can reach true_passthrough servers; admission is delegated upstream.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + MCPServerManager, + ) + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + manager = MCPServerManager() + tp_server = MCPServer( + server_id="tp-server", + name="tp_server", + transport="http", + auth_type=MCPAuth.true_passthrough, + available_on_public_internet=True, + ) + manager.registry = {tp_server.server_id: tp_server} + + with patch.object( + MCPRequestHandler, + "get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=[], + ): + result = await manager.get_allowed_mcp_servers(None) + + assert "tp-server" in result + def test_extract_target_server_names_matches_routing_parser(self): """ Regression: _extract_target_server_names_from_path must match the @@ -2316,13 +2460,12 @@ class TestMCPDelegateAuthToUpstream: ("/", []), ] for path_input, expected in cases: - assert ( - MCPRequestHandler._extract_target_server_names_from_path(path_input) - == expected - ), f"path={path_input!r} → expected {expected!r}" - assert ( - _get_mcp_servers_in_path(path_input) or [] - ) == expected, f"path={path_input!r} → routing expected {expected!r}" + assert MCPRequestHandler._extract_target_server_names_from_path(path_input) == expected, ( + f"path={path_input!r} → expected {expected!r}" + ) + assert (_get_mcp_servers_in_path(path_input) or []) == expected, ( + f"path={path_input!r} → routing expected {expected!r}" + ) async def test_delegate_does_not_bypass_on_extra_path_segment(self): """ @@ -2366,9 +2509,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.side_effect = lookup_by_name with pytest.raises(HTTPException) as exc_info: @@ -2431,9 +2572,7 @@ class TestMCPDelegateAuthToUpstream: "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", side_effect=mock_auth_raises, ) as mock_auth, - patch( - "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager" - ) as mock_mgr, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, ): mock_mgr.get_mcp_server_by_name.side_effect = lookup_by_name # Bypass MUST NOT fire — path-derived target is the non-delegate @@ -2453,15 +2592,12 @@ class TestMCPDelegateAuthToUpstream: empty-list case, which fails closed). """ # Path matches /mcp/... — header is ignored. - assert MCPRequestHandler._resolve_target_server_names( - path="/mcp/foo", mcp_servers_header=["evil"] - ) == ["foo"] - assert MCPRequestHandler._resolve_target_server_names( - path="/mcp/foo,bar", mcp_servers_header=["evil"] - ) == ["foo", "bar"] - assert MCPRequestHandler._resolve_target_server_names( - path="/foo/mcp", mcp_servers_header=["evil"] - ) == ["foo"] + assert MCPRequestHandler._resolve_target_server_names(path="/mcp/foo", mcp_servers_header=["evil"]) == ["foo"] + assert MCPRequestHandler._resolve_target_server_names(path="/mcp/foo,bar", mcp_servers_header=["evil"]) == [ + "foo", + "bar", + ] + assert MCPRequestHandler._resolve_target_server_names(path="/foo/mcp", mcp_servers_header=["evil"]) == ["foo"] # Path does not match — header is trusted. assert MCPRequestHandler._resolve_target_server_names( path="/.well-known/oauth-authorization-server", @@ -2505,16 +2641,12 @@ class TestMCPCustomHeaderName: (None, "", "x-mcp-auth"), ], ) - def test_get_mcp_client_side_auth_header_name( - self, env_var, general_setting, expected_header_name - ): + def test_get_mcp_client_side_auth_header_name(self, env_var, general_setting, expected_header_name): """Test that custom header name configuration works correctly""" # Mock the secret manager and general settings with patch("litellm.secret_managers.main.get_secret_str") as mock_get_secret: - with patch( - "litellm.proxy.proxy_server.general_settings" - ) as mock_general_settings: + with patch("litellm.proxy.proxy_server.general_settings") as mock_general_settings: # Configure mocks mock_get_secret.return_value = env_var mock_general_settings.get.return_value = general_setting @@ -2537,9 +2669,7 @@ class TestMCPCustomHeaderName: if env_var is None: # When env var is None, general settings should be checked (twice if not None) expected_general_calls = 2 if general_setting is not None else 1 - assert ( - mock_general_settings.get.call_count == expected_general_calls - ) + assert mock_general_settings.get.call_count == expected_general_calls for call in mock_general_settings.get.call_args_list: assert call.args == ("mcp_client_side_auth_header_name",) else: @@ -2580,9 +2710,7 @@ class TestMCPCustomHeaderName: ), ], ) - def test_get_mcp_auth_header_from_headers_with_custom_name( - self, custom_header_name, headers, expected_auth_header - ): + def test_get_mcp_auth_header_from_headers_with_custom_name(self, custom_header_name, headers, expected_auth_header): """Test that MCP auth header extraction uses custom header name""" # Mock the header name method @@ -2601,9 +2729,7 @@ class TestMCPCustomHeaderName: extracted_headers = MCPRequestHandler._safe_get_headers_from_scope(scope) # Call the method - result = MCPRequestHandler._get_mcp_auth_header_from_headers( - extracted_headers - ) + result = MCPRequestHandler._get_mcp_auth_header_from_headers(extracted_headers) # Assert the result assert result == expected_auth_header @@ -2670,9 +2796,7 @@ class TestMCPCustomHeaderName: from starlette.datastructures import Headers # Test case 1: No server-specific headers - headers = Headers( - {"x-litellm-api-key": "test-key", "content-type": "application/json"} - ) + headers = Headers({"x-litellm-api-key": "test-key", "content-type": "application/json"}) result = MCPRequestHandler._get_mcp_server_auth_headers_from_headers(headers) assert result == {} @@ -2756,17 +2880,13 @@ class TestMCPCustomHeaderName: assert result == {"github_mcp": {"Authorization": "Bearer github-mcp-token"}} # Test case 8: Edge case - empty header value - headers = Headers( - {"x-litellm-api-key": "test-key", "x-mcp-github-authorization": ""} - ) + headers = Headers({"x-litellm-api-key": "test-key", "x-mcp-github-authorization": ""}) result = MCPRequestHandler._get_mcp_server_auth_headers_from_headers(headers) assert result == {"github": {"Authorization": ""}} # Test case 9: Edge case - very long header value long_token = "Bearer " + "x" * 1000 - headers = Headers( - {"x-litellm-api-key": "test-key", "x-mcp-github-authorization": long_token} - ) + headers = Headers({"x-litellm-api-key": "test-key", "x-mcp-github-authorization": long_token}) result = MCPRequestHandler._get_mcp_server_auth_headers_from_headers(headers) assert result == {"github": {"Authorization": long_token}} @@ -2832,9 +2952,7 @@ class TestMCPAccessGroupsE2E: # Assert the results assert auth_result.api_key == "test-api-key" assert mcp_auth_header is None - assert ( - mcp_servers is None - ) # x-mcp-access-groups is not parsed as mcp_servers + assert mcp_servers is None # x-mcp-access-groups is not parsed as mcp_servers assert mcp_server_auth_headers == {} # Verify the mock was called @@ -2949,9 +3067,7 @@ def test_mcp_path_based_server_segregation(monkeypatch): # Use TestClient to make a request to /mcp/zapier,group1/tools client = TestClient(app) - response = client.get( - "/mcp/zapier,group1/tools", headers={"x-litellm-api-key": "test"} - ) + response = client.get("/mcp/zapier,group1/tools", headers={"x-litellm-api-key": "test"}) assert response.status_code == 200 assert response.json() == {"status": "ok"} @@ -3029,15 +3145,11 @@ async def test_get_team_object_permission_with_already_loaded_permission(): mock_prisma, ): with patch("litellm.proxy.auth.auth_checks.get_team_object") as mock_get_team: - with patch( - "litellm.proxy.auth.auth_checks.get_object_permission" - ) as mock_get_perm: + with patch("litellm.proxy.auth.auth_checks.get_object_permission") as mock_get_perm: mock_get_team.return_value = mock_team_obj # Call the method - result = await MCPRequestHandler._get_team_object_permission( - mock_user_auth - ) + result = await MCPRequestHandler._get_team_object_permission(mock_user_auth) # Assert we got the object permission assert result == mock_object_permission @@ -3105,6 +3217,102 @@ async def test_get_team_object_permission_with_core_auth_auto_loading(): mock_get_team.assert_called_once() +@pytest.mark.asyncio +async def test_get_team_object_permission_ui_session_team_skips_db_lookup(): + """ + UI session tokens carry the virtual team_id "litellm-dashboard" (UI_TEAM_ID), + which is never persisted. The lookup must short-circuit to None without + calling get_team_object; otherwise every MCP tools listing from the + dashboard logs a "Team doesn't exist in db" warning per server. + """ + from litellm.proxy._types import UI_TEAM_ID + + mock_user_auth = UserAPIKeyAuth( + api_key="test-key", + user_id="test-user", + team_id=UI_TEAM_ID, + ) + + mock_prisma = MagicMock() + with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma): + with patch("litellm.proxy.auth.auth_checks.get_team_object") as mock_get_team: + result = await MCPRequestHandler._get_team_object_permission(mock_user_auth) + + assert result is None + mock_get_team.assert_not_called() + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "helper_name,expected", + [ + ("_get_allowed_mcp_servers_for_team", []), + ("_get_mcp_access_groups_for_team", []), + ], +) +async def test_team_mcp_helpers_ui_session_team_skip_db_lookup(helper_name, expected): + """ + The server-permission and access-group helpers hit get_team_object with the + session's team_id too; for the virtual UI team each used to 404 into its + own swallowed warning per MCP listing. They must short-circuit without a + DB lookup. + """ + from litellm.proxy._types import UI_TEAM_ID + + mock_user_auth = UserAPIKeyAuth( + api_key="test-key", + user_id="test-user", + team_id=UI_TEAM_ID, + ) + + mock_prisma = MagicMock() + with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma): + with patch("litellm.proxy.auth.auth_checks.get_team_object") as mock_get_team: + helper = getattr(MCPRequestHandler, helper_name) + result = await helper(mock_user_auth) + + assert result == expected + mock_get_team.assert_not_called() + + +@pytest.mark.asyncio +async def test_get_allowed_tools_for_server_ui_session_team_keeps_key_restrictions(): + """ + Regression: the 404 raised by get_team_object for the virtual UI team used + to escape into get_allowed_tools_for_server's blanket except, dropping + key-level tool restrictions (fail-open) and logging a warning. With the + short-circuit, key restrictions still apply for UI sessions. + """ + from fastapi import HTTPException + + from litellm.proxy._types import UI_TEAM_ID + + user_api_key_auth = UserAPIKeyAuth( + api_key="test-key", + user_id="test-user", + team_id=UI_TEAM_ID, + ) + key_perm = MagicMock() + key_perm.mcp_tool_permissions = {"server_1": ["tool_a"]} + + mock_prisma = MagicMock() + with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma): + with patch( + "litellm.proxy.auth.auth_checks.get_team_object", + side_effect=HTTPException( + status_code=404, + detail={"error": "Team doesn't exist in db. Team=litellm-dashboard."}, + ), + ): + with patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm): + result = await MCPRequestHandler.get_allowed_tools_for_server( + server_id="server_1", + user_api_key_auth=user_api_key_auth, + ) + + assert result == ["tool_a"] + + @pytest.mark.asyncio async def test_get_allowed_mcp_servers_for_team_uses_helper(): """ @@ -3162,9 +3370,7 @@ async def test_get_allowed_mcp_servers_for_team_uses_helper(): return_value=["group-server1", "group-server2"], ) as mock_get_access_group_servers, ): - result = await MCPRequestHandler._get_allowed_mcp_servers_for_team( - mock_user_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_team(mock_user_auth) assert set(result) == { "direct-server1", @@ -3207,9 +3413,7 @@ async def test_get_allowed_mcp_servers_for_team_with_no_object_permission(): return_value=mock_team, ), ): - result = await MCPRequestHandler._get_allowed_mcp_servers_for_team( - mock_user_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_team(mock_user_auth) assert result == [] @@ -3259,9 +3463,7 @@ async def test_get_allowed_mcp_servers_for_team_without_team_id_returns_empty(): ), ], ) -async def test_get_allowed_mcp_servers_for_key_guard_conditions( - user_api_key_auth, prisma_client_value, scenario -): +async def test_get_allowed_mcp_servers_for_key_guard_conditions(user_api_key_auth, prisma_client_value, scenario): """Ensure guard clauses return [] before hitting get_object_permission.""" with patch( @@ -3269,9 +3471,7 @@ async def test_get_allowed_mcp_servers_for_key_guard_conditions( new_callable=AsyncMock, ) as mock_get_perm: with patch("litellm.proxy.proxy_server.prisma_client", prisma_client_value): - result = await MCPRequestHandler._get_allowed_mcp_servers_for_key( - user_api_key_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth) assert result == [] mock_get_perm.assert_not_called() @@ -3298,9 +3498,7 @@ async def test_get_allowed_mcp_servers_for_key_returns_empty_when_db_returns_non ): mock_get_perm.return_value = None - result = await MCPRequestHandler._get_allowed_mcp_servers_for_key( - user_api_key_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth) assert result == [] mock_get_perm.assert_awaited_once() @@ -3343,14 +3541,10 @@ async def test_get_allowed_mcp_servers_for_key_prefers_in_memory_permission(): "litellm.proxy.auth.auth_checks.get_object_permission", new_callable=AsyncMock, ) as mock_get_perm: - with patch.object( - MCPRequestHandler, "_get_mcp_servers_from_access_groups" - ) as mock_access_groups: + with patch.object(MCPRequestHandler, "_get_mcp_servers_from_access_groups") as mock_access_groups: mock_access_groups.return_value = ["group-server"] - result = await MCPRequestHandler._get_allowed_mcp_servers_for_key( - user_api_key_auth - ) + result = await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth) assert set(result) == {"direct-server", "group-server"} mock_get_perm.assert_not_called() @@ -3371,21 +3565,13 @@ class TestAgentMCPPermissions: team_id="test-team", agent_id="agent-123", ) - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_agent" - ) as mock_agent: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_agent") as mock_agent: mock_key.return_value = ["server_1", "server_2"] mock_team.return_value = [] mock_agent.return_value = ["server_1"] - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth=user_api_key_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth=user_api_key_auth) assert sorted(result) == ["server_1"] mock_agent.assert_called_once_with(user_api_key_auth) @@ -3396,21 +3582,13 @@ class TestAgentMCPPermissions: user_id="test-user", agent_id="agent-456", ) - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_agent" - ) as mock_agent: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_agent") as mock_agent: mock_key.return_value = ["server_1", "server_2"] mock_team.return_value = [] mock_agent.return_value = [] # no agent-level restriction - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth=user_api_key_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth=user_api_key_auth) assert sorted(result) == ["server_1", "server_2"] mock_agent.assert_called_once_with(user_api_key_auth) @@ -3421,21 +3599,13 @@ class TestAgentMCPPermissions: user_id="test-user", agent_id="agent-789", ) - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_key" - ) as mock_key: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_team" - ) as mock_team: - with patch.object( - MCPRequestHandler, "_get_allowed_mcp_servers_for_agent" - ) as mock_agent: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_key") as mock_key: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_team") as mock_team: + with patch.object(MCPRequestHandler, "_get_allowed_mcp_servers_for_agent") as mock_agent: mock_key.return_value = ["server_1", "server_2"] mock_team.return_value = [] mock_agent.return_value = ["server_2", "server_3"] - result = await MCPRequestHandler.get_allowed_mcp_servers( - user_api_key_auth=user_api_key_auth - ) + result = await MCPRequestHandler.get_allowed_mcp_servers(user_api_key_auth=user_api_key_auth) assert sorted(result) == ["server_2"] async def test_get_allowed_tools_for_server_agent_intersection(self): @@ -3448,9 +3618,7 @@ class TestAgentMCPPermissions: key_perm = MagicMock() key_perm.mcp_tool_permissions = {"server_1": ["tool_a", "tool_b"]} team_perm = None - with patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ): + with patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm): with patch.object( MCPRequestHandler, "_get_team_object_permission", @@ -3482,9 +3650,7 @@ class TestAgentMCPPermissions: ) key_perm = MagicMock() key_perm.mcp_tool_permissions = {"server_1": ["tool_a", "tool_b"]} - with patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ): + with patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm): with patch.object( MCPRequestHandler, "_get_team_object_permission", @@ -3514,9 +3680,7 @@ class TestAgentMCPPermissions: agent_row = MagicMock() agent_row.object_permission_id = "perm-xyz" prisma_client = MagicMock() - prisma_client.db.litellm_agentstable.find_unique = AsyncMock( - return_value=agent_row - ) + prisma_client.db.litellm_agentstable.find_unique = AsyncMock(return_value=agent_row) user_api_key_auth = UserAPIKeyAuth( api_key="test-key", user_id="test-user", @@ -3534,9 +3698,7 @@ class TestAgentMCPPermissions: return_value=expected_perm, ) as mock_get_perm, ): - result = await MCPRequestHandler._get_agent_object_permission( - user_api_key_auth - ) + result = await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) assert result is expected_perm mock_get_perm.assert_awaited_once() assert mock_get_perm.await_args.kwargs["object_permission_id"] == "perm-xyz" @@ -3556,9 +3718,7 @@ class TestAgentMCPPermissions: agent_row = MagicMock() agent_row.object_permission_id = None prisma_client = MagicMock() - prisma_client.db.litellm_agentstable.find_unique = AsyncMock( - return_value=agent_row - ) + prisma_client.db.litellm_agentstable.find_unique = AsyncMock(return_value=agent_row) user_api_key_auth = UserAPIKeyAuth( api_key="test-key", user_id="test-user", @@ -3574,14 +3734,8 @@ class TestAgentMCPPermissions: new_callable=AsyncMock, ) as mock_get_perm, ): - assert ( - await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) - is None - ) - assert ( - await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) - is None - ) + assert await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) is None + assert await MCPRequestHandler._get_agent_object_permission(user_api_key_auth) is None mock_get_perm.assert_not_awaited() prisma_client.db.litellm_agentstable.find_unique.assert_awaited_once() @@ -3622,9 +3776,7 @@ async def test_tool_permission_servers_included_in_allowed_servers(): ) with ( - patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=perm - ), + patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=perm), patch.object( MCPRequestHandler, "_get_mcp_servers_from_access_groups", @@ -3857,9 +4009,7 @@ class TestOrgMCPPermissions: org_perm.mcp_tool_permissions = {"server_1": ["tool_a", "tool_b"]} with ( - patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ), + patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm), patch.object( MCPRequestHandler, "_get_team_object_permission", @@ -3889,9 +4039,7 @@ class TestOrgMCPPermissions: org_perm.mcp_tool_permissions = {} with ( - patch.object( - MCPRequestHandler, "_get_key_object_permission", return_value=key_perm - ), + patch.object(MCPRequestHandler, "_get_key_object_permission", return_value=key_perm), patch.object( MCPRequestHandler, "_get_team_object_permission", @@ -3983,9 +4131,7 @@ async def test_mcp_key_access_group_extras_when_team_authorized(): ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == ["srv-stripe"] finally: _stop_patches(patches) @@ -4022,9 +4168,7 @@ async def test_mcp_key_access_group_extras_when_key_directly_authorized(): ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == ["srv-stripe"] finally: _stop_patches(patches) @@ -4038,9 +4182,7 @@ async def test_mcp_key_access_group_extras_when_key_has_no_groups(): access_group_ids=[], team_id="team-a", ) - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == [] @@ -4067,9 +4209,7 @@ async def test_mcp_key_access_group_extras_when_group_has_no_servers(): ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == [] finally: _stop_patches(patches) @@ -4103,9 +4243,7 @@ async def test_mcp_key_access_group_extras_granted_even_when_group_authorizes_ne ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == ["srv-finance-only"] finally: _stop_patches(patches) @@ -4128,9 +4266,7 @@ async def test_mcp_key_access_group_extras_when_get_access_object_raises(): ] _start_patches(patches) try: - result = await MCPRequestHandler._get_key_access_group_mcp_server_extras( - valid_token - ) + result = await MCPRequestHandler._get_key_access_group_mcp_server_extras(valid_token) assert result == [] finally: _stop_patches(patches) @@ -4495,3 +4631,1134 @@ async def test_get_allowed_mcp_servers_surfaces_ungated_key_access_group_grant_e assert result == ["srv-deepwiki"] finally: _stop_patches(patches) + + +def test_expand_permission_list_does_not_honor_all_proxy_sentinel(): + """The all-proxy sentinel is a team-only grant. The shared expand_permission_list + also feeds the key/org/end_user/agent resolvers, so it must NOT expand the + sentinel to the full registry; it passes through as an inert literal (denied + downstream). Concrete ids still resolve normally. If the sentinel were expanded + here, any stored key/org/end_user permission holding it would silently gain every + server.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import SpecialMCPServerName + from litellm.types.mcp import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + sentinel = SpecialMCPServerName.all_proxy_servers.value + for sid in ("srv-x", "srv-y"): + global_mcp_server_manager.registry[sid] = MCPServer( + server_id=sid, + name=sid, + server_name=sid, + url=f"https://{sid}.example.com", + transport=MCPTransport.http, + ) + try: + result = global_mcp_server_manager.expand_permission_list([sentinel]) + assert set(result).isdisjoint({"srv-x", "srv-y"}) + assert result == [sentinel] + assert global_mcp_server_manager.expand_permission_list(["srv-x"]) == ["srv-x"] + finally: + for sid in ("srv-x", "srv-y"): + global_mcp_server_manager.registry.pop(sid, None) + + +@pytest.mark.asyncio +async def test_get_allowed_mcp_servers_for_team_expands_all_proxy_sentinel_dynamically(): + """The TEAM resolver expands the all-proxy sentinel to every registered server and + picks up a server registered later, so a team scoped to all-proxy tracks the live + registry without any change to its stored permission. Reverting the team-side + expansion collapses this to the inert literal and the result no longer contains the + real servers.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import ( + LiteLLM_ObjectPermissionTable, + LiteLLM_TeamTable, + SpecialMCPServerName, + ) + from litellm.types.mcp import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + for sid in ("srv-x", "srv-y"): + global_mcp_server_manager.registry[sid] = MCPServer( + server_id=sid, + name=sid, + server_name=sid, + url=f"https://{sid}.example.com", + transport=MCPTransport.http, + ) + try: + team_perm = LiteLLM_ObjectPermissionTable( + object_permission_id="team-perm", + mcp_servers=[SpecialMCPServerName.all_proxy_servers.value], + mcp_access_groups=[], + vector_stores=[], + ) + team_obj = LiteLLM_TeamTable( + team_id="team-1", + access_group_ids=[], + object_permission_id="team-perm", + ) + team_obj.object_permission = team_perm + auth = UserAPIKeyAuth(token="test-token", api_key="sk-test", team_id="team-1") + + patches = _patch_proxy_server_globals_for_mcp() + [ + patch( + "litellm.proxy.auth.auth_checks.get_team_object", + new_callable=AsyncMock, + return_value=team_obj, + ), + patch( + "litellm.proxy.auth.auth_checks._get_mcp_server_ids_from_access_groups", + new_callable=AsyncMock, + return_value=[], + ), + ] + _start_patches(patches) + try: + result = await MCPRequestHandler._get_allowed_mcp_servers_for_team(auth) + assert set(result) == {"srv-x", "srv-y"} + + global_mcp_server_manager.registry["srv-z"] = MCPServer( + server_id="srv-z", + name="srv-z", + server_name="srv-z", + url="https://srv-z.example.com", + transport=MCPTransport.http, + ) + result_after = await MCPRequestHandler._get_allowed_mcp_servers_for_team(auth) + assert "srv-z" in result_after + finally: + _stop_patches(patches) + finally: + for sid in ("srv-x", "srv-y", "srv-z"): + global_mcp_server_manager.registry.pop(sid, None) + + +@pytest.mark.asyncio +async def test_key_with_all_proxy_sentinel_does_not_grant_all_servers(): + """Security regression: the all-proxy sentinel is a team-only grant. A KEY whose + stored object_permission holds the sentinel (via a stale write, a configured + default, or a bug) must NOT be silently widened to every server at runtime. A + teamless key with the sentinel resolves to no real server — never srv-secret or the + full registry. On the pre-hardening code the key path expanded the sentinel and + this key would reach srv-secret.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import ( + LiteLLM_ObjectPermissionTable, + SpecialMCPServerName, + ) + from litellm.types.mcp import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + for sid in ("srv-x", "srv-y", "srv-secret"): + global_mcp_server_manager.registry[sid] = MCPServer( + server_id=sid, + name=sid, + server_name=sid, + url=f"https://{sid}.example.com", + transport=MCPTransport.http, + ) + try: + key_perm = LiteLLM_ObjectPermissionTable( + object_permission_id="key-perm", + mcp_servers=[SpecialMCPServerName.all_proxy_servers.value], + mcp_access_groups=[], + vector_stores=[], + ) + auth = UserAPIKeyAuth(token="test-token", api_key="sk-test", object_permission=key_perm) + + patches = _patch_proxy_server_globals_for_mcp() + _start_patches(patches) + try: + result = await MCPRequestHandler.get_allowed_mcp_servers(auth) + finally: + _stop_patches(patches) + + assert "srv-secret" not in result + assert set(result).isdisjoint(global_mcp_server_manager.get_registry().keys()) + finally: + for sid in ("srv-x", "srv-y", "srv-secret"): + global_mcp_server_manager.registry.pop(sid, None) + + +@pytest.mark.asyncio +async def test_get_allowed_mcp_servers_team_all_proxy_key_scoped_to_one_end_to_end(): + """End-to-end: a team scoped to the all-proxy sentinel is a ceiling of every + registered server, so a key scoped to a single server (srv-x) resolves to + exactly that server (key ∩ all-servers == key). If the sentinel branch is + reverted the team ceiling collapses to the literal marker, the intersection + empties, and the result is [] instead of ["srv-x"].""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import ( + LiteLLM_ObjectPermissionTable, + LiteLLM_TeamTable, + SpecialMCPServerName, + ) + from litellm.types.mcp import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + for sid in ("srv-x", "srv-y"): + global_mcp_server_manager.registry[sid] = MCPServer( + server_id=sid, + name=sid, + server_name=sid, + url=f"https://{sid}.example.com", + transport=MCPTransport.http, + ) + try: + key_perm = LiteLLM_ObjectPermissionTable( + object_permission_id="key-perm", + mcp_servers=["srv-x"], + mcp_access_groups=[], + vector_stores=[], + ) + team_perm = LiteLLM_ObjectPermissionTable( + object_permission_id="team-perm", + mcp_servers=[SpecialMCPServerName.all_proxy_servers.value], + mcp_access_groups=[], + vector_stores=[], + ) + team_obj = LiteLLM_TeamTable( + team_id="team-1", + access_group_ids=[], + object_permission_id="team-perm", + ) + team_obj.object_permission = team_perm + + auth = UserAPIKeyAuth( + token="test-token", + api_key="sk-test", + team_id="team-1", + object_permission=key_perm, + ) + + patches = _patch_proxy_server_globals_for_mcp() + [ + patch( + "litellm.proxy.auth.auth_checks.get_team_object", + new_callable=AsyncMock, + return_value=team_obj, + ), + patch( + "litellm.proxy.auth.auth_checks._get_mcp_server_ids_from_access_groups", + new_callable=AsyncMock, + return_value=[], + ), + patch.object( + MCPRequestHandler, + "_get_mcp_servers_from_access_groups", + new_callable=AsyncMock, + return_value=[], + ), + ] + _start_patches(patches) + try: + result = await MCPRequestHandler.get_allowed_mcp_servers(auth) + finally: + _stop_patches(patches) + + assert result == ["srv-x"] + finally: + for sid in ("srv-x", "srv-y"): + global_mcp_server_manager.registry.pop(sid, None) + + +@pytest.mark.asyncio +class TestMCPDcrBridgeDelegateAdmission: + """Admission-side arm for a DCR-bridge ``oauth_delegate`` client that authenticates with + a single envelope bearer (LIT-4338). + + The arm fires only for a single ``is_dcr_bridge`` ``is_oauth_delegate`` target carrying an + envelope-shaped Authorization. It opens the litellm-signed envelope, reloads the live key + record the sealed ``key_hash`` references so the caller is admitted under the key's current + authorization context (team/org/object-permission) and revocation state, and injects the inner + upstream token under the server's per-server auth-header key so egress forwards it. A key that + is missing, blocked, or expired fails closed with a 401. Everything else must stay on its + existing admission path. + """ + + _MASTER_KEY = "sk-bridge-master-key-for-envelope-derivation" + + @staticmethod + def _bridge_delegate_server(server_name="bridge_delegate_server", dcr_bridge=True, alias=None): + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + return MCPServer( + server_id="bridge-server-id", + name=server_name or "bridge-fallback-name", + server_name=server_name, + alias=alias, + transport="http", + auth_type=MCPAuth.oauth_delegate, + dcr_bridge=dcr_bridge, + ) + + _KEY_HASH = "hashed-litellm-key-abc123" + + @classmethod + def _mint_bridge_envelope( + cls, + *, + key_hash=None, + server_id="bridge-server-id", + access_token="inner-upstream-access-token", + token_type="Bearer", + expires_in=1800, + minted_at=None, + master_key=None, + ): + from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( + envelope_keys_from_master_key, + ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + EnvelopeIdentity, + SealedEnvelope, + UpstreamTokenGrant, + mint_envelope, + ) + from pydantic import SecretStr + + keys = envelope_keys_from_master_key(master_key or cls._MASTER_KEY) + now = minted_at or datetime.now(timezone.utc) + sealed = mint_envelope( + identity=EnvelopeIdentity(server_id=server_id, key_hash=key_hash or cls._KEY_HASH), + grant=UpstreamTokenGrant( + access_token=SecretStr(access_token), + token_type=token_type, + expires_in=expires_in, + ), + keys=keys, + now=now, + ) + assert isinstance(sealed, SealedEnvelope), sealed + return sealed.token.get_secret_value() + + @staticmethod + def _reloaded_key(**overrides): + """A live key record as ``get_key_object`` would return it: carries real authorization + context (key identity, team, org, and an object-permission restricting MCP servers) so a + test can prove admission admits under THAT context rather than a blank identity.""" + defaults = dict( + user_id="envelope-user-42", + api_key=TestMCPDcrBridgeDelegateAdmission._KEY_HASH, + team_id="team-restricted", + org_id="org-restricted", + object_permission=LiteLLM_ObjectPermissionTable( + object_permission_id="op-1", mcp_servers=["only-this-server"] + ), + ) + defaults.update(overrides) + return UserAPIKeyAuth(**defaults) + + @staticmethod + @contextlib.contextmanager + def _patch_key_reload(*, return_value=None, side_effect=None, team_blocked=False, owner=None, project_object=None): + """Patch the live-policy dependencies of the admission arm: the ``get_key_object`` reload, + the ``prisma_client`` / ``user_api_key_cache`` globals, and optionally the live objects the + policy gates re-check. ``team_blocked=True`` patches the centralized gate's + ``get_team_object`` at the ``user_api_key_auth`` namespace it actually calls; ``owner`` + patches the SCIM gate's ``get_user_object`` (``auth_checks`` namespace); ``project_object`` + patches the centralized gate's ``get_project_object``. Unpatched lookups hit the MagicMock + prisma and are swallowed (``_safe_fetch`` / the SCIM gate's fail-open), so their checks + skip. Yields the ``get_key_object`` mock so callers can assert the sealed ``key_hash`` was + the reload key.""" + get_key_object = AsyncMock(return_value=return_value, side_effect=side_effect) + patchers = [ + patch("litellm.proxy.auth.auth_checks.get_key_object", get_key_object), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + patch("litellm.proxy.proxy_server.user_api_key_cache", MagicMock()), + ] + if team_blocked: + patchers.append( + patch( + "litellm.proxy.auth.user_api_key_auth.get_team_object", + AsyncMock(return_value=MagicMock(blocked=True)), + ) + ) + if owner is not None: + patchers.append(patch("litellm.proxy.auth.auth_checks.get_user_object", AsyncMock(return_value=owner))) + if project_object is not None: + patchers.append( + patch( + "litellm.proxy.auth.user_api_key_auth.get_project_object", + AsyncMock(return_value=project_object), + ) + ) + with contextlib.ExitStack() as stack: + for patcher in patchers: + stack.enter_context(patcher) + yield get_key_object + + @staticmethod + def _mcp_request(path="/mcp/bridge_delegate_server"): + """A minimal ``Request`` for direct ``_admit_dcr_bridge_delegate`` calls, mirroring how + ``process_mcp_request`` builds one from the ASGI scope with a stubbed empty JSON body.""" + from starlette.requests import Request + + request = Request(scope={"type": "http", "method": "POST", "path": path, "headers": [], "query_string": b""}) + + async def mock_body(): + return b"{}" + + request.body = mock_body + return request + + async def test_valid_envelope_reloads_live_key_and_admits_its_authorization_context(self): + """A valid envelope admits under the LIVE key record the sealed key_hash references, not a + blank identity: the reload is keyed by that exact hash, and the admitted auth carries the + key's current team/org/object-permission (the MCP tool/server restrictions the finding was + about). The heavyweight ``user_api_key_auth`` pipeline is still never invoked. The inner + upstream token is injected under the per-server key for egress.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key()) as get_key_object, + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + ( + auth_result, + _mcp_auth_header, + _mcp_servers, + mcp_server_auth_headers, + _oauth2_headers, + _raw_headers, + ) = await MCPRequestHandler.process_mcp_request(scope) + + # The live key was reloaded by the exact hash the envelope sealed. + assert get_key_object.await_args.kwargs["hashed_token"] == self._KEY_HASH + # Admission carries the reloaded key's authorization context, not a blank UserAPIKeyAuth. + assert auth_result.user_id == "envelope-user-42" + assert auth_result.team_id == "team-restricted" + assert auth_result.org_id == "org-restricted" + assert auth_result.object_permission is not None + assert auth_result.object_permission.mcp_servers == ["only-this-server"] + # The full raw-key auth pipeline is still bypassed for the envelope arm. + mock_auth.assert_not_called() + # Inner upstream token injected under the per-server key so egress forwards it. + assert mcp_server_auth_headers == { + "bridge_delegate_server": {"Authorization": "Bearer inner-upstream-access-token"} + } + + async def test_revoked_key_envelope_fails_closed_401(self): + """An envelope whose key has since been deleted must fail closed: ``get_key_object`` raises + for the missing row, so admission 401s instead of admitting the caller as an unrestricted + identity. This is the core regression for the dropped-authorization-context finding.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + revoked = self._patch_key_reload( + side_effect=ProxyException( + message="Authentication Error, Invalid proxy server token passed.", + type="token_not_found_in_db", + param="key", + code=401, + ) + ) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + revoked, + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + mock_auth.assert_not_called() + + async def test_blocked_key_envelope_fails_closed_401(self): + """A reloaded key that is blocked must fail closed with a 401, so revoking a key by blocking + it takes effect immediately for any envelope still holding its hash.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key(blocked=True)), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + + async def test_expired_key_record_fails_closed_401(self): + """A reloaded key past its expiry must fail closed with a 401, distinct from an expired + envelope: even a still-valid envelope cannot outlive the key it was minted under.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + expired_at = datetime.now(timezone.utc) - timedelta(hours=1) + + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key(expires=expired_at)), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + + async def test_blocked_team_envelope_fails_closed_401(self): + """Blocking the key's TEAM must revoke its envelopes immediately: the reloaded key is active + but its team is blocked, so admission 401s. Without the live team re-check, a caller could + keep executing tools after an admin blocked the team, until the envelope expired.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key(), team_blocked=True), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + + async def test_scim_deactivated_owner_envelope_fails_closed_401(self): + """SCIM-deactivating the key's OWNER must revoke the user's envelopes immediately: the + standard pipeline rejects every key of a deactivated user inline in the builder, so the + admission arm mirrors that gate. Without it, IdP offboarding would leave the offboarded + user's already-minted envelopes executing tools until they expired.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload( + return_value=self._reloaded_key(), + owner=MagicMock(metadata={"scim_active": False}), + ), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + + async def test_scim_active_owner_envelope_admits(self): + """A SCIM-ACTIVE owner must still be admitted: the gate rejects only an explicit + ``scim_active: False``, so SCIM-managed users whose accounts are in good standing keep + working (and non-SCIM deployments, which never set the flag, are untouched).""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload( + return_value=self._reloaded_key(), + owner=MagicMock(metadata={"scim_active": True}), + ), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + (auth_result, _, _, _, _, _) = await MCPRequestHandler.process_mcp_request(scope) + + assert auth_result.user_id == "envelope-user-42" + + async def test_blocked_project_envelope_fails_closed_401(self): + """Blocking the key's PROJECT must revoke its envelopes immediately: the admitted identity + runs through the standard pipeline's centralized policy gate, which rejects a blocked + project exactly as it would for the same key presented directly. This is the regression for + the project half of the revocation finding; the gate also covers future policy dimensions + without the admission arm mirroring them one by one.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload( + return_value=self._reloaded_key(project_id="project-restricted"), + project_object=MagicMock(blocked=True), + ), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + + _POLICY_GATE = "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp._run_centralized_common_checks" + + async def _enforce_with_gate_error(self, error): + """Drive _enforce_admitted_live_policy with the centralized gate raising ``error`` and return + the HTTPException the arm maps it to.""" + with patch(self._POLICY_GATE, new=AsyncMock(side_effect=error)): + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler._enforce_admitted_live_policy( + admitted=UserAPIKeyAuth(user_id="envelope-user-42"), + request=self._mcp_request(), + route="/mcp/bridge_delegate_server", + ) + return exc_info.value + + async def test_over_budget_admission_surfaces_429_not_401(self): + """A validly-authenticated but over-budget identity surfaces the standard pipeline's 429, not + a misleading 401. Flattening budget to 401 told the caller their credential was invalid, which + on a DCR client reads as broken auth and triggers a re-authorize that cannot fix a budget + problem. Regression for the status-flattening finding on the live-policy gate.""" + import litellm + + mapped = await self._enforce_with_gate_error(litellm.BudgetExceededError(current_cost=10.0, max_budget=1.0)) + assert mapped.status_code == 429 + + async def test_db_outage_during_policy_surfaces_503_not_401(self): + """A transient database outage during the live-policy gate surfaces a retryable 503, not a 401 + that masks the outage as an auth failure and tells a valid caller to re-authenticate.""" + mapped = await self._enforce_with_gate_error(ConnectionError("could not reach database server")) + assert mapped.status_code == 503 + + async def test_db_outage_during_key_reload_surfaces_503_not_500(self): + """A DB outage while reloading the admitted key surfaces a retryable 503, not the opaque 500 a + raw get_key_object transport error would otherwise propagate as, and not a 401 that masks the + outage as an auth failure. Regression for the reload-path exception gap.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(side_effect=ConnectionError("could not reach database server")), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 503 + + async def test_envelope_for_key_barred_from_mcp_routes_is_rejected_403(self): + """A key whose allowed_routes exclude MCP must not reach tools via an envelope: the arm runs + RouteChecks.should_call_route before admitting, exactly as the standard pipeline does between + the builder and common_checks. A route-restricted key can mint an envelope at the token + endpoint (not itself an MCP route) and would otherwise replay it against MCP, because the + centralized checks treat MCP as an inference route and never re-check allowed_routes; the + route gate rejects it with its own 403.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key(allowed_routes=["/chat/completions"])), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 403 + + async def test_envelope_rejected_by_proxy_wide_pre_db_gates_403(self): + """The envelope arm runs the same proxy-wide pre-DB gates user_api_key_auth applies before any + key lookup (request size, body safety, IP allowlist, general_settings route allowlist). Here + the proxy route allowlist forbids MCP, so the envelope is turned away with a 403 before the + identity is even reloaded, closing the gap where an envelope bypassed the IP/route allowlists + the normal MCP admission path enforces.""" + envelope = self._mint_bridge_envelope(key_hash=self._KEY_HASH) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + patch("litellm.proxy.proxy_server.general_settings", {"allowed_routes": ["/chat/completions"]}), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 403 + + async def test_blocked_state_bare_exception_stays_401(self): + """A blocked team/project raises a bare Exception (no status) in common_checks, which the + standard pipeline renders as 401; the arm keeps failing those closed as 401, never a 500.""" + mapped = await self._enforce_with_gate_error(Exception("Team=team-x is blocked.")) + assert mapped.status_code == 401 + + async def test_subcheck_httpexception_status_preserved(self): + """A sub-check that raises its own HTTPException (e.g. a 403 model-access denial) keeps that + status through the arm rather than being flattened to 401.""" + mapped = await self._enforce_with_gate_error(HTTPException(status_code=403, detail="model not allowed")) + assert mapped.status_code == 403 + + async def test_alias_only_server_injects_under_alias_egress_can_resolve(self): + """When server_name is None, the inner token must be keyed under the alias (which egress + resolves), never under server.name (which egress never looks up), so the forwarded token is + not silently dropped.""" + envelope = self._mint_bridge_envelope() + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ), + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key()), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server( + server_name=None, alias="bridge_alias" + ) + (_auth, _h, _s, mcp_server_auth_headers, _o, _r) = await MCPRequestHandler.process_mcp_request(scope) + + assert mcp_server_auth_headers == {"bridge_alias": {"Authorization": "Bearer inner-upstream-access-token"}} + + async def test_sealed_token_wins_over_caller_forwarded_alias_header(self): + """When a bridge server has both a server_name and a distinct alias, the sealed inner token + must occupy the alias slot, the identifier egress resolves first. Otherwise a caller who + forwards x-mcp-{alias}-authorization keeps that entry at the higher-priority slot and pairs + the admitted identity with an attacker-chosen upstream credential.""" + envelope = self._mint_bridge_envelope() + attacker_forwarded = {"bridge_alias": {"Authorization": "Bearer ATTACKER-UPSTREAM-TOKEN"}} + + with ( + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key()), + ): + _auth, new_headers = await MCPRequestHandler._admit_dcr_bridge_delegate( + server=self._bridge_delegate_server(server_name="bridge_name", alias="bridge_alias"), + authorization_value=f"Bearer {envelope}", + mcp_server_auth_headers=attacker_forwarded, + request=self._mcp_request(), + route="/mcp/bridge_name", + ) + + # The sealed token owns the alias slot, overwriting the caller's value; the attacker token + # survives nowhere egress would resolve. + assert new_headers == {"bridge_alias": {"Authorization": "Bearer inner-upstream-access-token"}} + + @pytest.mark.parametrize( + "server_name,alias", + [ + (None, "bridge_alias"), + ("bridge_delegate_server", None), + ("bridge_name", "bridge_alias"), + ], + ids=["alias_only", "server_name_only", "both"], + ) + async def test_injection_key_agrees_with_egress_lookup(self, server_name, alias): + """Round-trip the injected headers through the REAL egress resolver for every admissible + server shape: whatever identifier the admission arm keys the sealed token under, + ``lookup_mcp_server_auth_in_headers`` called the way egress calls it (alias first, then + server_name) must recover exactly that token. This pins the agreement between the two key + hierarchies so neither side can drift and silently drop the forwarded token.""" + from litellm.proxy._experimental.mcp_server.utils import lookup_mcp_server_auth_in_headers + + envelope = self._mint_bridge_envelope() + server = self._bridge_delegate_server(server_name=server_name, alias=alias) + + with ( + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key()), + ): + _auth, new_headers = await MCPRequestHandler._admit_dcr_bridge_delegate( + server=server, + authorization_value=f"Bearer {envelope}", + mcp_server_auth_headers=None, + request=self._mcp_request(), + route="/mcp/bridge_delegate_server", + ) + + resolved = lookup_mcp_server_auth_in_headers( + new_headers, + alias=server.alias, + server_name=server.server_name, + ) + assert resolved == {"Authorization": "Bearer inner-upstream-access-token"} + + async def test_server_with_no_alias_or_server_name_is_not_admitted_via_bridge_arm(self): + """A bridge server egress cannot route to (no alias and no server_name) must not take the + envelope arm; it fails closed to normal oauth2 admission rather than admitting and dropping + the inner token under an unresolvable key.""" + envelope = self._mint_bridge_envelope() + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + side_effect=HTTPException(status_code=401, detail="Invalid key"), + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server(server_name=None, alias=None) + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + mock_auth.assert_called_once() + + async def test_expired_envelope_fails_closed_401(self): + """An envelope whose exp is in the past must fail closed with a 401, never fall through to + anonymous admission.""" + expired = self._mint_bridge_envelope( + expires_in=60, + minted_at=datetime.now(timezone.utc) - timedelta(hours=2), + ) + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {expired}".encode("latin-1"))], + } + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + mock_auth.assert_not_called() + + async def test_envelope_minted_for_a_different_server_fails_closed_401(self): + """An envelope sealed for another server_id must be rejected when presented to this server, + so a captured or misrouted envelope cannot forward one server's upstream credential to + another. The signature verifies, but the server binding does not.""" + wrong_server = self._mint_bridge_envelope(server_id="some-other-server-id") + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {wrong_server}".encode("latin-1"))], + } + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + mock_auth.assert_not_called() + + async def test_envelope_under_wrong_master_key_fails_closed_401(self): + """An envelope-shaped bearer whose signature does not verify under the proxy's derived keys + (e.g. minted against a different master_key, or tampered) must fail closed with a 401.""" + foreign = self._mint_bridge_envelope(master_key="a-different-master-key-entirely") + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", f"Bearer {foreign}".encode("latin-1"))], + } + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + new_callable=AsyncMock, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + mock_auth.assert_not_called() + + async def test_non_envelope_bearer_on_bridge_server_falls_through_to_oauth2_arm(self): + """A plain (non-envelope) bearer on the same bridge server must NOT be admitted by the + envelope arm: it falls through to the oauth2 arm, which validates it as a LiteLLM key and + 401s here. Proves the arm is gated on envelope shape, not merely on the target being a + bridge server.""" + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [(b"authorization", b"Bearer plain-upstream-bearer-not-an-envelope")], + } + + async def mock_user_api_key_auth_fails(api_key, request): + raise HTTPException(status_code=401, detail="Invalid API key") + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + side_effect=mock_user_api_key_auth_fails, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + # The envelope arm was skipped, so the oauth2 arm ran and validated the bearer. + mock_auth.assert_called_once() + + async def test_explicit_litellm_key_wins_over_envelope_arm(self): + """An explicit x-litellm-api-key is always a LiteLLM credential and its arm precedes the + envelope arm: user_api_key_auth validates the key and NO inner token is injected, even + though the Authorization header carries a valid envelope.""" + envelope = self._mint_bridge_envelope() + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/bridge_delegate_server", + "headers": [ + (b"x-litellm-api-key", b"sk-explicit-litellm-key"), + (b"authorization", f"Bearer {envelope}".encode("latin-1")), + ], + } + + async def mock_user_api_key_auth(api_key, request): + return UserAPIKeyAuth(api_key=api_key, user_id="litellm-key-user") + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + side_effect=mock_user_api_key_auth, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server() + ( + auth_result, + _mcp_auth_header, + _mcp_servers, + mcp_server_auth_headers, + _oauth2_headers, + _raw_headers, + ) = await MCPRequestHandler.process_mcp_request(scope) + + mock_auth.assert_called_once() + assert mock_auth.call_args.kwargs["api_key"] == "sk-explicit-litellm-key" + # The explicit-key arm admitted; the envelope arm never ran, so no inner token is injected. + assert auth_result.user_id == "litellm-key-user" + assert mcp_server_auth_headers == {} + + async def test_non_bridge_oauth_delegate_server_does_not_take_envelope_arm(self): + """An oauth_delegate server that is NOT a DCR bridge (``dcr_bridge`` unset) must not take the + envelope arm even for an envelope-shaped bearer: is_dcr_bridge is False, so the gate returns + None and admission falls through to the oauth2 arm (which 401s here).""" + envelope = self._mint_bridge_envelope() + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/plain_delegate_server", + "headers": [(b"authorization", f"Bearer {envelope}".encode("latin-1"))], + } + + async def mock_user_api_key_auth_fails(api_key, request): + raise HTTPException(status_code=401, detail="Invalid API key") + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + side_effect=mock_user_api_key_auth_fails, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + ): + mock_mgr.get_mcp_server_by_name.return_value = self._bridge_delegate_server( + server_name="plain_delegate_server", dcr_bridge=False + ) + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + # Not admitted by the envelope arm — the oauth2 arm ran instead. + mock_auth.assert_called_once() + + async def test_multi_target_including_bridge_server_does_not_take_envelope_arm(self): + """A multi-target request that includes the bridge server must not take the envelope arm: + the gate requires exactly one target, so it returns None and admission falls through.""" + from litellm.types.mcp import MCPAuth + + envelope = self._mint_bridge_envelope() + scope = { + "type": "http", + "method": "POST", + "path": "/mcp", + "headers": [ + (b"authorization", f"Bearer {envelope}".encode("latin-1")), + (b"x-mcp-servers", b"bridge_delegate_server,other_server"), + ], + } + + def mock_lookup(name, client_ip=None): + if name == "bridge_delegate_server": + return self._bridge_delegate_server() + return TestMCPDelegateAuthToUpstream._make_server(auth_type=MCPAuth.api_key) + + async def mock_user_api_key_auth_fails(api_key, request): + raise HTTPException(status_code=401, detail="Invalid API key") + + with ( + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.user_api_key_auth", + side_effect=mock_user_api_key_auth_fails, + ) as mock_auth, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager") as mock_mgr, + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + ): + mock_mgr.get_mcp_server_by_name.side_effect = mock_lookup + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler.process_mcp_request(scope) + + assert exc_info.value.status_code == 401 + mock_auth.assert_called_once() + + async def test_admit_helper_returns_new_headers_without_mutating_input(self): + """Unit: ``_admit_dcr_bridge_delegate`` must return a NEW headers dict that preserves the + caller's existing per-server entries and adds the injected inner token, never mutating the + input dict.""" + envelope = self._mint_bridge_envelope() + existing = {"other_server": {"Authorization": "Bearer someone-elses-token"}} + + with ( + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + self._patch_key_reload(return_value=self._reloaded_key(user_id="unit-user")), + ): + auth_result, new_headers = await MCPRequestHandler._admit_dcr_bridge_delegate( + server=self._bridge_delegate_server(), + authorization_value=f"Bearer {envelope}", + mcp_server_auth_headers=existing, + request=self._mcp_request(), + route="/mcp/bridge_delegate_server", + ) + + assert auth_result.user_id == "unit-user" + # Input untouched. + assert existing == {"other_server": {"Authorization": "Bearer someone-elses-token"}} + # New dict carries both the pre-existing entry and the injected inner token. + assert new_headers is not existing + assert new_headers == { + "other_server": {"Authorization": "Bearer someone-elses-token"}, + "bridge_delegate_server": {"Authorization": "Bearer inner-upstream-access-token"}, + } + + async def test_admit_helper_raises_500_when_master_key_missing(self): + """Unit: without a configured master_key the gateway cannot derive envelope keys, so + admission raises a 500 rather than silently admitting.""" + envelope = self._mint_bridge_envelope() + with patch("litellm.proxy.proxy_server.master_key", None): + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler._admit_dcr_bridge_delegate( + server=self._bridge_delegate_server(), + authorization_value=f"Bearer {envelope}", + mcp_server_auth_headers=None, + request=self._mcp_request(), + route="/mcp/bridge_delegate_server", + ) + assert exc_info.value.status_code == 500 + + async def test_admit_helper_raises_500_when_no_db_connection(self): + """Unit: with a valid envelope but no database to reload the key from, admission raises a 500 + rather than admitting on unresolved authorization.""" + envelope = self._mint_bridge_envelope() + with ( + patch("litellm.proxy.proxy_server.master_key", self._MASTER_KEY), + patch("litellm.proxy.proxy_server.prisma_client", None), + ): + with pytest.raises(HTTPException) as exc_info: + await MCPRequestHandler._admit_dcr_bridge_delegate( + server=self._bridge_delegate_server(), + authorization_value=f"Bearer {envelope}", + mcp_server_auth_headers=None, + request=self._mcp_request(), + route="/mcp/bridge_delegate_server", + ) + assert exc_info.value.status_code == 500 diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_adapter.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_adapter.py index 383dc255607..17960e917a4 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_adapter.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_adapter.py @@ -7,12 +7,12 @@ maps each CredError onto its HTTP status. These pin the parity-critical mapping import base64 from types import SimpleNamespace -from unittest.mock import patch import pytest from fastapi import HTTPException from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( + oauth_protected_resource_path, raise_public, raise_user_oauth_challenge, to_server_spec, @@ -23,7 +23,9 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( AuthorizationCodeConfig, CredError, NoneConfig, + PassthroughConfig, SharedKey, + TokenExchangeConfig, ) from litellm.types.mcp import MCPAuth, MCPTransport from litellm.types.mcp_server.mcp_server_manager import MCPServer @@ -65,9 +67,7 @@ def test_authorization_schemes_map_with_their_prefix(auth_type, prefix): def test_basic_scheme_base64_encodes_the_token(): - spec = to_server_spec( - _server(auth_type=MCPAuth.basic, authentication_token="user:pass") - ) + spec = to_server_spec(_server(auth_type=MCPAuth.basic, authentication_token="user:pass")) assert spec is not None and isinstance(spec.config, ApiKeyConfig) assert spec.config.value_prefix == "Basic" expected = base64.b64encode(b"user:pass").decode() @@ -89,17 +89,16 @@ def test_oauth2_user_token_maps_to_authorization_code(oauth2_flow): [ _server(auth_type=MCPAuth.api_key), # no token configured _server(auth_type=MCPAuth.bearer_token), # no token configured + _server(auth_type=MCPAuth.oauth2, oauth2_flow="client_credentials"), # M2M -> v1 + _server(auth_type=MCPAuth.oauth2, delegate_auth_to_upstream=True), # delegated upstream OAuth -> v1 + _server(auth_type=MCPAuth.oauth2_token_exchange), # no endpoint/client creds -> incomplete -> v1 _server( - auth_type=MCPAuth.oauth2, oauth2_flow="client_credentials" - ), # M2M -> v1 - _server( - auth_type=MCPAuth.oauth2, delegate_auth_to_upstream=True - ), # delegated upstream OAuth -> v1 - _server(auth_type=MCPAuth.oauth2_token_exchange), + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp/token", + client_id="cid", + ), # missing client_secret -> incomplete -> v1 _server(auth_type=MCPAuth.aws_sigv4), - _server( - auth_type=None, oauth_passthrough=True, extra_headers=["Authorization"] - ), + _server(auth_type=None, oauth_passthrough=True, extra_headers=["Authorization"]), ], ) def test_unmigrated_modes_defer_to_v1(server): @@ -107,6 +106,141 @@ def test_unmigrated_modes_defer_to_v1(server): assert to_server_spec(server) is None +def test_token_exchange_maps_full_config(): + spec = to_server_spec( + _server( + auth_type=MCPAuth.oauth2_token_exchange, + url="https://up.example.com/mcp", + token_exchange_endpoint="https://idp.example.com/token", + audience="https://up.example.com", + client_id="cid", + client_secret="csec", + subject_token_type="urn:ietf:params:oauth:token-type:jwt", + scopes=["a", "b"], + ) + ) + assert spec is not None + config = spec.config + assert isinstance(config, TokenExchangeConfig) + assert config.token_exchange_endpoint == "https://idp.example.com/token" + assert config.audience == "https://up.example.com" + assert config.client_id == "cid" + assert config.client_secret is not None + assert config.client_secret.get_secret_value() == "csec" + assert config.subject_token_type == "urn:ietf:params:oauth:token-type:jwt" + assert config.scopes == ("a", "b") + + +def test_token_exchange_falls_back_to_token_url_when_no_exchange_endpoint(): + spec = to_server_spec( + _server( + auth_type=MCPAuth.oauth2_token_exchange, + token_url="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + ) + assert spec is not None + assert isinstance(spec.config, TokenExchangeConfig) + assert spec.config.token_exchange_endpoint == "https://idp.example.com/token" + + +def test_token_exchange_with_creds_but_no_endpoint_is_owned_for_fail_closed(): + # An OBO server with client credentials but no endpoint is still owned by v2 (spec, not None) so + # it fails closed at the exchanger (412) rather than silently deferring to v1 and connecting + # unauthenticated. The endpoint stays None for the exchanger to reject. + spec = to_server_spec( + _server( + auth_type=MCPAuth.oauth2_token_exchange, + client_id="cid", + client_secret="csec", + ) + ) + assert spec is not None + assert isinstance(spec.config, TokenExchangeConfig) + assert spec.config.token_exchange_endpoint is None + + +def test_token_exchange_maps_entra_obo_profile(): + spec = to_server_spec( + _server( + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://login.microsoftonline.com/tid/oauth2/v2.0/token", + client_id="cid", + client_secret="csec", + token_exchange_profile="entra_obo", + scopes=["api://target/.default"], + ) + ) + assert spec is not None and isinstance(spec.config, TokenExchangeConfig) + assert spec.config.profile == "entra_obo" + + +def test_token_exchange_defaults_to_rfc8693_profile(): + spec = to_server_spec( + _server( + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp/token", + client_id="cid", + client_secret="csec", + ) + ) + assert spec is not None and isinstance(spec.config, TokenExchangeConfig) + assert spec.config.profile == "rfc8693" + + +@pytest.mark.parametrize("bogus", ["", "RFC8693", "jwt_bearer", "entra", "unknown"]) +def test_token_exchange_unknown_profile_normalizes_to_rfc8693(bogus): + # A bad DB/config value must normalize to rfc8693, not raise a ValidationError building the spec. + spec = to_server_spec( + _server( + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp/token", + client_id="cid", + client_secret="csec", + token_exchange_profile=bogus, + ) + ) + assert spec is not None and isinstance(spec.config, TokenExchangeConfig) + assert spec.config.profile == "rfc8693" + + +def test_token_exchange_omits_audience_when_unset(): + spec = to_server_spec( + _server( + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + ) + assert spec is not None + assert isinstance(spec.config, TokenExchangeConfig) + assert spec.config.audience is None + + +def test_token_exchange_empty_subject_token_type_normalizes_to_default(): + # Parity with v1: a falsy subject_token_type must not be sent verbatim to the IdP. + spec = to_server_spec( + _server( + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + subject_token_type="", + ) + ) + assert spec is not None + assert isinstance(spec.config, TokenExchangeConfig) + assert spec.config.subject_token_type == "urn:ietf:params:oauth:token-type:access_token" + + +@pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +def test_client_forwarded_modes_map_to_passthrough_config(auth_type): + spec = to_server_spec(_server(auth_type=auth_type)) + assert spec is not None and isinstance(spec.config, PassthroughConfig) + + @pytest.mark.parametrize( "server", [ @@ -115,9 +249,7 @@ def test_unmigrated_modes_defer_to_v1(server): # static token must not route a BYOK server to a v2 shared-key spec with the wrong value. _server(auth_type=MCPAuth.bearer_token, is_byok=True, authentication_token="x"), _server(auth_type=MCPAuth.basic, is_byok=True, authentication_token="x"), - _server( - auth_type=MCPAuth.authorization, is_byok=True, authentication_token="x" - ), + _server(auth_type=MCPAuth.authorization, is_byok=True, authentication_token="x"), _server(auth_type=MCPAuth.token, is_byok=True, authentication_token="x"), _server(auth_type=None, is_byok=True), ], @@ -161,9 +293,7 @@ def test_raise_public_maps_each_error_to_its_status(error, status): def test_raise_public_emits_unauthorized_challenge(): body = {"error": "byok_auth_required", "server_id": "s1"} - error = CredError.of_unauthorized( - "needs key", www_authenticate='Bearer resource_metadata="/x"', body=body - ) + error = CredError.of_unauthorized("needs key", www_authenticate='Bearer resource_metadata="/x"', body=body) with pytest.raises(HTTPException) as exc_info: raise_public(error) exc = exc_info.value @@ -182,30 +312,17 @@ def test_raise_public_plain_unauthorized_has_no_challenge(): assert exc.headers is None -_ROOT_PATH = "litellm.proxy.utils.get_server_root_path" - - -def test_raise_user_oauth_challenge_points_at_per_server_prm(): - with patch(_ROOT_PATH, return_value="/"), pytest.raises(HTTPException) as exc_info: - raise_user_oauth_challenge(_server(alias="my-srv")) - exc = exc_info.value - assert exc.status_code == 401 - assert ( - exc.headers["WWW-Authenticate"] - == 'Bearer resource_metadata="/.well-known/oauth-protected-resource/mcp/my-srv"' - ) - - -def test_raise_user_oauth_challenge_includes_server_root_path(): - with ( - patch(_ROOT_PATH, return_value="/api/v1"), - pytest.raises(HTTPException) as exc_info, - ): - raise_user_oauth_challenge(_server(alias="my-srv")) - assert ( - exc_info.value.headers["WWW-Authenticate"] - == 'Bearer resource_metadata="/.well-known/oauth-protected-resource/api/v1/mcp/my-srv"' - ) +@pytest.mark.parametrize( + "root_path, expected_prefix", + [ + ("/", ""), # "/" means no prefix + ("", ""), # empty means no prefix + ("/api/v1", "/api/v1"), # a real root path is prepended verbatim + ], +) +def test_oauth_protected_resource_path_honors_root_path(root_path, expected_prefix): + path = oauth_protected_resource_path(root_path, _server(alias="my-srv")) + assert path == f"/.well-known/oauth-protected-resource{expected_prefix}/mcp/my-srv" @pytest.mark.parametrize( @@ -216,7 +333,83 @@ def test_raise_user_oauth_challenge_includes_server_root_path(): ({}, "n"), # then the name field (server_id is the last fallback) ], ) -def test_raise_user_oauth_challenge_name_fallback(kwargs, expected_name): - with patch(_ROOT_PATH, return_value="/"), pytest.raises(HTTPException) as exc_info: - raise_user_oauth_challenge(_server(**kwargs)) - assert f'/mcp/{expected_name}"' in exc_info.value.headers["WWW-Authenticate"] +def test_oauth_protected_resource_path_name_fallback(kwargs, expected_name): + assert oauth_protected_resource_path("/", _server(**kwargs)).endswith(f"/mcp/{expected_name}") + + +def test_raise_user_oauth_challenge_points_at_per_server_prm(): + with pytest.raises(HTTPException) as exc_info: + raise_user_oauth_challenge(_server(alias="my-srv"), root_path="/") + exc = exc_info.value + assert exc.status_code == 401 + assert ( + exc.headers["WWW-Authenticate"] == 'Bearer resource_metadata="/.well-known/oauth-protected-resource/mcp/my-srv"' + ) + + +def test_raise_user_oauth_challenge_includes_server_root_path(): + with pytest.raises(HTTPException) as exc_info: + raise_user_oauth_challenge(_server(alias="my-srv"), root_path="/api/v1") + assert ( + exc_info.value.headers["WWW-Authenticate"] + == 'Bearer resource_metadata="/.well-known/oauth-protected-resource/api/v1/mcp/my-srv"' + ) + + +def test_raise_token_exchange_challenge_is_rfc9728_invalid_token(): + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( + raise_token_exchange_challenge, + ) + + with pytest.raises(HTTPException) as exc_info: + raise_token_exchange_challenge(_server(alias="obo-srv"), root_path="/") + exc = exc_info.value + www = exc.headers["WWW-Authenticate"] + assert exc.status_code == 401 + # RFC 9728 resource_metadata so the client can discover the IdP, plus RFC 6750 invalid_token. + assert 'resource_metadata="/.well-known/oauth-protected-resource/mcp/obo-srv"' in www + assert 'error="invalid_token"' in www + assert "error_description=" in www + + +def test_raise_token_exchange_challenge_includes_server_root_path(): + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( + raise_token_exchange_challenge, + ) + + with pytest.raises(HTTPException) as exc_info: + raise_token_exchange_challenge(_server(alias="obo-srv"), root_path="/api/v1") + www = exc_info.value.headers["WWW-Authenticate"] + assert 'resource_metadata="/.well-known/oauth-protected-resource/api/v1/mcp/obo-srv"' in www + + +def test_raise_token_exchange_challenge_static_form_is_unchanged_without_step_up(): + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( + raise_token_exchange_challenge, + ) + + with pytest.raises(HTTPException) as exc_info: + raise_token_exchange_challenge(_server(alias="obo-srv"), root_path="") + assert exc_info.value.headers["WWW-Authenticate"] == ( + 'Bearer resource_metadata="/.well-known/oauth-protected-resource/mcp/obo-srv", ' + 'error="invalid_token", ' + 'error_description="Missing or invalid subject token; authenticate with the IdP and retry"' + ) + + +def test_raise_token_exchange_challenge_uses_insufficient_claims_with_claims_present(): + # Per the Microsoft claims-challenge format, a claims challenge MUST use error=insufficient_claims + # (the value MSAL-family clients key on), and the claims ride base64-encoded, never raw. + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( + raise_token_exchange_challenge, + ) + + claims = '{"access_token":{"acrs":{"essential":true,"value":"c1"}}}' + with pytest.raises(HTTPException) as exc_info: + raise_token_exchange_challenge(_server(alias="obo-srv"), root_path="", claims=claims) + www = exc_info.value.headers["WWW-Authenticate"] + assert 'resource_metadata="/.well-known/oauth-protected-resource/mcp/obo-srv"' in www + assert 'error="insufficient_claims"' in www + assert 'error="invalid_token"' not in www + assert f'claims="{base64.b64encode(claims.encode()).decode()}"' in www + assert claims not in www # raw JSON never appears; only the base64 form diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py new file mode 100644 index 00000000000..82e8e2aae89 --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py @@ -0,0 +1,218 @@ +"""Spec tests for the DCR-bridge envelope producer and consumer helpers. + +These pin the contracts the token endpoint and admission edge depend on: the master-key +key derivation is deterministic, domain-separated (keyed HMAC), and always yields a +>= 32-byte signing key; the ``Authorization`` classifier is total over the cases admission +branches on (non-envelope, valid envelope bound to this server, envelope-shaped-but- +unopenable, and envelope minted for a different server); the producer helper round-trips +through the consumer; and no path leaks the upstream token in a repr. +""" + +from datetime import datetime, timedelta, timezone + +from pydantic import SecretStr + +from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credentials import ( + BridgeEnvelopeAdmitted, + BridgeEnvelopeInvalid, + NotBridgeEnvelope, + build_bridge_token_response, + envelope_keys_from_master_key, + is_bridge_envelope_shaped, + resolve_bridge_envelope, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + ENVELOPE_PREFIX, + EnvelopeIdentity, + EnvelopeKeys, + EnvelopeTooLarge, + SealedEnvelope, + UpstreamTokenGrant, + mint_envelope, +) + +_NOW = datetime(2026, 7, 9, 12, 0, 0, tzinfo=timezone.utc) +_MASTER_KEY = "sk-master-key-for-derivation-tests-0123456789" +_ACCESS_TOKEN = "upstream-access-token-do-not-leak-8f14e45fceea" +_IDENTITY = EnvelopeIdentity(server_id="srv-456", key_hash="hashed-key-123") +_SERVER_ID = _IDENTITY.server_id + + +def _grant() -> UpstreamTokenGrant: + return UpstreamTokenGrant(access_token=SecretStr(_ACCESS_TOKEN), token_type="Bearer", expires_in=600) + + +def _sealed_token(keys: EnvelopeKeys, now: datetime = _NOW, identity: EnvelopeIdentity = _IDENTITY) -> str: + sealed = mint_envelope(identity, _grant(), keys, now) + assert isinstance(sealed, SealedEnvelope) + return sealed.token.get_secret_value() + + +def test_key_derivation_is_deterministic(): + assert envelope_keys_from_master_key(_MASTER_KEY) == envelope_keys_from_master_key(_MASTER_KEY) + + +def test_key_derivation_signing_and_encryption_differ(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + assert keys.signing_key.get_secret_value() != keys.encryption_key.get_secret_value() + + +def test_key_derivation_differs_by_master_key(): + a = envelope_keys_from_master_key(_MASTER_KEY) + b = envelope_keys_from_master_key(_MASTER_KEY + "x") + assert a.signing_key.get_secret_value() != b.signing_key.get_secret_value() + assert a.encryption_key.get_secret_value() != b.encryption_key.get_secret_value() + + +def test_key_derivation_signing_key_meets_hs256_floor_for_short_master_key(): + keys = envelope_keys_from_master_key("x") + assert len(keys.signing_key.get_secret_value()) >= 32 + + +def test_key_derivation_is_cached_so_the_memory_hard_kdf_runs_once_per_key(): + """The scrypt KDF is intentionally expensive to resist offline guessing, so it must be cached: + repeated calls for the same master key return the identical object rather than re-deriving, + keeping the per-request admission path free. Returning a distinct object each call would mean + the cache was dropped and every open would pay the memory-hard cost.""" + first = envelope_keys_from_master_key("sk-cache-probe-key-9988776655") + assert envelope_keys_from_master_key("sk-cache-probe-key-9988776655") is first + + +def test_derived_keys_round_trip_mint_and_open(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + result = resolve_bridge_envelope(_sealed_token(keys), keys, _NOW, _SERVER_ID) + assert isinstance(result, BridgeEnvelopeAdmitted) + assert result.identity == _IDENTITY + + +def test_resolve_non_envelope_is_not_bridge_envelope(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + assert isinstance(resolve_bridge_envelope("Bearer sk-some-litellm-key", keys, _NOW, _SERVER_ID), NotBridgeEnvelope) + assert isinstance(resolve_bridge_envelope("plain-token", keys, _NOW, _SERVER_ID), NotBridgeEnvelope) + + +def test_resolve_valid_envelope_returns_identity_and_upstream_authorization(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + result = resolve_bridge_envelope(_sealed_token(keys), keys, _NOW, _SERVER_ID) + assert isinstance(result, BridgeEnvelopeAdmitted) + assert result.identity == _IDENTITY + assert result.upstream_authorization.get_secret_value() == f"Bearer {_ACCESS_TOKEN}" + + +def test_resolve_strips_optional_bearer_scheme_before_detection(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + token = _sealed_token(keys) + assert token.startswith(ENVELOPE_PREFIX) + bare = resolve_bridge_envelope(token, keys, _NOW, _SERVER_ID) + prefixed = resolve_bridge_envelope(f"Bearer {token}", keys, _NOW, _SERVER_ID) + lower = resolve_bridge_envelope(f"bearer {token}", keys, _NOW, _SERVER_ID) + assert isinstance(bare, BridgeEnvelopeAdmitted) + assert isinstance(prefixed, BridgeEnvelopeAdmitted) + assert isinstance(lower, BridgeEnvelopeAdmitted) + assert prefixed.upstream_authorization.get_secret_value() == bare.upstream_authorization.get_secret_value() + + +def test_resolve_expired_envelope_is_invalid_not_admitted(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + token = _sealed_token(keys, now=_NOW) + later = _NOW + timedelta(seconds=601) + assert isinstance(resolve_bridge_envelope(token, keys, later, _SERVER_ID), BridgeEnvelopeInvalid) + + +def test_resolve_envelope_minted_under_a_different_master_key_is_invalid(): + minted = envelope_keys_from_master_key(_MASTER_KEY) + other = envelope_keys_from_master_key("a-completely-different-master-key") + assert isinstance(resolve_bridge_envelope(_sealed_token(minted), other, _NOW, _SERVER_ID), BridgeEnvelopeInvalid) + + +def test_resolve_tampered_envelope_is_invalid(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + token = _sealed_token(keys) + tampered = token[:-4] + ("aaaa" if token[-4:] != "aaaa" else "bbbb") + result = resolve_bridge_envelope(tampered, keys, _NOW, _SERVER_ID) + assert isinstance(result, BridgeEnvelopeInvalid) + + +def test_resolve_envelope_minted_for_another_server_is_invalid(): + """An envelope sealed for server A must be rejected when presented to server B, so a + captured or misrouted envelope cannot forward one server's upstream credential to + another. The valid access token stays sealed; the mismatch alone fails the resolve.""" + keys = envelope_keys_from_master_key(_MASTER_KEY) + other_server_identity = EnvelopeIdentity(server_id="srv-OTHER", key_hash=_IDENTITY.key_hash) + token = _sealed_token(keys, identity=other_server_identity) + result = resolve_bridge_envelope(token, keys, _NOW, _SERVER_ID) + assert isinstance(result, BridgeEnvelopeInvalid) + + +def test_resolve_matching_server_binding_is_admitted(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + result = resolve_bridge_envelope(_sealed_token(keys), keys, _NOW, "srv-456") + assert isinstance(result, BridgeEnvelopeAdmitted) + + +def test_resolve_non_ascii_server_id_stays_total_and_does_not_raise(): + """The server-binding check must not raise on a non-ASCII server_id (an admin can register a + unicode server_id); it stays total and returns a typed result. A matching non-ASCII id admits, + a mismatching one is BridgeEnvelopeInvalid, and neither raises.""" + keys = envelope_keys_from_master_key(_MASTER_KEY) + unicode_identity = EnvelopeIdentity(server_id="srv-café", key_hash=_IDENTITY.key_hash) + token = _sealed_token(keys, identity=unicode_identity) + assert isinstance(resolve_bridge_envelope(token, keys, _NOW, "srv-café"), BridgeEnvelopeAdmitted) + assert isinstance(resolve_bridge_envelope(token, keys, _NOW, "srv-cafe"), BridgeEnvelopeInvalid) + + +def test_resolve_strips_bearer_with_extra_whitespace(): + """A Bearer scheme separated by extra spaces or a tab still yields the envelope, so a client + using non-minimal but legal whitespace is not misclassified as a non-envelope.""" + keys = envelope_keys_from_master_key(_MASTER_KEY) + token = _sealed_token(keys) + for header in (f"Bearer {token}", f"Bearer\t{token}", f" Bearer {token}"): + assert isinstance(resolve_bridge_envelope(header, keys, _NOW, _SERVER_ID), BridgeEnvelopeAdmitted) + + +def test_admitted_result_repr_never_leaks_upstream_token(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + result = resolve_bridge_envelope(_sealed_token(keys), keys, _NOW, _SERVER_ID) + assert isinstance(result, BridgeEnvelopeAdmitted) + assert _ACCESS_TOKEN not in repr(result) + assert _ACCESS_TOKEN not in str(result) + + +def test_build_bridge_token_response_round_trips_through_the_consumer(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + sealed = build_bridge_token_response(_IDENTITY, _grant(), keys, _NOW) + assert isinstance(sealed, SealedEnvelope) + assert sealed.token.get_secret_value().startswith(ENVELOPE_PREFIX) + result = resolve_bridge_envelope(sealed.token.get_secret_value(), keys, _NOW, _SERVER_ID) + assert isinstance(result, BridgeEnvelopeAdmitted) + assert result.identity == _IDENTITY + assert result.upstream_authorization.get_secret_value() == f"Bearer {_ACCESS_TOKEN}" + + +def test_build_bridge_token_response_oversized_grant_returns_error_value(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + huge = UpstreamTokenGrant(access_token=SecretStr("x" * 20000), token_type="Bearer") + result = build_bridge_token_response(_IDENTITY, huge, keys, _NOW) + assert isinstance(result, EnvelopeTooLarge) + + +def test_build_bridge_token_response_repr_never_leaks_upstream_token(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + sealed = build_bridge_token_response(_IDENTITY, _grant(), keys, _NOW) + assert isinstance(sealed, SealedEnvelope) + assert _ACCESS_TOKEN not in repr(sealed) + assert _ACCESS_TOKEN not in str(sealed) + + +def test_is_bridge_envelope_shaped_detects_envelope_with_and_without_bearer(): + keys = envelope_keys_from_master_key(_MASTER_KEY) + token = _sealed_token(keys) + assert is_bridge_envelope_shaped(token) is True + assert is_bridge_envelope_shaped(f"Bearer {token}") is True + assert is_bridge_envelope_shaped(f"bearer {token}") is True + + +def test_is_bridge_envelope_shaped_rejects_non_envelope_bearer(): + assert is_bridge_envelope_shaped("Bearer sk-some-litellm-key") is False + assert is_bridge_envelope_shaped("plain-upstream-token") is False + assert is_bridge_envelope_shaped("") is False diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_envelope.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_envelope.py new file mode 100644 index 00000000000..b44f3f84cc9 --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_envelope.py @@ -0,0 +1,487 @@ +"""Spec tests for the sealed-envelope module (oauth_delegate DCR bridge). + +The envelope is the single client-held bearer carrying both a litellm identity and the +encrypted upstream grant, with zero server-side storage. These tests pin the security +contract: an envelope opens only under the exact keys that minted it, tampering with any +signed byte is detected, expiry is enforced against the injected clock (capped by the +module TTL ceiling), oversized envelopes are rejected rather than truncated, and no +error value, model repr, or raised exception ever contains the inner access token. +""" + +import base64 +import hashlib +import hmac +import json +from datetime import datetime, timedelta, timezone + +import jwt +import pytest +from cryptography.hazmat.primitives.asymmetric import rsa +from pydantic import SecretStr, ValidationError + +from litellm.proxy._experimental.mcp_server.outbound_credentials.envelope import ( + ENVELOPE_ISSUER, + ENVELOPE_PREFIX, + MAX_ENVELOPE_BYTES, + MAX_ENVELOPE_TTL_SECONDS, + BadSignature, + DecryptFailed, + EnvelopeIdentity, + EnvelopeKeys, + EnvelopeTooLarge, + Expired, + MalformedPayload, + NotAnEnvelope, + OpenedEnvelope, + SealedEnvelope, + UpstreamTokenGrant, + is_envelope, + mint_envelope, + open_envelope, +) +from litellm.proxy.common_utils.encrypt_decrypt_utils import decrypt_value, encrypt_value + +_NOW = datetime(2026, 7, 9, 12, 0, 0, tzinfo=timezone.utc) +_SIGNING_KEY = "unit-test-signing-key-0123456789abcdef0123456789abcdef" +_ENCRYPTION_KEY = "unit-test-encryption-key-fedcba9876543210fedcba9876543210" +_OTHER_SIGNING_KEY = "other-signing-key-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +_OTHER_ENCRYPTION_KEY = "other-encryption-key-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" +_KEYS = EnvelopeKeys(signing_key=SecretStr(_SIGNING_KEY), encryption_key=SecretStr(_ENCRYPTION_KEY)) +_WRONG_SIGNING = EnvelopeKeys(signing_key=SecretStr(_OTHER_SIGNING_KEY), encryption_key=SecretStr(_ENCRYPTION_KEY)) +_WRONG_ENCRYPTION = EnvelopeKeys(signing_key=SecretStr(_SIGNING_KEY), encryption_key=SecretStr(_OTHER_ENCRYPTION_KEY)) +_ACCESS_TOKEN = "upstream-access-token-do-not-leak-8f14e45fceea" +_REFRESH_TOKEN = "upstream-refresh-token-do-not-leak-1d0aa4b7" +_IDENTITY = EnvelopeIdentity(server_id="srv-456", key_hash="hashed-key-123") + + +def _full_grant() -> UpstreamTokenGrant: + return UpstreamTokenGrant( + access_token=SecretStr(_ACCESS_TOKEN), + token_type="Bearer", + refresh_token=SecretStr(_REFRESH_TOKEN), + scope="read:tools write:tools", + expires_in=600, + ) + + +def _minimal_grant() -> UpstreamTokenGrant: + return UpstreamTokenGrant(access_token=SecretStr(_ACCESS_TOKEN), token_type="Bearer") + + +def _sealed_token(grant: UpstreamTokenGrant, keys: EnvelopeKeys = _KEYS) -> str: + sealed = mint_envelope(_IDENTITY, grant, keys, _NOW) + assert isinstance(sealed, SealedEnvelope) + return sealed.token.get_secret_value() + + +def _unverified_claims(sealed_token: str) -> dict[str, object]: + return jwt.decode(sealed_token.removeprefix(ENVELOPE_PREFIX), options={"verify_signature": False}) + + +def _forge(claims: dict[str, object], signing_key: str = _SIGNING_KEY) -> str: + return ENVELOPE_PREFIX + jwt.encode(claims, signing_key, algorithm="HS256") + + +def _b64url(raw: bytes) -> str: + return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii") + + +def _hand_crafted_hs256(payload: dict[str, object], signing_key: str = _SIGNING_KEY) -> str: + """Assemble an HS256 envelope from raw bytes, bypassing PyJWT's encode-side claim + guards (it refuses to build a token with a non-string ``iss``). This is the real + attacker path: a client crafts the compact JWT directly, so any registered claim can + carry a hostile JSON type.""" + header = _b64url(json.dumps({"alg": "HS256", "typ": "JWT"}).encode("utf-8")) + body = _b64url(json.dumps(payload).encode("utf-8")) + signing_input = f"{header}.{body}".encode("ascii") + signature = _b64url(hmac.new(signing_key.encode("utf-8"), signing_input, hashlib.sha256).digest()) + return ENVELOPE_PREFIX + f"{header}.{body}.{signature}" + + +def _tampered(sealed_token: str, segment: int, index: int) -> str: + parts = sealed_token.removeprefix(ENVELOPE_PREFIX).split(".") + original = parts[segment][index] + replacement = "A" if original in "QRST" else "Q" + mutated = parts[segment][:index] + replacement + parts[segment][index + 1 :] + rebuilt = ".".join(parts[:segment] + [mutated] + parts[segment + 1 :]) + return ENVELOPE_PREFIX + rebuilt + + +def test_round_trip_recovers_identity_and_grant_exactly(): + grant = _full_grant() + token = _sealed_token(grant) + assert is_envelope(token) + opened = open_envelope(token, _KEYS, _NOW) + assert isinstance(opened, OpenedEnvelope) + assert opened.identity == _IDENTITY + assert opened.grant == grant + assert opened.grant.access_token.get_secret_value() == _ACCESS_TOKEN + assert opened.grant.refresh_token is not None + assert opened.grant.refresh_token.get_secret_value() == _REFRESH_TOKEN + + +def test_minimal_grant_round_trips_without_none_leakage_into_claims(): + token = _sealed_token(_minimal_grant()) + claims = _unverified_claims(token) + blob = claims["grant"] + assert isinstance(blob, str) + plaintext = decrypt_value(value=base64.urlsafe_b64decode(blob), signing_key=_ENCRYPTION_KEY) + assert set(json.loads(plaintext)) == {"access_token", "token_type"} + opened = open_envelope(token, _KEYS, _NOW) + assert isinstance(opened, OpenedEnvelope) + assert opened.grant.refresh_token is None + assert opened.grant.scope is None + assert opened.grant.expires_in is None + + +def test_claim_layout_and_no_plaintext_token_in_envelope(): + token = _sealed_token(_full_grant()) + claims = _unverified_claims(token) + assert set(claims) == {"iss", "iat", "exp", "server_id", "key_hash", "grant"} + assert claims["iss"] == ENVELOPE_ISSUER + assert claims["iat"] == int(_NOW.timestamp()) + assert claims["exp"] == int(_NOW.timestamp()) + 600 + assert claims["server_id"] == "srv-456" + assert claims["key_hash"] == "hashed-key-123" + assert _ACCESS_TOKEN not in token + assert _ACCESS_TOKEN not in json.dumps(claims) + assert _REFRESH_TOKEN not in json.dumps(claims) + + +@pytest.mark.parametrize( + "expires_in, expected_ttl", + [ + (600, 600), + (MAX_ENVELOPE_TTL_SECONDS + 82800, MAX_ENVELOPE_TTL_SECONDS), + (None, MAX_ENVELOPE_TTL_SECONDS), + ], +) +def test_exp_is_min_of_upstream_expires_in_and_cap(expires_in, expected_ttl): + grant = UpstreamTokenGrant(access_token=SecretStr(_ACCESS_TOKEN), token_type="Bearer", expires_in=expires_in) + sealed = mint_envelope(_IDENTITY, grant, _KEYS, _NOW) + assert isinstance(sealed, SealedEnvelope) + assert sealed.expires_at == _NOW + timedelta(seconds=expected_ttl) + + +def test_expiry_honored_against_injected_clock(): + token = _sealed_token(_full_grant()) + assert isinstance(open_envelope(token, _KEYS, _NOW + timedelta(seconds=599)), OpenedEnvelope) + assert isinstance(open_envelope(token, _KEYS, _NOW + timedelta(seconds=600)), Expired) + assert isinstance(open_envelope(token, _KEYS, _NOW + timedelta(seconds=601)), Expired) + + +def test_ttl_cap_enforced_on_open_even_when_upstream_token_lives_longer(): + grant = UpstreamTokenGrant(access_token=SecretStr(_ACCESS_TOKEN), token_type="Bearer", expires_in=86400) + token = _sealed_token(grant) + just_before_cap = _NOW + timedelta(seconds=MAX_ENVELOPE_TTL_SECONDS - 1) + at_cap = _NOW + timedelta(seconds=MAX_ENVELOPE_TTL_SECONDS) + assert isinstance(open_envelope(token, _KEYS, just_before_cap), OpenedEnvelope) + assert isinstance(open_envelope(token, _KEYS, at_cap), Expired) + + +def test_tampering_any_payload_or_signature_byte_is_bad_signature(): + token = _sealed_token(_full_grant()) + parts = token.removeprefix(ENVELOPE_PREFIX).split(".") + for segment in (1, 2): + for index in range(len(parts[segment])): + result = open_envelope(_tampered(token, segment, index), _KEYS, _NOW) + assert isinstance(result, BadSignature), f"segment {segment} index {index}: {result!r}" + + +def test_tampering_header_bytes_never_opens(): + token = _sealed_token(_full_grant()) + parts = token.removeprefix(ENVELOPE_PREFIX).split(".") + for index in range(len(parts[0])): + result = open_envelope(_tampered(token, 0, index), _KEYS, _NOW) + assert isinstance(result, (BadSignature, MalformedPayload)), f"header index {index}: {result!r}" + + +def test_alg_none_is_rejected(): + claims = _unverified_claims(_sealed_token(_full_grant())) + unsigned = ENVELOPE_PREFIX + jwt.encode(claims, None, algorithm="none") + assert isinstance(open_envelope(unsigned, _KEYS, _NOW), MalformedPayload) + + +def test_wrong_signing_key_is_bad_signature(): + token = _sealed_token(_full_grant()) + assert isinstance(open_envelope(token, _WRONG_SIGNING, _NOW), BadSignature) + + +def test_wrong_encryption_key_is_decrypt_failed(): + token = _sealed_token(_full_grant()) + assert isinstance(open_envelope(token, _WRONG_ENCRYPTION, _NOW), DecryptFailed) + + +def test_ciphertext_swapped_from_another_envelope_is_decrypt_failed(): + claims_a = _unverified_claims(_sealed_token(_full_grant(), keys=_KEYS)) + claims_b = _unverified_claims(_sealed_token(_minimal_grant(), keys=_WRONG_ENCRYPTION)) + swapped = _forge({**claims_a, "grant": claims_b["grant"]}) + assert isinstance(open_envelope(swapped, _KEYS, _NOW), DecryptFailed) + + +def test_wrong_issuer_is_malformed_payload(): + claims = _unverified_claims(_sealed_token(_full_grant())) + assert isinstance(open_envelope(_forge({**claims, "iss": "evil-issuer"}), _KEYS, _NOW), MalformedPayload) + + +def test_missing_identity_claim_is_malformed_payload(): + claims = _unverified_claims(_sealed_token(_full_grant())) + forged = _forge({key: value for key, value in claims.items() if key != "key_hash"}) + assert isinstance(open_envelope(forged, _KEYS, _NOW), MalformedPayload) + + +@pytest.mark.parametrize("identity_claim", ["server_id", "key_hash"]) +def test_signed_empty_identity_claim_is_malformed_payload_not_a_raise(identity_claim): + claims = _unverified_claims(_sealed_token(_full_grant())) + forged = _forge({**claims, identity_claim: ""}) + assert isinstance(open_envelope(forged, _KEYS, _NOW), MalformedPayload) + intact = open_envelope(_forge(claims), _KEYS, _NOW) + assert isinstance(intact, OpenedEnvelope) + assert intact.identity == _IDENTITY + + +def test_lone_surrogate_candidate_is_malformed_payload_not_a_raise(): + surrogate_candidate = ENVELOPE_PREFIX + "\ud800abc.def.ghi" + result = open_envelope(surrogate_candidate, _KEYS, _NOW) + assert isinstance(result, MalformedPayload) + + +@pytest.mark.parametrize( + "override", + [ + {"iat": [1]}, + {"iat": {}}, + {"iat": float("inf")}, + {"nbf": None}, + {"nbf": [1]}, + ], +) +def test_hostile_iat_nbf_types_are_malformed_payload_not_a_raise(override): + claims = _unverified_claims(_sealed_token(_full_grant())) + forged = _forge({**claims, **override}) + result = open_envelope(forged, _KEYS, _NOW) + assert isinstance(result, MalformedPayload) + + +@pytest.mark.parametrize("hostile_iss", [["litellm-mcp-bridge"], 5, {"iss": "x"}]) +def test_non_string_issuer_claim_is_malformed_payload_not_a_raise(hostile_iss): + claims = _unverified_claims(_sealed_token(_full_grant())) + forged = _hand_crafted_hs256({**claims, "iss": hostile_iss}) + result = open_envelope(forged, _KEYS, _NOW) + assert isinstance(result, MalformedPayload) + + +@pytest.mark.parametrize("hostile_exp", ["600", 600.5, [600]]) +def test_non_int_exp_claim_is_malformed_payload_not_a_raise(hostile_exp): + claims = _unverified_claims(_sealed_token(_full_grant())) + forged = _hand_crafted_hs256({**claims, "exp": hostile_exp}) + result = open_envelope(forged, _KEYS, _NOW) + assert isinstance(result, MalformedPayload) + + +def test_unexpected_extra_claim_is_malformed_payload(): + claims = _unverified_claims(_sealed_token(_full_grant())) + forged = _forge({**claims, "role": "admin"}) + assert isinstance(open_envelope(forged, _KEYS, _NOW), MalformedPayload) + + +def test_future_iat_opens_against_injected_now_not_wall_clock(): + future = _NOW + timedelta(seconds=100_000) + sealed = mint_envelope(_IDENTITY, _full_grant(), _KEYS, future) + assert isinstance(sealed, SealedEnvelope) + opened = open_envelope(sealed.token.get_secret_value(), _KEYS, future) + assert isinstance(opened, OpenedEnvelope) + assert opened.identity == _IDENTITY + assert opened.grant == _full_grant() + + +def test_rs256_signed_token_is_rejected_against_the_hs256_pin(): + claims = _unverified_claims(_sealed_token(_full_grant())) + private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + rs256_token = ENVELOPE_PREFIX + jwt.encode(claims, private_key, algorithm="RS256") + result = open_envelope(rs256_token, _KEYS, _NOW) + assert isinstance(result, MalformedPayload) + + +@pytest.mark.parametrize("short_key", ["", "too-short", "x" * 31]) +def test_signing_key_below_hs256_minimum_is_rejected_at_construction(short_key): + with pytest.raises(ValidationError): + EnvelopeKeys(signing_key=SecretStr(short_key), encryption_key=SecretStr(_ENCRYPTION_KEY)) + + +def test_signing_key_at_hs256_minimum_is_accepted(): + keys = EnvelopeKeys(signing_key=SecretStr("y" * 32), encryption_key=SecretStr(_ENCRYPTION_KEY)) + assert keys.signing_key.get_secret_value() == "y" * 32 + + +def test_correctly_signed_garbage_grant_blob_is_decrypt_failed(): + claims = _unverified_claims(_sealed_token(_full_grant())) + forged = _forge({**claims, "grant": "not-a-ciphertext"}) + assert isinstance(open_envelope(forged, _KEYS, _NOW), DecryptFailed) + + +def test_decryptable_blob_that_is_not_a_grant_is_malformed_payload(): + claims = _unverified_claims(_sealed_token(_full_grant())) + wrong_shape = base64.urlsafe_b64encode( + bytes(encrypt_value(value=json.dumps({"nope": 1}), signing_key=_ENCRYPTION_KEY)) + ).decode("ascii") + forged = _forge({**claims, "grant": wrong_shape}) + assert isinstance(open_envelope(forged, _KEYS, _NOW), MalformedPayload) + + +def _mint_with_token_len(n: int) -> SealedEnvelope | EnvelopeTooLarge: + grant = UpstreamTokenGrant(access_token=SecretStr("a" * n), token_type="Bearer") + return mint_envelope(_IDENTITY, grant, _KEYS, _NOW) + + +def _largest_token_len_that_mints(lo: int, hi: int) -> int: + if hi - lo <= 1: + return lo + mid = (lo + hi) // 2 + if isinstance(_mint_with_token_len(mid), SealedEnvelope): + return _largest_token_len_that_mints(mid, hi) + return _largest_token_len_that_mints(lo, mid) + + +def test_oversized_grant_is_a_typed_mint_error_never_truncated(): + result = _mint_with_token_len(30000) + assert isinstance(result, EnvelopeTooLarge) + assert result.tag == "envelope_too_large" + assert result.size_bytes > MAX_ENVELOPE_BYTES + assert result.max_bytes == MAX_ENVELOPE_BYTES + + +def test_size_cap_boundary_just_under_succeeds_and_just_over_fails(): + assert isinstance(_mint_with_token_len(1), SealedEnvelope) + assert isinstance(_mint_with_token_len(30000), EnvelopeTooLarge) + largest = _largest_token_len_that_mints(1, 30000) + assert largest > 6000 + sealed = _mint_with_token_len(largest) + assert isinstance(sealed, SealedEnvelope) + assert len(sealed.token.get_secret_value().encode("utf-8")) <= MAX_ENVELOPE_BYTES + overflowing = _mint_with_token_len(largest + 1) + assert isinstance(overflowing, EnvelopeTooLarge) + assert overflowing.size_bytes > MAX_ENVELOPE_BYTES + opened = open_envelope(sealed.token.get_secret_value(), _KEYS, _NOW) + assert isinstance(opened, OpenedEnvelope) + + +def test_open_size_guard_measures_bytes_not_characters(): + """The open-side size guard must reject on UTF-8 byte length, matching mint's cap, so a + hostile multi-byte candidate whose character count is under the cap but whose byte count is + over it is rejected up front rather than reaching the expensive HMAC/decrypt path. Patching + _decode_claims to fail loudly proves the guard short-circuits before decode.""" + from unittest.mock import patch + + from litellm.proxy._experimental.mcp_server.outbound_credentials import envelope + + multibyte_body = "é" * 7000 # 7000 chars, 14000 UTF-8 bytes + candidate = ENVELOPE_PREFIX + multibyte_body + assert len(candidate) <= MAX_ENVELOPE_BYTES + assert len(candidate.encode("utf-8")) > MAX_ENVELOPE_BYTES + + with patch.object(envelope, "_decode_claims", side_effect=AssertionError("decode reached")) as decode: + result = open_envelope(candidate, _KEYS, _NOW) + + assert isinstance(result, MalformedPayload) + decode.assert_not_called() + + +def test_open_size_guard_rejects_oversize_character_count_before_decode(): + """A candidate whose character count already exceeds the cap is rejected up front, before the + decode path, so an arbitrarily long hostile string is not run through HMAC/decrypt. The cheap + character precheck makes this O(1) since UTF-8 byte length is never below character length.""" + from unittest.mock import patch + + from litellm.proxy._experimental.mcp_server.outbound_credentials import envelope + + candidate = ENVELOPE_PREFIX + ("a" * (MAX_ENVELOPE_BYTES + 1)) + assert len(candidate) > MAX_ENVELOPE_BYTES + + with patch.object(envelope, "_decode_claims", side_effect=AssertionError("decode reached")) as decode: + result = open_envelope(candidate, _KEYS, _NOW) + + assert isinstance(result, MalformedPayload) + decode.assert_not_called() + + +def test_is_envelope_detects_only_prefixed_values(): + assert is_envelope(_sealed_token(_full_grant())) + raw_jwt = jwt.encode({"sub": "user-123"}, _SIGNING_KEY, algorithm="HS256") + assert not is_envelope(raw_jwt) + assert not is_envelope("some-random-opaque-token") + assert not is_envelope("") + + +def test_open_on_non_envelope_input_is_not_an_envelope(): + raw_jwt = jwt.encode({"sub": "user-123"}, _SIGNING_KEY, algorithm="HS256") + assert isinstance(open_envelope(raw_jwt, _KEYS, _NOW), NotAnEnvelope) + assert isinstance(open_envelope("", _KEYS, _NOW), NotAnEnvelope) + assert isinstance(open_envelope(_ACCESS_TOKEN, _KEYS, _NOW), NotAnEnvelope) + + +def test_open_on_prefixed_garbage_is_malformed_payload(): + assert isinstance(open_envelope(ENVELOPE_PREFIX + "garbage", _KEYS, _NOW), MalformedPayload) + assert isinstance(open_envelope(ENVELOPE_PREFIX + _ACCESS_TOKEN, _KEYS, _NOW), MalformedPayload) + + +def test_no_result_value_ever_reveals_the_access_token(): + grant = _full_grant() + sealed = mint_envelope(_IDENTITY, grant, _KEYS, _NOW) + assert isinstance(sealed, SealedEnvelope) + token = sealed.token.get_secret_value() + oversized_grant = UpstreamTokenGrant(access_token=SecretStr(_ACCESS_TOKEN + "x" * 30000), token_type="Bearer") + values = ( + sealed, + open_envelope(token, _KEYS, _NOW), + mint_envelope(_IDENTITY, oversized_grant, _KEYS, _NOW), + open_envelope(_ACCESS_TOKEN, _KEYS, _NOW), + open_envelope(ENVELOPE_PREFIX + _ACCESS_TOKEN, _KEYS, _NOW), + open_envelope(token, _WRONG_SIGNING, _NOW), + open_envelope(token, _WRONG_ENCRYPTION, _NOW), + open_envelope(token, _KEYS, _NOW + timedelta(seconds=601)), + grant, + ) + for value in values: + assert _ACCESS_TOKEN not in repr(value) + assert _ACCESS_TOKEN not in str(value) + assert _REFRESH_TOKEN not in repr(value) + assert _REFRESH_TOKEN not in str(value) + + +def test_non_positive_expires_in_is_rejected_at_construction_without_leaking(): + for bad_expires_in in (0, -5): + with pytest.raises(ValidationError) as excinfo: + UpstreamTokenGrant( + access_token=SecretStr(_ACCESS_TOKEN), + token_type="Bearer", + expires_in=bad_expires_in, + ) + assert _ACCESS_TOKEN not in str(excinfo.value) + assert _ACCESS_TOKEN not in repr(excinfo.value) + + +def test_empty_identity_and_key_fields_are_rejected_at_construction(): + with pytest.raises(ValidationError): + EnvelopeIdentity(server_id="", key_hash="hashed-key-123") + with pytest.raises(ValidationError): + EnvelopeIdentity(server_id="srv-456", key_hash="") + with pytest.raises(ValidationError): + EnvelopeKeys(signing_key=SecretStr(""), encryption_key=SecretStr(_ENCRYPTION_KEY)) + with pytest.raises(ValidationError): + EnvelopeKeys(signing_key=SecretStr(_SIGNING_KEY), encryption_key=SecretStr("")) + with pytest.raises(ValidationError): + UpstreamTokenGrant(access_token=SecretStr(""), token_type="Bearer") + + +def test_public_models_are_frozen(): + sealed = mint_envelope(_IDENTITY, _full_grant(), _KEYS, _NOW) + assert isinstance(sealed, SealedEnvelope) + opened = open_envelope(sealed.token.get_secret_value(), _KEYS, _NOW) + assert isinstance(opened, OpenedEnvelope) + with pytest.raises(ValidationError): + sealed.token = SecretStr("overwritten") + with pytest.raises(ValidationError): + opened.grant = _minimal_grant() + with pytest.raises(ValidationError): + _IDENTITY.key_hash = "someone-elses-hash" diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_per_user_oauth_store.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_per_user_oauth_store.py index f64ce594efa..ca32cf2bb8d 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_per_user_oauth_store.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_per_user_oauth_store.py @@ -3,8 +3,8 @@ import asyncio import pytest from litellm.proxy._experimental.mcp_server.outbound_credentials.oauth_token_store import ( + InvalidatableOAuthTokenStore, OAuthToken, - OAuthTokenStore, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.per_user_oauth_store import ( LazyPerUserOAuthTokenStore, @@ -16,11 +16,15 @@ class _RecordingStore: def __init__(self, access_token: str) -> None: self._access_token = access_token self.calls: list[tuple[str, str]] = [] + self.invalidations: list[tuple[str, str]] = [] async def fetch(self, user_id: str, server_id: str) -> OAuthToken | None: self.calls.append((user_id, server_id)) return OAuthToken(access_token=self._access_token) + async def invalidate(self, user_id: str, server_id: str) -> None: + self.invalidations.append((user_id, server_id)) + class _BlockingStore: def __init__(self, access_token: str) -> None: @@ -28,6 +32,7 @@ class _BlockingStore: self.started = asyncio.Event() self.release = asyncio.Event() self.calls: list[tuple[str, str]] = [] + self.invalidations: list[tuple[str, str]] = [] async def fetch(self, user_id: str, server_id: str) -> OAuthToken | None: self.calls.append((user_id, server_id)) @@ -35,6 +40,9 @@ class _BlockingStore: await self.release.wait() return OAuthToken(access_token=self._access_token) + async def invalidate(self, user_id: str, server_id: str) -> None: + self.invalidations.append((user_id, server_id)) + class _RedisAvailability: def __init__(self) -> None: @@ -59,7 +67,7 @@ async def test_lazy_store_rebuilds_when_redis_becomes_available() -> None: redis_available = _RedisAvailability() build_calls = 0 - def build_store(_server_lookup: ServerLookup) -> tuple[OAuthTokenStore, bool]: + def build_store(_server_lookup: ServerLookup) -> tuple[InvalidatableOAuthTokenStore, bool]: nonlocal build_calls build_calls += 1 if redis_available.available: @@ -94,7 +102,7 @@ async def test_lazy_store_allows_concurrent_local_fetches_without_redis() -> Non redis_available = _RedisAvailability() build_calls = 0 - def build_store(_server_lookup: ServerLookup) -> tuple[OAuthTokenStore, bool]: + def build_store(_server_lookup: ServerLookup) -> tuple[InvalidatableOAuthTokenStore, bool]: nonlocal build_calls build_calls += 1 return local_store, False @@ -127,7 +135,7 @@ async def test_lazy_store_waits_for_in_flight_local_fetch_before_redis_rebuild() redis_store = _RecordingStore("redis") redis_available = _RedisAvailability() - def build_store(_server_lookup: ServerLookup) -> tuple[OAuthTokenStore, bool]: + def build_store(_server_lookup: ServerLookup) -> tuple[InvalidatableOAuthTokenStore, bool]: if redis_available.available: return redis_store, True return local_store, False @@ -158,3 +166,83 @@ async def test_lazy_store_waits_for_in_flight_local_fetch_before_redis_rebuild() assert second is not None and second.access_token == "redis" assert local_store.calls == [("u", "s")] assert redis_store.calls == [("u", "s")] + + +@pytest.mark.asyncio +async def test_lazy_store_invalidate_builds_chain_and_delegates() -> None: + local_store = _RecordingStore("local") + build_calls = 0 + + def build_store(_server_lookup: ServerLookup) -> tuple[InvalidatableOAuthTokenStore, bool]: + nonlocal build_calls + build_calls += 1 + return local_store, False + + def server_lookup(_server_id: str) -> None: + return None + + store = LazyPerUserOAuthTokenStore( + server_lookup, + store_builder=build_store, + redis_available=_RedisAvailability(), + ) + + await store.invalidate("u", "s") + + assert build_calls == 1 + assert local_store.invalidations == [("u", "s")] + + +@pytest.mark.asyncio +async def test_lazy_store_invalidate_reaches_the_store_fetch_reads() -> None: + local_store = _RecordingStore("local") + build_calls = 0 + + def build_store(_server_lookup: ServerLookup) -> tuple[InvalidatableOAuthTokenStore, bool]: + nonlocal build_calls + build_calls += 1 + return local_store, False + + def server_lookup(_server_id: str) -> None: + return None + + store = LazyPerUserOAuthTokenStore( + server_lookup, + store_builder=build_store, + redis_available=_RedisAvailability(), + ) + + await store.fetch("u", "s") + await store.invalidate("u", "s") + + assert build_calls == 1 + assert local_store.calls == [("u", "s")] + assert local_store.invalidations == [("u", "s")] + + +@pytest.mark.asyncio +async def test_lazy_store_invalidate_works_after_redis_chain_is_built() -> None: + redis_store = _RecordingStore("redis") + redis_available = _RedisAvailability() + redis_available.available = True + build_calls = 0 + + def build_store(_server_lookup: ServerLookup) -> tuple[InvalidatableOAuthTokenStore, bool]: + nonlocal build_calls + build_calls += 1 + return redis_store, True + + def server_lookup(_server_id: str) -> None: + return None + + store = LazyPerUserOAuthTokenStore( + server_lookup, + store_builder=build_store, + redis_available=redis_available, + ) + + await store.fetch("u", "s") + await store.invalidate("u", "s") + + assert build_calls == 1 + assert redis_store.invalidations == [("u", "s")] diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_resolver.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_resolver.py index 73e9a52b937..c88027abcd4 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_resolver.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_resolver.py @@ -1,9 +1,9 @@ """Tests for the resolver dispatch: live arms produce auth, stubbed arms fail closed. -`none`, `api_key` (shared-key source), and `authorization_code` are implemented; every other arm, -plus the `api_key` BYOK source, returns a typed `not_implemented` error until its mode lands. -Parametrizing the stubs over one config each also guards reachability: a dropped `case` would hit -`assert_never` and raise instead of returning the stub. +`none`, `api_key` (shared-key source), `passthrough`, `authorization_code`, and `token_exchange` are +implemented; every other arm, plus the `api_key` BYOK source, returns a typed `not_implemented` error +until its mode lands. Parametrizing the stubs over one config each also guards reachability: a dropped +`case` would hit `assert_never` and raise instead of returning the stub. """ import httpx @@ -16,11 +16,13 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials import ( AwsSigV4Config, Byok, ClientCredentialsConfig, + CredError, Error, NoneConfig, NoOpAuth, Ok, PassthroughConfig, + Result, ServerSpec, SharedKey, StaticHeaderAuth, @@ -37,9 +39,7 @@ _SUBJECT = Subject(tenant_id="", subject_id="") def _spec(config): - return ServerSpec( - server_id="s", resource="https://upstream.example.com", config=config - ) + return ServerSpec(server_id="s", resource="https://upstream.example.com", config=config) def _emitted(auth: httpx.Auth) -> httpx.Headers: @@ -52,9 +52,7 @@ def _emitted(auth: httpx.Auth) -> httpx.Headers: @pytest.mark.asyncio async def test_none_mode_yields_a_no_op_auth(): - result = await UpstreamCredentialProvider().resolve_credentials( - _SUBJECT, _spec(NoneConfig()) - ) + result = await UpstreamCredentialProvider().resolve_credentials(_SUBJECT, _spec(NoneConfig())) assert isinstance(result, Ok) assert isinstance(result.ok, NoOpAuth) @@ -66,9 +64,7 @@ async def test_api_key_shared_emits_the_configured_header(): value_prefix="", key_source=SharedKey(value=SecretStr("secret-key")), ) - result = await UpstreamCredentialProvider().resolve_credentials( - _SUBJECT, _spec(config) - ) + result = await UpstreamCredentialProvider().resolve_credentials(_SUBJECT, _spec(config)) assert isinstance(result, Ok) assert isinstance(result.ok, StaticHeaderAuth) assert _emitted(result.ok)["X-API-Key"] == "secret-key" @@ -81,9 +77,7 @@ async def test_api_key_shared_honors_authorization_scheme(): value_prefix="Bearer", key_source=SharedKey(value=SecretStr("tok")), ) - result = await UpstreamCredentialProvider().resolve_credentials( - _SUBJECT, _spec(config) - ) + result = await UpstreamCredentialProvider().resolve_credentials(_SUBJECT, _spec(config)) assert isinstance(result, Ok) assert _emitted(result.ok)["Authorization"] == "Bearer tok" @@ -101,9 +95,7 @@ class _FakeTokenStore: @pytest.mark.asyncio async def test_authorization_code_emits_bearer_for_a_stored_token(): store = _FakeTokenStore({("alice", "s"): OAuthToken(access_token="at-alice")}) - result = await UpstreamCredentialProvider( - oauth_token_store=store - ).resolve_credentials( + result = await UpstreamCredentialProvider(oauth_token_store=store).resolve_credentials( Subject(tenant_id="", subject_id="alice"), _spec(AuthorizationCodeConfig()) ) assert isinstance(result, Ok) @@ -112,9 +104,7 @@ async def test_authorization_code_emits_bearer_for_a_stored_token(): @pytest.mark.asyncio async def test_authorization_code_without_token_is_semantically_unauthorized(): - result = await UpstreamCredentialProvider( - oauth_token_store=_FakeTokenStore({}) - ).resolve_credentials( + result = await UpstreamCredentialProvider(oauth_token_store=_FakeTokenStore({})).resolve_credentials( Subject(tenant_id="", subject_id="alice"), _spec(AuthorizationCodeConfig()) ) assert isinstance(result, Error) @@ -131,9 +121,7 @@ async def test_authorization_code_store_unavailable_is_unauthorized(): async def fetch(self, user_id: str, server_id: str): raise TokenStoreUnavailable("down") - result = await UpstreamCredentialProvider( - oauth_token_store=_Unavailable() - ).resolve_credentials( + result = await UpstreamCredentialProvider(oauth_token_store=_Unavailable()).resolve_credentials( Subject(tenant_id="", subject_id="alice"), _spec(AuthorizationCodeConfig()) ) assert isinstance(result, Error) @@ -156,22 +144,38 @@ async def test_authorization_code_isolates_by_subject(): alice = await provider.resolve_credentials( Subject(tenant_id="", subject_id="alice"), _spec(AuthorizationCodeConfig()) ) - bob = await provider.resolve_credentials( - Subject(tenant_id="", subject_id="bob"), _spec(AuthorizationCodeConfig()) - ) - assert ( - isinstance(alice, Ok) - and _emitted(alice.ok)["Authorization"] == "Bearer at-alice" - ) + bob = await provider.resolve_credentials(Subject(tenant_id="", subject_id="bob"), _spec(AuthorizationCodeConfig())) + assert isinstance(alice, Ok) and _emitted(alice.ok)["Authorization"] == "Bearer at-alice" assert isinstance(bob, Error) and bob.error.tag == "unauthorized" +@pytest.mark.asyncio +async def test_authorization_code_isolates_by_server_id_even_when_servers_share_a_url(): + """A token stored for one server must be invisible to a different server_id pointing at the + same upstream URL: credentials bind to the server entry they were authorized for, so a + recreated or duplicated server starts unauthorized instead of inheriting the old grant. Guards + against any future token lookup keyed on the resource URL instead of (user_id, server_id) -- + both the egress resolve and the has_user_token discovery check must agree.""" + shared_url = "https://upstream.example.com" + store = _FakeTokenStore({("alice", "server-a"): OAuthToken(access_token="at-alice")}) + provider = UpstreamCredentialProvider(oauth_token_store=store) + subject = Subject(tenant_id="", subject_id="alice") + spec_a = ServerSpec(server_id="server-a", resource=shared_url, config=AuthorizationCodeConfig()) + spec_b = ServerSpec(server_id="server-b", resource=shared_url, config=AuthorizationCodeConfig()) + + granted = await provider.resolve_credentials(subject, spec_a) + fresh = await provider.resolve_credentials(subject, spec_b) + + assert isinstance(granted, Ok) and _emitted(granted.ok)["Authorization"] == "Bearer at-alice" + assert isinstance(fresh, Error) and fresh.error.tag == "unauthorized" + assert await provider.has_user_token(subject, spec_a) is True + assert await provider.has_user_token(subject, spec_b) is False + + @pytest.mark.asyncio async def test_has_user_token_reflects_the_stored_token(): present = UpstreamCredentialProvider( - oauth_token_store=_FakeTokenStore( - {("alice", "s"): OAuthToken(access_token="at")} - ) + oauth_token_store=_FakeTokenStore({("alice", "s"): OAuthToken(access_token="at")}) ) absent = UpstreamCredentialProvider(oauth_token_store=_FakeTokenStore({})) spec = _spec(AuthorizationCodeConfig()) @@ -185,17 +189,112 @@ async def test_has_user_token_false_for_a_non_per_user_mode(): # A none-mode server has no per-user token to check. provider = UpstreamCredentialProvider() spec = _spec(NoneConfig()) - assert ( - await provider.has_user_token(Subject(tenant_id="", subject_id="a"), spec) - is False + assert await provider.has_user_token(Subject(tenant_id="", subject_id="a"), spec) is False + + +class _FakeExchanger: + def __init__(self, result: Result[OAuthToken, CredError]) -> None: + self._result = result + self.calls: list[tuple[str, str, str]] = [] + self.invalidations: list[tuple[str, str, str]] = [] + + async def exchange(self, subject_token, server, config, *, tenant_id=""): + self.calls.append((subject_token, tenant_id, server.server_id)) + return self._result + + async def invalidate(self, subject_token, server, config, *, tenant_id=""): + self.invalidations.append((subject_token, tenant_id, server.server_id)) + + +_OBO = TokenExchangeConfig( + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret=SecretStr("csec"), +) + + +@pytest.mark.asyncio +async def test_token_exchange_emits_the_exchanged_bearer(): + exchanger = _FakeExchanger(Ok(OAuthToken(access_token="exchanged-at"))) + subject = Subject(tenant_id="acme", subject_id="alice", inbound_token=SecretStr("caller-jwt")) + result = await UpstreamCredentialProvider(token_exchanger=exchanger).resolve_credentials(subject, _spec(_OBO)) + assert isinstance(result, Ok) + assert _emitted(result.ok)["Authorization"] == "Bearer exchanged-at" + # The arm hands the unwrapped caller token AND the tenant to the exchanger, never the upstream. + assert exchanger.calls == [("caller-jwt", "acme", "s")] + + +@pytest.mark.asyncio +async def test_invalidate_credentials_drops_the_exchanged_token_for_the_subject_and_tenant(): + exchanger = _FakeExchanger(Ok(OAuthToken(access_token="exchanged-at"))) + provider = UpstreamCredentialProvider(token_exchanger=exchanger) + subject = Subject(tenant_id="acme", subject_id="alice", inbound_token=SecretStr("caller-jwt")) + await provider.invalidate_credentials(subject, _spec(_OBO)) + assert exchanger.invalidations == [("caller-jwt", "acme", "s")] + + +@pytest.mark.asyncio +async def test_invalidate_credentials_is_a_noop_without_a_caller_token(): + exchanger = _FakeExchanger(Ok(OAuthToken(access_token="never"))) + provider = UpstreamCredentialProvider(token_exchanger=exchanger) + await provider.invalidate_credentials(Subject(tenant_id="acme", subject_id="alice"), _spec(_OBO)) + assert exchanger.invalidations == [] + + +@pytest.mark.asyncio +async def test_token_exchange_without_caller_token_is_unauthorized(): + exchanger = _FakeExchanger(Ok(OAuthToken(access_token="never"))) + result = await UpstreamCredentialProvider(token_exchanger=exchanger).resolve_credentials( + Subject(tenant_id="", subject_id="alice"), _spec(_OBO) ) + assert isinstance(result, Error) + assert result.error.tag == "unauthorized" + assert result.error.unauthorized.www_authenticate == 'Bearer error="invalid_request"' + # No caller token means nothing to exchange: the IdP is never hit. + assert exchanger.calls == [] + + +@pytest.mark.asyncio +async def test_token_exchange_propagates_the_exchanger_error(): + err = CredError.of_upstream_unavailable("idp down") + result = await UpstreamCredentialProvider(token_exchanger=_FakeExchanger(Error(err))).resolve_credentials( + Subject(tenant_id="", subject_id="alice", inbound_token=SecretStr("jwt")), + _spec(_OBO), + ) + assert isinstance(result, Error) + assert result.error.tag == "upstream_unavailable" + + +@pytest.mark.asyncio +async def test_token_exchange_without_an_exchanger_fails_closed(): + # The fail-closed default (no exchanger wired) must not produce a credential. + result = await UpstreamCredentialProvider().resolve_credentials( + Subject(tenant_id="", subject_id="alice", inbound_token=SecretStr("jwt")), + _spec(_OBO), + ) + assert isinstance(result, Error) + assert result.error.tag == "misconfigured" + + +@pytest.mark.asyncio +async def test_passthrough_forwards_the_inbound_token_verbatim(): + subject = Subject(tenant_id="", subject_id="", inbound_token=SecretStr("Bearer upstream-xyz")) + result = await UpstreamCredentialProvider().resolve_credentials(subject, _spec(PassthroughConfig())) + assert isinstance(result, Ok) + assert isinstance(result.ok, StaticHeaderAuth) + assert _emitted(result.ok)["Authorization"] == "Bearer upstream-xyz" + + +@pytest.mark.asyncio +async def test_passthrough_without_inbound_token_is_a_no_op(): + result = await UpstreamCredentialProvider().resolve_credentials(_SUBJECT, _spec(PassthroughConfig())) + assert isinstance(result, Ok) + assert isinstance(result.ok, NoOpAuth) _STUBBED = [ ("api_key_byok", ApiKeyConfig(key_source=Byok())), - ("passthrough", PassthroughConfig()), ("client_credentials", ClientCredentialsConfig()), - ("token_exchange", TokenExchangeConfig()), ("aws_sigv4", AwsSigV4Config(region="us-east-1")), ] @@ -203,8 +302,6 @@ _STUBBED = [ @pytest.mark.asyncio @pytest.mark.parametrize("label, config", _STUBBED) async def test_unbuilt_arms_fail_closed_with_not_implemented(label, config): - result = await UpstreamCredentialProvider().resolve_credentials( - _SUBJECT, _spec(config) - ) + result = await UpstreamCredentialProvider().resolve_credentials(_SUBJECT, _spec(config)) assert isinstance(result, Error) assert result.error.tag == "not_implemented" diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_token_exchange_provider.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_token_exchange_provider.py new file mode 100644 index 00000000000..cf15fdb3e26 --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_token_exchange_provider.py @@ -0,0 +1,153 @@ +"""Tests for the token_exchange composition root: the built exchanger and the HTTP edge contract. + +`build_token_exchanger` wires the pure exchanger to its runtime edges; `_post_exchange_endpoint` is +the I/O edge that maps any transport/HTTP failure to None and parses a JSON body on success. +""" + +from unittest.mock import patch + +import pytest + +from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchange_provider import ( + _post_exchange_endpoint, + build_token_exchanger, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger import ( + OboTokenExchanger, + SubjectTokenRejected, + TokenExchangeClientError, +) + +_HTTP_CLIENT = "litellm.llms.custom_httpx.http_handler.get_async_httpx_client" + + +def _client_raising_4xx(body: object): + """An httpx client whose POST returns a 4xx whose ``raise_for_status`` raises an HTTPStatusError + carrying ``body`` as its JSON, so the RFC 6749 error-code classification can be driven.""" + import httpx + + request = httpx.Request("POST", "https://idp/token") + response = httpx.Response(400, json=body, request=request) + + class _Resp: + def raise_for_status(self) -> None: + raise httpx.HTTPStatusError("bad request", request=request, response=response) + + class _Client: + async def post(self, url, headers, data): + return _Resp() + + return _Client() + + +def test_build_token_exchanger_returns_an_exchanger(): + assert isinstance(build_token_exchanger(), OboTokenExchanger) + + +def test_build_gives_each_caller_an_independent_cache(): + # Separate builds must not share a cache, so one egress instance cannot serve another's tokens. + assert build_token_exchanger() is not build_token_exchanger() + + +@pytest.mark.asyncio +async def test_post_returns_none_on_transport_error(): + with patch(_HTTP_CLIENT, side_effect=RuntimeError("boom")): + result = await _post_exchange_endpoint("https://idp/token", {"grant_type": "x"}, {}) + assert result is None + + +@pytest.mark.asyncio +async def test_post_parses_json_body_on_success(): + class _Resp: + def raise_for_status(self) -> None: + return None + + def json(self) -> dict[str, object]: + return {"access_token": "x", "expires_in": 60} + + class _Client: + async def post(self, url, headers, data): + return _Resp() + + with patch(_HTTP_CLIENT, return_value=_Client()): + result = await _post_exchange_endpoint("https://idp/token", {"grant_type": "x"}, {}) + assert result == {"access_token": "x", "expires_in": 60} + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "code", ["invalid_client", "unauthorized_client", "unsupported_grant_type", "invalid_target", "invalid_scope"] +) +async def test_post_maps_gateway_fault_4xx_to_client_error(code): + # RFC 6749 5.2 gateway-fault codes must raise TokenExchangeClientError (-> 500), not the caller 401. + with patch(_HTTP_CLIENT, return_value=_client_raising_4xx({"error": code})): + with pytest.raises(TokenExchangeClientError): + await _post_exchange_endpoint("https://idp/token", {"grant_type": "x"}, {}) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "body", + [{"error": "invalid_grant"}, {"error": "invalid_request"}, {}, {"error": 123}, "not-json-object"], + ids=["invalid_grant", "invalid_request", "no_error", "non_str_error", "non_dict"], +) +async def test_post_maps_subject_fault_4xx_to_subject_rejected(body): + # A subject-fault code (or an unparseable/absent error) is the caller's problem -> SubjectTokenRejected (401). + with patch(_HTTP_CLIENT, return_value=_client_raising_4xx(body)): + with pytest.raises(SubjectTokenRejected): + await _post_exchange_endpoint("https://idp/token", {"grant_type": "x"}, {}) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("payload", [["a", "b"], "a-string", 42], ids=["list", "str", "int"]) +async def test_post_returns_none_on_non_object_json(payload): + # A valid-but-non-object JSON body must become a miss, not crash field parsing downstream. + class _Resp: + def raise_for_status(self) -> None: + return None + + def json(self) -> object: + return payload + + class _Client: + async def post(self, url, headers, data): + return _Resp() + + with patch(_HTTP_CLIENT, return_value=_Client()): + result = await _post_exchange_endpoint("https://idp/token", {"grant_type": "x"}, {}) + assert result is None + + +@pytest.mark.asyncio +async def test_post_threads_step_up_error_and_claims_into_subject_rejected(): + # Entra Conditional Access: the 4xx body's machine code and claims blob must ride on the + # rejection so the edge challenge can drive the client's step-up; error_description never does. + claims = '{"access_token":{"acrs":{"essential":true,"value":"c1"}}}' + body = { + "error": "interaction_required", + "error_description": "AADSTS50079: the user must enroll MFA", + "claims": claims, + } + with patch(_HTTP_CLIENT, return_value=_client_raising_4xx(body)): + with pytest.raises(SubjectTokenRejected) as exc_info: + await _post_exchange_endpoint("https://idp/token", {"grant_type": "x"}, {}) + assert exc_info.value.claims == claims + assert "AADSTS50079" not in str(exc_info.value) + + +@pytest.mark.asyncio +async def test_post_subject_rejection_without_claims_carries_none_claims(): + with patch(_HTTP_CLIENT, return_value=_client_raising_4xx({"error": "invalid_grant"})): + with pytest.raises(SubjectTokenRejected) as exc_info: + await _post_exchange_endpoint("https://idp/token", {"grant_type": "x"}, {}) + assert exc_info.value.claims is None + + +@pytest.mark.asyncio +async def test_post_gateway_fault_still_wins_when_claims_are_present(): + # A gateway-fault code stays a 500-class TokenExchangeClientError even if the body carries + # claims; the caller cannot fix invalid_client by stepping up. + body = {"error": "invalid_client", "claims": '{"access_token":{}}'} + with patch(_HTTP_CLIENT, return_value=_client_raising_4xx(body)): + with pytest.raises(TokenExchangeClientError): + await _post_exchange_endpoint("https://idp/token", {"grant_type": "x"}, {}) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_token_exchanger.py b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_token_exchanger.py new file mode 100644 index 00000000000..1fa394e1249 --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_token_exchanger.py @@ -0,0 +1,564 @@ +"""Tests for the pure RFC 8693 token exchanger: the OBO swap, caching, and single-flight. + +Drives `OboTokenExchanger` through an injected fake HTTP post and clock, so the exchange, the +form it sends, the per-caller-token cache, and the failure mapping are pinned without a live IdP. +""" + +import asyncio + +import pytest +from pydantic import SecretStr + +from litellm.proxy._experimental.mcp_server.outbound_credentials import ( + Error, + Ok, + ServerSpec, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger import ( + OboTokenExchanger, +) +from litellm.proxy._experimental.mcp_server.outbound_credentials.types import ( + TokenExchangeConfig, +) + +_GRANT = "urn:ietf:params:oauth:grant-type:token-exchange" + +_CONFIG = TokenExchangeConfig( + token_exchange_endpoint="https://idp.example.com/token", + audience="https://up.example.com", + client_id="cid", + client_secret=SecretStr("csec"), + scopes=("s1", "s2"), +) +_SERVER = ServerSpec(server_id="srv", resource="https://up.example.com", config=_CONFIG) + +_JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer" +# audience + a non-default subject_token_type are set deliberately: the entra_obo form must drop both. +_ENTRA_CONFIG = TokenExchangeConfig( + profile="entra_obo", + token_exchange_endpoint="https://login.microsoftonline.com/tid/oauth2/v2.0/token", + audience="ignored-in-entra-obo", + subject_token_type="urn:ietf:params:oauth:token-type:jwt", + client_id="cid", + client_secret=SecretStr("csec"), + scopes=("api://target-api/.default",), +) + + +class _Clock: + def __init__(self, now: float = 1000.0) -> None: + self.now = now + + def __call__(self) -> float: + return self.now + + +class _RecordingPost: + def __init__(self, body: dict[str, object] | None) -> None: + self._body = body + self.calls: list[tuple[str, dict[str, str]]] = [] + self.headers: list[dict[str, str]] = [] + + async def __call__(self, url: str, form: dict[str, str], headers: dict[str, str]) -> dict[str, object] | None: + self.calls.append((url, dict(form))) + self.headers.append(dict(headers)) + return self._body + + +def _spec(config: TokenExchangeConfig) -> ServerSpec: + return ServerSpec(server_id="srv", resource="https://up.example.com", config=config) + + +@pytest.mark.asyncio +async def test_exchange_emits_token_and_sends_rfc8693_form(): + post = _RecordingPost({"access_token": "exchanged", "expires_in": 3600}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("caller-jwt", _SERVER, _CONFIG) + assert isinstance(result, Ok) + assert result.ok.access_token == "exchanged" + url, form = post.calls[0] + assert url == "https://idp.example.com/token" + assert form == { + "grant_type": _GRANT, + "subject_token": "caller-jwt", + "subject_token_type": "urn:ietf:params:oauth:token-type:access_token", + "client_id": "cid", + "client_secret": "csec", + "audience": "https://up.example.com", + "scope": "s1 s2", + } + # client_secret_post is the default: creds in the body, no client-auth header + assert post.headers[0] == {} + + +@pytest.mark.asyncio +async def test_client_secret_basic_sends_authorization_header_and_omits_body_creds(): + import base64 + + config = TokenExchangeConfig( + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret=SecretStr("csec"), + token_endpoint_auth_method="client_secret_basic", + ) + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _spec(config), config) + assert isinstance(result, Ok) + _, form = post.calls[0] + assert "client_id" not in form and "client_secret" not in form + assert post.headers[0]["Authorization"] == "Basic " + base64.b64encode(b"cid:csec").decode() + + +@pytest.mark.asyncio +async def test_client_secret_post_keeps_creds_in_body_with_no_auth_header(): + config = TokenExchangeConfig( + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret=SecretStr("csec"), + token_endpoint_auth_method="client_secret_post", + ) + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _spec(config), config) + _, form = post.calls[0] + assert form["client_id"] == "cid" and form["client_secret"] == "csec" + assert "Authorization" not in post.headers[0] + + +@pytest.mark.asyncio +async def test_exchange_maps_idp_rejection_to_unauthorized(): + """An IdP 4xx (surfaced as SubjectTokenRejected by the post adapter) is non-retryable: it maps + to ``unauthorized`` (the 401 OBO challenge), not the retryable ``upstream_unavailable`` (503).""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger import ( + SubjectTokenRejected, + ) + + async def _rejecting_post(url, form, headers): + raise SubjectTokenRejected("IdP rejected the token exchange (HTTP 400)") + + result = await OboTokenExchanger(_rejecting_post, clock=_Clock()).exchange("bad-jwt", _SERVER, _CONFIG) + assert isinstance(result, Error) + assert result.error.tag == "unauthorized" + + +@pytest.mark.asyncio +async def test_exchange_maps_gateway_fault_to_misconfigured(): + """A gateway-fault RFC 6749 code (invalid_client / invalid_target / ..., surfaced as + TokenExchangeClientError) is the gateway's problem, not the caller's, so it maps to misconfigured + (500) rather than the retryable 503 or the 401 OBO challenge the caller can't act on.""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger import ( + TokenExchangeClientError, + ) + + async def _client_error_post(url, form, headers): + raise TokenExchangeClientError("invalid_client") + + result = await OboTokenExchanger(_client_error_post, clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Error) + assert result.error.tag == "misconfigured" + + +@pytest.mark.asyncio +async def test_exchange_maps_transport_failure_to_upstream_unavailable(): + """A post returning None (5xx / network / timeout / malformed body) stays retryable: 503.""" + result = await OboTokenExchanger(_RecordingPost(None), clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Error) + assert result.error.tag == "upstream_unavailable" + + +@pytest.mark.asyncio +async def test_exchange_caches_per_caller_token(): + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, clock=_Clock()) + await exchanger.exchange("jwt", _SERVER, _CONFIG) + second = await exchanger.exchange("jwt", _SERVER, _CONFIG) + assert isinstance(second, Ok) and second.ok.access_token == "x" + assert len(post.calls) == 1 + + +@pytest.mark.asyncio +async def test_rotated_caller_token_re_exchanges(): + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, clock=_Clock()) + await exchanger.exchange("jwt-1", _SERVER, _CONFIG) + await exchanger.exchange("jwt-2", _SERVER, _CONFIG) + assert len(post.calls) == 2 + + +@pytest.mark.asyncio +async def test_same_token_different_tenant_does_not_share_cache(): + # Two tenants presenting the same opaque token (e.g. a shared/service token) must not collide on + # one cache entry: tenant_id is part of the key, so each tenant gets its own exchange. + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, clock=_Clock()) + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="acme") + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="globex") + assert len(post.calls) == 2 + # Same tenant + token still hits the cache. + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="acme") + assert len(post.calls) == 2 + + +@pytest.mark.asyncio +async def test_invalidate_forces_re_exchange(): + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, clock=_Clock()) + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="acme") + await exchanger.invalidate("jwt", _SERVER, _CONFIG, tenant_id="acme") + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="acme") + assert len(post.calls) == 2 + + +@pytest.mark.asyncio +async def test_invalidate_targets_only_the_matching_tenant(): + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, clock=_Clock()) + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="acme") + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="globex") + await exchanger.invalidate("jwt", _SERVER, _CONFIG, tenant_id="acme") + # globex's entry survives; only acme re-exchanges. + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="globex") + await exchanger.exchange("jwt", _SERVER, _CONFIG, tenant_id="acme") + assert len(post.calls) == 3 + + +@pytest.mark.asyncio +async def test_rotated_config_re_exchanges_before_ttl(): + # Same caller token + server, but the operator rotated the audience/scope: the cached token was + # minted for the old config, so it must re-exchange (not serve the stale token) before TTL. + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, clock=_Clock()) + rotated = _CONFIG.model_copy(update={"audience": "https://new.example.com", "scopes": ("s3",)}) + await exchanger.exchange("jwt", _SERVER, _CONFIG) + await exchanger.exchange("jwt", _SERVER, rotated) + assert len(post.calls) == 2 + _, second_form = post.calls[1] + assert second_form["audience"] == "https://new.example.com" + assert second_form["scope"] == "s3" + + +@pytest.mark.asyncio +async def test_rotated_token_endpoint_auth_method_re_exchanges_before_ttl(): + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, clock=_Clock()) + rotated = _CONFIG.model_copy(update={"token_endpoint_auth_method": "client_secret_basic"}) + await exchanger.exchange("jwt", _SERVER, _CONFIG) + await exchanger.exchange("jwt", _SERVER, rotated) + assert len(post.calls) == 2 + _, second_form = post.calls[1] + assert "client_id" not in second_form + assert "client_secret" not in second_form + assert "Authorization" in post.headers[1] + + +@pytest.mark.asyncio +async def test_concurrent_callers_single_flight_one_exchange(): + release = asyncio.Event() + + class _Blocking: + def __init__(self) -> None: + self.calls = 0 + + async def __call__(self, url, form, headers): + self.calls += 1 + await release.wait() + return {"access_token": "x", "expires_in": 3600} + + post = _Blocking() + exchanger = OboTokenExchanger(post, clock=_Clock()) + first = asyncio.create_task(exchanger.exchange("jwt", _SERVER, _CONFIG)) + second = asyncio.create_task(exchanger.exchange("jwt", _SERVER, _CONFIG)) + await asyncio.sleep(0.02) + release.set() + r1, r2 = await asyncio.gather(first, second) + assert post.calls == 1 + assert isinstance(r1, Ok) and isinstance(r2, Ok) + + +@pytest.mark.asyncio +async def test_idp_failure_is_upstream_unavailable(): + result = await OboTokenExchanger(_RecordingPost(None), clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Error) + assert result.error.tag == "upstream_unavailable" + + +@pytest.mark.asyncio +async def test_missing_access_token_is_upstream_unavailable(): + post = _RecordingPost({"token_type": "Bearer"}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Error) + assert result.error.tag == "upstream_unavailable" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("token_type", ["N_A", "n_a", "DPoP", "mac"]) +async def test_non_bearer_token_type_is_refused(token_type): + # RFC 8693 2.2.1: the resolver forwards the exchanged token as `Bearer`. A non-Bearer token_type + # (e.g. N_A = not a standalone access token) must fail closed, not be minted as a bogus Bearer. + post = _RecordingPost({"access_token": "x", "token_type": token_type, "expires_in": 3600}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Error) + assert result.error.tag == "upstream_unavailable" + + +@pytest.mark.asyncio +async def test_non_bearer_token_type_is_logged(): + from unittest.mock import patch + + post = _RecordingPost({"access_token": "x", "token_type": "N_A", "expires_in": 3600}) + with patch( + "litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger.verbose_logger" + ) as mock_logger: + await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert mock_logger.warning.called + assert "N_A" in repr(mock_logger.warning.call_args) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("token_type", ["Bearer", "bearer", "BEARER"]) +async def test_bearer_token_type_is_accepted_case_insensitively(token_type): + post = _RecordingPost({"access_token": "x", "token_type": token_type, "expires_in": 3600}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Ok) and result.ok.access_token == "x" + + +@pytest.mark.asyncio +async def test_absent_token_type_defaults_to_bearer(): + # Many IdPs omit token_type; absence must not fail the exchange (RFC 6750 default is Bearer). + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Ok) and result.ok.access_token == "x" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "issued_token_type", + [ + "urn:ietf:params:oauth:token-type:refresh_token", + "urn:ietf:params:oauth:token-type:id_token", + "urn:ietf:params:oauth:token-type:saml2", + ], +) +async def test_non_access_issued_token_type_is_refused_even_when_bearer(issued_token_type): + # A malformed STS could mint a refresh/id/saml token but label it Bearer; issued_token_type must + # still fail it closed rather than forward a non-access token as an upstream access credential. + post = _RecordingPost( + {"access_token": "x", "token_type": "Bearer", "issued_token_type": issued_token_type, "expires_in": 3600} + ) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Error) + assert result.error.tag == "upstream_unavailable" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "issued_token_type", + ["urn:ietf:params:oauth:token-type:access_token", "urn:ietf:params:oauth:token-type:jwt", "custom-unknown", None], +) +async def test_access_or_unknown_issued_token_type_is_accepted(issued_token_type): + # access_token / jwt are usable; an absent or unrecognized type is accepted (lenient), so real + # IdPs that omit issued_token_type or use a custom URN keep working. + body: dict[str, object] = {"access_token": "x", "token_type": "Bearer", "expires_in": 3600} + if issued_token_type is not None: + body["issued_token_type"] = issued_token_type + result = await OboTokenExchanger(_RecordingPost(body), clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Ok) and result.ok.access_token == "x" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "config", + [ + TokenExchangeConfig(token_exchange_endpoint="https://idp/token", client_secret=SecretStr("s")), + TokenExchangeConfig(token_exchange_endpoint="https://idp/token", client_id="c"), + ], +) +async def test_incomplete_config_is_misconfigured_without_hitting_idp(config): + post = _RecordingPost({"access_token": "x"}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _spec(config), config) + assert isinstance(result, Error) + assert result.error.tag == "misconfigured" + assert post.calls == [] + + +@pytest.mark.asyncio +async def test_missing_endpoint_is_precondition_required_without_hitting_idp(): + # No endpoint configured (and none discoverable): fail closed with a 412-mapped precondition + # rather than guessing an IdP or falling back. The subject token is never POSTed anywhere. + config = TokenExchangeConfig(client_id="c", client_secret=SecretStr("s")) + post = _RecordingPost({"access_token": "x"}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _spec(config), config) + assert isinstance(result, Error) + assert result.error.tag == "precondition_required" + assert post.calls == [] + + +@pytest.mark.asyncio +async def test_cached_token_expires_after_its_ttl(): + clock = _Clock(1000.0) + # expires_in=120, buffer=60 -> ttl 60 -> cached until t=1060. + post = _RecordingPost({"access_token": "x", "expires_in": 120}) + exchanger = OboTokenExchanger(post, clock=clock) + await exchanger.exchange("jwt", _SERVER, _CONFIG) + clock.now = 1059.0 + await exchanger.exchange("jwt", _SERVER, _CONFIG) + assert len(post.calls) == 1 + clock.now = 1061.0 + await exchanger.exchange("jwt", _SERVER, _CONFIG) + assert len(post.calls) == 2 + + +@pytest.mark.asyncio +async def test_audience_and_scope_omitted_when_unset(): + config = TokenExchangeConfig( + token_exchange_endpoint="https://idp/token", + client_id="cid", + client_secret=SecretStr("csec"), + ) + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _spec(config), config) + _, form = post.calls[0] + assert "audience" not in form + assert "scope" not in form + + +@pytest.mark.asyncio +@pytest.mark.parametrize("expires_in", ["120", 120.0, "120.0"], ids=["str", "float", "str_float"]) +async def test_numeric_expires_in_is_honored(expires_in): + # A JSON int/float/numeric-string expires_in must drive the TTL, not fall back to the default. + clock = _Clock(1000.0) + post = _RecordingPost({"access_token": "x", "expires_in": expires_in}) + exchanger = OboTokenExchanger(post, clock=clock) # ttl = max(120-60, 10) = 60 -> until 1060 + await exchanger.exchange("jwt", _SERVER, _CONFIG) + clock.now = 1061.0 + await exchanger.exchange("jwt", _SERVER, _CONFIG) + assert len(post.calls) == 2 + + +@pytest.mark.asyncio +async def test_short_lived_token_is_not_cached_past_its_expiry(): + # expires_in (5s) below the buffer/min floor must NOT be served stale: cache only until expiry. + clock = _Clock(1000.0) + post = _RecordingPost({"access_token": "x", "expires_in": 5}) + exchanger = OboTokenExchanger(post, clock=clock) + await exchanger.exchange("jwt", _SERVER, _CONFIG) + clock.now = 1004.0 # within the 5s lifetime -> still cached + await exchanger.exchange("jwt", _SERVER, _CONFIG) + assert len(post.calls) == 1 + clock.now = 1006.0 # past expiry -> re-exchange, not a stale bearer + await exchanger.exchange("jwt", _SERVER, _CONFIG) + assert len(post.calls) == 2 + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "body", + [ + {"access_token": "x"}, + {"access_token": "x", "expires_in": "not-a-number"}, + {"access_token": "x", "expires_in": True}, + ], + ids=["missing", "unparseable", "bool"], +) +async def test_unusable_expires_in_falls_back_to_default_ttl(body): + clock = _Clock(1000.0) + post = _RecordingPost(body) + exchanger = OboTokenExchanger(post, clock=clock, default_ttl_seconds=300.0) + await exchanger.exchange("jwt", _SERVER, _CONFIG) + clock.now = 1299.0 + await exchanger.exchange("jwt", _SERVER, _CONFIG) + assert len(post.calls) == 1 + + +@pytest.mark.asyncio +async def test_entra_obo_emits_rfc7523_jwt_bearer_form(): + # Microsoft Entra OBO is the RFC 7523 jwt-bearer grant, not RFC 8693: the inbound token is the + # `assertion`, the target rides in `scope`, and `requested_token_use=on_behalf_of` is required. + # subject_token / subject_token_type / audience must NOT appear even though the config carries them. + post = _RecordingPost({"access_token": "minted", "expires_in": 3600}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange( + "caller-entra-jwt", _spec(_ENTRA_CONFIG), _ENTRA_CONFIG + ) + assert isinstance(result, Ok) + assert result.ok.access_token == "minted" + url, form = post.calls[0] + assert url == "https://login.microsoftonline.com/tid/oauth2/v2.0/token" + assert form == { + "grant_type": _JWT_BEARER_GRANT, + "assertion": "caller-entra-jwt", + "client_id": "cid", + "client_secret": "csec", + "scope": "api://target-api/.default", + "requested_token_use": "on_behalf_of", + } + + +@pytest.mark.asyncio +async def test_entra_obo_without_scope_is_misconfigured_without_hitting_idp(): + # Entra carries the target resource in `scope`; with none, fail closed as misconfigured and never + # POST to the IdP (no fall-through to a weaker source). + config = TokenExchangeConfig( + profile="entra_obo", + token_exchange_endpoint="https://login.microsoftonline.com/tid/oauth2/v2.0/token", + client_id="cid", + client_secret=SecretStr("csec"), + ) + post = _RecordingPost({"access_token": "x"}) + result = await OboTokenExchanger(post, clock=_Clock()).exchange("jwt", _spec(config), config) + assert isinstance(result, Error) + assert result.error.tag == "misconfigured" + assert post.calls == [] + + +@pytest.mark.asyncio +async def test_profile_is_part_of_the_cache_key(): + # Same caller token, tenant, endpoint, creds, and scopes; only the profile differs. The two dialects + # mint different upstream tokens, so they must not collide on one cache entry. + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, clock=_Clock()) + shared = dict( + token_exchange_endpoint="https://idp/token", + client_id="cid", + client_secret=SecretStr("csec"), + scopes=("api://target/.default",), + ) + rfc8693 = TokenExchangeConfig(profile="rfc8693", **shared) + entra = TokenExchangeConfig(profile="entra_obo", **shared) + await exchanger.exchange("jwt", _spec(rfc8693), rfc8693) + await exchanger.exchange("jwt", _spec(entra), entra) + assert len(post.calls) == 2 + + +@pytest.mark.asyncio +async def test_distributed_coordinator_refresh_and_reread_use_the_cache(): + # Mimics the cross-replica coordinator contract: the winner's refresh populates the cache, a + # re-entrant refresh sees the fresh entry, and a loser reads it back via reread, all without a + # second IdP call. + class _ReplayCoordinator: + async def run(self, user_id, server_id, refresh, reread): + first = await refresh() + second = await refresh() + via_reread = await reread() + assert first is not None and second is not None and via_reread is not None + return via_reread + + post = _RecordingPost({"access_token": "x", "expires_in": 3600}) + exchanger = OboTokenExchanger(post, coordinator=_ReplayCoordinator(), clock=_Clock()) + result = await exchanger.exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Ok) and result.ok.access_token == "x" + assert len(post.calls) == 1 + + +@pytest.mark.asyncio +async def test_step_up_rejection_threads_claims_into_unauthorized(): + from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger import ( + SubjectTokenRejected, + ) + + claims = '{"access_token":{"acrs":{"essential":true,"value":"c1"}}}' + + async def _step_up_post(url, form, headers): + raise SubjectTokenRejected("IdP rejected the subject token (HTTP 400)", claims=claims) + + result = await OboTokenExchanger(_step_up_post, clock=_Clock()).exchange("jwt", _SERVER, _CONFIG) + assert isinstance(result, Error) + assert result.error.tag == "unauthorized" + assert result.error.unauthorized.claims == claims diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py index 7c9f5216d59..7269774442b 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_db_credentials.py @@ -11,12 +11,14 @@ keeps a plain-base64 fallback on read so existing rows continue to work. import base64 import json from datetime import datetime, timedelta, timezone +from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock import pytest from litellm.proxy._experimental.mcp_server.db import ( _decode_user_credential, + _prepare_mcp_server_data, get_user_credential, get_user_oauth_credential, is_oauth_credential_expired, @@ -27,10 +29,12 @@ from litellm.proxy._experimental.mcp_server.db import ( store_user_credential, store_user_oauth_credential, ) +from litellm.proxy._types import NewMCPServerRequest, UpdateMCPServerRequest from litellm.proxy.common_utils.encrypt_decrypt_utils import ( decrypt_value_helper, encrypt_value_helper, ) +from litellm.types.mcp import MCPAuth, MCPTransport SALT_KEY = "test-salt-key-for-byok-credential-tests-1234" @@ -60,6 +64,264 @@ def _legacy_row(payload: str): return row +def _identity_server(**overrides): + base = dict( + url="https://up.example.com/mcp", + auth_type="oauth2", + oauth2_flow="authorization_code", + authorization_url="https://idp.example.com/authorize", + token_url="https://idp.example.com/token", + registration_url="https://idp.example.com/register", + credentials={"client_id": "cid", "client_secret": "csec", "scopes": ["a"]}, + server_name="srv", + description="d", + ) + base.update(overrides) + return SimpleNamespace(**base) + + +@pytest.mark.parametrize( + "overrides", + [ + {"url": "https://other.example.com/mcp"}, + {"spec_path": "https://up.example.com/openapi.json"}, + {"auth_type": "oauth_delegate"}, + {"oauth2_flow": "client_credentials"}, + {"authorization_url": "https://other.example.com/authorize"}, + {"token_url": "https://other.example.com/token"}, + {"registration_url": "https://other.example.com/register"}, + {"credentials": {"client_id": "new", "client_secret": "csec", "scopes": ["a"]}}, + {"credentials": {"client_id": "cid", "client_secret": "rotated", "scopes": ["a"]}}, + {"credentials": {"client_id": "cid", "client_secret": "csec", "scopes": ["b"]}}, + ], +) +def test_mcp_oauth_token_identity_changes_on_mint_relevant_fields(overrides): + from litellm.proxy._experimental.mcp_server.db import mcp_oauth_token_identity + + assert mcp_oauth_token_identity(_identity_server()) != mcp_oauth_token_identity(_identity_server(**overrides)) + + +@pytest.mark.parametrize( + "overrides", + [ + {"server_name": "renamed"}, + {"description": "changed"}, + ], +) +def test_mcp_oauth_token_identity_stable_on_non_mint_fields(overrides): + from litellm.proxy._experimental.mcp_server.db import mcp_oauth_token_identity + + assert mcp_oauth_token_identity(_identity_server()) == mcp_oauth_token_identity(_identity_server(**overrides)) + + +def _encrypted_creds_json(client_id: str = "cid", client_secret: str = "csec") -> str: + from litellm.proxy._experimental.mcp_server.db import encrypt_credentials + + encrypted = encrypt_credentials( + credentials={"client_id": client_id, "client_secret": client_secret, "scopes": ["a"]}, + encryption_key=None, + ) + return json.dumps(encrypted) + + +def test_mcp_oauth_token_identity_stable_across_reencryption(): + """Stored client_id/client_secret are NaCl-encrypted with a fresh nonce on every write, so two + saves of the SAME plaintext produce different ciphertext. The identity must compare decrypted + values; comparing ciphertext would flag every routine save as a mint-relevant change and purge + per-user tokens that are still valid.""" + from litellm.proxy._experimental.mcp_server.db import mcp_oauth_token_identity + + first = _encrypted_creds_json() + second = _encrypted_creds_json() + assert first != second + + assert mcp_oauth_token_identity(_identity_server(credentials=first)) == mcp_oauth_token_identity( + _identity_server(credentials=second) + ) + + +def test_mcp_oauth_token_identity_detects_change_under_encryption(): + from litellm.proxy._experimental.mcp_server.db import mcp_oauth_token_identity + + unchanged = _identity_server(credentials=_encrypted_creds_json()) + changed = _identity_server(credentials=_encrypted_creds_json(client_id="other")) + assert mcp_oauth_token_identity(unchanged) != mcp_oauth_token_identity(changed) + + +def _oauth_row(user_id: str, server_id: str = "srv-1"): + """A stored per-user OAuth token row (payload tagged type=oauth2, legacy plain-base64 encoding).""" + row = _legacy_row(json.dumps({"type": "oauth2", "access_token": "tok-" + user_id})) + row.user_id = user_id + row.server_id = server_id + return row + + +def _byok_row(user_id: str, server_id: str = "srv-1"): + """A stored BYOK API key row: the same column, but the payload is a plain string, not OAuth JSON.""" + row = _legacy_row("sk-byok-" + user_id) + row.user_id = user_id + row.server_id = server_id + return row + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_invalidates_each_user(): + """The purge must route each (user, server) row through the invalidator exactly once.""" + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice"), _oauth_row("bob")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) + + invalidations = [] + + async def record_invalidation(user_id: str, server_id: str) -> None: + invalidations.append((user_id, server_id)) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=record_invalidation) + + assert purged == 2 + prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once_with( + where={"server_id": "srv-1", "user_id": {"in": ["alice", "bob"]}} + ) + assert set(invalidations) == {("alice", "srv-1"), ("bob", "srv-1")} + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_spares_byok_rows(): + """Regression: the purge used to delete_many on server_id alone, wiping BYOK API keys that share + the LiteLLM_MCPUserCredentials table. Only rows holding an OAuth2 payload may be deleted (one + batched query filtered to their user_ids), and only their users' token caches invalidated.""" + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_byok_row("carol"), _oauth_row("alice")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=1) + + invalidations = [] + + async def record_invalidation(user_id: str, server_id: str) -> None: + invalidations.append((user_id, server_id)) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=record_invalidation) + + assert purged == 1 + prisma.db.litellm_mcpusercredentials.delete_many.assert_awaited_once_with( + where={"server_id": "srv-1", "user_id": {"in": ["alice"]}} + ) + assert invalidations == [("alice", "srv-1")] + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_all_byok_is_noop(): + """An api_key (BYOK-only) server whose identity tuple changes (e.g. its url) must purge nothing.""" + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_byok_row("carol"), _byok_row("dave")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock() + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + + assert purged == 0 + prisma.db.litellm_mcpusercredentials.delete_many.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_defaults_to_manager_invalidator(monkeypatch): + """When no invalidator is injected, the purge must resolve to the manager's shared + invalidate_user_oauth_token_cache, the single point covering both the legacy per-user token cache + and the v2 per-user OAuth token store; a wrong or no-op default silently leaves every cache + serving tokens minted for the superseded config.""" + from litellm.proxy._experimental.mcp_server import mcp_server_manager + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=1) + + shared_invalidator = AsyncMock() + monkeypatch.setattr( + mcp_server_manager.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + shared_invalidator, + ) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + + assert purged == 1 + shared_invalidator.assert_awaited_once_with("alice", "srv-1") + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_logs_raced_rows(monkeypatch): + from litellm.proxy._experimental.mcp_server import db as db_module + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=0) + warning = MagicMock() + monkeypatch.setattr(db_module.verbose_proxy_logger, "warning", warning) + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1", invalidate_token_cache=AsyncMock()) + + assert purged == 0 + warning.assert_called_once() + + +@pytest.mark.asyncio +async def test_delete_mcp_server_invalidates_cached_tokens_for_enumerated_users(): + """Deleting a server must invalidate each enumerated user's cached per-user token: the caches are + keyed by (user_id, server_id), so a re-created server reusing the same server_id would otherwise + serve tokens minted for the deleted server until TTL.""" + from litellm.proxy._experimental.mcp_server.db import delete_mcp_server + + prisma = MagicMock() + prisma.db.litellm_mcpservertable.delete = AsyncMock(return_value=MagicMock(server_id="srv-1")) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[_oauth_row("alice"), _byok_row("bob")]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock(return_value=2) + prisma.db.litellm_mcpuserenvvars.delete_many = AsyncMock(return_value=0) + + invalidations = [] + + async def record_invalidation(user_id: str, server_id: str) -> None: + invalidations.append((user_id, server_id)) + + deleted = await delete_mcp_server(prisma, "srv-1", invalidate_token_cache=record_invalidation) + + assert deleted is not None + assert set(invalidations) == {("alice", "srv-1"), ("bob", "srv-1")} + + +@pytest.mark.asyncio +async def test_delete_mcp_server_returns_none_without_cleanup_when_server_missing(): + from litellm.proxy._experimental.mcp_server.db import delete_mcp_server + + prisma = MagicMock() + prisma.db.litellm_mcpservertable.delete = AsyncMock(return_value=None) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock() + + deleted = await delete_mcp_server(prisma, "srv-1", invalidate_token_cache=AsyncMock()) + + assert deleted is None + prisma.db.litellm_mcpusercredentials.find_many.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_purge_user_oauth_credentials_for_server_noop_when_empty(): + from litellm.proxy._experimental.mcp_server.db import purge_user_oauth_credentials_for_server + + prisma = MagicMock() + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[]) + prisma.db.litellm_mcpusercredentials.delete_many = AsyncMock() + + purged = await purge_user_oauth_credentials_for_server(prisma, "srv-1") + + assert purged == 0 + prisma.db.litellm_mcpusercredentials.delete_many.assert_not_awaited() + + def _stored_value(prisma) -> str: """Pull the credential_b64 value passed to the most recent upsert call.""" call = prisma.db.litellm_mcpusercredentials.upsert.call_args @@ -133,9 +395,7 @@ async def test_store_user_oauth_credential_does_not_persist_plaintext(): access_token = "ya29.a0AfH6SMBverysecretaccesstoken" prisma = _make_prisma_with_existing(row=None) - await store_user_oauth_credential( - prisma, "alice", "srv-1", access_token, refresh_token="rfr-xyz" - ) + await store_user_oauth_credential(prisma, "alice", "srv-1", access_token, refresh_token="rfr-xyz") stored = _stored_value(prisma) try: @@ -218,9 +478,7 @@ async def test_byok_guard_rejects_overwriting_encrypted_byok(): encrypted_row = MagicMock() encrypted_row.credential_b64 = _stored_value(prisma) - prisma.db.litellm_mcpusercredentials.find_unique = AsyncMock( - return_value=encrypted_row - ) + prisma.db.litellm_mcpusercredentials.find_unique = AsyncMock(return_value=encrypted_row) with pytest.raises(ValueError, match="could not be verified as an OAuth2"): await store_user_oauth_credential(prisma, "alice", "srv-1", "tok") @@ -262,18 +520,14 @@ async def test_list_oauth_credentials_filters_byok_and_returns_payloads(): "connected_at": "2024-01-01T00:00:00Z", } legacy_row = MagicMock() - legacy_row.credential_b64 = base64.urlsafe_b64encode( - json.dumps(legacy_payload).encode() - ).decode() + legacy_row.credential_b64 = base64.urlsafe_b64encode(json.dumps(legacy_payload).encode()).decode() legacy_row.server_id = "srv-legacy" byok_row = MagicMock() byok_row.credential_b64 = base64.urlsafe_b64encode(b"plain-byok-key").decode() byok_row.server_id = "srv-byok" - prisma.db.litellm_mcpusercredentials.find_many = AsyncMock( - return_value=[encrypted_row, legacy_row, byok_row] - ) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[encrypted_row, legacy_row, byok_row]) results = await list_user_oauth_credentials(prisma, "alice") @@ -323,9 +577,7 @@ async def test_rotate_re_encrypts_byok_with_new_key(monkeypatch): prisma.db.litellm_mcpusercredentials.update = AsyncMock() new_master_key = "rotated-salt-key-9999-9999-9999-9999" - await rotate_mcp_user_credentials_master_key( - prisma_client=prisma, new_master_key=new_master_key - ) + await rotate_mcp_user_credentials_master_key(prisma_client=prisma, new_master_key=new_master_key) update_call = prisma.db.litellm_mcpusercredentials.update.call_args new_stored = update_call.kwargs["data"]["credential_b64"] @@ -353,19 +605,13 @@ async def test_rotate_migrates_legacy_plaintext_rows(monkeypatch): legacy_row.user_id = "alice" legacy_row.server_id = "srv-legacy" legacy_row.credential_b64 = base64.urlsafe_b64encode(b"legacy-plain").decode() - prisma.db.litellm_mcpusercredentials.find_many = AsyncMock( - return_value=[legacy_row] - ) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[legacy_row]) prisma.db.litellm_mcpusercredentials.update = AsyncMock() new_key = "another-rotation-key-aaaa-bbbb-cccc-dddd" - await rotate_mcp_user_credentials_master_key( - prisma_client=prisma, new_master_key=new_key - ) + await rotate_mcp_user_credentials_master_key(prisma_client=prisma, new_master_key=new_key) - new_stored = prisma.db.litellm_mcpusercredentials.update.call_args.kwargs["data"][ - "credential_b64" - ] + new_stored = prisma.db.litellm_mcpusercredentials.update.call_args.kwargs["data"]["credential_b64"] monkeypatch.setenv("LITELLM_SALT_KEY", new_key) assert ( decrypt_value_helper( @@ -393,14 +639,10 @@ async def test_rotate_skips_undecodable_rows(): good_row.server_id = "srv-ok" good_row.credential_b64 = base64.urlsafe_b64encode(b"good-byok").decode() - prisma.db.litellm_mcpusercredentials.find_many = AsyncMock( - return_value=[bad_row, good_row] - ) + prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=[bad_row, good_row]) prisma.db.litellm_mcpusercredentials.update = AsyncMock() - await rotate_mcp_user_credentials_master_key( - prisma_client=prisma, new_master_key="new-key-xxxx" - ) + await rotate_mcp_user_credentials_master_key(prisma_client=prisma, new_master_key="new-key-xxxx") # Only one update call — the good row. assert prisma.db.litellm_mcpusercredentials.update.call_count == 1 @@ -416,9 +658,7 @@ def _oauth_cred(access_token="at-live", refresh_token=None, expires_in_seconds=N if refresh_token is not None: cred["refresh_token"] = refresh_token if expires_in_seconds is not None: - cred["expires_at"] = ( - datetime.now(timezone.utc) + timedelta(seconds=expires_in_seconds) - ).isoformat() + cred["expires_at"] = (datetime.now(timezone.utc) + timedelta(seconds=expires_in_seconds)).isoformat() return cred @@ -435,12 +675,7 @@ def test_expiry_buffer_treats_soon_to_expire_as_expired(): cred = _oauth_cred(expires_in_seconds=30) assert is_oauth_credential_expired(cred, buffer_seconds=60) is True # A token comfortably beyond the buffer stays valid. - assert ( - is_oauth_credential_expired( - _oauth_cred(expires_in_seconds=600), buffer_seconds=60 - ) - is False - ) + assert is_oauth_credential_expired(_oauth_cred(expires_in_seconds=600), buffer_seconds=60) is False def test_expiry_past_is_expired_regardless_of_buffer(): @@ -462,9 +697,7 @@ async def test_resolve_returns_valid_token_without_refreshing(monkeypatch): refresh = AsyncMock() monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) - cred = _oauth_cred( - access_token="at-live", refresh_token="rt-1", expires_in_seconds=600 - ) + cred = _oauth_cred(access_token="at-live", refresh_token="rt-1", expires_in_seconds=600) result = await resolve_valid_user_oauth_token( user_id="alice", server=MagicMock(), cred=cred, prisma_client=MagicMock() ) @@ -480,15 +713,11 @@ async def test_resolve_refreshes_expired_token_with_refresh_token(monkeypatch): # new token rather than returning None (which left the UI tool list empty). import litellm.proxy._experimental.mcp_server.db as db_mod - refreshed = _oauth_cred( - access_token="at-fresh", refresh_token="rt-2", expires_in_seconds=3600 - ) + refreshed = _oauth_cred(access_token="at-fresh", refresh_token="rt-2", expires_in_seconds=3600) refresh = AsyncMock(return_value=refreshed) monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) - expired = _oauth_cred( - access_token="at-dead", refresh_token="rt-1", expires_in_seconds=-5 - ) + expired = _oauth_cred(access_token="at-dead", refresh_token="rt-1", expires_in_seconds=-5) result = await resolve_valid_user_oauth_token( user_id="alice", server=MagicMock(), cred=expired, prisma_client=MagicMock() ) @@ -507,9 +736,7 @@ async def test_resolve_refreshes_token_expiring_within_buffer(monkeypatch): refresh = AsyncMock(return_value=refreshed) monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) - soon = _oauth_cred( - access_token="at-soon", refresh_token="rt-1", expires_in_seconds=30 - ) + soon = _oauth_cred(access_token="at-soon", refresh_token="rt-1", expires_in_seconds=30) result = await resolve_valid_user_oauth_token( user_id="alice", server=MagicMock(), cred=soon, prisma_client=MagicMock() ) @@ -543,9 +770,7 @@ async def test_resolve_returns_none_when_refresh_fails(monkeypatch): refresh = AsyncMock(return_value=None) monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) - expired = _oauth_cred( - access_token="at-dead", refresh_token="rt-1", expires_in_seconds=-5 - ) + expired = _oauth_cred(access_token="at-dead", refresh_token="rt-1", expires_in_seconds=-5) result = await resolve_valid_user_oauth_token( user_id="alice", server=MagicMock(), cred=expired, prisma_client=MagicMock() ) @@ -562,9 +787,7 @@ async def test_resolve_returns_none_for_missing_credential(monkeypatch): monkeypatch.setattr(db_mod, "refresh_user_oauth_token", refresh) assert ( - await resolve_valid_user_oauth_token( - user_id="alice", server=MagicMock(), cred=None, prisma_client=MagicMock() - ) + await resolve_valid_user_oauth_token(user_id="alice", server=MagicMock(), cred=None, prisma_client=MagicMock()) is None ) assert ( @@ -598,19 +821,13 @@ async def test_rotate_user_env_vars_re_encrypts_with_new_key(monkeypatch): encrypted_old = encrypt_value_helper(json.dumps(values)) prisma = MagicMock() - prisma.db.litellm_mcpuserenvvars.find_many = AsyncMock( - return_value=[_env_var_row(encrypted_old)] - ) + prisma.db.litellm_mcpuserenvvars.find_many = AsyncMock(return_value=[_env_var_row(encrypted_old)]) prisma.db.litellm_mcpuserenvvars.update = AsyncMock() new_master_key = "rotated-env-key-1111-2222-3333-4444" - await rotate_mcp_user_env_vars_master_key( - prisma_client=prisma, new_master_key=new_master_key - ) + await rotate_mcp_user_env_vars_master_key(prisma_client=prisma, new_master_key=new_master_key) - new_stored = prisma.db.litellm_mcpuserenvvars.update.call_args.kwargs["data"][ - "values_b64" - ] + new_stored = prisma.db.litellm_mcpuserenvvars.update.call_args.kwargs["data"]["values_b64"] assert new_stored != encrypted_old, "rotation must produce different ciphertext" monkeypatch.setenv("LITELLM_SALT_KEY", new_master_key) @@ -627,18 +844,14 @@ async def test_rotate_user_env_vars_re_encrypts_with_new_key(monkeypatch): async def test_rotate_user_env_vars_skips_undecryptable_rows(): # A corrupt row must be skipped (not overwritten) so recoverable data is # preserved and one bad row does not abort the rest of the rotation. - good = _env_var_row( - encrypt_value_helper(json.dumps({"A": "1"})), server_id="srv-ok" - ) + good = _env_var_row(encrypt_value_helper(json.dumps({"A": "1"})), server_id="srv-ok") bad = _env_var_row("!!! not encrypted !!!", server_id="srv-corrupt") prisma = MagicMock() prisma.db.litellm_mcpuserenvvars.find_many = AsyncMock(return_value=[bad, good]) prisma.db.litellm_mcpuserenvvars.update = AsyncMock() - await rotate_mcp_user_env_vars_master_key( - prisma_client=prisma, new_master_key="new-key-xxxx" - ) + await rotate_mcp_user_env_vars_master_key(prisma_client=prisma, new_master_key="new-key-xxxx") assert prisma.db.litellm_mcpuserenvvars.update.call_count == 1 where = prisma.db.litellm_mcpuserenvvars.update.call_args.kwargs["where"] @@ -666,9 +879,7 @@ async def test_refresh_user_oauth_token_uses_client_secret_basic(monkeypatch): monkeypatch.setattr(db_mod, "get_async_httpx_client", lambda **kwargs: mock_client) monkeypatch.setattr(db_mod, "store_user_oauth_credential", AsyncMock()) - monkeypatch.setattr( - db_mod, "get_user_oauth_credential", AsyncMock(return_value={"access_token": "new-at"}) - ) + monkeypatch.setattr(db_mod, "get_user_oauth_credential", AsyncMock(return_value={"access_token": "new-at"})) result = await db_mod.refresh_user_oauth_token( prisma_client=MagicMock(), @@ -707,9 +918,7 @@ async def test_refresh_user_oauth_token_defaults_to_client_secret_post(monkeypat monkeypatch.setattr(db_mod, "get_async_httpx_client", lambda **kwargs: mock_client) monkeypatch.setattr(db_mod, "store_user_oauth_credential", AsyncMock()) - monkeypatch.setattr( - db_mod, "get_user_oauth_credential", AsyncMock(return_value={"access_token": "new-at"}) - ) + monkeypatch.setattr(db_mod, "get_user_oauth_credential", AsyncMock(return_value={"access_token": "new-at"})) await db_mod.refresh_user_oauth_token( prisma_client=MagicMock(), @@ -722,3 +931,50 @@ async def test_refresh_user_oauth_token_defaults_to_client_secret_post(monkeypat assert "Authorization" not in kwargs["headers"] assert kwargs["data"]["client_id"] == "cid" assert kwargs["data"]["client_secret"] == "sec" + + +def test_prepare_mcp_server_data_create_carries_token_exchange_columns(): + """The create path (POST /v1/mcp/server) must emit token_exchange_endpoint/audience/ + subject_token_type as top-level column values so an auth_type=oauth2_token_exchange server + persists via the REST API, not only via config.yaml. Dropping the fields from the request + model would leave them out of the prepared column data.""" + request = NewMCPServerRequest( + server_name="te_write", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/oauth2/token", + audience="https://upstream.example.com", + subject_token_type="urn:ietf:params:oauth:token-type:jwt", + token_exchange_profile="entra_obo", + credentials={"client_id": "te-client", "client_secret": "te-secret"}, + ) + + data = _prepare_mcp_server_data(request) + + assert data["token_exchange_endpoint"] == "https://idp.example.com/oauth2/token" + assert data["audience"] == "https://upstream.example.com" + assert data["subject_token_type"] == "urn:ietf:params:oauth:token-type:jwt" + assert data["token_exchange_profile"] == "entra_obo" + + +def test_prepare_mcp_server_data_update_carries_token_exchange_columns(): + """The partial-update path (PUT /v1/mcp/server, exclude_unset) must carry the three + token-exchange columns when the caller provides them.""" + request = UpdateMCPServerRequest( + server_id="te-update", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/oauth2/token", + audience="https://upstream.example.com", + subject_token_type="urn:ietf:params:oauth:token-type:jwt", + token_exchange_profile="entra_obo", + ) + + data = _prepare_mcp_server_data(request, exclude_unset=True) + + assert data["token_exchange_endpoint"] == "https://idp.example.com/oauth2/token" + assert data["audience"] == "https://upstream.example.com" + assert data["subject_token_type"] == "urn:ietf:params:oauth:token-type:jwt" + assert data["token_exchange_profile"] == "entra_obo" diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py index b4a26c911dc..c55a631c7b3 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_discoverable_endpoints.py @@ -6,6 +6,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi import HTTPException +from litellm.types.mcp import MCPAuth + # Fixture to mock IP address check for all MCP tests # This prevents tests from failing due to IP-based access control @@ -35,6 +37,7 @@ def _mock_callback_request(base_url: str = "http://localhost:3000/"): req = MagicMock() req.base_url = base_url req.headers = {} + req.cookies = {} return req @@ -96,9 +99,7 @@ async def test_authorize_endpoint_includes_response_type(): mock_request.headers = {} # Mock the encryption functions to avoid needing a signing key - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper" - ) as mock_encrypt: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: mock_encrypt.return_value = "mocked_encrypted_state" # Call authorize endpoint @@ -120,6 +121,61 @@ async def test_authorize_endpoint_includes_response_type(): assert "scope=read+write" in response.headers["location"] +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type_value", ["true_passthrough", "oauth_delegate"]) +async def test_authorize_endpoint_allows_client_forwarded_modes(auth_type_value): + """The browser-only Authorize relays the gateway authorize flow for the client-forwarded + token modes; the oauth2-only gate must let them through and redirect to the upstream IdP.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + authorize, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + + server = MCPServer( + server_id="test_cf_server", + name="test_cf", + server_name="test_cf", + alias="test_cf", + transport=MCPTransport.http, + auth_type=MCPAuth(auth_type_value), + # Discovery stamps these onto the in-memory registry entry at build time. + authorization_url="https://provider.com/oauth/authorize", + token_url="https://provider.com/oauth/token", + ) + global_mcp_server_manager.registry[server.server_id] = server + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: + mock_encrypt.return_value = "mocked_encrypted_state" + + response = await authorize( + request=mock_request, + client_id="dcr_client_id", + mcp_server_name="test_cf", + redirect_uri="http://127.0.0.1:60108/callback", + state="test_state", + ) + + assert response.status_code == 307 + assert "https://provider.com/oauth/authorize" in response.headers["location"] + assert "client_id=dcr_client_id" in response.headers["location"] + + @pytest.mark.asyncio async def test_authorize_endpoint_preserves_existing_query_params(): """Test that authorize endpoint merges OAuth params with existing query params in authorization_url""" @@ -160,9 +216,7 @@ async def test_authorize_endpoint_preserves_existing_query_params(): mock_request.base_url = "https://litellm.example.com/" mock_request.headers = {} - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper" - ) as mock_encrypt: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: mock_encrypt.return_value = "mocked_encrypted_state" response = await authorize( @@ -176,9 +230,7 @@ async def test_authorize_endpoint_preserves_existing_query_params(): location = response.headers["location"] # Must NOT have double '?' — existing params must be merged correctly - assert ( - location.count("?") == 1 - ), f"Expected exactly one '?' in URL but got {location.count('?')}: {location}" + assert location.count("?") == 1, f"Expected exactly one '?' in URL but got {location.count('?')}: {location}" assert "tenant=system" in location assert "client_id=test_client_id" in location assert "response_type=code" in location @@ -228,9 +280,7 @@ async def test_authorize_endpoint_forwards_pkce_parameters(): mock_request.headers = {} # Mock the encryption function - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper" - ) as mock_encrypt: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: mock_encrypt.return_value = "mocked_encrypted_state_with_pkce" # Call authorize endpoint with PKCE parameters @@ -338,10 +388,7 @@ async def test_token_endpoint_forwards_code_verifier(): # Check the data parameter includes code_verifier assert call_args[1]["data"]["code_verifier"] == "test_code_verifier_from_client" assert call_args[1]["data"]["code"] == "4/test_authorization_code" - assert ( - call_args[1]["data"]["client_id"] - == "669428968603-test.apps.googleusercontent.com" - ) + assert call_args[1]["data"]["client_id"] == "669428968603-test.apps.googleusercontent.com" assert call_args[1]["data"]["client_secret"] == "GOCSPX-test_secret" assert call_args[1]["data"]["grant_type"] == "authorization_code" @@ -428,9 +475,7 @@ async def test_register_client_returns_existing_server_credentials(): "litellm.proxy._experimental.mcp_server.discoverable_endpoints._read_request_body", new=AsyncMock(return_value={}), ): - result = await register_client( - request=mock_request, mcp_server_name=oauth2_server.server_name - ) + result = await register_client(request=mock_request, mcp_server_name=oauth2_server.server_name) finally: global_mcp_server_manager.registry.clear() @@ -505,9 +550,7 @@ async def test_register_client_remote_registration_success(): return_value=mock_async_client, ), ): - response = await register_client( - request=mock_request, mcp_server_name=oauth2_server.server_name - ) + response = await register_client(request=mock_request, mcp_server_name=oauth2_server.server_name) finally: global_mcp_server_manager.registry.clear() @@ -524,15 +567,1021 @@ async def test_register_client_remote_registration_success(): "Content-Type": "application/json", "Accept": "application/json", } - assert call_args.kwargs["json"]["redirect_uris"] == [ - "https://proxy.litellm.example/callback" - ] + assert call_args.kwargs["json"]["redirect_uris"] == ["https://proxy.litellm.example/callback"] assert call_args.kwargs["json"]["grant_types"] == request_payload["grant_types"] - assert ( - call_args.kwargs["json"]["token_endpoint_auth_method"] - == request_payload["token_endpoint_auth_method"] + assert call_args.kwargs["json"]["token_endpoint_auth_method"] == request_payload["token_endpoint_auth_method"] + + +@pytest.mark.asyncio +async def test_register_client_persists_dcr_client_identity(): + """A dynamic client registration (RFC 7591) must persist the issued client_id / + client_secret / token_endpoint_auth_method and the token_url onto the server row so + autonomous refresh can authenticate as the registered client. Without persistence the + minted client_id is discarded and the refresh_token grant has no client identity. + + The persist must also stamp oauth2_flow="authorization_code": only the interactive + flow reaches this persist, and without the stamp the row (client creds + token_url, + no persisted authorization_url) matches the legacy M2M inference whenever endpoint + discovery fails at registry build, flipping the server to client_credentials.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = MCPServer( + server_id="remote_server", + name="remote_server", + server_name="remote_server", + alias="remote_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id=None, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", ) + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_response = MagicMock() + mock_response.json.return_value = { + "client_id": "generated-client", + "client_secret": "generated-secret", + "token_endpoint_auth_method": "client_secret_basic", + } + mock_response.raise_for_status = MagicMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + mock_update = AsyncMock(return_value=MagicMock()) + mock_update_server = AsyncMock() + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch("litellm.proxy._experimental.mcp_server.db.update_mcp_server", new=mock_update), + patch.object(global_mcp_server_manager, "update_server", new=mock_update_server), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="client_secret_basic", + persist_credentials=True, + ) + + import json + + assert response.status_code == 200 + assert json.loads(response.body.decode("utf-8")) == mock_response.json.return_value + + mock_update.assert_called_once() + update_data = mock_update.call_args.kwargs["data"] + assert update_data.server_id == "remote_server" + assert update_data.token_url == "https://provider.example/oauth/token" + assert update_data.credentials["client_id"] == "generated-client" + assert update_data.credentials["client_secret"] == "generated-secret" + assert update_data.credentials["token_endpoint_auth_method"] == "client_secret_basic" + assert update_data.credentials["redirect_uris"] == ["https://proxy.litellm.example/callback"] + assert update_data.oauth2_flow == "authorization_code" + + mock_update_server.assert_called_once() + + +async def _register_persistence_attempted_for_auth_type(auth_type: MCPAuth) -> bool: + """Run register_client_with_server with persist_credentials=True for a server of ``auth_type`` + and report whether the DCR result was persisted onto the server row. The client-forwarded token + modes must skip the persist even on the admin path: writing it stamps oauth2_flow and a + client_id onto a server whose contract is that the gateway stores nothing, which makes a fresh + pass-through server read as gateway-authorized. The upstream registration must still be relayed + to the browser either way, since the caller needs the minted client to run its own flow.""" + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + server = MCPServer( + server_id="pt_server", + name="pt_server", + server_name="pt_server", + alias="pt_server", + transport=MCPTransport.http, + auth_type=auth_type, + client_id=None, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", + ) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_response = MagicMock() + mock_response.json.return_value = { + "client_id": "generated-client", + "client_secret": "generated-secret", + "token_endpoint_auth_method": "none", + } + mock_response.raise_for_status = MagicMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + mock_update = AsyncMock(return_value=MagicMock()) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch("litellm.proxy._experimental.mcp_server.db.update_mcp_server", new=mock_update), + patch.object(global_mcp_server_manager, "update_server", new=AsyncMock()), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=True, + ) + + assert json.loads(response.body.decode("utf-8")) == mock_response.json.return_value + return mock_update.await_count > 0 + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +async def test_register_client_does_not_persist_for_client_forwarded_modes(auth_type): + """The admin Authorize path (persist_credentials=True) must not write the DCR client onto a + true_passthrough / oauth_delegate server row: the browser still receives the registration, but + the gateway keeps no OAuth client identity for these modes.""" + assert await _register_persistence_attempted_for_auth_type(auth_type) is False + + +@pytest.mark.asyncio +async def test_register_client_persist_discriminator_oauth2_persists(): + """Guard the no-persist assertion above against vacuity: the same helper run against a genuine + oauth2 server DOES persist, so a regression that silently disables persistence everywhere (or a + helper that never reaches the persist) fails here instead of passing both.""" + assert await _register_persistence_attempted_for_auth_type(MCPAuth.oauth2) is True + + +@pytest.mark.asyncio +async def test_register_client_persists_only_to_its_own_row_when_another_server_shares_the_url(): + """A fresh server must mint and persist its OWN DCR client even when another server row with + the same upstream URL already holds one: both the reuse lookup and the persist are keyed by + server_id, never by URL, so OAuth client identity is not transferable between server entries. + If either side ever falls back to a URL match, this fails: the fresh server would skip the + upstream registration (adopting the sibling's client) or persist onto the wrong row.""" + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + shared_url = "https://provider.example/mcp" + fresh_server = MCPServer( + server_id="server-b", + name="server-b", + server_name="server-b", + alias="server-b", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + url=shared_url, + client_id=None, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", + ) + sibling_row_with_client = MagicMock(server_id="server-a", url=shared_url) + sibling_row_with_client.credentials = {"client_id": "client-a-do-not-adopt"} + own_row_without_client = MagicMock(server_id="server-b", url=shared_url) + own_row_without_client.credentials = {} + rows_by_server_id = {"server-a": sibling_row_with_client, "server-b": own_row_without_client} + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_response = MagicMock() + mock_response.json.return_value = {"client_id": "fresh-client-b", "token_endpoint_auth_method": "none"} + mock_response.raise_for_status = MagicMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + mock_update = AsyncMock(return_value=MagicMock()) + + async def _get_row(prisma_client, server_id): + return rows_by_server_id.get(server_id) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch("litellm.proxy._experimental.mcp_server.db.get_mcp_server", new=AsyncMock(side_effect=_get_row)), + patch("litellm.proxy._experimental.mcp_server.db.update_mcp_server", new=mock_update), + patch.object(global_mcp_server_manager, "update_server", new=AsyncMock()), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=fresh_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=True, + ) + + mock_async_client.post.assert_called_once() + body = json.loads(response.body.decode("utf-8")) + assert body["client_id"] == "fresh-client-b" + + mock_update.assert_called_once() + update_data = mock_update.call_args.kwargs["data"] + assert update_data.server_id == "server-b" + assert update_data.credentials["client_id"] == "fresh-client-b" + + +@pytest.mark.asyncio +async def test_register_client_does_not_clobber_token_url_when_absent(): + """When the in-memory server has no token_url, the DCR persist must omit it from the + partial update rather than passing None, so exclude_unset leaves the token_url column + untouched instead of overwriting an existing value with NULL.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = MCPServer( + server_id="remote_server", + name="remote_server", + server_name="remote_server", + alias="remote_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id=None, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url=None, + registration_url="https://provider.example/oauth/register", + ) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_response = MagicMock() + mock_response.json.return_value = {"client_id": "generated-client"} + mock_response.raise_for_status = MagicMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + mock_update = AsyncMock(return_value=MagicMock()) + mock_update_server = AsyncMock() + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch("litellm.proxy._experimental.mcp_server.db.update_mcp_server", new=mock_update), + patch.object(global_mcp_server_manager, "update_server", new=mock_update_server), + ): + await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=True, + ) + + mock_update.assert_called_once() + update_data = mock_update.call_args.kwargs["data"] + assert update_data.credentials["client_id"] == "generated-client" + assert "token_url" not in update_data.model_fields_set + + +@pytest.mark.asyncio +async def test_register_client_reuses_persisted_client_id_for_non_admin_when_registry_is_stale(): + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = MCPServer( + server_id="remote_server", + name="remote_server", + server_name="remote_server", + alias="remote_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id=None, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", + ) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + persisted_server = MagicMock() + persisted_server.credentials = {"client_id": "persisted-client"} + mock_get_mcp_server = AsyncMock(return_value=persisted_server) + mock_update_mcp_server = AsyncMock() + mock_update_server = AsyncMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock() + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_mcp_server", + new=mock_get_mcp_server, + ), + patch( + "litellm.proxy._experimental.mcp_server.db.update_mcp_server", + new=mock_update_mcp_server, + ), + patch.object(global_mcp_server_manager, "update_server", new=mock_update_server), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=False, + ) + + assert response["client_id"] == "remote_server" + assert oauth2_server.client_id == "persisted-client" + mock_async_client.post.assert_not_called() + mock_update_mcp_server.assert_not_called() + mock_update_server.assert_called_once_with(persisted_server) + + +@pytest.mark.asyncio +async def test_register_client_reuse_refreshes_request_server_when_manager_update_fails(): + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = MCPServer( + server_id="remote_server", + name="remote_server", + server_name="remote_server", + alias="remote_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id=None, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", + ) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + persisted_server = MagicMock() + persisted_server.credentials = { + "client_id": "persisted-client", + "client_secret": "persisted-secret", + "token_endpoint_auth_method": "client_secret_basic", + } + mock_get_mcp_server = AsyncMock(return_value=persisted_server) + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock() + mock_update_server = AsyncMock(side_effect=RuntimeError("registry update failed")) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_mcp_server", + new=mock_get_mcp_server, + ), + patch.object(global_mcp_server_manager, "update_server", new=mock_update_server), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=False, + ) + + assert response["client_id"] == "remote_server" + assert oauth2_server.client_id == "persisted-client" + assert oauth2_server.client_secret == "persisted-secret" + assert oauth2_server.token_endpoint_auth_method == "client_secret_basic" + mock_async_client.post.assert_not_called() + mock_update_server.assert_called_once_with(persisted_server) + + +@pytest.mark.asyncio +async def test_register_client_returns_reused_client_when_concurrent_persist_wins(): + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = MCPServer( + server_id="remote_server", + name="remote_server", + server_name="remote_server", + alias="remote_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id=None, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", + ) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_response = MagicMock() + mock_response.json.return_value = {"client_id": "generated-client"} + mock_response.raise_for_status = MagicMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + persisted_server = MagicMock() + persisted_server.credentials = {"client_id": "persisted-client"} + mock_get_mcp_server = AsyncMock(side_effect=[None, persisted_server]) + mock_update_mcp_server = AsyncMock() + mock_update_server = AsyncMock() + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_mcp_server", + new=mock_get_mcp_server, + ), + patch( + "litellm.proxy._experimental.mcp_server.db.update_mcp_server", + new=mock_update_mcp_server, + ), + patch.object(global_mcp_server_manager, "update_server", new=mock_update_server), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=True, + ) + + assert response["client_id"] == "remote_server" + assert oauth2_server.client_id == "persisted-client" + mock_async_client.post.assert_called_once() + mock_update_mcp_server.assert_not_called() + mock_update_server.assert_called_once_with(persisted_server) + + +def _dcr_redirect_test_server(client_id): + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + return MCPServer( + server_id="remote_server", + name="remote_server", + server_name="remote_server", + alias="remote_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id=client_id, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", + ) + + +@pytest.mark.asyncio +async def test_register_client_re_registers_when_persisted_redirect_uri_no_longer_matches_origin(): + """A persisted DCR client is bound to the redirect_uri it was registered with. When the + proxy's resolved public origin changes, every authorize built for the reused client is + rejected IdP-side and the server is permanently stranded (GH #32473). A positive mismatch + between the recorded redirect_uris and the current callback must therefore re-register on + the admin path and persist the replacement client, with the new binding recorded and the + old client's secret/auth method cleared rather than merged into the new identity.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = _dcr_redirect_test_server(client_id="stale-client") + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_response = MagicMock() + mock_response.json.return_value = { + "client_id": "fresh-client", + "redirect_uris": ["https://proxy.litellm.example/callback"], + } + mock_response.raise_for_status = MagicMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + persisted_server = MagicMock() + persisted_server.credentials = { + "client_id": "stale-client", + "client_secret": "stale-secret", + "token_endpoint_auth_method": "client_secret_basic", + "redirect_uris": ["https://old-origin.example/callback"], + } + mock_get_mcp_server = AsyncMock(return_value=persisted_server) + mock_update_mcp_server = AsyncMock(return_value=MagicMock()) + mock_update_server = AsyncMock() + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_mcp_server", + new=mock_get_mcp_server, + ), + patch( + "litellm.proxy._experimental.mcp_server.db.update_mcp_server", + new=mock_update_mcp_server, + ), + patch.object(global_mcp_server_manager, "update_server", new=mock_update_server), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=True, + ) + + mock_async_client.post.assert_called_once() + register_payload = mock_async_client.post.call_args.kwargs["json"] + assert register_payload["redirect_uris"] == ["https://proxy.litellm.example/callback"] + + mock_update_mcp_server.assert_called_once() + update_data = mock_update_mcp_server.call_args.kwargs["data"] + assert update_data.credentials["client_id"] == "fresh-client" + assert update_data.credentials["redirect_uris"] == ["https://proxy.litellm.example/callback"] + assert update_data.credentials["client_secret"] is None + assert update_data.credentials["token_endpoint_auth_method"] is None + + assert response.status_code == 200 + assert json.loads(response.body.decode("utf-8"))["client_id"] == "fresh-client" + + +@pytest.mark.asyncio +async def test_register_client_grandfathers_persisted_client_without_recorded_redirect_uris(): + """Clients persisted before redirect_uris were recorded (and admin-configured clients, + which never get a recording) have nothing to compare against; treating that as a mismatch + would re-mint a client_id for every existing install on upgrade and orphan all users' + refresh tokens for those servers. A missing recording must read as a match: no DCR call, + no persistence write, existing client returned.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = _dcr_redirect_test_server(client_id="legacy-client") + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock() + + persisted_server = MagicMock() + persisted_server.credentials = {"client_id": "legacy-client"} + mock_get_mcp_server = AsyncMock(return_value=persisted_server) + mock_update_mcp_server = AsyncMock() + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_mcp_server", + new=mock_get_mcp_server, + ), + patch( + "litellm.proxy._experimental.mcp_server.db.update_mcp_server", + new=mock_update_mcp_server, + ), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=True, + ) + + mock_async_client.post.assert_not_called() + mock_update_mcp_server.assert_not_called() + assert response["client_secret"] == "dummy" + assert oauth2_server.client_id == "legacy-client" + + +@pytest.mark.asyncio +async def test_register_client_keeps_persisted_client_when_recorded_redirect_uri_matches_origin(): + """When the recorded redirect_uris still cover the current callback the persisted client + is valid; re-registering would orphan refresh tokens for no reason.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = _dcr_redirect_test_server(client_id="kept-client") + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock() + + persisted_server = MagicMock() + persisted_server.credentials = { + "client_id": "kept-client", + "redirect_uris": ["https://proxy.litellm.example/callback"], + } + mock_get_mcp_server = AsyncMock(return_value=persisted_server) + mock_update_mcp_server = AsyncMock() + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_mcp_server", + new=mock_get_mcp_server, + ), + patch( + "litellm.proxy._experimental.mcp_server.db.update_mcp_server", + new=mock_update_mcp_server, + ), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=True, + ) + + mock_async_client.post.assert_not_called() + mock_update_mcp_server.assert_not_called() + assert response["client_secret"] == "dummy" + + +@pytest.mark.asyncio +async def test_register_client_non_admin_reuses_persisted_client_despite_redirect_mismatch(): + """Non-persisting callers (the public register routes and non-admin users) must keep + today's reuse behavior even when the recorded redirect_uris mismatch: re-registering + without persistence would mint an orphan upstream client on every connect while the + stored client keeps being used at authorize time. Only the admin path re-registers.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + oauth2_server = _dcr_redirect_test_server(client_id=None) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock() + + persisted_server = MagicMock() + persisted_server.credentials = { + "client_id": "persisted-client", + "redirect_uris": ["https://old-origin.example/callback"], + } + mock_get_mcp_server = AsyncMock(return_value=persisted_server) + mock_update_server = AsyncMock() + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_mcp_server", + new=mock_get_mcp_server, + ), + patch.object(global_mcp_server_manager, "update_server", new=mock_update_server), + ): + response = await register_client_with_server( + request=mock_request, + mcp_server=oauth2_server, + client_name="Litellm Proxy", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + persist_credentials=False, + ) + + mock_async_client.post.assert_not_called() + assert oauth2_server.client_id == "persisted-client" + assert response["client_secret"] == "dummy" + + +@pytest.mark.asyncio +async def test_register_client_reuses_existing_client_id_without_re_dcr(): + """A server that already has a client_id (admin-configured or previously DCR'd) must be + reused, not re-registered, even without a client_secret. A client_id is one-per-application + in OAuth and shared across users; re-minting per authorize would orphan other users' refresh + tokens by overwriting the server's client_id.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + oauth2_server = MCPServer( + server_id="remote_server", + name="remote_server", + server_name="remote_server", + alias="remote_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id="existing-shared-client", + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", + ) + global_mcp_server_manager.registry[oauth2_server.server_id] = oauth2_server + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + request_payload = { + "client_name": "Litellm Proxy", + "grant_types": ["authorization_code", "refresh_token"], + "response_types": ["code"], + "token_endpoint_auth_method": "none", + } + + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock() + + try: + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._read_request_body", + new=AsyncMock(return_value=request_payload), + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + ): + response = await register_client(request=mock_request, mcp_server_name=oauth2_server.server_name) + finally: + global_mcp_server_manager.registry.clear() + + mock_async_client.post.assert_not_called() + body = response if isinstance(response, dict) else json.loads(response.body.decode("utf-8")) + assert body["client_secret"] == "dummy" + + +@pytest.mark.asyncio +async def test_public_register_route_does_not_persist_client_credentials(): + """The unauthenticated root /register route must not persist the DCR result onto the + server row; only the authenticated management path passes persist_credentials=True. An + external caller could otherwise bind a caller-controlled client (and leak its secret) to + a server that has no client yet.""" + try: + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + oauth2_server = MCPServer( + server_id="remote_server", + name="remote_server", + server_name="remote_server", + alias="remote_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id=None, + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + registration_url="https://provider.example/oauth/register", + ) + global_mcp_server_manager.registry[oauth2_server.server_id] = oauth2_server + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://proxy.litellm.example/" + mock_request.headers = {} + + request_payload = { + "client_name": "attacker", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "token_endpoint_auth_method": "none", + } + + mock_response = MagicMock() + mock_response.json.return_value = { + "client_id": "attacker-client", + "client_secret": "attacker-secret", + } + mock_response.raise_for_status = MagicMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + mock_update = AsyncMock(return_value=MagicMock()) + + try: + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._read_request_body", + new=AsyncMock(return_value=request_payload), + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch("litellm.proxy.utils.get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(global_mcp_server_manager, "update_server", new=AsyncMock()), + patch("litellm.proxy._experimental.mcp_server.db.update_mcp_server", new=mock_update), + ): + await register_client(request=mock_request, mcp_server_name=oauth2_server.server_name) + finally: + global_mcp_server_manager.registry.clear() + + mock_update.assert_not_called() + @pytest.mark.asyncio @pytest.mark.usefixtures("trust_xff") @@ -578,9 +1627,7 @@ async def test_authorize_endpoint_respects_x_forwarded_proto(): mock_request.headers = {"X-Forwarded-Proto": "https"} # Behind HTTPS proxy # Mock the encryption functions - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper" - ) as mock_encrypt: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: mock_encrypt.return_value = "mocked_encrypted_state" # Call authorize endpoint @@ -675,10 +1722,7 @@ async def test_token_endpoint_respects_x_forwarded_proto(): # Verify that the redirect_uri sent to the provider uses HTTPS call_args = mock_async_client.post.call_args - assert ( - call_args[1]["data"]["redirect_uri"] - == "https://litellm-proxy.example.com/callback" - ) + assert call_args[1]["data"]["redirect_uri"] == "https://litellm-proxy.example.com/callback" @pytest.mark.asyncio @@ -730,9 +1774,7 @@ async def test_oauth_protected_resource_respects_x_forwarded_proto(): ) # Verify response uses HTTPS URLs - assert response["authorization_servers"][0].startswith( - "https://litellm.example.com/" - ) + assert response["authorization_servers"][0].startswith("https://litellm.example.com/") assert response["scopes_supported"] == oauth2_server.scopes @@ -879,9 +1921,7 @@ async def test_authorize_endpoint_respects_x_forwarded_host(): } # Mock the encryption functions - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper" - ) as mock_encrypt: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: mock_encrypt.return_value = "mocked_encrypted_state" # Call authorize endpoint @@ -898,8 +1938,7 @@ async def test_authorize_endpoint_respects_x_forwarded_host(): # The redirect_uri parameter should use the external URL assert ( - "redirect_uri=https%3A%2F%2Fproxy.example.com%2Fgithub%2Fmcp%2Fcallback" - in location + "redirect_uri=https%3A%2F%2Fproxy.example.com%2Fgithub%2Fmcp%2Fcallback" in location or "redirect_uri=https://proxy.example.com/github/mcp/callback" in location ) @@ -980,10 +2019,7 @@ async def test_token_endpoint_respects_x_forwarded_host(): # Verify that the redirect_uri sent to the provider uses the external URL call_args = mock_async_client.post.call_args - assert ( - call_args[1]["data"]["redirect_uri"] - == "https://proxy.example.com/github/mcp/callback" - ) + assert call_args[1]["data"]["redirect_uri"] == "https://proxy.example.com/github/mcp/callback" @pytest.mark.parametrize( @@ -1103,6 +2139,41 @@ async def test_token_endpoint_respects_x_forwarded_host(): None, "https://external.com", ), + ( + "http://localhost:4000/", + "https", + "proxy.example.com", + "443", + "https://proxy.example.com", + ), + ( + "http://localhost:4000/", + "http", + "proxy.example.com", + "80", + "http://proxy.example.com", + ), + ( + "http://internal.local/", + "https", + None, + "443", + "https://internal.local", + ), + ( + "http://localhost:4000/", + "https", + "proxy.example.com", + "8443", + "https://proxy.example.com:8443", + ), + ( + "http://localhost:4000/", + "https", + "proxy.example.com:443", + None, + "https://proxy.example.com", + ), ], ) def test_get_request_base_url_comprehensive( @@ -1191,9 +2262,7 @@ def test_get_request_base_url_comprehensive( ), ], ) -def test_get_request_base_url_xff_trust_gate( - general_settings, direct_ip, expect_xff_honoured -): +def test_get_request_base_url_xff_trust_gate(general_settings, direct_ip, expect_xff_honoured): """Verify the X-Forwarded-* trust gate. With XFF poisoning attempted, the helper must return either the literal @@ -1271,12 +2340,10 @@ def test_xff_misconfig_warning_emitted_once(caplog): for _ in range(3): get_request_base_url(mock_request) - matching = [ - rec for rec in caplog.records if "mcp_trusted_proxy_ranges" in rec.getMessage() - ] - assert ( - len(matching) == 1 - ), f"expected exactly one warning, got {len(matching)}: {[r.getMessage() for r in matching]}" + matching = [rec for rec in caplog.records if "mcp_trusted_proxy_ranges" in rec.getMessage()] + assert len(matching) == 1, ( + f"expected exactly one warning, got {len(matching)}: {[r.getMessage() for r in matching]}" + ) def test_get_request_base_url_honors_proxy_base_url_env(monkeypatch): @@ -1307,9 +2374,7 @@ def test_get_request_base_url_honors_proxy_base_url_env(monkeypatch): assert get_request_base_url(mock_request) == "https://litellm.example.com" -def test_validate_trusted_redirect_uri_logs_diagnostic_on_rejection( - caplog, monkeypatch -): +def test_validate_trusted_redirect_uri_logs_diagnostic_on_rejection(caplog, monkeypatch): try: from fastapi import HTTPException, Request @@ -1356,8 +2421,7 @@ def test_validate_trusted_redirect_uri_logs_diagnostic_on_rejection( matching = [r for r in caplog.records if "rejecting redirect_uri" in r.getMessage()] assert len(matching) == 1, ( - "expected exactly one diagnostic warning, got " - f"{[r.getMessage() for r in caplog.records]}" + f"expected exactly one diagnostic warning, got {[r.getMessage() for r in caplog.records]}" ) msg = matching[0].getMessage() assert "https://litellm.example.com/ui/mcp/oauth/callback" in msg @@ -1376,9 +2440,7 @@ def test_validate_trusted_redirect_uri_logs_diagnostic_on_rejection( "not a url at all", ], ) -def test_get_request_base_url_rejects_malformed_proxy_base_url( - bad_value, monkeypatch, caplog -): +def test_get_request_base_url_rejects_malformed_proxy_base_url(bad_value, monkeypatch, caplog): try: from fastapi import Request @@ -1408,26 +2470,16 @@ def test_get_request_base_url_rejects_malformed_proxy_base_url( result = get_request_base_url(mock_request) assert result == "http://litellm-internal:4000", ( - f"malformed PROXY_BASE_URL={bad_value!r} should be ignored, " f"got {result!r}" + f"malformed PROXY_BASE_URL={bad_value!r} should be ignored, got {result!r}" ) - matching = [ - r - for r in caplog.records - if "PROXY_BASE_URL" in r.getMessage() and "ignored" in r.getMessage() - ] + matching = [r for r in caplog.records if "PROXY_BASE_URL" in r.getMessage() and "ignored" in r.getMessage()] assert len(matching) == 1, ( - "expected one diagnostic for malformed PROXY_BASE_URL, got " - f"{[r.getMessage() for r in caplog.records]}" - ) - assert ( - repr(bad_value) in matching[0].getMessage() - or bad_value in matching[0].getMessage() + f"expected one diagnostic for malformed PROXY_BASE_URL, got {[r.getMessage() for r in caplog.records]}" ) + assert repr(bad_value) in matching[0].getMessage() or bad_value in matching[0].getMessage() -def test_get_request_base_url_malformed_proxy_base_url_warning_is_one_shot( - monkeypatch, caplog -): +def test_get_request_base_url_malformed_proxy_base_url_warning_is_one_shot(monkeypatch, caplog): try: from fastapi import Request @@ -1457,14 +2509,8 @@ def test_get_request_base_url_malformed_proxy_base_url_warning_is_one_shot( for _ in range(5): get_request_base_url(mock_request) - matching = [ - r - for r in caplog.records - if "PROXY_BASE_URL" in r.getMessage() and "ignored" in r.getMessage() - ] - assert ( - len(matching) == 1 - ), f"expected exactly one warning across 5 calls, got {len(matching)}" + matching = [r for r in caplog.records if "PROXY_BASE_URL" in r.getMessage() and "ignored" in r.getMessage()] + assert len(matching) == 1, f"expected exactly one warning across 5 calls, got {len(matching)}" # ------------------------------------------------------------------- @@ -1677,12 +2723,8 @@ async def test_authorize_root_fails_with_multiple_oauth2_servers(): pytest.skip("MCP discoverable endpoints not available") global_mcp_server_manager.registry.clear() - server1 = _create_oauth2_server( - server_id="server1", name="server1", server_name="server1", alias="server1" - ) - server2 = _create_oauth2_server( - server_id="server2", name="server2", server_name="server2", alias="server2" - ) + server1 = _create_oauth2_server(server_id="server1", name="server1", server_name="server1", alias="server1") + server2 = _create_oauth2_server(server_id="server2", name="server2", server_name="server2", alias="server2") global_mcp_server_manager.registry[server1.server_id] = server1 global_mcp_server_manager.registry[server2.server_id] = server2 @@ -2040,9 +3082,7 @@ async def test_oauth_callback_redirects_with_state(): "client_redirect_uri": "http://localhost:3000/ui/mcp/oauth/callback", } - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash" - ) as mock_decode: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash") as mock_decode: mock_decode.return_value = mock_state_data # Call callback endpoint with code and state @@ -2054,10 +3094,7 @@ async def test_oauth_callback_redirects_with_state(): # Should redirect to the client callback URL with code and original state assert response.status_code == 302 - assert ( - "http://localhost:3000/ui/mcp/oauth/callback" - in response.headers["location"] - ) + assert "http://localhost:3000/ui/mcp/oauth/callback" in response.headers["location"] assert "code=test_authorization_code_12345" in response.headers["location"] assert "state=test-uuid-state-123" in response.headers["location"] @@ -2077,17 +3114,13 @@ async def test_oauth_callback_preserves_client_redirect_uri_query(): except ImportError: pytest.skip("MCP discoverable endpoints not available") - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash" - ) as mock_decode: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash") as mock_decode: mock_decode.return_value = { "base_url": "http://localhost:3000/ui/mcp/oauth/callback", "original_state": "test-uuid-state-123", "code_challenge": "test_challenge", "code_challenge_method": "S256", - "client_redirect_uri": ( - "http://localhost:3000/ui/mcp/oauth/callback?session=abc" - ), + "client_redirect_uri": ("http://localhost:3000/ui/mcp/oauth/callback?session=abc"), } response = await callback( @@ -2115,9 +3148,7 @@ async def test_oauth_callback_handles_invalid_state(): pytest.skip("MCP discoverable endpoints not available") # Mock state decoding to raise an exception - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash" - ) as mock_decode: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash") as mock_decode: mock_decode.side_effect = Exception("Failed to decrypt state") # Call callback endpoint with invalid state @@ -2140,9 +3171,7 @@ async def test_oauth_callback_accepts_same_origin_ui_redirect(): callback, ) - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash" - ) as mock_decode: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash") as mock_decode: mock_decode.return_value = { "base_url": "https://proxy.example.com/ui/mcp/oauth/callback", "original_state": "state-123", @@ -2158,14 +3187,167 @@ async def test_oauth_callback_accepts_same_origin_ui_redirect(): ) assert response.status_code == 302 - assert ( - "https://proxy.example.com/ui/mcp/oauth/callback" - in response.headers["location"] - ) + assert "https://proxy.example.com/ui/mcp/oauth/callback" in response.headers["location"] assert "code=auth-code-123" in response.headers["location"] assert "state=state-123" in response.headers["location"] +@pytest.mark.asyncio +async def test_authorize_forwards_short_state_and_round_trips_via_cookie(monkeypatch): + """LIT-4197: the ``state`` sent to the upstream authorization server must be + a short opaque handle, not the long encrypted OAuth session (some IdPs + reject an over-long state). The session must instead ride in a per-flow + HttpOnly cookie so ``/callback`` still recovers the client's original state + and redirects back to the client's redirect_uri.""" + from http.cookies import SimpleCookie + from urllib.parse import parse_qs, urlparse + + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _oauth_state_cookie_name, + authorize_with_server, + callback, + decode_state_hash, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + # Real encryption so the cookie value is a genuine encrypted session. + monkeypatch.setenv("LITELLM_SALT_KEY", "sk-test-salt-for-LIT-4197") + + client_state = "ee230e3dfd4f19c7441941684f39c8a4e0e2c3c61a088e33403df5662b4047b8" + client_redirect_uri = "http://127.0.0.1:6274/oauth/callback/debug" + + server = MCPServer( + server_id="leanix_server", + name="leanix", + server_name="leanix", + alias="leanix", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id="upstream-client-id", + authorization_url="https://idp.example.com/oauth/authorize", + token_url="https://idp.example.com/oauth/token", + ) + + authorize_request = MagicMock(spec=Request) + authorize_request.base_url = "https://proxy.example.com/" + authorize_request.headers = {} + + authorize_response = await authorize_with_server( + request=authorize_request, + mcp_server=server, + client_id="upstream-client-id", + redirect_uri=client_redirect_uri, + state=client_state, + code_challenge="challenge", + code_challenge_method="S256", + ) + + location = authorize_response.headers["location"] + upstream_state = parse_qs(urlparse(location).query)["state"][0] + + # The upstream must receive a short handle, not the encrypted session blob. + assert len(upstream_state) <= 64 + assert upstream_state != client_state + + # The encrypted session rides in a per-flow HttpOnly cookie bound to it. + jar = SimpleCookie() + jar.load(authorize_response.headers["set-cookie"]) + cookie_name = _oauth_state_cookie_name(upstream_state) + assert cookie_name in jar + morsel = jar[cookie_name] + assert morsel["httponly"] + assert morsel["samesite"].lower() == "lax" + assert len(morsel.value) > len(upstream_state) + session = decode_state_hash(morsel.value) + assert session["original_state"] == client_state + assert session["client_redirect_uri"] == client_redirect_uri + + # /callback recovers the original state from the cookie (not the handle) and + # redirects back to the client with the client's own state. + callback_request = MagicMock(spec=Request) + callback_request.base_url = "https://proxy.example.com/" + callback_request.headers = {} + callback_request.cookies = {cookie_name: morsel.value} + + callback_response = await callback( + request=callback_request, + code="upstream-auth-code", + state=upstream_state, + ) + + assert callback_response.status_code == 302 + cb_query = parse_qs(urlparse(callback_response.headers["location"]).query) + assert callback_response.headers["location"].startswith(client_redirect_uri) + assert cb_query["code"] == ["upstream-auth-code"] + assert cb_query["state"] == [client_state] + + # The one-time cookie is expired on the callback response so it cannot be replayed. + cleared = SimpleCookie() + cleared.load(callback_response.headers["set-cookie"]) + assert cookie_name in cleared + assert cleared[cookie_name].value == "" + assert cleared[cookie_name]["max-age"] == "0" + + +@pytest.mark.asyncio +async def test_callback_error_path_reads_cookie_and_clears_it(monkeypatch): + """LIT-4197: an IdP error routed through /callback must recover the client's + original state from the cookie (not the short handle), propagate the error to + the client's redirect_uri, and expire the one-time cookie.""" + from http.cookies import SimpleCookie + from urllib.parse import parse_qs, urlparse + + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _oauth_state_cookie_name, + callback, + encode_state_with_base_url, + ) + + monkeypatch.setenv("LITELLM_SALT_KEY", "sk-test-salt-for-LIT-4197") + + client_state = "client-original-state-abc" + client_redirect_uri = "http://127.0.0.1:6274/oauth/callback/debug" + handle = "shortRelayHandle123" + encoded_state = encode_state_with_base_url( + base_url=client_redirect_uri, + original_state=client_state, + client_redirect_uri=client_redirect_uri, + ) + cookie_name = _oauth_state_cookie_name(handle) + + request = MagicMock(spec=Request) + request.base_url = "https://proxy.example.com/" + request.headers = {} + request.cookies = {cookie_name: encoded_state} + + response = await callback( + request=request, + error="access_denied", + error_description="User declined access", + state=handle, + ) + + assert response.status_code == 302 + location = response.headers["location"] + assert location.startswith(client_redirect_uri) + query = parse_qs(urlparse(location).query) + assert query["error"] == ["access_denied"] + # The client's own state is echoed back, recovered from the cookie. + assert query["state"] == [client_state] + + cleared = SimpleCookie() + cleared.load(response.headers["set-cookie"]) + assert cookie_name in cleared + assert cleared[cookie_name].value == "" + assert cleared[cookie_name]["max-age"] == "0" + + @pytest.mark.asyncio async def test_oauth_authorize_includes_scopes_from_server_config(): """Test that authorize endpoint includes scopes from server configuration.""" @@ -2198,9 +3380,7 @@ async def test_oauth_authorize_includes_scopes_from_server_config(): mock_request.base_url = "https://litellm.example.com/" mock_request.headers = {} - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper" - ) as mock_encrypt: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: mock_encrypt.return_value = "encrypted_state" # Call authorize without explicit scope parameter @@ -2220,8 +3400,7 @@ async def test_oauth_authorize_includes_scopes_from_server_config(): assert response.status_code in (307, 302) redirect_url = response.headers["location"] assert ( - "scope=api+read_user+ai_workflows" in redirect_url - or "scope=api%20read_user%20ai_workflows" in redirect_url + "scope=api+read_user+ai_workflows" in redirect_url or "scope=api%20read_user%20ai_workflows" in redirect_url ) @@ -2256,9 +3435,7 @@ async def test_oauth_authorize_prefers_request_scope_over_server_config(): mock_request.base_url = "https://litellm.example.com/" mock_request.headers = {} - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper" - ) as mock_encrypt: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper") as mock_encrypt: mock_encrypt.return_value = "encrypted_state" # Call authorize WITH explicit scope parameter @@ -2278,8 +3455,7 @@ async def test_oauth_authorize_prefers_request_scope_over_server_config(): assert response.status_code in (307, 302) redirect_url = response.headers["location"] assert ( - "scope=custom_scope1+custom_scope2" in redirect_url - or "scope=custom_scope1%20custom_scope2" in redirect_url + "scope=custom_scope1+custom_scope2" in redirect_url or "scope=custom_scope1%20custom_scope2" in redirect_url ) assert "default_scope" not in redirect_url @@ -2536,9 +3712,7 @@ async def test_callback_revalidates_loopback_on_decoded_base_url(): callback, ) - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash" - ) as mock_decode: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash") as mock_decode: mock_decode.return_value = { "base_url": "https://attacker.example.com/cb", "original_state": "s", @@ -2562,9 +3736,7 @@ async def test_callback_revalidates_loopback_on_decoded_client_redirect_uri(): callback, ) - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash" - ) as mock_decode: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash") as mock_decode: mock_decode.return_value = { "base_url": "http://localhost:3000/cb", "original_state": "s", @@ -2588,9 +3760,7 @@ async def test_callback_rejects_state_missing_redirect_uri(): callback, ) - with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash" - ) as mock_decode: + with patch("litellm.proxy._experimental.mcp_server.discoverable_endpoints.decode_state_hash") as mock_decode: mock_decode.return_value = { "original_state": "s", "code_challenge": None, @@ -2719,9 +3889,7 @@ async def test_token_exchange_omits_expires_in_when_upstream_omits_it(): rotation) returns no ``expires_in``. The exchange must mirror that and omit ``expires_in`` rather than fabricate a 1-hour TTL, so the stored credential is treated as non-expiring instead of dying after an hour.""" - body = await _exchange_with_upstream_token_response( - {"access_token": "tok", "token_type": "Bearer"} - ) + body = await _exchange_with_upstream_token_response({"access_token": "tok", "token_type": "Bearer"}) assert "expires_in" not in body @@ -2735,6 +3903,656 @@ async def test_token_exchange_passes_through_upstream_expires_in(): assert body["expires_in"] == 43200 +_BRIDGE_CLIENT_REDIRECT = "https://claude.ai/api/mcp/auth_callback" + + +def _bridge_server(**overrides): + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + fields = { + "server_id": "bridge_srv", + "name": "bridge_srv", + "server_name": "bridge_srv", + "alias": "bridge_srv", + "transport": MCPTransport.http, + "auth_type": MCPAuth.true_passthrough, + "dcr_bridge": True, + "authorization_url": "https://provider.com/oauth/authorize", + "token_url": "https://provider.com/oauth/token", + "registration_url": "https://provider.com/oauth/register", + **overrides, + } + return MCPServer(**fields) + + +def _bridge_mock_request(): + from fastapi import Request + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + return mock_request + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type_value", ["true_passthrough", "oauth_delegate"]) +async def test_authorize_bridge_relay_passes_client_params_verbatim(auth_type_value): + """The bridge relay arm (registration relayed upstream, no admin-configured client) passes the + client's client_id, redirect_uri, state, and PKCE through verbatim: the code returns straight + to the client's own redirect URI, so the gateway sets no state cookie, injects no /callback, + and applies no gateway-side redirect trust (the upstream enforces its registered binding).""" + from urllib.parse import parse_qs, urlparse + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + authorize_with_server, + ) + from litellm.types.mcp import MCPAuth + + response = await authorize_with_server( + request=_bridge_mock_request(), + mcp_server=_bridge_server(auth_type=MCPAuth(auth_type_value)), + client_id="dcr-client-123", + redirect_uri=_BRIDGE_CLIENT_REDIRECT, + state="client-state", + code_challenge="chal", + code_challenge_method="S256", + ) + + assert response.status_code == 307 + location = response.headers["location"] + assert location.startswith("https://provider.com/oauth/authorize") + query = parse_qs(urlparse(location).query) + assert query["client_id"] == ["dcr-client-123"] + assert query["redirect_uri"] == [_BRIDGE_CLIENT_REDIRECT] + assert query["state"] == ["client-state"] + assert query["code_challenge"] == ["chal"] + assert query["code_challenge_method"] == ["S256"] + assert "litellm.example.com" not in location + assert "set-cookie" not in {key.lower() for key in response.headers.keys()} + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "code_challenge,code_challenge_method", + [(None, None), ("chal", None), ("chal", "plain"), (None, "S256")], +) +async def test_authorize_bridge_requires_s256_pkce(code_challenge, code_challenge_method): + """Bridge servers serve unauthenticated public clients, so the PKCE downgrade paths (missing + challenge, or a method that is not S256; RFC 7636 defaults a missing method to plain) are + rejected at the gateway on both bridge arms.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + authorize_with_server, + ) + + with pytest.raises(HTTPException) as exc: + await authorize_with_server( + request=_bridge_mock_request(), + mcp_server=_bridge_server(), + client_id="dcr-client-123", + redirect_uri=_BRIDGE_CLIENT_REDIRECT, + state="s", + code_challenge=code_challenge, + code_challenge_method=code_challenge_method, + ) + + assert exc.value.status_code == 400 + assert "S256" in str(exc.value.detail) + + +@pytest.mark.asyncio +async def test_authorize_bridge_short_circuit_keeps_callback_and_redirect_trust(): + """The bridge short-circuit arm (admin-configured OAuth client, upstream only knows the + gateway callback) keeps the /callback state relay and the gateway redirect trust: a public + client redirect target is rejected unless ops allowlist it, and a trusted target still routes + through the gateway callback with the state cookie.""" + from urllib.parse import parse_qs, urlparse + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + authorize_with_server, + ) + + short_circuit_server = _bridge_server(client_id="admin-client", registration_url=None) + + with pytest.raises(HTTPException) as exc: + await authorize_with_server( + request=_bridge_mock_request(), + mcp_server=short_circuit_server, + client_id="ignored", + redirect_uri=_BRIDGE_CLIENT_REDIRECT, + state="s", + code_challenge="chal", + code_challenge_method="S256", + ) + assert exc.value.status_code in (400, 403) + + with patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper", + return_value="mocked_encrypted_state", + ): + response = await authorize_with_server( + request=_bridge_mock_request(), + mcp_server=short_circuit_server, + client_id="ignored", + redirect_uri="http://127.0.0.1:60108/callback", + state="s", + code_challenge="chal", + code_challenge_method="S256", + ) + + query = parse_qs(urlparse(response.headers["location"]).query) + assert query["redirect_uri"] == ["https://litellm.example.com/callback"] + assert query["client_id"] == ["admin-client"] + + +@pytest.mark.asyncio +async def test_authorize_non_bridge_client_forwarded_keeps_pre_bridge_contract(): + """A client-forwarded server without dcr_bridge keeps the pre-bridge behavior: no PKCE + requirement and the gateway /callback relay (this is the browser-only Authorize path).""" + from urllib.parse import parse_qs, urlparse + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + authorize_with_server, + ) + + with patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.encrypt_value_helper", + return_value="mocked_encrypted_state", + ): + response = await authorize_with_server( + request=_bridge_mock_request(), + mcp_server=_bridge_server(dcr_bridge=None), + client_id="cid", + redirect_uri="http://127.0.0.1:60108/callback", + state="s", + ) + + assert response.status_code == 307 + query = parse_qs(urlparse(response.headers["location"]).query) + assert query["redirect_uri"] == ["https://litellm.example.com/callback"] + + +async def _bridge_token_post_data(server, redirect_uri): + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + exchange_token_with_server, + ) + + fake_http_response = MagicMock() + fake_http_response.json.return_value = {"access_token": "tok", "token_type": "Bearer"} + fake_http_response.raise_for_status = MagicMock() + fake_http_client = MagicMock() + fake_http_client.post = AsyncMock(return_value=fake_http_response) + + with patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=fake_http_client, + ): + await exchange_token_with_server( + request=_bridge_mock_request(), + mcp_server=server, + grant_type="authorization_code", + code="auth-code", + redirect_uri=redirect_uri, + client_id="dcr-client-123", + client_secret=None, + code_verifier="verifier", + ) + return fake_http_client.post.call_args.kwargs["data"] + + +@pytest.mark.asyncio +async def test_token_bridge_relay_posts_client_redirect_uri(): + """The bridge relay arm's token exchange sends the client's own redirect_uri upstream (it must + match the authorize leg) with the caller's public client_id and PKCE verifier.""" + data = await _bridge_token_post_data(_bridge_server(), redirect_uri=_BRIDGE_CLIENT_REDIRECT) + + assert data["redirect_uri"] == _BRIDGE_CLIENT_REDIRECT + assert data["client_id"] == "dcr-client-123" + assert data["code_verifier"] == "verifier" + assert "client_secret" not in data + + +@pytest.mark.asyncio +async def test_token_bridge_relay_requires_redirect_uri(): + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + exchange_token_with_server, + ) + + with pytest.raises(HTTPException) as exc: + await exchange_token_with_server( + request=_bridge_mock_request(), + mcp_server=_bridge_server(), + grant_type="authorization_code", + code="auth-code", + redirect_uri=None, + client_id="dcr-client-123", + client_secret=None, + code_verifier="verifier", + ) + + assert exc.value.status_code == 400 + assert "redirect_uri" in str(exc.value.detail) + + +@pytest.mark.asyncio +async def test_token_non_bridge_keeps_gateway_callback(): + """Without dcr_bridge the token exchange keeps posting the gateway callback as redirect_uri, + pinning the pre-bridge contract for the browser-only Authorize path.""" + data = await _bridge_token_post_data(_bridge_server(dcr_bridge=None), redirect_uri=_BRIDGE_CLIENT_REDIRECT) + + assert data["redirect_uri"] == "https://litellm.example.com/callback" + + +def _named_as_metadata_response(server): + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _build_oauth_authorization_server_response, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + global_mcp_server_manager.registry.clear() + global_mcp_server_manager.registry[server.server_id] = server + try: + with patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.IPAddressUtils.get_mcp_client_ip", + return_value=None, + ): + return _build_oauth_authorization_server_response( + request=_bridge_mock_request(), + mcp_server_name=server.server_name, + ) + finally: + global_mcp_server_manager.registry.clear() + + +def test_oauth_authorization_server_metadata_served_for_bridge_server(): + """Bridge servers get the gateway's AS metadata (the register, authorize, and token relays), + which is what makes the DCR front door discoverable to standard MCP clients.""" + result = _named_as_metadata_response(_bridge_server()) + + assert result["authorization_endpoint"] == "https://litellm.example.com/bridge_srv/authorize" + assert result["token_endpoint"] == "https://litellm.example.com/bridge_srv/token" + assert result["registration_endpoint"] == "https://litellm.example.com/bridge_srv/register" + + +def test_oauth_authorization_server_404_for_non_bridge_client_forwarded_server(): + """Without dcr_bridge a client-forwarded server keeps 404ing AS-metadata discovery: verbatim + upstream discovery is the contract and the gateway must not advertise itself as its AS.""" + with pytest.raises(HTTPException) as exc: + _named_as_metadata_response(_bridge_server(dcr_bridge=None)) + + assert exc.value.status_code == 404 + + +async def _bridge_register_response(server, request_payload, persist_credentials=False): + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + + mock_response = MagicMock() + mock_response.status_code = 201 + mock_response.json.return_value = { + "client_id": "upstream-issued-client", + "redirect_uris": request_payload.get("redirect_uris", []), + "token_endpoint_auth_method": "none", + } + mock_response.raise_for_status = MagicMock() + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._persist_dcr_client_registration", + new_callable=AsyncMock, + ) as mock_persist, + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._reuse_persisted_dcr_client_if_available", + new_callable=AsyncMock, + return_value=False, + ), + ): + response = await register_client_with_server( + request=_bridge_mock_request(), + mcp_server=server, + client_name=request_payload.get("client_name", ""), + grant_types=request_payload.get("grant_types"), + response_types=request_payload.get("response_types"), + token_endpoint_auth_method=request_payload.get("token_endpoint_auth_method"), + persist_credentials=persist_credentials, + client_redirect_uris=request_payload.get("redirect_uris"), + ) + return response, mock_async_client, mock_persist + + +@pytest.mark.asyncio +async def test_register_bridge_relay_forwards_client_redirect_uris(): + """The bridge relay arm registers the client's own redirect_uris upstream with public-client + defaults and relays the upstream response verbatim, so the upstream AS enforces the redirect + binding for that client and the auth code never transits the gateway.""" + import json + + response, mock_async_client, _ = await _bridge_register_response( + _bridge_server(), + {"client_name": "Claude", "redirect_uris": [_BRIDGE_CLIENT_REDIRECT]}, + ) + + posted = mock_async_client.post.call_args.kwargs["json"] + assert posted["redirect_uris"] == [_BRIDGE_CLIENT_REDIRECT] + assert posted["grant_types"] == ["authorization_code", "refresh_token"] + assert posted["response_types"] == ["code"] + assert posted["token_endpoint_auth_method"] == "none" + + payload = json.loads(response.body.decode("utf-8")) + assert payload["client_id"] == "upstream-issued-client" + + +@pytest.mark.asyncio +async def test_register_bridge_relay_requires_redirect_uris(): + with pytest.raises(HTTPException) as exc: + await _bridge_register_response(_bridge_server(), {"client_name": "Claude"}) + + assert exc.value.status_code == 400 + assert "redirect_uris" in str(exc.value.detail) + + +@pytest.mark.asyncio +async def test_register_bridge_relay_surfaces_upstream_error_not_500(): + """A bridge relay registration the upstream rejects must surface the upstream status and its + RFC 7591 error body to the client, not a bare 500 that hides the real reason.""" + import httpx + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + + error_response = MagicMock() + error_response.status_code = 400 + error_response.text = '{"error":"invalid_redirect_uri","error_description":"redirect_uri not allowed"}' + error_response.raise_for_status = MagicMock( + side_effect=httpx.HTTPStatusError("bad", request=MagicMock(), response=error_response) + ) + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=error_response) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._reuse_persisted_dcr_client_if_available", + new_callable=AsyncMock, + return_value=False, + ), + ): + with pytest.raises(HTTPException) as exc: + await register_client_with_server( + request=_bridge_mock_request(), + mcp_server=_bridge_server(), + client_name="Claude", + grant_types=None, + response_types=None, + token_endpoint_auth_method=None, + client_redirect_uris=[_BRIDGE_CLIENT_REDIRECT], + ) + + assert exc.value.status_code == 400 + assert "invalid_redirect_uri" in str(exc.value.detail) + + +@pytest.mark.asyncio +async def test_register_non_bridge_upstream_error_still_raises_500(): + """Non-bridge DCR keeps its pre-change behavior: raise_for_status propagates so the flag-off + contract is byte-identical; only the bridge relay arm relays the upstream status.""" + import httpx + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client_with_server, + ) + + error_response = MagicMock() + error_response.status_code = 400 + error_response.text = '{"error":"invalid_client_metadata"}' + error_response.raise_for_status = MagicMock( + side_effect=httpx.HTTPStatusError("bad", request=MagicMock(), response=error_response) + ) + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=error_response) + + oauth2_server = _bridge_server(auth_type=MCPAuth.oauth2, dcr_bridge=None) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._reuse_persisted_dcr_client_if_available", + new_callable=AsyncMock, + return_value=False, + ), + ): + with pytest.raises(httpx.HTTPStatusError): + await register_client_with_server( + request=_bridge_mock_request(), + mcp_server=oauth2_server, + client_name="Claude", + grant_types=None, + response_types=None, + token_endpoint_auth_method=None, + ) + + +@pytest.mark.asyncio +async def test_register_bridge_relay_never_persists(): + """Relayed registrations belong to individual clients; persisting one as the server's own DCR + client would hand every future caller the first client's identity.""" + _, _, mock_persist = await _bridge_register_response( + _bridge_server(), + {"client_name": "Claude", "redirect_uris": [_BRIDGE_CLIENT_REDIRECT]}, + persist_credentials=True, + ) + + mock_persist.assert_not_called() + + +async def _exchange_persistence_attempted_for_auth_type(auth_type) -> bool: + """Run exchange_token_with_server for a server of ``auth_type`` and report whether it attempted + to persist the exchanged token server-side. The client-forwarded token modes must not persist: + their contract is that the upstream token stays browser-held, minted/stored/refreshed nowhere.""" + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + exchange_token_with_server, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + server = MCPServer( + server_id="t", + name="t", + server_name="t", + alias="t", + transport=MCPTransport.http, + auth_type=auth_type, + client_id="cid", + client_secret="cs", + authorization_url="https://provider.com/oauth/authorize", + token_url="https://provider.com/oauth/token", + ) + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + fake_http_response = MagicMock() + fake_http_response.json.return_value = {"access_token": "tok", "refresh_token": "r", "token_type": "Bearer"} + fake_http_response.raise_for_status = MagicMock() + fake_http_client = MagicMock() + fake_http_client.post = AsyncMock(return_value=fake_http_response) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=fake_http_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._extract_user_id_from_request", + new_callable=AsyncMock, + return_value="admin-user", + ), + patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._store_per_user_token_server_side", + new_callable=AsyncMock, + ) as mock_store, + ): + await exchange_token_with_server( + request=mock_request, + mcp_server=server, + grant_type="authorization_code", + code="c", + redirect_uri="http://127.0.0.1:3000/cb", + client_id="cid", + client_secret=None, + code_verifier=None, + ) + return mock_store.await_count > 0 + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +async def test_token_exchange_does_not_persist_for_client_forwarded_modes(auth_type): + """The browser-only Authorize for true_passthrough / oauth_delegate must not write the upstream + token to the DB: these modes forward a browser-held token and persist nothing server-side.""" + assert await _exchange_persistence_attempted_for_auth_type(auth_type) is False + + +@pytest.mark.asyncio +async def test_token_exchange_persists_for_oauth2(): + """Guard the test's own discriminator: a genuine oauth2 (authorization_code) server DOES persist, + so the passthrough no-persist assertion above is meaningful and not vacuously true.""" + assert await _exchange_persistence_attempted_for_auth_type(MCPAuth.oauth2) is True + + +# ------------------------------------------------------------------- +# OBO (token_exchange) Protected Resource Metadata: discovery must name the +# JWT-auth issuer the client SSOs with, not the gateway. +# ------------------------------------------------------------------- + +_OBO_RESOURCE = "https://litellm.example.com/mcp/obo_mcp" +_PATCH_ISSUERS = "litellm.proxy._experimental.mcp_server.discoverable_endpoints._jwt_auth_issuers" + + +def _obo_server(scopes=None): + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + return MCPServer( + server_id="obo_mcp", + name="obo_mcp", + server_name="obo_mcp", + alias="obo_mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + scopes=scopes, + ) + + +def test_obo_protected_resource_response_names_jwt_issuers(): + """An OBO server's PRM points authorization_servers at the configured JWT issuers (the IdP that + mints and validates the subject token), with the gateway resource echoed back.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _obo_protected_resource_response, + ) + + with patch(_PATCH_ISSUERS, return_value=["https://idp.example.com"]): + response = _obo_protected_resource_response(_obo_server(scopes=["read"]), _OBO_RESOURCE) + assert response == { + "authorization_servers": ["https://idp.example.com"], + "resource": _OBO_RESOURCE, + "scopes_supported": ["read"], + } + + +def test_obo_protected_resource_response_scopes_default_empty(): + """A scopeless OBO server reports scopes_supported as [] rather than None.""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _obo_protected_resource_response, + ) + + with patch(_PATCH_ISSUERS, return_value=["https://idp.example.com"]): + response = _obo_protected_resource_response(_obo_server(scopes=None), _OBO_RESOURCE) + assert response["scopes_supported"] == [] + + +def test_obo_protected_resource_response_falls_back_when_no_issuer(): + """With no JWT issuer configured, the OBO branch returns None so the caller falls back to the + gateway-default PRM (discovery still works, it just can't name the IdP).""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _obo_protected_resource_response, + ) + + with patch(_PATCH_ISSUERS, return_value=[]): + assert _obo_protected_resource_response(_obo_server(), _OBO_RESOURCE) is None + + +def test_obo_protected_resource_response_ignores_non_obo_server(): + """Non-OBO servers are not handled by this branch (returns None -> gateway default).""" + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _obo_protected_resource_response, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + oauth2_server = MCPServer( + server_id="oauth2_mcp", + name="oauth2_mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + ) + assert _obo_protected_resource_response(oauth2_server, _OBO_RESOURCE) is None + + +@pytest.mark.asyncio +async def test_build_oauth_protected_resource_response_obo_end_to_end(): + """End to end through the response builder: an OBO server's PRM advertises the JWT issuer as + authorization_servers, proving the extracted branch is wired into the public discovery path.""" + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _build_oauth_protected_resource_response, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + global_mcp_server_manager.registry.clear() + global_mcp_server_manager.registry["obo_mcp"] = _obo_server(scopes=["read"]) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + try: + with patch(_PATCH_ISSUERS, return_value=["https://idp.example.com"]): + response = await _build_oauth_protected_resource_response( + request=mock_request, + mcp_server_name="obo_mcp", + use_standard_pattern=True, + ) + assert response["authorization_servers"] == ["https://idp.example.com"] + assert response["resource"] == "https://litellm.example.com/mcp/obo_mcp" + finally: + global_mcp_server_manager.registry.clear() + + def _token_request(headers): """A real Starlette request with case-insensitive headers (matches production).""" from starlette.requests import Request @@ -3011,3 +4829,480 @@ async def test_token_endpoint_client_secret_basic_without_secret_returns_400(): code_verifier="verifier", ) assert exc_info.value.status_code == 400 + + +# ------------------------------------------------------------------- +# Non-oauth2 (auth_type=none, access-group gated) servers must not be +# driven through the gateway OAuth authorize/token/register/discovery +# flow, and must not be advertised as OAuth-protected in discovery docs. +# ------------------------------------------------------------------- + + +def _access_group_none_server(server_name="access_group_server"): + """A non-oauth2, access-group gated MCP server: no client_id, no OAuth.""" + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + return MCPServer( + server_id=server_name, + name=server_name, + server_name=server_name, + alias=server_name, + transport=MCPTransport.http, + auth_type=MCPAuth.none, + access_groups=["eng"], + ) + + +@pytest.mark.asyncio +async def test_authorize_endpoint_rejects_non_oauth2_server(): + """authorize() against a none-auth server returns an accurate 'does not use OAuth' 400, + not the misleading 'client_id is required' that fired before the auth_type was checked.""" + try: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + authorize, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + server = _access_group_none_server() + global_mcp_server_manager.registry[server.server_id] = server + + mock_request = MagicMock() + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + try: + with pytest.raises(HTTPException) as exc_info: + await authorize( + request=mock_request, + client_id=None, + mcp_server_name="access_group_server", + redirect_uri="http://127.0.0.1:60108/callback", + state="test_state", + ) + assert exc_info.value.status_code == 400 + detail_text = str(exc_info.value.detail) + assert "does not use OAuth" in detail_text + assert "client_id is required" not in detail_text + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +async def test_token_endpoint_rejects_non_oauth2_server(): + """token_endpoint() against a none-auth server returns 'does not use OAuth' 400 instead + of the misleading 'token url is not set'.""" + try: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + token_endpoint, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + server = _access_group_none_server() + global_mcp_server_manager.registry[server.server_id] = server + + mock_request = MagicMock() + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + try: + with pytest.raises(HTTPException) as exc_info: + await token_endpoint( + request=mock_request, + grant_type="authorization_code", + code="auth-code", + redirect_uri="http://localhost/callback", + client_id="some-client", + mcp_server_name="access_group_server", + client_secret=None, + code_verifier="verifier", + ) + assert exc_info.value.status_code == 400 + detail_text = str(exc_info.value.detail) + assert "does not use OAuth" in detail_text + assert "token url is not set" not in detail_text + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +async def test_register_client_rejects_non_oauth2_server(): + """register_client() against a named none-auth server returns 'does not use OAuth' 400 + instead of the misleading 'authorization url is not set'.""" + try: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + register_client, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + server = _access_group_none_server() + global_mcp_server_manager.registry[server.server_id] = server + + mock_request = MagicMock() + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + try: + with pytest.raises(HTTPException) as exc_info: + with patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints._read_request_body", + new=AsyncMock(return_value={}), + ): + await register_client(request=mock_request, mcp_server_name="access_group_server") + assert exc_info.value.status_code == 400 + detail_text = str(exc_info.value.detail) + assert "does not use OAuth" in detail_text + assert "authorization url is not set" not in detail_text + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +async def test_oauth_protected_resource_404_for_non_oauth2_server(): + """Discovery must not advertise a none-auth server as an OAuth-protected resource.""" + try: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _build_oauth_protected_resource_response, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + server = _access_group_none_server() + global_mcp_server_manager.registry[server.server_id] = server + + mock_request = MagicMock() + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + try: + with pytest.raises(HTTPException) as exc_info: + await _build_oauth_protected_resource_response( + request=mock_request, + mcp_server_name="access_group_server", + use_standard_pattern=False, + ) + assert exc_info.value.status_code == 404 + assert "not an OAuth-protected resource" in str(exc_info.value.detail) + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +async def test_oauth_authorization_server_404_for_non_oauth2_server(): + """Discovery must not advertise a none-auth server as an OAuth authorization server.""" + try: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _build_oauth_authorization_server_response, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + server = _access_group_none_server() + global_mcp_server_manager.registry[server.server_id] = server + + mock_request = MagicMock() + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + try: + with pytest.raises(HTTPException) as exc_info: + _build_oauth_authorization_server_response( + request=mock_request, + mcp_server_name="access_group_server", + ) + assert exc_info.value.status_code == 404 + assert "not an OAuth authorization server" in str(exc_info.value.detail) + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +async def test_oauth_protected_resource_passthrough_none_auth_not_404(): + """Regression guard for the protected-resource auth_type gate placement: a none-auth + server that opted into OAuth pass-through must still proxy upstream metadata, it must + NOT be 404'd. The gate has to sit after the pass-through branch.""" + try: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _build_oauth_protected_resource_response, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + passthrough_server = MCPServer( + server_id="passthrough_server", + name="passthrough_server", + server_name="passthrough_server", + alias="passthrough_server", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.none, + oauth_passthrough=True, + extra_headers=["Authorization"], + ) + global_mcp_server_manager.registry[passthrough_server.server_id] = passthrough_server + + mock_request = MagicMock() + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + try: + with patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.fetch_upstream_oauth_protected_resource", + new=AsyncMock(return_value={"authorization_servers": ["https://upstream-idp.example.com"]}), + ): + response = await _build_oauth_protected_resource_response( + request=mock_request, + mcp_server_name="passthrough_server", + use_standard_pattern=False, + ) + assert response["authorization_servers"] == ["https://upstream-idp.example.com"] + assert response["resource"].endswith("/passthrough_server/mcp") + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +async def test_oauth_protected_resource_404_for_unknown_server_name(): + """A discovery request for an unknown server name returns the same 404 as a non-oauth2 + server (not a 200 metadata doc with broken URLs), so the well-known paths cannot be used + to enumerate non-OAuth server names.""" + try: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _build_oauth_protected_resource_response, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + mock_request = MagicMock() + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + with pytest.raises(HTTPException) as exc_info: + await _build_oauth_protected_resource_response( + request=mock_request, + mcp_server_name="does_not_exist", + use_standard_pattern=True, + ) + assert exc_info.value.status_code == 404 + + +@pytest.mark.asyncio +async def test_oauth_authorization_server_404_for_unknown_server_name(): + """A named authorization-server discovery request for an unknown server returns 404, not a + 200 metadata document pointing at non-existent /{name}/authorize and /{name}/token.""" + try: + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _build_oauth_authorization_server_response, + ) + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + except ImportError: + pytest.skip("MCP discoverable endpoints not available") + + global_mcp_server_manager.registry.clear() + mock_request = MagicMock() + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + with pytest.raises(HTTPException) as exc_info: + _build_oauth_authorization_server_response( + request=mock_request, + mcp_server_name="does_not_exist", + ) + assert exc_info.value.status_code == 404 + + +@pytest.mark.asyncio +async def test_store_per_user_token_server_side_invalidates_v2_token_cache(): + """A token stored by the OAuth callback (code exchange or refresh) drops the v2 per-user + token cache entry, so egress stops serving the replaced token immediately instead of + until its TTL.""" + from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _store_per_user_token_server_side, + ) + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + server = MCPServer( + server_id="srv-cb-1", + name="cb_server", + url="https://upstream.example/mcp", + transport="http", + auth_type=MCPAuth.oauth2, + ) + invalidate_mock = AsyncMock(return_value=None) + cache_set_mock = AsyncMock(return_value=None) + + with ( + patch( + "litellm.proxy.utils.get_prisma_client_or_throw", + return_value=MagicMock(), + ), + patch( + "litellm.proxy._experimental.mcp_server.db.store_user_oauth_credential", + new=AsyncMock(return_value=None), + ), + patch( + "litellm.proxy._experimental.mcp_server.oauth2_token_cache.mcp_per_user_token_cache.set", + new=cache_set_mock, + ), + patch.object( + manager_module.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + new=invalidate_mock, + ), + ): + await _store_per_user_token_server_side( + server=server, + user_id="user-cb-1", + token_response={"access_token": "fresh-tok", "expires_in": 3600}, + ) + + invalidate_mock.assert_awaited_once_with("user-cb-1", "srv-cb-1") + cache_set_mock.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_store_per_user_token_server_side_skips_invalidate_when_db_write_fails(): + """A failed DB write neither warms the v1 cache nor drops the v2 cache entry; the + previously stored token is still the truth.""" + from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + _store_per_user_token_server_side, + ) + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + server = MCPServer( + server_id="srv-cb-2", + name="cb_server_2", + url="https://upstream.example/mcp", + transport="http", + auth_type=MCPAuth.oauth2, + ) + invalidate_mock = AsyncMock(return_value=None) + cache_set_mock = AsyncMock(return_value=None) + + with ( + patch( + "litellm.proxy.utils.get_prisma_client_or_throw", + return_value=MagicMock(), + ), + patch( + "litellm.proxy._experimental.mcp_server.db.store_user_oauth_credential", + new=AsyncMock(side_effect=RuntimeError("db down")), + ), + patch( + "litellm.proxy._experimental.mcp_server.oauth2_token_cache.mcp_per_user_token_cache.set", + new=cache_set_mock, + ), + patch.object( + manager_module.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + new=invalidate_mock, + ), + ): + await _store_per_user_token_server_side( + server=server, + user_id="user-cb-2", + token_response={"access_token": "fresh-tok", "expires_in": 3600}, + ) + + invalidate_mock.assert_not_awaited() + cache_set_mock.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_token_exchange_pairs_client_secret_with_server_client_id(): + """Re-auth regression: the register short-circuit hands the browser a placeholder + ``client_secret: "dummy"``, which the browser echoes back to /token. The server-side + persisted client_id wins the resolution, so the secret must come from the same (server) + source; pairing the persisted public PKCE client (no stored secret) with the caller's + placeholder makes the IdP reject the exchange with 401 on every re-auth.""" + from fastapi import Request + + from litellm.proxy._experimental.mcp_server.discoverable_endpoints import ( + exchange_token_with_server, + ) + from litellm.proxy._types import MCPTransport + from litellm.types.mcp import MCPAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + server = MCPServer( + server_id="srv-1", + name="srv-1", + server_name="srv-1", + alias="srv-1", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + client_id="persisted-client", + client_secret=None, + authorization_url="https://provider.example/oauth/authorize", + token_url="https://provider.example/oauth/token", + ) + + mock_request = MagicMock(spec=Request) + mock_request.base_url = "https://litellm.example.com/" + mock_request.headers = {} + + mock_response = MagicMock() + mock_response.raise_for_status = MagicMock() + mock_response.json.return_value = {"access_token": "at", "token_type": "Bearer"} + mock_async_client = MagicMock() + mock_async_client.post = AsyncMock(return_value=mock_response) + + with patch( + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.get_async_httpx_client", + return_value=mock_async_client, + ): + await exchange_token_with_server( + request=mock_request, + mcp_server=server, + grant_type="authorization_code", + code="auth-code", + redirect_uri="https://litellm.example.com/ui/mcp/oauth/callback", + client_id="srv-1", + client_secret="dummy", + code_verifier="verifier", + ) + + sent = mock_async_client.post.call_args.kwargs["data"] + assert sent["client_id"] == "persisted-client" + assert "client_secret" not in sent diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_hook_extra_headers.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_hook_extra_headers.py index 363948ff4e6..73486fe0b6a 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_hook_extra_headers.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_hook_extra_headers.py @@ -43,17 +43,13 @@ class TestConvertMcpHookResponseToKwargs: def test_extracts_modified_arguments(self): original = {"arguments": {"old": "value"}} response = {"modified_arguments": {"new": "value"}} - result = self.proxy_logging._convert_mcp_hook_response_to_kwargs( - response, original - ) + result = self.proxy_logging._convert_mcp_hook_response_to_kwargs(response, original) assert result["arguments"] == {"new": "value"} def test_extracts_extra_headers(self): original = {"arguments": {"key": "val"}} response = {"extra_headers": {"Authorization": "Bearer signed-jwt"}} - result = self.proxy_logging._convert_mcp_hook_response_to_kwargs( - response, original - ) + result = self.proxy_logging._convert_mcp_hook_response_to_kwargs(response, original) assert result["extra_headers"] == {"Authorization": "Bearer signed-jwt"} def test_extracts_both_arguments_and_headers(self): @@ -62,9 +58,7 @@ class TestConvertMcpHookResponseToKwargs: "modified_arguments": {"new": "value"}, "extra_headers": {"X-Custom": "header-val"}, } - result = self.proxy_logging._convert_mcp_hook_response_to_kwargs( - response, original - ) + result = self.proxy_logging._convert_mcp_hook_response_to_kwargs(response, original) assert result["arguments"] == {"new": "value"} assert result["extra_headers"] == {"X-Custom": "header-val"} @@ -72,9 +66,7 @@ class TestConvertMcpHookResponseToKwargs: """Backward compat: hooks that only return modified_arguments still work.""" original = {"arguments": {"key": "val"}} response = {"modified_arguments": {"key": "new_val"}} - result = self.proxy_logging._convert_mcp_hook_response_to_kwargs( - response, original - ) + result = self.proxy_logging._convert_mcp_hook_response_to_kwargs(response, original) assert "extra_headers" not in result assert result["arguments"] == {"key": "new_val"} @@ -82,9 +74,7 @@ class TestConvertMcpHookResponseToKwargs: """Empty dict for extra_headers is falsy and should not be set.""" original = {"arguments": {"key": "val"}} response = {"extra_headers": {}} - result = self.proxy_logging._convert_mcp_hook_response_to_kwargs( - response, original - ) + result = self.proxy_logging._convert_mcp_hook_response_to_kwargs(response, original) assert "extra_headers" not in result @@ -107,18 +97,10 @@ class TestPreCallToolCheckReturnsHeaders: server = self._make_server() proxy_logging = MagicMock(spec=ProxyLogging) - proxy_logging._create_mcp_request_object_from_kwargs = MagicMock( - return_value=MagicMock() - ) - proxy_logging._convert_mcp_to_llm_format = MagicMock( - return_value={"model": "fake"} - ) - proxy_logging.pre_call_hook = AsyncMock( - return_value={"modified_arguments": {"key": "val"}} - ) - proxy_logging._convert_mcp_hook_response_to_kwargs = MagicMock( - return_value={"arguments": {"key": "val"}} - ) + proxy_logging._create_mcp_request_object_from_kwargs = MagicMock(return_value=MagicMock()) + proxy_logging._convert_mcp_to_llm_format = MagicMock(return_value={"model": "fake"}) + proxy_logging.pre_call_hook = AsyncMock(return_value={"modified_arguments": {"key": "val"}}) + proxy_logging._convert_mcp_hook_response_to_kwargs = MagicMock(return_value={"arguments": {"key": "val"}}) with patch.object(manager, "check_allowed_or_banned_tools", return_value=True): with patch.object( @@ -146,15 +128,9 @@ class TestPreCallToolCheckReturnsHeaders: hook_headers = {"Authorization": "Bearer signed-jwt", "X-Trace-Id": "abc123"} proxy_logging = MagicMock(spec=ProxyLogging) - proxy_logging._create_mcp_request_object_from_kwargs = MagicMock( - return_value=MagicMock() - ) - proxy_logging._convert_mcp_to_llm_format = MagicMock( - return_value={"model": "fake"} - ) - proxy_logging.pre_call_hook = AsyncMock( - return_value={"extra_headers": hook_headers} - ) + proxy_logging._create_mcp_request_object_from_kwargs = MagicMock(return_value=MagicMock()) + proxy_logging._convert_mcp_to_llm_format = MagicMock(return_value={"model": "fake"}) + proxy_logging.pre_call_hook = AsyncMock(return_value={"extra_headers": hook_headers}) proxy_logging._convert_mcp_hook_response_to_kwargs = MagicMock( return_value={"arguments": {"key": "val"}, "extra_headers": hook_headers} ) @@ -183,12 +159,8 @@ class TestPreCallToolCheckReturnsHeaders: server = self._make_server() proxy_logging = MagicMock(spec=ProxyLogging) - proxy_logging._create_mcp_request_object_from_kwargs = MagicMock( - return_value=MagicMock() - ) - proxy_logging._convert_mcp_to_llm_format = MagicMock( - return_value={"model": "fake"} - ) + proxy_logging._create_mcp_request_object_from_kwargs = MagicMock(return_value=MagicMock()) + proxy_logging._convert_mcp_to_llm_format = MagicMock(return_value={"model": "fake"}) proxy_logging.pre_call_hook = AsyncMock(return_value=None) with patch.object(manager, "check_allowed_or_banned_tools", return_value=True): @@ -219,18 +191,10 @@ class TestPreCallToolCheckReturnsHeaders: modified_args = {"key": "modified", "extra": "added"} proxy_logging = MagicMock(spec=ProxyLogging) - proxy_logging._create_mcp_request_object_from_kwargs = MagicMock( - return_value=MagicMock() - ) - proxy_logging._convert_mcp_to_llm_format = MagicMock( - return_value={"model": "fake"} - ) - proxy_logging.pre_call_hook = AsyncMock( - return_value={"modified_arguments": modified_args} - ) - proxy_logging._convert_mcp_hook_response_to_kwargs = MagicMock( - return_value={"arguments": modified_args} - ) + proxy_logging._create_mcp_request_object_from_kwargs = MagicMock(return_value=MagicMock()) + proxy_logging._convert_mcp_to_llm_format = MagicMock(return_value={"model": "fake"}) + proxy_logging.pre_call_hook = AsyncMock(return_value={"modified_arguments": modified_args}) + proxy_logging._convert_mcp_hook_response_to_kwargs = MagicMock(return_value={"arguments": modified_args}) with patch.object(manager, "check_allowed_or_banned_tools", return_value=True): with patch.object( @@ -260,12 +224,8 @@ class TestPreCallToolCheckReturnsHeaders: hook_headers = {"Authorization": "Bearer jwt"} proxy_logging = MagicMock(spec=ProxyLogging) - proxy_logging._create_mcp_request_object_from_kwargs = MagicMock( - return_value=MagicMock() - ) - proxy_logging._convert_mcp_to_llm_format = MagicMock( - return_value={"model": "fake"} - ) + proxy_logging._create_mcp_request_object_from_kwargs = MagicMock(return_value=MagicMock()) + proxy_logging._convert_mcp_to_llm_format = MagicMock(return_value={"model": "fake"}) proxy_logging.pre_call_hook = AsyncMock(return_value={"dummy": True}) proxy_logging._convert_mcp_hook_response_to_kwargs = MagicMock( return_value={"arguments": modified_args, "extra_headers": hook_headers} @@ -345,9 +305,7 @@ class TestCallToolFlowsHookHeaders: mock_call.assert_called_once() call_kwargs = mock_call.call_args - assert ( - call_kwargs.kwargs.get("hook_extra_headers") == hook_headers - ) + assert call_kwargs.kwargs.get("hook_extra_headers") == hook_headers @pytest.mark.asyncio async def test_no_hook_headers_when_no_proxy_logging(self): @@ -434,9 +392,7 @@ class TestCallToolFlowsHookHeaders: spec_path="/path/to/spec.yaml", ) - with patch.object( - manager, "_get_mcp_server_from_tool_name", return_value=server - ): + with patch.object(manager, "_get_mcp_server_from_tool_name", return_value=server): with patch.object( manager, "pre_call_tool_check", @@ -467,10 +423,7 @@ class TestCallToolFlowsHookHeaders: proxy_logging_obj=proxy_logging, ) mock_logger.warning.assert_called_once() - assert ( - "header injection is not supported" - in mock_logger.warning.call_args[0][0] - ) + assert "header injection is not supported" in mock_logger.warning.call_args[0][0] @pytest.mark.asyncio async def test_openapi_server_no_error_without_hook_headers(self): @@ -486,9 +439,7 @@ class TestCallToolFlowsHookHeaders: spec_path="/path/to/spec.yaml", ) - with patch.object( - manager, "_get_mcp_server_from_tool_name", return_value=server - ): + with patch.object(manager, "_get_mcp_server_from_tool_name", return_value=server): with patch.object( manager, "pre_call_tool_check", @@ -539,25 +490,19 @@ class TestHookHeaderMergePriority: async def test_hook_headers_override_static_headers(self): """Hook headers should take precedence over static_headers.""" manager = MCPServerManager() - server = self._make_server( - static_headers={"Authorization": "Bearer static-token", "X-Static": "yes"} - ) + server = self._make_server(static_headers={"Authorization": "Bearer static-token", "X-Static": "yes"}) hook_headers = {"Authorization": "Bearer hook-signed-jwt"} captured_extra_headers: Dict[str, Any] = {} - async def fake_create_mcp_client( - server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs - ): + async def fake_create_mcp_client(server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs): captured_extra_headers["value"] = extra_headers mock_client = MagicMock() mock_client.call_tool = AsyncMock(return_value=MagicMock()) return mock_client - with patch.object( - manager, "_create_mcp_client", side_effect=fake_create_mcp_client - ): + with patch.object(manager, "_create_mcp_client", side_effect=fake_create_mcp_client): with patch.object(manager, "_build_stdio_env", return_value=None): try: await manager._call_regular_mcp_tool( @@ -587,17 +532,13 @@ class TestHookHeaderMergePriority: captured_extra_headers: Dict[str, Any] = {} - async def fake_create_mcp_client( - server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs - ): + async def fake_create_mcp_client(server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs): captured_extra_headers["value"] = extra_headers mock_client = MagicMock() mock_client.call_tool = AsyncMock(return_value=MagicMock()) return mock_client - with patch.object( - manager, "_create_mcp_client", side_effect=fake_create_mcp_client - ): + with patch.object(manager, "_create_mcp_client", side_effect=fake_create_mcp_client): with patch.object(manager, "_build_stdio_env", return_value=None): try: await manager._call_regular_mcp_tool( @@ -633,17 +574,13 @@ class TestHookHeaderMergePriority: captured_extra_headers: Dict[str, Any] = {} - async def fake_create_mcp_client( - server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs - ): + async def fake_create_mcp_client(server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs): captured_extra_headers["value"] = extra_headers mock_client = MagicMock() mock_client.call_tool = AsyncMock(return_value=MagicMock()) return mock_client - with patch.object( - manager, "_create_mcp_client", side_effect=fake_create_mcp_client - ): + with patch.object(manager, "_create_mcp_client", side_effect=fake_create_mcp_client): with patch.object(manager, "_build_stdio_env", return_value=None): try: await manager._call_regular_mcp_tool( @@ -689,17 +626,13 @@ class TestHookHeaderMergePriority: captured_extra_headers: Dict[str, Any] = {} - async def fake_create_mcp_client( - server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs - ): + async def fake_create_mcp_client(server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs): captured_extra_headers["value"] = extra_headers mock_client = MagicMock() mock_client.call_tool = AsyncMock(return_value=MagicMock()) return mock_client - with patch.object( - manager, "_create_mcp_client", side_effect=fake_create_mcp_client - ): + with patch.object(manager, "_create_mcp_client", side_effect=fake_create_mcp_client): with patch.object(manager, "_build_stdio_env", return_value=None): try: await manager._call_regular_mcp_tool( @@ -737,17 +670,13 @@ class TestHookHeaderMergePriority: captured_extra_headers: Dict[str, Any] = {} - async def fake_create_mcp_client( - server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs - ): + async def fake_create_mcp_client(server, mcp_auth_header=None, extra_headers=None, stdio_env=None, **kwargs): captured_extra_headers["value"] = extra_headers mock_client = MagicMock() mock_client.call_tool = AsyncMock(return_value=MagicMock()) return mock_client - with patch.object( - manager, "_create_mcp_client", side_effect=fake_create_mcp_client - ): + with patch.object(manager, "_create_mcp_client", side_effect=fake_create_mcp_client): with patch.object(manager, "_build_stdio_env", return_value=None): try: await manager._call_regular_mcp_tool( @@ -822,9 +751,7 @@ class TestMcpRateLimitServerNameSurfacing: request_obj.tool_name = "list_repos" request_obj.arguments = {"org": "acme"} - result = self.proxy_logging._convert_mcp_to_llm_format( - request_obj, {"mcp_rate_limit_server_name": "github"} - ) + result = self.proxy_logging._convert_mcp_to_llm_format(request_obj, {"mcp_rate_limit_server_name": "github"}) assert result["mcp_server_name"] == "github" @@ -861,16 +788,10 @@ class TestMcpRateLimitServerNameSurfacing: return {"model": "fake"} proxy_logging = MagicMock(spec=ProxyLogging) - proxy_logging._create_mcp_request_object_from_kwargs = MagicMock( - return_value=MagicMock() - ) - proxy_logging._convert_mcp_to_llm_format = MagicMock( - side_effect=capture_convert - ) + proxy_logging._create_mcp_request_object_from_kwargs = MagicMock(return_value=MagicMock()) + proxy_logging._convert_mcp_to_llm_format = MagicMock(side_effect=capture_convert) proxy_logging.pre_call_hook = AsyncMock(return_value=None) - proxy_logging._convert_mcp_hook_response_to_kwargs = MagicMock( - return_value={"arguments": {}} - ) + proxy_logging._convert_mcp_hook_response_to_kwargs = MagicMock(return_value={"arguments": {}}) with patch.object(manager, "check_allowed_or_banned_tools", return_value=True): with patch.object( @@ -889,3 +810,226 @@ class TestMcpRateLimitServerNameSurfacing: ) assert captured["kwargs"]["mcp_rate_limit_server_name"] == "gh" + + +class TestOpenApiByokCallTool: + @pytest.mark.asyncio + async def test_call_tool_openapi_byok_injects_request_auth_contextvar(self): + """Playground/responses call call_tool directly; BYOK must reach OpenAPI handlers.""" + from litellm.proxy._experimental.mcp_server.openapi_to_mcp_generator import ( + _request_auth_header, + ) + + manager = MCPServerManager() + server = MCPServer( + server_id="byok-openapi", + name="firecrawl_byok_test", + server_name="firecrawl_byok_test", + url="https://api.firecrawl.dev", + transport=MCPTransport.http, + auth_type=MCPAuth.api_key, + spec_path="https://example.com/openapi.json", + is_byok=True, + ) + user_auth = UserAPIKeyAuth(user_id="default_user_id", api_key="sk-dashboard") + captured_auth: dict[str, Optional[str]] = {} + + async def fake_openapi_handler(_server, _name, _arguments): + captured_auth["value"] = _request_auth_header.get() + return MagicMock() + + with patch.object(manager, "_resolve_mcp_server_for_tool_call", return_value=server): + with patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager._resolve_byok_mcp_auth_header", + new=AsyncMock(return_value="fc-test-key"), + ): + with patch.object( + manager, + "_call_openapi_tool_handler", + side_effect=fake_openapi_handler, + ): + await manager.call_tool( + server_name=server.server_name, + name="scrapeandextractfromurl", + arguments={"body": {"url": "https://example.com"}}, + user_api_key_auth=user_auth, + ) + + assert captured_auth["value"] == "ApiKey fc-test-key" + + +class TestFormatByokOpenapiAuthHeader: + def _server(self, auth_type): + return MCPServer( + server_id="s1", + name="s1", + server_name="s1", + url="https://example.com", + transport=MCPTransport.http, + auth_type=auth_type, + ) + + def test_api_key_auth_type(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _format_byok_openapi_auth_header, + ) + + assert _format_byok_openapi_auth_header(self._server(MCPAuth.api_key), "secret") == "ApiKey secret" + + def test_basic_auth_type(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _format_byok_openapi_auth_header, + ) + + assert _format_byok_openapi_auth_header(self._server(MCPAuth.basic), "secret") == "Basic secret" + + def test_defaults_to_bearer(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _format_byok_openapi_auth_header, + ) + + assert _format_byok_openapi_auth_header(self._server(MCPAuth.oauth2), "secret") == "Bearer secret" + + +class TestOpenapiForwardedExtraHeaders: + def _server(self, extra_headers): + return MCPServer( + server_id="s1", + name="s1", + server_name="s1", + url="https://example.com", + transport=MCPTransport.http, + extra_headers=extra_headers, + ) + + def test_returns_none_without_extra_headers_config(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _openapi_forwarded_extra_headers, + ) + + server = self._server(None) + assert _openapi_forwarded_extra_headers(server, {"X-Custom": "v"}, None) is None + + def test_returns_none_without_raw_headers(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _openapi_forwarded_extra_headers, + ) + + server = self._server(["X-Custom"]) + assert _openapi_forwarded_extra_headers(server, None, None) is None + + def test_forwards_configured_header_case_insensitively(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _openapi_forwarded_extra_headers, + ) + + server = self._server(["X-Custom"]) + result = _openapi_forwarded_extra_headers(server, {"x-custom": "v"}, None) + assert result == {"X-Custom": "v"} + + def test_returns_none_when_no_configured_header_is_present(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _openapi_forwarded_extra_headers, + ) + + server = self._server(["X-Missing"]) + assert _openapi_forwarded_extra_headers(server, {"x-custom": "v"}, None) is None + + def test_skips_authorization_when_caller_header_must_be_stripped(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _openapi_forwarded_extra_headers, + ) + + server = self._server(["Authorization"]) + server.auth_type = MCPAuth.oauth2_token_exchange + result = _openapi_forwarded_extra_headers(server, {"authorization": "Bearer caller-token"}, None) + assert result is None + + def test_skips_non_string_header_entries(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _openapi_forwarded_extra_headers, + ) + + server = self._server(["X-Custom"]) + server.extra_headers = [123, "X-Custom"] # simulate malformed legacy config data + result = _openapi_forwarded_extra_headers(server, {"x-custom": "v"}, None) + assert result == {"X-Custom": "v"} + + +class TestResolveByokMcpAuthHeader: + def _server(self, is_byok): + return MCPServer( + server_id="s1", + name="s1", + server_name="s1", + url="https://example.com", + transport=MCPTransport.http, + is_byok=is_byok, + ) + + @pytest.mark.asyncio + async def test_non_byok_server_passes_header_through_unchanged(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _resolve_byok_mcp_auth_header, + ) + + server = self._server(is_byok=False) + result = await _resolve_byok_mcp_auth_header(server, None, "caller-header") + assert result == "caller-header" + + @pytest.mark.asyncio + async def test_byok_server_uses_stored_credential_when_no_header_supplied(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _resolve_byok_mcp_auth_header, + ) + + server = self._server(is_byok=True) + user_auth = UserAPIKeyAuth(user_id="user-1", api_key="sk-dashboard") + + with patch( + "litellm.proxy._experimental.mcp_server.server._get_byok_credential", + new=AsyncMock(return_value="stored-cred"), + ): + result = await _resolve_byok_mcp_auth_header(server, user_auth, None) + + assert result == "stored-cred" + + @pytest.mark.asyncio + async def test_byok_server_raises_401_when_no_credential_stored(self): + from fastapi import HTTPException + + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _resolve_byok_mcp_auth_header, + ) + + server = self._server(is_byok=True) + user_auth = UserAPIKeyAuth(user_id="user-1", api_key="sk-dashboard") + + with patch( + "litellm.proxy._experimental.mcp_server.server._get_byok_credential", + new=AsyncMock(return_value=None), + ): + with pytest.raises(HTTPException) as exc_info: + await _resolve_byok_mcp_auth_header(server, user_auth, None) + + assert exc_info.value.status_code == 401 + assert exc_info.value.detail["error"] == "byok_auth_required" + + @pytest.mark.asyncio + async def test_byok_server_checks_credential_and_keeps_caller_header_when_supplied(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _resolve_byok_mcp_auth_header, + ) + + server = self._server(is_byok=True) + user_auth = UserAPIKeyAuth(user_id="user-1", api_key="sk-dashboard") + check_mock = AsyncMock(return_value=None) + + with patch( + "litellm.proxy._experimental.mcp_server.server._check_byok_credential", + new=check_mock, + ): + result = await _resolve_byok_mcp_auth_header(server, user_auth, "caller-header") + + check_mock.assert_awaited_once_with(server, user_auth) + assert result == "caller-header" diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_max_concurrent_requests.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_max_concurrent_requests.py new file mode 100644 index 00000000000..e11897b65c2 --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_max_concurrent_requests.py @@ -0,0 +1,203 @@ +import asyncio +from typing import Dict, Optional + +import pytest +from unittest.mock import patch + +from litellm.proxy._experimental.mcp_server.mcp_server_manager import MCPServerManager +from litellm.types.mcp import MCPAuth, MCPTransport +from litellm.types.mcp_server.mcp_server_manager import MCPServer + +HOLD_SECONDS = 0.1 + + +class _ConcurrencyTracker: + """Records how many call_tool invocations are simultaneously in flight.""" + + def __init__(self) -> None: + self.current_by_server: Dict[str, int] = {} + self.peak_by_server: Dict[str, int] = {} + self.global_current = 0 + self.global_peak = 0 + + def enter(self, server_id: str) -> None: + self.current_by_server[server_id] = self.current_by_server.get(server_id, 0) + 1 + self.peak_by_server[server_id] = max(self.peak_by_server.get(server_id, 0), self.current_by_server[server_id]) + self.global_current += 1 + self.global_peak = max(self.global_peak, self.global_current) + + def exit(self, server_id: str) -> None: + self.current_by_server[server_id] -= 1 + self.global_current -= 1 + + +def _make_server(server_id: str, max_concurrent_requests: Optional[int]) -> MCPServer: + return MCPServer( + server_id=server_id, + name=server_id, + server_name=server_id, + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.none, + max_concurrent_requests=max_concurrent_requests, + ) + + +def _patch_client_with_tracker(manager: MCPServerManager, tracker: _ConcurrencyTracker): + async def fake_create_mcp_client(server, **kwargs): + class _ProbeClient: + async def call_tool(self, params, host_progress_callback=None): + tracker.enter(server.server_id) + try: + await asyncio.sleep(HOLD_SECONDS) + return "ok" + finally: + tracker.exit(server.server_id) + + return _ProbeClient() + + return patch.object(manager, "_create_mcp_client", side_effect=fake_create_mcp_client) + + +async def _fire(manager: MCPServerManager, server: MCPServer, n: int) -> None: + await asyncio.gather( + *[ + manager._call_regular_mcp_tool( + mcp_server=server, + original_tool_name="tool", + arguments={}, + tasks=[], + mcp_auth_header=None, + mcp_server_auth_headers=None, + oauth2_headers=None, + raw_headers=None, + proxy_logging_obj=None, + ) + for _ in range(n) + ] + ) + + +@pytest.mark.asyncio +async def test_max_concurrent_requests_caps_in_flight_tool_calls(): + """A configured cap of 2 must never let more than 2 calls hit one server at once.""" + manager = MCPServerManager() + tracker = _ConcurrencyTracker() + server = _make_server("srv-limited", max_concurrent_requests=2) + + with _patch_client_with_tracker(manager, tracker): + await _fire(manager, server, n=8) + + assert tracker.peak_by_server["srv-limited"] == 2 + + +@pytest.mark.asyncio +async def test_unset_limit_allows_unbounded_concurrency(): + """With no cap, all calls run concurrently (backward-compatible default).""" + manager = MCPServerManager() + tracker = _ConcurrencyTracker() + server = _make_server("srv-unbounded", max_concurrent_requests=None) + + with _patch_client_with_tracker(manager, tracker): + await _fire(manager, server, n=6) + + assert tracker.peak_by_server["srv-unbounded"] == 6 + + +@pytest.mark.asyncio +async def test_non_positive_limit_is_treated_as_unlimited(): + """A cap of 0 must not deadlock; it means unlimited, not a zero-permit semaphore.""" + manager = MCPServerManager() + tracker = _ConcurrencyTracker() + server = _make_server("srv-zero", max_concurrent_requests=0) + + with _patch_client_with_tracker(manager, tracker): + await asyncio.wait_for(_fire(manager, server, n=5), timeout=5) + + assert tracker.peak_by_server["srv-zero"] == 5 + + +@pytest.mark.asyncio +async def test_limit_is_scoped_per_server(): + """Each server gets its own limiter; one server's cap must not throttle another.""" + manager = MCPServerManager() + tracker = _ConcurrencyTracker() + server_a = _make_server("srv-a", max_concurrent_requests=1) + server_b = _make_server("srv-b", max_concurrent_requests=1) + + with _patch_client_with_tracker(manager, tracker): + await asyncio.gather( + _fire(manager, server_a, n=3), + _fire(manager, server_b, n=3), + ) + + assert tracker.peak_by_server["srv-a"] == 1 + assert tracker.peak_by_server["srv-b"] == 1 + assert tracker.global_peak == 2 + + +@pytest.mark.asyncio +async def test_openapi_backed_server_also_respects_the_cap(): + """OpenAPI (spec_path) servers dispatch through a different handler; the cap + must apply there too, not only on the regular MCP client path.""" + manager = MCPServerManager() + tracker = _ConcurrencyTracker() + server = _make_server("srv-openapi", max_concurrent_requests=2) + server.spec_path = "/fake/openapi.json" + + async def fake_openapi_handler(mcp_server, name, arguments): + tracker.enter(mcp_server.server_id) + try: + await asyncio.sleep(HOLD_SECONDS) + return "ok" + finally: + tracker.exit(mcp_server.server_id) + + with ( + patch.object(manager, "_resolve_mcp_server_for_tool_call", return_value=server), + patch.object(manager, "_call_openapi_tool_handler", side_effect=fake_openapi_handler), + ): + await asyncio.gather( + *[manager.call_tool(server_name="srv-openapi", name="tool", arguments={}) for _ in range(6)] + ) + + assert tracker.peak_by_server["srv-openapi"] == 2 + + +@pytest.mark.asyncio +async def test_edited_limit_takes_effect_without_restart(): + """Editing max_concurrent_requests must rebuild the cached semaphore so the + new cap applies to subsequent calls immediately, not only after a restart.""" + manager = MCPServerManager() + server = _make_server("srv-edited", max_concurrent_requests=3) + + before_edit = _ConcurrencyTracker() + with _patch_client_with_tracker(manager, before_edit): + await _fire(manager, server, n=6) + assert before_edit.peak_by_server["srv-edited"] == 3 + + server.max_concurrent_requests = 1 + after_edit = _ConcurrencyTracker() + with _patch_client_with_tracker(manager, after_edit): + await _fire(manager, server, n=6) + assert after_edit.peak_by_server["srv-edited"] == 1 + + +def test_semaphore_is_reused_per_server_and_distinct_across_servers(): + manager = MCPServerManager() + server_a = _make_server("srv-a", max_concurrent_requests=3) + server_b = _make_server("srv-b", max_concurrent_requests=3) + + sem_a_first = manager._get_call_semaphore(server_a) + sem_a_second = manager._get_call_semaphore(server_a) + sem_b = manager._get_call_semaphore(server_b) + + assert sem_a_first is sem_a_second + assert sem_a_first is not sem_b + + +def test_no_semaphore_created_when_limit_absent(): + manager = MCPServerManager() + server = _make_server("srv-none", max_concurrent_requests=None) + + assert manager._get_call_semaphore(server) is None diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py index ad78609ee18..ec285f8eba0 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough.py @@ -34,8 +34,7 @@ from litellm.types.mcp_server.mcp_server_manager import MCPServer def _mock_mcp_client_ip(): """Bypass IP-based access control in tests.""" with patch( - "litellm.proxy._experimental.mcp_server.discoverable_endpoints" - ".IPAddressUtils.get_mcp_client_ip", + "litellm.proxy._experimental.mcp_server.discoverable_endpoints.IPAddressUtils.get_mcp_client_ip", return_value=None, ): yield @@ -111,6 +110,42 @@ def test_is_oauth_passthrough_false_without_authorization_header(): assert server.is_oauth_passthrough is False +@pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +def test_is_dcr_bridge_true_for_flagged_client_forwarded_modes(auth_type): + server = MCPServer( + server_id="s1", + name="s1", + transport=MCPTransport.http, + auth_type=auth_type, + dcr_bridge=True, + ) + assert server.is_dcr_bridge is True + + +@pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +def test_is_dcr_bridge_false_when_flag_unset(auth_type): + server = MCPServer( + server_id="s1", + name="s1", + transport=MCPTransport.http, + auth_type=auth_type, + ) + assert server.dcr_bridge is None + assert server.is_dcr_bridge is False + + +@pytest.mark.parametrize("auth_type", [MCPAuth.oauth2, MCPAuth.none, MCPAuth.api_key, None]) +def test_is_dcr_bridge_false_for_non_client_forwarded_auth_types(auth_type): + server = MCPServer( + server_id="s1", + name="s1", + transport=MCPTransport.http, + auth_type=auth_type, + dcr_bridge=True, + ) + assert server.is_dcr_bridge is False + + def test_is_oauth_passthrough_false_without_extra_headers(): server = MCPServer( server_id="s1", @@ -191,9 +226,7 @@ async def test_oauth_protected_resource_passthrough_proxies_upstream_metadata(): extra_headers=["Authorization"], oauth_passthrough=True, ) - global_mcp_server_manager.registry[passthrough_server.server_id] = ( - passthrough_server - ) + global_mcp_server_manager.registry[passthrough_server.server_id] = passthrough_server upstream_payload = { "resource": "https://upstream.example.com/mcp", @@ -207,18 +240,14 @@ async def test_oauth_protected_resource_passthrough_proxies_upstream_metadata(): mock_client = MagicMock() mock_client.get = AsyncMock(return_value=mock_response) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): result = await _build_oauth_protected_resource_response( request=_make_request(), mcp_server_name="sample_docs", use_standard_pattern=True, ) - assert result["authorization_servers"] == [ - "https://okta.example.com/oauth2/default" - ] + assert result["authorization_servers"] == ["https://okta.example.com/oauth2/default"] # resource is normalized to the gateway URL so bearers are sent back to us assert result["resource"].endswith("/mcp/sample_docs") assert result["scopes_supported"] == ["openid", "profile"] @@ -242,9 +271,7 @@ async def test_oauth_protected_resource_passthrough_cache_hit(): extra_headers=["Authorization"], oauth_passthrough=True, ) - global_mcp_server_manager.registry[passthrough_server.server_id] = ( - passthrough_server - ) + global_mcp_server_manager.registry[passthrough_server.server_id] = passthrough_server mock_response = MagicMock() mock_response.status_code = 200 @@ -254,9 +281,7 @@ async def test_oauth_protected_resource_passthrough_cache_hit(): mock_client = MagicMock() mock_client.get = AsyncMock(return_value=mock_response) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): await _build_oauth_protected_resource_response( request=_make_request(), mcp_server_name="sample_docs", @@ -348,12 +373,8 @@ async def test_oauth_metadata_cache_expired_entry_is_refetched(): mock_client = MagicMock() mock_client.get = AsyncMock(return_value=mock_response) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): - result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource( - passthrough_server - ) + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): + result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource(passthrough_server) assert result == {"authorization_servers": ["https://fresh.example.com"]} assert mock_client.get.await_count == 1 @@ -377,16 +398,12 @@ async def test_oauth_protected_resource_passthrough_network_error_returns_502(): extra_headers=["Authorization"], oauth_passthrough=True, ) - global_mcp_server_manager.registry[passthrough_server.server_id] = ( - passthrough_server - ) + global_mcp_server_manager.registry[passthrough_server.server_id] = passthrough_server mock_client = MagicMock() mock_client.get = AsyncMock(side_effect=httpx.ConnectError("boom")) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): with pytest.raises(HTTPException) as exc_info: await _build_oauth_protected_resource_response( request=_make_request(), @@ -414,16 +431,10 @@ async def test_fetch_upstream_metadata_returns_none_when_not_all_candidates_netw not_found_response = MagicMock() not_found_response.status_code = 404 mock_client = MagicMock() - mock_client.get = AsyncMock( - side_effect=[not_found_response, httpx.ConnectError("path fallback failed")] - ) + mock_client.get = AsyncMock(side_effect=[not_found_response, httpx.ConnectError("path fallback failed")]) - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): - result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource( - passthrough_server - ) + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): + result = await discoverable_endpoints.fetch_upstream_oauth_protected_resource(passthrough_server) assert result is None assert mock_client.get.await_count == 2 @@ -458,9 +469,7 @@ async def test_oauth_protected_resource_gateway_managed_unchanged(): mock_client = MagicMock() mock_client.get = AsyncMock() - with patch.object( - discoverable_endpoints, "get_async_httpx_client", return_value=mock_client - ): + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): result = await _build_oauth_protected_resource_response( request=_make_request(), mcp_server_name="keycloak_whoami", @@ -468,7 +477,147 @@ async def test_oauth_protected_resource_gateway_managed_unchanged(): ) mock_client.get.assert_not_awaited() - assert result["authorization_servers"] == [ - "https://gateway.example.com/keycloak_whoami" - ] + assert result["authorization_servers"] == ["https://gateway.example.com/keycloak_whoami"] assert result["scopes_supported"] == ["read"] + + +def _make_upstream_metadata_client() -> tuple[dict, MagicMock]: + upstream_payload = { + "resource": "https://upstream.example.com/mcp", + "authorization_servers": ["https://okta.example.com/oauth2/default"], + "scopes_supported": ["openid", "profile"], + "bearer_methods_supported": ["header"], + } + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = upstream_payload + mock_client = MagicMock() + mock_client.get = AsyncMock(return_value=mock_response) + return upstream_payload, mock_client + + +@pytest.mark.asyncio +async def test_oauth_protected_resource_oauth_delegate_returns_upstream_metadata_verbatim(): + """oauth_delegate discovery must return the upstream metadata verbatim, + resource included. The caller's token is forwarded to and validated by the + upstream, so its audience must be the upstream; rewriting resource to the + gateway would make a strict IdP refuse to mint it or the upstream reject it. + A regression that dropped oauth_delegate from the pass-through predicate would + fall through to the gateway-AS branch and advertise LiteLLM as the AS.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + global_mcp_server_manager.registry.clear() + delegate_server = MCPServer( + server_id="delegate-1", + name="sample_docs", + server_name="sample_docs", + alias="sample_docs", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + global_mcp_server_manager.registry[delegate_server.server_id] = delegate_server + + upstream_payload, mock_client = _make_upstream_metadata_client() + try: + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): + result = await _build_oauth_protected_resource_response( + request=_make_request(), + mcp_server_name="sample_docs", + use_standard_pattern=True, + ) + + assert result == upstream_payload + assert result["authorization_servers"] == ["https://okta.example.com/oauth2/default"] + assert result["resource"] == "https://upstream.example.com/mcp" + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +async def test_oauth_protected_resource_true_passthrough_returns_upstream_metadata_verbatim(): + """true_passthrough discovery must return the upstream metadata verbatim, + resource included, so the client treats the upstream as the resource and + authorizes directly against it. A regression that rewrote resource (the + gateway-proxied behavior) would break the transparent-proxy contract.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + global_mcp_server_manager.registry.clear() + true_passthrough_server = MCPServer( + server_id="tp-1", + name="sample_docs", + server_name="sample_docs", + alias="sample_docs", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + global_mcp_server_manager.registry[true_passthrough_server.server_id] = true_passthrough_server + + upstream_payload, mock_client = _make_upstream_metadata_client() + try: + with patch.object(discoverable_endpoints, "get_async_httpx_client", return_value=mock_client): + result = await _build_oauth_protected_resource_response( + request=_make_request(), + mcp_server_name="sample_docs", + use_standard_pattern=True, + ) + + assert result == upstream_payload + assert result["resource"] == "https://upstream.example.com/mcp" + finally: + global_mcp_server_manager.registry.clear() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) +@pytest.mark.parametrize("use_standard_pattern", [True, False]) +async def test_oauth_protected_resource_dcr_bridge_returns_gateway_facade(auth_type, use_standard_pattern): + """With dcr_bridge on, discovery flips from the upstream-verbatim contract to the gateway + facade: resource is the gateway URL the client dialed and authorization_servers names the + gateway's per-server AS, so DCR-only clients (which enforce the RFC 9728 resource match) + can register and sign in through the gateway. No upstream metadata fetch happens.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + + global_mcp_server_manager.registry.clear() + bridge_server = MCPServer( + server_id="bridge-1", + name="sample_docs", + server_name="sample_docs", + alias="sample_docs", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=auth_type, + dcr_bridge=True, + scopes=["read"], + registration_url="https://okta.example.com/register", + ) + global_mcp_server_manager.registry[bridge_server.server_id] = bridge_server + + try: + with patch.object(discoverable_endpoints, "get_async_httpx_client") as mock_client_factory: + result = await _build_oauth_protected_resource_response( + request=_make_request(), + mcp_server_name="sample_docs", + use_standard_pattern=use_standard_pattern, + ) + finally: + global_mcp_server_manager.registry.clear() + + expected_resource = ( + "https://gateway.example.com/mcp/sample_docs" + if use_standard_pattern + else "https://gateway.example.com/sample_docs/mcp" + ) + assert result == { + "authorization_servers": ["https://gateway.example.com/sample_docs"], + "resource": expected_resource, + "scopes_supported": ["read"], + } + mock_client_factory.assert_not_called() diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough_tools.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough_tools.py index d51cf8c5b72..16c36af5156 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough_tools.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_oauth_passthrough_tools.py @@ -76,9 +76,7 @@ async def test_fetch_tools_from_passthrough_raises_on_upstream_401(): mock_client.list_tools = AsyncMock(side_effect=upstream_error) with pytest.raises(MCPUpstreamAuthError) as exc_info: - await manager._fetch_tools_with_timeout( - mock_client, passthrough_server.name, server=passthrough_server - ) + await manager._fetch_tools_with_timeout(mock_client, passthrough_server.name) assert exc_info.value.status_code == 401 assert exc_info.value.www_authenticate == ( @@ -113,9 +111,7 @@ async def test_fetch_tools_from_delegated_oauth2_raises_on_upstream_401(): mock_client.list_tools = AsyncMock(side_effect=upstream_error) with pytest.raises(MCPUpstreamAuthError) as exc_info: - await manager._fetch_tools_with_timeout( - mock_client, delegated_server.name, server=delegated_server - ) + await manager._fetch_tools_with_timeout(mock_client, delegated_server.name) assert exc_info.value.status_code == 401 assert exc_info.value.www_authenticate == ( @@ -126,7 +122,10 @@ async def test_fetch_tools_from_delegated_oauth2_raises_on_upstream_401(): @pytest.mark.asyncio -async def test_fetch_tools_from_client_credentials_oauth2_keeps_swallow_behavior(): +async def test_fetch_tools_from_client_credentials_oauth2_surfaces_upstream_401(): + """The auth_type carve-out was removed: a client_credentials (M2M) server now + surfaces an upstream 401 as MCPUpstreamAuthError too, instead of swallowing it + to an empty list, so single-server routes can return a 401 challenge.""" manager = MCPServerManager() m2m_server = MCPServer( server_id="oauth-m2m", @@ -150,12 +149,12 @@ async def test_fetch_tools_from_client_credentials_oauth2_keeps_swallow_behavior mock_client = MagicMock() mock_client.list_tools = AsyncMock(side_effect=upstream_error) - tools = await manager._fetch_tools_with_timeout( - mock_client, m2m_server.name, server=m2m_server - ) + with pytest.raises(MCPUpstreamAuthError) as exc_info: + await manager._fetch_tools_with_timeout(mock_client, m2m_server.name) - assert tools == [] - mock_client.list_tools.assert_awaited_with(raise_on_error=False) + assert exc_info.value.status_code == 401 + assert exc_info.value.server_name == "m2m_docs" + mock_client.list_tools.assert_awaited_with(raise_on_error=True) @pytest.mark.asyncio @@ -176,9 +175,7 @@ async def test_fetch_tools_from_passthrough_returns_tools_on_success(): mock_client = MagicMock() mock_client.list_tools = AsyncMock(return_value=[tool]) - tools = await manager._fetch_tools_with_timeout( - mock_client, passthrough_server.name, server=passthrough_server - ) + tools = await manager._fetch_tools_with_timeout(mock_client, passthrough_server.name) assert tools == [tool] @@ -238,8 +235,11 @@ def test_to_http_exception_skips_challenge_for_non_401_status(): @pytest.mark.asyncio -async def test_fetch_tools_from_gateway_managed_swallows_errors(): - """Regression guard: non-pass-through servers keep returning [] on errors.""" +async def test_fetch_tools_from_gateway_managed_surfaces_upstream_401(): + """An oauth2 server that is neither pass-through nor delegate now surfaces an + upstream 401 as MCPUpstreamAuthError as well; the auth_type carve-out that + swallowed it to [] was removed. A missing upstream WWW-Authenticate is carried + through as None (the single-server route fabricates one from the gateway URL).""" manager = MCPServerManager() oauth2_server = MCPServer( server_id="o1", @@ -260,8 +260,138 @@ async def test_fetch_tools_from_gateway_managed_swallows_errors(): mock_client = MagicMock() mock_client.list_tools = AsyncMock(side_effect=upstream_error) - tools = await manager._fetch_tools_with_timeout( - mock_client, oauth2_server.name, server=oauth2_server + with pytest.raises(MCPUpstreamAuthError) as exc_info: + await manager._fetch_tools_with_timeout(mock_client, oauth2_server.name) + + assert exc_info.value.status_code == 401 + assert exc_info.value.www_authenticate is None + assert exc_info.value.server_name == "keycloak_whoami" + mock_client.list_tools.assert_awaited_with(raise_on_error=True) + + +def _http_server(server_id: str, name: str, **kwargs) -> MCPServer: + return MCPServer( + server_id=server_id, + name=name, + url=f"https://{name}/mcp", + transport=MCPTransport.http, + **kwargs, ) + + +@pytest.mark.asyncio +async def test_aggregate_list_tools_absorbs_one_unauthenticated_server(): + """Regression: across the aggregate (/mcp), a delegate/passthrough server that raises + MCPUpstreamAuthError must not empty every other server's tools. Re-raising it on the + aggregate path (introduced with the passthrough feature) zeroed the whole list because the + fan-out gather propagated it.""" + from unittest.mock import patch + + from mcp.types import Tool as MCPTool + + from litellm.proxy._experimental.mcp_server import server as mcp_server + from litellm.proxy._types import UserAPIKeyAuth + + delegate = _http_server( + "s1", "delegate_docs", auth_type=MCPAuth.oauth2, delegate_auth_to_upstream=True + ) + working = _http_server("s2", "working_docs", auth_type=MCPAuth.none) + good_tool = MCPTool(name="working_docs-read", description="d", inputSchema={"type": "object"}) + + async def fake_get_tools(server, **kwargs): + if server.server_id == delegate.server_id: + raise MCPUpstreamAuthError(status_code=401, www_authenticate=None, server_name=server.name) + return [good_tool] + + with patch.object(mcp_server, "_get_allowed_mcp_servers", AsyncMock(return_value=[delegate, working])), patch.object( + mcp_server, "_prefetch_oauth_creds_for_user", AsyncMock(return_value={}) + ), patch.object(mcp_server, "_prepare_mcp_server_headers", MagicMock(return_value=(None, None))), patch.object( + mcp_server, "_get_user_oauth_extra_headers_from_db", AsyncMock(return_value=None) + ), patch.object( + mcp_server, "filter_tools_by_key_team_permissions", AsyncMock(side_effect=lambda tools, **k: tools) + ), patch.object( + mcp_server.global_mcp_server_manager, "_get_tools_from_server", AsyncMock(side_effect=fake_get_tools) + ): + tools = await mcp_server._get_tools_from_mcp_servers( + user_api_key_auth=UserAPIKeyAuth(token="h", user_id="u1"), + mcp_auth_header=None, + mcp_servers=None, + ) + + assert [t.name for t in tools] == ["working_docs-read"] + + +@pytest.mark.asyncio +async def test_single_server_route_also_absorbs_upstream_auth_error(): + """A single-server route (//mcp) absorbs an upstream-auth error just like the aggregate: + the failing server is omitted (empty list) rather than re-raised. Surfacing it to the client as a + 401 + WWW-Authenticate challenge cannot be done from this list handler — the MCP session manager + serializes a raise into a JSON-RPC error, not an HTTP 401 — so re-auth surfacing is handled by a + request-scope preemptive check, tracked separately.""" + from unittest.mock import patch + + from litellm.proxy._experimental.mcp_server import server as mcp_server + from litellm.proxy._experimental.mcp_server.mcp_context import _mcp_gateway_server_name + from litellm.proxy._types import UserAPIKeyAuth + + delegate = _http_server( + "s1", "delegate_docs", auth_type=MCPAuth.oauth2, delegate_auth_to_upstream=True + ) + + async def fake_get_tools(server, **kwargs): + raise MCPUpstreamAuthError(status_code=401, www_authenticate=None, server_name=server.name) + + # //mcp sets the path-derived single-server scope; absorption must hold even then. + token = _mcp_gateway_server_name.set("delegate_docs") + try: + with patch.object(mcp_server, "_get_allowed_mcp_servers", AsyncMock(return_value=[delegate])), patch.object( + mcp_server, "_prefetch_oauth_creds_for_user", AsyncMock(return_value={}) + ), patch.object(mcp_server, "_prepare_mcp_server_headers", MagicMock(return_value=(None, None))), patch.object( + mcp_server, "_get_user_oauth_extra_headers_from_db", AsyncMock(return_value=None) + ), patch.object( + mcp_server.global_mcp_server_manager, "_get_tools_from_server", AsyncMock(side_effect=fake_get_tools) + ): + tools = await mcp_server._get_tools_from_mcp_servers( + user_api_key_auth=UserAPIKeyAuth(token="h", user_id="u1"), + mcp_auth_header=None, + mcp_servers=["delegate_docs"], + ) + assert tools == [] + finally: + _mcp_gateway_server_name.reset(token) + + +@pytest.mark.asyncio +async def test_aggregate_with_single_accessible_server_still_absorbs(): + """Regression for the route-misclassification: an aggregate request (/mcp, mcp_servers=None) + from a key that can access exactly one server must still absorb that server's + MCPUpstreamAuthError, not surface it. Keying the surface decision off the allowed count rather + than the request filter would re-raise here and leave the aggregate broken for one-server + permission sets.""" + from unittest.mock import patch + + from litellm.proxy._experimental.mcp_server import server as mcp_server + from litellm.proxy._types import UserAPIKeyAuth + + delegate = _http_server( + "s1", "delegate_docs", auth_type=MCPAuth.oauth2, delegate_auth_to_upstream=True + ) + + async def fake_get_tools(server, **kwargs): + raise MCPUpstreamAuthError(status_code=401, www_authenticate=None, server_name=server.name) + + with patch.object(mcp_server, "_get_allowed_mcp_servers", AsyncMock(return_value=[delegate])), patch.object( + mcp_server, "_prefetch_oauth_creds_for_user", AsyncMock(return_value={}) + ), patch.object(mcp_server, "_prepare_mcp_server_headers", MagicMock(return_value=(None, None))), patch.object( + mcp_server, "_get_user_oauth_extra_headers_from_db", AsyncMock(return_value=None) + ), patch.object( + mcp_server.global_mcp_server_manager, "_get_tools_from_server", AsyncMock(side_effect=fake_get_tools) + ): + # Aggregate route: no explicit server filter, even though only one server is accessible. + tools = await mcp_server._get_tools_from_mcp_servers( + user_api_key_auth=UserAPIKeyAuth(token="h", user_id="u1"), + mcp_auth_header=None, + mcp_servers=None, + ) + assert tools == [] - mock_client.list_tools.assert_awaited_with(raise_on_error=False) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_partial_update.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_partial_update.py index 49facdbaeaf..41d61c8f508 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_partial_update.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_partial_update.py @@ -7,9 +7,11 @@ Omitting a field must NOT reset it to its Pydantic schema default (e.g. would silently overwrite the existing DB row. """ +import json from unittest.mock import AsyncMock, MagicMock import pytest +from prisma import Json from litellm.proxy._experimental.mcp_server.db import ( create_mcp_server, @@ -18,6 +20,11 @@ from litellm.proxy._experimental.mcp_server.db import ( from litellm.proxy._types import NewMCPServerRequest, UpdateMCPServerRequest +def _credentials_cleared(value) -> bool: + """The clear sentinel after the edge translation: prisma Json(None) (SQL null) or a bare None.""" + return value is None or (isinstance(value, Json) and getattr(value, "data", "x") is None) + + def _mock_prisma(): mock_prisma = MagicMock() mock_prisma.db.litellm_mcpservertable = AsyncMock() @@ -134,14 +141,10 @@ async def test_partial_update_writes_explicitly_provided_fields(): @pytest.mark.asyncio async def test_partial_update_can_explicitly_reset_allow_all_keys(): """Caller can still reset a field to its default by sending it explicitly.""" - enabled = await _run_update( - UpdateMCPServerRequest(server_id="s", allow_all_keys=True) - ) + enabled = await _run_update(UpdateMCPServerRequest(server_id="s", allow_all_keys=True)) assert enabled["allow_all_keys"] is True - disabled = await _run_update( - UpdateMCPServerRequest(server_id="s", allow_all_keys=False) - ) + disabled = await _run_update(UpdateMCPServerRequest(server_id="s", allow_all_keys=False)) assert disabled["allow_all_keys"] is False @@ -178,6 +181,111 @@ async def test_partial_update_can_explicitly_clear_alias(): assert data_dict["alias"] is None +async def _run_update_with_existing(data: UpdateMCPServerRequest, existing_auth_type: str) -> dict: + mock_prisma = _mock_prisma() + existing = MagicMock() + existing.auth_type = existing_auth_type + existing.credentials = None + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + await update_mcp_server(mock_prisma, data, "test-user") + return mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + +@pytest.mark.asyncio +async def test_auth_type_switch_clears_stale_flow_scoped_fields(): + """ + Switching oauth2 -> oauth2_token_exchange must clear the previous flow's + endpoint config: a stale token_url would otherwise be picked up as the + token-exchange endpoint and suppress RFC 9728/8414 discovery. + """ + data = UpdateMCPServerRequest(server_id="my-test-server", auth_type="oauth2_token_exchange") + + data_dict = await _run_update_with_existing(data, existing_auth_type="oauth2") + + for stale_field in ( + "authorization_url", + "token_url", + "registration_url", + "oauth2_flow", + "dcr_bridge", + "token_exchange_endpoint", + "audience", + "subject_token_type", + "token_exchange_profile", + ): + assert data_dict[stale_field] is None, f"{stale_field} must be cleared on auth_type switch" + assert _credentials_cleared(data_dict["credentials"]) + + +@pytest.mark.asyncio +async def test_auth_type_switch_keeps_explicitly_provided_flow_fields(): + """Fields explicitly provided alongside the auth_type switch must survive it.""" + data = UpdateMCPServerRequest( + server_id="my-test-server", + auth_type="oauth2_token_exchange", + token_exchange_endpoint="https://idp.example.com/oauth2/token", + ) + + data_dict = await _run_update_with_existing(data, existing_auth_type="oauth2") + + assert data_dict["token_exchange_endpoint"] == "https://idp.example.com/oauth2/token" + assert data_dict["token_url"] is None + + +@pytest.mark.asyncio +async def test_auth_type_switch_to_client_forwarded_keeps_explicit_dcr_bridge(): + data = UpdateMCPServerRequest( + server_id="my-test-server", + auth_type="true_passthrough", + dcr_bridge=True, + ) + + data_dict = await _run_update_with_existing(data, existing_auth_type="oauth2") + + assert data_dict["dcr_bridge"] is True + assert data_dict["oauth2_flow"] is None + + +@pytest.mark.asyncio +async def test_auth_type_switch_back_to_oauth2_clears_token_exchange_fields(): + """The reverse switch must not leave token-exchange settings behind to + silently reactivate if the server is later switched back.""" + data = UpdateMCPServerRequest(server_id="my-test-server", auth_type="oauth2") + + data_dict = await _run_update_with_existing(data, existing_auth_type="oauth2_token_exchange") + + assert data_dict["token_exchange_endpoint"] is None + assert data_dict["audience"] is None + assert data_dict["subject_token_type"] is None + assert data_dict["token_exchange_profile"] is None + + +@pytest.mark.asyncio +async def test_unchanged_auth_type_does_not_clear_flow_fields(): + """An update that keeps the auth_type must not touch flow-scoped fields, so a + legacy OBO server using token_url as its exchange endpoint keeps working.""" + data = UpdateMCPServerRequest( + server_id="my-test-server", + auth_type="oauth2_token_exchange", + allowed_tools=["foo"], + ) + + data_dict = await _run_update_with_existing(data, existing_auth_type="oauth2_token_exchange") + + for flow_field in ( + "authorization_url", + "token_url", + "registration_url", + "oauth2_flow", + "dcr_bridge", + "token_exchange_endpoint", + "audience", + "subject_token_type", + "token_exchange_profile", + ): + assert flow_field not in data_dict + + @pytest.mark.asyncio async def test_create_still_writes_defaults(): """ @@ -203,3 +311,356 @@ async def test_create_still_writes_defaults(): # audit fields set by create_mcp_server. assert data_dict["created_by"] == "test-user" assert data_dict["updated_by"] == "test-user" + + +# ── token-exchange blob → column normalization ──────────────────────────────── +# +# token_exchange_endpoint / audience / subject_token_type have dedicated columns; +# their MCPCredentials copies are a legacy shape. Writes must lift blob values +# into the columns and strip them from the stored blob so the read-time +# ``column or blob`` fallback can never resurrect a stale blob value after the +# column is cleared. + + +@pytest.fixture(autouse=True) +def _salt_key(monkeypatch): + monkeypatch.setenv("LITELLM_SALT_KEY", "sk-1234") + + +def _existing_row(auth_type: str, credentials: dict | None = None): + existing = MagicMock() + existing.auth_type = auth_type + existing.credentials = json.dumps(credentials) if credentials is not None else None + existing.token_exchange_endpoint = None + existing.audience = None + existing.subject_token_type = None + existing.token_exchange_profile = None + return existing + + +@pytest.mark.asyncio +async def test_create_lifts_blob_token_exchange_settings_into_columns(): + """The legacy REST shape (TE settings inside ``credentials``) must land in + the dedicated columns, and the stored blob must not keep a copy.""" + mock_prisma = _mock_prisma() + data = NewMCPServerRequest( + server_id="te-server", + url="https://example.com/mcp", + transport="http", + auth_type="oauth2_token_exchange", + credentials={ + "client_id": "cid", + "client_secret": "sec", + "token_exchange_endpoint": "https://idp.example.com/oauth2/token", + "audience": "api://upstream", + "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", + "token_exchange_profile": "entra_obo", + }, + ) + + await create_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.create.call_args[1]["data"] + + assert data_dict["token_exchange_endpoint"] == "https://idp.example.com/oauth2/token" + assert data_dict["audience"] == "api://upstream" + assert data_dict["subject_token_type"] == "urn:ietf:params:oauth:token-type:jwt" + assert data_dict["token_exchange_profile"] == "entra_obo" + stored_blob = json.loads(data_dict["credentials"]) + for te_field in ("token_exchange_endpoint", "audience", "subject_token_type", "token_exchange_profile"): + assert te_field not in stored_blob + assert "client_id" in stored_blob + + +@pytest.mark.asyncio +async def test_create_explicit_column_wins_over_blob_copy(): + mock_prisma = _mock_prisma() + data = NewMCPServerRequest( + server_id="te-server", + url="https://example.com/mcp", + transport="http", + auth_type="oauth2_token_exchange", + token_exchange_endpoint="https://top-level.example.com/token", + credentials={"client_id": "cid", "token_exchange_endpoint": "https://blob.example.com/token"}, + ) + + await create_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.create.call_args[1]["data"] + + assert data_dict["token_exchange_endpoint"] == "https://top-level.example.com/token" + assert "token_exchange_endpoint" not in json.loads(data_dict["credentials"]) + + +@pytest.mark.asyncio +async def test_credentials_merge_migrates_legacy_blob_te_settings(): + """A same-auth credentials update on a legacy row (TE settings in the blob, + columns null) must move the settings to the columns and drop them from the + merged blob.""" + mock_prisma = _mock_prisma() + existing = _existing_row( + "oauth2_token_exchange", + credentials={ + "client_id": "enc-old-cid", + "token_exchange_endpoint": "https://legacy-idp.example.com/token", + "audience": "api://legacy", + "token_exchange_profile": "entra_obo", + }, + ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest( + server_id="te-server", + auth_type="oauth2_token_exchange", + credentials={"client_id": "new-cid"}, + ) + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + assert data_dict["token_exchange_endpoint"] == "https://legacy-idp.example.com/token" + assert data_dict["audience"] == "api://legacy" + assert data_dict["token_exchange_profile"] == "entra_obo" + merged_blob = json.loads(data_dict["credentials"]) + for te_field in ("token_exchange_endpoint", "audience", "subject_token_type", "token_exchange_profile"): + assert te_field not in merged_blob + + +@pytest.mark.asyncio +async def test_cleared_column_is_not_resurrected_by_legacy_blob_value(): + """The Greptile scenario: explicitly clearing the column (to re-enable + RFC 9728/8414 discovery) while the legacy blob still holds an endpoint must + NOT resurrect the blob value — the explicit null wins and the blob copy is + stripped.""" + mock_prisma = _mock_prisma() + existing = _existing_row( + "oauth2_token_exchange", + credentials={"client_id": "enc-old-cid", "token_exchange_endpoint": "https://dead-idp.example.com/token"}, + ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest( + server_id="te-server", + auth_type="oauth2_token_exchange", + token_exchange_endpoint=None, + credentials={"client_id": "new-cid"}, + ) + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + assert data_dict["token_exchange_endpoint"] is None + assert "token_exchange_endpoint" not in json.loads(data_dict["credentials"]) + + +@pytest.mark.asyncio +async def test_merge_strips_blob_te_copy_when_column_already_set(): + """When the row already has a column value, the blob copy is shadowed at + read time anyway — the merge must strip it rather than carry it forward.""" + mock_prisma = _mock_prisma() + existing = _existing_row( + "oauth2_token_exchange", + credentials={"client_id": "enc-old-cid", "token_exchange_endpoint": "https://blob-copy.example.com/token"}, + ) + existing.token_exchange_endpoint = "https://column.example.com/token" + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest( + server_id="te-server", + auth_type="oauth2_token_exchange", + credentials={"client_id": "new-cid"}, + ) + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + # Column untouched by this update (not in payload), blob copy gone. + assert "token_exchange_endpoint" not in data_dict + assert "token_exchange_endpoint" not in json.loads(data_dict["credentials"]) + + +@pytest.mark.asyncio +async def test_auth_type_switch_clears_flow_fields_with_external_fields_set(): + """The management endpoint passes ``fields_set`` explicitly (PUT + /v1/mcp/server). The auth-switch clearing must fire on that path too — it is + gated on ``data.auth_type``/the existing row, not on how fields_set arrives.""" + mock_prisma = _mock_prisma() + existing = _existing_row("oauth2") + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest(server_id="te-server", auth_type="oauth2_token_exchange") + await update_mcp_server(mock_prisma, data, "test-user", fields_set=set(data.fields_set())) + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + for stale_field in ( + "authorization_url", + "token_url", + "registration_url", + "oauth2_flow", + "token_exchange_endpoint", + "audience", + "subject_token_type", + "token_exchange_profile", + ): + assert data_dict[stale_field] is None, f"{stale_field} must be cleared via the fields_set path" + + +@pytest.mark.asyncio +async def test_explicit_clear_without_credentials_purges_legacy_blob_copy(): + """Clearing a column in an update that does not touch credentials must strip + the legacy blob copy too — otherwise the next credentials update's + migrate-on-write would repopulate the column the admin just cleared.""" + mock_prisma = _mock_prisma() + existing = _existing_row( + "oauth2_token_exchange", + credentials={"client_id": "enc-old-cid", "token_exchange_endpoint": "https://dead-idp.example.com/token"}, + ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest(server_id="te-server", token_exchange_endpoint=None) + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + assert data_dict["token_exchange_endpoint"] is None + stored_blob = json.loads(data_dict["credentials"]) + assert "token_exchange_endpoint" not in stored_blob + # Unrelated blob keys (encrypted secrets) survive untouched. + assert stored_blob["client_id"] == "enc-old-cid" + + +@pytest.mark.asyncio +async def test_explicit_te_write_without_credentials_migrates_other_legacy_fields(): + """A no-credentials update that writes one token-exchange column migrates the + whole row: untouched null columns are lifted from the blob, and every blob + copy is stripped.""" + mock_prisma = _mock_prisma() + existing = _existing_row( + "oauth2_token_exchange", + credentials={ + "client_id": "enc-old-cid", + "token_exchange_endpoint": "https://legacy-idp.example.com/token", + "audience": "api://legacy", + }, + ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest(server_id="te-server", audience="api://new") + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + assert data_dict["audience"] == "api://new" + assert data_dict["token_exchange_endpoint"] == "https://legacy-idp.example.com/token" + stored_blob = json.loads(data_dict["credentials"]) + for te_field in ("token_exchange_endpoint", "audience", "subject_token_type"): + assert te_field not in stored_blob + + +@pytest.mark.asyncio +async def test_te_update_without_blob_te_keys_leaves_credentials_untouched(): + """A no-credentials column write on a row whose blob has no legacy copies + must not rewrite the credentials blob at all.""" + mock_prisma = _mock_prisma() + existing = _existing_row("oauth2_token_exchange", credentials={"client_id": "enc-old-cid"}) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest(server_id="te-server", token_exchange_endpoint="https://new.example.com/token") + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + assert data_dict["token_exchange_endpoint"] == "https://new.example.com/token" + assert "credentials" not in data_dict + + +# ── client-forwarded credential class: true_passthrough <-> oauth_delegate share one +# stored-app shape, so a switch between them must MERGE (keep the declared app), not REPLACE ── + + +@pytest.mark.asyncio +async def test_cf_pair_switch_without_credentials_keeps_stored_app_and_endpoints(): + """true_passthrough -> oauth_delegate with no credentials in the update must not clear the + stored client or null the endpoint columns: both modes use the same declared app and relay.""" + mock_prisma = _mock_prisma() + existing = _existing_row("true_passthrough", credentials={"client_id": "enc-A", "client_secret": "enc-B"}) + existing.authorization_url = "https://provider.example/authorize" + existing.token_url = "https://provider.example/token" + existing.registration_url = "https://provider.example/register" + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest(server_id="cf-server", auth_type="oauth_delegate") + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + assert "credentials" not in data_dict + for scoped_field in ("authorization_url", "token_url", "registration_url", "oauth2_flow"): + assert scoped_field not in data_dict, f"{scoped_field} must not be nulled within the CF class" + + +@pytest.mark.asyncio +async def test_cf_pair_switch_with_partial_credentials_merges_not_replaces(): + """oauth_delegate update carrying only client_id onto a true_passthrough row must MERGE, so the + stored client_secret survives instead of being dropped by a REPLACE.""" + mock_prisma = _mock_prisma() + existing = _existing_row("true_passthrough", credentials={"client_id": "enc-A", "client_secret": "enc-B"}) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest(server_id="cf-server", auth_type="oauth_delegate", credentials={"client_id": "B"}) + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + merged = json.loads(data_dict["credentials"]) + assert merged["client_secret"] == "enc-B" + assert merged["client_id"] != "enc-A" + + +@pytest.mark.asyncio +async def test_null_existing_auth_type_to_cf_counts_as_changed_and_clears_blob(): + """A legacy row with NULL auth_type switched to a client-forwarded mode is a cross-class change, + so the stale blob must be cleared (the two change predicates must agree on this).""" + mock_prisma = _mock_prisma() + existing = _existing_row(None, credentials={"client_id": "enc-old"}) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest(server_id="cf-server", auth_type="true_passthrough") + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + # The clear must reach prisma as Json(None) (SQL null), never a raw None, which prisma rejects. + assert isinstance(data_dict["credentials"], Json) + assert getattr(data_dict["credentials"], "data", "x") is None + + +@pytest.mark.asyncio +async def test_client_rotation_strips_legacy_minted_token_keys(): + """Rotating the client on a same-class row must drop stale minted token material the update did + not set, so an old access_token/refresh_token never rides forward under the new client.""" + mock_prisma = _mock_prisma() + existing = _existing_row( + "oauth2", credentials={"client_id": "A", "access_token": "T", "refresh_token": "R", "expires_in": 3600} + ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing) + + data = UpdateMCPServerRequest( + server_id="oauth2-server", auth_type="oauth2", credentials={"client_id": "B", "client_secret": "S"} + ) + await update_mcp_server(mock_prisma, data, "test-user") + data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] + + merged = json.loads(data_dict["credentials"]) + assert "access_token" not in merged + assert "refresh_token" not in merged + assert "expires_in" not in merged + assert "client_secret" in merged + + +@pytest.mark.asyncio +async def test_cf_to_non_cf_switch_clears_dcr_bridge(): + """A cross-class switch OUT of a client-forwarded mode (true_passthrough -> api_key) must clear + dcr_bridge: the switch is cross-class so the flow-scoped sweep runs and nulls it, leaving no stale + dcr_bridge=True on a row that no longer supports it.""" + data = UpdateMCPServerRequest(server_id="s", auth_type="api_key") + data_dict = await _run_update_with_existing(data, existing_auth_type="true_passthrough") + assert data_dict["dcr_bridge"] is None + + +@pytest.mark.asyncio +async def test_cf_pair_switch_does_not_clear_dcr_bridge(): + """A within-class switch (true_passthrough <-> oauth_delegate) is not a credential-class change, so + the flow-scoped sweep does not run and dcr_bridge is left intact (both modes use the DCR bridge).""" + data = UpdateMCPServerRequest(server_id="s", auth_type="oauth_delegate") + data_dict = await _run_update_with_existing(data, existing_auth_type="true_passthrough") + assert "dcr_bridge" not in data_dict diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py index 34e932b6ae7..32996905166 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server.py @@ -8,8 +8,10 @@ from fastapi import HTTPException from mcp import ReadResourceResult, Resource from mcp.types import ( BlobResourceContents, + CallToolResult, Prompt, ResourceTemplate, + TextContent, TextResourceContents, ) @@ -69,9 +71,7 @@ async def test_mcp_server_tool_call_body_contains_request_data(): # Mock the add_litellm_data_to_request function to capture the data captured_data = {} - async def mock_add_litellm_data_to_request( - data, request, user_api_key_dict, proxy_config - ): + async def mock_add_litellm_data_to_request(data, request, user_api_key_dict, proxy_config): captured_data.update(data) # Simulate the proxy_server_request creation captured_data["proxy_server_request"] = { @@ -110,6 +110,55 @@ async def test_mcp_server_tool_call_body_contains_request_data(): assert body["arguments"] == tool_arguments +@pytest.mark.asyncio +async def test_mcp_server_tool_call_relays_upstream_auth_error_as_iserror(): + """The MCP session manager serializes handler exceptions as JSON-RPC errors, so a mid-session + tool call cannot emit a raw 401 the way the REST path does. mcp_server_tool_call must turn an + upstream MCPUpstreamAuthError into an explicit isError result naming the status, not a masked + 500 or a raw traceback, so the client still learns it must re-authenticate upstream.""" + try: + from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError + from litellm.proxy._experimental.mcp_server.server import ( + mcp_server_tool_call, + set_auth_context, + ) + except ImportError: + pytest.skip("MCP server not available") + + set_auth_context(UserAPIKeyAuth(api_key="test_key", user_id="test_user")) + + async def mock_add_litellm_data_to_request(data, request, user_api_key_dict, proxy_config): + return data + + async def mock_call_mcp_tool(*args, **kwargs): + raise MCPUpstreamAuthError(status_code=401, www_authenticate="Bearer", server_name="pt") + + mock_logger = MagicMock() + with patch( + "litellm.proxy.litellm_pre_call_utils.add_litellm_data_to_request", + mock_add_litellm_data_to_request, + ): + with patch( + "litellm.proxy._experimental.mcp_server.server.call_mcp_tool", + mock_call_mcp_tool, + ): + with patch("litellm.proxy.proxy_server.proxy_config", MagicMock()): + with patch("litellm.proxy._experimental.mcp_server.server.verbose_logger", mock_logger): + result = await mcp_server_tool_call("test_tool", {"param": "value"}) + + assert result.isError is True + # The dedicated MCPUpstreamAuthError branch (not the generic Exception fallthrough) produces this + # specific message and logs at info, never a traceback via verbose_logger.exception. + assert "upstream authentication required" in result.content[0].text + assert "401" in result.content[0].text + exception_calls = [str(c.args[0]) for c in mock_logger.exception.call_args_list if c.args] + assert not any("mcp_server_tool_call" in m for m in exception_calls), ( + "must not log a traceback for the expected re-auth" + ) + info_calls = [str(c.args[0]) for c in mock_logger.info.call_args_list if c.args] + assert any("Upstream auth failure" in m for m in info_calls) + + def test_prepare_mcp_server_headers_case_insensitive_extra_headers(): try: from litellm.proxy._experimental.mcp_server.server import ( @@ -355,6 +404,122 @@ def test_prepare_mcp_server_headers_m2m_skips_authorization_from_raw_extra_heade assert extra_headers.get("X-Custom") == "trace" +def _client_forwarded_mode_server(server_id: str, auth_type) -> MCPServer: + return MCPServer( + server_id=server_id, + name=server_id, + transport=MCPTransport.http, + auth_type=auth_type, + ) + + +def _prepare_headers_in_scope(server: MCPServer, scope_servers): + from litellm.proxy._experimental.mcp_server.server import ( + _prepare_mcp_server_headers, + ) + + return _prepare_mcp_server_headers( + server=server, + mcp_server_auth_headers=None, + mcp_auth_header=None, + oauth2_headers={"Authorization": "Bearer upstream-token"}, + raw_headers={ + "x-litellm-api-key": "Bearer sk-litellm-key", + "authorization": "Bearer upstream-token", + }, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + scope_servers=scope_servers, + ) + + +def test_prepare_mcp_server_headers_withholds_global_authorization_when_scope_fans_out(): + """One caller bearer must not be replayed against multiple upstreams (RFC 9700 + cross-resource replay): in a fan-out scope with a second Authorization-consuming + server, the client-forwarded modes get no global Authorization.""" + delegate = _client_forwarded_mode_server("od-fanout", MCPAuth.oauth_delegate) + second_consumer = _client_forwarded_mode_server("tp-fanout", MCPAuth.true_passthrough) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, second_consumer]) + + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + +def test_prepare_mcp_server_headers_forwards_global_authorization_to_sole_consumer(): + """Non-consuming servers (static api_key) in scope do not make the forward ambiguous.""" + delegate = _client_forwarded_mode_server("od-sole", MCPAuth.oauth_delegate) + static_server = MCPServer( + server_id="static-api-key", + name="static-api-key", + transport=MCPTransport.http, + auth_type=MCPAuth.api_key, + authentication_token="static-key", + ) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, static_server]) + + assert extra_headers == {"Authorization": "Bearer upstream-token"} + + +def test_prepare_mcp_server_headers_scope_counts_legacy_delegate_as_consumer(): + """Legacy upstream-delegated oauth2 servers still receive the caller's Authorization on the + v1 path, so their presence in scope must suppress the new modes' forward too.""" + delegate = _client_forwarded_mode_server("od-vs-legacy", MCPAuth.oauth_delegate) + legacy_delegate = MCPServer( + server_id="legacy-delegate", + name="legacy-delegate", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, + ) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, legacy_delegate]) + + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + +def test_prepare_mcp_server_headers_fanout_withhold_survives_extra_headers_loop(): + """Regression: when fan-out withholds the request-wide Authorization from a client-forwarded + server, the later server.extra_headers copy loop must not re-add it from raw_headers even if + the server lists Authorization in extra_headers. Otherwise one bearer is replayed across every + consuming upstream in the scope (the exact cross-resource replay the withholding prevents).""" + delegate = MCPServer( + server_id="od-extra-hdr", + name="od-extra-hdr", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + extra_headers=["Authorization"], + ) + second_consumer = _client_forwarded_mode_server("tp-peer", MCPAuth.true_passthrough) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, second_consumer]) + + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + +def test_prepare_mcp_server_headers_sole_consumer_still_forwards_via_extra_headers(): + """Guard the fix does not over-withhold: with no second consumer in scope, a client-forwarded + server that lists Authorization in extra_headers still forwards the caller's bearer.""" + delegate = MCPServer( + server_id="od-extra-sole", + name="od-extra-sole", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + extra_headers=["Authorization"], + ) + static_server = MCPServer( + server_id="static-peer", + name="static-peer", + transport=MCPTransport.http, + auth_type=MCPAuth.api_key, + authentication_token="static-key", + ) + + _, extra_headers = _prepare_headers_in_scope(delegate, [delegate, static_server]) + + assert extra_headers is not None + assert extra_headers.get("Authorization") == "Bearer upstream-token" + + @pytest.mark.asyncio async def test_call_tool_m2m_skips_authorization_headers(): """M2M call_tool must not forward caller Authorization in oauth2/raw headers.""" @@ -382,9 +547,7 @@ async def test_call_tool_m2m_skips_authorization_headers(): mock_client = MagicMock() mock_client.call_tool = AsyncMock(return_value=MagicMock()) - with patch.object( - manager, "_create_mcp_client", new=AsyncMock(return_value=mock_client) - ) as create_client_mock: + with patch.object(manager, "_create_mcp_client", new=AsyncMock(return_value=mock_client)) as create_client_mock: await manager._call_regular_mcp_tool( mcp_server=server, original_tool_name="echo", @@ -879,9 +1042,7 @@ async def test_get_tools_from_mcp_servers_continues_when_one_server_fails(): # Mock global_mcp_server_manager mock_manager = MagicMock() - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["working_server", "failing_server"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["working_server", "failing_server"]) mock_manager.get_mcp_server_by_id = lambda server_id: ( working_server if server_id == "working_server" else failing_server ) @@ -942,9 +1103,7 @@ async def test_get_tools_from_mcp_servers_continues_when_one_server_fails(): ) # Verify success logging - mock_logger.info.assert_any_call( - "Successfully fetched 1 tools total from all MCP servers" - ) + mock_logger.info.assert_any_call("Successfully fetched 1 tools total from all MCP servers") @pytest.mark.asyncio @@ -985,9 +1144,7 @@ async def test_get_tools_from_mcp_servers_handles_all_servers_failing(): # Mock global_mcp_server_manager mock_manager = MagicMock() - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["failing_server1", "failing_server2"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["failing_server1", "failing_server2"]) mock_manager.get_mcp_server_by_id = lambda server_id: ( failing_server1 if server_id == "failing_server1" else failing_server2 ) @@ -1042,9 +1199,7 @@ async def test_get_tools_from_mcp_servers_handles_all_servers_failing(): ) # Verify total logging - mock_logger.info.assert_any_call( - "Successfully fetched 0 tools total from all MCP servers" - ) + mock_logger.info.assert_any_call("Successfully fetched 0 tools total from all MCP servers") @pytest.mark.asyncio @@ -1069,9 +1224,7 @@ async def test_mcp_server_tool_call_body_with_none_arguments(): # Mock the add_litellm_data_to_request function to capture the data captured_data = {} - async def mock_add_litellm_data_to_request( - data, request, user_api_key_dict, proxy_config - ): + async def mock_add_litellm_data_to_request(data, request, user_api_key_dict, proxy_config): captured_data.update(data) captured_data["proxy_server_request"] = { "url": str(request.url), @@ -1176,31 +1329,29 @@ async def test_concurrent_initialize_session_managers(): results = await asyncio.gather(*tasks, return_exceptions=True) # All tasks should complete successfully (no exceptions) - assert all( - result == "success" for result in results - ), f"Some tasks failed: {results}" + assert all(result == "success" for result in results), f"Some tasks failed: {results}" # Each session manager.run() should only be called once due to the lock - assert ( - mock_stateless_run.call_count == 1 - ), f"Expected 1 call to session_manager_stateless.run(), got {mock_stateless_run.call_count}" - assert ( - mock_stateful_run.call_count == 1 - ), f"Expected 1 call to session_manager_stateful.run(), got {mock_stateful_run.call_count}" - assert ( - mock_sse_run.call_count == 1 - ), f"Expected 1 call to sse_session_manager.run(), got {mock_sse_run.call_count}" + assert mock_stateless_run.call_count == 1, ( + f"Expected 1 call to session_manager_stateless.run(), got {mock_stateless_run.call_count}" + ) + assert mock_stateful_run.call_count == 1, ( + f"Expected 1 call to session_manager_stateful.run(), got {mock_stateful_run.call_count}" + ) + assert mock_sse_run.call_count == 1, ( + f"Expected 1 call to sse_session_manager.run(), got {mock_sse_run.call_count}" + ) # The context managers should only be entered once each - assert ( - mock_cm_stateless.__aenter__.call_count == 1 - ), f"Expected 1 call to stateless __aenter__, got {mock_cm_stateless.__aenter__.call_count}" - assert ( - mock_cm_stateful.__aenter__.call_count == 1 - ), f"Expected 1 call to stateful __aenter__, got {mock_cm_stateful.__aenter__.call_count}" - assert ( - mock_cm_sse.__aenter__.call_count == 1 - ), f"Expected 1 call to sse __aenter__, got {mock_cm_sse.__aenter__.call_count}" + assert mock_cm_stateless.__aenter__.call_count == 1, ( + f"Expected 1 call to stateless __aenter__, got {mock_cm_stateless.__aenter__.call_count}" + ) + assert mock_cm_stateful.__aenter__.call_count == 1, ( + f"Expected 1 call to stateful __aenter__, got {mock_cm_stateful.__aenter__.call_count}" + ) + assert mock_cm_sse.__aenter__.call_count == 1, ( + f"Expected 1 call to sse __aenter__, got {mock_cm_sse.__aenter__.call_count}" + ) # State should be properly set assert mcp_server._SESSION_MANAGERS_INITIALIZED is True @@ -1343,16 +1494,12 @@ async def test_mcp_routing_initialize_to_stateful_no_session_to_stateless(): # initialize → stateful init_body = b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}' stateless_called, stateful_called = await make_request(init_body) - assert ( - stateful_called and not stateless_called - ), "initialize (no session) should route to stateful, not stateless" + assert stateful_called and not stateless_called, "initialize (no session) should route to stateful, not stateless" # tools/list → stateless tools_body = b'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' stateless_called, stateful_called = await make_request(tools_body) - assert ( - stateless_called and not stateful_called - ), "tools/list (no session) should route to stateless, not stateful" + assert stateless_called and not stateful_called, "tools/list (no session) should route to stateless, not stateful" @pytest.mark.asyncio @@ -1437,9 +1584,9 @@ async def test_mcp_routing_chunked_initialize_to_stateful(): ): await handle_streamable_http_mcp(scope, receive, send) - assert ( - stateful_called and not stateless_called - ), "chunked initialize (no session) should route to stateful, not stateless" + assert stateful_called and not stateless_called, ( + "chunked initialize (no session) should route to stateful, not stateless" + ) @pytest.mark.asyncio @@ -1468,10 +1615,7 @@ async def test_mcp_routing_caps_body_peek_for_oversized_chunked_body(): messages = [ {"type": "http.request", "body": first_chunk, "more_body": True}, - *[ - {"type": "http.request", "body": chunk, "more_body": True} - for chunk in oversized_tail - ], + *[{"type": "http.request", "body": chunk, "more_body": True} for chunk in oversized_tail], {"type": "http.request", "body": b"", "more_body": False}, ] receive_calls = {"count": 0} @@ -1522,12 +1666,8 @@ async def test_mcp_routing_caps_body_peek_for_oversized_chunked_body(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateless, "handle_request", side_effect=stateless_handle - ), - patch.object( - session_manager_stateful, "handle_request", side_effect=stateful_handle - ), + patch.object(session_manager_stateless, "handle_request", side_effect=stateless_handle), + patch.object(session_manager_stateful, "handle_request", side_effect=stateful_handle), patch.object(session_manager_stateless, "_server_instances", {}), patch.object(session_manager_stateful, "_server_instances", {}), ): @@ -1578,9 +1718,7 @@ async def test_enforce_stateful_session_cap_evicts_oldest_idle_then_rejects(): patch.object(session_manager_stateful, "_server_instances", instances), patch.object(mcp_server, "_MAX_STATEFUL_SESSIONS_PER_OWNER", 3), patch.dict(mcp_server._stateful_session_owners, owners, clear=True), - patch.dict( - mcp_server._stateful_session_auth_context_last_seen, last_seen, clear=True - ), + patch.dict(mcp_server._stateful_session_auth_context_last_seen, last_seen, clear=True), patch.dict(mcp_server._stateful_session_auth_contexts, contexts, clear=True), patch.dict(mcp_server._stateful_session_active_request_counts, {}, clear=True), ): @@ -1593,9 +1731,7 @@ async def test_enforce_stateful_session_cap_evicts_oldest_idle_then_rejects(): # A different owner at the cap is unaffected by owner-A's sessions. terminated.clear() - allowed_other = await mcp_server._enforce_stateful_session_cap_for_owner( - "owner-B" - ) + allowed_other = await mcp_server._enforce_stateful_session_cap_for_owner("owner-B") assert allowed_other is True assert terminated == [] @@ -1614,9 +1750,7 @@ async def test_enforce_stateful_session_cap_evicts_oldest_idle_then_rejects(): {f"s{i}": float(i) for i in range(3)}, clear=True, ), - patch.dict( - mcp_server._stateful_session_active_request_counts, active, clear=True - ), + patch.dict(mcp_server._stateful_session_active_request_counts, active, clear=True), ): rejected = await mcp_server._enforce_stateful_session_cap_for_owner("owner-A") assert rejected is False @@ -1662,9 +1796,7 @@ async def test_mcp_routing_initialize_rejected_when_owner_at_session_cap(): (b"authorization", b"Bearer test-key"), ], } - receive = AsyncMock( - return_value={"type": "http.request", "body": init_body, "more_body": False} - ) + receive = AsyncMock(return_value={"type": "http.request", "body": init_body, "more_body": False}) send = AsyncMock() stateful_called = [] @@ -1685,9 +1817,7 @@ async def test_mcp_routing_initialize_rejected_when_owner_at_session_cap(): ), patch.object(mcp_server, "_owner_fingerprint_for", return_value="owner-X"), patch.object(mcp_server, "_MAX_STATEFUL_SESSIONS_PER_OWNER", cap), - patch.object( - session_manager_stateful, "handle_request", side_effect=stateful_handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=stateful_handle), patch.object(session_manager_stateful, "_server_instances", instances), patch.object(session_manager_stateless, "_server_instances", {}), patch.dict(mcp_server._stateful_session_owners, owners, clear=True), @@ -1696,18 +1826,14 @@ async def test_mcp_routing_initialize_rejected_when_owner_at_session_cap(): {f"s{i}": float(i) for i in range(cap)}, clear=True, ), - patch.dict( - mcp_server._stateful_session_active_request_counts, active, clear=True - ), + patch.dict(mcp_server._stateful_session_active_request_counts, active, clear=True), patch.dict(mcp_server._stateful_session_auth_contexts, contexts, clear=True), ): await handle_streamable_http_mcp(scope, receive, send) assert not stateful_called, "initialize at session cap must not reach the manager" start_messages = [ - call.args[0] - for call in send.call_args_list - if call.args and call.args[0].get("type") == "http.response.start" + call.args[0] for call in send.call_args_list if call.args and call.args[0].get("type") == "http.response.start" ] assert start_messages, "a response should have been sent" assert start_messages[0]["status"] == 429 @@ -1743,9 +1869,7 @@ async def test_stateful_mcp_requests_refresh_session_auth_context(): None, "1.1.1.1", ) - mcp_server._stateful_session_auth_contexts[session_id] = callback_context.run( - mcp_server.auth_context_var.get - ) + mcp_server._stateful_session_auth_contexts[session_id] = callback_context.run(mcp_server.auth_context_var.get) scope = { "type": "http", @@ -2006,24 +2130,14 @@ async def test_initialize_request_with_existing_session_tracks_new_session(): ) await mcp_server._purge_expired_stateful_session_auth_contexts(now=now) assert new_session_id in mcp_server._stateful_session_auth_contexts - assert ( - mcp_server._stateful_session_auth_contexts[new_session_id] - is not existing_auth_user - ) - assert ( - mcp_server._stateful_session_auth_contexts[new_session_id].mcp_auth_header - == "new-mcp-auth" - ) + assert mcp_server._stateful_session_auth_contexts[new_session_id] is not existing_auth_user + assert mcp_server._stateful_session_auth_contexts[new_session_id].mcp_auth_header == "new-mcp-auth" async def stateless_handle(s, r, se): - raise AssertionError( - "initialize request with session should use stateful manager" - ) + raise AssertionError("initialize request with session should use stateful manager") try: - mcp_server._stateful_session_auth_contexts[existing_session_id] = ( - existing_auth_user - ) + mcp_server._stateful_session_auth_contexts[existing_session_id] = existing_auth_user mcp_server._stateful_session_auth_context_last_seen[existing_session_id] = 1.0 mcp_server._stateful_session_owners[existing_session_id] = owner_fingerprint @@ -2065,10 +2179,7 @@ async def test_initialize_request_with_existing_session_tracks_new_session(): assert stateful_called assert new_session_id not in mcp_server._stateful_session_active_request_counts assert new_session_id in mcp_server._stateful_session_auth_contexts - assert ( - mcp_server._stateful_session_auth_contexts[existing_session_id] - is existing_auth_user - ) + assert mcp_server._stateful_session_auth_contexts[existing_session_id] is existing_auth_user assert existing_auth_user.mcp_auth_header == "old-mcp-auth" assert existing_auth_user.mcp_servers == ["old-server"] finally: @@ -2191,15 +2302,11 @@ async def test_stateful_mcp_cleanup_loop_survives_purge_errors(): except ImportError: pytest.skip("MCP server not available") - purge = AsyncMock( - side_effect=[RuntimeError("terminate failed"), asyncio.CancelledError()] - ) + purge = AsyncMock(side_effect=[RuntimeError("terminate failed"), asyncio.CancelledError()]) with ( patch.object(mcp_server.asyncio, "sleep", AsyncMock(return_value=None)), - patch.object( - mcp_server, "_purge_expired_stateful_session_auth_contexts", purge - ), + patch.object(mcp_server, "_purge_expired_stateful_session_auth_contexts", purge), ): with pytest.raises(asyncio.CancelledError): await mcp_server._cleanup_expired_stateful_session_auth_contexts() @@ -2289,9 +2396,7 @@ async def test_stateful_mcp_session_owner_mismatch_returns_403(): intruder_auth = UserAPIKeyAuth(api_key="intruder-key", user_id="intruder") mcp_server._stateful_session_auth_contexts[session_id] = MagicMock() - mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for( - owner_auth - ) + mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for(owner_auth) scope = { "type": "http", @@ -2341,9 +2446,7 @@ async def test_stateful_mcp_session_owner_mismatch_returns_403(): await handle_streamable_http_mcp(scope, receive, capture_send) handle_request_mock.assert_not_awaited() - statuses = [ - m["status"] for m in sent_messages if m.get("type") == "http.response.start" - ] + statuses = [m["status"] for m in sent_messages if m.get("type") == "http.response.start"] assert statuses == [403] mcp_server._stateful_session_auth_contexts.pop(session_id, None) @@ -2368,12 +2471,10 @@ async def test_stateful_mcp_session_serializes_concurrent_requests(): session_id = "serialized-session-1" owner_auth = UserAPIKeyAuth(api_key="owner-key", user_id="owner") - mcp_server._stateful_session_auth_contexts[session_id] = ( - mcp_server.MCPAuthenticatedUser(user_api_key_auth=owner_auth) - ) - mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for( - owner_auth + mcp_server._stateful_session_auth_contexts[session_id] = mcp_server.MCPAuthenticatedUser( + user_api_key_auth=owner_auth ) + mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for(owner_auth) inside = 0 max_inside = 0 @@ -2414,9 +2515,7 @@ async def test_stateful_mcp_session_serializes_concurrent_requests(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateful, "handle_request", side_effect=slow_handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=slow_handle), patch.object( session_manager_stateful, "_server_instances", @@ -2432,9 +2531,7 @@ async def test_stateful_mcp_session_serializes_concurrent_requests(): mcp_server._stateful_session_owners.pop(session_id, None) mcp_server._stateful_session_locks.pop(session_id, None) - assert ( - max_inside == 1 - ), "concurrent requests on same stateful session must be serialized" + assert max_inside == 1, "concurrent requests on same stateful session must be serialized" @pytest.mark.asyncio @@ -2486,9 +2583,7 @@ async def test_stateful_mcp_lock_does_not_leak_when_auth_context_missing(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateful, "handle_request", side_effect=handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=handle), patch.object( session_manager_stateful, "_server_instances", @@ -2498,9 +2593,9 @@ async def test_stateful_mcp_lock_does_not_leak_when_auth_context_missing(): assert session_id not in mcp_server._stateful_session_auth_contexts await handle_streamable_http_mcp(scope, receive, AsyncMock()) - assert ( - session_id not in mcp_server._stateful_session_locks - ), "lock entry must be cleaned up for untracked stateful session" + assert session_id not in mcp_server._stateful_session_locks, ( + "lock entry must be cleaned up for untracked stateful session" + ) finally: mcp_server._stateful_session_auth_contexts.pop(session_id, None) mcp_server._stateful_session_owners.pop(session_id, None) @@ -2526,12 +2621,10 @@ async def test_stateful_mcp_get_stream_does_not_block_post(): session_id = "stream-session-1" owner_auth = UserAPIKeyAuth(api_key="owner-key", user_id="owner") - mcp_server._stateful_session_auth_contexts[session_id] = ( - mcp_server.MCPAuthenticatedUser(user_api_key_auth=owner_auth) - ) - mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for( - owner_auth + mcp_server._stateful_session_auth_contexts[session_id] = mcp_server.MCPAuthenticatedUser( + user_api_key_auth=owner_auth ) + mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for(owner_auth) stream_release = asyncio.Event() post_finished = asyncio.Event() @@ -2569,9 +2662,7 @@ async def test_stateful_mcp_get_stream_does_not_block_post(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateful, "handle_request", side_effect=handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=handle), patch.object( session_manager_stateful, "_server_instances", @@ -2615,10 +2706,7 @@ def test_jsonrpc_text_has_top_level_method_ignores_nested_method(): assert _jsonrpc_text_has_top_level_method(reordered) is True # response whose result nests a "method" key (and arrays of them) - response = ( - '{"jsonrpc":"2.0","id":1,"result":{"toolResult":{"method":"GET"},' - '"steps":[{"method":"x"}]}}' - ) + response = '{"jsonrpc":"2.0","id":1,"result":{"toolResult":{"method":"GET"},"steps":[{"method":"x"}]}}' assert _jsonrpc_text_has_top_level_method(response) is False # truncated response: result value never closes, no top-level method seen @@ -2643,12 +2731,10 @@ async def test_truncated_jsonrpc_response_with_nested_method_skips_lock(): session_id = "nested-method-response-session" owner_auth = UserAPIKeyAuth(api_key="owner-key", user_id="owner") - mcp_server._stateful_session_auth_contexts[session_id] = ( - mcp_server.MCPAuthenticatedUser(user_api_key_auth=owner_auth) - ) - mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for( - owner_auth + mcp_server._stateful_session_auth_contexts[session_id] = mcp_server.MCPAuthenticatedUser( + user_api_key_auth=owner_auth ) + mcp_server._stateful_session_owners[session_id] = mcp_server._owner_fingerprint_for(owner_auth) gate = asyncio.Event() request_in_handle = asyncio.Event() @@ -2685,8 +2771,7 @@ async def test_truncated_jsonrpc_response_with_nested_method_skips_lock(): # parsed, with a nested "method" key in the first bytes to trip a flat # substring heuristic. response_body = ( - '{"jsonrpc":"2.0","id":99,"result":{"toolResult":' - '{"method":"GET","payload":"' + ("x" * 5000) + '"}}}' + '{"jsonrpc":"2.0","id":99,"result":{"toolResult":{"method":"GET","payload":"' + ("x" * 5000) + '"}}}' ).encode() try: @@ -2700,9 +2785,7 @@ async def test_truncated_jsonrpc_response_with_nested_method_skips_lock(): "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True, ), - patch.object( - session_manager_stateful, "handle_request", side_effect=handle - ), + patch.object(session_manager_stateful, "handle_request", side_effect=handle), patch.object( session_manager_stateful, "_server_instances", @@ -2774,13 +2857,9 @@ async def test_mcp_routing_with_conflicting_alias_and_group_name(): mock_get_tools_spy = AsyncMock(return_value=[]) # Mock the function that checks DB for an access group named "custom_solutions" - mock_db_lookup = AsyncMock( - return_value=[specific_server.server_id, other_server.server_id] - ) + mock_db_lookup = AsyncMock(return_value=[specific_server.server_id, other_server.server_id]) - mock_get_allowed = AsyncMock( - return_value=[specific_server.server_id, other_server.server_id] - ) + mock_get_allowed = AsyncMock(return_value=[specific_server.server_id, other_server.server_id]) with ( patch( @@ -2805,14 +2884,12 @@ async def test_mcp_routing_with_conflicting_alias_and_group_name(): ) # Get the list of actual server objects that the orchestrator tried to contact - called_servers = [ - call.kwargs["server"] for call in mock_get_tools_spy.call_args_list - ] + called_servers = [call.kwargs["server"] for call in mock_get_tools_spy.call_args_list] assert len(called_servers) == 1, "Should have resolved to exactly one server." - assert ( - called_servers[0].server_id == specific_server.server_id - ), "Should have contacted the specific server alias, not the group." + assert called_servers[0].server_id == specific_server.server_id, ( + "Should have contacted the specific server alias, not the group." + ) @pytest.mark.asyncio @@ -2926,9 +3003,7 @@ async def test_oauth2_caller_headers_not_forwarded_for_migrated_server(): ) # Verify that _create_mcp_client was called - assert ( - mock_create_client.call_count == 1 - ), "Expected _create_mcp_client to be called once" + assert mock_create_client.call_count == 1, "Expected _create_mcp_client to be called once" # Verify the server passed to _create_mcp_client is the OAuth2 server assert captured_client_args["server"].server_id == oauth2_server.server_id @@ -2938,9 +3013,9 @@ async def test_oauth2_caller_headers_not_forwarded_for_migrated_server(): # oauth2 Authorization upstream. The v2 resolver injects the stored per-user token, # so a caller-supplied bearer cannot override another user's stored credential. extra_headers = captured_client_args["extra_headers"] - assert extra_headers is None or "Authorization" not in { - k.lower() for k in extra_headers - }, f"Caller Authorization must not be forwarded, got {extra_headers}" + assert extra_headers is None or "Authorization" not in {k.lower() for k in extra_headers}, ( + f"Caller Authorization must not be forwarded, got {extra_headers}" + ) @pytest.mark.asyncio @@ -3049,12 +3124,8 @@ async def test_list_tools_multiple_servers_prefixed_names(): # Mock manager mock_manager = MagicMock() - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["server1", "server2"] - ) - mock_manager.get_mcp_server_by_id = lambda server_id: ( - server1 if server_id == "server1" else server2 - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["server1", "server2"]) + mock_manager.get_mcp_server_by_id = lambda server_id: server1 if server_id == "server1" else server2 # Mock filter_server_ids_by_ip to return server_ids unchanged (no IP filtering) mock_manager.filter_server_ids_by_ip_with_info = lambda server_ids, client_ip: ( server_ids, @@ -3653,9 +3724,7 @@ async def test_list_tools_strips_prefix_when_matching_permissions(): tool1.inputSchema = {} tool2 = MagicMock() - tool2.name = ( - "GITMCP-search_litellm_documentation" # Prefixed, not in allowed list - ) + tool2.name = "GITMCP-search_litellm_documentation" # Prefixed, not in allowed list tool2.description = "Search docs" tool2.inputSchema = {} @@ -3876,17 +3945,13 @@ class TestMCPServerManagerReload: db_row = _make_db_mcp_server("server-1", timestamp) mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=[db_row] - ) + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[db_row]) with ( patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", return_value=mock_prisma, ), - patch.object( - manager, "build_mcp_server_from_table", AsyncMock() - ) as mock_build, + patch.object(manager, "build_mcp_server_from_table", AsyncMock()) as mock_build, ): await manager.reload_servers_from_database() @@ -3922,9 +3987,7 @@ class TestMCPServerManagerReload: ) mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=[db_row] - ) + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[db_row]) with ( patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", @@ -4049,9 +4112,7 @@ class TestMCPServerManagerReload: raise RuntimeError("blocked address") mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=[healthy_row, bad_openapi_row] - ) + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[healthy_row, bad_openapi_row]) with ( patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", @@ -4147,10 +4208,7 @@ async def test_call_mcp_tool_logs_failure_via_post_call_failure_hook(): ) proxy_logging_mock.post_call_failure_hook.assert_awaited_once() - assert ( - proxy_logging_mock.post_call_failure_hook.await_args.kwargs.get("route") - == "/mcp/call_tool" - ) + assert proxy_logging_mock.post_call_failure_hook.await_args.kwargs.get("route") == "/mcp/call_tool" @pytest.mark.asyncio @@ -4164,6 +4222,7 @@ async def test_get_tools_from_mcp_servers_logs_list_tools_to_spendlogs_when_enab _get_tools_from_mcp_servers, ) from litellm.proxy._types import UserAPIKeyAuth + from mcp.types import Tool as MCPTool except ImportError: pytest.skip("MCP server not available") @@ -4177,12 +4236,20 @@ async def test_get_tools_from_mcp_servers_logs_list_tools_to_spendlogs_when_enab server_a.auth_type = None server_a.extra_headers = None - tool_1 = MagicMock() - tool_1.name = "server_a-tool_1" + tool_1 = MCPTool( + name="server_a-tool_1", + description="test tool", + inputSchema={"type": "object"}, + ) dummy_logging_obj = MagicMock() dummy_logging_obj.model_call_details = {"metadata": {"spend_logs_metadata": {}}} dummy_logging_obj.async_success_handler = AsyncMock() + function_setup_kwargs = {} + + def _capture_function_setup(*_args, **kwargs): + function_setup_kwargs.update(kwargs) + return dummy_logging_obj, None with ( patch( @@ -4206,7 +4273,7 @@ async def test_get_tools_from_mcp_servers_logs_list_tools_to_spendlogs_when_enab ), patch( "litellm.proxy._experimental.mcp_server.server.function_setup", - return_value=(dummy_logging_obj, None), + side_effect=_capture_function_setup, ), ): mock_manager._get_tools_from_server = AsyncMock(return_value=[tool_1]) @@ -4218,13 +4285,13 @@ async def test_get_tools_from_mcp_servers_logs_list_tools_to_spendlogs_when_enab mcp_server_auth_headers=None, log_list_tools_to_spendlogs=True, list_tools_log_source="mcp_protocol", + request_tags=["team-a"], ) assert tools == [tool_1] dummy_logging_obj.async_success_handler.assert_awaited_once() - assert dummy_logging_obj.async_success_handler.await_args.kwargs["result"] == [ - tool_1 - ] + assert dummy_logging_obj.async_success_handler.await_args.kwargs["result"] == [tool_1.model_dump(mode="json")] + assert function_setup_kwargs["metadata"]["tags"] == ["team-a"] spend_meta = dummy_logging_obj.model_call_details["metadata"]["spend_logs_metadata"] assert spend_meta["tool_count_total"] == 1 @@ -4569,9 +4636,7 @@ async def test_get_tools_from_mcp_servers_injects_stored_oauth2_token(): oauth2_server.extra_headers = None # Simulate the DB returning a valid credential for this user+server - prefetched_creds = { - SERVER_ID: {"access_token": STORED_TOKEN, "server_id": SERVER_ID} - } + prefetched_creds = {SERVER_ID: {"access_token": STORED_TOKEN, "server_id": SERVER_ID}} tool_1 = MagicMock() tool_1.name = "atlassian_test-search" @@ -4678,16 +4743,12 @@ class TestMergeGatewayInitializeInstructions: global_mcp_server_manager, ) - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "s1" - ] = "upstream" + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["s1"] = "upstream" try: s = _make_instruction_server(instructions="yaml wins") assert self._merge([s]) == "yaml wins" finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "s1", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("s1", None) def test_upstream_cache_used_when_no_yaml(self): """Upstream cached instructions are used when no YAML override is set.""" @@ -4695,16 +4756,12 @@ class TestMergeGatewayInitializeInstructions: global_mcp_server_manager, ) - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "s1" - ] = "from upstream" + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["s1"] = "from upstream" try: s = _make_instruction_server(instructions=None) assert self._merge([s]) == "from upstream" finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "s1", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("s1", None) def test_spec_path_servers_skipped(self): """OpenAPI (spec_path) servers do not contribute instructions.""" @@ -4718,12 +4775,8 @@ class TestMergeGatewayInitializeInstructions: def test_multiple_servers_merged_with_labels(self): """Multiple servers get label-prefixed and separator-joined.""" - s1 = _make_instruction_server( - server_id="a", name="a", alias="Alpha", instructions="instr A" - ) - s2 = _make_instruction_server( - server_id="b", name="b", alias="Beta", instructions="instr B" - ) + s1 = _make_instruction_server(server_id="a", name="a", alias="Alpha", instructions="instr A") + s2 = _make_instruction_server(server_id="b", name="b", alias="Beta", instructions="instr B") result = self._merge([s1, s2]) assert result is not None assert "[Alpha]" in result and "[Beta]" in result @@ -4743,25 +4796,17 @@ class TestMergeGatewayInitializeInstructions: global_mcp_server_manager, ) - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "c" - ] = "cached C" + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["c"] = "cached C" try: - s_yaml = _make_instruction_server( - server_id="a", name="a", alias="A", instructions="yaml A" - ) - s_spec = _make_instruction_server( - server_id="b", name="b", alias="B", spec_path="/spec.json", url=None - ) + s_yaml = _make_instruction_server(server_id="a", name="a", alias="A", instructions="yaml A") + s_spec = _make_instruction_server(server_id="b", name="b", alias="B", spec_path="/spec.json", url=None) s_cached = _make_instruction_server(server_id="c", name="c", alias="C") result = self._merge([s_yaml, s_spec, s_cached]) assert "yaml A" in result assert "cached C" in result assert "[B]" not in result finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "c", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("c", None) class TestEnsureUpstreamInitializeInstructionsCached: @@ -4773,15 +4818,9 @@ class TestEnsureUpstreamInitializeInstructionsCached: global_mcp_server_manager, ) - server = _make_instruction_server( - server_id="yaml-only", instructions="from yaml" - ) - with patch.object( - global_mcp_server_manager, "_create_mcp_client", AsyncMock() - ) as mock_create: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) + server = _make_instruction_server(server_id="yaml-only", instructions="from yaml") + with patch.object(global_mcp_server_manager, "_create_mcp_client", AsyncMock()) as mock_create: + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) mock_create.assert_not_awaited() @pytest.mark.asyncio @@ -4793,21 +4832,13 @@ class TestEnsureUpstreamInitializeInstructionsCached: ) server = _make_instruction_server(server_id="cached-only", instructions=None) - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "cached-only" - ] = "warm" + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["cached-only"] = "warm" try: - with patch.object( - global_mcp_server_manager, "_create_mcp_client", AsyncMock() - ) as mock_create: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) + with patch.object(global_mcp_server_manager, "_create_mcp_client", AsyncMock()) as mock_create: + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) mock_create.assert_not_awaited() finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "cached-only", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("cached-only", None) @pytest.mark.asyncio async def test_skips_when_spec_path_set(self): @@ -4817,15 +4848,9 @@ class TestEnsureUpstreamInitializeInstructionsCached: global_mcp_server_manager, ) - server = _make_instruction_server( - server_id="openapi-spec", spec_path="/openapi.json", url=None - ) - with patch.object( - global_mcp_server_manager, "_create_mcp_client", AsyncMock() - ) as mock_create: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) + server = _make_instruction_server(server_id="openapi-spec", spec_path="/openapi.json", url=None) + with patch.object(global_mcp_server_manager, "_create_mcp_client", AsyncMock()) as mock_create: + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) mock_create.assert_not_awaited() @pytest.mark.asyncio @@ -4847,22 +4872,14 @@ class TestEnsureUpstreamInitializeInstructionsCached: AsyncMock(return_value=fake_client), ): try: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) assert ( - global_mcp_server_manager._upstream_initialize_instructions_by_server_id[ - "cold-server" - ] + global_mcp_server_manager._upstream_initialize_instructions_by_server_id["cold-server"] == "upstream says hi" ) finally: - global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop( - "cold-server", None - ) - global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop( - "cold-server", None - ) + global_mcp_server_manager._upstream_initialize_instructions_by_server_id.pop("cold-server", None) + global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop("cold-server", None) @pytest.mark.asyncio async def test_cooldown_after_empty_upstream_response(self): @@ -4881,27 +4898,13 @@ class TestEnsureUpstreamInitializeInstructionsCached: create = AsyncMock(return_value=fake_client) with patch.object(global_mcp_server_manager, "_create_mcp_client", create): try: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) - assert ( - create.await_count == 1 - ), "Second probe within cooldown must not reconnect to upstream" - assert ( - "empty-server" - not in global_mcp_server_manager._upstream_initialize_instructions_by_server_id - ) - assert ( - "empty-server" - in global_mcp_server_manager._upstream_initialize_instructions_probed_at - ) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) + assert create.await_count == 1, "Second probe within cooldown must not reconnect to upstream" + assert "empty-server" not in global_mcp_server_manager._upstream_initialize_instructions_by_server_id + assert "empty-server" in global_mcp_server_manager._upstream_initialize_instructions_probed_at finally: - global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop( - "empty-server", None - ) + global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop("empty-server", None) @pytest.mark.asyncio async def test_cooldown_after_upstream_failure(self): @@ -4914,35 +4917,19 @@ class TestEnsureUpstreamInitializeInstructionsCached: server = _make_instruction_server(server_id="boom-server", instructions=None) fake_client = MagicMock() - fake_client.run_with_session = AsyncMock( - side_effect=RuntimeError("upstream down") - ) + fake_client.run_with_session = AsyncMock(side_effect=RuntimeError("upstream down")) fake_client._last_initialize_instructions = None create = AsyncMock(return_value=fake_client) with patch.object(global_mcp_server_manager, "_create_mcp_client", create): try: - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) - await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached( - server - ) - assert ( - create.await_count == 1 - ), "Second probe within cooldown must not reconnect after failure" - assert ( - "boom-server" - not in global_mcp_server_manager._upstream_initialize_instructions_by_server_id - ) - assert ( - "boom-server" - in global_mcp_server_manager._upstream_initialize_instructions_probed_at - ) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) + await global_mcp_server_manager._ensure_upstream_initialize_instructions_cached(server) + assert create.await_count == 1, "Second probe within cooldown must not reconnect after failure" + assert "boom-server" not in global_mcp_server_manager._upstream_initialize_instructions_by_server_id + assert "boom-server" in global_mcp_server_manager._upstream_initialize_instructions_probed_at finally: - global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop( - "boom-server", None - ) + global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop("boom-server", None) @pytest.mark.asyncio async def test_reload_resets_probe_cooldown(self): @@ -4951,19 +4938,12 @@ class TestEnsureUpstreamInitializeInstructionsCached: global_mcp_server_manager, ) - global_mcp_server_manager._upstream_initialize_instructions_probed_at[ - "reload-target" - ] = 1.0 + global_mcp_server_manager._upstream_initialize_instructions_probed_at["reload-target"] = 1.0 try: await global_mcp_server_manager.load_servers_from_config({}) - assert ( - "reload-target" - not in global_mcp_server_manager._upstream_initialize_instructions_probed_at - ) + assert "reload-target" not in global_mcp_server_manager._upstream_initialize_instructions_probed_at finally: - global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop( - "reload-target", None - ) + global_mcp_server_manager._upstream_initialize_instructions_probed_at.pop("reload-target", None) class TestGatewayCreateInitializationOptions: @@ -5029,9 +5009,7 @@ class TestGatewayCreateInitializationOptions: ): assert server.create_initialization_options().server_name == "grafana" - assert ( - server.create_initialization_options().server_name == "litellm-mcp-server" - ) + assert server.create_initialization_options().server_name == "litellm-mcp-server" @pytest.mark.asyncio async def test_sse_handler_scopes_server_name_from_single_server_path(self): @@ -5108,9 +5086,7 @@ class TestGatewayCreateInitializationOptions: await handle_sse_mcp(scope, AsyncMock(), AsyncMock()) assert captured["server_name"] == "grafana" - assert ( - server.create_initialization_options().server_name == "litellm-mcp-server" - ) + assert server.create_initialization_options().server_name == "litellm-mcp-server" def test_contextvar_set_injects_instructions(self): """When ContextVar has a value, it appears in InitializationOptions.""" @@ -5173,6 +5149,10 @@ async def test_list_tools_with_legacy_db_m2m_server_resolves_oauth2_flow(): legacy_server.server_id = "legacy-m2m-id" legacy_server.auth_type = MCPAuth.oauth2 legacy_server.oauth2_flow = None # Legacy: field not set in DB + legacy_server.token_exchange_endpoint = None + legacy_server.audience = None + legacy_server.subject_token_type = None + legacy_server.token_exchange_profile = None legacy_server.token_url = "https://oauth.example.com/token" legacy_server.authorization_url = None legacy_server.client_id = "client-id" @@ -5224,12 +5204,8 @@ async def test_list_tools_with_legacy_db_m2m_server_resolves_oauth2_flow(): ): mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["legacy-m2m-id"]) mock_manager.get_mcp_server_by_id = MagicMock(return_value=legacy_server) - mock_manager.filter_server_ids_by_ip_with_info = MagicMock( - return_value=(["legacy-m2m-id"], 0) - ) - mock_manager._get_tools_from_server = AsyncMock( - side_effect=capture_extra_headers - ) + mock_manager.filter_server_ids_by_ip_with_info = MagicMock(return_value=(["legacy-m2m-id"], 0)) + mock_manager._get_tools_from_server = AsyncMock(side_effect=capture_extra_headers) tools = await _get_tools_from_mcp_servers( user_api_key_auth=user_auth, @@ -5321,10 +5297,8 @@ async def test_call_tool_empty_extra_headers_returns_none(): pass # We only care about the captured headers # With P2 fix: extra_headers should be None (not {}) when all headers filtered - assert ( - captured_extra_headers is None - ), "P2 API consistency issue: expected None for empty extra_headers, got: " + str( - captured_extra_headers + assert captured_extra_headers is None, ( + "P2 API consistency issue: expected None for empty extra_headers, got: " + str(captured_extra_headers) ) @@ -5349,9 +5323,7 @@ async def test_probe_upstream_auth_returns_upstream_status(): "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", return_value=mock_client, ): - status, www_auth = await _probe_upstream_auth( - "http://upstream/mcp", "Bearer some-token" - ) + status, www_auth = await _probe_upstream_auth("http://upstream/mcp", "Bearer some-token") assert status == 401 assert www_auth == 'Bearer realm="test"' @@ -5378,9 +5350,7 @@ async def test_probe_upstream_auth_surfaces_httpx_status_error(): mock_response.status_code = 401 mock_response.headers = {"www-authenticate": 'Bearer realm="test"'} request = httpx.Request("POST", "http://upstream/mcp") - error = httpx.HTTPStatusError( - message="401 Unauthorized", request=request, response=mock_response - ) + error = httpx.HTTPStatusError(message="401 Unauthorized", request=request, response=mock_response) mock_client = MagicMock() mock_client.post = AsyncMock(side_effect=error) @@ -5389,9 +5359,7 @@ async def test_probe_upstream_auth_surfaces_httpx_status_error(): "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", return_value=mock_client, ): - status, www_auth = await _probe_upstream_auth( - "http://upstream/mcp", "Bearer some-token" - ) + status, www_auth = await _probe_upstream_auth("http://upstream/mcp", "Bearer some-token") assert status == 401 assert www_auth == 'Bearer realm="test"' @@ -5409,9 +5377,7 @@ async def test_probe_upstream_auth_fails_open_on_network_error(): "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", return_value=mock_client, ): - status, www_auth = await _probe_upstream_auth( - "http://upstream/mcp", "Bearer some-token" - ) + status, www_auth = await _probe_upstream_auth("http://upstream/mcp", "Bearer some-token") assert status == 200 assert www_auth is None @@ -6334,9 +6300,7 @@ class TestProxyExceptionToHttpException: from litellm.proxy._types import ProxyException http_exc = _proxy_exception_to_http_exception( - ProxyException( - message="Forbidden", type="auth_error", param="key", code=403 - ) + ProxyException(message="Forbidden", type="auth_error", param="key", code=403) ) assert http_exc.status_code == 403 @@ -6400,10 +6364,7 @@ class TestStreamableHttpAuthErrorMapping: assert exc_info.value.status_code == 401 assert exc_info.value.headers["WWW-Authenticate"] == "Bearer" # Must not have emitted a 500 body via the generic catch-all. - assert not any( - m.get("type") == "http.response.start" and m.get("status") == 500 - for m in sent - ) + assert not any(m.get("type") == "http.response.start" and m.get("status") == 500 for m in sent) @pytest.mark.asyncio async def test_sse_propagates_proxy_exception_as_401(self): @@ -6443,7 +6404,585 @@ class TestStreamableHttpAuthErrorMapping: assert exc_info.value.status_code == 401 assert exc_info.value.headers["WWW-Authenticate"] == "Bearer" - assert not any( - m.get("type") == "http.response.start" and m.get("status") == 500 - for m in sent + assert not any(m.get("type") == "http.response.start" and m.get("status") == 500 for m in sent) + + +class TestMCPMetaTraceCarrier: + """`_mcp_meta_trace_carrier` extracts the W3C trace context the MCP client + propagated in the request's params._meta (SEP-414) so the otel_v2 MCP span can + parent to the client's span. Exercises the real MCP SDK `RequestParams.Meta` + shape (extra='allow' preserves the unprefixed keys), not just an injected + carrier.""" + + def test_extracts_trace_context_and_excludes_baggage_and_other_meta(self): + """Only traceparent/tracestate are carried. The client's W3C ``baggage`` is + deliberately dropped even though it rides in params._meta: it is + caller-controlled, and the otel baggage processor stamps allowlisted baggage + keys onto the span, so honoring it would let a client spoof a span's identity + (e.g. ``litellm.team.id``). Dropping it at the source is the regression guard.""" + from types import SimpleNamespace + + from mcp.types import RequestParams + + from litellm.proxy._experimental.mcp_server.server import ( + _mcp_meta_trace_carrier, ) + + meta = RequestParams.Meta.model_validate( + { + "traceparent": "00-11111111111111111111111111111111-2222222222222222-01", + "tracestate": "rojo=1", + "baggage": "litellm.team.id=spoofed-team,litellm.metadata.user_api_key_user_id=attacker", + "progressToken": "p1", + } + ) + carrier = _mcp_meta_trace_carrier(SimpleNamespace(meta=meta)) + assert carrier == { + "traceparent": "00-11111111111111111111111111111111-2222222222222222-01", + "tracestate": "rojo=1", + } + assert "baggage" not in carrier + + def test_none_when_no_trace_context(self): + from types import SimpleNamespace + + from mcp.types import RequestParams + + from litellm.proxy._experimental.mcp_server.server import ( + _mcp_meta_trace_carrier, + ) + + assert _mcp_meta_trace_carrier(None) is None + assert _mcp_meta_trace_carrier(SimpleNamespace(meta=None)) is None + only_progress = RequestParams.Meta.model_validate({"progressToken": "p1"}) + assert _mcp_meta_trace_carrier(SimpleNamespace(meta=only_progress)) is None + + +@pytest.mark.asyncio +async def test_get_allowed_mcp_servers_includes_active_servers_submitted_by_user(): + """BYOM submitters can see approved servers they submitted without allow_all_keys.""" + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + global_mcp_server_manager, + ) + from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth + + submitted_server = _make_mcp_server_for_scope_filter("submitted-1", "user_mcp") + submitter = UserAPIKeyAuth( + user_id="submitter-user", + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-submitter", + ) + other_user = UserAPIKeyAuth( + user_id="other-user", + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-other", + ) + + async def _submitted_ids(prisma_client, user_id): + return ["submitted-1"] if user_id == "submitter-user" else [] + + with ( + patch.object( + global_mcp_server_manager, + "get_registry", + return_value={"submitted-1": submitted_server}, + ), + patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp." + "MCPRequestHandler.get_allowed_mcp_servers", + AsyncMock(return_value=[]), + ), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_active_submitted_mcp_server_ids_for_user", + side_effect=_submitted_ids, + ), + ): + submitter_allowed = await global_mcp_server_manager.get_allowed_mcp_servers(submitter) + other_allowed = await global_mcp_server_manager.get_allowed_mcp_servers(other_user) + + assert "submitted-1" in submitter_allowed + assert "submitted-1" not in other_allowed + + +@pytest.mark.asyncio +async def test_get_active_submitted_mcp_server_ids_for_user_queries_active_rows(): + from litellm.proxy._experimental.mcp_server.db import ( + get_active_submitted_mcp_server_ids_for_user, + ) + from litellm.proxy._types import MCPApprovalStatus + + row = MagicMock() + row.server_id = "submitted-1" + prisma_client = MagicMock() + prisma_client.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[row]) + + result = await get_active_submitted_mcp_server_ids_for_user(prisma_client, "submitter-user") + + assert result == ["submitted-1"] + prisma_client.db.litellm_mcpservertable.find_many.assert_awaited_once_with( + where={ + "submitted_by": "submitter-user", + "approval_status": MCPApprovalStatus.active, + }, + ) + + +@pytest.mark.asyncio +async def test_get_active_submitted_mcp_server_ids_for_user_empty_user_id_skips_db(): + from litellm.proxy._experimental.mcp_server.db import ( + get_active_submitted_mcp_server_ids_for_user, + ) + + prisma_client = MagicMock() + prisma_client.db.litellm_mcpservertable.find_many = AsyncMock() + + assert await get_active_submitted_mcp_server_ids_for_user(prisma_client, "") == [] + prisma_client.db.litellm_mcpservertable.find_many.assert_not_awaited() + + +# --------------------------------------------------------------------------- # +# MCP tool-call isError failure logging +# --------------------------------------------------------------------------- # + + +def _call_tool_result(is_error: bool, text: str) -> CallToolResult: + return CallToolResult(content=[TextContent(type="text", text=text)], isError=is_error) + + +def _mock_mcp_logging_obj() -> MagicMock: + logging_obj = MagicMock() + logging_obj.model_call_details = {} + logging_obj.async_post_mcp_tool_call_hook = AsyncMock() + logging_obj.async_success_handler = AsyncMock() + logging_obj.async_failure_handler = AsyncMock() + return logging_obj + + +def test_extract_mcp_tool_result_error_message(): + from litellm.proxy._experimental.mcp_server.utils import ( + extract_mcp_tool_result_error_message, + ) + + assert extract_mcp_tool_result_error_message(_call_tool_result(True, "boom")) == "boom" + assert extract_mcp_tool_result_error_message(_call_tool_result(False, "ok")) is None + assert ( + extract_mcp_tool_result_error_message(CallToolResult(content=[], isError=True)) + == "MCP tool call returned isError=true" + ) + assert ( + extract_mcp_tool_result_error_message({"isError": True, "content": [{"type": "text", "text": "denied"}]}) + == "denied" + ) + assert extract_mcp_tool_result_error_message({"isError": False, "content": []}) is None + assert extract_mcp_tool_result_error_message({}) is None + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_iserror_logs_failure(): + """Regression test: a CallToolResult with isError=True must go + down the failure logging path (async_failure_handler + post_call_failure_hook), + never async_success_handler.""" + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + from litellm.proxy._experimental.mcp_server.exceptions import MCPToolResultError + + logging_obj = _mock_mcp_logging_obj() + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + user_auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user") + + with patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock): + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(True, "upstream exploded"), + start_time=datetime.now(), + end_time=datetime.now(), + user_api_key_auth=user_auth, + request_data={"litellm_call_id": "cid"}, + ) + + logging_obj.async_success_handler.assert_not_awaited() + logging_obj.failure_handler.assert_called_once() + logging_obj.async_failure_handler.assert_awaited_once() + tool_error = logging_obj.async_failure_handler.await_args.args[0] + assert isinstance(tool_error, MCPToolResultError) + assert str(tool_error) == "upstream exploded" + logging_obj.has_run_logging.assert_any_call(event_type="sync_success") + logging_obj.has_run_logging.assert_any_call(event_type="async_success") + proxy_logging_mock.post_call_failure_hook.assert_awaited_once() + hook_kwargs = proxy_logging_mock.post_call_failure_hook.await_args.kwargs + assert hook_kwargs["route"] == "/mcp/call_tool" + assert hook_kwargs["original_exception"] is tool_error + assert hook_kwargs["user_api_key_dict"] is user_auth + logging_obj.async_post_mcp_tool_call_hook.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_success_path_unchanged(): + """isError=False must keep today's behavior: success handler fires, no + failure logging, no post_call_failure_hook.""" + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + logging_obj = _mock_mcp_logging_obj() + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + result = _call_tool_result(False, "all good") + + with patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock): + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=result, + start_time=datetime.now(), + end_time=datetime.now(), + user_api_key_auth=UserAPIKeyAuth(api_key="test-key", user_id="test-user"), + request_data={}, + ) + + logging_obj.async_success_handler.assert_awaited_once() + assert logging_obj.async_success_handler.await_args.kwargs["result"] is result + logging_obj.async_failure_handler.assert_not_awaited() + logging_obj.failure_handler.assert_not_called() + proxy_logging_mock.post_call_failure_hook.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_iserror_without_auth_skips_failure_hook(): + """Without a UserAPIKeyAuth the failure handlers still fire but the proxy + post_call_failure_hook (which requires one) is skipped.""" + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + logging_obj = _mock_mcp_logging_obj() + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + + with patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock): + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result={"isError": True, "content": [{"type": "text", "text": "denied"}]}, + start_time=datetime.now(), + end_time=datetime.now(), + ) + + logging_obj.async_success_handler.assert_not_awaited() + logging_obj.async_failure_handler.assert_awaited_once() + assert str(logging_obj.async_failure_handler.await_args.args[0]) == "denied" + proxy_logging_mock.post_call_failure_hook.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_strips_credentials_from_failure_hook(): + """Credential-bearing request_data fields (raw request headers, upstream MCP + auth headers, OAuth tokens) must never reach post_call_failure_hook + callbacks; non-credential fields must survive untouched.""" + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + logging_obj = _mock_mcp_logging_obj() + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + user_auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user") + request_data = { + "name": "explode", + "litellm_call_id": "cid", + "raw_headers": {"authorization": "Bearer sk-caller-secret"}, + "mcp_auth_header": "upstream-secret", + "mcp_server_auth_headers": {"srv": {"authorization": "Bearer srv-secret"}}, + "oauth2_headers": {"authorization": "Bearer oauth-secret"}, + "user_api_key_auth": user_auth, + } + + with patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock): + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(True, "boom"), + start_time=datetime.now(), + end_time=datetime.now(), + user_api_key_auth=user_auth, + request_data=request_data, + ) + + proxy_logging_mock.post_call_failure_hook.assert_awaited_once() + hook_request_data = proxy_logging_mock.post_call_failure_hook.await_args.kwargs["request_data"] + assert hook_request_data == {"name": "explode", "litellm_call_id": "cid"} + assert "secret" not in str(hook_request_data) + + +def _real_mcp_logging_obj(call_id: str): + from litellm.litellm_core_utils.litellm_logging import Logging + + start_time = datetime.now() + logging_obj = Logging( + model="MCP: weather/get_forecast", + messages=[{"role": "user", "content": "tool call"}], + stream=False, + call_type="call_mcp_tool", + start_time=start_time, + litellm_call_id=call_id, + function_id="test-fn", + ) + logging_obj.update_environment_variables( + model="MCP: weather/get_forecast", + user="", + optional_params={}, + litellm_params={"api_base": ""}, + ) + logging_obj.model_call_details["mcp_tool_call_metadata"] = { + "name": "get_forecast", + "arguments": {"city": "Paris"}, + "mcp_server_name": "weather", + } + return logging_obj, start_time + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_iserror_builds_failure_payload(monkeypatch): + """The standard logging payload for an isError=True result must carry + status='failure' with the tool's error text, so OTel (whose _parse_error + keys off status) marks the MCP span ERROR.""" + import litellm + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + monkeypatch.setattr(litellm, "failure_callback", []) + monkeypatch.setattr(litellm, "_async_failure_callback", []) + monkeypatch.setattr(litellm, "success_callback", []) + monkeypatch.setattr(litellm, "_async_success_callback", []) + + logging_obj, start_time = _real_mcp_logging_obj("test-mcp-iserror-payload") + + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(True, "upstream exploded"), + start_time=start_time, + end_time=datetime.now(), + ) + + payload = logging_obj.model_call_details["standard_logging_object"] + assert payload["status"] == "failure" + assert payload["error_str"] == "upstream exploded" + assert payload["error_information"]["error_class"] == "MCPToolResultError" + assert payload["metadata"]["mcp_tool_call_metadata"]["name"] == "get_forecast" + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_success_builds_success_payload(monkeypatch): + """isError=False still produces a status='success' payload.""" + import litellm + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + monkeypatch.setattr(litellm, "failure_callback", []) + monkeypatch.setattr(litellm, "_async_failure_callback", []) + monkeypatch.setattr(litellm, "success_callback", []) + monkeypatch.setattr(litellm, "_async_success_callback", []) + + logging_obj, start_time = _real_mcp_logging_obj("test-mcp-success-payload") + + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(False, "all good"), + start_time=start_time, + end_time=datetime.now(), + ) + + payload = logging_obj.model_call_details["standard_logging_object"] + assert payload["status"] == "success" + + +@pytest.mark.asyncio +async def test_fire_mcp_tool_call_logging_iserror_emits_otel_error_span(monkeypatch): + """End-to-end regression for the OTel symptom: an isError=True tool + result must reach OTel as an MCP span with StatusCode.ERROR and the tool's + error message, while isError=False stays non-error.""" + pytest.importorskip("opentelemetry") + from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, + ) + from opentelemetry.trace.status import StatusCode + + import litellm + from litellm.integrations.otel import OpenTelemetryV2Config + from litellm.integrations.otel.logger import OpenTelemetryV2 + from litellm.integrations.otel.plumbing import providers + from litellm.proxy._experimental.mcp_server.server import ( + _fire_mcp_tool_call_logging, + ) + + cfg = OpenTelemetryV2Config(exporter="in_memory", legacy_compat=False) + exporter = InMemorySpanExporter() + tracer_provider = providers.build_tracer_provider(cfg, exporter=exporter) + otel_logger = OpenTelemetryV2(config=cfg, tracer_provider=tracer_provider) + + monkeypatch.setattr(litellm, "failure_callback", []) + monkeypatch.setattr(litellm, "_async_failure_callback", [otel_logger]) + monkeypatch.setattr(litellm, "success_callback", []) + monkeypatch.setattr(litellm, "_async_success_callback", [otel_logger]) + + logging_obj, start_time = _real_mcp_logging_obj("test-mcp-iserror-otel") + + await _fire_mcp_tool_call_logging( + logging_obj=logging_obj, + result=_call_tool_result(True, "upstream exploded"), + start_time=start_time, + end_time=datetime.now(), + ) + + (span,) = exporter.get_finished_spans() + assert span.name == "tools/call get_forecast" + assert span.status.status_code is StatusCode.ERROR + assert span.attributes["error.type"] == "MCPToolResultError" + assert "upstream exploded" in (span.status.description or "") + + +@pytest.mark.asyncio +async def test_call_tool_with_legacy_db_m2m_server_resolves_oauth2_flow(): + """ + Finding 3 regression: the call_mcp_tool path must apply the same request-time + oauth2_flow backstop the listing path does. A legacy DB row with oauth2_flow=NULL + but the M2M credential shape must reach execute_mcp_tool resolved to + client_credentials, or the caller's Authorization would be forwarded to an M2M + upstream on tool execution during a backfill gap (the list path was covered, the + call path was not). + """ + try: + from litellm.proxy._experimental.mcp_server.server import call_mcp_tool + from litellm.proxy._types import UserAPIKeyAuth + from litellm.types.mcp import MCPAuth + except ImportError: + pytest.skip("MCP server not available") + + user_auth = UserAPIKeyAuth(api_key="sk-1234", user_id="test-user") + + legacy_server = MCPServer( + server_id="legacy-m2m-id", + name="legacy_m2m", + alias="legacy_m2m", + server_name="legacy_m2m", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + oauth2_flow=None, # legacy: unstamped + token_url="https://oauth.example.com/token", + client_id="client-id", + client_secret="client-secret", + ) + assert legacy_server.has_client_credentials is False + + captured_servers = {} + + async def capture_execute(*args, **kwargs): + captured_servers["allowed"] = kwargs.get("allowed_mcp_servers") + return MagicMock(name="call_tool_result") + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager", + ) as mock_manager, + patch( + "litellm.proxy._experimental.mcp_server.server.execute_mcp_tool", + side_effect=capture_execute, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._get_allowed_mcp_servers_from_mcp_server_names", + new=AsyncMock(side_effect=lambda mcp_servers, allowed_mcp_servers: allowed_mcp_servers), + ), + ): + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["legacy-m2m-id"]) + mock_manager.get_mcp_server_by_id = MagicMock(return_value=legacy_server) + + await call_mcp_tool(name="legacy_m2m-tool", arguments={}, user_api_key_auth=user_auth) + + resolved = captured_servers["allowed"] + assert resolved and resolved[0].oauth2_flow == "client_credentials" + assert resolved[0].has_client_credentials is True + + +@pytest.mark.parametrize( + "url, expected", + [ + # only the origin may be logged: userinfo, query, fragment, and the PATH are all stripped, + # because hosted MCP servers routinely embed the credential in the path (e.g. /mcp/s/) + ("https://user:s3cr3t@mcp.example.com/mcp?token=abcd1234&x=1", "https://mcp.example.com"), + ("https://mcp.example.com/mcp#frag", "https://mcp.example.com"), + ("https://host:8443/a/b?q=1", "https://host:8443"), + ("https://mcp.zapier.com/api/mcp/s/NDgzcret-token/mcp", "https://mcp.zapier.com"), + ("https://mcp.notion.com/mcp", "https://mcp.notion.com"), + (None, None), + ("", None), + ("not a url", None), + ("http://[::1", None), + ], +) +def test_redact_mcp_resource_url_strips_credentials(url, expected): + """The MCP tool-call log records the upstream resource, so the URL must be redacted to + scheme+host+path: userinfo, query string, and fragment (which can carry embedded tokens or + secret parameters) must never reach spend-log metadata or logging callbacks.""" + from litellm.proxy._experimental.mcp_server.server import _redact_mcp_resource_url + + assert _redact_mcp_resource_url(url) == expected + + +@pytest.mark.asyncio +async def test_call_mcp_tool_skips_failure_hook_for_upstream_auth_error(): + """A client-forwarded pass-through upstream 401/403 (MCPUpstreamAuthError) is an expected + caller-must-reauth signal, not a failed call, so call_mcp_tool must re-raise it WITHOUT firing + post_call_failure_hook (which records a failure and can trip LLM exception alerts). The + streamable handler downgrades it to an informational isError result afterward.""" + from litellm.proxy._experimental.mcp_server.server import ( + call_mcp_tool, + global_mcp_server_manager, + ) + from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError + from litellm.proxy._types import MCPTransport, UserAPIKeyAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + mock_server = MCPServer( + server_id="server-auth", + name="test_server", + alias="test_server", + server_name="test_server", + url="https://test-server.com/mcp", + transport=MCPTransport.http, + mcp_info={"server_name": "test_server"}, + ) + proxy_logging_mock = MagicMock() + proxy_logging_mock.post_call_failure_hook = AsyncMock() + user_auth = UserAPIKeyAuth(api_key="test-key", user_id="test-user") + + with ( + patch.object( + global_mcp_server_manager, + "get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=[mock_server.server_id], + ), + patch.object(global_mcp_server_manager, "get_mcp_server_by_id", return_value=mock_server), + patch( + "litellm.proxy._experimental.mcp_server.server._get_allowed_mcp_servers_from_mcp_server_names", + new_callable=AsyncMock, + return_value=[mock_server], + ), + patch( + "litellm.proxy._experimental.mcp_server.server.execute_mcp_tool", + new_callable=AsyncMock, + side_effect=MCPUpstreamAuthError(status_code=401, www_authenticate="Bearer", server_name="test_server"), + ), + patch("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging_mock), + ): + with pytest.raises(MCPUpstreamAuthError): + await call_mcp_tool( + name="test_server-any_tool", + arguments={"x": 1}, + user_api_key_auth=user_auth, + litellm_call_id="cid", + ) + + proxy_logging_mock.post_call_failure_hook.assert_not_awaited() diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py index 5dec580c771..7c55bd4560f 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -5,12 +5,14 @@ import logging import os import sys from datetime import datetime -from typing import Any, Dict +from typing import Any, Dict, Optional from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi import HTTPException +from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError + # Add the parent directory to the path so we can import litellm sys.path.insert(0, "../../../../../") @@ -47,18 +49,14 @@ from litellm.types.mcp_server.mcp_server_manager import MCPOAuthMetadata, MCPSer def _reload_mcp_manager_module(): utils_module = sys.modules["litellm.proxy._experimental.mcp_server.utils"] - manager_module = sys.modules[ - "litellm.proxy._experimental.mcp_server.mcp_server_manager" - ] + manager_module = sys.modules["litellm.proxy._experimental.mcp_server.mcp_server_manager"] importlib.reload(utils_module) reloaded = importlib.reload(manager_module) # After reload, server.py still holds a stale reference to the old # global_mcp_server_manager. Update it so tests that exercise server.py # functions (e.g. _get_tools_from_mcp_servers) use the fresh instance. server_module = sys.modules.get("litellm.proxy._experimental.mcp_server.server") - if server_module is not None and hasattr( - server_module, "global_mcp_server_manager" - ): + if server_module is not None and hasattr(server_module, "global_mcp_server_manager"): server_module.global_mcp_server_manager = reloaded.global_mcp_server_manager return reloaded @@ -168,9 +166,7 @@ class TestMCPServerManager: auth_type=MCPAuth.oauth2, # oauth2 + no client creds + not delegate -> authorization_code ) - client = await manager._create_mcp_client( - server, mcp_auth_header="Bearer caller-supplied-token" - ) + client = await manager._create_mcp_client(server, mcp_auth_header="Bearer caller-supplied-token") # the v2 resolver ran (the caller override did NOT defer to v1); the stored token wins assert calls == [("", "authz-srv")] @@ -297,6 +293,146 @@ class TestMCPServerManager: assert server.alias == "friendly_alias" assert server.server_name == "validserver" + def _oauth2_config(self, **overrides): + base = { + "url": "https://example.com/mcp", + "transport": MCPTransport.http, + "auth_type": MCPAuth.oauth2, + "token_url": "https://idp.example.com/token", + "client_id": "cid", + "client_secret": "csec", + } + base.update(overrides) + return {"m2mserver": base} + + @pytest.mark.asyncio + async def test_load_servers_from_config_requires_oauth2_flow(self): + """auth_type oauth2 without an explicit oauth2_flow is a config error: the + credential shape is ambiguous (a DCR interactive server looks identical to M2M), + so the config must assert the flow instead of the proxy guessing it.""" + + manager = MCPServerManager() + + with ( + patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)), + pytest.raises(ValueError) as exc_info, + ): + await manager.load_servers_from_config(self._oauth2_config()) + + assert "oauth2_flow: client_credentials" in str(exc_info.value) + assert "oauth2_flow: authorization_code" in str(exc_info.value) + + @pytest.mark.asyncio + async def test_load_servers_from_config_rejects_unknown_oauth2_flow(self): + manager = MCPServerManager() + + with ( + patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)), + pytest.raises(ValueError) as exc_info, + ): + await manager.load_servers_from_config(self._oauth2_config(oauth2_flow="m2m")) + + assert "got 'm2m'" in str(exc_info.value) + + @pytest.mark.asyncio + async def test_load_servers_from_config_accepts_explicit_client_credentials(self): + manager = MCPServerManager() + + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)): + await manager.load_servers_from_config(self._oauth2_config(oauth2_flow="client_credentials")) + + server = next(iter(manager.config_mcp_servers.values())) + assert server.oauth2_flow == "client_credentials" + assert server.has_client_credentials is True + + @pytest.mark.asyncio + async def test_load_servers_from_config_accepts_explicit_authorization_code(self): + manager = MCPServerManager() + + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)): + await manager.load_servers_from_config(self._oauth2_config(oauth2_flow="authorization_code")) + + server = next(iter(manager.config_mcp_servers.values())) + assert server.oauth2_flow == "authorization_code" + assert server.needs_user_oauth_token is True + + @pytest.mark.asyncio + async def test_load_servers_from_config_non_oauth2_needs_no_flow(self): + manager = MCPServerManager() + config = { + "apiserver": { + "url": "https://example.com/mcp", + "transport": MCPTransport.http, + "auth_type": MCPAuth.api_key, + "auth_value": "sk-upstream", + } + } + + await manager.load_servers_from_config(config) + + server = next(iter(manager.config_mcp_servers.values())) + assert server.oauth2_flow is None + + def _client_forwarded_config(self, auth_type, **overrides): + base = { + "url": "https://example.com/mcp", + "transport": MCPTransport.http, + "auth_type": auth_type, + } + base.update(overrides) + return {"bridgeserver": base} + + @pytest.mark.asyncio + async def test_load_servers_from_config_rejects_dcr_bridge_on_gateway_managed_auth_type(self): + manager = MCPServerManager() + + with ( + patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)), + pytest.raises(ValueError) as exc_info, + ): + await manager.load_servers_from_config( + self._oauth2_config(oauth2_flow="authorization_code", dcr_bridge=True) + ) + + assert "dcr_bridge is only supported" in str(exc_info.value) + + @pytest.mark.asyncio + async def test_load_servers_from_config_rejects_non_boolean_dcr_bridge(self): + manager = MCPServerManager() + + with ( + patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)), + pytest.raises(ValueError) as exc_info, + ): + await manager.load_servers_from_config( + self._client_forwarded_config(MCPAuth.true_passthrough, dcr_bridge="yes") + ) + + assert "must be a boolean" in str(exc_info.value) + + @pytest.mark.asyncio + @pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) + async def test_load_servers_from_config_accepts_dcr_bridge_on_client_forwarded_modes(self, auth_type): + manager = MCPServerManager() + + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)): + await manager.load_servers_from_config(self._client_forwarded_config(auth_type, dcr_bridge=True)) + + server = next(iter(manager.config_mcp_servers.values())) + assert server.dcr_bridge is True + assert server.is_dcr_bridge is True + + @pytest.mark.asyncio + async def test_load_servers_from_config_dcr_bridge_defaults_off(self): + manager = MCPServerManager() + + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)): + await manager.load_servers_from_config(self._client_forwarded_config(MCPAuth.true_passthrough)) + + server = next(iter(manager.config_mcp_servers.values())) + assert server.dcr_bridge is None + assert server.is_dcr_bridge is False + @pytest.mark.asyncio async def test_load_servers_from_config_coerces_cost_string_to_float(self): """YAML 1.1 parses `7e-05` as a string; ingest must coerce it to float.""" @@ -441,9 +577,7 @@ class TestMCPServerManager: # Mock get_allowed_mcp_servers to return our test servers manager.get_allowed_mcp_servers = AsyncMock(return_value=["github", "zapier"]) - manager.get_mcp_server_by_id = MagicMock( - side_effect=lambda x: server1 if x == "github" else server2 - ) + manager.get_mcp_server_by_id = MagicMock(side_effect=lambda x: server1 if x == "github" else server2) # Mock _get_tools_from_server to return different results async def mock_get_tools_from_server( @@ -470,9 +604,7 @@ class TestMCPServerManager: "zapier": "zapier-api-key", } - result = await manager.list_tools( - mcp_server_auth_headers=mcp_server_auth_headers - ) + result = await manager.list_tools(mcp_server_auth_headers=mcp_server_auth_headers) # Verify that both servers were called with their specific auth headers assert len(result) == 3 # 2 from github + 1 from zapier @@ -541,9 +673,7 @@ class TestMCPServerManager: mcp_auth_header=None, **kwargs, ): - assert ( - mcp_auth_header == "server-specific-token" - ) # Should use server-specific header + assert mcp_auth_header == "server-specific-token" # Should use server-specific header tool = MagicMock() tool.name = "github_tool_1" return [tool] @@ -574,9 +704,7 @@ class TestMCPServerManager: ) mock_client = AsyncMock() - mock_client.call_tool = AsyncMock( - return_value=CallToolResult(content=[], isError=False) - ) + mock_client.call_tool = AsyncMock(return_value=CallToolResult(content=[], isError=False)) captured_extra_headers = None async def capture_create_mcp_client( @@ -603,6 +731,527 @@ class TestMCPServerManager: assert captured_extra_headers == {"Authorization": "Bearer token"} assert isinstance(result, CallToolResult) + async def _capture_list_subject_token(self, server, oauth2_headers, raw_headers=None): + """Run _get_tools_from_server and return the subject_token it threaded to _create_mcp_client.""" + manager = MCPServerManager() + captured = {} + + async def capture_create_mcp_client( + server, mcp_auth_header, extra_headers, stdio_env, subject_token=None, **kwargs + ): # pragma: no cover - helper + captured["subject_token"] = subject_token + return AsyncMock() + + manager._create_mcp_client = AsyncMock(side_effect=capture_create_mcp_client) + manager._fetch_tools_with_timeout = AsyncMock(return_value=[]) + await manager._get_tools_from_server(server=server, oauth2_headers=oauth2_headers, raw_headers=raw_headers) + return captured["subject_token"] + + @pytest.mark.asyncio + async def test_list_threads_subject_token_for_token_exchange(self): + """tools/list discovery must hand the caller's bearer to the resolver for OBO servers.""" + server = MCPServer( + server_id="te-list", + name="te-list-server", + url="https://up.example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + subject_token = await self._capture_list_subject_token( + server, oauth2_headers={"Authorization": "Bearer subj-jwt"} + ) + assert subject_token == "subj-jwt" + + @pytest.mark.asyncio + async def test_list_does_not_thread_subject_token_for_non_token_exchange(self): + """A non-OBO server must not get the caller's bearer threaded (no leak across modes).""" + server = MCPServer( + server_id="none-list", + name="none-list-server", + url="https://up.example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.none, + ) + subject_token = await self._capture_list_subject_token( + server, oauth2_headers={"Authorization": "Bearer subj-jwt"} + ) + assert subject_token is None + + @pytest.mark.asyncio + async def test_list_subject_token_none_without_oauth2_headers(self): + """Background/registry refresh (no oauth2 headers) lists with no subject token, as before.""" + server = MCPServer( + server_id="te-list-bg", + name="te-list-bg-server", + url="https://up.example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + subject_token = await self._capture_list_subject_token(server, oauth2_headers=None) + assert subject_token is None + + @pytest.mark.asyncio + async def test_list_surfaces_resolver_401_as_upstream_auth_error(self): + """A v2 resolver auth challenge (HTTPException 401) raised while building the client must + surface as MCPUpstreamAuthError with its WWW-Authenticate preserved, so single-server routes + challenge the client instead of the old behavior of masking it to an empty tool list.""" + server = MCPServer( + server_id="te-401", + name="te-401-server", + url="https://up.example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + manager = MCPServerManager() + challenge = ( + 'Bearer resource_metadata="/.well-known/oauth-protected-resource/mcp/te-401-server", error="invalid_token"' + ) + manager._create_mcp_client = AsyncMock( + side_effect=HTTPException(status_code=401, detail="Unauthorized", headers={"WWW-Authenticate": challenge}) + ) + with pytest.raises(MCPUpstreamAuthError) as exc_info: + await manager._get_tools_from_server(server=server, oauth2_headers={"Authorization": "Bearer subj-jwt"}) + assert exc_info.value.status_code == 401 + assert exc_info.value.www_authenticate == challenge + + @pytest.mark.asyncio + async def test_list_absorbs_non_auth_httpexception(self): + """A non-auth HTTP error (e.g. 412 no endpoint, 503 IdP down) must stay absorbed to [] so one + misconfigured/unavailable server does not blank the whole aggregate listing.""" + server = MCPServer( + server_id="te-412", + name="te-412-server", + url="https://up.example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + manager = MCPServerManager() + manager._create_mcp_client = AsyncMock( + side_effect=HTTPException(status_code=412, detail="token exchange endpoint is not configured") + ) + result = await manager._get_tools_from_server( + server=server, oauth2_headers={"Authorization": "Bearer subj-jwt"} + ) + assert result == [] + + def _upstream_status_error(self, status_code: int, www_authenticate: Optional[str] = None) -> httpx.HTTPStatusError: + """Build an httpx.HTTPStatusError shaped like the one the MCP SDK surfaces for an upstream + HTTP failure, so _extract_upstream_auth_failure can read status_code and WWW-Authenticate.""" + request = httpx.Request("POST", "https://up.example.com/mcp") + headers = {"www-authenticate": www_authenticate} if www_authenticate else {} + response = httpx.Response(status_code, headers=headers, request=request) + return httpx.HTTPStatusError(f"HTTP {status_code}", request=request, response=response) + + def _passthrough_call_server(self, auth_type, server_id: str = "pt-call") -> "MCPServer": + return MCPServer( + server_id=server_id, + name=f"{server_id}-server", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=auth_type, + ) + + async def _run_call_regular(self, manager, server): + return await manager._call_regular_mcp_tool( + mcp_server=server, + original_tool_name="tool", + arguments={}, + tasks=[], + mcp_auth_header=None, + mcp_server_auth_headers=None, + oauth2_headers=None, + raw_headers={"authorization": "Bearer caller-upstream-token"}, + proxy_logging_obj=None, + ) + + @pytest.mark.asyncio + @pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) + async def test_call_relays_upstream_401_for_client_forwarded_modes(self, auth_type): + """A client-forwarded pass-through/delegate call must relay an upstream 401 (expired/invalid + token) as MCPUpstreamAuthError with the upstream WWW-Authenticate preserved, so single-server + REST routes challenge the caller instead of masking it as a generic isError. Only 401 is a + re-auth signal; the relay opts into raise_on_error so the transport failure surfaces.""" + server = self._passthrough_call_server(auth_type) + challenge = f'Bearer resource_metadata="/.well-known/oauth-protected-resource/mcp/{server.name}"' + manager = MCPServerManager() + mock_client = AsyncMock() + mock_client.call_tool = AsyncMock(side_effect=self._upstream_status_error(401, challenge)) + manager._create_mcp_client = AsyncMock(return_value=mock_client) + + with pytest.raises(MCPUpstreamAuthError) as exc_info: + await self._run_call_regular(manager, server) + + assert exc_info.value.status_code == 401 + assert exc_info.value.www_authenticate == challenge + assert mock_client.call_tool.call_args.kwargs.get("raise_on_error") is True + + @pytest.mark.asyncio + @pytest.mark.parametrize("is_error", [False, True]) + async def test_call_passthrough_returns_tool_result_unchanged(self, is_error): + """The relay only re-raises transport failures. A tool that RETURNS a result (a success, or a + tool-level isError, neither of which raises) on a pass-through call must be returned verbatim, + never wrapped as MCPUpstreamAuthError or replaced by error_tool_result.""" + server = self._passthrough_call_server(MCPAuth.true_passthrough, server_id=f"pt-ok-{is_error}") + manager = MCPServerManager() + expected = CallToolResult(content=[], isError=is_error) + mock_client = AsyncMock() + mock_client.call_tool = AsyncMock(return_value=expected) + manager._create_mcp_client = AsyncMock(return_value=mock_client) + + result = await self._run_call_regular(manager, server) + + assert result is expected + assert mock_client.call_tool.call_args.kwargs.get("raise_on_error") is True + + @pytest.mark.asyncio + @pytest.mark.parametrize("status_code", [403, 503]) + async def test_call_passthrough_non_reauth_failure_stays_iserror(self, status_code): + """Only an upstream 401 is a re-auth signal. A 403 (authenticated but forbidden; re-auth + won't help) and a genuine non-auth failure (e.g. 503) both keep the default isError + degradation and stay a visible warning, mirroring the list path, rather than being relayed as + a re-auth challenge.""" + from litellm.experimental_mcp_client.client import MCPClient + + server = self._passthrough_call_server(MCPAuth.true_passthrough, server_id=f"pt-{status_code}") + manager = MCPServerManager() + mock_client = AsyncMock() + mock_client.call_tool = AsyncMock(side_effect=self._upstream_status_error(status_code)) + mock_client.error_tool_result = MCPClient.error_tool_result + manager._create_mcp_client = AsyncMock(return_value=mock_client) + + import litellm.proxy._experimental.mcp_server.mcp_server_manager as _mgr_mod + + with patch.object(_mgr_mod, "verbose_logger") as mock_log: + result = await self._run_call_regular(manager, server) + + assert result.isError is True + # A genuine non-auth failure keeps operator visibility at warning level, since call_tool's + # raise_on_error demoted the client-layer error log to debug. + assert mock_log.warning.called + + @pytest.mark.asyncio + async def test_call_non_passthrough_does_not_opt_into_raise_on_error(self): + """Non-client-forwarded auth types keep the default call_tool masking (raise_on_error stays + off), so this relay is scoped to the pass-through modes and cannot regress api_key/OBO calls.""" + server = MCPServer( + server_id="ak-call", + name="ak-call-server", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.api_key, + authentication_token="static-key", + ) + manager = MCPServerManager() + mock_client = AsyncMock() + mock_client.call_tool = AsyncMock(return_value=CallToolResult(content=[], isError=False)) + manager._create_mcp_client = AsyncMock(return_value=mock_client) + + result = await manager._call_regular_mcp_tool( + mcp_server=server, + original_tool_name="tool", + arguments={}, + tasks=[], + mcp_auth_header=None, + mcp_server_auth_headers=None, + oauth2_headers=None, + raw_headers=None, + proxy_logging_obj=None, + ) + + assert result.isError is False + assert mock_client.call_tool.call_args.kwargs.get("raise_on_error") is not True + + def _token_exchange_server(self, server_id: str) -> "MCPServer": + return MCPServer( + server_id=server_id, + name=f"{server_id}-server", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + + @pytest.mark.asyncio + async def test_entra_obo_profile_survives_db_credentials_round_trip(self): + # A credentials blob from the management API / DB carries token_exchange_profile; the DB build + # must reconstruct it onto the MCPServer, and the v2 adapter must map it onto the resolver + # config. Without threading it through, an entra_obo server persisted via the API silently + # falls back to rfc8693 and posts the wrong grant to the IdP. + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import to_server_spec + from litellm.proxy._experimental.mcp_server.outbound_credentials.types import TokenExchangeConfig + + manager = MCPServerManager() + row = LiteLLM_MCPServerTable( + server_id="entra-db-1", + alias="entra_db", + description="entra obo from db", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + credentials={ + "client_id": "cid", + "client_secret": "csec", + "token_exchange_endpoint": "https://login.microsoftonline.com/tid/oauth2/v2.0/token", + "scopes": ["api://target/.default"], + "token_exchange_profile": "entra_obo", + }, + created_at=datetime.now(), + updated_at=datetime.now(), + ) + + built = await manager.build_mcp_server_from_table(row, credentials_are_encrypted=False) + assert built.token_exchange_profile == "entra_obo" + + spec = to_server_spec(built) + assert spec is not None and isinstance(spec.config, TokenExchangeConfig) + assert spec.config.profile == "entra_obo" + + @pytest.mark.asyncio + @pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) + async def test_build_from_table_discovers_upstream_oauth_for_client_forwarded_modes(self, auth_type): + """The gateway's relayed authorize flow (used by the browser-only Authorize) needs the + upstream's authorization_url on the registry entry, and these rows never persist one, so + the DB build must discover it the same way oauth2 rows do.""" + from types import SimpleNamespace + + manager = MCPServerManager() + row = LiteLLM_MCPServerTable( + server_id="cf-db-1", + alias="cf_db", + description="client-forwarded from db", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=auth_type, + created_at=datetime.now(), + updated_at=datetime.now(), + ) + + metadata = SimpleNamespace( + authorization_url="https://idp.example.com/authorize", + token_url="https://idp.example.com/token", + registration_url="https://idp.example.com/register", + scopes=None, + ) + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=metadata)) as mock_discovery: + built = await manager.build_mcp_server_from_table(row, credentials_are_encrypted=False) + + mock_discovery.assert_awaited_once() + assert built.authorization_url == "https://idp.example.com/authorize" + assert built.token_url == "https://idp.example.com/token" + + async def _capture_subject_token(self, call) -> Optional[str]: + """Run a manager method (via ``call(manager)``) and return the subject_token it threaded + into ``_create_mcp_client``.""" + manager = MCPServerManager() + captured: Dict[str, Any] = {} + + async def capture_create_mcp_client( + server, mcp_auth_header, extra_headers, stdio_env, subject_token=None, **kwargs + ): # pragma: no cover - helper + captured["subject_token"] = subject_token + return AsyncMock() + + manager._create_mcp_client = AsyncMock(side_effect=capture_create_mcp_client) + await call(manager) + return captured.get("subject_token") + + @pytest.mark.asyncio + async def test_prompts_thread_subject_token_for_token_exchange(self): + """prompts/list on an OBO server must exchange the caller's bearer, not connect with none.""" + server = self._token_exchange_server("te-prompts") + st = await self._capture_subject_token( + lambda m: m.get_prompts_from_server(server=server, raw_headers={"authorization": "Bearer subj-jwt"}) + ) + assert st == "subj-jwt" + + @pytest.mark.asyncio + async def test_resources_thread_subject_token_for_token_exchange(self): + """resources/list on an OBO server must exchange the caller's bearer.""" + server = self._token_exchange_server("te-resources") + st = await self._capture_subject_token( + lambda m: m.get_resources_from_server(server=server, raw_headers={"authorization": "Bearer subj-jwt"}) + ) + assert st == "subj-jwt" + + @pytest.mark.asyncio + async def test_read_resource_threads_subject_token_for_token_exchange(self): + """resources/read on an OBO server must exchange the caller's bearer.""" + server = self._token_exchange_server("te-read") + st = await self._capture_subject_token( + lambda m: m.read_resource_from_server( + server=server, + url="https://up.example.com/r", + raw_headers={"authorization": "Bearer subj-jwt"}, + ) + ) + assert st == "subj-jwt" + + @pytest.mark.asyncio + async def test_prompts_no_subject_token_for_non_token_exchange(self): + """A non-OBO server must not get the caller's bearer threaded (no cross-mode leak).""" + server = MCPServer( + server_id="none-prompts", + name="none-prompts-server", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.none, + ) + st = await self._capture_subject_token( + lambda m: m.get_prompts_from_server(server=server, raw_headers={"authorization": "Bearer subj-jwt"}) + ) + assert st is None + + @pytest.mark.asyncio + async def test_caller_header_cannot_bypass_v2_for_token_exchange(self): + """A caller-supplied per-server header (x-mcp-*) must NOT disable the OBO exchange: + _create_mcp_client keeps the v2 spec and runs the resolver (which exchanges the subject), + rather than deferring to v1 and forwarding the caller's header verbatim upstream.""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.httpx_auth import ( + StaticHeaderAuth, + ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.result import Ok + + exchanged_subjects = [] + + class _FakeProvider: + async def resolve_credentials(self, subject, server): + exchanged_subjects.append(subject.inbound_token.get_secret_value() if subject.inbound_token else None) + return Ok(StaticHeaderAuth("Bearer MINTED", header_name="Authorization")) + + manager = MCPServerManager(cred_provider=_FakeProvider()) + server = self._token_exchange_server("te-bypass") + + client = await manager._create_mcp_client( + server, + mcp_auth_header="Bearer x-mcp-caller-header", + subject_token="subj-jwt", + ) + + # The resolver ran and exchanged the subject despite the per-server header; not bypassed. + assert exchanged_subjects == ["subj-jwt"] + assert client is not None + + @pytest.mark.asyncio + async def test_injected_authorization_does_not_shadow_obo_minted_token(self): + """A guardrail/static Authorization (e.g. MCPJWTSigner) must NOT shadow the exchanged OBO + token. The resolver-owned credential is authoritative: the conflicting header is dropped and + the minted token is what reaches the upstream, not the injected JWT.""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.httpx_auth import ( + StaticHeaderAuth, + ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.result import Ok + + class _FakeProvider: + async def resolve_credentials(self, subject, server): + return Ok(StaticHeaderAuth("Bearer MINTED", header_name="Authorization")) + + manager = MCPServerManager(cred_provider=_FakeProvider()) + server = self._token_exchange_server("te-shadow") + + client = await manager._create_mcp_client( + server, + extra_headers={"Authorization": "Bearer signer-jwt"}, # simulate the JWT signer + subject_token="subj-jwt", + ) + + # minted token wins (resolved_auth kept), signer's header dropped from extra_headers + assert client._resolved_auth is not None + assert "authorization" not in {k.lower() for k in (client.extra_headers or {})} + + @pytest.mark.asyncio + async def test_preflight_token_exchange_challenges_on_rejected_subject(self): + """A subject the IdP rejects must raise the RFC 9728 401 challenge from the preflight, so a + single-server route fails observably at the transport edge instead of the old behavior of + the session opening and list_tools masking the failed exchange as an empty tool list.""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.result import Error + from litellm.proxy._experimental.mcp_server.outbound_credentials.types import CredError + + class _FakeProvider: + async def resolve_credentials(self, subject, server): + return Error(CredError.of_unauthorized("subject token rejected by the IdP")) + + manager = MCPServerManager(cred_provider=_FakeProvider()) + server = self._token_exchange_server("te-preflight-401") + + with pytest.raises(HTTPException) as exc_info: + await manager.preflight_token_exchange( + server=server, + oauth2_headers={"Authorization": "Bearer rejected-subject"}, + user_api_key_auth=None, + ) + assert exc_info.value.status_code == 401 + headers = exc_info.value.headers or {} + www_authenticate = headers.get("WWW-Authenticate") or headers.get("www-authenticate") or "" + assert "resource_metadata" in www_authenticate + + @pytest.mark.asyncio + async def test_preflight_token_exchange_maps_gateway_fault_to_public_status(self): + """A gateway-fault CredError (e.g. invalid_client) must surface its public status (500) + from the preflight, not the OBO 401 challenge and not an empty-success session.""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.result import Error + from litellm.proxy._experimental.mcp_server.outbound_credentials.types import CredError + + class _FakeProvider: + async def resolve_credentials(self, subject, server): + return Error(CredError.of_misconfigured("token exchange configuration error")) + + manager = MCPServerManager(cred_provider=_FakeProvider()) + server = self._token_exchange_server("te-preflight-500") + + with pytest.raises(HTTPException) as exc_info: + await manager.preflight_token_exchange( + server=server, + oauth2_headers={"Authorization": "Bearer subj"}, + user_api_key_auth=None, + ) + assert exc_info.value.status_code == 500 + + @pytest.mark.asyncio + async def test_preflight_token_exchange_noop_on_success_and_without_subject(self): + """A successful exchange returns without raising, and a request with no bearer never + reaches the resolver (the no-subject case is the existing preemptive challenge's job).""" + from litellm.proxy._experimental.mcp_server.outbound_credentials.httpx_auth import ( + StaticHeaderAuth, + ) + from litellm.proxy._experimental.mcp_server.outbound_credentials.result import Ok + + resolved = [] + + class _FakeProvider: + async def resolve_credentials(self, subject, server): + resolved.append(subject.inbound_token.get_secret_value() if subject.inbound_token else None) + return Ok(StaticHeaderAuth("Bearer MINTED", header_name="Authorization")) + + manager = MCPServerManager(cred_provider=_FakeProvider()) + server = self._token_exchange_server("te-preflight-ok") + + assert ( + await manager.preflight_token_exchange( + server=server, + oauth2_headers={"Authorization": "Bearer good-subject"}, + user_api_key_auth=None, + ) + is None + ) + assert resolved == ["good-subject"] + + await manager.preflight_token_exchange(server=server, oauth2_headers=None, user_api_key_auth=None) + assert resolved == ["good-subject"] + @pytest.mark.asyncio async def test_call_regular_mcp_tool_passthrough_strips_authorization_when_admission_consumed_litellm_key( self, @@ -624,9 +1273,7 @@ class TestMCPServerManager: ) mock_client = AsyncMock() - mock_client.call_tool = AsyncMock( - return_value=CallToolResult(content=[], isError=False) - ) + mock_client.call_tool = AsyncMock(return_value=CallToolResult(content=[], isError=False)) captured_extra_headers = None async def capture_create_mcp_client( @@ -682,17 +1329,10 @@ class TestMCPServerManager: ) # Migrated authorization_code => the centralized strip decision says drop the # caller's Authorization (the v2 resolver injects the stored token). - assert ( - _should_strip_caller_authorization( - mcp_server=server, raw_headers=None, user_api_key_auth=None - ) - is True - ) + assert _should_strip_caller_authorization(mcp_server=server, raw_headers=None, user_api_key_auth=None) is True mock_client = AsyncMock() - mock_client.call_tool = AsyncMock( - return_value=CallToolResult(content=[], isError=False) - ) + mock_client.call_tool = AsyncMock(return_value=CallToolResult(content=[], isError=False)) captured_extra_headers = "unset" async def capture_create_mcp_client( @@ -735,9 +1375,7 @@ class TestMCPServerManager: # Only Authorization present -> nothing left -> None (case-insensitive) assert _without_authorization({"authorization": "Bearer x"}) is None # Authorization dropped, other headers kept - assert _without_authorization( - {"Authorization": "Bearer x", "X-Trace-Id": "t"} - ) == {"X-Trace-Id": "t"} + assert _without_authorization({"Authorization": "Bearer x", "X-Trace-Id": "t"}) == {"X-Trace-Id": "t"} @pytest.mark.asyncio async def test_call_regular_mcp_tool_passthrough_forwards_authorization_with_admission_header( @@ -760,9 +1398,7 @@ class TestMCPServerManager: ) mock_client = AsyncMock() - mock_client.call_tool = AsyncMock( - return_value=CallToolResult(content=[], isError=False) - ) + mock_client.call_tool = AsyncMock(return_value=CallToolResult(content=[], isError=False)) captured_extra_headers = None async def capture_create_mcp_client( @@ -795,9 +1431,7 @@ class TestMCPServerManager: user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), ) - assert captured_extra_headers == { - "Authorization": "Bearer upstream-oauth-bearer" - } + assert captured_extra_headers == {"Authorization": "Bearer upstream-oauth-bearer"} @pytest.mark.asyncio async def test_call_regular_mcp_tool_passthrough_forwards_authorization_for_anonymous_admission( @@ -821,9 +1455,7 @@ class TestMCPServerManager: ) mock_client = AsyncMock() - mock_client.call_tool = AsyncMock( - return_value=CallToolResult(content=[], isError=False) - ) + mock_client.call_tool = AsyncMock(return_value=CallToolResult(content=[], isError=False)) captured_extra_headers = None async def capture_create_mcp_client( @@ -853,9 +1485,342 @@ class TestMCPServerManager: user_api_key_auth=UserAPIKeyAuth(api_key=None), ) - assert captured_extra_headers == { - "Authorization": "Bearer upstream-oauth-bearer" - } + assert captured_extra_headers == {"Authorization": "Bearer upstream-oauth-bearer"} + + async def _capture_call_extra_headers(self, server, oauth2_headers, raw_headers, user_api_key_auth): + manager = MCPServerManager() + mock_client = AsyncMock() + mock_client.call_tool = AsyncMock(return_value=CallToolResult(content=[], isError=False)) + captured = {"extra_headers": "unset"} + + async def capture_create_mcp_client( + server, mcp_auth_header, extra_headers, stdio_env, subject_token=None, **kwargs + ): # pragma: no cover - helper + captured["extra_headers"] = extra_headers + return mock_client + + manager._create_mcp_client = AsyncMock(side_effect=capture_create_mcp_client) + await manager._call_regular_mcp_tool( + mcp_server=server, + original_tool_name="tool", + arguments={}, + tasks=[], + mcp_auth_header=None, + mcp_server_auth_headers=None, + oauth2_headers=oauth2_headers, + raw_headers=raw_headers, + proxy_logging_obj=None, + user_api_key_auth=user_api_key_auth, + ) + return captured["extra_headers"] + + @pytest.mark.asyncio + async def test_call_regular_mcp_tool_true_passthrough_forwards_authorization(self): + from litellm.proxy._types import UserAPIKeyAuth + + server = MCPServer( + server_id="server-true-passthrough", + name="tp-server", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + extra_headers = await self._capture_call_extra_headers( + server, + oauth2_headers={"Authorization": "Bearer upstream-token"}, + raw_headers={"authorization": "Bearer upstream-token"}, + user_api_key_auth=UserAPIKeyAuth(api_key=None), + ) + assert extra_headers == {"Authorization": "Bearer upstream-token"} + + @pytest.mark.asyncio + async def test_call_regular_mcp_tool_oauth_delegate_forwards_separate_authorization(self): + from litellm.proxy._types import UserAPIKeyAuth + + server = MCPServer( + server_id="server-oauth-delegate", + name="od-server", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + extra_headers = await self._capture_call_extra_headers( + server, + oauth2_headers={"Authorization": "Bearer upstream-token"}, + raw_headers={ + "x-litellm-api-key": "Bearer sk-litellm-key", + "authorization": "Bearer upstream-token", + }, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + ) + assert extra_headers == {"Authorization": "Bearer upstream-token"} + + @pytest.mark.asyncio + async def test_call_regular_mcp_tool_oauth_delegate_never_forwards_admission_key(self): + from litellm.proxy._types import UserAPIKeyAuth + + server = MCPServer( + server_id="server-oauth-delegate-leak", + name="od-server", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + extra_headers = await self._capture_call_extra_headers( + server, + oauth2_headers={"Authorization": "Bearer sk-litellm-key"}, + raw_headers={"authorization": "Bearer sk-litellm-key"}, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + ) + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + def test_should_strip_caller_authorization_new_modes(self): + from litellm.proxy._types import UserAPIKeyAuth + + true_passthrough = MCPServer( + server_id="tp", + name="tp", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + assert ( + _should_strip_caller_authorization( + mcp_server=true_passthrough, + raw_headers={"authorization": "Bearer upstream"}, + user_api_key_auth=UserAPIKeyAuth(api_key=None), + ) + is False + ) + + oauth_delegate = MCPServer( + server_id="od", + name="od", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + assert ( + _should_strip_caller_authorization( + mcp_server=oauth_delegate, + raw_headers={ + "x-litellm-api-key": "Bearer sk-litellm-key", + "authorization": "Bearer upstream", + }, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + ) + is False + ) + assert ( + _should_strip_caller_authorization( + mcp_server=oauth_delegate, + raw_headers={"authorization": "Bearer sk-litellm-key"}, + user_api_key_auth=UserAPIKeyAuth(api_key="sk-litellm-key"), + ) + is True + ) + + def test_should_strip_authorization_for_oauth_delegate_admitted_via_jwt_without_api_key(self): + """JWT / SSO / OIDC / session admission yields a UserAPIKeyAuth with a user_id but + api_key=None; the caller's Authorization was that credential and must be stripped for + oauth_delegate when no separate x-litellm-api-key carried admission (LIT-3794-class leak).""" + from litellm.proxy._types import UserAPIKeyAuth + + oauth_delegate = MCPServer( + server_id="od-jwt", + name="od", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + assert ( + _should_strip_caller_authorization( + mcp_server=oauth_delegate, + raw_headers={"authorization": "Bearer eyJ-idp-jwt"}, + user_api_key_auth=UserAPIKeyAuth(user_id="alice", api_key=None), + ) + is True + ) + assert ( + _should_strip_caller_authorization( + mcp_server=oauth_delegate, + raw_headers={ + "x-litellm-api-key": "Bearer sk-1234", + "authorization": "Bearer upstream", + }, + user_api_key_auth=UserAPIKeyAuth(user_id="alice", api_key=None), + ) + is False + ) + + @pytest.mark.asyncio + async def test_call_regular_mcp_tool_oauth_delegate_never_forwards_jwt_admission(self): + from litellm.proxy._types import UserAPIKeyAuth + + server = MCPServer( + server_id="od-jwt-e2e", + name="od", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + extra_headers = await self._capture_call_extra_headers( + server, + oauth2_headers={"Authorization": "Bearer eyJ-idp-jwt"}, + raw_headers={"authorization": "Bearer eyJ-idp-jwt"}, + user_api_key_auth=UserAPIKeyAuth(user_id="alice", api_key=None), + ) + assert not extra_headers or "authorization" not in {k.lower() for k in extra_headers} + + def test_new_passthrough_modes_require_per_user_auth(self): + for auth_type in (MCPAuth.true_passthrough, MCPAuth.oauth_delegate): + server = MCPServer( + server_id="s", + name="s", + url="https://example.com", + transport=MCPTransport.http, + auth_type=auth_type, + ) + assert server.requires_per_user_auth is True + + @pytest.mark.asyncio + async def test_create_mcp_client_forwarded_modes_use_the_passthrough_arm(self): + manager = MCPServerManager() + server = MCPServer( + server_id="tp-egress", + name="tp", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + with ( + patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.resolve_mcp_auth", + new_callable=AsyncMock, + ) as mock_resolve, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient") as mock_client_cls, + ): + await manager._create_mcp_client(server=server, extra_headers={"Authorization": "Bearer upstream-token"}) + mock_resolve.assert_not_awaited() + kwargs = mock_client_cls.call_args.kwargs + emitted = httpx.Request("GET", "https://example.com/mcp") + flow = kwargs["resolved_auth"].auth_flow(emitted) + next(flow) + flow.close() + assert emitted.headers["Authorization"] == "Bearer upstream-token" + assert not kwargs["extra_headers"] or "authorization" not in {k.lower() for k in kwargs["extra_headers"]} + + @staticmethod + def _emitted_authorization(mock_client_cls) -> str: + kwargs = mock_client_cls.call_args.kwargs + emitted = httpx.Request("GET", "https://example.com/mcp") + flow = kwargs["resolved_auth"].auth_flow(emitted) + next(flow) + flow.close() + return emitted.headers["Authorization"] + + @pytest.mark.asyncio + @pytest.mark.parametrize( + "per_server_header", + ["Bearer per-server-token", {"Authorization": "Bearer per-server-token"}], + ) + async def test_create_mcp_client_passthrough_prefers_per_server_token(self, per_server_header): + """A per-server x-mcp-{alias}-authorization value is the explicit one-token-one-server + binding, so it must win over the request-wide Authorization and reach the upstream + verbatim through the passthrough arm.""" + manager = MCPServerManager() + server = MCPServer( + server_id="tp-per-server", + name="tp", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + with ( + patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.resolve_mcp_auth", + new_callable=AsyncMock, + ) as mock_resolve, + patch("litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient") as mock_client_cls, + ): + await manager._create_mcp_client( + server=server, + mcp_auth_header=per_server_header, + extra_headers={"Authorization": "Bearer global-token"}, + ) + mock_resolve.assert_not_awaited() + assert self._emitted_authorization(mock_client_cls) == "Bearer per-server-token" + kwargs = mock_client_cls.call_args.kwargs + assert not kwargs["extra_headers"] or "authorization" not in {k.lower() for k in kwargs["extra_headers"]} + + def test_consumes_caller_authorization_per_mode(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _consumes_caller_authorization, + ) + + def build(**kwargs) -> MCPServer: + return MCPServer( + server_id="s", + name="s", + url="https://example.com", + transport=MCPTransport.http, + **kwargs, + ) + + assert _consumes_caller_authorization(build(auth_type=MCPAuth.true_passthrough)) is True + assert _consumes_caller_authorization(build(auth_type=MCPAuth.oauth_delegate)) is True + assert ( + _consumes_caller_authorization( + build(auth_type=MCPAuth.none, extra_headers=["Authorization"], oauth_passthrough=True) + ) + is True + ) + assert _consumes_caller_authorization(build(auth_type=MCPAuth.oauth2, delegate_auth_to_upstream=True)) is True + assert _consumes_caller_authorization(build(auth_type=MCPAuth.api_key, authentication_token="x")) is False + assert ( + _consumes_caller_authorization( + build( + auth_type=MCPAuth.oauth2, + delegate_auth_to_upstream=True, + oauth2_flow="client_credentials", + token_url="https://idp/token", + ) + ) + is False + ) + + def test_caller_authorization_fans_out_only_with_second_consumer(self): + from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( + _caller_authorization_fans_out, + ) + + delegate = MCPServer( + server_id="od", + name="od", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + ) + second = MCPServer( + server_id="tp", + name="tp", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + ) + static_server = MCPServer( + server_id="static", + name="static", + url="https://example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.api_key, + authentication_token="x", + ) + + assert _caller_authorization_fans_out(delegate, None) is False + assert _caller_authorization_fans_out(delegate, [delegate]) is False + assert _caller_authorization_fans_out(delegate, [delegate, static_server]) is False + assert _caller_authorization_fans_out(delegate, [delegate, second]) is True @pytest.mark.asyncio async def test_get_prompts_from_server_success(self): @@ -944,9 +1909,7 @@ class TestMCPServerManager: mock_client = AsyncMock() mock_resources = [Resource(name="file", uri="https://example.com/file")] mock_client.list_resources = AsyncMock(return_value=mock_resources) - prefixed_resources = [ - Resource(name="alias-server-file", uri="https://example.com/file") - ] + prefixed_resources = [Resource(name="alias-server-file", uri="https://example.com/file")] with ( patch.object( @@ -1030,6 +1993,7 @@ class TestMCPServerManager: mcp_auth_header="auth", extra_headers=None, stdio_env=None, + subject_token=None, ) mock_client.list_resource_templates.assert_awaited_once() mock_prefix.assert_called_once_with(mock_templates, server, add_prefix=False) @@ -1077,9 +2041,7 @@ class TestMCPServerManager: mock_create_client.assert_called_once() called_kwargs = mock_create_client.call_args.kwargs assert called_kwargs["extra_headers"] == {"X-Test": "1", "X-Static": "1"} - mock_client.read_resource.assert_awaited_once_with( - "https://example.com/resource" - ) + mock_client.read_resource.assert_awaited_once_with("https://example.com/resource") assert result is read_result @pytest.mark.asyncio @@ -1184,9 +2146,7 @@ class TestMCPServerManager: request = httpx.Request("GET", url) response_obj = httpx.Response(status_code=404, request=request) mock_response.raise_for_status = MagicMock( - side_effect=httpx.HTTPStatusError( - "not found", request=request, response=response_obj - ) + side_effect=httpx.HTTPStatusError("not found", request=request, response=response_obj) ) return mock_response @@ -1200,19 +2160,11 @@ class TestMCPServerManager: # The Azure issuer is cross-origin against the server_url — use # the issuer itself as server_url so the test exercises the # well-known fetch logic without needing real DNS. - result = await manager._fetch_single_authorization_server_metadata( - issuer, issuer - ) + result = await manager._fetch_single_authorization_server_metadata(issuer, issuer) assert result is not None - assert ( - result.authorization_url - == "https://login.microsoftonline.com/test-tenant-id/oauth2/v2.0/authorize" - ) - assert ( - result.token_url - == "https://login.microsoftonline.com/test-tenant-id/oauth2/v2.0/token" - ) + assert result.authorization_url == "https://login.microsoftonline.com/test-tenant-id/oauth2/v2.0/authorize" + assert result.token_url == "https://login.microsoftonline.com/test-tenant-id/oauth2/v2.0/token" assert result.scopes == ["api://some-scope/.default"] @pytest.mark.asyncio @@ -1226,9 +2178,7 @@ class TestMCPServerManager: response_obj = httpx.Response(status_code=404, request=request) mock_response = MagicMock() mock_response.raise_for_status = MagicMock( - side_effect=httpx.HTTPStatusError( - "not found", request=request, response=response_obj - ) + side_effect=httpx.HTTPStatusError("not found", request=request, response=response_obj) ) mock_client = MagicMock() @@ -1238,19 +2188,11 @@ class TestMCPServerManager: "litellm.proxy._experimental.mcp_server.mcp_server_manager.get_async_httpx_client", return_value=mock_client, ): - result = await manager._fetch_single_authorization_server_metadata( - issuer, issuer - ) + result = await manager._fetch_single_authorization_server_metadata(issuer, issuer) assert result is not None - assert ( - result.authorization_url - == "https://login.microsoftonline.com/test-tenant-id/oauth2/v2.0/authorize" - ) - assert ( - result.token_url - == "https://login.microsoftonline.com/test-tenant-id/oauth2/v2.0/token" - ) + assert result.authorization_url == "https://login.microsoftonline.com/test-tenant-id/oauth2/v2.0/authorize" + assert result.token_url == "https://login.microsoftonline.com/test-tenant-id/oauth2/v2.0/token" @pytest.mark.asyncio async def test_descovery_metadata_falls_back_to_origin_when_no_auth_servers(self): @@ -1265,9 +2207,7 @@ class TestMCPServerManager: ) def raise_http_error(): - raise httpx.HTTPStatusError( - "unauthorized", request=request, response=response_obj - ) + raise httpx.HTTPStatusError("unauthorized", request=request, response=response_obj) response_obj.raise_for_status = MagicMock(side_effect=raise_http_error) @@ -1319,8 +2259,10 @@ class TestMCPServerManager: registration_url="https://discovered.example.com/register", ) - async def fake_discovery(server_url: str): + async def fake_discovery(server_url: str, *, allow_origin_fallback: bool = True): assert server_url == "https://example.com/mcp" + # oauth2 (browser flow) keeps the origin fallback; only OBO disables it. + assert allow_origin_fallback is True return discovered_metadata manager._descovery_metadata = fake_discovery # type: ignore[attr-defined] @@ -1330,6 +2272,7 @@ class TestMCPServerManager: "url": "https://example.com/mcp", "transport": MCPTransport.http, "auth_type": MCPAuth.oauth2, + "oauth2_flow": "authorization_code", "scopes": ["config"], "authorization_url": "https://config.example.com/auth", } @@ -1343,6 +2286,107 @@ class TestMCPServerManager: assert server.token_url == "https://discovered.example.com/token" assert server.registration_url == "https://discovered.example.com/register" + @pytest.mark.asyncio + async def test_load_servers_from_config_filters_blank_scopes(self): + """A YAML ``scopes: [""]`` must normalize to None (matching the DB path), so a blank-only + list never becomes a ``("",)`` tuple that skips the entra_obo fail-closed scope check.""" + manager = MCPServerManager() + config = { + "entra": { + "url": "https://up.example.com/mcp", + "transport": MCPTransport.http, + "auth_type": MCPAuth.oauth2_token_exchange, + "token_exchange_profile": "entra_obo", + "token_exchange_endpoint": "https://login.microsoftonline.com/t/oauth2/v2.0/token", + "client_id": "cid", + "client_secret": "csec", + "scopes": ["", " "], + } + } + + await manager.load_servers_from_config(config) + + server = next(iter(manager.config_mcp_servers.values())) + assert server.scopes is None + + # And the exchange precondition now fails closed before any IdP call. + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import to_server_spec + from litellm.proxy._experimental.mcp_server.outbound_credentials.result import Error + from litellm.proxy._experimental.mcp_server.outbound_credentials.token_exchanger import ( + OboTokenExchanger, + ) + + spec = to_server_spec(server) + assert spec is not None + assert spec.config.scopes == () + + async def _must_not_post(url, form, headers): + raise AssertionError("entra_obo with blank scopes must fail closed before POSTing to the IdP") + + result = await OboTokenExchanger(_must_not_post).exchange("subj", spec, spec.config) + assert isinstance(result, Error) + assert result.error.tag == "misconfigured" + + @pytest.mark.asyncio + async def test_load_servers_from_config_reads_all_token_exchange_fields(self): + """Every token-exchange setting is configurable through config.yaml as a top-level + key (the config counterpart of the REST/UI columns) and reaches the resolver spec; + omitted keys resolve to their documented defaults. token_exchange servers need no + oauth2_flow (that requirement is oauth2-only).""" + from litellm.types.mcp import DEFAULT_SUBJECT_TOKEN_TYPE + + manager = MCPServerManager() + config = { + "te_full": { + "url": "https://up.example.com/mcp", + "transport": MCPTransport.http, + "auth_type": MCPAuth.oauth2_token_exchange, + "token_exchange_endpoint": "https://idp.example.com/oauth2/token", + "audience": "api://upstream", + "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", + "token_exchange_profile": "entra_obo", + "client_id": "cid", + "client_secret": "csec", + "scopes": ["api://upstream/.default"], + }, + "te_minimal": { + "url": "https://up2.example.com/mcp", + "transport": MCPTransport.http, + "auth_type": MCPAuth.oauth2_token_exchange, + "token_exchange_endpoint": "https://idp2.example.com/oauth2/token", + "client_id": "cid2", + "client_secret": "csec2", + }, + } + + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)): + await manager.load_servers_from_config(config) + + by_name = {s.server_name: s for s in manager.config_mcp_servers.values()} + + full = by_name["te_full"] + assert full.token_exchange_endpoint == "https://idp.example.com/oauth2/token" + assert full.audience == "api://upstream" + assert full.subject_token_type == "urn:ietf:params:oauth:token-type:jwt" + assert full.token_exchange_profile == "entra_obo" + + minimal = by_name["te_minimal"] + assert minimal.audience is None + assert minimal.subject_token_type == DEFAULT_SUBJECT_TOKEN_TYPE + assert minimal.token_exchange_profile == "rfc8693" + + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import to_server_spec + + spec = to_server_spec(full) + assert spec is not None + assert spec.config.token_exchange_endpoint == "https://idp.example.com/oauth2/token" + assert spec.config.profile == "entra_obo" + + minimal_spec = to_server_spec(minimal) + assert minimal_spec is not None + assert minimal_spec.config.subject_token_type == DEFAULT_SUBJECT_TOKEN_TYPE + assert minimal_spec.config.profile == "rfc8693" + @pytest.mark.asyncio async def test_config_oauth_initialize_tool_name_to_mcp_server_name_mapping(self): manager = MCPServerManager() @@ -1352,6 +2396,7 @@ class TestMCPServerManager: "url": "https://example.com/mcp", "transport": MCPTransport.http, "auth_type": MCPAuth.oauth2, + "oauth2_flow": "authorization_code", "scopes": ["config"], "authorization_url": "https://config.example.com/auth", } @@ -1384,9 +2429,7 @@ class TestMCPServerManager: mcp_auth_header=None, **kwargs, ): - assert ( - mcp_auth_header == "server-specific-token" - ) # Should use server-specific header via server_name + assert mcp_auth_header == "server-specific-token" # Should use server-specific header via server_name tool = MagicMock() tool.name = "github_tool_1" return [tool] @@ -1453,9 +2496,7 @@ class TestMCPServerManager: # Mock failed client.run_with_session mock_client = AsyncMock() - mock_client.run_with_session = AsyncMock( - side_effect=Exception("Connection timeout") - ) + mock_client.run_with_session = AsyncMock(side_effect=Exception("Connection timeout")) manager._create_mcp_client = AsyncMock(return_value=mock_client) # Perform health check @@ -1577,9 +2618,7 @@ class TestMCPServerManager: # Capture the extra_headers passed to _create_mcp_client captured_extra_headers = None - async def capture_create_mcp_client( - server, mcp_auth_header, extra_headers, stdio_env - ): + async def capture_create_mcp_client(server, mcp_auth_header, extra_headers, stdio_env): nonlocal captured_extra_headers captured_extra_headers = extra_headers return mock_client @@ -1921,9 +2960,7 @@ class TestMCPServerManager: proxy_logging_obj = MagicMock() # Mock the async methods that pre_call_tool_check calls - proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock( - return_value={} - ) + proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock(return_value={}) proxy_logging_obj._convert_mcp_to_llm_format = MagicMock(return_value={}) proxy_logging_obj.pre_call_hook = AsyncMock(return_value={}) @@ -1967,13 +3004,8 @@ class TestMCPServerManager: ) assert exc_info.value.status_code == 403 - assert ( - "Tool blocked_tool is not allowed for server test-server" - in exc_info.value.detail["error"] - ) - assert ( - "Contact proxy admin to allow this tool" in exc_info.value.detail["error"] - ) + assert "Tool blocked_tool is not allowed for server test-server" in exc_info.value.detail["error"] + assert "Contact proxy admin to allow this tool" in exc_info.value.detail["error"] @pytest.mark.asyncio async def test_pre_call_tool_check_disallowed_tools_list_allows_tool(self): @@ -1997,9 +3029,7 @@ class TestMCPServerManager: proxy_logging_obj = MagicMock() # Mock the async methods that pre_call_tool_check calls - proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock( - return_value={} - ) + proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock(return_value={}) proxy_logging_obj._convert_mcp_to_llm_format = MagicMock(return_value={}) proxy_logging_obj.pre_call_hook = AsyncMock(return_value={}) @@ -2043,13 +3073,8 @@ class TestMCPServerManager: ) assert exc_info.value.status_code == 403 - assert ( - "Tool banned_tool is not allowed for server test-server" - in exc_info.value.detail["error"] - ) - assert ( - "Contact proxy admin to allow this tool" in exc_info.value.detail["error"] - ) + assert "Tool banned_tool is not allowed for server test-server" in exc_info.value.detail["error"] + assert "Contact proxy admin to allow this tool" in exc_info.value.detail["error"] @pytest.mark.asyncio async def test_pre_call_tool_check_no_restrictions_allows_any_tool(self): @@ -2073,9 +3098,7 @@ class TestMCPServerManager: proxy_logging_obj = MagicMock() # Mock the async methods that pre_call_tool_check calls - proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock( - return_value={} - ) + proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock(return_value={}) proxy_logging_obj._convert_mcp_to_llm_format = MagicMock(return_value={}) proxy_logging_obj.pre_call_hook = AsyncMock(return_value={}) @@ -2112,9 +3135,7 @@ class TestMCPServerManager: proxy_logging_obj = MagicMock() # Mock the async methods that pre_call_tool_check calls - proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock( - return_value={} - ) + proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock(return_value={}) proxy_logging_obj._convert_mcp_to_llm_format = MagicMock(return_value={}) proxy_logging_obj.pre_call_hook = AsyncMock(return_value={}) @@ -2140,10 +3161,7 @@ class TestMCPServerManager: ) assert exc_info.value.status_code == 403 - assert ( - "Tool tool3 is not allowed for server test-server" - in exc_info.value.detail["error"] - ) + assert "Tool tool3 is not allowed for server test-server" in exc_info.value.detail["error"] async def test_get_tools_from_server_add_prefix(self): """Verify _get_tools_from_server respects add_prefix True/False.""" @@ -2174,9 +3192,7 @@ class TestMCPServerManager: assert tools_prefixed[0].name == "zapier-send_email" # Case 2: add_prefix=False (single-server) -> expect unprefixed - tools_unprefixed = await manager._get_tools_from_server( - server, add_prefix=False - ) + tools_unprefixed = await manager._get_tools_from_server(server, add_prefix=False) assert len(tools_unprefixed) == 1 assert tools_unprefixed[0].name == "send_email" @@ -2254,9 +3270,7 @@ class TestMCPServerManager: manager.registry = {"srv-uuid-123": server} manager.tool_name_to_mcp_server_name_mapping["create_zap"] = "zapier" - resolved = manager._resolve_mcp_server_for_tool_call( - "zapier-alias", "create_zap" - ) + resolved = manager._resolve_mcp_server_for_tool_call("zapier-alias", "create_zap") assert resolved is server def test_resolve_mcp_server_for_tool_call_unknown_tool_with_empty_mapping(self): @@ -2481,6 +3495,93 @@ class TestMCPServerManager: assert await manager.has_user_oauth_token(server, user_auth) is False assert calls == [] # short-circuited on the None spec, never hit the resolver + @pytest.mark.asyncio + async def test_invalidate_user_oauth_token_cache_delegates_to_store(self): + """The write side's cache drop reaches the same per-user store the resolver reads.""" + + class _Store: + def __init__(self) -> None: + self.invalidations: list[tuple[str, str]] = [] + + async def fetch(self, user_id: str, server_id: str): + return None + + async def invalidate(self, user_id: str, server_id: str) -> None: + self.invalidations.append((user_id, server_id)) + + store = _Store() + manager = MCPServerManager(per_user_oauth_token_store=store) + await manager.invalidate_user_oauth_token_cache("alice", "srv-1") + assert store.invalidations == [("alice", "srv-1")] + + @pytest.mark.asyncio + async def test_invalidate_user_oauth_token_cache_drops_legacy_cache_too(self): + """A per-user token can be served from the legacy per-user token cache as well as the v2 + store; the shared invalidation must evict both, or the path not evicted keeps serving a + token minted for a replaced credential row until its TTL.""" + + class _Store: + async def fetch(self, user_id: str, server_id: str): + return None + + async def invalidate(self, user_id: str, server_id: str) -> None: + return None + + class _LegacyCache: + def __init__(self) -> None: + self.deletes: list[tuple[str, str]] = [] + + async def delete(self, user_id: str, server_id: str) -> None: + self.deletes.append((user_id, server_id)) + + legacy_cache = _LegacyCache() + manager = MCPServerManager(per_user_oauth_token_store=_Store(), per_user_token_cache=legacy_cache) + await manager.invalidate_user_oauth_token_cache("alice", "srv-1") + assert legacy_cache.deletes == [("alice", "srv-1")] + + @pytest.mark.asyncio + async def test_invalidate_user_oauth_token_cache_swallows_store_errors(self): + """A cache-drop failure must not fail the credential write that triggered it, and the + legacy cache must still be evicted after the v2 store drop fails.""" + + class _Store: + async def fetch(self, user_id: str, server_id: str): + return None + + async def invalidate(self, user_id: str, server_id: str) -> None: + raise RuntimeError("redis down") + + class _LegacyCache: + def __init__(self) -> None: + self.deletes: list[tuple[str, str]] = [] + + async def delete(self, user_id: str, server_id: str) -> None: + self.deletes.append((user_id, server_id)) + + legacy_cache = _LegacyCache() + manager = MCPServerManager(per_user_oauth_token_store=_Store(), per_user_token_cache=legacy_cache) + await manager.invalidate_user_oauth_token_cache("alice", "srv-1") + assert legacy_cache.deletes == [("alice", "srv-1")] + + @pytest.mark.asyncio + async def test_invalidate_user_oauth_token_cache_swallows_legacy_cache_errors(self): + """The legacy cache drop is best-effort like the v2 drop: a failure must be logged, never + raised into the credential write that triggered the invalidation.""" + + class _Store: + async def fetch(self, user_id: str, server_id: str): + return None + + async def invalidate(self, user_id: str, server_id: str) -> None: + return None + + class _RaisingLegacyCache: + async def delete(self, user_id: str, server_id: str) -> None: + raise RuntimeError("redis down") + + manager = MCPServerManager(per_user_oauth_token_store=_Store(), per_user_token_cache=_RaisingLegacyCache()) + await manager.invalidate_user_oauth_token_cache("alice", "srv-1") + @pytest.mark.asyncio async def test_resolve_oauth2_headers_no_user_id(self): """Skip lookup entirely when user_api_key_auth has no user_id.""" @@ -2537,13 +3638,9 @@ class TestMCPServerManager: # Mapping should include both original and prefixed names -> resolves calls either way assert manager.tool_name_to_mcp_server_name_mapping["create_issue"] == "jira" - assert ( - manager.tool_name_to_mcp_server_name_mapping["jira-create_issue"] == "jira" - ) + assert manager.tool_name_to_mcp_server_name_mapping["jira-create_issue"] == "jira" assert manager.tool_name_to_mcp_server_name_mapping["close_issue"] == "jira" - assert ( - manager.tool_name_to_mcp_server_name_mapping["jira-close_issue"] == "jira" - ) + assert manager.tool_name_to_mcp_server_name_mapping["jira-close_issue"] == "jira" def test_get_mcp_server_from_tool_name_with_prefixed_and_unprefixed(self): """After mapping is populated, manager resolves both prefixed and unprefixed tool names to the same server.""" @@ -2573,9 +3670,7 @@ class TestMCPServerManager: assert resolved_server_unpref.server_id == server.server_id # Prefixed resolution - resolved_server_pref = manager._get_mcp_server_from_tool_name( - "zapier-create_zap" - ) + resolved_server_pref = manager._get_mcp_server_from_tool_name("zapier-create_zap") assert resolved_server_pref is not None assert resolved_server_pref.server_id == server.server_id @@ -2620,9 +3715,7 @@ class TestMCPServerManager: new=AsyncMock(return_value=[tool1, tool2, tool3]), ): # Call the REST endpoint helper - filtered_response = await _get_tools_for_single_server( - server, server_auth_header=None - ) + filtered_response = await _get_tools_for_single_server(server, server_auth_header=None) # Verify only allowed tools are in the response assert len(filtered_response) == 2 @@ -2672,9 +3765,7 @@ class TestMCPServerManager: new=AsyncMock(return_value=[tool1, tool2, tool3]), ): # Call the REST endpoint helper - all_tools_response = await _get_tools_for_single_server( - server, server_auth_header=None - ) + all_tools_response = await _get_tools_for_single_server(server, server_auth_header=None) # Verify all tools are returned (no filtering) assert len(all_tools_response) == 3 @@ -2719,9 +3810,7 @@ class TestMCPServerManager: new=AsyncMock(return_value=[tool1, tool2]), ): # Call the REST endpoint helper - all_tools_response = await _get_tools_for_single_server( - server, server_auth_header=None - ) + all_tools_response = await _get_tools_for_single_server(server, server_auth_header=None) # Verify all tools are returned (no filtering) assert len(all_tools_response) == 2 @@ -2791,9 +3880,7 @@ class TestMCPServerManager: ) proxy_logging = MagicMock() - proxy_logging._create_mcp_request_object_from_kwargs = MagicMock( - return_value={} - ) + proxy_logging._create_mcp_request_object_from_kwargs = MagicMock(return_value={}) proxy_logging._convert_mcp_to_llm_format = MagicMock(return_value={}) proxy_logging.pre_call_hook = AsyncMock(return_value=None) @@ -2836,9 +3923,7 @@ class TestMCPServerManager: ) proxy_logging = MagicMock() - proxy_logging._create_mcp_request_object_from_kwargs = MagicMock( - return_value={} - ) + proxy_logging._create_mcp_request_object_from_kwargs = MagicMock(return_value={}) proxy_logging._convert_mcp_to_llm_format = MagicMock(return_value={}) proxy_logging.pre_call_hook = AsyncMock(return_value=None) @@ -2983,9 +4068,7 @@ class TestMCPServerManager: proxy_logging_obj = MagicMock() # Mock the async methods that pre_call_tool_check calls - proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock( - return_value={} - ) + proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock(return_value={}) proxy_logging_obj._convert_mcp_to_llm_format = MagicMock(return_value={}) proxy_logging_obj.pre_call_hook = AsyncMock(return_value={}) @@ -3021,13 +4104,8 @@ class TestMCPServerManager: ) assert exc_info.value.status_code == 403 - assert ( - "Tool deletepet is not allowed for server my_api_mcp" - in exc_info.value.detail["error"] - ) - assert ( - "Contact proxy admin to allow this tool" in exc_info.value.detail["error"] - ) + assert "Tool deletepet is not allowed for server my_api_mcp" in exc_info.value.detail["error"] + assert "Contact proxy admin to allow this tool" in exc_info.value.detail["error"] @pytest.mark.asyncio async def test_call_tool_without_broken_pipe_error(self): @@ -3052,9 +4130,7 @@ class TestMCPServerManager: # Register the server and map a tool to it manager.registry = {"test-server": server} manager.tool_name_to_mcp_server_name_mapping["test_tool"] = "test-server" - manager.tool_name_to_mcp_server_name_mapping["test-server-test_tool"] = ( - "test-server" - ) + manager.tool_name_to_mcp_server_name_mapping["test-server-test_tool"] = "test-server" # Create mock client that tracks call_tool usage mock_client = AsyncMock() @@ -3078,9 +4154,7 @@ class TestMCPServerManager: # Mock proxy logging proxy_logging_obj = MagicMock() - proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock( - return_value={} - ) + proxy_logging_obj._create_mcp_request_object_from_kwargs = MagicMock(return_value={}) proxy_logging_obj._convert_mcp_to_llm_format = MagicMock(return_value={}) proxy_logging_obj.pre_call_hook = AsyncMock(return_value={}) proxy_logging_obj.during_call_hook = AsyncMock(return_value=None) @@ -3145,9 +4219,7 @@ class TestMCPServerManager: # Verify MCPRequestHandler.get_allowed_mcp_servers was called with user_api_key_auth mock_get_allowed.assert_called_once() call_args = mock_get_allowed.call_args - assert ( - call_args[0][0] is user_api_key_auth - ) # First positional arg should be user_api_key_auth + assert call_args[0][0] is user_api_key_auth # First positional arg should be user_api_key_auth assert call_args[0][0].user_id == "user-123" assert call_args[0][0].object_permission_id == "perm_123" assert call_args[0][0].object_permission is not None @@ -3183,9 +4255,7 @@ class TestMCPServerManager: ) with ( - patch.object( - manager, "get_allow_all_keys_server_ids", return_value=["global-server"] - ), + patch.object(manager, "get_allow_all_keys_server_ids", return_value=["global-server"]), patch.object( MCPRequestHandler, "get_allowed_mcp_servers", @@ -3198,6 +4268,244 @@ class TestMCPServerManager: assert result == [] mock_inner.assert_not_called() + @pytest.mark.asyncio + async def test_no_mcp_servers_sentinel_excludes_submitted_byom_servers(self): + from litellm.proxy import proxy_server as proxy_server_module + from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( + MCPRequestHandler, + ) + from litellm.proxy._types import LiteLLM_ObjectPermissionTable, UserAPIKeyAuth + + class _Cache: + async def async_get_cache(self, key: str): + return ["submitted-server"] + + manager = MCPServerManager() + manager.registry = { + "submitted-server": MCPServer( + server_id="submitted-server", + name="submitted", + transport=MCPTransport.http, + ) + } + object_permission = LiteLLM_ObjectPermissionTable( + object_permission_id="perm_no_mcp", + mcp_servers=["no-mcp-servers"], + mcp_access_groups=[], + ) + user_api_key_auth = UserAPIKeyAuth( + api_key="sk-test", + user_id="user-123", + object_permission=object_permission, + object_permission_id="perm_no_mcp", + ) + + with ( + patch.object(proxy_server_module, "user_api_key_cache", _Cache()), + patch.object(proxy_server_module, "prisma_client", None), + patch.object(manager, "get_allow_all_keys_server_ids", return_value=["global-server"]), + patch.object( + MCPRequestHandler, + "get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=["leaked-server"], + ) as mock_inner, + ): + result = await manager.get_allowed_mcp_servers(user_api_key_auth) + + assert result == [] + mock_inner.assert_not_called() + + @pytest.mark.asyncio + async def test_explicitly_scoped_key_excludes_submitted_byom_servers(self): + from litellm.proxy import proxy_server as proxy_server_module + from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( + MCPRequestHandler, + ) + from litellm.proxy._types import LiteLLM_ObjectPermissionTable, UserAPIKeyAuth + + cache = MagicMock() + cache.async_get_cache = AsyncMock(return_value=["submitted-server"]) + + manager = MCPServerManager() + manager.registry = { + "submitted-server": MCPServer( + server_id="submitted-server", + name="submitted", + transport=MCPTransport.http, + ), + "scoped-server": MCPServer( + server_id="scoped-server", + name="scoped", + transport=MCPTransport.http, + ), + } + object_permission = LiteLLM_ObjectPermissionTable( + object_permission_id="perm_scoped", + mcp_servers=["scoped-server"], + mcp_access_groups=[], + ) + user_api_key_auth = UserAPIKeyAuth( + api_key="sk-test", + user_id="user-123", + object_permission=object_permission, + object_permission_id="perm_scoped", + ) + + with ( + patch.object(proxy_server_module, "user_api_key_cache", cache), + patch.object(proxy_server_module, "prisma_client", None), + patch.object(manager, "get_allow_all_keys_server_ids", return_value=[]), + patch.object( + MCPRequestHandler, + "get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=["scoped-server"], + ), + ): + result = await manager.get_allowed_mcp_servers(user_api_key_auth) + + assert result == ["scoped-server"] + cache.async_get_cache.assert_not_awaited() + + @pytest.mark.asyncio + async def test_toolset_scope_excludes_submitted_byom_servers(self): + from litellm.proxy import proxy_server as proxy_server_module + from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( + MCPRequestHandler, + ) + from litellm.proxy._experimental.mcp_server.mcp_context import ( + _mcp_active_toolset_id, + ) + from litellm.proxy._types import UserAPIKeyAuth + + cache = MagicMock() + cache.async_get_cache = AsyncMock(return_value=["submitted-server"]) + + manager = MCPServerManager() + manager.registry = { + "submitted-server": MCPServer( + server_id="submitted-server", + name="submitted", + transport=MCPTransport.http, + ), + "toolset-server": MCPServer( + server_id="toolset-server", + name="toolset", + transport=MCPTransport.http, + ), + } + user_api_key_auth = UserAPIKeyAuth(api_key="sk-test", user_id="user-123") + + token = _mcp_active_toolset_id.set("toolset-abc") + try: + with ( + patch.object(proxy_server_module, "user_api_key_cache", cache), + patch.object(proxy_server_module, "prisma_client", None), + patch.object(manager, "get_allow_all_keys_server_ids", return_value=["global-server"]), + patch.object( + MCPRequestHandler, + "get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=["toolset-server"], + ), + ): + result = await manager.get_allowed_mcp_servers(user_api_key_auth) + finally: + _mcp_active_toolset_id.reset(token) + + assert result == ["toolset-server"] + + @pytest.mark.asyncio + async def test_invalidate_byom_submitted_servers_cache_deletes_key(self): + from litellm.proxy import proxy_server as proxy_server_module + + cache = MagicMock() + cache.async_delete_cache = AsyncMock() + manager = MCPServerManager() + + with patch.object(proxy_server_module, "user_api_key_cache", cache): + await manager.invalidate_byom_submitted_servers_cache("user-123") + await manager.invalidate_byom_submitted_servers_cache(None) + + cache.async_delete_cache.assert_awaited_once_with(key="byom_submitted_servers:user-123") + + @pytest.mark.asyncio + async def test_get_active_submitted_ids_cache_miss_queries_db_and_caches(self): + from litellm.proxy import proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + + cache = MagicMock() + cache.async_get_cache = AsyncMock(return_value=None) + cache.async_set_cache = AsyncMock() + manager = MCPServerManager() + manager.registry = { + "submitted-server": MCPServer( + server_id="submitted-server", + name="submitted", + transport=MCPTransport.http, + ) + } + user_api_key_auth = UserAPIKeyAuth(api_key="sk-test", user_id="user-123") + + with ( + patch.object(proxy_server_module, "user_api_key_cache", cache), + patch.object(proxy_server_module, "prisma_client", MagicMock()), + patch( + "litellm.proxy._experimental.mcp_server.db.get_active_submitted_mcp_server_ids_for_user", + AsyncMock(return_value=["submitted-server", "unknown-server"]), + ), + ): + result = await manager._get_active_submitted_mcp_server_ids_for_user(user_api_key_auth) + + assert result == ["submitted-server"] + cache.async_set_cache.assert_awaited_once_with( + key="byom_submitted_servers:user-123", + value=["submitted-server", "unknown-server"], + ttl=60, + ) + + @pytest.mark.asyncio + async def test_get_allowed_mcp_servers_fallback_keeps_submitted_byom_servers(self): + from litellm.proxy import proxy_server as proxy_server_module + from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( + MCPRequestHandler, + ) + from litellm.proxy._types import UserAPIKeyAuth + + class _Cache: + async def async_get_cache(self, key: str): + assert key == "byom_submitted_servers:user-123" + return ["submitted-server"] + + manager = MCPServerManager() + manager.registry = { + "submitted-server": MCPServer( + server_id="submitted-server", + name="submitted", + transport=MCPTransport.http, + ) + } + user_api_key_auth = UserAPIKeyAuth( + api_key="sk-test", + user_id="user-123", + ) + + with ( + patch.object(proxy_server_module, "user_api_key_cache", _Cache()), + patch.object(proxy_server_module, "prisma_client", None), + patch.object(manager, "get_allow_all_keys_server_ids", return_value=["global-server"]), + patch.object( + MCPRequestHandler, + "get_allowed_mcp_servers", + new_callable=AsyncMock, + side_effect=RuntimeError("permission resolver failed"), + ), + ): + result = await manager.get_allowed_mcp_servers(user_api_key_auth) + + assert set(result) == {"global-server", "submitted-server"} + @pytest.mark.asyncio async def test_get_allowed_mcp_servers_anonymous_delegate_requires_oauth2(self): """Anonymous delegated auth listing should only include oauth2 servers.""" @@ -3339,6 +4647,180 @@ class TestMCPServerTimestamps: default_server = await manager.build_mcp_server_from_table(default_record, credentials_are_encrypted=False) assert default_server.token_endpoint_auth_method is None + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_discovers_obo_token_url_when_unset(self): + """DB path: an OBO server with no token_exchange_endpoint in credentials and no token_url + column runs discovery, and the resolved endpoint lands on the returned MCPServer.""" + manager = MCPServerManager() + calls: list[bool] = [] + + async def fake_discovery(server_url: str, *, allow_origin_fallback: bool = True): + calls.append(allow_origin_fallback) + return MCPOAuthMetadata( + scopes=None, + authorization_url=None, + token_url="https://discovered.example.com/token", + registration_url=None, + ) + + manager._descovery_metadata = fake_discovery # type: ignore[attr-defined] + + record = LiteLLM_MCPServerTable( + server_id="obo-discover-db-1", + server_name="obo_discover_db", + url="https://example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + credentials={"client_id": "cid", "client_secret": "csec"}, + ) + + # prisma_client None -> the write-back no-ops; this test isolates the discovery behavior. + with patch("litellm.proxy.proxy_server.prisma_client", None): + server = await manager.build_mcp_server_from_table(record, credentials_are_encrypted=False) + + assert calls == [False] # discovery ran once, origin fallback disabled for OBO + assert server.token_url == "https://discovered.example.com/token" + + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_skips_discovery_when_obo_endpoint_configured(self): + """DB path: a configured token_exchange_endpoint in the credentials JSON wins and skips + discovery entirely, even though the token_url column is empty (the DB-specific lookup uses + credentials_dict["token_exchange_endpoint"], not the column).""" + manager = MCPServerManager() + calls: list[str] = [] + + async def fake_discovery(server_url: str, *, allow_origin_fallback: bool = True): + calls.append(server_url) + raise AssertionError("discovery must not run when token_exchange_endpoint is configured") + + manager._descovery_metadata = fake_discovery # type: ignore[attr-defined] + + record = LiteLLM_MCPServerTable( + server_id="obo-configured-db-1", + server_name="obo_configured_db", + url="https://example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + credentials={ + "client_id": "cid", + "client_secret": "csec", + "token_exchange_endpoint": "https://idp.example.com/token", + }, + ) + + server = await manager.build_mcp_server_from_table(record, credentials_are_encrypted=False) + + assert calls == [] # discovery never ran + assert server.token_exchange_endpoint == "https://idp.example.com/token" + + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_persists_discovered_obo_token_url(self): + """A DB-backed OBO server with no configured endpoint discovers token_url and must write it + back to the row, so the next rebuild skips discovery instead of re-running it every time.""" + manager = MCPServerManager() + + async def fake_discovery(server_url: str, *, allow_origin_fallback: bool = True): + assert server_url == "https://example.com/mcp" + assert allow_origin_fallback is False # OBO never guesses the origin + return MCPOAuthMetadata( + scopes=None, + authorization_url=None, + token_url="https://discovered.example.com/token", + registration_url=None, + ) + + manager._descovery_metadata = fake_discovery # type: ignore[attr-defined] + + record = LiteLLM_MCPServerTable( + server_id="obo-persist-1", + server_name="obo_persist", + url="https://example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + credentials={"client_id": "cid", "client_secret": "csec", "audience": "aud"}, + ) + + update_mock = AsyncMock() + repo_instance = MagicMock() + repo_instance.table.update = update_mock + with ( + patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPServerRepository", + return_value=repo_instance, + ), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + ): + server = await manager.build_mcp_server_from_table(record, credentials_are_encrypted=False) + + assert server.token_url == "https://discovered.example.com/token" + update_mock.assert_awaited_once() + assert update_mock.call_args.kwargs["where"] == {"server_id": "obo-persist-1"} + assert update_mock.call_args.kwargs["data"] == {"token_url": "https://discovered.example.com/token"} + + @pytest.mark.asyncio + async def test_persist_discovered_obo_token_url_skips_when_not_needed(self): + """The write-back fires only for an OBO server that discovered a new endpoint: a row that + already has token_url, a non-OBO auth_type, or a discovery that found nothing all no-op.""" + manager = MCPServerManager() + update_mock = AsyncMock() + repo_instance = MagicMock() + repo_instance.table.update = update_mock + + with ( + patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPServerRepository", + return_value=repo_instance, + ), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + ): + # already populated -> no write + await manager._persist_discovered_obo_token_url( + server_id="s", + auth_type=MCPAuth.oauth2_token_exchange, + existing_token_url="https://already.example.com/token", + discovered_token_url="https://new.example.com/token", + ) + # not an OBO server -> no write + await manager._persist_discovered_obo_token_url( + server_id="s", + auth_type=MCPAuth.oauth2, + existing_token_url=None, + discovered_token_url="https://new.example.com/token", + ) + # discovery found nothing -> no write + await manager._persist_discovered_obo_token_url( + server_id="s", + auth_type=MCPAuth.oauth2_token_exchange, + existing_token_url=None, + discovered_token_url=None, + ) + + update_mock.assert_not_awaited() + + @pytest.mark.asyncio + async def test_persist_discovered_obo_token_url_is_best_effort(self): + """A write-back failure must not propagate; discovery just re-runs on the next build.""" + manager = MCPServerManager() + update_mock = AsyncMock(side_effect=Exception("db unavailable")) + repo_instance = MagicMock() + repo_instance.table.update = update_mock + + with ( + patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPServerRepository", + return_value=repo_instance, + ), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + ): + await manager._persist_discovered_obo_token_url( + server_id="s", + auth_type=MCPAuth.oauth2_token_exchange, + existing_token_url=None, + discovered_token_url="https://new.example.com/token", + ) + + update_mock.assert_awaited_once() + def test_build_mcp_server_table_preserves_timestamps(self): """_build_mcp_server_table must use the MCPServer's stored timestamps, not datetime.now().""" manager = MCPServerManager() @@ -3376,6 +4858,24 @@ class TestMCPServerTimestamps: assert table.created_at is None assert table.updated_at is None + def test_build_mcp_server_table_preserves_tool_overrides(self): + """Tool display/description overrides must survive registry -> API table conversion.""" + manager = MCPServerManager() + server = MCPServer( + server_id="override-server", + name="deepwiki", + server_name="deepwiki_mcp", + url="https://example.com/mcp", + transport=MCPTransport.http, + tool_name_to_display_name={"read_wiki_structure": "browse_docs"}, + tool_name_to_description={"read_wiki_structure": "Browse repository documentation"}, + ) + + table = manager._build_mcp_server_table(server) + + assert table.tool_name_to_display_name == {"read_wiki_structure": "browse_docs"} + assert table.tool_name_to_description == {"read_wiki_structure": "Browse repository documentation"} + @pytest.mark.asyncio async def test_round_trip_timestamps_preserved(self): """Timestamps survive the full round-trip: LiteLLM_MCPServerTable -> MCPServer -> LiteLLM_MCPServerTable.""" @@ -3404,9 +4904,7 @@ class TestMCPServerTimestamps: ``_deserialize_json_list`` must hand back plain dicts so ``MCPServer`` (typed ``List[Dict[str, Any]]``) validates.""" env_vars = [ - MCPEnvVar( - name="GITHUB_TOKEN", scope=MCPEnvVarScope.user, description="PAT" - ), + MCPEnvVar(name="GITHUB_TOKEN", scope=MCPEnvVarScope.user, description="PAT"), MCPEnvVar(name="REGION", value="us-east-1", scope=MCPEnvVarScope.global_), ] result = _deserialize_json_list(env_vars) @@ -3591,6 +5089,177 @@ class TestMCPServerTimestamps: assert "0.01s" in exc_info.value.detail["message"] +class TestMCPServerTokenExchangeColumns: + """Token-exchange (RFC 8693) config persists through the dedicated columns added for the + create/update REST + DB path, mirroring how ``token_url`` is stored. The credentials JSON + blob is kept as a read-fallback so servers persisted before the columns existed still load.""" + + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_reads_token_exchange_columns(self): + """The DB->runtime loader must read the three fields from the dedicated columns. Before the + columns existed it only read the credentials blob, so column values would be dropped.""" + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="te-cols", + server_name="te_cols", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/oauth2/token", + audience="https://upstream.example.com", + subject_token_type="urn:ietf:params:oauth:token-type:jwt", + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + + assert mcp_server.token_exchange_endpoint == "https://idp.example.com/oauth2/token" + assert mcp_server.audience == "https://upstream.example.com" + assert mcp_server.subject_token_type == "urn:ietf:params:oauth:token-type:jwt" + + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_falls_back_to_credentials_blob(self): + """Backwards compatibility: a server whose token-exchange config lives only in the + credentials blob (no columns) must still load with those values.""" + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="te-blob", + server_name="te_blob", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + credentials={ + "token_exchange_endpoint": "https://idp.example.com/legacy/token", + "audience": "legacy-audience", + "subject_token_type": "urn:ietf:params:oauth:token-type:saml2", + }, + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + + assert mcp_server.token_exchange_endpoint == "https://idp.example.com/legacy/token" + assert mcp_server.audience == "legacy-audience" + assert mcp_server.subject_token_type == "urn:ietf:params:oauth:token-type:saml2" + + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_subject_token_type_defaults(self): + """subject_token_type falls back to the RFC 8693 access_token URN when unset.""" + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="te-default", + server_name="te_default", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/oauth2/token", + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + + assert mcp_server.subject_token_type == "urn:ietf:params:oauth:token-type:access_token" + + @pytest.mark.asyncio + async def test_round_trip_token_exchange_columns_preserved(self): + """The three fields survive LiteLLM_MCPServerTable -> MCPServer -> LiteLLM_MCPServerTable. + Before the table builder wrote them back, a registry round-trip dropped them.""" + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="te-rt", + server_name="te_rt", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/oauth2/token", + audience="https://upstream.example.com", + subject_token_type="urn:ietf:params:oauth:token-type:jwt", + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + rebuilt_table = manager._build_mcp_server_table(mcp_server) + + assert rebuilt_table.token_exchange_endpoint == "https://idp.example.com/oauth2/token" + assert rebuilt_table.audience == "https://upstream.example.com" + assert rebuilt_table.subject_token_type == "urn:ietf:params:oauth:token-type:jwt" + + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_reads_token_exchange_profile_column(self): + """The profile dialect selector (rfc8693 vs entra_obo) is read from its dedicated column + so a server created via the REST API/UI as entra_obo resolves to the Entra dialect.""" + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="te-profile", + server_name="te_profile", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://login.microsoftonline.com/tenant/oauth2/v2.0/token", + token_exchange_profile="entra_obo", + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + + assert mcp_server.token_exchange_profile == "entra_obo" + + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_token_exchange_profile_defaults_rfc8693(self): + """token_exchange_profile falls back to rfc8693 when neither column nor blob sets it.""" + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="te-profile-default", + server_name="te_profile_default", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/oauth2/token", + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + + assert mcp_server.token_exchange_profile == "rfc8693" + + @pytest.mark.asyncio + async def test_build_mcp_server_from_table_token_exchange_profile_blob_fallback(self): + """Backwards compatibility: a server with the profile only in the credentials blob still loads.""" + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="te-profile-blob", + server_name="te_profile_blob", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + credentials={"token_exchange_profile": "entra_obo"}, + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + + assert mcp_server.token_exchange_profile == "entra_obo" + + @pytest.mark.asyncio + async def test_round_trip_token_exchange_profile_preserved(self): + """token_exchange_profile survives LiteLLM_MCPServerTable -> MCPServer -> LiteLLM_MCPServerTable.""" + manager = MCPServerManager() + + table_record = LiteLLM_MCPServerTable( + server_id="te-profile-rt", + server_name="te_profile_rt", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_profile="entra_obo", + ) + + mcp_server = await manager.build_mcp_server_from_table(table_record) + rebuilt_table = manager._build_mcp_server_table(mcp_server) + + assert rebuilt_table.token_exchange_profile == "entra_obo" + + class TestInternalDelegatePkceWarningLog: @pytest.mark.asyncio async def test_build_mcp_server_logs_on_internal_delegate_interactive(self, caplog): @@ -3752,10 +5421,7 @@ class TestMCPServerManagerUpstreamInstructionsCache: def test_get_returns_none_when_empty(self): """Empty cache returns None for any key.""" manager = MCPServerManager() - assert ( - manager._upstream_initialize_instructions_by_server_id.get("nonexistent") - is None - ) + assert manager._upstream_initialize_instructions_by_server_id.get("nonexistent") is None def test_remember_stores_stripped_value(self): """_remember_upstream_initialize_instructions stores a stripped string.""" @@ -3763,9 +5429,7 @@ class TestMCPServerManagerUpstreamInstructionsCache: fake_server = MagicMock(server_id="srv") fake_client = MagicMock(_last_initialize_instructions=" hello \n") manager._remember_upstream_initialize_instructions(fake_server, fake_client) - assert ( - manager._upstream_initialize_instructions_by_server_id.get("srv") == "hello" - ) + assert manager._upstream_initialize_instructions_by_server_id.get("srv") == "hello" def test_remember_ignores_empty_string(self): """Whitespace-only instructions are not stored.""" @@ -3843,9 +5507,7 @@ class TestMCPServerManagerExpandPermissionList: def test_expands_server_name(self): manager = MCPServerManager() - manager.config_mcp_servers["id-usw1"] = self._make_server( - "id-usw1", server_name="a" - ) + manager.config_mcp_servers["id-usw1"] = self._make_server("id-usw1", server_name="a") assert manager.expand_permission_list(["a"]) == ["id-usw1"] @@ -3869,9 +5531,7 @@ class TestMCPServerManagerExpandPermissionList: def test_name_collision_expands_to_all_matches(self): """Two servers sharing a server_name both resolve — the documented behavior.""" manager = MCPServerManager() - manager.config_mcp_servers["id-config"] = self._make_server( - "id-config", server_name="shared" - ) + manager.config_mcp_servers["id-config"] = self._make_server("id-config", server_name="shared") manager.registry["id-db"] = self._make_server("id-db", server_name="shared") assert sorted(manager.expand_permission_list(["shared"])) == [ @@ -3881,9 +5541,7 @@ class TestMCPServerManagerExpandPermissionList: def test_searches_config_and_registry_union(self): manager = MCPServerManager() - manager.config_mcp_servers["cfg-id"] = self._make_server( - "cfg-id", server_name="a" - ) + manager.config_mcp_servers["cfg-id"] = self._make_server("cfg-id", server_name="a") manager.registry["reg-id"] = self._make_server("reg-id", server_name="b") assert manager.expand_permission_list(["a"]) == ["cfg-id"] @@ -3895,23 +5553,15 @@ class TestMCPServerManagerExpandPermissionList: servers whose server_name happens to equal that id. """ manager = MCPServerManager() - manager.config_mcp_servers["id-1"] = self._make_server( - "id-1", server_name="other_name" - ) - manager.config_mcp_servers["id-2"] = self._make_server( - "id-2", server_name="id-1" - ) + manager.config_mcp_servers["id-1"] = self._make_server("id-1", server_name="other_name") + manager.config_mcp_servers["id-2"] = self._make_server("id-2", server_name="id-1") assert manager.expand_permission_list(["id-1"]) == ["id-1"] def test_mixed_ids_and_names_in_same_list(self): manager = MCPServerManager() - manager.config_mcp_servers["uuid-1"] = self._make_server( - "uuid-1", server_name="a" - ) - manager.config_mcp_servers["uuid-2"] = self._make_server( - "uuid-2", server_name="b" - ) + manager.config_mcp_servers["uuid-1"] = self._make_server("uuid-1", server_name="a") + manager.config_mcp_servers["uuid-2"] = self._make_server("uuid-2", server_name="b") # ["uuid-1", "b"] -> uuid-1 passes through, "b" resolves to uuid-2 assert sorted(manager.expand_permission_list(["uuid-1", "b"])) == [ @@ -3922,9 +5572,7 @@ class TestMCPServerManagerExpandPermissionList: def test_deduplicates_overlapping_id_and_name_entries(self): """If a list references the same server by both id and name, return it once.""" manager = MCPServerManager() - manager.config_mcp_servers["uuid-1"] = self._make_server( - "uuid-1", server_name="a" - ) + manager.config_mcp_servers["uuid-1"] = self._make_server("uuid-1", server_name="a") assert manager.expand_permission_list(["uuid-1", "a"]) == ["uuid-1"] @@ -3934,14 +5582,10 @@ class TestMCPServerManagerExpandPermissionList: the cross-region portability the customer is asking for. """ usw1 = MCPServerManager() - usw1.config_mcp_servers["hash-usw1"] = self._make_server( - "hash-usw1", server_name="a" - ) + usw1.config_mcp_servers["hash-usw1"] = self._make_server("hash-usw1", server_name="a") usc1 = MCPServerManager() - usc1.config_mcp_servers["hash-usc1"] = self._make_server( - "hash-usc1", server_name="a" - ) + usc1.config_mcp_servers["hash-usc1"] = self._make_server("hash-usc1", server_name="a") assert usw1.expand_permission_list(["a"]) == ["hash-usw1"] assert usc1.expand_permission_list(["a"]) == ["hash-usc1"] @@ -3970,18 +5614,14 @@ class TestMCPServerManagerExpandToolPermissions: concrete server_id, otherwise `.get(server_id)` misses and the tool restriction is silently dropped (caller treats None as allow-all).""" manager = MCPServerManager() - manager.config_mcp_servers["uuid-a"] = self._make_server( - "uuid-a", server_name="my-alias" - ) + manager.config_mcp_servers["uuid-a"] = self._make_server("uuid-a", server_name="my-alias") result = manager.expand_tool_permissions({"my-alias": ["read_file"]}) assert result == {"uuid-a": ["read_file"]} def test_passes_through_existing_server_id_key(self): manager = MCPServerManager() - manager.config_mcp_servers["uuid-a"] = self._make_server( - "uuid-a", server_name="alpha" - ) + manager.config_mcp_servers["uuid-a"] = self._make_server("uuid-a", server_name="alpha") result = manager.expand_tool_permissions({"uuid-a": ["read_file"]}) assert result == {"uuid-a": ["read_file"]} @@ -4000,9 +5640,7 @@ class TestMCPServerManagerExpandToolPermissions: """Two servers sharing a server_name both match; their tool lists get the restriction (matches the list-expansion collision semantics).""" manager = MCPServerManager() - manager.config_mcp_servers["uuid-1"] = self._make_server( - "uuid-1", server_name="shared" - ) + manager.config_mcp_servers["uuid-1"] = self._make_server("uuid-1", server_name="shared") manager.registry["uuid-2"] = self._make_server("uuid-2", server_name="shared") result = manager.expand_tool_permissions({"shared": ["read_file"]}) @@ -4015,13 +5653,9 @@ class TestMCPServerManagerExpandToolPermissions: both refer to the same server, the tool lists are unioned rather than one overwriting the other.""" manager = MCPServerManager() - manager.config_mcp_servers["uuid-a"] = self._make_server( - "uuid-a", server_name="alias-a" - ) + manager.config_mcp_servers["uuid-a"] = self._make_server("uuid-a", server_name="alias-a") - result = manager.expand_tool_permissions( - {"uuid-a": ["read_file"], "alias-a": ["write_file"]} - ) + result = manager.expand_tool_permissions({"uuid-a": ["read_file"], "alias-a": ["write_file"]}) assert sorted(result["uuid-a"]) == ["read_file", "write_file"] @@ -4048,9 +5682,7 @@ class TestOAuthDiscoverySSRFGuard: if host not in mapping: raise _socket.gaierror(f"unknown host {host}") family = _socket.AF_INET - return [ - (family, _socket.SOCK_STREAM, 0, "", (ip, port)) for ip in mapping[host] - ] + return [(family, _socket.SOCK_STREAM, 0, "", (ip, port)) for ip in mapping[host]] monkeypatch.setattr( "litellm.litellm_core_utils.url_utils.socket.getaddrinfo", @@ -4088,9 +5720,7 @@ class TestOAuthDiscoverySSRFGuard: ], ) @pytest.mark.asyncio - async def test_cross_origin_blocked_when_resolves_to_unsafe_ip( - self, monkeypatch, ip - ): + async def test_cross_origin_blocked_when_resolves_to_unsafe_ip(self, monkeypatch, ip): self._patch_resolves(monkeypatch, {"attacker.example.com": [ip]}) manager = MCPServerManager() @@ -4111,9 +5741,7 @@ class TestOAuthDiscoverySSRFGuard: @pytest.mark.asyncio async def test_cross_origin_allowed_when_resolves_to_public_ip(self, monkeypatch): - self._patch_resolves( - monkeypatch, {"login.microsoftonline.com": ["20.190.151.7"]} - ) + self._patch_resolves(monkeypatch, {"login.microsoftonline.com": ["20.190.151.7"]}) manager = MCPServerManager() mock_response = MagicMock() @@ -4140,10 +5768,7 @@ class TestOAuthDiscoverySSRFGuard: assert scopes == ["mcp.read"] mock_client.get.assert_awaited_once() assert mock_client.get.await_args.kwargs["follow_redirects"] is False - assert ( - mock_client.get.await_args.kwargs["headers"]["Host"] - == "login.microsoftonline.com" - ) + assert mock_client.get.await_args.kwargs["headers"]["Host"] == "login.microsoftonline.com" @pytest.mark.asyncio async def test_cross_origin_blocked_when_unresolvable(self, monkeypatch): @@ -4194,9 +5819,7 @@ class TestOAuthDiscoverySSRFGuard: async def test_dual_resolution_blocked_if_any_ip_unsafe(self, monkeypatch): # If the attacker controls a DNS record returning multiple A records, # one of which is private, async_safe_get rejects before any network call. - self._patch_resolves( - monkeypatch, {"dual-stack.example.com": ["8.8.8.8", "127.0.0.1"]} - ) + self._patch_resolves(monkeypatch, {"dual-stack.example.com": ["8.8.8.8", "127.0.0.1"]}) manager = MCPServerManager() mock_client = MagicMock() @@ -4421,9 +6044,7 @@ class TestApprovalStatusGate: ("approved", True), ], ) - async def test_add_server_respects_approval_status( - self, approval_status, expect_in_registry - ): + async def test_add_server_respects_approval_status(self, approval_status, expect_in_registry): manager = MCPServerManager() server_id = f"sid-{approval_status}" await manager.add_server(self._make_server(server_id, approval_status)) @@ -4434,19 +6055,13 @@ class TestApprovalStatusGate: # The stale registry entry must be evicted so subsequent tool calls # and health probes can't reach it. manager = MCPServerManager() - await manager.add_server( - self._make_server("evict-me", MCPApprovalStatus.active) - ) + await manager.add_server(self._make_server("evict-me", MCPApprovalStatus.active)) assert "evict-me" in manager.registry - await manager.update_server( - self._make_server("evict-me", MCPApprovalStatus.rejected) - ) + await manager.update_server(self._make_server("evict-me", MCPApprovalStatus.rejected)) assert "evict-me" not in manager.registry - async def test_update_server_eviction_clears_openapi_routing_artifacts( - self, tmp_path - ): + async def test_update_server_eviction_clears_openapi_routing_artifacts(self, tmp_path): """Rejecting a server must remove its OpenAPI tools and name mappings.""" from litellm.proxy._experimental.mcp_server.tool_registry import ( global_mcp_tool_registry, @@ -4457,9 +6072,7 @@ class TestApprovalStatusGate: ) manager = MCPServerManager() - await manager.add_server( - self._make_server("evict-openapi", MCPApprovalStatus.active) - ) + await manager.add_server(self._make_server("evict-openapi", MCPApprovalStatus.active)) assert "evict-openapi" in manager.registry server = manager.registry["evict-openapi"] @@ -4479,9 +6092,7 @@ class TestApprovalStatusGate: manager.tool_name_to_mcp_server_name_mapping["demo_tool"] = prefix manager.tool_name_to_mcp_server_name_mapping[prefixed] = prefix - await manager.update_server( - self._make_server("evict-openapi", MCPApprovalStatus.rejected) - ) + await manager.update_server(self._make_server("evict-openapi", MCPApprovalStatus.rejected)) assert "evict-openapi" not in manager.registry assert prefixed not in global_mcp_tool_registry.tools @@ -4494,9 +6105,7 @@ class TestApprovalStatusGate: # so a future refactor can't accidentally route the pending row to # build_mcp_server_from_table. manager = MCPServerManager() - await manager.update_server( - self._make_server("never-seen", MCPApprovalStatus.pending_review) - ) + await manager.update_server(self._make_server("never-seen", MCPApprovalStatus.pending_review)) assert "never-seen" not in manager.registry @@ -4710,9 +6319,7 @@ class TestGetPublicMCPServers: @patch("litellm.public_mcp_servers", []) def test_returns_empty_when_whitelist_is_empty(self): """Explicit empty whitelist → hub returns nothing.""" - manager = self._make_manager( - [self._make_server("a", available_on_public_internet=True)] - ) + manager = self._make_manager([self._make_server("a", available_on_public_internet=True)]) assert manager.get_public_mcp_servers() == [] @patch("litellm.public_mcp_servers", ["a"]) @@ -4750,9 +6357,7 @@ class TestGetPublicMCPServers: @patch("litellm.public_mcp_servers", ["does-not-exist"]) def test_stale_whitelist_id_returns_empty(self): """Whitelist references an unknown server_id → no spurious results.""" - manager = self._make_manager( - [self._make_server("a", available_on_public_internet=True)] - ) + manager = self._make_manager([self._make_server("a", available_on_public_internet=True)]) assert manager.get_public_mcp_servers() == [] @@ -4831,9 +6436,7 @@ class TestCreateMcpClientV2Graft: NoOpAuth, ) - client = await MCPServerManager()._create_mcp_client( - self._http_server(auth_type=None) - ) + client = await MCPServerManager()._create_mcp_client(self._http_server(auth_type=None)) assert isinstance(client._resolved_auth, NoOpAuth) assert client._mcp_auth_value is None @@ -4847,9 +6450,7 @@ class TestCreateMcpClientV2Graft: (MCPAuth.authorization, "raw-123", "Authorization", "raw-123"), ], ) - async def test_static_family_emits_expected_header( - self, auth_type, token, expected_name, expected_value - ): + async def test_static_family_emits_expected_header(self, auth_type, token, expected_name, expected_value): from litellm.proxy._experimental.mcp_server.outbound_credentials.httpx_auth import ( StaticHeaderAuth, ) @@ -4877,9 +6478,7 @@ class TestCreateMcpClientV2Graft: encoded = base64.b64encode(b"user:pass").decode() assert isinstance(client._resolved_auth, StaticHeaderAuth) assert client._resolved_auth.header_name == "Authorization" - assert ( - client._resolved_auth._header_value.get_secret_value() == f"Basic {encoded}" - ) + assert client._resolved_auth._header_value.get_secret_value() == f"Basic {encoded}" async def test_m2m_client_credentials_defers_to_v1(self): # M2M (oauth2 client_credentials) is not migrated: to_server_spec returns @@ -4944,9 +6543,7 @@ class TestCreateMcpClientV2Graft: # A per-request override (mcp_auth_header) must win over the shared static token, # exactly as v1 did, so a migrated static server defers to v1 when one is present. client = await MCPServerManager()._create_mcp_client( - self._http_server( - auth_type=MCPAuth.bearer_token, authentication_token="shared-tok" - ), + self._http_server(auth_type=MCPAuth.bearer_token, authentication_token="shared-tok"), mcp_auth_header="caller-override", ) @@ -4958,9 +6555,7 @@ class TestCreateMcpClientV2Graft: # signer, static_headers, or a forwarded caller header) must win. The server stays on # the v2 path but skips resolved_auth, so nothing overwrites the inbound header. client = await MCPServerManager()._create_mcp_client( - self._http_server( - auth_type=MCPAuth.bearer_token, authentication_token="shared-tok" - ), + self._http_server(auth_type=MCPAuth.bearer_token, authentication_token="shared-tok"), extra_headers={"Authorization": "Bearer hook-jwt"}, ) @@ -4984,5 +6579,759 @@ class TestCreateMcpClientV2Graft: assert client._get_auth_headers()["Authorization"] == "Bearer hook-jwt" +def _upstream_status_error(status_code: int, challenge: str) -> httpx.HTTPStatusError: + request = httpx.Request("POST", "https://upstream.example/mcp") + response = httpx.Response( + status_code, + headers={"WWW-Authenticate": challenge}, + request=request, + ) + return httpx.HTTPStatusError("upstream rejected token", request=request, response=response) + + +class TestMCPToolsListAuthSurfacing: + """Regression: MCP tools/list 401 auth failures must surface as MCPUpstreamAuthError. + + Previously a missing/expired per-user OAuth token, or an upstream 401 for any + non-carveout auth_type, was swallowed to an empty tool list, so a single-server + client saw a 200 with no tools instead of a 401 challenge. The listing helpers + now raise MCPUpstreamAuthError on a 401 regardless of auth_type; the single-server + routes turn it into a 401 + WWW-Authenticate while the aggregator absorbs it to an + empty list. Only a 401 challenges; a 403 (forbidden) degrades like any other error. + """ + + @pytest.mark.asyncio + async def test_fetch_tools_with_timeout_surfaces_upstream_401(self): + from litellm.proxy._experimental.mcp_server.exceptions import ( + MCPUpstreamAuthError, + ) + + manager = MCPServerManager() + challenge = 'Bearer resource_metadata="https://upstream.example/.well-known/oauth-protected-resource"' + client = MagicMock() + client.list_tools = AsyncMock(side_effect=_upstream_status_error(401, challenge)) + + with pytest.raises(MCPUpstreamAuthError) as exc_info: + await manager._fetch_tools_with_timeout(client, "static-key-server") + + assert exc_info.value.status_code == 401 + assert exc_info.value.www_authenticate == challenge + assert exc_info.value.server_name == "static-key-server" + + @pytest.mark.asyncio + async def test_fetch_tools_with_timeout_absorbs_upstream_403(self): + """Only a 401 drives the re-auth challenge. A 403 (authenticated but + forbidden, e.g. insufficient scope) is not a re-auth signal, so even + with a WWW-Authenticate header it degrades to an empty list rather than + surfacing a challenge.""" + manager = MCPServerManager() + challenge = 'Bearer error="insufficient_scope", scope="read:tools"' + client = MagicMock() + client.list_tools = AsyncMock(side_effect=_upstream_status_error(403, challenge)) + + assert await manager._fetch_tools_with_timeout(client, "forbidden-server") == [] + + @pytest.mark.asyncio + async def test_fetch_tools_with_timeout_returns_empty_on_non_auth_error(self): + manager = MCPServerManager() + client = MagicMock() + client.list_tools = AsyncMock(side_effect=RuntimeError("upstream 500")) + + assert await manager._fetch_tools_with_timeout(client, "srv") == [] + + @pytest.mark.asyncio + async def test_get_tools_from_server_surfaces_unusable_user_token(self): + from litellm.proxy._experimental.mcp_server.exceptions import ( + MCPUpstreamAuthError, + ) + + manager = MCPServerManager() + server = MCPServer(server_id="oauth-srv", name="oauth-srv", transport=MCPTransport.http) + challenge = 'Bearer resource_metadata="/.well-known/oauth-protected-resource/mcp/oauth-srv"' + manager._create_mcp_client = AsyncMock( + side_effect=HTTPException( + status_code=401, + detail="Unauthorized", + headers={"WWW-Authenticate": challenge}, + ) + ) + + with pytest.raises(MCPUpstreamAuthError) as exc_info: + await manager._get_tools_from_server(server) + + assert exc_info.value.status_code == 401 + assert exc_info.value.www_authenticate == challenge + assert exc_info.value.server_name == "oauth-srv" + + @pytest.mark.asyncio + async def test_get_tools_from_server_absorbs_non_challenge_http_error(self): + """A non-auth HTTPException (500) stays absorbed so one misconfigured server cannot blank + the listing; 401/403 are the challenge-class statuses routed to MCPUpstreamAuthError.""" + manager = MCPServerManager() + server = MCPServer(server_id="stdio-srv", name="stdio-srv", transport=MCPTransport.http) + manager._create_mcp_client = AsyncMock( + side_effect=HTTPException( + status_code=500, + detail="MCP stdio command 'foo' is not in the allowlist", + ) + ) + + assert await manager._get_tools_from_server(server) == [] + + @pytest.mark.asyncio + async def test_get_tools_from_server_suppresses_upstream_challenge_for_dcr_bridge(self): + """A dcr_bridge server must never relay the upstream's own WWW-Authenticate: it points + clients at the upstream protected-resource metadata, which fails the RFC 9728 resource + match against the gateway URL they dialed. Stripping it makes the single-server route + fabricate the gateway well-known challenge, whose content is the bridge facade.""" + from litellm.proxy._experimental.mcp_server.exceptions import ( + MCPUpstreamAuthError, + ) + from litellm.types.mcp import MCPAuth + + manager = MCPServerManager() + bridge_server = MCPServer( + server_id="bridge-srv", + name="bridge-srv", + transport=MCPTransport.http, + auth_type=MCPAuth.true_passthrough, + dcr_bridge=True, + ) + upstream_challenge = 'Bearer resource_metadata="https://upstream.example/.well-known/oauth-protected-resource"' + client = MagicMock() + client.list_tools = AsyncMock(side_effect=_upstream_status_error(401, upstream_challenge)) + manager._create_mcp_client = AsyncMock(return_value=client) + + with pytest.raises(MCPUpstreamAuthError) as exc_info: + await manager._get_tools_from_server(bridge_server) + + assert exc_info.value.status_code == 401 + assert exc_info.value.www_authenticate is None + assert exc_info.value.server_name == "bridge-srv" + + @pytest.mark.asyncio + async def test_get_tools_from_server_suppresses_resolver_challenge_for_dcr_bridge(self): + """The client-build-time HTTPException conversion path applies the same suppression.""" + from litellm.proxy._experimental.mcp_server.exceptions import ( + MCPUpstreamAuthError, + ) + from litellm.types.mcp import MCPAuth + + manager = MCPServerManager() + bridge_server = MCPServer( + server_id="bridge-srv", + name="bridge-srv", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth_delegate, + dcr_bridge=True, + ) + manager._create_mcp_client = AsyncMock( + side_effect=HTTPException( + status_code=401, + detail="Unauthorized", + headers={"WWW-Authenticate": 'Bearer resource_metadata="https://upstream.example/prm"'}, + ) + ) + + with pytest.raises(MCPUpstreamAuthError) as exc_info: + await manager._get_tools_from_server(bridge_server) + + assert exc_info.value.status_code == 401 + assert exc_info.value.www_authenticate is None + + @pytest.mark.asyncio + async def test_aggregate_list_tools_absorbs_unauthenticated_server(self): + from litellm.proxy._experimental.mcp_server.exceptions import ( + MCPUpstreamAuthError, + ) + + manager = MCPServerManager() + good = MCPServer(server_id="good", name="good", transport=MCPTransport.http) + bad = MCPServer(server_id="bad", name="bad", transport=MCPTransport.http) + manager.get_allowed_mcp_servers = AsyncMock(return_value=["good", "bad"]) + manager.get_mcp_server_by_id = MagicMock( + side_effect=lambda server_id: {"good": good, "bad": bad}.get(server_id) + ) + good_tool = MCPTool(name="good-do_thing", description="do thing", inputSchema={}) + + async def fake_get_tools(server, **kwargs): + if server.server_id == "bad": + raise MCPUpstreamAuthError( + status_code=401, + www_authenticate='Bearer realm="x"', + server_name="bad", + ) + return [good_tool] + + manager._get_tools_from_server = fake_get_tools + + result = await manager.list_tools() + + assert [t.name for t in result] == ["good-do_thing"] + + +def test_should_strip_caller_authorization_for_token_exchange(): + """OBO: the inbound bearer is the subject token (exchanged), never forwarded upstream raw.""" + server = MCPServer( + server_id="te-strip", + name="te-strip-server", + url="https://up.example.com", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + assert _should_strip_caller_authorization(mcp_server=server, raw_headers=None, user_api_key_auth=None) is True + + +class _UpstreamAuthError(Exception): + """Mimics a wrapped upstream 401 the way _extract_upstream_auth_failure detects it.""" + + def __init__(self, status_code: int = 401) -> None: + super().__init__(f"HTTP {status_code}") + self.response = httpx.Response(status_code) + + +class _RetryFakeClient: + """A fake MCPClient whose call_tool fails on the first attempt and (optionally) succeeds after.""" + + def __init__(self, *, raises=None, result=None) -> None: + from litellm.experimental_mcp_client.client import MCPClient + + self._raises = raises + self._result = result + self._MCPClient = MCPClient + self.attempts = 0 + + async def call_tool(self, params, host_progress_callback=None, raise_on_error=False): + self.attempts += 1 + if self._raises is not None: + if raise_on_error: + raise self._raises + return self._MCPClient.error_tool_result(self._raises) + return self._result + + +def _obo_server() -> MCPServer: + return MCPServer( + server_id="obo-srv", + name="obo", + url="https://upstream.example/mcp", + transport=MCPTransport.sse, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + + +class TestOBOCallToolRetry: + """The token_exchange (OBO) tool-call path re-mints the exchanged token once on an upstream 401.""" + + def _manager(self): + manager = MCPServerManager() + manager._cred_provider = MagicMock() + manager._cred_provider.invalidate_credentials = AsyncMock() + return manager + + @pytest.mark.asyncio + async def test_upstream_401_invalidates_and_retries_once(self): + manager = self._manager() + success = CallToolResult(content=[], isError=False) + first = _RetryFakeClient(raises=_UpstreamAuthError(401)) + retry = _RetryFakeClient(result=success) + manager._create_mcp_client = AsyncMock(return_value=retry) + + result = await manager._obo_call_tool_with_retry( + client=first, + call_tool_params=MagicMock(), + host_progress_callback=None, + mcp_server=_obo_server(), + server_auth_header=None, + extra_headers=None, + stdio_env=None, + subject_token="caller-jwt", + user_api_key_auth=None, + ) + + assert result is success + manager._cred_provider.invalidate_credentials.assert_awaited_once() + manager._create_mcp_client.assert_awaited_once() + assert first.attempts == 1 and retry.attempts == 1 + + @pytest.mark.asyncio + async def test_non_auth_error_does_not_retry(self): + manager = self._manager() + first = _RetryFakeClient(raises=ValueError("tool blew up")) + manager._create_mcp_client = AsyncMock() + + result = await manager._obo_call_tool_with_retry( + client=first, + call_tool_params=MagicMock(), + host_progress_callback=None, + mcp_server=_obo_server(), + server_auth_header=None, + extra_headers=None, + stdio_env=None, + subject_token="caller-jwt", + user_api_key_auth=None, + ) + + assert result.isError is True + manager._cred_provider.invalidate_credentials.assert_not_awaited() + manager._create_mcp_client.assert_not_awaited() + assert first.attempts == 1 + + @pytest.mark.asyncio + async def test_second_401_degrades_without_looping(self): + manager = self._manager() + first = _RetryFakeClient(raises=_UpstreamAuthError(401)) + # The retry client still fails; with raise_on_error defaulting False it returns isError. + retry = _RetryFakeClient(raises=_UpstreamAuthError(401)) + manager._create_mcp_client = AsyncMock(return_value=retry) + + result = await manager._obo_call_tool_with_retry( + client=first, + call_tool_params=MagicMock(), + host_progress_callback=None, + mcp_server=_obo_server(), + server_auth_header=None, + extra_headers=None, + stdio_env=None, + subject_token="caller-jwt", + user_api_key_auth=None, + ) + + assert result.isError is True + manager._create_mcp_client.assert_awaited_once() + assert first.attempts == 1 and retry.attempts == 1 + + +class TestOBOConcurrencyLimit: + """OBO (token_exchange) tool calls must honor the server's max_concurrent_requests. + + Regression: the token_exchange dispatch built its coroutine outside + _limit_outbound_concurrency, so OBO calls skipped the per-server semaphore the + non-OBO path enforces and a caller could exceed the admin-configured cap. + """ + + @pytest.mark.asyncio + async def test_obo_dispatch_respects_max_concurrent_requests(self): + max_concurrent = 2 + overflow = 3 + server = MCPServer( + server_id="obo-concurrency", + name="obo", + url="https://upstream.example/mcp", + transport=MCPTransport.sse, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + max_concurrent_requests=max_concurrent, + ) + + release = asyncio.Event() + inflight = {"current": 0, "peak": 0} + + class _ConcurrencyRecordingClient: + async def call_tool(self, params, host_progress_callback=None, raise_on_error=False): + inflight["current"] += 1 + inflight["peak"] = max(inflight["peak"], inflight["current"]) + try: + await release.wait() + finally: + inflight["current"] -= 1 + return CallToolResult(content=[], isError=False) + + manager = MCPServerManager() + manager._create_mcp_client = AsyncMock(return_value=_ConcurrencyRecordingClient()) + + async def _dispatch(): + return await manager._call_regular_mcp_tool( + mcp_server=server, + original_tool_name="do_thing", + arguments={}, + tasks=[], + mcp_auth_header=None, + mcp_server_auth_headers=None, + oauth2_headers={"Authorization": "Bearer subject-jwt"}, + raw_headers=None, + proxy_logging_obj=None, + ) + + callers = [asyncio.create_task(_dispatch()) for _ in range(max_concurrent + overflow)] + + stable = 0 + previous = -1 + for _ in range(1000): + await asyncio.sleep(0) + current = inflight["current"] + if current == previous: + stable += 1 + if current > 0 and stable >= 10: + break + else: + stable = 0 + previous = current + + peak_while_blocked = inflight["peak"] + release.set() + results = await asyncio.gather(*callers) + + assert peak_while_blocked == max_concurrent + assert inflight["current"] == 0 + assert all(result.isError is False for result in results) + + +class TestOBOEndpointDiscovery: + """An oauth2_token_exchange server with no configured token endpoint discovers it (RFC 9728 -> + RFC 8414) like the oauth2 flow does; an explicitly configured endpoint skips discovery.""" + + @pytest.mark.parametrize( + "auth_type, endpoint, token_url, expected", + [ + (MCPAuth.oauth2_token_exchange, None, None, True), # OBO, nothing configured -> discover + (MCPAuth.oauth2_token_exchange, "https://idp/token", None, False), # endpoint set -> skip + (MCPAuth.oauth2_token_exchange, None, "https://idp/token", False), # token_url set -> skip + (MCPAuth.oauth2, None, None, False), # not OBO + (MCPAuth.none, None, None, False), + (None, None, None, False), + ], + ) + def test_decision(self, auth_type, endpoint, token_url, expected): + assert MCPServerManager._obo_needs_endpoint_discovery(auth_type, endpoint, token_url) is expected + + @pytest.mark.asyncio + async def test_config_obo_without_endpoint_discovers_token_endpoint(self): + manager = MCPServerManager() + discovered = MCPOAuthMetadata( + scopes=None, + authorization_url=None, + token_url="https://discovered.example.com/token", + registration_url=None, + ) + seen = [] + + async def fake_discovery(server_url: str, *, allow_origin_fallback: bool = True): + seen.append((server_url, allow_origin_fallback)) + return discovered + + manager._descovery_metadata = fake_discovery # type: ignore[attr-defined] + + await manager.load_servers_from_config( + { + "obo": { + "url": "https://example.com/mcp", + "transport": MCPTransport.http, + "auth_type": MCPAuth.oauth2_token_exchange, + "client_id": "cid", + "client_secret": "csec", + } + } + ) + + server = next(iter(manager.config_mcp_servers.values())) + # OBO discovery runs, and never guesses the resource origin as the IdP (origin fallback off). + assert seen == [("https://example.com/mcp", False)] + # The discovered token endpoint lands on token_url, which _token_exchange_spec reads. + assert server.token_url == "https://discovered.example.com/token" + + @pytest.mark.asyncio + async def test_config_obo_with_configured_endpoint_skips_discovery(self): + manager = MCPServerManager() + + async def fake_discovery(server_url: str, *, allow_origin_fallback: bool = True): + raise AssertionError("discovery must not run when the endpoint is configured") + + manager._descovery_metadata = fake_discovery # type: ignore[attr-defined] + + await manager.load_servers_from_config( + { + "obo": { + "url": "https://example.com/mcp", + "transport": MCPTransport.http, + "auth_type": MCPAuth.oauth2_token_exchange, + "token_exchange_endpoint": "https://configured.example.com/token", + "client_id": "cid", + "client_secret": "csec", + } + } + ) + + server = next(iter(manager.config_mcp_servers.values())) + assert server.token_exchange_endpoint == "https://configured.example.com/token" + + @pytest.mark.asyncio + async def test_descovery_metadata_does_not_guess_origin_when_disallowed(self): + # The authoritative RFC 9728 -> RFC 8414 chain stays, but with allow_origin_fallback=False the + # resource origin is never assumed to be the IdP, so no token endpoint is invented. + manager = MCPServerManager() + server_url = "https://example.com/public/mcp" + request = httpx.Request("GET", server_url) + response_obj = httpx.Response( + status_code=401, request=request, headers={"WWW-Authenticate": 'Bearer scope="read"'} + ) + response_obj.raise_for_status = MagicMock( + side_effect=lambda: (_ for _ in ()).throw( + httpx.HTTPStatusError("unauthorized", request=request, response=response_obj) + ) + ) + mock_client = MagicMock() + mock_client.get = AsyncMock(return_value=response_obj) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.get_async_httpx_client", + return_value=mock_client, + ), + patch.object(manager, "_fetch_oauth_metadata_from_resource", AsyncMock(return_value=([], None))), + patch.object(manager, "_attempt_well_known_discovery", AsyncMock(return_value=([], None))), + patch.object(manager, "_fetch_authorization_server_metadata", AsyncMock()) as mock_fetch_auth, + ): + result = await manager._descovery_metadata(server_url, allow_origin_fallback=False) + + # No advertised AS -> with the guess disabled, the AS-metadata fetch is never attempted, so no + # token endpoint is discovered (only the scopes parsed from the challenge survive). + mock_fetch_auth.assert_not_awaited() + assert result is None or result.token_url is None + + if __name__ == "__main__": pytest.main([__file__]) + + +@pytest.mark.asyncio +async def test_preflight_challenge_carries_step_up_error_and_claims(): + """An Entra Conditional Access rejection must surface error=insufficient_claims plus the base64 + claims in the single-server 401 challenge, so an MSAL-family client can drive the step-up and retry.""" + import base64 + + from litellm.proxy._experimental.mcp_server.outbound_credentials.result import Error + from litellm.proxy._experimental.mcp_server.outbound_credentials.types import CredError + + claims = '{"access_token":{"acrs":{"essential":true,"value":"c1"}}}' + + class _FakeProvider: + async def resolve_credentials(self, subject, server): + return Error(CredError.of_unauthorized("step-up required", claims=claims)) + + manager = MCPServerManager(cred_provider=_FakeProvider()) + server = MCPServer( + server_id="te-ca", + name="te-ca-server", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2_token_exchange, + token_exchange_endpoint="https://idp.example.com/token", + client_id="cid", + client_secret="csec", + ) + with pytest.raises(HTTPException) as exc_info: + await manager.preflight_token_exchange( + server=server, + oauth2_headers={"Authorization": "Bearer subj"}, + user_api_key_auth=None, + ) + assert exc_info.value.status_code == 401 + headers = exc_info.value.headers or {} + www = headers.get("WWW-Authenticate") or headers.get("www-authenticate") or "" + assert 'error="insufficient_claims"' in www + assert base64.b64encode(claims.encode()).decode() in www + assert "resource_metadata" in www + + +@pytest.mark.asyncio +async def test_aggregate_list_still_absorbs_step_up_challenged_server(): + """A step-up (claims-bearing) 401 from one server must not change the aggregate contract: the + multi-server listing still absorbs it and returns the healthy servers' tools.""" + from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError + + manager = MCPServerManager() + good = MCPServer(server_id="good", name="good", transport=MCPTransport.http) + ca = MCPServer(server_id="ca", name="ca", transport=MCPTransport.http) + manager.get_allowed_mcp_servers = AsyncMock(return_value=["good", "ca"]) + manager.get_mcp_server_by_id = MagicMock(side_effect=lambda server_id: {"good": good, "ca": ca}.get(server_id)) + good_tool = MCPTool(name="good-do_thing", description="do thing", inputSchema={}) + + async def fake_get_tools(server, **kwargs): + if server.server_id == "ca": + raise MCPUpstreamAuthError( + status_code=401, + www_authenticate=('Bearer resource_metadata="/x", error="insufficient_claims", claims="eyJhIjoxfQ=="'), + server_name="ca", + ) + return [good_tool] + + manager._get_tools_from_server = fake_get_tools + + result = await manager.list_tools() + + assert [t.name for t in result] == ["good-do_thing"] + + +class TestDbBuildReadsOauth2FlowColumnVerbatim: + """The DB build must not re-infer the flow from field shape: rows are stamped at + write time and by the startup backfill, and a DCR-registered interactive server + has the exact M2M shape (client creds + token_url, no persisted authorization_url) + whenever discovery is unavailable. Inference survives only for config-loaded + servers and the request-time backstop in _get_allowed_mcp_servers.""" + + def _row(self, oauth2_flow): + return LiteLLM_MCPServerTable( + server_id="flow-column-row", + alias="flow_column_row", + description="", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + oauth2_flow=oauth2_flow, + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csec"}, + created_at=datetime.now(), + updated_at=datetime.now(), + ) + + @pytest.mark.asyncio + async def test_null_flow_m2m_shape_row_is_not_inferred_m2m(self): + manager = MCPServerManager() + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)): + built = await manager.build_mcp_server_from_table(self._row(None), credentials_are_encrypted=False) + + assert built.oauth2_flow is None + assert built.has_client_credentials is False + assert built.needs_user_oauth_token is True + + @pytest.mark.asyncio + async def test_explicit_flow_column_is_read_verbatim(self): + manager = MCPServerManager() + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)): + built = await manager.build_mcp_server_from_table( + self._row("client_credentials"), credentials_are_encrypted=False + ) + + assert built.oauth2_flow == "client_credentials" + assert built.has_client_credentials is True + assert built.needs_user_oauth_token is False + + @pytest.mark.asyncio + async def test_authorization_code_flow_column_is_read_verbatim(self): + manager = MCPServerManager() + with patch.object(manager, "_descovery_metadata", new=AsyncMock(return_value=None)): + built = await manager.build_mcp_server_from_table( + self._row("authorization_code"), credentials_are_encrypted=False + ) + + assert built.oauth2_flow == "authorization_code" + assert built.has_client_credentials is False + assert built.needs_user_oauth_token is True + + +class TestRequestTimeOauth2FlowBackstop: + """The single request-time resolution helpers every security site shares: + effective_oauth2_flow (the enum/boolean decision) and + resolve_oauth2_flow_for_request (the egress object copy).""" + + def _oauth2_server(self, **overrides): + base = dict( + server_id="flow-server", + name="flow_server", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + ) + base.update(overrides) + return MCPServer(**base) + + def test_effective_flow_stamped_values_returned_verbatim(self): + assert ( + MCPServerManager.effective_oauth2_flow(self._oauth2_server(oauth2_flow="client_credentials")) + == "client_credentials" + ) + assert ( + MCPServerManager.effective_oauth2_flow(self._oauth2_server(oauth2_flow="authorization_code")) + == "authorization_code" + ) + + def test_effective_flow_null_m2m_shape_resolves_client_credentials(self): + server = self._oauth2_server( + oauth2_flow=None, + client_id="cid", + client_secret="csecret", + token_url="https://idp.example.com/token", + ) + assert MCPServerManager.effective_oauth2_flow(server) == "client_credentials" + + def test_effective_flow_null_pure_pkce_resolves_none(self): + assert MCPServerManager.effective_oauth2_flow(self._oauth2_server(oauth2_flow=None)) is None + + def test_resolve_for_request_stamped_row_is_unchanged_identity(self): + server = self._oauth2_server(oauth2_flow="client_credentials") + assert MCPServerManager.resolve_oauth2_flow_for_request(server) is server + + def test_resolve_for_request_null_pure_pkce_is_unchanged_identity(self): + server = self._oauth2_server(oauth2_flow=None) + assert MCPServerManager.resolve_oauth2_flow_for_request(server) is server + + def test_resolve_for_request_null_m2m_shape_copies_client_credentials(self, caplog): + import logging + + server = self._oauth2_server( + oauth2_flow=None, + client_id="cid", + client_secret="csecret", + token_url="https://idp.example.com/token", + ) + with caplog.at_level(logging.WARNING, logger="LiteLLM"): + resolved = MCPServerManager.resolve_oauth2_flow_for_request(server) + + assert resolved is not server + assert resolved.oauth2_flow == "client_credentials" + assert server.oauth2_flow is None # original untouched + # Finding 2: the warning must NOT promise the backfill will stamp this row. + joined = " ".join(caplog.messages) + assert "no persisted oauth2_flow" in joined + assert "next proxy boot" not in joined + assert "will NOT self-heal" in joined + + +def test_build_mcp_server_table_carries_oauth2_flow(): + """GET /v1/mcp/server (list and by-id) serves registry servers through this + conversion; dropping oauth2_flow here blinds the dashboard to the persisted + flow, so the edit page cannot prefill and M2M gating never activates.""" + manager = MCPServerManager() + server = MCPServer( + server_id="flow-table-server", + name="flow_table_server", + server_name="flow_table_server", + alias="flow_table_server", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + oauth2_flow="client_credentials", + ) + + table = manager._build_mcp_server_table(server) + + assert table.oauth2_flow == "client_credentials" + + +def test_build_mcp_server_table_carries_null_oauth2_flow(): + """A legacy row the backfill left unstamped must surface as oauth2_flow=None in + the GET response, so the dashboard maps it to undefined and prompts the admin to + choose a flow rather than showing a guessed default.""" + manager = MCPServerManager() + server = MCPServer( + server_id="null-flow-server", + name="null_flow_server", + server_name="null_flow_server", + alias="null_flow_server", + url="https://up.example.com/mcp", + transport=MCPTransport.http, + auth_type=MCPAuth.oauth2, + oauth2_flow=None, + ) + + table = manager._build_mcp_server_table(server) + + assert table.oauth2_flow is None diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py index c2164a9f19f..5992fd1814f 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_sigv4_auth.py @@ -399,9 +399,7 @@ class TestMCPServerManagerSigV4: server = next(iter(manager.config_mcp_servers.values())) assert server.auth_type == MCPAuth.aws_sigv4 assert server.aws_access_key_id == "AKIAIOSFODNN7EXAMPLE" - assert ( - server.aws_secret_access_key == "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - ) + assert server.aws_secret_access_key == "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" assert server.aws_region_name == "us-east-1" assert server.aws_service_name == "bedrock-agentcore" @@ -531,9 +529,7 @@ class TestMCPServerManagerSigV4: "aws_session_name": "my-session", } - result = manager._extract_aws_credentials( - creds, credentials_are_encrypted=False - ) + result = manager._extract_aws_credentials(creds, credentials_are_encrypted=False) assert result["aws_role_name"] == "arn:aws:iam::123456789012:role/TestRole" assert result["aws_session_name"] == "my-session" @@ -561,10 +557,7 @@ class TestSigV4CredentialEncryption: # Secrets should be encrypted assert result["aws_access_key_id"] == "enc:AKIAIOSFODNN7EXAMPLE" - assert ( - result["aws_secret_access_key"] - == "enc:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - ) + assert result["aws_secret_access_key"] == "enc:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" assert result["aws_session_token"] == "enc:FwoGZX..." # Non-secrets should be unchanged assert result["aws_region_name"] == "us-east-1" @@ -606,12 +599,8 @@ class TestCredentialMergeOnUpdate: ) mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock( - return_value=existing_record - ) - mock_prisma.db.litellm_mcpservertable.update = AsyncMock( - return_value=MagicMock() - ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing_record) + mock_prisma.db.litellm_mcpservertable.update = AsyncMock(return_value=MagicMock()) data = UpdateMCPServerRequest( server_id="test-server", @@ -650,9 +639,7 @@ class TestCredentialMergeOnUpdate: from litellm.proxy._types import UpdateMCPServerRequest mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.update = AsyncMock( - return_value=MagicMock() - ) + mock_prisma.db.litellm_mcpservertable.update = AsyncMock(return_value=MagicMock()) data = UpdateMCPServerRequest( server_id="test-server", @@ -679,12 +666,8 @@ class TestCredentialMergeOnUpdate: existing_record.credentials = None mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock( - return_value=existing_record - ) - mock_prisma.db.litellm_mcpservertable.update = AsyncMock( - return_value=MagicMock() - ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing_record) + mock_prisma.db.litellm_mcpservertable.update = AsyncMock(return_value=MagicMock()) data = UpdateMCPServerRequest( server_id="test-server", @@ -725,12 +708,8 @@ class TestCredentialMergeOnUpdate: ) mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock( - return_value=existing_record - ) - mock_prisma.db.litellm_mcpservertable.update = AsyncMock( - return_value=MagicMock() - ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing_record) + mock_prisma.db.litellm_mcpservertable.update = AsyncMock(return_value=MagicMock()) data = UpdateMCPServerRequest( server_id="test-server", @@ -772,12 +751,8 @@ class TestCredentialMergeOnUpdate: ) mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock( - return_value=existing_record - ) - mock_prisma.db.litellm_mcpservertable.update = AsyncMock( - return_value=MagicMock() - ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing_record) + mock_prisma.db.litellm_mcpservertable.update = AsyncMock(return_value=MagicMock()) data = UpdateMCPServerRequest( server_id="test-server", @@ -819,9 +794,7 @@ class TestSigV4BuildFromTable: table_record.server_name = "sigv4_server" table_record.alias = None table_record.description = None - table_record.url = ( - "https://bedrock-agentcore.us-east-1.amazonaws.com/invocations" - ) + table_record.url = "https://bedrock-agentcore.us-east-1.amazonaws.com/invocations" table_record.spec_path = None table_record.transport = "http" table_record.auth_type = "aws_sigv4" @@ -856,6 +829,10 @@ class TestSigV4BuildFromTable: table_record.tool_name_to_description = None table_record.byok_api_key_help_url = None table_record.oauth2_flow = None + table_record.token_exchange_endpoint = None + table_record.audience = None + table_record.subject_token_type = None + table_record.token_exchange_profile = None table_record.instructions = None table_record.source_url = None @@ -863,9 +840,7 @@ class TestSigV4BuildFromTable: with patch( "litellm.proxy._experimental.mcp_server.mcp_server_manager.decrypt_value_helper", - side_effect=lambda value, key, exception_type, return_original_value: value.replace( - "enc:", "" - ), + side_effect=lambda value, key, exception_type, return_original_value: value.replace("enc:", ""), ): server = await manager.build_mcp_server_from_table(table_record) @@ -915,6 +890,10 @@ class TestSigV4BuildFromTable: table_record.tool_name_to_description = None table_record.byok_api_key_help_url = None table_record.oauth2_flow = None + table_record.token_exchange_endpoint = None + table_record.audience = None + table_record.subject_token_type = None + table_record.token_exchange_profile = None table_record.instructions = None table_record.source_url = None @@ -922,9 +901,7 @@ class TestSigV4BuildFromTable: with patch( "litellm.proxy._experimental.mcp_server.mcp_server_manager.decrypt_value_helper", - side_effect=lambda value, key, exception_type, return_original_value: value.replace( - "enc:", "" - ), + side_effect=lambda value, key, exception_type, return_original_value: value.replace("enc:", ""), ): server = await manager.build_mcp_server_from_table(table_record) @@ -1010,9 +987,7 @@ class TestRotateCredentials: server.env_vars = None mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=[server] - ) + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[server]) mock_prisma.db.litellm_mcpservertable.update = AsyncMock() with ( @@ -1031,9 +1006,7 @@ class TestRotateCredentials: side_effect=lambda value, new_encryption_key: f"enc_new:{value}", ), ): - await rotate_mcp_server_credentials_master_key( - mock_prisma, "admin", "new-key" - ) + await rotate_mcp_server_credentials_master_key(mock_prisma, "admin", "new-key") update_call = mock_prisma.db.litellm_mcpservertable.update assert update_call.called @@ -1061,9 +1034,7 @@ class TestRotateCredentials: ] mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=[server] - ) + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=[server]) mock_prisma.db.litellm_mcpservertable.update = AsyncMock() with ( @@ -1082,9 +1053,7 @@ class TestRotateCredentials: side_effect=lambda value, new_encryption_key: f"enc_new:{value}", ), ): - await rotate_mcp_server_credentials_master_key( - mock_prisma, "admin", "new-key" - ) + await rotate_mcp_server_credentials_master_key(mock_prisma, "admin", "new-key") update_call = mock_prisma.db.litellm_mcpservertable.update assert update_call.called @@ -1108,17 +1077,11 @@ class TestAuthTypeSwitchClearsCredentials: existing_record = MagicMock() existing_record.auth_type = "oauth2" - existing_record.credentials = json.dumps( - {"client_id": "enc:cid", "client_secret": "enc:csec"} - ) + existing_record.credentials = json.dumps({"client_id": "enc:cid", "client_secret": "enc:csec"}) mock_prisma = MagicMock() - mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock( - return_value=existing_record - ) - mock_prisma.db.litellm_mcpservertable.update = AsyncMock( - return_value=MagicMock() - ) + mock_prisma.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing_record) + mock_prisma.db.litellm_mcpservertable.update = AsyncMock(return_value=MagicMock()) data = UpdateMCPServerRequest( server_id="test-server", @@ -1133,8 +1096,12 @@ class TestAuthTypeSwitchClearsCredentials: await update_mcp_server(mock_prisma, data, "test-user") data_dict = mock_prisma.db.litellm_mcpservertable.update.call_args[1]["data"] - # Credentials should be cleared (set to None) - assert data_dict.get("credentials") is None + # Credentials should be cleared. The clear reaches prisma as Json(None) (SQL null), which + # prisma-python requires for a Json? field; a bare None is also accepted for older callers. + from prisma import Json + + cleared = data_dict.get("credentials") + assert cleared is None or (isinstance(cleared, Json) and getattr(cleared, "data", "x") is None) class TestInheritCredentials: diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py index e4e1890a45a..be95b3f3f73 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_stale_session.py @@ -294,15 +294,9 @@ async def test_stale_mcp_session_id_is_stripped(): # Verify the mcp-session-id header was stripped header_names = [k for k, v in captured_scope.get("headers", [])] - assert ( - b"mcp-session-id" not in header_names - ), "Stale mcp-session-id header should have been stripped from the scope" - assert ( - stateless_handle_request.called - ), "Stale non-initialize requests should route stateless" - assert ( - not stateful_handle_request.called - ), "Stale non-initialize requests should not route stateful" + assert b"mcp-session-id" not in header_names, "Stale mcp-session-id header should have been stripped from the scope" + assert stateless_handle_request.called, "Stale non-initialize requests should route stateless" + assert not stateful_handle_request.called, "Stale non-initialize requests should not route stateful" @pytest.mark.asyncio @@ -366,9 +360,7 @@ async def test_delete_stale_mcp_session_returns_success(): await handle_streamable_http_mcp(scope, receive, send) # Verify session manager was NOT called (request was handled early) - assert ( - not mock_handle_request.called - ), "Session manager should not be called for DELETE on non-existent session" + assert not mock_handle_request.called, "Session manager should not be called for DELETE on non-existent session" # Verify a success response was sent assert send.called, "A response should have been sent" @@ -523,9 +515,7 @@ async def test_valid_mcp_session_id_is_preserved(): # Verify the mcp-session-id header was preserved header_names = [k for k, v in captured_scope.get("headers", [])] - assert ( - b"mcp-session-id" in header_names - ), "Valid mcp-session-id header should have been preserved" + assert b"mcp-session-id" in header_names, "Valid mcp-session-id header should have been preserved" @pytest.mark.asyncio @@ -723,6 +713,9 @@ async def test_handle_streamable_http_mcp_delegated_server_surfaces_upstream_cha delegated_server.auth_type = MCPAuth.oauth2 delegated_server.delegate_auth_to_upstream = True delegated_server.needs_user_oauth_token = True + delegated_server.is_oauth_passthrough = False + delegated_server.is_oauth_delegate = False + delegated_server.is_true_passthrough = False delegated_server.server_id = "delegated-oauth-server" upstream_challenge = 'Bearer resource_metadata="https://upstream.example.com/.well-known/oauth-protected-resource"' @@ -967,6 +960,585 @@ async def test_handle_streamable_http_mcp_delegated_server_without_token_returns challenge = exc_info.value.headers["www-authenticate"] assert "resource_metadata=" in challenge assert "authorization_uri=" not in challenge - assert ( - "/.well-known/oauth-protected-resource/delegated_oauth_server/mcp" in challenge + assert "/.well-known/oauth-protected-resource/delegated_oauth_server/mcp" in challenge + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_token_exchange_without_subject_returns_preemptive_resource_metadata_401(): + """An ``oauth2_token_exchange`` (OBO) server with no caller subject token must fail fast at + connect with a 401 carrying the RFC 9728 ``resource_metadata`` + RFC 6750 ``invalid_token`` + challenge, so the client discovers the IdP and retries with a subject token. A tool-call-time + 401 would be wrapped into a JSON-RPC error and the WWW-Authenticate lost, so this preemptive + challenge is what drives the discovery flow.""" + from fastapi import HTTPException + + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateful, + ) + except ImportError: + pytest.skip("MCP server not available") + + scope = { + "type": "http", + "method": "POST", + "path": "/mcp/obo_server", + "_original_path": "/mcp/obo_server", + "scheme": "https", + "query_string": b"", + "root_path": "", + "server": ("litellm.example.com", 443), + "headers": [ + (b"content-type", b"application/json"), + (b"host", b"litellm.example.com"), + ], + } + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}', + "more_body": False, + } ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = None + obo_server = MagicMock() + obo_server.auth_type = MCPAuth.oauth2_token_exchange + obo_server.alias = None + obo_server.server_name = "obo_server" + obo_server.name = "obo_server" + obo_server.server_id = "obo-server" + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["obo_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._handle_stale_mcp_session", + new_callable=AsyncMock, + return_value=False, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=obo_server, + ), + patch.object( + session_manager_stateful, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + ): + with pytest.raises(HTTPException) as exc_info: + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 0 + assert exc_info.value.status_code == 401 + headers = {k.lower(): v for k, v in (exc_info.value.headers or {}).items()} + challenge = headers["www-authenticate"] + # Structural invariants only: the exact root-path prefix is exercised in the adapter's + # oauth_protected_resource_path unit test, so this handler test stays hermetic w.r.t. + # SERVER_ROOT_PATH (which other tests in the shard may have left set in the environment). + assert "resource_metadata=" in challenge + assert "/.well-known/oauth-protected-resource" in challenge + assert challenge.split('resource_metadata="', 1)[1].split('"', 1)[0].endswith("/mcp/obo_server") + assert 'error="invalid_token"' in challenge + + +def _passthrough_mode_scope(server_name: str, extra_headers=None): + headers = [ + (b"content-type", b"application/json"), + (b"host", b"litellm.example.com"), + ] + list(extra_headers or []) + return { + "type": "http", + "method": "POST", + "path": f"/mcp/{server_name}", + "_original_path": f"/{server_name}/mcp", + "scheme": "https", + "query_string": b"", + "root_path": "", + "server": ("litellm.example.com", 443), + "headers": headers, + } + + +def _build_passthrough_mode_server(server_name: str, auth_type): + from litellm.proxy._types import MCPTransport + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + return MCPServer( + server_id=f"{server_name}-id", + name=server_name, + server_name=server_name, + alias=server_name, + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type=auth_type, + ) + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_oauth_delegate_without_token_returns_gateway_proxied_401(): + """oauth_delegate is admitted with the LiteLLM key but still owns upstream + OAuth. With no forwarded upstream token the gateway must challenge with the + proxied resource_metadata (which advertises the upstream IdP), never the + gateway authorization_uri and never a silent 200.""" + from fastapi import HTTPException + + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateful, + ) + except ImportError: + pytest.skip("MCP server not available") + + scope = _passthrough_mode_scope("od_server") + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = "u1" + od_server = _build_passthrough_mode_server("od_server", MCPAuth.oauth_delegate) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["od_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=od_server, + ), + patch.object( + session_manager_stateful, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + ): + with pytest.raises(HTTPException) as exc_info: + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 0 + assert exc_info.value.status_code == 401 + challenge = exc_info.value.headers["www-authenticate"] + assert "resource_metadata=" in challenge + assert "authorization_uri=" not in challenge + assert "/.well-known/oauth-protected-resource/od_server/mcp" in challenge + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_oauth_delegate_with_forwarded_token_skips_challenge(): + """When the oauth_delegate caller carries both the LiteLLM key and a separate + upstream Authorization, the gateway must forward to the session manager, not + re-challenge. Guards the ``_get_forwarded_auth_from_scope(...) is None`` + condition: dropping it would 401 even a fully-authenticated request.""" + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateless, + ) + except ImportError: + pytest.skip("MCP server not available") + + scope = _passthrough_mode_scope( + "od_server", + extra_headers=[ + (b"x-litellm-api-key", b"Bearer sk-1234"), + (b"authorization", b"Bearer upstream-token"), + ], + ) + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = "u1" + od_server = _build_passthrough_mode_server("od_server", MCPAuth.oauth_delegate) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["od_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._handle_stale_mcp_session", + new_callable=AsyncMock, + return_value=False, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._check_passthrough_upstream_auth", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=od_server, + ), + patch.object( + session_manager_stateless, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + patch.object(session_manager_stateless, "_server_instances", {}), + ): + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 1 + + +async def _run_passthrough_connect( + *, + auth_type, + server_names, + mcp_server_auth_headers, + scope_extra_headers=None, +): + """Drive handle_streamable_http_mcp through the preemptive-401 gate and report whether it + challenged (raised) or forwarded to the session manager. Returns (challenged, www_authenticate).""" + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateless, + ) + + scope = _passthrough_mode_scope(server_names[0], extra_headers=scope_extra_headers) + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = "u1" + server = _build_passthrough_mode_server(server_names[0], auth_type) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, server_names, mcp_server_auth_headers, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch("litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", True), + patch( + "litellm.proxy._experimental.mcp_server.server._handle_stale_mcp_session", + new_callable=AsyncMock, + return_value=False, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._check_passthrough_upstream_auth", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=server, + ), + patch.object(session_manager_stateless, "handle_request", new_callable=AsyncMock) as mock_handle_request, + patch.object(session_manager_stateless, "_server_instances", {}), + ): + try: + await handle_streamable_http_mcp(scope, receive, send) + except HTTPException as exc: + return True, (exc.headers or {}).get("www-authenticate") + return mock_handle_request.await_count == 0, None + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.oauth_delegate, MCPAuth.true_passthrough]) +async def test_handle_streamable_http_mcp_per_server_header_skips_preemptive_challenge(auth_type): + """A per-server x-mcp-{alias}-authorization header binds the upstream token to one server; the + connect gate must recognize it and forward instead of spuriously 401-ing, since egress already + honors it. Without this, the mandatory multi-server binding is unusable at connect.""" + try: + from litellm.proxy._experimental.mcp_server.server import handle_streamable_http_mcp # noqa: F401 + except ImportError: + pytest.skip("MCP server not available") + + challenged, _ = await _run_passthrough_connect( + auth_type=auth_type, + server_names=["pt_server"], + mcp_server_auth_headers={"pt_server": {"Authorization": "Bearer upstream-token"}}, + ) + assert challenged is False + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.oauth_delegate, MCPAuth.true_passthrough]) +async def test_handle_streamable_http_mcp_sanitized_per_server_header_skips_preemptive_challenge(auth_type): + """A dashboard client sends x-mcp-{sanitize_mcp_alias_for_header(alias)}-authorization, so the + alias 'pt-server' arrives as the header key 'pt_server'. Egress resolves that via the sanitized + alias, so the connect gate must too, or it 401s a token egress would forward.""" + try: + from litellm.proxy._experimental.mcp_server.server import handle_streamable_http_mcp # noqa: F401 + except ImportError: + pytest.skip("MCP server not available") + + challenged, _ = await _run_passthrough_connect( + auth_type=auth_type, + server_names=["pt-server"], + mcp_server_auth_headers={"pt_server": {"Authorization": "Bearer upstream-token"}}, + ) + assert challenged is False + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auth_type", [MCPAuth.oauth_delegate, MCPAuth.true_passthrough]) +async def test_handle_streamable_http_mcp_aggregate_does_not_preemptively_challenge(auth_type): + """A multi-server aggregate must degrade gracefully: the preemptive 401 is single-server only, so + one server missing a token cannot 401 the whole connect (the listing absorbs per-server failures).""" + try: + from litellm.proxy._experimental.mcp_server.server import handle_streamable_http_mcp # noqa: F401 + except ImportError: + pytest.skip("MCP server not available") + + challenged, _ = await _run_passthrough_connect( + auth_type=auth_type, + server_names=["pt_server", "pt_server_2"], + mcp_server_auth_headers=None, + ) + assert challenged is False + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_true_passthrough_without_token_surfaces_verbatim_upstream_challenge(): + """true_passthrough is a transparent proxy: with no client Authorization the + gateway probes the upstream and surfaces its own WWW-Authenticate verbatim, + so the client discovers and authorizes against the upstream directly. Guards + against answering initialize locally with a silent 200.""" + from fastapi import HTTPException + + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateful, + ) + except ImportError: + pytest.skip("MCP server not available") + + upstream_challenge = 'Bearer resource_metadata="https://upstream.example.com/.well-known/oauth-protected-resource"' + probe_response = MagicMock() + probe_response.status_code = 401 + probe_response.headers = {"www-authenticate": upstream_challenge} + probe_client = MagicMock() + probe_client.post = AsyncMock(return_value=probe_response) + + scope = _passthrough_mode_scope("tp_server") + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = None + tp_server = _build_passthrough_mode_server("tp_server", MCPAuth.true_passthrough) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["tp_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", + return_value=probe_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=tp_server, + ), + patch.object( + session_manager_stateful, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + ): + with pytest.raises(HTTPException) as exc_info: + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 0 + assert exc_info.value.status_code == 401 + assert exc_info.value.headers["www-authenticate"] == upstream_challenge + probe_client.post.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_true_passthrough_dcr_bridge_challenges_with_gateway_metadata(): + """With dcr_bridge on, the missing-token challenge names the GATEWAY's well-known instead of + relaying the upstream's: the gateway is the authorization server for bridge clients, and the + upstream's own challenge would point them at metadata that fails the RFC 9728 resource match. + The upstream probe is skipped entirely; the gateway can answer authoritatively.""" + from fastapi import HTTPException + + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateful, + ) + except ImportError: + pytest.skip("MCP server not available") + + probe_client = MagicMock() + probe_client.post = AsyncMock() + + scope = _passthrough_mode_scope("tp_bridge_server") + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = None + bridge_server = _build_passthrough_mode_server("tp_bridge_server", MCPAuth.true_passthrough).model_copy( + update={"dcr_bridge": True} + ) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["tp_bridge_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", + return_value=probe_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=bridge_server, + ), + patch.object( + session_manager_stateful, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + ): + with pytest.raises(HTTPException) as exc_info: + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 0 + assert exc_info.value.status_code == 401 + challenge = exc_info.value.headers["www-authenticate"] + assert "/.well-known/oauth-protected-resource/tp_bridge_server/mcp" in challenge + assert "upstream.example.com" not in challenge + probe_client.post.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_handle_streamable_http_mcp_true_passthrough_with_token_skips_probe_and_challenge(): + """When the true_passthrough caller already carries an Authorization the + gateway must forward without probing or challenging. Guards the + ``not _scope_has_authorization_header(scope)`` condition and the no-probe + fast path.""" + try: + from litellm.proxy._experimental.mcp_server.server import ( + handle_streamable_http_mcp, + session_manager_stateless, + ) + except ImportError: + pytest.skip("MCP server not available") + + probe_client = MagicMock() + probe_client.post = AsyncMock() + + scope = _passthrough_mode_scope( + "tp_server", + extra_headers=[(b"authorization", b"Bearer upstream-token")], + ) + receive = AsyncMock( + return_value={ + "type": "http.request", + "body": b'{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}', + "more_body": False, + } + ) + send = AsyncMock() + user_auth = MagicMock() + user_auth.user_id = None + tp_server = _build_passthrough_mode_server("tp_server", MCPAuth.true_passthrough) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.extract_mcp_auth_context", + new_callable=AsyncMock, + return_value=(user_auth, None, ["tp_server"], None, None, None), + ), + patch("litellm.proxy._experimental.mcp_server.server.set_auth_context"), + patch( + "litellm.proxy._experimental.mcp_server.server._SESSION_MANAGERS_INITIALIZED", + True, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._handle_stale_mcp_session", + new_callable=AsyncMock, + return_value=False, + ), + patch( + "litellm.proxy._experimental.mcp_server.server._check_passthrough_upstream_auth", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.get_async_httpx_client", + return_value=probe_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.server.global_mcp_server_manager.get_mcp_server_by_name", + return_value=tp_server, + ), + patch.object( + session_manager_stateless, + "handle_request", + new_callable=AsyncMock, + ) as mock_handle_request, + patch.object(session_manager_stateless, "_server_instances", {}), + ): + await handle_streamable_http_mcp(scope, receive, send) + + assert mock_handle_request.await_count == 1 + probe_client.post.assert_not_awaited() diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_tool_search.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_tool_search.py new file mode 100644 index 00000000000..b8f0b205831 --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_tool_search.py @@ -0,0 +1,884 @@ +""" +Tests for MCP tool search feature. + +Covers: +- search_tools() pure function +- get_virtual_tool_definitions() shape +- list_tool_rest_api returns only virtual tools when mcp_tool_search_enabled=True +- call_tool_rest_api intercepts mcp_tool_search calls +- call_tool_rest_api intercepts mcp_tool_call calls +""" + +import json +from typing import Any +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from litellm.models.object_permission import LiteLLM_ObjectPermissionTable +from litellm.proxy._experimental.mcp_server.tool_search import ( + MCP_TOOL_CALL_TOOL_NAME, + MCP_TOOL_SEARCH_TOOL_NAME, + coerce_top_k, + get_virtual_tool_definitions, + search_tools, +) +from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth + + +def _make_tools(specs: list[tuple[str, str]]) -> list[dict[str, Any]]: + return [ + { + "name": name, + "description": desc, + "inputSchema": {"type": "object", "properties": {}}, + } + for name, desc in specs + ] + + +def _make_perm(**kwargs: Any) -> LiteLLM_ObjectPermissionTable: + return LiteLLM_ObjectPermissionTable(object_permission_id="test", **kwargs) + + +SAMPLE_TOOLS = _make_tools( + [ + ("github-create_issue", "Create a new issue in a GitHub repository"), + ("github-list_repos", "List all repositories for a GitHub user"), + ("slack-send_message", "Send a message to a Slack channel"), + ("slack-list_channels", "List all Slack channels in a workspace"), + ("notion-create_page", "Create a new page in Notion"), + ] +) + + +class TestCoerceTopK: + def test_int_passthrough(self) -> None: + assert coerce_top_k(3) == 3 + + def test_numeric_string_coerced(self) -> None: + assert coerce_top_k("7") == 7 + + def test_float_truncated(self) -> None: + assert coerce_top_k(3.9) == 3 + + def test_non_numeric_string_returns_default(self) -> None: + assert coerce_top_k("abc") == 5 + + def test_none_returns_default(self) -> None: + assert coerce_top_k(None) == 5 + + def test_custom_default(self) -> None: + assert coerce_top_k("nope", default=10) == 10 + + +class TestSearchTools: + def test_returns_matching_tools(self) -> None: + results = search_tools("github issue", SAMPLE_TOOLS) + names = [t["name"] for t in results] + assert "github-create_issue" in names + + def test_ranks_by_relevance(self) -> None: + results = search_tools("github", SAMPLE_TOOLS) + names = [t["name"] for t in results] + github_positions = [i for i, n in enumerate(names) if n.startswith("github")] + other_positions = [i for i, n in enumerate(names) if not n.startswith("github")] + assert all(g < o for g in github_positions for o in other_positions) + + def test_top_k_limits_results(self) -> None: + results = search_tools("a", SAMPLE_TOOLS, top_k=2) + assert len(results) <= 2 + + def test_empty_query_returns_empty(self) -> None: + assert search_tools("", SAMPLE_TOOLS) == [] + + def test_no_match_returns_empty(self) -> None: + assert search_tools("xyzzy_nonexistent_zzz", SAMPLE_TOOLS) == [] + + def test_matches_description_not_just_name(self) -> None: + results = search_tools("channel", SAMPLE_TOOLS) + names = [t["name"] for t in results] + assert "slack-list_channels" in names + + def test_case_insensitive(self) -> None: + lower = [t["name"] for t in search_tools("github", SAMPLE_TOOLS)] + upper = [t["name"] for t in search_tools("GITHUB", SAMPLE_TOOLS)] + assert lower == upper + + def test_result_tools_have_full_schema(self) -> None: + for tool in search_tools("github", SAMPLE_TOOLS): + assert "name" in tool + assert "description" in tool + assert "inputSchema" in tool + + +class TestGetVirtualToolDefinitions: + def test_returns_two_tools(self) -> None: + assert len(get_virtual_tool_definitions()) == 2 + + def test_has_mcp_tool_search(self) -> None: + names = [t["name"] for t in get_virtual_tool_definitions()] + assert MCP_TOOL_SEARCH_TOOL_NAME in names + + def test_has_mcp_tool_call(self) -> None: + names = [t["name"] for t in get_virtual_tool_definitions()] + assert MCP_TOOL_CALL_TOOL_NAME in names + + def test_mcp_tool_search_schema_has_query(self) -> None: + tools = get_virtual_tool_definitions() + search_tool = next(t for t in tools if t["name"] == MCP_TOOL_SEARCH_TOOL_NAME) + props = search_tool["inputSchema"]["properties"] + assert "query" in props + assert search_tool["inputSchema"]["required"] == ["query"] + + def test_mcp_tool_call_schema_has_tool_name_and_arguments(self) -> None: + tools = get_virtual_tool_definitions() + call_tool = next(t for t in tools if t["name"] == MCP_TOOL_CALL_TOOL_NAME) + props = call_tool["inputSchema"]["properties"] + assert "tool_name" in props + assert "arguments" in props + assert "tool_name" in call_tool["inputSchema"]["required"] + + def test_all_tools_have_description(self) -> None: + for tool in get_virtual_tool_definitions(): + assert tool.get("description"), f"{tool['name']} missing description" + + def test_definitions_construct_mcp_protocol_tool(self) -> None: + """The MCP protocol list_tools handler builds mcp.types.Tool(**d) from + each definition, so the dict keys must stay valid Tool fields.""" + from mcp.types import Tool + + built = [Tool(**d) for d in get_virtual_tool_definitions()] + assert {t.name for t in built} == { + MCP_TOOL_SEARCH_TOOL_NAME, + MCP_TOOL_CALL_TOOL_NAME, + } + + +class TestListToolRestApiWithToolSearch: + @pytest.mark.asyncio + async def test_returns_only_virtual_tools_when_flag_enabled(self) -> None: + from litellm.proxy._experimental.mcp_server.rest_endpoints import router + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + object_permission=_make_perm( + mcp_tool_search_enabled=True, + mcp_servers=["github", "slack"], + ), + ) + + mock_request = MagicMock() + mock_request.headers = {} + + list_fn = next( + r.endpoint + for r in router.routes + if hasattr(r, "path") and r.path.endswith("/tools/list") and hasattr(r, "methods") and "GET" in r.methods + ) + + result = await list_fn( + request=mock_request, + server_id=None, + include_disabled_tools=False, + user_api_key_dict=user_api_key_dict, + ) + + assert result["error"] is None + tool_names = [t["name"] for t in result["tools"]] + assert set(tool_names) == {MCP_TOOL_SEARCH_TOOL_NAME, MCP_TOOL_CALL_TOOL_NAME} + + @pytest.mark.asyncio + async def test_returns_full_catalog_when_flag_disabled(self) -> None: + from litellm.proxy._experimental.mcp_server.rest_endpoints import router + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + object_permission=_make_perm( + mcp_tool_search_enabled=False, + mcp_servers=["github"], + ), + ) + + mock_request = MagicMock() + mock_request.headers = {} + + fake_tools = [ + { + "name": "github-create_issue", + "description": "Create issue", + "inputSchema": {"type": "object"}, + } + ] + + list_fn = next( + r.endpoint + for r in router.routes + if hasattr(r, "path") and r.path.endswith("/tools/list") and hasattr(r, "methods") and "GET" in r.methods + ) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints.build_effective_auth_contexts", + new_callable=AsyncMock, + return_value=[user_api_key_dict], + ), + patch("litellm.proxy._experimental.mcp_server.rest_endpoints.global_mcp_server_manager") as mock_manager, + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._get_tools_for_single_server", + new_callable=AsyncMock, + return_value=fake_tools, + ), + patch("litellm.proxy._experimental.mcp_server.rest_endpoints.IPAddressUtils"), + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._prefetch_user_oauth_creds", + new_callable=AsyncMock, + return_value={}, + ), + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._get_oauth2_server_ids", + return_value=[], + ), + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._get_server_auth_header", + return_value=None, + ), + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._get_user_oauth_extra_headers", + new_callable=AsyncMock, + return_value=None, + ), + ): + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["github"]) + mock_manager.filter_server_ids_by_ip_with_info = MagicMock(return_value=(["github"], 0)) + mock_manager.get_mcp_server_by_id = MagicMock(return_value=MagicMock(name="github", server_id="github")) + result = await list_fn( + request=mock_request, + server_id=None, + include_disabled_tools=False, + user_api_key_dict=user_api_key_dict, + ) + + tool_names = [t["name"] for t in result["tools"]] + assert MCP_TOOL_SEARCH_TOOL_NAME not in tool_names + assert "github-create_issue" in tool_names + + @pytest.mark.asyncio + async def test_admin_include_disabled_tools_bypasses_virtual_catalog(self) -> None: + """Regression: an admin listing with include_disabled_tools must see the + real catalog (to configure allowlists) even when mcp_tool_search_enabled is + set, instead of the two virtual tools.""" + from litellm.proxy._experimental.mcp_server.rest_endpoints import router + + user_api_key_dict = UserAPIKeyAuth( + api_key="admin_key", + user_role=LitellmUserRoles.PROXY_ADMIN, + object_permission=_make_perm( + mcp_tool_search_enabled=True, + mcp_servers=["github"], + ), + ) + + mock_request = MagicMock() + mock_request.headers = {} + + fake_tools = [ + { + "name": "github-create_issue", + "description": "Create issue", + "inputSchema": {"type": "object"}, + } + ] + + list_fn = next( + r.endpoint + for r in router.routes + if hasattr(r, "path") and r.path.endswith("/tools/list") and hasattr(r, "methods") and "GET" in r.methods + ) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints.build_effective_auth_contexts", + new_callable=AsyncMock, + return_value=[user_api_key_dict], + ), + patch("litellm.proxy._experimental.mcp_server.rest_endpoints.global_mcp_server_manager") as mock_manager, + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._get_tools_for_single_server", + new_callable=AsyncMock, + return_value=fake_tools, + ), + patch("litellm.proxy._experimental.mcp_server.rest_endpoints.IPAddressUtils"), + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._prefetch_user_oauth_creds", + new_callable=AsyncMock, + return_value={}, + ), + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._get_oauth2_server_ids", + return_value=[], + ), + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._get_server_auth_header", + return_value=None, + ), + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._get_user_oauth_extra_headers", + new_callable=AsyncMock, + return_value=None, + ), + ): + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["github"]) + mock_manager.filter_server_ids_by_ip_with_info = MagicMock(return_value=(["github"], 0)) + mock_manager.get_mcp_server_by_id = MagicMock(return_value=MagicMock(name="github", server_id="github")) + result = await list_fn( + request=mock_request, + server_id=None, + include_disabled_tools=True, + user_api_key_dict=user_api_key_dict, + ) + + tool_names = [t["name"] for t in result["tools"]] + assert MCP_TOOL_SEARCH_TOOL_NAME not in tool_names + assert "github-create_issue" in tool_names + + +class TestCallToolRestApiVirtualTools: + def _make_request(self, body: dict[str, Any]) -> MagicMock: + mock_request = MagicMock() + mock_request.json = AsyncMock(return_value=body) + mock_request.headers = {} + mock_request.url = MagicMock() + mock_request.url.path = "/mcp-rest/tools/call" + return mock_request + + def _get_call_fn(self) -> Any: + from litellm.proxy._experimental.mcp_server.rest_endpoints import router + + return next( + r.endpoint + for r in router.routes + if hasattr(r, "path") and r.path.endswith("/tools/call") and hasattr(r, "methods") and "POST" in r.methods + ) + + @pytest.mark.asyncio + async def test_mcp_tool_search_call_returns_tool_defs(self) -> None: + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + object_permission=_make_perm( + mcp_tool_search_enabled=True, + mcp_servers=["github"], + ), + ) + + request = self._make_request({"name": MCP_TOOL_SEARCH_TOOL_NAME, "arguments": {"query": "create issue"}}) + + mock_tool = MagicMock() + mock_tool.name = "github-create_issue" + mock_tool.description = "Create a GitHub issue" + mock_tool.inputSchema = {"type": "object", "properties": {}} + + with patch( + "litellm.proxy._experimental.mcp_server.server._list_mcp_tools", + new_callable=AsyncMock, + return_value=[mock_tool], + ): + result = await self._get_call_fn()( + request=request, + user_api_key_dict=user_api_key_dict, + ) + + assert result.content + assert result.content[0].type == "text" + returned_tools = json.loads(result.content[0].text) + assert isinstance(returned_tools, list) + assert any(t["name"] == "github-create_issue" for t in returned_tools) + + @pytest.mark.asyncio + async def test_mcp_tool_call_executes_discovered_tool(self) -> None: + from mcp.types import CallToolResult, TextContent + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + object_permission=_make_perm( + mcp_tool_search_enabled=True, + mcp_servers=["github"], + ), + ) + + request = self._make_request( + { + "name": MCP_TOOL_CALL_TOOL_NAME, + "arguments": { + "tool_name": "github-create_issue", + "arguments": {"title": "bug", "repo": "myrepo"}, + }, + } + ) + + fake_result = CallToolResult( + content=[TextContent(type="text", text="Issue created")], + isError=False, + ) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.server._get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=[MagicMock()], + ), + patch( + "litellm.proxy._experimental.mcp_server.server.execute_mcp_tool", + new_callable=AsyncMock, + return_value=fake_result, + ) as mock_execute, + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints._fire_mcp_tool_call_logging", + new_callable=AsyncMock, + side_effect=RuntimeError("logging failed"), + ) as mock_fire_logging, + ): + result = await self._get_call_fn()( + request=request, + user_api_key_dict=user_api_key_dict, + ) + + mock_execute.assert_awaited_once() + mock_fire_logging.assert_awaited_once() + assert mock_execute.await_args.kwargs["name"] == "github-create_issue" + + assert result.isError is False + assert result.content[0].text == "Issue created" + + @pytest.mark.asyncio + async def test_mcp_tool_call_forwards_client_ip_for_ip_filtering(self) -> None: + """Regression: the virtual call path must resolve allowed servers with the + request's client IP so IP-restricted servers (available_on_public_internet: + false) cannot be reached from a public IP.""" + from mcp.types import CallToolResult, TextContent + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + object_permission=_make_perm(mcp_tool_search_enabled=True, mcp_servers=["github"]), + ) + request = self._make_request( + { + "name": MCP_TOOL_CALL_TOOL_NAME, + "arguments": {"tool_name": "github-create_issue", "arguments": {}}, + } + ) + + fake_result = CallToolResult(content=[TextContent(type="text", text="ok")], isError=False) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints.IPAddressUtils.get_mcp_client_ip", + return_value="203.0.113.7", + ), + patch( + "litellm.proxy._experimental.mcp_server.server._get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=[MagicMock()], + ) as mock_allowed, + patch( + "litellm.proxy._experimental.mcp_server.server.execute_mcp_tool", + new_callable=AsyncMock, + return_value=fake_result, + ), + ): + await self._get_call_fn()(request=request, user_api_key_dict=user_api_key_dict) + + mock_allowed.assert_awaited_once() + assert mock_allowed.await_args.kwargs["client_ip"] == "203.0.113.7" + + @pytest.mark.asyncio + async def test_mcp_tool_search_forwards_client_ip_for_ip_filtering(self) -> None: + """Search must list tools through the IP-filtered catalog, not the raw one.""" + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + object_permission=_make_perm(mcp_tool_search_enabled=True, mcp_servers=["github"]), + ) + request = self._make_request({"name": MCP_TOOL_SEARCH_TOOL_NAME, "arguments": {"query": "issue"}}) + + with ( + patch( + "litellm.proxy._experimental.mcp_server.rest_endpoints.IPAddressUtils.get_mcp_client_ip", + return_value="203.0.113.7", + ), + patch( + "litellm.proxy._experimental.mcp_server.server._list_mcp_tools", + new_callable=AsyncMock, + return_value=[], + ) as mock_list, + ): + await self._get_call_fn()(request=request, user_api_key_dict=user_api_key_dict) + + mock_list.assert_awaited_once() + assert mock_list.await_args.kwargs["client_ip"] == "203.0.113.7" + + @pytest.mark.asyncio + async def test_mcp_tool_search_requires_flag_enabled(self) -> None: + from fastapi import HTTPException + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + object_permission=_make_perm(mcp_tool_search_enabled=False), + ) + + request = self._make_request({"name": MCP_TOOL_SEARCH_TOOL_NAME, "arguments": {"query": "create issue"}}) + + with pytest.raises(HTTPException) as exc_info: + await self._get_call_fn()( + request=request, + user_api_key_dict=user_api_key_dict, + ) + + assert exc_info.value.status_code in (400, 403, 404) + + +class TestDispatchVirtualMcpTool: + """Covers the SSE/protocol-path interception helper in server.py.""" + + @pytest.mark.asyncio + async def test_returns_none_for_non_virtual_tool(self) -> None: + from litellm.proxy._experimental.mcp_server.server import ( + _dispatch_virtual_mcp_tool, + ) + + result = await _dispatch_virtual_mcp_tool( + name="github-create_issue", + arguments={}, + user_api_key_auth=UserAPIKeyAuth(api_key="k"), + client_ip=None, + ) + assert result is None + + @pytest.mark.asyncio + async def test_rejects_when_flag_disabled(self) -> None: + from litellm.proxy._experimental.mcp_server.server import ( + _dispatch_virtual_mcp_tool, + ) + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=False)) + result = await _dispatch_virtual_mcp_tool( + name=MCP_TOOL_SEARCH_TOOL_NAME, + arguments={"query": "x"}, + user_api_key_auth=uak, + client_ip=None, + ) + assert result is not None + assert result.isError is True + + @pytest.mark.asyncio + async def test_routes_search_with_client_ip(self) -> None: + from litellm.proxy._experimental.mcp_server import server as srv + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=True)) + with patch( + "litellm.proxy._experimental.mcp_server.tool_search.handle_mcp_tool_search", + new_callable=AsyncMock, + return_value="SEARCH_RESULT", + ) as mock_search: + result = await srv._dispatch_virtual_mcp_tool( + name=MCP_TOOL_SEARCH_TOOL_NAME, + arguments={"query": "q", "top_k": 3}, + user_api_key_auth=uak, + client_ip="203.0.113.9", + ) + + assert result == "SEARCH_RESULT" + assert mock_search.await_args.kwargs["client_ip"] == "203.0.113.9" + assert mock_search.await_args.kwargs["query"] == "q" + assert mock_search.await_args.kwargs["top_k"] == 3 + + @pytest.mark.asyncio + async def test_routes_call_with_client_ip(self) -> None: + from litellm.proxy._experimental.mcp_server import server as srv + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=True)) + with patch( + "litellm.proxy._experimental.mcp_server.tool_search.handle_mcp_tool_call", + new_callable=AsyncMock, + return_value="CALL_RESULT", + ) as mock_call: + result = await srv._dispatch_virtual_mcp_tool( + name=MCP_TOOL_CALL_TOOL_NAME, + arguments={"tool_name": "math-add", "arguments": {"a": 1, "b": 2}}, + user_api_key_auth=uak, + client_ip="203.0.113.9", + mcp_auth_header="bearer-xyz", + mcp_server_auth_headers={"github": {"Authorization": "Bearer gh"}}, + oauth2_headers={"Authorization": "Bearer oauth"}, + raw_headers={"x-mcp-auth": "tok"}, + ) + + assert result == "CALL_RESULT" + kw = mock_call.await_args.kwargs + assert kw["tool_name"] == "math-add" + assert kw["client_ip"] == "203.0.113.9" + assert kw["mcp_auth_header"] == "bearer-xyz" + assert kw["mcp_server_auth_headers"] == {"github": {"Authorization": "Bearer gh"}} + assert kw["oauth2_headers"] == {"Authorization": "Bearer oauth"} + assert kw["raw_headers"] == {"x-mcp-auth": "tok"} + + @pytest.mark.asyncio + async def test_call_builds_and_forwards_logging_obj(self) -> None: + """Regression: the SSE dispatch must run the pre-call pipeline and forward + the resulting logging object to handle_mcp_tool_call, otherwise mcp_tool_call + over /mcp/ skips spend logging and guardrails (unlike the REST path).""" + from litellm.proxy._experimental.mcp_server import server as srv + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=True)) + sentinel_logging_obj = object() + with ( + patch.object( + srv, + "_build_virtual_call_logging_obj", + new_callable=AsyncMock, + return_value=sentinel_logging_obj, + ) as mock_build, + patch( + "litellm.proxy._experimental.mcp_server.tool_search.handle_mcp_tool_call", + new_callable=AsyncMock, + return_value="CALL_RESULT", + ) as mock_call, + ): + await srv._dispatch_virtual_mcp_tool( + name=MCP_TOOL_CALL_TOOL_NAME, + arguments={"tool_name": "math-add", "arguments": {"a": 1}}, + user_api_key_auth=uak, + client_ip=None, + ) + + assert mock_build.await_count == 1 + assert mock_call.await_args.kwargs["litellm_logging_obj"] is sentinel_logging_obj + + @pytest.mark.asyncio + async def test_search_coerces_non_int_top_k(self) -> None: + """Regression: a non-integer top_k from an MCP client must not raise; it + falls back to the default instead of ValueError propagating out.""" + from litellm.proxy._experimental.mcp_server import server as srv + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=True)) + with patch( + "litellm.proxy._experimental.mcp_server.tool_search.handle_mcp_tool_search", + new_callable=AsyncMock, + return_value="SEARCH_RESULT", + ) as mock_search: + await srv._dispatch_virtual_mcp_tool( + name=MCP_TOOL_SEARCH_TOOL_NAME, + arguments={"query": "issue", "top_k": "not-a-number"}, + user_api_key_auth=uak, + client_ip=None, + ) + + assert mock_search.await_args.kwargs["top_k"] == 5 + + @pytest.mark.asyncio + async def test_call_handler_forwards_auth_headers_to_execute(self) -> None: + """Regression: per-request auth headers must reach execute_mcp_tool so + upstream MCP servers needing pass-through auth can be called.""" + from mcp.types import CallToolResult, TextContent + + from litellm.proxy._experimental.mcp_server.tool_search import ( + handle_mcp_tool_call, + ) + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=True)) + fake = CallToolResult(content=[TextContent(type="text", text="ok")], isError=False) + with ( + patch( + "litellm.proxy._experimental.mcp_server.server._get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=[MagicMock()], + ) as mock_allowed, + patch( + "litellm.proxy._experimental.mcp_server.server.execute_mcp_tool", + new_callable=AsyncMock, + return_value=fake, + ) as mock_exec, + ): + sentinel_logging_obj = object() + await handle_mcp_tool_call( + tool_name="github-create_issue", + arguments={}, + user_api_key_dict=uak, + mcp_servers=["github"], + mcp_auth_header="bearer-xyz", + mcp_server_auth_headers={"github": {"Authorization": "Bearer gh"}}, + oauth2_headers={"Authorization": "Bearer oauth"}, + raw_headers={"x-mcp-auth": "tok"}, + litellm_logging_obj=sentinel_logging_obj, + ) + + kw = mock_exec.await_args.kwargs + assert kw["mcp_auth_header"] == "bearer-xyz" + assert kw["mcp_server_auth_headers"] == {"github": {"Authorization": "Bearer gh"}} + assert kw["oauth2_headers"] == {"Authorization": "Bearer oauth"} + assert kw["raw_headers"] == {"x-mcp-auth": "tok"} + # Spend logging: the logging object must reach execute_mcp_tool + assert kw["litellm_logging_obj"] is sentinel_logging_obj + # Scoped session: the requested mcp_servers scope must reach server resolution + assert mock_allowed.await_args.kwargs["mcp_servers"] == ["github"] + + @pytest.mark.asyncio + async def test_call_rejected_when_no_accessible_servers(self) -> None: + """Regression: a key with no accessible MCP servers must not reach + execute_mcp_tool, where an unprefixed local tool name would otherwise + run via the local registry without a server permission check.""" + from fastapi import HTTPException + + from litellm.proxy._experimental.mcp_server.tool_search import ( + handle_mcp_tool_call, + ) + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=True)) + with ( + patch( + "litellm.proxy._experimental.mcp_server.server._get_allowed_mcp_servers", + new_callable=AsyncMock, + return_value=[], + ), + patch( + "litellm.proxy._experimental.mcp_server.server.execute_mcp_tool", + new_callable=AsyncMock, + ) as mock_exec, + ): + with pytest.raises(HTTPException) as exc_info: + await handle_mcp_tool_call( + tool_name="local_secret_tool", + arguments={}, + user_api_key_dict=uak, + ) + + assert exc_info.value.status_code == 403 + mock_exec.assert_not_awaited() + + +class TestCaptureHostProgressCallback: + """Covers the host progress-forwarding helper extracted from the tool call path.""" + + def test_returns_none_when_request_context_unavailable(self) -> None: + from litellm.proxy._experimental.mcp_server.server import ( + _capture_host_progress_callback, + ) + + class _NoCtx: + @property + def request_context(self): # type: ignore[no-untyped-def] + raise RuntimeError("no context") + + assert _capture_host_progress_callback(_NoCtx()) is None + + def test_returns_none_when_no_progress_token(self) -> None: + from litellm.proxy._experimental.mcp_server.server import ( + _capture_host_progress_callback, + ) + + host = MagicMock() + host.request_context.meta.progressToken = None + assert _capture_host_progress_callback(host) is None + + def test_returns_callable_when_token_present(self) -> None: + from litellm.proxy._experimental.mcp_server.server import ( + _capture_host_progress_callback, + ) + + host = MagicMock() + host.request_context.meta.progressToken = "tok12345" + host.request_context.session = MagicMock() + assert callable(_capture_host_progress_callback(host)) + + def test_returns_callable_when_token_is_integer(self) -> None: + from litellm.proxy._experimental.mcp_server.server import ( + _capture_host_progress_callback, + ) + + host = MagicMock() + host.request_context.meta.progressToken = 12345 + host.request_context.session = MagicMock() + assert callable(_capture_host_progress_callback(host)) + + def test_returns_callable_when_token_is_zero(self) -> None: + from litellm.proxy._experimental.mcp_server.server import ( + _capture_host_progress_callback, + ) + + host = MagicMock() + host.request_context.meta.progressToken = 0 + host.request_context.session = MagicMock() + assert callable(_capture_host_progress_callback(host)) + + @pytest.mark.asyncio + async def test_forwarded_progress_token_preserves_integer_value(self) -> None: + from litellm.proxy._experimental.mcp_server.server import ( + _capture_host_progress_callback, + ) + + host = MagicMock() + host.request_context.meta.progressToken = 12345 + session = AsyncMock() + host.request_context.session = session + + callback = _capture_host_progress_callback(host) + assert callback is not None + await callback(0.5, 1.0) + + session.send_progress_notification.assert_awaited_once_with( + progress_token=12345, + progress=0.5, + total=1.0, + ) + + +class TestHandleListToolsVirtual: + """Covers the protocol list_tools early-return when the flag is enabled.""" + + @pytest.mark.asyncio + async def test_returns_virtual_tools_when_flag_enabled(self) -> None: + from litellm.proxy._experimental.mcp_server import server as srv + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=True)) + with patch( + "litellm.proxy._experimental.mcp_server.server.get_or_extract_auth_context", + new_callable=AsyncMock, + return_value=(uak, None, None, None, None, None, None), + ): + tools = await srv.handle_list_tools() + + assert {t.name for t in tools} == { + MCP_TOOL_SEARCH_TOOL_NAME, + MCP_TOOL_CALL_TOOL_NAME, + } + + +class TestMcpServerToolCallErrorHandling: + """The protocol tool-call handler must convert virtual-tool errors to an + isError CallToolResult instead of letting them raise out of the handler.""" + + @pytest.mark.asyncio + async def test_virtual_tool_error_returns_iserror_not_raised(self) -> None: + from fastapi import HTTPException + + from litellm.proxy._experimental.mcp_server import server as srv + + uak = UserAPIKeyAuth(api_key="k", object_permission=_make_perm(mcp_tool_search_enabled=True)) + with ( + patch( + "litellm.proxy._experimental.mcp_server.server.get_or_extract_auth_context", + new_callable=AsyncMock, + return_value=(uak, None, None, None, None, None, None), + ), + patch( + "litellm.proxy._experimental.mcp_server.server._dispatch_virtual_mcp_tool", + new_callable=AsyncMock, + side_effect=HTTPException(status_code=403, detail="User not allowed to call this tool"), + ), + ): + result = await srv.mcp_server_tool_call( + name=MCP_TOOL_CALL_TOOL_NAME, + arguments={"tool_name": "other-server-tool", "arguments": {}}, + ) + + assert result.isError is True + assert "User not allowed to call this tool" in result.content[0].text diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_oauth2_flow_backfill.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_oauth2_flow_backfill.py new file mode 100644 index 00000000000..c1239c228aa --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_oauth2_flow_backfill.py @@ -0,0 +1,234 @@ +""" +Tests for the startup oauth2_flow backfill. + +Legacy oauth2 rows with a null oauth2_flow are classified once, at rest, using +signals read-time inference never had (per-user token rows first), and the +result is persisted so the read path never infers again. The signal order is +the spec, and so is the refusal to stamp client_credentials: the M2M credential +shape is shared by DCR-registered interactive servers whose authorization +endpoint lives only in discovery, so ambiguous rows are left unstamped for a +human to assert rather than being permanently mislabeled M2M. +""" + +import base64 +import json +from types import SimpleNamespace +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from litellm.proxy._experimental.mcp_server.oauth2_flow_backfill import ( + backfill_null_oauth2_flows, + classify_null_flow_row, +) + + +def test_classify_per_user_tokens_beat_m2m_shape(): + """The DCR trap row: creds + token_url, no authorization_url, but a user has + signed in. Tokens are definitive; the M2M shape must not win.""" + flow, rule = classify_null_flow_row( + has_per_user_tokens=True, + authorization_url=None, + registration_url=None, + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + assert flow == "authorization_code" + assert rule == "per_user_tokens" + + +def test_classify_authorization_url_beats_m2m_shape(): + flow, rule = classify_null_flow_row( + has_per_user_tokens=False, + authorization_url="https://idp.example.com/authorize", + registration_url=None, + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + assert flow == "authorization_code" + assert rule == "authorization_url" + + +def test_classify_registration_url_beats_m2m_shape(): + """A registration endpoint means DCR, and DCR exists to mint interactive + clients; an abandoned-DCR row (no sign-in yet) must not be stamped M2M.""" + flow, rule = classify_null_flow_row( + has_per_user_tokens=False, + authorization_url=None, + registration_url="https://idp.example.com/register", + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + assert flow == "authorization_code" + assert rule == "registration_url" + + +def test_classify_m2m_shape_is_ambiguous_and_unstamped(): + """The M2M shape alone must never stamp client_credentials: a DCR-registered + interactive server that nobody signed into yet has the identical shape, and a + wrong M2M stamp would permanently route its per-user traffic through the + proxy's stored client credential.""" + flow, rule = classify_null_flow_row( + has_per_user_tokens=False, + authorization_url=None, + registration_url=None, + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + assert flow is None + assert rule == "ambiguous_m2m_shape" + + +def test_classify_partial_credentials_default_interactive(): + """token_url without a full credential pair is not the M2M shape.""" + flow, rule = classify_null_flow_row( + has_per_user_tokens=False, + authorization_url=None, + registration_url=None, + token_url="https://idp.example.com/token", + credentials={"client_id": "cid"}, + ) + assert flow == "authorization_code" + assert rule == "interactive_default" + + +def test_classify_bare_row_default_interactive(): + flow, rule = classify_null_flow_row( + has_per_user_tokens=False, + authorization_url=None, + registration_url=None, + token_url=None, + credentials=None, + ) + assert flow == "authorization_code" + assert rule == "interactive_default" + + +def _row(server_id, *, authorization_url=None, registration_url=None, token_url=None, credentials=None): + return SimpleNamespace( + server_id=server_id, + authorization_url=authorization_url, + registration_url=registration_url, + token_url=token_url, + credentials=credentials, + ) + + +def _oauth_token_row(server_id): + payload = json.dumps({"type": "oauth2", "access_token": "tok", "connected_at": "2026-07-01T00:00:00Z"}) + return SimpleNamespace( + server_id=server_id, + user_id="u1", + credential_b64=base64.urlsafe_b64encode(payload.encode()).decode(), + ) + + +def _byok_key_row(server_id): + return SimpleNamespace( + server_id=server_id, + user_id="u1", + credential_b64=base64.urlsafe_b64encode(b"sk-user-supplied-upstream-key").decode(), + ) + + +def _mock_prisma(null_rows, token_rows): + mock_prisma = MagicMock() + mock_prisma.db.litellm_mcpservertable.find_many = AsyncMock(return_value=null_rows) + mock_prisma.db.litellm_mcpservertable.update_many = AsyncMock(return_value=MagicMock()) + mock_prisma.db.litellm_mcpusercredentials.find_many = AsyncMock(return_value=token_rows) + return mock_prisma + + +@pytest.mark.asyncio +async def test_backfill_only_targets_null_flow_oauth2_rows(): + """The where clause is the guard that explicit and non-oauth2 rows are never touched.""" + mock_prisma = _mock_prisma([], []) + + counts = await backfill_null_oauth2_flows(mock_prisma) + + assert counts == {} + mock_prisma.db.litellm_mcpservertable.find_many.assert_awaited_once_with( + where={"auth_type": "oauth2", "oauth2_flow": None}, + ) + mock_prisma.db.litellm_mcpusercredentials.find_many.assert_not_awaited() + mock_prisma.db.litellm_mcpservertable.update_many.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_backfill_stamps_rows_and_reports_rule_counts(): + dcr_trap_row = _row( + "signed_in_dcr", + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + m2m_row = _row( + "legacy_m2m", + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + interactive_row = _row("legacy_interactive", authorization_url="https://idp.example.com/authorize") + + mock_prisma = _mock_prisma( + [dcr_trap_row, m2m_row, interactive_row], + [_oauth_token_row("signed_in_dcr")], + ) + + counts = await backfill_null_oauth2_flows(mock_prisma) + + assert counts == {"per_user_tokens": 1, "ambiguous_m2m_shape": 1, "authorization_url": 1} + + mock_prisma.db.litellm_mcpservertable.update_many.assert_awaited_once() + call = mock_prisma.db.litellm_mcpservertable.update_many.await_args + assert sorted(call.kwargs["where"]["server_id"]["in"]) == ["legacy_interactive", "signed_in_dcr"] + assert "oauth2_flow" in call.kwargs["where"] and call.kwargs["where"]["oauth2_flow"] is None + assert call.kwargs["data"] == {"oauth2_flow": "authorization_code", "updated_by": "oauth2_flow_backfill"} + + +@pytest.mark.asyncio +async def test_backfill_handles_json_string_credentials(): + """JSON-string credential blobs must decode: the M2M shape is recognized (and + therefore deliberately left unstamped) rather than misread as credential-less.""" + m2m_row = _row( + "json_creds_m2m", + token_url="https://idp.example.com/token", + credentials='{"client_id": "cid", "client_secret": "csecret"}', + ) + mock_prisma = _mock_prisma([m2m_row], []) + + counts = await backfill_null_oauth2_flows(mock_prisma) + + assert counts == {"ambiguous_m2m_shape": 1} + mock_prisma.db.litellm_mcpservertable.update_many.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_backfill_treats_undecodable_credentials_as_absent(): + row = _row( + "corrupt_creds", + token_url="https://idp.example.com/token", + credentials="not-json", + ) + mock_prisma = _mock_prisma([row], []) + + counts = await backfill_null_oauth2_flows(mock_prisma) + + assert counts == {"interactive_default": 1} + + +@pytest.mark.asyncio +async def test_backfill_byok_key_rows_are_not_sign_in_proof(): + """BYOK API keys live in the same table as per-user OAuth tokens; a bare key row + must not satisfy the per_user_tokens rule, or a BYOK-flavored M2M-shaped server + would be permanently stamped authorization_code. Only rows whose payload decodes + as a type oauth2 token count.""" + byok_shaped_row = _row( + "byok_m2m_shape", + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + mock_prisma = _mock_prisma([byok_shaped_row], [_byok_key_row("byok_m2m_shape")]) + + counts = await backfill_null_oauth2_flows(mock_prisma) + + assert counts == {"ambiguous_m2m_shape": 1} + mock_prisma.db.litellm_mcpservertable.update_many.assert_not_awaited() diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_oauth2_token_cache.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_oauth2_token_cache.py index a60dab9148d..7d2cf4442a5 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_oauth2_token_cache.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_oauth2_token_cache.py @@ -92,6 +92,36 @@ async def test_token_cached_across_calls(): assert mock_client.post.call_count == 1 +@pytest.mark.asyncio +async def test_m2m_token_not_shared_across_server_ids_with_identical_config(): + """Two servers with byte-identical client_credentials config but different server_ids must not + share a cached M2M token: the cache is keyed by server_id, so a new server entry (even one + recreated with the same URL and credentials) mints its own token instead of inheriting the + sibling's. Guards against the cache key ever collapsing to the URL or the client config.""" + cache = MCPOAuth2TokenCache() + server_a = _server(server_id="srv-a") + server_b = _server(server_id="srv-b") + mock_client = AsyncMock() + mock_client.post.side_effect = [_token_response("tok-for-a"), _token_response("tok-for-b")] + + with ( + patch( + "litellm.proxy._experimental.mcp_server.oauth2_token_cache.get_async_httpx_client", + return_value=mock_client, + ), + patch( + "litellm.proxy._experimental.mcp_server.oauth2_token_cache.mcp_oauth2_token_cache", + cache, + ), + ): + token_a = await resolve_mcp_auth(server_a) + token_b = await resolve_mcp_auth(server_b) + + assert token_a == "tok-for-a" + assert token_b == "tok-for-b" + assert mock_client.post.call_count == 2 + + @pytest.mark.asyncio async def test_per_request_header_beats_oauth2(): """An explicit mcp_auth_header takes priority over the OAuth2 token.""" diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py index 9e3862b43eb..99e05182361 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py @@ -1,6 +1,8 @@ +import asyncio import json +from datetime import datetime from typing import Any, Dict, Optional -from unittest.mock import MagicMock +from unittest.mock import AsyncMock, MagicMock import httpx import pytest @@ -35,10 +37,7 @@ def _build_request( body_bytes = body else: body_bytes = b"" - raw_headers = [ - (key.lower().encode("latin-1"), value.encode("latin-1")) - for key, value in headers.items() - ] + raw_headers = [(key.lower().encode("latin-1"), value.encode("latin-1")) for key, value in headers.items()] scope = { "type": "http", "http_version": "1.1", @@ -60,25 +59,18 @@ def _build_request( def _get_route(path: str, method: str): for route in rest_endpoints.router.routes: - if getattr(route, "path", None) == path and method in getattr( - route, "methods", set() - ): + if getattr(route, "path", None) == path and method in getattr(route, "methods", set()): return route raise AssertionError(f"Route {method} {path} not found") def _route_has_dependency(route, dependency) -> bool: - if any( - getattr(dep, "dependency", None) == dependency - for dep in getattr(route, "dependencies", []) - ): + if any(getattr(dep, "dependency", None) == dependency for dep in getattr(route, "dependencies", [])): return True dependant = getattr(route, "dependant", None) if dependant is None: return False - return any( - getattr(dep, "call", None) == dependency for dep in dependant.dependencies - ) + return any(getattr(dep, "call", None) == dependency for dep in dependant.dependencies) class TestExecuteWithMcpClient: @@ -102,9 +94,7 @@ class TestExecuteWithMcpClient: auth_type=MCPAuth.none, ) - result = await rest_endpoints._execute_with_mcp_client( - payload, failing_operation - ) + result = await rest_endpoints._execute_with_mcp_client(payload, failing_operation) assert result["status"] == "error" assert "stack_trace" not in result @@ -265,15 +255,10 @@ class TestExecuteWithMcpClient: assert result["status"] == "ok" # The incoming Authorization must be dropped — extra_headers should # contain no oauth2 headers (only static_headers, which are None here). - assert ( - captured["extra_headers"] is None - or "Authorization" not in captured["extra_headers"] - ) + assert captured["extra_headers"] is None or "Authorization" not in captured["extra_headers"] @pytest.mark.asyncio - async def test_interactive_oauth_resolves_forwarded_token_via_presented_store( - self, monkeypatch - ): + async def test_interactive_oauth_resolves_forwarded_token_via_presented_store(self, monkeypatch): """Interactive authorization_code preview (oauth2, no client credentials): the forwarded just-authorized token is resolved THROUGH the v2 resolver via a one-shot presented store (cred_provider), not the caller-override path. The bare token (Bearer stripped) is the @@ -330,7 +315,8 @@ class TestExecuteWithMcpClient: @pytest.mark.asyncio async def test_m2m_does_not_build_presented_store(self, monkeypatch): """M2M (client_credentials): to_server_spec returns None, so no presented provider is built; - the auto-fetch path is unchanged (no cred_provider, the incoming header dropped as before).""" + the auto-fetch path is unchanged (no cred_provider, the incoming header dropped as before). + """ captured: dict = {} def fake_build_stdio_env(server, raw_headers): @@ -377,7 +363,8 @@ class TestExecuteWithMcpClient: @pytest.mark.asyncio async def test_token_exchange_does_not_build_presented_store(self, monkeypatch): """OBO / token-exchange (auth_type oauth2_token_exchange, not oauth2): excluded by the - auth_type == oauth2 guard, so no presented provider is built and the v1 exchange path runs.""" + auth_type == oauth2 guard, so no presented provider is built and the v1 exchange path runs. + """ captured: dict = {} def fake_build_stdio_env(server, raw_headers): @@ -429,9 +416,7 @@ class TestExecuteWithMcpClient: return None async def fake_create_client(*args, **kwargs): - raise BaseExceptionGroup( - "test group", [RuntimeError("Cancelled via cancel scope")] - ) + raise BaseExceptionGroup("test group", [RuntimeError("Cancelled via cancel scope")]) monkeypatch.setattr( rest_endpoints.global_mcp_server_manager, @@ -493,9 +478,7 @@ class TestTestToolsList: "message": "Successfully retrieved tools", } - monkeypatch.setattr( - rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False - ) + monkeypatch.setattr(rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False) oauth_call_counter = {"count": 0} @@ -551,9 +534,7 @@ class TestTestToolsList: "message": "Successfully retrieved tools", } - monkeypatch.setattr( - rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False - ) + monkeypatch.setattr(rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False) oauth_headers = {"Authorization": "Bearer oauth"} oauth_call_counter = {"count": 0} @@ -569,7 +550,7 @@ class TestTestToolsList: raising=False, ) - request = _build_request({"authorization": "Bearer incoming"}) + request = _build_request({"authorization": "Bearer incoming", "x-litellm-api-key": "sk-admission"}) payload = NewMCPServerRequest( server_name="example", url="https://example.com", @@ -589,6 +570,101 @@ class TestTestToolsList: assert captured["oauth2_headers"] == oauth_headers assert oauth_call_counter["count"] == 1 + @pytest.mark.parametrize("auth_type", [MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) + async def test_extracts_oauth2_headers_for_client_forwarded_modes(self, monkeypatch, auth_type): + """The browser-only authorize flow sends the upstream token as Authorization; the preview + must thread it through for the client-forwarded token modes so the passthrough arm can + forward it, instead of probing the upstream unauthenticated.""" + + captured: dict = {} + + async def fake_execute( + request, + operation, + mcp_auth_header=None, + oauth2_headers=None, + raw_headers=None, + ): + captured["mcp_auth_header"] = mcp_auth_header + captured["oauth2_headers"] = oauth2_headers + return { + "tools": [], + "error": None, + "message": "Successfully retrieved tools", + } + + monkeypatch.setattr(rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False) + + oauth_headers = {"Authorization": "Bearer upstream-token"} + + monkeypatch.setattr( + auth_mcp.MCPRequestHandler, + "_get_oauth2_headers_from_headers", + staticmethod(lambda headers: oauth_headers), + raising=False, + ) + + request = _build_request({"authorization": "Bearer upstream-token", "x-litellm-api-key": "sk-admission"}) + payload = NewMCPServerRequest( + server_name="example", + url="https://example.com", + auth_type=auth_type, + ) + + from litellm.proxy._types import LitellmUserRoles + + result = await rest_endpoints.test_tools_list( + request, + payload, + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + + assert result["message"] == "Successfully retrieved tools" + assert captured["mcp_auth_header"] is None + assert captured["oauth2_headers"] == oauth_headers + + @pytest.mark.parametrize("auth_type", [MCPAuth.oauth2, MCPAuth.true_passthrough, MCPAuth.oauth_delegate]) + async def test_does_not_forward_authorization_that_satisfied_admission(self, monkeypatch, auth_type): + """Authorization is also the admission fallback: with no x-litellm-api-key on the request, + the Authorization value is the caller's LiteLLM key, so forwarding it would send the + admission credential to the upstream.""" + + captured: dict = {} + + async def fake_execute( + request, + operation, + mcp_auth_header=None, + oauth2_headers=None, + raw_headers=None, + ): + captured["oauth2_headers"] = oauth2_headers + return { + "tools": [], + "error": None, + "message": "Successfully retrieved tools", + } + + monkeypatch.setattr(rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False) + + request = _build_request({"authorization": "Bearer sk-litellm-admission-key"}) + payload = NewMCPServerRequest( + server_name="example", + url="https://example.com", + auth_type=auth_type, + ) + + from litellm.proxy._types import LitellmUserRoles + + result = await rest_endpoints.test_tools_list( + request, + payload, + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + + assert result["message"] == "Successfully retrieved tools" + assert captured["oauth2_headers"] is None + class TestListToolsRestAPI: pytestmark = pytest.mark.asyncio @@ -718,9 +794,7 @@ class TestListToolsRestAPI: stub_server = StubServer() captured = {} - async def fake_get_tools( - server, server_auth_header, *args, apply_tool_filters=True, **kwargs - ): + async def fake_get_tools(server, server_auth_header, *args, apply_tool_filters=True, **kwargs): captured["apply_tool_filters"] = apply_tool_filters return ["tool-1"] @@ -768,9 +842,7 @@ class TestListToolsRestAPI: assert captured["apply_tool_filters"] is True @pytest.mark.parametrize("upstream_status", [401, 403]) - async def test_upstream_auth_failure_surfaces_status_and_challenge( - self, monkeypatch, upstream_status - ): + async def test_upstream_auth_failure_surfaces_status_and_challenge(self, monkeypatch, upstream_status): """A single-server pass-through request whose upstream rejects the token must surface the upstream status (401 or 403) plus its WWW-Authenticate challenge, not collapse into a 200 ``unexpected_error`` body.""" @@ -839,6 +911,76 @@ class TestListToolsRestAPI: assert exc_info.value.status_code == upstream_status assert exc_info.value.headers == {"www-authenticate": challenge} + async def test_aggregate_list_absorbs_one_server_auth_failure(self, monkeypatch): + """The multi-server aggregate listing degrades a server whose upstream + rejects auth to an empty contribution and still returns the healthy + server's tools with a 200, rather than surfacing a 401.""" + from litellm.proxy._experimental.mcp_server.exceptions import ( + MCPUpstreamAuthError, + ) + + class StubServer: + def __init__(self, name): + self.alias = name + self.server_name = name + self.name = name + self.allowed_tools = None + self.mcp_info = {"server_name": name} + self.available_on_public_internet = True + + good = StubServer("good") + bad = StubServer("bad") + + async def fake_contexts(user_api_key_auth): + return [user_api_key_auth] + + async def fake_get_allowed_mcp_servers(*args, **kwargs): + return ["good", "bad"] + + async def fake_get_tools(server, *args, **kwargs): + if server.server_name == "bad": + raise MCPUpstreamAuthError( + status_code=401, + www_authenticate='Bearer realm="x"', + server_name="bad", + ) + return ["good-tool"] + + monkeypatch.setattr( + rest_endpoints, + "build_effective_auth_contexts", + fake_contexts, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_allowed_mcp_servers", + fake_get_allowed_mcp_servers, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_mcp_server_by_id", + lambda server_id: {"good": good, "bad": bad}.get(server_id), + raising=False, + ) + monkeypatch.setattr( + rest_endpoints, + "_get_tools_for_single_server", + fake_get_tools, + raising=False, + ) + + request = _build_request(path="/mcp-rest/tools/list", method="GET") + result = await rest_endpoints.list_tool_rest_api( + request, + server_id=None, + user_api_key_dict=UserAPIKeyAuth(), + ) + + assert result["tools"] == ["good-tool"] + assert result["error"] is None + async def test_name_resolution_finds_server_by_uuid(self, monkeypatch): """When server_id is a name string, it should be resolved to its UUID and used for the tools lookup when the UUID is in allowed_server_ids.""" @@ -976,6 +1118,292 @@ class TestListToolsRestAPI: assert result["error"] == "unexpected_error" assert "access_denied" in result["message"] + async def test_mcp_server_name_query_param_resolves_to_server(self, monkeypatch): + """mcp_server_name is a name-based alias for server_id: it should + resolve to the matching server and scope the response to it.""" + from litellm.proxy._experimental.mcp_server.server import MCPServer + from litellm.types.mcp import MCPTransport + + stub_server = MCPServer( + server_id="uuid-abc-123", + name="my-server", + transport=MCPTransport.sse, + ) + stub_server.alias = "my-server" + stub_server.server_name = "my-server" + stub_server.available_on_public_internet = True + stub_server.allowed_tools = None + stub_server.mcp_info = {"server_name": "my-server"} + + async def fake_contexts(user_api_key_auth): + return [user_api_key_auth] + + async def fake_get_allowed_mcp_servers(*args, **kwargs): + return ["uuid-abc-123"] + + captured = {"called": False, "server_arg": None} + + async def fake_get_tools( + server, + server_auth_header, + raw_headers=None, + user_api_key_auth=None, + extra_headers=None, + apply_tool_filters=True, + ): + captured["called"] = True + captured["server_arg"] = server + return ["tool-x"] + + monkeypatch.setattr( + rest_endpoints, + "build_effective_auth_contexts", + fake_contexts, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_allowed_mcp_servers", + fake_get_allowed_mcp_servers, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_mcp_server_by_name", + lambda name: stub_server if name == "my-server" else None, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_mcp_server_by_id", + lambda sid: stub_server if sid == "uuid-abc-123" else None, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints, + "_get_tools_for_single_server", + fake_get_tools, + raising=False, + ) + + request = _build_request(path="/mcp-rest/tools/list", method="GET") + result = await rest_endpoints.list_tool_rest_api( + request, + server_id=None, + mcp_server_name="my-server", + user_api_key_dict=UserAPIKeyAuth(), + ) + + assert captured["called"] is True + assert captured["server_arg"] is stub_server + assert result["tools"] == ["tool-x"] + assert result["error"] is None + + async def test_mcp_server_name_filter_uses_real_catalog_with_tool_search(self, monkeypatch): + from litellm.proxy._experimental.mcp_server.server import MCPServer + from litellm.proxy._types import LiteLLM_ObjectPermissionTable + from litellm.types.mcp import MCPTransport + + stub_server = MCPServer( + server_id="uuid-search-123", + name="search-server", + transport=MCPTransport.sse, + ) + stub_server.alias = "search-server" + stub_server.server_name = "search-server" + stub_server.available_on_public_internet = True + stub_server.allowed_tools = None + stub_server.mcp_info = {"server_name": "search-server"} + user_api_key_dict = UserAPIKeyAuth( + object_permission=LiteLLM_ObjectPermissionTable( + object_permission_id="search-scope", + mcp_tool_search_enabled=True, + mcp_servers=["uuid-search-123"], + ) + ) + + async def fake_contexts(user_api_key_auth): + return [user_api_key_auth] + + async def fake_get_allowed_mcp_servers(*args, **kwargs): + return ["uuid-search-123"] + + async def fake_get_tools( + server, + server_auth_header, + raw_headers=None, + user_api_key_auth=None, + extra_headers=None, + apply_tool_filters=True, + ): + return ["scoped-tool"] + + monkeypatch.setattr( + rest_endpoints, + "build_effective_auth_contexts", + fake_contexts, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_allowed_mcp_servers", + fake_get_allowed_mcp_servers, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_mcp_server_by_name", + lambda name: stub_server if name == "search-server" else None, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_mcp_server_by_id", + lambda server_id: stub_server if server_id == "uuid-search-123" else None, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints, + "_get_tools_for_single_server", + fake_get_tools, + raising=False, + ) + + request = _build_request(path="/mcp-rest/tools/list", method="GET") + result = await rest_endpoints.list_tool_rest_api( + request, + server_id=None, + mcp_server_name="search-server", + user_api_key_dict=user_api_key_dict, + ) + + assert result["tools"] == ["scoped-tool"] + assert result["error"] is None + + async def test_toolset_name_query_param_scopes_to_toolset_servers(self, monkeypatch): + """toolset_name should resolve the toolset, apply its scope to the + caller's UserAPIKeyAuth via _apply_toolset_scope, and only list tools + from servers the scoped auth is allowed to see.""" + from litellm.proxy._types import LiteLLM_ObjectPermissionTable + + scoped_auth = UserAPIKeyAuth( + object_permission=LiteLLM_ObjectPermissionTable( + object_permission_id="toolset-scope", + mcp_tool_search_enabled=True, + mcp_servers=["toolset-server-1"], + ) + ) + + class StubToolset: + toolset_id = "toolset-1" + + class StubServer: + alias = "toolset-server-1" + server_name = "toolset-server-1" + name = "toolset-server-1" + allowed_tools = None + mcp_info = {"server_name": "toolset-server-1"} + available_on_public_internet = True + + stub_server = StubServer() + + async def fake_get_toolset_by_name_cached(prisma_client, toolset_name): + assert toolset_name == "research_tools" + return StubToolset() + + async def fake_apply_toolset_scope(user_api_key_auth, toolset_id): + assert toolset_id == "toolset-1" + return scoped_auth + + async def fake_contexts(user_api_key_auth): + return [user_api_key_auth] + + async def fake_get_allowed_mcp_servers(**kwargs): + assert kwargs["user_api_key_auth"] is scoped_auth + return ["toolset-server-1"] + + async def fake_get_tools(server, server_auth_header, *args, **kwargs): + return ["toolset-tool-1"] + + monkeypatch.setattr( + "litellm.proxy.utils.get_prisma_client_or_throw", + lambda *args, **kwargs: MagicMock(), + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_toolset_by_name_cached", + fake_get_toolset_by_name_cached, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints, + "_apply_toolset_scope", + fake_apply_toolset_scope, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints, + "build_effective_auth_contexts", + fake_contexts, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_allowed_mcp_servers", + fake_get_allowed_mcp_servers, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_mcp_server_by_id", + lambda server_id: stub_server if server_id == "toolset-server-1" else None, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints, + "_get_tools_for_single_server", + fake_get_tools, + raising=False, + ) + + request = _build_request(path="/mcp-rest/tools/list", method="GET") + result = await rest_endpoints.list_tool_rest_api( + request, + server_id=None, + toolset_name="research_tools", + user_api_key_dict=UserAPIKeyAuth(), + ) + + assert result["tools"] == ["toolset-tool-1"] + assert result["error"] is None + + async def test_toolset_name_not_found_returns_error(self, monkeypatch): + async def fake_get_toolset_by_name_cached(prisma_client, toolset_name): + return None + + monkeypatch.setattr( + "litellm.proxy.utils.get_prisma_client_or_throw", + lambda *args, **kwargs: MagicMock(), + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_toolset_by_name_cached", + fake_get_toolset_by_name_cached, + raising=False, + ) + + request = _build_request(path="/mcp-rest/tools/list", method="GET") + with pytest.raises(HTTPException) as exc_info: + await rest_endpoints.list_tool_rest_api( + request, + server_id=None, + toolset_name="does-not-exist", + user_api_key_dict=UserAPIKeyAuth(), + ) + + assert exc_info.value.status_code == 404 + assert "does-not-exist" in str(exc_info.value.detail) + async def test_oauth2_user_token_injected_for_single_server(self, monkeypatch): """For a single-server OAuth2 request, _get_user_oauth_extra_headers is called and the returned headers are forwarded to _get_tools_for_single_server.""" @@ -1002,9 +1430,7 @@ class TestListToolsRestAPI: oauth_headers = {"Authorization": "Bearer user-oauth-token"} - async def fake_get_user_oauth_extra_headers( - server, user_api_key_dict, prefetched_creds=None - ): + async def fake_get_user_oauth_extra_headers(server, user_api_key_dict, prefetched_creds=None): return oauth_headers captured = {} @@ -1196,6 +1622,13 @@ class TestCallToolRestAPI: fake_execute_mcp_tool, raising=False, ) + fire_logging = AsyncMock(side_effect=RuntimeError("logging failed")) + monkeypatch.setattr( + rest_endpoints, + "_fire_mcp_tool_call_logging", + fire_logging, + raising=False, + ) request_payload = { "server_id": "server-1", @@ -1217,6 +1650,230 @@ class TestCallToolRestAPI: assert captured["name"] == "demo-tool" assert captured["arguments"] == {"foo": "bar"} assert captured["allowed_mcp_servers"] == [stub_server] + fire_logging.assert_awaited_once() + + @pytest.mark.parametrize("upstream_status", [401, 403]) + async def test_call_tool_rest_relays_upstream_auth_failure(self, monkeypatch, upstream_status): + """A pass-through call that hits an upstream 401/403 (surfaced by the manager as + MCPUpstreamAuthError) must reach the REST caller as that status with the upstream + WWW-Authenticate preserved, so an MCP client can run the upstream OAuth flow, instead of the + generic 500 the catch-all would otherwise produce.""" + from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError + + async def fake_contexts(user_api_key_auth): + return [user_api_key_auth] + + async def fake_get_allowed_mcp_servers(*args, **kwargs): + return ["server-1"] + + class StubServer: + server_id = "server-1" + alias = "server-1" + server_name = "server-1" + name = "stub" + allowed_tools = None + mcp_info = {"server_name": "stub"} + available_on_public_internet = True + auth_type = None + + stub_server = StubServer() + + async def fake_add_litellm_data_to_request(**kwargs): + return kwargs.get("data", {}) + + challenge = 'Bearer resource_metadata="https://gw.example.com/.well-known/oauth-protected-resource/mcp/stub"' + + async def fake_execute_mcp_tool(**kwargs): + raise MCPUpstreamAuthError( + status_code=upstream_status, + www_authenticate=challenge, + server_name="stub", + ) + + monkeypatch.setattr(rest_endpoints, "build_effective_auth_contexts", fake_contexts, raising=False) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_allowed_mcp_servers", + fake_get_allowed_mcp_servers, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_mcp_server_by_id", + lambda server_id: stub_server if server_id == "server-1" else None, + raising=False, + ) + monkeypatch.setattr( + "litellm.proxy.proxy_server.add_litellm_data_to_request", + fake_add_litellm_data_to_request, + raising=False, + ) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_config", {}, raising=False) + monkeypatch.setattr(rest_endpoints, "execute_mcp_tool", fake_execute_mcp_tool, raising=False) + + mock_logger = MagicMock() + monkeypatch.setattr(rest_endpoints, "verbose_logger", mock_logger, raising=False) + + request_payload = { + "server_id": "server-1", + "name": "demo-tool", + "arguments": {"foo": "bar"}, + } + request = _build_request( + path="/mcp-rest/tools/call", + method="POST", + json_body=request_payload, + ) + + with pytest.raises(HTTPException) as exc_info: + await rest_endpoints.call_tool_rest_api( + request, + user_api_key_dict=UserAPIKeyAuth(), + ) + + assert exc_info.value.status_code == upstream_status + assert exc_info.value.headers is not None + assert exc_info.value.headers.get("www-authenticate") == challenge + # The expected caller-must-reauth signal is logged once, at info, and never at error, so + # error-rate alerts do not fire on normal pass-through re-authentication. + error_messages = [str(c.args[0]) for c in mock_logger.error.call_args_list if c.args] + assert not any("MCP tool call" in m for m in error_messages) + info_messages = [str(c.args[0]) for c in mock_logger.info.call_args_list if c.args] + assert sum(str(upstream_status) in m for m in info_messages) == 1 + + async def test_local_permission_denial_keeps_error_level_logging(self, monkeypatch): + """Only the relayed upstream 401 may be demoted to info; a locally generated HTTPException 403 + (tool permission, server access, IP filtering) raised inside the call must stay at error level + so an authenticated user probing restrictions keeps full monitoring visibility, and must be + re-raised unchanged (not converted to a re-auth relay).""" + + async def fake_contexts(user_api_key_auth): + return [user_api_key_auth] + + async def fake_get_allowed_mcp_servers(*args, **kwargs): + return ["server-1"] + + class StubServer: + server_id = "server-1" + alias = "server-1" + server_name = "server-1" + name = "stub" + allowed_tools = None + mcp_info = {"server_name": "stub"} + available_on_public_internet = True + auth_type = None + + async def fake_add_litellm_data_to_request(**kwargs): + return kwargs.get("data", {}) + + async def fake_execute_mcp_tool(**kwargs): + raise HTTPException(status_code=403, detail="tool not allowed for key") + + monkeypatch.setattr(rest_endpoints, "build_effective_auth_contexts", fake_contexts, raising=False) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_allowed_mcp_servers", + fake_get_allowed_mcp_servers, + raising=False, + ) + monkeypatch.setattr( + rest_endpoints.global_mcp_server_manager, + "get_mcp_server_by_id", + lambda server_id: StubServer() if server_id == "server-1" else None, + raising=False, + ) + monkeypatch.setattr( + "litellm.proxy.proxy_server.add_litellm_data_to_request", fake_add_litellm_data_to_request, raising=False + ) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_config", {}, raising=False) + monkeypatch.setattr(rest_endpoints, "execute_mcp_tool", fake_execute_mcp_tool, raising=False) + mock_logger = MagicMock() + monkeypatch.setattr(rest_endpoints, "verbose_logger", mock_logger, raising=False) + + request = _build_request( + path="/mcp-rest/tools/call", + method="POST", + json_body={"server_id": "server-1", "name": "demo-tool", "arguments": {}}, + ) + + with pytest.raises(HTTPException) as exc_info: + await rest_endpoints.call_tool_rest_api(request, user_api_key_dict=UserAPIKeyAuth()) + + assert exc_info.value.status_code == 403 + error_messages = [str(c.args[0]) for c in mock_logger.error.call_args_list if c.args] + assert any("HTTPException in MCP tool call" in m for m in error_messages) + info_messages = [str(c.args[0]) for c in mock_logger.info.call_args_list if c.args] + assert not any("relaying upstream" in m for m in info_messages) + + async def test_success_logging_cancellation_propagates(self, monkeypatch): + fire_logging = AsyncMock(side_effect=asyncio.CancelledError()) + monkeypatch.setattr( + rest_endpoints, + "_fire_mcp_tool_call_logging", + fire_logging, + raising=False, + ) + + with pytest.raises(asyncio.CancelledError): + await rest_endpoints._safe_fire_mcp_tool_call_logging( + object(), {"result": "ok"}, datetime.now(), datetime.now() + ) + + fire_logging.assert_awaited_once() + + @pytest.mark.parametrize("upstream_status", [401, 403]) + async def test_virtual_mcp_tool_call_relays_upstream_auth_failure(self, monkeypatch, upstream_status): + """The virtual mcp_tool_call REST branch reaches execute_mcp_tool via handle_mcp_tool_call + without the direct branch's relay wrapper, so an MCPUpstreamAuthError from it must be relayed + by the endpoint-level handler (a real 401/403 + WWW-Authenticate) rather than falling through + the catch-all into a generic 500.""" + import litellm.proxy._experimental.mcp_server.tool_search as tool_search_mod + from litellm.proxy._experimental.mcp_server.exceptions import MCPUpstreamAuthError + from litellm.proxy._types import LiteLLM_ObjectPermissionTable + + challenge = 'Bearer resource_metadata="https://gw.example.com/.well-known/oauth-protected-resource"' + + async def fake_contexts(user_api_key_auth): + return [user_api_key_auth] + + async def fake_handle_mcp_tool_call(**kwargs): + raise MCPUpstreamAuthError(status_code=upstream_status, www_authenticate=challenge, server_name="stub") + + class _FakePreCall: + def __init__(self, data): + pass + + async def common_processing_pre_call_logic(self, **kwargs): + return None, MagicMock() + + monkeypatch.setattr(rest_endpoints, "build_effective_auth_contexts", fake_contexts, raising=False) + monkeypatch.setattr(tool_search_mod, "handle_mcp_tool_call", fake_handle_mcp_tool_call, raising=False) + monkeypatch.setattr( + "litellm.proxy.common_request_processing.ProxyBaseLLMRequestProcessing", + _FakePreCall, + raising=False, + ) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_config", {}, raising=False) + monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {}, raising=False) + + user_api_key_dict = UserAPIKeyAuth( + object_permission=LiteLLM_ObjectPermissionTable( + object_permission_id="search-scope", + mcp_tool_search_enabled=True, + ) + ) + request = _build_request( + path="/mcp-rest/tools/call", + method="POST", + json_body={"name": "mcp_tool_call", "arguments": {"tool_name": "x", "arguments": {}}}, + ) + + with pytest.raises(HTTPException) as exc_info: + await rest_endpoints.call_tool_rest_api(request, user_api_key_dict=user_api_key_dict) + + assert exc_info.value.status_code == upstream_status + assert exc_info.value.headers is not None + assert exc_info.value.headers.get("www-authenticate") == challenge class TestGetToolsForSingleServer: @@ -1224,9 +1881,7 @@ class TestGetToolsForSingleServer: pytestmark = pytest.mark.asyncio - async def test_filters_tools_by_object_permission_mcp_tool_permissions( - self, monkeypatch - ): + async def test_filters_tools_by_object_permission_mcp_tool_permissions(self, monkeypatch): """Test that tools are filtered by user_api_key_auth.object_permission.mcp_tool_permissions""" from litellm.proxy._experimental.mcp_server.server import MCPServer from litellm.proxy._types import LiteLLM_ObjectPermissionTable @@ -1389,9 +2044,7 @@ class TestGetToolsForSingleServer: # All tools should be returned assert len(result) == 2 - async def test_no_filtering_when_server_not_in_mcp_tool_permissions( - self, monkeypatch - ): + async def test_no_filtering_when_server_not_in_mcp_tool_permissions(self, monkeypatch): """Test that all tools are returned when server is not in mcp_tool_permissions""" from litellm.proxy._experimental.mcp_server.server import MCPServer from litellm.proxy._types import LiteLLM_ObjectPermissionTable @@ -1444,9 +2097,7 @@ class TestGetToolsForSingleServer: # All tools should be returned since server is not in permissions assert len(result) == 2 - async def test_combines_server_allowed_tools_and_object_permission_filters( - self, monkeypatch - ): + async def test_combines_server_allowed_tools_and_object_permission_filters(self, monkeypatch): """Test that both server.allowed_tools and object_permission.mcp_tool_permissions filters are applied""" from litellm.proxy._experimental.mcp_server.server import MCPServer from litellm.proxy._types import LiteLLM_ObjectPermissionTable @@ -1764,9 +2415,7 @@ class TestPreviewOpenAPITools: "paths": { "/repos/{owner}/{repo}/actions/jobs/{job_id}/logs": { "get": { - "operationId": ( - "actions/download-job-logs-for-workflow-run" - ), + "operationId": ("actions/download-job-logs-for-workflow-run"), "summary": "Download job logs", } }, @@ -1809,9 +2458,7 @@ class TestPreviewOpenAPITools: names = [t["name"] for t in result["tools"]] anthropic_re = re.compile(r"^[a-zA-Z0-9_-]{1,128}$") for name in names: - assert anthropic_re.match(name), ( - f"preview tool name {name!r} violates ^[a-zA-Z0-9_-]+$" - ) + assert anthropic_re.match(name), f"preview tool name {name!r} violates ^[a-zA-Z0-9_-]+$" assert "actions_download-job-logs-for-workflow-run" in names assert "pulls_list-files" in names @@ -1890,9 +2537,7 @@ class TestPreviewOpenAPITools: _StubRegistry(), ) - openapi_to_mcp_generator.register_tools_from_openapi( - spec, base_url="https://example.invalid" - ) + openapi_to_mcp_generator.register_tools_from_openapi(spec, base_url="https://example.invalid") assert preview_summary_to_name == registered_summary_to_name, ( f"preview {preview_summary_to_name} != " @@ -1920,15 +2565,11 @@ class TestConnectionErrorMessage: assert secret not in message def test_connect_error_points_at_reachability(self): - message = rest_endpoints._connection_error_message( - httpx.ConnectError("All connection attempts failed") - ) + message = rest_endpoints._connection_error_message(httpx.ConnectError("All connection attempts failed")) assert "unreachable" in message.lower() def test_timeout_error_message(self): - message = rest_endpoints._connection_error_message( - httpx.ConnectTimeout("timed out") - ) + message = rest_endpoints._connection_error_message(httpx.ConnectTimeout("timed out")) assert "unreachable" in message.lower() def test_http_status_error_includes_status_code(self): diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_semantic_tool_filter.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_semantic_tool_filter.py index cebc265a148..9bc0a525326 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_semantic_tool_filter.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_semantic_tool_filter.py @@ -773,6 +773,251 @@ async def test_semantic_filter_hook_responses_api_name_collision(): print("✅ Responses API tool with MCP-matching name correctly classified as native") +@pytest.mark.asyncio +async def test_semantic_filter_hook_filters_expanded_litellm_proxy_tools(): + """ + Regression test (LIT-4214): litellm_proxy MCP references must be + semantically filtered after expansion, with real filter stats. + + Given: A /v1/responses-style request whose tools are a single + {"type": "mcp", "server_url": "litellm_proxy"} reference that + expands to 5 flat OpenAI function dicts + When: The hook processes the request + Then: The expanded tools go through the semantic filter (top_k=2) + and litellm_semantic_filter_stats reports pre/post counts, so + the x-litellm-semantic-filter header shows how many tools + were filtered out instead of silently forwarding all tools + with no stats. + """ + from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + SemanticMCPToolFilter, + ) + from litellm.proxy.hooks.mcp_semantic_filter import SemanticToolFilterHook + from litellm.types.utils import Embedding, EmbeddingResponse + + mock_router = Mock() + + def mock_embedding_sync(*args, **kwargs): + return EmbeddingResponse( + data=[Embedding(embedding=[0.1] * 1536, index=0, object="embedding")], + model="text-embedding-3-small", + object="list", + usage={"prompt_tokens": 10, "total_tokens": 10}, + ) + + async def mock_embedding_async(*args, **kwargs): + return mock_embedding_sync() + + mock_router.embedding = mock_embedding_sync + mock_router.aembedding = mock_embedding_async + + filter_instance = SemanticMCPToolFilter( + embedding_model="text-embedding-3-small", + litellm_router_instance=mock_router, + top_k=2, + similarity_threshold=0.3, + enabled=True, + ) + + registry_tools = [ + MCPTool( + name=f"srv-tool_{i}", + description=f"Registry tool {i}", + inputSchema={"type": "object"}, + ) + for i in range(5) + ] + filter_instance._build_router(registry_tools) + + expanded_tools = [ + { + "type": "function", + "name": f"srv-tool_{i}", + "description": f"Registry tool {i}", + "parameters": {"type": "object", "properties": {}}, + } + for i in range(5) + ] + + hook = SemanticToolFilterHook(filter_instance) + hook._expand_mcp_tools = AsyncMock( # type: ignore[method-assign] + return_value=expanded_tools + ) + + data = { + "model": "gpt-4", + "input": [{"role": "user", "content": "Send an email", "type": "message"}], + "tools": [ + { + "type": "mcp", + "server_url": "litellm_proxy", + "require_approval": "never", + } + ], + "metadata": {}, + } + + result = await hook.async_pre_call_hook( + user_api_key_dict=Mock(), + cache=Mock(), + data=data, + call_type="aresponses", + ) + + assert result is not None, "Hook should return modified data" + filtered = result["tools"] + + assert len(filtered) <= 2, f"Expanded tools should be filtered to top_k=2, got {len(filtered)}" + assert len(filtered) < len(expanded_tools), ( + f"Hook must not forward all {len(expanded_tools)} expanded tools unfiltered, got {len(filtered)}" + ) + for tool in filtered: + assert tool in expanded_tools, "Filtered tools must be the original expanded tool dicts" + + assert ( + "litellm_semantic_filter_stats" in result["metadata"] + ), "Filter stats must be emitted for the litellm_proxy expansion path" + stats = result["metadata"]["litellm_semantic_filter_stats"] + total, selected = stats.split("->") + assert int(total) == 5, f"Stats 'from' should be pre-filter expanded count (5), got {total}" + assert int(selected) == len(filtered), f"Stats 'to' should match post-filter count, got {selected}" + + print(f"✅ Expanded litellm_proxy tools filtered: {len(expanded_tools)} -> {len(filtered)}, stats={stats}") + + +@pytest.mark.asyncio +async def test_semantic_filter_hook_filters_expanded_tools_with_string_input(): + """ + Responses API requests may pass ``input`` as a plain string; the + expanded-tool filtering must treat it as the user query instead of + crashing (which would silently disable MCP expansion). + """ + from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + SemanticMCPToolFilter, + ) + from litellm.proxy.hooks.mcp_semantic_filter import SemanticToolFilterHook + from litellm.types.utils import Embedding, EmbeddingResponse + + mock_router = Mock() + + def mock_embedding_sync(*args, **kwargs): + return EmbeddingResponse( + data=[Embedding(embedding=[0.1] * 1536, index=0, object="embedding")], + model="text-embedding-3-small", + object="list", + usage={"prompt_tokens": 10, "total_tokens": 10}, + ) + + async def mock_embedding_async(*args, **kwargs): + return mock_embedding_sync() + + mock_router.embedding = mock_embedding_sync + mock_router.aembedding = mock_embedding_async + + filter_instance = SemanticMCPToolFilter( + embedding_model="text-embedding-3-small", + litellm_router_instance=mock_router, + top_k=2, + similarity_threshold=0.3, + enabled=True, + ) + + registry_tools = [ + MCPTool( + name=f"srv-tool_{i}", + description=f"Registry tool {i}", + inputSchema={"type": "object"}, + ) + for i in range(5) + ] + filter_instance._build_router(registry_tools) + + expanded_tools = [ + { + "type": "function", + "name": f"srv-tool_{i}", + "description": f"Registry tool {i}", + "parameters": {"type": "object", "properties": {}}, + } + for i in range(5) + ] + + hook = SemanticToolFilterHook(filter_instance) + + filtered = await hook._filter_expanded_tools( + data={"input": "Send an email"}, + expanded_tools=expanded_tools, + ) + + assert len(filtered) <= 2, f"String input must still drive semantic filtering, got {len(filtered)} tools" + + print(f"✅ String input filtered expanded tools: {len(expanded_tools)} -> {len(filtered)}") + + +@pytest.mark.asyncio +async def test_semantic_filter_hook_expansion_skips_filter_when_disabled(): + """ + When the filter is disabled at runtime (e.g. via the UI toggle), the + expansion path must forward all expanded tools and emit NO filter + stats, mirroring the generic path's enabled guard. + """ + from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + SemanticMCPToolFilter, + ) + from litellm.proxy.hooks.mcp_semantic_filter import SemanticToolFilterHook + + filter_instance = SemanticMCPToolFilter( + embedding_model="text-embedding-3-small", + litellm_router_instance=Mock(), + top_k=2, + similarity_threshold=0.3, + enabled=False, + ) + + expanded_tools = [ + { + "type": "function", + "name": f"srv-tool_{i}", + "description": f"Registry tool {i}", + "parameters": {"type": "object", "properties": {}}, + } + for i in range(5) + ] + + hook = SemanticToolFilterHook(filter_instance) + hook._expand_mcp_tools = AsyncMock( # type: ignore[method-assign] + return_value=expanded_tools + ) + + data = { + "model": "gpt-4", + "input": [{"role": "user", "content": "Send an email", "type": "message"}], + "tools": [ + { + "type": "mcp", + "server_url": "litellm_proxy", + "require_approval": "never", + } + ], + "metadata": {}, + } + + result = await hook.async_pre_call_hook( + user_api_key_dict=Mock(), + cache=Mock(), + data=data, + call_type="aresponses", + ) + + assert result is not None, "Hook should still expand MCP references when the filter is disabled" + assert len(result["tools"]) == 5, f"All expanded tools must be forwarded when disabled, got {len(result['tools'])}" + assert ( + "litellm_semantic_filter_stats" not in result["metadata"] + ), "No filter stats may be emitted when the filter is disabled" + + print("✅ Disabled filter: expansion preserved, no spurious stats") + + @pytest.mark.asyncio async def test_semantic_filter_hook_preserves_tool_order(): """ @@ -1050,3 +1295,372 @@ class TestGetToolsByNames: ) assert matched == [] + + +@pytest.mark.asyncio +async def test_semantic_filter_headers_hook_emits_only_complete_tool_names(): + """ + Regression test for LIT-4215. + + The x-litellm-semantic-filter-tools header used to be sliced mid-name at + MAX_MCP_SEMANTIC_FILTER_TOOLS_HEADER_LENGTH with a "..." suffix, so the UI + rendered a chopped tool name as the last entry. The header must only ever + contain complete tool names, in their original order, within the cap. + """ + from litellm.constants import MAX_MCP_SEMANTIC_FILTER_TOOLS_HEADER_LENGTH + from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + SemanticMCPToolFilter, + ) + from litellm.proxy.hooks.mcp_semantic_filter import SemanticToolFilterHook + + filter_instance = SemanticMCPToolFilter( + embedding_model="text-embedding-3-small", + litellm_router_instance=Mock(), + top_k=10, + similarity_threshold=0.3, + enabled=True, + ) + hook = SemanticToolFilterHook(filter_instance) + + tool_names = [f"metrics_mcp-very_long_tool_name_for_header_{i:02d}" for i in range(8)] + data = { + "metadata": { + "litellm_semantic_filter_stats": "40->8", + "litellm_semantic_filter_tools": ",".join(tool_names), + } + } + + headers = await hook.async_post_call_response_headers_hook( + data=data, + user_api_key_dict=Mock(), + response=None, + ) + + assert headers is not None + assert headers["x-litellm-semantic-filter"] == "40->8" + + tools_header = headers["x-litellm-semantic-filter-tools"] + assert len(tools_header) <= MAX_MCP_SEMANTIC_FILTER_TOOLS_HEADER_LENGTH + emitted_names = tools_header.split(",") + assert emitted_names == tool_names[: len(emitted_names)] + assert 0 < len(emitted_names) < len(tool_names) + + +def test_truncate_csv_at_tool_name_boundary_edges(): + from litellm.proxy.hooks.mcp_semantic_filter.hook import ( + _truncate_csv_at_tool_name_boundary, + ) + + assert _truncate_csv_at_tool_name_boundary(tool_names_csv="a,b,c", max_length=150) == "a,b,c" + assert _truncate_csv_at_tool_name_boundary(tool_names_csv="abc,def", max_length=3) == "abc" + assert _truncate_csv_at_tool_name_boundary(tool_names_csv="ab,cd,ef", max_length=5) == "ab,cd" + assert _truncate_csv_at_tool_name_boundary(tool_names_csv="ab,cd,ef", max_length=4) == "ab" + assert _truncate_csv_at_tool_name_boundary(tool_names_csv="single_name_longer_than_cap", max_length=10) == "" + + +def _make_context_window_raising_router(state): + """ + Mock litellm Router whose embedding call raises ContextWindowExceededError + once state["raise_context_error"] is flipped to True. + """ + import litellm + from litellm.types.utils import Embedding, EmbeddingResponse + + def mock_embedding_sync(*args, **kwargs): + if state["raise_context_error"]: + raise litellm.ContextWindowExceededError( + message="Invalid 'input[0]': maximum input length is 8192 tokens.", + model="text-embedding-3-small", + llm_provider="openai", + ) + return EmbeddingResponse( + data=[Embedding(embedding=[0.1] * 1536, index=0, object="embedding")], + model="text-embedding-3-small", + object="list", + usage={"prompt_tokens": 10, "total_tokens": 10}, + ) + + async def mock_embedding_async(*args, **kwargs): + return mock_embedding_sync(*args, **kwargs) + + mock_router = Mock() + mock_router.embedding = mock_embedding_sync + mock_router.aembedding = mock_embedding_async + return mock_router + + +def _make_context_window_filter(state, top_k: int = 3): + from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + SemanticMCPToolFilter, + ) + + return SemanticMCPToolFilter( + embedding_model="text-embedding-3-small", + litellm_router_instance=_make_context_window_raising_router(state), + top_k=top_k, + similarity_threshold=0.3, + enabled=True, + ) + + +@pytest.mark.asyncio +async def test_semantic_filter_fails_closed_on_query_time_context_window_error(): + """ + Regression test (LIT-4284): a context-window overflow while embedding the + user query must fail closed with a typed error instead of silently + returning all tools (previously reported as N->N "success"). + """ + from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + SemanticToolFilterContextWindowError, + ) + + state = {"raise_context_error": False} + filter_instance = _make_context_window_filter(state) + + tools = [ + MCPTool(name=f"tool_{i}", description=f"Tool {i}", inputSchema={"type": "object"}) + for i in range(5) + ] + filter_instance._build_router(tools) + assert filter_instance.tool_router is not None + + state["raise_context_error"] = True + with pytest.raises(SemanticToolFilterContextWindowError) as exc_info: + await filter_instance.filter_tools(query="send an email", available_tools=tools) + + message = str(exc_info.value) + assert "context window" in message + assert "text-embedding-3-small" in message + print("✅ Query-time context window overflow fails closed") + + +@pytest.mark.asyncio +async def test_semantic_filter_records_build_time_context_window_error(): + """ + Regression test (LIT-4284): a context-window overflow while embedding the + tool descriptions at router-build time must be recorded (not raised out of + the build, which previously left the hook unregistered and filtering + silently disabled) and must fail subsequent filtering closed. + """ + from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + SemanticToolFilterContextWindowError, + ) + + state = {"raise_context_error": True} + filter_instance = _make_context_window_filter(state) + + tools = [ + MCPTool(name=f"tool_{i}", description=f"Tool {i}", inputSchema={"type": "object"}) + for i in range(5) + ] + filter_instance._build_router(tools) + + assert filter_instance.tool_router is None + assert filter_instance.context_window_error is not None + + with pytest.raises(SemanticToolFilterContextWindowError) as exc_info: + await filter_instance.filter_tools(query="send an email", available_tools=tools) + + message = str(exc_info.value) + assert "context window" in message + assert "tool descriptions" in message + print("✅ Build-time context window overflow is recorded and fails closed") + + +@pytest.mark.asyncio +async def test_semantic_filter_hook_fails_closed_on_context_window_error(): + """ + Regression test (LIT-4284): the pre-call hook must reject the request with + an actionable HTTP 400 when the embedding model overflows its context + window, instead of forwarding all tools and emitting an N->N success + header. + """ + from fastapi import HTTPException + + from litellm.proxy.hooks.mcp_semantic_filter import SemanticToolFilterHook + + state = {"raise_context_error": False} + filter_instance = _make_context_window_filter(state) + + tools = [ + MCPTool(name=f"tool_{i}", description=f"Tool {i}", inputSchema={"type": "object"}) + for i in range(5) + ] + filter_instance._build_router(tools) + hook = SemanticToolFilterHook(filter_instance) + + state["raise_context_error"] = True + data = { + "model": "gpt-4", + "messages": [{"role": "user", "content": "Send an email"}], + "tools": tools, + "metadata": {}, + } + + with pytest.raises(HTTPException) as exc_info: + await hook.async_pre_call_hook( + user_api_key_dict=Mock(), + cache=Mock(), + data=data, + call_type="completion", + ) + + assert exc_info.value.status_code == 400 + error_message = exc_info.value.detail["error"] + assert "context window" in error_message + assert "text-embedding-3-small" in error_message + assert "larger context window" in error_message + assert "maximum input length" not in error_message + print("✅ Hook fails closed with actionable 400 on context window overflow") + + +@pytest.mark.asyncio +async def test_semantic_filter_hook_fails_closed_on_expanded_tools_context_window_error(): + """ + Regression test (LIT-4284): the litellm_proxy MCP expansion path (driven + by the dashboard test panel via /v1/responses) must also fail closed with + an actionable HTTP 400 instead of being swallowed by the expansion + catch-all. + """ + from fastapi import HTTPException + + from litellm.proxy.hooks.mcp_semantic_filter import SemanticToolFilterHook + + state = {"raise_context_error": False} + filter_instance = _make_context_window_filter(state) + + registry_tools = [ + MCPTool(name=f"srv-tool_{i}", description=f"Registry tool {i}", inputSchema={"type": "object"}) + for i in range(5) + ] + filter_instance._build_router(registry_tools) + + expanded_tools = [ + { + "type": "function", + "name": f"srv-tool_{i}", + "description": f"Registry tool {i}", + "parameters": {"type": "object", "properties": {}}, + } + for i in range(5) + ] + + hook = SemanticToolFilterHook(filter_instance) + hook._expand_mcp_tools = AsyncMock( # type: ignore[method-assign] + return_value=expanded_tools + ) + + state["raise_context_error"] = True + data = { + "model": "gpt-4", + "input": [{"role": "user", "content": "Send an email", "type": "message"}], + "tools": [ + { + "type": "mcp", + "server_url": "litellm_proxy", + "require_approval": "never", + } + ], + "metadata": {}, + } + + with pytest.raises(HTTPException) as exc_info: + await hook.async_pre_call_hook( + user_api_key_dict=Mock(), + cache=Mock(), + data=data, + call_type="aresponses", + ) + + assert exc_info.value.status_code == 400 + error_message = exc_info.value.detail["error"] + assert "context window" in error_message + assert "larger context window" in error_message + assert "maximum input length" not in error_message + print("✅ Expansion path fails closed with actionable 400 on context window overflow") + + +@pytest.mark.asyncio +async def test_semantic_filter_hook_ignores_build_error_for_native_only_tools(): + """ + A recorded build-time context-window error must only block requests that + rely on MCP tool filtering; requests carrying only native tools pass + through untouched. + """ + from litellm.proxy.hooks.mcp_semantic_filter import SemanticToolFilterHook + + state = {"raise_context_error": True} + filter_instance = _make_context_window_filter(state) + + mcp_tools = [ + MCPTool(name=f"tool_{i}", description=f"Tool {i}", inputSchema={"type": "object"}) + for i in range(3) + ] + filter_instance._build_router(mcp_tools) + assert filter_instance.context_window_error is not None + + hook = SemanticToolFilterHook(filter_instance) + + native_tools = [ + { + "type": "function", + "function": {"name": "local_fn", "description": "A local function", "parameters": {}}, + } + ] + data = { + "model": "gpt-4", + "messages": [{"role": "user", "content": "Send an email"}], + "tools": native_tools, + "metadata": {}, + } + + result = await hook.async_pre_call_hook( + user_api_key_dict=Mock(), + cache=Mock(), + data=data, + call_type="completion", + ) + + assert result is not None + assert result["tools"] == native_tools + print("✅ Native-only requests pass through despite recorded build error") + + +def test_is_context_window_error_detection_variants(): + """ + _is_context_window_error must detect the overflow in every shape it + reaches filter_tools in: the raw typed exception, the encoder's + explicitly chained ValueError wrapper, an implicitly chained wrapper, + and a bare error whose message carries a known overflow phrase; a + generic error must not match. + """ + import litellm + from litellm.proxy._experimental.mcp_server.semantic_tool_filter import ( + _is_context_window_error, + ) + + cwe = litellm.ContextWindowExceededError( + message="Invalid 'input[0]': maximum input length is 8192 tokens.", + model="text-embedding-3-small", + llm_provider="openai", + ) + assert _is_context_window_error(cwe) + + try: + raise ValueError("Internal_litellm_router API call failed") from cwe + except ValueError as explicitly_chained: + assert _is_context_window_error(explicitly_chained) + + try: + try: + raise litellm.ContextWindowExceededError( + message="overflow", model="m", llm_provider="openai" + ) + except litellm.ContextWindowExceededError: + raise ValueError("wrapper without explicit chaining") + except ValueError as implicitly_chained: + assert _is_context_window_error(implicitly_chained) + + assert _is_context_window_error(ValueError("Invalid 'input[0]': maximum input length is 8192 tokens.")) + assert not _is_context_window_error(ValueError("A generic API error occurred.")) + assert not _is_context_window_error(None) diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_utils.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_utils.py new file mode 100644 index 00000000000..73fdee9cde3 --- /dev/null +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_utils.py @@ -0,0 +1,49 @@ +import pytest +from fastapi import HTTPException + +from litellm.proxy._experimental.mcp_server.utils import ( + validate_and_normalize_mcp_server_payload, + validate_tool_display_names, +) +from litellm.proxy._types import NewMCPServerRequest + + +class TestValidateToolDisplayNames: + def test_allows_none_and_empty(self): + validate_tool_display_names(None) + validate_tool_display_names({}) + + @pytest.mark.parametrize( + "display_name", + ["browse_repo_docs", "browse-repo-docs", "BrowseRepoDocs123"], + ) + def test_allows_bedrock_safe_names(self, display_name): + validate_tool_display_names({"read_wiki_structure": display_name}) + + @pytest.mark.parametrize( + "display_name", + ["Browse Repo Docs", "browse.repo.docs", "browse/repo", "browse@docs"], + ) + def test_rejects_names_bedrock_would_reject(self, display_name): + with pytest.raises(HTTPException) as exc_info: + validate_tool_display_names({"read_wiki_structure": display_name}) + assert exc_info.value.status_code == 400 + assert display_name in str(exc_info.value.detail) + + +class TestValidateAndNormalizeMcpServerPayload: + def test_rejects_invalid_tool_display_name_on_create(self): + payload = NewMCPServerRequest( + server_name="deepwiki_mcp", + tool_name_to_display_name={"read_wiki_structure": "Browse Repo Docs"}, + ) + with pytest.raises(HTTPException) as exc_info: + validate_and_normalize_mcp_server_payload(payload) + assert exc_info.value.status_code == 400 + + def test_accepts_valid_tool_display_name_on_create(self): + payload = NewMCPServerRequest( + server_name="deepwiki_mcp", + tool_name_to_display_name={"read_wiki_structure": "browse_repo_docs"}, + ) + validate_and_normalize_mcp_server_payload(payload) diff --git a/tests/test_litellm/proxy/anthropic_endpoints/test_endpoints.py b/tests/test_litellm/proxy/anthropic_endpoints/test_endpoints.py index a4da4587b7f..69d90a8b59b 100644 --- a/tests/test_litellm/proxy/anthropic_endpoints/test_endpoints.py +++ b/tests/test_litellm/proxy/anthropic_endpoints/test_endpoints.py @@ -58,15 +58,71 @@ class TestAnthropicEndpoints(unittest.TestCase): self.assertEqual(result, expected_result) # Assert safe_dumps was called for dictionary objects - mock_safe_dumps.assert_any_call( - {"type": "message_start", "message": {"id": "msg_123"}} + mock_safe_dumps.assert_any_call({"type": "message_start", "message": {"id": "msg_123"}}) + mock_safe_dumps.assert_any_call({"type": "content_block_delta", "delta": {"text": "more data"}}) + assert mock_safe_dumps.call_count == 2 # Called twice, once for each dict object + + +class TestBlockedResponseUsage: + """Blocked responses report the blocked LLM response's real usage.""" + + def test_uses_original_response_usage(self): + from litellm.proxy.anthropic_endpoints.endpoints import _blocked_response_usage + + # original_response is the AnthropicMessagesResponse the LLM produced + # before the guardrail blocked it; its usage is real. + original = {"usage": {"input_tokens": 31, "output_tokens": 9}} + assert _blocked_response_usage(original) == { + "input_tokens": 31, + "output_tokens": 9, + } + + def test_zero_usage_when_no_original_response(self): + from litellm.proxy.anthropic_endpoints.endpoints import _blocked_response_usage + + # Pre-call blocks never invoked the LLM -> nothing consumed. + assert _blocked_response_usage(None) == { + "input_tokens": 0, + "output_tokens": 0, + } + + @pytest.mark.asyncio + async def test_blocked_endpoint_response_carries_original_usage(self): + """The /v1/messages block handler reports the blocked response's real + usage, carried on ModifyResponseException.original_response.""" + from unittest.mock import AsyncMock, MagicMock + + import litellm.proxy.anthropic_endpoints.endpoints as ep + import litellm.proxy.proxy_server as proxy_server + from litellm.integrations.custom_guardrail import ModifyResponseException + + exc = ModifyResponseException( + message="blocked by guardrail", + model="claude-3-5-sonnet-20240620", + request_data={"messages": [{"role": "user", "content": "hi"}]}, + guardrail_name="rubrik", + original_response={"usage": {"input_tokens": 12, "output_tokens": 5}}, ) - mock_safe_dumps.assert_any_call( - {"type": "content_block_delta", "delta": {"text": "more data"}} - ) - assert ( - mock_safe_dumps.call_count == 2 - ) # Called twice, once for each dict object + + with ( + patch.object(ep, "_read_request_body", new=AsyncMock(return_value={})), + patch.object( + ep.ProxyBaseLLMRequestProcessing, + "base_process_llm_request", + new=AsyncMock(side_effect=exc), + ), + patch.object(proxy_server, "proxy_logging_obj") as mock_logging, + ): + mock_logging.post_call_failure_hook = AsyncMock() + response = await ep.anthropic_response( + fastapi_response=MagicMock(), + request=MagicMock(), + user_api_key_dict=MagicMock(), + ) + + assert response["content"][0]["text"] == "blocked by guardrail" + assert response["usage"] == {"input_tokens": 12, "output_tokens": 5} + mock_logging.post_call_failure_hook.assert_awaited_once() class TestEventLoggingBatchEndpoint: @@ -159,9 +215,7 @@ class TestStripTotalTokens(unittest.TestCase): # SimpleNamespace mimics the .usage attribute access pattern; the # helper's contract: if .usage is dict-shaped, strip total_tokens. - response = SimpleNamespace( - usage={"input_tokens": 100, "output_tokens": 50, "total_tokens": 150} - ) + response = SimpleNamespace(usage={"input_tokens": 100, "output_tokens": 50, "total_tokens": 150}) _strip_total_tokens_from_anthropic_response(response) assert "total_tokens" not in response.usage assert response.usage == {"input_tokens": 100, "output_tokens": 50} diff --git a/tests/test_litellm/proxy/auth/test_auth_checks.py b/tests/test_litellm/proxy/auth/test_auth_checks.py index 7e34cdf29bc..d12ff20ee5b 100644 --- a/tests/test_litellm/proxy/auth/test_auth_checks.py +++ b/tests/test_litellm/proxy/auth/test_auth_checks.py @@ -333,8 +333,10 @@ async def test_can_key_call_model_all_team_models_empty_team_models_is_unrestric @pytest.mark.asyncio -async def test_can_key_call_model_all_team_models_no_team_id_is_denied(): - """Key with all-team-models but no team_id cannot resolve the sentinel; access must be denied.""" +async def test_can_key_call_model_all_team_models_no_team_id_is_unrestricted(): + """A teamless key with all-team-models inherits the full proxy model list + (empty resolved list = unrestricted access), the same as leaving the models + field empty. This test will fail if someone re-introduces a teamless denial.""" from litellm.proxy._types import SpecialModelNames from litellm.proxy.auth.auth_checks import can_key_call_model @@ -344,15 +346,86 @@ async def test_can_key_call_model_all_team_models_no_team_id_is_denied(): team_models=[], ) - with pytest.raises(ProxyException) as exc_info: + assert ( await can_key_call_model( model="gpt-4o", llm_model_list=None, valid_token=valid_token, llm_router=None, ) + is True + ) - assert exc_info.value.type == ProxyErrorTypes.key_model_access_denied + +def test_resolve_key_models_teamless_all_team_models_returns_empty(): + """_resolve_key_models_for_auth_check must return [] for a teamless key + with all-team-models, making it equivalent to an unscoped key (unrestricted + access). Fails if someone returns the sentinel list for teamless keys.""" + from litellm.proxy._types import SpecialModelNames + from litellm.proxy.auth.auth_checks import _resolve_key_models_for_auth_check + + valid_token = UserAPIKeyAuth( + api_key="sk-orphan", + models=[SpecialModelNames.all_team_models.value], + team_models=[], + ) + + result = _resolve_key_models_for_auth_check(valid_token) + assert result == [], "teamless all-team-models must resolve to [] (unrestricted)" + + +@pytest.mark.asyncio +async def test_enforce_key_access_teamless_all_team_models_passes(): + """_enforce_key_and_fallback_model_access must not deny a teamless key with + all-team-models. The inference path skips the key-level model check when + the sentinel is present, regardless of team_id. Fails if someone adds a + team_id guard to the pass branch.""" + from litellm.proxy._types import SpecialModelNames + from litellm.proxy.auth.user_api_key_auth import _enforce_key_and_fallback_model_access + + valid_token = UserAPIKeyAuth( + api_key="sk-orphan", + models=[SpecialModelNames.all_team_models.value], + team_models=[], + ) + + await _enforce_key_and_fallback_model_access( + valid_token=valid_token, + request_data={"model": "gpt-4o"}, + route="/chat/completions", + request=None, + llm_model_list=None, + llm_router=None, + ) + + +@pytest.mark.asyncio +async def test_can_key_call_resolved_model_teamless_all_team_models_passes(): + """can_key_call_resolved_model must skip the key model check for a teamless + key with all-team-models. Fails if someone adds a team_id guard to the + skip_key_model_check condition.""" + from unittest.mock import AsyncMock, patch + + from litellm.proxy._types import SpecialModelNames + from litellm.proxy.auth.auth_checks import can_key_call_resolved_model + + valid_token = UserAPIKeyAuth( + api_key="sk-orphan", + models=[SpecialModelNames.all_team_models.value], + team_models=[], + ) + + with patch("litellm.proxy.auth.auth_checks.can_key_call_model", new_callable=AsyncMock) as mock_call: + with patch("litellm.proxy.proxy_server.prisma_client", None): + with patch("litellm.proxy.proxy_server.proxy_logging_obj", None): + with patch("litellm.proxy.proxy_server.user_api_key_cache", None): + await can_key_call_resolved_model( + model="gpt-4o", + llm_model_list=None, + valid_token=valid_token, + llm_router=None, + ) + mock_call.assert_not_awaited() @pytest.mark.asyncio @@ -2562,6 +2635,170 @@ async def test_virtual_key_budget_check_fallback_no_counter(): assert exc_info.value.current_cost == 15.0 +# ===================================================================== +# Throttle-on-budget-exceeded tests (LIT-3894): an over-budget key that +# opted in is throttled to a global % of its TPM/RPM instead of blocked. +# ===================================================================== + + +def _over_budget_token(**overrides) -> UserAPIKeyAuth: + base = dict( + token="throttle-token", + spend=20.0, + max_budget=10.0, + user_id="test-user", + ) + base.update(overrides) + return UserAPIKeyAuth(**base) + + +def _patched_spend(value: float): + async def mock_get_current_spend( + counter_key, fallback_spend, max_budget=None, **kwargs + ): + return value + + return patch("litellm.proxy.proxy_server.get_current_spend", mock_get_current_spend) + + +def _budget_logging_obj(): + from litellm.proxy.utils import ProxyLogging + + proxy_logging_obj = ProxyLogging(user_api_key_cache=None) + proxy_logging_obj.budget_alerts = AsyncMock() + return proxy_logging_obj + + +@pytest.mark.parametrize( + "limit, pct, expected", + [ + (1000, 0.1, 100), + (100, 0.1, 10), + (1, 0.1, 1), # floor would be 0; trickle of 1 keeps the key alive + (None, 0.1, None), + (50, 0.5, 25), + (1000, None, 1000), # no percentage -> limit unchanged + ], +) +def test_throttled_limit(limit, pct, expected): + from litellm.proxy.auth.budget_throttle import throttled_limit + + assert throttled_limit(limit, pct) == expected + + +@pytest.mark.asyncio +async def test_budget_exceeded_throttles_instead_of_blocking(monkeypatch): + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.1) + valid_token = _over_budget_token( + tpm_limit=1000, + rpm_limit=100, + metadata={"throttle_on_budget_exceeded": True}, + ) + + with _patched_spend(20.0): + await _virtual_key_max_budget_check( + valid_token=valid_token, + proxy_logging_obj=_budget_logging_obj(), + ) + + # persistent limits are untouched (so the throttle never compounds); the + # request-scoped percentage is what the rate limiter scales by + assert valid_token.budget_throttle_pct == 0.1 + assert valid_token.tpm_limit == 1000 + assert valid_token.rpm_limit == 100 + # the request-scoped decision must not leak into serialized responses + assert "budget_throttle_pct" not in valid_token.model_dump() + + +@pytest.mark.asyncio +async def test_budget_throttle_decision_cleared_before_caching(): + """The request-scoped throttle decision must not persist into the key cache, + otherwise it would re-apply (and compound) on every subsequent request.""" + from litellm.proxy.auth.auth_checks import _copy_user_api_key_auth_for_cache + + valid_token = _over_budget_token( + tpm_limit=1000, rpm_limit=100, metadata={"throttle_on_budget_exceeded": True} + ) + valid_token.budget_throttle_pct = 0.1 + + cached = _copy_user_api_key_auth_for_cache(user_api_key_obj=valid_token) + + assert cached.budget_throttle_pct is None + assert cached.tpm_limit == 1000 + assert cached.rpm_limit == 100 + + +@pytest.mark.asyncio +async def test_budget_exceeded_throttle_no_configured_limits(monkeypatch): + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.1) + valid_token = _over_budget_token(metadata={"throttle_on_budget_exceeded": True}) + assert valid_token.tpm_limit is None + assert valid_token.rpm_limit is None + + with _patched_spend(20.0): + with pytest.raises(litellm.BudgetExceededError): + await _virtual_key_max_budget_check( + valid_token=valid_token, + proxy_logging_obj=_budget_logging_obj(), + ) + + assert valid_token.budget_throttle_pct is None + + +@pytest.mark.asyncio +async def test_budget_exceeded_not_opted_in_still_blocks(monkeypatch): + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.1) + valid_token = _over_budget_token(tpm_limit=1000, rpm_limit=100) + + with _patched_spend(20.0): + with pytest.raises(litellm.BudgetExceededError): + await _virtual_key_max_budget_check( + valid_token=valid_token, + proxy_logging_obj=_budget_logging_obj(), + ) + + assert valid_token.budget_throttle_pct is None + + +@pytest.mark.parametrize("pct", [None, 0, 1.5, -0.1, True]) +@pytest.mark.asyncio +async def test_budget_exceeded_invalid_percentage_blocks(monkeypatch, pct): + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", pct) + valid_token = _over_budget_token( + tpm_limit=1000, + rpm_limit=100, + metadata={"throttle_on_budget_exceeded": True}, + ) + + with _patched_spend(20.0): + with pytest.raises(litellm.BudgetExceededError): + await _virtual_key_max_budget_check( + valid_token=valid_token, + proxy_logging_obj=_budget_logging_obj(), + ) + + assert valid_token.budget_throttle_pct is None + + +@pytest.mark.asyncio +async def test_under_budget_does_not_throttle(monkeypatch): + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.1) + valid_token = _over_budget_token( + max_budget=100.0, + tpm_limit=1000, + rpm_limit=100, + metadata={"throttle_on_budget_exceeded": True}, + ) + + with _patched_spend(5.0): + await _virtual_key_max_budget_check( + valid_token=valid_token, + proxy_logging_obj=_budget_logging_obj(), + ) + + assert valid_token.budget_throttle_pct is None + + @pytest.mark.asyncio async def test_team_budget_check_reads_from_spend_counter(): """Team budget check should use get_current_spend when counter exists.""" diff --git a/tests/test_litellm/proxy/auth/test_auth_hot_path_network_requests.py b/tests/test_litellm/proxy/auth/test_auth_hot_path_network_requests.py index f1ff001777c..22752f767ce 100644 --- a/tests/test_litellm/proxy/auth/test_auth_hot_path_network_requests.py +++ b/tests/test_litellm/proxy/auth/test_auth_hot_path_network_requests.py @@ -516,3 +516,104 @@ async def test_full_hot_path_network_count(): assert ( summary["total_network_requests"] == 4 ), f"Expected 4 total network requests on warm path, got {summary['total_network_requests']}" + + +# ============================================================================ +# TEST: negative caching for entities that do not exist in the DB +# ============================================================================ + + +@pytest.mark.asyncio +async def test_get_user_object_missing_user_negative_cache(): + """ + A user_id with no DB row (e.g. the master key's default admin user_id) + must not trigger a DB query on every request. The first lookup hits the + DB; repeat lookups inside the db_cache_expiry window are throttled. + """ + user_id = "user-missing-negative-cache" + + cache = DualCache(in_memory_cache=InMemoryCache()) + + mock_prisma = MagicMock() + mock_prisma.db = MagicMock() + mock_prisma.db.litellm_usertable = MagicMock() + mock_prisma.db.litellm_usertable.find_unique = AsyncMock(return_value=None) + + for _ in range(3): + with pytest.raises(ValueError): + await get_user_object( + user_id=user_id, + prisma_client=mock_prisma, + user_api_key_cache=cache, + parent_otel_span=None, + proxy_logging_obj=None, + user_id_upsert=False, + ) + + assert mock_prisma.db.litellm_usertable.find_unique.call_count == 1 + + +@pytest.mark.asyncio +async def test_get_user_object_missing_user_rechecks_after_expiry(): + """ + The negative cache must expire: a user created after a miss becomes + visible once the db_cache_expiry window has passed. + """ + from litellm.proxy.auth.auth_checks import db_cache_expiry, last_db_access_time + + user_id = "user-missing-expiry-recheck" + + cache = DualCache(in_memory_cache=InMemoryCache()) + + mock_prisma = MagicMock() + mock_prisma.db = MagicMock() + mock_prisma.db.litellm_usertable = MagicMock() + mock_prisma.db.litellm_usertable.find_unique = AsyncMock(return_value=None) + + with pytest.raises(ValueError): + await get_user_object( + user_id=user_id, + prisma_client=mock_prisma, + user_api_key_cache=cache, + parent_otel_span=None, + proxy_logging_obj=None, + user_id_upsert=False, + ) + assert mock_prisma.db.litellm_usertable.find_unique.call_count == 1 + + last_db_access_time[f"user_id:{user_id}"] = ( + None, + time.time() - (db_cache_expiry + 1), + ) + + with pytest.raises(ValueError): + await get_user_object( + user_id=user_id, + prisma_client=mock_prisma, + user_api_key_cache=cache, + parent_otel_span=None, + proxy_logging_obj=None, + user_id_upsert=False, + ) + assert mock_prisma.db.litellm_usertable.find_unique.call_count == 2 + + +def test_should_check_db_negative_entry_throttles_then_expires(): + """ + A recorded miss (value=None) suppresses DB checks inside the expiry + window and allows them again after it. Exercises the timestamp element + of the stored (value, time) tuple directly. + """ + from litellm.caching.dual_cache import LimitedSizeOrderedDict + from litellm.proxy.auth.auth_checks import ( + _should_check_db, + _update_last_db_access_time, + ) + + tracker: LimitedSizeOrderedDict = LimitedSizeOrderedDict(max_size=10) + + _update_last_db_access_time(key="k", value=None, last_db_access_time=tracker) + assert _should_check_db(key="k", last_db_access_time=tracker, db_cache_expiry=5) is False + + tracker["k"] = (None, time.time() - 6) + assert _should_check_db(key="k", last_db_access_time=tracker, db_cache_expiry=5) is True diff --git a/tests/test_litellm/proxy/auth/test_auth_utils.py b/tests/test_litellm/proxy/auth/test_auth_utils.py index cd8cf10d037..042fc107f40 100644 --- a/tests/test_litellm/proxy/auth/test_auth_utils.py +++ b/tests/test_litellm/proxy/auth/test_auth_utils.py @@ -19,6 +19,7 @@ from litellm.proxy.auth.auth_utils import ( get_key_mcp_rpm_limit, get_key_model_rpm_limit, get_key_model_tpm_limit, + get_key_tag_rpm_limit, get_model_from_request, get_project_model_rpm_limit, get_project_model_tpm_limit, @@ -1520,6 +1521,42 @@ class TestGetDynamicLitellmParamsClearsAdminConfigOnBaseOverride: assert "vertex_credentials" not in out assert "vertex_project" not in out + def test_clears_nvcf_function_id_on_base_override(self): + from litellm.router_utils.clientside_credential_handler import ( + get_dynamic_litellm_params, + ) + + admin_params = { + "model": "nvidia_riva/parakeet", + "api_base": "grpc.nvcf.nvidia.com:443", + "api_key": "nvapi-admin", + "nvcf_function_id": "admin-pinned-function", + } + out = get_dynamic_litellm_params( + litellm_params=dict(admin_params), + request_kwargs={"api_base": "self-hosted.example.com:50051"}, + ) + assert out["api_base"] == "self-hosted.example.com:50051" + assert "nvcf_function_id" not in out + + def test_clears_use_ssl_on_base_override(self): + from litellm.router_utils.clientside_credential_handler import ( + get_dynamic_litellm_params, + ) + + admin_params = { + "model": "nvidia_riva/parakeet", + "api_base": "grpc.nvcf.nvidia.com:443", + "api_key": "nvapi-admin", + "use_ssl": True, + } + out = get_dynamic_litellm_params( + litellm_params=dict(admin_params), + request_kwargs={"api_base": "self-hosted.example.com:50051"}, + ) + assert out["api_base"] == "self-hosted.example.com:50051" + assert "use_ssl" not in out + def test_caller_resupplied_value_overrides_admin_value_on_base_override(self): # When the caller redirects ``api_base`` and *also* supplies their # own value for one of the admin fields (e.g. ``organization``), @@ -1712,6 +1749,127 @@ class TestIsRequestBodySafeBlocksBedrockProjectOverride: ) +class TestIsRequestBodySafeBlocksNVCFFunctionOverride: + """``nvcf_function_id`` is rejected as a request-body param unless the + admin opted in proxy-wide or per-deployment.""" + + def test_nvcf_function_id_in_request_body_is_rejected(self): + with pytest.raises(ValueError, match="nvcf_function_id"): + is_request_body_safe( + request_body={ + "model": "nvidia_riva/parakeet", + "nvcf_function_id": "caller-supplied", + }, + general_settings={}, + llm_router=None, + model="nvidia_riva/parakeet", + ) + + def test_nvcf_function_id_with_api_key_still_rejected(self): + with pytest.raises(ValueError, match="nvcf_function_id"): + is_request_body_safe( + request_body={ + "model": "nvidia_riva/parakeet", + "api_key": "sk-anything", + "nvcf_function_id": "caller-supplied", + }, + general_settings={}, + llm_router=None, + model="nvidia_riva/parakeet", + ) + + def test_admin_opt_in_proxy_wide_allows_nvcf_function_id(self): + assert ( + is_request_body_safe( + request_body={ + "model": "nvidia_riva/parakeet", + "nvcf_function_id": "byok-function-id", + }, + general_settings={"allow_client_side_credentials": True}, + llm_router=None, + model="nvidia_riva/parakeet", + ) + is True + ) + + def test_admin_opt_in_per_deployment_allows_nvcf_function_id(self, monkeypatch): + """The error message lists per-deployment ``configurable_clientside_auth_params`` + as a second opt-in. Cover that path too so it can't silently regress.""" + from litellm.proxy.auth import auth_utils + + monkeypatch.setattr( + auth_utils, + "_allow_model_level_clientside_configurable_parameters", + lambda model, param, request_body_value, llm_router: param == "nvcf_function_id", + ) + + assert ( + is_request_body_safe( + request_body={ + "model": "nvidia_riva/parakeet", + "nvcf_function_id": "byok-function-id", + }, + general_settings={}, + llm_router=None, + model="nvidia_riva/parakeet", + ) + is True + ) + + +class TestIsRequestBodySafeBlocksRivaUseSsl: + """``use_ssl`` is rejected as a request-body param unless the admin + opted in proxy-wide or per-deployment.""" + + def test_use_ssl_in_request_body_is_rejected(self): + with pytest.raises(ValueError, match="use_ssl"): + is_request_body_safe( + request_body={ + "model": "nvidia_riva/parakeet", + "use_ssl": False, + }, + general_settings={}, + llm_router=None, + model="nvidia_riva/parakeet", + ) + + def test_admin_opt_in_proxy_wide_allows_use_ssl(self): + assert ( + is_request_body_safe( + request_body={ + "model": "nvidia_riva/parakeet", + "use_ssl": True, + }, + general_settings={"allow_client_side_credentials": True}, + llm_router=None, + model="nvidia_riva/parakeet", + ) + is True + ) + + def test_admin_opt_in_per_deployment_allows_use_ssl(self, monkeypatch): + from litellm.proxy.auth import auth_utils + + monkeypatch.setattr( + auth_utils, + "_allow_model_level_clientside_configurable_parameters", + lambda model, param, request_body_value, llm_router: param == "use_ssl", + ) + + assert ( + is_request_body_safe( + request_body={ + "model": "nvidia_riva/parakeet", + "use_ssl": True, + }, + general_settings={}, + llm_router=None, + model="nvidia_riva/parakeet", + ) + is True + ) + + # ── is_request_body_safe nested-config recursion (VERIA-6) ──────────────────── @@ -1748,6 +1906,22 @@ class TestIsRequestBodySafeNestedConfig: model="milvus-store", ) + def test_nested_nvcf_function_id_in_metadata_blocked(self): + """Smuggling ``nvcf_function_id`` via ``metadata`` / ``extra_body`` + is the same shape as the VERIA-6 ``api_base`` bypass — must be + rejected by the recursive walk so the NVCF override gate cannot + be sidestepped with nesting.""" + with pytest.raises(ValueError, match="nvcf_function_id"): + is_request_body_safe( + request_body={ + "model": "nvidia_riva/parakeet", + "litellm_metadata": {"nvcf_function_id": "attacker-via-metadata"}, + }, + general_settings={}, + llm_router=None, + model="nvidia_riva/parakeet", + ) + def test_nested_langfuse_host_in_embedding_config_blocked(self): """The recursion uses the *full* banned-param list, not a special subset — so any flag that's banned at the root is also banned @@ -1931,6 +2105,21 @@ class TestObservabilityCallbackBans: ) assert field in str(exc.value) + def test_observability_field_in_litellm_params_metadata_is_rejected(self): + with pytest.raises(ValueError) as exc: + is_request_body_safe( + request_body={ + "model": "gpt-4", + "litellm_params": { + "metadata": {"turn_off_message_logging": False} + }, + }, + general_settings={}, + llm_router=None, + model="gpt-4", + ) + assert "turn_off_message_logging" in str(exc.value) + @pytest.mark.parametrize( "metadata_key", ["metadata", "litellm_metadata"], @@ -2205,3 +2394,17 @@ class TestIsRequestBodySafeBlocksModelList: ) is True ) + + +class TestGetKeyTagRateLimits: + """Tests for get_key_tag_rpm_limit.""" + + def test_reads_tag_rpm_limit_from_metadata(self): + key = UserAPIKeyAuth( + api_key="sk-123", metadata={"tag_rpm_limit": {"cell-1": 5}} + ) + assert get_key_tag_rpm_limit(key) == {"cell-1": 5} + + def test_returns_none_when_unset(self): + key = UserAPIKeyAuth(api_key="sk-123") + assert get_key_tag_rpm_limit(key) is None diff --git a/tests/test_litellm/proxy/auth/test_handle_jwt.py b/tests/test_litellm/proxy/auth/test_handle_jwt.py index b547ec877e2..13041950f98 100644 --- a/tests/test_litellm/proxy/auth/test_handle_jwt.py +++ b/tests/test_litellm/proxy/auth/test_handle_jwt.py @@ -663,7 +663,11 @@ def test_get_all_jwt_team_ids_unions_singular_and_plural(): # singular field as multi-element list (some IdPs) — merge all, preserve plural-first order assert jwt_handler.get_all_jwt_team_ids( {"team_id": ["primary", "secondary"], "teams": ["a"]} - ) == ["a", "primary", "secondary"] + ) == [ + "a", + "primary", + "secondary", + ] # neither populated assert jwt_handler.get_all_jwt_team_ids({}) == [] @@ -1241,24 +1245,24 @@ async def test_auth_builder_returns_team_membership_object(): ) # Verify that team_membership_object is returned - assert ( - result["team_membership"] is not None - ), "team_membership should be present" - assert ( - result["team_membership"] == mock_team_membership - ), "team_membership should match the mock object" - assert ( - result["team_membership"].user_id == _user_id - ), "team_membership user_id should match" - assert ( - result["team_membership"].team_id == _team_id - ), "team_membership team_id should match" - assert ( - result["team_membership"].budget_id == "budget_123" - ), "team_membership budget_id should match" - assert ( - result["team_membership"].spend == 10.5 - ), "team_membership spend should match" + assert result["team_membership"] is not None, ( + "team_membership should be present" + ) + assert result["team_membership"] == mock_team_membership, ( + "team_membership should match the mock object" + ) + assert result["team_membership"].user_id == _user_id, ( + "team_membership user_id should match" + ) + assert result["team_membership"].team_id == _team_id, ( + "team_membership team_id should match" + ) + assert result["team_membership"].budget_id == "budget_123", ( + "team_membership budget_id should match" + ) + assert result["team_membership"].spend == 10.5, ( + "team_membership spend should match" + ) @pytest.mark.asyncio @@ -2717,9 +2721,9 @@ async def test_find_and_validate_specific_team_id_hints_bracket_notation(): error_msg = str(exc_info.value) # Should mention the bad field name and suggest the fix assert "roles.0" in error_msg, f"Expected field name in: {error_msg}" - assert ( - "roles" in error_msg and "list" in error_msg - ), f"Expected hint about using 'roles' instead: {error_msg}" + assert "roles" in error_msg and "list" in error_msg, ( + f"Expected hint about using 'roles' instead: {error_msg}" + ) @pytest.mark.asyncio @@ -2747,9 +2751,9 @@ async def test_find_and_validate_specific_team_id_hints_bracket_index_notation() error_msg = str(exc_info.value) assert "roles[0]" in error_msg, f"Expected field name in: {error_msg}" - assert ( - "roles" in error_msg and "list" in error_msg - ), f"Expected hint about using 'roles' instead: {error_msg}" + assert "roles" in error_msg and "list" in error_msg, ( + f"Expected hint about using 'roles' instead: {error_msg}" + ) @pytest.mark.asyncio @@ -3164,9 +3168,9 @@ def test_build_decode_kwargs_warns_once_when_unscoped( if "JWT auth is enabled" in r.getMessage() and "neither JWT_AUDIENCE nor JWT_ISSUER" in r.getMessage() ] - assert ( - len(matching) == 1 - ), f"Expected exactly one warning across 3 calls, got {len(matching)}" + assert len(matching) == 1, ( + f"Expected exactly one warning across 3 calls, got {len(matching)}" + ) def test_build_decode_kwargs_no_warning_when_scoped( @@ -4339,3 +4343,1599 @@ def test_build_decode_kwargs_warns_for_unscoped_global_fallback_in_mixed_deploym if "neither JWT_AUDIENCE nor JWT_ISSUER" in r.getMessage() ] assert len(matching) == 1 + + +# --------------------------------------------------------------------------- +# fallback_to_db_teams: resolve team from DB memberships when JWT has no team +# claims (config flag on LiteLLM_JWTAuth) +# --------------------------------------------------------------------------- + + +def test_get_team_id_from_header_defers_to_db_membership_only_without_jwt_claims(): + """With fallback_to_db_teams=True, an x-litellm-team-id header is accepted + provisionally only when the JWT carries no team claims (allowed set empty). + When the JWT does carry team claims, the header must still be validated + against them, and the flag-off behavior must keep rejecting unknown teams.""" + deferred = JWTAuthManager.get_team_id_from_header( + request_headers={"x-litellm-team-id": "team-from-db"}, + allowed_team_ids=set(), + fallback_to_db_teams=True, + ) + assert deferred == "team-from-db" + + with pytest.raises(HTTPException) as exc_info: + JWTAuthManager.get_team_id_from_header( + request_headers={"x-litellm-team-id": "team-x"}, + allowed_team_ids={"team-1", "team-2"}, + fallback_to_db_teams=True, + ) + assert exc_info.value.status_code == 403 + + with pytest.raises(HTTPException): + JWTAuthManager.get_team_id_from_header( + request_headers={"x-litellm-team-id": "team-from-db"}, + allowed_team_ids=set(), + fallback_to_db_teams=False, + ) + + +@pytest.mark.asyncio +async def test_find_team_with_model_access_defers_no_team_403_under_db_fallback(): + """find_team_with_model_access raises the early "no teams in token" 403 when + enforcement is on, but defers (returns no team) so auth_builder's DB fallback + can run when fallback_to_db_teams is enabled.""" + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth( + enforce_team_based_model_access=True, + fallback_to_db_teams=False, + ) + with pytest.raises(HTTPException) as exc_info: + await JWTAuthManager.find_team_with_model_access( + team_ids=set(), + requested_model="gpt-4", + route="/chat/completions", + jwt_handler=jwt_handler, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + assert exc_info.value.status_code == 403 + + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth( + enforce_team_based_model_access=True, + fallback_to_db_teams=True, + ) + team_id, team_object = await JWTAuthManager.find_team_with_model_access( + team_ids=set(), + requested_model="gpt-4", + route="/chat/completions", + jwt_handler=jwt_handler, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + assert team_id is None + assert team_object is None + + +def _db_fallback_handler(litellm_jwtauth: Optional[LiteLLM_JWTAuth] = None) -> JWTHandler: + handler = JWTHandler() + handler.litellm_jwtauth = litellm_jwtauth or LiteLLM_JWTAuth() + return handler + + +@pytest.mark.asyncio +async def test_resolve_db_team_fallback_skips_unresolvable_membership(): + """An orphaned membership (team row missing/erroring) is skipped and the next + resolvable DB team is selected instead of aborting the fallback.""" + user_object = LiteLLM_UserTable( + user_id="u_skip", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["ghost_team", "real_team"], + ) + resolved = LiteLLM_TeamTable(team_id="real_team") + + async def fake_get_team(team_id, **kwargs): + if team_id == "ghost_team": + raise HTTPException(status_code=404, detail="missing") + return resolved + + with patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ): + ( + team_id, + team_object, + _membership, + ) = await JWTAuthManager._resolve_db_team_fallback( + user_object=user_object, + user_id=None, + requested_model=None, + route="/chat/completions", + jwt_handler=_db_fallback_handler(), + enforce_team_based_model_access=True, + team_id_upsert=False, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + + assert team_id == "real_team" + assert team_object is resolved + + +@pytest.mark.parametrize( + ( + "fallback_to_db_teams", + "user_teams", + "header_team_id", + "expected_team_id", + "expect_403", + ), + [ + pytest.param( + True, ["team_solo"], None, "team_solo", False, id="flag_on_single_db_team" + ), + pytest.param( + True, + ["team_a", "team_b"], + None, + "team_a", + False, + id="flag_on_multi_db_team_picks_first", + ), + pytest.param( + True, + ["team_a", "team_b"], + "team_b", + "team_b", + False, + id="flag_on_header_team_in_membership", + ), + pytest.param( + True, + ["team_a", "team_b"], + "team_x", + None, + True, + id="flag_on_header_team_not_in_membership_403", + ), + pytest.param(True, [], None, None, True, id="flag_on_no_db_team_enforced_403"), + pytest.param( + False, + ["team_a", "team_b"], + None, + None, + False, + id="flag_off_multi_db_team_no_fallback", + ), + pytest.param( + False, + ["team_solo"], + None, + "team_solo", + False, + id="flag_off_single_db_team_upstream_fallback", + ), + ], +) +@pytest.mark.asyncio +async def test_auth_builder_db_team_fallback_when_jwt_has_no_team( + fallback_to_db_teams: bool, + user_teams: list, + header_team_id: Optional[str], + expected_team_id: Optional[str], + expect_403: bool, +) -> None: + """End-to-end auth_builder behavior with no JWT team claims. + + fallback_to_db_teams=True attributes usage to the user's first resolvable DB + team, honors a valid x-litellm-team-id header, and rejects a header team the + user does not belong to. The default (flag off) preserves the upstream + single-team fallback: a lone DB team is resolved, multiple are ambiguous. + """ + user_id = "u_db_fallback" + user_object = LiteLLM_UserTable( + user_id=user_id, + user_role=LitellmUserRoles.INTERNAL_USER, + teams=user_teams, + ) + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth( + enforce_team_based_model_access=True, + fallback_to_db_teams=fallback_to_db_teams, + ) + + request_headers = {"x-litellm-team-id": header_team_id} if header_team_id else None + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + async def call_auth_builder(): + with ( + patch.object( + jwt_handler, "auth_jwt", new_callable=AsyncMock + ) as mock_auth_jwt, + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=None), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=None), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object( + JWTAuthManager, + "find_and_validate_specific_team_id", + new_callable=AsyncMock, + return_value=(None, None), + ), + patch.object(JWTAuthManager, "get_all_team_ids", return_value=set()), + patch.object( + JWTAuthManager, + "find_team_with_model_access", + new_callable=AsyncMock, + return_value=(None, None), + ), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_membership", + new_callable=AsyncMock, + return_value=LiteLLM_TeamMembership( + user_id=user_id, + team_id=user_teams[0] if user_teams else "none", + litellm_budget_table=None, + ), + ), + ): + mock_auth_jwt.return_value = {"sub": user_id, "scope": ""} + return await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gpt-4"}, + general_settings={"enforce_rbac": False}, + route="/chat/completions", + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers=request_headers, + ) + + if expect_403: + with pytest.raises(HTTPException) as exc_info: + await call_auth_builder() + assert exc_info.value.status_code == 403 + else: + result = await call_auth_builder() + assert result["team_id"] == expected_team_id + + +@pytest.mark.parametrize( + "fallback_to_db_teams, expect_teams_stripped", + [ + pytest.param(True, False, id="fallback_on_preserves_db_teams"), + pytest.param(False, True, id="fallback_off_strips_db_teams"), + ], +) +@pytest.mark.asyncio +async def test_sync_user_role_and_teams_no_claim_team_preservation( + fallback_to_db_teams: bool, + expect_teams_stripped: bool, +) -> None: + """A no-team-claim JWT must not permanently strip a user's DB team memberships + when fallback_to_db_teams is enabled — otherwise the DB fallback that runs + right after has nothing to resolve and every request silently wipes the user + out of their teams. With the flag off, the legacy mirror-the-IdP behavior + (remove teams absent from the token) is preserved.""" + jwt_handler = JWTHandler() + jwt_handler.update_environment( + prisma_client=None, + user_api_key_cache=AsyncMock(), + litellm_jwtauth=LiteLLM_JWTAuth( + team_ids_jwt_field="teams", + sync_user_role_and_teams=True, + fallback_to_db_teams=fallback_to_db_teams, + ), + ) + + token = {"sub": "u1"} + user = LiteLLM_UserTable( + user_id="u1", + user_role=LitellmUserRoles.INTERNAL_USER.value, + teams=["team_a", "team_b"], + ) + prisma = AsyncMock() + + with patch( + "litellm.proxy.management_endpoints.scim.scim_v2.patch_team_membership", + new_callable=AsyncMock, + ) as mock_patch: + await JWTAuthManager.sync_user_role_and_teams(jwt_handler, token, user, prisma) + + if expect_teams_stripped: + mock_patch.assert_awaited_once() + assert set(mock_patch.call_args.kwargs["teams_ids_to_remove_user_from"]) == { + "team_a", + "team_b", + } + assert user.teams == [] + else: + mock_patch.assert_not_called() + assert user.teams == ["team_a", "team_b"] + + +@pytest.mark.asyncio +async def test_resolve_db_team_fallback_skips_team_without_model_access(): + """The DB-team fallback must apply the same per-team model-access check as the + claim-based path: a DB team that cannot access the requested model is skipped + in favor of one that can, instead of selecting the first membership blindly.""" + user_object = LiteLLM_UserTable( + user_id="u_model_access", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["restricted_team", "allowed_team"], + ) + teams = { + "restricted_team": LiteLLM_TeamTable( + team_id="restricted_team", models=["claude-3"] + ), + "allowed_team": LiteLLM_TeamTable(team_id="allowed_team", models=["gpt-4"]), + } + + async def fake_get_team(team_id, **kwargs): + return teams[team_id] + + async def fake_can_access(model, team_object, llm_router, team_model_aliases=None): + if model in (team_object.models or []): + return True + raise ProxyException( + message="team not allowed to access model", + type=ProxyErrorTypes.team_model_access_denied, + param="model", + code=403, + ) + + with ( + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.can_team_access_model", + new_callable=AsyncMock, + side_effect=fake_can_access, + ), + ): + ( + team_id, + team_object, + _membership, + ) = await JWTAuthManager._resolve_db_team_fallback( + user_object=user_object, + user_id=None, + requested_model="gpt-4", + route="/chat/completions", + jwt_handler=_db_fallback_handler(), + enforce_team_based_model_access=True, + team_id_upsert=False, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + + assert team_id == "allowed_team" + assert team_object is teams["allowed_team"] + + +@pytest.mark.asyncio +async def test_resolve_db_team_fallback_enforces_team_allowed_routes(): + """The DB-team fallback must apply the same team_allowed_routes gate as the + claim-based path: a route the JWT config excludes for team-role callers must + not become reachable by selecting a DB team, even when that team can access + the requested model. Without the gate, a teamless JWT could reach the + info/management routes an admin narrowed team_allowed_routes to exclude.""" + user_object = LiteLLM_UserTable( + user_id="u_routes", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["team_a"], + ) + team = LiteLLM_TeamTable(team_id="team_a", models=["gpt-4"]) + handler = _db_fallback_handler(LiteLLM_JWTAuth(team_allowed_routes=["openai_routes"])) + + async def fake_get_team(team_id, **kwargs): + return team + + async def fake_can_access(model, team_object, llm_router, team_model_aliases=None): + return True + + async def resolve(route): + return await JWTAuthManager._resolve_db_team_fallback( + user_object=user_object, + user_id=None, + requested_model="gpt-4", + route=route, + jwt_handler=handler, + enforce_team_based_model_access=False, + team_id_upsert=False, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + + with ( + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.can_team_access_model", + new_callable=AsyncMock, + side_effect=fake_can_access, + ), + ): + excluded_team_id, excluded_team_object, _ = await resolve("/key/info") + allowed_team_id, allowed_team_object, _ = await resolve("/chat/completions") + + assert excluded_team_id is None + assert excluded_team_object is None + assert allowed_team_id == "team_a" + assert allowed_team_object is team + + +def test_validate_header_team_in_db_membership_does_not_leak_team_ids(): + """The 403 raised for an x-litellm-team-id header outside the user's DB + memberships must not enumerate the user's team IDs back to the caller; any + valid-JWT caller could otherwise probe header values to discover team IDs.""" + user_object = LiteLLM_UserTable( + user_id="u_leak", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["secret_team_alpha", "secret_team_beta"], + ) + + with pytest.raises(HTTPException) as exc_info: + JWTAuthManager._validate_header_team_in_db_membership( + team_id="outsider_team", + user_object=user_object, + ) + + detail = exc_info.value.detail + assert exc_info.value.status_code == 403 + assert "secret_team_alpha" not in detail + assert "secret_team_beta" not in detail + assert "outsider_team" in detail + + +async def _run_auth_builder_with_header_team( + jwt_auth_config: LiteLLM_JWTAuth, + token: dict, + header_team_id: str, + user_object: LiteLLM_UserTable, + fake_get_team, + allowed_team_ids: set, +): + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = jwt_auth_config + with ( + patch.object( + jwt_handler, "auth_jwt", new_callable=AsyncMock, return_value=token + ), + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=None), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=None), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_object.user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object( + JWTAuthManager, "get_all_team_ids", return_value=allowed_team_ids + ), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_object.user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + ): + return await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gpt-4"}, + general_settings={"enforce_rbac": False}, + route="/chat/completions", + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers={"x-litellm-team-id": header_team_id}, + ) + + +async def _team_lookup_404(team_id, **kwargs): + raise HTTPException( + status_code=404, + detail=f"Team doesn't exist in db. Team={team_id}. Create team via `/team/new` call.", + ) + + +@pytest.mark.asyncio +async def test_auth_builder_header_team_not_found_matches_non_membership_denial() -> ( + None +): + """A provisional x-litellm-team-id naming a nonexistent team must produce + the exact same 403 shape as one naming an existing team outside the + caller's memberships. Letting get_team_object's 404 surface would give any + valid-JWT caller an oracle to probe which team ids exist.""" + user_object = LiteLLM_UserTable( + user_id="u_oracle", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["team_member"], + ) + config = LiteLLM_JWTAuth( + enforce_team_based_model_access=True, + fallback_to_db_teams=True, + ) + token = {"sub": "u_oracle", "scope": ""} + + async def team_exists(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + with pytest.raises(HTTPException) as missing_exc: + await _run_auth_builder_with_header_team( + config, token, "team_ghost", user_object, _team_lookup_404, set() + ) + with pytest.raises(HTTPException) as outsider_exc: + await _run_auth_builder_with_header_team( + config, token, "team_other", user_object, team_exists, set() + ) + + assert missing_exc.value.status_code == 403 + assert outsider_exc.value.status_code == 403 + assert missing_exc.value.detail.replace( + "team_ghost", "" + ) == outsider_exc.value.detail.replace("team_other", "") + assert "exist" not in missing_exc.value.detail + + +@pytest.mark.asyncio +async def test_auth_builder_claim_backed_header_team_lookup_error_propagates() -> None: + """When the JWT carries team claims the header team is not provisional, so + a failed team lookup keeps the upstream contract: get_team_object's 404 + surfaces unchanged instead of being rewritten into the membership 403.""" + user_object = LiteLLM_UserTable( + user_id="u_claimed", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["team_member"], + ) + config = LiteLLM_JWTAuth( + enforce_team_based_model_access=True, + fallback_to_db_teams=True, + team_ids_jwt_field="team_ids", + ) + token = {"sub": "u_claimed", "scope": "", "team_ids": ["team_claimed"]} + + with pytest.raises(HTTPException) as exc_info: + await _run_auth_builder_with_header_team( + config, token, "team_claimed", user_object, _team_lookup_404, {"team_claimed"} + ) + + assert exc_info.value.status_code == 404 + + +@pytest.mark.asyncio +async def test_resolve_db_team_fallback_loads_team_membership(): + """The DB-team fallback must load the resolved team's membership row (when a + user_id is known) so per-team membership budget limits are enforced on the + fallback path the same as on the claim-based path; returning a None membership + would silently skip LiteLLM_TeamMembership budget checks for every request.""" + user_object = LiteLLM_UserTable( + user_id="u_membership", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["team_with_budget"], + ) + membership = LiteLLM_TeamMembership( + user_id="u_membership", + team_id="team_with_budget", + budget_id="budget_xyz", + litellm_budget_table=None, + ) + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + async def fake_get_membership(user_id, team_id, **kwargs): + assert user_id == "u_membership" + assert team_id == "team_with_budget" + return membership + + with ( + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_membership", + new_callable=AsyncMock, + side_effect=fake_get_membership, + ), + ): + ( + team_id, + team_object, + team_membership, + ) = await JWTAuthManager._resolve_db_team_fallback( + user_object=user_object, + user_id="u_membership", + requested_model=None, + route="/chat/completions", + jwt_handler=_db_fallback_handler(), + enforce_team_based_model_access=True, + team_id_upsert=False, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + + assert team_id == "team_with_budget" + assert team_object is not None + assert team_membership is membership + assert team_membership.budget_id == "budget_xyz" + + +@pytest.mark.asyncio +async def test_resolve_db_team_fallback_survives_membership_lookup_error(): + """A transient membership-lookup failure must not deny an otherwise-authorized + request. get_team_membership swallows DB errors internally and returns None, so + the fallback must return the resolved team with a None membership (budget + enforcement degrades gracefully) instead of treating it as a denial.""" + user_object = LiteLLM_UserTable( + user_id="u_flaky", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["team_flaky"], + ) + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + async def none_on_db_error_membership(user_id, team_id, **kwargs): + return None + + with ( + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_membership", + new_callable=AsyncMock, + side_effect=none_on_db_error_membership, + ), + ): + ( + team_id, + team_object, + team_membership, + ) = await JWTAuthManager._resolve_db_team_fallback( + user_object=user_object, + user_id="u_flaky", + requested_model=None, + route="/chat/completions", + jwt_handler=_db_fallback_handler(), + enforce_team_based_model_access=True, + team_id_upsert=False, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + + assert team_id == "team_flaky" + assert team_object is not None + assert team_membership is None + + +@pytest.mark.asyncio +async def test_auth_builder_db_fallback_does_not_validate_rbac_team_against_db_membership(): + """When fallback_to_db_teams is on and the JWT carries an RBAC team role but no + group/team claims, team_id is set from the RBAC object_id (not the provisional + x-litellm-team-id header). That RBAC-asserted team must not be re-validated + against the user's DB memberships; only a team that actually came from the + header is provisional. Without the team_id == header_team_id guard, every such + RBAC request 403s when the RBAC team is not also a DB membership.""" + rbac_team = "rbac_asserted_team" + user_id = "u_rbac" + user_object = LiteLLM_UserTable( + user_id=user_id, + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["unrelated_db_team"], + ) + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth( + enforce_team_based_model_access=True, + fallback_to_db_teams=True, + ) + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + with ( + patch.object(jwt_handler, "auth_jwt", new_callable=AsyncMock) as mock_auth_jwt, + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=LitellmUserRoles.TEAM), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=rbac_team), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object(JWTAuthManager, "get_all_team_ids", return_value=set()), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + ): + mock_auth_jwt.return_value = {"sub": user_id, "scope": ""} + result = await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gpt-4"}, + general_settings={"enforce_rbac": False}, + route="/chat/completions", + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers=None, + ) + + assert result["team_id"] == rbac_team + + +@pytest.mark.asyncio +async def test_resolve_db_team_fallback_distinguishes_no_membership_vs_model_denied(): + """When enforce_team_based_model_access is on, a user with no DB memberships + and a user with memberships that all fail the model-access check must surface + different 403s; collapsing both into the no-membership message hides the real + cause and diverges from find_team_with_model_access's claim-based message.""" + membership_user = LiteLLM_UserTable( + user_id="u_no_model", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["only_team"], + ) + no_membership_user = LiteLLM_UserTable( + user_id="u_empty", + user_role=LitellmUserRoles.INTERNAL_USER, + teams=[], + ) + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id, models=["other"]) + + async def fake_can_access(model, team_object, llm_router, team_model_aliases=None): + raise ProxyException( + message="team not allowed to access model", + type=ProxyErrorTypes.team_model_access_denied, + param="model", + code=403, + ) + + with ( + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.can_team_access_model", + new_callable=AsyncMock, + side_effect=fake_can_access, + ), + ): + with pytest.raises(HTTPException) as model_denied: + await JWTAuthManager._resolve_db_team_fallback( + user_object=membership_user, + user_id=None, + requested_model="gpt-4", + route="/chat/completions", + jwt_handler=_db_fallback_handler(), + enforce_team_based_model_access=True, + team_id_upsert=False, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + + with pytest.raises(HTTPException) as no_member: + await JWTAuthManager._resolve_db_team_fallback( + user_object=no_membership_user, + user_id=None, + requested_model="gpt-4", + route="/chat/completions", + jwt_handler=_db_fallback_handler(), + enforce_team_based_model_access=True, + team_id_upsert=False, + prisma_client=None, + user_api_key_cache=MagicMock(), + parent_otel_span=None, + proxy_logging_obj=MagicMock(), + ) + + assert model_denied.value.status_code == 403 + assert "requested model" in model_denied.value.detail + assert "gpt-4" in model_denied.value.detail + assert "only_team" not in model_denied.value.detail + + assert no_member.value.status_code == 403 + assert "not a member of any team" in no_member.value.detail + + +@pytest.mark.asyncio +async def test_auth_builder_db_fallback_runs_when_only_team_id_default_set(): + """team_id_default makes JWTHandler.get_team_id return a non-None team for a + claimless token. The fallback gate must look at real JWT team claims (not the + operator-configured default) so fallback_to_db_teams still attributes to the + user's DB memberships instead of silently routing to the default team.""" + user_id = "u_default_token" + user_object = LiteLLM_UserTable( + user_id=user_id, + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["db_team_for_user"], + ) + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth( + fallback_to_db_teams=True, + team_id_default="config_default_team", + ) + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + with ( + patch.object(jwt_handler, "auth_jwt", new_callable=AsyncMock) as mock_auth_jwt, + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=None), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=None), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_membership", + new_callable=AsyncMock, + return_value=None, + ), + ): + mock_auth_jwt.return_value = {"sub": user_id, "scope": ""} + result = await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gpt-4"}, + general_settings={"enforce_rbac": False}, + route="/chat/completions", + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers=None, + ) + + assert result["team_id"] == "db_team_for_user" + + +@pytest.mark.asyncio +async def test_auth_builder_alias_only_token_resolves_alias_not_db_fallback(): + """An alias-only JWT (team_alias_jwt_field set, no team-id claims) must resolve + its alias via find_and_validate_specific_team_id, not fall into the DB-membership + fallback. get_all_jwt_team_ids ignores aliases, so without the get_team_alias + clause in the db_team_fallback gate the alias is silently dropped and the request + is mis-attributed to the user's first DB team instead of the alias-named team.""" + user_id = "u_alias_only" + user_object = LiteLLM_UserTable( + user_id=user_id, + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["db_membership_team"], + ) + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth( + fallback_to_db_teams=True, + team_alias_jwt_field="team_name", + ) + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + async def fake_get_team_by_alias(team_alias, **kwargs): + return LiteLLM_TeamTable(team_id="alias_resolved_team", team_alias=team_alias) + + with ( + patch.object(jwt_handler, "auth_jwt", new_callable=AsyncMock) as mock_auth_jwt, + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=None), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=None), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object_by_alias", + new_callable=AsyncMock, + side_effect=fake_get_team_by_alias, + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_membership", + new_callable=AsyncMock, + return_value=None, + ), + ): + mock_auth_jwt.return_value = {"sub": user_id, "team_name": "resolvable_alias"} + result = await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gpt-4"}, + general_settings={"enforce_rbac": False}, + route="/chat/completions", + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers=None, + ) + + assert result["team_id"] == "alias_resolved_team" + + +@pytest.mark.asyncio +async def test_find_and_validate_specific_team_id_alias_wins_over_team_id_default(): + """When the JWT carries only an alias claim (no team_id claim) and + team_id_default is configured, alias resolution must win. get_team_id + silently substitutes team_id_default for a missing claim, which would + otherwise mask the alias-resolved team and mis-attribute spend/access + to the configured default team.""" + from litellm.caching import DualCache + from litellm.proxy.utils import ProxyLogging + + jwt_handler = JWTHandler() + user_api_key_cache = DualCache() + proxy_logging_obj = ProxyLogging(user_api_key_cache=user_api_key_cache) + jwt_handler.update_environment( + prisma_client=None, + user_api_key_cache=user_api_key_cache, + litellm_jwtauth=LiteLLM_JWTAuth( + team_alias_jwt_field="team_alias", + team_id_default="config_default_team", + ), + ) + + jwt_token = {"sub": "user-1", "team_alias": "my-team"} + alias_team = LiteLLM_TeamTable( + team_id="alias_resolved_team", team_alias="my-team" + ) + + with ( + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", new_callable=AsyncMock + ) as mock_get_by_id, + patch( + "litellm.proxy.auth.handle_jwt.get_team_object_by_alias", + new_callable=AsyncMock, + ) as mock_get_by_alias, + ): + mock_get_by_alias.return_value = alias_team + + team_id, team_obj = await JWTAuthManager.find_and_validate_specific_team_id( + jwt_handler=jwt_handler, + jwt_valid_token=jwt_token, + prisma_client=None, + user_api_key_cache=user_api_key_cache, + parent_otel_span=None, + proxy_logging_obj=proxy_logging_obj, + ) + + assert team_id == "alias_resolved_team" + assert team_obj == alias_team + mock_get_by_id.assert_not_called() + mock_get_by_alias.assert_called_once() + + +@pytest.mark.asyncio +async def test_find_and_validate_specific_team_id_team_id_default_used_without_alias(): + """When the token carries neither a team_id nor an alias claim and + team_id_default is configured, the default still resolves the team. The + alias-precedence fix must not regress this baseline fallback behavior.""" + from litellm.caching import DualCache + from litellm.proxy.utils import ProxyLogging + + jwt_handler = JWTHandler() + user_api_key_cache = DualCache() + proxy_logging_obj = ProxyLogging(user_api_key_cache=user_api_key_cache) + jwt_handler.update_environment( + prisma_client=None, + user_api_key_cache=user_api_key_cache, + litellm_jwtauth=LiteLLM_JWTAuth( + team_alias_jwt_field="team_alias", + team_id_default="config_default_team", + ), + ) + + jwt_token = {"sub": "user-1"} + default_team = LiteLLM_TeamTable(team_id="config_default_team") + + with ( + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", new_callable=AsyncMock + ) as mock_get_by_id, + patch( + "litellm.proxy.auth.handle_jwt.get_team_object_by_alias", + new_callable=AsyncMock, + ) as mock_get_by_alias, + ): + mock_get_by_id.return_value = default_team + + team_id, team_obj = await JWTAuthManager.find_and_validate_specific_team_id( + jwt_handler=jwt_handler, + jwt_valid_token=jwt_token, + prisma_client=None, + user_api_key_cache=user_api_key_cache, + parent_otel_span=None, + proxy_logging_obj=proxy_logging_obj, + ) + + assert team_id == "config_default_team" + assert team_obj == default_team + mock_get_by_alias.assert_not_called() + + +@pytest.mark.asyncio +async def test_auth_builder_db_fallback_enforces_passthrough_route_access(): + """A team selected only via _resolve_db_team_fallback must still pass the + auth-enforced passthrough route check; previously the earlier gate ran while + team_id was None and the fallback-resolved team bypassed it entirely.""" + user_id = "u_passthrough" + user_object = LiteLLM_UserTable( + user_id=user_id, + user_role=LitellmUserRoles.INTERNAL_USER, + teams=["team_no_passthrough"], + ) + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth(fallback_to_db_teams=True) + + passthrough_route = "/vertex_ai/v1/projects/p/locations/us/publishers/google/models/gemini:generateContent" + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id, metadata={}) + + with ( + patch.object(jwt_handler, "auth_jwt", new_callable=AsyncMock) as mock_auth_jwt, + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=None), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=None), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_membership", + new_callable=AsyncMock, + return_value=None, + ), + patch( + "litellm.proxy.auth.handle_jwt.RouteChecks.is_auth_enforced_pass_through_route", + return_value=True, + ), + patch.object( + JWTAuthManager, + "_team_has_passthrough_route_access", + return_value=False, + ), + ): + mock_auth_jwt.return_value = {"sub": user_id, "scope": ""} + with pytest.raises(HTTPException) as exc_info: + await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gemini"}, + general_settings={"enforce_rbac": False}, + route=passthrough_route, + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers=None, + request_method="POST", + ) + + assert exc_info.value.status_code == 403 + assert "passthrough route" in exc_info.value.detail + + +@pytest.mark.asyncio +async def test_sync_user_role_and_teams_singular_claim_reconciles_memberships(): + """When fallback_to_db_teams is on but the JWT carries a singular team claim + (Okta/Auth0 default for users with one primary team), sync must treat it as a + real claim and reconcile DB memberships against it. Otherwise stale DB teams + persist and a subsequent claimless JWT for the same user is silently attributed + to a team the IdP never asserted on the singular-claim login.""" + jwt_handler = JWTHandler() + jwt_handler.update_environment( + prisma_client=None, + user_api_key_cache=AsyncMock(), + litellm_jwtauth=LiteLLM_JWTAuth( + team_id_jwt_field="primary_team", + sync_user_role_and_teams=True, + fallback_to_db_teams=True, + ), + ) + + token = {"sub": "u_singular", "primary_team": "team_primary"} + user = LiteLLM_UserTable( + user_id="u_singular", + user_role=LitellmUserRoles.INTERNAL_USER.value, + teams=["team_stale_a", "team_stale_b"], + ) + + with patch( + "litellm.proxy.management_endpoints.scim.scim_v2.patch_team_membership", + new_callable=AsyncMock, + ) as mock_patch: + await JWTAuthManager.sync_user_role_and_teams( + jwt_handler, token, user, AsyncMock() + ) + + mock_patch.assert_awaited_once() + assert set(mock_patch.call_args.kwargs["teams_ids_to_remove_user_from"]) == { + "team_stale_a", + "team_stale_b", + } + assert set(mock_patch.call_args.kwargs["teams_ids_to_add_user_to"]) == { + "team_primary" + } + assert user.teams == ["team_primary"] + + +@pytest.mark.asyncio +async def test_auth_builder_provisional_header_team_is_not_upserted(): + """A provisional x-litellm-team-id (accepted only because the JWT carries no + team claims) must not be upserted even when team_id_upsert is enabled: it is + validated against DB membership afterwards, so upserting first would let an + attacker-supplied header create an orphaned team row. A genuine membership + team already exists, so the resolved request still succeeds.""" + user_id = "u_no_upsert" + header_team = "header_supplied_team" + user_object = LiteLLM_UserTable( + user_id=user_id, + user_role=LitellmUserRoles.INTERNAL_USER, + teams=[header_team], + ) + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth( + fallback_to_db_teams=True, + team_id_upsert=True, + ) + + upsert_by_team: dict[str, Optional[bool]] = {} + + async def spy_get_team(team_id, **kwargs): + upsert_by_team[team_id] = kwargs.get("team_id_upsert") + return LiteLLM_TeamTable(team_id=team_id) + + with ( + patch.object(jwt_handler, "auth_jwt", new_callable=AsyncMock) as mock_auth_jwt, + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=None), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=None), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object(JWTAuthManager, "get_all_team_ids", return_value=set()), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=spy_get_team, + ), + ): + mock_auth_jwt.return_value = {"sub": user_id, "scope": ""} + result = await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gpt-4"}, + general_settings={"enforce_rbac": False}, + route="/chat/completions", + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers={"x-litellm-team-id": header_team}, + ) + + assert result["team_id"] == header_team + assert upsert_by_team[header_team] is False + + +@pytest.mark.asyncio +async def test_auth_builder_header_cannot_override_rbac_team_under_db_fallback(): + """An RBAC team-role JWT already pins team_id to the asserted team. With + fallback_to_db_teams on, a caller must not be able to substitute that team + by sending x-litellm-team-id for any other team they happen to belong to: + the provisional-header path is only for tokens with no team identity at all, + so an RBAC token plus a non-claim header team is rejected with 403.""" + user_id = "u_rbac_override" + rbac_team = "rbac_pinned_team" + other_team = "other_db_team" + user_object = LiteLLM_UserTable( + user_id=user_id, + user_role=LitellmUserRoles.INTERNAL_USER, + teams=[other_team], + ) + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth(fallback_to_db_teams=True) + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + with ( + patch.object(jwt_handler, "auth_jwt", new_callable=AsyncMock) as mock_auth_jwt, + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=LitellmUserRoles.TEAM), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=rbac_team), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object(JWTAuthManager, "get_all_team_ids", return_value=set()), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + ): + mock_auth_jwt.return_value = {"sub": user_id, "scope": ""} + with pytest.raises(HTTPException) as exc_info: + await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gpt-4"}, + general_settings={"enforce_rbac": False}, + route="/chat/completions", + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers={"x-litellm-team-id": other_team}, + ) + + assert exc_info.value.status_code == 403 + + +@pytest.mark.asyncio +async def test_auth_builder_header_team_enforces_team_allowed_routes_under_db_fallback(): + """A claimless JWT with x-litellm-team-id under fallback_to_db_teams must + obey the same team_allowed_routes gate as the auto-pick fallback path. + Otherwise the header bypasses the route gate the JWT config narrows for + team-role callers, letting management/info routes be reached with a + team_id the auto-pick path would silently refuse to set.""" + user_id = "u_header_routes" + header_team = "header_supplied_team" + user_object = LiteLLM_UserTable( + user_id=user_id, + user_role=LitellmUserRoles.INTERNAL_USER, + teams=[header_team], + ) + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth( + fallback_to_db_teams=True, + team_allowed_routes=["openai_routes"], + ) + + async def fake_get_team(team_id, **kwargs): + return LiteLLM_TeamTable(team_id=team_id) + + async def call(route: str): + with ( + patch.object( + jwt_handler, "auth_jwt", new_callable=AsyncMock + ) as mock_auth_jwt, + patch.object(JWTAuthManager, "check_rbac_role", new_callable=AsyncMock), + patch.object(jwt_handler, "get_rbac_role", return_value=None), + patch.object(jwt_handler, "get_scopes", return_value=[]), + patch.object(jwt_handler, "get_object_id", return_value=None), + patch.object( + JWTAuthManager, + "get_user_info", + new_callable=AsyncMock, + return_value=(user_id, "u@example.com", True), + ), + patch.object(jwt_handler, "get_org_id", return_value=None), + patch.object(jwt_handler, "get_end_user_id", return_value=None), + patch.object( + JWTAuthManager, + "check_admin_access", + new_callable=AsyncMock, + return_value=None, + ), + patch.object(JWTAuthManager, "get_all_team_ids", return_value=set()), + patch.object( + JWTAuthManager, + "get_objects", + new_callable=AsyncMock, + return_value=(user_object, None, None, None, user_id), + ), + patch.object(JWTAuthManager, "map_user_to_teams", new_callable=AsyncMock), + patch.object(JWTAuthManager, "validate_object_id", return_value=True), + patch.object( + JWTAuthManager, "sync_user_role_and_teams", new_callable=AsyncMock + ), + patch( + "litellm.proxy.auth.handle_jwt.get_team_object", + new_callable=AsyncMock, + side_effect=fake_get_team, + ), + ): + mock_auth_jwt.return_value = {"sub": user_id, "scope": ""} + return await JWTAuthManager.auth_builder( + api_key="test_jwt_token", + jwt_handler=jwt_handler, + request_data={"model": "gpt-4"}, + general_settings={"enforce_rbac": False}, + route=route, + prisma_client=None, + user_api_key_cache=None, + parent_otel_span=None, + proxy_logging_obj=None, + request_headers={"x-litellm-team-id": header_team}, + ) + + with pytest.raises(HTTPException) as exc_info: + await call("/key/info") + assert exc_info.value.status_code == 403 + assert "not allowed to access route" in exc_info.value.detail + assert "/key/info" in exc_info.value.detail + + result = await call("/chat/completions") + assert result["team_id"] == header_team + + +@pytest.mark.asyncio +async def test_sync_user_role_and_teams_singular_claim_only_recognized_under_flag(): + """Reading the singular team claim during sync is scoped to fallback_to_db_teams. + With the flag off, sync keeps the upstream plural-only reconciliation, so a + singular-only token is treated as claimless and existing DB teams are removed + exactly as before this PR; the new dual-claim behavior must not silently change + membership reconciliation for deployments that never opted in.""" + jwt_handler = JWTHandler() + jwt_handler.update_environment( + prisma_client=None, + user_api_key_cache=AsyncMock(), + litellm_jwtauth=LiteLLM_JWTAuth( + team_id_jwt_field="primary_team", + sync_user_role_and_teams=True, + fallback_to_db_teams=False, + ), + ) + + token = {"sub": "u_flag_off", "primary_team": "team_primary"} + user = LiteLLM_UserTable( + user_id="u_flag_off", + user_role=LitellmUserRoles.INTERNAL_USER.value, + teams=["team_existing"], + ) + + with patch( + "litellm.proxy.management_endpoints.scim.scim_v2.patch_team_membership", + new_callable=AsyncMock, + ) as mock_patch: + await JWTAuthManager.sync_user_role_and_teams( + jwt_handler, token, user, AsyncMock() + ) + + mock_patch.assert_awaited_once() + assert set(mock_patch.call_args.kwargs["teams_ids_to_remove_user_from"]) == { + "team_existing" + } + assert mock_patch.call_args.kwargs["teams_ids_to_add_user_to"] == [] + assert user.teams == [] diff --git a/tests/test_litellm/proxy/auth/test_model_checks.py b/tests/test_litellm/proxy/auth/test_model_checks.py index e30bfb15938..5d1cef87fba 100644 --- a/tests/test_litellm/proxy/auth/test_model_checks.py +++ b/tests/test_litellm/proxy/auth/test_model_checks.py @@ -620,6 +620,29 @@ def test_get_team_models_all_team_models_expands_with_access_groups(): assert "group-2" in result +def test_get_key_models_teamless_all_team_models_returns_unrestricted(): + """Teamless key with all-team-models must resolve the same as leaving the + models field empty ([] = unrestricted). The sentinel must not leak into + the returned list. Fails if someone adds a team_id guard to the sentinel + expansion in get_key_models.""" + from litellm.proxy._types import SpecialModelNames + from litellm.proxy.auth.model_checks import get_key_models + + user_api_key_dict = type( + "obj", + (object,), + { + "models": [SpecialModelNames.all_team_models.value], + "team_id": None, + "team_models": [], + }, + )() + proxy_model_list = ["gpt-4o", "claude-sonnet-4-20250514"] + result = get_key_models(user_api_key_dict, proxy_model_list, {}) + assert SpecialModelNames.all_team_models.value not in result + assert result == [], "should return [] (unrestricted), same as an unscoped key" + + def test_expand_wildcard_deployments_non_wildcard_passthrough(): """Non-wildcard deployments must be returned unchanged.""" from litellm.proxy.auth.model_checks import ( diff --git a/tests/test_litellm/proxy/auth/test_route_checks.py b/tests/test_litellm/proxy/auth/test_route_checks.py index d623149ff6a..a6d4dc63697 100644 --- a/tests/test_litellm/proxy/auth/test_route_checks.py +++ b/tests/test_litellm/proxy/auth/test_route_checks.py @@ -2595,6 +2595,293 @@ def test_org_admin_of_multiple_orgs_can_operate_on_both(): assert _user_is_org_admin({"organizations": ["org-A", "org-B"]}, user_obj) is True +# ── LIT-4221: /team/update org-context resolution from team_id ──────────────── +from litellm.proxy.auth.auth_checks_organization import ( + add_team_org_context_to_request_body, +) + + +@pytest.mark.asyncio +async def test_add_team_org_context_resolves_org_from_team(): + """For /team/update with only team_id, the target team's org is resolved and + injected so the org-admin route gate can see it. This is what lets an org + admin update a team budget from the Hub UI, which sends team_id, not + organization_id (LIT-4221).""" + + async def fetch(team_id: str): + assert team_id == "team-1" + return "org-1" + + out = await add_team_org_context_to_request_body( + route="/team/update", + request_body={"team_id": "team-1", "max_budget": 42}, + fetch_team_org_id=fetch, + ) + assert out == {"team_id": "team-1", "max_budget": 42, "organization_id": "org-1"} + + +@pytest.mark.asyncio +async def test_add_team_org_context_noop_when_org_id_already_present(): + """If the caller already passed organization_id, no lookup happens and the + body is returned unchanged.""" + + async def fetch(team_id: str): + raise AssertionError("must not resolve when organization_id is present") + + body = {"team_id": "team-1", "organization_id": "org-explicit"} + out = await add_team_org_context_to_request_body( + route="/team/update", request_body=body, fetch_team_org_id=fetch + ) + assert out == body + + +@pytest.mark.asyncio +async def test_add_team_org_context_noop_for_other_routes(): + """Only /team/update opts into org resolution; other routes are untouched.""" + + async def fetch(team_id: str): + raise AssertionError("must not resolve for a non-opted-in route") + + body = {"team_id": "team-1"} + out = await add_team_org_context_to_request_body( + route="/team/delete", request_body=body, fetch_team_org_id=fetch + ) + assert out == body + + +@pytest.mark.asyncio +async def test_add_team_org_context_noop_when_team_has_no_org(): + """A standalone team (no org) resolves to None, so nothing is injected and + the org-admin branch stays unreachable (no blanket access).""" + + async def fetch(team_id: str): + return None + + body = {"team_id": "team-1"} + out = await add_team_org_context_to_request_body( + route="/team/update", request_body=body, fetch_team_org_id=fetch + ) + assert out == body + + +def test_team_update_gate_allows_org_admin_with_resolved_org(): + """Post-resolution (organization_id present), an org admin of that org clears + the gate for /team/update.""" + user_obj = _make_org_admin_user("org-1") + valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + request = MagicMock(spec=Request) + request.method = "POST" + request.query_params = {} + + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/update", + request=request, + valid_token=valid_token, + request_data={"team_id": "team-1", "organization_id": "org-1"}, + ) + + +def test_team_update_gate_rejects_without_org_context(): + """Without organization_id (i.e. resolution found no org, or a non-org-admin), + the gate still rejects /team/update — the fix adds no blanket allow. Guards + against re-widening the route (e.g. dropping it into self_managed_routes).""" + user_obj = _make_org_admin_user("org-1") + valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + request = MagicMock(spec=Request) + request.method = "POST" + request.query_params = {} + + with pytest.raises(Exception): + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/update", + request=request, + valid_token=valid_token, + request_data={"team_id": "team-1", "max_budget": 42}, + ) + + +def test_team_update_gate_rejects_cross_org_admin_with_resolved_org(): + """Even after the target team's org is resolved, an org admin of a DIFFERENT + org is rejected at the gate (no cross-org escalation).""" + user_obj = _make_org_admin_user("org-1") + valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + request = MagicMock(spec=Request) + request.method = "POST" + request.query_params = {} + + with pytest.raises(Exception): + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/update", + request=request, + valid_token=valid_token, + request_data={"team_id": "team-1", "organization_id": "org-2"}, + ) + + +# ── PATCH /team/{team_id}: same org-context + role reach as POST /team/update ── + + +@pytest.mark.asyncio +async def test_add_team_org_context_resolves_org_from_path_for_patch_route(): + """PATCH /team/{team_id} carries team_id in the PATH, not the body. The target + team's org is resolved from the last path segment (identified by the route + template) and injected, so an org admin of that team's org clears the same gate + they clear for POST /team/update.""" + + async def fetch(team_id: str): + assert team_id == "team-1" + return "org-1" + + out = await add_team_org_context_to_request_body( + route="/team/team-1", + request_body={"metadata": {"cost_center": "x"}}, + fetch_team_org_id=fetch, + route_template="/team/{team_id}", + ) + assert out == {"metadata": {"cost_center": "x"}, "organization_id": "org-1"} + + +@pytest.mark.asyncio +async def test_add_team_org_context_path_noop_for_team_subresource(): + """A sub-resource like /team/{team_id}/members/me has a different route template, + so it is not mistaken for the bare team route and no org is injected.""" + + async def fetch(team_id: str): + raise AssertionError("must not resolve for a team sub-resource route") + + body = {"foo": "bar"} + out = await add_team_org_context_to_request_body( + route="/team/team-1/members/me", + request_body=body, + fetch_team_org_id=fetch, + route_template="/team/{team_id}/members/me", + ) + assert out == body + + +@pytest.mark.asyncio +async def test_add_team_org_context_noop_for_static_team_route(): + """A static sibling route (e.g. POST /team/new) whose resolved path also has the + single-segment shape has its own template, not /team/{team_id}, so no team lookup + is attempted — the guard against a spurious DB hit on every /team/ call.""" + + async def fetch(team_id: str): + raise AssertionError("must not resolve for a static /team/ route") + + body = {"team_alias": "new team"} + out = await add_team_org_context_to_request_body( + route="/team/new", + request_body=body, + fetch_team_org_id=fetch, + route_template="/team/new", + ) + assert out == body + + +def test_patch_team_route_has_same_reach_as_team_update(): + """/team/{team_id} is reachable by org admins (in org_admin_allowed_routes) but + NOT by regular internal users or the role-agnostic self_managed_routes — the + latter would open /team/new (the collision footgun) to any authenticated user.""" + from litellm.proxy._types import LiteLLMRoutes + + assert RouteChecks.check_route_access( + route="/team/abc-123", allowed_routes=LiteLLMRoutes.org_admin_allowed_routes.value + ) + assert not RouteChecks.check_route_access( + route="/team/abc-123", allowed_routes=LiteLLMRoutes.internal_user_routes.value + ) + assert not RouteChecks.check_route_access( + route="/team/abc-123", allowed_routes=LiteLLMRoutes.self_managed_routes.value + ) + + +def _patch_team_request() -> MagicMock: + request = MagicMock(spec=Request) + request.method = "PATCH" + request.query_params = {} + return request + + +def test_patch_team_gate_allows_org_admin_with_resolved_org(): + """Post-resolution, an org admin of the team's org clears the coarse gate for + PATCH /team/{team_id} — parity with /team/update.""" + user_obj = _make_org_admin_user("org-1") + valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/team-1", + request=_patch_team_request(), + valid_token=valid_token, + request_data={"organization_id": "org-1"}, + ) + + +def test_patch_team_gate_rejects_regular_internal_user(): + """A plain internal user (not an org admin) is rejected at the coarse gate for + PATCH /team/{team_id}, even with the team's org resolved — injection alone is + not access. Same outcome as /team/update.""" + user_obj = LiteLLM_UserTable( + user_id="regular-user", + user_role=LitellmUserRoles.INTERNAL_USER.value, + organization_memberships=None, + ) + valid_token = UserAPIKeyAuth(user_id="regular-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + + with pytest.raises(Exception): + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/team-1", + request=_patch_team_request(), + valid_token=valid_token, + request_data={"organization_id": "org-1"}, + ) + + +def test_patch_team_gate_rejects_cross_org_admin(): + """An org admin of a DIFFERENT org is rejected even after org resolution.""" + user_obj = _make_org_admin_user("org-1") + valid_token = UserAPIKeyAuth(user_id="org-admin-user", user_role=LitellmUserRoles.INTERNAL_USER.value) + + with pytest.raises(Exception): + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/team/team-1", + request=_patch_team_request(), + valid_token=valid_token, + request_data={"organization_id": "org-2"}, + ) + + +def test_patch_team_gate_rejects_view_only_admin(): + """A view-only proxy admin cannot PATCH a team (unsafe method), parity with the + /team/update view-only block.""" + user_obj = LiteLLM_UserTable( + user_id="viewer", + user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value, + ) + valid_token = UserAPIKeyAuth(user_id="viewer", user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value) + + with pytest.raises(Exception): + RouteChecks.non_proxy_admin_allowed_routes_check( + user_obj=user_obj, + _user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value, + route="/team/team-1", + request=_patch_team_request(), + valid_token=valid_token, + request_data={"organization_id": "org-1"}, + ) + + @pytest.mark.asyncio async def test_initialize_pass_through_registers_wildcard_for_auth_subpath(): """ diff --git a/tests/test_litellm/proxy/auth/test_user_api_key_auth.py b/tests/test_litellm/proxy/auth/test_user_api_key_auth.py index 4141b1f20c3..90f46152837 100644 --- a/tests/test_litellm/proxy/auth/test_user_api_key_auth.py +++ b/tests/test_litellm/proxy/auth/test_user_api_key_auth.py @@ -31,6 +31,7 @@ from litellm.proxy.auth.handle_jwt import JWTHandler from litellm.proxy.auth.auth_checks import get_key_object, _cache_key_object from litellm.proxy.auth.route_checks import RouteChecks from litellm.proxy.auth.user_api_key_auth import ( + _check_key_model_budget_with_fallback, _PendingAutoRegister, _matches_routing_override, _reserve_budget_after_common_checks, @@ -4084,3 +4085,265 @@ async def test_auth_path_caches_team_object_under_canonical_team_id_key(): assert served is not None and served.team_id == team_id assert cache.get_cache(key=team_id) is None assert cache.get_cache(key=None) is None + + +class TestCheckKeyModelBudgetWithFallback: + """`_check_key_model_budget_with_fallback` must reroute a request to the + first configured `budget_fallbacks` entry still within its own budget, + and only raise `BudgetExceededError` when no fallback is available.""" + + def _make_request(self): + request = MagicMock() + request.scope = {} + return request + + @pytest.mark.asyncio + async def test_within_budget_does_not_reroute(self): + valid_token = UserAPIKeyAuth( + token="test-key", budget_fallbacks={"gpt-4o": ["gpt-4o-mini"]} + ) + limiter = AsyncMock() + limiter.is_key_within_model_budget.return_value = True + request_data = {"model": "gpt-4o"} + request = self._make_request() + + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=limiter, + model_name="gpt-4o", + request_data=request_data, + request=request, + ) + + assert request_data["model"] == "gpt-4o" + limiter.get_fallback_model_within_budget.assert_not_awaited() + assert "parsed_body" not in request.scope + + @pytest.mark.asyncio + async def test_exceeded_budget_reroutes_to_fallback(self): + valid_token = UserAPIKeyAuth( + token="test-key", + budget_fallbacks={"gpt-4o": ["gpt-4o-mini", "claude-haiku"]}, + ) + limiter = AsyncMock() + limiter.is_key_within_model_budget.side_effect = litellm.BudgetExceededError( + current_cost=10, max_budget=5 + ) + limiter.get_fallback_model_within_budget.return_value = "gpt-4o-mini" + request_data = {"model": "gpt-4o"} + request = self._make_request() + + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=limiter, + model_name="gpt-4o", + request_data=request_data, + request=request, + ) + + assert request_data["model"] == "gpt-4o-mini" + limiter.get_fallback_model_within_budget.assert_awaited_once_with( + user_api_key_dict=valid_token, model="gpt-4o" + ) + # the rerouted model must be visible to a later, separate + # `_read_request_body` call on the same `request` (route handlers + # re-parse the body from this cache instead of reusing the dict). + cached_keys, cached_body = request.scope["parsed_body"] + assert cached_body["model"] == "gpt-4o-mini" + + @pytest.mark.asyncio + async def test_raises_when_every_fallback_also_exceeded(self): + valid_token = UserAPIKeyAuth( + token="test-key", budget_fallbacks={"gpt-4o": ["gpt-4o-mini"]} + ) + limiter = AsyncMock() + original_error = litellm.BudgetExceededError(current_cost=10, max_budget=5) + limiter.is_key_within_model_budget.side_effect = original_error + limiter.get_fallback_model_within_budget.return_value = None + request_data = {"model": "gpt-4o"} + request = self._make_request() + + with pytest.raises(litellm.BudgetExceededError) as exc_info: + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=limiter, + model_name="gpt-4o", + request_data=request_data, + request=request, + ) + + assert exc_info.value is original_error + assert request_data["model"] == "gpt-4o" + + @pytest.mark.asyncio + async def test_raises_when_fallback_not_authorized(self): + """If the fallback model is within budget but the key is not allowed + to call it, the original BudgetExceededError must be raised instead + of rerouting to an unauthorized model.""" + valid_token = UserAPIKeyAuth( + token="test-key", + models=["gpt-4o"], + budget_fallbacks={"gpt-4o": ["restricted-model"]}, + ) + limiter = AsyncMock() + original_error = litellm.BudgetExceededError(current_cost=10, max_budget=5) + limiter.is_key_within_model_budget.side_effect = original_error + limiter.get_fallback_model_within_budget.return_value = "restricted-model" + request_data = {"model": "gpt-4o"} + request = self._make_request() + + with patch( + "litellm.proxy.auth.user_api_key_auth.can_key_call_model", + side_effect=ProxyException( + message="model not allowed", + type=ProxyErrorTypes.budget_exceeded, + param="model", + code=status.HTTP_403_FORBIDDEN, + ), + ): + with pytest.raises(litellm.BudgetExceededError) as exc_info: + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=limiter, + model_name="gpt-4o", + request_data=request_data, + request=request, + llm_model_list=None, + llm_router=None, + ) + + assert exc_info.value is original_error + assert request_data["model"] == "gpt-4o" + + @pytest.mark.asyncio + async def test_reroute_succeeds_when_fallback_is_authorized(self): + """If the fallback model is within budget AND authorized, the request + must be rerouted to it.""" + valid_token = UserAPIKeyAuth( + token="test-key", + models=["gpt-4o", "gpt-4o-mini"], + budget_fallbacks={"gpt-4o": ["gpt-4o-mini"]}, + ) + limiter = AsyncMock() + limiter.is_key_within_model_budget.side_effect = litellm.BudgetExceededError( + current_cost=10, max_budget=5 + ) + limiter.get_fallback_model_within_budget.return_value = "gpt-4o-mini" + request_data = {"model": "gpt-4o"} + request = self._make_request() + + with patch( + "litellm.proxy.auth.user_api_key_auth.can_key_call_model", + return_value=True, + ): + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=limiter, + model_name="gpt-4o", + request_data=request_data, + request=request, + llm_model_list=None, + llm_router=None, + ) + + assert request_data["model"] == "gpt-4o-mini" + + @pytest.mark.asyncio + async def test_raises_when_fallback_blocked_by_team_models(self): + """If the key allows the fallback but the team does not, the original + BudgetExceededError must be raised.""" + valid_token = UserAPIKeyAuth( + token="test-key", + models=["gpt-4o", "restricted-model"], + team_id="team-1", + team_models=["gpt-4o"], + budget_fallbacks={"gpt-4o": ["restricted-model"]}, + ) + limiter = AsyncMock() + original_error = litellm.BudgetExceededError(current_cost=10, max_budget=5) + limiter.is_key_within_model_budget.side_effect = original_error + limiter.get_fallback_model_within_budget.return_value = "restricted-model" + request_data = {"model": "gpt-4o"} + request = self._make_request() + + with patch( + "litellm.proxy.auth.user_api_key_auth.can_key_call_model", + return_value=True, + ): + with pytest.raises(litellm.BudgetExceededError) as exc_info: + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=limiter, + model_name="gpt-4o", + request_data=request_data, + request=request, + llm_model_list=None, + llm_router=None, + ) + + assert exc_info.value is original_error + assert request_data["model"] == "gpt-4o" + + @pytest.mark.asyncio + async def test_reroute_updates_path_params_model(self): + """On path-model routes (/openai/deployments/{model}/...) the fallback + must also update path_params so downstream logic does not revert to the + original path model.""" + valid_token = UserAPIKeyAuth( + token="test-key", + models=["gpt-4o", "gpt-4o-mini"], + budget_fallbacks={"gpt-4o": ["gpt-4o-mini"]}, + ) + limiter = AsyncMock() + limiter.is_key_within_model_budget.side_effect = litellm.BudgetExceededError( + current_cost=10, max_budget=5 + ) + limiter.get_fallback_model_within_budget.return_value = "gpt-4o-mini" + request_data = {"model": "gpt-4o"} + request = self._make_request() + request.scope["path_params"] = {"model": "gpt-4o"} + + with patch( + "litellm.proxy.auth.user_api_key_auth.can_key_call_model", + return_value=True, + ): + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=limiter, + model_name="gpt-4o", + request_data=request_data, + request=request, + llm_model_list=None, + llm_router=None, + ) + + assert request_data["model"] == "gpt-4o-mini" + assert request.scope["path_params"]["model"] == "gpt-4o-mini" + + @pytest.mark.asyncio + async def test_raises_when_model_from_nested_field(self): + """Budget fallback must not attempt rewrite when the checked model + came from a nested field (e.g. session.model) rather than the + top-level request_data['model'].""" + valid_token = UserAPIKeyAuth( + token="test-key", + models=["gpt-4o", "gpt-4o-mini"], + budget_fallbacks={"gpt-4o": ["gpt-4o-mini"]}, + ) + limiter = AsyncMock() + original_error = litellm.BudgetExceededError(current_cost=10, max_budget=5) + limiter.is_key_within_model_budget.side_effect = original_error + request_data = {"session": {"model": "gpt-4o"}} + request = self._make_request() + + with pytest.raises(litellm.BudgetExceededError) as exc_info: + await _check_key_model_budget_with_fallback( + valid_token=valid_token, + model_max_budget_limiter=limiter, + model_name="gpt-4o", + request_data=request_data, + request=request, + ) + + assert exc_info.value is original_error + assert "model" not in request_data diff --git a/tests/test_litellm/proxy/client/cli/test_auth_commands.py b/tests/test_litellm/proxy/client/cli/test_auth_commands.py index 4ee8b502aa2..6be43c9da44 100644 --- a/tests/test_litellm/proxy/client/cli/test_auth_commands.py +++ b/tests/test_litellm/proxy/client/cli/test_auth_commands.py @@ -5,13 +5,12 @@ import time from pathlib import Path from unittest.mock import Mock, mock_open, patch -sys.path.insert( - 0, os.path.abspath("../../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../..")) # Adds the parent directory to the system path from click.testing import CliRunner +from litellm.constants import CLI_JWT_EXPIRATION_HOURS from litellm.proxy.client.cli.commands.auth import ( clear_token, get_stored_api_key, @@ -19,6 +18,7 @@ from litellm.proxy.client.cli.commands.auth import ( load_token, login, logout, + print_token, save_token, whoami, ) @@ -78,12 +78,9 @@ class TestTokenUtilities: with ( patch("builtins.open", mock_open()) as mock_file, - patch( - "litellm.proxy.client.cli.commands.auth.get_token_file_path" - ) as mock_path, + patch("litellm.proxy.client.cli.commands.auth.get_token_file_path") as mock_path, patch("os.chmod") as mock_chmod, ): - mock_path.return_value = "/test/path/token.json" save_token(token_data) @@ -93,9 +90,7 @@ class TestTokenUtilities: mock_chmod.assert_called_once_with("/test/path/token.json", 0o600) # Verify JSON content was written correctly - written_content = "".join( - call[0][0] for call in mock_file().write.call_args_list - ) + written_content = "".join(call[0][0] for call in mock_file().write.call_args_list) parsed_content = json.loads(written_content) assert parsed_content == token_data @@ -109,12 +104,9 @@ class TestTokenUtilities: with ( patch("builtins.open", mock_open(read_data=json.dumps(token_data))), - patch( - "litellm.proxy.client.cli.commands.auth.get_token_file_path" - ) as mock_path, + patch("litellm.proxy.client.cli.commands.auth.get_token_file_path") as mock_path, patch("os.path.exists", return_value=True), ): - mock_path.return_value = "/test/path/token.json" result = load_token() @@ -124,12 +116,9 @@ class TestTokenUtilities: def test_load_token_file_not_exists(self): """Test loading token when file doesn't exist""" with ( - patch( - "litellm.proxy.client.cli.commands.auth.get_token_file_path" - ) as mock_path, + patch("litellm.proxy.client.cli.commands.auth.get_token_file_path") as mock_path, patch("os.path.exists", return_value=False), ): - mock_path.return_value = "/test/path/token.json" result = load_token() @@ -140,12 +129,9 @@ class TestTokenUtilities: """Test loading token with invalid JSON""" with ( patch("builtins.open", mock_open(read_data="invalid json")), - patch( - "litellm.proxy.client.cli.commands.auth.get_token_file_path" - ) as mock_path, + patch("litellm.proxy.client.cli.commands.auth.get_token_file_path") as mock_path, patch("os.path.exists", return_value=True), ): - mock_path.return_value = "/test/path/token.json" result = load_token() @@ -156,12 +142,9 @@ class TestTokenUtilities: """Test loading token with IO error""" with ( patch("builtins.open", side_effect=IOError("Permission denied")), - patch( - "litellm.proxy.client.cli.commands.auth.get_token_file_path" - ) as mock_path, + patch("litellm.proxy.client.cli.commands.auth.get_token_file_path") as mock_path, patch("os.path.exists", return_value=True), ): - mock_path.return_value = "/test/path/token.json" result = load_token() @@ -171,13 +154,10 @@ class TestTokenUtilities: def test_clear_token_file_exists(self): """Test clearing token when file exists""" with ( - patch( - "litellm.proxy.client.cli.commands.auth.get_token_file_path" - ) as mock_path, + patch("litellm.proxy.client.cli.commands.auth.get_token_file_path") as mock_path, patch("os.path.exists", return_value=True), patch("os.remove") as mock_remove, ): - mock_path.return_value = "/test/path/token.json" clear_token() @@ -187,13 +167,10 @@ class TestTokenUtilities: def test_clear_token_file_not_exists(self): """Test clearing token when file doesn't exist""" with ( - patch( - "litellm.proxy.client.cli.commands.auth.get_token_file_path" - ) as mock_path, + patch("litellm.proxy.client.cli.commands.auth.get_token_file_path") as mock_path, patch("os.path.exists", return_value=False), patch("os.remove") as mock_remove, ): - mock_path.return_value = "/test/path/token.json" clear_token() @@ -238,10 +215,7 @@ class TestTokenUtilities: "litellm.litellm_core_utils.cli_token_utils.load_cli_token", return_value=token_data, ): - assert ( - get_stored_api_key(expected_base_url="https://real-proxy.com") - == "sk-prod" - ) + assert get_stored_api_key(expected_base_url="https://real-proxy.com") == "sk-prod" def test_get_stored_api_key_base_url_match_trailing_slash(self): """Trailing slash on expected_base_url is normalised before comparison""" @@ -250,10 +224,7 @@ class TestTokenUtilities: "litellm.litellm_core_utils.cli_token_utils.load_cli_token", return_value=token_data, ): - assert ( - get_stored_api_key(expected_base_url="https://real-proxy.com/") - == "sk-prod" - ) + assert get_stored_api_key(expected_base_url="https://real-proxy.com/") == "sk-prod" def test_get_stored_api_key_base_url_mismatch(self): """Stored key is NOT returned when expected_base_url differs from stored origin""" @@ -271,9 +242,7 @@ class TestTokenUtilities: "litellm.litellm_core_utils.cli_token_utils.load_cli_token", return_value=token_data, ): - assert ( - get_stored_api_key(expected_base_url="https://real-proxy.com") is None - ) + assert get_stored_api_key(expected_base_url="https://real-proxy.com") is None class TestLoginCommand: @@ -307,11 +276,8 @@ class TestLoginCommand: ) as mock_post, patch("requests.get", return_value=mock_response) as mock_get, patch("litellm.proxy.client.cli.commands.auth.save_token") as mock_save, - patch( - "litellm.proxy.client.cli.interface.show_commands" - ) as mock_show_commands, + patch("litellm.proxy.client.cli.interface.show_commands") as mock_show_commands, ): - result = self.runner.invoke(login, obj=mock_context.obj) assert result.exit_code == 0 @@ -326,9 +292,7 @@ class TestLoginCommand: assert "Verification code: ABCD-EFGH" in result.output mock_post.assert_called_once() mock_get.assert_called() - assert mock_get.call_args.kwargs["headers"] == { - "x-litellm-cli-poll-secret": "poll-secret" - } + assert mock_get.call_args.kwargs["headers"] == {"x-litellm-cli-poll-secret": "poll-secret"} # Verify JWT was saved mock_save.assert_called_once() @@ -355,7 +319,6 @@ class TestLoginCommand: patch("requests.get", return_value=mock_response), patch("time.sleep"), ): - # Mock time.sleep to avoid actual delays in tests result = self.runner.invoke(login, obj=mock_context.obj) @@ -377,7 +340,6 @@ class TestLoginCommand: patch("requests.get", return_value=mock_response), patch("time.sleep"), ): - result = self.runner.invoke(login, obj=mock_context.obj) assert result.exit_code == 0 @@ -399,7 +361,6 @@ class TestLoginCommand: ), patch("time.sleep"), ): - result = self.runner.invoke(login, obj=mock_context.obj) assert result.exit_code == 0 @@ -415,7 +376,6 @@ class TestLoginCommand: patch("requests.post", return_value=_mock_cli_sso_start_response()), patch("requests.get", side_effect=KeyboardInterrupt), ): - result = self.runner.invoke(login, obj=mock_context.obj) assert result.exit_code == 0 @@ -440,7 +400,6 @@ class TestLoginCommand: patch("requests.get", return_value=mock_response), patch("time.sleep"), ): - result = self.runner.invoke(login, obj=mock_context.obj) assert result.exit_code == 0 @@ -456,7 +415,6 @@ class TestLoginCommand: patch("requests.post", return_value=_mock_cli_sso_start_response()), patch("requests.get", side_effect=ValueError("Invalid value")), ): - result = self.runner.invoke(login, obj=mock_context.obj) assert result.exit_code == 0 @@ -496,9 +454,7 @@ class TestWhoamiCommand: "timestamp": time.time() - 3600, # 1 hour ago } - with patch( - "litellm.proxy.client.cli.commands.auth.load_token", return_value=token_data - ): + with patch("litellm.proxy.client.cli.commands.auth.load_token", return_value=token_data): result = self.runner.invoke(whoami) assert result.exit_code == 0 @@ -510,9 +466,7 @@ class TestWhoamiCommand: def test_whoami_not_authenticated(self): """Test whoami when user is not authenticated""" - with patch( - "litellm.proxy.client.cli.commands.auth.load_token", return_value=None - ): + with patch("litellm.proxy.client.cli.commands.auth.load_token", return_value=None): result = self.runner.invoke(whoami) assert result.exit_code == 0 @@ -528,9 +482,7 @@ class TestWhoamiCommand: "timestamp": time.time() - (25 * 3600), # 25 hours ago } - with patch( - "litellm.proxy.client.cli.commands.auth.load_token", return_value=token_data - ): + with patch("litellm.proxy.client.cli.commands.auth.load_token", return_value=token_data): result = self.runner.invoke(whoami) assert result.exit_code == 0 @@ -540,21 +492,16 @@ class TestWhoamiCommand: def test_whoami_missing_fields(self): """Test whoami with token missing some fields""" token_data = { - "timestamp": time.time() - - 3600 + "timestamp": time.time() - 3600 # Missing user_email, user_id, user_role } - with patch( - "litellm.proxy.client.cli.commands.auth.load_token", return_value=token_data - ): + with patch("litellm.proxy.client.cli.commands.auth.load_token", return_value=token_data): result = self.runner.invoke(whoami) assert result.exit_code == 0 assert "✅ Authenticated" in result.output - assert ( - "Unknown" in result.output - ) # Should show "Unknown" for missing fields + assert "Unknown" in result.output # Should show "Unknown" for missing fields def test_whoami_no_timestamp(self): """Test whoami with token missing timestamp""" @@ -572,7 +519,6 @@ class TestWhoamiCommand: ), patch("time.time", return_value=1000), ): - result = self.runner.invoke(whoami) assert result.exit_code == 0 @@ -625,20 +571,13 @@ class TestCLIKeyRegenerationFlow: patch("webbrowser.open") as mock_browser, patch( "requests.post", - return_value=_mock_cli_sso_start_response( - login_id="cli-session-uuid-456" - ), + return_value=_mock_cli_sso_start_response(login_id="cli-session-uuid-456"), ), - patch( - "requests.get", side_effect=[mock_first_response, mock_second_response] - ) as mock_get, + patch("requests.get", side_effect=[mock_first_response, mock_second_response]) as mock_get, patch("litellm.proxy.client.cli.commands.auth.save_token") as mock_save, - patch( - "litellm.proxy.client.cli.interface.show_commands" - ) as mock_show_commands, + patch("litellm.proxy.client.cli.interface.show_commands") as mock_show_commands, patch("click.prompt", return_value="2"), ): # User selects index 2 - result = self.runner.invoke(login, obj=mock_context.obj) assert result.exit_code == 0 @@ -659,9 +598,7 @@ class TestCLIKeyRegenerationFlow: first_poll_url = mock_get.call_args_list[0][0][0] assert "cli-session-uuid-456" in first_poll_url assert "team_id=" not in first_poll_url - assert mock_get.call_args_list[0].kwargs["headers"] == { - "x-litellm-cli-poll-secret": "poll-secret" - } + assert mock_get.call_args_list[0].kwargs["headers"] == {"x-litellm-cli-poll-secret": "poll-secret"} # Second poll should include team_id=team-beta second_poll_url = mock_get.call_args_list[1][0][0] @@ -670,10 +607,7 @@ class TestCLIKeyRegenerationFlow: # Verify JWT was saved mock_save.assert_called_once() saved_data = mock_save.call_args[0][0] - assert ( - saved_data["key"] - == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.team-beta.jwt" - ) + assert saved_data["key"] == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.team-beta.jwt" assert saved_data["user_id"] == "test-user-456" mock_show_commands.assert_called_once() @@ -698,15 +632,12 @@ class TestCLIKeyRegenerationFlow: patch("webbrowser.open") as mock_browser, patch( "requests.post", - return_value=_mock_cli_sso_start_response( - login_id="cli-session-uuid-solo" - ), + return_value=_mock_cli_sso_start_response(login_id="cli-session-uuid-solo"), ), patch("requests.get", return_value=mock_response), patch("litellm.proxy.client.cli.commands.auth.save_token") as mock_save, patch("litellm.proxy.client.cli.interface.show_commands"), ): - result = self.runner.invoke(login, obj=mock_context.obj) assert result.exit_code == 0 @@ -722,7 +653,118 @@ class TestCLIKeyRegenerationFlow: # Verify JWT was saved mock_save.assert_called_once() saved_data = mock_save.call_args[0][0] - assert ( - saved_data["key"] == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.no-team.jwt" - ) + assert saved_data["key"] == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.no-team.jwt" assert saved_data["user_id"] == "test-user-solo" + + +class TestPrintTokenCommand: + """Test `lite auth print-token`, used as Claude Code's apiKeyHelper. + + stdout must contain *only* the token -- Claude Code treats stdout + verbatim as the bearer token, so any diagnostic text on stdout would + corrupt authentication. + + apiKeyHelper is configured as a bare command (managed-settings.json sets + just `"apiKeyHelper": "lite auth print-token"`, no --base-url flag) -- + so in the common case ctx.obj has no explicit base_url at all, and the + command must resolve the server from whatever `lite login` stored in + token.json, not from a CLI default. `--base-url`/`LITELLM_PROXY_URL` + only matters when a caller explicitly overrides it (tracked via + ctx.obj["base_url_explicit"], set by the `cli` group from + click's ParameterSource). + """ + + def setup_method(self): + self.runner = CliRunner() + + def test_no_stored_token_fails_cleanly(self): + with patch("litellm.proxy.client.cli.commands.auth.load_token", return_value=None): + result = self.runner.invoke(print_token, obj={}) + + assert result.exit_code != 0 + assert "Not authenticated" in result.output + + def test_bare_invocation_resolves_server_from_stored_token(self): + """The apiKeyHelper's real invocation shape: no --base-url given at + all. Must use token.json's own base_url, not a hardcoded default.""" + with ( + patch( + "litellm.proxy.client.cli.commands.auth.load_token", + return_value={ + "base_url": "https://litellm-proxy.corp.com", + "key": "sk-prod-fresh", + "timestamp": time.time(), + }, + ), + patch("requests.post") as mock_post, + ): + result = self.runner.invoke(print_token, obj={}) + + assert result.exit_code == 0 + assert result.output.strip() == "sk-prod-fresh" + mock_post.assert_not_called() + + def test_explicit_base_url_mismatch_fails_cleanly(self): + """When the caller *does* explicitly pass --base-url, a token issued + for a different server must never be printed.""" + with patch( + "litellm.proxy.client.cli.commands.auth.load_token", + return_value={ + "base_url": "https://other-server.com", + "key": "sk-should-not-print", + "timestamp": time.time(), + }, + ): + result = self.runner.invoke( + print_token, + obj={"base_url": "http://localhost:4000", "base_url_explicit": True}, + ) + + assert result.exit_code != 0 + assert "sk-should-not-print" not in result.output + + def test_fresh_cached_key_printed_without_network_call(self): + """A recently-issued key should be printed straight from cache -- no + refresh call on every single invocation (apiKeyHelper gets called + frequently).""" + with ( + patch( + "litellm.proxy.client.cli.commands.auth.load_token", + return_value={ + "base_url": "http://localhost:4000", + "key": "sk-cached-fresh", + "timestamp": time.time(), + }, + ), + patch("requests.post") as mock_post, + ): + result = self.runner.invoke(print_token, obj={}) + + assert result.exit_code == 0 + assert result.output.strip() == "sk-cached-fresh" + mock_post.assert_not_called() + + def test_stale_key_fails_fast_without_network_call(self): + """There is no silent refresh: an expired cached key must fail + loudly (stderr, nonzero exit) telling the user to `lite login` + again, rather than making a network call or printing a dead key + that will just 401 Claude Code.""" + old_timestamp = time.time() - (CLI_JWT_EXPIRATION_HOURS + 1) * 3600 + + with ( + patch( + "litellm.proxy.client.cli.commands.auth.load_token", + return_value={ + "base_url": "http://localhost:4000", + "key": "sk-stale-key", + "timestamp": old_timestamp, + }, + ), + patch("requests.post") as mock_post, + ): + result = self.runner.invoke(print_token, obj={}) + + assert result.exit_code != 0 + assert "sk-stale-key" not in result.output + assert "lite login" in result.output + mock_post.assert_not_called() diff --git a/tests/test_litellm/proxy/client/cli/test_global_options.py b/tests/test_litellm/proxy/client/cli/test_global_options.py index 3a19f735c1b..53b7e4dbc29 100644 --- a/tests/test_litellm/proxy/client/cli/test_global_options.py +++ b/tests/test_litellm/proxy/client/cli/test_global_options.py @@ -1,7 +1,7 @@ # stdlib imports import os import sys -from unittest.mock import patch +from unittest.mock import Mock, patch import pytest from click.testing import CliRunner @@ -36,6 +36,35 @@ def test_cli_version_flag(cli_runner): assert "LiteLLM Proxy Server Version: 1.2.3" in result.output +def test_base_url_trailing_slash_normalized(cli_runner): + """A trailing slash on --base-url must not produce a double slash (e.g. '//sso/cli/start').""" + with ( + patch("webbrowser.open"), + patch( + "requests.post", + return_value=Mock( + status_code=200, + json=Mock( + return_value={ + "login_id": "cli-test-uuid", + "poll_secret": "poll-secret", + "user_code": "ABCD-EFGH", + } + ), + raise_for_status=Mock(), + ), + ) as mock_post, + patch("requests.get", side_effect=ValueError("stop after start request")), + ): + cli_runner.invoke( + cli, ["--base-url", "https://gateway.litellm-sandbox.ai/", "login"] + ) + + mock_post.assert_called_once_with( + "https://gateway.litellm-sandbox.ai/sso/cli/start", timeout=10 + ) + + def test_cli_version_command(cli_runner): """Test that 'version' command prints the correct version, server URL, and server version, and exits successfully""" with ( diff --git a/tests/test_litellm/proxy/common_utils/test_cache_codec.py b/tests/test_litellm/proxy/common_utils/test_cache_codec.py index 044d4c2d1a7..ded35227971 100644 --- a/tests/test_litellm/proxy/common_utils/test_cache_codec.py +++ b/tests/test_litellm/proxy/common_utils/test_cache_codec.py @@ -1,10 +1,11 @@ import logging -from typing import Optional +from typing import Any, Dict, Optional from unittest.mock import patch import pytest from pydantic import BaseModel, ValidationError +from litellm.models.managed_files import LiteLLM_ManagedVectorStoresTable from litellm.proxy.common_utils.cache_pydantic_utils import CacheCodec @@ -17,6 +18,11 @@ class _SampleSubModel(_SampleModel): pass +class _RequiredNullableModel(BaseModel): + id: str + budget_table: Optional[Dict[str, Any]] + + class TestCacheCodecSerialize: def test_without_model_type_base_model_dumped_json_safe(self): m = _SampleModel(name="a", count=1) @@ -37,12 +43,12 @@ class TestCacheCodecSerialize: def test_with_model_type_base_model_validated_and_dumped(self): m = _SampleModel(name="c", count=None) out = CacheCodec.serialize(m, model_type=_SampleModel) - assert out == {"name": "c"} + assert out == {"name": "c", "count": None} - def test_with_model_type_exclude_none_on_dump(self): + def test_with_model_type_none_field_preserved_on_dump(self): out = CacheCodec.serialize({"name": "d"}, model_type=_SampleModel) - assert out == {"name": "d"} - assert "count" not in out + assert out == {"name": "d", "count": None} + assert "count" in out def test_with_model_type_non_dict_non_model_passthrough(self): assert CacheCodec.serialize("raw", model_type=_SampleModel) == "raw" @@ -124,3 +130,39 @@ class TestCacheCodecDeserialize: for r in caplog.records if r.levelno >= logging.WARNING ), f"Expected deserialize validation warning. Records: {[r.message for r in caplog.records]}" + + +class TestCacheCodecRoundTripPreservesNoneFields: + def test_none_value_kept_as_null_not_dropped(self): + out = CacheCodec.serialize( + _RequiredNullableModel(id="x", budget_table=None), + model_type=_RequiredNullableModel, + ) + assert out == {"id": "x", "budget_table": None} + assert "budget_table" in out + + def test_required_nullable_none_field_survives_round_trip(self): + original = _RequiredNullableModel(id="x", budget_table=None) + wire = CacheCodec.serialize(original, model_type=_RequiredNullableModel) + restored = CacheCodec.deserialize(wire, model_type=_RequiredNullableModel) + assert restored == original + + def test_managed_vector_store_row_round_trips_with_optional_fields_none(self): + vs = LiteLLM_ManagedVectorStoresTable( + vector_store_id="vs_1", + custom_llm_provider="openai", + vector_store_name=None, + vector_store_description=None, + vector_store_metadata=None, + created_at=None, + updated_at=None, + litellm_credential_name=None, + litellm_params=None, + team_id=None, + user_id=None, + ) + wire = CacheCodec.serialize(vs, model_type=LiteLLM_ManagedVectorStoresTable) + assert wire.get("vector_store_name", "MISSING") is None + assert wire.get("team_id", "MISSING") is None + restored = CacheCodec.deserialize(wire, model_type=LiteLLM_ManagedVectorStoresTable) + assert restored == vs diff --git a/tests/test_litellm/proxy/common_utils/test_encrypt_decrypt_utils.py b/tests/test_litellm/proxy/common_utils/test_encrypt_decrypt_utils.py index bee39e01dd6..08cf1e45812 100644 --- a/tests/test_litellm/proxy/common_utils/test_encrypt_decrypt_utils.py +++ b/tests/test_litellm/proxy/common_utils/test_encrypt_decrypt_utils.py @@ -18,9 +18,7 @@ from litellm.proxy.common_utils.encrypt_decrypt_utils import ( def _use_aes(monkeypatch): """Flip the write-time algorithm to AES-256-GCM for the duration of a test.""" - monkeypatch.setattr( - proxy_server, "general_settings", {"encryption_algorithm": "aes-256-gcm"} - ) + monkeypatch.setattr(proxy_server, "general_settings", {"encryption_algorithm": "aes-256-gcm"}) @pytest.fixture(autouse=True) @@ -96,12 +94,7 @@ def test_aes_decrypt_failure_returns_original_when_requested(monkeypatch): _use_aes(monkeypatch) garbled = _V2_GCM_PREFIX + "###" - assert ( - decrypt_value_helper( - garbled, key="t", exception_type="debug", return_original_value=True - ) - == garbled - ) + assert decrypt_value_helper(garbled, key="t", exception_type="debug", return_original_value=True) == garbled def test_empty_string_round_trips_under_aes(monkeypatch): @@ -139,10 +132,56 @@ def test_callback_prefix_composes_with_v2(monkeypatch): def test_unknown_algorithm_falls_back_to_legacy(monkeypatch): """An unrecognized encryption_algorithm value does not produce v2 writes.""" - monkeypatch.setattr( - proxy_server, "general_settings", {"encryption_algorithm": "rot13"} - ) + monkeypatch.setattr(proxy_server, "general_settings", {"encryption_algorithm": "rot13"}) ct = encrypt_value_helper("secret") assert not ct.startswith(_V2_GCM_PREFIX) assert decrypt_value_helper(ct, key="t") == "secret" + + +def test_decrypt_failure_debug_log_omits_raw_value(monkeypatch): + """Regression for LIT-4152: the decrypt-failure debug breadcrumb must not + embed the raw value. + + A DB ``environment_variables`` secret (e.g. a ``DATABASE_URL`` connection + string) reaches this path when it cannot be decrypted, for example after a + salt or master key change, and previously printed in cleartext when the + module regex scrubber was bypassed. The failing key still names the pair so + the breadcrumb keeps its debugging value. Uses a dedicated handler rather + than caplog because caplog is unreliable under pytest-xdist. + """ + import logging + + import litellm._logging as _logging_module + from litellm._logging import verbose_proxy_logger + + monkeypatch.setattr(_logging_module, "_ENABLE_SECRET_REDACTION", False) + + secret = "postgresql://leak_user:leak_pw_decrypt@leak-host:5432/leak_db" + + class LogRecordHandler(logging.Handler): + def __init__(self) -> None: + super().__init__() + self.records: list[logging.LogRecord] = [] + + def emit(self, record: logging.LogRecord) -> None: + self.records.append(record) + + handler = LogRecordHandler() + handler.setLevel(logging.DEBUG) + original_level = verbose_proxy_logger.level + verbose_proxy_logger.setLevel(logging.DEBUG) + verbose_proxy_logger.addHandler(handler) + try: + result = decrypt_value_helper(secret, key="DATABASE_URL", return_original_value=True) + rendered = " ".join(record.getMessage() for record in handler.records) + finally: + verbose_proxy_logger.removeHandler(handler) + verbose_proxy_logger.setLevel(original_level) + + assert secret not in rendered, f"raw value leaked in decrypt-failure log: {rendered!r}" + assert "leak_pw_decrypt" not in rendered + assert any("DATABASE_URL" in record.getMessage() for record in handler.records), ( + "the failing key should still be named in the breadcrumb" + ) + assert result == secret diff --git a/tests/test_litellm/proxy/common_utils/test_json_merge_patch.py b/tests/test_litellm/proxy/common_utils/test_json_merge_patch.py new file mode 100644 index 00000000000..d4b60d6a1e7 --- /dev/null +++ b/tests/test_litellm/proxy/common_utils/test_json_merge_patch.py @@ -0,0 +1,94 @@ +import copy + +import pytest + +from litellm.proxy.common_utils.json_merge_patch import _MAX_MERGE_DEPTH, apply_json_merge_patch + +# RFC 7386 Appendix A — the normative test suite for JSON Merge Patch. +# https://www.rfc-editor.org/rfc/rfc7386#appendix-A +RFC_7386_APPENDIX_A = [ + ({"a": "b"}, {"a": "c"}, {"a": "c"}), + ({"a": "b"}, {"b": "c"}, {"a": "b", "b": "c"}), + ({"a": "b"}, {"a": None}, {}), + ({"a": "b", "b": "c"}, {"a": None}, {"b": "c"}), + ({"a": ["b"]}, {"a": "c"}, {"a": "c"}), + ({"a": "c"}, {"a": ["b"]}, {"a": ["b"]}), + ({"a": {"b": "c"}}, {"a": {"b": "d", "c": None}}, {"a": {"b": "d"}}), + ({"a": [{"b": "c"}]}, {"a": [1]}, {"a": [1]}), + (["a", "b"], ["c", "d"], ["c", "d"]), + ({"a": "b"}, ["c"], ["c"]), + ({"a": "foo"}, None, None), + ({"a": "foo"}, "bar", "bar"), + ({"e": None}, {"a": 1}, {"e": None, "a": 1}), + ([1, 2], {"a": "b", "c": None}, {"a": "b"}), + ({}, {"a": {"bb": {"ccc": None}}}, {"a": {"bb": {}}}), +] + + +@pytest.mark.parametrize("target, patch, expected", RFC_7386_APPENDIX_A) +def test_rfc_7386_appendix_a(target, patch, expected): + assert apply_json_merge_patch(target, patch) == expected + + +def test_does_not_mutate_target(): + """The target must be treated as immutable — a fresh value is returned.""" + target = {"keep": "me", "nested": {"a": 1, "b": 2}, "drop": "later"} + target_snapshot = copy.deepcopy(target) + + result = apply_json_merge_patch(target, {"nested": {"b": None, "c": 3}, "drop": None}) + + assert target == target_snapshot, "apply_json_merge_patch mutated its target argument" + assert result == {"keep": "me", "nested": {"a": 1, "c": 3}} + assert result["nested"] is not target["nested"] + + +def test_absent_key_is_preserved_but_null_key_is_deleted(): + """The core distinction PATCH relies on: omission preserves, explicit null deletes.""" + target = {"cost_center": "1234", "team": "core"} + + # Omitting cost_center preserves it; only the explicitly-null key is removed. + assert apply_json_merge_patch(target, {"team": "platform"}) == { + "cost_center": "1234", + "team": "platform", + } + assert apply_json_merge_patch(target, {"cost_center": None}) == {"team": "core"} + + +def test_deep_nested_merge_and_delete(): + target = {"limits": {"gpt-4": {"rpm": 100, "tpm": 1000}, "gpt-3.5": {"rpm": 200}}} + patch = {"limits": {"gpt-4": {"tpm": 2000}, "gpt-3.5": None, "claude": {"rpm": 50}}} + + assert apply_json_merge_patch(target, patch) == { + "limits": {"gpt-4": {"rpm": 100, "tpm": 2000}, "claude": {"rpm": 50}} + } + + +def test_scalar_patch_replaces_object_wholesale(): + assert apply_json_merge_patch({"a": {"b": 1}}, 5) == 5 + + +def test_object_patch_over_non_object_target_starts_from_empty(): + assert apply_json_merge_patch("not-an-object", {"a": 1, "b": None}) == {"a": 1} + + +def _nest(levels: int) -> dict: + """A patch nested ``levels`` dicts deep with a scalar leaf at the bottom.""" + value: object = "leaf" + for _ in range(levels): + value = {"a": value} + return value # type: ignore[return-value] + + +def test_merge_within_max_depth_is_allowed(): + """A deeply-but-not-pathologically nested patch merges without raising.""" + result = apply_json_merge_patch({}, _nest(_MAX_MERGE_DEPTH - 1)) + for _ in range(_MAX_MERGE_DEPTH - 1): + result = result["a"] + assert result == "leaf" + + +def test_merge_beyond_max_depth_raises(): + """A patch nested past the cap fails closed (ValueError) rather than + overflowing the Python stack — the guard the recursion detector requires.""" + with pytest.raises(ValueError, match="maximum depth"): + apply_json_merge_patch({}, _nest(_MAX_MERGE_DEPTH + 5)) diff --git a/tests/test_litellm/proxy/conftest.py b/tests/test_litellm/proxy/conftest.py index 607315eb246..1d71035b67f 100644 --- a/tests/test_litellm/proxy/conftest.py +++ b/tests/test_litellm/proxy/conftest.py @@ -20,6 +20,31 @@ _PROXY_MODULE_GLOBALS_TO_ISOLATE = ( ) +class StubClientNotConnectedError(Exception): + pass + + +class DisconnectedPrisma: + """Mimics prisma-client-py after disconnect(): ``is_connected()`` is False + and the ``_engine`` property raises ``ClientNotConnectedError``.""" + + def is_connected(self) -> bool: + return False + + @property + def _engine(self) -> None: + raise StubClientNotConnectedError( + "Client is not connected to the query engine, you must call `connect()` " + "before attempting to query data." + ) + + +@pytest.fixture +def disconnected_prisma() -> DisconnectedPrisma: + """A stand-in for a Prisma client wedged in the disconnected state.""" + return DisconnectedPrisma() + + @pytest.fixture(autouse=True) def _isolate_proxy_module_globals(): """ diff --git a/tests/test_litellm/proxy/db/conftest.py b/tests/test_litellm/proxy/db/conftest.py new file mode 100644 index 00000000000..a0fb6bed4fa --- /dev/null +++ b/tests/test_litellm/proxy/db/conftest.py @@ -0,0 +1,65 @@ +import os +from collections.abc import Generator +from typing import Optional + +import pytest + +DB_ENV_KEYS = ( + "IAM_TOKEN_DB_AUTH", + "DATABASE_URL", + "DIRECT_URL", + "DATABASE_URL_READ_REPLICA", + "DATABASE_HOST", + "DATABASE_PORT", + "DATABASE_USER", + "DATABASE_USERNAME", + "DATABASE_NAME", + "DATABASE_SCHEMA", + "DATABASE_PASSWORD", + "DATABASE_HOST_READ_REPLICA", + "DATABASE_PORT_READ_REPLICA", + "DATABASE_USER_READ_REPLICA", + "DATABASE_USERNAME_READ_REPLICA", + "DATABASE_NAME_READ_REPLICA", + "DATABASE_SCHEMA_READ_REPLICA", + "DATABASE_PASSWORD_READ_REPLICA", +) + +_db_env_snapshot_key = pytest.StashKey[dict[str, Optional[str]]]() + + +def _db_env_snapshot() -> dict[str, Optional[str]]: + return {key: os.environ.get(key) for key in DB_ENV_KEYS} + + +@pytest.hookimpl(wrapper=True) +def pytest_runtest_setup(item: pytest.Item) -> Generator[None, None, None]: + item.stash[_db_env_snapshot_key] = _db_env_snapshot() + return (yield) + + +@pytest.hookimpl(wrapper=True) +def pytest_runtest_teardown(item: pytest.Item, nextitem: Optional[pytest.Item]) -> Generator[None, None, None]: + result = yield + before = item.stash[_db_env_snapshot_key] + leaked = {key: value for key, value in _db_env_snapshot().items() if value != before[key]} + for key, original in before.items(): + if original is None: + os.environ.pop(key, None) + else: + os.environ[key] = original + assert not leaked, ( + f"{item.nodeid} leaked DB env vars past monkeypatch teardown: {leaked}. " + "Product code under test writes DATABASE_URL(_READ_REPLICA) into os.environ as a side effect; " + "monkeypatch only restores keys it has a record for, so a value written to a previously unset " + "key survives the test and poisons every later test in this pytest-xdist worker process " + "(DB-backed e2e tests arm themselves on DATABASE_URL and then fail to connect). " + "Use the unset_database_url fixture (or monkeypatch.setenv) so restoration is registered." + ) + return result + + +@pytest.fixture +def unset_database_url(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("DATABASE_URL", "about-to-be-unset") + monkeypatch.delenv("DATABASE_URL") diff --git a/tests/test_litellm/proxy/db/test_db_spend_update_writer.py b/tests/test_litellm/proxy/db/test_db_spend_update_writer.py index 04c93f48ca9..c29cdaf4171 100644 --- a/tests/test_litellm/proxy/db/test_db_spend_update_writer.py +++ b/tests/test_litellm/proxy/db/test_db_spend_update_writer.py @@ -1,4 +1,5 @@ import asyncio +import copy import json import os import sys @@ -1419,8 +1420,7 @@ async def test_batch_database_updates_isolation_on_failure(): prisma_client=MagicMock(), user_api_key_cache=MagicMock(), litellm_proxy_budget_name="budget", - payload_copy={"key": "value"}, - request_tags=None, + payload={"key": "value"}, ) # _update_key_db raised, but all others should still have been called @@ -1704,3 +1704,117 @@ async def test_commit_spend_updates_iterates_in_sorted_order( ) assert captured_where_values == expected_order + + +@pytest.mark.asyncio +async def test_update_database_does_not_deepcopy_on_request_path(): + """ + Regression for LIT-4088: copy.deepcopy must not run while the caller awaits + update_database(). The deepcopy used to isolate the daily-spend helpers is + relocated into the _batch_database_updates background task, and the spend-log + insert receives the payload directly (all consumers are read-only). + + Asserts: + - zero copy.deepcopy calls happen on the awaited request path + - the batch background task still hands the daily helpers an isolated copy + (mutating the original after the task ran does not bleed into it) + - the spend-log insert receives the payload on the request path with the + correct content + """ + db_writer = DBSpendUpdateWriter() + + captured_batch_payloads = [] + captured_spend_log = {} + + async def capture_batch_payload(**kwargs): + captured_batch_payloads.append(kwargs.get("payload")) + + async def capture_spend_log(**kwargs): + payload = kwargs.get("payload") + captured_spend_log["ref"] = payload + captured_spend_log["model_at_call"] = payload["model"] + + db_writer._insert_spend_log_to_db = AsyncMock(side_effect=capture_spend_log) + db_writer._update_user_db = AsyncMock() + db_writer._update_key_db = AsyncMock() + db_writer._update_team_db = AsyncMock() + db_writer._update_org_db = AsyncMock() + db_writer._update_tag_db = AsyncMock() + db_writer._update_agent_db = AsyncMock() + db_writer.add_spend_log_transaction_to_daily_user_transaction = AsyncMock( + side_effect=capture_batch_payload + ) + db_writer.add_spend_log_transaction_to_daily_end_user_transaction = AsyncMock() + db_writer.add_spend_log_transaction_to_daily_agent_transaction = AsyncMock() + db_writer.add_spend_log_transaction_to_daily_team_transaction = AsyncMock() + db_writer.add_spend_log_transaction_to_daily_org_transaction = AsyncMock() + db_writer.add_spend_log_transaction_to_daily_tag_transaction = AsyncMock() + + fake_payload = { + "startTime": "2024-01-01T00:00:00", + "endTime": "2024-01-01T00:01:00", + "model": "gpt-4", + "custom_llm_provider": "openai", + "request_tags": '["prod-tag"]', + "spend": 0.0, + "nested": {"a": 1}, + } + + deepcopy_calls = [] + real_deepcopy = copy.deepcopy + + def counting_deepcopy(obj, *args, **kwargs): + deepcopy_calls.append(obj) + return real_deepcopy(obj, *args, **kwargs) + + with ( + patch("litellm.proxy.proxy_server.disable_spend_logs", False), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + patch("litellm.proxy.proxy_server.user_api_key_cache", MagicMock()), + patch("litellm.proxy.proxy_server.litellm_proxy_budget_name", "test-budget"), + patch( + "litellm.proxy.spend_tracking.spend_tracking_utils.get_logging_payload", + return_value=fake_payload, + ), + patch( + "litellm.proxy.db.db_spend_update_writer.copy.deepcopy", + counting_deepcopy, + ), + ): + await db_writer.update_database( + token="test-token", + user_id="test-user", + end_user_id="test-end-user", + team_id="test-team", + org_id="test-org", + kwargs={"model": "gpt-4", "custom_llm_provider": "openai"}, + completion_response=MagicMock(), + start_time=datetime.now(), + end_time=datetime.now(), + response_cost=0.1, + ) + + # Request path is clean: nothing was deepcopied while the caller awaited. + assert len(deepcopy_calls) == 0 + + # The spend-log insert ran inline on the request path with the real payload. + assert captured_spend_log["ref"] is fake_payload + assert captured_spend_log["model_at_call"] == "gpt-4" + assert fake_payload["spend"] == 0.1 + + # Now let the batch background task run; the deepcopy happens here. + await asyncio.sleep(0) + + assert len(deepcopy_calls) >= 1 + assert len(captured_batch_payloads) == 1 + batch_payload = captured_batch_payloads[0] + assert batch_payload is not fake_payload + assert batch_payload["model"] == "gpt-4" + assert batch_payload["spend"] == 0.1 + + # Mutating the original after the batch task captured its snapshot must not + # leak into the daily helper's isolated copy. + fake_payload["model"] = "MUTATED" + fake_payload["nested"]["a"] = 999 + assert batch_payload["model"] == "gpt-4" + assert batch_payload["nested"]["a"] == 1 diff --git a/tests/test_litellm/proxy/db/test_db_url_settings.py b/tests/test_litellm/proxy/db/test_db_url_settings.py index 573bd5ae584..e5aa09addab 100644 --- a/tests/test_litellm/proxy/db/test_db_url_settings.py +++ b/tests/test_litellm/proxy/db/test_db_url_settings.py @@ -51,25 +51,21 @@ _MANAGED_DB_ENV_VARS = ( @pytest.fixture(autouse=True) -def _scrub_db_env(): +def _scrub_db_env(monkeypatch): """Start each test from a clean slate and restore the original env afterward. - ``apply_to_env`` writes ``DATABASE_URL`` straight into ``os.environ``, which - ``monkeypatch`` cannot undo. Snapshotting and restoring here keeps a - synthesized URL (e.g. ``writer.example.com``) from leaking into later tests - that read ``DATABASE_URL`` to decide whether to hit a real database. + ``apply_to_env`` writes ``DATABASE_URL`` straight into ``os.environ``. + Registering a setenv+delenv pair per var gives ``monkeypatch`` a restore + record even for previously unset keys, so a synthesized URL (e.g. + ``writer.example.com``) cannot leak into later tests that read + ``DATABASE_URL`` to decide whether to hit a real database. Restoring via + the same ``monkeypatch`` instance the tests use also keeps undo ordering + consistent (a hand-rolled snapshot/restore runs before ``monkeypatch``'s + own undo and gets clobbered by it). """ - saved = {var: os.environ.get(var) for var in _MANAGED_DB_ENV_VARS} for var in _MANAGED_DB_ENV_VARS: - os.environ.pop(var, None) - try: - yield - finally: - for var, value in saved.items(): - if value is None: - os.environ.pop(var, None) - else: - os.environ[var] = value + monkeypatch.setenv(var, "scrubbed") + monkeypatch.delenv(var) def _stub_iam_token(token: str = "FAKE_TOKEN"): diff --git a/tests/test_litellm/proxy/db/test_exception_handler.py b/tests/test_litellm/proxy/db/test_exception_handler.py index 6021c221426..0634a01326c 100644 --- a/tests/test_litellm/proxy/db/test_exception_handler.py +++ b/tests/test_litellm/proxy/db/test_exception_handler.py @@ -148,6 +148,37 @@ def test_is_database_service_unavailable_error_prisma_p1001_masquerades_as_datae ) +def test_is_prisma_data_error_only_true_for_dataerror(): + """The spend-log poison-row isolation gates on this: only a prisma + ``DataError`` (the DB refused the data, e.g. a NUL byte) may be bisected + into a per-row drop. A connectivity failure or any non-prisma exception + must not be treated as a data rejection, so the whole batch surfaces.""" + import httpx + + data_error = DataError(data={"user_facing_error": {"message": "invalid byte sequence for encoding UTF8: 0x00"}}) + assert PrismaDBExceptionHandler.is_prisma_data_error(data_error) is True + + for non_data in ( + httpx.ConnectError("conn refused"), + PrismaError("can't reach database server"), + UniqueViolationError(data={"user_facing_error": {"meta": {"table": "t"}}}), + RuntimeError("boom"), + ): + assert PrismaDBExceptionHandler.is_prisma_data_error(non_data) is False + + +def test_is_prisma_data_error_true_for_connection_masquerade_dataerror(): + """The P1001 outage prisma mislabels as a ``DataError`` is still a + ``DataError`` by type, so this returns True; the spend-log helper relies on + ``is_database_service_unavailable_error`` (not this check) to keep that + outage on the retry path instead of dropping rows.""" + p1001_as_dataerror = DataError( + data={"user_facing_error": {"message": "Can't reach database server at `127.0.0.1`:`5499`"}} + ) + assert PrismaDBExceptionHandler.is_prisma_data_error(p1001_as_dataerror) is True + assert PrismaDBExceptionHandler.is_database_service_unavailable_error(p1001_as_dataerror) is True + + def test_is_database_service_unavailable_error_cached_plan_escapes_as_503(): """Composes with the cached-plan retry: when that recovery fails and the Postgres "cached plan must not change result type" error escapes (raised by diff --git a/tests/test_litellm/proxy/db/test_prisma_client.py b/tests/test_litellm/proxy/db/test_prisma_client.py index 397e3f36e41..eeaf726941f 100644 --- a/tests/test_litellm/proxy/db/test_prisma_client.py +++ b/tests/test_litellm/proxy/db/test_prisma_client.py @@ -92,6 +92,7 @@ async def test_recreate_prisma_client_kills_old_engine_on_disconnect_failure( """When disconnect() fails, recreate_prisma_client must SIGTERM/SIGKILL the old engine PID.""" mock_prisma = AsyncMock() mock_prisma.disconnect.side_effect = Exception("engine hung") + mock_prisma.is_connected = MagicMock(return_value=True) # Simulate engine subprocess with a known PID mock_engine = MagicMock() @@ -122,6 +123,7 @@ async def test_recreate_prisma_client_skips_kill_on_successful_disconnect( ): """When disconnect() succeeds, no kill should be attempted.""" mock_prisma = AsyncMock() + mock_prisma.is_connected = MagicMock(return_value=True) mock_prisma.disconnect.return_value = None wrapper = PrismaWrapper(original_prisma=mock_prisma, iam_token_db_auth=False) @@ -142,6 +144,7 @@ async def test_recreate_prisma_client_handles_missing_engine_pid( ): """When engine PID is unavailable (no _engine attr), kill is skipped gracefully.""" mock_prisma = AsyncMock() + mock_prisma.is_connected = MagicMock(return_value=True) mock_prisma.disconnect.side_effect = Exception("engine hung") mock_prisma._engine = None # No engine subprocess @@ -158,3 +161,35 @@ async def test_recreate_prisma_client_handles_missing_engine_pid( mock_kill.assert_not_called() # PID was 0, kill skipped mock_new_prisma.connect.assert_awaited_once() + + +def test_get_engine_pid_returns_zero_for_disconnected_client(disconnected_prisma): + """A disconnected client must read as "no engine" instead of raising, + otherwise the reconnect path can never recover.""" + wrapper = PrismaWrapper( + original_prisma=disconnected_prisma, iam_token_db_auth=False + ) + + assert wrapper._get_engine_pid() == 0 + + +@pytest.mark.asyncio +async def test_recreate_prisma_client_recovers_from_disconnected_client( + mock_prisma_binary, disconnected_prisma +): + """recreate_prisma_client must still build a replacement client when the + current one is disconnected.""" + wrapper = PrismaWrapper( + original_prisma=disconnected_prisma, iam_token_db_auth=False + ) + + mock_new_prisma = AsyncMock() + mock_prisma_binary.Prisma.return_value = mock_new_prisma + + with patch("os.kill") as mock_kill: + result = await wrapper.recreate_prisma_client("postgresql://new") + + assert result is True + mock_kill.assert_not_called() + assert wrapper._original_prisma is mock_new_prisma + mock_new_prisma.connect.assert_awaited_once() diff --git a/tests/test_litellm/proxy/db/test_prisma_planned_engine_restart.py b/tests/test_litellm/proxy/db/test_prisma_planned_engine_restart.py index 5e74004cc0b..9b382a41964 100644 --- a/tests/test_litellm/proxy/db/test_prisma_planned_engine_restart.py +++ b/tests/test_litellm/proxy/db/test_prisma_planned_engine_restart.py @@ -42,6 +42,7 @@ def mock_prisma_binary(): def _make_wrapper(engine_pid: int = 111, iam: bool = False) -> PrismaWrapper: mock_prisma = MagicMock() mock_prisma.connect = AsyncMock() + mock_prisma.is_connected = MagicMock(return_value=True) mock_prisma._engine = MagicMock() mock_prisma._engine.process.pid = engine_pid return PrismaWrapper(original_prisma=mock_prisma, iam_token_db_auth=iam) diff --git a/tests/test_litellm/proxy/db/test_prisma_self_heal.py b/tests/test_litellm/proxy/db/test_prisma_self_heal.py index 265940e51ed..7f723fa3ae0 100644 --- a/tests/test_litellm/proxy/db/test_prisma_self_heal.py +++ b/tests/test_litellm/proxy/db/test_prisma_self_heal.py @@ -20,7 +20,7 @@ def mock_prisma_binary(): """Mock prisma.Prisma to avoid requiring generated Prisma binaries for unit tests.""" mock_module = MagicMock() with patch.dict(sys.modules, {"prisma": mock_module}): - yield + yield mock_module @pytest.fixture @@ -513,3 +513,131 @@ async def test_engine_confirmed_dead_persists_across_failed_heavy_reconnect( # The flag must STILL be True so the next attempt re-enters the heavy # branch instead of silently demoting to the lightweight path. assert client._engine_confirmed_dead is True + + +@pytest.mark.asyncio +async def test_heavy_reconnect_recovers_from_disconnected_prisma_client( + mock_proxy_logging, mock_prisma_binary, disconnected_prisma +): + """Once the active Prisma client is in the disconnected state, every DB + call raises ClientNotConnectedError. The heavy reconnect path is the only + way out, so it must not re-raise that same error while inspecting the + broken client; otherwise `recreate_prisma_client` fails before it can + build a replacement and the proxy loops on failed reconnects forever. + + The full real reconnect path (attempt_db_reconnect -> _run_reconnect_cycle + -> recreate_prisma_client) must succeed from that wedged state. + """ + client = PrismaClient( + database_url="mock://test", proxy_logging_obj=mock_proxy_logging + ) + client.db._original_prisma = disconnected_prisma + client._engine_confirmed_dead = True + client._start_engine_watcher = AsyncMock() + + replacement = MagicMock() + replacement.connect = AsyncMock() + mock_prisma_binary.Prisma.return_value = replacement + + with patch.dict(os.environ, {"DATABASE_URL": "postgresql://test"}): + result = await client.attempt_db_reconnect( + reason="unit_test_disconnected_client", + force=True, + ) + + assert result is True + assert client.db._original_prisma is replacement + replacement.connect.assert_awaited_once() + assert client._consecutive_reconnect_failures == 0 + assert client._engine_confirmed_dead is False + + +@pytest.mark.asyncio +async def test_db_health_watchdog_should_reconnect_degraded_writer( + mock_proxy_logging, +): + """LIT-3792: when the proxy booted during a primary outage (reads served + by the replica, writer never connected), a healthy reader probe must not + mask the degraded writer — the watchdog drives the writer reconnect.""" + from litellm.proxy.db.routing_prisma_wrapper import RoutingPrismaWrapper + + client = PrismaClient( + database_url="mock://test", proxy_logging_obj=mock_proxy_logging + ) + writer = MagicMock() + reader = MagicMock() + reader.query_raw = AsyncMock(return_value=[{"result": 1}]) + routing = RoutingPrismaWrapper(writer=writer, reader=reader) + routing._writer_unavailable = True + client.db = routing + client.attempt_db_reconnect = AsyncMock(return_value=True) + client._db_health_watchdog_interval_seconds = 1 + client._db_watchdog_reconnect_timeout_seconds = 7.0 + client._db_health_watchdog_probe_timeout_seconds = 0.2 + + with patch( + "litellm.proxy.utils.asyncio.sleep", + AsyncMock(side_effect=[None, asyncio.CancelledError()]), + ): + await client._db_health_watchdog_loop() + + client.attempt_db_reconnect.assert_awaited_once_with( + reason="db_health_watchdog_writer_unavailable", + timeout_seconds=7.0, + ) + + +@pytest.mark.asyncio +async def test_db_health_watchdog_should_not_reconnect_healthy_writer( + mock_proxy_logging, +): + """A healthy probe with no degraded writer must not trigger reconnects.""" + from litellm.proxy.db.routing_prisma_wrapper import RoutingPrismaWrapper + + client = PrismaClient( + database_url="mock://test", proxy_logging_obj=mock_proxy_logging + ) + writer = MagicMock() + reader = MagicMock() + reader.query_raw = AsyncMock(return_value=[{"result": 1}]) + routing = RoutingPrismaWrapper(writer=writer, reader=reader) + client.db = routing + client.attempt_db_reconnect = AsyncMock(return_value=True) + client._db_health_watchdog_interval_seconds = 1 + client._db_health_watchdog_probe_timeout_seconds = 0.2 + + with patch( + "litellm.proxy.utils.asyncio.sleep", + AsyncMock(side_effect=[None, asyncio.CancelledError()]), + ): + await client._db_health_watchdog_loop() + + client.attempt_db_reconnect.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_direct_reconnect_probe_success_clears_writer_unavailable( + mock_proxy_logging, +): + """If the writer probe inside _do_direct_reconnect succeeds (engine already + reconnected by another path, e.g. an IAM token refresh), the early return + skips recreate_prisma_client — the degraded-writer flag must still be + cleared there or the watchdog fires reconnect attempts forever.""" + from litellm.proxy.db.routing_prisma_wrapper import RoutingPrismaWrapper + + client = PrismaClient( + database_url="mock://test", proxy_logging_obj=mock_proxy_logging + ) + writer = MagicMock() + writer.query_raw = AsyncMock(return_value=[{"result": 1}]) + reader = MagicMock() + routing = RoutingPrismaWrapper(writer=writer, reader=reader) + routing._writer_unavailable = True + client.db = routing + client._start_engine_watcher = AsyncMock() + + with patch.dict(os.environ, {"DATABASE_URL": "postgresql://test"}): + await client._run_reconnect_cycle(timeout_seconds=5.0) + + writer.query_raw.assert_awaited_once_with("SELECT 1") + assert routing.writer_unavailable is False diff --git a/tests/test_litellm/proxy/db/test_rds_iam_token_expiry.py b/tests/test_litellm/proxy/db/test_rds_iam_token_expiry.py index b92fd86ed7a..ca24f856022 100644 --- a/tests/test_litellm/proxy/db/test_rds_iam_token_expiry.py +++ b/tests/test_litellm/proxy/db/test_rds_iam_token_expiry.py @@ -26,25 +26,13 @@ class TestPrismaWrapperTokenRefresh: """Tests for the PrismaWrapper RDS IAM token refresh implementation.""" @pytest.fixture - def setup_env(self): + def setup_env(self, monkeypatch, unset_database_url): """Setup environment variables for testing.""" - os.environ["DATABASE_HOST"] = "test-host.rds.amazonaws.com" - os.environ["DATABASE_PORT"] = "5432" - os.environ["DATABASE_USER"] = "test_user" - os.environ["DATABASE_NAME"] = "test_db" - os.environ["IAM_TOKEN_DB_AUTH"] = "True" - yield - # Cleanup - for key in [ - "DATABASE_HOST", - "DATABASE_PORT", - "DATABASE_USER", - "DATABASE_NAME", - "DATABASE_URL", - "IAM_TOKEN_DB_AUTH", - "DATABASE_SCHEMA", - ]: - os.environ.pop(key, None) + monkeypatch.setenv("DATABASE_HOST", "test-host.rds.amazonaws.com") + monkeypatch.setenv("DATABASE_PORT", "5432") + monkeypatch.setenv("DATABASE_USER", "test_user") + monkeypatch.setenv("DATABASE_NAME", "test_db") + monkeypatch.setenv("IAM_TOKEN_DB_AUTH", "True") def _generate_mock_token(self, expires_in_seconds: int = 900) -> str: """Generate a mock IAM token with expiration info.""" @@ -172,22 +160,12 @@ class TestBackgroundRefreshLoop: """Tests for the background refresh loop timing.""" @pytest.fixture - def setup_env(self): + def setup_env(self, monkeypatch, unset_database_url): """Setup environment variables for testing.""" - os.environ["DATABASE_HOST"] = "test-host.rds.amazonaws.com" - os.environ["DATABASE_PORT"] = "5432" - os.environ["DATABASE_USER"] = "test_user" - os.environ["DATABASE_NAME"] = "test_db" - yield - # Cleanup - for key in [ - "DATABASE_HOST", - "DATABASE_PORT", - "DATABASE_USER", - "DATABASE_NAME", - "DATABASE_URL", - ]: - os.environ.pop(key, None) + monkeypatch.setenv("DATABASE_HOST", "test-host.rds.amazonaws.com") + monkeypatch.setenv("DATABASE_PORT", "5432") + monkeypatch.setenv("DATABASE_USER", "test_user") + monkeypatch.setenv("DATABASE_NAME", "test_db") @pytest.mark.asyncio async def test_calculate_seconds_fallback_when_no_url(self, setup_env): diff --git a/tests/test_litellm/proxy/db/test_routing_prisma_wrapper.py b/tests/test_litellm/proxy/db/test_routing_prisma_wrapper.py index efc3a6cf5b7..e5bb8b99507 100644 --- a/tests/test_litellm/proxy/db/test_routing_prisma_wrapper.py +++ b/tests/test_litellm/proxy/db/test_routing_prisma_wrapper.py @@ -626,7 +626,7 @@ async def test_getattr_does_not_block_inside_running_loop_on_expired_token(monke assert refresh_calls["count"] == 1 -def test_writer_get_rds_iam_token_defaults_port_when_unset(monkeypatch): +def test_writer_get_rds_iam_token_defaults_port_when_unset(monkeypatch, unset_database_url): """When DATABASE_PORT is unset, the writer must default to the Postgres standard port instead of passing `None` through. Passing None to `generate_iam_auth_token` makes botocore embed the literal string @@ -639,7 +639,6 @@ def test_writer_get_rds_iam_token_defaults_port_when_unset(monkeypatch): monkeypatch.setenv("DATABASE_USER", "litellm") monkeypatch.setenv("DATABASE_NAME", "litellm") monkeypatch.delenv("DATABASE_SCHEMA", raising=False) - monkeypatch.delenv("DATABASE_URL", raising=False) captured: Dict[str, Any] = {} @@ -661,7 +660,7 @@ def test_writer_get_rds_iam_token_defaults_port_when_unset(monkeypatch): assert ":5432/litellm" in (new_url or "") -def test_writer_get_rds_iam_token_uses_database_host_env_vars(monkeypatch): +def test_writer_get_rds_iam_token_uses_database_host_env_vars(monkeypatch, unset_database_url): """Writer's IAM path (no iam_endpoint configured) reads host/port/user/db from the legacy DATABASE_HOST/PORT/USER/NAME env vars and writes the URL back to DATABASE_URL — this is the pre-read-replica behavior the patch @@ -673,7 +672,6 @@ def test_writer_get_rds_iam_token_uses_database_host_env_vars(monkeypatch): monkeypatch.setenv("DATABASE_USER", "litellm") monkeypatch.setenv("DATABASE_NAME", "litellm") monkeypatch.setenv("DATABASE_SCHEMA", "public") - monkeypatch.delenv("DATABASE_URL", raising=False) captured: Dict[str, Any] = {} @@ -885,3 +883,111 @@ def test_prisma_client_init_falls_back_to_writer_when_reader_iam_token_fails( "Failed to initialize read replica Prisma client" in r.getMessage() for r in caplog.records ) + + +@pytest.mark.asyncio +async def test_connect_degrades_writer_when_reader_available(): + """A writer connect failure with a healthy reader must NOT abort proxy + startup (LIT-3792): startup swallows the raise when + allow_requests_on_db_unavailable is set, leaving the proxy with no Prisma + client at all, so DB-stored models never load and every request 400s. + Degrading instead keeps reads (key auth, model loads) on the replica.""" + from litellm.proxy.db.routing_prisma_wrapper import RoutingPrismaWrapper + + writer, writer_inner, reader, reader_inner = _make_wrappers() + writer_inner.connect = AsyncMock(side_effect=RuntimeError("primary unreachable")) + reader_inner.connect = AsyncMock() + routing = RoutingPrismaWrapper(writer=writer, reader=reader) + + # Must not raise — writer failure is non-fatal while the reader is up. + await routing.connect() + + assert routing.writer_unavailable is True + assert routing.reader_unavailable is False + writer_inner.connect.assert_awaited_once() + reader_inner.connect.assert_awaited_once() + + # Reads keep routing to the reader. + assert routing.query_raw is reader_inner.query_raw + + +@pytest.mark.asyncio +async def test_connect_raises_when_writer_and_reader_both_fail(): + """Full DB outage: with neither side reachable the wrapper must raise the + writer's error so existing allow_requests_on_db_unavailable startup + handling applies unchanged.""" + from litellm.proxy.db.routing_prisma_wrapper import RoutingPrismaWrapper + + writer, writer_inner, reader, reader_inner = _make_wrappers() + writer_inner.connect = AsyncMock(side_effect=RuntimeError("primary down")) + reader_inner.connect = AsyncMock(side_effect=RuntimeError("replica down")) + routing = RoutingPrismaWrapper(writer=writer, reader=reader) + + with pytest.raises(RuntimeError, match="primary down"): + await routing.connect() + + +@pytest.mark.asyncio +async def test_connect_logs_writer_degradation(caplog): + """Operators need a clear signal that the proxy booted without a writer.""" + from litellm.proxy.db.routing_prisma_wrapper import RoutingPrismaWrapper + + writer, writer_inner, reader, reader_inner = _make_wrappers() + writer_inner.connect = AsyncMock(side_effect=RuntimeError("primary unreachable")) + reader_inner.connect = AsyncMock() + routing = RoutingPrismaWrapper(writer=writer, reader=reader) + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + await routing.connect() + + assert any( + "Failed to connect to primary (writer) DB" in r.getMessage() + for r in caplog.records + ) + + +@pytest.mark.asyncio +async def test_recreate_clears_writer_unavailable(): + """A successful writer recreate (health watchdog reconnect once the + primary is back) must clear the degraded-writer flag.""" + from litellm.proxy.db.routing_prisma_wrapper import RoutingPrismaWrapper + + writer = MagicMock() + writer.recreate_prisma_client = AsyncMock(return_value=True) + reader = MagicMock() + reader.iam_token_db_auth = False + reader.recreate_prisma_client = AsyncMock() + + routing = RoutingPrismaWrapper(writer=writer, reader=reader) + routing._writer_unavailable = True + + with patch.dict(os.environ, {"DATABASE_URL_READ_REPLICA": "reader-url"}): + await routing.recreate_prisma_client("writer-url") + + assert routing.writer_unavailable is False + + +@pytest.mark.asyncio +async def test_recreate_keeps_writer_unavailable_when_writer_recreate_fails(): + """While the primary is still down, a failed writer recreate must leave + the degraded flag set so the watchdog keeps retrying.""" + from litellm.proxy.db.routing_prisma_wrapper import RoutingPrismaWrapper + + writer = MagicMock() + writer.recreate_prisma_client = AsyncMock( + side_effect=RuntimeError("primary still down") + ) + reader = MagicMock() + reader.iam_token_db_auth = False + reader.recreate_prisma_client = AsyncMock() + + routing = RoutingPrismaWrapper(writer=writer, reader=reader) + routing._writer_unavailable = True + + with ( + patch.dict(os.environ, {"DATABASE_URL_READ_REPLICA": "reader-url"}), + pytest.raises(RuntimeError, match="primary still down"), + ): + await routing.recreate_prisma_client("writer-url") + + assert routing.writer_unavailable is True diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py index bb079ea6580..452e2f666a9 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py @@ -2398,3 +2398,455 @@ class TestTracingFieldsE2E: # No detections, so these should be None assert slg.get("detection_method") is None assert slg.get("match_details") is None + + +class TestContentFilterMCPPreCall: + """Test pre_mcp_call support: MCP tool call argument scanning in apply_guardrail""" + + def test_pre_mcp_call_is_supported_event_hook(self): + """ + Constructing the guardrail with mode pre_mcp_call must succeed (LIT-4226) + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-mode", + event_hook=GuardrailEventHooks.pre_mcp_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + assert GuardrailEventHooks.pre_mcp_call in guardrail.supported_event_hooks + + @pytest.mark.asyncio + async def test_apply_guardrail_blocks_mcp_tool_arguments(self): + """ + Blocked word inside MCP tool call arguments raises HTTPException + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-block", + event_hook=GuardrailEventHooks.pre_mcp_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + request_data = { + "mcp_tool_name": "send_email", + "mcp_arguments": {"body": "this is confidential data"}, + } + + with pytest.raises(HTTPException) as exc_info: + await guardrail.apply_guardrail( + inputs={}, + request_data=request_data, + input_type="request", + ) + + assert exc_info.value.status_code == 400 + assert "confidential" in str(exc_info.value.detail) + + @pytest.mark.asyncio + async def test_apply_guardrail_masks_mcp_tool_arguments(self): + """ + MASK pattern match inside MCP arguments writes masked args to modified_arguments + """ + patterns = [ + ContentFilterPattern( + pattern_type="prebuilt", + pattern_name="email", + action=ContentFilterAction.MASK, + ), + ] + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-mask", + event_hook=GuardrailEventHooks.pre_mcp_call, + patterns=patterns, + ) + + request_data = { + "mcp_tool_name": "send_email", + "mcp_arguments": {"contact": "reach me at test@example.com"}, + } + + await guardrail.apply_guardrail( + inputs={}, + request_data=request_data, + input_type="request", + ) + + modified = request_data["modified_arguments"] + assert "[EMAIL_REDACTED]" in modified["contact"] + assert "test@example.com" not in modified["contact"] + assert request_data["mcp_arguments"] == modified + + @pytest.mark.asyncio + async def test_apply_guardrail_mcp_arguments_clean_pass(self): + """ + Clean MCP arguments pass through without modification + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-clean", + event_hook=GuardrailEventHooks.pre_mcp_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + request_data = { + "mcp_tool_name": "get_weather", + "mcp_arguments": {"city": "Paris"}, + } + + await guardrail.apply_guardrail( + inputs={}, + request_data=request_data, + input_type="request", + ) + + assert "modified_arguments" not in request_data + + @pytest.mark.asyncio + async def test_apply_guardrail_mcp_anchored_regex_matches_argument_value(self): + """ + Anchored custom regexes apply to each argument value, not to a serialized JSON document + """ + patterns = [ + ContentFilterPattern( + pattern_type="regex", + pattern="^secret$", + name="anchored_secret", + action=ContentFilterAction.BLOCK, + ), + ] + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-anchored", + event_hook=GuardrailEventHooks.pre_mcp_call, + patterns=patterns, + ) + + with pytest.raises(HTTPException) as exc_info: + await guardrail.apply_guardrail( + inputs={}, + request_data={"mcp_tool_name": "save_note", "mcp_arguments": {"note": "secret"}}, + input_type="request", + ) + + assert exc_info.value.status_code == 400 + + @pytest.mark.asyncio + async def test_apply_guardrail_mcp_mask_preserves_nested_structure(self): + """ + Masking rewrites string values inside nested dicts and lists without corrupting the structure + """ + patterns = [ + ContentFilterPattern( + pattern_type="prebuilt", + pattern_name="email", + action=ContentFilterAction.MASK, + ), + ] + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-nested-mask", + event_hook=GuardrailEventHooks.pre_mcp_call, + patterns=patterns, + ) + + request_data = { + "mcp_tool_name": "send_email", + "mcp_arguments": { + "recipients": ["team", "cc jane.doe@example.com"], + "meta": {"note": "from bob@example.com", "count": 2}, + }, + } + + await guardrail.apply_guardrail(inputs={}, request_data=request_data, input_type="request") + + modified = request_data["modified_arguments"] + assert modified["recipients"][0] == "team" + assert "[EMAIL_REDACTED]" in modified["recipients"][1] + assert "jane.doe@example.com" not in modified["recipients"][1] + assert "[EMAIL_REDACTED]" in modified["meta"]["note"] + assert modified["meta"]["count"] == 2 + assert request_data["mcp_arguments"] == modified + + @pytest.mark.asyncio + @pytest.mark.parametrize( + "mcp_arguments", + [ + {"this is confidential data": "x"}, + {"a": {"contains confidential stuff": "x"}}, + ], + ) + async def test_apply_guardrail_mcp_argument_keys_scanned(self, mcp_arguments): + """ + Blocked content smuggled in argument keys (top-level or nested) is detected, not just values + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-key-smuggling", + event_hook=GuardrailEventHooks.pre_mcp_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + with pytest.raises(HTTPException) as exc_info: + await guardrail.apply_guardrail( + inputs={}, + request_data={"mcp_tool_name": "send_email", "mcp_arguments": mcp_arguments}, + input_type="request", + ) + + assert exc_info.value.status_code == 400 + + @pytest.mark.asyncio + async def test_apply_guardrail_mcp_numeric_mask_match_blocks(self): + """ + A MASK rule matching a numeric argument blocks the call, since a redaction tag cannot be + represented in a number + """ + patterns = [ + ContentFilterPattern( + pattern_type="prebuilt", + pattern_name="visa", + action=ContentFilterAction.MASK, + ), + ] + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-numeric-mask", + event_hook=GuardrailEventHooks.pre_mcp_call, + patterns=patterns, + ) + + with pytest.raises(HTTPException) as exc_info: + await guardrail.apply_guardrail( + inputs={}, + request_data={"mcp_tool_name": "charge_card", "mcp_arguments": {"card": 4111111111111111}}, + input_type="request", + ) + + assert exc_info.value.status_code == 400 + assert "non-rewritable" in str(exc_info.value.detail) + + @pytest.mark.asyncio + async def test_apply_guardrail_mcp_arguments_exceeding_max_depth_block(self): + """ + Arguments nested beyond DEFAULT_MAX_RECURSE_DEPTH block fail-closed instead of passing unscanned + """ + from litellm.constants import DEFAULT_MAX_RECURSE_DEPTH + + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-depth-cap", + event_hook=GuardrailEventHooks.pre_mcp_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + deep: dict = {"leaf": "confidential data"} + for _ in range(DEFAULT_MAX_RECURSE_DEPTH + 1): + deep = {"level": deep} + + with pytest.raises(HTTPException) as exc_info: + await guardrail.apply_guardrail( + inputs={}, + request_data={"mcp_tool_name": "save_note", "mcp_arguments": deep}, + input_type="request", + ) + + assert exc_info.value.status_code == 400 + assert "nesting depth" in str(exc_info.value.detail) + + @pytest.mark.asyncio + async def test_apply_guardrail_mixed_mode_chat_call_not_scanned(self): + """ + A mixed pre_call + pre_mcp_call guardrail must not run the MCP scan on a chat invocation, + identified by the proxy-owned logging object's call_type, even when MCP keys are planted + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mixed-mode-chat", + event_hook=[GuardrailEventHooks.pre_call, GuardrailEventHooks.pre_mcp_call], + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + chat_logging_obj = MagicMock() + chat_logging_obj.call_type = "acompletion" + + request_data = { + "mcp_tool_name": "send_email", + "mcp_arguments": {"body": "this is confidential data"}, + "messages": [{"role": "user", "content": "hello"}], + } + + await guardrail.apply_guardrail( + inputs={"texts": ["hello"]}, + request_data=request_data, + input_type="request", + logging_obj=chat_logging_obj, + ) + + assert "modified_arguments" not in request_data + + @pytest.mark.asyncio + @pytest.mark.parametrize("mcp_call_type", [None, "call_mcp_tool"]) + async def test_apply_guardrail_mixed_mode_mcp_call_scanned(self, mcp_call_type): + """ + The same mixed mode guardrail still scans genuine MCP invocations, whether the logging + object is absent (canonical synthetic payload) or carries the MCP call_type + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mixed-mode-mcp", + event_hook=[GuardrailEventHooks.pre_call, GuardrailEventHooks.pre_mcp_call], + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + logging_obj = None + if mcp_call_type is not None: + logging_obj = MagicMock() + logging_obj.call_type = mcp_call_type + + with pytest.raises(HTTPException): + await guardrail.apply_guardrail( + inputs={}, + request_data={ + "mcp_tool_name": "send_email", + "mcp_arguments": {"body": "this is confidential data"}, + }, + input_type="request", + logging_obj=logging_obj, + ) + + @pytest.mark.asyncio + async def test_apply_guardrail_non_mcp_name_arguments_not_scanned(self): + """ + A raw body with top-level name + arguments (e.g. pass-through) is not treated as an MCP call; + only the canonical mcp_tool_name + mcp_arguments keys trigger the scan + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-non-mcp-shape", + event_hook=GuardrailEventHooks.pre_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + await guardrail.apply_guardrail( + inputs={"texts": ["hello"]}, + request_data={"name": "send_email", "arguments": {"body": "confidential data"}}, + input_type="request", + ) + + @pytest.mark.asyncio + async def test_apply_guardrail_mcp_response_side_not_scanned(self): + """ + input_type response does not run the MCP argument scan + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-response-side", + event_hook=GuardrailEventHooks.pre_mcp_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + await guardrail.apply_guardrail( + inputs={"texts": ["clean response"]}, + request_data={ + "mcp_tool_name": "send_email", + "mcp_arguments": {"body": "this is confidential data"}, + }, + input_type="response", + ) + @pytest.mark.asyncio + async def test_apply_guardrail_blocks_non_ascii_mcp_arguments(self): + """ + Non-ASCII blocked words in MCP arguments must be detected (json.dumps must not escape them) + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-non-ascii", + event_hook=GuardrailEventHooks.pre_mcp_call, + blocked_words=[BlockedWord(keyword="\u673a\u5bc6", action=ContentFilterAction.BLOCK)], + ) + + with pytest.raises(HTTPException) as exc_info: + await guardrail.apply_guardrail( + inputs={}, + request_data={ + "mcp_tool_name": "send_email", + "mcp_arguments": {"body": "this is \u673a\u5bc6 data"}, + }, + input_type="request", + ) + + assert exc_info.value.status_code == 400 + + @pytest.mark.asyncio + async def test_apply_guardrail_mcp_mask_survives_chained_guardrails(self): + """ + A second masking guardrail must scan the already-masked arguments, not resurrect the originals + """ + email_guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-chain-email", + event_hook=GuardrailEventHooks.pre_mcp_call, + patterns=[ + ContentFilterPattern(pattern_type="prebuilt", pattern_name="email", action=ContentFilterAction.MASK) + ], + ) + phone_guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-chain-phone", + event_hook=GuardrailEventHooks.pre_mcp_call, + patterns=[ + ContentFilterPattern(pattern_type="prebuilt", pattern_name="us_phone", action=ContentFilterAction.MASK) + ], + ) + + request_data = { + "mcp_tool_name": "send_email", + "mcp_arguments": {"contact": "email test@example.com phone 555-123-4567"}, + } + + await email_guardrail.apply_guardrail(inputs={}, request_data=request_data, input_type="request") + await phone_guardrail.apply_guardrail(inputs={}, request_data=request_data, input_type="request") + + final = request_data["modified_arguments"] + assert "test@example.com" not in final["contact"] + assert "555-123-4567" not in final["contact"] + assert "[EMAIL_REDACTED]" in final["contact"] + assert "[US_PHONE_REDACTED]" in final["contact"] + + @pytest.mark.asyncio + async def test_apply_guardrail_pre_call_mode_ignores_forged_mcp_keys(self): + """ + A pre_call mode guardrail must not run the MCP scan even when a caller plants MCP keys in the body + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-forged-mcp-keys", + event_hook=GuardrailEventHooks.pre_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + request_data = { + "mcp_tool_name": "send_email", + "mcp_arguments": {"body": "this is confidential data"}, + "messages": [{"role": "user", "content": "hello"}], + } + + await guardrail.apply_guardrail( + inputs={"texts": ["hello"]}, + request_data=request_data, + input_type="request", + ) + + assert "modified_arguments" not in request_data + assert request_data["mcp_arguments"] == {"body": "this is confidential data"} + + @pytest.mark.asyncio + @pytest.mark.parametrize( + "request_data", + [ + {"name": "send_email", "arguments": {"body": "confidential data"}}, + {"name": "send_email", "mcp_arguments": {"body": "confidential data"}}, + ], + ) + async def test_apply_guardrail_pre_mcp_mode_requires_canonical_keys(self, request_data): + """ + Even in pre_mcp_call mode, only the canonical mcp_tool_name key identifies an MCP call; + a bare name key must not, regardless of which arguments key accompanies it + """ + guardrail = ContentFilterGuardrail( + guardrail_name="test-mcp-canonical-keys", + event_hook=GuardrailEventHooks.pre_mcp_call, + blocked_words=[BlockedWord(keyword="confidential", action=ContentFilterAction.BLOCK)], + ) + + await guardrail.apply_guardrail( + inputs={}, + request_data=request_data, + input_type="request", + ) + assert "modified_arguments" not in request_data diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_aim.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_aim.py new file mode 100644 index 00000000000..2e83422074e --- /dev/null +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_aim.py @@ -0,0 +1,88 @@ +"""Tests for the AIM guardrail's inspection-payload construction.""" + +from litellm.proxy.guardrails.guardrail_hooks.aim.aim import AimGuardrail + + +def test_aim_inspection_messages_coerces_chat_completions_tool_role_to_user(): + """LIT-4294: A valid chat-completions ``role: "tool"`` message carries a + ``tool_call_id``, but the inspection flatten drops every field except + ``role`` and ``content``. A bare ``tool`` message without ``tool_call_id`` + is schema-invalid per the OpenAI chat schema, and the customer's writeup + reproduced AIM's ``/fw/v1/analyze`` returning 422 on exactly that shape. + The AIM POST collapses the role to ``user``; the outbound request to the + LLM is untouched.""" + data = { + "messages": [ + {"role": "user", "content": "weather in SF"}, + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "id": "c1", + "type": "function", + "function": {"name": "get_weather", "arguments": "{}"}, + } + ], + }, + {"role": "tool", "tool_call_id": "c1", "content": "sunny"}, + ] + } + assert AimGuardrail._build_aim_inspection_messages(data) == [ + {"role": "user", "content": "weather in SF"}, + {"role": "user", "content": "sunny"}, + ] + + +def test_aim_inspection_messages_coerces_non_standard_caller_role_to_user(): + """LIT-4294: A caller-supplied role outside {system, user, assistant} + (e.g. ``developer``, ``function``) is coerced to ``user`` for the AIM + POST, since AIM validates the payload against the OpenAI chat schema + and rejects unknown roles the same way it rejects bare ``tool``.""" + data = { + "messages": [ + {"role": "developer", "content": "system-ish instruction"}, + {"role": "user", "content": "normal user text"}, + ] + } + assert AimGuardrail._build_aim_inspection_messages(data) == [ + {"role": "user", "content": "system-ish instruction"}, + {"role": "user", "content": "normal user text"}, + ] + + +def test_aim_inspection_messages_coerces_responses_function_call_output_role(): + """LIT-4294: the shared helper synthesises ``role: "tool"`` for a + Responses ``function_call_output`` item (semantic equivalent of + chat-completions tool messages). AIM's schema-validating POST cannot + carry ``tool_call_id`` in the flat inspection payload, so AIM collapses + that ``tool`` role to ``user`` locally before POSTing.""" + data = { + "input": [ + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "sunny"}], + }, + ] + } + assert AimGuardrail._build_aim_inspection_messages(data) == [ + {"role": "user", "content": "sunny"}, + ] + + +def test_aim_inspection_messages_preserves_safe_roles(): + """Safe roles pass through untouched — the coercion only fires for + roles the OpenAI chat schema flatten cannot represent standalone.""" + data = { + "messages": [ + {"role": "system", "content": "be helpful"}, + {"role": "user", "content": "hi"}, + {"role": "assistant", "content": "hello"}, + ] + } + assert AimGuardrail._build_aim_inspection_messages(data) == [ + {"role": "system", "content": "be helpful"}, + {"role": "user", "content": "hi"}, + {"role": "assistant", "content": "hello"}, + ] diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py index f43d8e85aca..15827b80bcf 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py @@ -2767,3 +2767,510 @@ async def test_grounding_output_blocked_raises_400(): ) assert exc_info.value.status_code == 400 + + +############################################################################### +# LIT-4186: disable_exception_on_block regression tests +# +# Before the fix, a Bedrock block with disable_exception_on_block=True raised +# GuardrailInterventionNormalStringError, which no proxy code handled: the +# unified pre_call path re-raised it, so the client saw HTTP 500 with the block +# message; the native during_call hook swallowed it and set data["mock_response"], +# which was dead code because route_request already unpacked kwargs. +# +# The fix converts blocks to ModifyResponseException at the raise site inside +# make_bedrock_api_request. That exception is already the industry-standard +# proxy contract (caught in proxy_server.py, anthropic_endpoints, etc.) and +# turns into a 200 response whose content is the block message. +############################################################################### + + +def _blocked_bedrock_httpx_response() -> MagicMock: + response = MagicMock() + response.status_code = 200 + response.json.return_value = { + "action": "GUARDRAIL_INTERVENED", + "outputs": [{"text": "Sorry, the model cannot answer this question."}], + "assessments": [ + { + "topicPolicy": { + "topics": [{"name": "Denied", "type": "DENY", "action": "BLOCKED"}] + } + } + ], + } + return response + + +@pytest.mark.asyncio +async def test_make_bedrock_api_request_block_raises_modify_response_when_flag_set(): + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + request_data = {"model": "bedrock-nova-micro"} + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.make_bedrock_api_request( + source="INPUT", + messages=[{"role": "user", "content": "My name is John Doe"}], + request_data=request_data, + ) + + assert exc_info.value.message == "Sorry, the model cannot answer this question." + assert exc_info.value.model == "bedrock-nova-micro" + assert exc_info.value.guardrail_name == "test-bedrock-guard" + + +@pytest.mark.asyncio +async def test_make_bedrock_api_request_block_raises_http_400_when_flag_unset(): + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=False, + ) + + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(HTTPException) as exc_info: + await guardrail.make_bedrock_api_request( + source="INPUT", + messages=[{"role": "user", "content": "hi"}], + request_data={"model": "bedrock-nova-micro"}, + ) + + assert exc_info.value.status_code == 400 + + +@pytest.mark.asyncio +async def test_async_pre_call_hook_propagates_modify_response_on_block(): + """pre_call: block with disable_exception_on_block=True must raise + ModifyResponseException so the endpoint handler returns 200 with the block + message. Before LIT-4186 the exception was swallowed and only data + ["mock_response"] was mutated, which the unified pre_call path never read + (surfaced as HTTP 500).""" + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + request_data = { + "model": "bedrock-nova-micro", + "messages": [{"role": "user", "content": "My name is John Doe"}], + } + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.async_pre_call_hook( + user_api_key_dict=UserAPIKeyAuth(), + cache=DualCache(), + data=request_data, + call_type="acompletion", + ) + + assert exc_info.value.message == "Sorry, the model cannot answer this question." + # No `mock_response` mutation: the old broken contract must be gone + # (route_request unpacks kwargs before this hook runs, so `mock_response` + # would never reach the LLM call anyway). + assert "mock_response" not in request_data + + +@pytest.mark.asyncio +async def test_async_moderation_hook_propagates_modify_response_on_block(): + """during_call: block must raise ModifyResponseException from the moderation + task so the surrounding asyncio.gather cancels the LLM call, instead of + the old behavior of swallowing the block and letting the model call proceed + (LIT-4186 symptom 2: silent bypass, model billed).""" + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + request_data = { + "model": "bedrock-nova-micro", + "messages": [{"role": "user", "content": "My name is John Doe"}], + } + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.async_moderation_hook( + data=request_data, + user_api_key_dict=UserAPIKeyAuth(), + call_type="acompletion", + ) + + assert exc_info.value.message == "Sorry, the model cannot answer this question." + + +@pytest.mark.asyncio +async def test_async_post_call_success_hook_attaches_original_response_on_block(): + """post_call: block must raise ModifyResponseException and attach the LLM + response to `original_response` so the synthetic block reply reports the + upstream call's real token usage instead of zero.""" + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + request_data = { + "model": "bedrock-nova-micro", + "messages": [{"role": "user", "content": "hi"}], + } + llm_response = _model_response("Hello John Doe! The capital of France is Paris.") + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object( + guardrail.async_handler, "post", new_callable=AsyncMock + ) as mock_post, + patch.object( + guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1") + ), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.async_post_call_success_hook( + data=request_data, + user_api_key_dict=UserAPIKeyAuth(), + response=llm_response, + ) + + assert exc_info.value.original_response is llm_response + + +@pytest.mark.asyncio +async def test_apply_guardrail_propagates_modify_response_on_block(): + """apply_guardrail (unified path used by pre_call / /apply_guardrail + endpoint) must let ModifyResponseException propagate as-is so the endpoint + handler catches it and returns a 200.""" + from litellm.exceptions import ModifyResponseException + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + with patch.object( + guardrail, "make_bedrock_api_request", new_callable=AsyncMock + ) as mock_api: + mock_api.side_effect = ModifyResponseException( + message="Sorry, the model cannot answer this question.", + model="bedrock-nova-micro", + request_data={}, + guardrail_name="test-bedrock-guard", + ) + + with pytest.raises(ModifyResponseException) as exc_info: + await guardrail.apply_guardrail( + inputs={"texts": ["My name is John Doe"]}, + request_data={"model": "bedrock-nova-micro"}, + input_type="request", + ) + + assert exc_info.value.message == "Sorry, the model cannot answer this question." + + +@pytest.mark.asyncio +async def test_streaming_post_call_block_yields_synthetic_stream_not_raise(): + """LIT-4186 regression: with disable_exception_on_block=True, streaming + post_call blocks must be delivered as a synthetic stream (finish_reason= + content_filter, block message as content), NOT raised. Pre-fix the local + handler already produced this shape; the LIT-4186 refactor briefly turned + it into an SSE 500 by letting ModifyResponseException escape the streaming + generator. This test locks in the correct streaming contract. + """ + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + async def _stream(): + yield ModelResponseStream( + choices=[ + StreamingChoices( + index=0, + delta=Delta(role="assistant", content="Coffee is a popular"), + ) + ] + ) + yield ModelResponseStream( + choices=[StreamingChoices(index=0, delta=Delta(content=" beverage."), finish_reason="stop")] + ) + + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object(guardrail.async_handler, "post", new_callable=AsyncMock) as mock_post, + patch.object(guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1")), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + chunks = [ + c + async for c in guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=UserAPIKeyAuth(), + response=_stream(), + request_data={"model": "bedrock-nova-micro"}, + ) + ] + + assert chunks, "streaming block should yield synthetic chunks, not error out" + assembled_content = "".join( + (c.choices[0].delta.content or "") + for c in chunks + if getattr(c, "choices", None) and getattr(c.choices[0], "delta", None) + ) + assert assembled_content == "Sorry, the model cannot answer this question." + assert chunks[-1].choices[0].finish_reason == "content_filter" + + +@pytest.mark.asyncio +async def test_streaming_post_call_block_preserves_upstream_usage(): + """LIT-4186: streaming block must report the usage the upstream LLM call + actually consumed. Non-streaming blocks carry it via original_response + + _blocked_response_usage in the endpoint handler; streaming has to copy it + onto the synthetic ModelResponse directly since the exception can't escape + the SSE generator. Without this, clients see accurate billing on + non-streaming blocks and zero on streaming blocks -- silent revenue leak.""" + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices, Usage + + guardrail = BedrockGuardrail( + guardrail_name="test-bedrock-guard", + guardrailIdentifier="test-guardrail", + guardrailVersion="DRAFT", + disable_exception_on_block=True, + ) + + async def _stream_with_usage(): + # Terminal chunk carrying usage, as OpenAI-style streams do with + # stream_options={"include_usage": True}. stream_chunk_builder + # aggregates this into the assembled ModelResponse's .usage. + yield ModelResponseStream( + choices=[StreamingChoices(index=0, delta=Delta(role="assistant", content="Coffee is delicious"))] + ) + yield ModelResponseStream( + choices=[StreamingChoices(index=0, delta=Delta(content=""), finish_reason="stop")], + usage=Usage(prompt_tokens=42, completion_tokens=17, total_tokens=59), + ) + + mock_credentials = MagicMock() + mock_credentials.access_key = "k" + mock_credentials.secret_key = "s" + mock_credentials.token = None + + with ( + patch.object(guardrail.async_handler, "post", new_callable=AsyncMock) as mock_post, + patch.object(guardrail, "_load_credentials", return_value=(mock_credentials, "us-east-1")), + patch.object(guardrail, "_prepare_request", return_value=MagicMock()), + ): + mock_post.return_value = _blocked_bedrock_httpx_response() + + chunks = [ + c + async for c in guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=UserAPIKeyAuth(), + response=_stream_with_usage(), + request_data={"model": "bedrock-nova-micro"}, + ) + ] + + # Find the chunk carrying usage (MockResponseIterator emits it on the + # terminating chunk when the source ModelResponse has .usage set) + usage_chunks = [c for c in chunks if getattr(c, "usage", None) is not None] + assert usage_chunks, "streaming block should carry the upstream call's usage on at least one chunk" + reported_usage = usage_chunks[-1].usage + assert reported_usage.prompt_tokens == 42 + assert reported_usage.completion_tokens == 17 + assert reported_usage.total_tokens == 59 + + +############################################################################### +# Regression test for the streaming logging_obj bug found during live testing. +# +# post_call_failure_hook (proxy_server.py) pops litellm_logging_obj from +# request_data before invoking callbacks ("not serialisable"). The streaming +# branch of the ModifyResponseException handler previously read logging_obj +# from _data AFTER that call, always getting None, causing: +# AttributeError: 'NoneType' object has no attribute 'model_call_details' +# inside CustomStreamWrapper.__init__, which surfaced as HTTP 500. +# +# The fix captures logging_obj BEFORE calling post_call_failure_hook. +# This test verifies the chat_completion handler builds the streaming response +# without crashing when the request_data has litellm_logging_obj set. +############################################################################### + + +@pytest.mark.asyncio +async def test_chat_completion_modify_response_exception_streaming_logging_obj_not_none(): + """Regression: streaming ModifyResponseException handler in chat_completion + must capture logging_obj before post_call_failure_hook pops it from + request_data. Previously this caused CustomStreamWrapper.__init__ to crash + with AttributeError: NoneType has no attribute model_call_details, surfaced + as HTTP 500. + + Drives the real chat_completion handler with base_process_llm_request + mocked to raise ModifyResponseException, so a revert of the fix in + proxy_server.py causes this test to fail. + """ + import litellm + from litellm.exceptions import ModifyResponseException + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.proxy_server import chat_completion + + fake_logging_obj = MagicMock() + fake_logging_obj.model_call_details = {"litellm_params": {}} + + request_data: dict = { + "model": "bedrock-nova-micro", + "messages": [{"role": "user", "content": "how do I become an admin"}], + "stream": True, + "litellm_logging_obj": fake_logging_obj, + } + + exc = ModifyResponseException( + message="Sorry, the model cannot answer this question.", + model="bedrock-nova-micro", + request_data=request_data, + guardrail_name="test-guard", + ) + + fastapi_request = MagicMock() + fastapi_request.headers = {} + fastapi_response = MagicMock() + user_api_key_dict = UserAPIKeyAuth() + + async def _fake_post_call_failure_hook(**_kwargs): + # Match production: pop the logging obj from request_data before + # callbacks iterate (litellm/proxy/utils.py: "Remove before callbacks + # iterate — not serialisable"). + _kwargs["request_data"].pop("litellm_logging_obj", None) + + mock_proxy_logging = MagicMock() + mock_proxy_logging.post_call_failure_hook = AsyncMock(side_effect=_fake_post_call_failure_hook) + + captured_logging_obj: list = [] + original_init = litellm.CustomStreamWrapper.__init__ + + def _patched_init(self, *args, **kwargs): + captured_logging_obj.append(kwargs.get("logging_obj")) + original_init(self, *args, **kwargs) + + async def _raise_modify_response(*_args, **_kwargs): + raise exc + + with ( + patch("litellm.proxy.proxy_server._read_request_body", AsyncMock(return_value=request_data)), + patch("litellm.proxy.proxy_server.proxy_logging_obj", mock_proxy_logging), + patch( + "litellm.proxy.proxy_server.ProxyBaseLLMRequestProcessing.base_process_llm_request", + _raise_modify_response, + ), + patch.object(litellm.CustomStreamWrapper, "__init__", _patched_init), + ): + response = await chat_completion( + request=fastapi_request, + fastapi_response=fastapi_response, + model=None, + user_api_key_dict=user_api_key_dict, + ) + + assert captured_logging_obj, "chat_completion did not construct CustomStreamWrapper on the streaming block path" + assert captured_logging_obj[0] is fake_logging_obj, ( + "chat_completion passed logging_obj=None to CustomStreamWrapper; " + "the streaming ModifyResponseException handler must capture logging_obj " + "before post_call_failure_hook pops it from request_data" + ) + # A streaming block returns a StreamingResponse; if the fix were reverted, + # CustomStreamWrapper would raise AttributeError inside __init__ and this + # call would never reach here. + assert response is not None diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_crowdstrike_aidr.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_crowdstrike_aidr.py index f8fd9a0a185..a1c3186e0b9 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_crowdstrike_aidr.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_crowdstrike_aidr.py @@ -93,9 +93,7 @@ async def test_apply_guardrail_request_blocked( ], } request_data = {"messages": inputs["structured_messages"]} - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -108,9 +106,7 @@ async def test_apply_guardrail_request_blocked( ), ), ) as mock_method: - with pytest.raises( - HTTPException, match="Violated CrowdStrike AIDR guardrail policy" - ): + with pytest.raises(HTTPException, match="Violated CrowdStrike AIDR guardrail policy"): await crowdstrike_aidr_guardrail.apply_guardrail( inputs=inputs, request_data=request_data, @@ -121,10 +117,7 @@ async def test_apply_guardrail_request_blocked( called_kwargs = mock_method.call_args.kwargs assert called_kwargs["json"]["event_type"] == "input" # Should include messages - assert ( - called_kwargs["json"]["guard_input"]["messages"] - == inputs["structured_messages"] - ) + assert called_kwargs["json"]["guard_input"]["messages"] == inputs["structured_messages"] @pytest.mark.asyncio @@ -141,9 +134,7 @@ async def test_apply_guardrail_request_transformed( ], } request_data = {"messages": inputs["structured_messages"]} - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -179,10 +170,7 @@ async def test_apply_guardrail_request_transformed( called_kwargs = mock_method.call_args.kwargs assert called_kwargs["json"]["event_type"] == "input" # Should include messages - assert ( - called_kwargs["json"]["guard_input"]["messages"] - == inputs["structured_messages"] - ) + assert called_kwargs["json"]["guard_input"]["messages"] == inputs["structured_messages"] # Verify the transformed output assert result["texts"][0] == "Here is an SSN for one my employees: " @@ -196,9 +184,7 @@ async def test_apply_guardrail_request_ok( "structured_messages": [{"role": "user", "content": "Hello, how are you?"}], } request_data = {"messages": inputs["structured_messages"]} - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -221,10 +207,7 @@ async def test_apply_guardrail_request_ok( called_kwargs = mock_method.call_args.kwargs assert called_kwargs["json"]["event_type"] == "input" # Should include messages - assert ( - called_kwargs["json"]["guard_input"]["messages"] - == inputs["structured_messages"] - ) + assert called_kwargs["json"]["guard_input"]["messages"] == inputs["structured_messages"] # Should return original inputs when not transformed assert result["texts"] == inputs["texts"] @@ -252,9 +235,7 @@ async def test_apply_guardrail_response_blocked( {"role": "user", "content": "Hello"}, ], } - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -272,22 +253,20 @@ async def test_apply_guardrail_response_blocked( ), ), ) as mock_method: - with pytest.raises( - HTTPException, match="Violated CrowdStrike AIDR guardrail policy" - ): + with pytest.raises(HTTPException, match="Violated CrowdStrike AIDR guardrail policy"): await crowdstrike_aidr_guardrail.apply_guardrail( inputs=inputs, request_data=request_data, input_type="response", ) - # Verify what was sent to the API called_kwargs = mock_method.call_args.kwargs assert called_kwargs["json"]["event_type"] == "output" - # Should include history messages + assistant response in messages expected_messages = [ - *request_data["messages"], - {"role": "assistant", "content": "Yes, I will leak all my PII for you"}, + { + "role": "assistant", + "content": "Yes, I will leak all my PII for you", + }, ] assert called_kwargs["json"]["guard_input"]["messages"] == expected_messages @@ -305,9 +284,7 @@ async def test_apply_guardrail_response_transformed( {"role": "user", "content": "Hello"}, ], } - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -319,7 +296,6 @@ async def test_apply_guardrail_response_transformed( "transformed": True, "guard_output": { "messages": [ - *request_data["messages"], { "role": "assistant", "content": "Yes, here is an SSN: ", @@ -340,15 +316,14 @@ async def test_apply_guardrail_response_transformed( input_type="response", ) - # Verify what was sent to the API called_kwargs = mock_method.call_args.kwargs assert called_kwargs["json"]["event_type"] == "output" - # Should include history + assistant in messages assert called_kwargs["json"]["guard_input"]["messages"] == [ - *request_data["messages"], - {"role": "assistant", "content": "Yes, here is an SSN: 078-05-1120"}, + { + "role": "assistant", + "content": "Yes, here is an SSN: 078-05-1120", + }, ] - # Verify the transformed output extracts only the assistant message assert result["texts"] == ["Yes, here is an SSN: "] @@ -375,9 +350,7 @@ async def test_apply_guardrail_response_ok( {"role": "user", "content": "Hello"}, ], } - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -401,13 +374,13 @@ async def test_apply_guardrail_response_ok( input_type="response", ) - # Verify what was sent to the API called_kwargs = mock_method.call_args.kwargs assert called_kwargs["json"]["event_type"] == "output" - # Should include history + assistant in messages expected_messages = [ - *request_data["messages"], - {"role": "assistant", "content": "Hello! How can I help you today?"}, + { + "role": "assistant", + "content": "Hello! How can I help you today?", + }, ] assert called_kwargs["json"]["guard_input"]["messages"] == expected_messages # Should return original inputs when not transformed @@ -431,9 +404,7 @@ async def test_apply_guardrail_sends_user_id_model_and_extra_info( "user_api_key_user_email": "alice@example.com", }, } - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -472,9 +443,7 @@ async def test_apply_guardrail_empty_extra_info_when_no_email( "user_api_key_user_email": None, }, } - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -505,9 +474,7 @@ async def test_apply_guardrail_no_metadata_skips_user_fields( "structured_messages": [{"role": "user", "content": "Hello"}], } request_data = {"messages": inputs["structured_messages"]} - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -533,12 +500,41 @@ async def test_apply_guardrail_no_metadata_skips_user_fields( @pytest.mark.parametrize( "litellm_metadata, metadata", [ - (None, {"user_api_key_user_id": "uid-abc", "user_api_key_user_email": "alice@example.com"}), - ({"trace_id": "t1"}, {"user_api_key_user_id": "uid-abc", "user_api_key_user_email": "alice@example.com"}), - (["unexpected"], {"user_api_key_user_id": "uid-abc", "user_api_key_user_email": "alice@example.com"}), - ({"user_api_key_user_id": "uid-abc", "user_api_key_user_email": "alice@example.com"}, {"trace_id": "t1"}), + ( + None, + { + "user_api_key_user_id": "uid-abc", + "user_api_key_user_email": "alice@example.com", + }, + ), + ( + {"trace_id": "t1"}, + { + "user_api_key_user_id": "uid-abc", + "user_api_key_user_email": "alice@example.com", + }, + ), + ( + ["unexpected"], + { + "user_api_key_user_id": "uid-abc", + "user_api_key_user_email": "alice@example.com", + }, + ), + ( + { + "user_api_key_user_id": "uid-abc", + "user_api_key_user_email": "alice@example.com", + }, + {"trace_id": "t1"}, + ), + ], + ids=[ + "identity_in_metadata_llm_none", + "identity_in_metadata_llm_user_dict", + "identity_in_metadata_llm_non_mapping", + "identity_in_litellm_metadata", ], - ids=["identity_in_metadata_llm_none", "identity_in_metadata_llm_user_dict", "identity_in_metadata_llm_non_mapping", "identity_in_litellm_metadata"], ) async def test_apply_guardrail_reads_identity_from_either_metadata_bag( crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, @@ -556,9 +552,7 @@ async def test_apply_guardrail_reads_identity_from_either_metadata_bag( "litellm_metadata": litellm_metadata, "metadata": metadata, } - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -593,17 +587,13 @@ async def test_apply_guardrail_request_skipped_messages_stay_aligned( {"role": "user", "content": "Hello, help me with my task"}, { "role": "tool", - "content": [ - {"type": "tool_result", "tool_use_id": "t1", "content": "ok"} - ], + "content": [{"type": "tool_result", "tool_use_id": "t1", "content": "ok"}], }, {"role": "user", "content": "Here is my SSN: 078-05-1120"}, ], } request_data = {"messages": inputs["structured_messages"]} - guardrail_endpoint = ( - f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" - ) + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", @@ -644,4 +634,677 @@ async def test_apply_guardrail_request_skipped_messages_stay_aligned( assert result["texts"][0] == "Hello, help me with my task" assert result["texts"][1] == "" assert result["texts"][2] == "Here is my SSN: " - assert result["structured_messages"] == inputs["structured_messages"] + assert result["structured_messages"] == [ + {"role": "user", "content": "Hello, help me with my task"}, + {"role": "tool", "content": [{"type": "tool_result", "tool_use_id": "t1", "content": "ok"}]}, + {"role": "user", "content": "Here is my SSN: "}, + ] + + +class TestMessageFiltering: + """Verify that only new messages since the last assistant response are sent to CrowdStrike.""" + + @pytest.mark.asyncio + async def test_last_message_is_assistant_sends_system_plus_that_message( + self, crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler + ) -> None: + structured_messages = [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "Hi"}, + {"role": "assistant", "content": "Hello!"}, + {"role": "user", "content": "Tell me a joke"}, + {"role": "assistant", "content": "Why did the chicken cross the road?"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["Why did the chicken cross the road?"], + "structured_messages": structured_messages, + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data={"messages": structured_messages}, + input_type="request", + ) + + sent = mock_method.call_args.kwargs["json"]["guard_input"]["messages"] + assert sent == [ + {"role": "system", "content": "You are helpful"}, + {"role": "assistant", "content": "Why did the chicken cross the road?"}, + ] + + @pytest.mark.asyncio + async def test_last_message_is_user_sends_system_plus_messages_after_assistant( + self, crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler + ) -> None: + structured_messages = [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "Hi"}, + {"role": "assistant", "content": "Hello!"}, + {"role": "user", "content": "Tell me a joke"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["Tell me a joke"], + "structured_messages": structured_messages, + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data={"messages": structured_messages}, + input_type="request", + ) + + sent = mock_method.call_args.kwargs["json"]["guard_input"]["messages"] + assert sent == [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "Tell me a joke"}, + ] + + @pytest.mark.asyncio + async def test_no_prior_assistant_sends_all_messages( + self, crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler + ) -> None: + structured_messages = [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "Hi"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["Hi"], + "structured_messages": structured_messages, + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data={"messages": structured_messages}, + input_type="request", + ) + + sent = mock_method.call_args.kwargs["json"]["guard_input"]["messages"] + assert sent == [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "Hi"}, + ] + + @pytest.mark.asyncio + async def test_multiple_user_messages_after_assistant( + self, crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler + ) -> None: + structured_messages = [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "Hi"}, + {"role": "assistant", "content": "Hello!"}, + {"role": "user", "content": "First question"}, + {"role": "user", "content": "Second question"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["First question", "Second question"], + "structured_messages": structured_messages, + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data={"messages": structured_messages}, + input_type="request", + ) + + sent = mock_method.call_args.kwargs["json"]["guard_input"]["messages"] + assert sent == [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "First question"}, + {"role": "user", "content": "Second question"}, + ] + + @pytest.mark.asyncio + async def test_system_message_after_assistant_included( + self, crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler + ) -> None: + structured_messages = [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "Hi"}, + {"role": "assistant", "content": "Hello!"}, + {"role": "system", "content": "New instructions"}, + {"role": "user", "content": "Do something"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["Do something"], + "structured_messages": structured_messages, + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data={"messages": structured_messages}, + input_type="request", + ) + + sent = mock_method.call_args.kwargs["json"]["guard_input"]["messages"] + assert sent == [ + {"role": "system", "content": "You are helpful"}, + {"role": "system", "content": "New instructions"}, + {"role": "user", "content": "Do something"}, + ] + + @pytest.mark.asyncio + async def test_no_system_messages(self, crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler) -> None: + structured_messages = [ + {"role": "user", "content": "Hi"}, + {"role": "assistant", "content": "Hello!"}, + {"role": "user", "content": "Bye"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["Bye"], + "structured_messages": structured_messages, + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data={"messages": structured_messages}, + input_type="request", + ) + + sent = mock_method.call_args.kwargs["json"]["guard_input"]["messages"] + assert sent == [ + {"role": "user", "content": "Bye"}, + ] + + +@pytest.mark.asyncio +async def test_apply_guardrail_request_sends_only_new_messages( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + structured_messages = [ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": "What is 2+2?"}, + {"role": "assistant", "content": "4"}, + {"role": "user", "content": "Here is my SSN: 078-05-1120"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["Here is my SSN: 078-05-1120"], + "structured_messages": structured_messages, + } + request_data = {"messages": structured_messages} + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data=request_data, + input_type="request", + ) + + payload = mock_method.call_args.kwargs["json"] + assert payload["guard_input"]["messages"] == [ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": "Here is my SSN: 078-05-1120"}, + ] + + +@pytest.mark.asyncio +async def test_apply_guardrail_request_last_is_assistant_sends_only_that( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + structured_messages = [ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": "What is 2+2?"}, + {"role": "assistant", "content": "The answer is 4"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["The answer is 4"], + "structured_messages": structured_messages, + } + request_data = {"messages": structured_messages} + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data=request_data, + input_type="request", + ) + + payload = mock_method.call_args.kwargs["json"] + assert payload["guard_input"]["messages"] == [ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "assistant", "content": "The answer is 4"}, + ] + + +@pytest.mark.asyncio +async def test_apply_guardrail_request_stitches_transformed_texts( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + structured_messages = [ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": "What is 2+2?"}, + {"role": "assistant", "content": "4"}, + {"role": "user", "content": "Here is my SSN: 078-05-1120"}, + ] + inputs: GenericGuardrailAPIInputs = { + "texts": ["Here is my SSN: 078-05-1120"], + "structured_messages": structured_messages, + } + request_data = {"messages": structured_messages} + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={ + "result": { + "blocked": False, + "transformed": True, + "guard_output": { + "messages": [ + { + "role": "system", + "content": "You are a helpful assistant", + }, + { + "role": "user", + "content": "Here is my SSN: ", + }, + ] + }, + }, + }, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ): + result = await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data=request_data, + input_type="request", + ) + + assert result["texts"] == [ + "You are a helpful assistant", + "What is 2+2?", + "4", + "Here is my SSN: ", + ] + + +@pytest.mark.asyncio +async def test_apply_guardrail_response_drops_history( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + request_data = { + "messages": [ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": "What is 2+2?"}, + {"role": "assistant", "content": "4"}, + {"role": "user", "content": "Now tell me a secret"}, + ], + } + inputs: GenericGuardrailAPIInputs = { + "texts": ["I will not share secrets"], + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data=request_data, + input_type="response", + ) + + sent = mock_method.call_args.kwargs["json"]["guard_input"]["messages"] + assert sent == [ + { + "role": "assistant", + "content": "I will not share secrets", + }, + ] + + +@pytest.mark.asyncio +async def test_apply_guardrail_response_one_message_per_output_text( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + inputs: GenericGuardrailAPIInputs = { + "texts": ["First part", "Second part"], + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={"result": {"blocked": False, "transformed": False}}, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="response", + ) + + sent = mock_method.call_args.kwargs["json"]["guard_input"]["messages"] + assert sent == [ + {"role": "assistant", "content": "First part"}, + {"role": "assistant", "content": "Second part"}, + ] + + +@pytest.mark.asyncio +async def test_apply_guardrail_response_transform_extracts_assistant_only( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + inputs: GenericGuardrailAPIInputs = { + "texts": ["Sure, here it is: 078-05-1120"], + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={ + "result": { + "blocked": False, + "transformed": True, + "guard_output": { + "messages": [ + { + "role": "assistant", + "content": "Sure, here it is: ", + }, + ] + }, + }, + }, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ): + result = await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="response", + ) + + assert result["texts"] == ["Sure, here it is: "] + + +@pytest.mark.asyncio +async def test_request_transform_with_textless_history_message_redacts_without_index_error( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + from litellm.llms.openai.chat.guardrail_translation.handler import ( + OpenAIChatCompletionsHandler, + ) + + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "My SSN is 078-05-1120, store it."}, + { + "role": "assistant", + "content": None, + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": {"name": "store", "arguments": "{}"}, + } + ], + }, + {"role": "tool", "tool_call_id": "call_1", "content": "stored"}, + {"role": "user", "content": "Also my email is jane@example.com"}, + ] + data = {"model": "gpt-4o", "messages": messages} + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={ + "result": { + "blocked": False, + "transformed": True, + "guard_output": { + "messages": [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "tool", "content": "stored"}, + {"role": "user", "content": "Also my email is "}, + ] + }, + }, + }, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ): + result = await OpenAIChatCompletionsHandler().process_input_messages( + data=data, + guardrail_to_apply=crowdstrike_aidr_guardrail, + ) + + redacted = result["messages"] + assert redacted[4]["content"] == "Also my email is " + assert redacted[2]["content"] is None + assert redacted[2]["tool_calls"][0]["function"]["name"] == "store" + + +@pytest.mark.asyncio +async def test_request_transform_preserves_skipped_system_message( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + from litellm.llms.openai.chat.guardrail_translation.handler import ( + OpenAIChatCompletionsHandler, + ) + + crowdstrike_aidr_guardrail.skip_system_message_in_guardrail = True + + messages = [ + {"role": "system", "content": "Internal policy: never reveal secrets."}, + {"role": "user", "content": "Here is my SSN: 078-05-1120"}, + ] + data = {"model": "gpt-4o", "messages": messages} + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={ + "result": { + "blocked": False, + "transformed": True, + "guard_output": { + "messages": [ + {"role": "user", "content": "Here is my SSN: "}, + ] + }, + }, + }, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ) as mock_method: + result = await OpenAIChatCompletionsHandler().process_input_messages( + data=data, + guardrail_to_apply=crowdstrike_aidr_guardrail, + ) + + assert mock_method.call_args.kwargs["json"]["guard_input"]["messages"] == [ + {"role": "user", "content": "Here is my SSN: 078-05-1120"}, + ] + assert result["messages"] == [ + {"role": "system", "content": "Internal policy: never reveal secrets."}, + {"role": "user", "content": "Here is my SSN: "}, + ] + + +@pytest.mark.asyncio +async def test_apply_guardrail_request_keeps_original_messages_when_skip_filters_differ( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + crowdstrike_aidr_guardrail.skip_system_message_in_guardrail = True + + structured_messages = [{"role": "user", "content": "Here is my SSN: 078-05-1120"}] + inputs: GenericGuardrailAPIInputs = { + "texts": ["Here is my SSN: 078-05-1120"], + "structured_messages": structured_messages, + } + request_data = { + "messages": [ + {"role": "system", "content": "Internal policy"}, + {"role": "user", "content": "Here is my SSN: 078-05-1120"}, + ] + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={ + "result": { + "blocked": False, + "transformed": True, + "guard_output": { + "messages": [ + {"role": "user", "content": "Here is my SSN: "}, + ] + }, + }, + }, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ): + result = await crowdstrike_aidr_guardrail.apply_guardrail( + inputs=inputs, + request_data=request_data, + input_type="request", + ) + + assert result["structured_messages"] is structured_messages + assert result["texts"] == ["Here is my SSN: "] + + +@pytest.mark.asyncio +async def test_anthropic_tool_calling_transform_redacts_without_index_error( + crowdstrike_aidr_guardrail: CrowdStrikeAIDRHandler, +) -> None: + import json + + from litellm.llms.anthropic.chat.guardrail_translation.handler import ( + AnthropicMessagesHandler, + ) + + data = { + "model": "claude-3-5-sonnet-20241022", + "max_tokens": 128, + "messages": [ + {"role": "user", "content": "My SSN is 078-05-1120. Look it up."}, + { + "role": "assistant", + "content": [{"type": "tool_use", "id": "tu1", "name": "lookup", "input": {"q": "ssn"}}], + }, + {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "tu1", "content": "stored"}]}, + {"role": "user", "content": "Also my email is jane.doe@example.com"}, + ], + } + guardrail_endpoint = f"{crowdstrike_aidr_guardrail.api_base}/v1/guard_chat_completions" + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + return_value=httpx.Response( + status_code=200, + json={ + "result": { + "blocked": False, + "transformed": True, + "guard_output": { + "messages": [ + {"role": "tool", "content": "stored"}, + {"role": "user", "content": "Also my email is "}, + ] + }, + }, + }, + request=httpx.Request(method="POST", url=guardrail_endpoint), + ), + ): + result = await AnthropicMessagesHandler().process_input_messages( + data=data, + guardrail_to_apply=crowdstrike_aidr_guardrail, + ) + + serialized = json.dumps(result["messages"]) + assert "" in serialized + assert "jane.doe@example.com" not in serialized + assert "tu1" in serialized diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_generic_guardrail_api.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_generic_guardrail_api.py index 399442a5f71..791fdd4077c 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_generic_guardrail_api.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_generic_guardrail_api.py @@ -609,7 +609,6 @@ class TestImageSupport: request_data=mock_request_data_input, input_type="request", ) - result_texts = guardrailed_inputs.get("texts", []) result_images = guardrailed_inputs.get("images", None) # Verify API was called with images @@ -943,7 +942,7 @@ class TestMultimodalSupport: guardrail.async_handler, "post", return_value=mock_response ) as mock_post: # This should not raise SerializationIterator error - result = await guardrail.apply_guardrail( + await guardrail.apply_guardrail( inputs={ "texts": ["What's in this image?"], "images": ["https://example.com/image.jpg"], @@ -1006,7 +1005,7 @@ class TestMultimodalSupport: with patch.object( guardrail.async_handler, "post", return_value=mock_response ) as mock_post: - result = await guardrail.apply_guardrail( + await guardrail.apply_guardrail( inputs={ "texts": ["Hello", "World"], "structured_messages": messages_with_iterable, @@ -1023,6 +1022,717 @@ class TestMultimodalSupport: assert isinstance(json_payload["structured_messages"], list) +def _make_stream_chunk(content: str, finish_reason=None): + """Build a real ModelResponseStream so the handler's isinstance checks pass.""" + from litellm.types.utils import Delta, ModelResponseStream + + return ModelResponseStream( + model="gpt-4", + choices=[ + litellm.StreamingChoices( + index=0, + delta=Delta(role="assistant", content=content), + finish_reason=finish_reason, + ) + ], + ) + + +def _make_assembled_model_response(content: str) -> ModelResponse: + return ModelResponse( + id="mock-response", + model="gpt-4", + choices=[ + litellm.Choices( + index=0, + message=litellm.Message(role="assistant", content=content), + finish_reason="stop", + ) + ], + ) + + +def _mock_guardrail_post_response(action: str = "NONE", texts=None, blocked_reason=None): + mock_response = MagicMock() + payload = {"action": action} + if texts is not None: + payload["texts"] = texts + if blocked_reason is not None: + payload["blocked_reason"] = blocked_reason + mock_response.json.return_value = payload + mock_response.raise_for_status = MagicMock() + return mock_response + + +def _make_responses_stream_events(text: str): + """Minimal /v1/responses SSE event sequence ending in response.completed.""" + return ( + {"type": "response.created", "response": {"id": "resp_test"}}, + { + "type": "response.output_item.added", + "item": {"type": "message", "id": "msg_test"}, + }, + { + "type": "response.content_part.added", + "part": {"type": "output_text", "text": ""}, + }, + {"type": "response.output_text.delta", "delta": text}, + { + "type": "response.output_text.done", + "text": text, + }, + { + "type": "response.completed", + "response": { + "id": "resp_test", + "output": [ + { + "type": "message", + "id": "msg_test", + "status": "completed", + "role": "assistant", + "content": [{"type": "output_text", "text": text}], + } + ], + "status": "completed", + }, + }, + ) + + +class TestGenericGuardrailAPIStreamingConfig: + """Streaming knobs on GenericGuardrailAPI and initialize_guardrail plumbing.""" + + def test_streaming_defaults(self): + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + ) + assert guardrail.streaming_end_of_stream_only is False + assert guardrail.streaming_sampling_rate == 5 + + def test_streaming_overrides(self): + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + streaming_end_of_stream_only=True, + streaming_sampling_rate=2, + ) + assert guardrail.streaming_end_of_stream_only is True + assert guardrail.streaming_sampling_rate == 2 + + @pytest.mark.parametrize("invalid_rate", [0, -1, -5]) + def test_streaming_sampling_rate_rejects_non_positive(self, invalid_rate): + with pytest.raises(ValueError, match="streaming_sampling_rate must be >= 1"): + GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + streaming_sampling_rate=invalid_rate, + ) + + def test_optional_params_streaming_sampling_rate_ge_one(self): + from pydantic import ValidationError + + from litellm.types.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + GenericGuardrailAPIOptionalParams, + ) + + with pytest.raises(ValidationError): + GenericGuardrailAPIOptionalParams(streaming_sampling_rate=0) + + def test_get_config_model(self): + from litellm.types.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + GenericGuardrailAPIConfigModel, + ) + + assert GenericGuardrailAPI.get_config_model() is GenericGuardrailAPIConfigModel + + def test_initialize_guardrail_forwards_streaming_flags(self): + from litellm.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + initialize_guardrail, + ) + from litellm.types.guardrails import LitellmParams + + litellm_params = LitellmParams( + guardrail="generic_guardrail_api", + mode="post_call", + api_base="https://api.test.guardrail.com", + default_on=False, + ) + # LitellmParams uses extra="allow" on the base; set streaming knobs dynamically + litellm_params.streaming_end_of_stream_only = False # type: ignore[attr-defined] + litellm_params.streaming_sampling_rate = 3 # type: ignore[attr-defined] + + guardrail_config = {"guardrail_name": "test-generic-streaming"} + + with patch( + "litellm.logging_callback_manager.add_litellm_callback" + ): + guardrail = initialize_guardrail(litellm_params, guardrail_config) + + assert guardrail.streaming_end_of_stream_only is False + assert guardrail.streaming_sampling_rate == 3 + + def test_initialize_guardrail_optional_params_defaults_do_not_shadow_top_level( + self, + ): + """Top-level streaming knobs win when optional_params only carries siblings.""" + from litellm.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + initialize_guardrail, + ) + from litellm.types.guardrails import LitellmParams + from litellm.types.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + GenericGuardrailAPIOptionalParams, + ) + + litellm_params = LitellmParams( + guardrail="generic_guardrail_api", + mode="post_call", + api_base="https://api.test.guardrail.com", + default_on=False, + ) + litellm_params.streaming_end_of_stream_only = True # type: ignore[attr-defined] + litellm_params.streaming_sampling_rate = 2 # type: ignore[attr-defined] + # Sibling optional_params only; streaming fields stay at Pydantic default None. + litellm_params.optional_params = GenericGuardrailAPIOptionalParams( # type: ignore[attr-defined] + additional_provider_specific_params={"tenant": "acme"}, + ) + + guardrail_config = {"guardrail_name": "test-generic-streaming-mixed"} + + with patch( + "litellm.logging_callback_manager.add_litellm_callback" + ): + guardrail = initialize_guardrail(litellm_params, guardrail_config) + + assert guardrail.streaming_end_of_stream_only is True + assert guardrail.streaming_sampling_rate == 2 + + def test_initialize_guardrail_explicit_optional_params_streaming_wins(self): + from litellm.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + initialize_guardrail, + ) + from litellm.types.guardrails import LitellmParams + from litellm.types.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + GenericGuardrailAPIOptionalParams, + ) + + litellm_params = LitellmParams( + guardrail="generic_guardrail_api", + mode="post_call", + api_base="https://api.test.guardrail.com", + default_on=False, + ) + litellm_params.streaming_end_of_stream_only = False # type: ignore[attr-defined] + litellm_params.streaming_sampling_rate = 9 # type: ignore[attr-defined] + litellm_params.optional_params = GenericGuardrailAPIOptionalParams( # type: ignore[attr-defined] + streaming_end_of_stream_only=True, + streaming_sampling_rate=1, + ) + + guardrail_config = {"guardrail_name": "test-generic-streaming-nested-wins"} + + with patch( + "litellm.logging_callback_manager.add_litellm_callback" + ): + guardrail = initialize_guardrail(litellm_params, guardrail_config) + + assert guardrail.streaming_end_of_stream_only is True + assert guardrail.streaming_sampling_rate == 1 + + def test_initialize_guardrail_dict_optional_params_streaming_wins(self): + """Guardrail API/UI delivers optional_params as a plain dict, not a model.""" + from litellm.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + initialize_guardrail, + ) + from litellm.types.guardrails import LitellmParams + + litellm_params = LitellmParams( + guardrail="generic_guardrail_api", + mode="post_call", + api_base="https://api.test.guardrail.com", + default_on=False, + ) + litellm_params.streaming_end_of_stream_only = False # type: ignore[attr-defined] + litellm_params.streaming_sampling_rate = 9 # type: ignore[attr-defined] + # Plain dict mirrors how configs arrive from the guardrail API/UI. + litellm_params.optional_params = { # type: ignore[attr-defined] + "streaming_end_of_stream_only": True, + "streaming_sampling_rate": 1, + } + + guardrail_config = {"guardrail_name": "test-generic-streaming-dict-optional"} + + with patch( + "litellm.logging_callback_manager.add_litellm_callback" + ): + guardrail = initialize_guardrail(litellm_params, guardrail_config) + + assert guardrail.streaming_end_of_stream_only is True + assert guardrail.streaming_sampling_rate == 1 + + def test_initialize_guardrail_dict_optional_params_sibling_only_falls_through( + self, + ): + """Dict optional_params without streaming keys must not shadow top-level knobs.""" + from litellm.proxy.guardrails.guardrail_hooks.generic_guardrail_api import ( + initialize_guardrail, + ) + from litellm.types.guardrails import LitellmParams + + litellm_params = LitellmParams( + guardrail="generic_guardrail_api", + mode="post_call", + api_base="https://api.test.guardrail.com", + default_on=False, + ) + litellm_params.streaming_end_of_stream_only = True # type: ignore[attr-defined] + litellm_params.streaming_sampling_rate = 2 # type: ignore[attr-defined] + litellm_params.optional_params = { # type: ignore[attr-defined] + "additional_provider_specific_params": {"tenant": "acme"}, + } + + guardrail_config = {"guardrail_name": "test-generic-streaming-dict-sibling"} + + with patch( + "litellm.logging_callback_manager.add_litellm_callback" + ): + guardrail = initialize_guardrail(litellm_params, guardrail_config) + + assert guardrail.streaming_end_of_stream_only is True + assert guardrail.streaming_sampling_rate == 2 + + +class TestGenericGuardrailAPIStreamingViaUnified: + """Streaming output checks routed through UnifiedLLMGuardrails.""" + + @pytest.mark.asyncio + async def test_streaming_safe_content_yields_all_chunks(self): + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, + ) + + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + ) + unified_guardrail = UnifiedLLMGuardrails() + + async def mock_stream(): + chunks_data = ["Hello", " ", "world", "!", " Goodbye"] + for i, content in enumerate(chunks_data): + yield _make_stream_chunk( + content, + finish_reason="stop" if i == len(chunks_data) - 1 else None, + ) + + mock_post = AsyncMock( + return_value=_mock_guardrail_post_response( + action="NONE", texts=["Hello world! Goodbye"] + ) + ) + + with ( + patch.object(guardrail.async_handler, "post", mock_post), + patch( + "litellm.llms.openai.chat.guardrail_translation.handler.stream_chunk_builder", + return_value=_make_assembled_model_response("Hello world! Goodbye"), + ), + ): + user_api_key_dict = UserAPIKeyAuth( + api_key="test", request_route="/chat/completions" + ) + request_data = { + "messages": [{"role": "user", "content": "hi"}], + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-generic-guardrail"]}, + } + + chunks_received = 0 + async for _ in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=mock_stream(), + request_data=request_data, + ): + chunks_received += 1 + + assert chunks_received == 5 + assert mock_post.await_count >= 1 + + @pytest.mark.asyncio + async def test_streaming_blocked_content_raises(self): + from litellm.exceptions import GuardrailRaisedException + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, + ) + + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + streaming_sampling_rate=1, + ) + unified_guardrail = UnifiedLLMGuardrails() + + async def mock_stream(): + chunks_data = ["Hello", " ishaan", " here"] + for i, content in enumerate(chunks_data): + yield _make_stream_chunk( + content, + finish_reason="stop" if i == len(chunks_data) - 1 else None, + ) + + mock_post = AsyncMock( + return_value=_mock_guardrail_post_response( + action="BLOCKED", blocked_reason="Ishaan is not allowed" + ) + ) + + with ( + patch.object(guardrail.async_handler, "post", mock_post), + patch( + "litellm.llms.openai.chat.guardrail_translation.handler.stream_chunk_builder", + return_value=_make_assembled_model_response("Hello ishaan here"), + ), + ): + user_api_key_dict = UserAPIKeyAuth( + api_key="test", request_route="/chat/completions" + ) + request_data = { + "messages": [{"role": "user", "content": "hi"}], + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-generic-guardrail"]}, + } + + with pytest.raises(GuardrailRaisedException) as exc_info: + async for _ in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=mock_stream(), + request_data=request_data, + ): + pass + + assert "Ishaan is not allowed" in str(exc_info.value) + + @pytest.mark.asyncio + async def test_streaming_default_uses_sampled_cadence(self): + """Default samples every 5th chunk + final pass: 10 chunks → calls at 5, 10, and final = 3.""" + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, + ) + + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + ) + unified_guardrail = UnifiedLLMGuardrails() + + async def mock_stream(): + chunks_data = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"] + for i, content in enumerate(chunks_data): + yield _make_stream_chunk( + content, + finish_reason="stop" if i == len(chunks_data) - 1 else None, + ) + + mock_post = AsyncMock( + return_value=_mock_guardrail_post_response( + action="NONE", texts=["ABCDEFGHIJ"] + ) + ) + + with ( + patch.object(guardrail.async_handler, "post", mock_post), + patch( + "litellm.llms.openai.chat.guardrail_translation.handler.stream_chunk_builder", + return_value=_make_assembled_model_response("ABCDEFGHIJ"), + ), + ): + user_api_key_dict = UserAPIKeyAuth( + api_key="test", request_route="/chat/completions" + ) + request_data = { + "messages": [{"role": "user", "content": "hi"}], + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-generic-guardrail"]}, + } + + async for _ in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=mock_stream(), + request_data=request_data, + ): + pass + + assert mock_post.await_count == 3, ( + f"Expected 3 guardrail calls (2 sampled at chunks 5 / 10 + 1 final), " + f"got {mock_post.await_count}" + ) + for call in mock_post.await_args_list: + assert call.kwargs["json"]["input_type"] == "response" + + @pytest.mark.asyncio + async def test_streaming_end_of_stream_only_calls_guardrail_once(self): + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, + ) + + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + streaming_end_of_stream_only=True, + ) + unified_guardrail = UnifiedLLMGuardrails() + + async def mock_stream(): + chunks_data = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"] + for i, content in enumerate(chunks_data): + yield _make_stream_chunk( + content, + finish_reason="stop" if i == len(chunks_data) - 1 else None, + ) + + mock_post = AsyncMock( + return_value=_mock_guardrail_post_response( + action="NONE", texts=["ABCDEFGHIJ"] + ) + ) + + with ( + patch.object(guardrail.async_handler, "post", mock_post), + patch( + "litellm.llms.openai.chat.guardrail_translation.handler.stream_chunk_builder", + return_value=_make_assembled_model_response("ABCDEFGHIJ"), + ), + ): + user_api_key_dict = UserAPIKeyAuth( + api_key="test", request_route="/chat/completions" + ) + request_data = { + "messages": [{"role": "user", "content": "hi"}], + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-generic-guardrail"]}, + } + + async for _ in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=mock_stream(), + request_data=request_data, + ): + pass + + assert mock_post.await_count == 1, ( + f"Expected exactly one guardrail call at end of stream, " + f"got {mock_post.await_count}" + ) + + @pytest.mark.asyncio + async def test_streaming_sampling_rate_override(self): + """sampling_rate=2 on 6 chunks → in-stream at 2,4,6 plus final = 4 calls.""" + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, + ) + + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + streaming_end_of_stream_only=False, + streaming_sampling_rate=2, + ) + unified_guardrail = UnifiedLLMGuardrails() + + async def mock_stream(): + chunks_data = ["A", "B", "C", "D", "E", "F"] + for i, content in enumerate(chunks_data): + yield _make_stream_chunk( + content, + finish_reason="stop" if i == len(chunks_data) - 1 else None, + ) + + mock_post = AsyncMock( + return_value=_mock_guardrail_post_response(action="NONE", texts=["ABCDEF"]) + ) + + with ( + patch.object(guardrail.async_handler, "post", mock_post), + patch( + "litellm.llms.openai.chat.guardrail_translation.handler.stream_chunk_builder", + return_value=_make_assembled_model_response("ABCDEF"), + ), + ): + user_api_key_dict = UserAPIKeyAuth( + api_key="test", request_route="/chat/completions" + ) + request_data = { + "messages": [{"role": "user", "content": "hi"}], + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-generic-guardrail"]}, + } + + async for _ in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=mock_stream(), + request_data=request_data, + ): + pass + + assert mock_post.await_count == 4, ( + f"Expected 4 guardrail calls (3 sampled + 1 final aggregate), " + f"got {mock_post.await_count}" + ) + + @pytest.mark.asyncio + async def test_streaming_fail_open_on_unreachable_continues_stream(self): + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, + ) + + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + unreachable_fallback="fail_open", + streaming_end_of_stream_only=True, + ) + unified_guardrail = UnifiedLLMGuardrails() + + async def mock_stream(): + for i, content in enumerate(["A", "B", "C"]): + yield _make_stream_chunk( + content, finish_reason="stop" if i == 2 else None + ) + + mock_post = AsyncMock(side_effect=httpx.ConnectError("connection refused")) + + with ( + patch.object(guardrail.async_handler, "post", mock_post), + patch( + "litellm.llms.openai.chat.guardrail_translation.handler.stream_chunk_builder", + return_value=_make_assembled_model_response("ABC"), + ), + ): + user_api_key_dict = UserAPIKeyAuth( + api_key="test", request_route="/chat/completions" + ) + request_data = { + "messages": [{"role": "user", "content": "hi"}], + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-generic-guardrail"]}, + } + + chunks_received = 0 + async for _ in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=mock_stream(), + request_data=request_data, + ): + chunks_received += 1 + + assert chunks_received == 3 + + @pytest.mark.asyncio + async def test_responses_api_streaming_end_of_stream_only_calls_guardrail_once(self): + """/v1/responses path through unified hook; end-of-stream-only = one call.""" + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, + ) + + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + streaming_end_of_stream_only=True, + ) + unified_guardrail = UnifiedLLMGuardrails() + + async def mock_responses_stream(): + for event in _make_responses_stream_events("Hello world"): + yield event + + mock_post = AsyncMock( + return_value=_mock_guardrail_post_response( + action="NONE", texts=["Hello world"] + ) + ) + + with patch.object(guardrail.async_handler, "post", mock_post): + user_api_key_dict = UserAPIKeyAuth( + api_key="test", request_route="/v1/responses" + ) + request_data = { + "input": "hi", + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-generic-guardrail"]}, + } + + events_received = 0 + async for _ in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=mock_responses_stream(), + request_data=request_data, + ): + events_received += 1 + + assert events_received == 6 + assert mock_post.await_count == 1, ( + f"Expected exactly one guardrail call at end of /v1/responses stream, " + f"got {mock_post.await_count}" + ) + assert mock_post.await_args.kwargs["json"]["input_type"] == "response" + + @pytest.mark.asyncio + async def test_responses_api_streaming_blocked_raises(self): + """Mid-stream BLOCKED on /v1/responses surfaces GuardrailRaisedException.""" + from litellm.exceptions import GuardrailRaisedException + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, + ) + + guardrail = GenericGuardrailAPI( + api_base="https://api.test.guardrail.com", + guardrail_name="test-generic-guardrail", + event_hook="post_call", + streaming_sampling_rate=1, + ) + unified_guardrail = UnifiedLLMGuardrails() + + async def mock_responses_stream(): + for event in _make_responses_stream_events("blocked content"): + yield event + + mock_post = AsyncMock( + return_value=_mock_guardrail_post_response( + action="BLOCKED", blocked_reason="Responses content not allowed" + ) + ) + + with patch.object(guardrail.async_handler, "post", mock_post): + user_api_key_dict = UserAPIKeyAuth( + api_key="test", request_route="/v1/responses" + ) + request_data = { + "input": "hi", + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-generic-guardrail"]}, + } + + with pytest.raises(GuardrailRaisedException) as exc_info: + async for _ in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=mock_responses_stream(), + request_data=request_data, + ): + pass + + assert "Responses content not allowed" in str(exc_info.value) + class TestToolSupport: """Test tool handling in guardrail requests""" diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_grayswan.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_grayswan.py index f2e7447239f..53af7f36a5f 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_grayswan.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_grayswan.py @@ -5,6 +5,7 @@ from fastapi import HTTPException from litellm.integrations.custom_guardrail import ModifyResponseException from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.guardrails.guardrail_hooks.grayswan import grayswan as grayswan_module from litellm.proxy.guardrails.guardrail_hooks.grayswan.grayswan import ( GraySwanGuardrail, GraySwanGuardrailAPIError, @@ -70,12 +71,118 @@ def test_prepare_payload_includes_dynamic_metadata( assert payload["metadata"] == dynamic_body["metadata"] +def test_prepare_payload_forwards_only_scan_id_header( + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + request_data = { + "proxy_server_request": { + "headers": { + "SHADE_SCAN_ID": "scan-123", + "authorization": "Bearer secret", + } + }, + "litellm_metadata": {"request_id": "request-123"}, + } + + payload = grayswan_guardrail._prepare_payload(messages, {}, request_data) + + assert payload["litellm_metadata"] == { + "request_id": "request-123", + "headers": {"SHADE_SCAN_ID": "scan-123"}, + } + + +def test_prepare_payload_merges_scan_id_with_existing_metadata_headers( + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + request_data = { + "proxy_server_request": { + "headers": { + "shade_scan_id": "scan-123", + } + }, + "litellm_metadata": { + "request_id": "request-123", + "headers": {"x-existing": "keep-me"}, + }, + } + + payload = grayswan_guardrail._prepare_payload(messages, {}, request_data) + + assert payload["litellm_metadata"] == { + "request_id": "request-123", + "headers": { + "x-existing": "keep-me", + "shade_scan_id": "scan-123", + }, + } + + +def test_prepare_payload_sanitizes_headers_when_litellm_metadata_absent( + monkeypatch: pytest.MonkeyPatch, + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + request_data = { + "proxy_server_request": { + "headers": { + "shade_scan_id": "scan-123", + } + } + } + + monkeypatch.setattr(grayswan_module, "safe_dumps", lambda _data: "{}") + + payload = grayswan_guardrail._prepare_payload(messages, {}, request_data) + + assert "litellm_metadata" not in payload + + +def test_prepare_payload_extracts_headers_from_logging_obj( + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + request_data = {} + logging_obj = type( + "LoggingObj", + (), + { + "model_call_details": { + "litellm_params": { + "metadata": { + "headers": { + "shade_scan_id": "scan-from-logging", + "authorization": "Bearer secret", + } + } + } + } + }, + )() + + payload = grayswan_guardrail._prepare_payload(messages, {}, request_data, logging_obj) + + assert payload["litellm_metadata"] == { + "headers": {"shade_scan_id": "scan-from-logging"}, + } + + +def test_prepare_payload_ignores_logging_obj_without_model_call_details( + grayswan_guardrail: GraySwanGuardrail, +) -> None: + messages = [{"role": "user", "content": "hello"}] + + payload = grayswan_guardrail._prepare_payload(messages, {}, {}, object()) + + assert "litellm_metadata" not in payload + + def test_process_response_does_not_block_under_threshold( grayswan_guardrail: GraySwanGuardrail, ) -> None: - grayswan_guardrail._process_grayswan_response( - {"violation": 0.3, "violated_rules": []} - ) + grayswan_guardrail._process_grayswan_response({"violation": 0.3, "violated_rules": []}) def test_process_response_blocks_when_threshold_exceeded() -> None: @@ -127,16 +234,12 @@ class _DummyClient: self.calls: list[dict] = [] async def post(self, *, url: str, headers: dict, json: dict, timeout: float): - self.calls.append( - {"url": url, "headers": headers, "json": json, "timeout": timeout} - ) + self.calls.append({"url": url, "headers": headers, "json": json, "timeout": timeout}) return _DummyResponse(self.payload) @pytest.mark.asyncio -async def test_run_guardrail_posts_payload( - monkeypatch, grayswan_guardrail: GraySwanGuardrail -) -> None: +async def test_run_guardrail_posts_payload(monkeypatch, grayswan_guardrail: GraySwanGuardrail) -> None: dummy_client = _DummyClient({"violation": 0.1}) grayswan_guardrail.async_handler = dummy_client @@ -308,9 +411,7 @@ def test_process_response_passthrough_raises_exception_in_pre_call() -> None: # Should raise ModifyResponseException with pytest.raises(ModifyResponseException) as exc: - guardrail._process_grayswan_response( - response_json, data, GuardrailEventHooks.pre_call - ) + guardrail._process_grayswan_response(response_json, data, GuardrailEventHooks.pre_call) assert "Gray Swan Cygnal Guardrail" in exc.value.message assert exc.value.model == "gpt-4" @@ -338,9 +439,7 @@ def test_process_response_passthrough_raises_exception_in_during_call() -> None: # Should raise ModifyResponseException with pytest.raises(ModifyResponseException) as exc: - guardrail._process_grayswan_response( - response_json, data, GuardrailEventHooks.during_call - ) + guardrail._process_grayswan_response(response_json, data, GuardrailEventHooks.during_call) assert "Gray Swan Cygnal Guardrail" in exc.value.message assert exc.value.model == "gpt-4" @@ -365,9 +464,7 @@ def test_process_response_passthrough_stores_detection_info_in_post_call() -> No } # Should NOT raise an exception in post_call - guardrail._process_grayswan_response( - response_json, data, GuardrailEventHooks.post_call - ) + guardrail._process_grayswan_response(response_json, data, GuardrailEventHooks.post_call) # Verify detection info was stored in metadata assert "metadata" in data @@ -400,9 +497,7 @@ def test_process_response_passthrough_does_not_raise_if_under_threshold() -> Non } # Should not raise an exception since under threshold - guardrail._process_grayswan_response( - response_json, data, GuardrailEventHooks.pre_call - ) + guardrail._process_grayswan_response(response_json, data, GuardrailEventHooks.pre_call) # Should not have any detection info since it didn't exceed threshold assert "guardrail_detections" not in data.get("metadata", {}) @@ -436,10 +531,7 @@ def test_format_violation_message() -> None: assert "Gray Swan Cygnal Guardrail" in message assert "the input query has a violation score of 0.85" in message assert "violating the rule(s): 1, 3, 5" in message - assert ( - "Mutation effort to make the harmful intention disguised was DETECTED" - in message - ) + assert "Mutation effort to make the harmful intention disguised was DETECTED" in message # IPI should not be in message since it's False assert "Indirect Prompt Injection was DETECTED" not in message @@ -450,10 +542,7 @@ def test_format_violation_message() -> None: assert "Gray Swan Cygnal Guardrail" in message assert "the model response has a violation score of 0.85" in message assert "violating the rule(s): 1, 3, 5" in message - assert ( - "Mutation effort to make the harmful intention disguised was DETECTED" - in message - ) + assert "Mutation effort to make the harmful intention disguised was DETECTED" in message def test_prepare_payload_includes_litellm_metadata( diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_headroom.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_headroom.py index 66395035384..7f412c008ca 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_headroom.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_headroom.py @@ -6,17 +6,32 @@ Tests cover: - x-headroom-bypass: true header causes guardrail to skip compression - missing or empty messages are passed through unchanged - response-type input is passed through unchanged -- /v1/compress HTTP error raises HTTPException +- /v1/compress HTTP error raises HTTPException (fail_closed, the default) - /v1/compress returning malformed JSON raises HTTPException +- /v1/compress non-2xx surfaces as httpx.HTTPStatusError (raise_for_status), + not a status_code check on the returned response -- both are handled +- unreachable_fallback="fail_open" forwards the request uncompressed instead of raising +- CCR: headroom_retrieve tool injected when compressed messages contain hashes +- CCR: async_should_run_agentic_loop returns True when response has headroom_retrieve tool calls +- CCR: async_build_agentic_loop_plan calls retrieve endpoint and builds follow-up messages """ +import json +import time from unittest.mock import AsyncMock, MagicMock, patch import httpx import pytest from fastapi import HTTPException -from litellm.proxy.guardrails.guardrail_hooks.headroom.headroom import HeadroomGuardrail +import litellm + +from litellm.proxy.guardrails.guardrail_hooks.headroom.headroom import ( + HeadroomGuardrail, + extract_hashes_from_messages, + has_headroom_retrieve_tool, + HEADROOM_RETRIEVE_TOOL_NAME, +) from litellm.types.utils import GenericGuardrailAPIInputs FAKE_API_BASE = "https://headroom.example.com" @@ -30,6 +45,13 @@ COMPRESSED_MESSAGES = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "A" * 500}, ] +COMPRESSED_MESSAGES_WITH_HASH = [ + {"role": "system", "content": "You are a helpful assistant."}, + { + "role": "user", + "content": "Summary. Retrieve more: hash=b573993006976af767214fac", + }, +] def _make_guardrail(**kwargs) -> HeadroomGuardrail: @@ -57,6 +79,36 @@ def _make_compress_response(messages: list, status: int = 200) -> MagicMock: return mock +def _make_retrieve_response(original_content: str, status: int = 200) -> MagicMock: + mock = MagicMock() + mock.status_code = status + mock.json.return_value = {"original_content": original_content} + mock.text = original_content + return mock + + +def _make_openai_response_with_tool_call(tool_name: str, arguments: dict, tool_id: str = "call_abc123") -> MagicMock: + fn = MagicMock() + fn.name = tool_name + fn.arguments = json.dumps(arguments) + + tc = MagicMock() + tc.id = tool_id + tc.type = "function" + tc.function = fn + + message = MagicMock() + message.content = None + message.tool_calls = [tc] + + choice = MagicMock() + choice.message = message + + response = MagicMock() + response.choices = [choice] + return response + + @pytest.fixture def guardrail() -> HeadroomGuardrail: return _make_guardrail() @@ -87,6 +139,567 @@ async def test_apply_guardrail_compresses_and_returns_structured_messages( assert result.get("structured_messages") == COMPRESSED_MESSAGES +@pytest.mark.asyncio +async def test_apply_guardrail_injects_retrieve_tool_when_hashes_present( + guardrail: HeadroomGuardrail, +): + inputs = GenericGuardrailAPIInputs( + texts=["A" * 5000], + structured_messages=ORIGINAL_MESSAGES, + ) + mock_response = _make_compress_response(COMPRESSED_MESSAGES_WITH_HASH) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + return_value=mock_response, + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={"model": "gpt-4o"}, + input_type="request", + ) + + tools = result.get("tools") + assert tools is not None + assert has_headroom_retrieve_tool(tools) + + +@pytest.mark.asyncio +async def test_apply_guardrail_no_tool_injected_when_no_hashes( + guardrail: HeadroomGuardrail, +): + inputs = GenericGuardrailAPIInputs( + texts=["A" * 5000], + structured_messages=ORIGINAL_MESSAGES, + ) + mock_response = _make_compress_response(COMPRESSED_MESSAGES) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + return_value=mock_response, + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={"model": "gpt-4o"}, + input_type="request", + ) + + tools = result.get("tools") + assert not has_headroom_retrieve_tool(tools or []) + + +@pytest.mark.asyncio +async def test_apply_guardrail_preserves_existing_tools_when_injecting( + guardrail: HeadroomGuardrail, +): + existing_tool = {"type": "function", "function": {"name": "my_tool", "parameters": {}}} + inputs = GenericGuardrailAPIInputs( + texts=["A" * 5000], + structured_messages=ORIGINAL_MESSAGES, + tools=[existing_tool], + ) + mock_response = _make_compress_response(COMPRESSED_MESSAGES_WITH_HASH) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + return_value=mock_response, + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={"model": "gpt-4o"}, + input_type="request", + ) + + tools = result.get("tools") + assert tools is not None + assert isinstance(tools, list) + assert any(isinstance(t, dict) and t.get("function", {}).get("name") == "my_tool" for t in tools) + assert has_headroom_retrieve_tool(tools) + + +@pytest.mark.asyncio +async def test_async_should_run_agentic_loop_returns_true_for_retrieve_call( + guardrail: HeadroomGuardrail, +): + retrieve_tool_def = [{"type": "function", "function": {"name": HEADROOM_RETRIEVE_TOOL_NAME}}] + response = _make_openai_response_with_tool_call( + tool_name=HEADROOM_RETRIEVE_TOOL_NAME, + arguments={"hash": "b573993006976af767214fac"}, + ) + + should_run, ctx = await guardrail.async_should_run_agentic_loop( + response=response, + model="gpt-4o", + messages=[], + tools=retrieve_tool_def, + stream=False, + custom_llm_provider="openai", + kwargs={}, + ) + + assert should_run is True + assert len(ctx["tool_calls"]) == 1 + assert ctx["tool_calls"][0]["arguments"]["hash"] == "b573993006976af767214fac" + + +@pytest.mark.asyncio +async def test_async_should_run_agentic_loop_returns_false_without_retrieve_tool( + guardrail: HeadroomGuardrail, +): + other_tools = [{"type": "function", "function": {"name": "other_tool"}}] + response = _make_openai_response_with_tool_call( + tool_name="other_tool", + arguments={}, + ) + + should_run, _ = await guardrail.async_should_run_agentic_loop( + response=response, + model="gpt-4o", + messages=[], + tools=other_tools, + stream=False, + custom_llm_provider="openai", + kwargs={}, + ) + + assert should_run is False + + +@pytest.mark.asyncio +async def test_async_should_run_agentic_loop_returns_false_when_no_retrieve_calls( + guardrail: HeadroomGuardrail, +): + retrieve_tool_def = [{"type": "function", "function": {"name": HEADROOM_RETRIEVE_TOOL_NAME}}] + response = _make_openai_response_with_tool_call( + tool_name="some_other_function", + arguments={}, + ) + + should_run, _ = await guardrail.async_should_run_agentic_loop( + response=response, + model="gpt-4o", + messages=[], + tools=retrieve_tool_def, + stream=False, + custom_llm_provider="openai", + kwargs={}, + ) + + assert should_run is False + + +@pytest.mark.asyncio +async def test_async_build_agentic_loop_plan_calls_retrieve_and_builds_messages( + guardrail: HeadroomGuardrail, +): + original_content = "This is the full compressed content." + mock_retrieve = _make_retrieve_response(original_content) + + tool_calls = [ + { + "id": "call_abc123", + "type": "function", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "arguments": {"hash": "b573993006976af767214fac"}, + } + ] + response = _make_openai_response_with_tool_call( + tool_name=HEADROOM_RETRIEVE_TOOL_NAME, + arguments={"hash": "b573993006976af767214fac"}, + tool_id="call_abc123", + ) + messages = [{"role": "user", "content": "What does it say? hash=b573993006976af767214fac"}] + guardrail._issued_hashes_by_call_id["call-1"] = ( + frozenset({"b573993006976af767214fac"}), + time.monotonic() + 999, + ) + + with patch.object( + guardrail.async_handler, + "get", + new_callable=AsyncMock, + return_value=mock_retrieve, + ) as mock_get: + plan = await guardrail.async_build_agentic_loop_plan( + tools={"tool_calls": tool_calls}, + model="gpt-4o", + messages=messages, + response=response, + anthropic_messages_provider_config=None, + anthropic_messages_optional_request_params={}, + logging_obj=None, + stream=False, + kwargs={"litellm_call_id": "call-1"}, + ) + + assert plan.run_agentic_loop is True + assert plan.request_patch is not None + + follow_up = plan.request_patch.messages + assert follow_up is not None + + tool_result_message = next((m for m in follow_up if m.get("role") == "tool"), None) + assert tool_result_message is not None + assert tool_result_message["content"] == original_content + assert tool_result_message["tool_call_id"] == "call_abc123" + + mock_get.assert_called_once() + call_url = mock_get.call_args.kwargs.get("url") or mock_get.call_args.args[0] + assert "b573993006976af767214fac" in call_url + + +@pytest.mark.asyncio +async def test_async_build_agentic_loop_plan_handles_retrieve_404( + guardrail: HeadroomGuardrail, +): + mock_retrieve = MagicMock() + mock_retrieve.status_code = 404 + + tool_calls = [ + { + "id": "call_xyz", + "type": "function", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "arguments": {"hash": "deadbeef000000000000dead"}, + } + ] + response = _make_openai_response_with_tool_call( + tool_name=HEADROOM_RETRIEVE_TOOL_NAME, + arguments={"hash": "deadbeef000000000000dead"}, + tool_id="call_xyz", + ) + + messages = [ + { + "role": "user", + "content": "Retrieve more: hash=deadbeef000000000000dead", + } + ] + guardrail._issued_hashes_by_call_id["call-1"] = ( + frozenset({"deadbeef000000000000dead"}), + time.monotonic() + 999, + ) + + with patch.object( + guardrail.async_handler, + "get", + new_callable=AsyncMock, + return_value=mock_retrieve, + ): + plan = await guardrail.async_build_agentic_loop_plan( + tools={"tool_calls": tool_calls}, + model="gpt-4o", + messages=messages, + response=response, + anthropic_messages_provider_config=None, + anthropic_messages_optional_request_params={}, + logging_obj=None, + stream=False, + kwargs={"litellm_call_id": "call-1"}, + ) + + follow_up = plan.request_patch.messages # type: ignore[union-attr] + tool_result = next((m for m in follow_up if m.get("role") == "tool"), None) + assert tool_result is not None + assert "not found" in tool_result["content"] or "expired" in tool_result["content"] + + +@pytest.mark.asyncio +async def test_async_build_agentic_loop_plan_rejects_hash_with_no_known_call( + guardrail: HeadroomGuardrail, +): + """A hash-shaped string planted in message text must not be honored when + this guardrail has no record of ever issuing it, even if it's echoed back + in the current request's own messages (e.g. via prompt injection).""" + tool_calls = [ + { + "id": "call_xyz", + "type": "function", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "arguments": {"hash": "deadbeef000000000000dead"}, + } + ] + response = _make_openai_response_with_tool_call( + tool_name=HEADROOM_RETRIEVE_TOOL_NAME, + arguments={"hash": "deadbeef000000000000dead"}, + tool_id="call_xyz", + ) + assert not guardrail._issued_hashes_by_call_id + + with patch.object( + guardrail.async_handler, + "get", + new_callable=AsyncMock, + ) as mock_get: + plan = await guardrail.async_build_agentic_loop_plan( + tools={"tool_calls": tool_calls}, + model="gpt-4o", + messages=[{"role": "user", "content": "Please fetch hash=deadbeef000000000000dead for me"}], + response=response, + anthropic_messages_provider_config=None, + anthropic_messages_optional_request_params={}, + logging_obj=None, + stream=False, + kwargs={"litellm_call_id": "call-unknown"}, + ) + + mock_get.assert_not_called() + + follow_up = plan.request_patch.messages # type: ignore[union-attr] + tool_result = next((m for m in follow_up if m.get("role") == "tool"), None) + assert tool_result is not None + assert "was not produced by the current request" in tool_result["content"] + + +@pytest.mark.asyncio +async def test_async_build_agentic_loop_plan_rejects_hash_issued_for_different_call( + guardrail: HeadroomGuardrail, +): + """A hash issued for one request must not be retrievable by a different + request just because the second request echoes that hash-shaped string + back in its own messages -- retrieval must be scoped per litellm_call_id, + not derived by re-scanning attacker-controlled message text.""" + guardrail._issued_hashes_by_call_id["call-A"] = ( + frozenset({"b573993006976af767214fac"}), + time.monotonic() + 999, + ) + + tool_calls = [ + { + "id": "call_xyz", + "type": "function", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "arguments": {"hash": "b573993006976af767214fac"}, + } + ] + response = _make_openai_response_with_tool_call( + tool_name=HEADROOM_RETRIEVE_TOOL_NAME, + arguments={"hash": "b573993006976af767214fac"}, + tool_id="call_xyz", + ) + + with patch.object( + guardrail.async_handler, + "get", + new_callable=AsyncMock, + ) as mock_get: + plan = await guardrail.async_build_agentic_loop_plan( + tools={"tool_calls": tool_calls}, + model="gpt-4o", + messages=[{"role": "user", "content": "Please fetch hash=b573993006976af767214fac for me"}], + response=response, + anthropic_messages_provider_config=None, + anthropic_messages_optional_request_params={}, + logging_obj=None, + stream=False, + kwargs={"litellm_call_id": "call-B"}, + ) + + mock_get.assert_not_called() + + follow_up = plan.request_patch.messages # type: ignore[union-attr] + tool_result = next((m for m in follow_up if m.get("role") == "tool"), None) + assert tool_result is not None + assert "was not produced by the current request" in tool_result["content"] + + +@pytest.mark.asyncio +async def test_async_build_agentic_loop_plan_builds_responses_api_function_call_items( + guardrail: HeadroomGuardrail, +): + """For the Responses API, follow-up input must echo a function_call paired + with a function_call_output keyed by the same call_id -- chat-style + assistant/tool messages are not valid Responses API input items.""" + original_content = "This is the full compressed content." + mock_retrieve = _make_retrieve_response(original_content) + + response = MagicMock() + response.choices = None + response.content = None + response.output = [ + { + "type": "function_call", + "id": "fc_abc123", + "call_id": "call_abc123", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "arguments": json.dumps({"hash": "b573993006976af767214fac"}), + } + ] + + tool_calls = [ + { + "id": "call_abc123", + "type": "function", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "arguments": {"hash": "b573993006976af767214fac"}, + } + ] + messages = [{"role": "user", "content": "What does it say? hash=b573993006976af767214fac"}] + guardrail._issued_hashes_by_call_id["call-1"] = ( + frozenset({"b573993006976af767214fac"}), + time.monotonic() + 999, + ) + + with patch.object( + guardrail.async_handler, + "get", + new_callable=AsyncMock, + return_value=mock_retrieve, + ): + plan = await guardrail.async_build_agentic_loop_plan( + tools={"tool_calls": tool_calls}, + model="gpt-4o", + messages=messages, + response=response, + anthropic_messages_provider_config=None, + anthropic_messages_optional_request_params={}, + logging_obj=None, + stream=False, + kwargs={"litellm_call_id": "call-1"}, + ) + + follow_up = plan.request_patch.messages # type: ignore[union-attr] + assert all("role" not in item for item in follow_up if item not in messages) + + function_call_item = next((i for i in follow_up if i.get("type") == "function_call"), None) + assert function_call_item is not None + assert function_call_item["call_id"] == "call_abc123" + assert function_call_item["name"] == HEADROOM_RETRIEVE_TOOL_NAME + + output_item = next((i for i in follow_up if i.get("type") == "function_call_output"), None) + assert output_item is not None + assert output_item["call_id"] == "call_abc123" + assert output_item["output"] == original_content + + +@pytest.mark.asyncio +async def test_async_build_agentic_loop_plan_builds_anthropic_tool_result_messages( + guardrail: HeadroomGuardrail, +): + """For the Anthropic Messages API, follow-up must echo a tool_use content + block in an assistant message paired with a tool_result content block in a + user message keyed by the same tool_use_id -- chat-style tool-role + messages are not valid Anthropic input. + + AnthropicMessagesResponse is a TypedDict, so real responses are plain + dicts at runtime; a MagicMock response here would pass even if branch + selection used bare getattr() and silently fell through to the + chat-completions replay shape for every real Anthropic response. + """ + original_content = "This is the full compressed content." + mock_retrieve = _make_retrieve_response(original_content) + + response = { + "content": [ + { + "type": "tool_use", + "id": "toolu_abc123", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "input": {"hash": "b573993006976af767214fac"}, + } + ] + } + + tool_calls = [ + { + "id": "toolu_abc123", + "type": "function", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "arguments": {"hash": "b573993006976af767214fac"}, + } + ] + messages = [{"role": "user", "content": "What does it say? hash=b573993006976af767214fac"}] + guardrail._issued_hashes_by_call_id["call-1"] = ( + frozenset({"b573993006976af767214fac"}), + time.monotonic() + 999, + ) + + with patch.object( + guardrail.async_handler, + "get", + new_callable=AsyncMock, + return_value=mock_retrieve, + ): + plan = await guardrail.async_build_agentic_loop_plan( + tools={"tool_calls": tool_calls}, + model="claude-sonnet-4-5", + messages=messages, + response=response, + anthropic_messages_provider_config=None, + anthropic_messages_optional_request_params={}, + logging_obj=None, + stream=False, + kwargs={"litellm_call_id": "call-1"}, + ) + + follow_up = plan.request_patch.messages # type: ignore[union-attr] + assert all(m.get("role") != "tool" for m in follow_up) + + assistant_message = next((m for m in follow_up if m.get("role") == "assistant"), None) + assert assistant_message is not None + tool_use_block = next((b for b in assistant_message["content"] if b.get("type") == "tool_use"), None) + assert tool_use_block is not None + assert tool_use_block["id"] == "toolu_abc123" + + user_message = follow_up[-1] + assert user_message["role"] == "user" + tool_result_block = next((b for b in user_message["content"] if b.get("type") == "tool_result"), None) + assert tool_result_block is not None + assert tool_result_block["tool_use_id"] == "toolu_abc123" + assert tool_result_block["content"] == original_content + + +def test_extract_hashes_from_messages_finds_hashes(): + messages = [ + {"role": "user", "content": "Retrieve more: hash=b573993006976af767214fac"}, + {"role": "assistant", "content": "Also: hash=aabbccdd001122334455aabb"}, + ] + hashes = extract_hashes_from_messages(messages) + assert "b573993006976af767214fac" in hashes + assert "aabbccdd001122334455aabb" in hashes + + +def test_extract_hashes_from_messages_ignores_short_hashes(): + messages = [{"role": "user", "content": "hash=tooshort"}] + hashes = extract_hashes_from_messages(messages) + assert not hashes + + +def test_extract_hashes_from_list_content_blocks(): + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "hash=b573993006976af767214fac found here"}, + ], + } + ] + hashes = extract_hashes_from_messages(messages) + assert "b573993006976af767214fac" in hashes + + +def test_has_headroom_retrieve_tool_recognizes_anthropic_native_shape(): + """By the time an Anthropic Messages API response reaches the agentic-loop + gate, the OpenAI-shaped tool this guardrail injects (type: "function") + has already been transformed into Anthropic's native tool shape + (type: "custom", top-level "name", no nested "function" object).""" + anthropic_native_tools = [ + { + "type": "custom", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "input_schema": {"type": "object", "properties": {"hash": {"type": "string"}}}, + } + ] + assert has_headroom_retrieve_tool(anthropic_native_tools) + assert not has_headroom_retrieve_tool([{"type": "custom", "name": "some_other_tool"}]) + + @pytest.mark.asyncio async def test_apply_guardrail_bypass_header_skips_compression( guardrail: HeadroomGuardrail, @@ -97,9 +710,7 @@ async def test_apply_guardrail_bypass_header_skips_compression( ) request_data = {"proxy_server_request": {"headers": {"x-headroom-bypass": "true"}}} - with patch.object( - guardrail.async_handler, "post", new_callable=AsyncMock - ) as mock_post: + with patch.object(guardrail.async_handler, "post", new_callable=AsyncMock) as mock_post: result = await guardrail.apply_guardrail( inputs=inputs, request_data=request_data, @@ -119,9 +730,7 @@ async def test_apply_guardrail_response_type_passthrough( structured_messages=ORIGINAL_MESSAGES, ) - with patch.object( - guardrail.async_handler, "post", new_callable=AsyncMock - ) as mock_post: + with patch.object(guardrail.async_handler, "post", new_callable=AsyncMock) as mock_post: result = await guardrail.apply_guardrail( inputs=inputs, request_data={}, @@ -138,9 +747,7 @@ async def test_apply_guardrail_empty_structured_messages_passthrough( ): inputs = GenericGuardrailAPIInputs(texts=["hello"]) - with patch.object( - guardrail.async_handler, "post", new_callable=AsyncMock - ) as mock_post: + with patch.object(guardrail.async_handler, "post", new_callable=AsyncMock) as mock_post: result = await guardrail.apply_guardrail( inputs=inputs, request_data={}, @@ -204,6 +811,244 @@ async def test_apply_guardrail_transport_error_raises(): assert "unreachable" in str(exc_info.value.detail) +def _make_http_status_error(status: int, body: str) -> httpx.HTTPStatusError: + request = httpx.Request("POST", f"{FAKE_API_BASE}/v1/compress") + response = httpx.Response(status, request=request, text=body) + return httpx.HTTPStatusError( + f"Server error '{status}' for url", + request=request, + response=response, + ) + + +@pytest.mark.asyncio +async def test_apply_guardrail_http_status_error_raises(): + """Regression test: litellm's async httpx client calls raise_for_status() + internally, so a non-2xx /v1/compress response surfaces as + httpx.HTTPStatusError, not as a returned MagicMock with status_code set. + A prior version of _call_compress only checked response.status_code and + never caught this exception, so it went unhandled instead of blocking + the request per fail_closed policy.""" + guardrail = _make_guardrail() + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + side_effect=_make_http_status_error(500, "headroom internal error"), + ): + with pytest.raises(HTTPException) as exc_info: + await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert exc_info.value.status_code == 502 + + +@pytest.mark.asyncio +async def test_apply_guardrail_http_status_error_fail_open_forwards_uncompressed(): + guardrail = _make_guardrail(unreachable_fallback="fail_open") + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + side_effect=_make_http_status_error(500, "headroom internal error"), + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert result["structured_messages"] == ORIGINAL_MESSAGES + + +@pytest.mark.asyncio +async def test_apply_guardrail_transport_error_fail_open_forwards_uncompressed(): + guardrail = _make_guardrail(unreachable_fallback="fail_open") + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + side_effect=httpx.ConnectError("Connection refused"), + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert result["structured_messages"] == ORIGINAL_MESSAGES + + +@pytest.mark.asyncio +async def test_apply_guardrail_http_error_fail_open_forwards_uncompressed(): + guardrail = _make_guardrail(unreachable_fallback="fail_open") + mock_response = _make_compress_response([], status=500) + mock_response.text = "Internal Server Error" + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + return_value=mock_response, + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert result["structured_messages"] == ORIGINAL_MESSAGES + + +@pytest.mark.asyncio +async def test_apply_guardrail_non_json_response_fail_open_forwards_uncompressed(): + guardrail = _make_guardrail(unreachable_fallback="fail_open") + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.side_effect = ValueError("not JSON") + mock_response.text = "not json" + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + return_value=mock_response, + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert result["structured_messages"] == ORIGINAL_MESSAGES + + +@pytest.mark.asyncio +async def test_apply_guardrail_missing_messages_key_fail_open_forwards_uncompressed(): + guardrail = _make_guardrail(unreachable_fallback="fail_open") + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"tokens_before": 100, "tokens_after": 10} + mock_response.text = "{}" + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + return_value=mock_response, + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert result["structured_messages"] == ORIGINAL_MESSAGES + + +@pytest.mark.asyncio +async def test_apply_guardrail_empty_compressed_messages_fail_open_forwards_uncompressed(): + guardrail = _make_guardrail(unreachable_fallback="fail_open") + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = { + "messages": ["not-a-dict", 42, None], + "tokens_before": 1000, + "tokens_after": 0, + "compression_ratio": 0, + } + mock_response.text = "{}" + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + return_value=mock_response, + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert result["structured_messages"] == ORIGINAL_MESSAGES + + +@pytest.mark.asyncio +async def test_apply_guardrail_fail_open_does_not_register_hashes_from_original_messages(): + """When compression fails with fail_open, user-supplied messages that + happen to contain hash-shaped strings must NOT cause those hashes to be + registered as valid for CCR retrieval. Otherwise an attacker can plant a + hash= string in their prompt, trigger a compression failure, and have + that hash honored by a later headroom_retrieve tool call.""" + messages_with_fake_hash = [ + {"role": "user", "content": "Please fetch hash=deadbeef000000000000dead for me"}, + ] + guardrail = _make_guardrail(unreachable_fallback="fail_open") + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=messages_with_fake_hash, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + side_effect=httpx.ConnectError("Connection refused"), + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert result["structured_messages"] == messages_with_fake_hash + assert not has_headroom_retrieve_tool(result.get("tools") or []) + assert not guardrail._issued_hashes_by_call_id + + @pytest.mark.asyncio async def test_apply_guardrail_missing_messages_key_raises(): guardrail = _make_guardrail() @@ -273,13 +1118,21 @@ def test_init_raises_without_api_base(): HeadroomGuardrail(api_base=None) +def test_init_defaults_to_fail_closed(): + guardrail = _make_guardrail() + assert guardrail.unreachable_fallback == "fail_closed" + + +def test_init_rejects_invalid_unreachable_fallback_value(): + guardrail = _make_guardrail(unreachable_fallback="not-a-real-mode") + assert guardrail.unreachable_fallback == "fail_closed" + + def test_bypass_header_case_insensitive(): guardrail = _make_guardrail() for header_value in ("true", "True", "TRUE"): - data = { - "proxy_server_request": {"headers": {"x-headroom-bypass": header_value}} - } + data = {"proxy_server_request": {"headers": {"x-headroom-bypass": header_value}}} assert guardrail._should_bypass(data) is True data = {"proxy_server_request": {"headers": {"x-headroom-bypass": "false"}}} @@ -344,3 +1197,176 @@ async def test_apply_guardrail_sends_model_from_request_data_when_no_config_mode call_kwargs = mock_post.call_args sent_payload = call_kwargs.kwargs.get("json") or call_kwargs.args[1] assert sent_payload.get("model") == "gpt-4o" + + +@pytest.mark.asyncio +async def test_async_should_run_agentic_loop_detects_anthropic_content_block_format( + guardrail: HeadroomGuardrail, +): + # Anthropic's native tool format (type: "custom", top-level "name") -- + # by the time a Messages API response reaches this gate, the OpenAI-shaped + # tool this guardrail injects has already been transformed into this shape. + retrieve_tool_def = [ + { + "type": "custom", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "input_schema": {"type": "object", "properties": {"hash": {"type": "string"}}}, + } + ] + + response = MagicMock() + response.choices = None + response.content = [ + { + "type": "tool_use", + "id": "toolu_abc", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "input": {"hash": "b573993006976af767214fac"}, + } + ] + + should_run, ctx = await guardrail.async_should_run_agentic_loop( + response=response, + model="claude-sonnet-4-6", + messages=[], + tools=retrieve_tool_def, + stream=False, + custom_llm_provider="anthropic", + kwargs={}, + ) + + assert should_run is True + assert len(ctx["tool_calls"]) == 1 + assert ctx["tool_calls"][0]["arguments"]["hash"] == "b573993006976af767214fac" + + +@pytest.mark.asyncio +async def test_async_should_run_agentic_loop_detects_anthropic_response_as_plain_dict( + guardrail: HeadroomGuardrail, +): + """AnthropicMessagesResponse is a TypedDict -- real Messages API responses + are plain dicts at runtime, not objects with attribute access. A + MagicMock-only test would pass even if detection used bare getattr() and + silently treated every real response as having no tool calls.""" + retrieve_tool_def = [ + { + "type": "custom", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "input_schema": {"type": "object", "properties": {"hash": {"type": "string"}}}, + } + ] + response = { + "content": [ + { + "type": "tool_use", + "id": "toolu_abc", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "input": {"hash": "b573993006976af767214fac"}, + } + ] + } + + should_run, ctx = await guardrail.async_should_run_agentic_loop( + response=response, + model="claude-sonnet-4-6", + messages=[], + tools=retrieve_tool_def, + stream=False, + custom_llm_provider="anthropic", + kwargs={}, + ) + + assert should_run is True + assert len(ctx["tool_calls"]) == 1 + assert ctx["tool_calls"][0]["arguments"]["hash"] == "b573993006976af767214fac" + + +@pytest.mark.asyncio +async def test_async_should_run_agentic_loop_detects_responses_api_output_format( + guardrail: HeadroomGuardrail, +): + retrieve_tool_def = [{"type": "function", "function": {"name": HEADROOM_RETRIEVE_TOOL_NAME}}] + + response = MagicMock() + response.choices = None + response.content = None + response.output = [ + { + "type": "function_call", + "id": "fc_abc123", + "name": HEADROOM_RETRIEVE_TOOL_NAME, + "arguments": json.dumps({"hash": "b573993006976af767214fac"}), + } + ] + + should_run, ctx = await guardrail.async_should_run_agentic_loop( + response=response, + model="gpt-4o", + messages=[], + tools=retrieve_tool_def, + stream=False, + custom_llm_provider="openai", + kwargs={}, + ) + + assert should_run is True + assert len(ctx["tool_calls"]) == 1 + assert ctx["tool_calls"][0]["arguments"]["hash"] == "b573993006976af767214fac" + + +@pytest.mark.asyncio +async def test_apply_guardrail_litellm_timeout_raises_when_fail_closed(): + guardrail = _make_guardrail() + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + side_effect=litellm.Timeout( + message="Connection timed out after 10 seconds.", + model="default-model-name", + llm_provider="litellm-httpx-handler", + ), + ): + with pytest.raises(HTTPException) as exc_info: + await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert exc_info.value.status_code == 502 + assert "unreachable" in str(exc_info.value.detail) + + +@pytest.mark.asyncio +async def test_apply_guardrail_litellm_timeout_fail_open_forwards_uncompressed(): + guardrail = _make_guardrail(unreachable_fallback="fail_open") + + inputs = GenericGuardrailAPIInputs( + texts=["hello"], + structured_messages=ORIGINAL_MESSAGES, + ) + + with patch.object( + guardrail.async_handler, + "post", + new_callable=AsyncMock, + side_effect=litellm.Timeout( + message="Connection timed out after 10 seconds.", + model="default-model-name", + llm_provider="litellm-httpx-handler", + ), + ): + result = await guardrail.apply_guardrail( + inputs=inputs, + request_data={}, + input_type="request", + ) + + assert result["structured_messages"] == ORIGINAL_MESSAGES diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_model_armor.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_model_armor.py index 64df9ee7ab5..19c200bdaf0 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_model_armor.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_model_armor.py @@ -2940,3 +2940,127 @@ def test_accumulated_responses_are_redactable_as_a_list(): assert "secret-one" not in blob assert "secret-two" not in blob assert blob.count("[REDACTED]") == 2 + + +def _mcp_synthetic_data(tool_name: str = "send_email", arguments: dict = None): + """Mirror ProxyLogging._convert_mcp_to_llm_format: an MCP tool call rendered as a + synthetic user message so the existing prompt-scanning path can inspect it.""" + if arguments is None: + arguments = {"to": "user@example.com", "body": "some content"} + return { + "model": "mcp-tool-call", + "messages": [ + { + "role": "user", + "content": f"Tool: {tool_name}\nArguments: {arguments}", + } + ], + "metadata": {"guardrails": ["model-armor-test"]}, + "mcp_tool_name": tool_name, + "mcp_arguments": arguments, + } + + +@pytest.mark.asyncio +async def test_pre_call_hook_scans_mcp_tool_call_when_configured_for_pre_mcp_call(): + """A guardrail configured with mode `pre_mcp_call` must scan MCP tool calls. + + Regression: async_pre_call_hook hardcoded its event-type gate to `pre_call`, so a + `pre_mcp_call` guardrail's own inner should_run_guardrail check returned False for an + MCP call (call_type=call_mcp_tool) and the scan was skipped entirely -- letting + sensitive content in tool arguments through unscanned. The gate must remap + call_mcp_tool -> pre_mcp_call. + """ + guardrail = _make_guardrail(event_hook="pre_mcp_call", mask_request_content=True) + data = _mcp_synthetic_data() + + with patch.object( + guardrail.async_handler, + "post", + AsyncMock(return_value=_armor_response(blocked=False)), + ) as mock_post: + await guardrail.async_pre_call_hook( + user_api_key_dict=UserAPIKeyAuth(), + cache=MagicMock(spec=DualCache), + data=data, + call_type="call_mcp_tool", + ) + + mock_post.assert_called() + + +@pytest.mark.asyncio +async def test_pre_call_hook_skips_chat_traffic_when_configured_for_pre_mcp_call(): + """A `pre_mcp_call` guardrail must NOT scan ordinary chat completions -- the remap is + scoped to MCP calls, so a `completion` call_type still fails the gate and is skipped.""" + guardrail = _make_guardrail(event_hook="pre_mcp_call", mask_request_content=True) + data = { + "model": "gpt-4", + "messages": [{"role": "user", "content": "hello there"}], + "metadata": {"guardrails": ["model-armor-test"]}, + } + + with patch.object( + guardrail.async_handler, + "post", + AsyncMock(return_value=_armor_response(blocked=False)), + ) as mock_post: + result = await guardrail.async_pre_call_hook( + user_api_key_dict=UserAPIKeyAuth(), + cache=MagicMock(spec=DualCache), + data=data, + call_type="completion", + ) + + assert result == data + mock_post.assert_not_called() + + +@pytest.mark.asyncio +async def test_moderation_hook_scans_mcp_tool_call_when_configured_for_during_mcp_call(): + """A guardrail configured with mode `during_mcp_call` must scan MCP tool calls. + + Regression: async_moderation_hook hardcoded its event-type gate to `during_call`, so a + `during_mcp_call` guardrail skipped MCP calls (call_type=call_mcp_tool). The gate must + remap call_mcp_tool -> during_mcp_call. + """ + guardrail = _make_guardrail(event_hook="during_mcp_call", mask_request_content=True) + data = _mcp_synthetic_data() + + with patch.object( + guardrail.async_handler, + "post", + AsyncMock(return_value=_armor_response(blocked=False)), + ) as mock_post: + await guardrail.async_moderation_hook( + data=data, + user_api_key_dict=UserAPIKeyAuth(), + call_type="call_mcp_tool", + ) + + mock_post.assert_called() + + +@pytest.mark.asyncio +async def test_moderation_hook_skips_chat_traffic_when_configured_for_during_mcp_call(): + """A `during_mcp_call` guardrail must NOT scan ordinary chat completions.""" + guardrail = _make_guardrail(event_hook="during_mcp_call", mask_request_content=True) + data = { + "model": "gpt-4", + "messages": [{"role": "user", "content": "hello there"}], + "metadata": {"guardrails": ["model-armor-test"]}, + } + + with patch.object( + guardrail.async_handler, + "post", + AsyncMock(return_value=_armor_response(blocked=False)), + ) as mock_post: + result = await guardrail.async_moderation_hook( + data=data, + user_api_key_dict=UserAPIKeyAuth(), + call_type="completion", + ) + + assert result == data + mock_post.assert_not_called() diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_xecguard.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_xecguard.py index 60c595c64e5..6e601df897b 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_xecguard.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_xecguard.py @@ -1644,12 +1644,64 @@ class TestXecGuardLoggingHook: ) assert out_kwargs is kwargs assert out_result is result - info = kwargs["standard_logging_object"]["guardrail_information"] + info_list = kwargs["standard_logging_object"]["guardrail_information"] + assert isinstance(info_list, list), "guardrail_information must be a list" + assert len(info_list) == 1 + info = info_list[0] assert info["guardrail_mode"] == "logging_only" - assert info["guardrail_name"] == "xecguard" + assert info["guardrail_name"] == "test-xecguard" assert info["guardrail_status"] == "success" assert info["guardrail_response"]["trace_id"] == "lg-1" + @pytest.mark.asyncio + async def test_async_logging_hook_appends_to_existing_guardrail_info( + self, xecguard_guardrail, mock_request_data + ): + resp = _make_response({"decision": "SAFE", "trace_id": "lg-4"}) + prior_entry = {"guardrail_name": "other-guardrail"} + with patch.object(xecguard_guardrail.async_handler, "post", return_value=resp): + kwargs = { + **mock_request_data, + "standard_logging_object": {"guardrail_information": [prior_entry]}, + } + await xecguard_guardrail.async_logging_hook( + kwargs=kwargs, + result=_build_model_response("some answer"), + call_type="acompletion", + ) + info_list = kwargs["standard_logging_object"]["guardrail_information"] + assert len(info_list) == 2 + assert info_list[0] is prior_entry + assert info_list[1]["guardrail_name"] == "test-xecguard" + assert info_list[1]["guardrail_response"]["trace_id"] == "lg-4" + + @pytest.mark.asyncio + async def test_async_logging_hook_sanitizes_scan_result( + self, xecguard_guardrail, mock_request_data + ): + resp = _make_response( + { + "decision": "SAFE", + "trace_id": "lg-5", + "secret_fields": {"authorization": "Bearer xgs_raw"}, + "detections": [{"match": "raw matched span", "policy": "pii"}], + "api_key": "xgs_super_secret_value", + } + ) + with patch.object(xecguard_guardrail.async_handler, "post", return_value=resp): + kwargs = {**mock_request_data, "standard_logging_object": {}} + await xecguard_guardrail.async_logging_hook( + kwargs=kwargs, + result=_build_model_response("some answer"), + call_type="acompletion", + ) + info = kwargs["standard_logging_object"]["guardrail_information"][0] + guardrail_response = info["guardrail_response"] + assert "secret_fields" not in guardrail_response + assert guardrail_response["detections"][0]["match"] == "[REDACTED]" + assert guardrail_response["api_key"] != "xgs_super_secret_value" + assert guardrail_response["trace_id"] == "lg-5" + @pytest.mark.asyncio async def test_async_logging_hook_without_response_records_info( self, xecguard_guardrail, mock_request_data @@ -1680,7 +1732,9 @@ class TestXecGuardLoggingHook: result=_build_model_response("x"), call_type="acompletion", ) - info = kwargs["standard_logging_object"]["guardrail_information"] + info_list = kwargs["standard_logging_object"]["guardrail_information"] + assert isinstance(info_list, list), "guardrail_information must be a list" + info = info_list[0] assert info["guardrail_status"] == "guardrail_intervened" @pytest.mark.asyncio diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_anthropic_streaming_block.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_anthropic_streaming_block.py new file mode 100644 index 00000000000..865b4164e5e --- /dev/null +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_anthropic_streaming_block.py @@ -0,0 +1,362 @@ +""" +Regression tests for blocking an Anthropic streaming response from the +unified guardrail post-call streaming iterator hook. + +When a guardrail's ``apply_guardrail`` raises ``ModifyResponseException`` while +(or at the end of) an Anthropic ``/v1/messages`` stream is being relayed, the +hook must emit a well-formed Anthropic SSE termination sequence carrying the +block message - NOT a bare ``data: {"error": ...}`` blob that truncates the +stream and causes the Anthropic SDK parser to discard the response. +""" + +import json +from typing import Any, List, Literal, Optional + +import pytest + +from litellm.integrations.custom_guardrail import ( + CustomGuardrail, + ModifyResponseException, +) +from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, +) +from litellm.types.utils import GenericGuardrailAPIInputs + +BLOCK_MESSAGE = "Blocked by policy: this response was withheld." + + +class _BlockingGuardrail(CustomGuardrail): + """Mock guardrail that always blocks by raising ModifyResponseException.""" + + async def apply_guardrail( + self, + inputs: GenericGuardrailAPIInputs, + request_data: dict, + input_type: Literal["request", "response"], + logging_obj: Optional[Any] = None, + ) -> GenericGuardrailAPIInputs: + raise ModifyResponseException( + message=BLOCK_MESSAGE, + model="claude-3-5-sonnet", + request_data=request_data, + guardrail_name=self.guardrail_name, + ) + + +def _sse_event(event_type: str, data: dict) -> bytes: + return f"event: {event_type}\ndata: {json.dumps(data)}\n\n".encode() + + +async def _anthropic_stream(end: bool): + """Yield Anthropic SSE byte chunks. If end=True, include a terminating + message_delta (stop_reason set) so the hook's end-of-stream path runs.""" + yield _sse_event( + "message_start", + { + "type": "message_start", + "message": { + "id": "msg_orig", + "type": "message", + "role": "assistant", + "model": "claude-3-5-sonnet", + "content": [], + "stop_reason": None, + "usage": {"input_tokens": 1, "output_tokens": 0}, + }, + }, + ) + yield _sse_event( + "content_block_start", + { + "type": "content_block_start", + "index": 0, + "content_block": {"type": "text", "text": ""}, + }, + ) + for text in ["This ", "is ", "the ", "original ", "answer."]: + yield _sse_event( + "content_block_delta", + { + "type": "content_block_delta", + "index": 0, + "delta": {"type": "text_delta", "text": text}, + }, + ) + if end: + yield _sse_event("content_block_stop", {"type": "content_block_stop", "index": 0}) + yield _sse_event( + "message_delta", + { + "type": "message_delta", + "delta": {"stop_reason": "end_turn", "stop_sequence": None}, + "usage": {"output_tokens": 5}, + }, + ) + yield _sse_event("message_stop", {"type": "message_stop"}) + + +def _decode(chunks: List[Any]) -> str: + parts = [] + for chunk in chunks: + parts.append(chunk.decode() if isinstance(chunk, bytes) else str(chunk)) + return "".join(parts) + + +def _parse_sse_event_types(raw: str) -> List[str]: + event_types = [] + for block in raw.split("\n\n"): + for line in block.strip().split("\n"): + if line.startswith("data:"): + payload = line[len("data:") :].strip() + try: + event_types.append(json.loads(payload).get("type")) + except json.JSONDecodeError: + pass + return event_types + + +async def _run_hook(end: bool, sampling_rate: int = 1, end_of_stream_only: bool = False) -> str: + guardrail = _BlockingGuardrail(guardrail_name="test-blocking-guardrail", event_hook="post_call") + # sampling_rate controls how many chunks are forwarded before the block + # fires: 1 blocks on the first chunk (nothing sent yet); >1 forwards earlier + # chunks first, exercising the mid-stream "continue the message" path. + guardrail.streaming_sampling_rate = sampling_rate + guardrail.streaming_end_of_stream_only = end_of_stream_only + + unified_guardrail = UnifiedLLMGuardrails() + user_api_key_dict = UserAPIKeyAuth(api_key="test", request_route="/v1/messages") + request_data = { + "messages": [{"role": "user", "content": "hi"}], + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": ["test-blocking-guardrail"]}, + } + + collected: List[Any] = [] + async for chunk in unified_guardrail.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=_anthropic_stream(end=end), + request_data=request_data, + ): + collected.append(chunk) + return _decode(collected) + + +def _assert_clean_block_termination(raw: str) -> None: + # No bare error blob that would truncate the stream. + assert '"error"' not in raw, f"unexpected error blob in stream: {raw!r}" + # The block message is delivered as assistant text. + assert BLOCK_MESSAGE in raw, f"block message missing from stream: {raw!r}" + # A complete, parseable Anthropic SSE termination sequence is present. + event_types = _parse_sse_event_types(raw) + assert "message_start" in event_types + assert "content_block_delta" in event_types + # Exactly one message_start: a block must never inject a second + # message envelope into an already-started stream (clients reject it). + assert event_types.count("message_start") == 1, f"expected a single message_start, got: {event_types}" + assert event_types[-1] == "message_stop", f"stream did not end cleanly: {event_types}" + # message_delta carries a stop_reason. + assert any('"stop_reason"' in block and "message_delta" in block for block in raw.split("\n\n")) + + +def _parse_sse_payloads(raw: str) -> List[dict]: + payloads = [] + for block in raw.split("\n\n"): + for line in block.strip().split("\n"): + if line.startswith("data:"): + payload = line[len("data:") :].strip() + try: + parsed = json.loads(payload) + except json.JSONDecodeError: + continue + if isinstance(parsed, dict): + payloads.append(parsed) + return payloads + + +@pytest.mark.asyncio +async def test_mid_stream_block_emits_clean_anthropic_sse(): + """Per-chunk block: a clean SSE termination with the block message, no error blob.""" + raw = await _run_hook(end=False) + _assert_clean_block_termination(raw) + + +@pytest.mark.asyncio +async def test_end_of_stream_block_emits_clean_anthropic_sse(): + """End-of-stream block: same clean SSE termination guarantees.""" + raw = await _run_hook(end=True) + _assert_clean_block_termination(raw) + + +@pytest.mark.asyncio +async def test_mid_stream_block_after_prior_chunks_continues_message(): + """Regression: when real chunks were already forwarded (sampling_rate>1), + the block must continue the in-progress message, not start a second one.""" + raw = await _run_hook(end=False, sampling_rate=5) + # Some original content was forwarded before the block... + assert "message_start" in raw + # ...and the block continues that same message (single message_start) with + # the block message appended, ending cleanly. + _assert_clean_block_termination(raw) + + +@pytest.mark.asyncio +async def test_end_of_stream_only_block_does_not_append_after_message_stop(): + raw = await _run_hook(end=True, end_of_stream_only=True) + event_types = _parse_sse_event_types(raw) + message_delta_usages = [ + payload.get("usage", {}).get("output_tokens") + for payload in _parse_sse_payloads(raw) + if payload.get("type") == "message_delta" + ] + + assert BLOCK_MESSAGE in raw + assert event_types.count("message_stop") == 1 + assert event_types[-1] == "message_stop" + assert message_delta_usages[-1] == 5 + + +def test_blocked_stream_reports_usage_from_original_chunks(): + from litellm.integrations.custom_guardrail import ModifyResponseException + from litellm.llms.anthropic.chat.guardrail_translation.handler import ( + AnthropicMessagesHandler, + ) + from litellm.llms.base_llm.guardrail_translation.utils import ( + blocked_response_usage, + ) + + original_chunks: List[Any] = [ + _sse_event( + "message_start", + { + "type": "message_start", + "message": { + "id": "msg_orig", + "type": "message", + "role": "assistant", + "model": "claude-3-5-sonnet", + "content": [], + "stop_reason": None, + "usage": {"input_tokens": 12, "output_tokens": 0}, + }, + }, + ), + {"type": "message_delta", "delta": {"stop_reason": "end_turn"}, "usage": {"output_tokens": 5}}, + ] + seen_chunks = [ + _sse_event( + "content_block_start", + {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}, + ) + ] + exc = ModifyResponseException( + message=BLOCK_MESSAGE, + model="claude-3-5-sonnet", + request_data={}, + guardrail_name="g", + original_response=original_chunks, + ) + + usage = blocked_response_usage(original_chunks) + raw = b"".join( + AnthropicMessagesHandler().build_block_sse_chunks(exc, stream_started=True, responses_so_far=seen_chunks) + ).decode() + message_delta_usages = [ + payload.get("usage", {}).get("output_tokens") + for payload in _parse_sse_payloads(raw) + if payload.get("type") == "message_delta" + ] + + assert usage == {"input_tokens": 12, "output_tokens": 5} + assert message_delta_usages[-1] == 5 + + +class TestContentBlockState: + """`_content_block_state` must reflect the true open/last block index across + the two chunk formats the stream can carry (multi-event bytes, parsed dict), + so a mid-stream block closes/opens the right indices.""" + + def _handler(self): + from litellm.llms.anthropic.chat.guardrail_translation.handler import ( + AnthropicMessagesHandler, + ) + + return AnthropicMessagesHandler() + + def test_multi_event_bytes_chunk_is_fully_parsed(self): + # One item bundles start(0) + delta + stop(0): the block is already + # closed, so open_index is None (not 0) and max_index is 0. + bundled = ( + _sse_event( + "content_block_start", + {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}, + ) + + _sse_event( + "content_block_delta", + {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "hi"}}, + ) + + _sse_event("content_block_stop", {"type": "content_block_stop", "index": 0}) + ) + open_index, max_index = self._handler()._content_block_state([bundled]) + assert open_index is None + assert max_index == 0 + + def test_open_block_across_separate_chunks(self): + chunks = [ + _sse_event( + "content_block_start", + {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}, + ), + _sse_event("content_block_stop", {"type": "content_block_stop", "index": 0}), + _sse_event( + "content_block_start", + {"type": "content_block_start", "index": 1, "content_block": {"type": "text", "text": ""}}, + ), + ] + open_index, max_index = self._handler()._content_block_state(chunks) + assert open_index == 1 + assert max_index == 1 + + def test_dict_format_chunks_are_parsed(self): + # The backwards-compat parsed-dict format must be understood too. + chunks = [ + {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}, + {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "x"}}, + ] + open_index, max_index = self._handler()._content_block_state(chunks) + assert open_index == 0 + assert max_index == 0 + + def test_continuation_closes_open_block_and_appends_after_it(self): + from litellm.integrations.custom_guardrail import ModifyResponseException + + handler = self._handler() + # Client has seen an open text block at index 0. + seen = [ + _sse_event( + "content_block_start", + {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}, + ), + _sse_event( + "content_block_delta", + {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "partial"}}, + ), + ] + exc = ModifyResponseException( + message=BLOCK_MESSAGE, model="claude-3-5-sonnet", request_data={}, guardrail_name="g" + ) + raw = b"".join(handler.build_block_sse_chunks(exc, stream_started=True, responses_so_far=seen)).decode() + events = _parse_sse_event_types(raw) + # No new message envelope, closes block 0, appends block text at index 1. + assert "message_start" not in events + assert events == [ + "content_block_stop", + "content_block_start", + "content_block_delta", + "content_block_stop", + "message_delta", + "message_stop", + ] + assert BLOCK_MESSAGE in raw + assert '"index": 1' in raw diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_streaming_buffer_until_moderated.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_streaming_buffer_until_moderated.py new file mode 100644 index 00000000000..2b163ee5233 --- /dev/null +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_streaming_buffer_until_moderated.py @@ -0,0 +1,173 @@ +""" +Tests for ``streaming_buffer_until_moderated`` on the unified guardrail +post-call streaming iterator hook. + +With this flag set, the hook must withhold every upstream chunk until +end-of-stream moderation has run. The decisive guarantee versus the +detect-only ``streaming_end_of_stream_only`` behavior: when the guardrail +blocks, the original (objectionable) content is NEVER yielded to the client -- +only the block message is. On a clean response, all original chunks are +released unchanged after moderation passes. +""" + +import json +from typing import Any, List, Literal, Optional + +import pytest + +from litellm.integrations.custom_guardrail import ( + CustomGuardrail, + ModifyResponseException, +) +from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + UnifiedLLMGuardrails, +) +from litellm.types.utils import GenericGuardrailAPIInputs + +BLOCK_MESSAGE = "Blocked by policy: this response was withheld." +ORIGINAL_MARKER = "ORIGINAL-SECRET-ANSWER" + + +class _BlockingGuardrail(CustomGuardrail): + """Always blocks at moderation time.""" + + async def apply_guardrail( + self, + inputs: GenericGuardrailAPIInputs, + request_data: dict, + input_type: Literal["request", "response"], + logging_obj: Optional[Any] = None, + ) -> GenericGuardrailAPIInputs: + raise ModifyResponseException( + message=BLOCK_MESSAGE, + model="claude-3-5-sonnet", + request_data=request_data, + guardrail_name=self.guardrail_name, + ) + + +class _PassingGuardrail(CustomGuardrail): + """Never blocks; returns inputs unchanged.""" + + async def apply_guardrail( + self, + inputs: GenericGuardrailAPIInputs, + request_data: dict, + input_type: Literal["request", "response"], + logging_obj: Optional[Any] = None, + ) -> GenericGuardrailAPIInputs: + return inputs + + +def _sse_event(event_type: str, data: dict) -> bytes: + return f"event: {event_type}\ndata: {json.dumps(data)}\n\n".encode() + + +async def _anthropic_stream(): + """A complete Anthropic /v1/messages SSE stream whose assistant text + contains ORIGINAL_MARKER so leakage is unambiguous to assert.""" + yield _sse_event( + "message_start", + { + "type": "message_start", + "message": { + "id": "msg_orig", + "type": "message", + "role": "assistant", + "model": "claude-3-5-sonnet", + "content": [], + "stop_reason": None, + "usage": {"input_tokens": 1, "output_tokens": 0}, + }, + }, + ) + yield _sse_event( + "content_block_start", + { + "type": "content_block_start", + "index": 0, + "content_block": {"type": "text", "text": ""}, + }, + ) + for text in ["Here is ", "the ", ORIGINAL_MARKER, " for you."]: + yield _sse_event( + "content_block_delta", + { + "type": "content_block_delta", + "index": 0, + "delta": {"type": "text_delta", "text": text}, + }, + ) + yield _sse_event("content_block_stop", {"type": "content_block_stop", "index": 0}) + yield _sse_event( + "message_delta", + { + "type": "message_delta", + "delta": {"stop_reason": "end_turn", "stop_sequence": None}, + "usage": {"output_tokens": 5}, + }, + ) + yield _sse_event("message_stop", {"type": "message_stop"}) + + +def _decode(chunks: List[Any]) -> str: + return "".join(c.decode() if isinstance(c, bytes) else str(c) for c in chunks) + + +async def _run(guardrail: CustomGuardrail) -> str: + # Rubrik's real config: end-of-stream-only moderation. Without buffering + # this releases every chunk before moderation runs (content leaks on + # block); the buffer flag must change that to moderate-then-release. + guardrail.streaming_end_of_stream_only = True + guardrail.streaming_buffer_until_moderated = True + unified = UnifiedLLMGuardrails() + user_api_key_dict = UserAPIKeyAuth(api_key="test", request_route="/v1/messages") + request_data = { + "messages": [{"role": "user", "content": "hi"}], + "guardrail_to_apply": guardrail, + "metadata": {"guardrails": [guardrail.guardrail_name]}, + } + collected: List[Any] = [] + async for chunk in unified.async_post_call_streaming_iterator_hook( + user_api_key_dict=user_api_key_dict, + response=_anthropic_stream(), + request_data=request_data, + ): + collected.append(chunk) + return _decode(collected) + + +@pytest.mark.asyncio +async def test_buffered_block_withholds_original_content(): + raw = await _run(_BlockingGuardrail(guardrail_name="blk", event_hook="post_call")) + # The original content must never reach the client... + assert ORIGINAL_MARKER not in raw, f"original content leaked: {raw!r}" + # ...only the block message, in a clean terminating stream. + assert BLOCK_MESSAGE in raw + assert '"error"' not in raw + + +@pytest.mark.asyncio +async def test_buffered_clean_releases_all_content(): + raw = await _run(_PassingGuardrail(guardrail_name="pass", event_hook="post_call")) + # A clean response is released in full after moderation passes. + assert ORIGINAL_MARKER in raw + assert ( + raw.rstrip().endswith('event: message_stop\ndata: {"type": "message_stop"}'.rstrip()) or "message_stop" in raw + ) + assert BLOCK_MESSAGE not in raw + + +@pytest.mark.asyncio +async def test_buffered_mode_disabled_for_content_rewriting_guardrail(): + """Buffered replay yields the withheld *original* chunks verbatim, which + is unsafe for a guardrail that rewrites response text (e.g. PII masking): + the client would get the unredacted original instead of the moderated + output. mask_response_content=True must force buffering off so the + request falls back to the (correctly moderated) non-buffered path.""" + guardrail = _PassingGuardrail(guardrail_name="masker", event_hook="post_call", mask_response_content=True) + raw = await _run(guardrail) + assert guardrail.streaming_buffer_until_moderated is True # request asked for buffering + assert ORIGINAL_MARKER in raw + assert BLOCK_MESSAGE not in raw diff --git a/tests/test_litellm/proxy/guardrails/test_content_utils.py b/tests/test_litellm/proxy/guardrails/test_content_utils.py index 099fca78a62..34d92505359 100644 --- a/tests/test_litellm/proxy/guardrails/test_content_utils.py +++ b/tests/test_litellm/proxy/guardrails/test_content_utils.py @@ -8,7 +8,6 @@ from litellm.proxy.guardrails._content_utils import ( walk_user_text, ) - # ── iter_message_text ──────────────────────────────────────────────────────────── @@ -101,6 +100,55 @@ def test_iter_message_text_empty_data(): assert list(iter_message_text({"input": ""})) == [] +def test_iter_message_text_responses_api_input_text_and_output_text_parts(): + """LIT-4294: Responses-API content parts use ``input_text`` (request) and + ``output_text`` (assistant); reading only ``type == "text"`` skipped every + ``/v1/responses`` body and every text guardrail was a no-op on that path.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "user text"}], + }, + { + "type": "message", + "role": "assistant", + "content": [{"type": "output_text", "text": "assistant text"}], + }, + ] + } + assert list(iter_message_text(data)) == ["user text", "assistant text"] + + +def test_iter_message_text_responses_api_tool_call_taxonomy(): + """LIT-4294: a Responses ``input`` list freely mixes message items, + ``function_call`` (no ``role``), and ``function_call_output`` items. The + old ``all(item has 'role')`` gate wrapped the whole list as one blob and + yielded nothing; every text fragment must be visited independently.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "hello"}], + }, + { + "type": "function_call", + "call_id": "c1", + "name": "get_weather", + "arguments": "{}", + }, + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "sunny"}], + }, + ] + } + assert list(iter_message_text(data)) == ["hello", "sunny"] + + # ── walk_user_text ──────────────────────────────────────────────────────────── @@ -160,6 +208,89 @@ def test_walk_user_text_redacts_responses_api_list_input(): assert data["input"][1] == {"type": "image_url", "image_url": {"url": "..."}} +def test_walk_user_text_redacts_responses_input_text_and_output_text_parts(): + """LIT-4294: ``walk_user_text`` must recognise the Responses text-part + variants so masking guardrails (secret detection, PII) actually redact + ``/v1/responses`` bodies instead of no-op'ing on them.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "AKIAEXAMPLE"}], + }, + { + "type": "message", + "role": "assistant", + "content": [{"type": "output_text", "text": "AKIAEXAMPLE too"}], + }, + ] + } + visited = walk_user_text(data, lambda s: s.replace("AKIAEXAMPLE", "[REDACTED]")) + assert visited == 2 + assert data["input"][0]["content"][0] == { + "type": "input_text", + "text": "[REDACTED]", + } + assert data["input"][1]["content"][0] == { + "type": "output_text", + "text": "[REDACTED] too", + } + + +def test_walk_user_text_redacts_function_call_output_text(): + """LIT-4294: tool-call round-trips carry secrets in + ``function_call_output.output``; the redact walker must descend into it + while leaving ``function_call`` items (call_id, arguments) untouched.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "AKIAEXAMPLE user"}], + }, + { + "type": "function_call", + "call_id": "c1", + "name": "get_weather", + "arguments": '{"AKIAEXAMPLE": 1}', + }, + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "AKIAEXAMPLE tool"}], + }, + ] + } + visited = walk_user_text(data, lambda s: s.replace("AKIAEXAMPLE", "[REDACTED]")) + assert visited == 2 + assert data["input"][0]["content"][0]["text"] == "[REDACTED] user" + assert data["input"][1] == { + "type": "function_call", + "call_id": "c1", + "name": "get_weather", + "arguments": '{"AKIAEXAMPLE": 1}', + } + assert data["input"][2]["output"][0]["text"] == "[REDACTED] tool" + + +def test_walk_user_text_redacts_function_call_output_string_output(): + """LIT-4294: ``function_call_output.output`` is also a plain string in + OpenAI's Responses spec; the redact walker must handle both forms.""" + data = { + "input": [ + { + "type": "function_call_output", + "call_id": "c1", + "output": "AKIAEXAMPLE tool", + }, + ] + } + visited = walk_user_text(data, lambda s: s.replace("AKIAEXAMPLE", "[REDACTED]")) + assert visited == 1 + assert data["input"][0]["output"] == "[REDACTED] tool" + + def test_walk_user_text_redacts_mixed_list_input(): """Read and write helpers must agree on coverage — bare strings inside a mixed ``input`` list are inspected by both.""" @@ -206,17 +337,13 @@ def test_build_inspection_messages_joins_multimodal_text_parts(): } ] } - assert build_inspection_messages(data) == [ - {"role": "user", "content": "first part\nsecond part"} - ] + assert build_inspection_messages(data) == [{"role": "user", "content": "first part\nsecond part"}] def test_build_inspection_messages_lifts_responses_api_input(): """fniVO9-F: ``input`` must be visible to hooks that POST messages to a remote API.""" data = {"input": "responses-api content"} - assert build_inspection_messages(data) == [ - {"role": "user", "content": "responses-api content"} - ] + assert build_inspection_messages(data) == [{"role": "user", "content": "responses-api content"}] def test_build_inspection_messages_drops_messages_with_no_text(): @@ -233,6 +360,102 @@ def test_build_inspection_messages_drops_messages_with_no_text(): assert build_inspection_messages(data) == [{"role": "user", "content": "kept"}] +def test_build_inspection_messages_responses_api_tool_call_taxonomy(): + """LIT-4294: mixed Responses ``input`` (message + function_call + + function_call_output) must produce a non-empty inspection list. The + customer's writeup reproduced a 422 from AIM's ``/fw/v1/analyze`` + (``No messages in the request``) when this synthesised list came back + empty; every other guardrail silently scanned nothing on the same + input.""" + data = { + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "hello"}], + }, + { + "type": "function_call", + "call_id": "c1", + "name": "get_weather", + "arguments": "{}", + }, + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "sunny"}], + }, + ] + } + assert build_inspection_messages(data) == [ + {"role": "user", "content": "hello"}, + {"role": "tool", "content": "sunny"}, + ] + + +def test_build_inspection_messages_function_call_output_defaults_to_tool(): + """LIT-4294: a Responses ``function_call_output`` item is the semantic + equivalent of a chat-completions ``role: "tool"`` message, so the shared + helper synthesises ``role: "tool"`` when the item has no explicit role. + AIM's schema-safe coercion happens at the AIM call site, not here.""" + data = { + "input": [ + { + "type": "function_call_output", + "call_id": "c1", + "output": [{"type": "input_text", "text": "tool text"}], + }, + ] + } + assert build_inspection_messages(data) == [{"role": "tool", "content": "tool text"}] + + +def test_build_inspection_messages_function_call_output_preserves_explicit_role(): + """When ``function_call_output`` carries a caller-supplied ``role`` the + shared helper preserves it rather than synthesising ``tool``.""" + data = { + "input": [ + { + "type": "function_call_output", + "role": "assistant", + "call_id": "c1", + "output": [{"type": "input_text", "text": "tool text"}], + }, + ] + } + assert build_inspection_messages(data) == [{"role": "assistant", "content": "tool text"}] + + +def test_build_inspection_messages_bare_content_part_preserves_explicit_role(): + """A bare content-part dict with an explicit ``role`` keeps it. Only + absent roles get defaulted to ``user``.""" + data = { + "input": [ + {"type": "input_text", "text": "no role"}, + {"type": "output_text", "role": "assistant", "text": "with role"}, + ] + } + assert build_inspection_messages(data) == [ + {"role": "user", "content": "no role"}, + {"role": "assistant", "content": "with role"}, + ] + + +def test_build_inspection_messages_message_item_preserves_role(): + """Responses message items carry a role explicitly; the shared helper + passes it through untouched.""" + data = { + "input": [ + {"type": "message", "role": "system", "content": [{"type": "input_text", "text": "sys"}]}, + {"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "asst"}]}, + ] + } + assert build_inspection_messages(data) == [ + {"role": "system", "content": "sys"}, + {"role": "assistant", "content": "asst"}, + ] + + def test_build_inspection_messages_empty_data(): assert build_inspection_messages({}) == [] assert build_inspection_messages({"messages": []}) == [] diff --git a/tests/test_litellm/proxy/guardrails/test_guardrail_endpoints.py b/tests/test_litellm/proxy/guardrails/test_guardrail_endpoints.py index ce8f0802ae1..21e7186fca3 100644 --- a/tests/test_litellm/proxy/guardrails/test_guardrail_endpoints.py +++ b/tests/test_litellm/proxy/guardrails/test_guardrail_endpoints.py @@ -25,6 +25,7 @@ from litellm.proxy.guardrails.guardrail_endpoints import ( delete_guardrail, get_guardrail_info, get_guardrail_submission, + get_guardrail_ui_settings, list_guardrail_submissions, list_guardrails_v2, patch_guardrail, @@ -2079,3 +2080,135 @@ async def test_list_submissions_summary_counts_unaffected_by_filters(mocker): assert result.summary.total == 2 # unfiltered assert result.summary.pending_review == 1 assert result.summary.active == 1 + + +@pytest.mark.asyncio +async def test_get_guardrail_ui_settings_returns_per_provider_supported_modes(): + """ + Regression test for LIT-4226. The Admin UI used to render `pre_mcp_call` as a + selectable mode for every guardrail because the settings endpoint returned a + single global `supported_modes` list. The proxy then rejected the save because + Content Filter and Tool Permission do not accept `pre_mcp_call`. The endpoint + must now return per-provider modes so the UI can filter its dropdown. + """ + result = await get_guardrail_ui_settings() + + modes_by_provider = result.supported_modes_by_provider + + # Content Filter now supports pre_mcp_call (LIT-4226 feature half) but not + # during_mcp_call; Tool Permission still supports neither, and the settings + # endpoint must reflect both so the UI shows exactly the savable modes. + assert "pre_mcp_call" in modes_by_provider["litellm_content_filter"] + assert "during_mcp_call" not in modes_by_provider["litellm_content_filter"] + assert modes_by_provider["tool_permission"] == ["pre_call", "post_call"] + + # MCP-capable guardrails must still advertise the MCP hooks so users who + # picked one of them can actually configure pre_mcp_call / during_mcp_call. + for provider in ("bedrock", "panw_prisma_airs", "cisco_ai_defense", "custom_code", "pillar"): + assert "pre_mcp_call" in modes_by_provider[provider], provider + assert "during_mcp_call" in modes_by_provider[provider], provider + + # The union list stays exhaustive for legacy clients that ignore the + # per-provider map; it must cover every declared GuardrailEventHooks value. + from litellm.types.guardrails import GuardrailEventHooks + + assert set(result.supported_modes) == {m.value for m in GuardrailEventHooks} + + +@pytest.mark.asyncio +async def test_ui_settings_map_matches_runtime_supported_event_hooks(): + """ + Regression guard against the two-copy-of-the-list drift risk. The map the + UI reads must agree with what CustomGuardrail._validate_event_hook accepts + at save time, otherwise the bug in LIT-4226 comes back one classname at a + time as future guardrails drift. + """ + from litellm.proxy.guardrails.guardrail_registry import guardrail_class_registry + + result = await get_guardrail_ui_settings() + + for provider, guardrail_class in guardrail_class_registry.items(): + declared = guardrail_class.get_supported_event_hooks() + if declared is None: + assert ( + provider not in result.supported_modes_by_provider + ), f"{provider} returned None from classmethod but appears in map" + continue + + assert provider in result.supported_modes_by_provider, provider + assert result.supported_modes_by_provider[provider] == [ + hook.value for hook in declared + ], provider + + +def test_content_filter_runtime_rejects_unsupported_mcp_hook(): + """ + Locks the runtime side of the LIT-4226 contract: the ContentFilterGuardrail + validator must reject a hook missing from its supported list at + construction. pre_mcp_call is supported since the LIT-4226 feature half, so + during_mcp_call is the unsupported example now. If someone widens the + UI classmethod but forgets to widen the runtime supported_event_hooks (or + vice versa), the two-lists-must-agree test above catches the drift and this + test catches the specific bug the ticket reported. + """ + from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_filter import ( + ContentFilterGuardrail, + ) + from litellm.types.guardrails import GuardrailEventHooks + + with pytest.raises(ValueError, match="not in the supported event hooks"): + ContentFilterGuardrail( + guardrail_name="lit4226-runtime-check", + event_hook=GuardrailEventHooks.during_mcp_call, + ) + + +def test_model_armor_runtime_supported_event_hooks_match_classmethod(): + """ + Regression for the drift Round 2 caught: the ModelArmorGuardrail classmethod + declared its supported hooks for the UI, but __init__ did not seed the + runtime instance's `supported_event_hooks` from that classmethod, so the + runtime validator accepted any hook (including nonsense like logging_only) + while the UI hid them. Ensures the two sides agree at instantiation time. + """ + from litellm.proxy.guardrails.guardrail_hooks.model_armor.model_armor import ( + ModelArmorGuardrail, + ) + + instance = ModelArmorGuardrail( + guardrail_name="lit4226-model-armor-drift", + template_id="t", + project_id="p", + ) + assert instance.supported_event_hooks == ModelArmorGuardrail.get_supported_event_hooks() + + +def test_strict_guardrail_modes_flag_controls_raise_vs_warn(monkeypatch, caplog): + """ + Escape hatch for the boot-time behavior change. Deployments upgrading from + a build where a guardrail previously silently no-op'd on an unsupported + mode should be able to set LITELLM_STRICT_GUARDRAIL_MODES=false and boot + with a warning instead of a hard failure while they fix their config. + """ + import logging + + from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_filter import ( + ContentFilterGuardrail, + ) + from litellm.types.guardrails import GuardrailEventHooks + + monkeypatch.delenv("LITELLM_STRICT_GUARDRAIL_MODES", raising=False) + with pytest.raises(ValueError, match="not in the supported event hooks"): + ContentFilterGuardrail( + guardrail_name="lit4226-strict-default", + event_hook=GuardrailEventHooks.during_mcp_call, + ) + + monkeypatch.setenv("LITELLM_STRICT_GUARDRAIL_MODES", "false") + with caplog.at_level(logging.WARNING): + instance = ContentFilterGuardrail( + guardrail_name="lit4226-strict-off", + event_hook=GuardrailEventHooks.during_mcp_call, + ) + assert instance is not None + assert any("not in the supported event hooks" in rec.message for rec in caplog.records) diff --git a/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py b/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py index a04ad5598df..917bedcb93f 100644 --- a/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py +++ b/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py @@ -696,6 +696,309 @@ async def test_test_model_connection_falls_back_to_deployments_zero_without_id() assert model_params.get("api_key") == "fake-key-A" +@pytest.mark.asyncio +async def test_test_model_connection_uses_loaded_deployment_team_id(): + """ + /health/test_connection must authorize using the team_id of the + deployment it actually loaded (by model_info.id), not the team_id + supplied in the request body. Requesting team A's deployment while + authenticated as an admin of team B must be denied. + """ + from fastapi import HTTPException + + from litellm.proxy._types import LiteLLM_TeamTable + from litellm.proxy.management_endpoints.model_management_endpoints import ( + ModelManagementAuthChecks, + ) + from litellm.types.router import Deployment, LiteLLM_Params, ModelInfo + + mock_request = MagicMock() + + requester_team_id = "team-b" + deployment_owner_team_id = "team-a" + deployment_id = "team-a-deployment-id" + + requester_user_api_key_dict = UserAPIKeyAuth( + token="requester-token", + user_id="team-b-admin-user", + team_id=requester_team_id, + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + mock_prisma_client = MagicMock() + + other_team_deployment = Deployment( + model_name="team-a-model", + litellm_params=LiteLLM_Params( + model="openai/gpt-4o", + api_base="https://team-a-api.invalid/v1", + api_key="TEAM-A-API-KEY", + ), + model_info=ModelInfo(id=deployment_id, team_id=deployment_owner_team_id), + ) + + mock_router = MagicMock() + mock_router.get_deployment.return_value = other_team_deployment + + async def fake_find_unique(*, where): + team_id = where["team_id"] + if team_id == requester_team_id: + return SimpleNamespace( + model_dump=lambda: LiteLLM_TeamTable( + team_id=requester_team_id, + members_with_roles=[ + { + "user_id": "team-b-admin-user", + "role": "admin", + } + ], + ).model_dump() + ) + if team_id == deployment_owner_team_id: + return SimpleNamespace( + model_dump=lambda: LiteLLM_TeamTable( + team_id=deployment_owner_team_id, + members_with_roles=[], + ).model_dump() + ) + return None + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), + patch("litellm.proxy.proxy_server.llm_router", mock_router), + patch("litellm.proxy.proxy_server.premium_user", True), + patch.object( + ModelManagementAuthChecks, + "can_user_make_model_call", + wraps=ModelManagementAuthChecks.can_user_make_model_call, + ) as spy_auth_check, + patch( + "litellm.proxy.management_endpoints.model_management_endpoints.TeamRepository" + ) as MockTeamRepo, + ): + mock_team_repo_instance = MagicMock() + mock_team_repo_instance.table.find_unique = AsyncMock( + side_effect=fake_find_unique + ) + MockTeamRepo.return_value = mock_team_repo_instance + + with pytest.raises(HTTPException) as exc_info: + await health_test_model_connection( + request=mock_request, + mode="chat", + litellm_params={ + "model": "openai/gpt-4o", + "api_base": "https://swapped-base.invalid/v1", + }, + model_info={ + "id": deployment_id, + "team_id": requester_team_id, + }, + user_api_key_dict=requester_user_api_key_dict, + ) + + assert exc_info.value.status_code == 403 + assert spy_auth_check.called + passed_model_params = spy_auth_check.call_args.kwargs["model_params"] + assert passed_model_params.model_info.team_id == deployment_owner_team_id, ( + "Auth check must run against the loaded deployment's team_id " + f"({deployment_owner_team_id!r}); got " + f"{passed_model_params.model_info.team_id!r}." + ) + + +@pytest.mark.asyncio +async def test_test_model_connection_uses_loaded_deployment_team_id_via_model_name_fallback(): + """ + Companion to the id-lookup case: when the caller provides only a model + name (no `model_info.id`) and that name resolves via the router's + `model_name` fallback to a deployment owned by a different team, the + auth check must still run against the loaded deployment's `team_id`, + not the caller-supplied one in the request body. + """ + from fastapi import HTTPException + + from litellm.proxy._types import LiteLLM_TeamTable + from litellm.proxy.management_endpoints.model_management_endpoints import ( + ModelManagementAuthChecks, + ) + + mock_request = MagicMock() + + requester_team_id = "team-b-2" + deployment_owner_team_id = "team-a-2" + + requester_user_api_key_dict = UserAPIKeyAuth( + token="requester-token-2", + user_id="team-b-admin-user-2", + team_id=requester_team_id, + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + mock_prisma_client = MagicMock() + + other_team_deployment_dict = { + "model_name": "shared-model-name", + "litellm_params": { + "model": "openai/gpt-4o", + "api_base": "https://team-a-api-2.invalid/v1", + "api_key": "TEAM-A-API-KEY-2", + }, + "model_info": { + "id": "team-a-deployment-id-2", + "team_id": deployment_owner_team_id, + }, + } + + mock_router = MagicMock() + mock_router.get_model_list.return_value = [other_team_deployment_dict] + + async def fake_find_unique(*, where): + return SimpleNamespace( + model_dump=lambda: LiteLLM_TeamTable( + team_id=where["team_id"], + members_with_roles=( + [{"user_id": "team-b-admin-user-2", "role": "admin"}] + if where["team_id"] == requester_team_id + else [] + ), + ).model_dump() + ) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), + patch("litellm.proxy.proxy_server.llm_router", mock_router), + patch("litellm.proxy.proxy_server.premium_user", True), + patch.object( + ModelManagementAuthChecks, + "can_user_make_model_call", + wraps=ModelManagementAuthChecks.can_user_make_model_call, + ) as spy_auth_check, + patch( + "litellm.proxy.management_endpoints.model_management_endpoints.TeamRepository" + ) as MockTeamRepo, + ): + mock_team_repo_instance = MagicMock() + mock_team_repo_instance.table.find_unique = AsyncMock( + side_effect=fake_find_unique + ) + MockTeamRepo.return_value = mock_team_repo_instance + + with pytest.raises(HTTPException) as exc_info: + await health_test_model_connection( + request=mock_request, + mode="chat", + litellm_params={ + "model": "shared-model-name", + "api_base": "https://swapped-base-2.invalid/v1", + }, + model_info={"team_id": requester_team_id}, + user_api_key_dict=requester_user_api_key_dict, + ) + + assert exc_info.value.status_code == 403 + + passed_model_params = spy_auth_check.call_args.kwargs["model_params"] + assert passed_model_params.model_info.team_id == deployment_owner_team_id + + +@pytest.mark.asyncio +async def test_test_model_connection_authorized_team_admin_passes_real_auth(): + """ + Positive-path companion to the deny tests above. When the caller is a + genuine admin of the team that owns the loaded deployment, the real + (unmocked) auth check must pass and the endpoint must reach the outbound + health probe. Guards against a regression that swaps the auth `team_id` + for something deny-all on the legit path. + """ + from litellm.proxy._types import LiteLLM_TeamTable + from litellm.proxy.management_endpoints.model_management_endpoints import ( + ModelManagementAuthChecks, + ) + from litellm.types.router import Deployment, LiteLLM_Params, ModelInfo + + mock_request = MagicMock() + + owner_team_id = "team-owner" + owner_admin_user_id = "team-owner-admin" + owned_deployment_id = "owned-deployment-id" + + owner_admin_api_key_dict = UserAPIKeyAuth( + token="owner-admin-token", + user_id=owner_admin_user_id, + team_id=owner_team_id, + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + mock_prisma_client = MagicMock() + + owned_deployment = Deployment( + model_name="owner-model", + litellm_params=LiteLLM_Params( + model="openai/gpt-4o-mini", + api_base="https://owner-real-api.invalid/v1", + api_key="owner-team-api-key", + ), + model_info=ModelInfo(id=owned_deployment_id, team_id=owner_team_id), + ) + + mock_router = MagicMock() + mock_router.get_deployment.return_value = owned_deployment + + async def fake_find_unique(*, where): + if where["team_id"] == owner_team_id: + return SimpleNamespace( + model_dump=lambda: LiteLLM_TeamTable( + team_id=owner_team_id, + members_with_roles=[ + {"user_id": owner_admin_user_id, "role": "admin"} + ], + ).model_dump() + ) + return None + + health_result = {"status": "healthy", "response_time_ms": 50} + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), + patch("litellm.proxy.proxy_server.llm_router", mock_router), + patch("litellm.proxy.proxy_server.premium_user", True), + patch.object( + ModelManagementAuthChecks, + "can_user_make_model_call", + wraps=ModelManagementAuthChecks.can_user_make_model_call, + ) as spy_auth_check, + patch( + "litellm.proxy.management_endpoints.model_management_endpoints.TeamRepository" + ) as MockTeamRepo, + patch( + "litellm.proxy.health_endpoints._health_endpoints.litellm.ahealth_check", + AsyncMock(return_value=health_result), + ), + patch( + "litellm.proxy.health_endpoints._health_endpoints.run_with_timeout", + AsyncMock(return_value=health_result), + ), + ): + mock_team_repo_instance = MagicMock() + mock_team_repo_instance.table.find_unique = AsyncMock( + side_effect=fake_find_unique + ) + MockTeamRepo.return_value = mock_team_repo_instance + + result = await health_test_model_connection( + request=mock_request, + mode="chat", + litellm_params={"model": "openai/gpt-4o-mini"}, + model_info={"id": owned_deployment_id, "team_id": owner_team_id}, + user_api_key_dict=owner_admin_api_key_dict, + ) + + assert result["status"] == "success" + passed_model_params = spy_auth_check.call_args.kwargs["model_params"] + assert passed_model_params.model_info.team_id == owner_team_id + + @pytest.mark.asyncio @pytest.mark.parametrize( "status,error_message", diff --git a/tests/test_litellm/proxy/hooks/test_batch_file_validation.py b/tests/test_litellm/proxy/hooks/test_batch_file_validation.py index 1d4d39ec140..7ff1bc11d81 100644 --- a/tests/test_litellm/proxy/hooks/test_batch_file_validation.py +++ b/tests/test_litellm/proxy/hooks/test_batch_file_validation.py @@ -463,6 +463,42 @@ async def test_pre_call_fails_closed_when_current_team_fetch_fails_for_all_team_ mock_can_key_call_model.assert_not_awaited() +@pytest.mark.asyncio +async def test_pre_call_allows_teamless_all_team_models_key(): + """A teamless key with all-team-models must be allowed to submit batch jobs + for any model (same as leaving models empty = unrestricted). Fails if + someone re-introduces a teamless denial in _resolve_key_models_for_auth_check + or adds a team_id guard that blocks the batch path.""" + from litellm.proxy._types import SpecialModelNames + from litellm.proxy.hooks.batch_rate_limiter import _PROXY_BatchRateLimiter + + rate_limiter = _PROXY_BatchRateLimiter( + internal_usage_cache=MagicMock(), + parallel_request_limiter=MagicMock(), + ) + file_dict = [ + { + "body": { + "model": "gpt-4o", + "messages": [{"role": "user", "content": "x"}], + } + } + ] + user = UserAPIKeyAuth( + api_key="sk-orphan", + user_id="alice", + models=[SpecialModelNames.all_team_models.value], + team_models=[], + user_role=LitellmUserRoles.INTERNAL_USER.value, + ) + + with patch("litellm.proxy.proxy_server.llm_router", None): + await rate_limiter._enforce_batch_file_model_access( + user_api_key_dict=user, + models=_models(file_dict), + ) + + @pytest.mark.asyncio async def test_pre_call_allows_authorized_model_in_batch_file(): """If every model in the JSONL is on the caller's allowlist, the hook diff --git a/tests/test_litellm/proxy/hooks/test_dynamic_rate_limiter.py b/tests/test_litellm/proxy/hooks/test_dynamic_rate_limiter.py new file mode 100644 index 00000000000..b630ff1605c --- /dev/null +++ b/tests/test_litellm/proxy/hooks/test_dynamic_rate_limiter.py @@ -0,0 +1,44 @@ +from datetime import datetime, timezone + +import pytest + +from litellm.caching.caching import DualCache +from litellm.proxy.hooks.dynamic_rate_limiter import ( + DynamicRateLimiterCache, + _PROXY_DynamicRateLimitHandler, +) + + +@pytest.mark.asyncio +async def test_sadd_and_get_share_injected_clock_window(): + dual_cache = DualCache() + cache = DynamicRateLimiterCache( + cache=dual_cache, + time_fn=lambda: datetime(2024, 1, 1, 10, 30, 0, tzinfo=timezone.utc), + ) + await cache.async_set_cache_sadd(model="my-fake-model", value=["p1", "p2", "p3"]) + assert await cache.async_get_cache(model="my-fake-model") == 3 + assert await dual_cache.async_get_cache(key="10-30:my-fake-model") is not None + + +@pytest.mark.asyncio +async def test_minute_rollover_between_sadd_and_get_reads_empty_window(): + ticks = iter( + ( + datetime(2024, 1, 1, 10, 30, 59, 999999, tzinfo=timezone.utc), + datetime(2024, 1, 1, 10, 31, 0, 0, tzinfo=timezone.utc), + ) + ) + cache = DynamicRateLimiterCache(cache=DualCache(), time_fn=lambda: next(ticks)) + await cache.async_set_cache_sadd(model="my-fake-model", value=["p1"]) + assert await cache.async_get_cache(model="my-fake-model") is None + + +@pytest.mark.asyncio +async def test_handler_threads_time_fn_to_internal_cache(): + handler = _PROXY_DynamicRateLimitHandler( + internal_usage_cache=DualCache(), + time_fn=lambda: datetime(2024, 1, 1, 10, 30, 0, tzinfo=timezone.utc), + ) + await handler.internal_usage_cache.async_set_cache_sadd(model="my-fake-model", value=["p1", "p2"]) + assert await handler.internal_usage_cache.async_get_cache(model="my-fake-model") == 2 diff --git a/tests/test_litellm/proxy/hooks/test_parallel_request_limiter_v3.py b/tests/test_litellm/proxy/hooks/test_parallel_request_limiter_v3.py index 50f471721b1..e7d2909263a 100644 --- a/tests/test_litellm/proxy/hooks/test_parallel_request_limiter_v3.py +++ b/tests/test_litellm/proxy/hooks/test_parallel_request_limiter_v3.py @@ -48,6 +48,42 @@ def time_controller(monkeypatch): return controller +@pytest.mark.parametrize( + "throttle_pct, expected_rpm, expected_tpm", + [ + (None, 100, 1000), # no throttle -> configured limits + (0.1, 10, 100), # 10% of configured + (0.5, 50, 500), + ], +) +def test_api_key_descriptor_applies_budget_throttle( + throttle_pct, expected_rpm, expected_tpm +): + """The api_key rate-limit descriptor scales the key's configured TPM/RPM by + the request-scoped budget_throttle_pct, leaving the configured limits intact.""" + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(DualCache()) + ) + user_api_key_dict = UserAPIKeyAuth( + api_key=hash_token("sk-throttle"), + rpm_limit=100, + tpm_limit=1000, + budget_throttle_pct=throttle_pct, + ) + + descriptors = handler._create_rate_limit_descriptors( + user_api_key_dict=user_api_key_dict, + data={}, + rpm_limit_type=None, + tpm_limit_type=None, + model_has_failures=False, + ) + + api_key_descriptor = next(d for d in descriptors if d["key"] == "api_key") + assert api_key_descriptor["rate_limit"]["requests_per_unit"] == expected_rpm + assert api_key_descriptor["rate_limit"]["tokens_per_unit"] == expected_tpm + + @pytest.mark.flaky(reruns=3) @pytest.mark.asyncio async def test_sliding_window_rate_limit_v3(monkeypatch, time_controller): @@ -3537,3 +3573,474 @@ async def test_pre_call_hook_skips_reservation_when_disabled(monkeypatch): ) assert TPM_RESERVED_TOKENS_KEY not in (data.get("metadata") or {}) + + +@pytest.mark.asyncio +async def test_per_tag_rate_limit_independent_counters_v3(monkeypatch): + """ + A single key with per-tag RPM limits tracks each tag independently: a tag + at its limit returns 429 while a different (unlimited) tag keeps flowing, + governed only by the generous key-level limit. + """ + monkeypatch.setenv("LITELLM_RATE_LIMIT_WINDOW_SIZE", "60") + _api_key = hash_token("sk-per-tag-rpm") + user_api_key_dict = UserAPIKeyAuth( + api_key=_api_key, + rpm_limit=100, + metadata={"tag_rpm_limit": {"cell-1": 2}}, + ) + local_cache = DualCache() + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(local_cache) + ) + + async def call(tag: str) -> None: + await handler.async_pre_call_hook( + user_api_key_dict=user_api_key_dict, + cache=local_cache, + data={"model": "gpt-3.5-turbo", "metadata": {"tags": [tag]}}, + call_type="", + ) + + await call("cell-1") + await call("cell-1") + with pytest.raises(HTTPException) as exc_info: + await call("cell-1") + assert exc_info.value.status_code == 429 + assert "tag_per_key" in str(exc_info.value.detail) + + # cell-2 has no configured tag limit, so cell-1's exhausted counter must + # not block it; only the generous key-level limit applies. + for _ in range(5): + await call("cell-2") + + +@pytest.mark.asyncio +async def test_per_tag_descriptor_creation_v3(): + """ + _create_rate_limit_descriptors emits a tag_per_key descriptor carrying the + configured RPM limit only for request tags present in the configured map. + """ + _api_key = hash_token("sk-per-tag-desc") + user_api_key_dict = UserAPIKeyAuth( + api_key=_api_key, + metadata={"tag_rpm_limit": {"cell-1": 5}}, + ) + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(DualCache()) + ) + + descriptors = handler._create_rate_limit_descriptors( + user_api_key_dict=user_api_key_dict, + data={"model": "gpt-3.5-turbo", "metadata": {"tags": ["cell-1", "cell-2"]}}, + rpm_limit_type=None, + tpm_limit_type=None, + model_has_failures=False, + ) + + tag_descriptors = [d for d in descriptors if d["key"] == "tag_per_key"] + assert len(tag_descriptors) == 1, "only the configured tag yields a descriptor" + descriptor = tag_descriptors[0] + assert descriptor["value"] == f"{_api_key}:cell-1" + assert descriptor["rate_limit"]["requests_per_unit"] == 5 + + +@pytest.mark.asyncio +async def test_per_tag_descriptor_absent_without_config_v3(): + """No tag_per_key descriptor is created when the key has no tag limits.""" + user_api_key_dict = UserAPIKeyAuth( + api_key=hash_token("sk-no-tag"), + rpm_limit=10, + ) + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(DualCache()) + ) + + descriptors = handler._create_rate_limit_descriptors( + user_api_key_dict=user_api_key_dict, + data={"model": "gpt-3.5-turbo", "metadata": {"tags": ["cell-1"]}}, + rpm_limit_type=None, + tpm_limit_type=None, + model_has_failures=False, + ) + + assert not [d for d in descriptors if d["key"] == "tag_per_key"] + + +@pytest.mark.asyncio +async def test_per_tag_untagged_request_governed_by_key_limit_v3(monkeypatch): + """ + Per-tag limits are opt-in sub-limits under the key-level ceiling, not a + standalone enforcement boundary: a request that carries no tag (or a tag + without a configured limit) is not rejected by any tag counter, but it is + still bounded by the key-level rpm_limit. This pins the documented + untagged-fallback behavior so a future "fail closed on missing tag" change + would fail here instead of silently breaking it. + """ + monkeypatch.setenv("LITELLM_RATE_LIMIT_WINDOW_SIZE", "60") + _api_key = hash_token("sk-untagged-fallback") + user_api_key_dict = UserAPIKeyAuth( + api_key=_api_key, + rpm_limit=3, + metadata={"tag_rpm_limit": {"cell-1": 2}}, + ) + local_cache = DualCache() + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(local_cache) + ) + + async def call(metadata: dict) -> None: + await handler.async_pre_call_hook( + user_api_key_dict=user_api_key_dict, + cache=local_cache, + data={"model": "gpt-3.5-turbo", "metadata": metadata}, + call_type="", + ) + + # Untagged and unconfigured-tag requests share the key-level budget of 3 + # and never hit a tag_per_key counter. + await call({}) + await call({"tags": ["cell-99"]}) + await call({}) + with pytest.raises(HTTPException) as exc_info: + await call({"tags": ["cell-99"]}) + assert exc_info.value.status_code == 429 + assert "tag_per_key" not in str(exc_info.value.detail) + + +# -------------------------------------------------------------------------- +# Streaming success logging mirrors x-ratelimit-* remaining values into +# standard_logging_object.hidden_params.additional_headers so Prometheus / +# logging callbacks see them for streams too (non-streaming already gets +# them via async_post_call_success_hook, which the streaming path skips). +# -------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_streaming_end_to_end_populates_slp_ratelimit_headers(monkeypatch): + """ + End-to-end regression: on a streaming request, the same pre-call + + success-callback pair the proxy uses must land ``x-ratelimit-*`` + remaining/limit values in + ``kwargs["standard_logging_object"]["hidden_params"]["additional_headers"]``. + """ + monkeypatch.setenv("LITELLM_RATE_LIMIT_WINDOW_SIZE", "60") + _api_key = hash_token("sk-stream-e2e") + user_api_key_dict = UserAPIKeyAuth( + api_key=_api_key, + rpm_limit=100, + tpm_limit=10000, + ) + local_cache = DualCache() + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(local_cache) + ) + + # Real pre-call: populates data and stashes the response into metadata + # so the success callback can find it via litellm_params.metadata. + data: Dict[str, Any] = { + "model": "gpt-4o-mini", + "metadata": {}, + "stream": True, + } + await handler.async_pre_call_hook( + user_api_key_dict=user_api_key_dict, + cache=local_cache, + data=data, + call_type="", + ) + + # Simulate the wrapper handing the pre-call metadata dict to the + # completion() call: it becomes kwargs["litellm_params"]["metadata"] by + # the time the success callback fires. + mock_response = ModelResponse( + id="mock-stream-e2e", + object="chat.completion", + created=int(datetime.now().timestamp()), + model="gpt-4o-mini", + usage=Usage(prompt_tokens=100, completion_tokens=50, total_tokens=150), + choices=[], + ) + mock_kwargs: Dict[str, Any] = { + "standard_logging_object": { + "metadata": { + "user_api_key_hash": _api_key, + "user_api_key_user_id": None, + "user_api_key_team_id": None, + "user_api_key_end_user_id": None, + } + }, + "litellm_params": {"metadata": data["metadata"]}, + "model": "gpt-4o-mini", + } + + async def _noop_increment(increment_list, **_): + return True + + monkeypatch.setattr( + handler.internal_usage_cache.dual_cache, + "async_increment_cache_pipeline", + _noop_increment, + ) + + # async_logging_hook runs before async_log_success_event, so any + # downstream callback that reads the SLP sees the mirrored values. + await handler.async_logging_hook( + kwargs=mock_kwargs, + result=mock_response, + call_type="acompletion", + ) + + additional_headers = ( + mock_kwargs["standard_logging_object"] + .get("hidden_params", {}) + .get("additional_headers", {}) + ) + + # api_key-scoped remaining/limit values are the baseline every request + # emits and must always reach the SLP. + remaining_keys = [ + k for k in additional_headers if "-remaining-" in k + ] + assert ( + remaining_keys + ), f"streaming success must populate remaining values, got {additional_headers!r}" + limit_keys = [k for k in additional_headers if "-limit-" in k] + assert limit_keys, "streaming success must also populate limit values" + assert ( + additional_headers.get("x-ratelimit-api_key-remaining-requests") == 99 + ), ( + "api_key remaining requests should reflect the just-consumed slot;" + f" got {additional_headers!r}" + ) + + +@pytest.mark.asyncio +async def test_streaming_populates_model_per_key_ratelimit_headers(monkeypatch): + """ + Streaming must land the per-(key, model) remaining/limit values in the + SLP under ``x-ratelimit-model_per_key-{remaining|limit}-{requests,tokens}``. + """ + monkeypatch.setenv("LITELLM_RATE_LIMIT_WINDOW_SIZE", "60") + _api_key = hash_token("sk-stream-mirror") + user_api_key_dict = UserAPIKeyAuth( + api_key=_api_key, + metadata={ + "model_rpm_limit": {"gpt-4o-mini": 100}, + "model_tpm_limit": {"gpt-4o-mini": 10000}, + }, + ) + local_cache = DualCache() + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(local_cache) + ) + + async def _noop_increment(increment_list, **_): + return True + + monkeypatch.setattr( + handler.internal_usage_cache.dual_cache, + "async_increment_cache_pipeline", + _noop_increment, + ) + + data: Dict[str, Any] = {"model": "gpt-4o-mini", "metadata": {}, "stream": True} + await handler.async_pre_call_hook( + user_api_key_dict=user_api_key_dict, + cache=local_cache, + data=data, + call_type="", + ) + + mock_response = ModelResponse( + id="mock-stream", + object="chat.completion", + created=int(datetime.now().timestamp()), + model="gpt-4o-mini", + usage=Usage(prompt_tokens=100, completion_tokens=50, total_tokens=150), + choices=[], + ) + + mock_kwargs: Dict[str, Any] = { + "standard_logging_object": { + "metadata": { + "user_api_key_hash": _api_key, + "user_api_key_user_id": None, + "user_api_key_team_id": None, + "user_api_key_end_user_id": None, + } + }, + "litellm_params": {"metadata": data["metadata"]}, + "model": "gpt-4o-mini", + } + + await handler.async_logging_hook( + kwargs=mock_kwargs, + result=mock_response, + call_type="acompletion", + ) + + hidden_params = mock_kwargs["standard_logging_object"].get("hidden_params") or {} + additional_headers = hidden_params.get("additional_headers") or {} + + assert ( + additional_headers.get("x-ratelimit-model_per_key-remaining-requests") == 99 + ), f"got {additional_headers!r}" + assert additional_headers.get("x-ratelimit-model_per_key-limit-requests") == 100 + + # response._hidden_params is also updated for late readers. + response_hidden = getattr(mock_response, "_hidden_params", None) or {} + response_headers = response_hidden.get("additional_headers") or {} + assert response_headers.get("x-ratelimit-model_per_key-remaining-requests") == 99 + + +@pytest.mark.asyncio +async def test_async_log_success_event_no_mirror_when_no_snapshot(monkeypatch): + """ + No pre-call snapshot (no descriptors matched) -> no fabricated + ``x-ratelimit-*`` headers. + """ + monkeypatch.setenv("LITELLM_RATE_LIMIT_WINDOW_SIZE", "60") + _api_key = hash_token("sk-stream-no-mirror") + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(DualCache()) + ) + + async def _noop_increment(increment_list, **_): + return True + + monkeypatch.setattr( + handler.internal_usage_cache.dual_cache, + "async_increment_cache_pipeline", + _noop_increment, + ) + + mock_response = ModelResponse( + id="mock-stream-none", + object="chat.completion", + created=int(datetime.now().timestamp()), + model="gpt-4o-mini", + usage=Usage(prompt_tokens=1, completion_tokens=1, total_tokens=2), + choices=[], + ) + + mock_kwargs: Dict[str, Any] = { + "standard_logging_object": { + "metadata": { + "user_api_key_hash": _api_key, + "user_api_key_user_id": None, + "user_api_key_team_id": None, + "user_api_key_end_user_id": None, + } + }, + "litellm_params": {"metadata": {}}, + "model": "gpt-4o-mini", + } + + await handler.async_logging_hook( + kwargs=mock_kwargs, + result=mock_response, + call_type="acompletion", + ) + + hidden_params = mock_kwargs["standard_logging_object"].get("hidden_params") or {} + additional_headers = hidden_params.get("additional_headers") or {} + ratelimit_keys = [k for k in additional_headers if k.startswith("x-ratelimit-")] + assert ( + not ratelimit_keys + ), f"no snapshot must produce no rate-limit headers, got {ratelimit_keys}" + + +@pytest.mark.asyncio +async def test_streaming_mirror_matches_non_streaming_header_shape(monkeypatch): + """ + Given the same pre-call state, streaming and non-streaming must write + the identical ``x-ratelimit-*`` key/value shape to their respective + ``additional_headers`` slots. + """ + monkeypatch.setenv("LITELLM_RATE_LIMIT_WINDOW_SIZE", "60") + _api_key = hash_token("sk-shape") + user_api_key_dict = UserAPIKeyAuth( + api_key=_api_key, + metadata={ + "model_rpm_limit": {"gpt-4o-mini": 50}, + "model_tpm_limit": {"gpt-4o-mini": 5000}, + }, + ) + local_cache = DualCache() + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(local_cache) + ) + + async def _noop_increment(increment_list, **_): + return True + + monkeypatch.setattr( + handler.internal_usage_cache.dual_cache, + "async_increment_cache_pipeline", + _noop_increment, + ) + + # Drive pre-call once so both paths have the same authoritative snapshot. + data: Dict[str, Any] = {"model": "gpt-4o-mini", "metadata": {}} + await handler.async_pre_call_hook( + user_api_key_dict=user_api_key_dict, + cache=local_cache, + data=data, + call_type="", + ) + + # Non-streaming path: async_post_call_success_hook mutates response._hidden_params. + non_stream_response = ModelResponse( + id="mock-non-stream", + object="chat.completion", + created=int(datetime.now().timestamp()), + model="gpt-4o-mini", + usage=Usage(prompt_tokens=1, completion_tokens=1, total_tokens=2), + choices=[], + ) + non_stream_response._hidden_params = {} + await handler.async_post_call_success_hook( + data=data, + user_api_key_dict=user_api_key_dict, + response=non_stream_response, + ) + non_stream_headers = non_stream_response._hidden_params.get( + "additional_headers", {} + ) + + # Streaming path: async_logging_hook mirrors into standard_logging_object. + stream_kwargs: Dict[str, Any] = { + "standard_logging_object": { + "metadata": {"user_api_key_hash": _api_key} + }, + "litellm_params": {"metadata": data["metadata"]}, + "model": "gpt-4o-mini", + } + stream_response = ModelResponse( + id="mock-stream", + object="chat.completion", + created=int(datetime.now().timestamp()), + model="gpt-4o-mini", + usage=Usage(prompt_tokens=1, completion_tokens=1, total_tokens=2), + choices=[], + ) + await handler.async_logging_hook( + kwargs=stream_kwargs, + result=stream_response, + call_type="acompletion", + ) + stream_slp_headers = ( + stream_kwargs["standard_logging_object"] + .get("hidden_params", {}) + .get("additional_headers", {}) + ) + + def _rl_only(headers: Dict[str, Any]) -> Dict[str, Any]: + return {k: v for k, v in headers.items() if k.startswith("x-ratelimit-")} + + assert _rl_only(stream_slp_headers) == _rl_only(non_stream_headers), ( + f"streaming={_rl_only(stream_slp_headers)}" + f" non_streaming={_rl_only(non_stream_headers)}" + ) + assert "x-ratelimit-model_per_key-remaining-requests" in stream_slp_headers diff --git a/tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py b/tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py index 0cbf308076c..813a0c5e38f 100644 --- a/tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py +++ b/tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py @@ -1104,3 +1104,76 @@ async def test_async_post_call_failure_hook_records_recovered_partial_spend(): mock_update_database.assert_called_once() assert mock_update_database.call_args[1]["response_cost"] == 3.5e-05 + + +@pytest.mark.asyncio +async def test_track_cost_callback_enriches_user_id_for_mcp_style_metadata(): + """MCP tool calls may only carry user_api_key; user/team rollups still need user_id.""" + from litellm.proxy._types import UserAPIKeyAuth + + logger = _ProxyDBLogger() + key_obj = UserAPIKeyAuth( + api_key="hashed-key", + user_id="mcp-user@example.com", + team_id="team-123", + org_id="org-456", + key_alias="mcp-key", + ) + + kwargs = { + "call_type": "call_mcp_tool", + "model": "MCP: echo", + "litellm_params": { + "metadata": { + "user_api_key": "hashed-key", + } + }, + "standard_logging_object": { + "response_cost": 10.0, + "request_tags": [], + "metadata": {}, + }, + } + + with ( + patch( + "litellm.proxy.hooks.proxy_track_cost_callback.get_key_object", + new_callable=AsyncMock, + return_value=key_obj, + ), + patch( + "litellm.proxy.proxy_server.increment_spend_counters", + new_callable=AsyncMock, + ) as mock_increment, + patch( + "litellm.proxy.proxy_server.update_cache", + new_callable=AsyncMock, + ), + patch( + "litellm.proxy.proxy_server.proxy_logging_obj", + ) as mock_proxy_logging, + ): + mock_proxy_logging.db_spend_update_writer.update_database = AsyncMock() + mock_proxy_logging.slack_alerting_instance.customer_spend_alert = AsyncMock() + + await logger._PROXY_track_cost_callback( + kwargs=kwargs, + completion_response={"id": "mcp-call-1"}, + start_time=datetime.now(), + end_time=datetime.now(), + ) + + mock_increment.assert_awaited_once() + assert mock_increment.call_args.kwargs["user_id"] == "mcp-user@example.com" + assert mock_increment.call_args.kwargs["team_id"] == "team-123" + assert mock_increment.call_args.kwargs["org_id"] == "org-456" + + update_kwargs = ( + mock_proxy_logging.db_spend_update_writer.update_database.await_args.kwargs + ) + assert update_kwargs["user_id"] == "mcp-user@example.com" + assert update_kwargs["team_id"] == "team-123" + assert ( + kwargs["litellm_params"]["metadata"]["user_api_key_user_id"] + == "mcp-user@example.com" + ) diff --git a/tests/test_litellm/proxy/management_endpoints/scim/test_scim_v2_endpoints.py b/tests/test_litellm/proxy/management_endpoints/scim/test_scim_v2_endpoints.py index 7f5aee51f51..f39ff93cee7 100644 --- a/tests/test_litellm/proxy/management_endpoints/scim/test_scim_v2_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/scim/test_scim_v2_endpoints.py @@ -258,6 +258,7 @@ async def test_scim_create_user_respects_default_role_set_via_ui(mocker, monkeyp ) import litellm + from litellm.proxy._types import UserAPIKeyAuth settings = DefaultInternalUserParams( user_role=LitellmUserRoles.INTERNAL_USER, @@ -266,6 +267,7 @@ async def test_scim_create_user_respects_default_role_set_via_ui(mocker, monkeyp settings=settings, settings_key="default_internal_user_params", success_message="ok", + user_api_key_dict=UserAPIKeyAuth(user_id="test-admin"), ) # Verify the in-memory variable was actually updated diff --git a/tests/test_litellm/proxy/management_endpoints/test_cache_settings_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_cache_settings_endpoints.py index 4bdef2e8f96..f4c6d4f8d15 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_cache_settings_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_cache_settings_endpoints.py @@ -10,20 +10,24 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -sys.path.insert( - 0, os.path.abspath("../../../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../../..")) # Adds the parent directory to the system path import litellm from litellm.proxy._types import LitellmTableNames, LitellmUserRoles from litellm.proxy.auth.user_api_key_auth import UserAPIKeyAuth from litellm.proxy.management_endpoints.cache_settings_endpoints import ( + _CACHE_SENSITIVE_FIELDS, CacheSettingsManager, CacheSettingsUpdateRequest, CacheTestRequest, + _resolve_cache_url_precedence, + get_cache_settings, test_cache_connection, update_cache_settings, ) +from litellm.types.management_endpoints.cache_settings_endpoints import ( + CACHE_SETTINGS_FIELDS, +) @pytest.mark.asyncio @@ -41,9 +45,7 @@ async def test_test_cache_connection_calls_cache_test_connection_with_params(): } request = CacheTestRequest(cache_settings=cache_settings) - user_api_key_dict = UserAPIKeyAuth( - user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-test", user_id="test-user" - ) + user_api_key_dict = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-test", user_id="test-user") # Mock Cache class and its test_connection method mock_cache_instance = MagicMock() @@ -60,9 +62,7 @@ async def test_test_cache_connection_calls_cache_test_connection_with_params(): mock_cache_class.return_value = mock_cache_instance # Call the endpoint - result = await test_cache_connection( - request=request, user_api_key_dict=user_api_key_dict - ) + result = await test_cache_connection(request=request, user_api_key_dict=user_api_key_dict) # Verify Cache was instantiated with correct params mock_cache_class.assert_called_once_with(**cache_settings) @@ -76,6 +76,205 @@ async def test_test_cache_connection_calls_cache_test_connection_with_params(): assert result.error is None +def test_cache_settings_fields_expose_url_and_db(): + """The dynamic UI form is driven by CACHE_SETTINGS_FIELDS; url + db must be + present (with the right types) so the Redis URL and logical database index + are configurable from the Admin UI.""" + by_name = {f.field_name: f for f in CACHE_SETTINGS_FIELDS} + + assert "url" in by_name + assert "db" in by_name + # db is a logical database index → integer + assert by_name["db"].field_type == "Integer" + # Both are common connection fields, shown for every Redis type + assert by_name["url"].redis_type is None + assert by_name["db"].redis_type is None + + +class TestResolveCacheUrlPrecedence: + """url wins over the discrete host/port/db/password fields.""" + + def test_url_overrides_discrete_connection_fields(self): + settings = { + "type": "redis", + "url": "redis://user:pw@host:6379/1", + "host": "host", + "port": "6379", + "db": 1, + "username": "user", + "password": "pw", + "namespace": "ns", + "ttl": 60, + } + + result = _resolve_cache_url_precedence(settings) + + assert result["url"] == "redis://user:pw@host:6379/1" + assert "host" not in result + assert "port" not in result + assert "db" not in result + # username and password are both encodable in the url, so the discrete + # copies must not ride along and override it + assert "username" not in result + assert "password" not in result + # Non-connection fields survive + assert result["type"] == "redis" + assert result["namespace"] == "ns" + assert result["ttl"] == 60 + + def test_no_url_returns_copy_unchanged(self): + settings = {"type": "redis", "host": "host", "port": "6379", "db": 1} + + result = _resolve_cache_url_precedence(settings) + + assert result == settings + assert result is not settings + + def test_blank_url_does_not_strip_discrete_fields(self): + settings = {"type": "redis", "url": " ", "host": "host", "db": 2} + + result = _resolve_cache_url_precedence(settings) + + assert result["host"] == "host" + assert result["db"] == 2 + + def test_cluster_mode_keeps_discrete_fields(self): + settings = { + "type": "redis", + "url": "redis://host:6379", + "redis_startup_nodes": [{"host": "127.0.0.1", "port": "7001"}], + "host": "host", + "password": "pw", + } + + result = _resolve_cache_url_precedence(settings) + + assert result["host"] == "host" + assert result["password"] == "pw" + + +@pytest.mark.asyncio +async def test_test_cache_connection_url_takes_precedence_over_discrete_fields(): + """When url + discrete fields are both sent, the tested Cache instance is + built from the url alone (host/port/db/password dropped).""" + cache_settings = { + "type": "redis", + "url": "redis://:pw@host:6379/1", + "host": "ignored-host", + "port": "6379", + "db": 1, + "password": "pw", + } + + request = CacheTestRequest(cache_settings=cache_settings) + user_api_key_dict = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-test", user_id="test-user") + + mock_cache_instance = MagicMock() + mock_cache_instance.cache = MagicMock() + mock_cache_instance.cache.test_connection = AsyncMock(return_value={"status": "success", "message": "ok"}) + + with patch("litellm.Cache") as mock_cache_class: + mock_cache_class.return_value = mock_cache_instance + + result = await test_cache_connection(request=request, user_api_key_dict=user_api_key_dict) + + called_kwargs = mock_cache_class.call_args.kwargs + assert called_kwargs["url"] == "redis://:pw@host:6379/1" + assert "host" not in called_kwargs + assert "port" not in called_kwargs + assert "db" not in called_kwargs + assert "password" not in called_kwargs + assert result.status == "success" + + +@pytest.mark.asyncio +async def test_update_cache_settings_persists_url_precedence(monkeypatch): + """The persisted (source-of-truth) row and the reinitialized cache both use + the url-resolved settings, so a stored config never carries a contradictory + host+url pair.""" + monkeypatch.setattr(litellm, "store_audit_logs", False) + + mock_prisma = MagicMock() + mock_prisma.db.litellm_cacheconfig.find_unique = AsyncMock(return_value=None) + mock_prisma.db.litellm_cacheconfig.upsert = AsyncMock() + + proxy_config = MagicMock() + proxy_config._encrypt_env_variables = MagicMock( + side_effect=lambda environment_variables: dict(environment_variables) + ) + proxy_config._decrypt_db_variables = MagicMock(side_effect=lambda variables_dict: dict(variables_dict)) + proxy_config._init_cache = MagicMock() + proxy_config.switch_on_llm_response_caching = MagicMock() + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.proxy_config", proxy_config), + patch("litellm.proxy.proxy_server.store_model_in_db", True), + ): + await update_cache_settings( + request=CacheSettingsUpdateRequest( + cache_settings={ + "type": "redis", + "url": "redis://:pw@host:6379/1", + "host": "ignored-host", + "port": "6379", + "db": 1, + "password": "pw", + "namespace": "ns", + } + ), + user_api_key_dict=_admin_auth(), + litellm_changed_by=None, + ) + + persisted = proxy_config._encrypt_env_variables.call_args.kwargs["environment_variables"] + assert persisted["url"] == "redis://:pw@host:6379/1" + assert persisted["namespace"] == "ns" + assert "host" not in persisted + assert "port" not in persisted + assert "db" not in persisted + assert "password" not in persisted + + init_params = proxy_config._init_cache.call_args.kwargs["cache_params"] + assert "host" not in init_params + assert init_params["url"] == "redis://:pw@host:6379/1" + + +def test_url_is_a_masked_field(): + """A Redis URL can carry an inline password, so it must be masked on read + alongside the discrete password fields.""" + assert "url" in _CACHE_SENSITIVE_FIELDS + + +@pytest.mark.asyncio +async def test_get_cache_settings_masks_password_bearing_url(): + """GET /cache/settings must not leak an inline url password in plaintext, + while non-credential fields (e.g. namespace) come back untouched.""" + stored_url = "redis://:supersecretpassword@host:6379/1" + stored_settings = {"type": "redis", "url": stored_url, "namespace": "ns"} + + cache_row = MagicMock() + cache_row.cache_settings = json.dumps(stored_settings) + + mock_prisma = MagicMock() + mock_prisma.db.litellm_cacheconfig.find_unique = AsyncMock(return_value=cache_row) + + proxy_config = MagicMock() + proxy_config._decrypt_db_variables = MagicMock(side_effect=lambda variables_dict: dict(variables_dict)) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.proxy_config", proxy_config), + ): + response = await get_cache_settings(user_api_key_dict=_admin_auth()) + + returned_url = response.current_values["url"] + assert returned_url != stored_url + assert "supersecretpassword" not in returned_url + # non-credential field is not masked + assert response.current_values["namespace"] == "ns" + + class TestCacheSettingsManager: """Tests for CacheSettingsManager class""" @@ -182,12 +381,8 @@ class TestCacheSettingsManager: # Mock prisma client mock_prisma_client = MagicMock() mock_cache_config = MagicMock() - mock_cache_config.cache_settings = ( - '{"type": "redis", "host": "localhost", "port": "6379"}' - ) - mock_prisma_client.db.litellm_cacheconfig.find_unique = AsyncMock( - return_value=mock_cache_config - ) + mock_cache_config.cache_settings = '{"type": "redis", "host": "localhost", "port": "6379"}' + mock_prisma_client.db.litellm_cacheconfig.find_unique = AsyncMock(return_value=mock_cache_config) # Mock proxy_config mock_proxy_config = MagicMock() @@ -231,12 +426,8 @@ class TestCacheSettingsManager: # Mock prisma client mock_prisma_client = MagicMock() mock_cache_config = MagicMock() - mock_cache_config.cache_settings = ( - '{"type": "redis", "host": "localhost", "port": "6379"}' - ) - mock_prisma_client.db.litellm_cacheconfig.find_unique = AsyncMock( - return_value=mock_cache_config - ) + mock_cache_config.cache_settings = '{"type": "redis", "host": "localhost", "port": "6379"}' + mock_prisma_client.db.litellm_cacheconfig.find_unique = AsyncMock(return_value=mock_cache_config) # Mock proxy_config mock_proxy_config = MagicMock() @@ -274,9 +465,7 @@ class TestCacheSettingsManager: return None # No config → function returns early after retry. mock_prisma_client = MagicMock() - mock_prisma_client.db.litellm_cacheconfig.find_unique = AsyncMock( - side_effect=_flaky_find_unique - ) + mock_prisma_client.db.litellm_cacheconfig.find_unique = AsyncMock(side_effect=_flaky_find_unique) mock_prisma_client.attempt_db_reconnect = AsyncMock(return_value=True) mock_prisma_client._db_auth_reconnect_timeout_seconds = 2.0 mock_prisma_client._db_auth_reconnect_lock_timeout_seconds = 0.1 @@ -289,10 +478,7 @@ class TestCacheSettingsManager: assert len(invocations) == 2 mock_prisma_client.attempt_db_reconnect.assert_awaited_once() reconnect_kwargs = mock_prisma_client.attempt_db_reconnect.await_args.kwargs - assert ( - reconnect_kwargs["reason"] - == "init_cache_settings_in_db_lookup_failure" - ) + assert reconnect_kwargs["reason"] == "init_cache_settings_in_db_lookup_failure" # ── Audit-log emission for /cache/settings ──────────────────────────────────── @@ -320,9 +506,7 @@ async def test_update_cache_settings_emits_audit_log_when_enabled(monkeypatch): proxy_config._encrypt_env_variables = MagicMock( side_effect=lambda environment_variables: dict(environment_variables) ) - proxy_config._decrypt_db_variables = MagicMock( - side_effect=lambda variables_dict: dict(variables_dict) - ) + proxy_config._decrypt_db_variables = MagicMock(side_effect=lambda variables_dict: dict(variables_dict)) proxy_config._init_cache = MagicMock() proxy_config.switch_on_llm_response_caching = MagicMock() @@ -392,9 +576,7 @@ async def test_update_cache_settings_no_audit_when_disabled(monkeypatch): proxy_config._encrypt_env_variables = MagicMock( side_effect=lambda environment_variables: dict(environment_variables) ) - proxy_config._decrypt_db_variables = MagicMock( - side_effect=lambda variables_dict: dict(variables_dict) - ) + proxy_config._decrypt_db_variables = MagicMock(side_effect=lambda variables_dict: dict(variables_dict)) proxy_config._init_cache = MagicMock() proxy_config.switch_on_llm_response_caching = MagicMock() @@ -422,9 +604,7 @@ async def test_update_cache_settings_no_audit_when_disabled(monkeypatch): ), ): await update_cache_settings( - request=CacheSettingsUpdateRequest( - cache_settings={"type": "redis", "host": "redis.example.com"} - ), + request=CacheSettingsUpdateRequest(cache_settings={"type": "redis", "host": "redis.example.com"}), user_api_key_dict=_admin_auth(), litellm_changed_by=None, ) diff --git a/tests/test_litellm/proxy/management_endpoints/test_compliance_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_compliance_endpoints.py index 2c41b16ba7f..33e45ccb22c 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_compliance_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_compliance_endpoints.py @@ -7,9 +7,7 @@ import sys import pytest -sys.path.insert( - 0, os.path.abspath("../../../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../../..")) # Adds the parent directory to the system path from litellm.proxy.compliance_checks import ComplianceChecker from litellm.types.proxy.compliance_endpoints import ComplianceCheckRequest @@ -385,3 +383,214 @@ class TestGdprCompliant: ) checks = ComplianceChecker(data).check_gdpr() assert all(c.passed for c in checks) + + +class TestModeMatching: + """Direct coverage of ComplianceChecker._mode_matches for every shape. + + LitellmParams.mode is Union[str, List[str], Mode], so a spend-log + guardrail_mode can be None / str / list / tuple / dict. A prior + implementation compared `g_mode == mode`, which silently failed for the + non-str shapes and reported NON-COMPLIANT for every multi-mode guardrail. + A match now reports a mode satisfied only when every configured branch + runs in that mode: fails safe, no false-COMPLIANT. + """ + + @pytest.mark.parametrize( + "g_mode, mode, expected", + [ + (None, "pre_call", True), + (None, "post_call", False), + (None, "during_call", False), + ("pre_call", "pre_call", True), + ("post_call", "pre_call", False), + ("during_call", "during_call", True), + # list/tuple: only guaranteed when every listed mode equals `mode` + (["pre_call"], "pre_call", True), + (["pre_call", "pre_call"], "pre_call", True), + (["pre_call", "post_call"], "pre_call", False), + (["pre_call", "post_call"], "post_call", False), + ([], "pre_call", False), + (("during_call",), "during_call", True), + (("pre_call", "post_call"), "pre_call", False), + # dict: default only + ({"default": "pre_call"}, "pre_call", True), + ({"default": "pre_call"}, "post_call", False), + ({"default": ["pre_call", "post_call"]}, "post_call", False), + ({"default": ["pre_call"]}, "pre_call", True), + # dict with tags: every branch must run in mode + ({"default": "pre_call", "tags": {"a": "pre_call"}}, "pre_call", True), + ({"default": "pre_call", "tags": {"a": ["pre_call"]}}, "pre_call", True), + ({"default": "pre_call", "tags": {"a": ["pre_call", "post_call"]}}, "pre_call", False), + ({"default": "pre_call", "tags": {"eu": "post_call"}}, "pre_call", False), + ({"default": "pre_call", "tags": {"eu": "post_call"}}, "post_call", False), + ({"default": "pre_call", "tags": {"eu": ["during_call"]}}, "during_call", False), + ({"default": ["pre_call", "post_call"], "tags": {"a": "pre_call"}}, "post_call", False), + # Missing default: untagged routing is unknown, nothing guaranteed + ({"tags": {"x": "post_call"}}, "pre_call", False), + ({"tags": {"x": "post_call"}}, "post_call", False), + ({}, "pre_call", False), + ({}, "post_call", False), + ({"default": 123}, "pre_call", False), + # Unknown top-level shapes never match + (5, "pre_call", False), + (object(), "pre_call", False), + ], + ) + def test_mode_matches(self, g_mode, mode, expected): + assert ComplianceChecker._mode_matches(g_mode, mode) is expected + + def test_list_mode_guardrail_not_misclassified(self): + """A guardrail configured with mode ["pre_call", "post_call"] is logged + with the raw list when the writer cannot infer the concrete hook that + ran (e.g. apply_guardrail invocations). The spend log records "this + guardrail could have run at either hook", not "which hook fired this + request". Counting it for both would let a request that only fired + post_call pass a pre_call compliance check. It counts for neither.""" + data = ComplianceCheckRequest( + request_id="req-mode-1", + user_id="user-1", + model="gpt-4", + timestamp="2026-02-17T00:00:00Z", + guardrail_information=[ + { + "guardrail_name": "pii_masking", + "guardrail_mode": ["pre_call", "post_call"], + "guardrail_status": "success", + } + ], + ) + checker = ComplianceChecker(data) + assert len(checker._get_guardrails_by_mode("pre_call")) == 0 + assert len(checker._get_guardrails_by_mode("post_call")) == 0 + results = {c.check_name: c.passed for c in checker.check_eu_ai_act()} + assert results["Content screened before LLM"] is False + + def test_list_mode_single_value_counts(self): + """A single-entry list ["pre_call"] runs pre_call unconditionally, so it + counts for pre_call and no other mode.""" + data = ComplianceCheckRequest( + request_id="req-mode-1b", + user_id="user-1", + model="gpt-4", + timestamp="2026-02-17T00:00:00Z", + guardrail_information=[ + { + "guardrail_name": "pii_masking", + "guardrail_mode": ["pre_call"], + "guardrail_status": "success", + } + ], + ) + checker = ComplianceChecker(data) + assert len(checker._get_guardrails_by_mode("pre_call")) == 1 + assert len(checker._get_guardrails_by_mode("post_call")) == 0 + + def test_dict_tag_routed_guardrail_not_misclassified(self): + """A tag-routed guardrail (default=pre_call, a post_call tag) is not + guaranteed to run in either mode, so it counts for neither.""" + data = ComplianceCheckRequest( + request_id="req-mode-2", + user_id="user-1", + model="gpt-4", + timestamp="2026-02-17T12:00:00Z", + guardrail_information=[ + { + "guardrail_name": "pii_masking", + "guardrail_mode": {"default": "pre_call", "tags": {"eu": "post_call"}}, + "guardrail_status": "success", + } + ], + ) + checker = ComplianceChecker(data) + assert len(checker._get_guardrails_by_mode("pre_call")) == 0 + assert len(checker._get_guardrails_by_mode("post_call")) == 0 + results = {c.check_name: c.passed for c in checker.check_eu_ai_act()} + assert results["Content screened before LLM"] is False + + def test_dict_all_branches_pre_call_counts(self): + """When default and every tag override all run pre_call, the guardrail is + guaranteed pre_call regardless of routing, so it counts for pre_call.""" + data = ComplianceCheckRequest( + request_id="req-mode-4", + user_id="user-1", + model="gpt-4", + timestamp="2026-02-17T12:00:00Z", + guardrail_information=[ + { + "guardrail_name": "pii_masking", + "guardrail_mode": {"default": "pre_call", "tags": {"eu": "pre_call"}}, + "guardrail_status": "success", + } + ], + ) + checker = ComplianceChecker(data) + assert len(checker._get_guardrails_by_mode("pre_call")) == 1 + + def test_none_mode_defaults_to_pre_call(self): + """A guardrail logged without a mode counts as pre_call only.""" + data = ComplianceCheckRequest( + request_id="req-mode-3", + user_id="user-1", + model="gpt-4", + timestamp="2026-02-17T12:00:00Z", + guardrail_information=[{"guardrail_name": "pii_masking", "guardrail_status": "success"}], + ) + checker = ComplianceChecker(data) + assert len(checker._get_guardrails_by_mode("pre_call")) == 1 + assert len(checker._get_guardrails_by_mode("post_call")) == 0 + + def test_never_reports_false_compliant(self): + """The core invariant: a match reports `mode` satisfied only when every + configured branch runs in that mode. So True can never claim a hook the + guardrail may not have actually executed. The only allowed error + direction is under-reporting.""" + + def _branch_modes(value): + if isinstance(value, str): + return {value} + if isinstance(value, (list, tuple)): + return {v for v in value if isinstance(v, str)} + return set() + + def _guaranteed_modes(g_mode): + """Modes every branch of ``g_mode`` runs in.""" + if isinstance(g_mode, str): + return {g_mode} + if isinstance(g_mode, (list, tuple)): + sets = [_branch_modes(m) for m in g_mode] + return set.intersection(*sets) if sets else set() + if isinstance(g_mode, dict): + default = g_mode.get("default") + if default is None: + return set() + branches = [default, *(g_mode.get("tags") or {}).values()] + sets = [_branch_modes(b) for b in branches] + return set.intersection(*sets) if sets else set() + return set() + + shapes = [ + None, + "pre_call", + "post_call", + ["pre_call"], + ["pre_call", "post_call"], + [], + {"default": "pre_call"}, + {"default": ["pre_call", "post_call"]}, + {"default": "pre_call", "tags": {"a": "pre_call"}}, + {"default": "pre_call", "tags": {"a": "post_call"}}, + {"default": ["pre_call", "post_call"], "tags": {"a": "pre_call"}}, + {"tags": {"a": "post_call"}}, + {}, + {"default": 123}, + 5, + ] + for g_mode in shapes: + for mode in ("pre_call", "post_call", "during_call"): + matched = ComplianceChecker._mode_matches(g_mode, mode) + if g_mode is None: + assert matched is (mode == "pre_call"), (g_mode, mode) + continue + if matched: + assert mode in _guaranteed_modes(g_mode), (g_mode, mode) diff --git a/tests/test_litellm/proxy/management_endpoints/test_coordination_redis_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_coordination_redis_endpoints.py new file mode 100644 index 00000000000..4e6bfc4c063 --- /dev/null +++ b/tests/test_litellm/proxy/management_endpoints/test_coordination_redis_endpoints.py @@ -0,0 +1,577 @@ +""" +Unit tests for coordination Redis settings management endpoints +""" + +import asyncio +import json +import os +import sys +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from fastapi import HTTPException + +sys.path.insert(0, os.path.abspath("../../../..")) # Adds the parent directory to the system path + +import litellm +from litellm.caching.caching import RedisCache +from litellm.caching.redis_cluster_cache import RedisClusterCache +from litellm.proxy._types import LitellmTableNames, LitellmUserRoles +from litellm.proxy.auth.user_api_key_auth import UserAPIKeyAuth +from litellm.proxy.management_endpoints.coordination_redis_endpoints import ( + _REDACTED_VALUE, + CoordinationRedisSettingsRequest, + get_coordination_redis_settings, + check_coordination_redis_connection, + update_coordination_redis_settings, +) +from litellm.types.management_endpoints.coordination_redis_endpoints import ( + COORDINATION_REDIS_SETTINGS_FIELDS, +) + +_SAVED_SETTINGS = { + "host": "coord-redis.example.com", + "port": 6379, + "password": "super-secret-redis-pw", + "url": "redis://:super-secret-redis-pw@coord-redis.example.com:6379", + "sentinel_password": "super-secret-sentinel-pw", +} + + +def _admin_auth() -> UserAPIKeyAuth: + return UserAPIKeyAuth( + api_key="hashed", + user_id="admin-user", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + +def _prisma_with_general_settings(general_settings: dict | None) -> MagicMock: + """A prisma client whose LiteLLM_Config `general_settings` row holds ``general_settings``.""" + row = None + if general_settings is not None: + row = MagicMock() + row.param_value = json.dumps(general_settings) + + mock_prisma = MagicMock() + mock_prisma.db.litellm_config.find_unique = AsyncMock(return_value=row) + mock_prisma.db.litellm_config.upsert = AsyncMock() + return mock_prisma + + +def _proxy_config(file_general_settings: dict | None = None) -> MagicMock: + proxy_config = MagicMock() + proxy_config.get_config_state = MagicMock( + return_value={"general_settings": file_general_settings or {}}, + ) + return proxy_config + + +# ── GET /coordination_redis/settings ────────────────────────────────────────── + + +@pytest.mark.asyncio +async def test_get_redacts_every_credential_field(): + """password, sentinel_password and the (password-bearing) url never leave the + server in plaintext; non-credential fields come back untouched.""" + with ( + patch( + "litellm.proxy.proxy_server.prisma_client", + _prisma_with_general_settings({"coordination_redis": _SAVED_SETTINGS}), + ), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + ): + response = await get_coordination_redis_settings(user_api_key_dict=_admin_auth()) + + serialized = json.dumps(response.model_dump()) + assert "super-secret-redis-pw" not in serialized + assert "super-secret-sentinel-pw" not in serialized + + assert response.values["password"] == _REDACTED_VALUE + assert response.values["sentinel_password"] == _REDACTED_VALUE + assert response.values["url"] == _REDACTED_VALUE + assert response.values["host"] == "coord-redis.example.com" + assert response.values["port"] == 6379 + + # field metadata is hydrated with the same redacted values + by_name = {field.field_name: field for field in response.fields} + assert by_name["password"].field_value == _REDACTED_VALUE + assert by_name["host"].field_value == "coord-redis.example.com" + + +@pytest.mark.asyncio +async def test_get_source_is_coordination_redis_when_block_present(monkeypatch): + """An explicit block wins even when a Redis cache backend and REDIS_* env both exist.""" + monkeypatch.setattr(litellm, "cache", MagicMock(cache=MagicMock(spec=RedisCache))) + monkeypatch.setenv("REDIS_HOST", "env-redis") + + with ( + patch( + "litellm.proxy.proxy_server.prisma_client", + _prisma_with_general_settings({"coordination_redis": _SAVED_SETTINGS}), + ), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + ): + response = await get_coordination_redis_settings(user_api_key_dict=_admin_auth()) + + assert response.source == "coordination_redis" + + +@pytest.mark.asyncio +async def test_get_source_reads_block_from_yaml_config_when_db_row_absent(monkeypatch): + """A block set in config.yaml (not the DB) still reports source=coordination_redis.""" + monkeypatch.setattr(litellm, "cache", None) + monkeypatch.delenv("REDIS_HOST", raising=False) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings(None)), + patch( + "litellm.proxy.proxy_server.proxy_config", + _proxy_config({"coordination_redis": {"host": "yaml-redis"}}), + ), + ): + response = await get_coordination_redis_settings(user_api_key_dict=_admin_auth()) + + assert response.source == "coordination_redis" + assert response.values["host"] == "yaml-redis" + + +@pytest.mark.parametrize("cache_backend_cls", [RedisCache, RedisClusterCache]) +@pytest.mark.asyncio +async def test_get_source_is_cache_backend_when_no_block(monkeypatch, cache_backend_cls): + """With no explicit block, a plain-Redis response-cache backend is borrowed — + which beats the REDIS_* env fallback.""" + monkeypatch.setattr(litellm, "cache", MagicMock(cache=MagicMock(spec=cache_backend_cls))) + monkeypatch.setenv("REDIS_HOST", "env-redis") + + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings({})), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + ): + response = await get_coordination_redis_settings(user_api_key_dict=_admin_auth()) + + assert response.source == "cache_backend" + assert response.values == {} + + +@pytest.mark.asyncio +async def test_get_source_is_environment_when_no_block_and_non_redis_cache(monkeypatch): + """A non-Redis cache backend falls through to the REDIS_* env fallback.""" + monkeypatch.setattr(litellm, "cache", MagicMock(cache=MagicMock())) + monkeypatch.setenv("REDIS_HOST", "env-redis") + + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings({})), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + ): + response = await get_coordination_redis_settings(user_api_key_dict=_admin_auth()) + + assert response.source == "environment" + + +@pytest.mark.asyncio +async def test_get_source_is_none_when_nothing_configured(monkeypatch): + monkeypatch.setattr(litellm, "cache", None) + for env_var in ("REDIS_HOST", "REDIS_URL", "REDIS_CLUSTER_NODES", "REDIS_SENTINEL_NODES"): + monkeypatch.delenv(env_var, raising=False) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings({})), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + ): + response = await get_coordination_redis_settings(user_api_key_dict=_admin_auth()) + + assert response.source is None + + +@pytest.mark.asyncio +async def test_get_source_does_not_build_a_client(monkeypatch): + """The env-fallback probe is read-only: no Redis client is constructed on GET.""" + monkeypatch.setattr(litellm, "cache", None) + monkeypatch.setenv("REDIS_HOST", "env-redis") + + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings({})), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server._build_redis_usage_cache") as mock_build, + ): + response = await get_coordination_redis_settings(user_api_key_dict=_admin_auth()) + + assert response.source == "environment" + mock_build.assert_not_called() + + +@pytest.mark.asyncio +async def test_get_rejects_non_admin(): + with pytest.raises(HTTPException) as exc_info: + await get_coordination_redis_settings( + user_api_key_dict=UserAPIKeyAuth(api_key="hashed", user_role=LitellmUserRoles.INTERNAL_USER) + ) + assert exc_info.value.status_code == 403 + + +def test_fields_cover_every_coordination_redis_param(): + """The declarative field list drives the Admin UI form; it must stay in sync + with the model the backend validates against.""" + from litellm.proxy._types import CoordinationRedisParams + + assert {field.field_name for field in COORDINATION_REDIS_SETTINGS_FIELDS} == set( + CoordinationRedisParams.model_fields.keys() + ) + + by_name = {field.field_name: field for field in COORDINATION_REDIS_SETTINGS_FIELDS} + assert by_name["startup_nodes"].section == "cluster" + assert by_name["sentinel_nodes"].section == "sentinel" + assert by_name["host"].section == "connection" + + +# ── POST /coordination_redis/settings ───────────────────────────────────────── + + +@pytest.mark.asyncio +async def test_update_rejects_settings_without_a_connection_target(monkeypatch): + """A block with no host/url/startup_nodes/sentinel_nodes would blow up at + startup; reject it at write time and persist nothing.""" + monkeypatch.setattr(litellm, "store_audit_logs", False) + mock_prisma = _prisma_with_general_settings({}) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server.store_model_in_db", True), + ): + with pytest.raises(HTTPException) as exc_info: + await update_coordination_redis_settings( + request=CoordinationRedisSettingsRequest(settings={"ssl": True, "service_name": "mymaster"}), + user_api_key_dict=_admin_auth(), + litellm_changed_by=None, + ) + + assert exc_info.value.status_code == 400 + mock_prisma.db.litellm_config.upsert.assert_not_called() + + +@pytest.mark.asyncio +async def test_update_persists_into_the_general_settings_config_row(monkeypatch): + """Settings land under `general_settings.coordination_redis` in LiteLLM_Config + (the row startup merges over the yaml config), and sibling general_settings + keys survive the write.""" + monkeypatch.setattr(litellm, "store_audit_logs", False) + mock_prisma = _prisma_with_general_settings({"master_key": "sk-1234"}) + invalidated: list[str] = [] + + async def _capture_invalidate(param_name: str) -> None: + invalidated.append(param_name) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server.store_model_in_db", True), + patch( + "litellm.proxy.management_endpoints.coordination_redis_endpoints.invalidate_config_param", + new=_capture_invalidate, + ), + ): + response = await update_coordination_redis_settings( + request=CoordinationRedisSettingsRequest( + settings={"host": "coord-redis.example.com", "port": 6379, "password": "pw"} + ), + user_api_key_dict=_admin_auth(), + litellm_changed_by=None, + ) + + upsert_kwargs = mock_prisma.db.litellm_config.upsert.call_args.kwargs + assert upsert_kwargs["where"] == {"param_name": "general_settings"} + persisted = json.loads(upsert_kwargs["data"]["update"]["param_value"]) + assert persisted["coordination_redis"] == { + "host": "coord-redis.example.com", + "port": 6379, + "password": "pw", + } + assert persisted["master_key"] == "sk-1234" + assert invalidated == ["general_settings"] + + # the response echoes the saved settings back redacted + assert response["settings"]["password"] == _REDACTED_VALUE + assert response["settings"]["host"] == "coord-redis.example.com" + + +@pytest.mark.asyncio +async def test_update_persists_os_environ_refs_verbatim(monkeypatch): + """`os.environ/VAR` refs are resolved only to validate; the ref itself is what + gets stored, so the credential never lands in the DB.""" + monkeypatch.setattr(litellm, "store_audit_logs", False) + monkeypatch.setenv("MY_REDIS_HOST", "resolved-host") + mock_prisma = _prisma_with_general_settings({}) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server.store_model_in_db", True), + patch( + "litellm.proxy.management_endpoints.coordination_redis_endpoints.invalidate_config_param", + new=AsyncMock(), + ), + ): + await update_coordination_redis_settings( + request=CoordinationRedisSettingsRequest(settings={"host": "os.environ/MY_REDIS_HOST"}), + user_api_key_dict=_admin_auth(), + litellm_changed_by=None, + ) + + persisted = json.loads(mock_prisma.db.litellm_config.upsert.call_args.kwargs["data"]["update"]["param_value"]) + assert persisted["coordination_redis"] == {"host": "os.environ/MY_REDIS_HOST"} + + +@pytest.mark.asyncio +async def test_update_keeps_saved_credential_when_client_echoes_the_redaction_marker(monkeypatch): + """The UI reads settings back redacted; re-submitting them must not persist + `***REDACTED***` as the password.""" + monkeypatch.setattr(litellm, "store_audit_logs", False) + mock_prisma = _prisma_with_general_settings({"coordination_redis": _SAVED_SETTINGS}) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server.store_model_in_db", True), + patch( + "litellm.proxy.management_endpoints.coordination_redis_endpoints.invalidate_config_param", + new=AsyncMock(), + ), + ): + await update_coordination_redis_settings( + request=CoordinationRedisSettingsRequest( + settings={"host": "new-host", "port": 6380, "password": _REDACTED_VALUE} + ), + user_api_key_dict=_admin_auth(), + litellm_changed_by=None, + ) + + persisted = json.loads(mock_prisma.db.litellm_config.upsert.call_args.kwargs["data"]["update"]["param_value"]) + assert persisted["coordination_redis"]["password"] == "super-secret-redis-pw" + assert persisted["coordination_redis"]["host"] == "new-host" + + +@pytest.mark.asyncio +async def test_update_emits_audit_log_with_values_redacted(monkeypatch): + monkeypatch.setattr(litellm, "store_audit_logs", True) + mock_prisma = _prisma_with_general_settings({}) + audit_calls = [] + + async def capture(request_data): + audit_calls.append(request_data) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server.store_model_in_db", True), + patch("litellm.proxy.proxy_server.litellm_proxy_admin_name", "admin"), + patch( + "litellm.proxy.management_endpoints.coordination_redis_endpoints.invalidate_config_param", + new=AsyncMock(), + ), + patch("litellm.proxy.management_helpers.audit_logs.create_audit_log_for_update", new=capture), + ): + await update_coordination_redis_settings( + request=CoordinationRedisSettingsRequest( + settings={"host": "coord-redis.example.com", "password": "super-secret-redis-pw"} + ), + user_api_key_dict=_admin_auth(), + litellm_changed_by=None, + ) + for _ in range(3): + await asyncio.sleep(0) + + assert len(audit_calls) == 1 + log = audit_calls[0] + assert log.table_name == LitellmTableNames.CONFIG_TABLE_NAME + assert log.object_id == "coordination_redis" + assert log.action == "created" # no prior block → create + + after = json.loads(log.updated_values) + assert set(after["settings"].keys()) == {"host", "password"} + assert "super-secret-redis-pw" not in log.updated_values + assert "coord-redis.example.com" not in log.updated_values + + +@pytest.mark.asyncio +async def test_update_audit_action_is_updated_when_a_block_already_exists(monkeypatch): + monkeypatch.setattr(litellm, "store_audit_logs", True) + mock_prisma = _prisma_with_general_settings({"coordination_redis": {"host": "old-host"}}) + audit_calls = [] + + async def capture(request_data): + audit_calls.append(request_data) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server.store_model_in_db", True), + patch("litellm.proxy.proxy_server.litellm_proxy_admin_name", "admin"), + patch( + "litellm.proxy.management_endpoints.coordination_redis_endpoints.invalidate_config_param", + new=AsyncMock(), + ), + patch("litellm.proxy.management_helpers.audit_logs.create_audit_log_for_update", new=capture), + ): + await update_coordination_redis_settings( + request=CoordinationRedisSettingsRequest(settings={"host": "new-host"}), + user_api_key_dict=_admin_auth(), + litellm_changed_by=None, + ) + for _ in range(3): + await asyncio.sleep(0) + + assert audit_calls[0].action == "updated" + assert json.loads(audit_calls[0].before_value)["settings"] == {"host": _REDACTED_VALUE} + + +@pytest.mark.asyncio +async def test_update_rejects_non_admin(): + with pytest.raises(HTTPException) as exc_info: + await update_coordination_redis_settings( + request=CoordinationRedisSettingsRequest(settings={"host": "coord-redis.example.com"}), + user_api_key_dict=UserAPIKeyAuth(api_key="hashed", user_role=LitellmUserRoles.INTERNAL_USER), + litellm_changed_by=None, + ) + assert exc_info.value.status_code == 403 + + +# ── POST /coordination_redis/settings/test ──────────────────────────────────── + + +@pytest.mark.asyncio +async def test_connection_test_returns_healthy_on_successful_ping(): + mock_client = MagicMock() + mock_client.ping = AsyncMock(return_value=True) + mock_client.disconnect = AsyncMock() + + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings({})), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server._build_redis_usage_cache", return_value=mock_client) as mock_build, + ): + response = await check_coordination_redis_connection( + request=CoordinationRedisSettingsRequest( + settings={"host": "coord-redis.example.com", "port": 6379, "password": "pw"} + ), + user_api_key_dict=_admin_auth(), + ) + + assert response.status == "healthy" + assert response.error is None + assert mock_build.call_args.args[0] == {"host": "coord-redis.example.com", "port": 6379, "password": "pw"} + mock_client.ping.assert_awaited_once() + mock_client.disconnect.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_connection_test_reports_unhealthy_without_leaking_the_password(): + """Redis client errors echo the connection url back; the password must be + scrubbed out of the error the admin sees.""" + mock_client = MagicMock() + mock_client.ping = AsyncMock( + side_effect=ConnectionError("Error connecting to redis://:super-secret-redis-pw@coord-redis.example.com:6379") + ) + mock_client.disconnect = AsyncMock() + + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings({})), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server._build_redis_usage_cache", return_value=mock_client), + ): + response = await check_coordination_redis_connection( + request=CoordinationRedisSettingsRequest( + settings={ + "host": "coord-redis.example.com", + "url": "redis://:super-secret-redis-pw@coord-redis.example.com:6379", + "password": "super-secret-redis-pw", + } + ), + user_api_key_dict=_admin_auth(), + ) + + assert response.status == "unhealthy" + assert response.error is not None + assert "super-secret-redis-pw" not in response.error + assert _REDACTED_VALUE in response.error + mock_client.disconnect.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_connection_test_uses_the_saved_password_for_a_redacted_field(): + """An admin re-testing settings read back from GET sends `***REDACTED***`; + the saved credential is what actually gets dialed.""" + mock_client = MagicMock() + mock_client.ping = AsyncMock(return_value=True) + mock_client.disconnect = AsyncMock() + + with ( + patch( + "litellm.proxy.proxy_server.prisma_client", + _prisma_with_general_settings({"coordination_redis": _SAVED_SETTINGS}), + ), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server._build_redis_usage_cache", return_value=mock_client) as mock_build, + ): + response = await check_coordination_redis_connection( + request=CoordinationRedisSettingsRequest( + settings={"host": "coord-redis.example.com", "password": _REDACTED_VALUE} + ), + user_api_key_dict=_admin_auth(), + ) + + assert response.status == "healthy" + assert mock_build.call_args.args[0]["password"] == "super-secret-redis-pw" + + +@pytest.mark.asyncio +async def test_connection_test_times_out_instead_of_hanging(): + async def _never_returns(): + await asyncio.sleep(60) + + mock_client = MagicMock() + mock_client.ping = MagicMock(side_effect=lambda: _never_returns()) + mock_client.disconnect = AsyncMock() + + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings({})), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + patch("litellm.proxy.proxy_server._build_redis_usage_cache", return_value=mock_client), + patch( + "litellm.proxy.management_endpoints.coordination_redis_endpoints._PING_TIMEOUT_SECONDS", + 0.01, + ), + ): + response = await check_coordination_redis_connection( + request=CoordinationRedisSettingsRequest(settings={"host": "unreachable"}), + user_api_key_dict=_admin_auth(), + ) + + assert response.status == "unhealthy" + assert "timed out" in (response.error or "") + + +@pytest.mark.asyncio +async def test_connection_test_rejects_settings_without_a_connection_target(): + with ( + patch("litellm.proxy.proxy_server.prisma_client", _prisma_with_general_settings({})), + patch("litellm.proxy.proxy_server.proxy_config", _proxy_config()), + ): + with pytest.raises(HTTPException) as exc_info: + await check_coordination_redis_connection( + request=CoordinationRedisSettingsRequest(settings={"ssl": True}), + user_api_key_dict=_admin_auth(), + ) + + assert exc_info.value.status_code == 400 + + +@pytest.mark.asyncio +async def test_connection_test_rejects_non_admin(): + with pytest.raises(HTTPException) as exc_info: + await check_coordination_redis_connection( + request=CoordinationRedisSettingsRequest(settings={"host": "coord-redis.example.com"}), + user_api_key_dict=UserAPIKeyAuth(api_key="hashed", user_role=LitellmUserRoles.INTERNAL_USER), + ) + assert exc_info.value.status_code == 403 diff --git a/tests/test_litellm/proxy/management_endpoints/test_customer_budget.py b/tests/test_litellm/proxy/management_endpoints/test_customer_budget.py index 41f43c75f7d..0beca0c15e8 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_customer_budget.py +++ b/tests/test_litellm/proxy/management_endpoints/test_customer_budget.py @@ -134,8 +134,10 @@ async def test_update_customer_creates_budget_with_proper_relations( ) # Mock end user update + mock_updated_user = MagicMock() + mock_updated_user.model_dump.return_value = {"user_id": "test-user", "blocked": False} mock_prisma_client.db.litellm_endusertable.update = AsyncMock( - return_value=MagicMock() + return_value=mock_updated_user ) # Create update request with budget creation fields (not just budget_id) @@ -190,8 +192,10 @@ async def test_update_customer_creates_budget_with_required_fields( ) # Mock end user update + mock_updated_user = MagicMock() + mock_updated_user.model_dump.return_value = {"user_id": "test-user", "blocked": False} mock_prisma_client.db.litellm_endusertable.update = AsyncMock( - return_value=MagicMock() + return_value=mock_updated_user ) # Create update request with budget creation fields @@ -253,8 +257,10 @@ async def test_update_customer_budget_creation_with_fallback_admin( ) # Mock end user update + mock_updated_user = MagicMock() + mock_updated_user.model_dump.return_value = {"user_id": "test-user", "blocked": False} mock_prisma_client.db.litellm_endusertable.update = AsyncMock( - return_value=MagicMock() + return_value=mock_updated_user ) # Create update request with budget creation fields @@ -309,6 +315,7 @@ async def test_update_customer_with_budget_id_and_creation_fields( # Mock end user update mock_updated_user = MagicMock() + mock_updated_user.model_dump.return_value = {"user_id": "test-user", "blocked": False} mock_prisma_client.db.litellm_endusertable.update = AsyncMock( return_value=mock_updated_user ) diff --git a/tests/test_litellm/proxy/management_endpoints/test_customer_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_customer_endpoints.py index 6c5ccd3562f..5fbc3c4869b 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_customer_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_customer_endpoints.py @@ -1,18 +1,28 @@ +from typing import List from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi import FastAPI, HTTPException, Request, status from fastapi.responses import JSONResponse +from fastapi.routing import APIRoute from fastapi.testclient import TestClient from litellm.proxy._types import ( - LiteLLM_BudgetTable, LiteLLM_EndUserTable, LitellmUserRoles, ProxyException, ) from litellm.proxy.auth.user_api_key_auth import UserAPIKeyAuth, user_api_key_auth from litellm.proxy.management_endpoints.customer_endpoints import router +from litellm.types.proxy.management_endpoints.common_daily_activity import ( + SpendAnalyticsPaginatedResponse, +) +from litellm.types.proxy.management_endpoints.customer_endpoints import ( + BlockUsersResponse, + CustomerResponse, + DeleteCustomersResponse, + UnblockUsersResponse, +) app = FastAPI() @@ -22,9 +32,7 @@ async def openai_exception_handler(request: Request, exc: ProxyException): headers = exc.headers error_dict = exc.to_dict() return JSONResponse( - status_code=( - int(exc.code) if exc.code else status.HTTP_500_INTERNAL_SERVER_ERROR - ), + status_code=(int(exc.code) if exc.code else status.HTTP_500_INTERNAL_SERVER_ERROR), content={"error": error_dict}, headers=headers, ) @@ -54,30 +62,20 @@ def mock_user_api_key_auth(): def test_update_customer_success(mock_prisma_client, mock_user_api_key_auth): # Mock the database responses - mock_end_user = LiteLLM_EndUserTable( - user_id="test-user-1", alias="Test User", blocked=False - ) - updated_mock_end_user = LiteLLM_EndUserTable( - user_id="test-user-1", alias="Updated Test User", blocked=False - ) + mock_end_user = LiteLLM_EndUserTable(user_id="test-user-1", alias="Test User", blocked=False) + updated_mock_end_user = LiteLLM_EndUserTable(user_id="test-user-1", alias="Updated Test User", blocked=False) # Mock the find_first response - mock_prisma_client.db.litellm_endusertable.find_first = AsyncMock( - return_value=mock_end_user - ) + mock_prisma_client.db.litellm_endusertable.find_first = AsyncMock(return_value=mock_end_user) # Mock the update response - mock_prisma_client.db.litellm_endusertable.update = AsyncMock( - return_value=updated_mock_end_user - ) + mock_prisma_client.db.litellm_endusertable.update = AsyncMock(return_value=updated_mock_end_user) # Test data test_data = {"user_id": "test-user-1", "alias": "Updated Test User"} # Make the request - response = client.post( - "/customer/update", json=test_data, headers={"Authorization": "Bearer test-key"} - ) + response = client.post("/customer/update", json=test_data, headers={"Authorization": "Bearer test-key"}) # Assert response assert response.status_code == 200 @@ -106,10 +104,7 @@ def test_update_customer_not_found(mock_prisma_client, mock_user_api_key_auth): assert response.status_code == 404 response_json = response.json() assert "error" in response_json - assert ( - response_json["error"]["message"] - == "End User Id=non-existent-user does not exist in db" - ) + assert response_json["error"]["message"] == "End User Id=non-existent-user does not exist in db" assert response_json["error"]["type"] == "not_found" assert response_json["error"]["param"] == "user_id" assert response_json["error"]["code"] == "404" @@ -132,10 +127,7 @@ def test_info_customer_not_found(mock_prisma_client, mock_user_api_key_auth): assert response.status_code == 404 response_json = response.json() assert "error" in response_json - assert ( - response_json["error"]["message"] - == "End User Id=non-existent-user does not exist in db" - ) + assert response_json["error"]["message"] == "End User Id=non-existent-user does not exist in db" assert response_json["error"]["type"] == "not_found" assert response_json["error"]["param"] == "end_user_id" assert response_json["error"]["code"] == "404" @@ -220,11 +212,6 @@ def test_error_schema_consistency(mock_prisma_client, mock_user_api_key_auth): assert error["code"] == "404" # Test /customer/new - duplicate user error - from unittest.mock import MagicMock - - mock_end_user = LiteLLM_EndUserTable( - user_id="existing-user", alias="Existing User", blocked=False - ) mock_prisma_client.db.litellm_endusertable.create = AsyncMock( side_effect=Exception("Unique constraint failed on the fields: (`user_id`)") ) @@ -238,9 +225,7 @@ def test_error_schema_consistency(mock_prisma_client, mock_user_api_key_auth): assert error["code"] == "400" -def test_customer_endpoints_error_schema_consistency( - mock_prisma_client, mock_user_api_key_auth -): +def test_customer_endpoints_error_schema_consistency(mock_prisma_client, mock_user_api_key_auth): """ Test the exact scenarios from the curl examples provided. @@ -307,9 +292,7 @@ def test_customer_endpoints_error_schema_consistency( assert "Customer already exists" in error2["message"] # Verify both errors have the same schema structure - assert set(error1.keys()) == set( - error2.keys() - ), "Both errors should have the same top-level keys" + assert set(error1.keys()) == set(error2.keys()), "Both errors should have the same top-level keys" # Both should have string values for all fields for key in ["message", "type", "code"]: @@ -317,6 +300,153 @@ def test_customer_endpoints_error_schema_consistency( assert isinstance(error2[key], str), f"error2[{key}] should be a string" +EXPECTED_RESPONSE_MODELS = { + "/customer/block": BlockUsersResponse, + "/customer/unblock": UnblockUsersResponse, + "/customer/new": CustomerResponse, + "/customer/update": CustomerResponse, + "/customer/delete": DeleteCustomersResponse, + "/customer/info": CustomerResponse, + "/customer/list": List[CustomerResponse], + "/customer/daily/activity": SpendAnalyticsPaginatedResponse, +} + + +@pytest.mark.parametrize("path, expected_model", EXPECTED_RESPONSE_MODELS.items()) +def test_customer_routes_declare_response_model(path, expected_model): + """ + Every public /customer/* operation must declare a typed response_model so + the generated OpenAPI schema documents the response body. Regression for the + OpenAPI response-type coverage goal: drop a response_model and this fails. + """ + route = next(r for r in router.routes if isinstance(r, APIRoute) and r.path == path) + assert route.response_model == expected_model + + +def test_customer_new_documented_in_openapi_schema(): + """ + The response_model must surface in the OpenAPI schema as a concrete ref, not + an empty/default response. This is what the coverage metric measures. + """ + schema = app.openapi()["paths"]["/customer/new"]["post"] + json_schema = schema["responses"]["200"]["content"]["application/json"]["schema"] + assert json_schema["$ref"].endswith("/CustomerResponse") + + +def test_update_customer_response_preserves_budget_id(mock_prisma_client, mock_user_api_key_auth): + """ + Regression for the response_model field-stripping concern: budget_id is a real + column on the end-user table that /customer/update echoes. response_model= + LiteLLM_EndUserTable must NOT drop it, so budget_id stays in LiteLLM_EndUserTable. + """ + existing = LiteLLM_EndUserTable(user_id="cust-1", blocked=False) + updated = LiteLLM_EndUserTable(user_id="cust-1", blocked=False, budget_id="budget-123") + mock_prisma_client.db.litellm_endusertable.find_first = AsyncMock(return_value=existing) + mock_prisma_client.db.litellm_endusertable.update = AsyncMock(return_value=updated) + + response = client.post( + "/customer/update", + json={"user_id": "cust-1", "budget_id": "budget-123"}, + headers={"Authorization": "Bearer test-key"}, + ) + + assert response.status_code == 200 + assert response.json()["budget_id"] == "budget-123" + + +def test_update_customer_response_keeps_nested_budget_server_fields(mock_prisma_client, mock_user_api_key_auth): + """ + Faithfulness regression: /customer/update embeds the full budget row. The + response_model must keep the server-managed budget fields the endpoint used + to return (budget_reset_at, created_at) instead of the narrow write-allowlist + shape. The intentionally-internal audit fields (created_by/updated_by) stay out. + """ + existing = LiteLLM_EndUserTable(user_id="cust-1", blocked=False) + raw_row = MagicMock() + raw_row.model_dump.return_value = { + "user_id": "cust-1", + "blocked": False, + "alias": "renamed", + "spend": 0.0, + "allowed_model_region": None, + "default_model": None, + "budget_id": "b-1", + "object_permission_id": None, + "object_permission": None, + "litellm_budget_table": { + "budget_id": "b-1", + "max_budget": 10.0, + "budget_duration": "30d", + "budget_reset_at": "2024-02-01T00:00:00", + "created_at": "2024-01-01T00:00:00", + "created_by": "admin", + "updated_at": "2024-01-02T00:00:00", + "updated_by": "admin", + }, + } + mock_prisma_client.db.litellm_endusertable.find_first = AsyncMock(return_value=existing) + mock_prisma_client.db.litellm_endusertable.update = AsyncMock(return_value=raw_row) + + response = client.post( + "/customer/update", + json={"user_id": "cust-1", "alias": "renamed"}, + headers={"Authorization": "Bearer test-key"}, + ) + + assert response.status_code == 200 + budget = response.json()["litellm_budget_table"] + assert budget["budget_reset_at"] == "2024-02-01T00:00:00" + assert budget["created_at"] == "2024-01-01T00:00:00" + assert "created_by" not in budget + assert "updated_by" not in budget + + +def test_block_customer_success_serializes_through_response_model(mock_prisma_client, mock_user_api_key_auth): + """ + /customer/block returns {"blocked_users": []}. With + response_model=BlockUsersResponse, a shape mismatch would raise a 500 + ResponseValidationError, so a clean 200 proves the model matches runtime output. + """ + blocked_row = LiteLLM_EndUserTable(user_id="blocked-1", blocked=True) + mock_prisma_client.db.litellm_endusertable.upsert = AsyncMock(return_value=blocked_row) + + response = client.post( + "/customer/block", + json={"user_ids": ["blocked-1"]}, + headers={"Authorization": "Bearer test-key"}, + ) + + assert response.status_code == 200 + body = response.json() + assert body["blocked_users"][0]["user_id"] == "blocked-1" + assert body["blocked_users"][0]["blocked"] is True + + +def test_delete_customer_success_serializes_through_response_model(mock_prisma_client, mock_user_api_key_auth): + """ + /customer/delete returns {"deleted_customers": , "message": }. + response_model=DeleteCustomersResponse enforces that exact shape. + """ + existing = [ + LiteLLM_EndUserTable(user_id="u1", blocked=False), + LiteLLM_EndUserTable(user_id="u2", blocked=False), + ] + mock_prisma_client.db.litellm_endusertable.find_many = AsyncMock(return_value=existing) + mock_prisma_client.db.litellm_endusertable.delete_many = AsyncMock(return_value=2) + + response = client.post( + "/customer/delete", + json={"user_ids": ["u1", "u2"]}, + headers={"Authorization": "Bearer test-key"}, + ) + + assert response.status_code == 200 + assert response.json() == { + "deleted_customers": 2, + "message": "Successfully deleted customers with ids: ['u1', 'u2']", + } + + @pytest.mark.asyncio async def test_get_customer_daily_activity_admin_param_passing(monkeypatch): from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth @@ -331,9 +461,7 @@ async def test_get_customer_daily_activity_admin_param_passing(monkeypatch): mocked_response = MagicMock(name="SpendAnalyticsPaginatedResponse") get_daily_activity_mock = AsyncMock(return_value=mocked_response) - monkeypatch.setattr( - customer_endpoints, "get_daily_activity", get_daily_activity_mock - ) + monkeypatch.setattr(customer_endpoints, "get_daily_activity", get_daily_activity_mock) auth = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin1") result = await get_customer_daily_activity( @@ -380,16 +508,12 @@ async def test_get_customer_daily_activity_with_end_user_aliases(monkeypatch): mock_end_user2.user_id = "end-user-2" mock_end_user2.alias = "Customer Two" - mock_prisma_client.db.litellm_endusertable.find_many = AsyncMock( - return_value=[mock_end_user1, mock_end_user2] - ) + mock_prisma_client.db.litellm_endusertable.find_many = AsyncMock(return_value=[mock_end_user1, mock_end_user2]) monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) mocked_response = MagicMock(name="SpendAnalyticsPaginatedResponse") get_daily_activity_mock = AsyncMock(return_value=mocked_response) - monkeypatch.setattr( - customer_endpoints, "get_daily_activity", get_daily_activity_mock - ) + monkeypatch.setattr(customer_endpoints, "get_daily_activity", get_daily_activity_mock) auth = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin1") await get_customer_daily_activity( @@ -436,9 +560,7 @@ async def test_get_customer_daily_activity_non_admin_is_rejected(monkeypatch): monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) get_daily_activity_mock = AsyncMock() - monkeypatch.setattr( - customer_endpoints, "get_daily_activity", get_daily_activity_mock - ) + monkeypatch.setattr(customer_endpoints, "get_daily_activity", get_daily_activity_mock) non_admin_key = UserAPIKeyAuth( user_id="regular-user-abc", @@ -482,9 +604,7 @@ async def test_get_customer_daily_activity_service_account_key_is_rejected(monke monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) get_daily_activity_mock = AsyncMock() - monkeypatch.setattr( - customer_endpoints, "get_daily_activity", get_daily_activity_mock - ) + monkeypatch.setattr(customer_endpoints, "get_daily_activity", get_daily_activity_mock) service_account_key = UserAPIKeyAuth( user_id=None, @@ -507,3 +627,158 @@ async def test_get_customer_daily_activity_service_account_key_is_rejected(monke assert exc_info.value.status_code == 401 assert "Admin-only endpoint" in str(exc_info.value.detail) get_daily_activity_mock.assert_not_called() + + +# --------------------------------------------------------------------------- +# Characterization (golden-master) tests. +# +# These lock the EXACT JSON body every customer-object endpoint returns today, +# so a type-safety refactor of the handlers is only allowed to land if it +# reproduces these byte for byte. The input below is what a Prisma row's +# .model_dump() yields (full nested budget incl. audit fields + object_permission +# incl. reverse relations); the expected output is what the live endpoint emits. +# --------------------------------------------------------------------------- + +_FULL_DB_ROW = { + "user_id": "c1", + "blocked": False, + "alias": "Acme", + "spend": 1.5, + "allowed_model_region": None, + "default_model": None, + "budget_id": "b1", + "object_permission_id": "p1", + "litellm_budget_table": { + "budget_id": "b1", + "max_budget": 10.0, + "soft_budget": None, + "max_parallel_requests": None, + "tpm_limit": None, + "rpm_limit": None, + "model_max_budget": None, + "budget_duration": "30d", + "allowed_models": [], + "budget_reset_at": "2024-02-01T00:00:00", + "created_at": "2024-01-01T00:00:00", + "created_by": "admin", + "updated_at": "2024-01-02T00:00:00", + "updated_by": "admin", + }, + "object_permission": { + "object_permission_id": "p1", + "mcp_servers": ["s1"], + "mcp_access_groups": [], + "mcp_tool_permissions": None, + "vector_stores": [], + "agents": [], + "agent_access_groups": [], + "models": [], + "mcp_toolsets": None, + "blocked_tools": [], + "search_tools": [], + "teams": [{"team_id": "t1"}], + "users": [{"user_id": "x"}], + "end_users": [], + "organizations": [], + "verification_tokens": [], + }, +} + +_EXPECTED_CUSTOMER = { + "user_id": "c1", + "blocked": False, + "alias": "Acme", + "spend": 1.5, + "allowed_model_region": None, + "default_model": None, + "budget_id": "b1", + "litellm_budget_table": { + "budget_id": "b1", + "soft_budget": None, + "max_budget": 10.0, + "max_parallel_requests": None, + "tpm_limit": None, + "rpm_limit": None, + "model_max_budget": None, + "budget_duration": "30d", + "allowed_models": [], + "budget_reset_at": "2024-02-01T00:00:00", + "created_at": "2024-01-01T00:00:00", + }, + "object_permission_id": "p1", + "object_permission": { + "object_permission_id": "p1", + "mcp_servers": ["s1"], + "mcp_access_groups": [], + "mcp_tool_permissions": None, + "vector_stores": [], + "agents": [], + "agent_access_groups": [], + "models": [], + "mcp_toolsets": None, + "blocked_tools": [], + "search_tools": [], + "mcp_tool_search_enabled": None, + }, +} + + +def _row(dump: dict) -> MagicMock: + row = MagicMock() + row.model_dump.return_value = dump + return row + + +def test_char_info_body(mock_prisma_client, mock_user_api_key_auth): + mock_prisma_client.db.litellm_endusertable.find_first = AsyncMock(return_value=_row(_FULL_DB_ROW)) + response = client.get("/customer/info?end_user_id=c1", headers={"Authorization": "Bearer k"}) + assert response.status_code == 200 + assert response.json() == _EXPECTED_CUSTOMER + + +def test_char_list_body(mock_prisma_client, mock_user_api_key_auth): + mock_prisma_client.db.litellm_endusertable.find_many = AsyncMock(return_value=[_row(_FULL_DB_ROW)]) + response = client.get("/customer/list", headers={"Authorization": "Bearer k"}) + assert response.status_code == 200 + assert response.json() == [_EXPECTED_CUSTOMER] + + +def test_char_new_body(mock_prisma_client, mock_user_api_key_auth): + mock_prisma_client.db.litellm_endusertable.create = AsyncMock(return_value=_row(_FULL_DB_ROW)) + response = client.post("/customer/new", json={"user_id": "c1"}, headers={"Authorization": "Bearer k"}) + assert response.status_code == 200 + assert response.json() == _EXPECTED_CUSTOMER + + +def test_char_update_body(mock_prisma_client, mock_user_api_key_auth): + mock_prisma_client.db.litellm_endusertable.find_first = AsyncMock( + return_value=_row({"user_id": "c1", "blocked": False}) + ) + mock_prisma_client.db.litellm_endusertable.update = AsyncMock(return_value=_row(_FULL_DB_ROW)) + response = client.post( + "/customer/update", + json={"user_id": "c1", "alias": "Acme"}, + headers={"Authorization": "Bearer k"}, + ) + assert response.status_code == 200 + assert response.json() == _EXPECTED_CUSTOMER + + +def test_char_delete_body(mock_prisma_client, mock_user_api_key_auth): + mock_prisma_client.db.litellm_endusertable.find_many = AsyncMock( + return_value=[ + LiteLLM_EndUserTable(user_id="c1", blocked=False), + LiteLLM_EndUserTable(user_id="c2", blocked=False), + ] + ) + mock_prisma_client.db.litellm_endusertable.delete_many = AsyncMock(return_value=2) + response = client.post( + "/customer/delete", + json={"user_ids": ["c1", "c2"]}, + headers={"Authorization": "Bearer k"}, + ) + assert response.status_code == 200 + assert response.json() == { + "deleted_customers": 2, + "message": "Successfully deleted customers with ids: ['c1', 'c2']", + } diff --git a/tests/test_litellm/proxy/management_endpoints/test_internal_user_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_internal_user_endpoints.py index 56eeea82223..ce2d04f0d26 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_internal_user_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_internal_user_endpoints.py @@ -1000,6 +1000,259 @@ async def test_new_user_non_admin_cannot_create_admin(mocker): assert str(LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY) in str(exc_info2.value.message) +@pytest.mark.asyncio +async def test_new_user_non_admin_permissions_non_empty_rejected(mocker): + """`new_user` rejects a non-admin when `permissions` is present in the + request body. `/user/new` propagates the value into the auto-created + key via `generate_key_helper_fn`.""" + from litellm.proxy.management_endpoints.internal_user_endpoints import new_user + + mock_prisma_client = mocker.MagicMock() + + async def mock_count(*args, **kwargs): + return 5 + + mock_prisma_client.db.litellm_usertable.count = mock_count + + async def mock_check(*_args, **_kwargs): + return None + + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints._check_duplicate_user_email", + mock_check, + ) + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints._check_duplicate_user_id", + mock_check, + ) + mock_license_check = mocker.MagicMock() + mock_license_check.is_over_limit.return_value = False + mocker.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + mocker.patch("litellm.proxy.proxy_server._license_check", mock_license_check) + + data = NewUserRequest( + user_email="alice@example.com", + user_role=LitellmUserRoles.INTERNAL_USER, + permissions={"get_spend_routes": True}, + ) + caller = UserAPIKeyAuth( + user_id="org-admin", user_role=LitellmUserRoles.ORG_ADMIN + ) + + with pytest.raises(ProxyException) as exc_info: + await new_user(data=data, user_api_key_dict=caller) + assert str(exc_info.value.code) == "403" + assert "permissions" in str(exc_info.value.message) + + +@pytest.mark.asyncio +async def test_new_user_non_admin_permissions_explicit_empty_rejected(mocker): + """`new_user` rejects a non-admin when `permissions` is present as + `{}` in the request body. The value matches the model default but + `model_fields_set` distinguishes the two.""" + from litellm.proxy.management_endpoints.internal_user_endpoints import new_user + + mock_prisma_client = mocker.MagicMock() + + async def mock_count(*args, **kwargs): + return 5 + + mock_prisma_client.db.litellm_usertable.count = mock_count + + async def mock_check(*_args, **_kwargs): + return None + + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints._check_duplicate_user_email", + mock_check, + ) + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints._check_duplicate_user_id", + mock_check, + ) + mock_license_check = mocker.MagicMock() + mock_license_check.is_over_limit.return_value = False + mocker.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + mocker.patch("litellm.proxy.proxy_server._license_check", mock_license_check) + + data = NewUserRequest( + user_email="alice@example.com", + user_role=LitellmUserRoles.INTERNAL_USER, + permissions={}, + ) + assert "permissions" in data.model_fields_set + caller = UserAPIKeyAuth( + user_id="org-admin", user_role=LitellmUserRoles.ORG_ADMIN + ) + + with pytest.raises(ProxyException) as exc_info: + await new_user(data=data, user_api_key_dict=caller) + assert str(exc_info.value.code) == "403" + assert "permissions" in str(exc_info.value.message) + + +@pytest.mark.asyncio +async def test_new_user_non_admin_omits_permissions_succeeds(mocker): + """`new_user` does not fire the permissions gate when `permissions` + is absent from the request body. The model-level default `{}` is not + in `model_fields_set`.""" + from litellm.proxy.management_endpoints.internal_user_endpoints import new_user + + mock_prisma_client = mocker.MagicMock() + + async def mock_count(*args, **kwargs): + return 5 + + mock_prisma_client.db.litellm_usertable.count = mock_count + + async def mock_check(*_args, **_kwargs): + return None + + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints._check_duplicate_user_email", + mock_check, + ) + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints._check_duplicate_user_id", + mock_check, + ) + mock_license_check = mocker.MagicMock() + mock_license_check.is_over_limit.return_value = False + mocker.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + mocker.patch("litellm.proxy.proxy_server._license_check", mock_license_check) + + stub_response = {"user_id": "alice", "key": "sk-alice", "expires": None} + + async def stub_helper(**_kwargs): + return stub_response + + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints.generate_key_helper_fn", + stub_helper, + ) + + data = NewUserRequest( + user_email="alice@example.com", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + assert "permissions" not in data.model_fields_set + caller = UserAPIKeyAuth( + user_id="org-admin", user_role=LitellmUserRoles.ORG_ADMIN + ) + + result = await new_user(data=data, user_api_key_dict=caller) + assert result is not None + + +@pytest.mark.asyncio +async def test_new_user_admin_can_set_permissions(mocker): + """`new_user` accepts a PROXY_ADMIN caller for any shape of + `permissions` in the request body.""" + from litellm.proxy.management_endpoints.internal_user_endpoints import new_user + + mock_prisma_client = mocker.MagicMock() + + async def mock_count(*args, **kwargs): + return 5 + + mock_prisma_client.db.litellm_usertable.count = mock_count + + async def mock_check(*_args, **_kwargs): + return None + + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints._check_duplicate_user_email", + mock_check, + ) + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints._check_duplicate_user_id", + mock_check, + ) + mock_license_check = mocker.MagicMock() + mock_license_check.is_over_limit.return_value = False + mocker.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + mocker.patch("litellm.proxy.proxy_server._license_check", mock_license_check) + + async def stub_helper(**_kwargs): + return {"user_id": "alice", "key": "sk-alice", "expires": None} + + mocker.patch( + "litellm.proxy.management_endpoints.internal_user_endpoints.generate_key_helper_fn", + stub_helper, + ) + + admin = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) + for permissions_value in ({"get_spend_routes": True}, {}, None): + data = NewUserRequest( + user_email=f"alice-{permissions_value}@example.com", + user_role=LitellmUserRoles.INTERNAL_USER, + permissions=permissions_value, + ) + result = await new_user(data=data, user_api_key_dict=admin) + assert result is not None + + +@pytest.mark.asyncio +async def test_update_single_user_non_admin_permissions_rejected(mocker): + """`_update_single_user_helper` rejects a non-admin when `permissions` + is present in the request body. Covers both `/user/update` and + `/user/bulk_update`, which share this helper.""" + from fastapi import HTTPException + + from litellm.proxy._types import UpdateUserRequest + from litellm.proxy.management_endpoints.internal_user_endpoints import ( + _update_single_user_helper, + ) + + mock_prisma_client = mocker.MagicMock() + mocker.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + mocker.patch("litellm.proxy.proxy_server.litellm_proxy_admin_name", "admin") + + data = UpdateUserRequest( + user_id="alice", + permissions={"get_spend_routes": True}, + ) + caller = UserAPIKeyAuth( + user_id="org-admin", user_role=LitellmUserRoles.ORG_ADMIN + ) + + with pytest.raises(HTTPException) as exc_info: + await _update_single_user_helper( + user_request=data, user_api_key_dict=caller + ) + assert exc_info.value.status_code == 403 + assert "permissions" in str(exc_info.value.detail) + + +@pytest.mark.asyncio +async def test_update_single_user_non_admin_permissions_explicit_empty_rejected(mocker): + """`_update_single_user_helper` rejects a non-admin when `permissions` + is present as `{}` in the request body.""" + from fastapi import HTTPException + + from litellm.proxy._types import UpdateUserRequest + from litellm.proxy.management_endpoints.internal_user_endpoints import ( + _update_single_user_helper, + ) + + mock_prisma_client = mocker.MagicMock() + mocker.patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + mocker.patch("litellm.proxy.proxy_server.litellm_proxy_admin_name", "admin") + + data = UpdateUserRequest(user_id="alice", permissions={}) + assert "permissions" in data.model_fields_set + caller = UserAPIKeyAuth( + user_id="org-admin", user_role=LitellmUserRoles.ORG_ADMIN + ) + + with pytest.raises(HTTPException) as exc_info: + await _update_single_user_helper( + user_request=data, user_api_key_dict=caller + ) + assert exc_info.value.status_code == 403 + assert "permissions" in str(exc_info.value.detail) + + @pytest.mark.asyncio async def test_user_info_url_encoding_plus_character(mocker): """ diff --git a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py index 04048020e18..db6d3489830 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py @@ -15,8 +15,11 @@ from unittest.mock import AsyncMock, MagicMock, patch from fastapi import HTTPException +import inspect + from litellm.proxy._types import ( GenerateKeyRequest, + NewUserRequest, LiteLLM_BudgetTable, LiteLLM_OrganizationTable, LiteLLM_TeamTableCachedObj, @@ -762,6 +765,58 @@ async def test_generate_key_helper_fn_with_access_group_ids(monkeypatch): assert captured_key_data.get("access_group_ids") == ["ag-1", "ag-2"] +@pytest.mark.asyncio +async def test_generate_key_helper_fn_with_budget_fallbacks(monkeypatch): + """Regression: /key/generate must accept `budget_fallbacks` end-to-end. + + generate_key_helper_fn previously had no `budget_fallbacks` parameter, so + passing it via /key/generate (which unpacks the full request body as + kwargs) raised "unexpected keyword argument" before ever reaching the DB. + """ + mock_prisma_client = AsyncMock() + mock_prisma_client.jsonify_object = lambda data: data # type: ignore + mock_prisma_client.db = MagicMock() + mock_prisma_client.db.litellm_objectpermissiontable = MagicMock() + mock_prisma_client.db.litellm_objectpermissiontable.create = AsyncMock( + return_value=MagicMock(object_permission_id=None) + ) + + captured_key_data = {} + + async def _insert_data_side_effect(*args, **kwargs): + table_name = kwargs.get("table_name") + if table_name == "user": + return MagicMock(models=[], spend=0) + elif table_name == "key": + captured_key_data.update(kwargs.get("data", {})) + return MagicMock( + token="hashed_token_budget_fallbacks", + litellm_budget_table=None, + object_permission=None, + created_at=None, + updated_at=None, + ) + return MagicMock() + + mock_prisma_client.insert_data = AsyncMock(side_effect=_insert_data_side_effect) + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + from litellm.proxy.management_endpoints.key_management_endpoints import ( + generate_key_helper_fn, + ) + + await generate_key_helper_fn( + request_type="key", + table_name="key", + user_id="test-user", + budget_fallbacks={"anthropic-haiku-4-5": ["gpt-5.5"]}, + ) + + assert json.loads(captured_key_data["budget_fallbacks"]) == { + "anthropic-haiku-4-5": ["gpt-5.5"] + } + + @pytest.mark.asyncio async def test_key_generation_with_mcp_tool_permissions(monkeypatch): """ @@ -1443,6 +1498,57 @@ async def test_generate_service_account_works_with_team_id(): ) +@pytest.mark.asyncio +async def test_generate_key_throttle_rejected_for_non_admin(): + """Security regression: a non-admin creating a key must not be able to set + throttle_on_budget_exceeded=true, which would let the new key keep spending + past an admin-imposed per-key budget ceiling instead of hard-blocking. The + /key/update gate does not cover generate, so generate needs its own admin + check. Only the enable value is gated, so this must 403.""" + mock_prisma_client = AsyncMock() + with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client): + with pytest.raises(HTTPException) as exc: + await _common_key_generation_helper( + data=GenerateKeyRequest(throttle_on_budget_exceeded=True), + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-alice", + user_id="alice", + ), + litellm_changed_by=None, + team_table=None, + ) + assert int(getattr(exc.value, "status_code", 0)) == 403 + assert "Only proxy admins can enable throttle_on_budget_exceeded" in str(exc.value.detail) + + +@pytest.mark.asyncio +async def test_generate_key_throttle_allowed_for_admin(): + """A proxy admin may create a key with throttle_on_budget_exceeded=true; the + generate admin gate must let the admin through to key creation.""" + with ( + patch("litellm.proxy.proxy_server.prisma_client", AsyncMock()), + patch("litellm.proxy.proxy_server.llm_router", None), + patch("litellm.proxy.proxy_server.premium_user", False), + patch( + "litellm.proxy.management_endpoints.key_management_endpoints.generate_key_helper_fn" + ) as mock_generate_key, + ): + mock_generate_key.return_value = { + "key": "sk-test-key", + "expires": None, + "user_id": "admin", + "team_id": None, + } + await _common_key_generation_helper( + data=GenerateKeyRequest(throttle_on_budget_exceeded=True), + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-1"), + litellm_changed_by=None, + team_table=None, + ) + assert mock_generate_key.called + + @pytest.mark.asyncio async def test_update_service_account_requires_team_id(): data = UpdateKeyRequest(key="sk-1", metadata={"service_account_id": "sa"}) @@ -4222,6 +4328,7 @@ def test_transform_verification_tokens_to_deleted_records(): permissions={"permission": True}, metadata={}, model_max_budget={"gpt-4": {"budget_limit": 100.0}}, + budget_fallbacks={"gpt-4": ["gpt-4o-mini"]}, model_spend={}, soft_budget_cooldown=False, allowed_routes=[], @@ -4260,6 +4367,8 @@ def test_transform_verification_tokens_to_deleted_records(): record2 = records[1] assert record2["token"] == "hashed-token-2" assert isinstance(record2["model_max_budget"], str) + assert isinstance(record2["budget_fallbacks"], str) + assert json.loads(record2["budget_fallbacks"]) == {"gpt-4": ["gpt-4o-mini"]} def test_transform_verification_tokens_to_deleted_records_empty_list(): @@ -7781,6 +7890,264 @@ async def test_default_key_generate_params_duration(monkeypatch): litellm.default_key_generate_params = original_value +async def test_default_key_generate_params_object_permission_applied_when_absent( + monkeypatch, +): + """ + default_key_generate_params.object_permission is applied to a key that + doesn't specify object_permission at all. + """ + import litellm + + mock_prisma_client = AsyncMock() + mock_insert_data = AsyncMock( + return_value=MagicMock( + token="hashed_token_123", litellm_budget_table=None, object_permission=None + ) + ) + mock_prisma_client.insert_data = mock_insert_data + mock_prisma_client.db = MagicMock() + mock_prisma_client.db.litellm_verificationtoken = MagicMock() + mock_prisma_client.db.litellm_verificationtoken.find_unique = AsyncMock( + return_value=None + ) + mock_prisma_client.db.litellm_verificationtoken.find_many = AsyncMock( + return_value=[] + ) + mock_prisma_client.db.litellm_verificationtoken.count = AsyncMock(return_value=0) + mock_prisma_client.db.litellm_verificationtoken.update = AsyncMock( + return_value=MagicMock( + token="hashed_token_123", litellm_budget_table=None, object_permission=None + ) + ) + mock_prisma_client.db.litellm_objectpermissiontable = MagicMock() + mock_prisma_client.db.litellm_objectpermissiontable.create = AsyncMock( + return_value=MagicMock(object_permission_id="objperm-1") + ) + + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + original_value = litellm.default_key_generate_params + litellm.default_key_generate_params = { + "object_permission": {"vector_stores": ["default-vs"]} + } + + try: + request = GenerateKeyRequest() # No object_permission specified + await _common_key_generation_helper( + data=request, + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, + api_key="sk-1234", + user_id="1234", + ), + litellm_changed_by=None, + team_table=None, + ) + + created_data = mock_prisma_client.db.litellm_objectpermissiontable.create.call_args.kwargs["data"] + assert created_data["vector_stores"] == ["default-vs"] + finally: + litellm.default_key_generate_params = original_value + + +async def test_default_key_generate_params_object_permission_merges_partial( + monkeypatch, +): + """ + default_key_generate_params.object_permission fills only the fields the + caller left unset - an explicitly supplied field (agents here) is + preserved alongside the defaulted field (vector_stores). + """ + import litellm + from litellm.proxy._types import LiteLLM_ObjectPermissionBase + + mock_prisma_client = AsyncMock() + mock_insert_data = AsyncMock( + return_value=MagicMock( + token="hashed_token_123", litellm_budget_table=None, object_permission=None + ) + ) + mock_prisma_client.insert_data = mock_insert_data + mock_prisma_client.db = MagicMock() + mock_prisma_client.db.litellm_verificationtoken = MagicMock() + mock_prisma_client.db.litellm_verificationtoken.find_unique = AsyncMock( + return_value=None + ) + mock_prisma_client.db.litellm_verificationtoken.find_many = AsyncMock( + return_value=[] + ) + mock_prisma_client.db.litellm_verificationtoken.count = AsyncMock(return_value=0) + mock_prisma_client.db.litellm_verificationtoken.update = AsyncMock( + return_value=MagicMock( + token="hashed_token_123", litellm_budget_table=None, object_permission=None + ) + ) + mock_prisma_client.db.litellm_objectpermissiontable = MagicMock() + mock_prisma_client.db.litellm_objectpermissiontable.create = AsyncMock( + return_value=MagicMock(object_permission_id="objperm-2") + ) + + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + original_value = litellm.default_key_generate_params + litellm.default_key_generate_params = { + "object_permission": {"vector_stores": ["default-vs"]} + } + + try: + request = GenerateKeyRequest( + object_permission=LiteLLM_ObjectPermissionBase(agents=["agent-1"]) + ) + await _common_key_generation_helper( + data=request, + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, + api_key="sk-1234", + user_id="1234", + ), + litellm_changed_by=None, + team_table=None, + ) + + created_data = mock_prisma_client.db.litellm_objectpermissiontable.create.call_args.kwargs["data"] + assert created_data["agents"] == ["agent-1"] + assert created_data["vector_stores"] == ["default-vs"] + finally: + litellm.default_key_generate_params = original_value + + +async def test_default_key_generate_params_object_permission_does_not_override_explicit( + monkeypatch, +): + """ + A field the caller explicitly set on object_permission must win over the + same field in default_key_generate_params. + """ + import litellm + from litellm.proxy._types import LiteLLM_ObjectPermissionBase + + mock_prisma_client = AsyncMock() + mock_insert_data = AsyncMock( + return_value=MagicMock( + token="hashed_token_123", litellm_budget_table=None, object_permission=None + ) + ) + mock_prisma_client.insert_data = mock_insert_data + mock_prisma_client.db = MagicMock() + mock_prisma_client.db.litellm_verificationtoken = MagicMock() + mock_prisma_client.db.litellm_verificationtoken.find_unique = AsyncMock( + return_value=None + ) + mock_prisma_client.db.litellm_verificationtoken.find_many = AsyncMock( + return_value=[] + ) + mock_prisma_client.db.litellm_verificationtoken.count = AsyncMock(return_value=0) + mock_prisma_client.db.litellm_verificationtoken.update = AsyncMock( + return_value=MagicMock( + token="hashed_token_123", litellm_budget_table=None, object_permission=None + ) + ) + mock_prisma_client.db.litellm_objectpermissiontable = MagicMock() + mock_prisma_client.db.litellm_objectpermissiontable.create = AsyncMock( + return_value=MagicMock(object_permission_id="objperm-3") + ) + + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + original_value = litellm.default_key_generate_params + litellm.default_key_generate_params = { + "object_permission": {"vector_stores": ["default-vs"]} + } + + try: + request = GenerateKeyRequest( + object_permission=LiteLLM_ObjectPermissionBase( + vector_stores=["explicit-vs"] + ) + ) + await _common_key_generation_helper( + data=request, + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, + api_key="sk-1234", + user_id="1234", + ), + litellm_changed_by=None, + team_table=None, + ) + + created_data = mock_prisma_client.db.litellm_objectpermissiontable.create.call_args.kwargs["data"] + assert created_data["vector_stores"] == ["explicit-vs"] + finally: + litellm.default_key_generate_params = original_value + + +async def test_default_key_generate_params_object_permission_not_rejected_for_non_admin_personal_key( + monkeypatch, +): + """ + Regression test: a default_key_generate_params.object_permission containing + a team-scoped field (vector_stores) must not turn ordinary non-admin + personal key creation into a 403. The default is merged in *after* the + caller-scope validation, so it is never mistaken for a caller-requested + permission. + """ + import litellm + + mock_prisma_client = AsyncMock() + mock_insert_data = AsyncMock( + return_value=MagicMock( + token="hashed_token_123", litellm_budget_table=None, object_permission=None + ) + ) + mock_prisma_client.insert_data = mock_insert_data + mock_prisma_client.db = MagicMock() + mock_prisma_client.db.litellm_verificationtoken = MagicMock() + mock_prisma_client.db.litellm_verificationtoken.find_unique = AsyncMock( + return_value=None + ) + mock_prisma_client.db.litellm_verificationtoken.find_many = AsyncMock( + return_value=[] + ) + mock_prisma_client.db.litellm_verificationtoken.count = AsyncMock(return_value=0) + mock_prisma_client.db.litellm_verificationtoken.update = AsyncMock( + return_value=MagicMock( + token="hashed_token_123", litellm_budget_table=None, object_permission=None + ) + ) + mock_prisma_client.db.litellm_objectpermissiontable = MagicMock() + mock_prisma_client.db.litellm_objectpermissiontable.create = AsyncMock( + return_value=MagicMock(object_permission_id="objperm-4") + ) + + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + original_value = litellm.default_key_generate_params + litellm.default_key_generate_params = { + "object_permission": {"vector_stores": ["default-vs"]} + } + + try: + request = GenerateKeyRequest(user_id="alice") # No object_permission specified + response = await _common_key_generation_helper( + data=request, + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-alice", + user_id="alice", + ), + litellm_changed_by=None, + team_table=None, + ) + + assert response is not None + created_data = mock_prisma_client.db.litellm_objectpermissiontable.create.call_args.kwargs["data"] + assert created_data["vector_stores"] == ["default-vs"] + finally: + litellm.default_key_generate_params = original_value + + @pytest.mark.asyncio async def test_build_key_filter_member_team_service_accounts(): """ @@ -8656,7 +9023,7 @@ class TestValidateKeyAliasFormat: litellm.enable_key_alias_format_validation = False def test_validation_skipped_when_flag_disabled(self): - """When enable_key_alias_format_validation is False (default), no validation occurs.""" + """When enable_key_alias_format_validation is False (default), no charset/length validation occurs.""" from litellm.proxy.management_endpoints.key_management_endpoints import ( _validate_key_alias_format, ) @@ -8667,6 +9034,33 @@ class TestValidateKeyAliasFormat: _validate_key_alias_format("!invalid!") _validate_key_alias_format("a" * 256) + @pytest.mark.parametrize( + "unsafe_alias", + [ + "../../../other-app/creds", + "litellm/../../secret", + "foo\n- !grant\n role: !!admin\n member: attacker", + "foo\rbar", + "foo\x00bar", + ], + ) + def test_validate_key_alias_format_rejects_traversal_and_control_chars_even_when_flag_disabled( + self, unsafe_alias + ): + """ + Regression test: this check must reject an invalid key_alias unconditionally, + even when enable_key_alias_format_validation (the separate, opt-in charset + rule) is disabled. + """ + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _validate_key_alias_format, + ) + + with pytest.raises(ProxyException) as exc: + _validate_key_alias_format(unsafe_alias) + assert str(exc.value.code) == "400" + assert "Invalid key_alias" in str(exc.value.message) + def test_validate_key_alias_format_valid(self): from litellm.proxy.management_endpoints.key_management_endpoints import ( _validate_key_alias_format, @@ -9264,6 +9658,165 @@ async def test_update_key_non_budget_fields_allowed_for_internal_user(monkeypatc assert result is not None +@pytest.mark.asyncio +async def test_update_key_throttle_on_budget_exceeded_rejected_for_internal_user( + monkeypatch, +): + """Security regression: throttle_on_budget_exceeded turns an admin-imposed + hard budget block into a soft throttle that keeps spending past max_budget, + so it is a budget-enforcement change. A non-admin key owner (same setup that + is allowed to change non-budget fields via the caller_is_creator shortcut) + must NOT be able to self-opt-in to it; it has to route through the admin-only + _check_key_admin_access and return 403. Without treating the flag as a budget + change this update would succeed, letting the owner bypass their own cap.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + update_key_fn, + ) + + mock_prisma_client = AsyncMock() + mock_user_api_key_cache = AsyncMock() + mock_proxy_logging_obj = MagicMock() + + test_hashed_token = "a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd" + + # Owner of the key (created_by == user_id) so caller_is_creator is True. + # This is exactly the setup that is allowed to change non-budget fields; + # the throttle flag must still be rejected. + mock_existing_key = MagicMock() + mock_existing_key.token = test_hashed_token + mock_existing_key.user_id = "internal_user" + mock_existing_key.created_by = "internal_user" + mock_existing_key.team_id = None + mock_existing_key.project_id = None + mock_existing_key.max_budget = 10.0 + mock_existing_key.key_alias = None + mock_existing_key.models = [] + mock_existing_key.metadata = {} + mock_existing_key.model_dump.return_value = { + "token": test_hashed_token, + "user_id": "internal_user", + "team_id": None, + "max_budget": 10.0, + } + + mock_prisma_client.get_data = AsyncMock(return_value=mock_existing_key) + mock_prisma_client.db.litellm_verificationtoken.find_unique = AsyncMock(return_value=mock_existing_key) + + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + monkeypatch.setattr("litellm.proxy.proxy_server.user_api_key_cache", mock_user_api_key_cache) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_logging_obj", mock_proxy_logging_obj) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None) + monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True) + + mock_request = MagicMock() + mock_request.query_params = {} + user_api_key_dict = UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-internal", + user_id="internal_user", + ) + + with pytest.raises(ProxyException) as exc: + await update_key_fn( + request=mock_request, + data=UpdateKeyRequest( + key=test_hashed_token, + throttle_on_budget_exceeded=True, + ), + user_api_key_dict=user_api_key_dict, + litellm_changed_by=None, + ) + + assert str(exc.value.code) == "403" + assert "Only proxy admins can enable throttle_on_budget_exceeded" in str(exc.value.message) + + +@pytest.mark.asyncio +async def test_update_key_throttle_unchanged_allows_non_budget_edit_for_internal_user( + monkeypatch, +): + """A non-admin owner editing a non-budget field must not be blocked just + because the UI resends throttle_on_budget_exceeded unchanged (the edit form + always includes it). Only the transition to enabled is admin-gated, so an + unchanged False here leaves the key owner's non-budget edit working.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + update_key_fn, + ) + + mock_prisma_client = AsyncMock() + mock_user_api_key_cache = AsyncMock() + mock_proxy_logging_obj = MagicMock() + + test_hashed_token = "a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd" + + mock_existing_key = MagicMock() + mock_existing_key.token = test_hashed_token + mock_existing_key.user_id = "internal_user" + mock_existing_key.created_by = "internal_user" + mock_existing_key.team_id = None + mock_existing_key.project_id = None + mock_existing_key.max_budget = 10.0 + mock_existing_key.key_alias = None + mock_existing_key.models = [] + mock_existing_key.metadata = {"throttle_on_budget_exceeded": False} + mock_existing_key.model_dump.return_value = { + "token": test_hashed_token, + "user_id": "internal_user", + "team_id": None, + "max_budget": 10.0, + } + + mock_updated_key = MagicMock() + mock_updated_key.token = test_hashed_token + mock_updated_key.key_alias = "my-alias" + + mock_prisma_client.get_data = AsyncMock(return_value=mock_existing_key) + mock_prisma_client.update_data = AsyncMock(return_value=mock_updated_key) + mock_prisma_client.db.litellm_verificationtoken.find_unique = AsyncMock(return_value=mock_existing_key) + + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + monkeypatch.setattr("litellm.proxy.proxy_server.user_api_key_cache", mock_user_api_key_cache) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_logging_obj", mock_proxy_logging_obj) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None) + monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True) + monkeypatch.setattr("litellm.store_audit_logs", False) + + monkeypatch.setattr("litellm.proxy.proxy_server.hash_token", lambda token: test_hashed_token) + + async def _noop(**kwargs): + pass + + monkeypatch.setattr( + "litellm.proxy.management_endpoints.key_management_endpoints._delete_cache_key_object", + _noop, + ) + monkeypatch.setattr( + "litellm.proxy.management_endpoints.key_management_endpoints._enforce_unique_key_alias", + _noop, + ) + + mock_request = MagicMock() + mock_request.query_params = {} + user_api_key_dict = UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-internal", + user_id="internal_user", + ) + + result = await update_key_fn( + request=mock_request, + data=UpdateKeyRequest( + key=test_hashed_token, + key_alias="my-alias", + throttle_on_budget_exceeded=False, + ), + user_api_key_dict=user_api_key_dict, + litellm_changed_by=None, + ) + + assert result is not None + + @pytest.mark.asyncio async def test_update_key_non_budget_rejects_cross_user_modification(monkeypatch): """Regression: previously _check_key_admin_access was gated on @@ -10883,6 +11436,249 @@ class TestAllowedRoutesCallerPermission: assert str(exc_info.value.code) == "403" assert "allowed_routes" in str(exc_info.value.message) + @pytest.mark.asyncio + async def test_non_admin_update_key_explicit_empty_allowed_routes_rejected(self): + """`update_key_fn` rejects a non-admin when `allowed_routes` is + present as `[]` in the request body. The value matches the model + default but `model_fields_set` distinguishes the two.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + update_key_fn, + ) + + data = UpdateKeyRequest(key="sk-test", allowed_routes=[]) + assert "allowed_routes" in data.model_fields_set + user_api_key_dict = UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + mock_prisma_client = AsyncMock() + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), + patch("litellm.proxy.proxy_server.user_api_key_cache", MagicMock()), + patch("litellm.proxy.proxy_server.user_custom_key_update", None), + patch("litellm.proxy.proxy_server.llm_router", None), + patch("litellm.proxy.proxy_server.premium_user", True), + patch("litellm.proxy.proxy_server.proxy_logging_obj", MagicMock()), + patch( + "litellm.proxy.management_endpoints.key_management_endpoints._get_and_validate_existing_key", + new_callable=AsyncMock, + return_value=MagicMock(), + ), + ): + with pytest.raises(ProxyException) as exc_info: + await update_key_fn( + request=MagicMock(), + data=data, + user_api_key_dict=user_api_key_dict, + litellm_changed_by=None, + ) + assert str(exc_info.value.code) == "403" + assert "allowed_routes" in str(exc_info.value.message) + + @pytest.mark.asyncio + async def test_non_admin_update_key_explicit_null_allowed_routes_rejected(self): + """`update_key_fn` rejects a non-admin when `allowed_routes` is + present as `null` in the request body.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + update_key_fn, + ) + + data = UpdateKeyRequest(key="sk-test", allowed_routes=None) + assert "allowed_routes" in data.model_fields_set + user_api_key_dict = UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + mock_prisma_client = AsyncMock() + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), + patch("litellm.proxy.proxy_server.user_api_key_cache", MagicMock()), + patch("litellm.proxy.proxy_server.user_custom_key_update", None), + patch("litellm.proxy.proxy_server.llm_router", None), + patch("litellm.proxy.proxy_server.premium_user", True), + patch("litellm.proxy.proxy_server.proxy_logging_obj", MagicMock()), + patch( + "litellm.proxy.management_endpoints.key_management_endpoints._get_and_validate_existing_key", + new_callable=AsyncMock, + return_value=MagicMock(), + ), + ): + with pytest.raises(ProxyException) as exc_info: + await update_key_fn( + request=MagicMock(), + data=data, + user_api_key_dict=user_api_key_dict, + litellm_changed_by=None, + ) + assert str(exc_info.value.code) == "403" + assert "allowed_routes" in str(exc_info.value.message) + + @pytest.mark.asyncio + async def test_non_admin_regenerate_key_explicit_empty_allowed_routes_rejected(self): + """`regenerate_key_fn` rejects a non-admin when `allowed_routes` is + present as `[]` in the request body.""" + from litellm.proxy._types import RegenerateKeyRequest + from litellm.proxy.management_endpoints.key_management_endpoints import ( + regenerate_key_fn, + ) + + data = RegenerateKeyRequest(key="sk-test", allowed_routes=[]) + assert "allowed_routes" in data.model_fields_set + user_api_key_dict = UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + with patch("litellm.proxy.proxy_server.premium_user", True): + with pytest.raises(ProxyException) as exc_info: + await regenerate_key_fn( + key=None, + data=data, + user_api_key_dict=user_api_key_dict, + litellm_changed_by=None, + ) + assert str(exc_info.value.code) == "403" + assert "allowed_routes" in str(exc_info.value.message) + + @pytest.mark.asyncio + async def test_non_admin_regenerate_key_allowed_routes_rejected_before_enterprise_gate(self): + """`regenerate_key_fn` runs `_check_allowed_routes_caller_permission` + before the `premium_user` check, so a non-premium proxy still returns + the allowed_routes rejection (403) rather than the enterprise-license + error (500) when a non-admin sends `allowed_routes`.""" + from litellm.proxy._types import RegenerateKeyRequest + from litellm.proxy.management_endpoints.key_management_endpoints import ( + regenerate_key_fn, + ) + + data = RegenerateKeyRequest(key="sk-test", allowed_routes=["/*"]) + user_api_key_dict = UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + with patch("litellm.proxy.proxy_server.premium_user", False): + with pytest.raises(ProxyException) as exc_info: + await regenerate_key_fn( + key=None, + data=data, + user_api_key_dict=user_api_key_dict, + litellm_changed_by=None, + ) + assert str(exc_info.value.code) == "403" + assert "allowed_routes" in str(exc_info.value.message) + assert "Enterprise" not in str(exc_info.value.message) + + @pytest.mark.asyncio + async def test_non_admin_generate_key_explicit_empty_allowed_routes_rejected(self): + """`generate_key_fn` rejects a non-admin when `allowed_routes` is + present as `[]` in the request body. The value matches the model + default but `model_fields_set` distinguishes the two, so the + explicit-empty case on the create path is caught.""" + data = GenerateKeyRequest(key_alias="plain-key", allowed_routes=[]) + assert "allowed_routes" in data.model_fields_set + user_api_key_dict = UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + mock_prisma_client = AsyncMock() + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), + patch("litellm.proxy.proxy_server.user_api_key_cache", MagicMock()), + patch("litellm.proxy.proxy_server.user_custom_key_generate", None), + patch( + "litellm.proxy.management_endpoints.key_management_endpoints._common_key_generation_helper", + new_callable=AsyncMock, + return_value=MagicMock(), + ), + ): + with pytest.raises(ProxyException) as exc_info: + await generate_key_fn( + data=data, + user_api_key_dict=user_api_key_dict, + litellm_changed_by=None, + ) + assert str(exc_info.value.code) == "403" + assert "allowed_routes" in str(exc_info.value.message) + + def test_helper_accepts_derived_safe_preset_for_non_admin(self): + """`_check_allowed_routes_caller_permission` accepts a non-admin + when `allow_safe_presets=True` and `allowed_routes` is entirely + composed of `_NON_ADMIN_SAFE_ALLOWED_ROUTES_PRESETS` tokens. This + is the shape the post-`handle_key_type` recheck at line 914 uses + after deriving `["llm_api_routes"]` from `key_type=llm_api`.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _check_allowed_routes_caller_permission, + ) + + _check_allowed_routes_caller_permission( + allowed_routes=["llm_api_routes"], + user_api_key_dict=UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ), + allow_safe_presets=True, + ) + _check_allowed_routes_caller_permission( + allowed_routes=["info_routes"], + user_api_key_dict=UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ), + allow_safe_presets=True, + ) + + def test_helper_rejects_derived_unsafe_preset_for_non_admin(self): + """`_check_allowed_routes_caller_permission` rejects a non-admin + when `allow_safe_presets=True` but the derived value is outside + `_NON_ADMIN_SAFE_ALLOWED_ROUTES_PRESETS` (for example + `["management_routes"]` from `key_type=management`).""" + from fastapi import HTTPException + + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _check_allowed_routes_caller_permission, + ) + + with pytest.raises(HTTPException) as exc_info: + _check_allowed_routes_caller_permission( + allowed_routes=["management_routes"], + user_api_key_dict=UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ), + allow_safe_presets=True, + ) + assert exc_info.value.status_code == 403 + assert "allowed_routes" in str(exc_info.value.detail) + + def test_helper_rejects_when_provided_and_none_without_typeerror(self): + """`_check_allowed_routes_caller_permission` returns a 403 (not a + TypeError from iterating `None`) when a caller ever combines + `allowed_routes_was_provided=True` with `allowed_routes=None` and + `allow_safe_presets=True`. Pins the `and allowed_routes` guard on + the safe-preset branch.""" + from fastapi import HTTPException + + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _check_allowed_routes_caller_permission, + ) + + with pytest.raises(HTTPException) as exc_info: + _check_allowed_routes_caller_permission( + allowed_routes=None, + user_api_key_dict=UserAPIKeyAuth( + user_id="internal-user-123", + user_role=LitellmUserRoles.INTERNAL_USER, + ), + allowed_routes_was_provided=True, + allow_safe_presets=True, + ) + assert exc_info.value.status_code == 403 + assert "allowed_routes" in str(exc_info.value.detail) + def test_jinja_prompt_manager_is_sandboxed(): """ @@ -11006,6 +11802,67 @@ async def test_process_single_key_update_cache_invalidation_with_token_hash(): assert call_kwargs["hashed_token"] == token_hash +@pytest.mark.asyncio +async def test_process_single_key_update_non_admin_permissions_rejected(): + """`_process_single_key_update` rejects a non-admin when `permissions` + is present in the constructed `UpdateKeyRequest`. Guards the bulk path + against a future widening of the `BulkUpdateKeyRequestItem` or + `KeyUpdateFields` allowlists reopening the class.""" + update_key_request = UpdateKeyRequest( + key="abc123", + permissions={"get_spend_routes": True}, + ) + assert "permissions" in update_key_request.model_fields_set + + non_admin = UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-non-admin", + user_id="user-1", + ) + + with pytest.raises(HTTPException) as exc_info: + await _process_single_key_update( + update_key_request=update_key_request, + user_api_key_dict=non_admin, + litellm_changed_by=None, + prisma_client=AsyncMock(), + user_api_key_cache=MagicMock(), + proxy_logging_obj=MagicMock(), + llm_router=MagicMock(), + ) + assert exc_info.value.status_code == 403 + assert "permissions" in str(exc_info.value.detail) + + +@pytest.mark.asyncio +async def test_process_single_key_update_non_admin_permissions_explicit_empty_rejected(): + """`_process_single_key_update` rejects a non-admin when `permissions` + is present as `{}` in the constructed `UpdateKeyRequest`. Presence + check on `model_fields_set` catches the explicit-empty case the same + as any other value.""" + update_key_request = UpdateKeyRequest(key="abc123", permissions={}) + assert "permissions" in update_key_request.model_fields_set + + non_admin = UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-non-admin", + user_id="user-1", + ) + + with pytest.raises(HTTPException) as exc_info: + await _process_single_key_update( + update_key_request=update_key_request, + user_api_key_dict=non_admin, + litellm_changed_by=None, + prisma_client=AsyncMock(), + user_api_key_cache=MagicMock(), + proxy_logging_obj=MagicMock(), + llm_router=MagicMock(), + ) + assert exc_info.value.status_code == 403 + assert "permissions" in str(exc_info.value.detail) + + @pytest.mark.asyncio async def test_execute_virtual_key_regeneration_cache_invalidation_with_token_hash(): """ @@ -13386,3 +14243,551 @@ async def test_permissions_admin_can_set_any(monkeypatch): team_table=None, ) assert result is not None + + +@pytest.mark.asyncio +async def test_permissions_explicit_empty_rejected_for_non_admin_on_generate(monkeypatch): + """`_common_key_generation_helper` rejects a non-admin when + `permissions` is present in the request body, even as `{}`. Omit-default + stays allowed; that carve-out lives in + `test_permissions_empty_default_allowed_for_non_admin`.""" + monkeypatch.setattr( + "litellm.proxy.management_endpoints.key_management_endpoints.litellm.default_key_generate_params", + None, + raising=False, + ) + caller = UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + user_id="user-1", + max_budget=100.0, + ) + request = GenerateKeyRequest(permissions={}) + assert "permissions" in request.model_fields_set + with pytest.raises(HTTPException) as exc_info: + await _common_key_generation_helper( + data=request, + user_api_key_dict=caller, + litellm_changed_by=None, + team_table=None, + ) + assert exc_info.value.status_code == 403 + assert "permissions" in str(exc_info.value.detail) + + +def _make_personal_key_row_for_alice(): + return MagicMock( + token="hashed_alice_personal_key", + user_id="alice", + team_id=None, + created_by="alice", + max_budget=None, + organization_id=None, + project_id=None, + ) + + +def _make_alice_internal_user(): + return UserAPIKeyAuth( + user_role=LitellmUserRoles.INTERNAL_USER, + api_key="sk-alice", + user_id="alice", + ) + + +@pytest.mark.asyncio +async def test_update_key_non_admin_permissions_non_empty_rejected(monkeypatch): + """`_validate_update_key_data` rejects a non-admin when `permissions` + is present in the request body (personal-key fast-path caller).""" + mock_prisma_client = AsyncMock() + mock_prisma_client.jsonify_object = lambda data: data + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + data = UpdateKeyRequest( + key="sk-alice-personal", + permissions={"get_spend_routes": True}, + ) + + with pytest.raises(HTTPException) as exc: + await _validate_update_key_data( + data=data, + existing_key_row=_make_personal_key_row_for_alice(), + user_api_key_dict=_make_alice_internal_user(), + llm_router=None, + premium_user=True, + prisma_client=mock_prisma_client, + user_api_key_cache=MagicMock(), + ) + assert exc.value.status_code == 403 + assert "permissions" in str(exc.value.detail) + + +@pytest.mark.asyncio +async def test_update_key_non_admin_permissions_explicit_empty_rejected(monkeypatch): + """`_validate_update_key_data` rejects a non-admin when `permissions` + is present as `{}` in the request body. The value matches the model + default but `model_fields_set` distinguishes the two.""" + mock_prisma_client = AsyncMock() + mock_prisma_client.jsonify_object = lambda data: data + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + data = UpdateKeyRequest( + key="sk-alice-personal", + permissions={}, + ) + assert "permissions" in data.model_fields_set + + with pytest.raises(HTTPException) as exc: + await _validate_update_key_data( + data=data, + existing_key_row=_make_personal_key_row_for_alice(), + user_api_key_dict=_make_alice_internal_user(), + llm_router=None, + premium_user=True, + prisma_client=mock_prisma_client, + user_api_key_cache=MagicMock(), + ) + assert exc.value.status_code == 403 + assert "permissions" in str(exc.value.detail) + + +@pytest.mark.asyncio +async def test_update_key_non_admin_permissions_explicit_null_rejected(monkeypatch): + """`_validate_update_key_data` rejects a non-admin when `permissions` + is present as `null` in the request body.""" + mock_prisma_client = AsyncMock() + mock_prisma_client.jsonify_object = lambda data: data + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + data = UpdateKeyRequest( + key="sk-alice-personal", + permissions=None, + ) + assert "permissions" in data.model_fields_set + + with pytest.raises(HTTPException) as exc: + await _validate_update_key_data( + data=data, + existing_key_row=_make_personal_key_row_for_alice(), + user_api_key_dict=_make_alice_internal_user(), + llm_router=None, + premium_user=True, + prisma_client=mock_prisma_client, + user_api_key_cache=MagicMock(), + ) + assert exc.value.status_code == 403 + assert "permissions" in str(exc.value.detail) + + +@pytest.mark.asyncio +async def test_update_key_non_admin_omits_permissions_succeeds(monkeypatch): + """`_validate_update_key_data` accepts a non-admin owner when + `permissions` is absent from the request body (personal-key fast path + on an unrelated field).""" + mock_prisma_client = AsyncMock() + mock_prisma_client.jsonify_object = lambda data: data + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + data = UpdateKeyRequest(key="sk-alice-personal", tpm_limit=42) + assert "permissions" not in data.model_fields_set + + await _validate_update_key_data( + data=data, + existing_key_row=_make_personal_key_row_for_alice(), + user_api_key_dict=_make_alice_internal_user(), + llm_router=None, + premium_user=True, + prisma_client=mock_prisma_client, + user_api_key_cache=MagicMock(), + ) + + +@pytest.mark.asyncio +async def test_update_key_admin_can_set_permissions(monkeypatch): + """`_validate_update_key_data` accepts a PROXY_ADMIN caller for every + shape of `permissions` in the request body.""" + mock_prisma_client = AsyncMock() + mock_prisma_client.jsonify_object = lambda data: data + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + admin = UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, + api_key="sk-admin", + user_id="admin-1", + ) + for permissions_value in ({"get_spend_routes": True}, {}, None): + data = UpdateKeyRequest( + key="sk-alice-personal", + permissions=permissions_value, + ) + await _validate_update_key_data( + data=data, + existing_key_row=_make_personal_key_row_for_alice(), + user_api_key_dict=admin, + llm_router=None, + premium_user=True, + prisma_client=mock_prisma_client, + user_api_key_cache=MagicMock(), + ) + + +@pytest.mark.asyncio +async def test_regenerate_key_non_admin_permissions_rejected(monkeypatch): + """`regenerate_key_fn` rejects a non-admin when `permissions` is + present in the request body, before any DB work.""" + from litellm.proxy._types import RegenerateKeyRequest + from litellm.proxy.management_endpoints.key_management_endpoints import ( + regenerate_key_fn, + ) + + monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True) + + data = RegenerateKeyRequest( + key="sk-alice-personal", + permissions={"get_spend_routes": True}, + ) + + with pytest.raises(ProxyException) as exc: + await regenerate_key_fn( + key=None, + data=data, + user_api_key_dict=_make_alice_internal_user(), + litellm_changed_by=None, + ) + assert int(exc.value.code) == 403 + assert "permissions" in str(exc.value.message) + + +@pytest.mark.asyncio +async def test_regenerate_key_non_admin_permissions_explicit_empty_rejected(monkeypatch): + """`regenerate_key_fn` rejects a non-admin when `permissions` is + present as `{}` in the request body.""" + from litellm.proxy._types import RegenerateKeyRequest + from litellm.proxy.management_endpoints.key_management_endpoints import ( + regenerate_key_fn, + ) + + monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True) + + data = RegenerateKeyRequest(key="sk-alice-personal", permissions={}) + assert "permissions" in data.model_fields_set + + with pytest.raises(ProxyException) as exc: + await regenerate_key_fn( + key=None, + data=data, + user_api_key_dict=_make_alice_internal_user(), + litellm_changed_by=None, + ) + assert int(exc.value.code) == 403 + assert "permissions" in str(exc.value.message) + + +@pytest.mark.asyncio +async def test_regenerate_key_non_admin_permissions_rejected_before_enterprise_gate(monkeypatch): + """`regenerate_key_fn` runs `_check_permissions_caller_permission` + before the `premium_user` check, so a non-premium proxy still returns + the permissions rejection (403) rather than the enterprise-license + error (500) when a non-admin sends `permissions`.""" + from litellm.proxy._types import RegenerateKeyRequest + from litellm.proxy.management_endpoints.key_management_endpoints import ( + regenerate_key_fn, + ) + + monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", False) + + data = RegenerateKeyRequest( + key="sk-alice-personal", + permissions={"get_spend_routes": True}, + ) + + with pytest.raises(ProxyException) as exc: + await regenerate_key_fn( + key=None, + data=data, + user_api_key_dict=_make_alice_internal_user(), + litellm_changed_by=None, + ) + assert int(exc.value.code) == 403 + assert "permissions" in str(exc.value.message) + assert "Enterprise" not in str(exc.value.message) + + +def test_generate_key_helper_fn_accepts_per_tag_rate_limits(): + """ + Regression: new_user / SSO sign-in forward NewUserRequest fields to + generate_key_helper_fn via `**data_json`. The per-tag limit field must be + an accepted kwarg, otherwise user creation 500s with + "generate_key_helper_fn() got an unexpected keyword argument 'tag_rpm_limit'". + """ + params = inspect.signature(generate_key_helper_fn).parameters + assert "tag_rpm_limit" in params + + # The field exists on the request model that new_user forwards via **data_json. + assert "tag_rpm_limit" in NewUserRequest.model_fields + + # Binding the per-tag kwarg must not raise an unexpected-keyword TypeError. + inspect.signature(generate_key_helper_fn).bind_partial( + request_type="user", + tag_rpm_limit={"cell-1": 5}, + ) + + +def _find_expires_clauses(node): + """Recursively collect every value keyed 'expires' anywhere in a Prisma where dict.""" + found = [] + if isinstance(node, dict): + for key, value in node.items(): + if key == "expires": + found.append(value) + else: + found.extend(_find_expires_clauses(value)) + elif isinstance(node, list): + for item in node: + found.extend(_find_expires_clauses(item)) + return found + + +def test_build_expires_where_clause_expired_shape(): + """'expired' must exclude never-expiring (NULL) keys and match expires < now.""" + from datetime import datetime, timezone + + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _build_expires_where_clause, + ) + + now = datetime(2026, 1, 2, 3, 4, 5, tzinfo=timezone.utc) + assert _build_expires_where_clause("expired", now) == { + "AND": [{"expires": {"not": None}}, {"expires": {"lt": now}}] + } + + +def test_build_expires_where_clause_active_shape(): + """'active' must include never-expiring (NULL) keys and match expires >= now.""" + from datetime import datetime, timezone + + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _build_expires_where_clause, + ) + + now = datetime(2026, 1, 2, 3, 4, 5, tzinfo=timezone.utc) + assert _build_expires_where_clause("active", now) == { + "OR": [{"expires": None}, {"expires": {"gte": now}}] + } + + +def test_build_key_filter_conditions_expired_applies_lt_clause(): + """expires_filter='expired' ANDs in a not-NULL + lt(now) constraint.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _build_key_filter_conditions, + ) + + where = _build_key_filter_conditions( + user_id="u1", + team_id=None, + organization_id=None, + key_alias=None, + key_hash=None, + exclude_team_id=None, + admin_team_ids=None, + expires_filter="expired", + ) + + clauses = _find_expires_clauses(where) + assert {"not": None} in clauses + lt_clauses = [c for c in clauses if isinstance(c, dict) and "lt" in c] + assert len(lt_clauses) == 1 + assert "gte" not in str(clauses) + + +def test_build_key_filter_conditions_active_applies_gte_and_null(): + """expires_filter='active' ANDs in a NULL-or-gte(now) constraint.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _build_key_filter_conditions, + ) + + where = _build_key_filter_conditions( + user_id="u1", + team_id=None, + organization_id=None, + key_alias=None, + key_hash=None, + exclude_team_id=None, + admin_team_ids=None, + expires_filter="active", + ) + + clauses = _find_expires_clauses(where) + assert None in clauses + gte_clauses = [c for c in clauses if isinstance(c, dict) and "gte" in c] + assert len(gte_clauses) == 1 + assert "lt" not in str(clauses) + + +def test_build_key_filter_conditions_no_expires_filter_omits_clause(): + """Default (no expires_filter) must not add any expires constraint — preserves existing callers.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _build_key_filter_conditions, + ) + + where = _build_key_filter_conditions( + user_id="u1", + team_id=None, + organization_id=None, + key_alias=None, + key_hash=None, + exclude_team_id=None, + admin_team_ids=None, + ) + + assert _find_expires_clauses(where) == [] + + +def test_build_key_filter_conditions_invalid_expires_filter_omits_clause(): + """An unrecognized expires_filter value is ignored, not applied blindly.""" + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _build_key_filter_conditions, + ) + + where = _build_key_filter_conditions( + user_id="u1", + team_id=None, + organization_id=None, + key_alias=None, + key_hash=None, + exclude_team_id=None, + admin_team_ids=None, + expires_filter="garbage", + ) + + assert _find_expires_clauses(where) == [] + + +def test_build_key_filter_conditions_expires_now_is_call_time_utc(): + """The lt(now) boundary is computed at call time as a tz-aware UTC datetime.""" + from datetime import datetime, timezone + + from litellm.proxy.management_endpoints.key_management_endpoints import ( + _build_key_filter_conditions, + ) + + before = datetime.now(timezone.utc) + where = _build_key_filter_conditions( + user_id="u1", + team_id=None, + organization_id=None, + key_alias=None, + key_hash=None, + exclude_team_id=None, + admin_team_ids=None, + expires_filter="expired", + ) + after = datetime.now(timezone.utc) + + lt_values = [c["lt"] for c in _find_expires_clauses(where) if isinstance(c, dict) and "lt" in c] + assert len(lt_values) == 1 + now_value = lt_values[0] + assert now_value.tzinfo is not None + assert now_value.utcoffset().total_seconds() == 0 + assert before <= now_value <= after + + +@pytest.mark.asyncio +async def test_list_keys_rejects_invalid_expires(): + """A typo'd expires value must 400, never silently fall back to returning all keys.""" + from unittest.mock import Mock, patch + + mock_prisma_client = AsyncMock() + mock_user_api_key_dict = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN) + + with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client): + with pytest.raises(ProxyException) as exc_info: + await list_keys( + request=Mock(), + user_api_key_dict=mock_user_api_key_dict, + status=None, + expires="expred", + ) + + assert exc_info.value.code == "400" + assert "Invalid expires value" in str(exc_info.value.message) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "expires_value, expected_forward", + [("expired", "expired"), ("active", "active"), (None, None)], +) +async def test_list_keys_forwards_expires_filter(expires_value, expected_forward): + """list_keys forwards a valid/None expires value verbatim to _list_key_helper as expires_filter.""" + from unittest.mock import Mock, patch + + mock_prisma_client = AsyncMock() + mock_user_api_key_dict = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN) + mock_user_info = LiteLLM_UserTable( + user_id="admin-user", + user_email="admin@example.com", + teams=[], + organization_memberships=[], + ) + mock_helper = AsyncMock( + return_value={"keys": [], "total_count": 0, "current_page": 1, "total_pages": 0} + ) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), + patch( + "litellm.proxy.management_endpoints.key_management_endpoints.validate_key_list_check", + return_value=mock_user_info, + ), + patch( + "litellm.proxy.management_endpoints.key_management_endpoints._list_key_helper", + mock_helper, + ), + ): + await list_keys( + request=Mock(), + user_api_key_dict=mock_user_api_key_dict, + status=None, + expires=expires_value, + ) + + mock_helper.assert_called_once() + assert mock_helper.call_args.kwargs["expires_filter"] == expected_forward + + +@pytest.mark.asyncio +async def test_list_keys_without_expires_param_forwards_none(): + """Existing callers that never pass `expires` must not 400 and must forward expires_filter=None.""" + from unittest.mock import Mock, patch + + mock_prisma_client = AsyncMock() + mock_user_api_key_dict = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN) + mock_user_info = LiteLLM_UserTable( + user_id="admin-user", + user_email="admin@example.com", + teams=[], + organization_memberships=[], + ) + mock_helper = AsyncMock( + return_value={"keys": [], "total_count": 0, "current_page": 1, "total_pages": 0} + ) + + with ( + patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), + patch( + "litellm.proxy.management_endpoints.key_management_endpoints.validate_key_list_check", + return_value=mock_user_info, + ), + patch( + "litellm.proxy.management_endpoints.key_management_endpoints._list_key_helper", + mock_helper, + ), + ): + await list_keys( + request=Mock(), + user_api_key_dict=mock_user_api_key_dict, + status=None, + ) + + mock_helper.assert_called_once() + assert mock_helper.call_args.kwargs["expires_filter"] is None diff --git a/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py index f40904e234d..a669a277d2b 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py @@ -16,9 +16,7 @@ from litellm.proxy.management_endpoints import ( mcp_management_endpoints as mgmt_endpoints, ) -sys.path.insert( - 0, os.path.abspath("../../../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../../..")) # Adds the parent directory to the system path from litellm.proxy._types import ( LiteLLM_MCPServerTable, @@ -101,9 +99,7 @@ def generate_mock_user_api_key_auth( ) -def generate_mock_team_record( - team_id: str, team_alias: str, organization_id: str, mcp_servers: List[str] -): +def generate_mock_team_record(team_id: str, team_alias: str, organization_id: str, mcp_servers: List[str]): """Generate a mock team record with object permissions""" return MagicMock( team_id=team_id, @@ -122,13 +118,9 @@ def setup_mock_prisma_client( """Helper to set up a mock prisma client with proper async behavior""" mock_prisma_client.db = MagicMock() mock_prisma_client.db.litellm_teamtable = AsyncMock() - mock_prisma_client.db.litellm_teamtable.find_many = AsyncMock( - return_value=team_records - ) + mock_prisma_client.db.litellm_teamtable.find_many = AsyncMock(return_value=team_records) mock_prisma_client.db.litellm_mcpservertable = AsyncMock() - mock_prisma_client.db.litellm_mcpservertable.find_many = AsyncMock( - return_value=mcp_servers - ) + mock_prisma_client.db.litellm_mcpservertable.find_many = AsyncMock(return_value=mcp_servers) return mock_prisma_client @@ -148,6 +140,34 @@ def patch_proxy_general_settings(settings: dict): ) +class TestMCPCredentialsTokenExchangeProfile: + """token_exchange_profile must be a declared MCPCredentials field so the management API can + persist the entra_obo profile. An undeclared key is silently stripped by pydantic when the + credentials dict is validated against the TypedDict, so it would never reach the JSON blob.""" + + @pytest.mark.parametrize( + "build", + [ + lambda creds: NewMCPServerRequest( + server_name="s", auth_type=MCPAuth.oauth2_token_exchange, credentials=creds + ), + lambda creds: UpdateMCPServerRequest(server_id="s", credentials=creds), + ], + ids=["new", "update"], + ) + def test_request_preserves_token_exchange_profile_in_credentials(self, build): + creds = { + "client_id": "cid", + "client_secret": "sec", + "token_exchange_endpoint": "https://login.microsoftonline.com/tid/oauth2/v2.0/token", + "scopes": ["api://target/.default"], + "token_exchange_profile": "entra_obo", + } + request = build(creds) + assert request.credentials is not None + assert request.credentials.get("token_exchange_profile") == "entra_obo" + + class TestListMCPServers: """Test suite for list MCP servers functionality""" @@ -195,9 +215,7 @@ class TestListMCPServers: "config_server_1": config_server_1, "config_server_2": config_server_2, } - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["config_server_1", "config_server_2"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["config_server_1", "config_server_2"]) # Mock the new method that returns servers without health check mock_servers = [ @@ -268,9 +286,7 @@ class TestListMCPServers: async def test_list_mcp_servers_view_all_mode(self): """Users should see all MCP servers when view_all mode is enabled.""" - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.INTERNAL_USER - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.INTERNAL_USER) mock_servers = [ generate_mock_mcp_server_db_record(server_id="server-1", alias="One"), @@ -278,9 +294,7 @@ class TestListMCPServers: ] mock_manager = MagicMock() - mock_manager.get_all_mcp_servers_unfiltered = AsyncMock( - return_value=mock_servers - ) + mock_manager.get_all_mcp_servers_unfiltered = AsyncMock(return_value=mock_servers) with ( patch( @@ -327,9 +341,7 @@ class TestListMCPServers: server.extra_headers = ["Authorization"] mock_manager = MagicMock() - mock_manager.get_all_mcp_servers_unfiltered = AsyncMock( - return_value=mock_servers - ) + mock_manager.get_all_mcp_servers_unfiltered = AsyncMock(return_value=mock_servers) mock_manager.get_all_allowed_mcp_servers = AsyncMock(return_value=mock_servers) with ( @@ -568,14 +580,10 @@ class TestListMCPServers: mock_manager = MagicMock() mock_manager.config_mcp_servers = { "config_server_allowed": config_server_allowed, - "config_server_not_allowed": generate_mock_mcp_server_config_record( - server_id="config_server_not_allowed" - ), + "config_server_not_allowed": generate_mock_mcp_server_config_record(server_id="config_server_not_allowed"), } # User only has access to specific servers - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["db_server_allowed", "config_server_allowed"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["db_server_allowed", "config_server_allowed"]) # Mock the new method that returns servers without health check mock_servers = [ @@ -678,9 +686,7 @@ class TestListMCPServers: # Mock manager mock_manager = MagicMock() - mock_manager.get_all_allowed_mcp_servers = AsyncMock( - return_value=[server_1, server_2] - ) + mock_manager.get_all_allowed_mcp_servers = AsyncMock(return_value=[server_1, server_2]) with ( patch( @@ -708,24 +714,18 @@ class TestListMCPServers: @pytest.mark.asyncio async def test_fetch_single_mcp_server_redacts_credentials(self): - mock_server = generate_mock_mcp_server_db_record( - server_id="server-1", alias="Server 1" - ) + mock_server = generate_mock_mcp_server_db_record(server_id="server-1", alias="Server 1") mock_server.credentials = {"auth_value": "top-secret"} mock_prisma_client = MagicMock() # Mock health check result as LiteLLM_MCPServerTable - mock_health_result = generate_mock_mcp_server_db_record( - server_id="server-1", alias="Server 1" - ) + mock_health_result = generate_mock_mcp_server_db_record(server_id="server-1", alias="Server 1") mock_health_result.status = "healthy" mock_health_result.last_health_check = datetime.now() mock_health_result.health_check_error = None - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) with ( patch( @@ -762,25 +762,19 @@ class TestListMCPServers: @pytest.mark.asyncio async def test_fetch_single_mcp_server_handles_missing_credentials_field(self): - mock_server = generate_mock_mcp_server_db_record( - server_id="server-2", alias="Server 2" - ) + mock_server = generate_mock_mcp_server_db_record(server_id="server-2", alias="Server 2") # Simulate ORM object without credentials attribute (e.g., older schema) delattr(mock_server, "credentials") mock_prisma_client = MagicMock() # Mock health check result as LiteLLM_MCPServerTable - mock_health_result = generate_mock_mcp_server_db_record( - server_id="server-2", alias="Server 2" - ) + mock_health_result = generate_mock_mcp_server_db_record(server_id="server-2", alias="Server 2") mock_health_result.status = "healthy" mock_health_result.last_health_check = datetime.now() mock_health_result.health_check_error = None - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) with ( patch( @@ -828,18 +822,14 @@ class TestListMCPServers: transport="http", ) - mock_health_result = generate_mock_mcp_server_db_record( - server_id="serper_custom_dev", alias="Serper MCP" - ) + mock_health_result = generate_mock_mcp_server_db_record(server_id="serper_custom_dev", alias="Serper MCP") mock_health_result.status = "healthy" mock_health_result.last_health_check = datetime.now() mock_health_result.health_check_error = None mock_manager = MagicMock() mock_manager.get_mcp_server_by_id = MagicMock( - side_effect=lambda sid: ( - config_server if sid == "serper_custom_dev" else None - ) + side_effect=lambda sid: config_server if sid == "serper_custom_dev" else None ) mock_manager.get_mcp_server_by_name = MagicMock(return_value=None) mock_manager._build_mcp_server_table = MagicMock( @@ -850,14 +840,10 @@ class TestListMCPServers: transport="http", ) ) - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["serper_custom_dev"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["serper_custom_dev"]) mock_manager.health_check_server = AsyncMock(return_value=mock_health_result) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) with ( patch( @@ -916,18 +902,12 @@ class TestListMCPServers: transport="http", ) ) - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["serper_custom_dev"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["serper_custom_dev"]) mock_manager.health_check_server = AsyncMock( - return_value=generate_mock_mcp_server_db_record( - server_id="serper_custom_dev", alias="Serper MCP" - ) + return_value=generate_mock_mcp_server_db_record(server_id="serper_custom_dev", alias="Serper MCP") ) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) with ( patch( @@ -959,9 +939,7 @@ class TestListMCPServers: assert result.server_id == "serper_custom_dev" mock_manager.get_mcp_server_by_id.assert_called_with("Serper MCP") - mock_manager.get_mcp_server_by_name.assert_called_once_with( - "Serper MCP", client_ip="192.168.1.100" - ) + mock_manager.get_mcp_server_by_name.assert_called_once_with("Serper MCP", client_ip="192.168.1.100") @pytest.mark.asyncio async def test_fetch_single_mcp_server_from_registry_non_admin_denied(self): @@ -977,9 +955,7 @@ class TestListMCPServers: mock_manager = MagicMock() mock_manager.get_mcp_server_by_id = MagicMock( - side_effect=lambda sid: ( - config_server if sid == "restricted_server" else None - ) + side_effect=lambda sid: config_server if sid == "restricted_server" else None ) mock_manager.get_mcp_server_by_name = MagicMock(return_value=None) mock_manager._build_mcp_server_table = MagicMock( @@ -994,9 +970,7 @@ class TestListMCPServers: return_value=["other_server"] # restricted_server NOT in list ) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.INTERNAL_USER - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.INTERNAL_USER) with ( patch( @@ -1042,18 +1016,14 @@ class TestListMCPServers: transport="http", ) - mock_health_result = generate_mock_mcp_server_db_record( - server_id="allowed_config_server", alias="Allowed MCP" - ) + mock_health_result = generate_mock_mcp_server_db_record(server_id="allowed_config_server", alias="Allowed MCP") mock_health_result.status = "healthy" mock_health_result.last_health_check = datetime.now() mock_health_result.health_check_error = None mock_manager = MagicMock() mock_manager.get_mcp_server_by_id = MagicMock( - side_effect=lambda sid: ( - config_server if sid == "allowed_config_server" else None - ) + side_effect=lambda sid: config_server if sid == "allowed_config_server" else None ) mock_manager.get_mcp_server_by_name = MagicMock(return_value=None) mock_manager._build_mcp_server_table = MagicMock( @@ -1064,14 +1034,10 @@ class TestListMCPServers: transport="http", ) ) - mock_manager.get_allowed_mcp_servers = AsyncMock( - return_value=["allowed_config_server"] - ) + mock_manager.get_allowed_mcp_servers = AsyncMock(return_value=["allowed_config_server"]) mock_manager.health_check_server = AsyncMock(return_value=mock_health_result) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.INTERNAL_USER - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.INTERNAL_USER) with ( patch( @@ -1142,13 +1108,9 @@ class TestListMCPServers: assert isinstance(raw_prisma_model.env_vars[0], dict) mock_prisma_client = MagicMock() - mock_prisma_client.db.litellm_mcpservertable.find_unique = AsyncMock( - return_value=raw_prisma_model - ) + mock_prisma_client.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=raw_prisma_model) - mock_health_result = generate_mock_mcp_server_db_record( - server_id="env-server", alias="Env Server" - ) + mock_health_result = generate_mock_mcp_server_db_record(server_id="env-server", alias="Env Server") mock_health_result.status = "healthy" mock_health_result.last_health_check = datetime.now() mock_health_result.health_check_error = None @@ -1157,9 +1119,7 @@ class TestListMCPServers: mock_manager.add_server = AsyncMock() mock_manager.health_check_server = AsyncMock(return_value=mock_health_result) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.INTERNAL_USER - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.INTERNAL_USER) with ( patch( @@ -1172,11 +1132,7 @@ class TestListMCPServers: ), patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.get_all_mcp_servers_for_user", - AsyncMock( - return_value=[ - generate_mock_mcp_server_db_record(server_id="env-server") - ] - ), + AsyncMock(return_value=[generate_mock_mcp_server_db_record(server_id="env-server")]), ), patch( "litellm.proxy.management_endpoints.mcp_management_endpoints._user_has_admin_view", @@ -1221,9 +1177,7 @@ class TestListMCPServers: mock_prisma_client = MagicMock() - mock_health_result = generate_mock_mcp_server_db_record( - server_id="leaky-server", alias="Leaky Server" - ) + mock_health_result = generate_mock_mcp_server_db_record(server_id="leaky-server", alias="Leaky Server") mock_health_result.status = "healthy" mock_health_result.last_health_check = datetime.now() mock_health_result.health_check_error = None @@ -1232,9 +1186,7 @@ class TestListMCPServers: mock_manager.add_server = AsyncMock() mock_manager.health_check_server = AsyncMock(return_value=mock_health_result) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY) with ( patch( @@ -1283,9 +1235,7 @@ class TestListMCPServers: mock_prisma_client = MagicMock() - mock_health_result = generate_mock_mcp_server_db_record( - server_id="admin-server", alias="Admin Server" - ) + mock_health_result = generate_mock_mcp_server_db_record(server_id="admin-server", alias="Admin Server") mock_health_result.status = "healthy" mock_health_result.last_health_check = datetime.now() mock_health_result.health_check_error = None @@ -1294,9 +1244,7 @@ class TestListMCPServers: mock_manager.add_server = AsyncMock() mock_manager.health_check_server = AsyncMock(return_value=mock_health_result) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) with ( patch( @@ -1362,9 +1310,7 @@ class TestTeamScopedMCPServerAccess: ) with pytest.raises(HTTPException) as exc_info: - await fetch_all_mcp_servers( - user_api_key_dict=mock_user_auth, team_id="foreign-team-id" - ) + await fetch_all_mcp_servers(user_api_key_dict=mock_user_auth, team_id="foreign-team-id") assert exc_info.value.status_code == 403 assert "permission" in str(exc_info.value.detail).lower() @@ -1384,9 +1330,7 @@ class TestTeamScopedMCPServerAccess: ] mock_team_obj.object_permission = MagicMock(mcp_servers=["server-1"]) - mock_server = generate_mock_mcp_server_config_record( - server_id="server-1", name="Team Server" - ) + mock_server = generate_mock_mcp_server_config_record(server_id="server-1", name="Team Server") mock_manager = MagicMock() mock_manager.get_mcp_server_by_id = MagicMock(return_value=mock_server) mock_manager._build_mcp_server_table = MagicMock( @@ -1404,11 +1348,7 @@ class TestTeamScopedMCPServerAccess: ), patch( "litellm.proxy.management_endpoints.mcp_management_endpoints._get_team_scoped_mcp_server_list", - AsyncMock( - return_value=[ - generate_mock_mcp_server_db_record(server_id="server-1") - ] - ), + AsyncMock(return_value=[generate_mock_mcp_server_db_record(server_id="server-1")]), ), patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.global_mcp_server_manager", @@ -1419,9 +1359,7 @@ class TestTeamScopedMCPServerAccess: fetch_all_mcp_servers, ) - result = await fetch_all_mcp_servers( - user_api_key_dict=mock_user_auth, team_id="my-team-id" - ) + result = await fetch_all_mcp_servers(user_api_key_dict=mock_user_auth, team_id="my-team-id") assert len(result) == 1 assert result[0].server_id == "server-1" @@ -1440,11 +1378,7 @@ class TestTeamScopedMCPServerAccess: ), patch( "litellm.proxy.management_endpoints.mcp_management_endpoints._get_team_scoped_mcp_server_list", - AsyncMock( - return_value=[ - generate_mock_mcp_server_db_record(server_id="server-1") - ] - ), + AsyncMock(return_value=[generate_mock_mcp_server_db_record(server_id="server-1")]), ), ): from litellm.proxy.management_endpoints.mcp_management_endpoints import ( @@ -1452,9 +1386,7 @@ class TestTeamScopedMCPServerAccess: ) # Admin should NOT need to be a team member - result = await fetch_all_mcp_servers( - user_api_key_dict=mock_user_auth, team_id="any-team-id" - ) + result = await fetch_all_mcp_servers(user_api_key_dict=mock_user_auth, team_id="any-team-id") assert len(result) == 1 @pytest.mark.asyncio @@ -1472,9 +1404,7 @@ class TestTeamScopedMCPServerAccess: ) with pytest.raises(HTTPException) as exc_info: - await fetch_all_mcp_servers( - user_api_key_dict=mock_user_auth, team_id="some-team" - ) + await fetch_all_mcp_servers(user_api_key_dict=mock_user_auth, team_id="some-team") assert exc_info.value.status_code == 403 assert "Restricted virtual key" in str(exc_info.value.detail) @@ -1653,9 +1583,7 @@ class TestTemporaryMCPSessionEndpoints: AsyncMock(return_value=[non_admin]), ), ): - result = await _get_cached_temporary_mcp_server_or_404( - "server-x", non_admin - ) + result = await _get_cached_temporary_mcp_server_or_404("server-x", non_admin) assert result is registry_server @@ -1704,9 +1632,7 @@ class TestTemporaryMCPSessionEndpoints: AsyncMock(return_value=[ui_session_auth, team_context]), ), ): - result = await _get_cached_temporary_mcp_server_or_404( - "server-x", ui_session_auth - ) + result = await _get_cached_temporary_mcp_server_or_404("server-x", ui_session_auth) assert result is registry_server assert mock_manager.get_allowed_mcp_servers.await_count == 2 @@ -1790,12 +1716,8 @@ class TestTemporaryMCPSessionEndpoints: validate_mock.assert_called_once_with(payload) mock_manager.build_mcp_server_from_table.assert_awaited_once() - cache_mock.assert_called_once_with( - built_server, ttl_seconds=TEMPORARY_MCP_SERVER_TTL_SECONDS - ) - redis_cache_mock.assert_awaited_once_with( - built_server, ttl_seconds=TEMPORARY_MCP_SERVER_TTL_SECONDS - ) + cache_mock.assert_called_once_with(built_server, ttl_seconds=TEMPORARY_MCP_SERVER_TTL_SECONDS) + redis_cache_mock.assert_awaited_once_with(built_server, ttl_seconds=TEMPORARY_MCP_SERVER_TTL_SECONDS) args, _ = mock_manager.build_mcp_server_from_table.call_args temp_record = args[0] @@ -1900,9 +1822,7 @@ class TestTemporaryMCPSessionEndpoints: _mcp_oauth_user_api_key_auth, ) - expected_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + expected_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) mock_request = MagicMock() mock_request.headers = {"Authorization": "Bearer sk-header-key"} mock_request.cookies = {} @@ -1936,9 +1856,7 @@ class TestTemporaryMCPSessionEndpoints: _mcp_oauth_user_api_key_auth, ) - expected_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + expected_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) mock_request = MagicMock() mock_request.headers = {} mock_request.cookies = {} @@ -1986,9 +1904,7 @@ class TestTemporaryMCPSessionEndpoints: _mcp_oauth_user_api_key_auth, ) - expected_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + expected_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) mock_request = MagicMock() mock_request.headers = {} mock_request.cookies = {} @@ -2068,6 +1984,7 @@ class TestTemporaryMCPSessionEndpoints: request = MagicMock() server = generate_mock_mcp_server_config_record(server_id="server-1") + server.auth_type = MCPAuth.oauth2 authorize_response = MagicMock() admin_auth = generate_mock_user_api_key_auth( user_role=LitellmUserRoles.PROXY_ADMIN, @@ -2110,6 +2027,91 @@ class TestTemporaryMCPSessionEndpoints: scope="scope1", ) + @pytest.mark.asyncio + async def test_mcp_authorize_rejects_non_oauth2_server(self): + """mcp_authorize must reject a none-auth server with an accurate 'does not use OAuth' + 400 before the client_id check, never delegating to authorize_with_server.""" + from litellm.proxy.management_endpoints.mcp_management_endpoints import ( + mcp_authorize, + ) + + server = generate_mock_mcp_server_config_record(server_id="none-server") + server.auth_type = MCPAuth.none + admin_auth = generate_mock_user_api_key_auth( + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + with ( + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints._get_cached_temporary_mcp_server_or_404", + return_value=server, + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.authorize_with_server", + AsyncMock(), + ) as authorize_mock, + ): + with pytest.raises(HTTPException) as exc_info: + await mcp_authorize( + request=MagicMock(), + server_id="none-server", + user_api_key_dict=admin_auth, + client_id=None, + redirect_uri="https://example.com/callback", + state="state123", + ) + + assert exc_info.value.status_code == 400 + detail_text = str(exc_info.value.detail) + assert "does not use OAuth" in detail_text + assert "missing_client_id" not in detail_text + authorize_mock.assert_not_awaited() + + @pytest.mark.asyncio + async def test_mcp_token_rejects_non_oauth2_server(self): + """mcp_token must reject a none-auth server with 'does not use OAuth' 400 before the + client_id check, never delegating to exchange_token_with_server.""" + from litellm.proxy.management_endpoints.mcp_management_endpoints import ( + mcp_token, + ) + + server = generate_mock_mcp_server_config_record(server_id="none-server") + server.auth_type = MCPAuth.none + admin_auth = generate_mock_user_api_key_auth( + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + with ( + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints._get_cached_temporary_mcp_server_or_404", + return_value=server, + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.exchange_token_with_server", + AsyncMock(), + ) as exchange_mock, + ): + with pytest.raises(HTTPException) as exc_info: + await mcp_token( + request=MagicMock(), + server_id="none-server", + user_api_key_dict=admin_auth, + grant_type="authorization_code", + code="code-123", + redirect_uri="https://example.com/callback", + client_id=None, + client_secret=None, + code_verifier="verifier", + refresh_token=None, + scope=None, + ) + + assert exc_info.value.status_code == 400 + detail_text = str(exc_info.value.detail) + assert "does not use OAuth" in detail_text + assert "missing_client_id" not in detail_text + exchange_mock.assert_not_awaited() + @pytest.mark.asyncio async def test_mcp_token_proxies_to_exchange_endpoint(self): from litellm.proxy.management_endpoints.mcp_management_endpoints import ( @@ -2118,6 +2120,7 @@ class TestTemporaryMCPSessionEndpoints: request = MagicMock() server = generate_mock_mcp_server_config_record(server_id="server-1") + server.auth_type = MCPAuth.oauth2 exchange_response = {"access_token": "token"} admin_auth = generate_mock_user_api_key_auth( user_role=LitellmUserRoles.PROXY_ADMIN, @@ -2170,6 +2173,7 @@ class TestTemporaryMCPSessionEndpoints: request = MagicMock() server = generate_mock_mcp_server_config_record(server_id="server-1") + server.auth_type = MCPAuth.oauth2 exchange_response = {"access_token": "new-token", "refresh_token": "new-rt"} admin_auth = generate_mock_user_api_key_auth( user_role=LitellmUserRoles.PROXY_ADMIN, @@ -2222,6 +2226,7 @@ class TestTemporaryMCPSessionEndpoints: request = MagicMock() server = generate_mock_mcp_server_config_record(server_id="server-1") + server.auth_type = MCPAuth.oauth2 register_response = {"client_id": "generated"} request_body = { "client_name": "LiteLLM", @@ -2264,8 +2269,55 @@ class TestTemporaryMCPSessionEndpoints: response_types=["code"], token_endpoint_auth_method="client_secret_basic", fallback_client_id="server-1", + persist_credentials=True, ) + @pytest.mark.asyncio + async def test_mcp_register_does_not_persist_for_non_admin(self): + """A non-admin caller (who may have access to a real server) must not persist the DCR + result onto the shared server row. register_client_with_server is invoked with + persist_credentials=False, so user-side registration returns the DCR response without + writing shared client credentials. Only a full PROXY_ADMIN establishes the shared client.""" + from litellm.proxy.management_endpoints.mcp_management_endpoints import ( + mcp_register, + ) + + request = MagicMock() + server = generate_mock_mcp_server_config_record(server_id="server-1") + register_response = {"client_id": "generated"} + request_body = { + "client_name": "LiteLLM", + "grant_types": ["authorization_code"], + "response_types": ["code"], + "token_endpoint_auth_method": "client_secret_basic", + } + non_admin_auth = generate_mock_user_api_key_auth( + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + with ( + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints._get_cached_temporary_mcp_server_or_404", + return_value=server, + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints._read_request_body", + AsyncMock(return_value=request_body), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.register_client_with_server", + AsyncMock(return_value=register_response), + ) as register_mock, + ): + result = await mcp_register( + request=request, + server_id="server-1", + user_api_key_dict=non_admin_auth, + ) + + assert result is register_response + assert register_mock.await_args.kwargs["persist_credentials"] is False + @pytest.mark.asyncio async def test_get_cached_temporary_mcp_server_falls_back_to_redis(self): from litellm.proxy.management_endpoints.mcp_management_endpoints import ( @@ -2274,9 +2326,7 @@ class TestTemporaryMCPSessionEndpoints: server = generate_mock_mcp_server_config_record(server_id="from-redis") serialized = json.dumps(server.model_dump(mode="json")) - mock_cache_backend = SimpleNamespace( - async_get_cache=AsyncMock(return_value="encrypted-payload") - ) + mock_cache_backend = SimpleNamespace(async_get_cache=AsyncMock(return_value="encrypted-payload")) original_cache = mgmt_endpoints.litellm.cache mgmt_endpoints.litellm.cache = SimpleNamespace(cache=mock_cache_backend) try: @@ -2296,9 +2346,7 @@ class TestTemporaryMCPSessionEndpoints: assert result is not None assert result.server_id == "from-redis" - mock_cache_backend.async_get_cache.assert_awaited_once_with( - key="litellm:mcp:temporary_server:from-redis" - ) + mock_cache_backend.async_get_cache.assert_awaited_once_with(key="litellm:mcp:temporary_server:from-redis") @pytest.mark.asyncio async def test_cache_temporary_mcp_server_in_redis_uses_ttl_and_key(self): @@ -2353,13 +2401,9 @@ class TestTemporaryMCPSessionEndpoints: _get_temporary_mcp_server_from_redis, ) - server = generate_mock_mcp_server_config_record( - server_id="from-redis-encrypted" - ) + server = generate_mock_mcp_server_config_record(server_id="from-redis-encrypted") serialized = json.dumps(server.model_dump(mode="json")) - mock_cache_backend = SimpleNamespace( - async_get_cache=AsyncMock(return_value="encrypted-payload") - ) + mock_cache_backend = SimpleNamespace(async_get_cache=AsyncMock(return_value="encrypted-payload")) original_cache = mgmt_endpoints.litellm.cache mgmt_endpoints.litellm.cache = SimpleNamespace(cache=mock_cache_backend) try: @@ -2367,9 +2411,7 @@ class TestTemporaryMCPSessionEndpoints: "litellm.proxy.management_endpoints.mcp_management_endpoints.decrypt_value_helper", return_value=serialized, ) as decrypt_mock: - result = await _get_temporary_mcp_server_from_redis( - "from-redis-encrypted" - ) + result = await _get_temporary_mcp_server_from_redis("from-redis-encrypted") finally: mgmt_endpoints.litellm.cache = original_cache @@ -2429,9 +2471,7 @@ class TestTemporaryMCPSessionEndpoints: _get_temporary_mcp_server_from_redis, ) - mock_cache_backend = SimpleNamespace( - async_get_cache=AsyncMock(return_value="enc") - ) + mock_cache_backend = SimpleNamespace(async_get_cache=AsyncMock(return_value="enc")) original_cache = mgmt_endpoints.litellm.cache mgmt_endpoints.litellm.cache = SimpleNamespace(cache=mock_cache_backend) try: @@ -2453,9 +2493,7 @@ class TestTemporaryMCPSessionEndpoints: _get_temporary_mcp_server_from_redis, ) - mock_cache_backend = SimpleNamespace( - async_get_cache=AsyncMock(return_value="enc") - ) + mock_cache_backend = SimpleNamespace(async_get_cache=AsyncMock(return_value="enc")) original_cache = mgmt_endpoints.litellm.cache mgmt_endpoints.litellm.cache = SimpleNamespace(cache=mock_cache_backend) try: @@ -2477,9 +2515,7 @@ class TestTemporaryMCPSessionEndpoints: ) server = generate_mock_mcp_server_config_record(server_id="legacy-dict") - mock_cache_backend = SimpleNamespace( - async_get_cache=AsyncMock(return_value=server.model_dump(mode="json")) - ) + mock_cache_backend = SimpleNamespace(async_get_cache=AsyncMock(return_value=server.model_dump(mode="json"))) original_cache = mgmt_endpoints.litellm.cache mgmt_endpoints.litellm.cache = SimpleNamespace(cache=mock_cache_backend) try: @@ -2530,16 +2566,10 @@ class TestUpdateMCPServer: mock_prisma_client = MagicMock() mock_prisma_client.db = MagicMock() mock_prisma_client.db.litellm_mcpservertable = AsyncMock() - mock_prisma_client.db.litellm_mcpservertable.find_unique = AsyncMock( - return_value=existing_server - ) - mock_prisma_client.db.litellm_mcpservertable.update = AsyncMock( - return_value=updated_server - ) + mock_prisma_client.db.litellm_mcpservertable.find_unique = AsyncMock(return_value=existing_server) + mock_prisma_client.db.litellm_mcpservertable.update = AsyncMock(return_value=updated_server) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) # Mock the update_mcp_server function to capture the call with ( @@ -2569,9 +2599,7 @@ class TestUpdateMCPServer: edit_mcp_server, ) - result = await edit_mcp_server( - payload=update_request, user_api_key_dict=mock_user_auth - ) + result = await edit_mcp_server(payload=update_request, user_api_key_dict=mock_user_auth) # Verify that update_mcp_server was called with the correct payload update_mock.assert_awaited_once() @@ -2611,18 +2639,12 @@ class TestAddMCPServerAtomicity: url="https://echo.example.com/mcp", transport=MCPTransport.http, ) - admin = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin-user" - ) - created_server = generate_mock_mcp_server_db_record( - server_id="created-1", alias="echo" - ) + admin = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin-user") + created_server = generate_mock_mcp_server_db_record(server_id="created-1", alias="echo") mock_manager = MagicMock() mock_manager.add_server = AsyncMock() - mock_manager.reload_servers_from_database = AsyncMock( - side_effect=Exception("malformed pre-existing row") - ) + mock_manager.reload_servers_from_database = AsyncMock(side_effect=Exception("malformed pre-existing row")) with ( patch( @@ -2659,9 +2681,7 @@ class TestAddMCPServerAtomicity: url="https://echo.example.com/mcp", transport=MCPTransport.http, ) - admin = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin-user" - ) + admin = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin-user") mock_manager = MagicMock() mock_manager.add_server = AsyncMock() @@ -2789,9 +2809,7 @@ class TestMCPRegistryEndpoint: mock_manager = MagicMock() mock_manager.get_registry.return_value = {mock_server.server_id: mock_server} # The registry endpoint uses get_filtered_registry (filters by client IP) - mock_manager.get_filtered_registry.return_value = { - mock_server.server_id: mock_server - } + mock_manager.get_filtered_registry.return_value = {mock_server.server_id: mock_server} with ( patch_proxy_general_settings({"enable_mcp_registry": True}), @@ -2841,9 +2859,7 @@ class TestMCPRegistryEndpoint: # Mock manager mock_manager = MagicMock() - mock_manager.get_all_mcp_servers_with_health_and_teams = AsyncMock( - return_value=[mock_health_result] - ) + mock_manager.get_all_mcp_servers_with_health_and_teams = AsyncMock(return_value=[mock_health_result]) with ( patch( @@ -2892,18 +2908,12 @@ class TestManagementPayloadValidation: health_check_servers, ) - mock_user_auth = generate_mock_user_api_key_auth( - user_role=LitellmUserRoles.INTERNAL_USER - ) + mock_user_auth = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.INTERNAL_USER) - health_result_one = generate_mock_mcp_server_db_record( - server_id="server-1", alias="One" - ) + health_result_one = generate_mock_mcp_server_db_record(server_id="server-1", alias="One") health_result_one.status = "healthy" - health_result_two = generate_mock_mcp_server_db_record( - server_id="server-2", alias="Two" - ) + health_result_two = generate_mock_mcp_server_db_record(server_id="server-2", alias="Two") health_result_two.status = "unhealthy" mock_manager = MagicMock() @@ -3073,9 +3083,7 @@ class TestMCPApprovalWorkflow: AsyncMock(return_value=created_record), ) as mock_create, ): - result = await register_mcp_server( - payload=payload, user_api_key_dict=user_auth - ) + result = await register_mcp_server(payload=payload, user_api_key_dict=user_auth) # Endpoint sets pending_review before calling create_mcp_server call_payload = mock_create.call_args[0][1] @@ -3106,9 +3114,7 @@ class TestMCPApprovalWorkflow: admin = generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN) pending = generate_mock_mcp_server_db_record(alias="Pending") pending.approval_status = "pending_review" - summary = MCPSubmissionsSummary( - total=1, pending_review=1, active=0, rejected=0, items=[pending] - ) + summary = MCPSubmissionsSummary(total=1, pending_review=1, active=0, rejected=0, items=[pending]) with ( patch( @@ -3126,45 +3132,20 @@ class TestMCPApprovalWorkflow: assert result.pending_review == 1 @pytest.mark.asyncio - @pytest.mark.parametrize( - "user_role, expected_global_value", - [ - (LitellmUserRoles.PROXY_ADMIN, "super-secret"), - (LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY, ""), - ], - ) - async def test_get_submissions_redacts_global_env_for_view_only_admin( - self, user_role, expected_global_value - ): - """Read-only admins reviewing the submission queue must not receive the - submitter's global env var secrets; full admins still see them.""" + async def test_get_submissions_sanitizes_for_view_only_admin(self): + """PROXY_ADMIN_VIEW_ONLY reviewing the submission queue must go through + the non-admin sanitizer that fetch/list endpoints use: url, + static_headers, env, env_vars, and credentials are all dropped. A + mutation swapping the gate back to the old partial-blank pattern (which + left url/static_headers/env and env-var names intact) would fail this.""" from litellm.proxy._types import MCPSubmissionsSummary from litellm.proxy.management_endpoints.mcp_management_endpoints import ( get_mcp_server_submissions, ) - base = generate_mock_mcp_server_db_record(alias="Pending") - item = LiteLLM_MCPServerTable( - **{ - **base.model_dump(), - "env_vars": [ - { - "name": "ADMIN_API_KEY", - "value": "super-secret", - "scope": "global", - }, - { - "name": "USER_TOKEN", - "value": "placeholder-hint", - "scope": "user", - }, - ], - } - ) + item = _leaky_list_server() item.approval_status = "pending_review" - summary = MCPSubmissionsSummary( - total=1, pending_review=1, active=0, rejected=0, items=[item] - ) + summary = MCPSubmissionsSummary(total=1, pending_review=1, active=0, rejected=0, items=[item]) with ( patch( @@ -3177,12 +3158,64 @@ class TestMCPApprovalWorkflow: ), ): result = await get_mcp_server_submissions( - user_api_key_dict=generate_mock_user_api_key_auth(user_role=user_role), + user_api_key_dict=generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY), ) - by_name = {ev.name: ev for ev in result.items[0].env_vars} - assert by_name["ADMIN_API_KEY"].value == expected_global_value - assert by_name["USER_TOKEN"].value == "placeholder-hint" + assert len(result.items) == 1 + sanitized = result.items[0] + assert sanitized.url is None + assert sanitized.static_headers is None + assert sanitized.env == {} + assert sanitized.env_vars is None + assert sanitized.credentials is None + + # The source record must not be mutated by sanitization. + assert item.url == "https://leaky.example.com/mcp?api_key=sk-embedded-in-url" + assert item.static_headers == {"Authorization": "Bearer sk-secret-header"} + + @pytest.mark.asyncio + async def test_get_submissions_full_admin_still_sees_secrets(self): + """The view-only redaction must not over-redact for a full PROXY_ADMIN, + who needs url/static_headers/env/env_vars to review the pending + submission. Only the explicit credentials field is cleared.""" + from litellm.proxy._types import MCPSubmissionsSummary + from litellm.proxy.management_endpoints.mcp_management_endpoints import ( + get_mcp_server_submissions, + ) + + item = _leaky_list_server() + item.approval_status = "pending_review" + summary = MCPSubmissionsSummary(total=1, pending_review=1, active=0, rejected=0, items=[item]) + + with ( + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", + return_value=MagicMock(), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_mcp_submissions", + AsyncMock(return_value=summary), + ), + ): + result = await get_mcp_server_submissions( + user_api_key_dict=generate_mock_user_api_key_auth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + + assert len(result.items) == 1 + raw = result.items[0] + assert raw.url == "https://leaky.example.com/mcp?api_key=sk-embedded-in-url" + assert raw.static_headers == {"Authorization": "Bearer sk-secret-header"} + assert raw.env == {"UPSTREAM_TOKEN": "sk-secret-env"} + assert raw.credentials is None + assert raw.env_vars is not None + assert len(raw.env_vars) == 1 + # ``model_construct`` in ``_leaky_list_server`` skips validation, so + # env_vars stays as raw dicts; mirror the fixture shape here. + entry = raw.env_vars[0] + name = entry["name"] if isinstance(entry, dict) else entry.name + value = entry["value"] if isinstance(entry, dict) else entry.value + assert name == "GLOBAL_KEY" + assert value == "super-secret" @pytest.mark.asyncio async def test_approve_non_pending_server_raises_400(self): @@ -3206,9 +3239,7 @@ class TestMCPApprovalWorkflow: ), ): with pytest.raises(HTTPException) as exc_info: - await approve_mcp_server_submission( - server_id="server-1", user_api_key_dict=admin - ) + await approve_mcp_server_submission(server_id="server-1", user_api_key_dict=admin) assert exc_info.value.status_code == 400 @pytest.mark.asyncio @@ -3223,8 +3254,10 @@ class TestMCPApprovalWorkflow: pending_server.approval_status = MCPApprovalStatus.pending_review approved_server = generate_mock_mcp_server_db_record() approved_server.approval_status = MCPApprovalStatus.active + approved_server.submitted_by = "submitter-user" mock_manager = MagicMock() + mock_manager.invalidate_byom_submitted_servers_cache = AsyncMock() mock_manager.reload_servers_from_database = AsyncMock() with ( @@ -3245,11 +3278,10 @@ class TestMCPApprovalWorkflow: mock_manager, ), ): - result = await approve_mcp_server_submission( - server_id=pending_server.server_id, user_api_key_dict=admin - ) + result = await approve_mcp_server_submission(server_id=pending_server.server_id, user_api_key_dict=admin) mock_manager.reload_servers_from_database.assert_awaited_once() + mock_manager.invalidate_byom_submitted_servers_cache.assert_awaited_once_with("submitter-user") assert result is not None @pytest.mark.asyncio @@ -3374,9 +3406,7 @@ class TestValidateMCPRequiredFields: source_url="https://github.com/org/repo", auth_type=MCPAuth.bearer_token, ) - with patch_proxy_general_settings( - {"mcp_required_fields": ["source_url", "auth_type"]} - ): + with patch_proxy_general_settings({"mcp_required_fields": ["source_url", "auth_type"]}): # Should not raise _validate_mcp_required_fields(payload) @@ -3464,9 +3494,7 @@ async def test_store_mcp_oauth_user_credential_returns_status(): ), patch( "litellm.proxy.management_endpoints.mcp_management_endpoints.get_mcp_server", - new=AsyncMock( - return_value=generate_mock_mcp_server_db_record(server_id=server_id) - ), + new=AsyncMock(return_value=generate_mock_mcp_server_db_record(server_id=server_id)), ), patch( "litellm.proxy.management_endpoints.mcp_management_endpoints._user_has_admin_view", @@ -3538,6 +3566,148 @@ async def test_delete_mcp_oauth_user_credential_only_deletes_oauth(): assert result.has_credential is False +@pytest.mark.asyncio +async def test_store_mcp_oauth_user_credential_invalidates_cached_token(): + """Re-authorizing via the Tools-tab persist drops the v2 per-user token cache entry, so + egress stops serving the replaced token immediately instead of until its TTL.""" + from litellm.proxy._types import MCPOAuthUserCredentialRequest + + if not mgmt_endpoints.MCP_AVAILABLE: + pytest.skip("MCP module not installed") + + from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module + from litellm.proxy.management_endpoints.mcp_management_endpoints import ( + store_mcp_oauth_user_credential, + ) + + server_id = "srv-inv-1" + user_id = "user-inv-1" + invalidate_mock = AsyncMock(return_value=None) + + with ( + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", + return_value=_make_prisma_client(), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_mcp_server", + new=AsyncMock(return_value=generate_mock_mcp_server_db_record(server_id=server_id)), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints._user_has_admin_view", + return_value=True, + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.store_user_oauth_credential", + new=AsyncMock(return_value=None), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_user_oauth_credential", + new=AsyncMock(return_value={"type": "oauth2", "access_token": "new-tok"}), + ), + patch.object( + manager_module.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + new=invalidate_mock, + ), + ): + await store_mcp_oauth_user_credential( + server_id=server_id, + payload=MCPOAuthUserCredentialRequest(access_token="new-tok", expires_in=3600), + user_api_key_dict=_make_user_auth(user_id), + ) + + invalidate_mock.assert_awaited_once_with(user_id, server_id) + + +@pytest.mark.asyncio +async def test_delete_mcp_oauth_user_credential_invalidates_cached_token(): + """Revoking a stored OAuth credential drops the v2 per-user token cache entry, so the + revoked token stops flowing upstream immediately instead of until its TTL.""" + if not mgmt_endpoints.MCP_AVAILABLE: + pytest.skip("MCP module not installed") + + from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module + from litellm.proxy.management_endpoints.mcp_management_endpoints import ( + delete_mcp_oauth_user_credential, + ) + + server_id = "srv-inv-2" + user_id = "user-inv-2" + invalidate_mock = AsyncMock(return_value=None) + + with ( + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", + return_value=_make_prisma_client(), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_user_oauth_credential", + new=AsyncMock(return_value={"type": "oauth2", "access_token": "revoked-tok"}), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.delete_user_credential", + new=AsyncMock(return_value=None), + ), + patch.object( + manager_module.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + new=invalidate_mock, + ), + ): + result = await delete_mcp_oauth_user_credential( + server_id=server_id, + user_api_key_dict=_make_user_auth(user_id), + ) + + invalidate_mock.assert_awaited_once_with(user_id, server_id) + assert result.has_credential is False + + +@pytest.mark.asyncio +async def test_delete_mcp_oauth_user_credential_invalidates_when_record_already_gone(): + """A concurrent delete can remove the row between the read and the delete; the cache may + still hold the revoked token, so the invalidate must fire even on RecordNotFoundError.""" + if not mgmt_endpoints.MCP_AVAILABLE: + pytest.skip("MCP module not installed") + + from litellm.proxy._experimental.mcp_server import mcp_server_manager as manager_module + from litellm.proxy.management_endpoints.mcp_management_endpoints import ( + delete_mcp_oauth_user_credential, + ) + + server_id = "srv-inv-3" + user_id = "user-inv-3" + invalidate_mock = AsyncMock(return_value=None) + + with ( + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", + return_value=_make_prisma_client(), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_user_oauth_credential", + new=AsyncMock(return_value={"type": "oauth2", "access_token": "revoked-tok"}), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.delete_user_credential", + new=AsyncMock(side_effect=mgmt_endpoints.RecordNotFoundError({}, message="already gone")), + ), + patch.object( + manager_module.global_mcp_server_manager, + "invalidate_user_oauth_token_cache", + new=invalidate_mock, + ), + ): + result = await delete_mcp_oauth_user_credential( + server_id=server_id, + user_api_key_dict=_make_user_auth(user_id), + ) + + invalidate_mock.assert_awaited_once_with(user_id, server_id) + assert result.has_credential is False + + @pytest.mark.asyncio async def test_list_mcp_user_credentials_batch_server_fetch(): """list_mcp_user_credentials uses a single batch DB call, not N+1 queries.""" @@ -3561,9 +3731,7 @@ async def test_list_mcp_user_credentials_batch_server_fetch(): "server_id": server_id, } ] - mock_server = generate_mock_mcp_server_db_record( - server_id=server_id, alias="My Server" - ) + mock_server = generate_mock_mcp_server_db_record(server_id=server_id, alias="My Server") # get_mcp_servers (batch) should be called once; get_mcp_server (single) must not be called. batch_mock = AsyncMock(return_value=[mock_server]) single_mock = AsyncMock(return_value=mock_server) @@ -3746,6 +3914,10 @@ def test_sanitize_mcp_server_for_non_admin_clears_credential_fields(): server.authorization_url = "https://idp/authorize" server.token_url = "https://idp/token" server.registration_url = "https://idp/register" + server.token_exchange_endpoint = "https://idp/token-exchange" + server.audience = "https://upstream/api" + server.subject_token_type = "urn:ietf:params:oauth:token-type:jwt" + server.token_exchange_profile = "entra_obo" sanitized = _sanitize_mcp_server_for_non_admin(server) @@ -3760,6 +3932,13 @@ def test_sanitize_mcp_server_for_non_admin_clears_credential_fields(): assert sanitized.authorization_url is None assert sanitized.token_url is None assert sanitized.registration_url is None + # The token-exchange IdP endpoint is as sensitive as token_url; audience names the upstream. + # subject_token_type is a public RFC 8693 URN, cleared for uniformity: non-admins + # receive no token-exchange config at all. + assert sanitized.token_exchange_endpoint is None + assert sanitized.audience is None + assert sanitized.subject_token_type is None + assert sanitized.token_exchange_profile is None # Identity / metadata fields are preserved so the UI can list the # server without exposing secrets. @@ -3813,6 +3992,27 @@ def test_sanitize_virtual_key_drops_all_env_vars(): assert server.env_vars[0].value == "super-secret" +def test_sanitize_virtual_key_clears_token_exchange_endpoint_and_audience(): + """Virtual-key callers must not receive the token-exchange IdP endpoint or audience, + matching how token_url is scrubbed for the same view.""" + import litellm.proxy.management_endpoints.mcp_management_endpoints as mgmt + + server = generate_mock_mcp_server_db_record() + server.token_url = "https://idp/token" + server.token_exchange_endpoint = "https://idp/token-exchange" + server.audience = "https://upstream/api" + server.subject_token_type = "urn:ietf:params:oauth:token-type:jwt" + server.token_exchange_profile = "entra_obo" + + sanitized = mgmt._sanitize_mcp_server_for_virtual_key(server) + + assert sanitized.token_url is None + assert sanitized.token_exchange_endpoint is None + assert sanitized.audience is None + assert sanitized.subject_token_type is None + assert sanitized.token_exchange_profile is None + + def _server_with_env_vars(server_id: str = "srv-env"): base = generate_mock_mcp_server_db_record(server_id=server_id) return LiteLLM_MCPServerTable( @@ -3874,9 +4074,7 @@ async def test_fetch_single_mcp_server_env_vars_full_admin_vs_view_only(): assert view_only.env_vars is None # The source record must never be mutated. - assert {ev.name: ev.value for ev in server.env_vars}[ - "ADMIN_API_KEY" - ] == "super-secret" + assert {ev.name: ev.value for ev in server.env_vars}["ADMIN_API_KEY"] == "super-secret" @pytest.mark.asyncio @@ -3913,9 +4111,7 @@ async def test_fetch_all_mcp_servers_env_vars_full_admin_vs_view_only(): view_only = await _fetch_all(LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY) assert view_only[0].env_vars is None - assert {ev.name: ev.value for ev in server.env_vars}[ - "ADMIN_API_KEY" - ] == "super-secret" + assert {ev.name: ev.value for ev in server.env_vars}["ADMIN_API_KEY"] == "super-secret" def _leaky_list_server() -> "LiteLLM_MCPServerTable": @@ -3968,9 +4164,7 @@ async def test_list_mcp_servers_sanitized_for_view_only_admin(): A mutation swapping _user_is_full_admin() back to _user_has_admin_view() (which also grants view-only admins) would return the raw url/headers and fail this. The real role helpers are exercised; the gate is not patched.""" - source, result = await _fetch_all_via_view_all( - LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY - ) + source, result = await _fetch_all_via_view_all(LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY) assert len(result) == 1 sanitized = result[0] @@ -4045,12 +4239,8 @@ class TestComputeUserEnvVarStatus: """Unit tests for the _compute_user_env_var_status helper.""" def test_only_referenced_per_user_vars_are_required(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) - status = mgmt_endpoints._compute_user_env_var_status( - server=server, stored_values={"CORP_USERNAME": "alice"} - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) + status = mgmt_endpoints._compute_user_env_var_status(server=server, stored_values={"CORP_USERNAME": "alice"}) names = {spec.name for spec in status.required} # UNUSED_USER_VAR is declared per-user but never referenced -> not blocking. assert names == {"CORP_USERNAME", "CORP_PASSWORD"} @@ -4068,9 +4258,7 @@ class TestComputeUserEnvVarStatus: assert status.setup_url and "srv-1" in status.setup_url def test_all_filled_has_zero_missing(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) status = mgmt_endpoints._compute_user_env_var_status( server=server, stored_values={"CORP_USERNAME": "alice", "CORP_PASSWORD": "s3cret"}, @@ -4083,20 +4271,14 @@ class TestComputeUserEnvVarStatus: env_vars=_ENV_VARS_MIXED, static_headers='{"Authorization": "${CORP_USERNAME}"}', ) - status = mgmt_endpoints._compute_user_env_var_status( - server=server, stored_values={} - ) + status = mgmt_endpoints._compute_user_env_var_status(server=server, stored_values={}) # Only CORP_USERNAME is referenced via the JSON-string headers. assert {spec.name for spec in status.required} == {"CORP_USERNAME"} assert status.missing_count == 1 def test_static_headers_invalid_json_string_yields_no_required(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers="not-json{" - ) - status = mgmt_endpoints._compute_user_env_var_status( - server=server, stored_values={} - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers="not-json{") + status = mgmt_endpoints._compute_user_env_var_status(server=server, stored_values={}) assert status.required == [] assert status.missing_count == 0 # No required fields -> no setup URL. @@ -4107,9 +4289,7 @@ class TestComputeUserEnvVarStatus: env_vars=[{"name": "DB_PROTOCOL", "value": "postgres", "scope": "global"}], static_headers={"Authorization": "${DB_PROTOCOL}://host"}, ) - status = mgmt_endpoints._compute_user_env_var_status( - server=server, stored_values={} - ) + status = mgmt_endpoints._compute_user_env_var_status(server=server, stored_values={}) assert status.required == [] assert status.setup_url is None @@ -4126,9 +4306,7 @@ class TestComputeUserEnvVarStatus: ], static_headers={"Authorization": "Bearer ${SHARED_TOKEN}"}, ) - status = mgmt_endpoints._compute_user_env_var_status( - server=server, stored_values={} - ) + status = mgmt_endpoints._compute_user_env_var_status(server=server, stored_values={}) assert status.required == [] assert status.missing_count == 0 assert status.setup_url is None @@ -4146,9 +4324,7 @@ class TestComputeUserEnvVarStatus: ], static_headers={"Authorization": "Bearer ${SHARED_TOKEN}"}, ) - status = mgmt_endpoints._compute_user_env_var_status( - server=server, stored_values={} - ) + status = mgmt_endpoints._compute_user_env_var_status(server=server, stored_values={}) assert {spec.name for spec in status.required} == {"SHARED_TOKEN"} assert status.missing_count == 1 assert status.setup_url and "srv-1" in status.setup_url @@ -4157,16 +4333,10 @@ class TestComputeUserEnvVarStatus: class TestGetMCPUserEnvVars: @pytest.mark.asyncio async def test_returns_status_for_server(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object( mgmt_endpoints, "get_user_env_vars", @@ -4189,9 +4359,7 @@ class TestGetMCPUserEnvVars: @pytest.mark.asyncio async def test_missing_user_id_raises_400(self): - with patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ): + with patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()): with pytest.raises(HTTPException) as exc: await mgmt_endpoints.get_mcp_user_env_vars( server_id="srv-1", @@ -4202,12 +4370,8 @@ class TestGetMCPUserEnvVars: @pytest.mark.asyncio async def test_unknown_server_raises_404(self): with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), ): with pytest.raises(HTTPException) as exc: await mgmt_endpoints.get_mcp_user_env_vars( @@ -4220,17 +4384,11 @@ class TestGetMCPUserEnvVars: class TestStoreMCPUserEnvVars: @pytest.mark.asyncio async def test_persists_only_allowed_non_empty_values(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) merge_mock = AsyncMock(return_value={"CORP_USERNAME": "alice"}) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object(mgmt_endpoints, "merge_user_env_vars", merge_mock), ): result = await mgmt_endpoints.store_mcp_user_env_vars( @@ -4262,26 +4420,16 @@ class TestStoreMCPUserEnvVars: """The endpoint forwards only the user's submitted (allowed, non-empty) update to the atomic merge and reports status from the merged result, so a one-field edit never sends the other stored values back through.""" - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) - merge_mock = AsyncMock( - return_value={"CORP_USERNAME": "alice", "CORP_PASSWORD": "new"} - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) + merge_mock = AsyncMock(return_value={"CORP_USERNAME": "alice", "CORP_PASSWORD": "new"}) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object(mgmt_endpoints, "merge_user_env_vars", merge_mock), ): result = await mgmt_endpoints.store_mcp_user_env_vars( server_id="srv-1", - payload=mgmt_endpoints.MCPUserEnvVarsRequest( - values={"CORP_PASSWORD": "new"} - ), + payload=mgmt_endpoints.MCPUserEnvVarsRequest(values={"CORP_PASSWORD": "new"}), user_api_key_dict=generate_mock_user_api_key_auth(user_id="alice"), ) merge_mock.assert_awaited_once() @@ -4292,9 +4440,7 @@ class TestStoreMCPUserEnvVars: @pytest.mark.asyncio async def test_missing_user_id_raises_400(self): - with patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ): + with patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()): with pytest.raises(HTTPException) as exc: await mgmt_endpoints.store_mcp_user_env_vars( server_id="srv-1", @@ -4306,12 +4452,8 @@ class TestStoreMCPUserEnvVars: @pytest.mark.asyncio async def test_unknown_server_raises_404(self): with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), ): with pytest.raises(HTTPException) as exc: await mgmt_endpoints.store_mcp_user_env_vars( @@ -4325,17 +4467,11 @@ class TestStoreMCPUserEnvVars: class TestClearMCPUserEnvVars: @pytest.mark.asyncio async def test_clears_and_returns_empty_status(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) delete_mock = AsyncMock() with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object(mgmt_endpoints, "delete_user_env_vars", delete_mock), ): result = await mgmt_endpoints.clear_mcp_user_env_vars( @@ -4349,16 +4485,10 @@ class TestClearMCPUserEnvVars: @pytest.mark.asyncio async def test_delete_db_error_propagates(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object( mgmt_endpoints, "delete_user_env_vars", @@ -4374,9 +4504,7 @@ class TestClearMCPUserEnvVars: @pytest.mark.asyncio async def test_missing_user_id_raises_400(self): - with patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ): + with patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()): with pytest.raises(HTTPException) as exc: await mgmt_endpoints.clear_mcp_user_env_vars( server_id="srv-1", @@ -4387,12 +4515,8 @@ class TestClearMCPUserEnvVars: @pytest.mark.asyncio async def test_unknown_server_raises_404(self): with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), ): with pytest.raises(HTTPException) as exc: await mgmt_endpoints.clear_mcp_user_env_vars( @@ -4405,9 +4529,7 @@ class TestClearMCPUserEnvVars: class TestListMCPUserEnvVarStatus: @pytest.mark.asyncio async def test_no_user_id_returns_empty(self): - with patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ): + with patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()): result = await mgmt_endpoints.list_mcp_user_env_var_status( user_api_key_dict=generate_mock_user_api_key_auth(user_id="") ) @@ -4416,9 +4538,7 @@ class TestListMCPUserEnvVarStatus: @pytest.mark.asyncio async def test_no_accessible_servers_returns_empty(self): with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), patch.object( mgmt_endpoints, "_resolve_accessible_mcp_servers", @@ -4444,9 +4564,7 @@ class TestListMCPUserEnvVarStatus: static_headers={"Authorization": "${DB_PROTOCOL}://host"}, ) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), patch.object( mgmt_endpoints, "_resolve_accessible_mcp_servers", @@ -4474,9 +4592,7 @@ class TestListMCPUserEnvVarStatus: static_headers=_STATIC_HEADERS_MIXED, ) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), patch.object( mgmt_endpoints, "_resolve_accessible_mcp_servers", @@ -4509,9 +4625,7 @@ class TestListMCPUserEnvVarStatus: static_headers=_STATIC_HEADERS_MIXED, ) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), patch.object( mgmt_endpoints, "_get_user_mcp_management_mode", @@ -4549,17 +4663,11 @@ class TestMCPUserEnvVarsAccessControl: @pytest.mark.asyncio async def test_get_forbidden_for_non_admin_without_access(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) get_user_env_vars = AsyncMock(return_value={}) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object( mgmt_endpoints, "build_effective_auth_contexts", @@ -4585,17 +4693,11 @@ class TestMCPUserEnvVarsAccessControl: @pytest.mark.asyncio async def test_store_forbidden_for_non_admin_without_access(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) merge_mock = AsyncMock() with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object( mgmt_endpoints, "build_effective_auth_contexts", @@ -4611,9 +4713,7 @@ class TestMCPUserEnvVarsAccessControl: with pytest.raises(HTTPException) as exc: await mgmt_endpoints.store_mcp_user_env_vars( server_id="srv-1", - payload=mgmt_endpoints.MCPUserEnvVarsRequest( - values={"CORP_USERNAME": "alice"} - ), + payload=mgmt_endpoints.MCPUserEnvVarsRequest(values={"CORP_USERNAME": "alice"}), user_api_key_dict=generate_mock_user_api_key_auth( user_id="alice", user_role=LitellmUserRoles.INTERNAL_USER, @@ -4624,17 +4724,11 @@ class TestMCPUserEnvVarsAccessControl: @pytest.mark.asyncio async def test_clear_forbidden_for_non_admin_without_access(self): - server = _make_env_var_server( - env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED - ) + server = _make_env_var_server(env_vars=_ENV_VARS_MIXED, static_headers=_STATIC_HEADERS_MIXED) delete_mock = AsyncMock() with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object( mgmt_endpoints, "build_effective_auth_contexts", @@ -4666,12 +4760,8 @@ class TestMCPUserEnvVarsAccessControl: static_headers=_STATIC_HEADERS_MIXED, ) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object( mgmt_endpoints, "build_effective_auth_contexts", @@ -4708,20 +4798,14 @@ class TestMCPUserEnvVarsAccessControl: ) allowed_mock = AsyncMock(return_value=[]) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=server)), patch.object( mgmt_endpoints.global_mcp_server_manager, "get_allowed_mcp_servers", allowed_mock, ), - patch.object( - mgmt_endpoints, "get_user_env_vars", AsyncMock(return_value={}) - ), + patch.object(mgmt_endpoints, "get_user_env_vars", AsyncMock(return_value={})), ): result = await mgmt_endpoints.get_mcp_user_env_vars( server_id="srv-1", @@ -4740,12 +4824,8 @@ class TestMCPUserEnvVarsAccessControl: ids stay non-enumerable, even when neither the DB nor the registry has the server.""" with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), patch.object( mgmt_endpoints, "build_effective_auth_contexts", @@ -4817,9 +4897,159 @@ def test_oauth2_flow_defaults_to_none_when_omitted(): ) assert UpdateMCPServerRequest(server_id="srv-1").oauth2_flow is None - assert ( - LiteLLM_MCPServerTable(server_id="srv-1", transport="http").oauth2_flow is None + assert LiteLLM_MCPServerTable(server_id="srv-1", transport="http").oauth2_flow is None + + +def test_dcr_bridge_rejected_on_create_for_gateway_managed_auth_type(): + from pydantic import ValidationError + + from litellm.proxy._types import NewMCPServerRequest + + with pytest.raises(ValidationError) as exc: + NewMCPServerRequest( + server_name="bridge-server", + url="https://example.com/mcp", + transport="http", + auth_type="oauth2", + oauth2_flow="authorization_code", + dcr_bridge=True, + ) + assert "dcr_bridge is only supported" in str(exc.value) + + +def test_dcr_bridge_rejected_on_create_when_auth_type_omitted(): + from pydantic import ValidationError + + from litellm.proxy._types import NewMCPServerRequest + + with pytest.raises(ValidationError) as exc: + NewMCPServerRequest( + server_name="bridge-server", + url="https://example.com/mcp", + transport="http", + dcr_bridge=True, + ) + assert "dcr_bridge is only supported" in str(exc.value) + + +@pytest.mark.parametrize("auth_type", ["true_passthrough", "oauth_delegate"]) +def test_dcr_bridge_accepted_on_create_for_client_forwarded_modes(auth_type): + from litellm.proxy._experimental.mcp_server.db import _prepare_mcp_server_data + from litellm.proxy._types import NewMCPServerRequest + + payload = NewMCPServerRequest( + server_name="bridge-server", + url="https://example.com/mcp", + transport="http", + auth_type=auth_type, + dcr_bridge=True, ) + data_dict = _prepare_mcp_server_data(payload) + assert data_dict["dcr_bridge"] is True + + +def test_dcr_bridge_update_rejected_when_payload_auth_type_not_client_forwarded(): + from pydantic import ValidationError + + from litellm.proxy._types import UpdateMCPServerRequest + + with pytest.raises(ValidationError) as exc: + UpdateMCPServerRequest(server_id="srv-1", auth_type="oauth2", dcr_bridge=True) + assert "dcr_bridge is only supported" in str(exc.value) + + +def test_dcr_bridge_update_without_auth_type_defers_to_endpoint(): + from litellm.proxy._types import UpdateMCPServerRequest + + assert UpdateMCPServerRequest(server_id="srv-1", dcr_bridge=True).dcr_bridge is True + + +def test_dcr_bridge_round_trips_on_response_model(): + from litellm.proxy._types import LiteLLM_MCPServerTable + + row = LiteLLM_MCPServerTable(server_id="srv-1", transport="http", dcr_bridge=True) + assert row.dcr_bridge is True + assert LiteLLM_MCPServerTable(server_id="srv-1", transport="http").dcr_bridge is None + + +def _edit_endpoint_patches(old_record, update_mock): + return ( + patch("litellm.proxy.management_endpoints.mcp_management_endpoints.MCP_AVAILABLE", True), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", + return_value=MagicMock(), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_mcp_server", + AsyncMock(side_effect=old_record) if isinstance(old_record, Exception) else AsyncMock(return_value=old_record), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.update_mcp_server", + update_mock, + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.validate_and_normalize_mcp_server_payload", + autospec=True, + ), + ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("stored_auth_type", ["oauth2", "api_key", "none"]) +async def test_edit_mcp_server_rejects_dcr_bridge_when_stored_auth_type_not_client_forwarded(stored_auth_type): + from litellm.proxy._types import UpdateMCPServerRequest + from litellm.proxy.management_endpoints.mcp_management_endpoints import edit_mcp_server + + old_record = MagicMock() + old_record.auth_type = stored_auth_type + update_mock = AsyncMock() + p1, p2, p3, p4, p5 = _edit_endpoint_patches(old_record, update_mock) + with p1, p2, p3, p4, p5: + payload = UpdateMCPServerRequest(server_id="srv-1", dcr_bridge=True) + user_auth = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) + with pytest.raises(HTTPException) as exc: + await edit_mcp_server(payload=payload, user_api_key_dict=user_auth) + + assert exc.value.status_code == 400 + assert "dcr_bridge is only supported" in str(exc.value.detail) + update_mock.assert_not_called() + + +@pytest.mark.asyncio +async def test_edit_mcp_server_rejects_dcr_bridge_when_stored_record_unreadable(): + from litellm.proxy._types import UpdateMCPServerRequest + from litellm.proxy.management_endpoints.mcp_management_endpoints import edit_mcp_server + + update_mock = AsyncMock() + p1, p2, p3, p4, p5 = _edit_endpoint_patches(RuntimeError("db down"), update_mock) + with p1, p2, p3, p4, p5: + payload = UpdateMCPServerRequest(server_id="srv-1", dcr_bridge=True) + user_auth = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) + with pytest.raises(HTTPException) as exc: + await edit_mcp_server(payload=payload, user_api_key_dict=user_auth) + + assert exc.value.status_code == 400 + update_mock.assert_not_called() + + +@pytest.mark.asyncio +async def test_edit_mcp_server_dcr_bridge_on_unknown_server_returns_404_not_400(): + """A dcr_bridge enablement targeting a server_id that does not exist must surface the accurate + 404 from the update path, not a misleading 400 about the stored auth_type: get_mcp_server + returns None for a missing row without raising, which is distinct from a failed read.""" + from litellm.proxy._types import UpdateMCPServerRequest + from litellm.proxy.management_endpoints.mcp_management_endpoints import edit_mcp_server + + update_mock = AsyncMock(return_value=None) + p1, p2, p3, p4, p5 = _edit_endpoint_patches(None, update_mock) + with p1, p2, p3, p4, p5: + payload = UpdateMCPServerRequest(server_id="does-not-exist", dcr_bridge=True) + user_auth = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) + with pytest.raises(HTTPException) as exc: + await edit_mcp_server(payload=payload, user_api_key_dict=user_auth) + + assert exc.value.status_code == 404 + update_mock.assert_called_once() class TestPerUserCredentialConfigServerResolution: @@ -4835,17 +5065,13 @@ class TestPerUserCredentialConfigServerResolution: def _registry_only_manager(self, *, is_byok: bool = False): """A manager mock where the server exists only in the registry (DB miss).""" - config_server = generate_mock_mcp_server_config_record( - server_id=self.CONFIG_SERVER_ID, name="Config Server" + config_server = generate_mock_mcp_server_config_record(server_id=self.CONFIG_SERVER_ID, name="Config Server") + record = generate_mock_mcp_server_db_record(server_id=self.CONFIG_SERVER_ID).model_copy( + update={"is_byok": is_byok} ) - record = generate_mock_mcp_server_db_record( - server_id=self.CONFIG_SERVER_ID - ).model_copy(update={"is_byok": is_byok}) manager = MagicMock() manager.get_mcp_server_by_id = MagicMock( - side_effect=lambda sid: ( - config_server if sid == self.CONFIG_SERVER_ID else None - ) + side_effect=lambda sid: config_server if sid == self.CONFIG_SERVER_ID else None ) manager._build_mcp_server_table = MagicMock(return_value=record) manager.get_allowed_mcp_servers = AsyncMock(return_value=[]) @@ -4858,12 +5084,8 @@ class TestPerUserCredentialConfigServerResolution: manager = self._registry_only_manager() store_mock = AsyncMock(return_value=None) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), patch.object(mgmt_endpoints, "global_mcp_server_manager", manager), patch.object(mgmt_endpoints, "store_user_oauth_credential", store_mock), patch.object( @@ -4874,9 +5096,7 @@ class TestPerUserCredentialConfigServerResolution: ): result = await mgmt_endpoints.store_mcp_oauth_user_credential( server_id=self.CONFIG_SERVER_ID, - payload=mgmt_endpoints.MCPOAuthUserCredentialRequest( - access_token="tok", expires_in=3600 - ), + payload=mgmt_endpoints.MCPOAuthUserCredentialRequest(access_token="tok", expires_in=3600), user_api_key_dict=generate_mock_user_api_key_auth(user_id="admin"), ) assert result.has_credential is True @@ -4890,12 +5110,8 @@ class TestPerUserCredentialConfigServerResolution: manager = self._registry_only_manager(is_byok=True) store_mock = AsyncMock(return_value=None) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), patch.object(mgmt_endpoints, "global_mcp_server_manager", manager), patch.object(mgmt_endpoints, "store_user_credential", store_mock), ): @@ -4915,12 +5131,8 @@ class TestPerUserCredentialConfigServerResolution: manager.get_allowed_mcp_servers = AsyncMock(return_value=[]) store_mock = AsyncMock(return_value=None) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), patch.object(mgmt_endpoints, "global_mcp_server_manager", manager), patch.object( mgmt_endpoints, @@ -4932,9 +5144,7 @@ class TestPerUserCredentialConfigServerResolution: with pytest.raises(HTTPException) as exc: await mgmt_endpoints.store_mcp_oauth_user_credential( server_id=self.CONFIG_SERVER_ID, - payload=mgmt_endpoints.MCPOAuthUserCredentialRequest( - access_token="tok", expires_in=3600 - ), + payload=mgmt_endpoints.MCPOAuthUserCredentialRequest(access_token="tok", expires_in=3600), user_api_key_dict=generate_mock_user_api_key_auth( user_id="alice", user_role=LitellmUserRoles.INTERNAL_USER ), @@ -4947,17 +5157,11 @@ class TestPerUserCredentialConfigServerResolution: """A non-admin with the config server in their allowed set persists the token; proves the non-admin authz uses the registry-aware allowed set.""" manager = self._registry_only_manager() - manager.get_allowed_mcp_servers = AsyncMock( - return_value=[self.CONFIG_SERVER_ID] - ) + manager.get_allowed_mcp_servers = AsyncMock(return_value=[self.CONFIG_SERVER_ID]) store_mock = AsyncMock(return_value=None) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), patch.object(mgmt_endpoints, "global_mcp_server_manager", manager), patch.object( mgmt_endpoints, @@ -4973,9 +5177,7 @@ class TestPerUserCredentialConfigServerResolution: ): result = await mgmt_endpoints.store_mcp_oauth_user_credential( server_id=self.CONFIG_SERVER_ID, - payload=mgmt_endpoints.MCPOAuthUserCredentialRequest( - access_token="tok", expires_in=3600 - ), + payload=mgmt_endpoints.MCPOAuthUserCredentialRequest(access_token="tok", expires_in=3600), user_api_key_dict=generate_mock_user_api_key_auth( user_id="alice", user_role=LitellmUserRoles.INTERNAL_USER ), @@ -4997,22 +5199,14 @@ class TestPerUserCredentialConfigServerResolution: ) manager = MagicMock() manager.get_mcp_server_by_id = MagicMock( - return_value=generate_mock_mcp_server_config_record( - server_id=self.CONFIG_SERVER_ID - ) + return_value=generate_mock_mcp_server_config_record(server_id=self.CONFIG_SERVER_ID) ) manager._build_mcp_server_table = MagicMock(return_value=env_var_server) - manager.get_allowed_mcp_servers = AsyncMock( - return_value=[self.CONFIG_SERVER_ID] - ) + manager.get_allowed_mcp_servers = AsyncMock(return_value=[self.CONFIG_SERVER_ID]) merge_mock = AsyncMock(return_value={"CORP_USERNAME": "alice"}) with ( - patch.object( - mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock() - ), - patch.object( - mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None) - ), + patch.object(mgmt_endpoints, "get_prisma_client_or_throw", return_value=MagicMock()), + patch.object(mgmt_endpoints, "get_mcp_server", AsyncMock(return_value=None)), patch.object(mgmt_endpoints, "global_mcp_server_manager", manager), patch.object( mgmt_endpoints, @@ -5023,9 +5217,7 @@ class TestPerUserCredentialConfigServerResolution: ): result = await mgmt_endpoints.store_mcp_user_env_vars( server_id=self.CONFIG_SERVER_ID, - payload=mgmt_endpoints.MCPUserEnvVarsRequest( - values={"CORP_USERNAME": "alice"} - ), + payload=mgmt_endpoints.MCPUserEnvVarsRequest(values={"CORP_USERNAME": "alice"}), user_api_key_dict=generate_mock_user_api_key_auth( user_id="alice", user_role=LitellmUserRoles.INTERNAL_USER ), @@ -5034,3 +5226,153 @@ class TestPerUserCredentialConfigServerResolution: _, _, _, updates, _ = merge_mock.await_args.args assert updates == {"CORP_USERNAME": "alice"} assert result.server_id == self.CONFIG_SERVER_ID + + +def _oauth2_create_payload(**overrides): + base = dict( + server_name="stamp_test_server", + url="https://upstream.example.com/mcp", + transport=MCPTransport.http, + auth_type="oauth2", + ) + base.update(overrides) + return NewMCPServerRequest(**base) + + +def test_stamp_oauth2_flow_bare_oauth2_defaults_to_authorization_code(): + """A bare oauth2 create (no endpoints, no creds) is interactive: stamping it + authorization_code matches how needs_user_oauth_token treats a null flow.""" + payload = _oauth2_create_payload() + mgmt_endpoints.stamp_omitted_oauth2_flow(payload) + assert payload.oauth2_flow == "authorization_code" + + +def test_stamp_oauth2_flow_marks_m2m_shape_client_credentials(): + """token_url + full client credentials and no authorization_url is the M2M shape; + the stamp mirrors the legacy inference in _resolve_oauth2_flow so REST-created M2M + servers persist the flow instead of relying on read-time inference.""" + payload = _oauth2_create_payload( + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + mgmt_endpoints.stamp_omitted_oauth2_flow(payload) + assert payload.oauth2_flow == "client_credentials" + + +def test_stamp_oauth2_flow_authorization_url_wins_over_m2m_shape(): + """An authorization endpoint means interactive even when client creds + token_url + are present (GitHub Enterprise style); M2M never has an authorization endpoint.""" + payload = _oauth2_create_payload( + authorization_url="https://idp.example.com/authorize", + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + mgmt_endpoints.stamp_omitted_oauth2_flow(payload) + assert payload.oauth2_flow == "authorization_code" + + +def test_stamp_oauth2_flow_respects_explicit_value(): + """An explicit oauth2_flow from the caller must never be overridden by the stamp.""" + payload = _oauth2_create_payload( + oauth2_flow="authorization_code", + token_url="https://idp.example.com/token", + credentials={"client_id": "cid", "client_secret": "csecret"}, + ) + mgmt_endpoints.stamp_omitted_oauth2_flow(payload) + assert payload.oauth2_flow == "authorization_code" + + +def test_stamp_oauth2_flow_ignores_non_oauth2(): + payload = _oauth2_create_payload(auth_type="none") + mgmt_endpoints.stamp_omitted_oauth2_flow(payload) + assert payload.oauth2_flow is None + + +async def _run_edit(old_record, updated_record, purge_mock=None): + from litellm.proxy.management_endpoints.mcp_management_endpoints import edit_mcp_server + + server_id = updated_record.server_id + with ( + patch("litellm.proxy.management_endpoints.mcp_management_endpoints.MCP_AVAILABLE", True), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_prisma_client_or_throw", + return_value=MagicMock(), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.get_mcp_server", + AsyncMock(side_effect=old_record) + if isinstance(old_record, Exception) + else AsyncMock(return_value=old_record), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.update_mcp_server", + AsyncMock(return_value=updated_record), + ), + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.validate_and_normalize_mcp_server_payload", + autospec=True, + ), + patch("litellm.proxy.management_endpoints.mcp_management_endpoints.global_mcp_server_manager") as mock_manager, + patch( + "litellm.proxy.management_endpoints.mcp_management_endpoints.purge_user_oauth_credentials_for_server", + purge_mock if purge_mock is not None else AsyncMock(return_value=1), + ) as mock_purge, + ): + mock_manager.update_server = AsyncMock() + mock_manager.reload_servers_from_database = AsyncMock() + payload = UpdateMCPServerRequest(server_id=server_id, alias=updated_record.alias, url=updated_record.url) + user_auth = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) + result = await edit_mcp_server(payload=payload, user_api_key_dict=user_auth) + return result, mock_purge + + +@pytest.mark.asyncio +async def test_edit_mcp_server_purges_user_tokens_on_mint_relevant_change(): + server_id = str(uuid.uuid4()) + old = generate_mock_mcp_server_db_record(server_id=server_id, url="https://old.example.com/mcp") + updated = generate_mock_mcp_server_db_record(server_id=server_id, url="https://new.example.com/mcp") + + result, mock_purge = await _run_edit(old, updated) + + assert result.server_id == server_id + mock_purge.assert_awaited_once() + assert mock_purge.await_args.args[1] == server_id + + +@pytest.mark.asyncio +async def test_edit_mcp_server_skips_purge_when_identity_unchanged(): + server_id = str(uuid.uuid4()) + old = generate_mock_mcp_server_db_record(server_id=server_id, alias="Before") + updated = generate_mock_mcp_server_db_record(server_id=server_id, alias="After") + + result, mock_purge = await _run_edit(old, updated) + + assert result.server_id == server_id + mock_purge.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_edit_mcp_server_purge_failure_does_not_fail_the_edit(): + """The purge is best-effort: a purge exception after a successful update must be swallowed and + logged, never turned into an error response for an edit whose primary job already succeeded.""" + server_id = str(uuid.uuid4()) + old = generate_mock_mcp_server_db_record(server_id=server_id, url="https://old.example.com/mcp") + updated = generate_mock_mcp_server_db_record(server_id=server_id, url="https://new.example.com/mcp") + + result, mock_purge = await _run_edit(old, updated, purge_mock=AsyncMock(side_effect=RuntimeError("db down"))) + + assert result.server_id == server_id + mock_purge.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_edit_mcp_server_snapshot_failure_skips_purge_but_edit_succeeds(): + """The pre-update snapshot read is advisory (it only feeds the purge decision); a read failure + must skip the stale-token check with a warning, never fail the edit itself.""" + server_id = str(uuid.uuid4()) + updated = generate_mock_mcp_server_db_record(server_id=server_id, url="https://new.example.com/mcp") + + result, mock_purge = await _run_edit(RuntimeError("db read failed"), updated) + + assert result.server_id == server_id + mock_purge.assert_not_awaited() diff --git a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py index 6a81b1b613b..8c6bdefedae 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py @@ -436,28 +436,31 @@ class TestClearCache: clear_cache, ) - # Create mock router with mixed DB and config models + # Create mock router with mixed DB and config router deployments. The two DB + # entries are auto_router/* deployments (so their router-map entries should be + # cleared for reload); the config-defined router is preserved. mock_router = MagicMock() mock_router.model_list = [ { - "model_name": "gpt-4", + "model_name": "db-auto-router", "model_info": {"id": "db-model-1", "db_model": True}, - "litellm_params": {"model": "gpt-4"}, + "litellm_params": {"model": "auto_router/db-auto-router"}, }, { - "model_name": "gpt-3.5-turbo", + "model_name": "config-router", "model_info": {"id": "config-model-1", "db_model": False}, - "litellm_params": {"model": "gpt-3.5-turbo"}, + "litellm_params": {"model": "auto_router/complexity_router"}, }, { - "model_name": "claude-3", + "model_name": "db-complexity-router", "model_info": {"id": "db-model-2", "db_model": True}, - "litellm_params": {"model": "claude-3"}, + "litellm_params": {"model": "auto_router/complexity_router"}, }, ] mock_router.delete_deployment = MagicMock(return_value=True) - mock_router.auto_routers = MagicMock() - mock_router.auto_routers.clear = MagicMock() + # Real dicts (not MagicMock) so we can assert on their actual contents below. + mock_router.auto_routers = {"db-auto-router": MagicMock(), "config-router": MagicMock()} + mock_router.complexity_routers = {"db-complexity-router": MagicMock(), "config-router": MagicMock()} mock_config = MagicMock() mock_config.add_deployment = AsyncMock(return_value=True) @@ -479,8 +482,14 @@ class TestClearCache: mock_router.delete_deployment.assert_any_call(id="db-model-1") mock_router.delete_deployment.assert_any_call(id="db-model-2") - # Should have cleared auto routers - mock_router.auto_routers.clear.assert_called_once() + # DB-backed router entries are cleared so they can be re-populated by the + # reload below; the config-backed router must survive, since add_deployment() + # only reloads DB models and would otherwise leave it permanently unroutable + # (see TestClearCachePreservesConfigRouters). + assert "db-auto-router" not in mock_router.auto_routers + assert "db-complexity-router" not in mock_router.complexity_routers + assert "config-router" in mock_router.auto_routers + assert "config-router" in mock_router.complexity_routers # Should have called add_deployment to reload DB models mock_config.add_deployment.assert_called_once_with( @@ -488,6 +497,260 @@ class TestClearCache: ) +class TestClearCachePreservesConfigRouters: + """ + Regression test: clear_cache() must not wipe config-defined auto/complexity + routers. + + clear_cache() runs after any DB model write (e.g. a team admin patching a + team-owned model via PATCH /model/{id}/update). Before this fix, it called + auto_routers.clear() / complexity_routers.clear() unconditionally, which also + dropped routers defined in config.yaml belonging to *other* tenants. Those + entries are never restored, because the reload below only re-adds DB models + (proxy_config.add_deployment), so a config-defined router would stay + permanently unroutable until a full proxy restart - a cross-tenant + denial-of-service triggerable by any team admin's unrelated model update. + """ + + @pytest.mark.asyncio + async def test_config_backed_routers_survive_unrelated_db_model_update(self): + from litellm.proxy.management_endpoints.model_management_endpoints import ( + clear_cache, + ) + + mock_router = MagicMock() + mock_router.model_list = [ + { + "model_name": "team-a-db-router", + "model_info": {"id": "db-model-1", "db_model": True}, + "litellm_params": {"model": "auto_router/complexity_router"}, + }, + ] + mock_router.delete_deployment = MagicMock(return_value=True) + mock_router.auto_routers = {"config-semantic-router": MagicMock()} + mock_router.complexity_routers = { + "team-a-db-router": MagicMock(), + "config-defined-complexity-router": MagicMock(), + } + + mock_config = MagicMock() + mock_config.add_deployment = AsyncMock(return_value=True) + + with ( + patch("litellm.proxy.proxy_server.llm_router", mock_router), + patch("litellm.proxy.proxy_server.proxy_config", mock_config), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + patch("litellm.proxy.proxy_server.proxy_logging_obj", MagicMock()), + patch("litellm.proxy.proxy_server.verbose_proxy_logger"), + ): + await clear_cache() + + # The DB-backed router for the model that was actually updated is cleared + # so the reload below can re-populate it. + assert "team-a-db-router" not in mock_router.complexity_routers + # Config-defined routers for unrelated tenants must survive untouched. + assert "config-defined-complexity-router" in mock_router.complexity_routers + assert "config-semantic-router" in mock_router.auto_routers + + @pytest.mark.asyncio + async def test_config_router_sharing_name_with_regular_db_model_is_preserved(self): + """A config router must not be evicted just because a regular (non-router) DB + model happens to share its model_name; only DB deployments that are themselves + auto_router/* deployments should have their router entry cleared. + """ + from litellm.proxy.management_endpoints.model_management_endpoints import clear_cache + + mock_router = MagicMock() + mock_router.model_list = [ + { + "model_name": "shared-name", + "model_info": {"id": "db-model-1", "db_model": True}, + "litellm_params": {"model": "openai/gpt-4o"}, # a regular model, NOT a router + }, + ] + mock_router.delete_deployment = MagicMock(return_value=True) + # A config-defined complexity router registered under the same name as the DB model. + mock_router.auto_routers = {} + mock_router.complexity_routers = {"shared-name": MagicMock()} + + mock_config = MagicMock() + mock_config.add_deployment = AsyncMock(return_value=True) + + with ( + patch("litellm.proxy.proxy_server.llm_router", mock_router), + patch("litellm.proxy.proxy_server.proxy_config", mock_config), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + patch("litellm.proxy.proxy_server.proxy_logging_obj", MagicMock()), + patch("litellm.proxy.proxy_server.verbose_proxy_logger"), + ): + await clear_cache() + + # The DB model isn't a router, so the same-named config router must be left intact. + assert "shared-name" in mock_router.complexity_routers + + @pytest.mark.asyncio + async def test_db_quality_and_adaptive_routers_are_evicted(self): + """The auto_router/ prefix also covers quality_router/ and adaptive_router/. Their + registry entries must be popped too, or reload's init raises 'already exists' + (quality) or leaves a stale entry (adaptive). + """ + from litellm.proxy.management_endpoints.model_management_endpoints import clear_cache + + mock_router = MagicMock() + mock_router.model_list = [ + { + "model_name": "q1", + "model_info": {"id": "db-q", "db_model": True}, + "litellm_params": {"model": "auto_router/quality_router/q1"}, + }, + { + "model_name": "a1", + "model_info": {"id": "db-a", "db_model": True}, + "litellm_params": {"model": "auto_router/adaptive_router/a1"}, + }, + ] + mock_router.delete_deployment = MagicMock(return_value=True) + mock_router.auto_routers = {} + mock_router.complexity_routers = {} + mock_router.quality_routers = {"q1": MagicMock()} + mock_router.adaptive_routers = {"a1": MagicMock()} + + mock_config = MagicMock() + mock_config.add_deployment = AsyncMock(return_value=True) + + with ( + patch("litellm.proxy.proxy_server.llm_router", mock_router), + patch("litellm.proxy.proxy_server.proxy_config", mock_config), + patch("litellm.proxy.proxy_server.prisma_client", MagicMock()), + patch("litellm.proxy.proxy_server.proxy_logging_obj", MagicMock()), + patch("litellm.proxy.proxy_server.verbose_proxy_logger"), + ): + await clear_cache() + + assert "q1" not in mock_router.quality_routers + assert "a1" not in mock_router.adaptive_routers + + +class TestDeleteModelClearsRouterRegistry: + """delete_model must evict the deleted deployment from the auto/complexity router maps, + not just from model_list, or a stale (now unbacked) router entry lingers until restart. + """ + + @pytest.mark.asyncio + async def test_delete_model_pops_router_registries(self): + from litellm.proxy.management_endpoints.model_management_endpoints import ( + delete_model as delete_model_endpoint, + ) + from litellm.proxy.management_endpoints.model_management_endpoints import ModelInfoDelete + + model_id = "router-del-1" + admin_user = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) + db_row = LiteLLM_ProxyModelTable( + model_id=model_id, + model_name="smart-router", + litellm_params={"model": "auto_router/complexity_router"}, + model_info={"id": model_id}, + created_by="admin", + updated_by="admin", + ) + + mock_prisma = MagicMock() + mock_prisma.db = MagicMock() + mock_prisma.db.litellm_proxymodeltable = AsyncMock() + mock_prisma.db.litellm_proxymodeltable.find_unique = AsyncMock(return_value=db_row) + mock_prisma.db.litellm_proxymodeltable.delete = AsyncMock(return_value=db_row) + + mock_router = MagicMock() + mock_router.delete_deployment = MagicMock( + return_value={ + "model_name": "smart-router", + "litellm_params": {"model": "auto_router/complexity_router"}, + "model_info": {"id": model_id}, + } + ) + mock_router.auto_routers = {"smart-router": MagicMock()} + mock_router.complexity_routers = {"smart-router": MagicMock()} + + _PS = "litellm.proxy.proxy_server" + with ( + patch(f"{_PS}.prisma_client", mock_prisma), + patch(f"{_PS}.store_model_in_db", True), + patch(f"{_PS}.proxy_config", MagicMock()), + patch(f"{_PS}.proxy_logging_obj", MagicMock()), + patch(f"{_PS}.general_settings", {}), + patch(f"{_PS}.premium_user", True), + patch(f"{_PS}.llm_router", mock_router), + ): + await delete_model_endpoint( + model_info=ModelInfoDelete(id=model_id), + user_api_key_dict=admin_user, + ) + + mock_router.delete_deployment.assert_called_once_with(id=model_id) + assert "smart-router" not in mock_router.auto_routers + assert "smart-router" not in mock_router.complexity_routers + + @pytest.mark.asyncio + async def test_delete_regular_model_preserves_config_router_sharing_name(self): + """Deleting a regular (non-router) DB model must not evict a config-defined router + that merely shares its model_name. delete_deployment pops the DB model, but the + auto/complexity registries hold a config router under the same name that + add_deployment never restores, so an unguarded pop would make it permanently + unroutable (the same cross-tenant DoS clear_cache was hardened against). + """ + from litellm.proxy.management_endpoints.model_management_endpoints import ( + delete_model as delete_model_endpoint, + ) + from litellm.proxy.management_endpoints.model_management_endpoints import ModelInfoDelete + + model_id = "regular-del-1" + admin_user = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) + db_row = LiteLLM_ProxyModelTable( + model_id=model_id, + model_name="shared-name", + litellm_params={"model": "openai/gpt-4o"}, + model_info={"id": model_id}, + created_by="admin", + updated_by="admin", + ) + + mock_prisma = MagicMock() + mock_prisma.db = MagicMock() + mock_prisma.db.litellm_proxymodeltable = AsyncMock() + mock_prisma.db.litellm_proxymodeltable.find_unique = AsyncMock(return_value=db_row) + mock_prisma.db.litellm_proxymodeltable.delete = AsyncMock(return_value=db_row) + + mock_router = MagicMock() + mock_router.delete_deployment = MagicMock( + return_value={ + "model_name": "shared-name", + "litellm_params": {"model": "openai/gpt-4o"}, + "model_info": {"id": model_id}, + } + ) + config_router = MagicMock() + mock_router.auto_routers = {} + mock_router.complexity_routers = {"shared-name": config_router} + + _PS = "litellm.proxy.proxy_server" + with ( + patch(f"{_PS}.prisma_client", mock_prisma), + patch(f"{_PS}.store_model_in_db", True), + patch(f"{_PS}.proxy_config", MagicMock()), + patch(f"{_PS}.proxy_logging_obj", MagicMock()), + patch(f"{_PS}.general_settings", {}), + patch(f"{_PS}.premium_user", True), + patch(f"{_PS}.llm_router", mock_router), + ): + await delete_model_endpoint( + model_info=ModelInfoDelete(id=model_id), + user_api_key_dict=admin_user, + ) + + mock_router.delete_deployment.assert_called_once_with(id=model_id) + assert mock_router.complexity_routers.get("shared-name") is config_router + + class TestUpdateModel: """ Tests for the update_model (POST /model/update) handler. diff --git a/tests/test_litellm/proxy/management_endpoints/test_team_default_params.py b/tests/test_litellm/proxy/management_endpoints/test_team_default_params.py index 443089b5f01..e0b90332ca0 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_team_default_params.py +++ b/tests/test_litellm/proxy/management_endpoints/test_team_default_params.py @@ -426,6 +426,7 @@ class TestUpdateLitellmSettingOrdering: settings=new_settings, settings_key="default_team_params", success_message="Updated", + user_api_key_dict=UserAPIKeyAuth(user_id="test-admin"), ) # In-memory value should be the NEW value, not the stale one @@ -459,6 +460,7 @@ class TestUpdateLitellmSettingOrdering: settings=DefaultTeamSSOParams(max_budget=100.0), settings_key="default_team_params", success_message="Updated", + user_api_key_dict=UserAPIKeyAuth(user_id="test-admin"), ) assert exc_info.value.status_code == 500 diff --git a/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py index 5f3974b46fb..59b08a7ec4e 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py @@ -9495,3 +9495,372 @@ class TestEmitTeamMembersMetric: # A metric failure must be swallowed, not propagated to the handler. _emit_team_members_metric(self._team(1)) fake_logger.set_team_members_metric.assert_called_once() + + +@pytest.mark.asyncio +async def test_new_team_rejects_reserved_ui_session_team_id(): + """ + /team/new must reject team_id "litellm-dashboard" (UI_TEAM_ID): it is the + virtual team stamped on every UI dashboard session token, so a real DB row + with that id would bind its budget and permissions to every UI session. + """ + from fastapi import Request + + from litellm.proxy._types import UI_TEAM_ID, NewTeamRequest + from litellm.proxy.management_endpoints.team_endpoints import new_team + + team_request = NewTeamRequest( + team_alias="dashboard-clone", + team_id=UI_TEAM_ID, + ) + dummy_request = MagicMock(spec=Request) + + with ( + patch("litellm.proxy.proxy_server.prisma_client") as mock_prisma, + patch("litellm.proxy.proxy_server._license_check") as mock_license, + ): + mock_prisma.db.litellm_teamtable.count = AsyncMock(return_value=0) + mock_license.is_team_count_over_limit.return_value = False + mock_prisma.get_data = AsyncMock(return_value=None) + + with pytest.raises(ProxyException) as exc_info: + await new_team( + data=team_request, + http_request=dummy_request, + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN + ), + ) + + assert exc_info.value.code == "400" + assert "reserved" in str(exc_info.value.message) + mock_prisma.get_data.assert_not_called() + + +# --------------------------------------------------------------------------- +# PATCH /team/{team_id} — RFC 7386 JSON Merge Patch +# +# The new PATCH endpoint delegates to the same write path as POST /team/update; +# the single intended divergence is metadata. POST replaces the metadata column +# wholesale, PATCH merges it per RFC 7386 (omit preserves, null deletes, value +# overwrites, recursing into nested objects). Every other field must behave +# identically. Each test drives BOTH endpoints against an identical mocked team +# and asserts on the exact dict handed to litellm_teamtable.update. +# --------------------------------------------------------------------------- + +_PATCH_TEAM_ID = "team-merge-patch-test" +_ABSENT = object() + + +async def _drive_team_write( + kind, + *, + existing_metadata=None, + existing_kwargs=None, + payload=None, + raw_body=None, + user=None, + find_returns_none=False, + json_side_effect=None, +): + """Drive POST ``update_team`` or PATCH ``patch_team`` against a mocked team. + + Returns ``(endpoint_result, update_mock)``; propagates whatever the endpoint + raises. Inspect ``update_mock.call_args.kwargs["data"]`` for the DB write. + """ + from unittest.mock import AsyncMock, MagicMock, Mock + from unittest.mock import patch as _patch + + from fastapi import Request + + from litellm.proxy._types import ( + LiteLLM_TeamTable, + LitellmUserRoles, + UpdateTeamRequest, + UserAPIKeyAuth, + ) + from litellm.proxy.management_endpoints.team_endpoints import ( + patch_team, + update_team, + ) + + existing = LiteLLM_TeamTable( + team_id=_PATCH_TEAM_ID, + team_alias="t", + metadata=existing_metadata, + organization_id=None, + **(existing_kwargs or {}), + ) + auth = user or UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="u") + + with ( + _patch("litellm.proxy.proxy_server.prisma_client") as pc, + _patch("litellm.proxy.proxy_server.llm_router", None), + _patch("litellm.proxy.proxy_server.user_api_key_cache", MagicMock()), + _patch("litellm.proxy.proxy_server.proxy_logging_obj", MagicMock()), + _patch("litellm.proxy.proxy_server.litellm_proxy_admin_name", "admin"), + _patch( + "litellm.proxy.management_endpoints.team_endpoints._refresh_cached_team", + new=AsyncMock(), + ), + ): + pc.db.litellm_teamtable.find_unique = AsyncMock( + return_value=None if find_returns_none else existing + ) + pc.db.litellm_teamtable.update = AsyncMock( + return_value=LiteLLM_TeamTable(team_id=_PATCH_TEAM_ID, team_alias="t") + ) + pc.jsonify_team_object = MagicMock(side_effect=lambda db_data: db_data) + + req = Mock(spec=Request) + if kind == "post": + result = await update_team( + data=UpdateTeamRequest(team_id=_PATCH_TEAM_ID, **(payload or {})), + http_request=req, + user_api_key_dict=auth, + litellm_changed_by=None, + ) + else: + if json_side_effect is not None: + req.json = AsyncMock(side_effect=json_side_effect) + else: + req.json = AsyncMock( + return_value=raw_body if raw_body is not None else dict(payload or {}) + ) + result = await patch_team( + team_id=_PATCH_TEAM_ID, + http_request=req, + user_api_key_dict=auth, + litellm_changed_by=None, + ) + return result, pc.db.litellm_teamtable.update + + +async def _written_metadata(kind, existing_metadata, body): + _, update_mock = await _drive_team_write(kind, existing_metadata=existing_metadata, payload=body) + written = update_mock.call_args.kwargs["data"] + return written["metadata"] if "metadata" in written else _ABSENT + + +# (label, existing_metadata, merge_patch_body, expected_POST_metadata, expected_PATCH_metadata) +_METADATA_MAPPING = [ + ( + "omit-metadata-preserves-in-both", + {"cost_center": "1234"}, + {"tpm_limit": 5}, + _ABSENT, # POST: metadata column left untouched + _ABSENT, # PATCH: metadata column left untouched + ), + ( + "add-key-POST-wipes-others-PATCH-preserves", + {"cost_center": "1234", "foo": "bar"}, + {"metadata": {"foo": "baz"}}, + {"foo": "baz"}, # POST replaces wholesale -> cost_center wiped + {"cost_center": "1234", "foo": "baz"}, # PATCH merges -> cost_center kept + ), + ( + "overwrite-plus-null-delete-plus-add", + {"cost_center": "1234", "foo": "bar"}, + {"metadata": {"cost_center": "9999", "foo": None, "new": "x"}}, + {"cost_center": "9999", "foo": None, "new": "x"}, # POST stores the literal null + {"cost_center": "9999", "new": "x"}, # PATCH deletes foo via null + ), + ( + "null-delete-one-key", + {"a": 1, "b": 2}, + {"metadata": {"b": None}}, + {"b": None}, # POST wholesale replace -> only b:null survives + {"a": 1}, # PATCH deletes b, preserves a + ), + ( + "nested-object-deep-merge", + {"settings": {"x": 1, "y": 2}}, + {"metadata": {"settings": {"y": 3, "z": 4}}}, + {"settings": {"y": 3, "z": 4}}, # POST replaces the nested object wholesale + {"settings": {"x": 1, "y": 3, "z": 4}}, # PATCH deep-merges the nested object + ), + ( + "nested-object-null-delete", + {"settings": {"x": 1, "y": 2}}, + {"metadata": {"settings": {"x": None}}}, + {"settings": {"x": None}}, # POST wholesale + {"settings": {"y": 2}}, # PATCH deletes nested key, keeps sibling + ), + ( + "empty-object-POST-clears-PATCH-noops", + {"a": 1}, + {"metadata": {}}, + {}, # POST replaces with an empty object + {"a": 1}, # PATCH: an empty patch is a no-op + ), + ( + "metadata-null-clears-in-both", + {"a": 1}, + {"metadata": None}, + None, # POST clears the column + None, # PATCH: an RFC 7386 null patch clears the column too (parity) + ), +] + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "label, existing_metadata, body, expected_post, expected_patch", + _METADATA_MAPPING, + ids=[row[0] for row in _METADATA_MAPPING], +) +async def test_post_vs_patch_metadata_write_mapping( + label, existing_metadata, body, expected_post, expected_patch +): + """Exhaustive map: POST replaces metadata wholesale, PATCH merges per RFC 7386.""" + post_meta = await _written_metadata("post", existing_metadata, body) + patch_meta = await _written_metadata("patch", existing_metadata, body) + + assert post_meta == expected_post, f"POST metadata mismatch for '{label}'" + assert patch_meta == expected_patch, f"PATCH metadata mismatch for '{label}'" + + +@pytest.mark.asyncio +async def test_patch_preserves_required_metadata_key_that_post_would_wipe(): + """The reason PATCH exists: editing one metadata key must not silently drop + the others, which POST /team/update does because it replaces wholesale.""" + existing = {"cost_center": "FINOPS-1", "team_notes": "keep me"} + body = {"metadata": {"team_notes": "edited"}} + + post_meta = await _written_metadata("post", existing, body) + patch_meta = await _written_metadata("patch", existing, body) + + assert post_meta == {"team_notes": "edited"} + assert "cost_center" not in post_meta # wiped by POST + assert patch_meta == {"cost_center": "FINOPS-1", "team_notes": "edited"} # preserved by PATCH + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "body, field, expected", + [ + ({"tpm_limit": 50}, "tpm_limit", 50), + ({"tpm_limit": None}, "tpm_limit", None), + ({"models": ["gpt-4", "claude-3"]}, "models", ["gpt-4", "claude-3"]), + ({"blocked": True}, "blocked", True), + ({"max_budget": 10.0}, "max_budget", 10.0), + ], +) +async def test_top_level_fields_identical_post_and_patch(body, field, expected): + """Non-metadata fields are unaffected by merge semantics: value overwrites in both, + and neither touches metadata when the patch omits it.""" + _, post_update = await _drive_team_write("post", existing_metadata={"k": "v"}, payload=body) + _, patch_update = await _drive_team_write("patch", existing_metadata={"k": "v"}, payload=body) + post_written = post_update.call_args.kwargs["data"] + patch_written = patch_update.call_args.kwargs["data"] + + assert post_written[field] == expected + assert patch_written[field] == expected + assert "metadata" not in post_written + assert "metadata" not in patch_written + + +@pytest.mark.asyncio +async def test_patch_strips_system_managed_metadata_key_like_post(): + """A caller cannot inject/overwrite server-owned keys via PATCH any more than + via POST: team_member_budget_id is stripped from the write in both.""" + existing = {"team_member_budget_id": "budget-123", "cost_center": "1234"} + body = {"metadata": {"team_member_budget_id": "HACKED", "cost_center": "9999"}} + + post_meta = await _written_metadata("post", existing, body) + patch_meta = await _written_metadata("patch", existing, body) + + assert "team_member_budget_id" not in post_meta + assert "team_member_budget_id" not in patch_meta + assert post_meta == {"cost_center": "9999"} + assert patch_meta == {"cost_center": "9999"} + + +@pytest.mark.asyncio +@pytest.mark.parametrize("raw_body", [["not", "an", "object"], "a-string", 42, True]) +async def test_patch_rejects_non_object_body(raw_body): + from litellm.proxy._types import ProxyException + + with pytest.raises(ProxyException) as exc: + await _drive_team_write("patch", existing_metadata={"a": 1}, raw_body=raw_body) + assert exc.value.code == "400" or exc.value.code == 400 + + +@pytest.mark.asyncio +async def test_patch_rejects_invalid_json_body(): + from litellm.proxy._types import ProxyException + + with pytest.raises(ProxyException) as exc: + await _drive_team_write( + "patch", existing_metadata={"a": 1}, json_side_effect=ValueError("no body") + ) + assert exc.value.code == "400" or exc.value.code == 400 + + +@pytest.mark.asyncio +async def test_patch_rejects_team_id_mismatch_between_path_and_body(): + from litellm.proxy._types import ProxyException + + with pytest.raises(ProxyException) as exc: + await _drive_team_write( + "patch", + existing_metadata={"a": 1}, + raw_body={"team_id": "some-other-team", "tpm_limit": 5}, + ) + assert exc.value.code == "400" or exc.value.code == 400 + + +@pytest.mark.asyncio +async def test_patch_accepts_matching_team_id_in_body(): + """A body team_id equal to the path is tolerated and does not leak into the write.""" + _, update_mock = await _drive_team_write( + "patch", + existing_metadata={"a": 1}, + raw_body={"team_id": _PATCH_TEAM_ID, "tpm_limit": 7}, + ) + written = update_mock.call_args.kwargs["data"] + assert written["tpm_limit"] == 7 + + +@pytest.mark.asyncio +async def test_patch_team_not_found_returns_404(): + from litellm.proxy._types import ProxyException + + # metadata present -> patch_team does its own existence check + with pytest.raises(ProxyException) as exc: + await _drive_team_write( + "patch", raw_body={"metadata": {"cost_center": "1"}}, find_returns_none=True + ) + assert exc.value.code == "404" or exc.value.code == 404 + + # metadata absent -> existence check happens in the delegated update_team + with pytest.raises(ProxyException) as exc2: + await _drive_team_write("patch", raw_body={"tpm_limit": 5}, find_returns_none=True) + assert exc2.value.code == "404" or exc2.value.code == 404 + + +@pytest.mark.asyncio +async def test_patch_enforces_team_access_via_delegation(): + """PATCH inherits POST's team-level RBAC: a caller who is neither proxy admin, + team admin, nor org admin of the team is rejected.""" + from litellm.proxy._types import LitellmUserRoles, ProxyException, UserAPIKeyAuth + + outsider = UserAPIKeyAuth(user_role=LitellmUserRoles.INTERNAL_USER, user_id="outsider") + with pytest.raises(ProxyException) as exc: + await _drive_team_write( + "patch", raw_body={"tpm_limit": 5}, user=outsider + ) + assert exc.value.code == "403" or exc.value.code == 403 + + +@pytest.mark.asyncio +async def test_patch_returns_full_team_object_not_wrapper(): + """Per REST convention the PATCH response is the full team, not POST's + {"team_id", "data"} envelope.""" + from litellm.proxy._types import LiteLLM_TeamTable + + result, _ = await _drive_team_write( + "patch", existing_metadata={"a": 1}, raw_body={"metadata": {"b": 2}} + ) + assert isinstance(result, LiteLLM_TeamTable) + assert result.team_id == _PATCH_TEAM_ID diff --git a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py index 976048b9521..045e15f8b8b 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -389,6 +389,78 @@ async def test_get_user_groups_error_handling(): assert len(result) == 0 +@pytest.mark.asyncio +async def test_get_user_groups_uses_default_graph_endpoint(monkeypatch): + monkeypatch.delenv("MICROSOFT_GRAPH_ENDPOINT", raising=False) + + requested_urls: list[str] = [] + + async def mock_get(url, *args, **kwargs): + requested_urls.append(url) + mock = MagicMock() + mock.json.return_value = {"value": []} + return mock + + with patch( + "litellm.proxy.management_endpoints.ui_sso.get_async_httpx_client" + ) as mock_client: + mock_client.return_value = MagicMock() + mock_client.return_value.get = mock_get + + await MicrosoftSSOHandler.get_user_groups_from_graph_api(access_token="mock_token") + + assert requested_urls == ["https://graph.microsoft.com/v1.0/me/memberOf"] + + +@pytest.mark.asyncio +async def test_get_user_groups_uses_configured_graph_endpoint(monkeypatch): + monkeypatch.setenv("MICROSOFT_GRAPH_ENDPOINT", "https://graph.microsoft.us/v1.0") + + requested_urls: list[str] = [] + + async def mock_get(url, *args, **kwargs): + requested_urls.append(url) + mock = MagicMock() + mock.json.return_value = {"value": []} + return mock + + with patch( + "litellm.proxy.management_endpoints.ui_sso.get_async_httpx_client" + ) as mock_client: + mock_client.return_value = MagicMock() + mock_client.return_value.get = mock_get + + await MicrosoftSSOHandler.get_user_groups_from_graph_api(access_token="mock_token") + + assert requested_urls == ["https://graph.microsoft.us/v1.0/me/memberOf"] + + +@pytest.mark.asyncio +async def test_get_group_ids_from_service_principal_uses_configured_graph_endpoint(monkeypatch): + monkeypatch.setenv("MICROSOFT_GRAPH_ENDPOINT", "https://graph.microsoft.us/v1.0") + + requested_urls: list[str] = [] + + async def mock_get(url, *args, **kwargs): + requested_urls.append(url) + mock = MagicMock() + mock.json.return_value = {"value": []} + return mock + + async_client = MagicMock() + async_client.get = mock_get + + await MicrosoftSSOHandler.get_group_ids_from_service_principal( + service_principal_id="sp-123", + async_client=async_client, + access_token="mock_token", + ) + + assert requested_urls == [ + "https://graph.microsoft.us/v1.0/servicePrincipals/sp-123/appRoleAssignedTo" + ] + + def test_get_group_ids_from_graph_api_response(): # Arrange mock_response = MicrosoftGraphAPIUserGroupResponse( @@ -2711,6 +2783,7 @@ class TestCLIKeyRegenerationFlow: # Mock request mock_request = MagicMock(spec=Request) + mock_request.query_params = {"code": "some-auth-code"} cli_state = f"{LITELLM_CLI_SESSION_TOKEN_PREFIX}:cli-new-session-key-456" @@ -2752,6 +2825,7 @@ class TestCLIKeyRegenerationFlow: from litellm.proxy.management_endpoints.ui_sso import auth_callback mock_request = MagicMock(spec=Request) + mock_request.query_params = {"code": "some-auth-code"} cli_state = ( f"{LITELLM_CLI_SESSION_TOKEN_PREFIX}:cli-new-session-key-456:WXYZ-2345" ) @@ -7131,3 +7205,112 @@ async def test_legacy_login_page_hides_credentials_hint_via_general_settings(): assert response.status_code == 200 assert "Default Credentials" not in body assert "MASTER_KEY" not in body + + +@pytest.mark.asyncio +async def test_cli_poll_key_tolerates_missing_user_row(): + """The CLI poll must still mint the JWT when the user lookup raises, + e.g. the user row was created moments ago and a negative-cache window + from the pre-creation SSO existence check is still active on this pod.""" + from litellm.proxy.management_endpoints.ui_sso import ( + _hash_cli_sso_secret, + cli_poll_key, + ) + + session_key = "cli-session-missing-user" + session_data = { + "user_id": "just-created-user", + "user_role": "internal_user", + "teams": [], + "models": ["gpt-4"], + } + + mock_cache = MagicMock() + mock_cache.get_cache.return_value = { + "poll_secret_hash": _hash_cli_sso_secret("poll-secret"), + "sso_complete": True, + "user_code_verified": True, + "session_data": session_data, + } + + mock_jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.missing.user" + + with ( + patch("litellm.proxy.proxy_server.user_api_key_cache", mock_cache), + patch("litellm.proxy.proxy_server.prisma_client"), + patch( + "litellm.proxy.auth.auth_checks.ExperimentalUIJWTToken.get_cli_jwt_auth_token", + return_value=mock_jwt_token, + ), + patch( + "litellm.proxy.auth.auth_checks.get_user_object", + new=AsyncMock(side_effect=ValueError("User doesn't exist in db. 'user_id'=just-created-user")), + ), + ): + result = await cli_poll_key( + key_id=session_key, + team_id=None, + x_litellm_cli_poll_secret="poll-secret", + ) + + assert result["status"] == "ready" + assert result["key"] == mock_jwt_token + assert result["user_id"] == "just-created-user" + + +def _make_sso_callback_request(query_params: dict) -> MagicMock: + mock_request = MagicMock(spec=Request) + mock_request.query_params = query_params + return mock_request + + +@pytest.mark.asyncio +async def test_auth_callback_surfaces_oauth_error_with_description(): + """ + Regression: when the IdP denies access it redirects back with + ?error=...&error_description=... and no `code`. The callback must surface + that reason as a 401 instead of failing later on the missing `code` param. + """ + from litellm.proxy.management_endpoints.ui_sso import auth_callback + + mock_request = _make_sso_callback_request( + {"error": "access_denied", "error_description": "User is not assigned to the client application"} + ) + + with pytest.raises(HTTPException) as exc_info: + await auth_callback(request=mock_request, state=None) + + assert exc_info.value.status_code == 401 + assert "access_denied" in str(exc_info.value.detail) + assert "User is not assigned to the client application" in str(exc_info.value.detail) + + +@pytest.mark.asyncio +async def test_auth_callback_surfaces_oauth_error_without_description(): + """error_description is optional in the OAuth error response; the 401 detail must not render 'None'.""" + from litellm.proxy.management_endpoints.ui_sso import auth_callback + + mock_request = _make_sso_callback_request({"error": "access_denied"}) + + with pytest.raises(HTTPException) as exc_info: + await auth_callback(request=mock_request, state=None) + + assert exc_info.value.status_code == 401 + assert "access_denied" in str(exc_info.value.detail) + assert "None" not in str(exc_info.value.detail) + assert "error_description" not in str(exc_info.value.detail) + + +@pytest.mark.asyncio +async def test_auth_callback_without_oauth_error_proceeds_to_normal_flow(): + """Without an `error` query param the guard must not fire; the callback proceeds into the normal flow.""" + from litellm.proxy.management_endpoints.ui_sso import auth_callback + + mock_request = _make_sso_callback_request({"code": "some-auth-code"}) + + with patch("litellm.proxy.proxy_server.prisma_client", None): + with pytest.raises(HTTPException) as exc_info: + await auth_callback(request=mock_request, state=None) + + assert exc_info.value.status_code == 500 + assert "DB not connected" in str(exc_info.value.detail) diff --git a/tests/test_litellm/proxy/management_helpers/test_object_permission_utils.py b/tests/test_litellm/proxy/management_helpers/test_object_permission_utils.py index 26c8c774812..d797a27aa67 100644 --- a/tests/test_litellm/proxy/management_helpers/test_object_permission_utils.py +++ b/tests/test_litellm/proxy/management_helpers/test_object_permission_utils.py @@ -9,13 +9,19 @@ sys.path.insert(0, os.path.abspath("../../../..")) from unittest.mock import AsyncMock, MagicMock, patch -from litellm.proxy._types import LiteLLM_ObjectPermissionBase, LiteLLM_ObjectPermissionTable, ObjectPermissionDict +from litellm.proxy._types import ( + LiteLLM_ObjectPermissionBase, + LiteLLM_ObjectPermissionTable, + ObjectPermissionDict, + SpecialMCPServerName, +) from litellm.proxy.management_helpers.object_permission_utils import ( _extract_requested_mcp_access_groups, _extract_requested_mcp_server_ids, _resolve_team_allowed_mcp_servers, _rewrite_object_permission_mcp_servers, _set_object_permission, + enforce_all_proxy_mcp_servers_grant_is_admin_only, validate_key_mcp_servers_against_team, validate_key_search_tools_against_team, validate_key_vector_stores_against_team, @@ -87,6 +93,38 @@ async def test_set_object_permission(): assert result["models"] == ["gpt-4"] +@pytest.mark.asyncio +async def test_set_object_permission_persists_mcp_tool_search_enabled(): + """ + Regression: mcp_tool_search_enabled must be carried into the Prisma create + payload so it persists to LiteLLM_ObjectPermissionTable. The field was + present on the Pydantic models but missing from the create path, so keys + generated with mcp_tool_search_enabled=True silently lost the flag. + """ + mock_prisma_client = MagicMock() + mock_created_permission = MagicMock() + mock_created_permission.object_permission_id = "perm_id" + mock_prisma_client.db.litellm_objectpermissiontable.create = AsyncMock( + return_value=mock_created_permission + ) + + data_json = { + "object_permission": { + "mcp_servers": ["server_a"], + "mcp_tool_search_enabled": True, + }, + } + + await _set_object_permission(data_json=data_json, prisma_client=mock_prisma_client) + + created_data = ( + mock_prisma_client.db.litellm_objectpermissiontable.create.call_args.kwargs[ + "data" + ] + ) + assert created_data["mcp_tool_search_enabled"] is True + + # ---- Tests for _extract_requested_mcp_server_ids ---- @@ -844,6 +882,172 @@ async def test_resolve_team_allowed_mcp_servers_dict_tool_permissions( assert result == {"server-a"} +# ---- Tests for the all-proxy-mcpservers sentinel (team scoped to every server) ---- + + +@pytest.mark.asyncio +@patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.MCPRequestHandler._get_mcp_servers_from_access_groups", + new_callable=AsyncMock, + return_value=[], +) +async def test_resolve_team_all_proxy_sentinel_resolves_dynamically(mock_access_groups): + """A team whose object_permission.mcp_servers holds the all-proxy sentinel + resolves to every registered server id, and picks up a server registered + later without any change to the team's stored permission (this kills the + early-return that maps the sentinel to the live registry).""" + registry = { + "srv-x": _make_mock_mcp_server("srv-x"), + "srv-y": _make_mock_mcp_server("srv-y"), + } + mock_mgr = MagicMock() + mock_mgr.get_registry.return_value = registry + + team_perm = MagicMock(spec=LiteLLM_ObjectPermissionTable) + team_perm.mcp_servers = [SpecialMCPServerName.all_proxy_servers.value] + team_perm.mcp_access_groups = [] + team_perm.mcp_tool_permissions = {} + + with patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager", + mock_mgr, + ): + assert await _resolve_team_allowed_mcp_servers(team_perm) == {"srv-x", "srv-y"} + + registry["srv-z"] = _make_mock_mcp_server("srv-z") + assert await _resolve_team_allowed_mcp_servers(team_perm) == { + "srv-x", + "srv-y", + "srv-z", + } + + +@pytest.mark.asyncio +@patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager", + new=_make_mock_mcp_manager("srv-x", "srv-y", "srv-z"), +) +@patch( + "litellm.proxy.management_helpers.object_permission_utils._get_allow_all_keys_server_ids", + return_value=set(), +) +@patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.MCPRequestHandler._get_mcp_servers_from_access_groups", + new_callable=AsyncMock, + return_value=[], +) +async def test_validate_key_scoped_to_server_added_after_team_all_proxy( + mock_access_groups, mock_allow_all +): + """The exact user scenario: a team scoped to the all-proxy sentinel, a server + (srv-z) registered afterwards, and a key scoped to just srv-z. Because the + team ceiling resolves to every registered server, the key passes validation + and keeps srv-z in its normalized permission.""" + team_obj = _make_team_obj(mcp_servers=[SpecialMCPServerName.all_proxy_servers.value]) + object_permission = {"mcp_servers": ["srv-z"]} + result = await validate_key_mcp_servers_against_team( + object_permission=object_permission, + team_obj=team_obj, + ) + assert result is not None + assert result["mcp_servers"] == ["srv-z"] + + +@pytest.mark.asyncio +@patch( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager", + new=_make_mock_mcp_manager("srv-x", "srv-z"), +) +@patch( + "litellm.proxy.management_helpers.object_permission_utils._get_allow_all_keys_server_ids", + return_value=set(), +) +@patch( + "litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp.MCPRequestHandler._get_mcp_servers_from_access_groups", + new_callable=AsyncMock, + return_value=[], +) +async def test_validate_key_scoped_to_server_rejected_when_team_not_all_proxy( + mock_access_groups, mock_allow_all +): + """Contrast with the sentinel case: a team scoped to a concrete server list + (srv-x, not the sentinel) does NOT unlock srv-z for a key. It is the sentinel + specifically, not a blanket allow, that widens the team ceiling.""" + team_obj = _make_team_obj(mcp_servers=["srv-x"]) + with pytest.raises(HTTPException) as exc_info: + await validate_key_mcp_servers_against_team( + object_permission={"mcp_servers": ["srv-z"]}, + team_obj=team_obj, + ) + assert exc_info.value.status_code == 403 + assert "srv-z" in str(exc_info.value.detail) + + +# ---- Tests for the proxy-admin gate on granting a team the all-proxy sentinel ---- + + +@pytest.mark.asyncio +async def test_enforce_all_proxy_mcp_grant_blocks_non_admin_adding_sentinel(): + """A non-proxy-admin (e.g. a team admin) cannot newly grant a team the all-proxy + MCP sentinel. Without this gate a team admin could self-escalate their team to + every MCP server on the proxy via team create/update.""" + with pytest.raises(HTTPException) as exc_info: + await enforce_all_proxy_mcp_servers_grant_is_admin_only( + requested_mcp_servers=[SpecialMCPServerName.all_proxy_servers.value], + existing_object_permission_id=None, + is_proxy_admin=False, + prisma_client=None, + ) + assert exc_info.value.status_code == 403 + assert "all-proxy-mcpservers" in str(exc_info.value.detail) + + +@pytest.mark.asyncio +async def test_enforce_all_proxy_mcp_grant_allows_proxy_admin(): + """A proxy admin may grant the sentinel — the intended way to scope a team to all + proxy MCP servers.""" + await enforce_all_proxy_mcp_servers_grant_is_admin_only( + requested_mcp_servers=[SpecialMCPServerName.all_proxy_servers.value], + existing_object_permission_id=None, + is_proxy_admin=True, + prisma_client=None, + ) + + +@pytest.mark.asyncio +async def test_enforce_all_proxy_mcp_grant_allows_non_admin_without_sentinel(): + """A non-admin scoping a team to concrete servers is unaffected by the gate.""" + await enforce_all_proxy_mcp_servers_grant_is_admin_only( + requested_mcp_servers=["srv-x", "srv-y"], + existing_object_permission_id=None, + is_proxy_admin=False, + prisma_client=None, + ) + + +@pytest.mark.asyncio +async def test_enforce_all_proxy_mcp_grant_allows_non_admin_when_sentinel_already_set(): + """The gate blocks only NEW grants: a non-admin editing a team a proxy admin + already scoped to all-proxy is not forced to strip the sentinel, so unrelated + edits still succeed. The existing permission is read from the DB by id.""" + existing_row = MagicMock() + existing_row.mcp_servers = [SpecialMCPServerName.all_proxy_servers.value] + mock_repo = MagicMock() + mock_repo.table.find_unique = AsyncMock(return_value=existing_row) + + with patch( + "litellm.proxy.management_helpers.object_permission_utils.ObjectPermissionRepository", + return_value=mock_repo, + ): + await enforce_all_proxy_mcp_servers_grant_is_admin_only( + requested_mcp_servers=[SpecialMCPServerName.all_proxy_servers.value], + existing_object_permission_id="op-1", + is_proxy_admin=False, + prisma_client=MagicMock(), + ) + mock_repo.table.find_unique.assert_awaited_once() + + # ---- Tests for validate_key_search_tools_against_team ---- diff --git a/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py b/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py index 8bb7b52af14..cf3351c4ff8 100644 --- a/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py +++ b/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py @@ -1918,7 +1918,8 @@ class TestForwardHeaders: ): # Setup mock httpx client mock_client = MagicMock() - mock_client.request = AsyncMock(return_value=mock_httpx_response) + mock_client.build_request = MagicMock(return_value=MagicMock()) + mock_client.send = AsyncMock(return_value=mock_httpx_response) mock_client_obj = MagicMock() mock_client_obj.client = mock_client mock_get_client.return_value = mock_client_obj @@ -1942,10 +1943,10 @@ class TestForwardHeaders: ) # Verify the httpx client was called - assert mock_client.request.called + assert mock_client.send.called # Get the headers that were sent to the target - call_args = mock_client.request.call_args + call_args = mock_client.build_request.call_args sent_headers = call_args[1]["headers"] # Verify user headers were forwarded (except content-length and host) @@ -2019,7 +2020,8 @@ class TestForwardHeaders: ): # Setup mock httpx client mock_client = MagicMock() - mock_client.request = AsyncMock(return_value=mock_httpx_response) + mock_client.build_request = MagicMock(return_value=MagicMock()) + mock_client.send = AsyncMock(return_value=mock_httpx_response) mock_client_obj = MagicMock() mock_client_obj.client = mock_client mock_get_client.return_value = mock_client_obj @@ -2043,10 +2045,10 @@ class TestForwardHeaders: ) # Verify the httpx client was called - assert mock_client.request.called + assert mock_client.send.called # Get the headers that were sent to the target - call_args = mock_client.request.call_args + call_args = mock_client.build_request.call_args sent_headers = call_args[1]["headers"] # Verify only custom headers were sent diff --git a/tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py b/tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py index 2f609a1c7e4..89d100cc3a4 100644 --- a/tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py +++ b/tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py @@ -1,9 +1,12 @@ +import asyncio import json +import logging import os import sys from contextlib import ExitStack from io import BytesIO from types import SimpleNamespace +from typing import Optional from unittest.mock import AsyncMock, MagicMock, patch import httpx @@ -28,6 +31,7 @@ from litellm.proxy.pass_through_endpoints.pass_through_endpoints import ( resolve_pass_through_request_timeout, resolve_llm_passthrough_timeout, ) +from litellm.proxy._types import UserAPIKeyAuth from litellm.types.passthrough_endpoints.pass_through_endpoints import ( LITELLM_PASS_THROUGH_RAW_BODY_STATE_KEY, ) @@ -1334,7 +1338,8 @@ async def test_pass_through_request_sse_response_marks_logging_obj_as_stream(): upstream_response.raise_for_status = MagicMock() async_client = MagicMock() - async_client.request = AsyncMock(return_value=upstream_response) + async_client.build_request = MagicMock(return_value=MagicMock()) + async_client.send = AsyncMock(return_value=upstream_response) mock_get_client.return_value = MagicMock(client=async_client) async def _empty_chunks(*args, **kwargs): @@ -1358,7 +1363,7 @@ async def test_pass_through_request_sse_response_marks_logging_obj_as_stream(): stream=False, ) - async_client.request.assert_awaited_once() + async_client.send.assert_awaited_once() mock_chunk_processor.assert_called_once() logging_obj = mock_chunk_processor.call_args.kwargs[ @@ -2250,6 +2255,165 @@ async def test_pass_through_request_query_params_forwarding(): assert call_kwargs["_parsed_body"] == test_body +class _FakeManagedFilesHook: + def __init__(self, file_row: SimpleNamespace): + self._file_row = file_row + + async def get_unified_file_id(self, file_id: str, litellm_parent_otel_span=None) -> SimpleNamespace: + return self._file_row + + +async def _run_pass_through_and_capture_wire_url( + target: str, + incoming_query: str, + merge_query_params: bool = False, + default_query_params: Optional[dict] = None, + custom_llm_provider: Optional[str] = None, + managed_files_hook: Optional[_FakeManagedFilesHook] = None, + user_api_key_dict: Optional[UserAPIKeyAuth] = None, +) -> httpx.URL: + import litellm + from litellm.llms.custom_httpx.http_handler import get_async_httpx_client + from litellm.types.llms.custom_http import httpxSpecialProvider + + recorded_requests = [] + + def transport_handler(upstream_request: httpx.Request) -> httpx.Response: + recorded_requests.append(upstream_request) + return httpx.Response(200, json={"ok": True}) + + real_handler = get_async_httpx_client( + llm_provider=httpxSpecialProvider.PassThroughEndpoint, + params={"timeout": resolve_pass_through_request_timeout(None)}, + ) + cache_dict = litellm.in_memory_llm_clients_cache.cache_dict + cache_key = next((key for key, cached in cache_dict.items() if cached is real_handler), None) + assert cache_key is not None, ( + "PassThroughEndpoint client not found in in_memory_llm_clients_cache; " + "get_async_httpx_client may not be caching this provider." + ) + cache_dict[cache_key] = SimpleNamespace( + client=httpx.AsyncClient(transport=httpx.MockTransport(transport_handler)) + ) + + mock_request = MagicMock(spec=Request) + mock_request.method = "GET" + mock_request.headers = Headers({}) + mock_request.query_params = QueryParams(incoming_query) + mock_request.body = AsyncMock(return_value=b"") + + mock_proxy_logging = MagicMock() + mock_proxy_logging.pre_call_hook = AsyncMock( + side_effect=lambda user_api_key_dict, data, call_type: data + ) + mock_proxy_logging.post_call_failure_hook = AsyncMock() + mock_proxy_logging.post_call_response_headers_hook = AsyncMock(return_value={}) + mock_proxy_logging.get_proxy_hook = MagicMock(return_value=managed_files_hook) + + try: + with ExitStack() as stack: + stack.enter_context( + patch("litellm.proxy.proxy_server.proxy_logging_obj", mock_proxy_logging) + ) + if managed_files_hook is not None: + stack.enter_context( + patch( + "litellm.proxy.proxy_server.general_settings", + {"passthrough_managed_object_ids": True}, + ) + ) + stack.enter_context(patch("litellm.proxy.proxy_server.prisma_client", None)) + response = await pass_through_request( + request=mock_request, + target=target, + custom_headers={}, + user_api_key_dict=user_api_key_dict if user_api_key_dict is not None else MagicMock(), + merge_query_params=merge_query_params, + default_query_params=default_query_params, + custom_llm_provider=custom_llm_provider, + ) + finally: + cache_dict[cache_key] = real_handler + + assert response.status_code == 200 + assert len(recorded_requests) == 1 + return recorded_requests[0].url + + +@pytest.mark.asyncio +async def test_pass_through_request_merge_query_params_preserves_target_query_on_wire(): + """ + Regression test: with merge_query_params=True, the target URL's own query + params must survive on the final outgoing request. Passing the incoming + params via httpx's params= replaces the URL's entire query string, which + used to silently drop the merged target params. + """ + wire_url = await _run_pass_through_and_capture_wire_url( + target="https://www.bing.com/search?setLang=en-US&mkt=en-US", + incoming_query="q=litellm", + merge_query_params=True, + ) + assert dict(wire_url.params) == { + "setLang": "en-US", + "mkt": "en-US", + "q": "litellm", + } + + +@pytest.mark.asyncio +async def test_pass_through_request_default_query_params_reach_the_wire(): + """ + default_query_params are sent with every request and can be overridden + per-key by client-provided query params; params the client does not + override must not be dropped from the outgoing request. + """ + wire_url = await _run_pass_through_and_capture_wire_url( + target="https://example.com/api", + incoming_query="limit=5&api-version=client-version", + default_query_params={"api-version": "2024-01-01", "setLang": "en-US"}, + ) + assert dict(wire_url.params) == { + "api-version": "client-version", + "setLang": "en-US", + "limit": "5", + } + + +@pytest.mark.asyncio +async def test_pass_through_request_without_merge_replaces_target_query(): + wire_url = await _run_pass_through_and_capture_wire_url( + target="https://www.bing.com/search?setLang=en-US", + incoming_query="q=litellm", + ) + assert dict(wire_url.params) == {"q": "litellm"} + + +@pytest.mark.asyncio +async def test_pass_through_request_merge_query_params_rewrites_managed_ids_on_the_wire(): + """ + Regression test: on merge-enabled endpoints the managed-ID rewrite must see + the incoming query params before they are folded into the URL. Folding + first bakes the un-rewritten managed ID into the URL and hands the rewriter + None, leaking the managed ID upstream. + """ + from litellm.proxy.pass_through_endpoints.managed_id_codec import new_managed_id + + managed_id = new_managed_id("openai", "file-raw-123") + hook = _FakeManagedFilesHook(SimpleNamespace(created_by="user-1", team_id=None)) + wire_url = await _run_pass_through_and_capture_wire_url( + target="https://api.openai.com/v1/files/content?api-version=preview", + incoming_query=f"file_id={managed_id}", + merge_query_params=True, + custom_llm_provider="openai", + managed_files_hook=hook, + user_api_key_dict=UserAPIKeyAuth(user_id="user-1"), + ) + assert dict(wire_url.params) == { + "api-version": "preview", + "file_id": "file-raw-123", + } + + @pytest.mark.asyncio async def test_pass_through_with_httpbin_redirect(): """ @@ -2884,7 +3048,8 @@ async def test_pass_through_request_non_streaming_uses_content_for_state_raw_bod ) mock_async_client = AsyncMock() - mock_async_client.request = AsyncMock(return_value=upstream) + mock_async_client.build_request = MagicMock(return_value=MagicMock()) + mock_async_client.send = AsyncMock(return_value=upstream) mock_client_obj = MagicMock() mock_client_obj.client = mock_async_client @@ -2920,10 +3085,12 @@ async def test_pass_through_request_non_streaming_uses_content_for_state_raw_bod stream=False, ) - mock_async_client.request.assert_called_once() - req_kw = mock_async_client.request.call_args[1] - assert req_kw.get("content") == raw_signed - assert "json" not in req_kw + mock_async_client.build_request.assert_called_once() + build_kw = mock_async_client.build_request.call_args[1] + assert build_kw.get("content") == raw_signed + assert "json" not in build_kw + mock_async_client.send.assert_awaited_once() + assert mock_async_client.send.call_args.kwargs.get("stream") is True @pytest.mark.asyncio @@ -3619,3 +3786,718 @@ async def test_non_guardrail_exception_still_logs_with_traceback(): assert ( logger.warning.call_count == 0 ), "a genuine failure must not be downgraded to WARNING" + + +# Regression: generic config-based passthrough (`pass_through_request`) used to +# call `response.raise_for_status()` on upstream errors and re-raise as an +# `HTTPException`, which the outer `except` block then reshaped into a +# `ProxyException` (`{"error": {"message": "", ...}}`). +# Upstream error responses must reach the client byte-for-byte, with the +# original status code, exactly like success responses already do. +_UPSTREAM_ERROR_BODY = { + "error": "Permission denied", + "error_code": "ACCESS_DENIED", + "request_id": "req_mock_403", + "trace_id": "trace_mock_403", +} + + +@pytest.mark.asyncio +async def test_pass_through_request_non_streaming_upstream_error_returned_unchanged(): + upstream_content = json.dumps(_UPSTREAM_ERROR_BODY).encode("utf-8") + upstream_response = httpx.Response( + status_code=403, + headers={"content-type": "application/json"}, + content=upstream_content, + request=httpx.Request("POST", "http://target-api.com/api/denied"), + ) + + with patch("litellm.proxy.proxy_server.proxy_logging_obj") as mock_proxy_logging: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.get_async_httpx_client" + ) as mock_get_client: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.ProxyBaseLLMRequestProcessing" + ) as mock_processing: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.pass_through_endpoint_logging.pass_through_async_success_handler" + ) as mock_success_handler: + mock_proxy_logging.pre_call_hook = AsyncMock(return_value={}) + mock_proxy_logging.post_call_failure_hook = AsyncMock() + mock_proxy_logging.post_call_response_headers_hook = AsyncMock( + return_value=None + ) + mock_processing.get_custom_headers.return_value = {} + mock_success_handler.return_value = None + + async_client = MagicMock() + async_client.build_request = MagicMock(return_value=MagicMock()) + async_client.send = AsyncMock(return_value=upstream_response) + mock_get_client.return_value = MagicMock(client=async_client) + + mock_request = MagicMock(spec=Request) + mock_request.method = "POST" + mock_request.url = "http://test-proxy.com/mock-upstream/api/denied" + mock_request.body = AsyncMock(return_value=b'{"action": "read"}') + mock_request.headers = Headers({"content-type": "application/json"}) + mock_request.query_params = QueryParams({}) + + response = await pass_through_request( + request=mock_request, + target="http://target-api.com/api/denied", + custom_headers={}, + user_api_key_dict=MagicMock(), + ) + await asyncio.sleep(0) + + assert response.status_code == 403 + body = json.loads(response.body) + # Exact dict equality proves the upstream body was forwarded verbatim, + # not stringified into a ProxyException's `error.message` field. + assert body == _UPSTREAM_ERROR_BODY + assert set(body.keys()) != {"error"} or not isinstance(body["error"], dict) + + # Regression: the success handler has no status-code awareness, so it must + # never be called for a 4xx/5xx upstream response - otherwise the same + # request gets recorded as both a failure and a success in SpendLogs. + mock_success_handler.assert_not_called() + + # Regression: post_call_failure_hook (spend-tracking, alerting callbacks) + # must still fire for upstream errors even though the client-facing + # response is unchanged and no ProxyException is raised. + from fastapi import HTTPException + + mock_proxy_logging.post_call_failure_hook.assert_called_once() + failure_call_kwargs = mock_proxy_logging.post_call_failure_hook.call_args.kwargs + # Must be reported as HTTPException, not the raw httpx error: ProxyLogging's + # alerting only excludes HTTPException/ProxyException from its "High" + # severity llm_exceptions alert, so a raw HTTPStatusError here would page + # ops for every routine upstream 4xx returned through passthrough. + assert isinstance(failure_call_kwargs["original_exception"], HTTPException) + assert failure_call_kwargs["original_exception"].status_code == 403 + + # Regression: the failure-hook log payload's response_body must reflect + # the upstream error JSON, not None, so downstream spend-tracking/logging + # integrations can see what the upstream actually returned. + assert failure_call_kwargs["request_data"]["response_body"] == _UPSTREAM_ERROR_BODY + + +@pytest.mark.asyncio +async def test_pass_through_request_upstream_error_failure_hook_exception_is_swallowed(): + """ + A broken failure-hook callback (e.g. a misconfigured alerting integration) + must never take down the passthrough response - the upstream error body + must still reach the client unchanged, and the callback's exception must + only be logged, not raised. + """ + upstream_content = json.dumps(_UPSTREAM_ERROR_BODY).encode("utf-8") + upstream_response = httpx.Response( + status_code=403, + headers={"content-type": "application/json"}, + content=upstream_content, + request=httpx.Request("POST", "http://target-api.com/api/denied"), + ) + + with patch("litellm.proxy.proxy_server.proxy_logging_obj") as mock_proxy_logging: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.get_async_httpx_client" + ) as mock_get_client: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.ProxyBaseLLMRequestProcessing" + ) as mock_processing: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.pass_through_endpoint_logging.pass_through_async_success_handler" + ) as mock_success_handler: + mock_proxy_logging.pre_call_hook = AsyncMock(return_value={}) + mock_proxy_logging.post_call_failure_hook = AsyncMock( + side_effect=RuntimeError("alerting integration misconfigured") + ) + mock_proxy_logging.post_call_response_headers_hook = AsyncMock( + return_value=None + ) + mock_processing.get_custom_headers.return_value = {} + mock_success_handler.return_value = None + + async_client = MagicMock() + async_client.build_request = MagicMock(return_value=MagicMock()) + async_client.send = AsyncMock(return_value=upstream_response) + mock_get_client.return_value = MagicMock(client=async_client) + + mock_request = MagicMock(spec=Request) + mock_request.method = "POST" + mock_request.url = "http://test-proxy.com/mock-upstream/api/denied" + mock_request.body = AsyncMock(return_value=b'{"action": "read"}') + mock_request.headers = Headers({"content-type": "application/json"}) + mock_request.query_params = QueryParams({}) + + response = await pass_through_request( + request=mock_request, + target="http://target-api.com/api/denied", + custom_headers={}, + user_api_key_dict=MagicMock(), + ) + await asyncio.sleep(0) + + mock_proxy_logging.post_call_failure_hook.assert_called_once() + assert response.status_code == 403 + assert json.loads(response.body) == _UPSTREAM_ERROR_BODY + + +@pytest.mark.asyncio +async def test_pass_through_request_streaming_upstream_error_returned_unchanged(): + from fastapi.responses import StreamingResponse + + upstream_content = json.dumps(_UPSTREAM_ERROR_BODY).encode("utf-8") + upstream_response = httpx.Response( + status_code=403, + headers={"content-type": "application/json"}, + content=upstream_content, + request=httpx.Request("GET", "http://target-api.com/api/stream-denied"), + ) + + with patch("litellm.proxy.proxy_server.proxy_logging_obj") as mock_proxy_logging: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.get_async_httpx_client" + ) as mock_get_client: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.pass_through_endpoint_logging.pass_through_async_success_handler" + ) as mock_success_handler: + mock_proxy_logging.pre_call_hook = AsyncMock(return_value={}) + mock_proxy_logging.post_call_failure_hook = AsyncMock() + mock_proxy_logging.post_call_response_headers_hook = AsyncMock( + return_value=None + ) + mock_success_handler.return_value = None + + async_client = MagicMock() + async_client.build_request = MagicMock(return_value=MagicMock()) + async_client.send = AsyncMock(return_value=upstream_response) + mock_get_client.return_value = MagicMock(client=async_client) + + mock_request = MagicMock(spec=Request) + mock_request.method = "GET" + mock_request.url = "http://test-proxy.com/mock-upstream/api/stream-denied" + mock_request.body = AsyncMock(return_value=b"") + mock_request.headers = Headers({}) + mock_request.query_params = QueryParams({}) + + response = await pass_through_request( + request=mock_request, + target="http://target-api.com/api/stream-denied", + custom_headers={}, + user_api_key_dict=MagicMock(), + stream=True, + ) + + assert isinstance(response, StreamingResponse) + assert response.status_code == 403 + + streamed_chunks = [chunk async for chunk in response.body_iterator] + await asyncio.sleep(0) + streamed_bytes = b"".join( + chunk if isinstance(chunk, bytes) else chunk.encode("utf-8") + for chunk in streamed_chunks + ) + assert streamed_bytes == upstream_content + assert json.loads(streamed_bytes) == _UPSTREAM_ERROR_BODY + + # Regression: chunk_processor's end-of-stream success logging has no + # status-code awareness, so it must never fire for a 4xx/5xx upstream + # response - otherwise the same request gets recorded as both a failure + # (via the hook below) and a success in SpendLogs. + mock_success_handler.assert_not_called() + + # Regression: post_call_failure_hook must still fire for streaming + # upstream errors, mirroring the non-streaming behavior, and must also + # report an HTTPException (not the raw httpx error) to avoid triggering + # a "High" severity llm_exceptions alert for a routine upstream 4xx. + from fastapi import HTTPException + + mock_proxy_logging.post_call_failure_hook.assert_called_once() + failure_call_kwargs = mock_proxy_logging.post_call_failure_hook.call_args.kwargs + assert isinstance(failure_call_kwargs["original_exception"], HTTPException) + assert failure_call_kwargs["original_exception"].status_code == 403 + + +@pytest.mark.asyncio +async def test_pass_through_request_non_streaming_success_unchanged(): + """Success (2xx) passthrough behavior must remain unchanged by the error fix.""" + upstream_success_body = {"status": "ok", "message": "mock upstream success"} + upstream_content = json.dumps(upstream_success_body).encode("utf-8") + upstream_response = httpx.Response( + status_code=200, + headers={"content-type": "application/json"}, + content=upstream_content, + request=httpx.Request("GET", "http://target-api.com/api/success"), + ) + + with patch("litellm.proxy.proxy_server.proxy_logging_obj") as mock_proxy_logging: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.get_async_httpx_client" + ) as mock_get_client: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.ProxyBaseLLMRequestProcessing" + ) as mock_processing: + with patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.pass_through_endpoint_logging.pass_through_async_success_handler" + ) as mock_success_handler: + mock_proxy_logging.pre_call_hook = AsyncMock(return_value={}) + mock_proxy_logging.post_call_failure_hook = AsyncMock() + mock_proxy_logging.post_call_response_headers_hook = AsyncMock( + return_value=None + ) + mock_processing.get_custom_headers.return_value = {} + mock_success_handler.return_value = None + + async_client = MagicMock() + async_client.build_request = MagicMock(return_value=MagicMock()) + async_client.send = AsyncMock(return_value=upstream_response) + mock_get_client.return_value = MagicMock(client=async_client) + + mock_request = MagicMock(spec=Request) + mock_request.method = "GET" + mock_request.url = "http://test-proxy.com/mock-upstream/api/success" + mock_request.body = AsyncMock(return_value=b"") + mock_request.headers = Headers({}) + mock_request.query_params = QueryParams({}) + + response = await pass_through_request( + request=mock_request, + target="http://target-api.com/api/success", + custom_headers={}, + user_api_key_dict=MagicMock(), + ) + await asyncio.sleep(0) + + assert response.status_code == 200 + assert json.loads(response.body) == upstream_success_body + # Regression guard: the failure hook must only fire for upstream errors, + # never for a successful upstream response. + mock_proxy_logging.post_call_failure_hook.assert_not_called() + # ...and the success handler must still fire exactly once for a 2xx, + # proving the status_code gate doesn't also swallow real successes. + mock_success_handler.assert_called_once() + + +@pytest.mark.asyncio +async def test_pass_through_request_internal_failure_still_raises_proxy_exception(): + """ + Internal proxy failures (e.g. a hook raising before any upstream request is + made) must still surface as ProxyException, distinct from upstream + passthrough errors which are now returned unchanged. + """ + from litellm.proxy._types import ProxyException + + with patch("litellm.proxy.proxy_server.proxy_logging_obj") as mock_proxy_logging: + mock_proxy_logging.pre_call_hook = AsyncMock( + side_effect=RuntimeError("auth backend unavailable") + ) + mock_proxy_logging.post_call_failure_hook = AsyncMock() + + mock_request = MagicMock(spec=Request) + mock_request.method = "GET" + mock_request.url = "http://test-proxy.com/mock-upstream/api/success" + mock_request.body = AsyncMock(return_value=b"") + mock_request.headers = Headers({}) + mock_request.query_params = QueryParams({}) + + with pytest.raises(ProxyException) as exc_info: + await pass_through_request( + request=mock_request, + target="http://target-api.com/api/success", + custom_headers={}, + user_api_key_dict=MagicMock(), + ) + + assert int(exc_info.value.code) == 500 + assert "auth backend unavailable" in exc_info.value.message + + +class _RecordingUpstreamByteStream(httpx.AsyncByteStream): + def __init__(self, chunks): + self._chunks = chunks + self.chunks_served = 0 + self.closed = False + + async def __aiter__(self): + for chunk in self._chunks: + self.chunks_served += 1 + yield chunk + + async def aclose(self): + self.closed = True + + +class _FakeUpstreamTransport(httpx.AsyncBaseTransport): + def __init__(self, status_code, headers, stream): + self._status_code = status_code + self._headers = headers + self._stream = stream + + async def handle_async_request(self, request): + return httpx.Response( + status_code=self._status_code, + headers=self._headers, + stream=self._stream, + request=request, + ) + + +def _inject_fake_passthrough_client(transport, timeout): + """Dependency-inject a fake upstream via the client cache that + get_async_httpx_client resolves passthrough clients from (no monkeypatching + of the HTTP layer). The cache entry is located by calling the production + get_async_httpx_client and identity-scanning the cache for the handler it + returned, so the internal cache-key format is never duplicated here. Must + run inside the test's event loop because cache keys are loop-scoped. + Returns (client, cleanup).""" + import litellm + from litellm.llms.custom_httpx.http_handler import get_async_httpx_client + from litellm.types.llms.custom_http import httpxSpecialProvider + + real_handler = get_async_httpx_client( + httpxSpecialProvider.PassThroughEndpoint, + params={"timeout": resolve_pass_through_request_timeout(timeout)}, + ) + cache = litellm.in_memory_llm_clients_cache + cache_key = next( + (key for key, cached in cache.cache_dict.items() if cached is real_handler), + None, + ) + assert cache_key is not None, ( + "PassThroughEndpoint client not found in in_memory_llm_clients_cache; " + "get_async_httpx_client may not be caching this provider." + ) + fake_client = httpx.AsyncClient(transport=transport) + cache.cache_dict[cache_key] = SimpleNamespace(client=fake_client) + + def _cleanup(): + cache.cache_dict.pop(cache_key, None) + + return fake_client, _cleanup + + +def _enter_relay_logging_mocks(stack, parsed_body): + from litellm.litellm_core_utils.logging_worker import GLOBAL_LOGGING_WORKER + + mock_proxy_logging = stack.enter_context( + patch("litellm.proxy.proxy_server.proxy_logging_obj") + ) + mock_proxy_logging.pre_call_hook = AsyncMock(return_value=parsed_body) + mock_proxy_logging.post_call_failure_hook = AsyncMock() + mock_proxy_logging.post_call_response_headers_hook = AsyncMock(return_value=None) + mock_success_handler = stack.enter_context( + patch( + "litellm.proxy.pass_through_endpoints.pass_through_endpoints.pass_through_endpoint_logging.pass_through_async_success_handler" + ) + ) + mock_success_handler.return_value = None + stack.enter_context( + patch.object( + GLOBAL_LOGGING_WORKER, "ensure_initialized_and_enqueue", new=MagicMock() + ) + ) + return mock_proxy_logging, mock_success_handler + + +def _relay_client_request(method="GET"): + mock_request = MagicMock(spec=Request) + mock_request.method = method + mock_request.url = "http://localhost:4000/passthrough-relay/results" + mock_request.body = AsyncMock(return_value=b"") + mock_request.headers = Headers({}) + mock_request.query_params = QueryParams({}) + return mock_request + + +@pytest.mark.asyncio +async def test_pass_through_request_relays_non_json_body_without_buffering(): + """ + Regression (LIT-4009): non-SSE passthrough responses used to be fully + buffered in proxy memory (content = await response.aread()) before a single + byte reached the client, ballooning proxy RSS to a multiple of the body size + for large non-JSON downloads (e.g. Anthropic batch results .jsonl files) and + producing near-total TTFB dead air that let intermediaries kill the silent + connection mid-download. + + A non-JSON 2xx body must be relayed as a StreamingResponse whose chunks are + pulled from the upstream one at a time, with zero chunks consumed before the + handler returns, upstream status/headers plus x-litellm-* headers preserved, + and the success-handler logging fired with response_body=None once the + stream completes. Pre-fix, the handler returned a plain Response after + reading the entire body, so these assertions fail on the old code. + """ + from fastapi.responses import StreamingResponse + + from litellm.proxy._types import UserAPIKeyAuth + + upstream_chunks = ( + b'{"custom_id": "a", "result": {}}\n', + b'{"custom_id": "b", "result": {}}\n', + b'{"custom_id": "c", "result": {}}\n', + ) + upstream_stream = _RecordingUpstreamByteStream(upstream_chunks) + fake_client, cleanup = _inject_fake_passthrough_client( + _FakeUpstreamTransport( + status_code=200, + headers={ + "content-type": "application/x-jsonl", + "x-upstream-marker": "batch-results", + "content-length": str(sum(len(c) for c in upstream_chunks)), + }, + stream=upstream_stream, + ), + timeout=311.0, + ) + try: + with ExitStack() as stack: + _, mock_success_handler = _enter_relay_logging_mocks(stack, {}) + + response = await pass_through_request( + request=_relay_client_request(), + target="http://upstream.test/v1/messages/batches/b1/results", + custom_headers={}, + user_api_key_dict=UserAPIKeyAuth(api_key="sk-relay-test"), + timeout=311.0, + ) + + assert isinstance(response, StreamingResponse) + assert upstream_stream.chunks_served == 0 + mock_success_handler.assert_not_called() + + iterator = response.body_iterator + first_chunk = await iterator.__anext__() + assert first_chunk == upstream_chunks[0] + assert upstream_stream.chunks_served == 1 + + remaining = [chunk async for chunk in iterator] + assert b"".join([first_chunk, *remaining]) == b"".join(upstream_chunks) + assert upstream_stream.closed is True + + assert response.status_code == 200 + assert response.headers["x-upstream-marker"] == "batch-results" + assert "x-litellm-call-id" in response.headers + assert "content-length" not in response.headers + + mock_success_handler.assert_called_once() + success_kwargs = mock_success_handler.call_args.kwargs + assert success_kwargs["response_body"] is None + assert ( + success_kwargs["url_route"] + == "http://upstream.test/v1/messages/batches/b1/results" + ) + finally: + cleanup() + await fake_client.aclose() + + +@pytest.mark.asyncio +async def test_pass_through_request_json_response_stays_buffered_for_logging(): + """ + JSON responses (content-type application/json) must keep the buffered + behavior: spend logging and guardrails inspect the parsed body, so the + handler reads the full upstream body and passes the parsed dict to the + success handler. + """ + from fastapi.responses import StreamingResponse + + from litellm.proxy._types import UserAPIKeyAuth + + upstream_chunks = (b'{"id": "file-123"', b', "status": "processed"}') + upstream_stream = _RecordingUpstreamByteStream(upstream_chunks) + fake_client, cleanup = _inject_fake_passthrough_client( + _FakeUpstreamTransport( + status_code=200, + headers={"content-type": "application/json"}, + stream=upstream_stream, + ), + timeout=312.0, + ) + try: + with ExitStack() as stack: + _, mock_success_handler = _enter_relay_logging_mocks(stack, {}) + + response = await pass_through_request( + request=_relay_client_request(), + target="http://upstream.test/v1/files/file-123", + custom_headers={}, + user_api_key_dict=UserAPIKeyAuth(api_key="sk-relay-test"), + timeout=312.0, + ) + + assert not isinstance(response, StreamingResponse) + assert response.status_code == 200 + assert response.body == b"".join(upstream_chunks) + assert upstream_stream.chunks_served == len(upstream_chunks) + + mock_success_handler.assert_called_once() + success_kwargs = mock_success_handler.call_args.kwargs + assert success_kwargs["response_body"] == { + "id": "file-123", + "status": "processed", + } + finally: + cleanup() + await fake_client.aclose() + + +@pytest.mark.asyncio +async def test_pass_through_request_upstream_error_body_stays_buffered(): + """ + Upstream errors are never relayed as a stream, whatever their content-type: + the body must stay available for the failure hook and reach the client + buffered with the upstream status code, exactly as before the fix. + """ + from fastapi.responses import StreamingResponse + + from litellm.proxy._types import UserAPIKeyAuth + + upstream_stream = _RecordingUpstreamByteStream((b"upstream ", b"exploded")) + fake_client, cleanup = _inject_fake_passthrough_client( + _FakeUpstreamTransport( + status_code=502, + headers={"content-type": "application/x-jsonl"}, + stream=upstream_stream, + ), + timeout=313.0, + ) + try: + with ExitStack() as stack: + mock_proxy_logging, mock_success_handler = _enter_relay_logging_mocks( + stack, {} + ) + + response = await pass_through_request( + request=_relay_client_request(), + target="http://upstream.test/v1/messages/batches/b1/results", + custom_headers={}, + user_api_key_dict=UserAPIKeyAuth(api_key="sk-relay-test"), + timeout=313.0, + ) + + assert not isinstance(response, StreamingResponse) + assert response.status_code == 502 + assert response.body == b"upstream exploded" + mock_proxy_logging.post_call_failure_hook.assert_called_once() + mock_success_handler.assert_not_called() + finally: + cleanup() + await fake_client.aclose() + + +_PARTIAL_RELAY_WARNING_MARKER = "ended before upstream body was fully relayed" + + +@pytest.mark.asyncio +async def test_pass_through_relay_client_disconnect_logs_partial_relay_warning(caplog): + """ + Regression: when the client disconnects mid-relay (GeneratorExit), the + proxy log must record that the upstream body was only partially delivered, + including the route and the byte count that reached the client, while the + success handler still fires so the partial delivery produces a spend-log + row. Pre-fix, the finally block fired the success handler silently and a + partial delivery was indistinguishable from a complete one. + """ + from fastapi.responses import StreamingResponse + + from litellm.proxy._types import UserAPIKeyAuth + + upstream_chunks = (b'{"custom_id": "a"}\n', b'{"custom_id": "b"}\n') + upstream_stream = _RecordingUpstreamByteStream(upstream_chunks) + fake_client, cleanup = _inject_fake_passthrough_client( + _FakeUpstreamTransport( + status_code=200, + headers={"content-type": "application/x-jsonl"}, + stream=upstream_stream, + ), + timeout=314.0, + ) + try: + with ExitStack() as stack: + _, mock_success_handler = _enter_relay_logging_mocks(stack, {}) + + response = await pass_through_request( + request=_relay_client_request(), + target="http://upstream.test/v1/messages/batches/b1/results", + custom_headers={}, + user_api_key_dict=UserAPIKeyAuth(api_key="sk-relay-test"), + timeout=314.0, + ) + + assert isinstance(response, StreamingResponse) + iterator = response.body_iterator + first_chunk = await iterator.__anext__() + assert first_chunk == upstream_chunks[0] + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + await iterator.aclose() + + partial_relay_warnings = [ + record.getMessage() + for record in caplog.records + if record.levelno == logging.WARNING + and _PARTIAL_RELAY_WARNING_MARKER in record.getMessage() + ] + assert len(partial_relay_warnings) == 1 + assert ( + "http://upstream.test/v1/messages/batches/b1/results" + in partial_relay_warnings[0] + ) + assert ( + f"{len(first_chunk)} bytes were sent to the client" + in partial_relay_warnings[0] + ) + + assert upstream_stream.closed is True + mock_success_handler.assert_called_once() + assert mock_success_handler.call_args.kwargs["response_body"] is None + finally: + cleanup() + await fake_client.aclose() + + +@pytest.mark.asyncio +async def test_pass_through_relay_full_consumption_logs_no_partial_relay_warning(caplog): + """ + A fully consumed relay must not be reported as a partial delivery: the + success handler fires and no partial-relay warning is logged. + """ + from fastapi.responses import StreamingResponse + + from litellm.proxy._types import UserAPIKeyAuth + + upstream_chunks = (b'{"custom_id": "a"}\n', b'{"custom_id": "b"}\n') + upstream_stream = _RecordingUpstreamByteStream(upstream_chunks) + fake_client, cleanup = _inject_fake_passthrough_client( + _FakeUpstreamTransport( + status_code=200, + headers={"content-type": "application/x-jsonl"}, + stream=upstream_stream, + ), + timeout=315.0, + ) + try: + with ExitStack() as stack: + _, mock_success_handler = _enter_relay_logging_mocks(stack, {}) + + response = await pass_through_request( + request=_relay_client_request(), + target="http://upstream.test/v1/messages/batches/b1/results", + custom_headers={}, + user_api_key_dict=UserAPIKeyAuth(api_key="sk-relay-test"), + timeout=315.0, + ) + + assert isinstance(response, StreamingResponse) + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + relayed = [chunk async for chunk in response.body_iterator] + + assert b"".join(relayed) == b"".join(upstream_chunks) + assert not any( + _PARTIAL_RELAY_WARNING_MARKER in record.getMessage() + for record in caplog.records + ) + mock_success_handler.assert_called_once() + finally: + cleanup() + await fake_client.aclose() diff --git a/tests/test_litellm/proxy/pass_through_endpoints/test_streaming_handler_interrupt.py b/tests/test_litellm/proxy/pass_through_endpoints/test_streaming_handler_interrupt.py index b781190eaef..163a0cbff3c 100644 --- a/tests/test_litellm/proxy/pass_through_endpoints/test_streaming_handler_interrupt.py +++ b/tests/test_litellm/proxy/pass_through_endpoints/test_streaming_handler_interrupt.py @@ -93,6 +93,41 @@ async def test_chunk_processor_logs_on_client_disconnect(): assert call_kwargs["raw_bytes"] == [chunks[0]] +@pytest.mark.asyncio +async def test_chunk_processor_does_not_schedule_success_logging_for_upstream_error(): + """A 4xx/5xx upstream response is already logged as a failure by the caller + before this generator starts; scheduling success logging here too would + double-log the same request in SpendLogs.""" + chunks = [b'{"error": "denied"}'] + response = _make_streaming_response(chunks) + response.status_code = 403 + + mock_logging_obj = MagicMock() + mock_passthrough_handler = MagicMock() + + with patch.object( + PassThroughStreamingHandler, + "_route_streaming_logging_to_handler", + new=AsyncMock(), + ) as mock_route: + received = [] + async for chunk in PassThroughStreamingHandler.chunk_processor( + response=response, + request_body={"model": "claude-3-haiku"}, + litellm_logging_obj=mock_logging_obj, + endpoint_type=EndpointType.GENERIC, + start_time=datetime.now(), + passthrough_success_handler_obj=mock_passthrough_handler, + url_route="/bedrock/model/claude/invoke-with-response-stream", + ): + received.append(chunk) + + await asyncio.sleep(0) + + assert received == chunks + mock_route.assert_not_called() + + @pytest.mark.asyncio async def test_chunk_processor_does_not_schedule_logging_when_no_chunks(): response = _make_streaming_response([]) @@ -206,6 +241,126 @@ async def test_chunk_processor_routes_logging_through_logging_worker_on_disconne assert asyncio.iscoroutine(enqueued[0]) +def _logging_obj_with_write_once_cst(): + """Build a MagicMock that mirrors the real Logging behavior: _update_completion_start_time + latches self.completion_start_time so the write-once guard actually latches.""" + obj = MagicMock() + obj.completion_start_time = None + + def _update(*, completion_start_time): + obj.completion_start_time = completion_start_time + + obj._update_completion_start_time.side_effect = _update + return obj + + +@pytest.mark.asyncio +async def test_chunk_processor_stamps_completion_start_time_on_first_chunk(): + """Regression: LIT-4185 — streaming pass-through must stamp completion_start_time on + the first upstream chunk. Otherwise _success_handler_helper_fn falls back to + completion_start_time = end_time, and Prometheus/OTEL/SpendLogs TTFT reads as + total request duration (0 speedup).""" + chunks = [b"chunk-1", b"chunk-2", b"chunk-3"] + response = _make_streaming_response(chunks) + + mock_logging_obj = _logging_obj_with_write_once_cst() + mock_passthrough_handler = MagicMock() + + with patch.object( + PassThroughStreamingHandler, + "_route_streaming_logging_to_handler", + new=AsyncMock(), + ): + received = [] + async for chunk in PassThroughStreamingHandler.chunk_processor( + response=response, + request_body={"model": "claude-haiku-4-5"}, + litellm_logging_obj=mock_logging_obj, + endpoint_type=EndpointType.ANTHROPIC, + start_time=datetime.now(), + passthrough_success_handler_obj=mock_passthrough_handler, + url_route="/v1/messages", + ): + received.append(chunk) + + await asyncio.sleep(0) + + assert received == chunks + mock_logging_obj._update_completion_start_time.assert_called_once() + stamped = mock_logging_obj._update_completion_start_time.call_args.kwargs["completion_start_time"] + assert isinstance(stamped, datetime) + + +@pytest.mark.asyncio +async def test_chunk_processor_does_not_reset_completion_start_time_on_later_chunks(): + """The stamp must be write-once: reading it on chunk 2/3 must not overwrite a real TTFT + from chunk 1 (which would collapse TTFT to time-to-last-chunk).""" + chunks = [b"chunk-1", b"chunk-2", b"chunk-3"] + response = _make_streaming_response(chunks) + + real_first = datetime(2020, 1, 1, 0, 0, 0) + mock_logging_obj = MagicMock() + # Simulate first-chunk stamp having already landed (e.g. under contention or a + # prior wrapper that already set it): later chunks must be no-ops. + mock_logging_obj.completion_start_time = real_first + mock_passthrough_handler = MagicMock() + + with patch.object( + PassThroughStreamingHandler, + "_route_streaming_logging_to_handler", + new=AsyncMock(), + ): + async for _ in PassThroughStreamingHandler.chunk_processor( + response=response, + request_body={"model": "claude-haiku-4-5"}, + litellm_logging_obj=mock_logging_obj, + endpoint_type=EndpointType.ANTHROPIC, + start_time=datetime.now(), + passthrough_success_handler_obj=mock_passthrough_handler, + url_route="/v1/messages", + ): + pass + + mock_logging_obj._update_completion_start_time.assert_not_called() + assert mock_logging_obj.completion_start_time == real_first + + +@pytest.mark.asyncio +async def test_chunk_processor_stamps_completion_start_time_on_cost_injection_path(): + """The cost-injection branch runs alongside a hot path; both must stamp TTFT.""" + import litellm as litellm_mod + + chunks = [b"event: message_start\ndata: {}\n\n"] + response = _make_streaming_response(chunks) + + mock_logging_obj = _logging_obj_with_write_once_cst() + mock_logging_obj.model_call_details = {"model": "claude-haiku-4-5"} + mock_passthrough_handler = MagicMock() + + original = getattr(litellm_mod, "include_cost_in_streaming_usage", False) + litellm_mod.include_cost_in_streaming_usage = True + try: + with patch.object( + PassThroughStreamingHandler, + "_route_streaming_logging_to_handler", + new=AsyncMock(), + ): + async for _ in PassThroughStreamingHandler.chunk_processor( + response=response, + request_body={"model": "claude-haiku-4-5"}, + litellm_logging_obj=mock_logging_obj, + endpoint_type=EndpointType.ANTHROPIC, + start_time=datetime.now(), + passthrough_success_handler_obj=mock_passthrough_handler, + url_route="/v1/messages", + ): + pass + finally: + litellm_mod.include_cost_in_streaming_usage = original + + mock_logging_obj._update_completion_start_time.assert_called_once() + + def test_convert_raw_bytes_survives_truncated_multibyte_sequence(): """A stream cut mid-multibyte-sequence (client disconnect) must still decode via errors="replace" so the usage events already received are logged, instead diff --git a/tests/test_litellm/proxy/pass_through_endpoints/test_vertex_ai_batch_passthrough.py b/tests/test_litellm/proxy/pass_through_endpoints/test_vertex_ai_batch_passthrough.py index 044827e287a..5de682ec8a0 100644 --- a/tests/test_litellm/proxy/pass_through_endpoints/test_vertex_ai_batch_passthrough.py +++ b/tests/test_litellm/proxy/pass_through_endpoints/test_vertex_ai_batch_passthrough.py @@ -84,7 +84,7 @@ class TestVertexAIBatchPassthroughHandler: "input_file_id": "file-123", "output_file_id": "file-456", "error_file_id": None, - "completion_window": "24hrs", + "completion_window": "24h", } mock_transformation._get_batch_id_from_vertex_ai_batch_response.return_value = ( "123456789" @@ -451,7 +451,7 @@ class TestVertexAIBatchPassthroughHandler: "input_file_id": "file-123", "output_file_id": "file-456", "error_file_id": None, - "completion_window": "24hrs", + "completion_window": "24h", } mock_transformation._get_batch_id_from_vertex_ai_batch_response.return_value = ( "123456789" diff --git a/tests/test_litellm/proxy/policy_engine/test_policy_validator.py b/tests/test_litellm/proxy/policy_engine/test_policy_validator.py index de2dde58698..a56695ee7f5 100644 --- a/tests/test_litellm/proxy/policy_engine/test_policy_validator.py +++ b/tests/test_litellm/proxy/policy_engine/test_policy_validator.py @@ -6,6 +6,7 @@ Tests validation of: - Guardrail names exist in registry """ +from typing import Optional, Set from unittest.mock import MagicMock, patch import pytest @@ -18,6 +19,37 @@ from litellm.types.proxy.policy_engine import ( ) +class _FakeTable: + """Minimal stand-in for a Prisma table whose find_first matches on one field.""" + + def __init__(self, existing: Set[str], match_field: str): + self._existing = existing + self._match_field = match_field + + async def find_first(self, where: dict) -> Optional[object]: + return object() if where.get(self._match_field) in self._existing else None + + +class _FakeDB: + def __init__(self, teams: Set[str], keys: Set[str]): + self.litellm_teamtable = _FakeTable(teams, "team_alias") + self.litellm_verificationtoken = _FakeTable(keys, "key_alias") + + +class _FakePrisma: + """Injected prisma client so PolicyValidator can be unit-tested without a DB.""" + + def __init__(self, teams: Set[str] = frozenset(), keys: Set[str] = frozenset()): + self.db = _FakeDB(teams, keys) + + +class _FakeRouter: + """Injected router exposing only what check_model_exists reads.""" + + def __init__(self, model_names: Set[str]): + self.model_names = list(model_names) + + class TestPolicyValidator: """Test policy validation logic.""" @@ -91,3 +123,72 @@ class TestPolicyValidator: assert result.valid is True assert len(result.errors) == 0 + + +class TestAttachmentScopeValidation: + """Regression tests for LIT-4199: attachments must not accept non-existent teams/keys/models.""" + + @pytest.mark.asyncio + async def test_nonexistent_team_is_flagged(self): + validator = PolicyValidator(prisma_client=_FakePrisma(teams={"real-team"})) + errors = await validator.find_invalid_scope_entries( + policy_name="p", teams=["real-team", "ghost-team"] + ) + assert [(e.field, e.value) for e in errors] == [("teams", "ghost-team")] + assert errors[0].error_type == PolicyValidationErrorType.INVALID_TEAM + + @pytest.mark.asyncio + async def test_existing_team_passes(self): + validator = PolicyValidator(prisma_client=_FakePrisma(teams={"payments"})) + errors = await validator.find_invalid_scope_entries(policy_name="p", teams=["payments"]) + assert errors == [] + + @pytest.mark.asyncio + async def test_trailing_star_wildcard_is_allowed_even_when_it_matches_nothing(self): + validator = PolicyValidator(prisma_client=_FakePrisma(teams=set())) + errors = await validator.find_invalid_scope_entries( + policy_name="p", teams=["healthcare-*", "brand-new-*"] + ) + assert errors == [] + + @pytest.mark.asyncio + async def test_only_trailing_star_counts_as_a_wildcard(self): + # Request-time matching treats only a trailing "*" as a wildcard; "?" and a + # non-trailing "*" are compared literally, so they are validated as concrete + # aliases (and here resolve to nothing -> flagged). + validator = PolicyValidator(prisma_client=_FakePrisma(teams=set())) + errors = await validator.find_invalid_scope_entries( + policy_name="p", teams=["ops-?", "heal*care"] + ) + assert {e.value for e in errors} == {"ops-?", "heal*care"} + + @pytest.mark.asyncio + async def test_keys_and_models_are_validated_too(self): + validator = PolicyValidator( + prisma_client=_FakePrisma(keys={"prod-key"}), + llm_router=_FakeRouter(model_names={"gpt-4o"}), + ) + errors = await validator.find_invalid_scope_entries( + policy_name="p", + keys=["prod-key", "ghost-key"], + models=["gpt-4o", "ghost-model", "bedrock/*"], + ) + flagged = {(e.field, e.value) for e in errors} + assert ("keys", "ghost-key") in flagged + assert ("models", "ghost-model") in flagged + assert ("keys", "prod-key") not in flagged + assert ("models", "gpt-4o") not in flagged + assert ("models", "bedrock/*") not in flagged # wildcard model allowed through + + @pytest.mark.asyncio + async def test_no_scope_entries_returns_no_errors(self): + validator = PolicyValidator(prisma_client=_FakePrisma()) + errors = await validator.find_invalid_scope_entries(policy_name="p") + assert errors == [] + + @pytest.mark.asyncio + async def test_without_db_connection_assumes_valid(self): + # Fail-open: with no DB we cannot verify existence, so nothing is blocked. + validator = PolicyValidator(prisma_client=None) + errors = await validator.find_invalid_scope_entries(policy_name="p", teams=["anything"]) + assert errors == [] diff --git a/tests/test_litellm/proxy/proxy_server/test_lifecycle.py b/tests/test_litellm/proxy/proxy_server/test_lifecycle.py index 9343dcbc29f..a3f5049ef1d 100644 --- a/tests/test_litellm/proxy/proxy_server/test_lifecycle.py +++ b/tests/test_litellm/proxy/proxy_server/test_lifecycle.py @@ -212,6 +212,145 @@ def test_save_worker_config_invalid_no_kwargs_yields_empty(monkeypatch): assert os.environ["WORKER_CONFIG"] == "{}" +# --------------------------------------------------------------------------- +# _redact_worker_config_for_logging (LIT-4152) +# --------------------------------------------------------------------------- + + +_LIT4152_SECRETS = ( + "sk-lit4152-regression-master-key-abcdef1234567890", + "leak_password_9090", + "sk-lit4152-provider-api-key-abcdef", + "postgresql://leak_user:leak_password_9090@leak-host.internal:5432/leak_db", +) + + +def _lit4152_worker_config_dict(): + return { + "model": "openai/gpt-4o-mini", + "config": "/tmp/c.yaml", + "master_key": _LIT4152_SECRETS[0], + "database_url": _LIT4152_SECRETS[3], + "api_key": _LIT4152_SECRETS[2], + "telemetry": True, + } + + +def test__redact_worker_config_for_logging_dict_masks_all_secret_shapes(): + """LIT-4152 regression: dict-form worker_config must not embed any raw + secret. Covers the segment-matched fields (`master_key`, `api_key`) and the + URL-with-credentials field (`database_url`), which the segment masker + misses because neither segment matches its sensitive-pattern set. + """ + from litellm.proxy.proxy_server import _redact_worker_config_for_logging + + redacted = _redact_worker_config_for_logging(_lit4152_worker_config_dict()) + rendered = repr(redacted) + for secret in _LIT4152_SECRETS: + assert secret not in rendered, f"leak: {secret} in {rendered!r}" + assert isinstance(redacted, dict) + assert redacted["model"] == "openai/gpt-4o-mini" + assert redacted["telemetry"] is True + + +def test__redact_worker_config_for_logging_json_string_round_trips_masked(): + """Docker/K8s deployments hand the proxy a JSON string via ``WORKER_CONFIG``. + Confirm the string path also masks and that the returned value re-parses + into a dict with the sensitive fields masked. + """ + from litellm.proxy.proxy_server import _redact_worker_config_for_logging + + payload = json.dumps(_lit4152_worker_config_dict()) + redacted = _redact_worker_config_for_logging(payload) + assert isinstance(redacted, str) + for secret in _LIT4152_SECRETS: + assert secret not in redacted, f"leak: {secret} in {redacted!r}" + parsed = json.loads(redacted) + assert parsed["model"] == "openai/gpt-4o-mini" + + +def test__redact_worker_config_for_logging_passthrough_for_none_and_non_json_string(): + """Non-dict, non-JSON-parseable string is passed through verbatim (nothing + to mask) and ``None`` returns ``None``. + """ + from litellm.proxy.proxy_server import _redact_worker_config_for_logging + + assert _redact_worker_config_for_logging(None) is None + assert _redact_worker_config_for_logging("/tmp/some_config.yaml") == "/tmp/some_config.yaml" + + +def test__redact_worker_config_for_logging_masks_non_string_url_webhook_values(): + """The URL/webhook fields the segment masker cannot catch by key name + (``alert_to_webhook_url``, ``pass_through_endpoints``, + ``database_extra_connection_params``) can hold non-string shapes: + ``alert_to_webhook_url`` is typed as ``Optional[Dict]`` and can nest + secret query params under keys the segment masker also misses. Confirm + the whole value is replaced regardless of shape so a nested webhook or + Bearer token under a non-segment-matched key does not slip through. + """ + from litellm.proxy.proxy_server import _redact_worker_config_for_logging + + nested_webhook_secret = "https://hooks.slack.com/services/T0/B0/nested-webhook-secret-xyz" + data = { + "master_key": "sk-should-be-masked", + "alert_to_webhook_url": {"budget_alerts": nested_webhook_secret}, + "pass_through_endpoints": [ + { + "path": "/upstream", + "target": "https://api.provider.com", + "headers": {"Authorization": "Bearer nested-token-should-be-gone"}, + } + ], + "database_extra_connection_params": {"password": "extra-db-password-abc"}, + } + redacted = _redact_worker_config_for_logging(data) + rendered = repr(redacted) + for secret in ( + "sk-should-be-masked", + nested_webhook_secret, + "nested-token-should-be-gone", + "extra-db-password-abc", + ): + assert secret not in rendered, f"leak: {secret} in {rendered!r}" + + +def test__redact_worker_config_for_logging_masks_nested_secret_fields(): + """LIT-4152 nested regression: the URL/webhook credential fields the segment + masker cannot catch by name (``database_url``, + ``database_extra_connection_params``, ``pass_through_endpoints``, + ``alert_to_webhook_url``) must be redacted at any depth, not just the top + level. A worker_config that nests ``general_settings`` under a parent key + must not leak a nested ``database_url`` or webhook secret; the earlier + top-level-only redaction would have passed these through raw. + """ + from litellm.proxy.proxy_server import _redact_worker_config_for_logging + + nested_db_url = "postgresql://nested_user:nested_pw_4152@nested-host:5432/db" + nested_webhook = "https://hooks.slack.com/services/T0/B0/nested-4152-webhook" + nested_extra_pw = "nested-extra-conn-pw-4152" + nested_bearer = "Bearer nested-passthrough-token-4152" + data = { + "config": { + "general_settings": { + "database_url": nested_db_url, + "database_extra_connection_params": {"password": nested_extra_pw}, + "alert_to_webhook_url": {"budget_alerts": nested_webhook}, + "pass_through_endpoints": [ + {"path": "/up", "headers": {"Authorization": nested_bearer}} + ], + } + } + } + redacted = _redact_worker_config_for_logging(data) + rendered = repr(redacted) + for secret in (nested_db_url, nested_webhook, nested_extra_pw, nested_bearer): + assert secret not in rendered, f"nested leak: {secret} in {rendered!r}" + + inner = redacted["config"]["general_settings"] + assert inner["database_url"] == "REDACTED" + assert inner["pass_through_endpoints"] == "REDACTED" + + # --------------------------------------------------------------------------- # initialize # --------------------------------------------------------------------------- diff --git a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py index 196ff045208..98b270788dc 100644 --- a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py @@ -189,9 +189,7 @@ def test_ProxyConfig__load_yaml_file_raises_on_missing_file(): @pytest.mark.asyncio async def test_ProxyConfig__get_config_from_file_loads_yaml(tmp_path): f = tmp_path / "c.yaml" - f.write_text( - "model_list: []\ngeneral_settings: {}\nlitellm_settings:\n drop_params: true\n" - ) + f.write_text("model_list: []\ngeneral_settings: {}\nlitellm_settings:\n drop_params: true\n") pc = ProxyConfig() result = await pc._get_config_from_file(config_file_path=str(f)) assert result == { @@ -534,6 +532,139 @@ def test_ProxyConfig_parse_search_tools_missing_returns_none(): assert pc.parse_search_tools({}) is None +def test_ProxyConfig_merge_config_and_db_search_tools_returns_superset(): + config_tools = [ + { + "search_tool_name": "config-search", + "litellm_params": {"search_provider": "tavily"}, + } + ] + db_tools = [ + { + "search_tool_name": "db-search", + "litellm_params": { + "search_provider": "exa_ai", + "api_key": "fake-db-key", + }, + } + ] + + merged = ProxyConfig._merge_config_and_db_search_tools( + config_search_tools=config_tools, + db_search_tools=db_tools, + ) + + assert [tool["search_tool_name"] for tool in merged] == ["config-search", "db-search"] + assert merged[1]["litellm_params"]["api_key"] == "fake-db-key" + + +def test_ProxyConfig_merge_config_and_db_search_tools_prefers_db_duplicate(): + config_tools = [ + { + "search_tool_name": "shared-search", + "litellm_params": {"search_provider": "tavily"}, + }, + { + "search_tool_name": "config-only", + "litellm_params": {"search_provider": "perplexity"}, + }, + ] + db_tools = [ + { + "search_tool_name": "shared-search", + "litellm_params": { + "search_provider": "exa_ai", + "api_key": "fake-db-key", + }, + } + ] + + merged = ProxyConfig._merge_config_and_db_search_tools( + config_search_tools=config_tools, + db_search_tools=db_tools, + ) + + assert [tool["search_tool_name"] for tool in merged] == ["config-only", "shared-search"] + assert merged[1]["litellm_params"]["search_provider"] == "exa_ai" + assert merged[1]["litellm_params"]["api_key"] == "fake-db-key" + + +@pytest.mark.asyncio +async def test_ProxyConfig__init_search_tools_in_db_loads_merged_tools(monkeypatch): + from litellm.proxy import proxy_server + from litellm.router_utils.search_api_router import SearchAPIRouter + + pc = ProxyConfig() + pc.update_config_state( + { + "search_tools": [ + { + "search_tool_name": "shared-search", + "litellm_params": {"search_provider": "tavily"}, + }, + { + "search_tool_name": "config-only", + "litellm_params": {"search_provider": "perplexity"}, + }, + ] + } + ) + db_tools = [ + { + "search_tool_name": "shared-search", + "litellm_params": { + "search_provider": "exa_ai", + "api_key": "fake-db-key", + }, + } + ] + fake_router = MagicMock() + mock_get_db_tools = AsyncMock(return_value=db_tools) + mock_update_router = AsyncMock() + + monkeypatch.setattr(proxy_server, "llm_router", fake_router) + monkeypatch.setattr( + "litellm.proxy.search_endpoints.search_tool_registry.SearchToolRegistry.get_all_search_tools_from_db", + mock_get_db_tools, + ) + monkeypatch.setattr(SearchAPIRouter, "update_router_search_tools", mock_update_router) + + await pc._init_search_tools_in_db(prisma_client=MagicMock()) + + mock_get_db_tools.assert_awaited_once() + mock_update_router.assert_awaited_once() + update_kwargs = mock_update_router.await_args.kwargs + assert update_kwargs["router_instance"] is fake_router + assert [tool["search_tool_name"] for tool in update_kwargs["search_tools"]] == [ + "config-only", + "shared-search", + ] + assert update_kwargs["search_tools"][1]["litellm_params"]["api_key"] == "fake-db-key" + + +@pytest.mark.asyncio +async def test_ProxyConfig__init_search_tools_in_db_skips_empty_router_update(monkeypatch): + from litellm.proxy import proxy_server + from litellm.router_utils.search_api_router import SearchAPIRouter + + pc = ProxyConfig() + pc.update_config_state({}) + mock_get_db_tools = AsyncMock(return_value=[]) + mock_update_router = AsyncMock() + + monkeypatch.setattr(proxy_server, "llm_router", MagicMock()) + monkeypatch.setattr( + "litellm.proxy.search_endpoints.search_tool_registry.SearchToolRegistry.get_all_search_tools_from_db", + mock_get_db_tools, + ) + monkeypatch.setattr(SearchAPIRouter, "update_router_search_tools", mock_update_router) + + await pc._init_search_tools_in_db(prisma_client=MagicMock()) + + mock_get_db_tools.assert_awaited_once() + mock_update_router.assert_not_awaited() + + # --------------------------------------------------------------------------- # ProxyConfig._load_environment_variables # --------------------------------------------------------------------------- @@ -542,9 +673,7 @@ def test_ProxyConfig_parse_search_tools_missing_returns_none(): def test_ProxyConfig__load_environment_variables_sets_env(monkeypatch): monkeypatch.delenv("TEST_LOAD_ENV_X", raising=False) pc = ProxyConfig() - pc._load_environment_variables( - {"environment_variables": {"TEST_LOAD_ENV_X": "hello"}} - ) + pc._load_environment_variables({"environment_variables": {"TEST_LOAD_ENV_X": "hello"}}) result = { "TEST_LOAD_ENV_X": os.environ.get("TEST_LOAD_ENV_X"), "set": True, @@ -591,6 +720,37 @@ async def test_ProxyConfig_load_config_minimal_yaml(tmp_path, monkeypatch): } +@pytest.mark.asyncio +async def test_ProxyConfig_load_config_wires_general_settings_url_validation(tmp_path, monkeypatch): + """Regression for #26599: SSRF settings in general_settings must reach litellm globals.""" + f = tmp_path / "c.yaml" + f.write_text( + "model_list: []\n" + "general_settings:\n" + " user_url_validation: false\n" + " user_url_allowed_hosts:\n" + " - internal.corp\n" + " provider_url_destination_allowed_hosts:\n" + " - api.example.com\n" + ) + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False) + monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False) + + original_validation = litellm.user_url_validation + original_hosts = list(litellm.user_url_allowed_hosts) + original_provider_hosts = list(litellm.provider_url_destination_allowed_hosts) + try: + await ProxyConfig().load_config(router=None, config_file_path=str(f)) + assert litellm.user_url_validation is False + assert litellm.user_url_allowed_hosts == ["internal.corp"] + assert litellm.provider_url_destination_allowed_hosts == ["api.example.com"] + finally: + litellm.user_url_validation = original_validation + litellm.user_url_allowed_hosts = original_hosts + litellm.provider_url_destination_allowed_hosts = original_provider_hosts + + @pytest.mark.asyncio async def test_ProxyConfig_load_config_missing_file_raises(monkeypatch): monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None) @@ -602,9 +762,7 @@ async def test_ProxyConfig_load_config_missing_file_raises(monkeypatch): @pytest.mark.asyncio -async def test_ProxyConfig_load_config_forwards_callback_specific_params( - tmp_path, monkeypatch -): +async def test_ProxyConfig_load_config_forwards_callback_specific_params(tmp_path, monkeypatch): """Regression: callback_settings from config must be forwarded to initialize_callbacks_on_proxy as callback_specific_params. @@ -645,16 +803,12 @@ async def test_ProxyConfig_load_config_forwards_callback_specific_params( # The callbacks branch must forward the loaded callback_settings. assert captured.get("callback_specific_params") == { - "datadog_cost_management": { - "cost_tag_keys": ["capability", "platform", "ai_product"] - } + "datadog_cost_management": {"cost_tag_keys": ["capability", "platform", "ai_product"]} } @pytest.mark.asyncio -async def test_ProxyConfig_load_config_blank_callback_settings_does_not_crash( - tmp_path, monkeypatch -): +async def test_ProxyConfig_load_config_blank_callback_settings_does_not_crash(tmp_path, monkeypatch): """Regression: `callback_settings:` with no body loads as None because dict.get() only falls back to the default when the key is absent. The None was forwarded verbatim to initialize_callbacks_on_proxy, where the first @@ -678,17 +832,13 @@ async def test_ProxyConfig_load_config_blank_callback_settings_does_not_crash( CompressionInterceptionLogger, ) - original_callbacks = ( - list(litellm.callbacks) if isinstance(litellm.callbacks, list) else [] - ) + original_callbacks = list(litellm.callbacks) if isinstance(litellm.callbacks, list) else [] litellm.callbacks = [] try: pc = ProxyConfig() await pc.load_config(router=None, config_file_path=str(f)) - assert any( - isinstance(c, CompressionInterceptionLogger) for c in litellm.callbacks - ) + assert any(isinstance(c, CompressionInterceptionLogger) for c in litellm.callbacks) finally: litellm.callbacks = original_callbacks @@ -784,6 +934,70 @@ def test_ProxyConfig__load_alerting_settings_invalid_alerting_raises(): pc._load_alerting_settings({"alerting": 12345}) +def test_ProxyConfig__load_alerting_settings_does_not_log_general_settings_dict(monkeypatch): + """Regression for LIT-4152. + + ``_load_alerting_settings`` used to log ``general_settings`` verbatim in a + line labelled ``_alerting_callbacks:``, leaking ``master_key``, + ``database_url``, and any other secret sitting in ``general_settings`` in + cleartext at DEBUG. The fix logs only the alerting callback list. + + The regression check runs with the last-line-of-defense regex scrubber + (``SecretRedactionFilter``) DISABLED, since defense in depth is the point. + The caller must not construct the leaky string, so consumers of the log + stream that bypass the module filter (versions before it existed, + ``LITELLM_DISABLE_REDACT_SECRETS=true`` operators, downstream handlers + that snapshot the record pre-filter) still do not see the secret. Uses a + dedicated handler rather than caplog because caplog is unreliable under + pytest-xdist. + """ + import logging + + import litellm._logging as _logging_module + from litellm._logging import verbose_proxy_logger + + monkeypatch.setattr(_logging_module, "_ENABLE_SECRET_REDACTION", False) + + class LogRecordHandler(logging.Handler): + def __init__(self) -> None: + super().__init__() + self.records: list[logging.LogRecord] = [] + + def emit(self, record: logging.LogRecord) -> None: + self.records.append(record) + + master_key_secret = "sk-lit4152-regression-master-key-abcdef1234567890" + db_url_secret = "postgresql://leak_user:leak_password_9090@leak-host.internal:5432/leak_db" + settings = { + "alerting": ["slack"], + "alerting_threshold": 300, + "master_key": master_key_secret, + "database_url": db_url_secret, + } + + handler = LogRecordHandler() + handler.setLevel(logging.DEBUG) + original_level = verbose_proxy_logger.level + verbose_proxy_logger.setLevel(logging.DEBUG) + verbose_proxy_logger.addHandler(handler) + try: + try: + ProxyConfig()._load_alerting_settings(settings) + except Exception: + pass # downstream init may fail without full env; the debug log fires first + rendered = " ".join(record.getMessage() for record in handler.records) + finally: + verbose_proxy_logger.removeHandler(handler) + verbose_proxy_logger.setLevel(original_level) + + assert master_key_secret not in rendered, f"master_key leaked in logs: {rendered!r}" + assert db_url_secret not in rendered, f"database_url leaked in logs: {rendered!r}" + assert "leak_password_9090" not in rendered + assert any("['slack']" in r.getMessage() for r in handler.records), ( + f"expected the alerting callback list to appear in a debug record; got {[r.getMessage() for r in handler.records]!r}" + ) + + # --------------------------------------------------------------------------- # ProxyConfig.initialize_secret_manager # --------------------------------------------------------------------------- @@ -888,6 +1102,11 @@ def test_ProxyConfig__add_deployment_invalid_litellm_params_skips(monkeypatch): def test_ProxyConfig__add_deployment_resolves_env_refs_after_db_decrypt(monkeypatch): + """Every ``os.environ/`` value on an admin-scoped DB row resolves at + load time, regardless of the field name. Replaces the earlier + behavior where only fields in ``_DB_LITELLM_PARAM_ENV_REF_KEYS`` + resolved: the whitelist has been removed so the resolver applies to + every string field.""" monkeypatch.setenv("LITELLM_DB_MODEL_API_KEY", "resolved-secret") monkeypatch.setenv("LITELLM_MASTER_KEY", "master-secret") monkeypatch.setattr( @@ -915,19 +1134,21 @@ def test_ProxyConfig__add_deployment_resolves_env_refs_after_db_decrypt(monkeypa assert added == 1 assert deployment.litellm_params.api_key == "resolved-secret" - assert deployment.litellm_params.api_base == "os.environ/LITELLM_MASTER_KEY" + assert deployment.litellm_params.api_base == "master-secret" -def test_ProxyConfig__add_deployment_keeps_team_env_refs_literal(monkeypatch): - def fail_on_call(secret_name, *args, **kwargs): - raise AssertionError("team DB models should not resolve env refs") - +def test_ProxyConfig__add_deployment_resolves_team_env_refs(monkeypatch): + """Team-scoped DB rows now resolve ``os.environ/`` refs the same way + admin rows do. The prior team-scoped short-circuit and the + field-by-field whitelist have both been removed; the write-side team + auth check in ``ModelManagementAuthChecks.can_user_make_model_call`` + remains the single trust boundary. A literal (non-``os.environ/``) + value still passes through unchanged.""" monkeypatch.setenv("LITELLM_MASTER_KEY", "master-secret") monkeypatch.setattr( "litellm.proxy.proxy_server.decrypt_value_helper", lambda value, key, return_original_value: value, ) - monkeypatch.setattr("litellm.proxy.proxy_server.get_secret", fail_on_call) fake_router = MagicMock() fake_router.upsert_deployment = MagicMock(return_value=True) monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router) @@ -939,7 +1160,7 @@ def test_ProxyConfig__add_deployment_keeps_team_env_refs_literal(monkeypatch): litellm_params={ "model": "openai/gpt-4o-mini", "api_key": "os.environ/LITELLM_MASTER_KEY", - "api_base": "https://attacker.example", + "api_base": "https://team.example", }, blocked=False, ) @@ -948,8 +1169,8 @@ def test_ProxyConfig__add_deployment_keeps_team_env_refs_literal(monkeypatch): deployment = fake_router.upsert_deployment.call_args.kwargs["deployment"] assert added == 1 - assert deployment.litellm_params.api_key == "os.environ/LITELLM_MASTER_KEY" - assert deployment.litellm_params.api_base == "https://attacker.example" + assert deployment.litellm_params.api_key == "master-secret" + assert deployment.litellm_params.api_base == "https://team.example" def test_ProxyConfig__resolve_db_litellm_param_skips_non_string_values(monkeypatch): @@ -965,6 +1186,100 @@ def test_ProxyConfig__resolve_db_litellm_param_skips_non_string_values(monkeypat assert pc._resolve_db_litellm_param(key="tpm", value=100) == 100 +def test_ProxyConfig__add_deployment_resolves_env_refs_for_aws_bedrock_auth_params( + monkeypatch, +): + """Regression: DB-stored Bedrock/SageMaker auth params like + ``aws_role_name: os.environ/BEDROCK_ASSUME_ROLE_ARN`` must resolve at + DB-load time. PR #30867 removed request-time expansion in + ``BaseAWSLLM.get_credentials``; without DB-load resolution the literal + string reaches STS and fails with ``ValidationError: ... is invalid``.""" + aws_env = { + "aws_session_token": ("BEDROCK_SESSION_TOKEN", "resolved-session-token"), + "aws_region_name": ("BEDROCK_REGION", "us-east-1"), + "aws_session_name": ("BEDROCK_SESSION_NAME", "resolved-session"), + "aws_profile_name": ("BEDROCK_PROFILE", "resolved-profile"), + "aws_role_name": ( + "BEDROCK_ASSUME_ROLE_ARN", + "arn:aws:iam::123456789012:role/resolved", + ), + "aws_web_identity_token": ("BEDROCK_WEB_IDENTITY_TOKEN", "resolved-token"), + "aws_sts_endpoint": ( + "BEDROCK_STS_ENDPOINT", + "https://sts.us-east-1.amazonaws.com", + ), + "aws_external_id": ("BEDROCK_EXTERNAL_ID", "resolved-external-id"), + "aws_bedrock_runtime_endpoint": ( + "BEDROCK_RUNTIME_ENDPOINT", + "https://bedrock-runtime.us-east-1.amazonaws.com", + ), + "aws_bedrock_project_id": ("BEDROCK_PROJECT_ID", "resolved-project-id"), + "aws_batch_role_arn": ( + "BEDROCK_BATCH_ROLE_ARN", + "arn:aws:iam::123456789012:role/batch", + ), + "aws_workspace_id": ("BEDROCK_WORKSPACE_ID", "resolved-workspace-id"), + } + for _, (env_name, env_value) in aws_env.items(): + monkeypatch.setenv(env_name, env_value) + monkeypatch.setattr( + "litellm.proxy.proxy_server.decrypt_value_helper", + lambda value, key, return_original_value: value, + ) + fake_router = MagicMock() + fake_router.upsert_deployment = MagicMock(return_value=True) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router) + pc = ProxyConfig() + litellm_params: Dict[str, Any] = {"model": "bedrock/anthropic.claude-v2"} + for key, (env_name, _) in aws_env.items(): + litellm_params[key] = f"os.environ/{env_name}" + db_model = SimpleNamespace( + model_id="model-1", + model_name="bedrock-model", + model_info={"id": "model-1"}, + litellm_params=litellm_params, + blocked=False, + ) + + added = pc._add_deployment(db_models=[db_model]) + deployment = fake_router.upsert_deployment.call_args.kwargs["deployment"] + + assert added == 1 + for key, (_, expected) in aws_env.items(): + assert getattr(deployment.litellm_params, key) == expected, key + + +def test_ProxyConfig__add_deployment_resolves_env_refs_on_arbitrary_field(monkeypatch): + """A made-up field name that was never on the removed whitelist still + resolves ``os.environ/`` refs. Pins the "no whitelist" invariant: + the resolver applies to every string field, not a curated list.""" + monkeypatch.setenv("SOME_CUSTOM_ENV", "resolved-custom-value") + monkeypatch.setattr( + "litellm.proxy.proxy_server.decrypt_value_helper", + lambda value, key, return_original_value: value, + ) + fake_router = MagicMock() + fake_router.upsert_deployment = MagicMock(return_value=True) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router) + pc = ProxyConfig() + db_model = SimpleNamespace( + model_id="model-1", + model_name="custom-field-model", + model_info={"id": "model-1"}, + litellm_params={ + "model": "openai/gpt-4o-mini", + "some_future_field": "os.environ/SOME_CUSTOM_ENV", + }, + blocked=False, + ) + + added = pc._add_deployment(db_models=[db_model]) + deployment = fake_router.upsert_deployment.call_args.kwargs["deployment"] + + assert added == 1 + assert deployment.litellm_params.some_future_field == "resolved-custom-value" + + # --------------------------------------------------------------------------- # ProxyConfig.decrypt_model_list_from_db # --------------------------------------------------------------------------- @@ -1000,6 +1315,9 @@ def test_ProxyConfig_decrypt_model_list_from_db_returns_decrypted(monkeypatch): def test_ProxyConfig_decrypt_model_list_from_db_resolves_env_refs_after_db_decrypt( monkeypatch, ): + """Path B (feeding /v2/model/info fallback and /model/info fallback) + resolves every ``os.environ/`` field on admin-scoped rows, mirroring + path A. Both paths now share the same universal-resolution shape.""" monkeypatch.setenv("LITELLM_DB_MODEL_API_KEY", "resolved-secret") monkeypatch.setenv("LITELLM_MASTER_KEY", "master-secret") monkeypatch.setattr( @@ -1007,7 +1325,9 @@ def test_ProxyConfig_decrypt_model_list_from_db_resolves_env_refs_after_db_decry lambda value, key, return_original_value: ( "os.environ/LITELLM_DB_MODEL_API_KEY" if key == "api_key" - else "os.environ/LITELLM_MASTER_KEY" if key == "api_base" else value + else "os.environ/LITELLM_MASTER_KEY" + if key == "api_base" + else value ), ) pc = ProxyConfig() @@ -1026,23 +1346,21 @@ def test_ProxyConfig_decrypt_model_list_from_db_resolves_env_refs_after_db_decry out = pc.decrypt_model_list_from_db(new_models=[m]) assert out[0]["litellm_params"]["api_key"] == "resolved-secret" - assert out[0]["litellm_params"]["api_base"] == "os.environ/LITELLM_MASTER_KEY" + assert out[0]["litellm_params"]["api_base"] == "master-secret" -def test_ProxyConfig_decrypt_model_list_from_db_keeps_team_env_refs_literal_after_db_decrypt( +def test_ProxyConfig_decrypt_model_list_from_db_resolves_team_env_refs_after_db_decrypt( monkeypatch, ): - def fail_on_call(secret_name, *args, **kwargs): - raise AssertionError("team DB models should not resolve env refs") - + """Team-scoped rows on path B resolve ``os.environ/`` refs just like + admin rows do. Pairs with + ``test_ProxyConfig__add_deployment_resolves_team_env_refs`` on path + A — both paths now agree on the trust model.""" monkeypatch.setenv("LITELLM_MASTER_KEY", "master-secret") monkeypatch.setattr( "litellm.proxy.proxy_server.decrypt_value_helper", - lambda value, key, return_original_value: ( - "os.environ/LITELLM_MASTER_KEY" if key == "api_key" else value - ), + lambda value, key, return_original_value: "os.environ/LITELLM_MASTER_KEY" if key == "api_key" else value, ) - monkeypatch.setattr("litellm.proxy.proxy_server.get_secret", fail_on_call) pc = ProxyConfig() m = SimpleNamespace( model_id="model-1", @@ -1050,7 +1368,7 @@ def test_ProxyConfig_decrypt_model_list_from_db_keeps_team_env_refs_literal_afte model_info={"id": "model-1", "team_id": "team-1"}, litellm_params={ "api_key": "encrypted-env-ref", - "api_base": "https://attacker.example", + "api_base": "https://team.example", "model": "openai/gpt-4o-mini", }, blocked=False, @@ -1058,15 +1376,13 @@ def test_ProxyConfig_decrypt_model_list_from_db_keeps_team_env_refs_literal_afte out = pc.decrypt_model_list_from_db(new_models=[m]) - assert out[0]["litellm_params"]["api_key"] == "os.environ/LITELLM_MASTER_KEY" - assert out[0]["litellm_params"]["api_base"] == "https://attacker.example" + assert out[0]["litellm_params"]["api_key"] == "master-secret" + assert out[0]["litellm_params"]["api_base"] == "https://team.example" def test_ProxyConfig_decrypt_model_list_from_db_invalid_params_skips(): pc = ProxyConfig() - bad = SimpleNamespace( - model_id="m-1", model_name="x", model_info={}, litellm_params="not-a-dict" - ) + bad = SimpleNamespace(model_id="m-1", model_name="x", model_info={}, litellm_params="not-a-dict") out = pc.decrypt_model_list_from_db(new_models=[bad]) # Invalid entries skipped — empty list returned. assert out == [] @@ -1116,9 +1432,7 @@ async def test_ProxyConfig__update_llm_router_bad_proxy_logging_raises(monkeypat monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router) monkeypatch.setattr("litellm.proxy.proxy_server.master_key", "sk-x") monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None) - monkeypatch.setattr( - "litellm.proxy.proxy_server.general_settings", {"alerting": ["email"]} - ) + monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {"alerting": ["email"]}) monkeypatch.setattr("litellm.proxy.proxy_server.proxy_config", pc) # Passing None for proxy_logging_obj triggers AttributeError in _add_general_settings_from_db_config # when it calls proxy_logging_obj.update_values. @@ -1369,9 +1683,7 @@ async def test_ProxyConfig__add_router_settings_from_db_config_updates_router(): fake_router.update_settings = MagicMock() fake_prisma = MagicMock() fake_prisma.db.litellm_config.find_first = AsyncMock( - return_value=SimpleNamespace( - param_value={"timeout": 30, "retries": 2, "fallbacks": []} - ) + return_value=SimpleNamespace(param_value={"timeout": 30, "retries": 2, "fallbacks": []}) ) config_data = {"router_settings": {"timeout": 10}} await pc._add_router_settings_from_db_config( @@ -1382,9 +1694,7 @@ async def test_ProxyConfig__add_router_settings_from_db_config_updates_router(): snapshot = { "called": fake_router.update_settings.called, "call_count": fake_router.update_settings.call_count, - "kwargs_keys": sorted( - list(fake_router.update_settings.call_args.kwargs.keys()) - ), + "kwargs_keys": sorted(list(fake_router.update_settings.call_args.kwargs.keys())), } assert snapshot == { "called": True, @@ -1397,9 +1707,7 @@ async def test_ProxyConfig__add_router_settings_from_db_config_updates_router(): async def test_ProxyConfig__add_router_settings_from_db_config_none_router_noop(): pc = ProxyConfig() # No router and no prisma — should silently return. - await pc._add_router_settings_from_db_config( - config_data={}, llm_router=None, prisma_client=None - ) + await pc._add_router_settings_from_db_config(config_data={}, llm_router=None, prisma_client=None) # Error-style: bad call signature raises. with pytest.raises(TypeError): await pc._add_router_settings_from_db_config() # type: ignore[call-arg] @@ -1504,9 +1812,7 @@ async def test_ProxyConfig__update_general_settings_updates_max_parallel(monkeyp snapshot = { "max_parallel_requests": ps.general_settings.get("max_parallel_requests"), - "global_max_parallel_requests": ps.general_settings.get( - "global_max_parallel_requests" - ), + "global_max_parallel_requests": ps.general_settings.get("global_max_parallel_requests"), "ui_access_mode": ps.general_settings.get("ui_access_mode"), } assert snapshot == { @@ -1548,3 +1854,158 @@ def test_ProxyConfig__update_config_fields_invalid_param_raises(): with pytest.raises(Exception): # Missing required arg. pc._update_config_fields(current_config={}, param_name="general_settings") # type: ignore[call-arg] + + +# --------------------------------------------------------------------------- +# ProxyConfig._update_config_from_db +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_ProxyConfig__update_config_from_db_does_not_log_general_settings_secrets( + monkeypatch, +): + """Regression for LIT-4152 on the store_model_in_db path. + + ``_update_config_from_db`` logged each DB ``param_value`` verbatim at DEBUG; + for ``general_settings`` that value is the whole dict, leaking ``master_key`` + and ``database_url`` the same way the startup config load did. The value now + routes through the recursive redactor. Asserted with the module regex + scrubber (``_ENABLE_SECRET_REDACTION``) disabled so the caller itself must + not build the leaky string. The merge into the returned config must still + carry the raw values, proving only the log record is redacted. + """ + import logging + + import litellm._logging as _logging_module + from litellm._logging import verbose_proxy_logger + + monkeypatch.setattr(_logging_module, "_ENABLE_SECRET_REDACTION", False) + monkeypatch.delenv("DATABASE_URL", raising=False) + + def _fake_decrypt_value_helper(value, key, **_kwargs): + return value + + monkeypatch.setattr("litellm.proxy.proxy_server.decrypt_value_helper", _fake_decrypt_value_helper) + + master_key_secret = "sk-lit4152-db-path-master-key-abcdef1234567890" + db_url_secret = "postgresql://leak_user:leak_password_9090@leak-host.internal:5432/leak_db" + env_db_url_secret = "postgresql://env_leak_user:env_leak_password_9090@env-leak-host.internal:5432/env_leak_db" + nested_webhook_secret = "https://hooks.slack.com/services/T0/B0/db-path-webhook-secret" + + responses = { + "general_settings": SimpleNamespace( + param_name="general_settings", + param_value={ + "master_key": master_key_secret, + "database_url": db_url_secret, + "alert_to_webhook_url": {"budget_alerts": nested_webhook_secret}, + }, + ), + "router_settings": None, + "litellm_settings": None, + "environment_variables": SimpleNamespace( + param_name="environment_variables", + param_value={"DATABASE_URL": env_db_url_secret}, + ), + } + + async def _fake_get_config_param(prisma_client, key): + return responses[key] + + monkeypatch.setattr("litellm.proxy.proxy_server.get_config_param", _fake_get_config_param) + + class LogRecordHandler(logging.Handler): + def __init__(self) -> None: + super().__init__() + self.records: list[logging.LogRecord] = [] + + def emit(self, record: logging.LogRecord) -> None: + self.records.append(record) + + handler = LogRecordHandler() + handler.setLevel(logging.DEBUG) + original_level = verbose_proxy_logger.level + verbose_proxy_logger.setLevel(logging.DEBUG) + verbose_proxy_logger.addHandler(handler) + try: + merged = await ProxyConfig()._update_config_from_db( + prisma_client=MagicMock(), + config={"general_settings": {}}, + store_model_in_db=True, + ) + rendered = " ".join(record.getMessage() for record in handler.records) + finally: + verbose_proxy_logger.removeHandler(handler) + verbose_proxy_logger.setLevel(original_level) + + for secret in ( + master_key_secret, + db_url_secret, + env_db_url_secret, + nested_webhook_secret, + "leak_password_9090", + "env_leak_password_9090", + ): + assert secret not in rendered, f"leak: {secret} in {rendered!r}" + assert merged["general_settings"]["master_key"] == master_key_secret + assert merged["general_settings"]["database_url"] == db_url_secret + assert merged["environment_variables"]["DATABASE_URL"] == env_db_url_secret + + +@pytest.mark.asyncio +async def test_ProxyConfig_load_config_redacts_secret_litellm_setting_keeps_plain(tmp_path, monkeypatch): + """Regression for LIT-4152 on the ``litellm_settings`` apply loop. + + ``load_config`` logged ``setting litellm.=`` verbatim at DEBUG, + so a secret-bearing setting such as ``api_key`` leaked in cleartext. The + value now routes through ``_redact_general_setting_value``. Crucially the + redaction must be surgical: a secret-named key is masked, but a plain + operational setting like ``num_retries`` must still log its real value, so + the debug line keeps its signal. Asserted with the module regex scrubber + (``_ENABLE_SECRET_REDACTION``) disabled. + """ + import logging + + import litellm._logging as _logging_module + from litellm._logging import verbose_proxy_logger + + monkeypatch.setattr(_logging_module, "_ENABLE_SECRET_REDACTION", False) + + api_key_secret = "sk-lit4152-litellm-settings-secret-abcdef1234567890" + f = tmp_path / "c.yaml" + f.write_text( + f"model_list: []\ngeneral_settings: {{}}\nlitellm_settings:\n api_key: {api_key_secret}\n num_retries: 7\n" + ) + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False) + monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False) + + class LogRecordHandler(logging.Handler): + def __init__(self) -> None: + super().__init__() + self.records: list[logging.LogRecord] = [] + + def emit(self, record: logging.LogRecord) -> None: + self.records.append(record) + + handler = LogRecordHandler() + handler.setLevel(logging.DEBUG) + original_level = verbose_proxy_logger.level + original_api_key = getattr(litellm, "api_key", None) + original_num_retries = getattr(litellm, "num_retries", None) + verbose_proxy_logger.setLevel(logging.DEBUG) + verbose_proxy_logger.addHandler(handler) + try: + await ProxyConfig().load_config(router=None, config_file_path=str(f)) + rendered = " ".join(record.getMessage() for record in handler.records) + finally: + verbose_proxy_logger.removeHandler(handler) + verbose_proxy_logger.setLevel(original_level) + litellm.api_key = original_api_key + litellm.num_retries = original_num_retries + + assert api_key_secret not in rendered, f"api_key leaked in logs: {rendered!r}" + assert "num_retries=7" in rendered, ( + f"non-secret num_retries value was over-redacted; expected it visible in {rendered!r}" + ) diff --git a/tests/test_litellm/proxy/proxy_server/test_routes_config.py b/tests/test_litellm/proxy/proxy_server/test_routes_config.py index df14dc5b5dc..4ac6fc46a61 100644 --- a/tests/test_litellm/proxy/proxy_server/test_routes_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_routes_config.py @@ -741,6 +741,222 @@ def test_get_config_callbacks_internal_error(client, auth_as, mock_prisma, monke ) +_CALLBACK_ENV_FIXTURE = { + "LANGFUSE_PUBLIC_KEY": "pk-public-1234567890", + "LANGFUSE_SECRET_KEY": "sk-langfuse-super-secret", + "LANGFUSE_HOST": "https://cloud.langfuse.com", + "DD_API_KEY": "dd-super-secret-api-key", + "DD_SITE": "datadoghq.com", + "OTEL_HEADERS": "Authorization=Bearer otel-super-secret", + "OTEL_ENDPOINT": "https://otlp.example.com", + "SLACK_WEBHOOK_URL": "https://hooks.slack.com/services/T000/B000/SLACK-WEBHOOK-FIXTURE-SECRET", +} + + +def _install_callbacks_config(monkeypatch, mock_prisma): + from litellm.proxy import proxy_server as ps + + _install_litellm_config(mock_prisma) + monkeypatch.setattr(ps, "prisma_client", mock_prisma) + monkeypatch.setattr(ps, "llm_router", None) + + fake_proxy_config = MagicMock() + fake_proxy_config.get_config = AsyncMock( + return_value={ + "litellm_settings": {"success_callback": ["langfuse", "datadog", "otel"]}, + "general_settings": {"alerting": ["slack"]}, + "environment_variables": dict(_CALLBACK_ENV_FIXTURE), + } + ) + monkeypatch.setattr(ps, "proxy_config", fake_proxy_config) + + +def _callback_variables(body: dict, name: str) -> dict: + return next( + cb["variables"] for cb in body["callbacks"] if cb["name"] == name + ) + + +def test_get_config_callbacks_redacts_secret_env_vars_for_view_only_admin( + client, auth_as, mock_prisma, monkeypatch +): + from litellm.proxy._types import LitellmUserRoles + + _install_callbacks_config(monkeypatch, mock_prisma) + + with auth_as(LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY): + response = client.get("/get/config/callbacks") + assert response.status_code == 200 + body = response.json() + + for secret in ( + _CALLBACK_ENV_FIXTURE["LANGFUSE_SECRET_KEY"], + _CALLBACK_ENV_FIXTURE["DD_API_KEY"], + _CALLBACK_ENV_FIXTURE["OTEL_HEADERS"], + _CALLBACK_ENV_FIXTURE["LANGFUSE_PUBLIC_KEY"], + ): + assert secret not in response.text + + langfuse_vars = _callback_variables(body, "langfuse") + assert langfuse_vars["LANGFUSE_PUBLIC_KEY"] == "REDACTED" + assert langfuse_vars["LANGFUSE_SECRET_KEY"] == "REDACTED" + assert langfuse_vars["LANGFUSE_HOST"] == _CALLBACK_ENV_FIXTURE["LANGFUSE_HOST"] + + datadog_vars = _callback_variables(body, "datadog") + assert datadog_vars["DD_API_KEY"] == "REDACTED" + assert datadog_vars["DD_SITE"] == _CALLBACK_ENV_FIXTURE["DD_SITE"] + + otel_vars = _callback_variables(body, "otel") + assert otel_vars["OTEL_HEADERS"] == "REDACTED" + assert otel_vars["OTEL_ENDPOINT"] == _CALLBACK_ENV_FIXTURE["OTEL_ENDPOINT"] + + +def test_get_config_callbacks_full_admin_still_sees_secret_env_vars( + client, auth_as, mock_prisma, monkeypatch +): + from litellm.proxy._types import LitellmUserRoles + + _install_callbacks_config(monkeypatch, mock_prisma) + + with auth_as(LitellmUserRoles.PROXY_ADMIN): + response = client.get("/get/config/callbacks") + assert response.status_code == 200 + body = response.json() + + langfuse_vars = _callback_variables(body, "langfuse") + assert langfuse_vars["LANGFUSE_SECRET_KEY"] == _CALLBACK_ENV_FIXTURE["LANGFUSE_SECRET_KEY"] + assert langfuse_vars["LANGFUSE_PUBLIC_KEY"] == _CALLBACK_ENV_FIXTURE["LANGFUSE_PUBLIC_KEY"] + + datadog_vars = _callback_variables(body, "datadog") + assert datadog_vars["DD_API_KEY"] == _CALLBACK_ENV_FIXTURE["DD_API_KEY"] + + otel_vars = _callback_variables(body, "otel") + assert otel_vars["OTEL_HEADERS"] == _CALLBACK_ENV_FIXTURE["OTEL_HEADERS"] + + +def test_get_config_callbacks_redacts_slack_webhook_urls_for_view_only_admin( + client, auth_as, mock_prisma, monkeypatch +): + from litellm.proxy import proxy_server as ps + from litellm.proxy._types import LitellmUserRoles + + _install_callbacks_config(monkeypatch, mock_prisma) + + webhooks = { + "spend_reports": "https://hooks.slack.com/services/T000/B000/SPEND-WEBHOOK-SECRET", + "budget_alerts": "https://hooks.slack.com/services/T000/B111/BUDGET-WEBHOOK-SECRET", + } + monkeypatch.setattr( + ps.proxy_logging_obj.slack_alerting_instance, + "alert_to_webhook_url", + webhooks, + raising=False, + ) + + def _slack_block(body): + return next(a for a in body["alerts"] if a["name"] == "slack") + + with auth_as(LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY): + view_resp = client.get("/get/config/callbacks") + assert view_resp.status_code == 200 + for url in webhooks.values(): + assert url not in view_resp.text + assert _CALLBACK_ENV_FIXTURE["SLACK_WEBHOOK_URL"] not in view_resp.text + view_slack = _slack_block(view_resp.json()) + assert view_slack["alerts_to_webhook"] == { + "spend_reports": "REDACTED", + "budget_alerts": "REDACTED", + } + assert view_slack["variables"]["SLACK_WEBHOOK_URL"] == "REDACTED" + + with auth_as(LitellmUserRoles.PROXY_ADMIN): + admin_resp = client.get("/get/config/callbacks") + assert admin_resp.status_code == 200 + admin_slack = _slack_block(admin_resp.json()) + assert admin_slack["alerts_to_webhook"] == webhooks + assert admin_slack["variables"]["SLACK_WEBHOOK_URL"] != "REDACTED" + + +def test_redact_callback_env_vars_helper_handles_none_and_non_secret_keys(): + from litellm.proxy import proxy_server as ps + + out = ps._redact_callback_env_vars( + { + "LANGFUSE_SECRET_KEY": "sk-leak", + "LANGFUSE_HOST": "https://cloud.langfuse.com", + "DD_API_KEY": None, + "GALILEO_USERNAME": "galileo-user-1234", + "GENERIC_LOGGER_HEADERS": "Authorization=Bearer x", + "GCS_PATH_SERVICE_ACCOUNT": "/etc/secrets/gcs.json", + "SLACK_WEBHOOK_URL": "https://hooks.slack.com/services/T/B/token", + "SMTP_USERNAME": "smtp-user-1234", + } + ) + assert out == { + "LANGFUSE_SECRET_KEY": "REDACTED", + "LANGFUSE_HOST": "https://cloud.langfuse.com", + "DD_API_KEY": None, + "GALILEO_USERNAME": "REDACTED", + "GENERIC_LOGGER_HEADERS": "REDACTED", + "GCS_PATH_SERVICE_ACCOUNT": "REDACTED", + "SLACK_WEBHOOK_URL": "REDACTED", + "SMTP_USERNAME": "REDACTED", + } + + +def test_get_config_callbacks_redacts_email_alerting_vars_for_view_only_admin( + client, auth_as, mock_prisma, monkeypatch +): + from litellm.proxy import proxy_server as ps + from litellm.proxy._types import LitellmUserRoles + + _install_litellm_config(mock_prisma) + monkeypatch.setattr(ps, "prisma_client", mock_prisma) + monkeypatch.setattr(ps, "llm_router", None) + + fake_proxy_config = MagicMock() + fake_proxy_config.get_config = AsyncMock( + return_value={ + "litellm_settings": {"success_callback": []}, + "general_settings": {"alerting": ["email"]}, + "environment_variables": { + "SMTP_HOST": "smtp.resend.com", + "SMTP_PORT": "587", + "SMTP_USERNAME": "smtp-user-fixture-1234", + "SMTP_PASSWORD": "smtp-password-fixture-1234", + "SMTP_SENDER_EMAIL": "alerts@example.com", + "TEST_EMAIL_ADDRESS": "admin@example.com", + "EMAIL_LOGO_URL": "https://example.com/logo.png", + "EMAIL_SUPPORT_CONTACT": "support@example.com", + }, + } + ) + monkeypatch.setattr(ps, "proxy_config", fake_proxy_config) + + def _email_block(body): + return next(a for a in body["alerts"] if a["name"] == "email") + + with auth_as(LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY): + view_resp = client.get("/get/config/callbacks") + assert view_resp.status_code == 200 + for secret in ("smtp-user-fixture-1234", "smtp-password-fixture-1234"): + assert secret not in view_resp.text + view_email = _email_block(view_resp.json())["variables"] + assert view_email["SMTP_PASSWORD"] == "REDACTED" + assert view_email["SMTP_USERNAME"] == "REDACTED" + assert view_email["SMTP_HOST"] == "smtp.resend.com" + assert view_email["SMTP_PORT"] == "587" + assert view_email["SMTP_SENDER_EMAIL"] == "alerts@example.com" + + with auth_as(LitellmUserRoles.PROXY_ADMIN): + admin_resp = client.get("/get/config/callbacks") + assert admin_resp.status_code == 200 + admin_email = _email_block(admin_resp.json())["variables"] + assert admin_email["SMTP_USERNAME"] == "smtp-user-fixture-1234" + assert admin_email["SMTP_PASSWORD"] != "REDACTED" + assert admin_email["SMTP_HOST"] == "smtp.resend.com" + + # --------------------------------------------------------------------------- # GET /config/yaml # --------------------------------------------------------------------------- diff --git a/tests/test_litellm/proxy/proxy_server/test_spend_counters.py b/tests/test_litellm/proxy/proxy_server/test_spend_counters.py index a839d82984c..51980342a1d 100644 --- a/tests/test_litellm/proxy/proxy_server/test_spend_counters.py +++ b/tests/test_litellm/proxy/proxy_server/test_spend_counters.py @@ -20,6 +20,7 @@ Pins covered: from __future__ import annotations +import asyncio from datetime import datetime from unittest.mock import AsyncMock, MagicMock @@ -420,6 +421,191 @@ async def test_increment_spend_counters_increments_all_buckets(monkeypatch): } +class _ConcurrencyProbe: + """Stand-in for redis_cache.async_increment that pins concurrency. + + Each call registers itself as in-flight and blocks on ``release`` until the + test lets it proceed. ``all_arrived`` fires once ``expected`` distinct scope + increments are simultaneously suspended here, which can only happen if the + per-scope increments are gathered rather than awaited one after another. + """ + + def __init__(self, expected_concurrency: int): + self.expected = expected_concurrency + self.in_flight = 0 + self.max_in_flight = 0 + self.all_arrived = asyncio.Event() + self.release = asyncio.Event() + self.values: dict[str, float] = {} + + async def async_increment(self, *, key, value, refresh_ttl=True): + self.in_flight += 1 + self.max_in_flight = max(self.max_in_flight, self.in_flight) + if self.in_flight >= self.expected: + self.all_arrived.set() + if not self.release.is_set(): + await self.release.wait() + self.in_flight -= 1 + self.values[key] = self.values.get(key, 0.0) + value + return self.values[key] + + +@pytest.mark.asyncio +async def test_increment_spend_counters_runs_scopes_concurrently(monkeypatch): + """The six independent scopes (key, team, team_member, user, end_user+tags, + org) must be incremented concurrently. The probe only fires once all six are + suspended in async_increment at the same time, which is impossible if the + awaits are chained sequentially.""" + probe = _ConcurrencyProbe(expected_concurrency=6) + fake_cache = _make_spend_counter_cache(redis_get_value=None) + fake_cache.redis_cache.async_increment = probe.async_increment + fake_user_cache = _make_user_api_key_cache(get_value=None) + monkeypatch.setattr(ps, "spend_counter_cache", fake_cache) + monkeypatch.setattr(ps, "user_api_key_cache", fake_user_cache) + monkeypatch.setattr(ps, "prisma_client", None) + monkeypatch.setattr( + ps.SpendCounterReseed, "coalesced", AsyncMock(return_value=None) + ) + + task = asyncio.create_task( + ps.increment_spend_counters( + token="hashed-tok", + team_id="t1", + user_id="u1", + org_id="org1", + end_user_id="eu1", + tags=["a", "b"], + response_cost=5.0, + ) + ) + + try: + await asyncio.wait_for(probe.all_arrived.wait(), timeout=2.0) + except asyncio.TimeoutError: + probe.release.set() + await task + pytest.fail( + "scope increments did not run concurrently; sequential awaits " + f"detected (peak in-flight was {probe.max_in_flight}, expected 6)" + ) + + assert probe.in_flight == 6 + assert probe.max_in_flight == 6 + probe.release.set() + await task + + assert probe.values == { + "spend:key:hashed-tok": 5.0, + "spend:team:t1": 5.0, + "spend:team_member:u1:t1": 5.0, + "spend:user:u1": 5.0, + "spend:end_user:eu1": 5.0, + "spend:tag:a": 5.0, + "spend:tag:b": 5.0, + "spend:org:org1": 5.0, + } + + +@pytest.mark.asyncio +async def test_increment_spend_counters_skips_reserved_counter_keys(monkeypatch): + """Counters already reserved by a budget reservation are skipped, every + other scope is still incremented exactly once, and the reservation is + finalized after the gathered work completes.""" + import litellm.proxy.spend_tracking.budget_reservation as br + + reserved = {"spend:key:hashed-tok", "spend:org:org1"} + monkeypatch.setattr( + br, "get_reserved_counter_keys", MagicMock(return_value=set(reserved)) + ) + monkeypatch.setattr(br, "reconcile_budget_reservation", AsyncMock()) + + recorded: dict[str, float] = {} + + async def _record_increment(*, key, value, refresh_ttl=True): + recorded[key] = recorded.get(key, 0.0) + value + return recorded[key] + + fake_cache = _make_spend_counter_cache(redis_get_value=None) + fake_cache.redis_cache.async_increment = _record_increment + fake_user_cache = _make_user_api_key_cache(get_value=None) + monkeypatch.setattr(ps, "spend_counter_cache", fake_cache) + monkeypatch.setattr(ps, "user_api_key_cache", fake_user_cache) + monkeypatch.setattr(ps, "prisma_client", None) + monkeypatch.setattr( + ps.SpendCounterReseed, "coalesced", AsyncMock(return_value=None) + ) + + reservation = {"finalized": False} + await ps.increment_spend_counters( + token="hashed-tok", + team_id="t1", + user_id="u1", + org_id="org1", + end_user_id="eu1", + tags=["a"], + response_cost=5.0, + budget_reservation=reservation, + ) + + assert reservation["finalized"] is True + assert recorded == { + "spend:team:t1": 5.0, + "spend:team_member:u1:t1": 5.0, + "spend:user:u1": 5.0, + "spend:end_user:eu1": 5.0, + "spend:tag:a": 5.0, + } + + +@pytest.mark.asyncio +async def test_increment_spend_counters_failing_scope_propagates_after_siblings_settle( + monkeypatch, +): + """A failure in one scope must propagate to the caller (so it can invalidate + reserved counters) while every other scope still settles rather than being + left as an orphaned background task, and the reservation is not finalized.""" + recorded: dict[str, float] = {} + + async def _increment(*, key, value, refresh_ttl=True): + if key == "spend:team:t1": + raise RuntimeError("redis increment failed") + recorded[key] = recorded.get(key, 0.0) + value + return recorded[key] + + fake_cache = _make_spend_counter_cache(redis_get_value=None) + fake_cache.redis_cache.async_increment = _increment + fake_user_cache = _make_user_api_key_cache(get_value=None) + monkeypatch.setattr(ps, "spend_counter_cache", fake_cache) + monkeypatch.setattr(ps, "user_api_key_cache", fake_user_cache) + monkeypatch.setattr(ps, "prisma_client", None) + monkeypatch.setattr( + ps.SpendCounterReseed, "coalesced", AsyncMock(return_value=None) + ) + + reservation = {"finalized": False} + with pytest.raises(RuntimeError, match="redis increment failed"): + await ps.increment_spend_counters( + token="hashed-tok", + team_id="t1", + user_id="u1", + org_id="org1", + end_user_id="eu1", + tags=["a"], + response_cost=5.0, + budget_reservation=reservation, + ) + + assert reservation["finalized"] is False + assert recorded == { + "spend:key:hashed-tok": 5.0, + "spend:team_member:u1:t1": 5.0, + "spend:user:u1": 5.0, + "spend:end_user:eu1": 5.0, + "spend:tag:a": 5.0, + "spend:org:org1": 5.0, + } + + @pytest.mark.asyncio async def test_increment_spend_counters_zero_cost_is_noop_finalizes_reservation( monkeypatch, diff --git a/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py b/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py index b37bb18744d..ffca5e5a368 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py +++ b/tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py @@ -1,4 +1,5 @@ import asyncio +import collections import datetime import json import os @@ -66,13 +67,14 @@ def _reconstruct_ui_where_from_sql(sql_query, params): Rebuild the Prisma-style ``where`` dict the filter_fns below expect from the raw SQL + params the endpoint emits. - ``ui_view_spend_logs`` folds the total into the page query via - ``COUNT(*) OVER ()`` and no longer issues a separate ``count(where=...)`` - call, so the mock derives the active filter from the one query it sees - instead of from the (now absent) count call. + ``ui_view_spend_logs`` computes the total with a bounded + ``SELECT COUNT(*) FROM (SELECT 1 ... LIMIT $cap+1)`` query and fetches the + page with a separate ``ORDER BY ... LIMIT/OFFSET`` query. Both carry the + same WHERE clause, so the terminator can be ``ORDER BY`` (page query) or + ``LIMIT`` (bounded count query). """ where: dict = {} - clause = re.search(r"WHERE (.*) ORDER BY", sql_query, re.DOTALL) + clause = re.search(r"WHERE (.*?)\s+(?:ORDER BY|LIMIT)", sql_query, re.DOTALL) if clause is None: return where @@ -97,6 +99,7 @@ def _reconstruct_ui_where_from_sql(sql_query, params): alias = re.search(r"user_api_key_alias' LIKE \$(\d+)", cond) code = re.search(r"error_code' = \$(\d+)", cond) msg = re.search(r"error_message' LIKE \$(\d+)", cond) + sess = re.fullmatch(r"session_id LIKE \$(\d+)", cond) status = re.fullmatch(r"status = \$(\d+)", cond) if gte: date_bounds["gte"] = _iso(params[int(gte.group(1)) - 1]) @@ -106,6 +109,8 @@ def _reconstruct_ui_where_from_sql(sql_query, params): where["OR"] = where.get("OR", []) + [{"multi_team": True}] elif "status = 'success'" in cond: where["OR"] = where.get("OR", []) + [{"status": "success"}] + elif sess: + where["session_id"] = {"contains": str(params[int(sess.group(1)) - 1]).strip("%")} elif status: where["status"] = {"equals": params[int(status.group(1)) - 1]} elif alias: @@ -161,15 +166,27 @@ def make_ui_spend_logs_mock_prisma(mock_spend_logs, filter_fn, team_lookup_fn=No async def count(self, *args, **kwargs): return len(filter_fn(kwargs.get("where", {}))) + async def group_by(self, by, where, count): + col = by[0] + allowed = where.get(col, {}).get("in") + tallied = collections.Counter( + log[col] + for log in mock_spend_logs + if log.get(col) is not None and (allowed is None or log[col] in allowed) + ) + return [{col: value, "_count": {col: n}} for value, n in tallied.items()] + async def query_raw(self, sql_query, *params): + if "mcp_tool_call_count" in sql_query: + return [] filtered = filter_fn(_reconstruct_ui_where_from_sql(sql_query, params)) + total = len(filtered) + if "COUNT(*)" in sql_query: + cap_plus_one = params[-1] + return [{"total_count": min(total, cap_plus_one)}] page_size = params[-2] if len(params) >= 2 else 50 skip = params[-1] if len(params) >= 1 else 0 - total = len(filtered) - return [ - {**row, "total_count": total} - for row in filtered[skip : skip + page_size] - ] + return [row for row in filtered[skip : skip + page_size]] class MockPrismaClient: def __init__(self): @@ -596,6 +613,73 @@ async def test_ui_view_spend_logs_with_user_id(client, monkeypatch): assert data["data"][0]["user"] == "test_user_1" +@pytest.mark.asyncio +@pytest.mark.parametrize( + "session_id_query,expected_request_ids", + [ + ("session-filter-demo-1", {"req1", "req2"}), + ("session-filter-demo-2", {"req3"}), + ("session-filter", {"req1", "req2", "req3"}), + ("demo", {"req1", "req2", "req3"}), + ("no-such-session", set()), + ], +) +async def test_ui_view_spend_logs_with_session_id( + client, monkeypatch, session_id_query, expected_request_ids +): + def make_log(request_id, session_id): + return { + "id": f"log-{request_id}", + "request_id": request_id, + "api_key": "sk-test-key", + "user": "test_user_1", + "session_id": session_id, + "spend": 0.05, + "startTime": datetime.datetime.now(timezone.utc).isoformat(), + "model": "gpt-4", + } + + mock_spend_logs = [ + make_log("req1", "session-filter-demo-1"), + make_log("req2", "session-filter-demo-1"), + make_log("req3", "session-filter-demo-2"), + make_log("req4", "unrelated-abc"), + ] + + def filter_by_session(where): + session_filter = where.get("session_id") + if session_filter is None: + return mock_spend_logs + return [ + log + for log in mock_spend_logs + if session_filter["contains"] in log["session_id"] + ] + + monkeypatch.setattr( + "litellm.proxy.proxy_server.prisma_client", + make_ui_spend_logs_mock_prisma(mock_spend_logs, filter_by_session), + ) + + start_date, end_date = _default_date_range() + + response = client.get( + "/spend/logs/ui", + params={ + "session_id": session_id_query, + "start_date": start_date, + "end_date": end_date, + }, + headers={"Authorization": "Bearer sk-test"}, + ) + + assert response.status_code == 200 + data = response.json() + assert data["total"] == len(expected_request_ids) + assert {log["request_id"] for log in data["data"]} == expected_request_ids + assert all(session_id_query in log["session_id"] for log in data["data"]) + + # Mock spend logs with distinct values for sorting tests. # req_a: spend=0.10, tokens=500, start/end earliest # req_b: spend=0.05, tokens=200, start/end 2nd @@ -684,6 +768,8 @@ async def test_ui_view_spend_logs_sort_by_and_sort_order( return len(base_logs) async def mock_query_raw(sql_query, *params): + if "COUNT(*)" in sql_query: + return [{"total_count": len(base_logs)}] # Endpoint uses raw SQL with ORDER BY startTime DESC; mock returns sorted data order = ( {"startTime": "desc"} @@ -693,10 +779,7 @@ async def test_ui_view_spend_logs_sort_by_and_sort_order( sorted_logs = _sort_logs(base_logs, order) page_size = params[-2] if len(params) >= 2 else 50 skip = params[-1] if len(params) >= 1 else 0 - return [ - {**row, "total_count": len(base_logs)} - for row in sorted_logs[skip : skip + page_size] - ] + return [row for row in sorted_logs[skip : skip + page_size]] class MockPrismaClient: def __init__(self): @@ -830,16 +913,15 @@ async def test_ui_view_spend_logs_sort_by_request_duration_ms(client, monkeypatc return len(base_logs) async def mock_query_raw(sql_query, *params): + if "COUNT(*)" in sql_query: + return [{"total_count": len(base_logs)}] reverse = "DESC" in sql_query sorted_logs = sorted( base_logs, key=lambda x: x.get("request_duration_ms", 0), reverse=reverse ) page_size = params[-2] if len(params) >= 2 else 50 skip = params[-1] if len(params) >= 1 else 0 - return [ - {**row, "total_count": len(base_logs)} - for row in sorted_logs[skip : skip + page_size] - ] + return [row for row in sorted_logs[skip : skip + page_size]] class MockPrismaClient: def __init__(self): @@ -926,6 +1008,8 @@ async def test_ui_view_spend_logs_sort_by_model( return len(base_logs) async def mock_query_raw(sql_query, *params): + if "COUNT(*)" in sql_query: + return [{"total_count": len(base_logs)}] assert "model" in sql_query # model is non-nullable in the schema, so NULLS LAST should NOT be # appended — only ttft_ms gets that clause. This guards against @@ -937,10 +1021,7 @@ async def test_ui_view_spend_logs_sort_by_model( ) page_size = params[-2] if len(params) >= 2 else 50 skip = params[-1] if len(params) >= 1 else 0 - return [ - {**row, "total_count": len(base_logs)} - for row in sorted_logs[skip : skip + page_size] - ] + return [row for row in sorted_logs[skip : skip + page_size]] class MockPrismaClient: def __init__(self): @@ -1040,6 +1121,8 @@ async def test_ui_view_spend_logs_sort_by_ttft_ms(client, monkeypatch): return len(base_logs) async def mock_query_raw(sql_query, *params): + if "COUNT(*)" in sql_query: + return [{"total_count": len(base_logs)}] # Endpoint must compute TTFT inline and use NULLS LAST. assert "completionStartTime" in sql_query assert "NULLS LAST" in sql_query @@ -1051,7 +1134,7 @@ async def test_ui_view_spend_logs_sort_by_ttft_ms(client, monkeypatch): page_size = params[-2] if len(params) >= 2 else 50 skip = params[-1] if len(params) >= 1 else 0 return [ - {**{k: v for k, v in row.items() if k != "_ttft_ms"}, "total_count": len(base_logs)} + {k: v for k, v in row.items() if k != "_ttft_ms"} for row in sorted_logs[skip : skip + page_size] ] @@ -2917,10 +3000,11 @@ async def test_build_ui_spend_logs_response_dict_rows_session_counts(): ) session_id = "sess-abc-123" + api_key = "hashed-key-xyz" dict_rows = [ - {"request_id": "req-1", "session_id": session_id, "call_type": "completion"}, - {"request_id": "req-2", "session_id": session_id, "call_type": "mcp_tool_call"}, - {"request_id": "req-3", "session_id": None, "call_type": "completion"}, + {"request_id": "req-1", "session_id": session_id, "call_type": "completion", "api_key": api_key}, + {"request_id": "req-2", "session_id": session_id, "call_type": "mcp_tool_call", "api_key": api_key}, + {"request_id": "req-3", "session_id": None, "call_type": "completion", "api_key": api_key}, ] mock_prisma = MagicMock() @@ -2929,6 +3013,16 @@ async def test_build_ui_spend_logs_response_dict_rows_session_counts(): {"session_id": session_id, "_count": {"session_id": 2}}, ] ) + mock_prisma.db.query_raw = AsyncMock( + return_value=[ + { + "session_id": session_id, + "session_total_spend": 15.0, + "mcp_tool_call_count": 1, + "mcp_tool_call_spend": 10.0, + } + ] + ) result = await _build_ui_spend_logs_response( prisma_client=mock_prisma, @@ -2946,6 +3040,14 @@ async def test_build_ui_spend_logs_response_dict_rows_session_counts(): # Rows with the shared session_id should have session_total_count=2 assert rows[0]["session_total_count"] == 2 assert rows[1]["session_total_count"] == 2 + assert rows[0]["mcp_tool_call_count"] == 1 + assert rows[0]["mcp_tool_call_spend"] == 10.0 + assert rows[1]["mcp_tool_call_count"] == 1 + assert rows[1]["mcp_tool_call_spend"] == 10.0 + + # Every row in the session carries the full session spend, not just its own + assert rows[0]["session_total_spend"] == 15.0 + assert rows[1]["session_total_spend"] == 15.0 # Row without a session_id defaults to 1 assert rows[2]["session_total_count"] == 1 @@ -2958,6 +3060,64 @@ async def test_build_ui_spend_logs_response_dict_rows_session_counts(): ) +@pytest.mark.asyncio +async def test_build_ui_spend_logs_response_sums_multi_round_session_spend(): + """ + Regression test for LIT-4342: for a multi-round session the UI must show the + summed cost of every round, not just the first call. _build_ui_spend_logs_response + enriches each row of a session with session_total_spend aggregated across the + whole session, scoped to the authorized api_keys of the page. + """ + from litellm.proxy.spend_tracking.spend_management_endpoints import ( + _build_ui_spend_logs_response, + ) + + session_id = "sess-multi-round" + api_key = "hashed-key-xyz" + # Three rounds of the same chat session with different per-call spend. + dict_rows = [ + {"request_id": "req-1", "session_id": session_id, "call_type": "completion", "api_key": api_key, "spend": 0.01}, + {"request_id": "req-2", "session_id": session_id, "call_type": "completion", "api_key": api_key, "spend": 0.02}, + {"request_id": "req-3", "session_id": session_id, "call_type": "completion", "api_key": api_key, "spend": 0.03}, + ] + + mock_prisma = MagicMock() + mock_prisma.db.litellm_spendlogs.group_by = AsyncMock( + return_value=[{"session_id": session_id, "_count": {"session_id": 3}}] + ) + # The raw aggregate query returns the full session spend (0.01 + 0.02 + 0.03). + mock_prisma.db.query_raw = AsyncMock( + return_value=[ + { + "session_id": session_id, + "session_total_spend": 0.06, + "mcp_tool_call_count": 0, + "mcp_tool_call_spend": 0.0, + } + ] + ) + + result = await _build_ui_spend_logs_response( + prisma_client=mock_prisma, + data=dict_rows, + total_records=3, + page=1, + page_size=50, + total_pages=1, + enrich_session_counts=True, + ) + + rows = result["data"] + assert [row["session_total_spend"] for row in rows] == [0.06, 0.06, 0.06] + # No MCP calls in this session, so MCP fields must not be attached. + assert all("mcp_tool_call_count" not in row for row in rows) + + # The aggregate must be scoped to the authorized api_keys of the page. + _, call_args, _ = mock_prisma.db.query_raw.mock_calls[0] + assert call_args[1] == [session_id] + assert call_args[2] == [api_key] + + # --------------------------------------------------------------------------- # Tests for /spend/logs team-member permission # --------------------------------------------------------------------------- @@ -4104,7 +4264,9 @@ async def test_cold_storage_handler_returns_none_when_no_logger_configured(monke @pytest.mark.asyncio -async def test_cold_storage_handler_resolves_configured_logger_from_registry(monkeypatch): +async def test_cold_storage_handler_resolves_configured_logger_from_registry( + monkeypatch, +): from litellm.proxy.spend_tracking.cold_storage_handler import ColdStorageHandler logger = _FakeColdStorageLogger({"messages": "from-registry"}) diff --git a/tests/test_litellm/proxy/spend_tracking/test_spend_query_optimization.py b/tests/test_litellm/proxy/spend_tracking/test_spend_query_optimization.py index 5b793adbbb4..19083486974 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_spend_query_optimization.py +++ b/tests/test_litellm/proxy/spend_tracking/test_spend_query_optimization.py @@ -16,6 +16,7 @@ import pytest sys.path.insert(0, os.path.abspath("../../../..")) from litellm.proxy.spend_tracking.spend_tracking_utils import ( + get_spend_by_team, get_spend_by_team_and_customer, ) @@ -60,31 +61,17 @@ async def test_spend_query_uses_timestamp_filtering(): params = call_args[1:] # 1) SQL should NOT cast the startTime column to DATE (prevents index usage) - assert ( - "::date" not in sql.lower() - ), "SQL should not use '::date' casting which prevents index usage" - assert ( - "date(" not in sql.lower() - ), "SQL should not use DATE() function which prevents index usage" + assert "::date" not in sql.lower(), "SQL should not use '::date' casting which prevents index usage" + assert "date(" not in sql.lower(), "SQL should not use DATE() function which prevents index usage" # 2) SQL should use timestamp-range filtering pattern for index optimization - assert ( - '"startTime" >=' in sql or '"startTime">=' in sql - ), "SQL should use >= operator for lower bound" - assert ( - '"startTime" <' in sql or '"startTime"<' in sql - ), "SQL should use < operator for upper bound" - assert ( - "interval '1 day'" in sql.lower() - ), "SQL should use INTERVAL for date arithmetic" + assert '"startTime" >=' in sql or '"startTime">=' in sql, "SQL should use >= operator for lower bound" + assert '"startTime" <' in sql or '"startTime"<' in sql, "SQL should use < operator for upper bound" + assert "interval '1 day'" in sql.lower(), "SQL should use INTERVAL for date arithmetic" # 3) Parameters should be datetime objects (not date objects) - assert isinstance( - params[0], datetime.datetime - ), "First parameter (start_date) should be datetime object" - assert isinstance( - params[1], datetime.datetime - ), "Second parameter (end_date) should be datetime object" + assert isinstance(params[0], datetime.datetime), "First parameter (start_date) should be datetime object" + assert isinstance(params[1], datetime.datetime), "Second parameter (end_date) should be datetime object" assert params[0].tzinfo is not None, "start_date should be timezone-aware" assert params[1].tzinfo is not None, "end_date should be timezone-aware" @@ -130,12 +117,8 @@ async def test_global_activity_wraps_params_in_at_time_zone_utc(monkeypatch): # 2) Params must still be tz-aware UTC datetimes (preserves existing contract). assert isinstance(params[0], datetime.datetime) assert isinstance(params[1], datetime.datetime) - assert params[0].tzinfo is not None and params[0].utcoffset() == datetime.timedelta( - 0 - ) - assert params[1].tzinfo is not None and params[1].utcoffset() == datetime.timedelta( - 0 - ) + assert params[0].tzinfo is not None and params[0].utcoffset() == datetime.timedelta(0) + assert params[1].tzinfo is not None and params[1].utcoffset() == datetime.timedelta(0) @pytest.mark.asyncio @@ -158,9 +141,7 @@ async def test_global_activity_internal_user_wraps_params_in_at_time_zone_utc( monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma) - auth = UserAPIKeyAuth( - user_role=LitellmUserRoles.INTERNAL_USER, user_id="internal_user_1" - ) + auth = UserAPIKeyAuth(user_role=LitellmUserRoles.INTERNAL_USER, user_id="internal_user_1") await get_global_activity( start_date="2026-02-16", @@ -171,8 +152,7 @@ async def test_global_activity_internal_user_wraps_params_in_at_time_zone_utc( assert mock_prisma.db.query_raw.called sql = mock_prisma.db.query_raw.call_args[0][0] assert sql.count("AT TIME ZONE 'UTC'") >= 2, ( - "Internal-user branch must also wrap date bounds with " - f"`AT TIME ZONE 'UTC'`. SQL was:\n{sql}" + f"Internal-user branch must also wrap date bounds with `AT TIME ZONE 'UTC'`. SQL was:\n{sql}" ) @@ -219,36 +199,46 @@ async def test_spend_logs_ui_wraps_params_in_at_time_zone_utc(monkeypatch): assert mock_prisma.db.query_raw.called, "query_raw should have been called" sql = mock_prisma.db.query_raw.call_args[0][0] assert sql.count("AT TIME ZONE 'UTC'") >= 2, ( - "/spend/logs/ui must wrap both `startTime` bounds with " - f"`AT TIME ZONE 'UTC'`. SQL was:\n{sql}" + f"/spend/logs/ui must wrap both `startTime` bounds with `AT TIME ZONE 'UTC'`. SQL was:\n{sql}" ) +def _make_ui_spend_logs_mock(count_total, page_rows): + """ + Build a prisma mock whose first `query_raw` (the bounded count) returns + `count_total` and whose second `query_raw` (the page data) returns + `page_rows`. + """ + mock_prisma = MagicMock() + mock_prisma.db = MagicMock() + mock_prisma.db.query_raw = AsyncMock( + side_effect=[[{"total_count": count_total}], page_rows] + ) + mock_prisma.db.litellm_spendlogs = MagicMock() + mock_prisma.db.litellm_spendlogs.count = AsyncMock(return_value=0) + return mock_prisma + + @pytest.mark.asyncio -async def test_spend_logs_ui_folds_count_into_window_function(monkeypatch): +async def test_spend_logs_ui_uses_bounded_count_not_full_scan(monkeypatch): """ - /spend/logs/ui must not issue a separate `COUNT(*)` round trip to compute - the total. On sharded engines like YugabyteDB a standalone `COUNT(*)` is a - distributed RPC that contacts every tablet and times out regardless of row - count, so the logs tab 500s (LIT-4027). The total is folded into the page - query via `COUNT(*) OVER ()` and read off the returned rows instead. + /spend/logs/ui must compute its pagination total with a bounded + `SELECT COUNT(*) FROM (SELECT 1 ... LIMIT $cap+1)` so it never scans the + whole time window of a huge LiteLLM_SpendLogs table (Aurora ACU spike, + LIT-4119). It must also avoid the unbounded prisma `.count()` / + `COUNT(*) OVER ()` full-window count that reads every matching row. """ from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth from litellm.proxy.spend_tracking.spend_management_endpoints import ( + SPEND_LOGS_PAGINATION_COUNT_CAP, ui_view_spend_logs, ) - rows = [ - {"request_id": "req-1", "metadata": "{}", "session_id": None, "total_count": 137}, - {"request_id": "req-2", "metadata": "{}", "session_id": None, "total_count": 137}, + page_rows = [ + {"request_id": "req-1", "metadata": "{}", "session_id": None}, + {"request_id": "req-2", "metadata": "{}", "session_id": None}, ] - - mock_prisma = MagicMock() - mock_prisma.db = MagicMock() - mock_prisma.db.query_raw = AsyncMock(return_value=rows) - mock_prisma.db.litellm_spendlogs = MagicMock() - mock_prisma.db.litellm_spendlogs.count = AsyncMock(return_value=0) - + mock_prisma = _make_ui_spend_logs_mock(count_total=137, page_rows=page_rows) monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma) auth = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin") @@ -271,36 +261,90 @@ async def test_spend_logs_ui_folds_count_into_window_function(monkeypatch): mock_prisma.db.litellm_spendlogs.count.assert_not_called() - sql = mock_prisma.db.query_raw.call_args[0][0] - assert "COUNT(*) OVER ()" in sql, ( - "the page query must carry a window-function count so a separate " - f"distributed COUNT(*) is avoided. SQL was:\n{sql}" + count_call = mock_prisma.db.query_raw.call_args_list[0] + count_sql = count_call[0][0] + assert "COUNT(*) OVER ()" not in count_sql + assert "LIMIT" in count_sql and "FROM (" in count_sql, ( + "the total must come from a bounded subquery count, not a full-window " + f"scan. SQL was:\n{count_sql}" + ) + assert count_call[0][-1] == SPEND_LOGS_PAGINATION_COUNT_CAP + 1, ( + "the bounded count must probe at most cap+1 rows" + ) + + page_sql = mock_prisma.db.query_raw.call_args_list[1][0][0] + assert "COUNT(*) OVER ()" not in page_sql, ( + "the page query must not carry a window count that forces a full-window " + f"scan. SQL was:\n{page_sql}" ) assert response["total"] == 137 + assert response["total_is_capped"] is False assert response["total_pages"] == (137 + 50 - 1) // 50 for row in response["data"]: - assert "total_count" not in row, ( - "the window-function helper column must be stripped before " - "serialising rows" - ) + assert "total_count" not in row, "the window-function helper column must be stripped before serialising rows" + + +@pytest.mark.asyncio +async def test_spend_logs_ui_caps_total_for_large_result_sets(monkeypatch): + """ + When more than the cap match, /spend/logs/ui reports the cap and flags + `total_is_capped` so the UI can render `+` instead of an exact total + that would require scanning the whole window (LIT-4119). + """ + from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth + from litellm.proxy.spend_tracking.spend_management_endpoints import ( + SPEND_LOGS_PAGINATION_COUNT_CAP, + ui_view_spend_logs, + ) + + page_rows = [{"request_id": "req-1", "metadata": "{}", "session_id": None}] + mock_prisma = _make_ui_spend_logs_mock( + count_total=SPEND_LOGS_PAGINATION_COUNT_CAP + 1, page_rows=page_rows + ) + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma) + + auth = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin") + mock_request = MagicMock() + mock_request.url.path = "/spend/logs/ui" + + response = await ui_view_spend_logs( + request=mock_request, + api_key=None, + user_id=None, + request_id=None, + start_date="2026-02-16 00:00:00", + end_date="2026-02-16 23:59:59", + page=1, + page_size=50, + sort_by="startTime", + sort_order="desc", + user_api_key_dict=auth, + ) + + assert response["total"] == SPEND_LOGS_PAGINATION_COUNT_CAP + assert response["total_is_capped"] is True + assert response["total_pages"] == (SPEND_LOGS_PAGINATION_COUNT_CAP + 50 - 1) // 50 @pytest.mark.asyncio async def test_spend_logs_ui_empty_page_reports_zero_total(monkeypatch): """ - When a page matches no rows the window-function count row is absent, so the - total must fall back to zero without issuing a separate `COUNT(*)`. + When nothing matches, the bounded count query returns a single row with a + zero count (real `COUNT(*)` always returns one row) and the page query + returns no rows, so the total is zero without an unbounded prisma `.count()`. """ from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth from litellm.proxy.spend_tracking.spend_management_endpoints import ( ui_view_spend_logs, ) + # First query_raw call is the bounded count (0 matches), second is the empty + # page. mock_prisma = MagicMock() mock_prisma.db = MagicMock() - mock_prisma.db.query_raw = AsyncMock(return_value=[]) + mock_prisma.db.query_raw = AsyncMock(side_effect=[[{"total_count": 0}], []]) mock_prisma.db.litellm_spendlogs = MagicMock() mock_prisma.db.litellm_spendlogs.count = AsyncMock(return_value=0) @@ -331,24 +375,25 @@ async def test_spend_logs_ui_empty_page_reports_zero_total(monkeypatch): @pytest.mark.asyncio -async def test_spend_logs_ui_out_of_range_page_falls_back_to_count(monkeypatch): +async def test_spend_logs_ui_out_of_range_page_keeps_total(monkeypatch): """ - An out-of-range page (offset past the last matching row) returns no rows, so - the window-function count is unavailable. The total must not collapse to zero - there; it falls back to a direct count so total/total_pages stay accurate. - This fallback only fires off the hot path (page > 1 with an empty result), so - the YugabyteDB timeout the fix removes from page 1 stays removed. + An out-of-range page (offset past the last matching row) returns no rows, + but the bounded count query runs independently of the page query, so the + total must not collapse to zero and no unbounded prisma `.count()` is + needed. total/total_pages stay accurate off the hot path too. """ from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth from litellm.proxy.spend_tracking.spend_management_endpoints import ( ui_view_spend_logs, ) + # First query_raw call is the bounded count (7 matches), second is the + # out-of-range page (empty). mock_prisma = MagicMock() mock_prisma.db = MagicMock() - mock_prisma.db.query_raw = AsyncMock(return_value=[]) + mock_prisma.db.query_raw = AsyncMock(side_effect=[[{"total_count": 7}], []]) mock_prisma.db.litellm_spendlogs = MagicMock() - mock_prisma.db.litellm_spendlogs.count = AsyncMock(return_value=7) + mock_prisma.db.litellm_spendlogs.count = AsyncMock(return_value=0) monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma) @@ -370,6 +415,90 @@ async def test_spend_logs_ui_out_of_range_page_falls_back_to_count(monkeypatch): user_api_key_dict=auth, ) - mock_prisma.db.litellm_spendlogs.count.assert_called_once() + mock_prisma.db.litellm_spendlogs.count.assert_not_called() assert response["total"] == 7 assert response["total_pages"] == (7 + 2 - 1) // 2 + assert response["data"] == [] + + +@pytest.mark.asyncio +async def test_get_spend_by_team_binds_optional_team_filter(): + """ + get_spend_by_team must bind team_id as query parameter $3 behind an + `IS NULL OR` predicate: a provided team_id narrows the result to that team, + a None team_id short-circuits the filter and returns every team. Regression + guard for LIT-4125 (the team query previously carried no team_id predicate). + """ + mock_prisma = MagicMock() + mock_prisma.db = MagicMock() + mock_query_raw = AsyncMock(return_value=[]) + mock_prisma.db.query_raw = mock_query_raw + + start_date = datetime.datetime(2024, 1, 1, tzinfo=timezone.utc) + end_date = datetime.datetime(2024, 1, 31, tzinfo=timezone.utc) + + await get_spend_by_team( + start_date=start_date, + end_date=end_date, + team_id="test_team", + prisma_client=mock_prisma, + ) + + assert mock_query_raw.called, "query_raw should have been called" + sql = mock_query_raw.call_args[0][0] + params = mock_query_raw.call_args[0][1:] + + # team_id is bound as parameter $3 (not string-interpolated) and referenced in WHERE + assert "sl.team_id = $3" in sql, f"WHERE must filter on team_id. SQL was:\n{sql}" + assert "$3::text IS NULL OR" in sql, ( + f"the team filter must be optional via an IS NULL short-circuit. SQL was:\n{sql}" + ) + assert params[2] == "test_team", "team_id must be forwarded as the third query param" + + # None team_id still forwards param $3 (as None) so the predicate no-ops + mock_query_raw.reset_mock() + await get_spend_by_team( + start_date=start_date, + end_date=end_date, + team_id=None, + prisma_client=mock_prisma, + ) + assert mock_query_raw.call_args[0][1:][2] is None + + +@pytest.mark.asyncio +async def test_global_spend_report_team_group_forwards_team_id(monkeypatch): + """ + GET /global/spend/report?group_by=team&team_id=X must filter to team X. + + Before LIT-4125 the group_by=team branch ran a query with no team_id + predicate, so spend for every team in the range was returned regardless of + team_id (team_id was only honored when a customer_id was also supplied). + This asserts the endpoint forwards team_id into the DB query. + """ + from litellm.proxy.spend_tracking.spend_management_endpoints import ( + get_global_spend_report, + ) + + mock_prisma = MagicMock() + mock_prisma.db = MagicMock() + mock_prisma.db.query_raw = AsyncMock(return_value=[]) + + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma) + monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True) + + await get_global_spend_report( + start_date="2026-07-01", + end_date="2026-07-03", + group_by="team", + api_key=None, + internal_user_id=None, + team_id="team_x", + customer_id=None, + ) + + assert mock_prisma.db.query_raw.called, "query_raw should have been called" + sql = mock_prisma.db.query_raw.call_args[0][0] + params = mock_prisma.db.query_raw.call_args[0][1:] + assert "team_x" in params, "team_id must be forwarded into the DB query params" + assert "sl.team_id = $3" in sql, f"team query must filter on team_id. SQL was:\n{sql}" diff --git a/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py b/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py index 874e0654a1f..9a8f8146d6f 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py +++ b/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py @@ -29,9 +29,11 @@ from litellm.proxy.spend_tracking.spend_tracking_utils import ( _get_response_for_spend_logs_payload, _get_spend_logs_metadata, _get_vector_store_request_for_spend_logs_payload, + _hash_api_key_for_spend_log, _is_master_key, _redact_prompt_leaks_in_error_string, _sanitize_error_information_for_spend_logs, + _sanitize_guardrail_information_for_spend_logs, _sanitize_request_body_for_spend_logs_payload, _should_store_prompts_and_responses_in_spend_logs, get_logging_payload, @@ -1262,6 +1264,260 @@ def test_get_spend_logs_metadata_guardrail_info_fallback_from_metadata(): assert result["guardrail_information"] is None +@patch("litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs") +def test_sanitize_guardrail_information_redacts_all_prompt_carrying_fields_when_flag_false( + mock_should_store, +): + """ + match_details and classification are declared as structured metadata but + in-tree writers (litellm_content_filter, block_code_execution) inline + raw prompt content into them, so they leak the same way + guardrail_request/guardrail_response do. Redaction must cover all four. + """ + mock_should_store.return_value = False + guardrail_info = [ + { + "guardrail_name": "demo-echo-guard", + "guardrail_status": "success", + "guardrail_request": {"messages": [{"role": "user", "content": "hi"}]}, + "guardrail_response": {"evaluated_input": "hi"}, + "match_details": [{"type": "pattern", "snippet": "hi", "action_taken": "log"}], + "classification": {"intent": "x", "evidence": [{"match": "hi"}]}, + "guardrail_action": "NONE", + } + ] + + result = _sanitize_guardrail_information_for_spend_logs(guardrail_info) + + assert result is not None + entry = result[0] + assert entry["guardrail_request"] == REDACTED_BY_LITELM_STRING + assert entry["guardrail_response"] == REDACTED_BY_LITELM_STRING + assert entry["match_details"] == REDACTED_BY_LITELM_STRING + assert entry["classification"] == REDACTED_BY_LITELM_STRING + assert entry["guardrail_name"] == "demo-echo-guard" + assert entry["guardrail_status"] == "success" + assert entry["guardrail_action"] == "NONE" + + +@patch("litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs") +def test_sanitize_guardrail_information_redacts_prompt_fields_when_flag_false( + mock_should_store, +): + """ + LIT-4314 Issue A regression: with store_prompts_in_spend_logs=False, + guardrail_request and guardrail_response must be redacted before they + land in LiteLLM_SpendLogs.metadata, while every other field on the + entry is preserved bit-for-bit. + """ + mock_should_store.return_value = False + guardrail_info = [ + { + "guardrail_name": "demo-echo-guard", + "guardrail_provider": "custom", + "guardrail_mode": "pre_call", + "guardrail_status": "success", + "guardrail_request": { + "messages": [{"role": "user", "content": "Say hi in 3 words"}], + }, + "guardrail_response": { + "evaluated_input": "Say hi in 3 words", + "verdict": "allow", + }, + "start_time": 1_700_000_000.0, + "end_time": 1_700_000_000.5, + "duration": 0.5, + "guardrail_id": "gd-42", + "masked_entity_count": {"EMAIL": 1}, + "violation_categories": ["prompt_injection"], + "risk_score": 3.5, + "guardrail_action": "NONE", + } + ] + + result = _sanitize_guardrail_information_for_spend_logs(guardrail_info) + + assert result is not None + assert len(result) == 1 + entry = result[0] + assert entry["guardrail_request"] == REDACTED_BY_LITELM_STRING + assert entry["guardrail_response"] == REDACTED_BY_LITELM_STRING + assert entry["guardrail_name"] == "demo-echo-guard" + assert entry["guardrail_provider"] == "custom" + assert entry["guardrail_mode"] == "pre_call" + assert entry["guardrail_status"] == "success" + assert entry["start_time"] == 1_700_000_000.0 + assert entry["end_time"] == 1_700_000_000.5 + assert entry["duration"] == 0.5 + assert entry["guardrail_id"] == "gd-42" + assert entry["masked_entity_count"] == {"EMAIL": 1} + assert entry["violation_categories"] == ["prompt_injection"] + assert entry["risk_score"] == 3.5 + assert entry["guardrail_action"] == "NONE" + + assert guardrail_info[0]["guardrail_request"] == { + "messages": [{"role": "user", "content": "Say hi in 3 words"}], + } + assert guardrail_info[0]["guardrail_response"] == { + "evaluated_input": "Say hi in 3 words", + "verdict": "allow", + } + + +@patch("litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs") +def test_sanitize_guardrail_information_passthrough_when_flag_true( + mock_should_store, +): + """ + When store_prompts_in_spend_logs=True the sanitizer must be a no-op so + operators who explicitly opted in still see full guardrail payloads. + """ + mock_should_store.return_value = True + guardrail_info = [ + { + "guardrail_name": "content_filter", + "guardrail_status": "success", + "guardrail_request": {"messages": [{"role": "user", "content": "hi"}]}, + "guardrail_response": {"verdict": "allow"}, + } + ] + + result = _sanitize_guardrail_information_for_spend_logs(guardrail_info) + + assert result == guardrail_info + + +@patch("litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs") +def test_sanitize_guardrail_information_none_passthrough(mock_should_store): + mock_should_store.return_value = False + assert _sanitize_guardrail_information_for_spend_logs(None) is None + + +@patch("litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs") +def test_sanitize_guardrail_information_normalizes_bare_dict_input(mock_should_store): + """ + Regression: xecguard (xecguard.py:246) assigns a bare dict to + standard_logging_object["guardrail_information"] even though the typed + contract is Optional[List[...]]. Without defensive normalization here, + the for-loop would iterate the dict's string keys and _redact... + would TypeError on {**"guardrail_name"}, taking down the entire + spend-log write via update_database's broad except. + """ + mock_should_store.return_value = False + bare_dict_entry = { + "guardrail_name": "xecguard", + "guardrail_status": "success", + "guardrail_response": {"decision": "SAFE", "raw_prompt": "hi"}, + "start_time": 1.0, + "end_time": 2.0, + "duration": 1.0, + } + + result = _sanitize_guardrail_information_for_spend_logs(bare_dict_entry) + + assert result is not None + assert isinstance(result, list) + assert len(result) == 1 + entry = result[0] + assert entry["guardrail_response"] == REDACTED_BY_LITELM_STRING + assert entry["guardrail_name"] == "xecguard" + assert entry["guardrail_status"] == "success" + assert entry["start_time"] == 1.0 + + +@patch("litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs") +def test_sanitize_guardrail_information_drops_non_dict_items_in_list(mock_should_store): + """ + A stray non-dict item in the list (e.g. from a buggy caller that + accidentally appends a string) should be silently skipped instead of + crashing the spend-log write. + """ + mock_should_store.return_value = False + mixed_input = [ + {"guardrail_name": "x", "guardrail_response": {"leak": "hi"}}, + "not-a-dict", + None, + ] + + result = _sanitize_guardrail_information_for_spend_logs(mixed_input) + + assert result == [{"guardrail_name": "x", "guardrail_response": REDACTED_BY_LITELM_STRING}] + + +@patch("litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs") +def test_sanitize_guardrail_information_preserves_absent_prompt_fields(mock_should_store): + """ + Entries that never carried guardrail_request or guardrail_response must + not gain those keys after sanitization; consumers keying on presence + (`"guardrail_request" in entry`) would otherwise flip from absent to + the sentinel string. + """ + mock_should_store.return_value = False + guardrail_info = [ + { + "guardrail_name": "demo-echo-guard", + "guardrail_status": "success", + "guardrail_response": {"verdict": "allow", "evaluated_input": "hi"}, + } + ] + + result = _sanitize_guardrail_information_for_spend_logs(guardrail_info) + + assert result is not None + entry = result[0] + assert "guardrail_request" not in entry + assert entry["guardrail_response"] == REDACTED_BY_LITELM_STRING + assert entry["guardrail_name"] == "demo-echo-guard" + assert entry["guardrail_status"] == "success" + + +@patch("litellm.proxy.proxy_server.master_key", "sk-master") +@patch( + "litellm.proxy.proxy_server.general_settings", + {"store_prompts_in_spend_logs": False}, +) +def test_get_logging_payload_redacts_guardrail_prompt_fields_when_flag_false(): + """ + End-to-end wire-in check: get_logging_payload -> _get_spend_logs_metadata + -> sanitizer. Without the wire-in at line 139, the raw guardrail_response + lands in payload["metadata"] verbatim. + """ + guardrail_info = [ + { + "guardrail_name": "demo-echo-guard", + "guardrail_provider": "custom", + "guardrail_status": "success", + "guardrail_request": {"messages": [{"role": "user", "content": "secret"}]}, + "guardrail_response": {"evaluated_input": "secret"}, + } + ] + kwargs = { + "model": "gpt-4o-mini", + "litellm_call_id": "test-call-id", + "litellm_params": { + "metadata": { + "user_api_key": "test-key", + "standard_logging_guardrail_information": guardrail_info, + }, + "proxy_server_request": {}, + }, + } + + payload = get_logging_payload( + kwargs=kwargs, + response_obj={}, + start_time=datetime.datetime.now(tz=timezone.utc), + end_time=datetime.datetime.now(tz=timezone.utc), + ) + + metadata_result = json.loads(payload["metadata"]) + stored = metadata_result["guardrail_information"][0] + assert stored["guardrail_request"] == REDACTED_BY_LITELM_STRING + assert stored["guardrail_response"] == REDACTED_BY_LITELM_STRING + assert stored["guardrail_name"] == "demo-echo-guard" + assert stored["guardrail_status"] == "success" + + def test_get_logging_payload_guardrail_info_when_no_standard_logging_payload(): """ When a guardrail blocks a request before the LLM call, the standard_logging_object @@ -1294,7 +1550,10 @@ def test_get_logging_payload_guardrail_info_when_no_standard_logging_payload(): } with patch("litellm.proxy.proxy_server.master_key", "sk-master"): - with patch("litellm.proxy.proxy_server.general_settings", {}): + with patch( + "litellm.proxy.proxy_server.general_settings", + {"store_prompts_in_spend_logs": True}, + ): payload = get_logging_payload( kwargs=kwargs, response_obj={}, @@ -2229,3 +2488,86 @@ def test_get_logging_payload_cache_hit_keeps_raw_litellm_call_id(): assert json.loads(payload["metadata"])["litellm_call_id"] == trace_call_id assert "_cache_hit" in payload["request_id"] assert json.loads(payload["metadata"])["litellm_call_id"] != payload["request_id"] + + +class TestHashApiKeyForSpendLog: + """Regression: plaintext API keys with Bearer prefix were stored in + SpendLogs for failed requests (LIT-4121)""" + + def test_bearer_prefixed_sk_key_is_hashed(self): + raw = "Bearer sk-WLi4iRn4JmbVlTaYw12IOA" + result = _hash_api_key_for_spend_log(raw) + assert not result.startswith("Bearer") + assert not result.startswith("sk-") + assert len(result) == 64 + + def test_bare_sk_key_is_hashed(self): + raw = "sk-WLi4iRn4JmbVlTaYw12IOA" + result = _hash_api_key_for_spend_log(raw) + assert not result.startswith("sk-") + assert len(result) == 64 + + def test_bearer_lowercase_is_handled(self): + raw = "bearer sk-WLi4iRn4JmbVlTaYw12IOA" + result = _hash_api_key_for_spend_log(raw) + assert not result.startswith("bearer") + assert not result.startswith("sk-") + assert len(result) == 64 + + def test_already_hashed_key_unchanged(self): + hashed = "bcfe8173f5447f10be0e7fb37aaa8b97829d5c9e0498232152f9d123456789ab" + assert _hash_api_key_for_spend_log(hashed) == hashed + + def test_bearer_prefixed_non_sk_key_strips_prefix(self): + raw = "Bearer some-other-token-format" + result = _hash_api_key_for_spend_log(raw) + assert result == "some-other-token-format" + assert not result.startswith("Bearer") + + def test_bearer_and_bare_produce_same_hash(self): + bare = "sk-WLi4iRn4JmbVlTaYw12IOA" + bearer = "Bearer sk-WLi4iRn4JmbVlTaYw12IOA" + assert _hash_api_key_for_spend_log(bare) == _hash_api_key_for_spend_log(bearer) + + +@patch("litellm.proxy.proxy_server.master_key", None) +@patch("litellm.proxy.proxy_server.general_settings", {}) +def test_get_logging_payload_hashes_bearer_prefixed_api_key(): + """Regression for LIT-4121: failed-request spend logs stored plaintext + 'Bearer sk-...' in both the api_key column and metadata.user_api_key""" + raw_key = "Bearer sk-WLi4iRn4JmbVlTaYw12IOA" + + kwargs = { + "model": "openai/gpt-4.1", + "call_type": "acompletion", + "litellm_params": { + "metadata": { + "user_api_key": raw_key, + "user_api_key_user_id": "test_user", + "user_api_key_team_id": "test_team", + "status": "failure", + } + }, + } + + payload = get_logging_payload( + kwargs=kwargs, + response_obj=Exception("model error"), + start_time=datetime.datetime.now(timezone.utc), + end_time=datetime.datetime.now(timezone.utc), + ) + + assert not payload["api_key"].startswith("Bearer"), ( + f"api_key column contains plaintext Bearer key: {payload['api_key']}" + ) + assert not payload["api_key"].startswith("sk-"), ( + f"api_key column contains unhashed key: {payload['api_key']}" + ) + + metadata_dict = json.loads(payload["metadata"]) + assert not metadata_dict["user_api_key"].startswith("Bearer"), ( + f"metadata user_api_key contains plaintext Bearer key: {metadata_dict['user_api_key']}" + ) + assert not metadata_dict["user_api_key"].startswith("sk-"), ( + f"metadata user_api_key contains unhashed key: {metadata_dict['user_api_key']}" + ) diff --git a/tests/test_litellm/proxy/test_blocked_response_usage.py b/tests/test_litellm/proxy/test_blocked_response_usage.py new file mode 100644 index 00000000000..d486431ca3e --- /dev/null +++ b/tests/test_litellm/proxy/test_blocked_response_usage.py @@ -0,0 +1,84 @@ +""" +Token usage on synthetic guardrail-blocked responses for the OpenAI-format +proxy endpoints (/v1/chat/completions and /v1/completions). + +A post-call block replaces the LLM response with the violation message, but the +upstream call already consumed tokens. `_blocked_response_usage` reports that +real usage (carried on `ModifyResponseException.original_response`) rather than +zero; a pre-call block never invoked the LLM, so usage is zero. +""" + +import pytest + +import litellm +from litellm.proxy.proxy_server import _blocked_response_usage + + +def test_uses_original_response_usage(): + resp = litellm.ModelResponse() + resp.usage = litellm.Usage(prompt_tokens=42, completion_tokens=7, total_tokens=49) + + usage = _blocked_response_usage(resp) + + assert usage.prompt_tokens == 42 + assert usage.completion_tokens == 7 + assert usage.total_tokens == 49 + + +def test_zero_usage_when_no_original_response(): + usage = _blocked_response_usage(None) + + assert usage.prompt_tokens == 0 + assert usage.completion_tokens == 0 + assert usage.total_tokens == 0 + + +@pytest.mark.asyncio +async def test_success_hook_attaches_original_response_on_block(): + """The unified guardrail's post-call success hook must attach the blocked + LLM response to ModifyResponseException so its real usage isn't discarded.""" + from unittest.mock import AsyncMock, MagicMock, patch + + import litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail as ug + from litellm.integrations.custom_guardrail import ModifyResponseException + from litellm.proxy._types import UserAPIKeyAuth + from litellm.types.utils import CallTypes + + response = litellm.ModelResponse() + response.usage = litellm.Usage(prompt_tokens=15, completion_tokens=3, total_tokens=18) + + guardrail = MagicMock() + guardrail.should_run_guardrail.return_value = True + guardrail.guardrail_name = "rubrik" + + # The translation layer raises a block without pre-setting original_response. + translation = MagicMock() + translation.process_output_response = AsyncMock( + side_effect=ModifyResponseException( + message="blocked", + model="gpt-4o", + request_data={}, + guardrail_name="rubrik", + ) + ) + + unified = ug.UnifiedLLMGuardrails() + user_api_key_dict = UserAPIKeyAuth(api_key="test", request_route="/chat/completions") + data = {"guardrail_to_apply": guardrail, "model": "gpt-4o"} + + # Inject our translation for the inferred call type (the module global is + # cached across tests, so patch it directly rather than the loader). + with patch.object( + ug, + "endpoint_guardrail_translation_mappings", + { + CallTypes.acompletion: lambda: translation, + CallTypes.completion: lambda: translation, + }, + ): + with pytest.raises(ModifyResponseException) as excinfo: + await unified.async_post_call_success_hook( + data=data, user_api_key_dict=user_api_key_dict, response=response + ) + + assert excinfo.value.original_response is response diff --git a/tests/test_litellm/proxy/test_budget_reservation.py b/tests/test_litellm/proxy/test_budget_reservation.py index d940f592a83..3da103683ba 100644 --- a/tests/test_litellm/proxy/test_budget_reservation.py +++ b/tests/test_litellm/proxy/test_budget_reservation.py @@ -26,6 +26,7 @@ from litellm.proxy.spend_tracking.budget_reservation import ( reserve_budget_for_request, ) from litellm.proxy.utils import ProxyLogging +from litellm.router import Router @pytest.fixture() @@ -58,6 +59,101 @@ def _request_body() -> dict: } +async def _reserve(valid_token, cost, key_cache, proxy_logging_obj): + with patch( + "litellm.proxy.spend_tracking.budget_reservation.estimate_request_max_cost", + return_value=cost, + ): + return await reserve_budget_for_request( + request_body=_request_body(), + route="/chat/completions", + llm_router=None, + valid_token=valid_token, + team_object=None, + user_object=None, + prisma_client=None, + user_api_key_cache=key_cache, + proxy_logging_obj=proxy_logging_obj, + ) + + +@pytest.mark.asyncio +async def test_reservation_still_protects_under_budget_throttled_key( + spend_counter_state, monkeypatch +): + """An opted-in key that is still under budget keeps its reservation counter, + so concurrent requests can't collectively overshoot max_budget.""" + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.1) + counter_cache, key_cache = spend_counter_state + proxy_logging_obj = ProxyLogging(user_api_key_cache=key_cache) + valid_token = UserAPIKeyAuth( + token="key-throttle-under", + spend=0.0, + max_budget=1.0, + metadata={"throttle_on_budget_exceeded": True}, + ) + + reservation = await _reserve(valid_token, 0.6, key_cache, proxy_logging_obj) + + assert reservation is not None + assert ( + counter_cache.in_memory_cache.get_cache(key="spend:key:key-throttle-under") + == 0.6 + ) + + +@pytest.mark.asyncio +async def test_reservation_does_not_block_over_budget_throttled_key( + spend_counter_state, monkeypatch +): + """Once an opted-in key is over budget the reservation path must not raise; + the rate limiter throttles it instead.""" + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.1) + counter_cache, key_cache = spend_counter_state + proxy_logging_obj = ProxyLogging(user_api_key_cache=key_cache) + valid_token = UserAPIKeyAuth( + token="key-throttle-over", + spend=0.0, + max_budget=1.0, + tpm_limit=1000, + rpm_limit=100, + metadata={"throttle_on_budget_exceeded": True}, + ) + + # first reservation lands under budget (counter -> 0.6) + await _reserve(valid_token, 0.6, key_cache, proxy_logging_obj) + + # any further request is over budget (0.6 + 0.6 > 1.0): the opted-in key is + # released and allowed through (None), not blocked, and its over-budget + # increment is released so the counter is not permanently inflated + result = await _reserve(valid_token, 0.6, key_cache, proxy_logging_obj) + assert result is None + assert ( + counter_cache.in_memory_cache.get_cache(key="spend:key:key-throttle-over") + == 0.6 + ) + + +@pytest.mark.asyncio +async def test_reservation_blocks_over_budget_non_throttled_key( + spend_counter_state, monkeypatch +): + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.1) + counter_cache, key_cache = spend_counter_state + proxy_logging_obj = ProxyLogging(user_api_key_cache=key_cache) + valid_token = UserAPIKeyAuth( + token="key-no-optin-over", + spend=0.0, + max_budget=1.0, + ) + + await _reserve(valid_token, 0.6, key_cache, proxy_logging_obj) + await _reserve(valid_token, 0.6, key_cache, proxy_logging_obj) # counter -> 1.0 + + with pytest.raises(litellm.BudgetExceededError): + await _reserve(valid_token, 0.6, key_cache, proxy_logging_obj) + + def test_should_not_serialize_budget_reservation_on_user_api_key_auth(): auth = UserAPIKeyAuth( token="key-budget-runtime-state", @@ -649,6 +745,250 @@ async def test_should_clamp_reservation_to_default_when_output_cap_missing( await release_budget_reservation(reservation) +@pytest.mark.asyncio +async def test_should_reserve_tiered_pricing_cost(spend_counter_state): + _, key_cache = spend_counter_state + proxy_logging_obj = ProxyLogging(user_api_key_cache=key_cache) + router = Router( + model_list=[ + { + "model_name": "dashscope/qwen3-max", + "litellm_params": { + "model": "dashscope/qwen3-max", + "api_key": "sk-fake", + }, + "model_info": { + "max_input_tokens": 258048, + "max_output_tokens": 65536, + "tiered_pricing": [ + { + "input_cost_per_token": 1.2e-06, + "output_cost_per_token": 6e-06, + "range": [0, 32000], + }, + { + "input_cost_per_token": 2.4e-06, + "output_cost_per_token": 1.2e-05, + "range": [32000, 128000], + }, + ], + }, + } + ] + ) + request_body = { + "model": "dashscope/qwen3-max", + "messages": [{"role": "user", "content": "hello"}], + "max_tokens": 10, + } + + estimated_cost = estimate_request_max_cost( + request_body=request_body, + route="/chat/completions", + llm_router=router, + ) + assert estimated_cost is not None + assert estimated_cost > 0 + + valid_token = UserAPIKeyAuth( + token="key-tiered-pricing", + spend=0.0, + max_budget=estimated_cost, + ) + reservation = await reserve_budget_for_request( + request_body=request_body, + route="/chat/completions", + llm_router=router, + valid_token=valid_token, + team_object=None, + user_object=None, + prisma_client=None, + user_api_key_cache=key_cache, + proxy_logging_obj=proxy_logging_obj, + ) + + assert reservation is not None + assert reservation["reserved_cost"] == pytest.approx(estimated_cost) + with pytest.raises(litellm.BudgetExceededError): + await reserve_budget_for_request( + request_body=request_body, + route="/chat/completions", + llm_router=router, + valid_token=valid_token, + team_object=None, + user_object=None, + prisma_client=None, + user_api_key_cache=key_cache, + proxy_logging_obj=proxy_logging_obj, + ) + await release_budget_reservation(reservation) + + +def test_tiered_reservation_is_all_or_nothing_with_output_tier_from_input_length(): + """Dashscope tiered pricing is all-or-nothing: the tier is chosen by the total + input tokens and every token (input and output) is billed at that tier's rate. + + A long-context request with a large output allowance must reserve the output at + the input-selected tier, not at the cheapest tier picked from the output volume. + The earlier graduated calculation under-reserved such requests, letting a caller + slip past a depleted budget.""" + tiered_pricing = [ + {"range": [0, 32000], "input_cost_per_token": 1e-06, "output_cost_per_token": 2e-06}, + {"range": [32000, 128000], "input_cost_per_token": 4e-06, "output_cost_per_token": 8e-06}, + ] + input_tokens = 100000 # falls entirely in the second tier + output_tokens = 1000 + + with ( + patch( + "litellm.proxy.spend_tracking.budget_reservation._get_model_cost_info", + return_value={"tiered_pricing": tiered_pricing, "max_output_tokens": 200000}, + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._estimate_input_tokens", + return_value=input_tokens, + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._estimate_output_tokens", + return_value=output_tokens, + ), + ): + estimated = estimate_request_max_cost( + request_body=_request_body(), + route="/chat/completions", + llm_router=None, + ) + + expected = (input_tokens * 4e-06) + (output_tokens * 8e-06) + assert estimated == pytest.approx(expected) + + # What the old graduated math (with the output tier taken from output volume) + # would have reserved. The all-or-nothing estimate must be strictly larger. + graduated_under_reserve = (32000 * 1e-06) + (68000 * 4e-06) + (output_tokens * 2e-06) + assert estimated > graduated_under_reserve + + +def test_tiered_reservation_uses_higher_reasoning_output_rate(): + """Some tiered models price reasoning output above standard output. The + reasoning-token share is unknown before the request runs, so reservation must + charge every output token at the higher of the two rates to avoid under-reserving + reasoning-heavy requests.""" + tiered_pricing = [ + { + "range": [0, 32000], + "input_cost_per_token": 1e-06, + "output_cost_per_token": 1.2e-06, + "output_cost_per_reasoning_token": 4e-06, + } + ] + input_tokens = 1000 + output_tokens = 500 + + with ( + patch( + "litellm.proxy.spend_tracking.budget_reservation._get_model_cost_info", + return_value={"tiered_pricing": tiered_pricing, "max_output_tokens": 200000}, + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._estimate_input_tokens", + return_value=input_tokens, + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._estimate_output_tokens", + return_value=output_tokens, + ), + ): + estimated = estimate_request_max_cost( + request_body=_request_body(), + route="/chat/completions", + llm_router=None, + ) + + expected = (input_tokens * 1e-06) + (output_tokens * 4e-06) + assert estimated == pytest.approx(expected) + + # Reserving output at the plain rate would under-reserve reasoning-heavy calls. + under_reserve = (input_tokens * 1e-06) + (output_tokens * 1.2e-06) + assert estimated > under_reserve + + +def test_flat_reservation_uses_higher_reasoning_output_rate(): + """The same reasoning under-reservation gap exists for flat-rate models that + declare output_cost_per_reasoning_token above output_cost_per_token.""" + input_tokens = 1000 + output_tokens = 500 + + with ( + patch( + "litellm.proxy.spend_tracking.budget_reservation._get_model_cost_info", + return_value={ + "input_cost_per_token": 1e-06, + "output_cost_per_token": 1.2e-06, + "output_cost_per_reasoning_token": 4e-06, + "max_output_tokens": 200000, + }, + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._estimate_input_tokens", + return_value=input_tokens, + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._estimate_output_tokens", + return_value=output_tokens, + ), + ): + estimated = estimate_request_max_cost( + request_body=_request_body(), + route="/chat/completions", + llm_router=None, + ) + + expected = (input_tokens * 1e-06) + (output_tokens * 4e-06) + assert estimated == pytest.approx(expected) + under_reserve = (input_tokens * 1e-06) + (output_tokens * 1.2e-06) + assert estimated > under_reserve + + +def test_reservation_uses_most_expensive_deployment_in_group(): + """When a model group mixes deployments with different tiered rates, reservation + must estimate against the most expensive one. Reserving the cheaper sibling would + let a caller repeatedly hit the alias and exceed the budget once routed to the + costlier deployment.""" + cheap = [{"range": [0, 32000], "input_cost_per_token": 1e-06, "output_cost_per_token": 2e-06}] + expensive = [{"range": [0, 32000], "input_cost_per_token": 5e-06, "output_cost_per_token": 1e-05}] + input_tokens = 1000 + output_tokens = 10 + + with ( + patch( + "litellm.proxy.spend_tracking.budget_reservation._get_model_cost_info", + return_value={"max_output_tokens": 200000}, + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._get_deployment_tiered_pricing_tables", + return_value=[cheap, expensive], + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._estimate_input_tokens", + return_value=input_tokens, + ), + patch( + "litellm.proxy.spend_tracking.budget_reservation._estimate_output_tokens", + return_value=output_tokens, + ), + ): + estimated = estimate_request_max_cost( + request_body=_request_body(), + route="/chat/completions", + llm_router=None, + ) + + expected_expensive = (input_tokens * 5e-06) + (output_tokens * 1e-05) + expected_cheap = (input_tokens * 1e-06) + (output_tokens * 2e-06) + assert expected_expensive > expected_cheap + assert estimated == pytest.approx(expected_expensive) + + @pytest.mark.asyncio async def test_should_clamp_reservation_to_model_ceiling_when_caller_overrequests( spend_counter_state, diff --git a/tests/test_litellm/proxy/test_common_request_processing.py b/tests/test_litellm/proxy/test_common_request_processing.py index 1d0dafed171..aa1911f80bc 100644 --- a/tests/test_litellm/proxy/test_common_request_processing.py +++ b/tests/test_litellm/proxy/test_common_request_processing.py @@ -3304,6 +3304,75 @@ class TestStreamingClientDisconnectLogging: assert recorded is False assert "client_disconnected" not in request_data["metadata"] + @pytest.mark.asyncio + async def test_record_streaming_client_disconnect_handles_none_metadata(self): + from litellm.proxy.common_request_processing import ( + _record_streaming_client_disconnect_if_needed, + ) + + mock_logging_obj = MagicMock() + mock_logging_obj.model_call_details = { + "litellm_params": {"metadata": None}, + "metadata": None, + } + mock_request = MagicMock(spec=Request) + mock_request.is_disconnected = AsyncMock(return_value=True) + request_data = { + "litellm_call_id": "test-call-id", + "litellm_logging_obj": mock_logging_obj, + "metadata": {}, + "litellm_params": {"metadata": {}}, + } + + recorded = await _record_streaming_client_disconnect_if_needed( + mock_request, request_data + ) + + assert recorded is True + assert request_data["metadata"]["client_disconnected"] is True + assert ( + mock_logging_obj.model_call_details["litellm_params"]["metadata"][ + "client_disconnected" + ] + is True + ) + assert ( + mock_logging_obj.model_call_details["metadata"]["client_disconnected"] + is True + ) + + @pytest.mark.asyncio + async def test_record_streaming_client_disconnect_handles_none_request_data_metadata(self): + from litellm.proxy.common_request_processing import ( + _record_streaming_client_disconnect_if_needed, + ) + + mock_request = MagicMock(spec=Request) + mock_request.is_disconnected = AsyncMock(return_value=True) + request_data = { + "litellm_call_id": "test-call-id", + "metadata": None, + "litellm_params": {"metadata": None}, + } + + recorded = await _record_streaming_client_disconnect_if_needed( + mock_request, request_data + ) + + assert recorded is True + assert request_data["metadata"]["client_disconnected"] is True + assert ( + request_data["litellm_params"]["metadata"]["client_disconnected"] is True + ) + + @pytest.mark.asyncio + async def test_apply_client_disconnect_metadata_none_returns_early(self): + from litellm.proxy.common_request_processing import ( + _apply_client_disconnect_metadata, + ) + + _apply_client_disconnect_metadata(None) + @pytest.mark.asyncio async def test_finalize_streaming_generator_cleanup_fires_deferred_logging( self, monkeypatch @@ -4090,7 +4159,7 @@ class TestResponseCostHeaderForTypedDictResponses: logging_obj._on_deferred_stream_complete = None return logging_obj - async def _drive_non_streaming(self, *, monkeypatch, response, logging_obj, route_type): + async def _drive_non_streaming(self, *, monkeypatch, response, logging_obj, route_type, return_result=False): import litellm.proxy.common_request_processing as crp from litellm.proxy._types import UserAPIKeyAuth as RealUserAPIKeyAuth @@ -4119,7 +4188,7 @@ class TestResponseCostHeaderForTypedDictResponses: "_has_post_call_guardrails", return_value=False, ): - await processing_obj.base_process_llm_request( + result = await processing_obj.base_process_llm_request( request=MagicMock(spec=Request, headers={}), fastapi_response=fastapi_response, user_api_key_dict=RealUserAPIKeyAuth(api_key="sk-test"), @@ -4131,6 +4200,8 @@ class TestResponseCostHeaderForTypedDictResponses: llm_router=None, skip_pre_call_logic=True, ) + if return_result: + return fastapi_response, result return fastapi_response @pytest.mark.asyncio @@ -4352,3 +4423,451 @@ class TestResponseCostHeaderForTypedDictResponses: assert "x-litellm-response-cost" not in fastapi_response.headers recompute.assert_not_called() + + @pytest.mark.asyncio + async def test_messages_typeddict_does_not_leak_hidden_params_into_response_body(self, monkeypatch): + """ + Router.set_response_headers now writes rate-limit headers onto dict-shaped + responses (e.g. Anthropic /v1/messages, whose AnthropicMessagesResponse is a + TypedDict) via response["_hidden_params"] = ... . Unlike a pydantic model's + private attribute, that key is indistinguishable from any other dict key and + would otherwise serialize verbatim into the client-facing JSON body, leaking + response_cost/model_id/api_base/fallback errors. base_process_llm_request + must strip it before returning the response to the endpoint layer. + """ + from litellm.types.utils import AnthropicMessagesResponse + + response = AnthropicMessagesResponse( + id="msg_1", + type="message", + role="assistant", + content=[{"type": "text", "text": "hi"}], + model="claude-haiku-4-5", + usage={"input_tokens": 10, "output_tokens": 5}, + ) + response["_hidden_params"] = { + "additional_headers": {"x-ratelimit-limit-input-tokens": "25"}, + "response_cost": 0.00123, + "model_id": "internal-deployment-id", + } + logging_obj = self._build_logging_obj( + model_call_details={"response_cost": 0.00123}, + response_cost_calculator=MagicMock(return_value=999.0), + ) + + fastapi_response, result = await self._drive_non_streaming( + monkeypatch=monkeypatch, + response=response, + logging_obj=logging_obj, + route_type="anthropic_messages", + return_result=True, + ) + + assert "_hidden_params" not in result + assert fastapi_response.headers["x-ratelimit-limit-input-tokens"] == "25" + assert fastapi_response.headers["x-litellm-response-cost"] == "0.00123" + + +class TestPreCallWithFallbacksOnLocalRateLimit: + + @pytest.mark.asyncio + async def test_fallback_triggered_on_local_rate_limit(self): + from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError + from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing + + primary_model = "gpt-4" + fallback_model = "gpt-3.5-turbo" + + processor = ProxyBaseLLMRequestProcessing(data={"model": primary_model}) + + call_count = 0 + + async def mock_pre_call_logic(**kwargs): + nonlocal call_count + call_count += 1 + model_in_data = processor.data.get("model") + if model_in_data == primary_model: + raise ProxyRateLimitError( + detail="TPM limit exceeded for gpt-4", + headers={"retry-after": "30"}, + ) + logging_obj = MagicMock() + return processor.data, logging_obj + + mock_router = MagicMock() + mock_router.fallbacks = [{"gpt-4": ["gpt-3.5-turbo"]}] + + with patch.object( + processor, + "common_processing_pre_call_logic", + side_effect=mock_pre_call_logic, + ): + data, logging_obj = await processor._pre_call_with_fallbacks( + request=MagicMock(), + general_settings={}, + proxy_logging_obj=MagicMock(), + user_api_key_dict=MagicMock(router_settings=None), + version=None, + proxy_config=MagicMock(), + user_model=None, + user_temperature=None, + user_request_timeout=None, + user_max_tokens=None, + user_api_base=None, + model=primary_model, + route_type="acompletion", + llm_router=mock_router, + ) + + assert processor.data["model"] == fallback_model + assert call_count == 2 + + @pytest.mark.asyncio + async def test_raises_when_no_fallbacks_configured(self): + from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError + from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing + + processor = ProxyBaseLLMRequestProcessing(data={"model": "gpt-4"}) + + async def mock_pre_call_logic(**kwargs): + raise ProxyRateLimitError( + detail="TPM limit exceeded", + headers={"retry-after": "30"}, + ) + + mock_router = MagicMock() + mock_router.fallbacks = None + + with patch.object( + processor, + "common_processing_pre_call_logic", + side_effect=mock_pre_call_logic, + ): + with pytest.raises(ProxyRateLimitError): + await processor._pre_call_with_fallbacks( + request=MagicMock(), + general_settings={}, + proxy_logging_obj=MagicMock(), + user_api_key_dict=MagicMock(router_settings=None), + version=None, + proxy_config=MagicMock(), + user_model=None, + user_temperature=None, + user_request_timeout=None, + user_max_tokens=None, + user_api_base=None, + model="gpt-4", + route_type="acompletion", + llm_router=mock_router, + ) + + @pytest.mark.asyncio + async def test_raises_when_all_fallbacks_also_rate_limited(self): + from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError + from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing + + processor = ProxyBaseLLMRequestProcessing(data={"model": "gpt-4"}) + + async def mock_pre_call_logic(**kwargs): + raise ProxyRateLimitError( + detail=f"TPM limit exceeded for {processor.data.get('model')}", + headers={"retry-after": "30"}, + ) + + mock_router = MagicMock() + mock_router.fallbacks = [{"gpt-4": ["gpt-3.5-turbo", "claude-3-haiku"]}] + + with patch.object( + processor, + "common_processing_pre_call_logic", + side_effect=mock_pre_call_logic, + ): + with pytest.raises(ProxyRateLimitError, match="gpt-4"): + await processor._pre_call_with_fallbacks( + request=MagicMock(), + general_settings={}, + proxy_logging_obj=MagicMock(), + user_api_key_dict=MagicMock(router_settings=None), + version=None, + proxy_config=MagicMock(), + user_model=None, + user_temperature=None, + user_request_timeout=None, + user_max_tokens=None, + user_api_base=None, + model="gpt-4", + route_type="acompletion", + llm_router=mock_router, + ) + + assert processor.data["model"] == "gpt-4" + + @pytest.mark.asyncio + async def test_fallback_uses_key_level_router_settings(self): + from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError + from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing + + processor = ProxyBaseLLMRequestProcessing(data={"model": "gpt-4"}) + + async def mock_pre_call_logic(**kwargs): + if processor.data.get("model") == "gpt-4": + raise ProxyRateLimitError( + detail="TPM limit exceeded", + headers={"retry-after": "30"}, + ) + return processor.data, MagicMock() + + mock_router = MagicMock() + mock_router.fallbacks = [{"gpt-4": ["gpt-3.5-turbo"]}] + + user_api_key_dict = MagicMock() + user_api_key_dict.router_settings = { + "fallbacks": [{"gpt-4": ["claude-3-haiku"]}] + } + + with patch.object( + processor, + "common_processing_pre_call_logic", + side_effect=mock_pre_call_logic, + ): + data, _ = await processor._pre_call_with_fallbacks( + request=MagicMock(), + general_settings={}, + proxy_logging_obj=MagicMock(), + user_api_key_dict=user_api_key_dict, + version=None, + proxy_config=MagicMock(), + user_model=None, + user_temperature=None, + user_request_timeout=None, + user_max_tokens=None, + user_api_base=None, + model="gpt-4", + route_type="acompletion", + llm_router=mock_router, + ) + + assert processor.data["model"] == "claude-3-haiku" + + @pytest.mark.asyncio + async def test_disable_fallbacks_flag_respected(self): + from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError + from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing + + processor = ProxyBaseLLMRequestProcessing( + data={"model": "gpt-4", "disable_fallbacks": True} + ) + + async def mock_pre_call_logic(**kwargs): + raise ProxyRateLimitError( + detail="TPM limit exceeded", + headers={"retry-after": "30"}, + ) + + mock_router = MagicMock() + mock_router.fallbacks = [{"gpt-4": ["gpt-3.5-turbo"]}] + + with patch.object( + processor, + "common_processing_pre_call_logic", + side_effect=mock_pre_call_logic, + ): + with pytest.raises(ProxyRateLimitError): + await processor._pre_call_with_fallbacks( + request=MagicMock(), + general_settings={}, + proxy_logging_obj=MagicMock(), + user_api_key_dict=MagicMock(router_settings=None), + version=None, + proxy_config=MagicMock(), + user_model=None, + user_temperature=None, + user_request_timeout=None, + user_max_tokens=None, + user_api_base=None, + model="gpt-4", + route_type="acompletion", + llm_router=mock_router, + ) + + @pytest.mark.asyncio + async def test_model_restored_on_non_rate_limit_exception(self): + from litellm.proxy.common_utils.proxy_rate_limit_error import ProxyRateLimitError + from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing + + primary_model = "gpt-4" + + processor = ProxyBaseLLMRequestProcessing(data={"model": primary_model}) + + async def mock_pre_call_logic(**kwargs): + model_in_data = processor.data.get("model") + if model_in_data == primary_model: + raise ProxyRateLimitError( + detail="TPM limit exceeded for gpt-4", + headers={"retry-after": "30"}, + ) + raise ValueError("unexpected auth failure on fallback") + + mock_router = MagicMock() + mock_router.fallbacks = [{"gpt-4": ["gpt-3.5-turbo"]}] + + with patch.object( + processor, + "common_processing_pre_call_logic", + side_effect=mock_pre_call_logic, + ): + with pytest.raises(ValueError, match="unexpected auth failure"): + await processor._pre_call_with_fallbacks( + request=MagicMock(), + general_settings={}, + proxy_logging_obj=MagicMock(), + user_api_key_dict=MagicMock(router_settings=None), + version=None, + proxy_config=MagicMock(), + user_model=None, + user_temperature=None, + user_request_timeout=None, + user_max_tokens=None, + user_api_base=None, + model="gpt-4", + route_type="acompletion", + llm_router=mock_router, + ) + + assert processor.data["model"] == primary_model + + @pytest.mark.asyncio + async def test_real_parallel_request_limiter_model_tpm_limit_triggers_fallback(self): + """ + Customer-reported scenario from LIT-3890 / GH #8822. + + The prior tests in this class hand-build a ``ProxyRateLimitError``. The + customer's production setup is different: they set a *per-key per-model* + TPM cap on the key itself:: + + Model TPM Limits: {"gpt-4.1-20250414-test": 100} + + and configure a proxy-side fallback (gpt-4.1-...-test -> gpt-4.1-...). + When the per-model TPM cap trips, the real + ``parallel_request_limiter`` raises ``ProxyRateLimitError`` from inside + ``proxy_logging_obj.pre_call_hook`` — the seam ``_pre_call_with_fallbacks`` + wraps. This test drives that *real* limiter (not a mock error) end-to-end + to prove the customer's exact knob triggers the gateway fallback instead + of returning a 429 to the client. + """ + from litellm.caching.caching import DualCache + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.common_request_processing import ( + ProxyBaseLLMRequestProcessing, + ) + from litellm.proxy.common_utils.proxy_rate_limit_error import ( + ProxyRateLimitError, + ) + from litellm.proxy.hooks.parallel_request_limiter import ( + _PROXY_MaxParallelRequestsHandler, + ) + from litellm.proxy.utils import InternalUsageCache + + primary_model = "gpt-4" + fallback_model = "gpt-3.5-turbo" + + # Freeze the limiter's clock so the per-minute counter key is stable and + # the pre-seeded counter is guaranteed to be the one it reads. + class _FrozenClock(datetime.datetime): + @classmethod + def now(cls, tz=None): + return cls(2026, 1, 1, 12, 30, 0) + + precise_minute = "2026-01-01-12-30" + + # Real per-key per-model TPM limiter + a key carrying the customer's + # `model_tpm_limit` metadata (only the primary is capped). + limiter = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=InternalUsageCache(DualCache()) + ) + user_api_key_dict = UserAPIKeyAuth( + api_key="sk-lit3890", + metadata={"model_tpm_limit": {primary_model: 100}}, + ) + + # Pre-seed the primary's per-model token counter at the cap so the very + # next request trips it. The counter key uses the *hashed* api_key. + counter_key = ( + f"{user_api_key_dict.api_key}::{primary_model}" + f"::{precise_minute}::request_count" + ) + await limiter.internal_usage_cache.async_set_cache( + key=counter_key, + value={"current_requests": 0, "current_tpm": 100, "current_rpm": 0}, + litellm_parent_otel_span=None, + local_only=True, + ) + + processor = ProxyBaseLLMRequestProcessing(data={"model": primary_model}) + + # Stand in for common_processing_pre_call_logic's pre_call_hook step by + # invoking the real limiter for whatever model is currently selected. + limiter_calls = [] + + async def real_limiter_pre_call(**kwargs): + current_model = processor.data["model"] + limiter_calls.append(current_model) + await limiter.async_pre_call_hook( + user_api_key_dict=user_api_key_dict, + cache=DualCache(), + data={ + "model": current_model, + "messages": [{"role": "user", "content": "hi"}], + }, + call_type="acompletion", + ) + return processor.data, MagicMock() + + mock_router = MagicMock() + mock_router.fallbacks = [{primary_model: [fallback_model]}] + + with patch( + "litellm.proxy.hooks.parallel_request_limiter.datetime", _FrozenClock + ): + with patch.object( + processor, + "common_processing_pre_call_logic", + side_effect=real_limiter_pre_call, + ): + data, logging_obj = await processor._pre_call_with_fallbacks( + request=MagicMock(), + general_settings={}, + proxy_logging_obj=MagicMock(), + user_api_key_dict=user_api_key_dict, + version=None, + proxy_config=MagicMock(), + user_model=None, + user_temperature=None, + user_request_timeout=None, + user_max_tokens=None, + user_api_base=None, + model=primary_model, + route_type="acompletion", + llm_router=mock_router, + ) + + # The capped primary tripped the real limiter, and the fallback (which + # has no per-model cap) served the request — no 429 to the client. + assert processor.data["model"] == fallback_model + assert limiter_calls == [primary_model, fallback_model] + + # Sanity-check the premise: the limiter genuinely raises a + # ProxyRateLimitError for the capped primary under the frozen clock. + with patch( + "litellm.proxy.hooks.parallel_request_limiter.datetime", _FrozenClock + ): + with pytest.raises(ProxyRateLimitError): + await limiter.async_pre_call_hook( + user_api_key_dict=user_api_key_dict, + cache=DualCache(), + data={ + "model": primary_model, + "messages": [{"role": "user", "content": "hi"}], + }, + call_type="acompletion", + ) diff --git a/tests/test_litellm/proxy/test_component_allowlists.py b/tests/test_litellm/proxy/test_component_allowlists.py index 926ce3bee66..3dd8d8b28cd 100644 --- a/tests/test_litellm/proxy/test_component_allowlists.py +++ b/tests/test_litellm/proxy/test_component_allowlists.py @@ -11,10 +11,16 @@ clients hitting that path on the corresponding pod get a 404. This test guarantees that the union of the two trimmed route sets equals the full set of routes on the proxy app — i.e. no endpoint is dropped on the floor. -The test reproduces the same predicate that ``gateway/main.py`` and -``backend/main.py`` use, without importing them. The component modules wrap +The union-coverage test reproduces the same predicate that ``gateway/main.py`` +and ``backend/main.py`` use, without importing them. The component modules wrap the shared ``app.router.lifespan_context``; importing them in the test process -would chain wrappers and corrupt the snapshot. +would chain wrappers and corrupt the snapshot. The gateway Mount tests below +import the real ``gateway.main._is_gateway_route`` instead, undoing both of the +module's import-time side effects: the lifespan wrapper is restored right after +the import, and the DATABASE_* env vars are popped for its duration because +``gateway.main`` runs ``DatabaseURLSettings.from_env().apply_to_env()`` at +import (which raises on a non-postgres ``DATABASE_URL`` scheme and can mint an +RDS IAM token when ``IAM_TOKEN_DB_AUTH`` is set). """ import os @@ -36,6 +42,7 @@ for _key, _value in _THROWAWAY_ENV.items(): os.environ.setdefault(_key, _value) from fastapi.routing import Mount +from prometheus_client import make_asgi_app # gateway/ and backend/ live at the repo root, not inside litellm/. _REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) @@ -47,7 +54,11 @@ from backend.routes.allowlist import ( BACKEND_MOUNT_PATHS, BACKEND_PATH_PREFIXES, ) -from gateway.routes.allowlist import GATEWAY_EXACT_PATHS, GATEWAY_PATH_PREFIXES +from gateway.routes.allowlist import ( + GATEWAY_EXACT_PATHS, + GATEWAY_MOUNT_PATHS, + GATEWAY_PATH_PREFIXES, +) from litellm.proxy.proxy_server import app for _key, _previous in _PRE_EXISTING_ENV.items(): @@ -56,6 +67,24 @@ for _key, _previous in _PRE_EXISTING_ENV.items(): else: os.environ[_key] = _previous +_DB_ENV_KEYS = ( + "DATABASE_URL", + "DIRECT_URL", + "DATABASE_URL_READ_REPLICA", + "DATABASE_HOST", + "DATABASE_HOST_READ_REPLICA", + "DATABASE_PASSWORD", + "IAM_TOKEN_DB_AUTH", +) +_PRE_DB_ENV = {_key: os.environ.pop(_key, None) for _key in _DB_ENV_KEYS} +_PRE_COMPONENT_LIFESPAN = app.router.lifespan_context +from gateway.main import _is_gateway_route + +app.router.lifespan_context = _PRE_COMPONENT_LIFESPAN +for _key, _previous in _PRE_DB_ENV.items(): + if _previous is not None: + os.environ[_key] = _previous + def _component_paths(routes, exact_paths, path_prefixes) -> set[str]: """Reproduce ``gateway.main._is_gateway_route`` / ``backend.main._is_backend_route``.""" @@ -133,3 +162,61 @@ def test_backend_drops_non_allowlisted_mounts(): for mount_path in non_backend_mounts: assert mount_path not in BACKEND_MOUNT_PATHS, \ f"Mount {mount_path} should not be in BACKEND_MOUNT_PATHS" + + +def test_gateway_mount_paths_defined(): + """GATEWAY_MOUNT_PATHS constant must exist and expose /metrics.""" + assert isinstance(GATEWAY_MOUNT_PATHS, frozenset), \ + f"GATEWAY_MOUNT_PATHS must be a frozenset, got {type(GATEWAY_MOUNT_PATHS)}" + assert "/metrics" in GATEWAY_MOUNT_PATHS, \ + "/metrics Mount path must be in GATEWAY_MOUNT_PATHS" + + +def test_gateway_trim_keeps_metrics_mount(): + """The Prometheus /metrics Mount must survive the gateway route trim. + + Regression test for https://github.com/BerriAI/litellm/issues/30291: + ``_is_gateway_route`` used to reject every Mount before the allowlist + check, so the /metrics Mount registered by + ``PrometheusLogger._mount_metrics_endpoint()`` was dropped at startup and + the gateway returned 404 on /metrics. + """ + metrics_mount = Mount("/metrics", app=make_asgi_app()) + routes = [*app.router.routes, metrics_mount] + trimmed = [r for r in routes if _is_gateway_route(r)] + assert metrics_mount in trimmed, \ + "/metrics Mount must survive the gateway route trim" + + +def test_gateway_drops_ui_and_swagger_mounts(): + """UI static and swagger Mounts must still be trimmed from the gateway.""" + for path in ("/ui", "/_next", "/litellm-asset-prefix/_next", "/swagger"): + assert not _is_gateway_route(Mount(path, app=make_asgi_app())), \ + f"Mount {path} must not be served by the gateway" + + +def test_every_app_mount_is_assigned_to_a_component(): + """Every Mount on the proxy app must be consciously assigned to a component. + + A Mount must be kept by the gateway (GATEWAY_MOUNT_PATHS), kept by the + backend (BACKEND_MOUNT_PATHS), or be a static mount served by the + dedicated UI container. A Mount matching none of these is unreachable in + a componentized deployment, which is exactly how the /metrics Mount was + silently dropped. + """ + ui_served_prefixes = ("/ui", "/_next", "/litellm-asset-prefix") + mounts = [*app.router.routes, Mount("/metrics", app=make_asgi_app())] + unassigned = { + path + for r in mounts + if isinstance(r, Mount) + and (path := getattr(r, "path", None)) is not None + and path not in GATEWAY_MOUNT_PATHS + and path not in BACKEND_MOUNT_PATHS + and not path.startswith(ui_served_prefixes) + } + assert not unassigned, ( + f"{len(unassigned)} Mount(s) are not exposed on any component. " + f"Add them to GATEWAY_MOUNT_PATHS, BACKEND_MOUNT_PATHS, or serve them " + f"from the UI container:\n " + "\n ".join(sorted(unassigned)) + ) diff --git a/tests/test_litellm/proxy/test_litellm_pre_call_utils.py b/tests/test_litellm/proxy/test_litellm_pre_call_utils.py index 74f681bb97e..913ac116866 100644 --- a/tests/test_litellm/proxy/test_litellm_pre_call_utils.py +++ b/tests/test_litellm/proxy/test_litellm_pre_call_utils.py @@ -856,10 +856,19 @@ async def test_add_litellm_data_to_request_strips_client_redaction_bypass_contro "model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "hello"}], "turn_off_message_logging": False, - "metadata": {"headers": {"litellm-disable-message-redaction": "true"}}, + "metadata": { + "headers": {"litellm-disable-message-redaction": "true"}, + "turn_off_message_logging": False, + }, "litellm_metadata": json.dumps( - {"headers": {"LiteLLM-Disable-Message-Redaction": "true"}} + { + "headers": {"LiteLLM-Disable-Message-Redaction": "true"}, + "turn_off_message_logging": "false", + } ), + "litellm_params": { + "metadata": {"turn_off_message_logging": False}, + }, }, request=request_mock, user_api_key_dict=UserAPIKeyAuth(api_key="hashed-key"), @@ -871,6 +880,9 @@ async def test_add_litellm_data_to_request_strips_client_redaction_bypass_contro litellm.turn_off_message_logging = original_turn_off_message_logging assert "turn_off_message_logging" not in updated + assert "turn_off_message_logging" not in (updated.get("litellm_params") or {}).get("metadata", {}) + assert "turn_off_message_logging" not in updated["metadata"] + assert "turn_off_message_logging" not in (updated.get("litellm_metadata") or {}) assert "litellm-disable-message-redaction" not in { header.lower() for header in updated["metadata"]["headers"] } @@ -891,6 +903,158 @@ async def test_add_litellm_data_to_request_strips_client_redaction_bypass_contro } +@pytest.mark.parametrize( + "admin_metadata_kwargs", + [ + { + "metadata": { + "logging": [ + { + "callback_name": "langfuse", + "callback_type": "success_and_failure", + "callback_vars": {"turn_off_message_logging": False}, + } + ] + } + }, + { + "team_metadata": { + "logging": [ + { + "callback_name": "langfuse", + "callback_type": "success_and_failure", + "callback_vars": {"turn_off_message_logging": False}, + } + ] + } + }, + ], +) +@pytest.mark.asyncio +async def test_add_litellm_data_to_request_admin_callback_vars_turn_off_message_logging_overrides_global( + admin_metadata_kwargs, +): + from litellm.litellm_core_utils.initialize_dynamic_callback_params import ( + initialize_standard_callback_dynamic_params, + ) + from litellm.litellm_core_utils.redact_messages import should_redact_message_logging + + request_mock = MagicMock(spec=Request) + request_mock.url.path = "/v1/chat/completions" + request_mock.url = MagicMock() + request_mock.url.__str__.return_value = "http://localhost/v1/chat/completions" + request_mock.method = "POST" + request_mock.query_params = {} + request_mock.headers = {"Content-Type": "application/json"} + request_mock.client = MagicMock() + request_mock.client.host = "127.0.0.1" + + original_turn_off_message_logging = litellm.turn_off_message_logging + litellm.turn_off_message_logging = True + try: + updated = await add_litellm_data_to_request( + data={ + "model": "gpt-3.5-turbo", + "messages": [{"role": "user", "content": "hello"}], + }, + request=request_mock, + user_api_key_dict=UserAPIKeyAuth(api_key="hashed-key", **admin_metadata_kwargs), + proxy_config=MagicMock(), + general_settings={}, + version="test-version", + ) + + assert updated.get("turn_off_message_logging") == "False" + + dynamic_params = initialize_standard_callback_dynamic_params(updated) + assert dynamic_params.get("turn_off_message_logging") == "False" + + assert ( + should_redact_message_logging( + {"standard_callback_dynamic_params": dynamic_params} + ) + is False + ) + finally: + litellm.turn_off_message_logging = original_turn_off_message_logging + + +@pytest.mark.parametrize( + "admin_metadata_kwargs", + [ + { + "metadata": { + "logging": [ + { + "callback_name": "langfuse", + "callback_type": "success_and_failure", + "callback_vars": {"turn_off_message_logging": True}, + } + ] + } + }, + { + "team_metadata": { + "logging": [ + { + "callback_name": "langfuse", + "callback_type": "success_and_failure", + "callback_vars": {"turn_off_message_logging": True}, + } + ] + } + }, + ], +) +@pytest.mark.asyncio +async def test_add_litellm_data_to_request_admin_callback_vars_turn_off_message_logging_enables_redaction_when_global_off( + admin_metadata_kwargs, +): + from litellm.litellm_core_utils.initialize_dynamic_callback_params import ( + initialize_standard_callback_dynamic_params, + ) + from litellm.litellm_core_utils.redact_messages import should_redact_message_logging + + request_mock = MagicMock(spec=Request) + request_mock.url.path = "/v1/chat/completions" + request_mock.url = MagicMock() + request_mock.url.__str__.return_value = "http://localhost/v1/chat/completions" + request_mock.method = "POST" + request_mock.query_params = {} + request_mock.headers = {"Content-Type": "application/json"} + request_mock.client = MagicMock() + request_mock.client.host = "127.0.0.1" + + original_turn_off_message_logging = litellm.turn_off_message_logging + litellm.turn_off_message_logging = False + try: + updated = await add_litellm_data_to_request( + data={ + "model": "gpt-3.5-turbo", + "messages": [{"role": "user", "content": "hello"}], + }, + request=request_mock, + user_api_key_dict=UserAPIKeyAuth(api_key="hashed-key", **admin_metadata_kwargs), + proxy_config=MagicMock(), + general_settings={}, + version="test-version", + ) + + assert updated.get("turn_off_message_logging") == "True" + + dynamic_params = initialize_standard_callback_dynamic_params(updated) + assert dynamic_params.get("turn_off_message_logging") == "True" + + assert ( + should_redact_message_logging( + {"standard_callback_dynamic_params": dynamic_params} + ) + is True + ) + finally: + litellm.turn_off_message_logging = original_turn_off_message_logging + + @pytest.mark.parametrize( "auth_kwargs", [ @@ -923,7 +1087,10 @@ async def test_add_litellm_data_to_request_allows_redaction_opt_out_with_admin_o "model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "hello"}], "turn_off_message_logging": False, - "metadata": {"headers": {"litellm-disable-message-redaction": "true"}}, + "metadata": { + "headers": {"litellm-disable-message-redaction": "true"}, + "turn_off_message_logging": False, + }, "litellm_metadata": json.dumps( {"headers": {"LiteLLM-Disable-Message-Redaction": "true"}} ), @@ -938,6 +1105,7 @@ async def test_add_litellm_data_to_request_allows_redaction_opt_out_with_admin_o litellm.turn_off_message_logging = original_turn_off_message_logging assert updated["turn_off_message_logging"] is False + assert updated["metadata"]["turn_off_message_logging"] is False assert "litellm-disable-message-redaction" in { header.lower() for header in updated["metadata"]["headers"] } @@ -4798,3 +4966,91 @@ async def test_add_litellm_data_to_request_claude_code_drop_params( ) assert updated.get("drop_params") == expected_drop_params + + +@pytest.mark.asyncio +async def test_add_litellm_data_to_request_merges_metadata_tags_on_responses_route(): + """Regression for #31584: user-supplied metadata.tags must be merged into + litellm_metadata.tags on /v1/responses so they reach SpendLogs.request_tags.""" + from litellm.proxy.litellm_pre_call_utils import add_litellm_data_to_request + + request_mock = MagicMock(spec=Request) + request_mock.url = MagicMock() + request_mock.url.path = "/v1/responses" + request_mock.url.__str__.return_value = "http://localhost/v1/responses" + request_mock.method = "POST" + request_mock.query_params = {} + request_mock.headers = {"Content-Type": "application/json"} + request_mock.client = MagicMock() + request_mock.client.host = "127.0.0.1" + request_mock.state = MagicMock() + + data = { + "model": "gpt-4o", + "input": "hello", + "metadata": {"tags": ["cost-center-1", "team-alpha"]}, + } + + user_api_key_dict = UserAPIKeyAuth( + api_key="hashed-key", + metadata={}, + team_metadata={}, + ) + + updated = await add_litellm_data_to_request( + data=data, + request=request_mock, + user_api_key_dict=user_api_key_dict, + proxy_config=MagicMock(), + general_settings={}, + version="test-version", + ) + + assert "cost-center-1" in updated["litellm_metadata"]["tags"] + assert "team-alpha" in updated["litellm_metadata"]["tags"] + + +@pytest.mark.asyncio +async def test_add_litellm_data_to_request_unions_metadata_tags_with_header_tags_on_responses_route(): + """On /v1/responses, tags from metadata.tags AND x-litellm-tags header + must both appear in litellm_metadata.tags.""" + from litellm.proxy.litellm_pre_call_utils import add_litellm_data_to_request + + request_mock = MagicMock(spec=Request) + request_mock.url = MagicMock() + request_mock.url.path = "/v1/responses" + request_mock.url.__str__.return_value = "http://localhost/v1/responses" + request_mock.method = "POST" + request_mock.query_params = {} + request_mock.headers = { + "Content-Type": "application/json", + "x-litellm-tags": "header-tag", + } + request_mock.client = MagicMock() + request_mock.client.host = "127.0.0.1" + request_mock.state = MagicMock() + + data = { + "model": "gpt-4o", + "input": "hello", + "metadata": {"tags": ["body-tag"]}, + } + + user_api_key_dict = UserAPIKeyAuth( + api_key="hashed-key", + metadata={}, + team_metadata={}, + ) + + updated = await add_litellm_data_to_request( + data=data, + request=request_mock, + user_api_key_dict=user_api_key_dict, + proxy_config=MagicMock(), + general_settings={}, + version="test-version", + ) + + tags = updated["litellm_metadata"]["tags"] + assert "header-tag" in tags + assert "body-tag" in tags diff --git a/tests/test_litellm/proxy/test_proxy_server.py b/tests/test_litellm/proxy/test_proxy_server.py index 019a7dc90d2..603d5cc15b7 100644 --- a/tests/test_litellm/proxy/test_proxy_server.py +++ b/tests/test_litellm/proxy/test_proxy_server.py @@ -5,6 +5,7 @@ import os import socket import subprocess import sys +import types from datetime import datetime, timedelta, timezone from pathlib import Path from unittest import mock @@ -23,6 +24,10 @@ sys.path.insert( ) # Adds the parent directory to the system-path import litellm +import litellm.proxy.proxy_server as proxy_server_module +from litellm.caching.caching import RedisCache +from litellm.caching.redis_cluster_cache import RedisClusterCache +from litellm.caching.dual_cache import DualCache from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.proxy_server import app, initialize @@ -604,17 +609,9 @@ def test_ui_extensionless_route_requires_restructure(tmp_path): assert "login" in response.text -def test_admin_ui_export_serves_nested_extensionless_routes(tmp_path): - from litellm.proxy import proxy_server - - out_dir = tmp_path / "out" - (out_dir / "_next").mkdir(parents=True) - (out_dir / "index.html").write_text("home") - callback_src = out_dir / "mcp" / "oauth" / "callback.html" - callback_src.parent.mkdir(parents=True) - callback_src.write_text("callback") - - proxy_server._restructure_ui_html_files(str(out_dir)) +def test_admin_ui_export_serves_nested_extensionless_routes(): + out_dir = Path(litellm.__file__).parent / "proxy" / "_experimental" / "out" + assert out_dir.is_dir(), f"missing UI export at {out_dir}" nested_html_offenders = [ path.relative_to(out_dir).as_posix() @@ -754,6 +751,66 @@ async def test_initialize_scheduled_jobs_credentials(monkeypatch): assert len(mock_scheduler_calls) > 0 +@pytest.mark.asyncio +async def test_initialize_scheduled_jobs_hydrates_mcp_when_store_model_in_db_false(monkeypatch): + """ + Regression (LIT-4128): MCP servers created via the UI are persisted to the DB + regardless of store_model_in_db, but the in-memory registry that GET + /v1/mcp/server reads is hydrated from the DB only by the store_model_in_db + model-sync loop (add_deployment). On a DB-backed proxy with store_model_in_db + unset the registry must still be hydrated on startup so previously-added + servers survive a restart instead of showing an empty list until a write. + """ + monkeypatch.delenv("DISABLE_PRISMA_SCHEMA_UPDATE", raising=False) + monkeypatch.delenv("STORE_MODEL_IN_DB", raising=False) + from litellm.proxy.proxy_server import ProxyStartupEvent + from litellm.proxy.utils import ProxyLogging + + mock_prisma_client = MagicMock() + mock_proxy_logging = MagicMock(spec=ProxyLogging) + mock_proxy_logging.slack_alerting_instance = MagicMock() + mock_proxy_config = AsyncMock() + + with ( + patch("litellm.proxy.proxy_server.proxy_config", mock_proxy_config), + patch("litellm.proxy.proxy_server.store_model_in_db", False), + ): + await ProxyStartupEvent.initialize_scheduled_background_jobs( + general_settings={}, + prisma_client=mock_prisma_client, + proxy_budget_rescheduler_min_time=1, + proxy_budget_rescheduler_max_time=2, + proxy_batch_write_at=5, + proxy_logging_obj=mock_proxy_logging, + ) + + mock_proxy_config.add_deployment.assert_not_called() + mock_proxy_config.init_mcp_servers_from_db.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_init_mcp_servers_from_db_respects_supported_db_objects(monkeypatch): + """ + init_mcp_servers_from_db hydrates MCP from the DB by default but skips it when + an explicit supported_db_objects allowlist omits "mcp". + """ + from litellm.proxy.proxy_server import ProxyConfig + + config = ProxyConfig() + with patch.object(config, "_init_mcp_servers_in_db", new=AsyncMock()) as mock_init: + monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {}) + await config.init_mcp_servers_from_db() + mock_init.assert_awaited_once() + + mock_init.reset_mock() + monkeypatch.setattr( + "litellm.proxy.proxy_server.general_settings", + {"supported_db_objects": ["models"]}, + ) + await config.init_mcp_servers_from_db() + mock_init.assert_not_awaited() + + def test_update_config_fields_deep_merge_db_wins(): from litellm.proxy.proxy_server import ProxyConfig @@ -844,7 +901,9 @@ def test_get_config_custom_callback_api_env_vars(monkeypatch): # Bypass auth dependency original_overrides = app.dependency_overrides.copy() - app.dependency_overrides[user_api_key_auth] = lambda: MagicMock() + app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-1234" + ) client = TestClient(app) try: @@ -898,7 +957,9 @@ def test_get_config_returns_email_settings(monkeypatch): monkeypatch.setattr(proxy_config, "get_config", AsyncMock(return_value=config_data)) original_overrides = app.dependency_overrides.copy() - app.dependency_overrides[user_api_key_auth] = lambda: MagicMock() + app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-1234" + ) client = TestClient(app) try: @@ -955,7 +1016,9 @@ def test_get_config_returns_slack_webhook(monkeypatch): monkeypatch.setattr(proxy_config, "get_config", AsyncMock(return_value=config_data)) original_overrides = app.dependency_overrides.copy() - app.dependency_overrides[user_api_key_auth] = lambda: MagicMock() + app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-1234" + ) client = TestClient(app) try: @@ -1009,7 +1072,9 @@ def test_get_config_cleared_slack_webhook_not_overridden_by_os_env(monkeypatch): monkeypatch.setattr(proxy_config, "get_config", AsyncMock(return_value=config_data)) original_overrides = app.dependency_overrides.copy() - app.dependency_overrides[user_api_key_auth] = lambda: MagicMock() + app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-1234" + ) client = TestClient(app) try: @@ -2523,6 +2588,50 @@ async def test_load_config_max_budget_env_var_coerced_to_float(tmp_path, monkeyp litellm.max_budget = original_max_budget +@pytest.mark.asyncio +async def test_load_config_user_url_validation_handles_null_and_string_false(tmp_path, monkeypatch): + from litellm.proxy.proxy_server import ProxyConfig + + monkeypatch.setattr(litellm, "user_url_validation", True) + monkeypatch.setattr(litellm, "user_url_allowed_hosts", ["internal.example"]) + monkeypatch.setattr(litellm, "provider_url_destination_allowed_hosts", ["provider.example"]) + null_config_file = tmp_path / "null_config.yaml" + null_config_file.write_text( + yaml.dump( + { + "model_list": [], + "general_settings": { + "user_url_allowed_hosts": None, + "user_url_validation": None, + "provider_url_destination_allowed_hosts": None, + }, + } + ) + ) + + await ProxyConfig().load_config( + router=MagicMock(), config_file_path=str(null_config_file) + ) + assert litellm.user_url_validation is True + assert litellm.user_url_allowed_hosts is None + assert litellm.provider_url_destination_allowed_hosts is None + + false_config_file = tmp_path / "false_config.yaml" + false_config_file.write_text( + yaml.dump( + { + "model_list": [], + "general_settings": {"user_url_validation": "false"}, + } + ) + ) + + await ProxyConfig().load_config( + router=MagicMock(), config_file_path=str(false_config_file) + ) + assert litellm.user_url_validation is False + + @pytest.mark.asyncio async def test_load_environment_variables_direct_and_os_environ(): """ @@ -5153,7 +5262,9 @@ def test_get_config_normalizes_string_callbacks(monkeypatch): monkeypatch.setattr(proxy_config, "get_config", AsyncMock(return_value=config_data)) original_overrides = app.dependency_overrides.copy() - app.dependency_overrides[user_api_key_auth] = lambda: MagicMock() + app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-1234" + ) client = TestClient(app) try: @@ -8538,6 +8649,149 @@ def test_get_config_list_includes_cancel_on_disconnect(monkeypatch): app.dependency_overrides.clear() +def test_get_config_list_includes_budget_exceeded_throttle_percentage(monkeypatch): + """The throttle fraction is a litellm_settings scalar surfaced on the General + Settings table as a Float field so it sits with the other global limits; it + must appear in /config/list reading its live litellm. value.""" + import types + from unittest.mock import AsyncMock, MagicMock + + from fastapi.testclient import TestClient + + import litellm.proxy.proxy_server as ps + from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth + from litellm.proxy.proxy_server import app + + mock_prisma = MagicMock() + mock_config_table = MagicMock() + mock_config_table.find_first = AsyncMock(return_value=None) + mock_prisma.db = types.SimpleNamespace(litellm_config=mock_config_table) + monkeypatch.setattr(ps, "prisma_client", mock_prisma) + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.15) + app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( + user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN + ) + try: + client = TestClient(app) + resp = client.get("/config/list", params={"config_type": "general_settings"}) + assert resp.status_code == 200, resp.text + fields = {item["field_name"]: item for item in resp.json()} + assert "budget_exceeded_throttle_percentage" in fields + assert fields["budget_exceeded_throttle_percentage"]["field_type"] == "Float" + assert fields["budget_exceeded_throttle_percentage"]["field_value"] == 0.15 + finally: + app.dependency_overrides.clear() + + +@pytest.mark.asyncio +async def test_update_config_field_throttle_persists_to_litellm_settings(monkeypatch): + """Editing the throttle Float row on the General Settings table routes to + litellm_settings (not general_settings): it sets litellm. live and + persists under litellm_settings so the runtime read is unchanged.""" + from unittest.mock import MagicMock + + import litellm.proxy.proxy_server as ps + from litellm.proxy._types import ( + ConfigFieldUpdate, + LitellmUserRoles, + UserAPIKeyAuth, + ) + from litellm.proxy.proxy_server import update_config_general_settings + + saved: dict = {} + + async def fake_get_config(): + return {"litellm_settings": {}} + + async def fake_save_config(new_config=None): + saved.update(new_config or {}) + + monkeypatch.setattr(ps.proxy_config, "get_config", fake_get_config) + monkeypatch.setattr(ps.proxy_config, "save_config", fake_save_config) + monkeypatch.setattr(ps, "prisma_client", MagicMock()) + monkeypatch.setattr(litellm, "store_audit_logs", False) + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", None) + + admin = UserAPIKeyAuth(api_key="k", user_id="a", user_role=LitellmUserRoles.PROXY_ADMIN) + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="budget_exceeded_throttle_percentage", + field_value=0.1, + config_type="general_settings", + ), + user_api_key_dict=admin, + ) + + assert litellm.budget_exceeded_throttle_percentage == 0.1 + assert saved["litellm_settings"]["budget_exceeded_throttle_percentage"] == 0.1 + + +@pytest.mark.parametrize("bad_value", [0, -0.1, 1.5, True]) +@pytest.mark.asyncio +async def test_update_config_field_throttle_rejects_invalid(monkeypatch, bad_value): + from unittest.mock import MagicMock + + from fastapi import HTTPException + + import litellm.proxy.proxy_server as ps + from litellm.proxy._types import ( + ConfigFieldUpdate, + LitellmUserRoles, + UserAPIKeyAuth, + ) + from litellm.proxy.proxy_server import update_config_general_settings + + async def fake_get_config(): + return {"litellm_settings": {}} + + monkeypatch.setattr(ps.proxy_config, "get_config", fake_get_config) + monkeypatch.setattr(ps, "prisma_client", MagicMock()) + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", None) + + admin = UserAPIKeyAuth(api_key="k", user_id="a", user_role=LitellmUserRoles.PROXY_ADMIN) + with pytest.raises(HTTPException) as exc: + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="budget_exceeded_throttle_percentage", + field_value=bad_value, + config_type="general_settings", + ), + user_api_key_dict=admin, + ) + assert exc.value.status_code == 400 + assert litellm.budget_exceeded_throttle_percentage is None + + +@pytest.mark.asyncio +async def test_update_config_field_throttle_rejected_for_non_admin(monkeypatch): + from unittest.mock import MagicMock + + from fastapi import HTTPException + + import litellm.proxy.proxy_server as ps + from litellm.proxy._types import ( + ConfigFieldUpdate, + LitellmUserRoles, + UserAPIKeyAuth, + ) + from litellm.proxy.proxy_server import update_config_general_settings + + monkeypatch.setattr(ps, "prisma_client", MagicMock()) + monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", None) + + non_admin = UserAPIKeyAuth(api_key="k", user_id="u", user_role=LitellmUserRoles.INTERNAL_USER) + with pytest.raises(HTTPException): + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="budget_exceeded_throttle_percentage", + field_value=0.1, + config_type="general_settings", + ), + user_api_key_dict=non_admin, + ) + assert litellm.budget_exceeded_throttle_percentage is None + + def test_preserve_redacted_plugin_keys_keeps_stored_credential(): """A redacted or blank plugin_key on update must not overwrite the real key.""" from litellm.proxy.proxy_server import _preserve_redacted_plugin_keys @@ -8658,3 +8912,777 @@ def test_config_field_info_returns_raw_secrets_for_full_admin(monkeypatch): ) finally: app.dependency_overrides.clear() + + +def _fake_prisma_with_config(existing_param_value): + """MagicMock prisma whose litellm_config row returns existing_param_value and + whose litellm_auditlog.create records the written audit row.""" + fake = MagicMock() + config_row = MagicMock() + config_row.param_value = existing_param_value + fake.db.litellm_config.find_first = AsyncMock(return_value=config_row) + fake.db.litellm_config.upsert = AsyncMock(return_value=config_row) + fake.db.litellm_auditlog.create = AsyncMock() + return fake + + +def test_dump_redacted_config_redacts_secret_leaves(): + from litellm.proxy.proxy_server import _dump_redacted_config + + assert _dump_redacted_config(None) is None + + restored = json.loads( + _dump_redacted_config( + { + "api_key": "sk-leak", + "model": "gpt-4", + "nested": {"aws_secret_access_key": "abc", "region": "us-east-1"}, + } + ) + ) + assert restored["api_key"] == "REDACTED" + assert restored["model"] == "gpt-4" + assert restored["nested"]["aws_secret_access_key"] == "REDACTED" + assert restored["nested"]["region"] == "us-east-1" + + +@pytest.mark.asyncio +async def test_create_config_audit_log_writes_redacted_entry(monkeypatch): + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import LitellmTableNames + from litellm.proxy.proxy_server import create_config_audit_log + + fake = _fake_prisma_with_config({}) + monkeypatch.setattr(proxy_server_module, "prisma_client", fake) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + + caller = UserAPIKeyAuth(api_key="hashed-key-abc", user_id="admin-7") + await create_config_audit_log( + "router_settings", + "updated", + {"routing_strategy": "simple-shuffle", "api_key": "sk-old"}, + {"routing_strategy": "latency-based", "api_key": "sk-new"}, + caller, + ) + + fake.db.litellm_auditlog.create.assert_awaited_once() + written = fake.db.litellm_auditlog.create.call_args.kwargs["data"] + assert written["table_name"] == LitellmTableNames.CONFIG_TABLE_NAME.value + assert written["object_id"] == "router_settings" + assert written["action"] == "updated" + assert written["changed_by"] == "admin-7" + assert written["changed_by_api_key"] == "hashed-key-abc" + + before = json.loads(written["before_value"]) + after = json.loads(written["updated_values"]) + assert before["routing_strategy"] == "simple-shuffle" + assert after["routing_strategy"] == "latency-based" + assert "sk-old" not in written["before_value"] + assert "sk-new" not in written["updated_values"] + assert before["api_key"] != "sk-old" + assert after["api_key"] != "sk-new" + + +@pytest.mark.asyncio +async def test_create_config_audit_log_noop_when_store_audit_logs_disabled(monkeypatch): + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy.proxy_server import create_config_audit_log + + fake = _fake_prisma_with_config({}) + monkeypatch.setattr(proxy_server_module, "prisma_client", fake) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(litellm, "store_audit_logs", False) + + await create_config_audit_log( + "router_settings", + "updated", + {}, + {"a": 1}, + UserAPIKeyAuth(api_key="k", user_id="u"), + ) + fake.db.litellm_auditlog.create.assert_not_called() + + +def test_dump_redacted_config_serializes_non_json_native_values(): + """YAML-loaded config can contain datetime/date/custom values that plain + json.dumps refuses. Without default=str the audit write turns into a 500 + after the config change has already committed; the sibling audit-log + serializers in team_endpoints.py use default=str for the same reason.""" + from datetime import datetime, timezone + + from litellm.proxy.proxy_server import _dump_redacted_config + + out = _dump_redacted_config({"updated_at": datetime(2026, 6, 30, tzinfo=timezone.utc)}) + assert out is not None + restored = json.loads(out) + assert "2026-06-30" in restored["updated_at"] + + +@pytest.mark.asyncio +async def test_update_config_general_settings_emits_audit_log(monkeypatch): + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import ConfigFieldUpdate + from litellm.proxy.proxy_server import update_config_general_settings + + existing = {"max_parallel_requests": 5, "some_api_key": "sk-stored-secret"} + fake = _fake_prisma_with_config(existing) + monkeypatch.setattr(proxy_server_module, "prisma_client", fake) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + + admin = UserAPIKeyAuth( + api_key="hashed-admin", + user_id="admin-1", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="max_parallel_requests", + field_value=42, + config_type="general_settings", + ), + user_api_key_dict=admin, + ) + # Audit is scheduled via asyncio.create_task; yield so it runs. + await asyncio.sleep(0) + + fake.db.litellm_auditlog.create.assert_awaited_once() + written = fake.db.litellm_auditlog.create.call_args.kwargs["data"] + assert written["table_name"] == "LiteLLM_Config" + assert written["object_id"] == "general_settings" + assert written["action"] == "updated" + assert written["changed_by"] == "admin-1" + + before = json.loads(written["before_value"]) + after = json.loads(written["updated_values"]) + assert before["max_parallel_requests"] == 5 + assert after["max_parallel_requests"] == 42 + assert "sk-stored-secret" not in written["before_value"] + assert "sk-stored-secret" not in written["updated_values"] + assert before["some_api_key"] != "sk-stored-secret" + + +@pytest.mark.asyncio +async def test_update_config_general_settings_applies_ssrf_globals(monkeypatch): + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import ConfigFieldUpdate + from litellm.proxy.proxy_server import update_config_general_settings + + fake = _fake_prisma_with_config({}) + monkeypatch.setattr(proxy_server_module, "prisma_client", fake) + monkeypatch.setattr(litellm, "store_audit_logs", False) + monkeypatch.setattr(litellm, "user_url_validation", True) + monkeypatch.setattr(litellm, "user_url_allowed_hosts", []) + monkeypatch.setattr(litellm, "provider_url_destination_allowed_hosts", []) + + admin = UserAPIKeyAuth( + api_key="hashed-admin", + user_id="admin-1", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="user_url_validation", + field_value="false", + config_type="general_settings", + ), + user_api_key_dict=admin, + ) + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="user_url_allowed_hosts", + field_value=["internal.example"], + config_type="general_settings", + ), + user_api_key_dict=admin, + ) + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="provider_url_destination_allowed_hosts", + field_value=["provider.example"], + config_type="general_settings", + ), + user_api_key_dict=admin, + ) + await asyncio.sleep(0) + + assert litellm.user_url_validation is False + assert litellm.user_url_allowed_hosts == ["internal.example"] + assert litellm.provider_url_destination_allowed_hosts == ["provider.example"] + + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="user_url_allowed_hosts", + field_value=None, + config_type="general_settings", + ), + user_api_key_dict=admin, + ) + await update_config_general_settings( + data=ConfigFieldUpdate( + field_name="provider_url_destination_allowed_hosts", + field_value=None, + config_type="general_settings", + ), + user_api_key_dict=admin, + ) + await asyncio.sleep(0) + + assert litellm.user_url_allowed_hosts is None + assert litellm.provider_url_destination_allowed_hosts is None + + +@pytest.mark.asyncio +async def test_delete_config_general_settings_emits_deleted_audit_log(monkeypatch): + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import ConfigFieldDelete + from litellm.proxy.proxy_server import delete_config_general_settings + + existing = {"max_parallel_requests": 5} + fake = _fake_prisma_with_config(existing) + monkeypatch.setattr(proxy_server_module, "prisma_client", fake) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + + admin = UserAPIKeyAuth( + api_key="hashed-admin", + user_id="admin-1", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + await delete_config_general_settings( + data=ConfigFieldDelete( + field_name="max_parallel_requests", config_type="general_settings" + ), + user_api_key_dict=admin, + ) + # Audit is scheduled via asyncio.create_task; yield so it runs. + await asyncio.sleep(0) + + fake.db.litellm_auditlog.create.assert_awaited_once() + written = fake.db.litellm_auditlog.create.call_args.kwargs["data"] + assert written["object_id"] == "general_settings" + assert written["action"] == "deleted" + before = json.loads(written["before_value"]) + after = json.loads(written["updated_values"]) + assert before["max_parallel_requests"] == 5 + assert "max_parallel_requests" not in after + + +def test_update_config_audits_every_written_section(_update_config_setup, monkeypatch): + """/config/update must emit one audit row per section it writes, so each + of the four call sites (general_settings, environment_variables, + litellm_settings, router_settings) is mutation-protected. litellm_settings + is the row that holds default_internal_user_params ("default user settings").""" + import litellm.proxy.proxy_server as proxy_server_module + + client, prisma, restore = _update_config_setup( + initial_rows={"litellm_settings": {"drop_params": True}} + ) + audit_create = AsyncMock() + prisma.db.litellm_auditlog.create = audit_create + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + try: + resp = client.post( + "/config/update", + json={ + "general_settings": {"store_prompts_in_spend_logs": True}, + "environment_variables": {"FOO": "bar"}, + "litellm_settings": { + "default_internal_user_params": {"max_budget": 10} + }, + "router_settings": {"routing_strategy": "latency-based-routing"}, + }, + ) + assert resp.status_code == 200, resp.text + + audited = { + call.kwargs["data"]["object_id"]: call.kwargs["data"]["action"] + for call in audit_create.await_args_list + } + assert audited == { + "general_settings": "updated", + "environment_variables": "updated", + "litellm_settings": "updated", + "router_settings": "updated", + } + for call in audit_create.await_args_list: + assert call.kwargs["data"]["table_name"] == "LiteLLM_Config" + assert call.kwargs["data"]["changed_by"] == "test_admin" + + ls_call = next( + c + for c in audit_create.await_args_list + if c.kwargs["data"]["object_id"] == "litellm_settings" + ) + after = json.loads(ls_call.kwargs["data"]["updated_values"]) + assert after["default_internal_user_params"] == {"max_budget": 10} + finally: + restore() + + +def test_delete_callback_audits_litellm_settings_deletion( + _update_config_setup, monkeypatch +): + """/config/callback/delete must emit a deleted audit row for litellm_settings + capturing the success_callback list before and after removal.""" + import litellm.proxy.proxy_server as proxy_server_module + + client, prisma, restore = _update_config_setup() + audit_create = AsyncMock() + prisma.db.litellm_auditlog.create = audit_create + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + + from litellm.proxy.proxy_server import proxy_config as real_proxy_config + + monkeypatch.setattr( + real_proxy_config, + "get_config", + AsyncMock( + return_value={ + "litellm_settings": {"success_callback": ["langfuse", "datadog"]} + } + ), + ) + monkeypatch.setattr( + real_proxy_config, "save_config", AsyncMock(return_value=None) + ) + try: + resp = client.post( + "/config/callback/delete", json={"callback_name": "datadog"} + ) + assert resp.status_code == 200, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + assert written["object_id"] == "litellm_settings" + assert written["action"] == "deleted" + before = json.loads(written["before_value"]) + after = json.loads(written["updated_values"]) + assert before["success_callback"] == ["langfuse", "datadog"] + assert after["success_callback"] == ["langfuse"] + finally: + restore() + + +def test_delete_callback_audits_before_reload_failure(_update_config_setup, monkeypatch): + import litellm.proxy.proxy_server as proxy_server_module + + client, prisma, restore = _update_config_setup() + audit_create = AsyncMock() + prisma.db.litellm_auditlog.create = audit_create + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + + from litellm.proxy.proxy_server import proxy_config as real_proxy_config + + monkeypatch.setattr( + real_proxy_config, + "get_config", + AsyncMock( + return_value={ + "litellm_settings": {"success_callback": ["langfuse", "datadog"]} + } + ), + ) + monkeypatch.setattr( + real_proxy_config, "save_config", AsyncMock(return_value=None) + ) + monkeypatch.setattr( + real_proxy_config, + "add_deployment", + AsyncMock(side_effect=RuntimeError("reload failed")), + ) + try: + resp = client.post( + "/config/callback/delete", json={"callback_name": "datadog"} + ) + assert resp.status_code == 500, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + assert written["object_id"] == "litellm_settings" + assert written["action"] == "deleted" + finally: + restore() + + +def test_update_config_redacts_all_environment_variable_values( + _update_config_setup, monkeypatch +): + """environment_variables hold credentials under arbitrary uppercase keys + (DATABASE_URL) that key-name secret matching misses, so every value in the + section must be redacted before the audit row is written; a plaintext + secret must never reach LiteLLM_AuditLog.""" + import litellm.proxy.proxy_server as proxy_server_module + + # DATABASE_URL is the bug class: an uppercase env key that key-name secret + # matching does NOT flag, so only whole-section value redaction protects it. + client, prisma, restore = _update_config_setup( + initial_rows={ + "environment_variables": { + "DATABASE_URL": "enc:postgresql://OLDsecret@old.host:5432/db" + } + } + ) + audit_create = AsyncMock() + prisma.db.litellm_auditlog.create = audit_create + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + try: + resp = client.post( + "/config/update", + json={ + "environment_variables": { + "DATABASE_URL": "postgresql://u:p@db.internal:5432/litellm", + "LOG_LEVEL": "debug", + } + }, + ) + assert resp.status_code == 200, resp.text + + env_call = next( + c + for c in audit_create.await_args_list + if c.kwargs["data"]["object_id"] == "environment_variables" + ) + data = env_call.kwargs["data"] + + # the pre-existing secret must be redacted in the before snapshot + before = json.loads(data["before_value"]) + assert before == {"DATABASE_URL": "REDACTED"} + assert "OLDsecret" not in data["before_value"] + assert "old.host" not in data["before_value"] + + # the newly-written values must be redacted in the after snapshot + after = json.loads(data["updated_values"]) + assert after == {"DATABASE_URL": "REDACTED", "LOG_LEVEL": "REDACTED"} + assert "postgresql://" not in data["updated_values"] + assert "db.internal" not in data["updated_values"] + finally: + restore() + + +class _EnvBuiltRedisCache(RedisCache): + """RedisCache stand-in that records its constructor kwargs and never + opens a network connection, so tests can assert which connection params + the proxy used to build its coordination Redis.""" + + def __init__(self, **kwargs): + self.init_kwargs = kwargs + + +def _run_init_cache_with_backend(cache_backend, redis_env_kwargs): + """Run ProxyConfig._init_cache with a stubbed response-cache backend and a + controlled REDIS_* environment, returning (redis_usage_cache, + spend_counter redis, config-cache redis) as observed after the call.""" + mock_litellm_cache = MagicMock() + mock_litellm_cache.cache = cache_backend + fresh_spend_cache = DualCache() + fresh_config_cache = types.SimpleNamespace(redis_cache=None) + + with ( + patch.object(proxy_server_module, "redis_usage_cache", None), + patch.object(proxy_server_module, "spend_counter_cache", fresh_spend_cache), + patch.object(proxy_server_module, "user_api_key_cache", DualCache()), + patch.object(proxy_server_module, "llm_router", None), + patch.object(proxy_server_module, "litellm_config_cache", fresh_config_cache), + patch.object(proxy_server_module, "RedisCache", _EnvBuiltRedisCache), + patch( + "litellm._redis._redis_kwargs_from_environment", + return_value=redis_env_kwargs, + ), + patch("litellm.Cache", return_value=mock_litellm_cache), + ): + litellm.cache = None + resolved = proxy_server_module.ProxyConfig()._init_cache(cache_params={"type": "qdrant-semantic"}) + return ( + resolved, + fresh_spend_cache.redis_cache, + fresh_config_cache.redis_cache, + ) + + +def test_init_cache_non_redis_backend_builds_usage_redis_from_environment(): + """A semantic (non-Redis-KV) response cache must not disable the proxy's + coordination Redis: when REDIS_* env vars provide a connection, + _init_cache builds a standalone usage cache so cross-pod rate limits, + spend tracking, and the pod lock manager stay Redis-backed.""" + usage_cache, spend_redis, config_redis = _run_init_cache_with_backend( + cache_backend=object(), + redis_env_kwargs={"host": "coordination-redis", "port": "6379"}, + ) + + assert isinstance(usage_cache, _EnvBuiltRedisCache) + assert usage_cache.init_kwargs["host"] == "coordination-redis" + assert spend_redis is usage_cache + assert config_redis is usage_cache + + +def test_init_cache_non_redis_backend_without_redis_env_stays_in_memory(): + """Without any REDIS_* connection info, a non-Redis response cache must + leave the coordination Redis unset instead of building a broken client.""" + usage_cache, spend_redis, config_redis = _run_init_cache_with_backend( + cache_backend=object(), + redis_env_kwargs={}, + ) + + assert usage_cache is None + assert spend_redis is None + assert config_redis is None + + +def test_init_cache_redis_backend_reuses_cache_backend_over_environment(): + """When the response cache itself is a plain Redis KV cache, it must be + reused as the coordination Redis; the REDIS_* environment fallback must + not construct a second client.""" + redis_backend = _EnvBuiltRedisCache(host="cache-params-host") + usage_cache, spend_redis, _ = _run_init_cache_with_backend( + cache_backend=redis_backend, + redis_env_kwargs={"host": "env-host"}, + ) + + assert usage_cache is redis_backend + assert usage_cache.init_kwargs["host"] == "cache-params-host" + assert spend_redis is redis_backend + + +class _EnvBuiltClusterCache(RedisClusterCache): + """RedisClusterCache stand-in that records constructor kwargs and never + opens a network connection.""" + + def __init__(self, **kwargs): + self.init_kwargs = kwargs + + +def _run_init_coordination_redis(config, env=None): + """Run ProxyConfig._init_coordination_redis against a stubbed module state, + returning (redis_usage_cache, spend_counter redis, config-cache redis).""" + fresh_spend_cache = DualCache() + fresh_config_cache = types.SimpleNamespace(redis_cache=None) + + with ( + patch.object(proxy_server_module, "redis_usage_cache", None), + patch.object(proxy_server_module, "spend_counter_cache", fresh_spend_cache), + patch.object(proxy_server_module, "user_api_key_cache", DualCache()), + patch.object(proxy_server_module, "litellm_config_cache", fresh_config_cache), + patch.object(proxy_server_module, "RedisCache", _EnvBuiltRedisCache), + patch.object(proxy_server_module, "RedisClusterCache", _EnvBuiltClusterCache), + mock.patch.dict(os.environ, env or {}, clear=False), + ): + built = proxy_server_module.ProxyConfig()._init_coordination_redis(config=config) + return ( + built, + fresh_spend_cache.redis_cache, + fresh_config_cache.redis_cache, + ) + + +def test_init_coordination_redis_explicit_block_builds_standalone_client(): + """general_settings.coordination_redis must build the coordination Redis + even when no response cache is configured at all, and attach it to the + spend counter and config caches.""" + usage_cache, spend_redis, config_redis = _run_init_coordination_redis( + config={"general_settings": {"coordination_redis": {"host": "coord-host", "port": 6380}}}, + ) + + assert isinstance(usage_cache, _EnvBuiltRedisCache) + assert usage_cache.init_kwargs["host"] == "coord-host" + assert usage_cache.init_kwargs["port"] == 6380 + assert spend_redis is usage_cache + assert config_redis is usage_cache + + +def test_init_coordination_redis_resolves_os_environ_references(): + """os.environ/ values inside the coordination_redis block must be resolved + the same way cache_params values are.""" + usage_cache, _, _ = _run_init_coordination_redis( + config={"general_settings": {"coordination_redis": {"host": "os.environ/COORD_REDIS_HOST"}}}, + env={"COORD_REDIS_HOST": "resolved-host"}, + ) + + assert usage_cache.init_kwargs["host"] == "resolved-host" + + +def test_init_coordination_redis_startup_nodes_builds_cluster_client(): + """A coordination_redis block with startup_nodes must construct a cluster + client, so cluster-aware consumers (v3 rate limiter) take the cluster path.""" + usage_cache, _, _ = _run_init_coordination_redis( + config={ + "general_settings": { + "coordination_redis": {"startup_nodes": [{"host": "node-1", "port": 7000}]} + } + }, + ) + + assert isinstance(usage_cache, _EnvBuiltClusterCache) + assert usage_cache.init_kwargs["startup_nodes"] == [{"host": "node-1", "port": 7000}] + + +def test_init_coordination_redis_without_connection_target_raises(): + """A coordination_redis block with no host, url, startup_nodes, or + sentinel_nodes is a config error and must fail startup loudly instead of + silently running without coordination.""" + with pytest.raises(ValueError, match="connection target"): + _run_init_coordination_redis( + config={"general_settings": {"coordination_redis": {"ssl": True}}}, + ) + + +def test_init_coordination_redis_non_mapping_block_raises(): + """A scalar coordination_redis value is a config error.""" + with pytest.raises(ValueError, match="mapping"): + _run_init_coordination_redis( + config={"general_settings": {"coordination_redis": "redis://host:6379"}}, + ) + + +def test_init_coordination_redis_absent_leaves_usage_cache_unset(): + """Without the block, nothing changes: the coordination Redis stays unset + for the downstream borrow / env fallback logic to decide.""" + usage_cache, spend_redis, _ = _run_init_coordination_redis( + config={"general_settings": {}}, + ) + + assert usage_cache is None + assert spend_redis is None + + +def test_explicit_coordination_redis_takes_precedence_over_cache_backend(): + """When both an explicit coordination_redis block and a plain-Redis + response cache are configured, the explicit block must win; the cache + backend must not overwrite it.""" + fresh_spend_cache = DualCache() + fresh_config_cache = types.SimpleNamespace(redis_cache=None) + cache_backend = _EnvBuiltRedisCache(host="cache-backend-host") + mock_litellm_cache = MagicMock() + mock_litellm_cache.cache = cache_backend + + with ( + patch.object(proxy_server_module, "redis_usage_cache", None), + patch.object(proxy_server_module, "spend_counter_cache", fresh_spend_cache), + patch.object(proxy_server_module, "user_api_key_cache", DualCache()), + patch.object(proxy_server_module, "llm_router", None), + patch.object(proxy_server_module, "litellm_config_cache", fresh_config_cache), + patch.object(proxy_server_module, "RedisCache", _EnvBuiltRedisCache), + patch.object(proxy_server_module, "RedisClusterCache", _EnvBuiltClusterCache), + patch("litellm.Cache", return_value=mock_litellm_cache), + ): + litellm.cache = None + proxy_config = proxy_server_module.ProxyConfig() + built = proxy_config._init_coordination_redis( + config={"general_settings": {"coordination_redis": {"host": "explicit-coord-host"}}} + ) + assert built is not None + proxy_server_module.redis_usage_cache = built + usage_cache = proxy_config._init_cache(cache_params={"type": "redis"}) + + assert isinstance(usage_cache, _EnvBuiltRedisCache) + assert usage_cache is not cache_backend + assert usage_cache.init_kwargs["host"] == "explicit-coord-host" + assert fresh_spend_cache.redis_cache is usage_cache + + +def test_env_fallback_builds_cluster_client_from_cluster_nodes_env(): + """A deployment whose only Redis env is REDIS_CLUSTER_NODES must still get + a coordination Redis from the env fallback, and it must be a cluster + client so cluster-aware consumers take the cluster path.""" + nodes = '[{"host": "cnode-1", "port": 7000}]' + with ( + patch.object(proxy_server_module, "RedisCache", _EnvBuiltRedisCache), + patch.object(proxy_server_module, "RedisClusterCache", _EnvBuiltClusterCache), + patch("litellm._redis._redis_kwargs_from_environment", return_value={}), + mock.patch.dict(os.environ, {"REDIS_CLUSTER_NODES": nodes}, clear=False), + ): + result = proxy_server_module._build_redis_usage_cache_from_environment() + + assert isinstance(result, _EnvBuiltClusterCache) + assert result.init_kwargs["startup_nodes"] == [{"host": "cnode-1", "port": 7000}] + + +def test_env_fallback_builds_client_from_sentinel_nodes_env(): + """A sentinel-only environment (REDIS_SENTINEL_NODES, no host or url) must + also produce a coordination Redis from the env fallback.""" + with ( + patch.object(proxy_server_module, "RedisCache", _EnvBuiltRedisCache), + patch.object(proxy_server_module, "RedisClusterCache", _EnvBuiltClusterCache), + patch("litellm._redis._redis_kwargs_from_environment", return_value={}), + mock.patch.dict(os.environ, {"REDIS_SENTINEL_NODES": '[["s1", 26379]]'}, clear=False), + ): + result = proxy_server_module._build_redis_usage_cache_from_environment() + + assert isinstance(result, _EnvBuiltRedisCache) + + +@pytest.mark.asyncio +async def test_startup_applies_coordination_redis_saved_in_database(): + """A coordination_redis block saved from the admin UI lives only in the + database, so startup must read it and build the coordination Redis from it. + Without this the save endpoint's "restart to apply" promise is false and the + proxy silently coordinates in per-pod memory.""" + fresh_spend_cache = DualCache() + fresh_config_cache = types.SimpleNamespace(redis_cache=None) + + with ( + patch.object(proxy_server_module, "spend_counter_cache", fresh_spend_cache), + patch.object(proxy_server_module, "user_api_key_cache", DualCache()), + patch.object(proxy_server_module, "litellm_config_cache", fresh_config_cache), + patch.object(proxy_server_module, "RedisCache", _EnvBuiltRedisCache), + patch.object(proxy_server_module, "RedisClusterCache", _EnvBuiltClusterCache), + patch.object( + proxy_server_module, + "get_persisted_coordination_redis_settings", + AsyncMock(return_value={"host": "db-host", "port": 6381}), + ), + ): + result = await proxy_server_module.ProxyStartupEvent._init_coordination_redis_from_db( + litellm_settings={}, + llm_router=None, + ) + + assert isinstance(result, _EnvBuiltRedisCache) + assert result.init_kwargs["host"] == "db-host" + assert fresh_spend_cache.redis_cache is result + assert fresh_config_cache.redis_cache is result + + +@pytest.mark.asyncio +async def test_startup_ignores_database_coordination_redis_without_connection_target(): + """A persisted block with no host/url/cluster/sentinel must be ignored rather + than crashing startup or building a client that cannot connect.""" + with ( + patch.object(proxy_server_module, "spend_counter_cache", DualCache()), + patch.object(proxy_server_module, "litellm_config_cache", types.SimpleNamespace(redis_cache=None)), + patch.object(proxy_server_module, "RedisCache", _EnvBuiltRedisCache), + patch.object( + proxy_server_module, + "get_persisted_coordination_redis_settings", + AsyncMock(return_value={"ssl": True}), + ), + ): + result = await proxy_server_module.ProxyStartupEvent._init_coordination_redis_from_db( + litellm_settings={}, + llm_router=None, + ) + + assert result is None + + +@pytest.mark.asyncio +async def test_startup_survives_database_read_failure_for_coordination_redis(): + """A config-row read failure must not block proxy startup.""" + with ( + patch.object( + proxy_server_module, + "get_persisted_coordination_redis_settings", + AsyncMock(side_effect=RuntimeError("db unreachable")), + ), + ): + result = await proxy_server_module.ProxyStartupEvent._init_coordination_redis_from_db( + litellm_settings={}, + llm_router=None, + ) + + assert result is None diff --git a/tests/test_litellm/proxy/test_route_llm_request.py b/tests/test_litellm/proxy/test_route_llm_request.py index 47dc6e6d37d..74a0efba43d 100644 --- a/tests/test_litellm/proxy/test_route_llm_request.py +++ b/tests/test_litellm/proxy/test_route_llm_request.py @@ -3,9 +3,7 @@ import sys import pytest -sys.path.insert( - 0, os.path.abspath("../../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../../..")) # Adds the parent directory to the system path from unittest.mock import MagicMock @@ -169,9 +167,7 @@ async def test_route_request_no_model_required_with_router_settings_and_no_route "messages": [{"role": "user", "content": "what llm are you"}], } - with patch.object( - litellm, "acompletion", return_value="fake_response" - ) as mock_completion: + with patch.object(litellm, "acompletion", return_value="fake_response") as mock_completion: await route_request(data, None, "gpt-3.5-turbo", "acompletion") mock_completion.assert_called_once_with(**data) @@ -209,9 +205,7 @@ async def test_route_request_with_router_settings_override(): assert call_kwargs["fallbacks"] == [{"gpt-3.5-turbo": ["gpt-4"]}] assert call_kwargs["num_retries"] == 5 assert call_kwargs["timeout"] == 30 - assert call_kwargs["model_group_retry_policy"] == { - "gpt-3.5-turbo": {"RateLimitErrorRetries": 3} - } + assert call_kwargs["model_group_retry_policy"] == {"gpt-3.5-turbo": {"RateLimitErrorRetries": 3}} # Verify unsupported settings were NOT merged assert "routing_strategy" not in call_kwargs assert "model_group_alias" not in call_kwargs @@ -292,9 +286,7 @@ def test_mock_testing_kwarg_names_matches_dataclass(): from litellm.proxy.route_llm_request import _MOCK_TESTING_KWARG_NAMES from litellm.types.router import MockRouterTestingParams - assert set(_MOCK_TESTING_KWARG_NAMES) == { - f.name for f in fields(MockRouterTestingParams) - } + assert set(_MOCK_TESTING_KWARG_NAMES) == {f.name for f in fields(MockRouterTestingParams)} @pytest.mark.asyncio @@ -329,9 +321,7 @@ async def test_route_request_strips_mock_testing_flags(mock_flag): assert mock_flag not in data -@pytest.mark.parametrize( - "route_type", ["agenerate_content", "agenerate_content_stream"] -) +@pytest.mark.parametrize("route_type", ["agenerate_content", "agenerate_content_stream"]) @pytest.mark.asyncio async def test_route_request_maps_generation_config_for_google_routes(route_type): """For Google generate_content routes, route_request must rename @@ -359,9 +349,7 @@ async def test_route_request_maps_generation_config_for_google_routes(route_type assert call_kwargs["config"]["imageConfig"]["imageSize"] == "4K" -@pytest.mark.parametrize( - "route_type", ["agenerate_content", "agenerate_content_stream"] -) +@pytest.mark.parametrize("route_type", ["agenerate_content", "agenerate_content_stream"]) @pytest.mark.asyncio async def test_route_request_preserves_existing_config_for_google_routes(route_type): """If the caller already supplies `config`, route_request must not @@ -379,3 +367,261 @@ async def test_route_request_preserves_existing_config_for_google_routes(route_t call_kwargs = getattr(llm_router, route_type).call_args[1] assert call_kwargs["config"] == {"existing": True} + + +async def _invoke_realtime_route( + data: dict, + llm_router, + route_type: str = "acreate_realtime_client_secret", +): + llm_call = await route_request(data, llm_router, None, route_type) + return await llm_call + + +@pytest.fixture +def openai_realtime_credential(): + import litellm + from litellm.types.utils import CredentialItem + + litellm.credential_list = [ + CredentialItem( + credential_name="openai-realtime-cred", + credential_info={"custom_llm_provider": "openai"}, + credential_values={"api_key": "resolved-credential-key"}, + ) + ] + yield + litellm.credential_list = [] + + +@pytest.mark.asyncio +async def test_route_request_realtime_wildcard_model_resolves_credentials( + monkeypatch, +): + """ + POST /realtime/client_secrets with a request model like openai/gpt-realtime + must match an openai/* deployment and forward its api_key upstream. + """ + import httpx + import litellm + from unittest.mock import AsyncMock, patch + + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + + router = litellm.Router( + model_list=[ + { + "model_name": "openai/*", + "litellm_params": { + "model": "openai/*", + "api_key": "wildcard-realtime-key", + }, + } + ] + ) + with patch( + "litellm.realtime_api.main.base_llm_http_handler.async_realtime_client_secret_handler", + new_callable=AsyncMock, + ) as mock_handler: + mock_handler.return_value = httpx.Response(200, json={"value": "ephemeral"}) + await _invoke_realtime_route( + {"model": "openai/gpt-realtime"}, + router, + ) + + assert mock_handler.call_args.kwargs["api_key"] == "wildcard-realtime-key" + + +@pytest.mark.asyncio +async def test_route_request_realtime_team_scoped_model_resolves_credentials( + monkeypatch, +): + """ + Team-scoped deployments (team_public_model_name) must be selected when + user_api_key_team_id is present, same as /chat/completions. + """ + import httpx + import litellm + from unittest.mock import AsyncMock, patch + + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + + router = litellm.Router( + model_list=[ + { + "model_name": "internal-realtime", + "litellm_params": { + "model": "openai/gpt-realtime", + "api_key": "team-realtime-key", + }, + "model_info": { + "team_id": "team-a", + "team_public_model_name": "team-realtime", + }, + } + ] + ) + with patch( + "litellm.realtime_api.main.base_llm_http_handler.async_realtime_client_secret_handler", + new_callable=AsyncMock, + ) as mock_handler: + mock_handler.return_value = httpx.Response(200, json={"value": "ephemeral"}) + await _invoke_realtime_route( + { + "model": "team-realtime", + "metadata": {"user_api_key_team_id": "team-a"}, + }, + router, + ) + + assert mock_handler.call_args.kwargs["api_key"] == "team-realtime-key" + + +@pytest.mark.asyncio +async def test_route_request_realtime_litellm_credential_name_resolves_api_key( + openai_realtime_credential, + monkeypatch, +): + """ + litellm_credential_name on a wildcard deployment must resolve to the stored + api_key when routing acreate_realtime_client_secret through the router. + """ + import httpx + import litellm + from unittest.mock import AsyncMock, patch + + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + + router = litellm.Router( + model_list=[ + { + "model_name": "openai/*", + "litellm_params": { + "model": "openai/*", + "litellm_credential_name": "openai-realtime-cred", + }, + } + ] + ) + with patch( + "litellm.realtime_api.main.base_llm_http_handler.async_realtime_client_secret_handler", + new_callable=AsyncMock, + ) as mock_handler: + mock_handler.return_value = httpx.Response(200, json={"value": "ephemeral"}) + await _invoke_realtime_route({"model": "openai/gpt-realtime"}, router) + + assert mock_handler.call_args.kwargs["api_key"] == "resolved-credential-key" + + +@pytest.mark.asyncio +async def test_route_request_realtime_unresolvable_model_raises_not_found( + monkeypatch, +): + """ + An unknown model must not silently fall through to litellm with an empty + OPENAI_API_KEY env var. + """ + import litellm + from unittest.mock import AsyncMock, patch + + from litellm.proxy.route_llm_request import ProxyModelNotFoundError + + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + + router = litellm.Router( + model_list=[ + { + "model_name": "other-model", + "litellm_params": {"model": "openai/gpt-4", "api_key": "other-key"}, + } + ] + ) + with patch( + "litellm.realtime_api.main.base_llm_http_handler.async_realtime_client_secret_handler", + new_callable=AsyncMock, + ) as mock_handler: + with pytest.raises(ProxyModelNotFoundError): + await _invoke_realtime_route({"model": "nonexistent-realtime-model"}, router) + + mock_handler.assert_not_called() + + +@pytest.mark.asyncio +async def test_route_request_realtime_calls_resolves_api_base(monkeypatch): + """ + /realtime/calls must resolve the deployment's api_base through the router so a + non-default (self-hosted / proxied) OpenAI endpoint is honored, instead of + defaulting to https://api.openai.com. + """ + import httpx + import litellm + from unittest.mock import AsyncMock, patch + + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + monkeypatch.delenv("OPENAI_API_BASE", raising=False) + + router = litellm.Router( + model_list=[ + { + "model_name": "my-realtime", + "litellm_params": { + "model": "openai/gpt-realtime", + "api_key": "calls-key", + "api_base": "https://custom-realtime.example.com/v1", + }, + } + ] + ) + with patch( + "litellm.realtime_api.main.base_llm_http_handler.async_realtime_calls_handler", + new_callable=AsyncMock, + ) as mock_handler: + mock_handler.return_value = httpx.Response(200, content=b"v=0\r\n") + await _invoke_realtime_route( + { + "model": "my-realtime", + "openai_ephemeral_key": "ek_test", + "sdp_body": b"v=0\r\n", + }, + router, + route_type="arealtime_calls", + ) + + assert mock_handler.call_args.kwargs["api_base"] == "https://custom-realtime.example.com/v1" + + +@pytest.mark.asyncio +async def test_route_request_realtime_transcription_session_resolves_credentials(monkeypatch): + """ + /realtime/transcription_sessions must resolve credentials through the router + (wildcard deployment) rather than falling back to an empty OPENAI_API_KEY. + """ + import httpx + import litellm + from unittest.mock import AsyncMock, patch + + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + + router = litellm.Router( + model_list=[ + { + "model_name": "openai/*", + "litellm_params": { + "model": "openai/*", + "api_key": "transcription-key", + }, + } + ] + ) + with patch( + "litellm.realtime_api.main.base_llm_http_handler.async_realtime_transcription_session_handler", + new_callable=AsyncMock, + ) as mock_handler: + mock_handler.return_value = httpx.Response(200, json={"client_secret": {"value": "ephemeral"}}) + await _invoke_realtime_route( + {"model": "openai/gpt-realtime"}, + router, + route_type="acreate_realtime_transcription_session", + ) + + assert mock_handler.call_args.kwargs["api_key"] == "transcription-key" diff --git a/tests/test_litellm/proxy/test_tools_allowlist_enforcement.py b/tests/test_litellm/proxy/test_tools_allowlist_enforcement.py index 8196cc97f50..31f5fbf606b 100644 --- a/tests/test_litellm/proxy/test_tools_allowlist_enforcement.py +++ b/tests/test_litellm/proxy/test_tools_allowlist_enforcement.py @@ -70,6 +70,22 @@ class TestExtractRequestToolNames: } assert extract_request_tool_names("/v1/responses", data) == ["dmcp"] + def test_openai_responses_custom_tools(self): + """Custom tools become callable function tools on the Chat Completions + bridge, so their names must be extracted for allowlist enforcement; + otherwise a restricted key could invoke a disallowed tool by declaring + it with type "custom" (VERIA finding on PR #32258).""" + data = { + "tools": [ + {"type": "custom", "name": "apply_patch", "description": "x"}, + {"type": "function", "name": "get_current_weather"}, + ] + } + assert extract_request_tool_names("/v1/responses", data) == [ + "apply_patch", + "get_current_weather", + ] + def test_anthropic_tools(self): data = {"tools": [{"name": "get_weather"}, {"name": "run_sql"}]} assert extract_request_tool_names("/v1/messages", data) == [ @@ -143,6 +159,20 @@ class TestCheckToolsAllowlist: assert exc_info.value.type == ProxyErrorTypes.tool_access_denied assert "get_weather" in str(exc_info.value.message) + @pytest.mark.asyncio + async def test_disallowed_custom_tool_raises_on_responses_route(self): + token = _token(metadata={"allowed_tools": ["other_tool"]}) + body = {"tools": [{"type": "custom", "name": "restricted_tool"}]} + with pytest.raises(ProxyException) as exc_info: + await check_tools_allowlist( + request_body=body, + valid_token=token, + team_object=None, + route="/v1/responses", + ) + assert exc_info.value.type == ProxyErrorTypes.tool_access_denied + assert "restricted_tool" in str(exc_info.value.message) + @pytest.mark.asyncio async def test_team_allowlist_used_when_key_empty(self): token = _token( diff --git a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py index ae217aca16e..69845ec59c2 100644 --- a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py +++ b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py @@ -396,6 +396,7 @@ class TestProxySettingEndpoints: # Mock the prisma client mock_prisma = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() mock_prisma.db.litellm_config = MagicMock() mock_prisma.db.litellm_config.find_unique = AsyncMock(return_value=None) @@ -466,6 +467,62 @@ class TestProxySettingEndpoints: create_sso_settings = json.loads(create_data["sso_settings"]) assert create_sso_settings["google_client_id"] == "new_google_client_id" + def test_update_sso_settings_audits_when_env_cleanup_fails( + self, mock_proxy_config, mock_auth, monkeypatch + ): + import json + from unittest.mock import AsyncMock, MagicMock + + monkeypatch.setenv("LITELLM_SALT_KEY", "test_salt_key") + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + + mock_prisma = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) + mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() + mock_prisma.db.litellm_config = MagicMock() + mock_prisma.db.litellm_config.find_unique = AsyncMock( + side_effect=ValueError("cleanup failed") + ) + mock_prisma.db.litellm_config.update = AsyncMock() + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma) + + from litellm.proxy.proxy_server import proxy_config + + monkeypatch.setattr( + proxy_config, + "_encrypt_env_variables", + lambda environment_variables: environment_variables, + ) + + create_config_audit_log = AsyncMock() + monkeypatch.setattr( + "litellm.proxy.proxy_server.create_config_audit_log", + create_config_audit_log, + ) + + response = client.patch( + "/update/sso_settings", + json={"google_client_id": "new_google_client_id"}, + ) + + assert response.status_code == 500 + assert mock_prisma.db.litellm_ssoconfig.upsert.called + create_config_audit_log.assert_awaited_once() + audit_log_kwargs = create_config_audit_log.await_args.kwargs + assert audit_log_kwargs["param_name"] == "sso_config" + assert ( + audit_log_kwargs["after_value"]["google_client_id"] + == "new_google_client_id" + ) + assert ( + json.loads( + mock_prisma.db.litellm_ssoconfig.upsert.call_args.kwargs["data"][ + "create" + ]["sso_settings"] + )["google_client_id"] + == "new_google_client_id" + ) + def test_update_sso_settings_with_null_values_clears_env_vars( self, mock_proxy_config, mock_auth, monkeypatch ): @@ -478,6 +535,7 @@ class TestProxySettingEndpoints: # Mock the prisma client mock_prisma = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() mock_prisma.db.litellm_config = MagicMock() @@ -557,6 +615,7 @@ class TestProxySettingEndpoints: # Mock the prisma client mock_prisma = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() mock_prisma.db.litellm_config = MagicMock() env_var_entry = MagicMock() @@ -627,6 +686,7 @@ class TestProxySettingEndpoints: # Mock the prisma client mock_prisma = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() mock_prisma.db.litellm_config = MagicMock() @@ -704,6 +764,7 @@ class TestProxySettingEndpoints: # Mock the prisma client mock_prisma = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() mock_prisma.db.litellm_config = MagicMock() mock_prisma.db.litellm_config.find_unique = AsyncMock(return_value=None) @@ -1350,6 +1411,7 @@ class TestProxySettingEndpoints: # Mock the prisma client mock_prisma = MagicMock() upsert_mock = AsyncMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = upsert_mock mock_prisma.db.litellm_config = MagicMock() mock_prisma.db.litellm_config.find_unique = AsyncMock(return_value=None) @@ -1429,6 +1491,7 @@ class TestProxySettingEndpoints: mock_prisma = MagicMock() mock_prisma.db = MagicMock() mock_prisma.db.litellm_ssoconfig = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() env_var_entry = MagicMock() @@ -1480,6 +1543,7 @@ class TestProxySettingEndpoints: mock_prisma = MagicMock() mock_prisma.db = MagicMock() mock_prisma.db.litellm_ssoconfig = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() env_var_entry = MagicMock() @@ -1651,6 +1715,7 @@ class TestProxySettingEndpoints: # Mock the prisma client mock_prisma = MagicMock() + mock_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) mock_prisma.db.litellm_ssoconfig.upsert = AsyncMock() mock_prisma.db.litellm_config = MagicMock() mock_prisma.db.litellm_config.find_unique = AsyncMock(return_value=None) @@ -1869,3 +1934,462 @@ class TestProxySettingEndpoints: assert "field_schema" in data assert "properties" in data["field_schema"] assert "role_mappings" in data["field_schema"]["properties"] + + +def test_update_internal_user_settings_writes_audit_log(mock_proxy_config, monkeypatch): + """Regression for the reported scenario: an admin changes Default User + Settings from the dashboard, which issues PATCH /update/internal_user_settings + (NOT /config/update). An audit row must record who changed it and what + changed.""" + from unittest.mock import AsyncMock, MagicMock + + import litellm + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + audit_create = AsyncMock() + fake_prisma = MagicMock() + fake_prisma.db.litellm_auditlog.create = audit_create + + monkeypatch.setattr(proxy_server_module, "prisma_client", fake_prisma) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + monkeypatch.setattr(litellm, "default_internal_user_params", {}) + + async def _admin_auth(): + return UserAPIKeyAuth( + user_id="audit-admin", + api_key="hashed-admin-key", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + app.dependency_overrides[user_api_key_auth] = _admin_auth + try: + resp = client.patch( + "/update/internal_user_settings", + json={"max_budget": 999.0, "models": ["gpt-4"]}, + ) + assert resp.status_code == 200, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + assert written["object_id"] == "default_internal_user_params" + assert written["action"] == "updated" + assert written["table_name"] == "LiteLLM_Config" + assert written["changed_by"] == "audit-admin" + assert written["changed_by_api_key"] == "hashed-admin-key" + + before = json.loads(written["before_value"]) + after = json.loads(written["updated_values"]) + assert before["max_budget"] == 100.0 + assert after["max_budget"] == 999.0 + finally: + app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_update_internal_user_settings_returns_200_when_audit_write_raises( + mock_proxy_config, monkeypatch +): + """The settings change is already committed by save_config, so an + audit-log failure must never surface as a 500. Scheduling via + asyncio.create_task keeps the audit call off the request path; this + test asserts that contract by making the audit helper raise.""" + import litellm + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + monkeypatch.setattr(litellm, "default_internal_user_params", {}) + + async def _raise(**_kwargs): + raise RuntimeError("audit prisma blip") + + monkeypatch.setattr(proxy_server_module, "create_config_audit_log", _raise) + + async def _admin_auth(): + return UserAPIKeyAuth( + user_id="audit-admin", + api_key="hashed-admin-key", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + app.dependency_overrides[user_api_key_auth] = _admin_auth + try: + resp = client.patch( + "/update/internal_user_settings", json={"max_budget": 42.0} + ) + assert resp.status_code == 200, resp.text + assert resp.json()["status"] == "success" + finally: + app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_update_sso_settings_writes_redacted_audit_log(mock_proxy_config, monkeypatch): + """Updating SSO settings must write an audit row to the SSO config table + with the client secret redacted.""" + from unittest.mock import AsyncMock, MagicMock + + import litellm + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + audit_create = AsyncMock() + fake_prisma = MagicMock() + fake_prisma.db.litellm_auditlog.create = audit_create + fake_prisma.db.litellm_ssoconfig.upsert = AsyncMock() + # No prior SSO row, so before_value resolves to None. + fake_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=None) + fake_prisma.db.litellm_config.find_unique = AsyncMock(return_value=None) + + monkeypatch.setattr(proxy_server_module, "prisma_client", fake_prisma) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + monkeypatch.setattr( + proxy_server_module.proxy_config, + "_encrypt_env_variables", + lambda environment_variables: environment_variables, + ) + + async def _admin_auth(): + return UserAPIKeyAuth( + user_id="audit-admin", + api_key="hashed-admin-key", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + app.dependency_overrides[user_api_key_auth] = _admin_auth + try: + resp = client.patch( + "/update/sso_settings", + json={ + "google_client_id": "client-id-123", + "google_client_secret": "super-secret-xyz", + }, + ) + assert resp.status_code == 200, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + assert written["object_id"] == "sso_config" + assert written["table_name"] == "LiteLLM_SSOConfig" + assert written["changed_by"] == "audit-admin" + + after = json.loads(written["updated_values"]) + assert after["google_client_id"] == "client-id-123" + assert after["google_client_secret"] == "REDACTED" + assert "super-secret-xyz" not in written["updated_values"] + finally: + app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_update_sso_settings_audit_captures_redacted_before_snapshot( + mock_proxy_config, monkeypatch +): + """An auditor reviewing an SSO secret rotation needs to see a real + before/after diff in the audit row, not before_value=None. The endpoint + reads the existing (encrypted) SSO row, decrypts it, and lets the audit + helper redact the *_client_secret fields before persistence so neither + the old nor the new plaintext secret is recorded.""" + from unittest.mock import AsyncMock, MagicMock + + import litellm + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + audit_create = AsyncMock() + fake_prisma = MagicMock() + fake_prisma.db.litellm_auditlog.create = audit_create + fake_prisma.db.litellm_ssoconfig.upsert = AsyncMock() + + # Pre-existing SSO row contains the *prior* secret (would be ciphertext in + # production; the test patches _decrypt_db_variables to pass through). + existing_record = MagicMock() + existing_record.sso_settings = { + "google_client_id": "old-client-id", + "google_client_secret": "OLD-SUPER-SECRET", + } + fake_prisma.db.litellm_ssoconfig.find_unique = AsyncMock(return_value=existing_record) + fake_prisma.db.litellm_config.find_unique = AsyncMock(return_value=None) + + monkeypatch.setattr(proxy_server_module, "prisma_client", fake_prisma) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + monkeypatch.setattr( + proxy_server_module.proxy_config, + "_encrypt_env_variables", + lambda environment_variables: environment_variables, + ) + # Pretend the stored value is already plaintext for the test (production + # decrypts via Fernet); the audit helper still has to redact it. + monkeypatch.setattr( + proxy_server_module.proxy_config, + "_decrypt_db_variables", + lambda variables_dict: dict(variables_dict), + ) + + async def _admin_auth(): + return UserAPIKeyAuth( + user_id="audit-admin", + api_key="hashed-admin-key", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + app.dependency_overrides[user_api_key_auth] = _admin_auth + try: + resp = client.patch( + "/update/sso_settings", + json={ + "google_client_id": "new-client-id", + "google_client_secret": "NEW-SUPER-SECRET", + }, + ) + assert resp.status_code == 200, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + before = json.loads(written["before_value"]) + after = json.loads(written["updated_values"]) + + # Non-secret field shows the diff + assert before["google_client_id"] == "old-client-id" + assert after["google_client_id"] == "new-client-id" + + # Secret field is redacted in BOTH snapshots — auditor sees the + # rotation event without ever seeing either plaintext secret. + assert before["google_client_secret"] == "REDACTED" + assert after["google_client_secret"] == "REDACTED" + assert "OLD-SUPER-SECRET" not in written["before_value"] + assert "NEW-SUPER-SECRET" not in written["updated_values"] + finally: + app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_add_allowed_ip_writes_audit_log(mock_proxy_config, monkeypatch): + """Adding an allowed IP is a system-wide security setting change and must + be audited with the before and after IP list.""" + from unittest.mock import AsyncMock, MagicMock + + import litellm + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + audit_create = AsyncMock() + fake_prisma = MagicMock() + fake_prisma.db.litellm_auditlog.create = audit_create + + monkeypatch.setattr(proxy_server_module, "prisma_client", fake_prisma) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr(proxy_server_module, "general_settings", {}) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + + async def _admin_auth(): + return UserAPIKeyAuth( + user_id="audit-admin", + api_key="hashed-admin-key", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + app.dependency_overrides[user_api_key_auth] = _admin_auth + try: + resp = client.post("/add/allowed_ip", json={"ip": "203.0.113.77"}) + assert resp.status_code == 200, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + assert written["object_id"] == "general_settings" + assert written["action"] == "updated" + assert written["changed_by"] == "audit-admin" + + before = json.loads(written["before_value"]) + after = json.loads(written["updated_values"]) + assert "203.0.113.77" not in before["allowed_ips"] + assert "203.0.113.77" in after["allowed_ips"] + finally: + app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_delete_allowed_ip_writes_deleted_audit_log(monkeypatch): + """Removing an allowed IP must be audited as a deletion, symmetric with the + add path.""" + from unittest.mock import AsyncMock, MagicMock + + import litellm + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + audit_create = AsyncMock() + fake_prisma = MagicMock() + fake_prisma.db.litellm_auditlog.create = audit_create + + config = {"general_settings": {"allowed_ips": ["203.0.113.77", "198.51.100.1"]}} + + async def _get_config(): + return config + + async def _save_config(new_config=None): + nonlocal config + if new_config is not None: + config = new_config + return config + + monkeypatch.setattr(proxy_server_module, "prisma_client", fake_prisma) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr( + proxy_server_module, "general_settings", {"allowed_ips": ["203.0.113.77"]} + ) + monkeypatch.setattr(litellm, "store_audit_logs", True) + monkeypatch.setattr(proxy_server_module.proxy_config, "get_config", _get_config) + monkeypatch.setattr(proxy_server_module.proxy_config, "save_config", _save_config) + + async def _admin_auth(): + return UserAPIKeyAuth( + user_id="audit-admin", + api_key="hashed-admin-key", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + app.dependency_overrides[user_api_key_auth] = _admin_auth + try: + resp = client.post("/delete/allowed_ip", json={"ip": "203.0.113.77"}) + assert resp.status_code == 200, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + assert written["object_id"] == "general_settings" + assert written["action"] == "deleted" + before = json.loads(written["before_value"]) + after = json.loads(written["updated_values"]) + assert "203.0.113.77" in before["allowed_ips"] + assert "203.0.113.77" not in after["allowed_ips"] + finally: + app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_update_ui_theme_settings_writes_audit_log(mock_proxy_config, monkeypatch): + """Updating the UI theme must be audited under ui_theme_config.""" + from unittest.mock import AsyncMock, MagicMock + + import litellm + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + audit_create = AsyncMock() + fake_prisma = MagicMock() + fake_prisma.db.litellm_auditlog.create = audit_create + + monkeypatch.setattr(proxy_server_module, "prisma_client", fake_prisma) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + monkeypatch.setattr( + proxy_server_module.proxy_config, + "_encrypt_env_variables", + lambda environment_variables: environment_variables, + ) + + async def _admin_auth(): + return UserAPIKeyAuth( + user_id="audit-admin", + api_key="hashed-admin-key", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + app.dependency_overrides[user_api_key_auth] = _admin_auth + try: + resp = client.patch( + "/update/ui_theme_settings", + json={"logo_url": "https://example.com/logo.png"}, + ) + assert resp.status_code == 200, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + assert written["object_id"] == "ui_theme_config" + assert written["action"] == "updated" + assert written["changed_by"] == "audit-admin" + after = json.loads(written["updated_values"]) + assert after["logo_url"] == "https://example.com/logo.png" + finally: + app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_update_ui_settings_writes_audit_log(monkeypatch): + """Updating UI settings must be audited under the UI settings table.""" + from unittest.mock import AsyncMock, MagicMock + + import litellm + import litellm.proxy.proxy_server as proxy_server_module + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + audit_create = AsyncMock() + fake_prisma = MagicMock() + fake_prisma.db.litellm_auditlog.create = audit_create + fake_prisma.db.litellm_uisettings.find_unique = AsyncMock(return_value=None) + fake_prisma.db.litellm_uisettings.upsert = AsyncMock() + + monkeypatch.setattr(proxy_server_module, "prisma_client", fake_prisma) + monkeypatch.setattr(proxy_server_module, "premium_user", True) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + monkeypatch.setattr(litellm, "store_audit_logs", True) + + async def _admin_auth(): + return UserAPIKeyAuth( + user_id="audit-admin", + api_key="hashed-admin-key", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + + app.dependency_overrides[user_api_key_auth] = _admin_auth + try: + resp = client.patch( + "/update/ui_settings", json={"disable_custom_api_keys": True} + ) + assert resp.status_code == 200, resp.text + + audit_create.assert_awaited_once() + written = audit_create.await_args.kwargs["data"] + assert written["object_id"] == "ui_settings" + assert written["table_name"] == "LiteLLM_UISettings" + assert written["changed_by"] == "audit-admin" + after = json.loads(written["updated_values"]) + assert after["disable_custom_api_keys"] is True + finally: + app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_update_mcp_semantic_filter_settings_requires_proxy_admin(monkeypatch): + """Non-admin callers must not mutate global MCP semantic filter settings.""" + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True) + + async def _internal_user_auth(): + return UserAPIKeyAuth( + user_id="internal-user-1", + api_key="hashed-internal-key", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + app.dependency_overrides[user_api_key_auth] = _internal_user_auth + try: + resp = client.patch( + "/update/mcp_semantic_filter_settings", + json={"enabled": True, "top_k": 99, "similarity_threshold": 0.01}, + ) + assert resp.status_code == 403 + assert "proxy admin" in resp.json()["detail"].lower() + finally: + app.dependency_overrides.pop(user_api_key_auth, None) diff --git a/tests/test_litellm/proxy/utils/prisma_and_spend/test_prisma_client_engine_watcher.py b/tests/test_litellm/proxy/utils/prisma_and_spend/test_prisma_client_engine_watcher.py index 2fedd6bb134..25c04caabba 100644 --- a/tests/test_litellm/proxy/utils/prisma_and_spend/test_prisma_client_engine_watcher.py +++ b/tests/test_litellm/proxy/utils/prisma_and_spend/test_prisma_client_engine_watcher.py @@ -42,6 +42,7 @@ def test_get_engine_pid_extracts_process_pid(prisma_client: PrismaClient) -> Non fake_engine.process = MagicMock() fake_engine.process.pid = 4242 prisma_client.db._original_prisma = MagicMock() + prisma_client.db._original_prisma.is_connected = MagicMock(return_value=True) prisma_client.db._original_prisma._engine = fake_engine actual = { "pid": prisma_client._get_engine_pid(), @@ -58,6 +59,15 @@ def test_get_engine_pid_returns_zero_when_engine_attr_missing( assert prisma_client._get_engine_pid() == 0 +def test_get_engine_pid_returns_zero_when_client_disconnected( + prisma_client: PrismaClient, disconnected_prisma +) -> None: + """The reconnect path calls this on an arbitrarily-broken client; it must + report "no engine" instead of re-raising ClientNotConnectedError.""" + prisma_client.db._original_prisma = disconnected_prisma + assert prisma_client._get_engine_pid() == 0 + + def test_is_engine_alive_true_when_pid_zero(prisma_client: PrismaClient) -> None: prisma_client._engine_pid = 0 pinned = { diff --git a/tests/test_litellm/proxy/utils/prisma_and_spend/test_proxy_update_spend.py b/tests/test_litellm/proxy/utils/prisma_and_spend/test_proxy_update_spend.py index 6a4fd516c9b..d5d4de7f2cf 100644 --- a/tests/test_litellm/proxy/utils/prisma_and_spend/test_proxy_update_spend.py +++ b/tests/test_litellm/proxy/utils/prisma_and_spend/test_proxy_update_spend.py @@ -231,6 +231,112 @@ async def test_update_spend_logs_failure_raises_after_retries( ) +def _data_error(message: str) -> Any: + from prisma.errors import DataError + + return DataError({"user_facing_error": {"message": message}}) + + +@pytest.mark.asyncio +async def test_update_spend_logs_isolates_poison_row_and_persists_good_rows( + mock_prisma_client: Any, make_spend_log_row: Any +) -> None: + """One row Postgres rejects (22P05) must not drop the whole batch. + + The good rows still persist and only the offending row is dropped, with no + exception bubbling up. On the unfixed single-shot ``create_many`` the first + write raises and the entire batch is lost. + """ + poison_id = "r1" + written: List[str] = [] + + async def _create_many(*, data: Any, skip_duplicates: bool) -> None: + ids = [row["request_id"] for row in data] + if poison_id in ids: + raise _data_error( + "Inconsistent column data: 22P05 invalid byte sequence for encoding UTF8: 0x00" + ) + written.extend(ids) + + mock_prisma_client.db.litellm_spendlogs.create_many = AsyncMock(side_effect=_create_many) + proxy_logging = MagicMock() + proxy_logging.failure_handler = AsyncMock() + + logs = [make_spend_log_row(request_id=f"r{i}") for i in range(4)] + await ProxyUpdateSpend.update_spend_logs( + n_retry_times=0, + prisma_client=mock_prisma_client, + db_writer_client=None, + proxy_logging_obj=proxy_logging, + logs_to_process=logs, + ) + assert sorted(written) == ["r0", "r2", "r3"] + + +@pytest.mark.asyncio +async def test_update_spend_logs_reraises_connection_masquerade_dataerror( + mock_prisma_client: Any, make_spend_log_row: Any +) -> None: + """A P1001 "can't reach database server" outage that prisma mislabels as a + ``DataError`` is transient, not a poison row: it must propagate so the batch + is surfaced/retried rather than bisected into silent per-row drops. + """ + err = _data_error("Can't reach database server at db-host:5432") + mock_prisma_client.db.litellm_spendlogs.create_many = AsyncMock(side_effect=err) + proxy_logging = MagicMock() + proxy_logging.failure_handler = AsyncMock() + + with pytest.raises(type(err)): + await ProxyUpdateSpend.update_spend_logs( + n_retry_times=0, + prisma_client=mock_prisma_client, + db_writer_client=None, + proxy_logging_obj=proxy_logging, + logs_to_process=[ + make_spend_log_row(request_id="a"), + make_spend_log_row(request_id="b"), + ], + ) + + +@pytest.mark.asyncio +async def test_update_spend_logs_caps_isolation_attempts_under_poison_flood( + mock_prisma_client: Any, make_spend_log_row: Any +) -> None: + """A flood of poisoned rows must not amplify one failed bulk insert into + unbounded failed inserts. The per-batch attempt budget hard-caps the number + of ``create_many`` calls regardless of how many rows are poisoned, so the DB + work stays bounded and well below the input row count, and the helper still + completes without raising. + """ + import litellm.proxy.utils as utils_mod + + attempt_cap = utils_mod.MAX_SPEND_LOG_ISOLATION_ATTEMPTS_PER_BATCH + # single create_many batch (< BATCH_SIZE) whose row count exceeds the attempt + # cap, so the bound bites and attempts stay below the input row count + n_rows = attempt_cap * 3 + + async def _always_poison(*, data: Any, skip_duplicates: bool) -> None: + raise _data_error("invalid byte sequence for encoding UTF8: 0x00") + + mock_prisma_client.db.litellm_spendlogs.create_many = AsyncMock(side_effect=_always_poison) + proxy_logging = MagicMock() + proxy_logging.failure_handler = AsyncMock() + logs = [make_spend_log_row(request_id=f"r{i}") for i in range(n_rows)] + + await ProxyUpdateSpend.update_spend_logs( + n_retry_times=0, + prisma_client=mock_prisma_client, + db_writer_client=None, + proxy_logging_obj=proxy_logging, + logs_to_process=logs, + ) + + attempts = mock_prisma_client.db.litellm_spendlogs.create_many.await_count + assert attempts <= attempt_cap + assert attempts < n_rows + + def test_disable_spend_updates_reflects_general_settings( monkeypatch: pytest.MonkeyPatch, ) -> None: diff --git a/tests/test_litellm/realtime_api/test_main.py b/tests/test_litellm/realtime_api/test_main.py new file mode 100644 index 00000000000..406f5ef56d9 --- /dev/null +++ b/tests/test_litellm/realtime_api/test_main.py @@ -0,0 +1,105 @@ +import asyncio +import os +import sys + +sys.path.insert(0, os.path.abspath("../../..")) + +import pytest + +from litellm.realtime_api import main as realtime_main +from litellm.realtime_api.main import _with_resolved_session_model + + +class FakeLogging: + def update_from_kwargs(self, **kwargs): + pass + + +def test_resolves_top_level_session_model(): + resolved = _with_resolved_session_model({"model": "alias/gpt-realtime"}, "gpt-realtime") + assert resolved == {"model": "gpt-realtime"} + + +def test_session_without_model_is_returned_unchanged(): + session = {"type": "realtime", "audio": {"input": {}}} + assert _with_resolved_session_model(session, "gpt-realtime") == session + + +def test_does_not_clobber_flat_transcription_model(): + """The nested transcription model is a different model than the realtime + conversation model and must not be overwritten with the routing model.""" + resolved = _with_resolved_session_model( + {"model": "gpt-4o-realtime-preview", "input_audio_transcription": {"model": "whisper-1"}}, + "gpt-4o-realtime-preview", + ) + assert resolved["input_audio_transcription"]["model"] == "whisper-1" + + +def test_does_not_clobber_nested_audio_transcription_model(): + resolved = _with_resolved_session_model( + { + "model": "gpt-4o-realtime-preview", + "audio": {"input": {"transcription": {"model": "whisper-1"}}}, + }, + "gpt-4o-realtime-preview", + ) + assert resolved["audio"]["input"]["transcription"]["model"] == "whisper-1" + + +def test_original_session_is_not_mutated(): + session = {"model": "alias/gpt-realtime"} + _with_resolved_session_model(session, "gpt-realtime") + assert session == {"model": "alias/gpt-realtime"} + + +def _run_client_secret(session, model, monkeypatch): + captured = {} + + async def mock_handler(**kwargs): + captured.update(kwargs) + return object() + + def mock_get_llm_provider(model, api_base, api_key): + return model, "openai", None, api_base + + monkeypatch.setattr(realtime_main, "get_llm_provider", mock_get_llm_provider) + monkeypatch.setattr( + realtime_main.base_llm_http_handler, + "async_realtime_client_secret_handler", + mock_handler, + ) + + asyncio.run( + realtime_main.acreate_realtime_client_secret.__wrapped__( + model=model, + session=session, + litellm_logging_obj=FakeLogging(), + ) + ) + return captured + + +def test_client_secret_session_model_takes_priority_over_top_level(monkeypatch): + """Backwards-compatible ordering: an explicit session.model wins over the + top-level model, matching the proxy's own resolution order.""" + captured = _run_client_secret( + session={"model": "gpt-realtime-session"}, + model="gpt-realtime-top-level", + monkeypatch=monkeypatch, + ) + assert captured["model"] == "gpt-realtime-session" + assert captured["request_data"]["session"]["model"] == "gpt-realtime-session" + + +def test_client_secret_forwards_nested_transcription_model_untouched(monkeypatch): + captured = _run_client_secret( + session={ + "model": "gpt-4o-realtime-preview", + "input_audio_transcription": {"model": "whisper-1"}, + }, + model=None, + monkeypatch=monkeypatch, + ) + session = captured["request_data"]["session"] + assert session["model"] == "gpt-4o-realtime-preview" + assert session["input_audio_transcription"]["model"] == "whisper-1" diff --git a/tests/test_litellm/rerank_api/__init__.py b/tests/test_litellm/rerank_api/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/test_litellm/rerank_api/test_main.py b/tests/test_litellm/rerank_api/test_main.py new file mode 100644 index 00000000000..46d1461da50 --- /dev/null +++ b/tests/test_litellm/rerank_api/test_main.py @@ -0,0 +1,67 @@ +import logging +import os +import sys +from unittest.mock import MagicMock, patch + +sys.path.insert(0, os.path.abspath("../../..")) + +import litellm + +MARKER_QUERY = "MARKER_QUERY_do_not_log_at_info" +MARKER_DOC = "MARKER_DOC_sensitive_customer_text" + + +def _mock_cohere_response() -> MagicMock: + mock_response = MagicMock() + + def return_val(): + return { + "id": "cmpl-mockid", + "results": [{"index": 0, "relevance_score": 0.95}], + "meta": { + "api_version": {"version": "1.0"}, + "billed_units": {"search_units": 1}, + }, + } + + mock_response.json = return_val + mock_response.headers = {"key": "value"} + mock_response.status_code = 200 + return mock_response + + +def test_rerank_does_not_log_request_content_at_info(caplog): + """Regression for #32525: rerank must not emit query/documents to logs at INFO. + + The mapped ``optional_rerank_params`` (which always contains ``query`` and + ``documents``) bypasses ``turn_off_message_logging`` / ``redact_messages``, + so logging it at INFO leaks raw request content into stdout and any log sink. + """ + litellm.cohere_key = "test_api_key" + caplog.set_level(logging.DEBUG, logger="LiteLLM") + + with patch( + "litellm.llms.custom_httpx.http_handler.HTTPHandler.post", + return_value=_mock_cohere_response(), + ): + litellm.rerank( + model="cohere/rerank-english-v3.0", + query=MARKER_QUERY, + documents=[MARKER_DOC, "unrelated"], + top_n=2, + ) + + litellm_records = [r for r in caplog.records if r.name == "LiteLLM"] + + info_or_above = [ + r.getMessage() + for r in litellm_records + if r.levelno >= logging.INFO and (MARKER_QUERY in r.getMessage() or MARKER_DOC in r.getMessage()) + ] + assert not info_or_above, f"rerank leaked request content at INFO+: {info_or_above}" + + optional_params_logs = [r for r in litellm_records if "optional_rerank_params" in r.getMessage()] + assert optional_params_logs, "expected the optional_rerank_params line to be logged" + assert all( + r.levelno == logging.DEBUG for r in optional_params_logs + ), "optional_rerank_params must be logged at DEBUG, not INFO" diff --git a/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py b/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py index 426c73645c1..d8e3f495ced 100644 --- a/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py +++ b/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py @@ -1,6 +1,8 @@ import os import sys +import pytest + sys.path.insert( 0, os.path.abspath("../../..") ) # Adds the parent directory to the system path @@ -1066,7 +1068,13 @@ class TestToolTransformation: assert web_search_options is None def test_transform_computer_use_tools(self): - """Test that computer_use tools are passed through as-is""" + """Test that computer_use tools are dropped (no Chat Completions equivalent). + + This deliberately reverses the previous pass-through regression guard: + forwarding computer_use verbatim made Chat Completions providers reject + the whole request with "'function' is a required property", so the + bridge now drops such tools (with a warning log) instead. + """ computer_use_tool = { "type": "computer_use", "display_width_px": 1024, @@ -1083,11 +1091,129 @@ class TestToolTransformation: tools=tools ) + # Assert - computer_use has no Chat Completions equivalent, so it is dropped + assert len(result_tools) == 0 + assert web_search_options is None + + def test_transform_custom_tools_to_function_tools(self): + """Test that custom (freeform/grammar) tools are converted to function tools""" + custom_tool = { + "type": "custom", + "name": "apply_patch", + "description": "Apply a patch to files", + "format": { + "type": "grammar", + "syntax": "lark", + "definition": "start: begin_patch hunk+ end_patch", + }, + } + + tools = [custom_tool] + + # Execute + ( + result_tools, + web_search_options, + ) = LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools( + tools=tools + ) + + # Assert - custom tool is converted to a function tool + assert len(result_tools) == 1 + assert result_tools[0]["type"] == "function" + assert result_tools[0]["function"]["name"] == "apply_patch" + assert "content" in result_tools[0]["function"]["parameters"]["properties"] + assert result_tools[0]["function"]["parameters"]["required"] == ["content"] + assert "begin_patch" in result_tools[0]["function"]["description"] + assert web_search_options is None + + def test_transform_custom_tools_without_format(self): + """Test that custom tools without format info are still converted""" + custom_tool = { + "type": "custom", + "name": "exec", + "description": "Execute code", + } + + tools = [custom_tool] + + # Execute + ( + result_tools, + web_search_options, + ) = LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools( + tools=tools + ) + # Assert assert len(result_tools) == 1 - assert result_tools[0] == computer_use_tool - assert result_tools[0]["type"] == "computer_use" - assert web_search_options is None + assert result_tools[0]["type"] == "function" + assert result_tools[0]["function"]["name"] == "exec" + assert result_tools[0]["function"]["description"] == "Execute code" + + def test_transform_custom_tools_preserves_allowed_callers(self): + """allowed_callers on a custom tool gates direct model invocation in the + Anthropic adapter, so it must survive the custom->function conversion.""" + custom_tool = { + "type": "custom", + "name": "apply_patch", + "description": "Apply a patch to files", + "allowed_callers": ["exec"], + } + + tools = [custom_tool] + + # Execute + ( + result_tools, + _, + ) = LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools( + tools=tools + ) + + # Assert + assert len(result_tools) == 1 + assert result_tools[0]["type"] == "function" + assert result_tools[0]["allowed_callers"] == ["exec"] + + def test_transform_custom_tools_without_allowed_callers(self): + """A custom tool without allowed_callers must not synthesize the field.""" + custom_tool = { + "type": "custom", + "name": "apply_patch", + "description": "Apply a patch to files", + } + + tools = [custom_tool] + + # Execute + ( + result_tools, + _, + ) = LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools( + tools=tools + ) + + # Assert + assert len(result_tools) == 1 + assert "allowed_callers" not in result_tools[0] + + def test_transform_custom_tools_rejects_invalid_allowed_callers(self): + """Invalid allowed_callers must raise rather than silently dropping the + provider-side allowlist.""" + custom_tool = { + "type": "custom", + "name": "apply_patch", + "description": "Apply a patch to files", + "allowed_callers": "exec", + } + + tools = [custom_tool] + + with pytest.raises(ValueError, match="allowed_callers must be a list of strings"): + LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools( + tools=tools + ) def test_transform_web_search_tools_to_web_search_options(self): """Test that web_search tools are converted to web_search_options""" @@ -1833,6 +1959,110 @@ class TestUsageTransformation: assert response_usage.output_tokens_details.text_tokens == 50 assert response_usage.output_tokens_details.image_tokens == 100 + def test_reasoning_tokens_not_forced_to_zero_when_absent(self): + # Regression: previously the else branch wrote reasoning_tokens=0 even when + # completion_tokens_details had no reasoning (reasoning_tokens=None). That caused + # the proxy to always report reasoning_tokens=0 for non-thinking responses. + usage = Usage( + prompt_tokens=10, + completion_tokens=50, + total_tokens=60, + completion_tokens_details=CompletionTokensDetailsWrapper( + text_tokens=50, + # reasoning_tokens intentionally absent -> None + ), + ) + + chat_completion_response = ModelResponse( + id="test-response-id", + created=1234567890, + model="claude-haiku-4-5", + object="chat.completion", + usage=usage, + choices=[ + Choices( + finish_reason="stop", + index=0, + message=Message(content="Hello!", role="assistant"), + ) + ], + ) + + response_usage = LiteLLMCompletionResponsesConfig._transform_chat_completion_usage_to_responses_usage( + chat_completion_response=chat_completion_response + ) + + assert response_usage.output_tokens_details is not None + assert response_usage.output_tokens_details.reasoning_tokens is None + + def test_reasoning_tokens_preserved_when_thinking_occurred(self): + # Regression: reasoning_tokens must survive the chat->responses translation + # when the provider actually did thinking. + usage = Usage( + prompt_tokens=100, + completion_tokens=612, + total_tokens=712, + completion_tokens_details=CompletionTokensDetailsWrapper( + reasoning_tokens=512, + text_tokens=100, + ), + ) + + chat_completion_response = ModelResponse( + id="test-response-id", + created=1234567890, + model="claude-haiku-4-5", + object="chat.completion", + usage=usage, + choices=[ + Choices( + finish_reason="stop", + index=0, + message=Message(content="Hello!", role="assistant"), + ) + ], + ) + + response_usage = LiteLLMCompletionResponsesConfig._transform_chat_completion_usage_to_responses_usage( + chat_completion_response=chat_completion_response + ) + + assert response_usage.output_tokens_details is not None + assert response_usage.output_tokens_details.reasoning_tokens == 512 + + def test_reasoning_tokens_explicit_zero_preserved(self): + usage = Usage( + prompt_tokens=10, + completion_tokens=50, + total_tokens=60, + completion_tokens_details=CompletionTokensDetailsWrapper( + reasoning_tokens=0, + text_tokens=50, + ), + ) + + chat_completion_response = ModelResponse( + id="test-response-id", + created=1234567890, + model="gpt-5.6", + object="chat.completion", + usage=usage, + choices=[ + Choices( + finish_reason="stop", + index=0, + message=Message(content="Hello!", role="assistant"), + ) + ], + ) + + response_usage = LiteLLMCompletionResponsesConfig._transform_chat_completion_usage_to_responses_usage( + chat_completion_response=chat_completion_response + ) + + assert response_usage.output_tokens_details is not None + assert response_usage.output_tokens_details.reasoning_tokens == 0 + class TestStreamingIDConsistency: """Test cases for consistent IDs across streaming events (issue #14962)""" @@ -2185,6 +2415,155 @@ class TestStreamingIDConsistency: assert tool_calls is not None and len(tool_calls) == 1 +class TestCompletedResponseLatchedOnStreamEnd: + """Regression: LiteLLMCompletionStreamingIterator (the Chat Completions + bridge path) never set ``self.completed_response`` because it overrides + __anext__ and bypasses the base class's _process_chunk where that + attribute is normally latched. FallbackResponsesStreamWrapper reads + ``completed_response`` via getattr to record container ownership; when + it stays None the proxy logs a "Container ownership recording skipped" + warning and follow-up /v1/containers//files calls 403 for non-admin + keys. Codex CLI's apply_patch tool also surfaces as "aborted" because + the terminal response.completed event never propagates correctly.""" + + def _make_iterator_with_stop(self, model_response): + """Build a LiteLLMCompletionStreamingIterator whose underlying + CustomStreamWrapper raises StopAsyncIteration immediately (simulating + a stream that already delivered all content chunks).""" + from unittest.mock import Mock + + import litellm + from litellm.responses.litellm_completion_transformation.streaming_iterator import ( + LiteLLMCompletionStreamingIterator, + ) + + mock_wrapper = Mock(spec=litellm.CustomStreamWrapper) + mock_wrapper.logging_obj = Mock() + mock_wrapper.logging_obj._response_cost_calculator = Mock(return_value=0.0) + mock_wrapper.__aiter__ = Mock(return_value=mock_wrapper) + mock_wrapper.__anext__ = Mock(side_effect=StopAsyncIteration) + + iterator = LiteLLMCompletionStreamingIterator( + model="deepseek/deepseek-chat", + litellm_custom_stream_wrapper=mock_wrapper, + request_input="test", + responses_api_request={}, + ) + iterator.litellm_model_response = model_response + return iterator + + def test_completed_response_set_after_common_done_event_logic(self): + """common_done_event_logic builds a ResponseCompletedEvent and must + latch it onto self.completed_response so downstream wrappers can + read it. Before the fix the event was returned but + completed_response stayed None.""" + from litellm.types.utils import Choices, Message, ModelResponse + + complete_response = ModelResponse( + id="resp_test", + created=1234567890, + model="deepseek-chat", + object="chat.completion", + choices=[ + Choices( + finish_reason="stop", + index=0, + message=Message(content="hello", role="assistant"), + ) + ], + ) + iterator = self._make_iterator_with_stop(complete_response) + + import asyncio + + async def drain(): + results = [] + try: + async for chunk in iterator: + results.append(chunk) + except StopAsyncIteration: + pass + return results + + results = asyncio.run(drain()) + assert len(results) > 0 + assert iterator.completed_response is not None, ( + "LiteLLMCompletionStreamingIterator.completed_response is still None " + "after common_done_event_logic ran — downstream wrappers and the " + "proxy container-ownership hook will see no terminal event" + ) + assert iterator.completed_response.type == "response.completed" + + +class TestFallbackWrapperStopAsyncIterationFallback: + """Regression: FallbackResponsesStreamWrapper.__anext__ only sniffed + terminal events off forwarded chunks. When the inner generator raises + StopAsyncIteration without a sniffable chunk (the bridge path ends this + way), the wrapper re-raised without checking source_iterator for a + latched completed_response, leaving its own completed_response None.""" + + def test_falls_back_to_source_completed_response_on_stop(self): + """When the inner async generator raises StopAsyncIteration and the + wrapper never sniffed a terminal chunk, it must copy + source_iterator.completed_response so the proxy ownership hook + still sees the terminal event.""" + import asyncio + from types import SimpleNamespace + + from litellm.router import Router + + source = SimpleNamespace( + response=None, + model="deepseek/deepseek-chat", + logging_obj=None, + responses_api_provider_config=None, + start_time=None, + litellm_metadata=None, + custom_llm_provider="deepseek", + request_data={}, + call_type="aresponses", + _hidden_params={}, + completed_response=SimpleNamespace( + type="response.completed", + response=SimpleNamespace(id="resp_src", output=[], container=None), + ), + ) + + async def empty_gen(): + return + yield # pragma: no cover + + async def _drive(): + router = Router( + model_list=[ + { + "model_name": "deepseek/deepseek-chat", + "litellm_params": {"model": "deepseek/deepseek-chat", "api_key": "sk-test"}, + } + ] + ) + wrapper = await router._aresponses_streaming_iterator( + response=source, # type: ignore[arg-type] + initial_kwargs={}, + ) + wrapper._async_generator = empty_gen() + out = [] + try: + async for chunk in wrapper: + out.append(chunk) + except StopAsyncIteration: + pass + return wrapper, out + + wrapper, _ = asyncio.run(_drive()) + assert wrapper.completed_response is not None, ( + "FallbackResponsesStreamWrapper.completed_response is None after " + "StopAsyncIteration even though source_iterator had one — the " + "proxy ownership hook will log a spurious warning" + ) + assert wrapper.completed_response.type == "response.completed" + + class TestEnsureOutputItemContentPartAdded: """Test that _ensure_output_item_for_chunk emits content_part.added after output_item.added for message items.""" diff --git a/tests/test_litellm/responses/mcp/test_chat_completions_handler.py b/tests/test_litellm/responses/mcp/test_chat_completions_handler.py index fc5d2e5d382..ab4c5185057 100644 --- a/tests/test_litellm/responses/mcp/test_chat_completions_handler.py +++ b/tests/test_litellm/responses/mcp/test_chat_completions_handler.py @@ -1105,3 +1105,242 @@ async def test_execute_tool_calls_sets_proxy_server_request_arguments(monkeypatc "param1": "value1", "param2": 123, }, "arguments should be parsed correctly" + + +@pytest.mark.asyncio +async def test_acompletion_with_mcp_streaming_drain_error_does_not_drop_final_chunk(monkeypatch): + """ + Regression test: after yielding the final chunk, MCPStreamingIterator drains + the inner CustomStreamWrapper to fire end-of-stream spend logging. If the + inner stream raises a non-StopAsyncIteration error during that drain (e.g. + a transient APIError on the trailing usage chunk), the error must not + escape __anext__ and drop the already-assembled final chunk. + """ + from unittest.mock import MagicMock + + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices + from litellm.utils import CustomStreamWrapper + + tools = [{"type": "mcp", "server_url": "litellm_proxy/mcp/local"}] + openai_tools = [{"type": "function", "function": {"name": "local_search"}}] + + def create_chunk(content, finish_reason=None): + return ModelResponseStream( + id="test-stream", + model="test-model", + created=1234567890, + object="chat.completion.chunk", + choices=[ + StreamingChoices( + index=0, + delta=Delta(content=content, role="assistant"), + finish_reason=finish_reason, + ) + ], + ) + + chunks = [ + create_chunk("Hello"), + create_chunk(" world", finish_reason="stop"), + ] + + logging_obj = MagicMock() + logging_obj.model_call_details = {} + + class DrainErrorStreamingResponse(CustomStreamWrapper): + def __init__(self): + super().__init__( + completion_stream=None, + model="test-model", + logging_obj=logging_obj, + ) + self.chunks = chunks + self._index = 0 + + def __aiter__(self): + return self + + async def __anext__(self): + if self._index < len(self.chunks): + chunk = self.chunks[self._index] + self._index += 1 + return chunk + if self._index == len(self.chunks): + self._index += 1 + raise RuntimeError("connection dropped on trailing usage chunk") + raise StopAsyncIteration + + mock_acompletion = AsyncMock(return_value=DrainErrorStreamingResponse()) + + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_should_use_litellm_mcp_gateway", + staticmethod(lambda tools: True), + ) + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_parse_mcp_tools", + staticmethod(lambda tools: (tools, [])), + ) + + async def mock_process(**_): + return (tools, {"local_search": "local"}) + + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_process_mcp_tools_without_openai_transform", + mock_process, + ) + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_transform_mcp_tools_to_openai", + staticmethod(lambda *_, **__: openai_tools), + ) + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_should_auto_execute_tools", + staticmethod(lambda **_: True), + ) + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_extract_tool_calls_from_chat_response", + staticmethod(lambda **_: []), + ) + monkeypatch.setattr( + ResponsesAPIRequestUtils, + "extract_mcp_headers_from_request", + staticmethod(lambda **_: (None, None, None, None)), + ) + + with patch("litellm.acompletion", mock_acompletion): + result = await acompletion_with_mcp( + model="gpt-4o-mini", + messages=[{"role": "user", "content": "hello"}], + tools=tools, + stream=True, + ) + + all_chunks = [] + async for chunk in result: + all_chunks.append(chunk) + + final_chunks = [ + chunk + for chunk in all_chunks + if chunk.choices and chunk.choices[0].finish_reason == "stop" + ] + assert len(final_chunks) == 1, f"Final chunk must survive a drain error. Got chunks: {all_chunks}" + assert all_chunks[-1].choices[0].finish_reason == "stop" + + +@pytest.mark.asyncio +async def test_acompletion_with_mcp_streaming_drains_inner_stream_after_exhaustion(monkeypatch): + from unittest.mock import MagicMock + + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices + from litellm.utils import CustomStreamWrapper + + tools = [{"type": "mcp", "server_url": "litellm_proxy/mcp/local"}] + openai_tools = [{"type": "function", "function": {"name": "local_search"}}] + + def create_chunk(content): + return ModelResponseStream( + id="test-stream", + model="test-model", + created=1234567890, + object="chat.completion.chunk", + choices=[ + StreamingChoices( + index=0, + delta=Delta(content=content, role="assistant"), + finish_reason=None, + ) + ], + ) + + chunks = [create_chunk("Hello"), create_chunk(" world")] + logging_obj = MagicMock() + logging_obj.model_call_details = {} + + class ExhaustingStreamingResponse(CustomStreamWrapper): + def __init__(self): + super().__init__( + completion_stream=None, + model="test-model", + logging_obj=logging_obj, + ) + self.chunks = chunks + self._index = 0 + self.drained_after_exhaustion = False + + def __aiter__(self): + return self + + async def __anext__(self): + if self._index < len(self.chunks): + chunk = self.chunks[self._index] + self._index += 1 + return chunk + if self._index == len(self.chunks): + self._index += 1 + raise StopAsyncIteration + self.drained_after_exhaustion = True + raise StopAsyncIteration + + initial_stream = ExhaustingStreamingResponse() + mock_acompletion = AsyncMock(return_value=initial_stream) + + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_should_use_litellm_mcp_gateway", + staticmethod(lambda tools: True), + ) + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_parse_mcp_tools", + staticmethod(lambda tools: (tools, [])), + ) + + async def mock_process(**_): + return (tools, {"local_search": "local"}) + + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_process_mcp_tools_without_openai_transform", + mock_process, + ) + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_transform_mcp_tools_to_openai", + staticmethod(lambda *_, **__: openai_tools), + ) + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_should_auto_execute_tools", + staticmethod(lambda **_: True), + ) + monkeypatch.setattr( + LiteLLM_Proxy_MCP_Handler, + "_extract_tool_calls_from_chat_response", + staticmethod(lambda **_: []), + ) + monkeypatch.setattr( + ResponsesAPIRequestUtils, + "extract_mcp_headers_from_request", + staticmethod(lambda **_: (None, None, None, None)), + ) + + with patch("litellm.acompletion", mock_acompletion): + result = await acompletion_with_mcp( + model="gpt-4o-mini", + messages=[{"role": "user", "content": "hello"}], + tools=tools, + stream=True, + ) + + all_chunks = [] + async for chunk in result: + all_chunks.append(chunk) + + assert len(all_chunks) == 3 + assert initial_stream.drained_after_exhaustion is True diff --git a/tests/test_litellm/responses/mcp/test_litellm_proxy_mcp_handler.py b/tests/test_litellm/responses/mcp/test_litellm_proxy_mcp_handler.py index 94712813783..6fdbb0741aa 100644 --- a/tests/test_litellm/responses/mcp/test_litellm_proxy_mcp_handler.py +++ b/tests/test_litellm/responses/mcp/test_litellm_proxy_mcp_handler.py @@ -1,4 +1,6 @@ +import subprocess import sys +import textwrap import types from unittest.mock import AsyncMock, MagicMock @@ -28,6 +30,7 @@ def _setup_mcp_call_environment(monkeypatch: pytest.MonkeyPatch) -> AsyncMock: call_tool=AsyncMock(return_value=_DummyMCPResult()), # Newer logging path calls this to enrich spend logs metadata _get_mcp_server_from_tool_name=MagicMock(return_value=None), + get_mcp_server_by_name=MagicMock(return_value=None), ) monkeypatch.setattr( "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager", @@ -279,6 +282,87 @@ async def test_execute_tool_calls_keeps_tool_name_when_equal_to_server(monkeypat assert call_tool_mock.await_args.kwargs["name"] == tool_name +@pytest.mark.asyncio +async def test_execute_tool_calls_strips_prefix_when_alias_differs_from_server_name( + monkeypatch, +): + call_tool_mock = _setup_mcp_call_environment(monkeypatch) + fake_server = types.SimpleNamespace( + alias="my_deepwiki", + server_name="deepwiki_test", + server_id="test-server-id", + short_prefix=None, + mcp_info=None, + tool_name_to_display_name=None, + ) + from litellm.proxy._experimental.mcp_server import mcp_server_manager as _msm + + _msm.global_mcp_server_manager._get_mcp_server_from_tool_name = MagicMock( + return_value=fake_server + ) + + tool_name = "my_deepwiki-read_wiki_structure" + tool_calls = [ + { + "id": "call-4", + "function": {"name": tool_name, "arguments": "{}"}, + } + ] + + await LiteLLM_Proxy_MCP_Handler._execute_tool_calls( + tool_server_map={tool_name: "deepwiki_test"}, + tool_calls=tool_calls, + user_api_key_auth=None, + ) + + assert call_tool_mock.await_count == 1 + assert call_tool_mock.await_args is not None + assert call_tool_mock.await_args.kwargs["name"] == "read_wiki_structure" + + +@pytest.mark.asyncio +async def test_execute_tool_calls_reverse_maps_display_name(monkeypatch): + call_tool_mock = _setup_mcp_call_environment(monkeypatch) + colliding_server = types.SimpleNamespace( + alias=None, + server_name="other_mcp", + server_id="other-server-id", + short_prefix=None, + mcp_info=None, + tool_name_to_display_name={"search": "search_docs"}, + ) + fake_server = types.SimpleNamespace( + alias=None, + server_name="deepwiki_mcp", + server_id="test-server-id", + short_prefix=None, + mcp_info=None, + tool_name_to_display_name={"read_wiki_structure": "browse_repo_docs"}, + ) + from litellm.proxy._experimental.mcp_server import mcp_server_manager as _msm + + _msm.global_mcp_server_manager._get_mcp_server_from_tool_name = MagicMock(return_value=colliding_server) + _msm.global_mcp_server_manager.get_mcp_server_by_name = MagicMock(return_value=fake_server) + + tool_name = "browse_repo_docs" + tool_calls = [ + { + "id": "call-5", + "function": {"name": tool_name, "arguments": "{}"}, + } + ] + + await LiteLLM_Proxy_MCP_Handler._execute_tool_calls( + tool_server_map={tool_name: "deepwiki_mcp"}, + tool_calls=tool_calls, + user_api_key_auth=None, + ) + + assert call_tool_mock.await_count == 1 + assert call_tool_mock.await_args is not None + assert call_tool_mock.await_args.kwargs["name"] == "read_wiki_structure" + + @pytest.mark.asyncio async def test_execute_tool_calls_logs_failure_via_post_call_failure_hook(monkeypatch): """ @@ -401,3 +485,123 @@ async def test_get_mcp_tools_from_manager_enables_list_tools_logging(monkeypatch assert mock_get_tools.await_args is not None assert mock_get_tools.await_args.kwargs["log_list_tools_to_spendlogs"] is True assert mock_get_tools.await_args.kwargs["list_tools_log_source"] == "responses" + + +def test_get_parent_request_tags_from_metadata(): + tags = LiteLLM_Proxy_MCP_Handler._get_parent_request_tags( + {"metadata": {"tags": ["team-a", "prod"]}} + ) + assert tags == ["team-a", "prod"] + + +def test_get_parent_request_tags_from_nested_litellm_params(): + tags = LiteLLM_Proxy_MCP_Handler._get_parent_request_tags( + { + "metadata": {"tags": ["top-level"]}, + "litellm_params": { + "metadata": {"tags": ["nested"]}, + "proxy_server_request": {"headers": {"user-agent": "client/1.0"}}, + }, + } + ) + assert tags == ["nested", "User-Agent: client", "User-Agent: client/1.0"] + + +@pytest.mark.asyncio +async def test_get_mcp_tools_from_manager_forwards_request_tags(monkeypatch): + mock_get_tools = AsyncMock(return_value=[]) + monkeypatch.setattr( + "litellm.proxy._experimental.mcp_server.server._get_tools_from_mcp_servers", + mock_get_tools, + ) + fake_manager = types.SimpleNamespace( + get_allowed_mcp_servers=AsyncMock(return_value=[]), + get_mcp_servers_from_ids=MagicMock(return_value=[]), + get_mcp_server_by_name=MagicMock(return_value=None), + ) + monkeypatch.setattr( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager", + fake_manager, + ) + + await LiteLLM_Proxy_MCP_Handler._get_mcp_tools_from_manager( + user_api_key_auth=types.SimpleNamespace(api_key="k", user_id="u"), + mcp_tools_with_litellm_proxy=[ + {"type": "mcp", "server_url": "litellm_proxy/mcp/deepwiki"} + ], + request_tags=["team-a"], + ) + + assert mock_get_tools.await_args.kwargs["request_tags"] == ["team-a"] + + +@pytest.mark.asyncio +async def test_execute_tool_calls_propagates_request_tags_to_function_setup(monkeypatch): + _setup_proxy_logging(monkeypatch) + _setup_mcp_call_environment(monkeypatch) + captured = {} + + def fake_function_setup(*_args, **kwargs): + captured.update(kwargs) + return None, None + + handler_module = importlib.import_module( + "litellm.responses.mcp.litellm_proxy_mcp_handler" + ) + monkeypatch.setattr(handler_module, "function_setup", fake_function_setup) + + tool_name = "deepwiki-read_wiki_structure" + await LiteLLM_Proxy_MCP_Handler._execute_tool_calls( + tool_server_map={tool_name: "deepwiki"}, + tool_calls=[{"id": "call-1", "function": {"name": tool_name, "arguments": "{}"}}], + user_api_key_auth=None, + request_tags=["team-a", "prod"], + ) + + assert captured["metadata"]["tags"] == ["team-a", "prod"] + + +def test_completion_with_function_tools_works_without_fastapi_installed(): + script = textwrap.dedent( + """ + import sys + + class _FastapiBlocker: + def find_spec(self, fullname, path=None, target=None): + if fullname == "fastapi" or fullname.startswith("fastapi."): + raise ModuleNotFoundError("No module named 'fastapi'") + return None + + sys.meta_path.insert(0, _FastapiBlocker()) + + import litellm + + response = litellm.completion( + model="openai/gpt-5.5", + messages=[{"role": "user", "content": "What is the weather in SF?"}], + tools=[ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get the current weather for a location", + "parameters": { + "type": "object", + "properties": {"location": {"type": "string"}}, + "required": ["location"], + }, + }, + } + ], + mock_response="sunny", + ) + assert response.choices[0].message.content == "sunny" + """ + ) + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + timeout=120, + ) + assert result.returncode == 0, result.stderr diff --git a/tests/test_litellm/responses/mcp/test_mcp_streaming_iterator.py b/tests/test_litellm/responses/mcp/test_mcp_streaming_iterator.py new file mode 100644 index 00000000000..cdace5f6327 --- /dev/null +++ b/tests/test_litellm/responses/mcp/test_mcp_streaming_iterator.py @@ -0,0 +1,172 @@ +import sys +import types +from types import SimpleNamespace +from unittest.mock import AsyncMock, MagicMock + +import pytest +from mcp.types import CallToolResult, TextContent + +import litellm # noqa: F401 - ensures litellm.responses.main is registered in sys.modules +from litellm.responses.mcp.mcp_streaming_iterator import ( + MAX_MCP_TOOL_CALL_ROUNDS, + MCPEnhancedStreamingIterator, +) +from litellm.types.llms.openai import ResponsesAPIResponse, ResponsesAPIStreamEvents + +# `litellm.__init__` re-exports a function named `responses`, which shadows the +# `litellm.responses` subpackage as an attribute — `import litellm.responses.main` +# can resolve to the unrelated third-party `responses` package instead. Look the +# real submodule up in sys.modules directly to sidestep the shadowing. +responses_main_module = sys.modules["litellm.responses.main"] + + +class _FakeAsyncStream: + """Minimal async iterator yielding pre-built chunks, one per __anext__ call.""" + + def __init__(self, chunks): + self._chunks = list(chunks) + + def __aiter__(self): + return self + + async def __anext__(self): + if not self._chunks: + raise StopAsyncIteration + return self._chunks.pop(0) + + +def _output_item_added_chunk(): + return SimpleNamespace(type=ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED) + + +def _completed_chunk(output): + response = ResponsesAPIResponse(id="resp-1", created_at=0, output=output) + return SimpleNamespace(type=ResponsesAPIStreamEvents.RESPONSE_COMPLETED, response=response) + + +def _function_call(call_id: str, name: str, arguments: str = "{}"): + return {"type": "function_call", "call_id": call_id, "name": name, "arguments": arguments} + + +def _text_message(text: str): + return {"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": text}]} + + +def _tool_call_stream(call_id: str, tool_name: str) -> _FakeAsyncStream: + return _FakeAsyncStream([_completed_chunk([_function_call(call_id, tool_name)])]) + + +def _text_only_stream(text: str) -> _FakeAsyncStream: + return _FakeAsyncStream([_completed_chunk([_text_message(text)])]) + + +def _mock_mcp_environment(monkeypatch: pytest.MonkeyPatch) -> AsyncMock: + """Patch the MCP tool-call plumbing so _execute_tool_calls can run in tests.""" + call_tool = AsyncMock(return_value=CallToolResult(content=[TextContent(type="text", text="ok")], isError=False)) + fake_manager = types.SimpleNamespace( + call_tool=call_tool, + _get_mcp_server_from_tool_name=MagicMock(return_value=None), + get_mcp_server_by_name=MagicMock(return_value=None), + ) + monkeypatch.setattr( + "litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager", + fake_manager, + ) + monkeypatch.setitem( + sys.modules, + "litellm.proxy.proxy_server", + types.SimpleNamespace(proxy_logging_obj=MagicMock()), + ) + return call_tool + + +def _make_iterator(initial_chunks) -> MCPEnhancedStreamingIterator: + return MCPEnhancedStreamingIterator( + base_iterator=_FakeAsyncStream(initial_chunks), + mcp_events=[], + tool_server_map={"read_wiki_contents": "deepwiki"}, + mcp_tools_with_litellm_proxy=[{"require_approval": "never"}], + user_api_key_auth=None, + original_request_params={ + "model": "gpt-4", + "input": "what is berriai/litellm?", + "tools": [{"type": "mcp"}], + }, + ) + + +@pytest.mark.asyncio +async def test_second_round_tool_call_is_executed_and_reaches_final_text(monkeypatch): + """ + Regression test: a tool call that errors, followed by the model retrying + the tool in its follow-up turn, must have that second tool call executed + too — the stream should not silently end after round 1 with no final + text (see PR discussion: deepwiki tool call errors, model retries once, + reply used to just stop with no explanation). + """ + call_tool = _mock_mcp_environment(monkeypatch) + + aresponses_mock = AsyncMock( + side_effect=[ + _tool_call_stream("call_2", "read_wiki_contents"), # round 2: model retries + _text_only_stream("Here's what I found after retrying."), # round 3: final answer + ] + ) + monkeypatch.setattr(responses_main_module, "aresponses", aresponses_mock) + + iterator = _make_iterator( + [ + _output_item_added_chunk(), + _completed_chunk([_function_call("call_1", "read_wiki_contents")]), # round 1: errors + ] + ) + + chunks = [chunk async for chunk in iterator] + + # Both rounds' tool calls were actually executed, not just streamed unexecuted. + assert call_tool.call_count == 2 + assert iterator.tool_call_round == 2 + + # The stream reached round 3 and produced the final text response instead + # of stopping after round 1 or round 2. + completed_chunks = [c for c in chunks if getattr(c, "type", None) == ResponsesAPIStreamEvents.RESPONSE_COMPLETED] + assert len(completed_chunks) == 3 + final_output = completed_chunks[-1].response.output + assert final_output[0]["content"][0]["text"] == "Here's what I found after retrying." + + +@pytest.mark.asyncio +async def test_tool_call_rounds_are_capped(monkeypatch): + """ + A model that keeps calling tools every round must not loop forever — + auto-execution stops at MAX_MCP_TOOL_CALL_ROUNDS, and the follow-up made + at the cap drops "tools" from the request so the model is forced to + answer in text instead of the stream just hanging. + """ + call_tool = _mock_mcp_environment(monkeypatch) + + # Rounds 2..MAX_MCP_TOOL_CALL_ROUNDS keep calling the tool; the call made + # once the cap is hit returns a text-only response. + tool_call_streams = [ + _tool_call_stream(f"call_{i}", "read_wiki_contents") for i in range(2, MAX_MCP_TOOL_CALL_ROUNDS + 1) + ] + aresponses_mock = AsyncMock(side_effect=[*tool_call_streams, _text_only_stream("giving up on tools")]) + monkeypatch.setattr(responses_main_module, "aresponses", aresponses_mock) + + iterator = _make_iterator( + [ + _output_item_added_chunk(), + _completed_chunk([_function_call("call_1", "read_wiki_contents")]), + ] + ) + + _ = [chunk async for chunk in iterator] + + assert iterator.tool_call_round == MAX_MCP_TOOL_CALL_ROUNDS + assert call_tool.call_count == MAX_MCP_TOOL_CALL_ROUNDS + assert aresponses_mock.call_count == MAX_MCP_TOOL_CALL_ROUNDS + + # Every follow-up before the cap still offered tools; only the capped one drops them. + for call in aresponses_mock.call_args_list[:-1]: + assert "tools" in call.kwargs + assert "tools" not in aresponses_mock.call_args_list[-1].kwargs diff --git a/tests/test_litellm/responses/test_custom_tool_call.py b/tests/test_litellm/responses/test_custom_tool_call.py new file mode 100644 index 00000000000..c605ef24934 --- /dev/null +++ b/tests/test_litellm/responses/test_custom_tool_call.py @@ -0,0 +1,343 @@ +""" +Test custom_tool_call adaptation for apply_patch and other custom tools. + +This test verifies that when Codex sends custom tools (type="custom"), +LiteLLM bridge correctly: +1. Converts them to function tools for Chat Completions providers +2. Converts function_call responses back to custom_tool_call output items +3. Unwraps the JSON-wrapping arguments to extract the actual input content +""" + +import json +import pytest +from typing import Dict, Any, List + +from openai.types.responses import ResponseFunctionToolCall + +from litellm.responses.litellm_completion_transformation.transformation import ( + LiteLLMCompletionResponsesConfig, +) +from litellm.responses.litellm_completion_transformation.custom_tools import ( + extract_custom_tool_names, + is_custom_tool_call, + unwrap_custom_tool_arguments, + build_tool_call_item_kwargs, + convert_custom_tool_to_function_tool, + _MAX_ARGUMENTS_LEN, +) + +from litellm.types.responses.main import CustomToolCallOutputItem + + +class TestCustomToolUtilities: + """Test the custom_tools utility functions.""" + + def test_extract_custom_tool_names(self): + """Test extraction of custom tool names from tools list.""" + tools = [ + {"type": "function", "name": "regular_tool"}, + {"type": "custom", "name": "apply_patch"}, + {"type": "function", "name": "another_tool"}, + {"type": "custom", "name": "custom_format"}, + ] + + names = extract_custom_tool_names(tools) + assert names == {"apply_patch", "custom_format"} + + def test_extract_custom_tool_names_empty(self): + """Test extraction with no custom tools.""" + tools = [ + {"type": "function", "name": "tool1"}, + {"type": "function", "name": "tool2"}, + ] + + names = extract_custom_tool_names(tools) + assert names == set() + + def test_extract_custom_tool_names_none(self): + """Test extraction with None input.""" + names = extract_custom_tool_names(None) + assert names == set() + + def test_is_custom_tool_call_true(self): + """Test identification of custom tool call.""" + custom_names = {"apply_patch", "custom_format"} + assert is_custom_tool_call("apply_patch", custom_names) is True + assert is_custom_tool_call("custom_format", custom_names) is True + + def test_is_custom_tool_call_false(self): + """Test identification of non-custom tool call.""" + custom_names = {"apply_patch"} + assert is_custom_tool_call("regular_tool", custom_names) is False + assert is_custom_tool_call("unknown_tool", custom_names) is False + + def test_unwrap_custom_tool_arguments(self): + """Test unwrapping of JSON-wrapped arguments.""" + # Test with valid JSON + wrapped = json.dumps({"content": "*** Begin Patch\n*** Add File: test.py\n+hello\n*** End Patch"}) + unwrapped = unwrap_custom_tool_arguments(wrapped) + assert unwrapped == "*** Begin Patch\n*** Add File: test.py\n+hello\n*** End Patch" + + def test_unwrap_custom_tool_arguments_invalid_json(self): + """Test unwrapping with invalid JSON returns original.""" + raw = "*** Begin Patch\n*** Add File: test.py\n+hello\n*** End Patch" + unwrapped = unwrap_custom_tool_arguments(raw) + assert unwrapped == raw + + def test_unwrap_custom_tool_arguments_no_content_key(self): + """Test unwrapping with JSON but no content key.""" + wrapped = json.dumps({"other_key": "value"}) + unwrapped = unwrap_custom_tool_arguments(wrapped) + assert unwrapped == wrapped + + def test_build_tool_call_item_kwargs_custom_completed(self): + """A completed custom tool call unwraps the content into `input`.""" + wrapped = json.dumps({"content": "patch body"}) + kwargs = build_tool_call_item_kwargs( + call_id="c1", + name="apply_patch", + arguments_or_input=wrapped, + status="completed", + custom_tool_names={"apply_patch"}, + ) + assert kwargs["type"] == "custom_tool_call" + assert kwargs["input"] == "patch body" + assert "arguments" not in kwargs + + def test_build_tool_call_item_kwargs_custom_in_progress(self): + """An in-progress custom tool call seeds an empty input string.""" + kwargs = build_tool_call_item_kwargs( + call_id="c2", + name="apply_patch", + arguments_or_input="ignored-until-completed", + status="in_progress", + custom_tool_names={"apply_patch"}, + ) + assert kwargs["input"] == "" + + def test_build_tool_call_item_kwargs_regular_function(self): + """A regular function call keeps raw arguments and uses function_call type.""" + raw = json.dumps({"k": "v"}) + kwargs = build_tool_call_item_kwargs( + call_id="c3", + name="get_weather", + arguments_or_input=raw, + status="completed", + custom_tool_names=set(), + ) + assert kwargs["type"] == "function_call" + assert kwargs["arguments"] == raw + assert "input" not in kwargs + + def test_unwrap_custom_tool_arguments_oversized_returns_raw(self): + """Arguments larger than the safety cap are returned unchanged to avoid + OOM on JSON parsing a pathologically large string.""" + oversized = "x" * (_MAX_ARGUMENTS_LEN + 1) + assert unwrap_custom_tool_arguments(oversized) == oversized + + def test_unwrap_custom_tool_arguments_empty(self): + """Empty arguments unwrap to an empty string, not the raw input.""" + assert unwrap_custom_tool_arguments("") == "" + + def test_convert_custom_tool_to_function_tool_with_format(self): + """The grammar definition is embedded in the description so the model can + produce correctly-formatted output.""" + tool = { + "type": "custom", + "name": "apply_patch", + "description": "Apply a patch", + "format": { + "type": "grammar", + "syntax": "lark", + "definition": "start: begin_patch", + }, + } + result = convert_custom_tool_to_function_tool(tool) + assert result is not None + assert result["type"] == "function" + assert "begin_patch" in result["function"]["description"] + assert result["function"]["parameters"]["required"] == ["content"] + + def test_convert_custom_tool_to_function_tool_non_custom_returns_none(self): + """Non-custom tools are not convertible; the caller keeps them as-is.""" + assert convert_custom_tool_to_function_tool({"type": "function"}) is None + + +class TestTransformationCustomTools: + """Test custom tool handling in transformation logic.""" + + def test_transform_apply_patch_function_call_to_custom_tool_call(self): + """Test that apply_patch function_call is converted to custom_tool_call.""" + # Simulate a Chat Completion response with apply_patch function call + from litellm.types.utils import ModelResponse, Choices, Message, ChatCompletionMessageToolCall, Function + + tool_call = ChatCompletionMessageToolCall( + id="call_abc123", + type="function", + function=Function( + name="apply_patch", + arguments=json.dumps({"content": "*** Begin Patch\n*** Add File: test.py\n+hello\n*** End Patch"}), + ), + ) + + message = Message(role="assistant", content=None, tool_calls=[tool_call]) + + choices = [Choices(index=0, message=message, finish_reason="tool_calls")] + + response = ModelResponse( + id="test_response", choices=choices, created=1234567890, model="gpt-4", object="chat.completion" + ) + + # Transform with custom tool names + responses_api_request = { + "tools": [{"type": "custom", "name": "apply_patch"}, {"type": "function", "name": "regular_tool"}] + } + + result = LiteLLMCompletionResponsesConfig.transform_chat_completion_tools_to_responses_tools( + response, responses_api_request=responses_api_request + ) + + # Should return a CustomToolCallOutputItem object; ResponsesAPIResponse + # accepts it directly via its output item union. + assert len(result) == 1 + item = result[0] + assert isinstance(item, CustomToolCallOutputItem) + assert item.type == "custom_tool_call" + assert item.call_id == "call_abc123" + assert item.name == "apply_patch" + assert item.input == "*** Begin Patch\n*** Add File: test.py\n+hello\n*** End Patch" + assert item.status == "completed" + + def test_custom_tool_call_input_item_recovers_payload_from_input(self): + """A custom_tool_call input item stores its payload in `input`; the + assistant tool call must carry it as a JSON content envelope whether + `arguments` is missing or an empty string.""" + for arguments in (None, ""): + item = { + "type": "custom_tool_call", + "call_id": "call_1", + "name": "apply_patch", + "input": "*** Begin Patch\n+hello\n*** End Patch", + } + if arguments is not None: + item["arguments"] = arguments + messages = ( + LiteLLMCompletionResponsesConfig._transform_responses_api_function_call_to_chat_completion_message( + function_call=item + ) + ) + tool_call = messages[0]["tool_calls"][0] + assert tool_call["function"]["arguments"] == json.dumps( + {"content": "*** Begin Patch\n+hello\n*** End Patch"} + ) + + def test_function_call_input_item_with_empty_arguments_keeps_them_empty(self): + """A plain function_call input item with empty or missing `arguments` + must produce an empty arguments string, never a `{"content": ...}` + envelope (that recovery is reserved for custom_tool_call items) and + never the literal string "None".""" + for item in ( + { + "type": "function_call", + "call_id": "call_2", + "name": "get_weather", + "arguments": "", + "input": "stray value", + }, + { + "type": "function_call", + "call_id": "call_3", + "name": "get_weather", + }, + ): + messages = ( + LiteLLMCompletionResponsesConfig._transform_responses_api_function_call_to_chat_completion_message( + function_call=item + ) + ) + tool_call = messages[0]["tool_calls"][0] + assert tool_call["function"]["arguments"] == "" + + def test_transform_regular_function_call_unchanged(self): + """Test that regular function calls remain as ResponseFunctionToolCall.""" + from litellm.types.utils import ModelResponse, Choices, Message, ChatCompletionMessageToolCall, Function + + tool_call = ChatCompletionMessageToolCall( + id="call_xyz789", + type="function", + function=Function(name="regular_tool", arguments=json.dumps({"param": "value"})), + ) + + message = Message(role="assistant", content=None, tool_calls=[tool_call]) + + choices = [Choices(index=0, message=message, finish_reason="tool_calls")] + + response = ModelResponse( + id="test_response", choices=choices, created=1234567890, model="gpt-4", object="chat.completion" + ) + + # Transform with custom tool names (regular_tool is NOT custom) + responses_api_request = { + "tools": [{"type": "custom", "name": "apply_patch"}, {"type": "function", "name": "regular_tool"}] + } + + result = LiteLLMCompletionResponsesConfig.transform_chat_completion_tools_to_responses_tools( + response, responses_api_request=responses_api_request + ) + + # Should return ResponseFunctionToolCall + assert len(result) == 1 + item = result[0] + assert isinstance(item, ResponseFunctionToolCall) + assert item.type == "function_call" + assert item.name == "regular_tool" + assert item.arguments == json.dumps({"param": "value"}) + + def test_transform_mixed_tool_calls(self): + """Test transformation with both custom and regular tool calls.""" + from litellm.types.utils import ModelResponse, Choices, Message, ChatCompletionMessageToolCall, Function + + custom_call = ChatCompletionMessageToolCall( + id="call_001", + type="function", + function=Function(name="apply_patch", arguments=json.dumps({"content": "patch content"})), + ) + + regular_call = ChatCompletionMessageToolCall( + id="call_002", type="function", function=Function(name="get_weather", arguments=json.dumps({"city": "SF"})) + ) + + message = Message(role="assistant", content=None, tool_calls=[custom_call, regular_call]) + + choices = [Choices(index=0, message=message, finish_reason="tool_calls")] + + response = ModelResponse( + id="test_response", choices=choices, created=1234567890, model="gpt-4", object="chat.completion" + ) + + responses_api_request = { + "tools": [{"type": "custom", "name": "apply_patch"}, {"type": "function", "name": "get_weather"}] + } + + result = LiteLLMCompletionResponsesConfig.transform_chat_completion_tools_to_responses_tools( + response, responses_api_request=responses_api_request + ) + + assert len(result) == 2 + + # First should be custom_tool_call object + first = result[0] + assert isinstance(first, CustomToolCallOutputItem) + assert first.type == "custom_tool_call" + assert first.name == "apply_patch" + assert first.input == "patch content" + + # Second should be function_call + second = result[1] + assert isinstance(second, ResponseFunctionToolCall) + assert second.type == "function_call" + assert second.name == "get_weather" + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/tests/test_litellm/responses/test_responses_api_request_body.py b/tests/test_litellm/responses/test_responses_api_request_body.py index e312a11e893..c39ba75bd97 100644 --- a/tests/test_litellm/responses/test_responses_api_request_body.py +++ b/tests/test_litellm/responses/test_responses_api_request_body.py @@ -1,6 +1,7 @@ """ Test that litellm.responses() / litellm.aresponses() send the expected request body -over the wire. Expected JSON bodies are stored in expected_responses_api_request/. +over the wire and surface provider errors correctly. Expected JSON bodies are stored +in expected_responses_api_request/. """ import json @@ -18,24 +19,20 @@ def _expected_dir() -> Path: return Path(__file__).resolve().parent.parent / "expected_responses_api_request" -@pytest.mark.asyncio -async def test_aresponses_context_management_and_shell_request_body_matches_expected(): - """ - Call litellm.aresponses() with context_management and shell tool; - assert the httpx POST request body matches the expected JSON. - """ - expected_path = _expected_dir() / "context_management_and_shell.json" +def _load_expected_body(filename: str) -> dict: + expected_path = _expected_dir() / filename assert expected_path.exists(), f"Expected file not found: {expected_path}" with open(expected_path) as f: - expected_body = json.load(f) + return json.load(f) - # Minimal Responses API response so parsing succeeds - mock_response = { - "id": "resp_ctx_shell_test", + +def _minimal_responses_api_payload(response_id: str, model: str) -> dict: + return { + "id": response_id, "object": "response", "created_at": 1734366691, "status": "completed", - "model": "gpt-4o", + "model": model, "output": [ { "type": "message", @@ -69,21 +66,41 @@ async def test_aresponses_context_management_and_shell_request_body_matches_expe "user": None, } - class MockResponse: - def __init__(self, json_data, status_code=200): - self._json_data = json_data - self.status_code = status_code - self.text = json.dumps(json_data) - self.headers = httpx.Headers({}) - def json(self): - return self._json_data +class MockResponse: + def __init__(self, json_data, status_code=200): + self._json_data = json_data + self.status_code = status_code + self.text = json.dumps(json_data) + self.headers = httpx.Headers({}) + + def json(self): + return self._json_data + + +def _assert_request_body_matches(request_body: dict, expected_body: dict) -> None: + for key, expected_value in expected_body.items(): + assert key in request_body, f"Missing key in request body: {key}" + assert ( + request_body[key] == expected_value + ), f"Mismatch for key {key}: got {request_body[key]!r}, expected {expected_value!r}" + + +@pytest.mark.asyncio +async def test_aresponses_context_management_and_shell_request_body_matches_expected(): + """ + Call litellm.aresponses() with context_management and shell tool; + assert the httpx POST request body matches the expected JSON. + """ + expected_body = _load_expected_body("context_management_and_shell.json") with patch( "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", new_callable=AsyncMock, ) as mock_post: - mock_post.return_value = MockResponse(mock_response, 200) + mock_post.return_value = MockResponse( + _minimal_responses_api_payload("resp_ctx_shell_test", "gpt-4o"), 200 + ) await litellm.aresponses( model="openai/gpt-4o", @@ -95,10 +112,87 @@ async def test_aresponses_context_management_and_shell_request_body_matches_expe ) mock_post.assert_called_once() - request_body = mock_post.call_args.kwargs["json"] + _assert_request_body_matches(mock_post.call_args.kwargs["json"], expected_body) - for key, expected_value in expected_body.items(): - assert key in request_body, f"Missing key in request body: {key}" - assert ( - request_body[key] == expected_value - ), f"Mismatch for key {key}: got {request_body[key]!r}, expected {expected_value!r}" + +@pytest.mark.asyncio +async def test_aresponses_azure_shell_tool_request_body_matches_expected(): + """ + Call litellm.aresponses() on the Azure route with the shell tool; + assert the httpx POST request body carries the shell tool verbatim. + """ + expected_body = _load_expected_body("azure_shell_tool.json") + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + new_callable=AsyncMock, + ) as mock_post: + mock_post.return_value = MockResponse( + _minimal_responses_api_payload("resp_azure_shell_test", "gpt-5-mini"), 200 + ) + + await litellm.aresponses( + model="azure/gpt-5-mini", + api_base="https://fake-resource.openai.azure.com", + api_key="fake-api-key", + api_version="2025-03-01-preview", + input=expected_body["input"], + tools=expected_body["tools"], + tool_choice=expected_body["tool_choice"], + max_output_tokens=expected_body["max_output_tokens"], + ) + + mock_post.assert_called_once() + _assert_request_body_matches(mock_post.call_args.kwargs["json"], expected_body) + + +@pytest.mark.asyncio +async def test_aresponses_azure_shell_tool_400_maps_to_bad_request_error(): + """ + Azure rejects the shell tool for unsupported deployments with a 400; + litellm must surface that as litellm.BadRequestError carrying the provider message. + """ + error_body = { + "error": { + "message": "Tool of type 'shell' is not supported with this model.", + "type": "invalid_request_error", + "param": "tools", + "code": None, + } + } + + def _raise_azure_400(*args, **kwargs): + response = httpx.Response( + status_code=400, + json=error_body, + request=httpx.Request( + "POST", + kwargs.get( + "url", + "https://fake-resource.openai.azure.com/openai/responses", + ), + ), + ) + response.raise_for_status() + + with patch( + "litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", + new_callable=AsyncMock, + ) as mock_post: + mock_post.side_effect = _raise_azure_400 + + with pytest.raises(litellm.BadRequestError) as excinfo: + await litellm.aresponses( + model="azure/gpt-5-mini", + api_base="https://fake-resource.openai.azure.com", + api_key="fake-api-key", + api_version="2025-03-01-preview", + input="List files in /mnt/data and run python --version.", + tools=[{"type": "shell", "environment": {"type": "container_auto"}}], + tool_choice="auto", + max_output_tokens=256, + ) + + assert excinfo.value.status_code == 400 + assert "shell" in str(excinfo.value).lower() + assert "not supported" in str(excinfo.value).lower() diff --git a/tests/test_litellm/responses/test_responses_streaming_iterator.py b/tests/test_litellm/responses/test_responses_streaming_iterator.py new file mode 100644 index 00000000000..9ba7dfcaa80 --- /dev/null +++ b/tests/test_litellm/responses/test_responses_streaming_iterator.py @@ -0,0 +1,158 @@ +""" +Regression tests for LIT-4210: the streaming iterators must never run the sync +success_handler on the thread-pool executor concurrently with +async_success_handler. Concurrent mutation of the shared response object / +model_call_details from two threads segfaults pydantic-core (customer pods +crashed with exit 139 whenever any CustomLogger was registered). +""" + +import asyncio +import time + +import httpx +import pytest + +import litellm +from litellm.integrations.custom_logger import CustomLogger +from litellm.litellm_core_utils import thread_pool_executor as thread_pool_executor_module +from litellm.responses import streaming_iterator as responses_streaming_iterator_module +from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging +from litellm.responses.streaming_iterator import ResponsesAPIStreamingIterator +from litellm.types.llms.openai import ResponsesAPIResponse + + +class RecordingCustomLogger(CustomLogger): + def __init__(self): + super().__init__() + self.async_hook_started: float | None = None + self.async_hook_finished: float | None = None + + async def _record(self): + self.async_hook_started = time.monotonic() + await asyncio.sleep(0.2) + self.async_hook_finished = time.monotonic() + + async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): + await self._record() + + async def async_log_stream_event(self, kwargs, response_obj, start_time, end_time): + await self._record() + + +class RecordingExecutor: + def __init__(self, inner): + self._inner = inner + self.submits: list = [] + + def submit(self, fn, *args, **kwargs): + self.submits.append((time.monotonic(), fn)) + return self._inner.submit(fn, *args, **kwargs) + + def submit_times_for(self, logging_obj) -> list: + return [t for t, fn in self.submits if getattr(fn, "__self__", None) is logging_obj] + + +@pytest.fixture(autouse=True) +def _isolate_callbacks(): + saved = ( + litellm.callbacks, + litellm.success_callback, + litellm._async_success_callback, + litellm.failure_callback, + litellm._async_failure_callback, + ) + yield + ( + litellm.callbacks, + litellm.success_callback, + litellm._async_success_callback, + litellm.failure_callback, + litellm._async_failure_callback, + ) = saved + + +@pytest.fixture +def recording_executor(monkeypatch): + recording = RecordingExecutor(thread_pool_executor_module.executor) + monkeypatch.setattr(thread_pool_executor_module, "executor", recording) + monkeypatch.setattr(responses_streaming_iterator_module, "executor", recording) + return recording + + +def _make_logging_obj() -> LitellmLogging: + logging_obj = LitellmLogging( + model="gpt-5.4-nano", + messages=[{"role": "user", "content": "hi"}], + stream=True, + call_type="aresponses", + start_time=time.time(), + litellm_call_id="lit-4210-test", + function_id="lit-4210-test", + ) + logging_obj.model_call_details["litellm_params"] = {"aresponses": True} + return logging_obj + + +def _make_iterator(logging_obj: LitellmLogging) -> ResponsesAPIStreamingIterator: + iterator = ResponsesAPIStreamingIterator( + response=httpx.Response(200), + model="gpt-5.4-nano", + responses_api_provider_config=None, + logging_obj=logging_obj, + ) + iterator.completed_response = ResponsesAPIResponse( + id="resp_lit4210", + created_at=1700000000.0, + model="gpt-5.4-nano", + object="response", + output=[], + parallel_tool_calls=True, + tool_choice="auto", + tools=[], + error=None, + incomplete_details=None, + instructions=None, + metadata={}, + temperature=1.0, + top_p=1.0, + ) + return iterator + + +@pytest.mark.asyncio +async def test_custom_logger_only_never_submits_sync_success_handler(recording_executor): + recorder = RecordingCustomLogger() + litellm.success_callback = [recorder] + litellm._async_success_callback = [recorder] + + logging_obj = _make_logging_obj() + iterator = _make_iterator(logging_obj) + + iterator._log_completed_response(is_async=True) + await asyncio.sleep(0.6) + + assert recorder.async_hook_started is not None + assert recording_executor.submit_times_for(logging_obj) == [] + + +@pytest.mark.asyncio +async def test_sync_callbacks_run_only_after_async_handler_completes(recording_executor): + recorder = RecordingCustomLogger() + sync_events: list = [] + + def sync_callback(kwargs, response_obj, start_time, end_time): + sync_events.append(time.monotonic()) + + litellm.success_callback = [recorder, sync_callback] + litellm._async_success_callback = [recorder] + + logging_obj = _make_logging_obj() + iterator = _make_iterator(logging_obj) + + iterator._log_completed_response(is_async=True) + await asyncio.sleep(0.8) + + assert recorder.async_hook_finished is not None + submit_times = recording_executor.submit_times_for(logging_obj) + assert len(submit_times) == 1 + assert submit_times[0] >= recorder.async_hook_finished diff --git a/tests/test_litellm/responses/test_responses_utils.py b/tests/test_litellm/responses/test_responses_utils.py index bd441321507..bbc137b959f 100644 --- a/tests/test_litellm/responses/test_responses_utils.py +++ b/tests/test_litellm/responses/test_responses_utils.py @@ -142,6 +142,26 @@ class TestResponsesAPIRequestUtils: assert decoded.get("model_id") == "gpt-4o" assert decoded.get("custom_llm_provider") == "openai" + + def test_update_responses_api_response_id_with_model_id_is_idempotent_for_litellm_ids(self): + raw = "resp_" + "a" * 48 + litellm_metadata = {"model_info": {"id": "model-123"}} + + once = ResponsesAPIRequestUtils._update_responses_api_response_id_with_model_id( + {"id": raw}, + custom_llm_provider="openai", + litellm_metadata=litellm_metadata, + ) + twice = ResponsesAPIRequestUtils._update_responses_api_response_id_with_model_id( + {"id": once["id"]}, + custom_llm_provider="openai", + litellm_metadata=litellm_metadata, + ) + + assert twice == once + assert ResponsesAPIRequestUtils.decode_previous_response_id_to_original_previous_response_id(twice["id"]) == raw + assert ResponsesAPIRequestUtils._decode_responses_api_response_id(once["id"]).get("response_id") == raw + def test_build_decode_container_id_omits_none_model_id(self): """model_id=None must not round-trip as the truthy string 'None'.""" encoded = ResponsesAPIRequestUtils._build_container_id( diff --git a/tests/test_litellm/responses/test_responses_websocket_all_providers.py b/tests/test_litellm/responses/test_responses_websocket_all_providers.py index 0d13ff4fd05..4509abc7749 100644 --- a/tests/test_litellm/responses/test_responses_websocket_all_providers.py +++ b/tests/test_litellm/responses/test_responses_websocket_all_providers.py @@ -1122,7 +1122,7 @@ class TestNativeWebSocketGuardrails: client_ws = MagicMock() client_ws.send_text = AsyncMock() logging_obj = MagicMock() - logging_obj.async_success_handler = AsyncMock() + logging_obj.dispatch_success_handlers = AsyncMock() delta_event = json.dumps( {"type": "response.output_text.delta", "delta": "alice@example.com"} @@ -1196,7 +1196,7 @@ class TestNativeWebSocketGuardrails: client_ws = MagicMock() client_ws.send_text = AsyncMock() logging_obj = MagicMock() - logging_obj.async_success_handler = AsyncMock() + logging_obj.dispatch_success_handlers = AsyncMock() done_events = [ json.dumps( @@ -1895,7 +1895,7 @@ class TestNativeWebSocketGuardrailMasking: ] ) logging_obj = MagicMock() - logging_obj.async_success_handler = AsyncMock() + logging_obj.dispatch_success_handlers = AsyncMock() handler = _make_streaming( websocket=websocket, @@ -1951,7 +1951,7 @@ class TestNativeWebSocketGuardrailMasking: ] ) logging_obj = MagicMock() - logging_obj.async_success_handler = AsyncMock() + logging_obj.dispatch_success_handlers = AsyncMock() handler = _make_streaming( websocket=websocket, @@ -2014,7 +2014,7 @@ class TestNativeWebSocketGuardrailMasking: ] ) logging_obj = MagicMock() - logging_obj.async_success_handler = AsyncMock() + logging_obj.dispatch_success_handlers = AsyncMock() handler = _make_streaming( websocket=websocket, @@ -2077,7 +2077,7 @@ class TestNativeWebSocketGuardrailMasking: ] ) logging_obj = MagicMock() - logging_obj.async_success_handler = AsyncMock() + logging_obj.dispatch_success_handlers = AsyncMock() handler = _make_streaming( websocket=websocket, diff --git a/tests/test_litellm/responses/test_streaming_iterator.py b/tests/test_litellm/responses/test_streaming_iterator.py new file mode 100644 index 00000000000..3e4b07fce18 --- /dev/null +++ b/tests/test_litellm/responses/test_streaming_iterator.py @@ -0,0 +1,124 @@ +"""Regression tests for LIT-4185 — /v1/responses streaming must stamp +completion_start_time on the first chunk so downstream TTFT consumers +(Prometheus, OTEL, SpendLogs completionStartTime) do not fall back to +completion_start_time = end_time.""" + +import json +from datetime import datetime +from unittest.mock import Mock + +import pytest + +from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig +from litellm.responses.streaming_iterator import ResponsesAPIStreamingIterator +from litellm.types.llms.openai import ( + ResponseCompletedEvent, + ResponsesAPIResponse, + ResponsesAPIStreamEvents, +) + + +def _sse_event(payload: dict) -> bytes: + return f"data: {json.dumps(payload)}\n\n".encode("utf-8") + + +def _make_iterator( + *, + sse_events: list[bytes], + logging_obj: LiteLLMLoggingObj, +) -> ResponsesAPIStreamingIterator: + async def aiter_bytes(): + for evt in sse_events: + yield evt + + mock_response = Mock() + mock_response.headers = {} + mock_response.aiter_bytes = aiter_bytes + + mock_config = Mock(spec=BaseResponsesAPIConfig) + mock_responses_api_response = Mock(spec=ResponsesAPIResponse) + mock_responses_api_response.id = "resp_ttft" + + def _transform(model, parsed_chunk, logging_obj): + evt_type = parsed_chunk.get("type") + if evt_type == "response.completed": + completed = Mock(spec=ResponseCompletedEvent) + completed.type = ResponsesAPIStreamEvents.RESPONSE_COMPLETED + completed.response = mock_responses_api_response + return completed + stub = Mock() + stub.type = evt_type + return stub + + mock_config.transform_streaming_response.side_effect = _transform + + return ResponsesAPIStreamingIterator( + response=mock_response, + model="gpt-4o-mini", + responses_api_provider_config=mock_config, + logging_obj=logging_obj, + litellm_metadata={}, + custom_llm_provider="openai", + ) + + +@pytest.mark.asyncio +async def test_responses_streaming_stamps_completion_start_time_on_first_chunk(): + """Without the fix, `logging_obj.completion_start_time` stays None across the + entire stream and _success_handler_helper_fn falls back to end_time — collapsing + the reported TTFT to full generation time.""" + logging_obj = Mock(spec=LiteLLMLoggingObj) + logging_obj.completion_start_time = None + logging_obj.model_call_details = {"litellm_params": {}} + stamped: list[datetime] = [] + + def _update(*, completion_start_time): + stamped.append(completion_start_time) + logging_obj.completion_start_time = completion_start_time + logging_obj.model_call_details["completion_start_time"] = completion_start_time + + logging_obj._update_completion_start_time.side_effect = _update + + iterator = _make_iterator( + sse_events=[ + _sse_event({"type": "response.created"}), + _sse_event({"type": "response.output_text.delta", "delta": "hi"}), + _sse_event({"type": "response.completed"}), + ], + logging_obj=logging_obj, + ) + + async for _ in iterator: + pass + + assert len(stamped) == 1, ( + f"Expected exactly one first-chunk stamp; got {len(stamped)}. " + "Later chunks must not re-stamp completion_start_time." + ) + assert isinstance(stamped[0], datetime) + + +@pytest.mark.asyncio +async def test_responses_streaming_does_not_reset_prior_completion_start_time(): + """If `completion_start_time` is already set (e.g. by an outer wrapper), the + iterator must not overwrite it — otherwise TTFT would collapse to + time-to-last-chunk under contention.""" + prior = datetime(2020, 1, 1, 0, 0, 0) + logging_obj = Mock(spec=LiteLLMLoggingObj) + logging_obj.completion_start_time = prior + logging_obj.model_call_details = {"litellm_params": {}} + + iterator = _make_iterator( + sse_events=[ + _sse_event({"type": "response.created"}), + _sse_event({"type": "response.completed"}), + ], + logging_obj=logging_obj, + ) + + async for _ in iterator: + pass + + logging_obj._update_completion_start_time.assert_not_called() + assert logging_obj.completion_start_time == prior diff --git a/tests/test_litellm/responses/test_streaming_iterator_error_events.py b/tests/test_litellm/responses/test_streaming_iterator_error_events.py new file mode 100644 index 00000000000..1a2dcd0fcb7 --- /dev/null +++ b/tests/test_litellm/responses/test_streaming_iterator_error_events.py @@ -0,0 +1,357 @@ +""" +Regression: in-stream error events (type="error", type="response.failed") must +raise instead of being returned as benign chunks, mirroring chat streaming +semantics (_handle_stream_fallback_error): non-retriable 4xx (except 429) +raise litellm.APIError directly; 429 and 5xx are wrapped in +MidStreamFallbackError so the Router's mid-stream fallback machinery fires. + +Status mapping must consider both the OpenAI error `type` (e.g. +"invalid_request_error") and `code` (e.g. "invalid_prompt", +"rate_limit_exceeded") fields — previously only `code` was read, so +type-classified client errors fell through to 500. + +Also covers: ErrorEventError.param must accept dict payloads without raising a +Pydantic ValidationError (previously typed as Optional[str]). +""" + +import json +import os +import sys +from unittest.mock import Mock, patch + +import pytest + +sys.path.insert(0, os.path.abspath("../..")) + +import litellm +from litellm.exceptions import MidStreamFallbackError +from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig +from litellm.responses.streaming_iterator import ( + BaseResponsesAPIStreamingIterator, + ResponsesAPIStreamingIterator, + SyncResponsesAPIStreamingIterator, +) +from litellm.types.llms.openai import ( + ErrorEvent, + ErrorEventError, + ResponseAPIUsage, + ResponsesAPIStreamEvents, +) + + +def _make_iterator() -> BaseResponsesAPIStreamingIterator: + mock_logging_obj = Mock(spec=LiteLLMLoggingObj) + mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_config = Mock(spec=BaseResponsesAPIConfig) + mock_response = Mock() + mock_response.headers = {} + return BaseResponsesAPIStreamingIterator( + response=mock_response, + model="gpt-5", + responses_api_provider_config=mock_config, + logging_obj=mock_logging_obj, + custom_llm_provider="openai", + ) + + +def _make_error_chunk(error_type: str, code: str, message: str = "err") -> ErrorEvent: + error_obj = ErrorEventError(type=error_type, code=code, message=message) + return ErrorEvent(type=ResponsesAPIStreamEvents.ERROR, sequence_number=0, error=error_obj) + + +def test_maybe_raise_for_error_event_wraps_unknown_error_in_mid_stream_fallback(): + iterator = _make_iterator() + chunk = _make_error_chunk("server_error", "internal_error", "something went wrong") + with pytest.raises(MidStreamFallbackError) as exc_info: + iterator._maybe_raise_for_error_event(chunk) + assert exc_info.value.status_code == 500 + assert isinstance(exc_info.value.original_exception, litellm.APIError) + assert exc_info.value.original_exception.status_code == 500 + + +def test_maybe_raise_for_error_event_maps_rate_limit_code_to_429_mid_stream_fallback(): + """429 is retriable: it must be wrapped so the Router can fall back, carrying the mapped APIError.""" + iterator = _make_iterator() + chunk = _make_error_chunk("tokens", "rate_limit_exceeded", "Too many requests") + with pytest.raises(MidStreamFallbackError) as exc_info: + iterator._maybe_raise_for_error_event(chunk) + assert exc_info.value.status_code == 429 + assert exc_info.value.generated_content == "" + assert exc_info.value.is_pre_first_chunk is True + assert isinstance(exc_info.value.original_exception, litellm.APIError) + assert exc_info.value.original_exception.status_code == 429 + + +def test_maybe_raise_for_error_event_maps_invalid_request_type_to_400(): + """Client errors classified via the `type` field must raise APIError directly (no fallback).""" + iterator = _make_iterator() + chunk = _make_error_chunk("invalid_request_error", "invalid_prompt", "bad request") + with pytest.raises(litellm.APIError) as exc_info: + iterator._maybe_raise_for_error_event(chunk) + assert exc_info.value.status_code == 400 + assert not isinstance(exc_info.value, MidStreamFallbackError) + + +def test_maybe_raise_for_error_event_maps_context_length_code_to_400(): + """Client errors classified via the `code` field alone must still map to 400.""" + iterator = _make_iterator() + chunk = Mock() + chunk.type = "error" + chunk.error = {"code": "context_length_exceeded", "message": "too long"} + with pytest.raises(litellm.APIError) as exc_info: + iterator._maybe_raise_for_error_event(chunk) + assert exc_info.value.status_code == 400 + assert not isinstance(exc_info.value, MidStreamFallbackError) + + +def test_maybe_raise_for_error_event_maps_insufficient_quota_to_429(): + """OpenAI returns HTTP 429 for insufficient_quota; it must not map to 400 even though its type + is invalid_request_error-adjacent, and it must be wrapped for fallback.""" + iterator = _make_iterator() + chunk = _make_error_chunk("invalid_request_error", "insufficient_quota", "You exceeded your current quota") + with pytest.raises(MidStreamFallbackError) as exc_info: + iterator._maybe_raise_for_error_event(chunk) + assert exc_info.value.status_code == 429 + + +def test_maybe_raise_for_error_event_passes_through_normal_chunk(): + iterator = _make_iterator() + chunk = Mock() + chunk.type = ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA + iterator._maybe_raise_for_error_event(chunk) # must not raise + + +def test_error_event_error_param_accepts_dict(): + error_obj = ErrorEventError( + type="invalid_request_error", + code="context_length_exceeded", + message="too long", + param={"field": "messages", "index": 0}, + ) + assert isinstance(error_obj.param, dict) + + +def _make_async_iterator_with_events(events: list) -> ResponsesAPIStreamingIterator: + sse_payload = b"".join(f"data: {json.dumps(event)}\n\n".encode() for event in events) + + async def mock_aiter_bytes(): + yield sse_payload + + mock_response = Mock() + mock_response.headers = {} + mock_response.aiter_bytes = mock_aiter_bytes + mock_logging_obj = Mock(spec=LiteLLMLoggingObj) + mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None + mock_config = Mock(spec=BaseResponsesAPIConfig) + + def transform(model, parsed_chunk, logging_obj): + if parsed_chunk.get("type") == "error": + return ErrorEvent( + type=ResponsesAPIStreamEvents.ERROR, + sequence_number=0, + error=ErrorEventError(**parsed_chunk["error"]), + ) + delta_event = Mock() + delta_event.type = ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA + delta_event.delta = parsed_chunk.get("delta", "") + return delta_event + + mock_config.transform_streaming_response.side_effect = transform + + return ResponsesAPIStreamingIterator( + response=mock_response, + model="gpt-5", + responses_api_provider_config=mock_config, + logging_obj=mock_logging_obj, + custom_llm_provider="openai", + ) + + +@pytest.mark.asyncio +async def test_async_iterator_raises_mid_stream_fallback_on_rate_limit_error_event(): + iterator = _make_async_iterator_with_events( + [ + { + "type": "error", + "error": {"type": "tokens", "code": "rate_limit_exceeded", "message": "rate limited"}, + } + ] + ) + + with pytest.raises(MidStreamFallbackError) as exc_info: + async for _ in iterator: + pass + assert exc_info.value.status_code == 429 + assert exc_info.value.is_pre_first_chunk is True + assert exc_info.value.generated_content == "" + assert isinstance(exc_info.value.original_exception, litellm.APIError) + assert exc_info.value.original_exception.status_code == 429 + + +@pytest.mark.asyncio +async def test_async_iterator_error_after_first_chunk_carries_generated_content(): + """An error after streamed output must expose the accumulated text so the router's + fallback can build a continuation input instead of restarting from scratch.""" + iterator = _make_async_iterator_with_events( + [ + {"type": "response.output_text.delta", "delta": "hello "}, + {"type": "response.output_text.delta", "delta": "world"}, + { + "type": "error", + "error": {"type": "server_error", "code": "internal_error", "message": "boom"}, + }, + ] + ) + + chunks = [] + with pytest.raises(MidStreamFallbackError) as exc_info: + async for chunk in iterator: + chunks.append(chunk) + assert len(chunks) == 2 + assert exc_info.value.status_code == 500 + assert exc_info.value.is_pre_first_chunk is False + assert exc_info.value.generated_content == "hello world" + + +def test_maybe_raise_for_response_failed_event_with_dict_error(): + """response.failed chunks carry a dict error on .response.error; covers dict branch.""" + iterator = _make_iterator() + mock_response_obj = Mock() + mock_response_obj.error = {"type": "tokens", "code": "rate_limit_exceeded", "message": "throttled"} + chunk = Mock() + chunk.type = "response.failed" + chunk.response = mock_response_obj + with pytest.raises(MidStreamFallbackError) as exc_info: + iterator._maybe_raise_for_error_event(chunk) + assert exc_info.value.status_code == 429 + + +def test_maybe_raise_for_error_event_null_error_obj(): + """error chunk with no error field: message and code default; wrapped as 500.""" + iterator = _make_iterator() + chunk = Mock() + chunk.type = "error" + chunk.error = None + with pytest.raises(MidStreamFallbackError) as exc_info: + iterator._maybe_raise_for_error_event(chunk) + assert exc_info.value.status_code == 500 + assert "Response API in-stream error" in str(exc_info.value) + + +def _make_failed_chunk(error: dict, usage: ResponseAPIUsage | None = None) -> Mock: + mock_response_obj = Mock() + mock_response_obj.error = error + mock_response_obj.usage = usage + chunk = Mock() + chunk.type = "response.failed" + chunk.response = mock_response_obj + return chunk + + +def test_handle_logging_failed_response_maps_rate_limit_to_429(): + """The exception logged to failure handlers must carry the mapped status, not a hardcoded 500.""" + iterator = _make_iterator() + iterator.completed_response = _make_failed_chunk( + {"type": "tokens", "code": "rate_limit_exceeded", "message": "throttled"} + ) + with ( + patch("litellm.responses.streaming_iterator.run_async_function") as mock_run_async, + patch("litellm.responses.streaming_iterator.executor"), + ): + iterator._handle_logging_failed_response() + logged_exception = mock_run_async.call_args.kwargs["exception"] + assert isinstance(logged_exception, litellm.APIError) + assert logged_exception.status_code == 429 + assert "throttled" in str(logged_exception) + + +def test_handle_logging_failed_response_maps_type_field_to_400(): + """Status derivation for failed-response logging must also read the error `type` field.""" + iterator = _make_iterator() + iterator.completed_response = _make_failed_chunk( + {"type": "invalid_request_error", "code": "invalid_prompt", "message": "bad prompt"} + ) + with ( + patch("litellm.responses.streaming_iterator.run_async_function") as mock_run_async, + patch("litellm.responses.streaming_iterator.executor"), + ): + iterator._handle_logging_failed_response() + logged_exception = mock_run_async.call_args.kwargs["exception"] + assert isinstance(logged_exception, litellm.APIError) + assert logged_exception.status_code == 400 + + +def test_handle_logging_failed_response_records_usage_and_cost(): + """Usage on a response.failed event must reach failure spend accounting via combined_usage_object.""" + iterator = _make_iterator() + usage = ResponseAPIUsage(input_tokens=10, output_tokens=5, total_tokens=15) + chunk = _make_failed_chunk( + {"type": "server_error", "code": "server_error", "message": "boom"}, + usage=usage, + ) + iterator.completed_response = chunk + iterator.logging_obj._response_cost_calculator.return_value = 0.0042 + with ( + patch("litellm.responses.streaming_iterator.run_async_function"), + patch("litellm.responses.streaming_iterator.executor"), + ): + iterator._handle_logging_failed_response() + combined_usage = iterator.logging_obj.model_call_details["combined_usage_object"] + assert isinstance(combined_usage, litellm.Usage) + assert combined_usage.prompt_tokens == 10 + assert combined_usage.completion_tokens == 5 + assert combined_usage.total_tokens == 15 + assert iterator.logging_obj.model_call_details["response_cost"] == 0.0042 + iterator.logging_obj._response_cost_calculator.assert_called_once_with(result=chunk.response) + + +def test_handle_logging_failed_response_without_usage_skips_recording(): + iterator = _make_iterator() + iterator.completed_response = _make_failed_chunk( + {"type": "server_error", "code": "server_error", "message": "boom"} + ) + with ( + patch("litellm.responses.streaming_iterator.run_async_function"), + patch("litellm.responses.streaming_iterator.executor"), + ): + iterator._handle_logging_failed_response() + assert "combined_usage_object" not in iterator.logging_obj.model_call_details + iterator.logging_obj._response_cost_calculator.assert_not_called() + + +def test_sync_iterator_raises_mid_stream_fallback_on_rate_limit_error_event(): + """SyncResponsesAPIStreamingIterator must wrap retriable error events for fallback.""" + error_payload = { + "type": "error", + "error": {"type": "tokens", "code": "rate_limit_exceeded", "message": "throttled"}, + } + sse_bytes = f"data: {json.dumps(error_payload)}\n\n".encode() + + mock_response = Mock() + mock_response.headers = {} + mock_response.iter_bytes.return_value = iter([sse_bytes]) + mock_logging_obj = Mock(spec=LiteLLMLoggingObj) + mock_logging_obj.model_call_details = {"litellm_params": {}} + mock_logging_obj.completion_start_time = None + mock_config = Mock(spec=BaseResponsesAPIConfig) + + error_obj = ErrorEventError(type="tokens", code="rate_limit_exceeded", message="throttled") + mock_config.transform_streaming_response.return_value = ErrorEvent( + type=ResponsesAPIStreamEvents.ERROR, sequence_number=0, error=error_obj + ) + + iterator = SyncResponsesAPIStreamingIterator( + response=mock_response, + model="gpt-5", + responses_api_provider_config=mock_config, + logging_obj=mock_logging_obj, + custom_llm_provider="openai", + ) + + with pytest.raises(MidStreamFallbackError) as exc_info: + for _ in iterator: + pass + assert exc_info.value.status_code == 429 + assert isinstance(exc_info.value.original_exception, litellm.APIError) diff --git a/tests/test_litellm/router_strategy/adaptive_router/test_adaptive_router.py b/tests/test_litellm/router_strategy/adaptive_router/test_adaptive_router.py index 93c4db90dad..cbf5635a5ae 100644 --- a/tests/test_litellm/router_strategy/adaptive_router/test_adaptive_router.py +++ b/tests/test_litellm/router_strategy/adaptive_router/test_adaptive_router.py @@ -7,9 +7,6 @@ from litellm.router_strategy.adaptive_router import adaptive_router as ar_module import pytest from litellm.router_strategy.adaptive_router.adaptive_router import AdaptiveRouter -from litellm.router_strategy.adaptive_router.config import ( - OWNER_CACHE_TTL_SECONDS, -) from litellm.router_strategy.adaptive_router.signals import Turn from litellm.types.router import ( AdaptiveRouterConfig, @@ -22,9 +19,7 @@ def _make_router() -> AdaptiveRouter: cfg = AdaptiveRouterConfig(available_models=["fast", "smart"]) prefs = { "fast": AdaptiveRouterPreferences(quality_tier=1, strengths=[]), - "smart": AdaptiveRouterPreferences( - quality_tier=3, strengths=[RequestType.CODE_GENERATION] - ), + "smart": AdaptiveRouterPreferences(quality_tier=3, strengths=[RequestType.CODE_GENERATION]), } costs = {"fast": 0.0001, "smart": 0.001} return AdaptiveRouter( @@ -58,85 +53,6 @@ async def test_pick_model_min_quality_tier_filter_raises_when_no_eligible(): await r.pick_model(RequestType.GENERAL, min_quality_tier=4) -@pytest.mark.asyncio -async def test_pick_model_is_stateless_no_owner_cache_writes(): - """pick_model must not touch the owner cache — that's gated post-call.""" - r = _make_router() - for _ in range(5): - await r.pick_model(RequestType.GENERAL) - assert r._owner_cache == {} - - -# ---- claim_or_check_owner ----------------------------------------------- - - -def test_claim_or_check_owner_first_call_claims_and_returns_true(monkeypatch): - r = _make_router() - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - - assert r.claim_or_check_owner("sess-A", "fast") is True - assert r._owner_cache["sess-A"] == ("fast", 1_000.0 + OWNER_CACHE_TTL_SECONDS) - assert r._skipped_updates_total == 0 - - -def test_claim_or_check_owner_same_model_returns_true_without_extending_ttl( - monkeypatch, -): - r = _make_router() - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - r.claim_or_check_owner("sess-A", "fast") - original_expiry = r._owner_cache["sess-A"][1] - - monkeypatch.setattr(ar_module.time, "time", lambda: 1_500.0) - assert r.claim_or_check_owner("sess-A", "fast") is True - # No extension on hit — owner cache snapshots the first claim. - assert r._owner_cache["sess-A"][1] == original_expiry - - -def test_claim_or_check_owner_mismatch_skips_and_increments_counter(monkeypatch): - r = _make_router() - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - r.claim_or_check_owner("sess-A", "fast") - - assert r.claim_or_check_owner("sess-A", "smart") is False - assert r._skipped_updates_total == 1 - # Owner unchanged. - assert r._owner_cache["sess-A"][0] == "fast" - - -def test_claim_or_check_owner_expired_owner_reclaims_for_new_model(monkeypatch): - r = _make_router() - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - r.claim_or_check_owner("sess-A", "fast") - - monkeypatch.setattr( - ar_module.time, "time", lambda: 1_000.0 + OWNER_CACHE_TTL_SECONDS + 1 - ) - assert r.claim_or_check_owner("sess-A", "smart") is True - assert r._owner_cache["sess-A"][0] == "smart" - # Reclaim isn't a skip. - assert r._skipped_updates_total == 0 - - -def test_owner_cache_evicts_expired_entries_when_threshold_crossed(monkeypatch): - """Past _OWNER_CACHE_SWEEP_THRESHOLD live entries, new claims sweep stale.""" - r = _make_router() - monkeypatch.setattr(ar_module, "_OWNER_CACHE_SWEEP_THRESHOLD", 5) - monkeypatch.setattr(ar_module.time, "time", lambda: 1_000.0) - for i in range(5): - r.claim_or_check_owner(f"old-{i}", "fast") - assert len(r._owner_cache) == 5 - - # Jump past TTL so all "old-*" entries are now expired. - monkeypatch.setattr( - ar_module.time, "time", lambda: 1_000.0 + OWNER_CACHE_TTL_SECONDS + 1 - ) - r.claim_or_check_owner("new-1", "fast") - # Sweep ran -> only the new entry remains. - assert "new-1" in r._owner_cache - assert all(k.startswith("new-") for k in r._owner_cache) - - # ---- record_turn -------------------------------------------------------- @@ -185,9 +101,7 @@ async def test_record_turn_satisfaction_increments_alpha(): # Prime with 2 prior turns to clear the MIN_TURNS_FOR_CLEAN_CREDIT gate. # Use distinct content to avoid incidentally firing stagnation/misalignment. priming_turns = [ - Turn( - user_content="alpha bravo charlie", assistant_content="delta echo foxtrot" - ), + Turn(user_content="alpha bravo charlie", assistant_content="delta echo foxtrot"), Turn( user_content="golf hotel india juliet", assistant_content="kilo lima mike november", @@ -232,6 +146,128 @@ async def test_record_turn_failure_increments_beta(): assert cell_after.alpha == pytest.approx(cell_before.alpha) +@pytest.mark.asyncio +async def test_record_turn_detects_exhaustion_in_tool_results(): + r = _make_router() + + delta = await r.record_turn( + session_id="exhausted", + model_name="smart", + request_type=RequestType.GENERAL, + turn=Turn(tool_results=[{"content": "rate limit exceeded"}]), + ) + + assert delta.exhaustion == 1 + assert r._session_states[("exhausted", "smart")].exhaustion_count == 1 + + +@pytest.mark.asyncio +async def test_record_turn_attributes_user_feedback_to_previous_response_model(): + r = _make_router() + fast_before = r._cells[(RequestType.CODE_GENERATION, "fast")] + smart_before = r._cells[(RequestType.GENERAL, "smart")] + + await r.record_turn( + session_id="feedback-switch", + model_name="fast", + request_type=RequestType.CODE_GENERATION, + turn=Turn( + user_content="fix this python retry bug", + assistant_content="clear the cache on every retry", + ), + ) + await r.record_turn( + session_id="feedback-switch", + model_name="smart", + request_type=RequestType.GENERAL, + turn=Turn( + user_content="the python fix is still broken", + assistant_content="keep successful cache entries", + ), + ) + + fast_after = r._cells[(RequestType.CODE_GENERATION, "fast")] + smart_after = r._cells[(RequestType.GENERAL, "smart")] + assert fast_after.beta == pytest.approx(fast_before.beta + 1.0) + assert smart_after.beta == pytest.approx(smart_before.beta) + snapshot = await r.get_state_snapshot() + assert snapshot["feedback_attributed_total"] == 1 + assert snapshot["cross_model_feedback_total"] == 1 + assert snapshot["feedback_without_context_total"] == 0 + + +@pytest.mark.asyncio +async def test_record_turn_attributes_satisfaction_to_previous_response_model(): + r = _make_router() + await r.record_turn( + session_id="satisfaction-switch", + model_name="smart", + request_type=RequestType.CODE_GENERATION, + turn=Turn( + user_content="write a python retry helper", + assistant_content="first draft", + ), + ) + await r.record_turn( + session_id="satisfaction-switch", + model_name="fast", + request_type=RequestType.CODE_GENERATION, + turn=Turn( + user_content="add exponential backoff to the python helper", + assistant_content="updated draft", + ), + ) + fast_before = r._cells[(RequestType.CODE_GENERATION, "fast")] + smart_before = r._cells[(RequestType.GENERAL, "smart")] + + await r.record_turn( + session_id="satisfaction-switch", + model_name="smart", + request_type=RequestType.GENERAL, + turn=Turn( + user_content="thanks, that worked", + assistant_content="glad to help", + ), + ) + + fast_after = r._cells[(RequestType.CODE_GENERATION, "fast")] + smart_after = r._cells[(RequestType.GENERAL, "smart")] + assert fast_after.alpha == pytest.approx(fast_before.alpha + 1.0) + assert smart_after.alpha == pytest.approx(smart_before.alpha) + + +@pytest.mark.asyncio +async def test_record_turn_bounds_feedback_contexts_and_evicts_least_recent_session(): + r = _make_router() + context_limit = ar_module._FEEDBACK_CONTEXT_MAX_ENTRIES + + for index in range(context_limit): + await r.record_turn( + session_id=f"session-{index}", + model_name="fast", + request_type=RequestType.GENERAL, + turn=Turn(user_content="question", assistant_content="answer"), + ) + + await r.record_turn( + session_id="session-0", + model_name="fast", + request_type=RequestType.GENERAL, + turn=Turn(user_content="follow up", assistant_content="updated answer"), + ) + await r.record_turn( + session_id="overflow", + model_name="fast", + request_type=RequestType.GENERAL, + turn=Turn(user_content="question", assistant_content="answer"), + ) + + assert len(r._feedback_contexts) == context_limit + assert "session-0" in r._feedback_contexts + assert "session-1" not in r._feedback_contexts + assert "overflow" in r._feedback_contexts + + @pytest.mark.asyncio async def test_load_state_from_db_overrides_cold_start(): r = _make_router() @@ -270,9 +306,7 @@ async def test_load_state_from_db_handles_unknown_request_type(): good_row.beta = 3.0 prisma = MagicMock() - prisma.db.litellm_adaptiverouterstate.find_many = AsyncMock( - return_value=[bad_row, good_row] - ) + prisma.db.litellm_adaptiverouterstate.find_many = AsyncMock(return_value=[bad_row, good_row]) await r.load_state_from_db(prisma) # Unknown skipped; good applied. diff --git a/tests/test_litellm/router_strategy/adaptive_router/test_e2e_adaptive_router.py b/tests/test_litellm/router_strategy/adaptive_router/test_e2e_adaptive_router.py index 9786832b4ae..3071f916ef1 100644 --- a/tests/test_litellm/router_strategy/adaptive_router/test_e2e_adaptive_router.py +++ b/tests/test_litellm/router_strategy/adaptive_router/test_e2e_adaptive_router.py @@ -110,23 +110,6 @@ async def test_pick_record_flush_full_cycle(): assert session_call.kwargs["data"]["create"]["model_name"] == chosen -@pytest.mark.asyncio -async def test_owner_cache_pins_attribution_to_first_picked_model(): - """First call claims ownership; matching model returns True, mismatch False.""" - router = _make_router() - chosen = await router.pick_model(RequestType.GENERAL) - assert router.claim_or_check_owner("sess-own", chosen) is True - - # Same model on later turns keeps attributing. - for _ in range(5): - assert router.claim_or_check_owner("sess-own", chosen) is True - - # A different model on a later turn is rejected. - other = "gpt-4o" if chosen == "gpt-4o-mini" else "gpt-4o-mini" - assert router.claim_or_check_owner("sess-own", other) is False - assert router._skipped_updates_total == 1 - - @pytest.mark.asyncio async def test_pick_model_returns_valid_models_without_error(): router = _make_router() diff --git a/tests/test_litellm/router_strategy/adaptive_router/test_hooks.py b/tests/test_litellm/router_strategy/adaptive_router/test_hooks.py index a2b85f2ce53..ad61f43c5a0 100644 --- a/tests/test_litellm/router_strategy/adaptive_router/test_hooks.py +++ b/tests/test_litellm/router_strategy/adaptive_router/test_hooks.py @@ -16,10 +16,9 @@ from litellm.router_strategy.adaptive_router.hooks import ( from litellm.router_strategy.adaptive_router.signals import Turn -def _make_hook(claim: bool = True) -> AdaptiveRouterPostCallHook: +def _make_hook() -> AdaptiveRouterPostCallHook: fake_router = MagicMock() fake_router.record_turn = AsyncMock() - fake_router.claim_or_check_owner = MagicMock(return_value=claim) return AdaptiveRouterPostCallHook(adaptive_router=fake_router) @@ -151,7 +150,24 @@ async def test_hook_skips_when_below_signal_gate(): kwargs = _kwargs(messages=short) await hook.async_log_success_event(kwargs, _resp_with_content("ok"), 0.0, 1.0) hook.adaptive_router.record_turn.assert_not_awaited() - hook.adaptive_router.claim_or_check_owner.assert_not_called() + + +@pytest.mark.asyncio +async def test_hook_tracks_short_conversation_with_explicit_session_id(): + hook = _make_hook() + kwargs = _kwargs( + messages=[{"role": "user", "content": "hi"}], + extra_litellm_params={"litellm_session_id": "explicit-short"}, + ) + await hook.async_log_success_event( + kwargs, + _resp_with_content("hello"), + 0.0, + 1.0, + ) + assert hook.adaptive_router.record_turn.await_args.kwargs["session_id"] == ( + "explicit-short" + ) @pytest.mark.asyncio @@ -168,22 +184,19 @@ async def test_hook_skips_when_chosen_model_missing_from_metadata(): kwargs = _kwargs(chosen=None) await hook.async_log_success_event(kwargs, _resp_with_content("ok"), 0.0, 1.0) hook.adaptive_router.record_turn.assert_not_awaited() - hook.adaptive_router.claim_or_check_owner.assert_not_called() @pytest.mark.asyncio -async def test_hook_skips_when_owner_cache_mismatch(): - """A different model owns this conversation -> no attribution.""" - hook = _make_hook(claim=False) +async def test_hook_records_when_model_changes(): + hook = _make_hook() kwargs = _kwargs(chosen="fast") await hook.async_log_success_event(kwargs, _resp_with_content("ok"), 0.0, 1.0) - hook.adaptive_router.claim_or_check_owner.assert_called_once() - hook.adaptive_router.record_turn.assert_not_awaited() + hook.adaptive_router.record_turn.assert_awaited_once() @pytest.mark.asyncio -async def test_hook_records_turn_when_owner_claims(): - hook = _make_hook(claim=True) +async def test_hook_records_turn(): + hook = _make_hook() kwargs = _kwargs(chosen="smart", messages=_long_messages("ask")) await hook.async_log_success_event( kwargs, _resp_with_content("answer here"), 0.0, 1.0 @@ -205,8 +218,6 @@ async def test_hook_uses_explicit_session_id_when_provided(): extra_litellm_params={"litellm_session_id": "explicit-sess"}, ) await hook.async_log_success_event(kwargs, _resp_with_content("ok"), 0.0, 1.0) - args, _ = hook.adaptive_router.claim_or_check_owner.call_args - assert args[0] == "explicit-sess" assert hook.adaptive_router.record_turn.await_args.kwargs["session_id"] == ( "explicit-sess" ) diff --git a/tests/test_litellm/router_strategy/adaptive_router/test_state_endpoint.py b/tests/test_litellm/router_strategy/adaptive_router/test_state_endpoint.py index 753a449791b..d6d89c8e811 100644 --- a/tests/test_litellm/router_strategy/adaptive_router/test_state_endpoint.py +++ b/tests/test_litellm/router_strategy/adaptive_router/test_state_endpoint.py @@ -1,7 +1,6 @@ """Tests for the GET /adaptive_router/state introspection endpoint and the underlying `AdaptiveRouter.get_state_snapshot()` helper.""" -import time from unittest.mock import MagicMock import pytest @@ -9,7 +8,7 @@ from fastapi import HTTPException from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth from litellm.router_strategy.adaptive_router.adaptive_router import AdaptiveRouter -from litellm.router_strategy.adaptive_router.bandit import BanditCell, apply_delta +from litellm.router_strategy.adaptive_router.bandit import apply_delta from litellm.types.router import ( AdaptiveRouterConfig, AdaptiveRouterPreferences, @@ -47,8 +46,6 @@ async def test_get_state_snapshot_returns_cell_per_request_type_per_model(): assert snap["available_models"] == ["fast", "smart"] assert snap["weights"] == {"quality": 0.7, "cost": 0.3} assert snap["model_costs"] == {"fast": 0.0001, "smart": 0.001} - assert snap["owner_cache_live"] == 0 - assert snap["skipped_updates_total"] == 0 assert set(snap["queue"].keys()) == { "state_pending", "session_pending", @@ -95,26 +92,6 @@ async def test_get_state_snapshot_quality_mean_matches_alpha_over_total(): assert cell["quality_mean"] == pytest.approx(expected_mean) -@pytest.mark.asyncio -async def test_get_state_snapshot_counts_only_live_owner_cache_entries(): - r = _make_router() - now = time.time() - r._owner_cache["live-1"] = ("fast", now + 3600) - r._owner_cache["live-2"] = ("smart", now + 3600) - r._owner_cache["expired-1"] = ("fast", now - 1) - - snap = await r.get_state_snapshot() - assert snap["owner_cache_live"] == 2 - - -@pytest.mark.asyncio -async def test_get_state_snapshot_exposes_skipped_updates_total(): - r = _make_router() - r._skipped_updates_total = 7 - snap = await r.get_state_snapshot() - assert snap["skipped_updates_total"] == 7 - - # ---- endpoint -------------------------------------------------------- diff --git a/tests/test_litellm/router_strategy/test_complexity_router.py b/tests/test_litellm/router_strategy/test_complexity_router.py index e68ea863d82..da02b774e41 100644 --- a/tests/test_litellm/router_strategy/test_complexity_router.py +++ b/tests/test_litellm/router_strategy/test_complexity_router.py @@ -4,24 +4,30 @@ Tests for the ComplexityRouter. Tests the rule-based complexity scoring and tier assignment logic. """ +import asyncio +import logging import os import sys from typing import Dict, List -from unittest.mock import MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, patch import pytest +from pydantic import ValidationError sys.path.insert( 0, os.path.abspath("../../..") ) # Adds the parent directory to the system path +import litellm from litellm import Router +from litellm._logging import verbose_router_logger from litellm.router_strategy.complexity_router.complexity_router import ( ComplexityRouter, DimensionScore, ) from litellm.router_strategy.complexity_router.config import ( DEFAULT_COMPLEXITY_CONFIG, + DEFAULT_TECHNICAL_KEYWORDS, ComplexityRouterConfig, ComplexityTier, ) @@ -309,6 +315,36 @@ class TestModelSelection: model = router.get_model_for_tier(ComplexityTier.SIMPLE) assert model == "fallback-model" + def test_get_model_for_tier_list_random_choice(self, mock_router_instance): + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "tiers": {"SIMPLE": ["cheap", "premium"], "MEDIUM": "mid"}, + "default_model": "mid", + }, + ) + pool = ["cheap", "premium"] + with patch( + "litellm.router_strategy.complexity_router.complexity_router.random.choice", + return_value="premium", + ) as choice: + assert router.get_model_for_tier(ComplexityTier.SIMPLE) == "premium" + choice.assert_called_once_with(pool) + assert router.get_model_for_tier(ComplexityTier.MEDIUM) == "mid" + + def test_get_model_for_tier_empty_pool_raises(self, mock_router_instance): + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "tiers": {"SIMPLE": []}, + "default_model": "mid", + }, + ) + with pytest.raises(ValueError, match="Empty model pool for tier SIMPLE"): + router.get_model_for_tier(ComplexityTier.SIMPLE) + class TestPreRoutingHook: """Test the async_pre_routing_hook method.""" @@ -468,6 +504,89 @@ class TestConfigOverrides: ), f"Expected 'long' signal, got {signals}" +class TestCustomTechnicalKeywords: + """Test the custom_technical_keywords config option.""" + + def test_custom_keywords_appended_to_defaults(self, mock_router_instance): + """Custom keywords should be appended to the default technical keywords.""" + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={"custom_technical_keywords": ["udp", "kafka"]}, + ) + assert router.technical_keywords == DEFAULT_TECHNICAL_KEYWORDS + ["udp", "kafka"] + + def test_custom_keywords_appended_to_technical_keywords_override( + self, mock_router_instance + ): + """Custom keywords should be appended to a technical_keywords override.""" + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "technical_keywords": ["quantum", "photonics"], + "custom_technical_keywords": ["udp"], + }, + ) + assert router.technical_keywords == ["quantum", "photonics", "udp"] + + def test_custom_keywords_deduplicated_case_insensitively(self, mock_router_instance): + """Duplicates against the base list and within the custom list should be dropped.""" + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "custom_technical_keywords": ["TCP", "udp", "UDP", "kafka"] + }, + ) + lowered = [kw.lower() for kw in router.technical_keywords] + assert lowered == [kw.lower() for kw in DEFAULT_TECHNICAL_KEYWORDS] + [ + "udp", + "kafka", + ] + + def test_no_custom_keywords_leaves_defaults_unchanged(self, mock_router_instance): + """Absent or None custom_technical_keywords should leave the keyword list identical.""" + router_absent = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={"tiers": {"MEDIUM": "gpt-4o"}}, + ) + router_none = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={"custom_technical_keywords": None}, + ) + assert router_absent.technical_keywords == DEFAULT_TECHNICAL_KEYWORDS + assert router_none.technical_keywords == DEFAULT_TECHNICAL_KEYWORDS + + def test_prompt_with_only_custom_keywords_scores_technical( + self, mock_router_instance, basic_config + ): + """A prompt matching only custom keywords should score higher on technicalTerms.""" + prompt = "Configure udp multicast between kafka brokers" + baseline_router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=basic_config, + ) + custom_router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + **basic_config, + "custom_technical_keywords": ["UDP", "Kafka"], + }, + ) + _, baseline_score, baseline_signals = baseline_router.classify(prompt) + _, custom_score, custom_signals = custom_router.classify(prompt) + assert not any("technical" in s.lower() for s in baseline_signals) + assert any( + "technical" in s.lower() for s in custom_signals + ), f"Expected technical signal, got {custom_signals}" + assert custom_score > baseline_score + + class TestAsyncPreRoutingHookEdgeCases: """Test edge cases for async_pre_routing_hook method.""" @@ -659,7 +778,7 @@ class TestKeywordFalsePositives: # Should NOT detect code presence from 'api' in 'capital' assert not any( "code" in s.lower() for s in signals - ), f"False positive: got code signal from 'capital'" + ), "False positive: got code signal from 'capital'" # Should be SIMPLE (definition question) assert tier == ComplexityTier.SIMPLE @@ -670,7 +789,7 @@ class TestKeywordFalsePositives: # Should NOT detect code presence from 'git' in 'digital' assert not any( "code" in s.lower() for s in signals - ), f"False positive: got code signal from 'digital'" + ), "False positive: got code signal from 'digital'" def test_try_not_in_entry(self, complexity_router): """'try' should not match in 'entry'.""" @@ -686,7 +805,7 @@ class TestKeywordFalsePositives: tier, score, signals = complexity_router.classify(prompt) assert not any( "code" in s.lower() for s in signals - ), f"False positive: got code signal from 'terrorism'" + ), "False positive: got code signal from 'terrorism'" def test_class_not_in_classical(self, complexity_router): """'class' should not match in 'classical'.""" @@ -694,7 +813,7 @@ class TestKeywordFalsePositives: tier, score, signals = complexity_router.classify(prompt) assert not any( "code" in s.lower() for s in signals - ), f"False positive: got code signal from 'classical'" + ), "False positive: got code signal from 'classical'" def test_merge_not_in_emerged(self, complexity_router): """'merge' should not match in 'emerged'.""" @@ -702,7 +821,7 @@ class TestKeywordFalsePositives: tier, score, signals = complexity_router.classify(prompt) assert not any( "code" in s.lower() for s in signals - ), f"False positive: got code signal from 'emerged'" + ), "False positive: got code signal from 'emerged'" def test_actual_api_keyword_detected(self, complexity_router): """Actual 'api' usage should be detected.""" @@ -829,6 +948,60 @@ class TestRouterComplexityDeploymentMethods: router.init_complexity_router_deployment(deployment) assert "auto_router/complexity_router/test-router" in router.complexity_routers + def test_hybrid_initialization_waits_for_later_pool_deployments(self): + router = Router( + model_list=[ + { + "model_name": "hybrid", + "litellm_params": { + "model": "auto_router/complexity_router", + "complexity_router_default_model": "cheap", + "complexity_router_config": { + "adaptive": True, + "tiers": { + "SIMPLE": ["cheap"], + "MEDIUM": ["cheap", "premium"], + }, + }, + }, + }, + { + "model_name": "cheap", + "litellm_params": { + "model": "openai/gpt-4o-mini", + "input_cost_per_token": 0.00000015, + }, + "model_info": { + "adaptive_router_preferences": { + "quality_tier": 1, + "strengths": [], + } + }, + }, + { + "model_name": "premium", + "litellm_params": { + "model": "openai/gpt-4o", + "input_cost_per_token": 0.000005, + }, + "model_info": { + "adaptive_router_preferences": { + "quality_tier": 3, + "strengths": [], + } + }, + }, + ] + ) + + adaptive = router.adaptive_routers["hybrid"] + assert adaptive.model_to_cost == { + "cheap": pytest.approx(0.00000015), + "premium": pytest.approx(0.000005), + } + assert adaptive.model_to_prefs["cheap"].quality_tier == 1 + assert adaptive.model_to_prefs["premium"].quality_tier == 3 + class TestAsyncPreRoutingHookMultiFormat: """Test async_pre_routing_hook with multiple input formats.""" @@ -1047,3 +1220,1207 @@ class TestExtractUserMessageAndSystemPrompt: ) assert user_msg is None assert sys_prompt is None + + +def _llm_response(content: str): + """Build a fake acompletion response with the given message content.""" + response = MagicMock() + response.choices = [MagicMock()] + response.choices[0].message.content = content + return response + + +@pytest.fixture +def llm_classifier_config() -> Dict: + """Config with an LLM-based classifier wired to a 'haiku-classifier' model.""" + return { + "tiers": { + "SIMPLE": "gpt-4o-mini", + "MEDIUM": "gpt-4o", + "COMPLEX": "claude-sonnet-4-20250514", + "REASONING": "o1-preview", + }, + "classifier_type": "llm", + "classifier_llm_config": {"model": "haiku-classifier", "timeout_ms": 400}, + } + + +@pytest.fixture +def llm_complexity_router(mock_router_instance, llm_classifier_config): + """ComplexityRouter configured to classify via an LLM call.""" + return ComplexityRouter( + model_name="test-complexity-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=llm_classifier_config, + ) + + +class TestLLMClassifierConfig: + """Test config validation for the LLM classifier option.""" + + def test_llm_classifier_type_requires_config(self): + """classifier_type='llm' without classifier_llm_config must raise.""" + with pytest.raises(ValidationError): + ComplexityRouterConfig(classifier_type="llm") + + def test_heuristic_classifier_type_needs_no_llm_config(self): + """classifier_type='heuristic' (the default) needs no classifier_llm_config.""" + config = ComplexityRouterConfig() + assert config.classifier_type == "heuristic" + assert config.classifier_llm_config is None + + +class TestLLMClassifier: + """Test the LLM-based classifier path (aclassify) and its fallback behavior.""" + + @pytest.mark.asyncio + async def test_aclassify_heuristic_skips_llm_call(self, complexity_router, mock_router_instance): + """When classifier_type is 'heuristic' (default), aclassify must not call the LLM.""" + mock_router_instance.acompletion = AsyncMock() + tier, score, signals = await complexity_router.aclassify("Hello!") + mock_router_instance.acompletion.assert_not_called() + assert tier == ComplexityTier.SIMPLE + + @pytest.mark.asyncio + async def test_aclassify_llm_success_routes_by_llm_verdict( + self, llm_complexity_router, mock_router_instance + ): + """A well-formed structured LLM response should decide the tier directly. + + Uses a prompt that heuristic scoring alone would classify as SIMPLE, to prove + the LLM verdict -- not the heuristic scorer -- is what decided the tier. + """ + mock_router_instance.acompletion = AsyncMock( + return_value=_llm_response('{"tier": "COMPLEX"}') + ) + tier, score, signals = await llm_complexity_router.aclassify("hi") + assert tier == ComplexityTier.COMPLEX + assert "llm-classifier:COMPLEX" in signals + mock_router_instance.acompletion.assert_awaited_once() + call_kwargs = mock_router_instance.acompletion.call_args.kwargs + assert call_kwargs["model"] == "haiku-classifier" + assert call_kwargs["timeout"] == 0.4 + + @pytest.mark.asyncio + async def test_aclassify_forwards_request_metadata_for_spend_tracking( + self, llm_complexity_router, mock_router_instance + ): + """The classifier call must carry the original request's metadata. + + Without this, the proxy's cost-tracking gate (_should_track_cost_callback) + sees no user_api_key/team_id/user_id and silently drops all spend logging + and budget accounting for the classifier call. + """ + mock_router_instance.acompletion = AsyncMock( + return_value=_llm_response('{"tier": "SIMPLE"}') + ) + request_metadata = {"user_api_key": "sk-abc", "user_api_key_team_id": "team-1"} + await llm_complexity_router.aclassify( + "hi", request_kwargs={"litellm_metadata": request_metadata} + ) + call_kwargs = mock_router_instance.acompletion.call_args.kwargs + assert call_kwargs["metadata"] == request_metadata + + @pytest.mark.asyncio + async def test_aclassify_strips_budget_reservation_from_classifier_metadata( + self, llm_complexity_router, mock_router_instance + ): + """The classifier call must not receive the parent request's budget reservation. + + The reservation belongs to the routed completion the classifier is deciding + on, not to this internal classifier call. Forwarding it would let the + classifier's own cost-tracking reconcile against a reservation it has no + business touching, so it must be stripped while the rest of the attribution + metadata (key/team) is preserved. + """ + mock_router_instance.acompletion = AsyncMock( + return_value=_llm_response('{"tier": "SIMPLE"}') + ) + request_metadata = { + "user_api_key": "sk-abc", + "user_api_key_team_id": "team-1", + "user_api_key_budget_reservation": {"reserved_cost": 1.0}, + "user_api_key_auth": {"models": ["gpt-4o"], "budget_reservation": {"reserved_cost": 1.0}}, + } + await llm_complexity_router.aclassify( + "hi", request_kwargs={"litellm_metadata": request_metadata} + ) + call_kwargs = mock_router_instance.acompletion.call_args.kwargs + # user_api_key_budget_reservation is stripped (budget enforcement) while + # user_api_key_auth is kept so _filter_deployments_by_model_access_groups + # can scope the classifier's model selection to the caller's access groups, + # but only as a sanitized copy without its budget_reservation sub-field: + # the cost callback falls back to reading the reservation from inside the + # auth object when the top-level key is absent. + assert call_kwargs["metadata"] == { + "user_api_key": "sk-abc", + "user_api_key_team_id": "team-1", + "user_api_key_auth": {"models": ["gpt-4o"]}, + } + assert request_metadata["user_api_key_auth"] == { + "models": ["gpt-4o"], + "budget_reservation": {"reserved_cost": 1.0}, + } + + @pytest.mark.asyncio + async def test_aclassify_falls_back_to_heuristic_on_llm_exception( + self, llm_complexity_router, mock_router_instance + ): + """A timeout/error from the classifier model must fall back to heuristic scoring.""" + mock_router_instance.acompletion = AsyncMock(side_effect=TimeoutError("classifier timed out")) + tier, score, signals = await llm_complexity_router.aclassify("Hello!") + assert tier == llm_complexity_router.classify("Hello!")[0] + assert tier == ComplexityTier.SIMPLE + + @pytest.mark.asyncio + async def test_aclassify_falls_back_to_heuristic_on_unparseable_response( + self, llm_complexity_router, mock_router_instance + ): + """Non-JSON or schema-violating output must fall back to heuristic scoring, not raise.""" + mock_router_instance.acompletion = AsyncMock(return_value=_llm_response("not json")) + tier, score, signals = await llm_complexity_router.aclassify("Hello!") + assert tier == ComplexityTier.SIMPLE + + @pytest.mark.asyncio + async def test_aclassify_falls_back_to_heuristic_on_empty_content( + self, llm_complexity_router, mock_router_instance + ): + """Empty/None message content (e.g. provider quirk) must fall back, not raise.""" + mock_router_instance.acompletion = AsyncMock(return_value=_llm_response(None)) + tier, score, signals = await llm_complexity_router.aclassify("Hello!") + assert tier == ComplexityTier.SIMPLE + + @pytest.mark.asyncio + async def test_pre_routing_hook_uses_llm_classifier_end_to_end( + self, llm_complexity_router, mock_router_instance + ): + """The full pre-routing hook should route using the LLM classifier's verdict.""" + mock_router_instance.acompletion = AsyncMock( + return_value=_llm_response('{"tier": "REASONING"}') + ) + request_metadata = {"user_api_key": "sk-abc", "user_api_key_team_id": "team-1"} + result = await llm_complexity_router.async_pre_routing_hook( + model="test-model", + request_kwargs={"litellm_metadata": request_metadata}, + messages=[{"role": "user", "content": "hi"}], + ) + assert result is not None + assert result.model == "o1-preview" # REASONING tier model + call_kwargs = mock_router_instance.acompletion.call_args.kwargs + assert call_kwargs["metadata"] == request_metadata + + +class TestAdaptiveSoftFloors: + def test_adaptive_defaults_use_cost_weighted_cold_policy(self): + config = ComplexityRouterConfig( + adaptive=True, + tiers={"SIMPLE": ["cheap"]}, + ) + assert config.adaptive_weights.quality == pytest.approx(0.3) + assert config.adaptive_weights.cost == pytest.approx(0.7) + assert config.tier_distance_penalty == pytest.approx(0.5) + + @pytest.fixture + def adaptive_router_instance(self): + router = MagicMock() + router.model_list = [ + { + "model_name": "cheap", + "litellm_params": { + "model": "openai/gpt-4o-mini", + "input_cost_per_token": 0.00000015, + }, + "model_info": { + "adaptive_router_preferences": {"quality_tier": 1, "strengths": []} + }, + }, + { + "model_name": "premium", + "litellm_params": { + "model": "openai/gpt-4o", + "input_cost_per_token": 0.000005, + }, + "model_info": { + "adaptive_router_preferences": {"quality_tier": 3, "strengths": []} + }, + }, + ] + router.model_name_to_deployment_indices = {"cheap": [0], "premium": [1]} + return router + + @pytest.fixture + def hybrid_config(self) -> Dict: + return { + "adaptive": True, + "adaptive_weights": {"quality": 0.7, "cost": 0.3}, + "tier_distance_penalty": 0.15, + "tiers": { + "SIMPLE": ["cheap"], + "MEDIUM": ["cheap"], + "COMPLEX": ["premium"], + "REASONING": ["premium"], + }, + "default_model": "cheap", + } + + def test_adaptive_config_requires_non_empty_pools(self): + with pytest.raises(ValidationError): + ComplexityRouterConfig(adaptive=True, tiers={"SIMPLE": []}) + + def test_cold_start_randomly_samples_unobserved_classified_tier_models( + self, adaptive_router_instance + ): + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config={ + "adaptive": True, + "tiers": { + "SIMPLE": ["cheap", "premium"], + "MEDIUM": ["premium"], + }, + }, + ) + request_kwargs: Dict = {"metadata": {}} + + with patch( + "litellm.router_strategy.complexity_router.complexity_router.random.choice", + return_value="premium", + ) as choice: + picked = cr._soft_floor_pick(ComplexityTier.SIMPLE, "hi", request_kwargs) + + assert picked == "premium" + choice.assert_called_once_with(("cheap", "premium")) + decision = request_kwargs["metadata"]["adaptive_router_decision"] + assert decision["phase"] == "cold_start" + assert {candidate["model"] for candidate in decision["candidates"]} == { + "cheap", + "premium", + } + + def test_get_model_for_tier_list_without_adaptive_random_choice( + self, mock_router_instance + ): + router = ComplexityRouter( + model_name="test", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "adaptive": False, + "tiers": {"SIMPLE": ["cheap", "premium"], "MEDIUM": "mid"}, + "default_model": "mid", + }, + ) + pool = ["cheap", "premium"] + with patch( + "litellm.router_strategy.complexity_router.complexity_router.random.choice", + return_value="premium", + ) as choice: + assert router.get_model_for_tier(ComplexityTier.SIMPLE) == "premium" + choice.assert_called_once_with(pool) + assert router.get_model_for_tier(ComplexityTier.MEDIUM) == "mid" + + def test_soft_floor_prefers_home_tier_when_posteriors_equal( + self, adaptive_router_instance, hybrid_config + ): + from litellm.router_strategy.adaptive_router.bandit import BanditCell + from litellm.types.router import RequestType + + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config=hybrid_config, + ) + adaptive = cr._ensure_adaptive_router() + assert adaptive is not None + for model in ("cheap", "premium"): + adaptive._cells[(RequestType.GENERAL, model)] = BanditCell( + alpha=5.0, beta=5.0 + ) + + # Equal quality samples; home-tier penalty should favor cheap for SIMPLE. + with patch( + "litellm.router_strategy.adaptive_router.bandit.thompson_sample", + return_value=0.5, + ): + picked = cr._soft_floor_pick(ComplexityTier.SIMPLE, "hi") + assert picked == "cheap" + + def test_soft_floor_allows_cross_tier_when_posterior_dominates( + self, adaptive_router_instance, hybrid_config + ): + from litellm.router_strategy.adaptive_router.bandit import BanditCell + from litellm.types.router import RequestType + + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config=hybrid_config, + ) + adaptive = cr._ensure_adaptive_router() + assert adaptive is not None + adaptive._cells[(RequestType.GENERAL, "cheap")] = BanditCell( + alpha=1.0, beta=20.0 + ) + adaptive._cells[(RequestType.GENERAL, "premium")] = BanditCell( + alpha=20.0, beta=1.0 + ) + + with patch( + "litellm.router_strategy.adaptive_router.bandit.thompson_sample", + side_effect=lambda cell, rng=None: cell.alpha / (cell.alpha + cell.beta), + ): + picked = cr._soft_floor_pick(ComplexityTier.SIMPLE, "hi") + assert picked == "premium" + + def test_reused_model_has_zero_distance_in_each_configured_tier( + self, adaptive_router_instance + ): + from litellm.router_strategy.adaptive_router.bandit import BanditCell + from litellm.types.router import RequestType + + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config={ + "adaptive": True, + "tiers": { + "SIMPLE": ["cheap"], + "MEDIUM": ["cheap", "premium"], + "COMPLEX": ["premium"], + }, + }, + ) + adaptive = cr._ensure_adaptive_router() + assert adaptive is not None + for model in ("cheap", "premium"): + adaptive._cells[(RequestType.GENERAL, model)] = BanditCell( + alpha=6.0, beta=5.0 + ) + request_kwargs: Dict = {"metadata": {}} + + with patch( + "litellm.router_strategy.adaptive_router.bandit.thompson_sample", + return_value=0.5, + ): + cr._soft_floor_pick(ComplexityTier.MEDIUM, "hi", request_kwargs) + + candidates = request_kwargs["metadata"]["adaptive_router_decision"][ + "candidates" + ] + assert { + candidate["model"]: candidate["tier_distance"] for candidate in candidates + } == { + "cheap": 0, + "premium": 0, + } + + @pytest.mark.asyncio + async def test_pre_routing_hook_adaptive_stashes_chosen_model( + self, adaptive_router_instance, hybrid_config + ): + cr = ComplexityRouter( + model_name="hybrid", + litellm_router_instance=adaptive_router_instance, + complexity_router_config=hybrid_config, + ) + request_kwargs: Dict = {"metadata": {}} + result = await cr.async_pre_routing_hook( + model="hybrid", + request_kwargs=request_kwargs, + messages=[{"role": "user", "content": "hi"}], + ) + assert result is not None + assert result.model in {"cheap", "premium"} + assert ( + request_kwargs["metadata"].get("adaptive_router_chosen_model") + == result.model + ) + decision = request_kwargs["metadata"]["adaptive_router_decision"] + assert decision["phase"] == "cold_start" + assert decision["classified_tier"] == "SIMPLE" + assert decision["request_type"] == "general" + assert decision["eligible_mode"] == "classified_tier" + assert decision["chosen_model"] == result.model + assert {candidate["model"] for candidate in decision["candidates"]} == {"cheap"} + + +class TestLexicalKeywordTierRules: + """Test deterministic (literal) keyword_tier_rules overrides.""" + + @pytest.fixture + def rule_config(self, basic_config) -> Dict: + return { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["deploy to k8s"], "tier": "REASONING"}, + ], + } + + @pytest.mark.asyncio + async def test_matching_rule_overrides_scoring( + self, mock_router_instance, rule_config + ): + """A prompt hitting a rule keyword routes to that tier, not the scored tier.""" + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=rule_config, + ) + prompt = "please deploy to k8s now" + # Without the rule this short prompt would not score into REASONING. + scored_tier, _, _ = router.classify(prompt) + assert scored_tier != ComplexityTier.REASONING + + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": prompt}], + ) + assert result is not None + assert result.model == "o1-preview" # REASONING tier model + + @pytest.mark.asyncio + async def test_most_severe_tier_wins_regardless_of_rule_order(self, mock_router_instance, basic_config): + """When several rules match, the highest-severity tier wins, independent of list order.""" + config = { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["database"], "tier": "SIMPLE"}, # listed first, lower tier + {"keywords": ["database"], "tier": "REASONING"}, # listed later, higher tier + ], + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "tell me about the database"}], + ) + assert result is not None + assert result.model == "o1-preview" # REASONING wins over the earlier SIMPLE rule + + @pytest.mark.asyncio + async def test_distinct_keywords_escalate_to_highest_tier(self, mock_router_instance, basic_config): + """A prompt hitting keywords across tiers routes to the most complex one.""" + config = { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["hi"], "tier": "SIMPLE"}, + {"keywords": ["advise"], "tier": "COMPLEX"}, + {"keywords": ["kubernetes"], "tier": "REASONING"}, + ], + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "hi, advise me on kubernetes"}], + ) + assert result is not None + assert result.model == "o1-preview" # REASONING, the highest of SIMPLE/COMPLEX/REASONING + + def test_lexical_override_returns_most_severe_matched_tier(self, mock_router_instance, basic_config): + """Unit-level check of the escalation helper across mixed matches.""" + config = { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["hi"], "tier": "SIMPLE"}, + {"keywords": ["advise"], "tier": "COMPLEX"}, + ], + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + assert router._lexical_tier_override("hi there, please advise") == ComplexityTier.COMPLEX + assert router._lexical_tier_override("just saying hi") == ComplexityTier.SIMPLE + assert router._lexical_tier_override("nothing relevant here") is None + + @pytest.mark.asyncio + async def test_no_rule_match_falls_back_to_scoring( + self, mock_router_instance, basic_config + ): + """A prompt that matches no rule is classified by the scorer as usual.""" + config = { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["zzznomatch"], "tier": "REASONING"}, + ], + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "Hello!"}], + ) + assert result is not None + assert result.model == "gpt-4o-mini" # SIMPLE via scoring, rule did not fire + + def test_word_boundary_avoids_substring_false_positive( + self, mock_router_instance, basic_config + ): + """A single-word rule keyword must not match inside a larger word.""" + config = { + **basic_config, + "keyword_tier_rules": [{"keywords": ["k8s"], "tier": "REASONING"}], + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + assert router._lexical_tier_override("running my k8s cluster") == ComplexityTier.REASONING + assert router._lexical_tier_override("what is a k8scluster thing") is None + + +def _make_embedding_response(vectors: List[List[float]]) -> "litellm.EmbeddingResponse": + return litellm.EmbeddingResponse( + model="fake-embed", + data=[ + {"embedding": vec, "index": idx, "object": "embedding"} + for idx, vec in enumerate(vectors) + ], + object="list", + ) + + +class FakeEmbeddingRouter: + """A stand-in router whose embeddings are deterministic 2D unit vectors. + + Any text mentioning a cluster/container concept maps to [1, 0]; everything + else maps to [0, 1]. This lets the real SemanticRouter compute exact cosine + similarities (1.0 or 0.0) so threshold behavior is testable without a network call. + """ + + _CLUSTER_MARKERS = ("k8s", "kube", "container", "cluster", "orchestrat") + + def __init__(self): + self.async_embedding_calls: List[List[str]] = [] + self.async_embedding_kwargs: List[Dict] = [] + # Every embedded batch (sync route-index build AND async query), so tests can count + # builds independently of which embedding path the library happens to use. + self.embedded_batches: List[List[str]] = [] + # Thread ids of the synchronous (route-index build) embedding calls, so a test can + # assert the build is offloaded off the event-loop thread. + self.sync_embedding_thread_ids: List[int] = [] + + def _vectors(self, docs: List[str]) -> List[List[float]]: + return [ + [1.0, 0.0] if any(marker in doc.lower() for marker in self._CLUSTER_MARKERS) else [0.0, 1.0] + for doc in docs + ] + + @staticmethod + def _as_list(text) -> List[str]: + return text if isinstance(text, list) else [text] + + def embedding(self, input, model, **kwargs): + import threading + + docs = self._as_list(input) + self.embedded_batches.append(docs) + self.sync_embedding_thread_ids.append(threading.get_ident()) + return _make_embedding_response(self._vectors(docs)) + + async def aembedding(self, input, model, **kwargs): + docs = self._as_list(input) + self.embedded_batches.append(docs) + self.async_embedding_calls.append(docs) + self.async_embedding_kwargs.append(kwargs) + return _make_embedding_response(self._vectors(docs)) + + def utterance_embedding_count(self, utterance: str) -> int: + """How many times the given route utterance was embedded == number of route-index builds.""" + return sum(1 for batch in self.embedded_batches if utterance in batch) + + +class TestSemanticKeywordTierRules: + """Test embedding-based keyword_tier_rules matching.""" + + @pytest.mark.asyncio + async def test_semantic_match_routes_to_rule_tier(self, basic_config): + """A paraphrase (no literal keyword) still routes via embedding similarity.""" + fake_router = FakeEmbeddingRouter() + config = { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["kubernetes deployment", "container orchestration"], "tier": "REASONING"}, + {"keywords": ["hello", "thanks"], "tier": "SIMPLE"}, + ], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake_router, + complexity_router_config=config, + ) + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "help me roll out my k8s cluster today"}], + ) + assert result is not None + assert result.model == "o1-preview" # REASONING via semantic match + assert fake_router.async_embedding_calls, "expected an embedding call for the prompt" + + @pytest.mark.asyncio + async def test_tier_matches_on_best_utterance_not_diluted_by_others(self, basic_config): + """A tier with several keywords must match if the query is close to ANY of them, + not the average across all of them. A tier's route holds one utterance per keyword; + mean aggregation (the semantic_router library default) scores the query against the + *average* similarity across every utterance in the route, so a real match on one + keyword gets dragged below threshold by the tier's other, unrelated keywords. + """ + fake_router = FakeEmbeddingRouter() + config = { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["kubernetes deployment", "thanks", "goodbye"], "tier": "REASONING"}, + ], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake_router, + complexity_router_config=config, + ) + # Only "kubernetes deployment" is close to this query (cos 1.0); "thanks" and + # "goodbye" are orthogonal (cos 0.0). Mean over the three would be ~0.33, below the + # 0.5 threshold; the best (max) utterance alone clears it. + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "help me roll out my k8s cluster today"}], + ) + assert result is not None + assert result.model == "o1-preview" # REASONING via best-utterance semantic match + + @pytest.mark.asyncio + async def test_semantic_embedding_call_carries_caller_metadata(self, basic_config): + """The query embedding call must carry the caller's metadata/litellm_metadata + so embedding spend is attributed and budget-checked against the originating + key/team, instead of being logged as an untracked, unattributed cost. + """ + fake_router = FakeEmbeddingRouter() + config = { + **basic_config, + "keyword_tier_rules": [{"keywords": ["kubernetes deployment"], "tier": "REASONING"}], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake_router, + complexity_router_config=config, + ) + caller_metadata = {"user_api_key_hash": "hash-abc", "user_api_key_team_id": "team-1"} + caller_litellm_metadata = {"user_api_key": "hash-abc"} + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={"metadata": caller_metadata, "litellm_metadata": caller_litellm_metadata}, + messages=[{"role": "user", "content": "roll out my k8s cluster"}], + ) + assert result is not None + assert fake_router.async_embedding_kwargs, "expected an embedding call for the prompt" + assert fake_router.async_embedding_kwargs[0]["metadata"] == caller_metadata + assert fake_router.async_embedding_kwargs[0]["litellm_metadata"] == caller_litellm_metadata + + @pytest.mark.asyncio + async def test_semantic_embedding_call_strips_budget_reservation(self, basic_config): + """The embedding call must not carry the parent request's budget reservation. + + The reservation belongs to the routed completion this embedding helps select, not + to the embedding call. Forwarding it would let the embedding's cost callback + finalize the reservation, so the routed completion's callback then skips + incrementing the key/team budget - letting a caller run completions while only the + embedding cost is enforced. Key/team attribution fields must still be forwarded. + """ + fake_router = FakeEmbeddingRouter() + config = { + **basic_config, + "keyword_tier_rules": [{"keywords": ["kubernetes deployment"], "tier": "REASONING"}], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake_router, + complexity_router_config=config, + ) + caller_metadata = { + "user_api_key_hash": "hash-abc", + "user_api_key_team_id": "team-1", + "user_api_key_budget_reservation": {"reserved_cost": 1.0}, + "user_api_key_auth": {"models": ["voyage-3-5"], "budget_reservation": {"reserved_cost": 1.0}}, + } + await router.async_pre_routing_hook( + model="test-model", + request_kwargs={"metadata": caller_metadata, "litellm_metadata": dict(caller_metadata)}, + messages=[{"role": "user", "content": "roll out my k8s cluster"}], + ) + assert fake_router.async_embedding_kwargs, "expected an embedding call for the prompt" + # user_api_key_budget_reservation is stripped to prevent budget-bypass. + # user_api_key_auth is kept so _filter_deployments_by_model_access_groups + # scopes the embedding model selection to the caller's authorized groups, + # but its budget_reservation sub-field is removed because the cost callback + # falls back to reading the reservation from inside the auth object. + expected = { + "user_api_key_hash": "hash-abc", + "user_api_key_team_id": "team-1", + "user_api_key_auth": {"models": ["voyage-3-5"]}, + } + assert fake_router.async_embedding_kwargs[0]["metadata"] == expected + assert fake_router.async_embedding_kwargs[0]["litellm_metadata"] == expected + assert caller_metadata["user_api_key_auth"] == { + "models": ["voyage-3-5"], + "budget_reservation": {"reserved_cost": 1.0}, + } + + @pytest.mark.asyncio + async def test_semantic_routelayer_build_runs_off_event_loop(self, basic_config): + """Building the SemanticRouter embeds route utterances via a synchronous provider + call; it must run in a worker thread, not block the async event loop. + """ + import threading + + fake_router = FakeEmbeddingRouter() + config = { + **basic_config, + "keyword_tier_rules": [{"keywords": ["kubernetes deployment"], "tier": "REASONING"}], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake_router, + complexity_router_config=config, + ) + loop_thread_id = threading.get_ident() + await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "roll out my k8s cluster"}], + ) + # The route-index build did a synchronous embedding call... + assert fake_router.sync_embedding_thread_ids, "expected the route-index build to embed utterances" + # ...and none of it ran on the event-loop thread. + assert all(tid != loop_thread_id for tid in fake_router.sync_embedding_thread_ids) + + @pytest.mark.asyncio + async def test_concurrent_cold_start_builds_routelayer_once(self, basic_config): + """Concurrent first requests must not each construct the route index (which would + fire duplicate embedding calls); the lazy build happens exactly once. + """ + config = { + **basic_config, + "keyword_tier_rules": [{"keywords": ["kubernetes deployment"], "tier": "REASONING"}], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + + def _make_router(fake): + return ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake, + complexity_router_config=config, + ) + + # Baseline: a single cold request's route-index build embeds the route utterance once. + route_utterance = "kubernetes deployment" + baseline_fake = FakeEmbeddingRouter() + await _make_router(baseline_fake)._semantic_tier_override("roll out my k8s cluster", {}) + baseline_builds = baseline_fake.utterance_embedding_count(route_utterance) + assert baseline_builds >= 1 + + # Ten simultaneous cold-start requests must build the index the same number of + # times as one request - i.e. exactly once, not once per concurrent caller. + concurrent_fake = FakeEmbeddingRouter() + concurrent_router = _make_router(concurrent_fake) + await asyncio.gather( + *(concurrent_router._semantic_tier_override("roll out my k8s cluster", {}) for _ in range(10)) + ) + assert concurrent_fake.utterance_embedding_count(route_utterance) == baseline_builds + + @pytest.mark.asyncio + async def test_below_threshold_falls_back_to_scoring(self, basic_config): + """When no route clears the threshold, scoring decides the tier.""" + fake_router = FakeEmbeddingRouter() + config = { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["kubernetes deployment"], "tier": "REASONING"}, + ], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.9, + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake_router, + complexity_router_config=config, + ) + # "hello there friend" embeds orthogonal to the REASONING route (cos 0 < 0.9). + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "hello there friend"}], + ) + assert result is not None + assert result.model == "gpt-4o-mini" # SIMPLE via scoring fallback + + @pytest.mark.asyncio + async def test_route_embeddings_cached_across_requests(self, basic_config): + """The route layer is built once and reused on subsequent requests.""" + fake_router = FakeEmbeddingRouter() + config = { + **basic_config, + "keyword_tier_rules": [ + {"keywords": ["kubernetes deployment"], "tier": "REASONING"}, + ], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake_router, + complexity_router_config=config, + ) + assert router._semantic_routelayer is None + await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "roll out my k8s cluster"}], + ) + first_layer = router._semantic_routelayer + assert first_layer is not None + await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "scale my container cluster"}], + ) + assert router._semantic_routelayer is first_layer + + +class TestSemanticConfigValidation: + """Test config validation for semantic_keyword_matching.""" + + def test_semantic_without_embedding_model_raises(self): + with pytest.raises(ValidationError): + ComplexityRouterConfig( + semantic_keyword_matching=True, + keyword_tier_rules=[{"keywords": ["k8s"], "tier": "REASONING"}], + ) + + def test_semantic_without_rules_raises(self): + with pytest.raises(ValidationError): + ComplexityRouterConfig( + semantic_keyword_matching=True, + embedding_model="fake-embed", + ) + + def test_semantic_disabled_needs_no_embedding_model(self): + config = ComplexityRouterConfig( + keyword_tier_rules=[{"keywords": ["k8s"], "tier": "REASONING"}], + ) + assert config.semantic_keyword_matching is False + assert config.match_threshold == 0.5 + + def test_keyword_tier_rule_rejects_empty_keywords(self): + """A rule with no keywords is meaningless (and yields a zero-utterance semantic route).""" + with pytest.raises(ValidationError): + ComplexityRouterConfig(keyword_tier_rules=[{"keywords": [], "tier": "SIMPLE"}]) + + def test_keyword_tier_rule_rejects_blank_only_keywords(self): + """Whitespace-only keywords don't count as content.""" + with pytest.raises(ValidationError): + ComplexityRouterConfig(keyword_tier_rules=[{"keywords": [" ", ""], "tier": "SIMPLE"}]) + + def test_keyword_tier_rule_strips_and_drops_blank_keywords(self): + """Blank keywords mixed with real ones are dropped (not kept), and survivors trimmed. + + A stray "" would otherwise match-all in _keyword_matches and silently force this + tier for every request. + """ + config = ComplexityRouterConfig( + keyword_tier_rules=[{"keywords": ["", " deploy to k8s ", " ", "kubernetes"], "tier": "REASONING"}] + ) + assert config.keyword_tier_rules is not None + assert config.keyword_tier_rules[0].keywords == ["deploy to k8s", "kubernetes"] + + +class _StubEncoder: + """Minimal stand-in for LiteLLMRouterEncoder.aencode_queries, capturing the kwargs it was called with.""" + + def __init__(self): + self.aencode_queries_calls: List[Dict] = [] + + async def aencode_queries(self, docs, **kwargs): + self.aencode_queries_calls.append(kwargs) + return [[0.0]] + + +class _StubRouteLayer: + """Returns a fixed acall result so _semantic_tier_override branches can be exercised.""" + + def __init__(self, result): + self._result = result + self.encoder = _StubEncoder() + + async def acall(self, text=None, vector=None): + return self._result + + +class _RaisingEncoder: + """Simulates an embedding-provider failure during semantic matching.""" + + async def aencode_queries(self, docs, **kwargs): + raise RuntimeError("embedding provider unavailable") + + +class _RaisingRouteLayer: + def __init__(self): + self.encoder = _RaisingEncoder() + + async def acall(self, text=None, vector=None): + raise AssertionError("acall should not be reached when the encoder fails") + + +class TestKeywordOverrideEdgeCases: + """Cover the defensive branches of the lexical and semantic override helpers.""" + + def _semantic_router(self, mock_router_instance, basic_config): + config = { + **basic_config, + "keyword_tier_rules": [{"keywords": ["kubernetes"], "tier": "REASONING"}], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + return ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + + def test_lexical_override_none_when_no_rules(self, mock_router_instance, basic_config): + """No keyword_tier_rules configured -> lexical override is a no-op.""" + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=basic_config, + ) + assert router._lexical_tier_override("deploy to k8s and reason step by step") is None + + def test_semantic_routelayer_requires_embedding_model(self, mock_router_instance, basic_config): + """Building the route layer without an embedding model raises (defensive invariant).""" + config = {**basic_config, "keyword_tier_rules": [{"keywords": ["k8s"], "tier": "REASONING"}]} + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + assert router.config.embedding_model is None + with pytest.raises(ValueError, match="embedding_model is required"): + router._get_or_create_semantic_routelayer() + + @pytest.mark.asyncio + async def test_semantic_override_maps_first_of_list(self, mock_router_instance, basic_config): + """A list RouteChoice result maps to the first entry's tier.""" + from semantic_router.schema import RouteChoice + + router = self._semantic_router(mock_router_instance, basic_config) + router._semantic_routelayer = _StubRouteLayer([RouteChoice(name="COMPLEX"), RouteChoice(name="SIMPLE")]) + assert await router._semantic_tier_override("anything", {}) == ComplexityTier.COMPLEX + + @pytest.mark.asyncio + async def test_semantic_override_empty_list_returns_none(self, mock_router_instance, basic_config): + """An empty list result falls through to scoring.""" + router = self._semantic_router(mock_router_instance, basic_config) + router._semantic_routelayer = _StubRouteLayer([]) + assert await router._semantic_tier_override("anything", {}) is None + + @pytest.mark.asyncio + async def test_semantic_override_unknown_route_name_returns_none(self, mock_router_instance, basic_config): + """A matched route whose name is not a ComplexityTier is ignored.""" + from semantic_router.schema import RouteChoice + + router = self._semantic_router(mock_router_instance, basic_config) + router._semantic_routelayer = _StubRouteLayer(RouteChoice(name="NOT_A_TIER")) + assert await router._semantic_tier_override("anything", {}) is None + + @pytest.mark.asyncio + async def test_semantic_embedding_error_falls_back_to_scoring(self, mock_router_instance, basic_config): + """An embedding failure must not fail the request: the override yields None so + async_pre_routing_hook falls through to the complexity scorer. + """ + router = self._semantic_router(mock_router_instance, basic_config) + router._semantic_routelayer = _RaisingRouteLayer() + + # _resolve_keyword_tier_override swallows the error and returns None (no override). + assert await router._resolve_keyword_tier_override("roll out my k8s cluster", {}) is None + + # End-to-end, the hook still returns a routed model (from scoring) rather than raising. + result = await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "roll out my k8s cluster"}], + ) + assert result is not None + assert result.model in {"gpt-4o-mini", "gpt-4o", "claude-sonnet-4-20250514", "o1-preview"} + + +class TestSubCallMetadataSanitization: + """The proxy cost callback must not be able to recover the parent budget reservation + from sub-call metadata, in either of the shapes it knows how to read.""" + + def test_cost_callback_cannot_recover_reservation_from_sanitized_metadata(self): + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.hooks.proxy_track_cost_callback import ( + _get_budget_reservation_from_metadata, + ) + from litellm.router_strategy.complexity_router.complexity_router import ( + _classifier_call_metadata, + ) + + reservation = {"reserved_cost": 1.0} + auth_shapes = ( + {"models": ["gpt-4o"], "budget_reservation": dict(reservation)}, + UserAPIKeyAuth(api_key="sk-abc", budget_reservation=dict(reservation)), + ) + for auth in auth_shapes: + metadata = { + "user_api_key_hash": "hash-abc", + "user_api_key_budget_reservation": dict(reservation), + "user_api_key_auth": auth, + } + assert _get_budget_reservation_from_metadata(metadata) == reservation + + sanitized = _classifier_call_metadata(metadata) + assert sanitized is not None + assert sanitized["user_api_key_auth"] is not None + assert _get_budget_reservation_from_metadata(sanitized) is None + + def test_sanitized_auth_keeps_access_group_fields_and_leaves_original_untouched(self): + from litellm.proxy._types import UserAPIKeyAuth + from litellm.router_strategy.complexity_router.complexity_router import ( + _classifier_call_metadata, + ) + + auth = UserAPIKeyAuth( + api_key="sk-abc", + team_id="team-1", + budget_reservation={"reserved_cost": 1.0}, + ) + sanitized = _classifier_call_metadata({"user_api_key_auth": auth}) + assert sanitized is not None + sanitized_auth = sanitized["user_api_key_auth"] + assert sanitized_auth.budget_reservation is None + assert sanitized_auth.team_id == "team-1" + assert sanitized_auth.api_key == auth.api_key + assert auth.budget_reservation == {"reserved_cost": 1.0} + + +class TestRoutingDecisionCauseLogging: + """The info log must name what drove each routing decision so an operator can tell a + literal keyword match, a semantic keyword match, and the complexity scorer apart. + """ + + @pytest.fixture + def router_log_capture(self, caplog): + # verbose_router_logger sets propagate=False, so caplog's root handler never sees + # its records; attach the capture handler directly for the duration of the test. + caplog.set_level(logging.INFO, logger="LiteLLM Router") + verbose_router_logger.addHandler(caplog.handler) + try: + yield caplog + finally: + verbose_router_logger.removeHandler(caplog.handler) + + @pytest.mark.asyncio + async def test_literal_keyword_match_logs_its_cause( + self, mock_router_instance, basic_config, router_log_capture + ): + config = { + **basic_config, + "keyword_tier_rules": [{"keywords": ["deploy to k8s"], "tier": "REASONING"}], + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "please deploy to k8s now"}], + ) + assert "routing decision cause=literal_keyword_match" in router_log_capture.text + assert "tier=REASONING" in router_log_capture.text + # A literal match must not be mislabelled as semantic. + assert "cause=semantic_keyword_match" not in router_log_capture.text + + @pytest.mark.asyncio + async def test_semantic_keyword_match_logs_its_cause(self, basic_config, router_log_capture): + fake_router = FakeEmbeddingRouter() + config = { + **basic_config, + "keyword_tier_rules": [{"keywords": ["kubernetes deployment"], "tier": "REASONING"}], + "semantic_keyword_matching": True, + "embedding_model": "fake-embed", + "match_threshold": 0.5, + } + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=fake_router, + complexity_router_config=config, + ) + await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "help me roll out my k8s cluster today"}], + ) + assert "routing decision cause=semantic_keyword_match" in router_log_capture.text + assert "tier=REASONING" in router_log_capture.text + # A semantic match must not be mislabelled as literal. + assert "cause=literal_keyword_match" not in router_log_capture.text + + @pytest.mark.asyncio + async def test_complexity_scorer_logs_its_cause( + self, mock_router_instance, basic_config, router_log_capture + ): + # No keyword rules -> the scorer decides, and its line must be tagged as such. + router = ComplexityRouter( + model_name="test-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=basic_config, + ) + await router.async_pre_routing_hook( + model="test-model", + request_kwargs={}, + messages=[{"role": "user", "content": "What is the boiling point of water at sea level?"}], + ) + assert "routing decision cause=complexity_scorer" in router_log_capture.text + assert "score=" in router_log_capture.text + assert "cause=literal_keyword_match" not in router_log_capture.text + assert "cause=semantic_keyword_match" not in router_log_capture.text diff --git a/tests/test_litellm/router_strategy/test_router_routing_plugins.py b/tests/test_litellm/router_strategy/test_router_routing_plugins.py new file mode 100644 index 00000000000..e9c12d009e2 --- /dev/null +++ b/tests/test_litellm/router_strategy/test_router_routing_plugins.py @@ -0,0 +1,220 @@ +""" +Tests for Router(plugins=[...]) -- a pipeline of routing plugins that run +before the routing decision is made, narrowing the candidate deployment pool. + +Discussion: https://github.com/BerriAI/litellm/discussions/32168 +""" + +import pytest + +from litellm import Router +from litellm.types.router import RoutingContext + + +class LanguageDetector: + async def run(self, context: RoutingContext) -> RoutingContext: + context.signals["language-detector"] = {"lang": "en"} + return context + + +class DomainClassifier: + async def run(self, context: RoutingContext) -> RoutingContext: + context.signals["domain-classifier"] = {"domain": "coding", "confidence": 0.93} + return context + + +class TenantPolicy: + ALLOWED_PROVIDERS = {"acme-corp": {"openai", "anthropic"}} + + async def run(self, context: RoutingContext) -> RoutingContext: + tenant = context.metadata.get("tenant", "default") + allowed = self.ALLOWED_PROVIDERS.get(tenant, {"openai", "anthropic", "self-hosted"}) + context.candidate_models = [m for m in context.candidate_models if m.split("/")[0] in allowed] + context.signals["tenant-policy"] = {"tenant": tenant, "allowed_providers": sorted(allowed)} + return context + + +class BudgetPolicy: + COST_CAP_PER_TOKEN = 0.000005 + COST_BY_MODEL = { + "openai/gpt-4o-mini": 0.00000015, + "anthropic/claude-haiku-4-5": 0.000001, + "openai/gpt-5.1": 0.00003, + } + + async def run(self, context: RoutingContext) -> RoutingContext: + context.candidate_models = [ + m for m in context.candidate_models if self.COST_BY_MODEL.get(m, 0) <= self.COST_CAP_PER_TOKEN + ] + context.signals["budget-policy"] = {"daily_limit": 100} + return context + + +class BlockEverything: + async def run(self, context: RoutingContext) -> RoutingContext: + context.candidate_models = [] + return context + + +def _smart_router_model_list(): + return [ + { + "model_name": "smart-router", + "litellm_params": {"model": "openai/gpt-4o-mini", "mock_response": "cheap openai"}, + "model_info": {"tags": ["openai"]}, + }, + { + "model_name": "smart-router", + "litellm_params": {"model": "anthropic/claude-haiku-4-5", "mock_response": "anthropic"}, + "model_info": {"tags": ["anthropic"]}, + }, + { + "model_name": "smart-router", + "litellm_params": {"model": "openai/gpt-5.1", "mock_response": "expensive openai"}, + "model_info": {"tags": ["openai"]}, + }, + { + "model_name": "smart-router", + "litellm_params": {"model": "ollama/llama-3-70b", "mock_response": "self hosted"}, + "model_info": {"tags": ["self-hosted"]}, + }, + ] + + +@pytest.mark.asyncio +async def test_routing_plugin_pipeline_matches_jeann2013_e2e_scenario(): + """ + https://github.com/BerriAI/litellm/discussions/32168#discussioncomment-17608820 + + language plugin -> domain classifier -> tenant policy (openai+anthropic only) + -> budget policy (drops over-cap models) -> Router picks the best remaining + candidate. Must never land on the self-hosted or over-budget deployment. + """ + router = Router( + model_list=_smart_router_model_list(), + plugins=[LanguageDetector(), DomainClassifier(), TenantPolicy(), BudgetPolicy()], + ) + + response = await router.acompletion( + model="smart-router", + messages=[{"role": "user", "content": "Write a function to reverse a linked list."}], + metadata={"tenant": "acme-corp"}, + ) + + # response.model is the bare model name (litellm strips the provider/ prefix + # on the response), so compare against bare names rather than litellm_params.model + routed_model = response.model + + assert routed_model in {"gpt-4o-mini", "claude-haiku-4-5"} + assert routed_model not in {"llama-3-70b", "gpt-5.1"} + + +@pytest.mark.asyncio +async def test_routing_plugin_narrowing_to_zero_candidates_raises(): + """A plugin narrowing to nothing is a policy decision -- must raise, not silently + fall back to the unfiltered pool (that would defeat the policy it enforces).""" + router = Router( + model_list=_smart_router_model_list(), + plugins=[BlockEverything()], + ) + + with pytest.raises(ValueError, match="No deployments left after routing-plugin filtering"): + await router.acompletion( + model="smart-router", + messages=[{"role": "user", "content": "hi"}], + ) + + +def test_sync_get_available_deployment_rejects_configured_plugins(): + """ + Router.completion() (and any other sync entry point) resolves deployments via + the synchronous get_available_deployment(), which never runs the routing-plugin + pipeline. Silently allowing that would let a deny-all policy plugin be bypassed + just by calling the sync API -- must fail closed instead. + """ + router = Router(model_list=_smart_router_model_list(), plugins=[TenantPolicy()]) + + with pytest.raises(ValueError, match="routing-plugin pipeline"): + router.get_available_deployment(model="smart-router", messages=[{"role": "user", "content": "hi"}]) + + +def test_sync_router_completion_rejects_configured_plugins(): + """End-to-end: Router.completion() (the sync API) must not silently skip plugins either.""" + router = Router(model_list=_smart_router_model_list(), plugins=[TenantPolicy()]) + + with pytest.raises(ValueError, match="routing-plugin pipeline"): + router.completion(model="smart-router", messages=[{"role": "user", "content": "hi"}]) + + +@pytest.mark.asyncio +async def test_async_completion_with_unsupported_strategy_rejects_configured_plugins(): + """ + async_get_available_deployment() itself delegates to the synchronous selector + for routing strategies outside {simple-shuffle, usage-based-routing-v2, + cost-based-routing, latency-based-routing, least-busy} -- e.g. "usage-based-routing" + (v1, not v2) -- which would silently bypass the plugin pipeline on the async path too. + """ + router = Router( + model_list=_smart_router_model_list(), + plugins=[TenantPolicy()], + routing_strategy="usage-based-routing", + ) + + with pytest.raises(ValueError, match="routing-plugin pipeline"): + await router.acompletion(model="smart-router", messages=[{"role": "user", "content": "hi"}]) + + +@pytest.mark.asyncio +async def test_router_without_plugins_is_unaffected(): + """Regression guard: a Router with no `plugins` configured behaves exactly as before.""" + router = Router( + model_list=[ + { + "model_name": "smart-router", + "litellm_params": {"model": "openai/gpt-4o-mini", "mock_response": "hi"}, + }, + ], + ) + response = await router.acompletion( + model="smart-router", + messages=[{"role": "user", "content": "hi"}], + ) + assert response.choices[0].message.content == "hi" + + +@pytest.mark.asyncio +async def test_run_routing_plugins_narrows_candidates_and_records_signals(): + """Unit-level check of _run_routing_plugins in isolation, independent of acompletion.""" + router = Router( + model_list=_smart_router_model_list(), + plugins=[LanguageDetector(), DomainClassifier(), TenantPolicy(), BudgetPolicy()], + ) + request_kwargs = {"metadata": {"tenant": "acme-corp"}} + + context = await router._run_routing_plugins( + model="smart-router", + request_kwargs=request_kwargs, + messages=[{"role": "user", "content": "hi"}], + ) + + assert context.candidate_models == ["openai/gpt-4o-mini", "anthropic/claude-haiku-4-5"] + assert context.signals["domain-classifier"]["domain"] == "coding" + assert request_kwargs["metadata"]["_routing_plugin_candidate_models"] == context.candidate_models + + +def test_filter_by_routing_plugin_candidates_narrows_and_raises_when_empty(): + """Unit-level check of _filter_by_routing_plugin_candidates in isolation.""" + router = Router(model_list=_smart_router_model_list(), plugins=[TenantPolicy()]) + healthy_deployments = router.model_list + + narrowed = router._filter_by_routing_plugin_candidates( + healthy_deployments=healthy_deployments, + request_kwargs={"metadata": {"_routing_plugin_candidate_models": ["openai/gpt-4o-mini"]}}, + ) + assert [d["litellm_params"]["model"] for d in narrowed] == ["openai/gpt-4o-mini"] + + with pytest.raises(ValueError, match="No deployments left after routing-plugin filtering"): + router._filter_by_routing_plugin_candidates( + healthy_deployments=healthy_deployments, + request_kwargs={"metadata": {"_routing_plugin_candidate_models": ["nonexistent/model"]}}, + ) diff --git a/tests/test_litellm/router_strategy/test_router_tag_routing.py b/tests/test_litellm/router_strategy/test_router_tag_routing.py index a6e39ec3c0a..eb289095c51 100644 --- a/tests/test_litellm/router_strategy/test_router_tag_routing.py +++ b/tests/test_litellm/router_strategy/test_router_tag_routing.py @@ -1,29 +1,17 @@ #### What this tests #### # This tests litellm router -import asyncio import os import sys -import time -import traceback -import openai import pytest -sys.path.insert( - 0, os.path.abspath("../..") -) # Adds the parent directory to the system path +sys.path.insert(0, os.path.abspath("../..")) # Adds the parent directory to the system path import logging import os -from collections import defaultdict -from concurrent.futures import ThreadPoolExecutor -from unittest.mock import AsyncMock, MagicMock, patch -import httpx -from dotenv import load_dotenv import litellm -from litellm import Router from litellm._logging import verbose_logger @@ -66,10 +54,7 @@ async def test_router_free_paid_tier(): mock_response="Tell me a joke.", ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "very-cheap-model" @@ -82,10 +67,7 @@ async def test_router_free_paid_tier(): mock_response="Tell me a joke.", ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "very-expensive-model" @@ -141,10 +123,7 @@ async def test_router_free_paid_tier_embeddings(): mock_response=[1, 2, 3], ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "very-cheap-model" @@ -157,10 +136,7 @@ async def test_router_free_paid_tier_embeddings(): mock_response=[1, 2, 3], ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "very-expensive-model" @@ -212,10 +188,7 @@ async def test_default_tagged_deployments(): mock_response="Tell me a joke.", ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "default-model" @@ -228,10 +201,7 @@ async def test_default_tagged_deployments(): mock_response="Tell me a joke.", ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "default-model" @@ -244,10 +214,7 @@ async def test_default_tagged_deployments(): mock_response="Tell me a joke.", ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "default-model" @@ -257,10 +224,6 @@ async def test_error_from_tag_routing(): """ Tests the correct error raised when no deployments found for tag """ - import logging - - from litellm._logging import verbose_logger - verbose_logger.setLevel(logging.DEBUG) router = litellm.Router( model_list=[ @@ -294,7 +257,7 @@ async def test_error_from_tag_routing(): ) try: - response = await router.acompletion( + await router.acompletion( model="gpt-4", messages=[{"role": "user", "content": "Tell me a joke."}], metadata={"tags": ["paid"]}, @@ -306,7 +269,6 @@ async def test_error_from_tag_routing(): from litellm.types.router import RouterErrors assert RouterErrors.no_deployments_with_tag_routing.value in str(e) - print("got expected exception = ", e) pass @@ -332,16 +294,10 @@ def test_tag_routing_with_list_of_tags_match_all(): from litellm.router_strategy.tag_based_routing import is_valid_deployment_tag assert is_valid_deployment_tag(["teamA", "teamB"], ["teamA"], match_any=False) - assert is_valid_deployment_tag( - ["teamA", "teamB"], ["teamA", "teamB"], match_any=False - ) - assert not is_valid_deployment_tag( - ["teamA", "teamB", "teamC"], ["teamA", "teamD"], match_any=False - ) + assert is_valid_deployment_tag(["teamA", "teamB"], ["teamA", "teamB"], match_any=False) + assert not is_valid_deployment_tag(["teamA", "teamB", "teamC"], ["teamA", "teamD"], match_any=False) assert not is_valid_deployment_tag(["teamA"], ["teamA", "teamB"], match_any=False) - assert not is_valid_deployment_tag( - ["teamA", "teamB"], ["teamA", "teamC"], match_any=False - ) + assert not is_valid_deployment_tag(["teamA", "teamB"], ["teamA", "teamC"], match_any=False) assert not is_valid_deployment_tag(["teamA", "teamB"], [], match_any=False) assert not is_valid_deployment_tag(["default"], ["teamA"], match_any=False) @@ -413,10 +369,7 @@ async def test_router_free_paid_tier_with_responses_api(): mock_response="Tell me a joke.", ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "very-cheap-model" @@ -429,10 +382,7 @@ async def test_router_free_paid_tier_with_responses_api(): mock_response="Tell me a joke.", ) - print("Response: ", response) - response_extra_info = response._hidden_params - print("response_extra_info: ", response_extra_info) assert response_extra_info["model_id"] == "very-expensive-model" @@ -455,9 +405,7 @@ def test_get_tags_from_request_kwargs_various_inputs(): assert _get_tags_from_request_kwargs({"metadata": None}) == [] # Indirect via "litellm_params" - metadata inside - assert _get_tags_from_request_kwargs( - {"litellm_params": {"metadata": {"tags": ["paid"]}}} - ) == ["paid"] + assert _get_tags_from_request_kwargs({"litellm_params": {"metadata": {"tags": ["paid"]}}}) == ["paid"] assert _get_tags_from_request_kwargs({"litellm_params": {"metadata": None}}) == [] assert _get_tags_from_request_kwargs({"litellm_params": {}}) == [] @@ -473,3 +421,601 @@ def test_get_tags_from_request_kwargs_various_inputs(): # No relevant keys present assert _get_tags_from_request_kwargs({"foo": "bar"}) == [] + + +# --- _split_tags unit tests --- + + +def test_split_tags_positive_only(): + from litellm.router_strategy.tag_based_routing import _split_tags + + positive, excluded = _split_tags(["paid", "teamA"]) + assert positive == ["paid", "teamA"] + assert excluded == [] + + +def test_split_tags_negation_only(): + from litellm.router_strategy.tag_based_routing import _split_tags + + positive, excluded = _split_tags(["!provider:anthropic"]) + assert positive == [] + assert excluded == ["provider:anthropic"] + + +def test_split_tags_mixed(): + from litellm.router_strategy.tag_based_routing import _split_tags + + positive, excluded = _split_tags(["paid", "!provider:anthropic", "!inference:cerebras"]) + assert positive == ["paid"] + assert len(excluded) == 2 + + +def test_split_tags_bare_bang_skipped(): + from litellm.router_strategy.tag_based_routing import _split_tags + + # A bare "!" with nothing after it is not a valid negation tag; skip it + positive, excluded = _split_tags(["paid", "!"]) + assert positive == ["paid"] + assert excluded == [] + + +def test_split_tags_empty(): + from litellm.router_strategy.tag_based_routing import _split_tags + + positive, excluded = _split_tags([]) + assert positive == [] + assert excluded == [] + + +# --- get_deployments_for_tag negation integration tests --- + + +@pytest.mark.asyncio() +async def test_negation_excludes_matching_deployments(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic", "model:claude-sonnet-4-6"], + }, + "model_info": {"id": "anthropic-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:openai", "model:gpt-4o"], + }, + "model_info": {"id": "openai-model"}, + }, + ], + enable_tag_filtering=True, + ) + + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:anthropic"]}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "openai-model" + + +@pytest.mark.asyncio() +async def test_negation_multiple_tags_exclude_multiple_providers(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic"], + }, + "model_info": {"id": "anthropic-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:openai"], + }, + "model_info": {"id": "openai-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:vertex"], + }, + "model_info": {"id": "vertex-model"}, + }, + ], + enable_tag_filtering=True, + ) + + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:anthropic", "!provider:openai"]}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "vertex-model" + + +@pytest.mark.asyncio() +async def test_negation_with_positive_tag(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["paid", "provider:anthropic"], + }, + "model_info": {"id": "anthropic-paid"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["paid", "provider:openai"], + }, + "model_info": {"id": "openai-paid"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["free", "provider:openai"], + }, + "model_info": {"id": "openai-free"}, + }, + ], + enable_tag_filtering=True, + ) + + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["paid", "!provider:anthropic"]}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "openai-paid" + + +@pytest.mark.asyncio() +async def test_negation_all_excluded_raises(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic"], + }, + "model_info": {"id": "anthropic-model"}, + }, + ], + enable_tag_filtering=True, + ) + + with pytest.raises(Exception) as exc_info: + await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:anthropic"]}, + mock_response="hi", + ) + + from litellm.types.router import RouterErrors + + assert RouterErrors.no_deployments_with_tag_routing.value in str(exc_info.value) + + +@pytest.mark.asyncio() +async def test_negation_ban_only_cannot_escape_default_pool(): + # A ban-only request must not route to tagged deployments outside the default pool. + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["default"], + }, + "model_info": {"id": "default-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["paid"], + }, + "model_info": {"id": "paid-model"}, + }, + ], + enable_tag_filtering=True, + ) + + # Sending only "!default" must NOT route to the paid deployment. + # The base pool for ban-only is the default pool; banning the only + # default deployment should raise rather than falling through to paid. + with pytest.raises(Exception) as exc_info: + await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!default"]}, + mock_response="hi", + ) + + from litellm.types.router import RouterErrors + + assert RouterErrors.no_deployments_with_tag_routing.value in str(exc_info.value) + + +@pytest.mark.asyncio() +async def test_negation_ban_only_respects_default_pool(): + # A ban-only request stays within the default pool; non-default deployments + # remain unreachable even when the negation tag is unrelated to the default. + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["default"], + }, + "model_info": {"id": "default-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["paid"], + }, + "model_info": {"id": "paid-model"}, + }, + ], + enable_tag_filtering=True, + ) + + # "!paid" bans the paid deployment, but the base pool for ban-only is + # already restricted to defaults; default-model must still be returned. + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!paid"]}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "default-model" + + +@pytest.mark.asyncio() +async def test_negation_untagged_deployment_kept(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic"], + }, + "model_info": {"id": "anthropic-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + }, + "model_info": {"id": "untagged-model"}, + }, + ], + enable_tag_filtering=True, + ) + + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:anthropic"]}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "untagged-model" + + +@pytest.mark.asyncio() +async def test_negation_literal_only_no_partial_match(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic-haiku"], + }, + "model_info": {"id": "anthropic-haiku-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:openai"], + }, + "model_info": {"id": "openai-model"}, + }, + ], + enable_tag_filtering=True, + ) + + # "!provider:anthropic" should NOT match "provider:anthropic-haiku" — exact tag match only + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:anthropic"]}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] in ( + "anthropic-haiku-model", + "openai-model", + ) + + +@pytest.mark.asyncio() +async def test_negation_regex_pattern_treated_as_literal(): + # "!provider:(anthropic|openai)" looks like a regex but is treated as a literal string. + # It does NOT exclude deployments tagged "provider:anthropic" or "provider:openai". + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic"], + }, + "model_info": {"id": "anthropic-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:openai"], + }, + "model_info": {"id": "openai-model"}, + }, + ], + enable_tag_filtering=True, + ) + + # The regex-like string matches no deployment tag literally, so all + # candidates survive and both model IDs are reachable. + seen_ids = set() + for _ in range(10): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:(anthropic|openai)"]}, + mock_response="hi", + ) + seen_ids.add(response._hidden_params["model_id"]) + + assert seen_ids == {"anthropic-model", "openai-model"} + + +@pytest.mark.asyncio() +async def test_positive_tags_unchanged_by_negation(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["free"], + }, + "model_info": {"id": "free-model"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["paid"], + }, + "model_info": {"id": "paid-model"}, + }, + ], + enable_tag_filtering=True, + ) + + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["free"]}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "free-model" + + +@pytest.mark.asyncio() +async def test_negation_skips_banned_group_and_uses_fallback(): + router = litellm.Router( + model_list=[ + { + "model_name": "primary", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic"], + }, + "model_info": {"id": "anthropic-primary"}, + }, + { + "model_name": "fallback", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:openai"], + }, + "model_info": {"id": "openai-fallback"}, + }, + ], + fallbacks=[{"primary": ["fallback"]}], + enable_tag_filtering=True, + ) + + response = await router.acompletion( + model="primary", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:anthropic"]}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "openai-fallback" + + +@pytest.mark.asyncio() +async def test_negation_exhausts_entire_fallback_chain(): + router = litellm.Router( + model_list=[ + { + "model_name": "primary", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic"], + }, + "model_info": {"id": "anthropic-primary"}, + }, + { + "model_name": "fallback", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic"], + }, + "model_info": {"id": "anthropic-fallback"}, + }, + ], + fallbacks=[{"primary": ["fallback"]}], + enable_tag_filtering=True, + ) + + with pytest.raises(Exception) as exc_info: + await router.acompletion( + model="primary", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:anthropic"]}, + mock_response="hi", + ) + + from litellm.types.router import RouterErrors + + assert RouterErrors.no_deployments_with_tag_routing.value in str(exc_info.value) + + +@pytest.mark.asyncio() +async def test_tag_regex_survives_when_negation_removes_other_deployment(): + # Negation removes a plain-tagged deployment; the surviving tag_regex deployment + # is still matched by User-Agent and selected. + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tag_regex": ["^User-Agent: claude-code\\/"], + }, + "model_info": {"id": "claude-code-deployment"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:anthropic"], + }, + "model_info": {"id": "anthropic-deployment"}, + }, + ], + enable_tag_filtering=True, + tag_filtering_match_any=True, + ) + + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!provider:anthropic"], "user_agent": "claude-code/1.2.3"}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "claude-code-deployment" + + +@pytest.mark.asyncio() +async def test_negation_removes_tag_regex_deployment_falls_to_ban_only(): + # When a negation tag removes the only tag_regex deployment, no regex deployments + # remain in the candidate pool. has_tag_filter becomes False, ban_only fires, + # and the remaining plain-tagged deployment is returned via the ban-only path. + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tag_regex": ["^User-Agent: claude-code\\/"], + "tags": ["group:claude"], + }, + "model_info": {"id": "claude-code-deployment"}, + }, + { + "model_name": "gpt-4", + "litellm_params": { + "model": "gpt-4o-mini", + "api_base": "https://exampleopenaiendpoint-production.up.railway.app/", + "tags": ["provider:openai"], + }, + "model_info": {"id": "openai-deployment"}, + }, + ], + enable_tag_filtering=True, + tag_filtering_match_any=True, + ) + + # !group:claude removes the tag_regex deployment from candidates, so no regex + # deployments remain. The ban-only path fires and returns the openai deployment. + for _ in range(5): + response = await router.acompletion( + model="gpt-4", + messages=[{"role": "user", "content": "hi"}], + metadata={"tags": ["!group:claude"], "user_agent": "claude-code/1.2.3"}, + mock_response="hi", + ) + assert response._hidden_params["model_id"] == "openai-deployment" diff --git a/tests/test_litellm/router_utils/test_add_retry_fallback_headers.py b/tests/test_litellm/router_utils/test_add_retry_fallback_headers.py index 2aee0f0a4ef..3a0deeb13d8 100644 --- a/tests/test_litellm/router_utils/test_add_retry_fallback_headers.py +++ b/tests/test_litellm/router_utils/test_add_retry_fallback_headers.py @@ -140,3 +140,25 @@ def test_get_fallback_errors_from_headers_invalid_json_returns_empty(): def test_get_fallback_errors_from_headers_missing_key_returns_empty(): result = get_fallback_errors_from_headers({}) assert result == [] + + +def test_get_hidden_params_dict_with_dict_response(): + response = {"id": "msg_1", "usage": {"input_tokens": 1, "output_tokens": 2}} + assert get_hidden_params_dict(response) == {} + + hidden_params = get_hidden_params_dict(response, create=True) + assert hidden_params == {} + assert response["_hidden_params"] == {} + + response["_hidden_params"] = {"additional_headers": {"x-test": "1"}} + assert get_hidden_params_dict(response) == { + "additional_headers": {"x-test": "1"}, + } + + +def test_add_fallback_headers_to_dict_response(): + response = {"id": "msg_1"} + result = add_fallback_headers_to_response(response=response, attempted_fallbacks=1) + + assert result is response + assert response["_hidden_params"]["additional_headers"]["x-litellm-attempted-fallbacks"] == 1 diff --git a/tests/test_litellm/secret_managers/test_base_secret_manager.py b/tests/test_litellm/secret_managers/test_base_secret_manager.py new file mode 100644 index 00000000000..cba6a99ab7f --- /dev/null +++ b/tests/test_litellm/secret_managers/test_base_secret_manager.py @@ -0,0 +1,59 @@ +""" +Test raise_if_unsafe_secret_name, the shared guard applied before secret_name +reaches a secret manager backend. +""" + +import os +import sys + +import pytest + +sys.path.insert(0, os.path.abspath("../..")) # Adds the parent directory to the system path + +from litellm.secret_managers.base_secret_manager import raise_if_unsafe_secret_name + + +@pytest.mark.parametrize( + "secret_name", + [ + "..", + "../../../other-app/creds", + "litellm/../../secret", + "foo/../bar", + "foo/..", + "../foo", + "foo\nbar", + "foo\rbar", + "foo\x00bar", + "foo\x7fbar", + "foo\x85bar", + "foo
bar", + "foo
bar", + ], +) +def test_raise_if_unsafe_secret_name_rejects_traversal_and_line_breaks(secret_name): + with pytest.raises(ValueError): + raise_if_unsafe_secret_name(secret_name) + + +@pytest.mark.parametrize( + "secret_name", + [ + "plain-alias", + "my-key-123", + "prod/my-service-key", + "team/user@example.com", + "foo: bar", + "foo # bar", + "foo?evil=1", + "foo#bar", + "a" * 500, + "release-1.0..2", + "my..key", + "..foo", + "foo..", + "v2.0..1-beta", + ], +) +def test_raise_if_unsafe_secret_name_allows_legitimate_aliases(secret_name): + raise_if_unsafe_secret_name(secret_name) diff --git a/tests/test_litellm/test_budget_ratchet_check.py b/tests/test_litellm/test_budget_ratchet_check.py index 77cee8a485c..1972c1b6386 100644 --- a/tests/test_litellm/test_budget_ratchet_check.py +++ b/tests/test_litellm/test_budget_ratchet_check.py @@ -1,9 +1,8 @@ """Tests for scripts/budget_ratchet_check.py. -The guard's contract is "baselines and ceilings may only fall": a raised ceiling, a -raised baseline (even when slack is cut to keep the ceiling flat), a dropped rule, or -a deleted file is a regression, while a lowered/equal baseline and ceiling, a brand-new -rule, or a brand-new budget file is fine. Each branch is pinned here. +The guard's contract is "limits may only fall": a raised limit, a dropped rule, or +a deleted file is a regression, while a lowered/equal limit, a brand-new rule, or a +brand-new budget file is fine. Each branch is pinned here. """ import importlib.util @@ -19,69 +18,64 @@ ratchet = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(ratchet) -def _spec_of(baseline, slack): - return {"baseline": baseline, "slack": slack} +def _spec_of(limit): + return {"limit": limit} -def test_caps_sum_baseline_and_slack_and_skip_malformed(): - caps = ratchet._caps({"LIT006": _spec_of(1013, 10), "junk": 5}) - assert caps == {"LIT006": 1023} # malformed (non-dict) spec ignored +def test_limits_read_the_limit_and_skip_malformed(): + limits = ratchet._limits({"LIT006": _spec_of(1023), "junk": 5}) + assert limits == {"LIT006": 1023} # malformed (non-dict) spec ignored -def test_raised_ceiling_is_a_regression(): - base = {"LIT006": _spec_of(1013, 10)} - head = {"LIT006": _spec_of(1013, 11)} # cap 1023 -> 1024 +def test_limits_fall_back_to_legacy_baseline_plus_slack(): + # The base side of a diff can predate the `limit` migration; its ceiling is + # baseline + slack, read on the same footing as a new-schema `limit`. + assert ratchet._limits({"LIT006": {"baseline": 1013, "slack": 10}}) == {"LIT006": 1023} + + +def test_migration_from_legacy_schema_to_equal_limit_is_clean(): + # baseline+slack (1023) -> limit 1023 is the same ceiling, so no regression. + base = {"LIT006": {"baseline": 1013, "slack": 10}} + assert ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1023)}) == [] + # ...and a genuine raise across the migration is still caught. + regs = ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1024)}) + assert [r.rule for r in regs] == ["LIT006"] and "1023 -> 1024" in regs[0].detail + + +def test_raised_limit_is_a_regression(): + base = {"LIT006": _spec_of(1023)} + head = {"LIT006": _spec_of(1024)} regs = ratchet.regressions_for("b.json", base, head) assert [r.rule for r in regs] == ["LIT006"] assert "1023 -> 1024" in regs[0].detail -def test_lowered_or_equal_ceiling_is_clean(): - base = {"LIT006": _spec_of(1013, 10)} - # baseline drops, slack flat -> ceiling falls - assert ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1000, 10)}) == [] +def test_lowered_or_equal_limit_is_clean(): + base = {"LIT006": _spec_of(1023)} + # limit drops + assert ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1000)}) == [] # nothing changes - assert ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1013, 10)}) == [] - # slack cut while baseline holds -> ceiling falls, baseline flat - assert ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1013, 0)}) == [] - - -def test_raised_baseline_is_a_regression_even_when_ceiling_held_flat(): - # baseline 1013 -> 1023 with slack cut 10 -> 0 keeps the ceiling at 1023, but a - # higher baseline bakes in more accepted debt and must still surface as a regression - base = {"LIT006": _spec_of(1013, 10)} - regs = ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1023, 0)}) - assert [r.rule for r in regs] == ["LIT006"] - assert "baseline raised 1013 -> 1023" in regs[0].detail - assert "ceiling raised" not in regs[0].detail - - -def test_raised_baseline_and_ceiling_report_both_reasons(): - base = {"LIT006": _spec_of(1013, 10)} - regs = ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1100, 10)}) - assert [r.rule for r in regs] == ["LIT006"] - assert "ceiling raised 1023 -> 1110" in regs[0].detail - assert "baseline raised 1013 -> 1100" in regs[0].detail + assert ratchet.regressions_for("b.json", base, {"LIT006": _spec_of(1023)}) == [] def test_dropped_rule_is_a_regression(): - regs = ratchet.regressions_for("b.json", {"LIT007": _spec_of(0, 0)}, {}) + regs = ratchet.regressions_for("b.json", {"LIT007": _spec_of(0)}, {}) assert [r.rule for r in regs] == ["LIT007"] assert "dropped" in regs[0].detail def test_new_rule_in_head_is_clean(): - assert ratchet.regressions_for("b.json", {}, {"new-rule": _spec_of(5, 0)}) == [] + assert ratchet.regressions_for("b.json", {}, {"new-rule": _spec_of(5)}) == [] def test_deleted_budget_file_is_a_regression(): - regs = ratchet.regressions_for("b.json", {"LIT006": _spec_of(1, 0)}, None) + regs = ratchet.regressions_for("b.json", {"LIT006": _spec_of(1)}, None) assert [r.rule for r in regs] == ["*"] assert "deleted" in regs[0].detail def test_new_budget_file_has_nothing_to_ratchet(): - assert ratchet.regressions_for("b.json", None, {"LIT006": _spec_of(1, 0)}) == [] + assert ratchet.regressions_for("b.json", None, {"LIT006": _spec_of(1)}) == [] def test_default_budgets_watch_every_budget_file_in_the_repo(): diff --git a/tests/test_litellm/test_check_type_discipline.py b/tests/test_litellm/test_check_type_discipline.py index 436904b017c..13edf6d1a95 100644 --- a/tests/test_litellm/test_check_type_discipline.py +++ b/tests/test_litellm/test_check_type_discipline.py @@ -62,12 +62,38 @@ def test_noqa_with_codes_and_reason_is_clean(tmp_path): assert "LIT003" not in _codes(tmp_path, "x = 1 # noqa: TID251 # legacy import, removed in #123\n") -def test_ignore_without_reason_is_flagged(tmp_path): - assert "LIT004" in _codes(tmp_path, "x = 1 # type: ignore[arg-type]\n") +def test_pyright_ignore_without_codes_is_flagged(tmp_path): + assert "LIT004" in _codes(tmp_path, "x = 1 # pyright: ignore\n") + + +def test_pyright_ignore_without_reason_is_flagged(tmp_path): + assert "LIT004" in _codes(tmp_path, "x = 1 # pyright: ignore[reportArgumentType]\n") def test_ignore_with_codes_and_reason_is_clean(tmp_path): - assert "LIT004" not in _codes(tmp_path, "x = 1 # pyright: ignore[reportArgumentType] # upstream stub is wrong\n") + codes = _codes(tmp_path, "x = 1 # pyright: ignore[reportArgumentType] # upstream stub is wrong\n") + assert "LIT004" not in codes + assert "LIT009" not in codes + + +def test_bare_type_ignore_is_flagged(tmp_path): + assert "LIT009" in _codes(tmp_path, "x = 1 # type: ignore\n") + + +def test_type_ignore_with_codes_and_reason_is_still_flagged(tmp_path): + codes = _codes(tmp_path, "x = 1 # type: ignore[arg-type] # inertness is the point\n") + assert "LIT009" in codes + assert "LIT004" not in codes + + +def test_prose_mentioning_type_ignored_is_not_flagged(tmp_path): + assert "LIT009" not in _codes(tmp_path, "x = 1 # type: ignored by the stub refresh, revisit\n") + + +def test_mypy_ignore_shape_is_lit004_not_lit009(tmp_path): + codes = _codes(tmp_path, "x = 1 # mypy: ignore[assignment]\n") + assert "LIT004" in codes + assert "LIT009" not in codes def test_ok_suppression_without_reason_is_flagged(tmp_path): @@ -196,4 +222,4 @@ def test_typed_args_is_clean_but_kwargs_ok_suppresses(tmp_path): def test_budget_covers_exactly_the_checker_rules(): budget = json.loads((_REPO_ROOT / "type-discipline-budget.json").read_text()) - assert set(budget) == {f"LIT00{n}" for n in range(1, 9)} + assert set(budget) == {f"LIT00{n}" for n in range(1, 10)} diff --git a/tests/test_litellm/test_circleci_path_filter.py b/tests/test_litellm/test_circleci_path_filter.py new file mode 100644 index 00000000000..b8e763b4979 --- /dev/null +++ b/tests/test_litellm/test_circleci_path_filter.py @@ -0,0 +1,167 @@ +"""Regression tests for CircleCI change-based job gating. + +`.circleci/scripts/classify_changes.sh` is the pure decision function behind +`path_filter.sh`: given the list of files a PR changed (on stdin) and a job +category, it prints `run` or `skip`. The gating contract we lock in here: + + * docs-only changes (``*.md``, ``*.mdx``, ``docs/``) run nothing + * client-only changes (``ui/``) run client jobs but skip backend jobs + * any backend change runs both client and backend jobs + +If this logic silently regresses, real test jobs get skipped, so these cases +are the guardrail against that. +""" + +from __future__ import annotations + +import os +import shutil +import subprocess +from pathlib import Path + +import pytest + +SCRIPTS_DIR = Path(__file__).resolve().parents[2] / ".circleci" / "scripts" +SCRIPT = SCRIPTS_DIR / "classify_changes.sh" +PATH_FILTER = SCRIPTS_DIR / "path_filter.sh" + + +def classify(category: str, changed: list[str]) -> str: + result = subprocess.run( + ["bash", str(SCRIPT), category], + input="\n".join(changed), + capture_output=True, + text=True, + check=True, + ) + return result.stdout.strip() + + +DOCS = ["README.md", "docs/my_website/index.mdx", "litellm/anywhere.md"] +CLIENT = ["ui/litellm-dashboard/src/App.tsx"] +BACKEND = ["litellm/main.py"] + + +@pytest.mark.parametrize( + "category,changed,expected", + [ + # docs-only: skip everything + ("backend", DOCS, "skip"), + ("client", DOCS, "skip"), + ("backend", [], "skip"), + ("client", [], "skip"), + # client-only: backend skips, client runs + ("backend", CLIENT, "skip"), + ("client", CLIENT, "run"), + ("backend", CLIENT + DOCS, "skip"), + ("client", CLIENT + DOCS, "run"), + # any backend change: both run ("backend runs both") + ("backend", BACKEND, "run"), + ("client", BACKEND, "run"), + ("backend", BACKEND + DOCS, "run"), + ("client", BACKEND + DOCS, "run"), + ("backend", BACKEND + CLIENT, "run"), + ("client", BACKEND + CLIENT, "run"), + ], +) +def test_classify_decisions(category: str, changed: list[str], expected: str) -> None: + assert classify(category, changed) == expected + + +def test_markdown_under_ui_counts_as_client_not_docs() -> None: + assert classify("client", ["ui/litellm-dashboard/README.md"]) == "run" + assert classify("backend", ["ui/litellm-dashboard/README.md"]) == "skip" + + +def test_non_docs_directory_with_docs_in_name_is_backend() -> None: + assert classify("backend", ["documentation_tests/foo.py"]) == "run" + + +def test_unknown_category_fails_open_to_run() -> None: + assert classify("mystery", DOCS) == "run" + + +def _git(cwd: Path, *args: str) -> None: + subprocess.run(["git", *args], cwd=cwd, check=True, capture_output=True, text=True) + + +def _pr_repo(tmp_path: Path, feature_files: dict[str, str]) -> Path: + """A repo whose HEAD is a feature branch off `main` with `feature_files` changed.""" + remote = tmp_path / "remote.git" + subprocess.run(["git", "init", "-q", "--bare", str(remote)], check=True) + work = tmp_path / "work" + work.mkdir() + _git(work, "init", "-q", "-b", "main") + _git(work, "config", "user.email", "t@t") + _git(work, "config", "user.name", "t") + _git(work, "remote", "add", "origin", str(remote)) + (work / "litellm_core.py").write_text("x\n") + _git(work, "add", ".") + _git(work, "commit", "-qm", "base") + _git(work, "push", "-q", "origin", "main") + _git(work, "checkout", "-q", "-b", "litellm_feature") + for rel, content in feature_files.items(): + target = work / rel + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(content) + _git(work, "add", "-A") + _git(work, "commit", "-qm", "feature") + return work + + +def _run_path_filter(work: Path, tmp_path: Path, category: str, scripts_dir: Path, is_pr: bool = True): + bin_dir = tmp_path / "bin" + bin_dir.mkdir(exist_ok=True) + stub = bin_dir / "circleci-agent" + stub.write_text("#!/usr/bin/env bash\necho \"[stub] circleci-agent $*\"\nexit 0\n") + stub.chmod(0o755) + env = dict(os.environ) + env["PATH"] = f"{bin_dir}{os.pathsep}{env['PATH']}" + env.pop("CIRCLE_PULL_REQUEST", None) + if is_pr: + env["CIRCLE_PULL_REQUEST"] = "https://github.com/x/y/pull/1" + return subprocess.run( + ["bash", str(scripts_dir / "path_filter.sh"), category], + cwd=work, + capture_output=True, + text=True, + env=env, + ) + + +def test_path_filter_halts_docs_only_pr(tmp_path: Path) -> None: + work = _pr_repo(tmp_path, {"README.md": "# docs\n"}) + result = _run_path_filter(work, tmp_path, "backend", SCRIPTS_DIR) + assert result.returncode == 0 + assert "circleci-agent step halt" in result.stdout + + +def test_path_filter_runs_backend_pr(tmp_path: Path) -> None: + work = _pr_repo(tmp_path, {"litellm/new.py": "y\n"}) + result = _run_path_filter(work, tmp_path, "backend", SCRIPTS_DIR) + assert result.returncode == 0 + assert "running job" in result.stdout + assert "halt" not in result.stdout + + +def test_path_filter_fails_open_when_not_a_pr(tmp_path: Path) -> None: + work = _pr_repo(tmp_path, {"README.md": "# docs\n"}) + result = _run_path_filter(work, tmp_path, "backend", SCRIPTS_DIR, is_pr=False) + assert result.returncode == 0 + assert "not a pull request" in result.stdout + assert "halt" not in result.stdout + + +def test_path_filter_fails_open_when_classifier_errors(tmp_path: Path) -> None: + """Regression: a broken classifier must run the job, never silently halt it.""" + broken_scripts = tmp_path / "broken_scripts" + broken_scripts.mkdir() + shutil.copy(PATH_FILTER, broken_scripts / "path_filter.sh") + (broken_scripts / "classify_changes.sh").write_text("#!/usr/bin/env bash\nexit 1\n") + (broken_scripts / "classify_changes.sh").chmod(0o755) + + work = _pr_repo(tmp_path, {"README.md": "# docs\n"}) + result = _run_path_filter(work, tmp_path, "backend", broken_scripts) + assert result.returncode == 0 + assert "classify_changes.sh failed" in result.stdout + assert "halt" not in result.stdout diff --git a/tests/test_litellm/test_claude_fable_5_config.py b/tests/test_litellm/test_claude_fable_5_config.py index d8d95fba0da..3a9ebf65bbb 100644 --- a/tests/test_litellm/test_claude_fable_5_config.py +++ b/tests/test_litellm/test_claude_fable_5_config.py @@ -204,7 +204,7 @@ def test_adaptive_thinking_detected_for_fable_5(local_model_cost_map, model): maps to ``thinking.type='adaptive'`` + ``output_config.effort``.""" from litellm.llms.anthropic.common_utils import AnthropicModelInfo - assert AnthropicModelInfo._is_adaptive_thinking_model(model) is True + assert AnthropicModelInfo._is_adaptive_thinking_model(model, "anthropic") is True @pytest.mark.parametrize( diff --git a/tests/test_litellm/test_claude_sonnet_5_config.py b/tests/test_litellm/test_claude_sonnet_5_config.py new file mode 100644 index 00000000000..506ffa16597 --- /dev/null +++ b/tests/test_litellm/test_claude_sonnet_5_config.py @@ -0,0 +1,187 @@ +""" +Validate Claude Sonnet 5 model configuration entries. + +Sonnet 5 ships with the gen-5 adaptive-thinking profile (adaptive thinking +always on, no extended thinking, ``effort`` defaults to ``high``), so it must +mirror the sampling-param and prefill restrictions that Fable 5 / Opus 4.8 carry +rather than the older Sonnet 4.6 behavior. The cost-map entries are also what +populate ``litellm.anthropic_models`` at import, which is what lets a bare +``claude-sonnet-5`` name resolve to the ``anthropic`` provider (and match an +``anthropic/*`` wildcard deployment). +""" + +import json +import os + +import pytest + +import litellm +from litellm.constants import BEDROCK_CONVERSE_MODELS +from litellm.litellm_core_utils.get_model_cost_map import GetModelCostMap + +REPO_ROOT = os.path.join(os.path.dirname(__file__), "../..") + +ALL_SONNET_5_VARIANTS = ( + "claude-sonnet-5", + "anthropic.claude-sonnet-5", + "global.anthropic.claude-sonnet-5", + "us.anthropic.claude-sonnet-5", + "eu.anthropic.claude-sonnet-5", + "au.anthropic.claude-sonnet-5", + "jp.anthropic.claude-sonnet-5", + "vertex_ai/claude-sonnet-5", + "vertex_ai/claude-sonnet-5@default", + "azure_ai/claude-sonnet-5", +) + + +def _load_root_cost_map() -> dict: + json_path = os.path.join(REPO_ROOT, "model_prices_and_context_window.json") + with open(json_path) as f: + return json.load(f) + + +@pytest.fixture +def local_model_cost_map(monkeypatch): + """Force the bundled backup cost map so assertions don't depend on the + network-fetched ``main`` copy (which lags this branch until merge).""" + original_model_cost = litellm.model_cost + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + litellm.model_cost = litellm.get_model_cost_map(url="") + litellm.get_model_info.cache_clear() + try: + yield + finally: + litellm.model_cost = original_model_cost + litellm.get_model_info.cache_clear() + + +def test_sonnet_5_pricing_and_capabilities(): + model_data = _load_root_cost_map() + + expected_providers = { + "claude-sonnet-5": "anthropic", + "anthropic.claude-sonnet-5": "bedrock_converse", + "vertex_ai/claude-sonnet-5": "vertex_ai-anthropic_models", + "azure_ai/claude-sonnet-5": "azure_ai", + } + + for model_name, provider in expected_providers.items(): + assert model_name in model_data, f"Missing model entry: {model_name}" + info = model_data[model_name] + + assert info["litellm_provider"] == provider + assert info["mode"] == "chat" + assert info["max_input_tokens"] == 1000000 + assert info["max_output_tokens"] == 128000 + assert info["max_tokens"] == 128000 + + # Introductory Sonnet 5 pricing through 2026-08-31: $2 / $10 per MTok, + # with the 1.25x cache-write and 0.1x cache-read multipliers. On + # 2026-09-01 flip these five fields back to the sticker rate, here and + # in both cost-map JSON files (all ten claude-sonnet-5 entries): + # input_cost_per_token: 3e-06 + # output_cost_per_token: 1.5e-05 + # cache_creation_input_token_cost: 3.75e-06 + # cache_creation_input_token_cost_above_1hr: 6e-06 + # cache_read_input_token_cost: 3e-07 + # Regional Bedrock profiles (us./eu./au./jp.) stay at 1.1x those values: + # 3.3e-06 / 1.65e-05 / 4.125e-06 / 6.6e-06 / 3.3e-07 (see + # test_sonnet_5_bedrock_regional_pricing below). + assert info["input_cost_per_token"] == 2e-06 + assert info["output_cost_per_token"] == 1e-05 + assert info["cache_creation_input_token_cost"] == 2.5e-06 + assert info["cache_creation_input_token_cost_above_1hr"] == 4e-06 + assert info["cache_read_input_token_cost"] == 2e-07 + + # gen-5 adaptive-thinking profile: effort-driven, no sampling params, no + # assistant prefill. + assert info["supports_adaptive_thinking"] is True + assert info["supports_reasoning"] is True + assert info["supports_sampling_params"] is False + assert info["supports_assistant_prefill"] is False + + assert info["supports_function_calling"] is True + assert info["supports_prompt_caching"] is True + assert info["supports_tool_choice"] is True + assert info["supports_vision"] is True + + +def test_sonnet_5_bedrock_regional_pricing(): + """Global/base endpoints use base pricing; the us./eu./au./jp. regional + cross-region inference profiles carry a 10% premium.""" + model_data = _load_root_cost_map() + + base_pricing = { + "input_cost_per_token": 2e-06, + "output_cost_per_token": 1e-05, + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 2e-07, + } + regional_pricing = { + "input_cost_per_token": 2.2e-06, + "output_cost_per_token": 1.1e-05, + "cache_creation_input_token_cost": 2.75e-06, + "cache_creation_input_token_cost_above_1hr": 4.4e-06, + "cache_read_input_token_cost": 2.2e-07, + } + + expected = { + "anthropic.claude-sonnet-5": base_pricing, + "global.anthropic.claude-sonnet-5": base_pricing, + "us.anthropic.claude-sonnet-5": regional_pricing, + "eu.anthropic.claude-sonnet-5": regional_pricing, + "au.anthropic.claude-sonnet-5": regional_pricing, + "jp.anthropic.claude-sonnet-5": regional_pricing, + } + + for model_name, pricing in expected.items(): + assert model_name in model_data, f"Missing model entry: {model_name}" + info = model_data[model_name] + assert info["litellm_provider"] == "bedrock_converse" + assert info["bedrock_output_config_effort_ceiling"] == "xhigh" + for key, value in pricing.items(): + assert info[key] == value, f"{model_name}.{key} = {info[key]}, want {value}" + + +def test_sonnet_5_present_in_bundled_backup(): + """The bundled backup is the runtime fallback (and what tests load with + ``LITELLM_LOCAL_MODEL_COST_MAP=True``); it must carry the same entries as the + root cost map, otherwise the model resolves on one path but not the other.""" + backup = GetModelCostMap.load_local_model_cost_map() + for model_name in ALL_SONNET_5_VARIANTS: + assert model_name in backup, f"Missing from backup cost map: {model_name}" + + +def test_sonnet_5_registered_for_bedrock_converse(): + assert "anthropic.claude-sonnet-5" in BEDROCK_CONVERSE_MODELS + + +def test_sonnet_5_provider_resolves_via_model_info(local_model_cost_map): + """Regression: ``claude-sonnet-5`` must resolve to provider ``anthropic``. + + Before the cost-map entry existed, the model was unknown to LiteLLM, so it + could not be tied to the ``anthropic`` provider and an ``anthropic/*`` + wildcard deployment would not match it.""" + info = litellm.get_model_info(model="claude-sonnet-5") + assert info["litellm_provider"] == "anthropic" + assert info["max_input_tokens"] == 1000000 + assert info["max_output_tokens"] == 128000 + + +@pytest.mark.parametrize( + "cost_map", + [_load_root_cost_map(), GetModelCostMap.load_local_model_cost_map()], + ids=["root", "bundled_backup"], +) +def test_sonnet_5_all_variants_carry_adaptive_thinking_flag(cost_map): + """Every Sonnet 5 entry must advertise ``supports_adaptive_thinking``. + + Adaptive-thinking detection is cost-map driven, so a single variant missing + the flag silently sends the legacy ``thinking.type='enabled'`` shape and the + provider 400s. This guards against a future variant being added without it.""" + variants = [k for k in cost_map if "claude-sonnet-5" in k] + assert variants, "no claude-sonnet-5 entries found in cost map" + missing = [k for k in variants if cost_map[k].get("supports_adaptive_thinking") is not True] + assert not missing, f"missing supports_adaptive_thinking: {missing}" diff --git a/tests/test_litellm/test_cost_calculator.py b/tests/test_litellm/test_cost_calculator.py index 81627cd3393..9636db4f4cd 100644 --- a/tests/test_litellm/test_cost_calculator.py +++ b/tests/test_litellm/test_cost_calculator.py @@ -347,6 +347,30 @@ def test_transcription_cost_falls_back_to_duration(): assert pytest.approx(cost, rel=1e-6) == expected_cost +def test_vertex_chirp_3_transcription_cost_from_duration(): + """Regression: the chirp_3 cost map entry shipped with output_cost_per_second 0.0, + and cost_per_second prefers output_cost_per_second whenever it is not None, so + every transcription priced to $0.00 instead of using input_cost_per_second.""" + from litellm import completion_cost + + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + + response = TranscriptionResponse(text="demo text") + response.duration = 18.0 + + cost = completion_cost( + completion_response=response, + model="vertex_ai/chirp_3", + custom_llm_provider="vertex_ai", + call_type="atranscription", + ) + + expected_cost = 18.0 * 0.00026667 + assert cost > 0 + assert pytest.approx(cost, rel=1e-6) == expected_cost + + def test_handle_realtime_stream_cost_calculation(): from litellm.cost_calculator import RealtimeAPITokenUsageProcessor @@ -423,6 +447,64 @@ def test_handle_realtime_stream_cost_calculation(): assert cost == 0.0 # No usage, no cost +def test_handle_realtime_stream_cost_calculation_stores_cost_breakdown(): + """Regression: realtime cost must populate logging_obj.cost_breakdown so the + spend logs / UI show input vs output cost (issue: cost_breakdown was None for + /v1/realtime even though a total spend was computed).""" + from datetime import datetime + + from litellm.litellm_core_utils.litellm_logging import Logging + + results: OpenAIRealtimeStreamList = [ + {"type": "session.created", "session": {"model": "gpt-4o-realtime-preview"}}, + { + "type": "response.done", + "response": { + "usage": { + "input_tokens": 100, + "output_tokens": 50, + "total_tokens": 150, + } + }, + }, + ] + combined_usage_object = RealtimeAPITokenUsageProcessor.collect_and_combine_usage_from_realtime_stream_results( + results=results, + ) + + logging_obj = Logging( + model="gpt-4o-realtime-preview", + messages=[], + stream=False, + call_type="_arealtime", + start_time=datetime.now(), + litellm_call_id="realtime-cost-breakdown-test", + function_id="realtime-cost-breakdown-test", + ) + + total_cost = handle_realtime_stream_cost_calculation( + results=results, + combined_usage_object=combined_usage_object, + custom_llm_provider="openai", + litellm_model_name="gpt-4o-realtime-preview", + litellm_logging_obj=logging_obj, + ) + + assert total_cost > 0 + assert logging_obj.cost_breakdown is not None + assert logging_obj.cost_breakdown["input_cost"] > 0 + assert logging_obj.cost_breakdown["output_cost"] > 0 + assert ( + abs( + logging_obj.cost_breakdown["input_cost"] + + logging_obj.cost_breakdown["output_cost"] + - total_cost + ) + < 1e-9 + ) + assert abs(logging_obj.cost_breakdown["total_cost"] - total_cost) < 1e-9 + + def test_realtime_stream_combines_text_and_audio_token_details(): """Realtime response.done usage with input_token_details / output_token_details.""" from litellm.cost_calculator import RealtimeAPITokenUsageProcessor @@ -579,6 +661,10 @@ def test_realtime_transcription_duration_cost(monkeypatch): ($0.017/min). The .completed events carry usage {type: duration, seconds: N}; cost must equal total_seconds * input_cost_per_second. """ + from datetime import datetime + + from litellm.litellm_core_utils.litellm_logging import Logging + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url="")) @@ -609,17 +695,41 @@ def test_realtime_transcription_duration_cost(monkeypatch): combined = RealtimeAPITokenUsageProcessor.collect_and_combine_usage_from_realtime_stream_results( results=results ) + logging_obj = Logging( + model="gpt-realtime-whisper", + messages=[], + stream=False, + call_type="_arealtime", + start_time=datetime.now(), + litellm_call_id="realtime-transcription-cost-breakdown-test", + function_id="realtime-transcription-cost-breakdown-test", + ) cost = handle_realtime_stream_cost_calculation( results=results, combined_usage_object=combined, custom_llm_provider="openai", litellm_model_name="gpt-realtime-whisper", + litellm_logging_obj=logging_obj, ) # 90 seconds at $0.017/minute. expected = 90.0 * (0.017 / 60) assert abs(cost - expected) < 1e-9 assert cost > 0 # guards against the duration branch being dropped + assert logging_obj.cost_breakdown is not None + assert abs(logging_obj.cost_breakdown["total_cost"] - cost) < 1e-9 + + # The transcription cost must be attributed in the breakdown, not just folded + # into total_cost, or input_cost + output_cost + additional_costs won't sum to total_cost. + additional_costs = logging_obj.cost_breakdown.get("additional_costs") + assert additional_costs is not None + assert abs(additional_costs["transcription_cost"] - expected) < 1e-9 + attributed_total = ( + logging_obj.cost_breakdown["input_cost"] + + logging_obj.cost_breakdown["output_cost"] + + additional_costs["transcription_cost"] + ) + assert abs(attributed_total - logging_obj.cost_breakdown["total_cost"]) < 1e-9 def test_realtime_transcription_duration_cost_resolves_model_from_litellm_name( @@ -890,6 +1000,110 @@ def test_per_request_custom_pricing_with_router(): assert "gpt-3.5-turbo" in selected +def test_tiered_pricing_only_deployment_selects_router_model_id(): + """A deployment priced solely via ``tiered_pricing`` (no flat + input/output cost) must resolve cost against its ``router_model_id`` + entry, which holds the tiered table, instead of the shared backend alias + that has custom pricing fields stripped. Regression for tier-only models + (e.g. dashscope/qwen3.7-plus) being billed as free. + """ + from litellm import Router + from litellm.cost_calculator import _select_model_name_for_cost_calc + + router = Router( + model_list=[ + { + "model_name": "qwen-3.7-plus", + "litellm_params": { + "model": "dashscope/qwen3.7-plus", + "api_key": "sk-fake", + }, + "model_info": { + "tiered_pricing": [ + { + "input_cost_per_token": 4e-07, + "output_cost_per_token": 1.6e-06, + "range": [0, 256000], + }, + ], + }, + }, + ] + ) + router_model_id = router.model_list[0]["model_info"]["id"] + + entry = litellm.model_cost[router_model_id] + assert entry.get("input_cost_per_token") is None + assert entry.get("tiered_pricing") is not None + # The stripped shared alias must not carry tiered pricing. + assert litellm.model_cost["dashscope/qwen3.7-plus"].get("tiered_pricing") is None + + selected = _select_model_name_for_cost_calc( + model="dashscope/qwen3.7-plus", + completion_response=None, + custom_pricing=True, + custom_llm_provider="dashscope", + router_model_id=router_model_id, + ) + assert selected is not None + assert router_model_id in selected + + +def test_tiered_pricing_only_deployment_completion_cost_is_nonzero(): + """End-to-end: a tier-only deployment must produce the tiered cost, not + $0. Mirrors the reported dashscope/qwen3.7-plus trace (12 prompt + 377 + completion tokens). + """ + from litellm import Router + from litellm.types.utils import Choices, Message + + router = Router( + model_list=[ + { + "model_name": "qwen-3.7-plus", + "litellm_params": { + "model": "dashscope/qwen3.7-plus", + "api_key": "sk-fake", + }, + "model_info": { + "tiered_pricing": [ + { + "input_cost_per_token": 4e-07, + "output_cost_per_token": 1.6e-06, + "range": [0, 256000], + }, + { + "input_cost_per_token": 1.2e-06, + "output_cost_per_token": 4.8e-06, + "range": [256000, 1000000], + }, + ], + }, + }, + ] + ) + router_model_id = router.model_list[0]["model_info"]["id"] + + response = ModelResponse( + model="dashscope/qwen3.7-plus", + choices=[Choices(index=0, message=Message(role="assistant", content="hi"))], + usage=Usage(prompt_tokens=12, completion_tokens=377, total_tokens=389), + ) + response._hidden_params = {"custom_llm_provider": "dashscope", "model_id": router_model_id} + + cost = completion_cost( + completion_response=response, + model="dashscope/qwen3.7-plus", + custom_llm_provider="dashscope", + custom_pricing=True, + router_model_id=router_model_id, + ) + + expected = 12 * 4e-07 + 377 * 1.6e-06 + assert cost == pytest.approx(expected) + assert cost > 0 + + def test_azure_realtime_cost_calculator(): os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" litellm.model_cost = litellm.get_model_cost_map(url="") @@ -3176,3 +3390,92 @@ def test_completion_cost_logs_reasoning_and_cache_breakdown(): assert logging_obj.cost_breakdown is not None assert logging_obj.cost_breakdown["reasoning_cost"] == pytest.approx(3114 * 2.5e-06) assert logging_obj.cost_breakdown["cache_read_cost"] == pytest.approx(100 * 3e-08) + + +def test_cost_per_token_per_second_pricing(monkeypatch): + """ + Models priced by duration (input/output_cost_per_second) with no per-token rates + must be billed as cost_per_second * response_time_ms / 1000 in cost_per_token. + """ + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url="")) + + model = "test-per-second-pricing-model" + litellm.register_model( + model_cost={ + model: { + "input_cost_per_second": 0.02, + "output_cost_per_second": 0.04, + "litellm_provider": "together_ai", + "mode": "chat", + } + } + ) + + prompt_cost, completion_cost_value = cost_per_token( + model=model, + custom_llm_provider="together_ai", + prompt_tokens=10, + completion_tokens=20, + response_time_ms=1500.0, + ) + + assert prompt_cost == pytest.approx(0.02 * 1.5) + assert completion_cost_value == pytest.approx(0.04 * 1.5) + + +def _batch_cache_usage() -> Usage: + return Usage( + prompt_tokens=11000, + completion_tokens=200, + total_tokens=11200, + prompt_tokens_details=PromptTokensDetailsWrapper( + cached_tokens=8000, + cache_creation_tokens=2000, + text_tokens=1000, + ), + cache_creation_input_tokens=2000, + cache_read_input_tokens=8000, + ) + + +def test_batch_cost_calculator_prices_cache_creation_tokens_at_cache_write_rate(): + """ + LIT-4008 regression: anthropic batch usage is dominated by cache tokens. + Cache creation tokens must be priced at cache_creation_input_token_cost / 2, + not folded into the base input rate, and must not also be billed as base + input tokens. + """ + from litellm.cost_calculator import batch_cost_calculator + + prompt_cost, completion_cost_value = batch_cost_calculator( + usage=_batch_cache_usage(), + model="claude-sonnet-4-5-20250929", + custom_llm_provider="anthropic", + model_info={ # type: ignore[arg-type] + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "cache_read_input_token_cost": 3e-7, + "cache_creation_input_token_cost": 3.75e-6, + }, + ) + + assert prompt_cost == pytest.approx((1000 * 3e-6 + 8000 * 3e-7 + 2000 * 3.75e-6) / 2) + assert completion_cost_value == pytest.approx(200 * 15e-6 / 2) + + +def test_batch_cost_calculator_cache_creation_falls_back_to_input_rate(): + from litellm.cost_calculator import batch_cost_calculator + + prompt_cost, _ = batch_cost_calculator( + usage=_batch_cache_usage(), + model="claude-sonnet-4-5-20250929", + custom_llm_provider="anthropic", + model_info={ # type: ignore[arg-type] + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "cache_read_input_token_cost": 3e-7, + }, + ) + + assert prompt_cost == pytest.approx((1000 * 3e-6 + 8000 * 3e-7 + 2000 * 3e-6) / 2) diff --git a/tests/test_litellm/test_gpt_5_6_model_metadata.py b/tests/test_litellm/test_gpt_5_6_model_metadata.py new file mode 100644 index 00000000000..5a7b621d521 --- /dev/null +++ b/tests/test_litellm/test_gpt_5_6_model_metadata.py @@ -0,0 +1,159 @@ +import json +from pathlib import Path + +import pytest + +from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + +GPT_5_6_MODELS = ("gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna") + +STANDARD_PRICING = { + "gpt-5.6": (5e-06, 3e-05, 5e-07, 6.25e-06), + "gpt-5.6-sol": (5e-06, 3e-05, 5e-07, 6.25e-06), + "gpt-5.6-terra": (2.5e-06, 1.5e-05, 2.5e-07, 3.125e-06), + "gpt-5.6-luna": (1e-06, 6e-06, 1e-07, 1.25e-06), +} + + +@pytest.mark.parametrize("model", GPT_5_6_MODELS) +def test_openai_gpt_5_6_model_info(model): + json_path = Path(__file__).parents[2] / "model_prices_and_context_window.json" + with open(json_path) as f: + model_cost = json.load(f) + + info = model_cost.get(model) + assert info is not None, f"{model} not found in model_prices_and_context_window.json" + + assert info["litellm_provider"] == "openai" + assert info["mode"] == "chat" + + input_cost, output_cost, cache_read_cost, cache_write_cost = STANDARD_PRICING[model] + assert info["input_cost_per_token"] == input_cost + assert info["output_cost_per_token"] == output_cost + assert info["cache_read_input_token_cost"] == cache_read_cost + assert info["cache_creation_input_token_cost"] == cache_write_cost + assert info["cache_creation_input_token_cost"] == pytest.approx(input_cost * 1.25) + + assert info["input_cost_per_token_above_272k_tokens"] == pytest.approx(input_cost * 2) + assert info["output_cost_per_token_above_272k_tokens"] == pytest.approx(output_cost * 1.5) + assert info["cache_read_input_token_cost_above_272k_tokens"] == pytest.approx(cache_read_cost * 2) + + assert info["max_input_tokens"] == 1050000 + assert info["max_output_tokens"] == 128000 + assert info["max_tokens"] == 128000 + + assert info["supports_function_calling"] is True + assert info["supports_prompt_caching"] is True + assert info["supports_reasoning"] is True + assert info["supports_response_schema"] is True + assert info["supports_tool_choice"] is True + assert info["supports_vision"] is True + assert info["supports_web_search"] is True + assert info["supports_none_reasoning_effort"] is True + assert info["supports_xhigh_reasoning_effort"] is True + assert info["supports_minimal_reasoning_effort"] is False + + assert info["supported_endpoints"] == ["/v1/chat/completions", "/v1/batch", "/v1/responses"] + assert info["supported_modalities"] == ["text", "image"] + assert info["supported_output_modalities"] == ["text"] + + routed_model, provider, _, _ = get_llm_provider(model=f"openai/{model}") + assert routed_model == model + assert provider == "openai" + + +AZURE_GLOBAL_MODELS = ( + "azure/gpt-5.6", + "azure/gpt-5.6-sol", + "azure/gpt-5.6-terra", + "azure/gpt-5.6-luna", +) + +AZURE_REGIONAL_MODELS = tuple( + f"azure/{region}/{tier}" + for region in ("us", "eu") + for tier in ("gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna") +) + + +def _tier_key(azure_model): + return azure_model.split("/")[-1] + + +def _load_main(): + json_path = Path(__file__).parents[2] / "model_prices_and_context_window.json" + with open(json_path) as f: + return json.load(f) + + +@pytest.mark.parametrize("model", AZURE_GLOBAL_MODELS) +def test_azure_gpt_5_6_global_model_info(model): + model_cost = _load_main() + info = model_cost.get(model) + assert info is not None, f"{model} not found in model_prices_and_context_window.json" + + assert info["litellm_provider"] == "azure" + assert info["mode"] == "chat" + + input_cost, output_cost, cache_read_cost, _ = STANDARD_PRICING[_tier_key(model)] + assert info["input_cost_per_token"] == input_cost + assert info["output_cost_per_token"] == output_cost + assert info["cache_read_input_token_cost"] == cache_read_cost + + assert info["input_cost_per_token_above_272k_tokens"] == pytest.approx(input_cost * 2) + assert info["output_cost_per_token_above_272k_tokens"] == pytest.approx(output_cost * 1.5) + assert info["input_cost_per_token_priority"] == pytest.approx(input_cost * 2) + assert info["output_cost_per_token_priority"] == pytest.approx(output_cost * 2) + assert info["input_cost_per_token_above_272k_tokens_priority"] == pytest.approx(input_cost * 4) + assert info["output_cost_per_token_above_272k_tokens_priority"] == pytest.approx(output_cost * 3) + + assert info["max_input_tokens"] == 1050000 + assert info["max_output_tokens"] == 128000 + assert info["supports_reasoning"] is True + + routed_model, provider, _, _ = get_llm_provider(model=model) + assert provider == "azure" + + +@pytest.mark.parametrize("model", AZURE_REGIONAL_MODELS) +def test_azure_gpt_5_6_regional_model_info(model): + model_cost = _load_main() + info = model_cost.get(model) + assert info is not None, f"{model} not found in model_prices_and_context_window.json" + + assert info["litellm_provider"] == "azure" + assert info["mode"] == "chat" + + input_cost, output_cost, cache_read_cost, _ = STANDARD_PRICING[_tier_key(model)] + + assert info["input_cost_per_token"] == pytest.approx(input_cost * 1.1) + assert info["output_cost_per_token"] == pytest.approx(output_cost * 1.1) + assert info["cache_read_input_token_cost"] == pytest.approx(cache_read_cost * 1.1) + assert info["input_cost_per_token_above_272k_tokens"] == pytest.approx(input_cost * 2.2) + assert info["output_cost_per_token_above_272k_tokens"] == pytest.approx(output_cost * 1.65) + assert info["input_cost_per_token_priority"] == pytest.approx(input_cost * 2.75) + assert info["output_cost_per_token_priority"] == pytest.approx(output_cost * 2.75) + + assert info["max_input_tokens"] == 1050000 + assert info["max_output_tokens"] == 128000 + assert info["supports_reasoning"] is True + + _, provider, _, _ = get_llm_provider(model=model) + assert provider == "azure" + + +def test_gpt_5_6_backup_matches_main(): + """Ensure the bundled model cost map stays in sync with the canonical file.""" + repo_root = Path(__file__).parents[2] + main_path = repo_root / "model_prices_and_context_window.json" + backup_path = repo_root / "litellm" / "model_prices_and_context_window_backup.json" + + with open(main_path) as f: + main_cost = json.load(f) + with open(backup_path) as f: + backup_cost = json.load(f) + + for model in GPT_5_6_MODELS + AZURE_GLOBAL_MODELS + AZURE_REGIONAL_MODELS: + assert backup_cost.get(model) == main_cost.get(model), ( + f"{model} differs between main and backup model cost maps" + ) diff --git a/tests/test_litellm/test_main.py b/tests/test_litellm/test_main.py index 113e1bc0df8..28cf4fa0744 100644 --- a/tests/test_litellm/test_main.py +++ b/tests/test_litellm/test_main.py @@ -638,6 +638,93 @@ def test_bedrock_llama(): ) +def _mocked_openai_chat_response(model: str) -> httpx.Response: + return httpx.Response( + status_code=200, + json={ + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1677652288, + "model": model, + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "Hello from mocked response!", + }, + "finish_reason": "stop", + } + ], + "usage": { + "prompt_tokens": 9, + "completion_tokens": 12, + "total_tokens": 21, + }, + }, + ) + + +def test_completion_forwards_verbosity_in_raw_request(respx_mock: respx.MockRouter): + """Regression test: completion() must forward the verbosity param to the provider request body.""" + from litellm.types.utils import CallTypes + from litellm.utils import return_raw_request + + model = "gpt-5.2" + messages = [{"role": "user", "content": "hi"}] + respx_mock.post("https://api.openai.com/v1/chat/completions").mock( + return_value=_mocked_openai_chat_response(model) + ) + + request = return_raw_request( + endpoint=CallTypes.completion, + kwargs={ + "model": model, + "messages": messages, + "verbosity": "high", + }, + ) + + assert request["raw_request_body"]["verbosity"] == "high" + assert request["raw_request_body"]["model"] == model + assert request["raw_request_body"]["messages"] == messages + + +@pytest.mark.asyncio +async def test_acompletion_forwards_verbosity_to_provider_request( + respx_mock: respx.MockRouter, monkeypatch +): + """Regression test: acompletion() must forward the verbosity param to the provider request body.""" + original_disable_aiohttp = litellm.disable_aiohttp_transport + try: + litellm.disable_aiohttp_transport = True + monkeypatch.setenv("DISABLE_AIOHTTP_TRANSPORT", "True") + litellm.in_memory_llm_clients_cache.flush_cache() + + model = "gpt-5.2" + messages = [{"role": "user", "content": "hi"}] + mock_route = respx_mock.post("https://api.openai.com/v1/chat/completions").mock( + return_value=_mocked_openai_chat_response(model) + ) + + response = await litellm.acompletion( + model=model, + messages=messages, + verbosity="low", + api_key="fake-openai-api-key", + ) + + assert response.choices[0].message.content == "Hello from mocked response!" + assert mock_route.called + request_body = json.loads(respx_mock.calls[0].request.read()) + assert request_body["verbosity"] == "low" + assert request_body["model"] == model + assert request_body["messages"] == messages + finally: + litellm.disable_aiohttp_transport = original_disable_aiohttp + litellm.in_memory_llm_clients_cache.flush_cache() + + def test_responses_api_bridge_check_strips_responses_prefix(): """Test that responses_api_bridge_check strips 'responses/' prefix and sets mode.""" from litellm.main import responses_api_bridge_check @@ -1962,3 +2049,35 @@ class TestCallTypesOCR: call_type = CallTypes("aocr") assert call_type == CallTypes.aocr + + +def test_stream_chunk_builder_text_completion_combines_text_and_usage(): + from litellm.main import stream_chunk_builder_text_completion + from litellm.types.utils import TextCompletionResponse + + chunks = [ + TextCompletionResponse( + id="cmpl-1", + object="text_completion", + created=1, + model="gpt-3.5-turbo-instruct", + choices=[{"text": "Hello", "index": 0, "logprobs": None, "finish_reason": None}], + ), + TextCompletionResponse( + id="cmpl-1", + object="text_completion", + created=1, + model="gpt-3.5-turbo-instruct", + choices=[{"text": " world", "index": 0, "logprobs": None, "finish_reason": "stop"}], + ), + ] + + response = stream_chunk_builder_text_completion( + chunks=chunks, messages=[{"role": "user", "content": "say hello"}] + ) + + assert response.choices[0].text == "Hello world" + assert response.choices[0].finish_reason == "stop" + assert response.usage.prompt_tokens > 0 + assert response.usage.completion_tokens > 0 + assert response.usage.total_tokens == response.usage.prompt_tokens + response.usage.completion_tokens diff --git a/tests/test_litellm/test_muse_spark_1_1_model_metadata.py b/tests/test_litellm/test_muse_spark_1_1_model_metadata.py new file mode 100644 index 00000000000..540b97884dc --- /dev/null +++ b/tests/test_litellm/test_muse_spark_1_1_model_metadata.py @@ -0,0 +1,63 @@ +import json +from pathlib import Path + +from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + +MUSE_SPARK_MODEL = "meta/muse-spark-1.1" + + +def test_muse_spark_1_1_model_info(): + json_path = Path(__file__).parents[2] / "model_prices_and_context_window.json" + with open(json_path) as f: + model_cost = json.load(f) + + info = model_cost.get(MUSE_SPARK_MODEL) + assert info is not None, f"{MUSE_SPARK_MODEL} not found in model_prices_and_context_window.json" + + assert info["litellm_provider"] == "meta" + assert info["mode"] == "chat" + + assert info["input_cost_per_token"] == 1.25e-06 + assert info["output_cost_per_token"] == 4.25e-06 + assert info["cache_read_input_token_cost"] == 1.5e-07 + + assert info["max_input_tokens"] == 1048576 + assert info["max_output_tokens"] == 131072 + assert info["max_tokens"] == 131072 + + assert info["supports_function_calling"] is True + assert info["supports_parallel_function_calling"] is True + assert info["supports_prompt_caching"] is True + assert info["supports_reasoning"] is True + assert info["supports_response_schema"] is True + assert info["supports_tool_choice"] is True + assert info["supports_vision"] is True + assert info["supports_pdf_input"] is True + assert info["supports_web_search"] is True + assert info["supports_minimal_reasoning_effort"] is True + assert info["supports_xhigh_reasoning_effort"] is True + + assert info["supported_endpoints"] == ["/v1/chat/completions", "/v1/responses", "/v1/messages"] + assert info["supported_modalities"] == ["text", "image", "video"] + assert info["supported_output_modalities"] == ["text"] + + routed_model, provider, _, api_base = get_llm_provider(model=MUSE_SPARK_MODEL, api_key="sk-test") + assert routed_model == "muse-spark-1.1" + assert provider == "meta" + assert api_base == "https://api.meta.ai/v1" + + +def test_muse_spark_1_1_backup_matches_main(): + """Ensure the bundled model cost map stays in sync with the canonical file.""" + repo_root = Path(__file__).parents[2] + main_path = repo_root / "model_prices_and_context_window.json" + backup_path = repo_root / "litellm" / "model_prices_and_context_window_backup.json" + + with open(main_path) as f: + main_cost = json.load(f) + with open(backup_path) as f: + backup_cost = json.load(f) + + assert backup_cost.get(MUSE_SPARK_MODEL) == main_cost.get(MUSE_SPARK_MODEL), ( + f"{MUSE_SPARK_MODEL} differs between main and backup model cost maps" + ) diff --git a/tests/test_litellm/test_prisma_generate_if_needed.py b/tests/test_litellm/test_prisma_generate_if_needed.py new file mode 100644 index 00000000000..39b9fcc4202 --- /dev/null +++ b/tests/test_litellm/test_prisma_generate_if_needed.py @@ -0,0 +1,35 @@ +import importlib.util +from pathlib import Path + +_MODULE_PATH = ( + Path(__file__).resolve().parents[2] / "scripts" / "prisma_generate_if_needed.py" +) +_spec = importlib.util.spec_from_file_location("prisma_generate_if_needed", _MODULE_PATH) +mod = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(mod) + + +def test_stamp_changes_with_schema_and_with_prisma_version(): + stamp = mod.stamp_value(b"model A {}", "0.11.0") + assert mod.stamp_value(b"model A {}", "0.11.0") == stamp + assert mod.stamp_value(b"model B {}", "0.11.0") != stamp + assert mod.stamp_value(b"model A {}", "0.12.0") != stamp + + +def test_skip_requires_a_matching_stamp(tmp_path): + stamp = tmp_path / "stamp" + expected = mod.stamp_value(b"schema", "0.11.0") + assert mod.should_skip(stamp, expected, client_generated=True) is False + stamp.write_text(expected) + assert mod.should_skip(stamp, expected, client_generated=True) is True + assert ( + mod.should_skip(stamp, mod.stamp_value(b"other", "0.11.0"), client_generated=True) + is False + ) + + +def test_skip_requires_a_generated_client_even_with_a_matching_stamp(tmp_path): + stamp = tmp_path / "stamp" + expected = mod.stamp_value(b"schema", "0.11.0") + stamp.write_text(expected) + assert mod.should_skip(stamp, expected, client_generated=False) is False diff --git a/tests/test_litellm/test_redis.py b/tests/test_litellm/test_redis.py index a89e30a0e06..4b9f13c340b 100644 --- a/tests/test_litellm/test_redis.py +++ b/tests/test_litellm/test_redis.py @@ -12,6 +12,7 @@ from litellm._redis import ( get_redis_connection_pool, get_redis_url_from_environment, ) +from litellm.constants import REDIS_CLUSTER_HEALTH_CHECK_INTERVAL from litellm._redis_credential_provider import ( GCPIAMCredentialProvider, _token_cache, @@ -171,6 +172,46 @@ def test_socket_timeouts_in_cluster_kwargs(): assert "socket_connect_timeout" in kwargs +def test_reconnect_kwargs_in_cluster_kwargs(): + """Health check and keepalive must survive the cluster kwarg allow-list so + operators can tune Redis cluster reconnection behavior via config.""" + kwargs = _get_redis_cluster_kwargs() + assert "health_check_interval" in kwargs + assert "socket_keepalive" in kwargs + + +@patch("litellm._redis.async_redis.RedisCluster") +def test_async_cluster_sets_reconnect_defaults(mock_cluster_cls): + """ + The async RedisCluster client must be built with a periodic health check and + TCP keepalive so a connection silently dropped by a cluster restart (e.g. + ElastiCache Serverless maintenance) is revalidated and reconnected before + reuse instead of stalling in re-initialization. Regression for LIT-4083. + """ + get_redis_async_client(startup_nodes=[{"host": "cluster-node", "port": 6379}]) + + mock_cluster_cls.assert_called_once() + call_kwargs = mock_cluster_cls.call_args[1] + assert call_kwargs["health_check_interval"] == REDIS_CLUSTER_HEALTH_CHECK_INTERVAL + assert call_kwargs["health_check_interval"] > 0 + assert call_kwargs["socket_keepalive"] is True + + +@patch("litellm._redis.async_redis.RedisCluster") +def test_async_cluster_reconnect_defaults_are_overridable(mock_cluster_cls): + """An explicit health_check_interval / socket_keepalive from config must win + over the built-in reconnect defaults.""" + get_redis_async_client( + startup_nodes=[{"host": "cluster-node", "port": 6379}], + health_check_interval=7, + socket_keepalive=False, + ) + + call_kwargs = mock_cluster_cls.call_args[1] + assert call_kwargs["health_check_interval"] == 7 + assert call_kwargs["socket_keepalive"] is False + + def test_get_redis_async_client_with_connection_pool(): """Test that connection_pool parameter is properly passed to Redis client""" # Create a mock connection pool @@ -588,3 +629,98 @@ def test_sync_client_url_used_when_no_cluster(mock_from_url, monkeypatch): get_redis_client() mock_from_url.assert_called_once() + + +@patch("litellm._redis.redis.Redis.from_url") +def test_explicit_host_outranks_environment_redis_url(mock_from_url, monkeypatch): + """ + An explicitly configured host must win over REDIS_URL in the environment. + + Otherwise the url branch strips the caller's host/port and the client + silently connects to whatever REDIS_URL names, so an explicit config block + (or a connection test typed into the admin UI) targets the wrong server. + """ + monkeypatch.setenv("REDIS_URL", "redis://env-host:6379") + monkeypatch.delenv("REDIS_CLUSTER_NODES", raising=False) + + client = get_redis_client(host="explicit-host", port=6380) + + mock_from_url.assert_not_called() + assert client.connection_pool.connection_kwargs["host"] == "explicit-host" + assert client.connection_pool.connection_kwargs["port"] == 6380 + + +@patch("litellm._redis.redis.Redis.from_url") +def test_explicit_url_still_wins_over_environment_host(mock_from_url, monkeypatch): + """An explicit url argument keeps taking the from_url path.""" + monkeypatch.setenv("REDIS_HOST", "env-host") + monkeypatch.setenv("REDIS_PORT", "6379") + monkeypatch.delenv("REDIS_CLUSTER_NODES", raising=False) + + get_redis_client(url="redis://explicit-host:6380") + + mock_from_url.assert_called_once() + assert mock_from_url.call_args.kwargs["url"] == "redis://explicit-host:6380" + + +@patch("litellm._redis.redis.Redis.from_url") +def test_environment_redis_url_used_when_caller_names_no_target(mock_from_url, monkeypatch): + """With no caller-supplied connection target, REDIS_URL still drives the client.""" + monkeypatch.setenv("REDIS_URL", "redis://env-host:6379") + monkeypatch.delenv("REDIS_CLUSTER_NODES", raising=False) + + get_redis_client() + + mock_from_url.assert_called_once() + + +@pytest.mark.parametrize("falsy_ssl", [False, None, 0, ""]) +def test_connection_pool_falsy_ssl_uses_plain_connection(falsy_ssl, monkeypatch): + """ + ssl=False must produce a plain (non-TLS) connection pool. + + The admin UI's coordination Redis form always sends ssl explicitly, so a + presence check here turns ssl=False into an SSLConnection; the TLS + handshake against a plaintext Redis then hangs until the ping timeout and + every connection test from the UI fails. + """ + monkeypatch.delenv("REDIS_URL", raising=False) + monkeypatch.delenv("REDIS_SSL", raising=False) + monkeypatch.delenv("REDIS_CLUSTER_NODES", raising=False) + + with patch("litellm._redis.async_redis.BlockingConnectionPool") as mock_pool: + get_redis_connection_pool(host="plain-redis.example.com", port=6379, ssl=falsy_ssl) + + call_kwargs = mock_pool.call_args.kwargs + assert call_kwargs.get("connection_class") is not async_redis.SSLConnection, ( + f"ssl={falsy_ssl!r} must not select SSLConnection" + ) + assert "ssl" not in call_kwargs, "ssl must never leak into BlockingConnectionPool kwargs" + + +def test_connection_pool_ssl_true_uses_ssl_connection(monkeypatch): + """ssl=True must still opt in to a TLS connection pool.""" + monkeypatch.delenv("REDIS_URL", raising=False) + monkeypatch.delenv("REDIS_SSL", raising=False) + monkeypatch.delenv("REDIS_CLUSTER_NODES", raising=False) + + with patch("litellm._redis.async_redis.BlockingConnectionPool") as mock_pool: + get_redis_connection_pool(host="tls-redis.example.com", port=6380, ssl=True) + + call_kwargs = mock_pool.call_args.kwargs + assert call_kwargs.get("connection_class") is async_redis.SSLConnection + assert "ssl" not in call_kwargs, "ssl must be consumed, not forwarded to the pool" + + +def test_connection_pool_without_ssl_kwarg_uses_plain_connection(monkeypatch): + """Omitting ssl entirely must keep the historical plain-connection default.""" + monkeypatch.delenv("REDIS_URL", raising=False) + monkeypatch.delenv("REDIS_SSL", raising=False) + monkeypatch.delenv("REDIS_CLUSTER_NODES", raising=False) + + with patch("litellm._redis.async_redis.BlockingConnectionPool") as mock_pool: + get_redis_connection_pool(host="plain-redis.example.com", port=6379) + + call_kwargs = mock_pool.call_args.kwargs + assert call_kwargs.get("connection_class") is not async_redis.SSLConnection + assert "ssl" not in call_kwargs diff --git a/tests/test_litellm/test_register_model_custom_pricing.py b/tests/test_litellm/test_register_model_custom_pricing.py index e384d3e1161..ba82bfaadc6 100644 --- a/tests/test_litellm/test_register_model_custom_pricing.py +++ b/tests/test_litellm/test_register_model_custom_pricing.py @@ -9,15 +9,32 @@ mode, and supports_prompt_caching were dropped, causing incorrect cost calculations for DB-sourced models with prompt caching pricing. """ +import copy import os import sys +import pytest + sys.path.insert( 0, os.path.abspath("../..") ) # Adds the parent directory to the system path import litellm from litellm.main import _build_custom_pricing_entry +from litellm.utils import _invalidate_model_cost_lowercase_map + + +def _snapshot_model_cost_entries(keys): + return {key: copy.deepcopy(litellm.model_cost.get(key)) for key in keys} + + +def _restore_model_cost_entries(original_entries): + for key, value in original_entries.items(): + if value is None: + litellm.model_cost.pop(key, None) + else: + litellm.model_cost[key] = value + _invalidate_model_cost_lowercase_map() def test_build_custom_pricing_entry_includes_all_kwargs_fields(): @@ -303,8 +320,9 @@ def test_register_model_strips_none_litellm_provider_from_get_model_info(monkeyp def test_register_model_inherits_builtin_cache_pricing_for_unmapped_key(): """Registering a custom override under a key shape that - ``get_model_info`` cannot resolve (e.g. a double provider prefix like - ``bedrock/bedrock/us.anthropic.claude-sonnet-4-6``) must still inherit + ``get_model_info`` cannot resolve (e.g. a triple provider prefix like + ``bedrock/bedrock/bedrock/us.anthropic.claude-sonnet-4-6``; a double + prefix now resolves like a routing prefix) must still inherit the built-in cache pricing for the underlying model. Before the fix ``register_model`` fell back to an empty ``existing_model`` @@ -324,7 +342,7 @@ def test_register_model_inherits_builtin_cache_pricing_for_unmapped_key(): litellm.model_cost = litellm.get_model_cost_map(url="") builtin_key = "us.anthropic.claude-sonnet-4-6" - registered_key = f"bedrock/bedrock/{builtin_key}" + registered_key = f"bedrock/bedrock/bedrock/{builtin_key}" builtin = litellm.model_cost[builtin_key] assert builtin["cache_creation_input_token_cost"] > 0 @@ -471,3 +489,166 @@ def test_register_model_router_add_deployment_custom_pricing_applies(): litellm.model_cost.pop(model_key, None) litellm.model_cost.pop(deployment_model, None) del router + + +def test_embedding_router_zero_pricing_does_not_clobber_builtin_pricing(): + """LIT-3991: a router-originated embedding request that carries explicit + zero custom pricing (e.g. resolved through an ``openai/*`` wildcard + deployment with ``input_cost_per_token: 0``) must not overwrite the shared + ``openai/text-embedding-3-small`` entry in ``litellm.model_cost``. Before + the fix, one call through the wildcard poisoned the shared key and every + sibling deployment relying on built-in pricing logged $0 until restart. + """ + shared_key = "openai/text-embedding-3-small" + deployment_id = "lit3991-wildcard-embed-zero" + snapshot = _snapshot_model_cost_entries( + [shared_key, "text-embedding-3-small", deployment_id] + ) + builtin_input_cost = litellm.get_model_info(model=shared_key)[ + "input_cost_per_token" + ] + assert builtin_input_cost > 0 + + try: + litellm.embedding( + model=shared_key, + input=["hello"], + api_key="fake-key", + input_cost_per_token=0.0, + output_cost_per_token=0.0, + model_info={"id": deployment_id}, + metadata={"model_info": {"id": deployment_id}}, + mock_response=[0.1, 0.2], + ) + + assert ( + litellm.get_model_info(model=shared_key)["input_cost_per_token"] + == builtin_input_cost + ), "wildcard deployment's zero pricing leaked into the shared model_cost key" + assert litellm.model_cost[deployment_id]["input_cost_per_token"] == 0.0 + assert litellm.model_cost[deployment_id]["output_cost_per_token"] == 0.0 + + sibling_response = litellm.embedding( + model=shared_key, + input=["hello"], + api_key="fake-key", + mock_response=[0.1, 0.2], + ) + sibling_cost = litellm.completion_cost( + completion_response=sibling_response, call_type="embedding" + ) + assert sibling_cost == pytest.approx(10 * builtin_input_cost) + finally: + _restore_model_cost_entries(snapshot) + + +def test_embedding_router_custom_pricing_costs_request_via_deployment_id(): + """The request that carries custom pricing must still be costed with that + pricing (via its deployment id entry), while the shared backend key keeps + the built-in rate for siblings. + """ + shared_key = "openai/text-embedding-3-small" + deployment_id = "lit3991-wildcard-embed-custom" + override_input_cost = 5e-05 + snapshot = _snapshot_model_cost_entries( + [shared_key, "text-embedding-3-small", deployment_id] + ) + builtin_input_cost = litellm.get_model_info(model=shared_key)[ + "input_cost_per_token" + ] + assert builtin_input_cost != override_input_cost + + try: + response = litellm.embedding( + model=shared_key, + input=["hello"], + api_key="fake-key", + input_cost_per_token=override_input_cost, + output_cost_per_token=override_input_cost * 2, + model_info={"id": deployment_id}, + metadata={"model_info": {"id": deployment_id}}, + mock_response=[0.1, 0.2], + ) + + request_cost = litellm.completion_cost( + completion_response=response, + model=shared_key, + custom_llm_provider="openai", + call_type="embedding", + custom_pricing=True, + router_model_id=deployment_id, + ) + assert request_cost == pytest.approx(10 * override_input_cost) + assert ( + litellm.get_model_info(model=shared_key)["input_cost_per_token"] + == builtin_input_cost + ) + finally: + _restore_model_cost_entries(snapshot) + + +def test_completion_router_zero_pricing_does_not_clobber_builtin_pricing(): + """Same isolation as the embedding path, exercised through completion().""" + shared_key = "openai/gpt-4o-mini" + deployment_id = "lit3991-wildcard-chat-zero" + snapshot = _snapshot_model_cost_entries( + [shared_key, "gpt-4o-mini", deployment_id] + ) + builtin_input_cost = litellm.get_model_info(model=shared_key)[ + "input_cost_per_token" + ] + assert builtin_input_cost > 0 + + try: + litellm.completion( + model=shared_key, + messages=[{"role": "user", "content": "hello"}], + api_key="fake-key", + input_cost_per_token=0.0, + output_cost_per_token=0.0, + model_info={"id": deployment_id}, + metadata={"model_info": {"id": deployment_id}}, + mock_response="hello back", + ) + + assert ( + litellm.get_model_info(model=shared_key)["input_cost_per_token"] + == builtin_input_cost + ), "wildcard deployment's zero pricing leaked into the shared model_cost key" + assert litellm.model_cost[deployment_id]["input_cost_per_token"] == 0.0 + finally: + _restore_model_cost_entries(snapshot) + + +def test_embedding_direct_sdk_custom_pricing_still_registers_shared_key(): + """Direct SDK calls (no router deployment id in metadata) keep the legacy + behavior: custom pricing is registered under ``{provider}/{model}`` and the + request is costed with it. + """ + model_key = "openai/lit3991-direct-sdk-embed-model" + override_input_cost = 3e-05 + try: + response = litellm.embedding( + model=model_key, + input=["hello"], + api_key="fake-key", + input_cost_per_token=override_input_cost, + output_cost_per_token=override_input_cost * 2, + mock_response=[0.1, 0.2], + ) + + assert ( + litellm.model_cost[model_key]["input_cost_per_token"] + == override_input_cost + ) + cost = litellm.completion_cost( + completion_response=response, + model=model_key, + custom_llm_provider="openai", + call_type="embedding", + custom_pricing=True, + ) + assert cost == pytest.approx(10 * override_input_cost) + finally: + litellm.model_cost.pop(model_key, None) + _invalidate_model_cost_lowercase_map() diff --git a/tests/test_litellm/test_responses_api_bridge_non_stream.py b/tests/test_litellm/test_responses_api_bridge_non_stream.py index 8905293d6b6..25a3bc2dbba 100644 --- a/tests/test_litellm/test_responses_api_bridge_non_stream.py +++ b/tests/test_litellm/test_responses_api_bridge_non_stream.py @@ -269,29 +269,30 @@ def test_transform_usage_with_zero_values(): """ Test transformation when token details are explicitly set to 0. - This ensures 0 values are preserved and not treated as None. + cached_tokens=0 is preserved (cache was available; nothing was cached). + reasoning_tokens=0 is preserved the same way: an explicit provider-reported + zero passes through, while an absent value (None) is omitted. """ completion_response = create_mock_completion_response( model="gpt-4", prompt_tokens=100, completion_tokens=50, total_tokens=150, - cached_tokens=0, # Explicitly 0 - reasoning_tokens=0, # Explicitly 0 + cached_tokens=0, # Explicitly 0 — preserved + reasoning_tokens=0, # Explicitly 0 — preserved ) responses_usage = LiteLLMCompletionResponsesConfig._transform_chat_completion_usage_to_responses_usage( completion_response ) - # Should preserve 0 values assert responses_usage.input_tokens_details is not None assert responses_usage.input_tokens_details.cached_tokens == 0 assert responses_usage.output_tokens_details is not None assert responses_usage.output_tokens_details.reasoning_tokens == 0 - print("✓ Transformation preserves explicit 0 values") + print("✓ Transformation preserves explicit reasoning_tokens=0 and omits absent values") def test_input_tokens_details_requires_cached_tokens(): @@ -315,25 +316,23 @@ def test_input_tokens_details_requires_cached_tokens(): print("✓ InputTokensDetails correctly defaults cached_tokens to 0") -def test_output_tokens_details_requires_reasoning_tokens(): +def test_output_tokens_details_reasoning_tokens(): """ - Test that OutputTokensDetails has reasoning_tokens as an int with default value 0. + Test OutputTokensDetails.reasoning_tokens field semantics. - This ensures backward compatibility while making the field non-optional. + reasoning_tokens is Optional[int] = None: present only when reasoning actually occurred. """ - # Should work with reasoning_tokens=0 - details1 = OutputTokensDetails(reasoning_tokens=0) - assert details1.reasoning_tokens == 0 + details_explicit_zero = OutputTokensDetails(reasoning_tokens=0) + assert details_explicit_zero.reasoning_tokens == 0 - # Should work with reasoning_tokens=100 - details2 = OutputTokensDetails(reasoning_tokens=100) - assert details2.reasoning_tokens == 100 + details_positive = OutputTokensDetails(reasoning_tokens=100) + assert details_positive.reasoning_tokens == 100 - # Should work without reasoning_tokens (defaults to 0) - details3 = OutputTokensDetails() - assert details3.reasoning_tokens == 0 + # Default is None — absence means reasoning did not occur (or was not tracked) + details_default = OutputTokensDetails() + assert details_default.reasoning_tokens is None - print("✓ OutputTokensDetails correctly defaults reasoning_tokens to 0") + print("✓ OutputTokensDetails.reasoning_tokens defaults to None") def test_all_providers_transformation_scenarios(): @@ -419,7 +418,7 @@ if __name__ == "__main__": test_transform_usage_with_both_token_details() test_transform_usage_with_zero_values() test_input_tokens_details_requires_cached_tokens() - test_output_tokens_details_requires_reasoning_tokens() + test_output_tokens_details_reasoning_tokens() test_all_providers_transformation_scenarios() print("\n" + "=" * 60) diff --git a/tests/test_litellm/test_responses_id_security.py b/tests/test_litellm/test_responses_id_security.py index 0f75e9cab3a..17487030cc1 100644 --- a/tests/test_litellm/test_responses_id_security.py +++ b/tests/test_litellm/test_responses_id_security.py @@ -519,6 +519,62 @@ class TestAsyncPreCallHook: assert "team" in exc_info.value.detail.lower() + @pytest.mark.asyncio + async def test_async_pre_call_hook_alist_input_items_decrypts_response_id( + self, responses_id_security, mock_user_api_key_dict, mock_cache + ): + data = {"response_id": "resp_encrypted_789"} + + with patch.object( + responses_id_security, "_is_encrypted_response_id", return_value=True + ): + with patch.object( + responses_id_security, + "_decrypt_response_id", + return_value=("resp_original_789", "test-user-123", "test-team-123"), + ): + result = await responses_id_security.async_pre_call_hook( + user_api_key_dict=mock_user_api_key_dict, + cache=mock_cache, + data=data, + call_type="alist_input_items", + ) + + assert result is not None + assert result["response_id"] == "resp_original_789" + + @pytest.mark.asyncio + async def test_async_pre_call_hook_alist_input_items_team_security( + self, responses_id_security, mock_cache + ): + mock_auth_team_a = MagicMock() + mock_auth_team_a.user_id = None + mock_auth_team_a.team_id = "team-a" + mock_auth_team_a.user_role = None + + data = {"response_id": "resp_encrypted_team_b"} + + with patch.object( + responses_id_security, "_is_encrypted_response_id", return_value=True + ): + with patch.object( + responses_id_security, + "_decrypt_response_id", + return_value=("resp_original_team_b", None, "team-b"), + ): + with patch("litellm.proxy.proxy_server.general_settings", {}): + with pytest.raises(HTTPException) as exc_info: + await responses_id_security.async_pre_call_hook( + user_api_key_dict=mock_auth_team_a, + cache=mock_cache, + data=data, + call_type="alist_input_items", + ) + + assert exc_info.value.status_code == 403 + assert "team" in exc_info.value.detail.lower() + + class TestAsyncPostCallSuccessHook: """Test async_post_call_success_hook function""" diff --git a/tests/test_litellm/test_router/test_io_token_rate_limits.py b/tests/test_litellm/test_router/test_io_token_rate_limits.py new file mode 100644 index 00000000000..939e3189596 --- /dev/null +++ b/tests/test_litellm/test_router/test_io_token_rate_limits.py @@ -0,0 +1,979 @@ +""" +Tests for separate ITPM/OTPM deployment rate limits (enforce_model_rate_limits). +""" + +import asyncio + +import pytest + +import litellm +from litellm import Router +from litellm.caching.dual_cache import DualCache +from litellm.router_utils.pre_call_checks.io_token_rate_limit_check import ( + ITPM_CACHE_KEY, + ITPM_RESERVED_KEY, + OTPM_CACHE_KEY, + OTPM_RESERVED_KEY, + _reservation_value, + _resolve_max_tokens, + async_io_token_pre_call_check, + async_io_token_reconcile_success, + build_io_token_rate_limit_headers, + deployment_has_io_token_limits, + get_io_token_rate_limit_request_kwargs, + io_token_reconcile_success, + io_token_refund_failure, + refund_stale_reservation_before_retry, + set_io_token_rate_limit_request_kwargs, +) +from litellm.router_utils.pre_call_checks.model_rate_limit_check import ( + ModelRateLimitingCheck, +) +from litellm.types.utils import ModelResponse, Usage + + +class TestIOTokenRateLimitHelpers: + def test_deployment_has_io_token_limits(self): + assert deployment_has_io_token_limits({"litellm_params": {"itpm": 100, "otpm": 50}}) + assert not deployment_has_io_token_limits({"litellm_params": {"model": "x"}}) + + def test_reservation_value_minimal_when_estimate_fails(self): + # A failed/empty estimate (0) must reserve a minimal slot, not the + # entire limit - otherwise one request whose estimate failed fills + # the whole bucket and blocks every concurrent request until it + # completes and reconciles. + assert _reservation_value(0, 100) == 1 + assert _reservation_value(0, 1) == 1 + # A real non-zero estimate is reserved as-is. + assert _reservation_value(42, 100) == 42 + + def test_resolve_max_tokens_respects_explicit_zero(self): + deployment = {"litellm_params": {"model": "openai/gpt-4o-mini"}} + # An explicit max_tokens=0 is honored, not replaced by the model default. + assert _resolve_max_tokens({"max_tokens": 0}, deployment) == 0 + # max_completion_tokens is the fallback only when max_tokens is absent. + assert _resolve_max_tokens({"max_completion_tokens": 12}, deployment) == 12 + assert _resolve_max_tokens({"max_output_tokens": 9}, deployment) == 9 + + def test_build_io_token_rate_limit_headers(self): + headers = build_io_token_rate_limit_headers( + itpm_limit=200, + otpm_limit=40, + current_itpm=15, + current_otpm=4, + ) + assert headers["x-ratelimit-limit-input-tokens"] == 200 + assert headers["x-ratelimit-remaining-input-tokens"] == 185 + assert headers["x-ratelimit-limit-output-tokens"] == 40 + assert headers["x-ratelimit-remaining-output-tokens"] == 36 + + +class TestModelRateLimitingCheckIOTokens: + @pytest.mark.asyncio + async def test_itpm_reservation_and_reconcile(self): + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "itpm": 100, + "otpm": 50, + }, + "model_info": {"id": "io-test-id"}, + "model_name": "opus", + } + + request_kwargs = { + "messages": [{"role": "user", "content": "hello"}], + "max_tokens": 10, + "metadata": {}, + } + set_io_token_rate_limit_request_kwargs(request_kwargs) + await check.async_pre_call_check(deployment) + + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:io-test-id:bedrock_mantle/anthropic.claude-opus-4-7:itpm:{minute}" + otpm_key = f"global_router:io-test-id:bedrock_mantle/anthropic.claude-opus-4-7:otpm:{minute}" + + kwargs = { + "standard_logging_object": { + "model_id": "io-test-id", + "hidden_params": {"litellm_model_name": "bedrock_mantle/anthropic.claude-opus-4-7"}, + "metadata": dict(request_kwargs["metadata"]), + }, + "metadata": request_kwargs["metadata"], + } + response = ModelResponse( + choices=[ + { + "message": {"role": "assistant", "content": "hi"}, + "index": 0, + "finish_reason": "stop", + } + ], + usage=Usage(prompt_tokens=5, completion_tokens=3, total_tokens=8), + ) + await check.async_log_success_event(kwargs, response, None, None) + + current_itpm = await dual_cache.async_get_cache(key=itpm_key) + current_otpm = await dual_cache.async_get_cache(key=otpm_key) + # ITPM tracks input tokens only (billable prompt tokens), not output. + assert current_itpm == 5 + assert current_otpm == 3 + + @pytest.mark.asyncio + async def test_itpm_limit_raises_429(self): + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "itpm": 5, + }, + "model_info": {"id": "io-limit-id"}, + "model_name": "opus", + } + + # ITPM enforces input tokens only; the prompt alone must exceed the limit, + # a large max_tokens must not contribute to the ITPM reservation. + set_io_token_rate_limit_request_kwargs( + { + "messages": [ + { + "role": "user", + "content": "hello world this is a longer prompt that exceeds the tiny itpm limit", + } + ], + "max_tokens": 10, + "metadata": {}, + } + ) + + with pytest.raises(litellm.RateLimitError) as exc_info: + await check.async_pre_call_check(deployment) + + assert "ITPM limit=5" in str(exc_info.value) + + @pytest.mark.asyncio + async def test_otpm_atomic_reservation_no_overshoot_under_concurrency(self): + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + otpm_limit = 10 + max_tokens = 4 + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "otpm": otpm_limit, + }, + "model_info": {"id": "io-otpm-race-id"}, + "model_name": "opus", + } + + set_io_token_rate_limit_request_kwargs( + { + "messages": [{"role": "user", "content": "hi"}], + "max_tokens": max_tokens, + "metadata": {}, + } + ) + + async def _attempt(): + try: + await check.async_pre_call_check(deployment) + return True + except litellm.RateLimitError: + return False + + results = await asyncio.gather(*[_attempt() for _ in range(8)]) + successes = sum(1 for r in results if r) + + minute = get_utc_datetime().strftime("%H-%M") + otpm_key = f"global_router:io-otpm-race-id:bedrock_mantle/anthropic.claude-opus-4-7:otpm:{minute}" + current_otpm = await dual_cache.async_get_cache(key=otpm_key) + + # Atomic reservation must never let concurrent requests overshoot the limit. + assert current_otpm is not None + assert current_otpm <= otpm_limit + assert successes == otpm_limit // max_tokens + assert current_otpm == successes * max_tokens + + @pytest.mark.asyncio + async def test_itpm_estimate_failure_reserves_minimal_not_full_limit(self): + """ + When input-token estimation yields 0 (no messages/prompt/input field, + unsupported model, tokenizer error), the reservation must be a + minimal 1 token, not the entire itpm limit. Otherwise the first + request whose estimate fails fills the whole bucket and every + concurrent request is rejected until it completes - effectively + serializing traffic to the deployment. + """ + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + itpm_limit = 5 + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "itpm": itpm_limit, + }, + "model_info": {"id": "io-itpm-estimate-fail-id"}, + "model_name": "opus", + } + + # No messages/prompt/input field -> _estimate_input_tokens returns 0. + set_io_token_rate_limit_request_kwargs( + { + "max_tokens": 5, + "metadata": {}, + } + ) + + async def _attempt(): + try: + await check.async_pre_call_check(deployment) + return True + except litellm.RateLimitError: + return False + + results = await asyncio.gather(*[_attempt() for _ in range(8)]) + successes = sum(1 for r in results if r) + + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:io-itpm-estimate-fail-id:bedrock_mantle/anthropic.claude-opus-4-7:itpm:{minute}" + current_itpm = await dual_cache.async_get_cache(key=itpm_key) + + # A minimal 1-token reservation per request lets itpm_limit concurrent + # requests through, instead of a single request starving the rest. + assert current_itpm is not None + assert current_itpm <= itpm_limit + assert successes == itpm_limit + + @pytest.mark.asyncio + async def test_reservation_read_prefers_top_level_metadata_over_litellm_params(self): + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:io-lp-id:bedrock_mantle/test:itpm:{minute}" + await dual_cache.async_increment_cache(key=itpm_key, value=20, ttl=60) + + # Production kwargs commonly carry litellm_params.metadata; the stashed + # reservation lives in the top-level metadata and must still be found. + kwargs = { + "standard_logging_object": { + "model_id": "io-lp-id", + "hidden_params": {"litellm_model_name": "bedrock_mantle/test"}, + "metadata": {}, + }, + "metadata": {ITPM_RESERVED_KEY: 20, ITPM_CACHE_KEY: itpm_key}, + "litellm_params": {"metadata": {"user_api_key_hash": "abc123"}}, + } + await check.async_log_failure_event(kwargs, None, None, None) + + current = await dual_cache.async_get_cache(key=itpm_key) + assert current == 0 + + @pytest.mark.asyncio + async def test_reconcile_tracks_actual_usage_when_estimate_zero(self): + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "itpm": 100, + }, + "model_info": {"id": "io-zero-est-id"}, + "model_name": "opus", + } + + request_kwargs = {"max_tokens": 5, "metadata": {}} + set_io_token_rate_limit_request_kwargs(request_kwargs) + await check.async_pre_call_check(deployment) + + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:io-zero-est-id:bedrock_mantle/anthropic.claude-opus-4-7:itpm:{minute}" + # A failed/zero estimate reserves a minimal 1 token, not the full + # itpm limit, so it doesn't starve concurrent requests. + assert await dual_cache.async_get_cache(key=itpm_key) == 1 + + kwargs = { + "standard_logging_object": { + "model_id": "io-zero-est-id", + "hidden_params": {"litellm_model_name": "bedrock_mantle/anthropic.claude-opus-4-7"}, + "metadata": dict(request_kwargs["metadata"]), + }, + "metadata": request_kwargs["metadata"], + } + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "hi"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=7, completion_tokens=0, total_tokens=7), + ) + await check.async_log_success_event(kwargs, response, None, None) + + assert await dual_cache.async_get_cache(key=itpm_key) == 7 + + @pytest.mark.asyncio + async def test_zero_estimate_reserves_minimal_capacity_before_reconcile(self): + """ + A zero/failed estimate reserves a minimal 1 token rather than the + full itpm limit, so up to itpm_limit such calls are allowed + concurrently instead of the first one claiming the entire bucket. + """ + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "itpm": 2, + }, + "model_info": {"id": "io-zero-cap-id"}, + "model_name": "opus", + } + request_kwargs = {"max_tokens": 5, "metadata": {}} + set_io_token_rate_limit_request_kwargs(request_kwargs) + await check.async_pre_call_check(deployment) + + # Second zero-estimate call still fits within the itpm=2 limit. + set_io_token_rate_limit_request_kwargs({"max_tokens": 5, "metadata": {}}) + await check.async_pre_call_check(deployment) + + # A third exceeds the limit and is rejected. + set_io_token_rate_limit_request_kwargs({"max_tokens": 5, "metadata": {}}) + with pytest.raises(litellm.RateLimitError): + await check.async_pre_call_check(deployment) + + @pytest.mark.asyncio + async def test_explicit_zero_max_tokens_does_not_reserve_otpm(self): + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "otpm": 5, + }, + "model_info": {"id": "io-zero-output-id"}, + "model_name": "opus", + } + zero_output_kwargs = { + "messages": [{"role": "user", "content": "hello"}], + "max_tokens": 0, + "metadata": {}, + } + set_io_token_rate_limit_request_kwargs(zero_output_kwargs) + await check.async_pre_call_check(deployment) + + zero_output_otpm_key = zero_output_kwargs["metadata"][OTPM_CACHE_KEY] + assert zero_output_kwargs["metadata"][OTPM_RESERVED_KEY] == 0 + assert (await dual_cache.async_get_cache(key=zero_output_otpm_key) or 0) == 0 + + normal_output_kwargs = { + "messages": [{"role": "user", "content": "hello"}], + "max_tokens": 5, + "metadata": {}, + } + set_io_token_rate_limit_request_kwargs(normal_output_kwargs) + await check.async_pre_call_check(deployment) + + normal_output_otpm_key = normal_output_kwargs["metadata"][OTPM_CACHE_KEY] + assert await dual_cache.async_get_cache(key=normal_output_otpm_key) == 5 + + def test_sync_io_pre_call_reserves_and_reconciles(self): + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "itpm": 100, + "otpm": 50, + }, + "model_info": {"id": "io-sync-id"}, + "model_name": "opus", + } + request_kwargs = { + "messages": [{"role": "user", "content": "hello"}], + "max_tokens": 10, + "metadata": {}, + } + set_io_token_rate_limit_request_kwargs(request_kwargs) + check.pre_call_check(deployment) + + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:io-sync-id:bedrock_mantle/anthropic.claude-opus-4-7:itpm:{minute}" + otpm_key = f"global_router:io-sync-id:bedrock_mantle/anthropic.claude-opus-4-7:otpm:{minute}" + kwargs = { + "standard_logging_object": { + "model_id": "io-sync-id", + "hidden_params": {"litellm_model_name": "bedrock_mantle/anthropic.claude-opus-4-7"}, + "metadata": dict(request_kwargs["metadata"]), + }, + "metadata": request_kwargs["metadata"], + } + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "hi"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=5, completion_tokens=3, total_tokens=8), + ) + check.log_success_event(kwargs, response, None, None) + + assert dual_cache.get_cache(key=itpm_key) == 5 + assert dual_cache.get_cache(key=otpm_key) == 3 + + @pytest.mark.asyncio + async def test_reconcile_runs_via_success_event_without_model_id(self): + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + itpm_key = "global_router:io-noid:bedrock_mantle/test:itpm:12-34" + await dual_cache.async_increment_cache(key=itpm_key, value=10, ttl=60) + + # standard_logging_object has no model_id (only the TPM path needs it); + # IO reconciliation must still run off the stashed cache key. + kwargs = { + "standard_logging_object": { + "hidden_params": {"litellm_model_name": "bedrock_mantle/test"}, + "metadata": {}, + }, + "metadata": {ITPM_RESERVED_KEY: 10, ITPM_CACHE_KEY: itpm_key}, + } + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "hi"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=3, completion_tokens=0, total_tokens=3), + ) + await check.async_log_success_event(kwargs, response, None, None) + + assert await dual_cache.async_get_cache(key=itpm_key) == 3 + + @pytest.mark.asyncio + async def test_failure_clears_reservation_so_retry_is_not_poisoned(self): + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:io-first:bedrock_mantle/test:itpm:{minute}" + await dual_cache.async_increment_cache(key=itpm_key, value=8, ttl=60) + + # Shared request metadata carrying the first (IO) deployment's reservation. + metadata = {ITPM_RESERVED_KEY: 8, ITPM_CACHE_KEY: itpm_key} + fail_kwargs = { + "metadata": metadata, + "standard_logging_object": { + "model_id": "io-first", + "hidden_params": {"litellm_model_name": "bedrock_mantle/test"}, + "metadata": {}, + }, + } + await check.async_log_failure_event(fail_kwargs, None, None, None) + + assert await dual_cache.async_get_cache(key=itpm_key) == 0 + assert ITPM_RESERVED_KEY not in metadata + assert ITPM_CACHE_KEY not in metadata + + # Retry succeeds on a non-IO fallback deployment reusing the same metadata. + retry_kwargs = { + "metadata": metadata, + "standard_logging_object": { + "model_id": "non-io-second", + "hidden_params": {"litellm_model_name": "openai/gpt-4o-mini"}, + "metadata": {}, + "total_tokens": 12, + }, + } + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "ok"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=6, completion_tokens=6, total_tokens=12), + ) + await check.async_log_success_event(retry_kwargs, response, None, None) + + # The first deployment's ITPM counter is not driven negative... + assert await dual_cache.async_get_cache(key=itpm_key) == 0 + # ...and the non-IO deployment's TPM usage is tracked normally. + tpm_key = f"non-io-second:openai/gpt-4o-mini:tpm:{minute}" + assert await dual_cache.async_get_cache(key=tpm_key) == 12 + + @pytest.mark.asyncio + async def test_stale_reservation_refunded_before_retry_overwrites_it(self): + """ + A retry reuses the same mutable kwargs dict for the next deployment. + If deployment A's failure event hasn't run yet (e.g. it was scheduled + as a background task) when the retry calls + set_io_token_rate_limit_request_kwargs for deployment B, the router + must first synchronously refund + clear A's reservation via + refund_stale_reservation_before_retry - otherwise A's counter stays + elevated by the reservation until its TTL expires, and the + now-orphaned sentinels must not leak into B's accounting either. + """ + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + minute = get_utc_datetime().strftime("%H-%M") + itpm_key_a = f"global_router:io-retry-a:bedrock_mantle/test-a:itpm:{minute}" + await dual_cache.async_increment_cache(key=itpm_key_a, value=9, ttl=60) + + # Deployment A's still-unreconciled reservation, stashed on the shared + # kwargs dict the retry loop reuses. + shared_kwargs = {"metadata": {ITPM_RESERVED_KEY: 9, ITPM_CACHE_KEY: itpm_key_a}} + + # Router calls this before overwriting kwargs for deployment B's attempt - + # simulating the fix landing ahead of set_io_token_rate_limit_request_kwargs. + refund_stale_reservation_before_retry(dual_cache, shared_kwargs) + + # A's reservation is refunded immediately, not left stranded for a + # background failure task that may run arbitrarily later (or never, + # if the sentinels get cleared out from under it first). + assert await dual_cache.async_get_cache(key=itpm_key_a) == 0 + assert ITPM_RESERVED_KEY not in shared_kwargs["metadata"] + assert ITPM_CACHE_KEY not in shared_kwargs["metadata"] + + # A's own (now-late) failure event finds nothing left to refund and + # is a safe no-op, since the sentinels were already cleared above. + io_token_refund_failure(dual_cache, shared_kwargs) + assert await dual_cache.async_get_cache(key=itpm_key_a) == 0 + + # The retry proceeds to stash deployment B's own reservation on the + # same dict; it starts clean, unaffected by A's cleared sentinels. + set_io_token_rate_limit_request_kwargs(shared_kwargs) + itpm_key_b = f"global_router:io-retry-b:bedrock_mantle/test-b:itpm:{minute}" + shared_kwargs["metadata"][ITPM_RESERVED_KEY] = 4 + shared_kwargs["metadata"][ITPM_CACHE_KEY] = itpm_key_b + await dual_cache.async_increment_cache(key=itpm_key_b, value=4, ttl=60) + assert await dual_cache.async_get_cache(key=itpm_key_b) == 4 + + @pytest.mark.asyncio + async def test_client_supplied_reservation_keys_are_stripped(self): + # metadata is caller-controlled; the server-only reservation sentinels + # must be removed before the router captures the request kwargs. + forged = { + "metadata": {ITPM_RESERVED_KEY: 999999, ITPM_CACHE_KEY: "attacker:key:itpm:00-00"}, + "litellm_metadata": {OTPM_RESERVED_KEY: 7}, + "litellm_params": {"metadata": {OTPM_CACHE_KEY: "attacker:key:otpm:00-00"}}, + } + set_io_token_rate_limit_request_kwargs(forged) + stored = get_io_token_rate_limit_request_kwargs() + + assert ITPM_RESERVED_KEY not in stored["metadata"] + assert ITPM_CACHE_KEY not in stored["metadata"] + assert OTPM_RESERVED_KEY not in stored["litellm_metadata"] + assert OTPM_CACHE_KEY not in stored["litellm_params"]["metadata"] + + @pytest.mark.asyncio + async def test_forged_reservation_cannot_decrement_counter(self): + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + victim_key = "global_router:victim:model:itpm:00-00" + await dual_cache.async_increment_cache(key=victim_key, value=100, ttl=60) + + # A caller forges a reservation pointing at another deployment's counter. + kwargs = { + "metadata": {ITPM_RESERVED_KEY: 100, ITPM_CACHE_KEY: victim_key}, + "standard_logging_object": { + "model_id": "m", + "hidden_params": {"litellm_model_name": "model"}, + "metadata": {}, + "total_tokens": 2, + }, + } + # The router sanitizes the request kwargs before the call runs. + set_io_token_rate_limit_request_kwargs(kwargs) + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "ok"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=1, completion_tokens=1, total_tokens=2), + ) + await check.async_log_success_event(kwargs, response, None, None) + + # The forged reservation was stripped, so the victim counter is untouched. + assert await dual_cache.async_get_cache(key=victim_key) == 100 + + @pytest.mark.asyncio + async def test_otpm_reservation_error_rolls_back_itpm(self): + from litellm.utils import get_utc_datetime + + class _OtpmFailCache(DualCache): + async def async_increment_cache(self, key, **kwargs): + if ":otpm:" in key: + raise RuntimeError("transient cache error") + return await super().async_increment_cache(key=key, **kwargs) + + dual_cache = _OtpmFailCache() + deployment = { + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "itpm": 1000, + "otpm": 1000, + }, + "model_info": {"id": "io-rollback-id"}, + "model_name": "opus", + } + set_io_token_rate_limit_request_kwargs( + { + "messages": [{"role": "user", "content": "hello world"}], + "max_tokens": 5, + "metadata": {}, + } + ) + + with pytest.raises(RuntimeError): + await async_io_token_pre_call_check(dual_cache, deployment) + + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:io-rollback-id:bedrock_mantle/anthropic.claude-opus-4-7:itpm:{minute}" + # A transient OTPM error must release the ITPM reservation, not leak it. + assert (await dual_cache.async_get_cache(key=itpm_key) or 0) == 0 + + @pytest.mark.asyncio + async def test_reconcile_clears_stash_even_when_increment_errors(self): + class _ItpmFailCache(DualCache): + async def async_increment_cache(self, key, **kwargs): + if ":itpm:" in key: + raise RuntimeError("transient cache error") + return await super().async_increment_cache(key=key, **kwargs) + + dual_cache = _ItpmFailCache() + metadata = {ITPM_RESERVED_KEY: 5, ITPM_CACHE_KEY: "global_router:x:model:itpm:00-00"} + kwargs = {"metadata": metadata} + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "ok"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=3, completion_tokens=0, total_tokens=3), + ) + + with pytest.raises(RuntimeError): + await async_io_token_reconcile_success(dual_cache, kwargs, response) + + # The stash is cleared even though reconciliation raised, so a duplicate + # success event can't re-process it. + assert ITPM_RESERVED_KEY not in metadata + assert ITPM_CACHE_KEY not in metadata + + @pytest.mark.asyncio + async def test_io_conflict_warning_not_collapsed_for_missing_model_id(self, caplog): + import logging + + check = ModelRateLimitingCheck(dual_cache=DualCache()) + deployment = { + "litellm_params": {"model": "openai/gpt-4o-mini", "itpm": 100, "tpm": 1000}, + "model_info": {}, + } + with caplog.at_level(logging.WARNING, logger="LiteLLM Router"): + check._warn_io_token_and_tpm_rpm_coexist_once(deployment) + check._warn_io_token_and_tpm_rpm_coexist_once(deployment) + + warnings = [r for r in caplog.records if "both limit types are enforced" in r.message] + # id-less deployments are not collapsed onto a single dedup key. + assert len(warnings) == 2 + + @pytest.mark.asyncio + async def test_missing_deployment_id_skips_io_reservation(self): + dual_cache = DualCache() + deployment = { + "litellm_params": {"model": "openai/gpt-4o-mini", "itpm": 100}, + "model_info": {}, # no id -> cannot build a per-deployment cache key + "model_name": "opus", + } + request_kwargs = { + "messages": [{"role": "user", "content": "hello world"}], + "max_tokens": 5, + "metadata": {}, + } + set_io_token_rate_limit_request_kwargs(request_kwargs) + + result = await async_io_token_pre_call_check(dual_cache, deployment) + + assert result is deployment + # No reservation is stashed, so nothing lands in a shared None:None bucket. + assert ITPM_RESERVED_KEY not in request_kwargs["metadata"] + + @pytest.mark.asyncio + async def test_reconcile_uses_reservation_minute_key(self): + dual_cache = DualCache() + # Reservation was made on a fixed minute key; a call that finishes in a + # later minute must reconcile against that same key, never a key built + # from the response-time minute. + itpm_key = "global_router:io-min-id:bedrock_mantle/test:itpm:99-99" + await dual_cache.async_increment_cache(key=itpm_key, value=10, ttl=60) + + kwargs = {"metadata": {ITPM_RESERVED_KEY: 10, ITPM_CACHE_KEY: itpm_key}} + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "hi"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=4, completion_tokens=0, total_tokens=4), + ) + await async_io_token_reconcile_success(dual_cache, kwargs, response) + + assert await dual_cache.async_get_cache(key=itpm_key) == 4 + + @pytest.mark.asyncio + async def test_reconcile_missing_usage_keeps_reservation(self): + dual_cache = DualCache() + itpm_key = "global_router:io-missing-usage:bedrock_mantle/test:itpm:00-00" + otpm_key = "global_router:io-missing-usage:bedrock_mantle/test:otpm:00-00" + await dual_cache.async_increment_cache(key=itpm_key, value=8, ttl=60) + await dual_cache.async_increment_cache(key=otpm_key, value=5, ttl=60) + + kwargs = { + "metadata": { + ITPM_RESERVED_KEY: 8, + OTPM_RESERVED_KEY: 5, + ITPM_CACHE_KEY: itpm_key, + OTPM_CACHE_KEY: otpm_key, + } + } + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "ok"}, "index": 0, "finish_reason": "stop"}], + ) + + await async_io_token_reconcile_success(dual_cache, kwargs, response) + + assert await dual_cache.async_get_cache(key=itpm_key) == 8 + assert await dual_cache.async_get_cache(key=otpm_key) == 5 + assert ITPM_RESERVED_KEY not in kwargs["metadata"] + + @pytest.mark.asyncio + async def test_reconcile_total_tokens_only_keeps_reservation(self): + """ + A response usage object with only total_tokens (no prompt/completion + breakdown) can't be split into input/output, so it must be treated the + same as missing usage: keep the reservation instead of resolving to + (0, 0) and refunding it in full. + """ + dual_cache = DualCache() + itpm_key = "global_router:io-total-only:bedrock_mantle/test:itpm:00-00" + otpm_key = "global_router:io-total-only:bedrock_mantle/test:otpm:00-00" + await dual_cache.async_increment_cache(key=itpm_key, value=8, ttl=60) + await dual_cache.async_increment_cache(key=otpm_key, value=5, ttl=60) + + kwargs = { + "metadata": { + ITPM_RESERVED_KEY: 8, + OTPM_RESERVED_KEY: 5, + ITPM_CACHE_KEY: itpm_key, + OTPM_CACHE_KEY: otpm_key, + } + } + response = {"type": "message", "usage": {"total_tokens": 13}} + + await async_io_token_reconcile_success(dual_cache, kwargs, response) + + assert await dual_cache.async_get_cache(key=itpm_key) == 8 + assert await dual_cache.async_get_cache(key=otpm_key) == 5 + + def test_reconcile_standard_logging_total_tokens_only_keeps_reservation(self): + dual_cache = DualCache() + itpm_key = "global_router:io-slo-total-only:bedrock_mantle/test:itpm:00-00" + dual_cache.set_cache(key=itpm_key, value=10, ttl=60) + + kwargs = { + "metadata": {ITPM_RESERVED_KEY: 10, ITPM_CACHE_KEY: itpm_key}, + "standard_logging_object": {"total_tokens": 4}, + } + response = {"type": "message", "role": "assistant", "content": []} + + io_token_reconcile_success(dual_cache, kwargs, response) + + assert dual_cache.get_cache(key=itpm_key) == 10 + + @pytest.mark.asyncio + async def test_reconcile_falls_back_to_standard_logging_object(self): + dual_cache = DualCache() + itpm_key = "global_router:io-slo-fallback:bedrock_mantle/test:itpm:00-00" + await dual_cache.async_increment_cache(key=itpm_key, value=10, ttl=60) + + kwargs = { + "metadata": {ITPM_RESERVED_KEY: 10, ITPM_CACHE_KEY: itpm_key}, + "standard_logging_object": { + "prompt_tokens": 4, + "completion_tokens": 0, + "total_tokens": 4, + }, + } + response = {"type": "message", "role": "assistant", "content": []} + + await async_io_token_reconcile_success(dual_cache, kwargs, response) + + assert await dual_cache.async_get_cache(key=itpm_key) == 4 + + def test_sync_reconcile_anthropic_dict_usage(self): + dual_cache = DualCache() + itpm_key = "global_router:io-anthropic:bedrock_mantle/test:itpm:00-00" + otpm_key = "global_router:io-anthropic:bedrock_mantle/test:otpm:00-00" + dual_cache.set_cache(key=itpm_key, value=6, ttl=60) + dual_cache.set_cache(key=otpm_key, value=4, ttl=60) + + kwargs = { + "metadata": { + ITPM_RESERVED_KEY: 6, + OTPM_RESERVED_KEY: 4, + ITPM_CACHE_KEY: itpm_key, + OTPM_CACHE_KEY: otpm_key, + } + } + response = { + "type": "message", + "usage": {"input_tokens": 3, "output_tokens": 2, "cache_read_input_tokens": 1}, + } + + io_token_reconcile_success(dual_cache, kwargs, response) + + assert dual_cache.get_cache(key=itpm_key) == 2 + assert dual_cache.get_cache(key=otpm_key) == 2 + + @pytest.mark.asyncio + async def test_io_and_tpm_rpm_limits_both_enforced_with_warning(self, caplog): + import logging + + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + model_id = "io-mixed-id" + deployment_name = "bedrock_mantle/anthropic.claude-opus-4-7" + deployment = { + "litellm_params": { + "model": deployment_name, + "itpm": 100, + "rpm": 1, + }, + "model_info": {"id": model_id}, + "model_name": "opus", + } + + minute = get_utc_datetime().strftime("%H-%M") + rpm_key = f"{model_id}:{deployment_name}:rpm:{minute}" + itpm_key = f"global_router:{model_id}:{deployment_name}:itpm:{minute}" + await dual_cache.async_increment_cache(key=rpm_key, value=5, ttl=60) + + request_kwargs = { + "messages": [{"role": "user", "content": "hi"}], + "max_tokens": 5, + "metadata": {}, + } + set_io_token_rate_limit_request_kwargs(request_kwargs) + + with caplog.at_level(logging.WARNING, logger="LiteLLM Router"): + with pytest.raises(litellm.RateLimitError): + await check.async_pre_call_check(deployment) + + assert await dual_cache.async_get_cache(key=rpm_key) == 6 + assert (await dual_cache.async_get_cache(key=itpm_key) or 0) == 0 + assert ITPM_RESERVED_KEY not in request_kwargs["metadata"] + assert any("both limit types are enforced" in record.message for record in caplog.records) + + @pytest.mark.asyncio + async def test_io_success_still_tracks_tpm_for_mixed_deployment(self): + """ + A deployment with itpm/otpm AND tpm/rpm must have BOTH counters updated on + success, otherwise the tpm_key the pre-call check reads is never written + and the tpm_limit can never be enforced. + """ + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + model_id = "io-tpm-mixed-id" + deployment_name = "bedrock_mantle/anthropic.claude-opus-4-7" + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:{model_id}:{deployment_name}:itpm:{minute}" + tpm_key = f"{model_id}:{deployment_name}:tpm:{minute}" + await dual_cache.async_increment_cache(key=itpm_key, value=5, ttl=60) + + kwargs = { + "metadata": {ITPM_RESERVED_KEY: 5, ITPM_CACHE_KEY: itpm_key}, + "standard_logging_object": { + "model_id": model_id, + "total_tokens": 7, + "hidden_params": {"litellm_model_name": deployment_name}, + }, + } + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "hi"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=3, completion_tokens=0, total_tokens=3), + ) + + await check.async_log_success_event(kwargs, response, None, None) + + # ITPM reconciled down from the 5-token reservation to actual usage (3). + assert await dual_cache.async_get_cache(key=itpm_key) == 3 + # TPM tracking must still run so the tpm/rpm pre-call path can enforce it. + assert await dual_cache.async_get_cache(key=tpm_key) == 7 + + def test_io_success_still_tracks_tpm_for_mixed_deployment_sync(self): + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + model_id = "io-tpm-mixed-sync-id" + deployment_name = "bedrock_mantle/anthropic.claude-opus-4-7" + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:{model_id}:{deployment_name}:itpm:{minute}" + tpm_key = f"{model_id}:{deployment_name}:tpm:{minute}" + dual_cache.set_cache(key=itpm_key, value=5, ttl=60) + + kwargs = { + "metadata": {ITPM_RESERVED_KEY: 5, ITPM_CACHE_KEY: itpm_key}, + "standard_logging_object": { + "model_id": model_id, + "total_tokens": 7, + "hidden_params": {"litellm_model_name": deployment_name}, + }, + } + response = ModelResponse( + choices=[{"message": {"role": "assistant", "content": "hi"}, "index": 0, "finish_reason": "stop"}], + usage=Usage(prompt_tokens=3, completion_tokens=0, total_tokens=3), + ) + + check.log_success_event(kwargs, response, None, None) + + assert dual_cache.get_cache(key=itpm_key) == 3 + assert dual_cache.get_cache(key=tpm_key) == 7 + + @pytest.mark.asyncio + async def test_failure_refunds_itpm_reservation(self): + from litellm.utils import get_utc_datetime + + dual_cache = DualCache() + check = ModelRateLimitingCheck(dual_cache=dual_cache) + minute = get_utc_datetime().strftime("%H-%M") + itpm_key = f"global_router:io-refund-id:bedrock_mantle/test:itpm:{minute}" + await dual_cache.async_increment_cache(key=itpm_key, value=20, ttl=60) + + reservation = {ITPM_RESERVED_KEY: 20, ITPM_CACHE_KEY: itpm_key} + kwargs = { + "standard_logging_object": { + "model_id": "io-refund-id", + "hidden_params": {"litellm_model_name": "bedrock_mantle/test"}, + "metadata": dict(reservation), + }, + "metadata": dict(reservation), + } + await check.async_log_failure_event(kwargs, None, None, None) + + current = await dual_cache.async_get_cache(key=itpm_key) + assert current == 0 + + +class TestRouterIOTokenIntegration: + @pytest.mark.asyncio + async def test_model_group_info_aggregates_io_limits(self): + router = Router( + model_list=[ + { + "model_name": "opus", + "litellm_params": { + "model": "bedrock_mantle/anthropic.claude-opus-4-7", + "itpm": 100, + "otpm": 20, + }, + } + ], + optional_pre_call_checks=["enforce_model_rate_limits"], + ) + info = router.get_model_group_info("opus") + assert info is not None + assert info.itpm == 100 + assert info.otpm == 20 diff --git a/tests/test_litellm/test_router_exception_redaction.py b/tests/test_litellm/test_router_exception_redaction.py index e40bf661da4..2066352e2ce 100644 --- a/tests/test_litellm/test_router_exception_redaction.py +++ b/tests/test_litellm/test_router_exception_redaction.py @@ -5,15 +5,21 @@ The Router historically appended internal config names (model_group, fallback_model_group, fallback failure detail, deployment timeouts, context_window_fallbacks dict, etc.) onto the message of the exception it re-raises. That message is then surfaced to clients by -ProxyException, leaking the proxy's internal wiring. +ProxyException, leaking the proxy's internal wiring and, when fallbacks +are configured as inline deployment dicts, the provider credentials +inside those dicts. The flag defaults to True to preserve historical behavior (no breaking change for existing deployments). Set it to False to redact -those strings from the raised exception's message. +those strings from the raised exception's message. Regardless of the +flag, provider credentials inside inline-dict fallbacks are now masked +so a raw api_key / aws_* value never reaches the client. These tests verify that with the flag ON (default) the historical -leak strings appear in the raised exception's message, and with the -flag OFF the proxy's internal wiring is redacted. +topology strings appear in the raised exception's message, with the +flag OFF the proxy's internal wiring is redacted, and that a raw +provider credential never appears in the message regardless of the +flag. Five leak sites are gated in `litellm/router.py`: @@ -40,6 +46,7 @@ _RECEIVED_MODEL_GROUP_PHRASE = "Received Model Group=" _AVAILABLE_FALLBACKS_PHRASE = "Available Model Group Fallbacks=" _CONTEXT_WINDOW_HINT_PHRASE = "context_window_fallbacks=" _INTERNAL_MODEL_GROUP_NAME = "all-anthropic/claude-secret-internal" +_FALLBACK_CREDENTIAL = "sk-INLINEFALLBACKSECRET1234567890" def _router_with_rate_limit_failure() -> Router: @@ -76,6 +83,37 @@ def _router_with_context_window_failure() -> Router: ) +def _router_with_credentialed_fallback() -> Router: + """Primary fails, and its fallback is an inline dict that carries a provider + api_key. When the fallback also fails, the router embeds that dict in the + exception message, which is where a raw credential would otherwise leak.""" + return Router( + model_list=[ + { + "model_name": _INTERNAL_MODEL_GROUP_NAME, + "litellm_params": { + "model": "gpt-4o", + "api_key": "key", + "mock_response": "litellm.RateLimitError", + }, + "model_info": {"id": "secret-deployment-id"}, + }, + ], + fallbacks=[ + { + _INTERNAL_MODEL_GROUP_NAME: [ + { + "model": "gpt-4o", + "api_key": _FALLBACK_CREDENTIAL, + "mock_response": "litellm.RateLimitError", + } + ] + } + ], + num_retries=0, + ) + + @pytest.fixture(autouse=True) def _reset_expose_flag(): """Each test starts with the flag in its default (on) state.""" @@ -110,7 +148,8 @@ async def test_flag_off_does_not_leak_received_model_group(): @pytest.mark.asyncio -async def test_default_leaks_received_model_group(): +async def test_flag_on_shows_received_model_group(): + litellm.expose_router_debug_in_errors = True router = _router_with_rate_limit_failure() with pytest.raises(litellm.RateLimitError) as excinfo: await router.acompletion( @@ -142,7 +181,8 @@ async def test_flag_off_does_not_leak_context_window_fallback_hint(): @pytest.mark.asyncio -async def test_default_leaks_context_window_fallback_hint(): +async def test_flag_on_shows_context_window_fallback_hint(): + litellm.expose_router_debug_in_errors = True router = _router_with_context_window_failure() with pytest.raises(litellm.ContextWindowExceededError) as excinfo: await router.acompletion( @@ -153,7 +193,7 @@ async def test_default_leaks_context_window_fallback_hint(): assert _CONTEXT_WINDOW_HINT_PHRASE in msg, msg # Site 5 also fires for ContextWindow errors that exit the # orchestrator without fallback resolution, so the model_group - # name leaks under the default behavior. + # name is shown under the opt-in behavior. assert _INTERNAL_MODEL_GROUP_NAME in msg, msg @@ -192,7 +232,8 @@ async def test_flag_off_does_not_leak_when_no_fallback_group_found(): @pytest.mark.asyncio -async def test_default_leaks_when_no_fallback_group_found(): +async def test_flag_on_shows_when_no_fallback_group_found(): + litellm.expose_router_debug_in_errors = True router = Router( model_list=[ { @@ -258,7 +299,8 @@ async def test_flag_off_does_not_leak_deployment_timeout_debug(): @pytest.mark.asyncio -async def test_default_leaks_deployment_timeout_debug(): +async def test_flag_on_shows_deployment_timeout_debug(): + litellm.expose_router_debug_in_errors = True router = _router_with_plain_deployment() with pytest.raises(litellm.Timeout) as excinfo: await router.acompletion( @@ -298,7 +340,8 @@ async def test_flag_off_does_not_leak_content_policy_fallback_hint(): @pytest.mark.asyncio -async def test_default_leaks_content_policy_fallback_hint(): +async def test_flag_on_shows_content_policy_fallback_hint(): + litellm.expose_router_debug_in_errors = True router = _router_with_plain_deployment() with pytest.raises(litellm.ContentPolicyViolationError) as excinfo: await router.acompletion( @@ -309,3 +352,82 @@ async def test_default_leaks_content_policy_fallback_hint(): msg = excinfo.value.message assert "content_policy_fallback=" in msg, msg assert _INTERNAL_MODEL_GROUP_NAME in msg, msg + + +# --- Credential masking: raw provider keys never leak, either flag state ---- + + +@pytest.mark.asyncio +async def test_flag_off_hides_fallback_credentials(): + litellm.expose_router_debug_in_errors = False + router = _router_with_credentialed_fallback() + with pytest.raises(litellm.RateLimitError) as excinfo: + await router.acompletion( + model=_INTERNAL_MODEL_GROUP_NAME, + messages=[{"role": "user", "content": "hi"}], + ) + msg = excinfo.value.message + assert _FALLBACK_CREDENTIAL not in msg, msg + assert _AVAILABLE_FALLBACKS_PHRASE not in msg, msg + + +@pytest.mark.asyncio +async def test_flag_on_masks_fallback_credentials(): + litellm.expose_router_debug_in_errors = True + router = _router_with_credentialed_fallback() + with pytest.raises(litellm.RateLimitError) as excinfo: + await router.acompletion( + model=_INTERNAL_MODEL_GROUP_NAME, + messages=[{"role": "user", "content": "hi"}], + ) + msg = excinfo.value.message + # The raw credential must never appear, even though debug exposure is on + assert _FALLBACK_CREDENTIAL not in msg, msg + # The fallback wiring is still shown (masking preserves structure, it does + # not drop the whole message), so the api_key key name survives + assert "api_key" in msg, msg + + +@pytest.mark.asyncio +async def test_flag_on_scrubs_credential_from_inner_fallback_exception_string(): + """If the fallback attempt itself raises an exception whose message embeds a + raw provider credential (e.g. a provider SDK echoing back the api_key it was + called with), that string is re-embedded via `Error doing the fallback: ...` + on the terminal raise. The router must scrub known secret patterns from it. + The primary fails with a benign rate-limit; the fallback deployment fails + with an exception whose text contains the secret.""" + litellm.expose_router_debug_in_errors = True + inner_secret = "sk-INNERFALLBACKEXCEPTIONSECRET1234" + router = Router( + model_list=[ + { + "model_name": _INTERNAL_MODEL_GROUP_NAME, + "litellm_params": { + "model": "gpt-4o", + "api_key": "key", + "mock_response": "litellm.RateLimitError", + }, + "model_info": {"id": "secret-deployment-id"}, + }, + { + "model_name": "fallback-group", + "litellm_params": { + "model": "gpt-4o", + "api_key": "key", + "mock_response": f"Exception: content_filter_policy - api_key={inner_secret}", + }, + "model_info": {"id": "fallback-deployment-id"}, + }, + ], + fallbacks=[{_INTERNAL_MODEL_GROUP_NAME: ["fallback-group"]}], + num_retries=0, + ) + with pytest.raises(litellm.RateLimitError) as excinfo: + await router.acompletion( + model=_INTERNAL_MODEL_GROUP_NAME, + messages=[{"role": "user", "content": "hi"}], + ) + msg = excinfo.value.message + assert "Error doing the fallback:" in msg, msg + assert inner_secret not in msg, msg + assert "REDACTED" in msg, msg diff --git a/tests/test_litellm/test_router_model_cost_isolation.py b/tests/test_litellm/test_router_model_cost_isolation.py index d4ac9659f00..6db7b04b3b7 100644 --- a/tests/test_litellm/test_router_model_cost_isolation.py +++ b/tests/test_litellm/test_router_model_cost_isolation.py @@ -681,3 +681,76 @@ def test_custom_pricing_isolated_from_sibling_via_proxy_model_info_path(): assert resolved["gemini-2.5-flash"] != resolved["custom-priced-flash"] finally: _restore_model_cost_entries(model_keys) + + +def test_wildcard_zero_cost_request_does_not_poison_named_deployment_pricing(): + """LIT-3991 end to end: a proxy has a named text-embedding-3-small + deployment relying on built-in pricing plus an ``openai/*`` wildcard with + explicit zero pricing. One embedding call routed through the wildcard must + not clobber the shared ``openai/text-embedding-3-small`` pricing; requests + to the named deployment afterwards must still cost non-zero. + """ + shared_key = "openai/text-embedding-3-small" + model_keys = { + shared_key: copy.deepcopy(litellm.model_cost.get(shared_key)), + "text-embedding-3-small": copy.deepcopy( + litellm.model_cost.get("text-embedding-3-small") + ), + "openai/*": copy.deepcopy(litellm.model_cost.get("openai/*")), + "lit3991-named": litellm.model_cost.get("lit3991-named"), + "lit3991-wildcard": litellm.model_cost.get("lit3991-wildcard"), + } + builtin_input_cost = litellm.get_model_info(model=shared_key)[ + "input_cost_per_token" + ] + assert builtin_input_cost > 0 + + try: + router = Router( + model_list=[ + { + "model_name": "text-embedding-3-small", + "litellm_params": { + "model": "openai/text-embedding-3-small", + "api_key": "fake-key-named", + }, + "model_info": {"id": "lit3991-named"}, + }, + { + "model_name": "openai/*", + "litellm_params": { + "model": "openai/*", + "api_key": "fake-key-wildcard", + "input_cost_per_token": 0.0, + "output_cost_per_token": 0.0, + }, + "model_info": {"id": "lit3991-wildcard"}, + }, + ], + ) + + router.embedding( + model="openai/text-embedding-3-small", + input=["hello"], + mock_response=[0.1, 0.2], + ) + + assert ( + litellm.get_model_info(model=shared_key)["input_cost_per_token"] + == builtin_input_cost + ), ( + "one call through the zero-cost wildcard poisoned the shared " + f"{shared_key} pricing for the named deployment" + ) + + named_response = router.embedding( + model="text-embedding-3-small", + input=["hello"], + mock_response=[0.1, 0.2], + ) + named_cost = litellm.completion_cost( + completion_response=named_response, call_type="embedding" + ) + assert named_cost == pytest.approx(10 * builtin_input_cost) + finally: + _restore_model_cost_entries(model_keys) diff --git a/tests/test_litellm/test_router_retry_policy_update.py b/tests/test_litellm/test_router_retry_policy_update.py index 450391fd503..3fc6bc71b84 100644 --- a/tests/test_litellm/test_router_retry_policy_update.py +++ b/tests/test_litellm/test_router_retry_policy_update.py @@ -273,7 +273,13 @@ async def test_config_update_persists_and_reads_back_retry_policy(monkeypatch): assert isinstance(router.retry_policy, RetryPolicy) assert router.retry_policy.RateLimitErrorRetries == 7 - read_back = (await proxy_server.get_config())["router_settings"]["retry_policy"] + read_back = ( + await proxy_server.get_config( + user_api_key_dict=UserAPIKeyAuth( + user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-1234" + ) + ) + )["router_settings"]["retry_policy"] assert read_back.BadRequestErrorRetries == 5 assert read_back.TimeoutErrorRetries == 3 assert read_back.RateLimitErrorRetries == 7 diff --git a/tests/test_litellm/test_ruff_strict_gate.py b/tests/test_litellm/test_ruff_strict_gate.py index 22255f0555e..aad0e1bc9f9 100644 --- a/tests/test_litellm/test_ruff_strict_gate.py +++ b/tests/test_litellm/test_ruff_strict_gate.py @@ -11,16 +11,16 @@ _spec.loader.exec_module(gate) Violation = gate.Violation -def rule(name, baseline, slack): - return {name: {"baseline": baseline, "slack": slack}} +def rule(name, limit): + return {name: {"limit": limit}} def test_under_ceiling_passes(): - assert gate.evaluate({"ANN001": 100}, {"ANN001": 100}, rule("ANN001", 90, 20)) == [] + assert gate.evaluate({"ANN001": 100}, {"ANN001": 100}, rule("ANN001", 110)) == [] -def test_ceiling_is_baseline_plus_slack_boundary(): - budget = rule("ANN001", 90, 20) # cap 110 +def test_ceiling_is_the_limit_boundary(): + budget = rule("ANN001", 110) at = gate.evaluate({"ANN001": 110}, {"ANN001": 90}, budget) over = gate.evaluate({"ANN001": 111}, {"ANN001": 90}, budget) assert at == [] @@ -30,23 +30,23 @@ def test_ceiling_is_baseline_plus_slack_boundary(): def test_over_ceiling_and_change_added_fails(): - breaches = gate.evaluate({"C901": 11}, {"C901": 9}, rule("C901", 10, 0)) + breaches = gate.evaluate({"C901": 11}, {"C901": 9}, rule("C901", 10)) assert [b.rule for b in breaches] == ["C901"] assert breaches[0].added == 2 def test_base_already_over_ceiling_change_added_nothing_is_not_blamed(): - # drift safety: base is over cap, this change leaves the count where it is - assert gate.evaluate({"C901": 15}, {"C901": 15}, rule("C901", 10, 0)) == [] + # drift safety: base is over limit, this change leaves the count where it is + assert gate.evaluate({"C901": 15}, {"C901": 15}, rule("C901", 10)) == [] def test_change_that_reduces_an_over_ceiling_rule_is_not_blamed(): - # still over cap, but moving the right direction - assert gate.evaluate({"C901": 14}, {"C901": 16}, rule("C901", 10, 0)) == [] + # still over limit, but moving the right direction + assert gate.evaluate({"C901": 14}, {"C901": 16}, rule("C901", 10)) == [] def test_rules_are_independent(): - budget = {**rule("ANN001", 100, 50), **rule("C901", 10, 0)} + budget = {**rule("ANN001", 150), **rule("C901", 10)} breaches = gate.evaluate( {"ANN001": 130, "C901": 11}, {"ANN001": 100, "C901": 10}, budget ) @@ -54,7 +54,19 @@ def test_rules_are_independent(): def test_missing_rule_counts_as_zero(): - assert gate.evaluate({}, {}, rule("C901", 0, 0)) == [] + assert gate.evaluate({}, {}, rule("C901", 0)) == [] + + +def test_update_ratchets_limit_down_by_what_the_branch_fixed_never_up(): + budget = {**rule("ANN001", 150), **rule("C901", 10)} + # ANN001 fixed 20 (100 -> 80) so its limit falls 150 -> 130; C901 grew, so its + # limit holds flat at 10 (a fix must never loosen a ceiling). + current = {"ANN001": 80, "C901": 12} + base = {"ANN001": 100, "C901": 9} + assert gate.ratcheted_budget(budget, current, base) == { + "ANN001": {"limit": 130}, + "C901": {"limit": 10}, + } def test_parse_changed_lines_maps_added_lines_per_file(): @@ -82,3 +94,19 @@ def test_introduced_keeps_only_violations_on_changed_lines(): @pytest.mark.parametrize("hunk", ["@@ -1 +1 @@", "@@ -1,0 +1,2 @@"]) def test_parse_changed_lines_handles_single_and_ranged_hunks(hunk): assert gate.parse_changed_lines(f"+++ b/litellm/a.py\n{hunk}\n")["litellm/a.py"] + + +def test_over_ceiling_flags_only_counts_above_the_limit(): + budget = rule("C901", 10) + assert gate.over_ceiling({"C901": 10}, budget) == frozenset() + assert gate.over_ceiling({"C901": 11}, budget) == frozenset({"C901"}) + assert gate.over_ceiling({}, budget) == frozenset() + + +def test_over_ceiling_ignores_rules_missing_from_the_budget(): + assert gate.over_ceiling({"NEW99": 100}, rule("C901", 10)) == frozenset() + + +def test_over_ceiling_is_independent_across_rules(): + budget = {**rule("ANN001", 150), **rule("C901", 10)} + assert gate.over_ceiling({"ANN001": 130, "C901": 11}, budget) == frozenset({"C901"}) diff --git a/tests/test_litellm/test_type_check_gate.py b/tests/test_litellm/test_type_check_gate.py index e99ad0a4f41..66a28360af9 100644 --- a/tests/test_litellm/test_type_check_gate.py +++ b/tests/test_litellm/test_type_check_gate.py @@ -54,78 +54,114 @@ def test_paths_outside_repo_are_skipped(): assert gate.count_basedpyright(payload) == {} +def test_symlinked_root_keeps_diagnostics_in_tree(tmp_path): + real = tmp_path / "real" + real.mkdir() + link = tmp_path / "link" + link.symlink_to(real) + payload = json.dumps( + { + "generalDiagnostics": [ + _bpr(link / "litellm" / "x.py", "error", "reportArgumentType") + ] + } + ) + assert gate.count_basedpyright(payload, root=link) == {"reportArgumentType": 1} + + def test_at_or_under_ceiling_passes(): - budget = {"no-any-return": {"baseline": 5, "slack": 0}} + budget = {"no-any-return": {"limit": 5}} assert gate.evaluate({"no-any-return": 5}, {}, budget) == [] def test_one_more_error_than_ceiling_fails(): - budget = {"no-any-return": {"baseline": 5, "slack": 0}} + budget = {"no-any-return": {"limit": 5}} assert gate.evaluate({"no-any-return": 6}, {}, budget) == [ gate.Breach("no-any-return", 6, 5, 6) ] -def test_slack_absorbs_small_increase_then_fails_past_it(): - budget = {"arg-type": {"baseline": 5, "slack": 5}} +def test_limit_absorbs_increase_up_to_it_then_fails_past_it(): + budget = {"arg-type": {"limit": 10}} assert gate.evaluate({"arg-type": 10}, {}, budget) == [] assert gate.evaluate({"arg-type": 11}, {}, budget) == [ gate.Breach("arg-type", 11, 10, 11) ] -def test_unbudgeted_new_code_uses_default_slack(): - assert gate.evaluate({"brand-new": gate.DEFAULT_SLACK}, {}, {}) == [] - assert gate.evaluate({"brand-new": gate.DEFAULT_SLACK + 1}, {}, {}) == [ +def test_unbudgeted_new_code_uses_default_limit(): + assert gate.evaluate({"brand-new": gate.DEFAULT_LIMIT}, {}, {}) == [] + assert gate.evaluate({"brand-new": gate.DEFAULT_LIMIT + 1}, {}, {}) == [ gate.Breach( "brand-new", - gate.DEFAULT_SLACK + 1, - gate.DEFAULT_SLACK, - gate.DEFAULT_SLACK + 1, + gate.DEFAULT_LIMIT + 1, + gate.DEFAULT_LIMIT, + gate.DEFAULT_LIMIT + 1, ) ] def test_drift_already_over_cap_in_base_is_not_blamed_on_a_flat_change(): - # The bystander case: a rule sits over its ceiling because two earlier PRs + # The bystander case: a rule sits over its limit because two earlier PRs # summed past it. A PR that branches off that base and adds nothing must pass - # -- total > cap but total == base, so the `> base` guard spares it. - budget = {"arg-type": {"baseline": 5, "slack": 5}} + # -- total > limit but total == base, so the `> base` guard spares it. + budget = {"arg-type": {"limit": 10}} assert gate.evaluate({"arg-type": 12}, {"arg-type": 12}, budget) == [] def test_change_that_grows_an_over_cap_rule_is_blamed_for_only_what_it_added(): - # Over cap AND above base: blamed, and `added` is the delta vs base, not the + # Over limit AND above base: blamed, and `added` is the delta vs base, not the # whole overage, so the message points at this change's contribution. - budget = {"arg-type": {"baseline": 5, "slack": 5}} + budget = {"arg-type": {"limit": 10}} assert gate.evaluate({"arg-type": 14}, {"arg-type": 12}, budget) == [ gate.Breach("arg-type", 14, 10, 2) ] def test_reducing_an_over_cap_rule_below_base_passes(): - budget = {"arg-type": {"baseline": 5, "slack": 5}} + budget = {"arg-type": {"limit": 10}} assert gate.evaluate({"arg-type": 11}, {"arg-type": 12}, budget) == [] def test_no_output_against_a_nonempty_budget_is_a_vacuous_run(): # A crashed type checker emits nothing; the gate must not certify it as clean. - budget = {"no-untyped-def": {"baseline": 4888, "slack": 10}} + budget = {"no-untyped-def": {"limit": 4898}} assert gate.is_vacuous_run({}, budget) is True def test_genuine_zero_and_empty_budget_are_not_vacuous(): assert gate.is_vacuous_run({}, {}) is False + assert gate.is_vacuous_run({}, {"no-untyped-def": {"limit": 0}}) is False assert ( - gate.is_vacuous_run({}, {"no-untyped-def": {"baseline": 0, "slack": 3}}) - is False - ) - assert ( - gate.is_vacuous_run({"arg-type": 1}, {"arg-type": {"baseline": 9, "slack": 1}}) - is False + gate.is_vacuous_run({"arg-type": 1}, {"arg-type": {"limit": 10}}) is False ) +def test_update_ratchets_a_limit_down_by_what_the_branch_fixed(): + # A rule that dropped from 40 (branch point) to 30 (current) fixed 10, so its + # limit of 100 falls to 90 -- the granted headroom (60) is preserved, not the + # raw count. + budget = {"reportAny": {"limit": 100}} + assert gate.ratcheted_budget(budget, {"reportAny": 30}, {"reportAny": 40}) == { + "reportAny": {"limit": 90} + } + + +def test_update_never_raises_a_limit_when_a_rule_grows(): + # Adding violations must not loosen the ceiling; the limit holds flat. + budget = {"reportAny": {"limit": 100}} + assert gate.ratcheted_budget(budget, {"reportAny": 55}, {"reportAny": 40}) == { + "reportAny": {"limit": 100} + } + + +def test_update_clamps_a_limit_at_zero_never_negative(): + budget = {"reportAny": {"limit": 5}} + assert gate.ratcheted_budget(budget, {"reportAny": 0}, {"reportAny": 40}) == { + "reportAny": {"limit": 0} + } + + def test_malformed_basedpyright_json_exits_loudly_not_as_zero_errors(): import pytest @@ -137,3 +173,117 @@ def test_empty_basedpyright_payload_counts_zero(): # Empty (not malformed) output parses to zero; the vacuous-run guard, not the # parser, is what rejects an empty run. assert gate.count_basedpyright("") == {} + + +def test_over_ceiling_flags_only_rules_above_their_limit(): + budget = {"reportAny": {"limit": 10}} + assert gate.over_ceiling({"reportAny": 10}, budget) == frozenset() + assert gate.over_ceiling({"reportAny": 11}, budget) == frozenset({"reportAny"}) + assert gate.over_ceiling({}, budget) == frozenset() + + +def test_over_ceiling_holds_unbudgeted_rules_to_the_default_limit(): + assert gate.over_ceiling({"brand-new": gate.DEFAULT_LIMIT}, {}) == frozenset() + assert gate.over_ceiling({"brand-new": gate.DEFAULT_LIMIT + 1}, {}) == frozenset( + {"brand-new"} + ) + + +def test_over_ceiling_is_independent_across_rules(): + budget = {"reportAny": {"limit": 10}, "reportArgumentType": {"limit": 5}} + assert gate.over_ceiling( + {"reportAny": 9, "reportArgumentType": 6}, budget + ) == frozenset({"reportArgumentType"}) + + +def test_cache_key_changes_with_base_point_and_each_fingerprint(): + key = gate.cache_key("abc", ("cfg", "lock")) + assert gate.cache_key("abc", ("cfg", "lock")) == key + assert gate.cache_key("def", ("cfg", "lock")) != key + assert gate.cache_key("abc", ("cfg2", "lock")) != key + assert gate.cache_key("abc", ("cfg", "lock2")) != key + + +def test_cached_counts_round_trip(tmp_path): + path = gate.cache_path(tmp_path, "abc123", ("f1", "f2")) + gate.store_counts(tmp_path, path, "abc123", {"reportAny": 3, "reportCall": 1}) + assert gate.load_cached_counts(path) == {"reportAny": 3, "reportCall": 1} + + +def test_missing_corrupt_or_misshapen_cache_reads_as_none(tmp_path): + path = tmp_path / "cache.json" + assert gate.load_cached_counts(path) is None + path.write_text("{not json") + assert gate.load_cached_counts(path) is None + path.write_text(json.dumps(["counts"])) + assert gate.load_cached_counts(path) is None + path.write_text(json.dumps({"base_point": "abc"})) + assert gate.load_cached_counts(path) is None + path.write_text(json.dumps({"counts": {"reportAny": "three"}})) + assert gate.load_cached_counts(path) is None + path.write_text(json.dumps({"counts": {"reportAny": True}})) + assert gate.load_cached_counts(path) is None + + +def test_scratch_is_invisible_to_the_prune_glob(): + import fnmatch + + scratch = gate.scratch_path(gate.cache_path(Path("/c"), "abc", ("f",))) + assert not fnmatch.fnmatch(scratch.name, f"{gate.CACHE_FILE_PREFIX}*") + + +def test_store_prune_spares_a_concurrent_runs_in_flight_scratch(tmp_path): + foreign = gate.scratch_path(gate.cache_path(tmp_path, "other", ("f",))) + foreign.parent.mkdir(parents=True, exist_ok=True) + foreign.write_text("{}") + mine = gate.cache_path(tmp_path, "mine", ("f",)) + gate.store_counts(tmp_path, mine, "mine", {"reportAny": 1}) + assert foreign.exists() + assert gate.load_cached_counts(mine) == {"reportAny": 1} + + +def test_store_prunes_entries_for_other_branch_points(tmp_path): + old = gate.cache_path(tmp_path, "old", ("f",)) + gate.store_counts(tmp_path, old, "old", {"reportAny": 1}) + new = gate.cache_path(tmp_path, "new", ("f",)) + gate.store_counts(tmp_path, new, "new", {"reportAny": 2}) + assert not old.exists() + assert gate.load_cached_counts(new) == {"reportAny": 2} + + +def test_base_counts_cached_returns_the_hit_without_recomputing(tmp_path): + path = gate.cache_path(tmp_path, "abc123", gate.environment_fingerprints()) + gate.store_counts(tmp_path, path, "abc123", {"reportAny": 7}) + + def explode(ref): + raise AssertionError("a cache hit must not re-run the base pass") + + assert gate.base_counts_cached("abc123", cache_dir=tmp_path, compute=explode) == { + "reportAny": 7 + } + + +def test_base_counts_cached_computes_once_then_hits(tmp_path): + calls = [] + + def fake(ref): + calls.append(ref) + return {"reportAny": 4} + + first = gate.base_counts_cached("abc123", cache_dir=tmp_path, compute=fake) + second = gate.base_counts_cached("abc123", cache_dir=tmp_path, compute=fake) + assert first == second == {"reportAny": 4} + assert calls == ["abc123"] + + +def test_an_empty_base_pass_is_never_cached(tmp_path): + calls = [] + + def crashed(ref): + calls.append(ref) + return {} + + assert gate.base_counts_cached("abc123", cache_dir=tmp_path, compute=crashed) == {} + assert gate.base_counts_cached("abc123", cache_dir=tmp_path, compute=crashed) == {} + assert calls == ["abc123", "abc123"] + assert list(tmp_path.iterdir()) == [] diff --git a/tests/test_litellm/test_type_discipline_gate.py b/tests/test_litellm/test_type_discipline_gate.py index d7d827685a6..8424d480fa6 100644 --- a/tests/test_litellm/test_type_discipline_gate.py +++ b/tests/test_litellm/test_type_discipline_gate.py @@ -14,27 +14,39 @@ gate = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(gate) -def _budget(baseline, slack): - return {"LIT006": {"baseline": baseline, "slack": slack}} +def _budget(limit): + return {"LIT006": {"limit": limit}} -def test_over_ceiling_flags_only_counts_above_baseline_plus_slack(): - budget = _budget(10, 2) # cap 12 - assert gate.over_ceiling({"LIT006": 12}, budget) == frozenset() # at cap - assert gate.over_ceiling({"LIT006": 13}, budget) == frozenset({"LIT006"}) # over cap +def test_over_ceiling_flags_only_counts_above_the_limit(): + budget = _budget(12) + assert gate.over_ceiling({"LIT006": 12}, budget) == frozenset() # at limit + assert gate.over_ceiling({"LIT006": 13}, budget) == frozenset({"LIT006"}) # over limit assert gate.over_ceiling({}, budget) == frozenset() # missing rule counts as zero def test_over_ceiling_is_independent_across_rules(): - budget = {"LIT001": {"baseline": 5, "slack": 0}, "LIT006": {"baseline": 10, "slack": 0}} + budget = {"LIT001": {"limit": 5}, "LIT006": {"limit": 10}} assert gate.over_ceiling({"LIT001": 6, "LIT006": 10}, budget) == frozenset({"LIT001"}) -def test_evaluate_blames_only_a_rule_over_cap_and_over_base(): - budget = _budget(10, 0) # cap 10 - # over cap and grown vs base -> breach +def test_evaluate_blames_only_a_rule_over_limit_and_over_base(): + budget = _budget(10) + # over limit and grown vs base -> breach assert [b.rule for b in gate.evaluate({"LIT006": 12}, {"LIT006": 9}, budget)] == ["LIT006"] - # over cap but flat vs base (pre-existing drift) -> not blamed + # over limit but flat vs base (pre-existing drift) -> not blamed assert gate.evaluate({"LIT006": 12}, {"LIT006": 12}, budget) == [] - # within cap -> not blamed regardless of base + # within limit -> not blamed regardless of base assert gate.evaluate({"LIT006": 10}, {"LIT006": 0}, budget) == [] + + +def test_update_ratchets_limit_down_by_what_the_branch_fixed_never_up(): + budget = {"LIT001": {"limit": 100}, "LIT006": {"limit": 10}} + # LIT001 fixed 15 (60 -> 45) so its limit falls 100 -> 85; LIT006 grew, so its + # limit holds flat at 10. + current = {"LIT001": 45, "LIT006": 12} + base = {"LIT001": 60, "LIT006": 9} + assert gate.ratcheted_budget(budget, current, base) == { + "LIT001": {"limit": 85}, + "LIT006": {"limit": 10}, + } diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index 6f9f26bf6dd..1a5c419754b 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -710,6 +710,9 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "cache_creation_input_token_cost": {"type": "number"}, "cache_creation_input_token_cost_above_1hr": {"type": "number"}, "cache_creation_input_token_cost_above_200k_tokens": {"type": "number"}, + "cache_creation_input_token_cost_above_272k_tokens": {"type": "number"}, + "cache_creation_input_token_cost_flex": {"type": "number"}, + "cache_creation_input_token_cost_priority": {"type": "number"}, "cache_read_input_token_cost": {"type": "number"}, "cache_read_input_token_cost_above_200k_tokens": {"type": "number"}, "cache_read_input_token_cost_above_272k_tokens": {"type": "number"}, @@ -833,6 +836,7 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "supports_image_input": {"type": "boolean"}, "supports_nova_canvas_image_edit": {"type": "boolean"}, "supports_parallel_function_calling": {"type": "boolean"}, + "supports_parallel_tool_use_config": {"type": "boolean"}, "supports_pdf_input": {"type": "boolean"}, "supports_prompt_caching": {"type": "boolean"}, "supports_response_schema": {"type": "boolean"}, @@ -851,6 +855,7 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "supports_xhigh_reasoning_effort": {"type": "boolean"}, "supports_max_reasoning_effort": {"type": "boolean"}, "supports_adaptive_thinking": {"type": "boolean"}, + "supports_mid_conversation_system": {"type": "boolean"}, "supports_sampling_params": {"type": "boolean"}, "supports_output_config": {"type": "boolean"}, "supports_speed": {"type": "boolean"}, @@ -858,6 +863,7 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "type": "string", "enum": ["low", "medium", "high", "max", "xhigh"], }, + "bedrock_converse_supports_strict_tools": {"type": "boolean"}, "tpm": {"type": "number"}, "provider_specific_entry": {"type": "object"}, "supported_endpoints": { @@ -869,6 +875,7 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "/v1/embeddings", "/v1/chat/completions", "/v1/completions", + "/v1/messages", "/v1/images/generations", "/v1/realtime", "/v1/realtime/transcription_sessions", @@ -879,7 +886,6 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "/v1/audio/speech", "/v1/ocr", "/vertex_ai/live", - "/v1/realtime/transcription_sessions", ], }, }, @@ -1061,6 +1067,43 @@ def test_get_model_info_gemini(): assert info.get("rpm") is not None, f"{model} does not have rpm" +def test_get_model_info_bedrock_regional_inference_profile_pricing(local_model_cost_map): + """Regression LIT-4056: with the bedrock/ routing prefix (plain, converse/, or + invoke/), the exact regional cost-map entry must win over the region-stripped + base entry, matching the unprefixed control form.""" + regional = litellm.model_cost["au.anthropic.claude-opus-4-8"] + base = litellm.model_cost["anthropic.claude-opus-4-8"] + assert regional["input_cost_per_token"] > base["input_cost_per_token"] + + for model in ( + "bedrock/au.anthropic.claude-opus-4-8", + "bedrock/converse/au.anthropic.claude-opus-4-8", + "bedrock/invoke/au.anthropic.claude-opus-4-8", + ): + info = litellm.get_model_info(model=model) + assert info["key"] == "au.anthropic.claude-opus-4-8", model + assert info["input_cost_per_token"] == regional["input_cost_per_token"], model + assert info["output_cost_per_token"] == regional["output_cost_per_token"], model + + control = litellm.get_model_info(model="au.anthropic.claude-opus-4-8", custom_llm_provider="bedrock") + assert control["key"] == "au.anthropic.claude-opus-4-8" + + +def test_get_model_info_bedrock_regional_profile_without_entry_falls_back_to_base(local_model_cost_map): + """A regional profile with no dedicated cost-map entry must still resolve to its + region-stripped base entry.""" + assert "apac.anthropic.claude-opus-4-8" not in litellm.model_cost + info = litellm.get_model_info(model="bedrock/apac.anthropic.claude-opus-4-8") + assert info["key"] == "anthropic.claude-opus-4-8" + + +def test_get_model_info_bedrock_double_provider_prefix_resolves(local_model_cost_map): + """A doubled bedrock/ prefix routes at runtime via strip_bedrock_routing_prefix, + so model info must resolve it to the same entry the request actually bills as.""" + info = litellm.get_model_info(model="bedrock/bedrock/us.anthropic.claude-sonnet-4-6") + assert info["key"] == "us.anthropic.claude-sonnet-4-6" + + def test_openai_models_in_model_info(): os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" litellm.model_cost = litellm.get_model_cost_map(url="") @@ -1345,6 +1388,48 @@ def test_pre_process_non_default_params(model, custom_llm_provider): } +@pytest.mark.parametrize( + "custom_llm_provider, expected", + [ + ("vertex_ai", True), + ("vertex_ai_beta", True), + ("gdc", True), + ("openai", False), + ("bedrock", False), + ("not_a_real_provider", False), + ], +) +def test_provider_supports_vertex_params(custom_llm_provider, expected): + from litellm.utils import _provider_supports_vertex_params + + assert _provider_supports_vertex_params(custom_llm_provider) is expected + + +@pytest.mark.parametrize( + "model, custom_llm_provider, should_keep", + [ + ("gemini-2.5-pro", "vertex_ai", True), + ("gemini-2.5-pro", "vertex_ai_beta", True), + ("gdc/gemini-2.5-flash", "gdc", True), + ("gpt-4o", "openai", False), + ], +) +def test_vertex_params_not_stripped_for_vertex_family( + model, custom_llm_provider, should_keep +): + optional_params = litellm.utils.get_optional_params( + model=model, + custom_llm_provider=custom_llm_provider, + vertex_project="my-project", + vertex_location="us-central1", + ) + assert ("vertex_project" in optional_params) is should_keep + assert ("vertex_location" in optional_params) is should_keep + if should_keep: + assert optional_params["vertex_project"] == "my-project" + assert optional_params["vertex_location"] == "us-central1" + + from litellm.utils import supports_function_calling @@ -4533,3 +4618,122 @@ def test_aws_bedrock_project_id_excluded_from_bedrock_optional_params(): assert "aws_bedrock_project_id" not in result assert result["aws_region_name"] == "us-east-1" + + +class TestGetOptionalParamsTencent: + """Tests that tencent provider uses TencentChatConfig for parameter mapping.""" + + def test_tencent_supports_thinking_param(self): + """Verify get_optional_params for tencent accepts the 'thinking' param.""" + from unittest.mock import patch + + from litellm.utils import get_optional_params + + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=True, + ): + result = get_optional_params( + model="tencent/deepseek-v4-pro", + custom_llm_provider="tencent", + thinking={"type": "enabled"}, + ) + assert result.get("thinking") == {"type": "enabled"} + + def test_tencent_supports_reasoning_effort(self): + """Verify get_optional_params for tencent converts reasoning_effort to thinking.""" + from unittest.mock import patch + + from litellm.utils import get_optional_params + + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=True, + ): + result = get_optional_params( + model="tencent/deepseek-v4-pro", + custom_llm_provider="tencent", + reasoning_effort="medium", + ) + assert result.get("thinking") == {"type": "enabled"} + + def test_tencent_supported_params_includes_thinking_and_reasoning_effort(self): + """Verify get_supported_openai_params for tencent includes custom params.""" + from unittest.mock import patch + + from litellm.litellm_core_utils.get_supported_openai_params import ( + get_supported_openai_params, + ) + + with patch( + "litellm.llms.tencent.chat.transformation.supports_reasoning", + return_value=True, + ): + params = get_supported_openai_params( + model="tencent/deepseek-v4-pro", + custom_llm_provider="tencent", + ) + assert "thinking" in params + assert "reasoning_effort" in params + + def test_tencent_messages_config_routing(self): + """Verify ProviderConfigManager routes tencent to TencentAnthropicMessagesConfig.""" + import litellm + from litellm.llms.tencent.messages.transformation import ( + TencentAnthropicMessagesConfig, + ) + from litellm.utils import ProviderConfigManager + + config = ProviderConfigManager.get_provider_anthropic_messages_config( + model="deepseek-v4-pro", + provider=litellm.LlmProviders.TENCENT, + ) + assert isinstance(config, TencentAnthropicMessagesConfig) + assert config.custom_llm_provider == "tencent" + + +class TestValidateEnvironmentTencent: + """Tests that validate_environment resolves TENCENT_API_KEY for the tencent provider.""" + + def test_reports_key_present(self): + with patch.dict(os.environ, {"TENCENT_API_KEY": "sk-tencent"}): + result = litellm.validate_environment(model="tencent/deepseek-v4-pro") + + assert result["keys_in_environment"] is True + assert result["missing_keys"] == [] + + def test_reports_key_missing(self): + with patch.dict(os.environ, {}, clear=True): + result = litellm.validate_environment(model="tencent/deepseek-v4-pro") + + assert result["keys_in_environment"] is False + assert "TENCENT_API_KEY" in result["missing_keys"] + + + +@pytest.mark.parametrize( + "model", + [ + "vertex_ai/gemini-2.5-flash-image", + "vertex_ai/gemini-3-pro-image", + "vertex_ai/gemini-3-pro-image-preview", + "vertex_ai/gemini-3.1-flash-image", + "vertex_ai/gemini-3.1-flash-image-preview", + "gemini/gemini-2.5-flash-image", + "gemini/gemini-3-pro-image", + "gemini/gemini-3-pro-image-preview", + "gemini/gemini-3.1-flash-image", + "gemini/gemini-3.1-flash-image-preview", + ], +) +def test_gemini_image_models_do_not_support_reasoning( + model: str, local_model_cost_map: None +) -> None: + assert model in litellm.model_cost, ( + f"{model} is missing from the local model cost map. " + "Add its entry to litellm/model_prices_and_context_window_backup.json." + ) + assert litellm.supports_reasoning(model) is False, ( + f"{model} incorrectly classified as reasoning-capable. " + "Add 'supports_reasoning: false' to its model_cost entry." + ) diff --git a/type-discipline-budget.json b/type-discipline-budget.json index a6588ac89aa..87b4c96e323 100644 --- a/type-discipline-budget.json +++ b/type-discipline-budget.json @@ -1,34 +1,29 @@ { "LIT001": { - "baseline": 21452, - "slack": 2000 + "limit": 23409 }, "LIT002": { - "baseline": 25022, - "slack": 2500 + "limit": 27511 }, "LIT003": { - "baseline": 397, - "slack": 25 + "limit": 292 }, "LIT004": { - "baseline": 2515, - "slack": 50 + "limit": 44 }, "LIT005": { - "baseline": 0, - "slack": 0 + "limit": 0 }, "LIT006": { - "baseline": 1013, - "slack": 100 + "limit": 1113 }, "LIT007": { - "baseline": 0, - "slack": 0 + "limit": 0 }, "LIT008": { - "baseline": 914, - "slack": 90 + "limit": 1004 + }, + "LIT009": { + "limit": 2501 } } diff --git a/ui/litellm-dashboard/build_ui.sh b/ui/litellm-dashboard/build_ui.sh index b59301233ec..a3ab475dc58 100755 --- a/ui/litellm-dashboard/build_ui.sh +++ b/ui/litellm-dashboard/build_ui.sh @@ -31,9 +31,6 @@ if [ $? -ne 0 ]; then exit 1 fi -# print contents of ui_colors.json -echo "Contents of ui_colors.json:" -cat ui_colors.json # Run npm build npm run build @@ -49,8 +46,7 @@ if [ $? -eq 0 ]; then # Specify the destination directory destination_dir="../../litellm/proxy/_experimental/out" - # Ensure the destination directory exists, then clear it - mkdir -p "$destination_dir" + # Remove existing files in the destination directory rm -rf "$destination_dir"/* # Copy the contents of the output directory to the specified destination diff --git a/ui/litellm-dashboard/build_ui_custom_path.sh b/ui/litellm-dashboard/build_ui_custom_path.sh index 93d8c080b76..a92927f8ea7 100755 --- a/ui/litellm-dashboard/build_ui_custom_path.sh +++ b/ui/litellm-dashboard/build_ui_custom_path.sh @@ -55,8 +55,7 @@ if [ $? -eq 0 ]; then # Specify the destination directory destination_dir="../../litellm/proxy/_experimental/out" - # Ensure the destination directory exists, then clear it - mkdir -p "$destination_dir" + # Remove existing files in the destination directory rm -rf "$destination_dir"/* # Copy the contents of the output directory to the specified destination diff --git a/ui/litellm-dashboard/components.json b/ui/litellm-dashboard/components.json new file mode 100644 index 00000000000..48f5c3d0f37 --- /dev/null +++ b/ui/litellm-dashboard/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-vega", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "gray", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks", + "utils": "@/lib/cva.config" + } +} diff --git a/ui/litellm-dashboard/e2e_tests/migration.serverRootPath.config.ts b/ui/litellm-dashboard/e2e_tests/migration.serverRootPath.config.ts index 205348463c8..d32f59b16bf 100644 --- a/ui/litellm-dashboard/e2e_tests/migration.serverRootPath.config.ts +++ b/ui/litellm-dashboard/e2e_tests/migration.serverRootPath.config.ts @@ -6,14 +6,6 @@ import { defineConfig, devices } from "@playwright/test"; * running. globalSetup logs in at `${SERVER_ROOT_PATH}/ui/login` so the admin * storage state is valid under the prefix. */ -if (!process.env.SERVER_ROOT_PATH) { - throw new Error( - "migration.serverRootPath.config.ts requires SERVER_ROOT_PATH to be set (e.g. SERVER_ROOT_PATH=/litellm). " + - "Without it this config silently re-runs the default mount and never exercises the prefix. " + - "For the root-less run use the default playwright.config.ts (npm run e2e:migration).", - ); -} - export default defineConfig({ testDir: "./tests/migration", testMatch: ["migratedPages.spec.ts"], @@ -34,5 +26,5 @@ export default defineConfig({ projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }], timeout: 3 * 60 * 1000, expect: { timeout: 10 * 1000 }, - globalSetup: require.resolve("./globalSetup"), + globalSetup: require.resolve("./migration.serverRootPath.globalSetup"), }); diff --git a/ui/litellm-dashboard/e2e_tests/migration.serverRootPath.globalSetup.ts b/ui/litellm-dashboard/e2e_tests/migration.serverRootPath.globalSetup.ts new file mode 100644 index 00000000000..d11f49dae74 --- /dev/null +++ b/ui/litellm-dashboard/e2e_tests/migration.serverRootPath.globalSetup.ts @@ -0,0 +1,12 @@ +import globalSetup from "./globalSetup"; + +export default async function migrationServerRootPathGlobalSetup() { + if (!process.env.SERVER_ROOT_PATH) { + throw new Error( + "migration.serverRootPath.config.ts requires SERVER_ROOT_PATH to be set (e.g. SERVER_ROOT_PATH=/litellm). " + + "Without it this config silently re-runs the default mount and never exercises the prefix. " + + "For the root-less run use the default playwright.config.ts (npm run e2e:migration).", + ); + } + await globalSetup(); +} diff --git a/ui/litellm-dashboard/e2e_tests/tests/auth/logout.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/auth/logout.spec.ts index d8644babfe3..351ba91e8d7 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/auth/logout.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/auth/logout.spec.ts @@ -6,24 +6,20 @@ test.describe("Logout", () => { test("Clicking Logout clears the session and forces re-login on a protected page", async ({ page }) => { await page.goto("/ui"); - await expect(page.getByText("Virtual Keys")).toBeVisible({ timeout: 10_000 }); + // Scope to the sidebar; the top-bar breadcrumb also shows "Virtual Keys". + await expect(page.getByRole("complementary").getByText("Virtual Keys")).toBeVisible({ timeout: 10_000 }); - // Open the navbar User dropdown. The trigger button exposes an aria-label - // of "Account menu — — signed in as ", and the antd Dropdown - // is declared with trigger={["click"]}, so a plain click opens the popup. + // Open the sidebar account menu. The trigger button exposes an aria-label + // of "Account menu — — signed in as "; clicking it opens the + // Base UI popover panel. await page.getByRole("button", { name: /Account menu/i }).click(); - const popup = page - .locator(".ant-dropdown:visible") - .filter({ - has: page.locator(".bg-white.rounded-lg.shadow-lg"), - }) - .first(); + const popup = page.getByTestId("sidebar-account-menu-panel"); await expect(popup).toBeVisible({ timeout: 5_000 }); // Click Logout — the handler clears the auth cookie and navigates via // window.location.href = PROXY_LOGOUT_URL (empty string in the e2e env). - await popup.getByText("Logout", { exact: true }).click(); + await popup.getByRole("button", { name: "Logout" }).click(); // The cookie is now gone — visiting a protected page must redirect to /ui/login. await page.goto("/ui?page=llm-playground", { waitUntil: "domcontentloaded" }); diff --git a/ui/litellm-dashboard/e2e_tests/tests/auth/proxyLogoutUrl.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/auth/proxyLogoutUrl.spec.ts index 6358fcf438e..7f6cc6f2f87 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/auth/proxyLogoutUrl.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/auth/proxyLogoutUrl.spec.ts @@ -34,15 +34,16 @@ test.describe("PROXY_LOGOUT_URL redirect", () => { }), ); - // navbar.tsx populates the logout target only after the proxy UI settings - // fetch (/sso/get/ui_settings) resolves. Clicking Logout before that lands + // The logout handler populates the logout target only after the proxy UI + // settings fetch (/sso/get/ui_settings) resolves. Clicking Logout before that lands // runs `window.location.href = ""` — a same-origin reload, not a redirect — // so gate the click on the settings response, not just on first paint. const settingsLoaded = page.waitForResponse((r) => r.url().includes("/sso/get/ui_settings") && r.ok(), { timeout: 30_000, }); await page.goto("/ui"); - await expect(page.getByText("Virtual Keys")).toBeVisible({ timeout: 15_000 }); + // Scope to the sidebar; the top-bar breadcrumb also shows "Virtual Keys". + await expect(page.getByRole("complementary").getByText("Virtual Keys")).toBeVisible({ timeout: 15_000 }); await settingsLoaded; // Pre-condition: we start authenticated. The admin storage state carries a @@ -50,10 +51,10 @@ test.describe("PROXY_LOGOUT_URL redirect", () => { const tokensBefore = (await page.context().cookies()).filter((c) => c.name === "token"); expect(tokensBefore.length, "should start logged in with a token cookie").toBeGreaterThan(0); - // Open the navbar account dropdown (trigger=click) and click Logout by role - // rather than internal Ant Design CSS classes, which are not a stable API. + // Open the sidebar account menu and click Logout by role rather than by + // internal CSS classes, which are not a stable API. await page.getByRole("button", { name: /^Account menu/ }).click(); - const logout = page.getByRole("menuitem", { name: "Logout" }); + const logout = page.getByRole("button", { name: "Logout" }); await expect(logout).toBeVisible({ timeout: 5_000 }); // handleLogout clears cookies/local storage, then assigns window.location.href. diff --git a/ui/litellm-dashboard/e2e_tests/tests/internal-user/internalUserNoTeam.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/internal-user/internalUserNoTeam.spec.ts index 548639d6877..92e46d6b27c 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/internal-user/internalUserNoTeam.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/internal-user/internalUserNoTeam.spec.ts @@ -16,7 +16,8 @@ test.describe("Internal User with no team memberships", () => { await page.getByPlaceholder("Enter your username").fill("noteam@test.local"); await page.getByPlaceholder("Enter your password").fill("test"); await page.getByRole("button", { name: "Login", exact: true }).click(); - await expect(page.getByText("Virtual Keys")).toBeVisible({ timeout: 15_000 }); + // Scope to the sidebar; the top-bar breadcrumb also shows "Virtual Keys". + await expect(page.getByRole("complementary").getByText("Virtual Keys")).toBeVisible({ timeout: 15_000 }); await dismissFeedbackPopup(page); // Open the Create Key modal. diff --git a/ui/litellm-dashboard/e2e_tests/tests/login/internalUserIdentity.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/login/internalUserIdentity.spec.ts index 6008049a2aa..101b91daeb1 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/login/internalUserIdentity.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/login/internalUserIdentity.spec.ts @@ -14,7 +14,8 @@ test.describe("Navbar identity scoping", () => { test("Internal user navbar dropdown shows their own role and user id, not the admin's", async ({ page }) => { await page.goto("/ui"); - await expect(page.getByText("Virtual Keys")).toBeVisible({ timeout: 10_000 }); + // Scope to the sidebar; the top-bar breadcrumb also shows "Virtual Keys". + await expect(page.getByRole("complementary").getByText("Virtual Keys")).toBeVisible({ timeout: 10_000 }); // The account menu button carries the user's role and email/id in its // aria-label (see UserDropdown.tsx). Match by partial role. @@ -26,13 +27,13 @@ test.describe("Navbar identity scoping", () => { { timeout: 5_000 }, ); - // Open the dropdown (UserDropdown configures trigger=["click"]). + // Open the account menu (click to open the Base UI popover). await accountButton.click(); - // Locate the panel by its test id (data-testid on the popupRender div in - // UserDropdown.tsx) rather than Ant/Tailwind class names, so styling - // refactors don't silently break the identity-scoping assertions below. - const popup = page.getByTestId("user-dropdown-panel"); + // Locate the panel by its test id (data-testid on SidebarAccountMenu's + // popover content) rather than class names, so styling refactors don't + // silently break the identity-scoping assertions below. + const popup = page.getByTestId("sidebar-account-menu-panel"); await expect(popup).toBeVisible({ timeout: 5_000 }); // The popup must show the internal user's identity — not the seeded diff --git a/ui/litellm-dashboard/e2e_tests/tests/login/login.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/login/login.spec.ts index d1b64f37156..88378df36c3 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/login/login.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/login/login.spec.ts @@ -9,24 +9,17 @@ test("user can log in", async ({ page }) => { const loginButton = page.getByRole("button", { name: "Login", exact: true }); await expect(loginButton).toBeEnabled(); await loginButton.click(); - await expect(page.getByText("Virtual Keys")).toBeVisible(); + // Scope to the sidebar; the top-bar breadcrumb also shows "Virtual Keys". + await expect(page.getByRole("complementary").getByText("Virtual Keys")).toBeVisible(); - // Match the navbar account button by its stable aria-label (UserDropdown.tsx - // emits "Account menu — — signed in as "). Earlier this used - // `hasText: /^User$/`, which never matched the rendered button (text is - // displayName = "Account" for the master-key admin), so the trigger evaluate - // would time out in CI. + // Match the sidebar account button by its stable aria-label + // (SidebarAccountMenu emits "Account menu — — signed in as "; + // displayName is "Account" for the master-key admin, so match on the label). const userTrigger = page.locator('button[aria-label^="Account menu"]').first(); await userTrigger.click(); - // Filter by the popupRender wrapper class to disambiguate from other - // ant-dropdown popups. - const popup = page - .locator(".ant-dropdown:visible") - .filter({ - has: page.locator(".bg-white.rounded-lg.shadow-lg"), - }) - .first(); + // The account menu is a Base UI popover; locate its panel by test id. + const popup = page.getByTestId("sidebar-account-menu-panel"); await expect(popup).toBeVisible({ timeout: 5_000 }); await expect(popup.getByText("Admin", { exact: true })).toBeVisible({ timeout: 5_000 }); await expect(popup.getByText("default_user_id", { exact: true })).toBeVisible({ timeout: 5_000 }); diff --git a/ui/litellm-dashboard/e2e_tests/tests/migration/migratedPages.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/migration/migratedPages.spec.ts index 0a3be326e42..3ad4b217d08 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/migration/migratedPages.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/migration/migratedPages.spec.ts @@ -17,7 +17,11 @@ const ROOT = process.env.SERVER_ROOT_PATH ?? ""; const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const pathRe = (segment: string) => new RegExp(`${esc(ROOT)}/ui/${esc(segment)}/?($|\\?)`); -const virtualKeysLink = (page: Page) => page.getByRole("link", { name: "Virtual Keys", exact: true }); +// Scope nav lookups to the sidebar (a `complementary` landmark). The top bar +// now renders a breadcrumb whose current-page item is also a "Virtual Keys" +// link, so an unscoped locator would match two elements. +const sidebar = (page: Page) => page.getByRole("complementary"); +const virtualKeysLink = (page: Page) => sidebar(page).getByRole("link", { name: "Virtual Keys", exact: true }); /** The dashboard shell is present (sidebar rendered); page didn't 404 / crash. */ async function expectRendered(page: Page) { @@ -26,16 +30,21 @@ async function expectRendered(page: Page) { /** * Click a migrated page's sidebar link. Migrated items render as
; - * nested ones live under collapsible submenus, so expand submenus until the link is clickable. + * nested ones live under collapsible groups whose children only render while the + * group is open, so expand collapsed groups until the link is clickable. */ async function clickSidebar(page: Page, segment: string) { - const link = page.locator(`a[href$="/ui/${segment}"]`).first(); + const link = sidebar(page).locator(`a[href$="/ui/${segment}"]`).first(); for (let i = 0; i < 8 && !(await link.isVisible().catch(() => false)); i++) { - const collapsedSubmenu = page - .locator(".ant-menu-submenu:not(.ant-menu-submenu-open) > .ant-menu-submenu-title") + // A collapsed group is a menu item with a group-toggle button but no + // rendered submenu yet; clicking the toggle expands it. + const collapsedGroup = sidebar(page) + .locator( + '[data-slot="sidebar-menu-item"]:has(> [data-slot="sidebar-menu-button"]):not(:has(> [data-slot="sidebar-menu-sub"])) > [data-slot="sidebar-menu-button"]', + ) .first(); - if (!(await collapsedSubmenu.isVisible().catch(() => false))) break; - await collapsedSubmenu.click(); + if (!(await collapsedGroup.isVisible().catch(() => false))) break; + await collapsedGroup.click(); await page.waitForTimeout(250); } await link.click(); diff --git a/ui/litellm-dashboard/e2e_tests/tests/navigation/sidebar.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/navigation/sidebar.spec.ts index 7ac2e7df39d..7e42d07ae7c 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/navigation/sidebar.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/navigation/sidebar.spec.ts @@ -42,7 +42,9 @@ for (const { role, storage } of roles) { throw new Error(`No page mapping found for menu label: ${buttonLabel}`); } - const tab = page.getByRole("menuitem", { name: buttonLabel }); + // Sidebar items are links inside the `complementary` landmark; scoping + // there avoids the top-bar breadcrumb, which also links the page name. + const tab = page.getByRole("complementary").getByRole("link", { name: buttonLabel }); await expect(tab).toBeVisible(); await tab.click(); diff --git a/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts index 644228c5ff9..a55c19a53de 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts @@ -137,11 +137,10 @@ test.describe("Proxy Admin - Keys", () => { // No team selection — leave team dropdown empty so the key is owned by the admin user // Select models — open the multi-select and pick the all-models meta-option. - // The Create Key modal labels this "All Team Models" even when no team is selected - // (see src/components/organisms/create_key_button.tsx:944), unlike the team/user - // settings screens which use "All Proxy Models". + // With no team selected the modal offers "All Proxy Models"; the team-scoped + // "All Team Models" option only appears once a team is picked. await page.locator(".ant-select-selection-overflow").click(); - await page.locator(".ant-select-dropdown:visible").getByText("All Team Models").click(); + await page.locator(".ant-select-dropdown:visible").getByText("All Proxy Models").click(); await page.keyboard.press("Escape"); await page.getByRole("button", { name: "Create Key", exact: true }).click(); diff --git a/ui/litellm-dashboard/e2e_tests/tests/settings/adminSettings.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/settings/adminSettings.spec.ts index f61532b05a5..c4a14a891d4 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/settings/adminSettings.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/settings/adminSettings.spec.ts @@ -6,8 +6,11 @@ test.describe("Add Model", () => { test("admin settings test", async ({ page }) => { await page.goto("/ui"); - await page.getByRole("menuitem", { name: /Settings/ }).click(); - await page.getByRole("menuitem", { name: /Admin Settings/ }).click(); + // "Settings" is a collapsible group (button) in the sidebar; expand it, then + // click the "Admin Settings" child link. Scope to the complementary landmark. + const sidebar = page.getByRole("complementary"); + await sidebar.getByRole("button", { name: /Settings/ }).click(); + await sidebar.getByRole("link", { name: /Admin Settings/ }).click(); await page.getByRole("tab", { name: "UI Settings" }).click(); await expect(page.getByText("Configuration for UI-specific")).toBeVisible(); }); diff --git a/ui/litellm-dashboard/e2e_tests/tests/settings/routerSettings.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/settings/routerSettings.spec.ts index 98b86ec9b11..3e140b9ab56 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/settings/routerSettings.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/settings/routerSettings.spec.ts @@ -3,6 +3,14 @@ import { ADMIN_STORAGE_PATH } from "../../constants"; import { navigateToPage } from "../../helpers/navigation"; import { Page } from "../../fixtures/pages"; import { Role, users } from "../../fixtures/users"; +// Type-only import of the OpenAPI-generated backend schema, erased at runtime by +// esbuild. It types the round-trips below so mistakes surface in the editor; the live +// test against the real proxy is what actually enforces the contract. +import type { components } from "../../../src/lib/http/schema"; + +// These tests mutate the proxy's shared router_settings, and the Loadbalancing save +// echoes the whole settings object, so they must not run concurrently. +test.describe.configure({ mode: "serial" }); const PRIMARY = "fake-openai-gpt-4"; const FALLBACK = "fake-anthropic-claude"; @@ -99,3 +107,84 @@ test.describe("Router Settings - Fallbacks", () => { await expect(newRow).toHaveCount(1, { timeout: 10_000 }); }); }); + +type ConfigYAML = components["schemas"]["ConfigYAML"]; +type RouterSettingsResponse = components["schemas"]["RouterSettingsResponse"]; + +const BASE_URL = "http://localhost:4000"; +const ADMIN_AUTH = { Authorization: `Bearer ${users[Role.ProxyAdmin].password}` }; + +/** + * Apply a router_settings patch through the typed /config/update contract. The + * server merges it over existing settings (request wins), so only the passed keys + * change. Fails loudly if the write is rejected instead of leaving a silent bad seed. + */ +async function patchRouterSettings( + request: import("@playwright/test").APIRequestContext, + patch: Partial>, +) { + const res = await request.post(`${BASE_URL}/config/update`, { + headers: ADMIN_AUTH, + data: { router_settings: patch }, + }); + expect(res.ok(), `seed /config/update failed: ${res.status()} ${await res.text()}`).toBeTruthy(); +} + +test.describe("Router Settings - Loadbalancing", () => { + test.use({ storageState: ADMIN_STORAGE_PATH }); + + // Pin num_retries and an empty routing_groups so the assertions are deterministic. + // Empty already reproduces LIT-4057: the old tab serialized [] to the string "[]" + // and the save 422'd. + test.beforeEach(async ({ request }) => { + await patchRouterSettings(request, { num_retries: 3, routing_groups: [] }); + }); + + test.afterEach(async ({ request }) => { + await patchRouterSettings(request, { num_retries: 3 }); + }); + + test("saves the Loadbalancing tab without a 422 when routing_groups is present, and persists", async ({ + page, + request, + }) => { + await navigateToPage(page, Page.RouterSettings); + await page.getByRole("tab", { name: "Loadbalancing" }).click(); + + const numRetries = page.locator('input[name="num_retries"]'); + await expect(numRetries).toHaveValue("3", { timeout: 15_000 }); + // routing_groups belongs to its own tab and must not leak into this form. + await expect(page.locator('input[name="routing_groups"]')).toHaveCount(0); + + await numRetries.fill("5"); + + // LIT-4057: the tab used to serialize routing_groups as the string "[]", + // which the backend rejects with 422 while the UI still claimed success. + // Assert the save actually succeeds at the network level. + const saveResponse = page.waitForResponse( + (res) => res.url().includes("/config/update") && res.request().method() === "POST", + { timeout: 15_000 }, + ); + await page.getByRole("button", { name: /save changes/i }).click(); + expect((await saveResponse).status()).toBe(200); + + await expect(page.getByText(/router settings updated successfully/i).first()).toBeVisible({ timeout: 10_000 }); + + // The ticket's core symptom was that a refresh showed the old value. + await navigateToPage(page, Page.RouterSettings); + await page.getByRole("tab", { name: "Loadbalancing" }).click(); + await expect(page.locator('input[name="num_retries"]')).toHaveValue("5", { timeout: 15_000 }); + + // The typed backend read agrees the change persisted. + await expect + .poll( + async () => { + const res = await request.get(`${BASE_URL}/router/settings`, { headers: ADMIN_AUTH }); + const data = (await res.json()) as RouterSettingsResponse; + return data.current_values?.num_retries; + }, + { timeout: 10_000 }, + ) + .toBe(5); + }); +}); diff --git a/ui/litellm-dashboard/eslint-budgets.json b/ui/litellm-dashboard/eslint-budgets.json index 2139d177512..f08e1bb6160 100644 --- a/ui/litellm-dashboard/eslint-budgets.json +++ b/ui/litellm-dashboard/eslint-budgets.json @@ -1,5 +1,8 @@ { "@typescript-eslint/no-explicit-any": { "max": 2040, "target": 1500 }, + "no-console": { "max": 484, "target": 0 }, "complexity": { "max": 140, "target": 80 }, - "max-depth": { "max": 70, "target": 30 } + "max-depth": { "max": 70, "target": 30 }, + "local/no-large-inline-object-arg": { "max": 560, "target": 300 }, + "local/no-long-condition-chain": { "max": 265, "target": 120 } } diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json deleted file mode 100644 index deef1136b43..00000000000 --- a/ui/litellm-dashboard/eslint-metrics.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "@typescript-eslint/no-explicit-any": 2016, - "complexity": 127, - "max-depth": 61 -} diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index e3ae304c41c..3f6f4da25d0 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -1,7 +1,358 @@ { - "src/app/(dashboard)/api-reference/APIReferenceView.tsx": { + "scripts/check-lint-budgets.mjs": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx": { "no-restricted-imports": { "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/agents/_components/AgentsPanel.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/agents/_components/add_agent_form.tsx": { + "no-nested-ternary": { + "count": 3 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 2 + }, + "unused-imports/no-unused-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/agents/_components/agent_card_discovery.tsx": { + "react-hooks/refs": { + "count": 3 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/agents/_components/agent_cost_view.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/agents/_components/agent_form_fields.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/app/(dashboard)/agents/_components/agent_info.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 1 + } + }, + "src/app/(dashboard)/agents/_components/agent_virtual_keys.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/app/(dashboard)/agents/_components/dynamic_agent_form_fields.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, + "src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/budgets/_components/budget_modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/budgets/_components/budget_panel.test.tsx": { + "unused-imports/no-unused-imports": { + "count": 2 + } + }, + "src/app/(dashboard)/budgets/_components/budget_panel.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/caching/_components/cache_dashboard.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/purity": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 2 + } + }, + "src/app/(dashboard)/caching/_components/cache_health.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/caching/_components/cache_settings/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/add_margin_form.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/add_provider_form.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.tsx": { + "no-nested-ternary": { + "count": 2 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/how_it_works.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.test.tsx": { + "unused-imports/no-unused-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx": { + "unused-imports/no-unused-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/pricing_calculator/use_multi_cost_estimate.ts": { + "no-restricted-syntax": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/provider_discount_table.test.tsx": { + "unused-imports/no-unused-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/provider_discount_table.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/provider_display_helpers.test.ts": { + "unused-imports/no-unused-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/provider_margin_table.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/cost-tracking/_components/use_discount_config.ts": { + "no-restricted-syntax": { + "count": 2 + } + }, + "src/app/(dashboard)/cost-tracking/_components/use_margin_config.ts": { + "no-restricted-syntax": { + "count": 2 + } + }, + "src/app/(dashboard)/guardrails-monitor/_components/EvaluationSettingsModal.tsx": { + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails-monitor/_components/GuardrailDetail.tsx": { + "no-nested-ternary": { + "count": 3 + } + }, + "src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails-monitor/_components/GuardrailsOverview.tsx": { + "no-nested-ternary": { + "count": 8 + } + }, + "src/app/(dashboard)/guardrails/_components/GuardrailTestPanel.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/GuardrailTestPlayground.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/GuardrailTestResults.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/GuardrailsPanel.tsx": { + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/TeamGuardrailsTab.tsx": { + "no-nested-ternary": { + "count": 2 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/add_guardrail_form.tsx": { + "react-hooks/set-state-in-effect": { + "count": 1 + }, + "react/no-unescaped-entities": { + "count": 2 + } + }, + "src/app/(dashboard)/guardrails/_components/content_filter/CompetitorIntentConfiguration.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/content_filter/ContentCategoryConfiguration.tsx": { + "no-nested-ternary": { + "count": 3 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterDisplay.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterManager.tsx": { + "max-params": { + "count": 2 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeModal.tsx": { + "no-nested-ternary": { + "count": 6 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/edit_guardrail_form.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "no-restricted-syntax": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/guardrail_info.tsx": { + "max-params": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 3 + } + }, + "src/app/(dashboard)/guardrails/_components/guardrail_optional_params.tsx": { + "no-nested-ternary": { + "count": 5 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/guardrail_provider_fields.tsx": { + "no-nested-ternary": { + "count": 5 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/guardrail_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/guardrails/_components/tool_permission/ToolPermissionRulesEditor.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/purity": { + "count": 1 } }, "src/app/(dashboard)/hooks/accessGroups/useAccessGroupDetails.ts": { @@ -164,6 +515,11 @@ "count": 2 } }, + "src/app/(dashboard)/memory/_components/MemoryView.tsx": { + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, "src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx": { "no-restricted-imports": { "count": 1 @@ -194,6 +550,9 @@ } }, "src/app/(dashboard)/models-and-endpoints/components/ModelRetrySettingsTab.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -203,11 +562,679 @@ "count": 1 } }, + "src/app/(dashboard)/old-usage/_components/usage.tsx": { + "no-restricted-imports": { + "count": 2 + }, + "react-hooks/immutability": { + "count": 1 + }, + "react-hooks/purity": { + "count": 1 + } + }, + "src/app/(dashboard)/organizations/_components/organizations.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 2 + } + }, + "src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx": { + "no-nested-ternary": { + "count": 2 + }, + "react-hooks/set-state-in-effect": { + "count": 5 + } + }, + "src/app/(dashboard)/playground/components/chat_ui/ChatImageUtils.test.tsx": { + "max-nested-callbacks": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/components/chat_ui/ChatMessageBubble.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx": { + "no-nested-ternary": { + "count": 7 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 4 + }, + "unused-imports/no-unused-imports": { + "count": 13 + } + }, + "src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-syntax": { + "count": 2 + } + }, + "src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterTool.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/components/chat_ui/RealtimePlayground.tsx": { + "no-nested-ternary": { + "count": 2 + }, + "react-hooks/immutability": { + "count": 2 + }, + "react-hooks/preserve-manual-memoization": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/components/compareUI/CompareUI.tsx": { + "no-nested-ternary": { + "count": 4 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/components/compareUI/components/MessageDisplay.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/components/compareUI/components/ModelSelector.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/components/complianceUI/ComplianceUI.tsx": { + "no-nested-ternary": { + "count": 8 + }, + "react-hooks/preserve-manual-memoization": { + "count": 3 + } + }, + "src/app/(dashboard)/playground/llm_calls/a2a_send_message.tsx": { + "max-params": { + "count": 2 + }, + "no-restricted-syntax": { + "count": 2 + } + }, + "src/app/(dashboard)/playground/llm_calls/anthropic_messages.tsx": { + "max-params": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/llm_calls/audio_speech.tsx": { + "max-params": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/llm_calls/audio_transcriptions.tsx": { + "max-params": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/llm_calls/embeddings_api.tsx": { + "max-params": { + "count": 1 + }, + "no-restricted-syntax": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/llm_calls/fetch_agents.tsx": { + "no-restricted-syntax": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/llm_calls/image_edits.tsx": { + "max-params": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/llm_calls/image_generation.tsx": { + "max-params": { + "count": 1 + } + }, + "src/app/(dashboard)/playground/llm_calls/interactions_api.tsx": { + "max-params": { + "count": 1 + }, + "no-restricted-syntax": { + "count": 1 + } + }, "src/app/(dashboard)/playground/page.tsx": { "no-restricted-imports": { "count": 1 } }, + "src/app/(dashboard)/policies/_components/add_attachment_form.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/add_policy_form.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 2 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/ai_suggestion_modal.tsx": { + "no-nested-ternary": { + "count": 10 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/attachment_table.test.tsx": { + "react/display-name": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/attachment_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/guardrail_selection_modal.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/impact_popover.test.tsx": { + "react/display-name": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/impact_popover.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/index.test.tsx": { + "react/display-name": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/pipeline_flow_builder.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 2 + } + }, + "src/app/(dashboard)/policies/_components/policy_info.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/policy_table.test.tsx": { + "react/display-name": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/policy_table.tsx": { + "no-nested-ternary": { + "count": 2 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/policy_test_panel.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 1 + } + }, + "src/app/(dashboard)/policies/_components/template_parameter_modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/projects/_components/ProjectDetailsPage.tsx": { + "no-nested-ternary": { + "count": 3 + } + }, + "src/app/(dashboard)/projects/_components/ProjectKeysSection.tsx": { + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/projects/_components/ProjectModals/ProjectBaseForm.tsx": { + "react-hooks/set-state-in-effect": { + "count": 2 + } + }, + "src/app/(dashboard)/projects/_components/ProjectsPage.tsx": { + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/add_prompt_form.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/index.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/DeveloperMessageCard.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/ModelConfigCard.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/PromptCodeSnippets.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/PromptEditorHeader.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/PromptMessagesCard.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/PublishModal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/ToolsCard.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/VersionHistorySidePanel.test.tsx": { + "max-nested-callbacks": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/VersionHistorySidePanel.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/MessageInput.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/useConversation.ts": { + "no-restricted-syntax": { + "count": 1 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_info.tsx": { + "no-nested-ternary": { + "count": 3 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 2 + } + }, + "src/app/(dashboard)/prompts/_components/prompt_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/router-settings/_components/general_settings.tsx": { + "no-nested-ternary": { + "count": 3 + }, + "no-restricted-imports": { + "count": 2 + } + }, + "src/app/(dashboard)/search-tools/_components/CreateSearchTools.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/search-tools/_components/SearchToolView.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/search-tools/_components/SearchTools.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/static-components": { + "count": 1 + } + }, + "src/app/(dashboard)/skills/_components/ClaudeCodePluginsPanel.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/skills/_components/add_plugin_form.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/skills/_components/plugin_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/tag-management/_components/TagTable.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/tag-management/_components/components/CreateTagModal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/tag-management/_components/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/tag-management/_components/tag_info.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/transform-request/TransformRequestPanel.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/ui-theme/UIThemeSettings.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "no-restricted-syntax": { + "count": 3 + }, + "react-hooks/immutability": { + "count": 1 + } + }, + "src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageTable.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/usage/_components/components/EntityUsage/SpendByProvider.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/usage/_components/components/UsageAIChatPanel.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "react-hooks/immutability": { + "count": 1 + } + }, + "src/app/(dashboard)/usage/_components/components/UsagePageView.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/purity": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 3 + } + }, + "src/app/(dashboard)/usage/_components/hooks/usePaginatedDailyActivity.ts": { + "react-hooks/refs": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/users/_components/DefaultUserSettings.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/users/_components/edit_user.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/users/_components/user_edit_view.test.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "react/display-name": { + "count": 1 + } + }, + "src/app/(dashboard)/users/_components/user_edit_view.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/users/_components/view_users.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/users/_components/view_users/columns.tsx": { + "max-params": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/users/_components/view_users/table.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/users/_components/view_users/user_info_view.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/vector-stores/_components/CreateVectorStore.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx": { + "no-nested-ternary": { + "count": 2 + }, + "no-restricted-imports": { + "count": 1 + }, + "react/no-unescaped-entities": { + "count": 1 + } + }, + "src/app/(dashboard)/vector-stores/_components/VectorStoreTable.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "src/app/(dashboard)/vector-stores/_components/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/vector-stores/_components/vector_store_info.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/(dashboard)/workflows/WorkflowRuns.tsx": { + "no-nested-ternary": { + "count": 1 + }, + "no-restricted-syntax": { + "count": 3 + }, + "react-hooks/set-state-in-effect": { + "count": 1 + } + }, + "src/app/chat/page.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/login/LoginPage.tsx": { "react-hooks/set-state-in-effect": { "count": 2 @@ -239,6 +1266,9 @@ } }, "src/components/AIHub/ModelHubTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -270,6 +1300,9 @@ } }, "src/components/AIHub/forms/MakeMCPPublicForm.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 }, @@ -285,89 +1318,6 @@ "count": 1 } }, - "src/components/AdminPanel.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/add_margin_form.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/add_provider_form.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/cost_tracking_settings.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/how_it_works.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/pricing_calculator/multi_cost_results.test.tsx": { - "unused-imports/no-unused-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/pricing_calculator/multi_cost_results.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/pricing_calculator/multi_export_dropdown.test.tsx": { - "unused-imports/no-unused-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/pricing_calculator/multi_export_dropdown.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/pricing_calculator/use_multi_cost_estimate.ts": { - "no-restricted-syntax": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/provider_discount_table.test.tsx": { - "unused-imports/no-unused-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/provider_discount_table.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/provider_display_helpers.test.ts": { - "unused-imports/no-unused-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/provider_margin_table.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/cost-tracking/components/use_discount_config.ts": { - "no-restricted-syntax": { - "count": 2 - } - }, - "src/app/(dashboard)/cost-tracking/components/use_margin_config.ts": { - "no-restricted-syntax": { - "count": 2 - } - }, "src/components/CreateUserButton.tsx": { "no-restricted-imports": { "count": 1 @@ -376,17 +1326,18 @@ "count": 1 } }, - "src/components/DefaultUserSettings.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, "src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -419,23 +1370,13 @@ "count": 1 } }, - "src/app/(dashboard)/guardrails-monitor/components/EvaluationSettingsModal.tsx": { - "react-hooks/set-state-in-effect": { + "src/components/GuardrailSettingsView.tsx": { + "no-nested-ternary": { "count": 1 } }, - "src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails-monitor/components/ScoreChart.test.tsx": { - "react/display-name": { - "count": 1 - } - }, - "src/app/(dashboard)/guardrails-monitor/components/ScoreChart.tsx": { - "no-restricted-imports": { + "src/components/GuardrailsMonitor/LogViewer.tsx": { + "no-nested-ternary": { "count": 1 } }, @@ -444,8 +1385,8 @@ "count": 1 } }, - "src/app/(dashboard)/memory/components/MemoryView.tsx": { - "react-hooks/set-state-in-effect": { + "src/components/ModelSelect/PaginatedModelSelect/PaginatedModelSelect.tsx": { + "no-nested-ternary": { "count": 1 } }, @@ -465,6 +1406,9 @@ } }, "src/components/OldTeams.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 }, @@ -472,26 +1416,6 @@ "count": 4 } }, - "src/app/(dashboard)/projects/components/ProjectDetailsPage.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/projects/components/ProjectKeysSection.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/app/(dashboard)/projects/components/ProjectModals/ProjectBaseForm.tsx": { - "react-hooks/set-state-in-effect": { - "count": 2 - } - }, - "src/app/(dashboard)/projects/components/ProjectsPage.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, "src/components/SCIM.tsx": { "no-restricted-imports": { "count": 1 @@ -505,33 +1429,15 @@ "count": 1 } }, - "src/app/(dashboard)/search-tools/_components/CreateSearchTools.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/app/(dashboard)/search-tools/_components/SearchToolTester.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/search-tools/_components/SearchToolView.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/search-tools/_components/SearchTools.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/static-components": { + "src/components/Settings/AdminSettings/HashicorpVault/HashicorpVault.tsx": { + "no-nested-ternary": { "count": 1 } }, "src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterSettings.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -546,6 +1452,11 @@ "count": 1 } }, + "src/components/Settings/AdminSettings/SSOSettings/RedactableField.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Settings/AdminSettings/SSOSettings/SSOSettingsLoadingSkeleton.test.tsx": { "max-nested-callbacks": { "count": 4 @@ -556,7 +1467,15 @@ "count": 2 } }, + "src/components/Settings/AdminSettings/UISettings/UISettings.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -582,12 +1501,20 @@ "count": 1 } }, + "src/components/TeamSSOSettings.test.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/ToolDetail.tsx": { "unused-imports/no-unused-imports": { "count": 2 } }, "src/components/ToolPolicies.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -607,6 +1534,9 @@ } }, "src/components/UsageIndicator.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 }, @@ -614,76 +1544,26 @@ "count": 1 } }, - "src/components/UsagePage/components/EndpointUsage/components/EndpointUsageBarChart.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/UsagePage/components/EndpointUsage/components/EndpointUsageLineChart.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/UsagePage/components/EntityUsage/EntityUsage.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/UsagePage/components/EntityUsage/TopKeyView.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/UsagePage/components/EntityUsage/TopModelView.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/UsagePage/components/KeyModelUsageView.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/UsagePage/components/UsageAIChatPanel.tsx": { - "react-hooks/immutability": { - "count": 1 - } - }, - "src/components/UsagePage/components/UsagePageView.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/purity": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 3 - } - }, - "src/components/UsagePage/hooks/usePaginatedDailyActivity.ts": { - "react-hooks/refs": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, "src/components/VirtualKeysPage/VirtualKeysTable.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } }, "src/components/activity_metrics.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, "src/components/add_model/AddModelForm.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -720,11 +1600,22 @@ } }, "src/components/add_model/litellm_model_name.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, + "src/components/add_model/model_connection_test.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/add_model/provider_specific_fields.tsx": { + "no-nested-ternary": { + "count": 5 + }, "no-restricted-imports": { "count": 1 }, @@ -745,67 +1636,10 @@ "count": 1 } }, - "src/components/agents.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 2 - } - }, - "src/components/agents/add_agent_form.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 2 - }, - "unused-imports/no-unused-imports": { - "count": 1 - } - }, - "src/components/agents/agent_card_discovery.tsx": { - "react-hooks/refs": { - "count": 3 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/agents/agent_cost_view.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/agents/agent_info.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/immutability": { - "count": 1 - } - }, "src/components/alerting/dynamic_form.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/budgets/components/budget_modal.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/budgets/components/budget_panel.test.tsx": { - "unused-imports/no-unused-imports": { - "count": 2 - } - }, - "src/app/(dashboard)/budgets/components/budget_panel.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/budgets/components/edit_budget_modal.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 } @@ -818,48 +1652,31 @@ "count": 1 } }, - "src/app/(dashboard)/caching/components/cache_dashboard.tsx": { - "no-restricted-imports": { + "src/components/chat/KeysPanel.tsx": { + "no-nested-ternary": { "count": 1 - }, - "react-hooks/purity": { + } + }, + "src/components/chat/MCPAppsPanel.tsx": { + "no-nested-ternary": { + "count": 7 + } + }, + "src/components/chat/MCPConnectPicker.tsx": { + "no-nested-ternary": { "count": 1 - }, - "react-hooks/set-state-in-effect": { + } + }, + "src/components/chat/MCPCredentialsTab.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/chat/UsagePanel.tsx": { + "no-nested-ternary": { "count": 2 } }, - "src/app/(dashboard)/caching/components/cache_health.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/caching/components/cache_settings/CacheFieldRenderer.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/caching/components/cache_settings/index.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/claude_code_plugins.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, "src/components/claude_code_plugins/MakeSkillPublicForm.tsx": { "no-restricted-imports": { "count": 1 @@ -868,21 +1685,6 @@ "count": 1 } }, - "src/components/claude_code_plugins/add_plugin_form.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/claude_code_plugins/helpers.test.ts": { - "unused-imports/no-unused-imports": { - "count": 1 - } - }, - "src/components/claude_code_plugins/plugin_table.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/cloudzero_export_modal.tsx": { "no-restricted-imports": { "count": 1 @@ -969,6 +1771,9 @@ } }, "src/components/common_components/chartUtils.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -984,6 +1789,9 @@ } }, "src/components/common_components/simple_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1006,11 +1814,6 @@ "count": 1 } }, - "src/components/edit_user.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/email_events/email_event_settings.tsx": { "no-restricted-imports": { "count": 1 @@ -1024,115 +1827,6 @@ "count": 1 } }, - "src/components/general_settings.tsx": { - "no-restricted-imports": { - "count": 2 - } - }, - "src/components/guardrails.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/GuardrailTestPanel.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/guardrails/GuardrailTestResults.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/guardrails/TeamGuardrailsTab.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/add_guardrail_form.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - }, - "react/no-unescaped-entities": { - "count": 2 - } - }, - "src/components/guardrails/content_filter/CompetitorIntentConfiguration.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/content_filter/ContentCategoryConfiguration.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/content_filter/ContentFilterDisplay.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/guardrails/content_filter/ContentFilterManager.tsx": { - "max-params": { - "count": 2 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/custom_code/CustomCodeModal.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/edit_guardrail_form.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "no-restricted-syntax": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/guardrail_info.tsx": { - "max-params": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 3 - } - }, - "src/components/guardrails/guardrail_optional_params.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/guardrail_provider_fields.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/guardrails/guardrail_table.tsx": { - "no-restricted-imports": { - "count": 2 - } - }, - "src/components/guardrails/tool_permission/ToolPermissionRulesEditor.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/purity": { - "count": 1 - } - }, "src/components/key_team_helpers/key_list.tsx": { "react-hooks/set-state-in-effect": { "count": 1 @@ -1143,6 +1837,19 @@ "count": 1 } }, + "src/components/llm_calls/chat_completion.tsx": { + "max-params": { + "count": 1 + }, + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/llm_calls/responses_api.tsx": { + "max-params": { + "count": 1 + } + }, "src/components/mcp_hub_table_columns.tsx": { "no-restricted-imports": { "count": 1 @@ -1158,22 +1865,30 @@ "count": 1 } }, - "src/components/mcp_tools/MCPLogoSelector.test.tsx": { + "src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.test.tsx": { "unused-imports/no-unused-imports": { "count": 1 } }, - "src/components/mcp_tools/MCPNetworkSettings.tsx": { + "src/app/(dashboard)/mcp-servers/_components/MCPNetworkSettings.tsx": { "react-hooks/immutability": { "count": 2 } }, - "src/components/mcp_tools/MCPSubmissionsTab.tsx": { + "src/app/(dashboard)/mcp-servers/_components/MCPSubmissionsTab.tsx": { "react-hooks/set-state-in-effect": { "count": 1 } }, - "src/components/mcp_tools/MCPToolsetsTab.tsx": { + "src/components/mcp_tools/MCPToolArgumentsForm.tsx": { + "no-nested-ternary": { + "count": 5 + } + }, + "src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1185,21 +1900,30 @@ } }, "src/components/mcp_tools/McpCrudPermissionPanel.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } }, - "src/components/mcp_tools/OAuthFormFields.tsx": { + "src/app/(dashboard)/mcp-servers/_components/OAuthFormFields.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, - "src/components/mcp_tools/OpenAPIQuickPicker.tsx": { + "src/app/(dashboard)/mcp-servers/_components/OpenAPIQuickPicker.tsx": { "react-hooks/set-state-in-effect": { "count": 1 } }, - "src/components/mcp_tools/ToolTestPanel.tsx": { + "src/app/(dashboard)/mcp-servers/_components/ToolTestPanel.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1207,15 +1931,23 @@ "count": 1 } }, - "src/components/mcp_tools/create_mcp_server.tsx": { + "src/app/(dashboard)/mcp-servers/_components/UserEnvVarsModal.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, + "src/app/(dashboard)/mcp-servers/_components/create_mcp_server.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, "react-hooks/set-state-in-effect": { - "count": 5 + "count": 4 } }, - "src/components/mcp_tools/mcp_connect.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_connect.tsx": { "no-restricted-imports": { "count": 1 }, @@ -1223,27 +1955,33 @@ "count": 4 } }, - "src/components/mcp_tools/mcp_connection_status.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } }, - "src/components/mcp_tools/mcp_discovery.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_discovery.tsx": { "react-hooks/set-state-in-effect": { "count": 2 } }, - "src/components/mcp_tools/mcp_server_cost_config.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/components/mcp_tools/mcp_server_cost_display.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/components/mcp_tools/mcp_server_edit.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1254,12 +1992,15 @@ "count": 5 } }, - "src/components/mcp_tools/mcp_server_view.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_server_view.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/components/mcp_tools/mcp_servers.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1267,12 +2008,15 @@ "count": 2 } }, - "src/components/mcp_tools/mcp_tool_configuration.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/components/mcp_tools/mcp_tools.tsx": { + "src/app/(dashboard)/mcp-servers/_components/mcp_tools.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1304,6 +2048,9 @@ } }, "src/components/model_dashboard/HealthCheckComponent.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1312,6 +2059,9 @@ } }, "src/components/model_dashboard/all_models_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1320,11 +2070,17 @@ "max-params": { "count": 1 }, + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } }, "src/components/model_dashboard/table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1348,6 +2104,9 @@ } }, "src/components/model_info_view.tsx": { + "no-nested-ternary": { + "count": 14 + }, "no-restricted-imports": { "count": 1 }, @@ -1356,6 +2115,9 @@ } }, "src/components/molecules/filter.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/use-memo": { "count": 1 } @@ -1372,6 +2134,9 @@ "max-params": { "count": 1 }, + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1385,6 +2150,9 @@ "max-params": { "count": 23 }, + "no-nested-ternary": { + "count": 5 + }, "no-restricted-syntax": { "count": 154 } @@ -1426,11 +2194,6 @@ "count": 1 } }, - "src/components/organizations.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/page_utils.test.ts": { "max-nested-callbacks": { "count": 3 @@ -1460,6 +2223,9 @@ } }, "src/components/permissions/MCPServerPermissions.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } @@ -1469,239 +2235,8 @@ "count": 1 } }, - "src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 2 - } - }, - "src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx": { - "react-hooks/set-state-in-effect": { - "count": 5 - } - }, - "src/app/(dashboard)/playground/components/chat_ui/ChatImageUtils.test.tsx": { - "max-nested-callbacks": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 4 - }, - "unused-imports/no-unused-imports": { - "count": 13 - } - }, - "src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.tsx": { - "no-restricted-syntax": { - "count": 2 - } - }, - "src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterTool.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/components/chat_ui/RealtimePlayground.tsx": { - "react-hooks/immutability": { - "count": 2 - }, - "react-hooks/preserve-manual-memoization": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/components/compareUI/CompareUI.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/components/compareUI/components/ModelSelector.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/components/complianceUI/ComplianceUI.tsx": { - "react-hooks/preserve-manual-memoization": { - "count": 3 - } - }, - "src/app/(dashboard)/playground/llm_calls/a2a_send_message.tsx": { - "max-params": { - "count": 2 - }, - "no-restricted-syntax": { - "count": 2 - } - }, - "src/app/(dashboard)/playground/llm_calls/anthropic_messages.tsx": { - "max-params": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/llm_calls/audio_speech.tsx": { - "max-params": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/llm_calls/audio_transcriptions.tsx": { - "max-params": { - "count": 1 - } - }, - "src/components/llm_calls/chat_completion.tsx": { - "max-params": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/llm_calls/embeddings_api.tsx": { - "max-params": { - "count": 1 - }, - "no-restricted-syntax": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/llm_calls/fetch_agents.tsx": { - "no-restricted-syntax": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/llm_calls/image_edits.tsx": { - "max-params": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/llm_calls/image_generation.tsx": { - "max-params": { - "count": 1 - } - }, - "src/app/(dashboard)/playground/llm_calls/interactions_api.tsx": { - "max-params": { - "count": 1 - }, - "no-restricted-syntax": { - "count": 1 - } - }, - "src/components/llm_calls/responses_api.tsx": { - "max-params": { - "count": 1 - } - }, - "src/components/policies/add_attachment_form.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/immutability": { - "count": 1 - } - }, - "src/components/policies/add_policy_form.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/immutability": { - "count": 2 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/policies/ai_suggestion_modal.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/immutability": { - "count": 1 - } - }, - "src/components/policies/attachment_table.test.tsx": { - "react/display-name": { - "count": 1 - } - }, - "src/components/policies/attachment_table.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/policies/guardrail_selection_modal.tsx": { - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/policies/impact_popover.test.tsx": { - "react/display-name": { - "count": 1 - } - }, - "src/components/policies/impact_popover.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/policies/index.test.tsx": { - "react/display-name": { - "count": 1 - } - }, - "src/components/policies/index.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/policies/pipeline_flow_builder.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 2 - } - }, - "src/components/policies/policy_info.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/policies/policy_table.test.tsx": { - "react/display-name": { - "count": 1 - } - }, - "src/components/policies/policy_table.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/policies/policy_test_panel.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/immutability": { - "count": 1 - } - }, - "src/components/policies/template_parameter_modal.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/immutability": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { + "src/components/policies/PolicySelector.tsx": { + "no-nested-ternary": { "count": 1 } }, @@ -1710,88 +2245,10 @@ "count": 2 } }, - "src/app/(dashboard)/prompts/components/add_prompt_form.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/DeveloperMessageCard.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/ModelConfigCard.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/PromptCodeSnippets.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/PromptEditorHeader.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/PromptMessagesCard.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/PublishModal.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/ToolsCard.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/VersionHistorySidePanel.test.tsx": { - "max-nested-callbacks": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/VersionHistorySidePanel.tsx": { - "react-hooks/immutability": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/conversation_panel/MessageInput.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/conversation_panel/index.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_editor_view/conversation_panel/useConversation.ts": { - "no-restricted-syntax": { - "count": 1 - } - }, - "src/app/(dashboard)/prompts/components/prompt_info.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 2 - } - }, - "src/app/(dashboard)/prompts/components/prompt_table.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/public_model_hub.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1801,12 +2258,25 @@ "count": 1 } }, + "src/components/router_settings/ReliabilityRetriesSection.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/routing_groups/index.tsx": { "react-hooks/preserve-manual-memoization": { "count": 1 } }, + "src/components/search_tools/SearchToolSelector.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/settings.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1834,33 +2304,10 @@ "count": 1 } }, - "src/components/tag_management/TagTable.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/tag_management/components/CreateTagModal.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/tag_management/index.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/tag_management/tag_info.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, "src/components/team/EditMembership.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1871,6 +2318,9 @@ } }, "src/components/team/TeamInfo.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1879,6 +2329,9 @@ } }, "src/components/team/TeamVirtualKeysTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1902,6 +2355,9 @@ } }, "src/components/templates/key_edit_view.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1912,6 +2368,9 @@ } }, "src/components/templates/key_info_view.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1919,33 +2378,6 @@ "count": 1 } }, - "src/app/(dashboard)/transform-request/TransformRequestPanel.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/app/(dashboard)/ui-theme/UIThemeSettings.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "no-restricted-syntax": { - "count": 3 - }, - "react-hooks/immutability": { - "count": 1 - } - }, - "src/components/usage.tsx": { - "no-restricted-imports": { - "count": 2 - }, - "react-hooks/immutability": { - "count": 1 - }, - "react-hooks/purity": { - "count": 1 - } - }, "src/components/user_agent_activity.tsx": { "no-restricted-imports": { "count": 2 @@ -1962,59 +2394,38 @@ "count": 2 } }, - "src/components/user_edit_view.test.tsx": { - "react/display-name": { - "count": 1 - } - }, - "src/components/user_edit_view.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/vector_store_management/CreateVectorStore.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/vector_store_management/VectorStoreForm.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react/no-unescaped-entities": { - "count": 1 - } - }, - "src/components/vector_store_management/VectorStoreTable.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/vector_store_management/index.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/vector_store_management/vector_store_info.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { + "src/components/view_logs/EvalViewer/EvalViewer.tsx": { + "no-nested-ternary": { "count": 1 } }, "src/components/view_logs/GuardrailViewer/CompliancePanel.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, + "src/components/view_logs/GuardrailViewer/ContentFilterDetails.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/view_logs/GuardrailViewer/GuardrailViewer.tsx": { + "no-nested-ternary": { + "count": 4 + } + }, + "src/components/view_logs/LogDetailsDrawer/LogDetailContent.tsx": { + "no-nested-ternary": { + "count": 4 + } + }, "src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx": { + "no-nested-ternary": { + "count": 3 + }, "react-hooks/set-state-in-effect": { "count": 2 } @@ -2024,14 +2435,19 @@ "count": 2 } }, + "src/components/view_logs/LogDetailsDrawer/prettyMessagesUtils.ts": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/view_logs/LogDetailsDrawer/useKeyboardNavigation.ts": { "react-hooks/immutability": { "count": 2 } }, - "src/components/view_logs/columns.tsx": { - "no-restricted-imports": { - "count": 1 + "src/components/view_logs/LogsTableToolbar.tsx": { + "no-nested-ternary": { + "count": 4 } }, "src/components/view_logs/index.tsx": { @@ -2043,8 +2459,8 @@ } }, "src/components/view_logs/table.tsx": { - "no-restricted-imports": { - "count": 1 + "no-nested-ternary": { + "count": 2 } }, "src/components/view_user_spend.tsx": { @@ -2052,43 +2468,6 @@ "count": 2 } }, - "src/components/view_users.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/components/view_users/columns.tsx": { - "max-params": { - "count": 1 - }, - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/view_users/table.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/view_users/user_info_view.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, - "src/app/(dashboard)/workflows/WorkflowRuns.tsx": { - "no-restricted-syntax": { - "count": 3 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } - }, "src/contexts/AuthContext.tsx": { "react-hooks/set-state-in-effect": { "count": 1 @@ -2120,6 +2499,9 @@ } }, "src/hooks/useTestMCPConnection.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -2137,19 +2519,14 @@ "count": 1 } }, - "src/utils/dataUtils.test.ts": { - "max-nested-callbacks": { + "src/lib/http/client.ts": { + "no-nested-ternary": { "count": 1 } }, - "tailwind.config.js": { - "@typescript-eslint/no-require-imports": { - "count": 4 - } - }, - "tailwind.config.ts": { - "@typescript-eslint/no-require-imports": { - "count": 3 + "src/utils/dataUtils.test.ts": { + "max-nested-callbacks": { + "count": 1 } }, "tests/CreateKeyPage.expiredToken.test.tsx": { @@ -2167,13 +2544,5 @@ "react/display-name": { "count": 1 } - }, - "src/app/(dashboard)/prompts/components/index.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "react-hooks/set-state-in-effect": { - "count": 1 - } } } diff --git a/ui/litellm-dashboard/eslint.config.mjs b/ui/litellm-dashboard/eslint.config.mjs index 10caebb5196..0cf5b4ff655 100644 --- a/ui/litellm-dashboard/eslint.config.mjs +++ b/ui/litellm-dashboard/eslint.config.mjs @@ -3,6 +3,7 @@ import tseslint from "typescript-eslint"; import nextCoreWebVitals from "eslint-config-next/core-web-vitals"; import prettier from "eslint-config-prettier/flat"; import unusedImports from "eslint-plugin-unused-imports"; +import local from "./scripts/eslint-rules/index.mjs"; const eslintConfig = [ { @@ -13,10 +14,13 @@ const eslintConfig = [ ...nextCoreWebVitals, prettier, { - plugins: { "unused-imports": unusedImports }, + plugins: { "unused-imports": unusedImports, local }, rules: { "unused-imports/no-unused-imports": "error", + "local/no-large-inline-object-arg": "warn", + "local/no-long-condition-chain": "warn", "@typescript-eslint/no-explicit-any": "warn", + "no-console": ["warn", { allow: ["warn", "error"] }], "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-expressions": "off", "@typescript-eslint/ban-ts-comment": "off", @@ -27,6 +31,7 @@ const eslintConfig = [ "no-useless-escape": "off", "no-self-assign": "error", "no-var": "error", + "no-nested-ternary": "error", "react/no-danger": "error", complexity: ["warn", 20], "max-depth": ["warn", 4], diff --git a/ui/litellm-dashboard/knip.json b/ui/litellm-dashboard/knip.json index 6f129398981..afed6b0f90e 100644 --- a/ui/litellm-dashboard/knip.json +++ b/ui/litellm-dashboard/knip.json @@ -1,11 +1,40 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", - "entry": ["scripts/**/*.{ts,mjs}"], + "entry": ["scripts/**/*.{ts,mjs}", "src/components/ui/**/*.{ts,tsx}"], "project": ["src/**/*.{ts,tsx}", "tests/**/*.{ts,tsx}", "scripts/**/*.{ts,mjs}", "e2e_tests/**/*.ts"], "ignore": ["src/lib/http/schema.d.ts"], - "ignoreDependencies": ["openapi-typescript"], + "ignoreDependencies": [ + "openapi-typescript", + "@headlessui/tailwindcss", + "@tailwindcss/forms", + "tailwindcss", + "tw-animate-css" + ], "playwright": { - "config": "e2e_tests/playwright.config.ts", + "config": [ + "e2e_tests/playwright.config.ts", + "e2e_tests/serverRootPath.config.ts", + "e2e_tests/migration.serverRootPath.config.ts" + ], "entry": ["e2e_tests/**/*.spec.ts", "e2e_tests/**/*.setup.ts", "e2e_tests/globalSetup.ts"] + }, + "vitest": { + "config": ["vitest.config.ts"] + }, + "rules": { + "files": "error", + "dependencies": "error", + "devDependencies": "error", + "optionalPeerDependencies": "error", + "unlisted": "error", + "binaries": "error", + "unresolved": "error", + "exports": "warn", + "nsExports": "warn", + "types": "warn", + "nsTypes": "warn", + "enumMembers": "warn", + "classMembers": "warn", + "duplicates": "warn" } } diff --git a/ui/litellm-dashboard/next.config.mjs b/ui/litellm-dashboard/next.config.mjs index 19a2ca298fe..876df2b49cf 100644 --- a/ui/litellm-dashboard/next.config.mjs +++ b/ui/litellm-dashboard/next.config.mjs @@ -7,6 +7,9 @@ const __dirname = path.dirname(__filename); const nextConfig = { output: "export", + compiler: { + removeConsole: process.env.NODE_ENV === "production" ? { exclude: ["error", "warn"] } : false, + }, // Required with output: "export" — default image optimizer runs only in server mode. // See https://nextjs.org/docs/messages/export-image-api images: { diff --git a/ui/litellm-dashboard/package-lock.json b/ui/litellm-dashboard/package-lock.json index 2afc145d15b..b8f6265441b 100644 --- a/ui/litellm-dashboard/package-lock.json +++ b/ui/litellm-dashboard/package-lock.json @@ -8,7 +8,10 @@ "name": "litellm-dashboard", "version": "0.1.0", "dependencies": { + "@ant-design/cssinjs": "1.24.0", + "@ant-design/icons": "5.6.1", "@anthropic-ai/sdk": "0.92.0", + "@base-ui/react": "^1.6.0", "@headlessui/tailwindcss": "0.2.2", "@heroicons/react": "1.0.6", "@tanstack/react-pacer": "0.2.0", @@ -18,12 +21,15 @@ "@types/papaparse": "5.5.2", "antd": "5.29.3", "cva": "1.0.0-beta.4", + "date-fns": "^4.4.0", "dayjs": "1.11.19", "jwt-decode": "4.0.0", "lucide-react": "0.513.0", "moment": "2.30.1", "next": "16.2.6", "openai": "4.104.0", + "openapi-fetch": "^0.17.0", + "openapi-react-query": "^0.5.4", "papaparse": "5.5.3", "react": "18.3.1", "react-copy-to-clipboard": "5.1.1", @@ -31,6 +37,7 @@ "react-json-view-lite": "2.5.0", "react-markdown": "9.1.0", "react-syntax-highlighter": "15.6.6", + "recharts": "3.9.2", "remark-gfm": "4.0.1", "tailwind-merge": "3.4.0", "uuid": "14.0.0" @@ -39,6 +46,7 @@ "@eslint/js": "9.39.2", "@playwright/test": "1.58.1", "@tailwindcss/forms": "0.5.11", + "@tailwindcss/postcss": "4.3.2", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.9.1", "@testing-library/react": "16.3.2", @@ -51,7 +59,6 @@ "@types/react-syntax-highlighter": "15.5.13", "@vitest/coverage-v8": "3.2.6", "@vitest/ui": "3.2.6", - "autoprefixer": "10.4.24", "eslint": "9.39.2", "eslint-config-next": "16.2.6", "eslint-config-prettier": "10.1.8", @@ -61,7 +68,8 @@ "openapi-typescript": "7.13.0", "postcss": "8.5.13", "prettier": "3.2.5", - "tailwindcss": "3.4.19", + "tailwindcss": "4.3.2", + "tw-animate-css": "1.4.0", "typescript": "5.9.3", "typescript-eslint": "8.60.1", "vitest": "3.2.6" @@ -89,6 +97,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -555,6 +564,79 @@ "node": ">=6.9.0" } }, + "node_modules/@base-ui/react": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.6.0.tgz", + "integrity": "sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@base-ui/utils": "0.3.1", + "@floating-ui/react-dom": "^2.1.8", + "@floating-ui/utils": "^0.2.11", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@date-fns/tz": "^1.2.0", + "@types/react": "^17 || ^18 || ^19", + "date-fns": "^4.0.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@date-fns/tz": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "date-fns": { + "optional": true + } + } + }, + "node_modules/@base-ui/react/node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@base-ui/utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.3.1.tgz", + "integrity": "sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@floating-ui/utils": "^0.2.11", + "reselect": "^5.2.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@bcoe/v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", @@ -2048,6 +2130,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -2069,6 +2152,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2078,12 +2162,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2091,14 +2177,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -2257,6 +2343,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2270,6 +2357,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -2279,6 +2367,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2842,6 +2931,32 @@ "npm": ">=9.5.0" } }, + "node_modules/@reduxjs/toolkit": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz", + "integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.61.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", @@ -3199,10 +3314,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@swc/helpers": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", - "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" @@ -3221,6 +3348,277 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" } }, + "node_modules/@tailwindcss/node": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.2.tgz", + "integrity": "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "5.21.6", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.2.tgz", + "integrity": "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-x64": "4.3.2", + "@tailwindcss/oxide-freebsd-x64": "4.3.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-x64-musl": "4.3.2", + "@tailwindcss/oxide-wasm32-wasi": "4.3.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", + "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", + "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", + "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", + "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", + "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", + "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", + "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", + "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", + "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", + "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", + "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz", + "integrity": "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.2.tgz", + "integrity": "sha512-rjVWYCa7Ngbi5AarT6k8TkxUG3Wl1QKzHdIZVsjZSzf36Jmo2IKZt/NHRAwly8oDkbBOH0YTu+CHuf9jPxMc+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.2", + "@tailwindcss/oxide": "4.3.2", + "postcss": "^8.5.15", + "tailwindcss": "4.3.2" + } + }, "node_modules/@tanstack/pacer": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@tanstack/pacer/-/pacer-0.2.0.tgz", @@ -3448,6 +3846,42 @@ "react-dom": ">=16.6.0" } }, + "node_modules/@tremor/react/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/@tremor/react/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/@tremor/react/node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "deprecated": "1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@tremor/react/node_modules/tailwind-merge": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", @@ -3458,10 +3892,32 @@ "url": "https://github.com/sponsors/dcastil" } }, + "node_modules/@tremor/react/node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -3713,6 +4169,12 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.60.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", @@ -4590,43 +5052,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4868,43 +5293,6 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, - "node_modules/autoprefixer": { - "version": "10.4.24", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", - "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001766", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4983,18 +5371,6 @@ "require-from-string": "^2.0.2" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", @@ -5012,6 +5388,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -5123,15 +5500,6 @@ "node": ">=6" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001791", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", @@ -5253,42 +5621,6 @@ "node": ">= 16" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -5359,15 +5691,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/compute-scroll-into-view": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", @@ -5426,18 +5749,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/cssstyle": { "version": "5.3.7", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz", @@ -5687,9 +5998,9 @@ } }, "node_modules/date-fns": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", - "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", "license": "MIT", "funding": { "type": "github", @@ -5820,8 +6131,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8" } @@ -5839,18 +6150,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -5909,6 +6208,20 @@ "dev": true, "license": "MIT" }, + "node_modules/enhanced-resolve": { + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/entities": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", @@ -6102,6 +6415,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-toolkit": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.49.0.tgz", + "integrity": "sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/esbuild": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", @@ -6665,9 +6988,9 @@ } }, "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, "node_modules/expect-type": { @@ -6694,9 +7017,9 @@ "license": "MIT" }, "node_modules/fast-equals": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", - "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.1.tgz", + "integrity": "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==", "license": "MIT", "engines": { "node": ">=6.0.0" @@ -6750,6 +7073,7 @@ "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -6782,6 +7106,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -6819,6 +7144,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -6940,24 +7266,11 @@ "node": ">= 12.20" } }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -7118,6 +7431,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -7168,6 +7482,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -7483,6 +7804,16 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "11.1.11", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.11.tgz", + "integrity": "sha512-qzXuyXAkPySAGYkfsAwodDPWT8Zm7/Uo5BNt4BjhMhG5WlWyZZ4wQqnWwdS8kjlQ1Cwu6gjw3A6+0gTQwlyYtw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -7641,18 +7972,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", @@ -7697,6 +8016,7 @@ "version": "2.16.2", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.3" @@ -7757,6 +8077,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7802,6 +8123,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -7850,6 +8172,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -8123,10 +8446,10 @@ } }, "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "devOptional": true, + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -8444,23 +8767,266 @@ "node": ">= 0.8.0" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, "engines": { - "node": ">=14" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/antonk52" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, "node_modules/locate-path": { "version": "6.0.0", @@ -8535,9 +9101,9 @@ } }, "node_modules/lru-cache": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", - "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -8913,6 +9479,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -9485,6 +10052,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -9498,6 +10066,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -9608,17 +10177,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, "node_modules/nanoid": { "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", @@ -9713,15 +10271,6 @@ } } }, - "node_modules/next/node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -9820,15 +10369,6 @@ "dev": true, "license": "MIT" }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -9838,15 +10378,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -10005,6 +10536,28 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, + "node_modules/openapi-fetch": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.17.0.tgz", + "integrity": "sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==", + "license": "MIT", + "dependencies": { + "openapi-typescript-helpers": "^0.1.0" + } + }, + "node_modules/openapi-react-query": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/openapi-react-query/-/openapi-react-query-0.5.4.tgz", + "integrity": "sha512-V9lRiozjHot19/BYSgXYoyznDxDJQhEBSdi26+SJ0UqjMANLQhkni4XG+Z7e3Ag7X46ZLMrL9VxYkghU3QvbWg==", + "license": "MIT", + "dependencies": { + "openapi-typescript-helpers": "^0.1.0" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.80.0", + "openapi-fetch": "^0.17.0" + } + }, "node_modules/openapi-typescript": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", @@ -10026,6 +10579,12 @@ "typescript": "^5.x" } }, + "node_modules/openapi-typescript-helpers": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.1.0.tgz", + "integrity": "sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==", + "license": "MIT" + }, "node_modules/openapi-typescript/node_modules/supports-color": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", @@ -10238,6 +10797,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, "license": "MIT" }, "node_modules/path-scurry": { @@ -10281,9 +10841,10 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -10292,24 +10853,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/playwright": { "version": "1.58.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.1.tgz", @@ -10390,155 +10933,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-import/node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -10620,9 +11014,9 @@ "license": "MIT" }, "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", "license": "MIT", "funding": { "type": "github", @@ -10643,6 +11037,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -11348,7 +11743,6 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, "license": "MIT" }, "node_modules/react-json-view-lite": { @@ -11390,6 +11784,29 @@ "react": ">=18" } }, + "node_modules/react-redux": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz", + "integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -11465,60 +11882,34 @@ "react-dom": ">=16.8.0" } }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/recharts": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", - "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.9.2.tgz", + "integrity": "sha512-G4fy+Pk46RaXgwWMh+Nzhyo/lbFAVqXo9gtetlyehe6Ehge9CsgDuOTwQDD+i1+llaLktNBiNq4bhnGlDRXFtw==", "license": "MIT", + "workspaces": [ + "www" + ], "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^11.1.8", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.2.0", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/recharts-scale": { @@ -11530,12 +11921,6 @@ "decimal.js-light": "^2.4.1" } }, - "node_modules/recharts/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -11550,6 +11935,21 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -11777,6 +12177,12 @@ "node": ">=0.10.0" } }, + "node_modules/reselect": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", + "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==", + "license": "MIT" + }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", @@ -11831,6 +12237,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -11886,6 +12293,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -11992,9 +12400,9 @@ } }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "devOptional": true, "license": "ISC", "bin": { @@ -12522,28 +12930,6 @@ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", "license": "MIT" }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12561,6 +12947,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -12593,98 +12980,23 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", - "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.2.tgz", + "integrity": "sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==", + "license": "MIT" }, - "node_modules/tailwindcss/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/tailwindcss/node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tailwindcss/node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/tailwindcss/node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/test-exclude": { @@ -12702,27 +13014,6 @@ "node": ">=18" } }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/throttle-debounce": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", @@ -12756,6 +13047,7 @@ "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -12822,6 +13114,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -12911,12 +13204,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -12936,6 +13223,16 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tw-animate-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -13282,12 +13579,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/uuid": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", @@ -13330,9 +13621,9 @@ } }, "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", "license": "MIT AND ISC", "dependencies": { "@types/d3-array": "^3.0.3", diff --git a/ui/litellm-dashboard/package.json b/ui/litellm-dashboard/package.json index a8948f4be34..a14349c7239 100644 --- a/ui/litellm-dashboard/package.json +++ b/ui/litellm-dashboard/package.json @@ -8,7 +8,6 @@ "build": "next build", "start": "next start", "lint": "eslint .", - "lint:metrics": "node scripts/update-lint-metrics.mjs", "test": "vitest", "test:dot": "vitest --reporter=dot", "test:watch": "vitest -w", @@ -24,7 +23,10 @@ "gen:api": "node scripts/gen-api-types.mjs" }, "dependencies": { + "@ant-design/cssinjs": "1.24.0", + "@ant-design/icons": "5.6.1", "@anthropic-ai/sdk": "0.92.0", + "@base-ui/react": "^1.6.0", "@headlessui/tailwindcss": "0.2.2", "@heroicons/react": "1.0.6", "@tanstack/react-pacer": "0.2.0", @@ -34,12 +36,15 @@ "@types/papaparse": "5.5.2", "antd": "5.29.3", "cva": "1.0.0-beta.4", + "date-fns": "^4.4.0", "dayjs": "1.11.19", "jwt-decode": "4.0.0", "lucide-react": "0.513.0", "moment": "2.30.1", "next": "16.2.6", "openai": "4.104.0", + "openapi-fetch": "^0.17.0", + "openapi-react-query": "^0.5.4", "papaparse": "5.5.3", "react": "18.3.1", "react-copy-to-clipboard": "5.1.1", @@ -47,6 +52,7 @@ "react-json-view-lite": "2.5.0", "react-markdown": "9.1.0", "react-syntax-highlighter": "15.6.6", + "recharts": "3.9.2", "remark-gfm": "4.0.1", "tailwind-merge": "3.4.0", "uuid": "14.0.0" @@ -55,6 +61,7 @@ "@eslint/js": "9.39.2", "@playwright/test": "1.58.1", "@tailwindcss/forms": "0.5.11", + "@tailwindcss/postcss": "4.3.2", "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "6.9.1", "@testing-library/react": "16.3.2", @@ -67,7 +74,6 @@ "@types/react-syntax-highlighter": "15.5.13", "@vitest/coverage-v8": "3.2.6", "@vitest/ui": "3.2.6", - "autoprefixer": "10.4.24", "eslint": "9.39.2", "eslint-config-next": "16.2.6", "eslint-config-prettier": "10.1.8", @@ -77,7 +83,8 @@ "openapi-typescript": "7.13.0", "postcss": "8.5.13", "prettier": "3.2.5", - "tailwindcss": "3.4.19", + "tailwindcss": "4.3.2", + "tw-animate-css": "1.4.0", "typescript": "5.9.3", "typescript-eslint": "8.60.1", "vitest": "3.2.6" @@ -92,7 +99,8 @@ "braces": "3.0.3", "axios": "1.13.6", "postcss": "8.5.13", - "esbuild": "0.28.1" + "esbuild": "0.28.1", + "date-fns": "^4.4.0" }, "engines": { "node": ">=20.9.0", diff --git a/ui/litellm-dashboard/postcss.config.js b/ui/litellm-dashboard/postcss.config.js index 12a703d900d..483f378543c 100644 --- a/ui/litellm-dashboard/postcss.config.js +++ b/ui/litellm-dashboard/postcss.config.js @@ -1,6 +1,5 @@ module.exports = { plugins: { - tailwindcss: {}, - autoprefixer: {}, + "@tailwindcss/postcss": {}, }, }; diff --git a/ui/litellm-dashboard/scripts/check-lint-budgets.mjs b/ui/litellm-dashboard/scripts/check-lint-budgets.mjs index a7aba18ae76..3eec15a3a26 100644 --- a/ui/litellm-dashboard/scripts/check-lint-budgets.mjs +++ b/ui/litellm-dashboard/scripts/check-lint-budgets.mjs @@ -1,18 +1,7 @@ import { readFileSync } from "fs"; -import { countBudgetViolations, findDrift } from "./lint-budget-lib.mjs"; +import { countBudgetViolations } from "./lint-budget-lib.mjs"; -const argv = process.argv.slice(2); -const positional = []; -const flags = {}; -for (let i = 0; i < argv.length; i += 1) { - if (argv[i] === "--check") { - flags.check = argv[(i += 1)]; - } else { - positional.push(argv[i]); - } -} - -const [reportPath, budgetsPath] = positional; +const [reportPath, budgetsPath] = process.argv.slice(2); const report = JSON.parse(readFileSync(reportPath, "utf8")); const budgets = JSON.parse(readFileSync(budgetsPath, "utf8")); const counts = countBudgetViolations(report, budgets); @@ -30,20 +19,4 @@ for (const [rule, { max, target }] of Object.entries(budgets)) { } } -if (flags.check) { - const committed = JSON.parse(readFileSync(flags.check, "utf8")); - const drift = findDrift(committed, counts); - for (const { rule, committed: was, actual } of drift) { - console.error( - `::error::${flags.check} is stale for ${rule}: committed ${was ?? "missing"}, actual ${actual ?? "not a tracked rule"}.`, - ); - } - if (drift.length > 0) { - console.error(`::error::Run \`npm run lint:metrics\` and commit ${flags.check}.`); - failed = true; - } else { - console.log(`${flags.check} is up to date.`); - } -} - process.exit(failed ? 1 : 0); diff --git a/ui/litellm-dashboard/scripts/eslint-rules/index.mjs b/ui/litellm-dashboard/scripts/eslint-rules/index.mjs new file mode 100644 index 00000000000..150ba1d02e9 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/index.mjs @@ -0,0 +1,11 @@ +import noLargeInlineObjectArg from "./no-large-inline-object-arg.mjs"; +import noLongConditionChain from "./no-long-condition-chain.mjs"; + +const plugin = { + rules: { + "no-large-inline-object-arg": noLargeInlineObjectArg, + "no-long-condition-chain": noLongConditionChain, + }, +}; + +export default plugin; diff --git a/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs b/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs new file mode 100644 index 00000000000..5c5ae170e23 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs @@ -0,0 +1,41 @@ +const DEFAULT_MIN_PROPERTIES = 4; + +const isArgumentOf = (node) => { + const parent = node.parent; + if (parent == null) return false; + if (parent.type !== "CallExpression" && parent.type !== "NewExpression") return false; + return parent.arguments.includes(node); +}; + +const rule = { + meta: { + type: "suggestion", + docs: { + description: + "Disallow passing a large object literal inline as a call argument; assign it to a named variable first.", + }, + schema: [ + { + type: "object", + properties: { minProperties: { type: "integer", minimum: 1 } }, + additionalProperties: false, + }, + ], + messages: { + tooLarge: + "Object literal with {{count}} properties passed inline as an argument; assign it to a named variable first.", + }, + }, + create(context) { + const minProperties = context.options[0]?.minProperties ?? DEFAULT_MIN_PROPERTIES; + return { + ObjectExpression(node) { + if (!isArgumentOf(node)) return; + if (node.properties.length < minProperties) return; + context.report({ node, messageId: "tooLarge", data: { count: node.properties.length } }); + }, + }; + }, +}; + +export default rule; diff --git a/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs b/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs new file mode 100644 index 00000000000..638e57442e2 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs @@ -0,0 +1,41 @@ +const DEFAULT_MIN_CONDITIONS = 4; + +const isBooleanLogical = (node) => + node?.type === "LogicalExpression" && (node.operator === "&&" || node.operator === "||"); + +const countConditions = (node) => + isBooleanLogical(node) ? countConditions(node.left) + countConditions(node.right) : 1; + +const rule = { + meta: { + type: "suggestion", + docs: { + description: + "Disallow logical expressions that combine many conditions; extract the condition into a named boolean.", + }, + schema: [ + { + type: "object", + properties: { minConditions: { type: "integer", minimum: 2 } }, + additionalProperties: false, + }, + ], + messages: { + tooMany: "Boolean expression combines {{count}} conditions; extract it into a named variable.", + }, + }, + create(context) { + const minConditions = context.options[0]?.minConditions ?? DEFAULT_MIN_CONDITIONS; + return { + LogicalExpression(node) { + if (!isBooleanLogical(node)) return; + if (isBooleanLogical(node.parent)) return; + const count = countConditions(node); + if (count < minConditions) return; + context.report({ node, messageId: "tooMany", data: { count } }); + }, + }; + }, +}; + +export default rule; diff --git a/ui/litellm-dashboard/scripts/gen-api-types.mjs b/ui/litellm-dashboard/scripts/gen-api-types.mjs index 3c9373ec547..6b9f8581292 100644 --- a/ui/litellm-dashboard/scripts/gen-api-types.mjs +++ b/ui/litellm-dashboard/scripts/gen-api-types.mjs @@ -26,15 +26,26 @@ const python = (process.env.LITELLM_PYTHON ?? "python3").split(" "); // The dashboard calls internal UI routes that the public /openapi.json hides via // include_in_schema=False. Force them in so they get typed here; this mutates a // throwaway interpreter, so the spec the proxy actually serves is unchanged. +// Python 3.13 strips a docstring's common leading indentation at compile time +// while 3.12 keeps it, so the same model yields differently-indented descriptions +// depending on the interpreter — enough to make this output non-reproducible +// across CI and contributors. inspect.cleandoc normalizes every description to one +// canonical form regardless of interpreter, so the generated file is stable. const dumpSpec = [ - "import json, sys", + "import inspect, json, sys", "from litellm.proxy.proxy_server import app", "from fastapi.routing import APIRoute", "for route in app.routes:", " if isinstance(route, APIRoute):", " route.include_in_schema = True", "app.openapi_schema = None", - "with open(sys.argv[1], 'w') as f: json.dump(app.openapi(), f, sort_keys=True)", + "def normalize(node):", + " if isinstance(node, dict):", + " return {k: inspect.cleandoc(v) if k == 'description' and isinstance(v, str) else normalize(v) for k, v in node.items()}", + " if isinstance(node, list):", + " return [normalize(v) for v in node]", + " return node", + "with open(sys.argv[1], 'w') as f: json.dump(normalize(app.openapi()), f, sort_keys=True)", ].join("\n"); try { diff --git a/ui/litellm-dashboard/scripts/lint-budget-lib.mjs b/ui/litellm-dashboard/scripts/lint-budget-lib.mjs index a43305fc4ed..9b32bb0d3a0 100644 --- a/ui/litellm-dashboard/scripts/lint-budget-lib.mjs +++ b/ui/litellm-dashboard/scripts/lint-budget-lib.mjs @@ -13,10 +13,3 @@ export function countBudgetViolations(report, budgets) { .map((rule) => [rule, counts[rule] || 0]), ); } - -export function findDrift(committed, actual) { - const rules = [...new Set([...Object.keys(actual), ...Object.keys(committed)])].sort(); - return rules - .filter((rule) => committed[rule] !== actual[rule]) - .map((rule) => ({ rule, committed: committed[rule] ?? null, actual: actual[rule] ?? null })); -} diff --git a/ui/litellm-dashboard/scripts/update-lint-metrics.mjs b/ui/litellm-dashboard/scripts/update-lint-metrics.mjs deleted file mode 100644 index 16704d1f7a2..00000000000 --- a/ui/litellm-dashboard/scripts/update-lint-metrics.mjs +++ /dev/null @@ -1,25 +0,0 @@ -import { execSync } from "child_process"; -import { mkdtempSync, readFileSync, writeFileSync, rmSync } from "fs"; -import { tmpdir } from "os"; -import { join } from "path"; -import { countBudgetViolations } from "./lint-budget-lib.mjs"; - -const ESLINT_EXIT_LINT_ERRORS = 1; - -const budgets = JSON.parse(readFileSync("eslint-budgets.json", "utf8")); -const dir = mkdtempSync(join(tmpdir(), "litellm-lint-")); -const reportPath = join(dir, "report.json"); - -try { - execSync(`npx eslint . -f json -o "${reportPath}"`, { stdio: "inherit" }); -} catch (err) { - if (err.status !== ESLINT_EXIT_LINT_ERRORS) throw err; -} - -const report = JSON.parse(readFileSync(reportPath, "utf8")); -rmSync(dir, { recursive: true, force: true }); - -const metrics = countBudgetViolations(report, budgets); -writeFileSync("eslint-metrics.json", JSON.stringify(metrics, null, 2) + "\n"); -console.log("Updated eslint-metrics.json"); -console.table(metrics); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsDetailsPage.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsDetailsPage.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsDetailsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsDetailsPage.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupBaseForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupBaseForm.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupCreateModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupCreateModal.tsx similarity index 95% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupCreateModal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupCreateModal.tsx index d0a15dbe975..2103701bb67 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupCreateModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupCreateModal.tsx @@ -38,9 +38,7 @@ export function AccessGroupCreateModal({ visible, onCancel, onSuccess }: AccessG }, }); }) - .catch((info) => { - console.log("Validate Failed:", info); - }); + .catch((info) => {}); }; return ( diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupEditModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupEditModal.tsx similarity index 96% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupEditModal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupEditModal.tsx index 875dd75489a..ec557260aa5 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsModal/AccessGroupEditModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupEditModal.tsx @@ -52,9 +52,7 @@ export function AccessGroupEditModal({ visible, accessGroup, onCancel, onSuccess }, ); }) - .catch((info) => { - console.log("Validate Failed:", info); - }); + .catch((info) => {}); }; return ( diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx similarity index 94% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx index c3dd1d54b32..dbbf4e35900 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/AccessGroupsPage.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsPage.tsx @@ -19,6 +19,7 @@ import { SortState, TableHeaderSortDropdown, } from "@/components/common_components/TableHeaderSortDropdown/TableHeaderSortDropdown"; +import { DateCell, IdCell } from "@/components/shared/table_cells"; import { AccessGroupDetail } from "./AccessGroupsDetailsPage"; import { AccessGroupCreateModal } from "./AccessGroupsModal/AccessGroupCreateModal"; import { AccessGroup } from "./types"; @@ -143,21 +144,7 @@ export function AccessGroupsPage() { header: () => ID, enableSorting: false, size: 170, - cell: ({ row }) => { - const record = row.original; - return ( - - setSelectedGroupId(record.id)} - > - {record.id} - - - ); - }, + cell: ({ row }) => , }, { id: "name", @@ -211,7 +198,7 @@ export function AccessGroupsPage() { header: () => Created, enableSorting: true, sortingFn: "datetime", - cell: ({ getValue }) => new Date(getValue() as string).toLocaleDateString(), + cell: ({ getValue }) => , meta: { responsive: ["lg"] }, }, { @@ -219,7 +206,7 @@ export function AccessGroupsPage() { accessorKey: "updatedAt", header: () => Updated, enableSorting: false, - cell: ({ getValue }) => new Date(getValue() as string).toLocaleDateString(), + cell: ({ getValue }) => , meta: { responsive: ["xl"] }, }, ...(canModify diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/types.ts b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/types.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/access-groups/components/types.ts rename to ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/types.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/page.tsx index ae4712b826e..4e9f7031c6d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { AccessGroupsPage } from "./components/AccessGroupsPage"; +import { AccessGroupsPage } from "./_components/AccessGroupsPage"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function AccessGroups() { diff --git a/ui/litellm-dashboard/src/components/AdminPanel.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.test.tsx similarity index 96% rename from ui/litellm-dashboard/src/components/AdminPanel.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.test.tsx index 7d1d2f46cf1..220db23338e 100644 --- a/ui/litellm-dashboard/src/components/AdminPanel.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.test.tsx @@ -8,34 +8,34 @@ const mockGetAllowedIPs = vi.fn(); const mockAddAllowedIP = vi.fn(); const mockDeleteAllowedIP = vi.fn(); -vi.mock("./networking", () => ({ +vi.mock("@/components/networking", () => ({ getSSOSettings: (...args: unknown[]) => mockGetSSOSettings(...args), getAllowedIPs: (...args: unknown[]) => mockGetAllowedIPs(...args), addAllowedIP: (...args: unknown[]) => mockAddAllowedIP(...args), deleteAllowedIP: (...args: unknown[]) => mockDeleteAllowedIP(...args), })); -vi.mock("./constants", () => ({ +vi.mock("@/components/constants", () => ({ useBaseUrl: () => "http://localhost:4000", })); -vi.mock("./Settings/AdminSettings/SSOSettings/SSOSettings", () => ({ +vi.mock("@/components/Settings/AdminSettings/SSOSettings/SSOSettings", () => ({ default: () =>
SSO Settings
, })); -vi.mock("./Settings/AdminSettings/UISettings/UISettings", () => ({ +vi.mock("@/components/Settings/AdminSettings/UISettings/UISettings", () => ({ default: () =>
UI Settings
, })); -vi.mock("./SCIM", () => ({ +vi.mock("@/components/SCIM", () => ({ default: () =>
SCIM Config
, })); -vi.mock("./SSOModals", () => ({ +vi.mock("@/components/SSOModals", () => ({ default: () =>
SSO Modals
, })); -vi.mock("./UIAccessControlForm", () => ({ +vi.mock("@/components/UIAccessControlForm", () => ({ default: () =>
UI Access Control Form
, })); diff --git a/ui/litellm-dashboard/src/components/AdminPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx similarity index 93% rename from ui/litellm-dashboard/src/components/AdminPanel.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx index 7867c184ed2..611efd6a588 100644 --- a/ui/litellm-dashboard/src/components/AdminPanel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx @@ -16,18 +16,18 @@ import { } from "@tremor/react"; import { Alert, Button as Button2, Form, Input, Modal, Space, Tabs, Typography } from "antd"; import React, { useEffect, useState } from "react"; -import NewBadge from "./common_components/NewBadge"; -import { useBaseUrl } from "./constants"; -import NotificationsManager from "./molecules/notifications_manager"; -import { addAllowedIP, deleteAllowedIP, getAllowedIPs, getSSOSettings } from "./networking"; -import SCIMConfig from "./SCIM"; -import LoggingSettings from "./Settings/AdminSettings/LoggingSettings/LoggingSettings"; -import SSOSettings from "./Settings/AdminSettings/SSOSettings/SSOSettings"; -import UISettings from "./Settings/AdminSettings/UISettings/UISettings"; -import HashicorpVault from "./Settings/AdminSettings/HashicorpVault/HashicorpVault"; -import PluginSettings from "./Settings/AdminSettings/PluginSettings/PluginSettings"; -import SSOModals from "./SSOModals"; -import UIAccessControlForm from "./UIAccessControlForm"; +import NewBadge from "@/components/common_components/NewBadge"; +import { useBaseUrl } from "@/components/constants"; +import NotificationsManager from "@/components/molecules/notifications_manager"; +import { addAllowedIP, deleteAllowedIP, getAllowedIPs, getSSOSettings } from "@/components/networking"; +import SCIMConfig from "@/components/SCIM"; +import LoggingSettings from "@/components/Settings/AdminSettings/LoggingSettings/LoggingSettings"; +import SSOSettings from "@/components/Settings/AdminSettings/SSOSettings/SSOSettings"; +import UISettings from "@/components/Settings/AdminSettings/UISettings/UISettings"; +import HashicorpVault from "@/components/Settings/AdminSettings/HashicorpVault/HashicorpVault"; +import PluginSettings from "@/components/Settings/AdminSettings/PluginSettings/PluginSettings"; +import SSOModals from "@/components/SSOModals"; +import UIAccessControlForm from "@/components/UIAccessControlForm"; const { Title, Paragraph, Text } = Typography; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/page.tsx index aac835b02fc..47076acc9f0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/page.tsx @@ -1,6 +1,6 @@ "use client"; -import AdminPanel from "@/components/AdminPanel"; +import AdminPanel from "./_components/AdminPanel"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySettings"; diff --git a/ui/litellm-dashboard/src/components/agents.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsPanel.test.tsx similarity index 95% rename from ui/litellm-dashboard/src/components/agents.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsPanel.test.tsx index 848b8d5e891..48674f21883 100644 --- a/ui/litellm-dashboard/src/components/agents.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsPanel.test.tsx @@ -1,19 +1,19 @@ import React from "react"; import { render, screen, waitFor, act, fireEvent, within } from "@testing-library/react"; import { describe, it, expect, vi, beforeEach } from "vitest"; -import AgentsPanel from "./agents"; -import * as networking from "./networking"; +import AgentsPanel from "./AgentsPanel"; +import * as networking from "@/components/networking"; -vi.mock("./networking", () => ({ +vi.mock("@/components/networking", () => ({ getAgentsList: vi.fn().mockResolvedValue({ agents: [] }), deleteAgentCall: vi.fn(), })); -vi.mock("./agents/add_agent_form", () => ({ +vi.mock("./add_agent_form", () => ({ default: () =>
, })); -vi.mock("./agents/agent_info", () => ({ +vi.mock("./agent_info", () => ({ default: () =>
, })); diff --git a/ui/litellm-dashboard/src/components/agents.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsPanel.tsx similarity index 84% rename from ui/litellm-dashboard/src/components/agents.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsPanel.tsx index 0d8916942da..84634620426 100644 --- a/ui/litellm-dashboard/src/components/agents.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsPanel.tsx @@ -13,15 +13,15 @@ import { } from "@tremor/react"; import { Modal, Alert, Tooltip, Skeleton, Switch } from "antd"; import { CheckCircleOutlined } from "@ant-design/icons"; -import { getAgentsList, deleteAgentCall } from "./networking"; -import AddAgentForm from "./agents/add_agent_form"; +import { getAgentsList, deleteAgentCall } from "@/components/networking"; +import AddAgentForm from "./add_agent_form"; import { isAdminRole } from "@/utils/roles"; -import AgentInfoView from "./agents/agent_info"; -import NotificationsManager from "./molecules/notifications_manager"; -import { Agent } from "./agents/types"; -import { Team } from "./key_team_helpers/key_list"; -import { formatNumberWithCommas } from "@/utils/dataUtils"; -import TableIconActionButton from "./common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; +import AgentInfoView from "./agent_info"; +import NotificationsManager from "@/components/molecules/notifications_manager"; +import { Agent } from "@/components/agents/types"; +import { Team } from "@/components/key_team_helpers/key_list"; +import { DateCell, IdCell, MoneyCell, StatusBadge } from "@/components/shared/table_cells"; +import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; interface AgentsPanelProps { accessToken: string | null; @@ -193,19 +193,10 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams {agent.agent_name} - - - + setSelectedAgentId(id)} /> - {formatNumberWithCommas(agent.spend, 4)} + @@ -213,13 +204,13 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams - {agent.created_at ? new Date(agent.created_at).toLocaleDateString() : "N/A"} + {(agent.keys?.length ?? 0) > 0 ? ( - Active + ) : ( - Needs Setup + )} {isAdmin && ( diff --git a/ui/litellm-dashboard/src/components/agents/add_agent_form.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/add_agent_form.tsx similarity index 98% rename from ui/litellm-dashboard/src/components/agents/add_agent_form.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/add_agent_form.tsx index 77236c23a24..8ca2b5afe16 100644 --- a/ui/litellm-dashboard/src/components/agents/add_agent_form.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/add_agent_form.tsx @@ -4,7 +4,7 @@ import MessageManager from "@/components/molecules/message_manager"; import { resolveLogoSrc } from "@/lib/assetPaths"; import { Button } from "@tremor/react"; import { CheckCircleFilled, KeyOutlined, RobotOutlined, AppstoreOutlined, InfoCircleOutlined } from "@ant-design/icons"; -import CreatedKeyDisplay from "../shared/CreatedKeyDisplay"; +import CreatedKeyDisplay from "@/components/shared/CreatedKeyDisplay"; import { createAgentCall, getAgentCreateMetadata, @@ -14,19 +14,19 @@ import { keyUpdateCall, modelAvailableCall, AgentCreateInfo, -} from "../networking"; +} from "@/components/networking"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; -import { getModelDisplayName } from "../key_team_helpers/fetch_available_models_team_key"; -import { Team } from "../key_team_helpers/key_list"; -import TeamDropdown from "../common_components/team_dropdown"; +import { getModelDisplayName } from "@/components/key_team_helpers/fetch_available_models_team_key"; +import { Team } from "@/components/key_team_helpers/key_list"; +import TeamDropdown from "@/components/common_components/team_dropdown"; import AgentFormFields from "./agent_form_fields"; import AgentCardDiscovery, { DiscoveredAgentCardSelection } from "./agent_card_discovery"; import { buildDiscoveryRequest, overlayDiscoveredCardParams } from "./agent_discovery_utils"; import DynamicAgentFormFields, { buildDynamicAgentData } from "./dynamic_agent_form_fields"; import { getDefaultFormValues, buildAgentDataFromForm } from "./agent_config"; -import MCPServerSelector from "../mcp_server_management/MCPServerSelector"; -import MCPToolPermissions from "../mcp_server_management/MCPToolPermissions"; -import GuardrailSelector from "../guardrails/GuardrailSelector"; +import MCPServerSelector from "@/components/mcp_server_management/MCPServerSelector"; +import MCPToolPermissions from "@/components/mcp_server_management/MCPToolPermissions"; +import GuardrailSelector from "@/components/guardrails/GuardrailSelector"; const { Step } = Steps; @@ -1010,7 +1010,7 @@ const AddAgentForm: React.FC = ({ visible, onClose, accessTok diff --git a/ui/litellm-dashboard/src/components/agents/agent_card_discovery.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx similarity index 97% rename from ui/litellm-dashboard/src/components/agents/agent_card_discovery.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx index 3a9b7cbb41c..dc70aaa409b 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_card_discovery.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx @@ -2,18 +2,18 @@ import React from "react"; import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { renderWithProviders } from "../../../tests/test-utils"; +import { renderWithProviders } from "@/../tests/test-utils"; import AgentCardDiscovery from "./agent_card_discovery"; -vi.mock("../networking", async () => { - const actual = await vi.importActual("../networking"); +vi.mock("@/components/networking", async () => { + const actual = await vi.importActual("@/components/networking"); return { ...actual, discoverAgentCardCall: vi.fn(), }; }); -import { discoverAgentCardCall } from "../networking"; +import { discoverAgentCardCall } from "@/components/networking"; const mockDiscover = discoverAgentCardCall as unknown as ReturnType; diff --git a/ui/litellm-dashboard/src/components/agents/agent_card_discovery.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.tsx similarity index 98% rename from ui/litellm-dashboard/src/components/agents/agent_card_discovery.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.tsx index 3fa35419fde..5ea7458f643 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_card_discovery.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.tsx @@ -11,7 +11,7 @@ import { SearchOutlined, } from "@ant-design/icons"; -import { DiscoveredAgentCard, discoverAgentCardCall } from "../networking"; +import { DiscoveredAgentCard, discoverAgentCardCall } from "@/components/networking"; import { ALLOWED_CAPABILITY_KEYS, selectionsFromSavedAgentCard, @@ -249,7 +249,7 @@ const AgentCardDiscovery: React.FC = ({ Using the connection details you entered above. We'll fetch: -
+
{discoveryRequest!.display_url || effectiveUrl || ( Fill in the fields above first )} @@ -409,7 +409,7 @@ const AgentCardDiscovery: React.FC = ({ return (
diff --git a/ui/litellm-dashboard/src/components/agents/agent_config.ts b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_config.ts similarity index 100% rename from ui/litellm-dashboard/src/components/agents/agent_config.ts rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_config.ts diff --git a/ui/litellm-dashboard/src/components/agents/agent_cost_view.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_cost_view.tsx similarity index 95% rename from ui/litellm-dashboard/src/components/agents/agent_cost_view.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_cost_view.tsx index 04837fe823f..e5d88ecc721 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_cost_view.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_cost_view.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Title } from "@tremor/react"; import { Descriptions } from "antd"; -import { Agent } from "./types"; +import { Agent } from "@/components/agents/types"; interface AgentCostViewProps { agent: Agent; diff --git a/ui/litellm-dashboard/src/components/agents/agent_discovery_utils.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_discovery_utils.test.ts similarity index 100% rename from ui/litellm-dashboard/src/components/agents/agent_discovery_utils.test.ts rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_discovery_utils.test.ts diff --git a/ui/litellm-dashboard/src/components/agents/agent_discovery_utils.ts b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_discovery_utils.ts similarity index 99% rename from ui/litellm-dashboard/src/components/agents/agent_discovery_utils.ts rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_discovery_utils.ts index 8040bb3089b..fd34ec471eb 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_discovery_utils.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_discovery_utils.ts @@ -1,4 +1,4 @@ -import { AgentCreateInfo, DiscoveredAgentCard, DiscoveryMode } from "../networking"; +import { AgentCreateInfo, DiscoveredAgentCard, DiscoveryMode } from "@/components/networking"; export interface DiscoveryRequestPlan { url: string; diff --git a/ui/litellm-dashboard/src/components/agents/agent_form_fields.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_form_fields.tsx similarity index 100% rename from ui/litellm-dashboard/src/components/agents/agent_form_fields.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_form_fields.tsx diff --git a/ui/litellm-dashboard/src/components/agents/agent_info.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_info.tsx similarity index 98% rename from ui/litellm-dashboard/src/components/agents/agent_info.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_info.tsx index 46ffa9b5ccf..d82ef82df8c 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_info.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_info.tsx @@ -3,11 +3,11 @@ import { Card, Title, Text, Button as TremorButton, Tab, TabGroup, TabList, TabP import { Form, Input, InputNumber, Button as AntButton, Spin, Descriptions, Divider } from "antd"; import MessageManager from "@/components/molecules/message_manager"; import { ArrowLeftIcon } from "@heroicons/react/outline"; -import { getAgentInfo, patchAgentCall, getAgentCreateMetadata, AgentCreateInfo } from "../networking"; -import { Agent } from "./types"; -import { KeyResponse } from "../key_team_helpers/key_list"; +import { getAgentInfo, patchAgentCall, getAgentCreateMetadata, AgentCreateInfo } from "@/components/networking"; +import { Agent } from "@/components/agents/types"; +import { KeyResponse } from "@/components/key_team_helpers/key_list"; import { useKeys } from "@/app/(dashboard)/hooks/keys/useKeys"; -import KeyInfoView from "../templates/key_info_view"; +import KeyInfoView from "@/components/templates/key_info_view"; import AgentVirtualKeys from "./agent_virtual_keys"; import AgentFormFields from "./agent_form_fields"; import DynamicAgentFormFields, { buildDynamicAgentData } from "./dynamic_agent_form_fields"; diff --git a/ui/litellm-dashboard/src/components/agents/agent_type_utils.ts b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_type_utils.ts similarity index 95% rename from ui/litellm-dashboard/src/components/agents/agent_type_utils.ts rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_type_utils.ts index 5bcdd0abe64..f91590c5732 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_type_utils.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_type_utils.ts @@ -1,5 +1,5 @@ -import { Agent } from "./types"; -import { AgentCreateInfo } from "../networking"; +import { Agent } from "@/components/agents/types"; +import { AgentCreateInfo } from "@/components/networking"; /** * Detects the agent type from an agent's litellm_params. diff --git a/ui/litellm-dashboard/src/components/agents/agent_virtual_keys.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_virtual_keys.test.tsx similarity index 93% rename from ui/litellm-dashboard/src/components/agents/agent_virtual_keys.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_virtual_keys.test.tsx index 9a58ad557a2..ce13c22dfbf 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_virtual_keys.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_virtual_keys.test.tsx @@ -2,9 +2,9 @@ import React from "react"; import { describe, it, expect, vi } from "vitest"; import { screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { renderWithProviders } from "../../../tests/test-utils"; +import { renderWithProviders } from "@/../tests/test-utils"; import AgentVirtualKeys from "./agent_virtual_keys"; -import type { KeyResponse } from "../key_team_helpers/key_list"; +import type { KeyResponse } from "@/components/key_team_helpers/key_list"; const makeKey = (overrides: Partial): KeyResponse => ({ diff --git a/ui/litellm-dashboard/src/components/agents/agent_virtual_keys.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_virtual_keys.tsx similarity index 92% rename from ui/litellm-dashboard/src/components/agents/agent_virtual_keys.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_virtual_keys.tsx index afc6344c2ef..9a7001ae941 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_virtual_keys.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_virtual_keys.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Button, Tooltip, Typography } from "antd"; import { KeyOutlined } from "@ant-design/icons"; -import { KeyResponse } from "../key_team_helpers/key_list"; +import { KeyResponse } from "@/components/key_team_helpers/key_list"; const { Title, Text } = Typography; @@ -22,7 +22,7 @@ const AgentVirtualKeys: React.FC = ({ keys, isLoading, on ) : (
{keys.map((key) => ( -
+
{key.key_alias || "Unnamed key"} {key.key_name && {key.key_name}} diff --git a/ui/litellm-dashboard/src/components/agents/cost_config_fields.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/cost_config_fields.tsx similarity index 100% rename from ui/litellm-dashboard/src/components/agents/cost_config_fields.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/cost_config_fields.tsx diff --git a/ui/litellm-dashboard/src/components/agents/dynamic_agent_form_fields.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/dynamic_agent_form_fields.tsx similarity index 98% rename from ui/litellm-dashboard/src/components/agents/dynamic_agent_form_fields.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/agents/_components/dynamic_agent_form_fields.tsx index 3edea4362c9..435e628bfc0 100644 --- a/ui/litellm-dashboard/src/components/agents/dynamic_agent_form_fields.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/dynamic_agent_form_fields.tsx @@ -1,6 +1,6 @@ import React from "react"; import { Form, Input, Select, Collapse } from "antd"; -import { AgentCreateInfo, AgentCredentialFieldMetadata } from "../networking"; +import { AgentCreateInfo, AgentCredentialFieldMetadata } from "@/components/networking"; import { AGENT_FORM_CONFIG } from "./agent_config"; import CostConfigFields from "./cost_config_fields"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/agents/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/page.tsx index d60daae13a7..885c9a36f30 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/agents/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/page.tsx @@ -1,6 +1,6 @@ "use client"; -import AgentsPanel from "@/components/agents"; +import AgentsPanel from "./_components/AgentsPanel"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx similarity index 97% rename from ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx index a73973bd742..66fa0dfa63f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx @@ -2,7 +2,7 @@ import { render } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import APIReferenceView from "./APIReferenceView"; -vi.mock("./components/CodeBlock", () => ({ +vi.mock("@/components/CodeBlock", () => ({ __esModule: true, default: ({ code }: { code: string }) =>
{code}
, })); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx similarity index 97% rename from ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx index 5861cc87e5b..333bd1cad13 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/APIReferenceView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.tsx @@ -1,8 +1,8 @@ "use client"; import React from "react"; import { Text, Tab, TabGroup, TabList, TabPanel, TabPanels, Grid } from "@tremor/react"; -import CodeBlock from "./components/CodeBlock"; -import DocLink from "@/app/(dashboard)/api-reference/components/DocLink"; +import CodeBlock from "@/components/CodeBlock"; +import DocLink from "./DocLink"; interface ApiRefProps { proxySettings: { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/components/DocLink.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/DocLink.tsx similarity index 75% rename from ui/litellm-dashboard/src/app/(dashboard)/api-reference/components/DocLink.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/DocLink.tsx index a3d0416053e..a3b34360246 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/components/DocLink.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/DocLink.tsx @@ -1,9 +1,7 @@ import React from "react"; import { ExternalLink } from "lucide-react"; -function cn(...parts: Array) { - return parts.filter(Boolean).join(" "); -} +import { cn } from "@/lib/cva.config"; export type DocLinkProps = { href?: string; @@ -18,8 +16,8 @@ const DocLink = ({ href, className }: DocLinkProps) => { rel="noopener noreferrer" title="Open documentation in a new tab" className={cn( - "inline-flex items-center gap-2 rounded-xl border border-zinc-200 bg-white/80 px-3.5 py-2 text-sm font-medium text-zinc-700 shadow-sm", - "hover:bg-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 active:translate-y-[0.5px]", + "inline-flex items-center gap-2 rounded-xl border border-zinc-200 bg-white/80 px-3.5 py-2 text-sm font-medium text-zinc-700 shadow-xs", + "hover:bg-white focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-blue-500 active:translate-y-[0.5px]", className, )} > diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx index a4a4d3d0f43..d7c977b0870 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx @@ -1,6 +1,7 @@ "use client"; -import APIReferenceView from "@/app/(dashboard)/api-reference/APIReferenceView"; +import APIReferenceView from "./_components/APIReferenceView"; +import { DeprecationBanner } from "@/components/DeprecationBanner"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySettings"; @@ -8,7 +9,12 @@ const APIReferencePage = () => { const { accessToken } = useAuthorized(); const proxySettings = useProxySettings(accessToken); - return ; + return ( + <> + + + + ); }; export default APIReferencePage; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_modal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_modal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_modal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_modal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx similarity index 96% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx index 0e601645c20..af15a99f0b4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/budget_panel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_panel.tsx @@ -25,6 +25,7 @@ import DeleteResourceModal from "@/components/common_components/DeleteResourceMo import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; import NotificationsManager from "@/components/molecules/notifications_manager"; import { useBudgets, useDeleteBudget, budgetItem } from "@/app/(dashboard)/hooks/budgets/useBudgets"; +import { MoneyCell } from "@/components/shared/table_cells"; import BudgetModal from "./budget_modal"; import EditBudgetModal from "./edit_budget_modal"; import { CREATE_END_USER_CURL_COMMAND, CHAT_COMPLETIONS_CURL_COMMAND, OPENAI_SDK_PYTHON_CODE } from "./constants"; @@ -127,7 +128,9 @@ const BudgetPanel: React.FC = ({ accessToken }) => { .map((value: budgetItem) => ( {value.budget_id} - {value.max_budget ? value.max_budget : "n/a"} + + + {value.tpm_limit ? value.tpm_limit : "n/a"} {value.rpm_limit ? value.rpm_limit : "n/a"} {canModify && ( diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/constants.ts b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/constants.ts similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/constants.ts rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/constants.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/components/edit_budget_modal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/budgets/components/edit_budget_modal.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/page.tsx index 547699411e7..ca34589a679 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/budgets/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/budgets/page.tsx @@ -1,6 +1,6 @@ "use client"; -import BudgetPanel from "./components/budget_panel"; +import BudgetPanel from "./_components/budget_panel"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; export default function Budgets() { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.test.tsx new file mode 100644 index 00000000000..17d14cd7fac --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.test.tsx @@ -0,0 +1,135 @@ +import React from "react"; +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { screen, waitFor, within } from "@testing-library/react"; +import { renderWithProviders } from "../../../../../tests/test-utils"; +import CacheDashboard from "./cache_dashboard"; + +const { adminGlobalCacheActivity, cachingHealthCheckCall } = vi.hoisted(() => ({ + adminGlobalCacheActivity: vi.fn(), + cachingHealthCheckCall: vi.fn(), +})); + +vi.mock("@/components/networking", () => ({ + adminGlobalCacheActivity, + cachingHealthCheckCall, +})); + +const cacheActivity = [ + { + api_key: "sk-1", + model: "gpt-5.1", + call_type: "acompletion", + total_rows: 1500, + cache_hit_true_rows: 300, + cached_completion_tokens: 12000, + generated_completion_tokens: 48000, + }, + { + api_key: "sk-2", + model: "text-embedding-3-large", + call_type: "aembedding", + total_rows: 700, + cache_hit_true_rows: 100, + cached_completion_tokens: 2000, + generated_completion_tokens: 9000, + }, +]; + +const renderDashboard = () => + renderWithProviders( + , + ); + +const findChartCards = async () => { + await screen.findByText("Cache Hits vs API Requests"); + await waitFor(() => { + expect(document.querySelectorAll("path.recharts-rectangle").length).toBeGreaterThan(0); + }); + const cards = Array.from(document.querySelectorAll('[data-slot="card"]')); + expect(cards).toHaveLength(2); + return { requestsCard: cards[0] as HTMLElement, tokensCard: cards[1] as HTMLElement }; +}; + +const barFills = (card: HTMLElement) => + Array.from(card.querySelectorAll(".recharts-bar")).map((bar) => + bar.querySelector("path.recharts-rectangle")?.getAttribute("fill"), + ); + +const legendFillByCategory = (card: HTMLElement) => + Object.fromEntries( + Array.from(card.querySelectorAll('.recharts-legend-wrapper [style*="background-color"]')).map((swatch) => [ + swatch.parentElement?.textContent, + swatch.getAttribute("style")?.match(/background-color:\s*([^;]+);?/)?.[1], + ]), + ); + +describe("CacheDashboard cache analytics charts", () => { + beforeEach(() => { + vi.clearAllMocks(); + adminGlobalCacheActivity.mockResolvedValue(cacheActivity); + }); + + it("renders both chart card titles", async () => { + renderDashboard(); + + expect(await screen.findByText("Cache Hits vs API Requests")).toBeInTheDocument(); + expect(screen.getByText("Cached Completion Tokens vs Generated Completion Tokens")).toBeInTheDocument(); + }); + + it("renders the requests chart with each category legend-bound to its fill and stacked in order", async () => { + renderDashboard(); + const { requestsCard } = await findChartCards(); + + expect(legendFillByCategory(requestsCard)).toEqual({ + "LLM API requests": "var(--color-sky-500, #0ea5e9)", + "Cache hit": "var(--color-teal-500, #14b8a6)", + }); + expect(barFills(requestsCard)).toEqual(["var(--color-sky-500, #0ea5e9)", "var(--color-teal-500, #14b8a6)"]); + }); + + it("renders the tokens chart with each category legend-bound to its fill and stacked in order", async () => { + renderDashboard(); + const { tokensCard } = await findChartCards(); + + expect(legendFillByCategory(tokensCard)).toEqual({ + "Generated Completion Tokens": "var(--color-sky-500, #0ea5e9)", + "Cached Completion Tokens": "var(--color-teal-500, #14b8a6)", + }); + expect(barFills(tokensCard)).toEqual(["var(--color-sky-500, #0ea5e9)", "var(--color-teal-500, #14b8a6)"]); + }); + + it("indexes bars by call_type name on the x axis", async () => { + renderDashboard(); + const { requestsCard, tokensCard } = await findChartCards(); + + for (const card of [requestsCard, tokensCard]) { + expect(within(card).getAllByText("acompletion").length).toBeGreaterThan(0); + expect(within(card).getAllByText("aembedding").length).toBeGreaterThan(0); + } + }); + + it("stacks the two categories into one column per call_type", async () => { + renderDashboard(); + const { requestsCard, tokensCard } = await findChartCards(); + + for (const card of [requestsCard, tokensCard]) { + const rects = Array.from(card.querySelectorAll("path.recharts-rectangle")); + expect(rects).toHaveLength(4); + const xPositions = rects.map((rect) => rect.getAttribute("d")?.split(",")[0]); + expect(new Set(xPositions).size).toBe(2); + } + }); + + it("formats y-axis ticks with compact notation", async () => { + renderDashboard(); + const { requestsCard, tokensCard } = await findChartCards(); + + const compactTicks = (card: HTMLElement) => + within(card) + .getAllByText(/^\d+(\.\d+)?K$/) + .map((tick) => tick.textContent); + + expect(compactTicks(requestsCard).length).toBeGreaterThan(0); + expect(compactTicks(tokensCard)).toContain("60K"); + }); +}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_dashboard.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx similarity index 88% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_dashboard.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx index 99656f0db4a..b8e8dc8adb1 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_dashboard.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx @@ -1,5 +1,4 @@ import { - BarChart, Card, Col, DateRangePickerValue, @@ -7,7 +6,6 @@ import { Icon, MultiSelect, MultiSelectItem, - Subtitle, Tab, TabGroup, TabList, @@ -18,6 +16,8 @@ import { import React, { useEffect, useState } from "react"; import NotificationsManager from "@/components/molecules/notifications_manager"; import UsageDatePicker from "@/components/shared/usage_date_picker"; +import { BarChart } from "@/components/shared/charts"; +import { Card as ChartCard, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { RefreshIcon } from "@heroicons/react/outline"; import { adminGlobalCacheActivity, cachingHealthCheckCall } from "@/components/networking"; @@ -25,6 +25,7 @@ import { adminGlobalCacheActivity, cachingHealthCheckCall } from "@/components/n // Import the new component import { CacheHealthTab } from "./cache_health"; import CacheSettings from "./cache_settings"; +import CoordinationRedisSettings from "./coordination_redis_settings"; const formatDateWithoutTZ = (date: Date | undefined) => { if (!date) return undefined; @@ -61,13 +62,13 @@ interface cacheDataItem { // Add other properties as needed } -interface uiData { +type uiData = { name: string; "LLM API requests": number; "Cache hit": number; "Cached Completion Tokens": number; "Generated Completion Tokens": number; -} +}; interface CacheHealthResponse { status?: string; @@ -150,7 +151,6 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole }; useEffect(() => { - console.log("DATA IN CACHE DASHBOARD", data); let newData: cacheDataItem[] = data; if (selectedApiKeys.length > 0) { newData = newData.filter((item) => selectedApiKeys.includes(item.api_key)); @@ -180,16 +180,11 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole // } // ] - console.log("before processed data in cache dashboard", newData); - let llm_api_requests = 0; let cache_hits = 0; let cached_tokens = 0; const processedData = newData.reduce((acc: uiData[], item) => { - console.log("Processing item:", item); - if (!item.call_type) { - console.log("Item has no call_type:", item); item.call_type = "Unknown"; } @@ -227,8 +222,6 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole } setFilteredData(processedData); - - console.log("PROCESSED DATA IN CACHE DASHBOARD", processedData); }, [selectedApiKeys, selectedModels, dateValue, data]); const handleRefreshClick = () => { @@ -242,7 +235,6 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole NotificationsManager.info("Running cache health check..."); setHealthCheckResponse(""); const response = await cachingHealthCheckCall(accessToken !== null ? accessToken : ""); - console.log("CACHING HEALTH CHECK RESPONSE", response); setHealthCheckResponse(response); } catch (error: any) { console.error("Error running health check:", error); @@ -273,6 +265,7 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole Cache Analytics Cache Health Cache Settings + Coordination Redis
@@ -357,29 +350,41 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole
- Cache Hits vs API Requests - + + + Cache Hits vs API Requests + + + + + - Cached Completion Tokens vs Generated Completion Tokens - + + + + Cached Completion Tokens vs Generated Completion Tokens + + + + + + @@ -392,6 +397,9 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole + + + ); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_health.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx similarity index 98% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_health.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx index 92bbc14846f..887c12a4f3d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_health.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx @@ -151,7 +151,7 @@ const HealthCheckDetails: React.FC<{ response: any }> = ({ response }) => { }; return ( -
+
Summary @@ -225,7 +225,7 @@ const HealthCheckDetails: React.FC<{ response: any }> = ({ response }) => {
-
+              
                 {(() => {
                   try {
                     const data = {
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFieldSection.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFieldSection.tsx
new file mode 100644
index 00000000000..ced822cd796
--- /dev/null
+++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFieldSection.tsx
@@ -0,0 +1,42 @@
+import React from "react";
+import CacheFormField, { EmbeddingModelOption } from "./CacheFormField";
+import { fieldsForSection } from "./cacheSettingsUtils";
+import { CacheSection, RedisType } from "./cacheSettingsFields";
+
+interface CacheFieldSectionProps {
+  title: string;
+  section: CacheSection;
+  redisType: RedisType;
+  embeddingModels: EmbeddingModelOption[];
+  gridCols?: string;
+  headingLevel?: "h4" | "h5";
+}
+
+const CacheFieldSection: React.FC = ({
+  title,
+  section,
+  redisType,
+  embeddingModels,
+  gridCols = "grid-cols-1 gap-6 sm:grid-cols-2",
+  headingLevel = "h4",
+}) => {
+  const fields = fieldsForSection(section, redisType);
+  if (fields.length === 0) {
+    return null;
+  }
+
+  const Heading = headingLevel;
+
+  return (
+    
+ {title} +
+ {fields.map((field) => ( + + ))} +
+
+ ); +}; + +export default CacheFieldSection; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFormField.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFormField.tsx new file mode 100644 index 00000000000..d92ca302901 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/CacheFormField.tsx @@ -0,0 +1,54 @@ +import { Form, Input, Select, Switch } from "antd"; +import React from "react"; +import { CacheField } from "./cacheSettingsFields"; + +export interface EmbeddingModelOption { + value: string; + label: string; +} + +interface CacheFormFieldProps { + field: CacheField; + embeddingModels: EmbeddingModelOption[]; +} + +const renderControl = (field: CacheField, embeddingModels: EmbeddingModelOption[]): React.ReactNode => { + switch (field.type) { + case "boolean": + return ; + case "password": + return ; + case "integer": + case "float": + return ; + case "list": + return ; + case "model-select": + return ( + ; + } +}; + +const CacheFormField: React.FC = ({ field, embeddingModels }) => ( + + {renderControl(field, embeddingModels)} + +); + +export default CacheFormField; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.test.tsx similarity index 100% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.test.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.test.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx similarity index 94% rename from ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.tsx rename to ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx index a94d44dd4e0..fbca7ab5a97 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/caching/components/cache_settings/RedisTypeSelector.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx @@ -3,7 +3,7 @@ import { Select, SelectItem } from "@tremor/react"; interface RedisTypeSelectorProps { redisType: string; - redisTypeDescriptions: { [key: string]: string }; + redisTypeDescriptions: Readonly>; onTypeChange: (type: string) => void; } diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsFields.ts b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsFields.ts new file mode 100644 index 00000000000..1f5b566fc5f --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsFields.ts @@ -0,0 +1,261 @@ +import type { FormItemProps } from "antd"; + +export type CacheFieldType = "string" | "password" | "integer" | "float" | "boolean" | "list" | "model-select"; + +export type RedisType = "node" | "cluster" | "sentinel" | "semantic"; + +export type CacheSection = "connection" | "cluster" | "sentinel" | "semantic" | "ssl" | "cacheManagement" | "gcp"; + +export type CacheFieldRule = NonNullable[number]; + +export interface CacheField { + readonly name: string; + readonly label: string; + readonly type: CacheFieldType; + readonly section: CacheSection; + readonly helpText: string; + readonly redisType: RedisType | null; + readonly defaultValue?: string | number | boolean; + readonly rules?: CacheFieldRule[]; +} + +export const REDIS_TYPES: readonly RedisType[] = ["node", "cluster", "sentinel", "semantic"]; + +export const REDIS_TYPE_DESCRIPTIONS: Readonly> = { + node: "Standard Redis node/single instance", + cluster: "Redis Cluster mode for high availability and horizontal scaling", + sentinel: "Redis Sentinel mode for high availability with automatic failover", + semantic: "Semantic caching that reuses responses for similar prompts", +}; + +const portRule: CacheFieldRule = { + validator: (_rule, value) => { + if (value === undefined || value === null || String(value).trim() === "") { + return Promise.resolve(); + } + const port = Number(value); + if (!Number.isInteger(port) || port < 1 || port > 65535) { + return Promise.reject(new Error("Port must be an integer between 1 and 65535")); + } + return Promise.resolve(); + }, +}; + +const jsonListRule: CacheFieldRule = { + validator: (_rule, value) => { + if (value === undefined || value === null || String(value).trim() === "") { + return Promise.resolve(); + } + let parsed: unknown; + try { + parsed = JSON.parse(String(value)); + } catch { + return Promise.reject(new Error("Must be a valid JSON array (use double quotes)")); + } + if (!Array.isArray(parsed)) { + return Promise.reject(new Error("Must be a JSON array")); + } + return Promise.resolve(); + }, +}; + +const nonNegativeIntegerRule: CacheFieldRule = { + validator: (_rule, value) => { + if (value === undefined || value === null || String(value).trim() === "") { + return Promise.resolve(); + } + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed < 0) { + return Promise.reject(new Error("Must be a non-negative integer")); + } + return Promise.resolve(); + }, +}; + +const numberRule: CacheFieldRule = { + validator: (_rule, value) => { + if (value === undefined || value === null || String(value).trim() === "") { + return Promise.resolve(); + } + if (Number.isNaN(Number(value))) { + return Promise.reject(new Error("Must be a number")); + } + return Promise.resolve(); + }, +}; + +export const CACHE_FIELDS: readonly CacheField[] = [ + { + name: "url", + label: "Redis URL", + type: "string", + section: "connection", + helpText: + "Full Redis/Valkey connection URL (e.g. redis://:password@host:6379/1). When set, it takes precedence over Host, Port, Password, and Database Index.", + redisType: null, + }, + { + name: "host", + label: "Host", + type: "string", + section: "connection", + helpText: "Redis server hostname or IP address", + redisType: null, + }, + { + name: "port", + label: "Port", + type: "string", + section: "connection", + helpText: "Redis server port number", + redisType: null, + defaultValue: "6379", + rules: [portRule], + }, + { + name: "db", + label: "Database Index", + type: "integer", + section: "connection", + helpText: "Logical database index to isolate the cache (e.g. 1 for redis://host:6379/1)", + redisType: null, + rules: [nonNegativeIntegerRule], + }, + { + name: "password", + label: "Password", + type: "password", + section: "connection", + helpText: "Redis server password", + redisType: null, + }, + { + name: "username", + label: "Username", + type: "string", + section: "connection", + helpText: "Redis server username (if required)", + redisType: null, + }, + { + name: "redis_startup_nodes", + label: "Startup Nodes", + type: "list", + section: "cluster", + helpText: 'List of startup nodes for Redis Cluster (e.g., [{"host": "127.0.0.1", "port": "7001"}])', + redisType: "cluster", + rules: [jsonListRule], + }, + { + name: "sentinel_nodes", + label: "Sentinel Nodes", + type: "list", + section: "sentinel", + helpText: 'List of Sentinel nodes (e.g., [["localhost", 26379]])', + redisType: "sentinel", + rules: [jsonListRule], + }, + { + name: "service_name", + label: "Service Name", + type: "string", + section: "sentinel", + helpText: "Master service name for Redis Sentinel", + redisType: "sentinel", + }, + { + name: "sentinel_password", + label: "Sentinel Password", + type: "password", + section: "sentinel", + helpText: "Password for Redis Sentinel authentication", + redisType: "sentinel", + }, + { + name: "similarity_threshold", + label: "Similarity Threshold", + type: "float", + section: "semantic", + helpText: "Similarity threshold for semantic cache", + redisType: "semantic", + defaultValue: 0.8, + rules: [numberRule], + }, + { + name: "redis_semantic_cache_embedding_model", + label: "Embedding Model", + type: "model-select", + section: "semantic", + helpText: "Embedding model for semantic cache", + redisType: "semantic", + }, + { + name: "ssl", + label: "SSL", + type: "boolean", + section: "ssl", + helpText: "Enable SSL/TLS connection", + redisType: null, + defaultValue: false, + }, + { + name: "ssl_cert_reqs", + label: "SSL Cert Reqs", + type: "string", + section: "ssl", + helpText: "SSL certificate requirements (None, CERT_REQUIRED, CERT_OPTIONAL)", + redisType: null, + }, + { + name: "ssl_check_hostname", + label: "SSL Check Hostname", + type: "boolean", + section: "ssl", + helpText: "Enable SSL hostname verification", + redisType: null, + defaultValue: false, + }, + { + name: "namespace", + label: "Namespace", + type: "string", + section: "cacheManagement", + helpText: "Namespace prefix for cache keys", + redisType: null, + }, + { + name: "ttl", + label: "TTL (seconds)", + type: "float", + section: "cacheManagement", + helpText: "Time-to-live for cached items in seconds", + redisType: null, + rules: [numberRule], + }, + { + name: "max_connections", + label: "Max Connections", + type: "integer", + section: "cacheManagement", + helpText: "Maximum number of connections in the connection pool", + redisType: null, + rules: [nonNegativeIntegerRule], + }, + { + name: "gcp_service_account", + label: "GCP Service Account", + type: "string", + section: "gcp", + helpText: + "GCP service account for IAM authentication (e.g., projects/-/serviceAccounts/your-sa@project.iam.gserviceaccount.com)", + redisType: null, + }, + { + name: "gcp_ssl_ca_certs", + label: "GCP SSL CA Certs", + type: "string", + section: "gcp", + helpText: "Path to SSL CA certificate file for GCP Memorystore Redis", + redisType: null, + }, +]; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.test.ts new file mode 100644 index 00000000000..79f28a97842 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.test.ts @@ -0,0 +1,86 @@ +import { describe, it, expect } from "vitest"; +import { buildCachePayload, buildInitialValues, fieldsForSection } from "./cacheSettingsUtils"; + +describe("fieldsForSection", () => { + it("should only include a redis-type-specific field when that type is selected", () => { + expect(fieldsForSection("cluster", "cluster").map((f) => f.name)).toEqual(["redis_startup_nodes"]); + expect(fieldsForSection("cluster", "node")).toEqual([]); + }); + + it("should include connection fields for every redis type in schema order", () => { + expect(fieldsForSection("connection", "node").map((f) => f.name)).toEqual([ + "url", + "host", + "port", + "db", + "password", + "username", + ]); + }); +}); + +describe("buildInitialValues", () => { + it("should apply defaults as strings for text inputs and coerce booleans", () => { + const values = buildInitialValues({}); + expect(values.port).toBe("6379"); + expect(values.similarity_threshold).toBe("0.8"); + expect(values.ssl).toBe(false); + expect(values.db).toBe(""); + }); + + it("should stringify list values so they render in a textarea", () => { + const nodes = [{ host: "127.0.0.1", port: "7001" }]; + const values = buildInitialValues({ redis_startup_nodes: nodes }); + expect(values.redis_startup_nodes).toBe(JSON.stringify(nodes, null, 2)); + }); + + it("should render numeric current values as strings for their text inputs", () => { + const values = buildInitialValues({ max_connections: 10 }); + expect(values.max_connections).toBe("10"); + }); +}); + +describe("buildCachePayload", () => { + it("should tag the payload as redis and drop empty fields and the UI-only redis_type", () => { + const payload = buildCachePayload("node", { host: "localhost", port: "6379", username: "" }, { forTesting: false }); + expect(payload).toEqual({ + type: "redis", + host: "localhost", + port: "6379", + ssl: false, + ssl_check_hostname: false, + }); + expect(payload).not.toHaveProperty("redis_type"); + expect(payload).not.toHaveProperty("username"); + }); + + it("should parse list fields from their textarea string into arrays", () => { + const payload = buildCachePayload( + "cluster", + { redis_startup_nodes: '[{"host":"127.0.0.1","port":"7001"}]' }, + { forTesting: false }, + ); + expect(payload.redis_startup_nodes).toEqual([{ host: "127.0.0.1", port: "7001" }]); + }); + + it("should omit a list field whose textarea holds invalid JSON", () => { + const payload = buildCachePayload("cluster", { redis_startup_nodes: "not json" }, { forTesting: false }); + expect(payload).not.toHaveProperty("redis_startup_nodes"); + }); + + it("should send type redis-semantic when saving a semantic cache", () => { + const payload = buildCachePayload("semantic", { similarity_threshold: 0.9 }, { forTesting: false }); + expect(payload.type).toBe("redis-semantic"); + expect(payload.similarity_threshold).toBe(0.9); + }); + + it("should keep type redis when testing a semantic cache so the test endpoint accepts it", () => { + const payload = buildCachePayload("semantic", { similarity_threshold: 0.9 }, { forTesting: true }); + expect(payload.type).toBe("redis"); + }); + + it("should exclude fields that do not belong to the selected redis type", () => { + const payload = buildCachePayload("node", { sentinel_nodes: '[["localhost",26379]]' }, { forTesting: false }); + expect(payload).not.toHaveProperty("sentinel_nodes"); + }); +}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.ts b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.ts new file mode 100644 index 00000000000..088da21961c --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsUtils.ts @@ -0,0 +1,81 @@ +import { CACHE_FIELDS, CacheField, CacheSection, RedisType } from "./cacheSettingsFields"; + +export type CacheFormValue = string | number | boolean | undefined; +export type CacheFormValues = Record; +export type CacheSavePayloadValue = string | number | boolean | unknown[]; +export type CacheSavePayload = Record; + +export const isFieldVisible = (field: CacheField, redisType: RedisType): boolean => + field.redisType === null || field.redisType === redisType; + +export const fieldsForSection = (section: CacheSection, redisType: RedisType): CacheField[] => + CACHE_FIELDS.filter((field) => field.section === section && isFieldVisible(field, redisType)); + +const initialValueForField = (field: CacheField, raw: unknown): CacheFormValue => { + const source = raw ?? field.defaultValue; + + if (field.type === "boolean") { + return source === true || source === "true"; + } + + if (field.type === "list") { + if (source === undefined || source === null || source === "") { + return ""; + } + return typeof source === "string" ? source : JSON.stringify(source, null, 2); + } + + if (source === undefined || source === null) { + return ""; + } + return String(source); +}; + +export const buildInitialValues = (currentValues: Record): CacheFormValues => + Object.fromEntries(CACHE_FIELDS.map((field) => [field.name, initialValueForField(field, currentValues[field.name])])); + +const saveValueForField = (field: CacheField, raw: CacheFormValue): CacheSavePayloadValue | undefined => { + if (field.type === "boolean") { + return Boolean(raw); + } + + if (field.type === "list") { + if (typeof raw !== "string" || raw.trim() === "") { + return undefined; + } + try { + return JSON.parse(raw) as unknown[]; + } catch { + return undefined; + } + } + + if (field.type === "integer" || field.type === "float") { + if (raw === undefined || raw === null || raw === "") { + return undefined; + } + const parsed = Number(raw); + return Number.isNaN(parsed) ? undefined : parsed; + } + + if (typeof raw !== "string") { + return raw === undefined ? undefined : String(raw); + } + const trimmed = raw.trim(); + return trimmed === "" ? undefined : trimmed; +}; + +export const buildCachePayload = ( + redisType: RedisType, + values: CacheFormValues, + { forTesting }: { forTesting: boolean }, +): CacheSavePayload => { + const type = !forTesting && redisType === "semantic" ? "redis-semantic" : "redis"; + + const entries = CACHE_FIELDS.filter((field) => isFieldVisible(field, redisType)).flatMap((field) => { + const value = saveValueForField(field, values[field.name]); + return value === undefined ? [] : [[field.name, value] as const]; + }); + + return { type, ...Object.fromEntries(entries) }; +}; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.test.tsx new file mode 100644 index 00000000000..0f768372ad9 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.test.tsx @@ -0,0 +1,147 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import CacheSettings from "./index"; + +const { getCacheSettingsCall, testCacheConnectionCall, updateCacheSettingsCall } = vi.hoisted(() => ({ + getCacheSettingsCall: vi.fn(), + testCacheConnectionCall: vi.fn(), + updateCacheSettingsCall: vi.fn(), +})); + +vi.mock("@/components/networking", () => ({ + getCacheSettingsCall, + testCacheConnectionCall, + updateCacheSettingsCall, +})); + +vi.mock("@/components/llm_calls/fetch_models", () => ({ + fetchAvailableModels: vi.fn().mockResolvedValue([]), +})); + +const renderSettings = () => render(); + +describe("CacheSettings", () => { + beforeEach(() => { + vi.clearAllMocks(); + getCacheSettingsCall.mockResolvedValue({ current_values: {} }); + updateCacheSettingsCall.mockResolvedValue({ status: "success" }); + testCacheConnectionCall.mockResolvedValue({ status: "success" }); + }); + + it("should render the connection fields once current values load", async () => { + renderSettings(); + expect(await screen.findByText("Connection Settings")).toBeInTheDocument(); + }); + + describe("when the redis type is node", () => { + it("should show the connection fields and hide cluster/sentinel/semantic fields", async () => { + renderSettings(); + + expect(await screen.findByText("Redis URL")).toBeInTheDocument(); + expect(screen.getByText("Database Index")).toBeInTheDocument(); + expect(screen.queryByText("Startup Nodes")).not.toBeInTheDocument(); + expect(screen.queryByText("Sentinel Nodes")).not.toBeInTheDocument(); + expect(screen.queryByText("Embedding Model")).not.toBeInTheDocument(); + }); + }); + + describe("when the redis type is cluster", () => { + it("should reveal the cluster startup nodes field", async () => { + getCacheSettingsCall.mockResolvedValue({ current_values: { redis_type: "cluster" } }); + renderSettings(); + expect(await screen.findByText("Startup Nodes")).toBeInTheDocument(); + }); + }); + + describe("when the redis type is sentinel", () => { + it("should reveal the sentinel fields", async () => { + getCacheSettingsCall.mockResolvedValue({ current_values: { redis_type: "sentinel" } }); + renderSettings(); + expect(await screen.findByText("Sentinel Nodes")).toBeInTheDocument(); + expect(screen.getByText("Service Name")).toBeInTheDocument(); + }); + }); + + describe("when the redis type is semantic", () => { + it("should reveal the semantic fields", async () => { + getCacheSettingsCall.mockResolvedValue({ current_values: { redis_type: "semantic" } }); + renderSettings(); + expect(await screen.findByText("Similarity Threshold")).toBeInTheDocument(); + expect(screen.getByText("Embedding Model")).toBeInTheDocument(); + }); + }); + + describe("when a field fails inline validation", () => { + it("should block save and surface the validation message", async () => { + const user = userEvent.setup(); + renderSettings(); + + const port = await screen.findByLabelText("Port"); + await user.clear(port); + await user.type(port, "99999"); + await user.click(screen.getByRole("button", { name: /save changes/i })); + + expect(await screen.findByText(/Port must be an integer between 1 and 65535/i)).toBeInTheDocument(); + expect(updateCacheSettingsCall).not.toHaveBeenCalled(); + }); + + it("should block save when a list field holds malformed JSON instead of silently dropping it", async () => { + const user = userEvent.setup(); + getCacheSettingsCall.mockResolvedValue({ current_values: { redis_type: "cluster" } }); + renderSettings(); + + const startupNodes = await screen.findByLabelText("Startup Nodes"); + await user.type(startupNodes, "not json"); + await user.click(screen.getByRole("button", { name: /save changes/i })); + + expect(await screen.findByText(/Must be a valid JSON array/i)).toBeInTheDocument(); + expect(updateCacheSettingsCall).not.toHaveBeenCalled(); + }); + + it("should block save with an error when a non-numeric value is entered into a numeric field", async () => { + const user = userEvent.setup(); + renderSettings(); + + const db = await screen.findByLabelText("Database Index"); + await user.type(db, "redis://host:6379/1"); + await user.click(screen.getByRole("button", { name: /save changes/i })); + + expect(await screen.findByText(/Must be a non-negative integer/i)).toBeInTheDocument(); + expect(updateCacheSettingsCall).not.toHaveBeenCalled(); + }); + }); + + describe("when saving a valid node configuration", () => { + it("should send the backend payload shape with type redis and no UI-only fields", async () => { + const user = userEvent.setup(); + renderSettings(); + + const host = await screen.findByLabelText("Host"); + await user.type(host, "localhost"); + await user.click(screen.getByRole("button", { name: /save changes/i })); + + await waitFor(() => + expect(updateCacheSettingsCall).toHaveBeenCalledWith("sk-test", { + type: "redis", + host: "localhost", + port: "6379", + ssl: false, + ssl_check_hostname: false, + }), + ); + }); + + it("should include a numeric field like Database Index in the save payload", async () => { + const user = userEvent.setup(); + renderSettings(); + + await user.type(await screen.findByLabelText("Redis URL"), "redis://host:6379/1"); + await user.type(await screen.findByLabelText("Database Index"), "2"); + await user.click(screen.getByRole("button", { name: /save changes/i })); + + await waitFor(() => expect(updateCacheSettingsCall).toHaveBeenCalled()); + expect(updateCacheSettingsCall.mock.calls[0][1]).toMatchObject({ db: 2, url: "redis://host:6379/1" }); + }); + }); +}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.tsx new file mode 100644 index 00000000000..4382769ae9c --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/index.tsx @@ -0,0 +1,228 @@ +import React, { useState, useEffect, useCallback } from "react"; +import { Button, Accordion, AccordionHeader, AccordionBody } from "@tremor/react"; +import { Form } from "antd"; +import { getCacheSettingsCall, testCacheConnectionCall, updateCacheSettingsCall } from "@/components/networking"; +import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_models"; +import NotificationsManager from "@/components/molecules/notifications_manager"; +import RedisTypeSelector from "./RedisTypeSelector"; +import CacheFieldSection from "./CacheFieldSection"; +import { EmbeddingModelOption } from "./CacheFormField"; +import { REDIS_TYPES, REDIS_TYPE_DESCRIPTIONS, RedisType } from "./cacheSettingsFields"; +import { buildCachePayload, buildInitialValues, CacheFormValues } from "./cacheSettingsUtils"; + +interface CacheSettingsProps { + accessToken: string | null; + userRole: string | null; + userID: string | null; +} + +const toRedisType = (value: unknown): RedisType => + REDIS_TYPES.includes(value as RedisType) ? (value as RedisType) : "node"; + +const CacheSettings: React.FC = ({ accessToken }) => { + const [form] = Form.useForm(); + const [redisType, setRedisType] = useState("node"); + const [embeddingModels, setEmbeddingModels] = useState([]); + const [isTesting, setIsTesting] = useState(false); + const [isSaving, setIsSaving] = useState(false); + + const loadCacheSettings = useCallback(async () => { + if (!accessToken) { + return; + } + try { + const data = (await getCacheSettingsCall(accessToken)) as { current_values?: Record }; + const currentValues = data.current_values ?? {}; + form.setFieldsValue(buildInitialValues(currentValues)); + setRedisType(toRedisType(currentValues.redis_type)); + } catch (error) { + console.error("Failed to load cache settings:", error); + NotificationsManager.fromBackend("Failed to load cache settings"); + } + }, [accessToken, form]); + + useEffect(() => { + loadCacheSettings(); + }, [loadCacheSettings]); + + useEffect(() => { + if (!accessToken) { + return; + } + fetchAvailableModels(accessToken) + .then((models: ModelGroup[]) => + setEmbeddingModels( + models + .filter((model) => model.mode === "embedding") + .map((model) => ({ value: model.model_group, label: model.model_group })), + ), + ) + .catch((error) => console.error("Error fetching embedding models:", error)); + }, [accessToken]); + + const validate = async (): Promise => { + try { + return await form.validateFields(); + } catch { + return null; + } + }; + + const handleTestConnection = async () => { + if (!accessToken) { + return; + } + const values = await validate(); + if (values === null) { + return; + } + + setIsTesting(true); + try { + const result = await testCacheConnectionCall( + accessToken, + buildCachePayload(redisType, values, { forTesting: true }), + ); + if (result.status === "success") { + NotificationsManager.success("Cache connection test successful!"); + } else { + NotificationsManager.fromBackend(`Connection test failed: ${result.message || result.error}`); + } + } catch (error) { + console.error("Test connection error:", error); + NotificationsManager.fromBackend( + `Connection test failed: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } finally { + setIsTesting(false); + } + }; + + const handleSaveChanges = async () => { + if (!accessToken) { + return; + } + const values = await validate(); + if (values === null) { + return; + } + + setIsSaving(true); + try { + await updateCacheSettingsCall(accessToken, buildCachePayload(redisType, values, { forTesting: false })); + NotificationsManager.success("Cache settings updated successfully"); + await loadCacheSettings(); + } catch (error) { + console.error("Failed to save cache settings:", error); + NotificationsManager.fromBackend("Failed to update cache settings"); + } finally { + setIsSaving(false); + } + }; + + if (!accessToken) { + return null; + } + + return ( +
+
+
+

Cache Settings

+

Configure Redis cache for LiteLLM

+
+ + setRedisType(toRedisType(type))} + /> + +
+ +
+ + {redisType === "cluster" && ( +
+ +
+ )} + + {redisType === "sentinel" && ( +
+ +
+ )} + + {redisType === "semantic" && ( +
+ +
+ )} + + + + Advanced Settings + + +
+ + + +
+
+
+ + +
+ + +
+
+ ); +}; + +export default CacheSettings; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisFieldSection.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisFieldSection.tsx new file mode 100644 index 00000000000..af807926ed5 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisFieldSection.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import CoordinationRedisFormField from "./CoordinationRedisFormField"; +import { fieldsForSection } from "./coordinationRedisUtils"; +import { CoordinationRedisType, CoordinationSection } from "./coordinationRedisFields"; + +interface CoordinationRedisFieldSectionProps { + title: string; + section: CoordinationSection; + redisType: CoordinationRedisType; + configuredSecrets: ReadonlySet; + gridCols?: string; + headingLevel?: "h4" | "h5"; +} + +const CoordinationRedisFieldSection: React.FC = ({ + title, + section, + redisType, + configuredSecrets, + gridCols = "grid-cols-1 gap-6 sm:grid-cols-2", + headingLevel = "h4", +}) => { + const fields = fieldsForSection(section, redisType); + if (fields.length === 0) { + return null; + } + + const Heading = headingLevel; + + return ( +
+ {title} +
+ {fields.map((field) => ( + + ))} +
+
+ ); +}; + +export default CoordinationRedisFieldSection; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisFormField.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisFormField.tsx new file mode 100644 index 00000000000..50c2a39567a --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisFormField.tsx @@ -0,0 +1,39 @@ +import { Form, Input, Switch } from "antd"; +import React from "react"; +import { CoordinationField } from "./coordinationRedisFields"; + +export const SECRET_ALREADY_SET_PLACEHOLDER = "Already set. Enter a new value to replace it."; + +interface CoordinationRedisFormFieldProps { + field: CoordinationField; + isSecretConfigured: boolean; +} + +const renderControl = (field: CoordinationField, placeholder: string): React.ReactNode => { + switch (field.type) { + case "boolean": + return ; + case "password": + return ; + case "integer": + return ; + case "list": + return ; + default: + return ; + } +}; + +const CoordinationRedisFormField: React.FC = ({ field, isSecretConfigured }) => ( + + {renderControl(field, isSecretConfigured ? SECRET_ALREADY_SET_PLACEHOLDER : field.helpText)} + +); + +export default CoordinationRedisFormField; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisTypeSelector.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisTypeSelector.tsx new file mode 100644 index 00000000000..daab8505890 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisTypeSelector.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import { Select } from "antd"; +import { + COORDINATION_REDIS_TYPES, + COORDINATION_REDIS_TYPE_DESCRIPTIONS, + COORDINATION_REDIS_TYPE_LABELS, + CoordinationRedisType, +} from "./coordinationRedisFields"; + +interface CoordinationRedisTypeSelectorProps { + redisType: CoordinationRedisType; + onTypeChange: (type: CoordinationRedisType) => void; +} + +const OPTIONS = COORDINATION_REDIS_TYPES.map((type) => ({ value: type, label: COORDINATION_REDIS_TYPE_LABELS[type] })); + +const CoordinationRedisTypeSelector: React.FC = ({ redisType, onTypeChange }) => ( +
+ + - {field.field_description} -
-
- ); - } - - if (field.field_type === "Integer" || field.field_type === "Float") { - return ( -
- - -

{field.field_description}

-
- ); - } - - if (field.field_type === "List") { - return ( -
- -